diff -Nru game-music-emu-0.6.2/bitbucket-pipelines.yml game-music-emu-0.6.3/bitbucket-pipelines.yml --- game-music-emu-0.6.2/bitbucket-pipelines.yml 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/bitbucket-pipelines.yml 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,26 @@ +# This is a sample build configuration for C++. +# Check our guides at https://confluence.atlassian.com/x/VYk8Lw for more examples. +# Only use spaces to indent your .yml configuration. +# ----- +# You can specify a custom docker image from Docker Hub as your build environment. +image: gcc:8 + +pipelines: + default: + - parallel: + - step: + # Test whether it compiles on older GCC + image: gcc:6 + name: GCC 6 compile + script: + - apt-get -qq update && apt-get -qq install -y cmake + - mkdir build && cd build && cmake ../ && make + - cd demo && make + - step: + # Test whether it compiles on newer GCC + image: gcc:8 + name: GCC 8 compile + script: + - apt-get -qq update && apt-get -qq install -y cmake + - mkdir build && cd build && cmake ../ && make + - cd demo && make diff -Nru game-music-emu-0.6.2/CMakeLists.txt game-music-emu-0.6.3/CMakeLists.txt --- game-music-emu-0.6.2/CMakeLists.txt 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/CMakeLists.txt 2020-02-25 01:28:10.000000000 +0000 @@ -4,7 +4,7 @@ include (CheckCXXCompilerFlag) # When version is changed, also change the one in gme/gme.h to match -set(GME_VERSION 0.6.2 CACHE INTERNAL "libgme Version") +set(GME_VERSION 0.6.3 CACHE INTERNAL "libgme Version") # 2.6+ always assumes FATAL_ERROR, but 2.4 and below don't. # Of course, 2.4 might work, in which case you're welcome to drop @@ -48,10 +48,18 @@ SET(USE_GME_SPC 1 CACHE BOOL "Enable SNES SPC music emulation") endif() +if (NOT DEFINED GME_SPC_ISOLATED_ECHO_BUFFER) + option(GME_SPC_ISOLATED_ECHO_BUFFER "Enable isolated echo buffer on SPC emulator to allow correct playing of \"dodgy\" SPC files made for various ROM hacks ran on ZSNES" OFF) +endif() + if (NOT DEFINED USE_GME_VGM) SET(USE_GME_VGM 1 CACHE BOOL "Enable Sega VGM/VGZ music emulation") endif() +if (NOT DEFINED GME_YM2612_EMU) + SET(GME_YM2612_EMU "Nuked" CACHE STRING "Which YM2612 emulator to use: \"Nuked\" (LGPLv2.1+), \"MAME\" (GPLv2+), or \"GENS\" (LGPLv2.1+)") +endif() + if (USE_GME_NSFE AND NOT USE_GME_NSF) MESSAGE(" -- NSFE support requires NSF, enabling NSF support. --") SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE) @@ -59,32 +67,36 @@ option(BUILD_SHARED_LIBS "Build shared library (set to OFF for static library)" ON) -# Check for GCC "visibility" support. -if (CMAKE_COMPILER_IS_GNUCXX) - check_cxx_compiler_flag (-fvisibility=hidden __LIBGME_TEST_VISIBILITY) - set (ENABLE_VISIBILITY OFF) - if (__LIBGME_TEST_VISIBILITY) - # get the gcc version - exec_program(${CMAKE_CXX_COMPILER} ARGS --version OUTPUT_VARIABLE _gcc_version_info) - string (REGEX MATCH "[3-9]\\.[0-9]\\.[0-9]" _gcc_version "${_gcc_version_info}") - - # gcc <4.1 had poor support for symbol visibility - if ((${_gcc_version} VERSION_GREATER "4.1") OR (${_gcc_version} VERSION_EQUAL "4.1")) - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") - set (ENABLE_VISIBILITY ON) - add_definitions (-DLIBGME_VISIBILITY) - - # GCC >= 4.2 also correctly supports making inline members have hidden - # visibility by default. - if ((${_gcc_version} VERSION_GREATER "4.2") OR (${_gcc_version} VERSION_EQUAL "4.2")) - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") - endif() - endif() - endif() # test visibility - - # Cache this result - set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility") -endif (CMAKE_COMPILER_IS_GNUCXX) +option(ENABLE_UBSAN "Enable Undefined Behavior Sanitizer error-checking" ON) + +option(BUILD_FRAMEWORK "Build framework instead of dylib (on macOS)" OFF) + +# Check for GCC/Clang "visibility" support. +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" + OR + CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -W -Wextra") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + + # Assume we have visibility support on any compiler that supports C++11 + add_definitions (-DLIBGME_VISIBILITY) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden") + + # Try to protect against undefined behavior from signed integer overflow + # This has caused miscompilation of code already and there are other + # potential uses; see https://bitbucket.org/mpyne/game-music-emu/issues/18/ + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fwrapv") + + if (ENABLE_UBSAN) + # GCC needs -static-libubsan + if (NOT BUILD_SHARED_LIBS AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -static-libubsan") + else() + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") + endif() + endif() +endif () # Shared library defined here add_subdirectory(gme) diff -Nru game-music-emu-0.6.2/debian/changelog game-music-emu-0.6.3/debian/changelog --- game-music-emu-0.6.2/debian/changelog 2020-03-23 06:14:34.000000000 +0000 +++ game-music-emu-0.6.3/debian/changelog 2020-04-17 16:16:56.000000000 +0000 @@ -1,8 +1,30 @@ -game-music-emu (0.6.2-1build1) focal; urgency=medium +game-music-emu (0.6.3-2) unstable; urgency=medium - * No-change rebuild for libgcc-s1 package name change. + * debian/libgme0.symbols: + + Add some regexes for the C++ specific symbols to fix build failure on 32 + bit architectures. + * debian/rules: + + Don't enable ubsan support, for whatever reason it's enabled by default. - -- Matthias Klose Mon, 23 Mar 2020 07:14:34 +0100 + -- Sebastian Dröge Fri, 17 Apr 2020 19:16:56 +0300 + +game-music-emu (0.6.3-1) unstable; urgency=medium + + * New upstream release + + Drop all patches as they're not needed anymore. + * debian/watch: + + Update watch file syntax. + * debian/libgme0.symbols: + + Add new symbols. + + Make some symbols optional to fix build with GCC 10 (Closes: #957252). + * debian/rules, + debian/control, + debian/compat: + + Fix various lintian warnings. + + Ship pkg-config file. + + Update to debhelper compat version 12 and standards version 4.5.0. + + -- Sebastian Dröge Fri, 17 Apr 2020 14:29:33 +0300 game-music-emu (0.6.2-1) unstable; urgency=high diff -Nru game-music-emu-0.6.2/debian/compat game-music-emu-0.6.3/debian/compat --- game-music-emu-0.6.2/debian/compat 2015-09-19 16:51:41.000000000 +0000 +++ game-music-emu-0.6.3/debian/compat 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -9 diff -Nru game-music-emu-0.6.2/debian/control game-music-emu-0.6.3/debian/control --- game-music-emu-0.6.2/debian/control 2015-09-19 16:51:41.000000000 +0000 +++ game-music-emu-0.6.3/debian/control 2020-04-17 12:45:01.000000000 +0000 @@ -2,10 +2,11 @@ Section: sound Priority: optional Maintainer: Sebastian Dröge -Build-Depends: debhelper (>= 9), - cmake (>= 2.6.0), +Build-Depends: debhelper (>= 12), + debhelper-compat (= 12), + cmake (>= 2.6), zlib1g-dev -Standards-Version: 3.8.4 +Standards-Version: 4.5.0 Homepage: https://bitbucket.org/mpyne/game-music-emu/wiki/Home Package: libgme0 diff -Nru game-music-emu-0.6.2/debian/libgme0.shlibs game-music-emu-0.6.3/debian/libgme0.shlibs --- game-music-emu-0.6.2/debian/libgme0.shlibs 2010-03-22 13:20:44.000000000 +0000 +++ game-music-emu-0.6.3/debian/libgme0.shlibs 2020-04-17 12:04:40.000000000 +0000 @@ -1 +1 @@ -libgme 0 libgme0 (>= 0.5.5) +libgme 0 libgme0 (>= 0.6.3) diff -Nru game-music-emu-0.6.2/debian/libgme0.symbols game-music-emu-0.6.3/debian/libgme0.symbols --- game-music-emu-0.6.2/debian/libgme0.symbols 2017-01-30 17:10:39.000000000 +0000 +++ game-music-emu-0.6.3/debian/libgme0.symbols 2020-04-17 16:16:56.000000000 +0000 @@ -1,33 +1,47 @@ libgme.so.0 libgme0 #MINVER# * Build-Depends-Package: libgme-dev + (c++|optional)"std::vector >, std::allocator > > >::~vector()@Base" 0.6.3 + (c++|optional|regex)"std::vector >::_M_default_append\(unsigned (int|long)\)@Base" 0.6.3 + gme_ay_type@Base 0.6.3 gme_clear_playlist@Base 0.6.0 gme_delete@Base 0.5.5 gme_enable_accuracy@Base 0.6.0 gme_equalizer@Base 0.5.5 gme_free_info@Base 0.5.5 + gme_gbs_type@Base 0.6.3 + gme_gym_type@Base 0.6.3 + gme_hes_type@Base 0.6.3 gme_identify_extension@Base 0.5.5 gme_identify_file@Base 0.5.5 gme_identify_header@Base 0.5.5 gme_ignore_silence@Base 0.5.5 + gme_kss_type@Base 0.6.3 gme_load_custom@Base 0.5.5 gme_load_data@Base 0.5.5 gme_load_file@Base 0.5.5 gme_load_m3u@Base 0.5.5 gme_load_m3u_data@Base 0.5.5 + gme_multi_channel@Base 0.6.3 gme_mute_voice@Base 0.5.5 gme_mute_voices@Base 0.5.5 gme_new_emu@Base 0.5.5 + gme_new_emu_multi_channel@Base 0.6.3 + gme_nsf_type@Base 0.6.3 + gme_nsfe_type@Base 0.6.3 gme_open_data@Base 0.5.5 gme_open_file@Base 0.5.5 gme_play@Base 0.5.5 + gme_sap_type@Base 0.6.3 gme_seek@Base 0.5.5 gme_seek_samples@Base 0.6.1 + gme_set_autoload_playback_limit@Base 0.6.3 gme_set_equalizer@Base 0.5.5 gme_set_fade@Base 0.5.5 gme_set_stereo_depth@Base 0.5.5 gme_set_tempo@Base 0.5.5 gme_set_user_cleanup@Base 0.5.5 gme_set_user_data@Base 0.5.5 + gme_spc_type@Base 0.6.3 gme_start_track@Base 0.5.5 gme_tell@Base 0.5.5 gme_tell_samples@Base 0.6.1 @@ -35,10 +49,14 @@ gme_track_ended@Base 0.5.5 gme_track_info@Base 0.5.5 gme_type@Base 0.5.5 + gme_type_extension@Base 0.6.3 gme_type_list@Base 0.5.5 gme_type_multitrack@Base 0.6.0 gme_type_system@Base 0.5.5 gme_user_data@Base 0.5.5 + gme_vgm_type@Base 0.6.3 + gme_vgz_type@Base 0.6.3 gme_voice_count@Base 0.5.5 gme_voice_name@Base 0.5.5 gme_warning@Base 0.5.5 + gme_wrong_file_type@Base 0.6.3 diff -Nru game-music-emu-0.6.2/debian/libgme-dev.install game-music-emu-0.6.3/debian/libgme-dev.install --- game-music-emu-0.6.2/debian/libgme-dev.install 2015-07-22 21:38:44.000000000 +0000 +++ game-music-emu-0.6.3/debian/libgme-dev.install 2020-04-17 12:50:26.000000000 +0000 @@ -1,2 +1,3 @@ debian/tmp/usr/lib/*/libgme.so -debian/tmp/usr/include/gme/gme.h +debian/tmp/usr/lib/*/pkgconfig +debian/tmp/usr/include/gme diff -Nru game-music-emu-0.6.2/debian/patches/01_enable-zlib.patch game-music-emu-0.6.3/debian/patches/01_enable-zlib.patch --- game-music-emu-0.6.2/debian/patches/01_enable-zlib.patch 2015-09-19 16:14:43.000000000 +0000 +++ game-music-emu-0.6.3/debian/patches/01_enable-zlib.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ ---- game-music-emu/gme/blargg_config.h.old 2015-09-19 18:12:25.420145148 +0200 -+++ game-music-emu/gme/blargg_config.h 2015-09-19 18:13:11.392456563 +0200 -@@ -4,7 +4,7 @@ - #define BLARGG_CONFIG_H - - // Uncomment to use zlib for transparent decompression of gzipped files --//#define HAVE_ZLIB_H -+#define HAVE_ZLIB_H - - // Uncomment and edit list to support only the listed game music types, - // so that the others don't get linked in at all. diff -Nru game-music-emu-0.6.2/debian/patches/03_link-zlib.patch game-music-emu-0.6.3/debian/patches/03_link-zlib.patch --- game-music-emu-0.6.2/debian/patches/03_link-zlib.patch 2017-01-30 17:10:39.000000000 +0000 +++ game-music-emu-0.6.3/debian/patches/03_link-zlib.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -Index: game-music-emu-0.6.1/gme/CMakeLists.txt -=================================================================== ---- game-music-emu-0.6.1.orig/gme/CMakeLists.txt -+++ game-music-emu-0.6.1/gme/CMakeLists.txt -@@ -145,6 +145,12 @@ include_directories(${CMAKE_CURRENT_BINA - # Add library to be compiled. - add_library(gme ${libgme_SRCS}) - -+find_package(ZLIB REQUIRED) -+if (ZLIB_FOUND) -+ include_directories(${ZLIB_INCLUDE_DIRS}) -+ target_link_libraries(gme ${ZLIB_LIBRARIES}) -+endif (ZLIB_FOUND) -+ - # The version is the release. The "soversion" is the API version. As long - # as only build fixes are performed (i.e. no backwards-incompatible changes - # to the API), the SOVERSION should be the same even when bumping up VERSION. diff -Nru game-music-emu-0.6.2/debian/patches/series game-music-emu-0.6.3/debian/patches/series --- game-music-emu-0.6.2/debian/patches/series 2017-01-30 17:10:39.000000000 +0000 +++ game-music-emu-0.6.3/debian/patches/series 2020-04-17 11:38:33.000000000 +0000 @@ -1,2 +0,0 @@ -01_enable-zlib.patch -03_link-zlib.patch diff -Nru game-music-emu-0.6.2/debian/rules game-music-emu-0.6.3/debian/rules --- game-music-emu-0.6.2/debian/rules 2015-07-22 21:51:31.000000000 +0000 +++ game-music-emu-0.6.3/debian/rules 2020-04-17 16:16:51.000000000 +0000 @@ -1,14 +1,18 @@ #!/usr/bin/make -f -DEB_HOST_MULTIARCH=$(shell dpkg-architecture -qDEB_HOST_MULTIARCH) +include /usr/share/dpkg/architecture.mk %: dh $@ +override_dh_auto_configure: + dh_auto_configure -- -DENABLE_UBSAN=OFF + override_dh_auto_install: dh_auto_install mkdir debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH) mv debian/tmp/usr/lib/libgme.so* debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH) + mv debian/tmp/usr/lib/pkgconfig debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH) override_dh_makeshlibs: dh_makeshlibs -- -c4 diff -Nru game-music-emu-0.6.2/debian/watch game-music-emu-0.6.3/debian/watch --- game-music-emu-0.6.2/debian/watch 2015-09-19 16:28:57.000000000 +0000 +++ game-music-emu-0.6.3/debian/watch 2020-04-17 11:29:56.000000000 +0000 @@ -1,2 +1,2 @@ -version=2 -https://bitbucket.org/mpyne/game-music-emu/downloads .*/(\d\S*)\.tar\.gz +version=4 +https://bitbucket.org/mpyne/game-music-emu/downloads/ game-music-emu-([\d\.]+)\.tar\.xz diff -Nru game-music-emu-0.6.2/demo/basics.c game-music-emu-0.6.3/demo/basics.c --- game-music-emu-0.6.2/demo/basics.c 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/demo/basics.c 2020-02-25 01:28:10.000000000 +0000 @@ -14,7 +14,7 @@ if ( argc >= 2 ) filename = argv[1]; - long sample_rate = 44100; /* number of samples per second */ + int sample_rate = 44100; /* number of samples per second */ /* index of track to play (0 = first) */ int track = argc >= 3 ? atoi(argv[2]) : 0; diff -Nru game-music-emu-0.6.2/demo/basics_mem.c game-music-emu-0.6.3/demo/basics_mem.c --- game-music-emu-0.6.2/demo/basics_mem.c 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/demo/basics_mem.c 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,84 @@ +/* C example that opens a game music file and records 10 seconds to "out.wav" */ + +#include "gme/gme.h" + +#include "Wave_Writer.h" /* wave_ functions for writing sound file */ +#include +#include + +void handle_error( const char* str ); + +char * dump_file(const char*file_path, size_t *size) +{ + FILE *in = fopen(file_path, "rb"); + char *buffer = NULL; + if (!in) + return NULL; + fseek(in, 0, SEEK_END); + (*size) = (size_t)ftell(in); + fseek(in, 0, SEEK_SET); + buffer = (char*)malloc(*size); + fread(buffer, 1, *size, in); + return buffer; +} + +int main(int argc, char *argv[]) +{ + const char *filename = "test.vgz"; /* Default file to open */ + if ( argc >= 2 ) + filename = argv[1]; + + int sample_rate = 44100; /* number of samples per second */ + /* index of track to play (0 = first) */ + int track = argc >= 3 ? atoi(argv[2]) : 0; + + size_t file_size = 0; + char *file_data = dump_file(filename, &file_size); + if (!file_data) + { + printf( "Error: Can't dump %s!\n", filename ); + exit( EXIT_FAILURE ); + } + + /* Open music file in new emulator */ + Music_Emu* emu; + handle_error( gme_open_data(file_data, (long)file_size, &emu, sample_rate) ); + /* File dump is no more needed */ + free(file_data); + + /* Start track */ + handle_error( gme_start_track( emu, track ) ); + + /* Begin writing to wave file */ + wave_open( sample_rate, "out.wav" ); + wave_enable_stereo(); + + /* Record 10 seconds of track */ + while ( gme_tell( emu ) < 10 * 1000L ) + { + /* Sample buffer */ + #define buf_size 1024 /* can be any multiple of 2 */ + short buf [buf_size]; + + /* Fill sample buffer */ + handle_error( gme_play( emu, buf_size, buf ) ); + + /* Write samples to wave file */ + wave_write( buf, buf_size ); + } + + /* Cleanup */ + gme_delete( emu ); + wave_close(); + + return 0; +} + +void handle_error( const char* str ) +{ + if ( str ) + { + printf( "Error: %s\n", str ); getchar(); + exit( EXIT_FAILURE ); + } +} diff -Nru game-music-emu-0.6.2/demo/CMakeLists.txt game-music-emu-0.6.3/demo/CMakeLists.txt --- game-music-emu-0.6.2/demo/CMakeLists.txt 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/demo/CMakeLists.txt 2020-02-25 01:28:10.000000000 +0000 @@ -15,3 +15,14 @@ VERBATIM) # VERBATIM is essentially required, "please use correct command line kthx" target_link_libraries(demo gme) + + +add_executable(demo_mem Wave_Writer.cpp basics_mem.c) + +add_custom_command(TARGET demo_mem + POST_BUILD + COMMAND cmake -E copy "${CMAKE_SOURCE_DIR}/test.vgz" ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Add convenience copy of test.nsf file for demo application" + VERBATIM) # VERBATIM is essentially required, "please use correct command line kthx" + +target_link_libraries(demo_mem gme) diff -Nru game-music-emu-0.6.2/demo/Wave_Writer.cpp game-music-emu-0.6.3/demo/Wave_Writer.cpp --- game-music-emu-0.6.2/demo/Wave_Writer.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/demo/Wave_Writer.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,8 +1,9 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Wave_Writer.h" #include +#include #include /* Copyright (C) 2003-2006 by Shay Green. Permission is hereby granted, free @@ -119,28 +120,29 @@ flush(); // generate header - long ds = sample_count_ * sizeof (sample_t); - long rs = header_size - 8 + ds; - int frame_size = chan_count * sizeof (sample_t); - long bps = rate * frame_size; + uint32_t ds = sample_count_ * sizeof (sample_t); + uint32_t rs = header_size - 8 + ds; + uint32_t frame_size = chan_count * sizeof (sample_t); + uint32_t bps = rate * frame_size; + +#define LE32(x) (unsigned char)(x), (unsigned char)((x)>>8), \ + (unsigned char)((x)>>16), (unsigned char)((x)>>24) + unsigned char header [header_size] = { 'R','I','F','F', - rs,rs>>8, // length of rest of file - rs>>16,rs>>24, + LE32(rs), // length of rest of file 'W','A','V','E', 'f','m','t',' ', 0x10,0,0,0, // size of fmt chunk 1,0, // uncompressed format - chan_count,0, // channel count - rate,rate >> 8, // sample rate - rate>>16,rate>>24, - bps,bps>>8, // bytes per second - bps>>16,bps>>24, - frame_size,0, // bytes per sample frame + (unsigned char)chan_count,0,// channel count + LE32(rate), // sample rate + LE32(bps), // bytes per second + (unsigned char)frame_size,0,// bytes per sample frame 16,0, // bits per sample 'd','a','t','a', - ds,ds>>8,ds>>16,ds>>24// size of sample data + LE32(ds) // size of sample data // ... // sample data }; diff -Nru game-music-emu-0.6.2/.gitignore game-music-emu-0.6.3/.gitignore --- game-music-emu-0.6.2/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/.gitignore 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,17 @@ +# Ignore "build" folder +build/* + +# Intellij-based IDE local settings +.idea/* + +# Qt Creator local user settings files +*.user* + +# test data +test/demo +test/demo_mem +test/cur/* +test/curm/* +test/new/* +test/newm/* + diff -Nru game-music-emu-0.6.2/gme/Ay_Apu.cpp game-music-emu-0.6.3/gme/Ay_Apu.cpp --- game-music-emu-0.6.2/gme/Ay_Apu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ay_Apu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Ay_Apu.h" diff -Nru game-music-emu-0.6.2/gme/Ay_Apu.h game-music-emu-0.6.3/gme/Ay_Apu.h --- game-music-emu-0.6.2/gme/Ay_Apu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ay_Apu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // AY-3-8910 sound chip emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef AY_APU_H #define AY_APU_H diff -Nru game-music-emu-0.6.2/gme/Ay_Cpu.cpp game-music-emu-0.6.3/gme/Ay_Cpu.cpp --- game-music-emu-0.6.2/gme/Ay_Cpu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ay_Cpu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ /* Last validated with zexall 2006.11.21 5:26 PM @@ -110,7 +110,7 @@ #define R16( n, shift, offset )\ (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) -#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e +#define CASE5( a, b, c, d, e ) /* FALLTHRU */ case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e #define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f #define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g #define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h @@ -136,11 +136,6 @@ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, }; -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - bool Ay_Cpu::run( cpu_time_t end_time ) { set_end_time( end_time ); @@ -148,8 +143,6 @@ this->state = &s; bool warning = false; - typedef BOOST::int8_t int8_t; - union { regs_t rg; pairs_t rp; @@ -160,10 +153,10 @@ cpu_time_t s_time = s.time; uint8_t* const mem = this->mem; // cache - fuint16 pc = r.pc; - fuint16 sp = r.sp; - fuint16 ix = r.ix; // TODO: keep in memory for direct access? - fuint16 iy = r.iy; + uint16_t pc = r.pc; + uint16_t sp = r.sp; + uint16_t ix = r.ix; // TODO: keep in memory for direct access? + uint16_t iy = r.iy; int flags = r.b.flags; goto loop; @@ -182,7 +175,7 @@ check( (unsigned) ix < 0x10000 ); check( (unsigned) iy < 0x10000 ); - fuint8 opcode; + uint8_t opcode; opcode = READ_PROG( pc ); pc++; @@ -206,7 +199,7 @@ 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F }; - fuint16 data; + uint16_t data; data = base_timing [opcode]; if ( (s_time += data) >= 0 ) goto possibly_out_of_time; @@ -262,7 +255,7 @@ goto loop; case 0x3A:{// LD A,(addr) - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; rg.a = READ( addr ); goto loop; @@ -277,7 +270,7 @@ // JR #define JR( cond ) {\ - int disp = (BOOST::int8_t) data;\ + int disp = (int8_t) data;\ pc++;\ if ( !(cond) )\ goto jr_not_taken;\ @@ -349,7 +342,7 @@ case 0xCD:{// CALL addr call_taken: - fuint16 addr = pc + 2; + uint16_t addr = pc + 2; pc = GET_ADDR(); sp = uint16_t (sp - 2); WRITE_WORD( sp, addr ); @@ -366,7 +359,7 @@ CASE7( C7, CF, D7, DF, E7, EF, F7 ): data = pc; pc = opcode & 0x38; - goto push_data; + goto push_data; /* fallthrough */ // PUSH/POP case 0xF5: // PUSH AF @@ -398,7 +391,7 @@ // ADC/ADD/SBC/SUB case 0x96: // SUB (HL) case 0x86: // ADD (HL) - flags &= ~C01; + flags &= ~C01; /* fallthrough */ case 0x9E: // SBC (HL) case 0x8E: // ADC (HL) data = READ( rp.hl ); @@ -406,7 +399,7 @@ case 0xD6: // SUB A,imm case 0xC6: // ADD imm - flags &= ~C01; + flags &= ~C01; /* fallthrough */ case 0xDE: // SBC A,imm case 0xCE: // ADC imm pc++; @@ -414,7 +407,7 @@ CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r - flags &= ~C01; + flags &= ~C01; /* fallthrough */ CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r data = R8( opcode & 7, 0 ); @@ -659,21 +652,21 @@ goto loop; case 0x2A:{// LD HL,(addr) - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; rp.hl = READ_WORD( addr ); goto loop; } case 0x32:{// LD (addr),A - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; WRITE( addr, rg.a ); goto loop; } case 0x22:{// LD (addr),HL - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, rp.hl ); goto loop; @@ -696,7 +689,7 @@ // Rotate case 0x07:{// RLCA - fuint16 temp = rg.a; + uint16_t temp = rg.a; temp = (temp << 1) | (temp >> 7); flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08 | C01)); @@ -705,7 +698,7 @@ } case 0x0F:{// RRCA - fuint16 temp = rg.a; + uint16_t temp = rg.a; flags = (flags & (S80 | Z40 | P04)) | (temp & C01); temp = (temp << 7) | (temp >> 1); @@ -724,7 +717,7 @@ } case 0x1F:{// RRA - fuint16 temp = (flags << 7) | (rg.a >> 1); + uint16_t temp = (flags << 7) | (rg.a >> 1); flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08)) | (rg.a & C01); @@ -734,7 +727,7 @@ // Misc case 0x2F:{// CPL - fuint16 temp = ~rg.a; + uint16_t temp = ~rg.a; flags = (flags & (S80 | Z40 | P04 | C01)) | (temp & (F20 | F08)) | (H10 | N02); @@ -760,21 +753,21 @@ goto loop; case 0xE3:{// EX (SP),HL - fuint16 temp = READ_WORD( sp ); + uint16_t temp = READ_WORD( sp ); WRITE_WORD( sp, rp.hl ); rp.hl = temp; goto loop; } case 0xEB:{// EX DE,HL - fuint16 temp = rp.hl; + uint16_t temp = rp.hl; rp.hl = rp.de; rp.de = temp; goto loop; } case 0xD9:{// EXX DE,HL - fuint16 temp = r.alt.w.bc; + uint16_t temp = r.alt.w.bc; r.alt.w.bc = rp.bc; rp.bc = temp; @@ -815,7 +808,7 @@ // Rotate left #define RLC( read, write ) {\ - fuint8 result = read;\ + uint8_t result = read;\ result = uint8_t (result << 1) | (result >> 7);\ flags = SZ28P( result ) | (result & C01);\ write;\ @@ -834,7 +827,7 @@ } #define RL( read, write ) {\ - fuint16 result = (read << 1) | (flags & C01);\ + uint16_t result = (read << 1) | (flags & C01);\ flags = SZ28PC( result );\ write;\ goto loop;\ @@ -852,7 +845,7 @@ } #define SLA( read, add, write ) {\ - fuint16 result = (read << 1) | add;\ + uint16_t result = (read << 1) | add;\ flags = SZ28PC( result );\ write;\ goto loop;\ @@ -883,7 +876,7 @@ // Rotate right #define RRC( read, write ) {\ - fuint8 result = read;\ + uint8_t result = read;\ flags = result & C01;\ result = uint8_t (result << 7) | (result >> 1);\ flags |= SZ28P( result );\ @@ -903,8 +896,8 @@ } #define RR( read, write ) {\ - fuint8 result = read;\ - fuint8 temp = result & C01;\ + uint8_t result = read;\ + uint8_t temp = result & C01;\ result = uint8_t (flags << 7) | (result >> 1);\ flags = SZ28P( result ) | temp;\ write;\ @@ -923,7 +916,7 @@ } #define SRA( read, write ) {\ - fuint8 result = read;\ + uint8_t result = read;\ flags = result & C01;\ result = (result & 0x80) | (result >> 1);\ flags |= SZ28P( result );\ @@ -943,7 +936,7 @@ } #define SRL( read, write ) {\ - fuint8 result = read;\ + uint8_t result = read;\ flags = result & C01;\ result >>= 1;\ flags |= SZ28P( result );\ @@ -1069,7 +1062,7 @@ flags = (flags & C01) | SZ28P( temp ); goto loop; } - + case 0x71: // OUT (C),0 rg.flags = 0; CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r @@ -1084,7 +1077,7 @@ case 0x43: // LD (ADDR),BC case 0x53: // LD (ADDR),DE temp = R16( data, 4, 0x43 ); - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, temp ); goto loop; @@ -1092,21 +1085,21 @@ case 0x4B: // LD BC,(ADDR) case 0x5B:{// LD DE,(ADDR) - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; R16( data, 4, 0x4B ) = READ_WORD( addr ); goto loop; } case 0x7B:{// LD SP,(ADDR) - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; sp = READ_WORD( addr ); goto loop; } case 0x67:{// RRD - fuint8 temp = READ( rp.hl ); + uint8_t temp = READ( rp.hl ); WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); temp = (rg.a & 0xF0) | (temp & 0x0F); flags = (flags & C01) | SZ28P( temp ); @@ -1115,7 +1108,7 @@ } case 0x6F:{// RLD - fuint8 temp = READ( rp.hl ); + uint8_t temp = READ( rp.hl ); WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); temp = (rg.a & 0xF0) | (temp >> 4); flags = (flags & C01) | SZ28P( temp ); @@ -1139,7 +1132,7 @@ case 0xA1: // CPI case 0xB1: // CPIR inc = +1; - fuint16 addr = rp.hl; + uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); @@ -1172,7 +1165,7 @@ case 0xA0: // LDI case 0xB0: // LDIR inc = +1; - fuint16 addr = rp.hl; + uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); @@ -1204,7 +1197,7 @@ case 0xA3: // OUTI case 0xB3: // OTIR inc = +1; - fuint16 addr = rp.hl; + uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); @@ -1230,7 +1223,7 @@ case 0xB2: // INIR inc = +1; - fuint16 addr = rp.hl; + uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = IN( rp.bc ); @@ -1295,7 +1288,7 @@ //////////////////////////////////////// DD/FD prefix { - fuint16 ixy; + uint16_t ixy; case 0xDD: ixy = ix; goto ix_prefix; @@ -1316,7 +1309,7 @@ case 0x96: // SUB (IXY+disp) case 0x86: // ADD (IXY+disp) - flags &= ~C01; + flags &= ~C01; /*fallthrough*/ case 0x9E: // SBC (IXY+disp) case 0x8E: // ADC (IXY+disp) pc++; @@ -1326,7 +1319,7 @@ case 0x94: // SUB HXY case 0x84: // ADD HXY - flags &= ~C01; + flags &= ~C01; /*fallthrough*/ case 0x9C: // SBC HXY case 0x8C: // ADC HXY opcode = data; @@ -1335,7 +1328,7 @@ case 0x95: // SUB LXY case 0x85: // ADD LXY - flags &= ~C01; + flags &= ~C01; /* fallthrough */ case 0x9D: // SBC LXY case 0x8D: // ADC LXY opcode = data; @@ -1490,7 +1483,7 @@ goto loop; case 0x22:{// LD (ADDR),IXY - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, ixy ); goto loop; @@ -1502,7 +1495,7 @@ goto set_ixy; case 0x2A:{// LD IXY,(addr) - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); ixy = READ_WORD( addr ); pc += 2; goto set_ixy; @@ -1526,7 +1519,7 @@ case 0x3E: goto srl_data_addr; // SRL (IXY) CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) - fuint8 temp = READ( data ); + uint8_t temp = READ( data ); int masked = temp & 1 << (data2 >> 3 & 7); flags = (flags & C01) | H10 | (masked & S80) | @@ -1628,7 +1621,7 @@ goto loop; case 0xE3:{// EX (SP),IXY - fuint16 temp = READ_WORD( sp ); + uint16_t temp = READ_WORD( sp ); WRITE_WORD( sp, ixy ); ixy = temp; goto set_ixy; diff -Nru game-music-emu-0.6.2/gme/Ay_Cpu.h game-music-emu-0.6.3/gme/Ay_Cpu.h --- game-music-emu-0.6.2/gme/Ay_Cpu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ay_Cpu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Z80 CPU emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef AY_CPU_H #define AY_CPU_H @@ -28,9 +28,6 @@ void set_time( cpu_time_t t ) { state->time = t - state->base; } void adjust_time( int delta ) { state->time += delta; } - typedef BOOST::uint8_t uint8_t; - typedef BOOST::uint16_t uint16_t; - #if BLARGG_BIG_ENDIAN struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; #else diff -Nru game-music-emu-0.6.2/gme/Ay_Emu.cpp game-music-emu-0.6.3/gme/Ay_Emu.cpp --- game-music-emu-0.6.2/gme/Ay_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ay_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,10 +1,12 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Ay_Emu.h" #include "blargg_endian.h" #include +#include // min, max + /* Copyright (C) 2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -24,6 +26,9 @@ unsigned const ram_start = 0x4000; int const osc_count = Ay_Apu::osc_count + 1; +using std::min; +using std::max; + Ay_Emu::Ay_Emu() { beeper_output = 0; @@ -50,7 +55,7 @@ long pos = ptr - (byte const*) file.header; long file_size = file.end - (byte const*) file.header; assert( (unsigned long) pos <= (unsigned long) file_size - 2 ); - int offset = (BOOST::int16_t) get_be16( ptr ); + int offset = (int16_t) get_be16( ptr ); if ( !offset || blargg_ulong (pos + offset) > blargg_ulong (file_size - min_size) ) return 0; return ptr + offset; @@ -117,7 +122,7 @@ static Music_Emu* new_ay_file() { return BLARGG_NEW Ay_File; } static gme_type_t_ const gme_ay_type_ = { "ZX Spectrum", 0, &new_ay_emu, &new_ay_file, "AY", 1 }; -gme_type_t const gme_ay_type = &gme_ay_type_; +extern gme_type_t const gme_ay_type = &gme_ay_type_; // Setup diff -Nru game-music-emu-0.6.2/gme/Ay_Emu.h game-music-emu-0.6.3/gme/Ay_Emu.h --- game-music-emu-0.6.2/gme/Ay_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ay_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Sinclair Spectrum AY music file emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef AY_EMU_H #define AY_EMU_H diff -Nru game-music-emu-0.6.2/gme/blargg_common.h game-music-emu-0.6.3/gme/blargg_common.h --- game-music-emu-0.6.2/gme/blargg_common.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/blargg_common.h 2020-02-25 01:28:10.000000000 +0000 @@ -80,6 +80,9 @@ #define BLARGG_4CHAR( a, b, c, d ) \ ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) +#define BLARGG_2CHAR( a, b ) \ + ((a&0xFF)*0x100L + (b&0xFF)) + // BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. #ifndef BOOST_STATIC_ASSERT #ifdef _MSC_VER @@ -132,51 +135,12 @@ typedef unsigned blargg_ulong; #endif -// BOOST::int8_t etc. +// int8_t etc. -// HAVE_STDINT_H: If defined, use for int8_t etc. -#if defined (HAVE_STDINT_H) +// TODO: Add CMake check for this, although I'd likely just point affected +// persons to a real compiler... +#if 1 || defined (HAVE_STDINT_H) #include - #define BOOST - -// HAVE_INTTYPES_H: If defined, use for int8_t etc. -#elif defined (HAVE_INTTYPES_H) - #include - #define BOOST - -#else - struct BOOST - { - #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F - typedef signed char int8_t; - typedef unsigned char uint8_t; - #else - // No suitable 8-bit type available - typedef struct see_blargg_common_h int8_t; - typedef struct see_blargg_common_h uint8_t; - #endif - - #if USHRT_MAX == 0xFFFF - typedef short int16_t; - typedef unsigned short uint16_t; - #else - // No suitable 16-bit type available - typedef struct see_blargg_common_h int16_t; - typedef struct see_blargg_common_h uint16_t; - #endif - - #if ULONG_MAX == 0xFFFFFFFF - typedef long int32_t; - typedef unsigned long uint32_t; - #elif UINT_MAX == 0xFFFFFFFF - typedef int int32_t; - typedef unsigned int uint32_t; - #else - // No suitable 32-bit type available - typedef struct see_blargg_common_h int32_t; - typedef struct see_blargg_common_h uint32_t; - #endif - }; #endif #if __GNUC__ >= 3 diff -Nru game-music-emu-0.6.2/gme/blargg_endian.h game-music-emu-0.6.3/gme/blargg_endian.h --- game-music-emu-0.6.2/gme/blargg_endian.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/blargg_endian.h 2020-02-25 01:28:10.000000000 +0000 @@ -123,15 +123,15 @@ #if BLARGG_NONPORTABLE // Optimized implementation if byte order is known #if BLARGG_LITTLE_ENDIAN - #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + #define GET_LE16( addr ) (*(uint16_t*) (addr)) + #define GET_LE32( addr ) (*(uint32_t*) (addr)) + #define SET_LE16( addr, data ) (void) (*(uint16_t*) (addr) = (data)) + #define SET_LE32( addr, data ) (void) (*(uint32_t*) (addr) = (data)) #elif BLARGG_BIG_ENDIAN - #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + #define GET_BE16( addr ) (*(uint16_t*) (addr)) + #define GET_BE32( addr ) (*(uint32_t*) (addr)) + #define SET_BE16( addr, data ) (void) (*(uint16_t*) (addr) = (data)) + #define SET_BE32( addr, data ) (void) (*(uint32_t*) (addr) = (data)) #if BLARGG_CPU_POWERPC // PowerPC has special byte-reversed instructions @@ -172,13 +172,13 @@ // auto-selecting versions -inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } -inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } -inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } -inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } -inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } -inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } -inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } -inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } +inline void set_le( uint16_t* p, unsigned n ) { SET_LE16( p, n ); } +inline void set_le( uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } +inline void set_be( uint16_t* p, unsigned n ) { SET_BE16( p, n ); } +inline void set_be( uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } +inline unsigned get_le( uint16_t* p ) { return GET_LE16( p ); } +inline blargg_ulong get_le( uint32_t* p ) { return GET_LE32( p ); } +inline unsigned get_be( uint16_t* p ) { return GET_BE16( p ); } +inline blargg_ulong get_be( uint32_t* p ) { return GET_BE32( p ); } #endif diff -Nru game-music-emu-0.6.2/gme/blargg_source.h game-music-emu-0.6.3/gme/blargg_source.h --- game-music-emu-0.6.2/gme/blargg_source.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/blargg_source.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,26 +1,26 @@ /* Included at the beginning of library source files, after all other #include lines. -Sets up helpful macros and services used in my source code. They don't need -module an annoying module prefix on their names since they are defined after -all other #include lines. */ + * Sets up helpful macros and services used in my source code. They don't need + * module an annoying module prefix on their names since they are defined after + * all other #include lines. */ #ifndef BLARGG_SOURCE_H #define BLARGG_SOURCE_H -// If debugging is enabled, abort program if expr is false. Meant for checking -// internal state and consistency. A failed assertion indicates a bug in the module. -// void assert( bool expr ); +/* If debugging is enabled, abort program if expr is false. Meant for checking + * internal state and consistency. A failed assertion indicates a bug in the module. + * void assert( bool expr ); */ #include -// If debugging is enabled and expr is false, abort program. Meant for checking -// caller-supplied parameters and operations that are outside the control of the -// module. A failed requirement indicates a bug outside the module. -// void require( bool expr ); +/* If debugging is enabled and expr is false, abort program. Meant for checking + * caller-supplied parameters and operations that are outside the control of the + * module. A failed requirement indicates a bug outside the module. + * void require( bool expr ); */ #undef require #define require( expr ) assert( expr ) -// Use to provide hints to compiler for optimized code layout in situations where we -// can almost always expect a conditional to go one way or the other. Should only be -// used in situations where an unexpected branch is truly exceptional though! +/* Use to provide hints to compiler for optimized code layout in situations where we + * can almost always expect a conditional to go one way or the other. Should only be + * used in situations where an unexpected branch is truly exceptional though! */ #undef likely #undef unlikely #ifdef __GNUC__ @@ -31,91 +31,57 @@ #define unlikely( x ) (x) #endif -// Like printf() except output goes to debug log file. Might be defined to do -// nothing (not even evaluate its arguments). -// void debug_printf( const char* format, ... ); -static inline void blargg_dprintf_( const char*, ... ) { } -#undef debug_printf -#define debug_printf (1) ? (void) 0 : blargg_dprintf_ - -// If enabled, evaluate expr and if false, make debug log entry with source file -// and line. Meant for finding situations that should be examined further, but that -// don't indicate a problem. In all cases, execution continues normally. +/* Like printf() except output goes to debug log file. Might be defined to do + * nothing (not even evaluate its arguments). + * void debug_printf( const char* format, ... ); */ +#if defined(__cplusplus) && defined(BLARGG_BUILD_DLL) + static inline void blargg_dprintf_( const char* fmt_str, ... ) { (void) fmt_str; } + #undef debug_printf + #define debug_printf (1) ? (void) 0 : blargg_dprintf_ +#endif + +/* If enabled, evaluate expr and if false, make debug log entry with source file + * and line. Meant for finding situations that should be examined further, but that + * don't indicate a problem. In all cases, execution continues normally. */ #undef check #define check( expr ) ((void) 0) -// If expr yields error string, return it from current function, otherwise continue. +/* If expr yields error string, return it from current function, otherwise continue. */ #undef RETURN_ERR #define RETURN_ERR( expr ) do { \ blargg_err_t blargg_return_err_ = (expr); \ if ( blargg_return_err_ ) return blargg_return_err_; \ } while ( 0 ) -// If ptr is 0, return out of memory error string. +/* If ptr is 0, return out of memory error string. */ #undef CHECK_ALLOC #define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) -// Avoid any macros which evaluate their arguments multiple times -#undef min -#undef max - -#define DEF_MIN_MAX( type ) \ - static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\ - static inline type max( type x, type y ) { if ( y < x ) return x; return y; } - -DEF_MIN_MAX( int ) -DEF_MIN_MAX( unsigned ) -DEF_MIN_MAX( long ) -DEF_MIN_MAX( unsigned long ) -DEF_MIN_MAX( float ) -DEF_MIN_MAX( double ) - -#undef DEF_MIN_MAX - -/* -// using const references generates crappy code, and I am currenly only using these -// for built-in types, so they take arguments by value - -// TODO: remove -inline int min( int x, int y ) -template -inline T min( T x, T y ) -{ - if ( x < y ) - return x; - return y; -} - -template -inline T max( T x, T y ) -{ - if ( x < y ) - return y; - return x; -} -*/ - -// TODO: good idea? bad idea? +/* TODO: good idea? bad idea? */ #undef byte #define byte byte_ typedef unsigned char byte; -// Setup compiler defines useful for exporting required public API symbols in gme.cpp +/* Setup compiler defines useful for exporting required public API symbols in gme.cpp */ #ifndef BLARGG_EXPORT - #if defined (_WIN32) && defined(BLARGG_BUILD_DLL) - #define BLARGG_EXPORT __declspec(dllexport) - #elif defined (LIBGME_VISIBILITY) + #if defined (_WIN32) + #if defined(BLARGG_BUILD_DLL) + #define BLARGG_EXPORT __declspec(dllexport) + #else + #define BLARGG_EXPORT /* Leave blank: friendly with both static and shared linking */ + #endif + #elif defined (LIBGME_VISIBILITY) && defined(__cplusplus) #define BLARGG_EXPORT __attribute__((visibility ("default"))) #else #define BLARGG_EXPORT #endif #endif -// deprecated +/* deprecated */ #define BLARGG_CHECK_ALLOC CHECK_ALLOC #define BLARGG_RETURN_ERR RETURN_ERR -// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of debug_printf and check +/* BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of debug_printf and check */ #ifdef BLARGG_SOURCE_BEGIN #include BLARGG_SOURCE_BEGIN #endif diff -Nru game-music-emu-0.6.2/gme/Blip_Buffer.h game-music-emu-0.6.3/gme/Blip_Buffer.h --- game-music-emu-0.6.2/gme/Blip_Buffer.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Blip_Buffer.h 2020-02-25 01:28:10.000000000 +0000 @@ -231,6 +231,11 @@ public: Blip_Synth() : impl( impulses, quality ) { } #endif + + // disable broken defaulted constructors, Blip_Synth_ isn't safe to move/copy + Blip_Synth (const Blip_Synth &) = delete; + Blip_Synth ( Blip_Synth &&) = delete; + Blip_Synth& operator=(const Blip_Synth &) = delete; }; // Low-pass equalization parameters diff -Nru game-music-emu-0.6.2/gme/Classic_Emu.cpp game-music-emu-0.6.3/gme/Classic_Emu.cpp --- game-music-emu-0.6.2/gme/Classic_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Classic_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Classic_Emu.h" @@ -54,6 +54,12 @@ return buf->set_sample_rate( rate, 1000 / 20 ); } +blargg_err_t Classic_Emu::set_multi_channel ( bool is_enabled ) +{ + RETURN_ERR( Music_Emu::set_multi_channel_( is_enabled ) ); + return 0; +} + void Classic_Emu::mute_voices_( int mask ) { Music_Emu::mute_voices_( mask ); diff -Nru game-music-emu-0.6.2/gme/Classic_Emu.h game-music-emu-0.6.3/gme/Classic_Emu.h --- game-music-emu-0.6.2/gme/Classic_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Classic_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Common aspects of emulators which use Blip_Buffer for sound output -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef CLASSIC_EMU_H #define CLASSIC_EMU_H @@ -13,6 +13,7 @@ Classic_Emu(); ~Classic_Emu(); void set_buffer( Multi_Buffer* ); + blargg_err_t set_multi_channel( bool is_enabled ) override; protected: // Services enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; diff -Nru game-music-emu-0.6.2/gme/CMakeLists.txt game-music-emu-0.6.3/gme/CMakeLists.txt --- game-music-emu-0.6.2/gme/CMakeLists.txt 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/CMakeLists.txt 2020-02-25 01:28:10.000000000 +0000 @@ -14,6 +14,15 @@ Music_Emu.cpp ) +# static builds need to find static zlib (and static forms of other needed +# libraries. Ensure CMake looks only for static libs if we're doing a static +# build. See https://stackoverflow.com/a/44738756 +if(NOT BUILD_SHARED_LIBS) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") +endif() + +find_package(ZLIB QUIET) + # Ay_Apu is very popular around here if (USE_GME_AY OR USE_GME_KSS) set(libgme_SRCS ${libgme_SRCS} @@ -23,9 +32,25 @@ # so is Ym2612_Emu if (USE_GME_VGM OR USE_GME_GYM) - set(libgme_SRCS ${libgme_SRCS} - Ym2612_Emu.cpp - ) + if(GME_YM2612_EMU STREQUAL "Nuked") + add_definitions(-DVGM_YM2612_NUKED) + set(libgme_SRCS ${libgme_SRCS} + Ym2612_Nuked.cpp + ) + message("VGM/GYM: Nuked OPN2 emulator will be used") + elseif(GME_YM2612_EMU STREQUAL "MAME") + add_definitions(-DVGM_YM2612_MAME) + set(libgme_SRCS ${libgme_SRCS} + Ym2612_MAME.cpp + ) + message("VGM/GYM: MAME YM2612 emulator will be used") + else() + add_definitions(-DVGM_YM2612_GENS) + set(libgme_SRCS ${libgme_SRCS} + Ym2612_GENS.cpp + ) + message("VGM/GYM: GENS 2.10 emulator will be used") + endif() endif() # But none are as popular as Sms_Apu @@ -112,6 +137,9 @@ Spc_Emu.cpp Spc_Filter.cpp ) + if (GME_SPC_ISOLATED_ECHO_BUFFER) + add_definitions(-DSPC_ISOLATED_ECHO_BUFFER) + endif() endif() if (USE_GME_VGM) @@ -125,14 +153,13 @@ endif() # These headers are part of the generic gme interface. -set (EXPORTED_HEADERS gme.h) - -# Run during cmake phase, so this is available during make -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gme_types.h.in - ${CMAKE_CURRENT_BINARY_DIR}/gme_types.h) +set (EXPORTED_HEADERS gme.h blargg_source.h) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libgme.pc.in - ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc @ONLY) +# while building a macOS framework, exported headers must be in the source +# list, or the header files aren't copied to the bundle. +if (BUILD_FRAMEWORK) + set(libgme_SRCS ${libgme_SRCS} ${EXPORTED_HEADERS}) +endif() # On some platforms we may need to change headers or whatnot based on whether # we're building the library or merely using the library. The following is @@ -145,6 +172,18 @@ # Add library to be compiled. add_library(gme ${libgme_SRCS}) +if(ZLIB_FOUND) + message(" ** ZLib library located, compressed file formats will be supported") + target_compile_definitions(gme PRIVATE -DHAVE_ZLIB_H) + target_include_directories(gme PRIVATE ${ZLIB_INCLUDE_DIRS}) + target_link_libraries(gme ${ZLIB_LIBRARIES}) + # Is not to be installed though + + set(PKG_CONFIG_ZLIB -lz) # evaluated in libgme.pc.in +else() + message("ZLib library not found, disabling support for compressed formats such as VGZ") +endif() + # The version is the release. The "soversion" is the API version. As long # as only build fixes are performed (i.e. no backwards-incompatible changes # to the API), the SOVERSION should be the same even when bumping up VERSION. @@ -154,9 +193,29 @@ PROPERTIES VERSION ${GME_VERSION} SOVERSION 0) +# macOS framework build +if(BUILD_FRAMEWORK) + set_target_properties(gme + PROPERTIES FRAMEWORK TRUE + FRAMEWORK_VERSION A + MACOSX_FRAMEWORK_IDENTIFIER net.mpyne.gme + VERSION ${GME_VERSION} + SOVERSION 0 + PUBLIC_HEADER "${EXPORTED_HEADERS}") +endif() + install(TARGETS gme LIBRARY DESTINATION lib${LIB_SUFFIX} RUNTIME DESTINATION bin # DLL platforms - ARCHIVE DESTINATION lib) # DLL platforms + ARCHIVE DESTINATION lib # DLL platforms + FRAMEWORK DESTINATION /Library/Frameworks) # macOS framework + + +# Run during cmake phase, so this is available during make +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gme_types.h.in + ${CMAKE_CURRENT_BINARY_DIR}/gme_types.h) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libgme.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc @ONLY) install(FILES ${EXPORTED_HEADERS} DESTINATION include/gme) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig) diff -Nru game-music-emu-0.6.2/gme/Data_Reader.cpp game-music-emu-0.6.3/gme/Data_Reader.cpp --- game-music-emu-0.6.2/gme/Data_Reader.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Data_Reader.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -6,6 +6,7 @@ #include #include #include +#include /* Copyright (C) 2005-2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser @@ -20,6 +21,16 @@ #include "blargg_source.h" +#ifdef HAVE_ZLIB_H +#include +#include +#include +static const unsigned char gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ +#endif /* HAVE_ZLIB_H */ + +using std::min; +using std::max; + const char Data_Reader::eof_error [] = "Unexpected end of file"; #define RETURN_VALIDITY_CHECK( cond ) \ @@ -34,10 +45,10 @@ { if ( result >= 0 && result < s ) return eof_error; - + return "Read error"; } - + return 0; } @@ -106,11 +117,11 @@ long first = header_end - header; if ( first ) { - if ( first > count ) + if ( first > count || first < 0 ) first = count; void const* old = header; header += first; - memcpy( out, old, first ); + memcpy( out, old, (size_t) first ); } return first; } @@ -142,36 +153,128 @@ // Mem_File_Reader Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : - begin( (const char*) p ), - size_( max( 0l, s ) ) + m_begin( (const char*) p ), + m_size( max( 0l, s ) ), + m_pos( 0l ) +{ +#ifdef HAVE_ZLIB_H + if( !m_begin ) + return; + + if ( gz_decompress() ) + { + debug_printf( "Loaded compressed data\n" ); + m_ownedPtr = true; + } +#endif /* HAVE_ZLIB_H */ +} + +#ifdef HAVE_ZLIB_H +Mem_File_Reader::~Mem_File_Reader() { - pos = 0; + if ( m_ownedPtr ) + free( const_cast( m_begin ) ); // see gz_compress for the malloc } - -long Mem_File_Reader::size() const { return size_; } +#endif + +long Mem_File_Reader::size() const { return m_size; } long Mem_File_Reader::read_avail( void* p, long s ) { long r = remain(); - s = max( 0l, s ); - if ( s > r ) + if ( s > r || s < 0 ) s = r; - memcpy( p, begin + pos, s ); - pos += s; + memcpy( p, m_begin + m_pos, static_cast(s) ); + m_pos += s; return s; } -long Mem_File_Reader::tell() const { return pos; } +long Mem_File_Reader::tell() const { return m_pos; } blargg_err_t Mem_File_Reader::seek( long n ) { RETURN_VALIDITY_CHECK( n >= 0 ); - if ( n > size_ ) + if ( n > m_size ) return eof_error; - pos = n; + m_pos = n; return 0; } +#ifdef HAVE_ZLIB_H + +bool Mem_File_Reader::gz_decompress() +{ + if ( m_size >= 2 && memcmp(m_begin, gz_magic, 2) != 0 ) + { + /* Don't try to decompress non-GZ files, just assign input pointer */ + return false; + } + + using vec_size = size_t; + const vec_size full_length = static_cast( m_size ); + const vec_size half_length = static_cast( m_size / 2 ); + + // We use malloc/friends here so we can realloc to grow buffer if needed + char *raw_data = reinterpret_cast ( malloc( full_length ) ); + size_t raw_data_size = full_length; + if ( !raw_data ) + return false; + + z_stream strm; + strm.next_in = const_cast( reinterpret_cast( m_begin ) ); + strm.avail_in = static_cast( m_size ); + strm.total_out = 0; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + + bool done = false; + + // Adding 16 sets bit 4, which enables zlib to auto-detect the + // header. + if ( inflateInit2(&strm, (16 + MAX_WBITS)) != Z_OK ) + { + free( raw_data ); + return false; + } + + while ( !done ) + { + /* If our output buffer is too small */ + if ( strm.total_out >= raw_data_size ) + { + raw_data_size += half_length; + raw_data = reinterpret_cast( realloc( raw_data, raw_data_size ) ); + if ( !raw_data ) { + return false; + } + } + + strm.next_out = reinterpret_cast( raw_data + strm.total_out ); + strm.avail_out = static_cast( static_cast( raw_data_size ) - strm.total_out ); + + /* Inflate another chunk. */ + int err = inflate( &strm, Z_SYNC_FLUSH ); + if ( err == Z_STREAM_END ) + done = true; + else if ( err != Z_OK ) + break; + } + + if ( inflateEnd(&strm) != Z_OK ) + { + free( raw_data ); + return false; + } + + m_begin = raw_data; + m_size = static_cast( strm.total_out ); + + return true; +} + +#endif /* HAVE_ZLIB_H */ + + // Callback_Reader Callback_Reader::Callback_Reader( callback_t c, long size, void* d ) : @@ -197,25 +300,72 @@ RETURN_VALIDITY_CHECK( count >= 0 ); if ( count > remain_ ) return eof_error; - return callback( data, out, count ); + return callback( data, out, (int) count ); } // Std_File_Reader -Std_File_Reader::Std_File_Reader() : file_( 0 ) { } +#ifdef HAVE_ZLIB_H + +static const char* get_gzip_eof( const char* path, long* eof ) +{ + FILE* file = fopen( path, "rb" ); + if ( !file ) + return "Couldn't open file"; + + unsigned char buf [4]; + bool found_eof = false; + if ( fread( buf, 2, 1, file ) > 0 && buf [0] == 0x1F && buf [1] == 0x8B ) + { + fseek( file, -4, SEEK_END ); + if ( fread( buf, 4, 1, file ) > 0 ) { + *eof = get_le32( buf ); + found_eof = true; + } + } + if ( !found_eof ) + { + fseek( file, 0, SEEK_END ); + *eof = ftell( file ); + } + const char* err = (ferror( file ) || feof( file )) ? "Couldn't get file size" : nullptr; + fclose( file ); + return err; +} +#endif + + +Std_File_Reader::Std_File_Reader() : + file_( nullptr ) +#ifdef HAVE_ZLIB_H + , size_( 0 ) +#endif +{ } Std_File_Reader::~Std_File_Reader() { close(); } blargg_err_t Std_File_Reader::open( const char* path ) { +#ifdef HAVE_ZLIB_H + // zlib transparently handles uncompressed data if magic header + // not present but we still need to grab size + RETURN_ERR( get_gzip_eof( path, &size_ ) ); + file_ = gzopen( path, "rb" ); +#else file_ = fopen( path, "rb" ); +#endif + if ( !file_ ) return "Couldn't open file"; - return 0; + return nullptr; } long Std_File_Reader::size() const { +#ifdef HAVE_ZLIB_H + if ( file_ ) + return size_; // Set for both compressed and uncompressed modes +#endif long pos = tell(); fseek( (FILE*) file_, 0, SEEK_END ); long result = tell(); @@ -225,107 +375,79 @@ long Std_File_Reader::read_avail( void* p, long s ) { - return fread( p, 1, max( 0l, s ), (FILE*) file_ ); +#ifdef HAVE_ZLIB_H + if ( file_ && s > 0 && static_cast(s) <= UINT_MAX ) { + return gzread( reinterpret_cast(file_), + p, static_cast(s) ); + } + return 0l; +#else + const size_t readLength = static_cast( max( 0l, s ) ); + const auto result = fread( p, 1, readLength, reinterpret_cast(file_) ); + return static_cast( result ); +#endif /* HAVE_ZLIB_H */ } blargg_err_t Std_File_Reader::read( void* p, long s ) { - RETURN_VALIDITY_CHECK( s > 0 ); - if ( s == (long) fread( p, 1, s, (FILE*) file_ ) ) + RETURN_VALIDITY_CHECK( s > 0 && static_cast(s) <= UINT_MAX ); +#ifdef HAVE_ZLIB_H + if ( file_ ) + { + const auto &gzfile = reinterpret_cast( file_ ); + if ( s == gzread( gzfile, p, static_cast( s ) ) ) + return nullptr; + if ( gzeof( gzfile ) ) + return eof_error; + return "Couldn't read from GZ file"; + } +#endif + const auto &file = reinterpret_cast( file_ ); + if ( s == static_cast( fread( p, 1, static_cast(s), file ) ) ) return 0; - if ( feof( (FILE*) file_ ) ) + if ( feof( file ) ) return eof_error; return "Couldn't read from file"; } -long Std_File_Reader::tell() const { return ftell( (FILE*) file_ ); } - -blargg_err_t Std_File_Reader::seek( long n ) -{ - if ( !fseek( (FILE*) file_, n, SEEK_SET ) ) - return 0; - if ( n > size() ) - return eof_error; - return "Error seeking in file"; -} - -void Std_File_Reader::close() +long Std_File_Reader::tell() const { +#ifdef HAVE_ZLIB_H if ( file_ ) - { - fclose( (FILE*) file_ ); - file_ = 0; - } + return gztell( reinterpret_cast( file_ ) ); +#endif + return ftell( reinterpret_cast( file_ ) ); } -// Gzip_File_Reader - -#ifdef HAVE_ZLIB_H - -#include "zlib.h" - -static const char* get_gzip_eof( const char* path, long* eof ) +blargg_err_t Std_File_Reader::seek( long n ) { - FILE* file = fopen( path, "rb" ); - if ( !file ) - return "Couldn't open file"; - - unsigned char buf [4]; - if ( fread( buf, 2, 1, file ) > 0 && buf [0] == 0x1F && buf [1] == 0x8B ) - { - fseek( file, -4, SEEK_END ); - fread( buf, 4, 1, file ); - *eof = get_le32( buf ); - } - else +#ifdef HAVE_ZLIB_H + if ( file_ ) { - fseek( file, 0, SEEK_END ); - *eof = ftell( file ); + if ( gzseek( reinterpret_cast( file_ ), n, SEEK_SET ) >= 0 ) + return nullptr; + if ( n > size_ ) + return eof_error; + return "Error seeking in GZ file"; } - const char* err = (ferror( file ) || feof( file )) ? "Couldn't get file size" : 0; - fclose( file ); - return err; -} - -Gzip_File_Reader::Gzip_File_Reader() : file_( 0 ) { } - -Gzip_File_Reader::~Gzip_File_Reader() { close(); } - -blargg_err_t Gzip_File_Reader::open( const char* path ) -{ - close(); - - RETURN_ERR( get_gzip_eof( path, &size_ ) ); - - file_ = gzopen( path, "rb" ); - if ( !file_ ) - return "Couldn't open file"; - - return 0; -} - -long Gzip_File_Reader::size() const { return size_; } - -long Gzip_File_Reader::read_avail( void* p, long s ) { return gzread( file_, p, s ); } - -long Gzip_File_Reader::tell() const { return gztell( file_ ); } - -blargg_err_t Gzip_File_Reader::seek( long n ) -{ - if ( gzseek( file_, n, SEEK_SET ) >= 0 ) - return 0; - if ( n > size_ ) +#endif + if ( !fseek( reinterpret_cast( file_ ), n, SEEK_SET ) ) + return nullptr; + if ( n > size() ) return eof_error; return "Error seeking in file"; } -void Gzip_File_Reader::close() +void Std_File_Reader::close() { if ( file_ ) { - gzclose( file_ ); - file_ = 0; +#ifdef HAVE_ZLIB_H + gzclose( reinterpret_cast( file_ ) ); +#else + fclose( reinterpret_cast( file_ ) ); +#endif + file_ = nullptr; } } -#endif diff -Nru game-music-emu-0.6.2/gme/Data_Reader.h game-music-emu-0.6.3/gme/Data_Reader.h --- game-music-emu-0.6.2/gme/Data_Reader.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Data_Reader.h 2020-02-25 01:28:10.000000000 +0000 @@ -6,6 +6,10 @@ #include "blargg_common.h" +#ifdef HAVE_ZLIB_H +#include +#endif + // Supports reading and finding out how many bytes are remaining class Data_Reader { public: @@ -65,25 +69,39 @@ long tell() const; blargg_err_t seek( long ); private: - void* file_; + void* file_; // Either FILE* or zlib's gzFile +#ifdef HAVE_ZLIB_H + long size_; // TODO: Fix ABI compat +#endif /* HAVE_ZLIB_H */ }; // Treats range of memory as a file class Mem_File_Reader : public File_Reader { public: Mem_File_Reader( const void*, long size ); - +#ifdef HAVE_ZLIB_H + ~Mem_File_Reader( ); +#endif /* HAVE_ZLIB_H */ + public: long size() const; long read_avail( void*, long ); long tell() const; blargg_err_t seek( long ); private: - const char* const begin; - const long size_; - long pos; +#ifdef HAVE_ZLIB_H + bool gz_decompress(); +#endif /* HAVE_ZLIB_H */ + + const char* m_begin; + long m_size; + long m_pos; +#ifdef HAVE_ZLIB_H + bool m_ownedPtr = false; // set if we must free m_begin +#endif /* HAVE_ZLIB_H */ }; + // Makes it look like there are only count bytes remaining class Subset_Reader : public Data_Reader { public: @@ -128,26 +146,4 @@ long remain_; }; -#ifdef HAVE_ZLIB_H -#include - -// Gzip compressed file reader -class Gzip_File_Reader : public File_Reader { -public: - blargg_err_t open( const char* path ); - void close(); - -public: - Gzip_File_Reader(); - ~Gzip_File_Reader(); - long size() const; - long read_avail( void*, long ); - long tell() const; - blargg_err_t seek( long ); -private: - gzFile file_; - long size_; -}; -#endif - #endif diff -Nru game-music-emu-0.6.2/gme/Dual_Resampler.cpp game-music-emu-0.6.3/gme/Dual_Resampler.cpp --- game-music-emu-0.6.2/gme/Dual_Resampler.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Dual_Resampler.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Dual_Resampler.h" @@ -68,9 +68,13 @@ assert( blip_buf.samples_avail() == pair_count ); resampler.write( new_count ); - + +#ifdef NDEBUG // Avoid warning when asserts are disabled + resampler.read( sample_buf.begin(), sample_buf_size ); +#else long count = resampler.read( sample_buf.begin(), sample_buf_size ); assert( count == (long) sample_buf_size ); +#endif mix_samples( blip_buf, out ); blip_buf.remove_samples( pair_count ); @@ -118,12 +122,12 @@ { int s = sn.read(); blargg_long l = (blargg_long) in [0] * 2 + s; - if ( (BOOST::int16_t) l != l ) + if ( (int16_t) l != l ) l = 0x7FFF - (l >> 24); sn.next( bass ); blargg_long r = (blargg_long) in [1] * 2 + s; - if ( (BOOST::int16_t) r != r ) + if ( (int16_t) r != r ) r = 0x7FFF - (r >> 24); in += 2; diff -Nru game-music-emu-0.6.2/gme/Dual_Resampler.h game-music-emu-0.6.3/gme/Dual_Resampler.h --- game-music-emu-0.6.2/gme/Dual_Resampler.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Dual_Resampler.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Combination of Fir_Resampler and Blip_Buffer mixing. Used by Sega FM emulators. -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef DUAL_RESAMPLER_H #define DUAL_RESAMPLER_H diff -Nru game-music-emu-0.6.2/gme/Effects_Buffer.cpp game-music-emu-0.6.3/gme/Effects_Buffer.cpp --- game-music-emu-0.6.2/gme/Effects_Buffer.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Effects_Buffer.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,8 +1,9 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Effects_Buffer.h" #include +#include /* Copyright (C) 2003-2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser @@ -23,6 +24,9 @@ typedef blargg_long fixed_t; +using std::min; +using std::max; + #define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5) #define FMUL( x, y ) (((x) * (y)) >> 15) @@ -63,29 +67,49 @@ config( c ); } -Effects_Buffer::Effects_Buffer( bool center_only ) : Multi_Buffer( 2 ) +Effects_Buffer::Effects_Buffer( int num_voices, bool center_only ) + : Multi_Buffer( 2*num_voices ) + , max_voices(num_voices) + , bufs(max_voices * (center_only ? (max_buf_count - 4) : max_buf_count)) + , chan_types(max_voices * chan_types_count) + , stereo_remain(0) + , effect_remain(0) + // TODO: Reorder buf_count to be initialized before bufs to factor out channel sizing + , buf_count(max_voices * (center_only ? (max_buf_count - 4) : max_buf_count)) + , effects_enabled(false) + , reverb_buf(max_voices, std::vector(reverb_size)) + , echo_buf(max_voices, std::vector(echo_size)) + , reverb_pos(max_voices) + , echo_pos(max_voices) { - buf_count = center_only ? max_buf_count - 4 : max_buf_count; - - echo_pos = 0; - reverb_pos = 0; - - stereo_remain = 0; - effect_remain = 0; - effects_enabled = false; set_depth( 0 ); } -Effects_Buffer::~Effects_Buffer() { } +Effects_Buffer::~Effects_Buffer() +{} blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) { - if ( !echo_buf.size() ) - RETURN_ERR( echo_buf.resize( echo_size ) ); - - if ( !reverb_buf.size() ) - RETURN_ERR( reverb_buf.resize( reverb_size ) ); - + try + { + for(int i=0; i 2 ) - out = 2; + if ( out > chan_types_count-1 ) + out = chan_types_count-1; } else if ( !(type & noise_type) && (type & type_index_mask) % 3 != 0 ) { out = type & 1; } - return chan_types [out]; + return chan_types [(i%max_voices)*chan_types_count+out]; } void Effects_Buffer::end_frame( blip_time_t clock_count ) { int bufs_used = 0; - for ( int i = 0; i < buf_count; i++ ) + int stereo_mask = (config_.effects_enabled ? 0x78 : 0x06); + + const int buf_count_per_voice = buf_count/max_voices; + for ( int v = 0; v < max_voices; v++ ) // foreach voice { - bufs_used |= bufs [i].clear_modified() << i; - bufs [i].end_frame( clock_count ); + for ( int i = 0; i < buf_count_per_voice; i++) // foreach buffer of that voice + { + bufs_used |= bufs [v*buf_count_per_voice + i].clear_modified() << i; + bufs [v*buf_count_per_voice + i].end_frame( clock_count ); + + if ( (bufs_used & stereo_mask) && buf_count == max_voices*max_buf_count ) + stereo_remain = max(stereo_remain, bufs [v*buf_count_per_voice + i].samples_avail() + bufs [v*buf_count_per_voice + i].output_latency()); + if ( effects_enabled || config_.effects_enabled ) + effect_remain = max(effect_remain, bufs [v*buf_count_per_voice + i].samples_avail() + bufs [v*buf_count_per_voice + i].output_latency()); + } + bufs_used = 0; } - int stereo_mask = (config_.effects_enabled ? 0x78 : 0x06); - if ( (bufs_used & stereo_mask) && buf_count == max_buf_count ) - stereo_remain = bufs [0].samples_avail() + bufs [0].output_latency(); - - if ( effects_enabled || config_.effects_enabled ) - effect_remain = bufs [0].samples_avail() + bufs [0].output_latency(); - effects_enabled = config_.effects_enabled; } @@ -248,15 +295,17 @@ long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples ) { - require( total_samples % 2 == 0 ); // count must be even - + const int n_channels = max_voices * 2; + const int buf_count_per_voice = buf_count/max_voices; + + require( total_samples % n_channels == 0 ); // as many items needed to fill at least one frame + long remain = bufs [0].samples_avail(); - if ( remain > (total_samples >> 1) ) - remain = (total_samples >> 1); - total_samples = remain; + total_samples = remain = min( remain, total_samples/n_channels ); + while ( remain ) { - int active_bufs = buf_count; + int active_bufs = buf_count_per_voice; long count = remain; // optimizing mixing to skip any channels which had nothing added @@ -286,7 +335,7 @@ active_bufs = 1; } - out += count * 2; + out += count * n_channels; remain -= count; stereo_remain -= count; @@ -297,23 +346,31 @@ if ( effect_remain < 0 ) effect_remain = 0; - for ( int i = 0; i < buf_count; i++ ) + // skip the output from any buffers that didn't contribute to the sound output + // during this frame (e.g. if we only render mono then only the very first buf + // is 'active') + for ( int v = 0; v < max_voices; v++ ) // foreach voice { - if ( i < active_bufs ) - bufs [i].remove_samples( count ); - else - bufs [i].remove_silence( count ); // keep time synchronized + for ( int i = 0; i < buf_count_per_voice; i++) // foreach buffer of that voice + { + if ( i < active_bufs ) + bufs [v*buf_count_per_voice + i].remove_samples( count ); + else // keep time synchronized + bufs [v*buf_count_per_voice + i].remove_silence( count ); + } } } - return total_samples * 2; + return total_samples * n_channels; } void Effects_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) { + for(int i=0; i> 1; n; --n ) @@ -324,41 +381,45 @@ blargg_long cs1 = BLIP_READER_READ( c ); BLIP_READER_NEXT( c, bass ); - if ( (BOOST::int16_t) cs0 != cs0 ) + if ( (int16_t) cs0 != cs0 ) cs0 = 0x7FFF - (cs0 >> 24); - ((BOOST::uint32_t*) out) [0] = ((BOOST::uint16_t) cs0) | (cs0 << 16); + ((uint32_t*) out) [i*2+0] = ((uint16_t) cs0) | (uint16_t(cs0) << 16); - if ( (BOOST::int16_t) cs1 != cs1 ) + if ( (int16_t) cs1 != cs1 ) cs1 = 0x7FFF - (cs1 >> 24); - ((BOOST::uint32_t*) out) [1] = ((BOOST::uint16_t) cs1) | (cs1 << 16); - out += 4; + ((uint32_t*) out) [i*2+1] = ((uint16_t) cs1) | (uint16_t(cs1) << 16); + out += max_voices*4; } if ( count & 1 ) { int s = BLIP_READER_READ( c ); BLIP_READER_NEXT( c, bass ); - out [0] = s; - out [1] = s; - if ( (BOOST::int16_t) s != s ) + out [i*2+0] = s; + out [i*2+1] = s; + if ( (int16_t) s != s ) { s = 0x7FFF - (s >> 24); - out [0] = s; - out [1] = s; + out [i*2+0] = s; + out [i*2+1] = s; } } - BLIP_READER_END( c, bufs [0] ); + BLIP_READER_END( c, bufs [i*max_buf_count+0] ); + } } -void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) +void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long frames ) { + for(int i=0; i> 24); - out [0] = left; - out [1] = right; + if ( (int16_t) right != right ) + right = 0x7FFF - (right >> 24); + + out [i*2+0] = left; + out [i*2+1] = right; - out += 2; + out += max_voices*2; - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); } - BLIP_READER_END( r, bufs [2] ); - BLIP_READER_END( l, bufs [1] ); - BLIP_READER_END( c, bufs [0] ); + BLIP_READER_END( r, bufs [i*max_buf_count+2] ); + BLIP_READER_END( l, bufs [i*max_buf_count+1] ); + BLIP_READER_END( c, bufs [i*max_buf_count+0] ); + } } -void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long count ) +void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long frames ) { + for(int i=0; ireverb_buf.begin(); - blip_sample_t* const echo_buf = this->echo_buf.begin(); - int echo_pos = this->echo_pos; - int reverb_pos = this->reverb_pos; + int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+2] ); + BLIP_READER_BEGIN( center, bufs [i*max_buf_count+2] ); + BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] ); + BLIP_READER_BEGIN( sq2, bufs [i*max_buf_count+1] ); + + blip_sample_t* const reverb_buf = &this->reverb_buf[i][0]; + blip_sample_t* const echo_buf = &this->echo_buf[i][0]; + int echo_pos = this->echo_pos[i]; + int reverb_pos = this->reverb_pos[i]; + int count = frames; while ( count-- ) { int sum1_s = BLIP_READER_READ( sq1 ); @@ -430,42 +496,45 @@ echo_buf [echo_pos] = sum3_s; echo_pos = (echo_pos + 1) & echo_mask; - if ( (BOOST::int16_t) left != left ) + if ( (int16_t) left != left ) left = 0x7FFF - (left >> 24); - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); + if ( (int16_t) right != right ) + right = 0x7FFF - (right >> 24); + + out [i*2+0] = left; + out [i*2+1] = right; + out += max_voices*2; } - this->reverb_pos = reverb_pos; - this->echo_pos = echo_pos; + this->reverb_pos[i] = reverb_pos; + this->echo_pos[i] = echo_pos; - BLIP_READER_END( sq1, bufs [0] ); - BLIP_READER_END( sq2, bufs [1] ); - BLIP_READER_END( center, bufs [2] ); + BLIP_READER_END( sq1, bufs [i*max_buf_count+0] ); + BLIP_READER_END( sq2, bufs [i*max_buf_count+1] ); + BLIP_READER_END( center, bufs [i*max_buf_count+2] ); + } } -void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long count ) +void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long frames ) { + for(int i=0; ireverb_buf.begin(); - blip_sample_t* const echo_buf = this->echo_buf.begin(); - int echo_pos = this->echo_pos; - int reverb_pos = this->reverb_pos; + int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+2] ); + BLIP_READER_BEGIN( center, bufs [i*max_buf_count+2] ); + BLIP_READER_BEGIN( l1, bufs [i*max_buf_count+3] ); + BLIP_READER_BEGIN( r1, bufs [i*max_buf_count+4] ); + BLIP_READER_BEGIN( l2, bufs [i*max_buf_count+5] ); + BLIP_READER_BEGIN( r2, bufs [i*max_buf_count+6] ); + BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] ); + BLIP_READER_BEGIN( sq2, bufs [i*max_buf_count+1] ); + + blip_sample_t* const reverb_buf = &this->reverb_buf[i][0]; + blip_sample_t* const echo_buf = &this->echo_buf[i][0]; + int echo_pos = this->echo_pos[i]; + int reverb_pos = this->reverb_pos[i]; + int count = frames; while ( count-- ) { int sum1_s = BLIP_READER_READ( sq1 ); @@ -504,26 +573,27 @@ echo_buf [echo_pos] = sum3_s; echo_pos = (echo_pos + 1) & echo_mask; - if ( (BOOST::int16_t) left != left ) + if ( (int16_t) left != left ) left = 0x7FFF - (left >> 24); - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); + if ( (int16_t) right != right ) + right = 0x7FFF - (right >> 24); + + out [i*2+0] = left; + out [i*2+1] = right; + + out += max_voices*2; } - this->reverb_pos = reverb_pos; - this->echo_pos = echo_pos; - - BLIP_READER_END( l1, bufs [3] ); - BLIP_READER_END( r1, bufs [4] ); - BLIP_READER_END( l2, bufs [5] ); - BLIP_READER_END( r2, bufs [6] ); - BLIP_READER_END( sq1, bufs [0] ); - BLIP_READER_END( sq2, bufs [1] ); - BLIP_READER_END( center, bufs [2] ); + this->reverb_pos[i] = reverb_pos; + this->echo_pos[i] = echo_pos; + + BLIP_READER_END( l1, bufs [i*max_buf_count+3] ); + BLIP_READER_END( r1, bufs [i*max_buf_count+4] ); + BLIP_READER_END( l2, bufs [i*max_buf_count+5] ); + BLIP_READER_END( r2, bufs [i*max_buf_count+6] ); + BLIP_READER_END( sq1, bufs [i*max_buf_count+0] ); + BLIP_READER_END( sq2, bufs [i*max_buf_count+1] ); + BLIP_READER_END( center, bufs [i*max_buf_count+2] ); + } } diff -Nru game-music-emu-0.6.2/gme/Effects_Buffer.h game-music-emu-0.6.3/gme/Effects_Buffer.h --- game-music-emu-0.6.2/gme/Effects_Buffer.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Effects_Buffer.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,17 +1,21 @@ // Multi-channel effects buffer with panning, echo and reverb -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef EFFECTS_BUFFER_H #define EFFECTS_BUFFER_H #include "Multi_Buffer.h" +#include + // Effects_Buffer uses several buffers and outputs stereo sample pairs. class Effects_Buffer : public Multi_Buffer { public: + // nVoices indicates the number of voices for which buffers will be allocated + // to make Effects_Buffer work as "mix everything to one", nVoices will be 1 // If center_only is true, only center buffers are created and // less memory is used. - Effects_Buffer( bool center_only = false ); + Effects_Buffer( int nVoices = 1, bool center_only = false ); // Channel Effect Center Pan // --------------------------------- @@ -50,21 +54,21 @@ long samples_avail() const; private: typedef long fixed_t; - + int max_voices; enum { max_buf_count = 7 }; - Blip_Buffer bufs [max_buf_count]; + std::vector bufs; enum { chan_types_count = 3 }; - channel_t chan_types [3]; + std::vector chan_types; config_t config_; long stereo_remain; long effect_remain; int buf_count; bool effects_enabled; - blargg_vector reverb_buf; - blargg_vector echo_buf; - int reverb_pos; - int echo_pos; + std::vector > reverb_buf; + std::vector > echo_buf; + std::vector reverb_pos; + std::vector echo_pos; struct { fixed_t pan_1_levels [2]; diff -Nru game-music-emu-0.6.2/gme/Fir_Resampler.cpp game-music-emu-0.6.3/gme/Fir_Resampler.cpp --- game-music-emu-0.6.2/gme/Fir_Resampler.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Fir_Resampler.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Fir_Resampler.h" diff -Nru game-music-emu-0.6.2/gme/Fir_Resampler.h game-music-emu-0.6.3/gme/Fir_Resampler.h --- game-music-emu-0.6.2/gme/Fir_Resampler.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Fir_Resampler.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Finite impulse response (FIR) resampler with adjustable FIR size -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef FIR_RESAMPLER_H #define FIR_RESAMPLER_H @@ -109,51 +109,66 @@ count >>= 1; + // Resampling can add noise so don't actually do it if we've matched sample + // rate + const double ratio1 = ratio() - 1.0; + const bool should_resample = + ( ratio1 >= 0 ? ratio1 : -ratio1 ) >= 0.00001; + if ( end_pos - in >= width * stereo ) { end_pos -= width * stereo; do { count--; - - // accumulate in extended precision - blargg_long l = 0; - blargg_long r = 0; - - const sample_t* i = in; if ( count < 0 ) break; - for ( int n = width / 2; n; --n ) + if( !should_resample ) { - int pt0 = imp [0]; - l += pt0 * i [0]; - r += pt0 * i [1]; - int pt1 = imp [1]; - imp += 2; - l += pt1 * i [2]; - r += pt1 * i [3]; - i += 4; + out [0] = static_cast( in [0] ); + out [1] = static_cast( in [1] ); } - - remain--; - - l >>= 15; - r >>= 15; - - in += (skip * stereo) & stereo; - skip >>= 1; - in += step; - - if ( !remain ) + else { - imp = impulses [0]; - skip = skip_bits; - remain = res; + // accumulate in extended precision + blargg_long l = 0; + blargg_long r = 0; + + const sample_t* i = in; + + for ( int n = width / 2; n; --n ) + { + int pt0 = imp [0]; + l += pt0 * i [0]; + r += pt0 * i [1]; + int pt1 = imp [1]; + imp += 2; + l += pt1 * i [2]; + r += pt1 * i [3]; + i += 4; + } + + remain--; + + l >>= 15; + r >>= 15; + + in += (skip * stereo) & stereo; + skip >>= 1; + + if ( !remain ) + { + imp = impulses [0]; + skip = skip_bits; + remain = res; + } + + out [0] = (sample_t) l; + out [1] = (sample_t) r; } - out [0] = (sample_t) l; - out [1] = (sample_t) r; + in += step; out += 2; } while ( in <= end_pos ); diff -Nru game-music-emu-0.6.2/gme/Gb_Apu.cpp game-music-emu-0.6.3/gme/Gb_Apu.cpp --- game-music-emu-0.6.2/gme/Gb_Apu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gb_Apu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -3,6 +3,7 @@ #include "Gb_Apu.h" #include +#include /* Copyright (C) 2003-2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser @@ -20,6 +21,9 @@ unsigned const vol_reg = 0xFF24; unsigned const status_reg = 0xFF26; +using std::min; +using std::max; + Gb_Apu::Gb_Apu() { square1.synth = &square_synth; @@ -123,7 +127,7 @@ 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table 0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA }; - memcpy( wave.wave, initial_wave, sizeof wave.wave ); + memcpy( wave.wave, initial_wave, sizeof initial_wave ); } void Gb_Apu::run_until( blip_time_t end_time ) diff -Nru game-music-emu-0.6.2/gme/Gb_Apu.h game-music-emu-0.6.3/gme/Gb_Apu.h --- game-music-emu-0.6.2/gme/Gb_Apu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gb_Apu.h 2020-02-25 01:28:10.000000000 +0000 @@ -68,7 +68,7 @@ Gb_Square square2; Gb_Wave wave; Gb_Noise noise; - BOOST::uint8_t regs [register_count]; + uint8_t regs [register_count]; Gb_Square::Synth square_synth; // used by squares Gb_Wave::Synth other_synth; // used by wave and noise diff -Nru game-music-emu-0.6.2/gme/Gb_Cpu.cpp game-music-emu-0.6.3/gme/Gb_Cpu.cpp --- game-music-emu-0.6.2/gme/Gb_Cpu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gb_Cpu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Gb_Cpu.h" @@ -96,8 +96,6 @@ this->state = &s; memcpy( &s, &this->state_, sizeof s ); - typedef BOOST::uint16_t uint16_t; - #if BLARGG_BIG_ENDIAN #define R8( n ) (r8_ [n]) #elif BLARGG_LITTLE_ENDIAN @@ -110,11 +108,11 @@ core_regs_t rg; // individual registers struct { - BOOST::uint16_t bc, de, hl, unused; // pairs + uint16_t bc, de, hl, unused; // pairs } rp; uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence) - BOOST::uint16_t r16 [4]; // indexed pairs + uint16_t r16 [4]; // indexed pairs }; BOOST_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 ); @@ -162,7 +160,7 @@ #define BRANCH( cond )\ {\ pc++;\ - int offset = (BOOST::int8_t) data;\ + int offset = (int8_t) data;\ if ( !(cond) ) goto loop;\ pc = uint16_t (pc + offset);\ goto loop;\ @@ -262,7 +260,7 @@ if ( flags & z_flag ) goto loop; call: - pc -= 2; + pc -= 2; // FALLTHRU case 0xCD: // CALL (most-common) data = pc + 2; pc = GET_ADDR(); @@ -276,6 +274,7 @@ case 0xC8: // RNZ (next-most-common) if ( !(flags & z_flag) ) goto loop; + // FALLTHRU case 0xC9: // RET (most common) ret: pc = READ( sp ); @@ -433,7 +432,7 @@ goto rl_comm; case 0x3E: // SRL (HL) - data += 0x10; // bump up to 0x4n to avoid preserving sign bit + data += 0x10; /* fallthrough */ // bump up to 0x4n to avoid preserving sign bit case 0x1E: // RR (HL) case 0x0E: // RRC (HL) case 0x2E: // SRA (HL) @@ -441,7 +440,7 @@ goto rr_comm; case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL A - data += 0x10; // bump up to 0x4n + data += 0x10; /* fallthrough */ // bump up to 0x4n case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR A case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC A case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA A @@ -450,6 +449,7 @@ } // CB op assert( false ); // unhandled CB op + // fallthrough case 0x07: // RLCA case 0x17: // RLA @@ -682,7 +682,7 @@ unsigned prev; case 0xF8: // LD HL,SP+imm - temp = BOOST::int8_t (data); // sign-extend to 16 bits + temp = int8_t (data); // sign-extend to 16 bits pc++; flags = 0; temp += sp; @@ -690,7 +690,7 @@ goto add_16_hl; case 0xE8: // ADD SP,IMM - temp = BOOST::int8_t (data); // sign-extend to 16 bits + temp = int8_t (data); // sign-extend to 16 bits pc++; flags = 0; temp += sp; @@ -824,11 +824,11 @@ case 0xA6: // AND (HL) data = READ( rp.hl ); - pc--; + pc--; // FALLTHRU case 0xE6: // AND IMM pc++; and_comm: - rg.a &= data; + rg.a &= data; // FALLTHRU case 0xA7: // AND A flags = h_flag | (((rg.a - 1) >> 1) & z_flag); goto loop; @@ -844,11 +844,11 @@ case 0xB6: // OR (HL) data = READ( rp.hl ); - pc--; + pc--; // FALLTHRU case 0xF6: // OR IMM pc++; or_comm: - rg.a |= data; + rg.a |= data; // FALLTHRU case 0xB7: // OR A flags = ((rg.a - 1) >> 1) & z_flag; goto loop; @@ -864,7 +864,7 @@ case 0xAE: // XOR (HL) data = READ( rp.hl ); - pc--; + pc--; // FALLTHRU case 0xEE: // XOR IMM pc++; xor_comm: @@ -914,6 +914,7 @@ case 0xFF: if ( pc == idle_addr + 1 ) goto stop; + // FALLTHRU case 0xC7: case 0xCF: case 0xD7: case 0xDF: // RST case 0xE7: case 0xEF: case 0xF7: data = pc; diff -Nru game-music-emu-0.6.2/gme/Gb_Cpu.h game-music-emu-0.6.3/gme/Gb_Cpu.h --- game-music-emu-0.6.2/gme/Gb_Cpu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gb_Cpu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,7 +1,7 @@ // Nintendo Game Boy CPU emulator // Treats every instruction as taking 4 cycles -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef GB_CPU_H #define GB_CPU_H @@ -13,8 +13,6 @@ class Gb_Cpu { enum { clocks_per_instr = 4 }; public: - typedef BOOST::uint8_t uint8_t; - // Clear registers and map all pages to unmapped void reset( void* unmapped = 0 ); @@ -39,7 +37,7 @@ struct registers_t : core_regs_t { long pc; // more than 16 bits to allow overflow detection - BOOST::uint16_t sp; + uint16_t sp; }; registers_t r; @@ -81,7 +79,7 @@ void set_code_page( int, uint8_t* ); }; -inline BOOST::uint8_t* Gb_Cpu::get_code( gb_addr_t addr ) +inline uint8_t* Gb_Cpu::get_code( gb_addr_t addr ) { return state->code_map [addr >> page_shift] + addr #if !BLARGG_NONPORTABLE diff -Nru game-music-emu-0.6.2/gme/Gb_Oscs.cpp game-music-emu-0.6.3/gme/Gb_Oscs.cpp --- game-music-emu-0.6.2/gme/Gb_Oscs.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gb_Oscs.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -312,7 +312,7 @@ switch ( index ) { case 0: - sq = &square1; + sq = &square1; // FALLTHRU case 1: if ( sq->write_register( reg, data ) && index == 0 ) { diff -Nru game-music-emu-0.6.2/gme/Gb_Oscs.h game-music-emu-0.6.3/gme/Gb_Oscs.h --- game-music-emu-0.6.2/gme/Gb_Oscs.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gb_Oscs.h 2020-02-25 01:28:10.000000000 +0000 @@ -15,7 +15,7 @@ Blip_Buffer* outputs [4]; // NULL, right, left, center Blip_Buffer* output; int output_select; - BOOST::uint8_t* regs; // osc's 5 registers + uint8_t* regs; // osc's 5 registers int delay; int last_amp; @@ -68,7 +68,7 @@ Synth const* synth; int wave_pos; enum { wave_size = 32 }; - BOOST::uint8_t wave [wave_size]; + uint8_t wave [wave_size]; void write_register( int, int ); void run( blip_time_t, blip_time_t, int playing ); diff -Nru game-music-emu-0.6.2/gme/Gbs_Emu.cpp game-music-emu-0.6.3/gme/Gbs_Emu.cpp --- game-music-emu-0.6.2/gme/Gbs_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gbs_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Gbs_Emu.h" @@ -101,7 +101,7 @@ static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; } static gme_type_t_ const gme_gbs_type_ = { "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }; -gme_type_t const gme_gbs_type = &gme_gbs_type_; +extern gme_type_t const gme_gbs_type = &gme_gbs_type_; // Setup @@ -175,7 +175,7 @@ play_period = blip_time_t (play_period / tempo()); } -static BOOST::uint8_t const sound_data [Gb_Apu::register_count] = { +static uint8_t const sound_data [Gb_Apu::register_count] = { 0x80, 0xBF, 0x00, 0x00, 0xBF, // square 1 0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2 0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave diff -Nru game-music-emu-0.6.2/gme/Gbs_Emu.h game-music-emu-0.6.3/gme/Gbs_Emu.h --- game-music-emu-0.6.2/gme/Gbs_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gbs_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Nintendo Game Boy GBS music file emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef GBS_EMU_H #define GBS_EMU_H diff -Nru game-music-emu-0.6.2/gme/gme.cpp game-music-emu-0.6.3/gme/gme.cpp --- game-music-emu-0.6.2/gme/gme.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/gme.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Music_Emu.h" @@ -23,7 +23,7 @@ #include "blargg_source.h" -BLARGG_EXPORT gme_type_t const* gme_type_list() +gme_type_t const* gme_type_list() { static gme_type_t const gme_type_list_ [] = { #ifdef GME_TYPE_LIST @@ -67,7 +67,7 @@ return gme_type_list_; } -BLARGG_EXPORT const char* gme_identify_header( void const* header ) +const char* gme_identify_header( void const* header ) { switch ( get_be32( header ) ) { @@ -83,6 +83,8 @@ case BLARGG_4CHAR('S','N','E','S'): return "SPC"; case BLARGG_4CHAR('V','g','m',' '): return "VGM"; } + if (get_be16(header) == BLARGG_2CHAR(0x1F, 0x8B)) + return "VGZ"; return ""; } @@ -96,22 +98,30 @@ *out = 0; // extension too long } -BLARGG_EXPORT gme_type_t gme_identify_extension( const char* extension_ ) +gme_type_t gme_identify_extension( const char* extension_ ) { char const* end = strrchr( extension_, '.' ); if ( end ) extension_ = end + 1; - + char extension [6]; to_uppercase( extension_, sizeof extension, extension ); - + for ( gme_type_t const* types = gme_type_list(); *types; types++ ) if ( !strcmp( extension, (*types)->extension_ ) ) return *types; return 0; } -BLARGG_EXPORT gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ) +const char *gme_type_extension( gme_type_t music_type ) +{ + const gme_type_t_ *const music_typeinfo = static_cast( music_type ); + if ( music_type ) + return music_typeinfo->extension_; + return ""; +} + +gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ) { *type_out = gme_identify_extension( path ); // TODO: don't examine header if file has extension? @@ -123,44 +133,44 @@ RETURN_ERR( in.read( header, sizeof header ) ); *type_out = gme_identify_extension( gme_identify_header( header ) ); } - return 0; + return 0; } -BLARGG_EXPORT gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, int sample_rate ) +gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, int sample_rate ) { require( (data || !size) && out ); *out = 0; - + gme_type_t file_type = 0; if ( size >= 4 ) file_type = gme_identify_extension( gme_identify_header( data ) ); if ( !file_type ) return gme_wrong_file_type; - + Music_Emu* emu = gme_new_emu( file_type, sample_rate ); CHECK_ALLOC( emu ); - + gme_err_t err = gme_load_data( emu, data, size ); - + if ( err ) delete emu; else *out = emu; - + return err; } -BLARGG_EXPORT gme_err_t gme_open_file( const char* path, Music_Emu** out, int sample_rate ) +gme_err_t gme_open_file( const char* path, Music_Emu** out, int sample_rate ) { require( path && out ); *out = 0; - + GME_FILE_READER in; RETURN_ERR( in.open( path ) ); - + char header [4]; int header_size = 0; - + gme_type_t file_type = gme_identify_extension( path ); if ( !file_type ) { @@ -170,41 +180,61 @@ } if ( !file_type ) return gme_wrong_file_type; - + Music_Emu* emu = gme_new_emu( file_type, sample_rate ); CHECK_ALLOC( emu ); - + // optimization: avoids seeking/re-reading header Remaining_Reader rem( header, header_size, &in ); gme_err_t err = emu->load( rem ); in.close(); - + if ( err ) delete emu; else *out = emu; - + return err; } -BLARGG_EXPORT Music_Emu* gme_new_emu( gme_type_t type, int rate ) +void gme_set_autoload_playback_limit( Music_Emu *emu, int do_autoload_limit ) +{ + emu->set_autoload_playback_limit( do_autoload_limit != 0 ); +} + +int gme_autoload_playback_limit( Music_Emu *const emu ) +{ + return emu->autoload_playback_limit(); +} + +// Used to implement gme_new_emu and gme_new_emu_multi_channel +Music_Emu* gme_internal_new_emu_( gme_type_t type, int rate, bool multi_channel ) { if ( type ) { if ( rate == gme_info_only ) return type->new_info(); - + Music_Emu* me = type->new_emu(); if ( me ) { #if !GME_DISABLE_STEREO_DEPTH + me->set_multi_channel( multi_channel ); + if ( type->flags_ & 1 ) { - me->effects_buffer = BLARGG_NEW Effects_Buffer; + if ( me->multi_channel() ) + { + me->effects_buffer = BLARGG_NEW Effects_Buffer(8); + } + else + { + me->effects_buffer = BLARGG_NEW Effects_Buffer(1); + } if ( me->effects_buffer ) me->set_buffer( me->effects_buffer ); } - + if ( !(type->flags_ & 1) || me->effects_buffer ) #endif { @@ -220,55 +250,66 @@ return 0; } -BLARGG_EXPORT gme_err_t gme_load_file( Music_Emu* me, const char* path ) { return me->load_file( path ); } +Music_Emu* gme_new_emu( gme_type_t type, int rate ) +{ + return gme_internal_new_emu_( type, rate, false /* no multichannel */); +} + +Music_Emu* gme_new_emu_multi_channel( gme_type_t type, int rate ) +{ + // multi-channel emulator (if possible, not all emu types support multi-channel) + return gme_internal_new_emu_( type, rate, true /* multichannel */); +} + +gme_err_t gme_load_file( Music_Emu* me, const char* path ) { return me->load_file( path ); } -BLARGG_EXPORT gme_err_t gme_load_data( Music_Emu* me, void const* data, long size ) +gme_err_t gme_load_data( Music_Emu* me, void const* data, long size ) { Mem_File_Reader in( data, size ); return me->load( in ); } -BLARGG_EXPORT gme_err_t gme_load_custom( Music_Emu* me, gme_reader_t func, long size, void* data ) +gme_err_t gme_load_custom( Music_Emu* me, gme_reader_t func, long size, void* data ) { Callback_Reader in( func, size, data ); return me->load( in ); } -BLARGG_EXPORT void gme_delete( Music_Emu* me ) { delete me; } +void gme_delete( Music_Emu* me ) { delete me; } -BLARGG_EXPORT gme_type_t gme_type( Music_Emu const* me ) { return me->type(); } +gme_type_t gme_type( Music_Emu const* me ) { return me->type(); } -BLARGG_EXPORT const char* gme_warning( Music_Emu* me ) { return me->warning(); } +const char* gme_warning( Music_Emu* me ) { return me->warning(); } -BLARGG_EXPORT int gme_track_count( Music_Emu const* me ) { return me->track_count(); } +int gme_track_count( Music_Emu const* me ) { return me->track_count(); } struct gme_info_t_ : gme_info_t { track_info_t info; - + BLARGG_DISABLE_NOTHROW }; -BLARGG_EXPORT gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, int track ) +gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, int track ) { *out = NULL; - + gme_info_t_* info = BLARGG_NEW gme_info_t_; CHECK_ALLOC( info ); - + gme_err_t err = me->track_info( &info->info, track ); if ( err ) { gme_free_info( info ); return err; } - + #define COPY(name) info->name = info->info.name; - + COPY( length ); COPY( intro_length ); COPY( loop_length ); - + info->i4 = -1; info->i5 = -1; info->i6 = -1; @@ -281,7 +322,7 @@ info->i13 = -1; info->i14 = -1; info->i15 = -1; - + info->s7 = ""; info->s8 = ""; info->s9 = ""; @@ -291,7 +332,7 @@ info->s13 = ""; info->s14 = ""; info->s15 = ""; - + COPY( system ); COPY( game ); COPY( song ); @@ -299,9 +340,9 @@ COPY( copyright ); COPY( comment ); COPY( dumper ); - + #undef COPY - + info->play_length = info->length; if ( info->play_length <= 0 ) { @@ -309,18 +350,18 @@ if ( info->play_length <= 0 ) info->play_length = 150 * 1000; // 2.5 minutes } - + *out = info; - + return 0; } -BLARGG_EXPORT void gme_free_info( gme_info_t* info ) +void gme_free_info( gme_info_t* info ) { delete STATIC_CAST(gme_info_t_*,info); } -BLARGG_EXPORT void gme_set_stereo_depth( Music_Emu* me, double depth ) +void gme_set_stereo_depth( Music_Emu* me, double depth ) { #if !GME_DISABLE_STEREO_DEPTH if ( me->effects_buffer ) @@ -328,28 +369,29 @@ #endif } -BLARGG_EXPORT void* gme_user_data ( Music_Emu const* me ) { return me->user_data(); } -BLARGG_EXPORT void gme_set_user_data ( Music_Emu* me, void* new_user_data ) { me->set_user_data( new_user_data ); } -BLARGG_EXPORT void gme_set_user_cleanup(Music_Emu* me, gme_user_cleanup_t func ) { me->set_user_cleanup( func ); } - -BLARGG_EXPORT gme_err_t gme_start_track ( Music_Emu* me, int index ) { return me->start_track( index ); } -BLARGG_EXPORT gme_err_t gme_play ( Music_Emu* me, int n, short* p ) { return me->play( n, p ); } -BLARGG_EXPORT void gme_set_fade ( Music_Emu* me, int start_msec ) { me->set_fade( start_msec ); } -BLARGG_EXPORT int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); } -BLARGG_EXPORT int gme_tell ( Music_Emu const* me ) { return me->tell(); } -BLARGG_EXPORT int gme_tell_samples ( Music_Emu const* me ) { return me->tell_samples(); } -BLARGG_EXPORT gme_err_t gme_seek ( Music_Emu* me, int msec ) { return me->seek( msec ); } -BLARGG_EXPORT gme_err_t gme_seek_samples ( Music_Emu* me, int n ) { return me->seek_samples( n ); } -BLARGG_EXPORT int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); } -BLARGG_EXPORT void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); } -BLARGG_EXPORT void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); } -BLARGG_EXPORT void gme_mute_voice ( Music_Emu* me, int index, int mute ) { me->mute_voice( index, mute != 0 ); } -BLARGG_EXPORT void gme_mute_voices ( Music_Emu* me, int mask ) { me->mute_voices( mask ); } -BLARGG_EXPORT void gme_enable_accuracy( Music_Emu* me, int enabled ) { me->enable_accuracy( enabled ); } -BLARGG_EXPORT void gme_clear_playlist ( Music_Emu* me ) { me->clear_playlist(); } -BLARGG_EXPORT int gme_type_multitrack( gme_type_t t ) { return t->track_count != 1; } +void* gme_user_data ( Music_Emu const* me ) { return me->user_data(); } +void gme_set_user_data ( Music_Emu* me, void* new_user_data ) { me->set_user_data( new_user_data ); } +void gme_set_user_cleanup(Music_Emu* me, gme_user_cleanup_t func ) { me->set_user_cleanup( func ); } + +gme_err_t gme_start_track ( Music_Emu* me, int index ) { return me->start_track( index ); } +gme_err_t gme_play ( Music_Emu* me, int n, short* p ) { return me->play( n, p ); } +void gme_set_fade ( Music_Emu* me, int start_msec ) { me->set_fade( start_msec ); } +int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); } +int gme_tell ( Music_Emu const* me ) { return me->tell(); } +int gme_tell_samples ( Music_Emu const* me ) { return me->tell_samples(); } +gme_err_t gme_seek ( Music_Emu* me, int msec ) { return me->seek( msec ); } +gme_err_t gme_seek_samples ( Music_Emu* me, int n ) { return me->seek_samples( n ); } +int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); } +void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); } +void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); } +void gme_mute_voice ( Music_Emu* me, int index, int mute ) { me->mute_voice( index, mute != 0 ); } +void gme_mute_voices ( Music_Emu* me, int mask ) { me->mute_voices( mask ); } +void gme_enable_accuracy( Music_Emu* me, int enabled ) { me->enable_accuracy( enabled ); } +void gme_clear_playlist ( Music_Emu* me ) { me->clear_playlist(); } +int gme_type_multitrack( gme_type_t t ) { return t->track_count != 1; } +int gme_multi_channel ( Music_Emu const* me ) { return me->multi_channel(); } -BLARGG_EXPORT void gme_set_equalizer ( Music_Emu* me, gme_equalizer_t const* eq ) +void gme_set_equalizer ( Music_Emu* me, gme_equalizer_t const* eq ) { Music_Emu::equalizer_t e = me->equalizer(); e.treble = eq->treble; @@ -357,7 +399,7 @@ me->set_equalizer( e ); } -BLARGG_EXPORT void gme_equalizer( Music_Emu const* me, gme_equalizer_t* out ) +void gme_equalizer( Music_Emu const* me, gme_equalizer_t* out ) { gme_equalizer_t e = gme_equalizer_t(); // Default-init all fields to 0.0f e.treble = me->equalizer().treble; @@ -365,13 +407,13 @@ *out = e; } -BLARGG_EXPORT const char* gme_voice_name( Music_Emu const* me, int i ) +const char* gme_voice_name( Music_Emu const* me, int i ) { assert( (unsigned) i < (unsigned) me->voice_count() ); return me->voice_names() [i]; } -BLARGG_EXPORT const char* gme_type_system( gme_type_t type ) +const char* gme_type_system( gme_type_t type ) { assert( type ); return type->system; diff -Nru game-music-emu-0.6.2/gme/Gme_File.cpp game-music-emu-0.6.3/gme/Gme_File.cpp --- game-music-emu-0.6.2/gme/Gme_File.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gme_File.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Gme_File.h" @@ -208,9 +208,9 @@ M3u_Playlist::entry_t const& e = playlist [track]; copy_field_( out->song, e.name ); - if ( e.length >= 0 ) out->length = e.length * 1000L; - if ( e.intro >= 0 ) out->intro_length = e.intro * 1000L; - if ( e.loop >= 0 ) out->loop_length = e.loop * 1000L; + if ( e.length >= 0 ) out->length = e.length; + if ( e.intro >= 0 ) out->intro_length = e.intro; + if ( e.loop >= 0 ) out->loop_length = e.loop; } return 0; } diff -Nru game-music-emu-0.6.2/gme/Gme_File.h game-music-emu-0.6.3/gme/Gme_File.h --- game-music-emu-0.6.2/gme/Gme_File.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gme_File.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Common interface to game music file loading and information -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef GME_FILE_H #define GME_FILE_H @@ -105,7 +105,7 @@ Gme_File(); virtual ~Gme_File(); BLARGG_DISABLE_NOTHROW - typedef BOOST::uint8_t byte; + typedef uint8_t byte; protected: // Services void set_track_count( int n ) { track_count_ = raw_track_count_ = n; } @@ -154,11 +154,7 @@ { Gme_File::copy_field_( out->name, in.name, sizeof in.name ); } #ifndef GME_FILE_READER - #ifdef HAVE_ZLIB_H - #define GME_FILE_READER Gzip_File_Reader - #else - #define GME_FILE_READER Std_File_Reader - #endif + #define GME_FILE_READER Std_File_Reader #elif defined (GME_FILE_READER_INCLUDE) #include GME_FILE_READER_INCLUDE #endif diff -Nru game-music-emu-0.6.2/gme/gme.h game-music-emu-0.6.3/gme/gme.h --- game-music-emu-0.6.2/gme/gme.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/gme.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,14 +1,16 @@ /* Game music emulator library C interface (also usable from C++) */ -/* Game_Music_Emu 0.6.1 */ +/* Game_Music_Emu 0.6.3 */ #ifndef GME_H #define GME_H +#include "blargg_source.h" + #ifdef __cplusplus extern "C" { #endif -#define GME_VERSION 0x000601 /* 1 byte major, 1 byte minor, 1 byte patch-level */ +#define GME_VERSION 0x000603 /* 1 byte major, 1 byte minor, 1 byte patch-level */ /* Error string returned by library functions, or NULL if no error (success) */ typedef const char* gme_err_t; @@ -20,41 +22,60 @@ /******** Basic operations ********/ /* Create emulator and load game music file/data into it. Sets *out to new emulator. */ -gme_err_t gme_open_file( const char path [], Music_Emu** out, int sample_rate ); +BLARGG_EXPORT gme_err_t gme_open_file( const char path [], Music_Emu** out, int sample_rate ); /* Number of tracks available */ -int gme_track_count( Music_Emu const* ); +BLARGG_EXPORT int gme_track_count( Music_Emu const* ); /* Start a track, where 0 is the first track */ -gme_err_t gme_start_track( Music_Emu*, int index ); +BLARGG_EXPORT gme_err_t gme_start_track( Music_Emu*, int index ); /* Generate 'count' 16-bit signed samples info 'out'. Output is in stereo. */ -gme_err_t gme_play( Music_Emu*, int count, short out [] ); +BLARGG_EXPORT gme_err_t gme_play( Music_Emu*, int count, short out [] ); /* Finish using emulator and free memory */ -void gme_delete( Music_Emu* ); +BLARGG_EXPORT void gme_delete( Music_Emu* ); /******** Track position/length ********/ /* Set time to start fading track out. Once fade ends track_ended() returns true. Fade time can be changed while track is playing. */ -void gme_set_fade( Music_Emu*, int start_msec ); +BLARGG_EXPORT void gme_set_fade( Music_Emu*, int start_msec ); + +/** + * If do_autoload_limit is nonzero, then automatically load track length + * metadata (if present) and terminate playback once the track length has been + * reached. Otherwise playback will continue for an arbitrary period of time + * until a prolonged period of silence is detected. + * + * Not all individual emulators support this setting. + * + * By default, playback limits are loaded and applied. + * + * @since 0.6.2 + */ +BLARGG_EXPORT void gme_set_autoload_playback_limit( Music_Emu *, int do_autoload_limit ); + +/** See gme_set_autoload_playback_limit. + * @since 0.6.2 + */ +BLARGG_EXPORT int gme_autoload_playback_limit( Music_Emu const* ); /* True if a track has reached its end */ -int gme_track_ended( Music_Emu const* ); +BLARGG_EXPORT int gme_track_ended( Music_Emu const* ); /* Number of milliseconds (1000 = one second) played since beginning of track */ -int gme_tell( Music_Emu const* ); +BLARGG_EXPORT int gme_tell( Music_Emu const* ); /* Number of samples generated since beginning of track */ -int gme_tell_samples( Music_Emu const* ); +BLARGG_EXPORT int gme_tell_samples( Music_Emu const* ); /* Seek to new time in track. Seeking backwards or far forward can take a while. */ -gme_err_t gme_seek( Music_Emu*, int msec ); +BLARGG_EXPORT gme_err_t gme_seek( Music_Emu*, int msec ); /* Equivalent to restarting track then skipping n samples */ -gme_err_t gme_seek_samples( Music_Emu*, int n ); +BLARGG_EXPORT gme_err_t gme_seek_samples( Music_Emu*, int n ); /******** Informational ********/ @@ -65,24 +86,24 @@ /* Most recent warning string, or NULL if none. Clears current warning after returning. Warning is also cleared when loading a file and starting a track. */ -const char* gme_warning( Music_Emu* ); +BLARGG_EXPORT const char* gme_warning( Music_Emu* ); /* Load m3u playlist file (must be done after loading music) */ -gme_err_t gme_load_m3u( Music_Emu*, const char path [] ); +BLARGG_EXPORT gme_err_t gme_load_m3u( Music_Emu*, const char path [] ); /* Clear any loaded m3u playlist and any internal playlist that the music format supports (NSFE for example). */ -void gme_clear_playlist( Music_Emu* ); +BLARGG_EXPORT void gme_clear_playlist( Music_Emu* ); /* Gets information for a particular track (length, name, author, etc.). Must be freed after use. */ typedef struct gme_info_t gme_info_t; -gme_err_t gme_track_info( Music_Emu const*, gme_info_t** out, int track ); +BLARGG_EXPORT gme_err_t gme_track_info( Music_Emu const*, gme_info_t** out, int track ); /* Frees track information */ -void gme_free_info( gme_info_t* ); +BLARGG_EXPORT void gme_free_info( gme_info_t* ); -struct gme_info_t +struct BLARGG_EXPORT gme_info_t { /* times in milliseconds; -1 if unknown */ int length; /* total length, if file specifies it */ @@ -112,32 +133,32 @@ /* Adjust stereo echo depth, where 0.0 = off and 1.0 = maximum. Has no effect for GYM, SPC, and Sega Genesis VGM music */ -void gme_set_stereo_depth( Music_Emu*, double depth ); +BLARGG_EXPORT void gme_set_stereo_depth( Music_Emu*, double depth ); /* Disable automatic end-of-track detection and skipping of silence at beginning if ignore is true */ -void gme_ignore_silence( Music_Emu*, int ignore ); +BLARGG_EXPORT void gme_ignore_silence( Music_Emu*, int ignore ); /* Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. Track length as returned by track_info() assumes a tempo of 1.0. */ -void gme_set_tempo( Music_Emu*, double tempo ); +BLARGG_EXPORT void gme_set_tempo( Music_Emu*, double tempo ); /* Number of voices used by currently loaded file */ -int gme_voice_count( Music_Emu const* ); +BLARGG_EXPORT int gme_voice_count( Music_Emu const* ); /* Name of voice i, from 0 to gme_voice_count() - 1 */ -const char* gme_voice_name( Music_Emu const*, int i ); +BLARGG_EXPORT const char* gme_voice_name( Music_Emu const*, int i ); /* Mute/unmute voice i, where voice 0 is first voice */ -void gme_mute_voice( Music_Emu*, int index, int mute ); +BLARGG_EXPORT void gme_mute_voice( Music_Emu*, int index, int mute ); /* Set muting state of all voices at once using a bit mask, where -1 mutes all voices, 0 unmutes them all, 0x01 mutes just the first voice, etc. */ -void gme_mute_voices( Music_Emu*, int muting_mask ); +BLARGG_EXPORT void gme_mute_voices( Music_Emu*, int muting_mask ); /* Frequency equalizer parameters (see gme.txt) */ /* Implementers: If modified, also adjust Music_Emu::make_equalizer as needed */ -typedef struct gme_equalizer_t +typedef struct BLARGG_EXPORT gme_equalizer_t { double treble; /* -50.0 = muffled, 0 = flat, +5.0 = extra-crisp */ double bass; /* 1 = full bass, 90 = average, 16000 = almost no bass */ @@ -146,13 +167,13 @@ } gme_equalizer_t; /* Get current frequency equalizater parameters */ -void gme_equalizer( Music_Emu const*, gme_equalizer_t* out ); +BLARGG_EXPORT void gme_equalizer( Music_Emu const*, gme_equalizer_t* out ); /* Change frequency equalizer parameters */ -void gme_set_equalizer( Music_Emu*, gme_equalizer_t const* eq ); +BLARGG_EXPORT void gme_set_equalizer( Music_Emu*, gme_equalizer_t const* eq ); /* Enables/disables most accurate sound emulation options */ -void gme_enable_accuracy( Music_Emu*, int enabled ); +BLARGG_EXPORT void gme_enable_accuracy( Music_Emu*, int enabled ); /******** Game music types ********/ @@ -161,7 +182,7 @@ typedef const struct gme_type_t_* gme_type_t; /* Emulator type constants for each supported file type */ -extern const gme_type_t +extern BLARGG_EXPORT const gme_type_t gme_ay_type, gme_gbs_type, gme_gym_type, @@ -175,69 +196,89 @@ gme_vgz_type; /* Type of this emulator */ -gme_type_t gme_type( Music_Emu const* ); +BLARGG_EXPORT gme_type_t gme_type( Music_Emu const* ); /* Pointer to array of all music types, with NULL entry at end. Allows a player linked to this library to support new music types without having to be updated. */ -gme_type_t const* gme_type_list(); +BLARGG_EXPORT gme_type_t const* gme_type_list(); /* Name of game system for this music file type */ -const char* gme_type_system( gme_type_t ); +BLARGG_EXPORT const char* gme_type_system( gme_type_t ); /* True if this music file type supports multiple tracks */ -int gme_type_multitrack( gme_type_t ); +BLARGG_EXPORT int gme_type_multitrack( gme_type_t ); +/* whether the pcm output retrieved by gme_play() will have all 8 voices rendered to their + * individual stereo channel or (if false) these voices get mixed into one single stereo channel + * @since 0.6.2 */ +BLARGG_EXPORT int gme_multi_channel( Music_Emu const* ); /******** Advanced file loading ********/ /* Error returned if file type is not supported */ -extern const char* const gme_wrong_file_type; +extern BLARGG_EXPORT const char* const gme_wrong_file_type; -/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. */ -gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, int sample_rate ); +/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. + * The resulting Music_Emu object will be set to single channel mode. */ +BLARGG_EXPORT gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, int sample_rate ); /* Determine likely game music type based on first four bytes of file. Returns string containing proper file suffix (i.e. "NSF", "SPC", etc.) or "" if file header is not recognized. */ -const char* gme_identify_header( void const* header ); +BLARGG_EXPORT const char* gme_identify_header( void const* header ); /* Get corresponding music type for file path or extension passed in. */ -gme_type_t gme_identify_extension( const char path_or_extension [] ); +BLARGG_EXPORT gme_type_t gme_identify_extension( const char path_or_extension [] ); + +/** + * Get typical file extension for a given music type. This is not a replacement + * for a file content identification library (but see gme_identify_header). + * + * @since 0.6.2 + */ +BLARGG_EXPORT const char* gme_type_extension( gme_type_t music_type ); /* Determine file type based on file's extension or header (if extension isn't recognized). Sets *type_out to type, or 0 if unrecognized or error. */ -gme_err_t gme_identify_file( const char path [], gme_type_t* type_out ); +BLARGG_EXPORT gme_err_t gme_identify_file( const char path [], gme_type_t* type_out ); /* Create new emulator and set sample rate. Returns NULL if out of memory. If you only need track information, pass gme_info_only for sample_rate. */ -Music_Emu* gme_new_emu( gme_type_t, int sample_rate ); +BLARGG_EXPORT Music_Emu* gme_new_emu( gme_type_t, int sample_rate ); + +/* Create new multichannel emulator and set sample rate. Returns NULL if out of memory. + * If you only need track information, pass gme_info_only for sample_rate. + * (see gme_multi_channel for more information on multichannel support) + * @since 0.6.2 + */ +BLARGG_EXPORT Music_Emu* gme_new_emu_multi_channel( gme_type_t, int sample_rate ); /* Load music file into emulator */ -gme_err_t gme_load_file( Music_Emu*, const char path [] ); +BLARGG_EXPORT gme_err_t gme_load_file( Music_Emu*, const char path [] ); /* Load music file from memory into emulator. Makes a copy of data passed. */ -gme_err_t gme_load_data( Music_Emu*, void const* data, long size ); +BLARGG_EXPORT gme_err_t gme_load_data( Music_Emu*, void const* data, long size ); /* Load music file using custom data reader function that will be called to read file data. Most emulators load the entire file in one read call. */ typedef gme_err_t (*gme_reader_t)( void* your_data, void* out, int count ); -gme_err_t gme_load_custom( Music_Emu*, gme_reader_t, long file_size, void* your_data ); +BLARGG_EXPORT gme_err_t gme_load_custom( Music_Emu*, gme_reader_t, long file_size, void* your_data ); /* Load m3u playlist file from memory (must be done after loading music) */ -gme_err_t gme_load_m3u_data( Music_Emu*, void const* data, long size ); +BLARGG_EXPORT gme_err_t gme_load_m3u_data( Music_Emu*, void const* data, long size ); /******** User data ********/ /* Set/get pointer to data you want to associate with this emulator. You can use this for whatever you want. */ -void gme_set_user_data( Music_Emu*, void* new_user_data ); -void* gme_user_data( Music_Emu const* ); +BLARGG_EXPORT void gme_set_user_data( Music_Emu*, void* new_user_data ); +BLARGG_EXPORT void* gme_user_data( Music_Emu const* ); /* Register cleanup function to be called when deleting emulator, or NULL to clear it. Passes user_data to cleanup function. */ typedef void (*gme_user_cleanup_t)( void* user_data ); -void gme_set_user_cleanup( Music_Emu*, gme_user_cleanup_t func ); +BLARGG_EXPORT void gme_set_user_cleanup( Music_Emu*, gme_user_cleanup_t func ); #ifdef __cplusplus diff -Nru game-music-emu-0.6.2/gme/Gym_Emu.cpp game-music-emu-0.6.3/gme/Gym_Emu.cpp --- game-music-emu-0.6.2/gme/Gym_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gym_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Gym_Emu.h" @@ -162,7 +162,7 @@ static Music_Emu* new_gym_file() { return BLARGG_NEW Gym_File; } static gme_type_t_ const gme_gym_type_ = { "Sega Genesis", 1, &new_gym_emu, &new_gym_file, "GYM", 0 }; -gme_type_t const gme_gym_type = &gme_gym_type_; +extern gme_type_t const gme_gym_type = &gme_gym_type_; // Setup diff -Nru game-music-emu-0.6.2/gme/Gym_Emu.h game-music-emu-0.6.3/gme/Gym_Emu.h --- game-music-emu-0.6.2/gme/Gym_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Gym_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,7 +1,7 @@ // Sega Genesis/Mega Drive GYM music file emulator // Includes with PCM timing recovery to improve sample quality. -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef GYM_EMU_H #define GYM_EMU_H diff -Nru game-music-emu-0.6.2/gme/Hes_Apu.cpp game-music-emu-0.6.3/gme/Hes_Apu.cpp --- game-music-emu-0.6.2/gme/Hes_Apu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Hes_Apu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Hes_Apu.h" diff -Nru game-music-emu-0.6.2/gme/Hes_Apu.h game-music-emu-0.6.3/gme/Hes_Apu.h --- game-music-emu-0.6.2/gme/Hes_Apu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Hes_Apu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Turbo Grafx 16 (PC Engine) PSG sound chip emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef HES_APU_H #define HES_APU_H diff -Nru game-music-emu-0.6.2/gme/Hes_Cpu.cpp game-music-emu-0.6.3/gme/Hes_Cpu.cpp --- game-music-emu-0.6.2/gme/Hes_Cpu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Hes_Cpu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Hes_Cpu.h" @@ -50,19 +50,19 @@ { check( state == &state_ ); state = &state_; - + state_.time = 0; state_.base = 0; irq_time_ = future_hes_time; end_time_ = future_hes_time; - + r.status = st_i; r.sp = 0; r.pc = 0; r.a = 0; r.x = 0; r.y = 0; - + blargg_verify_byte_order(); } @@ -87,12 +87,6 @@ #define GET_SP() ((sp - 1) & 0xFF) #define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; -typedef blargg_long fint32; - bool Hes_Cpu::run( hes_time_t end_time ) { bool illegal_encountered = false; @@ -100,18 +94,18 @@ state_t s = this->state_; this->state = &s; // even on x86, using s.time in place of s_time was slower - fint16 s_time = s.time; - + blargg_long s_time = s.time; + // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 sp; + uint_fast16_t pc = r.pc; + uint_fast8_t a = r.a; + uint_fast8_t x = r.x; + uint_fast8_t y = r.y; + uint_fast16_t sp; SET_SP( r.sp ); - + #define IS_NEG (nz & 0x8080) - + #define CALC_STATUS( out ) do {\ out = status & (st_v | st_d | st_i);\ out |= ((nz >> 8) | nz) & st_n;\ @@ -125,20 +119,20 @@ c = nz;\ nz |= ~in & st_z;\ } while ( 0 ) - - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + + uint_fast8_t status; + uint_fast16_t c; // carry set if (c & 0x100) != 0 + uint_fast16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 { - fuint8 temp = r.status; + uint_fast8_t temp = r.status; SET_STATUS( temp ); } - + goto loop; branch_not_taken: s_time -= 2; loop: - + #ifndef NDEBUG { hes_time_t correct = end_time_; @@ -157,10 +151,10 @@ check( (unsigned) GET_SP() < 0x100 ); check( (unsigned) a < 0x100 ); check( (unsigned) x < 0x100 ); - + uint8_t const* instr = s.code_map [pc >> page_shift]; - fuint8 opcode; - + uint_fast8_t opcode; + // TODO: eliminate this special case #if BLARGG_NONPORTABLE opcode = instr [pc]; @@ -171,7 +165,7 @@ opcode = *instr++; pc++; #endif - + // TODO: each reference lists slightly different timing values, ugh static uint8_t const clock_table [256] = {// 0 1 2 3 4 5 6 7 8 9 A B C D E F @@ -192,21 +186,21 @@ 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E 4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F }; // 0x00 was 8 - - fuint16 data; + + uint_fast16_t data; data = clock_table [opcode]; if ( (s_time += data) >= 0 ) goto possibly_out_of_time; almost_out_of_time: - + data = *instr; - + #ifdef HES_CPU_LOG_H log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2], instr [3], instr [4], instr [5] ); //log_opcode( opcode ); #endif - + switch ( opcode ) { possibly_out_of_time: @@ -230,44 +224,45 @@ // TODO: more efficient way to handle negative branch that wraps PC around #define BRANCH( cond )\ {\ - fint16 offset = (BOOST::int8_t) data;\ + int_fast16_t offset = (int8_t) data;\ pc++;\ if ( !(cond) ) goto branch_not_taken;\ - pc = BOOST::uint16_t (pc + offset);\ + pc = uint16_t (pc + offset);\ goto loop;\ } case 0xF0: // BEQ BRANCH( !((uint8_t) nz) ); - + case 0xD0: // BNE BRANCH( (uint8_t) nz ); - + case 0x10: // BPL BRANCH( !IS_NEG ); - + case 0x90: // BCC BRANCH( !(c & 0x100) ) - + case 0x30: // BMI BRANCH( IS_NEG ) - + case 0x50: // BVC BRANCH( !(status & st_v) ) - + case 0x70: // BVS BRANCH( status & st_v ) - + case 0xB0: // BCS BRANCH( c & 0x100 ) - + case 0x80: // BRA branch_taken: BRANCH( true ); - + case 0xFF: if ( pc == idle_addr + 1 ) goto idle_done; + // FALLTHRU case 0x0F: // BBRn case 0x1F: case 0x2F: @@ -283,25 +278,25 @@ case 0xCF: case 0xDF: case 0xEF: { - fuint16 t = 0x101 * READ_LOW( data ); + uint_fast16_t t = 0x101 * READ_LOW( data ); t ^= 0xFF; pc++; data = GET_MSB(); BRANCH( t & (1 << (opcode >> 4)) ) } - + case 0x4C: // JMP abs pc = GET_ADDR(); goto loop; - + case 0x7C: // JMP (ind+X) - data += x; + data += x; // FALLTHRU case 0x6C:{// JMP (ind) data += 0x100 * GET_MSB(); pc = GET_LE16( &READ_PROG( data ) ); goto loop; } - + // Subroutine case 0x44: // BSR @@ -309,73 +304,73 @@ sp = (sp - 2) | 0x100; WRITE_LOW( sp, pc ); goto branch_taken; - + case 0x20: { // JSR - fuint16 temp = pc + 1; + uint_fast16_t temp = pc + 1; pc = GET_ADDR(); WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); sp = (sp - 2) | 0x100; WRITE_LOW( sp, temp ); goto loop; } - + case 0x60: // RTS pc = 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); pc += 1 + READ_LOW( sp ); sp = (sp - 0xFE) | 0x100; goto loop; - + case 0x00: // BRK goto handle_brk; - + // Common case 0xBD:{// LDA abs,X PAGE_CROSS_PENALTY( data + x ); - fuint16 addr = GET_ADDR() + x; + uint_fast16_t addr = GET_ADDR() + x; pc += 2; CPU_READ_FAST( this, addr, TIME, nz ); a = nz; goto loop; } - + case 0x9D:{// STA abs,X - fuint16 addr = GET_ADDR() + x; + uint_fast16_t addr = GET_ADDR() + x; pc += 2; CPU_WRITE_FAST( this, addr, a, TIME ); goto loop; } - + case 0x95: // STA zp,x - data = uint8_t (data + x); + data = uint8_t (data + x); // FALLTHRU case 0x85: // STA zp pc++; WRITE_LOW( data, a ); goto loop; - + case 0xAE:{// LDX abs - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc += 2; CPU_READ_FAST( this, addr, TIME, nz ); x = nz; goto loop; } - + case 0xA5: // LDA zp a = nz = READ_LOW( data ); pc++; goto loop; - + // Load/store - + { - fuint16 addr; + uint_fast16_t addr; case 0x91: // STA (ind),Y addr = 0x100 * READ_LOW( uint8_t (data + 1) ); addr += READ_LOW( data ) + y; pc++; goto sta_ptr; - + case 0x81: // STA (ind,X) data = uint8_t (data + x); case 0x92: // STA (ind) @@ -383,7 +378,7 @@ addr += READ_LOW( data ); pc++; goto sta_ptr; - + case 0x99: // STA abs,Y data += y; case 0x8D: // STA abs @@ -393,9 +388,9 @@ CPU_WRITE_FAST( this, addr, a, TIME ); goto loop; } - + { - fuint16 addr; + uint_fast16_t addr; case 0xA1: // LDA (ind,X) data = uint8_t (data + x); case 0xB2: // LDA (ind) @@ -403,14 +398,14 @@ addr += READ_LOW( data ); pc++; goto a_nz_read_addr; - + case 0xB1:// LDA (ind),Y addr = READ_LOW( data ) + y; PAGE_CROSS_PENALTY( addr ); addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); pc++; goto a_nz_read_addr; - + case 0xB9: // LDA abs,Y data += y; PAGE_CROSS_PENALTY( data ); @@ -425,19 +420,19 @@ case 0xBE:{// LDX abs,y PAGE_CROSS_PENALTY( data + y ); - fuint16 addr = GET_ADDR() + y; + uint_fast16_t addr = GET_ADDR() + y; pc += 2; FLUSH_TIME(); x = nz = READ( addr ); CACHE_TIME(); goto loop; } - + case 0xB5: // LDA zp,x a = nz = READ_LOW( uint8_t (data + x) ); pc++; goto loop; - + case 0xA9: // LDA #imm pc++; a = data; @@ -447,9 +442,9 @@ // Bit operations case 0x3C: // BIT abs,x - data += x; + data += x; // FALLTHRU case 0x2C:{// BIT abs - fuint16 addr; + uint_fast16_t addr; ADD_PAGE( addr ); FLUSH_TIME(); nz = READ( addr ); @@ -457,9 +452,9 @@ goto bit_common; } case 0x34: // BIT zp,x - data = uint8_t (data + x); + data = uint8_t (data + x); // FALLTHRU case 0x24: // BIT zp - data = READ_LOW( data ); + data = READ_LOW( data ); // FALLTHRU case 0x89: // BIT imm nz = data; bit_common: @@ -470,14 +465,14 @@ goto loop; // Z should be clear, and nz must be non-zero if nz & a is nz <<= 8; // set Z flag without affecting N flag goto loop; - + { - fuint16 addr; - + uint_fast16_t addr; + case 0xB3: // TST abs,x addr = GET_MSB() + x; goto tst_abs; - + case 0x93: // TST abs addr = GET_MSB(); tst_abs: @@ -488,11 +483,11 @@ CACHE_TIME(); goto tst_common; } - + case 0xA3: // TST zp,x nz = READ_LOW( uint8_t (GET_MSB() + x) ); goto tst_common; - + case 0x83: // TST zp nz = READ_LOW( GET_MSB() ); tst_common: @@ -503,15 +498,15 @@ goto loop; // Z should be clear, and nz must be non-zero if nz & data is nz <<= 8; // set Z flag without affecting N flag goto loop; - + { - fuint16 addr; + uint_fast16_t addr; case 0x0C: // TSB abs case 0x1C: // TRB abs addr = GET_ADDR(); pc++; goto txb_addr; - + // TODO: everyone lists different behaviors for the status flags, ugh case 0x04: // TSB zp case 0x14: // TRB zp @@ -528,7 +523,7 @@ CACHE_TIME(); goto loop; } - + case 0x07: // RMBn case 0x17: case 0x27: @@ -540,7 +535,7 @@ pc++; READ_LOW( data ) &= ~(1 << (opcode >> 4)); goto loop; - + case 0x87: // SMBn case 0x97: case 0xA7: @@ -552,11 +547,11 @@ pc++; READ_LOW( data ) |= 1 << ((opcode >> 4) - 8); goto loop; - + // Load/store - + case 0x9E: // STZ abs,x - data += x; + data += x; // FALLTHRU case 0x9C: // STZ abs ADD_PAGE( data ); pc++; @@ -564,70 +559,71 @@ WRITE( data, 0 ); CACHE_TIME(); goto loop; - + case 0x74: // STZ zp,x - data = uint8_t (data + x); + data = uint8_t (data + x); // FALLTHRU case 0x64: // STZ zp pc++; WRITE_LOW( data, 0 ); goto loop; - + case 0x94: // STY zp,x - data = uint8_t (data + x); + data = uint8_t (data + x); // FALLTHRU case 0x84: // STY zp pc++; WRITE_LOW( data, y ); goto loop; - + case 0x96: // STX zp,y - data = uint8_t (data + y); + data = uint8_t (data + y); // FALLTHRU case 0x86: // STX zp pc++; WRITE_LOW( data, x ); goto loop; - + case 0xB6: // LDX zp,y - data = uint8_t (data + y); + data = uint8_t (data + y); // FALLTHRU case 0xA6: // LDX zp - data = READ_LOW( data ); + data = READ_LOW( data ); // FALLTHRU case 0xA2: // LDX #imm pc++; x = data; nz = data; goto loop; - + case 0xB4: // LDY zp,x - data = uint8_t (data + x); + data = uint8_t (data + x); // FALLTHRU case 0xA4: // LDY zp - data = READ_LOW( data ); + data = READ_LOW( data ); // FALLTHRU case 0xA0: // LDY #imm pc++; y = data; nz = data; goto loop; - + case 0xBC: // LDY abs,X data += x; PAGE_CROSS_PENALTY( data ); + // FALLTHRU case 0xAC:{// LDY abs - fuint16 addr = data + 0x100 * GET_MSB(); + uint_fast16_t addr = data + 0x100 * GET_MSB(); pc += 2; FLUSH_TIME(); y = nz = READ( addr ); CACHE_TIME(); goto loop; } - + { - fuint8 temp; + uint_fast8_t temp; case 0x8C: // STY abs temp = y; goto store_abs; - + case 0x8E: // STX abs temp = x; store_abs: - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc += 2; FLUSH_TIME(); WRITE( addr, temp ); @@ -638,16 +634,16 @@ // Compare case 0xEC:{// CPX abs - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc++; FLUSH_TIME(); data = READ( addr ); CACHE_TIME(); goto cpx_data; } - + case 0xE4: // CPX zp - data = READ_LOW( data ); + data = READ_LOW( data ); // FALLTHRU case 0xE0: // CPX #imm cpx_data: nz = x - data; @@ -655,18 +651,18 @@ c = ~nz; nz &= 0xFF; goto loop; - + case 0xCC:{// CPY abs - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc++; FLUSH_TIME(); data = READ( addr ); CACHE_TIME(); goto cpy_data; } - + case 0xC4: // CPY zp - data = READ_LOW( data ); + data = READ_LOW( data ); // FALLTHRU case 0xC0: // CPY #imm cpy_data: nz = y - data; @@ -674,23 +670,23 @@ c = ~nz; nz &= 0xFF; goto loop; - + // Logical #define ARITH_ADDR_MODES( op )\ case op - 0x04: /* (ind,x) */\ - data = uint8_t (data + x);\ + data = uint8_t (data + x);/*FALLTHRU*/\ case op + 0x0D: /* (ind) */\ data = 0x100 * READ_LOW( uint8_t (data + 1) ) + READ_LOW( data );\ goto ptr##op;\ case op + 0x0C:{/* (ind),y */\ - fuint16 temp = READ_LOW( data ) + y;\ + uint_fast16_t temp = READ_LOW( data ) + y;\ PAGE_CROSS_PENALTY( temp );\ data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ goto ptr##op;\ }\ case op + 0x10: /* zp,X */\ - data = uint8_t (data + x);\ + data = uint8_t (data + x);/*FALLTHRU*/\ case op + 0x00: /* zp */\ data = READ_LOW( data );\ goto imm##op;\ @@ -699,14 +695,15 @@ goto ind##op;\ case op + 0x18: /* abs,X */\ data += x;\ - ind##op:\ - PAGE_CROSS_PENALTY( data );\ + goto ind##op;/*WORKAROUND: Mute a fallthrough warning*/\ + ind##op:/*FALLTHRU*/\ + PAGE_CROSS_PENALTY( data );/*FALLTHRU*/\ case op + 0x08: /* abs */\ - ADD_PAGE( data );\ + ADD_PAGE( data );/*FALLTHRU*/\ ptr##op:\ FLUSH_TIME();\ data = READ( data );\ - CACHE_TIME();\ + CACHE_TIME();/*FALLTHRU*/\ case op + 0x04: /* imm */\ imm##op: @@ -716,34 +713,35 @@ c = ~nz; nz &= 0xFF; goto loop; - + ARITH_ADDR_MODES( 0x25 ) // AND nz = (a &= data); pc++; goto loop; - + ARITH_ADDR_MODES( 0x45 ) // EOR nz = (a ^= data); pc++; goto loop; - + ARITH_ADDR_MODES( 0x05 ) // ORA nz = (a |= data); pc++; goto loop; - + // Add/subtract ARITH_ADDR_MODES( 0xE5 ) // SBC data ^= 0xFF; goto adc_imm; - + ARITH_ADDR_MODES( 0x65 ) // ADC + /*FALLTHRU*/ adc_imm: { if ( status & st_d ) debug_printf( "Decimal mode not supported\n" ); - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend + int_fast16_t carry = c >> 8 & 1; + int_fast16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend status &= ~st_v; status |= ov >> 2 & 0x40; c = nz = a + data + carry; @@ -751,11 +749,11 @@ a = (uint8_t) nz; goto loop; } - + // Shift/rotate case 0x4A: // LSR A - c = 0; + c = 0; // FALLTHRU case 0x6A: // ROR A nz = c >> 1 & 0x80; c = a << 8; @@ -771,17 +769,17 @@ case 0x2A: { // ROL A nz = a << 1; - fint16 temp = c >> 8 & 1; + int_fast16_t temp = c >> 8 & 1; c = nz; nz |= temp; a = (uint8_t) nz; goto loop; } - + case 0x5E: // LSR abs,X - data += x; + data += x;/*FALLTHRU*/ case 0x4E: // LSR abs - c = 0; + c = 0;/*FALLTHRU*/ case 0x6E: // ROR abs ror_abs: { ADD_PAGE( data ); @@ -791,15 +789,15 @@ c = temp << 8; goto rotate_common; } - + case 0x3E: // ROL abs,X data += x; goto rol_abs; - + case 0x1E: // ASL abs,X - data += x; + data += x;/*FALLTHRU*/ case 0x0E: // ASL abs - c = 0; + c = 0;/*FALLTHRU*/ case 0x2E: // ROL abs rol_abs: ADD_PAGE( data ); @@ -811,19 +809,19 @@ WRITE( data, (uint8_t) nz ); CACHE_TIME(); goto loop; - + case 0x7E: // ROR abs,X data += x; goto ror_abs; - + case 0x76: // ROR zp,x data = uint8_t (data + x); goto ror_zp; - + case 0x56: // LSR zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0x46: // LSR zp - c = 0; + c = 0;/*FALLTHRU*/ case 0x66: // ROR zp ror_zp: { int temp = READ_LOW( data ); @@ -831,51 +829,51 @@ c = temp << 8; goto write_nz_zp; } - + case 0x36: // ROL zp,x data = uint8_t (data + x); goto rol_zp; - + case 0x16: // ASL zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0x06: // ASL zp - c = 0; + c = 0;/*FALLTHRU*/ case 0x26: // ROL zp rol_zp: nz = c >> 8 & 1; nz |= (c = READ_LOW( data ) << 1); goto write_nz_zp; - + // Increment/decrement #define INC_DEC_AXY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; case 0x1A: // INA INC_DEC_AXY( a, +1 ) - + case 0xE8: // INX INC_DEC_AXY( x, +1 ) - + case 0xC8: // INY INC_DEC_AXY( y, +1 ) case 0x3A: // DEA INC_DEC_AXY( a, -1 ) - + case 0xCA: // DEX INC_DEC_AXY( x, -1 ) - + case 0x88: // DEY INC_DEC_AXY( y, -1 ) - + case 0xF6: // INC zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0xE6: // INC zp nz = 1; goto add_nz_zp; - + case 0xD6: // DEC zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0xC6: // DEC zp nz = (unsigned) -1; add_nz_zp: @@ -884,21 +882,21 @@ pc++; WRITE_LOW( data, nz ); goto loop; - + case 0xFE: // INC abs,x data = x + GET_ADDR(); goto inc_ptr; - + case 0xEE: // INC abs data = GET_ADDR(); inc_ptr: nz = 1; goto inc_common; - + case 0xDE: // DEC abs,x data = x + GET_ADDR(); goto dec_ptr; - + case 0xCE: // DEC abs data = GET_ADDR(); dec_ptr: @@ -910,24 +908,24 @@ WRITE( data, (uint8_t) nz ); CACHE_TIME(); goto loop; - + // Transfer case 0xA8: // TAY y = a; nz = a; goto loop; - + case 0x98: // TYA a = y; nz = y; goto loop; - + case 0xAA: // TAX x = a; nz = a; goto loop; - + case 0x8A: // TXA a = x; nz = x; @@ -936,55 +934,55 @@ case 0x9A: // TXS SET_SP( x ); // verified (no flag change) goto loop; - + case 0xBA: // TSX x = nz = GET_SP(); goto loop; - + #define SWAP_REGS( r1, r2 ) {\ - fuint8 t = r1;\ + uint_fast8_t t = r1;\ r1 = r2;\ r2 = t;\ goto loop;\ } - + case 0x02: // SXY SWAP_REGS( x, y ); - + case 0x22: // SAX SWAP_REGS( a, x ); - + case 0x42: // SAY SWAP_REGS( a, y ); - + case 0x62: // CLA a = 0; goto loop; - + case 0x82: // CLX x = 0; goto loop; - + case 0xC2: // CLY y = 0; goto loop; - + // Stack - + case 0x48: // PHA PUSH( a ); goto loop; - + case 0xDA: // PHX PUSH( x ); goto loop; - + case 0x5A: // PHY PUSH( y ); goto loop; - + case 0x40:{// RTI - fuint8 temp = READ_LOW( sp ); + uint_fast8_t temp = READ_LOW( sp ); pc = READ_LOW( 0x100 | (sp - 0xFF) ); pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; sp = (sp - 0xFD) | 0x100; @@ -1002,24 +1000,24 @@ } goto loop; } - + #define POP() READ_LOW( sp ); sp = (sp - 0xFF) | 0x100 - + case 0x68: // PLA a = nz = POP(); goto loop; - + case 0xFA: // PLX x = nz = POP(); goto loop; - + case 0x7A: // PLY y = nz = POP(); goto loop; - + case 0x28:{// PLP - fuint8 temp = POP(); - fuint8 changed = status ^ temp; + uint_fast8_t temp = POP(); + uint_fast8_t changed = status ^ temp; SET_STATUS( temp ); if ( !(changed & st_i) ) goto loop; // I flag didn't change @@ -1028,36 +1026,36 @@ goto handle_cli; } #undef POP - + case 0x08: { // PHP - fuint8 temp; + uint_fast8_t temp; CALC_STATUS( temp ); PUSH( temp | st_b ); goto loop; } - + // Flags case 0x38: // SEC c = (unsigned) ~0; goto loop; - + case 0x18: // CLC c = 0; goto loop; - + case 0xB8: // CLV status &= ~st_v; goto loop; - + case 0xD8: // CLD status &= ~st_d; goto loop; - + case 0xF8: // SED status |= st_d; goto loop; - + case 0x58: // CLI if ( !(status & st_i) ) goto loop; @@ -1075,7 +1073,7 @@ s_time += delta; if ( s_time < 0 ) goto loop; - + if ( delta >= s_time + 1 ) { // delayed irq until after next instruction @@ -1088,7 +1086,7 @@ debug_printf( "Delayed CLI not supported\n" ); // TODO: implement goto loop; } - + case 0x78: // SEI if ( status & st_i ) goto loop; @@ -1103,18 +1101,18 @@ debug_printf( "Delayed SEI not supported\n" ); // TODO: implement goto loop; } - + // Special - + case 0x53:{// TAM - fuint8 const bits = data; // avoid using data across function call + uint_fast8_t const bits = data; // avoid using data across function call pc++; for ( int i = 0; i < 8; i++ ) if ( bits & (1 << i) ) set_mmr( i, a ); goto loop; } - + case 0x43:{// TMA pc++; byte const* in = mmr; @@ -1127,11 +1125,11 @@ while ( (data >>= 1) != 0 ); goto loop; } - + case 0x03: // ST0 case 0x13: // ST1 case 0x23:{// ST2 - fuint16 addr = opcode >> 4; + uint_fast16_t addr = opcode >> 4; if ( addr ) addr++; pc++; @@ -1140,7 +1138,7 @@ CACHE_TIME(); goto loop; } - + case 0xEA: // NOP goto loop; @@ -1148,10 +1146,10 @@ debug_printf( "CSL not supported\n" ); illegal_encountered = true; goto loop; - + case 0xD4: // CSH goto loop; - + case 0xF4: { // SET //fuint16 operand = GET_MSB(); debug_printf( "SET not handled\n" ); @@ -1161,19 +1159,19 @@ illegal_encountered = true; goto loop; } - + // Block transfer { - fuint16 in_alt; - fint16 in_inc; - fuint16 out_alt; - fint16 out_inc; - + uint_fast16_t in_alt; + int_fast16_t in_inc; + uint_fast16_t out_alt; + int_fast16_t out_inc; + case 0xE3: // TIA in_alt = 0; goto bxfer_alt; - + case 0xF3: // TAI in_alt = 1; bxfer_alt: @@ -1181,17 +1179,17 @@ out_alt = in_inc; out_inc = in_alt; goto bxfer; - + case 0xD3: // TIN in_inc = 1; out_inc = 0; goto bxfer_no_alt; - + case 0xC3: // TDD in_inc = -1; out_inc = -1; goto bxfer_no_alt; - + case 0x73: // TII in_inc = 1; out_inc = 1; @@ -1199,8 +1197,8 @@ in_alt = 0; out_alt = 0; bxfer: - fuint16 in = GET_LE16( instr + 0 ); - fuint16 out = GET_LE16( instr + 2 ); + uint_fast16_t in = GET_LE16( instr + 0 ); + uint_fast16_t out = GET_LE16( instr + 2 ); int count = GET_LE16( instr + 4 ); if ( !count ) count = 0x10000; @@ -1212,7 +1210,7 @@ do { // TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O - fuint8 t = READ( in ); + uint_fast8_t t = READ( in ); in += in_inc; in &= 0xFFFF; s.time += 6; @@ -1232,43 +1230,42 @@ // Illegal default: - assert( (unsigned) opcode <= 0xFF ); debug_printf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 ); illegal_encountered = true; goto loop; } assert( false ); - + int result_; handle_brk: pc++; result_ = 6; - + interrupt: { s_time += 7; - + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); WRITE_LOW( 0x100 | (sp - 2), pc ); pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ ); - + sp = (sp - 3) | 0x100; - fuint8 temp; + uint_fast8_t temp; CALC_STATUS( temp ); if ( result_ == 6 ) temp |= st_b; WRITE_LOW( sp, temp ); - + status &= ~st_d; status |= st_i; this->r.status = status; // update externally-visible I flag - + blargg_long delta = s.base - end_time_; s.base = end_time_; s_time += delta; goto loop; } - + idle_done: s_time = 0; out_of_time: @@ -1280,24 +1277,23 @@ goto interrupt; if ( s_time < 0 ) goto loop; - + s.time = s_time; - + r.pc = pc; r.sp = GET_SP(); r.a = a; r.x = x; r.y = y; - + { - fuint8 temp; + uint_fast8_t temp; CALC_STATUS( temp ); r.status = temp; } - + this->state_ = s; this->state = &this->state_; - + return illegal_encountered; } - diff -Nru game-music-emu-0.6.2/gme/Hes_Cpu.h game-music-emu-0.6.3/gme/Hes_Cpu.h --- game-music-emu-0.6.2/gme/Hes_Cpu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Hes_Cpu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // PC Engine CPU emulator for use with HES music files -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef HES_CPU_H #define HES_CPU_H @@ -12,8 +12,6 @@ class Hes_Cpu { public: - typedef BOOST::uint8_t uint8_t; - void reset(); enum { page_size = 0x2000 }; @@ -27,7 +25,7 @@ // not kept updated during a call to run() struct registers_t { - BOOST::uint16_t pc; + uint16_t pc; uint8_t a; uint8_t x; uint8_t y; @@ -86,7 +84,7 @@ inline int update_end_time( hes_time_t end, hes_time_t irq ); }; -inline BOOST::uint8_t const* Hes_Cpu::get_code( hes_addr_t addr ) +inline uint8_t const* Hes_Cpu::get_code( hes_addr_t addr ) { return state->code_map [addr >> page_shift] + addr #if !BLARGG_NONPORTABLE diff -Nru game-music-emu-0.6.2/gme/Hes_Emu.cpp game-music-emu-0.6.3/gme/Hes_Emu.cpp --- game-music-emu-0.6.2/gme/Hes_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Hes_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,9 +1,10 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Hes_Emu.h" #include "blargg_endian.h" #include +#include /* Copyright (C) 2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser @@ -25,6 +26,9 @@ long const period_60hz = 262 * 455L; // scanlines * clocks per scanline +using std::min; +using std::max; + Hes_Emu::Hes_Emu() { timer.raw_load = 0; @@ -133,7 +137,7 @@ static Music_Emu* new_hes_file() { return BLARGG_NEW Hes_File; } static gme_type_t_ const gme_hes_type_ = { "PC Engine", 256, &new_hes_emu, &new_hes_file, "HES", 1 }; -gme_type_t const gme_hes_type = &gme_hes_type_; +extern gme_type_t const gme_hes_type = &gme_hes_type_; // Setup diff -Nru game-music-emu-0.6.2/gme/Hes_Emu.h game-music-emu-0.6.3/gme/Hes_Emu.h --- game-music-emu-0.6.2/gme/Hes_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Hes_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // TurboGrafx-16/PC Engine HES music file emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef HES_EMU_H #define HES_EMU_H diff -Nru game-music-emu-0.6.2/gme/Kss_Cpu.cpp game-music-emu-0.6.3/gme/Kss_Cpu.cpp --- game-music-emu-0.6.2/gme/Kss_Cpu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Kss_Cpu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ /* Last validated with zexall 2006.11.14 2:19 PM @@ -136,7 +136,7 @@ #define R16( n, shift, offset )\ (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) -#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e +#define CASE5( a, b, c, d, e ) /*FALLTHRU*/ case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e #define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f #define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g #define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h @@ -162,11 +162,6 @@ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, }; -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - bool Kss_Cpu::run( cpu_time_t end_time ) { set_end_time( end_time ); @@ -174,8 +169,6 @@ this->state = &s; bool warning = false; - typedef BOOST::int8_t int8_t; - union { regs_t rg; pairs_t rp; @@ -185,10 +178,10 @@ rg = this->r.b; cpu_time_t s_time = s.time; - fuint16 pc = r.pc; - fuint16 sp = r.sp; - fuint16 ix = r.ix; // TODO: keep in memory for direct access? - fuint16 iy = r.iy; + uint_fast32_t pc = r.pc; + uint_fast32_t sp = r.sp; + uint_fast32_t ix = r.ix; // TODO: keep in memory for direct access? + uint_fast32_t iy = r.iy; int flags = r.b.flags; goto loop; @@ -210,7 +203,7 @@ uint8_t const* instr = s.read [pc >> page_shift]; #define GET_ADDR() GET_LE16( instr ) - fuint8 opcode; + uint_fast8_t opcode; // TODO: eliminate this special case #if BLARGG_NONPORTABLE @@ -243,7 +236,7 @@ 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F }; - fuint16 data; + uint_fast16_t data; data = base_timing [opcode]; if ( (s_time += data) >= 0 ) goto possibly_out_of_time; @@ -299,7 +292,7 @@ goto loop; case 0x3A:{// LD A,(addr) - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc += 2; rg.a = READ( addr ); goto loop; @@ -315,7 +308,7 @@ // JR // TODO: more efficient way to handle negative branch that wraps PC around #define JR( cond ) {\ - int offset = (BOOST::int8_t) data;\ + int offset = (int8_t) data;\ pc++;\ if ( !(cond) )\ goto jr_not_taken;\ @@ -387,7 +380,7 @@ case 0xCD:{// CALL addr call_taken: - fuint16 addr = pc + 2; + uint_fast16_t addr = pc + 2; pc = GET_ADDR(); sp = uint16_t (sp - 2); WRITE_WORD( sp, addr ); @@ -397,6 +390,7 @@ case 0xFF: // RST if ( pc > idle_addr ) goto hit_idle_addr; + // FALLTHRU CASE7( C7, CF, D7, DF, E7, EF, F7 ): data = pc; pc = opcode & 0x38; @@ -432,7 +426,7 @@ // ADC/ADD/SBC/SUB case 0x96: // SUB (HL) case 0x86: // ADD (HL) - flags &= ~C01; + flags &= ~C01; // FALLTHRU case 0x9E: // SBC (HL) case 0x8E: // ADC (HL) data = READ( rp.hl ); @@ -440,7 +434,7 @@ case 0xD6: // SUB A,imm case 0xC6: // ADD imm - flags &= ~C01; + flags &= ~C01; // FALLTHRU case 0xDE: // SBC A,imm case 0xCE: // ADC imm pc++; @@ -693,21 +687,21 @@ goto loop; case 0x2A:{// LD HL,(addr) - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc += 2; rp.hl = READ_WORD( addr ); goto loop; } case 0x32:{// LD (addr),A - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc += 2; WRITE( addr, rg.a ); goto loop; } case 0x22:{// LD (addr),HL - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, rp.hl ); goto loop; @@ -730,7 +724,7 @@ // Rotate case 0x07:{// RLCA - fuint16 temp = rg.a; + uint_fast16_t temp = rg.a; temp = (temp << 1) | (temp >> 7); flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08 | C01)); @@ -739,7 +733,7 @@ } case 0x0F:{// RRCA - fuint16 temp = rg.a; + uint_fast16_t temp = rg.a; flags = (flags & (S80 | Z40 | P04)) | (temp & C01); temp = (temp << 7) | (temp >> 1); @@ -753,12 +747,12 @@ flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08)) | (temp >> 8); - rg.a = temp; + rg.a = (uint8_t)temp; goto loop; } case 0x1F:{// RRA - fuint16 temp = (flags << 7) | (rg.a >> 1); + uint_fast16_t temp = (flags << 7) | (rg.a >> 1); flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08)) | (rg.a & C01); @@ -768,7 +762,7 @@ // Misc case 0x2F:{// CPL - fuint16 temp = ~rg.a; + uint_fast16_t temp = ~rg.a; flags = (flags & (S80 | Z40 | P04 | C01)) | (temp & (F20 | F08)) | (H10 | N02); @@ -794,21 +788,21 @@ goto loop; case 0xE3:{// EX (SP),HL - fuint16 temp = READ_WORD( sp ); + uint_fast16_t temp = READ_WORD( sp ); WRITE_WORD( sp, rp.hl ); rp.hl = temp; goto loop; } case 0xEB:{// EX DE,HL - fuint16 temp = rp.hl; + uint_fast16_t temp = rp.hl; rp.hl = rp.de; rp.de = temp; goto loop; } case 0xD9:{// EXX DE,HL - fuint16 temp = r.alt.w.bc; + uint_fast16_t temp = r.alt.w.bc; r.alt.w.bc = rp.bc; rp.bc = temp; @@ -849,7 +843,7 @@ // Rotate left #define RLC( read, write ) {\ - fuint8 result = read;\ + uint_fast8_t result = read;\ result = uint8_t (result << 1) | (result >> 7);\ flags = SZ28P( result ) | (result & C01);\ write;\ @@ -868,7 +862,7 @@ } #define RL( read, write ) {\ - fuint16 result = (read << 1) | (flags & C01);\ + uint_fast16_t result = (read << 1) | (flags & C01);\ flags = SZ28PC( result );\ write;\ goto loop;\ @@ -886,7 +880,7 @@ } #define SLA( read, add, write ) {\ - fuint16 result = (read << 1) | add;\ + uint_fast16_t result = (read << 1) | add;\ flags = SZ28PC( result );\ write;\ goto loop;\ @@ -917,7 +911,7 @@ // Rotate right #define RRC( read, write ) {\ - fuint8 result = read;\ + uint_fast8_t result = read;\ flags = result & C01;\ result = uint8_t (result << 7) | (result >> 1);\ flags |= SZ28P( result );\ @@ -937,8 +931,8 @@ } #define RR( read, write ) {\ - fuint8 result = read;\ - fuint8 temp = result & C01;\ + uint_fast8_t result = read;\ + uint_fast8_t temp = result & C01;\ result = uint8_t (flags << 7) | (result >> 1);\ flags = SZ28P( result ) | temp;\ write;\ @@ -957,7 +951,7 @@ } #define SRA( read, write ) {\ - fuint8 result = read;\ + uint_fast8_t result = read;\ flags = result & C01;\ result = (result & 0x80) | (result >> 1);\ flags |= SZ28P( result );\ @@ -977,7 +971,7 @@ } #define SRL( read, write ) {\ - fuint8 result = read;\ + uint_fast8_t result = read;\ flags = result & C01;\ result >>= 1;\ flags |= SZ28P( result );\ @@ -1121,7 +1115,7 @@ case 0x43: // LD (ADDR),BC case 0x53: // LD (ADDR),DE temp = R16( data, 4, 0x43 ); - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, temp ); goto loop; @@ -1129,21 +1123,21 @@ case 0x4B: // LD BC,(ADDR) case 0x5B:{// LD DE,(ADDR) - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc += 2; R16( data, 4, 0x4B ) = READ_WORD( addr ); goto loop; } case 0x7B:{// LD SP,(ADDR) - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc += 2; sp = READ_WORD( addr ); goto loop; } case 0x67:{// RRD - fuint8 temp = READ( rp.hl ); + uint_fast8_t temp = READ( rp.hl ); WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); temp = (rg.a & 0xF0) | (temp & 0x0F); flags = (flags & C01) | SZ28P( temp ); @@ -1152,7 +1146,7 @@ } case 0x6F:{// RLD - fuint8 temp = READ( rp.hl ); + uint_fast8_t temp = READ( rp.hl ); WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); temp = (rg.a & 0xF0) | (temp >> 4); flags = (flags & C01) | SZ28P( temp ); @@ -1176,7 +1170,7 @@ case 0xA1: // CPI case 0xB1: // CPIR inc = +1; - fuint16 addr = rp.hl; + uint_fast16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); @@ -1209,7 +1203,7 @@ case 0xA0: // LDI case 0xB0: // LDIR inc = +1; - fuint16 addr = rp.hl; + uint_fast16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); @@ -1241,7 +1235,7 @@ case 0xA3: // OUTI case 0xB3: // OTIR inc = +1; - fuint16 addr = rp.hl; + uint_fast16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); @@ -1267,7 +1261,7 @@ case 0xB2: // INIR inc = +1; - fuint16 addr = rp.hl; + uint_fast16_t addr = rp.hl; rp.hl = addr + inc; int temp = IN( rp.bc ); @@ -1332,7 +1326,7 @@ //////////////////////////////////////// DD/FD prefix { - fuint16 ixy; + uint_fast16_t ixy; case 0xDD: ixy = ix; goto ix_prefix; @@ -1354,7 +1348,7 @@ case 0x96: // SUB (IXY+disp) case 0x86: // ADD (IXY+disp) - flags &= ~C01; + flags &= ~C01; // FALLTHRU case 0x9E: // SBC (IXY+disp) case 0x8E: // ADC (IXY+disp) pc++; @@ -1364,7 +1358,7 @@ case 0x94: // SUB HXY case 0x84: // ADD HXY - flags &= ~C01; + flags &= ~C01; // FALLTHRU case 0x9C: // SBC HXY case 0x8C: // ADC HXY opcode = data; @@ -1373,7 +1367,7 @@ case 0x95: // SUB LXY case 0x85: // ADD LXY - flags &= ~C01; + flags &= ~C01; // FALLTHRU case 0x9D: // SBC LXY case 0x8D: // ADC LXY opcode = data; @@ -1528,7 +1522,7 @@ goto loop; case 0x22:{// LD (ADDR),IXY - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, ixy ); goto loop; @@ -1540,7 +1534,7 @@ goto set_ixy; case 0x2A:{// LD IXY,(addr) - fuint16 addr = GET_ADDR(); + uint_fast16_t addr = GET_ADDR(); ixy = READ_WORD( addr ); pc += 2; goto set_ixy; @@ -1564,7 +1558,7 @@ case 0x3E: goto srl_data_addr; // SRL (IXY) CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) - fuint8 temp = READ( data ); + uint_fast8_t temp = READ( data ); int masked = temp & 1 << (data2 >> 3 & 7); flags = (flags & C01) | H10 | (masked & S80) | @@ -1666,7 +1660,7 @@ goto loop; case 0xE3:{// EX (SP),IXY - fuint16 temp = READ_WORD( sp ); + uint_fast16_t temp = READ_WORD( sp ); WRITE_WORD( sp, ixy ); ixy = temp; goto set_ixy; diff -Nru game-music-emu-0.6.2/gme/Kss_Cpu.h game-music-emu-0.6.3/gme/Kss_Cpu.h --- game-music-emu-0.6.2/gme/Kss_Cpu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Kss_Cpu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Z80 CPU emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef KSS_CPU_H #define KSS_CPU_H @@ -15,8 +15,6 @@ class Kss_Cpu { public: - typedef BOOST::uint8_t uint8_t; - // Clear registers and map all pages to unmapped void reset( void* unmapped_write, void const* unmapped_read ); @@ -39,8 +37,6 @@ void set_time( cpu_time_t t ) { state->time = t - state->base; } void adjust_time( int delta ) { state->time += delta; } - typedef BOOST::uint16_t uint16_t; - #if BLARGG_BIG_ENDIAN struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; #else @@ -104,12 +100,12 @@ #define KSS_CPU_PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) #endif -inline BOOST::uint8_t* Kss_Cpu::write( unsigned addr ) +inline uint8_t* Kss_Cpu::write( unsigned addr ) { return state->write [addr >> page_shift] + KSS_CPU_PAGE_OFFSET( addr ); } -inline BOOST::uint8_t const* Kss_Cpu::read( unsigned addr ) +inline uint8_t const* Kss_Cpu::read( unsigned addr ) { return state->read [addr >> page_shift] + KSS_CPU_PAGE_OFFSET( addr ); } diff -Nru game-music-emu-0.6.2/gme/Kss_Emu.cpp game-music-emu-0.6.3/gme/Kss_Emu.cpp --- game-music-emu-0.6.2/gme/Kss_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Kss_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,9 +1,10 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Kss_Emu.h" #include "blargg_endian.h" #include +#include /* Copyright (C) 2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser @@ -21,6 +22,9 @@ long const clock_rate = 3579545; int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; +using std::min; +using std::max; + Kss_Emu::Kss_Emu() { sn = 0; @@ -102,7 +106,7 @@ static Music_Emu* new_kss_file() { return BLARGG_NEW Kss_File; } static gme_type_t_ const gme_kss_type_ = { "MSX", 256, &new_kss_emu, &new_kss_file, "KSS", 0x03 }; -gme_type_t const gme_kss_type = &gme_kss_type_; +extern gme_type_t const gme_kss_type = &gme_kss_type_; // Setup diff -Nru game-music-emu-0.6.2/gme/Kss_Emu.h game-music-emu-0.6.3/gme/Kss_Emu.h --- game-music-emu-0.6.2/gme/Kss_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Kss_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // MSX computer KSS music file emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef KSS_EMU_H #define KSS_EMU_H diff -Nru game-music-emu-0.6.2/gme/Kss_Scc_Apu.cpp game-music-emu-0.6.3/gme/Kss_Scc_Apu.cpp --- game-music-emu-0.6.2/gme/Kss_Scc_Apu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Kss_Scc_Apu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Kss_Scc_Apu.h" @@ -43,7 +43,7 @@ volume = (regs [0x8A + index] & 0x0F) * (amp_range / 256 / 15); } - BOOST::int8_t const* wave = (BOOST::int8_t*) regs + index * wave_size; + int8_t const* wave = (int8_t*) regs + index * wave_size; if ( index == osc_count - 1 ) wave -= wave_size; // last two oscs share wave { diff -Nru game-music-emu-0.6.2/gme/Kss_Scc_Apu.h game-music-emu-0.6.3/gme/Kss_Scc_Apu.h --- game-music-emu-0.6.2/gme/Kss_Scc_Apu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Kss_Scc_Apu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Konami SCC sound chip emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef KSS_SCC_APU_H #define KSS_SCC_APU_H diff -Nru game-music-emu-0.6.2/gme/libgme.pc.in game-music-emu-0.6.3/gme/libgme.pc.in --- game-music-emu-0.6.2/gme/libgme.pc.in 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/libgme.pc.in 2020-02-25 01:28:10.000000000 +0000 @@ -9,8 +9,8 @@ Name: Game_Music_Emu Description: A video game emulation library for music. -URL: http://code.google.com/p/game-music-emu/ +URL: https://bitbucket.org/mpyne/game-music-emu/wiki/Home Version: @GME_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lgme -Libs.private: -lstdc++ +Libs.private: -lstdc++ @PKG_CONFIG_ZLIB@ diff -Nru game-music-emu-0.6.2/gme/M3u_Playlist.cpp game-music-emu-0.6.3/gme/M3u_Playlist.cpp --- game-music-emu-0.6.2/gme/M3u_Playlist.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/M3u_Playlist.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "M3u_Playlist.h" #include "Music_Emu.h" @@ -52,9 +52,9 @@ blargg_err_t Gme_File::load_m3u( Data_Reader& in ) { return load_m3u_( playlist.load( in ) ); } -BLARGG_EXPORT gme_err_t gme_load_m3u( Music_Emu* me, const char* path ) { return me->load_m3u( path ); } +gme_err_t gme_load_m3u( Music_Emu* me, const char* path ) { return me->load_m3u( path ); } -BLARGG_EXPORT gme_err_t gme_load_m3u_data( Music_Emu* me, const void* data, long size ) +gme_err_t gme_load_m3u_data( Music_Emu* me, const void* data, long size ) { Mem_File_Reader in( data, size ); return me->load_m3u( in ); @@ -153,6 +153,23 @@ return in; } +static char* parse_mil_( char* in, int* out ) +{ + int n = 0; + int x = 100; + while ( 1 ) + { + unsigned d = from_dec( *in ); + if ( d > 9 ) + break; + in++; + n += d * x; + x /= 10; + *out = n; + } + return in; +} + static char* parse_int( char* in, int* out, int* result ) { return next_field( parse_int_( in, out ), result ); @@ -200,13 +217,22 @@ if ( n >= 0 ) { *out = n; - if ( *in == ':' ) + while ( *in == ':' ) { n = -1; in = parse_int_( in + 1, &n ); if ( n >= 0 ) *out = *out * 60 + n; } + *out *= 1000; + + if ( *in == '.' ) + { + n = -1; + in = parse_mil_( in + 1, &n ); + if ( n >= 0 ) + *out += n; + } } return in; } diff -Nru game-music-emu-0.6.2/gme/M3u_Playlist.h game-music-emu-0.6.3/gme/M3u_Playlist.h --- game-music-emu-0.6.2/gme/M3u_Playlist.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/M3u_Playlist.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // M3U playlist file parser, with support for subtrack information -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef M3U_PLAYLIST_H #define M3U_PLAYLIST_H @@ -36,7 +36,7 @@ bool decimal_track; // true if track was specified in hex // integers are -1 if not present int track; // 1-based - int length; // seconds + int length; // milliseconds int intro; int loop; int fade; diff -Nru game-music-emu-0.6.2/gme/Multi_Buffer.cpp game-music-emu-0.6.3/gme/Multi_Buffer.cpp --- game-music-emu-0.6.2/gme/Multi_Buffer.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Multi_Buffer.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -81,7 +81,7 @@ void Stereo_Buffer::bass_freq( int bass ) { - for ( unsigned i = 0; i < buf_count; i++ ) + for ( int i = 0; i < buf_count; i++ ) bufs [i].bass_freq( bass ); } @@ -96,7 +96,7 @@ void Stereo_Buffer::end_frame( blip_time_t clock_count ) { stereo_added = 0; - for ( unsigned i = 0; i < buf_count; i++ ) + for ( int i = 0; i < buf_count; i++ ) { stereo_added |= bufs [i].clear_modified() << i; bufs [i].end_frame( clock_count ); @@ -161,11 +161,11 @@ int c = BLIP_READER_READ( center ); blargg_long l = c + BLIP_READER_READ( left ); blargg_long r = c + BLIP_READER_READ( right ); - if ( (BOOST::int16_t) l != l ) + if ( (int16_t) l != l ) l = 0x7FFF - (l >> 24); BLIP_READER_NEXT( center, bass ); - if ( (BOOST::int16_t) r != r ) + if ( (int16_t) r != r ) r = 0x7FFF - (r >> 24); BLIP_READER_NEXT( left, bass ); @@ -191,11 +191,11 @@ for ( ; count; --count ) { blargg_long l = BLIP_READER_READ( left ); - if ( (BOOST::int16_t) l != l ) + if ( (int16_t) l != l ) l = 0x7FFF - (l >> 24); blargg_long r = BLIP_READER_READ( right ); - if ( (BOOST::int16_t) r != r ) + if ( (int16_t) r != r ) r = 0x7FFF - (r >> 24); BLIP_READER_NEXT( left, bass ); @@ -219,7 +219,7 @@ for ( ; count; --count ) { blargg_long s = BLIP_READER_READ( center ); - if ( (BOOST::int16_t) s != s ) + if ( (int16_t) s != s ) s = 0x7FFF - (s >> 24); BLIP_READER_NEXT( center, bass ); diff -Nru game-music-emu-0.6.2/gme/Music_Emu.cpp game-music-emu-0.6.3/gme/Music_Emu.cpp --- game-music-emu-0.6.2/gme/Music_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Music_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,9 +1,10 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Music_Emu.h" #include "Multi_Buffer.h" #include +#include /* Copyright (C) 2003-2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser @@ -18,12 +19,14 @@ #include "blargg_source.h" -int const stereo = 2; // number of channels for stereo int const silence_max = 6; // seconds int const silence_threshold = 0x10; long const fade_block_size = 512; int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) +using std::min; +using std::max; + Music_Emu::equalizer_t const Music_Emu::tv_eq = Music_Emu::make_equalizer( -8.0, 180 ); @@ -52,7 +55,7 @@ Music_Emu::Music_Emu() { effects_buffer = 0; - + multi_channel_ = false; sample_rate_ = 0; mute_mask_ = 0; tempo_ = 1.0; @@ -65,6 +68,8 @@ equalizer_.treble = -1.0; equalizer_.bass = 60; + emu_autoload_playback_limit_ = true; + static const char* const names [] = { "Voice 1", "Voice 2", "Voice 3", "Voice 4", "Voice 5", "Voice 6", "Voice 7", "Voice 8" @@ -96,6 +101,25 @@ set_equalizer_( eq ); } +bool Music_Emu::multi_channel() const +{ + return this->multi_channel_; +} + +blargg_err_t Music_Emu::set_multi_channel( bool ) +{ + // by default not supported, derived may override this + return "unsupported for this emulator type"; +} + +blargg_err_t Music_Emu::set_multi_channel_( bool isEnabled ) +{ + // multi channel support must be set at the very beginning + require( !sample_rate() ); + multi_channel_ = isEnabled; + return 0; +} + void Music_Emu::mute_voice( int index, bool mute ) { require( (unsigned) index < (unsigned) voice_count() ); @@ -145,7 +169,7 @@ if ( !ignore_silence_ ) { // play until non-silence or end of track - for ( long end = max_initial_silence * stereo * sample_rate(); emu_time < end; ) + for ( long end = max_initial_silence * out_channels() * sample_rate(); emu_time < end; ) { fill_buf(); if ( buf_remain | (int) emu_track_ended_ ) @@ -169,13 +193,23 @@ } } +bool Music_Emu::autoload_playback_limit() const +{ + return emu_autoload_playback_limit_; +} + +void Music_Emu::set_autoload_playback_limit( bool do_autoload_limit ) +{ + emu_autoload_playback_limit_ = do_autoload_limit; +} + // Tell/Seek blargg_long Music_Emu::msec_to_samples( blargg_long msec ) const { blargg_long sec = msec / 1000; msec -= sec * 1000; - return (sec * sample_rate() + msec * sample_rate() / 1000) * stereo; + return (sec * sample_rate() + msec * sample_rate() / 1000) * out_channels(); } long Music_Emu::tell_samples() const @@ -185,7 +219,7 @@ long Music_Emu::tell() const { - blargg_long rate = sample_rate() * stereo; + blargg_long rate = sample_rate() * out_channels(); blargg_long sec = out_time / rate; return sec * 1000 + (out_time - sec * rate) * 1000 / rate; } @@ -263,7 +297,7 @@ void Music_Emu::set_fade( long start_msec, long length_msec ) { - fade_step = sample_rate() * length_msec / (fade_block_size * fade_shift * 1000 / stereo); + fade_step = sample_rate() * length_msec / (fade_block_size * fade_shift * 1000 / out_channels()); fade_start = msec_to_samples( start_msec ); } @@ -345,7 +379,7 @@ else { require( current_track() >= 0 ); - require( out_count % stereo == 0 ); + require( out_count % out_channels() == 0 ); assert( emu_time >= out_time ); @@ -365,7 +399,7 @@ memset( out, 0, pos * sizeof *out ); silence_count -= pos; - if ( emu_time - silence_time > silence_max * stereo * sample_rate() ) + if ( emu_time - silence_time > silence_max * out_channels() * sample_rate() ) { track_ended_ = emu_track_ended_ = true; silence_count = 0; @@ -401,7 +435,7 @@ } } - if ( out_time > fade_start ) + if ( fade_start >= 0 && out_time > fade_start ) handle_fade( out_count, out ); } out_time += out_count; diff -Nru game-music-emu-0.6.2/gme/Music_Emu.h game-music-emu-0.6.3/gme/Music_Emu.h --- game-music-emu-0.6.2/gme/Music_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Music_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Common interface to game music file emulators -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef MUSIC_EMU_H #define MUSIC_EMU_H @@ -13,6 +13,11 @@ // Set output sample rate. Must be called only once before loading file. blargg_err_t set_sample_rate( long sample_rate ); + + // specifies if all 8 voices get rendered to their own stereo channel + // default implementation of Music_Emu always returns not supported error (i.e. no multichannel support by default) + // derived emus must override this if they support multichannel rendering + virtual blargg_err_t set_multi_channel( bool is_enabled ); // Start a track, where 0 is the first track. Also clears warning string. blargg_err_t start_track( int ); @@ -35,6 +40,8 @@ // Names of voices const char** voice_names() const; + + bool multi_channel() const; // Track status/control @@ -60,6 +67,13 @@ // true. Fade time can be changed while track is playing. void set_fade( long start_msec, long length_msec = 8000 ); + // Controls whether or not to automatically load and obey track length + // metadata for supported emulators. + // + // @since 0.6.2. + bool autoload_playback_limit() const; + void set_autoload_playback_limit( bool do_autoload_limit ); + // Disable automatic end-of-track detection and skipping of silence at beginning void ignore_silence( bool disable = true ); @@ -127,6 +141,7 @@ double gain() const { return gain_; } double tempo() const { return tempo_; } void remute_voices(); + blargg_err_t set_multi_channel_( bool is_enabled ); virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0; virtual void set_equalizer_( equalizer_t const& ) { } @@ -149,7 +164,11 @@ int mute_mask_; double tempo_; double gain_; - + bool multi_channel_; + + // returns the number of output channels, i.e. usually 2 for stereo, unlesss multi_channel_ == true + int out_channels() const { return this->multi_channel() ? 2*8 : 2; } + long sample_rate_; blargg_long msec_to_samples( blargg_long msec ) const; @@ -158,6 +177,7 @@ blargg_long out_time; // number of samples played since start of track blargg_long emu_time; // number of samples emulator has generated since start of track bool emu_track_ended_; // emulator has reached end of track + bool emu_autoload_playback_limit_; // whether to load and obey track length by default volatile bool track_ended_; void clear_track_vars(); void end_track_if_error( blargg_err_t ); @@ -179,7 +199,7 @@ void emu_play( long count, sample_t* out ); Multi_Buffer* effects_buffer; - friend Music_Emu* gme_new_emu( gme_type_t, int ); + friend Music_Emu* gme_internal_new_emu_( gme_type_t, int, bool ); friend void gme_set_stereo_depth( Music_Emu*, double ); }; diff -Nru game-music-emu-0.6.2/gme/Nes_Cpu.cpp game-music-emu-0.6.3/gme/Nes_Cpu.cpp --- game-music-emu-0.6.2/gme/Nes_Cpu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Nes_Cpu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Nes_Cpu.h" @@ -114,25 +114,20 @@ #define GET_SP() ((sp - 1) & 0xFF) #define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - bool Nes_Cpu::run( nes_time_t end_time ) { set_end_time( end_time ); state_t s = this->state_; this->state = &s; // even on x86, using s.time in place of s_time was slower - fint16 s_time = s.time; + int16_t s_time = s.time; // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 sp; + uint16_t pc = r.pc; + uint8_t a = r.a; + uint8_t x = r.x; + uint8_t y = r.y; + uint16_t sp; SET_SP( r.sp ); // status flags @@ -152,11 +147,11 @@ nz |= ~in & st_z;\ } while ( 0 ) - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + uint8_t status; + uint16_t c; // carry set if (c & 0x100) != 0 + uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 { - fuint8 temp = r.status; + uint8_t temp = r.status; SET_STATUS( temp ); } @@ -173,7 +168,7 @@ check( -32768 <= s_time && s_time < 32767 ); uint8_t const* instr = s.code_map [pc >> page_bits]; - fuint8 opcode; + uint8_t opcode; // TODO: eliminate this special case #if BLARGG_NONPORTABLE @@ -206,7 +201,7 @@ 3,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 // F }; // 0x00 was 7 and 0xF2 was 2 - fuint16 data; + uint16_t data; #if !BLARGG_CPU_X86 if ( s_time >= 0 ) @@ -247,13 +242,13 @@ #define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; #define IND_Y( cross, out ) {\ - fuint16 temp = READ_LOW( data ) + y;\ + uint16_t temp = READ_LOW( data ) + y;\ out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ cross( temp );\ } #define IND_X( out ) {\ - fuint16 temp = data + x;\ + uint16_t temp = data + x;\ out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ } @@ -265,7 +260,7 @@ IND_Y( HANDLE_PAGE_CROSSING, data )\ goto ptr##op;\ case op + 0x10: /* zp,X */\ - data = uint8_t (data + x);\ + data = uint8_t (data + x);/* FALLTHRU */\ case op + 0x00: /* zp */\ data = READ_LOW( data );\ goto imm##op;\ @@ -275,23 +270,23 @@ case op + 0x18: /* abs,X */\ data += x;\ ind##op:\ - HANDLE_PAGE_CROSSING( data );\ + HANDLE_PAGE_CROSSING( data );/* FALLTHRU */\ case op + 0x08: /* abs */\ ADD_PAGE();\ ptr##op:\ FLUSH_TIME();\ data = READ( data );\ - CACHE_TIME();\ + CACHE_TIME();/*FALLTHRU*/\ case op + 0x04: /* imm */\ imm##op: // TODO: more efficient way to handle negative branch that wraps PC around #define BRANCH( cond )\ {\ - fint16 offset = (BOOST::int8_t) data;\ - fuint16 extra_clock = (++pc & 0xFF) + offset;\ + int16_t offset = (int8_t) data;\ + uint16_t extra_clock = (++pc & 0xFF) + offset;\ if ( !(cond) ) goto dec_clock_loop;\ - pc = BOOST::uint16_t (pc + offset);\ + pc = uint16_t (pc + offset);\ s_time += extra_clock >> 8 & 1;\ goto loop;\ } @@ -312,7 +307,7 @@ BRANCH( (uint8_t) nz ); case 0x20: { // JSR - fuint16 temp = pc + 1; + uint16_t temp = pc + 1; pc = GET_ADDR(); WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); sp = (sp - 2) | 0x100; @@ -344,7 +339,7 @@ BRANCH( !(uint8_t) nz ); case 0x95: // STA zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0x85: // STA zp pc++; WRITE_LOW( data, a ); @@ -378,7 +373,7 @@ goto loop; { - fuint16 addr; + uint16_t addr; case 0x99: // STA abs,Y addr = y + GET_ADDR(); @@ -434,7 +429,7 @@ // common read instructions { - fuint16 addr; + uint16_t addr; case 0xA1: // LDA (ind,X) IND_X( addr ) @@ -492,23 +487,23 @@ // Load/store case 0x94: // STY zp,x - data = uint8_t (data + x); + data = uint8_t (data + x); // FALLTHRU case 0x84: // STY zp pc++; WRITE_LOW( data, y ); goto loop; case 0x96: // STX zp,y - data = uint8_t (data + y); + data = uint8_t (data + y); // FALLTHRU case 0x86: // STX zp pc++; WRITE_LOW( data, x ); goto loop; case 0xB6: // LDX zp,y - data = uint8_t (data + y); + data = uint8_t (data + y); // FALLTHRU case 0xA6: // LDX zp - data = READ_LOW( data ); + data = READ_LOW( data ); // FALLTHRU case 0xA2: // LDX #imm pc++; x = data; @@ -516,9 +511,9 @@ goto loop; case 0xB4: // LDY zp,x - data = uint8_t (data + x); + data = uint8_t (data + x); // FALLTHRU case 0xA4: // LDY zp - data = READ_LOW( data ); + data = READ_LOW( data ); // FALLTHRU case 0xA0: // LDY #imm pc++; y = data; @@ -527,7 +522,7 @@ case 0xBC: // LDY abs,X data += x; - HANDLE_PAGE_CROSSING( data ); + HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ case 0xAC:{// LDY abs unsigned addr = data + 0x100 * GET_MSB(); pc += 2; @@ -539,7 +534,7 @@ case 0xBE: // LDX abs,y data += y; - HANDLE_PAGE_CROSSING( data ); + HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ case 0xAE:{// LDX abs unsigned addr = data + 0x100 * GET_MSB(); pc += 2; @@ -550,7 +545,7 @@ } { - fuint8 temp; + uint8_t temp; case 0x8C: // STY abs temp = y; goto store_abs; @@ -583,7 +578,7 @@ } case 0xE4: // CPX zp - data = READ_LOW( data ); + data = READ_LOW( data );/*FALLTHRU*/ case 0xE0: // CPX #imm cpx_data: nz = x - data; @@ -602,7 +597,7 @@ } case 0xC4: // CPY zp - data = READ_LOW( data ); + data = READ_LOW( data );/*FALLTHRU*/ case 0xC0: // CPY #imm cpy_data: nz = y - data; @@ -659,8 +654,8 @@ ARITH_ADDR_MODES( 0x65 ) // ADC adc_imm: { - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend + int16_t carry = c >> 8 & 1; + int16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend status &= ~st_v; status |= ov >> 2 & 0x40; c = nz = a + data + carry; @@ -672,7 +667,7 @@ // Shift/rotate case 0x4A: // LSR A - c = 0; + c = 0;/*FALLTHRU*/ case 0x6A: // ROR A nz = c >> 1 & 0x80; c = a << 8; @@ -688,7 +683,7 @@ case 0x2A: { // ROL A nz = a << 1; - fint16 temp = c >> 8 & 1; + int16_t temp = c >> 8 & 1; c = nz; nz |= temp; a = (uint8_t) nz; @@ -696,9 +691,9 @@ } case 0x5E: // LSR abs,X - data += x; + data += x;/*FALLTHRU*/ case 0x4E: // LSR abs - c = 0; + c = 0;/*FALLTHRU*/ case 0x6E: // ROR abs ror_abs: { ADD_PAGE(); @@ -714,9 +709,9 @@ goto rol_abs; case 0x1E: // ASL abs,X - data += x; + data += x;/*FALLTHRU*/ case 0x0E: // ASL abs - c = 0; + c = 0;/*FALLTHRU*/ case 0x2E: // ROL abs rol_abs: ADD_PAGE(); @@ -738,9 +733,9 @@ goto ror_zp; case 0x56: // LSR zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0x46: // LSR zp - c = 0; + c = 0;/*FALLTHRU*/ case 0x66: // ROR zp ror_zp: { int temp = READ_LOW( data ); @@ -754,9 +749,9 @@ goto rol_zp; case 0x16: // ASL zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0x06: // ASL zp - c = 0; + c = 0;/*FALLTHRU*/ case 0x26: // ROL zp rol_zp: nz = c >> 8 & 1; @@ -772,15 +767,15 @@ INC_DEC_XY( y, -1 ) case 0xF6: // INC zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0xE6: // INC zp nz = 1; goto add_nz_zp; case 0xD6: // DEC zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0xC6: // DEC zp - nz = (unsigned) -1; + nz = (uint16_t) -1; add_nz_zp: nz += READ_LOW( data ); write_nz_zp: @@ -805,7 +800,7 @@ case 0xCE: // DEC abs data = GET_ADDR(); dec_ptr: - nz = (unsigned) -1; + nz = (uint16_t) -1; inc_common: FLUSH_TIME(); nz += READ( data ); @@ -846,7 +841,7 @@ goto loop; case 0x40:{// RTI - fuint8 temp = READ_LOW( sp ); + uint8_t temp = READ_LOW( sp ); pc = READ_LOW( 0x100 | (sp - 0xFF) ); pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; sp = (sp - 0xFD) | 0x100; @@ -863,9 +858,9 @@ } case 0x28:{// PLP - fuint8 temp = READ_LOW( sp ); + uint8_t temp = READ_LOW( sp ); sp = (sp - 0xFF) | 0x100; - fuint8 changed = status ^ temp; + uint8_t changed = status ^ temp; SET_STATUS( temp ); if ( !(changed & st_i) ) goto loop; // I flag didn't change @@ -875,7 +870,7 @@ } case 0x08: { // PHP - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); PUSH( temp | (st_b | st_r) ); goto loop; @@ -897,7 +892,7 @@ // Flags case 0x38: // SEC - c = (unsigned) ~0; + c = (uint16_t) ~0; goto loop; case 0x18: // CLC @@ -968,9 +963,9 @@ // SKW - Skip word case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: - HANDLE_PAGE_CROSSING( data + x ); + HANDLE_PAGE_CROSSING( data + x );/*FALLTHRU*/ case 0x0C: - pc++; + pc++;/*FALLTHRU*/ // SKB - Skip byte case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: @@ -983,12 +978,6 @@ case bad_opcode: // HLT pc--; - if ( pc > 0xFFFF ) - { - // handle wrap-around (assumes caller has put page of HLT at 0x10000) - pc &= 0xFFFF; - goto loop; - } case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: goto stop; @@ -996,15 +985,15 @@ // Unimplemented case 0xFF: // force 256-entry jump table for optimization purposes - c |= 1; + c |= 1;/*FALLTHRU*/ default: check( (unsigned) opcode <= 0xFF ); // skip over proper number of bytes static unsigned char const illop_lens [8] = { 0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0 }; - fuint8 opcode = instr [-1]; - fint16 len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3; + uint8_t opcode = instr [-1]; + int16_t len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3; if ( opcode == 0x9C ) len = 2; pc += len; @@ -1035,7 +1024,7 @@ pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); sp = (sp - 3) | 0x100; - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); temp |= st_r; if ( result_ ) @@ -1071,7 +1060,7 @@ r.y = y; { - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); r.status = temp; } diff -Nru game-music-emu-0.6.2/gme/Nes_Cpu.h game-music-emu-0.6.3/gme/Nes_Cpu.h --- game-music-emu-0.6.2/gme/Nes_Cpu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Nes_Cpu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // NES 6502 CPU emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef NES_CPU_H #define NES_CPU_H @@ -12,8 +12,6 @@ class Nes_Cpu { public: - typedef BOOST::uint8_t uint8_t; - // Clear registers, map low memory and its three mirrors to address 0, // and mirror unmapped_page in remaining memory void reset( void const* unmapped_page = 0 ); @@ -32,12 +30,12 @@ // NES 6502 registers. Not kept updated during a call to run(). struct registers_t { - BOOST::uint16_t pc; - BOOST::uint8_t a; - BOOST::uint8_t x; - BOOST::uint8_t y; - BOOST::uint8_t status; - BOOST::uint8_t sp; + uint16_t pc; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t status; + uint8_t sp; }; registers_t r; @@ -84,7 +82,7 @@ inline int update_end_time( nes_time_t end, nes_time_t irq ); }; -inline BOOST::uint8_t const* Nes_Cpu::get_code( nes_addr_t addr ) +inline uint8_t const* Nes_Cpu::get_code( nes_addr_t addr ) { return state->code_map [addr >> page_bits] + addr #if !BLARGG_NONPORTABLE diff -Nru game-music-emu-0.6.2/gme/Nes_Fme7_Apu.cpp game-music-emu-0.6.3/gme/Nes_Fme7_Apu.cpp --- game-music-emu-0.6.2/gme/Nes_Fme7_Apu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Nes_Fme7_Apu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Nes_Fme7_Apu.h" diff -Nru game-music-emu-0.6.2/gme/Nes_Fme7_Apu.h game-music-emu-0.6.3/gme/Nes_Fme7_Apu.h --- game-music-emu-0.6.2/gme/Nes_Fme7_Apu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Nes_Fme7_Apu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Sunsoft FME-7 sound emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef NES_FME7_APU_H #define NES_FME7_APU_H @@ -10,10 +10,10 @@ struct fme7_apu_state_t { enum { reg_count = 14 }; - BOOST::uint8_t regs [reg_count]; - BOOST::uint8_t phases [3]; // 0 or 1 - BOOST::uint8_t latch; - BOOST::uint16_t delays [3]; // a, b, c + uint8_t regs [reg_count]; + uint8_t phases [3]; // 0 or 1 + uint8_t latch; + uint16_t delays [3]; // a, b, c }; class Nes_Fme7_Apu : private fme7_apu_state_t { diff -Nru game-music-emu-0.6.2/gme/Nes_Namco_Apu.cpp game-music-emu-0.6.3/gme/Nes_Namco_Apu.cpp --- game-music-emu-0.6.2/gme/Nes_Namco_Apu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Nes_Namco_Apu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -90,7 +90,7 @@ osc.delay = 0; if ( time < end_time ) { - const BOOST::uint8_t* osc_reg = ® [i * 8 + 0x40]; + const uint8_t* osc_reg = ® [i * 8 + 0x40]; if ( !(osc_reg [4] & 0xE0) ) continue; diff -Nru game-music-emu-0.6.2/gme/Nes_Namco_Apu.h game-music-emu-0.6.3/gme/Nes_Namco_Apu.h --- game-music-emu-0.6.2/gme/Nes_Namco_Apu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Nes_Namco_Apu.h 2020-02-25 01:28:10.000000000 +0000 @@ -54,24 +54,24 @@ int addr_reg; enum { reg_count = 0x80 }; - BOOST::uint8_t reg [reg_count]; + uint8_t reg [reg_count]; Blip_Synth synth; - BOOST::uint8_t& access(); + uint8_t& access(); void run_until( blip_time_t ); }; /* struct namco_state_t { - BOOST::uint8_t regs [0x80]; - BOOST::uint8_t addr; - BOOST::uint8_t unused; - BOOST::uint8_t positions [8]; - BOOST::uint32_t delays [8]; + uint8_t regs [0x80]; + uint8_t addr; + uint8_t unused; + uint8_t positions [8]; + uint32_t delays [8]; }; */ -inline BOOST::uint8_t& Nes_Namco_Apu::access() +inline uint8_t& Nes_Namco_Apu::access() { int addr = addr_reg & 0x7F; if ( addr_reg & 0x80 ) diff -Nru game-music-emu-0.6.2/gme/Nes_Vrc6_Apu.h game-music-emu-0.6.3/gme/Nes_Vrc6_Apu.h --- game-music-emu-0.6.2/gme/Nes_Vrc6_Apu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Nes_Vrc6_Apu.h 2020-02-25 01:28:10.000000000 +0000 @@ -40,7 +40,7 @@ struct Vrc6_Osc { - BOOST::uint8_t regs [3]; + uint8_t regs [3]; Blip_Buffer* output; int delay; int last_amp; @@ -66,11 +66,11 @@ struct vrc6_apu_state_t { - BOOST::uint8_t regs [3] [3]; - BOOST::uint8_t saw_amp; - BOOST::uint16_t delays [3]; - BOOST::uint8_t phases [3]; - BOOST::uint8_t unused; + uint8_t regs [3] [3]; + uint8_t saw_amp; + uint16_t delays [3]; + uint8_t phases [3]; + uint8_t unused; }; inline void Nes_Vrc6_Apu::osc_output( int i, Blip_Buffer* buf ) diff -Nru game-music-emu-0.6.2/gme/Nsfe_Emu.cpp game-music-emu-0.6.3/gme/Nsfe_Emu.cpp --- game-music-emu-0.6.2/gme/Nsfe_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Nsfe_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,10 +1,11 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Nsfe_Emu.h" #include "blargg_endian.h" #include #include +#include /* Copyright (C) 2005-2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser @@ -19,6 +20,9 @@ #include "blargg_source.h" +using std::min; +using std::max; + Nsfe_Info::Nsfe_Info() { playlist_disabled = false; } Nsfe_Info::~Nsfe_Info() { } @@ -135,7 +139,7 @@ blargg_long size = get_le32( block_header [0] ); blargg_long tag = get_le32( block_header [1] ); - if ( size <= 0 ) + if ( size < 0 ) return "Corrupt file"; //debug_printf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) ); @@ -243,7 +247,7 @@ int remapped = remap_track( track ); if ( (unsigned) remapped < track_times.size() ) { - long length = (BOOST::int32_t) get_le32( track_times [remapped] ); + long length = (int32_t) get_le32( track_times [remapped] ); if ( length > 0 ) out->length = length; } @@ -301,7 +305,7 @@ static Music_Emu* new_nsfe_file() { return BLARGG_NEW Nsfe_File; } static gme_type_t_ const gme_nsfe_type_ = { "Nintendo NES", 0, &new_nsfe_emu, &new_nsfe_file, "NSFE", 1 }; -gme_type_t const gme_nsfe_type = &gme_nsfe_type_; +extern gme_type_t const gme_nsfe_type = &gme_nsfe_type_; blargg_err_t Nsfe_Emu::load_( Data_Reader& in ) diff -Nru game-music-emu-0.6.2/gme/Nsfe_Emu.h game-music-emu-0.6.3/gme/Nsfe_Emu.h --- game-music-emu-0.6.2/gme/Nsfe_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Nsfe_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Nintendo NES/Famicom NSFE music file emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef NSFE_EMU_H #define NSFE_EMU_H diff -Nru game-music-emu-0.6.2/gme/Nsf_Emu.cpp game-music-emu-0.6.3/gme/Nsf_Emu.cpp --- game-music-emu-0.6.2/gme/Nsf_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Nsf_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,10 +1,11 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Nsf_Emu.h" #include "blargg_endian.h" #include #include +#include #if !NSF_EMU_APU_ONLY #include "Nes_Namco_Apu.h" @@ -31,6 +32,9 @@ long const clock_divisor = 12; +using std::min; +using std::max; + Nsf_Emu::equalizer_t const Nsf_Emu::nes_eq = Music_Emu::make_equalizer( -1.0, 80 ); Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = @@ -130,7 +134,7 @@ static Music_Emu* new_nsf_file() { return BLARGG_NEW Nsf_File; } static gme_type_t_ const gme_nsf_type_ = { "Nintendo NES", 0, &new_nsf_emu, &new_nsf_file, "NSF", 1 }; -gme_type_t const gme_nsf_type = &gme_nsf_type_; +extern gme_type_t const gme_nsf_type = &gme_nsf_type_; // Setup diff -Nru game-music-emu-0.6.2/gme/Nsf_Emu.h game-music-emu-0.6.3/gme/Nsf_Emu.h --- game-music-emu-0.6.2/gme/Nsf_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Nsf_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Nintendo NES/Famicom NSF music file emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef NSF_EMU_H #define NSF_EMU_H diff -Nru game-music-emu-0.6.2/gme/Sap_Apu.cpp game-music-emu-0.6.3/gme/Sap_Apu.cpp --- game-music-emu-0.6.2/gme/Sap_Apu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Sap_Apu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Sap_Apu.h" diff -Nru game-music-emu-0.6.2/gme/Sap_Apu.h game-music-emu-0.6.3/gme/Sap_Apu.h --- game-music-emu-0.6.2/gme/Sap_Apu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Sap_Apu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Atari POKEY sound chip emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef SAP_APU_H #define SAP_APU_H diff -Nru game-music-emu-0.6.2/gme/Sap_Cpu.cpp game-music-emu-0.6.3/gme/Sap_Cpu.cpp --- game-music-emu-0.6.2/gme/Sap_Cpu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Sap_Cpu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Sap_Cpu.h" @@ -68,27 +68,21 @@ #define GET_SP() ((sp - 1) & 0xFF) #define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; -typedef blargg_long fint32; - bool Sap_Cpu::run( sap_time_t end_time ) { bool illegal_encountered = false; set_end_time( end_time ); state_t s = this->state_; this->state = &s; - fint32 s_time = s.time; + int32_t s_time = s.time; uint8_t* const mem = this->mem; // cache // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 sp; + uint16_t pc = r.pc; + uint8_t a = r.a; + uint8_t x = r.x; + uint8_t y = r.y; + uint16_t sp; SET_SP( r.sp ); // status flags @@ -108,11 +102,11 @@ nz |= ~in & st_z;\ } while ( 0 ) - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + uint8_t status; + uint16_t c; // carry set if (c & 0x100) != 0 + uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 { - fuint8 temp = r.status; + uint8_t temp = r.status; SET_STATUS( temp ); } @@ -135,7 +129,7 @@ check( (unsigned) x < 0x100 ); check( (unsigned) y < 0x100 ); - fuint8 opcode = mem [pc]; + uint8_t opcode = mem [pc]; pc++; uint8_t const* instr = mem + pc; @@ -159,7 +153,7 @@ 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F }; // 0x00 was 7 - fuint16 data; + uint16_t data; data = clock_table [opcode]; if ( (s_time += data) >= 0 ) goto possibly_out_of_time; @@ -191,13 +185,13 @@ #define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; #define IND_Y( cross, out ) {\ - fuint16 temp = READ_LOW( data ) + y;\ + uint16_t temp = READ_LOW( data ) + y;\ out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ cross( temp );\ } #define IND_X( out ) {\ - fuint16 temp = data + x;\ + uint16_t temp = data + x;\ out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ } @@ -209,7 +203,7 @@ IND_Y( HANDLE_PAGE_CROSSING, data )\ goto ptr##op;\ case op + 0x10: /* zp,X */\ - data = uint8_t (data + x);\ + data = uint8_t (data + x);/*FALLTHRU*/\ case op + 0x00: /* zp */\ data = READ_LOW( data );\ goto imm##op;\ @@ -219,21 +213,21 @@ case op + 0x18: /* abs,X */\ data += x;\ ind##op:\ - HANDLE_PAGE_CROSSING( data );\ + HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/\ case op + 0x08: /* abs */\ ADD_PAGE();\ ptr##op:\ FLUSH_TIME();\ data = READ( data );\ - CACHE_TIME();\ + CACHE_TIME();/*FALLTHRU*/\ case op + 0x04: /* imm */\ imm##op: // TODO: more efficient way to handle negative branch that wraps PC around #define BRANCH( cond )\ {\ - fint16 offset = (BOOST::int8_t) data;\ - fuint16 extra_clock = (++pc & 0xFF) + offset;\ + int16_t offset = (int8_t) data;\ + uint16_t extra_clock = (++pc & 0xFF) + offset;\ if ( !(cond) ) goto dec_clock_loop;\ pc += offset;\ s_time += extra_clock >> 8 & 1;\ @@ -256,7 +250,7 @@ BRANCH( (uint8_t) nz ); case 0x20: { // JSR - fuint16 temp = pc + 1; + uint16_t temp = pc + 1; pc = GET_ADDR(); WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); sp = (sp - 2) | 0x100; @@ -288,7 +282,7 @@ BRANCH( !(uint8_t) nz ); case 0x95: // STA zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0x85: // STA zp pc++; WRITE_LOW( data, a ); @@ -322,7 +316,7 @@ goto loop; { - fuint16 addr; + uint16_t addr; case 0x99: // STA abs,Y addr = y + GET_ADDR(); @@ -378,7 +372,7 @@ // common read instructions { - fuint16 addr; + uint16_t addr; case 0xA1: // LDA (ind,X) IND_X( addr ) @@ -436,23 +430,23 @@ // Load/store case 0x94: // STY zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0x84: // STY zp pc++; WRITE_LOW( data, y ); goto loop; case 0x96: // STX zp,y - data = uint8_t (data + y); + data = uint8_t (data + y);/*FALLTHRU*/ case 0x86: // STX zp pc++; WRITE_LOW( data, x ); goto loop; case 0xB6: // LDX zp,y - data = uint8_t (data + y); + data = uint8_t (data + y);/*FALLTHRU*/ case 0xA6: // LDX zp - data = READ_LOW( data ); + data = READ_LOW( data );/*FALLTHRU*/ case 0xA2: // LDX #imm pc++; x = data; @@ -460,9 +454,9 @@ goto loop; case 0xB4: // LDY zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0xA4: // LDY zp - data = READ_LOW( data ); + data = READ_LOW( data );/*FALLTHRU*/ case 0xA0: // LDY #imm pc++; y = data; @@ -471,7 +465,7 @@ case 0xBC: // LDY abs,X data += x; - HANDLE_PAGE_CROSSING( data ); + HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ case 0xAC:{// LDY abs unsigned addr = data + 0x100 * GET_MSB(); pc += 2; @@ -483,7 +477,7 @@ case 0xBE: // LDX abs,y data += y; - HANDLE_PAGE_CROSSING( data ); + HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ case 0xAE:{// LDX abs unsigned addr = data + 0x100 * GET_MSB(); pc += 2; @@ -494,7 +488,7 @@ } { - fuint8 temp; + uint8_t temp; case 0x8C: // STY abs temp = y; goto store_abs; @@ -527,7 +521,7 @@ } case 0xE4: // CPX zp - data = READ_LOW( data ); + data = READ_LOW( data );/*FALLTHRU*/ case 0xE0: // CPX #imm cpx_data: nz = x - data; @@ -546,7 +540,7 @@ } case 0xC4: // CPY zp - data = READ_LOW( data ); + data = READ_LOW( data ); // FALLTHRU case 0xC0: // CPY #imm cpy_data: nz = y - data; @@ -604,8 +598,8 @@ ARITH_ADDR_MODES( 0x65 ) // ADC adc_imm: { check( !(status & st_d) ); - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend + int16_t carry = c >> 8 & 1; + int16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend status &= ~st_v; status |= ov >> 2 & 0x40; c = nz = a + data + carry; @@ -617,7 +611,7 @@ // Shift/rotate case 0x4A: // LSR A - c = 0; + c = 0;/*FALLTHRU*/ case 0x6A: // ROR A nz = c >> 1 & 0x80; c = a << 8; @@ -633,7 +627,7 @@ case 0x2A: { // ROL A nz = a << 1; - fint16 temp = c >> 8 & 1; + int16_t temp = c >> 8 & 1; c = nz; nz |= temp; a = (uint8_t) nz; @@ -641,9 +635,9 @@ } case 0x5E: // LSR abs,X - data += x; + data += x;/*FALLTHRU*/ case 0x4E: // LSR abs - c = 0; + c = 0;/*FALLTHRU*/ case 0x6E: // ROR abs ror_abs: { ADD_PAGE(); @@ -659,9 +653,9 @@ goto rol_abs; case 0x1E: // ASL abs,X - data += x; + data += x;/*FALLTHRU*/ case 0x0E: // ASL abs - c = 0; + c = 0;/*FALLTHRU*/ case 0x2E: // ROL abs rol_abs: ADD_PAGE(); @@ -683,9 +677,9 @@ goto ror_zp; case 0x56: // LSR zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0x46: // LSR zp - c = 0; + c = 0;/*FALLTHRU*/ case 0x66: // ROR zp ror_zp: { int temp = READ_LOW( data ); @@ -699,9 +693,9 @@ goto rol_zp; case 0x16: // ASL zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0x06: // ASL zp - c = 0; + c = 0;/*FALLTHRU*/ case 0x26: // ROL zp rol_zp: nz = c >> 8 & 1; @@ -717,15 +711,15 @@ INC_DEC_XY( y, -1 ) case 0xF6: // INC zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0xE6: // INC zp nz = 1; goto add_nz_zp; case 0xD6: // DEC zp,x - data = uint8_t (data + x); + data = uint8_t (data + x);/*FALLTHRU*/ case 0xC6: // DEC zp - nz = (unsigned) -1; + nz = (uint16_t) -1; add_nz_zp: nz += READ_LOW( data ); write_nz_zp: @@ -750,7 +744,7 @@ case 0xCE: // DEC abs data = GET_ADDR(); dec_ptr: - nz = (unsigned) -1; + nz = (uint16_t) -1; inc_common: FLUSH_TIME(); nz += READ( data ); @@ -791,7 +785,7 @@ goto loop; case 0x40:{// RTI - fuint8 temp = READ_LOW( sp ); + uint8_t temp = READ_LOW( sp ); pc = READ_LOW( 0x100 | (sp - 0xFF) ); pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; sp = (sp - 0xFD) | 0x100; @@ -811,9 +805,9 @@ } case 0x28:{// PLP - fuint8 temp = READ_LOW( sp ); + uint8_t temp = READ_LOW( sp ); sp = (sp - 0xFF) | 0x100; - fuint8 changed = status ^ temp; + uint8_t changed = status ^ temp; SET_STATUS( temp ); if ( !(changed & st_i) ) goto loop; // I flag didn't change @@ -823,7 +817,7 @@ } case 0x08: { // PHP - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); PUSH( temp | (st_b | st_r) ); goto loop; @@ -843,7 +837,7 @@ // Flags case 0x38: // SEC - c = (unsigned) ~0; + c = (uint16_t) ~0; goto loop; case 0x18: // CLC @@ -912,9 +906,9 @@ // SKW - Skip word case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: - HANDLE_PAGE_CROSSING( data + x ); + HANDLE_PAGE_CROSSING( data + x );/*FALLTHRU*/ case 0x0C: - pc++; + pc++;/*FALLTHRU*/ // SKB - Skip byte case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: @@ -932,7 +926,6 @@ //case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2: default: - assert( (unsigned) opcode <= 0xFF ); illegal_encountered = true; pc--; goto stop; @@ -956,7 +949,7 @@ pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); sp = (sp - 3) | 0x100; - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); temp |= st_r; if ( result_ ) @@ -998,7 +991,7 @@ r.y = y; { - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); r.status = temp; } diff -Nru game-music-emu-0.6.2/gme/Sap_Cpu.h game-music-emu-0.6.3/gme/Sap_Cpu.h --- game-music-emu-0.6.2/gme/Sap_Cpu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Sap_Cpu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Atari 6502 CPU emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef SAP_CPU_H #define SAP_CPU_H @@ -12,8 +12,6 @@ class Sap_Cpu { public: - typedef BOOST::uint8_t uint8_t; - // Clear all registers and keep pointer to 64K memory passed in void reset( void* mem_64k ); @@ -23,12 +21,12 @@ // Registers are not updated until run() returns (except I flag in status) struct registers_t { - BOOST::uint16_t pc; - BOOST::uint8_t a; - BOOST::uint8_t x; - BOOST::uint8_t y; - BOOST::uint8_t status; - BOOST::uint8_t sp; + uint16_t pc; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t status; + uint8_t sp; }; registers_t r; diff -Nru game-music-emu-0.6.2/gme/Sap_Emu.cpp game-music-emu-0.6.3/gme/Sap_Emu.cpp --- game-music-emu-0.6.2/gme/Sap_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Sap_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,9 +1,10 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Sap_Emu.h" #include "blargg_endian.h" #include +#include /* Copyright (C) 2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser @@ -20,6 +21,9 @@ long const base_scanline_period = 114; +using std::min; +using std::max; + Sap_Emu::Sap_Emu() { set_type( gme_sap_type ); @@ -236,8 +240,7 @@ static Music_Emu* new_sap_file() { return BLARGG_NEW Sap_File; } static gme_type_t_ const gme_sap_type_ = { "Atari XL", 0, &new_sap_emu, &new_sap_file, "SAP", 1 }; -gme_type_t const gme_sap_type = &gme_sap_type_; - +extern gme_type_t const gme_sap_type = &gme_sap_type_; // Setup diff -Nru game-music-emu-0.6.2/gme/Sap_Emu.h game-music-emu-0.6.3/gme/Sap_Emu.h --- game-music-emu-0.6.2/gme/Sap_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Sap_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Atari XL/XE SAP music file emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef SAP_EMU_H #define SAP_EMU_H @@ -54,8 +54,7 @@ // large items struct { byte padding1 [0x100]; - byte ram [0x10000]; - byte padding2 [0x100]; + byte ram [0x10000 + 0x100]; } mem; Sap_Apu_Impl apu_impl; diff -Nru game-music-emu-0.6.2/gme/Snes_Spc.cpp game-music-emu-0.6.3/gme/Snes_Spc.cpp --- game-music-emu-0.6.2/gme/Snes_Spc.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Snes_Spc.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // SPC emulation support: init, sample buffering, reset, SPC loading -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Snes_Spc.h" @@ -143,8 +143,8 @@ load_regs( &RAM [0xF0] ); // Put STOP instruction around memory to catch PC underflow/overflow - memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); - memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 ); + memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); + memset( m.ram.ram + 0x10000, cpu_pad_fill, sizeof m.ram.padding1 ); } // Registers were just loaded. Applies these new values. @@ -248,6 +248,8 @@ void Snes_Spc::clear_echo() { +// Allows playback of dodgy Super Mario World mod SPCs +#ifndef SPC_ISOLATED_ECHO_BUFFER if ( !(dsp.read( Spc_Dsp::r_flg ) & 0x20) ) { int addr = 0x100 * dsp.read( Spc_Dsp::r_esa ); @@ -256,6 +258,7 @@ end = 0x10000; memset( &RAM [addr], 0xFF, end - addr ); } +#endif } diff -Nru game-music-emu-0.6.2/gme/Snes_Spc.h game-music-emu-0.6.3/gme/Snes_Spc.h --- game-music-emu-0.6.2/gme/Snes_Spc.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Snes_Spc.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,16 +1,16 @@ // SNES SPC-700 APU emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef SNES_SPC_H #define SNES_SPC_H #include "Spc_Dsp.h" #include "blargg_endian.h" +#include + struct Snes_Spc { public: - typedef BOOST::uint8_t uint8_t; - // Must be called once before using blargg_err_t init(); @@ -108,12 +108,12 @@ // TODO: document struct regs_t { - int pc; - int a; - int x; - int y; - int psw; - int sp; + uint16_t pc; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t psw; + uint8_t sp; }; regs_t& smp_regs() { return m.cpu_regs; } @@ -123,8 +123,6 @@ public: BLARGG_DISABLE_NOTHROW - typedef BOOST::uint16_t uint16_t; - // Time relative to m_spc_time. Speeds up code a bit by eliminating need to // constantly add m_spc_time to time from CPU. CPU uses time that ends at // 0 to eliminate reloading end time every instruction. It pays off. @@ -184,13 +182,11 @@ struct { - // padding to neutralize address overflow - union { - uint8_t padding1 [0x100]; - uint16_t align; // makes compiler align data for 16-bit access - } padding1 [1]; - uint8_t ram [0x10000]; - uint8_t padding2 [0x100]; + // padding to neutralize address overflow -- but this is + // still undefined behavior! TODO: remove and instead properly + // guard usage of emulated memory + uint8_t padding1 [0x100]; + alignas(uint16_t) uint8_t ram [0x10000 + 0x100]; } ram; }; state_t m; @@ -226,13 +222,13 @@ Timer* run_timer ( Timer* t, rel_time_t ); int dsp_read ( rel_time_t ); void dsp_write ( int data, rel_time_t ); - void cpu_write_smp_reg_( int data, rel_time_t, int addr ); - void cpu_write_smp_reg ( int data, rel_time_t, int addr ); - void cpu_write_high ( int data, int i, rel_time_t ); - void cpu_write ( int data, int addr, rel_time_t ); + void cpu_write_smp_reg_( int data, rel_time_t, uint16_t addr ); + void cpu_write_smp_reg ( int data, rel_time_t, uint16_t addr ); + void cpu_write_high ( int data, uint8_t i ); + void cpu_write ( int data, uint16_t addr, rel_time_t ); int cpu_read_smp_reg ( int i, rel_time_t ); - int cpu_read ( int addr, rel_time_t ); - unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t ); + int cpu_read ( uint16_t addr, rel_time_t ); + unsigned CPU_mem_bit ( uint16_t pc, rel_time_t ); bool check_echo_access ( int addr ); uint8_t* run_until_( time_t end_time ); diff -Nru game-music-emu-0.6.2/gme/Spc_Cpu.cpp game-music-emu-0.6.3/gme/Spc_Cpu.cpp --- game-music-emu-0.6.2/gme/Spc_Cpu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Spc_Cpu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Core SPC emulation: CPU, timers, SMP registers, memory -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Snes_Spc.h" @@ -284,7 +284,7 @@ // If write isn't preceded by read, data has this added to it int const no_read_before_write = 0x2000; -void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, int addr ) +void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr ) { switch ( addr ) { @@ -385,7 +385,7 @@ } } -void Snes_Spc::cpu_write_smp_reg( int data, rel_time_t time, int addr ) +void Snes_Spc::cpu_write_smp_reg( int data, rel_time_t time, uint16_t addr ) { if ( addr == r_dspdata ) // 99% dsp_write( data, time ); @@ -393,33 +393,23 @@ cpu_write_smp_reg_( data, time, addr ); } -void Snes_Spc::cpu_write_high( int data, int i, rel_time_t time ) +void Snes_Spc::cpu_write_high( int data, uint8_t i ) { - if ( i < rom_size ) - { - m.hi_ram [i] = (uint8_t) data; - if ( m.rom_enabled ) - RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM - } - else - { - assert( RAM [i + rom_addr] == (uint8_t) data ); - RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding - cpu_write( data, i + rom_addr - 0x10000, time ); - } + assert ( i < rom_size ); + m.hi_ram [i] = (uint8_t) data; + if ( m.rom_enabled ) + RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM } -int const bits_in_int = CHAR_BIT * sizeof (int); - -void Snes_Spc::cpu_write( int data, int addr, rel_time_t time ) +void Snes_Spc::cpu_write( int data, uint16_t addr, rel_time_t time ) { MEM_ACCESS( time, addr ) // RAM RAM [addr] = (uint8_t) data; - int reg = addr - 0xF0; - if ( reg >= 0 ) // 64% + if ( addr >= 0xF0 ) // 64% { + const uint16_t reg = addr - 0xF0; // $F0-$FF if ( reg < reg_count ) // 87% { @@ -437,12 +427,8 @@ cpu_write_smp_reg( data, time, reg ); } // High mem/address wrap-around - else - { - reg -= rom_addr - 0xF0; - if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around - cpu_write_high( data, reg, time ); - } + else if ( addr >= rom_addr ) // 1% in IPL ROM area or address wrapped around + cpu_write_high( data, addr - rom_addr ); } } @@ -463,7 +449,7 @@ return result; } -int Snes_Spc::cpu_read( int addr, rel_time_t time ) +int Snes_Spc::cpu_read( uint16_t addr, rel_time_t time ) { MEM_ACCESS( time, addr ) @@ -507,7 +493,7 @@ // Prefix and suffix for CPU emulator function #define SPC_CPU_RUN_FUNC \ -BOOST::uint8_t* Snes_Spc::run_until_( time_t end_time )\ +uint8_t* Snes_Spc::run_until_( time_t end_time )\ {\ rel_time_t rel_time = m.spc_time - end_time;\ assert( rel_time <= 0 );\ diff -Nru game-music-emu-0.6.2/gme/Spc_Cpu.h game-music-emu-0.6.3/gme/Spc_Cpu.h --- game-music-emu-0.6.2/gme/Spc_Cpu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Spc_Cpu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ /* Copyright (C) 2004-2007 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser @@ -66,62 +66,37 @@ #define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) ) #define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data ) -#define READ_PROG16( addr ) GET_LE16( ram + (addr) ) +#define READ_PROG16( addr ) (RAM [(addr) & 0xffff] | (RAM [((addr) + 1) & 0xffff] << 8)) -#define SET_PC( n ) (pc = ram + (n)) -#define GET_PC() (pc - ram) -#define READ_PC( pc ) (*(pc)) -#define READ_PC16( pc ) GET_LE16( pc ) - -// TODO: remove non-wrapping versions? -#define SPC_NO_SP_WRAPAROUND 0 - -#define SET_SP( v ) (sp = ram + 0x101 + ((uint8_t) v)) -#define GET_SP() (uint8_t (sp - 0x101 - ram)) - -#if SPC_NO_SP_WRAPAROUND -#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) -#define PUSH( v ) (void) (*--sp = (uint8_t) (v)) -#define POP( out ) (void) ((out) = *sp++) +#define SET_PC( n ) (pc = n) +#define GET_PC() (pc) +#define READ_PC( pc ) (ram [pc]) +#define READ_PC16( pc ) READ_PROG16( pc ) + +#define SET_SP( v ) (sp = v) +#define GET_SP() ((uint8_t) (sp)) -#else #define PUSH16( data )\ {\ - int addr = (sp -= 2) - ram;\ - if ( addr > 0x100 )\ - {\ - SET_LE16( sp, data );\ - }\ - else\ - {\ - ram [(uint8_t) addr + 0x100] = (uint8_t) data;\ - sp [1] = (uint8_t) (data >> 8);\ - sp += 0x100;\ - }\ + PUSH( (data & 0xff00) >> 8 );\ + PUSH( data & 0xff );\ } #define PUSH( data )\ {\ - *--sp = (uint8_t) (data);\ - if ( sp - ram == 0x100 )\ - sp += 0x100;\ + ram [0x100 + sp] = (uint8_t) (data);\ + --sp;\ } #define POP( out )\ {\ - out = *sp++;\ - if ( sp - ram == 0x201 )\ - {\ - out = sp [-0x101];\ - sp -= 0x100;\ - }\ + ++sp;\ + out = ram [0x100 + sp];\ } -#endif - #define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) -unsigned Snes_Spc::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time ) +unsigned Snes_Spc::CPU_mem_bit( uint16_t pc, rel_time_t rel_time ) { unsigned addr = READ_PC16( pc ); unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); @@ -163,11 +138,11 @@ SPC_CPU_RUN_FUNC { uint8_t* const ram = RAM; - int a = m.cpu_regs.a; - int x = m.cpu_regs.x; - int y = m.cpu_regs.y; - uint8_t const* pc; - uint8_t* sp; + uint8_t a = m.cpu_regs.a; + uint8_t x = m.cpu_regs.x; + uint8_t y = m.cpu_regs.y; + uint16_t pc; + uint8_t sp; int psw; int c; int nz; @@ -183,7 +158,7 @@ // Main loop cbranch_taken_loop: - pc += *(BOOST::int8_t const*) pc; + pc += (int8_t) ram [pc]; inc_pc_loop: pc++; loop: @@ -195,7 +170,7 @@ check( (unsigned) x < 0x100 ); check( (unsigned) y < 0x100 ); - opcode = *pc; + opcode = ram [pc]; if ( (rel_time += m.cycle_table [opcode]) > 0 ) goto out_of_time; @@ -218,7 +193,8 @@ */ // TODO: if PC is at end of memory, this will get wrong operand (very obscure) - data = *++pc; + pc++; + data = ram [pc]; switch ( opcode ) { @@ -227,10 +203,10 @@ #define BRANCH( cond )\ {\ pc++;\ - pc += (BOOST::int8_t) data;\ + pc += (int8_t) data;\ if ( cond )\ goto loop;\ - pc -= (BOOST::int8_t) data;\ + pc -= (int8_t) data;\ rel_time -= 2;\ goto loop;\ } @@ -249,23 +225,12 @@ } case 0x6F:// RET - #if SPC_NO_SP_WRAPAROUND - { - SET_PC( GET_LE16( sp ) ); - sp += 2; - } - #else { - int addr = sp - ram; - SET_PC( GET_LE16( sp ) ); - sp += 2; - if ( addr < 0x1FF ) - goto loop; - - SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] ); - sp -= 0x100; + uint8_t l, h; + POP( l ); + POP( h ); + SET_PC( l | (h << 8) ); } - #endif goto loop; case 0xE4: // MOV a,dp @@ -294,8 +259,7 @@ REGS [i] = (uint8_t) data; // Registers other than $F2 and $F4-$F7 - //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 ) - if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12% + if ( i != 2 && (i < 4 || i > 7)) // 12% cpu_write_smp_reg( data, rel_time, i ); } } @@ -328,7 +292,7 @@ #endif goto loop; -#define CASE( n ) case n: +#define CASE( n ) /*FALLTHRU*/case n: // Define common address modes based on opcode for immediate mode. Execution // ends with data set to the address of the operand. @@ -347,13 +311,13 @@ data += y;\ goto abs_##op;\ CASE( op + 0x0D ) /* abs+X */\ - data += x;\ + data += x;/*FALLTHRU*/\ CASE( op - 0x03 ) /* abs */\ abs_##op:\ data += 0x100 * READ_PC( ++pc );\ goto end_##op;\ CASE( op + 0x0C ) /* dp+X */\ - data = (uint8_t) (data + x); + data = (uint8_t) (data + x);/*FALLTHRU*/ #define ADDR_MODES_NO_DP( op )\ ADDR_MODES_( op )\ @@ -385,7 +349,7 @@ goto inc_pc_loop; case 0xF9: // MOV X,dp+Y - data = (uint8_t) (data + y); + data = (uint8_t) (data + y);/*FALLTHRU*/ case 0xF8: // MOV X,dp READ_DP_TIMER( 0, data, x = nz ); goto inc_pc_loop; @@ -393,14 +357,14 @@ case 0xE9: // MOV X,abs data = READ_PC16( pc ); ++pc; - data = READ( 0, data ); + data = READ( 0, data );/*FALLTHRU*/ case 0xCD: // MOV X,imm x = data; nz = data; goto inc_pc_loop; case 0xFB: // MOV Y,dp+X - data = (uint8_t) (data + x); + data = (uint8_t) (data + x);/*FALLTHRU*/ case 0xEB: // MOV Y,dp // 70% from timer pc++; @@ -440,13 +404,13 @@ } case 0xD9: // MOV dp+Y,X - data = (uint8_t) (data + y); + data = (uint8_t) (data + y);/*FALLTHRU*/ case 0xD8: // MOV dp,X WRITE( 0, data + dp, x ); goto inc_pc_loop; case 0xDB: // MOV dp+X,Y - data = (uint8_t) (data + x); + data = (uint8_t) (data + x);/*FALLTHRU*/ case 0xCB: // MOV dp,Y WRITE( 0, data + dp, y ); goto inc_pc_loop; @@ -492,7 +456,7 @@ #define LOGICAL_OP( op, func )\ ADDR_MODES( op ) /* addr */\ - data = READ( 0, data );\ + data = READ( 0, data );/*FALLTHRU*/\ case op: /* imm */\ nz = a func##= data;\ goto inc_pc_loop;\ @@ -504,7 +468,7 @@ case op + 0x01: /* dp,dp */\ data = READ_DP( -3, data );\ case op + 0x10:{/*dp,imm*/\ - uint8_t const* addr2 = pc + 1;\ + uint16_t addr2 = pc + 1;\ pc += 2;\ addr = READ_PC( addr2 ) + dp;\ }\ @@ -523,7 +487,7 @@ // 4. 8-BIT ARITHMETIC OPERATION COMMANDS ADDR_MODES( 0x68 ) // CMP addr - data = READ( 0, data ); + data = READ( 0, data );/*FALLTHRU*/ case 0x68: // CMP imm nz = a - data; c = ~nz; @@ -538,7 +502,7 @@ goto loop; case 0x69: // CMP dp,dp - data = READ_DP( -3, data ); + data = READ_DP( -3, data );/*FALLTHRU*/ case 0x78: // CMP dp,imm nz = READ_DP( -1, READ_PC( ++pc ) ) - data; c = ~nz; @@ -552,7 +516,7 @@ data = READ_PC16( pc ); pc++; cmp_x_addr: - data = READ( 0, data ); + data = READ( 0, data );/*FALLTHRU*/ case 0xC8: // CMP X,imm nz = x - data; c = ~nz; @@ -566,7 +530,7 @@ data = READ_PC16( pc ); pc++; cmp_y_addr: - data = READ( 0, data ); + data = READ( 0, data );/*FALLTHRU*/ case 0xAD: // CMP Y,imm nz = y - data; c = ~nz; @@ -641,7 +605,7 @@ case 0x9B: // DEC dp+X case 0xBB: // INC dp+X - data = (uint8_t) (data + x); + data = (uint8_t) (data + x); /* fallthrough */ case 0x8B: // DEC dp case 0xAB: // INC dp data += dp; @@ -659,7 +623,7 @@ // 7. SHIFT, ROTATION COMMANDS case 0x5C: // LSR A - c = 0; + c = 0; /*fallthrough*/ case 0x7C:{// ROR A nz = (c >> 1 & 0x80) | (a >> 1); c = a << 8; @@ -668,7 +632,7 @@ } case 0x1C: // ASL A - c = 0; + c = 0; /*fallthrough*/ case 0x3C:{// ROL A int temp = c >> 8 & 1; c = a << 1; @@ -682,14 +646,14 @@ data += dp; goto rol_mem; case 0x1B: // ASL dp+X - c = 0; + c = 0; /*fallthrough*/ case 0x3B: // ROL dp+X - data = (uint8_t) (data + x); + data = (uint8_t) (data + x); /*fallthrough*/ case 0x2B: // ROL dp data += dp; goto rol_mem; case 0x0C: // ASL abs - c = 0; + c = 0; /*fallthrough*/ case 0x2C: // ROL abs data = READ_PC16( pc ); pc++; @@ -704,14 +668,14 @@ data += dp; goto ror_mem; case 0x5B: // LSR dp+X - c = 0; + c = 0; /*fallthrough*/ case 0x7B: // ROR dp+X - data = (uint8_t) (data + x); + data = (uint8_t) (data + x); /*fallthrough*/ case 0x6B: // ROR dp data += dp; goto ror_mem; case 0x4C: // LSR abs - c = 0; + c = 0; /*fallthrough*/ case 0x6C: // ROR abs data = READ_PC16( pc ); pc++; @@ -878,7 +842,7 @@ // 12. BRANCHING COMMANDS case 0x2F: // BRA rel - pc += (BOOST::int8_t) data; + pc += (int8_t) data; goto inc_pc_loop; case 0x30: // BMI @@ -1002,10 +966,12 @@ { int temp; + uint8_t l, h; case 0x7F: // RET1 - temp = *sp; - SET_PC( GET_LE16( sp + 1 ) ); - SET_SP( GET_SP() + 3 ); + POP (temp); + POP (l); + POP (h); + SET_PC( l | (h << 8) ); goto set_psw; case 0x8E: // POP PSW POP( temp ); @@ -1180,11 +1146,8 @@ case 0xFF:{// STOP // handle PC wrap-around - unsigned addr = GET_PC() - 1; - if ( addr >= 0x10000 ) + if ( pc == 0x0000 ) { - addr &= 0xFFFF; - SET_PC( addr ); debug_printf( "SPC: PC wrapped around\n" ); goto loop; } @@ -1199,14 +1162,12 @@ } // switch assert( 0 ); // catch any unhandled instructions -} +} out_of_time: - rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode + rel_time -= m.cycle_table [ ram [pc] ]; // undo partial execution of opcode stop: // Uncache registers - if ( GET_PC() >= 0x10000 ) - debug_printf( "SPC: PC wrapped around\n" ); m.cpu_regs.pc = (uint16_t) GET_PC(); m.cpu_regs.sp = ( uint8_t) GET_SP(); m.cpu_regs.a = ( uint8_t) a; diff -Nru game-music-emu-0.6.2/gme/Spc_Dsp.cpp game-music-emu-0.6.3/gme/Spc_Dsp.cpp --- game-music-emu-0.6.2/gme/Spc_Dsp.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Spc_Dsp.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Spc_Dsp.h" @@ -28,11 +28,11 @@ // TODO: add to blargg_endian.h -#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) +#define GET_LE16SA( addr ) ((int16_t) GET_LE16( addr )) #define GET_LE16A( addr ) GET_LE16( addr ) #define SET_LE16A( addr, data ) SET_LE16( addr, data ) -static BOOST::uint8_t const initial_regs [Spc_Dsp::register_count] = +static uint8_t const initial_regs [Spc_Dsp::register_count] = { 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, @@ -192,6 +192,9 @@ return; uint8_t* const ram = m.ram; +#ifdef SPC_ISOLATED_ECHO_BUFFER + uint8_t* const echo_ram = m.echo_ram; +#endif uint8_t const* const dir = &ram [REG(dir) * 0x100]; int const slow_gaussian = (REG(pmon) >> 1) | REG(non); int const noise_rate = REG(flg) & 0x1F; @@ -498,8 +501,9 @@ // Decode four samples for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) { - // Extract upper nybble and scale appropriately - int s = ((int16_t) nybbles >> right_shift) << left_shift; + // Extract upper nybble and scale appropriately. Every cast is + // necessary to maintain correctness and avoid undef behavior + int s = int16_t(uint16_t((int16_t) nybbles >> right_shift) << left_shift); // Apply IIR filter (8 is the most commonly used) int const filter = brr_header & 0x0C; @@ -547,7 +551,12 @@ // Echo position int echo_offset = m.echo_offset; +#ifdef SPC_ISOLATED_ECHO_BUFFER + // And here, we win no awards for accuracy, but gain playback of dodgy Super Mario World mod SPCs + uint8_t* const echo_ptr = &echo_ram [(REG(esa) * 0x100 + echo_offset) & 0xFFFF]; +#else uint8_t* const echo_ptr = &ram [(REG(esa) * 0x100 + echo_offset) & 0xFFFF]; +#endif if ( !echo_offset ) m.echo_length = (REG(edl) & 0x0F) * 0x800; echo_offset += 4; diff -Nru game-music-emu-0.6.2/gme/Spc_Dsp.h game-music-emu-0.6.3/gme/Spc_Dsp.h --- game-music-emu-0.6.2/gme/Spc_Dsp.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Spc_Dsp.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one) -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef SPC_DSP_H #define SPC_DSP_H @@ -8,8 +8,6 @@ struct Spc_Dsp { public: - typedef BOOST::uint8_t uint8_t; - // Setup // Initializes DSP and has it use the 64K RAM provided @@ -89,9 +87,6 @@ public: BLARGG_DISABLE_NOTHROW - typedef BOOST::int8_t int8_t; - typedef BOOST::int16_t int16_t; - enum { echo_hist_size = 8 }; enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; @@ -115,6 +110,11 @@ { uint8_t regs [register_count]; +#ifdef SPC_ISOLATED_ECHO_BUFFER + // Echo buffer, for dodgy SPC rips that were only made to work in dodgy emulators + uint8_t echo_ram [64 * 1024]; +#endif + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) int echo_hist [echo_hist_size * 2] [2]; int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] diff -Nru game-music-emu-0.6.2/gme/Spc_Emu.cpp game-music-emu-0.6.3/gme/Spc_Emu.cpp --- game-music-emu-0.6.2/gme/Spc_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Spc_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,10 +1,11 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Spc_Emu.h" #include "blargg_endian.h" #include #include +#include /* Copyright (C) 2004-2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser @@ -19,6 +20,9 @@ #include "blargg_source.h" +using std::min; +using std::max; + // TODO: support Spc_Filter's bass Spc_Emu::Spc_Emu() @@ -244,7 +248,7 @@ static Music_Emu* new_spc_file() { return BLARGG_NEW Spc_File; } static gme_type_t_ const gme_spc_type_ = { "Super Nintendo", 1, &new_spc_emu, &new_spc_file, "SPC", 0 }; -gme_type_t const gme_spc_type = &gme_spc_type_; +extern gme_type_t const gme_spc_type = &gme_spc_type_; // Setup @@ -299,6 +303,12 @@ RETURN_ERR( apu.load_spc( file_data, file_size ) ); filter.set_gain( (int) (gain() * SPC_Filter::gain_unit) ); apu.clear_echo(); + track_info_t spc_info; + RETURN_ERR( track_info_( &spc_info, track ) ); + + // Set a default track length, need a non-zero fadeout + if ( autoload_playback_limit() && ( spc_info.length > 0 ) ) + set_fade ( spc_info.length, 50 ); return 0; } diff -Nru game-music-emu-0.6.2/gme/Spc_Emu.h game-music-emu-0.6.3/gme/Spc_Emu.h --- game-music-emu-0.6.2/gme/Spc_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Spc_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Super Nintendo SPC music file emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef SPC_EMU_H #define SPC_EMU_H diff -Nru game-music-emu-0.6.2/gme/Spc_Filter.cpp game-music-emu-0.6.3/gme/Spc_Filter.cpp --- game-music-emu-0.6.2/gme/Spc_Filter.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Spc_Filter.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Spc_Filter.h" diff -Nru game-music-emu-0.6.2/gme/Spc_Filter.h game-music-emu-0.6.3/gme/Spc_Filter.h --- game-music-emu-0.6.2/gme/Spc_Filter.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Spc_Filter.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Simple low-pass and high-pass filter to better match sound output of a SNES -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef SPC_FILTER_H #define SPC_FILTER_H diff -Nru game-music-emu-0.6.2/gme/Vgm_Emu.cpp game-music-emu-0.6.3/gme/Vgm_Emu.cpp --- game-music-emu-0.6.2/gme/Vgm_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Vgm_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,10 +1,11 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Vgm_Emu.h" #include "blargg_endian.h" #include #include +#include /* Copyright (C) 2003-2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser @@ -21,7 +22,10 @@ double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow double const rolloff = 0.990; -double const oversample_factor = 1.5; +double const oversample_factor = 1.0; + +using std::min; +using std::max; Vgm_Emu::Vgm_Emu() { @@ -203,10 +207,10 @@ static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; } static gme_type_t_ const gme_vgm_type_ = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 }; -gme_type_t const gme_vgm_type = &gme_vgm_type_; +extern gme_type_t const gme_vgm_type = &gme_vgm_type_; static gme_type_t_ const gme_vgz_type_ = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 }; -gme_type_t const gme_vgz_type = &gme_vgz_type_; +extern gme_type_t const gme_vgz_type = &gme_vgz_type_; // Setup @@ -233,16 +237,59 @@ return Classic_Emu::set_sample_rate_( sample_rate ); } +blargg_err_t Vgm_Emu::set_multi_channel ( bool is_enabled ) +{ + // we acutally should check here whether this is classic emu or not + // however set_multi_channel() is called before setup_fm() resulting in uninited is_classic_emu() + // hard code it to unsupported +#if 0 + if ( is_classic_emu() ) + { + RETURN_ERR( Music_Emu::set_multi_channel_( is_enabled ) ); + return 0; + } + else +#endif + { + (void) is_enabled; + return "multichannel rendering not supported for YM2*** FM sound chip emulators"; + } +} + void Vgm_Emu::update_eq( blip_eq_t const& eq ) { - psg.treble_eq( eq ); + psg[0].treble_eq( eq ); + if ( psg_dual ) + psg[1].treble_eq( eq ); dac_synth.treble_eq( eq ); } void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { - if ( i < psg.osc_count ) - psg.osc_output( i, c, l, r ); + if ( psg_dual ) + { + if ( psg_t6w28 ) + { + // TODO: Make proper output of each PSG chip: 0 - all right, 1 - all left + if ( i < psg[0].osc_count ) + psg[0].osc_output( i, c, r, r ); + if ( i < psg[1].osc_count ) + psg[1].osc_output( i, c, l, l ); + } + else + { + if ( i < psg[0].osc_count ) + psg[0].osc_output( i, c, l, r ); + if ( i < psg[1].osc_count ) + psg[1].osc_output( i, c, l, r ); + } + } + else + { + if ( i < psg[0].osc_count ) + psg[0].osc_output( i, c, l, r ); + } + } void Vgm_Emu::mute_voices_( int mask ) @@ -251,21 +298,26 @@ dac_synth.output( &blip_buf ); if ( uses_fm ) { - psg.output( (mask & 0x80) ? 0 : &blip_buf ); - if ( ym2612.enabled() ) + psg[0].output( (mask & 0x80) ? 0 : &blip_buf ); + if ( psg_dual ) + psg[1].output( (mask & 0x80) ? 0 : &blip_buf ); + if ( ym2612[0].enabled() ) { dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() ); - ym2612.mute_voices( mask ); + ym2612[0].mute_voices( mask ); + if ( ym2612[1].enabled() ) + ym2612[1].mute_voices( mask ); } - - if ( ym2413.enabled() ) + if ( ym2413[0].enabled() ) { int m = mask & 0x3F; if ( mask & 0x20 ) m |= 0x01E0; // channels 5-8 if ( mask & 0x40 ) m |= 0x3E00; - ym2413.mute_voices( m ); + ym2413[0].mute_voices( m ); + if ( ym2413[1].enabled() ) + ym2413[1].mute_voices( m ); } } } @@ -287,6 +339,9 @@ psg_rate = get_le32( h.psg_rate ); if ( !psg_rate ) psg_rate = 3579545; + psg_dual = ( psg_rate & 0x40000000 ) != 0; + psg_t6w28 = ( psg_rate & 0x80000000 ) != 0; + psg_rate &= 0x0FFFFFFF; blip_buf.clock_rate( psg_rate ); data = new_data; @@ -297,7 +352,7 @@ if ( get_le32( h.loop_offset ) ) loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)]; - set_voice_count( psg.osc_count ); + set_voice_count( psg[0].osc_count ); RETURN_ERR( setup_fm() ); @@ -314,7 +369,9 @@ blargg_err_t Vgm_Emu::setup_fm() { long ym2612_rate = get_le32( header().ym2612_rate ); + bool ym2612_dual = ( ym2612_rate & 0x40000000 ) != 0; long ym2413_rate = get_le32( header().ym2413_rate ); + bool ym2413_dual = ( ym2413_rate & 0x40000000 ) != 0; if ( ym2413_rate && get_le32( header().version ) < 0x110 ) update_fm_rates( &ym2413_rate, &ym2612_rate ); @@ -324,39 +381,59 @@ if ( ym2612_rate ) { + ym2612_rate &= ~0xC0000000; uses_fm = true; if ( disable_oversampling_ ) fm_rate = ym2612_rate / 144.0; Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() ); - RETURN_ERR( ym2612.set_rate( fm_rate, ym2612_rate ) ); - ym2612.enable( true ); + RETURN_ERR( ym2612[0].set_rate( fm_rate, ym2612_rate ) ); + ym2612[0].enable( true ); + if ( ym2612_dual ) + { + RETURN_ERR( ym2612[1].set_rate( fm_rate, ym2612_rate ) ); + ym2612[1].enable( true ); + } set_voice_count( 8 ); } if ( !uses_fm && ym2413_rate ) { + ym2413_rate &= ~0xC0000000; uses_fm = true; if ( disable_oversampling_ ) fm_rate = ym2413_rate / 72.0; Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() ); - int result = ym2413.set_rate( fm_rate, ym2413_rate ); + int result = ym2413[0].set_rate( fm_rate, ym2413_rate ); if ( result == 2 ) return "YM2413 FM sound isn't supported"; CHECK_ALLOC( !result ); - ym2413.enable( true ); + ym2413[0].enable( true ); + if ( ym2413_dual ) + { + ym2413[1].enable( true ); + int result = ym2413[1].set_rate( fm_rate, ym2413_rate ); + if ( result == 2 ) + return "YM2413 FM sound isn't supported"; + CHECK_ALLOC( !result ); + } set_voice_count( 8 ); } if ( uses_fm ) { RETURN_ERR( Dual_Resampler::reset( blip_buf.length() * blip_buf.sample_rate() / 1000 ) ); - psg.volume( 0.135 * fm_gain * gain() ); + psg[0].volume( 0.135 * fm_gain * gain() ); + if ( psg_dual ) + psg[1].volume( 0.135 * fm_gain * gain() ); } else { - ym2612.enable( false ); - ym2413.enable( false ); - psg.volume( gain() ); + ym2612[0].enable( false ); + ym2612[1].enable( false ); + ym2413[0].enable( false ); + ym2413[1].enable( false ); + psg[0].volume( gain() ); + psg[1].volume( gain() ); } return 0; @@ -367,7 +444,9 @@ blargg_err_t Vgm_Emu::start_track_( int track ) { RETURN_ERR( Classic_Emu::start_track_( track ) ); - psg.reset( get_le16( header().noise_feedback ), header().noise_width ); + psg[0].reset( get_le16( header().noise_feedback ), header().noise_width ); + if ( psg_dual ) + psg[1].reset( get_le16( header().noise_feedback ), header().noise_width ); dac_disabled = -1; pos = data + header_size; @@ -385,11 +464,17 @@ if ( uses_fm ) { - if ( ym2413.enabled() ) - ym2413.reset(); + if ( ym2413[0].enabled() ) + ym2413[0].reset(); + + if ( ym2413[1].enabled() ) + ym2413[1].reset(); - if ( ym2612.enabled() ) - ym2612.reset(); + if ( ym2612[0].enabled() ) + ym2612[0].reset(); + + if ( ym2612[1].enabled() ) + ym2612[1].reset(); fm_time_offset = 0; blip_buf.clear(); @@ -401,7 +486,9 @@ blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec ) { time_io = run_commands( msec * vgm_rate / 1000 ); - psg.end_frame( time_io ); + psg[0].end_frame( time_io ); + if ( psg_dual ) + psg[1].end_frame( time_io ); return 0; } diff -Nru game-music-emu-0.6.2/gme/Vgm_Emu.h game-music-emu-0.6.3/gme/Vgm_Emu.h --- game-music-emu-0.6.2/gme/Vgm_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Vgm_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef VGM_EMU_H #define VGM_EMU_H @@ -17,6 +17,8 @@ // TODO: move into Music_Emu and rename to something like supports_custom_buffer() bool is_classic_emu() const { return !uses_fm; } + blargg_err_t set_multi_channel ( bool is_enabled ) override; + // Disable running FM chips at higher than normal rate. Will result in slightly // more aliasing of high notes. void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; } diff -Nru game-music-emu-0.6.2/gme/Vgm_Emu_Impl.cpp game-music-emu-0.6.3/gme/Vgm_Emu_Impl.cpp --- game-music-emu-0.6.2/gme/Vgm_Emu_Impl.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Vgm_Emu_Impl.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Vgm_Emu.h" @@ -36,6 +36,12 @@ cmd_pcm_delay = 0x80, cmd_pcm_seek = 0xE0, + cmd_gg_stereo_2 = 0x3F, + cmd_psg_2 = 0x30, + cmd_ym2413_2 = 0xA1, + cmd_ym2612_2_port0 = 0xA2, + cmd_ym2612_2_port1 = 0xA3, + pcm_block_type = 0x00, ym2612_dac_port = 0x2A }; @@ -142,13 +148,21 @@ break; case cmd_gg_stereo: - psg.write_ggstereo( to_blip_time( vgm_time ), *pos++ ); + psg[0].write_ggstereo( to_blip_time( vgm_time ), *pos++ ); break; case cmd_psg: - psg.write_data( to_blip_time( vgm_time ), *pos++ ); + psg[0].write_data( to_blip_time( vgm_time ), *pos++ ); break; + case cmd_gg_stereo_2: + psg[1].write_ggstereo( to_blip_time( vgm_time ), *pos++ ); + break; + + case cmd_psg_2: + psg[1].write_data( to_blip_time( vgm_time ), *pos++ ); + break; + case cmd_delay: vgm_time += pos [1] * 0x100L + pos [0]; pos += 2; @@ -159,8 +173,14 @@ break; case cmd_ym2413: - if ( ym2413.run_until( to_fm_time( vgm_time ) ) ) - ym2413.write( pos [0], pos [1] ); + if ( ym2413[0].run_until( to_fm_time( vgm_time ) ) ) + ym2413[0].write( pos [0], pos [1] ); + pos += 2; + break; + + case cmd_ym2413_2: + if ( ym2413[1].run_until( to_fm_time( vgm_time ) ) ) + ym2413[1].write( pos [0], pos [1] ); pos += 2; break; @@ -169,24 +189,47 @@ { write_pcm( vgm_time, pos [1] ); } - else if ( ym2612.run_until( to_fm_time( vgm_time ) ) ) + else if ( ym2612[0].run_until( to_fm_time( vgm_time ) ) ) { if ( pos [0] == 0x2B ) { dac_disabled = (pos [1] >> 7 & 1) - 1; dac_amp |= dac_disabled; } - ym2612.write0( pos [0], pos [1] ); + ym2612[0].write0( pos [0], pos [1] ); } pos += 2; break; case cmd_ym2612_port1: - if ( ym2612.run_until( to_fm_time( vgm_time ) ) ) - ym2612.write1( pos [0], pos [1] ); + if ( ym2612[0].run_until( to_fm_time( vgm_time ) ) ) + ym2612[0].write1( pos [0], pos [1] ); + pos += 2; + break; + + case cmd_ym2612_2_port0: + if ( pos [0] == ym2612_dac_port ) + { + write_pcm( vgm_time, pos [1] ); + } + else if ( ym2612[1].run_until( to_fm_time( vgm_time ) ) ) + { + if ( pos [0] == 0x2B ) + { + dac_disabled = (pos [1] >> 7 & 1) - 1; + dac_amp |= dac_disabled; + } + ym2612[1].write0( pos [0], pos [1] ); + } + pos += 2; + break; + + case cmd_ym2612_2_port1: + if ( ym2612[1].run_until( to_fm_time( vgm_time ) ) ) + ym2612[1].write1( pos [0], pos [1] ); pos += 2; break; - + case cmd_data_block: { check( *pos == cmd_end ); int type = pos [1]; @@ -246,24 +289,39 @@ vgm_time++; //debug_printf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs ); - if ( ym2612.enabled() ) + if ( ym2612[0].enabled() ) { - ym2612.begin_frame( buf ); + ym2612[0].begin_frame( buf ); + if ( ym2612[1].enabled() ) + ym2612[1].begin_frame( buf ); memset( buf, 0, pairs * stereo * sizeof *buf ); } - else if ( ym2413.enabled() ) + else if ( ym2413[0].enabled() ) { - ym2413.begin_frame( buf ); + ym2413[0].begin_frame( buf ); + if ( ym2413[1].enabled() ) + ym2413[1].begin_frame( buf ); + memset( buf, 0, pairs * stereo * sizeof *buf ); } run_commands( vgm_time ); - ym2612.run_until( pairs ); - ym2413.run_until( pairs ); + + if ( ym2612[0].enabled() ) + ym2612[0].run_until( pairs ); + if ( ym2612[1].enabled() ) + ym2612[1].run_until( pairs ); + + if ( ym2413[0].enabled() ) + ym2413[0].run_until( pairs ); + if ( ym2413[1].enabled() ) + ym2413[1].run_until( pairs ); fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) - ((long) pairs << fm_time_bits); - psg.end_frame( blip_time ); + psg[0].end_frame( blip_time ); + if ( psg_dual ) + psg[1].end_frame( blip_time ); return pairs * stereo; } diff -Nru game-music-emu-0.6.2/gme/Vgm_Emu_Impl.h game-music-emu-0.6.3/gme/Vgm_Emu_Impl.h --- game-music-emu-0.6.2/gme/Vgm_Emu_Impl.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Vgm_Emu_Impl.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Low-level parts of Vgm_Emu -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef VGM_EMU_IMPL_H #define VGM_EMU_IMPL_H @@ -58,11 +58,13 @@ int dac_disabled; // -1 if disabled void write_pcm( vgm_time_t, int amp ); - Ym_Emu ym2612; - Ym_Emu ym2413; + Ym_Emu ym2612[2]; + Ym_Emu ym2413[2]; Blip_Buffer blip_buf; - Sms_Apu psg; + Sms_Apu psg[2]; + bool psg_dual; + bool psg_t6w28; Blip_Synth dac_synth; friend class Vgm_Emu; diff -Nru game-music-emu-0.6.2/gme/Ym2413_Emu.cpp game-music-emu-0.6.3/gme/Ym2413_Emu.cpp --- game-music-emu-0.6.2/gme/Ym2413_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ym2413_Emu.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,7 +1,7 @@ // Use in place of Ym2413_Emu.cpp and ym2413.c to disable support for this chip -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Ym2413_Emu.h" diff -Nru game-music-emu-0.6.2/gme/Ym2413_Emu.h game-music-emu-0.6.3/gme/Ym2413_Emu.h --- game-music-emu-0.6.2/gme/Ym2413_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ym2413_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // YM2413 FM sound chip emulator interface -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef YM2413_EMU_H #define YM2413_EMU_H diff -Nru game-music-emu-0.6.2/gme/Ym2612_Emu.cpp game-music-emu-0.6.3/gme/Ym2612_Emu.cpp --- game-music-emu-0.6.2/gme/Ym2612_Emu.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ym2612_Emu.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1319 +0,0 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ - -// Based on Gens 2.10 ym2612.c - -#include "Ym2612_Emu.h" - -#include -#include -#include -#include -#include -#include - -/* Copyright (C) 2002 Stéphane Dallongeville (gens AT consolemul.com) */ -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -// This is mostly the original source in its C style and all. -// -// Somewhat optimized and simplified. Uses a template to generate the many -// variants of Update_Chan. Rewrote header file. In need of full rewrite by -// someone more familiar with FM sound and the YM2612. Has some inaccuracies -// compared to the Sega Genesis sound, particularly being mixed at such a -// high sample accuracy (the Genesis sounds like it has only 8 bit samples). -// - Shay - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -const int output_bits = 14; - -struct slot_t -{ - const int *DT; // parametre detune - int MUL; // parametre "multiple de frequence" - int TL; // Total Level = volume lorsque l'enveloppe est au plus haut - int TLL; // Total Level ajusted - int SLL; // Sustin Level (ajusted) = volume où l'enveloppe termine sa premiere phase de regression - int KSR_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe - int KSR; // Key Scale Rate = cette valeur est calculee par rapport à la frequence actuelle, elle va influer - // sur les differents parametres de l'enveloppe comme l'attaque, le decay ... comme dans la realite ! - int SEG; // Type enveloppe SSG - int env_xor; - int env_max; - - const int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR]) - const int *DR; // Decay Rate (table pointeur) = Taux pour la regression (DR[KSR]) - const int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR]) - const int *RR; // Release Rate (table pointeur) = Taux pour le rel'chement (RR[KSR]) - int Fcnt; // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN[Finc >> 16]) - int Finc; // frequency step = pas d'incrementation du compteur-frequence - // plus le pas est grand, plus la frequence est aïgu (ou haute) - int Ecurp; // Envelope current phase = cette variable permet de savoir dans quelle phase - // de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ... - // en fonction de la valeur de cette variable, on va appeler une fonction permettant - // de mettre à jour l'enveloppe courante. - int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe - int Einc; // Envelope step courant - int Ecmp; // Envelope counter limite pour la prochaine phase - int EincA; // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque - // cette valeur est egal à AR[KSR] - int EincD; // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression - // cette valeur est egal à DR[KSR] - int EincS; // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue - // cette valeur est egal à SR[KSR] - int EincR; // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement - // cette valeur est egal à RR[KSR] - int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entree - // d'un autre ou carrement à la sortie de la voie - int INd; // input data of the slot = donnees en entree du slot - int ChgEnM; // Change envelop mask. - int AMS; // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO - int AMSon; // AMS enable flag = drapeau d'activation de l'AMS -}; - -struct channel_t -{ - int S0_OUT[4]; // anciennes sorties slot 0 (pour le feed back) - int LEFT; // LEFT enable flag - int RIGHT; // RIGHT enable flag - int ALGO; // Algorythm = determine les connections entre les operateurs - int FB; // shift count of self feed back = degre de "Feed-Back" du SLOT 1 (il est son unique entree) - int FMS; // Frequency Modulation Sensitivity of channel = degre de modulation de la frequence sur la voie par le LFO - int AMS; // Amplitude Modulation Sensitivity of channel = degre de modulation de l'amplitude sur la voie par le LFO - int FNUM[4]; // hauteur frequence de la voie (+ 3 pour le mode special) - int FOCT[4]; // octave de la voie (+ 3 pour le mode special) - int KC[4]; // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S) - slot_t SLOT[4]; // four slot.operators = les 4 slots de la voie - int FFlag; // Frequency step recalculation flag -}; - -struct state_t -{ - int TimerBase; // TimerBase calculation - int Status; // YM2612 Status (timer overflow) - int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter - int TimerAL; - int TimerAcnt; // timerA counter = valeur courante du Timer A - int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter - int TimerBL; - int TimerBcnt; // timerB counter = valeur courante du Timer B - int Mode; // Mode actuel des voie 3 et 6 (normal / special) - int DAC; // DAC enabled flag - channel_t CHANNEL[Ym2612_Emu::channel_count]; // Les 6 voies du YM2612 - int REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif - // cela nous rend le debuggage plus facile -}; - -#ifndef PI -#define PI 3.14159265358979323846 -#endif - -#define ATTACK 0 -#define DECAY 1 -#define SUBSTAIN 2 -#define RELEASE 3 - -// SIN_LBITS <= 16 -// LFO_HBITS <= 16 -// (SIN_LBITS + SIN_HBITS) <= 26 -// (ENV_LBITS + ENV_HBITS) <= 28 -// (LFO_LBITS + LFO_HBITS) <= 28 - -#define SIN_HBITS 12 // Sinus phase counter int part -#define SIN_LBITS (26 - SIN_HBITS) // Sinus phase counter float part (best setting) - -#if (SIN_LBITS > 16) -#define SIN_LBITS 16 // Can't be greater than 16 bits -#endif - -#define ENV_HBITS 12 // Env phase counter int part -#define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting) - -#define LFO_HBITS 10 // LFO phase counter int part -#define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting) - -#define SIN_LENGHT (1 << SIN_HBITS) -#define ENV_LENGHT (1 << ENV_HBITS) -#define LFO_LENGHT (1 << LFO_HBITS) - -#define TL_LENGHT (ENV_LENGHT * 3) // Env + TL scaling + LFO - -#define SIN_MASK (SIN_LENGHT - 1) -#define ENV_MASK (ENV_LENGHT - 1) -#define LFO_MASK (LFO_LENGHT - 1) - -#define ENV_STEP (96.0 / ENV_LENGHT) // ENV_MAX = 96 dB - -#define ENV_ATTACK ((ENV_LENGHT * 0) << ENV_LBITS) -#define ENV_DECAY ((ENV_LENGHT * 1) << ENV_LBITS) -#define ENV_END ((ENV_LENGHT * 2) << ENV_LBITS) - -#define MAX_OUT_BITS (SIN_HBITS + SIN_LBITS + 2) // Modulation = -4 <--> +4 -#define MAX_OUT ((1 << MAX_OUT_BITS) - 1) - -#define PG_CUT_OFF ((int) (78.0 / ENV_STEP)) -#define ENV_CUT_OFF ((int) (68.0 / ENV_STEP)) - -#define AR_RATE 399128 -#define DR_RATE 5514396 - -//#define AR_RATE 426136 -//#define DR_RATE (AR_RATE * 12) - -#define LFO_FMS_LBITS 9 // FIXED (LFO_FMS_BASE gives somethink as 1) -#define LFO_FMS_BASE ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS))) - -#define S0 0 // Stupid typo of the YM2612 -#define S1 2 -#define S2 1 -#define S3 3 - -inline void set_seg( slot_t& s, int seg ) -{ - s.env_xor = 0; - s.env_max = INT_MAX; - s.SEG = seg; - if ( seg & 4 ) - { - s.env_xor = ENV_MASK; - s.env_max = ENV_MASK; - } -} - -struct tables_t -{ - short SIN_TAB [SIN_LENGHT]; // SINUS TABLE (offset into TL TABLE) - int LFOcnt; // LFO counter = compteur-frequence pour le LFO - int LFOinc; // LFO step counter = pas d'incrementation du compteur-frequence du LFO - // plus le pas est grand, plus la frequence est grande - unsigned int AR_TAB [128]; // Attack rate table - unsigned int DR_TAB [96]; // Decay rate table - unsigned int DT_TAB [8] [32]; // Detune table - unsigned int SL_TAB [16]; // Substain level table - unsigned int NULL_RATE [32]; // Table for NULL rate - int LFO_INC_TAB [8]; // LFO step table - - short ENV_TAB [2 * ENV_LENGHT + 8]; // ENV CURVE TABLE (attack & decay) - - short LFO_ENV_TAB [LFO_LENGHT]; // LFO AMS TABLE (adjusted for 11.8 dB) - short LFO_FREQ_TAB [LFO_LENGHT]; // LFO FMS TABLE - int TL_TAB [TL_LENGHT * 2]; // TOTAL LEVEL TABLE (positif and minus) - unsigned int DECAY_TO_ATTACK [ENV_LENGHT]; // Conversion from decay to attack phase - unsigned int FINC_TAB [2048]; // Frequency step table -}; - -static const unsigned char DT_DEF_TAB [4 * 32] = -{ -// FD = 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - -// FD = 1 - 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, - 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, - -// FD = 2 - 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, - 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16, - -// FD = 3 - 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, - 8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22 -}; - -static const unsigned char FKEY_TAB [16] = -{ - 0, 0, 0, 0, - 0, 0, 0, 1, - 2, 3, 3, 3, - 3, 3, 3, 3 -}; - -static const unsigned char LFO_AMS_TAB [4] = -{ - 31, 4, 1, 0 -}; - -static const unsigned char LFO_FMS_TAB [8] = -{ - LFO_FMS_BASE * 0, LFO_FMS_BASE * 1, - LFO_FMS_BASE * 2, LFO_FMS_BASE * 3, - LFO_FMS_BASE * 4, LFO_FMS_BASE * 6, - LFO_FMS_BASE * 12, LFO_FMS_BASE * 24 -}; - -inline void YM2612_Special_Update() { } - -struct Ym2612_Impl -{ - enum { channel_count = Ym2612_Emu::channel_count }; - - state_t YM2612; - int mute_mask; - tables_t g; - - void KEY_ON( channel_t&, int ); - void KEY_OFF( channel_t&, int ); - int SLOT_SET( int, int ); - int CHANNEL_SET( int, int ); - int YM_SET( int, int ); - - void set_rate( double sample_rate, double clock_factor ); - void reset(); - void write0( int addr, int data ); - void write1( int addr, int data ); - void run_timer( int ); - void run( int pair_count, Ym2612_Emu::sample_t* ); -}; - -void Ym2612_Impl::KEY_ON( channel_t& ch, int nsl) -{ - slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot - - if (SL->Ecurp == RELEASE) // la touche est-elle rel'chee ? - { - SL->Fcnt = 0; - - // Fix Ecco 2 splash sound - - SL->Ecnt = (g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM; - SL->ChgEnM = ~0; - -// SL->Ecnt = g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK; -// SL->Ecnt = 0; - - SL->Einc = SL->EincA; - SL->Ecmp = ENV_DECAY; - SL->Ecurp = ATTACK; - } -} - - -void Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl) -{ - slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot - - if (SL->Ecurp != RELEASE) // la touche est-elle appuyee ? - { - if (SL->Ecnt < ENV_DECAY) // attack phase ? - { - SL->Ecnt = (g.ENV_TAB [SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY; - } - - SL->Einc = SL->EincR; - SL->Ecmp = ENV_END; - SL->Ecurp = RELEASE; - } -} - - -int Ym2612_Impl::SLOT_SET( int Adr, int data ) -{ - int nch = Adr & 3; - if ( nch == 3 ) - return 1; - - channel_t& ch = YM2612.CHANNEL [nch + (Adr & 0x100 ? 3 : 0)]; - slot_t& sl = ch.SLOT [(Adr >> 2) & 3]; - - switch ( Adr & 0xF0 ) - { - case 0x30: - if ( (sl.MUL = (data & 0x0F)) != 0 ) sl.MUL <<= 1; - else sl.MUL = 1; - - sl.DT = (int*) g.DT_TAB [(data >> 4) & 7]; - - ch.SLOT [0].Finc = -1; - - break; - - case 0x40: - sl.TL = data & 0x7F; - - // SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound... - YM2612_Special_Update(); - -#if ((ENV_HBITS - 7) < 0) - sl.TLL = sl.TL >> (7 - ENV_HBITS); -#else - sl.TLL = sl.TL << (ENV_HBITS - 7); -#endif - - break; - - case 0x50: - sl.KSR_S = 3 - (data >> 6); - - ch.SLOT [0].Finc = -1; - - if (data &= 0x1F) sl.AR = (int*) &g.AR_TAB [data << 1]; - else sl.AR = (int*) &g.NULL_RATE [0]; - - sl.EincA = sl.AR [sl.KSR]; - if (sl.Ecurp == ATTACK) sl.Einc = sl.EincA; - break; - - case 0x60: - if ( (sl.AMSon = (data & 0x80)) != 0 ) sl.AMS = ch.AMS; - else sl.AMS = 31; - - if (data &= 0x1F) sl.DR = (int*) &g.DR_TAB [data << 1]; - else sl.DR = (int*) &g.NULL_RATE [0]; - - sl.EincD = sl.DR [sl.KSR]; - if (sl.Ecurp == DECAY) sl.Einc = sl.EincD; - break; - - case 0x70: - if (data &= 0x1F) sl.SR = (int*) &g.DR_TAB [data << 1]; - else sl.SR = (int*) &g.NULL_RATE [0]; - - sl.EincS = sl.SR [sl.KSR]; - if ((sl.Ecurp == SUBSTAIN) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincS; - break; - - case 0x80: - sl.SLL = g.SL_TAB [data >> 4]; - - sl.RR = (int*) &g.DR_TAB [((data & 0xF) << 2) + 2]; - - sl.EincR = sl.RR [sl.KSR]; - if ((sl.Ecurp == RELEASE) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincR; - break; - - case 0x90: - // SSG-EG envelope shapes : - /* - E At Al H - - 1 0 0 0 \\\\ - 1 0 0 1 \___ - 1 0 1 0 \/\/ - 1 0 1 1 \ - 1 1 0 0 //// - 1 1 0 1 / - 1 1 1 0 /\/\ - 1 1 1 1 /___ - - E = SSG-EG enable - At = Start negate - Al = Altern - H = Hold */ - - set_seg( sl, (data & 8) ? (data & 0x0F) : 0 ); - break; - } - - return 0; -} - - -int Ym2612_Impl::CHANNEL_SET( int Adr, int data ) -{ - int num = Adr & 3; - if ( num == 3 ) - return 1; - - channel_t& ch = YM2612.CHANNEL [num + (Adr & 0x100 ? 3 : 0)]; - - switch ( Adr & 0xFC ) - { - case 0xA0: - YM2612_Special_Update(); - - ch.FNUM [0] = (ch.FNUM [0] & 0x700) + data; - ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; - - ch.SLOT [0].Finc = -1; - break; - - case 0xA4: - YM2612_Special_Update(); - - ch.FNUM [0] = (ch.FNUM [0] & 0x0FF) + ((data & 0x07) << 8); - ch.FOCT [0] = (data & 0x38) >> 3; - ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; - - ch.SLOT [0].Finc = -1; - break; - - case 0xA8: - if ( Adr < 0x100 ) - { - num++; - - YM2612_Special_Update(); - - YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x700) + data; - YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | - FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; - - YM2612.CHANNEL [2].SLOT [0].Finc = -1; - } - break; - - case 0xAC: - if ( Adr < 0x100 ) - { - num++; - - YM2612_Special_Update(); - - YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x0FF) + ((data & 0x07) << 8); - YM2612.CHANNEL [2].FOCT [num] = (data & 0x38) >> 3; - YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | - FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; - - YM2612.CHANNEL [2].SLOT [0].Finc = -1; - } - break; - - case 0xB0: - if ( ch.ALGO != (data & 7) ) - { - // Fix VectorMan 2 heli sound (level 1) - YM2612_Special_Update(); - - ch.ALGO = data & 7; - - ch.SLOT [0].ChgEnM = 0; - ch.SLOT [1].ChgEnM = 0; - ch.SLOT [2].ChgEnM = 0; - ch.SLOT [3].ChgEnM = 0; - } - - ch.FB = 9 - ((data >> 3) & 7); // Real thing ? - -// if (ch.FB = ((data >> 3) & 7)) ch.FB = 9 - ch.FB; // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound... -// else ch.FB = 31; - break; - - case 0xB4: { - YM2612_Special_Update(); - - ch.LEFT = 0 - ((data >> 7) & 1); - ch.RIGHT = 0 - ((data >> 6) & 1); - - ch.AMS = LFO_AMS_TAB [(data >> 4) & 3]; - ch.FMS = LFO_FMS_TAB [data & 7]; - - for ( int i = 0; i < 4; i++ ) - { - slot_t& sl = ch.SLOT [i]; - sl.AMS = (sl.AMSon ? ch.AMS : 31); - } - break; - } - } - - return 0; -} - - -int Ym2612_Impl::YM_SET(int Adr, int data) -{ - switch ( Adr ) - { - case 0x22: - if (data & 8) // LFO enable - { - // Cool Spot music 1, LFO modified severals time which - // distord the sound, have to check that on a real genesis... - - g.LFOinc = g.LFO_INC_TAB [data & 7]; - } - else - { - g.LFOinc = g.LFOcnt = 0; - } - break; - - case 0x24: - YM2612.TimerA = (YM2612.TimerA & 0x003) | (((int) data) << 2); - - if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) - { - YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; - } - break; - - case 0x25: - YM2612.TimerA = (YM2612.TimerA & 0x3FC) | (data & 3); - - if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) - { - YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; - } - break; - - case 0x26: - YM2612.TimerB = data; - - if (YM2612.TimerBL != (256 - YM2612.TimerB) << (4 + 12)) - { - YM2612.TimerBcnt = YM2612.TimerBL = (256 - YM2612.TimerB) << (4 + 12); - } - break; - - case 0x27: - // Parametre divers - // b7 = CSM MODE - // b6 = 3 slot mode - // b5 = reset b - // b4 = reset a - // b3 = timer enable b - // b2 = timer enable a - // b1 = load b - // b0 = load a - - if ((data ^ YM2612.Mode) & 0x40) - { - // We changed the channel 2 mode, so recalculate phase step - // This fix the punch sound in Street of Rage 2 - - YM2612_Special_Update(); - - YM2612.CHANNEL [2].SLOT [0].Finc = -1; // recalculate phase step - } - -// if ((data & 2) && (YM2612.Status & 2)) YM2612.TimerBcnt = YM2612.TimerBL; -// if ((data & 1) && (YM2612.Status & 1)) YM2612.TimerAcnt = YM2612.TimerAL; - -// YM2612.Status &= (~data >> 4); // Reset du Status au cas ou c'est demande - YM2612.Status &= (~data >> 4) & (data >> 2); // Reset Status - - YM2612.Mode = data; - break; - - case 0x28: { - int nch = data & 3; - if ( nch == 3 ) - return 1; - if ( data & 4 ) - nch += 3; - channel_t& ch = YM2612.CHANNEL [nch]; - - YM2612_Special_Update(); - - if (data & 0x10) KEY_ON(ch, S0); // On appuie sur la touche pour le slot 1 - else KEY_OFF(ch, S0); // On rel'che la touche pour le slot 1 - if (data & 0x20) KEY_ON(ch, S1); // On appuie sur la touche pour le slot 3 - else KEY_OFF(ch, S1); // On rel'che la touche pour le slot 3 - if (data & 0x40) KEY_ON(ch, S2); // On appuie sur la touche pour le slot 2 - else KEY_OFF(ch, S2); // On rel'che la touche pour le slot 2 - if (data & 0x80) KEY_ON(ch, S3); // On appuie sur la touche pour le slot 4 - else KEY_OFF(ch, S3); // On rel'che la touche pour le slot 4 - break; - } - - case 0x2B: - if (YM2612.DAC ^ (data & 0x80)) YM2612_Special_Update(); - - YM2612.DAC = data & 0x80; // activation/desactivation du DAC - break; - } - - return 0; -} - -void Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) -{ - assert( sample_rate ); - assert( clock_rate > sample_rate ); - - int i; - - // 144 = 12 * (prescale * 2) = 12 * 6 * 2 - // prescale set to 6 by default - - double Frequence = clock_rate / sample_rate / 144.0; - if ( fabs( Frequence - 1.0 ) < 0.0000001 ) - Frequence = 1.0; - YM2612.TimerBase = int (Frequence * 4096.0); - - // Tableau TL : - // [0 - 4095] = +output [4095 - ...] = +output overflow (fill with 0) - // [12288 - 16383] = -output [16384 - ...] = -output overflow (fill with 0) - - for(i = 0; i < TL_LENGHT; i++) - { - if (i >= PG_CUT_OFF) // YM2612 cut off sound after 78 dB (14 bits output ?) - { - g.TL_TAB [TL_LENGHT + i] = g.TL_TAB [i] = 0; - } - else - { - double x = MAX_OUT; // Max output - x /= pow( 10.0, (ENV_STEP * i) / 20.0 ); // Decibel -> Voltage - - g.TL_TAB [i] = (int) x; - g.TL_TAB [TL_LENGHT + i] = -g.TL_TAB [i]; - } - } - - // Tableau SIN : - // g.SIN_TAB [x] [y] = sin(x) * y; - // x = phase and y = volume - - g.SIN_TAB [0] = g.SIN_TAB [SIN_LENGHT / 2] = PG_CUT_OFF; - - for(i = 1; i <= SIN_LENGHT / 4; i++) - { - double x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT)); // Sinus - x = 20 * log10(1 / x); // convert to dB - - int j = (int) (x / ENV_STEP); // Get TL range - - if (j > PG_CUT_OFF) j = (int) PG_CUT_OFF; - - g.SIN_TAB [i] = g.SIN_TAB [(SIN_LENGHT / 2) - i] = j; - g.SIN_TAB [(SIN_LENGHT / 2) + i] = g.SIN_TAB [SIN_LENGHT - i] = TL_LENGHT + j; - } - - // Tableau LFO (LFO wav) : - - for(i = 0; i < LFO_LENGHT; i++) - { - double x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus - x += 1.0; - x /= 2.0; // positive only - x *= 11.8 / ENV_STEP; // ajusted to MAX enveloppe modulation - - g.LFO_ENV_TAB [i] = (int) x; - - x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus - x *= (double) ((1 << (LFO_HBITS - 1)) - 1); - - g.LFO_FREQ_TAB [i] = (int) x; - - } - - // Tableau Enveloppe : - // g.ENV_TAB [0] -> g.ENV_TAB [ENV_LENGHT - 1] = attack curve - // g.ENV_TAB [ENV_LENGHT] -> g.ENV_TAB [2 * ENV_LENGHT - 1] = decay curve - - for(i = 0; i < ENV_LENGHT; i++) - { - // Attack curve (x^8 - music level 2 Vectorman 2) - double x = pow(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8); - x *= ENV_LENGHT; - - g.ENV_TAB [i] = (int) x; - - // Decay curve (just linear) - x = pow(((double) (i) / (double) (ENV_LENGHT)), 1); - x *= ENV_LENGHT; - - g.ENV_TAB [ENV_LENGHT + i] = (int) x; - } - for ( i = 0; i < 8; i++ ) - g.ENV_TAB [i + ENV_LENGHT * 2] = 0; - - g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1; // for the stopped state - - // Tableau pour la conversion Attack -> Decay and Decay -> Attack - - int j = ENV_LENGHT - 1; - for ( i = 0; i < ENV_LENGHT; i++ ) - { - while ( j && g.ENV_TAB [j] < i ) - j--; - - g.DECAY_TO_ATTACK [i] = j << ENV_LBITS; - } - - // Tableau pour le Substain Level - - for(i = 0; i < 15; i++) - { - double x = i * 3; // 3 and not 6 (Mickey Mania first music for test) - x /= ENV_STEP; - - g.SL_TAB [i] = ((int) x << ENV_LBITS) + ENV_DECAY; - } - - g.SL_TAB [15] = ((ENV_LENGHT - 1) << ENV_LBITS) + ENV_DECAY; // special case : volume off - - // Tableau Frequency Step - - for(i = 0; i < 2048; i++) - { - double x = (double) (i) * Frequence; - -#if ((SIN_LBITS + SIN_HBITS - (21 - 7)) < 0) - x /= (double) (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS)); -#else - x *= (double) (1 << (SIN_LBITS + SIN_HBITS - (21 - 7))); -#endif - - x /= 2.0; // because MUL = value * 2 - - g.FINC_TAB [i] = (unsigned int) x; - } - - // Tableaux Attack & Decay Rate - - for(i = 0; i < 4; i++) - { - g.AR_TAB [i] = 0; - g.DR_TAB [i] = 0; - } - - for(i = 0; i < 60; i++) - { - double x = Frequence; - - x *= 1.0 + ((i & 3) * 0.25); // bits 0-1 : x1.00, x1.25, x1.50, x1.75 - x *= (double) (1 << ((i >> 2))); // bits 2-5 : shift bits (x2^0 - x2^15) - x *= (double) (ENV_LENGHT << ENV_LBITS); // on ajuste pour le tableau g.ENV_TAB - - g.AR_TAB [i + 4] = (unsigned int) (x / AR_RATE); - g.DR_TAB [i + 4] = (unsigned int) (x / DR_RATE); - } - - for(i = 64; i < 96; i++) - { - g.AR_TAB [i] = g.AR_TAB [63]; - g.DR_TAB [i] = g.DR_TAB [63]; - - g.NULL_RATE [i - 64] = 0; - } - - for ( i = 96; i < 128; i++ ) - g.AR_TAB [i] = 0; - - // Tableau Detune - - for(i = 0; i < 4; i++) - { - for (int j = 0; j < 32; j++) - { -#if ((SIN_LBITS + SIN_HBITS - 21) < 0) - double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS)); -#else - double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21)); -#endif - - g.DT_TAB [i + 0] [j] = (int) y; - g.DT_TAB [i + 4] [j] = (int) -y; - } - } - - // Tableau LFO - g.LFO_INC_TAB [0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - - reset(); -} - -const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate ) -{ - if ( !impl ) - { - impl = (Ym2612_Impl*) malloc( sizeof *impl ); - if ( !impl ) - return "Out of memory"; - impl->mute_mask = 0; - } - memset( &impl->YM2612, 0, sizeof impl->YM2612 ); - - impl->set_rate( sample_rate, clock_rate ); - - return 0; -} - -Ym2612_Emu::~Ym2612_Emu() -{ - free( impl ); -} - -inline void Ym2612_Impl::write0( int opn_addr, int data ) -{ - assert( (unsigned) data <= 0xFF ); - - if ( opn_addr < 0x30 ) - { - YM2612.REG [0] [opn_addr] = data; - YM_SET( opn_addr, data ); - } - else if ( YM2612.REG [0] [opn_addr] != data ) - { - YM2612.REG [0] [opn_addr] = data; - - if ( opn_addr < 0xA0 ) - SLOT_SET( opn_addr, data ); - else - CHANNEL_SET( opn_addr, data ); - } -} - -inline void Ym2612_Impl::write1( int opn_addr, int data ) -{ - assert( (unsigned) data <= 0xFF ); - - if ( opn_addr >= 0x30 && YM2612.REG [1] [opn_addr] != data ) - { - YM2612.REG [1] [opn_addr] = data; - - if ( opn_addr < 0xA0 ) - SLOT_SET( opn_addr + 0x100, data ); - else - CHANNEL_SET( opn_addr + 0x100, data ); - } -} - -void Ym2612_Emu::reset() -{ - impl->reset(); -} - -void Ym2612_Impl::reset() -{ - g.LFOcnt = 0; - YM2612.TimerA = 0; - YM2612.TimerAL = 0; - YM2612.TimerAcnt = 0; - YM2612.TimerB = 0; - YM2612.TimerBL = 0; - YM2612.TimerBcnt = 0; - YM2612.DAC = 0; - - YM2612.Status = 0; - - int i; - for ( i = 0; i < channel_count; i++ ) - { - channel_t& ch = YM2612.CHANNEL [i]; - - ch.LEFT = ~0; - ch.RIGHT = ~0; - ch.ALGO = 0; - ch.FB = 31; - ch.FMS = 0; - ch.AMS = 0; - - for ( int j = 0 ;j < 4 ; j++ ) - { - ch.S0_OUT [j] = 0; - ch.FNUM [j] = 0; - ch.FOCT [j] = 0; - ch.KC [j] = 0; - - ch.SLOT [j].Fcnt = 0; - ch.SLOT [j].Finc = 0; - ch.SLOT [j].Ecnt = ENV_END; // Put it at the end of Decay phase... - ch.SLOT [j].Einc = 0; - ch.SLOT [j].Ecmp = 0; - ch.SLOT [j].Ecurp = RELEASE; - - ch.SLOT [j].ChgEnM = 0; - } - } - - for ( i = 0; i < 0x100; i++ ) - { - YM2612.REG [0] [i] = -1; - YM2612.REG [1] [i] = -1; - } - - for ( i = 0xB6; i >= 0xB4; i-- ) - { - write0( i, 0xC0 ); - write1( i, 0xC0 ); - } - - for ( i = 0xB2; i >= 0x22; i-- ) - { - write0( i, 0 ); - write1( i, 0 ); - } - - write0( 0x2A, 0x80 ); -} - -void Ym2612_Emu::write0( int addr, int data ) -{ - impl->write0( addr, data ); -} - -void Ym2612_Emu::write1( int addr, int data ) -{ - impl->write1( addr, data ); -} - -void Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; } - -static void update_envelope_( slot_t* sl ) -{ - switch ( sl->Ecurp ) - { - case 0: - // Env_Attack_Next - - // Verified with Gynoug even in HQ (explode SFX) - sl->Ecnt = ENV_DECAY; - - sl->Einc = sl->EincD; - sl->Ecmp = sl->SLL; - sl->Ecurp = DECAY; - break; - - case 1: - // Env_Decay_Next - - // Verified with Gynoug even in HQ (explode SFX) - sl->Ecnt = sl->SLL; - - sl->Einc = sl->EincS; - sl->Ecmp = ENV_END; - sl->Ecurp = SUBSTAIN; - break; - - case 2: - // Env_Substain_Next(slot_t *SL) - if (sl->SEG & 8) // SSG envelope type - { - int release = sl->SEG & 1; - - if ( !release ) - { - // re KEY ON - - // sl->Fcnt = 0; - // sl->ChgEnM = ~0; - - sl->Ecnt = 0; - sl->Einc = sl->EincA; - sl->Ecmp = ENV_DECAY; - sl->Ecurp = ATTACK; - } - - set_seg( *sl, (sl->SEG << 1) & 4 ); - - if ( !release ) - break; - } - // fall through - - case 3: - // Env_Release_Next - sl->Ecnt = ENV_END; - sl->Einc = 0; - sl->Ecmp = ENV_END + 1; - break; - - // default: no op - } -} - -inline void update_envelope( slot_t& sl ) -{ - int ecmp = sl.Ecmp; - if ( (sl.Ecnt += sl.Einc) >= ecmp ) - update_envelope_( &sl ); -} - -template -struct ym2612_update_chan { - static void func( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int ); -}; - -typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int ); - -template -void ym2612_update_chan::func( tables_t& g, channel_t& ch, - Ym2612_Emu::sample_t* buf, int length ) -{ - int not_end = ch.SLOT [S3].Ecnt - ENV_END; - - // algo is a compile-time constant, so all conditions based on it are resolved - // during compilation - - // special cases - if ( algo == 7 ) - not_end |= ch.SLOT [S0].Ecnt - ENV_END; - - if ( algo >= 5 ) - not_end |= ch.SLOT [S2].Ecnt - ENV_END; - - if ( algo >= 4 ) - not_end |= ch.SLOT [S1].Ecnt - ENV_END; - - int CH_S0_OUT_1 = ch.S0_OUT [1]; - - int in0 = ch.SLOT [S0].Fcnt; - int in1 = ch.SLOT [S1].Fcnt; - int in2 = ch.SLOT [S2].Fcnt; - int in3 = ch.SLOT [S3].Fcnt; - - int YM2612_LFOinc = g.LFOinc; - int YM2612_LFOcnt = g.LFOcnt + YM2612_LFOinc; - - if ( !not_end ) - return; - - do - { - // envelope - int const env_LFO = g.LFO_ENV_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK]; - - short const* const ENV_TAB = g.ENV_TAB; - - #define CALC_EN( x ) \ - int temp##x = ENV_TAB [ch.SLOT [S##x].Ecnt >> ENV_LBITS] + ch.SLOT [S##x].TLL; \ - int en##x = ((temp##x ^ ch.SLOT [S##x].env_xor) + (env_LFO >> ch.SLOT [S##x].AMS)) & \ - ((temp##x - ch.SLOT [S##x].env_max) >> 31); - - CALC_EN( 0 ) - CALC_EN( 1 ) - CALC_EN( 2 ) - CALC_EN( 3 ) - - int const* const TL_TAB = g.TL_TAB; - - #define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)]) - - // feedback - int CH_S0_OUT_0 = ch.S0_OUT [0]; - { - int temp = in0 + ((CH_S0_OUT_0 + CH_S0_OUT_1) >> ch.FB); - CH_S0_OUT_1 = CH_S0_OUT_0; - CH_S0_OUT_0 = SINT( (temp >> SIN_LBITS) & SIN_MASK, en0 ); - } - - int CH_OUTd; - if ( algo == 0 ) - { - int temp = in1 + CH_S0_OUT_1; - temp = in2 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ); - temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 1 ) - { - int temp = in2 + CH_S0_OUT_1 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); - temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 2 ) - { - int temp = in2 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); - temp = in3 + CH_S0_OUT_1 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 3 ) - { - int temp = in1 + CH_S0_OUT_1; - temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 4 ) - { - int temp = in3 + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ); - //DO_LIMIT - } - else if ( algo == 5 ) - { - int temp = CH_S0_OUT_1; - CH_OUTd = SINT( ((in3 + temp) >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( ((in1 + temp) >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( ((in2 + temp) >> SIN_LBITS) & SIN_MASK, en2 ); - //DO_LIMIT - } - else if ( algo == 6 ) - { - CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); - //DO_LIMIT - } - else if ( algo == 7 ) - { - CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ) + CH_S0_OUT_1; - //DO_LIMIT - } - - CH_OUTd >>= MAX_OUT_BITS - output_bits + 2; - - // update phase - unsigned freq_LFO = ((g.LFO_FREQ_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK] * - ch.FMS) >> (LFO_HBITS - 1 + 1)) + (1L << (LFO_FMS_LBITS - 1)); - YM2612_LFOcnt += YM2612_LFOinc; - in0 += (ch.SLOT [S0].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - in1 += (ch.SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - in2 += (ch.SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - in3 += (ch.SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - - int t0 = buf [0] + (CH_OUTd & ch.LEFT); - int t1 = buf [1] + (CH_OUTd & ch.RIGHT); - - update_envelope( ch.SLOT [0] ); - update_envelope( ch.SLOT [1] ); - update_envelope( ch.SLOT [2] ); - update_envelope( ch.SLOT [3] ); - - ch.S0_OUT [0] = CH_S0_OUT_0; - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - while ( --length ); - - ch.S0_OUT [1] = CH_S0_OUT_1; - - ch.SLOT [S0].Fcnt = in0; - ch.SLOT [S1].Fcnt = in1; - ch.SLOT [S2].Fcnt = in2; - ch.SLOT [S3].Fcnt = in3; -} - -static const ym2612_update_chan_t UPDATE_CHAN [8] = { - &ym2612_update_chan<0>::func, - &ym2612_update_chan<1>::func, - &ym2612_update_chan<2>::func, - &ym2612_update_chan<3>::func, - &ym2612_update_chan<4>::func, - &ym2612_update_chan<5>::func, - &ym2612_update_chan<6>::func, - &ym2612_update_chan<7>::func -}; - -void Ym2612_Impl::run_timer( int length ) -{ - int const step = 6; - int remain = length; - do - { - int n = step; - if ( n > remain ) - n = remain; - remain -= n; - - long i = n * YM2612.TimerBase; - if (YM2612.Mode & 1) // Timer A ON ? - { - // if ((YM2612.TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) - if ((YM2612.TimerAcnt -= i) <= 0) - { - // timer a overflow - - YM2612.Status |= (YM2612.Mode & 0x04) >> 2; - YM2612.TimerAcnt += YM2612.TimerAL; - - if (YM2612.Mode & 0x80) - { - KEY_ON( YM2612.CHANNEL [2], 0 ); - KEY_ON( YM2612.CHANNEL [2], 1 ); - KEY_ON( YM2612.CHANNEL [2], 2 ); - KEY_ON( YM2612.CHANNEL [2], 3 ); - } - } - } - - if (YM2612.Mode & 2) // Timer B ON ? - { - // if ((YM2612.TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) - if ((YM2612.TimerBcnt -= i) <= 0) - { - // timer b overflow - YM2612.Status |= (YM2612.Mode & 0x08) >> 2; - YM2612.TimerBcnt += YM2612.TimerBL; - } - } - } - while ( remain > 0 ); -} - -void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t* out ) -{ - if ( pair_count <= 0 ) - return; - - if ( YM2612.Mode & 3 ) - run_timer( pair_count ); - - // Mise à jour des pas des compteurs-frequences s'ils ont ete modifies - - for ( int chi = 0; chi < channel_count; chi++ ) - { - channel_t& ch = YM2612.CHANNEL [chi]; - if ( ch.SLOT [0].Finc != -1 ) - continue; - - int i2 = 0; - if ( chi == 2 && (YM2612.Mode & 0x40) ) - i2 = 2; - - for ( int i = 0; i < 4; i++ ) - { - // static int seq [4] = { 2, 1, 3, 0 }; - // if ( i2 ) i2 = seq [i]; - - slot_t& sl = ch.SLOT [i]; - int finc = g.FINC_TAB [ch.FNUM [i2]] >> (7 - ch.FOCT [i2]); - int ksr = ch.KC [i2] >> sl.KSR_S; // keycode attenuation - sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL; - if (sl.KSR != ksr) // si le KSR a change alors - { // les differents taux pour l'enveloppe sont mis à jour - sl.KSR = ksr; - - sl.EincA = sl.AR [ksr]; - sl.EincD = sl.DR [ksr]; - sl.EincS = sl.SR [ksr]; - sl.EincR = sl.RR [ksr]; - - if (sl.Ecurp == ATTACK) - { - sl.Einc = sl.EincA; - } - else if (sl.Ecurp == DECAY) - { - sl.Einc = sl.EincD; - } - else if (sl.Ecnt < ENV_END) - { - if (sl.Ecurp == SUBSTAIN) - sl.Einc = sl.EincS; - else if (sl.Ecurp == RELEASE) - sl.Einc = sl.EincR; - } - } - - if ( i2 ) - i2 = (i2 ^ 2) ^ (i2 >> 1); - } - } - - for ( int i = 0; i < channel_count; i++ ) - { - if ( !(mute_mask & (1 << i)) && (i != 5 || !YM2612.DAC) ) - UPDATE_CHAN [YM2612.CHANNEL [i].ALGO]( g, YM2612.CHANNEL [i], out, pair_count ); - } - - g.LFOcnt += g.LFOinc * pair_count; -} - -void Ym2612_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); } diff -Nru game-music-emu-0.6.2/gme/Ym2612_Emu.h game-music-emu-0.6.3/gme/Ym2612_Emu.h --- game-music-emu-0.6.2/gme/Ym2612_Emu.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ym2612_Emu.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,38 +1,19 @@ // YM2612 FM sound chip emulator interface -// Game_Music_Emu 0.6.0 -#ifndef YM2612_EMU_H -#define YM2612_EMU_H +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ -struct Ym2612_Impl; +#ifdef VGM_YM2612_GENS // LGPL v2.1+ license +#include "Ym2612_GENS.h" +typedef Ym2612_GENS_Emu Ym2612_Emu; +#endif -class Ym2612_Emu { - Ym2612_Impl* impl; -public: - Ym2612_Emu() { impl = 0; } - ~Ym2612_Emu(); - - // Set output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - const char* set_rate( double sample_rate, double clock_rate ); - - // Reset to power-up state - void reset(); - - // Mute voice n if bit n (1 << n) of mask is set - enum { channel_count = 6 }; - void mute_voices( int mask ); - - // Write addr to register 0 then data to register 1 - void write0( int addr, int data ); - - // Write addr to register 2 then data to register 3 - void write1( int addr, int data ); - - // Run and add pair_count samples into current output buffer contents - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; +#ifdef VGM_YM2612_NUKED // LGPL v2.1+ license +#include "Ym2612_Nuked.h" +typedef Ym2612_Nuked_Emu Ym2612_Emu; +#endif +#ifdef VGM_YM2612_MAME // GPL v2+ license +#include "Ym2612_MAME.h" +typedef Ym2612_MAME_Emu Ym2612_Emu; #endif + diff -Nru game-music-emu-0.6.2/gme/Ym2612_GENS.cpp game-music-emu-0.6.3/gme/Ym2612_GENS.cpp --- game-music-emu-0.6.2/gme/Ym2612_GENS.cpp 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ym2612_GENS.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,1319 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +// Based on Gens 2.10 ym2612.c + +#include "Ym2612_GENS.h" + +#include +#include +#include +#include +#include +#include + +/* Copyright (C) 2002 Stéphane Dallongeville (gens AT consolemul.com) */ +/* Copyright (C) 2004-2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +// This is mostly the original source in its C style and all. +// +// Somewhat optimized and simplified. Uses a template to generate the many +// variants of Update_Chan. Rewrote header file. In need of full rewrite by +// someone more familiar with FM sound and the YM2612. Has some inaccuracies +// compared to the Sega Genesis sound, particularly being mixed at such a +// high sample accuracy (the Genesis sounds like it has only 8 bit samples). +// - Shay + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +const int output_bits = 14; + +struct slot_t +{ + const int *DT; // parametre detune + int MUL; // parametre "multiple de frequence" + int TL; // Total Level = volume lorsque l'enveloppe est au plus haut + int TLL; // Total Level ajusted + int SLL; // Sustin Level (ajusted) = volume où l'enveloppe termine sa premiere phase de regression + int KSR_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe + int KSR; // Key Scale Rate = cette valeur est calculee par rapport à la frequence actuelle, elle va influer + // sur les differents parametres de l'enveloppe comme l'attaque, le decay ... comme dans la realite ! + int SEG; // Type enveloppe SSG + int env_xor; + int env_max; + + const int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR]) + const int *DR; // Decay Rate (table pointeur) = Taux pour la regression (DR[KSR]) + const int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR]) + const int *RR; // Release Rate (table pointeur) = Taux pour le rel'chement (RR[KSR]) + int Fcnt; // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN[Finc >> 16]) + int Finc; // frequency step = pas d'incrementation du compteur-frequence + // plus le pas est grand, plus la frequence est aïgu (ou haute) + int Ecurp; // Envelope current phase = cette variable permet de savoir dans quelle phase + // de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ... + // en fonction de la valeur de cette variable, on va appeler une fonction permettant + // de mettre à jour l'enveloppe courante. + int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe + int Einc; // Envelope step courant + int Ecmp; // Envelope counter limite pour la prochaine phase + int EincA; // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque + // cette valeur est egal à AR[KSR] + int EincD; // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression + // cette valeur est egal à DR[KSR] + int EincS; // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue + // cette valeur est egal à SR[KSR] + int EincR; // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement + // cette valeur est egal à RR[KSR] + int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entree + // d'un autre ou carrement à la sortie de la voie + int INd; // input data of the slot = donnees en entree du slot + int ChgEnM; // Change envelop mask. + int AMS; // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO + int AMSon; // AMS enable flag = drapeau d'activation de l'AMS +}; + +struct channel_t +{ + int S0_OUT[4]; // anciennes sorties slot 0 (pour le feed back) + int LEFT; // LEFT enable flag + int RIGHT; // RIGHT enable flag + int ALGO; // Algorythm = determine les connections entre les operateurs + int FB; // shift count of self feed back = degre de "Feed-Back" du SLOT 1 (il est son unique entree) + int FMS; // Frequency Modulation Sensitivity of channel = degre de modulation de la frequence sur la voie par le LFO + int AMS; // Amplitude Modulation Sensitivity of channel = degre de modulation de l'amplitude sur la voie par le LFO + int FNUM[4]; // hauteur frequence de la voie (+ 3 pour le mode special) + int FOCT[4]; // octave de la voie (+ 3 pour le mode special) + int KC[4]; // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S) + slot_t SLOT[4]; // four slot.operators = les 4 slots de la voie + int FFlag; // Frequency step recalculation flag +}; + +struct state_t +{ + int TimerBase; // TimerBase calculation + int Status; // YM2612 Status (timer overflow) + int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter + int TimerAL; + int TimerAcnt; // timerA counter = valeur courante du Timer A + int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter + int TimerBL; + int TimerBcnt; // timerB counter = valeur courante du Timer B + int Mode; // Mode actuel des voie 3 et 6 (normal / special) + int DAC; // DAC enabled flag + channel_t CHANNEL[Ym2612_GENS_Emu::channel_count]; // Les 6 voies du YM2612 + int REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif + // cela nous rend le debuggage plus facile +}; + +#ifndef PI +#define PI 3.14159265358979323846 +#endif + +#define ATTACK 0 +#define DECAY 1 +#define SUBSTAIN 2 +#define RELEASE 3 + +// SIN_LBITS <= 16 +// LFO_HBITS <= 16 +// (SIN_LBITS + SIN_HBITS) <= 26 +// (ENV_LBITS + ENV_HBITS) <= 28 +// (LFO_LBITS + LFO_HBITS) <= 28 + +#define SIN_HBITS 12 // Sinus phase counter int part +#define SIN_LBITS (26 - SIN_HBITS) // Sinus phase counter float part (best setting) + +#if (SIN_LBITS > 16) +#define SIN_LBITS 16 // Can't be greater than 16 bits +#endif + +#define ENV_HBITS 12 // Env phase counter int part +#define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting) + +#define LFO_HBITS 10 // LFO phase counter int part +#define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting) + +#define SIN_LENGHT (1 << SIN_HBITS) +#define ENV_LENGHT (1 << ENV_HBITS) +#define LFO_LENGHT (1 << LFO_HBITS) + +#define TL_LENGHT (ENV_LENGHT * 3) // Env + TL scaling + LFO + +#define SIN_MASK (SIN_LENGHT - 1) +#define ENV_MASK (ENV_LENGHT - 1) +#define LFO_MASK (LFO_LENGHT - 1) + +#define ENV_STEP (96.0 / ENV_LENGHT) // ENV_MAX = 96 dB + +#define ENV_ATTACK ((ENV_LENGHT * 0) << ENV_LBITS) +#define ENV_DECAY ((ENV_LENGHT * 1) << ENV_LBITS) +#define ENV_END ((ENV_LENGHT * 2) << ENV_LBITS) + +#define MAX_OUT_BITS (SIN_HBITS + SIN_LBITS + 2) // Modulation = -4 <--> +4 +#define MAX_OUT ((1 << MAX_OUT_BITS) - 1) + +#define PG_CUT_OFF ((int) (78.0 / ENV_STEP)) +#define ENV_CUT_OFF ((int) (68.0 / ENV_STEP)) + +#define AR_RATE 399128 +#define DR_RATE 5514396 + +//#define AR_RATE 426136 +//#define DR_RATE (AR_RATE * 12) + +#define LFO_FMS_LBITS 9 // FIXED (LFO_FMS_BASE gives somethink as 1) +#define LFO_FMS_BASE ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS))) + +#define S0 0 // Stupid typo of the YM2612 +#define S1 2 +#define S2 1 +#define S3 3 + +inline void set_seg( slot_t& s, int seg ) +{ + s.env_xor = 0; + s.env_max = INT_MAX; + s.SEG = seg; + if ( seg & 4 ) + { + s.env_xor = ENV_MASK; + s.env_max = ENV_MASK; + } +} + +struct tables_t +{ + short SIN_TAB [SIN_LENGHT]; // SINUS TABLE (offset into TL TABLE) + int LFOcnt; // LFO counter = compteur-frequence pour le LFO + int LFOinc; // LFO step counter = pas d'incrementation du compteur-frequence du LFO + // plus le pas est grand, plus la frequence est grande + unsigned int AR_TAB [128]; // Attack rate table + unsigned int DR_TAB [96]; // Decay rate table + unsigned int DT_TAB [8] [32]; // Detune table + unsigned int SL_TAB [16]; // Substain level table + unsigned int NULL_RATE [32]; // Table for NULL rate + int LFO_INC_TAB [8]; // LFO step table + + short ENV_TAB [2 * ENV_LENGHT + 8]; // ENV CURVE TABLE (attack & decay) + + short LFO_ENV_TAB [LFO_LENGHT]; // LFO AMS TABLE (adjusted for 11.8 dB) + short LFO_FREQ_TAB [LFO_LENGHT]; // LFO FMS TABLE + int TL_TAB [TL_LENGHT * 2]; // TOTAL LEVEL TABLE (positif and minus) + unsigned int DECAY_TO_ATTACK [ENV_LENGHT]; // Conversion from decay to attack phase + unsigned int FINC_TAB [2048]; // Frequency step table +}; + +static const unsigned char DT_DEF_TAB [4 * 32] = +{ +// FD = 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + +// FD = 1 + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, + +// FD = 2 + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16, + +// FD = 3 + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22 +}; + +static const unsigned char FKEY_TAB [16] = +{ + 0, 0, 0, 0, + 0, 0, 0, 1, + 2, 3, 3, 3, + 3, 3, 3, 3 +}; + +static const unsigned char LFO_AMS_TAB [4] = +{ + 31, 4, 1, 0 +}; + +static const unsigned char LFO_FMS_TAB [8] = +{ + LFO_FMS_BASE * 0, LFO_FMS_BASE * 1, + LFO_FMS_BASE * 2, LFO_FMS_BASE * 3, + LFO_FMS_BASE * 4, LFO_FMS_BASE * 6, + LFO_FMS_BASE * 12, LFO_FMS_BASE * 24 +}; + +inline void YM2612_Special_Update() { } + +struct Ym2612_GENS_Impl +{ + enum { channel_count = Ym2612_GENS_Emu::channel_count }; + + state_t YM2612; + int mute_mask; + tables_t g; + + void KEY_ON( channel_t&, int ); + void KEY_OFF( channel_t&, int ); + int SLOT_SET( int, int ); + int CHANNEL_SET( int, int ); + int YM_SET( int, int ); + + void set_rate( double sample_rate, double clock_factor ); + void reset(); + void write0( int addr, int data ); + void write1( int addr, int data ); + void run_timer( int ); + void run( int pair_count, Ym2612_GENS_Emu::sample_t* ); +}; + +void Ym2612_GENS_Impl::KEY_ON( channel_t& ch, int nsl) +{ + slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot + + if (SL->Ecurp == RELEASE) // la touche est-elle rel'chee ? + { + SL->Fcnt = 0; + + // Fix Ecco 2 splash sound + + SL->Ecnt = (g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM; + SL->ChgEnM = ~0; + +// SL->Ecnt = g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK; +// SL->Ecnt = 0; + + SL->Einc = SL->EincA; + SL->Ecmp = ENV_DECAY; + SL->Ecurp = ATTACK; + } +} + + +void Ym2612_GENS_Impl::KEY_OFF(channel_t& ch, int nsl) +{ + slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot + + if (SL->Ecurp != RELEASE) // la touche est-elle appuyee ? + { + if (SL->Ecnt < ENV_DECAY) // attack phase ? + { + SL->Ecnt = (g.ENV_TAB [SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY; + } + + SL->Einc = SL->EincR; + SL->Ecmp = ENV_END; + SL->Ecurp = RELEASE; + } +} + + +int Ym2612_GENS_Impl::SLOT_SET( int Adr, int data ) +{ + int nch = Adr & 3; + if ( nch == 3 ) + return 1; + + channel_t& ch = YM2612.CHANNEL [nch + (Adr & 0x100 ? 3 : 0)]; + slot_t& sl = ch.SLOT [(Adr >> 2) & 3]; + + switch ( Adr & 0xF0 ) + { + case 0x30: + if ( (sl.MUL = (data & 0x0F)) != 0 ) sl.MUL <<= 1; + else sl.MUL = 1; + + sl.DT = (int*) g.DT_TAB [(data >> 4) & 7]; + + ch.SLOT [0].Finc = -1; + + break; + + case 0x40: + sl.TL = data & 0x7F; + + // SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound... + YM2612_Special_Update(); + +#if ((ENV_HBITS - 7) < 0) + sl.TLL = sl.TL >> (7 - ENV_HBITS); +#else + sl.TLL = sl.TL << (ENV_HBITS - 7); +#endif + + break; + + case 0x50: + sl.KSR_S = 3 - (data >> 6); + + ch.SLOT [0].Finc = -1; + + if (data &= 0x1F) sl.AR = (int*) &g.AR_TAB [data << 1]; + else sl.AR = (int*) &g.NULL_RATE [0]; + + sl.EincA = sl.AR [sl.KSR]; + if (sl.Ecurp == ATTACK) sl.Einc = sl.EincA; + break; + + case 0x60: + if ( (sl.AMSon = (data & 0x80)) != 0 ) sl.AMS = ch.AMS; + else sl.AMS = 31; + + if (data &= 0x1F) sl.DR = (int*) &g.DR_TAB [data << 1]; + else sl.DR = (int*) &g.NULL_RATE [0]; + + sl.EincD = sl.DR [sl.KSR]; + if (sl.Ecurp == DECAY) sl.Einc = sl.EincD; + break; + + case 0x70: + if (data &= 0x1F) sl.SR = (int*) &g.DR_TAB [data << 1]; + else sl.SR = (int*) &g.NULL_RATE [0]; + + sl.EincS = sl.SR [sl.KSR]; + if ((sl.Ecurp == SUBSTAIN) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincS; + break; + + case 0x80: + sl.SLL = g.SL_TAB [data >> 4]; + + sl.RR = (int*) &g.DR_TAB [((data & 0xF) << 2) + 2]; + + sl.EincR = sl.RR [sl.KSR]; + if ((sl.Ecurp == RELEASE) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincR; + break; + + case 0x90: + // SSG-EG envelope shapes : + /* + E At Al H + + 1 0 0 0 \\\\ + 1 0 0 1 \___ + 1 0 1 0 \/\/ + 1 0 1 1 \ + 1 1 0 0 //// + 1 1 0 1 / + 1 1 1 0 /\/\ + 1 1 1 1 /___ + + E = SSG-EG enable + At = Start negate + Al = Altern + H = Hold */ + + set_seg( sl, (data & 8) ? (data & 0x0F) : 0 ); + break; + } + + return 0; +} + + +int Ym2612_GENS_Impl::CHANNEL_SET( int Adr, int data ) +{ + int num = Adr & 3; + if ( num == 3 ) + return 1; + + channel_t& ch = YM2612.CHANNEL [num + (Adr & 0x100 ? 3 : 0)]; + + switch ( Adr & 0xFC ) + { + case 0xA0: + YM2612_Special_Update(); + + ch.FNUM [0] = (ch.FNUM [0] & 0x700) + data; + ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; + + ch.SLOT [0].Finc = -1; + break; + + case 0xA4: + YM2612_Special_Update(); + + ch.FNUM [0] = (ch.FNUM [0] & 0x0FF) + ((data & 0x07) << 8); + ch.FOCT [0] = (data & 0x38) >> 3; + ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; + + ch.SLOT [0].Finc = -1; + break; + + case 0xA8: + if ( Adr < 0x100 ) + { + num++; + + YM2612_Special_Update(); + + YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x700) + data; + YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | + FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; + + YM2612.CHANNEL [2].SLOT [0].Finc = -1; + } + break; + + case 0xAC: + if ( Adr < 0x100 ) + { + num++; + + YM2612_Special_Update(); + + YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x0FF) + ((data & 0x07) << 8); + YM2612.CHANNEL [2].FOCT [num] = (data & 0x38) >> 3; + YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | + FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; + + YM2612.CHANNEL [2].SLOT [0].Finc = -1; + } + break; + + case 0xB0: + if ( ch.ALGO != (data & 7) ) + { + // Fix VectorMan 2 heli sound (level 1) + YM2612_Special_Update(); + + ch.ALGO = data & 7; + + ch.SLOT [0].ChgEnM = 0; + ch.SLOT [1].ChgEnM = 0; + ch.SLOT [2].ChgEnM = 0; + ch.SLOT [3].ChgEnM = 0; + } + + ch.FB = 9 - ((data >> 3) & 7); // Real thing ? + +// if (ch.FB = ((data >> 3) & 7)) ch.FB = 9 - ch.FB; // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound... +// else ch.FB = 31; + break; + + case 0xB4: { + YM2612_Special_Update(); + + ch.LEFT = 0 - ((data >> 7) & 1); + ch.RIGHT = 0 - ((data >> 6) & 1); + + ch.AMS = LFO_AMS_TAB [(data >> 4) & 3]; + ch.FMS = LFO_FMS_TAB [data & 7]; + + for ( int i = 0; i < 4; i++ ) + { + slot_t& sl = ch.SLOT [i]; + sl.AMS = (sl.AMSon ? ch.AMS : 31); + } + break; + } + } + + return 0; +} + + +int Ym2612_GENS_Impl::YM_SET(int Adr, int data) +{ + switch ( Adr ) + { + case 0x22: + if (data & 8) // LFO enable + { + // Cool Spot music 1, LFO modified severals time which + // distord the sound, have to check that on a real genesis... + + g.LFOinc = g.LFO_INC_TAB [data & 7]; + } + else + { + g.LFOinc = g.LFOcnt = 0; + } + break; + + case 0x24: + YM2612.TimerA = (YM2612.TimerA & 0x003) | (((int) data) << 2); + + if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) + { + YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; + } + break; + + case 0x25: + YM2612.TimerA = (YM2612.TimerA & 0x3FC) | (data & 3); + + if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) + { + YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; + } + break; + + case 0x26: + YM2612.TimerB = data; + + if (YM2612.TimerBL != (256 - YM2612.TimerB) << (4 + 12)) + { + YM2612.TimerBcnt = YM2612.TimerBL = (256 - YM2612.TimerB) << (4 + 12); + } + break; + + case 0x27: + // Parametre divers + // b7 = CSM MODE + // b6 = 3 slot mode + // b5 = reset b + // b4 = reset a + // b3 = timer enable b + // b2 = timer enable a + // b1 = load b + // b0 = load a + + if ((data ^ YM2612.Mode) & 0x40) + { + // We changed the channel 2 mode, so recalculate phase step + // This fix the punch sound in Street of Rage 2 + + YM2612_Special_Update(); + + YM2612.CHANNEL [2].SLOT [0].Finc = -1; // recalculate phase step + } + +// if ((data & 2) && (YM2612.Status & 2)) YM2612.TimerBcnt = YM2612.TimerBL; +// if ((data & 1) && (YM2612.Status & 1)) YM2612.TimerAcnt = YM2612.TimerAL; + +// YM2612.Status &= (~data >> 4); // Reset du Status au cas ou c'est demande + YM2612.Status &= (~data >> 4) & (data >> 2); // Reset Status + + YM2612.Mode = data; + break; + + case 0x28: { + int nch = data & 3; + if ( nch == 3 ) + return 1; + if ( data & 4 ) + nch += 3; + channel_t& ch = YM2612.CHANNEL [nch]; + + YM2612_Special_Update(); + + if (data & 0x10) KEY_ON(ch, S0); // On appuie sur la touche pour le slot 1 + else KEY_OFF(ch, S0); // On rel'che la touche pour le slot 1 + if (data & 0x20) KEY_ON(ch, S1); // On appuie sur la touche pour le slot 3 + else KEY_OFF(ch, S1); // On rel'che la touche pour le slot 3 + if (data & 0x40) KEY_ON(ch, S2); // On appuie sur la touche pour le slot 2 + else KEY_OFF(ch, S2); // On rel'che la touche pour le slot 2 + if (data & 0x80) KEY_ON(ch, S3); // On appuie sur la touche pour le slot 4 + else KEY_OFF(ch, S3); // On rel'che la touche pour le slot 4 + break; + } + + case 0x2B: + if (YM2612.DAC ^ (data & 0x80)) YM2612_Special_Update(); + + YM2612.DAC = data & 0x80; // activation/desactivation du DAC + break; + } + + return 0; +} + +void Ym2612_GENS_Impl::set_rate( double sample_rate, double clock_rate ) +{ + assert( sample_rate ); + assert( clock_rate > sample_rate ); + + int i; + + // 144 = 12 * (prescale * 2) = 12 * 6 * 2 + // prescale set to 6 by default + + double Frequence = clock_rate / sample_rate / 144.0; + if ( fabs( Frequence - 1.0 ) < 0.0000001 ) + Frequence = 1.0; + YM2612.TimerBase = int (Frequence * 4096.0); + + // Tableau TL : + // [0 - 4095] = +output [4095 - ...] = +output overflow (fill with 0) + // [12288 - 16383] = -output [16384 - ...] = -output overflow (fill with 0) + + for(i = 0; i < TL_LENGHT; i++) + { + if (i >= PG_CUT_OFF) // YM2612 cut off sound after 78 dB (14 bits output ?) + { + g.TL_TAB [TL_LENGHT + i] = g.TL_TAB [i] = 0; + } + else + { + double x = MAX_OUT; // Max output + x /= pow( 10.0, (ENV_STEP * i) / 20.0 ); // Decibel -> Voltage + + g.TL_TAB [i] = (int) x; + g.TL_TAB [TL_LENGHT + i] = -g.TL_TAB [i]; + } + } + + // Tableau SIN : + // g.SIN_TAB [x] [y] = sin(x) * y; + // x = phase and y = volume + + g.SIN_TAB [0] = g.SIN_TAB [SIN_LENGHT / 2] = PG_CUT_OFF; + + for(i = 1; i <= SIN_LENGHT / 4; i++) + { + double x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT)); // Sinus + x = 20 * log10(1 / x); // convert to dB + + int j = (int) (x / ENV_STEP); // Get TL range + + if (j > PG_CUT_OFF) j = (int) PG_CUT_OFF; + + g.SIN_TAB [i] = g.SIN_TAB [(SIN_LENGHT / 2) - i] = j; + g.SIN_TAB [(SIN_LENGHT / 2) + i] = g.SIN_TAB [SIN_LENGHT - i] = TL_LENGHT + j; + } + + // Tableau LFO (LFO wav) : + + for(i = 0; i < LFO_LENGHT; i++) + { + double x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus + x += 1.0; + x /= 2.0; // positive only + x *= 11.8 / ENV_STEP; // ajusted to MAX enveloppe modulation + + g.LFO_ENV_TAB [i] = (int) x; + + x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus + x *= (double) ((1 << (LFO_HBITS - 1)) - 1); + + g.LFO_FREQ_TAB [i] = (int) x; + + } + + // Tableau Enveloppe : + // g.ENV_TAB [0] -> g.ENV_TAB [ENV_LENGHT - 1] = attack curve + // g.ENV_TAB [ENV_LENGHT] -> g.ENV_TAB [2 * ENV_LENGHT - 1] = decay curve + + for(i = 0; i < ENV_LENGHT; i++) + { + // Attack curve (x^8 - music level 2 Vectorman 2) + double x = pow(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8); + x *= ENV_LENGHT; + + g.ENV_TAB [i] = (int) x; + + // Decay curve (just linear) + x = pow(((double) (i) / (double) (ENV_LENGHT)), 1); + x *= ENV_LENGHT; + + g.ENV_TAB [ENV_LENGHT + i] = (int) x; + } + for ( i = 0; i < 8; i++ ) + g.ENV_TAB [i + ENV_LENGHT * 2] = 0; + + g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1; // for the stopped state + + // Tableau pour la conversion Attack -> Decay and Decay -> Attack + + int j = ENV_LENGHT - 1; + for ( i = 0; i < ENV_LENGHT; i++ ) + { + while ( j && g.ENV_TAB [j] < i ) + j--; + + g.DECAY_TO_ATTACK [i] = j << ENV_LBITS; + } + + // Tableau pour le Substain Level + + for(i = 0; i < 15; i++) + { + double x = i * 3; // 3 and not 6 (Mickey Mania first music for test) + x /= ENV_STEP; + + g.SL_TAB [i] = ((int) x << ENV_LBITS) + ENV_DECAY; + } + + g.SL_TAB [15] = ((ENV_LENGHT - 1) << ENV_LBITS) + ENV_DECAY; // special case : volume off + + // Tableau Frequency Step + + for(i = 0; i < 2048; i++) + { + double x = (double) (i) * Frequence; + +#if ((SIN_LBITS + SIN_HBITS - (21 - 7)) < 0) + x /= (double) (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS)); +#else + x *= (double) (1 << (SIN_LBITS + SIN_HBITS - (21 - 7))); +#endif + + x /= 2.0; // because MUL = value * 2 + + g.FINC_TAB [i] = (unsigned int) x; + } + + // Tableaux Attack & Decay Rate + + for(i = 0; i < 4; i++) + { + g.AR_TAB [i] = 0; + g.DR_TAB [i] = 0; + } + + for(i = 0; i < 60; i++) + { + double x = Frequence; + + x *= 1.0 + ((i & 3) * 0.25); // bits 0-1 : x1.00, x1.25, x1.50, x1.75 + x *= (double) (1 << ((i >> 2))); // bits 2-5 : shift bits (x2^0 - x2^15) + x *= (double) (ENV_LENGHT << ENV_LBITS); // on ajuste pour le tableau g.ENV_TAB + + g.AR_TAB [i + 4] = (unsigned int) (x / AR_RATE); + g.DR_TAB [i + 4] = (unsigned int) (x / DR_RATE); + } + + for(i = 64; i < 96; i++) + { + g.AR_TAB [i] = g.AR_TAB [63]; + g.DR_TAB [i] = g.DR_TAB [63]; + + g.NULL_RATE [i - 64] = 0; + } + + for ( i = 96; i < 128; i++ ) + g.AR_TAB [i] = 0; + + // Tableau Detune + + for(i = 0; i < 4; i++) + { + for (int j = 0; j < 32; j++) + { +#if ((SIN_LBITS + SIN_HBITS - 21) < 0) + double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS)); +#else + double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21)); +#endif + + g.DT_TAB [i + 0] [j] = (int) y; + g.DT_TAB [i + 4] [j] = (int) -y; + } + } + + // Tableau LFO + g.LFO_INC_TAB [0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + g.LFO_INC_TAB [7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); + + reset(); +} + +const char* Ym2612_GENS_Emu::set_rate( double sample_rate, double clock_rate ) +{ + if ( !impl ) + { + impl = (Ym2612_GENS_Impl*) malloc( sizeof *impl ); + if ( !impl ) + return "Out of memory"; + impl->mute_mask = 0; + } + memset( &impl->YM2612, 0, sizeof impl->YM2612 ); + + impl->set_rate( sample_rate, clock_rate ); + + return 0; +} + +Ym2612_GENS_Emu::~Ym2612_GENS_Emu() +{ + free( impl ); +} + +inline void Ym2612_GENS_Impl::write0( int opn_addr, int data ) +{ + assert( (unsigned) data <= 0xFF ); + + if ( opn_addr < 0x30 ) + { + YM2612.REG [0] [opn_addr] = data; + YM_SET( opn_addr, data ); + } + else if ( YM2612.REG [0] [opn_addr] != data ) + { + YM2612.REG [0] [opn_addr] = data; + + if ( opn_addr < 0xA0 ) + SLOT_SET( opn_addr, data ); + else + CHANNEL_SET( opn_addr, data ); + } +} + +inline void Ym2612_GENS_Impl::write1( int opn_addr, int data ) +{ + assert( (unsigned) data <= 0xFF ); + + if ( opn_addr >= 0x30 && YM2612.REG [1] [opn_addr] != data ) + { + YM2612.REG [1] [opn_addr] = data; + + if ( opn_addr < 0xA0 ) + SLOT_SET( opn_addr + 0x100, data ); + else + CHANNEL_SET( opn_addr + 0x100, data ); + } +} + +void Ym2612_GENS_Emu::reset() +{ + impl->reset(); +} + +void Ym2612_GENS_Impl::reset() +{ + g.LFOcnt = 0; + YM2612.TimerA = 0; + YM2612.TimerAL = 0; + YM2612.TimerAcnt = 0; + YM2612.TimerB = 0; + YM2612.TimerBL = 0; + YM2612.TimerBcnt = 0; + YM2612.DAC = 0; + + YM2612.Status = 0; + + int i; + for ( i = 0; i < channel_count; i++ ) + { + channel_t& ch = YM2612.CHANNEL [i]; + + ch.LEFT = ~0; + ch.RIGHT = ~0; + ch.ALGO = 0; + ch.FB = 31; + ch.FMS = 0; + ch.AMS = 0; + + for ( int j = 0 ;j < 4 ; j++ ) + { + ch.S0_OUT [j] = 0; + ch.FNUM [j] = 0; + ch.FOCT [j] = 0; + ch.KC [j] = 0; + + ch.SLOT [j].Fcnt = 0; + ch.SLOT [j].Finc = 0; + ch.SLOT [j].Ecnt = ENV_END; // Put it at the end of Decay phase... + ch.SLOT [j].Einc = 0; + ch.SLOT [j].Ecmp = 0; + ch.SLOT [j].Ecurp = RELEASE; + + ch.SLOT [j].ChgEnM = 0; + } + } + + for ( i = 0; i < 0x100; i++ ) + { + YM2612.REG [0] [i] = -1; + YM2612.REG [1] [i] = -1; + } + + for ( i = 0xB6; i >= 0xB4; i-- ) + { + write0( i, 0xC0 ); + write1( i, 0xC0 ); + } + + for ( i = 0xB2; i >= 0x22; i-- ) + { + write0( i, 0 ); + write1( i, 0 ); + } + + write0( 0x2A, 0x80 ); +} + +void Ym2612_GENS_Emu::write0( int addr, int data ) +{ + impl->write0( addr, data ); +} + +void Ym2612_GENS_Emu::write1( int addr, int data ) +{ + impl->write1( addr, data ); +} + +void Ym2612_GENS_Emu::mute_voices( int mask ) { impl->mute_mask = mask; } + +static void update_envelope_( slot_t* sl ) +{ + switch ( sl->Ecurp ) + { + case 0: + // Env_Attack_Next + + // Verified with Gynoug even in HQ (explode SFX) + sl->Ecnt = ENV_DECAY; + + sl->Einc = sl->EincD; + sl->Ecmp = sl->SLL; + sl->Ecurp = DECAY; + break; + + case 1: + // Env_Decay_Next + + // Verified with Gynoug even in HQ (explode SFX) + sl->Ecnt = sl->SLL; + + sl->Einc = sl->EincS; + sl->Ecmp = ENV_END; + sl->Ecurp = SUBSTAIN; + break; + + case 2: + // Env_Substain_Next(slot_t *SL) + if (sl->SEG & 8) // SSG envelope type + { + int release = sl->SEG & 1; + + if ( !release ) + { + // re KEY ON + + // sl->Fcnt = 0; + // sl->ChgEnM = ~0; + + sl->Ecnt = 0; + sl->Einc = sl->EincA; + sl->Ecmp = ENV_DECAY; + sl->Ecurp = ATTACK; + } + + set_seg( *sl, (sl->SEG << 1) & 4 ); + + if ( !release ) + break; + } + // fall through + + case 3: + // Env_Release_Next + sl->Ecnt = ENV_END; + sl->Einc = 0; + sl->Ecmp = ENV_END + 1; + break; + + // default: no op + } +} + +inline void update_envelope( slot_t& sl ) +{ + int ecmp = sl.Ecmp; + if ( (sl.Ecnt += sl.Einc) >= ecmp ) + update_envelope_( &sl ); +} + +template +struct ym2612_update_chan { + static void func( tables_t&, channel_t&, Ym2612_GENS_Emu::sample_t*, int ); +}; + +typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_GENS_Emu::sample_t*, int ); + +template +void ym2612_update_chan::func( tables_t& g, channel_t& ch, + Ym2612_GENS_Emu::sample_t* buf, int length ) +{ + int not_end = ch.SLOT [S3].Ecnt - ENV_END; + + // algo is a compile-time constant, so all conditions based on it are resolved + // during compilation + + // special cases + if ( algo == 7 ) + not_end |= ch.SLOT [S0].Ecnt - ENV_END; + + if ( algo >= 5 ) + not_end |= ch.SLOT [S2].Ecnt - ENV_END; + + if ( algo >= 4 ) + not_end |= ch.SLOT [S1].Ecnt - ENV_END; + + int CH_S0_OUT_1 = ch.S0_OUT [1]; + + int in0 = ch.SLOT [S0].Fcnt; + int in1 = ch.SLOT [S1].Fcnt; + int in2 = ch.SLOT [S2].Fcnt; + int in3 = ch.SLOT [S3].Fcnt; + + int YM2612_LFOinc = g.LFOinc; + int YM2612_LFOcnt = g.LFOcnt + YM2612_LFOinc; + + if ( !not_end ) + return; + + do + { + // envelope + int const env_LFO = g.LFO_ENV_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK]; + + short const* const ENV_TAB = g.ENV_TAB; + + #define CALC_EN( x ) \ + int temp##x = ENV_TAB [ch.SLOT [S##x].Ecnt >> ENV_LBITS] + ch.SLOT [S##x].TLL; \ + int en##x = ((temp##x ^ ch.SLOT [S##x].env_xor) + (env_LFO >> ch.SLOT [S##x].AMS)) & \ + ((temp##x - ch.SLOT [S##x].env_max) >> 31); + + CALC_EN( 0 ) + CALC_EN( 1 ) + CALC_EN( 2 ) + CALC_EN( 3 ) + + int const* const TL_TAB = g.TL_TAB; + + #define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)]) + + // feedback + int CH_S0_OUT_0 = ch.S0_OUT [0]; + { + int temp = in0 + ((CH_S0_OUT_0 + CH_S0_OUT_1) >> ch.FB); + CH_S0_OUT_1 = CH_S0_OUT_0; + CH_S0_OUT_0 = SINT( (temp >> SIN_LBITS) & SIN_MASK, en0 ); + } + + int CH_OUTd; + if ( algo == 0 ) + { + int temp = in1 + CH_S0_OUT_1; + temp = in2 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ); + temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 1 ) + { + int temp = in2 + CH_S0_OUT_1 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); + temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 2 ) + { + int temp = in2 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); + temp = in3 + CH_S0_OUT_1 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 3 ) + { + int temp = in1 + CH_S0_OUT_1; + temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); + } + else if ( algo == 4 ) + { + int temp = in3 + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); + CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ); + //DO_LIMIT + } + else if ( algo == 5 ) + { + int temp = CH_S0_OUT_1; + CH_OUTd = SINT( ((in3 + temp) >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( ((in1 + temp) >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( ((in2 + temp) >> SIN_LBITS) & SIN_MASK, en2 ); + //DO_LIMIT + } + else if ( algo == 6 ) + { + CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); + //DO_LIMIT + } + else if ( algo == 7 ) + { + CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ) + + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ) + CH_S0_OUT_1; + //DO_LIMIT + } + + CH_OUTd >>= MAX_OUT_BITS - output_bits + 2; + + // update phase + unsigned freq_LFO = ((g.LFO_FREQ_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK] * + ch.FMS) >> (LFO_HBITS - 1 + 1)) + (1L << (LFO_FMS_LBITS - 1)); + YM2612_LFOcnt += YM2612_LFOinc; + in0 += (ch.SLOT [S0].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + in1 += (ch.SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + in2 += (ch.SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + in3 += (ch.SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); + + int t0 = buf [0] + (CH_OUTd & ch.LEFT); + int t1 = buf [1] + (CH_OUTd & ch.RIGHT); + + update_envelope( ch.SLOT [0] ); + update_envelope( ch.SLOT [1] ); + update_envelope( ch.SLOT [2] ); + update_envelope( ch.SLOT [3] ); + + ch.S0_OUT [0] = CH_S0_OUT_0; + buf [0] = t0; + buf [1] = t1; + buf += 2; + } + while ( --length ); + + ch.S0_OUT [1] = CH_S0_OUT_1; + + ch.SLOT [S0].Fcnt = in0; + ch.SLOT [S1].Fcnt = in1; + ch.SLOT [S2].Fcnt = in2; + ch.SLOT [S3].Fcnt = in3; +} + +static const ym2612_update_chan_t UPDATE_CHAN [8] = { + &ym2612_update_chan<0>::func, + &ym2612_update_chan<1>::func, + &ym2612_update_chan<2>::func, + &ym2612_update_chan<3>::func, + &ym2612_update_chan<4>::func, + &ym2612_update_chan<5>::func, + &ym2612_update_chan<6>::func, + &ym2612_update_chan<7>::func +}; + +void Ym2612_GENS_Impl::run_timer( int length ) +{ + int const step = 6; + int remain = length; + do + { + int n = step; + if ( n > remain ) + n = remain; + remain -= n; + + long i = n * YM2612.TimerBase; + if (YM2612.Mode & 1) // Timer A ON ? + { + // if ((YM2612.TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) + if ((YM2612.TimerAcnt -= i) <= 0) + { + // timer a overflow + + YM2612.Status |= (YM2612.Mode & 0x04) >> 2; + YM2612.TimerAcnt += YM2612.TimerAL; + + if (YM2612.Mode & 0x80) + { + KEY_ON( YM2612.CHANNEL [2], 0 ); + KEY_ON( YM2612.CHANNEL [2], 1 ); + KEY_ON( YM2612.CHANNEL [2], 2 ); + KEY_ON( YM2612.CHANNEL [2], 3 ); + } + } + } + + if (YM2612.Mode & 2) // Timer B ON ? + { + // if ((YM2612.TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) + if ((YM2612.TimerBcnt -= i) <= 0) + { + // timer b overflow + YM2612.Status |= (YM2612.Mode & 0x08) >> 2; + YM2612.TimerBcnt += YM2612.TimerBL; + } + } + } + while ( remain > 0 ); +} + +void Ym2612_GENS_Impl::run( int pair_count, Ym2612_GENS_Emu::sample_t* out ) +{ + if ( pair_count <= 0 ) + return; + + if ( YM2612.Mode & 3 ) + run_timer( pair_count ); + + // Mise à jour des pas des compteurs-frequences s'ils ont ete modifies + + for ( int chi = 0; chi < channel_count; chi++ ) + { + channel_t& ch = YM2612.CHANNEL [chi]; + if ( ch.SLOT [0].Finc != -1 ) + continue; + + int i2 = 0; + if ( chi == 2 && (YM2612.Mode & 0x40) ) + i2 = 2; + + for ( int i = 0; i < 4; i++ ) + { + // static int seq [4] = { 2, 1, 3, 0 }; + // if ( i2 ) i2 = seq [i]; + + slot_t& sl = ch.SLOT [i]; + int finc = g.FINC_TAB [ch.FNUM [i2]] >> (7 - ch.FOCT [i2]); + int ksr = ch.KC [i2] >> sl.KSR_S; // keycode attenuation + sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL; + if (sl.KSR != ksr) // si le KSR a change alors + { // les differents taux pour l'enveloppe sont mis à jour + sl.KSR = ksr; + + sl.EincA = sl.AR [ksr]; + sl.EincD = sl.DR [ksr]; + sl.EincS = sl.SR [ksr]; + sl.EincR = sl.RR [ksr]; + + if (sl.Ecurp == ATTACK) + { + sl.Einc = sl.EincA; + } + else if (sl.Ecurp == DECAY) + { + sl.Einc = sl.EincD; + } + else if (sl.Ecnt < ENV_END) + { + if (sl.Ecurp == SUBSTAIN) + sl.Einc = sl.EincS; + else if (sl.Ecurp == RELEASE) + sl.Einc = sl.EincR; + } + } + + if ( i2 ) + i2 = (i2 ^ 2) ^ (i2 >> 1); + } + } + + for ( int i = 0; i < channel_count; i++ ) + { + if ( !(mute_mask & (1 << i)) && (i != 5 || !YM2612.DAC) ) + UPDATE_CHAN [YM2612.CHANNEL [i].ALGO]( g, YM2612.CHANNEL [i], out, pair_count ); + } + + g.LFOcnt += g.LFOinc * pair_count; +} + +void Ym2612_GENS_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); } diff -Nru game-music-emu-0.6.2/gme/Ym2612_GENS.h game-music-emu-0.6.3/gme/Ym2612_GENS.h --- game-music-emu-0.6.2/gme/Ym2612_GENS.h 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ym2612_GENS.h 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,38 @@ +// YM2612 FM sound chip emulator interface + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef YM2612_EMU_H +#define YM2612_EMU_H + +struct Ym2612_GENS_Impl; + +class Ym2612_GENS_Emu { + Ym2612_GENS_Impl* impl; +public: + Ym2612_GENS_Emu() { impl = 0; } + ~Ym2612_GENS_Emu(); + + // Set output sample rate and chip clock rates, in Hz. Returns non-zero + // if error. + const char* set_rate( double sample_rate, double clock_rate ); + + // Reset to power-up state + void reset(); + + // Mute voice n if bit n (1 << n) of mask is set + enum { channel_count = 6 }; + void mute_voices( int mask ); + + // Write addr to register 0 then data to register 1 + void write0( int addr, int data ); + + // Write addr to register 2 then data to register 3 + void write1( int addr, int data ); + + // Run and add pair_count samples into current output buffer contents + typedef short sample_t; + enum { out_chan_count = 2 }; // stereo + void run( int pair_count, sample_t* out ); +}; + +#endif diff -Nru game-music-emu-0.6.2/gme/Ym2612_MAME.cpp game-music-emu-0.6.3/gme/Ym2612_MAME.cpp --- game-music-emu-0.6.2/gme/Ym2612_MAME.cpp 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ym2612_MAME.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,3108 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +// Based on Mame YM2612 ym2612.c + +#include "Ym2612_MAME.h" + +/* +** +** File: fm2612.c -- software implementation of Yamaha YM2612 FM sound generator +** Split from fm.c to keep 2612 fixes from infecting other OPN chips +** +** Copyright Jarek Burczynski (bujar at mame dot net) +** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development +** +** Version 1.5.1 (Genesis Plus GX ym2612.c rev. 368) +** +*/ + +/* +** History: +** +** 2006~2012 Eke-Eke (Genesis Plus GX): +** Huge thanks to Nemesis, lot of those fixes came from his tests on Sega Genesis hardware +** More informations at http://gendev.spritesmind.net/forum/viewtopic.php?t=386 +** +** TODO: +** +** - core documentation +** - BUSY flag support +** +** CHANGELOG: +** +** 26-09-2017 Eke-Eke (Genesis Plus GX): +** - fixed EG counter loopback behavior (verified on YM3438 die) +** - reverted changes to EG rates 2-7 increment values +** +** xx-xx-xxxx +** - fixed LFO implementation: +** .added support for CH3 special mode: fixes various sound effects (birds in Warlock, bug sound in Aladdin...) +** .inverted LFO AM waveform: fixes Spider-Man & Venom : Separation Anxiety (intro), California Games (surfing event) +** .improved LFO timing accuracy: now updated AFTER sample output, like EG/PG updates, and without any precision loss anymore. +** - improved internal timers emulation +** - adjusted lowest EG rates increment values +** - fixed Attack Rate not being updated in some specific cases (Batman & Robin intro) +** - fixed EG behavior when Attack Rate is maximal +** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation +** - implemented EG output immediate changes on register writes +** - fixed YM2612 initial values (after the reset): fixes missing intro in B.O.B +** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many other games using GEMS sound engine) +** - implemented accurate CSM mode emulation +** - implemented accurate SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Stix & many other games) +** - implemented accurate address/data ports behavior +** +** 06-23-2007 Zsolt Vasvari: +** - changed the timing not to require the use of floating point calculations +** +** 03-08-2003 Jarek Burczynski: +** - fixed YM2608 initial values (after the reset) +** - fixed flag and irqmask handling (YM2608) +** - fixed BUFRDY flag handling (YM2608) +** +** 14-06-2003 Jarek Burczynski: +** - implemented all of the YM2608 status register flags +** - implemented support for external memory read/write via YM2608 +** - implemented support for deltat memory limit register in YM2608 emulation +** +** 22-05-2003 Jarek Burczynski: +** - fixed LFO PM calculations (copy&paste bugfix) +** +** 08-05-2003 Jarek Burczynski: +** - fixed SSG support +** +** 22-04-2003 Jarek Burczynski: +** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608) +** +** 15-04-2003 Jarek Burczynski: +** - added support for YM2608's register 0x110 - status mask +** +** 01-12-2002 Jarek Burczynski: +** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608) +** The addressing patch used for early Neo-Geo games can be removed now. +** +** 26-11-2002 Jarek Burczynski, Nicola Salmoria: +** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to: +** - added emulation of YM2608 drums. +** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608) +** +** 16-08-2002 Jarek Burczynski: +** - binary exact Envelope Generator (verified on real YM2203); +** identical to YM2151 +** - corrected 'off by one' error in feedback calculations (when feedback is off) +** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610) +** +** 18-12-2001 Jarek Burczynski: +** - added SSG-EG support (verified on real YM2203) +** +** 12-08-2001 Jarek Burczynski: +** - corrected sin_tab and tl_tab data (verified on real chip) +** - corrected feedback calculations (verified on real chip) +** - corrected phase generator calculations (verified on real chip) +** - corrected envelope generator calculations (verified on real chip) +** - corrected FM volume level (YM2610 and YM2610B). +** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) : +** this was needed to calculate YM2610 FM channels output correctly. +** (Each FM channel is calculated as in other chips, but the output of the channel +** gets shifted right by one *before* sending to accumulator. That was impossible to do +** with previous implementation). +** +** 23-07-2001 Jarek Burczynski, Nicola Salmoria: +** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip) +** +** 11-06-2001 Jarek Burczynski: +** - corrected end of sample bug in ADPCMA_calc_cha(). +** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits). +** +** 08-12-98 hiro-shi: +** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA +** move ROM limit check.(CALC_CH? -> 2610Write1/2) +** test program (ADPCMB_TEST) +** move ADPCM A/B end check. +** ADPCMB repeat flag(no check) +** change ADPCM volume rate (8->16) (32->48). +** +** 09-12-98 hiro-shi: +** change ADPCM volume. (8->16, 48->64) +** replace ym2610 ch0/3 (YM-2610B) +** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff. +** add ADPCM_SHIFT_MASK +** change ADPCMA_DECODE_MIN/MAX. +*/ + +/************************************************************************/ +/* comment of hiro-shi(Hiromitsu Shioya) */ +/* YM2610(B) = OPN-B */ +/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */ +/************************************************************************/ + +#include +#include /* for memset */ +#include /* for NULL */ +#include +#include + +namespace Ym2612_MameImpl +{ + +/* ---- mamedef - begin ---- */ +/* typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h) */ +/* 8-bit values */ +typedef unsigned char UINT8; +typedef signed char INT8; + +/* 16-bit values */ +typedef unsigned short UINT16; +typedef signed short INT16; + +/* 32-bit values */ +#ifndef _WINDOWS_H +typedef unsigned int UINT32; +typedef signed int INT32; +#endif + +/* 64-bit values */ +#ifndef _WINDOWS_H +#ifdef _MSC_VER +typedef signed __int64 INT64; +typedef unsigned __int64 UINT64; +#else +__extension__ typedef unsigned long long UINT64; +__extension__ typedef signed long long INT64; +#endif +#endif + +/* offsets and addresses are 32-bit (for now...) */ +typedef UINT32 offs_t; + +/* stream_sample_t is used to represent a single sample in a sound stream */ +typedef INT16 stream_sample_t; + +#if defined(VGM_BIG_ENDIAN) +#define BYTE_XOR_BE(x) (x) +#elif defined(VGM_LITTLE_ENDIAN) +#define BYTE_XOR_BE(x) ((x) ^ 0x01) +#else +/* don't define BYTE_XOR_BE so that it throws an error when compiling */ +#endif + +#if defined(_MSC_VER) +//#define INLINE static __forceinline +#define INLINE static __inline +#elif defined(__GNUC__) +#define INLINE static __inline__ +#else +#define INLINE static inline +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifdef _DEBUG +#define logerror printf +#else +#define logerror +#endif + +typedef void (*SRATE_CALLBACK)(void*, UINT32); +/* ---- mamedef - end ---- */ + +/* --- select emulation chips --- */ +/* +#define BUILD_YM2203 (HAS_YM2203) // build YM2203(OPN) emulator +#define BUILD_YM2608 (HAS_YM2608) // build YM2608(OPNA) emulator +#define BUILD_YM2610 (HAS_YM2610) // build YM2610(OPNB) emulator +#define BUILD_YM2610B (HAS_YM2610B) // build YM2610B(OPNB?)emulator +#define BUILD_YM2612 (HAS_YM2612) // build YM2612(OPN2) emulator +#define BUILD_YM3438 (HAS_YM3438) // build YM3438(OPN) emulator +*/ +#define BUILD_YM2203 0 +#define BUILD_YM2608 0 +#define BUILD_YM2610 0 +#define BUILD_YM2610B 0 +#define BUILD_YM2612 1 +#define BUILD_YM3438 0 + +#define FM_BUSY_FLAG_SUPPORT 0 + +/* select bit size of output : 8 or 16 */ +#define FM_SAMPLE_BITS 16 + +/* select timer system internal or external */ +#define FM_INTERNAL_TIMER 1 + +/* --- speedup optimize --- */ +/* busy flag enulation , The definition of FM_GET_TIME_NOW() is necessary. */ +/* #define FM_BUSY_FLAG_SUPPORT 1 */ + +/* --- external SSG(YM2149/AY-3-8910)emulator interface port */ +/* used by YM2203,YM2608,and YM2610 */ +typedef struct _ssg_callbacks ssg_callbacks; +struct _ssg_callbacks +{ + void (*set_clock)(void *param, int clock); + void (*write)(void *param, int address, int data); + int (*read)(void *param); + void (*reset)(void *param); +}; + +/* --- external callback funstions for realtime update --- */ + +#if FM_BUSY_FLAG_SUPPORT +#define TIME_TYPE attotime +#define UNDEFINED_TIME attotime_zero +#define FM_GET_TIME_NOW(machine) timer_get_time(machine) +#define ADD_TIMES(t1, t2) attotime_add((t1), (t2)) +#define COMPARE_TIMES(t1, t2) attotime_compare((t1), (t2)) +#define MULTIPLY_TIME_BY_INT(t,i) attotime_mul(t, i) +#endif + +/* compiler dependence */ +#if 0 +#ifndef OSD_CPU_H +#define OSD_CPU_H +typedef unsigned char UINT8; /* unsigned 8bit */ +typedef unsigned short UINT16; /* unsigned 16bit */ +typedef unsigned int UINT32; /* unsigned 32bit */ +typedef signed char INT8; /* signed 8bit */ +typedef signed short INT16; /* signed 16bit */ +typedef signed int INT32; /* signed 32bit */ +#endif /* OSD_CPU_H */ +#endif + +typedef stream_sample_t FMSAMPLE; +/* +#if (FM_SAMPLE_BITS==16) +typedef INT16 FMSAMPLE; +#endif +#if (FM_SAMPLE_BITS==8) +typedef unsigned char FMSAMPLE; +#endif +*/ + +typedef void (*FM_TIMERHANDLER)(void *param,int c,int cnt,int clock); +typedef void (*FM_IRQHANDLER)(void *param,int irq); +/* FM_TIMERHANDLER : Stop or Start timer */ +/* int n = chip number */ +/* int c = Channel 0=TimerA,1=TimerB */ +/* int count = timer count (0=stop) */ +/* doube stepTime = step time of one count (sec.)*/ + +/* FM_IRQHHANDLER : IRQ level changing sense */ +/* int n = chip number */ +/* int irq = IRQ level 0=OFF,1=ON */ + +/** + * @brief Initialize chip and return the instance + * @param param Unused, keep NULL + * @param baseclock YM2612 clock + * @param rate Output sample rate + * @param TimerHandler Keep NULL + * @param IRQHandler Keep NULL + * @return Chip instance or NULL on any error + */ +static void * ym2612_init(void *param, int baseclock, int rate, + FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler); +/** + * @brief Free chip instance + * @param chip Chip instance + */ +static void ym2612_shutdown(void *chip); +/** + * @brief Reset state of the chip + * @param chip Chip instance + */ +static void ym2612_reset_chip(void *chip); +/** + * @brief Generate stereo output of specified length + * @param chip Chip instance + * @param buffer Output sound buffer + * @param frames Output buffer size in frames (one frame - two array entries of the buffer) + * @param mix 0 - override buffer data, 1 - mix output data with a content of the buffer + */ +static void ym2612_generate(void *chip, FMSAMPLE *buffer, int frames, int mix); +#define ym2612_update_one(chip, buffer, length) ym2612_generate(chip, buffer, length, 0) + +/** + * @brief Single-Sample generation prepare + * @param chip Chip instance + */ +static void ym2612_pre_generate(void *chip); +/** + * @brief Generate single stereo PCM frame. Will be used native sample rate of 53267 Hz + * @param chip Chip instance + * @param buffer One stereo PCM frame + */ +static void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[2]); + +/* void ym2612_post_generate(void *chip, int length); */ + +static int ym2612_write(void *chip, int a,unsigned char v); +#if 0 +static unsigned char ym2612_read(void *chip,int a); +static int ym2612_timer_over(void *chip, int c ); +#endif + +#ifdef __STATE_H__ +static void ym2612_postload(void *chip); +#endif + +static void ym2612_set_mutemask(void *chip, UINT32 MuteMask); +#if 0 +static void ym2612_setoptions(UINT8 Flags); +#endif + + +static stream_sample_t *DUMMYBUF = NULL; + +/* shared function building option */ +#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B||BUILD_YM2612||BUILD_YM3438) +#define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608) + +#define RSM_ENABLE 1 +#define RSM_FRAC 10 + +/* globals */ +#define TYPE_SSG 0x01 /* SSG support */ +#define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */ +#define TYPE_6CH 0x04 /* FM 6CH / 3CH */ +#define TYPE_DAC 0x08 /* YM2612's DAC device */ +#define TYPE_ADPCM 0x10 /* two ADPCM units */ +#define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */ + + +#define TYPE_YM2203 (TYPE_SSG) +#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM) +#define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610) +#define TYPE_YM2612 (TYPE_DAC |TYPE_LFOPAN |TYPE_6CH) + + +/* globals */ +#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */ +#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */ +#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */ +#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */ + +#define FREQ_MASK ((1<>3) + +/* sin waveform table in 'decibel' scale */ +static unsigned int sin_tab[SIN_LEN]; + +/* sustain level table (3dB per step) */ +/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */ +/* 1, 2, 4, 8, 16, 32, 64 (value)*/ +/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/ + +/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/ +/* attenuation value (10 bits) = (SL << 2) << 3 */ +#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) ) +static const UINT32 sl_table[16]={ + SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), + SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31) +}; +#undef SC + + +#define RATE_STEPS (8) +static const UINT8 eg_inc[19*RATE_STEPS]={ + +/*cycle:0 1 2 3 4 5 6 7*/ + +/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */ +/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */ +/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */ +/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */ + +/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */ +/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */ +/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */ +/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */ + +/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */ +/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */ +/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */ +/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */ + +/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */ +/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */ +/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */ +/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */ + +/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */ +/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */ +/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */ +}; + + +#define O(a) (a*RATE_STEPS) + +/*note that there is no O(17) in this table - it's directly in the code */ +static const UINT8 eg_rate_select2612[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates (same as Rate 0) */ +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), +O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), + +/* rates 00-11 */ +/* +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +*/ +O(18),O(18),O( 2),O( 3), /* from Nemesis's tests on real YM2612 hardware */ +O( 0),O( 1),O( 2),O( 2), /* Nemesis's tests */ + +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), +O( 0),O( 1),O( 2),O( 3), + +/* rate 12 */ +O( 4),O( 5),O( 6),O( 7), + +/* rate 13 */ +O( 8),O( 9),O(10),O(11), + +/* rate 14 */ +O(12),O(13),O(14),O(15), + +/* rate 15 */ +O(16),O(16),O(16),O(16), + +/* 32 dummy rates (same as 15 3) */ +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), +O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) + +}; +#undef O + +/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/ +/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */ +/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */ + +#define O(a) (a*1) +static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */ +/* 32 infinite time rates */ +/* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), +O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */ + +/* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */ +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), +O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11), + +/* rates 00-11 */ +O(11),O(11),O(11),O(11), +O(10),O(10),O(10),O(10), +O( 9),O( 9),O( 9),O( 9), +O( 8),O( 8),O( 8),O( 8), +O( 7),O( 7),O( 7),O( 7), +O( 6),O( 6),O( 6),O( 6), +O( 5),O( 5),O( 5),O( 5), +O( 4),O( 4),O( 4),O( 4), +O( 3),O( 3),O( 3),O( 3), +O( 2),O( 2),O( 2),O( 2), +O( 1),O( 1),O( 1),O( 1), +O( 0),O( 0),O( 0),O( 0), + +/* rate 12 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 13 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 14 */ +O( 0),O( 0),O( 0),O( 0), + +/* rate 15 */ +O( 0),O( 0),O( 0),O( 0), + +/* 32 dummy rates (same as 15 3) */ +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), +O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) + +}; +#undef O + +static const UINT8 dt_tab[4 * 32]={ +/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/ +/* FD=0 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +/* FD=1 */ + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, + 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, +/* FD=2 */ + 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, + 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, +/* FD=3 */ + 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, + 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 +}; + + +/* OPN key frequency number -> key code follow table */ +/* fnum higher 4bit -> keycode lower 2bit */ +static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3}; + + +/* 8 LFO speed parameters */ +/* each value represents number of samples that one LFO level will last for */ +static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5}; + + + +/*There are 4 different LFO AM depths available, they are: + 0 dB, 1.4 dB, 5.9 dB, 11.8 dB + Here is how it is generated (in EG steps): + + 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0 + 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0 + 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0 + + (1.4 dB is losing precision as you can see) + + It's implemented as generator from 0..126 with step 2 then a shift + right N times, where N is: + 8 for 0 dB + 3 for 1.4 dB + 1 for 5.9 dB + 0 for 11.8 dB +*/ +static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0}; + + + +/*There are 8 different LFO PM depths available, they are: + 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents) + + Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10 + (bits 8,9,10 = FNUM MSB from OCT/FNUM register) + + Here we store only first quarter (positive one) of full waveform. + Full table (lfo_pm_table) containing all 128 waveforms is build + at run (init) time. + + One value in table below represents 4 (four) basic LFO steps + (1 PM step = 4 AM steps). + + For example: + at LFO SPEED=0 (which is 108 samples per basic LFO step) + one value from "lfo_pm_output" table lasts for 432 consecutive + samples (4*108=432) and one full LFO waveform cycle lasts for 13824 + samples (32*432=13824; 32 because we store only a quarter of whole + waveform in the table below) +*/ +static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */ +/* FNUM BIT 4: 000 0001xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1}, + +/* FNUM BIT 5: 000 0010xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3}, + +/* FNUM BIT 6: 000 0100xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1}, +/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6}, + +/* FNUM BIT 7: 000 1000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1}, +/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2}, +/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3}, +/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, + +/* FNUM BIT 8: 001 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1}, +/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2}, +/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3}, +/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4}, +/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6}, +/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, + +/* FNUM BIT 9: 010 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2}, +/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4}, +/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6}, +/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8}, +/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc}, +/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, + +/* FNUM BIT10: 100 0000xxxx */ +/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0}, +/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4}, +/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8}, +/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc}, +/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10}, +/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18}, +/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30}, +/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60}, + +}; + +/* all 128 LFO PM waveforms */ +static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */ + +/* register number to channel number , slot offset */ +#define OPN_CHAN(N) (N&3) +#define OPN_SLOT(N) ((N>>2)&3) + +/* slot number */ +#define SLOT1 0 +#define SLOT2 2 +#define SLOT3 1 +#define SLOT4 3 + +/* bit0 = Right enable , bit1 = Left enable */ +#define OUTD_RIGHT 1 +#define OUTD_LEFT 2 +#define OUTD_CENTER 3 + + +/* save output as raw 16-bit sample */ +/* #define SAVE_SAMPLE */ + +#ifdef SAVE_SAMPLE +static FILE *sample[1]; + #if 1 /*save to MONO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #else /*save to STEREO file */ + #define SAVE_ALL_CHANNELS \ + { signed int pom = lt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + pom = rt; \ + fputc((unsigned short)pom&0xff,sample[0]); \ + fputc(((unsigned short)pom>>8)&0xff,sample[0]); \ + } + #endif +#endif + + +/* struct describing a single operator (SLOT) */ +typedef struct +{ + INT32 *DT; /* detune :dt_tab[DT] */ + UINT8 KSR; /* key scale rate :3-KSR */ + UINT32 ar; /* attack rate */ + UINT32 d1r; /* decay rate */ + UINT32 d2r; /* sustain rate */ + UINT32 rr; /* release rate */ + UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */ + UINT32 mul; /* multiple :ML_TABLE[ML] */ + + /* Phase Generator */ + UINT32 phase; /* phase counter */ + INT32 Incr; /* phase step */ + + /* Envelope Generator */ + UINT8 state; /* phase type */ + UINT32 tl; /* total level: TL << 3 */ + INT32 volume; /* envelope counter */ + UINT32 sl; /* sustain level:sl_table[SL] */ + UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */ + + UINT8 eg_sh_ar; /* (attack state) */ + UINT8 eg_sel_ar; /* (attack state) */ + UINT8 eg_sh_d1r; /* (decay state) */ + UINT8 eg_sel_d1r; /* (decay state) */ + UINT8 eg_sh_d2r; /* (sustain state) */ + UINT8 eg_sel_d2r; /* (sustain state) */ + UINT8 eg_sh_rr; /* (release state) */ + UINT8 eg_sel_rr; /* (release state) */ + + UINT8 ssg; /* SSG-EG waveform */ + UINT8 ssgn; /* SSG-EG negated output */ + + UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */ + + /* LFO */ + UINT32 AMmask; /* AM enable flag */ + +} FM_SLOT; + +typedef struct +{ + FM_SLOT SLOT[4]; /* four SLOTs (operators) */ + + UINT8 ALGO; /* algorithm */ + UINT8 FB; /* feedback shift */ + INT32 op1_out[2]; /* op1 output for feedback */ + + INT32 *connect1; /* SLOT1 output pointer */ + INT32 *connect3; /* SLOT3 output pointer */ + INT32 *connect2; /* SLOT2 output pointer */ + INT32 *connect4; /* SLOT4 output pointer */ + + INT32 *mem_connect;/* where to put the delayed sample (MEM) */ + INT32 mem_value; /* delayed sample (MEM) value */ + + INT32 pms; /* channel PMS */ + UINT8 ams; /* channel AMS */ + + UINT32 fc; /* fnum,blk:adjusted to sample rate */ + UINT8 kcode; /* key code: */ + UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + UINT8 Muted; +} FM_CH; + + +typedef struct +{ + /* running_device *device; */ + void * param; /* this chip parameter */ + double freqbase; /* frequency base */ + int timer_prescaler; /* timer prescaler */ + UINT8 irq; /* interrupt level */ + UINT8 irqmask; /* irq mask */ +#if FM_BUSY_FLAG_SUPPORT + TIME_TYPE busy_expiry_time; /* expiry time of the busy status */ +#endif + UINT32 clock; /* master clock (Hz) */ + UINT32 rate; /* internal sampling rate (Hz) */ +#if RSM_ENABLE + INT32 rateratio; /* resampling ratio */ + INT32 framecnt; /* resampling frames count*/ + FMSAMPLE cur_sample[2]; /* previous sample */ + FMSAMPLE prev_sample[2]; /* previous sample */ +#endif + UINT8 address; /* address register */ + UINT8 status; /* status flag */ + UINT32 mode; /* mode CSM / 3SLOT */ + UINT8 fn_h; /* freq latch */ + UINT8 prescaler_sel; /* prescaler selector */ + INT32 TA; /* timer a */ + INT32 TAC; /* timer a counter */ + UINT8 TB; /* timer b */ + INT32 TBC; /* timer b counter */ + /* local time tables */ + INT32 dt_tab[8][32]; /* DeTune table */ + /* Extention Timer and IRQ handler */ + FM_TIMERHANDLER timer_handler; + FM_IRQHANDLER IRQ_Handler; + const ssg_callbacks *SSG; +} FM_ST; + + + +/***********************************************************/ +/* OPN unit */ +/***********************************************************/ + +/* OPN 3slot struct */ +typedef struct +{ + UINT32 fc[3]; /* fnum3,blk3: calculated */ + UINT8 fn_h; /* freq3 latch */ + UINT8 kcode[3]; /* key code */ + UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */ + UINT8 key_csm; /* CSM mode Key-ON flag */ +} FM_3SLOT; + +/* OPN/A/B common state */ +typedef struct +{ + UINT8 type; /* chip type */ + FM_ST ST; /* general state */ + FM_3SLOT SL3; /* 3 slot mode state */ + FM_CH *P_CH; /* pointer of CH */ + unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */ + + UINT32 eg_cnt; /* global envelope generator counter */ + UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */ + UINT32 eg_timer_add; /* step of eg_timer */ + UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */ + + + /* there are 2048 FNUMs that can be generated using FNUM/BLK registers + but LFO works with one more bit of a precision so we really need 4096 elements */ + UINT32 fn_table[4096]; /* fnumber->increment counter */ + UINT32 fn_max; /* maximal phase increment (used for phase overflow) */ + + /* LFO */ + UINT8 lfo_cnt; /* current LFO phase (out of 128) */ + UINT32 lfo_timer; /* current LFO phase runs at LFO frequency */ + UINT32 lfo_timer_add; /* step of lfo_timer */ + UINT32 lfo_timer_overflow; /* LFO timer overflows every N samples (depends on LFO frequency) */ + UINT32 LFO_AM; /* current LFO AM step */ + UINT32 LFO_PM; /* current LFO PM step */ + + INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */ + INT32 mem; /* one sample delay memory */ + INT32 out_fm[6]; /* outputs of working channels */ + +} FM_OPN; + +/* here's the virtual YM2612 */ +typedef struct +{ + UINT8 REGS[512]; /* registers */ + FM_OPN OPN; /* OPN state */ + FM_CH CH[6]; /* channel state */ + UINT8 addr_A1; /* address line A1 */ + + /* dac output (YM2612) */ + /* int dacen; */ + UINT8 dacen; + UINT8 dac_test; + INT32 dacout; + UINT8 MuteDAC; + + UINT8 WaveOutMode; + INT32 WaveL; + INT32 WaveR; +} YM2612; + +/* log output level */ +#define LOG_ERR 3 /* ERROR */ +#define LOG_WAR 2 /* WARNING */ +#define LOG_INF 1 /* INFORMATION */ +#define LOG_LEVEL LOG_INF + +#ifndef __RAINE__ +#define LOG(n,x) do { if( (n)>=LOG_LEVEL ) logerror x; } while (0) +#endif + +/* limitter */ +#define Limit(val, max,min) { \ + if ( val > max ) val = max; \ + else if ( val < min ) val = min; \ +} + +#if 0 +#define USE_VGM_INIT_SWITCH +static UINT8 IsVGMInit = 0; +#endif +static UINT8 PseudoSt = 0x00; +/*#include +static FILE* hFile; +static UINT32 FileSample;*/ + +/* status set and IRQ handling */ +INLINE void FM_STATUS_SET(FM_ST *ST,int flag) +{ + /* set status flag */ + ST->status |= flag; + if ( !(ST->irq) && (ST->status & ST->irqmask) ) + { + ST->irq = 1; + /* callback user interrupt handler (IRQ is OFF to ON) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,1); + } +} + +/* status reset and IRQ handling */ +INLINE void FM_STATUS_RESET(FM_ST *ST,int flag) +{ + /* reset status flag */ + ST->status &=~flag; + if ( (ST->irq) && !(ST->status & ST->irqmask) ) + { + ST->irq = 0; + /* callback user interrupt handler (IRQ is ON to OFF) */ + if(ST->IRQ_Handler) (ST->IRQ_Handler)(ST->param,0); + } +} + +/* IRQ mask set */ +INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag) +{ + ST->irqmask = flag; + /* IRQ handling check */ + FM_STATUS_SET(ST,0); + FM_STATUS_RESET(ST,0); +} + +INLINE void FM_KEYON(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + /* Note by Valley Bell: + I assume that the CSM mode shouldn't affect channels + other than FM3, so I added a check for it here.*/ + if( !SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + SLOT->key = 1; +} + +INLINE void FM_KEYOFF(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if (SLOT->key && (!OPN->SL3.key_csm || CH == &OPN->P_CH[3])) + { +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) /* workaround for VGMs trimmed with VGMTool */ + { + SLOT->state = EG_OFF; + SLOT->volume = MAX_ATT_INDEX; + SLOT->vol_out= MAX_ATT_INDEX; + } + else +#endif + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } + + SLOT->key = 0; +} + +INLINE void FM_KEYON_CSM(FM_OPN *OPN, FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + + if( !SLOT->key && !OPN->SL3.key_csm) + { + /* restart Phase Generator */ + SLOT->phase = 0; + + /* reset SSG-EG inversion flag */ + SLOT->ssgn = 0; + + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* force attenuation level to 0 */ + SLOT->volume = MIN_ATT_INDEX; + + /* directly switch to Decay (or Sustain) */ + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } +} + +INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s ) +{ + FM_SLOT *SLOT = &CH->SLOT[s]; + if (!SLOT->key) + { +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + { + SLOT->state = EG_OFF; + SLOT->volume = MAX_ATT_INDEX; + SLOT->vol_out= MAX_ATT_INDEX; + } + else +#endif + if (SLOT->state>EG_REL) + { + SLOT->state = EG_REL; /* phase -> Release */ + + /* SSG-EG specific update */ + if (SLOT->ssg&0x08) + { + /* convert EG attenuation level */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->volume = (0x200 - SLOT->volume); + + /* force EG attenuation level */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + } +} + +/* OPN Mode Register Write */ +INLINE void set_timers( FM_OPN *OPN, FM_ST *ST, void *n, int v ) +{ + /* b7 = CSM MODE */ + /* b6 = 3 slot mode */ + /* b5 = reset b */ + /* b4 = reset a */ + /* b3 = timer enable b */ + /* b2 = timer enable a */ + /* b1 = load b */ + /* b0 = load a */ + + if ((OPN->ST.mode ^ v) & 0xC0) + { + /* phase increment need to be recalculated */ + OPN->P_CH[2].SLOT[SLOT1].Incr=-1; + + /* CSM mode disabled and CSM key ON active*/ + if (((v & 0xC0) != 0x80) && OPN->SL3.key_csm) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT1); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT2); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT3); + FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT4); + OPN->SL3.key_csm = 0; + } + } + + /* reset Timer b flag */ + if( v & 0x20 ) + FM_STATUS_RESET(ST,0x02); + /* reset Timer a flag */ + if( v & 0x10 ) + FM_STATUS_RESET(ST,0x01); + /* load b */ + if ((v&2) && !(ST->mode&2)) + { + ST->TBC = ( 256-ST->TB)<<4; + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,1,ST->TBC * ST->timer_prescaler,(int)ST->clock); + } + /* load a */ + if ((v&1) && !(ST->mode&1)) + { + ST->TAC = (1024-ST->TA); + /* External timer handler */ + if (ST->timer_handler) (ST->timer_handler)(n,0,ST->TAC * ST->timer_prescaler,(int)ST->clock); + ST->TAC *= 4096; + } + + ST->mode = (UINT32)v; +} + + +/* Timer A Overflow */ +INLINE void TimerAOver(FM_ST *ST) +{ + /* set status (if enabled) */ + if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01); + /* clear or reload the counter */ + ST->TAC = (1024-ST->TA); + if (ST->timer_handler) (ST->timer_handler)(ST->param,0,ST->TAC * ST->timer_prescaler,(int)ST->clock); + ST->TAC *= 4096; +} +/* Timer B Overflow */ +INLINE void TimerBOver(FM_ST *ST) +{ + /* set status (if enabled) */ + if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02); + /* clear or reload the counter */ + ST->TBC = ( 256-ST->TB)<<4; + if (ST->timer_handler) (ST->timer_handler)(ST->param,1,ST->TBC * ST->timer_prescaler,(int)ST->clock); +} + + +#if FM_INTERNAL_TIMER +/* ----- internal timer mode , update timer */ +/* Valley Bell: defines fixed */ + +/* ---------- calculate timer A ---------- */ + #define INTERNAL_TIMER_A(ST,CSM_CH) \ + { \ + if( (ST)->TAC && ((ST)->timer_handler==0) ) \ + if( ((ST)->TAC -= (int)((ST)->freqbase*4096)) <= 0 ) \ + { \ + TimerAOver( ST ); \ + /* CSM mode total level latch and auto key on */ \ + if( (ST)->mode & 0x80 ) \ + CSMKeyControll( OPN, CSM_CH ); \ + } \ + } +/* ---------- calculate timer B ---------- */ + #define INTERNAL_TIMER_B(ST,step) \ + { \ + if( (ST)->TBC && ((ST)->timer_handler==0) ) \ + if( ((ST)->TBC -= (int)((ST)->freqbase*4096*step)) <= 0 ) \ + TimerBOver( ST ); \ + } +#else /* FM_INTERNAL_TIMER */ +/* external timer mode */ +#define INTERNAL_TIMER_A(ST,CSM_CH) +#define INTERNAL_TIMER_B(ST,step) +#endif /* FM_INTERNAL_TIMER */ + + + +#if FM_BUSY_FLAG_SUPPORT +#define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME) +INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST) +{ + if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 ) + { + if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(ST->device->machine)) > 0) + return ST->status | 0x80; /* with busy */ + /* expire */ + FM_BUSY_CLEAR(ST); + } + return ST->status; +} +INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock ) +{ + TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(ATTOTIME_IN_HZ(ST->clock), busyclock * ST->timer_prescaler); + ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(ST->device->machine), expiry_period); +} +#else +#define FM_STATUS_FLAG(ST) ((ST)->status) +#define FM_BUSY_SET(ST,bclock) {} +#define FM_BUSY_CLEAR(ST) {} +#endif + + +/* set algorithm connection */ +INLINE void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch ) +{ + INT32 *carrier = &OPN->out_fm[ch]; + + INT32 **om1 = &CH->connect1; + INT32 **om2 = &CH->connect3; + INT32 **oc1 = &CH->connect2; + + INT32 **memc = &CH->mem_connect; + + switch( CH->ALGO ) + { + case 0: + /* M1---C1---MEM---M2---C2---OUT */ + *om1 = &OPN->c1; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 1: + /* M1------+-MEM---M2---C2---OUT */ + /* C1-+ */ + *om1 = &OPN->mem; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 2: + /* M1-----------------+-C2---OUT */ + /* C1---MEM---M2-+ */ + *om1 = &OPN->c2; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->m2; + break; + case 3: + /* M1---C1---MEM------+-C2---OUT */ + /* M2-+ */ + *om1 = &OPN->c1; + *oc1 = &OPN->mem; + *om2 = &OPN->c2; + *memc= &OPN->c2; + break; + case 4: + /* M1---C1-+-OUT */ + /* M2---C2-+ */ + /* MEM: not used */ + *om1 = &OPN->c1; + *oc1 = carrier; + *om2 = &OPN->c2; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + case 5: + /* +----C1----+ */ + /* M1-+-MEM---M2-+-OUT */ + /* +----C2----+ */ + *om1 = 0; /* special mark */ + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->m2; + break; + case 6: + /* M1---C1-+ */ + /* M2-+-OUT */ + /* C2-+ */ + /* MEM: not used */ + *om1 = &OPN->c1; + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + case 7: + /* M1-+ */ + /* C1-+-OUT */ + /* M2-+ */ + /* C2-+ */ + /* MEM: not used*/ + *om1 = carrier; + *oc1 = carrier; + *om2 = carrier; + *memc= &OPN->mem; /* store it anywhere where it will not be used */ + break; + } + + CH->connect4 = carrier; +} + +/* set detune & multiple */ +INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v) +{ + SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1; + SLOT->DT = ST->dt_tab[(v>>4)&7]; + CH->SLOT[SLOT1].Incr=-1; +} + +/* set total level */ +INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v) +{ + SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */ + (void)CH; + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; +} + +/* set attack rate & key scale */ +INLINE void set_ar_ksr(UINT8 type, FM_CH *CH,FM_SLOT *SLOT,int v) +{ + UINT8 old_KSR = SLOT->KSR; + (void)type; + + SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->KSR = 3-(v>>6); + if (SLOT->KSR != old_KSR) + { + CH->SLOT[SLOT1].Incr=-1; + } + + /* Even if it seems unnecessary, in some odd case, KSR and KC are both modified */ + /* and could result in SLOT->kc remaining unchanged. */ + /* In such case, AR values would not be recalculated despite SLOT->ar has changed */ + /* This fixes the introduction music of Batman & Robin (Eke-Eke) */ + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware */ + } +} + +/* set decay rate */ +INLINE void set_dr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr]; +} + +/* set sustain rate */ +INLINE void set_sr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0; + + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr]; +} + +/* set release rate */ +INLINE void set_sl_rr(UINT8 type, FM_SLOT *SLOT,int v) +{ + (void)type; + SLOT->sl = sl_table[ v>>4 ]; + + /* check EG state changes */ + if ((SLOT->state == EG_DEC) && (SLOT->volume >= (INT32)(SLOT->sl))) + SLOT->state = EG_SUS; + + SLOT->rr = 34 + ((v&0x0f)<<2); + + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr]; +} + +/* advance LFO to next sample */ +INLINE void advance_lfo(FM_OPN *OPN) +{ + if (OPN->lfo_timer_overflow) /* LFO enabled ? */ + { + /* increment LFO timer */ + OPN->lfo_timer += OPN->lfo_timer_add; + + /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */ + while (OPN->lfo_timer >= OPN->lfo_timer_overflow) + { + OPN->lfo_timer -= OPN->lfo_timer_overflow; + + /* There are 128 LFO steps */ + OPN->lfo_cnt = ( OPN->lfo_cnt + 1 ) & 127; + + /* Valley Bell: Replaced old code (non-inverted triangle) with + the one from Genesis Plus GX 1.71. */ + /* triangle (inverted) */ + /* AM: from 126 to 0 step -2, 0 to 126 step +2 */ + if (OPN->lfo_cnt<64) + OPN->LFO_AM = (UINT32)(OPN->lfo_cnt ^ 63) << 1; + else + OPN->LFO_AM = (UINT32)(OPN->lfo_cnt & 63) << 1; + + /* PM works with 4 times slower clock */ + OPN->LFO_PM = OPN->lfo_cnt >> 2; + } + } +} + +INLINE void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT) +{ + /* unsigned int out; */ + unsigned int i = 4; /* four operators per channel */ + + do + { + switch(SLOT->state) + { + case EG_ATT: /* attack phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_ar)-1))) + { + /* update attenuation level */ + SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4; + + /* check phase transition*/ + if (SLOT->volume <= MIN_ATT_INDEX) + { + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */ + } + + /* recalculate EG output */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + break; + + case EG_DEC: /* decay phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_d1r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)]; + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* check phase transition*/ + if (SLOT->volume >= (INT32)(SLOT->sl)) + SLOT->state = EG_SUS; + } + break; + + case EG_SUS: /* sustain phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_d2r)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + { + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */ + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)]; + + /* check phase transition*/ + if ( SLOT->volume >= MAX_ATT_INDEX ) + SLOT->volume = MAX_ATT_INDEX; + /* do not change SLOT->state (verified on real chip) */ + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + } + break; + + case EG_REL: /* release phase */ + if (!(OPN->eg_cnt & ((1<eg_sh_rr)-1))) + { + /* SSG EG type */ + if (SLOT->ssg&0x08) + { + /* update attenuation level */ + if (SLOT->volume < 0x200) + SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; + /* check phase transition */ + if (SLOT->volume >= 0x200) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + else + { + /* update attenuation level */ + SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)]; + + /* check phase transition*/ + if (SLOT->volume >= MAX_ATT_INDEX) + { + SLOT->volume = MAX_ATT_INDEX; + SLOT->state = EG_OFF; + } + } + + /* recalculate EG output */ + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + + } + break; + } + + /* Valley Bell: These few lines are missing in Genesis Plus GX' ym2612 core file. + Disabling them fixes the SSG-EG. + Additional Note: Asterix and the Great Rescue: Level 1 sounds "better" with these lines, + but less accurate. */ + #if 0 + out = ((UINT32)SLOT->volume); + + /* negate output (changes come from alternate bit, init comes from attack bit) */ + if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL)) + out ^= MAX_ATT_INDEX; + + /* we need to store the result here because we are going to change ssgn + in next instruction */ + SLOT->vol_out = out + SLOT->tl; + #endif + + SLOT++; + i--; + } while (i); + +} + +/* SSG-EG update process */ +/* The behavior is based upon Nemesis tests on real hardware */ +/* This is actually executed before each samples */ +INLINE void update_ssg_eg_channel(FM_SLOT *SLOT) +{ + unsigned int i = 4; /* four operators per channel */ + + do + { + /* detect SSG-EG transition */ + /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */ + /* if an Attack Phase is programmed, inversion can occur on each sample */ + if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL)) + { + if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */ + { + /* set inversion flag */ + if (SLOT->ssg & 0x02) + SLOT->ssgn = 4; + + /* force attenuation level during decay phases */ + if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04))) + SLOT->volume = MAX_ATT_INDEX; + } + else /* loop SSG-EG */ + { + /* toggle output inversion flag or reset Phase Generator */ + if (SLOT->ssg & 0x02) + SLOT->ssgn ^= 4; + else + SLOT->phase = 0; + + /* same as Key ON */ + if (SLOT->state != EG_ATT) + { + if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/) + { + SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT; + } + else + { + /* Attack Rate is maximal: directly switch to Decay or Substain */ + SLOT->volume = MIN_ATT_INDEX; + SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; + } + } + } + + /* recalculate EG output */ + if (SLOT->ssgn ^ (SLOT->ssg&0x04)) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* next slot */ + SLOT++; + i--; + } while (i); +} + + +INLINE void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum) +{ + UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; + INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ]; + + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + UINT8 blk = (block_fnum&0x7000) >> 12; + UINT32 fn = block_fnum & 0xfff; + + /* recalculate keyscale code */ + /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/ + /* This really stupid bug caused a read outside of the + array [size 0x10] and returned invalid values. + This caused an annoying vibrato for some notes. + (Note: seems to be a copy-and-paste from OPNWriteReg -> case 0xA0) + Why are MAME cores always SOO buggy ?! */ + /* Oh, and before I forget: it's correct in fm.c */ + int kc = (blk<<2) | opn_fktable[fn >> 8]; + /* Thanks to Blargg - his patch that helped me to find this bug */ + + /* recalculate (frequency) phase increment counter */ + int fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc]; + + /* (frequency) phase overflow (credits to Nemesis) */ + if (fc < 0) fc += OPN->fn_max; + + /* update phase */ + SLOT->phase += (fc * SLOT->mul) >> 1; + } + else /* LFO phase modulation = zero */ + { + SLOT->phase += SLOT->Incr; + } +} + +INLINE void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH) +{ + UINT32 block_fnum = CH->block_fnum; + + UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8; + INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ]; + + block_fnum = block_fnum*2 + lfo_fn_table_index_offset; + + if (lfo_fn_table_index_offset) /* LFO phase modulation active */ + { + UINT8 blk = (block_fnum&0x7000) >> 12; + UINT32 fn = block_fnum & 0xfff; + + /* recalculate keyscale code */ + /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/ + /* the same stupid bug as above */ + int kc = (blk<<2) | opn_fktable[fn >> 8]; + + /* recalculate (frequency) phase increment counter */ + int fc = (OPN->fn_table[fn]>>(7-blk)); + + /* (frequency) phase overflow (credits to Nemesis) */ + int finc = fc + CH->SLOT[SLOT1].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1; + + finc = fc + CH->SLOT[SLOT2].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1; + + finc = fc + CH->SLOT[SLOT3].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1; + + finc = fc + CH->SLOT[SLOT4].DT[kc]; + if (finc < 0) finc += OPN->fn_max; + CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1; + } + else /* LFO phase modulation = zero */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +/* update phase increment and envelope generator */ +INLINE void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc ) +{ + int ksr = kc >> SLOT->KSR; + + fc += SLOT->DT[kc]; + + /* detects frequency overflow (credits to Nemesis) */ + if (fc < 0) fc += OPN->fn_max; + + /* (frequency) phase increment counter */ + SLOT->Incr = (fc * SLOT->mul) >> 1; + + if( SLOT->ksr != ksr ) + { + SLOT->ksr = ksr; + + /* calculate envelope generator rates */ + if ((SLOT->ar + SLOT->ksr) < 32+62) + { + SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ]; + SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ]; + } + else + { + SLOT->eg_sh_ar = 0; + SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware (Attack phase is blocked) */ + } + + SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr]; + SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr]; + SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr]; + + SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr]; + SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr]; + SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr]; + } +} + +/* update phase increment counters */ +INLINE void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH ) +{ + if( CH->SLOT[SLOT1].Incr==-1) + { + int fc = CH->fc; + int kc = CH->kcode; + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc ); + refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc ); + } +} + +#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask)) + +INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm) +{ + UINT32 p; + + p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ]; + + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm) +{ + UINT32 p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ]; + if (p >= TL_TAB_LEN) + return 0; + return tl_tab[p]; +} + +INLINE void chan_calc(YM2612 *F2612, FM_OPN *OPN, FM_CH *CH) +{ + UINT32 AM = OPN->LFO_AM >> CH->ams; + unsigned int eg_out; + + if (CH->Muted) + return; + + OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0; + + *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */ + + eg_out = volume_calc(&CH->SLOT[SLOT1]); + { + INT32 out = CH->op1_out[0] + CH->op1_out[1]; + CH->op1_out[0] = CH->op1_out[1]; + + if( !CH->connect1 ) + { + /* algorithm 5 */ + OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0]; + } + else + { + /* other algorithms */ + *CH->connect1 += CH->op1_out[0]; + } + + + CH->op1_out[1] = 0; + if( eg_out < ENV_QUIET ) /* SLOT 1 */ + { + if (!CH->FB) + out=0; + + CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<FB) ); + } + } + + eg_out = volume_calc(&CH->SLOT[SLOT3]); + if( eg_out < ENV_QUIET ) /* SLOT 3 */ + *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2); + + eg_out = volume_calc(&CH->SLOT[SLOT2]); + if( eg_out < ENV_QUIET ) /* SLOT 2 */ + *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1); + + eg_out = volume_calc(&CH->SLOT[SLOT4]); + if( eg_out < ENV_QUIET ) /* SLOT 4 */ + *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2); + + + /* store current MEM */ + CH->mem_value = OPN->mem; + + /* update phase counters AFTER output calculations */ + if(CH->pms) + { + /* add support for 3 slot mode */ + if ((OPN->ST.mode & 0xC0) && (CH == &F2612->CH[2])) + { + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]); + update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum); + } + else update_phase_lfo_channel(OPN, CH); + } + else /* no LFO phase modulation */ + { + CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr; + CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr; + CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr; + CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr; + } +} + +static void FMCloseTable( void ) +{ +#ifdef SAVE_SAMPLE + fclose(sample[0]); +#endif + return; +} + + +/* CSM Key Controll */ +INLINE void CSMKeyControll(FM_OPN *OPN, FM_CH *CH) +{ + /* all key ON (verified by Nemesis on real hardware) */ + FM_KEYON_CSM(OPN,CH,SLOT1); + FM_KEYON_CSM(OPN,CH,SLOT2); + FM_KEYON_CSM(OPN,CH,SLOT3); + FM_KEYON_CSM(OPN,CH,SLOT4); + OPN->SL3.key_csm = 1; +} + +#ifdef __STATE_H__ +/* FM channel save , internal state only */ +static void FMsave_state_channel(running_device *device,FM_CH *CH,int num_ch) +{ + int slot , ch; + + for(ch=0;chop1_out); + state_save_register_device_item(device, ch, CH->fc); + /* slots */ + for(slot=0;slot<4;slot++) + { + FM_SLOT *SLOT = &CH->SLOT[slot]; + state_save_register_device_item(device, ch * 4 + slot, SLOT->phase); + state_save_register_device_item(device, ch * 4 + slot, SLOT->state); + state_save_register_device_item(device, ch * 4 + slot, SLOT->volume); + } + } +} + +static void FMsave_state_st(running_device *device,FM_ST *ST) +{ +#if FM_BUSY_FLAG_SUPPORT + state_save_register_device_item(device, 0, ST->busy_expiry_time.seconds ); + state_save_register_device_item(device, 0, ST->busy_expiry_time.attoseconds ); +#endif + state_save_register_device_item(device, 0, ST->address ); + state_save_register_device_item(device, 0, ST->irq ); + state_save_register_device_item(device, 0, ST->irqmask ); + state_save_register_device_item(device, 0, ST->status ); + state_save_register_device_item(device, 0, ST->mode ); + state_save_register_device_item(device, 0, ST->prescaler_sel ); + state_save_register_device_item(device, 0, ST->fn_h ); + state_save_register_device_item(device, 0, ST->TA ); + state_save_register_device_item(device, 0, ST->TAC ); + state_save_register_device_item(device, 0, ST->TB ); + state_save_register_device_item(device, 0, ST->TBC ); +} +#endif /* _STATE_H */ + +#if BUILD_OPN +/* write a OPN mode register 0x20-0x2f */ +static void OPNWriteMode(FM_OPN *OPN, int r, int v) +{ + UINT8 c; + FM_CH *CH; + + switch(r) + { + case 0x21: /* Test */ + break; + case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */ + if (v&8) /* LFO enabled ? */ + { + #if 0 + if (!OPN->lfo_timer_overflow) + { + /* restart LFO */ + OPN->lfo_cnt = 0; + OPN->lfo_timer = 0; + OPN->LFO_AM = 0; + OPN->LFO_PM = 0; + } + #endif + + OPN->lfo_timer_overflow = lfo_samples_per_step[v&7] << LFO_SH; + } + else + { + /* Valley Bell: Ported from Genesis Plus GX 1.71 + hold LFO waveform in reset state */ + OPN->lfo_timer_overflow = 0; + OPN->lfo_timer = 0; + OPN->lfo_cnt = 0; + + + OPN->LFO_PM = 0; + OPN->LFO_AM = 126; + /* OPN->lfo_timer_overflow = 0; */ + } + break; + case 0x24: /* timer A High 8*/ + OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2); + break; + case 0x25: /* timer A Low 2*/ + OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3); + break; + case 0x26: /* timer B */ + OPN->ST.TB = (UINT8)v; + break; + case 0x27: /* mode, timer control */ + set_timers( OPN, &(OPN->ST),OPN->ST.param,v ); + break; + case 0x28: /* key on / off */ + c = v & 0x03; + if( c == 3 ) break; + if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3; + CH = OPN->P_CH; + CH = &CH[c]; + if(v&0x10) FM_KEYON(OPN,CH,SLOT1); else FM_KEYOFF(OPN,CH,SLOT1); + if(v&0x20) FM_KEYON(OPN,CH,SLOT2); else FM_KEYOFF(OPN,CH,SLOT2); + if(v&0x40) FM_KEYON(OPN,CH,SLOT3); else FM_KEYOFF(OPN,CH,SLOT3); + if(v&0x80) FM_KEYON(OPN,CH,SLOT4); else FM_KEYOFF(OPN,CH,SLOT4); + break; + } +} + +/* write a OPN register (0x30-0xff) */ +static void OPNWriteReg(FM_OPN *OPN, int r, int v) +{ + FM_CH *CH; + FM_SLOT *SLOT; + + UINT8 c = OPN_CHAN(r); + + if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */ + + if (r >= 0x100) c+=3; + + CH = OPN->P_CH; + CH = &CH[c]; + + SLOT = &(CH->SLOT[OPN_SLOT(r)]); + + switch( r & 0xf0 ) { + case 0x30: /* DET , MUL */ + set_det_mul(&OPN->ST,CH,SLOT,v); + break; + + case 0x40: /* TL */ + set_tl(CH,SLOT,v); + break; + + case 0x50: /* KS, AR */ + set_ar_ksr(OPN->type,CH,SLOT,v); + break; + + case 0x60: /* bit7 = AM ENABLE, DR */ + set_dr(OPN->type, SLOT,v); + + if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */ + { + SLOT->AMmask = (v&0x80) ? ~0 : 0; + } + break; + + case 0x70: /* SR */ + set_sr(OPN->type,SLOT,v); + break; + + case 0x80: /* SL, RR */ + set_sl_rr(OPN->type,SLOT,v); + break; + + case 0x90: /* SSG-EG */ + SLOT->ssg = v&0x0f; + + /* recalculate EG output */ + if (SLOT->state > EG_REL) + { + if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) + SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl; + else + SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl; + } + + /* SSG-EG envelope shapes : + + E AtAlH + 1 0 0 0 \\\\ + + 1 0 0 1 \___ + + 1 0 1 0 \/\/ + ___ + 1 0 1 1 \ + + 1 1 0 0 //// + ___ + 1 1 0 1 / + + 1 1 1 0 /\/\ + + 1 1 1 1 /___ + + + E = SSG-EG enable + + + The shapes are generated using Attack, Decay and Sustain phases. + + Each single character in the diagrams above represents this whole + sequence: + + - when KEY-ON = 1, normal Attack phase is generated (*without* any + difference when compared to normal mode), + + - later, when envelope level reaches minimum level (max volume), + the EG switches to Decay phase (which works with bigger steps + when compared to normal mode - see below), + + - later when envelope level passes the SL level, + the EG swithes to Sustain phase (which works with bigger steps + when compared to normal mode - see below), + + - finally when envelope level reaches maximum level (min volume), + the EG switches to Attack phase again (depends on actual waveform). + + Important is that when switch to Attack phase occurs, the phase counter + of that operator will be zeroed-out (as in normal KEY-ON) but not always. + (I havent found the rule for that - perhaps only when the output level is low) + + The difference (when compared to normal Envelope Generator mode) is + that the resolution in Decay and Sustain phases is 4 times lower; + this results in only 256 steps instead of normal 1024. + In other words: + when SSG-EG is disabled, the step inside of the EG is one, + when SSG-EG is enabled, the step is four (in Decay and Sustain phases). + + Times between the level changes are the same in both modes. + + + Important: + Decay 1 Level (so called SL) is compared to actual SSG-EG output, so + it is the same in both SSG and no-SSG modes, with this exception: + + when the SSG-EG is enabled and is generating raising levels + (when the EG output is inverted) the SL will be found at wrong level !!! + For example, when SL=02: + 0 -6 = -6dB in non-inverted EG output + 96-6 = -90dB in inverted EG output + Which means that EG compares its level to SL as usual, and that the + output is simply inverted afterall. + + + The Yamaha's manuals say that AR should be set to 0x1f (max speed). + That is not necessary, but then EG will be generating Attack phase. + + */ + + + break; + + case 0xa0: + switch( OPN_SLOT(r) ) + { + case 0: /* 0xa0-0xa2 : FNUM1 */ +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->ST.fn_h = CH->block_fnum >> 8; +#endif + { + UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v; + UINT8 blk = OPN->ST.fn_h>>3; + /* keyscale code */ + CH->kcode = (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + CH->fc = OPN->fn_table[fn*2]>>(7-blk); + + /* store fnum in clear form for LFO PM calculations */ + CH->block_fnum = (blk<<11) | fn; + + CH->SLOT[SLOT1].Incr=-1; + } + break; + case 1: /* 0xa4-0xa6 : FNUM2,BLK */ + OPN->ST.fn_h = v&0x3f; +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) // workaround for stupid Kega Fusion init block + CH->block_fnum = (OPN->ST.fn_h << 8) | (CH->block_fnum & 0xFF); +#endif + break; + case 2: /* 0xa8-0xaa : 3CH FNUM1 */ +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->SL3.fn_h = OPN->SL3.block_fnum[c] >> 8; +#endif + if(r < 0x100) + { + UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v; + UINT8 blk = OPN->SL3.fn_h>>3; + /* keyscale code */ + OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7]; + /* phase increment counter */ + OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk); + OPN->SL3.block_fnum[c] = (blk<<11) | fn; + (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1; + } + break; + case 3: /* 0xac-0xae : 3CH FNUM2,BLK */ + if(r < 0x100) + { + OPN->SL3.fn_h = v&0x3f; +#ifdef USE_VGM_INIT_SWITCH + if (IsVGMInit) + OPN->SL3.block_fnum[c] = (OPN->SL3.fn_h << 8) | (OPN->SL3.block_fnum[c] & 0xFF); +#endif + } + break; + } + break; + + case 0xb0: + switch( OPN_SLOT(r) ) + { + case 0: /* 0xb0-0xb2 : FB,ALGO */ + { + unsigned char feedback = ((v>>3)&7); + CH->ALGO = v&7; + CH->FB = feedback ? feedback + 6 : 0; + setup_connection( OPN, CH, c ); + } + break; + case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */ + if( OPN->type & TYPE_LFOPAN) + { + /* b0-2 PMS */ + CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */ + + /* b4-5 AMS */ + CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03]; + + /* PAN : b7 = L, b6 = R */ + OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0; + OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0; + + } + break; + } + break; + } +} + +/* initialize time tables */ +static void init_timetables(FM_OPN *OPN, double freqbase) +{ + int i,d; + double rate; + + /* DeTune table */ + for (d = 0;d <= 3;d++) + { + for (i = 0;i <= 31;i++) + { + rate = ((double)dt_tab[d*32 + i]) * freqbase * (1<<(FREQ_SH-10)); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ + OPN->ST.dt_tab[d][i] = (INT32) rate; + OPN->ST.dt_tab[d+4][i] = -OPN->ST.dt_tab[d][i]; + } + } + + /* there are 2048 FNUMs that can be generated using FNUM/BLK registers + but LFO works with one more bit of a precision so we really need 4096 elements */ + /* calculate fnumber -> increment counter table */ + for(i = 0; i < 4096; i++) + { + /* freq table for octave 7 */ + /* OPN phase increment counter = 20bit */ + /* the correct formula is : F-Number = (144 * fnote * 2^20 / M) / 2^(B-1) */ + /* where sample clock is M/144 */ + /* this means the increment value for one clock sample is FNUM * 2^(B-1) = FNUM * 64 for octave 7 */ + /* we also need to handle the ratio between the chip frequency and the emulated frequency (can be 1.0) */ + OPN->fn_table[i] = (UINT32)( (double)i * 32 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */ + } + + /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */ + OPN->fn_max = (UINT32)( (double)0x20000 * freqbase * (1<<(FREQ_SH-10)) ); +} + +/* prescaler set (and make time tables) */ +static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres) +{ + /* frequency base */ + OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0; + + /* EG is updated every 3 samples */ + OPN->eg_timer_add = (UINT32)((1<ST.freqbase); + OPN->eg_timer_overflow = ( 3 ) * (1<lfo_timer_add = (UINT32)((1<ST.freqbase); + + /* Timer base time */ + OPN->ST.timer_prescaler = timer_prescaler; + + /* SSG part prescaler set */ + if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.param, OPN->ST.clock * 2 / SSGpres ); + + /* make time tables */ + init_timetables(OPN, OPN->ST.freqbase); +} + +static void reset_channels( FM_ST *ST , FM_CH *CH , int num ) +{ + int c,s; + (void)ST; + + for( c = 0 ; c < num ; c++ ) + { + /* memset(&CH[c], 0x00, sizeof(FM_CH)); */ + CH[c].mem_value = 0; + CH[c].op1_out[0] = 0; + CH[c].op1_out[1] = 0; + CH[c].fc = 0; + for(s = 0 ; s < 4 ; s++ ) + { + /* memset(&CH[c].SLOT[s], 0x00, sizeof(FM_SLOT)); */ + CH[c].SLOT[s].Incr = -1; + CH[c].SLOT[s].key = 0; + CH[c].SLOT[s].phase = 0; + CH[c].SLOT[s].ssg = 0; + CH[c].SLOT[s].ssgn = 0; + CH[c].SLOT[s].state= EG_OFF; + CH[c].SLOT[s].volume = MAX_ATT_INDEX; + CH[c].SLOT[s].vol_out= MAX_ATT_INDEX; + } + } +} + +/* initialize generic tables */ +static void init_tables(void) +{ + signed int i,x; + signed int n; + double o,m; + + /* build Linear Power Table */ + for (x=0; x>= 4; /* 12 bits here */ + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + /* 11 bits here (rounded) */ + n <<= 2; /* 13 bits here (as in real chip) */ + + + /* 14 bits (with sign bit) */ + tl_tab[ x*2 + 0 ] = n; + tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ]; + + /* one entry in the 'Power' table use the following format, xxxxxyyyyyyyys with: */ + /* s = sign bit */ + /* yyyyyyyy = 8-bits decimal part (0-TL_RES_LEN) */ + /* xxxxx = 5-bits integer 'shift' value (0-31) but, since Power table output is 13 bits, */ + /* any value above 13 (included) would be discarded. */ + for (i=1; i<13; i++) + { + tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i; + tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ]; + } + } + + /* build Logarithmic Sinus table */ + for (i=0; i0.0) + o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */ + else + o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */ + + o = o / (ENV_STEP/4); + + n = (int)(2.0*o); + if (n&1) /* round to nearest */ + n = (n>>1)+1; + else + n = n>>1; + + /* 13-bits (8.5) value is formatted for above 'Power' table */ + sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 ); + } + + /* build LFO PM modulation table */ + for(i = 0; i < 8; i++) /* 8 PM depths */ + { + UINT8 fnum; + for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */ + { + UINT8 value; + UINT8 step; + UINT32 offset_depth = i; + UINT32 offset_fnum_bit; + UINT32 bit_tmp; + + for (step=0; step<8; step++) + { + value = 0; + for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */ + { + if (fnum & (1<CH; + FMSAMPLE *bufOut = buffer; + int i; +#if !RSM_ENABLE + FMSAMPLE bufTmp[2]; +#endif + + ym2612_pre_generate(chip); + + if (!frames) + { + update_ssg_eg_channel(&cch[0].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[1].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[2].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[3].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[4].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[5].SLOT[SLOT1]); + } + + /* buffering */ + for(i=0 ; i < frames ; i++) + { +#if RSM_ENABLE + while(F2612->OPN.ST.framecnt >= F2612->OPN.ST.rateratio)/* Copy-Pasta from Nuked */ + { + /* Copy-Pasta from Nuked */ + F2612->OPN.ST.prev_sample[0] = F2612->OPN.ST.cur_sample[0]; + F2612->OPN.ST.prev_sample[1] = F2612->OPN.ST.cur_sample[1]; + ym2612_generate_one_native(chip, F2612->OPN.ST.cur_sample); + F2612->OPN.ST.framecnt -= F2612->OPN.ST.rateratio; + /* Copy-Pasta from Nuked */ + } + if (mix) + { + *bufOut++ += (FMSAMPLE)((F2612->OPN.ST.prev_sample[0] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[0] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + *bufOut++ += (FMSAMPLE)((F2612->OPN.ST.prev_sample[1] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[1] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + } else { + *bufOut++ = (FMSAMPLE)((F2612->OPN.ST.prev_sample[0] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[0] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + *bufOut++ = (FMSAMPLE)((F2612->OPN.ST.prev_sample[1] * (F2612->OPN.ST.rateratio - F2612->OPN.ST.framecnt) + + F2612->OPN.ST.cur_sample[1] * F2612->OPN.ST.framecnt) / F2612->OPN.ST.rateratio); + } + F2612->OPN.ST.framecnt += 1 << RSM_FRAC; +#else + if (mix) + { + ym2612_generate_one_native(chip, bufTmp); + bufOut[0] += bufTmp[0]; + bufOut[1] += bufTmp[1]; + } + else + { + ym2612_generate_one_native(chip, bufOut); + } + bufOut += 2; +#endif + } + /* ym2612_post_generate(chip, frames); */ +} + +void ym2612_pre_generate(void *chip) +{ + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + FM_CH *cch = F2612->CH; + + /* refresh PG and EG */ + refresh_fc_eg_chan( OPN, &cch[0] ); + refresh_fc_eg_chan( OPN, &cch[1] ); + if( (OPN->ST.mode & 0xc0) ) + { + /* 3SLOT MODE */ + if( cch[2].SLOT[SLOT1].Incr==-1) + { + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] ); + refresh_fc_eg_slot(OPN, &cch[2].SLOT[SLOT4] , cch[2].fc , cch[2].kcode ); + } + } else + refresh_fc_eg_chan( OPN, &cch[2] ); + refresh_fc_eg_chan( OPN, &cch[3] ); + refresh_fc_eg_chan( OPN, &cch[4] ); + refresh_fc_eg_chan( OPN, &cch[5] ); +} + +void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[]) +{ + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + INT32 *out_fm = OPN->out_fm; + FM_CH *cch = F2612->CH; + INT32 dacout; + int lt,rt; + + if (! F2612->MuteDAC) + dacout = F2612->dacout; + else + dacout = 0; + + /* clear outputs */ + out_fm[0] = 0; + out_fm[1] = 0; + out_fm[2] = 0; + out_fm[3] = 0; + out_fm[4] = 0; + out_fm[5] = 0; + + /* update SSG-EG output */ + update_ssg_eg_channel(&cch[0].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[1].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[2].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[3].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[4].SLOT[SLOT1]); + update_ssg_eg_channel(&cch[5].SLOT[SLOT1]); + + /* calculate FM */ + if (! F2612->dac_test) + { + chan_calc(F2612, OPN, &cch[0]); + chan_calc(F2612, OPN, &cch[1]); + chan_calc(F2612, OPN, &cch[2]); + chan_calc(F2612, OPN, &cch[3]); + chan_calc(F2612, OPN, &cch[4]); + if( F2612->dacen ) + cch[5].connect4 += dacout; + else + chan_calc(F2612, OPN, &cch[5]); + } + else + { + out_fm[0] = out_fm[1] = dacout; + out_fm[2] = out_fm[3] = dacout; + out_fm[5] = dacout; + } + + /* advance LFO */ + advance_lfo(OPN); + + /* advance envelope generator */ + OPN->eg_timer += OPN->eg_timer_add; + while (OPN->eg_timer >= OPN->eg_timer_overflow) + { + /* reset EG timer */ + OPN->eg_timer -= OPN->eg_timer_overflow; + /* increment EG counter */ + OPN->eg_cnt++; + /* EG counter is 12-bit only and zero value is skipped (verified on real hardware) */ + if (OPN->eg_cnt == 4096) + OPN->eg_cnt = 1; + + /* advance envelope generator */ + advance_eg_channel(OPN, &cch[0].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[1].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[2].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[3].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[4].SLOT[SLOT1]); + advance_eg_channel(OPN, &cch[5].SLOT[SLOT1]); + } + + /*fprintf(hFile, "%u", FileSample, out_fm[0]); + for (lt = 0; lt < 6; lt ++) + fprintf(hFile, "\t%d", out_fm[lt]); + fprintf(hFile, "\n"); + FileSample ++;*/ + + if (out_fm[0] > 8192) out_fm[0] = 8192; + else if (out_fm[0] < -8192) out_fm[0] = -8192; + if (out_fm[1] > 8192) out_fm[1] = 8192; + else if (out_fm[1] < -8192) out_fm[1] = -8192; + if (out_fm[2] > 8192) out_fm[2] = 8192; + else if (out_fm[2] < -8192) out_fm[2] = -8192; + if (out_fm[3] > 8192) out_fm[3] = 8192; + else if (out_fm[3] < -8192) out_fm[3] = -8192; + if (out_fm[4] > 8192) out_fm[4] = 8192; + else if (out_fm[4] < -8192) out_fm[4] = -8192; + if (out_fm[5] > 8192) out_fm[5] = 8192; + else if (out_fm[5] < -8192) out_fm[5] = -8192; + + /* 6-channels mixing */ + lt = ((out_fm[0]>>0) & OPN->pan[0]); + rt = ((out_fm[0]>>0) & OPN->pan[1]); + lt += ((out_fm[1]>>0) & OPN->pan[2]); + rt += ((out_fm[1]>>0) & OPN->pan[3]); + lt += ((out_fm[2]>>0) & OPN->pan[4]); + rt += ((out_fm[2]>>0) & OPN->pan[5]); + lt += ((out_fm[3]>>0) & OPN->pan[6]); + rt += ((out_fm[3]>>0) & OPN->pan[7]); + if (! F2612->dac_test) + { + lt += ((out_fm[4]>>0) & OPN->pan[8]); + rt += ((out_fm[4]>>0) & OPN->pan[9]); + } + else + { + lt += dacout; + lt += dacout; + } + lt += ((out_fm[5]>>0) & OPN->pan[10]); + rt += ((out_fm[5]>>0) & OPN->pan[11]); + + /* Limit( lt, MAXOUT, MINOUT ); */ + /* Limit( rt, MAXOUT, MINOUT ); */ + + #ifdef SAVE_SAMPLE + SAVE_ALL_CHANNELS + #endif + + /* buffering */ + if (F2612->WaveOutMode & 0x01) + F2612->WaveL = lt; + if (F2612->WaveOutMode & 0x02) + F2612->WaveR = rt; + if (F2612->WaveOutMode ^ 0x03) + F2612->WaveOutMode ^= 0x03; + + buffer[0] = (FMSAMPLE)(F2612->WaveL / 2); + buffer[1] = (FMSAMPLE)(F2612->WaveR / 2); + + /* CSM mode: if CSM Key ON has occured, CSM Key OFF need to be sent */ + /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */ + OPN->SL3.key_csm <<= 1; + + /* timer A control */ + /* INTERNAL_TIMER_A( &OPN->ST , cch[2] ) */ + { + if( OPN->ST.TAC && (OPN->ST.timer_handler==0) ) + if( (OPN->ST.TAC -= (int)(OPN->ST.freqbase*4096)) <= 0 ) + { + TimerAOver( &OPN->ST ); + /* CSM mode total level latch and auto key on */ + if( OPN->ST.mode & 0x80 ) + CSMKeyControll( OPN, &cch[2] ); + } + } + + /* CSM Mode Key ON still disabled */ + if (OPN->SL3.key_csm & 2) + { + /* CSM Mode Key OFF (verified by Nemesis on real hardware) */ + FM_KEYOFF_CSM(&cch[2],SLOT1); + FM_KEYOFF_CSM(&cch[2],SLOT2); + FM_KEYOFF_CSM(&cch[2],SLOT3); + FM_KEYOFF_CSM(&cch[2],SLOT4); + OPN->SL3.key_csm = 0; + } +} + +#if 0 +void ym2612_post_generate(void *chip, int length) +{ + YM2612 *F2612 = (YM2612 *)chip; + /* timer B control */ + INTERNAL_TIMER_B(&F2612->OPN.ST, length); +} +#endif + +#ifdef __STATE_H__ +void ym2612_postload(void *chip) +{ + if (chip) + { + YM2612 *F2612 = (YM2612 *)chip; + int r; + + /* DAC data & port */ + F2612->dacout = ((int)F2612->REGS[0x2a] - 0x80) << 6; /* level unknown */ + F2612->dacen = F2612->REGS[0x2d] & 0x80; + /* OPN registers */ + /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */ + for(r=0x30;r<0x9e;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); + OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); + } + /* FB / CONNECT , L / R / AMS / PMS */ + for(r=0xb0;r<0xb6;r++) + if((r&3) != 3) + { + OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]); + OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]); + } + /* channels */ + /*FM_channel_postload(F2612->CH,6);*/ + } +} + +static void YM2612_save_state(YM2612 *F2612, running_device *device) +{ + state_save_register_device_item_array(device, 0, F2612->REGS); + FMsave_state_st(device,&F2612->OPN.ST); + FMsave_state_channel(device,F2612->CH,6); + /* 3slots */ + state_save_register_device_item_array(device, 0, F2612->OPN.SL3.fc); + state_save_register_device_item(device, 0, F2612->OPN.SL3.fn_h); + state_save_register_device_item_array(device, 0, F2612->OPN.SL3.kcode); + /* address register1 */ + state_save_register_device_item(device, 0, F2612->addr_A1); +} +#endif /* _STATE_H */ + +/* initialize YM2612 emulator(s) */ +static void * ym2612_init(void *param, int clock, int rate, + FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler) +{ + YM2612 *F2612; + + if (clock <= 0 || rate <= 0) + return NULL; /* Forbid zero clock and sample rate */ + + /* allocate extend state space */ + /* F2612 = auto_alloc_clear(device->machine, YM2612); */ + F2612 = (YM2612 *)malloc(sizeof(YM2612)); + if (F2612 == NULL) + return NULL; + memset(F2612, 0x00, sizeof(YM2612)); + /* allocate total level table (128kb space) */ + init_tables(); + + F2612->OPN.ST.param = param; + F2612->OPN.type = TYPE_YM2612; + F2612->OPN.P_CH = F2612->CH; + /* F2612->OPN.ST.device = device; */ + F2612->OPN.ST.clock = clock; +#if RSM_ENABLE + F2612->OPN.ST.rate = 53267; + F2612->OPN.ST.rateratio = (INT32)(UINT32)((((UINT64)144 * rate) << RSM_FRAC) / clock); + F2612->OPN.ST.framecnt = 1 << RSM_FRAC; + memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2); + memset(&(F2612->OPN.ST.prev_sample), 0x00, sizeof(FMSAMPLE) * 2); +#else + F2612->OPN.ST.rate = rate; +#endif + /* F2612->OPN.ST.irq = 0; */ + /* F2612->OPN.ST.status = 0; */ + /* Extend handler */ + F2612->OPN.ST.timer_handler = timer_handler; + F2612->OPN.ST.IRQ_Handler = IRQHandler; + + if (PseudoSt) + F2612->WaveOutMode = 0x01; + else + F2612->WaveOutMode = 0x03; + /*hFile = fopen("YM2612.log", "wt"); + fprintf(hFile, "Clock: %d, Sample Rate: %d\n", clock, rate); + fprintf(hFile, "Sample\tCh 0\tCh 1\tCh 2\tCh 3\tCh 4\tCh 5\n"); + FileSample = 0;*/ + +#ifdef __STATE_H__ + YM2612_save_state(F2612, device); +#endif + return F2612; +} + +/* shut down emulator */ +static void ym2612_shutdown(void *chip) +{ + YM2612 *F2612 = (YM2612 *)chip; + /* fclose(hFile); */ + + FMCloseTable(); + /* auto_free(F2612->OPN.ST.device->machine, F2612); */ + free(F2612); +} + +/* reset one of chip */ +static void ym2612_reset_chip(void *chip) +{ + int i; + YM2612 *F2612 = (YM2612 *)chip; + FM_OPN *OPN = &F2612->OPN; + + OPNSetPres( OPN, 6*24, 6*24, 0); + /* status clear */ + FM_IRQMASK_SET(&OPN->ST,0x03); + FM_BUSY_CLEAR(&OPN->ST); + /* OPNWriteMode(OPN,0x27,0x30); */ /* mode 0 , timer reset */ + +#if RSM_ENABLE + /* Resampler's state */ + F2612->OPN.ST.framecnt = 1 << RSM_FRAC; + memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2); + memset(&(F2612->OPN.ST.prev_sample), 0x00, sizeof(FMSAMPLE) * 2); +#endif + + OPN->eg_timer = 0; + OPN->eg_cnt = 0; + + OPN->lfo_timer = 0; + OPN->lfo_cnt = 0; + OPN->LFO_AM = 126; + OPN->LFO_PM = 0; + + OPN->ST.TAC = 0; + OPN->ST.TBC = 0; + + OPN->SL3.key_csm = 0; + + OPN->ST.status = 0; + OPN->ST.mode = 0; + + memset(F2612->REGS, 0x00, sizeof(UINT8) * 512); + + OPNWriteMode(OPN,0x22,0x00); + + OPNWriteMode(OPN,0x27,0x30); + OPNWriteMode(OPN,0x26,0x00); + OPNWriteMode(OPN,0x25,0x00); + OPNWriteMode(OPN,0x24,0x00); + + reset_channels( &OPN->ST , &F2612->CH[0] , 6 ); + + for(i = 0xb6 ; i >= 0xb4 ; i-- ) + { + OPNWriteReg(OPN,i ,0xc0); + OPNWriteReg(OPN,i|0x100,0xc0); + } + for(i = 0xb2 ; i >= 0x30 ; i-- ) + { + OPNWriteReg(OPN,i ,0); + OPNWriteReg(OPN,i|0x100,0); + } + + /* DAC mode clear */ + F2612->dacen = 0; + F2612->dac_test = 0; + F2612->dacout = 0; + + if (F2612->WaveOutMode == 0x02) + F2612->WaveOutMode >>= 1; +} + +/* YM2612 write */ +/* n = number */ +/* a = address */ +/* v = value */ +static int ym2612_write(void *chip, int a, UINT8 v) +{ + YM2612 *F2612 = (YM2612 *)chip; + int addr; + + v &= 0xff; /* adjust to 8 bit bus */ + + switch( a&3) + { + case 0: /* address port 0 */ + F2612->OPN.ST.address = v; + F2612->addr_A1 = 0; + break; + + case 1: /* data port 0 */ + if (F2612->addr_A1 != 0) + break; /* verified on real YM2608 */ + + addr = F2612->OPN.ST.address; + F2612->REGS[addr] = v; + switch( addr & 0xf0 ) + { + case 0x20: /* 0x20-0x2f Mode */ + switch( addr ) + { + case 0x2a: /* DAC data (YM2612) */ + ym2612_update_one(chip, DUMMYBUF, 0); + F2612->dacout = ((int)v - 0x80) << 6; /* level unknown */ + break; + case 0x2b: /* DAC Sel (YM2612) */ + /* b7 = dac enable */ + F2612->dacen = v & 0x80; + break; + case 0x2C: /* undocumented: DAC Test Reg */ + /* b5 = volume enable */ + F2612->dac_test = v & 0x20; + break; + default: /* OPN section */ + /* ym2612_update_req(F2612->OPN.ST.param); */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* write register */ + OPNWriteMode(&(F2612->OPN),addr,v); + } + break; + default: /* 0x30-0xff OPN section */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* write register */ + OPNWriteReg(&(F2612->OPN),addr,v); + } + break; + + case 2: /* address port 1 */ + F2612->OPN.ST.address = v; + F2612->addr_A1 = 1; + break; + + case 3: /* data port 1 */ + if (F2612->addr_A1 != 1) + break; /* verified on real YM2608 */ + + addr = F2612->OPN.ST.address; + F2612->REGS[addr | 0x100] = v; + ym2612_update_one(chip, DUMMYBUF, 0); + OPNWriteReg(&(F2612->OPN),addr | 0x100,v); + break; + } + return F2612->OPN.ST.irq; +} + +#if 0 +static UINT8 ym2612_read(void *chip,int a) +{ + YM2612 *F2612 = (YM2612 *)chip; + + switch( a&3) + { + case 0: /* status 0 */ + return FM_STATUS_FLAG(&F2612->OPN.ST); + case 1: + case 2: + case 3: + /* LOG(LOG_WAR,("YM2612 #%p:A=%d read unmapped area\n",F2612->OPN.ST.param,a)); */ + return FM_STATUS_FLAG(&F2612->OPN.ST); + } + return 0; +} + +static int ym2612_timer_over(void *chip,int c) +{ + YM2612 *F2612 = (YM2612 *)chip; + + if( c ) + { /* Timer B */ + TimerBOver( &(F2612->OPN.ST) ); + } + else + { /* Timer A */ + ym2612_update_one(chip, DUMMYBUF, 0); + /* timer update */ + TimerAOver( &(F2612->OPN.ST) ); + /* CSM mode key,TL controll */ + if ((F2612->OPN.ST.mode & 0xc0) == 0x80) + { /* CSM mode total level latch and auto key on */ + CSMKeyControll( &F2612->OPN, &(F2612->CH[2]) ); + } + } + return F2612->OPN.ST.irq; +} +#endif + +static void ym2612_set_mutemask(void *chip, UINT32 MuteMask) +{ + YM2612 *F2612 = (YM2612 *)chip; + UINT8 CurChn; + + for (CurChn = 0; CurChn < 6; CurChn ++) + F2612->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01; + F2612->MuteDAC = (MuteMask >> 6) & 0x01; + + return; +} +#if 0 +static void ym2612_setoptions(UINT8 Flags) +{ + PseudoSt = (Flags >> 2) & 0x01; + + return; +} +#endif + +} // Ym2612_MameImpl + + +Ym2612_MAME_Emu::Ym2612_MAME_Emu() { impl = 0; } + +Ym2612_MAME_Emu::~Ym2612_MAME_Emu() +{ + if ( impl ) Ym2612_MameImpl::ym2612_shutdown( impl ); +} + +const char *Ym2612_MAME_Emu::set_rate(double sample_rate, double clock_rate) +{ + if ( impl ) Ym2612_MameImpl::ym2612_shutdown( impl ); + impl = Ym2612_MameImpl::ym2612_init( NULL, static_cast(clock_rate), static_cast(sample_rate), NULL, NULL ); + if ( !impl ) + return "Out of memory"; + return 0; +} + +void Ym2612_MAME_Emu::reset() +{ + if ( impl ) Ym2612_MameImpl::ym2612_reset_chip( impl ); +} + +void Ym2612_MAME_Emu::mute_voices(int mask) +{ + if ( impl ) Ym2612_MameImpl::ym2612_set_mutemask( impl, mask ); +} + +void Ym2612_MAME_Emu::write0(int addr, int data) +{ + if ( !impl ) return; + Ym2612_MameImpl::ym2612_write( impl, 0, static_cast(addr) ); + Ym2612_MameImpl::ym2612_write( impl, 1, static_cast(data) ); +} + +void Ym2612_MAME_Emu::write1(int addr, int data) +{ + if ( !impl ) return; + Ym2612_MameImpl::ym2612_write( impl, 0 + 2, static_cast(addr) ); + Ym2612_MameImpl::ym2612_write( impl, 1 + 2, static_cast(data) ); +} + +void Ym2612_MAME_Emu::run(int pair_count, Ym2612_MAME_Emu::sample_t *out) +{ + if ( impl ) Ym2612_MameImpl::ym2612_generate( impl, out, pair_count, 1); +} diff -Nru game-music-emu-0.6.2/gme/Ym2612_MAME.h game-music-emu-0.6.3/gme/Ym2612_MAME.h --- game-music-emu-0.6.2/gme/Ym2612_MAME.h 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ym2612_MAME.h 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,38 @@ +// YM2612 FM sound chip emulator interface + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef YM2612_EMU_H +#define YM2612_EMU_H + +typedef void Ym2612_MAME_Impl; + +class Ym2612_MAME_Emu { + Ym2612_MAME_Impl* impl; +public: + Ym2612_MAME_Emu(); + ~Ym2612_MAME_Emu(); + + // Set output sample rate and chip clock rates, in Hz. Returns non-zero + // if error. + const char* set_rate( double sample_rate, double clock_rate ); + + // Reset to power-up state + void reset(); + + // Mute voice n if bit n (1 << n) of mask is set + enum { channel_count = 6 }; + void mute_voices( int mask ); + + // Write addr to register 0 then data to register 1 + void write0( int addr, int data ); + + // Write addr to register 2 then data to register 3 + void write1( int addr, int data ); + + // Run and add pair_count samples into current output buffer contents + typedef short sample_t; + enum { out_chan_count = 2 }; // stereo + void run( int pair_count, sample_t* out ); +}; + +#endif diff -Nru game-music-emu-0.6.2/gme/Ym2612_Nuked.cpp game-music-emu-0.6.3/gme/Ym2612_Nuked.cpp --- game-music-emu-0.6.2/gme/Ym2612_Nuked.cpp 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ym2612_Nuked.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,1872 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +// Based on Nuked OPN2 ym3438.c and ym3438.h + +#include "Ym2612_Nuked.h" + +/* + * Copyright (C) 2017 Alexey Khokholov (Nuke.YKT) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * + * Nuked OPN2(Yamaha YM3438) emulator. + * Thanks: + * Silicon Pr0n: + * Yamaha YM3438 decap and die shot(digshadow). + * OPLx decapsulated(Matthew Gambrell, Olli Niemitalo): + * OPL2 ROMs. + * + * version: 1.0.7 + */ + + +#include +#include + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint64_t Bit64u; +typedef int64_t Bit64s; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +namespace Ym2612_NukedImpl +{ + +/*EXTRA*/ +#define RSM_FRAC 10 +#define OPN_WRITEBUF_SIZE 2048 +#define OPN_WRITEBUF_DELAY 15 + +enum { + ym3438_type_discrete = 0, /* Discrete YM3438 (Teradrive) */ + ym3438_type_asic = 1, /* ASIC YM3438 (MD1 VA7, MD2, MD3, etc) */ + ym3438_type_ym2612 = 2 /* YM2612 (MD1, MD2 VA2) */ +}; + +/*EXTRA*/ +typedef struct _opn2_writebuf { + Bit64u time; + Bit8u port; + Bit8u data; + Bit8u reserved[6]; +} opn2_writebuf; + +typedef struct +{ + Bit32u cycles; + Bit32u channel; + Bit16s mol, mor; + /* IO */ + Bit16u write_data; + Bit8u write_a; + Bit8u write_d; + Bit8u write_a_en; + Bit8u write_d_en; + Bit8u write_busy; + Bit8u write_busy_cnt; + Bit8u write_fm_address; + Bit8u write_fm_data; + Bit8u write_fm_mode_a; + Bit16u address; + Bit8u data; + Bit8u pin_test_in; + Bit8u pin_irq; + Bit8u busy; + /* LFO */ + Bit8u lfo_en; + Bit8u lfo_freq; + Bit8u lfo_pm; + Bit8u lfo_am; + Bit8u lfo_cnt; + Bit8u lfo_inc; + Bit8u lfo_quotient; + /* Phase generator */ + Bit16u pg_fnum; + Bit8u pg_block; + Bit8u pg_kcode; + Bit32u pg_inc[24]; + Bit32u pg_phase[24]; + Bit8u pg_reset[24]; + Bit32u pg_read; + /* Envelope generator */ + Bit8u eg_cycle; + Bit8u eg_cycle_stop; + Bit8u eg_shift; + Bit8u eg_shift_lock; + Bit8u eg_timer_low_lock; + Bit16u eg_timer; + Bit8u eg_timer_inc; + Bit16u eg_quotient; + Bit8u eg_custom_timer; + Bit8u eg_rate; + Bit8u eg_ksv; + Bit8u eg_inc; + Bit8u eg_ratemax; + Bit8u eg_sl[2]; + Bit8u eg_lfo_am; + Bit8u eg_tl[2]; + Bit8u eg_state[24]; + Bit16u eg_level[24]; + Bit16u eg_out[24]; + Bit8u eg_kon[24]; + Bit8u eg_kon_csm[24]; + Bit8u eg_kon_latch[24]; + Bit8u eg_csm_mode[24]; + Bit8u eg_ssg_enable[24]; + Bit8u eg_ssg_pgrst_latch[24]; + Bit8u eg_ssg_repeat_latch[24]; + Bit8u eg_ssg_hold_up_latch[24]; + Bit8u eg_ssg_dir[24]; + Bit8u eg_ssg_inv[24]; + Bit32u eg_read[2]; + Bit8u eg_read_inc; + /* FM */ + Bit16s fm_op1[6][2]; + Bit16s fm_op2[6]; + Bit16s fm_out[24]; + Bit16u fm_mod[24]; + /* Channel */ + Bit16s ch_acc[6]; + Bit16s ch_out[6]; + Bit16s ch_lock; + Bit8u ch_lock_l; + Bit8u ch_lock_r; + Bit16s ch_read; + /* Timer */ + Bit16u timer_a_cnt; + Bit16u timer_a_reg; + Bit8u timer_a_load_lock; + Bit8u timer_a_load; + Bit8u timer_a_enable; + Bit8u timer_a_reset; + Bit8u timer_a_load_latch; + Bit8u timer_a_overflow_flag; + Bit8u timer_a_overflow; + + Bit16u timer_b_cnt; + Bit8u timer_b_subcnt; + Bit16u timer_b_reg; + Bit8u timer_b_load_lock; + Bit8u timer_b_load; + Bit8u timer_b_enable; + Bit8u timer_b_reset; + Bit8u timer_b_load_latch; + Bit8u timer_b_overflow_flag; + Bit8u timer_b_overflow; + + /* Register set */ + Bit8u mode_test_21[8]; + Bit8u mode_test_2c[8]; + Bit8u mode_ch3; + Bit8u mode_kon_channel; + Bit8u mode_kon_operator[4]; + Bit8u mode_kon[24]; + Bit8u mode_csm; + Bit8u mode_kon_csm; + Bit8u dacen; + Bit16s dacdata; + + Bit8u ks[24]; + Bit8u ar[24]; + Bit8u sr[24]; + Bit8u dt[24]; + Bit8u multi[24]; + Bit8u sl[24]; + Bit8u rr[24]; + Bit8u dr[24]; + Bit8u am[24]; + Bit8u tl[24]; + Bit8u ssg_eg[24]; + + Bit16u fnum[6]; + Bit8u block[6]; + Bit8u kcode[6]; + Bit16u fnum_3ch[6]; + Bit8u block_3ch[6]; + Bit8u kcode_3ch[6]; + Bit8u reg_a4; + Bit8u reg_ac; + Bit8u connect[6]; + Bit8u fb[6]; + Bit8u pan_l[6], pan_r[6]; + Bit8u ams[6]; + Bit8u pms[6]; + + /*EXTRA*/ + Bit32u mute[7]; + Bit32s rateratio; + Bit32s samplecnt; + Bit32s oldsamples[2]; + Bit32s samples[2]; + + Bit64u writebuf_samplecnt; + Bit32u writebuf_cur; + Bit32u writebuf_last; + Bit64u writebuf_lasttime; + opn2_writebuf writebuf[OPN_WRITEBUF_SIZE]; +} ym3438_t; + +/* EXTRA, original was "void OPN2_Reset(ym3438_t *chip)" */ +void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock); +void OPN2_SetChipType(Bit32u type); +void OPN2_Clock(ym3438_t *chip, Bit16s *buffer); +void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data); +void OPN2_SetTestPin(ym3438_t *chip, Bit32u value); +Bit32u OPN2_ReadTestPin(ym3438_t *chip); +Bit32u OPN2_ReadIRQPin(ym3438_t *chip); +Bit8u OPN2_Read(ym3438_t *chip, Bit32u port); + +/*EXTRA*/ +void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data); +void OPN2_Generate(ym3438_t *chip, Bit16s *buf); +void OPN2_GenerateResampled(ym3438_t *chip, Bit16s *buf); +void OPN2_GenerateStream(ym3438_t *chip, Bit16s *output, Bit32u numsamples); +void OPN2_GenerateStreamMix(ym3438_t *chip, Bit16s *output, Bit32u numsamples); +void OPN2_SetOptions(Bit8u flags); +void OPN2_SetMute(ym3438_t *chip, Bit32u mute); + + + + + +enum { + eg_num_attack = 0, + eg_num_decay = 1, + eg_num_sustain = 2, + eg_num_release = 3 +}; + +/* logsin table */ +static const Bit16u logsinrom[256] = { + 0x859, 0x6c3, 0x607, 0x58b, 0x52e, 0x4e4, 0x4a6, 0x471, + 0x443, 0x41a, 0x3f5, 0x3d3, 0x3b5, 0x398, 0x37e, 0x365, + 0x34e, 0x339, 0x324, 0x311, 0x2ff, 0x2ed, 0x2dc, 0x2cd, + 0x2bd, 0x2af, 0x2a0, 0x293, 0x286, 0x279, 0x26d, 0x261, + 0x256, 0x24b, 0x240, 0x236, 0x22c, 0x222, 0x218, 0x20f, + 0x206, 0x1fd, 0x1f5, 0x1ec, 0x1e4, 0x1dc, 0x1d4, 0x1cd, + 0x1c5, 0x1be, 0x1b7, 0x1b0, 0x1a9, 0x1a2, 0x19b, 0x195, + 0x18f, 0x188, 0x182, 0x17c, 0x177, 0x171, 0x16b, 0x166, + 0x160, 0x15b, 0x155, 0x150, 0x14b, 0x146, 0x141, 0x13c, + 0x137, 0x133, 0x12e, 0x129, 0x125, 0x121, 0x11c, 0x118, + 0x114, 0x10f, 0x10b, 0x107, 0x103, 0x0ff, 0x0fb, 0x0f8, + 0x0f4, 0x0f0, 0x0ec, 0x0e9, 0x0e5, 0x0e2, 0x0de, 0x0db, + 0x0d7, 0x0d4, 0x0d1, 0x0cd, 0x0ca, 0x0c7, 0x0c4, 0x0c1, + 0x0be, 0x0bb, 0x0b8, 0x0b5, 0x0b2, 0x0af, 0x0ac, 0x0a9, + 0x0a7, 0x0a4, 0x0a1, 0x09f, 0x09c, 0x099, 0x097, 0x094, + 0x092, 0x08f, 0x08d, 0x08a, 0x088, 0x086, 0x083, 0x081, + 0x07f, 0x07d, 0x07a, 0x078, 0x076, 0x074, 0x072, 0x070, + 0x06e, 0x06c, 0x06a, 0x068, 0x066, 0x064, 0x062, 0x060, + 0x05e, 0x05c, 0x05b, 0x059, 0x057, 0x055, 0x053, 0x052, + 0x050, 0x04e, 0x04d, 0x04b, 0x04a, 0x048, 0x046, 0x045, + 0x043, 0x042, 0x040, 0x03f, 0x03e, 0x03c, 0x03b, 0x039, + 0x038, 0x037, 0x035, 0x034, 0x033, 0x031, 0x030, 0x02f, + 0x02e, 0x02d, 0x02b, 0x02a, 0x029, 0x028, 0x027, 0x026, + 0x025, 0x024, 0x023, 0x022, 0x021, 0x020, 0x01f, 0x01e, + 0x01d, 0x01c, 0x01b, 0x01a, 0x019, 0x018, 0x017, 0x017, + 0x016, 0x015, 0x014, 0x014, 0x013, 0x012, 0x011, 0x011, + 0x010, 0x00f, 0x00f, 0x00e, 0x00d, 0x00d, 0x00c, 0x00c, + 0x00b, 0x00a, 0x00a, 0x009, 0x009, 0x008, 0x008, 0x007, + 0x007, 0x007, 0x006, 0x006, 0x005, 0x005, 0x005, 0x004, + 0x004, 0x004, 0x003, 0x003, 0x003, 0x002, 0x002, 0x002, + 0x002, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000 +}; + +/* exp table */ +static const Bit16u exprom[256] = { + 0x000, 0x003, 0x006, 0x008, 0x00b, 0x00e, 0x011, 0x014, + 0x016, 0x019, 0x01c, 0x01f, 0x022, 0x025, 0x028, 0x02a, + 0x02d, 0x030, 0x033, 0x036, 0x039, 0x03c, 0x03f, 0x042, + 0x045, 0x048, 0x04b, 0x04e, 0x051, 0x054, 0x057, 0x05a, + 0x05d, 0x060, 0x063, 0x066, 0x069, 0x06c, 0x06f, 0x072, + 0x075, 0x078, 0x07b, 0x07e, 0x082, 0x085, 0x088, 0x08b, + 0x08e, 0x091, 0x094, 0x098, 0x09b, 0x09e, 0x0a1, 0x0a4, + 0x0a8, 0x0ab, 0x0ae, 0x0b1, 0x0b5, 0x0b8, 0x0bb, 0x0be, + 0x0c2, 0x0c5, 0x0c8, 0x0cc, 0x0cf, 0x0d2, 0x0d6, 0x0d9, + 0x0dc, 0x0e0, 0x0e3, 0x0e7, 0x0ea, 0x0ed, 0x0f1, 0x0f4, + 0x0f8, 0x0fb, 0x0ff, 0x102, 0x106, 0x109, 0x10c, 0x110, + 0x114, 0x117, 0x11b, 0x11e, 0x122, 0x125, 0x129, 0x12c, + 0x130, 0x134, 0x137, 0x13b, 0x13e, 0x142, 0x146, 0x149, + 0x14d, 0x151, 0x154, 0x158, 0x15c, 0x160, 0x163, 0x167, + 0x16b, 0x16f, 0x172, 0x176, 0x17a, 0x17e, 0x181, 0x185, + 0x189, 0x18d, 0x191, 0x195, 0x199, 0x19c, 0x1a0, 0x1a4, + 0x1a8, 0x1ac, 0x1b0, 0x1b4, 0x1b8, 0x1bc, 0x1c0, 0x1c4, + 0x1c8, 0x1cc, 0x1d0, 0x1d4, 0x1d8, 0x1dc, 0x1e0, 0x1e4, + 0x1e8, 0x1ec, 0x1f0, 0x1f5, 0x1f9, 0x1fd, 0x201, 0x205, + 0x209, 0x20e, 0x212, 0x216, 0x21a, 0x21e, 0x223, 0x227, + 0x22b, 0x230, 0x234, 0x238, 0x23c, 0x241, 0x245, 0x249, + 0x24e, 0x252, 0x257, 0x25b, 0x25f, 0x264, 0x268, 0x26d, + 0x271, 0x276, 0x27a, 0x27f, 0x283, 0x288, 0x28c, 0x291, + 0x295, 0x29a, 0x29e, 0x2a3, 0x2a8, 0x2ac, 0x2b1, 0x2b5, + 0x2ba, 0x2bf, 0x2c4, 0x2c8, 0x2cd, 0x2d2, 0x2d6, 0x2db, + 0x2e0, 0x2e5, 0x2e9, 0x2ee, 0x2f3, 0x2f8, 0x2fd, 0x302, + 0x306, 0x30b, 0x310, 0x315, 0x31a, 0x31f, 0x324, 0x329, + 0x32e, 0x333, 0x338, 0x33d, 0x342, 0x347, 0x34c, 0x351, + 0x356, 0x35b, 0x360, 0x365, 0x36a, 0x370, 0x375, 0x37a, + 0x37f, 0x384, 0x38a, 0x38f, 0x394, 0x399, 0x39f, 0x3a4, + 0x3a9, 0x3ae, 0x3b4, 0x3b9, 0x3bf, 0x3c4, 0x3c9, 0x3cf, + 0x3d4, 0x3da, 0x3df, 0x3e4, 0x3ea, 0x3ef, 0x3f5, 0x3fa +}; + +/* Note table */ +static const Bit32u fn_note[16] = { + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3 +}; + +/* Envelope generator */ +static const Bit32u eg_stephi[4][4] = { + { 0, 0, 0, 0 }, + { 1, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 1, 1, 1, 0 } +}; + +static const Bit8u eg_am_shift[4] = { + 7, 3, 1, 0 +}; + +/* Phase generator */ +static const Bit32u pg_detune[8] = { 16, 17, 19, 20, 22, 24, 27, 29 }; + +static const Bit32u pg_lfo_sh1[8][8] = { + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 7, 7, 1, 1 }, + { 7, 7, 7, 7, 1, 1, 1, 1 }, + { 7, 7, 7, 1, 1, 1, 1, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 }, + { 7, 7, 1, 1, 0, 0, 0, 0 } +}; + +static const Bit32u pg_lfo_sh2[8][8] = { + { 7, 7, 7, 7, 7, 7, 7, 7 }, + { 7, 7, 7, 7, 2, 2, 2, 2 }, + { 7, 7, 7, 2, 2, 2, 7, 7 }, + { 7, 7, 2, 2, 7, 7, 2, 2 }, + { 7, 7, 2, 7, 7, 7, 2, 7 }, + { 7, 7, 7, 2, 7, 7, 2, 1 }, + { 7, 7, 7, 2, 7, 7, 2, 1 }, + { 7, 7, 7, 2, 7, 7, 2, 1 } +}; + +/* Address decoder */ +static const Bit32u op_offset[12] = { + 0x000, /* Ch1 OP1/OP2 */ + 0x001, /* Ch2 OP1/OP2 */ + 0x002, /* Ch3 OP1/OP2 */ + 0x100, /* Ch4 OP1/OP2 */ + 0x101, /* Ch5 OP1/OP2 */ + 0x102, /* Ch6 OP1/OP2 */ + 0x004, /* Ch1 OP3/OP4 */ + 0x005, /* Ch2 OP3/OP4 */ + 0x006, /* Ch3 OP3/OP4 */ + 0x104, /* Ch4 OP3/OP4 */ + 0x105, /* Ch5 OP3/OP4 */ + 0x106 /* Ch6 OP3/OP4 */ +}; + +static const Bit32u ch_offset[6] = { + 0x000, /* Ch1 */ + 0x001, /* Ch2 */ + 0x002, /* Ch3 */ + 0x100, /* Ch4 */ + 0x101, /* Ch5 */ + 0x102 /* Ch6 */ +}; + +/* LFO */ +static const Bit32u lfo_cycles[8] = { + 108, 77, 71, 67, 62, 44, 8, 5 +}; + +/* FM algorithm */ +static const Bit32u fm_algorithm[4][6][8] = { + { + { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_0 */ + { 1, 1, 1, 1, 1, 1, 1, 1 }, /* OP1_1 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 1 } /* Out */ + }, + { + { 0, 1, 0, 0, 0, 1, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 1, 1, 1, 0, 0, 0, 0, 0 }, /* OP2 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 1, 1, 1 } /* Out */ + }, + { + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP2 */ + { 1, 0, 0, 1, 1, 1, 1, 0 }, /* Last operator */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* Last operator */ + { 0, 0, 0, 0, 1, 1, 1, 1 } /* Out */ + }, + { + { 0, 0, 1, 0, 0, 1, 0, 0 }, /* OP1_0 */ + { 0, 0, 0, 0, 0, 0, 0, 0 }, /* OP1_1 */ + { 0, 0, 0, 1, 0, 0, 0, 0 }, /* OP2 */ + { 1, 1, 0, 1, 1, 0, 0, 0 }, /* Last operator */ + { 0, 0, 1, 0, 0, 0, 0, 0 }, /* Last operator */ + { 1, 1, 1, 1, 1, 1, 1, 1 } /* Out */ + } +}; + +static Bit32u chip_type = ym3438_type_discrete; + +void OPN2_DoIO(ym3438_t *chip) +{ + /* Write signal check */ + chip->write_a_en = (chip->write_a & 0x03) == 0x01; + chip->write_d_en = (chip->write_d & 0x03) == 0x01; + chip->write_a <<= 1; + chip->write_d <<= 1; + /* Busy counter */ + chip->busy = chip->write_busy; + chip->write_busy_cnt += chip->write_busy; + chip->write_busy = (chip->write_busy && !(chip->write_busy_cnt >> 5)) || chip->write_d_en; + chip->write_busy_cnt &= 0x1f; +} + +void OPN2_DoRegWrite(ym3438_t *chip) +{ + Bit32u i; + Bit32u slot = chip->cycles % 12; + Bit32u address; + Bit32u channel = chip->channel; + /* Update registers */ + if (chip->write_fm_data) + { + /* Slot */ + if (op_offset[slot] == (chip->address & 0x107)) + { + if (chip->address & 0x08) + { + /* OP2, OP4 */ + slot += 12; + } + address = chip->address & 0xf0; + switch (address) + { + case 0x30: /* DT, MULTI */ + chip->multi[slot] = chip->data & 0x0f; + if (!chip->multi[slot]) + { + chip->multi[slot] = 1; + } + else + { + chip->multi[slot] <<= 1; + } + chip->dt[slot] = (chip->data >> 4) & 0x07; + break; + case 0x40: /* TL */ + chip->tl[slot] = chip->data & 0x7f; + break; + case 0x50: /* KS, AR */ + chip->ar[slot] = chip->data & 0x1f; + chip->ks[slot] = (chip->data >> 6) & 0x03; + break; + case 0x60: /* AM, DR */ + chip->dr[slot] = chip->data & 0x1f; + chip->am[slot] = (chip->data >> 7) & 0x01; + break; + case 0x70: /* SR */ + chip->sr[slot] = chip->data & 0x1f; + break; + case 0x80: /* SL, RR */ + chip->rr[slot] = chip->data & 0x0f; + chip->sl[slot] = (chip->data >> 4) & 0x0f; + chip->sl[slot] |= (chip->sl[slot] + 1) & 0x10; + break; + case 0x90: /* SSG-EG */ + chip->ssg_eg[slot] = chip->data & 0x0f; + break; + default: + break; + } + } + + /* Channel */ + if (ch_offset[channel] == (chip->address & 0x103)) + { + address = chip->address & 0xfc; + switch (address) + { + case 0xa0: + chip->fnum[channel] = (chip->data & 0xff) | ((chip->reg_a4 & 0x07) << 8); + chip->block[channel] = (chip->reg_a4 >> 3) & 0x07; + chip->kcode[channel] = (chip->block[channel] << 2) | fn_note[chip->fnum[channel] >> 7]; + break; + case 0xa4: + chip->reg_a4 = chip->data & 0xff; + break; + case 0xa8: + chip->fnum_3ch[channel] = (chip->data & 0xff) | ((chip->reg_ac & 0x07) << 8); + chip->block_3ch[channel] = (chip->reg_ac >> 3) & 0x07; + chip->kcode_3ch[channel] = (chip->block_3ch[channel] << 2) | fn_note[chip->fnum_3ch[channel] >> 7]; + break; + case 0xac: + chip->reg_ac = chip->data & 0xff; + break; + case 0xb0: + chip->connect[channel] = chip->data & 0x07; + chip->fb[channel] = (chip->data >> 3) & 0x07; + break; + case 0xb4: + chip->pms[channel] = chip->data & 0x07; + chip->ams[channel] = (chip->data >> 4) & 0x03; + chip->pan_l[channel] = (chip->data >> 7) & 0x01; + chip->pan_r[channel] = (chip->data >> 6) & 0x01; + break; + default: + break; + } + } + } + + if (chip->write_a_en || chip->write_d_en) + { + /* Data */ + if (chip->write_a_en) + { + chip->write_fm_data = 0; + } + + if (chip->write_fm_address && chip->write_d_en) + { + chip->write_fm_data = 1; + } + + /* Address */ + if (chip->write_a_en) + { + if ((chip->write_data & 0xf0) != 0x00) + { + /* FM Write */ + chip->address = chip->write_data; + chip->write_fm_address = 1; + } + else + { + /* SSG write */ + chip->write_fm_address = 0; + } + } + + /* FM Mode */ + /* Data */ + if (chip->write_d_en && (chip->write_data & 0x100) == 0) + { + switch (chip->address) + { + case 0x21: /* LSI test 1 */ + for (i = 0; i < 8; i++) + { + chip->mode_test_21[i] = (chip->write_data >> i) & 0x01; + } + break; + case 0x22: /* LFO control */ + if ((chip->write_data >> 3) & 0x01) + { + chip->lfo_en = 0x7f; + } + else + { + chip->lfo_en = 0; + } + chip->lfo_freq = chip->write_data & 0x07; + break; + case 0x24: /* Timer A */ + chip->timer_a_reg &= 0x03; + chip->timer_a_reg |= (chip->write_data & 0xff) << 2; + break; + case 0x25: + chip->timer_a_reg &= 0x3fc; + chip->timer_a_reg |= chip->write_data & 0x03; + break; + case 0x26: /* Timer B */ + chip->timer_b_reg = chip->write_data & 0xff; + break; + case 0x27: /* CSM, Timer control */ + chip->mode_ch3 = (chip->write_data & 0xc0) >> 6; + chip->mode_csm = chip->mode_ch3 == 2; + chip->timer_a_load = chip->write_data & 0x01; + chip->timer_a_enable = (chip->write_data >> 2) & 0x01; + chip->timer_a_reset = (chip->write_data >> 4) & 0x01; + chip->timer_b_load = (chip->write_data >> 1) & 0x01; + chip->timer_b_enable = (chip->write_data >> 3) & 0x01; + chip->timer_b_reset = (chip->write_data >> 5) & 0x01; + break; + case 0x28: /* Key on/off */ + for (i = 0; i < 4; i++) + { + chip->mode_kon_operator[i] = (chip->write_data >> (4 + i)) & 0x01; + } + if ((chip->write_data & 0x03) == 0x03) + { + /* Invalid address */ + chip->mode_kon_channel = 0xff; + } + else + { + chip->mode_kon_channel = (chip->write_data & 0x03) + ((chip->write_data >> 2) & 1) * 3; + } + break; + case 0x2a: /* DAC data */ + chip->dacdata &= 0x01; + chip->dacdata |= (chip->write_data ^ 0x80) << 1; + break; + case 0x2b: /* DAC enable */ + chip->dacen = chip->write_data >> 7; + break; + case 0x2c: /* LSI test 2 */ + for (i = 0; i < 8; i++) + { + chip->mode_test_2c[i] = (chip->write_data >> i) & 0x01; + } + chip->dacdata &= 0x1fe; + chip->dacdata |= chip->mode_test_2c[3]; + chip->eg_custom_timer = !chip->mode_test_2c[7] && chip->mode_test_2c[6]; + break; + default: + break; + } + } + + /* Address */ + if (chip->write_a_en) + { + chip->write_fm_mode_a = chip->write_data & 0xff; + } + } + + if (chip->write_fm_data) + { + chip->data = chip->write_data & 0xff; + } +} + +void OPN2_PhaseCalcIncrement(ym3438_t *chip) +{ + Bit32u chan = chip->channel; + Bit32u slot = chip->cycles; + Bit32u fnum = chip->pg_fnum; + Bit32u fnum_h = fnum >> 4; + Bit32u fm; + Bit32u basefreq; + Bit8u lfo = chip->lfo_pm; + Bit8u lfo_l = lfo & 0x0f; + Bit8u pms = chip->pms[chan]; + Bit8u dt = chip->dt[slot]; + Bit8u dt_l = dt & 0x03; + Bit8u detune = 0; + Bit8u block, note; + Bit8u sum, sum_h, sum_l; + Bit8u kcode = chip->pg_kcode; + + fnum <<= 1; + /* Apply LFO */ + if (lfo_l & 0x08) + { + lfo_l ^= 0x0f; + } + fm = (fnum_h >> pg_lfo_sh1[pms][lfo_l]) + (fnum_h >> pg_lfo_sh2[pms][lfo_l]); + if (pms > 5) + { + fm <<= pms - 5; + } + fm >>= 2; + if (lfo & 0x10) + { + fnum -= fm; + } + else + { + fnum += fm; + } + fnum &= 0xfff; + + basefreq = (fnum << chip->pg_block) >> 2; + + /* Apply detune */ + if (dt_l) + { + if (kcode > 0x1c) + { + kcode = 0x1c; + } + block = kcode >> 2; + note = kcode & 0x03; + sum = block + 9 + ((dt_l == 3) | (dt_l & 0x02)); + sum_h = sum >> 1; + sum_l = sum & 0x01; + detune = pg_detune[(sum_l << 2) | note] >> (9 - sum_h); + } + if (dt & 0x04) + { + basefreq -= detune; + } + else + { + basefreq += detune; + } + basefreq &= 0x1ffff; + chip->pg_inc[slot] = (basefreq * chip->multi[slot]) >> 1; + chip->pg_inc[slot] &= 0xfffff; +} + +void OPN2_PhaseGenerate(ym3438_t *chip) +{ + Bit32u slot; + /* Mask increment */ + slot = (chip->cycles + 20) % 24; + if (chip->pg_reset[slot]) + { + chip->pg_inc[slot] = 0; + } + /* Phase step */ + slot = (chip->cycles + 19) % 24; + chip->pg_phase[slot] += chip->pg_inc[slot]; + chip->pg_phase[slot] &= 0xfffff; + if (chip->pg_reset[slot] || chip->mode_test_21[3]) + { + chip->pg_phase[slot] = 0; + } +} + +void OPN2_EnvelopeSSGEG(ym3438_t *chip) +{ + Bit32u slot = chip->cycles; + Bit8u direction = 0; + chip->eg_ssg_pgrst_latch[slot] = 0; + chip->eg_ssg_repeat_latch[slot] = 0; + chip->eg_ssg_hold_up_latch[slot] = 0; + chip->eg_ssg_inv[slot] = 0; + if (chip->ssg_eg[slot] & 0x08) + { + direction = chip->eg_ssg_dir[slot]; + if (chip->eg_level[slot] & 0x200) + { + /* Reset */ + if ((chip->ssg_eg[slot] & 0x03) == 0x00) + { + chip->eg_ssg_pgrst_latch[slot] = 1; + } + /* Repeat */ + if ((chip->ssg_eg[slot] & 0x01) == 0x00) + { + chip->eg_ssg_repeat_latch[slot] = 1; + } + /* Inverse */ + if ((chip->ssg_eg[slot] & 0x03) == 0x02) + { + direction ^= 1; + } + if ((chip->ssg_eg[slot] & 0x03) == 0x03) + { + direction = 1; + } + } + /* Hold up */ + if (chip->eg_kon_latch[slot] + && ((chip->ssg_eg[slot] & 0x07) == 0x05 || (chip->ssg_eg[slot] & 0x07) == 0x03)) + { + chip->eg_ssg_hold_up_latch[slot] = 1; + } + direction &= chip->eg_kon[slot]; + chip->eg_ssg_inv[slot] = (chip->eg_ssg_dir[slot] ^ ((chip->ssg_eg[slot] >> 2) & 0x01)) + & chip->eg_kon[slot]; + } + chip->eg_ssg_dir[slot] = direction; + chip->eg_ssg_enable[slot] = (chip->ssg_eg[slot] >> 3) & 0x01; +} + +void OPN2_EnvelopeADSR(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 22) % 24; + + Bit8u nkon = chip->eg_kon_latch[slot]; + Bit8u okon = chip->eg_kon[slot]; + Bit8u kon_event; + Bit8u koff_event; + Bit8u eg_off; + Bit16s level; + Bit16s nextlevel = 0; + Bit16s ssg_level; + Bit8u nextstate = chip->eg_state[slot]; + Bit16s inc = 0; + chip->eg_read[0] = chip->eg_read_inc; + chip->eg_read_inc = chip->eg_inc > 0; + + /* Reset phase generator */ + chip->pg_reset[slot] = (nkon && !okon) || chip->eg_ssg_pgrst_latch[slot]; + + /* KeyOn/Off */ + kon_event = (nkon && !okon) || (okon && chip->eg_ssg_repeat_latch[slot]); + koff_event = okon && !nkon; + + ssg_level = level = (Bit16s)chip->eg_level[slot]; + + if (chip->eg_ssg_inv[slot]) + { + /* Inverse */ + ssg_level = 512 - level; + ssg_level &= 0x3ff; + } + if (koff_event) + { + level = ssg_level; + } + if (chip->eg_ssg_enable[slot]) + { + eg_off = level >> 9; + } + else + { + eg_off = (level & 0x3f0) == 0x3f0; + } + nextlevel = level; + if (kon_event) + { + nextstate = eg_num_attack; + /* Instant attack */ + if (chip->eg_ratemax) + { + nextlevel = 0; + } + else if (chip->eg_state[slot] == eg_num_attack && level != 0 && chip->eg_inc && nkon) + { + inc = (~level << chip->eg_inc) >> 5; + } + } + else + { + switch (chip->eg_state[slot]) + { + case eg_num_attack: + if (level == 0) + { + nextstate = eg_num_decay; + } + else if(chip->eg_inc && !chip->eg_ratemax && nkon) + { + inc = (~level << chip->eg_inc) >> 5; + } + break; + case eg_num_decay: + if ((level >> 5) == chip->eg_sl[1]) + { + nextstate = eg_num_sustain; + } + else if (!eg_off && chip->eg_inc) + { + inc = 1 << (chip->eg_inc - 1); + if (chip->eg_ssg_enable[slot]) + { + inc <<= 2; + } + } + break; + case eg_num_sustain: + case eg_num_release: + if (!eg_off && chip->eg_inc) + { + inc = 1 << (chip->eg_inc - 1); + if (chip->eg_ssg_enable[slot]) + { + inc <<= 2; + } + } + break; + default: + break; + } + if (!nkon) + { + nextstate = eg_num_release; + } + } + if (chip->eg_kon_csm[slot]) + { + nextlevel |= chip->eg_tl[1] << 3; + } + + /* Envelope off */ + if (!kon_event && !chip->eg_ssg_hold_up_latch[slot] && chip->eg_state[slot] != eg_num_attack && eg_off) + { + nextstate = eg_num_release; + nextlevel = 0x3ff; + } + + nextlevel += inc; + + chip->eg_kon[slot] = chip->eg_kon_latch[slot]; + chip->eg_level[slot] = (Bit16u)nextlevel & 0x3ff; + chip->eg_state[slot] = nextstate; +} + +void OPN2_EnvelopePrepare(ym3438_t *chip) +{ + Bit8u rate; + Bit8u sum; + Bit8u inc = 0; + Bit32u slot = chip->cycles; + Bit8u rate_sel; + + /* Prepare increment */ + rate = (chip->eg_rate << 1) + chip->eg_ksv; + + if (rate > 0x3f) + { + rate = 0x3f; + } + + sum = ((rate >> 2) + chip->eg_shift_lock) & 0x0f; + if (chip->eg_rate != 0 && chip->eg_quotient == 2) + { + if (rate < 48) + { + switch (sum) + { + case 12: + inc = 1; + break; + case 13: + inc = (rate >> 1) & 0x01; + break; + case 14: + inc = rate & 0x01; + break; + default: + break; + } + } + else + { + inc = eg_stephi[rate & 0x03][chip->eg_timer_low_lock] + (rate >> 2) - 11; + if (inc > 4) + { + inc = 4; + } + } + } + chip->eg_inc = inc; + chip->eg_ratemax = (rate >> 1) == 0x1f; + + /* Prepare rate & ksv */ + rate_sel = chip->eg_state[slot]; + if ((chip->eg_kon[slot] && chip->eg_ssg_repeat_latch[slot]) + || (!chip->eg_kon[slot] && chip->eg_kon_latch[slot])) + { + rate_sel = eg_num_attack; + } + switch (rate_sel) + { + case eg_num_attack: + chip->eg_rate = chip->ar[slot]; + break; + case eg_num_decay: + chip->eg_rate = chip->dr[slot]; + break; + case eg_num_sustain: + chip->eg_rate = chip->sr[slot]; + break; + case eg_num_release: + chip->eg_rate = (chip->rr[slot] << 1) | 0x01; + break; + default: + break; + } + chip->eg_ksv = chip->pg_kcode >> (chip->ks[slot] ^ 0x03); + if (chip->am[slot]) + { + chip->eg_lfo_am = chip->lfo_am >> eg_am_shift[chip->ams[chip->channel]]; + } + else + { + chip->eg_lfo_am = 0; + } + /* Delay TL & SL value */ + chip->eg_tl[1] = chip->eg_tl[0]; + chip->eg_tl[0] = chip->tl[slot]; + chip->eg_sl[1] = chip->eg_sl[0]; + chip->eg_sl[0] = chip->sl[slot]; +} + +void OPN2_EnvelopeGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 23) % 24; + Bit16u level; + + level = chip->eg_level[slot]; + + if (chip->eg_ssg_inv[slot]) + { + /* Inverse */ + level = 512 - level; + } + if (chip->mode_test_21[5]) + { + level = 0; + } + level &= 0x3ff; + + /* Apply AM LFO */ + level += chip->eg_lfo_am; + + /* Apply TL */ + if (!(chip->mode_csm && chip->channel == 2 + 1)) + { + level += chip->eg_tl[0] << 3; + } + if (level > 0x3ff) + { + level = 0x3ff; + } + chip->eg_out[slot] = level; +} + +void OPN2_UpdateLFO(ym3438_t *chip) +{ + if ((chip->lfo_quotient & lfo_cycles[chip->lfo_freq]) == lfo_cycles[chip->lfo_freq]) + { + chip->lfo_quotient = 0; + chip->lfo_cnt++; + } + else + { + chip->lfo_quotient += chip->lfo_inc; + } + chip->lfo_cnt &= chip->lfo_en; +} + +void OPN2_FMPrepare(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 6) % 24; + Bit32u channel = chip->channel; + Bit16s mod, mod1, mod2; + Bit32u op = slot / 6; + Bit8u connect = chip->connect[channel]; + Bit32u prevslot = (chip->cycles + 18) % 24; + + /* Calculate modulation */ + mod1 = mod2 = 0; + + if (fm_algorithm[op][0][connect]) + { + mod2 |= chip->fm_op1[channel][0]; + } + if (fm_algorithm[op][1][connect]) + { + mod1 |= chip->fm_op1[channel][1]; + } + if (fm_algorithm[op][2][connect]) + { + mod1 |= chip->fm_op2[channel]; + } + if (fm_algorithm[op][3][connect]) + { + mod2 |= chip->fm_out[prevslot]; + } + if (fm_algorithm[op][4][connect]) + { + mod1 |= chip->fm_out[prevslot]; + } + mod = mod1 + mod2; + if (op == 0) + { + /* Feedback */ + mod = mod >> (10 - chip->fb[channel]); + if (!chip->fb[channel]) + { + mod = 0; + } + } + else + { + mod >>= 1; + } + chip->fm_mod[slot] = mod; + + slot = (chip->cycles + 18) % 24; + /* OP1 */ + if (slot / 6 == 0) + { + chip->fm_op1[channel][1] = chip->fm_op1[channel][0]; + chip->fm_op1[channel][0] = chip->fm_out[slot]; + } + /* OP2 */ + if (slot / 6 == 2) + { + chip->fm_op2[channel] = chip->fm_out[slot]; + } +} + +void OPN2_ChGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 18) % 24; + Bit32u channel = chip->channel; + Bit32u op = slot / 6; + Bit32u test_dac = chip->mode_test_2c[5]; + Bit16s acc = chip->ch_acc[channel]; + Bit16s add = test_dac; + Bit16s sum = 0; + if (op == 0 && !test_dac) + { + acc = 0; + } + if (fm_algorithm[op][5][chip->connect[channel]] && !test_dac) + { + add += chip->fm_out[slot] >> 5; + } + sum = acc + add; + /* Clamp */ + if (sum > 255) + { + sum = 255; + } + else if(sum < -256) + { + sum = -256; + } + + if (op == 0 || test_dac) + { + chip->ch_out[channel] = chip->ch_acc[channel]; + } + chip->ch_acc[channel] = sum; +} + +void OPN2_ChOutput(ym3438_t *chip) +{ + Bit32u cycles = chip->cycles; + Bit32u slot = chip->cycles; + Bit32u channel = chip->channel; + Bit32u test_dac = chip->mode_test_2c[5]; + Bit16s out; + Bit16s sign; + Bit32u out_en; + chip->ch_read = chip->ch_lock; + if (slot < 12) + { + /* Ch 4,5,6 */ + channel++; + } + if ((cycles & 3) == 0) + { + if (!test_dac) + { + /* Lock value */ + chip->ch_lock = chip->ch_out[channel]; + } + chip->ch_lock_l = chip->pan_l[channel]; + chip->ch_lock_r = chip->pan_r[channel]; + } + /* Ch 6 */ + if (((cycles >> 2) == 1 && chip->dacen) || test_dac) + { + out = (Bit16s)chip->dacdata; + out <<= 7; + out >>= 7; + } + else + { + out = chip->ch_lock; + } + chip->mol = 0; + chip->mor = 0; + + if (chip_type == ym3438_type_ym2612) + { + out_en = ((cycles & 3) == 3) || test_dac; + /* YM2612 DAC emulation(not verified) */ + sign = out >> 8; + if (out >= 0) + { + out++; + sign++; + } + if (chip->ch_lock_l && out_en) + { + chip->mol = out; + } + else + { + chip->mol = sign; + } + if (chip->ch_lock_r && out_en) + { + chip->mor = out; + } + else + { + chip->mor = sign; + } + /* Amplify signal */ + chip->mol *= 3; + chip->mor *= 3; + } + else + { + out_en = ((cycles & 3) != 0) || test_dac; + /* Discrete YM3438 seems has the ladder effect too */ + if (out >= 0 && chip_type == ym3438_type_discrete) + { + out++; + } + if (chip->ch_lock_l && out_en) + { + chip->mol = out; + } + if (chip->ch_lock_r && out_en) + { + chip->mor = out; + } + } +} + +void OPN2_FMGenerate(ym3438_t *chip) +{ + Bit32u slot = (chip->cycles + 19) % 24; + /* Calculate phase */ + Bit16u phase = (chip->fm_mod[slot] + (chip->pg_phase[slot] >> 10)) & 0x3ff; + Bit16u quarter; + Bit16u level; + Bit16s output; + if (phase & 0x100) + { + quarter = (phase ^ 0xff) & 0xff; + } + else + { + quarter = phase & 0xff; + } + level = logsinrom[quarter]; + /* Apply envelope */ + level += chip->eg_out[slot] << 2; + /* Transform */ + if (level > 0x1fff) + { + level = 0x1fff; + } + output = ((exprom[(level & 0xff) ^ 0xff] | 0x400) << 2) >> (level >> 8); + if (phase & 0x200) + { + output = ((~output) ^ (chip->mode_test_21[4] << 13)) + 1; + } + else + { + output = output ^ (chip->mode_test_21[4] << 13); + } + output <<= 2; + output >>= 2; + chip->fm_out[slot] = output; +} + +void OPN2_DoTimerA(ym3438_t *chip) +{ + Bit16u time; + Bit8u load; + load = chip->timer_a_overflow; + if (chip->cycles == 2) + { + /* Lock load value */ + load |= (!chip->timer_a_load_lock && chip->timer_a_load); + chip->timer_a_load_lock = chip->timer_a_load; + if (chip->mode_csm) + { + /* CSM KeyOn */ + chip->mode_kon_csm = load; + } + else + { + chip->mode_kon_csm = 0; + } + } + /* Load counter */ + if (chip->timer_a_load_latch) + { + time = chip->timer_a_reg; + } + else + { + time = chip->timer_a_cnt; + } + chip->timer_a_load_latch = load; + /* Increase counter */ + if ((chip->cycles == 1 && chip->timer_a_load_lock) || chip->mode_test_21[2]) + { + time++; + } + /* Set overflow flag */ + if (chip->timer_a_reset) + { + chip->timer_a_reset = 0; + chip->timer_a_overflow_flag = 0; + } + else + { + chip->timer_a_overflow_flag |= chip->timer_a_overflow & chip->timer_a_enable; + } + chip->timer_a_overflow = (time >> 10); + chip->timer_a_cnt = time & 0x3ff; +} + +void OPN2_DoTimerB(ym3438_t *chip) +{ + Bit16u time; + Bit8u load; + load = chip->timer_b_overflow; + if (chip->cycles == 2) + { + /* Lock load value */ + load |= (!chip->timer_b_load_lock && chip->timer_b_load); + chip->timer_b_load_lock = chip->timer_b_load; + } + /* Load counter */ + if (chip->timer_b_load_latch) + { + time = chip->timer_b_reg; + } + else + { + time = chip->timer_b_cnt; + } + chip->timer_b_load_latch = load; + /* Increase counter */ + if (chip->cycles == 1) + { + chip->timer_b_subcnt++; + } + if ((chip->timer_b_subcnt == 0x10 && chip->timer_b_load_lock) || chip->mode_test_21[2]) + { + time++; + } + chip->timer_b_subcnt &= 0x0f; + /* Set overflow flag */ + if (chip->timer_b_reset) + { + chip->timer_b_reset = 0; + chip->timer_b_overflow_flag = 0; + } + else + { + chip->timer_b_overflow_flag |= chip->timer_b_overflow & chip->timer_b_enable; + } + chip->timer_b_overflow = (time >> 8); + chip->timer_b_cnt = time & 0xff; +} + +void OPN2_KeyOn(ym3438_t*chip) +{ + Bit32u slot = chip->cycles; + Bit32u chan = chip->channel; + /* Key On */ + chip->eg_kon_latch[slot] = chip->mode_kon[slot]; + chip->eg_kon_csm[slot] = 0; + if (chip->channel == 2 && chip->mode_kon_csm) + { + /* CSM Key On */ + chip->eg_kon_latch[slot] = 1; + chip->eg_kon_csm[slot] = 1; + } + if (chip->cycles == chip->mode_kon_channel) + { + /* OP1 */ + chip->mode_kon[chan] = chip->mode_kon_operator[0]; + /* OP2 */ + chip->mode_kon[chan + 12] = chip->mode_kon_operator[1]; + /* OP3 */ + chip->mode_kon[chan + 6] = chip->mode_kon_operator[2]; + /* OP4 */ + chip->mode_kon[chan + 18] = chip->mode_kon_operator[3]; + } +} + +void OPN2_Reset(ym3438_t *chip, Bit32u rate, Bit32u clock) +{ + Bit32u i, rateratio; + rateratio = (Bit32u)chip->rateratio; + memset(chip, 0, sizeof(ym3438_t)); + for (i = 0; i < 24; i++) + { + chip->eg_out[i] = 0x3ff; + chip->eg_level[i] = 0x3ff; + chip->eg_state[i] = eg_num_release; + chip->multi[i] = 1; + } + for (i = 0; i < 6; i++) + { + chip->pan_l[i] = 1; + chip->pan_r[i] = 1; + } + + if (rate != 0) + { + chip->rateratio = (Bit32s)(Bit32u)((((Bit64u)144 * rate) << RSM_FRAC) / clock); + } + else + { + chip->rateratio = (Bit32s)rateratio; + } +} + +void OPN2_SetChipType(Bit32u type) +{ + chip_type = type; +} + +void OPN2_Clock(ym3438_t *chip, Bit16s *buffer) +{ + Bit32u slot = chip->cycles; + chip->lfo_inc = chip->mode_test_21[1]; + chip->pg_read >>= 1; + chip->eg_read[1] >>= 1; + chip->eg_cycle++; + /* Lock envelope generator timer value */ + if (chip->cycles == 1 && chip->eg_quotient == 2) + { + if (chip->eg_cycle_stop) + { + chip->eg_shift_lock = 0; + } + else + { + chip->eg_shift_lock = chip->eg_shift + 1; + } + chip->eg_timer_low_lock = chip->eg_timer & 0x03; + } + /* Cycle specific functions */ + switch (chip->cycles) + { + case 0: + chip->lfo_pm = chip->lfo_cnt >> 2; + if (chip->lfo_cnt & 0x40) + { + chip->lfo_am = chip->lfo_cnt & 0x3f; + } + else + { + chip->lfo_am = chip->lfo_cnt ^ 0x3f; + } + chip->lfo_am <<= 1; + break; + case 1: + chip->eg_quotient++; + chip->eg_quotient %= 3; + chip->eg_cycle = 0; + chip->eg_cycle_stop = 1; + chip->eg_shift = 0; + chip->eg_timer_inc |= chip->eg_quotient >> 1; + chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; + chip->eg_timer_inc = chip->eg_timer >> 12; + chip->eg_timer &= 0xfff; + break; + case 2: + chip->pg_read = chip->pg_phase[21] & 0x3ff; + chip->eg_read[1] = chip->eg_out[0]; + break; + case 13: + chip->eg_cycle = 0; + chip->eg_cycle_stop = 1; + chip->eg_shift = 0; + chip->eg_timer = chip->eg_timer + chip->eg_timer_inc; + chip->eg_timer_inc = chip->eg_timer >> 12; + chip->eg_timer &= 0xfff; + break; + case 23: + chip->lfo_inc |= 1; + break; + } + chip->eg_timer &= ~(chip->mode_test_21[5] << chip->eg_cycle); + if (((chip->eg_timer >> chip->eg_cycle) | (chip->pin_test_in & chip->eg_custom_timer)) & chip->eg_cycle_stop) + { + chip->eg_shift = chip->eg_cycle; + chip->eg_cycle_stop = 0; + } + + OPN2_DoIO(chip); + + OPN2_DoTimerA(chip); + OPN2_DoTimerB(chip); + OPN2_KeyOn(chip); + + OPN2_ChOutput(chip); + OPN2_ChGenerate(chip); + + OPN2_FMPrepare(chip); + OPN2_FMGenerate(chip); + + OPN2_PhaseGenerate(chip); + OPN2_PhaseCalcIncrement(chip); + + OPN2_EnvelopeADSR(chip); + OPN2_EnvelopeGenerate(chip); + OPN2_EnvelopeSSGEG(chip); + OPN2_EnvelopePrepare(chip); + + /* Prepare fnum & block */ + if (chip->mode_ch3) + { + /* Channel 3 special mode */ + switch (slot) + { + case 1: /* OP1 */ + chip->pg_fnum = chip->fnum_3ch[1]; + chip->pg_block = chip->block_3ch[1]; + chip->pg_kcode = chip->kcode_3ch[1]; + break; + case 7: /* OP3 */ + chip->pg_fnum = chip->fnum_3ch[0]; + chip->pg_block = chip->block_3ch[0]; + chip->pg_kcode = chip->kcode_3ch[0]; + break; + case 13: /* OP2 */ + chip->pg_fnum = chip->fnum_3ch[2]; + chip->pg_block = chip->block_3ch[2]; + chip->pg_kcode = chip->kcode_3ch[2]; + break; + case 19: /* OP4 */ + default: + chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; + chip->pg_block = chip->block[(chip->channel + 1) % 6]; + chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; + break; + } + } + else + { + chip->pg_fnum = chip->fnum[(chip->channel + 1) % 6]; + chip->pg_block = chip->block[(chip->channel + 1) % 6]; + chip->pg_kcode = chip->kcode[(chip->channel + 1) % 6]; + } + + OPN2_UpdateLFO(chip); + OPN2_DoRegWrite(chip); + chip->cycles = (chip->cycles + 1) % 24; + chip->channel = chip->cycles % 6; + + buffer[0] = chip->mol; + buffer[1] = chip->mor; +} + +void OPN2_Write(ym3438_t *chip, Bit32u port, Bit8u data) +{ + port &= 3; + chip->write_data = ((port << 7) & 0x100) | data; + if (port & 1) + { + /* Data */ + chip->write_d |= 1; + } + else + { + /* Address */ + chip->write_a |= 1; + } +} + +void OPN2_SetTestPin(ym3438_t *chip, Bit32u value) +{ + chip->pin_test_in = value & 1; +} + +Bit32u OPN2_ReadTestPin(ym3438_t *chip) +{ + if (!chip->mode_test_2c[7]) + { + return 0; + } + return chip->cycles == 23; +} + +Bit32u OPN2_ReadIRQPin(ym3438_t *chip) +{ + return chip->timer_a_overflow_flag | chip->timer_b_overflow_flag; +} + +Bit8u OPN2_Read(ym3438_t *chip, Bit32u port) +{ + if ((port & 3) == 0 || chip_type == ym3438_type_asic) + { + if (chip->mode_test_21[6]) + { + /* Read test data */ + Bit32u slot = (chip->cycles + 18) % 24; + Bit16u testdata = ((chip->pg_read & 0x01) << 15) + | ((chip->eg_read[chip->mode_test_21[0]] & 0x01) << 14); + if (chip->mode_test_2c[4]) + { + testdata |= chip->ch_read & 0x1ff; + } + else + { + testdata |= chip->fm_out[slot] & 0x3fff; + } + if (chip->mode_test_21[7]) + { + return testdata & 0xff; + } + else + { + return testdata >> 8; + } + } + else + { + return (Bit8u)(chip->busy << 7) | (Bit8u)(chip->timer_b_overflow_flag << 1) + | (Bit8u)chip->timer_a_overflow_flag; + } + } + return 0; +} + +void OPN2_WriteBuffered(ym3438_t *chip, Bit32u port, Bit8u data) +{ + Bit64u time1, time2; + Bit16s buffer[2]; + Bit64u skip; + + if (chip->writebuf[chip->writebuf_last].port & 0x04) + { + OPN2_Write(chip, chip->writebuf[chip->writebuf_last].port & 0X03, + chip->writebuf[chip->writebuf_last].data); + + chip->writebuf_cur = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; + skip = chip->writebuf[chip->writebuf_last].time - chip->writebuf_samplecnt; + chip->writebuf_samplecnt = chip->writebuf[chip->writebuf_last].time; + while (skip--) + { + OPN2_Clock(chip, buffer); + } + } + + chip->writebuf[chip->writebuf_last].port = (port & 0x03) | 0x04; + chip->writebuf[chip->writebuf_last].data = data; + time1 = chip->writebuf_lasttime + OPN_WRITEBUF_DELAY; + time2 = chip->writebuf_samplecnt; + + if (time1 < time2) + { + time1 = time2; + } + + chip->writebuf[chip->writebuf_last].time = time1; + chip->writebuf_lasttime = time1; + chip->writebuf_last = (chip->writebuf_last + 1) % OPN_WRITEBUF_SIZE; +} + +void OPN2_Generate(ym3438_t *chip, Bit16s *buf) +{ + Bit32u i; + Bit16s buffer[2]; + Bit32u mute; + + buf[0] = 0; + buf[1] = 0; + + for (i = 0; i < 24; i++) + { + switch (chip->cycles >> 2) + { + case 0: /* Ch 2 */ + mute = chip->mute[1]; + break; + case 1: /* Ch 6, DAC */ + mute = chip->mute[5 + chip->dacen]; + break; + case 2: /* Ch 4 */ + mute = chip->mute[3]; + break; + case 3: /* Ch 1 */ + mute = chip->mute[0]; + break; + case 4: /* Ch 5 */ + mute = chip->mute[4]; + break; + case 5: /* Ch 3 */ + mute = chip->mute[2]; + break; + default: + mute = 0; + break; + } + OPN2_Clock(chip, buffer); + if (!mute) + { + buf[0] += buffer[0]; + buf[1] += buffer[1]; + } + + while (chip->writebuf[chip->writebuf_cur].time <= chip->writebuf_samplecnt) + { + if (!(chip->writebuf[chip->writebuf_cur].port & 0x04)) + { + break; + } + chip->writebuf[chip->writebuf_cur].port &= 0x03; + OPN2_Write(chip, chip->writebuf[chip->writebuf_cur].port, + chip->writebuf[chip->writebuf_cur].data); + chip->writebuf_cur = (chip->writebuf_cur + 1) % OPN_WRITEBUF_SIZE; + } + chip->writebuf_samplecnt++; + } +} + +void OPN2_GenerateResampled(ym3438_t *chip, Bit16s *buf) +{ + Bit16s buffer[2]; + + while (chip->samplecnt >= chip->rateratio) + { + chip->oldsamples[0] = chip->samples[0]; + chip->oldsamples[1] = chip->samples[1]; + OPN2_Generate(chip, buffer); + chip->samples[0] = buffer[0] * 11; + chip->samples[1] = buffer[1] * 11; + chip->samplecnt -= chip->rateratio; + } + buf[0] = (Bit16s)(((chip->oldsamples[0] * (chip->rateratio - chip->samplecnt) + + chip->samples[0] * chip->samplecnt) / chip->rateratio)>>1); + buf[1] = (Bit16s)(((chip->oldsamples[1] * (chip->rateratio - chip->samplecnt) + + chip->samples[1] * chip->samplecnt) / chip->rateratio)>>1); + chip->samplecnt += 1 << RSM_FRAC; +} + +void OPN2_GenerateStream(ym3438_t *chip, Bit16s *output, Bit32u numsamples) +{ + Bit32u i; + Bit16s buffer[2]; + + for (i = 0; i < numsamples; i++) + { + OPN2_GenerateResampled(chip, buffer); + *output++ = buffer[0]; + *output++ = buffer[1]; + } +} + +void OPN2_GenerateStreamMix(ym3438_t *chip, Bit16s *output, Bit32u numsamples) +{ + Bit32u i; + Bit16s buffer[2]; + + for (i = 0; i < numsamples; i++) + { + OPN2_GenerateResampled(chip, buffer); + *output++ += buffer[0]; + *output++ += buffer[1]; + } +} + + +void OPN2_SetOptions(Bit8u flags) +{ + switch ((flags >> 3) & 0x03) + { + case 0x00: /* YM2612 */ + default: + OPN2_SetChipType(ym3438_type_ym2612); + break; + case 0x01: /* ASIC YM3438 */ + OPN2_SetChipType(ym3438_type_asic); + break; + case 0x02: /* Discrete YM3438 */ + OPN2_SetChipType(ym3438_type_discrete); + break; + } +} + +void OPN2_SetMute(ym3438_t *chip, Bit32u mute) +{ + Bit32u i; + for (i = 0; i < 7; i++) + { + chip->mute[i] = (mute >> i) & 0x01; + } +} + + +} // Ym2612_NukedImpl + + +Ym2612_Nuked_Emu::Ym2612_Nuked_Emu() +{ + Ym2612_NukedImpl::OPN2_SetChipType( Ym2612_NukedImpl::ym3438_type_asic ); + impl = new Ym2612_NukedImpl::ym3438_t; +} + +Ym2612_Nuked_Emu::~Ym2612_Nuked_Emu() +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( chip_r ) delete chip_r; +} + +const char *Ym2612_Nuked_Emu::set_rate(double sample_rate, double clock_rate) +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( !chip_r ) + return "Out of memory"; + prev_sample_rate = sample_rate; + prev_clock_rate = clock_rate; + Ym2612_NukedImpl::OPN2_Reset( chip_r, static_cast(sample_rate), static_cast(clock_rate) ); + return 0; +} + +void Ym2612_Nuked_Emu::reset() +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( !chip_r ) Ym2612_NukedImpl::OPN2_Reset( chip_r, static_cast(prev_sample_rate), static_cast(prev_clock_rate) ); +} + +void Ym2612_Nuked_Emu::mute_voices(int mask) +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( chip_r ) Ym2612_NukedImpl::OPN2_SetMute( chip_r, mask ); +} + +void Ym2612_Nuked_Emu::write0(int addr, int data) +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( !chip_r ) return; + Ym2612_NukedImpl::OPN2_WriteBuffered( chip_r, 0, static_cast(addr) ); + Ym2612_NukedImpl::OPN2_WriteBuffered( chip_r, 1, static_cast(data) ); +} + +void Ym2612_Nuked_Emu::write1(int addr, int data) +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( !chip_r ) return; + Ym2612_NukedImpl::OPN2_WriteBuffered( chip_r, 0 + 2, static_cast(addr) ); + Ym2612_NukedImpl::OPN2_WriteBuffered( chip_r, 1 + 2, static_cast(data) ); +} + +void Ym2612_Nuked_Emu::run(int pair_count, Ym2612_Nuked_Emu::sample_t *out) +{ + Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); + if ( !chip_r ) return; + Ym2612_NukedImpl::OPN2_GenerateStreamMix(chip_r, out, pair_count); +} diff -Nru game-music-emu-0.6.2/gme/Ym2612_Nuked.h game-music-emu-0.6.3/gme/Ym2612_Nuked.h --- game-music-emu-0.6.2/gme/Ym2612_Nuked.h 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/gme/Ym2612_Nuked.h 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,41 @@ +// YM2612 FM sound chip emulator interface + +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifndef YM2612_EMU_H +#define YM2612_EMU_H + +typedef void Ym2612_Nuked_Impl; + +class Ym2612_Nuked_Emu { + Ym2612_Nuked_Impl* impl; + double prev_sample_rate; + double prev_clock_rate; +public: + Ym2612_Nuked_Emu(); + ~Ym2612_Nuked_Emu(); + + // Set output sample rate and chip clock rates, in Hz. Returns non-zero + // if error. + const char* set_rate( double sample_rate, double clock_rate ); + + // Reset to power-up state + void reset(); + + // Mute voice n if bit n (1 << n) of mask is set + enum { channel_count = 6 }; + void mute_voices( int mask ); + + // Write addr to register 0 then data to register 1 + void write0( int addr, int data ); + + // Write addr to register 2 then data to register 3 + void write1( int addr, int data ); + + // Run and add pair_count samples into current output buffer contents + typedef short sample_t; + enum { out_chan_count = 2 }; // stereo + void run( int pair_count, sample_t* out ); +}; + +#endif + diff -Nru game-music-emu-0.6.2/gme.txt game-music-emu-0.6.3/gme.txt --- game-music-emu-0.6.2/gme.txt 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/gme.txt 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -Game_Music_Emu 0.6.2 +Game_Music_Emu 0.6.3 -------------------- Author : Shay Green Maintainer : Michael Pyne diff -Nru game-music-emu-0.6.2/license.gpl2.txt game-music-emu-0.6.3/license.gpl2.txt --- game-music-emu-0.6.2/license.gpl2.txt 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/license.gpl2.txt 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff -Nru game-music-emu-0.6.2/player/Audio_Scope.cpp game-music-emu-0.6.3/player/Audio_Scope.cpp --- game-music-emu-0.6.2/player/Audio_Scope.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/player/Audio_Scope.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,9 +1,11 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Audio_Scope.h" #include #include +#include +#include /* Copyright (C) 2005-2006 by Shay Green. Permission is hereby granted, free of charge, to any person obtaining a copy of this software module and associated @@ -20,179 +22,138 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -int const step_bits = 8; -int const step_unit = 1 << step_bits; -int const erase_color = 1; -int const draw_color = 2; +/* Copyright (c) 2019 by Michael Pyne, under the same terms as above. */ + +// Returns largest power of 2 that is <= the given value. +// From Henry Warren's book "Hacker's Delight" +static unsigned largest_power_of_2_within(unsigned x) +{ + static_assert(sizeof(x) <= 4, "This code doesn't work on 64-bit int"); + + // Set all bits less significant than most significant bit that's set + // e.g. 0b00100111 -> 0b00111111 + x = x | (x >> 1); + x = x | (x >> 2); + x = x | (x >> 4); + x = x | (x >> 8); + x = x | (x >> 16); + + // Clear all set bits besides the highest one. + return x - (x >> 1); +} + +// ============= +// Error helpers +// ============= + +// If the given SDL return code is an error and if so returns a string +// with an explanation based on the provided explanation and SDL library +std::string check_sdl( int ret_code, const char *explanation ) +{ + static std::string empty; + if ( ret_code >= 0 ) + return empty; + + std::stringstream outstream; + outstream << explanation << " " << SDL_GetError(); + return outstream.str(); +} + +// Overload of above +std::string check_sdl( const void *ptr, const char *explanation ) +{ + return check_sdl( ptr ? 0 : -1, explanation ); +} + +#define RETURN_SDL_ERR(res,msg) do { \ + auto check_res = check_sdl( (res), (msg) ); \ + if( !check_res.empty() ) { \ + return check_res; \ + } \ +} while (0) + +// =========== +// Audio_Scope +// =========== Audio_Scope::Audio_Scope() { - surface = 0; - buf = 0; } Audio_Scope::~Audio_Scope() { - free( buf ); + free( scope_lines ); - if ( surface ) - SDL_FreeSurface( surface ); + if ( window_renderer ) + SDL_DestroyRenderer( window_renderer ); + if ( window ) + SDL_DestroyWindow( window ); } -const char* Audio_Scope::init( int width, int height ) +std::string Audio_Scope::init( int width, int height ) { - assert( height <= 256 ); - assert( !buf ); // can only call init() once + assert( height <= 16384 ); + assert( !scope_lines ); // can only call init() once - buf = (byte*) calloc( width * sizeof *buf, 1 ); - if ( !buf ) + scope_height = height; + scope_lines = reinterpret_cast( calloc( width, sizeof( SDL_Point ) ) ); + if ( !scope_lines ) return "Out of memory"; - low_y = 0; - high_y = height; buf_size = width; - for ( sample_shift = 6; sample_shift < 14; ) + for ( sample_shift = 1; sample_shift < 14; ) if ( ((0x7FFFL * 2) >> sample_shift++) < height ) break; - v_offset = height / 2 - (0x10000 >> sample_shift); - - screen = SDL_SetVideoMode( width, height, 0, 0 ); - if ( !screen ) - return "Couldn't set video mode"; - - surface = SDL_CreateRGBSurface( SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0 ); - if ( !screen ) - return "Couldn't create surface"; + v_offset = (height - largest_power_of_2_within(height)) / 2; - static SDL_Color palette [2] = { {0, 0, 0}, {0, 255, 0} }; - SDL_SetColors( surface, palette, 1, 2 ); + // What the user will see + window = SDL_CreateWindow( "libgme sample player", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + width, height, + 0 /* no flags */ ); + RETURN_SDL_ERR( window, "Couldn't create output window" ); + + // Render object used to update window (perhaps in video or GPU ram) + window_renderer = SDL_CreateRenderer( window, -1, 0 /* no flags */ ); + RETURN_SDL_ERR( window_renderer, "Couldn't create renderer for output window" ); - return 0; // success + return std::string(); // success } -const char* Audio_Scope::draw( const short* in, long count, double step ) +const char* Audio_Scope::draw( const short* in, long count, int step ) { - int low = low_y; - int high = high_y; - if ( count >= buf_size ) { count = buf_size; - low_y = 0x7FFF; - high_y = 0; } - if ( SDL_LockSurface( surface ) < 0 ) - return "Couldn't lock surface"; - render( in, count, (long) (step * step_unit) ); - SDL_UnlockSurface( surface ); - - if ( low > low_y ) - low = low_y; - - if ( high < high_y ) - high = high_y; - - SDL_Rect r; - r.x = 0; - r.w = buf_size; - r.y = low + v_offset; - r.h = high - low + 1; - - if ( SDL_BlitSurface( surface, &r, screen, &r ) < 0 ) - return "Blit to screen failed"; - - if ( SDL_Flip( screen ) < 0 ) - return "Couldn't flip screen"; - + SDL_SetRenderDrawColor( window_renderer, 0, 0, 0, 255 ); + SDL_RenderClear( window_renderer ); + + render( in, count, step ); + SDL_SetRenderDrawColor( window_renderer, 0, 255, 0, 255 ); + SDL_RenderDrawLines( window_renderer, scope_lines, count ); + + SDL_RenderPresent( window_renderer ); + return 0; // success } -void Audio_Scope::render( short const* in, long count, long step ) +void Audio_Scope::render( short const* in, long count, int step ) { - byte* old_pos = buf; - long surface_pitch = surface->pitch; - byte* out = (byte*) surface->pixels + v_offset * surface_pitch; - int old_erase = *old_pos; - int old_draw = 0; - long in_pos = 0; - - int low_y = this->low_y; - int high_y = this->high_y; - int half_step = (step + step_unit / 2) >> (step_bits + 1); - - while ( count-- ) + for( long i = 0; i < count; i++ ) { - // Line drawing/erasing starts at previous sample and ends one short of - // current sample, except when previous and current are the same. - - // Extra read on the last iteration of line loops will always be at the - // height of the next sample, and thus within the gworld bounds. - - // Erase old line - { - int delta = *old_pos - old_erase; - int offset = old_erase * surface_pitch; - old_erase += delta; - - int next_line = surface_pitch; - if ( delta < 0 ) - { - delta = -delta; - next_line = -surface_pitch; - } - - do - { - out [offset] = erase_color; - offset += next_line; - } - while ( delta-- > 1 ); - } - - // Draw new line and put in old_buf - { - - int in_whole = in_pos >> step_bits; - int sample = (0x7FFF * 2 - in [in_whole] - in [in_whole + half_step]) >> sample_shift; - if ( !in_pos ) - old_draw = sample; - in_pos += step; - - int delta = sample - old_draw; - int offset = old_draw * surface_pitch; - old_draw += delta; - - int next_line = surface_pitch; - if ( delta < 0 ) - { - delta = -delta; - next_line = -surface_pitch; - } - - *old_pos++ = sample; - - // min/max updating can be interleved anywhere - - if ( low_y > sample ) - low_y = sample; - - do - { - out [offset] = draw_color; - offset += next_line; - } - while ( delta-- > 1 ); - - if ( high_y < sample ) - high_y = sample; - } - - out++; + // Average left, right channels + int sample = (0x7FFF * 2 - in [i * step] - in [i * step + 1]) >> sample_shift; + scope_lines [i].x = i; + scope_lines [i].y = sample + v_offset; } - - this->low_y = low_y; - this->high_y = high_y; +} + +void Audio_Scope::set_caption( const char* caption ) +{ + SDL_SetWindowTitle( window, caption ); } diff -Nru game-music-emu-0.6.2/player/Audio_Scope.h game-music-emu-0.6.3/player/Audio_Scope.h --- game-music-emu-0.6.2/player/Audio_Scope.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/player/Audio_Scope.h 2020-02-25 01:28:10.000000000 +0000 @@ -5,32 +5,38 @@ #include "SDL.h" +#include + class Audio_Scope { public: typedef const char* error_t; - // Initialize scope window of specified size. Height must be 256 or less. - error_t init( int width, int height ); + // Initialize scope window of specified size. Height must be 16384 or less. + // If result is not an empty string, it is an error message + std::string init( int width, int height ); // Draw at most 'count' samples from 'in', skipping 'step' samples after - // each sample drawn. Step can be less than 1.0. - error_t draw( const short* in, long count, double step = 1.0 ); + // each sample drawn. Step should be 2 but wouldn't be hard to adapt + // to be 1. + error_t draw( const short* in, long count, int step = 2 ); Audio_Scope(); ~Audio_Scope(); + + void set_caption( const char* caption ); private: typedef unsigned char byte; - SDL_Surface* screen; - SDL_Surface* surface; + SDL_Window* window; + SDL_Renderer* window_renderer; byte* buf; + SDL_Point* scope_lines = nullptr; // lines to be drawn each frame int buf_size; + int scope_height; int sample_shift; - int low_y; - int high_y; int v_offset; - void render( short const* in, long count, long step ); + void render( short const* in, long count, int step ); }; #endif diff -Nru game-music-emu-0.6.2/player/CMakeLists.txt game-music-emu-0.6.3/player/CMakeLists.txt --- game-music-emu-0.6.2/player/CMakeLists.txt 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/player/CMakeLists.txt 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -find_package(SDL QUIET) +find_package(SDL2) set(player_SRCS Audio_Scope.cpp Audio_Scope.h @@ -6,15 +6,15 @@ Music_Player.h player.cpp) -if(SDL_FOUND) - message(" ** SDL library located, player demo is available to be built in the /player directory") +if(SDL2_FOUND) + message(" ** SDL 2 library located, player demo is available to be built in the /player directory") - include_directories(${SDL_INCLUDE_DIR} ${CMAKE_CURRENT_SOURCE_DIR} + include_directories(${SDL2_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR} "${CMAKE_HOME_DIRECTORY}" "${CMAKE_HOME_DIRECTORY}/gme" "${CMAKE_BINARY_DIR}/gme") add_executable(gme_player ${player_SRCS}) - target_link_libraries(gme_player ${SDL_LIBRARY} gme) + target_link_libraries(gme_player ${SDL2_LIBRARIES} gme) # Is not to be installed though else() diff -Nru game-music-emu-0.6.2/player/Music_Player.cpp game-music-emu-0.6.3/player/Music_Player.cpp --- game-music-emu-0.6.2/player/Music_Player.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/player/Music_Player.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Music_Player.h" @@ -20,6 +20,7 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#undef RETURN_ERR #define RETURN_ERR( expr ) \ do {\ gme_err_t err_ = (expr);\ @@ -28,7 +29,9 @@ } while ( 0 ) // Number of audio buffers per second. Adjust if you encounter audio skipping. -const int fill_rate = 45; +// Note that this sets the floor on how often you'll see changes to the audio +// scope +const int fill_rate = 80; // Simple sound driver using SDL typedef void (*sound_callback_t)( void* data, short* out, int count ); @@ -176,6 +179,11 @@ resume(); } +void Music_Player::set_fadeout( bool fade ) +{ + gme_set_fade( emu_, fade ? track_info_->length : -1 ); +} + void Music_Player::fill_buffer( void* data, sample_t* out, int count ) { Music_Player* self = (Music_Player*) data; @@ -195,7 +203,7 @@ static sound_callback_t sound_callback; static void* sound_callback_data; -static void sdl_callback( void* data, Uint8* out, int count ) +static void sdl_callback( void* /* data */, Uint8* out, int count ) { if ( sound_callback ) sound_callback( sound_callback_data, (short*) out, count / 2 ); diff -Nru game-music-emu-0.6.2/player/Music_Player.h game-music-emu-0.6.3/player/Music_Player.h --- game-music-emu-0.6.2/player/Music_Player.h 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/player/Music_Player.h 2020-02-25 01:28:10.000000000 +0000 @@ -1,6 +1,6 @@ // Simple game music file player -// Game_Music_Emu 0.6.0 +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #ifndef MUSIC_PLAYER_H #define MUSIC_PLAYER_H @@ -48,6 +48,10 @@ // Set voice muting bitmask void mute_voices( int ); + + // Toggle whether fadeout is used or not. If used, stops at track length, + // if not used, loop forever + void set_fadeout( bool do_fade ); // Set buffer to copy samples from each buffer into, or NULL to disable typedef short sample_t; diff -Nru game-music-emu-0.6.2/player/player.cpp game-music-emu-0.6.3/player/player.cpp --- game-music-emu-0.6.2/player/player.cpp 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/player/player.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -5,12 +5,18 @@ Left/Right Change track Space Pause/unpause E Normal/slight stereo echo/more stereo echo -A Enable/disable accurate emulation +A Enable/disable accurate emulation +L Toggle track looping (infinite playback) -/= Adjust tempo 1-9 Toggle channel on/off 0 Reset tempo and turn channels back on */ -int const scope_width = 512; +// Make ISO C99 symbols available for snprintf, define must be set before any +// system header includes +#define _ISOC99_SOURCE 1 + +int const scope_width = 1024; +int const scope_height = 512; #include "Music_Player.h" #include "Audio_Scope.h" @@ -33,14 +39,14 @@ if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO ) < 0 ) exit( EXIT_FAILURE ); atexit( SDL_Quit ); - SDL_EnableKeyRepeat( 500, 80 ); // Init scope scope = new Audio_Scope; if ( !scope ) handle_error( "Out of memory" ); - if ( scope->init( scope_width, 256 ) ) - handle_error( "Couldn't initialize scope" ); + std::string err_msg = scope->init( scope_width, scope_height ); + if ( !err_msg.empty() ) + handle_error( err_msg.c_str() ); memset( scope_buf, 0, sizeof scope_buf ); // Create player @@ -73,10 +79,12 @@ } char title [512]; - sprintf( title, "%s: %d/%d %s (%ld:%02ld)", + if ( 0 < snprintf( title, sizeof title, "%s: %d/%d %s (%ld:%02ld)", game, track, player->track_count(), player->track_info().song, - seconds / 60, seconds % 60 ); - SDL_WM_SetCaption( title, title ); + seconds / 60, seconds % 60 ) ) + { + scope->set_caption( title ); + } } int main( int argc, char** argv ) @@ -94,11 +102,10 @@ bool running = true; double stereo_depth = 0.0; bool accurate = false; + bool fading_out = true; int muting_mask = 0; while ( running ) { - SDL_Delay( 1000 / 100 ); - // Update scope scope->draw( scope_buf, scope_width, 2 ); @@ -172,6 +179,11 @@ player->set_stereo_depth( stereo_depth ); break; + case SDLK_l: // toggle loop + player->set_fadeout( fading_out = !fading_out ); + printf( "%s\n", fading_out ? "Will stop at track end" : "Playing forever" ); + break; + case SDLK_0: // reset tempo and muting tempo = 1.0; muting_mask = 0; @@ -188,6 +200,8 @@ } } } + + SDL_Delay( 1000 / 100 ); // Sets 'frame rate' } // Cleanup @@ -205,7 +219,7 @@ char str [256]; sprintf( str, "Error: %s", error ); fprintf( stderr, "%s\n", str ); - SDL_WM_SetCaption( str, str ); + scope->set_caption( str ); // wait for keyboard or mouse activity SDL_Event e; diff -Nru game-music-emu-0.6.2/readme.txt game-music-emu-0.6.3/readme.txt --- game-music-emu-0.6.2/readme.txt 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/readme.txt 2020-02-25 01:28:10.000000000 +0000 @@ -1,4 +1,4 @@ -Game_Music_Emu 0.6.2: Game Music Emulators +Game_Music_Emu 0.6.3: Game Music Emulators ------------------------------------------ Game_Music_Emu is a collection of video game music file emulators that support the following formats and systems: @@ -37,6 +37,9 @@ Website: https://bitbucket.org/mpyne/game-music-emu/wiki/Home License: GNU Lesser General Public License (LGPL) +Note: When you will use MAME YM2612 emulator, the license of library +will be GNU General Public License (GPL) v2.0+! + Current Maintainer: Michael Pyne Getting Started @@ -190,8 +193,13 @@ Sms_Apu.cpp Common Sega emulator files Sms_Apu.h Sms_Oscs.h - Ym2612_Emu.cpp Ym2612_Emu.h + Ym2612_GENS.cpp GENS 2.10 YM2612 emulator (LGPLv2.1+ license) + Ym2612_GENS.h + Ym2612_MAME.cpp MAME YM2612 emulator (GPLv2.0+ license) + Ym2612_MAME.h + Ym2612_Nuked.cpp Nuked OPN2 emulator (LGPLv2.1+ license) + Ym2612_Nuked.h Dual_Resampler.cpp Dual_Resampler.h Fir_Resampler.cpp @@ -218,7 +226,7 @@ Multi_Buffer.cpp Data_Reader.h Data_Reader.cpp - + CMakeLists.txt CMake build rules @@ -226,6 +234,8 @@ ----- Game_Music_Emu library copyright (C) 2003-2009 Shay Green. Sega Genesis YM2612 emulator copyright (C) 2002 Stephane Dallongeville. +MAME YM2612 emulator copyright (C) 2003 Jarek Burczynski, Tatsuyuki Satoh +Nuked OPN2 emulator copyright (C) 2017 Alexey Khokholov (Nuke.YKT) -- Shay Green diff -Nru game-music-emu-0.6.2/test/basics.c game-music-emu-0.6.3/test/basics.c --- game-music-emu-0.6.2/test/basics.c 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/test/basics.c 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,61 @@ +#define _ISOC99_SOURCE +#include "../gme/gme.h" + +#include "Wave_Writer.h" /* wave_ functions for writing sound file */ +#include +#include +#include + +void handle_error( const char* str ); + +int main(int argc, char *argv[]) +{ + if ( argc < 3 ) { + return 1; + } + const char *filename = argv[1]; + const char *outname = argv[2]; + + long sample_rate = 44100; /* number of samples per second */ + int track = 0; + + /* Open music file in new emulator */ + Music_Emu* emu; + handle_error( gme_open_file( filename, &emu, sample_rate ) ); + + /* Start track */ + handle_error( gme_start_track( emu, track ) ); + + /* Begin writing to wave file */ + wave_open( sample_rate, outname ); + wave_enable_stereo(); + + /* Record 3 minutes of track */ + while ( gme_tell( emu ) < 180 * 1000L ) + { + /* Sample buffer */ + #define buf_size 16384 /* can be any multiple of 2 */ + short buf [buf_size]; + + /* Fill sample buffer */ + handle_error( gme_play( emu, buf_size, buf ) ); + + /* Write samples to wave file */ + wave_write( buf, buf_size ); + } + + /* Cleanup */ + gme_delete( emu ); + wave_close(); + + return 0; +} + +void handle_error( const char* str ) +{ + if ( str ) + { + printf( "Error: %s\n", str ); + exit( EXIT_FAILURE ); + } +} diff -Nru game-music-emu-0.6.2/test/basics_mem.c game-music-emu-0.6.3/test/basics_mem.c --- game-music-emu-0.6.2/test/basics_mem.c 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/test/basics_mem.c 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,86 @@ +#define _ISOC99_SOURCE +#include "../gme/gme.h" + +#include "Wave_Writer.h" /* wave_ functions for writing sound file */ +#include +#include + +void handle_error( const char* str ); + +char * dump_file(const char*file_path, size_t *size) +{ + FILE *in = fopen(file_path, "rb"); + char *buffer = NULL; + if (!in) + return NULL; + fseek(in, 0, SEEK_END); + (*size) = (size_t)ftell(in); + fseek(in, 0, SEEK_SET); + buffer = (char*)malloc(*size); + fread(buffer, 1, *size, in); + return buffer; +} + +int main(int argc, char *argv[]) +{ + if ( argc < 3 ) { + return 1; + } + const char *filename = argv[1]; + const char *outname = argv[2]; + + int sample_rate = 44100; /* number of samples per second */ + /* index of track to play (0 = first) */ + int track = argc >= 3 ? atoi(argv[2]) : 0; + + size_t file_size = 0; + char *file_data = dump_file(filename, &file_size); + if (!file_data) + { + printf( "Error: Can't dump %s!\n", filename ); + exit( EXIT_FAILURE ); + } + + /* Open music file in new emulator */ + Music_Emu* emu; + handle_error( gme_open_data(file_data, (long)file_size, &emu, sample_rate) ); + /* File dump is no more needed */ + free(file_data); + + /* Start track */ + handle_error( gme_start_track( emu, track ) ); + + /* Begin writing to wave file */ + wave_open( sample_rate, outname ); + wave_enable_stereo(); + + /* Record 3 minutes of track */ + while ( gme_tell( emu ) < 180 * 1000L ) + { + /* Sample buffer */ + #define buf_size 16384 /* can be any multiple of 2 */ + short buf [buf_size]; + + /* Fill sample buffer */ + handle_error( gme_play( emu, buf_size, buf ) ); + + /* Write samples to wave file */ + wave_write( buf, buf_size ); + } + + /* Cleanup */ + gme_delete( emu ); + wave_close(); + + return 0; +} + +void handle_error( const char* str ) +{ + if ( str ) + { + printf( "Error: %s\n", str ); + exit( EXIT_FAILURE ); + } +} + diff -Nru game-music-emu-0.6.2/test/Makefile game-music-emu-0.6.3/test/Makefile --- game-music-emu-0.6.2/test/Makefile 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/test/Makefile 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,27 @@ +# Add -I flags if you need them +CXXFLAGS := -O2 +SRCS := basics.c Wave_Writer.cpp +SRCS_MEM := basics_mem.c Wave_Writer.cpp +INCLUDES := ../gme/ +LIBRARIES := ../build/gme/ +TEST_FILES := ../test.nsf # Add more files here that you want in testsuite + +all: demo demo_mem + +# We will use LD_PRELOAD later to pick up the right libgme +demo: $(SRCS) Wave_Writer.h + $(CXX) -I$(INCLUDES) $(CXXFLAGS) -o $@ $(SRCS) -L$(LIBRARIES) -lgme + +demo_mem: $(SRCS_MEM) Wave_Writer.h + $(CXX) -I$(INCLUDES) $(CXXFLAGS) -o $@ $(SRCS_MEM) -L$(LIBRARIES) -lgme + +test: demo demo_mem + parallel --bar ./test.sh {} ::: $(TEST_FILES) + +clean: + rm -f demo + rm -f demo_mem + rm -f new/*.out cur/*.out + rm -f newm/*.out curm/*.out + rmdir new cur newm curm + diff -Nru game-music-emu-0.6.2/test/test.sh game-music-emu-0.6.3/test/test.sh --- game-music-emu-0.6.2/test/test.sh 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/test/test.sh 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,77 @@ +#!/bin/sh + +# A simple script to help test whether libgme changes affect the output of the +# emulators using it. +# +# Idea is to run under a test directory (the directory holding this script), +# which itself has subdirectories: +# "cur" (for outputs from the current libgme) and +# "new" (for outputs from the new version we're testing) +# +# These hold the output of a version of the existing libgme 'demo' script, +# modified to take 2 argments (input file, output file) and to output 3 minutes +# worth of music (to account for looping tracks). Just run 'make' to build the +# script. +# +# This script compares the 2 output files generated by each run of the modified +# demo script, and outputs a warning if the 2 were different. In this case the +# output files are also left intact for post-test examination. +# +# LD_PRELOAD is used to control which libgme is run (LD_PRELOAD is unset to +# pull in the default libgme, set to $LIBGME_NEW_PATH to pull in the new one) +# +# Sample usage (with GNU parallel to use all your cores) +# parallel --bar ./test {} ::: ../test.nsf ~/Music/*.{spc,gym} +# +# Feel free to replace the files listed above with the path(s) to your own +# files. + +# Path to the build directory's libgme, which we want to test +# The part after the := below is just the default to use if unset +LIBGME_NEW_PATH=${LIBGME_NEW_PATH:=$(realpath ../build/gme/libgme.so)} : + +if [ "$#" -lt 1 ] +then + printf "This doesn't work on empty input!\n" + exit 1 +fi + +i="$1" + +filepath=$(realpath "$i") + +if [ ! -e "$filepath" ] +then + printf "File $i does not exist!\n" + exit 1 +fi + +outname=$(basename "$filepath").out + +# Ensure directories present +mkdir -p cur new curm newm + +(cd cur ; ../demo "$filepath" "$outname") +(cd new ; LD_PRELOAD="$LIBGME_NEW_PATH" ../demo "$filepath" "$outname") + +(cd curm ; ../demo_mem "$filepath" "$outname") +(cd newm ; LD_PRELOAD="$LIBGME_NEW_PATH" ../demo_mem "$filepath" "$outname") + +out1=$(sha1sum cur/"$outname" | cut -f1 -d' ') +out2=$(sha1sum new/"$outname" | cut -f1 -d' ') + +out1m=$(sha1sum curm/"$outname" | cut -f1 -d' ') +out2m=$(sha1sum newm/"$outname" | cut -f1 -d' ') + +if [ "x$out1" = "x$out2" -a "x$out1m" = "x$out2m" -a "x" != "x$out1" -a "x" != "x$out1m" ] +then + rm cur/"$outname" + rm new/"$outname" + rm curm/"$outname" + rm newm/"$outname" + printf "All checks has been passed!\n" + exit 0 +else + printf "$outname differed\n" + exit 1 +fi diff -Nru game-music-emu-0.6.2/test/Wave_Writer.cpp game-music-emu-0.6.3/test/Wave_Writer.cpp --- game-music-emu-0.6.2/test/Wave_Writer.cpp 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/test/Wave_Writer.cpp 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,184 @@ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ + +#include "Wave_Writer.h" + +#include +#include +#include + +/* Copyright (C) 2003-2006 by Shay Green. Permission is hereby granted, free +of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the +following conditions: The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. THE +SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ + +const int header_size = 0x2C; + +static void exit_with_error( const char* str ) +{ + printf( "Error: %s\n", str ); getchar(); + exit( EXIT_FAILURE ); +} + +Wave_Writer::Wave_Writer( long sample_rate, const char* filename ) +{ + sample_count_ = 0; + rate = sample_rate; + buf_pos = header_size; + chan_count = 1; + + buf = (unsigned char*) malloc( buf_size * sizeof *buf ); + if ( !buf ) + exit_with_error( "Out of memory" ); + + file = fopen( filename, "wb" ); + if ( !file ) + exit_with_error( "Couldn't open WAVE file for writing" ); + + setvbuf( file, 0, _IOFBF, 32 * 1024L ); +} + +void Wave_Writer::flush() +{ + if ( buf_pos && !fwrite( buf, buf_pos, 1, file ) ) + exit_with_error( "Couldn't write WAVE data" ); + buf_pos = 0; +} + +void Wave_Writer::write( const sample_t* in, long remain, int skip ) +{ + sample_count_ += remain; + while ( remain ) + { + if ( buf_pos >= buf_size ) + flush(); + + long n = (buf_size - buf_pos) / sizeof (sample_t); + if ( n > remain ) + n = remain; + remain -= n; + + // convert to lsb first format + unsigned char* p = &buf [buf_pos]; + while ( n-- ) + { + int s = *in; + in += skip; + *p++ = (unsigned char) s; + *p++ = (unsigned char) (s >> 8); + } + + buf_pos = p - buf; + assert( buf_pos <= buf_size ); + } +} + + +void Wave_Writer::write( const float* in, long remain, int skip ) +{ + sample_count_ += remain; + while ( remain ) + { + if ( buf_pos >= buf_size ) + flush(); + + long n = (buf_size - buf_pos) / sizeof (sample_t); + if ( n > remain ) + n = remain; + remain -= n; + + // convert to lsb first format + unsigned char* p = &buf [buf_pos]; + while ( n-- ) + { + long s = (long) (*in * 0x7FFF); + in += skip; + if ( (short) s != s ) + s = 0x7FFF - (s >> 24); // clamp to 16 bits + *p++ = (unsigned char) s; + *p++ = (unsigned char) (s >> 8); + } + + buf_pos = p - buf; + assert( buf_pos <= buf_size ); + } +} + +void Wave_Writer::close() +{ + if ( file ) + { + flush(); + + // generate header + uint32_t ds = sample_count_ * sizeof (sample_t); + uint32_t rs = header_size - 8 + ds; + uint32_t frame_size = chan_count * sizeof (sample_t); + uint32_t bps = rate * frame_size; + +#define LE32(x) (unsigned char)(x), (unsigned char)((x)>>8), \ + (unsigned char)((x)>>16), (unsigned char)((x)>>24) + + unsigned char header [header_size] = + { + 'R','I','F','F', + LE32(rs), // length of rest of file + 'W','A','V','E', + 'f','m','t',' ', + 0x10,0,0,0, // size of fmt chunk + 1,0, // uncompressed format + (unsigned char)chan_count,0,// channel count + LE32(rate), // sample rate + LE32(bps), // bytes per second + (unsigned char)frame_size,0,// bytes per sample frame + 16,0, // bits per sample + 'd','a','t','a', + LE32(ds) // size of sample data + // ... // sample data + }; + + // write header +// fseek( file, 0, SEEK_SET ); + fwrite( header, sizeof header, 1, file ); + + fclose( file ); + file = 0; + free( buf ); + } +} + +Wave_Writer::~Wave_Writer() +{ + close(); +} + +// C interface + +static Wave_Writer* ww; + +void wave_open( long sample_rate, const char* filename ) +{ + ww = new Wave_Writer( sample_rate, filename ); + assert( ww ); +} + +void wave_enable_stereo() { ww->enable_stereo(); } + +long wave_sample_count() { return ww->sample_count(); } + +void wave_write( const short* buf, long count ) { ww->write( buf, count ); } + +void wave_close() +{ + delete ww; + ww = 0; +} diff -Nru game-music-emu-0.6.2/test/Wave_Writer.h game-music-emu-0.6.3/test/Wave_Writer.h --- game-music-emu-0.6.2/test/Wave_Writer.h 1970-01-01 00:00:00.000000000 +0000 +++ game-music-emu-0.6.3/test/Wave_Writer.h 2020-02-25 01:28:10.000000000 +0000 @@ -0,0 +1,73 @@ +/* WAVE sound file writer for recording 16-bit output during program development */ + +#ifndef WAVE_WRITER_H +#define WAVE_WRITER_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* C interface */ +void wave_open( long sample_rate, const char* filename ); +void wave_enable_stereo( void ); +void wave_write( const short* buf, long count ); +long wave_sample_count( void ); +void wave_close( void ); + +#ifdef __cplusplus + } +#endif + +#ifdef __cplusplus +#include +#include + +/* C++ interface */ +class Wave_Writer { +public: + typedef short sample_t; + + // Create sound file with given sample rate (in Hz) and filename. + // Exits program if there's an error. + Wave_Writer( long sample_rate, char const* filename = "out.wav" ); + + // Enable stereo output + void enable_stereo(); + + // Append 'count' samples to file. Use every 'skip'th source sample; allows + // one channel of stereo sample pairs to be written by specifying a skip of 2. + void write( const sample_t*, long count, int skip = 1 ); + + // Append 'count' floating-point samples to file. Use every 'skip'th source sample; + // allows one channel of stereo sample pairs to be written by specifying a skip of 2. + void write( const float*, long count, int skip = 1 ); + + // Number of samples written so far + long sample_count() const; + + // Finish writing sound file and close it + void close(); + + ~Wave_Writer(); +public: + // Deprecated + void stereo( bool b ) { chan_count = b ? 2 : 1; } +private: + enum { buf_size = 32768 * 2 }; + unsigned char* buf; + FILE* file; + long sample_count_; + long rate; + long buf_pos; + int chan_count; + + void flush(); +}; + +inline void Wave_Writer::enable_stereo() { chan_count = 2; } + +inline long Wave_Writer::sample_count() const { return sample_count_; } + +#endif + +#endif diff -Nru game-music-emu-0.6.2/test.m3u game-music-emu-0.6.3/test.m3u --- game-music-emu-0.6.2/test.m3u 2017-12-07 03:19:39.000000000 +0000 +++ game-music-emu-0.6.3/test.m3u 2020-02-25 01:28:10.000000000 +0000 @@ -1,2 +1,2 @@ # filename,track number,track name,track time -test.nsf,$00,BGM C,1:16 +test.nsf,$00,BGM C,0:01:16.78 Binary files /tmp/tmpyelGHA/H1nQEpW0La/game-music-emu-0.6.2/test.vgz and /tmp/tmpyelGHA/zLGxb5Plkg/game-music-emu-0.6.3/test.vgz differ