Merge remote-tracking branch 'origin/master' into asmjit

This commit is contained in:
Magnus Norddahl 2018-11-01 21:23:26 +01:00
commit 369dcfd57f
304 changed files with 14829 additions and 8919 deletions

View file

@ -93,16 +93,15 @@ matrix:
- os: linux - os: linux
compiler: clang compiler: clang
env: env:
- CLANG_VERSION=6.0 - CLANG_VERSION=7
- CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=MinSizeRel -DDYN_OPENAL=NO -DDYN_SNDFILE=NO -DDYN_MPG123=NO -DDYN_FLUIDSYNTH=NO" - CMAKE_OPTIONS="-DCMAKE_BUILD_TYPE=MinSizeRel -DDYN_OPENAL=NO -DDYN_SNDFILE=NO -DDYN_MPG123=NO -DDYN_FLUIDSYNTH=NO"
addons: addons:
apt: apt:
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-trusty-6.0 - llvm-toolchain-trusty-7
packages: packages:
- clang-6.0 - clang-7
- libstdc++-5-dev
- libsdl2-dev - libsdl2-dev
- libgme-dev - libgme-dev
- libopenal-dev - libopenal-dev

View file

@ -328,6 +328,9 @@ if( GME_FOUND AND NOT FORCE_INTERNAL_GME )
message( STATUS "Using system gme library, includes found at ${GME_INCLUDE_DIR}" ) message( STATUS "Using system gme library, includes found at ${GME_INCLUDE_DIR}" )
else() else()
message( STATUS "Using internal gme library" ) 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 ) add_subdirectory( game-music-emu )
set( GME_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/game-music-emu" ) set( GME_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/game-music-emu" )
set( GME_LIBRARIES gme ) set( GME_LIBRARIES gme )

View file

@ -4,14 +4,12 @@ project(libgme)
include (CheckCXXCompilerFlag) include (CheckCXXCompilerFlag)
# When version is changed, also change the one in gme/gme.h to match # 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. # 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 # Of course, 2.4 might work, in which case you're welcome to drop
# down the requirement, but I can't test that. # down the requirement, but I can't test that.
cmake_minimum_required(VERSION 2.8.7 FATAL_ERROR) cmake_minimum_required(VERSION 2.6 FATAL_ERROR)
make_release_only()
# I don't plan on debugging this, so make it a release build. # I don't plan on debugging this, so make it a release build.
if( NOT CMAKE_BUILD_TYPE MATCHES "Release" ) if( NOT CMAKE_BUILD_TYPE MATCHES "Release" )
@ -29,84 +27,112 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
endif() endif()
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 # Enable fast flag for GME
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ZD_FASTMATH_FLAG}" ) set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${ZD_FASTMATH_FLAG}" )
# Default emulators to build (all of them! ;) # Default emulators to build (all of them! ;)
# [ZDoom] No options, enable all of them by default. # [ZDoom] No options, enable all of them by default.
#if (NOT DEFINED USE_GME_AY) #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() #endif()
#if (NOT DEFINED USE_GME_GBS) #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() #endif()
#if (NOT DEFINED USE_GME_GYM) #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() #endif()
#if (NOT DEFINED USE_GME_HES) #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() #endif()
#if (NOT DEFINED USE_GME_KSS) #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() #endif()
#if (NOT DEFINED USE_GME_NSF) #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() #endif()
#if (NOT DEFINED USE_GME_NSFE) #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() #endif()
#if (NOT DEFINED USE_GME_SAP) #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() #endif()
#if (NOT DEFINED USE_GME_SPC) #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() #endif()
#if (NOT DEFINED USE_GME_VGM) #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() #endif()
#if (USE_GME_NSFE AND NOT USE_GME_NSF) #if (USE_GME_NSFE AND NOT USE_GME_NSF)
#MESSAGE(" -- NSFE support requires NSF, enabling NSF support. --") #MESSAGE(" -- NSFE support requires NSF, enabling NSF support. --")
SET(USE_GME_NSF 1 BOOL "Enable NES NSF music emulation") SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE)
#endif() #endif()
# [ZDoom] Set always to OFF. # [ZDoom] Set always to OFF.
set(BUILD_SHARED_LIBS OFF) set(BUILD_SHARED_LIBS OFF)
set(ENABLE_UBSAN OFF)
# Check for GCC "visibility" support. # Check for GCC/Clang "visibility" support.
if (CMAKE_COMPILER_IS_GNUCXX) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
check_cxx_compiler_flag (-fvisibility=hidden __LIBGME_TEST_VISIBILITY) OR
set (ENABLE_VISIBILITY OFF) CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if (__LIBGME_TEST_VISIBILITY)
# get the gcc version
exec_program(${CMAKE_CXX_COMPILER} ARGS --version OUTPUT_VARIABLE _gcc_version_info)
string (REGEX MATCH "[3-9]\\.[0-9]\\.[0-9]" _gcc_version "${_gcc_version_info}")
# gcc <4.1 had poor support for symbol visibility set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -W -Wextra")
if ((${_gcc_version} VERSION_GREATER "4.1") OR (${_gcc_version} VERSION_EQUAL "4.1")) set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
set (ENABLE_VISIBILITY ON) # Assume we have visibility support on any compiler that supports C++11
add_definitions (-DLIBGME_VISIBILITY) add_definitions (-DLIBGME_VISIBILITY)
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden")
# GCC >= 4.2 also correctly supports making inline members have hidden # Try to protect against undefined behavior from signed integer overflow
# visibility by default. # This has caused miscompilation of code already and there are other
if ((${_gcc_version} VERSION_GREATER "4.2") OR (${_gcc_version} VERSION_EQUAL "4.2")) # potential uses; see https://bitbucket.org/mpyne/game-music-emu/issues/18/
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility-inlines-hidden") set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fwrapv")
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
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()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-inconsistent-missing-override -Wno-unused-const-variable")
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()
endif () endif ()
endif() # test visibility
# Cache this result if(LIBGME_SWITCH_FALLTHROUGH)
set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility") # Avoid warning spam about switch fallthroughs, which are numerous in
# the codebase.
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wimplicit-fallthrough=0")
endif() endif()
# Shared library defined here # Shared library defined here

View file

@ -1,274 +1,5 @@
Game_Music_Emu Change Log Game_Music_Emu Change Log
------------------------- -------------------------
Game_Music_Emu 0.6.1 Please see the git version history (e.g. git shortlog tags/0.6.0..tags/0.6.1)
-------------------- for the accurate change log.
- 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

View file

@ -1,9 +1,8 @@
Game_Music_Emu 0.6.1 Game_Music_Emu 0.6.2
-------------------- --------------------
Author : Shay Green <gblargg@gmail.com> Author : Shay Green <gblargg@gmail.com>
Maintainer : Michael Pyne <mpyne@purinchu.net> Maintainer : Michael Pyne <mpyne@purinchu.net>
Website : http://www.slack.net/~ant/libs/ Website : https://bitbucket.org/mpyne/game-music-emu/
Forum : http://groups.google.com/group/blargg-sound-libs
Source : 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 License : GNU Lesser General Public License (LGPL), see LICENSE.txt

View file

@ -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" #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 while ( ntime <= end ) // must advance *past* time to avoid hang
{ {
int changed = noise_lfsr + 1; 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 ) if ( changed & 2 )
{ {
delta = -delta; delta = -delta;

View file

@ -1,6 +1,6 @@
// AY-3-8910 sound chip emulator // 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 #ifndef AY_APU_H
#define AY_APU_H #define AY_APU_H

View file

@ -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 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, 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 ) bool Ay_Cpu::run( cpu_time_t end_time )
{ {
set_end_time( end_time ); set_end_time( end_time );
@ -148,8 +143,6 @@ bool Ay_Cpu::run( cpu_time_t end_time )
this->state = &s; this->state = &s;
bool warning = false; bool warning = false;
typedef BOOST::int8_t int8_t;
union { union {
regs_t rg; regs_t rg;
pairs_t rp; pairs_t rp;
@ -160,10 +153,10 @@ bool Ay_Cpu::run( cpu_time_t end_time )
cpu_time_t s_time = s.time; cpu_time_t s_time = s.time;
uint8_t* const mem = this->mem; // cache uint8_t* const mem = this->mem; // cache
fuint16 pc = r.pc; uint16_t pc = r.pc;
fuint16 sp = r.sp; uint16_t sp = r.sp;
fuint16 ix = r.ix; // TODO: keep in memory for direct access? uint16_t ix = r.ix; // TODO: keep in memory for direct access?
fuint16 iy = r.iy; uint16_t iy = r.iy;
int flags = r.b.flags; int flags = r.b.flags;
goto loop; goto loop;
@ -182,7 +175,7 @@ loop:
check( (unsigned) ix < 0x10000 ); check( (unsigned) ix < 0x10000 );
check( (unsigned) iy < 0x10000 ); check( (unsigned) iy < 0x10000 );
fuint8 opcode; uint8_t opcode;
opcode = READ_PROG( pc ); opcode = READ_PROG( pc );
pc++; pc++;
@ -206,7 +199,7 @@ loop:
11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F 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]; data = base_timing [opcode];
if ( (s_time += data) >= 0 ) if ( (s_time += data) >= 0 )
goto possibly_out_of_time; goto possibly_out_of_time;
@ -262,7 +255,7 @@ possibly_out_of_time:
goto loop; goto loop;
case 0x3A:{// LD A,(addr) case 0x3A:{// LD A,(addr)
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
rg.a = READ( addr ); rg.a = READ( addr );
goto loop; goto loop;
@ -277,7 +270,7 @@ possibly_out_of_time:
// JR // JR
#define JR( cond ) {\ #define JR( cond ) {\
int disp = (BOOST::int8_t) data;\ int disp = (int8_t) data;\
pc++;\ pc++;\
if ( !(cond) )\ if ( !(cond) )\
goto jr_not_taken;\ goto jr_not_taken;\
@ -349,7 +342,7 @@ possibly_out_of_time:
case 0xCD:{// CALL addr case 0xCD:{// CALL addr
call_taken: call_taken:
fuint16 addr = pc + 2; uint16_t addr = pc + 2;
pc = GET_ADDR(); pc = GET_ADDR();
sp = uint16_t (sp - 2); sp = uint16_t (sp - 2);
WRITE_WORD( sp, addr ); WRITE_WORD( sp, addr );
@ -469,7 +462,7 @@ possibly_out_of_time:
add_hl_data: { add_hl_data: {
blargg_ulong sum = rp.hl + data; blargg_ulong sum = rp.hl + data;
data ^= rp.hl; data ^= rp.hl;
rp.hl = (uint16_t)sum; rp.hl = sum;
flags = (flags & (S80 | Z40 | V04)) | flags = (flags & (S80 | Z40 | V04)) |
(sum >> 16) | (sum >> 16) |
(sum >> 8 & (F20 | F08)) | (sum >> 8 & (F20 | F08)) |
@ -659,21 +652,21 @@ possibly_out_of_time:
goto loop; goto loop;
case 0x2A:{// LD HL,(addr) case 0x2A:{// LD HL,(addr)
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
rp.hl = READ_WORD( addr ); rp.hl = READ_WORD( addr );
goto loop; goto loop;
} }
case 0x32:{// LD (addr),A case 0x32:{// LD (addr),A
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
WRITE( addr, rg.a ); WRITE( addr, rg.a );
goto loop; goto loop;
} }
case 0x22:{// LD (addr),HL case 0x22:{// LD (addr),HL
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
WRITE_WORD( addr, rp.hl ); WRITE_WORD( addr, rp.hl );
goto loop; goto loop;
@ -696,7 +689,7 @@ possibly_out_of_time:
// Rotate // Rotate
case 0x07:{// RLCA case 0x07:{// RLCA
fuint16 temp = rg.a; uint16_t temp = rg.a;
temp = (temp << 1) | (temp >> 7); temp = (temp << 1) | (temp >> 7);
flags = (flags & (S80 | Z40 | P04)) | flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08 | C01)); (temp & (F20 | F08 | C01));
@ -705,7 +698,7 @@ possibly_out_of_time:
} }
case 0x0F:{// RRCA case 0x0F:{// RRCA
fuint16 temp = rg.a; uint16_t temp = rg.a;
flags = (flags & (S80 | Z40 | P04)) | flags = (flags & (S80 | Z40 | P04)) |
(temp & C01); (temp & C01);
temp = (temp << 7) | (temp >> 1); temp = (temp << 7) | (temp >> 1);
@ -719,12 +712,12 @@ possibly_out_of_time:
flags = (flags & (S80 | Z40 | P04)) | flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08)) | (temp & (F20 | F08)) |
(temp >> 8); (temp >> 8);
rg.a = (uint8_t)temp; rg.a = temp;
goto loop; goto loop;
} }
case 0x1F:{// RRA case 0x1F:{// RRA
fuint16 temp = (flags << 7) | (rg.a >> 1); uint16_t temp = (flags << 7) | (rg.a >> 1);
flags = (flags & (S80 | Z40 | P04)) | flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08)) | (temp & (F20 | F08)) |
(rg.a & C01); (rg.a & C01);
@ -734,7 +727,7 @@ possibly_out_of_time:
// Misc // Misc
case 0x2F:{// CPL case 0x2F:{// CPL
fuint16 temp = ~rg.a; uint16_t temp = ~rg.a;
flags = (flags & (S80 | Z40 | P04 | C01)) | flags = (flags & (S80 | Z40 | P04 | C01)) |
(temp & (F20 | F08)) | (temp & (F20 | F08)) |
(H10 | N02); (H10 | N02);
@ -760,21 +753,21 @@ possibly_out_of_time:
goto loop; goto loop;
case 0xE3:{// EX (SP),HL case 0xE3:{// EX (SP),HL
fuint16 temp = READ_WORD( sp ); uint16_t temp = READ_WORD( sp );
WRITE_WORD( sp, rp.hl ); WRITE_WORD( sp, rp.hl );
rp.hl = temp; rp.hl = temp;
goto loop; goto loop;
} }
case 0xEB:{// EX DE,HL case 0xEB:{// EX DE,HL
fuint16 temp = rp.hl; uint16_t temp = rp.hl;
rp.hl = rp.de; rp.hl = rp.de;
rp.de = temp; rp.de = temp;
goto loop; goto loop;
} }
case 0xD9:{// EXX DE,HL case 0xD9:{// EXX DE,HL
fuint16 temp = r.alt.w.bc; uint16_t temp = r.alt.w.bc;
r.alt.w.bc = rp.bc; r.alt.w.bc = rp.bc;
rp.bc = temp; rp.bc = temp;
@ -815,7 +808,7 @@ possibly_out_of_time:
// Rotate left // Rotate left
#define RLC( read, write ) {\ #define RLC( read, write ) {\
fuint8 result = read;\ uint8_t result = read;\
result = uint8_t (result << 1) | (result >> 7);\ result = uint8_t (result << 1) | (result >> 7);\
flags = SZ28P( result ) | (result & C01);\ flags = SZ28P( result ) | (result & C01);\
write;\ write;\
@ -834,7 +827,7 @@ possibly_out_of_time:
} }
#define RL( read, write ) {\ #define RL( read, write ) {\
fuint16 result = (read << 1) | (flags & C01);\ uint16_t result = (read << 1) | (flags & C01);\
flags = SZ28PC( result );\ flags = SZ28PC( result );\
write;\ write;\
goto loop;\ goto loop;\
@ -852,7 +845,7 @@ possibly_out_of_time:
} }
#define SLA( read, add, write ) {\ #define SLA( read, add, write ) {\
fuint16 result = (read << 1) | add;\ uint16_t result = (read << 1) | add;\
flags = SZ28PC( result );\ flags = SZ28PC( result );\
write;\ write;\
goto loop;\ goto loop;\
@ -883,7 +876,7 @@ possibly_out_of_time:
// Rotate right // Rotate right
#define RRC( read, write ) {\ #define RRC( read, write ) {\
fuint8 result = read;\ uint8_t result = read;\
flags = result & C01;\ flags = result & C01;\
result = uint8_t (result << 7) | (result >> 1);\ result = uint8_t (result << 7) | (result >> 1);\
flags |= SZ28P( result );\ flags |= SZ28P( result );\
@ -903,8 +896,8 @@ possibly_out_of_time:
} }
#define RR( read, write ) {\ #define RR( read, write ) {\
fuint8 result = read;\ uint8_t result = read;\
fuint8 temp = result & C01;\ uint8_t temp = result & C01;\
result = uint8_t (flags << 7) | (result >> 1);\ result = uint8_t (flags << 7) | (result >> 1);\
flags = SZ28P( result ) | temp;\ flags = SZ28P( result ) | temp;\
write;\ write;\
@ -923,7 +916,7 @@ possibly_out_of_time:
} }
#define SRA( read, write ) {\ #define SRA( read, write ) {\
fuint8 result = read;\ uint8_t result = read;\
flags = result & C01;\ flags = result & C01;\
result = (result & 0x80) | (result >> 1);\ result = (result & 0x80) | (result >> 1);\
flags |= SZ28P( result );\ flags |= SZ28P( result );\
@ -943,7 +936,7 @@ possibly_out_of_time:
} }
#define SRL( read, write ) {\ #define SRL( read, write ) {\
fuint8 result = read;\ uint8_t result = read;\
flags = result & C01;\ flags = result & C01;\
result >>= 1;\ result >>= 1;\
flags |= SZ28P( result );\ flags |= SZ28P( result );\
@ -1048,7 +1041,7 @@ possibly_out_of_time:
blargg_ulong sum = temp + (flags & C01); blargg_ulong sum = temp + (flags & C01);
flags = ~data >> 2 & N02; flags = ~data >> 2 & N02;
if ( flags ) if ( flags )
sum = (blargg_ulong)-(blargg_long)sum; sum = -sum;
sum += rp.hl; sum += rp.hl;
temp ^= rp.hl; temp ^= rp.hl;
temp ^= sum; temp ^= sum;
@ -1056,7 +1049,7 @@ possibly_out_of_time:
(temp >> 8 & H10) | (temp >> 8 & H10) |
(sum >> 8 & (S80 | F20 | F08)) | (sum >> 8 & (S80 | F20 | F08)) |
((temp - -0x8000) >> 14 & V04); ((temp - -0x8000) >> 14 & V04);
rp.hl = (uint16_t)sum; rp.hl = sum;
if ( (uint16_t) sum ) if ( (uint16_t) sum )
goto loop; goto loop;
flags |= Z40; flags |= Z40;
@ -1084,7 +1077,7 @@ possibly_out_of_time:
case 0x43: // LD (ADDR),BC case 0x43: // LD (ADDR),BC
case 0x53: // LD (ADDR),DE case 0x53: // LD (ADDR),DE
temp = R16( data, 4, 0x43 ); temp = R16( data, 4, 0x43 );
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
WRITE_WORD( addr, temp ); WRITE_WORD( addr, temp );
goto loop; goto loop;
@ -1092,21 +1085,21 @@ possibly_out_of_time:
case 0x4B: // LD BC,(ADDR) case 0x4B: // LD BC,(ADDR)
case 0x5B:{// LD DE,(ADDR) case 0x5B:{// LD DE,(ADDR)
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
R16( data, 4, 0x4B ) = READ_WORD( addr ); R16( data, 4, 0x4B ) = READ_WORD( addr );
goto loop; goto loop;
} }
case 0x7B:{// LD SP,(ADDR) case 0x7B:{// LD SP,(ADDR)
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
sp = READ_WORD( addr ); sp = READ_WORD( addr );
goto loop; goto loop;
} }
case 0x67:{// RRD case 0x67:{// RRD
fuint8 temp = READ( rp.hl ); uint8_t temp = READ( rp.hl );
WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); WRITE( rp.hl, (rg.a << 4) | (temp >> 4) );
temp = (rg.a & 0xF0) | (temp & 0x0F); temp = (rg.a & 0xF0) | (temp & 0x0F);
flags = (flags & C01) | SZ28P( temp ); flags = (flags & C01) | SZ28P( temp );
@ -1115,7 +1108,7 @@ possibly_out_of_time:
} }
case 0x6F:{// RLD case 0x6F:{// RLD
fuint8 temp = READ( rp.hl ); uint8_t temp = READ( rp.hl );
WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) );
temp = (rg.a & 0xF0) | (temp >> 4); temp = (rg.a & 0xF0) | (temp >> 4);
flags = (flags & C01) | SZ28P( temp ); flags = (flags & C01) | SZ28P( temp );
@ -1139,7 +1132,7 @@ possibly_out_of_time:
case 0xA1: // CPI case 0xA1: // CPI
case 0xB1: // CPIR case 0xB1: // CPIR
inc = +1; inc = +1;
fuint16 addr = rp.hl; uint16_t addr = rp.hl;
rp.hl = addr + inc; rp.hl = addr + inc;
int temp = READ( addr ); int temp = READ( addr );
@ -1172,7 +1165,7 @@ possibly_out_of_time:
case 0xA0: // LDI case 0xA0: // LDI
case 0xB0: // LDIR case 0xB0: // LDIR
inc = +1; inc = +1;
fuint16 addr = rp.hl; uint16_t addr = rp.hl;
rp.hl = addr + inc; rp.hl = addr + inc;
int temp = READ( addr ); int temp = READ( addr );
@ -1204,7 +1197,7 @@ possibly_out_of_time:
case 0xA3: // OUTI case 0xA3: // OUTI
case 0xB3: // OTIR case 0xB3: // OTIR
inc = +1; inc = +1;
fuint16 addr = rp.hl; uint16_t addr = rp.hl;
rp.hl = addr + inc; rp.hl = addr + inc;
int temp = READ( addr ); int temp = READ( addr );
@ -1230,7 +1223,7 @@ possibly_out_of_time:
case 0xB2: // INIR case 0xB2: // INIR
inc = +1; inc = +1;
fuint16 addr = rp.hl; uint16_t addr = rp.hl;
rp.hl = addr + inc; rp.hl = addr + inc;
int temp = IN( rp.bc ); int temp = IN( rp.bc );
@ -1295,7 +1288,7 @@ possibly_out_of_time:
//////////////////////////////////////// DD/FD prefix //////////////////////////////////////// DD/FD prefix
{ {
fuint16 ixy; uint16_t ixy;
case 0xDD: case 0xDD:
ixy = ix; ixy = ix;
goto ix_prefix; goto ix_prefix;
@ -1490,7 +1483,7 @@ possibly_out_of_time:
goto loop; goto loop;
case 0x22:{// LD (ADDR),IXY case 0x22:{// LD (ADDR),IXY
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
WRITE_WORD( addr, ixy ); WRITE_WORD( addr, ixy );
goto loop; goto loop;
@ -1502,7 +1495,7 @@ possibly_out_of_time:
goto set_ixy; goto set_ixy;
case 0x2A:{// LD IXY,(addr) case 0x2A:{// LD IXY,(addr)
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
ixy = READ_WORD( addr ); ixy = READ_WORD( addr );
pc += 2; pc += 2;
goto set_ixy; goto set_ixy;
@ -1526,7 +1519,7 @@ possibly_out_of_time:
case 0x3E: goto srl_data_addr; // SRL (IXY) case 0x3E: goto srl_data_addr; // SRL (IXY)
CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) 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); int masked = temp & 1 << (data2 >> 3 & 7);
flags = (flags & C01) | H10 | flags = (flags & C01) | H10 |
(masked & S80) | (masked & S80) |
@ -1628,7 +1621,7 @@ possibly_out_of_time:
goto loop; goto loop;
case 0xE3:{// EX (SP),IXY case 0xE3:{// EX (SP),IXY
fuint16 temp = READ_WORD( sp ); uint16_t temp = READ_WORD( sp );
WRITE_WORD( sp, ixy ); WRITE_WORD( sp, ixy );
ixy = temp; ixy = temp;
goto set_ixy; goto set_ixy;

View file

@ -1,6 +1,6 @@
// Z80 CPU emulator // Z80 CPU emulator
// Game_Music_Emu 0.6.0 // Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
#ifndef AY_CPU_H #ifndef AY_CPU_H
#define 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 set_time( cpu_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; } void adjust_time( int delta ) { state->time += delta; }
typedef BOOST::uint8_t uint8_t;
typedef BOOST::uint16_t uint16_t;
#if BLARGG_BIG_ENDIAN #if BLARGG_BIG_ENDIAN
struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; struct regs_t { uint8_t b, c, d, e, h, l, flags, a; };
#else #else

View file

@ -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" #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 ) 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 pos = ptr - (byte const*) file.header;
long file_size = long(file.end - (byte const*) file.header); long file_size = file.end - (byte const*) file.header;
assert( (unsigned long) pos <= (unsigned long) file_size - 2 ); 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) ) if ( !offset || blargg_ulong (pos + offset) > blargg_ulong (file_size - min_size) )
return 0; return 0;
return ptr + offset; 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 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 }; 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 // Setup
@ -207,7 +207,7 @@ blargg_err_t Ay_Emu::start_track_( int track )
if ( len > blargg_ulong (file.end - in) ) if ( len > blargg_ulong (file.end - in) )
{ {
set_warning( "Missing file data" ); set_warning( "Missing file data" );
len = unsigned(file.end - in); len = file.end - in;
} }
//debug_printf( "addr: $%04X, len: $%04X\n", addr, len ); //debug_printf( "addr: $%04X, len: $%04X\n", addr, len );
if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data

