diff --git a/CMakeLists.txt b/CMakeLists.txt index 032dbb12c..991ff424d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,7 +53,7 @@ function( assort_pk3_source_folder FOLDER_NAME PK3_DIR ) assort_pk3_source_folder( ${FOLDER_NAME}\\${DIRNAME} ${PK3_SRC} ) endif() # Assign IDE group for current top-level source files - source_group(${FOLDER_NAME} FILES ${PK3_SRCS}) + source_group(${FOLDER_NAME} FILES ${PK3_SRCS}) endforeach() endfunction() @@ -78,7 +78,7 @@ function( add_pk3 PK3_NAME PK3_DIR ) DEPENDS zipdir ) endif() # Create a list of source files for this PK3, for use in the IDE - # Phase 1: Create a list of all source files for this PK3 archive, except + # Phase 1: Create a list of all source files for this PK3 archive, except # for a couple of strife image file names that confuse CMake. file(GLOB_RECURSE PK3_SRCS ${PK3_DIR}/*) # Exclude from the source list some gzdoom .png files with brackets in the @@ -180,7 +180,7 @@ if( MSVC ) # Function-level linking # Disable run-time type information set( ALL_C_FLAGS "/GF /Gy /GR-" ) - + # Use SSE 2 as minimum always as the true color drawers needs it for __vectorcall #set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") # This is already the default @@ -199,7 +199,7 @@ if( MSVC ) # else() # set( ALL_C_FLAGS "${ALL_C_FLAGS} /arch:SSE2") # endif() - + # Avoid CRT DLL dependancies in release builds, optionally generate assembly output for checking crash locations. option( ZDOOM_GENERATE_ASM "Generate assembly output." OFF ) if( ZDOOM_GENERATE_ASM ) @@ -208,7 +208,7 @@ if( MSVC ) set( REL_C_FLAGS "/MT /Oy /Oi /GS-" ) endif() - + # Debug allocations in debug builds set( DEB_C_FLAGS "/D _CRTDBG_MAP_ALLOC /MTd" ) @@ -216,7 +216,7 @@ if( MSVC ) if( MSVC_VERSION GREATER 1399 ) set( ALL_C_FLAGS "${ALL_C_FLAGS} /wd4996" ) endif() - + # The CMake configurations set /GR and /MD by default, which conflict with our settings. string(REPLACE "/MD " " " CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE} ) string(REPLACE "/MD " " " CMAKE_CXX_FLAGS_MINSIZEREL ${CMAKE_CXX_FLAGS_MINSIZEREL} ) @@ -233,7 +233,7 @@ else() set( REL_C_FLAGS "" ) set( DEB_C_FLAGS "" ) - + if( APPLE ) if( CMAKE_CXX_COMPILER_ID STREQUAL "Clang" ) # With standard Apple tools -stdlib=libc++ needs to be specified in order to get @@ -315,6 +315,9 @@ if( GME_FOUND AND NOT FORCE_INTERNAL_GME ) message( STATUS "Using system gme library, includes found at ${GME_INCLUDE_DIR}" ) else() message( STATUS "Using internal gme library" ) + # Use MAME as it's balanced emulator: well-accurate, but doesn't eats lot of CPU + # Nuked OPN2 is very accurate emulator, but it eats too much CPU for the workflow + set( GME_YM2612_EMU "MAME" ) add_subdirectory( game-music-emu ) set( GME_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/game-music-emu" ) set( GME_LIBRARIES gme ) @@ -334,8 +337,8 @@ if( WIN32 ) else() set( INSTALL_DOCS_PATH share/doc/${ZDOOM_EXE_NAME} CACHE STRING "Directory where the zdoom documentation will be placed during install." ) endif() -install(DIRECTORY docs/ - DESTINATION ${INSTALL_DOCS_PATH} +install(DIRECTORY docs/ + DESTINATION ${INSTALL_DOCS_PATH} COMPONENT "Documentation") add_subdirectory( lzma ) diff --git a/game-music-emu/CMakeLists.txt b/game-music-emu/CMakeLists.txt index 0af4f24d8..491b4e615 100644 --- a/game-music-emu/CMakeLists.txt +++ b/game-music-emu/CMakeLists.txt @@ -4,29 +4,32 @@ project(libgme) include (CheckCXXCompilerFlag) # When version is changed, also change the one in gme/gme.h to match -set(GME_VERSION 0.6.1 CACHE INTERNAL "libgme Version") +set(GME_VERSION 0.6.2 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 # down the requirement, but I can't test that. -cmake_minimum_required(VERSION 2.8.7 FATAL_ERROR) - -make_release_only() +cmake_minimum_required(VERSION 2.6 FATAL_ERROR) # I don't plan on debugging this, so make it a release build. if( NOT CMAKE_BUILD_TYPE MATCHES "Release" ) - set( CMAKE_BUILD_TYPE "RelWithDebInfo" ) + set( CMAKE_BUILD_TYPE "RelWithDebInfo" ) endif() if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra" ) - if( NOT PROFILE ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer" ) - endif() - check_cxx_compiler_flag( -Wno-array-bounds HAVE_NO_ARRAY_BOUNDS ) - if( HAVE_NO_ARRAY_BOUNDS ) - set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-array-bounds" ) - endif() + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra" ) + if( NOT PROFILE ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer" ) + endif() + check_cxx_compiler_flag( -Wno-array-bounds HAVE_NO_ARRAY_BOUNDS ) + if( HAVE_NO_ARRAY_BOUNDS ) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-array-bounds" ) + endif() +endif() + +#[ZDoom] Disable most of bogus and annoying MSVC warnings +if( MSVC ) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4101 /wd4800 /wd4702 /wd4706 /wd4805 /wd4310 /wd4244 /wd4456 /wd4459 /wd4146 /wd4127 /wd4458 /wd4267 /wd4804") endif() # Enable fast flag for GME @@ -34,79 +37,98 @@ set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ZD_FASTMATH_FLAG}" ) # Default emulators to build (all of them! ;) # [ZDoom] No options, enable all of them by default. + #if (NOT DEFINED USE_GME_AY) - SET(USE_GME_AY 1 BOOL "Enable support for Spectrum ZX music emulation") + SET(USE_GME_AY 1 CACHE BOOL "Enable support for Spectrum ZX music emulation") #endif() #if (NOT DEFINED USE_GME_GBS) - SET(USE_GME_GBS 1 BOOL "Enable support for Game Boy music emulation") + SET(USE_GME_GBS 1 CACHE BOOL "Enable support for Game Boy music emulation") #endif() #if (NOT DEFINED USE_GME_GYM) - SET(USE_GME_GYM 1 BOOL "Enable Sega MegaDrive/Genesis music emulation") + SET(USE_GME_GYM 1 CACHE BOOL "Enable Sega MegaDrive/Genesis music emulation") #endif() #if (NOT DEFINED USE_GME_HES) - SET(USE_GME_HES 1 BOOL "Enable PC Engine/TurboGrafx-16 music emulation") + SET(USE_GME_HES 1 CACHE BOOL "Enable PC Engine/TurboGrafx-16 music emulation") #endif() #if (NOT DEFINED USE_GME_KSS) - SET(USE_GME_KSS 1 BOOL "Enable MSX or other Z80 systems music emulation") + SET(USE_GME_KSS 1 CACHE BOOL "Enable MSX or other Z80 systems music emulation") #endif() #if (NOT DEFINED USE_GME_NSF) - SET(USE_GME_NSF 1 BOOL "Enable NES NSF music emulation") + SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation") #endif() #if (NOT DEFINED USE_GME_NSFE) - SET(USE_GME_NSFE 1 BOOL "Enable NES NSFE and NSF music emulation") + SET(USE_GME_NSFE 1 CACHE BOOL "Enable NES NSFE and NSF music emulation") #endif() #if (NOT DEFINED USE_GME_SAP) - SET(USE_GME_SAP 1 BOOL "Enable Atari SAP music emulation") + SET(USE_GME_SAP 1 CACHE BOOL "Enable Atari SAP music emulation") #endif() #if (NOT DEFINED USE_GME_SPC) - SET(USE_GME_SPC 1 BOOL "Enable SNES SPC music emulation") + SET(USE_GME_SPC 1 CACHE BOOL "Enable SNES SPC music emulation") #endif() #if (NOT DEFINED USE_GME_VGM) - SET(USE_GME_VGM 1 BOOL "Enable Sega VGM/VGZ music emulation") + 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 BOOL "Enable NES NSF music emulation") + #MESSAGE(" -- NSFE support requires NSF, enabling NSF support. --") + SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE) #endif() # [ZDoom] Set always to OFF. set(BUILD_SHARED_LIBS OFF) +set(ENABLE_UBSAN OFF) -# 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}") +# Check for GCC/Clang "visibility" support. +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" + OR + CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - # 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) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -W -Wextra") + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - # 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 + # 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") - # Cache this result - set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility") + # 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 (NOT DEFINED LIBGME_SWITCH_FALLTHROUGH) + check_cxx_compiler_flag (-Wimplicit-fallthrough __LIBGME_SWITCH_FALLTHROUGH_WARNINGS) + set (LIBGME_SWITCH_FALLTHROUGH ${__LIBGME_SWITCH_FALLTHROUGH_WARNINGS} + CACHE BOOL "Set if the compiler will complain about implicit switch fallthrough" + ) + endif() + + 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 () + +if(LIBGME_SWITCH_FALLTHROUGH) + # Avoid warning spam about switch fallthroughs, which are numerous in + # the codebase. + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wimplicit-fallthrough=0") endif() # Shared library defined here diff --git a/game-music-emu/changes.txt b/game-music-emu/changes.txt index 48986756a..034ba4821 100644 --- a/game-music-emu/changes.txt +++ b/game-music-emu/changes.txt @@ -1,274 +1,5 @@ Game_Music_Emu Change Log ------------------------- -Game_Music_Emu 0.6.1 --------------------- - -- Moved repository to Bitbucket since Google Code announced they would - shutdown this year. - -- Packaging improvements: - - Honor $LIB_SUFFIX for installed pkg-config metadata. - - Support setting BUILD_SHARED_LIBS to OFF to build libgme as a static - library. (Pass -DBUILD_SHARED_LIBS=OFF when running cmake). - Thanks to lachs0r. - -Game_Music_Emu 0.6.0 --------------------- - -- Note: A 0.5.6 release was referenced but never tagged or packaged. - -- SPC improvements: - - Switched to newer snes_spc 0.9.0 for SPC emulation. Uses fast DSP. - - Fixed Spc_Emu::gain(). - - Fixed support for files <0x10200 bytes. - -- Other bugfixes: - - Fixed a couple of GBS bugs, one involving access of memory after - realloc. - - Blip_Buffer works on systems where 'double' is a single-precision - floating-point type. - - Fix uninitialized buffer size in dual_resampler. - - Compilation warnings squashed out as of clang 3.3-pre and gcc 4.7.2. - -- API changes/additions: - - Removed documentation of C++ interface, as the C interface in gme.h is - the only supported one. - - Added gme_enable_accuracy() for enabling more accurate sound emulation - options (currently affects SPC only). - -- Build system improvements: - - Add pkg_config support. - - Fix build on case-insensitive systems. - - Allow for install on Cygwin. - - Fix install on multilib systems, such as many 64-bit distros (CMake must - be able to figure out your system's libsuffix, if any). - - C++ implementation symbols are not leaked into the resultant library - file (requires symbol visibility support). - -- Sample player improvements: - - Can toggle fast/accurate emulation (with the 'A' key). - -Game_Music_Emu 0.5.5 --------------------- -- CMake build support has been added. You can build Game_Music_Emu as -a shared library and install it so that you do not have to include your -own copy if you know libgme will be present on your target system. -Requires CMake 2.6 or higher. - - -Game_Music_Emu 0.5.2 --------------------- -- *TONS* of changes and improvements. You should re-read the new header -files and documentation as the changes will allow you to simplify your -code a lot (it might even be simpler to just rewrite it). Existing code -should continue to work without changes in most cases (see Deprecated -features in gme.txt). - -- New file formats: AY, HES, KSS, SAP, NSFE - -- All-new comprehensive C interface (also usable from C++). Simplifies -many things, especially file loading, and brings everything together in -one header file (gme.h). - -- Information tags and track names and times can be accessed for all -game music formats - -- New features supported by all emulators: end of track fading, -automatic silence detection, adjustable song tempo, seek to new time in -track - -- Updated mini player example to support track names and times, echo, -tempo, and channel muting, and added visual waveform display - -- Improved configuration to use blargg_config.h, which you can modify -and keep when you update to a newer libary version. Includes flag for -library to automatically handle gzipped files using zlib (so you don't -need to use Gzip_File_Reader anymore). - -- GBS: Fixed wave channel to not reset waveform when APU is powered off -(affected Garfield). Also improved invalid bank selection (affected Game -& Watch and others). - -- VGM: Added support for alternate noise shifter register -configurations, used by other systems like the BBC Micro. - -- SPC: Removed IPL ROM dump from emulator, as none of the SPC files I -scanned needed it, and an SPC file can include a copy if necessary. Also -re-enabled supposed clamping in gaussian interpolation between the third -and fourth lookups, though I don't know whether it matters - -- Added Music_Emu::load_mem() to use music data already in memory -(without copying it) - -- Added Music_Emu::warning(), which reports minor problems when loading -and playing a music file - -- Added Music_Emu::set_gain() for uniform adjustment of gain. Can only -be set during initialization, so not useful as a general volume control. - -- Added custom operator new to ensure that no exceptions are thrown in -the library (I'd use std::nothrow if it were part of pre-ISO (ARM) C++) - -- Added BLIP_BUFFER_FAST flag to blargg_config.h to use a lower quality -bandlimited synthesis in "classic" emulators, which might help -performance on ancient processors (measure first!). Don't use this -unless absolutely necessary, as quality suffers. - -- Improved performance a bit for x86 platforms - -- Text files now in DOS newline format so they will open in Notepad -properly - -- Removed requirement that file header structures not have any padding -added to the end - -- Fixed common bug in all CPU emulators where negative program counter -could crash emulator (occurred during a negative branch from the -beginning of memory). Also fixed related bug in Z80 emulator for -IX/IY+displacement mode. - -- Eliminated all warnings when compiling on gcc 4.0. The following -generates no diagnostics: - - gcc -S gme/*.cpp -o /dev/null -ansi -fno-gnu-keywords - -fno-nonansi-builtins -pedantic -W -Wabi -Wall -Wcast-align - -Wcast-qual -Wchar-subscripts -Wdisabled-optimization -Werror - -Winline -Wlong-long -Wmultichar -Winvalid-offsetof - -Wnon-virtual-dtor -Woverloaded-virtual -Wparentheses - -Wpointer-arith -Wredundant-decls -Wreorder -Wsign-compare - -Wsign-promo -Wunknown-pragmas -Wwrite-strings - - -Game_Music_Emu 0.3.0 --------------------- -- Added more demos, including music player using the SDL multimedia -library for sound, and improved documentation - -- All: Improved interface to emulators to allow simpler setup and -loading. Instead of various init() functions, all now support -set_sample_rate( long rate ) and load( const char* file_path ). - -- All: Removed error return from start_track() and play(), and added -error_count() to get the total number of emulation errors since the -track was last started. See demos for examples of new usage. - -- All: Fixed mute_voices() muting to be preserved after loading files -and starting tracks, instead of being cleared as it was whenever a track -was started - -- VGM: Rewrote Vgm_Emu to support Sega Genesis/Mega Drive FM sound at -any sample rate with optional FM oversampling, support for alternate -YM2612 sound cores, and support for optional YM2413 - -- VGM: Added tempo control, useful for slowing 60Hz NTSC Sega Genesis -music to 50Hz PAL - -- VGM: Removed Vgm_Emu::track_data(), since I realized that this -information is already present in the VGM header (oops!) - -- GYM: Changed Gym_Emu::track_length() operation (see Gym_Emu.h) - -- NSF: Added support for Sunsoft FME-7 sound chip used by Gimmick -soundtrack - -- NSF: Fixed Namco 106 problems with Final Lap and others - -- Moved library sources to gme/ directory to reduce clutter, and merged -boost/ functionality into blargg_common.h - -- Added Gzip_File_Reader for transparently using gzipped files - - -Game_Music_Emu 0.2.4 --------------------- -- Created a discussion forum for problems and feedback: -http://groups-beta.google.com/group/blargg-sound-libs - -- Changed error return value of Blip_Buffer::sample_rate() (also for -Stereo_Buffer, Effects_Buffer, etc.) to blargg_err_t (defined in -blargg_common.h), to make error reporting consistent with other -functions. This means the "no error" return value is the opposite of -what it was before, which will break current code which checks the error -return value: - - // current code (broken) - if ( !buf.sample_rate( samples_per_sec ) ) - out_of_memory(); - - // quick-and-dirty fix (just remove the ! operation) - if ( buf.sample_rate( samples_per_sec ) ) - out_of_memory(); - - // proper fix - blargg_err_t error = buf.sample_rate( samples_per_sec ); - if ( error ) - report_error( error ); - -- Implemented workaround for MSVC++ 6 compiler limitations, allowing it -to work on that compiler again - -- Added sample clamping to avoid wrap-around at high volumes, allowing -higher volume with little distortion - -- Added to-do list and design notes - -- Added Music_Emu::skip( long sample_count ) to skip ahead in current -track - -- Added Gym_Emu::track_length() and Vgm_Emu::track_length() for -determining the length of non-looped GYM and VGM files - -- Partially implemented DMC non-linearity when its value is directly set -using $4011, which reduces previously over-emphasized "popping" of -percussion on some games (TMNT II in particular) - -- Fixed Fir_Resampler, used for SPC and GYM playback (was incorrectly -using abs() instead of fabs()...argh) - -- Fixed SPC emulation bugs: eliminated clicks in Plok! soundtrack and -now stops sample slightly earlier than the end, as the SNES does. Fixed -a totally broken CPU addressing mode. - -- Fixed Konami VRC6 saw wave (was very broken before). Now VRC6 music -sounds decent - -- Fixed a minor GBS emulation bug - -- Fixed GYM loop point bug when track was restarted before loop point -had been reached - -- Made default GBS frequency equalization less muffled - -- Added pseudo-surround effect removal for SPC files - -- Added Music_Emu::voice_names() which returns names for each voice. - -- Added BLARGG_SOURCE_BEGIN which allows custom compiler options to be -easily set for library sources - -- Changed assignment of expansion sound chips in Nsf_Emu to be spread -more evenly when using Effects_Buffer - -- Changed 'size_t' values in Blip_Buffer interface to 'long' - -- Changed demo to generate a WAVE sound file rather than an AIFF file - - -Game_Music_Emu 0.2.0 --------------------- -- Redid framework and rewrote/cleaned up emulators - -- Changed licensing to GNU Lesser General Public License (LGPL) - -- Added Sega Genesis GYM and Super Nintendo SPC emulators - -- Added Namco-106 and Konami VRC6 sound chip support to NSF emulator - -- Eliminated use of static mutable data in emulators, allowing -multi-instance safety - - -Game_Music_Emu 0.1.0 --------------------- -- First release +Please see the git version history (e.g. git shortlog tags/0.6.0..tags/0.6.1) +for the accurate change log. diff --git a/game-music-emu/gme.txt b/game-music-emu/gme.txt index baec6f4dc..5a7d2f560 100644 --- a/game-music-emu/gme.txt +++ b/game-music-emu/gme.txt @@ -1,9 +1,8 @@ -Game_Music_Emu 0.6.1 +Game_Music_Emu 0.6.2 -------------------- Author : Shay Green Maintainer : Michael Pyne -Website : http://www.slack.net/~ant/libs/ -Forum : http://groups.google.com/group/blargg-sound-libs +Website : https://bitbucket.org/mpyne/game-music-emu/ Source : https://bitbucket.org/mpyne/game-music-emu/ License : GNU Lesser General Public License (LGPL), see LICENSE.txt diff --git a/game-music-emu/gme/Ay_Apu.cpp b/game-music-emu/gme/Ay_Apu.cpp index d58d839fe..d132c42f9 100644 --- a/game-music-emu/gme/Ay_Apu.cpp +++ b/game-music-emu/gme/Ay_Apu.cpp @@ -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" @@ -299,7 +299,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) while ( ntime <= end ) // must advance *past* time to avoid hang { int changed = noise_lfsr + 1; - noise_lfsr = ((blargg_ulong)-(blargg_long)(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1); + noise_lfsr = (-(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1); if ( changed & 2 ) { delta = -delta; diff --git a/game-music-emu/gme/Ay_Apu.h b/game-music-emu/gme/Ay_Apu.h index b031f0473..ad2d83692 100644 --- a/game-music-emu/gme/Ay_Apu.h +++ b/game-music-emu/gme/Ay_Apu.h @@ -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 --git a/game-music-emu/gme/Ay_Cpu.cpp b/game-music-emu/gme/Ay_Cpu.cpp index cdc3947f6..31c912568 100644 --- a/game-music-emu/gme/Ay_Cpu.cpp +++ b/game-music-emu/gme/Ay_Cpu.cpp @@ -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 @@ -136,11 +136,6 @@ static byte const ed_dd_timing [0x100] = { 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 @@ bool Ay_Cpu::run( cpu_time_t end_time ) this->state = &s; bool warning = false; - typedef BOOST::int8_t int8_t; - union { regs_t rg; pairs_t rp; @@ -160,10 +153,10 @@ bool Ay_Cpu::run( cpu_time_t end_time ) 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 @@ loop: check( (unsigned) ix < 0x10000 ); check( (unsigned) iy < 0x10000 ); - fuint8 opcode; + uint8_t opcode; opcode = READ_PROG( pc ); pc++; @@ -206,7 +199,7 @@ loop: 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 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: // 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 @@ possibly_out_of_time: 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 ); @@ -469,7 +462,7 @@ possibly_out_of_time: add_hl_data: { blargg_ulong sum = rp.hl + data; data ^= rp.hl; - rp.hl = (uint16_t)sum; + rp.hl = sum; flags = (flags & (S80 | Z40 | V04)) | (sum >> 16) | (sum >> 8 & (F20 | F08)) | @@ -659,21 +652,21 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: // 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 @@ possibly_out_of_time: } case 0x0F:{// RRCA - fuint16 temp = rg.a; + uint16_t temp = rg.a; flags = (flags & (S80 | Z40 | P04)) | (temp & C01); temp = (temp << 7) | (temp >> 1); @@ -719,12 +712,12 @@ possibly_out_of_time: flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08)) | (temp >> 8); - rg.a = (uint8_t)temp; + rg.a = temp; goto loop; } 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 @@ possibly_out_of_time: // 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 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: // 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 @@ possibly_out_of_time: } #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 @@ possibly_out_of_time: } #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 @@ possibly_out_of_time: // 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 @@ possibly_out_of_time: } #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 @@ possibly_out_of_time: } #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 @@ possibly_out_of_time: } #define SRL( read, write ) {\ - fuint8 result = read;\ + uint8_t result = read;\ flags = result & C01;\ result >>= 1;\ flags |= SZ28P( result );\ @@ -1048,7 +1041,7 @@ possibly_out_of_time: blargg_ulong sum = temp + (flags & C01); flags = ~data >> 2 & N02; if ( flags ) - sum = (blargg_ulong)-(blargg_long)sum; + sum = -sum; sum += rp.hl; temp ^= rp.hl; temp ^= sum; @@ -1056,7 +1049,7 @@ possibly_out_of_time: (temp >> 8 & H10) | (sum >> 8 & (S80 | F20 | F08)) | ((temp - -0x8000) >> 14 & V04); - rp.hl = (uint16_t)sum; + rp.hl = sum; if ( (uint16_t) sum ) goto loop; flags |= Z40; @@ -1084,7 +1077,7 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: } 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 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: //////////////////////////////////////// DD/FD prefix { - fuint16 ixy; + uint16_t ixy; case 0xDD: ixy = ix; goto ix_prefix; @@ -1490,7 +1483,7 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: 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 @@ possibly_out_of_time: 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 --git a/game-music-emu/gme/Ay_Cpu.h b/game-music-emu/gme/Ay_Cpu.h index cd3d66747..6984b42dc 100644 --- a/game-music-emu/gme/Ay_Cpu.h +++ b/game-music-emu/gme/Ay_Cpu.h @@ -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 @@ public: 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 --git a/game-music-emu/gme/Ay_Emu.cpp b/game-music-emu/gme/Ay_Emu.cpp index 565559441..a973ba0f1 100644 --- a/game-music-emu/gme/Ay_Emu.cpp +++ b/game-music-emu/gme/Ay_Emu.cpp @@ -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_Emu.h" @@ -47,10 +47,10 @@ Ay_Emu::~Ay_Emu() { } static byte const* get_data( Ay_Emu::file_t const& file, byte const* ptr, int min_size ) { - long pos = long(ptr - (byte const*) file.header); - long file_size = long(file.end - (byte const*) file.header); + 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 +117,7 @@ static Music_Emu* new_ay_emu () { return BLARGG_NEW Ay_Emu ; } 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_; +BLARGG_EXPORT extern gme_type_t const gme_ay_type = &gme_ay_type_; // Setup @@ -207,7 +207,7 @@ blargg_err_t Ay_Emu::start_track_( int track ) if ( len > blargg_ulong (file.end - in) ) { set_warning( "Missing file data" ); - len = unsigned(file.end - in); + len = file.end - in; } //debug_printf( "addr: $%04X, len: $%04X\n", addr, len ); if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data diff --git a/game-music-emu/gme/Ay_Emu.h b/game-music-emu/gme/Ay_Emu.h index 86b020487..6726f0157 100644 --- a/game-music-emu/gme/Ay_Emu.h +++ b/game-music-emu/gme/Ay_Emu.h @@ -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 --git a/game-music-emu/gme/Blip_Buffer.h b/game-music-emu/gme/Blip_Buffer.h index 4cc526d2f..e6facc820 100644 --- a/game-music-emu/gme/Blip_Buffer.h +++ b/game-music-emu/gme/Blip_Buffer.h @@ -95,6 +95,8 @@ public: Blip_Buffer(); ~Blip_Buffer(); + Blip_Buffer(Blip_Buffer &&) = default; + // Deprecated typedef blip_resampled_time_t resampled_time_t; blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } diff --git a/game-music-emu/gme/CMakeLists.txt b/game-music-emu/gme/CMakeLists.txt index 8d710709f..a5e06bc8a 100644 --- a/game-music-emu/gme/CMakeLists.txt +++ b/game-music-emu/gme/CMakeLists.txt @@ -14,6 +14,15 @@ set(libgme_SRCS Blip_Buffer.cpp 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 @@ endif() # 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 @@ -127,19 +152,11 @@ 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) - -# [ZDoom] Not needed. -if( FALSE ) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libgme.pc.in - ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc @ONLY) -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 # only defined when building the library to allow us to tell which is which. + +#[ZDoom] Not needed #add_definitions(-DBLARGG_BUILD_DLL) # For the gme_types.h @@ -148,13 +165,25 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) # 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() + +# [ZDoom] Not needed. +if( FALSE ) # 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. # The way gme.h is designed, SOVERSION should very rarely be bumped, if ever. # Hopefully the API can stay compatible with old versions. -# [ZDoom] Not needed. -if( FALSE ) set_target_properties(gme PROPERTIES VERSION ${GME_VERSION} SOVERSION 0) @@ -163,8 +192,13 @@ install(TARGETS gme LIBRARY DESTINATION lib${LIB_SUFFIX} RUNTIME DESTINATION bin # DLL platforms ARCHIVE DESTINATION lib) # DLL platforms +# 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) endif() - -target_link_libraries(gme) diff --git a/game-music-emu/gme/Classic_Emu.cpp b/game-music-emu/gme/Classic_Emu.cpp index 42bb2fbe2..c572d9b5c 100644 --- a/game-music-emu/gme/Classic_Emu.cpp +++ b/game-music-emu/gme/Classic_Emu.cpp @@ -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 @@ blargg_err_t Classic_Emu::set_sample_rate_( long rate ) 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 --git a/game-music-emu/gme/Classic_Emu.h b/game-music-emu/gme/Classic_Emu.h index 99e99afbe..57cdd5c32 100644 --- a/game-music-emu/gme/Classic_Emu.h +++ b/game-music-emu/gme/Classic_Emu.h @@ -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 @@ public: 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 --git a/game-music-emu/gme/Data_Reader.cpp b/game-music-emu/gme/Data_Reader.cpp index 67fc8aefd..1556c329f 100644 --- a/game-music-emu/gme/Data_Reader.cpp +++ b/game-music-emu/gme/Data_Reader.cpp @@ -1,7 +1,5 @@ // File_Extractor 0.4.0. http://www.slack.net/~ant/ -#define _CRT_SECURE_NO_WARNINGS - #include "Data_Reader.h" #include "blargg_endian.h" @@ -22,24 +20,38 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #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 */ + const char Data_Reader::eof_error [] = "Unexpected end of file"; +#define RETURN_VALIDITY_CHECK( cond ) \ + do { if ( unlikely( !(cond) ) ) return "Corrupt file"; } while(0) + blargg_err_t Data_Reader::read( void* p, long s ) { + RETURN_VALIDITY_CHECK( s > 0 ); + long result = read_avail( p, s ); if ( result != s ) { if ( result >= 0 && result < s ) return eof_error; - + return "Read error"; } - + return 0; } blargg_err_t Data_Reader::skip( long count ) { + RETURN_VALIDITY_CHECK( count >= 0 ); + char buf [512]; while ( count ) { @@ -56,7 +68,8 @@ long File_Reader::remain() const { return size() - tell(); } blargg_err_t File_Reader::skip( long n ) { - assert( n >= 0 ); + RETURN_VALIDITY_CHECK( n >= 0 ); + if ( !n ) return 0; return seek( tell() + n ); @@ -69,13 +82,14 @@ Subset_Reader::Subset_Reader( Data_Reader* dr, long size ) in = dr; remain_ = dr->remain(); if ( remain_ > size ) - remain_ = size; + remain_ = max( 0l, size ); } long Subset_Reader::remain() const { return remain_; } long Subset_Reader::read_avail( void* p, long s ) { + s = max( 0l, s ); if ( s > remain_ ) s = remain_; remain_ -= s; @@ -87,30 +101,32 @@ long Subset_Reader::read_avail( void* p, long s ) Remaining_Reader::Remaining_Reader( void const* h, long size, Data_Reader* r ) { header = (char const*) h; - header_end = header + size; + header_end = header + max( 0l, size ); in = r; } -long Remaining_Reader::remain() const { return long(header_end - header + in->remain()); } +long Remaining_Reader::remain() const { return header_end - header + in->remain(); } long Remaining_Reader::read_first( void* out, long count ) { - long first = long(header_end - header); + count = max( 0l, count ); + 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; } long Remaining_Reader::read_avail( void* out, long count ) { + count = max( 0l, count ); long first = read_first( out, count ); - long second = count - first; + long second = max( 0l, count - first ); if ( second ) { second = in->read_avail( (char*) out + first, second ); @@ -122,8 +138,9 @@ long Remaining_Reader::read_avail( void* out, long count ) blargg_err_t Remaining_Reader::read( void* out, long count ) { + count = max( 0l, count ); long first = read_first( out, count ); - long second = count - first; + long second = max( 0l, count - first ); if ( !second ) return 0; return in->read( (char*) out + first, second ); @@ -132,41 +149,135 @@ blargg_err_t Remaining_Reader::read( void* out, long count ) // Mem_File_Reader Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : - begin( (const char*) p ), - size_( s ) + m_begin( (const char*) p ), + m_size( max( 0l, s ) ), + m_pos( 0l ) { - pos = 0; +#ifdef HAVE_ZLIB_H + if( !m_begin ) + return; + + if ( gz_decompress() ) + { + debug_printf( "Loaded compressed data\n" ); + m_ownedPtr = true; + } +#endif /* HAVE_ZLIB_H */ } - -long Mem_File_Reader::size() const { return size_; } + +#ifdef HAVE_ZLIB_H +Mem_File_Reader::~Mem_File_Reader() +{ + if ( m_ownedPtr ) + free( const_cast( m_begin ) ); // see gz_compress for the malloc +} +#endif + +long Mem_File_Reader::size() const { return m_size; } long Mem_File_Reader::read_avail( void* p, long s ) { long r = remain(); - 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 ) { - if ( n > size_ ) + RETURN_VALIDITY_CHECK( n >= 0 ); + 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 ) : callback( c ), data( d ) { - remain_ = size; + remain_ = max( 0l, size ); } long Callback_Reader::remain() const { return remain_; } @@ -175,34 +286,82 @@ long Callback_Reader::read_avail( void* out, long count ) { if ( count > remain_ ) count = remain_; - if ( Callback_Reader::read( out, count ) ) + if ( count < 0 || Callback_Reader::read( out, count ) ) count = -1; return count; } blargg_err_t Callback_Reader::read( void* out, long count ) { + 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 ) { } +#if 0//[ZDOOM:unneeded]def 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 ) +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + , size_( 0 ) +#endif +{ } Std_File_Reader::~Std_File_Reader() { close(); } blargg_err_t Std_File_Reader::open( const char* path ) { +#if 0//[ZDOOM:unneeded]def 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 { +#if 0//[ZDOOM:unneeded]def 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(); @@ -212,24 +371,64 @@ long Std_File_Reader::size() const long Std_File_Reader::read_avail( void* p, long s ) { - return (long)fread( p, 1, s, (FILE*) file_ ); +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + if ( file_ && s > 0 && 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 ) { - if ( s == (long) fread( p, 1, s, (FILE*) file_ ) ) + RETURN_VALIDITY_CHECK( s > 0 && s <= UINT_MAX ); +#if 0//[ZDOOM:unneeded]def 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_ ); } +long Std_File_Reader::tell() const +{ +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + if ( file_ ) + return gztell( reinterpret_cast( file_ ) ); +#endif + return ftell( reinterpret_cast( file_ ) ); +} blargg_err_t Std_File_Reader::seek( long n ) { - if ( !fseek( (FILE*) file_, n, SEEK_SET ) ) - return 0; +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + if ( file_ ) + { + if ( gzseek( reinterpret_cast( file_ ), n, SEEK_SET ) >= 0 ) + return nullptr; + if ( n > size_ ) + return eof_error; + return "Error seeking in GZ file"; + } +#endif + if ( !fseek( reinterpret_cast( file_ ), n, SEEK_SET ) ) + return nullptr; if ( n > size() ) return eof_error; return "Error seeking in file"; @@ -239,79 +438,12 @@ void Std_File_Reader::close() { if ( file_ ) { - fclose( (FILE*) file_ ); - file_ = 0; - } -} - -// Gzip_File_Reader - -#ifdef HAVE_ZLIB_H - -#include "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]; - 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 - { - fseek( file, 0, SEEK_END ); - *eof = ftell( 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_ ) - return eof_error; - return "Error seeking in file"; -} - -void Gzip_File_Reader::close() -{ - if ( file_ ) - { - gzclose( file_ ); - file_ = 0; - } -} - +#if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H + gzclose( reinterpret_cast( file_ ) ); +#else + fclose( reinterpret_cast( file_ ) ); #endif + file_ = nullptr; + } +} + diff --git a/game-music-emu/gme/Data_Reader.h b/game-music-emu/gme/Data_Reader.h index 6c22b678e..59357767e 100644 --- a/game-music-emu/gme/Data_Reader.h +++ b/game-music-emu/gme/Data_Reader.h @@ -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 @@ public: long tell() const; blargg_err_t seek( long ); private: - void* file_; + void* file_; // Either FILE* or zlib's gzFile +#if 0//[ZDOOM:unneeded] def 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 @@ private: 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 --git a/game-music-emu/gme/Dual_Resampler.cpp b/game-music-emu/gme/Dual_Resampler.cpp index 0e7828530..e774d85f8 100644 --- a/game-music-emu/gme/Dual_Resampler.cpp +++ b/game-music-emu/gme/Dual_Resampler.cpp @@ -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" @@ -18,7 +18,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -//unsigned const resampler_extra = 256; +unsigned const resampler_extra = 256; Dual_Resampler::Dual_Resampler() : sample_buf_size(0), @@ -68,10 +68,13 @@ void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, dsample_t* out ) 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 ); - (void)count; // Silence warning in non-debug build +#endif mix_samples( blip_buf, out ); blip_buf.remove_samples( pair_count ); @@ -119,17 +122,17 @@ void Dual_Resampler::mix_samples( Blip_Buffer& blip_buf, dsample_t* out ) { 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; - out [0] = (dsample_t)l; - out [1] = (dsample_t)r; + out [0] = l; + out [1] = r; out += 2; } diff --git a/game-music-emu/gme/Dual_Resampler.h b/game-music-emu/gme/Dual_Resampler.h index 6dc8dcfc2..512fd97d0 100644 --- a/game-music-emu/gme/Dual_Resampler.h +++ b/game-music-emu/gme/Dual_Resampler.h @@ -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 --git a/game-music-emu/gme/Effects_Buffer.cpp b/game-music-emu/gme/Effects_Buffer.cpp index 6af9c14be..56b0c5b5c 100644 --- a/game-music-emu/gme/Effects_Buffer.cpp +++ b/game-music-emu/gme/Effects_Buffer.cpp @@ -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 "Effects_Buffer.h" @@ -63,29 +63,49 @@ void Effects_Buffer::set_depth( double d ) 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++ ) - { - bufs_used |= bufs [i].clear_modified() << i; - bufs [i].end_frame( clock_count ); - } - 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(); + + const int buf_count_per_voice = buf_count/max_voices; + for ( int v = 0; v < max_voices; v++ ) // foreach voice + { + 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; + } effects_enabled = config_.effects_enabled; } @@ -248,15 +291,17 @@ long Effects_Buffer::samples_avail() const 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 +331,7 @@ long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples ) active_bufs = 1; } - out += count * 2; + out += count * n_channels; remain -= count; stereo_remain -= count; @@ -297,23 +342,31 @@ long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples ) 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 +377,45 @@ void Effects_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) 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; + 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 +492,45 @@ void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long count ) 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; + 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 +569,27 @@ void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long count ) 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( 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] ); + 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 --git a/game-music-emu/gme/Effects_Buffer.h b/game-music-emu/gme/Effects_Buffer.h index 832c44b06..ec634d622 100644 --- a/game-music-emu/gme/Effects_Buffer.h +++ b/game-music-emu/gme/Effects_Buffer.h @@ -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 @@ public: 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 --git a/game-music-emu/gme/Fir_Resampler.cpp b/game-music-emu/gme/Fir_Resampler.cpp index a311895a2..d8dd6837c 100644 --- a/game-music-emu/gme/Fir_Resampler.cpp +++ b/game-music-emu/gme/Fir_Resampler.cpp @@ -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" @@ -23,10 +23,6 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #undef PI #define PI 3.1415926535897932384626433832795029 -#if _MSC_VER >= 1911 -#pragma float_control(precise, on, push) -#endif // _MSC_VER >= 1911 - static void gen_sinc( double rolloff, int width, double offset, double spacing, double scale, int count, short* out ) { @@ -56,10 +52,6 @@ static void gen_sinc( double rolloff, int width, double offset, double spacing, } } -#if _MSC_VER >= 1911 -#pragma float_control(pop) -#endif // _MSC_VER >= 1911 - Fir_Resampler_::Fir_Resampler_( int width, sample_t* impulses_ ) : width_( width ), write_offset( width * stereo - stereo ), @@ -164,7 +156,7 @@ int Fir_Resampler_::input_needed( blargg_long output_count ) const output_count -= 2; } - long input_extra = (long)(input_count - (write_pos - &buf [(width_ - 1) * stereo])); + long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]); if ( input_extra < 0 ) input_extra = 0; return input_extra; @@ -194,8 +186,8 @@ int Fir_Resampler_::avail_( blargg_long input_count ) const int Fir_Resampler_::skip_input( long count ) { - int remain = int(write_pos - buf.begin()); - int max_count = int(remain - width_ * stereo); + int remain = write_pos - buf.begin(); + int max_count = remain - width_ * stereo; if ( count > max_count ) count = max_count; diff --git a/game-music-emu/gme/Fir_Resampler.h b/game-music-emu/gme/Fir_Resampler.h index 6cd1c218d..d637ec41c 100644 --- a/game-music-emu/gme/Fir_Resampler.h +++ b/game-music-emu/gme/Fir_Resampler.h @@ -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 @@ -31,7 +31,7 @@ public: void clear(); // Number of input samples that can be written - int max_write() const { return int(buf.end() - write_pos); } + int max_write() const { return buf.end() - write_pos; } // Pointer to place to write input samples sample_t* buffer() { return write_pos; } @@ -40,7 +40,7 @@ public: void write( long count ); // Number of input samples in buffer - int written() const { return int(write_pos - &buf [write_offset]); } + int written() const { return write_pos - &buf [write_offset]; } // Skip 'count' input samples. Returns number of samples actually skipped. int skip_input( long count ); @@ -51,7 +51,7 @@ public: int input_needed( blargg_long count ) const; // Number of output samples available - int avail() const { return avail_( blargg_long(write_pos - &buf [width_ * stereo] )); } + int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); } public: ~Fir_Resampler_(); @@ -161,11 +161,11 @@ int Fir_Resampler::read( sample_t* out_begin, blargg_long count ) imp_phase = res - remain; - int left = int(write_pos - in); + int left = write_pos - in; write_pos = &buf [left]; memmove( buf.begin(), in, left * sizeof *in ); - return int(out - out_begin); + return out - out_begin; } #endif diff --git a/game-music-emu/gme/Gb_Apu.cpp b/game-music-emu/gme/Gb_Apu.cpp index 866594ddf..82a9cc1b6 100644 --- a/game-music-emu/gme/Gb_Apu.cpp +++ b/game-music-emu/gme/Gb_Apu.cpp @@ -123,7 +123,7 @@ void Gb_Apu::reset() 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 --git a/game-music-emu/gme/Gb_Apu.h b/game-music-emu/gme/Gb_Apu.h index e74ebc55b..9b251262f 100644 --- a/game-music-emu/gme/Gb_Apu.h +++ b/game-music-emu/gme/Gb_Apu.h @@ -68,7 +68,7 @@ private: 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 --git a/game-music-emu/gme/Gb_Cpu.cpp b/game-music-emu/gme/Gb_Cpu.cpp index bfae186e9..db1abee58 100644 --- a/game-music-emu/gme/Gb_Cpu.cpp +++ b/game-music-emu/gme/Gb_Cpu.cpp @@ -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" @@ -89,12 +89,6 @@ unsigned const n_flag = 0x40; unsigned const h_flag = 0x20; unsigned const c_flag = 0x10; -#ifdef _MSC_VER -// warning C4101: 'blargg_failed_' : unreferenced local variable -// -- produced by the BOOST_STATIC_ASSERT line below -#pragma warning(disable:4101) -#endif - bool Gb_Cpu::run( blargg_long cycle_count ) { state_.remain = blargg_ulong (cycle_count + clocks_per_instr) / clocks_per_instr; @@ -102,8 +96,6 @@ bool Gb_Cpu::run( blargg_long cycle_count ) 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 @@ -116,11 +108,11 @@ bool Gb_Cpu::run( blargg_long cycle_count ) 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 ); @@ -168,7 +160,7 @@ loop: #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;\ @@ -688,7 +680,7 @@ loop: 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; @@ -696,7 +688,7 @@ loop: 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; @@ -717,7 +709,7 @@ loop: temp += prev; flags &= z_flag; add_16_hl: - rp.hl = (uint16_t)temp; + rp.hl = temp; add_16_comm: flags |= (temp >> 12) & c_flag; flags |= (((temp & 0x0FFF) - (prev & 0x0FFF)) >> 7) & h_flag; diff --git a/game-music-emu/gme/Gb_Cpu.h b/game-music-emu/gme/Gb_Cpu.h index 0994cec29..d3df30cac 100644 --- a/game-music-emu/gme/Gb_Cpu.h +++ b/game-music-emu/gme/Gb_Cpu.h @@ -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 @@ typedef unsigned gb_addr_t; // 16-bit CPU address 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 @@ public: 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 @@ private: 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 --git a/game-music-emu/gme/Gb_Oscs.h b/game-music-emu/gme/Gb_Oscs.h index d7f88ea14..8cb026c3e 100644 --- a/game-music-emu/gme/Gb_Oscs.h +++ b/game-music-emu/gme/Gb_Oscs.h @@ -15,7 +15,7 @@ struct Gb_Osc 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 @@ struct Gb_Wave : Gb_Osc 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 --git a/game-music-emu/gme/Gbs_Emu.cpp b/game-music-emu/gme/Gbs_Emu.cpp index 05a0b9935..6c5def339 100644 --- a/game-music-emu/gme/Gbs_Emu.cpp +++ b/game-music-emu/gme/Gbs_Emu.cpp @@ -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_emu () { return BLARGG_NEW Gbs_Emu ; } 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_; +BLARGG_EXPORT extern gme_type_t const gme_gbs_type = &gme_gbs_type_; // Setup @@ -175,7 +175,7 @@ void Gbs_Emu::update_timer() 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 --git a/game-music-emu/gme/Gbs_Emu.h b/game-music-emu/gme/Gbs_Emu.h index b233a2b4f..580f395c6 100644 --- a/game-music-emu/gme/Gbs_Emu.h +++ b/game-music-emu/gme/Gbs_Emu.h @@ -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 --git a/game-music-emu/gme/Gme_File.cpp b/game-music-emu/gme/Gme_File.cpp index 912a9adc1..a5e4516d6 100644 --- a/game-music-emu/gme/Gme_File.cpp +++ b/game-music-emu/gme/Gme_File.cpp @@ -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" @@ -60,8 +60,8 @@ blargg_err_t Gme_File::load_mem_( byte const* data, long size ) blargg_err_t Gme_File::load_( Data_Reader& in ) { RETURN_ERR( file_data.resize( in.remain() ) ); - RETURN_ERR( in.read( file_data.begin(), (long)file_data.size() ) ); - return load_mem_( file_data.begin(), (long)file_data.size() ); + RETURN_ERR( in.read( file_data.begin(), file_data.size() ) ); + return load_mem_( file_data.begin(), file_data.size() ); } // public load functions call this at beginning diff --git a/game-music-emu/gme/Gme_File.h b/game-music-emu/gme/Gme_File.h index 2d0a57344..3ec36bc8e 100644 --- a/game-music-emu/gme/Gme_File.h +++ b/game-music-emu/gme/Gme_File.h @@ -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 @@ public: 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 @@ Music_Emu* gme_new_( Music_Emu*, long sample_rate ); { 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 --git a/game-music-emu/gme/Gym_Emu.cpp b/game-music-emu/gme/Gym_Emu.cpp index f149aebb5..bb99ff033 100644 --- a/game-music-emu/gme/Gym_Emu.cpp +++ b/game-music-emu/gme/Gym_Emu.cpp @@ -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_emu () { return BLARGG_NEW Gym_Emu ; } 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_; +BLARGG_EXPORT extern gme_type_t const gme_gym_type = &gme_gym_type_; // Setup diff --git a/game-music-emu/gme/Gym_Emu.h b/game-music-emu/gme/Gym_Emu.h index 1e4ed8b3e..290f57f5c 100644 --- a/game-music-emu/gme/Gym_Emu.h +++ b/game-music-emu/gme/Gym_Emu.h @@ -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 --git a/game-music-emu/gme/Hes_Apu.cpp b/game-music-emu/gme/Hes_Apu.cpp index 56b59dd77..1df811592 100644 --- a/game-music-emu/gme/Hes_Apu.cpp +++ b/game-music-emu/gme/Hes_Apu.cpp @@ -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" @@ -106,10 +106,10 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) unsigned noise_lfsr = this->noise_lfsr; do { - int new_dac = 0x1F & (unsigned)-(int)(noise_lfsr >> 1 & 1); + int new_dac = 0x1F & -(noise_lfsr >> 1 & 1); // Implemented using "Galios configuration" // TODO: find correct LFSR algorithm - noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & (unsigned)-(int)(noise_lfsr & 1)); + noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1)); //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1)); int delta = new_dac - dac; if ( delta ) diff --git a/game-music-emu/gme/Hes_Apu.h b/game-music-emu/gme/Hes_Apu.h index 89dde02cd..1efc0a064 100644 --- a/game-music-emu/gme/Hes_Apu.h +++ b/game-music-emu/gme/Hes_Apu.h @@ -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 --git a/game-music-emu/gme/Hes_Cpu.cpp b/game-music-emu/gme/Hes_Cpu.cpp index 51aa4259f..d15145968 100644 --- a/game-music-emu/gme/Hes_Cpu.cpp +++ b/game-music-emu/gme/Hes_Cpu.cpp @@ -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" @@ -39,7 +39,7 @@ int const ram_addr = 0x2000; // status flags int const st_n = 0x80; int const st_v = 0x40; -//unused: int const st_t = 0x20; +int const st_t = 0x20; int const st_b = 0x10; int const st_d = 0x08; int const st_i = 0x04; @@ -87,12 +87,6 @@ void Hes_Cpu::set_mmr( int reg, int bank ) #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,14 +94,14 @@ bool Hes_Cpu::run( hes_time_t 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 ); #define IS_NEG (nz & 0x8080) @@ -126,11 +120,11 @@ bool Hes_Cpu::run( hes_time_t end_time ) 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 ); } @@ -159,7 +153,7 @@ loop: check( (unsigned) x < 0x100 ); uint8_t const* instr = s.code_map [pc >> page_shift]; - fuint8 opcode; + uint8_t opcode; // TODO: eliminate this special case #if BLARGG_NONPORTABLE @@ -193,7 +187,7 @@ loop: 4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F }; // 0x00 was 8 - fuint16 data; + uint16_t data; data = clock_table [opcode]; if ( (s_time += data) >= 0 ) goto possibly_out_of_time; @@ -230,10 +224,10 @@ possibly_out_of_time: // TODO: more efficient way to handle negative branch that wraps PC around #define BRANCH( cond )\ {\ - fint16 offset = (BOOST::int8_t) data;\ + int16_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;\ } @@ -283,7 +277,7 @@ possibly_out_of_time: case 0xCF: case 0xDF: case 0xEF: { - fuint16 t = 0x101 * READ_LOW( data ); + uint16_t t = 0x101 * READ_LOW( data ); t ^= 0xFF; pc++; data = GET_MSB(); @@ -311,7 +305,7 @@ possibly_out_of_time: goto branch_taken; 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; @@ -332,7 +326,7 @@ possibly_out_of_time: case 0xBD:{// LDA abs,X PAGE_CROSS_PENALTY( data + x ); - fuint16 addr = GET_ADDR() + x; + uint16_t addr = GET_ADDR() + x; pc += 2; CPU_READ_FAST( this, addr, TIME, nz ); a = nz; @@ -340,7 +334,7 @@ possibly_out_of_time: } case 0x9D:{// STA abs,X - fuint16 addr = GET_ADDR() + x; + uint16_t addr = GET_ADDR() + x; pc += 2; CPU_WRITE_FAST( this, addr, a, TIME ); goto loop; @@ -354,7 +348,7 @@ possibly_out_of_time: goto loop; case 0xAE:{// LDX abs - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; CPU_READ_FAST( this, addr, TIME, nz ); x = nz; @@ -369,7 +363,7 @@ possibly_out_of_time: // Load/store { - fuint16 addr; + uint16_t addr; case 0x91: // STA (ind),Y addr = 0x100 * READ_LOW( uint8_t (data + 1) ); addr += READ_LOW( data ) + y; @@ -395,7 +389,7 @@ possibly_out_of_time: } { - fuint16 addr; + uint16_t addr; case 0xA1: // LDA (ind,X) data = uint8_t (data + x); case 0xB2: // LDA (ind) @@ -425,7 +419,7 @@ possibly_out_of_time: case 0xBE:{// LDX abs,y PAGE_CROSS_PENALTY( data + y ); - fuint16 addr = GET_ADDR() + y; + uint16_t addr = GET_ADDR() + y; pc += 2; FLUSH_TIME(); x = nz = READ( addr ); @@ -449,7 +443,7 @@ possibly_out_of_time: case 0x3C: // BIT abs,x data += x; case 0x2C:{// BIT abs - fuint16 addr; + uint16_t addr; ADD_PAGE( addr ); FLUSH_TIME(); nz = READ( addr ); @@ -472,7 +466,7 @@ possibly_out_of_time: goto loop; { - fuint16 addr; + uint16_t addr; case 0xB3: // TST abs,x addr = GET_MSB() + x; @@ -505,7 +499,7 @@ possibly_out_of_time: goto loop; { - fuint16 addr; + uint16_t addr; case 0x0C: // TSB abs case 0x1C: // TRB abs addr = GET_ADDR(); @@ -610,7 +604,7 @@ possibly_out_of_time: data += x; PAGE_CROSS_PENALTY( data ); case 0xAC:{// LDY abs - fuint16 addr = data + 0x100 * GET_MSB(); + uint16_t addr = data + 0x100 * GET_MSB(); pc += 2; FLUSH_TIME(); y = nz = READ( addr ); @@ -619,7 +613,7 @@ possibly_out_of_time: } { - fuint8 temp; + uint8_t temp; case 0x8C: // STY abs temp = y; goto store_abs; @@ -627,7 +621,7 @@ possibly_out_of_time: case 0x8E: // STX abs temp = x; store_abs: - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; FLUSH_TIME(); WRITE( addr, temp ); @@ -638,7 +632,7 @@ possibly_out_of_time: // Compare case 0xEC:{// CPX abs - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc++; FLUSH_TIME(); data = READ( addr ); @@ -657,7 +651,7 @@ possibly_out_of_time: goto loop; case 0xCC:{// CPY abs - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc++; FLUSH_TIME(); data = READ( addr ); @@ -684,7 +678,7 @@ possibly_out_of_time: 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;\ + uint16_t temp = READ_LOW( data ) + y;\ PAGE_CROSS_PENALTY( temp );\ data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ goto ptr##op;\ @@ -742,8 +736,8 @@ possibly_out_of_time: 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 + 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; @@ -771,7 +765,7 @@ possibly_out_of_time: 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; @@ -877,7 +871,7 @@ possibly_out_of_time: case 0xD6: // DEC zp,x data = uint8_t (data + x); case 0xC6: // DEC zp - nz = (unsigned) -1; + nz = (uint16_t) -1; add_nz_zp: nz += READ_LOW( data ); write_nz_zp: @@ -902,7 +896,7 @@ possibly_out_of_time: case 0xCE: // DEC abs data = GET_ADDR(); dec_ptr: - nz = (unsigned) -1; + nz = (uint16_t) -1; inc_common: FLUSH_TIME(); nz += READ( data ); @@ -942,7 +936,7 @@ possibly_out_of_time: goto loop; #define SWAP_REGS( r1, r2 ) {\ - fuint8 t = r1;\ + uint8_t t = r1;\ r1 = r2;\ r2 = t;\ goto loop;\ @@ -984,7 +978,7 @@ possibly_out_of_time: 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; @@ -1018,8 +1012,8 @@ possibly_out_of_time: goto loop; case 0x28:{// PLP - fuint8 temp = POP(); - fuint8 changed = status ^ temp; + uint8_t temp = POP(); + uint8_t changed = status ^ temp; SET_STATUS( temp ); if ( !(changed & st_i) ) goto loop; // I flag didn't change @@ -1030,7 +1024,7 @@ possibly_out_of_time: #undef POP case 0x08: { // PHP - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); PUSH( temp | st_b ); goto loop; @@ -1039,7 +1033,7 @@ possibly_out_of_time: // Flags case 0x38: // SEC - c = (unsigned) ~0; + c = (uint16_t) ~0; goto loop; case 0x18: // CLC @@ -1107,7 +1101,7 @@ possibly_out_of_time: // Special case 0x53:{// TAM - fuint8 const bits = data; // avoid using data across function call + uint8_t const bits = data; // avoid using data across function call pc++; for ( int i = 0; i < 8; i++ ) if ( bits & (1 << i) ) @@ -1131,7 +1125,7 @@ possibly_out_of_time: case 0x03: // ST0 case 0x13: // ST1 case 0x23:{// ST2 - fuint16 addr = opcode >> 4; + uint16_t addr = opcode >> 4; if ( addr ) addr++; pc++; @@ -1153,7 +1147,7 @@ possibly_out_of_time: goto loop; case 0xF4: { // SET - //fuint16 operand = GET_MSB(); + //uint16_t operand = GET_MSB(); debug_printf( "SET not handled\n" ); //switch ( data ) //{ @@ -1165,10 +1159,10 @@ possibly_out_of_time: // Block transfer { - fuint16 in_alt; - fint16 in_inc; - fuint16 out_alt; - fint16 out_inc; + uint16_t in_alt; + int16_t in_inc; + uint16_t out_alt; + int16_t out_inc; case 0xE3: // TIA in_alt = 0; @@ -1199,8 +1193,8 @@ possibly_out_of_time: in_alt = 0; out_alt = 0; bxfer: - fuint16 in = GET_LE16( instr + 0 ); - fuint16 out = GET_LE16( instr + 2 ); + uint16_t in = GET_LE16( instr + 0 ); + uint16_t out = GET_LE16( instr + 2 ); int count = GET_LE16( instr + 4 ); if ( !count ) count = 0x10000; @@ -1212,7 +1206,7 @@ possibly_out_of_time: do { // TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O - fuint8 t = READ( in ); + uint8_t t = READ( in ); in += in_inc; in &= 0xFFFF; s.time += 6; @@ -1232,7 +1226,6 @@ possibly_out_of_time: // Illegal default: - assert( (unsigned) opcode <= 0xFF ); debug_printf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 ); illegal_encountered = true; goto loop; @@ -1253,7 +1246,7 @@ interrupt: pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ ); sp = (sp - 3) | 0x100; - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); if ( result_ == 6 ) temp |= st_b; @@ -1290,7 +1283,7 @@ out_of_time: r.y = y; { - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); r.status = temp; } diff --git a/game-music-emu/gme/Hes_Cpu.h b/game-music-emu/gme/Hes_Cpu.h index d46a0b8a8..cec46fa9e 100644 --- a/game-music-emu/gme/Hes_Cpu.h +++ b/game-music-emu/gme/Hes_Cpu.h @@ -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 @@ enum { future_hes_time = INT_MAX / 2 + 1 }; class Hes_Cpu { public: - typedef BOOST::uint8_t uint8_t; - void reset(); enum { page_size = 0x2000 }; @@ -27,7 +25,7 @@ public: // 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 @@ private: 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 --git a/game-music-emu/gme/Hes_Emu.cpp b/game-music-emu/gme/Hes_Emu.cpp index e3c700dbc..818691fdc 100644 --- a/game-music-emu/gme/Hes_Emu.cpp +++ b/game-music-emu/gme/Hes_Emu.cpp @@ -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_Emu.h" @@ -133,7 +133,7 @@ static Music_Emu* new_hes_emu () { return BLARGG_NEW Hes_Emu ; } 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_; +BLARGG_EXPORT extern gme_type_t const gme_hes_type = &gme_hes_type_; // Setup diff --git a/game-music-emu/gme/Hes_Emu.h b/game-music-emu/gme/Hes_Emu.h index b4e20fd67..08c1370d4 100644 --- a/game-music-emu/gme/Hes_Emu.h +++ b/game-music-emu/gme/Hes_Emu.h @@ -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 --git a/game-music-emu/gme/Kss_Cpu.cpp b/game-music-emu/gme/Kss_Cpu.cpp index 74230bf71..10eec4fcc 100644 --- a/game-music-emu/gme/Kss_Cpu.cpp +++ b/game-music-emu/gme/Kss_Cpu.cpp @@ -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 @@ -162,11 +162,6 @@ static byte const ed_dd_timing [0x100] = { 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 @@ bool Kss_Cpu::run( cpu_time_t end_time ) this->state = &s; bool warning = false; - typedef BOOST::int8_t int8_t; - union { regs_t rg; pairs_t rp; @@ -185,10 +178,10 @@ bool Kss_Cpu::run( cpu_time_t end_time ) 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; + 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; @@ -210,7 +203,7 @@ loop: uint8_t const* instr = s.read [pc >> page_shift]; #define GET_ADDR() GET_LE16( instr ) - fuint8 opcode; + uint8_t opcode; // TODO: eliminate this special case #if BLARGG_NONPORTABLE @@ -243,7 +236,7 @@ loop: 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; @@ -299,7 +292,7 @@ possibly_out_of_time: goto loop; case 0x3A:{// LD A,(addr) - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; rg.a = READ( addr ); goto loop; @@ -315,7 +308,7 @@ possibly_out_of_time: // 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 @@ possibly_out_of_time: 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 ); @@ -395,7 +388,7 @@ possibly_out_of_time: } case 0xFF: // RST - if ( pc > idle_addr ) + if ( pc >= idle_addr ) goto hit_idle_addr; CASE7( C7, CF, D7, DF, E7, EF, F7 ): data = pc; @@ -503,7 +496,7 @@ possibly_out_of_time: add_hl_data: { blargg_ulong sum = rp.hl + data; data ^= rp.hl; - rp.hl = (uint16_t)sum; + rp.hl = sum; flags = (flags & (S80 | Z40 | V04)) | (sum >> 16) | (sum >> 8 & (F20 | F08)) | @@ -693,21 +686,21 @@ possibly_out_of_time: 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; @@ -730,7 +723,7 @@ possibly_out_of_time: // 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)); @@ -739,7 +732,7 @@ possibly_out_of_time: } case 0x0F:{// RRCA - fuint16 temp = rg.a; + uint16_t temp = rg.a; flags = (flags & (S80 | Z40 | P04)) | (temp & C01); temp = (temp << 7) | (temp >> 1); @@ -753,12 +746,12 @@ possibly_out_of_time: flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08)) | (temp >> 8); - rg.a = (uint8_t)temp; + rg.a = temp; goto loop; } 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); @@ -768,7 +761,7 @@ possibly_out_of_time: // Misc case 0x2F:{// CPL - fuint16 temp = ~rg.a; + uint16_t temp = ~rg.a; flags = (flags & (S80 | Z40 | P04 | C01)) | (temp & (F20 | F08)) | (H10 | N02); @@ -794,21 +787,21 @@ possibly_out_of_time: 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; @@ -849,7 +842,7 @@ possibly_out_of_time: // 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;\ @@ -868,7 +861,7 @@ possibly_out_of_time: } #define RL( read, write ) {\ - fuint16 result = (read << 1) | (flags & C01);\ + uint16_t result = (read << 1) | (flags & C01);\ flags = SZ28PC( result );\ write;\ goto loop;\ @@ -886,7 +879,7 @@ possibly_out_of_time: } #define SLA( read, add, write ) {\ - fuint16 result = (read << 1) | add;\ + uint16_t result = (read << 1) | add;\ flags = SZ28PC( result );\ write;\ goto loop;\ @@ -917,7 +910,7 @@ possibly_out_of_time: // 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 );\ @@ -937,8 +930,8 @@ possibly_out_of_time: } #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;\ @@ -957,7 +950,7 @@ possibly_out_of_time: } #define SRA( read, write ) {\ - fuint8 result = read;\ + uint8_t result = read;\ flags = result & C01;\ result = (result & 0x80) | (result >> 1);\ flags |= SZ28P( result );\ @@ -977,7 +970,7 @@ possibly_out_of_time: } #define SRL( read, write ) {\ - fuint8 result = read;\ + uint8_t result = read;\ flags = result & C01;\ result >>= 1;\ flags |= SZ28P( result );\ @@ -1085,7 +1078,7 @@ possibly_out_of_time: blargg_ulong sum = temp + (flags & C01); flags = ~data >> 2 & N02; if ( flags ) - sum = (blargg_ulong)-(blargg_long)sum; + sum = -sum; sum += rp.hl; temp ^= rp.hl; temp ^= sum; @@ -1093,7 +1086,7 @@ possibly_out_of_time: (temp >> 8 & H10) | (sum >> 8 & (S80 | F20 | F08)) | ((temp - -0x8000) >> 14 & V04); - rp.hl = (uint16_t)sum; + rp.hl = sum; if ( (uint16_t) sum ) goto loop; flags |= Z40; @@ -1121,7 +1114,7 @@ possibly_out_of_time: 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; @@ -1129,21 +1122,21 @@ possibly_out_of_time: 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 ); @@ -1152,7 +1145,7 @@ possibly_out_of_time: } 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 ); @@ -1176,7 +1169,7 @@ possibly_out_of_time: 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 ); @@ -1209,7 +1202,7 @@ possibly_out_of_time: 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 ); @@ -1241,7 +1234,7 @@ possibly_out_of_time: 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 ); @@ -1267,7 +1260,7 @@ possibly_out_of_time: case 0xB2: // INIR inc = +1; - fuint16 addr = rp.hl; + uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = IN( rp.bc ); @@ -1332,7 +1325,7 @@ possibly_out_of_time: //////////////////////////////////////// DD/FD prefix { - fuint16 ixy; + uint16_t ixy; case 0xDD: ixy = ix; goto ix_prefix; @@ -1528,7 +1521,7 @@ possibly_out_of_time: goto loop; case 0x22:{// LD (ADDR),IXY - fuint16 addr = GET_ADDR(); + uint16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, ixy ); goto loop; @@ -1540,7 +1533,7 @@ possibly_out_of_time: 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; @@ -1564,7 +1557,7 @@ possibly_out_of_time: 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) | @@ -1666,7 +1659,7 @@ possibly_out_of_time: 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 --git a/game-music-emu/gme/Kss_Cpu.h b/game-music-emu/gme/Kss_Cpu.h index aec24e9d5..d31864cd3 100644 --- a/game-music-emu/gme/Kss_Cpu.h +++ b/game-music-emu/gme/Kss_Cpu.h @@ -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 @@ void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data ); 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 @@ public: 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 @@ public: #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 --git a/game-music-emu/gme/Kss_Emu.cpp b/game-music-emu/gme/Kss_Emu.cpp index def6ee199..fd4905ce3 100644 --- a/game-music-emu/gme/Kss_Emu.cpp +++ b/game-music-emu/gme/Kss_Emu.cpp @@ -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_Emu.h" @@ -102,7 +102,7 @@ static Music_Emu* new_kss_emu () { return BLARGG_NEW Kss_Emu ; } 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_; +BLARGG_EXPORT extern gme_type_t const gme_kss_type = &gme_kss_type_; // Setup diff --git a/game-music-emu/gme/Kss_Emu.h b/game-music-emu/gme/Kss_Emu.h index e457f7311..467b28abd 100644 --- a/game-music-emu/gme/Kss_Emu.h +++ b/game-music-emu/gme/Kss_Emu.h @@ -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 --git a/game-music-emu/gme/Kss_Scc_Apu.cpp b/game-music-emu/gme/Kss_Scc_Apu.cpp index 1ab14bd00..bb84b3250 100644 --- a/game-music-emu/gme/Kss_Scc_Apu.cpp +++ b/game-music-emu/gme/Kss_Scc_Apu.cpp @@ -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 @@ void Scc_Apu::run_until( blip_time_t end_time ) 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 --git a/game-music-emu/gme/Kss_Scc_Apu.h b/game-music-emu/gme/Kss_Scc_Apu.h index c8c69f118..eda5747fe 100644 --- a/game-music-emu/gme/Kss_Scc_Apu.h +++ b/game-music-emu/gme/Kss_Scc_Apu.h @@ -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 --git a/game-music-emu/gme/M3u_Playlist.cpp b/game-music-emu/gme/M3u_Playlist.cpp index ffd7d82c3..e751d4cc8 100644 --- a/game-music-emu/gme/M3u_Playlist.cpp +++ b/game-music-emu/gme/M3u_Playlist.cpp @@ -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" @@ -407,7 +407,7 @@ blargg_err_t M3u_Playlist::parse() blargg_err_t M3u_Playlist::load( Data_Reader& in ) { RETURN_ERR( data.resize( in.remain() + 1 ) ); - RETURN_ERR( in.read( data.begin(), long(data.size() - 1) ) ); + RETURN_ERR( in.read( data.begin(), data.size() - 1 ) ); return parse(); } diff --git a/game-music-emu/gme/M3u_Playlist.h b/game-music-emu/gme/M3u_Playlist.h index fe90dd9f8..6757b7cfb 100644 --- a/game-music-emu/gme/M3u_Playlist.h +++ b/game-music-emu/gme/M3u_Playlist.h @@ -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 @@ -43,7 +43,7 @@ public: int repeat; // count }; entry_t const& operator [] ( int i ) const { return entries [i]; } - int size() const { return int(entries.size()); } + int size() const { return entries.size(); } void clear(); diff --git a/game-music-emu/gme/Multi_Buffer.cpp b/game-music-emu/gme/Multi_Buffer.cpp index b86142f63..5f000ceeb 100644 --- a/game-music-emu/gme/Multi_Buffer.cpp +++ b/game-music-emu/gme/Multi_Buffer.cpp @@ -81,7 +81,7 @@ void Stereo_Buffer::clock_rate( long rate ) 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::clear() 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,18 +161,18 @@ void Stereo_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) 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 ); BLIP_READER_NEXT( right, bass ); - out [0] = (blip_sample_t)l; - out [1] = (blip_sample_t)r; + out [0] = l; + out [1] = r; out += 2; } @@ -191,18 +191,18 @@ void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, blargg_long count 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 ); BLIP_READER_NEXT( right, bass ); - out [0] = (blip_sample_t)l; - out [1] = (blip_sample_t)r; + out [0] = l; + out [1] = r; out += 2; } @@ -219,12 +219,12 @@ void Stereo_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) 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 ); - out [0] = (blip_sample_t)s; - out [1] = (blip_sample_t)s; + out [0] = s; + out [1] = s; out += 2; } diff --git a/game-music-emu/gme/Music_Emu.cpp b/game-music-emu/gme/Music_Emu.cpp index b7fc4993b..66ffa2d96 100644 --- a/game-music-emu/gme/Music_Emu.cpp +++ b/game-music-emu/gme/Music_Emu.cpp @@ -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" @@ -18,7 +18,6 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #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; @@ -52,7 +51,7 @@ void Music_Emu::unload() Music_Emu::Music_Emu() { effects_buffer = 0; - + multi_channel_ = false; sample_rate_ = 0; mute_mask_ = 0; tempo_ = 1.0; @@ -96,6 +95,25 @@ void Music_Emu::set_equalizer( equalizer_t const& eq ) 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 +163,7 @@ blargg_err_t Music_Emu::start_track( int track ) 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_ ) @@ -175,7 +193,7 @@ 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 +203,7 @@ long Music_Emu::tell_samples() const 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 +281,7 @@ blargg_err_t Music_Emu::skip_( long count ) 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 +363,7 @@ blargg_err_t Music_Emu::play( long out_count, sample_t* out ) else { require( current_track() >= 0 ); - require( out_count % stereo == 0 ); + require( out_count % out_channels() == 0 ); assert( emu_time >= out_time ); @@ -357,7 +375,7 @@ blargg_err_t Music_Emu::play( long out_count, sample_t* out ) { // during a run of silence, run emulator at >=2x speed so it gets ahead long ahead_time = silence_lookahead * (out_time + out_count - silence_time) + silence_time; - while ( emu_time < ahead_time && !(buf_remain || emu_track_ended_) ) + while ( emu_time < ahead_time && !(buf_remain | emu_track_ended_) ) fill_buf(); // fill with silence @@ -365,7 +383,7 @@ blargg_err_t Music_Emu::play( long out_count, sample_t* out ) 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 +419,7 @@ blargg_err_t Music_Emu::play( long out_count, sample_t* out ) } } - if ( out_time > fade_start ) + if ( fade_start >= 0 && out_time > fade_start ) handle_fade( out_count, out ); } out_time += out_count; diff --git a/game-music-emu/gme/Music_Emu.h b/game-music-emu/gme/Music_Emu.h index d98f7ce7e..ee0f8379c 100644 --- a/game-music-emu/gme/Music_Emu.h +++ b/game-music-emu/gme/Music_Emu.h @@ -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 @@ public: // 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 @@ public: // Names of voices const char** voice_names() const; + + bool multi_channel() const; // Track status/control @@ -127,6 +134,7 @@ protected: 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 +157,11 @@ private: 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; @@ -179,7 +191,7 @@ private: 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 --git a/game-music-emu/gme/Nes_Cpu.cpp b/game-music-emu/gme/Nes_Cpu.cpp index ac548d0ff..5eb0862a3 100644 --- a/game-music-emu/gme/Nes_Cpu.cpp +++ b/game-music-emu/gme/Nes_Cpu.cpp @@ -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 @@ void Nes_Cpu::map_code( nes_addr_t start, unsigned size, void const* data, bool #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 @@ bool Nes_Cpu::run( nes_time_t end_time ) 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 @@ loop: 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 @@ loop: 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 @@ possibly_out_of_time: #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) );\ } @@ -288,10 +283,10 @@ 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 @@ imm##op: 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; @@ -378,7 +373,7 @@ imm##op: goto loop; { - fuint16 addr; + uint16_t addr; case 0x99: // STA abs,Y addr = y + GET_ADDR(); @@ -434,7 +429,7 @@ imm##op: // common read instructions { - fuint16 addr; + uint16_t addr; case 0xA1: // LDA (ind,X) IND_X( addr ) @@ -550,7 +545,7 @@ imm##op: } { - fuint8 temp; + uint8_t temp; case 0x8C: // STY abs temp = y; goto store_abs; @@ -659,8 +654,8 @@ imm##op: 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; @@ -688,7 +683,7 @@ imm##op: 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; @@ -780,7 +775,7 @@ imm##op: case 0xD6: // DEC zp,x data = uint8_t (data + x); 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 @@ imm##op: 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 @@ imm##op: 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 @@ imm##op: } 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 @@ imm##op: } case 0x08: { // PHP - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); PUSH( temp | (st_b | st_r) ); goto loop; @@ -897,7 +892,7 @@ imm##op: // Flags case 0x38: // SEC - c = (unsigned) ~0; + c = (uint16_t) ~0; goto loop; case 0x18: // CLC @@ -983,12 +978,6 @@ imm##op: 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; @@ -1003,8 +992,8 @@ imm##op: 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 @@ interrupt: 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 @@ stop: r.y = y; { - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); r.status = temp; } diff --git a/game-music-emu/gme/Nes_Cpu.h b/game-music-emu/gme/Nes_Cpu.h index c139e069f..878b5ba5c 100644 --- a/game-music-emu/gme/Nes_Cpu.h +++ b/game-music-emu/gme/Nes_Cpu.h @@ -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 @@ enum { future_nes_time = INT_MAX / 2 + 1 }; 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 @@ public: // 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 @@ private: 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 --git a/game-music-emu/gme/Nes_Fme7_Apu.cpp b/game-music-emu/gme/Nes_Fme7_Apu.cpp index bc8ca7f48..93973e409 100644 --- a/game-music-emu/gme/Nes_Fme7_Apu.cpp +++ b/game-music-emu/gme/Nes_Fme7_Apu.cpp @@ -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 --git a/game-music-emu/gme/Nes_Fme7_Apu.h b/game-music-emu/gme/Nes_Fme7_Apu.h index 9069bd06a..b79ed6f5e 100644 --- a/game-music-emu/gme/Nes_Fme7_Apu.h +++ b/game-music-emu/gme/Nes_Fme7_Apu.h @@ -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 --git a/game-music-emu/gme/Nes_Namco_Apu.cpp b/game-music-emu/gme/Nes_Namco_Apu.cpp index f3235b383..3e5fc1491 100644 --- a/game-music-emu/gme/Nes_Namco_Apu.cpp +++ b/game-music-emu/gme/Nes_Namco_Apu.cpp @@ -90,7 +90,7 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time ) 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 --git a/game-music-emu/gme/Nes_Namco_Apu.h b/game-music-emu/gme/Nes_Namco_Apu.h index db5fea4bf..876d85e0a 100644 --- a/game-music-emu/gme/Nes_Namco_Apu.h +++ b/game-music-emu/gme/Nes_Namco_Apu.h @@ -54,24 +54,24 @@ private: 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 --git a/game-music-emu/gme/Nes_Vrc6_Apu.h b/game-music-emu/gme/Nes_Vrc6_Apu.h index 18722233f..23a6519fc 100644 --- a/game-music-emu/gme/Nes_Vrc6_Apu.h +++ b/game-music-emu/gme/Nes_Vrc6_Apu.h @@ -40,7 +40,7 @@ private: struct Vrc6_Osc { - BOOST::uint8_t regs [3]; + uint8_t regs [3]; Blip_Buffer* output; int delay; int last_amp; @@ -66,11 +66,11 @@ private: 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 --git a/game-music-emu/gme/Nsf_Emu.cpp b/game-music-emu/gme/Nsf_Emu.cpp index eab4bfbfd..74d76850e 100644 --- a/game-music-emu/gme/Nsf_Emu.cpp +++ b/game-music-emu/gme/Nsf_Emu.cpp @@ -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 "Nsf_Emu.h" @@ -130,7 +130,7 @@ static Music_Emu* new_nsf_emu () { return BLARGG_NEW Nsf_Emu ; } 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_; +BLARGG_EXPORT extern gme_type_t const gme_nsf_type = &gme_nsf_type_; // Setup diff --git a/game-music-emu/gme/Nsf_Emu.h b/game-music-emu/gme/Nsf_Emu.h index 0b001686c..e538b1b30 100644 --- a/game-music-emu/gme/Nsf_Emu.h +++ b/game-music-emu/gme/Nsf_Emu.h @@ -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 --git a/game-music-emu/gme/Nsfe_Emu.cpp b/game-music-emu/gme/Nsfe_Emu.cpp index 5559bca9b..035f99dee 100644 --- a/game-music-emu/gme/Nsfe_Emu.cpp +++ b/game-music-emu/gme/Nsfe_Emu.cpp @@ -1,6 +1,4 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ - -#define _CRT_SECURE_NO_WARNINGS +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ #include "Nsfe_Emu.h" @@ -37,7 +35,7 @@ inline void Nsfe_Info::unload() void Nsfe_Info::disable_playlist( bool b ) { playlist_disabled = b; - info.track_count = (byte)playlist.size(); + info.track_count = playlist.size(); if ( !info.track_count || playlist_disabled ) info.track_count = actual_track_count_; } @@ -136,6 +134,9 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) RETURN_ERR( in.read( block_header, sizeof block_header ) ); blargg_long size = get_le32( block_header [0] ); blargg_long tag = get_le32( block_header [1] ); + + 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) ); @@ -173,7 +174,7 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) blargg_vector chars; blargg_vector strs; RETURN_ERR( read_strs( in, size, chars, strs ) ); - int n = (int)strs.size(); + int n = strs.size(); if ( n > 3 ) copy_str( strs [3], info.dumper, sizeof info.dumper ); @@ -192,7 +193,7 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) case BLARGG_4CHAR('e','m','i','t'): RETURN_ERR( track_times.resize( size / 4 ) ); - RETURN_ERR( in.read( track_times.begin(), (long)track_times.size() * 4 ) ); + RETURN_ERR( in.read( track_times.begin(), track_times.size() * 4 ) ); break; case BLARGG_4CHAR('l','b','l','t'): @@ -242,7 +243,7 @@ blargg_err_t Nsfe_Info::track_info_( track_info_t* out, int track ) const 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; } @@ -300,7 +301,7 @@ static Music_Emu* new_nsfe_emu () { return BLARGG_NEW Nsfe_Emu ; } 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_; +BLARGG_EXPORT extern gme_type_t const gme_nsfe_type = &gme_nsfe_type_; blargg_err_t Nsfe_Emu::load_( Data_Reader& in ) diff --git a/game-music-emu/gme/Nsfe_Emu.h b/game-music-emu/gme/Nsfe_Emu.h index 32b05d55e..fd65f0af8 100644 --- a/game-music-emu/gme/Nsfe_Emu.h +++ b/game-music-emu/gme/Nsfe_Emu.h @@ -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 --git a/game-music-emu/gme/Sap_Apu.cpp b/game-music-emu/gme/Sap_Apu.cpp index daf72ed4a..26fa2d13f 100644 --- a/game-music-emu/gme/Sap_Apu.cpp +++ b/game-music-emu/gme/Sap_Apu.cpp @@ -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" @@ -30,7 +30,7 @@ static void gen_poly( blargg_ulong mask, int count, byte* out ) { // implemented using "Galios configuration" bits |= (n & 1) << b; - n = (n >> 1) ^ (mask & (blargg_ulong)-(blargg_long)(n & 1)); + n = (n >> 1) ^ (mask & -(n & 1)); } while ( b++ < 7 ); *out++ = bits; diff --git a/game-music-emu/gme/Sap_Apu.h b/game-music-emu/gme/Sap_Apu.h index 8cdd7e50c..1b67571bc 100644 --- a/game-music-emu/gme/Sap_Apu.h +++ b/game-music-emu/gme/Sap_Apu.h @@ -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 --git a/game-music-emu/gme/Sap_Cpu.cpp b/game-music-emu/gme/Sap_Cpu.cpp index fd9112769..76ae277ad 100644 --- a/game-music-emu/gme/Sap_Cpu.cpp +++ b/game-music-emu/gme/Sap_Cpu.cpp @@ -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 @@ void Sap_Cpu::reset( void* new_mem ) #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 @@ bool Sap_Cpu::run( sap_time_t end_time ) 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 @@ loop: 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 @@ loop: 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 @@ possibly_out_of_time: #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) );\ } @@ -232,8 +226,8 @@ 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 @@ imm##op: 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; @@ -322,7 +316,7 @@ imm##op: goto loop; { - fuint16 addr; + uint16_t addr; case 0x99: // STA abs,Y addr = y + GET_ADDR(); @@ -378,7 +372,7 @@ imm##op: // common read instructions { - fuint16 addr; + uint16_t addr; case 0xA1: // LDA (ind,X) IND_X( addr ) @@ -494,7 +488,7 @@ imm##op: } { - fuint8 temp; + uint8_t temp; case 0x8C: // STY abs temp = y; goto store_abs; @@ -604,8 +598,8 @@ imm##op: 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; @@ -633,7 +627,7 @@ imm##op: 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; @@ -725,7 +719,7 @@ imm##op: case 0xD6: // DEC zp,x data = uint8_t (data + x); 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 @@ imm##op: 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 @@ imm##op: 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 @@ imm##op: } 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 @@ imm##op: } case 0x08: { // PHP - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); PUSH( temp | (st_b | st_r) ); goto loop; @@ -843,7 +837,7 @@ imm##op: // Flags case 0x38: // SEC - c = (unsigned) ~0; + c = (uint16_t) ~0; goto loop; case 0x18: // CLC @@ -932,7 +926,6 @@ imm##op: //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 @@ interrupt: 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 @@ stop: r.y = y; { - fuint8 temp; + uint8_t temp; CALC_STATUS( temp ); r.status = temp; } diff --git a/game-music-emu/gme/Sap_Cpu.h b/game-music-emu/gme/Sap_Cpu.h index ea42c7a80..fdfb9a310 100644 --- a/game-music-emu/gme/Sap_Cpu.h +++ b/game-music-emu/gme/Sap_Cpu.h @@ -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 @@ enum { future_sap_time = INT_MAX / 2 + 1 }; 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 @@ public: // 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 --git a/game-music-emu/gme/Sap_Emu.cpp b/game-music-emu/gme/Sap_Emu.cpp index 31bce6a90..dc5d666d6 100644 --- a/game-music-emu/gme/Sap_Emu.cpp +++ b/game-music-emu/gme/Sap_Emu.cpp @@ -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_Emu.h" @@ -119,7 +119,7 @@ static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out char const* tag = (char const*) in; while ( in < line_end && *in > ' ' ) in++; - int tag_len = int((char const*) in - tag); + int tag_len = (char const*) in - tag; while ( in < line_end && *in <= ' ' ) in++; @@ -236,8 +236,7 @@ static Music_Emu* new_sap_emu () { return BLARGG_NEW Sap_Emu ; } 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_; - +BLARGG_EXPORT extern gme_type_t const gme_sap_type = &gme_sap_type_; // Setup @@ -256,7 +255,7 @@ blargg_err_t Sap_Emu::load_mem_( byte const* in, long size ) set_warning( info.warning ); set_track_count( info.track_count ); - set_voice_count( Sap_Apu::osc_count << (int)info.stereo ); + set_voice_count( Sap_Apu::osc_count << info.stereo ); apu_impl.volume( gain() ); return setup_buffer( 1773447 ); @@ -315,8 +314,8 @@ inline void Sap_Emu::call_init( int track ) case 'C': r.a = 0x70; - r.x = (BOOST::uint8_t)(info.music_addr&0xFF); - r.y = (BOOST::uint8_t)(info.music_addr >> 8); + r.x = info.music_addr&0xFF; + r.y = info.music_addr >> 8; run_routine( info.play_addr + 3 ); r.a = 0; r.x = track; diff --git a/game-music-emu/gme/Sap_Emu.h b/game-music-emu/gme/Sap_Emu.h index 5b0dc47df..f75312713 100644 --- a/game-music-emu/gme/Sap_Emu.h +++ b/game-music-emu/gme/Sap_Emu.h @@ -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 @@ private: // large items struct { byte padding1 [0x100]; - byte ram [0x10000]; - byte padding2 [0x100]; + byte ram [0x10000 + 0x100]; } mem; Sap_Apu_Impl apu_impl; diff --git a/game-music-emu/gme/Sms_Apu.cpp b/game-music-emu/gme/Sms_Apu.cpp index be226f8f9..b41fdec41 100644 --- a/game-music-emu/gme/Sms_Apu.cpp +++ b/game-music-emu/gme/Sms_Apu.cpp @@ -141,7 +141,7 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time ) do { int changed = shifter + 1; - shifter = (feedback & (unsigned)-(signed)(shifter & 1)) ^ (shifter >> 1); + shifter = (feedback & -(shifter & 1)) ^ (shifter >> 1); if ( changed & 2 ) // true if bits 0 and 1 differ { delta = -delta; diff --git a/game-music-emu/gme/Snes_Spc.cpp b/game-music-emu/gme/Snes_Spc.cpp index 26574223d..0b2077d8c 100644 --- a/game-music-emu/gme/Snes_Spc.cpp +++ b/game-music-emu/gme/Snes_Spc.cpp @@ -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 @@ void Snes_Spc::ram_loaded() 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. @@ -303,7 +303,7 @@ void Snes_Spc::set_output( sample_t* out, int size ) assert( out <= out_end ); } - dsp.set_output( out, int(out_end - out) ); + dsp.set_output( out, out_end - out ); } else { diff --git a/game-music-emu/gme/Snes_Spc.h b/game-music-emu/gme/Snes_Spc.h index d6c4e8135..68c780ab7 100644 --- a/game-music-emu/gme/Snes_Spc.h +++ b/game-music-emu/gme/Snes_Spc.h @@ -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 @@ public: // 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: 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 @@ private: 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 @@ private: 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 --git a/game-music-emu/gme/Spc_Cpu.cpp b/game-music-emu/gme/Spc_Cpu.cpp index db96ac390..998fe121b 100644 --- a/game-music-emu/gme/Spc_Cpu.cpp +++ b/game-music-emu/gme/Spc_Cpu.cpp @@ -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 @@ static unsigned char const glitch_probs [3] [256] = // 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, 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 @@ void Snes_Spc::cpu_write_smp_reg( int data, rel_time_t time, int addr ) 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 @@ void Snes_Spc::cpu_write( int data, int addr, rel_time_t time ) 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 @@ inline int Snes_Spc::cpu_read_smp_reg( int reg, rel_time_t time ) 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 @@ int Snes_Spc::cpu_read( int addr, rel_time_t time ) // 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 );\ @@ -527,7 +513,7 @@ BOOST::uint8_t* Snes_Spc::run_until_( time_t end_time )\ return ®S [r_cpuio0];\ } -#define cpu_lag_max (12 - 1) // DIV YA,X takes 12 clocks +int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks void Snes_Spc::end_frame( time_t end_time ) { diff --git a/game-music-emu/gme/Spc_Cpu.h b/game-music-emu/gme/Spc_Cpu.h index 8829e818b..2dd3e63c2 100644 --- a/game-music-emu/gme/Spc_Cpu.h +++ b/game-music-emu/gme/Spc_Cpu.h @@ -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 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #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() (int(pc - ram)) -#define READ_PC( pc ) (*(pc)) -#define READ_PC16( pc ) GET_LE16( pc ) +#define SET_PC( n ) (pc = n) +#define GET_PC() (pc) +#define READ_PC( pc ) (ram [pc]) +#define READ_PC16( pc ) READ_PROG16( pc ) -// TODO: remove non-wrapping versions? -#define SPC_NO_SP_WRAPAROUND 0 +#define SET_SP( v ) (sp = v) +#define GET_SP() ((uint8_t) (sp)) -#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++) - -#else #define PUSH16( data )\ {\ - int addr = int((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 @@ int const nz_neg_mask = 0x880; // either bit set indicates N flag set 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 @@ SPC_CPU_RUN_FUNC // 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 @@ loop: 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 @@ loop: */ // 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 @@ loop: #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 @@ loop: } case 0x6F:// RET - #if SPC_NO_SP_WRAPAROUND { - SET_PC( GET_LE16( sp ) ); - sp += 2; + uint8_t l, h; + POP( l ); + POP( h ); + SET_PC( l | (h << 8) ); } - #else - { - int addr = int(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; - } - #endif goto loop; case 0xE4: // MOV a,dp @@ -294,8 +259,7 @@ loop: 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 ); } } @@ -504,7 +468,7 @@ loop: 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;\ }\ @@ -878,7 +842,7 @@ loop: // 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 @@ loop: { 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 @@ loop: 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 @@ loop: } // 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 --git a/game-music-emu/gme/Spc_Dsp.cpp b/game-music-emu/gme/Spc_Dsp.cpp index ac0baed6b..51556434d 100644 --- a/game-music-emu/gme/Spc_Dsp.cpp +++ b/game-music-emu/gme/Spc_Dsp.cpp @@ -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 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // 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, @@ -155,7 +155,7 @@ inline void Spc_Dsp::init_counter() // counters start out with this synchronization m.counters [0] = 1; m.counters [1] = 0; - m.counters [2] = -0x20; + m.counters [2] = -0x20u; m.counters [3] = 0x0B; int n = 2; @@ -498,8 +498,9 @@ void Spc_Dsp::run( int clock_count ) // 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; diff --git a/game-music-emu/gme/Spc_Dsp.h b/game-music-emu/gme/Spc_Dsp.h index 3cec1bd91..b364f0845 100644 --- a/game-music-emu/gme/Spc_Dsp.h +++ b/game-music-emu/gme/Spc_Dsp.h @@ -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: 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 }; @@ -154,7 +149,7 @@ private: #include -inline int Spc_Dsp::sample_count() const { return int(m.out - m.out_begin); } +inline int Spc_Dsp::sample_count() const { return m.out - m.out_begin; } inline int Spc_Dsp::read( int addr ) const { diff --git a/game-music-emu/gme/Spc_Emu.cpp b/game-music-emu/gme/Spc_Emu.cpp index 341b53f9d..35086ce71 100644 --- a/game-music-emu/gme/Spc_Emu.cpp +++ b/game-music-emu/gme/Spc_Emu.cpp @@ -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_Emu.h" @@ -228,14 +228,14 @@ struct Spc_File : Gme_Info_ { RETURN_ERR( xid6.resize( xid6_size ) ); RETURN_ERR( in.skip( xid6_offset - Spc_Emu::header_size ) ); - RETURN_ERR( in.read( xid6.begin(), (long)xid6.size() ) ); + RETURN_ERR( in.read( xid6.begin(), xid6.size() ) ); } return 0; } blargg_err_t track_info_( track_info_t* out, int ) const { - get_spc_info( header, xid6.begin(), (long)xid6.size(), out ); + get_spc_info( header, xid6.begin(), xid6.size(), out ); return 0; } }; @@ -244,7 +244,7 @@ static Music_Emu* new_spc_emu () { return BLARGG_NEW Spc_Emu ; } 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_; +BLARGG_EXPORT extern gme_type_t const gme_spc_type = &gme_spc_type_; // Setup @@ -299,6 +299,11 @@ blargg_err_t Spc_Emu::start_track_( int track ) 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 ( spc_info.length > 0 ) + set_fade ( spc_info.length, 50 ); return 0; } diff --git a/game-music-emu/gme/Spc_Emu.h b/game-music-emu/gme/Spc_Emu.h index 09063f123..76e1ac63d 100644 --- a/game-music-emu/gme/Spc_Emu.h +++ b/game-music-emu/gme/Spc_Emu.h @@ -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 --git a/game-music-emu/gme/Spc_Filter.cpp b/game-music-emu/gme/Spc_Filter.cpp index 0f2d18a83..2cc77fc93 100644 --- a/game-music-emu/gme/Spc_Filter.cpp +++ b/game-music-emu/gme/Spc_Filter.cpp @@ -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 --git a/game-music-emu/gme/Spc_Filter.h b/game-music-emu/gme/Spc_Filter.h index 5cdcc3606..d9994af5f 100644 --- a/game-music-emu/gme/Spc_Filter.h +++ b/game-music-emu/gme/Spc_Filter.h @@ -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 --git a/game-music-emu/gme/Vgm_Emu.cpp b/game-music-emu/gme/Vgm_Emu.cpp index 0f6001aae..8f19b7de5 100644 --- a/game-music-emu/gme/Vgm_Emu.cpp +++ b/game-music-emu/gme/Vgm_Emu.cpp @@ -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" @@ -57,7 +57,7 @@ static byte const* skip_gd3_str( byte const* in, byte const* end ) static byte const* get_gd3_str( byte const* in, byte const* end, char* field ) { byte const* mid = skip_gd3_str( in, end ); - int len = int(mid - in) / 2 - 1; + int len = (mid - in) / 2 - 1; if ( len > 0 ) { len = min( len, (int) Gme_File::max_field_ ); @@ -108,7 +108,7 @@ byte const* Vgm_Emu::gd3_data( int* size ) const return 0; byte const* gd3 = data + header_size + gd3_offset; - long gd3_size = check_gd3_header( gd3, long(data_end - gd3) ); + long gd3_size = check_gd3_header( gd3, data_end - gd3 ); if ( !gd3_size ) return 0; @@ -184,7 +184,7 @@ struct Vgm_File : Gme_Info_ if ( gd3_size ) { RETURN_ERR( gd3.resize( gd3_size ) ); - RETURN_ERR( in.read( gd3.begin(), (long)gd3.size() ) ); + RETURN_ERR( in.read( gd3.begin(), gd3.size() ) ); } } return 0; @@ -203,10 +203,10 @@ static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; } 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_; +BLARGG_EXPORT 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_; +BLARGG_EXPORT extern gme_type_t const gme_vgz_type = &gme_vgz_type_; // Setup @@ -233,6 +233,25 @@ blargg_err_t Vgm_Emu::set_sample_rate_( long sample_rate ) 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 ); diff --git a/game-music-emu/gme/Vgm_Emu.h b/game-music-emu/gme/Vgm_Emu.h index 65895afaa..40cfb7102 100644 --- a/game-music-emu/gme/Vgm_Emu.h +++ b/game-music-emu/gme/Vgm_Emu.h @@ -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 @@ public: // 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 --git a/game-music-emu/gme/Vgm_Emu_Impl.cpp b/game-music-emu/gme/Vgm_Emu_Impl.cpp index 60dc099fe..0d400254d 100644 --- a/game-music-emu/gme/Vgm_Emu_Impl.cpp +++ b/game-music-emu/gme/Vgm_Emu_Impl.cpp @@ -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" diff --git a/game-music-emu/gme/Vgm_Emu_Impl.h b/game-music-emu/gme/Vgm_Emu_Impl.h index b50094a01..dadbb9207 100644 --- a/game-music-emu/gme/Vgm_Emu_Impl.h +++ b/game-music-emu/gme/Vgm_Emu_Impl.h @@ -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 diff --git a/game-music-emu/gme/Ym2413_Emu.cpp b/game-music-emu/gme/Ym2413_Emu.cpp index d1c7a71b1..01e796d95 100644 --- a/game-music-emu/gme/Ym2413_Emu.cpp +++ b/game-music-emu/gme/Ym2413_Emu.cpp @@ -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 --git a/game-music-emu/gme/Ym2413_Emu.h b/game-music-emu/gme/Ym2413_Emu.h index 53ce38a96..ed4fd11df 100644 --- a/game-music-emu/gme/Ym2413_Emu.h +++ b/game-music-emu/gme/Ym2413_Emu.h @@ -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 --git a/game-music-emu/gme/Ym2612_Emu.h b/game-music-emu/gme/Ym2612_Emu.h index 51cff6586..f62209a07 100644 --- a/game-music-emu/gme/Ym2612_Emu.h +++ b/game-music-emu/gme/Ym2612_Emu.h @@ -1,38 +1,19 @@ // YM2612 FM sound chip emulator interface -// Game_Music_Emu 0.6.0 -#ifndef YM2612_EMU_H -#define YM2612_EMU_H - -struct Ym2612_Impl; - -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 ); -}; +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#ifdef VGM_YM2612_GENS // LGPL v2.1+ license +#include "Ym2612_GENS.h" +typedef Ym2612_GENS_Emu Ym2612_Emu; #endif + +#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 --git a/game-music-emu/gme/Ym2612_Emu.cpp b/game-music-emu/gme/Ym2612_GENS.cpp similarity index 92% rename from game-music-emu/gme/Ym2612_Emu.cpp rename to game-music-emu/gme/Ym2612_GENS.cpp index 4f9d84271..d9930d62b 100644 --- a/game-music-emu/gme/Ym2612_Emu.cpp +++ b/game-music-emu/gme/Ym2612_GENS.cpp @@ -1,8 +1,8 @@ -// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ +// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ // Based on Gens 2.10 ym2612.c -#include "Ym2612_Emu.h" +#include "Ym2612_GENS.h" #include #include @@ -11,7 +11,7 @@ #include #include -/* Copyright (C) 2002 Stephane Dallongeville (gens AT consolemul.com) */ +/* 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 @@ -44,9 +44,9 @@ struct slot_t 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 oEl'enveloppe termine sa premiere phase de regression + 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 Ela frequence actuelle, elle va influer + 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; @@ -58,24 +58,24 @@ struct slot_t 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 aEu (ou haute) + // 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 Ejour l'enveloppe courante. - int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir oEl'on se trouve dans l'enveloppe + // 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 EAR[KSR] + // 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 EDR[KSR] + // 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 ESR[KSR] + // 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 ERR[KSR] - int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot El'entree - // d'un autre ou carrement Ela sortie de la voie + // 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 @@ -102,15 +102,15 @@ struct state_t { int TimerBase; // TimerBase calculation int Status; // YM2612 Status (timer overflow) - int TimerA; // timerA limit = valeur jusqu'Elaquelle le timer A doit compter + 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'Elaquelle le timer B doit compter + 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 + 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 }; @@ -203,9 +203,9 @@ struct tables_t 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) @@ -233,7 +233,7 @@ static const unsigned char DT_DEF_TAB [4 * 32] = }; static const unsigned char FKEY_TAB [16] = -{ +{ 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, @@ -255,38 +255,38 @@ static const unsigned char LFO_FMS_TAB [8] = inline void YM2612_Special_Update() { } -struct Ym2612_Impl +struct Ym2612_GENS_Impl { - enum { channel_count = Ym2612_Emu::channel_count }; - + 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_Emu::sample_t* ); + void run( int pair_count, Ym2612_GENS_Emu::sample_t* ); }; -void Ym2612_Impl::KEY_ON( channel_t& ch, int nsl) +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; @@ -300,10 +300,10 @@ void Ym2612_Impl::KEY_ON( channel_t& ch, int nsl) } -void Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl) +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 ? @@ -318,12 +318,12 @@ void Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl) } -int Ym2612_Impl::SLOT_SET( int Adr, int data ) +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]; @@ -397,7 +397,7 @@ int Ym2612_Impl::SLOT_SET( int Adr, int data ) // SSG-EG envelope shapes : /* E At Al H - + 1 0 0 0 \\\\ 1 0 0 1 \___ 1 0 1 0 \/\/ @@ -406,7 +406,7 @@ int Ym2612_Impl::SLOT_SET( int Adr, int data ) 1 1 0 1 / 1 1 1 0 /\/\ 1 1 1 1 /___ - + E = SSG-EG enable At = Start negate Al = Altern @@ -420,14 +420,14 @@ int Ym2612_Impl::SLOT_SET( int Adr, int data ) } -int Ym2612_Impl::CHANNEL_SET( int Adr, int data ) +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: @@ -487,7 +487,7 @@ int Ym2612_Impl::CHANNEL_SET( int Adr, int data ) YM2612_Special_Update(); ch.ALGO = data & 7; - + ch.SLOT [0].ChgEnM = 0; ch.SLOT [1].ChgEnM = 0; ch.SLOT [2].ChgEnM = 0; @@ -502,13 +502,13 @@ int Ym2612_Impl::CHANNEL_SET( int Adr, int data ) 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]; @@ -517,12 +517,12 @@ int Ym2612_Impl::CHANNEL_SET( int Adr, int data ) break; } } - + return 0; } -int Ym2612_Impl::YM_SET(int Adr, int data) +int Ym2612_GENS_Impl::YM_SET(int Adr, int data) { switch ( Adr ) { @@ -617,27 +617,27 @@ int Ym2612_Impl::YM_SET(int Adr, int data) 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 ) +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; @@ -662,9 +662,9 @@ void Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) g.TL_TAB [TL_LENGHT + i] = -g.TL_TAB [i]; } } - + // Tableau SIN : - // g.SIN_TAB [x] [y] = sin(x) * y; + // 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; @@ -720,11 +720,11 @@ void Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) } 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++ ) { @@ -735,7 +735,7 @@ void Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) } // 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) @@ -770,7 +770,7 @@ void Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) g.AR_TAB [i] = 0; g.DR_TAB [i] = 0; } - + for(i = 0; i < 60; i++) { double x = Frequence; @@ -790,10 +790,10 @@ void Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) 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++) @@ -810,7 +810,7 @@ void Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) 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); @@ -820,35 +820,35 @@ void Ym2612_Impl::set_rate( double sample_rate, double clock_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 ) +const char* Ym2612_GENS_Emu::set_rate( double sample_rate, double clock_rate ) { if ( !impl ) { - impl = (Ym2612_Impl*) malloc( sizeof *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_Emu::~Ym2612_Emu() +Ym2612_GENS_Emu::~Ym2612_GENS_Emu() { free( impl ); } -inline void Ym2612_Impl::write0( int opn_addr, int data ) +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; @@ -857,7 +857,7 @@ inline void Ym2612_Impl::write0( int opn_addr, int 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 @@ -865,10 +865,10 @@ inline void Ym2612_Impl::write0( int opn_addr, int data ) } } -inline void Ym2612_Impl::write1( int opn_addr, int 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; @@ -880,12 +880,12 @@ inline void Ym2612_Impl::write1( int opn_addr, int data ) } } -void Ym2612_Emu::reset() +void Ym2612_GENS_Emu::reset() { impl->reset(); } -void Ym2612_Impl::reset() +void Ym2612_GENS_Impl::reset() { g.LFOcnt = 0; YM2612.TimerA = 0; @@ -902,7 +902,7 @@ void Ym2612_Impl::reset() for ( i = 0; i < channel_count; i++ ) { channel_t& ch = YM2612.CHANNEL [i]; - + ch.LEFT = ~0; ch.RIGHT = ~0; ch.ALGO = 0; @@ -945,21 +945,21 @@ void Ym2612_Impl::reset() write0( i, 0 ); write1( i, 0 ); } - + write0( 0x2A, 0x80 ); } -void Ym2612_Emu::write0( int addr, int data ) +void Ym2612_GENS_Emu::write0( int addr, int data ) { impl->write0( addr, data ); } -void Ym2612_Emu::write1( int addr, int data ) +void Ym2612_GENS_Emu::write1( int addr, int data ) { impl->write1( addr, data ); } -void Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; } +void Ym2612_GENS_Emu::mute_voices( int mask ) { impl->mute_mask = mask; } static void update_envelope_( slot_t* sl ) { @@ -967,7 +967,7 @@ static void update_envelope_( slot_t* sl ) { case 0: // Env_Attack_Next - + // Verified with Gynoug even in HQ (explode SFX) sl->Ecnt = ENV_DECAY; @@ -975,10 +975,10 @@ static void update_envelope_( slot_t* sl ) 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; @@ -986,13 +986,13 @@ static void update_envelope_( slot_t* sl ) 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 @@ -1007,19 +1007,19 @@ static void update_envelope_( slot_t* sl ) } 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 } } @@ -1033,64 +1033,64 @@ inline void update_envelope( slot_t& sl ) template struct ym2612_update_chan { - static void func( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int ); + static void func( tables_t&, channel_t&, Ym2612_GENS_Emu::sample_t*, int ); }; -typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_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_Emu::sample_t* buf, int length ) + 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]; { @@ -1098,7 +1098,7 @@ void ym2612_update_chan::func( tables_t& g, channel_t& ch, 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 ) { @@ -1155,9 +1155,9 @@ void ym2612_update_chan::func( tables_t& g, channel_t& ch, 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)); @@ -1166,24 +1166,24 @@ void ym2612_update_chan::func( tables_t& g, channel_t& ch, 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; @@ -1201,7 +1201,7 @@ static const ym2612_update_chan_t UPDATE_CHAN [8] = { &ym2612_update_chan<7>::func }; -void Ym2612_Impl::run_timer( int length ) +void Ym2612_GENS_Impl::run_timer( int length ) { int const step = 6; int remain = length; @@ -1211,7 +1211,7 @@ void Ym2612_Impl::run_timer( int length ) if ( n > remain ) n = remain; remain -= n; - + long i = n * YM2612.TimerBase; if (YM2612.Mode & 1) // Timer A ON ? { @@ -1219,7 +1219,7 @@ void Ym2612_Impl::run_timer( int length ) if ((YM2612.TimerAcnt -= i) <= 0) { // timer a overflow - + YM2612.Status |= (YM2612.Mode & 0x04) >> 2; YM2612.TimerAcnt += YM2612.TimerAL; @@ -1247,37 +1247,37 @@ void Ym2612_Impl::run_timer( int length ) while ( remain > 0 ); } -void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t* out ) +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 Ejour des pas des compteurs-frequences s'ils ont ete modifies - + + // 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 Ejour + { // les differents taux pour l'enveloppe sont mis à jour sl.KSR = ksr; sl.EincA = sl.AR [ksr]; @@ -1301,19 +1301,19 @@ void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t* out ) 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 ); } +void Ym2612_GENS_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); } diff --git a/game-music-emu/gme/Ym2612_GENS.h b/game-music-emu/gme/Ym2612_GENS.h new file mode 100644 index 000000000..4cb2e8ae3 --- /dev/null +++ b/game-music-emu/gme/Ym2612_GENS.h @@ -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 --git a/game-music-emu/gme/Ym2612_MAME.cpp b/game-music-emu/gme/Ym2612_MAME.cpp new file mode 100644 index 000000000..524dab55a --- /dev/null +++ b/game-music-emu/gme/Ym2612_MAME.cpp @@ -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 0 +#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, 0); +} diff --git a/game-music-emu/gme/Ym2612_MAME.h b/game-music-emu/gme/Ym2612_MAME.h new file mode 100644 index 000000000..03831065a --- /dev/null +++ b/game-music-emu/gme/Ym2612_MAME.h @@ -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 --git a/game-music-emu/gme/Ym2612_Nuked.cpp b/game-music-emu/gme/Ym2612_Nuked.cpp new file mode 100644 index 000000000..fc49ac690 --- /dev/null +++ b/game-music-emu/gme/Ym2612_Nuked.cpp @@ -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_GenerateStream(chip_r, out, pair_count); +} diff --git a/game-music-emu/gme/Ym2612_Nuked.h b/game-music-emu/gme/Ym2612_Nuked.h new file mode 100644 index 000000000..6c265b138 --- /dev/null +++ b/game-music-emu/gme/Ym2612_Nuked.h @@ -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 --git a/game-music-emu/gme/blargg_common.h b/game-music-emu/gme/blargg_common.h index ed218a8da..13cc2417e 100644 --- a/game-music-emu/gme/blargg_common.h +++ b/game-music-emu/gme/blargg_common.h @@ -80,6 +80,9 @@ public: #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 @@ public: 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 --git a/game-music-emu/gme/blargg_endian.h b/game-music-emu/gme/blargg_endian.h index ba09e067e..46e58e2f0 100644 --- a/game-music-emu/gme/blargg_endian.h +++ b/game-music-emu/gme/blargg_endian.h @@ -123,15 +123,15 @@ inline void set_be32( void* p, blargg_ulong n ) #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 @@ inline void set_be32( void* p, blargg_ulong n ) // 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 --git a/game-music-emu/gme/blargg_source.h b/game-music-emu/gme/blargg_source.h index b011777ad..b65afd30b 100644 --- a/game-music-emu/gme/blargg_source.h +++ b/game-music-emu/gme/blargg_source.h @@ -18,6 +18,19 @@ all other #include lines. */ #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! +#undef likely +#undef unlikely +#ifdef __GNUC__ + #define likely( x ) __builtin_expect(x, 1) + #define unlikely( x ) __builtin_expect(x, 0) +#else + #define likely( x ) (x) + #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, ... ); diff --git a/game-music-emu/gme/gme.cpp b/game-music-emu/gme/gme.cpp index 5247c1f1a..292cbff4c 100644 --- a/game-music-emu/gme/gme.cpp +++ b/game-music-emu/gme/gme.cpp @@ -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" @@ -83,6 +83,8 @@ BLARGG_EXPORT const char* gme_identify_header( void const* header ) 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 ""; } @@ -111,6 +113,14 @@ BLARGG_EXPORT gme_type_t gme_identify_extension( const char* extension_ ) return 0; } +BLARGG_EXPORT 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 ""; +} + BLARGG_EXPORT gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ) { *type_out = gme_identify_extension( path ); @@ -187,7 +197,8 @@ BLARGG_EXPORT gme_err_t gme_open_file( const char* path, Music_Emu** out, int sa return err; } -BLARGG_EXPORT Music_Emu* gme_new_emu( gme_type_t type, int rate ) +// 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 ) { @@ -198,9 +209,18 @@ BLARGG_EXPORT Music_Emu* gme_new_emu( gme_type_t type, int rate ) 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 ); } @@ -220,6 +240,17 @@ BLARGG_EXPORT Music_Emu* gme_new_emu( gme_type_t type, int rate ) return 0; } +BLARGG_EXPORT Music_Emu* gme_new_emu( gme_type_t type, int rate ) +{ + return gme_internal_new_emu_( type, rate, false /* no multichannel */); +} + +BLARGG_EXPORT 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 */); +} + BLARGG_EXPORT 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 ) @@ -345,9 +376,10 @@ BLARGG_EXPORT void gme_ignore_silence ( Music_Emu* me, int disable ) 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_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; } +BLARGG_EXPORT 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 ) { diff --git a/game-music-emu/gme/gme.h b/game-music-emu/gme/gme.h index cb07061b4..80c6ce846 100644 --- a/game-music-emu/gme/gme.h +++ b/game-music-emu/gme/gme.h @@ -1,6 +1,6 @@ /* Game music emulator library C interface (also usable from C++) */ -/* Game_Music_Emu 0.6.1 */ +/* Game_Music_Emu 0.6.2 */ #ifndef GME_H #define GME_H @@ -8,7 +8,7 @@ extern "C" { #endif -#define GME_VERSION 0x000601 /* 1 byte major, 1 byte minor, 1 byte patch-level */ +#define GME_VERSION 0x000602 /* 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; @@ -187,13 +187,18 @@ const char* gme_type_system( gme_type_t ); /* True if this music file type supports multiple tracks */ 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 */ +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; -/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. */ +/* 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. */ 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 @@ -204,6 +209,14 @@ 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 [] ); +/** + * 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 + */ +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 ); @@ -212,6 +225,13 @@ gme_err_t gme_identify_file( const char path [], gme_type_t* type_out ); track information, pass gme_info_only for sample_rate. */ 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 + */ +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 [] ); diff --git a/game-music-emu/gme/gme_types.h b/game-music-emu/gme/gme_types.h old mode 100755 new mode 100644 index bd3670e64..06226f4aa --- a/game-music-emu/gme/gme_types.h +++ b/game-music-emu/gme/gme_types.h @@ -1,13 +1,11 @@ #ifndef GME_TYPES_H #define GME_TYPES_H -/* CMake will either define the following to 1, or #undef it, - * depending on the options passed to CMake. This is used to - * conditionally compile in the various emulator types. - * - * See gme_type_list() in gme.cpp +/* + * This is a default gme_types.h for use when *not* using + * CMake. If CMake is in use gme_types.h.in will be + * processed instead. */ - #define USE_GME_AY #define USE_GME_GBS #define USE_GME_GYM diff --git a/game-music-emu/gme/libgme.pc.in b/game-music-emu/gme/libgme.pc.in new file mode 100644 index 000000000..f057ce17c --- /dev/null +++ b/game-music-emu/gme/libgme.pc.in @@ -0,0 +1,16 @@ +# entries grouped with CMake are expanded by CMake +# ${foo} entries are left alone by CMake and much +# later are used by pkg-config. +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +lib_suffix=@LIB_SUFFIX@ +libdir=${exec_prefix}/lib${lib_suffix} +includedir=${prefix}/include + +Name: Game_Music_Emu +Description: A video game emulation library for music. +URL: https://bitbucket.org/mpyne/game-music-emu/wiki/Home +Version: @GME_VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -lgme +Libs.private: -lstdc++ @PKG_CONFIG_ZLIB@ diff --git a/game-music-emu/readme.txt b/game-music-emu/readme.txt index b29a00797..22cc20aad 100644 --- a/game-music-emu/readme.txt +++ b/game-music-emu/readme.txt @@ -1,4 +1,4 @@ -Game_Music_Emu 0.6.1: Game Music Emulators +Game_Music_Emu 0.6.2: Game Music Emulators ------------------------------------------ Game_Music_Emu is a collection of video game music file emulators that support the following formats and systems: @@ -34,10 +34,12 @@ several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable, GP2X, and Nintendo DS. Author : Shay Green -Website: http://www.slack.net/~ant/ -Forum : http://groups.google.com/group/blargg-sound-libs +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 @@ -191,8 +193,13 @@ gme/ 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 @@ -219,7 +226,7 @@ gme/ Multi_Buffer.cpp Data_Reader.h Data_Reader.cpp - + CMakeLists.txt CMake build rules @@ -227,6 +234,8 @@ Legal ----- 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