Mercurial > audlegacy
changeset 492:ccb68bad47b2 trunk
[svn] Hostile merge, phase 1: remove merge target from the repository.
author | nenolod |
---|---|
date | Tue, 24 Jan 2006 20:17:26 -0800 |
parents | 7c5e886205ef |
children | c04dff121e1d |
files | Plugins/Input/console/Audacious_Driver.cpp Plugins/Input/console/Blip_Buffer.cpp Plugins/Input/console/Blip_Buffer.h Plugins/Input/console/Classic_Emu.cpp Plugins/Input/console/Classic_Emu.h Plugins/Input/console/Fir_Resampler.cpp Plugins/Input/console/Fir_Resampler.h Plugins/Input/console/Gb_Apu.cpp Plugins/Input/console/Gb_Apu.h Plugins/Input/console/Gb_Cpu.cpp Plugins/Input/console/Gb_Cpu.h Plugins/Input/console/Gb_Oscs.cpp Plugins/Input/console/Gb_Oscs.h Plugins/Input/console/Gbs_Emu.cpp Plugins/Input/console/Gbs_Emu.h Plugins/Input/console/Gym_Emu.cpp Plugins/Input/console/Gym_Emu.h Plugins/Input/console/Makefile.am Plugins/Input/console/Multi_Buffer.cpp Plugins/Input/console/Multi_Buffer.h Plugins/Input/console/Music_Emu.cpp Plugins/Input/console/Music_Emu.h Plugins/Input/console/Nes_Apu.cpp Plugins/Input/console/Nes_Apu.h Plugins/Input/console/Nes_Cpu.cpp Plugins/Input/console/Nes_Cpu.h Plugins/Input/console/Nes_Oscs.cpp Plugins/Input/console/Nes_Oscs.h Plugins/Input/console/Nsf_Emu.cpp Plugins/Input/console/Nsf_Emu.h Plugins/Input/console/Sms_Apu.cpp Plugins/Input/console/Sms_Apu.h Plugins/Input/console/Sms_Oscs.h Plugins/Input/console/Snes_Spc.cpp Plugins/Input/console/Snes_Spc.h Plugins/Input/console/Spc_Cpu.cpp Plugins/Input/console/Spc_Cpu.h Plugins/Input/console/Spc_Dsp.cpp Plugins/Input/console/Spc_Dsp.h Plugins/Input/console/Spc_Emu.cpp Plugins/Input/console/Spc_Emu.h Plugins/Input/console/Vgm_Emu.cpp Plugins/Input/console/Vgm_Emu.h Plugins/Input/console/abstract_file.cpp Plugins/Input/console/abstract_file.h Plugins/Input/console/blargg_common.h Plugins/Input/console/blargg_endian.h Plugins/Input/console/blargg_source.h Plugins/Input/console/gme_notes.txt Plugins/Input/console/gme_readme.txt Plugins/Input/console/notes.txt |
diffstat | 51 files changed, 0 insertions(+), 13704 deletions(-) [+] |
line wrap: on
line diff
--- a/Plugins/Input/console/Audacious_Driver.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,703 +0,0 @@ -/* - * Audacious: Cross platform multimedia player - * Copyright (c) 2005 Audacious Team - * - * Driver for Game_Music_Emu library. See details at: - * http://www.slack.net/~ant/libs/ - */ - -#include <glib.h> -#include <glib/gi18n.h> -#include <gtk/gtk.h> -#include "libaudacious/configdb.h" -#include "libaudacious/util.h" -#include "libaudacious/titlestring.h" -extern "C" { -#include "audacious/output.h" -} -#include <string.h> -#include <stdlib.h> -#include <ctype.h> -#include <math.h> - -// Game_Music_Emu -#include "Nsf_Emu.h" -#include "Nsfe_Emu.h" -#include "Gbs_Emu.h" -#include "Vgm_Emu.h" -#include "Gym_Emu.h" -#include "Spc_Emu.h" - -#include "Track_Emu.h" -#include "Vfs_File.h" -#include "Gzip_File.h" -#include "blargg_endian.h" - -//typedef Vfs_File_Reader Audacious_Reader; // will use VFS once it handles gzip transparently -typedef Gzip_File_Reader Audacious_Reader; - -struct AudaciousConsoleConfig { - gint loop_length; // length of tracks that lack timing information - gboolean resample; // whether or not to resample - gint resample_rate; // rate to resample at - gboolean nsfe_playlist; // if true, use optional NSFE playlist -}; -static AudaciousConsoleConfig audcfg = { 180, FALSE, 32000, TRUE }; -static GThread* decode_thread; -static GStaticMutex playback_mutex = G_STATIC_MUTEX_INIT; -static volatile gboolean console_ip_is_going; -static volatile long pending_seek; -extern InputPlugin console_ip; -static Music_Emu* emu = 0; -static Track_Emu track_emu; - -static void unload_file() -{ - delete emu; - emu = NULL; -} - -// Information - -typedef unsigned char byte; - -#define DUPE_FIELD( field ) g_strndup( field, sizeof (field) ); - -struct track_info_t -{ - int track; // track to get info for - int length; // in msec, -1 = unknown - int loop; // in msec, -1 = unknown, 0 = not looped - int intro; // in msec, -1 = unknown - - TitleInput* ti; -}; - -// NSFE - -void get_nsfe_info( Nsfe_Info const& nsfe, track_info_t* out ) -{ - Nsfe_Info::info_t const& h = nsfe.info(); - out->ti->performer = DUPE_FIELD( h.author ); - out->ti->album_name = DUPE_FIELD( h.game ); - out->ti->comment = DUPE_FIELD( h.copyright ); - out->ti->track_name = g_strdup( nsfe.track_name( out->track ) ); - int time = nsfe.track_time( out->track ); - if ( time > 0 ) - out->length = time; - if ( nsfe.info().track_count > 1 ) - out->ti->track_number = out->track + 1; -} - -inline void get_info_emu( Nsfe_Emu& emu, track_info_t* out ) -{ - emu.enable_playlist( audcfg.nsfe_playlist ); // to do: kind of hacky - get_nsfe_info( emu, out ); -} - -inline void get_file_info( Nsfe_Emu::header_t const& h, Data_Reader& in, track_info_t* out ) -{ - Nsfe_Info nsfe; - if ( !nsfe.load( h, in ) ) - { - nsfe.enable_playlist( audcfg.nsfe_playlist ); - get_nsfe_info( nsfe, out ); - } -} - -// NSF - -static void get_nsf_info_( Nsf_Emu::header_t const& h, track_info_t* out ) -{ - out->ti->performer = DUPE_FIELD( h.author ); - out->ti->album_name = DUPE_FIELD( h.game ); - out->ti->comment = DUPE_FIELD( h.copyright ); - if ( h.track_count > 1 ) - out->ti->track_number = out->track + 1; -} - -inline void get_info_emu( Nsf_Emu& emu, track_info_t* out ) -{ - get_nsf_info_( emu.header(), out ); -} - -inline void get_file_info( Nsf_Emu::header_t const& h, Data_Reader& in, track_info_t* out ) -{ - get_nsf_info_( h, out ); -} - -// GBS - -static void get_gbs_info_( Gbs_Emu::header_t const& h, track_info_t* out ) -{ - out->ti->performer = DUPE_FIELD( h.author ); - out->ti->album_name = DUPE_FIELD( h.game ); - out->ti->comment = DUPE_FIELD( h.copyright ); - if ( h.track_count > 1 ) - out->ti->track_number = out->track + 1; -} - -inline void get_info_emu( Gbs_Emu& emu, track_info_t* out ) -{ - get_gbs_info_( emu.header(), out ); -} - -inline void get_file_info( Gbs_Emu::header_t const& h, Data_Reader& in, track_info_t* out ) -{ - get_gbs_info_( h, out ); -} - -// GYM - -static void get_gym_info_( Gym_Emu::header_t const& h, track_info_t* out ) -{ - if ( !memcmp( h.tag, "GYMX", 4 ) ) - { - out->ti->performer = DUPE_FIELD( h.copyright ); - out->ti->album_name = DUPE_FIELD( h.game ); - out->ti->track_name = DUPE_FIELD( h.song ); - out->ti->comment = DUPE_FIELD( h.comment ); - } -} - -static void get_gym_timing_( Gym_Emu const& emu, track_info_t* out ) -{ - out->length = emu.track_length() * 50 / 3; // 1000 / 60 - out->loop = 0; - - long loop = get_le32( emu.header().loop_start ); - if ( loop ) - { - out->intro = loop * 50 / 3; - out->loop = out->length - out->intro; - out->length = -1; - } -} - -inline void get_info_emu( Gym_Emu& emu, track_info_t* out ) -{ - get_gym_info_( emu.header(), out ); - get_gym_timing_( emu, out ); -} - -inline void get_file_info( Gym_Emu::header_t const& h, Data_Reader& in, track_info_t* out ) -{ - get_gym_info_( h, out ); - - // have to load and parse entire GYM file to determine length - // to do: could make more efficient by manually parsing data (format is simple) - // rather than loading into emulator with its FM chips and resampler - Gym_Emu* emu = new Gym_Emu; - if ( emu && !emu->set_sample_rate( 44100 ) && !emu->load( h, in ) ) - get_gym_timing_( *emu, out ); - delete emu; -} - -// SPC - -static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) -{ - // header - byte const* end = begin + size; - if ( size < 8 || memcmp( begin, "xid6", 4 ) ) - return; - long info_size = get_le32( begin + 4 ); - byte const* in = begin + 8; - if ( end - in > info_size ) - end = in + info_size; - - while ( end - in >= 4 ) - { - // header - int id = in [0]; - int data = in [3] * 0x100 + in [2]; - int type = in [1]; - int len = type ? data : 0; - in += 4; - if ( len > end - in ) - break; // block goes past end of data - - // handle specific block types - switch ( id ) - { - case 0x01: out->ti->track_name = g_strndup( (char*) in, len ); break; - case 0x02: out->ti->album_name = g_strndup( (char*) in, len ); break; - case 0x03: out->ti->performer = g_strndup( (char*) in, len ); break; - case 0x07: out->ti->comment = g_strndup( (char*) in, len ); break; - //case 0x31: // loop length, but I haven't found any SPC files that use this - } - - // skip to next block - in += len; - - // blocks are supposed to be 4-byte aligned with zero-padding... - byte const* unaligned = in; - while ( (in - begin) & 3 && in < end ) - { - if ( *in++ != 0 ) - { - // ...but some files have no padding - in = unaligned; - break; - } - } - } -} - -static void get_spc_info_( Spc_Emu::header_t const& h, byte const* xid6, long xid6_size, - track_info_t* out ) -{ - // decode length (can be in text or binary format) - char s [4] = { h.len_secs [0], h.len_secs [1], h.len_secs [2], 0 }; - int len_secs = (unsigned char) s [1] * 0x100 + s [0]; - if ( s [1] >= ' ' || (!s [1] && isdigit( s [0] )) ) - len_secs = atoi( s ); - if ( len_secs ) - out->length = len_secs * 1000; - - if ( xid6_size ) - get_spc_xid6( xid6, xid6_size, out ); - - // use header to fill any remaining fields - if ( !out->ti->performer ) out->ti->performer = DUPE_FIELD( h.author ); - if ( !out->ti->album_name ) out->ti->album_name = DUPE_FIELD( h.game ); - if ( !out->ti->track_name ) out->ti->track_name = DUPE_FIELD( h.song ); -} - -inline void get_info_emu( Spc_Emu& emu, track_info_t* out ) -{ - get_spc_info_( emu.header(), emu.trailer(), emu.trailer_size(), out ); -} - -inline void get_file_info( Spc_Emu::header_t const& h, Data_Reader& in, track_info_t* out ) -{ - // handle xid6 data at end of file - long const xid6_skip = 0x10200 - sizeof (Spc_Emu::header_t); - long xid6_size = in.remain() - xid6_skip; - blargg_vector<byte> xid6; - if ( xid6_size <= 0 || xid6.resize( xid6_size ) || in.skip( xid6_skip ) || - in.read( xid6.begin(), xid6.size() ) ) - xid6_size = 0; - - get_spc_info_( h, xid6.begin(), xid6_size, out ); -} - -// VGM - -static void get_gd3_str( byte const* in, byte const* end, gchar** out ) -{ - int len = (end - in) / 2 - 1; - if ( len > 0 ) - { - *out = g_strndup( "", len ); - if ( !*out ) - return; - for ( int i = 0; i < len; i++ ) - (*out) [i] = in [i * 2]; // to do: convert to utf-8 - } -} - -static byte const* skip_gd3_str( byte const* in, byte const* end ) -{ - while ( end - in >= 2 ) - { - in += 2; - if ( !(in [-2] | in [-1]) ) - break; - } - return in; -} - -static byte const* get_gd3_pair( byte const* in, byte const* end, gchar** out ) -{ - byte const* mid = skip_gd3_str( in, end ); - if ( out ) - get_gd3_str( in, mid, out ); - return skip_gd3_str( mid, end ); -} - -static void get_vgm_gd3( byte const* in, long size, track_info_t* out ) -{ - byte const* end = in + size; - in = get_gd3_pair( in, end, &out->ti->track_name ); - in = get_gd3_pair( in, end, &out->ti->album_name ); - in = get_gd3_pair( in, end, 0 ); // system - in = get_gd3_pair( in, end, &out->ti->performer ); - in = get_gd3_pair( in, end, 0 ); // copyright - // ... other fields (release date, dumper, notes) -} - -static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) -{ - long length = get_le32( h.track_duration ); - if ( length > 0 ) - { - out->length = length * 10 / 441; // 1000 / 44100 (VGM files used 44100 as timebase) - out->loop = 0; - - long loop = get_le32( h.loop_duration ); - if ( loop > 0 && get_le32( h.loop_offset ) ) - { - out->loop = loop * 10 / 441; - out->intro = out->length - out->loop; - out->length = -1; - } - } -} - -inline void get_info_emu( Vgm_Emu& emu, track_info_t* out ) -{ - get_vgm_length( emu.header(), out ); - - int size; - byte const* data = emu.gd3_data( &size ); - if ( data ) - get_vgm_gd3( data + 12, size, out ); -} - -inline void get_file_info( Vgm_Emu::header_t const& vgm_h, Data_Reader& in, track_info_t* out ) -{ - get_vgm_length( vgm_h, out ); - - // find gd3 header - long gd3_offset = get_le32( vgm_h.gd3_offset ) + offsetof(Vgm_Emu::header_t,gd3_offset) - - sizeof vgm_h; - long gd3_max_size = in.remain() - gd3_offset; - byte gd3_h [12]; - if ( gd3_offset <= 0 || gd3_max_size < (int) sizeof gd3_h ) - return; - - // read gd3 header - if ( in.skip( gd3_offset ) || in.read( gd3_h, sizeof gd3_h ) ) - return; - - // check header signature and version - if ( memcmp( gd3_h, "Gd3 ", 4 ) || get_le32( gd3_h + 4 ) >= 0x200 ) - return; - - // get and check size - long gd3_size = get_le32( gd3_h + 8 ); - if ( gd3_size > gd3_max_size - 12 ) - return; - - // read and parse gd3 data - blargg_vector<byte> gd3; - if ( gd3.resize( gd3_size ) || in.read( gd3.begin(), gd3.size() ) ) - return; - - get_vgm_gd3( gd3.begin(), gd3.size(), out ); -} - -// File identification - -enum { type_none = 0, type_spc, type_nsf, type_nsfe, type_vgm, type_gbs, type_gym }; - -int const tag_size = 4; -typedef char tag_t [tag_size]; - -static int identify_file( gchar* path, tag_t tag ) -{ - // GYM file format doesn't require *any* header, just the ".gym" extension - if ( g_str_has_suffix( path, ".gym" ) ) // to do: is pathname in unicode? - return type_gym; - // to do: trust suffix for all file types, avoiding having to look inside files? - - int result = type_none; - if ( !memcmp( tag, "SNES", 4 ) ) result = type_spc; - if ( !memcmp( tag, "NESM", 4 ) ) result = type_nsf; - if ( !memcmp( tag, "NSFE", 4 ) ) result = type_nsfe; - if ( !memcmp( tag, "GYMX", 4 ) ) result = type_gym; - if ( !memcmp( tag, "GBS" , 3 ) ) result = type_gbs; - if ( !memcmp( tag, "Vgm ", 4 ) ) result = type_vgm; - return result; -} - -static gint is_our_file( gchar* path ) -{ - Audacious_Reader in; - tag_t tag; - return !in.open( path ) && !in.read( tag, sizeof tag ) && identify_file( path, tag ); -} - -// Get info - -static int begin_get_info( const char* path, track_info_t* out ) -{ - out->track = 0; - out->length = -1; - out->loop = -1; - out->intro = -1; - TitleInput* fields = bmp_title_input_new(); - out->ti = fields; - if ( !fields ) - return true; - - fields->file_name = g_path_get_basename( path ); - fields->file_path = g_path_get_dirname( path ); - return false; -} - -static char* end_get_info( track_info_t const& info, int* length, bool* has_length ) -{ - *length = info.length; - if ( has_length ) - *has_length = (*length > 0); - - if ( *length <= 0 ) - *length = audcfg.loop_length * 1000; - - // use filename for formats that don't have field for name of game - // to do: strip off file extension - if ( !info.ti->track_name ) - info.ti->track_name = g_strdup( info.ti->file_name ); - - char* result = xmms_get_titlestring( xmms_get_gentitle_format(), info.ti ); - g_free( info.ti ); - return result; -} - -template<class Header> -inline void get_info_t( tag_t tag, Data_Reader& in, track_info_t* out, Header* ) -{ - Header h; - memcpy( &h, tag, tag_size ); - if ( !in.read( (char*) &h + tag_size, sizeof h - tag_size ) ) - get_file_info( h, in, out ); -} - -static void get_song_info( char* path, char** title, int* length ) -{ - int track = 0; // to do: way to select other tracks - - *length = -1; - *title = NULL; - Audacious_Reader in; - tag_t tag; - if ( in.open( path ) || in.read( tag, sizeof tag ) ) - return; - - int type = identify_file( path, tag ); - if ( !type ) - return; - - track_info_t info; - if ( begin_get_info( path, &info ) ) - return; - info.track = track; - - switch ( type ) - { - case type_nsf: get_info_t( tag, in, &info, (Nsf_Emu::header_t*) 0 ); break; - case type_gbs: get_info_t( tag, in, &info, (Gbs_Emu::header_t*) 0 ); break; - case type_gym: get_info_t( tag, in, &info, (Gym_Emu::header_t*) 0 ); break; - case type_vgm: get_info_t( tag, in, &info, (Vgm_Emu::header_t*) 0 ); break; - case type_spc: get_info_t( tag, in, &info, (Spc_Emu::header_t*) 0 ); break; - case type_nsfe:get_info_t( tag, in, &info, (Nsfe_Emu::header_t*)0 ); break; - } - *title = end_get_info( info, length, 0 ); -} - -// Playback - -static int silence_pending; - -static void* play_loop_track( gpointer ) -{ - g_static_mutex_lock( &playback_mutex ); - - while ( console_ip_is_going ) - { - int const buf_size = 1024; - Music_Emu::sample_t buf [buf_size]; - - // wait for free space - while ( console_ip.output->buffer_free() < (int) sizeof buf ) - xmms_usleep( 10000 ); - - // handle pending seek - long s = pending_seek; - pending_seek = -1; // to do: use atomic swap - if ( s >= 0 ) - track_emu.seek( s ); - - // fill buffer - if ( track_emu.play( buf_size, buf ) ) - console_ip_is_going = false; - produce_audio( console_ip.output->written_time(), FMT_S16_NE, 1, sizeof buf, buf, NULL ); - } - - // stop playing - unload_file(); - console_ip.output->close_audio(); - console_ip_is_going = FALSE; - g_static_mutex_unlock( &playback_mutex ); - // to do: should decode_thread be cleared here? - g_thread_exit( NULL ); - return NULL; -} - -template<class Emu> -void load_file( tag_t tag, Data_Reader& in, long rate, track_info_t* out, Emu* dummy ) -{ - typename Emu::header_t h; - memcpy( &h, tag, tag_size ); - if ( in.read( (char*) &h + tag_size, sizeof h - tag_size ) ) - return; - - Emu* local_emu = new Emu; - if ( !local_emu || local_emu->set_sample_rate( rate ) || local_emu->load( h, in ) ) - { - delete local_emu; // delete NULL is safe - return; - } - - emu = local_emu; - get_info_emu( *local_emu, out ); -} - -static void play_file( char* path ) -{ - int track = 0; // to do: some way to select other tracks - - // open and identify file - unload_file(); - Audacious_Reader in; - tag_t tag; - if ( in.open( path ) || in.read( tag, sizeof tag ) ) - return; - int type = identify_file( path, tag ); - - // setup info - long sample_rate = 44100; - if ( type == type_spc ) - sample_rate = Spc_Emu::native_sample_rate; - if ( audcfg.resample ) - sample_rate = audcfg.resample_rate; - track_info_t info; - info.track = track; - if ( begin_get_info( path, &info ) ) - return; - - // load in emulator and get info - switch ( type ) - { - case type_nsf: load_file( tag, in, sample_rate, &info, (Nsf_Emu*) 0 ); break; - case type_nsfe:load_file( tag, in, sample_rate, &info, (Nsfe_Emu*)0 ); break; - case type_gbs: load_file( tag, in, sample_rate, &info, (Gbs_Emu*) 0 ); break; - case type_gym: load_file( tag, in, sample_rate, &info, (Gym_Emu*) 0 ); break; - case type_vgm: load_file( tag, in, sample_rate, &info, (Vgm_Emu*) 0 ); break; - case type_spc: load_file( tag, in, sample_rate, &info, (Spc_Emu*) 0 ); break; - } - in.close(); - if ( !emu ) - return; - - // set info - int length = -1; - bool has_length = false; - char* title = end_get_info( info, &length, &has_length ); - if ( title ) - { - console_ip.set_info( title, length, emu->voice_count() * 1000, sample_rate, 2 ); - g_free( title ); - } - - // start - if ( !console_ip.output->open_audio( FMT_S16_NE, sample_rate, 2 ) ) - return; - pending_seek = -1; - track_emu.start_track( emu, track, length, !has_length ); - console_ip_is_going = TRUE; - decode_thread = g_thread_create( play_loop_track, NULL, TRUE, NULL ); -} - -static void seek( gint time ) -{ - // to do: be sure seek works at all - // to do: disallow seek on slow formats (SPC, GYM, VGM using FM)? - pending_seek = time; -} - -static void console_stop(void) -{ - console_ip_is_going = FALSE; - if ( decode_thread ) - { - g_thread_join( decode_thread ); - decode_thread = NULL; - } - console_ip.output->close_audio(); - unload_file(); -} - -static void console_pause(gshort p) -{ - console_ip.output->pause(p); -} - -static int get_time(void) -{ - return console_ip_is_going ? console_ip.output->output_time() : -1; -} - -// Setup - -static void console_init(void) -{ - ConfigDb *db; - - db = bmp_cfg_db_open(); - - bmp_cfg_db_get_int(db, "console", "loop_length", &audcfg.loop_length); - bmp_cfg_db_get_bool(db, "console", "resample", &audcfg.resample); - bmp_cfg_db_get_int(db, "console", "resample_rate", &audcfg.resample_rate); - bmp_cfg_db_get_bool(db, "console", "nsfe_playlist", &audcfg.nsfe_playlist); - - bmp_cfg_db_close(db); -} - -extern "C" void console_aboutbox(void) -{ - xmms_show_message(_("About the Console Music Decoder"), - _("Console music decoder engine based on Game_Music_Emu 0.3.0.\n" - "Audacious implementation by: William Pitcock <nenolod@nenolod.net>, " - // Please do not put my hotpop.com address in the clear (I hate spam) - "Shay Green <hotpop.com@blargg>"), - _("Ok"), - FALSE, NULL, NULL); -} - -InputPlugin console_ip = -{ - NULL, - NULL, - NULL, - console_init, - console_aboutbox, - NULL, - is_our_file, - NULL, - play_file, - console_stop, - console_pause, - seek, - NULL, - get_time, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - get_song_info, - NULL, - NULL -}; - -extern "C" InputPlugin *get_iplugin_info(void) -{ - console_ip.description = g_strdup_printf(_("SPC, VGM, NSF/NSFE, GBS, and GYM module decoder")); - return &console_ip; -} -
--- a/Plugins/Input/console/Blip_Buffer.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,406 +0,0 @@ - -// Blip_Buffer 0.4.0. http://www.slack.net/~ant/ - -#include "Blip_Buffer.h" - -#include <assert.h> -#include <limits.h> -#include <string.h> -#include <stdlib.h> -#include <math.h> - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -int const buffer_extra = blip_widest_impulse_ + 2; - -Blip_Buffer::Blip_Buffer() -{ - factor_ = LONG_MAX; - offset_ = 0; - buffer_ = 0; - buffer_size_ = 0; - sample_rate_ = 0; - reader_accum = 0; - bass_shift = 0; - clock_rate_ = 0; - bass_freq_ = 16; - length_ = 0; - - // assumptions code makes about implementation-defined features - #ifndef NDEBUG - // right shift of negative value preserves sign - buf_t_ i = -0x7FFFFFFE; - assert( (i >> 1) == -0x3FFFFFFF ); - - // casting to short truncates to 16 bits and sign-extends - i = 0x18000; - assert( (short) i == -0x8000 ); - #endif -} - -Blip_Buffer::~Blip_Buffer() -{ - free( buffer_ ); -} - -void Blip_Buffer::clear( int entire_buffer ) -{ - offset_ = 0; - reader_accum = 0; - if ( buffer_ ) - { - long count = (entire_buffer ? buffer_size_ : samples_avail()); - memset( buffer_, 0, (count + buffer_extra) * sizeof (buf_t_) ); - } -} - -Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) -{ - // start with maximum length that resampled time can represent - long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - buffer_extra - 64; - if ( msec != blip_max_length ) - { - long s = (new_rate * (msec + 1) + 999) / 1000; - if ( s < new_size ) - new_size = s; - else - assert( 0 ); // fails if requested buffer length exceeds limit - } - - if ( buffer_size_ != new_size ) - { - void* p = realloc( buffer_, (new_size + buffer_extra) * sizeof *buffer_ ); - if ( !p ) - return "Out of memory"; - buffer_ = (buf_t_*) p; - } - - buffer_size_ = new_size; - - // update things based on the sample rate - sample_rate_ = new_rate; - length_ = new_size * 1000 / new_rate - 1; - if ( msec ) - assert( length_ == msec ); // ensure length is same as that passed in - if ( clock_rate_ ) - clock_rate( clock_rate_ ); - bass_freq( bass_freq_ ); - - clear(); - - return 0; // success -} - -blip_resampled_time_t Blip_Buffer::clock_rate_factor( long clock_rate ) const -{ - double ratio = (double) sample_rate_ / clock_rate; - long factor = (long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); - assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large - return (blip_resampled_time_t) factor; -} - -void Blip_Buffer::bass_freq( int freq ) -{ - bass_freq_ = freq; - int shift = 31; - if ( freq > 0 ) - { - shift = 13; - long f = (freq << 16) / sample_rate_; - while ( (f >>= 1) && --shift ) { } - } - bass_shift = shift; -} - -void Blip_Buffer::end_frame( blip_time_t t ) -{ - offset_ += t * factor_; - assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length -} - -void Blip_Buffer::remove_silence( long count ) -{ - assert( count <= samples_avail() ); // tried to remove more samples than available - offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; -} - -long Blip_Buffer::count_samples( blip_time_t t ) const -{ - unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; - unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; - return (long) (last_sample - first_sample); -} - -blip_time_t Blip_Buffer::count_clocks( long count ) const -{ - if ( count > buffer_size_ ) - count = buffer_size_; - blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; - return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); -} - -void Blip_Buffer::remove_samples( long count ) -{ - if ( count ) - { - remove_silence( count ); - - // copy remaining samples to beginning and clear old samples - long remain = samples_avail() + buffer_extra; - memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); - memset( buffer_ + remain, 0, count * sizeof *buffer_ ); - } -} - -// Blip_Synth_ - -Blip_Synth_::Blip_Synth_( short* p, int w ) : - impulses( p ), - width( w ) -{ - volume_unit_ = 0.0; - kernel_unit = 0; - buf = 0; - last_amp = 0; - delta_factor = 0; -} - -static double const pi = 3.1415926535897932384626433832795029; - -static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) -{ - if ( cutoff >= 0.999 ) - cutoff = 0.999; - - if ( treble < -300.0 ) - treble = -300.0; - if ( treble > 5.0 ) - treble = 5.0; - - double const maxh = 4096.0; - double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); - double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); - double const to_angle = pi / 2 / maxh / oversample; - for ( int i = 0; i < count; i++ ) - { - double angle = ((i - count) * 2 + 1) * to_angle; - double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); - double cos_nc_angle = cos( maxh * cutoff * angle ); - double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); - double cos_angle = cos( angle ); - - c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; - double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); - double b = 2.0 - cos_angle - cos_angle; - double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; - - out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d - } -} - -void blip_eq_t::generate( float* out, int count ) const -{ - // lower cutoff freq for narrow kernels with their wider transition band - // (8 points->1.49, 16 points->1.15) - double oversample = blip_res * 2.25 / count + 0.85; - double half_rate = sample_rate * 0.5; - if ( cutoff_freq ) - oversample = half_rate / cutoff_freq; - double cutoff = rolloff_freq * oversample / half_rate; - - gen_sinc( out, count, blip_res * oversample, treble, cutoff ); - - // apply (half of) hamming window - double to_fraction = pi / (count - 1); - for ( int i = count; i--; ) - out [i] *= 0.54 - 0.46 * cos( i * to_fraction ); -} - -void Blip_Synth_::adjust_impulse() -{ - // sum pairs for each phase and add error correction to end of first half - int const size = impulses_size(); - for ( int p = blip_res; p-- >= blip_res / 2; ) - { - int p2 = blip_res - 2 - p; - long error = kernel_unit; - for ( int i = 1; i < size; i += blip_res ) - { - error -= impulses [i + p ]; - error -= impulses [i + p2]; - } - if ( p == p2 ) - error /= 2; // phase = 0.5 impulse uses same half for both sides - impulses [size - blip_res + p] += error; - //printf( "error: %ld\n", error ); - } - - //for ( int i = blip_res; i--; printf( "\n" ) ) - // for ( int j = 0; j < width / 2; j++ ) - // printf( "%5ld,", impulses [j * blip_res + i + 1] ); -} - -void Blip_Synth_::treble_eq( blip_eq_t const& eq ) -{ - float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; - - int const half_size = blip_res / 2 * (width - 1); - eq.generate( &fimpulse [blip_res], half_size ); - - int i; - - // need mirror slightly past center for calculation - for ( i = blip_res; i--; ) - fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; - - // starts at 0 - for ( i = 0; i < blip_res; i++ ) - fimpulse [i] = 0.0f; - - // find rescale factor - double total = 0.0; - for ( i = 0; i < half_size; i++ ) - total += fimpulse [blip_res + i]; - - //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB - //double const base_unit = 37888.0; // allows treble to +5 dB - double const base_unit = 32768.0; // necessary for blip_unscaled to work - double rescale = base_unit / 2 / total; - kernel_unit = (long) base_unit; - - // integrate, first difference, rescale, convert to int - double sum = 0.0; - double next = 0.0; - int const impulses_size = this->impulses_size(); - for ( i = 0; i < impulses_size; i++ ) - { - impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); - sum += fimpulse [i]; - next += fimpulse [i + blip_res]; - } - adjust_impulse(); - - // volume might require rescaling - double vol = volume_unit_; - if ( vol ) - { - volume_unit_ = 0.0; - volume_unit( vol ); - } -} - -void Blip_Synth_::volume_unit( double new_unit ) -{ - if ( new_unit != volume_unit_ ) - { - // use default eq if it hasn't been set yet - if ( !kernel_unit ) - treble_eq( -8.0 ); - - volume_unit_ = new_unit; - double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; - - if ( factor > 0.0 ) - { - int shift = 0; - - // if unit is really small, might need to attenuate kernel - while ( factor < 2.0 ) - { - shift++; - factor *= 2.0; - } - - if ( shift ) - { - kernel_unit >>= shift; - assert( kernel_unit > 0 ); // fails if volume unit is too low - - // keep values positive to avoid round-towards-zero of sign-preserving - // right shift for negative values - long offset = 0x8000 + (1 << (shift - 1)); - long offset2 = 0x8000 >> shift; - for ( int i = impulses_size(); i--; ) - impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); - adjust_impulse(); - } - } - delta_factor = (int) floor( factor + 0.5 ); - //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); - } -} - -long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, int stereo ) -{ - long count = samples_avail(); - if ( count > max_samples ) - count = max_samples; - - if ( count ) - { - int const sample_shift = blip_sample_bits - 16; - int const bass_shift = this->bass_shift; - long accum = reader_accum; - buf_t_* in = buffer_; - - if ( !stereo ) - { - for ( long n = count; n--; ) - { - long s = accum >> sample_shift; - accum -= accum >> bass_shift; - accum += *in++; - *out++ = (blip_sample_t) s; - - // clamp sample - if ( (blip_sample_t) s != s ) - out [-1] = (blip_sample_t) (0x7FFF - (s >> 24)); - } - } - else - { - for ( long n = count; n--; ) - { - long s = accum >> sample_shift; - accum -= accum >> bass_shift; - accum += *in++; - *out = (blip_sample_t) s; - out += 2; - - // clamp sample - if ( (blip_sample_t) s != s ) - out [-2] = (blip_sample_t) (0x7FFF - (s >> 24)); - } - } - - reader_accum = accum; - remove_samples( count ); - } - return count; -} - -void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) -{ - buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; - - int const sample_shift = blip_sample_bits - 16; - int prev = 0; - while ( count-- ) - { - long s = (long) *in++ << sample_shift; - *out += s - prev; - prev = s; - ++out; - } - *out -= prev; -} -
--- a/Plugins/Input/console/Blip_Buffer.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,354 +0,0 @@ - -// Band-limited sound synthesis and buffering - -// Blip_Buffer 0.4.0 - -#ifndef BLIP_BUFFER_H -#define BLIP_BUFFER_H - -// Time unit at source clock rate -typedef long blip_time_t; - -// Output samples are 16-bit signed, with a range of -32768 to 32767 -typedef short blip_sample_t; -enum { blip_sample_max = 32767 }; - -class Blip_Buffer { -public: - typedef const char* blargg_err_t; - - // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults - // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there - // isn't enough memory, returns error without affecting current buffer setup. - blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); - - // Set number of source time units per second - void clock_rate( long ); - - // End current time frame of specified duration and make its samples available - // (along with any still-unread samples) for reading with read_samples(). Begins - // a new time frame at the end of the current frame. - void end_frame( blip_time_t time ); - - // Read at most 'max_samples' out of buffer into 'dest', removing them from from - // the buffer. Returns number of samples actually read and removed. If stereo is - // true, increments 'dest' one extra time after writing each sample, to allow - // easy interleving of two channels into a stereo output buffer. - long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); - -// Additional optional features - - // Current output sample rate - long sample_rate() const; - - // Length of buffer, in milliseconds - int length() const; - - // Number of source time units per second - long clock_rate() const; - - // Set frequency high-pass filter frequency, where higher values reduce bass more - void bass_freq( int frequency ); - - // Number of samples delay from synthesis to samples read out - int output_latency() const; - - // Remove all available samples and clear buffer to silence. If 'entire_buffer' is - // false, just clears out any samples waiting rather than the entire buffer. - void clear( int entire_buffer = 1 ); - - // Number of samples available for reading with read_samples() - long samples_avail() const; - - // Remove 'count' samples from those waiting to be read - void remove_samples( long count ); - -// Experimental features - - // Number of raw samples that can be mixed within frame of specified duration. - long count_samples( blip_time_t duration ) const; - - // Mix 'count' samples from 'buf' into buffer. - void mix_samples( blip_sample_t const* buf, long count ); - - // Count number of clocks needed until 'count' samples will be available. - // If buffer can't even hold 'count' samples, returns number of clocks until - // buffer becomes full. - blip_time_t count_clocks( long count ) const; - - // not documented yet - typedef unsigned long blip_resampled_time_t; - void remove_silence( long count ); - blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } - blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } - blip_resampled_time_t clock_rate_factor( long clock_rate ) const; -public: - Blip_Buffer(); - ~Blip_Buffer(); - - // Deprecated - 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, int msec ) { return set_sample_rate( r, msec ); } -private: - // noncopyable - Blip_Buffer( const Blip_Buffer& ); - Blip_Buffer& operator = ( const Blip_Buffer& ); -public: - typedef long buf_t_; - unsigned long factor_; - blip_resampled_time_t offset_; - buf_t_* buffer_; - long buffer_size_; -private: - long reader_accum; - int bass_shift; - long sample_rate_; - long clock_rate_; - int bass_freq_; - int length_; - friend class Blip_Reader; -}; - -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -// Number of bits in resample ratio fraction. Higher values give a more accurate ratio -// but reduce maximum buffer size. -#ifndef BLIP_BUFFER_ACCURACY - #define BLIP_BUFFER_ACCURACY 16 -#endif - -// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in -// noticeable broadband noise when synthesizing high frequency square waves. -// Affects size of Blip_Synth objects since they store the waveform directly. -#ifndef BLIP_PHASE_BITS - #define BLIP_PHASE_BITS 6 -#endif - - // Internal - typedef unsigned long blip_resampled_time_t; - int const blip_widest_impulse_ = 16; - int const blip_res = 1 << BLIP_PHASE_BITS; - class blip_eq_t; - - class Blip_Synth_ { - double volume_unit_; - short* const impulses; - int const width; - long kernel_unit; - int impulses_size() const { return blip_res / 2 * width + 1; } - void adjust_impulse(); - public: - Blip_Buffer* buf; - int last_amp; - int delta_factor; - - Blip_Synth_( short* impulses, int width ); - void treble_eq( blip_eq_t const& ); - void volume_unit( double ); - }; - -// Quality level. Start with blip_good_quality. -const int blip_med_quality = 8; -const int blip_good_quality = 12; -const int blip_high_quality = 16; - -// Range specifies the greatest expected change in amplitude. Calculate it -// by finding the difference between the maximum and minimum expected -// amplitudes (max - min). -template<int quality,int range> -class Blip_Synth { -public: - // Set overall volume of waveform - void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } - - // Configure low-pass filter (see notes.txt) - void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } - - // Get/set Blip_Buffer used for output - Blip_Buffer* output() const { return impl.buf; } - void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } - - // Update amplitude of waveform at given time. Using this requires a separate - // Blip_Synth for each waveform. - void update( blip_time_t time, int amplitude ); - -// Low-level interface - - // Add an amplitude transition of specified delta, optionally into specified buffer - // rather than the one set with output(). Delta can be positive or negative. - // The actual change in amplitude is delta * (volume / range) - void offset( blip_time_t, int delta, Blip_Buffer* ) const; - void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } - - // Works directly in terms of fractional output samples. Contact author for more. - void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; - - // Same as offset(), except code is inlined for higher performance - void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { - offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); - } - void offset_inline( blip_time_t t, int delta ) const { - offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); - } - -public: - Blip_Synth() : impl( impulses, quality ) { } -private: - typedef short imp_t; - imp_t impulses [blip_res * (quality / 2) + 1]; - Blip_Synth_ impl; -}; - -// Low-pass equalization parameters -class blip_eq_t { -public: - // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce - // treble, small positive values (0 to 5.0) increase treble. - blip_eq_t( double treble_db = 0 ); - - // See notes.txt - blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); - -private: - double treble; - long rolloff_freq; - long sample_rate; - long cutoff_freq; - void generate( float* out, int count ) const; - friend class Blip_Synth_; -}; - -int const blip_sample_bits = 30; - -// Optimized inline sample reader for custom sample formats and mixing of Blip_Buffer samples -class Blip_Reader { -public: - // Begin reading samples from buffer. Returns value to pass to next() (can - // be ignored if default bass_freq is acceptable). - int begin( Blip_Buffer& ); - - // Current sample - long read() const { return accum >> (blip_sample_bits - 16); } - - // Current raw sample in full internal resolution - long read_raw() const { return accum; } - - // Advance to next sample - void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } - - // End reading samples from buffer. The number of samples read must now be removed - // using Blip_Buffer::remove_samples(). - void end( Blip_Buffer& b ) { b.reader_accum = accum; } - -private: - const Blip_Buffer::buf_t_* buf; - long accum; -}; - - -// End of public interface - - -#include <assert.h> - -// Compatibility with older version -const long blip_unscaled = 65535; -const int blip_low_quality = blip_med_quality; -const int blip_best_quality = blip_high_quality; - -#define BLIP_FWD( i ) { \ - long t0 = i0 * delta + buf [fwd + i]; \ - long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i]; \ - i0 = imp [blip_res * (i + 2)]; \ - buf [fwd + i] = t0; \ - buf [fwd + 1 + i] = t1; } - -#define BLIP_REV( r ) { \ - long t0 = i0 * delta + buf [rev - r]; \ - long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r]; \ - i0 = imp [blip_res * (r - 1)]; \ - buf [rev - r] = t0; \ - buf [rev + 1 - r] = t1; } - -template<int quality,int range> -inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time, - int delta, Blip_Buffer* blip_buf ) const -{ - // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the - // need for a longer buffer as set by set_sample_rate(). - assert( (long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); - delta *= impl.delta_factor; - int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); - imp_t const* imp = impulses + blip_res - phase; - long* buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); - long i0 = *imp; - - int const fwd = (blip_widest_impulse_ - quality) / 2; - int const rev = fwd + quality - 2; - - BLIP_FWD( 0 ) - if ( quality > 8 ) BLIP_FWD( 2 ) - if ( quality > 12 ) BLIP_FWD( 4 ) - { - int const mid = quality / 2 - 1; - long t0 = i0 * delta + buf [fwd + mid - 1]; - long t1 = imp [blip_res * mid] * delta + buf [fwd + mid]; - imp = impulses + phase; - i0 = imp [blip_res * mid]; - buf [fwd + mid - 1] = t0; - buf [fwd + mid] = t1; - } - if ( quality > 12 ) BLIP_REV( 6 ) - if ( quality > 8 ) BLIP_REV( 4 ) - BLIP_REV( 2 ) - - long t0 = i0 * delta + buf [rev]; - long t1 = *imp * delta + buf [rev + 1]; - buf [rev] = t0; - buf [rev + 1] = t1; -} - -#undef BLIP_FWD -#undef BLIP_REV - -template<int quality,int range> -void Blip_Synth<quality,range>::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const -{ - offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); -} - -template<int quality,int range> -void Blip_Synth<quality,range>::update( blip_time_t t, int amp ) -{ - int delta = amp - impl.last_amp; - impl.last_amp = amp; - offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); -} - -inline blip_eq_t::blip_eq_t( double t ) : - treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } -inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : - treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } - -inline int Blip_Buffer::length() const { return length_; } -inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } -inline long Blip_Buffer::sample_rate() const { return sample_rate_; } -inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } -inline long Blip_Buffer::clock_rate() const { return clock_rate_; } -inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } - -inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) -{ - buf = blip_buf.buffer_; - accum = blip_buf.reader_accum; - return blip_buf.bass_shift; -} - -int const blip_max_length = 0; -int const blip_default_length = 250; - -#endif -
--- a/Plugins/Input/console/Classic_Emu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Classic_Emu.h" - -#include "Multi_Buffer.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -Classic_Emu::Classic_Emu() -{ - buf = NULL; - stereo_buffer = NULL; -} - -Classic_Emu::~Classic_Emu() -{ - delete stereo_buffer; -} - -void Classic_Emu::set_equalizer( equalizer_t const& eq ) -{ - Music_Emu::set_equalizer( eq ); - update_eq( eq.treble ); - if ( buf ) - buf->bass_freq( equalizer().bass ); -} - -blargg_err_t Classic_Emu::set_sample_rate( long sample_rate ) -{ - if ( !buf ) - { - if ( !stereo_buffer ) - BLARGG_CHECK_ALLOC( stereo_buffer = BLARGG_NEW Stereo_Buffer ); - buf = stereo_buffer; - } - - BLARGG_RETURN_ERR( buf->set_sample_rate( sample_rate, 1000 / 20 ) ); - return Music_Emu::set_sample_rate( sample_rate ); -} - -void Classic_Emu::mute_voices( int mask ) -{ - require( buf ); // set_sample_rate() must have been called - - Music_Emu::mute_voices( mask ); - for ( int i = voice_count(); i--; ) - { - if ( mask & (1 << i) ) - { - set_voice( i, NULL, NULL, NULL ); - } - else - { - Multi_Buffer::channel_t ch = buf->channel( i ); - set_voice( i, ch.center, ch.left, ch.right ); - } - } -} - -blargg_err_t Classic_Emu::setup_buffer( long new_clock_rate ) -{ - require( sample_rate() ); // fails if set_sample_rate() hasn't been called yet - - clock_rate = new_clock_rate; - buf->clock_rate( clock_rate ); - BLARGG_RETURN_ERR( buf->set_channel_count( voice_count() ) ); - set_equalizer( equalizer() ); - remute_voices(); - return blargg_success; -} - -void Classic_Emu::start_track( int track ) -{ - Music_Emu::start_track( track ); - buf->clear(); -} - -blip_time_t Classic_Emu::run_clocks( blip_time_t t, bool* ) -{ - assert( false ); - return t; -} - -blip_time_t Classic_Emu::run( int msec, bool* added_stereo ) -{ - return run_clocks( (long) msec * clock_rate / 1000, added_stereo ); -} - -void Classic_Emu::play( long count, sample_t* out ) -{ - require( sample_rate() ); // fails if set_sample_rate() hasn't been called yet - - long remain = count; - while ( remain ) - { - remain -= buf->read_samples( &out [count - remain], remain ); - if ( remain ) - { - bool added_stereo = false; - blip_time_t clocks_emulated = run( buf->length(), &added_stereo ); - buf->end_frame( clocks_emulated, added_stereo ); - } - } -} -
--- a/Plugins/Input/console/Classic_Emu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ - -// Classic game music emulator interface base class for emulators which use Blip_Buffer -// for sound output. - -// Game_Music_Emu 0.3.0 - -#ifndef CLASSIC_EMU_H -#define CLASSIC_EMU_H - -#include "Music_Emu.h" -class Blip_Buffer; -class blip_eq_t; -typedef long blip_time_t; - -class Classic_Emu : public Music_Emu { -public: - Classic_Emu(); - ~Classic_Emu(); - blargg_err_t set_sample_rate( long sample_rate ); - void set_buffer( Multi_Buffer* ); - void mute_voices( int ); - void play( long, sample_t* ); - void start_track( int track ); - void set_equalizer( equalizer_t const& ); -public: - // deprecated - blargg_err_t init( long rate ) { return set_sample_rate( rate ); } -protected: - virtual blargg_err_t setup_buffer( long clock_rate ); - virtual void set_voice( int index, Blip_Buffer* center, - Blip_Buffer* left, Blip_Buffer* right ) = 0; - virtual blip_time_t run( int msec, bool* added_stereo ); - virtual blip_time_t run_clocks( blip_time_t, bool* added_stereo ); - virtual void update_eq( blip_eq_t const& ) = 0; -private: - Multi_Buffer* buf; - Multi_Buffer* stereo_buffer; - long clock_rate; -}; - -inline void Classic_Emu::set_buffer( Multi_Buffer* new_buf ) -{ - assert( !buf && new_buf ); - buf = new_buf; -} - -#endif -
--- a/Plugins/Input/console/Fir_Resampler.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,253 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Fir_Resampler.h" - -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <math.h> - -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -// to do: fix problems with rolloff < 0.99 or so, and rolloff == 1.0, and related problems - -// Sinc impulse genertor - -const bool show_impulse = 0; - -static const double pi = 3.1415926535897932384626433832795029L; - -class Dsf { - double rolloff; - double factor; -public: - Dsf( double r ) : rolloff( r ) - { - factor = 1.0; - //if ( rolloff < 1.0 ) - // factor = 1.0 / (*this)( 0 ); - } - - double operator () ( double angle ) const - { - double const n_harm = 256; - angle /= n_harm; - double pow_a_n = pow( rolloff, n_harm ); - //double rescale = 1.0 / n_harm; - - double num = 1.0 - rolloff * cos( angle ) - - pow_a_n * cos( n_harm * angle ) + - pow_a_n * rolloff * cos( (n_harm - 1) * angle ); - double den = 1 + rolloff * (rolloff - 2 * cos( angle )); - - return (num / den - 1) / n_harm * factor; - } -}; - -template<class Sinc> -void gen_sinc( int width, double offset, double spacing, int count, double scale, short* p, - const Sinc& sinc ) -{ - double range = pi * (width / 2); - double step = pi * spacing; - double a = -step * (count / 2 - 1); - a -= offset * step; - - while ( count-- ) - { - double w = a / range; - double y = 0.0; - if ( fabs( w ) < 1.0 ) - { - double window = cos( pi * w ) * 0.5 + 0.5; - y = sinc( a ) * window; - } - - *p++ = (short) (y * scale); - a += step; - } -} - -static double plain_sinc( double a ) -{ - return fabs( a ) < 0.00001 ? 1.0 : sin( a ) / a; -} - -// Fir_Resampler - -Fir_Resampler_::Fir_Resampler_( int width, sample_t* impulses_ ) : - width_( width ), - write_offset( width * stereo - stereo ), - impulses( impulses_ ) -{ - write_pos = NULL; - res = 1; - imp = 0; - skip_bits = 0; - step = stereo; - ratio_ = 1.0; -} - -Fir_Resampler_::~Fir_Resampler_() -{ -} - -void Fir_Resampler_::clear() -{ - imp = 0; - if ( buf.size() ) - { - write_pos = &buf [write_offset]; - memset( buf.begin(), 0, write_offset * sizeof buf [0] ); - } -} - -blargg_err_t Fir_Resampler_::buffer_size( int new_size ) -{ - BLARGG_RETURN_ERR( buf.resize( new_size + write_offset ) ); - clear(); - return blargg_success; -} - -double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain ) -{ - ratio_ = new_factor; - - double fstep = 0.0; - { - double least_error = 2; - double pos = 0; - res = -1; - for ( int r = 1; r <= max_res; r++ ) - { - pos += ratio_; - double nearest = floor( pos + 0.5 ); - double error = fabs( pos - nearest ); - if ( error < least_error ) - { - res = r; - fstep = nearest / res; - least_error = error; - } - } - } - - skip_bits = 0; - - step = stereo * (int) floor( fstep ); - - ratio_ = fstep; - fstep = fmod( fstep, 1.0 ); - - double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; - double pos = 0.0; - input_per_cycle = 0; - Dsf dsf( rolloff ); - for ( int i = 0; i < res; i++ ) - { - if ( show_impulse ) - printf( "pos = %f\n", pos ); - - gen_sinc( int (width_ * filter + 1) & ~1, pos, filter, (int) width_, - double (0x7fff * gain * filter), impulses + i * width_, dsf ); - - if ( show_impulse ) - { - for ( int j = 0; j < width_; j++ ) - printf( "%d ", (int) impulses [i * width_ + j] ); - printf( "\n" ); - } - - pos += fstep; - input_per_cycle += step; - if ( pos >= 0.9999999 ) - { - pos -= 1.0; - skip_bits |= 1 << i; - input_per_cycle++; - } - } - - if ( show_impulse ) - { - printf( "skip = %8lX\n", (long) skip_bits ); - printf( "step = %d\n", step ); - } - - clear(); - - return ratio_; -} - -int Fir_Resampler_::input_needed( long output_count ) const -{ - long input_count = 0; - - unsigned long skip = skip_bits >> imp; - int remain = res - imp; - while ( (output_count -= 2) > 0 ) - { - input_count += step + (skip & 1) * stereo; - skip >>= 1; - if ( !--remain ) - { - skip = skip_bits; - remain = res; - } - output_count -= 2; - } - - long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]); - if ( input_extra < 0 ) - input_extra = 0; - return input_extra; -} - -int Fir_Resampler_::avail_( long input_count ) const -{ - int cycle_count = input_count / input_per_cycle; - int output_count = cycle_count * res * stereo; - input_count -= cycle_count * input_per_cycle; - - unsigned long skip = skip_bits >> imp; - int remain = res - imp; - while ( input_count >= 0 ) - { - input_count -= step + (skip & 1) * stereo; - skip >>= 1; - if ( !--remain ) - { - skip = skip_bits; - remain = res; - } - output_count += 2; - } - return output_count; -} - -int Fir_Resampler_::skip_input( long count ) -{ - int remain = write_pos - buf.begin(); - int avail = remain - width_ * stereo; - if ( count > avail ) - count = avail; - - remain -= count; - write_pos = &buf [remain]; - memmove( buf.begin(), &buf [count], remain * sizeof buf [0] ); - - return count; -} -
--- a/Plugins/Input/console/Fir_Resampler.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,174 +0,0 @@ - -// Finite impulse response (FIR) resampler with adjustable FIR size - -// Game_Music_Emu 0.3.0 - -#ifndef FIR_RESAMPLER_H -#define FIR_RESAMPLER_H - -#include "blargg_common.h" -#include <string.h> - -class Fir_Resampler_ { -public: - - // Use Fir_Resampler<width> (below) - - // Set input/output resampling ratio and optionally low-pass rolloff and gain. - // Returns actual ratio used (rounded to internal precision). - double time_ratio( double factor, double rolloff = 0.999, double gain = 1.0 ); - - // Current input/output ratio - double ratio() const { return ratio_; } - -// Input - - typedef short sample_t; - - // Resize and clear input buffer - blargg_err_t buffer_size( int ); - - // Clear input buffer. At least two output samples will be available after - // two input samples are written. - void clear(); - - // Number of input samples that can be written - int max_write() const { return buf.end() - write_pos; } - - // Pointer to place to write input samples - sample_t* buffer() { return write_pos; } - - // Notify resampler that 'count' input samples have been written - void write( long count ); - - // Number of input samples in buffer - int written() const { return write_pos - &buf [write_offset]; } - - // Skip 'count' input samples. Returns number of samples actually skipped. - int skip_input( long count ); - -// Output - - // Number of extra input samples needed until 'count' output samples are available - int input_needed( long count ) const; - - // Number of output samples available - int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); } - -public: - ~Fir_Resampler_(); -protected: - enum { stereo = 2 }; - enum { max_res = 32 }; - blargg_vector<sample_t> buf; - sample_t* write_pos; - int res; - int imp; - int const width_; - int const write_offset; - unsigned long skip_bits; - int step; - int input_per_cycle; - double ratio_; - sample_t* impulses; - - Fir_Resampler_( int width, sample_t* ); - int avail_( long input_count ) const; -}; - -// Width is number of points in FIR. Must be even and 4 or more. More points give -// better quality and rolloff effectiveness, and take longer to calculate. -template<int width> -class Fir_Resampler : public Fir_Resampler_ { - BOOST_STATIC_ASSERT( width >= 4 && width % 2 == 0 ); - short impulses [max_res] [width]; -public: - Fir_Resampler() : Fir_Resampler_( width, impulses [0] ) { } - - // Read at most 'count' samples. Returns number of samples actually read. - typedef short sample_t; - int read( sample_t* out, long count ); -}; - -// End of public interface - -inline void Fir_Resampler_::write( long count ) -{ - write_pos += count; - assert( write_pos <= buf.end() ); -} - -template<int width> -int Fir_Resampler<width>::read( sample_t* out_begin, long count ) -{ - sample_t* out = out_begin; - const sample_t* in = buf.begin(); - sample_t* end_pos = write_pos; - unsigned long skip = skip_bits >> this->imp; - sample_t const* imp = impulses [this->imp]; - int remain = res - this->imp; - int const step = this->step; - - count >>= 1; - - if ( end_pos - in >= width * stereo ) - { - end_pos -= width * stereo; - do - { - count--; - - // accumulate in extended precision - long l = 0; - long r = 0; - - const sample_t* i = in; - if ( count < 0 ) - break; - - for ( int n = width / 2; n; --n ) - { - int pt0 = imp [0]; - l += pt0 * i [0]; - r += pt0 * i [1]; - int pt1 = imp [1]; - imp += 2; - l += pt1 * i [2]; - r += pt1 * i [3]; - i += 4; - } - - remain--; - - l >>= 15; - r >>= 15; - - in += (skip * stereo) & stereo; - skip >>= 1; - in += step; - - if ( !remain ) - { - imp = impulses [0]; - skip = skip_bits; - remain = res; - } - - out [0] = l; - out [1] = r; - out += 2; - } - while ( in <= end_pos ); - } - - this->imp = res - remain; - - int left = write_pos - in; - write_pos = &buf [left]; - memmove( buf.begin(), in, left * sizeof *in ); - - return out - out_begin; -} - -#endif -
--- a/Plugins/Input/console/Gb_Apu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,311 +0,0 @@ - -// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/ - -#include "Gb_Apu.h" - -#include <string.h> - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -int const vol_reg = 0xFF24; -int const status_reg = 0xFF26; - -Gb_Apu::Gb_Apu() -{ - square1.synth = &square_synth; - square2.synth = &square_synth; - wave.synth = &other_synth; - noise.synth = &other_synth; - - oscs [0] = &square1; - oscs [1] = &square2; - oscs [2] = &wave; - oscs [3] = &noise; - - for ( int i = 0; i < osc_count; i++ ) - { - Gb_Osc& osc = *oscs [i]; - osc.regs = ®s [i * 5]; - osc.output = NULL; - osc.outputs [0] = NULL; - osc.outputs [1] = NULL; - osc.outputs [2] = NULL; - osc.outputs [3] = NULL; - } - - volume( 1.0 ); - reset(); -} - -Gb_Apu::~Gb_Apu() -{ -} - -void Gb_Apu::treble_eq( const blip_eq_t& eq ) -{ - square_synth.treble_eq( eq ); - other_synth.treble_eq( eq ); -} - -void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - require( (unsigned) index < osc_count ); - require( (center && left && right) || (!center && !left && !right) ); - Gb_Osc& osc = *oscs [index]; - osc.outputs [1] = right; - osc.outputs [2] = left; - osc.outputs [3] = center; - osc.output = osc.outputs [osc.output_select]; -} - -void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, center, left, right ); -} - -void Gb_Apu::update_volume() -{ - // to do: doesn't handle differing left/right global volume - int data = regs [vol_reg - start_addr]; - double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit; - square_synth.volume( vol ); - other_synth.volume( vol ); -} - -static unsigned char const powerup_regs [0x30] = { - 0x80,0x3F,0x00,0xFF,0xBF, // square 1 - 0xFF,0x3F,0x00,0xFF,0xBF, // square 2 - 0x7F,0xFF,0x9F,0xFF,0xBF, // wave - 0xFF,0xFF,0x00,0x00,0xBF, // noise - 0x00, // left/right enables - 0x77, // master volume - 0x80, // power - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table - 0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA -}; - -void Gb_Apu::reset() -{ - next_frame_time = 0; - last_time = 0; - frame_count = 0; - stereo_found = false; - - square1.reset(); - square2.reset(); - wave.reset(); - noise.reset(); - noise.bits = 1; - wave.wave_pos = 0; - - // avoid click at beginning - regs [vol_reg - start_addr] = 0x77; - update_volume(); - - regs [status_reg - start_addr] = 0x01; // force power - write_register( 0, status_reg, 0x00 ); -} - -// to do: remove -static unsigned long abs_time; - -void Gb_Apu::run_until( gb_time_t end_time ) -{ - require( end_time >= last_time ); // end_time must not be before previous time - if ( end_time == last_time ) - return; - - while ( true ) - { - gb_time_t time = next_frame_time; - if ( time > end_time ) - time = end_time; - - // run oscillators - for ( int i = 0; i < osc_count; ++i ) - { - Gb_Osc& osc = *oscs [i]; - if ( osc.output ) - { - int playing = false; - if ( osc.enabled && osc.volume && - (!(osc.regs [4] & osc.len_enabled_mask) || osc.length) ) - playing = -1; - if ( osc.output != osc.outputs [3] ) - stereo_found = true; - switch ( i ) - { - case 0: square1.run( last_time, time, playing ); break; - case 1: square2.run( last_time, time, playing ); break; - case 2: wave .run( last_time, time, playing ); break; - case 3: noise .run( last_time, time, playing ); break; - } - } - } - last_time = time; - - if ( time == end_time ) - break; - - next_frame_time += 4194304 / 256; // 256 Hz - - // 256 Hz actions - square1.clock_length(); - square2.clock_length(); - wave.clock_length(); - noise.clock_length(); - - frame_count = (frame_count + 1) & 3; - if ( frame_count == 0 ) - { - // 64 Hz actions - square1.clock_envelope(); - square2.clock_envelope(); - noise.clock_envelope(); - } - - if ( frame_count & 1 ) - square1.clock_sweep(); // 128 Hz action - } -} - -bool Gb_Apu::end_frame( gb_time_t end_time ) -{ - if ( end_time > last_time ) - run_until( end_time ); - - abs_time += end_time; - - assert( next_frame_time >= end_time ); - next_frame_time -= end_time; - - assert( last_time >= end_time ); - last_time -= end_time; - - bool result = stereo_found; - stereo_found = false; - return result; -} - -void Gb_Apu::write_register( gb_time_t time, gb_addr_t addr, int data ) -{ - require( (unsigned) data < 0x100 ); - - int reg = addr - start_addr; - if ( (unsigned) reg >= register_count ) - return; - - run_until( time ); - - int old_reg = regs [reg]; - regs [reg] = data; - - if ( addr < vol_reg ) - { - write_osc( reg / 5, reg, data ); - } - else if ( addr == vol_reg && data != old_reg ) // global volume - { - // return all oscs to 0 - for ( int i = 0; i < osc_count; i++ ) - { - Gb_Osc& osc = *oscs [i]; - int amp = osc.last_amp; - osc.last_amp = 0; - if ( amp && osc.enabled && osc.output ) - other_synth.offset( time, -amp, osc.output ); - } - - if ( wave.outputs [3] ) - other_synth.offset( time, 30, wave.outputs [3] ); - - update_volume(); - - if ( wave.outputs [3] ) - other_synth.offset( time, -30, wave.outputs [3] ); - - // oscs will update with new amplitude when next run - } - else if ( addr == 0xFF25 || addr == status_reg ) - { - int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0; - int flags = regs [0xFF25 - start_addr] & mask; - - // left/right assignments - for ( int i = 0; i < osc_count; i++ ) - { - Gb_Osc& osc = *oscs [i]; - osc.enabled &= mask; - int bits = flags >> i; - Blip_Buffer* old_output = osc.output; - osc.output_select = (bits >> 3 & 2) | (bits & 1); - osc.output = osc.outputs [osc.output_select]; - if ( osc.output != old_output ) - { - int amp = osc.last_amp; - osc.last_amp = 0; - if ( amp && old_output ) - other_synth.offset( time, -amp, old_output ); - } - } - - if ( addr == status_reg && data != old_reg ) - { - if ( !(data & 0x80) ) - { - for ( int i = 0; i < (int) sizeof powerup_regs; i++ ) - { - if ( i != status_reg - start_addr ) - write_register( time, i + start_addr, powerup_regs [i] ); - } - } - else - { - //dprintf( "APU powered on\n" ); - } - } - } - else if ( addr >= 0xFF30 ) - { - - int index = (addr & 0x0F) * 2; - wave.wave [index] = data >> 4; - wave.wave [index + 1] = data & 0x0F; - } -} - -int Gb_Apu::read_register( gb_time_t time, gb_addr_t addr ) -{ - run_until( time ); - - int index = addr - start_addr; - require( (unsigned) index < register_count ); - int data = regs [index]; - - if ( addr == status_reg ) - { - data = (data & 0x80) | 0x70; - for ( int i = 0; i < osc_count; i++ ) - { - const Gb_Osc& osc = *oscs [i]; - if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) ) - data |= 1 << i; - } - } - - return data; -} -
--- a/Plugins/Input/console/Gb_Apu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ - -// Nintendo Game Boy PAPU sound chip emulator - -// Gb_Snd_Emu 0.1.4 - -#ifndef GB_APU_H -#define GB_APU_H - -typedef long gb_time_t; // clock cycle count -typedef unsigned gb_addr_t; // 16-bit address - -#include "Gb_Oscs.h" - -class Gb_Apu { -public: - - // Set overall volume of all oscillators, where 1.0 is full volume - void volume( double ); - - // Set treble equalization - void treble_eq( const blip_eq_t& ); - - // Outputs can be assigned to a single buffer for mono output, or to three - // buffers for stereo output (using Stereo_Buffer to do the mixing). - - // Assign all oscillator outputs to specified buffer(s). If buffer - // is NULL, silences all oscillators. - void output( Blip_Buffer* mono ); - void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, - // which refer to Square 1, Square 2, Wave, and Noise. If buffer is NULL, - // silences oscillator. - enum { osc_count = 4 }; - void osc_output( int index, Blip_Buffer* mono ); - void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Reset oscillators and internal state - void reset(); - - // Reads and writes at addr must satisfy start_addr <= addr <= end_addr - enum { start_addr = 0xFF10 }; - enum { end_addr = 0xFF3f }; - enum { register_count = end_addr - start_addr + 1 }; - - // Write 'data' to address at specified time - void write_register( gb_time_t, gb_addr_t, int data ); - - // Read from address at specified time - int read_register( gb_time_t, gb_addr_t ); - - // Run all oscillators up to specified time, end current time frame, then - // start a new frame at time 0. Returns true if any oscillators added - // sound to one of the left/right buffers, false if they only added - // to the center buffer. - bool end_frame( gb_time_t ); - -public: - Gb_Apu(); - ~Gb_Apu(); -private: - // noncopyable - Gb_Apu( const Gb_Apu& ); - Gb_Apu& operator = ( const Gb_Apu& ); - - Gb_Osc* oscs [osc_count]; - gb_time_t next_frame_time; - gb_time_t last_time; - double volume_unit; - int frame_count; - bool stereo_found; - - Gb_Square square1; - Gb_Square square2; - Gb_Wave wave; - Gb_Noise noise; - BOOST::uint8_t regs [register_count]; - Gb_Square::Synth square_synth; // used by squares - Gb_Wave::Synth other_synth; // used by wave and noise - - void update_volume(); - void run_until( gb_time_t ); - void write_osc( int index, int reg, int data ); -}; - -inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } - -inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } - -inline void Gb_Apu::volume( double vol ) -{ - volume_unit = 0.60 / osc_count / 15 /*steps*/ / 2 /*?*/ / 8 /*master vol range*/ * vol; - update_volume(); -} - -#endif -
--- a/Plugins/Input/console/Gb_Cpu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1113 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Gb_Cpu.h" - -#include <string.h> -#include <limits.h> - -#include "blargg_endian.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -// Common instructions: -// -// 365880 FA LD A,IND16 -// 355863 20 JR NZ -// 313655 21 LD HL,IMM -// 274580 28 JR Z -// 252878 FE CMP IMM -// 230541 7E LD A,(HL) -// 226209 2A LD A,(HL+) -// 217467 CD CALL -// 212034 C9 RET -// 208376 CB CB prefix -// -// 27486 CB 7E BIT 7,(HL) -// 15925 CB 76 BIT 6,(HL) -// 13035 CB 19 RR C -// 11557 CB 7F BIT 7,A -// 10898 CB 37 SWAP A -// 10208 CB 66 BIT 4,(HL) - -#if BLARGG_NONPORTABLE - #define PAGE_OFFSET( addr ) (addr) -#else - #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) -#endif - -Gb_Cpu::Gb_Cpu( Gbs_Emu* gbs_emu ) -{ - callback_data = gbs_emu; - rst_base = 0; - reset(); -} - -inline void Gb_Cpu::set_code_page( int i, uint8_t const* p ) -{ - code_map [i] = p - PAGE_OFFSET( i * page_size ); -} - -void Gb_Cpu::reset( const void* unmapped_code_page, reader_t read, writer_t write ) -{ - interrupts_enabled = false; - remain_ = 0; - - r.pc = 0; - r.sp = 0; - r.flags = 0; - r.a = 0; - r.b = 0; - r.c = 0; - r.d = 0; - r.e = 0; - r.h = 0; - r.l = 0; - - for ( int i = 0; i < page_count + 1; i++ ) - { - set_code_page( i, (uint8_t*) unmapped_code_page ); - data_reader [i] = read; - data_writer [i] = write; - } -} - -void Gb_Cpu::map_code( gb_addr_t start, unsigned long size, const void* data ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - - unsigned first_page = start / page_size; - for ( unsigned i = size / page_size; i--; ) - set_code_page( first_page + i, (uint8_t*) data + i * page_size ); -} - -void Gb_Cpu::map_memory( gb_addr_t start, unsigned long size, reader_t read, writer_t write ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - - unsigned first_page = start / page_size; - for ( unsigned i = size / page_size; i--; ) - { - data_reader [first_page + i] = read; - data_writer [first_page + i] = write; - } -} - -// Note: 'addr' is evaulated more than once in the following macros, so it -// must not contain side-effects. - -#define READ( addr ) (data_reader [(addr) >> page_bits]( callback_data, addr )) -#define WRITE( addr, data ) (data_writer [(addr) >> page_bits]( callback_data, addr, data )) - -#define READ_PROG( addr ) (code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )]) -#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) ) - -int Gb_Cpu::read( gb_addr_t addr ) -{ - return READ( addr ); -} - -void Gb_Cpu::write( gb_addr_t addr, int data ) -{ - WRITE( addr, data ); -} - -BOOST::uint8_t* Gb_Cpu::get_code( gb_addr_t addr ) -{ - return (uint8_t*) &READ_PROG( addr ); -} - -#ifndef GB_CPU_GLUE_ONLY - -const unsigned z_flag = 0x80; -const unsigned n_flag = 0x40; -const unsigned h_flag = 0x20; -const unsigned c_flag = 0x10; - -#include BLARGG_ENABLE_OPTIMIZER - -Gb_Cpu::result_t Gb_Cpu::run( long cycle_count ) -{ - const int cycles_per_instruction = 4; - - // to do: use cycle table - remain_ = cycle_count + cycles_per_instruction; - - Gb_Cpu::result_t result = result_cycles; - -#if BLARGG_CPU_POWERPC - const reader_t* data_reader = this->data_reader; // cache - const writer_t* data_writer = this->data_writer; // cache -#endif - - union { - struct { -#if BLARGG_BIG_ENDIAN - uint8_t b, c, d, e, h, l, unused, a; -# define R8( n ) (r8_ [n]) -#elif BLARGG_LITTLE_ENDIAN - uint8_t c, b, e, d, l, h, a, unused; -# define R8( n ) (r8_ [(n) ^ 1]) -#else -# error "Byte order of CPU must be known" -#endif - } rg; // registers - - struct { - BOOST::uint16_t bc, de, hl, unused; // pairs - } rp; - - uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence) - BOOST::uint16_t r16 [4]; // indexed pairs - }; - BOOST_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 ); - - rg.a = r.a; - rg.b = r.b; - rg.c = r.c; - rg.d = r.d; - rg.e = r.e; - rg.h = r.h; - rg.l = r.l; - unsigned pc = r.pc; - unsigned sp = r.sp; - unsigned flags = r.flags; - -loop: - - int new_remain = remain_ - cycles_per_instruction; - - check( (unsigned) pc < 0x10000 ); - check( (unsigned) sp < 0x10000 ); - check( (flags & ~0xf0) == 0 ); - - uint8_t const* page = code_map [pc >> page_bits]; - unsigned op = page [PAGE_OFFSET( pc )]; - unsigned data = page [PAGE_OFFSET( pc ) + 1]; - check( op == 0xC9 || &READ_PROG( pc + 1 ) == &page [PAGE_OFFSET( pc ) + 1] ); - - pc++; - - remain_ = new_remain; - if ( new_remain <= 0 ) - goto stop; - - switch ( op ) - { - -#define BRANCH( cond ) \ -{ \ - pc++; \ - int offset = (BOOST::int8_t) data; \ - if ( !(cond) ) goto loop; \ - pc += offset; \ - goto loop; \ -} - -// Most Common - - case 0x20: // JR NZ - BRANCH( !(flags & z_flag) ) - - case 0x21: // LD HL,IMM (common) - rp.hl = READ_PROG16( pc ); - pc += 2; - goto loop; - - case 0x28: // JR Z - BRANCH( flags & z_flag ) - - { - unsigned temp; - - case 0xF0: // LD A,(0xff00+imm) - temp = data + 0xff00; - pc++; - goto ld_a_ind_comm; - - case 0xF2: // LD A,(0xff00+C) - temp = rg.c + 0xff00; - goto ld_a_ind_comm; - - case 0x0A: // LD A,(BC) - temp = rp.bc; - goto ld_a_ind_comm; - - case 0x3A: // LD A,(HL-) - temp = rp.hl; - rp.hl = temp - 1; - goto ld_a_ind_comm; - - case 0x1A: // LD A,(DE) - temp = rp.de; - goto ld_a_ind_comm; - - case 0x2A: // LD A,(HL+) (common) - temp = rp.hl; - rp.hl = temp + 1; - goto ld_a_ind_comm; - - case 0xFA: // LD A,IND16 (common) - temp = READ_PROG16( pc ); - pc += 2; - ld_a_ind_comm: - rg.a = READ( temp ); - goto loop; - } - - case 0xBE: // CMP (HL) - data = READ( rp.hl ); - goto cmp_comm; - - case 0xB8: // CMP B - case 0xB9: // CMP C - case 0xBA: // CMP D - case 0xBB: // CMP E - case 0xBC: // CMP H - case 0xBD: // CMP L - data = R8( op & 7 ); - goto cmp_comm; - - case 0xFE: // CMP IMM - pc++; - cmp_comm: - op = rg.a; - data = op - data; - sub_set_flags: - flags = ((op & 15) - (data & 15)) & h_flag; - flags |= (data >> 4) & c_flag; - flags |= n_flag; - if ( data & 0xff ) - goto loop; - flags |= z_flag; - goto loop; - - case 0x46: // LD B,(HL) - case 0x4E: // LD C,(HL) - case 0x56: // LD D,(HL) - case 0x5E: // LD E,(HL) - case 0x66: // LD H,(HL) - case 0x6E: // LD L,(HL) - case 0x7E: // LD A,(HL) - R8( (op >> 3) & 7 ) = READ( rp.hl ); - goto loop; - - case 0xC4: // CNZ (next-most-common) - pc += 2; - if ( flags & z_flag ) - goto loop; - call: - pc -= 2; - case 0xCD: // CALL (most-common) - data = pc + 2; - pc = READ_PROG16( pc ); - push: - sp = (sp - 1) & 0xFFFF; - WRITE( sp, data >> 8 ); - sp = (sp - 1) & 0xFFFF; - WRITE( sp, data & 0xff ); - goto loop; - - case 0xC8: // RNZ (next-most-common) - if ( !(flags & z_flag) ) - goto loop; - case 0xC9: // RET (most common) - ret: - pc = READ( sp ); - pc += 0x100 * READ( (sp + 1) & 0xFFFF ); - sp = (sp + 2) & 0xFFFF; - goto loop; - - case 0x00: // NOP - case 0x40: // LD B,B - case 0x49: // LD C,C - case 0x52: // LD D,D - case 0x5B: // LD E,E - case 0x64: // LD H,H - case 0x6D: // LD L,L - case 0x7F: // LD A,A - goto loop; - -// CB Instructions - - case 0xCB: - pc++; - // now data is the opcode - switch ( data ) { - - { - int temp; - - case 0x46: // BIT b,(HL) - case 0x4E: - case 0x56: - case 0x5E: - case 0x66: - case 0x6E: - case 0x76: - case 0x7E: - temp = READ( rp.hl ); - goto bit_comm; - - case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r - case 0x44: case 0x45: case 0x47: case 0x48: - case 0x49: case 0x4A: case 0x4B: case 0x4C: - case 0x4D: case 0x4F: case 0x50: case 0x51: - case 0x52: case 0x53: case 0x54: case 0x55: - case 0x57: case 0x58: case 0x59: case 0x5A: - case 0x5B: case 0x5C: case 0x5D: case 0x5F: - case 0x60: case 0x61: case 0x62: case 0x63: - case 0x64: case 0x65: case 0x67: case 0x68: - case 0x69: case 0x6A: case 0x6B: case 0x6C: - case 0x6D: case 0x6F: case 0x70: case 0x71: - case 0x72: case 0x73: case 0x74: case 0x75: - case 0x77: case 0x78: case 0x79: case 0x7A: - case 0x7B: case 0x7C: case 0x7D: case 0x7F: - temp = R8( data & 7 ); - bit_comm: - int bit = (~data >> 3) & 7; - flags &= ~n_flag; - flags |= h_flag | z_flag; - flags ^= (temp << bit) & z_flag; - goto loop; - } - - case 0x86: // RES b,(HL) - case 0x8E: - case 0x96: - case 0x9E: - case 0xA6: - case 0xAE: - case 0xB6: - case 0xBE: - case 0xC6: // SET b,(HL) - case 0xCE: - case 0xD6: - case 0xDE: - case 0xE6: - case 0xEE: - case 0xF6: - case 0xFE: { - int temp = READ( rp.hl ); - int bit = 1 << ((data >> 3) & 7); - temp &= ~bit; - if ( !(data & 0x40) ) - bit = 0; - WRITE( rp.hl, temp | bit ); - goto loop; - } - - case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r - case 0xC4: case 0xC5: case 0xC7: case 0xC8: - case 0xC9: case 0xCA: case 0xCB: case 0xCC: - case 0xCD: case 0xCF: case 0xD0: case 0xD1: - case 0xD2: case 0xD3: case 0xD4: case 0xD5: - case 0xD7: case 0xD8: case 0xD9: case 0xDA: - case 0xDB: case 0xDC: case 0xDD: case 0xDF: - case 0xE0: case 0xE1: case 0xE2: case 0xE3: - case 0xE4: case 0xE5: case 0xE7: case 0xE8: - case 0xE9: case 0xEA: case 0xEB: case 0xEC: - case 0xED: case 0xEF: case 0xF0: case 0xF1: - case 0xF2: case 0xF3: case 0xF4: case 0xF5: - case 0xF7: case 0xF8: case 0xF9: case 0xFA: - case 0xFB: case 0xFC: case 0xFD: case 0xFF: - R8( data & 7 ) |= 1 << ((data >> 3) & 7); - goto loop; - - case 0x80: case 0x81: case 0x82: case 0x83: // RES b,r - case 0x84: case 0x85: case 0x87: case 0x88: - case 0x89: case 0x8A: case 0x8B: case 0x8C: - case 0x8D: case 0x8F: case 0x90: case 0x91: - case 0x92: case 0x93: case 0x94: case 0x95: - case 0x97: case 0x98: case 0x99: case 0x9A: - case 0x9B: case 0x9C: case 0x9D: case 0x9F: - case 0xA0: case 0xA1: case 0xA2: case 0xA3: - case 0xA4: case 0xA5: case 0xA7: case 0xA8: - case 0xA9: case 0xAA: case 0xAB: case 0xAC: - case 0xAD: case 0xAF: case 0xB0: case 0xB1: - case 0xB2: case 0xB3: case 0xB4: case 0xB5: - case 0xB7: case 0xB8: case 0xB9: case 0xBA: - case 0xBB: case 0xBC: case 0xBD: case 0xBF: - R8( data & 7 ) &= ~(1 << ((data >> 3) & 7)); - goto loop; - - { - int temp; - case 0x36: // SWAP (HL) - temp = READ( rp.hl ); - goto swap_comm; - - case 0x30: // SWAP B - case 0x31: // SWAP C - case 0x32: // SWAP D - case 0x33: // SWAP E - case 0x34: // SWAP H - case 0x35: // SWAP L - case 0x37: // SWAP A - temp = R8( data & 7 ); - swap_comm: - op = (temp >> 4) | (temp << 4); - flags = 0; - goto shift_comm; - } - -// Shift/Rotate - - case 0x06: // RLC (HL) - case 0x16: // RL (HL) - case 0x26: // SLA (HL) - op = READ( rp.hl ); - goto rl_comm; - - case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA A - case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC A - case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL A - op = R8( data & 7 ); - goto rl_comm; - - case 0x3E: // SRL (HL) - data += 0x10; // bump up to 0x4n to avoid preserving sign bit - case 0x1E: // RR (HL) - case 0x0E: // RRC (HL) - case 0x2E: // SRA (HL) - op = READ( rp.hl ); - goto rr_comm; - - case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL A - data += 0x10; // bump up to 0x4n - case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR A - case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC A - case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA A - op = R8( data & 7 ); - goto rr_comm; - - } // CB op - assert( false ); // unhandled CB op - - case 0x07: // RLCA - case 0x17: // RLA - data = op; - op = rg.a; - rl_comm: - op <<= 1; - op |= ((data & flags) >> 4) & 1; // RL and carry is set - flags = (op >> 4) & c_flag; // C = bit shifted out - if ( data < 0x10 ) // RLC - op |= op >> 8; - // SLA doesn't fill lower bit - goto shift_comm; - - case 0x0F: // RRCA - case 0x1F: // RRA - data = op; - op = rg.a; - rr_comm: - op |= (data & flags) << 4; // RR and carry is set - flags = (op << 4) & c_flag; // C = bit shifted out - if ( data < 0x10 ) // RRC - op |= op << 8; - op >>= 1; - if ( data & 0x20 ) // SRA propagates sign bit - op |= (op << 1) & 0x80; - shift_comm: - data &= 7; - if ( !(op & 0xff) ) - flags |= z_flag; - if ( data == 6 ) - goto write_hl_op_ff; - R8( data ) = op; - goto loop; - -// Load - - case 0x70: // LD (HL),B - case 0x71: // LD (HL),C - case 0x72: // LD (HL),D - case 0x73: // LD (HL),E - case 0x74: // LD (HL),H - case 0x75: // LD (HL),L - case 0x77: // LD (HL),A - op = R8( op & 7 ); - write_hl_op_ff: - WRITE( rp.hl, op & 0xff ); - goto loop; - - case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x47: // LD r,r - case 0x48: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4F: - case 0x50: case 0x51: case 0x53: case 0x54: case 0x55: case 0x57: - case 0x58: case 0x59: case 0x5A: case 0x5C: case 0x5D: case 0x5F: - case 0x60: case 0x61: case 0x62: case 0x63: case 0x65: case 0x67: - case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6F: - case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: - R8( (op >> 3) & 7 ) = R8( op & 7 ); - goto loop; - - case 0x08: // LD IND16,SP - data = READ_PROG16( pc ); - pc += 2; - WRITE( data, sp&0xff ); - data++; - WRITE( data, sp >> 8 ); - goto loop; - - case 0xF9: // LD SP,HL - sp = rp.hl; - goto loop; - - case 0x31: // LD SP,IMM - sp = READ_PROG16( pc ); - pc += 2; - goto loop; - - case 0x01: // LD BC,IMM - case 0x11: // LD DE,IMM - r16 [op >> 4] = READ_PROG16( pc ); - pc += 2; - goto loop; - - { - unsigned temp; - case 0xE0: // LD (0xff00+imm),A - temp = data + 0xff00; - pc++; - goto write_data_rg_a; - - case 0xE2: // LD (0xff00+C),A - temp = rg.c + 0xff00; - goto write_data_rg_a; - - case 0x32: // LD (HL-),A - temp = rp.hl; - rp.hl = temp - 1; - goto write_data_rg_a; - - case 0x02: // LD (BC),A - temp = rp.bc; - goto write_data_rg_a; - - case 0x12: // LD (DE),A - temp = rp.de; - goto write_data_rg_a; - - case 0x22: // LD (HL+),A - temp = rp.hl; - rp.hl = temp + 1; - goto write_data_rg_a; - - case 0xEA: // LD IND16,A (common) - temp = READ_PROG16( pc ); - pc += 2; - write_data_rg_a: - WRITE( temp, rg.a ); - goto loop; - } - - case 0x06: // LD B,IMM - rg.b = data; - pc++; - goto loop; - - case 0x0E: // LD C,IMM - rg.c = data; - pc++; - goto loop; - - case 0x16: // LD D,IMM - rg.d = data; - pc++; - goto loop; - - case 0x1E: // LD E,IMM - rg.e = data; - pc++; - goto loop; - - case 0x26: // LD H,IMM - rg.h = data; - pc++; - goto loop; - - case 0x2E: // LD L,IMM - rg.l = data; - pc++; - goto loop; - - case 0x36: // LD (HL),IMM - WRITE( rp.hl, data ); - pc++; - goto loop; - - case 0x3E: // LD A,IMM - rg.a = data; - pc++; - goto loop; - -// Increment/Decrement - - case 0x03: // INC BC - case 0x13: // INC DE - case 0x23: // INC HL - r16 [op >> 4]++; - goto loop; - - case 0x33: // INC SP - sp = (sp + 1) & 0xFFFF; - goto loop; - - case 0x0B: // DEC BC - case 0x1B: // DEC DE - case 0x2B: // DEC HL - r16 [op >> 4]--; - goto loop; - - case 0x3B: // DEC SP - sp = (sp - 1) & 0xFFFF; - goto loop; - - case 0x34: // INC (HL) - op = rp.hl; - data = READ( op ); - data++; - WRITE( op, data & 0xff ); - goto inc_comm; - - case 0x04: // INC B - case 0x0C: // INC C (common) - case 0x14: // INC D - case 0x1C: // INC E - case 0x24: // INC H - case 0x2C: // INC L - case 0x3C: // INC A - op = (op >> 3) & 7; - R8( op ) = data = R8( op ) + 1; - inc_comm: - flags = (flags & c_flag) | (((data & 15) - 1) & h_flag) | ((data >> 1) & z_flag); - goto loop; - - case 0x35: // DEC (HL) - op = rp.hl; - data = READ( op ); - data--; - WRITE( op, data & 0xff ); - goto dec_comm; - - case 0x05: // DEC B - case 0x0D: // DEC C - case 0x15: // DEC D - case 0x1D: // DEC E - case 0x25: // DEC H - case 0x2D: // DEC L - case 0x3D: // DEC A - op = (op >> 3) & 7; - data = R8( op ) - 1; - R8( op ) = data; - dec_comm: - flags = (flags & c_flag) | n_flag | (((data & 15) + 0x31) & h_flag); - if ( data & 0xff ) - goto loop; - flags |= z_flag; - goto loop; - -// Add 16-bit - - { - unsigned long temp; // need more than 16 bits for carry - unsigned prev; - - case 0xF8: // LD HL,SP+imm - temp = BOOST::int8_t (data); // sign-extend to 16 bits - pc++; - flags = 0; - temp += sp; - prev = sp; - goto add_16_hl; - - case 0xE8: // ADD SP,IMM - temp = BOOST::int8_t (data); // sign-extend to 16 bits - pc++; - flags = 0; - temp += sp; - prev = sp; - sp = temp & 0xffff; - goto add_16_comm; - - case 0x39: // ADD HL,SP - temp = sp; - goto add_hl_comm; - - case 0x09: // ADD HL,BC - case 0x19: // ADD HL,DE - case 0x29: // ADD HL,HL - temp = r16 [op >> 4]; - add_hl_comm: - prev = rp.hl; - temp += prev; - flags &= z_flag; - add_16_hl: - rp.hl = temp; - add_16_comm: - flags |= (temp >> 12) & c_flag; - flags |= (((temp & 0x0fff) - (prev & 0x0fff)) >> 7) & h_flag; - goto loop; - } - - case 0x86: // ADD (HL) - data = READ( rp.hl ); - goto add_comm; - - case 0x80: // ADD B - case 0x81: // ADD C - case 0x82: // ADD D - case 0x83: // ADD E - case 0x84: // ADD H - case 0x85: // ADD L - case 0x87: // ADD A - data = R8( op & 7 ); - goto add_comm; - - case 0xC6: // ADD IMM - pc++; - add_comm: - flags = rg.a; - data += flags; - flags = ((data & 15) - (flags & 15)) & h_flag; - flags |= (data >> 4) & c_flag; - rg.a = data; - if ( data & 0xff ) - goto loop; - flags |= z_flag; - goto loop; - -// Add/Subtract - - case 0x8E: // ADC (HL) - data = READ( rp.hl ); - goto adc_comm; - - case 0x88: // ADC B - case 0x89: // ADC C - case 0x8A: // ADC D - case 0x8B: // ADC E - case 0x8C: // ADC H - case 0x8D: // ADC L - case 0x8F: // ADC A - data = R8( op & 7 ); - goto adc_comm; - - case 0xCE: // ADC IMM - pc++; - adc_comm: - data += (flags >> 4) & 1; - data &= 0xff; // to do: does carry get set when sum + carry = 0x100? - goto add_comm; - - case 0x96: // SUB (HL) - data = READ( rp.hl ); - goto sub_comm; - - case 0x90: // SUB B - case 0x91: // SUB C - case 0x92: // SUB D - case 0x93: // SUB E - case 0x94: // SUB H - case 0x95: // SUB L - case 0x97: // SUB A - data = R8( op & 7 ); - goto sub_comm; - - case 0xD6: // SUB IMM - pc++; - sub_comm: - op = rg.a; - data = op - data; - rg.a = data; - goto sub_set_flags; - - case 0x9E: // SBC (HL) - data = READ( rp.hl ); - goto sbc_comm; - - case 0x98: // SBC B - case 0x99: // SBC C - case 0x9A: // SBC D - case 0x9B: // SBC E - case 0x9C: // SBC H - case 0x9D: // SBC L - case 0x9F: // SBC A - data = R8( op & 7 ); - goto sbc_comm; - - case 0xDE: // SBC IMM - pc++; - sbc_comm: - data += (flags >> 4) & 1; - data &= 0xff; // to do: does carry get set when sum + carry = 0x100? - goto sub_comm; - -// Logical - - case 0xA0: // AND B - case 0xA1: // AND C - case 0xA2: // AND D - case 0xA3: // AND E - case 0xA4: // AND H - case 0xA5: // AND L - data = R8( op & 7 ); - goto and_comm; - - case 0xA6: // AND (HL) - data = READ( rp.hl ); - pc--; - case 0xE6: // AND IMM - pc++; - and_comm: - rg.a &= data; - case 0xA7: // AND A - flags = h_flag | (((rg.a - 1) >> 1) & z_flag); - goto loop; - - case 0xB0: // OR B - case 0xB1: // OR C - case 0xB2: // OR D - case 0xB3: // OR E - case 0xB4: // OR H - case 0xB5: // OR L - data = R8( op & 7 ); - goto or_comm; - - case 0xB6: // OR (HL) - data = READ( rp.hl ); - pc--; - case 0xF6: // OR IMM - pc++; - or_comm: - rg.a |= data; - case 0xB7: // OR A - flags = ((rg.a - 1) >> 1) & z_flag; - goto loop; - - case 0xA8: // XOR B - case 0xA9: // XOR C - case 0xAA: // XOR D - case 0xAB: // XOR E - case 0xAC: // XOR H - case 0xAD: // XOR L - data = R8( op & 7 ); - goto xor_comm; - - case 0xAE: // XOR (HL) - data = READ( rp.hl ); - pc--; - case 0xEE: // XOR IMM - pc++; - xor_comm: - data ^= rg.a; - rg.a = data; - data--; - flags = (data >> 1) & z_flag; - goto loop; - - case 0xAF: // XOR A - rg.a = 0; - flags = z_flag; - goto loop; - -// Stack - - case 0xC1: // POP BC - case 0xD1: // POP DE - case 0xE1:{// POP HL (common) - int temp = READ( sp ); - r16 [(op >> 4) & 3] = temp + 0x100 * READ( (sp + 1) & 0xFFFF ); - sp = (sp + 2) & 0xFFFF; - goto loop; - } - - case 0xF1: // POP FA - rg.a = READ( sp ); - flags = READ( (sp + 1) & 0xFFFF ) & 0xf0; - sp = (sp + 2) & 0xFFFF; - goto loop; - - case 0xC5: // PUSH BC - data = rp.bc; - goto push; - - case 0xD5: // PUSH DE - data = rp.de; - goto push; - - case 0xE5: // PUSH HL - data = rp.hl; - goto push; - - case 0xF5: // PUSH FA - data = (flags << 8) | rg.a; - goto push; - -// Flow control - - case 0xC7: case 0xCF: case 0xD7: case 0xDF: // RST - case 0xE7: case 0xEF: case 0xF7: case 0xFF: - data = pc; - pc = (op & 0x38) + rst_base; - goto push; - - case 0xCC: // CZ - pc += 2; - if ( flags & z_flag ) - goto call; - goto loop; - - case 0xD4: // CNC - pc += 2; - if ( !(flags & c_flag) ) - goto call; - goto loop; - - case 0xDC: // CC - pc += 2; - if ( flags & c_flag ) - goto call; - goto loop; - - case 0xD9: // RETI - Gb_Cpu::interrupts_enabled = 1; - goto ret; - - case 0xC0: // RZ - if ( !(flags & z_flag) ) - goto ret; - goto loop; - - case 0xD0: // RNC - if ( !(flags & c_flag) ) - goto ret; - goto loop; - - case 0xD8: // RC - if ( flags & c_flag ) - goto ret; - goto loop; - - case 0x18: // JR - BRANCH( true ) - - case 0x30: // JR NC - BRANCH( !(flags & c_flag) ) - - case 0x38: // JR C - BRANCH( flags & c_flag ) - - case 0xE9: // JP_HL - pc = rp.hl; - goto loop; - - case 0xC3: // JP (next-most-common) - pc = READ_PROG16( pc ); - goto loop; - - case 0xC2: // JP NZ - pc += 2; - if ( !(flags & z_flag) ) - goto jp_taken; - goto loop; - - case 0xCA: // JP Z (most common) - pc += 2; - if ( !(flags & z_flag) ) - goto loop; - jp_taken: - pc -= 2; - pc = READ_PROG16( pc ); - goto loop; - - case 0xD2: // JP NC - pc += 2; - if ( !(flags & c_flag) ) - goto jp_taken; - goto loop; - - case 0xDA: // JP C - pc += 2; - if ( flags & c_flag ) - goto jp_taken; - goto loop; - -// Flags - - case 0x2F: // CPL - rg.a = ~rg.a; - flags |= n_flag | h_flag; - goto loop; - - case 0x3F: // CCF - flags = (flags ^ c_flag) & ~(n_flag | h_flag); - goto loop; - - case 0x37: // SCF - flags = (flags | c_flag) & ~(n_flag | h_flag); - goto loop; - - case 0xF3: // DI - interrupts_enabled = 0; - goto loop; - - case 0xFB: // EI - interrupts_enabled = 1; - goto loop; - -// Special - - case 0xDD: case 0xD3: case 0xDB: case 0xE3: case 0xE4: // ? - case 0xEB: case 0xEC: case 0xF4: case 0xFD: case 0xFC: - case 0x10: // STOP - case 0x27: // DAA (I'll have to implement this eventually...) - case 0xBF: - case 0xED: // Z80 prefix - result = Gb_Cpu::result_badop; - goto stop; - - case 0x76: // HALT - result = Gb_Cpu::result_halt; - goto stop; - } - - // If this fails then the case above is missing an opcode - assert( false ); - -stop: - pc--; - - // copy state back - r.pc = pc; - r.sp = sp; - r.flags = flags; - r.a = rg.a; - r.b = rg.b; - r.c = rg.c; - r.d = rg.d; - r.e = rg.e; - r.h = rg.h; - r.l = rg.l; - - return result; -} - -#endif -
--- a/Plugins/Input/console/Gb_Cpu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ - -// Nintendo Game Boy CPU emulator - -// Game_Music_Emu 0.3.0 - -#ifndef GB_CPU_H -#define GB_CPU_H - -#include "blargg_common.h" - -typedef unsigned gb_addr_t; // 16-bit CPU address - -class Gbs_Emu; - -// Game Boy CPU emulator. Currently treats every instruction as taking 4 cycles. -class Gb_Cpu { - typedef BOOST::uint8_t uint8_t; - enum { page_bits = 8 }; - enum { page_count = 0x10000 >> page_bits }; - uint8_t const* code_map [page_count + 1]; - long remain_; - Gbs_Emu* callback_data; -public: - - Gb_Cpu( Gbs_Emu* ); - - // Memory read/write function types. Reader must return value from 0 to 255. - typedef int (*reader_t)( Gbs_Emu*, gb_addr_t ); - typedef void (*writer_t)( Gbs_Emu*, gb_addr_t, int data ); - - // Clear registers, unmap memory, and map code pages to unmapped_page. - void reset( const void* unmapped_page = NULL, reader_t read = NULL, writer_t write = NULL ); - - // Memory mapping functions take a block of memory of specified 'start' address - // and 'size' in bytes. Both start address and size must be a multiple of page_size. - enum { page_size = 1L << page_bits }; - - // Map code memory to 'code' (memory accessed via the program counter) - void map_code( gb_addr_t start, unsigned long size, const void* code ); - - // Map data memory to read and write functions - void map_memory( gb_addr_t start, unsigned long size, reader_t, writer_t ); - - // Access memory as the emulated CPU does. - int read( gb_addr_t ); - void write( gb_addr_t, int data ); - uint8_t* get_code( gb_addr_t ); // non-const to allow debugger to modify code - - // Push a byte on the stack - void push_byte( int ); - - // Game Boy Z80 registers. *Not* kept updated during a call to run(). - struct registers_t { - long pc; // more than 16 bits to allow overflow detection - BOOST::uint16_t sp; - uint8_t flags; - uint8_t a; - uint8_t b; - uint8_t c; - uint8_t d; - uint8_t e; - uint8_t h; - uint8_t l; - }; - registers_t r; - - // Interrupt enable flag set by EI and cleared by DI - bool interrupts_enabled; - - // Base address for RST vectors (normally 0) - gb_addr_t rst_base; - - // Reasons that run() returns - enum result_t { - result_cycles, // Requested number of cycles (or more) were executed - result_halt, // PC is at HALT instruction - result_badop // PC is at bad (unimplemented) instruction - }; - - // Run CPU for at least 'count' cycles, or until one of the above conditions - // arises. Returns reason for stopping. - result_t run( long count ); - - // Number of clock cycles remaining for most recent run() call - long remain() const; - -private: - // noncopyable - Gb_Cpu( const Gb_Cpu& ); - Gb_Cpu& operator = ( const Gb_Cpu& ); - - reader_t data_reader [page_count + 1]; // extra entry catches address overflow - writer_t data_writer [page_count + 1]; - void set_code_page( int, uint8_t const* ); -}; - -inline long Gb_Cpu::remain() const -{ - return remain_; -} - -#endif -
--- a/Plugins/Input/console/Gb_Oscs.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,333 +0,0 @@ - -// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/ - -#include "Gb_Apu.h" - -#include <string.h> - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -// Gb_Osc - -void Gb_Osc::reset() -{ - delay = 0; - last_amp = 0; - length = 0; - output_select = 3; - output = outputs [output_select]; -} - -void Gb_Osc::clock_length() -{ - if ( (regs [4] & len_enabled_mask) && length ) - length--; -} - -// Gb_Env - -void Gb_Env::clock_envelope() -{ - if ( env_delay && !--env_delay ) - { - env_delay = regs [2] & 7; - int v = volume - 1 + (regs [2] >> 2 & 2); - if ( (unsigned) v < 15 ) - volume = v; - } -} - -bool Gb_Env::write_register( int reg, int data ) -{ - switch ( reg ) - { - case 1: - length = 64 - (regs [1] & 0x3f); - break; - - case 2: - if ( !(data >> 4) ) - enabled = false; - break; - - case 4: - if ( data & trigger ) - { - env_delay = regs [2] & 7; - volume = regs [2] >> 4; - enabled = true; - if ( length == 0 ) - length = 64; - return true; - } - } - return false; -} - -// Gb_Square - -void Gb_Square::reset() -{ - phase = 0; - sweep_freq = 0; - sweep_delay = 0; - Gb_Env::reset(); -} - -void Gb_Square::clock_sweep() -{ - int sweep_period = (regs [0] & period_mask) >> 4; - if ( sweep_period && sweep_delay && !--sweep_delay ) - { - sweep_delay = sweep_period; - regs [3] = sweep_freq & 0xFF; - regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07); - - int offset = sweep_freq >> (regs [0] & shift_mask); - if ( regs [0] & 0x08 ) - offset = -offset; - sweep_freq += offset; - - if ( sweep_freq < 0 ) - { - sweep_freq = 0; - } - else if ( sweep_freq >= 2048 ) - { - sweep_delay = 0; // don't modify channel frequency any further - sweep_freq = 2048; // silence sound immediately - } - } -} - -void Gb_Square::run( gb_time_t time, gb_time_t end_time, int playing ) -{ - if ( sweep_freq == 2048 ) - playing = false; - - static unsigned char const table [4] = { 1, 2, 4, 6 }; - int const duty = table [regs [1] >> 6]; - int amp = volume & playing; - if ( phase >= duty ) - amp = -amp; - - int frequency = this->frequency(); - if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041 - { - // really high frequency results in DC at half volume - amp = volume >> 1; - playing = false; - } - - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); - } - - time += delay; - if ( !playing ) - time = end_time; - - if ( time < end_time ) - { - int const period = (2048 - frequency) * 4; - Blip_Buffer* const output = this->output; - int phase = this->phase; - int delta = amp * 2; - do - { - phase = (phase + 1) & 7; - if ( phase == 0 || phase == duty ) - { - delta = -delta; - synth->offset_inline( time, delta, output ); - } - time += period; - } - while ( time < end_time ); - - this->phase = phase; - last_amp = delta >> 1; - } - delay = time - end_time; -} - -// Gb_Noise - -#include BLARGG_ENABLE_OPTIMIZER - -void Gb_Noise::run( gb_time_t time, gb_time_t end_time, int playing ) -{ - int amp = volume & playing; - int tap = 13 - (regs [3] & 8); - if ( bits >> tap & 2 ) - amp = -amp; - - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); - } - - time += delay; - if ( !playing ) - time = end_time; - - if ( time < end_time ) - { - static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 }; - int period = table [regs [3] & 7] << (regs [3] >> 4); - - // keep parallel resampled time to eliminate time conversion in the loop - Blip_Buffer* const output = this->output; - const blip_resampled_time_t resampled_period = - output->resampled_duration( period ); - blip_resampled_time_t resampled_time = output->resampled_time( time ); - unsigned bits = this->bits; - int delta = amp * 2; - - do - { - unsigned changed = (bits >> tap) + 1; - time += period; - bits <<= 1; - if ( changed & 2 ) - { - delta = -delta; - bits |= 1; - synth->offset_resampled( resampled_time, delta, output ); - } - resampled_time += resampled_period; - } - while ( time < end_time ); - - this->bits = bits; - last_amp = delta >> 1; - } - delay = time - end_time; -} - -// Gb_Wave - -inline void Gb_Wave::write_register( int reg, int data ) -{ - switch ( reg ) - { - case 0: - if ( !(data & 0x80) ) - enabled = false; - break; - - case 1: - length = 256 - regs [1]; - break; - - case 2: - volume = data >> 5 & 3; - break; - - case 4: - if ( data & trigger & regs [0] ) - { - wave_pos = 0; - enabled = true; - if ( length == 0 ) - length = 256; - } - } -} - -void Gb_Wave::run( gb_time_t time, gb_time_t end_time, int playing ) -{ - int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7 - int amp = (wave [wave_pos] >> volume_shift & playing) * 2; - - int frequency = this->frequency(); - if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045 - { - amp = 30 >> volume_shift & playing; - playing = false; - } - - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); - } - - time += delay; - if ( !playing ) - time = end_time; - - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - int const period = (2048 - frequency) * 2; - int wave_pos = (this->wave_pos + 1) & (wave_size - 1); - - do - { - int amp = (wave [wave_pos] >> volume_shift) * 2; - wave_pos = (wave_pos + 1) & (wave_size - 1); - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset_inline( time, delta, output ); - } - time += period; - } - while ( time < end_time ); - - this->wave_pos = (wave_pos - 1) & (wave_size - 1); - } - delay = time - end_time; -} - -// Gb_Apu::write_osc - -void Gb_Apu::write_osc( int index, int reg, int data ) -{ - reg -= index * 5; - Gb_Square* sq = &square2; - switch ( index ) - { - case 0: - sq = &square1; - case 1: - if ( sq->write_register( reg, data ) && index == 0 ) - { - square1.sweep_freq = square1.frequency(); - if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) ) - { - square1.sweep_delay = 1; // cause sweep to recalculate now - square1.clock_sweep(); - } - } - break; - - case 2: - wave.write_register( reg, data ); - break; - - case 3: - if ( noise.write_register( reg, data ) ) - noise.bits = 0x7FFF; - } -} -
--- a/Plugins/Input/console/Gb_Oscs.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ - -// Private oscillators used by Gb_Apu - -// Gb_Snd_Emu 0.1.4 - -#ifndef GB_OSCS_H -#define GB_OSCS_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -struct Gb_Osc -{ - enum { trigger = 0x80 }; - enum { len_enabled_mask = 0x40 }; - - Blip_Buffer* outputs [4]; // NULL, right, left, center - Blip_Buffer* output; - int output_select; - BOOST::uint8_t* regs; // osc's 5 registers - - int delay; - int last_amp; - int volume; - int length; - bool enabled; - - void reset(); - void clock_length(); - int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } -}; - -struct Gb_Env : Gb_Osc -{ - int env_delay; - - void reset(); - void clock_envelope(); - bool write_register( int, int ); -}; - -struct Gb_Square : Gb_Env -{ - enum { period_mask = 0x70 }; - enum { shift_mask = 0x07 }; - - typedef Blip_Synth<blip_good_quality,1> Synth; - Synth const* synth; - int sweep_delay; - int sweep_freq; - int phase; - - void reset(); - void clock_sweep(); - void run( gb_time_t, gb_time_t, int playing ); -}; - -struct Gb_Noise : Gb_Env -{ - typedef Blip_Synth<blip_med_quality,1> Synth; - Synth const* synth; - unsigned bits; - - void run( gb_time_t, gb_time_t, int playing ); -}; - -struct Gb_Wave : Gb_Osc -{ - typedef Blip_Synth<blip_med_quality,1> Synth; - Synth const* synth; - int wave_pos; - enum { wave_size = 32 }; - BOOST::uint8_t wave [wave_size]; - - void write_register( int, int ); - void run( gb_time_t, gb_time_t, int playing ); -}; - -inline void Gb_Env::reset() -{ - env_delay = 0; - Gb_Osc::reset(); -} - -#endif -
--- a/Plugins/Input/console/Gbs_Emu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,366 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Gbs_Emu.h" - -#include <string.h> - -#include "blargg_endian.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -#ifndef RUN_GB_CPU - #define RUN_GB_CPU( cpu, n ) cpu.run( n ) -#endif - -const long bank_size = 0x4000; -const gb_addr_t ram_addr = 0xa000; -const gb_addr_t halt_addr = 0x9EFE; -static BOOST::uint8_t unmapped_code [Gb_Cpu::page_size]; - -Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = { -47.0, 2000 }; -Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 300 }; - -// RAM - -int Gbs_Emu::read_ram( Gbs_Emu* emu, gb_addr_t addr ) -{ - return emu->ram [addr - ram_addr]; -} - -void Gbs_Emu::write_ram( Gbs_Emu* emu, gb_addr_t addr, int data ) -{ - emu->ram [addr - ram_addr] = data; -} - -// Unmapped - -int Gbs_Emu::read_unmapped( Gbs_Emu*, gb_addr_t addr ) -{ - dprintf( "Read from unmapped memory $%.4x\n", (unsigned) addr ); - return 0xFF; // open bus value -} - -void Gbs_Emu::write_unmapped( Gbs_Emu*, gb_addr_t addr, int ) -{ - dprintf( "Wrote to unmapped memory $%.4x\n", (unsigned) addr ); -} - -// ROM - -int Gbs_Emu::read_rom( Gbs_Emu* emu, gb_addr_t addr ) -{ - return emu->rom [addr]; -} - -int Gbs_Emu::read_bank( Gbs_Emu* emu, gb_addr_t addr ) -{ - return emu->rom_bank [addr & (bank_size - 1)]; -} - -void Gbs_Emu::set_bank( int n ) -{ - if ( n >= bank_count ) - { - n = 0; - dprintf( "Set to non-existent bank %d\n", (int) n ); - } - if ( n == 0 && bank_count > 1 ) - { - // to do: what is the correct behavior? Current Wario Land 3 and - // Tetris DX GBS rips require that this have no effect or set to bank 1. - //return; - //dprintf( "Selected ROM bank 0\n" ); - } - rom_bank = &rom [n * bank_size]; - cpu.map_code( bank_size, bank_size, rom_bank ); -} - -void Gbs_Emu::write_rom( Gbs_Emu* emu, gb_addr_t addr, int data ) -{ - if ( unsigned (addr - 0x2000) < 0x2000 ) - emu->set_bank( data & 0x1F ); -} - -// I/O: Timer, APU - -void Gbs_Emu::set_timer( int modulo, int rate ) -{ - if ( timer_mode ) - { - static byte const rates [4] = { 10, 4, 6, 8 }; - play_period = (gb_time_t) (256 - modulo) << (rates [rate & 3] - double_speed); - } -} - -inline gb_time_t Gbs_Emu::clock() const -{ - return cpu_time - cpu.remain(); -} - -int Gbs_Emu::read_io( Gbs_Emu* emu, gb_addr_t addr ) -{ - // hi_page is accessed most - if ( addr >= 0xFF80 ) - return emu->hi_page [addr & 0xFF]; - - if ( unsigned (addr - Gb_Apu::start_addr) <= Gb_Apu::register_count ) - return emu->apu.read_register( emu->clock(), addr ); - - if ( addr == 0xFF00 ) - return 0; // joypad - - dprintf( "Unhandled I/O read 0x%4X\n", (unsigned) addr ); - - return 0xFF; -} - -void Gbs_Emu::write_io( Gbs_Emu* emu, gb_addr_t addr, int data ) -{ - // apu is accessed most - if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count ) - { - emu->apu.write_register( emu->clock(), addr, data ); - } - else - { - emu->hi_page [addr & 0xFF] = data; - - if ( addr == 0xFF06 || addr == 0xFF07 ) - emu->set_timer( emu->hi_page [6], emu->hi_page [7] ); - - //if ( addr == 0xFFFF ) - // dprintf( "Wrote interrupt mask\n" ); - } -} - -Gbs_Emu::Gbs_Emu( double gain ) : cpu( this ) -{ - apu.volume( gain ); - - static equalizer_t const eq = { -1.0, 120 }; - set_equalizer( eq ); - - // unmapped code is all HALT instructions - memset( unmapped_code, 0x76, sizeof unmapped_code ); - - // cpu - cpu.reset( unmapped_code, read_unmapped, write_unmapped ); - cpu.map_memory( 0x0000, 0x4000, read_rom, write_rom ); - cpu.map_memory( 0x4000, 0x4000, read_bank, write_rom ); - cpu.map_memory( ram_addr, 0x4000, read_ram, write_ram ); - cpu.map_code( ram_addr, 0x4000, ram ); - cpu.map_code( 0xFF00, 0x0100, hi_page ); - cpu.map_memory( 0xFF00, 0x0100, read_io, write_io ); -} - -Gbs_Emu::~Gbs_Emu() -{ -} - -void Gbs_Emu::unload() -{ - cpu.r.pc = halt_addr; - rom.clear(); -} - -void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - apu.osc_output( i, c, l, r ); -} - -void Gbs_Emu::update_eq( blip_eq_t const& eq ) -{ - apu.treble_eq( eq ); -} - -blargg_err_t Gbs_Emu::load( Data_Reader& in ) -{ - header_t h; - BLARGG_RETURN_ERR( in.read( &h, sizeof h ) ); - return load( h, in ); -} - -blargg_err_t Gbs_Emu::load( const header_t& h, Data_Reader& in ) -{ - header_ = h; - unload(); - - // check compatibility - if ( 0 != memcmp( header_.tag, "GBS", 3 ) ) - return "Not a GBS file"; - if ( header_.vers != 1 ) - return "Unsupported GBS format"; - - // gather relevant fields - load_addr = get_le16( header_.load_addr ); - init_addr = get_le16( header_.init_addr ); - play_addr = get_le16( header_.play_addr ); - stack_ptr = get_le16( header_.stack_ptr ); - double_speed = (header_.timer_mode & 0x80) != 0; - timer_modulo_init = header_.timer_modulo; - timer_mode = header_.timer_mode; - if ( !(timer_mode & 0x04) ) - timer_mode = 0; // using vbl - - #ifndef NDEBUG - { - if ( header_.timer_mode & 0x78 ) - dprintf( "TAC field has extra bits set: 0x%02x\n", (unsigned) header_.timer_mode ); - - if ( load_addr < 0x400 || load_addr >= 0x8000 || - init_addr < 0x400 || init_addr >= 0x8000 || - play_addr < 0x400 || play_addr >= 0x8000 ) - dprintf( "Load/init/play address violates GBS spec.\n" ); - } - #endif - - // rom - bank_count = (load_addr + in.remain() + bank_size - 1) / bank_size; - BLARGG_RETURN_ERR( rom.resize( bank_count * bank_size ) ); - memset( rom.begin(), 0, rom.size() ); - blargg_err_t err = in.read( &rom [load_addr], in.remain() ); - if ( err ) - { - unload(); - return err; - } - - // cpu - cpu.rst_base = load_addr; - cpu.map_code( 0x0000, 0x4000, rom.begin() ); - - set_voice_count( Gb_Apu::osc_count ); - set_track_count( header_.track_count ); - - return setup_buffer( 4194304 ); -} - -const char** Gbs_Emu::voice_names() const -{ - static const char* names [] = { "Square 1", "Square 2", "Wave", "Noise" }; - return names; -} - -// Emulation - -static const BOOST::uint8_t sound_data [Gb_Apu::register_count] = { - 0x80, 0xBF, 0x00, 0x00, 0xBF, // square 1 - 0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2 - 0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave - 0x00, 0xFF, 0x00, 0x00, 0xBF, // noise - 0x77, 0xF3, 0xF1, // vin/volume, status, power mode - 0, 0, 0, 0, 0, 0, 0, 0, 0, // unused - 0xAC, 0xDD, 0xDA, 0x48, 0x36, 0x02, 0xCF, 0x16, // waveform data - 0x2C, 0x04, 0xE5, 0x2C, 0xAC, 0xDD, 0xDA, 0x48 -}; - -void Gbs_Emu::cpu_jsr( gb_addr_t addr ) -{ - cpu.write( --cpu.r.sp, cpu.r.pc >> 8 ); - cpu.write( --cpu.r.sp, cpu.r.pc&0xFF ); - cpu.r.pc = addr; -} - -void Gbs_Emu::start_track( int track_index ) -{ - require( rom.size() ); // file must be loaded - - Classic_Emu::start_track( track_index ); - - apu.reset(); - - memset( ram, 0, sizeof ram ); - memset( hi_page, 0, sizeof hi_page ); - - // configure hardware - set_bank( bank_count > 1 ); - for ( int i = 0; i < (int) sizeof sound_data; i++ ) - apu.write_register( 0, i + apu.start_addr, sound_data [i] ); - play_period = 70224; // 59.73 Hz - set_timer( timer_modulo_init, timer_mode ); // ignored if using vbl - next_play = play_period; - - // set up init call - cpu.r.a = track_index; - cpu.r.b = 0; - cpu.r.c = 0; - cpu.r.d = 0; - cpu.r.e = 0; - cpu.r.h = 0; - cpu.r.l = 0; - cpu.r.flags = 0; - cpu.r.pc = halt_addr; - cpu.r.sp = stack_ptr; - cpu_jsr( init_addr ); -} - -blip_time_t Gbs_Emu::run_clocks( blip_time_t duration, bool* added_stereo ) -{ - require( rom.size() ); // file must be loaded - - cpu_time = 0; - while ( cpu_time < duration ) - { - // check for idle cpu - if ( cpu.r.pc == halt_addr ) - { - if ( next_play > duration ) - { - cpu_time = duration; - break; - } - - if ( cpu_time < next_play ) - cpu_time = next_play; - next_play += play_period; - cpu_jsr( play_addr ); - } - - long count = duration - cpu_time; - cpu_time = duration; - Gb_Cpu::result_t result = RUN_GB_CPU( cpu, count ); - cpu_time -= cpu.remain(); - - if ( (result == Gb_Cpu::result_halt && cpu.r.pc != halt_addr) || - result == Gb_Cpu::result_badop ) - { - if ( cpu.r.pc > 0xFFFF ) - { - dprintf( "PC wrapped around\n" ); - cpu.r.pc &= 0xFFFF; - } - else - { - log_error(); - dprintf( "Bad opcode $%.2x at $%.4x\n", - (int) cpu.read( cpu.r.pc ), (int) cpu.r.pc ); - cpu.r.pc = (cpu.r.pc + 1) & 0xFFFF; - cpu_time += 6; - } - } - } - - // end time frame - - next_play -= cpu_time; - if ( next_play < 0 ) // could go negative if routine is taking too long to return - next_play = 0; - - if ( apu.end_frame( cpu_time ) && added_stereo ) - *added_stereo = true; - - return cpu_time; -} -
--- a/Plugins/Input/console/Gbs_Emu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,108 +0,0 @@ - -// Nintendo Game Boy GBS music file emulator - -// Game_Music_Emu 0.3.0 - -#ifndef GBS_EMU_H -#define GBS_EMU_H - -#include "Classic_Emu.h" -#include "Gb_Apu.h" -#include "Gb_Cpu.h" - -class Gbs_Emu : public Classic_Emu { -public: - - // Sets internal gain, where 1.0 results in almost no clamping. Default gain - // roughly matches volume of other emulators. - Gbs_Emu( double gain = 1.2 ); - - // GBS file header - struct header_t - { - char tag [3]; - byte vers; - byte track_count; - byte first_track; - byte load_addr [2]; - byte init_addr [2]; - byte play_addr [2]; - byte stack_ptr [2]; - byte timer_modulo; - byte timer_mode; - char game [32]; - char author [32]; - char copyright [32]; - - enum { song = 0 }; // no song titles - }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 112 ); - - // Load GBS data - blargg_err_t load( Data_Reader& ); - - // Load GBS using already-loaded header and remaining data - blargg_err_t load( header_t const&, Data_Reader& ); - - // Header for currently loaded GBS - header_t const& header() const { return header_; } - - // Equalizer profiles for Game Boy Color speaker and headphones - static equalizer_t const handheld_eq; - static equalizer_t const headphones_eq; - -public: - ~Gbs_Emu(); - const char** voice_names() const; - void start_track( int ); -protected: - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); - blip_time_t run_clocks( blip_time_t, bool* ); -private: - // rom - const byte* rom_bank; - blargg_vector<byte> rom; - void unload(); - int bank_count; - void set_bank( int ); - static void write_rom( Gbs_Emu*, gb_addr_t, int ); - static int read_rom( Gbs_Emu*, gb_addr_t ); - static int read_bank( Gbs_Emu*, gb_addr_t ); - - // state - gb_addr_t load_addr; - gb_addr_t init_addr; - gb_addr_t play_addr; - gb_addr_t stack_ptr; - int timer_modulo_init; - int timer_mode; - - // timer - gb_time_t cpu_time; - gb_time_t play_period; - gb_time_t next_play; - int double_speed; - - // hardware - Gb_Apu apu; - void set_timer( int tma, int tmc ); - static int read_io( Gbs_Emu*, gb_addr_t ); - static void write_io( Gbs_Emu*, gb_addr_t, int ); - static int read_unmapped( Gbs_Emu*, gb_addr_t ); - static void write_unmapped( Gbs_Emu*, gb_addr_t, int ); - - // large objects - - header_t header_; - byte hi_page [0x100]; - Gb_Cpu cpu; - void cpu_jsr( gb_addr_t ); - gb_time_t clock() const; - byte ram [0x4000]; - static int read_ram( Gbs_Emu*, gb_addr_t ); - static void write_ram( Gbs_Emu*, gb_addr_t, int ); -}; - -#endif -
--- a/Plugins/Input/console/Gym_Emu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,350 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Gym_Emu.h" - -#include <string.h> -#include "blargg_endian.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -double const gain = 3.0; -double const oversample_factor = 5 / 3.0; - -const long base_clock = 53700300; -const long clock_rate = base_clock / 15; - -Gym_Emu::Gym_Emu() -{ - data = NULL; - pos = NULL; -} - -Gym_Emu::~Gym_Emu() -{ - unload(); -} - -void Gym_Emu::unload() -{ - data = NULL; - pos = NULL; - set_track_ended( false ); - mem.clear(); -} - -blargg_err_t Gym_Emu::set_sample_rate( long sample_rate ) -{ - blip_eq_t eq( -32, 8000, sample_rate ); - apu.treble_eq( eq ); - apu.volume( 0.135 * gain ); - dac_synth.treble_eq( eq ); - dac_synth.volume( 0.125 / 256 * gain ); - - BLARGG_RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 60 ) ); - blip_buf.clock_rate( clock_rate ); - - double factor = Dual_Resampler::setup( oversample_factor, 0.990, gain ); - double fm_sample_rate = sample_rate * factor; - BLARGG_RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) ); - BLARGG_RETURN_ERR( Dual_Resampler::resize( sample_rate / 60 ) ); - - return Music_Emu::set_sample_rate( sample_rate ); -} - -void Gym_Emu::mute_voices( int mask ) -{ - Music_Emu::mute_voices( mask ); - fm.mute_voices( mask ); - dac_muted = mask & 0x40; - apu.output( (mask & 0x80) ? NULL : &blip_buf ); -} - -const char** Gym_Emu::voice_names() const -{ - static const char* names [] = { - "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" - }; - return names; -} - -static blargg_err_t check_header( const Gym_Emu::header_t& h, int* data_offset = NULL ) -{ - if ( memcmp( h.tag, "GYMX", 4 ) == 0 ) - { - if ( memcmp( h.packed, "\0\0\0\0", 4 ) != 0 ) - return "Packed GYM file not supported"; - - if ( data_offset ) - *data_offset = sizeof h; - } - else if ( h.tag [0] != 0 && h.tag [0] != 1 ) - { - // not a headerless GYM - // to do: more thorough check, or just require a damn header - return "Not a GYM file"; - } - - return blargg_success; -} - -blargg_err_t Gym_Emu::load_( const void* file, long data_offset, long file_size ) -{ - require( blip_buf.length() ); - - data = (const byte*) file + data_offset; - data_end = (const byte*) file + file_size; - - loop_begin = NULL; - if ( data_offset ) - header_ = *(header_t*) file; - else - memset( &header_, 0, sizeof header_ ); - - set_voice_count( 8 ); - set_track_count( 1 ); - remute_voices(); - - return blargg_success; -} - -blargg_err_t Gym_Emu::load( const void* file, long file_size ) -{ - unload(); - - if ( file_size < (int) sizeof (header_t) ) - return "Not a GYM file"; - - int data_offset = 0; - BLARGG_RETURN_ERR( check_header( *(header_t*) file, &data_offset ) ); - - return load_( file, data_offset, file_size ); -} - -blargg_err_t Gym_Emu::load( Data_Reader& in ) -{ - header_t h; - BLARGG_RETURN_ERR( in.read( &h, sizeof h ) ); - return load( h, in ); -} - -blargg_err_t Gym_Emu::load( const header_t& h, Data_Reader& in ) -{ - unload(); - - int data_offset = 0; - BLARGG_RETURN_ERR( check_header( h, &data_offset ) ); - - BLARGG_RETURN_ERR( mem.resize( sizeof h + in.remain() ) ); - memcpy( mem.begin(), &h, sizeof h ); - BLARGG_RETURN_ERR( in.read( &mem [sizeof h], mem.size() - sizeof h ) ); - - return load_( mem.begin(), data_offset, mem.size() ); -} - -long Gym_Emu::track_length() const -{ - long time = 0; - const byte* p = data; - while ( p < data_end ) - { - switch ( *p++ ) - { - case 0: - time++; - break; - - case 1: - case 2: - p += 2; - break; - - case 3: - p += 1; - break; - } - } - return time; -} - -void Gym_Emu::start_track( int track ) -{ - require( data ); - - Music_Emu::start_track( track ); - - pos = &data [0]; - loop_remain = get_le32( header_.loop_start ); - - prev_dac_count = 0; - dac_enabled = false; - dac_amp = -1; - - fm.reset(); - apu.reset(); - blip_buf.clear(); - Dual_Resampler::clear(); -} - -void Gym_Emu::run_dac( int dac_count ) -{ - // Guess beginning and end of sample and adjust rate and buffer position accordingly. - - // count dac samples in next frame - int next_dac_count = 0; - const byte* p = this->pos; - int cmd; - while ( (cmd = *p++) != 0 ) - { - int data = *p++; - if ( cmd <= 2 ) - ++p; - if ( cmd == 1 && data == 0x2A ) - next_dac_count++; - } - - // detect beginning and end of sample - int rate_count = dac_count; - int start = 0; - if ( !prev_dac_count && next_dac_count && dac_count < next_dac_count ) - { - rate_count = next_dac_count; - start = next_dac_count - dac_count; - } - else if ( prev_dac_count && !next_dac_count && dac_count < prev_dac_count ) - { - rate_count = prev_dac_count; - } - - // Evenly space samples within buffer section being used - blip_resampled_time_t period = - blip_buf.resampled_duration( clock_rate / 60 ) / rate_count; - - blip_resampled_time_t time = blip_buf.resampled_time( 0 ) + - period * start + (period >> 1); - - int dac_amp = this->dac_amp; - if ( dac_amp < 0 ) - dac_amp = dac_buf [0]; - - for ( int i = 0; i < dac_count; i++ ) - { - int delta = dac_buf [i] - dac_amp; - dac_amp += delta; - dac_synth.offset_resampled( time, delta, &blip_buf ); - time += period; - } - this->dac_amp = dac_amp; -} - -void Gym_Emu::parse_frame() -{ - int dac_count = 0; - const byte* pos = this->pos; - - if ( loop_remain && !--loop_remain ) - loop_begin = pos; // find loop on first time through sequence - - int cmd; - while ( (cmd = *pos++) != 0 ) - { - int data = *pos++; - if ( cmd == 1 ) - { - int data2 = *pos++; - if ( data != 0x2A ) - { - if ( data == 0x2B ) - dac_enabled = (data2 & 0x80) != 0; - - fm.write0( data, data2 ); - } - else if ( dac_count < (int) sizeof dac_buf ) - { - dac_buf [dac_count] = data2; - dac_count += dac_enabled; - } - } - else if ( cmd == 2 ) - { - fm.write1( data, *pos++ ); - } - else if ( cmd == 3 ) - { - apu.write_data( 0, data ); - } - else - { - // to do: many GYM streams are full of errors, and error count should - // reflect cases where music is really having problems - //log_error(); - --pos; // put data back - } - } - - // loop - if ( pos >= data_end ) - { - if ( pos > data_end ) - log_error(); - - if ( loop_begin ) - pos = loop_begin; - else - set_track_ended(); - } - this->pos = pos; - - // dac - if ( dac_count && !dac_muted ) - run_dac( dac_count ); - prev_dac_count = dac_count; -} - -int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ) -{ - if ( !track_ended() ) - parse_frame(); - - apu.end_frame( blip_time ); - - memset( buf, 0, sample_count * sizeof *buf ); - fm.run( sample_count >> 1, buf ); - - return sample_count; -} - -void Gym_Emu::play( long count, sample_t* out ) -{ - require( pos ); - - Dual_Resampler::play( count, out, blip_buf ); -} - -void Gym_Emu::skip( long count ) -{ - // to do: figure out why total muting generated access violation on MorphOS - const int buf_size = 1024; - sample_t buf [buf_size]; - - while ( count ) - { - int n = buf_size; - if ( n > count ) - n = count; - count -= n; - play( n, buf ); - } -} -
--- a/Plugins/Input/console/Gym_Emu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ - -// Sega Genesis GYM music file emulator - -// Game_Music_Emu 0.3.0 - -#ifndef GYM_EMU_H -#define GYM_EMU_H - -#include "Dual_Resampler.h" -#include "Ym2612_Emu.h" -#include "Music_Emu.h" -#include "Sms_Apu.h" - -class Gym_Emu : public Music_Emu, private Dual_Resampler { -public: - - // GYM file header - struct header_t - { - char tag [4]; - char song [32]; - char game [32]; - char copyright [32]; - char emulator [32]; - char dumper [32]; - char comment [256]; - byte loop_start [4]; // in 1/60 seconds, 0 if not looped - byte packed [4]; - - enum { track_count = 1 }; // one track per file - enum { author = 0 }; // no author field - }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 428 ); - - // Load GYM data - blargg_err_t load( Data_Reader& ); - - // Load GYM file using already-loaded header and remaining data - blargg_err_t load( header_t const&, Data_Reader& ); - blargg_err_t load( void const* data, long size ); // keeps pointer to data - - // Header for currently loaded GYM (cleared to zero if GYM lacks header) - header_t const& header() const { return header_; } - - // Length of track in 1/60 seconds - enum { gym_rate = 60 }; // GYM time units (frames) per second - long track_length() const; - -public: - typedef Music_Emu::sample_t sample_t; - Gym_Emu(); - ~Gym_Emu(); - blargg_err_t set_sample_rate( long sample_rate ); - void mute_voices( int ); - void start_track( int ); - void play( long count, sample_t* ); - const char** voice_names() const; - void skip( long count ); -public: - // deprecated - blargg_err_t init( long r, double gain = 1.5, double oversample = 5 / 3.0 ) - { - return set_sample_rate( r ); - } -protected: - int play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ); -private: - // sequence data begin, loop begin, current position, end - const byte* data; - const byte* loop_begin; - const byte* pos; - const byte* data_end; - long loop_remain; // frames remaining until loop beginning has been located - blargg_vector<byte> mem; - header_t header_; - blargg_err_t load_( const void* file, long data_offset, long file_size ); - void unload(); - void parse_frame(); - - // dac (pcm) - int dac_amp; - int prev_dac_count; - bool dac_enabled; - bool dac_muted; - void run_dac( int ); - - // sound - Blip_Buffer blip_buf; - Ym2612_Emu fm; - Blip_Synth<blip_med_quality,1> dac_synth; - Sms_Apu apu; - byte dac_buf [1024]; -}; - -#endif -
--- a/Plugins/Input/console/Makefile.am Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -lib_LTLIBRARIES = libconsole.la - -libdir = $(plugindir)/$(INPUT_PLUGIN_DIR) - -libconsole_la_SOURCES = \ - Blip_Buffer.cpp \ - Classic_Emu.cpp \ - Dual_Resampler.cpp \ - Fir_Resampler.cpp \ - Gb_Apu.cpp \ - Gb_Cpu.cpp \ - Gb_Oscs.cpp \ - Gbs_Emu.cpp \ - Gym_Emu.cpp \ - Multi_Buffer.cpp \ - Music_Emu.cpp \ - Nes_Apu.cpp \ - Nes_Cpu.cpp \ - Nes_Fme7_Apu.cpp \ - Nes_Namco_Apu.cpp \ - Nes_Oscs.cpp \ - Nes_Vrc6_Apu.cpp \ - Nsfe_Emu.cpp \ - Nsf_Emu.cpp \ - Sms_Apu.cpp \ - Snes_Spc.cpp \ - Spc_Cpu.cpp \ - Spc_Dsp.cpp \ - Spc_Emu.cpp \ - Vgm_Emu.cpp \ - abstract_file.cpp \ - Vfs_File.cpp \ - Gzip_File.cpp \ - Vgm_Emu_Impl.cpp \ - Ym2413_Emu.cpp \ - Ym2612_Emu.cpp \ - Track_Emu.cpp \ - Audacious_Driver.cpp - -libconsole_la_LDFLAGS = $(PLUGIN_LDFLAGS) -libconsole_la_LIBADD = -lz $(GTK_LIBS) $(top_builddir)/libaudacious/libaudacious.la -INCLUDES = $(GTK_CFLAGS) $(ARCH_DEFINES) -I$(top_srcdir)/intl -I$(top_srcdir) -
--- a/Plugins/Input/console/Multi_Buffer.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,215 +0,0 @@ - -// Blip_Buffer 0.4.0. http://www.slack.net/~ant/ - -#include "Multi_Buffer.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) -{ - length_ = 0; - sample_rate_ = 0; - channels_changed_count_ = 1; -} - -blargg_err_t Multi_Buffer::set_channel_count( int ) -{ - return blargg_success; -} - -Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) -{ -} - -Mono_Buffer::~Mono_Buffer() -{ -} - -blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) -{ - BLARGG_RETURN_ERR( buf.set_sample_rate( rate, msec ) ); - return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); -} - -// Silent_Buffer - -Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse -{ - chan.left = NULL; - chan.center = NULL; - chan.right = NULL; -} - -// Mono_Buffer - -Mono_Buffer::channel_t Mono_Buffer::channel( int ) -{ - channel_t ch; - ch.center = &buf; - ch.left = &buf; - ch.right = &buf; - return ch; -} - -void Mono_Buffer::end_frame( blip_time_t t, bool ) -{ - buf.end_frame( t ); -} - -// Stereo_Buffer - -Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) -{ - chan.center = &bufs [0]; - chan.left = &bufs [1]; - chan.right = &bufs [2]; -} - -Stereo_Buffer::~Stereo_Buffer() -{ -} - -blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec ) -{ - for ( int i = 0; i < buf_count; i++ ) - BLARGG_RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); - return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); -} - -void Stereo_Buffer::clock_rate( long rate ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clock_rate( rate ); -} - -void Stereo_Buffer::bass_freq( int bass ) -{ - for ( unsigned i = 0; i < buf_count; i++ ) - bufs [i].bass_freq( bass ); -} - -void Stereo_Buffer::clear() -{ - stereo_added = false; - was_stereo = false; - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clear(); -} - -void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo ) -{ - for ( unsigned i = 0; i < buf_count; i++ ) - bufs [i].end_frame( clock_count ); - - stereo_added |= stereo; -} - -long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) -{ - require( !(count & 1) ); // count must be even - count = (unsigned) count / 2; - - long avail = bufs [0].samples_avail(); - if ( count > avail ) - count = avail; - if ( count ) - { - if ( stereo_added || was_stereo ) - { - mix_stereo( out, count ); - - bufs [0].remove_samples( count ); - bufs [1].remove_samples( count ); - bufs [2].remove_samples( count ); - } - else - { - mix_mono( out, count ); - - bufs [0].remove_samples( count ); - - bufs [1].remove_silence( count ); - bufs [2].remove_silence( count ); - } - - // to do: this might miss opportunities for optimization - if ( !bufs [0].samples_avail() ) { - was_stereo = stereo_added; - stereo_added = false; - } - } - - return count * 2; -} - -#include BLARGG_ENABLE_OPTIMIZER - -void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count ) -{ - Blip_Reader left; - Blip_Reader right; - Blip_Reader center; - - left.begin( bufs [1] ); - right.begin( bufs [2] ); - int bass = center.begin( bufs [0] ); - - while ( count-- ) - { - int c = center.read(); - long l = c + left.read(); - long r = c + right.read(); - center.next( bass ); - out [0] = l; - out [1] = r; - out += 2; - - if ( (BOOST::int16_t) l != l ) - out [-2] = 0x7FFF - (l >> 24); - - left.next( bass ); - right.next( bass ); - - if ( (BOOST::int16_t) r != r ) - out [-1] = 0x7FFF - (r >> 24); - } - - center.end( bufs [0] ); - right.end( bufs [2] ); - left.end( bufs [1] ); -} - -void Stereo_Buffer::mix_mono( blip_sample_t* out, long count ) -{ - Blip_Reader in; - int bass = in.begin( bufs [0] ); - - while ( count-- ) - { - long s = in.read(); - in.next( bass ); - out [0] = s; - out [1] = s; - out += 2; - - if ( (BOOST::int16_t) s != s ) { - s = 0x7FFF - (s >> 24); - out [-2] = s; - out [-1] = s; - } - } - - in.end( bufs [0] ); -} -
--- a/Plugins/Input/console/Multi_Buffer.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,175 +0,0 @@ - -// Multi-channel sound buffer interface, and basic mono and stereo buffers - -// Blip_Buffer 0.4.0 - -#ifndef MULTI_BUFFER_H -#define MULTI_BUFFER_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -// Interface to one or more Blip_Buffers mapped to one or more channels -// consisting of left, center, and right buffers. -class Multi_Buffer { -public: - Multi_Buffer( int samples_per_frame ); - virtual ~Multi_Buffer() { } - - // Set the number of channels available - virtual blargg_err_t set_channel_count( int ); - - // Get indexed channel, from 0 to channel count - 1 - struct channel_t { - Blip_Buffer* center; - Blip_Buffer* left; - Blip_Buffer* right; - }; - virtual channel_t channel( int index ) = 0; - - // See Blip_Buffer.h - virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0; - virtual void clock_rate( long ) = 0; - virtual void bass_freq( int ) = 0; - virtual void clear() = 0; - long sample_rate() const; - - // Length of buffer, in milliseconds - int length() const; - - // See Blip_Buffer.h. For optimal operation, pass false for 'added_stereo' - // if nothing was added to the left and right buffers of any channel for - // this time frame. - virtual void end_frame( blip_time_t, bool added_stereo = true ) = 0; - - // Number of samples per output frame (1 = mono, 2 = stereo) - int samples_per_frame() const; - - // Count of changes to channel configuration. Incremented whenever - // a change is made to any of the Blip_Buffers for any channel. - unsigned channels_changed_count() { return channels_changed_count_; } - - // See Blip_Buffer.h - virtual long read_samples( blip_sample_t*, long ) = 0; - virtual long samples_avail() const = 0; - -protected: - void channels_changed() { channels_changed_count_++; } -private: - // noncopyable - Multi_Buffer( const Multi_Buffer& ); - Multi_Buffer& operator = ( const Multi_Buffer& ); - - unsigned channels_changed_count_; - long sample_rate_; - int length_; - int const samples_per_frame_; -}; - -// Uses a single buffer and outputs mono samples. -class Mono_Buffer : public Multi_Buffer { - Blip_Buffer buf; -public: - Mono_Buffer(); - ~Mono_Buffer(); - - // Buffer used for all channels - Blip_Buffer* center() { return &buf; } - - // See Multi_Buffer - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int ); - void end_frame( blip_time_t, bool unused = true ); - long samples_avail() const; - long read_samples( blip_sample_t*, long ); -}; - -// Uses three buffers (one for center) and outputs stereo sample pairs. -class Stereo_Buffer : public Multi_Buffer { -public: - Stereo_Buffer(); - ~Stereo_Buffer(); - - // Buffers used for all channels - Blip_Buffer* center() { return &bufs [0]; } - Blip_Buffer* left() { return &bufs [1]; } - Blip_Buffer* right() { return &bufs [2]; } - - // See Multi_Buffer - blargg_err_t set_sample_rate( long, int msec = blip_default_length ); - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int index ); - void end_frame( blip_time_t, bool added_stereo = true ); - - long samples_avail() const; - long read_samples( blip_sample_t*, long ); - -private: - enum { buf_count = 3 }; - Blip_Buffer bufs [buf_count]; - channel_t chan; - bool stereo_added; - bool was_stereo; - - void mix_stereo( blip_sample_t*, long ); - void mix_mono( blip_sample_t*, long ); -}; - -// Silent_Buffer generates no samples, useful where no sound is wanted -class Silent_Buffer : public Multi_Buffer { - channel_t chan; -public: - Silent_Buffer(); - - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); - void clock_rate( long ) { } - void bass_freq( int ) { } - void clear() { } - channel_t channel( int ) { return chan; } - void end_frame( blip_time_t, bool unused = true ) { } - long samples_avail() const { return 0; } - long read_samples( blip_sample_t*, long ) { return 0; } -}; - - -// End of public interface - -inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) -{ - sample_rate_ = rate; - length_ = msec; - return blargg_success; -} - -inline blargg_err_t Silent_Buffer::set_sample_rate( long rate, int msec ) -{ - return Multi_Buffer::set_sample_rate( rate, msec ); -} - -inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } - -inline long Stereo_Buffer::samples_avail() const { return bufs [0].samples_avail() * 2; } - -inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int ) { return chan; } - -inline long Multi_Buffer::sample_rate() const { return sample_rate_; } - -inline int Multi_Buffer::length() const { return length_; } - -inline void Mono_Buffer::clock_rate( long rate ) { buf.clock_rate( rate ); } - -inline void Mono_Buffer::clear() { buf.clear(); } - -inline void Mono_Buffer::bass_freq( int freq ) { buf.bass_freq( freq ); } - -inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } - -inline long Mono_Buffer::samples_avail() const { return buf.samples_avail(); } - -#endif -
--- a/Plugins/Input/console/Music_Emu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Music_Emu.h" - -#include <string.h> - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -Music_Emu::equalizer_t const Music_Emu::tv_eq = { -8.0, 180 }; - -Music_Emu::Music_Emu() -{ - equalizer_.treble = -1.0; - equalizer_.bass = 60; - sample_rate_ = 0; - voice_count_ = 0; - mute_mask_ = 0; - track_count_ = 0; - error_count_ = 0; - track_ended_ = false; -} - -Music_Emu::~Music_Emu() -{ -} - -blargg_err_t Music_Emu::load_file( const char* path ) -{ - Std_File_Reader in; - BLARGG_RETURN_ERR( in.open( path ) ); - return load( in ); -} - -void Music_Emu::skip( long count ) -{ - const int buf_size = 1024; - sample_t buf [buf_size]; - - const long threshold = 30000; - if ( count > threshold ) - { - int saved_mute = mute_mask_; - mute_voices( ~0 ); - - while ( count > threshold / 2 ) - { - play( buf_size, buf ); - count -= buf_size; - } - - mute_voices( saved_mute ); - } - - while ( count ) - { - int n = buf_size; - if ( n > count ) - n = count; - count -= n; - play( n, buf ); - } -} - -const char** Music_Emu::voice_names() const -{ - static const char* names [] = { - "Voice 1", "Voice 2", "Voice 3", "Voice 4", - "Voice 5", "Voice 6", "Voice 7", "Voice 8" - }; - return names; -} -
--- a/Plugins/Input/console/Music_Emu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,137 +0,0 @@ - -// Game music emulator interface base class - -// Game_Music_Emu 0.3.0 - -#ifndef MUSIC_EMU_H -#define MUSIC_EMU_H - -#include "blargg_common.h" -#include "abstract_file.h" -class Multi_Buffer; - -class Music_Emu { -public: - - // Initialize emulator with specified sample rate. Currently should only be - // called once. - virtual blargg_err_t set_sample_rate( long sample_rate ) = 0; - - // Load music file - blargg_err_t load_file( const char* path ); - - // Start a track, where 0 is the first track. Might un-mute any muted voices. - virtual void start_track( int ) = 0; - - // Generate 'count' samples info 'buf'. Output is in stereo unless using custom - // buffer that generates mono output. - typedef short sample_t; - virtual void play( long count, sample_t* buf ) = 0; - -// Additional optional features - - // Request use of custom multichannel buffer. Only supported by "classic" emulators; - // on others this has no effect. Should be called only once *before* set_sample_rate(). - virtual void set_buffer( Multi_Buffer* ) { } - - // Load music file data from custom source - virtual blargg_err_t load( Data_Reader& ) = 0; - - // Sample rate sound is generated at - long sample_rate() const; - - // Number of voices used by currently loaded file - int voice_count() const; - - // Names of voices - virtual const char** voice_names() const; - - // Mute voice n if bit n (1 << n) of mask is set - virtual void mute_voices( int mask ); - - // Frequency equalizer parameters (see notes.txt) - struct equalizer_t { - double treble; // -50.0 = muffled, 0 = flat, +5.0 = extra-crisp - long bass; // 1 = full bass, 90 = average, 16000 = almost no bass - }; - - // Current frequency equalizater parameters - const equalizer_t& equalizer() const; - - // Set frequency equalizer parameters - virtual void set_equalizer( equalizer_t const& ); - - // Equalizer settings for TV speaker - static equalizer_t const tv_eq; - - // Number of tracks. Zero if file hasn't been loaded yet. - int track_count() const; - - // Skip 'count' samples - virtual void skip( long count ); - - // True if a track was started and has since ended. Currently only logged - // format tracks (VGM, GYM) without loop points have an ending. - bool track_ended() const; - - // Number of errors encountered while playing track due to undefined CPU - // instructions in emulated formats and undefined stream events in - // logged formats. - int error_count() const; - - Music_Emu(); - virtual ~Music_Emu(); - -protected: - typedef BOOST::uint8_t byte; - void set_voice_count( int n ) { voice_count_ = n; } - void set_track_count( int n ) { track_count_ = n; } - void set_track_ended( bool b = true ) { track_ended_ = b; } - void log_error() { error_count_++; } - void remute_voices(); -private: - // noncopyable - Music_Emu( const Music_Emu& ); - Music_Emu& operator = ( const Music_Emu& ); - - equalizer_t equalizer_; - long sample_rate_; - int voice_count_; - int mute_mask_; - int track_count_; - int error_count_; - bool track_ended_; -}; - -// Deprecated -typedef Data_Reader Emu_Reader; -typedef Std_File_Reader Emu_Std_Reader; -typedef Mem_File_Reader Emu_Mem_Reader; - -inline int Music_Emu::error_count() const { return error_count_; } -inline int Music_Emu::voice_count() const { return voice_count_; } -inline int Music_Emu::track_count() const { return track_count_; } -inline bool Music_Emu::track_ended() const { return track_ended_; } -inline void Music_Emu::mute_voices( int mask ) { mute_mask_ = mask; } -inline void Music_Emu::remute_voices() { mute_voices( mute_mask_ ); } -inline const Music_Emu::equalizer_t& Music_Emu::equalizer() const { return equalizer_; } -inline void Music_Emu::set_equalizer( const equalizer_t& eq ) { equalizer_ = eq; } -inline long Music_Emu::sample_rate() const { return sample_rate_; } - -inline blargg_err_t Music_Emu::set_sample_rate( long r ) -{ - assert( !sample_rate_ ); // sample rate can't be changed once set - sample_rate_ = r; - return blargg_success; -} - -inline void Music_Emu::start_track( int track ) -{ - assert( (unsigned) track <= (unsigned) track_count() ); - assert( sample_rate_ ); // set_sample_rate() must have been called first - track_ended_ = false; - error_count_ = 0; -} - -#endif -
--- a/Plugins/Input/console/Nes_Apu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,383 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ - -#include "Nes_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -int const amp_range = 15; - -Nes_Apu::Nes_Apu() : - square1( &square_synth ), - square2( &square_synth ) -{ - dmc.apu = this; - dmc.rom_reader = NULL; - irq_notifier_ = NULL; - - oscs [0] = &square1; - oscs [1] = &square2; - oscs [2] = ▵ - oscs [3] = &noise; - oscs [4] = &dmc; - - output( NULL ); - volume( 1.0 ); - reset( false ); -} - -Nes_Apu::~Nes_Apu() -{ -} - -void Nes_Apu::treble_eq( const blip_eq_t& eq ) -{ - square_synth.treble_eq( eq ); - triangle.synth.treble_eq( eq ); - noise.synth.treble_eq( eq ); - dmc.synth.treble_eq( eq ); -} - -void Nes_Apu::enable_nonlinear( double v ) -{ - dmc.nonlinear = true; - square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v ); - - const double tnd = 0.48 / 202 * nonlinear_tnd_gain(); - triangle.synth.volume( 3.0 * tnd ); - noise.synth.volume( 2.0 * tnd ); - dmc.synth.volume( tnd ); - - square1 .last_amp = 0; - square2 .last_amp = 0; - triangle.last_amp = 0; - noise .last_amp = 0; - dmc .last_amp = 0; -} - -void Nes_Apu::volume( double v ) -{ - dmc.nonlinear = false; - square_synth.volume( 0.1128 / amp_range * v ); - triangle.synth.volume( 0.12765 / amp_range * v ); - noise.synth.volume( 0.0741 / amp_range * v ); - dmc.synth.volume( 0.42545 / 127 * v ); -} - -void Nes_Apu::output( Blip_Buffer* buffer ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buffer ); -} - -void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) -{ - // to do: time pal frame periods exactly - frame_period = pal_mode ? 8314 : 7458; - dmc.pal_mode = pal_mode; - - square1.reset(); - square2.reset(); - triangle.reset(); - noise.reset(); - dmc.reset(); - - last_time = 0; - last_dmc_time = 0; - osc_enables = 0; - irq_flag = false; - earliest_irq_ = no_irq; - frame_delay = 1; - write_register( 0, 0x4017, 0x00 ); - write_register( 0, 0x4015, 0x00 ); - - for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ ) - write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); - - dmc.dac = initial_dmc_dac; - //if ( !dmc.nonlinear ) // to do: remove? - // dmc.last_amp = initial_dmc_dac; // prevent output transition -} - -void Nes_Apu::irq_changed() -{ - nes_time_t new_irq = dmc.next_irq; - if ( dmc.irq_flag | irq_flag ) { - new_irq = 0; - } - else if ( new_irq > next_irq ) { - new_irq = next_irq; - } - - if ( new_irq != earliest_irq_ ) { - earliest_irq_ = new_irq; - if ( irq_notifier_ ) - irq_notifier_( irq_data ); - } -} - -// frames - -void Nes_Apu::run_until( nes_time_t end_time ) -{ - require( end_time >= last_dmc_time ); - if ( end_time > next_dmc_read_time() ) - { - nes_time_t start = last_dmc_time; - last_dmc_time = end_time; - dmc.run( start, end_time ); - } -} - -void Nes_Apu::run_until_( nes_time_t end_time ) -{ - require( end_time >= last_time ); - - if ( end_time == last_time ) - return; - - if ( last_dmc_time < end_time ) - { - nes_time_t start = last_dmc_time; - last_dmc_time = end_time; - dmc.run( start, end_time ); - } - - while ( true ) - { - // earlier of next frame time or end time - nes_time_t time = last_time + frame_delay; - if ( time > end_time ) - time = end_time; - frame_delay -= time - last_time; - - // run oscs to present - square1.run( last_time, time ); - square2.run( last_time, time ); - triangle.run( last_time, time ); - noise.run( last_time, time ); - last_time = time; - - if ( time == end_time ) - break; // no more frames to run - - // take frame-specific actions - frame_delay = frame_period; - switch ( frame++ ) - { - case 0: - if ( !(frame_mode & 0xc0) ) { - next_irq = time + frame_period * 4 + 1; - irq_flag = true; - } - // fall through - case 2: - // clock length and sweep on frames 0 and 2 - square1.clock_length( 0x20 ); - square2.clock_length( 0x20 ); - noise.clock_length( 0x20 ); - triangle.clock_length( 0x80 ); // different bit for halt flag on triangle - - square1.clock_sweep( -1 ); - square2.clock_sweep( 0 ); - break; - - case 1: - // frame 1 is slightly shorter - frame_delay -= 2; - break; - - case 3: - frame = 0; - - // frame 3 is almost twice as long in mode 1 - if ( frame_mode & 0x80 ) - frame_delay += frame_period - 6; - break; - } - - // clock envelopes and linear counter every frame - triangle.clock_linear_counter(); - square1.clock_envelope(); - square2.clock_envelope(); - noise.clock_envelope(); - } -} - -// to do: remove -static long abs_time; - -template<class T> -inline void zero_apu_osc( T* osc, nes_time_t time ) -{ - Blip_Buffer* output = osc->output; - int last_amp = osc->last_amp; - osc->last_amp = 0; - if ( output && last_amp ) - osc->synth.offset( time, -last_amp, output ); -} - -void Nes_Apu::end_frame( nes_time_t end_time ) -{ - if ( end_time > last_time ) - run_until_( end_time ); - - abs_time += end_time; - - if ( dmc.nonlinear ) - { - zero_apu_osc( &square1, last_time ); - zero_apu_osc( &square2, last_time ); - zero_apu_osc( &triangle, last_time ); - zero_apu_osc( &noise, last_time ); - zero_apu_osc( &dmc, last_time ); - } - - // make times relative to new frame - last_time -= end_time; - require( last_time >= 0 ); - - last_dmc_time -= end_time; - require( last_dmc_time >= 0 ); - - if ( next_irq != no_irq ) { - next_irq -= end_time; - assert( next_irq >= 0 ); - } - if ( dmc.next_irq != no_irq ) { - dmc.next_irq -= end_time; - assert( dmc.next_irq >= 0 ); - } - if ( earliest_irq_ != no_irq ) { - earliest_irq_ -= end_time; - if ( earliest_irq_ < 0 ) - earliest_irq_ = 0; - } -} - -// registers - -static const unsigned char length_table [0x20] = { - 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, - 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, - 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, - 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E -}; - -void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) -{ - require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx) - require( (unsigned) data <= 0xff ); - - // Ignore addresses outside range - if ( addr < start_addr || end_addr < addr ) - return; - - run_until_( time ); - - if ( addr < 0x4014 ) - { - // Write to channel - int osc_index = (addr - start_addr) >> 2; - Nes_Osc* osc = oscs [osc_index]; - - int reg = addr & 3; - osc->regs [reg] = data; - osc->reg_written [reg] = true; - - if ( osc_index == 4 ) - { - // handle DMC specially - dmc.write_register( reg, data ); - } - else if ( reg == 3 ) - { - // load length counter - if ( (osc_enables >> osc_index) & 1 ) - osc->length_counter = length_table [(data >> 3) & 0x1f]; - - // reset square phase - if ( osc_index < 2 ) - ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1; - } - } - else if ( addr == 0x4015 ) - { - // Channel enables - for ( int i = osc_count; i--; ) - if ( !((data >> i) & 1) ) - oscs [i]->length_counter = 0; - - bool recalc_irq = dmc.irq_flag; - dmc.irq_flag = false; - - int old_enables = osc_enables; - osc_enables = data; - if ( !(data & 0x10) ) { - dmc.next_irq = no_irq; - recalc_irq = true; - } - else if ( !(old_enables & 0x10) ) { - dmc.start(); // dmc just enabled - } - - if ( recalc_irq ) - irq_changed(); - } - else if ( addr == 0x4017 ) - { - // Frame mode - frame_mode = data; - - bool irq_enabled = !(data & 0x40); - irq_flag &= irq_enabled; - next_irq = no_irq; - - // mode 1 - frame_delay = (frame_delay & 1); - frame = 0; - - if ( !(data & 0x80) ) - { - // mode 0 - frame = 1; - frame_delay += frame_period; - if ( irq_enabled ) - next_irq = time + frame_delay + frame_period * 3; - } - - irq_changed(); - } -} - -int Nes_Apu::read_status( nes_time_t time ) -{ - run_until_( time - 1 ); - - int result = (dmc.irq_flag << 7) | (irq_flag << 6); - - for ( int i = 0; i < osc_count; i++ ) - if ( oscs [i]->length_counter ) - result |= 1 << i; - - run_until_( time ); - - if ( irq_flag ) { - irq_flag = false; - irq_changed(); - } - - return result; -} -
--- a/Plugins/Input/console/Nes_Apu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,174 +0,0 @@ - -// NES 2A03 APU sound chip emulator - -// Nes_Snd_Emu 0.1.7 - -#ifndef NES_APU_H -#define NES_APU_H - -typedef long nes_time_t; // CPU clock cycle count -typedef unsigned nes_addr_t; // 16-bit memory address - -#include "Nes_Oscs.h" - -struct apu_snapshot_t; -class Nonlinear_Buffer; - -class Nes_Apu { -public: - Nes_Apu(); - ~Nes_Apu(); - - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); - - // Set memory reader callback used by DMC oscillator to fetch samples. - // When callback is invoked, 'user_data' is passed unchanged as the - // first parameter. - void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL ); - - // All time values are the number of CPU clock cycles relative to the - // beginning of the current time frame. Before resetting the CPU clock - // count, call end_frame( last_cpu_time ). - - // Write to register (0x4000-0x4017, except 0x4014 and 0x4016) - enum { start_addr = 0x4000 }; - enum { end_addr = 0x4017 }; - void write_register( nes_time_t, nes_addr_t, int data ); - - // Read from status register at 0x4015 - enum { status_addr = 0x4015 }; - int read_status( nes_time_t ); - - // Run all oscillators up to specified time, end current time frame, then - // start a new time frame at time 0. Time frames have no effect on emulation - // and each can be whatever length is convenient. - void end_frame( nes_time_t ); - -// Additional optional features (can be ignored without any problem) - - // Reset internal frame counter, registers, and all oscillators. - // Use PAL timing if pal_timing is true, otherwise use NTSC timing. - // Set the DMC oscillator's initial DAC value to initial_dmc_dac without - // any audible click. - void reset( bool pal_timing = false, int initial_dmc_dac = 0 ); - - // Save/load snapshot of exact emulation state - void save_snapshot( apu_snapshot_t* out ) const; - void load_snapshot( apu_snapshot_t const& ); - - // Set overall volume (default is 1.0) - void volume( double ); - - // Set treble equalization (see notes.txt) - void treble_eq( const blip_eq_t& ); - - // Set sound output of specific oscillator to buffer. If buffer is NULL, - // the specified oscillator is muted and emulation accuracy is reduced. - // The oscillators are indexed as follows: 0) Square 1, 1) Square 2, - // 2) Triangle, 3) Noise, 4) DMC. - enum { osc_count = 5 }; - void osc_output( int index, Blip_Buffer* buffer ); - - // Set IRQ time callback that is invoked when the time of earliest IRQ - // may have changed, or NULL to disable. When callback is invoked, - // 'user_data' is passed unchanged as the first parameter. - void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); - - // Get time that APU-generated IRQ will occur if no further register reads - // or writes occur. If IRQ is already pending, returns irq_waiting. If no - // IRQ will occur, returns no_irq. - enum { no_irq = LONG_MAX / 2 + 1 }; - enum { irq_waiting = 0 }; - nes_time_t earliest_irq( nes_time_t ) const; - - // Count number of DMC reads that would occur if 'run_until( t )' were executed. - // If last_read is not NULL, set *last_read to the earliest time that - // 'count_dmc_reads( time )' would result in the same result. - int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const; - - // Time when next DMC memory read will occur - nes_time_t next_dmc_read_time() const; - - // Run DMC until specified time, so that any DMC memory reads can be - // accounted for (i.e. inserting CPU wait states). - void run_until( nes_time_t ); - -// End of public interface. -private: - friend class Nes_Nonlinearizer; - void enable_nonlinear( double volume ); - static double nonlinear_tnd_gain() { return 0.75; } -private: - friend struct Nes_Dmc; - - // noncopyable - Nes_Apu( const Nes_Apu& ); - Nes_Apu& operator = ( const Nes_Apu& ); - - Nes_Osc* oscs [osc_count]; - Nes_Square square1; - Nes_Square square2; - Nes_Noise noise; - Nes_Triangle triangle; - Nes_Dmc dmc; - - nes_time_t last_time; // has been run until this time in current frame - nes_time_t last_dmc_time; - nes_time_t earliest_irq_; - nes_time_t next_irq; - int frame_period; - int frame_delay; // cycles until frame counter runs next - int frame; // current frame (0-3) - int osc_enables; - int frame_mode; - bool irq_flag; - void (*irq_notifier_)( void* user_data ); - void* irq_data; - Nes_Square::Synth square_synth; // shared by squares - - void irq_changed(); - void state_restored(); - void run_until_( nes_time_t ); -}; - -inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf ) -{ - assert( (unsigned) osc < osc_count ); - oscs [osc]->output = buf; -} - -inline nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const -{ - return earliest_irq_; -} - -inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data ) -{ - dmc.rom_reader_data = user_data; - dmc.rom_reader = func; -} - -inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data ) -{ - irq_notifier_ = func; - irq_data = user_data; -} - -inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const -{ - return dmc.count_reads( time, last_read ); -} - -inline nes_time_t Nes_Dmc::next_read_time() const -{ - if ( length_counter == 0 ) - return Nes_Apu::no_irq; // not reading - - return apu->last_dmc_time + delay + long (bits_remain - 1) * period; -} - -inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } - -#endif -
--- a/Plugins/Input/console/Nes_Cpu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,950 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/nes-emu/ - -#include "Nes_Cpu.h" - -#include <string.h> -#include <limits.h> - -#include "blargg_endian.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -#if BLARGG_NONPORTABLE - #define PAGE_OFFSET( addr ) (addr) -#else - #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) -#endif - -Nes_Cpu::Nes_Cpu() -{ - callback_data = NULL; - reset(); -} - -inline void Nes_Cpu::set_code_page( int i, uint8_t const* p ) -{ - code_map [i] = p - PAGE_OFFSET( i * page_size ); -} - -void Nes_Cpu::reset( const void* unmapped_code_page, reader_t read, writer_t write ) -{ - r.status = 0; - r.sp = 0; - r.pc = 0; - r.a = 0; - r.x = 0; - r.y = 0; - - clock_count = 0; - base_time = 0; - clock_limit = 0; - irq_time_ = LONG_MAX / 2 + 1; - end_time_ = LONG_MAX / 2 + 1; - - for ( int i = 0; i < page_count + 1; i++ ) - { - set_code_page( i, (uint8_t*) unmapped_code_page ); - data_reader [i] = read; - data_writer [i] = write; - } -} - -void Nes_Cpu::map_code( nes_addr_t start, unsigned long size, const void* data ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - require( start + size <= 0x10000 ); - - unsigned first_page = start / page_size; - for ( unsigned i = size / page_size; i--; ) - set_code_page( first_page + i, (uint8_t*) data + i * page_size ); -} - -void Nes_Cpu::set_reader( nes_addr_t start, unsigned long size, reader_t func ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - require( start + size <= 0x10000 + page_size ); - - unsigned first_page = start / page_size; - for ( unsigned i = size / page_size; i--; ) - data_reader [first_page + i] = func; -} - -void Nes_Cpu::set_writer( nes_addr_t start, unsigned long size, writer_t func ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - require( start + size <= 0x10000 + page_size ); - - unsigned first_page = start / page_size; - for ( unsigned i = size / page_size; i--; ) - data_writer [first_page + i] = func; -} - -// Note: 'addr' is evaulated more than once in the following macros, so it -// must not contain side-effects. - -#define READ( addr ) (data_reader [(addr) >> page_bits]( callback_data, addr )) -#define WRITE( addr, data ) (data_writer [(addr) >> page_bits]( callback_data, addr, data )) - -#define READ_LOW( addr ) (low_mem [int (addr)]) -#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) - -#define READ_PROG( addr ) (code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )]) -#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) ) - -#define SET_SP( v ) (sp = ((v) + 1) | 0x100) -#define GET_SP() ((sp - 1) & 0xff) - -#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) - -int Nes_Cpu::read( nes_addr_t addr ) -{ - return READ( addr ); -} - -void Nes_Cpu::write( nes_addr_t addr, int value ) -{ - WRITE( addr, value ); -} - -#ifndef NES_CPU_GLUE_ONLY - -static const unsigned char clock_table [256] = { -// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 - 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 - 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 - 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 - 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A - 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B - 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D - 2,6,3,8,3,3,5,5,2,2,2,2,4,4,6,6,// E - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F -}; - -#include BLARGG_ENABLE_OPTIMIZER - -Nes_Cpu::result_t Nes_Cpu::run( nes_time_t end ) -{ - set_end_time( end ); - - volatile result_t result = result_cycles; - -#if BLARGG_CPU_POWERPC - // cache commonly-used values in registers - long clock_count = this->clock_count; - writer_t* const data_writer = this->data_writer; - reader_t* const data_reader = this->data_reader; - uint8_t* const low_mem = this->low_mem; -#endif - - // registers - unsigned pc = r.pc; - int sp; - SET_SP( r.sp ); - int a = r.a; - int x = r.x; - int y = r.y; - - // status flags - - const int st_n = 0x80; - const int st_v = 0x40; - const int st_r = 0x20; - const int st_b = 0x10; - const int st_d = 0x08; - const int st_i = 0x04; - const int st_z = 0x02; - const int st_c = 0x01; - - #define IS_NEG (nz & 0x880) - - #define CALC_STATUS( out ) do { \ - out = status & (st_v | st_d | st_i); \ - out |= (c >> 8) & st_c; \ - if ( IS_NEG ) out |= st_n; \ - if ( !(nz & 0xFF) ) out |= st_z; \ - } while ( false ) - - #define SET_STATUS( in ) do { \ - status = in & (st_v | st_d | st_i); \ - c = in << 8; \ - nz = (in << 4) & 0x800; \ - nz |= ~in & st_z; \ - } while ( false ) - - int status; - int c; // carry set if (c & 0x100) != 0 - int nz; // Z set if (nz & 0xff) == 0, N set if (nz & 0x880) != 0 - { - int temp = r.status; - SET_STATUS( temp ); - } - - goto loop; -dec_clock_loop: - clock_count--; -loop: - - assert( unsigned (GET_SP()) < 0x100 ); - assert( unsigned (a) < 0x100 ); - assert( unsigned (x) < 0x100 ); - assert( unsigned (y) < 0x100 ); - - uint8_t const* page = code_map [pc >> page_bits]; - unsigned opcode = page [PAGE_OFFSET( pc )]; - unsigned data = page [PAGE_OFFSET( pc ) + 1]; - pc++; - - // page crossing - //check( opcode == 0x60 || &READ_PROG( pc ) == &page [PAGE_OFFSET( pc )] ); - - if ( clock_count >= clock_limit ) - goto stop; - - clock_count += clock_table [opcode]; - - #if BLARGG_CPU_POWERPC - this->clock_count = clock_count; - #endif - - switch ( opcode ) - { - -// Macros - -#define ADD_PAGE (pc++, data += 0x100 * READ_PROG( pc )); -#define GET_ADDR() READ_PROG16( pc ) - -#define HANDLE_PAGE_CROSSING( lsb ) clock_count += (lsb) >> 8; - -#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; - -#define IND_Y { \ - int temp = READ_LOW( data ) + y; \ - data = temp + 0x100 * READ_LOW( uint8_t (data + 1) ); \ - HANDLE_PAGE_CROSSING( temp ); \ - } - -#define IND_X { \ - int temp = data + x; \ - data = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) ); \ - } - -#define ARITH_ADDR_MODES( op ) \ -case op - 0x04: /* (ind,x) */ \ - IND_X \ - goto ptr##op; \ -case op + 0x0C: /* (ind),y */ \ - IND_Y \ - goto ptr##op; \ -case op + 0x10: /* zp,X */ \ - data = uint8_t (data + x); \ -case op + 0x00: /* zp */ \ - data = READ_LOW( data ); \ - goto imm##op; \ -case op + 0x14: /* abs,Y */ \ - data += y; \ - goto ind##op; \ -case op + 0x18: /* abs,X */ \ - data += x; \ -ind##op: \ - HANDLE_PAGE_CROSSING( data ); \ -case op + 0x08: /* abs */ \ - ADD_PAGE \ -ptr##op: \ - data = READ( data ); \ -case op + 0x04: /* imm */ \ -imm##op: \ - -#define BRANCH( cond ) \ -{ \ - pc++; \ - int offset = (BOOST::int8_t) data; \ - int extra_clock = (pc & 0xff) + offset; \ - if ( !(cond) ) goto dec_clock_loop; \ - pc += offset; \ - clock_count += (extra_clock >> 8) & 1; \ - goto loop; \ -} - -// Often-Used - - case 0xB5: // LDA zp,x - data = uint8_t (data + x); - case 0xA5: // LDA zp - a = nz = READ_LOW( data ); - pc++; - goto loop; - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ); - - case 0x20: { // JSR - int temp = pc + 1; - pc = READ_PROG16( pc ); - WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); - sp = (sp - 2) | 0x100; - WRITE_LOW( sp, temp ); - goto loop; - } - - case 0x4C: // JMP abs - pc = READ_PROG16( pc ); - goto loop; - - case 0xE8: INC_DEC_XY( x, 1 ) // INX - - case 0x10: // BPL - BRANCH( !IS_NEG ) - - ARITH_ADDR_MODES( 0xC5 ) // CMP - nz = a - data; - pc++; - c = ~nz; - nz &= 0xff; - goto loop; - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0xF0: // BEQ - BRANCH( !(uint8_t) nz ); - - case 0x95: // STA zp,x - data = uint8_t (data + x); - case 0x85: // STA zp - pc++; - WRITE_LOW( data, a ); - goto loop; - - case 0xC8: INC_DEC_XY( y, 1 ) // INY - - case 0xA8: // TAY - y = a; - case 0x98: // TYA - a = nz = y; - goto loop; - - case 0xB9: // LDA abs,Y - data += y; - goto lda_ind_common; - - case 0xBD: // LDA abs,X - data += x; - lda_ind_common: - HANDLE_PAGE_CROSSING( data ); - case 0xAD: // LDA abs - ADD_PAGE - lda_ptr: - a = nz = READ( data ); - pc++; - goto loop; - - case 0x60: // RTS - pc = 1 + READ_LOW( sp ); - pc += READ_LOW( 0x100 | (sp - 0xff) ) * 0x100; - sp = (sp - 0xfe) | 0x100; - goto loop; - - case 0x99: // STA abs,Y - data += y; - goto sta_ind_common; - - case 0x9D: // STA abs,X - data += x; - sta_ind_common: - HANDLE_PAGE_CROSSING( data ); - case 0x8D: // STA abs - ADD_PAGE - sta_ptr: - pc++; - WRITE( data, a ); - goto loop; - - case 0xA9: // LDA #imm - pc++; - a = data; - nz = data; - goto loop; - -// Branch - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - -// Load/store - - case 0x94: // STY zp,x - data = uint8_t (data + x); - case 0x84: // STY zp - pc++; - WRITE_LOW( data, y ); - goto loop; - - case 0x96: // STX zp,y - data = uint8_t (data + y); - case 0x86: // STX zp - pc++; - WRITE_LOW( data, x ); - goto loop; - - case 0xB6: // LDX zp,y - data = uint8_t (data + y); - case 0xA6: // LDX zp - data = READ_LOW( data ); - case 0xA2: // LDX #imm - pc++; - x = data; - nz = data; - goto loop; - - case 0xB4: // LDY zp,x - data = uint8_t (data + x); - case 0xA4: // LDY zp - data = READ_LOW( data ); - case 0xA0: // LDY #imm - pc++; - y = data; - nz = data; - goto loop; - - case 0xB1: // LDA (ind),Y - IND_Y - goto lda_ptr; - - case 0xA1: // LDA (ind,X) - IND_X - goto lda_ptr; - - case 0x91: // STA (ind),Y - IND_Y - goto sta_ptr; - - case 0x81: // STA (ind,X) - IND_X - goto sta_ptr; - - case 0xBC: // LDY abs,X - data += x; - HANDLE_PAGE_CROSSING( data ); - case 0xAC:{// LDY abs - pc++; - unsigned addr = data + 0x100 * READ_PROG( pc ); - pc++; - y = nz = READ( addr ); - goto loop; - } - - case 0xBE: // LDX abs,y - data += y; - HANDLE_PAGE_CROSSING( data ); - case 0xAE:{// LDX abs - pc++; - unsigned addr = data + 0x100 * READ_PROG( pc ); - pc++; - x = nz = READ( addr ); - goto loop; - } - - { - int temp; - case 0x8C: // STY abs - temp = y; - goto store_abs; - - case 0x8E: // STX abs - temp = x; - store_abs: - unsigned addr = GET_ADDR(); - WRITE( addr, temp ); - pc += 2; - goto loop; - } - -// Compare - - case 0xEC:{// CPX abs - unsigned addr = GET_ADDR(); - pc++; - data = READ( addr ); - goto cpx_data; - } - - case 0xE4: // CPX zp - data = READ_LOW( data ); - case 0xE0: // CPX #imm - cpx_data: - nz = x - data; - pc++; - c = ~nz; - nz &= 0xff; - goto loop; - - case 0xCC:{// CPY abs - unsigned addr = GET_ADDR(); - pc++; - data = READ( addr ); - goto cpy_data; - } - - case 0xC4: // CPY zp - data = READ_LOW( data ); - case 0xC0: // CPY #imm - cpy_data: - nz = y - data; - pc++; - c = ~nz; - nz &= 0xff; - goto loop; - -// Logical - - ARITH_ADDR_MODES( 0x25 ) // AND - nz = (a &= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x45 ) // EOR - nz = (a ^= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x05 ) // ORA - nz = (a |= data); - pc++; - goto loop; - - case 0x2C:{// BIT abs - unsigned addr = GET_ADDR(); - pc++; - nz = READ( addr ); - goto bit_common; - } - - case 0x24: // BIT zp - nz = READ_LOW( data ); - bit_common: - pc++; - status &= ~st_v; - status |= nz & st_v; - // if result is zero, might also be negative, so use secondary N bit - if ( !(a & nz) ) - nz = (nz << 4) & 0x800; - goto loop; - -// Add/subtract - - ARITH_ADDR_MODES( 0xE5 ) // SBC - case 0xEB: // unofficial equivalent - data ^= 0xFF; - goto adc_imm; - - ARITH_ADDR_MODES( 0x65 ) // ADC - adc_imm: { - int carry = (c >> 8) & 1; - int ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - status &= ~st_v; - status |= (ov >> 2) & 0x40; - c = nz = a + data + carry; - pc++; - a = (uint8_t) nz; - goto loop; - } - -// Shift/rotate - - case 0x4A: // LSR A - c = 0; - case 0x6A: // ROR A - nz = (c >> 1) & 0x80; // could use bit insert macro here - c = a << 8; - nz |= a >> 1; - a = nz; - goto loop; - - case 0x0A: // ASL A - nz = a << 1; - c = nz; - a = (uint8_t) nz; - goto loop; - - case 0x2A: { // ROL A - nz = a << 1; - int temp = (c >> 8) & 1; - c = nz; - nz |= temp; - a = (uint8_t) nz; - goto loop; - } - - case 0x3E: // ROL abs,X - data += x; - goto rol_abs; - - case 0x1E: // ASL abs,X - data += x; - case 0x0E: // ASL abs - c = 0; - case 0x2E: // ROL abs - rol_abs: - HANDLE_PAGE_CROSSING( data ); - ADD_PAGE - nz = (c >> 8) & 1; - nz |= (c = READ( data ) << 1); - rotate_common: - pc++; - WRITE( data, (uint8_t) nz ); - goto loop; - - case 0x7E: // ROR abs,X - data += x; - goto ror_abs; - - case 0x5E: // LSR abs,X - data += x; - case 0x4E: // LSR abs - c = 0; - case 0x6E: // ROR abs - ror_abs: { - HANDLE_PAGE_CROSSING( data ); - ADD_PAGE - int temp = READ( data ); - nz = ((c >> 1) & 0x80) | (temp >> 1); - c = temp << 8; - goto rotate_common; - } - - case 0x76: // ROR zp,x - data = uint8_t (data + x); - goto ror_zp; - - case 0x56: // LSR zp,x - data = uint8_t (data + x); - case 0x46: // LSR zp - c = 0; - case 0x66: // ROR zp - ror_zp: { - int temp = READ_LOW( data ); - nz = ((c >> 1) & 0x80) | (temp >> 1); - c = temp << 8; - goto write_nz_zp; - } - - case 0x36: // ROL zp,x - data = uint8_t (data + x); - goto rol_zp; - - case 0x16: // ASL zp,x - data = uint8_t (data + x); - case 0x06: // ASL zp - c = 0; - case 0x26: // ROL zp - rol_zp: - nz = (c >> 8) & 1; - nz |= (c = READ_LOW( data ) << 1); - goto write_nz_zp; - -// Increment/decrement - - case 0xCA: INC_DEC_XY( x, -1 ) // DEX - - case 0x88: INC_DEC_XY( y, -1 ) // DEY - - case 0xF6: // INC zp,x - data = uint8_t (data + x); - case 0xE6: // INC zp - nz = 1; - goto add_nz_zp; - - case 0xD6: // DEC zp,x - data = uint8_t (data + x); - case 0xC6: // DEC zp - nz = -1; - add_nz_zp: - nz += READ_LOW( data ); - write_nz_zp: - pc++; - WRITE_LOW( data, nz ); - goto loop; - - case 0xFE: // INC abs,x - HANDLE_PAGE_CROSSING( data + x ); - data = x + GET_ADDR(); - goto inc_ptr; - - case 0xEE: // INC abs - data = GET_ADDR(); - inc_ptr: - nz = 1; - goto inc_common; - - case 0xDE: // DEC abs,x - HANDLE_PAGE_CROSSING( data + x ); - data = x + GET_ADDR(); - goto dec_ptr; - - case 0xCE: // DEC abs - data = GET_ADDR(); - dec_ptr: - nz = -1; - inc_common: - nz += READ( data ); - pc += 2; - WRITE( data, (uint8_t) nz ); - goto loop; - -// Transfer - - case 0xAA: // TAX - x = a; - case 0x8A: // TXA - a = nz = x; - goto loop; - - case 0x9A: // TXS - SET_SP( x ); // verified (no flag change) - goto loop; - - case 0xBA: // TSX - x = nz = GET_SP(); - goto loop; - -// Stack - - case 0x48: // PHA - PUSH( a ); // verified - goto loop; - - case 0x68: // PLA - a = nz = READ_LOW( sp ); - sp = (sp - 0xff) | 0x100; - goto loop; - - case 0x40: // RTI - { - int temp = READ_LOW( sp ); - pc = READ_LOW( 0x100 | (sp - 0xff) ); - pc |= READ_LOW( 0x100 | (sp - 0xfe) ) * 0x100; - sp = (sp - 0xfd) | 0x100; - data = status; - SET_STATUS( temp ); - } - if ( !((data ^ status) & st_i) ) - goto loop; // I flag didn't change - i_flag_changed: - //dprintf( "%6d %s\n", time(), (status & st_i ? "SEI" : "CLI") ); - this->r.status = status; // update externally-visible I flag - // update clock_limit based on modified I flag - clock_limit = end_time_; - if ( end_time_ <= irq_time_ ) - goto loop; - if ( status & st_i ) - goto loop; - clock_limit = irq_time_; - goto loop; - - case 0x28:{// PLP - int temp = READ_LOW( sp ); - sp = (sp - 0xff) | 0x100; - data = status; - SET_STATUS( temp ); - if ( !((data ^ status) & st_i) ) - goto loop; // I flag didn't change - if ( !(status & st_i) ) - goto handle_cli; - goto handle_sei; - } - - case 0x08: { // PHP - int temp; - CALC_STATUS( temp ); - PUSH( temp | st_b | st_r ); - goto loop; - } - - case 0x6C: // JMP (ind) - data = GET_ADDR(); - pc = READ( data ); - pc |= READ( (data & 0xff00) | ((data + 1) & 0xff) ) << 8; - goto loop; - - case 0x00: { // BRK - pc++; - WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); - WRITE_LOW( 0x100 | (sp - 2), pc ); - int temp; - CALC_STATUS( temp ); - sp = (sp - 3) | 0x100; - WRITE_LOW( sp, temp | st_b | st_r ); - pc = READ_PROG16( 0xFFFE ); - status |= st_i; - goto i_flag_changed; - } - -// Flags - - case 0x38: // SEC - c = ~0; - goto loop; - - case 0x18: // CLC - c = 0; - goto loop; - - case 0xB8: // CLV - status &= ~st_v; - goto loop; - - case 0xD8: // CLD - status &= ~st_d; - goto loop; - - case 0xF8: // SED - status |= st_d; - goto loop; - - case 0x58: // CLI - if ( !(status & st_i) ) - goto loop; - status &= ~st_i; - handle_cli: - //dprintf( "%6d CLI\n", time() ); - this->r.status = status; // update externally-visible I flag - if ( clock_count < end_time_ ) - { - assert( clock_limit == end_time_ ); - if ( end_time_ <= irq_time_ ) - goto loop; // irq is later - if ( clock_count >= irq_time_ ) - irq_time_ = clock_count + 1; // delay IRQ until after next instruction - clock_limit = irq_time_; - goto loop; - } - // execution is stopping now, so delayed CLI must be handled by caller - result = result_cli; - goto end; - - case 0x78: // SEI - if ( status & st_i ) - goto loop; - status |= st_i; - handle_sei: - //dprintf( "%6d SEI\n", time() ); - this->r.status = status; // update externally-visible I flag - clock_limit = end_time_; - if ( clock_count < irq_time_ ) - goto loop; - result = result_sei; // IRQ will occur now, even though I flag is set - goto end; - -// Undocumented - - case 0x0C: case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: // SKW - pc++; - case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: // SKB - case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: - pc++; - case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: // NOP - goto loop; - -// Unimplemented - - case page_wrap_opcode: // HLT - if ( pc > 0x10000 ) - { - // handle wrap-around (assumes caller has put page of HLT at 0x10000) - pc = (pc - 1) & 0xffff; - clock_count -= 2; - goto loop; - } - // fall through - case 0x02: case 0x12: case 0x22: case 0x32: // HLT - case 0x42: case 0x52: case 0x62: case 0x72: - case 0x92: case 0xB2: case 0xD2: - case 0x9B: // TAS - case 0x9C: // SAY - case 0x9E: // XAS - case 0x93: // AXA - case 0x9F: // AXA - case 0x0B: // ANC - case 0x2B: // ANC - case 0xBB: // LAS - case 0x4B: // ALR - case 0x6B: // AAR - case 0x8B: // XAA - case 0xAB: // OAL - case 0xCB: // SAX - case 0x83: case 0x87: case 0x8F: case 0x97: // AXS - case 0xA3: case 0xA7: case 0xAF: case 0xB3: case 0xB7: case 0xBF: // LAX - case 0xE3: case 0xE7: case 0xEF: case 0xF3: case 0xF7: case 0xFB: case 0xFF: // INS - case 0xC3: case 0xC7: case 0xCF: case 0xD3: case 0xD7: case 0xDB: case 0xDF: // DCM - case 0x63: case 0x67: case 0x6F: case 0x73: case 0x77: case 0x7B: case 0x7F: // RRA - case 0x43: case 0x47: case 0x4F: case 0x53: case 0x57: case 0x5B: case 0x5F: // LSE - case 0x23: case 0x27: case 0x2F: case 0x33: case 0x37: case 0x3B: case 0x3F: // RLA - case 0x03: case 0x07: case 0x0F: case 0x13: case 0x17: case 0x1B: case 0x1F: // ASO - result = result_badop; - goto stop; - } - - // If this fails then the case above is missing an opcode - assert( false ); - -stop: - pc--; -end: - - { - int temp; - CALC_STATUS( temp ); - r.status = temp; - } - - base_time += clock_count; - clock_limit -= clock_count; - this->clock_count = 0; - r.pc = pc; - r.sp = GET_SP(); - r.a = a; - r.x = x; - r.y = y; - irq_time_ = LONG_MAX / 2 + 1; - - return result; -} - -#endif -
--- a/Plugins/Input/console/Nes_Cpu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,174 +0,0 @@ - -// Nintendo Entertainment System (NES) 6502 CPU emulator - -// Game_Music_Emu 0.3.0 - -#ifndef NES_CPU_H -#define NES_CPU_H - -#include "blargg_common.h" - -typedef long nes_time_t; // clock cycle count -typedef unsigned nes_addr_t; // 16-bit address - -class Nes_Emu; - -class Nes_Cpu { - typedef BOOST::uint8_t uint8_t; - enum { page_bits = 11 }; - enum { page_count = 0x10000 >> page_bits }; - uint8_t const* code_map [page_count + 1]; -public: - Nes_Cpu(); - - // Memory read/write function types. Reader must return value from 0 to 255. - typedef int (*reader_t)( Nes_Emu*, nes_addr_t ); - typedef void (*writer_t)( Nes_Emu*, nes_addr_t, int data ); - void set_emu( Nes_Emu* emu ) { callback_data = emu; } - - // Clear registers, unmap memory, and map code pages to unmapped_page. - void reset( const void* unmapped_page = NULL, reader_t read = NULL, writer_t write = NULL ); - - // Memory mapping functions take a block of memory of specified 'start' address - // and 'size' in bytes. Both start address and size must be a multiple of page_size. - enum { page_size = 1L << page_bits }; - - // Map code memory (memory accessed via the program counter) - void map_code( nes_addr_t start, unsigned long size, const void* code ); - - // Set read function for address range - void set_reader( nes_addr_t start, unsigned long size, reader_t ); - - // Set write function for address range - void set_writer( nes_addr_t start, unsigned long size, writer_t ); - - // Set read and write functions for address range - void map_memory( nes_addr_t start, unsigned long size, reader_t, writer_t ); - - // Access memory as the emulated CPU does. - int read( nes_addr_t ); - void write( nes_addr_t, int data ); - uint8_t* get_code( nes_addr_t ); // non-const to allow debugger to modify code - - // Push a byte on the stack - void push_byte( int ); - - // NES 6502 registers. *Not* kept updated during a call to run(). - struct registers_t { - long pc; // more than 16 bits to allow overflow detection - BOOST::uint8_t a; - BOOST::uint8_t x; - BOOST::uint8_t y; - BOOST::uint8_t status; - BOOST::uint8_t sp; - }; - registers_t r; - - // Reasons that run() returns - enum result_t { - result_cycles, // Requested number of cycles (or more) were executed - result_sei, // I flag just set and IRQ time would generate IRQ now - result_cli, // I flag just cleared but IRQ should occur *after* next instr - result_badop // unimplemented/illegal instruction - }; - - result_t run( nes_time_t end_time_ ); - - nes_time_t time() const { return base_time + clock_count; } - void set_time( nes_time_t t ); - void end_frame( nes_time_t ); - nes_time_t end_time() const { return base_time + end_time_; } - nes_time_t irq_time() const { return base_time + irq_time_; } - void set_end_time( nes_time_t t ); - void set_irq_time( nes_time_t t ); - - // If PC exceeds 0xFFFF and encounters page_wrap_opcode, it will be silently wrapped. - enum { page_wrap_opcode = 0xF2 }; - - // One of the many opcodes that are undefined and stop CPU emulation. - enum { bad_opcode = 0xD2 }; - - // End of public interface -private: - // noncopyable - Nes_Cpu( const Nes_Cpu& ); - Nes_Cpu& operator = ( const Nes_Cpu& ); - - nes_time_t clock_limit; - nes_time_t base_time; - nes_time_t clock_count; - nes_time_t irq_time_; - nes_time_t end_time_; - - Nes_Emu* callback_data; - - enum { irq_inhibit = 0x04 }; - reader_t data_reader [page_count + 1]; // extra entry catches address overflow - writer_t data_writer [page_count + 1]; - void set_code_page( int, uint8_t const* ); - void update_clock_limit(); - -public: - // low_mem is a full page size so it can be mapped with code_map - uint8_t low_mem [page_size > 0x800 ? page_size : 0x800]; -}; - -inline BOOST::uint8_t* Nes_Cpu::get_code( nes_addr_t addr ) -{ - #if BLARGG_NONPORTABLE - return (uint8_t*) code_map [addr >> page_bits] + addr; - #else - return (uint8_t*) code_map [addr >> page_bits] + (addr & (page_size - 1)); - #endif -} - -inline void Nes_Cpu::update_clock_limit() -{ - nes_time_t t = end_time_; - if ( t > irq_time_ && !(r.status & irq_inhibit) ) - t = irq_time_; - clock_limit = t; -} - -inline void Nes_Cpu::set_end_time( nes_time_t t ) -{ - end_time_ = t - base_time; - update_clock_limit(); -} - -inline void Nes_Cpu::set_irq_time( nes_time_t t ) -{ - irq_time_ = t - base_time; - update_clock_limit(); -} - -inline void Nes_Cpu::end_frame( nes_time_t end_time_ ) -{ - base_time -= end_time_; - assert( time() >= 0 ); -} - -inline void Nes_Cpu::set_time( nes_time_t t ) -{ - t -= time(); - clock_limit -= t; - end_time_ -= t; - irq_time_ -= t; - base_time += t; -} - -inline void Nes_Cpu::push_byte( int data ) -{ - int sp = r.sp; - r.sp = (sp - 1) & 0xff; - low_mem [0x100 + sp] = data; -} - -inline void Nes_Cpu::map_memory( nes_addr_t addr, unsigned long s, reader_t r, writer_t w ) -{ - set_reader( addr, s, r ); - set_writer( addr, s, w ); -} - -#endif -
--- a/Plugins/Input/console/Nes_Oscs.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,498 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ - -#include "Nes_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -// Nes_Osc - -void Nes_Osc::clock_length( int halt_mask ) -{ - if ( length_counter && !(regs [0] & halt_mask) ) - length_counter--; -} - -void Nes_Envelope::clock_envelope() -{ - int period = regs [0] & 15; - if ( reg_written [3] ) { - reg_written [3] = false; - env_delay = period; - envelope = 15; - } - else if ( --env_delay < 0 ) { - env_delay = period; - if ( envelope | (regs [0] & 0x20) ) - envelope = (envelope - 1) & 15; - } -} - -int Nes_Envelope::volume() const -{ - return length_counter == 0 ? 0 : (regs [0] & 0x10) ? (regs [0] & 15) : envelope; -} - -// Nes_Square - -void Nes_Square::clock_sweep( int negative_adjust ) -{ - int sweep = regs [1]; - - if ( --sweep_delay < 0 ) - { - reg_written [1] = true; - - int period = this->period(); - int shift = sweep & shift_mask; - if ( shift && (sweep & 0x80) && period >= 8 ) - { - int offset = period >> shift; - - if ( sweep & negate_flag ) - offset = negative_adjust - offset; - - if ( period + offset < 0x800 ) - { - period += offset; - // rewrite period - regs [2] = period & 0xff; - regs [3] = (regs [3] & ~7) | ((period >> 8) & 7); - } - } - } - - if ( reg_written [1] ) { - reg_written [1] = false; - sweep_delay = (sweep >> 4) & 7; - } -} - -void Nes_Square::run( nes_time_t time, nes_time_t end_time ) -{ - if ( !output ) - return; - - const int volume = this->volume(); - const int period = this->period(); - int offset = period >> (regs [1] & shift_mask); - if ( regs [1] & negate_flag ) - offset = 0; - - const int timer_period = (period + 1) * 2; - if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) - { - if ( last_amp ) { - synth.offset( time, -last_amp, output ); - last_amp = 0; - } - - time += delay; - if ( time < end_time ) - { - // maintain proper phase - int count = (end_time - time + timer_period - 1) / timer_period; - phase = (phase + count) & (phase_range - 1); - time += (long) count * timer_period; - } - } - else - { - // handle duty select - int duty_select = (regs [0] >> 6) & 3; - int duty = 1 << duty_select; // 1, 2, 4, 2 - int amp = 0; - if ( duty_select == 3 ) { - duty = 2; // negated 25% - amp = volume; - } - if ( phase < duty ) - amp ^= volume; - - int delta = update_amp( amp ); - if ( delta ) - synth.offset( time, delta, output ); - - time += delay; - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - const Synth& synth = this->synth; - int delta = amp * 2 - volume; - int phase = this->phase; - - do { - phase = (phase + 1) & (phase_range - 1); - if ( phase == 0 || phase == duty ) { - delta = -delta; - synth.offset_inline( time, delta, output ); - } - time += timer_period; - } - while ( time < end_time ); - - last_amp = (delta + volume) >> 1; - this->phase = phase; - } - } - - delay = time - end_time; -} - -// Nes_Triangle - -void Nes_Triangle::clock_linear_counter() -{ - if ( reg_written [3] ) - linear_counter = regs [0] & 0x7f; - else if ( linear_counter ) - linear_counter--; - - if ( !(regs [0] & 0x80) ) - reg_written [3] = false; -} - -inline int Nes_Triangle::calc_amp() const -{ - int amp = phase_range - phase; - if ( amp < 0 ) - amp = phase - (phase_range + 1); - return amp; -} - -void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) -{ - if ( !output ) - return; - - // to do: track phase when period < 3 - // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks. - - int delta = update_amp( calc_amp() ); - if ( delta ) - synth.offset( time, delta, output ); - - time += delay; - const int timer_period = period() + 1; - if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) - { - time = end_time; - } - else if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - - int phase = this->phase; - int volume = 1; - if ( phase > phase_range ) { - phase -= phase_range; - volume = -volume; - } - - do { - if ( --phase == 0 ) { - phase = phase_range; - volume = -volume; - } - else { - synth.offset_inline( time, volume, output ); - } - - time += timer_period; - } - while ( time < end_time ); - - if ( volume < 0 ) - phase += phase_range; - this->phase = phase; - last_amp = calc_amp(); - } - delay = time - end_time; -} - -// Nes_Dmc - -void Nes_Dmc::reset() -{ - address = 0; - dac = 0; - buf = 0; - bits_remain = 1; - bits = 0; - buf_full = false; - silence = true; - next_irq = Nes_Apu::no_irq; - irq_flag = false; - irq_enabled = false; - - Nes_Osc::reset(); - period = 0x1ac; -} - -void Nes_Dmc::recalc_irq() -{ - nes_time_t irq = Nes_Apu::no_irq; - if ( irq_enabled && length_counter ) - irq = apu->last_dmc_time + delay + - ((length_counter - 1) * 8 + bits_remain - 1) * nes_time_t (period) + 1; - if ( irq != next_irq ) { - next_irq = irq; - apu->irq_changed(); - } -} - -int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const -{ - if ( last_read ) - *last_read = time; - - if ( length_counter == 0 ) - return 0; // not reading - - long first_read = next_read_time(); - long avail = time - first_read; - if ( avail <= 0 ) - return 0; - - int count = (avail - 1) / (period * 8) + 1; - if ( !(regs [0] & loop_flag) && count > length_counter ) - count = length_counter; - - if ( last_read ) { - *last_read = first_read + (count - 1) * (period * 8) + 1; - assert( *last_read <= time ); - assert( count == count_reads( *last_read, NULL ) ); - assert( count - 1 == count_reads( *last_read - 1, NULL ) ); - } - - return count; -} - -static const short dmc_period_table [2] [16] = { - {0x1ac, 0x17c, 0x154, 0x140, 0x11e, 0x0fe, 0x0e2, 0x0d6, // NTSC - 0x0be, 0x0a0, 0x08e, 0x080, 0x06a, 0x054, 0x048, 0x036}, - - {0x18e, 0x161, 0x13c, 0x129, 0x10a, 0x0ec, 0x0d2, 0x0c7, // PAL (totally untested) - 0x0b1, 0x095, 0x084, 0x077, 0x062, 0x04e, 0x043, 0x032} // to do: verify PAL periods -}; - -inline void Nes_Dmc::reload_sample() -{ - address = 0x4000 + regs [2] * 0x40; - length_counter = regs [3] * 0x10 + 1; -} - -static const unsigned char dac_table [128] = -{ - 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14, - 15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27, - 27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38, - 39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49, - 50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59, - 59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67, - 68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75, - 76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83, -}; - -void Nes_Dmc::write_register( int addr, int data ) -{ - if ( addr == 0 ) - { - period = dmc_period_table [pal_mode] [data & 15]; - irq_enabled = (data & 0xc0) == 0x80; // enabled only if loop disabled - irq_flag &= irq_enabled; - recalc_irq(); - } - else if ( addr == 1 ) - { - int old_dac = dac; - dac = data & 0x7F; - - // adjust last_amp so that "pop" amplitude will be properly non-linear - // with respect to change in dac - int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]); - if ( !nonlinear ) - last_amp = faked_nonlinear; - } -} - -void Nes_Dmc::start() -{ - reload_sample(); - fill_buffer(); - recalc_irq(); -} - -void Nes_Dmc::fill_buffer() -{ - if ( !buf_full && length_counter ) - { - require( rom_reader ); // rom_reader must be set - buf = rom_reader( rom_reader_data, 0x8000u + address ); - address = (address + 1) & 0x7FFF; - buf_full = true; - if ( --length_counter == 0 ) - { - if ( regs [0] & loop_flag ) { - reload_sample(); - } - else { - apu->osc_enables &= ~0x10; - irq_flag = irq_enabled; - next_irq = Nes_Apu::no_irq; - apu->irq_changed(); - } - } - } -} - -void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) -{ - int delta = update_amp( dac ); - if ( !output ) - silence = true; - else if ( delta ) - synth.offset( time, delta, output ); - - time += delay; - if ( time < end_time ) - { - int bits_remain = this->bits_remain; - if ( silence && !buf_full ) - { - int count = (end_time - time + period - 1) / period; - bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1; - time += count * period; - } - else - { - Blip_Buffer* const output = this->output; - const int period = this->period; - int bits = this->bits; - int dac = this->dac; - - do - { - if ( !silence ) - { - int step = (bits & 1) * 4 - 2; - bits >>= 1; - if ( unsigned (dac + step) <= 0x7F ) { - dac += step; - synth.offset_inline( time, step, output ); - } - } - - time += period; - - if ( --bits_remain == 0 ) - { - bits_remain = 8; - if ( !buf_full ) { - silence = true; - } - else { - silence = false; - bits = buf; - buf_full = false; - if ( !output ) - silence = true; - fill_buffer(); - } - } - } - while ( time < end_time ); - - this->dac = dac; - this->last_amp = dac; - this->bits = bits; - } - this->bits_remain = bits_remain; - } - delay = time - end_time; -} - -// Nes_Noise - -#include BLARGG_ENABLE_OPTIMIZER - -static const short noise_period_table [16] = { - 0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0, - 0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4 -}; - -void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) -{ - if ( !output ) - return; - - const int volume = this->volume(); - int amp = (noise & 1) ? volume : 0; - int delta = update_amp( amp ); - if ( delta ) - synth.offset( time, delta, output ); - - time += delay; - if ( time < end_time ) - { - const int mode_flag = 0x80; - - int period = noise_period_table [regs [2] & 15]; - if ( !volume ) - { - // round to next multiple of period - time += (end_time - time + period - 1) / period * period; - - // approximate noise cycling while muted, by shuffling up noise register - // to do: precise muted noise cycling? - if ( !(regs [2] & mode_flag) ) { - int feedback = (noise << 13) ^ (noise << 14); - noise = (feedback & 0x4000) | (noise >> 1); - } - } - else - { - Blip_Buffer* const output = this->output; - - // using resampled time avoids conversion in synth.offset() - blip_resampled_time_t rperiod = output->resampled_duration( period ); - blip_resampled_time_t rtime = output->resampled_time( time ); - - int noise = this->noise; - int delta = amp * 2 - volume; - const int tap = (regs [2] & mode_flag ? 8 : 13); - - do { - int feedback = (noise << tap) ^ (noise << 14); - time += period; - - if ( (noise + 1) & 2 ) { - // bits 0 and 1 of noise differ - delta = -delta; - synth.offset_resampled( rtime, delta, output ); - } - - rtime += rperiod; - noise = (feedback & 0x4000) | (noise >> 1); - } - while ( time < end_time ); - - last_amp = (delta + volume) >> 1; - this->noise = noise; - } - } - - delay = time - end_time; -} -
--- a/Plugins/Input/console/Nes_Oscs.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,146 +0,0 @@ - -// Private oscillators used by Nes_Apu - -// Nes_Snd_Emu 0.1.7 - -#ifndef NES_OSCS_H -#define NES_OSCS_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -class Nes_Apu; - -struct Nes_Osc -{ - unsigned char regs [4]; - bool reg_written [4]; - Blip_Buffer* output; - int length_counter;// length counter (0 if unused by oscillator) - int delay; // delay until next (potential) transition - int last_amp; // last amplitude oscillator was outputting - - void clock_length( int halt_mask ); - int period() const { - return (regs [3] & 7) * 0x100 + (regs [2] & 0xff); - } - void reset() { - delay = 0; - last_amp = 0; - } - int update_amp( int amp ) { - int delta = amp - last_amp; - last_amp = amp; - return delta; - } -}; - -struct Nes_Envelope : Nes_Osc -{ - int envelope; - int env_delay; - - void clock_envelope(); - int volume() const; - void reset() { - envelope = 0; - env_delay = 0; - Nes_Osc::reset(); - } -}; - -// Nes_Square -struct Nes_Square : Nes_Envelope -{ - enum { negate_flag = 0x08 }; - enum { shift_mask = 0x07 }; - enum { phase_range = 8 }; - int phase; - int sweep_delay; - - typedef Blip_Synth<blip_good_quality,1> Synth; - Synth const& synth; // shared between squares - - Nes_Square( Synth const* s ) : synth( *s ) { } - - void clock_sweep( int adjust ); - void run( nes_time_t, nes_time_t ); - void reset() { - sweep_delay = 0; - Nes_Envelope::reset(); - } -}; - -// Nes_Triangle -struct Nes_Triangle : Nes_Osc -{ - enum { phase_range = 16 }; - int phase; - int linear_counter; - Blip_Synth<blip_med_quality,1> synth; - - int calc_amp() const; - void run( nes_time_t, nes_time_t ); - void clock_linear_counter(); - void reset() { - linear_counter = 0; - phase = phase_range; - Nes_Osc::reset(); - } -}; - -// Nes_Noise -struct Nes_Noise : Nes_Envelope -{ - int noise; - Blip_Synth<blip_med_quality,1> synth; - - void run( nes_time_t, nes_time_t ); - void reset() { - noise = 1 << 14; - Nes_Envelope::reset(); - } -}; - -// Nes_Dmc -struct Nes_Dmc : Nes_Osc -{ - int address; // address of next byte to read - int period; - //int length_counter; // bytes remaining to play (already defined in Nes_Osc) - int buf; - int bits_remain; - int bits; - bool buf_full; - bool silence; - - enum { loop_flag = 0x40 }; - - int dac; - - nes_time_t next_irq; - bool irq_enabled; - bool irq_flag; - bool pal_mode; - bool nonlinear; - - int (*rom_reader)( void*, nes_addr_t ); // needs to be initialized to rom read function - void* rom_reader_data; - - Nes_Apu* apu; - - Blip_Synth<blip_med_quality,1> synth; - - void start(); - void write_register( int, int ); - void run( nes_time_t, nes_time_t ); - void recalc_irq(); - void fill_buffer(); - void reload_sample(); - void reset(); - int count_reads( nes_time_t, nes_time_t* ) const; - nes_time_t next_read_time() const; -}; - -#endif -
--- a/Plugins/Input/console/Nsf_Emu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,622 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Nsf_Emu.h" - -#include <string.h> -#include <stdio.h> - -#if !NSF_EMU_APU_ONLY - #include "Nes_Vrc6_Apu.h" - #include "Nes_Namco_Apu.h" - #include "Nes_Fme7_Apu.h" -#endif - -#include "blargg_endian.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -#ifndef RUN_NES_CPU - #define RUN_NES_CPU( cpu, count ) cpu.run( count ) -#endif - -#ifndef NSF_BEGIN_FRAME - #define NSF_BEGIN_FRAME() -#endif - -const unsigned low_mem_size = 0x800; -const unsigned page_size = 0x1000; -const long ram_size = 0x10000; -const nes_addr_t rom_begin = 0x8000; -const nes_addr_t bank_select_addr = 0x5ff8; -const nes_addr_t exram_addr = bank_select_addr - (bank_select_addr % Nes_Cpu::page_size); -const int master_clock_divisor = 12; - -const int vrc6_flag = 0x01; -const int namco_flag = 0x10; -const int fme7_flag = 0x20; - -static BOOST::uint8_t unmapped_code [Nes_Cpu::page_size]; - -Nes_Emu::equalizer_t const Nes_Emu::nes_eq = { -1.0, 80 }; -Nes_Emu::equalizer_t const Nes_Emu::famicom_eq = { -15.0, 80 }; - -// ROM - -int Nsf_Emu::read_code( Nsf_Emu* emu, nes_addr_t addr ) -{ - return *emu->cpu.get_code( addr ); -} - -void Nsf_Emu::write_exram( Nsf_Emu* emu, nes_addr_t addr, int data ) -{ - unsigned bank = addr - bank_select_addr; - if ( bank < bank_count ) - { - if ( data < emu->total_banks ) - { - emu->cpu.map_code( (bank + 8) * page_size, page_size, - &emu->rom [data * page_size] ); - } - else - { - dprintf( "Bank %d out of range (%d banks total)\n", - data, (int) emu->total_banks ); - } - } -} - -// APU - -int Nsf_Emu::read_snd( Nsf_Emu* emu, nes_addr_t addr ) -{ - if ( addr == Nes_Apu::status_addr ) - return emu->apu.read_status( emu->cpu.time() ); - return addr >> 8; // high byte of address stays on bus -} - -void Nsf_Emu::write_snd( Nsf_Emu* emu, nes_addr_t addr, int data ) -{ - if ( unsigned (addr - Nes_Apu::start_addr) <= Nes_Apu::end_addr - Nes_Apu::start_addr ) - emu->apu.write_register( emu->cpu.time(), addr, data ); -} - -int Nsf_Emu::pcm_read( void* emu, nes_addr_t addr ) -{ - return ((Nsf_Emu*) emu)->cpu.read( addr ); -} - -// Low Mem - -int Nsf_Emu::read_low_mem( Nsf_Emu* emu, nes_addr_t addr ) -{ - return emu->cpu.low_mem [addr]; -} - -void Nsf_Emu::write_low_mem( Nsf_Emu* emu, nes_addr_t addr, int data ) -{ - emu->cpu.low_mem [addr] = data; -} - -// SRAM - -int Nsf_Emu::read_sram( Nsf_Emu* emu, nes_addr_t addr ) -{ - return emu->sram [addr & (sram_size - 1)]; -} - -void Nsf_Emu::write_sram( Nsf_Emu* emu, nes_addr_t addr, int data ) -{ - emu->sram [addr & (sram_size - 1)] = data; -} - -#if !NSF_EMU_APU_ONLY - -// Namco -int Nsf_Emu::read_namco( Nsf_Emu* emu, nes_addr_t addr ) -{ - if ( addr == Nes_Namco_Apu::data_reg_addr ) - return emu->namco->read_data(); - return addr >> 8; -} - -void Nsf_Emu::write_namco( Nsf_Emu* emu, nes_addr_t addr, int data ) -{ - if ( addr == Nes_Namco_Apu::data_reg_addr ) - emu->namco->write_data( emu->cpu.time(), data ); -} - -void Nsf_Emu::write_namco_addr( Nsf_Emu* emu, nes_addr_t addr, int data ) -{ - if ( addr == Nes_Namco_Apu::addr_reg_addr ) - emu->namco->write_addr( data ); -} - -// VRC6 -void Nsf_Emu::write_vrc6( Nsf_Emu* emu, nes_addr_t addr, int data ) -{ - unsigned reg = addr & (Nes_Vrc6_Apu::addr_step - 1); - unsigned osc = unsigned (addr - Nes_Vrc6_Apu::base_addr) / Nes_Vrc6_Apu::addr_step; - if ( osc < Nes_Vrc6_Apu::osc_count && reg < Nes_Vrc6_Apu::reg_count ) - emu->vrc6->write_osc( emu->cpu.time(), osc, reg, data ); -} - -// FME-7 -void Nsf_Emu::write_fme7( Nsf_Emu* emu, nes_addr_t addr, int data ) -{ - switch ( addr & Nes_Fme7_Apu::addr_mask ) - { - case Nes_Fme7_Apu::latch_addr: - emu->fme7->write_latch( data ); - break; - - case Nes_Fme7_Apu::data_addr: - emu->fme7->write_data( emu->cpu.time(), data ); - break; - } -} - -#endif - -// Unmapped -int Nsf_Emu::read_unmapped( Nsf_Emu*, nes_addr_t addr ) -{ - dprintf( "Read unmapped $%.4X\n", (unsigned) addr ); - return (addr >> 8) & 0xff; // high byte of address stays on bus -} - -void Nsf_Emu::write_unmapped( Nsf_Emu*, nes_addr_t addr, int data ) -{ - #ifdef NDEBUG - return; - #endif - - // some games write to $8000 and $8001 repeatedly - if ( addr == 0x8000 || addr == 0x8001 ) - return; - - // probably namco sound mistakenly turned on in mck - if ( addr == 0x4800 || addr == 0xF800 ) - return; - - // memory mapper? - if ( addr == 0xFFF8 ) - return; - - dprintf( "write_unmapped( 0x%04X, 0x%02X )\n", (unsigned) addr, (unsigned) data ); -} - -Nes_Emu::Nes_Emu( double gain_ ) -{ - cpu.set_emu( this ); - play_addr = 0; - gain = gain_; - apu.dmc_reader( pcm_read, this ); - vrc6 = NULL; - namco = NULL; - fme7 = NULL; - Music_Emu::set_equalizer( nes_eq ); - - // set unmapped code to illegal instruction - memset( unmapped_code, 0x32, sizeof unmapped_code ); -} - -Nes_Emu::~Nes_Emu() -{ - unload(); -} - -void Nsf_Emu::unload() -{ - #if !NSF_EMU_APU_ONLY - delete vrc6; - vrc6 = NULL; - - delete namco; - namco = NULL; - - delete fme7; - fme7 = NULL; - - #endif - - rom.clear(); -} - -const char** Nsf_Emu::voice_names() const -{ - static const char* base_names [] = { - "Square 1", "Square 2", "Triangle", "Noise", "DMC" - }; - static const char* namco_names [] = { - "Square 1", "Square 2", "Triangle", "Noise", "DMC", - "Namco 5&7", "Namco 4&6", "Namco 1-3" - }; - static const char* vrc6_names [] = { - "Square 1", "Square 2", "Triangle", "Noise", "DMC", - "VRC6 Square 1", "VRC6 Square 2", "VRC6 Saw" - }; - static const char* dual_names [] = { - "Square 1", "Square 2", "Triangle", "Noise", "DMC", - "VRC6.1,N106.5&7", "VRC6.2,N106.4&6", "VRC6.3,N106.1-3" - }; - - static const char* fme7_names [] = { - "Square 1", "Square 2", "Triangle", "Noise", "DMC", - "Square 3", "Square 4", "Square 5" - }; - - if ( namco ) - return vrc6 ? dual_names : namco_names; - - if ( vrc6 ) - return vrc6_names; - - if ( fme7 ) - return fme7_names; - - return base_names; -} - -blargg_err_t Nsf_Emu::init_sound() -{ - if ( exp_flags & ~(namco_flag | vrc6_flag | fme7_flag) ) - return "NSF requires unsupported expansion audio hardware"; - - // map memory - cpu.reset( unmapped_code, read_unmapped, write_unmapped ); - cpu.map_memory( 0, low_mem_size, read_low_mem, write_low_mem ); - cpu.map_code( 0, low_mem_size, cpu.low_mem ); - cpu.map_memory( 0x4000, Nes_Cpu::page_size, read_snd, write_snd ); - cpu.map_memory( exram_addr, Nes_Cpu::page_size, read_unmapped, write_exram ); - cpu.map_memory( 0x6000, sram_size, read_sram, write_sram ); - cpu.map_code ( 0x6000, sram_size, sram ); - cpu.map_memory( rom_begin, ram_size - rom_begin, read_code, write_unmapped ); - - set_voice_count( Nes_Apu::osc_count ); - - double adjusted_gain = gain; - - #if NSF_EMU_APU_ONLY - if ( exp_flags ) - return "NSF requires expansion audio hardware"; - #else - - if ( exp_flags ) - set_voice_count( Nes_Apu::osc_count + 3 ); - - // namco - if ( exp_flags & namco_flag ) - { - namco = BLARGG_NEW Nes_Namco_Apu; - BLARGG_CHECK_ALLOC( namco ); - - adjusted_gain *= 0.75; - cpu.map_memory( Nes_Namco_Apu::data_reg_addr, Nes_Cpu::page_size, - read_namco, write_namco ); - cpu.map_memory( Nes_Namco_Apu::addr_reg_addr, Nes_Cpu::page_size, - read_code, write_namco_addr ); - } - - // vrc6 - if ( exp_flags & vrc6_flag ) - { - vrc6 = BLARGG_NEW Nes_Vrc6_Apu; - BLARGG_CHECK_ALLOC( vrc6 ); - - adjusted_gain *= 0.75; - for ( int i = 0; i < Nes_Vrc6_Apu::osc_count; i++ ) - cpu.map_memory( Nes_Vrc6_Apu::base_addr + i * Nes_Vrc6_Apu::addr_step, - Nes_Cpu::page_size, read_code, write_vrc6 ); - } - - // fme7 - if ( exp_flags & fme7_flag ) - { - fme7 = BLARGG_NEW Nes_Fme7_Apu; - BLARGG_CHECK_ALLOC( fme7 ); - - adjusted_gain *= 0.75; - cpu.map_memory( fme7->latch_addr, ram_size - fme7->latch_addr, - read_code, write_fme7 ); - } - // to do: is gain adjustment even needed? other sound chip volumes should work - // naturally with the apu without change. - - if ( namco ) - namco->volume( adjusted_gain ); - - if ( vrc6 ) - vrc6->volume( adjusted_gain ); - - if ( fme7 ) - fme7->volume( adjusted_gain ); - -#endif - - apu.volume( adjusted_gain ); - - return blargg_success; -} - -void Nsf_Emu::update_eq( blip_eq_t const& eq ) -{ - apu.treble_eq( eq ); - - #if !NSF_EMU_APU_ONLY - if ( vrc6 ) - vrc6->treble_eq( eq ); - - if ( namco ) - namco->treble_eq( eq ); - - if ( fme7 ) - fme7->treble_eq( eq ); - #endif -} - -blargg_err_t Nsf_Emu::load( Data_Reader& in ) -{ - header_t h; - BLARGG_RETURN_ERR( in.read( &h, sizeof h ) ); - return load( h, in ); -} - -blargg_err_t Nsf_Emu::load( const header_t& h, Data_Reader& in ) -{ - header_ = h; - unload(); - - // check compatibility - if ( 0 != memcmp( header_.tag, "NESM\x1A", 5 ) ) - return "Not an NSF file"; - if ( header_.vers != 1 ) - return "Unsupported NSF format"; - - // sound and memory - exp_flags = header_.chip_flags; - blargg_err_t err = init_sound(); - if ( err ) - return err; - - // set up data - nes_addr_t load_addr = get_le16( header_.load_addr ); - init_addr = get_le16( header_.init_addr ); - play_addr = get_le16( header_.play_addr ); - if ( !load_addr ) load_addr = rom_begin; - if ( !init_addr ) init_addr = rom_begin; - if ( !play_addr ) play_addr = rom_begin; - if ( load_addr < rom_begin || init_addr < rom_begin ) - return "Invalid address in NSF"; - - // set up rom - total_banks = (in.remain() + load_addr % page_size + page_size - 1) / page_size; - BLARGG_RETURN_ERR( rom.resize( total_banks * page_size ) ); - memset( rom.begin(), 0, rom.size() ); - err = in.read( &rom [load_addr % page_size], in.remain() ); - if ( err ) - { - unload(); - return err; - } - - // bank switching - int first_bank = (load_addr - rom_begin) / page_size; - for ( int i = 0; i < bank_count; i++ ) - { - unsigned bank = i - first_bank; - initial_banks [i] = (bank < (unsigned) total_banks) ? bank : 0; - - if ( header_.banks [i] ) - { - // bank-switched - memcpy( initial_banks, header_.banks, sizeof initial_banks ); - break; - } - } - - // playback rate - unsigned playback_rate = get_le16( header_.ntsc_speed ); - unsigned standard_rate = 0x411A; - double clock_rate = 1789772.72727; - play_period = 262 * 341L * 4 + 2; - pal_only = false; - - // use pal speed if there is no ntsc speed - if ( (header_.speed_flags & 3) == 1 ) - { - pal_only = true; - play_period = 33247 * master_clock_divisor; - clock_rate = 1662607.125; - standard_rate = 0x4E20; - playback_rate = get_le16( header_.pal_speed ); - } - - // use custom playback rate if not the standard rate - if ( playback_rate && playback_rate != standard_rate ) - play_period = long (clock_rate * playback_rate * master_clock_divisor / - 1000000.0); - - // extra flags - int extra_flags = header_.speed_flags; - #if !NSF_EMU_EXTRA_FLAGS - extra_flags = 0; - #endif - needs_long_frames = (extra_flags & 0x10) != 0; - initial_pcm_dac = (extra_flags & 0x20) ? 0x3F : 0; - - set_track_count( header_.track_count ); - - return setup_buffer( (long) (clock_rate + 0.5) ); -} - -void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* ) -{ - if ( i < Nes_Apu::osc_count ) - { - apu.osc_output( i, buf ); - return; - } - - #if !NSF_EMU_APU_ONLY - if ( vrc6 ) - vrc6->osc_output( i - Nes_Apu::osc_count, buf ); - - if ( fme7 ) - fme7->osc_output( i - Nes_Apu::osc_count, buf ); - - if ( namco ) - { - if ( i < 7 ) - { - i &= 1; - namco->osc_output( i + 4, buf ); - namco->osc_output( i + 6, buf ); - } - else - { - for ( int n = 0; n < namco->osc_count / 2; n++ ) - namco->osc_output( n, buf ); - } - } - #endif -} - -void Nsf_Emu::start_track( int track ) -{ - require( rom.size() ); // file must be loaded - - Classic_Emu::start_track( track ); - - // clear memory - memset( cpu.low_mem, 0, sizeof cpu.low_mem ); - memset( sram, 0, sizeof sram ); - - // initial rom banks - for ( int i = 0; i < bank_count; ++i ) - cpu.write( bank_select_addr + i, initial_banks [i] ); - - // reset sound - apu.reset( pal_only, initial_pcm_dac ); - apu.write_register( 0, 0x4015, 0x0F ); - apu.write_register( 0, 0x4017, needs_long_frames ? 0x80 : 0 ); - - #if !NSF_EMU_APU_ONLY - if ( namco ) - namco->reset(); - - if ( vrc6 ) - vrc6->reset(); - - if ( fme7 ) - fme7->reset(); - #endif - - // reset cpu - cpu.r.pc = exram_addr; - cpu.r.a = track; - cpu.r.x = pal_only; - cpu.r.y = 0; - cpu.r.sp = 0xFF; - cpu.r.status = 0x04; // i flag - - // first call - cpu_jsr( init_addr, -1 ); - next_play = 0; - play_extra = 0; -} - -void Nsf_Emu::cpu_jsr( nes_addr_t pc, int adj ) -{ - unsigned addr = cpu.r.pc + adj; - cpu.r.pc = pc; - cpu.push_byte( addr >> 8 ); - cpu.push_byte( addr ); -} - -void Nsf_Emu::call_play() -{ - cpu_jsr( play_addr, -1 ); -} - -blip_time_t Nsf_Emu::run_clocks( blip_time_t duration, bool* ) -{ - // run cpu - cpu.set_time( 0 ); - bool first_illegal = true; // avoid swamping output with illegal instruction errors - while ( cpu.time() < duration ) - { - // check for idle cpu - if ( cpu.r.pc == exram_addr ) - { - if ( next_play > duration ) - { - cpu.set_time( duration ); - break; - } - - if ( next_play > cpu.time() ) - cpu.set_time( next_play ); - - nes_time_t period = (play_period + play_extra) / master_clock_divisor; - play_extra = play_period - period * master_clock_divisor; - next_play += period; - call_play(); - } - - Nes_Cpu::result_t result = RUN_NES_CPU( cpu, duration ); - if ( result == Nes_Cpu::result_badop && cpu.r.pc != exram_addr ) - { - if ( cpu.r.pc > 0xffff ) - { - cpu.r.pc &= 0xffff; - dprintf( "PC wrapped around\n" ); - } - else - { - cpu.r.pc = (cpu.r.pc + 1) & 0xffff; - cpu.set_time( cpu.time() + 4 ); - log_error(); - if ( first_illegal ) - { - first_illegal = false; - dprintf( "Bad opcode $%.2x at $%.4x\n", - (int) cpu.read( cpu.r.pc ), (int) cpu.r.pc ); - } - } - } - } - - // end time frame - duration = cpu.time(); - next_play -= duration; - if ( next_play < 0 ) // could go negative if routine is taking too long to return - next_play = 0; - apu.end_frame( duration ); - - #if !NSF_EMU_APU_ONLY - if ( namco ) - namco->end_frame( duration ); - - if ( vrc6 ) - vrc6->end_frame( duration ); - - if ( fme7 ) - fme7->end_frame( duration ); - - #endif - - return duration; -} -
--- a/Plugins/Input/console/Nsf_Emu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,137 +0,0 @@ - -// Nintendo Entertainment System (NES) NSF music file emulator - -// Game_Music_Emu 0.3.0 - -#ifndef NSF_EMU_H -#define NSF_EMU_H - -#include "Classic_Emu.h" -#include "Nes_Apu.h" -#include "Nes_Cpu.h" - -typedef Nes_Emu Nsf_Emu; - -class Nes_Emu : public Classic_Emu { -public: - - // Set internal gain, where 1.0 results in almost no clamping. Default gain - // roughly matches volume of other emulators. - Nes_Emu( double gain = 1.4 ); - - // NSF file header - struct header_t - { - char tag [5]; - byte vers; - byte track_count; - byte first_track; - byte load_addr [2]; - byte init_addr [2]; - byte play_addr [2]; - char game [32]; - char author [32]; - char copyright [32]; - byte ntsc_speed [2]; - byte banks [8]; - byte pal_speed [2]; - byte speed_flags; - byte chip_flags; - byte unused [4]; - - enum { song = 0 }; // no song titles - }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 0x80 ); - - // Load NSF data - blargg_err_t load( Data_Reader& ); - - // Load NSF using already-loaded header and remaining data - blargg_err_t load( header_t const&, Data_Reader& ); - - // Header for currently loaded NSF - header_t const& header() const { return header_; } - - // Equalizer profiles for US NES and Japanese Famicom - static equalizer_t const nes_eq; - static equalizer_t const famicom_eq; - -public: - ~Nes_Emu(); - void start_track( int ); - Nes_Apu* apu_() { return &apu; } - const char** voice_names() const; -protected: - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); - blip_time_t run_clocks( blip_time_t, bool* ); - virtual void call_play(); -protected: - // initial state - enum { bank_count = 8 }; - byte initial_banks [bank_count]; - int initial_pcm_dac; - double gain; - bool needs_long_frames; - bool pal_only; - unsigned init_addr; - unsigned play_addr; - int exp_flags; - - // timing - nes_time_t next_play; - long play_period; - int play_extra; - nes_time_t clock() const; - nes_time_t next_irq( nes_time_t end_time ); - static void irq_changed( void* ); - - // rom - int total_banks; - blargg_vector<byte> rom; - static int read_code( Nsf_Emu*, nes_addr_t ); - void unload(); - - blargg_err_t init_sound(); - - // expansion sound - - class Nes_Namco_Apu* namco; - static int read_namco( Nsf_Emu*, nes_addr_t ); - static void write_namco( Nsf_Emu*, nes_addr_t, int ); - static void write_namco_addr( Nsf_Emu*, nes_addr_t, int ); - - class Nes_Vrc6_Apu* vrc6; - static void write_vrc6( Nsf_Emu*, nes_addr_t, int ); - - class Nes_Fme7_Apu* fme7; - static void write_fme7( Nsf_Emu*, nes_addr_t, int ); - - // large objects - - header_t header_; - - // cpu - Nes_Cpu cpu; - void cpu_jsr( unsigned pc, int adj ); - static int read_low_mem( Nsf_Emu*, nes_addr_t ); - static void write_low_mem( Nsf_Emu*, nes_addr_t, int ); - static int read_unmapped( Nsf_Emu*, nes_addr_t ); - static void write_unmapped( Nsf_Emu*, nes_addr_t, int ); - static void write_exram( Nsf_Emu*, nes_addr_t, int ); - - // apu - Nes_Apu apu; - static int read_snd( Nsf_Emu*, nes_addr_t ); - static void write_snd( Nsf_Emu*, nes_addr_t, int ); - static int pcm_read( void*, nes_addr_t ); - - // sram - enum { sram_size = 0x2000 }; - byte sram [sram_size]; - static int read_sram( Nsf_Emu*, nes_addr_t ); - static void write_sram( Nsf_Emu*, nes_addr_t, int ); -}; - -#endif -
--- a/Plugins/Input/console/Sms_Apu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,319 +0,0 @@ - -// Sms_Snd_Emu 0.1.3. http://www.slack.net/~ant/ - -#include "Sms_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -// Sms_Osc - -Sms_Osc::Sms_Osc() -{ - output = NULL; - outputs [0] = NULL; // always stays NULL - outputs [1] = NULL; - outputs [2] = NULL; - outputs [3] = NULL; -} - -void Sms_Osc::reset() -{ - delay = 0; - last_amp = 0; - volume = 0; - output_select = 3; - output = outputs [3]; -} - -// Sms_Square - -inline void Sms_Square::reset() -{ - period = 0; - phase = 0; - Sms_Osc::reset(); -} - -void Sms_Square::run( sms_time_t time, sms_time_t end_time ) -{ - if ( !volume || period <= 128 ) - { - // ignore 16kHz and higher - if ( last_amp ) - { - synth->offset( time, -last_amp, output ); - last_amp = 0; - } - time += delay; - if ( !period ) - { - time = end_time; - } - else if ( time < end_time ) - { - // keep calculating phase - int count = (end_time - time + period - 1) / period; - phase = (phase + count) & 1; - time += count * period; - } - } - else - { - int amp = phase ? volume : -volume; - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); - } - - time += delay; - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - int delta = amp * 2; - do - { - delta = -delta; - synth->offset_inline( time, delta, output ); - time += period; - phase ^= 1; - } - while ( time < end_time ); - this->last_amp = phase ? volume : -volume; - } - } - delay = time - end_time; -} - -// Sms_Noise - -static const int noise_periods [3] = { 0x100, 0x200, 0x400 }; - -inline void Sms_Noise::reset() -{ - period = &noise_periods [0]; - shifter = 0x8000; - tap = 12; - Sms_Osc::reset(); -} - -void Sms_Noise::run( sms_time_t time, sms_time_t end_time ) -{ - int amp = volume; - if ( shifter & 1 ) - amp = -amp; - - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth.offset( time, delta, output ); - } - - time += delay; - if ( !volume ) - time = end_time; - - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - unsigned shifter = this->shifter; - int delta = amp * 2; - int period = *this->period * 2; - if ( !period ) - period = 16; - - do - { - int changed = (shifter + 1) & 2; // set if prev and next bits differ - shifter = (((shifter << 15) ^ (shifter << tap)) & 0x8000) | (shifter >> 1); - if ( changed ) - { - delta = -delta; - synth.offset_inline( time, delta, output ); - } - time += period; - } - while ( time < end_time ); - - this->shifter = shifter; - this->last_amp = delta >> 1; - } - delay = time - end_time; -} - -// Sms_Apu - -Sms_Apu::Sms_Apu() -{ - for ( int i = 0; i < 3; i++ ) - { - squares [i].synth = &square_synth; - oscs [i] = &squares [i]; - } - oscs [3] = &noise; - - volume( 1.0 ); - reset(); -} - -Sms_Apu::~Sms_Apu() -{ -} - -void Sms_Apu::volume( double vol ) -{ - vol *= 0.85 / (osc_count * 64 * 2); - square_synth.volume( vol ); - noise.synth.volume( vol ); -} - -void Sms_Apu::treble_eq( const blip_eq_t& eq ) -{ - square_synth.treble_eq( eq ); - noise.synth.treble_eq( eq ); -} - -void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - require( (unsigned) index < osc_count ); - require( (center && left && right) || (!center && !left && !right) ); - Sms_Osc& osc = *oscs [index]; - osc.outputs [1] = right; - osc.outputs [2] = left; - osc.outputs [3] = center; - osc.output = osc.outputs [osc.output_select]; -} - -void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, center, left, right ); -} - -void Sms_Apu::reset() -{ - stereo_found = false; - last_time = 0; - latch = 0; - - squares [0].reset(); - squares [1].reset(); - squares [2].reset(); - noise.reset(); -} - -void Sms_Apu::run_until( sms_time_t end_time ) -{ - require( end_time >= last_time ); // end_time must not be before previous time - - if ( end_time > last_time ) - { - // run oscillators - for ( int i = 0; i < osc_count; ++i ) - { - Sms_Osc& osc = *oscs [i]; - if ( osc.output ) - { - if ( osc.output != osc.outputs [3] ) - stereo_found = true; // playing on side output - - if ( i < 3 ) - squares [i].run( last_time, end_time ); - else - noise.run( last_time, end_time ); - } - } - - last_time = end_time; - } -} - -bool Sms_Apu::end_frame( sms_time_t end_time ) -{ - if ( end_time > last_time ) - run_until( end_time ); - - assert( last_time >= end_time ); - last_time -= end_time; - - bool result = stereo_found; - stereo_found = false; - return result; -} - -void Sms_Apu::write_ggstereo( sms_time_t time, int data ) -{ - require( (unsigned) data <= 0xFF ); - - run_until( time ); - - for ( int i = 0; i < osc_count; i++ ) - { - Sms_Osc& osc = *oscs [i]; - int flags = data >> i; - Blip_Buffer* old_output = osc.output; - osc.output_select = (flags >> 3 & 2) | (flags & 1); - osc.output = osc.outputs [osc.output_select]; - if ( osc.output != old_output && osc.last_amp ) - { - if ( old_output ) - square_synth.offset( time, -osc.last_amp, old_output ); - osc.last_amp = 0; - } - } -} - -static const unsigned char volumes [16] = { - // volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 ) - 64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0 -}; - -void Sms_Apu::write_data( sms_time_t time, int data ) -{ - require( (unsigned) data <= 0xFF ); - - run_until( time ); - - if ( data & 0x80 ) - latch = data; - - int index = (latch >> 5) & 3; - if ( latch & 0x10 ) - { - oscs [index]->volume = volumes [data & 15]; - } - else if ( index < 3 ) - { - Sms_Square& sq = squares [index]; - if ( data & 0x80 ) - sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF); - else - sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00); - } - else - { - int select = data & 3; - if ( select < 3 ) - noise.period = &noise_periods [select]; - else - noise.period = &squares [2].period; - - int const tap_disabled = 16; - noise.tap = (data & 0x04) ? 12 : tap_disabled; - noise.shifter = 0x8000; - } -} -
--- a/Plugins/Input/console/Sms_Apu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ - -// Sega Master System SN76489 PSG sound chip emulator - -// Sms_Snd_Emu 0.1.3 - -#ifndef SMS_APU_H -#define SMS_APU_H - -typedef long sms_time_t; // clock cycle count - -#include "Sms_Oscs.h" - -class Sms_Apu { -public: - // Set overall volume of all oscillators, where 1.0 is full volume - void volume( double ); - - // Set treble equalization - void treble_eq( const blip_eq_t& ); - - // Outputs can be assigned to a single buffer for mono output, or to three - // buffers for stereo output (using Stereo_Buffer to do the mixing). - - // Assign all oscillator outputs to specified buffer(s). If buffer - // is NULL, silences all oscillators. - void output( Blip_Buffer* mono ); - void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, - // which refer to Square 1, Square 2, Square 3, and Noise. If buffer is NULL, - // silences oscillator. - enum { osc_count = 4 }; - void osc_output( int index, Blip_Buffer* mono ); - void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Reset oscillators and internal state - void reset(); - - // Write GameGear left/right assignment byte - void write_ggstereo( sms_time_t, int ); - - // Write to data port - void write_data( sms_time_t, int ); - - // Run all oscillators up to specified time, end current frame, then - // start a new frame at time 0. Returns true if any oscillators added - // sound to one of the left/right buffers, false if they only added - // to the center buffer. - bool end_frame( sms_time_t ); - -public: - Sms_Apu(); - ~Sms_Apu(); -private: - // noncopyable - Sms_Apu( const Sms_Apu& ); - Sms_Apu& operator = ( const Sms_Apu& ); - - Sms_Osc* oscs [osc_count]; - Sms_Square squares [3]; - Sms_Square::Synth square_synth; // used by squares - sms_time_t last_time; - int latch; - bool stereo_found; - Sms_Noise noise; - - void run_until( sms_time_t ); -}; - -struct sms_apu_state_t -{ - unsigned char regs [8] [2]; - unsigned char latch; -}; - -inline void Sms_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } - -inline void Sms_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } - -#endif -
--- a/Plugins/Input/console/Sms_Oscs.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ - -// Private oscillators used by Sms_Apu - -// Sms_Snd_Emu 0.1.3 - -#ifndef SMS_OSCS_H -#define SMS_OSCS_H - -#include "blargg_common.h" -#include "Blip_Buffer.h" - -struct Sms_Osc -{ - Blip_Buffer* outputs [4]; // NULL, right, left, center - Blip_Buffer* output; - int output_select; - - int delay; - int last_amp; - int volume; - - Sms_Osc(); - void reset(); -}; - -struct Sms_Square : Sms_Osc -{ - int period; - int phase; - - typedef Blip_Synth<blip_good_quality,1> Synth; - const Synth* synth; - - void reset(); - void run( sms_time_t, sms_time_t ); -}; - -struct Sms_Noise : Sms_Osc -{ - const int* period; - unsigned shifter; - unsigned tap; - - typedef Blip_Synth<blip_med_quality,1> Synth; - Synth synth; - - void reset(); - void run( sms_time_t, sms_time_t ); -}; - -#endif -
--- a/Plugins/Input/console/Snes_Spc.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,475 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Snes_Spc.h" - -#include <assert.h> -#include <string.h> - -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -// always in the future (CPU time can go over 0, but not by this much) -int const timer_disabled_time = 127; - -Snes_Spc::Snes_Spc() : dsp( ram ), cpu( this, ram ) -{ - timer [0].shift = 7; // 8 kHz - timer [1].shift = 7; // 8 kHz - timer [2].shift = 4; // 64 kHz - - // Put STOP instruction past end of memory to catch PC overflow. - memset( ram + ram_size, 0xff, (sizeof ram) - ram_size ); -} - -// Load - -blargg_err_t Snes_Spc::load_spc( const void* data, long size, bool clear_echo_ ) -{ - struct spc_file_t { - char signature [27]; - char unused [10]; - uint8_t pc [2]; - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t status; - uint8_t sp; - char unused2 [212]; - uint8_t ram [0x10000]; - uint8_t dsp [128]; - }; - BOOST_STATIC_ASSERT( sizeof (spc_file_t) == spc_file_size ); - - const spc_file_t* spc = (spc_file_t*) data; - - if ( size < spc_file_size ) - return "Not an SPC file"; - - if ( strncmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 ) - return "Not an SPC file"; - - registers_t regs; - regs.pc = spc->pc [1] * 0x100 + spc->pc [0]; - regs.a = spc->a; - regs.x = spc->x; - regs.y = spc->y; - regs.status = spc->status; - regs.sp = spc->sp; - - const char* error = load_state( regs, spc->ram, spc->dsp ); - - echo_accessed = false; - - if ( clear_echo_ ) - clear_echo(); - - return error; -} - -void Snes_Spc::clear_echo() -{ - if ( !(dsp.read( 0x6c ) & 0x20) ) - { - unsigned addr = 0x100 * dsp.read( 0x6d ); - unsigned size = 0x800 * dsp.read( 0x7d ); - unsigned limit = ram_size - addr; - memset( ram + addr, 0xff, (size < limit) ? size : limit ); - } -} - -// Handle other file formats (emulator save states) in user code, not here. - -blargg_err_t Snes_Spc::load_state( const registers_t& cpu_state, const void* new_ram, - const void* dsp_state ) -{ - // cpu - cpu.r = cpu_state; - - // Allow DSP to generate one sample before code starts - // (Tengai Makyo Zero, Tenjin's Table Toss first notes are lost since it - // clears KON 31 cycles from starting execution. It works on the SNES - // since the SPC player adds a few extra cycles delay after restoring - // KON from the DSP registers at the end of an SPC file). - extra_cycles = 32; - - // ram - memcpy( ram, new_ram, ram_size ); - memcpy( extra_ram, ram + rom_addr, sizeof extra_ram ); - - // boot rom (have to force enable_rom() to update it) - rom_enabled = !(ram [0xf1] & 0x80); - enable_rom( !rom_enabled ); - - // dsp - dsp.reset(); - int i; - for ( i = 0; i < Spc_Dsp::register_count; i++ ) - dsp.write( i, ((uint8_t*) dsp_state) [i] ); - - // timers - for ( i = 0; i < timer_count; i++ ) - { - Timer& t = timer [i]; - - t.next_tick = 0; - t.enabled = (ram [0xf1] >> i) & 1; - if ( !t.enabled ) - t.next_tick = timer_disabled_time; - t.count = 0; - t.counter = ram [0xfd + i] & 15; - - int p = ram [0xfa + i]; - t.period = p ? p : 0x100; - } - - // Handle registers which already give 0 when read by setting RAM and not changing it. - // Put STOP instruction in registers which can be read, to catch attempted CPU execution. - ram [0xf0] = 0; - ram [0xf1] = 0; - ram [0xf3] = 0xff; - ram [0xfa] = 0; - ram [0xfb] = 0; - ram [0xfc] = 0; - ram [0xfd] = 0xff; - ram [0xfe] = 0xff; - ram [0xff] = 0xff; - - return NULL; // success -} - -// Hardware - -// Current time starts negative and ends at 0 -inline spc_time_t Snes_Spc::time() const -{ - return -cpu.remain(); -} - -// Keep track of next time to run and avoid a function call if it hasn't been reached. - -// Timers - -void Snes_Spc::Timer::run_until_( spc_time_t time ) -{ - if ( !enabled ) - dprintf( "next_tick: %ld, time: %ld", (long) next_tick, (long) time ); - assert( enabled ); // when disabled, next_tick should always be in the future - - int elapsed = ((time - next_tick) >> shift) + 1; - next_tick += elapsed << shift; - elapsed += count; - if ( elapsed >= period ) { // avoid costly divide - int n = elapsed / period; - elapsed -= n * period; - counter = (counter + n) & 15; - } - count = elapsed; -} - -// DSP - -const int clocks_per_sample = 32; // 1.024 MHz CPU clock / 32000 samples per second - -void Snes_Spc::run_dsp_( spc_time_t time ) -{ - int count = ((time - next_dsp) >> 5) + 1; // divide by clocks_per_sample - sample_t* buf = sample_buf; - if ( buf ) { - sample_buf = buf + count * 2; // stereo - assert( sample_buf <= buf_end ); - } - next_dsp += count * clocks_per_sample; - dsp.run( count, buf ); -} - -inline void Snes_Spc::run_dsp( spc_time_t time ) -{ - if ( time >= next_dsp ) - run_dsp_( time ); -} - -// Debug-only check for read/write within echo buffer, since this might result in -// inaccurate emulation due to the DSP not being caught up to the present. -inline void Snes_Spc::check_for_echo_access( spc_addr_t addr ) -{ - if ( !echo_accessed && !(dsp.read( 0x6c ) & 0x20) ) - { - // ** If echo accesses are found that require running the DSP, cache - // the start and end address on DSP writes to speed up checking. - - unsigned start = 0x100 * dsp.read( 0x6d ); - unsigned end = start + 0x800 * dsp.read( 0x7d ); - if ( start <= addr && addr < end ) { - echo_accessed = true; - dprintf( "Read/write at $%04X within echo buffer\n", (unsigned) addr ); - } - } -} - -// Read - -int Snes_Spc::read( spc_addr_t addr ) -{ - // zero page ram is used most often - if ( addr < 0xf0 ) - return ram [addr]; - - // dsp - if ( addr == 0xf3 ) { - run_dsp( time() ); - if ( ram [0xf2] >= Spc_Dsp::register_count ) - dprintf( "DSP read from $%02X\n", (int) ram [0xf2] ); - return dsp.read( ram [0xf2] & 0x7f ); - } - - // counters - unsigned i = addr - 0xfd; // negative converts to large positive unsigned - if ( i < timer_count ) { - Timer& t = timer [i]; - t.run_until( time() ); - int result = t.counter; - t.counter = 0; - return result; - } - - if ( addr == 0xf0 || addr == 0xf1 || addr == 0xf8 || - addr == 0xf9 || addr == 0xfa ) - dprintf( "Read from register $%02X\n", (int) addr ); - - // Registers which always read as 0 are handled by setting ram [reg] to 0 - // at startup then never changing that value. - - check(( check_for_echo_access( addr ), true )); - - // ram - return ram [addr]; -} - - -// Write - -const unsigned char Snes_Spc::boot_rom [rom_size] = { // verified - 0xCD, 0xEF, 0xBD, 0xE8, 0x00, 0xC6, 0x1D, 0xD0, - 0xFC, 0x8F, 0xAA, 0xF4, 0x8F, 0xBB, 0xF5, 0x78, - 0xCC, 0xF4, 0xD0, 0xFB, 0x2F, 0x19, 0xEB, 0xF4, - 0xD0, 0xFC, 0x7E, 0xF4, 0xD0, 0x0B, 0xE4, 0xF5, - 0xCB, 0xF4, 0xD7, 0x00, 0xFC, 0xD0, 0xF3, 0xAB, - 0x01, 0x10, 0xEF, 0x7E, 0xF4, 0x10, 0xEB, 0xBA, - 0xF6, 0xDA, 0x00, 0xBA, 0xF4, 0xC4, 0xF4, 0xDD, - 0x5D, 0xD0, 0xDB, 0x1F, 0x00, 0x00, 0xC0, 0xFF -}; - -void Snes_Spc::enable_rom( bool enable ) -{ - if ( rom_enabled != enable ) - { - rom_enabled = enable; - memcpy( ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size ); - } -} - -void Snes_Spc::write( spc_addr_t addr, int data ) -{ - // first page is very common - if ( addr < 0xf0 ) { - ram [addr] = data; - } - else switch ( addr ) - { - // RAM - default: - check(( check_for_echo_access( addr ), true )); - if ( addr < rom_addr ) { - ram [addr] = data; - } - else { - extra_ram [addr - rom_addr] = data; - if ( !rom_enabled ) - ram [addr] = data; - } - break; - - // DSP - //case 0xf2: // mapped to RAM - case 0xf3: { - run_dsp( time() ); - int reg = ram [0xf2]; - if ( next_dsp > 0 ) { - // skip mode - - // key press - if ( reg == 0x4C ) - keys_pressed |= data & ~dsp.read( 0x5C ); - - // key release - if ( reg == 0x5C ) { - keys_released |= data; - keys_pressed &= ~data; - } - } - if ( reg < Spc_Dsp::register_count ) { - dsp.write( reg, data ); - } - else { - dprintf( "DSP write to $%02X\n", (int) reg ); - } - break; - } - - case 0xf0: // Test register - dprintf( "Wrote $%02X to $F0\n", (int) data ); - break; - - // Config - case 0xf1: - { - // timers - for ( int i = 0; i < timer_count; i++ ) - { - Timer& t = timer [i]; - if ( !(data & (1 << i)) ) { - t.enabled = 0; - t.next_tick = timer_disabled_time; - } - else if ( !t.enabled ) { - // just enabled - t.enabled = 1; - t.counter = 0; - t.count = 0; - t.next_tick = time(); - } - } - - // port clears - if ( data & 0x10 ) { - ram [0xf4] = 0; - ram [0xf5] = 0; - } - if ( data & 0x20 ) { - ram [0xf6] = 0; - ram [0xf7] = 0; - } - - enable_rom( data & 0x80 ); - - break; - } - - // Ports - case 0xf4: - case 0xf5: - case 0xf6: - case 0xf7: - // to do: handle output ports - break; - - //case 0xf8: // verified on SNES that these are read/write (RAM) - //case 0xf9: - - // Timers - case 0xfa: - case 0xfb: - case 0xfc: { - Timer& t = timer [addr - 0xfa]; - if ( (t.period & 0xff) != data ) { - t.run_until( time() ); - t.period = data ? data : 0x100; - } - break; - } - - // Counters (cleared on write) - case 0xfd: - case 0xfe: - case 0xff: - dprintf( "Wrote to counter $%02X\n", (int) addr ); - timer [addr - 0xfd].counter = 0; - break; - } -} - -// Play - -blargg_err_t Snes_Spc::skip( long count ) -{ - if ( count > 4 * 32000L ) - { - // don't run DSP for long durations (2-3 times faster) - - const long sync_count = 32000L * 2; - - // keep track of any keys pressed/released (and not subsequently released) - keys_pressed = 0; - keys_released = 0; - // sentinel tells play to ignore DSP - BLARGG_RETURN_ERR( play( count - sync_count, skip_sentinel ) ); - - // press/release keys now - dsp.write( 0x5C, keys_released & ~keys_pressed ); - dsp.write( 0x4C, keys_pressed ); - - clear_echo(); - - // play the last few seconds normally to help synchronize DSP - count = sync_count; - } - - return play( count ); -} - -blargg_err_t Snes_Spc::play( long count, sample_t* out ) -{ - require( count % 2 == 0 ); // output is always in pairs of samples - - // CPU time() runs from -duration to 0 - spc_time_t duration = (count / 2) * clocks_per_sample; - - // DSP output is made on-the-fly when the CPU reads/writes DSP registers - sample_buf = out; - buf_end = out + (out && out != skip_sentinel ? count : 0); - next_dsp = (out == skip_sentinel) ? clocks_per_sample : -duration + clocks_per_sample; - - // Localize timer next_tick times and run them to the present to prevent a running - // but ignored timer's next_tick from getting too far behind and overflowing. - for ( int i = 0; i < timer_count; i++ ) - { - Timer& t = timer [i]; - if ( t.enabled ) - { - t.next_tick -= duration; - t.run_until( -duration ); - } - } - - // Run CPU for duration, reduced by any extra cycles from previous run - int elapsed = cpu.run( duration - extra_cycles ); - if ( elapsed > 0 ) - { - dprintf( "Unhandled instruction $%02X, pc = $%04X\n", - (int) cpu.read( cpu.r.pc ), (unsigned) cpu.r.pc ); - return "Emulation error"; - } - extra_cycles = -elapsed; - - // Catch DSP up to present. - run_dsp( 0 ); - if ( out ) { - assert( next_dsp == clocks_per_sample ); - assert( out == skip_sentinel || sample_buf - out == count ); - } - buf_end = NULL; - - return blargg_success; -} -
--- a/Plugins/Input/console/Snes_Spc.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,113 +0,0 @@ - -// Super Nintendo (SNES) SPC-700 APU Emulator - -// Game_Music_Emu 0.3.0 - -#ifndef SNES_SPC_H -#define SNES_SPC_H - -#include "blargg_common.h" -#include "Spc_Cpu.h" -#include "Spc_Dsp.h" - -class Snes_Spc { -public: - typedef BOOST::uint8_t uint8_t; - - Snes_Spc(); - - // Load copy of SPC data into emulator. Clear echo buffer if 'clear_echo' is true. - enum { spc_file_size = 0x10180 }; - blargg_err_t load_spc( const void* spc, long spc_size, bool clear_echo = 1 ); - - // Load copy of state into emulator. - typedef Spc_Cpu::registers_t registers_t; - blargg_err_t load_state( const registers_t& cpu_state, const void* ram_64k, - const void* dsp_regs_128 ); - - // Clear echo buffer - void clear_echo(); - - // Mute voice n if bit n (1 << n) of mask is set - enum { voice_count = Spc_Dsp::voice_count }; - void mute_voices( int mask ); - - // Generate 'count' samples and optionally write to 'buf'. Count must be even. - // Sample output is 16-bit 32kHz, signed stereo pairs with the left channel first. - typedef short sample_t; - blargg_err_t play( long count, sample_t* buf = NULL ); - - // Skip forward by the specified number of samples (64000 samples = 1 second) - blargg_err_t skip( long count ); - - // Set gain, where 1.0 is normal. When greater than 1.0, output is clamped the - // 16-bit sample range. - void set_gain( double ); - - // If true, prevent channels and global volumes from being phase-negated - void disable_surround( bool disable ); - -// End of public interface -private: - // timers - struct Timer - { - spc_time_t next_tick; - int period; - int count; - int shift; - int counter; - int enabled; - - void run_until_( spc_time_t ); - void run_until( spc_time_t time ) - { - if ( time >= next_tick ) - run_until_( time ); - } - }; - enum { timer_count = 3 }; - Timer timer [timer_count]; - - // hardware - int extra_cycles; - spc_time_t time() const; - int read( spc_addr_t ); - void write( spc_addr_t, int ); - friend class Spc_Cpu; - - // dsp - sample_t* sample_buf; - sample_t* buf_end; // to do: remove this once possible bug resolved - spc_time_t next_dsp; - Spc_Dsp dsp; - int keys_pressed; - int keys_released; - sample_t skip_sentinel [1]; // special value for play() passed by skip() - void run_dsp( spc_time_t ); - void run_dsp_( spc_time_t ); - bool echo_accessed; - void check_for_echo_access( spc_addr_t ); - - // boot rom - enum { rom_size = 64 }; - enum { rom_addr = 0xffc0 }; - bool rom_enabled; - uint8_t extra_ram [rom_size]; - static const uint8_t boot_rom [rom_size]; - void enable_rom( bool ); - - // CPU and RAM (at end because it's large) - Spc_Cpu cpu; - enum { ram_size = 0x10000 }; - uint8_t ram [ram_size + 0x100]; // padding for catching jumps past end -}; - -inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); } - -inline void Snes_Spc::mute_voices( int mask ) { dsp.mute_voices( mask ); } - -inline void Snes_Spc::set_gain( double v ) { dsp.set_gain( v ); } - -#endif -
--- a/Plugins/Input/console/Spc_Cpu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1066 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Spc_Cpu.h" - -#include <limits.h> - -#include "blargg_endian.h" -#include "Snes_Spc.h" - -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -// Several instructions are commented out (or not even implemented). These aren't -// used by the SPC files tested. - -// Optimize performance for the most common instructions, and size for the rest: -// -// 15% 0xF0 BEQ rel -// 8% 0xE4 MOV A,dp -// 4% 0xF5 MOV A,abs+X -// 4% 0xD0 BNE rel -// 4% 0x6F RET -// 4% 0x3F CALL addr -// 4% 0xF4 MOV A,dp+X -// 3% 0xC4 MOV dp,A -// 2% 0xEB MOV Y,dp -// 2% 0x3D INC X -// 2% 0xF6 MOV A,abs+Y -// (1% and below not shown) - -Spc_Cpu::Spc_Cpu( Snes_Spc* e, uint8_t* ram_in ) : ram( ram_in ), emu( *e ) -{ - remain_ = 0; - BOOST_STATIC_ASSERT( sizeof (int) >= 4 ); -} - -#define READ( addr ) (emu.read( addr )) -#define WRITE( addr, value ) (emu.write( addr, value )) - -#define READ_DP( addr ) READ( (addr) + dp ) -#define WRITE_DP( addr, value ) WRITE( (addr) + dp, value ) - -#define READ_PROG( addr ) (ram [addr]) -#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) ) - -int Spc_Cpu::read( spc_addr_t addr ) -{ - return READ( addr ); -} - -void Spc_Cpu::write( spc_addr_t addr, int data ) -{ - WRITE( addr, data ); -} - -// Cycle table derived from text copy of SPC-700 manual (using regular expressions) -static const unsigned char cycle_table [0x100] = { -// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, // 0 - 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, // 1 - 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, // 2 - 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, // 3 - 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, // 4 - 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, // 5 - 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, // 6 - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, // 7 - 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, // 8 - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,// 9 - 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, // A - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, // B - 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, // C - 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, // D - 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, // E - 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3 // F -}; - -// The C,mem instructions are hardly used, so a non-inline function is used for -// the common access code. -unsigned Spc_Cpu::mem_bit( spc_addr_t pc ) -{ - unsigned addr = READ_PROG16( pc ); - unsigned t = READ( addr & 0x1fff ) >> (addr >> 13); - return (t << 8) & 0x100; -} - -#include BLARGG_ENABLE_OPTIMIZER - -spc_time_t Spc_Cpu::run( spc_time_t cycle_count ) -{ - remain_ = cycle_count; - - uint8_t* const ram = this->ram; // cache - - // Stack pointer is kept one greater than usual SPC stack pointer to allow - // common pre-decrement and post-increment memory instructions that some - // processors have. Address wrap-around isn't supported. - #define PUSH( v ) (*--sp = (v)) - #define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) - #define POP() (*sp++) - #define SET_SP( v ) (sp = ram + 0x101 + (v)) - #define GET_SP() (sp - 0x101 - ram) - - uint8_t* sp; - SET_SP( r.sp ); - - // registers - unsigned pc = r.pc; - int a = r.a; - int x = r.x; - int y = r.y; - - // status flags - - const int st_n = 0x80; - const int st_v = 0x40; - const int st_p = 0x20; - const int st_b = 0x10; - const int st_h = 0x08; - const int st_i = 0x04; - const int st_z = 0x02; - const int st_c = 0x01; - - #define IS_NEG (nz & 0x880) - - #define CALC_STATUS( out ) do { \ - out = status & ~(st_n | st_z | st_c); \ - out |= (c >> 8) & st_c; \ - out |= (dp >> 3) & st_p; \ - if ( IS_NEG ) out |= st_n; \ - if ( !(nz & 0xFF) ) out |= st_z; \ - } while ( 0 ) - - #define SET_STATUS( in ) do { \ - status = in & ~(st_n | st_z | st_c | st_p); \ - c = in << 8; \ - nz = (in << 4) & 0x800; \ - nz |= ~in & st_z; \ - dp = (in << 3) & 0x100; \ - } while ( 0 ) - - uint8_t status; - int c; // store C as 'c' & 0x100. - int nz; // Z set if (nz & 0xff) == 0, N set if (nz & 0x880) != 0 - unsigned dp; // direct page base - { - int temp = r.status; - SET_STATUS( temp ); - } - - goto loop; - - unsigned data; // first operand of instruction and temporary across function calls - - // Common endings for instructions -cbranch_taken_loop: // compare and branch - pc += (BOOST::int8_t) READ_PROG( pc ); - remain_ -= 2; -inc_pc_loop: // end of instruction with an operand - pc++; -loop: - - check( (unsigned) pc < 0x10000 ); - check( (unsigned) GET_SP() < 0x100 ); - - assert( (unsigned) a < 0x100 ); - assert( (unsigned) x < 0x100 ); - assert( (unsigned) y < 0x100 ); - - unsigned opcode = READ_PROG( pc ); - pc++; - // to do: if pc is at end of memory, this will get wrong byte - data = READ_PROG( pc ); - - if ( remain_ <= 0 ) - goto stop; - - remain_ -= cycle_table [opcode]; - - // Use 'data' for temporaries whose lifetime crosses read/write calls, otherwise - // use a local temporary. - switch ( opcode ) - { - - #define BRANCH( cond ) { \ - pc++; \ - int offset = (BOOST::int8_t) data; \ - if ( cond ) { \ - pc += offset; \ - remain_ -= 2; \ - } \ - goto loop; \ - } - -// Most-Common - - case 0xF0: // BEQ (most common) - BRANCH( !(uint8_t) nz ) - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ) - - case 0x3F: // CALL - PUSH16( pc + 2 ); - pc = READ_PROG16( pc ); - goto loop; - - case 0x6F: // RET - pc = POP(); - pc += POP() * 0x100; - goto loop; - -#define CASE( n ) case n: - -// Define common address modes based on opcode for immediate mode. Execution -// ends with data set to the address of the operand. -#define ADDR_MODES( op ) \ - CASE( op - 0x02 ) /* (X) */ \ - data = x + dp; \ - pc--; \ - goto end_##op; \ - CASE( op + 0x0F ) /* (dp)+Y */ \ - data = READ_PROG16( data + dp ) + y;\ - goto end_##op; \ - CASE( op - 0x01 ) /* (dp+X) */ \ - data = READ_PROG16( uint8_t (data + x) + dp );\ - goto end_##op; \ - CASE( op + 0x0E ) /* abs+Y */ \ - data += y; \ - goto abs_##op; \ - CASE( op + 0x0D ) /* abs+X */ \ - data += x; \ - CASE( op - 0x03 ) /* abs */ \ - abs_##op: \ - pc++; \ - data += 0x100 * READ_PROG( pc );\ - goto end_##op; \ - CASE( op + 0x0C ) /* dp+X */ \ - data = uint8_t (data + x); \ - CASE( op - 0x04 ) /* dp */ \ - data += dp; \ - end_##op: - -// 1. 8-bit Data Transmission Commands. Group I - - ADDR_MODES( 0xE8 ) // MOV A,addr - // case 0xE4: // MOV a,dp (most common) - mov_a_addr: - a = nz = READ( data ); - goto inc_pc_loop; - case 0xBF: // MOV A,(X)+ - data = x + dp; - x = uint8_t (x + 1); - pc--; - goto mov_a_addr; - - case 0xE8: // MOV A,imm - a = data; - nz = data; - goto inc_pc_loop; - - case 0xF9: // MOV X,dp+Y - data = uint8_t (data + y); - case 0xF8: // MOV X,dp - data += dp; - goto mov_x_addr; - case 0xE9: // MOV X,abs - data = READ_PROG16( pc ); - pc++; - mov_x_addr: - data = READ( data ); - case 0xCD: // MOV X,imm - x = data; - nz = data; - goto inc_pc_loop; - - case 0xFB: // MOV Y,dp+X - data = uint8_t (data + x); - case 0xEB: // MOV Y,dp - data += dp; - goto mov_y_addr; - case 0xEC: // MOV Y,abs - data = READ_PROG16( pc ); - pc++; - mov_y_addr: - data = READ( data ); - case 0x8D: // MOV Y,imm - y = data; - nz = data; - goto inc_pc_loop; - -// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 - - ADDR_MODES( 0xC8 ) // MOV addr,A - WRITE( data, a ); - goto inc_pc_loop; - - { - int temp; - case 0xCC: // MOV abs,Y - temp = y; - goto mov_abs_temp; - case 0xC9: // MOV abs,X - temp = x; - mov_abs_temp: - WRITE( READ_PROG16( pc ), temp ); - pc += 2; - goto loop; - } - - case 0xD9: // MOV dp+Y,X - data = uint8_t (data + y); - case 0xD8: // MOV dp,X - WRITE( data + dp, x ); - goto inc_pc_loop; - - case 0xDB: // MOV dp+X,Y - data = uint8_t (data + x); - case 0xCB: // MOV dp,Y - WRITE( data + dp, y ); - goto inc_pc_loop; - - case 0xFA: // MOV dp,dp - data = READ( data + dp ); - case 0x8F: // MOV dp,#imm - pc++; - WRITE_DP( READ_PROG( pc ), data ); - goto inc_pc_loop; - -// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. - - case 0x7D: // MOV A,X - a = x; - nz = x; - goto loop; - - case 0xDD: // MOV A,Y - a = y; - nz = y; - goto loop; - - case 0x5D: // MOV X,A - x = a; - nz = a; - goto loop; - - case 0xFD: // MOV Y,A - y = a; - nz = a; - goto loop; - - case 0x9D: // MOV X,SP - x = nz = GET_SP(); - goto loop; - - case 0xBD: // MOV SP,X - SET_SP( x ); - goto loop; - - //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) - - case 0xAF: // MOV (X)+,A - WRITE_DP( x, a ); - x++; - goto loop; - -// 5. 8-BIT LOGIC OPERATION COMMANDS - -#define LOGICAL_OP( op, func ) \ - ADDR_MODES( op ) /* addr */ \ - data = READ( data ); \ - case op: /* imm */ \ - nz = a func##= data; \ - goto inc_pc_loop; \ - { unsigned addr; \ - case op + 0x11: /* X,Y */ \ - data = READ_DP( y ); \ - addr = x + dp; \ - pc--; \ - goto addr_##op; \ - case op + 0x01: /* dp,dp */ \ - data = READ_DP( data ); \ - case op + 0x10: /*dp,imm*/\ - pc++; \ - addr = READ_PROG( pc ) + dp;\ - addr_##op: \ - nz = data func READ( addr );\ - WRITE( addr, nz ); \ - goto inc_pc_loop; \ - } - - LOGICAL_OP( 0x28, & ); // AND - - LOGICAL_OP( 0x08, | ); // OR - - LOGICAL_OP( 0x48, ^ ); // EOR - -// 4. 8-BIT ARITHMETIC OPERATION COMMANDS - - ADDR_MODES( 0x68 ) // CMP addr - data = READ( data ); - case 0x68: // CMP imm - nz = a - data; - c = ~nz; - nz &= 0xff; - goto inc_pc_loop; - - case 0x79: // CMP (X),(Y) - data = READ_DP( x ); - nz = data - READ_DP( y ); - c = ~nz; - nz &= 0xff; - goto loop; - - case 0x69: // CMP (dp),(dp) - data = READ_DP( data ); - case 0x78: // CMP dp,imm - pc++; - nz = READ_DP( READ_PROG( pc ) ) - data; - c = ~nz; - nz &= 0xff; - goto inc_pc_loop; - - case 0x3E: // CMP X,dp - data += dp; - goto cmp_x_addr; - case 0x1E: // CMP X,abs - data = READ_PROG16( pc ); - pc++; - cmp_x_addr: - data = READ( data ); - case 0xC8: // CMP X,imm - nz = x - data; - c = ~nz; - nz &= 0xff; - goto inc_pc_loop; - - case 0x7E: // CMP Y,dp - data += dp; - goto cmp_y_addr; - case 0x5E: // CMP Y,abs - data = READ_PROG16( pc ); - pc++; - cmp_y_addr: - data = READ( data ); - case 0xAD: // CMP Y,imm - nz = y - data; - c = ~nz; - nz &= 0xff; - goto inc_pc_loop; - - { - int addr; - case 0xB9: // SBC (x),(y) - case 0x99: // ADC (x),(y) - pc--; // compensate for inc later - data = READ_DP( x ); - addr = y + dp; - goto adc_addr; - case 0xA9: // SBC dp,dp - case 0x89: // ADC dp,dp - data = READ_DP( data ); - case 0xB8: // SBC dp,imm - case 0x98: // ADC dp,imm - pc++; - addr = READ_PROG( pc ) + dp; - adc_addr: - nz = READ( addr ); - goto adc_data; - -// catch ADC and SBC together, then decode later based on operand -#undef CASE -#define CASE( n ) case n: case (n) + 0x20: - ADDR_MODES( 0x88 ) // ADC/SBC addr - data = READ( data ); - case 0xA8: // SBC imm - case 0x88: // ADC imm - addr = -1; // A - nz = a; - adc_data: { - if ( opcode & 0x20 ) - data ^= 0xff; // SBC - int carry = (c >> 8) & 1; - int ov = (nz ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - int hc = (nz & 15) + carry; - c = nz += data + carry; - hc = (nz & 15) - hc; - status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) | ((hc >> 1) & st_h); - if ( addr < 0 ) { - a = (uint8_t) nz; - goto inc_pc_loop; - } - WRITE( addr, (uint8_t) nz ); - goto inc_pc_loop; - } - - } - -// 6. ADDITION & SUBTRACTION COMMANDS - -#define INC_DEC_REG( reg, n ) \ - nz = reg + n; \ - reg = (uint8_t) nz; \ - goto loop; - - case 0xBC: INC_DEC_REG( a, 1 ) // INC A - case 0x3D: INC_DEC_REG( x, 1 ) // INC X - case 0xFC: INC_DEC_REG( y, 1 ) // INC Y - - case 0x9C: INC_DEC_REG( a, -1 ) // DEC A - case 0x1D: INC_DEC_REG( x, -1 ) // DEC X - case 0xDC: INC_DEC_REG( y, -1 ) // DEC Y - - case 0x9B: // DEC dp+X - case 0xBB: // INC dp+X - data = uint8_t (data + x); - case 0x8B: // DEC dp - case 0xAB: // INC dp - data += dp; - goto inc_abs; - case 0x8C: // DEC abs - case 0xAC: // INC abs - data = READ_PROG16( pc ); - pc++; - inc_abs: - nz = ((opcode >> 4) & 2) - 1; - nz += READ( data ); - WRITE( data, (uint8_t) nz ); - goto inc_pc_loop; - -// 7. SHIFT, ROTATION COMMANDS - - case 0x5C: // LSR A - c = 0; - case 0x7C:{// ROR A - nz = ((c >> 1) & 0x80) | (a >> 1); - c = a << 8; - a = nz; - goto loop; - } - - case 0x1C: // ASL A - c = 0; - case 0x3C:{// ROL A - int temp = (c >> 8) & 1; - c = a << 1; - nz = c | temp; - a = (uint8_t) nz; - goto loop; - } - - case 0x0B: // ASL dp - c = 0; - data += dp; - goto rol_mem; - case 0x1B: // ASL dp+X - c = 0; - case 0x3B: // ROL dp+X - data = uint8_t (data + x); - case 0x2B: // ROL dp - data += dp; - goto rol_mem; - case 0x0C: // ASL abs - c = 0; - case 0x2C: // ROL abs - data = READ_PROG16( pc ); - pc++; - rol_mem: - nz = (c >> 8) & 1; - nz |= (c = READ( data ) << 1); - WRITE( data, (uint8_t) nz ); - goto inc_pc_loop; - - case 0x4B: // LSR dp - c = 0; - data += dp; - goto ror_mem; - case 0x5B: // LSR dp+X - c = 0; - case 0x7B: // ROR dp+X - data = uint8_t (data + x); - case 0x6B: // ROR dp - data += dp; - goto ror_mem; - case 0x4C: // LSR abs - c = 0; - case 0x6C: // ROR abs - data = READ_PROG16( pc ); - pc++; - ror_mem: { - int temp = READ( data ); - nz = ((c >> 1) & 0x80) | (temp >> 1); - c = temp << 8; - WRITE( data, nz ); - goto inc_pc_loop; - } - - case 0x9F: // XCN - nz = a = (a >> 4) | uint8_t (a << 4); - goto loop; - -// 8. 16-BIT TRANSMISION COMMANDS - - case 0xBA: // MOVW YA,dp - a = READ_DP( data ); - nz = (a & 0x7f) | (a >> 1); - y = READ_DP( uint8_t (data + 1) ); - nz |= y; - goto inc_pc_loop; - - case 0xDA: // MOVW dp,YA - WRITE_DP( data, a ); - WRITE_DP( uint8_t (data + 1), y ); - goto inc_pc_loop; - -// 9. 16-BIT OPERATION COMMANDS - - case 0x3A: // INCW dp - case 0x1A:{// DECW dp - data += dp; - - // low byte - int temp = READ( data ); - temp += ((opcode >> 4) & 2) - 1; // +1 for INCW, -1 for DECW - nz = ((temp >> 1) | temp) & 0x7f; - WRITE( data, (uint8_t) temp ); - - // high byte - data = uint8_t (data + 1) + dp; - temp >>= 8; - temp = uint8_t (temp + READ( data )); - nz |= temp; - WRITE( data, temp ); - - goto inc_pc_loop; - } - - case 0x9A: // SUBW YA,dp - case 0x7A: // ADDW YA,dp - { - // read 16-bit addend - int temp = READ_DP( data ); - int sign = READ_DP( uint8_t (data + 1) ); - temp += 0x100 * sign; - status &= ~(st_v | st_h); - - // to do: fix half-carry for SUBW (it's probably wrong) - - // for SUBW, negate and truncate to 16 bits - if ( opcode & 0x80 ) { - temp = (temp ^ 0xFFFF) + 1; - sign = temp >> 8; - } - - // add low byte (A) - temp += a; - a = (uint8_t) temp; - nz = (temp | (temp >> 1)) & 0x7f; - - // add high byte (Y) - temp >>= 8; - c = y + temp; - nz = (nz | c) & 0xff; - - // half-carry (temporary avoids CodeWarrior optimizer bug) - unsigned hc = (c & 15) - (y & 15); - status |= (hc >> 4) & st_h; - - // overflow if sign of YA changed when previous sign and addend sign were same - status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v; - - y = (uint8_t) c; - - goto inc_pc_loop; - } - - case 0x5A: { // CMPW YA,dp - int temp = a - READ_DP( data ); - nz = ((temp >> 1) | temp) & 0x7f; - temp = y + (temp >> 8); - temp -= READ_DP( uint8_t (data + 1) ); - nz |= temp; - c = ~temp; - nz &= 0xff; - goto inc_pc_loop; - } - -// 10. MULTIPLICATION & DIVISON COMMANDS - - case 0xCF: { // MUL YA - unsigned temp = y * a; - a = (uint8_t) temp; - nz = ((temp >> 1) | temp) & 0x7f; - y = temp >> 8; - nz |= y; - goto loop; - } - - case 0x9E: // DIV YA,X - { - // behavior based on SPC CPU tests - - status &= ~(st_h | st_v); - - if ( (y & 15) >= (x & 15) ) - status |= st_h; - - if ( y >= x ) - status |= st_v; - - unsigned ya = y * 0x100 + a; - if ( y < x * 2 ) - { - a = ya / x; - y = ya - a * x; - } - else - { - a = 255 - (ya - x * 0x200) / (256 - x); - y = x + (ya - x * 0x200) % (256 - x); - } - - nz = (uint8_t) a; - a = (uint8_t) a; - - goto loop; - } - -// 11. DECIMAL COMPENSATION COMMANDS - - // seem unused - // case 0xDF: // DAA - // case 0xBE: // DAS - -// 12. BRANCHING COMMANDS - - case 0x2F: // BRA rel - pc += (BOOST::int8_t) data; - goto inc_pc_loop; - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0x10: // BPL - BRANCH( !IS_NEG ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x03: // BBS dp.bit,rel - case 0x23: - case 0x43: - case 0x63: - case 0x83: - case 0xA3: - case 0xC3: - case 0xE3: - pc++; - if ( (READ_DP( data ) >> (opcode >> 5)) & 1 ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0x13: // BBC dp.bit,rel - case 0x33: - case 0x53: - case 0x73: - case 0x93: - case 0xB3: - case 0xD3: - case 0xF3: - pc++; - if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0xDE: // CBNE dp+X,rel - data = uint8_t (data + x); - // fall through - case 0x2E: // CBNE dp,rel - pc++; - if ( READ_DP( data ) != a ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0xFE: // DBNZ Y,rel - y = uint8_t (y - 1); - BRANCH( y ) - - case 0x6E: { // DBNZ dp,rel - pc++; - unsigned temp = READ_DP( data ) - 1; - WRITE_DP( (uint8_t) data, (uint8_t) temp ); - if ( temp ) - goto cbranch_taken_loop; - goto inc_pc_loop; - } - - case 0x1F: // JMP (abs+X) - pc = READ_PROG16( pc ) + x; - // fall through - case 0x5F: // JMP abs - pc = READ_PROG16( pc ); - goto loop; - -// 13. SUB-ROUTINE CALL RETURN COMMANDS - - case 0x0F: // BRK - check( false ); // untested - PUSH16( pc + 1 ); - pc = READ_PROG16( 0xffde ); // vector address verified - int temp; - CALC_STATUS( temp ); - PUSH( temp ); - status = (status | st_b) & ~st_i; - goto loop; - - case 0x4F: // PCALL offset - pc++; - PUSH16( pc ); - pc = 0xff00 + data; - goto loop; - - case 0x01: // TCALL n - case 0x11: - case 0x21: - case 0x31: - case 0x41: - case 0x51: - case 0x61: - case 0x71: - case 0x81: - case 0x91: - case 0xA1: - case 0xB1: - case 0xC1: - case 0xD1: - case 0xE1: - case 0xF1: - PUSH16( pc ); - pc = READ_PROG16( 0xffde - (opcode >> 3) ); - goto loop; - -// 14. STACK OPERATION COMMANDS - - { - int temp; - case 0x7F: // RET1 - temp = POP(); - pc = POP(); - pc |= POP() << 8; - goto set_status; - case 0x8E: // POP PSW - temp = POP(); - set_status: - SET_STATUS( temp ); - goto loop; - } - - case 0x0D: { // PUSH PSW - int temp; - CALC_STATUS( temp ); - PUSH( temp ); - goto loop; - } - - case 0x2D: // PUSH A - PUSH( a ); - goto loop; - - case 0x4D: // PUSH X - PUSH( x ); - goto loop; - - case 0x6D: // PUSH Y - PUSH( y ); - goto loop; - - case 0xAE: // POP A - a = POP(); - goto loop; - - case 0xCE: // POP X - x = POP(); - goto loop; - - case 0xEE: // POP Y - y = POP(); - goto loop; - -// 15. BIT OPERATION COMMANDS - - case 0x02: // SET1 - case 0x22: - case 0x42: - case 0x62: - case 0x82: - case 0xA2: - case 0xC2: - case 0xE2: - case 0x12: // CLR1 - case 0x32: - case 0x52: - case 0x72: - case 0x92: - case 0xB2: - case 0xD2: - case 0xF2: { - data += dp; - int bit = 1 << (opcode >> 5); - int mask = ~bit; - if ( opcode & 0x10 ) - bit = 0; - WRITE( data, (READ( data ) & mask) | bit ); - goto inc_pc_loop; - } - - case 0x0E: // TSET1 abs - case 0x4E:{// TCLR1 abs - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data ); - nz = temp & a; - temp &= ~a; - if ( !(opcode & 0x40) ) - temp |= a; - WRITE( data, temp ); - goto loop; - } - - case 0x4A: // AND1 C,mem.bit - c &= mem_bit( pc ); - pc += 2; - goto loop; - - case 0x6A: // AND1 C,/mem.bit - check( false ); // untested - c &= ~mem_bit( pc ); - pc += 2; - goto loop; - - case 0x0A: // OR1 C,mem.bit - check( false ); // untested - c |= mem_bit( pc ); - pc += 2; - goto loop; - - case 0x2A: // OR1 C,/mem.bit - check( false ); // untested - c |= ~mem_bit( pc ); - pc += 2; - goto loop; - - case 0x8A: // EOR1 C,mem.bit - c ^= mem_bit( pc ); - pc += 2; - goto loop; - - case 0xEA: { // NOT1 mem.bit - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data & 0x1fff ); - temp ^= 1 << (data >> 13); - WRITE( data & 0x1fff, temp ); - goto loop; - } - - case 0xCA: { // MOV1 mem.bit,C - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data & 0x1fff ); - unsigned bit = data >> 13; - temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit); - WRITE( data & 0x1fff, temp ); - goto loop; - } - - case 0xAA: // MOV1 C,mem.bit - c = mem_bit( pc ); - pc += 2; - goto loop; - -// 16. PROGRAM STATUS FLAG OPERATION COMMANDS - - case 0x60: // CLRC - c = 0; - goto loop; - - case 0x80: // SETC - c = ~0; - goto loop; - - case 0xED: // NOTC - c ^= 0x100; - goto loop; - - case 0xE0: // CLRV - status &= ~(st_v | st_h); - goto loop; - - case 0x20: // CLRP - dp = 0; - goto loop; - - case 0x40: // SETP - dp = 0x100; - goto loop; - - case 0xA0: // EI - check( false ); // untested - status |= st_i; - goto loop; - - case 0xC0: // DI - check( false ); // untested - status &= ~st_i; - goto loop; - -// 17. OTHER COMMANDS - - case 0x00: // NOP - goto loop; - - //case 0xEF: // SLEEP - //case 0xFF: // STOP - - } // switch - - // unhandled instructions fall out of switch so emulator can catch them - -stop: - pc--; - - { - int temp; - CALC_STATUS( temp ); - r.status = temp; - } - - r.pc = pc; - r.sp = GET_SP(); - r.a = a; - r.x = x; - r.y = y; - - return remain_; -} -
--- a/Plugins/Input/console/Spc_Cpu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ - -// Super Nintendo (SNES) SPC-700 CPU emulator - -// Game_Music_Emu 0.3.0 - -#ifndef SPC_CPU_H -#define SPC_CPU_H - -#include "blargg_common.h" - -typedef unsigned spc_addr_t; -typedef long spc_time_t; - -class Snes_Spc; - -class Spc_Cpu { - typedef BOOST::uint8_t uint8_t; - uint8_t* const ram; -public: - // Keeps pointer to 64K RAM - Spc_Cpu( Snes_Spc* spc, uint8_t* ram ); - - // SPC-700 registers. *Not* kept updated during a call to run(). - struct registers_t { - long pc; // more than 16 bits to allow overflow detection - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t status; - uint8_t sp; - } r; - - // Run CPU for at least 'count' cycles. Return the number of cycles remaining - // when emulation stopped (negative if extra cycles were emulated). Emulation - // stops when there are no more remaining cycles or an unhandled instruction - // is encountered (STOP, SLEEP, and any others not yet implemented). In the - // latter case, the return value is greater than zero. - spc_time_t run( spc_time_t count ); - - // Number of clock cycles remaining for current run() call - spc_time_t remain() const; - - // Access memory as the emulated CPU does - int read ( spc_addr_t ); - void write( spc_addr_t, int ); - -private: - // noncopyable - Spc_Cpu( const Spc_Cpu& ); - Spc_Cpu& operator = ( const Spc_Cpu& ); - unsigned mem_bit( spc_addr_t ); - - spc_time_t remain_; - Snes_Spc& emu; -}; - -inline spc_time_t Spc_Cpu::remain() const { return remain_; } - -#endif -
--- a/Plugins/Input/console/Spc_Dsp.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,666 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -// Based on Brad Martin's OpenSPC DSP emulator - -#include "Spc_Dsp.h" - -#include <string.h> - -#include "blargg_endian.h" - -/* Copyright (C) 2002 Brad Martin */ -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -Spc_Dsp::Spc_Dsp( uint8_t* ram_ ) : ram( ram_ ) -{ - set_gain( 1.0 ); - mute_voices( 0 ); - disable_surround( false ); - - BOOST_STATIC_ASSERT( sizeof (g) == register_count && sizeof (voice) == register_count ); -} - -void Spc_Dsp::mute_voices( int mask ) -{ - for ( int i = 0; i < voice_count; i++ ) - voice_state [i].enabled = (mask >> i & 1) ? 31 : 7; -} - -void Spc_Dsp::reset() -{ - keys = 0; - echo_ptr = 0; - noise_count = 0; - noise = 1; - fir_offset = 0; - - g.flags = 0xE0; // reset, mute, echo off - g.key_ons = 0; - - for ( int i = 0; i < voice_count; i++ ) - { - voice_t& v = voice_state [i]; - v.on_cnt = 0; - v.volume [0] = 0; - v.volume [1] = 0; - v.envstate = state_release; - } - - memset( fir_buf, 0, sizeof fir_buf ); -} - -void Spc_Dsp::write( int i, int data ) -{ - require( (unsigned) i < register_count ); - - reg [i] = data; - int high = i >> 4; - switch ( i & 0x0f ) - { - // voice volume - case 0: - case 1: { - short* volume = voice_state [high].volume; - int left = (int8_t) reg [i & ~1]; - int right = (int8_t) reg [i | 1]; - volume [0] = left; - volume [1] = right; - // kill surround only if signs of volumes differ - if ( left * right < surround_threshold ) - { - if ( left < 0 ) - volume [0] = -left; - else - volume [1] = -right; - } - break; - } - - // fir coefficients - case 0x0f: - fir_coeff [high] = (int8_t) data; // sign-extend - break; - } -} - -// This table is for envelope timing. It represents the number of counts -// that should be subtracted from the counter each sample period (32kHz). -// The counter starts at 30720 (0x7800). Each count divides exactly into -// 0x7800 without remainder. -const int env_rate_init = 0x7800; -static const short env_rates [0x20] = -{ - 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C, - 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180, - 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00, - 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800 -}; - -const int env_range = 0x800; - -inline int Spc_Dsp::clock_envelope( int v ) -{ /* Return value is current - * ENVX */ - raw_voice_t& raw_voice = this->voice [v]; - voice_t& voice = voice_state [v]; - - int envx = voice.envx; - if ( voice.envstate == state_release ) - { - /* - * Docs: "When in the state of "key off". the "click" sound is - * prevented by the addition of the fixed value 1/256" WTF??? - * Alright, I'm going to choose to interpret that this way: - * When a note is keyed off, start the RELEASE state, which - * subtracts 1/256th each sample period (32kHz). Note there's - * no need for a count because it always happens every update. - */ - envx -= env_range / 256; - if ( envx <= 0 ) - { - envx = 0; - keys &= ~(1 << v); - return -1; - } - voice.envx = envx; - raw_voice.envx = envx >> 8; - return envx; - } - - int cnt = voice.envcnt; - int adsr1 = raw_voice.adsr [0]; - if ( adsr1 & 0x80 ) - { - switch ( voice.envstate ) - { - case state_attack: { - // increase envelope by 1/64 each step - int t = adsr1 & 15; - if ( t == 15 ) - { - envx += env_range / 2; - } - else - { - cnt -= env_rates [t * 2 + 1]; - if ( cnt > 0 ) - break; - envx += env_range / 64; - cnt = env_rate_init; - } - if ( envx >= env_range ) - { - envx = env_range - 1; - voice.envstate = state_decay; - } - voice.envx = envx; - break; - } - - case state_decay: { - // Docs: "DR... [is multiplied] by the fixed value - // 1-1/256." Well, at least that makes some sense. - // Multiplying ENVX by 255/256 every time DECAY is - // updated. - cnt -= env_rates [((adsr1 >> 3) & 0xE) + 0x10]; - if ( cnt <= 0 ) - { - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - voice.envx = envx; - } - int sustain_level = raw_voice.adsr [1] >> 5; - - if ( envx <= (sustain_level + 1) * 0x100 ) - voice.envstate = state_sustain; - break; - } - - case state_sustain: - // Docs: "SR [is multiplied] by the fixed value 1-1/256." - // Multiplying ENVX by 255/256 every time SUSTAIN is - // updated. - cnt -= env_rates [raw_voice.adsr [1] & 0x1f]; - if ( cnt <= 0 ) - { - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - voice.envx = envx; - } - break; - - case state_release: - // handled above - break; - } - } - else - { /* GAIN mode is set */ - /* - * Note: if the game switches between ADSR and GAIN modes - * partway through, should the count be reset, or should it - * continue from where it was? Does the DSP actually watch for - * that bit to change, or does it just go along with whatever - * it sees when it performs the update? I'm going to assume - * the latter and not update the count, unless I see a game - * that obviously wants the other behavior. The effect would - * be pretty subtle, in any case. - */ - int t = raw_voice.gain; - if (t < 0x80) - { - envx = voice.envx = t << 4; - } - else switch (t >> 5) - { - case 4: /* Docs: "Decrease (linear): Subtraction - * of the fixed value 1/64." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx -= env_range / 64; - if ( envx < 0 ) - { - envx = 0; - if ( voice.envstate == state_attack ) - voice.envstate = state_decay; - } - voice.envx = envx; - break; - case 5: /* Docs: "Drecrease <sic> (exponential): - * Multiplication by the fixed value - * 1-1/256." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - if ( envx < 0 ) - { - envx = 0; - if ( voice.envstate == state_attack ) - voice.envstate = state_decay; - } - voice.envx = envx; - break; - case 6: /* Docs: "Increase (linear): Addition of - * the fixed value 1/64." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx += env_range / 64; - if ( envx >= env_range ) - envx = env_range - 1; - voice.envx = envx; - break; - case 7: /* Docs: "Increase (bent line): Addition - * of the constant 1/64 up to .75 of the - * constaint <sic> 1/256 from .75 to 1." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - if ( envx < env_range * 3 / 4 ) - envx += env_range / 64; - else - envx += env_range / 256; - if ( envx >= env_range ) - envx = env_range - 1; - voice.envx = envx; - break; - } - } - voice.envcnt = cnt; - raw_voice.envx = envx >> 4; - return envx; -} - -// Clamp n into range -32768 <= n <= 32767 -inline int clamp_16( int n ) -{ - if ( (BOOST::int16_t) n != n ) - n = BOOST::int16_t (0x7FFF - (n >> 31)); - return n; -} - -void Spc_Dsp::run( long count, short* out_buf ) -{ - // to do: make clock_envelope() inline so that this becomes a leaf function? - - // Should we just fill the buffer with silence? Flags won't be cleared - // during this run so it seems it should keep resetting every sample. - if ( g.flags & 0x80 ) - reset(); - - struct src_dir { - char start [2]; - char loop [2]; - }; - - const src_dir* const sd = (src_dir*) &ram [g.wave_page * 0x100]; - - int left_volume = g.left_volume; - int right_volume = g.right_volume; - if ( left_volume * right_volume < surround_threshold ) - right_volume = -right_volume; // kill global surround - left_volume *= emu_gain; - right_volume *= emu_gain; - - while ( --count >= 0 ) - { - // Here we check for keys on/off. Docs say that successive writes - // to KON/KOF must be separated by at least 2 Ts periods or risk - // being neglected. Therefore DSP only looks at these during an - // update, and not at the time of the write. Only need to do this - // once however, since the regs haven't changed over the whole - // period we need to catch up with. - - g.wave_ended &= ~g.key_ons; // Keying on a voice resets that bit in ENDX. - - if ( g.noise_enables ) - { - noise_count -= env_rates [g.flags & 0x1F]; - if ( noise_count <= 0 ) - { - noise_count = env_rate_init; - - noise_amp = BOOST::int16_t (noise * 2); - - int feedback = (noise << 13) ^ (noise << 14); - noise = (feedback & 0x4000) | (noise >> 1); - } - } - - // What is the expected behavior when pitch modulation is enabled on - // voice 0? Jurassic Park 2 does this. Assume 0 for now. - long prev_outx = 0; - - int echol = 0; - int echor = 0; - int left = 0; - int right = 0; - for ( int vidx = 0; vidx < voice_count; vidx++ ) - { - const int vbit = 1 << vidx; - raw_voice_t& raw_voice = voice [vidx]; - voice_t& voice = voice_state [vidx]; - - if ( voice.on_cnt && !--voice.on_cnt ) - { - // key on - keys |= vbit; - voice.addr = GET_LE16( sd [raw_voice.waveform].start ); - voice.block_remain = 1; - voice.envx = 0; - voice.block_header = 0; - voice.fraction = 0x3fff; // decode three samples immediately - voice.interp0 = 0; // BRR decoder filter uses previous two samples - voice.interp1 = 0; - - // NOTE: Real SNES does *not* appear to initialize the - // envelope counter to anything in particular. The first - // cycle always seems to come at a random time sooner than - // expected; as yet, I have been unable to find any - // pattern. I doubt it will matter though, so we'll go - // ahead and do the full time for now. - voice.envcnt = env_rate_init; - voice.envstate = state_attack; - } - - if ( g.key_ons & vbit & ~g.key_offs ) - { - // voice doesn't come on if key off is set - g.key_ons &= ~vbit; - voice.on_cnt = 8; - } - - if ( keys & g.key_offs & vbit ) - { - // key off - voice.envstate = state_release; - voice.on_cnt = 0; - } - - int envx; - if ( !(keys & vbit) || (envx = clock_envelope( vidx )) < 0 ) - { - raw_voice.envx = 0; - raw_voice.outx = 0; - prev_outx = 0; - continue; - } - - // Decode samples when fraction >= 1.0 (0x1000) - for ( int n = voice.fraction >> 12; --n >= 0; ) - { - if ( !--voice.block_remain ) - { - if ( voice.block_header & 1 ) - { - g.wave_ended |= vbit; - - if ( voice.block_header & 2 ) - { - // verified (played endless looping sample and ENDX was set) - voice.addr = GET_LE16( sd [raw_voice.waveform].loop ); - } - else - { - // first block was end block; don't play anything (verified) - goto sample_ended; // to do: find alternative to goto - } - } - - voice.block_header = ram [voice.addr++]; - voice.block_remain = 16; // nybbles - } - - // if next block has end flag set, *this* block ends *early* (verified) - if ( voice.block_remain == 9 && (ram [voice.addr + 5] & 3) == 1 && - (voice.block_header & 3) != 3 ) - { - sample_ended: - g.wave_ended |= vbit; - keys &= ~vbit; - raw_voice.envx = 0; - voice.envx = 0; - // add silence samples to interpolation buffer - do - { - voice.interp3 = voice.interp2; - voice.interp2 = voice.interp1; - voice.interp1 = voice.interp0; - voice.interp0 = 0; - } - while ( --n >= 0 ); - break; - } - - int delta = ram [voice.addr]; - if ( voice.block_remain & 1 ) - { - delta <<= 4; // use lower nybble - voice.addr++; - } - - // Use sign-extended upper nybble - delta = int8_t (delta) >> 4; - - // For invalid ranges (D,E,F): if the nybble is negative, - // the result is F000. If positive, 0000. Nothing else - // like previous range, etc seems to have any effect. If - // range is valid, do the shift normally. Note these are - // both shifted right once to do the filters properly, but - // the output will be shifted back again at the end. - int shift = voice.block_header >> 4; - delta = (delta << shift) >> 1; - if ( shift > 0x0C ) - delta = (delta >> 14) & ~0x7FF; - - // One, two and three point IIR filters - int smp1 = voice.interp0; - int smp2 = voice.interp1; - switch ( (voice.block_header >> 2) & 3 ) - { - case 0: - break; - - case 1: - delta += smp1 >> 1; - delta += (-smp1) >> 5; - break; - - case 2: - delta += smp1; - delta += (-(smp1 + (smp1 >> 1))) >> 5; - delta -= smp2 >> 1; - delta += smp2 >> 5; - break; - - case 3: - delta += smp1; - delta += (-(smp1 + (smp1 << 2) + (smp1 << 3))) >> 7; - delta -= smp2 >> 1; - delta += (smp2 + (smp2 >> 1)) >> 4; - break; - } - - voice.interp3 = voice.interp2; - voice.interp2 = smp2; - voice.interp1 = smp1; - voice.interp0 = BOOST::int16_t (clamp_16( delta ) * 2); // sign-extend - } - - // rate (with possible modulation) - int rate = GET_LE16( raw_voice.rate ) & 0x3FFF; - if ( g.pitch_mods & vbit ) - rate = (rate * (prev_outx + 32768)) >> 15; - - // Gaussian interpolation using most recent 4 samples - int index = voice.fraction >> 2 & 0x3FC; - voice.fraction = (voice.fraction & 0x0FFF) + rate; - const BOOST::int16_t* table = (BOOST::int16_t*) ((char*) gauss + index); - const BOOST::int16_t* table2 = (BOOST::int16_t*) ((char*) gauss + (255*4 - index)); - int s = ((table [0] * voice.interp3) >> 12) + - ((table [1] * voice.interp2) >> 12); - s += ((table2 [1] * voice.interp1) >> 12) + - // to do: should clamp here - ((table2 [0] * voice.interp0) >> 12); - int output = noise_amp; // noise is rarely used - if ( !(g.noise_enables & vbit) ) - output = clamp_16( s * 2 ); - - // scale output and set outx values - output = (output * envx) >> 11 & ~1; - - // output and apply muting (by setting voice.enabled to 31) - // if voice is externally disabled (not a SNES feature) - int l = (voice.volume [0] * output) >> voice.enabled; - int r = (voice.volume [1] * output) >> voice.enabled; - prev_outx = output; - raw_voice.outx = output >> 8; - if ( g.echo_ons & vbit ) - { - echol += l; - echor += r; - } - left += l; - right += r; - } - // end of channel loop - - // main volume control - left = (left * left_volume ) >> (7 + emu_gain_bits); - right = (right * right_volume) >> (7 + emu_gain_bits); - - // Echo FIR filter - - // read feedback from echo buffer - int echo_ptr = this->echo_ptr; - uint8_t* echo_buf = &ram [(g.echo_page * 0x100 + echo_ptr) & 0xFFFF]; - echo_ptr += 4; - if ( echo_ptr >= (g.echo_delay & 15) * 0x800 ) - echo_ptr = 0; - int fb_left = (BOOST::int16_t) GET_LE16( echo_buf ); // sign-extend - int fb_right = (BOOST::int16_t) GET_LE16( echo_buf + 2 ); // sign-extend - this->echo_ptr = echo_ptr; - - // put samples in history ring buffer - const int fir_offset = this->fir_offset; - short (*fir_pos) [2] = &fir_buf [fir_offset]; - this->fir_offset = (fir_offset + 7) & 7; // move backwards one step - fir_pos [0] [0] = fb_left; - fir_pos [0] [1] = fb_right; - fir_pos [8] [0] = fb_left; // duplicate at +8 eliminates wrap checking below - fir_pos [8] [1] = fb_right; - - // FIR - fb_left = fb_left * fir_coeff [7] + - fir_pos [1] [0] * fir_coeff [6] + - fir_pos [2] [0] * fir_coeff [5] + - fir_pos [3] [0] * fir_coeff [4] + - fir_pos [4] [0] * fir_coeff [3] + - fir_pos [5] [0] * fir_coeff [2] + - fir_pos [6] [0] * fir_coeff [1] + - fir_pos [7] [0] * fir_coeff [0]; - - fb_right = fb_right * fir_coeff [7] + - fir_pos [1] [1] * fir_coeff [6] + - fir_pos [2] [1] * fir_coeff [5] + - fir_pos [3] [1] * fir_coeff [4] + - fir_pos [4] [1] * fir_coeff [3] + - fir_pos [5] [1] * fir_coeff [2] + - fir_pos [6] [1] * fir_coeff [1] + - fir_pos [7] [1] * fir_coeff [0]; - - left += (fb_left * g.left_echo_volume ) >> 14; - right += (fb_right * g.right_echo_volume) >> 14; - - // echo buffer feedback - if ( !(g.flags & 0x20) ) - { - echol += (fb_left * g.echo_feedback) >> 14; - echor += (fb_right * g.echo_feedback) >> 14; - SET_LE16( echo_buf , clamp_16( echol ) ); - SET_LE16( echo_buf + 2, clamp_16( echor ) ); - } - - if ( out_buf ) - { - // write final samples - - left = clamp_16( left ); - right = clamp_16( right ); - - int mute = g.flags & 0x40; - - out_buf [0] = left; - out_buf [1] = right; - out_buf += 2; - - // muting - if ( mute ) - { - out_buf [-2] = 0; - out_buf [-1] = 0; - } - } - } -} - -// Base normal_gauss table is almost exactly (with an error of 0 or -1 for each entry): -// int normal_gauss [512]; -// normal_gauss [i] = exp((i-511)*(i-511)*-9.975e-6)*pow(sin(0.00307096*i),1.7358)*1304.45 - -// Interleved gauss table (to improve cache coherency). -// gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i] -const BOOST::int16_t Spc_Dsp::gauss [512] = -{ - 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303, - 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299, - 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292, - 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282, - 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269, - 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253, - 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234, - 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213, - 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190, - 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164, - 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136, - 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106, - 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074, - 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040, - 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005, - 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969, - 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932, - 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894, - 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855, - 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816, - 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777, - 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737, - 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698, - 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659, - 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620, - 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582, - 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545, - 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508, - 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473, - 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439, - 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405, - 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374, -}; -
--- a/Plugins/Input/console/Spc_Dsp.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +0,0 @@ - -// Super Nintendo (SNES) SPC DSP emulator - -// Game_Music_Emu 0.3.0 - -#ifndef SPC_DSP_H -#define SPC_DSP_H - -#include "blargg_common.h" - -class Spc_Dsp { - typedef BOOST::int8_t int8_t; - typedef BOOST::uint8_t uint8_t; -public: - - // Keeps pointer to 64K ram - Spc_Dsp( uint8_t* ram ); - - // Mute voice n if bit n (1 << n) of mask is clear. - enum { voice_count = 8 }; - void mute_voices( int mask ); - - // Clear state and silence everything. - void reset(); - - // Set gain, where 1.0 is normal. When greater than 1.0, output is clamped to - // the 16-bit sample range. - void set_gain( double ); - - // If true, prevent channels and global volumes from being phase-negated - void disable_surround( bool disable ); - - // Read/write register 'n', where n ranges from 0 to register_count - 1. - enum { register_count = 128 }; - int read ( int n ); - void write( int n, int ); - - // Run DSP for 'count' samples. Write resulting samples to 'buf' if not NULL. - void run( long count, short* buf = NULL ); - - -// End of public interface -private: - - struct raw_voice_t { - int8_t left_vol; - int8_t right_vol; - uint8_t rate [2]; - uint8_t waveform; - uint8_t adsr [2]; // envelope rates for attack, decay, and sustain - uint8_t gain; // envelope gain (if not using ADSR) - int8_t envx; // current envelope level - int8_t outx; // current sample - int8_t unused [6]; - }; - - union { - raw_voice_t voice [voice_count]; - - uint8_t reg [register_count]; - - struct { - int8_t unused1 [12]; - int8_t left_volume; // 0C Main Volume Left (-.7) - int8_t echo_feedback; // 0D Echo Feedback (-.7) - int8_t unused2 [14]; - int8_t right_volume; // 1C Main Volume Right (-.7) - int8_t unused3 [15]; - int8_t left_echo_volume; // 2C Echo Volume Left (-.7) - uint8_t pitch_mods; // 2D Pitch Modulation on/off for each voice - int8_t unused4 [14]; - int8_t right_echo_volume; // 3C Echo Volume Right (-.7) - uint8_t noise_enables; // 3D Noise output on/off for each voice - int8_t unused5 [14]; - uint8_t key_ons; // 4C Key On for each voice - uint8_t echo_ons; // 4D Echo on/off for each voice - int8_t unused6 [14]; - uint8_t key_offs; // 5C key off for each voice (instantiates release mode) - uint8_t wave_page; // 5D source directory (wave table offsets) - int8_t unused7 [14]; - uint8_t flags; // 6C flags and noise freq - uint8_t echo_page; // 6D - int8_t unused8 [14]; - uint8_t wave_ended; // 7C - uint8_t echo_delay; // 7D ms >> 4 - char unused9 [2]; - } g; - }; - - uint8_t* const ram; - - // Cache of echo FIR values for faster access - short fir_coeff [voice_count]; - - // fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code - short fir_buf [16] [2]; - int fir_offset; // (0 to 7) - - enum { emu_gain_bits = 8 }; - int emu_gain; - - int keyed_on; // 8-bits for 8 voices - int keys; - - int echo_ptr; - int noise_amp; - int noise; - int noise_count; - - int surround_threshold; - - static const BOOST::int16_t gauss []; - - enum state_t { - state_attack, - state_decay, - state_sustain, - state_release - }; - - struct voice_t { - short volume [2]; - short fraction;// 12-bit fractional position - short interp3; // most recent four decoded samples - short interp2; - short interp1; - short interp0; - short block_remain; // number of nybbles remaining in current block - unsigned short addr; - short block_header; // header byte from current block - short envcnt; - short envx; - short on_cnt; - short enabled; // 7 if enabled, 31 if disabled - short envstate; - short unused; // pad to power of 2 - }; - - voice_t voice_state [voice_count]; - - int clock_envelope( int ); -}; - -inline void Spc_Dsp::disable_surround( bool disable ) { surround_threshold = disable ? 0 : -0x7FFF; } - -inline void Spc_Dsp::set_gain( double v ) { emu_gain = (int) (v * (1 << emu_gain_bits)); } - -inline int Spc_Dsp::read( int i ) -{ - assert( (unsigned) i < register_count ); - return reg [i]; -} - -#endif -
--- a/Plugins/Input/console/Spc_Emu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,133 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Spc_Emu.h" - -#include <stdlib.h> -#include <string.h> -#include "blargg_endian.h" - -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -Spc_Emu::Spc_Emu( double gain ) -{ - apu.set_gain( gain ); -} - -Spc_Emu::~Spc_Emu() -{ -} - -const char** Spc_Emu::voice_names() const -{ - static const char* names [] = { - "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8" - }; - return names; -} - -void Spc_Emu::mute_voices( int m ) -{ - Music_Emu::mute_voices( m ); - apu.mute_voices( m ); -} - -blargg_err_t Spc_Emu::set_sample_rate( long sample_rate ) -{ - if ( sample_rate != native_sample_rate ) - { - BLARGG_RETURN_ERR( resampler.buffer_size( native_sample_rate / 20 * 2 ) ); - resampler.time_ratio( (double) native_sample_rate / sample_rate, 0.9965 ); - } - return Music_Emu::set_sample_rate( sample_rate ); -} - -blargg_err_t Spc_Emu::load( Data_Reader& in ) -{ - header_t h; - BLARGG_RETURN_ERR( in.read( &h, sizeof h ) ); - return load( h, in ); -} - -blargg_err_t Spc_Emu::load( const header_t& h, Data_Reader& in ) -{ - if ( in.remain() < Snes_Spc::spc_file_size - (int) sizeof h ) - return "Not an SPC file"; - - if ( strncmp( h.tag, "SNES-SPC700 Sound File Data", 27 ) != 0 ) - return "Not an SPC file"; - - long remain = in.remain(); - long size = remain + sizeof h; - if ( size < trailer_offset ) - size = trailer_offset; - BLARGG_RETURN_ERR( spc_data.resize( size ) ); - - set_track_count( 1 ); - set_voice_count( Snes_Spc::voice_count ); - - memcpy( spc_data.begin(), &h, sizeof h ); - return in.read( &spc_data [sizeof h], remain ); -} - -void Spc_Emu::start_track( int track ) -{ - Music_Emu::start_track( track ); - - resampler.clear(); - if ( apu.load_spc( spc_data.begin(), spc_data.size() ) ) - check( false ); -} - -void Spc_Emu::skip( long count ) -{ - count = long (count * resampler.ratio()) & ~1; - - count -= resampler.skip_input( count ); - if ( count > 0 ) - apu.skip( count ); - - // eliminate pop due to resampler - const int resampler_latency = 64; - sample_t buf [resampler_latency]; - play( resampler_latency, buf ); -} - -void Spc_Emu::play( long count, sample_t* out ) -{ - require( track_count() ); // file must be loaded - - if ( sample_rate() == native_sample_rate ) - { - if ( apu.play( count, out ) ) - log_error(); - return; - } - - long remain = count; - while ( remain > 0 ) - { - remain -= resampler.read( &out [count - remain], remain ); - if ( remain > 0 ) - { - long n = resampler.max_write(); - if ( apu.play( n, resampler.buffer() ) ) - log_error(); - resampler.write( n ); - } - } - - assert( remain == 0 ); -} -
--- a/Plugins/Input/console/Spc_Emu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ - -// Super Nintendo (SNES) SPC music file emulator - -// Game_Music_Emu 0.3.0 - -#ifndef SPC_EMU_H -#define SPC_EMU_H - -#include "Fir_Resampler.h" -#include "Music_Emu.h" -#include "Snes_Spc.h" - -class Spc_Emu : public Music_Emu { - enum { trailer_offset = 0x10200 }; -public: - // A gain of 1.0 results in almost no clamping. Default gain roughly - // matches volume of other emulators. - Spc_Emu( double gain = 1.4 ); - - // The Super Nintendo hardware samples at 32kHz. Other sample rates are - // handled by resampling the 32kHz output; emulation accuracy is not affected. - enum { native_sample_rate = 32000 }; - - // SPC file header - struct header_t - { - char tag [35]; - byte format; - byte version; - byte pc [2]; - byte a, x, y, psw, sp; - byte unused [2]; - char song [32]; - char game [32]; - char dumper [16]; - char comment [32]; - byte date [11]; - char len_secs [3]; - byte fade_msec [5]; - char author [32]; - byte mute_mask; - byte emulator; - byte unused2 [45]; - - enum { track_count = 1 }; - enum { copyright = 0 }; // no copyright field - }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 0x100 ); - - // Load SPC data - blargg_err_t load( Data_Reader& ); - - // Load SPC using already-loaded header and remaining data - blargg_err_t load( header_t const&, Data_Reader& ); - - // Header for currently loaded SPC - header_t const& header() const { return *(header_t*) spc_data.begin(); } - - // Pointer and size for trailer data - byte const* trailer() const { return &spc_data [trailer_offset]; } - long trailer_size() const { return spc_data.size() - trailer_offset; } - - // If true, prevents channels and global volumes from being phase-negated - void disable_surround( bool disable = true ); - -public: - ~Spc_Emu(); - blargg_err_t set_sample_rate( long ); - void mute_voices( int ); - void start_track( int ); - void play( long, sample_t* ); - void skip( long ); - const char** voice_names() const; -public: - // deprecated - blargg_err_t init( long r, double gain = 1.4 ) { return set_sample_rate( r ); } -private: - blargg_vector<byte> spc_data; - Fir_Resampler<24> resampler; - Snes_Spc apu; -}; - -inline void Spc_Emu::disable_surround( bool b ) { apu.disable_surround( b ); } - -#endif -
--- a/Plugins/Input/console/Vgm_Emu.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,298 +0,0 @@ - -// Game_Music_Emu 0.3.0. http://www.slack.net/~ant/ - -#include "Vgm_Emu.h" - -#include <math.h> -#include <string.h> -#include "blargg_endian.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -double const gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow -double const rolloff = 0.990; -double const oversample_factor = 1.5; - -Vgm_Emu::Vgm_Emu( bool os, double tempo ) -{ - oversample = os; - pos = NULL; - data = NULL; - uses_fm = false; - vgm_rate = (long) (header_t::time_rate * tempo + 0.5); - - static equalizer_t const eq = { -14.0, 80 }; - set_equalizer( eq ); - psg.volume( 1.0 ); -} - -Vgm_Emu::~Vgm_Emu() -{ - unload(); -} - -void Vgm_Emu::unload() -{ - data = NULL; - pos = NULL; - set_track_ended( false ); - mem.clear(); -} - -blargg_err_t Vgm_Emu::set_sample_rate( long sample_rate ) -{ - BLARGG_RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) ); - return Classic_Emu::set_sample_rate( sample_rate ); -} - -BOOST::uint8_t const* Vgm_Emu::gd3_data( int* size ) const -{ - if ( size ) - *size = 0; - - long gd3_offset = get_le32( header_.gd3_offset ); - if ( !gd3_offset ) - return NULL; - - gd3_offset -= 0x40 - offsetof (header_t,gd3_offset); - if ( gd3_offset < 0 ) - return NULL; - - byte const* gd3 = data + gd3_offset; - if ( data_end - gd3 < 16 || 0 != memcmp( gd3, "Gd3 ", 4 ) || get_le32( gd3 + 4 ) >= 0x200 ) - return NULL; - - long gd3_size = get_le32( gd3 + 8 ); - if ( data_end - gd3 < gd3_size - 12 ) - return NULL; - - if ( size ) - *size = data_end - gd3; - return gd3; -} - -void Vgm_Emu::update_eq( blip_eq_t const& eq ) -{ - psg.treble_eq( eq ); - dac_synth.treble_eq( eq ); -} - -void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - if ( i < psg.osc_count ) - psg.osc_output( i, c, l, r ); -} - -const char** Vgm_Emu::voice_names() const -{ - static const char* fm_names [] = { - "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" - }; - if ( uses_fm ) - return fm_names; - - static const char* psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" }; - return psg_names; -} - -void Vgm_Emu::mute_voices( int mask ) -{ - Classic_Emu::mute_voices( mask ); - dac_synth.output( &blip_buf ); - if ( uses_fm ) - { - psg.output( (mask & 0x80) ? 0 : &blip_buf ); - if ( ym2612.enabled() ) - { - dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * gain ); - ym2612.mute_voices( mask ); - } - - if ( ym2413.enabled() ) - { - int m = mask & 0x3f; - if ( mask & 0x20 ) - m |= 0x01e0; // channels 5-8 - if ( mask & 0x40 ) - m |= 0x3e00; - ym2413.mute_voices( m ); - } - } -} - -blargg_err_t Vgm_Emu::load_( const header_t& h, void const* new_data, long new_size ) -{ - header_ = h; - - // compatibility - if ( 0 != memcmp( header_.tag, "Vgm ", 4 ) ) - return "Not a VGM file"; - check( get_le32( header_.version ) <= 0x150 ); - - // psg rate - long psg_rate = get_le32( header_.psg_rate ); - if ( !psg_rate ) - psg_rate = 3579545; - blip_time_factor = (long) floor( (double) (1L << blip_time_bits) / vgm_rate * psg_rate + 0.5 ); - blip_buf.clock_rate( psg_rate ); - - data = (byte*) new_data; - data_end = data + new_size; - - // get loop - loop_begin = data_end; - if ( get_le32( header_.loop_offset ) ) - loop_begin = &data [get_le32( header_.loop_offset ) + offsetof (header_t,loop_offset) - 0x40]; - - set_voice_count( psg.osc_count ); - set_track_count( 1 ); - - BLARGG_RETURN_ERR( setup_fm() ); - - // do after FM in case output buffer is changed - BLARGG_RETURN_ERR( Classic_Emu::setup_buffer( psg_rate ) ); - - return blargg_success; -} - -blargg_err_t Vgm_Emu::setup_fm() -{ - long ym2612_rate = get_le32( header_.ym2612_rate ); - long ym2413_rate = get_le32( header_.ym2413_rate ); - if ( ym2413_rate && get_le32( header_.version ) < 0x110 ) - update_fm_rates( &ym2413_rate, &ym2612_rate ); - - uses_fm = false; - - double fm_rate = blip_buf.sample_rate() * oversample_factor; - - if ( ym2612_rate ) - { - uses_fm = true; - if ( !oversample ) - fm_rate = ym2612_rate / 144.0; - Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, gain ); - BLARGG_RETURN_ERR( ym2612.set_rate( fm_rate, ym2612_rate ) ); - ym2612.enable( true ); - set_voice_count( 8 ); - } - - if ( !uses_fm && ym2413_rate ) - { - uses_fm = true; - if ( !oversample ) - fm_rate = ym2413_rate / 72.0; - Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, gain ); - int result = ym2413.set_rate( fm_rate, ym2413_rate ); - if ( result == 2 ) - return "YM2413 FM sound isn't supported"; - BLARGG_CHECK_ALLOC( !result ); - ym2413.enable( true ); - set_voice_count( 8 ); - } - - if ( uses_fm ) - { - //dprintf( "fm_rate: %f\n", fm_rate ); - fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 ); - BLARGG_RETURN_ERR( Dual_Resampler::resize( blip_buf.length() * blip_buf.sample_rate() / 1000 ) ); - psg.volume( 0.135 * gain ); - } - else - { - ym2612.enable( false ); - ym2413.enable( false ); - psg.volume( 1.0 ); - } - - return blargg_success; -} - -blargg_err_t Vgm_Emu::load( Data_Reader& reader ) -{ - header_t h; - BLARGG_RETURN_ERR( reader.read( &h, sizeof h ) ); - return load( h, reader ); -} - -blargg_err_t Vgm_Emu::load( const header_t& h, Data_Reader& reader ) -{ - unload(); - - // allocate and read data - long data_size = reader.remain(); - int const padding = 8; - BLARGG_RETURN_ERR( mem.resize( data_size + padding ) ); - blargg_err_t err = reader.read( mem.begin(), data_size ); - if ( err ) { - unload(); - return err; - } - memset( &mem [data_size], 0x66, padding ); // pad with end command - - return load_( h, mem.begin(), data_size ); -} - -void Vgm_Emu::start_track( int track ) -{ - require( data ); // file must have been loaded - - Classic_Emu::start_track( track ); - psg.reset(); - - dac_disabled = -1; - pcm_data = data; - pcm_pos = data; - dac_amp = -1; - vgm_time = 0; - pos = data; - if ( get_le32( header_.version ) >= 0x150 ) - { - long data_offset = get_le32( header_.data_offset ); - check( data_offset ); - if ( data_offset ) - pos += data_offset + offsetof (header_t,data_offset) - 0x40; - } - - if ( uses_fm ) - { - if ( ym2413.enabled() ) - ym2413.reset(); - - if ( ym2612.enabled() ) - ym2612.reset(); - - fm_time_offset = 0; - blip_buf.clear(); - Dual_Resampler::clear(); - } -} - -long Vgm_Emu::run( int msec, bool* added_stereo ) -{ - blip_time_t psg_end = run_commands( msec * vgm_rate / 1000 ); - *added_stereo = psg.end_frame( psg_end ); - return psg_end; -} - -void Vgm_Emu::play( long count, sample_t* out ) -{ - require( pos ); // track must have been started - - if ( uses_fm ) - Dual_Resampler::play( count, out, blip_buf ); - else - Classic_Emu::play( count, out ); -} -
--- a/Plugins/Input/console/Vgm_Emu.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,118 +0,0 @@ - -// Multi-format VGM music emulator with support for SMS PSG and Mega Drive FM - -// Game_Music_Emu 0.3.0 - -#ifndef VGM_EMU_H -#define VGM_EMU_H - -#include "abstract_file.h" -#include "Vgm_Emu_Impl.h" - -// Emulates VGM music using SN76489/SN76496 PSG, YM2612, and YM2413 FM sound chips. -// Supports custom sound buffer and frequency equalization when VGM uses just the PSG. -// FM sound chips can be run at their proper rates, or slightly higher to reduce -// aliasing on high notes. Currently YM2413 support requires that you supply a -// YM2413 sound chip emulator. I can provide one I've modified to work with the library. -class Vgm_Emu : public Vgm_Emu_Impl { -public: - - // Oversample runs FM chips at higher than normal rate. Tempo adjusts speed of - // music, but not pitch. - Vgm_Emu( bool oversample = true, double tempo = 1.0 ); - - // VGM header format - struct header_t - { - char tag [4]; - byte data_size [4]; - byte version [4]; - byte psg_rate [4]; - byte ym2413_rate [4]; - byte gd3_offset [4]; - byte track_duration [4]; - byte loop_offset [4]; - byte loop_duration [4]; - byte frame_rate [4]; - byte noise_feedback [2]; - byte noise_width; - byte unused1; - byte ym2612_rate [4]; - byte ym2151_rate [4]; - byte data_offset [4]; - byte unused2 [8]; - - enum { track_count = 1 }; // one track per file - enum { time_rate = 44100 }; // all times specified at this rate - - // track information is in gd3 data - enum { game = 0 }; - enum { song = 0 }; - enum { author = 0 }; - enum { copyright = 0 }; - }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 64 ); - - // Load VGM data - blargg_err_t load( Data_Reader& ); - - // Load VGM using already-loaded header and remaining data - blargg_err_t load( header_t const&, Data_Reader& ); - - // Load VGM using pointer to file data. Keeps pointer to data. - blargg_err_t load( void const* data, long size ); - - // Header for currently loaded VGM - header_t const& header() const { return header_; } - - // Pointer to gd3 data, or NULL if none. Optionally returns size of data. - // Checks for GD3 header and that version is less than 2.0. - byte const* gd3_data( int* size_out = NULL ) const; - - // to do: find better name for this - // True if Classic_Emu operations are supported - bool is_classic_emu() const { return !uses_fm; } - -public: - ~Vgm_Emu(); - blargg_err_t set_sample_rate( long sample_rate ); - void start_track( int ); - void mute_voices( int mask ); - const char** voice_names() const; - void play( long count, sample_t* ); -public: - // deprecated - int track_length( const byte** end_out = NULL, int* remain_out = NULL ) const - { - return (header().track_duration [3]*0x1000000L + - header().track_duration [2]*0x0010000L + - header().track_duration [1]*0x0000100L + - header().track_duration [0]) / header_t::time_rate; - } -protected: - // Classic_Emu - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); - blip_time_t run( int, bool* ); -private: - header_t header_; - blargg_vector<byte> mem; - long vgm_rate; - bool oversample; - bool uses_fm; - - blargg_err_t init_( long sample_rate ); - blargg_err_t load_( const header_t&, void const* data, long size ); - blargg_err_t setup_fm(); - void unload(); -}; - -inline blargg_err_t Vgm_Emu::load( void const* data, long size ) -{ - unload(); - return load_( *(header_t*) data, (char*) data + sizeof (header_t), - size - sizeof (header_t) ); -} - -#endif -
--- a/Plugins/Input/console/abstract_file.cpp Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,282 +0,0 @@ - -#include "abstract_file.h" - -#include <assert.h> -#include <string.h> -#include <stddef.h> -#include <stdlib.h> - -/* Copyright (C) 2005 Shay Green. Permission is hereby granted, free of -charge, to any person obtaining a copy of this software module and associated -documentation files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and -to permit persons to whom the Software is furnished to do so, subject to the -following conditions: The above copyright notice and this permission notice -shall be included in all copies or substantial portions of the Software. THE -SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -// to do: remove? -#ifndef RAISE_ERROR - #define RAISE_ERROR( str ) return str -#endif - -typedef Data_Reader::error_t error_t; - -error_t Data_Writer::write( const void*, long ) { return NULL; } - -void Data_Writer::satisfy_lame_linker_() { } - -error_t Data_Reader::read( void* p, long s ) -{ - long result = read_avail( p, s ); - if ( result != s ) - { - if ( result >= 0 && result < s ) - RAISE_ERROR( "Unexpected end-of-file" ); - - RAISE_ERROR( "Read error" ); - } - - return NULL; -} - -error_t Data_Reader::skip( long count ) -{ - char buf [512]; - while ( count ) - { - int n = sizeof buf; - if ( n > count ) - n = count; - count -= n; - RAISE_ERROR( read( buf, n ) ); - } - return NULL; -} - -long File_Reader::remain() const -{ - return size() - tell(); -} - -error_t File_Reader::skip( long n ) -{ - assert( n >= 0 ); - if ( n ) - RAISE_ERROR( seek( tell() + n ) ); - - return NULL; -} - - -// Subset_Reader - -Subset_Reader::Subset_Reader( Data_Reader* in_, long size ) : - in( in_ ), - remain_( in_->remain() ) -{ - if ( remain_ > size ) - remain_ = size; -} - -long Subset_Reader::remain() const { - return remain_; -} - -long Subset_Reader::read_avail( void* p, long s ) -{ - if ( s > remain_ ) - s = remain_; - remain_ -= s; - return in->read_avail( p, s ); -} - -// Mem_File_Reader - -Mem_File_Reader::Mem_File_Reader( const void* p, long s ) : - begin( (const char*) p ), - pos( 0 ), - size_( s ) -{ -} - -long Mem_File_Reader::size() const { - return size_; -} - -long Mem_File_Reader::read_avail( void* p, long s ) -{ - long r = remain(); - if ( s > r ) - s = r; - memcpy( p, begin + pos, s ); - pos += s; - return s; -} - -long Mem_File_Reader::tell() const { - return pos; -} - -error_t Mem_File_Reader::seek( long n ) -{ - if ( n > size_ ) - RAISE_ERROR( "Tried to go past end of file" ); - pos = n; - return NULL; -} - -// Std_File_Reader - -Std_File_Reader::Std_File_Reader() : file_( NULL ) { -} - -Std_File_Reader::~Std_File_Reader() { - close(); -} - -error_t Std_File_Reader::open( const char* path ) -{ - file_ = fopen( path, "rb" ); - if ( !file_ ) - RAISE_ERROR( "Couldn't open file" ); - return NULL; -} - -long Std_File_Reader::size() const -{ - long pos = tell(); - fseek( file_, 0, SEEK_END ); - long result = tell(); - fseek( file_, pos, SEEK_SET ); - return result; -} - -long Std_File_Reader::read_avail( void* p, long s ) { - return (long) fread( p, 1, s, file_ ); -} - -long Std_File_Reader::tell() const { - return ftell( file_ ); -} - -error_t Std_File_Reader::seek( long n ) -{ - if ( fseek( file_, n, SEEK_SET ) != 0 ) - RAISE_ERROR( "Error seeking in file" ); - return NULL; -} - -void Std_File_Reader::close() -{ - if ( file_ ) { - fclose( file_ ); - file_ = NULL; - } -} - -// Std_File_Writer - -Std_File_Writer::Std_File_Writer() : file_( NULL ) { -} - -Std_File_Writer::~Std_File_Writer() { - close(); -} - -error_t Std_File_Writer::open( const char* path ) -{ - file_ = fopen( path, "wb" ); - if ( !file_ ) - RAISE_ERROR( "Couldn't open file for writing" ); - - // to do: increase file buffer size - //setvbuf( file_, NULL, _IOFBF, 32 * 1024L ); - - return NULL; -} - -error_t Std_File_Writer::write( const void* p, long s ) -{ - long result = (long) fwrite( p, 1, s, file_ ); - if ( result != s ) - RAISE_ERROR( "Couldn't write to file" ); - return NULL; -} - -void Std_File_Writer::close() -{ - if ( file_ ) { - fclose( file_ ); - file_ = NULL; - } -} - -// Mem_Writer - -Mem_Writer::Mem_Writer( void* p, long s, int b ) -{ - data_ = (char*) p; - size_ = 0; - allocated = s; - mode = b ? ignore_excess : fixed; -} - -Mem_Writer::Mem_Writer() -{ - data_ = NULL; - size_ = 0; - allocated = 0; - mode = expanding; -} - -Mem_Writer::~Mem_Writer() -{ - if ( mode == expanding ) - free( data_ ); -} - -error_t Mem_Writer::write( const void* p, long s ) -{ - long remain = allocated - size_; - if ( s > remain ) - { - if ( mode == fixed ) - RAISE_ERROR( "Tried to write more data than expected" ); - - if ( mode == ignore_excess ) - { - s = remain; - } - else // expanding - { - long new_allocated = size_ + s; - new_allocated += (new_allocated >> 1) + 2048; - void* p = realloc( data_, new_allocated ); - if ( !p ) - RAISE_ERROR( "Out of memory" ); - data_ = (char*) p; - allocated = new_allocated; - } - } - - assert( size_ + s <= allocated ); - memcpy( data_ + size_, p, s ); - size_ += s; - - return NULL; -} - -// Null_Writer - -error_t Null_Writer::write( const void*, long ) -{ - return NULL; -} -
--- a/Plugins/Input/console/abstract_file.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,177 +0,0 @@ - -// Abstract file access interfaces - -#ifndef ABSTRACT_FILE_H -#define ABSTRACT_FILE_H - -#include <stdio.h> - -// Supports reading and finding out how many bytes are remaining -class Data_Reader { -public: - Data_Reader() { } - virtual ~Data_Reader() { } - - // NULL on success, otherwise error string - typedef const char* error_t; - - // Read at most 'n' bytes. Return number of bytes read, zero or negative - // if error. - virtual long read_avail( void*, long n ) = 0; - - // Read exactly 'n' bytes (error if fewer are available). - virtual error_t read( void*, long ); - - // Number of bytes remaining - virtual long remain() const = 0; - - // Skip forwards by 'n' bytes. - virtual error_t skip( long n ); - - // to do: bytes remaining = LONG_MAX when unknown? - -private: - // noncopyable - Data_Reader( const Data_Reader& ); - Data_Reader& operator = ( const Data_Reader& ); -}; - -// Adds seeking operations -class File_Reader : public Data_Reader { -public: - // Size of file - virtual long size() const = 0; - - // Current position in file - virtual long tell() const = 0; - - // Change position in file - virtual error_t seek( long ) = 0; - - virtual long remain() const; - - error_t skip( long n ); -}; - -// Limit access to a subset of data -class Subset_Reader : public Data_Reader { - Data_Reader* in; - long remain_; -public: - Subset_Reader( Data_Reader*, long size ); - long remain() const; - long read_avail( void*, long ); -}; - -// Treat range of memory as a file -class Mem_File_Reader : public File_Reader { - const char* const begin; - long pos; - const long size_; -public: - Mem_File_Reader( const void*, long size ); - - long size() const; - long read_avail( void*, long ); - - long tell() const; - error_t seek( long ); -}; - -// File reader based on C FILE -class Std_File_Reader : public File_Reader { - FILE* file_; -protected: - void reset( FILE* f ) { file_ = f; } - //FILE* owned_file; -public: - Std_File_Reader(); - ~Std_File_Reader(); - - error_t open( const char* ); - - FILE* file() const { return file_; } - - // Forward read requests to file. Caller must close file later. - //void forward( FILE* ); - - long size() const; - long read_avail( void*, long ); - - long tell() const; - error_t seek( long ); - - void close(); -}; - -// Supports writing -class Data_Writer { -public: - Data_Writer() { } - virtual ~Data_Writer() { } - - typedef const char* error_t; - - // Write 'n' bytes. NULL on success, otherwise error string. - virtual error_t write( const void*, long n ) = 0; - - void satisfy_lame_linker_(); -private: - // noncopyable - Data_Writer( const Data_Writer& ); - Data_Writer& operator = ( const Data_Writer& ); -}; - -class Std_File_Writer : public Data_Writer { - FILE* file_; -protected: - void reset( FILE* f ) { file_ = f; } -public: - Std_File_Writer(); - ~Std_File_Writer(); - - error_t open( const char* ); - - FILE* file() const { return file_; } - - // Forward writes to file. Caller must close file later. - //void forward( FILE* ); - - error_t write( const void*, long ); - - void close(); -}; - -// Write data to memory -class Mem_Writer : public Data_Writer { - char* data_; - long size_; - long allocated; - enum { expanding, fixed, ignore_excess } mode; -public: - // Keep all written data in expanding block of memory - Mem_Writer(); - - // Write to fixed-size block of memory. If ignore_excess is false, returns - // error if more than 'size' data is written, otherwise ignores any excess. - Mem_Writer( void*, long size, int ignore_excess = 0 ); - - error_t write( const void*, long ); - - // Pointer to beginning of written data - char* data() { return data_; } - - // Number of bytes written - long size() const { return size_; } - - ~Mem_Writer(); -}; - -// Written data is ignored -class Null_Writer : public Data_Writer { -public: - error_t write( const void*, long ); -}; - -#endif -
--- a/Plugins/Input/console/blargg_common.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,241 +0,0 @@ - -// Sets up common environment for Shay Green's libraries. -// -// To change configuration options, modify blargg_config.h, not this file. - -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -// HAVE_CONFIG_H: If defined, include user's "config.h" first (which *can* -// re-include blargg_common.h if it needs to) -#ifdef HAVE_CONFIG_H - #undef BLARGG_COMMON_H - #include "config.h" - #define BLARGG_COMMON_H -#endif - -// BLARGG_NONPORTABLE: If defined to 1, platform-specific (and possibly non-portable) -// optimizations are used. Defaults to off. Report any problems that occur only when -// this is enabled. -#ifndef BLARGG_NONPORTABLE - #define BLARGG_NONPORTABLE 0 -#endif - -// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only -// one must be #defined to 1. Only needed if something actually depends on byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) - #if defined (MSB_FIRST) || defined (__powerc) || defined (macintosh) || defined (WORDS_BIGENDIAN) - #define BLARGG_BIG_ENDIAN 1 - #else - #define BLARGG_LITTLE_ENDIAN 1 - #endif -#endif - -// Determine compiler's language support - -// Metrowerks CodeWarrior -#if defined (__MWERKS__) - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #if !__option(bool) - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - #define STATIC_CAST(T,expr) static_cast< T > (expr) - -// Microsoft Visual C++ -#elif defined (_MSC_VER) - #if _MSC_VER < 1100 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - -// GNU C++ -#elif defined (__GNUC__) - #if __GNUC__ > 2 - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #endif - -// Mingw -#elif defined (__MINGW32__) - // empty - -// Pre-ISO C++ compiler -#elif __cplusplus < 199711 - #ifndef BLARGG_COMPILER_HAS_BOOL - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - -#endif - -/* BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compilers. - If errors occur here, add the following line to your config.h file: - #define BLARGG_COMPILER_HAS_BOOL 0 -*/ -#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL - typedef int bool; - const bool true = 1; - const bool false = 0; -#endif - -// BLARGG_USE_NAMESPACE: If 1, use <cxxx> headers rather than <xxxx.h> -#if BLARGG_USE_NAMESPACE || (!defined (BLARGG_USE_NAMESPACE) && BLARGG_COMPILER_HAS_NAMESPACE) - #include <cstddef> - #include <cstdlib> - #include <cassert> - #include <climits> - #define STD std -#else - #include <stddef.h> - #include <stdlib.h> - #include <assert.h> - #include <limits.h> - #define STD -#endif - -// BLARGG_NEW is used in place of 'new' to create objects. By default, plain new is used. -// To prevent an exception if out of memory, #define BLARGG_NEW new (std::nothrow) -#ifndef BLARGG_NEW - #define BLARGG_NEW new -#endif - -// BOOST::int8_t etc. - -// HAVE_STDINT_H: If defined, use <stdint.h> for int8_t etc. -#if defined (HAVE_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 - -// BLARGG_SOURCE_BEGIN: Library sources #include this after other #includes. -#ifndef BLARGG_SOURCE_BEGIN - #define BLARGG_SOURCE_BEGIN "blargg_source.h" -#endif - -// BLARGG_ENABLE_OPTIMIZER: Library sources #include this for speed-critical code -#ifndef BLARGG_ENABLE_OPTIMIZER - #define BLARGG_ENABLE_OPTIMIZER "blargg_common.h" -#endif - -// BLARGG_CPU_*: Used to select between some optimizations -#if !defined (BLARGG_CPU_POWERPC) && !defined (BLARGG_CPU_X86) - #if defined (__powerc) - #define BLARGG_CPU_POWERPC 1 - #elif defined (_MSC_VER) && defined (_M_IX86) - #define BLARGG_CPU_X86 1 - #endif -#endif - -// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. -#ifndef BOOST_STATIC_ASSERT - #ifdef _MSC_VER - // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified - #define BOOST_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / ((expr) ? 1 : 0) - 1] ) - #else - // Some other compilers fail when declaring same function multiple times in class, - // so differentiate them by line - #define BOOST_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / ((expr) ? 1 : 0) - 1] [__LINE__] ) - #endif -#endif - -// STATIC_CAST(T,expr): Used in place of static_cast<T> (expr) -#ifndef STATIC_CAST - #define STATIC_CAST(T,expr) ((T) (expr)) -#endif - -// blargg_err_t (NULL on success, otherwise error string) -#ifndef blargg_err_t - typedef const char* blargg_err_t; -#endif -const char* const blargg_success = 0; - -// blargg_vector: Simple array that does *not* work for types with a constructor (non-POD). -template<class T> -class blargg_vector { - T* begin_; - STD::size_t size_; -public: - blargg_vector() : begin_( 0 ), size_( 0 ) { } - ~blargg_vector() { STD::free( begin_ ); } - - typedef STD::size_t size_type; - - blargg_err_t resize( size_type n ) - { - void* p = STD::realloc( begin_, n * sizeof (T) ); - if ( !p && n ) - return "Out of memory"; - begin_ = (T*) p; - size_ = n; - return 0; - } - - void clear() - { - void* p = begin_; - begin_ = 0; - size_ = 0; - STD::free( p ); - } - - size_type size() const { return size_; } - - T* begin() { return begin_; } - T* end() { return begin_ + size_; } - - const T* begin() const { return begin_; } - const T* end() const { return begin_ + size_; } - - T& operator [] ( size_type n ) - { - assert( n <= size_ ); // allow for past-the-end value - return begin_ [n]; - } - - const T& operator [] ( size_type n ) const - { - assert( n <= size_ ); // allow for past-the-end value - return begin_ [n]; - } -}; - -#endif -
--- a/Plugins/Input/console/blargg_endian.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,156 +0,0 @@ - -// CPU Byte Order Utilities - -// Game_Music_Emu 0.3.0 - -#ifndef BLARGG_ENDIAN -#define BLARGG_ENDIAN - -#include "blargg_common.h" - -#if 0 - // Read 16/32-bit little-endian integer from memory - unsigned GET_LE16( void const* ); - unsigned long GET_LE32( void const* ); - - // Read 16/32-bit big-endian integer from memory - unsigned GET_BE16( void const* ); - unsigned long GET_BE32( void const* ); - - // Write 16/32-bit integer to memory in little-endian format - void SET_LE16( void*, unsigned ); - void SET_LE32( void*, unsigned ); - - // Write 16/32-bit integer to memory in big-endian format - void SET_BE16( void*, unsigned long ); - void SET_BE32( void*, unsigned long ); -#endif - -inline unsigned get_le16( void const* p ) -{ - return ((unsigned char*) p) [1] * 0x100 + - ((unsigned char*) p) [0]; -} - -inline unsigned get_be16( void const* p ) -{ - return ((unsigned char*) p) [0] * 0x100 + - ((unsigned char*) p) [1]; -} - -inline unsigned long get_le32( void const* p ) -{ - return ((unsigned char*) p) [3] * 0x01000000 + - ((unsigned char*) p) [2] * 0x00010000 + - ((unsigned char*) p) [1] * 0x00000100 + - ((unsigned char*) p) [0]; -} - -inline unsigned long get_be32( void const* p ) -{ - return ((unsigned char*) p) [0] * 0x01000000 + - ((unsigned char*) p) [1] * 0x00010000 + - ((unsigned char*) p) [2] * 0x00000100 + - ((unsigned char*) p) [3]; -} - -inline void set_le16( void* p, unsigned n ) -{ - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} - -inline void set_be16( void* p, unsigned n ) -{ - ((unsigned char*) p) [0] = (unsigned char) (n >> 8); - ((unsigned char*) p) [1] = (unsigned char) n; -} - -inline void set_le32( void* p, unsigned long n ) -{ - ((unsigned char*) p) [3] = (unsigned char) (n >> 24); - ((unsigned char*) p) [2] = (unsigned char) (n >> 16); - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} - -inline void set_be32( void* p, unsigned long n ) -{ - ((unsigned char*) p) [0] = (unsigned char) (n >> 24); - ((unsigned char*) p) [1] = (unsigned char) (n >> 16); - ((unsigned char*) p) [2] = (unsigned char) (n >> 8); - ((unsigned char*) p) [3] = (unsigned char) n; -} - -#ifndef GET_LE16 - // Optimized implementation if byte order is known - #if BLARGG_NONPORTABLE && BLARGG_LITTLE_ENDIAN - #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_LE16( addr, data ) (void (*(BOOST::uint16_t*) (addr) = (data))) - #define SET_LE32( addr, data ) (void (*(BOOST::uint32_t*) (addr) = (data))) - - #elif BLARGG_NONPORTABLE && BLARGG_CPU_POWERPC - // PowerPC has special byte-reversed instructions - // to do: assumes that PowerPC is running in big-endian mode - #define GET_LE16( addr ) (__lhbrx( (addr), 0 )) - #define GET_LE32( addr ) (__lwbrx( (addr), 0 )) - #define SET_LE16( addr, data ) (__sthbrx( (data), (addr), 0 )) - #define SET_LE32( addr, data ) (__stwbrx( (data), (addr), 0 )) - - #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_BE16( addr, data ) (void (*(BOOST::uint16_t*) (addr) = (data))) - #define SET_BE32( addr, data ) (void (*(BOOST::uint32_t*) (addr) = (data))) - - #endif -#endif - -#ifndef GET_LE16 - #define GET_LE16( addr ) get_le16( addr ) -#endif - -#ifndef GET_LE32 - #define GET_LE32( addr ) get_le32( addr ) -#endif - -#ifndef SET_LE16 - #define SET_LE16( addr, data ) set_le16( addr, data ) -#endif - -#ifndef SET_LE32 - #define SET_LE32( addr, data ) set_le32( addr, data ) -#endif - -#ifndef GET_BE16 - #define GET_BE16( addr ) get_be16( addr ) -#endif - -#ifndef GET_BE32 - #define GET_BE32( addr ) get_be32( addr ) -#endif - -#ifndef SET_BE16 - #define SET_BE16( addr, data ) set_be16( addr, data ) -#endif - -#ifndef SET_BE32 - #define SET_BE32( addr, data ) set_be32( addr, data ) -#endif - -// auto-selecting versions - -inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } -inline void set_le( BOOST::uint32_t* p, unsigned long n ) { SET_LE32( p, n ); } - -inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } -inline void set_be( BOOST::uint32_t* p, unsigned long n ) { SET_BE32( p, n ); } - -inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } -inline unsigned long get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } - -inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } -inline unsigned long get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } - -#endif -
--- a/Plugins/Input/console/blargg_source.h Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ - -// By default, #included at beginning of library source files. -// Can be overridden by #defining BLARGG_SOURCE_BEGIN to path of alternate file. - -// Copyright (C) 2005 Shay Green. - -#ifndef BLARGG_SOURCE_H -#define BLARGG_SOURCE_H - -// If debugging is enabled, abort program if expr is false. Meant for checking -// internal state and consistency. A failed assertion indicates a bug in the module. -// void assert( bool expr ); -#include <assert.h> - -// If debugging is enabled and expr is false, abort program. Meant for checking -// caller-supplied parameters and operations that are outside the control of the -// module. A failed requirement indicates a bug outside the module. -// void require( bool expr ); -#undef require -#define require( expr ) assert( expr ) - -// Like printf() except output goes to debug log file. Might be defined to do -// nothing (not even evaluate its arguments). -// void dprintf( const char* format, ... ); -#undef dprintf -#ifdef BLARGG_DPRINTF - #define dprintf BLARGG_DPRINTF -#else - inline void blargg_dprintf_( const char*, ... ) { } - #define dprintf (1) ? (void) 0 : blargg_dprintf_ -#endif - -// If enabled, evaluate expr and if false, make debug log entry with source file -// and line. Meant for finding situations that should be examined further, but that -// don't indicate a problem. In all cases, execution continues normally. -#undef check -#ifdef BLARGG_CHECK - #define check( expr ) BLARGG_CHECK( expr ) -#else - #define check( expr ) ((void) 0) -#endif - -// If expr returns non-NULL error string, return it from current function, otherwise continue. -#define BLARGG_RETURN_ERR( expr ) do { \ - blargg_err_t blargg_return_err_ = (expr); \ - if ( blargg_return_err_ ) return blargg_return_err_; \ - } while ( 0 ) - -// If ptr is NULL, return out of memory error string. -#define BLARGG_CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) - -// Avoid any macros which evaluate their arguments multiple times -#undef min -#undef max - -// using const references generates crappy code, and I am currenly only using these -// for built-in types, so they take arguments by value - -template<class T> -inline T min( T x, T y ) -{ - if ( x < y ) - return x; - return y; -} - -template<class T> -inline T max( T x, T y ) -{ - if ( x < y ) - return y; - return x; -} - -#endif -
--- a/Plugins/Input/console/gme_notes.txt Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,181 +0,0 @@ -Game_Music_Emu 0.3.0 Notes --------------------------- -Author : Shay Green <hotpop.com@blargg> -Website: http://www.slack.net/~ant/ -Forum : http://groups.google.com/group/blargg-sound-libs - - -Overview --------- -This library is composed of several independent game music emulators -derived from the common Music_Emu interface. Each emulator can load a -game music file and play from any track. To play a game music file, do -the following: - -- Determine file's type -- Create appropriate emulator -- Set sample rate -- Load file into emulator -- Start desired track -- When samples are needed, call play() -- When done, delete emulator - -See Music_Emu.h for reference. - - -Information Fields ------------------- -Game music files include text fields with information about the game and -track. These are stored in the file's header or in an embedded block. -Text fields in most game music formats do *not* have a nul terminator if -the string completely fills the field. The simplest way to handle this -is to copy the string out and manually add a nul terminator at the end. - -This library is currently focused only on actual emulation, so it -doesn't provide a common interface to the different schemes each game -music file format uses. Refer to the file's official specification for -further information. - - -Modular Construction --------------------- -The library is made of many fairly independent modules. If you're using -only one music file emulator, you can eliminate many of the library -sources from your program. Refer to the files list in readme.txt to get -a general idea of what can be removed. Post to the forum if you'd like -me to put together a smaller version for a particular use, as this only -takes me a few minutes to do. - -If you want to use one of the individual sound chip emulators in your -console emulator, first check the libraries page on my website since I -have released several of them as standalone libraries with included -documentation and examples on their use. - -The "classic" sound chips use my Blip_Buffer library, which greatly -simplifies their implementation and efficiently handles band-limited -synthesis. It is also available as a standalone library with -documentation and many examples. - - -Sound Parameters ----------------- -All emulators support adjustable output sampling rate, set with -Music_Emu::set_sample_rate(). A rate of 44100 should work well on most -systems. Since band-limited synthesis is used, a sampling rate above -48000 Hz is not necessary. - -Some emulators support adjustable treble and bass frequency equalization -(NSF, GBS, VGM) using Music_Emu::set_equalizer(). Parameters are -specified using Music_Emu::equalizer_t eq = { treble_dB, bass_freq }. -Treble_dB sets the treble level (in dB), where 0.0 dB gives normal -treble; -200.0 dB is quite muffled, and 5.0 dB emphasizes treble for an -extra crisp sound. Bass_freq sets the frequency where bass response -starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass, and 15000 -Hz removes all bass. For example, the following makes the sound -extra-crisp but lacking bass: - - Music_Emu::equalizer_t eq = { 5.0, 1000 }; - music_emu->set_equalizer( eq ); - -Each emulator's equalization defaults to a profile that approximates its -particular console's sound quality; this default can be determined by -calling Music_Emu::equalizer() just after creating the emulator. Some -emulators include other profiles for different versions of the system. -The Music_Emu::tv_eq profile gives sound as if coming from a TV speaker. -For example, to use Famicom sound equalization with the NSF emulator, do -the following: - - nsf_emu->set_equalizer( Nsf_Emu::famicom_eq ); - - -VGM/GYM YM2413 & YM2612 FM Sound --------------------------------- -The library plays Sega Genesis/Mega Drive music using a YM2612 FM sound -chip emulator based on Gens. Because this has some inaccuracies, other -YM2612 emulators can be used in its place by reimplementing the -interface in YM2612_Emu.h. Available on my website is a modified version -of MAME's YM2612 emulator, which sounds better in some ways and whose -author is still making improvements. - -VGM music files using the YM2413 FM sound chip are also supported, but a -YM2413 emulator isn't included. Similar to above, I have put one of the -available YM2413 emulators on my website that can be used directly. - - -Misc ----- -Some emulators have constructor parameters which can be specified when -creating the object. For example, this creates a Vgm_Emu with -oversampling off and a tempo of 83%: - - Vgm_Emu* emu = new Vgm_Emu( false, 0.83 ); - -For a full example of using Game_Music_Emu see the source code for Game -Music Box, a full-featured game music player for Mac OS: - - http://www.slack.net/~ant/game-music-box/dev.html - - -Thanks ------- -Big thanks to Chris Moeller (kode54) for help with library testing and -feedback, for maintaining the Foobar2000 plugin foo_gep based on it, and -for original work on openspc++ that was used when developing Spc_Emu. -Brad Martin's excellent OpenSPC SNES DSP emulator worked well from the -start. Also thanks to Richard Bannister, Mahendra Tallur, Shazz, and the -Audacious team for testing and using the library in their game music -players. - - -Solving Problems ----------------- -If you're having problems, try the following: - -- Enable debugging support in your environment. This enables assertions -and other run-time checks. - -- Turn the compiler's optimizer is off. Sometimes an optimizer generates -bad code. - -- If multiple threads are being used, ensure that only one at a time is -accessing a given set of objects from the library. This library is not -in general thread-safe, though independent objects can be used in -separate threads. - -- If all else fails, see if the demos work. - - -Error handling --------------- -Functions which can fail have a return type of blargg_err_t, which is a -pointer to an error string (const char*). If the function is successful -it returns blargg_success (NULL), otherwise it returns a pointer to an -error string. Errors which the caller can easily detect are only checked -with debug assertions; blargg_err_t returns values are only used for -genuine run-time errors that can't be easily predicted in advance (out -of memory, I/O errors, incompatible file data). - -To allow compatibility with older C++ compilers, no exceptions are -thrown by any of the modules and code is generally exception-safe. Any -exceptions thrown by the standard library or caller-supplied functions -are allowed to propagate normally. - - -Configuration -------------- -The header "blargg_common.h" is used to establish a common environment, -and is #included at the beginning of all library headers and sources. It -attempts to automatically determine the features of the environment, but -might need help. Refer to "blargg_common.h" for descriptions of -features. - -If defined HAVE_CONFIG_H in the compiler command-line, the user-provided -"config.h" is included at the beginning of each library header file, -allowing configuration options for the library to be set. I have -attempted to design the library so that configuration can be done -*without* modifying any of the library sources and header files. This -makes it easy to upgrade to a new version without losing any -customizations to its configuration. - -Post to the forum if you have problems or suggestions. -
--- a/Plugins/Input/console/gme_readme.txt Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -Game_Music_Emu 0.3.0: Game Music Emulators ------------------------------------------- -Game_Music_Emu is a collection of portable video game music emulators. Its -modular design allows elimination of any unneeded emulators and features. -Modules are included supporting the following file formats: - -GBS Nintendo Game Boy -VGM/VGZ Sega Master System/Genesis/Mega Drive/Mark III/BBC Micro -GYM Sega Genesis -SPC Super Nintendo -NSF Nintendo NES (with VRC6, N106, and FME-7 sound) - -This library has been used in game music players for Win32, Linux x86-32/64, -Mac OS X, Mac OS Classic, MorphOS (Amiga), PlayStation Portable, and GP2X. - -Author : Shay Green <hotpop.com@blargg> -Website: http://www.slack.net/~ant/ -Forum : http://groups.google.com/group/blargg-sound-libs -License: GNU Lesser General Public License (LGPL) - - -Getting Started ---------------- -Build a program consisting of demo/basics.cpp, demo/Wave_Writer.cpp, and all -source files in gme/ except Gzip_File.cpp. Be sure "test.nsf" is in the same -directory. Running the program should generate a WAVE sound file "out.wav" of -music. - -See notes.txt for more information, and respective header (.h) files for -reference. Post to the discussion forum for assistance. - - -Files ------ -notes.txt General notes about the library -changes.txt Changes made since previous releases -design.txt Library design notes -LGPL.txt GNU Lesser General Public License - -test.nsf Test file for NSF emulator - -demo/ - basics.cpp Loads game music file and records to wave sound file - info_fields.cpp Reads information tags from files - multi_format.cpp Handles multiple game music types - custom_reader.cpp Loads music data from gzip file and memory block - stereo_effects.cpp Uses Effects_Buffer to add stereo echo - - simple_player.cpp Uses Music_Player to make simple player - Music_Player.cpp Simple game music player module using SDL sound - Music_Player.h - - Wave_Writer.h WAVE sound file writer used for demo output - Wave_Writer.cpp - -gme/ - Effects_Buffer.h Sound buffer with adjustable stereo echo and panning - Effects_Buffer.cpp - - Gzip_File.h Gzip reader for transparent access to gzipped files - Gzip_File.cpp - - Music_Emu.h Game music emulator interface - - Nsf_Emu.h Nintendo NES NSF emulator - Nsf_Emu.cpp - Nes_Apu.cpp - Nes_Apu.h - Nes_Cpu.cpp - Nes_Cpu.h - Nes_Oscs.cpp - Nes_Oscs.h - Nes_Fme7_Apu.cpp - Nes_Fme7_Apu.h - Nes_Namco_Apu.cpp - Nes_Namco_Apu.h - Nes_Vrc6_Apu.cpp - Nes_Vrc6_Apu.h - - Gbs_Emu.h Nintendo Game Boy GBS emulator - Gbs_Emu.cpp - Gb_Apu.cpp - Gb_Apu.h - Gb_Cpu.cpp - Gb_Cpu.h - Gb_Oscs.cpp - Gb_Oscs.h - - Spc_Emu.h Super Nintendo SPC emulator - Spc_Emu.cpp - Snes_Spc.cpp - Snes_Spc.h - Spc_Cpu.cpp - Spc_Cpu.h - Spc_Dsp.cpp - Spc_Dsp.h - Fir_Resampler.cpp - Fir_Resampler.h - - Gym_Emu.h Sega Genesis GYM emulator - Gym_Emu.cpp - Vgm_Emu.h Sega VGM emulator - Vgm_Emu_Impl.cpp - Vgm_Emu_Impl.h - Vgm_Emu.cpp - Ym2413_Emu.cpp - Ym2413_Emu.h - Sms_Apu.cpp Common Sega emulator files - Sms_Apu.h - Sms_Oscs.h - Ym2612_Emu.cpp - Ym2612_Emu.h - Dual_Resampler.cpp - Dual_Resampler.h - Fir_Resampler.cpp - Fir_Resampler.h - - blargg_common.h Common files - blargg_endian.h - blargg_source.h - Blip_Buffer.cpp - Blip_Buffer.h - Music_Emu.cpp - Classic_Emu.h - Classic_Emu.cpp - Multi_Buffer.h - Multi_Buffer.cpp - abstract_file.cpp - abstract_file.h - - -Legal ------ -Game_Music_Emu library copyright (C) 2003-2006 Shay Green. -SNES SPC DSP emulator based on OpenSPC, copyright (C) 2002 Brad Martin. -Sega Genesis YM2612 emulator copyright (C) 2002 Stephane Dallongeville.
--- a/Plugins/Input/console/notes.txt Tue Jan 24 19:10:07 2006 -0800 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -Audacious Console Game Music Driver ------------------------------------ -Contact: Shay Green <hotpop.com@blargg> - -Notes ------ -- This is a fairly rough version. I'm sending it so we can decide more -concretely on the desired features. I don't have Unix (or even Mac OS X) -so I've only tested this lightly with a quick framework I wrote to -simulate the Audacious environment (as best as I could determine based -on the limited documentation). - -- The most significant missing feature is a way to select the track -number of multi-track formats (NSF, NSFE, GBS). I've implemented -internal support for this and marked the places where the track number -is needed from an external source. - -- Seeking should be tested carefully. It might be too slow for some -formats. - -- Currently text fields are treated as they are already in UTF-8 format -(which they aren't), but they should probably be converted from Windows -charset to UTF-8. VGM files have 16-bit chars in an unknown encoding, -currently just truncated to 8-bits. - -- Errors in Audacious_Driver.cpp are checked and generally result in -exit of the current operation and no overall effect. Information about -the cause of the error is consistently lost, so it would be difficult to -switch over to a model of actually reporting the error so the user can -know about it and take useful action. - -- Each track may contain any of the following: preferred play length, -intro length, loop length. If play length is present, it is used as the -track time. If not present, the default play length from the config file -is used. A more sophisticated algorithm could be used that takes into -account the loop length. - -- File opening and reading has been significantly minimized. Bytes read -for file identification are preserved for when the rest of header is -read. When playing a track, file information is obtained using -already-loaded data in the emulator, eliminating extra reading. - - -Change Log ----------- -- Marked things to be addressed with "// to do:" comments - -- Updated to Game_Music_Emu 0.3.0 and eliminated unnecessary source -files - -- Eliminated Audacious_Driver.h since it served no purpose - -- Added support for NSFE files - -- Added Vfs_File, a wrapper for the vfs_* file functions. This allows -all the emulators to access files in this manner. - -- Updated Makefile.am but didn't add line to link with zlib - -- Added fading at end of tracks - -- Added end-of-track silence detection for tracks without timing -information. Stops track when 6 seconds of silence have passed, but -looks ahead so that the user only experiences about 1 second of silence -before the track ends. - -- Added beginning-of-track silence removal, as some tracks have many -seconds of silence (Zelda Link's Awakening.gbs track 61 has 20 seconds -of silence, making you think it's not a music track without this -feature). - - -To Do ------ -- Separate track info handling from Audacious_Driver.cpp, since the -current complexity is a good source of bugs -