View file

@ -1,6 +1,6 @@
// Sinclair Spectrum AY music file emulator // 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 #ifndef AY_EMU_H
#define AY_EMU_H #define AY_EMU_H

View file

@ -95,6 +95,8 @@ public:
Blip_Buffer(); Blip_Buffer();
~Blip_Buffer(); ~Blip_Buffer();
Blip_Buffer(Blip_Buffer &&) = default;
// Deprecated // Deprecated
typedef blip_resampled_time_t resampled_time_t; typedef blip_resampled_time_t resampled_time_t;
blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); }

View file

@ -14,6 +14,15 @@ set(libgme_SRCS Blip_Buffer.cpp
Music_Emu.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 # Ay_Apu is very popular around here
if (USE_GME_AY OR USE_GME_KSS) if (USE_GME_AY OR USE_GME_KSS)
set(libgme_SRCS ${libgme_SRCS} set(libgme_SRCS ${libgme_SRCS}
@ -23,9 +32,25 @@ endif()
# so is Ym2612_Emu # so is Ym2612_Emu
if (USE_GME_VGM OR USE_GME_GYM) if (USE_GME_VGM OR USE_GME_GYM)
if(GME_YM2612_EMU STREQUAL "Nuked")
add_definitions(-DVGM_YM2612_NUKED)
set(libgme_SRCS ${libgme_SRCS} set(libgme_SRCS ${libgme_SRCS}
Ym2612_Emu.cpp 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() endif()
# But none are as popular as Sms_Apu # But none are as popular as Sms_Apu
@ -127,19 +152,11 @@ endif()
# These headers are part of the generic gme interface. # These headers are part of the generic gme interface.
set (EXPORTED_HEADERS gme.h) 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 # 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 # 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. # only defined when building the library to allow us to tell which is which.
#[ZDoom] Not needed
#add_definitions(-DBLARGG_BUILD_DLL) #add_definitions(-DBLARGG_BUILD_DLL)
# For the gme_types.h # For the gme_types.h
@ -148,13 +165,25 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
# Add library to be compiled. # Add library to be compiled.
add_library(gme ${libgme_SRCS}) 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 # 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 # 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. # 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. # The way gme.h is designed, SOVERSION should very rarely be bumped, if ever.
# Hopefully the API can stay compatible with old versions. # Hopefully the API can stay compatible with old versions.
# [ZDoom] Not needed.
if( FALSE )
set_target_properties(gme set_target_properties(gme
PROPERTIES VERSION ${GME_VERSION} PROPERTIES VERSION ${GME_VERSION}
SOVERSION 0) SOVERSION 0)
@ -163,8 +192,13 @@ install(TARGETS gme LIBRARY DESTINATION lib${LIB_SUFFIX}
RUNTIME DESTINATION bin # DLL platforms RUNTIME DESTINATION bin # DLL platforms
ARCHIVE DESTINATION lib) # 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 ${EXPORTED_HEADERS} DESTINATION include/gme)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig)
endif() endif()
target_link_libraries(gme)

View file

@ -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" #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 ); 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 ) void Classic_Emu::mute_voices_( int mask )
{ {
Music_Emu::mute_voices_( mask ); Music_Emu::mute_voices_( mask );

View file

@ -1,6 +1,6 @@
// Common aspects of emulators which use Blip_Buffer for sound output // 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 #ifndef CLASSIC_EMU_H
#define CLASSIC_EMU_H #define CLASSIC_EMU_H
@ -13,6 +13,7 @@ public:
Classic_Emu(); Classic_Emu();
~Classic_Emu(); ~Classic_Emu();
void set_buffer( Multi_Buffer* ); void set_buffer( Multi_Buffer* );
blargg_err_t set_multi_channel( bool is_enabled ) override;
protected: protected:
// Services // Services
enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };

View file

@ -1,7 +1,5 @@
// File_Extractor 0.4.0. http://www.slack.net/~ant/ // File_Extractor 0.4.0. http://www.slack.net/~ant/
#define _CRT_SECURE_NO_WARNINGS
#include "Data_Reader.h" #include "Data_Reader.h"
#include "blargg_endian.h" #include "blargg_endian.h"
@ -22,10 +20,22 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h" #include "blargg_source.h"
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#include <stdlib.h>
#include <errno.h>
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"; 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 ) blargg_err_t Data_Reader::read( void* p, long s )
{ {
RETURN_VALIDITY_CHECK( s > 0 );
long result = read_avail( p, s ); long result = read_avail( p, s );
if ( result != s ) if ( result != s )
{ {
@ -40,6 +50,8 @@ blargg_err_t Data_Reader::read( void* p, long s )
blargg_err_t Data_Reader::skip( long count ) blargg_err_t Data_Reader::skip( long count )
{ {
RETURN_VALIDITY_CHECK( count >= 0 );
char buf [512]; char buf [512];
while ( count ) while ( count )
{ {
@ -56,7 +68,8 @@ long File_Reader::remain() const { return size() - tell(); }
blargg_err_t File_Reader::skip( long n ) blargg_err_t File_Reader::skip( long n )
{ {
assert( n >= 0 ); RETURN_VALIDITY_CHECK( n >= 0 );
if ( !n ) if ( !n )
return 0; return 0;
return seek( tell() + n ); return seek( tell() + n );
@ -69,13 +82,14 @@ Subset_Reader::Subset_Reader( Data_Reader* dr, long size )
in = dr; in = dr;
remain_ = dr->remain(); remain_ = dr->remain();
if ( remain_ > size ) if ( remain_ > size )
remain_ = size; remain_ = max( 0l, size );
} }
long Subset_Reader::remain() const { return remain_; } long Subset_Reader::remain() const { return remain_; }
long Subset_Reader::read_avail( void* p, long s ) long Subset_Reader::read_avail( void* p, long s )
{ {
s = max( 0l, s );
if ( s > remain_ ) if ( s > remain_ )
s = remain_; s = remain_;
remain_ -= s; 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 ) Remaining_Reader::Remaining_Reader( void const* h, long size, Data_Reader* r )
{ {
header = (char const*) h; header = (char const*) h;
header_end = header + size; header_end = header + max( 0l, size );
in = r; 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 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 )
{ {
if ( first > count ) if ( first > count || first < 0 )
first = count; first = count;
void const* old = header; void const* old = header;
header += first; header += first;
memcpy( out, old, first ); memcpy( out, old, (size_t) first );
} }
return first; return first;
} }
long Remaining_Reader::read_avail( void* out, long count ) long Remaining_Reader::read_avail( void* out, long count )
{ {
count = max( 0l, count );
long first = read_first( out, count ); long first = read_first( out, count );
long second = count - first; long second = max( 0l, count - first );
if ( second ) if ( second )
{ {
second = in->read_avail( (char*) out + first, 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 ) blargg_err_t Remaining_Reader::read( void* out, long count )
{ {
count = max( 0l, count );
long first = read_first( out, count ); long first = read_first( out, count );
long second = count - first; long second = max( 0l, count - first );
if ( !second ) if ( !second )
return 0; return 0;
return in->read( (char*) out + first, second ); 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::Mem_File_Reader( const void* p, long s ) : Mem_File_Reader::Mem_File_Reader( const void* p, long s ) :
begin( (const char*) p ), m_begin( (const char*) p ),
size_( s ) 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<char*>( 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 Mem_File_Reader::read_avail( void* p, long s )
{ {
long r = remain(); long r = remain();
if ( s > r ) if ( s > r || s < 0 )
s = r; s = r;
memcpy( p, begin + pos, s ); memcpy( p, m_begin + m_pos, static_cast<size_t>(s) );
pos += s; m_pos += s;
return 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 ) blargg_err_t Mem_File_Reader::seek( long n )
{ {
if ( n > size_ ) RETURN_VALIDITY_CHECK( n >= 0 );
if ( n > m_size )
return eof_error; return eof_error;
pos = n; m_pos = n;
return 0; 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<vec_size>( m_size );
const vec_size half_length = static_cast<vec_size>( m_size / 2 );
// We use malloc/friends here so we can realloc to grow buffer if needed
char *raw_data = reinterpret_cast<char *> ( malloc( full_length ) );
size_t raw_data_size = full_length;
if ( !raw_data )
return false;
z_stream strm;
strm.next_in = const_cast<Bytef *>( reinterpret_cast<const Bytef *>( m_begin ) );
strm.avail_in = static_cast<uInt>( 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<char *>( realloc( raw_data, raw_data_size ) );
if ( !raw_data ) {
return false;
}
}
strm.next_out = reinterpret_cast<Bytef *>( raw_data + strm.total_out );
strm.avail_out = static_cast<uInt>( static_cast<uLong>( 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<long>( strm.total_out );
return true;
}
#endif /* HAVE_ZLIB_H */
// Callback_Reader // Callback_Reader
Callback_Reader::Callback_Reader( callback_t c, long size, void* d ) : Callback_Reader::Callback_Reader( callback_t c, long size, void* d ) :
callback( c ), callback( c ),
data( d ) data( d )
{ {
remain_ = size; remain_ = max( 0l, size );
} }
long Callback_Reader::remain() const { return remain_; } long Callback_Reader::remain() const { return remain_; }
@ -175,34 +286,82 @@ long Callback_Reader::read_avail( void* out, long count )
{ {
if ( count > remain_ ) if ( count > remain_ )
count = remain_; count = remain_;
if ( Callback_Reader::read( out, count ) ) if ( count < 0 || Callback_Reader::read( out, count ) )
count = -1; count = -1;
return count; return count;
} }
blargg_err_t Callback_Reader::read( void* out, long count ) blargg_err_t Callback_Reader::read( void* out, long count )
{ {
RETURN_VALIDITY_CHECK( count >= 0 );
if ( count > remain_ ) if ( count > remain_ )
return eof_error; return eof_error;
return callback( data, out, count ); return callback( data, out, (int) count );
} }
// Std_File_Reader // 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(); } Std_File_Reader::~Std_File_Reader() { close(); }
blargg_err_t Std_File_Reader::open( const char* path ) 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" ); file_ = fopen( path, "rb" );
#endif
if ( !file_ ) if ( !file_ )
return "Couldn't open file"; return "Couldn't open file";
return 0; return nullptr;
} }
long Std_File_Reader::size() const 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(); long pos = tell();
fseek( (FILE*) file_, 0, SEEK_END ); fseek( (FILE*) file_, 0, SEEK_END );
long result = tell(); long result = tell();
@ -212,24 +371,64 @@ long Std_File_Reader::size() const
long Std_File_Reader::read_avail( void* p, long s ) 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<gzFile>(file_),
p, static_cast<unsigned>(s) );
}
return 0l;
#else
const size_t readLength = static_cast<size_t>( max( 0l, s ) );
const auto result = fread( p, 1, readLength, reinterpret_cast<FILE*>(file_) );
return static_cast<long>( result );
#endif /* HAVE_ZLIB_H */
} }
blargg_err_t Std_File_Reader::read( void* p, long s ) 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<gzFile>( file_ );
if ( s == gzread( gzfile, p, static_cast<unsigned>( s ) ) )
return nullptr;
if ( gzeof( gzfile ) )
return eof_error;
return "Couldn't read from GZ file";
}
#endif
const auto &file = reinterpret_cast<FILE*>( file_ );
if ( s == static_cast<long>( fread( p, 1, static_cast<size_t>(s), file ) ) )
return 0; return 0;
if ( feof( (FILE*) file_ ) ) if ( feof( file ) )
return eof_error; return eof_error;
return "Couldn't read from file"; 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<gzFile>( file_ ) );
#endif
return ftell( reinterpret_cast<FILE*>( file_ ) );
}
blargg_err_t Std_File_Reader::seek( long n ) blargg_err_t Std_File_Reader::seek( long n )
{ {
if ( !fseek( (FILE*) file_, n, SEEK_SET ) ) #if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H
return 0; if ( file_ )
{
if ( gzseek( reinterpret_cast<gzFile>( 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*>( file_ ), n, SEEK_SET ) )
return nullptr;
if ( n > size() ) if ( n > size() )
return eof_error; return eof_error;
return "Error seeking in file"; return "Error seeking in file";
@ -239,79 +438,12 @@ void Std_File_Reader::close()
{ {
if ( file_ ) if ( file_ )
{ {
fclose( (FILE*) file_ ); #if 0//[ZDOOM:unneeded]def HAVE_ZLIB_H
file_ = 0; gzclose( reinterpret_cast<gzFile>( file_ ) );
} #else
} fclose( reinterpret_cast<FILE*>( file_ ) );
// 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;
}
}
#endif #endif
file_ = nullptr;
}
}

View file

@ -6,6 +6,10 @@
#include "blargg_common.h" #include "blargg_common.h"
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#endif
// Supports reading and finding out how many bytes are remaining // Supports reading and finding out how many bytes are remaining
class Data_Reader { class Data_Reader {
public: public:
@ -65,13 +69,19 @@ public:
long tell() const; long tell() const;
blargg_err_t seek( long ); blargg_err_t seek( long );
private: 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 // Treats range of memory as a file
class Mem_File_Reader : public File_Reader { class Mem_File_Reader : public File_Reader {
public: public:
Mem_File_Reader( const void*, long size ); Mem_File_Reader( const void*, long size );
#ifdef HAVE_ZLIB_H
~Mem_File_Reader( );
#endif /* HAVE_ZLIB_H */
public: public:
long size() const; long size() const;
@ -79,11 +89,19 @@ public:
long tell() const; long tell() const;
blargg_err_t seek( long ); blargg_err_t seek( long );
private: private:
const char* const begin; #ifdef HAVE_ZLIB_H
const long size_; bool gz_decompress();
long pos; #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 // Makes it look like there are only count bytes remaining
class Subset_Reader : public Data_Reader { class Subset_Reader : public Data_Reader {
public: public:
@ -128,26 +146,4 @@ private:
long remain_; long remain_;
}; };
#ifdef HAVE_ZLIB_H
#include <zlib.h>
// 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 #endif

View file

@ -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" #include "Dual_Resampler.h"
@ -18,7 +18,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h" #include "blargg_source.h"
//unsigned const resampler_extra = 256; unsigned const resampler_extra = 256;
Dual_Resampler::Dual_Resampler() : Dual_Resampler::Dual_Resampler() :
sample_buf_size(0), sample_buf_size(0),
@ -69,9 +69,12 @@ void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, dsample_t* out )
resampler.write( new_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 ); long count = resampler.read( sample_buf.begin(), sample_buf_size );
assert( count == (long) sample_buf_size ); assert( count == (long) sample_buf_size );
(void)count; // Silence warning in non-debug build #endif
mix_samples( blip_buf, out ); mix_samples( blip_buf, out );
blip_buf.remove_samples( pair_count ); 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(); int s = sn.read();
blargg_long l = (blargg_long) in [0] * 2 + s; blargg_long l = (blargg_long) in [0] * 2 + s;
if ( (BOOST::int16_t) l != l ) if ( (int16_t) l != l )
l = 0x7FFF - (l >> 24); l = 0x7FFF - (l >> 24);
sn.next( bass ); sn.next( bass );
blargg_long r = (blargg_long) in [1] * 2 + s; blargg_long r = (blargg_long) in [1] * 2 + s;
if ( (BOOST::int16_t) r != r ) if ( (int16_t) r != r )
r = 0x7FFF - (r >> 24); r = 0x7FFF - (r >> 24);
in += 2; in += 2;
out [0] = (dsample_t)l; out [0] = l;
out [1] = (dsample_t)r; out [1] = r;
out += 2; out += 2;
} }

View file

@ -1,6 +1,6 @@
// Combination of Fir_Resampler and Blip_Buffer mixing. Used by Sega FM emulators. // 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 #ifndef DUAL_RESAMPLER_H
#define DUAL_RESAMPLER_H #define DUAL_RESAMPLER_H

View file

@ -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" #include "Effects_Buffer.h"
@ -63,28 +63,48 @@ void Effects_Buffer::set_depth( double d )
config( c ); 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<blip_sample_t>(reverb_size))
, echo_buf(max_voices, std::vector<blip_sample_t>(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 ); set_depth( 0 );
} }
Effects_Buffer::~Effects_Buffer() { } Effects_Buffer::~Effects_Buffer()
{}
blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec )
{ {
if ( !echo_buf.size() ) try
RETURN_ERR( echo_buf.resize( echo_size ) ); {
for(int i=0; i<max_voices; i++)
{
if ( !echo_buf[i].size() )
{
echo_buf[i].resize( echo_size );
}
if ( !reverb_buf.size() ) if ( !reverb_buf[i].size() )
RETURN_ERR( reverb_buf.resize( reverb_size ) ); {
reverb_buf[i].resize( reverb_size );
}
}
}
catch(std::bad_alloc& ba)
{
return "Out of memory";
}
for ( int i = 0; i < buf_count; i++ ) for ( int i = 0; i < buf_count; i++ )
RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) );
@ -111,11 +131,15 @@ void Effects_Buffer::clear()
{ {
stereo_remain = 0; stereo_remain = 0;
effect_remain = 0; effect_remain = 0;
if ( echo_buf.size() )
memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] );
if ( reverb_buf.size() ) for(int i=0; i<max_voices; i++)
memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); {
if ( echo_buf[i].size() )
memset( &echo_buf[i][0], 0, echo_size * sizeof echo_buf[i][0] );
if ( reverb_buf[i].size() )
memset( &reverb_buf[i][0], 0, reverb_size * sizeof reverb_buf[i][0] );
}
for ( int i = 0; i < buf_count; i++ ) for ( int i = 0; i < buf_count; i++ )
bufs [i].clear(); bufs [i].clear();
@ -135,10 +159,15 @@ void Effects_Buffer::config( const config_t& cfg )
channels_changed(); channels_changed();
// clear echo and reverb buffers // clear echo and reverb buffers
if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf.size() ) // ensure the echo/reverb buffers have already been allocated, so this method can be
// called before set_sample_rate is called
if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf[0].size() )
{ {
memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] ); for(int i=0; i<max_voices; i++)
memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); {
memset( &echo_buf[i][0], 0, echo_size * sizeof echo_buf[i][0] );
memset( &reverb_buf[i][0], 0, reverb_size * sizeof reverb_buf[i][0] );
}
} }
config_ = cfg; config_ = cfg;
@ -170,73 +199,87 @@ void Effects_Buffer::config( const config_t& cfg )
chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset), chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset),
echo_size - 1 ); echo_size - 1 );
chan_types [0].center = &bufs [0]; for(int i=0; i<max_voices; i++)
chan_types [0].left = &bufs [3]; {
chan_types [0].right = &bufs [4]; chan_types [i*chan_types_count+0].center = &bufs [i*max_buf_count+0];
chan_types [i*chan_types_count+0].left = &bufs [i*max_buf_count+3];
chan_types [i*chan_types_count+0].right = &bufs [i*max_buf_count+4];
chan_types [1].center = &bufs [1]; chan_types [i*chan_types_count+1].center = &bufs [i*max_buf_count+1];
chan_types [1].left = &bufs [3]; chan_types [i*chan_types_count+1].left = &bufs [i*max_buf_count+3];
chan_types [1].right = &bufs [4]; chan_types [i*chan_types_count+1].right = &bufs [i*max_buf_count+4];
chan_types [2].center = &bufs [2]; chan_types [i*chan_types_count+2].center = &bufs [i*max_buf_count+2];
chan_types [2].left = &bufs [5]; chan_types [i*chan_types_count+2].left = &bufs [i*max_buf_count+5];
chan_types [2].right = &bufs [6]; chan_types [i*chan_types_count+2].right = &bufs [i*max_buf_count+6];
}
assert( 2 < chan_types_count ); assert( 2 < chan_types_count );
} }
else else
{ {
// set up outputs for(int i=0; i<max_voices; i++)
for ( unsigned i = 0; i < chan_types_count; i++ )
{ {
channel_t& c = chan_types [i]; // set up outputs
c.center = &bufs [0]; for ( int j = 0; j < chan_types_count; j++ )
c.left = &bufs [1]; {
c.right = &bufs [2]; channel_t& c = chan_types [i*chan_types_count+j];
c.center = &bufs [i*max_buf_count+0];
c.left = &bufs [i*max_buf_count+1];
c.right = &bufs [i*max_buf_count+2];
}
} }
} }
if ( buf_count < max_buf_count ) if ( buf_count < max_buf_count ) // if center_only
{ {
for ( int i = 0; i < chan_types_count; i++ ) for(int i=0; i<max_voices; i++)
{ {
channel_t& c = chan_types [i]; for ( int j = 0; j < chan_types_count; j++ )
{
channel_t& c = chan_types [i*chan_types_count+j];
c.left = c.center; c.left = c.center;
c.right = c.center; c.right = c.center;
} }
} }
} }
}
Effects_Buffer::channel_t Effects_Buffer::channel( int i, int type ) Effects_Buffer::channel_t Effects_Buffer::channel( int i, int type )
{ {
int out = 2; int out = chan_types_count-1;
if ( !type ) if ( !type )
{ {
out = i % 5; out = i % 5;
if ( out > 2 ) if ( out > chan_types_count-1 )
out = 2; out = chan_types_count-1;
} }
else if ( !(type & noise_type) && (type & type_index_mask) % 3 != 0 ) else if ( !(type & noise_type) && (type & type_index_mask) % 3 != 0 )
{ {
out = type & 1; 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 ) void Effects_Buffer::end_frame( blip_time_t clock_count )
{ {
int bufs_used = 0; 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); 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();
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 ) if ( effects_enabled || config_.effects_enabled )
effect_remain = bufs [0].samples_avail() + bufs [0].output_latency(); 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; 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 ) 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(); long remain = bufs [0].samples_avail();
if ( remain > (total_samples >> 1) ) total_samples = remain = min( remain, total_samples/n_channels );
remain = (total_samples >> 1);
total_samples = remain;
while ( remain ) while ( remain )
{ {
int active_bufs = buf_count; int active_bufs = buf_count_per_voice;
long count = remain; long count = remain;
// optimizing mixing to skip any channels which had nothing added // 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; active_bufs = 1;
} }
out += count * 2; out += count * n_channels;
remain -= count; remain -= count;
stereo_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 ) if ( effect_remain < 0 )
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
{
for ( int i = 0; i < buf_count_per_voice; i++) // foreach buffer of that voice
{ {
if ( i < active_bufs ) if ( i < active_bufs )
bufs [i].remove_samples( count ); bufs [v*buf_count_per_voice + i].remove_samples( count );
else else // keep time synchronized
bufs [i].remove_silence( count ); // 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 ) void Effects_Buffer::mix_mono( blip_sample_t* out_, blargg_long count )
{
for(int i=0; i<max_voices; i++)
{ {
blip_sample_t* BLIP_RESTRICT out = out_; blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [0] ); int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( c, bufs [0] ); BLIP_READER_BEGIN( c, bufs [i*max_buf_count+0] );
// unrolled loop // unrolled loop
for ( blargg_long n = count >> 1; n; --n ) for ( blargg_long n = count >> 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 ); blargg_long cs1 = BLIP_READER_READ( c );
BLIP_READER_NEXT( c, bass ); BLIP_READER_NEXT( c, bass );
if ( (BOOST::int16_t) cs0 != cs0 ) if ( (int16_t) cs0 != cs0 )
cs0 = 0x7FFF - (cs0 >> 24); 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); cs1 = 0x7FFF - (cs1 >> 24);
((BOOST::uint32_t*) out) [1] = ((BOOST::uint16_t) cs1) | (cs1 << 16); ((uint32_t*) out) [i*2+1] = ((uint16_t) cs1) | (uint16_t(cs1) << 16);
out += 4; out += max_voices*4;
} }
if ( count & 1 ) if ( count & 1 )
{ {
int s = BLIP_READER_READ( c ); int s = BLIP_READER_READ( c );
BLIP_READER_NEXT( c, bass ); BLIP_READER_NEXT( c, bass );
out [0] = s; out [i*2+0] = s;
out [1] = s; out [i*2+1] = s;
if ( (BOOST::int16_t) s != s ) if ( (int16_t) s != s )
{ {
s = 0x7FFF - (s >> 24); s = 0x7FFF - (s >> 24);
out [0] = s; out [i*2+0] = s;
out [1] = 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<max_voices; i++)
{ {
blip_sample_t* BLIP_RESTRICT out = out_; blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [0] ); int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( c, bufs [0] ); BLIP_READER_BEGIN( c, bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( l, bufs [1] ); BLIP_READER_BEGIN( l, bufs [i*max_buf_count+1] );
BLIP_READER_BEGIN( r, bufs [2] ); BLIP_READER_BEGIN( r, bufs [i*max_buf_count+2] );
int count = frames;
while ( count-- ) while ( count-- )
{ {
int cs = BLIP_READER_READ( c ); int cs = BLIP_READER_READ( c );
@ -368,36 +425,41 @@ void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count )
BLIP_READER_NEXT( l, bass ); BLIP_READER_NEXT( l, bass );
BLIP_READER_NEXT( r, bass ); BLIP_READER_NEXT( r, bass );
if ( (BOOST::int16_t) left != left ) if ( (int16_t) left != left )
left = 0x7FFF - (left >> 24); left = 0x7FFF - (left >> 24);
out [0] = left; if ( (int16_t) right != right )
out [1] = right; right = 0x7FFF - (right >> 24);
out += 2; out [i*2+0] = left;
out [i*2+1] = right;
out += max_voices*2;
if ( (BOOST::int16_t) right != right )
out [-1] = 0x7FFF - (right >> 24);
} }
BLIP_READER_END( r, bufs [2] ); BLIP_READER_END( r, bufs [i*max_buf_count+2] );
BLIP_READER_END( l, bufs [1] ); BLIP_READER_END( l, bufs [i*max_buf_count+1] );
BLIP_READER_END( c, bufs [0] ); 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; i<max_voices; i++)
{ {
blip_sample_t* BLIP_RESTRICT out = out_; blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [2] ); int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+2] );
BLIP_READER_BEGIN( center, bufs [2] ); BLIP_READER_BEGIN( center, bufs [i*max_buf_count+2] );
BLIP_READER_BEGIN( sq1, bufs [0] ); BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( sq2, bufs [1] ); BLIP_READER_BEGIN( sq2, bufs [i*max_buf_count+1] );
blip_sample_t* const reverb_buf = this->reverb_buf.begin(); blip_sample_t* const reverb_buf = &this->reverb_buf[i][0];
blip_sample_t* const echo_buf = this->echo_buf.begin(); blip_sample_t* const echo_buf = &this->echo_buf[i][0];
int echo_pos = this->echo_pos; int echo_pos = this->echo_pos[i];
int reverb_pos = this->reverb_pos; int reverb_pos = this->reverb_pos[i];
int count = frames;
while ( count-- ) while ( count-- )
{ {
int sum1_s = BLIP_READER_READ( sq1 ); 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_buf [echo_pos] = sum3_s;
echo_pos = (echo_pos + 1) & echo_mask; echo_pos = (echo_pos + 1) & echo_mask;
if ( (BOOST::int16_t) left != left ) if ( (int16_t) left != left )
left = 0x7FFF - (left >> 24); left = 0x7FFF - (left >> 24);
out [0] = left; if ( (int16_t) right != right )
out [1] = right; right = 0x7FFF - (right >> 24);
out += 2; out [i*2+0] = left;
out [i*2+1] = right;
if ( (BOOST::int16_t) right != right ) out += max_voices*2;
out [-1] = 0x7FFF - (right >> 24);
} }
this->reverb_pos = reverb_pos; this->reverb_pos[i] = reverb_pos;
this->echo_pos = echo_pos; this->echo_pos[i] = echo_pos;
BLIP_READER_END( sq1, bufs [0] ); BLIP_READER_END( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_END( sq2, bufs [1] ); BLIP_READER_END( sq2, bufs [i*max_buf_count+1] );
BLIP_READER_END( center, bufs [2] ); 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; i<max_voices; i++)
{ {
blip_sample_t* BLIP_RESTRICT out = out_; blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [2] ); int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+2] );
BLIP_READER_BEGIN( center, bufs [2] ); BLIP_READER_BEGIN( center, bufs [i*max_buf_count+2] );
BLIP_READER_BEGIN( l1, bufs [3] ); BLIP_READER_BEGIN( l1, bufs [i*max_buf_count+3] );
BLIP_READER_BEGIN( r1, bufs [4] ); BLIP_READER_BEGIN( r1, bufs [i*max_buf_count+4] );
BLIP_READER_BEGIN( l2, bufs [5] ); BLIP_READER_BEGIN( l2, bufs [i*max_buf_count+5] );
BLIP_READER_BEGIN( r2, bufs [6] ); BLIP_READER_BEGIN( r2, bufs [i*max_buf_count+6] );
BLIP_READER_BEGIN( sq1, bufs [0] ); BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( sq2, bufs [1] ); BLIP_READER_BEGIN( sq2, bufs [i*max_buf_count+1] );
blip_sample_t* const reverb_buf = this->reverb_buf.begin(); blip_sample_t* const reverb_buf = &this->reverb_buf[i][0];
blip_sample_t* const echo_buf = this->echo_buf.begin(); blip_sample_t* const echo_buf = &this->echo_buf[i][0];
int echo_pos = this->echo_pos; int echo_pos = this->echo_pos[i];
int reverb_pos = this->reverb_pos; int reverb_pos = this->reverb_pos[i];
int count = frames;
while ( count-- ) while ( count-- )
{ {
int sum1_s = BLIP_READER_READ( sq1 ); 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_buf [echo_pos] = sum3_s;
echo_pos = (echo_pos + 1) & echo_mask; echo_pos = (echo_pos + 1) & echo_mask;
if ( (BOOST::int16_t) left != left ) if ( (int16_t) left != left )
left = 0x7FFF - (left >> 24); left = 0x7FFF - (left >> 24);
out [0] = left; if ( (int16_t) right != right )
out [1] = right; right = 0x7FFF - (right >> 24);
out += 2; out [i*2+0] = left;
out [i*2+1] = right;
if ( (BOOST::int16_t) right != right ) out += max_voices*2;
out [-1] = 0x7FFF - (right >> 24);
} }
this->reverb_pos = reverb_pos; this->reverb_pos[i] = reverb_pos;
this->echo_pos = echo_pos; this->echo_pos[i] = echo_pos;
BLIP_READER_END( l1, bufs [3] ); BLIP_READER_END( l1, bufs [i*max_buf_count+3] );
BLIP_READER_END( r1, bufs [4] ); BLIP_READER_END( r1, bufs [i*max_buf_count+4] );
BLIP_READER_END( l2, bufs [5] ); BLIP_READER_END( l2, bufs [i*max_buf_count+5] );
BLIP_READER_END( r2, bufs [6] ); BLIP_READER_END( r2, bufs [i*max_buf_count+6] );
BLIP_READER_END( sq1, bufs [0] ); BLIP_READER_END( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_END( sq2, bufs [1] ); BLIP_READER_END( sq2, bufs [i*max_buf_count+1] );
BLIP_READER_END( center, bufs [2] ); BLIP_READER_END( center, bufs [i*max_buf_count+2] );
}
} }

View file

@ -1,17 +1,21 @@
// Multi-channel effects buffer with panning, echo and reverb // 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 #ifndef EFFECTS_BUFFER_H
#define EFFECTS_BUFFER_H #define EFFECTS_BUFFER_H
#include "Multi_Buffer.h" #include "Multi_Buffer.h"
#include <vector>
// Effects_Buffer uses several buffers and outputs stereo sample pairs. // Effects_Buffer uses several buffers and outputs stereo sample pairs.
class Effects_Buffer : public Multi_Buffer { class Effects_Buffer : public Multi_Buffer {
public: 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 // If center_only is true, only center buffers are created and
// less memory is used. // less memory is used.
Effects_Buffer( bool center_only = false ); Effects_Buffer( int nVoices = 1, bool center_only = false );
// Channel Effect Center Pan // Channel Effect Center Pan
// --------------------------------- // ---------------------------------
@ -50,21 +54,21 @@ public:
long samples_avail() const; long samples_avail() const;
private: private:
typedef long fixed_t; typedef long fixed_t;
int max_voices;
enum { max_buf_count = 7 }; enum { max_buf_count = 7 };
Blip_Buffer bufs [max_buf_count]; std::vector<Blip_Buffer> bufs;
enum { chan_types_count = 3 }; enum { chan_types_count = 3 };
channel_t chan_types [3]; std::vector<channel_t> chan_types;
config_t config_; config_t config_;
long stereo_remain; long stereo_remain;
long effect_remain; long effect_remain;
int buf_count; int buf_count;
bool effects_enabled; bool effects_enabled;
blargg_vector<blip_sample_t> reverb_buf; std::vector<std::vector<blip_sample_t> > reverb_buf;
blargg_vector<blip_sample_t> echo_buf; std::vector<std::vector<blip_sample_t> > echo_buf;
int reverb_pos; std::vector<int> reverb_pos;
int echo_pos; std::vector<int> echo_pos;
struct { struct {
fixed_t pan_1_levels [2]; fixed_t pan_1_levels [2];

View file

@ -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" #include "Fir_Resampler.h"
@ -23,10 +23,6 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#undef PI #undef PI
#define PI 3.1415926535897932384626433832795029 #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, static void gen_sinc( double rolloff, int width, double offset, double spacing, double scale,
int count, short* out ) 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_ ) : Fir_Resampler_::Fir_Resampler_( int width, sample_t* impulses_ ) :
width_( width ), width_( width ),
write_offset( width * stereo - stereo ), write_offset( width * stereo - stereo ),
@ -164,7 +156,7 @@ int Fir_Resampler_::input_needed( blargg_long output_count ) const
output_count -= 2; 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 ) if ( input_extra < 0 )
input_extra = 0; input_extra = 0;
return input_extra; return input_extra;
@ -194,8 +186,8 @@ int Fir_Resampler_::avail_( blargg_long input_count ) const
int Fir_Resampler_::skip_input( long count ) int Fir_Resampler_::skip_input( long count )
{ {
int remain = int(write_pos - buf.begin()); int remain = write_pos - buf.begin();
int max_count = int(remain - width_ * stereo); int max_count = remain - width_ * stereo;
if ( count > max_count ) if ( count > max_count )
count = max_count; count = max_count;

View file

@ -1,6 +1,6 @@
// Finite impulse response (FIR) resampler with adjustable FIR size // 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 #ifndef FIR_RESAMPLER_H
#define FIR_RESAMPLER_H #define FIR_RESAMPLER_H
@ -31,7 +31,7 @@ public:
void clear(); void clear();
// Number of input samples that can be written // 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 // Pointer to place to write input samples
sample_t* buffer() { return write_pos; } sample_t* buffer() { return write_pos; }
@ -40,7 +40,7 @@ public:
void write( long count ); void write( long count );
// Number of input samples in buffer // 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. // Skip 'count' input samples. Returns number of samples actually skipped.
int skip_input( long count ); int skip_input( long count );
@ -51,7 +51,7 @@ public:
int input_needed( blargg_long count ) const; int input_needed( blargg_long count ) const;
// Number of output samples available // 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: public:
~Fir_Resampler_(); ~Fir_Resampler_();
@ -161,11 +161,11 @@ int Fir_Resampler<width>::read( sample_t* out_begin, blargg_long count )
imp_phase = res - remain; imp_phase = res - remain;
int left = int(write_pos - in); int left = write_pos - in;
write_pos = &buf [left]; write_pos = &buf [left];
memmove( buf.begin(), in, left * sizeof *in ); memmove( buf.begin(), in, left * sizeof *in );
return int(out - out_begin); return out - out_begin;
} }
#endif #endif

View file

@ -123,7 +123,7 @@ void Gb_Apu::reset()
0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table
0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA 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 ) void Gb_Apu::run_until( blip_time_t end_time )

View file

@ -68,7 +68,7 @@ private:
Gb_Square square2; Gb_Square square2;
Gb_Wave wave; Gb_Wave wave;
Gb_Noise noise; Gb_Noise noise;
BOOST::uint8_t regs [register_count]; uint8_t regs [register_count];
Gb_Square::Synth square_synth; // used by squares Gb_Square::Synth square_synth; // used by squares
Gb_Wave::Synth other_synth; // used by wave and noise Gb_Wave::Synth other_synth; // used by wave and noise

View file

@ -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" #include "Gb_Cpu.h"
@ -89,12 +89,6 @@ unsigned const n_flag = 0x40;
unsigned const h_flag = 0x20; unsigned const h_flag = 0x20;
unsigned const c_flag = 0x10; 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 ) bool Gb_Cpu::run( blargg_long cycle_count )
{ {
state_.remain = blargg_ulong (cycle_count + clocks_per_instr) / clocks_per_instr; 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; this->state = &s;
memcpy( &s, &this->state_, sizeof s ); memcpy( &s, &this->state_, sizeof s );
typedef BOOST::uint16_t uint16_t;
#if BLARGG_BIG_ENDIAN #if BLARGG_BIG_ENDIAN
#define R8( n ) (r8_ [n]) #define R8( n ) (r8_ [n])
#elif BLARGG_LITTLE_ENDIAN #elif BLARGG_LITTLE_ENDIAN
@ -116,11 +108,11 @@ bool Gb_Cpu::run( blargg_long cycle_count )
core_regs_t rg; // individual registers core_regs_t rg; // individual registers
struct { struct {
BOOST::uint16_t bc, de, hl, unused; // pairs uint16_t bc, de, hl, unused; // pairs
} rp; } rp;
uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence) 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 ); BOOST_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 );
@ -168,7 +160,7 @@ loop:
#define BRANCH( cond )\ #define BRANCH( cond )\
{\ {\
pc++;\ pc++;\
int offset = (BOOST::int8_t) data;\ int offset = (int8_t) data;\
if ( !(cond) ) goto loop;\ if ( !(cond) ) goto loop;\
pc = uint16_t (pc + offset);\ pc = uint16_t (pc + offset);\
goto loop;\ goto loop;\
@ -688,7 +680,7 @@ loop:
unsigned prev; unsigned prev;
case 0xF8: // LD HL,SP+imm 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++; pc++;
flags = 0; flags = 0;
temp += sp; temp += sp;
@ -696,7 +688,7 @@ loop:
goto add_16_hl; goto add_16_hl;
case 0xE8: // ADD SP,IMM 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++; pc++;
flags = 0; flags = 0;
temp += sp; temp += sp;
@ -717,7 +709,7 @@ loop:
temp += prev; temp += prev;
flags &= z_flag; flags &= z_flag;
add_16_hl: add_16_hl:
rp.hl = (uint16_t)temp; rp.hl = temp;
add_16_comm: add_16_comm:
flags |= (temp >> 12) & c_flag; flags |= (temp >> 12) & c_flag;
flags |= (((temp & 0x0FFF) - (prev & 0x0FFF)) >> 7) & h_flag; flags |= (((temp & 0x0FFF) - (prev & 0x0FFF)) >> 7) & h_flag;

View file

@ -1,7 +1,7 @@
// Nintendo Game Boy CPU emulator // Nintendo Game Boy CPU emulator
// Treats every instruction as taking 4 cycles // 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 #ifndef GB_CPU_H
#define GB_CPU_H #define GB_CPU_H
@ -13,8 +13,6 @@ typedef unsigned gb_addr_t; // 16-bit CPU address
class Gb_Cpu { class Gb_Cpu {
enum { clocks_per_instr = 4 }; enum { clocks_per_instr = 4 };
public: public:
typedef BOOST::uint8_t uint8_t;
// Clear registers and map all pages to unmapped // Clear registers and map all pages to unmapped
void reset( void* unmapped = 0 ); void reset( void* unmapped = 0 );
@ -39,7 +37,7 @@ public:
struct registers_t : core_regs_t { struct registers_t : core_regs_t {
long pc; // more than 16 bits to allow overflow detection long pc; // more than 16 bits to allow overflow detection
BOOST::uint16_t sp; uint16_t sp;
}; };
registers_t r; registers_t r;
@ -81,7 +79,7 @@ private:
void set_code_page( int, uint8_t* ); 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 return state->code_map [addr >> page_shift] + addr
#if !BLARGG_NONPORTABLE #if !BLARGG_NONPORTABLE

View file

@ -15,7 +15,7 @@ struct Gb_Osc
Blip_Buffer* outputs [4]; // NULL, right, left, center Blip_Buffer* outputs [4]; // NULL, right, left, center
Blip_Buffer* output; Blip_Buffer* output;
int output_select; int output_select;
BOOST::uint8_t* regs; // osc's 5 registers uint8_t* regs; // osc's 5 registers
int delay; int delay;
int last_amp; int last_amp;
@ -68,7 +68,7 @@ struct Gb_Wave : Gb_Osc
Synth const* synth; Synth const* synth;
int wave_pos; int wave_pos;
enum { wave_size = 32 }; enum { wave_size = 32 };
BOOST::uint8_t wave [wave_size]; uint8_t wave [wave_size];
void write_register( int, int ); void write_register( int, int );
void run( blip_time_t, blip_time_t, int playing ); void run( blip_time_t, blip_time_t, int playing );

View file

@ -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" #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 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 }; 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 // Setup
@ -175,7 +175,7 @@ void Gbs_Emu::update_timer()
play_period = blip_time_t (play_period / tempo()); 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 0x80, 0xBF, 0x00, 0x00, 0xBF, // square 1
0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2 0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2
0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave 0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave

View file

@ -1,6 +1,6 @@
// Nintendo Game Boy GBS music file emulator // 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 #ifndef GBS_EMU_H
#define GBS_EMU_H #define GBS_EMU_H

View file

@ -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" #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 ) blargg_err_t Gme_File::load_( Data_Reader& in )
{ {
RETURN_ERR( file_data.resize( in.remain() ) ); RETURN_ERR( file_data.resize( in.remain() ) );
RETURN_ERR( in.read( file_data.begin(), (long)file_data.size() ) ); RETURN_ERR( in.read( file_data.begin(), file_data.size() ) );
return load_mem_( file_data.begin(), (long)file_data.size() ); return load_mem_( file_data.begin(), file_data.size() );
} }
// public load functions call this at beginning // public load functions call this at beginning

View file

@ -1,6 +1,6 @@
// Common interface to game music file loading and information // 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 #ifndef GME_FILE_H
#define GME_FILE_H #define GME_FILE_H
@ -105,7 +105,7 @@ public:
Gme_File(); Gme_File();
virtual ~Gme_File(); virtual ~Gme_File();
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
typedef BOOST::uint8_t byte; typedef uint8_t byte;
protected: protected:
// Services // Services
void set_track_count( int n ) { track_count_ = raw_track_count_ = n; } 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 ); } { Gme_File::copy_field_( out->name, in.name, sizeof in.name ); }
#ifndef GME_FILE_READER #ifndef GME_FILE_READER
#ifdef HAVE_ZLIB_H
#define GME_FILE_READER Gzip_File_Reader
#else
#define GME_FILE_READER Std_File_Reader #define GME_FILE_READER Std_File_Reader
#endif
#elif defined (GME_FILE_READER_INCLUDE) #elif defined (GME_FILE_READER_INCLUDE)
#include GME_FILE_READER_INCLUDE #include GME_FILE_READER_INCLUDE
#endif #endif

View file

@ -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" #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 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 }; 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 // Setup

View file

@ -1,7 +1,7 @@
// Sega Genesis/Mega Drive GYM music file emulator // Sega Genesis/Mega Drive GYM music file emulator
// Includes with PCM timing recovery to improve sample quality. // 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 #ifndef GYM_EMU_H
#define GYM_EMU_H #define GYM_EMU_H

View file

@ -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" #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; unsigned noise_lfsr = this->noise_lfsr;
do do
{ {
int new_dac = 0x1F & (unsigned)-(int)(noise_lfsr >> 1 & 1); int new_dac = 0x1F & -(noise_lfsr >> 1 & 1);
// Implemented using "Galios configuration" // Implemented using "Galios configuration"
// TODO: find correct LFSR algorithm // 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)); //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1));
int delta = new_dac - dac; int delta = new_dac - dac;
if ( delta ) if ( delta )

View file

@ -1,6 +1,6 @@
// Turbo Grafx 16 (PC Engine) PSG sound chip emulator // 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 #ifndef HES_APU_H
#define HES_APU_H #define HES_APU_H

View file

@ -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" #include "Hes_Cpu.h"
@ -39,7 +39,7 @@ int const ram_addr = 0x2000;
// status flags // status flags
int const st_n = 0x80; int const st_n = 0x80;
int const st_v = 0x40; int const st_v = 0x40;
//unused: int const st_t = 0x20; int const st_t = 0x20;
int const st_b = 0x10; int const st_b = 0x10;
int const st_d = 0x08; int const st_d = 0x08;
int const st_i = 0x04; 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 GET_SP() ((sp - 1) & 0xFF)
#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) #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 Hes_Cpu::run( hes_time_t end_time )
{ {
bool illegal_encountered = false; bool illegal_encountered = false;
@ -100,14 +94,14 @@ bool Hes_Cpu::run( hes_time_t end_time )
state_t s = this->state_; state_t s = this->state_;
this->state = &s; this->state = &s;
// even on x86, using s.time in place of s_time was slower // 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 // registers
fuint16 pc = r.pc; uint16_t pc = r.pc;
fuint8 a = r.a; uint8_t a = r.a;
fuint8 x = r.x; uint8_t x = r.x;
fuint8 y = r.y; uint8_t y = r.y;
fuint16 sp; uint16_t sp;
SET_SP( r.sp ); SET_SP( r.sp );
#define IS_NEG (nz & 0x8080) #define IS_NEG (nz & 0x8080)
@ -126,11 +120,11 @@ bool Hes_Cpu::run( hes_time_t end_time )
nz |= ~in & st_z;\ nz |= ~in & st_z;\
} while ( 0 ) } while ( 0 )
fuint8 status; uint8_t status;
fuint16 c; // carry set if (c & 0x100) != 0 uint16_t c; // carry set if (c & 0x100) != 0
fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 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 ); SET_STATUS( temp );
} }
@ -159,7 +153,7 @@ loop:
check( (unsigned) x < 0x100 ); check( (unsigned) x < 0x100 );
uint8_t const* instr = s.code_map [pc >> page_shift]; uint8_t const* instr = s.code_map [pc >> page_shift];
fuint8 opcode; uint8_t opcode;
// TODO: eliminate this special case // TODO: eliminate this special case
#if BLARGG_NONPORTABLE #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 4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F
}; // 0x00 was 8 }; // 0x00 was 8
fuint16 data; uint16_t data;
data = clock_table [opcode]; data = clock_table [opcode];
if ( (s_time += data) >= 0 ) if ( (s_time += data) >= 0 )
goto possibly_out_of_time; 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 // TODO: more efficient way to handle negative branch that wraps PC around
#define BRANCH( cond )\ #define BRANCH( cond )\
{\ {\
fint16 offset = (BOOST::int8_t) data;\ int16_t offset = (int8_t) data;\
pc++;\ pc++;\
if ( !(cond) ) goto branch_not_taken;\ if ( !(cond) ) goto branch_not_taken;\
pc = BOOST::uint16_t (pc + offset);\ pc = uint16_t (pc + offset);\
goto loop;\ goto loop;\
} }
@ -283,7 +277,7 @@ possibly_out_of_time:
case 0xCF: case 0xCF:
case 0xDF: case 0xDF:
case 0xEF: { case 0xEF: {
fuint16 t = 0x101 * READ_LOW( data ); uint16_t t = 0x101 * READ_LOW( data );
t ^= 0xFF; t ^= 0xFF;
pc++; pc++;
data = GET_MSB(); data = GET_MSB();
@ -311,7 +305,7 @@ possibly_out_of_time:
goto branch_taken; goto branch_taken;
case 0x20: { // JSR case 0x20: { // JSR
fuint16 temp = pc + 1; uint16_t temp = pc + 1;
pc = GET_ADDR(); pc = GET_ADDR();
WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
sp = (sp - 2) | 0x100; sp = (sp - 2) | 0x100;
@ -332,7 +326,7 @@ possibly_out_of_time:
case 0xBD:{// LDA abs,X case 0xBD:{// LDA abs,X
PAGE_CROSS_PENALTY( data + x ); PAGE_CROSS_PENALTY( data + x );
fuint16 addr = GET_ADDR() + x; uint16_t addr = GET_ADDR() + x;
pc += 2; pc += 2;
CPU_READ_FAST( this, addr, TIME, nz ); CPU_READ_FAST( this, addr, TIME, nz );
a = nz; a = nz;
@ -340,7 +334,7 @@ possibly_out_of_time:
} }
case 0x9D:{// STA abs,X case 0x9D:{// STA abs,X
fuint16 addr = GET_ADDR() + x; uint16_t addr = GET_ADDR() + x;
pc += 2; pc += 2;
CPU_WRITE_FAST( this, addr, a, TIME ); CPU_WRITE_FAST( this, addr, a, TIME );
goto loop; goto loop;
@ -354,7 +348,7 @@ possibly_out_of_time:
goto loop; goto loop;
case 0xAE:{// LDX abs case 0xAE:{// LDX abs
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
CPU_READ_FAST( this, addr, TIME, nz ); CPU_READ_FAST( this, addr, TIME, nz );
x = nz; x = nz;
@ -369,7 +363,7 @@ possibly_out_of_time:
// Load/store // Load/store
{ {
fuint16 addr; uint16_t addr;
case 0x91: // STA (ind),Y case 0x91: // STA (ind),Y
addr = 0x100 * READ_LOW( uint8_t (data + 1) ); addr = 0x100 * READ_LOW( uint8_t (data + 1) );
addr += READ_LOW( data ) + y; addr += READ_LOW( data ) + y;
@ -395,7 +389,7 @@ possibly_out_of_time:
} }
{ {
fuint16 addr; uint16_t addr;
case 0xA1: // LDA (ind,X) case 0xA1: // LDA (ind,X)
data = uint8_t (data + x); data = uint8_t (data + x);
case 0xB2: // LDA (ind) case 0xB2: // LDA (ind)
@ -425,7 +419,7 @@ possibly_out_of_time:
case 0xBE:{// LDX abs,y case 0xBE:{// LDX abs,y
PAGE_CROSS_PENALTY( data + y ); PAGE_CROSS_PENALTY( data + y );
fuint16 addr = GET_ADDR() + y; uint16_t addr = GET_ADDR() + y;
pc += 2; pc += 2;
FLUSH_TIME(); FLUSH_TIME();
x = nz = READ( addr ); x = nz = READ( addr );
@ -449,7 +443,7 @@ possibly_out_of_time:
case 0x3C: // BIT abs,x case 0x3C: // BIT abs,x
data += x; data += x;
case 0x2C:{// BIT abs case 0x2C:{// BIT abs
fuint16 addr; uint16_t addr;
ADD_PAGE( addr ); ADD_PAGE( addr );
FLUSH_TIME(); FLUSH_TIME();
nz = READ( addr ); nz = READ( addr );
@ -472,7 +466,7 @@ possibly_out_of_time:
goto loop; goto loop;
{ {
fuint16 addr; uint16_t addr;
case 0xB3: // TST abs,x case 0xB3: // TST abs,x
addr = GET_MSB() + x; addr = GET_MSB() + x;
@ -505,7 +499,7 @@ possibly_out_of_time:
goto loop; goto loop;
{ {
fuint16 addr; uint16_t addr;
case 0x0C: // TSB abs case 0x0C: // TSB abs
case 0x1C: // TRB abs case 0x1C: // TRB abs
addr = GET_ADDR(); addr = GET_ADDR();
@ -610,7 +604,7 @@ possibly_out_of_time:
data += x; data += x;
PAGE_CROSS_PENALTY( data ); PAGE_CROSS_PENALTY( data );
case 0xAC:{// LDY abs case 0xAC:{// LDY abs
fuint16 addr = data + 0x100 * GET_MSB(); uint16_t addr = data + 0x100 * GET_MSB();
pc += 2; pc += 2;
FLUSH_TIME(); FLUSH_TIME();
y = nz = READ( addr ); y = nz = READ( addr );
@ -619,7 +613,7 @@ possibly_out_of_time:
} }
{ {
fuint8 temp; uint8_t temp;
case 0x8C: // STY abs case 0x8C: // STY abs
temp = y; temp = y;
goto store_abs; goto store_abs;
@ -627,7 +621,7 @@ possibly_out_of_time:
case 0x8E: // STX abs case 0x8E: // STX abs
temp = x; temp = x;
store_abs: store_abs:
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
FLUSH_TIME(); FLUSH_TIME();
WRITE( addr, temp ); WRITE( addr, temp );
@ -638,7 +632,7 @@ possibly_out_of_time:
// Compare // Compare
case 0xEC:{// CPX abs case 0xEC:{// CPX abs
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc++; pc++;
FLUSH_TIME(); FLUSH_TIME();
data = READ( addr ); data = READ( addr );
@ -657,7 +651,7 @@ possibly_out_of_time:
goto loop; goto loop;
case 0xCC:{// CPY abs case 0xCC:{// CPY abs
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc++; pc++;
FLUSH_TIME(); FLUSH_TIME();
data = READ( addr ); data = READ( addr );
@ -684,7 +678,7 @@ possibly_out_of_time:
data = 0x100 * READ_LOW( uint8_t (data + 1) ) + READ_LOW( data );\ data = 0x100 * READ_LOW( uint8_t (data + 1) ) + READ_LOW( data );\
goto ptr##op;\ goto ptr##op;\
case op + 0x0C:{/* (ind),y */\ case op + 0x0C:{/* (ind),y */\
fuint16 temp = READ_LOW( data ) + y;\ uint16_t temp = READ_LOW( data ) + y;\
PAGE_CROSS_PENALTY( temp );\ PAGE_CROSS_PENALTY( temp );\
data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\
goto ptr##op;\ goto ptr##op;\
@ -742,8 +736,8 @@ possibly_out_of_time:
adc_imm: { adc_imm: {
if ( status & st_d ) if ( status & st_d )
debug_printf( "Decimal mode not supported\n" ); debug_printf( "Decimal mode not supported\n" );
fint16 carry = c >> 8 & 1; int16_t carry = c >> 8 & 1;
fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend int16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend
status &= ~st_v; status &= ~st_v;
status |= ov >> 2 & 0x40; status |= ov >> 2 & 0x40;
c = nz = a + data + carry; c = nz = a + data + carry;
@ -771,7 +765,7 @@ possibly_out_of_time:
case 0x2A: { // ROL A case 0x2A: { // ROL A
nz = a << 1; nz = a << 1;
fint16 temp = c >> 8 & 1; int16_t temp = c >> 8 & 1;
c = nz; c = nz;
nz |= temp; nz |= temp;
a = (uint8_t) nz; a = (uint8_t) nz;
@ -877,7 +871,7 @@ possibly_out_of_time:
case 0xD6: // DEC zp,x case 0xD6: // DEC zp,x
data = uint8_t (data + x); data = uint8_t (data + x);
case 0xC6: // DEC zp case 0xC6: // DEC zp
nz = (unsigned) -1; nz = (uint16_t) -1;
add_nz_zp: add_nz_zp:
nz += READ_LOW( data ); nz += READ_LOW( data );
write_nz_zp: write_nz_zp:
@ -902,7 +896,7 @@ possibly_out_of_time:
case 0xCE: // DEC abs case 0xCE: // DEC abs
data = GET_ADDR(); data = GET_ADDR();
dec_ptr: dec_ptr:
nz = (unsigned) -1; nz = (uint16_t) -1;
inc_common: inc_common:
FLUSH_TIME(); FLUSH_TIME();
nz += READ( data ); nz += READ( data );
@ -942,7 +936,7 @@ possibly_out_of_time:
goto loop; goto loop;
#define SWAP_REGS( r1, r2 ) {\ #define SWAP_REGS( r1, r2 ) {\
fuint8 t = r1;\ uint8_t t = r1;\
r1 = r2;\ r1 = r2;\
r2 = t;\ r2 = t;\
goto loop;\ goto loop;\
@ -984,7 +978,7 @@ possibly_out_of_time:
goto loop; goto loop;
case 0x40:{// RTI 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 - 0xFF) );
pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
sp = (sp - 0xFD) | 0x100; sp = (sp - 0xFD) | 0x100;
@ -1018,8 +1012,8 @@ possibly_out_of_time:
goto loop; goto loop;
case 0x28:{// PLP case 0x28:{// PLP
fuint8 temp = POP(); uint8_t temp = POP();
fuint8 changed = status ^ temp; uint8_t changed = status ^ temp;
SET_STATUS( temp ); SET_STATUS( temp );
if ( !(changed & st_i) ) if ( !(changed & st_i) )
goto loop; // I flag didn't change goto loop; // I flag didn't change
@ -1030,7 +1024,7 @@ possibly_out_of_time:
#undef POP #undef POP
case 0x08: { // PHP case 0x08: { // PHP
fuint8 temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
PUSH( temp | st_b ); PUSH( temp | st_b );
goto loop; goto loop;
@ -1039,7 +1033,7 @@ possibly_out_of_time:
// Flags // Flags
case 0x38: // SEC case 0x38: // SEC
c = (unsigned) ~0; c = (uint16_t) ~0;
goto loop; goto loop;
case 0x18: // CLC case 0x18: // CLC
@ -1107,7 +1101,7 @@ possibly_out_of_time:
// Special // Special
case 0x53:{// TAM 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++; pc++;
for ( int i = 0; i < 8; i++ ) for ( int i = 0; i < 8; i++ )
if ( bits & (1 << i) ) if ( bits & (1 << i) )
@ -1131,7 +1125,7 @@ possibly_out_of_time:
case 0x03: // ST0 case 0x03: // ST0
case 0x13: // ST1 case 0x13: // ST1
case 0x23:{// ST2 case 0x23:{// ST2
fuint16 addr = opcode >> 4; uint16_t addr = opcode >> 4;
if ( addr ) if ( addr )
addr++; addr++;
pc++; pc++;
@ -1153,7 +1147,7 @@ possibly_out_of_time:
goto loop; goto loop;
case 0xF4: { // SET case 0xF4: { // SET
//fuint16 operand = GET_MSB(); //uint16_t operand = GET_MSB();
debug_printf( "SET not handled\n" ); debug_printf( "SET not handled\n" );
//switch ( data ) //switch ( data )
//{ //{
@ -1165,10 +1159,10 @@ possibly_out_of_time:
// Block transfer // Block transfer
{ {
fuint16 in_alt; uint16_t in_alt;
fint16 in_inc; int16_t in_inc;
fuint16 out_alt; uint16_t out_alt;
fint16 out_inc; int16_t out_inc;
case 0xE3: // TIA case 0xE3: // TIA
in_alt = 0; in_alt = 0;
@ -1199,8 +1193,8 @@ possibly_out_of_time:
in_alt = 0; in_alt = 0;
out_alt = 0; out_alt = 0;
bxfer: bxfer:
fuint16 in = GET_LE16( instr + 0 ); uint16_t in = GET_LE16( instr + 0 );
fuint16 out = GET_LE16( instr + 2 ); uint16_t out = GET_LE16( instr + 2 );
int count = GET_LE16( instr + 4 ); int count = GET_LE16( instr + 4 );
if ( !count ) if ( !count )
count = 0x10000; count = 0x10000;
@ -1212,7 +1206,7 @@ possibly_out_of_time:
do do
{ {
// TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O // 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 += in_inc;
in &= 0xFFFF; in &= 0xFFFF;
s.time += 6; s.time += 6;
@ -1232,7 +1226,6 @@ possibly_out_of_time:
// Illegal // Illegal
default: default:
assert( (unsigned) opcode <= 0xFF );
debug_printf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 ); debug_printf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 );
illegal_encountered = true; illegal_encountered = true;
goto loop; goto loop;
@ -1253,7 +1246,7 @@ interrupt:
pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ ); pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ );
sp = (sp - 3) | 0x100; sp = (sp - 3) | 0x100;
fuint8 temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
if ( result_ == 6 ) if ( result_ == 6 )
temp |= st_b; temp |= st_b;
@ -1290,7 +1283,7 @@ out_of_time:
r.y = y; r.y = y;
{ {
fuint8 temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
r.status = temp; r.status = temp;
} }

View file

@ -1,6 +1,6 @@
// PC Engine CPU emulator for use with HES music files // 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 #ifndef HES_CPU_H
#define HES_CPU_H #define HES_CPU_H
@ -12,8 +12,6 @@ enum { future_hes_time = INT_MAX / 2 + 1 };
class Hes_Cpu { class Hes_Cpu {
public: public:
typedef BOOST::uint8_t uint8_t;
void reset(); void reset();
enum { page_size = 0x2000 }; enum { page_size = 0x2000 };
@ -27,7 +25,7 @@ public:
// not kept updated during a call to run() // not kept updated during a call to run()
struct registers_t { struct registers_t {
BOOST::uint16_t pc; uint16_t pc;
uint8_t a; uint8_t a;
uint8_t x; uint8_t x;
uint8_t y; uint8_t y;
@ -86,7 +84,7 @@ private:
inline int update_end_time( hes_time_t end, hes_time_t irq ); 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 return state->code_map [addr >> page_shift] + addr
#if !BLARGG_NONPORTABLE #if !BLARGG_NONPORTABLE

View file

@ -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" #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 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 }; 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 // Setup

View file

@ -1,6 +1,6 @@
// TurboGrafx-16/PC Engine HES music file emulator // 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 #ifndef HES_EMU_H
#define HES_EMU_H #define HES_EMU_H

View file

@ -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 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, 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 ) bool Kss_Cpu::run( cpu_time_t end_time )
{ {
set_end_time( end_time ); set_end_time( end_time );
@ -174,8 +169,6 @@ bool Kss_Cpu::run( cpu_time_t end_time )
this->state = &s; this->state = &s;
bool warning = false; bool warning = false;
typedef BOOST::int8_t int8_t;
union { union {
regs_t rg; regs_t rg;
pairs_t rp; pairs_t rp;
@ -185,10 +178,10 @@ bool Kss_Cpu::run( cpu_time_t end_time )
rg = this->r.b; rg = this->r.b;
cpu_time_t s_time = s.time; cpu_time_t s_time = s.time;
fuint16 pc = r.pc; uint16_t pc = r.pc;
fuint16 sp = r.sp; uint16_t sp = r.sp;
fuint16 ix = r.ix; // TODO: keep in memory for direct access? uint16_t ix = r.ix; // TODO: keep in memory for direct access?
fuint16 iy = r.iy; uint16_t iy = r.iy;
int flags = r.b.flags; int flags = r.b.flags;
goto loop; goto loop;
@ -210,7 +203,7 @@ loop:
uint8_t const* instr = s.read [pc >> page_shift]; uint8_t const* instr = s.read [pc >> page_shift];
#define GET_ADDR() GET_LE16( instr ) #define GET_ADDR() GET_LE16( instr )
fuint8 opcode; uint8_t opcode;
// TODO: eliminate this special case // TODO: eliminate this special case
#if BLARGG_NONPORTABLE #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 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]; data = base_timing [opcode];
if ( (s_time += data) >= 0 ) if ( (s_time += data) >= 0 )
goto possibly_out_of_time; goto possibly_out_of_time;
@ -299,7 +292,7 @@ possibly_out_of_time:
goto loop; goto loop;
case 0x3A:{// LD A,(addr) case 0x3A:{// LD A,(addr)
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
rg.a = READ( addr ); rg.a = READ( addr );
goto loop; goto loop;
@ -315,7 +308,7 @@ possibly_out_of_time:
// JR // JR
// TODO: more efficient way to handle negative branch that wraps PC around // TODO: more efficient way to handle negative branch that wraps PC around
#define JR( cond ) {\ #define JR( cond ) {\
int offset = (BOOST::int8_t) data;\ int offset = (int8_t) data;\
pc++;\ pc++;\
if ( !(cond) )\ if ( !(cond) )\
goto jr_not_taken;\ goto jr_not_taken;\
@ -387,7 +380,7 @@ possibly_out_of_time:
case 0xCD:{// CALL addr case 0xCD:{// CALL addr
call_taken: call_taken:
fuint16 addr = pc + 2; uint16_t addr = pc + 2;
pc = GET_ADDR(); pc = GET_ADDR();
sp = uint16_t (sp - 2); sp = uint16_t (sp - 2);
WRITE_WORD( sp, addr ); WRITE_WORD( sp, addr );
@ -395,7 +388,7 @@ possibly_out_of_time:
} }
case 0xFF: // RST case 0xFF: // RST
if ( pc > idle_addr ) if ( pc >= idle_addr )
goto hit_idle_addr; goto hit_idle_addr;
CASE7( C7, CF, D7, DF, E7, EF, F7 ): CASE7( C7, CF, D7, DF, E7, EF, F7 ):
data = pc; data = pc;
@ -503,7 +496,7 @@ possibly_out_of_time:
add_hl_data: { add_hl_data: {
blargg_ulong sum = rp.hl + data; blargg_ulong sum = rp.hl + data;
data ^= rp.hl; data ^= rp.hl;
rp.hl = (uint16_t)sum; rp.hl = sum;
flags = (flags & (S80 | Z40 | V04)) | flags = (flags & (S80 | Z40 | V04)) |
(sum >> 16) | (sum >> 16) |
(sum >> 8 & (F20 | F08)) | (sum >> 8 & (F20 | F08)) |
@ -693,21 +686,21 @@ possibly_out_of_time:
goto loop; goto loop;
case 0x2A:{// LD HL,(addr) case 0x2A:{// LD HL,(addr)
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
rp.hl = READ_WORD( addr ); rp.hl = READ_WORD( addr );
goto loop; goto loop;
} }
case 0x32:{// LD (addr),A case 0x32:{// LD (addr),A
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
WRITE( addr, rg.a ); WRITE( addr, rg.a );
goto loop; goto loop;
} }
case 0x22:{// LD (addr),HL case 0x22:{// LD (addr),HL
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
WRITE_WORD( addr, rp.hl ); WRITE_WORD( addr, rp.hl );
goto loop; goto loop;
@ -730,7 +723,7 @@ possibly_out_of_time:
// Rotate // Rotate
case 0x07:{// RLCA case 0x07:{// RLCA
fuint16 temp = rg.a; uint16_t temp = rg.a;
temp = (temp << 1) | (temp >> 7); temp = (temp << 1) | (temp >> 7);
flags = (flags & (S80 | Z40 | P04)) | flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08 | C01)); (temp & (F20 | F08 | C01));
@ -739,7 +732,7 @@ possibly_out_of_time:
} }
case 0x0F:{// RRCA case 0x0F:{// RRCA
fuint16 temp = rg.a; uint16_t temp = rg.a;
flags = (flags & (S80 | Z40 | P04)) | flags = (flags & (S80 | Z40 | P04)) |
(temp & C01); (temp & C01);
temp = (temp << 7) | (temp >> 1); temp = (temp << 7) | (temp >> 1);
@ -753,12 +746,12 @@ possibly_out_of_time:
flags = (flags & (S80 | Z40 | P04)) | flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08)) | (temp & (F20 | F08)) |
(temp >> 8); (temp >> 8);
rg.a = (uint8_t)temp; rg.a = temp;
goto loop; goto loop;
} }
case 0x1F:{// RRA case 0x1F:{// RRA
fuint16 temp = (flags << 7) | (rg.a >> 1); uint16_t temp = (flags << 7) | (rg.a >> 1);
flags = (flags & (S80 | Z40 | P04)) | flags = (flags & (S80 | Z40 | P04)) |
(temp & (F20 | F08)) | (temp & (F20 | F08)) |
(rg.a & C01); (rg.a & C01);
@ -768,7 +761,7 @@ possibly_out_of_time:
// Misc // Misc
case 0x2F:{// CPL case 0x2F:{// CPL
fuint16 temp = ~rg.a; uint16_t temp = ~rg.a;
flags = (flags & (S80 | Z40 | P04 | C01)) | flags = (flags & (S80 | Z40 | P04 | C01)) |
(temp & (F20 | F08)) | (temp & (F20 | F08)) |
(H10 | N02); (H10 | N02);
@ -794,21 +787,21 @@ possibly_out_of_time:
goto loop; goto loop;
case 0xE3:{// EX (SP),HL case 0xE3:{// EX (SP),HL
fuint16 temp = READ_WORD( sp ); uint16_t temp = READ_WORD( sp );
WRITE_WORD( sp, rp.hl ); WRITE_WORD( sp, rp.hl );
rp.hl = temp; rp.hl = temp;
goto loop; goto loop;
} }
case 0xEB:{// EX DE,HL case 0xEB:{// EX DE,HL
fuint16 temp = rp.hl; uint16_t temp = rp.hl;
rp.hl = rp.de; rp.hl = rp.de;
rp.de = temp; rp.de = temp;
goto loop; goto loop;
} }
case 0xD9:{// EXX DE,HL case 0xD9:{// EXX DE,HL
fuint16 temp = r.alt.w.bc; uint16_t temp = r.alt.w.bc;
r.alt.w.bc = rp.bc; r.alt.w.bc = rp.bc;
rp.bc = temp; rp.bc = temp;
@ -849,7 +842,7 @@ possibly_out_of_time:
// Rotate left // Rotate left
#define RLC( read, write ) {\ #define RLC( read, write ) {\
fuint8 result = read;\ uint8_t result = read;\
result = uint8_t (result << 1) | (result >> 7);\ result = uint8_t (result << 1) | (result >> 7);\
flags = SZ28P( result ) | (result & C01);\ flags = SZ28P( result ) | (result & C01);\
write;\ write;\
@ -868,7 +861,7 @@ possibly_out_of_time:
} }
#define RL( read, write ) {\ #define RL( read, write ) {\
fuint16 result = (read << 1) | (flags & C01);\ uint16_t result = (read << 1) | (flags & C01);\
flags = SZ28PC( result );\ flags = SZ28PC( result );\
write;\ write;\
goto loop;\ goto loop;\
@ -886,7 +879,7 @@ possibly_out_of_time:
} }
#define SLA( read, add, write ) {\ #define SLA( read, add, write ) {\
fuint16 result = (read << 1) | add;\ uint16_t result = (read << 1) | add;\
flags = SZ28PC( result );\ flags = SZ28PC( result );\
write;\ write;\
goto loop;\ goto loop;\
@ -917,7 +910,7 @@ possibly_out_of_time:
// Rotate right // Rotate right
#define RRC( read, write ) {\ #define RRC( read, write ) {\
fuint8 result = read;\ uint8_t result = read;\
flags = result & C01;\ flags = result & C01;\
result = uint8_t (result << 7) | (result >> 1);\ result = uint8_t (result << 7) | (result >> 1);\
flags |= SZ28P( result );\ flags |= SZ28P( result );\
@ -937,8 +930,8 @@ possibly_out_of_time:
} }
#define RR( read, write ) {\ #define RR( read, write ) {\
fuint8 result = read;\ uint8_t result = read;\
fuint8 temp = result & C01;\ uint8_t temp = result & C01;\
result = uint8_t (flags << 7) | (result >> 1);\ result = uint8_t (flags << 7) | (result >> 1);\
flags = SZ28P( result ) | temp;\ flags = SZ28P( result ) | temp;\
write;\ write;\
@ -957,7 +950,7 @@ possibly_out_of_time:
} }
#define SRA( read, write ) {\ #define SRA( read, write ) {\
fuint8 result = read;\ uint8_t result = read;\
flags = result & C01;\ flags = result & C01;\
result = (result & 0x80) | (result >> 1);\ result = (result & 0x80) | (result >> 1);\
flags |= SZ28P( result );\ flags |= SZ28P( result );\
@ -977,7 +970,7 @@ possibly_out_of_time:
} }
#define SRL( read, write ) {\ #define SRL( read, write ) {\
fuint8 result = read;\ uint8_t result = read;\
flags = result & C01;\ flags = result & C01;\
result >>= 1;\ result >>= 1;\
flags |= SZ28P( result );\ flags |= SZ28P( result );\
@ -1085,7 +1078,7 @@ possibly_out_of_time:
blargg_ulong sum = temp + (flags & C01); blargg_ulong sum = temp + (flags & C01);
flags = ~data >> 2 & N02; flags = ~data >> 2 & N02;
if ( flags ) if ( flags )
sum = (blargg_ulong)-(blargg_long)sum; sum = -sum;
sum += rp.hl; sum += rp.hl;
temp ^= rp.hl; temp ^= rp.hl;
temp ^= sum; temp ^= sum;
@ -1093,7 +1086,7 @@ possibly_out_of_time:
(temp >> 8 & H10) | (temp >> 8 & H10) |
(sum >> 8 & (S80 | F20 | F08)) | (sum >> 8 & (S80 | F20 | F08)) |
((temp - -0x8000) >> 14 & V04); ((temp - -0x8000) >> 14 & V04);
rp.hl = (uint16_t)sum; rp.hl = sum;
if ( (uint16_t) sum ) if ( (uint16_t) sum )
goto loop; goto loop;
flags |= Z40; flags |= Z40;
@ -1121,7 +1114,7 @@ possibly_out_of_time:
case 0x43: // LD (ADDR),BC case 0x43: // LD (ADDR),BC
case 0x53: // LD (ADDR),DE case 0x53: // LD (ADDR),DE
temp = R16( data, 4, 0x43 ); temp = R16( data, 4, 0x43 );
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
WRITE_WORD( addr, temp ); WRITE_WORD( addr, temp );
goto loop; goto loop;
@ -1129,21 +1122,21 @@ possibly_out_of_time:
case 0x4B: // LD BC,(ADDR) case 0x4B: // LD BC,(ADDR)
case 0x5B:{// LD DE,(ADDR) case 0x5B:{// LD DE,(ADDR)
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
R16( data, 4, 0x4B ) = READ_WORD( addr ); R16( data, 4, 0x4B ) = READ_WORD( addr );
goto loop; goto loop;
} }
case 0x7B:{// LD SP,(ADDR) case 0x7B:{// LD SP,(ADDR)
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
sp = READ_WORD( addr ); sp = READ_WORD( addr );
goto loop; goto loop;
} }
case 0x67:{// RRD case 0x67:{// RRD
fuint8 temp = READ( rp.hl ); uint8_t temp = READ( rp.hl );
WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); WRITE( rp.hl, (rg.a << 4) | (temp >> 4) );
temp = (rg.a & 0xF0) | (temp & 0x0F); temp = (rg.a & 0xF0) | (temp & 0x0F);
flags = (flags & C01) | SZ28P( temp ); flags = (flags & C01) | SZ28P( temp );
@ -1152,7 +1145,7 @@ possibly_out_of_time:
} }
case 0x6F:{// RLD case 0x6F:{// RLD
fuint8 temp = READ( rp.hl ); uint8_t temp = READ( rp.hl );
WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) );
temp = (rg.a & 0xF0) | (temp >> 4); temp = (rg.a & 0xF0) | (temp >> 4);
flags = (flags & C01) | SZ28P( temp ); flags = (flags & C01) | SZ28P( temp );
@ -1176,7 +1169,7 @@ possibly_out_of_time:
case 0xA1: // CPI case 0xA1: // CPI
case 0xB1: // CPIR case 0xB1: // CPIR
inc = +1; inc = +1;
fuint16 addr = rp.hl; uint16_t addr = rp.hl;
rp.hl = addr + inc; rp.hl = addr + inc;
int temp = READ( addr ); int temp = READ( addr );
@ -1209,7 +1202,7 @@ possibly_out_of_time:
case 0xA0: // LDI case 0xA0: // LDI
case 0xB0: // LDIR case 0xB0: // LDIR
inc = +1; inc = +1;
fuint16 addr = rp.hl; uint16_t addr = rp.hl;
rp.hl = addr + inc; rp.hl = addr + inc;
int temp = READ( addr ); int temp = READ( addr );
@ -1241,7 +1234,7 @@ possibly_out_of_time:
case 0xA3: // OUTI case 0xA3: // OUTI
case 0xB3: // OTIR case 0xB3: // OTIR
inc = +1; inc = +1;
fuint16 addr = rp.hl; uint16_t addr = rp.hl;
rp.hl = addr + inc; rp.hl = addr + inc;
int temp = READ( addr ); int temp = READ( addr );
@ -1267,7 +1260,7 @@ possibly_out_of_time:
case 0xB2: // INIR case 0xB2: // INIR
inc = +1; inc = +1;
fuint16 addr = rp.hl; uint16_t addr = rp.hl;
rp.hl = addr + inc; rp.hl = addr + inc;
int temp = IN( rp.bc ); int temp = IN( rp.bc );
@ -1332,7 +1325,7 @@ possibly_out_of_time:
//////////////////////////////////////// DD/FD prefix //////////////////////////////////////// DD/FD prefix
{ {
fuint16 ixy; uint16_t ixy;
case 0xDD: case 0xDD:
ixy = ix; ixy = ix;
goto ix_prefix; goto ix_prefix;
@ -1528,7 +1521,7 @@ possibly_out_of_time:
goto loop; goto loop;
case 0x22:{// LD (ADDR),IXY case 0x22:{// LD (ADDR),IXY
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
pc += 2; pc += 2;
WRITE_WORD( addr, ixy ); WRITE_WORD( addr, ixy );
goto loop; goto loop;
@ -1540,7 +1533,7 @@ possibly_out_of_time:
goto set_ixy; goto set_ixy;
case 0x2A:{// LD IXY,(addr) case 0x2A:{// LD IXY,(addr)
fuint16 addr = GET_ADDR(); uint16_t addr = GET_ADDR();
ixy = READ_WORD( addr ); ixy = READ_WORD( addr );
pc += 2; pc += 2;
goto set_ixy; goto set_ixy;
@ -1564,7 +1557,7 @@ possibly_out_of_time:
case 0x3E: goto srl_data_addr; // SRL (IXY) case 0x3E: goto srl_data_addr; // SRL (IXY)
CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) 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); int masked = temp & 1 << (data2 >> 3 & 7);
flags = (flags & C01) | H10 | flags = (flags & C01) | H10 |
(masked & S80) | (masked & S80) |
@ -1666,7 +1659,7 @@ possibly_out_of_time:
goto loop; goto loop;
case 0xE3:{// EX (SP),IXY case 0xE3:{// EX (SP),IXY
fuint16 temp = READ_WORD( sp ); uint16_t temp = READ_WORD( sp );
WRITE_WORD( sp, ixy ); WRITE_WORD( sp, ixy );
ixy = temp; ixy = temp;
goto set_ixy; goto set_ixy;

View file

@ -1,6 +1,6 @@
// Z80 CPU emulator // Z80 CPU emulator
// Game_Music_Emu 0.6.0 // Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
#ifndef KSS_CPU_H #ifndef KSS_CPU_H
#define 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 { class Kss_Cpu {
public: public:
typedef BOOST::uint8_t uint8_t;
// Clear registers and map all pages to unmapped // Clear registers and map all pages to unmapped
void reset( void* unmapped_write, void const* unmapped_read ); 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 set_time( cpu_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; } void adjust_time( int delta ) { state->time += delta; }
typedef BOOST::uint16_t uint16_t;
#if BLARGG_BIG_ENDIAN #if BLARGG_BIG_ENDIAN
struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; struct regs_t { uint8_t b, c, d, e, h, l, flags, a; };
#else #else
@ -104,12 +100,12 @@ public:
#define KSS_CPU_PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) #define KSS_CPU_PAGE_OFFSET( addr ) ((addr) & (page_size - 1))
#endif #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 ); 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 ); return state->read [addr >> page_shift] + KSS_CPU_PAGE_OFFSET( addr );
} }

View file

@ -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" #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 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 }; 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 // Setup

View file

@ -1,6 +1,6 @@
// MSX computer KSS music file emulator // 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 #ifndef KSS_EMU_H
#define KSS_EMU_H #define KSS_EMU_H

View file

@ -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" #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); 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 ) if ( index == osc_count - 1 )
wave -= wave_size; // last two oscs share wave wave -= wave_size; // last two oscs share wave
{ {

View file

@ -1,6 +1,6 @@
// Konami SCC sound chip emulator // 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 #ifndef KSS_SCC_APU_H
#define KSS_SCC_APU_H #define KSS_SCC_APU_H

View file

@ -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 "M3u_Playlist.h"
#include "Music_Emu.h" #include "Music_Emu.h"
@ -407,7 +407,7 @@ blargg_err_t M3u_Playlist::parse()
blargg_err_t M3u_Playlist::load( Data_Reader& in ) blargg_err_t M3u_Playlist::load( Data_Reader& in )
{ {
RETURN_ERR( data.resize( in.remain() + 1 ) ); 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(); return parse();
} }

View file

@ -1,6 +1,6 @@
// M3U playlist file parser, with support for subtrack information // 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 #ifndef M3U_PLAYLIST_H
#define M3U_PLAYLIST_H #define M3U_PLAYLIST_H
@ -43,7 +43,7 @@ public:
int repeat; // count int repeat; // count
}; };
entry_t const& operator [] ( int i ) const { return entries [i]; } 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(); void clear();

View file

@ -81,7 +81,7 @@ void Stereo_Buffer::clock_rate( long rate )
void Stereo_Buffer::bass_freq( int bass ) 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 ); bufs [i].bass_freq( bass );
} }
@ -96,7 +96,7 @@ void Stereo_Buffer::clear()
void Stereo_Buffer::end_frame( blip_time_t clock_count ) void Stereo_Buffer::end_frame( blip_time_t clock_count )
{ {
stereo_added = 0; 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; stereo_added |= bufs [i].clear_modified() << i;
bufs [i].end_frame( clock_count ); 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 ); int c = BLIP_READER_READ( center );
blargg_long l = c + BLIP_READER_READ( left ); blargg_long l = c + BLIP_READER_READ( left );
blargg_long r = c + BLIP_READER_READ( right ); blargg_long r = c + BLIP_READER_READ( right );
if ( (BOOST::int16_t) l != l ) if ( (int16_t) l != l )
l = 0x7FFF - (l >> 24); l = 0x7FFF - (l >> 24);
BLIP_READER_NEXT( center, bass ); BLIP_READER_NEXT( center, bass );
if ( (BOOST::int16_t) r != r ) if ( (int16_t) r != r )
r = 0x7FFF - (r >> 24); r = 0x7FFF - (r >> 24);
BLIP_READER_NEXT( left, bass ); BLIP_READER_NEXT( left, bass );
BLIP_READER_NEXT( right, bass ); BLIP_READER_NEXT( right, bass );
out [0] = (blip_sample_t)l; out [0] = l;
out [1] = (blip_sample_t)r; out [1] = r;
out += 2; out += 2;
} }
@ -191,18 +191,18 @@ void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, blargg_long count
for ( ; count; --count ) for ( ; count; --count )
{ {
blargg_long l = BLIP_READER_READ( left ); blargg_long l = BLIP_READER_READ( left );
if ( (BOOST::int16_t) l != l ) if ( (int16_t) l != l )
l = 0x7FFF - (l >> 24); l = 0x7FFF - (l >> 24);
blargg_long r = BLIP_READER_READ( right ); blargg_long r = BLIP_READER_READ( right );
if ( (BOOST::int16_t) r != r ) if ( (int16_t) r != r )
r = 0x7FFF - (r >> 24); r = 0x7FFF - (r >> 24);
BLIP_READER_NEXT( left, bass ); BLIP_READER_NEXT( left, bass );
BLIP_READER_NEXT( right, bass ); BLIP_READER_NEXT( right, bass );
out [0] = (blip_sample_t)l; out [0] = l;
out [1] = (blip_sample_t)r; out [1] = r;
out += 2; out += 2;
} }
@ -219,12 +219,12 @@ void Stereo_Buffer::mix_mono( blip_sample_t* out_, blargg_long count )
for ( ; count; --count ) for ( ; count; --count )
{ {
blargg_long s = BLIP_READER_READ( center ); blargg_long s = BLIP_READER_READ( center );
if ( (BOOST::int16_t) s != s ) if ( (int16_t) s != s )
s = 0x7FFF - (s >> 24); s = 0x7FFF - (s >> 24);
BLIP_READER_NEXT( center, bass ); BLIP_READER_NEXT( center, bass );
out [0] = (blip_sample_t)s; out [0] = s;
out [1] = (blip_sample_t)s; out [1] = s;
out += 2; out += 2;
} }

View file

@ -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" #include "Music_Emu.h"
@ -18,7 +18,6 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h" #include "blargg_source.h"
int const stereo = 2; // number of channels for stereo
int const silence_max = 6; // seconds int const silence_max = 6; // seconds
int const silence_threshold = 0x10; int const silence_threshold = 0x10;
long const fade_block_size = 512; long const fade_block_size = 512;
@ -52,7 +51,7 @@ void Music_Emu::unload()
Music_Emu::Music_Emu() Music_Emu::Music_Emu()
{ {
effects_buffer = 0; effects_buffer = 0;
multi_channel_ = false;
sample_rate_ = 0; sample_rate_ = 0;
mute_mask_ = 0; mute_mask_ = 0;
tempo_ = 1.0; tempo_ = 1.0;
@ -96,6 +95,25 @@ void Music_Emu::set_equalizer( equalizer_t const& eq )
set_equalizer_( 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 ) void Music_Emu::mute_voice( int index, bool mute )
{ {
require( (unsigned) index < (unsigned) voice_count() ); require( (unsigned) index < (unsigned) voice_count() );
@ -145,7 +163,7 @@ blargg_err_t Music_Emu::start_track( int track )
if ( !ignore_silence_ ) if ( !ignore_silence_ )
{ {
// play until non-silence or end of track // 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(); fill_buf();
if ( buf_remain | (int) emu_track_ended_ ) 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; blargg_long sec = msec / 1000;
msec -= sec * 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 long Music_Emu::tell_samples() const
@ -185,7 +203,7 @@ long Music_Emu::tell_samples() const
long Music_Emu::tell() 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; blargg_long sec = out_time / rate;
return sec * 1000 + (out_time - sec * rate) * 1000 / 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 ) 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 ); fade_start = msec_to_samples( start_msec );
} }
@ -345,7 +363,7 @@ blargg_err_t Music_Emu::play( long out_count, sample_t* out )
else else
{ {
require( current_track() >= 0 ); require( current_track() >= 0 );
require( out_count % stereo == 0 ); require( out_count % out_channels() == 0 );
assert( emu_time >= out_time ); 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 // 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; 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_buf();
// fill with silence // 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 ); memset( out, 0, pos * sizeof *out );
silence_count -= pos; 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; track_ended_ = emu_track_ended_ = true;
silence_count = 0; 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 ); handle_fade( out_count, out );
} }
out_time += out_count; out_time += out_count;

View file

@ -1,6 +1,6 @@
// Common interface to game music file emulators // 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 #ifndef MUSIC_EMU_H
#define MUSIC_EMU_H #define MUSIC_EMU_H
@ -14,6 +14,11 @@ public:
// Set output sample rate. Must be called only once before loading file. // Set output sample rate. Must be called only once before loading file.
blargg_err_t set_sample_rate( long sample_rate ); 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. // Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t start_track( int ); blargg_err_t start_track( int );
@ -36,6 +41,8 @@ public:
// Names of voices // Names of voices
const char** voice_names() const; const char** voice_names() const;
bool multi_channel() const;
// Track status/control // Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track // Number of milliseconds (1000 msec = 1 second) played since beginning of track
@ -127,6 +134,7 @@ protected:
double gain() const { return gain_; } double gain() const { return gain_; }
double tempo() const { return tempo_; } double tempo() const { return tempo_; }
void remute_voices(); void remute_voices();
blargg_err_t set_multi_channel_( bool is_enabled );
virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0; virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0;
virtual void set_equalizer_( equalizer_t const& ) { } virtual void set_equalizer_( equalizer_t const& ) { }
@ -149,6 +157,10 @@ private:
int mute_mask_; int mute_mask_;
double tempo_; double tempo_;
double gain_; 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_; long sample_rate_;
blargg_long msec_to_samples( blargg_long msec ) const; blargg_long msec_to_samples( blargg_long msec ) const;
@ -179,7 +191,7 @@ private:
void emu_play( long count, sample_t* out ); void emu_play( long count, sample_t* out );
Multi_Buffer* effects_buffer; 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 ); friend void gme_set_stereo_depth( Music_Emu*, double );
}; };

View file

@ -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" #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 GET_SP() ((sp - 1) & 0xFF)
#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) #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 ) bool Nes_Cpu::run( nes_time_t end_time )
{ {
set_end_time( end_time ); set_end_time( end_time );
state_t s = this->state_; state_t s = this->state_;
this->state = &s; this->state = &s;
// even on x86, using s.time in place of s_time was slower // 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 // registers
fuint16 pc = r.pc; uint16_t pc = r.pc;
fuint8 a = r.a; uint8_t a = r.a;
fuint8 x = r.x; uint8_t x = r.x;
fuint8 y = r.y; uint8_t y = r.y;
fuint16 sp; uint16_t sp;
SET_SP( r.sp ); SET_SP( r.sp );
// status flags // status flags
@ -152,11 +147,11 @@ bool Nes_Cpu::run( nes_time_t end_time )
nz |= ~in & st_z;\ nz |= ~in & st_z;\
} while ( 0 ) } while ( 0 )
fuint8 status; uint8_t status;
fuint16 c; // carry set if (c & 0x100) != 0 uint16_t c; // carry set if (c & 0x100) != 0
fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 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 ); SET_STATUS( temp );
} }
@ -173,7 +168,7 @@ loop:
check( -32768 <= s_time && s_time < 32767 ); check( -32768 <= s_time && s_time < 32767 );
uint8_t const* instr = s.code_map [pc >> page_bits]; uint8_t const* instr = s.code_map [pc >> page_bits];
fuint8 opcode; uint8_t opcode;
// TODO: eliminate this special case // TODO: eliminate this special case
#if BLARGG_NONPORTABLE #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 3,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
}; // 0x00 was 7 and 0xF2 was 2 }; // 0x00 was 7 and 0xF2 was 2
fuint16 data; uint16_t data;
#if !BLARGG_CPU_X86 #if !BLARGG_CPU_X86
if ( s_time >= 0 ) 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 INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
#define IND_Y( cross, out ) {\ #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) );\ out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\
cross( temp );\ cross( temp );\
} }
#define IND_X( out ) {\ #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) );\ 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 // TODO: more efficient way to handle negative branch that wraps PC around
#define BRANCH( cond )\ #define BRANCH( cond )\
{\ {\
fint16 offset = (BOOST::int8_t) data;\ int16_t offset = (int8_t) data;\
fuint16 extra_clock = (++pc & 0xFF) + offset;\ uint16_t extra_clock = (++pc & 0xFF) + offset;\
if ( !(cond) ) goto dec_clock_loop;\ if ( !(cond) ) goto dec_clock_loop;\
pc = BOOST::uint16_t (pc + offset);\ pc = uint16_t (pc + offset);\
s_time += extra_clock >> 8 & 1;\ s_time += extra_clock >> 8 & 1;\
goto loop;\ goto loop;\
} }
@ -312,7 +307,7 @@ imm##op:
BRANCH( (uint8_t) nz ); BRANCH( (uint8_t) nz );
case 0x20: { // JSR case 0x20: { // JSR
fuint16 temp = pc + 1; uint16_t temp = pc + 1;
pc = GET_ADDR(); pc = GET_ADDR();
WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
sp = (sp - 2) | 0x100; sp = (sp - 2) | 0x100;
@ -378,7 +373,7 @@ imm##op:
goto loop; goto loop;
{ {
fuint16 addr; uint16_t addr;
case 0x99: // STA abs,Y case 0x99: // STA abs,Y
addr = y + GET_ADDR(); addr = y + GET_ADDR();
@ -434,7 +429,7 @@ imm##op:
// common read instructions // common read instructions
{ {
fuint16 addr; uint16_t addr;
case 0xA1: // LDA (ind,X) case 0xA1: // LDA (ind,X)
IND_X( addr ) IND_X( addr )
@ -550,7 +545,7 @@ imm##op:
} }
{ {
fuint8 temp; uint8_t temp;
case 0x8C: // STY abs case 0x8C: // STY abs
temp = y; temp = y;
goto store_abs; goto store_abs;
@ -659,8 +654,8 @@ imm##op:
ARITH_ADDR_MODES( 0x65 ) // ADC ARITH_ADDR_MODES( 0x65 ) // ADC
adc_imm: { adc_imm: {
fint16 carry = c >> 8 & 1; int16_t carry = c >> 8 & 1;
fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend int16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend
status &= ~st_v; status &= ~st_v;
status |= ov >> 2 & 0x40; status |= ov >> 2 & 0x40;
c = nz = a + data + carry; c = nz = a + data + carry;
@ -688,7 +683,7 @@ imm##op:
case 0x2A: { // ROL A case 0x2A: { // ROL A
nz = a << 1; nz = a << 1;
fint16 temp = c >> 8 & 1; int16_t temp = c >> 8 & 1;
c = nz; c = nz;
nz |= temp; nz |= temp;
a = (uint8_t) nz; a = (uint8_t) nz;
@ -780,7 +775,7 @@ imm##op:
case 0xD6: // DEC zp,x case 0xD6: // DEC zp,x
data = uint8_t (data + x); data = uint8_t (data + x);
case 0xC6: // DEC zp case 0xC6: // DEC zp
nz = (unsigned) -1; nz = (uint16_t) -1;
add_nz_zp: add_nz_zp:
nz += READ_LOW( data ); nz += READ_LOW( data );
write_nz_zp: write_nz_zp:
@ -805,7 +800,7 @@ imm##op:
case 0xCE: // DEC abs case 0xCE: // DEC abs
data = GET_ADDR(); data = GET_ADDR();
dec_ptr: dec_ptr:
nz = (unsigned) -1; nz = (uint16_t) -1;
inc_common: inc_common:
FLUSH_TIME(); FLUSH_TIME();
nz += READ( data ); nz += READ( data );
@ -846,7 +841,7 @@ imm##op:
goto loop; goto loop;
case 0x40:{// RTI 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 - 0xFF) );
pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
sp = (sp - 0xFD) | 0x100; sp = (sp - 0xFD) | 0x100;
@ -863,9 +858,9 @@ imm##op:
} }
case 0x28:{// PLP case 0x28:{// PLP
fuint8 temp = READ_LOW( sp ); uint8_t temp = READ_LOW( sp );
sp = (sp - 0xFF) | 0x100; sp = (sp - 0xFF) | 0x100;
fuint8 changed = status ^ temp; uint8_t changed = status ^ temp;
SET_STATUS( temp ); SET_STATUS( temp );
if ( !(changed & st_i) ) if ( !(changed & st_i) )
goto loop; // I flag didn't change goto loop; // I flag didn't change
@ -875,7 +870,7 @@ imm##op:
} }
case 0x08: { // PHP case 0x08: { // PHP
fuint8 temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
PUSH( temp | (st_b | st_r) ); PUSH( temp | (st_b | st_r) );
goto loop; goto loop;
@ -897,7 +892,7 @@ imm##op:
// Flags // Flags
case 0x38: // SEC case 0x38: // SEC
c = (unsigned) ~0; c = (uint16_t) ~0;
goto loop; goto loop;
case 0x18: // CLC case 0x18: // CLC
@ -983,12 +978,6 @@ imm##op:
case bad_opcode: // HLT case bad_opcode: // HLT
pc--; 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 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2:
goto stop; goto stop;
@ -1003,8 +992,8 @@ imm##op:
static unsigned char const illop_lens [8] = { static unsigned char const illop_lens [8] = {
0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0 0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0
}; };
fuint8 opcode = instr [-1]; uint8_t opcode = instr [-1];
fint16 len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3; int16_t len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3;
if ( opcode == 0x9C ) if ( opcode == 0x9C )
len = 2; len = 2;
pc += len; pc += len;
@ -1035,7 +1024,7 @@ interrupt:
pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ );
sp = (sp - 3) | 0x100; sp = (sp - 3) | 0x100;
fuint8 temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
temp |= st_r; temp |= st_r;
if ( result_ ) if ( result_ )
@ -1071,7 +1060,7 @@ stop:
r.y = y; r.y = y;
{ {
fuint8 temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
r.status = temp; r.status = temp;
} }

View file

@ -1,6 +1,6 @@
// NES 6502 CPU emulator // NES 6502 CPU emulator
// Game_Music_Emu 0.6.0 // Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
#ifndef NES_CPU_H #ifndef NES_CPU_H
#define NES_CPU_H #define NES_CPU_H
@ -12,8 +12,6 @@ enum { future_nes_time = INT_MAX / 2 + 1 };
class Nes_Cpu { class Nes_Cpu {
public: public:
typedef BOOST::uint8_t uint8_t;
// Clear registers, map low memory and its three mirrors to address 0, // Clear registers, map low memory and its three mirrors to address 0,
// and mirror unmapped_page in remaining memory // and mirror unmapped_page in remaining memory
void reset( void const* unmapped_page = 0 ); void reset( void const* unmapped_page = 0 );
@ -32,12 +30,12 @@ public:
// NES 6502 registers. Not kept updated during a call to run(). // NES 6502 registers. Not kept updated during a call to run().
struct registers_t { struct registers_t {
BOOST::uint16_t pc; uint16_t pc;
BOOST::uint8_t a; uint8_t a;
BOOST::uint8_t x; uint8_t x;
BOOST::uint8_t y; uint8_t y;
BOOST::uint8_t status; uint8_t status;
BOOST::uint8_t sp; uint8_t sp;
}; };
registers_t r; registers_t r;
@ -84,7 +82,7 @@ private:
inline int update_end_time( nes_time_t end, nes_time_t irq ); 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 return state->code_map [addr >> page_bits] + addr
#if !BLARGG_NONPORTABLE #if !BLARGG_NONPORTABLE

View file

@ -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" #include "Nes_Fme7_Apu.h"

View file

@ -1,6 +1,6 @@
// Sunsoft FME-7 sound emulator // 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 #ifndef NES_FME7_APU_H
#define NES_FME7_APU_H #define NES_FME7_APU_H
@ -10,10 +10,10 @@
struct fme7_apu_state_t struct fme7_apu_state_t
{ {
enum { reg_count = 14 }; enum { reg_count = 14 };
BOOST::uint8_t regs [reg_count]; uint8_t regs [reg_count];
BOOST::uint8_t phases [3]; // 0 or 1 uint8_t phases [3]; // 0 or 1
BOOST::uint8_t latch; uint8_t latch;
BOOST::uint16_t delays [3]; // a, b, c uint16_t delays [3]; // a, b, c
}; };
class Nes_Fme7_Apu : private fme7_apu_state_t { class Nes_Fme7_Apu : private fme7_apu_state_t {

View file

@ -90,7 +90,7 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time )
osc.delay = 0; osc.delay = 0;
if ( time < end_time ) if ( time < end_time )
{ {
const BOOST::uint8_t* osc_reg = &reg [i * 8 + 0x40]; const uint8_t* osc_reg = &reg [i * 8 + 0x40];
if ( !(osc_reg [4] & 0xE0) ) if ( !(osc_reg [4] & 0xE0) )
continue; continue;

View file

@ -54,24 +54,24 @@ private:
int addr_reg; int addr_reg;
enum { reg_count = 0x80 }; enum { reg_count = 0x80 };
BOOST::uint8_t reg [reg_count]; uint8_t reg [reg_count];
Blip_Synth<blip_good_quality,15> synth; Blip_Synth<blip_good_quality,15> synth;
BOOST::uint8_t& access(); uint8_t& access();
void run_until( blip_time_t ); void run_until( blip_time_t );
}; };
/* /*
struct namco_state_t struct namco_state_t
{ {
BOOST::uint8_t regs [0x80]; uint8_t regs [0x80];
BOOST::uint8_t addr; uint8_t addr;
BOOST::uint8_t unused; uint8_t unused;
BOOST::uint8_t positions [8]; uint8_t positions [8];
BOOST::uint32_t delays [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; int addr = addr_reg & 0x7F;
if ( addr_reg & 0x80 ) if ( addr_reg & 0x80 )

View file

@ -40,7 +40,7 @@ private:
struct Vrc6_Osc struct Vrc6_Osc
{ {
BOOST::uint8_t regs [3]; uint8_t regs [3];
Blip_Buffer* output; Blip_Buffer* output;
int delay; int delay;
int last_amp; int last_amp;
@ -66,11 +66,11 @@ private:
struct vrc6_apu_state_t struct vrc6_apu_state_t
{ {
BOOST::uint8_t regs [3] [3]; uint8_t regs [3] [3];
BOOST::uint8_t saw_amp; uint8_t saw_amp;
BOOST::uint16_t delays [3]; uint16_t delays [3];
BOOST::uint8_t phases [3]; uint8_t phases [3];
BOOST::uint8_t unused; uint8_t unused;
}; };
inline void Nes_Vrc6_Apu::osc_output( int i, Blip_Buffer* buf ) inline void Nes_Vrc6_Apu::osc_output( int i, Blip_Buffer* buf )

View file

@ -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" #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 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 }; 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 // Setup

View file

@ -1,6 +1,6 @@
// Nintendo NES/Famicom NSF music file emulator // 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 #ifndef NSF_EMU_H
#define NSF_EMU_H #define NSF_EMU_H

View file

@ -1,6 +1,4 @@
// Game_Music_Emu 0.6.0. http://www.slack.net/~ant/ // Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
#define _CRT_SECURE_NO_WARNINGS
#include "Nsfe_Emu.h" #include "Nsfe_Emu.h"
@ -37,7 +35,7 @@ inline void Nsfe_Info::unload()
void Nsfe_Info::disable_playlist( bool b ) void Nsfe_Info::disable_playlist( bool b )
{ {
playlist_disabled = b; playlist_disabled = b;
info.track_count = (byte)playlist.size(); info.track_count = playlist.size();
if ( !info.track_count || playlist_disabled ) if ( !info.track_count || playlist_disabled )
info.track_count = actual_track_count_; info.track_count = actual_track_count_;
} }
@ -137,6 +135,9 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
blargg_long size = get_le32( block_header [0] ); blargg_long size = get_le32( block_header [0] );
blargg_long tag = get_le32( block_header [1] ); 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) ); //debug_printf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) );
switch ( tag ) switch ( tag )
@ -173,7 +174,7 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
blargg_vector<char> chars; blargg_vector<char> chars;
blargg_vector<const char*> strs; blargg_vector<const char*> strs;
RETURN_ERR( read_strs( in, size, chars, strs ) ); RETURN_ERR( read_strs( in, size, chars, strs ) );
int n = (int)strs.size(); int n = strs.size();
if ( n > 3 ) if ( n > 3 )
copy_str( strs [3], info.dumper, sizeof info.dumper ); 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'): case BLARGG_4CHAR('e','m','i','t'):
RETURN_ERR( track_times.resize( size / 4 ) ); 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; break;
case BLARGG_4CHAR('l','b','l','t'): 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 ); int remapped = remap_track( track );
if ( (unsigned) remapped < track_times.size() ) 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 ) if ( length > 0 )
out->length = length; 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 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 }; 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 ) blargg_err_t Nsfe_Emu::load_( Data_Reader& in )

View file

@ -1,6 +1,6 @@
// Nintendo NES/Famicom NSFE music file emulator // 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 #ifndef NSFE_EMU_H
#define NSFE_EMU_H #define NSFE_EMU_H

View file

@ -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" #include "Sap_Apu.h"
@ -30,7 +30,7 @@ static void gen_poly( blargg_ulong mask, int count, byte* out )
{ {
// implemented using "Galios configuration" // implemented using "Galios configuration"
bits |= (n & 1) << b; bits |= (n & 1) << b;
n = (n >> 1) ^ (mask & (blargg_ulong)-(blargg_long)(n & 1)); n = (n >> 1) ^ (mask & -(n & 1));
} }
while ( b++ < 7 ); while ( b++ < 7 );
*out++ = bits; *out++ = bits;

View file

@ -1,6 +1,6 @@
// Atari POKEY sound chip emulator // 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 #ifndef SAP_APU_H
#define SAP_APU_H #define SAP_APU_H

View file

@ -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" #include "Sap_Cpu.h"
@ -68,27 +68,21 @@ void Sap_Cpu::reset( void* new_mem )
#define GET_SP() ((sp - 1) & 0xFF) #define GET_SP() ((sp - 1) & 0xFF)
#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) #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 Sap_Cpu::run( sap_time_t end_time )
{ {
bool illegal_encountered = false; bool illegal_encountered = false;
set_end_time( end_time ); set_end_time( end_time );
state_t s = this->state_; state_t s = this->state_;
this->state = &s; this->state = &s;
fint32 s_time = s.time; int32_t s_time = s.time;
uint8_t* const mem = this->mem; // cache uint8_t* const mem = this->mem; // cache
// registers // registers
fuint16 pc = r.pc; uint16_t pc = r.pc;
fuint8 a = r.a; uint8_t a = r.a;
fuint8 x = r.x; uint8_t x = r.x;
fuint8 y = r.y; uint8_t y = r.y;
fuint16 sp; uint16_t sp;
SET_SP( r.sp ); SET_SP( r.sp );
// status flags // status flags
@ -108,11 +102,11 @@ bool Sap_Cpu::run( sap_time_t end_time )
nz |= ~in & st_z;\ nz |= ~in & st_z;\
} while ( 0 ) } while ( 0 )
fuint8 status; uint8_t status;
fuint16 c; // carry set if (c & 0x100) != 0 uint16_t c; // carry set if (c & 0x100) != 0
fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 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 ); SET_STATUS( temp );
} }
@ -135,7 +129,7 @@ loop:
check( (unsigned) x < 0x100 ); check( (unsigned) x < 0x100 );
check( (unsigned) y < 0x100 ); check( (unsigned) y < 0x100 );
fuint8 opcode = mem [pc]; uint8_t opcode = mem [pc];
pc++; pc++;
uint8_t const* instr = mem + 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 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
}; // 0x00 was 7 }; // 0x00 was 7
fuint16 data; uint16_t data;
data = clock_table [opcode]; data = clock_table [opcode];
if ( (s_time += data) >= 0 ) if ( (s_time += data) >= 0 )
goto possibly_out_of_time; 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 INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop;
#define IND_Y( cross, out ) {\ #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) );\ out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\
cross( temp );\ cross( temp );\
} }
#define IND_X( out ) {\ #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) );\ 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 // TODO: more efficient way to handle negative branch that wraps PC around
#define BRANCH( cond )\ #define BRANCH( cond )\
{\ {\
fint16 offset = (BOOST::int8_t) data;\ int16_t offset = (int8_t) data;\
fuint16 extra_clock = (++pc & 0xFF) + offset;\ uint16_t extra_clock = (++pc & 0xFF) + offset;\
if ( !(cond) ) goto dec_clock_loop;\ if ( !(cond) ) goto dec_clock_loop;\
pc += offset;\ pc += offset;\
s_time += extra_clock >> 8 & 1;\ s_time += extra_clock >> 8 & 1;\
@ -256,7 +250,7 @@ imm##op:
BRANCH( (uint8_t) nz ); BRANCH( (uint8_t) nz );
case 0x20: { // JSR case 0x20: { // JSR
fuint16 temp = pc + 1; uint16_t temp = pc + 1;
pc = GET_ADDR(); pc = GET_ADDR();
WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); WRITE_LOW( 0x100 | (sp - 1), temp >> 8 );
sp = (sp - 2) | 0x100; sp = (sp - 2) | 0x100;
@ -322,7 +316,7 @@ imm##op:
goto loop; goto loop;
{ {
fuint16 addr; uint16_t addr;
case 0x99: // STA abs,Y case 0x99: // STA abs,Y
addr = y + GET_ADDR(); addr = y + GET_ADDR();
@ -378,7 +372,7 @@ imm##op:
// common read instructions // common read instructions
{ {
fuint16 addr; uint16_t addr;
case 0xA1: // LDA (ind,X) case 0xA1: // LDA (ind,X)
IND_X( addr ) IND_X( addr )
@ -494,7 +488,7 @@ imm##op:
} }
{ {
fuint8 temp; uint8_t temp;
case 0x8C: // STY abs case 0x8C: // STY abs
temp = y; temp = y;
goto store_abs; goto store_abs;
@ -604,8 +598,8 @@ imm##op:
ARITH_ADDR_MODES( 0x65 ) // ADC ARITH_ADDR_MODES( 0x65 ) // ADC
adc_imm: { adc_imm: {
check( !(status & st_d) ); check( !(status & st_d) );
fint16 carry = c >> 8 & 1; int16_t carry = c >> 8 & 1;
fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend int16_t ov = (a ^ 0x80) + carry + (int8_t) data; // sign-extend
status &= ~st_v; status &= ~st_v;
status |= ov >> 2 & 0x40; status |= ov >> 2 & 0x40;
c = nz = a + data + carry; c = nz = a + data + carry;
@ -633,7 +627,7 @@ imm##op:
case 0x2A: { // ROL A case 0x2A: { // ROL A
nz = a << 1; nz = a << 1;
fint16 temp = c >> 8 & 1; int16_t temp = c >> 8 & 1;
c = nz; c = nz;
nz |= temp; nz |= temp;
a = (uint8_t) nz; a = (uint8_t) nz;
@ -725,7 +719,7 @@ imm##op:
case 0xD6: // DEC zp,x case 0xD6: // DEC zp,x
data = uint8_t (data + x); data = uint8_t (data + x);
case 0xC6: // DEC zp case 0xC6: // DEC zp
nz = (unsigned) -1; nz = (uint16_t) -1;
add_nz_zp: add_nz_zp:
nz += READ_LOW( data ); nz += READ_LOW( data );
write_nz_zp: write_nz_zp:
@ -750,7 +744,7 @@ imm##op:
case 0xCE: // DEC abs case 0xCE: // DEC abs
data = GET_ADDR(); data = GET_ADDR();
dec_ptr: dec_ptr:
nz = (unsigned) -1; nz = (uint16_t) -1;
inc_common: inc_common:
FLUSH_TIME(); FLUSH_TIME();
nz += READ( data ); nz += READ( data );
@ -791,7 +785,7 @@ imm##op:
goto loop; goto loop;
case 0x40:{// RTI 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 - 0xFF) );
pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100;
sp = (sp - 0xFD) | 0x100; sp = (sp - 0xFD) | 0x100;
@ -811,9 +805,9 @@ imm##op:
} }
case 0x28:{// PLP case 0x28:{// PLP
fuint8 temp = READ_LOW( sp ); uint8_t temp = READ_LOW( sp );
sp = (sp - 0xFF) | 0x100; sp = (sp - 0xFF) | 0x100;
fuint8 changed = status ^ temp; uint8_t changed = status ^ temp;
SET_STATUS( temp ); SET_STATUS( temp );
if ( !(changed & st_i) ) if ( !(changed & st_i) )
goto loop; // I flag didn't change goto loop; // I flag didn't change
@ -823,7 +817,7 @@ imm##op:
} }
case 0x08: { // PHP case 0x08: { // PHP
fuint8 temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
PUSH( temp | (st_b | st_r) ); PUSH( temp | (st_b | st_r) );
goto loop; goto loop;
@ -843,7 +837,7 @@ imm##op:
// Flags // Flags
case 0x38: // SEC case 0x38: // SEC
c = (unsigned) ~0; c = (uint16_t) ~0;
goto loop; goto loop;
case 0x18: // CLC case 0x18: // CLC
@ -932,7 +926,6 @@ imm##op:
//case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2: //case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2:
default: default:
assert( (unsigned) opcode <= 0xFF );
illegal_encountered = true; illegal_encountered = true;
pc--; pc--;
goto stop; goto stop;
@ -956,7 +949,7 @@ interrupt:
pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ );
sp = (sp - 3) | 0x100; sp = (sp - 3) | 0x100;
fuint8 temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
temp |= st_r; temp |= st_r;
if ( result_ ) if ( result_ )
@ -998,7 +991,7 @@ stop:
r.y = y; r.y = y;
{ {
fuint8 temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
r.status = temp; r.status = temp;
} }

View file

@ -1,6 +1,6 @@
// Atari 6502 CPU emulator // Atari 6502 CPU emulator
// Game_Music_Emu 0.6.0 // Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
#ifndef SAP_CPU_H #ifndef SAP_CPU_H
#define SAP_CPU_H #define SAP_CPU_H
@ -12,8 +12,6 @@ enum { future_sap_time = INT_MAX / 2 + 1 };
class Sap_Cpu { class Sap_Cpu {
public: public:
typedef BOOST::uint8_t uint8_t;
// Clear all registers and keep pointer to 64K memory passed in // Clear all registers and keep pointer to 64K memory passed in
void reset( void* mem_64k ); void reset( void* mem_64k );
@ -23,12 +21,12 @@ public:
// Registers are not updated until run() returns (except I flag in status) // Registers are not updated until run() returns (except I flag in status)
struct registers_t { struct registers_t {
BOOST::uint16_t pc; uint16_t pc;
BOOST::uint8_t a; uint8_t a;
BOOST::uint8_t x; uint8_t x;
BOOST::uint8_t y; uint8_t y;
BOOST::uint8_t status; uint8_t status;
BOOST::uint8_t sp; uint8_t sp;
}; };
registers_t r; registers_t r;

View file

@ -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" #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; char const* tag = (char const*) in;
while ( in < line_end && *in > ' ' ) while ( in < line_end && *in > ' ' )
in++; in++;
int tag_len = int((char const*) in - tag); int tag_len = (char const*) in - tag;
while ( in < line_end && *in <= ' ' ) in++; 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 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 }; 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 // Setup
@ -256,7 +255,7 @@ blargg_err_t Sap_Emu::load_mem_( byte const* in, long size )
set_warning( info.warning ); set_warning( info.warning );
set_track_count( info.track_count ); 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() ); apu_impl.volume( gain() );
return setup_buffer( 1773447 ); return setup_buffer( 1773447 );
@ -315,8 +314,8 @@ inline void Sap_Emu::call_init( int track )
case 'C': case 'C':
r.a = 0x70; r.a = 0x70;
r.x = (BOOST::uint8_t)(info.music_addr&0xFF); r.x = info.music_addr&0xFF;
r.y = (BOOST::uint8_t)(info.music_addr >> 8); r.y = info.music_addr >> 8;
run_routine( info.play_addr + 3 ); run_routine( info.play_addr + 3 );
r.a = 0; r.a = 0;
r.x = track; r.x = track;

View file

@ -1,6 +1,6 @@
// Atari XL/XE SAP music file emulator // 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 #ifndef SAP_EMU_H
#define SAP_EMU_H #define SAP_EMU_H
@ -54,8 +54,7 @@ private:
// large items // large items
struct { struct {
byte padding1 [0x100]; byte padding1 [0x100];
byte ram [0x10000]; byte ram [0x10000 + 0x100];
byte padding2 [0x100];
} mem; } mem;
Sap_Apu_Impl apu_impl; Sap_Apu_Impl apu_impl;

View file

@ -141,7 +141,7 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time )
do do
{ {
int changed = shifter + 1; 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 if ( changed & 2 ) // true if bits 0 and 1 differ
{ {
delta = -delta; delta = -delta;

View file

@ -1,6 +1,6 @@
// SPC emulation support: init, sample buffering, reset, SPC loading // 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" #include "Snes_Spc.h"
@ -144,7 +144,7 @@ void Snes_Spc::ram_loaded()
// Put STOP instruction around memory to catch PC underflow/overflow // Put STOP instruction around memory to catch PC underflow/overflow
memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); 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.ram + 0x10000, cpu_pad_fill, sizeof m.ram.padding1 );
} }
// Registers were just loaded. Applies these new values. // 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 ); assert( out <= out_end );
} }
dsp.set_output( out, int(out_end - out) ); dsp.set_output( out, out_end - out );
} }
else else
{ {

View file

@ -1,16 +1,16 @@
// SNES SPC-700 APU emulator // 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 #ifndef SNES_SPC_H
#define SNES_SPC_H #define SNES_SPC_H
#include "Spc_Dsp.h" #include "Spc_Dsp.h"
#include "blargg_endian.h" #include "blargg_endian.h"
#include <stdint.h>
struct Snes_Spc { struct Snes_Spc {
public: public:
typedef BOOST::uint8_t uint8_t;
// Must be called once before using // Must be called once before using
blargg_err_t init(); blargg_err_t init();
@ -108,12 +108,12 @@ public:
// TODO: document // TODO: document
struct regs_t struct regs_t
{ {
int pc; uint16_t pc;
int a; uint8_t a;
int x; uint8_t x;
int y; uint8_t y;
int psw; uint8_t psw;
int sp; uint8_t sp;
}; };
regs_t& smp_regs() { return m.cpu_regs; } regs_t& smp_regs() { return m.cpu_regs; }
@ -123,8 +123,6 @@ public:
public: public:
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
typedef BOOST::uint16_t uint16_t;
// Time relative to m_spc_time. Speeds up code a bit by eliminating need to // 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 // 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. // 0 to eliminate reloading end time every instruction. It pays off.
@ -184,13 +182,11 @@ private:
struct struct
{ {
// padding to neutralize address overflow // padding to neutralize address overflow -- but this is
union { // still undefined behavior! TODO: remove and instead properly
// guard usage of emulated memory
uint8_t padding1 [0x100]; uint8_t padding1 [0x100];
uint16_t align; // makes compiler align data for 16-bit access alignas(uint16_t) uint8_t ram [0x10000 + 0x100];
} padding1 [1];
uint8_t ram [0x10000];
uint8_t padding2 [0x100];
} ram; } ram;
}; };
state_t m; state_t m;
@ -226,13 +222,13 @@ private:
Timer* run_timer ( Timer* t, rel_time_t ); Timer* run_timer ( Timer* t, rel_time_t );
int dsp_read ( rel_time_t ); int dsp_read ( rel_time_t );
void dsp_write ( int data, 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, uint16_t addr );
void cpu_write_smp_reg ( int data, rel_time_t, int addr ); void cpu_write_smp_reg ( int data, rel_time_t, uint16_t addr );
void cpu_write_high ( int data, int i, rel_time_t ); void cpu_write_high ( int data, uint8_t i );
void cpu_write ( int data, int addr, rel_time_t ); void cpu_write ( int data, uint16_t addr, rel_time_t );
int cpu_read_smp_reg ( int i, rel_time_t ); int cpu_read_smp_reg ( int i, rel_time_t );
int cpu_read ( int addr, rel_time_t ); int cpu_read ( uint16_t addr, rel_time_t );
unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t ); unsigned CPU_mem_bit ( uint16_t pc, rel_time_t );
bool check_echo_access ( int addr ); bool check_echo_access ( int addr );
uint8_t* run_until_( time_t end_time ); uint8_t* run_until_( time_t end_time );

View file

@ -1,6 +1,6 @@
// Core SPC emulation: CPU, timers, SMP registers, memory // 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" #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 // If write isn't preceded by read, data has this added to it
int const no_read_before_write = 0x2000; 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 ) 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% if ( addr == r_dspdata ) // 99%
dsp_write( data, time ); 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 ); 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 )
{ {
assert ( i < rom_size );
m.hi_ram [i] = (uint8_t) data; m.hi_ram [i] = (uint8_t) data;
if ( m.rom_enabled ) if ( m.rom_enabled )
RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM 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 );
}
}
int const bits_in_int = CHAR_BIT * sizeof (int); void Snes_Spc::cpu_write( int data, uint16_t addr, rel_time_t time )
void Snes_Spc::cpu_write( int data, int addr, rel_time_t time )
{ {
MEM_ACCESS( time, addr ) MEM_ACCESS( time, addr )
// RAM // RAM
RAM [addr] = (uint8_t) data; RAM [addr] = (uint8_t) data;
int reg = addr - 0xF0; if ( addr >= 0xF0 ) // 64%
if ( reg >= 0 ) // 64%
{ {
const uint16_t reg = addr - 0xF0;
// $F0-$FF // $F0-$FF
if ( reg < reg_count ) // 87% 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 ); cpu_write_smp_reg( data, time, reg );
} }
// High mem/address wrap-around // High mem/address wrap-around
else else if ( addr >= rom_addr ) // 1% in IPL ROM area or address wrapped around
{ cpu_write_high( data, addr - rom_addr );
reg -= rom_addr - 0xF0;
if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around
cpu_write_high( data, reg, time );
}
} }
} }
@ -463,7 +449,7 @@ inline int Snes_Spc::cpu_read_smp_reg( int reg, rel_time_t time )
return result; 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 ) 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 // Prefix and suffix for CPU emulator function
#define SPC_CPU_RUN_FUNC \ #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;\ rel_time_t rel_time = m.spc_time - end_time;\
assert( rel_time <= 0 );\ assert( rel_time <= 0 );\
@ -527,7 +513,7 @@ BOOST::uint8_t* Snes_Spc::run_until_( time_t end_time )\
return &REGS [r_cpuio0];\ return &REGS [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 ) void Snes_Spc::end_frame( time_t end_time )
{ {

View file

@ -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 /* 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 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 READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) )
#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data ) #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 SET_PC( n ) (pc = n)
#define GET_PC() (int(pc - ram)) #define GET_PC() (pc)
#define READ_PC( pc ) (*(pc)) #define READ_PC( pc ) (ram [pc])
#define READ_PC16( pc ) GET_LE16( pc ) #define READ_PC16( pc ) READ_PROG16( pc )
// TODO: remove non-wrapping versions? #define SET_SP( v ) (sp = v)
#define SPC_NO_SP_WRAPAROUND 0 #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 )\ #define PUSH16( data )\
{\ {\
int addr = int((sp -= 2) - ram);\ PUSH( (data & 0xff00) >> 8 );\
if ( addr > 0x100 )\ PUSH( data & 0xff );\
{\
SET_LE16( sp, data );\
}\
else\
{\
ram [(uint8_t) addr + 0x100] = (uint8_t) data;\
sp [1] = (uint8_t) (data >> 8);\
sp += 0x100;\
}\
} }
#define PUSH( data )\ #define PUSH( data )\
{\ {\
*--sp = (uint8_t) (data);\ ram [0x100 + sp] = (uint8_t) (data);\
if ( sp - ram == 0x100 )\ --sp;\
sp += 0x100;\
} }
#define POP( out )\ #define POP( out )\
{\ {\
out = *sp++;\ ++sp;\
if ( sp - ram == 0x201 )\ out = ram [0x100 + sp];\
{\
out = sp [-0x101];\
sp -= 0x100;\
}\
} }
#endif
#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) #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 addr = READ_PC16( pc );
unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); 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 SPC_CPU_RUN_FUNC
{ {
uint8_t* const ram = RAM; uint8_t* const ram = RAM;
int a = m.cpu_regs.a; uint8_t a = m.cpu_regs.a;
int x = m.cpu_regs.x; uint8_t x = m.cpu_regs.x;
int y = m.cpu_regs.y; uint8_t y = m.cpu_regs.y;
uint8_t const* pc; uint16_t pc;
uint8_t* sp; uint8_t sp;
int psw; int psw;
int c; int c;
int nz; int nz;
@ -183,7 +158,7 @@ SPC_CPU_RUN_FUNC
// Main loop // Main loop
cbranch_taken_loop: cbranch_taken_loop:
pc += *(BOOST::int8_t const*) pc; pc += (int8_t) ram [pc];
inc_pc_loop: inc_pc_loop:
pc++; pc++;
loop: loop:
@ -195,7 +170,7 @@ loop:
check( (unsigned) x < 0x100 ); check( (unsigned) x < 0x100 );
check( (unsigned) y < 0x100 ); check( (unsigned) y < 0x100 );
opcode = *pc; opcode = ram [pc];
if ( (rel_time += m.cycle_table [opcode]) > 0 ) if ( (rel_time += m.cycle_table [opcode]) > 0 )
goto out_of_time; goto out_of_time;
@ -218,7 +193,8 @@ loop:
*/ */
// TODO: if PC is at end of memory, this will get wrong operand (very obscure) // TODO: if PC is at end of memory, this will get wrong operand (very obscure)
data = *++pc; pc++;
data = ram [pc];
switch ( opcode ) switch ( opcode )
{ {
@ -227,10 +203,10 @@ loop:
#define BRANCH( cond )\ #define BRANCH( cond )\
{\ {\
pc++;\ pc++;\
pc += (BOOST::int8_t) data;\ pc += (int8_t) data;\
if ( cond )\ if ( cond )\
goto loop;\ goto loop;\
pc -= (BOOST::int8_t) data;\ pc -= (int8_t) data;\
rel_time -= 2;\ rel_time -= 2;\
goto loop;\ goto loop;\
} }
@ -249,23 +225,12 @@ loop:
} }
case 0x6F:// RET case 0x6F:// RET
#if SPC_NO_SP_WRAPAROUND
{ {
SET_PC( GET_LE16( sp ) ); uint8_t l, h;
sp += 2; 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; goto loop;
case 0xE4: // MOV a,dp case 0xE4: // MOV a,dp
@ -294,8 +259,7 @@ loop:
REGS [i] = (uint8_t) data; REGS [i] = (uint8_t) data;
// Registers other than $F2 and $F4-$F7 // Registers other than $F2 and $F4-$F7
//if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 ) if ( i != 2 && (i < 4 || i > 7)) // 12%
if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12%
cpu_write_smp_reg( data, rel_time, i ); cpu_write_smp_reg( data, rel_time, i );
} }
} }
@ -504,7 +468,7 @@ loop:
case op + 0x01: /* dp,dp */\ case op + 0x01: /* dp,dp */\
data = READ_DP( -3, data );\ data = READ_DP( -3, data );\
case op + 0x10:{/*dp,imm*/\ case op + 0x10:{/*dp,imm*/\
uint8_t const* addr2 = pc + 1;\ uint16_t addr2 = pc + 1;\
pc += 2;\ pc += 2;\
addr = READ_PC( addr2 ) + dp;\ addr = READ_PC( addr2 ) + dp;\
}\ }\
@ -878,7 +842,7 @@ loop:
// 12. BRANCHING COMMANDS // 12. BRANCHING COMMANDS
case 0x2F: // BRA rel case 0x2F: // BRA rel
pc += (BOOST::int8_t) data; pc += (int8_t) data;
goto inc_pc_loop; goto inc_pc_loop;
case 0x30: // BMI case 0x30: // BMI
@ -1002,10 +966,12 @@ loop:
{ {
int temp; int temp;
uint8_t l, h;
case 0x7F: // RET1 case 0x7F: // RET1
temp = *sp; POP (temp);
SET_PC( GET_LE16( sp + 1 ) ); POP (l);
SET_SP(GET_SP() + 3); POP (h);
SET_PC( l | (h << 8) );
goto set_psw; goto set_psw;
case 0x8E: // POP PSW case 0x8E: // POP PSW
POP( temp ); POP( temp );
@ -1180,11 +1146,8 @@ loop:
case 0xFF:{// STOP case 0xFF:{// STOP
// handle PC wrap-around // handle PC wrap-around
unsigned addr = GET_PC() - 1; if ( pc == 0x0000 )
if ( addr >= 0x10000 )
{ {
addr &= 0xFFFF;
SET_PC( addr );
debug_printf( "SPC: PC wrapped around\n" ); debug_printf( "SPC: PC wrapped around\n" );
goto loop; goto loop;
} }
@ -1201,12 +1164,10 @@ loop:
assert( 0 ); // catch any unhandled instructions assert( 0 ); // catch any unhandled instructions
} }
out_of_time: 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: stop:
// Uncache registers // Uncache registers
if ( GET_PC() >= 0x10000 )
debug_printf( "SPC: PC wrapped around\n" );
m.cpu_regs.pc = (uint16_t) GET_PC(); m.cpu_regs.pc = (uint16_t) GET_PC();
m.cpu_regs.sp = ( uint8_t) GET_SP(); m.cpu_regs.sp = ( uint8_t) GET_SP();
m.cpu_regs.a = ( uint8_t) a; m.cpu_regs.a = ( uint8_t) a;

View file

@ -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" #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 // 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 GET_LE16A( addr ) GET_LE16( addr )
#define SET_LE16A( addr, data ) SET_LE16( addr, data ) #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, 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, 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 // counters start out with this synchronization
m.counters [0] = 1; m.counters [0] = 1;
m.counters [1] = 0; m.counters [1] = 0;
m.counters [2] = -0x20; m.counters [2] = -0x20u;
m.counters [3] = 0x0B; m.counters [3] = 0x0B;
int n = 2; int n = 2;
@ -498,8 +498,9 @@ void Spc_Dsp::run( int clock_count )
// Decode four samples // Decode four samples
for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 )
{ {
// Extract upper nybble and scale appropriately // Extract upper nybble and scale appropriately. Every cast is
int s = ((int16_t) nybbles >> right_shift) << left_shift; // 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) // Apply IIR filter (8 is the most commonly used)
int const filter = brr_header & 0x0C; int const filter = brr_header & 0x0C;

View file

@ -1,6 +1,6 @@
// Fast SNES SPC-700 DSP emulator (about 3x speed of accurate one) // 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 #ifndef SPC_DSP_H
#define SPC_DSP_H #define SPC_DSP_H
@ -8,8 +8,6 @@
struct Spc_Dsp { struct Spc_Dsp {
public: public:
typedef BOOST::uint8_t uint8_t;
// Setup // Setup
// Initializes DSP and has it use the 64K RAM provided // Initializes DSP and has it use the 64K RAM provided
@ -89,9 +87,6 @@ public:
public: public:
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
typedef BOOST::int8_t int8_t;
typedef BOOST::int16_t int16_t;
enum { echo_hist_size = 8 }; enum { echo_hist_size = 8 };
enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
@ -154,7 +149,7 @@ private:
#include <assert.h> #include <assert.h>
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 inline int Spc_Dsp::read( int addr ) const
{ {

View file

@ -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" #include "Spc_Emu.h"
@ -228,14 +228,14 @@ struct Spc_File : Gme_Info_
{ {
RETURN_ERR( xid6.resize( xid6_size ) ); RETURN_ERR( xid6.resize( xid6_size ) );
RETURN_ERR( in.skip( xid6_offset - Spc_Emu::header_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; return 0;
} }
blargg_err_t track_info_( track_info_t* out, int ) const 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; 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 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 }; 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 // Setup
@ -299,6 +299,11 @@ blargg_err_t Spc_Emu::start_track_( int track )
RETURN_ERR( apu.load_spc( file_data, file_size ) ); RETURN_ERR( apu.load_spc( file_data, file_size ) );
filter.set_gain( (int) (gain() * SPC_Filter::gain_unit) ); filter.set_gain( (int) (gain() * SPC_Filter::gain_unit) );
apu.clear_echo(); 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; return 0;
} }

View file

@ -1,6 +1,6 @@
// Super Nintendo SPC music file emulator // 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 #ifndef SPC_EMU_H
#define SPC_EMU_H #define SPC_EMU_H

View file

@ -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" #include "Spc_Filter.h"

View file

@ -1,6 +1,6 @@
// Simple low-pass and high-pass filter to better match sound output of a SNES // 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 #ifndef SPC_FILTER_H
#define SPC_FILTER_H #define SPC_FILTER_H

View file

@ -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" #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 ) static byte const* get_gd3_str( byte const* in, byte const* end, char* field )
{ {
byte const* mid = skip_gd3_str( in, end ); byte const* mid = skip_gd3_str( in, end );
int len = int(mid - in) / 2 - 1; int len = (mid - in) / 2 - 1;
if ( len > 0 ) if ( len > 0 )
{ {
len = min( len, (int) Gme_File::max_field_ ); len = min( len, (int) Gme_File::max_field_ );
@ -108,7 +108,7 @@ byte const* Vgm_Emu::gd3_data( int* size ) const
return 0; return 0;
byte const* gd3 = data + header_size + gd3_offset; 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 ) if ( !gd3_size )
return 0; return 0;
@ -184,7 +184,7 @@ struct Vgm_File : Gme_Info_
if ( gd3_size ) if ( gd3_size )
{ {
RETURN_ERR( gd3.resize( 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; 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 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 }; 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 }; 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 // Setup
@ -233,6 +233,25 @@ blargg_err_t Vgm_Emu::set_sample_rate_( long sample_rate )
return Classic_Emu::set_sample_rate_( 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 ) void Vgm_Emu::update_eq( blip_eq_t const& eq )
{ {
psg.treble_eq( eq ); psg.treble_eq( eq );

View file

@ -1,6 +1,6 @@
// Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator // 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 #ifndef VGM_EMU_H
#define 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() // TODO: move into Music_Emu and rename to something like supports_custom_buffer()
bool is_classic_emu() const { return !uses_fm; } 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 // Disable running FM chips at higher than normal rate. Will result in slightly
// more aliasing of high notes. // more aliasing of high notes.
void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; } void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; }

View file

@ -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" #include "Vgm_Emu.h"

View file

@ -1,6 +1,6 @@
// Low-level parts of Vgm_Emu // 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 #ifndef VGM_EMU_IMPL_H
#define VGM_EMU_IMPL_H #define VGM_EMU_IMPL_H

View file

@ -1,7 +1,7 @@
// Use in place of Ym2413_Emu.cpp and ym2413.c to disable support for this chip // 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" #include "Ym2413_Emu.h"

View file

@ -1,6 +1,6 @@
// YM2413 FM sound chip emulator interface // 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 #ifndef YM2413_EMU_H
#define YM2413_EMU_H #define YM2413_EMU_H

View file

@ -1,38 +1,19 @@
// YM2612 FM sound chip emulator interface // YM2612 FM sound chip emulator interface
// Game_Music_Emu 0.6.0 // Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
#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 );
};
#ifdef VGM_YM2612_GENS // LGPL v2.1+ license
#include "Ym2612_GENS.h"
typedef Ym2612_GENS_Emu Ym2612_Emu;
#endif #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

View file

@ -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 // Based on Gens 2.10 ym2612.c
#include "Ym2612_Emu.h" #include "Ym2612_GENS.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
@ -11,7 +11,7 @@
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
/* 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 /* 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 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 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 MUL; // parametre "multiple de frequence"
int TL; // Total Level = volume lorsque l'enveloppe est au plus haut int TL; // Total Level = volume lorsque l'enveloppe est au plus haut
int TLL; // Total Level ajusted int TLL; // Total Level ajusted
int SLL; // Sustin Level (ajusted) = volume ol'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_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe
int KSR; // Key Scale Rate = cette valeur est calculee par rapport la frequence actuelle, elle va influer 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 ! // sur les differents parametres de l'enveloppe comme l'attaque, le decay ... comme dans la realite !
int SEG; // Type enveloppe SSG int SEG; // Type enveloppe SSG
int env_xor; int env_xor;
@ -58,24 +58,24 @@ struct slot_t
const int *RR; // Release Rate (table pointeur) = Taux pour le rel'chement (RR[KSR]) const int *RR; // Release Rate (table pointeur) = Taux pour le rel'chement (RR[KSR])
int Fcnt; // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN[Finc >> 16]) int Fcnt; // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN[Finc >> 16])
int Finc; // frequency step = pas d'incrementation du compteur-frequence int Finc; // frequency step = pas d'incrementation du compteur-frequence
// plus le pas est grand, plus la frequence est au (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 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 ... // 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 // en fonction de la valeur de cette variable, on va appeler une fonction permettant
// de mettre jour l'enveloppe courante. // de mettre à jour l'enveloppe courante.
int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir ol'on se trouve dans l'enveloppe int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe
int Einc; // Envelope step courant int Einc; // Envelope step courant
int Ecmp; // Envelope counter limite pour la prochaine phase 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 int EincA; // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque
// cette valeur est egal AR[KSR] // cette valeur est egal à AR[KSR]
int EincD; // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression int EincD; // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression
// cette valeur est egal DR[KSR] // cette valeur est egal à DR[KSR]
int EincS; // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue int EincS; // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue
// cette valeur est egal SR[KSR] // cette valeur est egal à SR[KSR]
int EincR; // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement int EincR; // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement
// cette valeur est egal RR[KSR] // cette valeur est egal à RR[KSR]
int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot l'entree 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 // d'un autre ou carrement à la sortie de la voie
int INd; // input data of the slot = donnees en entree du slot int INd; // input data of the slot = donnees en entree du slot
int ChgEnM; // Change envelop mask. int ChgEnM; // Change envelop mask.
int AMS; // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO 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 TimerBase; // TimerBase calculation
int Status; // YM2612 Status (timer overflow) int Status; // YM2612 Status (timer overflow)
int TimerA; // timerA limit = valeur jusqu'laquelle le timer A doit compter int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter
int TimerAL; int TimerAL;
int TimerAcnt; // timerA counter = valeur courante du Timer A int TimerAcnt; // timerA counter = valeur courante du Timer A
int TimerB; // timerB limit = valeur jusqu'laquelle le timer B doit compter int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter
int TimerBL; int TimerBL;
int TimerBcnt; // timerB counter = valeur courante du Timer B int TimerBcnt; // timerB counter = valeur courante du Timer B
int Mode; // Mode actuel des voie 3 et 6 (normal / special) int Mode; // Mode actuel des voie 3 et 6 (normal / special)
int DAC; // DAC enabled flag 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 int REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif
// cela nous rend le debuggage plus facile // cela nous rend le debuggage plus facile
}; };
@ -255,9 +255,9 @@ static const unsigned char LFO_FMS_TAB [8] =
inline void YM2612_Special_Update() { } 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; state_t YM2612;
int mute_mask; int mute_mask;
@ -274,10 +274,10 @@ struct Ym2612_Impl
void write0( int addr, int data ); void write0( int addr, int data );
void write1( int addr, int data ); void write1( int addr, int data );
void run_timer( int ); 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 slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot
@ -300,7 +300,7 @@ 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 slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot
@ -318,7 +318,7 @@ 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; int nch = Adr & 3;
if ( nch == 3 ) if ( nch == 3 )
@ -420,7 +420,7 @@ 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; int num = Adr & 3;
if ( num == 3 ) if ( num == 3 )
@ -522,7 +522,7 @@ int Ym2612_Impl::CHANNEL_SET( int Adr, int data )
} }
int Ym2612_Impl::YM_SET(int Adr, int data) int Ym2612_GENS_Impl::YM_SET(int Adr, int data)
{ {
switch ( Adr ) switch ( Adr )
{ {
@ -628,7 +628,7 @@ int Ym2612_Impl::YM_SET(int Adr, int data)
return 0; 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( sample_rate );
assert( clock_rate > sample_rate ); assert( clock_rate > sample_rate );
@ -824,11 +824,11 @@ void Ym2612_Impl::set_rate( double sample_rate, double clock_rate )
reset(); 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 ) if ( !impl )
{ {
impl = (Ym2612_Impl*) malloc( sizeof *impl ); impl = (Ym2612_GENS_Impl*) malloc( sizeof *impl );
if ( !impl ) if ( !impl )
return "Out of memory"; return "Out of memory";
impl->mute_mask = 0; impl->mute_mask = 0;
@ -840,12 +840,12 @@ const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate )
return 0; return 0;
} }
Ym2612_Emu::~Ym2612_Emu() Ym2612_GENS_Emu::~Ym2612_GENS_Emu()
{ {
free( impl ); 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 ); assert( (unsigned) data <= 0xFF );
@ -865,7 +865,7 @@ 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 ); assert( (unsigned) data <= 0xFF );
@ -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(); impl->reset();
} }
void Ym2612_Impl::reset() void Ym2612_GENS_Impl::reset()
{ {
g.LFOcnt = 0; g.LFOcnt = 0;
YM2612.TimerA = 0; YM2612.TimerA = 0;
@ -949,17 +949,17 @@ void Ym2612_Impl::reset()
write0( 0x2A, 0x80 ); write0( 0x2A, 0x80 );
} }
void Ym2612_Emu::write0( int addr, int data ) void Ym2612_GENS_Emu::write0( int addr, int data )
{ {
impl->write0( addr, 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 ); 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 ) static void update_envelope_( slot_t* sl )
{ {
@ -1033,14 +1033,14 @@ inline void update_envelope( slot_t& sl )
template<int algo> template<int algo>
struct ym2612_update_chan { 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<int algo> template<int algo>
void ym2612_update_chan<algo>::func( tables_t& g, channel_t& ch, void ym2612_update_chan<algo>::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; int not_end = ch.SLOT [S3].Ecnt - ENV_END;
@ -1201,7 +1201,7 @@ static const ym2612_update_chan_t UPDATE_CHAN [8] = {
&ym2612_update_chan<7>::func &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 const step = 6;
int remain = length; int remain = length;
@ -1247,7 +1247,7 @@ void Ym2612_Impl::run_timer( int length )
while ( remain > 0 ); 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 ) if ( pair_count <= 0 )
return; return;
@ -1255,7 +1255,7 @@ void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t* out )
if ( YM2612.Mode & 3 ) if ( YM2612.Mode & 3 )
run_timer( pair_count ); run_timer( pair_count );
// Mise jour 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++ ) for ( int chi = 0; chi < channel_count; chi++ )
{ {
@ -1277,7 +1277,7 @@ void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t* out )
int ksr = ch.KC [i2] >> sl.KSR_S; // keycode attenuation int ksr = ch.KC [i2] >> sl.KSR_S; // keycode attenuation
sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL; sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL;
if (sl.KSR != ksr) // si le KSR a change alors if (sl.KSR != ksr) // si le KSR a change alors
{ // les differents taux pour l'enveloppe sont mis jour { // les differents taux pour l'enveloppe sont mis à jour
sl.KSR = ksr; sl.KSR = ksr;
sl.EincA = sl.AR [ksr]; sl.EincA = sl.AR [ksr];
@ -1316,4 +1316,4 @@ void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t* out )
g.LFOcnt += g.LFOinc * 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 ); }

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -80,6 +80,9 @@ public:
#define BLARGG_4CHAR( a, b, c, d ) \ #define BLARGG_4CHAR( a, b, c, d ) \
((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) ((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. // BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0.
#ifndef BOOST_STATIC_ASSERT #ifndef BOOST_STATIC_ASSERT
#ifdef _MSC_VER #ifdef _MSC_VER
@ -132,51 +135,12 @@ public:
typedef unsigned blargg_ulong; typedef unsigned blargg_ulong;
#endif #endif
// BOOST::int8_t etc. // int8_t etc.
// HAVE_STDINT_H: If defined, use <stdint.h> for int8_t etc. // TODO: Add CMake check for this, although I'd likely just point affected
#if defined (HAVE_STDINT_H) // persons to a real compiler...
#if 1 || defined (HAVE_STDINT_H)
#include <stdint.h> #include <stdint.h>
#define BOOST
// HAVE_INTTYPES_H: If defined, use <stdint.h> for int8_t etc.
#elif defined (HAVE_INTTYPES_H)
#include <inttypes.h>
#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 #endif
#if __GNUC__ >= 3 #if __GNUC__ >= 3

View file

@ -123,15 +123,15 @@ inline void set_be32( void* p, blargg_ulong n )
#if BLARGG_NONPORTABLE #if BLARGG_NONPORTABLE
// Optimized implementation if byte order is known // Optimized implementation if byte order is known
#if BLARGG_LITTLE_ENDIAN #if BLARGG_LITTLE_ENDIAN
#define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) #define GET_LE16( addr ) (*(uint16_t*) (addr))
#define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) #define GET_LE32( addr ) (*(uint32_t*) (addr))
#define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) #define SET_LE16( addr, data ) (void) (*(uint16_t*) (addr) = (data))
#define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) #define SET_LE32( addr, data ) (void) (*(uint32_t*) (addr) = (data))
#elif BLARGG_BIG_ENDIAN #elif BLARGG_BIG_ENDIAN
#define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) #define GET_BE16( addr ) (*(uint16_t*) (addr))
#define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) #define GET_BE32( addr ) (*(uint32_t*) (addr))
#define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) #define SET_BE16( addr, data ) (void) (*(uint16_t*) (addr) = (data))
#define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) #define SET_BE32( addr, data ) (void) (*(uint32_t*) (addr) = (data))
#if BLARGG_CPU_POWERPC #if BLARGG_CPU_POWERPC
// PowerPC has special byte-reversed instructions // PowerPC has special byte-reversed instructions
@ -172,13 +172,13 @@ inline void set_be32( void* p, blargg_ulong n )
// auto-selecting versions // auto-selecting versions
inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } inline void set_le( 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_le( 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( 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 void set_be( uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); }
inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } inline unsigned get_le( uint16_t* p ) { return GET_LE16( p ); }
inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } inline blargg_ulong get_le( uint32_t* p ) { return GET_LE32( p ); }
inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } inline unsigned get_be( uint16_t* p ) { return GET_BE16( p ); }
inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } inline blargg_ulong get_be( uint32_t* p ) { return GET_BE32( p ); }
#endif #endif

View file

@ -18,6 +18,19 @@ all other #include lines. */
#undef require #undef require
#define require( expr ) assert( expr ) #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 // Like printf() except output goes to debug log file. Might be defined to do
// nothing (not even evaluate its arguments). // nothing (not even evaluate its arguments).
// void debug_printf( const char* format, ... ); // void debug_printf( const char* format, ... );

View file

@ -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" #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('S','N','E','S'): return "SPC";
case BLARGG_4CHAR('V','g','m',' '): return "VGM"; case BLARGG_4CHAR('V','g','m',' '): return "VGM";
} }
if (get_be16(header) == BLARGG_2CHAR(0x1F, 0x8B))
return "VGZ";
return ""; return "";
} }
@ -111,6 +113,14 @@ BLARGG_EXPORT gme_type_t gme_identify_extension( const char* extension_ )
return 0; return 0;
} }
BLARGG_EXPORT const char *gme_type_extension( gme_type_t music_type )
{
const gme_type_t_ *const music_typeinfo = static_cast<const gme_type_t_ *>( 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 ) BLARGG_EXPORT gme_err_t gme_identify_file( const char* path, gme_type_t* type_out )
{ {
*type_out = gme_identify_extension( path ); *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; 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 ) if ( type )
{ {
@ -198,9 +209,18 @@ BLARGG_EXPORT Music_Emu* gme_new_emu( gme_type_t type, int rate )
if ( me ) if ( me )
{ {
#if !GME_DISABLE_STEREO_DEPTH #if !GME_DISABLE_STEREO_DEPTH
me->set_multi_channel( multi_channel );
if ( type->flags_ & 1 ) 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 ) if ( me->effects_buffer )
me->set_buffer( 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; 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_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 ) 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_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_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_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 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_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 ) BLARGG_EXPORT void gme_set_equalizer ( Music_Emu* me, gme_equalizer_t const* eq )
{ {

View file

@ -1,6 +1,6 @@
/* Game music emulator library C interface (also usable from C++) */ /* 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 #ifndef GME_H
#define GME_H #define GME_H
@ -8,7 +8,7 @@
extern "C" { extern "C" {
#endif #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) */ /* Error string returned by library functions, or NULL if no error (success) */
typedef const char* gme_err_t; 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 */ /* True if this music file type supports multiple tracks */
int gme_type_multitrack( gme_type_t ); 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 ********/ /******** Advanced file loading ********/
/* Error returned if file type is not supported */ /* Error returned if file type is not supported */
extern const char* const gme_wrong_file_type; 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 ); 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 /* 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. */ /* Get corresponding music type for file path or extension passed in. */
gme_type_t gme_identify_extension( const char path_or_extension [] ); 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). /* 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. */ 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 ); 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. */ track information, pass gme_info_only for sample_rate. */
Music_Emu* gme_new_emu( gme_type_t, int 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 */ /* Load music file into emulator */
gme_err_t gme_load_file( Music_Emu*, const char path [] ); gme_err_t gme_load_file( Music_Emu*, const char path [] );

10
game-music-emu/gme/gme_types.h Executable file → Normal file
View file

@ -1,13 +1,11 @@
#ifndef GME_TYPES_H #ifndef GME_TYPES_H
#define 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 * This is a default gme_types.h for use when *not* using
* conditionally compile in the various emulator types. * CMake. If CMake is in use gme_types.h.in will be
* * processed instead.
* See gme_type_list() in gme.cpp
*/ */
#define USE_GME_AY #define USE_GME_AY
#define USE_GME_GBS #define USE_GME_GBS
#define USE_GME_GYM #define USE_GME_GYM

View file

@ -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@

View file

@ -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 Game_Music_Emu is a collection of video game music file emulators that
support the following formats and systems: support the following formats and systems:
@ -34,10 +34,12 @@ several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable,
GP2X, and Nintendo DS. GP2X, and Nintendo DS.
Author : Shay Green <gblargg@gmail.com> Author : Shay Green <gblargg@gmail.com>
Website: http://www.slack.net/~ant/ Website: https://bitbucket.org/mpyne/game-music-emu/wiki/Home
Forum : http://groups.google.com/group/blargg-sound-libs
License: GNU Lesser General Public License (LGPL) 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 <mpyne@purinchu.net> Current Maintainer: Michael Pyne <mpyne@purinchu.net>
Getting Started Getting Started
@ -191,8 +193,13 @@ gme/
Sms_Apu.cpp Common Sega emulator files Sms_Apu.cpp Common Sega emulator files
Sms_Apu.h Sms_Apu.h
Sms_Oscs.h Sms_Oscs.h
Ym2612_Emu.cpp
Ym2612_Emu.h 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.cpp
Dual_Resampler.h Dual_Resampler.h
Fir_Resampler.cpp Fir_Resampler.cpp
@ -227,6 +234,8 @@ Legal
----- -----
Game_Music_Emu library copyright (C) 2003-2009 Shay Green. Game_Music_Emu library copyright (C) 2003-2009 Shay Green.
Sega Genesis YM2612 emulator copyright (C) 2002 Stephane Dallongeville. 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 <gblargg@gmail.com> Shay Green <gblargg@gmail.com>

Some files were not shown because too many files have changed in this diff Show more