Mercurial > audlegacy-plugins
changeset 341:986f098da058 trunk
[svn] - merge in blargg's changes
line wrap: on
line diff
--- a/ChangeLog Wed Dec 06 07:57:05 2006 -0800 +++ b/ChangeLog Thu Dec 07 15:20:41 2006 -0800 @@ -1,3 +1,12 @@ +2006-12-06 15:57:05 +0000 William Pitcock <nenolod@nenolod.net> + revision [744] + - fix fprovide.[cxx,h] -- they were the wrong revision + + trunk/src/adplug/core/fprovide.cxx | 2 +- + trunk/src/adplug/core/fprovide.h | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + + 2006-12-06 15:37:05 +0000 William Pitcock <nenolod@nenolod.net> revision [742] - implement virtual class to use VFS through binio
--- a/src/console/Audacious_Driver.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Audacious_Driver.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -23,10 +23,11 @@ #include "Audacious_Config.h" #include "Music_Emu.h" -#include "Vfs_File.h" +#include "Gzip_Reader.h" int const fade_threshold = 10 * 1000; int const fade_length = 8 * 1000; +int const path_max = 4096; AudaciousConsoleConfig audcfg = { 180, FALSE, 32000, TRUE, 0, 0, FALSE, 0 }; static GThread* decode_thread; @@ -35,7 +36,6 @@ static volatile long pending_seek; extern InputPlugin console_ip; static Music_Emu* emu = 0; -static int track_ended; static blargg_err_t log_err( blargg_err_t err ) { @@ -59,84 +59,126 @@ emu = NULL; } -// Extracts track number from file path, also frees memory at end of block - -struct Url_Parser -{ - gchar* path; // path without track number specification - int track; // track number (0 = first track) - bool track_specified; // false if no track number was specified in path - Url_Parser( gchar* path ); - ~Url_Parser() { g_free( path ); } +// Handles URL parsing, file opening and identification, and file loading. +// Keeps file header around when loading rest of file to avoid seeking +// and re-reading. +class File_Handler { +public: + gchar* path; // path without track number specification + int track; // track number (0 = first track) + bool track_specified; // false if no track number was specified in path + Music_Emu* emu; // set to 0 to take ownership + gme_type_t type; + + // Parses path and identifies file type + File_Handler( const char* path, VFSFile* fd = 0 ); + + // Creates emulator and returns 0. If this wasn't a music file or + // emulator couldn't be created, returns 1. + int load( long sample_rate ); + + // Deletes owned emu and closes file + ~File_Handler(); +private: + char header [4]; + Vfs_File_Reader vfs_in; + Gzip_Reader in; }; -Url_Parser::Url_Parser( gchar* path_in ) +File_Handler::File_Handler( const char* path_in, VFSFile* fd ) { + emu = 0; + type = 0; track = 0; track_specified = false; path = g_strdup( path_in ); - if ( path ) + if ( !path ) + return; // out of memory + + // extract track number + gchar* args = strchr( path, '?' ); // TODO: use strrchr()? + if ( args ) { - gchar* args = strchr( path, '?' ); - if ( args ) + *args = '\0'; + // TODO: use func with better error reporting, and perhaps don't + // truncate path if there is no number after ? + track = atoi( args + 1 ); + track_specified = true; + } + + // open vfs + if ( fd ) + vfs_in.reset( fd ); + else if ( log_err( vfs_in.open( path ) ) ) + return; + + // now open gzip_reader on top of vfs + if ( log_err( in.open( &vfs_in ) ) ) + return; + + // read and identify header + if ( !log_err( in.read( header, sizeof header ) ) ) + { + type = gme_identify_extension( gme_identify_header( header ) ); + if ( !type ) { - *args = '\0'; - track = atoi( args + 1 ); - track_specified = true; + type = gme_identify_extension( path ); + if ( type != gme_gym_type ) // only trust file extension for headerless .gym files + type = 0; } } } -// Determine file type based on header contents. Returns 0 if unrecognized or path is NULL. -static gme_type_t identify_file( gchar* path ) +File_Handler::~File_Handler() { - if ( path ) - { - char header [4] = { }; - GME_FILE_READER in; - if ( !log_err( in.open( path ) ) && !log_err( in.read( header, sizeof header ) ) ) - return gme_identify_extension( gme_identify_header( header ), gme_type_list() ); - } - return 0; + gme_delete( emu ); + g_free( path ); } -// Load file into emulator/info reader and load m3u in same directory, if present. -// If emu is NULL, returns out of memory error. -static blargg_err_t load_in_emu( Music_Emu* emu, const char* path, VFSFile* fd = 0 ) +int File_Handler::load( long sample_rate ) { - if ( !emu ) - return "Out of memory"; - - Vfs_File_Reader in; - blargg_err_t err = 0; - if ( fd ) - in.reset( fd ); // use fd and let caller close it - else - err = in.open( path ); + if ( !type ) + return 1; - if ( !err ) - err = emu->load( in ); - in.close(); - - if ( !err ) + emu = gme_new_emu( type, sample_rate ); + if ( !emu ) { - log_warning( emu ); - - // load .m3u in same directory - int const path_max = 4096; - char m3u_path [path_max + 5]; - strncpy( m3u_path, path, path_max ); - m3u_path [path_max] = 0; - char* p = strrchr( m3u_path, '.' ); - if ( !p ) - p = m3u_path + strlen( m3u_path ); - strcpy( p, ".m3u" ); - - if ( emu->load_m3u( m3u_path ) ) { } // TODO: log error if m3u file exists + log_err( "Out of memory" ); + return 1; + } + + { + // combine header with remaining file data + Remaining_Reader reader( header, sizeof header, &in ); + if ( log_err( emu->load( reader ) ) ) + return 1; } - return err; + // files can be closed now + in.close(); + vfs_in.close(); + + log_warning( emu ); + + // load .m3u from same directory( replace/add extension with ".m3u") + char m3u_path [path_max + 5]; + strncpy( m3u_path, path, path_max ); + m3u_path [path_max] = 0; + // TODO: use better path-building functions + char* p = strrchr( m3u_path, '.' ); + if ( !p ) + p = m3u_path + strlen( m3u_path ); + strcpy( p, ".m3u" ); + + Vfs_File_Reader m3u; + if ( !m3u.open( m3u_path ) ) + { + if ( log_err( emu->load_m3u( m3u ) ) ) // TODO: fail if m3u can't be loaded? + log_warning( emu ); // this will log line number of first problem in m3u + } + + return 0; } // Get info @@ -154,6 +196,7 @@ if ( info.track_count > 1 ) ti->track_number = track + 1; ti->comment = g_strdup( info.copyright ); + ti->genre = g_strconcat( "Console: ", info.system, NULL ); int length = info.length; if ( length <= 0 ) @@ -180,14 +223,13 @@ static TitleInput *get_song_tuple( gchar *path ) { TitleInput* result = 0; - - Url_Parser url( path ); - Music_Emu* emu = gme_new_info( identify_file( url.path ) ); - track_info_t info; - if ( !log_err( load_in_emu( emu, url.path ) ) && - !log_err( emu->track_info( &info, url.track ) ) ) - result = get_track_ti( url.path, info, url.track ); - delete emu; + File_Handler fh( path ); + if ( !fh.load( gme_info_only ) ) + { + track_info_t info; + if ( !log_err( fh.emu->track_info( &info, fh.track ) ) ) + result = get_track_ti( fh.path, info, fh.track ); + } return result; } @@ -207,11 +249,8 @@ { g_static_mutex_lock( &playback_mutex ); - while ( console_ip_is_going ) + while ( console_ip_is_going && !emu->track_ended() ) { - int const buf_size = 1024; - Music_Emu::sample_t buf [buf_size]; - // handle pending seek long s = pending_seek; pending_seek = -1; // TODO: use atomic swap @@ -220,21 +259,13 @@ console_ip.output->flush( s * 1000 ); emu->seek( s * 1000 ); } - - // fill buffer - if ( track_ended ) - { - // TODO: remove delay once host doesn't cut the end of track off - int const delay = 0; // seconds - if ( track_ended++ > delay * emu->sample_rate() / (buf_size / 2) ) - console_ip_is_going = false; - memset( buf, 0, sizeof buf ); - } - else - { - emu->play( buf_size, buf ); - track_ended = emu->track_ended(); - } + + // fill and play buffer of audio + // TODO: see if larger buffer helps efficiency + int const buf_size = 1024; + Music_Emu::sample_t buf [buf_size]; + emu->play( buf_size, buf ); + produce_audio( console_ip.output->written_time(), FMT_S16_NE, 1, sizeof buf, buf, &console_ip_is_going ); @@ -255,29 +286,25 @@ unload_file(); // identify file - Url_Parser url( path ); - gme_type_t type = identify_file( url.path ); - if ( !type ) return; + File_Handler fh( path ); + if ( !fh.type ) + return; - // sample rate + // select sample rate long sample_rate = 0; - if ( type == gme_spc_type ) + if ( fh.type == gme_spc_type ) sample_rate = 32000; if ( audcfg.resample ) sample_rate = audcfg.resample_rate; if ( !sample_rate ) sample_rate = 44100; - // create emulator and load - emu = gme_new_emu( type, sample_rate ); - if ( load_in_emu( emu, url.path ) ) - { - unload_file(); + // create emulator and load file + if ( fh.load( sample_rate ) ) return; - } // stereo echo depth - gme_set_stereo_depth( emu, 1.0 / 100 * audcfg.echo ); + gme_set_stereo_depth( fh.emu, 1.0 / 100 * audcfg.echo ); // set equalizer if ( audcfg.treble || audcfg.bass ) @@ -292,53 +319,54 @@ double treble = audcfg.treble / 100.0; eq.treble = treble * (treble < 0 ? 50.0 : 5.0); - emu->set_equalizer(eq); + fh.emu->set_equalizer(eq); } // get info int length = -1; track_info_t info; - if ( !log_err( emu->track_info( &info, url.track ) ) ) + if ( !log_err( fh.emu->track_info( &info, fh.track ) ) ) { - if ( type == gme_spc_type && audcfg.ignore_spc_length ) + if ( fh.type == gme_spc_type && audcfg.ignore_spc_length ) info.length = -1; - TitleInput* ti = get_track_ti( url.path, info, url.track ); + TitleInput* ti = get_track_ti( fh.path, info, fh.track ); if ( ti ) { char* title = format_and_free_ti( ti, &length ); if ( title ) { - console_ip.set_info( title, length, emu->voice_count() * 1000, sample_rate, 2 ); + console_ip.set_info( title, length, fh.emu->voice_count() * 1000, sample_rate, 2 ); g_free( title ); } } } + + // start track + if ( log_err( fh.emu->start_track( fh.track ) ) ) + return; + log_warning( fh.emu ); + if ( !console_ip.output->open_audio( FMT_S16_NE, sample_rate, 2 ) ) + return; + + // set fade time if ( length <= 0 ) length = audcfg.loop_length * 1000; - - if ( log_err( emu->start_track( url.track ) ) ) - { - unload_file(); - return; - } - log_warning( emu ); - - // start track - if ( !console_ip.output->open_audio( FMT_S16_NE, sample_rate, 2 ) ) - return; - pending_seek = -1; - track_ended = 0; if ( length >= fade_threshold + fade_length ) length -= fade_length; - emu->set_fade( length, fade_length ); + fh.emu->set_fade( length, fade_length ); + + // take ownership of emu + emu = fh.emu; + fh.emu = 0; + + pending_seek = -1; console_ip_is_going = 1; decode_thread = g_thread_create( play_loop_track, NULL, TRUE, NULL ); } static void seek( gint time ) { - // TODO: be sure seek works at all - // TODO: disallow seek on slow formats (SPC, GYM, VGM using FM)? + // TODO: use thread-safe atomic set pending_seek = time; } @@ -364,59 +392,37 @@ return console_ip_is_going ? console_ip.output->output_time() : -1; } -static gint is_our_file_from_vfs( gchar* filename, VFSFile* fd ) +static gint is_our_file_from_vfs( gchar* path, VFSFile* fd ) { - Url_Parser url( filename ); - if ( !url.path ) return false; - - // open file if not already open - Vfs_File_Reader in; - if ( !fd ) + gint result = 0; + File_Handler fh( path, fd ); + if ( fh.type ) { - if ( log_err( in.open( url.path ) ) ) return false; - fd = in.file(); - // in will be closed when function ends - } - - // read header and identify type - gchar header [4] = { }; - vfs_fread( header, sizeof header, 1, fd ); - gme_type_t type = gme_identify_extension( gme_identify_header( header ), gme_type_list() ); - - gint result = 0; - if ( type ) - { - if ( url.track_specified || type->track_count == 1 ) + if ( fh.track_specified || fh.type->track_count == 1 ) { // don't even need to read file if track is specified or // that file format can't have more than one track per file result = 1; } - else + else if ( !fh.load( gme_info_only ) ) { // format requires reading file info to get track count - Music_Emu* emu = gme_new_info( type ); - vfs_rewind( fd ); - if ( !log_err( load_in_emu( emu, url.path, fd ) ) ) + if ( fh.emu->track_count() == 1 ) { - if ( emu->track_count() == 1 ) - { - result = 1; - } - else + result = 1; + } + else + { + // for multi-track types, add each track to playlist + for (int i = 0; i < fh.emu->track_count(); i++) { - // for multi-track types, add each track to playlist - for (int i = 0; i < emu->track_count(); i++) - { - gchar _buf[4096]; - g_snprintf(_buf, 4096, "%s?%d", url.path, i); + gchar _buf[path_max]; + g_snprintf(_buf, path_max, "%s?%d", fh.path, i); - playlist_add_url(_buf); - } - result = -1; + playlist_add_url(_buf); } + result = -1; } - delete emu; } } return result; @@ -436,7 +442,7 @@ if (!aboutbox) { aboutbox = xmms_show_message(_("About the Console Music Decoder"), - _("Console music decoder engine based on Game_Music_Emu 0.5.1.\n" + _("Console music decoder engine based on Game_Music_Emu 0.5.2.\n" "Audacious implementation by: William Pitcock <nenolod@nenolod.net>, \n" " Shay Green <gblargg@gmail.com>"), _("Ok"),
--- a/src/console/Ay_Apu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Ay_Apu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Ay_Apu.h" @@ -74,13 +74,13 @@ { byte* out = env.modes [m]; int flags = modes [m]; - for ( int n = 3; --n >= 0; ) + for ( int x = 3; --x >= 0; ) { int amp = flags & 1; int end = flags >> 1 & 1; int step = end - amp; amp *= 15; - for ( int n = 16; --n >= 0; ) + for ( int y = 16; --y >= 0; ) { *out++ = amp_table [amp]; amp += step; @@ -119,6 +119,8 @@ void Ay_Apu::write_data_( int addr, int data ) { + assert( (unsigned) addr < reg_count ); + if ( (unsigned) addr >= 14 ) { #ifdef dprintf @@ -268,11 +270,13 @@ int amp = 0; if ( (osc_mode | osc->phase) & 1 & (osc_mode >> 3 | noise_lfsr) ) amp = volume; - int delta = amp - osc->last_amp; - if ( delta ) { - osc->last_amp = amp; - synth_.offset( start_time, delta, osc_output ); + int delta = amp - osc->last_amp; + if ( delta ) + { + osc->last_amp = amp; + synth_.offset( start_time, delta, osc_output ); + } } // Run wave and noise interleved with each catching up to the other.
--- a/src/console/Ay_Apu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Ay_Apu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // AY-3-8910 sound chip emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef AY_APU_H #define AY_APU_H @@ -16,6 +16,7 @@ void reset(); // Write to register at specified time + enum { reg_count = 16 }; void write( blip_time_t time, int addr, int data ); // Run sound to specified time, end current time frame, then start a new @@ -50,7 +51,7 @@ } oscs [osc_count]; blip_time_t last_time; byte latch; - byte regs [16]; + byte regs [reg_count]; struct { blip_time_t delay;
--- a/src/console/Ay_Cpu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Ay_Cpu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,8 +1,10 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ -// Last validated with zexall 2006.11.21 5:26 PM -// Doesn't implement interrupts or the R register, though both would be -// easy to support. +/* +Last validated with zexall 2006.11.21 5:26 PM +* Doesn't implement the R register or immediate interrupt after EI. +* Address wrap-around isn't completely correct, but is prevented from crashing emulator. +*/ #include "Ay_Cpu.h" @@ -27,10 +29,10 @@ // Callbacks to emulator -#define CPU_OUT( cpu, addr, data, TIME ) \ +#define CPU_OUT( cpu, addr, data, TIME )\ ay_cpu_out( cpu, TIME, addr, data ) -#define CPU_IN( cpu, addr, TIME ) \ +#define CPU_IN( cpu, addr, TIME )\ ay_cpu_in( cpu, addr ) #include "blargg_source.h" @@ -105,7 +107,7 @@ //#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)]) // help compiler see that it can just adjust stack offset, saving an extra instruction -#define R16( n, shift, offset ) \ +#define R16( n, shift, offset )\ (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) #define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e @@ -354,7 +356,14 @@ goto loop; } - CASE8( C7, CF, D7, DF, E7, EF, F7, FF ): // RST + case 0xFF: // RST + if ( (pc - 1) > 0xFFFF ) + { + pc = uint16_t (pc - 1); + s_time -= 11; + goto loop; + } + CASE7( C7, CF, D7, DF, E7, EF, F7 ): data = pc; pc = opcode & 0x38; goto push_data; @@ -1297,6 +1306,9 @@ s_time += ed_dd_timing [data] & 0x0F; switch ( data ) { + // TODO: more efficient way of avoid negative address + #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) + #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; // ADD/ADC/SUB/SBC @@ -1308,7 +1320,7 @@ case 0x8E: // ADC (IXY+disp) pc++; opcode = data; - data = READ( ixy + (int8_t) data2 ); + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto adc_data; case 0x94: // SUB HXY @@ -1330,26 +1342,26 @@ goto adc_data; { - unsigned data2; + unsigned temp; case 0x39: // ADD IXY,SP - data2 = sp; + temp = sp; goto add_ixy_data; case 0x29: // ADD IXY,HL - data2 = ixy; + temp = ixy; goto add_ixy_data; case 0x09: // ADD IXY,BC case 0x19: // ADD IXY,DE - data2 = R16( data, 4, 0x09 ); + temp = R16( data, 4, 0x09 ); add_ixy_data: { - blargg_ulong sum = ixy + data2; - data2 ^= ixy; - ixy = sum; + blargg_ulong sum = ixy + temp; + temp ^= ixy; + ixy = (uint16_t) sum; flags = (flags & (S80 | Z40 | V04)) | (sum >> 16) | (sum >> 8 & (F20 | F08)) | - ((data2 ^ sum) >> 8 & H10); + ((temp ^ sum) >> 8 & H10); goto set_ixy; } } @@ -1357,7 +1369,7 @@ // AND case 0xA6: // AND (IXY+disp) pc++; - data = READ( ixy + (int8_t) data2 ); + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto and_data; case 0xA4: // AND HXY @@ -1371,7 +1383,7 @@ // OR case 0xB6: // OR (IXY+disp) pc++; - data = READ( ixy + (int8_t) data2 ); + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto or_data; case 0xB4: // OR HXY @@ -1385,7 +1397,7 @@ // XOR case 0xAE: // XOR (IXY+disp) pc++; - data = READ( ixy + (int8_t) data2 ); + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto xor_data; case 0xAC: // XOR HXY @@ -1399,7 +1411,7 @@ // CP case 0xBE: // CP (IXY+disp) pc++; - data = READ( ixy + (int8_t) data2 ); + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto cp_data; case 0xBC: // CP HXY @@ -1417,7 +1429,7 @@ case 0x36: // LD (IXY+disp),imm pc++, data = READ_PROG( pc ); pc++; - WRITE( ixy + (int8_t) data2, data ); + WRITE( IXY_DISP( ixy, (int8_t) data2 ), data ); goto loop; CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY @@ -1434,7 +1446,7 @@ CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) pc++; - R8( data >> 3, 8 ) = READ( ixy + (int8_t) data2 ); + R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto loop; case 0x26: // LD HXY,imm @@ -1497,7 +1509,7 @@ // DD/FD CB prefix case 0xCB: { - data = ixy + (int8_t) data2; + data = IXY_DISP( ixy, (int8_t) data2 ); pc++; data2 = READ_PROG( pc ); pc++; @@ -1550,14 +1562,14 @@ goto set_ixy; case 0x34: // INC (IXY+disp) - ixy += (int8_t) data2; + ixy = IXY_DISP( ixy, (int8_t) data2 ); pc++; data = READ( ixy ) + 1; WRITE( ixy, data ); goto inc_set_flags; case 0x35: // DEC (IXY+disp) - ixy += (int8_t) data2; + ixy = IXY_DISP( ixy, (int8_t) data2 ); pc++; data = READ( ixy ) - 1; WRITE( ixy, data ); @@ -1639,12 +1651,12 @@ out_of_time: pc--; - s.time = s_time; + s.time = s_time; rg.flags = flags; - r.ix = ix; - r.iy = iy; - r.sp = sp; - r.pc = pc; + r.ix = ix; + r.iy = iy; + r.sp = sp; + r.pc = pc; this->r.b = rg; this->state_ = s; this->state = &this->state_;
--- a/src/console/Ay_Cpu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Ay_Cpu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Z80 CPU emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef AY_CPU_H #define AY_CPU_H
--- a/src/console/Ay_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Ay_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Ay_Emu.h" @@ -18,6 +18,9 @@ #include "blargg_source.h" +long const spectrum_clock = 3546900; +long const cpc_clock = 2000000; + unsigned const ram_start = 0x4000; int const osc_count = Ay_Apu::osc_count + 1; @@ -59,7 +62,7 @@ out->header = (header_t const*) in; out->end = in + size; - if ( size < (long) sizeof (header_t) ) + if ( size < Ay_Emu::header_size ) return gme_wrong_file_type; header_t const& h = *(header_t const*) in; @@ -113,12 +116,14 @@ static Music_Emu* new_ay_emu () { return BLARGG_NEW Ay_Emu ; } static Music_Emu* new_ay_file() { return BLARGG_NEW Ay_File; } -gme_type_t_ const gme_ay_type [1] = { "Sinclair Spectrum", 0, &new_ay_emu, &new_ay_file, "AY", 1 }; +gme_type_t_ const gme_ay_type [1] = { "ZX Spectrum", 0, &new_ay_emu, &new_ay_file, "AY", 1 }; // Setup blargg_err_t Ay_Emu::load_mem_( byte const* in, long size ) { + assert( offsetof (header_t,track_info [2]) == header_size ); + RETURN_ERR( parse_header( in, size, &file ) ); set_track_count( file.header->max_track + 1 ); @@ -128,7 +133,7 @@ set_voice_count( osc_count ); apu.volume( gain() ); - return setup_buffer( 3546900 ); + return setup_buffer( spectrum_clock ); } void Ay_Emu::update_eq( blip_eq_t const& eq ) @@ -155,9 +160,11 @@ { RETURN_ERR( Classic_Emu::start_track_( track ) ); - memset( mem + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET - memset( mem + 0x0100, 0xFF, 0x4000 - 0x100 ); - memset( mem + ram_start, 0x00, sizeof mem - ram_start ); + memset( mem.ram + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET + memset( mem.ram + 0x0100, 0xFF, 0x4000 - 0x100 ); + memset( mem.ram + ram_start, 0x00, sizeof mem.ram - ram_start ); + memset( mem.padding1, 0xFF, sizeof mem.padding1 ); + memset( mem.ram + 0x10000, 0xFF, sizeof mem.ram - 0x10000 ); // locate data blocks byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 ); @@ -170,7 +177,7 @@ if ( !blocks ) return "File data missing"; // initial addresses - cpu::reset( mem ); + cpu::reset( mem.ram ); r.sp = get_be16( more_data ); r.b.a = r.b.b = r.b.d = r.b.h = data [8]; r.b.flags = r.b.c = r.b.e = r.b.l = data [9]; @@ -204,7 +211,7 @@ //dprintf( "addr: $%04X, len: $%04X\n", addr, len ); if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data dprintf( "Block addr in ROM\n" ); - memcpy( mem + addr, in, len ); + memcpy( mem.ram + addr, in, len ); if ( file.end - blocks < 8 ) { @@ -232,37 +239,98 @@ 0xCD, 0, 0, // CALL play 0x18, 0xF7 // JR LOOP }; - memcpy( mem, passive, sizeof passive ); + memcpy( mem.ram, passive, sizeof passive ); unsigned play_addr = get_be16( more_data + 4 ); //dprintf( "Play: $%04X\n", play_addr ); if ( play_addr ) { - memcpy( mem, active, sizeof active ); - mem [ 9] = play_addr; - mem [10] = play_addr >> 8; + memcpy( mem.ram, active, sizeof active ); + mem.ram [ 9] = play_addr; + mem.ram [10] = play_addr >> 8; } - mem [2] = init; - mem [3] = init >> 8; + mem.ram [2] = init; + mem.ram [3] = init >> 8; - mem [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET) + mem.ram [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET) - memcpy( mem + 0x10000, mem, sizeof mem - 0x10000 ); // some code wraps around (ugh) + memcpy( mem.ram + 0x10000, mem.ram, 0x80 ); // some code wraps around (ugh) beeper_delta = int (apu.amp_range * 0.65); last_beeper = 0; apu.reset(); next_play = play_period; + // start at spectrum speed + change_clock_rate( spectrum_clock ); + set_tempo( tempo() ); + + spectrum_mode = false; + cpc_mode = false; + cpc_latch = 0; + return 0; } // Emulation +void Ay_Emu::cpu_out_misc( cpu_time_t time, unsigned addr, int data ) +{ + if ( !cpc_mode ) + { + switch ( addr & 0xFEFF ) + { + case 0xFEFD: + spectrum_mode = true; + apu_addr = data & 0x0F; + return; + + case 0xBEFD: + spectrum_mode = true; + apu.write( time, apu_addr, data ); + return; + } + } + + if ( !spectrum_mode ) + { + switch ( addr >> 8 ) + { + case 0xF6: + switch ( data & 0xC0 ) + { + case 0xC0: + apu_addr = cpc_latch & 0x0F; + goto enable_cpc; + + case 0x80: + apu.write( time, apu_addr, cpc_latch ); + goto enable_cpc; + } + break; + + case 0xF4: + cpc_latch = data; + goto enable_cpc; + } + } + + dprintf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); + return; + +enable_cpc: + if ( !cpc_mode ) + { + cpc_mode = true; + change_clock_rate( cpc_clock ); + set_tempo( tempo() ); + } +} + void ay_cpu_out( Ay_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) { Ay_Emu& emu = STATIC_CAST(Ay_Emu&,*cpu); - if ( (addr & 0xFF) == 0xFE ) + if ( (addr & 0xFF) == 0xFE && !emu.cpc_mode ) { int delta = emu.beeper_delta; data &= 0x10; @@ -270,31 +338,22 @@ { emu.last_beeper = data; emu.beeper_delta = -delta; + emu.spectrum_mode = true; if ( emu.beeper_output ) emu.apu.synth_.offset( time, delta, emu.beeper_output ); } - return; } - - switch ( addr & 0xFEFF ) + else { - case 0xFEFD: - emu.apu_addr = data & 0x0F; - return; - - case 0xBEFD: - emu.apu.write( time, emu.apu_addr, data ); - //remote_write( apu_addr, data ); - return; + emu.cpu_out_misc( time, addr, data ); } - - dprintf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); } int ay_cpu_in( Ay_Cpu*, unsigned addr ) { // keyboard read and other things - if ( (addr & 0xFF) == 0xFE ) return 0xFF; // other values break some beeper tunes + if ( (addr & 0xFF) == 0xFE ) + return 0xFF; // other values break some beeper tunes dprintf( "Unmapped IN : $%04X\n", addr ); return 0xFF; @@ -303,9 +362,11 @@ blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int ) { set_time( 0 ); + if ( !(spectrum_mode | cpc_mode) ) + duration /= 2; // until mode is set, leave room for halved clock rate + while ( time() < duration ) { - //long start = time(); cpu::run( min( duration, next_play ) ); if ( time() >= next_play ) @@ -314,26 +375,23 @@ if ( r.iff1 ) { - // TODO: don't interrupt if not enabled - if ( mem [r.pc] == 0x76 ) + if ( mem.ram [r.pc] == 0x76 ) r.pc++; r.iff1 = r.iff2 = 0; - mem [--r.sp] = r.pc >> 8; - mem [--r.sp] = r.pc; + mem.ram [--r.sp] = r.pc >> 8; + mem.ram [--r.sp] = r.pc; r.pc = 0x38; cpu::adjust_time( 12 ); if ( r.im == 2 ) { cpu::adjust_time( 6 ); unsigned addr = r.i * 0x100u + 0xFF; - r.pc = mem [(addr + 1) & 0xFFFF] * 0x100u + mem [addr]; + r.pc = mem.ram [(addr + 1) & 0xFFFF] * 0x100u + mem.ram [addr]; } } } - //dprintf( "elapsed: %d\n", time() - start ); - //remote_frame(); } duration = time(); next_play -= duration;
--- a/src/console/Ay_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Ay_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Sinclair Spectrum AY music file emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef AY_EMU_H #define AY_EMU_H @@ -12,6 +12,7 @@ typedef Ay_Cpu cpu; public: // AY file header + enum { header_size = 0x14 }; struct header_t { byte tag [8]; @@ -24,7 +25,6 @@ byte first_track; byte track_info [2]; }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 0x14 ); static gme_type_t static_type() { return gme_ay_type; } public: @@ -53,11 +53,18 @@ int beeper_delta; int last_beeper; int apu_addr; + int cpc_latch; + bool spectrum_mode; + bool cpc_mode; // large items - byte mem [0x10000 + cpu_padding]; + struct { + byte padding1 [0x100]; + byte ram [0x10000 + 0x100]; + } mem; Ay_Apu apu; friend void ay_cpu_out( Ay_Cpu*, cpu_time_t, unsigned addr, int data ); + void cpu_out_misc( cpu_time_t, unsigned addr, int data ); }; #endif
--- a/src/console/Blip_Buffer.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Blip_Buffer.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -120,9 +120,9 @@ return 0; // success } -blip_resampled_time_t Blip_Buffer::clock_rate_factor( long clock_rate ) const +blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const { - double ratio = (double) sample_rate_ / clock_rate; + double ratio = (double) sample_rate_ / rate; blip_long factor = (blip_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; @@ -189,19 +189,19 @@ // Blip_Synth_ -#if BLIP_BUFFER_FAST - Blip_Synth_::Blip_Synth_() - { - buf = 0; - last_amp = 0; - delta_factor = 0; - } +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = 0; + last_amp = 0; + delta_factor = 0; +} - void Blip_Synth_::volume_unit( double new_unit ) - { - delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); - } -#else +void Blip_Synth_Fast_::volume_unit( double new_unit ) +{ + delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); +} + +#if !BLIP_BUFFER_FAST Blip_Synth_::Blip_Synth_( short* p, int w ) : impulses( p ),
--- a/src/console/Blip_Buffer.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Blip_Buffer.h Thu Dec 07 15:20:41 2006 -0800 @@ -149,6 +149,17 @@ int const blip_res = 1 << BLIP_PHASE_BITS; class blip_eq_t; + class Blip_Synth_Fast_ { + public: + Blip_Buffer* buf; + int last_amp; + int delta_factor; + + void volume_unit( double ); + Blip_Synth_Fast_(); + void treble_eq( blip_eq_t const& ) { } + }; + class Blip_Synth_ { public: Blip_Buffer* buf; @@ -156,10 +167,6 @@ int delta_factor; void volume_unit( double ); - #if BLIP_BUFFER_FAST - Blip_Synth_(); - void treble_eq( blip_eq_t const& ) { } - #else Blip_Synth_( short* impulses, int width ); void treble_eq( blip_eq_t const& ); private: @@ -169,7 +176,6 @@ blip_long kernel_unit; int impulses_size() const { return blip_res / 2 * width + 1; } void adjust_impulse(); - #endif }; // Quality level. Start with blip_good_quality. @@ -217,8 +223,10 @@ } private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; +#else Blip_Synth_ impl; -#if !BLIP_BUFFER_FAST typedef short imp_t; imp_t impulses [blip_res * (quality / 2) + 1]; public:
--- a/src/console/Classic_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Classic_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Classic_Emu.h" @@ -43,7 +43,7 @@ buf->bass_freq( equalizer().bass ); } -blargg_err_t Classic_Emu::set_sample_rate_( long sample_rate ) +blargg_err_t Classic_Emu::set_sample_rate_( long rate ) { if ( !buf ) { @@ -51,7 +51,7 @@ CHECK_ALLOC( stereo_buffer = BLARGG_NEW Stereo_Buffer ); buf = stereo_buffer; } - return buf->set_sample_rate( sample_rate, 1000 / 20 ); + return buf->set_sample_rate( rate, 1000 / 20 ); } void Classic_Emu::mute_voices_( int mask ) @@ -73,10 +73,15 @@ } } -blargg_err_t Classic_Emu::setup_buffer( long rate ) +void Classic_Emu::change_clock_rate( long rate ) { clock_rate_ = rate; buf->clock_rate( rate ); +} + +blargg_err_t Classic_Emu::setup_buffer( long rate ) +{ + change_clock_rate( rate ); RETURN_ERR( buf->set_channel_count( voice_count() ) ); set_equalizer( equalizer() ); buf_changed_count = buf->channels_changed_count();
--- a/src/console/Classic_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Classic_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Common aspects of emulators which use Blip_Buffer for sound output -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef CLASSIC_EMU_H #define CLASSIC_EMU_H @@ -19,6 +19,7 @@ void set_voice_types( int const* t ) { voice_types = t; } blargg_err_t setup_buffer( long clock_rate ); long clock_rate() const { return clock_rate_; } + void change_clock_rate( long ); // experimental // Overridable virtual void set_voice( int index, Blip_Buffer* center, @@ -113,4 +114,14 @@ } }; +#ifndef GME_APU_HOOK + #define GME_APU_HOOK( emu, addr, data ) ((void) 0) #endif + +#ifndef GME_FRAME_HOOK + #define GME_FRAME_HOOK( emu ) ((void) 0) +#else + #define GME_FRAME_HOOK_DEFINED 1 +#endif + +#endif
--- a/src/console/Data_Reader.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Data_Reader.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -2,6 +2,7 @@ #include "Data_Reader.h" +#include "blargg_endian.h" #include <assert.h> #include <string.h> #include <stdio.h> @@ -17,11 +18,11 @@ License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include "blargg_source.h" + const char Data_Reader::eof_error [] = "Unexpected end of file"; -typedef Data_Reader::error_t error_t; - -error_t Data_Reader::read( void* p, long s ) +blargg_err_t Data_Reader::read( void* p, long s ) { long result = read_avail( p, s ); if ( result != s ) @@ -35,7 +36,7 @@ return 0; } -error_t Data_Reader::skip( long count ) +blargg_err_t Data_Reader::skip( long count ) { char buf [512]; while ( count ) @@ -44,19 +45,19 @@ if ( n > count ) n = count; count -= n; - error_t err = read( buf, n ); - if ( err ) - return err; + RETURN_ERR( read( buf, n ) ); } return 0; } long File_Reader::remain() const { return size() - tell(); } -error_t File_Reader::skip( long n ) +blargg_err_t File_Reader::skip( long n ) { assert( n >= 0 ); - return n ? seek( tell() + n ) : 0; + if ( !n ) + return 0; + return seek( tell() + n ); } // Subset_Reader @@ -97,9 +98,9 @@ { if ( first > count ) first = count; - void const* in = header; + void const* old = header; header += first; - memcpy( out, in, first ); + memcpy( out, old, first ); } return first; } @@ -107,23 +108,23 @@ long Remaining_Reader::read_avail( void* out, long count ) { long first = read_first( out, count ); - long remain = count - first; - if ( remain ) + long second = count - first; + if ( second ) { - remain = in->read_avail( (char*) out + first, remain ); - if ( remain <= 0 ) - return remain; + second = in->read_avail( (char*) out + first, second ); + if ( second <= 0 ) + return second; } - return first + remain; + return first + second; } -error_t Remaining_Reader::read( void* out, long count ) +blargg_err_t Remaining_Reader::read( void* out, long count ) { long first = read_first( out, count ); - long remain = count - first; - if ( !remain ) + long second = count - first; + if ( !second ) return 0; - return in->read( (char*) out + first, remain ); + return in->read( (char*) out + first, second ); } // Mem_File_Reader @@ -149,7 +150,7 @@ long Mem_File_Reader::tell() const { return pos; } -error_t Mem_File_Reader::seek( long n ) +blargg_err_t Mem_File_Reader::seek( long n ) { if ( n > size_ ) return eof_error; @@ -157,63 +158,6 @@ return 0; } -// Std_File_Reader - -Std_File_Reader::Std_File_Reader() : file_( 0 ) { } - -Std_File_Reader::~Std_File_Reader() { close(); } - -error_t Std_File_Reader::open( const char* path ) -{ - file_ = fopen( path, "rb" ); - if ( !file_ ) - return "Couldn't open file"; - return 0; -} - -long Std_File_Reader::size() const -{ - long pos = tell(); - fseek( (FILE*) file_, 0, SEEK_END ); - long result = tell(); - fseek( (FILE*) file_, pos, SEEK_SET ); - return result; -} - -long Std_File_Reader::read_avail( void* p, long s ) -{ - return fread( p, 1, s, (FILE*) file_ ); -} - -error_t Std_File_Reader::read( void* p, long s ) -{ - if ( s == (long) fread( p, 1, s, (FILE*) file_ ) ) - return 0; - if ( feof( (FILE*) file_ ) ) - return eof_error; - return "Couldn't read from file"; -} - -long Std_File_Reader::tell() const { return ftell( (FILE*) file_ ); } - -error_t Std_File_Reader::seek( long n ) -{ - if ( !fseek( (FILE*) file_, n, SEEK_SET ) ) - return 0; - if ( n > size() ) - return eof_error; - return "Error seeking in file"; -} - -void Std_File_Reader::close() -{ - if ( file_ ) - { - fclose( (FILE*) file_ ); - file_ = 0; - } -} - // Callback_Reader Callback_Reader::Callback_Reader( callback_t c, long size, void* d ) : @@ -234,13 +178,70 @@ return count; } -Callback_Reader::error_t Callback_Reader::read( void* out, long count ) +blargg_err_t Callback_Reader::read( void* out, long count ) { if ( count > remain_ ) return eof_error; return callback( data, out, count ); } +// Std_File_Reader + +Std_File_Reader::Std_File_Reader() : file_( 0 ) { } + +Std_File_Reader::~Std_File_Reader() { close(); } + +blargg_err_t Std_File_Reader::open( const char* path ) +{ + file_ = fopen( path, "rb" ); + if ( !file_ ) + return "Couldn't open file"; + return 0; +} + +long Std_File_Reader::size() const +{ + long pos = tell(); + fseek( (FILE*) file_, 0, SEEK_END ); + long result = tell(); + fseek( (FILE*) file_, pos, SEEK_SET ); + return result; +} + +long Std_File_Reader::read_avail( void* p, long s ) +{ + return fread( p, 1, s, (FILE*) file_ ); +} + +blargg_err_t Std_File_Reader::read( void* p, long s ) +{ + if ( s == (long) fread( p, 1, s, (FILE*) file_ ) ) + return 0; + if ( feof( (FILE*) file_ ) ) + return eof_error; + return "Couldn't read from file"; +} + +long Std_File_Reader::tell() const { return ftell( (FILE*) file_ ); } + +blargg_err_t Std_File_Reader::seek( long n ) +{ + if ( !fseek( (FILE*) file_, n, SEEK_SET ) ) + return 0; + if ( n > size() ) + return eof_error; + return "Error seeking in file"; +} + +void Std_File_Reader::close() +{ + if ( file_ ) + { + fclose( (FILE*) file_ ); + file_ = 0; + } +} + // Gzip_File_Reader #ifdef HAVE_ZLIB_H @@ -254,11 +255,11 @@ return "Couldn't open file"; unsigned char buf [4]; - if ( fread( buf, 2, 1, file ) == 1 && buf [0] == 0x1F && buf [1] == 0x8B ) + if ( fread( buf, 2, 1, file ) > 0 && buf [0] == 0x1F && buf [1] == 0x8B ) { fseek( file, -4, SEEK_END ); fread( buf, 4, 1, file ); - *eof = buf [3] * 0x1000000L + buf [2] * 0x10000L + buf [1] * 0x100L + buf [0]; + *eof = get_le32( buf ); } else { @@ -274,13 +275,11 @@ Gzip_File_Reader::~Gzip_File_Reader() { close(); } -error_t Gzip_File_Reader::open( const char* path ) +blargg_err_t Gzip_File_Reader::open( const char* path ) { close(); - error_t err = get_gzip_eof( path, &size_ ); - if ( err ) - return err; + RETURN_ERR( get_gzip_eof( path, &size_ ) ); file_ = gzopen( path, "rb" ); if ( !file_ ) @@ -295,7 +294,7 @@ long Gzip_File_Reader::tell() const { return gztell( file_ ); } -error_t Gzip_File_Reader::seek( long n ) +blargg_err_t Gzip_File_Reader::seek( long n ) { if ( gzseek( file_, n, SEEK_SET ) >= 0 ) return 0;
--- a/src/console/Data_Reader.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Data_Reader.h Thu Dec 07 15:20:41 2006 -0800 @@ -4,34 +4,30 @@ #ifndef DATA_READER_H #define DATA_READER_H -#undef DATA_READER_H -// allow blargg_config.h to #include Data_Reader.h -#include "blargg_config.h" -#ifndef DATA_READER_H -#define DATA_READER_H +#include "blargg_common.h" // Supports reading and finding out how many bytes are remaining class Data_Reader { public: - Data_Reader() { } virtual ~Data_Reader() { } static const char eof_error []; // returned by read() when request goes beyond end - typedef const char* error_t; // NULL if successful - // Read at most count bytes and return number actually read, or <= 0 if error virtual long read_avail( void*, long n ) = 0; // Read exactly count bytes and return error if they couldn't be read - virtual error_t read( void*, long count ); + virtual blargg_err_t read( void*, long count ); // Number of bytes remaining until end of file virtual long remain() const = 0; // Read and discard count bytes - virtual error_t skip( long count ); + virtual blargg_err_t skip( long count ); +public: + Data_Reader() { } + typedef blargg_err_t error_t; // deprecated private: // noncopyable Data_Reader( const Data_Reader& ); @@ -48,26 +44,26 @@ virtual long tell() const = 0; // Go to new position - virtual error_t seek( long ) = 0; + virtual blargg_err_t seek( long ) = 0; long remain() const; - error_t skip( long n ); + blargg_err_t skip( long n ); }; // Disk file reader class Std_File_Reader : public File_Reader { public: - error_t open( const char* path ); + blargg_err_t open( const char* path ); void close(); public: Std_File_Reader(); ~Std_File_Reader(); long size() const; - error_t read( void*, long ); + blargg_err_t read( void*, long ); long read_avail( void*, long ); long tell() const; - error_t seek( long ); + blargg_err_t seek( long ); private: void* file_; }; @@ -81,7 +77,7 @@ long size() const; long read_avail( void*, long ); long tell() const; - error_t seek( long ); + blargg_err_t seek( long ); private: const char* const begin; const long size_; @@ -109,7 +105,7 @@ public: long remain() const; long read_avail( void*, long ); - error_t read( void*, long ); + blargg_err_t read( void*, long ); private: char const* header; char const* header_end; @@ -120,11 +116,11 @@ // Invokes callback function to read data. Size of data must be specified in advance. class Callback_Reader : public Data_Reader { public: - typedef error_t (*callback_t)( void* data, void* out, long count ); + typedef const char* (*callback_t)( void* data, void* out, long count ); Callback_Reader( callback_t, long size, void* data = 0 ); public: long read_avail( void*, long ); - error_t read( void*, long ); + blargg_err_t read( void*, long ); long remain() const; private: callback_t const callback; @@ -136,7 +132,7 @@ // Gzip compressed file reader class Gzip_File_Reader : public File_Reader { public: - error_t open( const char* path ); + blargg_err_t open( const char* path ); void close(); public: @@ -145,7 +141,7 @@ long size() const; long read_avail( void*, long ); long tell() const; - error_t seek( long ); + blargg_err_t seek( long ); private: void* file_; long size_; @@ -153,4 +149,3 @@ #endif #endif -#endif
--- a/src/console/Dual_Resampler.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Dual_Resampler.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Dual_Resampler.h"
--- a/src/console/Dual_Resampler.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Dual_Resampler.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Combination of Fir_Resampler and Blip_Buffer mixing. Used by Sega FM emulators. -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef DUAL_RESAMPLER_H #define DUAL_RESAMPLER_H
--- a/src/console/Effects_Buffer.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Effects_Buffer.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Effects_Buffer.h"
--- a/src/console/Effects_Buffer.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Effects_Buffer.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Multi-channel effects buffer with panning, echo and reverb -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef EFFECTS_BUFFER_H #define EFFECTS_BUFFER_H
--- a/src/console/Fir_Resampler.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Fir_Resampler.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Fir_Resampler.h" @@ -57,19 +57,19 @@ write_offset( width * stereo - stereo ), impulses( impulses_ ) { - write_pos = NULL; - res = 1; - imp = 0; + write_pos = 0; + res = 1; + imp_phase = 0; skip_bits = 0; - step = stereo; - ratio_ = 1.0; + step = stereo; + ratio_ = 1.0; } Fir_Resampler_::~Fir_Resampler_() { } void Fir_Resampler_::clear() { - imp = 0; + imp_phase = 0; if ( buf.size() ) { write_pos = &buf [write_offset]; @@ -142,8 +142,8 @@ { blargg_long input_count = 0; - unsigned long skip = skip_bits >> imp; - int remain = res - imp; + unsigned long skip = skip_bits >> imp_phase; + int remain = res - imp_phase; while ( (output_count -= 2) > 0 ) { input_count += step + (skip & 1) * stereo; @@ -168,8 +168,8 @@ int output_count = cycle_count * res * stereo; input_count -= cycle_count * input_per_cycle; - blargg_ulong skip = skip_bits >> imp; - int remain = res - imp; + blargg_ulong skip = skip_bits >> imp_phase; + int remain = res - imp_phase; while ( input_count >= 0 ) { input_count -= step + (skip & 1) * stereo; @@ -187,10 +187,10 @@ int Fir_Resampler_::skip_input( long count ) { int remain = write_pos - buf.begin(); - int avail = remain - width_ * stereo; - if ( count > avail ) - count = avail; - + int max_count = remain - width_ * stereo; + if ( count > max_count ) + count = max_count; + remain -= count; write_pos = &buf [remain]; memmove( buf.begin(), &buf [count], remain * sizeof buf [0] );
--- a/src/console/Fir_Resampler.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Fir_Resampler.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Finite impulse response (FIR) resampler with adjustable FIR size -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef FIR_RESAMPLER_H #define FIR_RESAMPLER_H @@ -61,7 +61,7 @@ blargg_vector<sample_t> buf; sample_t* write_pos; int res; - int imp; + int imp_phase; int const width_; int const write_offset; blargg_ulong skip_bits; @@ -102,9 +102,9 @@ sample_t* out = out_begin; const sample_t* in = buf.begin(); sample_t* end_pos = write_pos; - blargg_ulong skip = skip_bits >> this->imp; - sample_t const* imp = impulses [this->imp]; - int remain = res - this->imp; + blargg_ulong skip = skip_bits >> imp_phase; + sample_t const* imp = impulses [imp_phase]; + int remain = res - imp_phase; int const step = this->step; count >>= 1; @@ -159,7 +159,7 @@ while ( in <= end_pos ); } - this->imp = res - remain; + imp_phase = res - remain; int left = write_pos - in; write_pos = &buf [left];
--- a/src/console/Gb_Cpu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Gb_Cpu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Gb_Cpu.h" @@ -96,6 +96,8 @@ this->state = &s; memcpy( &s, &this->state_, sizeof s ); + typedef BOOST::uint16_t uint16_t; + #if BLARGG_BIG_ENDIAN #define R8( n ) (r8_ [n]) #elif BLARGG_LITTLE_ENDIAN @@ -156,13 +158,14 @@ switch ( op ) { -#define BRANCH( cond ) \ -{ \ - pc++; \ - int offset = (BOOST::int8_t) data; \ - if ( !(cond) ) goto loop; \ - pc += offset; \ - goto loop; \ +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH( cond )\ +{\ + pc++;\ + int offset = (BOOST::int8_t) data;\ + if ( !(cond) ) goto loop;\ + pc = uint16_t (pc + offset);\ + goto loop;\ } // Most Common
--- a/src/console/Gb_Cpu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Gb_Cpu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,7 +1,7 @@ // Nintendo Game Boy CPU emulator // Treats every instruction as taking 4 cycles -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef GB_CPU_H #define GB_CPU_H
--- a/src/console/Gb_Oscs.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Gb_Oscs.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -129,11 +129,13 @@ playing = false; } - int delta = amp - last_amp; - if ( delta ) { - last_amp = amp; - synth->offset( time, delta, output ); + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } } time += delay; @@ -173,13 +175,15 @@ if ( bits >> tap & 2 ) amp = -amp; - int delta = amp - last_amp; - if ( delta ) { - last_amp = amp; - synth->offset( time, delta, output ); + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } } - + time += delay; if ( !playing ) time = end_time; @@ -251,20 +255,23 @@ void Gb_Wave::run( blip_time_t time, blip_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 + int frequency; { - amp = 30 >> volume_shift & playing; - playing = false; - } - - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); + int amp = (wave [wave_pos] >> volume_shift & playing) * 2; + + 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;
--- a/src/console/Gbs_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Gbs_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Gbs_Emu.h" @@ -81,7 +81,7 @@ blargg_err_t load_( Data_Reader& in ) { - blargg_err_t err = in.read( &h, sizeof h ); + blargg_err_t err = in.read( &h, Gbs_Emu::header_size ); if ( err ) return (err == in.eof_error ? gme_wrong_file_type : err); @@ -105,8 +105,8 @@ blargg_err_t Gbs_Emu::load_( Data_Reader& in ) { - unload(); - RETURN_ERR( rom.load( in, sizeof header_, &header_, 0 ) ); + assert( offsetof (header_t,copyright [32]) == header_size ); + RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); set_track_count( header_.track_count ); RETURN_ERR( check_gbs_header( &header_ ) ); @@ -259,6 +259,8 @@ cpu_time = next_play; next_play += play_period; cpu_jsr( get_le16( header_.play_addr ) ); + GME_FRAME_HOOK( this ); + // TODO: handle timer rates different than 60 Hz } else if ( cpu::r.pc > 0xFFFF ) {
--- a/src/console/Gbs_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Gbs_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Nintendo Game Boy GBS music file emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef GBS_EMU_H #define GBS_EMU_H @@ -16,6 +16,7 @@ static equalizer_t const headphones_eq; // GBS file header + enum { header_size = 112 }; struct header_t { char tag [3]; @@ -32,7 +33,6 @@ char author [32]; char copyright [32]; }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 112 ); // Header for currently loaded file header_t const& header() const { return header_; }
--- a/src/console/Gme_File.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Gme_File.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Gme_File.h" @@ -37,12 +37,18 @@ Gme_File::Gme_File() { - type_ = 0; + type_ = 0; + user_data_ = 0; + user_cleanup_ = 0; unload(); // clears fields blargg_verify_byte_order(); // used by most emulator types, so save them the trouble } -Gme_File::~Gme_File() { } +Gme_File::~Gme_File() +{ + if ( user_cleanup_ ) + user_cleanup_( user_data_ ); +} blargg_err_t Gme_File::load_mem_( byte const* data, long size ) { @@ -145,7 +151,7 @@ copy_field_( out, in, max_field_ ); } -blargg_err_t Gme_File::remap_track( int* track_io ) const +blargg_err_t Gme_File::remap_track_( int* track_io ) const { if ( (unsigned) *track_io >= (unsigned) track_count() ) return "Invalid track"; @@ -188,7 +194,7 @@ copy_field_( out->system, type()->system ); int remapped = track; - RETURN_ERR( remap_track( &remapped ) ); + RETURN_ERR( remap_track_( &remapped ) ); RETURN_ERR( track_info_( out, remapped ) ); // override with m3u info
--- a/src/console/Gme_File.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Gme_File.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Common interface to game music file loading and information -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef GME_FILE_H #define GME_FILE_H @@ -55,6 +55,17 @@ // See gme.h for definition of struct track_info_t. blargg_err_t track_info( track_info_t* out, int track ) const; +// User data/cleanup + + // Set/get pointer to data you want to associate with this emulator. + // You can use this for whatever you want. + void set_user_data( void* p ) { user_data_ = p; } + void* user_data() const { return user_data_; } + + // Register cleanup function to be called when deleting emulator, or NULL to + // clear it. Passes user_data to cleanup function. + void set_user_cleanup( gme_user_cleanup_t func ) { user_cleanup_ = func; } + public: // deprecated int error_count() const; // use warning() @@ -79,8 +90,8 @@ virtual void post_load_(); virtual void clear_playlist_() { } -protected: - blargg_err_t remap_track( int* track_io ) const; // need by Music_Emu +public: + blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu private: // noncopyable Gme_File( const Gme_File& ); @@ -90,7 +101,10 @@ int track_count_; int raw_track_count_; const char* warning_; + void* user_data_; + gme_user_cleanup_t user_cleanup_; M3u_Playlist playlist; + char playlist_warning [64]; blargg_vector<byte> file_data; // only if loaded into memory using default load blargg_err_t load_m3u_( blargg_err_t ); @@ -113,6 +127,8 @@ #else #define GME_FILE_READER Std_File_Reader #endif +#elif defined (GME_FILE_READER_INCLUDE) + #include GME_FILE_READER_INCLUDE #endif inline gme_type_t Gme_File::type() const { return type_; }
--- a/src/console/Gym_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Gym_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Gym_Emu.h" @@ -117,16 +117,16 @@ if ( memcmp( in, "GYMX", 4 ) == 0 ) { - if ( size < (long) sizeof (Gym_Emu::header_t) + 1 ) + if ( size < Gym_Emu::header_size + 1 ) return gme_wrong_file_type; if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 ) return "Packed GYM file not supported"; if ( data_offset ) - *data_offset = sizeof (Gym_Emu::header_t); + *data_offset = Gym_Emu::header_size; } - else if ( *in != 0 && *in != 1 ) + else if ( *in > 3 ) { return gme_wrong_file_type; } @@ -209,6 +209,7 @@ blargg_err_t Gym_Emu::load_mem_( byte const* in, long size ) { + assert( offsetof (header_t,packed [4]) == header_size ); int offset = 0; RETURN_ERR( check_header( in, size, &offset ) ); set_voice_count( 8 );
--- a/src/console/Gym_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Gym_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,7 +1,7 @@ // Sega Genesis/Mega Drive GYM music file emulator // Includes with PCM timing recovery to improve sample quality. -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef GYM_EMU_H #define GYM_EMU_H @@ -13,6 +13,7 @@ class Gym_Emu : public Music_Emu, private Dual_Resampler { public: // GYM file header + enum { header_size = 428 }; struct header_t { char tag [4]; @@ -25,7 +26,6 @@ byte loop_start [4]; // in 1/60 seconds, 0 if not looped byte packed [4]; }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 428 ); // Header for currently loaded file header_t const& header() const { return header_; }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/console/Gzip_Reader.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -0,0 +1,108 @@ +// File_Extractor 0.4.0. http://www.slack.net/~ant/ + +#include "Gzip_Reader.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void Gzip_Reader::close() +{ + in = 0; + tell_ = 0; + size_ = 0; + inflater.end(); +} + +Gzip_Reader::Gzip_Reader() { close(); } + +Gzip_Reader::~Gzip_Reader() { } + +static blargg_err_t gzip_reader_read( void* file, void* out, long* count ) +{ + *count = ((File_Reader*) file)->read_avail( out, *count ); + return (*count < 0 ? "Read error" : 0); +} + +blargg_err_t Gzip_Reader::open( File_Reader* new_in ) +{ + close(); + + RETURN_ERR( inflater.begin( inflater.mode_auto, gzip_reader_read, new_in ) ); + + size_ = -1; // defer seeking to end of file until size is actually needed + in = new_in; + return 0; +} + +blargg_err_t Gzip_Reader::calc_size() +{ + long size = in->size(); + if ( inflater.deflated() ) + { + byte trailer [4]; + long pos = in->tell(); + RETURN_ERR( in->seek( size - sizeof trailer ) ); + RETURN_ERR( in->read( trailer, sizeof trailer ) ); + RETURN_ERR( in->seek( pos ) ); + size = get_le32( trailer ); + } + size_ = size; + return 0; +} + +long Gzip_Reader::remain() const +{ + if ( size_ < 0 ) + { + if ( !in ) + return 0; + + // need to cast away constness to change cached value + if ( ((Gzip_Reader*) this)->calc_size() ) + return -1; + } + return size_ - tell_; +} + +blargg_err_t Gzip_Reader::read_( void* out, long* count ) +{ + blargg_err_t err = inflater.read( out, count, gzip_reader_read, in ); + tell_ += *count; + if ( size_ >= 0 && tell_ > size_ ) + { + tell_ = size_; + return "Corrupt gzip file"; + } + return err; +} + +blargg_err_t Gzip_Reader::read( void* out, long count ) +{ + if ( in ) + { + long actual = count; + RETURN_ERR( read_( out, &actual ) ); + if ( actual == count ) + return 0; + } + return eof_error; +} + +long Gzip_Reader::read_avail( void* out, long count ) +{ + if ( !in || read_( out, &count ) ) + count = -1; + return count; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/console/Gzip_Reader.h Thu Dec 07 15:20:41 2006 -0800 @@ -0,0 +1,31 @@ +// Transparently decompresses gzip files, as well as uncompressed + +// File_Extractor 0.4.0 +#ifndef GZIP_READER_H +#define GZIP_READER_H + +#include "Data_Reader.h" +#include "Zlib_Inflater.h" + +class Gzip_Reader : public Data_Reader { +public: + error_t open( File_Reader* ); + void close(); + +public: + Gzip_Reader(); + ~Gzip_Reader(); + long remain() const; + error_t read( void*, long ); + long read_avail( void*, long ); +private: + File_Reader* in; + long tell_; + long size_; + Zlib_Inflater inflater; + + error_t calc_size(); + blargg_err_t read_( void* out, long* count ); +}; + +#endif
--- a/src/console/Hes_Apu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Hes_Apu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Hes_Apu.h"
--- a/src/console/Hes_Cpu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Hes_Cpu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Hes_Cpu.h" @@ -227,12 +227,13 @@ // Branch +// TODO: more efficient way to handle negative branch that wraps PC around #define BRANCH( cond )\ {\ fint16 offset = (BOOST::int8_t) data;\ pc++;\ if ( !(cond) ) goto branch_not_taken;\ - pc += offset;\ + pc = BOOST::uint16_t (pc + offset);\ goto loop;\ }
--- a/src/console/Hes_Cpu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Hes_Cpu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // PC Engine CPU emulator for use with HES music files -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef HES_CPU_H #define HES_CPU_H
--- a/src/console/Hes_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Hes_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Hes_Emu.h" @@ -105,8 +105,8 @@ struct Hes_File : Gme_Info_ { - struct { - Hes_Emu::header_t h; + struct header_t { + char header [Hes_Emu::header_size]; char unused [0x20]; byte fields [0x30 * 3]; } h; @@ -115,6 +115,7 @@ blargg_err_t load_( Data_Reader& in ) { + assert( offsetof (header_t,fields) == Hes_Emu::header_size + 0x20 ); blargg_err_t err = in.read( &h, sizeof h ); if ( err ) return (err == in.eof_error ? gme_wrong_file_type : err); @@ -137,7 +138,8 @@ blargg_err_t Hes_Emu::load_( Data_Reader& in ) { - RETURN_ERR( rom.load( in, sizeof header_, &header_, unmapped ) ); + assert( offsetof (header_t,unused [4]) == header_size ); + RETURN_ERR( rom.load( in, header_size, &header_, unmapped ) ); RETURN_ERR( check_hes_header( header_.tag ) ); @@ -243,6 +245,7 @@ r.a = track; recalc_timer_load(); + last_frame_hook = 0; return 0; } @@ -282,6 +285,7 @@ { if ( unsigned (addr - apu.start_addr) <= apu.end_addr - apu.start_addr ) { + GME_APU_HOOK( this, addr - apu.start_addr, data ); // avoid going way past end when a long block xfer is writing to I/O space hes_time_t t = min( time(), end_time() + 8 ); apu.write_data( t, addr, data ); @@ -458,6 +462,17 @@ timer.fired = true; irq.timer = future_hes_time; irq_changed(); // overkill, but not worth writing custom code + #if GME_FRAME_HOOK_DEFINED + { + unsigned const threshold = period_60hz / 30; + unsigned long elapsed = present - last_frame_hook; + if ( elapsed - period_60hz + threshold / 2 < threshold ) + { + last_frame_hook = present; + GME_FRAME_HOOK( this ); + } + } + #endif return 0x0A; } @@ -467,6 +482,10 @@ //run_until( present ); //irq.vdp = future_hes_time; //irq_changed(); + #if GME_FRAME_HOOK_DEFINED + last_frame_hook = present; + GME_FRAME_HOOK( this ); + #endif return 0x08; } } @@ -498,6 +517,9 @@ // end time frame timer.last_time -= duration; vdp.next_vbl -= duration; + #if GME_FRAME_HOOK_DEFINED + last_frame_hook -= duration; + #endif cpu::end_frame( duration ); ::adjust_time( irq.timer, duration ); ::adjust_time( irq.vdp, duration );
--- a/src/console/Hes_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Hes_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // TurboGrafx-16/PC Engine HES music file emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef HES_EMU_H #define HES_EMU_H @@ -12,6 +12,7 @@ typedef Hes_Cpu cpu; public: // HES file header + enum { header_size = 0x20 }; struct header_t { byte tag [4]; @@ -24,7 +25,6 @@ byte addr [4]; byte unused [4]; }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 0x20 ); // Header for currently loaded file header_t const& header() const { return header_; } @@ -57,6 +57,7 @@ Rom_Data<page_size> rom; header_t header_; hes_time_t play_period; + hes_time_t last_frame_hook; int timer_base; struct {
--- a/src/console/Kss_Cpu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Kss_Cpu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,8 +1,10 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ -// Last validated with zexall 2006.11.14 2:19 PM -// Doesn't implement interrupts or the R register, though both would be -// easy to support. +/* +Last validated with zexall 2006.11.14 2:19 PM +* Doesn't implement the R register or immediate interrupt after EI. +* Address wrap-around isn't completely correct, but is prevented from crashing emulator. +*/ #include "Kss_Cpu.h" @@ -27,13 +29,13 @@ // Callbacks to emulator -#define CPU_OUT( cpu, addr, data, time ) \ +#define CPU_OUT( cpu, addr, data, time )\ kss_cpu_out( this, time, addr, data ) -#define CPU_IN( cpu, addr, time ) \ +#define CPU_IN( cpu, addr, time )\ kss_cpu_in( this, time, addr ) -#define CPU_WRITE( cpu, addr, data, time ) \ +#define CPU_WRITE( cpu, addr, data, time )\ (SYNC_TIME(), kss_cpu_write( this, addr, data )) #include "blargg_source.h" @@ -59,7 +61,7 @@ Kss_Cpu::Kss_Cpu() { - state = &state_; + state = &state_; for ( int i = 0x100; --i >= 0; ) { @@ -130,8 +132,8 @@ //#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)]) -// help compiler see that it can adjust stack offset -#define R16( n, shift, offset ) \ +// help compiler see that it can just adjust stack offset, saving an extra instruction +#define R16( n, shift, offset )\ (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) #define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e @@ -311,12 +313,13 @@ #define MINUS (flags & S80) // JR +// TODO: more efficient way to handle negative branch that wraps PC around #define JR( cond ) {\ - int disp = (BOOST::int8_t) data;\ + int offset = (BOOST::int8_t) data;\ pc++;\ if ( !(cond) )\ goto jr_not_taken;\ - pc += disp;\ + pc = uint16_t (pc + offset);\ goto loop;\ } @@ -1340,6 +1343,10 @@ s_time += ed_dd_timing [data] & 0x0F; switch ( data ) { + // TODO: more efficient way of avoid negative address + // TODO: avoid using this as argument to READ() since it is evaluated twice + #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) + #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; // ADD/ADC/SUB/SBC @@ -1351,7 +1358,7 @@ case 0x8E: // ADC (IXY+disp) pc++; opcode = data; - data = READ( ixy + (int8_t) data2 ); + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto adc_data; case 0x94: // SUB HXY @@ -1373,26 +1380,26 @@ goto adc_data; { - unsigned data2; + unsigned temp; case 0x39: // ADD IXY,SP - data2 = sp; + temp = sp; goto add_ixy_data; case 0x29: // ADD IXY,HL - data2 = ixy; + temp = ixy; goto add_ixy_data; case 0x09: // ADD IXY,BC case 0x19: // ADD IXY,DE - data2 = R16( data, 4, 0x09 ); + temp = R16( data, 4, 0x09 ); add_ixy_data: { - blargg_ulong sum = ixy + data2; - data2 ^= ixy; + blargg_ulong sum = ixy + temp; + temp ^= ixy; ixy = (uint16_t) sum; flags = (flags & (S80 | Z40 | V04)) | (sum >> 16) | (sum >> 8 & (F20 | F08)) | - ((data2 ^ sum) >> 8 & H10); + ((temp ^ sum) >> 8 & H10); goto set_ixy; } } @@ -1400,7 +1407,7 @@ // AND case 0xA6: // AND (IXY+disp) pc++; - data = READ( ixy + (int8_t) data2 ); + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto and_data; case 0xA4: // AND HXY @@ -1414,7 +1421,7 @@ // OR case 0xB6: // OR (IXY+disp) pc++; - data = READ( ixy + (int8_t) data2 ); + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto or_data; case 0xB4: // OR HXY @@ -1428,7 +1435,7 @@ // XOR case 0xAE: // XOR (IXY+disp) pc++; - data = READ( ixy + (int8_t) data2 ); + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto xor_data; case 0xAC: // XOR HXY @@ -1442,7 +1449,7 @@ // CP case 0xBE: // CP (IXY+disp) pc++; - data = READ( ixy + (int8_t) data2 ); + data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto cp_data; case 0xBC: // CP HXY @@ -1460,7 +1467,7 @@ case 0x36: // LD (IXY+disp),imm pc++, data = READ_PROG( pc ); pc++; - WRITE( ixy + (int8_t) data2, data ); + WRITE( IXY_DISP( ixy, (int8_t) data2 ), data ); goto loop; CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY @@ -1477,7 +1484,7 @@ CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) pc++; - R8( data >> 3, 8 ) = READ( ixy + (int8_t) data2 ); + R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto loop; case 0x26: // LD HXY,imm @@ -1540,7 +1547,7 @@ // DD/FD CB prefix case 0xCB: { - data = ixy + (int8_t) data2; + data = IXY_DISP( ixy, (int8_t) data2 ); pc++; data2 = READ_PROG( pc ); pc++; @@ -1593,14 +1600,14 @@ goto set_ixy; case 0x34: // INC (IXY+disp) - ixy += (int8_t) data2; + ixy = IXY_DISP( ixy, (int8_t) data2 ); pc++; data = READ( ixy ) + 1; WRITE( ixy, data ); goto inc_set_flags; case 0x35: // DEC (IXY+disp) - ixy += (int8_t) data2; + ixy = IXY_DISP( ixy, (int8_t) data2 ); pc++; data = READ( ixy ) - 1; WRITE( ixy, data );
--- a/src/console/Kss_Cpu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Kss_Cpu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Z80 CPU emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef KSS_CPU_H #define KSS_CPU_H
--- a/src/console/Kss_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Kss_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Kss_Emu.h" @@ -37,6 +37,8 @@ wave_type | 3, wave_type | 4, wave_type | 5, wave_type | 6, wave_type | 7 }; set_voice_types( types ); + + memset( unmapped_read, 0xFF, sizeof unmapped_read ); } Kss_Emu::~Kss_Emu() { unload(); } @@ -54,7 +56,11 @@ { const char* system = "MSX"; if ( h.device_flags & 0x02 ) + { system = "Sega Master System"; + if ( h.device_flags & 0x04 ) + system = "Game Gear"; + } Gme_File::copy_field_( out->system, system ); } @@ -79,10 +85,9 @@ blargg_err_t load_( Data_Reader& in ) { - long file_size = in.remain(); - if ( file_size < (long) sizeof (Kss_Emu::composite_header_t) ) - return gme_wrong_file_type; - RETURN_ERR( in.read( &header_, sizeof header_ ) ); + blargg_err_t err = in.read( &header_, Kss_Emu::header_size ); + if ( err ) + return (err == in.eof_error ? gme_wrong_file_type : err); return check_kss_header( &header_ ); } @@ -114,7 +119,9 @@ blargg_err_t Kss_Emu::load_( Data_Reader& in ) { memset( &header_, 0, sizeof header_ ); - RETURN_ERR( rom.load( in, sizeof (header_t), STATIC_CAST(header_t*,&header_), 0 ) ); + assert( offsetof (header_t,device_flags) == header_size - 1 ); + assert( offsetof (ext_header_t,msx_audio_vol) == ext_header_size - 1 ); + RETURN_ERR( rom.load( in, header_size, STATIC_CAST(header_t*,&header_), 0 ) ); RETURN_ERR( check_kss_header( header_.tag ) ); @@ -134,7 +141,7 @@ else { ext_header_t& ext = header_; - memcpy( &ext, rom.begin(), min( (int) sizeof ext, (int) header_.extra_header ) ); + memcpy( &ext, rom.begin(), min( (int) ext_header_size, (int) header_.extra_header ) ); if ( header_.extra_header > 0x10 ) set_warning( "Unknown data in header" ); } @@ -226,7 +233,7 @@ //dprintf( "bank_count: %d (%d claimed)\n", bank_count, header_.bank_mode & 0x7F ); ram [idle_addr] = 0xFF; - cpu::reset( ram, ram ); + cpu::reset( unmapped_write, unmapped_read ); cpu::map_mem( 0, mem_size, ram, ram ); ay.reset(); @@ -242,6 +249,7 @@ scc_accessed = false; gain_updated = false; update_gain(); + ay_latch = 0; return 0; } @@ -265,7 +273,7 @@ long phys = physical * (blargg_long) bank_size; for ( unsigned offset = 0; offset < bank_size; offset += page_size ) cpu::map_mem( addr + offset, page_size, - unmapped_write(), rom.at_addr( phys + offset ) ); + unmapped_write, rom.at_addr( phys + offset ) ); } } @@ -312,6 +320,7 @@ return; case 0xA1: + GME_APU_HOOK( &emu, emu.ay_latch, data ); emu.ay.write( time, emu.ay_latch, data ); return; @@ -327,6 +336,7 @@ case 0x7F: if ( emu.sn ) { + GME_APU_HOOK( &emu, 16, data ); emu.sn->write_data( time, data ); return; } @@ -386,6 +396,7 @@ ram [--r.sp] = idle_addr >> 8; ram [--r.sp] = idle_addr & 0xFF; r.pc = get_le16( header_.play_addr ); + GME_FRAME_HOOK( this ); } } }
--- a/src/console/Kss_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Kss_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // MSX computer KSS music file emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef KSS_EMU_H #define KSS_EMU_H @@ -14,6 +14,7 @@ typedef Kss_Cpu cpu; public: // KSS file header + enum { header_size = 0x10 }; struct header_t { byte tag [4]; @@ -26,8 +27,8 @@ byte extra_header; byte device_flags; }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 0x10 ); + enum { ext_header_size = 0x10 }; struct ext_header_t { byte data_size [4]; @@ -39,7 +40,6 @@ byte msx_music_vol; byte msx_audio_vol; }; - BOOST_STATIC_ASSERT( sizeof (ext_header_t) == 0x10 ); struct composite_header_t : header_t, ext_header_t { }; @@ -62,7 +62,6 @@ private: Rom_Data<page_size> rom; composite_header_t header_; - byte* unmapped_write() { return rom.unmapped(); } bool scc_accessed; bool gain_updated; @@ -90,6 +89,8 @@ Ay_Apu ay; Scc_Apu scc; Sms_Apu* sn; + byte unmapped_read [0x100]; + byte unmapped_write [page_size]; }; #endif
--- a/src/console/Kss_Scc_Apu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Kss_Scc_Apu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Kss_Scc_Apu.h" @@ -46,12 +46,14 @@ BOOST::int8_t const* wave = (BOOST::int8_t*) regs + index * wave_size; if ( index == osc_count - 1 ) wave -= wave_size; // last two oscs share wave - int amp = wave [osc.phase] * volume; - int delta = amp - osc.last_amp; - if ( delta ) { - osc.last_amp = amp; - synth.offset( last_time, delta, output ); + int amp = wave [osc.phase] * volume; + int delta = amp - osc.last_amp; + if ( delta ) + { + osc.last_amp = amp; + synth.offset( last_time, delta, output ); + } } blip_time_t time = last_time + osc.delay;
--- a/src/console/Kss_Scc_Apu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Kss_Scc_Apu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Konami SCC sound chip emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef KSS_SCC_APU_H #define KSS_SCC_APU_H @@ -97,13 +97,8 @@ { last_time = 0; - osc_t* osc = &oscs [osc_count]; - do - { - osc--; - memset( osc, 0, offsetof (osc_t,output) ); - } - while ( osc != oscs ); + for ( int i = 0; i < osc_count; i++ ) + memset( &oscs [i], 0, offsetof (osc_t,output) ); memset( regs, 0, sizeof regs ); }
--- a/src/console/M3u_Playlist.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/M3u_Playlist.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "M3u_Playlist.h" #include "Music_Emu.h" @@ -22,9 +22,29 @@ blargg_err_t Gme_File::load_m3u_( blargg_err_t err ) { - if ( !err && playlist.size() ) - track_count_ = playlist.size(); require( raw_track_count_ ); // file must be loaded first + + if ( !err ) + { + if ( playlist.size() ) + track_count_ = playlist.size(); + + int line = playlist.first_error(); + if ( line ) + { + // avoid using bloated printf() + char* out = &playlist_warning [sizeof playlist_warning]; + *--out = 0; + do { + *--out = line % 10 + '0'; + } while ( (line /= 10) > 0 ); + + static const char str [] = "Problem in m3u at line "; + out -= sizeof str - 1; + memcpy( out, str, sizeof str - 1 ); + set_warning( out ); + } + } return err; } @@ -32,9 +52,9 @@ blargg_err_t Gme_File::load_m3u( Data_Reader& in ) { return load_m3u_( playlist.load( in ) ); } -const char* gme_load_m3u( Music_Emu* me, const char* path ) { return me->load_m3u( path ); } +gme_err_t gme_load_m3u( Music_Emu* me, const char* path ) { return me->load_m3u( path ); } -const char* gme_load_m3u_data( Music_Emu* me, const void* data, long size ) +gme_err_t gme_load_m3u_data( Music_Emu* me, const void* data, long size ) { Mem_File_Reader in( data, size ); return me->load_m3u( in );
--- a/src/console/M3u_Playlist.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/M3u_Playlist.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // M3U playlist file parser, with support for subtrack information -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef M3U_PLAYLIST_H #define M3U_PLAYLIST_H
--- a/src/console/Makefile Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Makefile Thu Dec 07 15:20:41 2006 -0800 @@ -23,8 +23,8 @@ Gb_Oscs.cxx \ gme.cxx \ Gme_File.cxx \ - gme_type_list.cxx \ Gym_Emu.cxx \ + Gzip_Reader.cxx \ Hes_Apu.cxx \ Hes_Cpu.cxx \ Hes_Emu.cxx \ @@ -54,7 +54,8 @@ Vgm_Emu.cxx \ Vgm_Emu_Impl.cxx \ Ym2413_Emu.cxx \ - Ym2612_Emu.cxx + Ym2612_Emu.cxx \ + Zlib_Inflater.cxx OBJECTS = ${SOURCES:.cxx=.o}
--- a/src/console/Music_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Music_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Music_Emu.h" @@ -134,7 +134,7 @@ clear_track_vars(); int remapped = track; - RETURN_ERR( remap_track( &remapped ) ); + RETURN_ERR( remap_track_( &remapped ) ); current_track_ = track; RETURN_ERR( start_track_( remapped ) );
--- a/src/console/Music_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Music_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,10 +1,9 @@ // Common interface to game music file emulators -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef MUSIC_EMU_H #define MUSIC_EMU_H -#include "blargg_common.h" #include "Gme_File.h" class Multi_Buffer;
--- a/src/console/Nes_Cpu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Nes_Cpu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Nes_Cpu.h" @@ -22,8 +22,8 @@ #include BLARGG_ENABLE_OPTIMIZER #endif -#define SYNC_TIME() (void) (s.time = s_time) -#define RELOAD_TIME() (void) (s_time = s.time) +#define FLUSH_TIME() (void) (s.time = s_time) +#define CACHE_TIME() (void) (s_time = s.time) #include "nes_cpu_io.h" @@ -36,9 +36,9 @@ #ifndef CPU_READ_PPU #define CPU_READ_PPU( cpu, addr, out, time )\ {\ - SYNC_TIME();\ + FLUSH_TIME();\ out = CPU_READ( cpu, addr, time );\ - RELOAD_TIME();\ + CACHE_TIME();\ } #endif @@ -138,18 +138,18 @@ // status flags #define IS_NEG (nz & 0x8080) - #define CALC_STATUS( out ) do { \ - out = status & (st_v | st_d | st_i); \ - out |= ((nz >> 8) | nz) & st_n; \ - out |= c >> 8 & st_c; \ - if ( !(nz & 0xFF) ) out |= st_z; \ + #define CALC_STATUS( out ) do {\ + out = status & (st_v | st_d | st_i);\ + out |= ((nz >> 8) | nz) & st_n;\ + out |= c >> 8 & st_c;\ + if ( !(nz & 0xFF) ) out |= st_z;\ } while ( 0 ) - #define SET_STATUS( in ) do { \ - status = in & (st_v | st_d | st_i); \ - nz = in << 8; \ - c = nz; \ - nz |= ~in & st_z; \ + #define SET_STATUS( in ) do {\ + status = in & (st_v | st_d | st_i);\ + nz = in << 8;\ + c = nz;\ + nz |= ~in & st_z;\ } while ( 0 ) fuint8 status; @@ -166,6 +166,7 @@ loop: check( (unsigned) GET_SP() < 0x100 ); + check( (unsigned) pc < 0x10000 ); check( (unsigned) a < 0x100 ); check( (unsigned) x < 0x100 ); check( (unsigned) y < 0x100 ); @@ -237,7 +238,7 @@ // Macros #define GET_MSB() (instr [1]) -#define ADD_PAGE (pc++, data += 0x100 * GET_MSB()); +#define ADD_PAGE() (pc++, data += 0x100 * GET_MSB()) #define GET_ADDR() GET_LE16( instr ) #define NO_PAGE_CROSSING( lsb ) @@ -245,53 +246,54 @@ #define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; -#define IND_Y( cross, out ) { \ - fuint16 temp = READ_LOW( data ) + y; \ - out = temp + 0x100 * READ_LOW( uint8_t (data + 1) ); \ - cross( temp ); \ +#define IND_Y( cross, out ) {\ + fuint16 temp = READ_LOW( data ) + y;\ + out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ + cross( temp );\ } -#define IND_X( out ) { \ - fuint16 temp = data + x; \ - out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) ); \ +#define IND_X( out ) {\ + fuint16 temp = data + x;\ + out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ } -#define ARITH_ADDR_MODES( op ) \ -case op - 0x04: /* (ind,x) */ \ - IND_X( data ) \ - goto ptr##op; \ -case op + 0x0C: /* (ind),y */ \ - IND_Y( HANDLE_PAGE_CROSSING, data ) \ - 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: \ - SYNC_TIME(); \ - data = READ( data ); \ - RELOAD_TIME(); \ -case op + 0x04: /* imm */ \ -imm##op: \ +#define ARITH_ADDR_MODES( op )\ +case op - 0x04: /* (ind,x) */\ + IND_X( data )\ + goto ptr##op;\ +case op + 0x0C: /* (ind),y */\ + IND_Y( HANDLE_PAGE_CROSSING, data )\ + 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:\ + FLUSH_TIME();\ + data = READ( data );\ + CACHE_TIME();\ +case op + 0x04: /* imm */\ +imm##op: -#define BRANCH( cond ) \ -{ \ - fint16 offset = (BOOST::int8_t) data; \ +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH( cond )\ +{\ + fint16 offset = (BOOST::int8_t) data;\ fuint16 extra_clock = (++pc & 0xFF) + offset;\ - if ( !(cond) ) goto dec_clock_loop; \ - pc += offset; \ - s_time += extra_clock >> 8 & 1; \ - goto loop; \ + if ( !(cond) ) goto dec_clock_loop;\ + pc = BOOST::uint16_t (pc + offset);\ + s_time += extra_clock >> 8 & 1;\ + goto loop;\ } // Often-Used @@ -407,9 +409,9 @@ goto loop; } sta_ptr: - SYNC_TIME(); + FLUSH_TIME(); WRITE( addr, a ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; case 0x91: // STA (ind),Y @@ -466,9 +468,9 @@ if ( (addr ^ 0x8000) <= 0x9FFF ) goto loop; a_nz_read_addr: - SYNC_TIME(); + FLUSH_TIME(); a = nz = READ( addr ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; } @@ -529,9 +531,9 @@ case 0xAC:{// LDY abs unsigned addr = data + 0x100 * GET_MSB(); pc += 2; - SYNC_TIME(); + FLUSH_TIME(); y = nz = READ( addr ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; } @@ -541,9 +543,9 @@ case 0xAE:{// LDX abs unsigned addr = data + 0x100 * GET_MSB(); pc += 2; - SYNC_TIME(); + FLUSH_TIME(); x = nz = READ( addr ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; } @@ -563,9 +565,9 @@ WRITE_LOW( addr, temp ); goto loop; } - SYNC_TIME(); + FLUSH_TIME(); WRITE( addr, temp ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; } @@ -574,9 +576,9 @@ case 0xEC:{// CPX abs unsigned addr = GET_ADDR(); pc++; - SYNC_TIME(); + FLUSH_TIME(); data = READ( addr ); - RELOAD_TIME(); + CACHE_TIME(); goto cpx_data; } @@ -593,9 +595,9 @@ case 0xCC:{// CPY abs unsigned addr = GET_ADDR(); pc++; - SYNC_TIME(); + FLUSH_TIME(); data = READ( addr ); - RELOAD_TIME(); + CACHE_TIME(); goto cpy_data; } @@ -699,8 +701,8 @@ c = 0; case 0x6E: // ROR abs ror_abs: { - ADD_PAGE - SYNC_TIME(); + ADD_PAGE(); + FLUSH_TIME(); int temp = READ( data ); nz = (c >> 1 & 0x80) | (temp >> 1); c = temp << 8; @@ -717,14 +719,14 @@ c = 0; case 0x2E: // ROL abs rol_abs: - ADD_PAGE + ADD_PAGE(); nz = c >> 8 & 1; - SYNC_TIME(); + FLUSH_TIME(); nz |= (c = READ( data ) << 1); rotate_common: pc++; WRITE( data, (uint8_t) nz ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; case 0x7E: // ROR abs,X @@ -805,11 +807,11 @@ dec_ptr: nz = (unsigned) -1; inc_common: - SYNC_TIME(); + FLUSH_TIME(); nz += READ( data ); pc += 2; WRITE( data, (uint8_t) nz ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; // Transfer @@ -1050,9 +1052,9 @@ out_of_time: pc--; - SYNC_TIME(); + FLUSH_TIME(); CPU_DONE( this, TIME, result_ ); - RELOAD_TIME(); + CACHE_TIME(); if ( result_ >= 0 ) goto interrupt; if ( s_time < 0 )
--- a/src/console/Nes_Cpu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Nes_Cpu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // NES 6502 CPU emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef NES_CPU_H #define NES_CPU_H
--- a/src/console/Nes_Fme7_Apu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Nes_Fme7_Apu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Nes_Fme7_Apu.h" @@ -78,11 +78,13 @@ int amp = volume; if ( !phases [index] ) amp = 0; - int delta = amp - oscs [index].last_amp; - if ( delta ) { - oscs [index].last_amp = amp; - synth.offset( last_time, delta, osc_output ); + int delta = amp - oscs [index].last_amp; + if ( delta ) + { + oscs [index].last_amp = amp; + synth.offset( last_time, delta, osc_output ); + } } blip_time_t time = last_time + delays [index];
--- a/src/console/Nes_Fme7_Apu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Nes_Fme7_Apu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Sunsoft FME-7 sound emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef NES_FME7_APU_H #define NES_FME7_APU_H @@ -15,7 +15,6 @@ BOOST::uint8_t latch; BOOST::uint16_t delays [3]; // a, b, c }; -BOOST_STATIC_ASSERT( sizeof (fme7_apu_state_t) == 24 ); class Nes_Fme7_Apu : private fme7_apu_state_t { public:
--- a/src/console/Nes_Oscs.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Nes_Oscs.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -133,9 +133,11 @@ if ( phase < duty ) amp ^= volume; - int delta = update_amp( amp ); - if ( delta ) - synth.offset( time, delta, output ); + { + int delta = update_amp( amp ); + if ( delta ) + synth.offset( time, delta, output ); + } time += delay; if ( time < end_time ) @@ -489,9 +491,11 @@ const int volume = this->volume(); int amp = (noise & 1) ? volume : 0; - int delta = update_amp( amp ); - if ( delta ) - synth.offset( time, delta, output ); + { + int delta = update_amp( amp ); + if ( delta ) + synth.offset( time, delta, output ); + } time += delay; if ( time < end_time )
--- a/src/console/Nes_Vrc6_Apu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Nes_Vrc6_Apu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -72,6 +72,7 @@ void Nes_Vrc6_Apu::save_state( vrc6_apu_state_t* out ) const { + assert( sizeof (vrc6_apu_state_t) == 20 ); out->saw_amp = oscs [2].amp; for ( int i = 0; i < osc_count; i++ ) {
--- a/src/console/Nes_Vrc6_Apu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Nes_Vrc6_Apu.h Thu Dec 07 15:20:41 2006 -0800 @@ -72,7 +72,6 @@ BOOST::uint8_t phases [3]; BOOST::uint8_t unused; }; -BOOST_STATIC_ASSERT( sizeof (vrc6_apu_state_t) == 20 ); inline void Nes_Vrc6_Apu::osc_output( int i, Blip_Buffer* buf ) {
--- a/src/console/Nsf_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Nsf_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Nsf_Emu.h" @@ -106,7 +106,7 @@ blargg_err_t load_( Data_Reader& in ) { - blargg_err_t err = in.read( &h, sizeof h ); + blargg_err_t err = in.read( &h, Nsf_Emu::header_size ); if ( err ) return (err == in.eof_error ? gme_wrong_file_type : err); @@ -213,13 +213,15 @@ CHECK_ALLOC( vrc6 ); adjusted_gain *= 0.75; - int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count; - static const char* const names [count] = { - APU_NAMES, - "Saw Wave", "Square 3", "Square 4" - }; - set_voice_count( count ); - set_voice_names( names ); + { + int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count; + static const char* const names [count] = { + APU_NAMES, + "Saw Wave", "Square 3", "Square 4" + }; + set_voice_count( count ); + set_voice_names( names ); + } if ( header_.chip_flags & namco_flag ) { @@ -264,8 +266,8 @@ blargg_err_t Nsf_Emu::load_( Data_Reader& in ) { - unload(); - RETURN_ERR( rom.load( in, sizeof header_, &header_, 0 ) ); + assert( offsetof (header_t,unused [4]) == header_size ); + RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); set_track_count( header_.track_count ); RETURN_ERR( check_nsf_header( &header_ ) ); @@ -388,13 +390,13 @@ { switch ( addr ) { - case Nes_Namco_Apu::data_reg_addr: - namco->write_data( time(), data ); - return; - - case Nes_Namco_Apu::addr_reg_addr: - namco->write_addr( data ); - return; + case Nes_Namco_Apu::data_reg_addr: + namco->write_data( time(), data ); + return; + + case Nes_Namco_Apu::addr_reg_addr: + namco->write_addr( data ); + return; } } @@ -402,13 +404,13 @@ { switch ( addr & Nes_Fme7_Apu::addr_mask ) { - case Nes_Fme7_Apu::latch_addr: - fme7->write_latch( data ); - return; - - case Nes_Fme7_Apu::data_addr: - fme7->write_data( time(), data ); - return; + case Nes_Fme7_Apu::latch_addr: + fme7->write_latch( data ); + return; + + case Nes_Fme7_Apu::data_addr: + fme7->write_data( time(), data ); + return; } } @@ -524,6 +526,7 @@ r.pc = play_addr; low_mem [0x100 + r.sp--] = (badop_addr - 1) >> 8; low_mem [0x100 + r.sp--] = (badop_addr - 1) & 0xFF; + GME_FRAME_HOOK( this ); } } }
--- a/src/console/Nsf_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Nsf_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Nintendo NES/Famicom NSF music file emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef NSF_EMU_H #define NSF_EMU_H @@ -16,6 +16,7 @@ static equalizer_t const famicom_eq; // NSF file header + enum { header_size = 0x80 }; struct header_t { char tag [5]; @@ -35,7 +36,6 @@ byte chip_flags; byte unused [4]; }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 0x80 ); // Header for currently loaded file header_t const& header() const { return header_; }
--- a/src/console/Nsfe_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Nsfe_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Nsfe_Emu.h" @@ -88,10 +88,12 @@ byte first_track; byte unused [6]; }; -BOOST_STATIC_ASSERT( sizeof (nsfe_info_t) == 16 ); blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) { + int const nsfe_info_size = 16; + assert( offsetof (nsfe_info_t,unused [6]) == nsfe_info_size ); + // check header byte signature [4]; blargg_err_t err = in.read( signature, sizeof signature ); @@ -146,9 +148,9 @@ finfo.track_count = 1; finfo.first_track = 0; - RETURN_ERR( in.read( &finfo, min( size, (blargg_long) sizeof finfo ) ) ); - if ( size > (int) sizeof finfo ) - RETURN_ERR( in.skip( size - sizeof finfo ) ); + RETURN_ERR( in.read( &finfo, min( size, (blargg_long) nsfe_info_size ) ) ); + if ( size > nsfe_info_size ) + RETURN_ERR( in.skip( size - nsfe_info_size ) ); phase = 1; info.speed_flags = finfo.speed_flags; info.chip_flags = finfo.chip_flags; @@ -203,19 +205,17 @@ case BLARGG_4CHAR('A','T','A','D'): { check( phase == 1 ); phase = 2; - disable_playlist( false ); if ( !nsf_emu ) { - in.skip( size ); + RETURN_ERR( in.skip( size ) ); } else { Subset_Reader sub( &in, size ); // limit emu to nsf data - Remaining_Reader rem( &header, sizeof header, &sub ); + Remaining_Reader rem( &header, Nsf_Emu::header_size, &sub ); RETURN_ERR( nsf_emu->load( rem ) ); check( rem.remain() == 0 ); } - disable_playlist( false ); // TODO: fix this crappy hack (unload() disables playlist) break; } @@ -283,6 +283,7 @@ blargg_err_t load_( Data_Reader& in ) { RETURN_ERR( info.load( in, 0 ) ); + info.disable_playlist( false ); set_track_count( info.info.track_count ); return 0; }
--- a/src/console/Nsfe_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Nsfe_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Nintendo NES/Famicom NSFE music file emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef NSFE_EMU_H #define NSFE_EMU_H
--- a/src/console/Sap_Apu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Sap_Apu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Sap_Apu.h"
--- a/src/console/Sap_Apu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Sap_Apu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Atari POKEY sound chip emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef SAP_APU_H #define SAP_APU_H
--- a/src/console/Sap_Cpu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Sap_Cpu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Sap_Cpu.h" @@ -18,8 +18,8 @@ License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#define SYNC_TIME() (void) (s.time = s_time) -#define RELOAD_TIME() (void) (s_time = s.time) +#define FLUSH_TIME() (void) (s.time = s_time) +#define CACHE_TIME() (void) (s_time = s.time) #include "sap_cpu_io.h" @@ -94,18 +94,18 @@ // status flags #define IS_NEG (nz & 0x8080) - #define CALC_STATUS( out ) do { \ - out = status & (st_v | st_d | st_i); \ - out |= ((nz >> 8) | nz) & st_n; \ - out |= c >> 8 & st_c; \ - if ( !(nz & 0xFF) ) out |= st_z; \ + #define CALC_STATUS( out ) do {\ + out = status & (st_v | st_d | st_i);\ + out |= ((nz >> 8) | nz) & st_n;\ + out |= c >> 8 & st_c;\ + if ( !(nz & 0xFF) ) out |= st_z;\ } while ( 0 ) - #define SET_STATUS( in ) do { \ - status = in & (st_v | st_d | st_i); \ - nz = in << 8; \ - c = nz; \ - nz |= ~in & st_z; \ + #define SET_STATUS( in ) do {\ + status = in & (st_v | st_d | st_i);\ + nz = in << 8;\ + c = nz;\ + nz |= ~in & st_z;\ } while ( 0 ) fuint8 status; @@ -182,7 +182,7 @@ // Macros #define GET_MSB() (instr [1]) -#define ADD_PAGE (pc++, data += 0x100 * GET_MSB()); +#define ADD_PAGE() (pc++, data += 0x100 * GET_MSB()) #define GET_ADDR() GET_LE16( instr ) #define NO_PAGE_CROSSING( lsb ) @@ -190,53 +190,54 @@ #define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; -#define IND_Y( cross, out ) { \ - fuint16 temp = READ_LOW( data ) + y; \ - out = temp + 0x100 * READ_LOW( uint8_t (data + 1) ); \ - cross( temp ); \ +#define IND_Y( cross, out ) {\ + fuint16 temp = READ_LOW( data ) + y;\ + out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ + cross( temp );\ } -#define IND_X( out ) { \ - fuint16 temp = data + x; \ - out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) ); \ +#define IND_X( out ) {\ + fuint16 temp = data + x;\ + out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ } -#define ARITH_ADDR_MODES( op ) \ -case op - 0x04: /* (ind,x) */ \ - IND_X( data ) \ - goto ptr##op; \ -case op + 0x0C: /* (ind),y */ \ - IND_Y( HANDLE_PAGE_CROSSING, data ) \ - 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: \ - SYNC_TIME(); \ - data = READ( data ); \ - RELOAD_TIME(); \ -case op + 0x04: /* imm */ \ -imm##op: \ +#define ARITH_ADDR_MODES( op )\ +case op - 0x04: /* (ind,x) */\ + IND_X( data )\ + goto ptr##op;\ +case op + 0x0C: /* (ind),y */\ + IND_Y( HANDLE_PAGE_CROSSING, data )\ + 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:\ + FLUSH_TIME();\ + data = READ( data );\ + CACHE_TIME();\ +case op + 0x04: /* imm */\ +imm##op: -#define BRANCH( cond ) \ -{ \ - fint16 offset = (BOOST::int8_t) data; \ +// TODO: more efficient way to handle negative branch that wraps PC around +#define BRANCH( cond )\ +{\ + fint16 offset = (BOOST::int8_t) data;\ fuint16 extra_clock = (++pc & 0xFF) + offset;\ - if ( !(cond) ) goto dec_clock_loop; \ - pc += offset; \ - s_time += extra_clock >> 8 & 1; \ - goto loop; \ + if ( !(cond) ) goto dec_clock_loop;\ + pc += offset;\ + s_time += extra_clock >> 8 & 1;\ + goto loop;\ } // Often-Used @@ -352,9 +353,9 @@ goto loop; } sta_ptr: - SYNC_TIME(); + FLUSH_TIME(); WRITE( addr, a ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; case 0x91: // STA (ind),Y @@ -411,9 +412,9 @@ if ( (addr ^ 0x8000) <= 0x9FFF ) goto loop; a_nz_read_addr: - SYNC_TIME(); + FLUSH_TIME(); a = nz = READ( addr ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; } @@ -474,9 +475,9 @@ case 0xAC:{// LDY abs unsigned addr = data + 0x100 * GET_MSB(); pc += 2; - SYNC_TIME(); + FLUSH_TIME(); y = nz = READ( addr ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; } @@ -486,9 +487,9 @@ case 0xAE:{// LDX abs unsigned addr = data + 0x100 * GET_MSB(); pc += 2; - SYNC_TIME(); + FLUSH_TIME(); x = nz = READ( addr ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; } @@ -508,9 +509,9 @@ WRITE_LOW( addr, temp ); goto loop; } - SYNC_TIME(); + FLUSH_TIME(); WRITE( addr, temp ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; } @@ -519,9 +520,9 @@ case 0xEC:{// CPX abs unsigned addr = GET_ADDR(); pc++; - SYNC_TIME(); + FLUSH_TIME(); data = READ( addr ); - RELOAD_TIME(); + CACHE_TIME(); goto cpx_data; } @@ -538,9 +539,9 @@ case 0xCC:{// CPY abs unsigned addr = GET_ADDR(); pc++; - SYNC_TIME(); + FLUSH_TIME(); data = READ( addr ); - RELOAD_TIME(); + CACHE_TIME(); goto cpy_data; } @@ -645,8 +646,8 @@ c = 0; case 0x6E: // ROR abs ror_abs: { - ADD_PAGE - SYNC_TIME(); + ADD_PAGE(); + FLUSH_TIME(); int temp = READ( data ); nz = (c >> 1 & 0x80) | (temp >> 1); c = temp << 8; @@ -663,14 +664,14 @@ c = 0; case 0x2E: // ROL abs rol_abs: - ADD_PAGE + ADD_PAGE(); nz = c >> 8 & 1; - SYNC_TIME(); + FLUSH_TIME(); nz |= (c = READ( data ) << 1); rotate_common: pc++; WRITE( data, (uint8_t) nz ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; case 0x7E: // ROR abs,X @@ -751,11 +752,11 @@ dec_ptr: nz = (unsigned) -1; inc_common: - SYNC_TIME(); + FLUSH_TIME(); nz += READ( data ); pc += 2; WRITE( data, (uint8_t) nz ); - RELOAD_TIME(); + CACHE_TIME(); goto loop; // Transfer @@ -940,7 +941,7 @@ int result_; handle_brk: - if ( pc >= idle_addr + 1 ) + if ( (pc - 1) >= idle_addr ) goto idle_done; pc++; result_ = 4; @@ -978,9 +979,9 @@ goto stop; out_of_time: pc--; - SYNC_TIME(); + FLUSH_TIME(); CPU_DONE( this, TIME, result_ ); - RELOAD_TIME(); + CACHE_TIME(); if ( result_ >= 0 ) goto interrupt; if ( s_time < 0 )
--- a/src/console/Sap_Cpu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Sap_Cpu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Atari 6502 CPU emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef SAP_CPU_H #define SAP_CPU_H
--- a/src/console/Sap_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Sap_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Sap_Emu.h" @@ -288,11 +288,11 @@ check( r.sp >= 0xFE ); // catch anything trying to leave data on stack r.pc = addr; int high_byte = (idle_addr - 1) >> 8; - if ( r.sp == 0xFE && mem [0x1FF] == high_byte ) + if ( r.sp == 0xFE && mem.ram [0x1FF] == high_byte ) r.sp = 0xFF; // pop extra byte off - mem [0x100 + r.sp--] = high_byte; // some routines use RTI to return - mem [0x100 + r.sp--] = high_byte; - mem [0x100 + r.sp--] = (idle_addr - 1) & 0xFF; + mem.ram [0x100 + r.sp--] = high_byte; // some routines use RTI to return + mem.ram [0x100 + r.sp--] = high_byte; + mem.ram [0x100 + r.sp--] = (idle_addr - 1) & 0xFF; } void Sap_Emu::run_routine( sap_addr_t addr ) @@ -327,7 +327,8 @@ { RETURN_ERR( Classic_Emu::start_track_( track ) ); - memset( mem, 0, sizeof mem ); + memset( &mem, 0, sizeof mem ); + byte const* in = info.rom_data; while ( file_end - in >= 5 ) { @@ -347,7 +348,7 @@ break; } - memcpy( mem + start, in, len ); + memcpy( mem.ram + start, in, len ); in += len; if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF ) in += 2; @@ -355,7 +356,7 @@ apu.reset( &apu_impl ); apu2.reset( &apu_impl ); - cpu::reset( mem ); + cpu::reset( mem.ram ); time_mask = 0; // disables sound during init call_init( track ); time_mask = -1; @@ -373,6 +374,7 @@ { if ( (addr ^ Sap_Apu::start_addr) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) ) { + GME_APU_HOOK( this, addr - Sap_Apu::start_addr, data ); apu.write_data( time() & time_mask, addr, data ); return; } @@ -380,6 +382,7 @@ if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) && info.stereo ) { + GME_APU_HOOK( this, addr - 0x10 - Sap_Apu::start_addr + 10, data ); apu2.write_data( time() & time_mask, addr ^ 0x10, data ); return; } @@ -417,6 +420,7 @@ set_time( next_play ); next_play += play_period(); call_play(); + GME_FRAME_HOOK( this ); } else {
--- a/src/console/Sap_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Sap_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Atari XL/XE SAP music file emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef SAP_EMU_H #define SAP_EMU_H @@ -52,7 +52,11 @@ Sap_Apu apu2; // large items - byte mem [0x10000 + 0x100]; + struct { + byte padding1 [0x100]; + byte ram [0x10000]; + byte padding2 [0x100]; + } mem; Sap_Apu_Impl apu_impl; sap_time_t play_period() const;
--- a/src/console/Sms_Apu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Sms_Apu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -70,11 +70,13 @@ else { int amp = phase ? volume : -volume; - int delta = amp - last_amp; - if ( delta ) { - last_amp = amp; - synth->offset( time, delta, output ); + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth->offset( time, delta, output ); + } } time += delay; @@ -114,11 +116,13 @@ if ( shifter & 1 ) amp = -amp; - int delta = amp - last_amp; - if ( delta ) { - last_amp = amp; - synth.offset( time, delta, output ); + int delta = amp - last_amp; + if ( delta ) + { + last_amp = amp; + synth.offset( time, delta, output ); + } } time += delay;
--- a/src/console/Snes_Spc.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Snes_Spc.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Snes_Spc.h" @@ -20,12 +20,13 @@ // 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 ) +Snes_Spc::Snes_Spc() : dsp( mem.ram ), cpu( this, mem.ram ) { set_tempo( 1.0 ); - // Put STOP instruction past end of memory to catch PC overflow. - memset( ram + ram_size, 0xFF, (sizeof ram) - ram_size ); + // Put STOP instruction around memory to catch PC underflow/overflow. + memset( mem.padding1, 0xFF, sizeof mem.padding1 ); + memset( mem.padding2, 0xFF, sizeof mem.padding2 ); // A few tracks read from the last four bytes of IPL ROM boot_rom [sizeof boot_rom - 2] = 0xC0; @@ -65,7 +66,7 @@ uint8_t dsp [128]; uint8_t ipl_rom [128]; }; - BOOST_STATIC_ASSERT( sizeof (spc_file_t) == spc_file_size + 128 ); + assert( offsetof (spc_file_t,ipl_rom) == spc_file_size ); const spc_file_t* spc = (spc_file_t const*) data; @@ -98,8 +99,8 @@ if ( !(dsp.read( 0x6C ) & 0x20) ) { unsigned addr = 0x100 * dsp.read( 0x6D ); - unsigned size = 0x800 * dsp.read( 0x7D ); - memset( ram + addr, 0xFF, min( size, ram_size - addr ) ); + size_t size = 0x800 * dsp.read( 0x7D ); + memset( mem.ram + addr, 0xFF, min( size, sizeof mem.ram - addr ) ); } } @@ -119,11 +120,11 @@ extra_cycles = 32; // ram - memcpy( ram, new_ram, ram_size ); - memcpy( extra_ram, ram + rom_addr, sizeof extra_ram ); + memcpy( mem.ram, new_ram, sizeof mem.ram ); + memcpy( extra_ram, mem.ram + rom_addr, sizeof extra_ram ); // boot rom (have to force enable_rom() to update it) - rom_enabled = !(ram [0xF1] & 0x80); + rom_enabled = !(mem.ram [0xF1] & 0x80); enable_rom( !rom_enabled ); // dsp @@ -138,27 +139,27 @@ Timer& t = timer [i]; t.next_tick = 0; - t.enabled = (ram [0xF1] >> i) & 1; + t.enabled = (mem.ram [0xF1] >> i) & 1; if ( !t.enabled ) t.next_tick = timer_disabled_time; t.count = 0; - t.counter = ram [0xFD + i] & 15; + t.counter = mem.ram [0xFD + i] & 15; - int p = ram [0xFA + i]; + int p = mem.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; + mem.ram [0xF0] = 0; + mem.ram [0xF1] = 0; + mem.ram [0xF3] = 0xFF; + mem.ram [0xFA] = 0; + mem.ram [0xFB] = 0; + mem.ram [0xFC] = 0; + mem.ram [0xFD] = 0xFF; + mem.ram [0xFE] = 0xFF; + mem.ram [0xFF] = 0xFF; return 0; // success } @@ -238,7 +239,7 @@ int Snes_Spc::read( spc_addr_t addr ) { - int result = ram [addr]; + int result = mem.ram [addr]; if ( (rom_addr <= addr && addr < 0xFFFC || addr >= 0xFFFE) && rom_enabled ) dprintf( "Read from ROM: %04X -> %02X\n", addr, result ); @@ -253,25 +254,25 @@ { Timer& t = timer [i]; t.run_until( time() ); - int result = t.counter; + int old = t.counter; t.counter = 0; - return result; + return old; } // 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 ); + if ( mem.ram [0xF2] >= Spc_Dsp::register_count ) + dprintf( "DSP read from $%02X\n", (int) mem.ram [0xF2] ); + return dsp.read( mem.ram [0xF2] & 0x7F ); } 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 + // Registers which always read as 0 are handled by setting mem.ram [reg] to 0 // at startup then never changing that value. check(( check_for_echo_access( addr ), true )); @@ -288,7 +289,7 @@ if ( rom_enabled != enable ) { rom_enabled = enable; - memcpy( ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size ); + memcpy( mem.ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size ); // TODO: ROM can still get overwritten when DSP writes to echo buffer } } @@ -297,7 +298,7 @@ { // first page is very common if ( addr < 0xF0 ) { - ram [addr] = (uint8_t) data; + mem.ram [addr] = (uint8_t) data; } else switch ( addr ) { @@ -305,12 +306,12 @@ default: check(( check_for_echo_access( addr ), true )); if ( addr < rom_addr ) { - ram [addr] = (uint8_t) data; + mem.ram [addr] = (uint8_t) data; } else { extra_ram [addr - rom_addr] = (uint8_t) data; if ( !rom_enabled ) - ram [addr] = (uint8_t) data; + mem.ram [addr] = (uint8_t) data; } break; @@ -318,7 +319,7 @@ //case 0xF2: // mapped to RAM case 0xF3: { run_dsp( time() ); - int reg = ram [0xF2]; + int reg = mem.ram [0xF2]; if ( next_dsp > 0 ) { // skip mode @@ -367,12 +368,12 @@ // port clears if ( data & 0x10 ) { - ram [0xF4] = 0; - ram [0xF5] = 0; + mem.ram [0xF4] = 0; + mem.ram [0xF5] = 0; } if ( data & 0x20 ) { - ram [0xF6] = 0; - ram [0xF7] = 0; + mem.ram [0xF6] = 0; + mem.ram [0xF7] = 0; } enable_rom( data & 0x80 );
--- a/src/console/Snes_Spc.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Snes_Spc.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Super Nintendo (SNES) SPC-700 APU Emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef SNES_SPC_H #define SNES_SPC_H @@ -102,9 +102,13 @@ // CPU and RAM (at end because it's large) Spc_Cpu cpu; - enum { ram_size = 0x10000 }; uint8_t extra_ram [rom_size]; - uint8_t ram [ram_size + 0x100]; // padding for catching jumps past end + struct { + // padding to catch jumps before beginning or past end + uint8_t padding1 [0x100]; + uint8_t ram [0x10000]; + uint8_t padding2 [0x100]; + } mem; uint8_t boot_rom [rom_size]; };
--- a/src/console/Spc_Cpu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Spc_Cpu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Spc_Cpu.h" @@ -39,7 +39,7 @@ Spc_Cpu::Spc_Cpu( Snes_Spc* e, uint8_t* ram_in ) : ram( ram_in ), emu( *e ) { remain_ = 0; - BOOST_STATIC_ASSERT( sizeof (int) >= 4 ); + assert( INT_MAX >= 0x7FFFFFFF ); // requires 32-bit int blargg_verify_byte_order(); } @@ -129,20 +129,20 @@ #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; \ + #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; \ + #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 ) int status; @@ -188,14 +188,14 @@ switch ( opcode ) { - #define BRANCH( cond ) { \ - pc++; \ - int offset = (BOOST::int8_t) data; \ - if ( cond ) { \ - pc += offset; \ - remain_ -= 2; \ - } \ - goto loop; \ + #define BRANCH( cond ) {\ + pc++;\ + int offset = (BOOST::int8_t) data;\ + if ( cond ) {\ + pc += offset;\ + remain_ -= 2;\ + }\ + goto loop;\ } // Most-Common @@ -220,31 +220,31 @@ // 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 */ \ +#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) */ \ + 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++; \ + 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; \ + 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 @@ -372,27 +372,27 @@ // 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 ); \ +#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++; \ + pc++;\ addr = READ_PROG( pc ) + dp;\ - addr_##op: \ + addr_##op:\ nz = data func READ( addr );\ - WRITE( addr, nz ); \ - goto inc_pc_loop; \ + WRITE( addr, nz );\ + goto inc_pc_loop;\ } LOGICAL_OP( 0x28, & ); // AND @@ -504,9 +504,9 @@ // 6. ADDITION & SUBTRACTION COMMANDS -#define INC_DEC_REG( reg, n ) \ - nz = reg + n; \ - reg = (uint8_t) nz; \ +#define INC_DEC_REG( reg, n )\ + nz = reg + n;\ + reg = (uint8_t) nz;\ goto loop; case 0xBC: INC_DEC_REG( a, 1 ) // INC A @@ -819,7 +819,7 @@ // 13. SUB-ROUTINE CALL RETURN COMMANDS - case 0x0F: // BRK + case 0x0F:{// BRK check( false ); // untested PUSH16( pc + 1 ); pc = READ_PROG16( 0xFFDE ); // vector address verified @@ -828,6 +828,7 @@ PUSH( temp ); status = (status | st_b) & ~st_i; goto loop; + } case 0x4F: // PCALL offset pc++;
--- a/src/console/Spc_Cpu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Spc_Cpu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Super Nintendo (SNES) SPC-700 CPU emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef SPC_CPU_H #define SPC_CPU_H
--- a/src/console/Spc_Dsp.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Spc_Dsp.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ // Based on Brad Martin's OpenSPC DSP emulator @@ -31,7 +31,8 @@ mute_voices( 0 ); disable_surround( false ); - BOOST_STATIC_ASSERT( sizeof (g) == register_count && sizeof (voice) == register_count ); + assert( offsetof (globals_t,unused9 [2]) == register_count ); + assert( sizeof (voice) == register_count ); blargg_verify_byte_order(); }
--- a/src/console/Spc_Dsp.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Spc_Dsp.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Super Nintendo (SNES) SPC DSP emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef SPC_DSP_H #define SPC_DSP_H @@ -52,37 +52,37 @@ int8_t unused [6]; }; + struct globals_t { + 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]; + }; + 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; + globals_t g; }; uint8_t* const ram;
--- a/src/console/Spc_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Spc_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Spc_Emu.h" @@ -218,14 +218,14 @@ long file_size = in.remain(); if ( file_size < Snes_Spc::spc_file_size ) return gme_wrong_file_type; - RETURN_ERR( in.read( &header, sizeof header ) ); + RETURN_ERR( in.read( &header, Spc_Emu::header_size ) ); RETURN_ERR( check_spc_header( header.tag ) ); long const xid6_offset = 0x10200; long xid6_size = file_size - xid6_offset; if ( xid6_size > 0 ) { RETURN_ERR( xid6.resize( xid6_size ) ); - RETURN_ERR( in.skip( xid6_offset - sizeof header ) ); + RETURN_ERR( in.skip( xid6_offset - Spc_Emu::header_size ) ); RETURN_ERR( in.read( xid6.begin(), xid6.size() ) ); } return 0; @@ -264,6 +264,7 @@ blargg_err_t Spc_Emu::load_mem_( byte const* in, long size ) { + assert( offsetof (header_t,unused2 [46]) == header_size ); file_data = in; file_size = size; set_voice_count( Snes_Spc::voice_count ); @@ -291,8 +292,7 @@ { count = long (count * resampler.ratio()) & ~1; count -= resampler.skip_input( count ); - } - + } if ( count > 0 ) RETURN_ERR( apu.skip( count ) );
--- a/src/console/Spc_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Spc_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Super Nintendo SPC music file emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef SPC_EMU_H #define SPC_EMU_H @@ -15,6 +15,7 @@ enum { native_sample_rate = 32000 }; // SPC file header + enum { header_size = 0x100 }; struct header_t { char tag [35]; @@ -35,7 +36,6 @@ byte emulator; byte unused2 [46]; }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 0x100 ); // Header for currently loaded file header_t const& header() const { return *(header_t const*) file_data; }
--- a/src/console/Vfs_File.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Vfs_File.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -43,7 +43,7 @@ if ( n == 0 ) // optimization vfs_rewind( file_ ); else if ( vfs_fseek( file_, n, SEEK_SET ) != 0 ) - return "Error seeking in file"; + return eof_error; return 0; }
--- a/src/console/Vfs_File.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Vfs_File.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// File_Reader based on a VFSFile +// File_Reader wrapper over a VFSFile #ifndef VFS_FILE_H #define VFS_FILE_H
--- a/src/console/Vgm_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Vgm_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Vgm_Emu.h" @@ -108,7 +108,7 @@ if ( gd3_offset < 0 ) return 0; - byte const* gd3 = data + sizeof (header_t) + gd3_offset; + byte const* gd3 = data + header_size + gd3_offset; long gd3_size = check_gd3_header( gd3, data_end - gd3 ); if ( !gd3_size ) return 0; @@ -168,14 +168,14 @@ blargg_err_t load_( Data_Reader& in ) { long file_size = in.remain(); - if ( file_size <= (long) sizeof h ) + if ( file_size <= Vgm_Emu::header_size ) return gme_wrong_file_type; - RETURN_ERR( in.read( &h, sizeof h ) ); + RETURN_ERR( in.read( &h, Vgm_Emu::header_size ) ); RETURN_ERR( check_vgm_header( h ) ); long gd3_offset = get_le32( h.gd3_offset ) - 0x2C; - long remain = file_size - sizeof h - gd3_offset; + long remain = file_size - Vgm_Emu::header_size - gd3_offset; byte gd3_h [gd3_header_size]; if ( gd3_offset > 0 || remain >= gd3_header_size ) { @@ -269,7 +269,9 @@ blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size ) { - if ( new_size <= (long) sizeof (header_t) ) + assert( offsetof (header_t,unused2 [8]) == header_size ); + + if ( new_size <= header_size ) return gme_wrong_file_type; header_t const& h = *(header_t const*) new_data; @@ -365,7 +367,7 @@ psg.reset( get_le16( header().noise_feedback ), header().noise_width ); dac_disabled = -1; - pos = data + sizeof (header_t); + pos = data + header_size; pcm_data = pos; pcm_pos = pos; dac_amp = -1;
--- a/src/console/Vgm_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Vgm_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef VGM_EMU_H #define VGM_EMU_H @@ -22,6 +22,7 @@ void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; } // VGM header format + enum { header_size = 0x40 }; struct header_t { char tag [4]; @@ -42,7 +43,6 @@ byte data_offset [4]; byte unused2 [8]; }; - BOOST_STATIC_ASSERT( sizeof (header_t) == 0x40 ); // Header for currently loaded file header_t const& header() const { return *(header_t const*) data; }
--- a/src/console/Vgm_Emu_Impl.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Vgm_Emu_Impl.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Vgm_Emu.h"
--- a/src/console/Vgm_Emu_Impl.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Vgm_Emu_Impl.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // Low-level parts of Vgm_Emu -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef VGM_EMU_IMPL_H #define VGM_EMU_IMPL_H
--- a/src/console/Ym2413_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Ym2413_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,7 +1,7 @@ // Use in place of Ym2413_Emu.cpp and ym2413.c to disable support for this chip -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Ym2413_Emu.h"
--- a/src/console/Ym2413_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Ym2413_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // YM2413 FM sound chip emulator interface -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef YM2413_EMU_H #define YM2413_EMU_H
--- a/src/console/Ym2612_Emu.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Ym2612_Emu.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ // Based on Gens 2.10 ym2612.c @@ -741,10 +741,7 @@ double x = i * 3; // 3 and not 6 (Mickey Mania first music for test) x /= ENV_STEP; - int j = (int) x; - j <<= ENV_LBITS; - - g.SL_TAB [i] = j + ENV_DECAY; + g.SL_TAB [i] = ((int) x << ENV_LBITS) + ENV_DECAY; } g.SL_TAB [15] = ((ENV_LENGHT - 1) << ENV_LBITS) + ENV_DECAY; // special case : volume off @@ -804,13 +801,13 @@ for (int j = 0; j < 32; j++) { #if ((SIN_LBITS + SIN_HBITS - 21) < 0) - double x = (double) DT_DEF_TAB [(i << 5) + j] * Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS)); + double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS)); #else - double x = (double) DT_DEF_TAB [(i << 5) + j] * Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21)); + double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21)); #endif - g.DT_TAB [i + 0] [j] = (int) x; - g.DT_TAB [i + 4] [j] = (int) -x; + g.DT_TAB [i + 0] [j] = (int) y; + g.DT_TAB [i + 4] [j] = (int) -y; } }
--- a/src/console/Ym2612_Emu.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/Ym2612_Emu.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // YM2612 FM sound chip emulator interface -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef YM2612_EMU_H #define YM2612_EMU_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/console/Zlib_Inflater.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -0,0 +1,175 @@ +// File_Extractor 0.4.0. http://www.slack.net/~ant/ + +#include "Zlib_Inflater.h" + +#include <assert.h> +#include <string.h> + +/* Copyright (C) 2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +static const char* get_zlib_err( int code ) +{ + assert( code != Z_OK ); + if ( code == Z_MEM_ERROR ) + return "Out of memory"; + + const char* str = zError( code ); + if ( code == Z_DATA_ERROR ) + str = "Zip data is corrupt"; + if ( !str ) + str = "Zip error"; + return str; +} + +void Zlib_Inflater::end() +{ + if ( deflated_ ) + { + deflated_ = false; + if ( inflateEnd( &zbuf ) ) + check( false ); + } + buf.clear(); + + static z_stream const empty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + memcpy( &zbuf, &empty, sizeof zbuf ); +} + +Zlib_Inflater::Zlib_Inflater() +{ + deflated_ = false; + end(); // initialize things +} + +Zlib_Inflater::~Zlib_Inflater() +{ + end(); +} + +blargg_err_t Zlib_Inflater::begin( mode_t mode, callback_t callback, void* user_data, + long buf_size ) +{ + end(); + + if ( buf_size && buf.resize( buf_size ) ) + buf_size = 0; // not enough memory for requested size, so use default + + if ( !buf_size ) + RETURN_ERR( buf.resize( 16 * 1024L ) ); + + // Fill buffer with some data, less than normal buffer size since caller might + // just be examining beginning of file. 4K is a common disk block size. + long count = (buf_size ? buf_size : 4096); + RETURN_ERR( callback( user_data, buf.begin(), &count ) ); + zbuf.avail_in = count; + zbuf.next_in = buf.begin(); + + if ( mode == mode_auto ) + { + // examine buffer for gzip header + mode = mode_copy; + int const min_gzip_size = 2 + 8 + 8; + if ( count >= min_gzip_size && buf [0] == 0x1F && buf [1] == 0x8B ) + mode = mode_ungz; + } + + if ( mode != mode_copy ) + { + int wb = MAX_WBITS + 16; // have zlib handle gzip header + if ( mode == mode_raw_deflate ) + wb = -MAX_WBITS; + + int zerr = inflateInit2( &zbuf, wb ); + if ( zerr ) + return get_zlib_err( zerr ); + + deflated_ = true; + } + return 0; +} + + +blargg_err_t Zlib_Inflater::read( void* out, long* count_io, + callback_t callback, void* user_data ) +{ + if ( !*count_io ) + return 0; + + if ( !deflated_ ) + { + // copy buffered data + long first = zbuf.avail_in; + if ( first ) + { + if ( first > *count_io ) + first = *count_io; + memcpy( out, zbuf.next_in, first ); + zbuf.next_in += first; + zbuf.avail_in -= first; + if ( !zbuf.avail_in ) + buf.clear(); // done with buffer + } + + // read remaining directly + long second = *count_io - first; + if ( second ) + { + long actual = second; + RETURN_ERR( callback( user_data, (char*) out + first, &actual ) ); + *count_io -= second - actual; + } + } + else + { + zbuf.next_out = (Bytef*) out; + zbuf.avail_out = *count_io; + + while ( 1 ) + { + uInt old_avail_in = zbuf.avail_in; + int err = inflate( &zbuf, Z_NO_FLUSH ); + if ( err == Z_STREAM_END ) + { + *count_io -= zbuf.avail_out; + end(); + break; // all data deflated + } + + if ( err == Z_BUF_ERROR && !old_avail_in ) + err = 0; // we just need to provide more input + + if ( err ) + return get_zlib_err( err ); + + if ( !zbuf.avail_out ) + break; // requested number of bytes deflated + + if ( zbuf.avail_in ) + { + // inflate() should never leave input if there's still space for output + assert( false ); + return "Corrupt zip data"; + } + + // refill buffer + long count = buf.size(); + RETURN_ERR( callback( user_data, buf.begin(), &count ) ); + zbuf.next_in = buf.begin(); + zbuf.avail_in = count; + if ( !zbuf.avail_in ) + return "Corrupt zip data"; // stream didn't end but there's no more data + } + } + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/console/Zlib_Inflater.h Thu Dec 07 15:20:41 2006 -0800 @@ -0,0 +1,48 @@ +// Simplifies use of zlib for deflating data + +// File_Extractor 0.4.0 +#ifndef ZLIB_INFLATER_H +#define ZLIB_INFLATER_H + +#include "blargg_common.h" +#include "zlib.h" + +class Zlib_Inflater { +public: + + // Data reader callback + typedef blargg_err_t (*callback_t)( void* user_data, void* out, long* count ); + + // Begin inflation using specified mode and fill buffer using read callback. + // Default buffer is 16K and filled to 4K, or specify buf_size for custom + // buffer size and to read that much file data. Using mode_auto selects + // between mode_copy and mode_ungz by examining first two bytes of buffer. + enum mode_t { mode_copy, mode_ungz, mode_raw_deflate, mode_auto }; + blargg_err_t begin( mode_t, callback_t, void* user_data, long buf_size = 0 ); + + // True if begin() has been called with mode_ungz or mode_raw_deflate + bool deflated() const { return deflated_; } + + // Read/inflate at most *count_io bytes into out and set *count_io to actual + // number of bytes read (less than requested if end of deflated data is reached). + // Keeps buffer full with user-provided callback. + blargg_err_t read( void* out, long* count_io, callback_t, void* user_data ); + + // End inflation and free memory + void end(); + +private: + // noncopyable + Zlib_Inflater( const Zlib_Inflater& ); + Zlib_Inflater& operator = ( const Zlib_Inflater& ); + +public: + Zlib_Inflater(); + ~Zlib_Inflater(); +private: + z_stream_s zbuf; + blargg_vector<unsigned char> buf; + bool deflated_; +}; + +#endif
--- a/src/console/blargg_common.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/blargg_common.h Thu Dec 07 15:20:41 2006 -0800 @@ -54,8 +54,13 @@ }; #ifndef BLARGG_DISABLE_NOTHROW + #if __cplusplus < 199711 + #define BLARGG_THROWS( spec ) + #else + #define BLARGG_THROWS( spec ) throw spec + #endif #define BLARGG_DISABLE_NOTHROW \ - void* operator new ( size_t s ) { return malloc( s ); }\ + void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ void operator delete ( void* p ) { free( p ); } #define BLARGG_NEW new #else
--- a/src/console/blargg_config.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/blargg_config.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,12 +1,9 @@ -// Game_Music_Emu 0.5.1 user configuration file. Don't replace when updating library. +// Custom Audacious configuration #ifndef BLARGG_CONFIG_H #define BLARGG_CONFIG_H -// Uncomment to use zlib for transparent decompression of gzipped files -#define HAVE_ZLIB_H - -// Uncomment to enable platform-specific (and possibly non-portable) optimizations. +// Uncomment to enable platform-specific (and possibly non-portable) optimizations //#define BLARGG_NONPORTABLE 1 // Uncomment if automatic byte-order determination doesn't work @@ -15,12 +12,11 @@ // Uncomment if you get errors in the bool section of blargg_common.h //#define BLARGG_COMPILER_HAS_BOOL 1 -// Uncomment to disable out-of-memory exceptions +// Use non-exception-throwing operator new #define BLARGG_DISABLE_NOTHROW -// You can have the library use your own custom Data_Reader -//#define GME_FILE_READER Gzip_File_Reader -#include "Vfs_File.h" +// Have GME use VFS file access #define GME_FILE_READER Vfs_File_Reader +#define GME_FILE_READER_INCLUDE "Vfs_File.h" #endif
--- a/src/console/blargg_endian.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/blargg_endian.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ // CPU Byte Order Utilities -// Game_Music_Emu 0.5.1 +// Game_Music_Emu 0.5.2 #ifndef BLARGG_ENDIAN #define BLARGG_ENDIAN
--- a/src/console/gb_cpu_io.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/gb_cpu_io.h Thu Dec 07 15:20:41 2006 -0800 @@ -26,7 +26,10 @@ if ( (addr ^ 0xE000) <= 0x1F80 - 1 ) { if ( unsigned (addr - Gb_Apu::start_addr) < Gb_Apu::register_count ) + { + GME_APU_HOOK( this, addr - Gb_Apu::start_addr, data ); apu.write_register( clock(), addr, data ); + } else if ( (addr ^ 0xFF06) < 2 ) update_timer(); else if ( addr == joypad_addr )
--- a/src/console/gme.cxx Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/gme.cxx Thu Dec 07 15:20:41 2006 -0800 @@ -1,8 +1,10 @@ -// Game_Music_Emu 0.5.1. http://www.slack.net/~ant/ +// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ #include "Music_Emu.h" +#if !GME_DISABLE_STEREO_DEPTH #include "Effects_Buffer.h" +#endif #include "blargg_endian.h" #include <string.h> #include <ctype.h> @@ -20,6 +22,32 @@ #include "blargg_source.h" +#ifndef GME_TYPE_LIST + +// Default list of all supported game music types (copy this to blargg_config.h +// if you want to modify it) +#define GME_TYPE_LIST \ + gme_ay_type,\ + gme_gbs_type,\ + gme_gym_type,\ + gme_hes_type,\ + gme_kss_type,\ + gme_nsf_type,\ + gme_nsfe_type,\ + gme_sap_type,\ + gme_spc_type,\ + gme_vgm_type,\ + gme_vgz_type + +#endif + +static gme_type_t const gme_type_list_ [] = { GME_TYPE_LIST, 0 }; + +gme_type_t const* gme_type_list() +{ + return gme_type_list_; +} + const char* gme_identify_header( void const* header ) { switch ( get_be32( header ) ) @@ -49,7 +77,7 @@ *out = 0; // extension too long } -gme_type_t gme_identify_extension( const char* extension_, gme_type_t const* types ) +gme_type_t gme_identify_extension( const char* extension_ ) { char const* end = strrchr( extension_, '.' ); if ( end ) @@ -58,33 +86,99 @@ char extension [6]; to_uppercase( extension_, sizeof extension, extension ); - for ( ; *types; types++ ) + for ( gme_type_t const* types = gme_type_list_; *types; types++ ) if ( !strcmp( extension, (*types)->extension_ ) ) return *types; return 0; } -gme_err_t gme_identify_file( const char* path, gme_type_t const* types, gme_type_t* type_out ) +gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ) { - *type_out = gme_identify_extension( path, types ); + *type_out = gme_identify_extension( path ); + // TODO: don't examine header if file has extension? if ( !*type_out ) { - char header [4] = { }; + char header [4]; GME_FILE_READER in; RETURN_ERR( in.open( path ) ); RETURN_ERR( in.read( header, sizeof header ) ); - *type_out = gme_identify_extension( gme_identify_header( header ), types ); + *type_out = gme_identify_extension( gme_identify_header( header ) ); } return 0; } +gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, long sample_rate ) +{ + require( (data || !size) && out ); + *out = 0; + + gme_type_t file_type = 0; + if ( size >= 4 ) + file_type = gme_identify_extension( gme_identify_header( data ) ); + if ( !file_type ) + return gme_wrong_file_type; + + Music_Emu* emu = gme_new_emu( file_type, sample_rate ); + CHECK_ALLOC( emu ); + + gme_err_t err = gme_load_data( emu, data, size ); + + if ( err ) + delete emu; + else + *out = emu; + + return err; +} + +gme_err_t gme_open_file( const char* path, Music_Emu** out, long sample_rate ) +{ + require( path && out ); + *out = 0; + + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + + char header [4]; + int header_size = 0; + + gme_type_t file_type = gme_identify_extension( path ); + if ( !file_type ) + { + header_size = sizeof header; + RETURN_ERR( in.read( header, sizeof header ) ); + file_type = gme_identify_extension( gme_identify_header( header ) ); + } + if ( !file_type ) + return gme_wrong_file_type; + + Music_Emu* emu = gme_new_emu( file_type, sample_rate ); + CHECK_ALLOC( emu ); + + // optimization: avoids seeking/re-reading header + Remaining_Reader rem( header, header_size, &in ); + gme_err_t err = emu->load( rem ); + in.close(); + + if ( err ) + delete emu; + else + *out = emu; + + return err; +} + Music_Emu* gme_new_emu( gme_type_t type, long rate ) { if ( type ) { + if ( rate == gme_info_only ) + return type->new_info(); + Music_Emu* me = type->new_emu(); if ( me ) { + #if !GME_DISABLE_STEREO_DEPTH if ( type->flags_ & 1 ) { me->effects_buffer = BLARGG_NEW Effects_Buffer; @@ -93,6 +187,7 @@ } if ( !(type->flags_ & 1) || me->effects_buffer ) + #endif { if ( !me->set_sample_rate( rate ) ) { @@ -106,17 +201,9 @@ return 0; } -Music_Emu* gme_new_info( gme_type_t type ) -{ - if ( !type ) - return 0; - - return type->new_info(); -} +gme_err_t gme_load_file( Music_Emu* me, const char* path ) { return me->load_file( path ); } -const char* gme_load_file( Music_Emu* me, const char* path ) { return me->load_file( path ); } - -const char* gme_load_data( Music_Emu* me, const char* data, long size ) +gme_err_t gme_load_data( Music_Emu* me, void const* data, long size ) { Mem_File_Reader in( data, size ); return me->load( in ); @@ -143,34 +230,27 @@ void gme_set_stereo_depth( Music_Emu* me, double depth ) { +#if !GME_DISABLE_STEREO_DEPTH if ( me->effects_buffer ) STATIC_CAST(Effects_Buffer*,me->effects_buffer)->set_depth( depth ); +#endif } -gme_err_t gme_start_track( Music_Emu* me, int index ) { return me->start_track( index ); } - -gme_err_t gme_play( Music_Emu* me, long n, short* p ) { return me->play( n, p ); } - -void gme_set_fade( Music_Emu* me, long start_msec ) { me->set_fade( start_msec ); } - -int gme_track_ended( Music_Emu const* me ) { return me->track_ended(); } - -long gme_tell( Music_Emu const* me ) { return me->tell(); } - -gme_err_t gme_seek( Music_Emu* me, long msec ) { return me->seek( msec ); } - -int gme_voice_count( Music_Emu const* me ) { return me->voice_count(); } +void* gme_user_data ( Music_Emu const* me ) { return me->user_data(); } +void gme_set_user_data ( Music_Emu* me, void* new_user_data ) { me->set_user_data( new_user_data ); } +void gme_set_user_cleanup(Music_Emu* me, gme_user_cleanup_t func ) { me->set_user_cleanup( func ); } -const char** gme_voice_names( Music_Emu const* me ) { return me->voice_names(); } - -void gme_ignore_silence( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); } - -void gme_set_tempo( Music_Emu* me, double t ) { me->set_tempo( t ); } - -void gme_mute_voice( Music_Emu* me, int index, int mute ) { me->mute_voice( index, mute != 0 ); } - -void gme_mute_voices( Music_Emu* me, int mask ) { me->mute_voices( mask ); } - -gme_equalizer_t gme_equalizer( Music_Emu const* me ) { return me->equalizer(); } - -void gme_set_equalizer( Music_Emu* me, gme_equalizer_t const* eq ) { me->set_equalizer( *eq ); } +gme_err_t gme_start_track ( Music_Emu* me, int index ) { return me->start_track( index ); } +gme_err_t gme_play ( Music_Emu* me, long n, short* p ) { return me->play( n, p ); } +void gme_set_fade ( Music_Emu* me, long start_msec ) { me->set_fade( start_msec ); } +int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); } +long gme_tell ( Music_Emu const* me ) { return me->tell(); } +gme_err_t gme_seek ( Music_Emu* me, long msec ) { return me->seek( msec ); } +int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); } +void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); } +void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); } +void gme_mute_voice ( Music_Emu* me, int index, int mute ) { me->mute_voice( index, mute != 0 ); } +void gme_mute_voices ( Music_Emu* me, int mask ) { me->mute_voices( mask ); } +void gme_set_equalizer ( Music_Emu* me, gme_equalizer_t const* eq ) { me->set_equalizer( *eq ); } +gme_equalizer_t gme_equalizer( Music_Emu const* me ) { return me->equalizer(); } +const char** gme_voice_names ( Music_Emu const* me ) { return me->voice_names(); }
--- a/src/console/gme.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/gme.h Thu Dec 07 15:20:41 2006 -0800 @@ -1,6 +1,6 @@ /* Game music emulator library C interface (also usable from C++) */ -/* Game_Music_Emu 0.5.1 */ +/* Game_Music_Emu 0.5.2 */ #ifndef GME_H #define GME_H @@ -15,101 +15,61 @@ typedef struct Music_Emu Music_Emu; -/******** Game music types ********/ - -/* Emulator type constants for each supported file type */ -extern struct gme_type_t_ const gme_ay_type [], gme_gbs_type [], gme_gym_type [], - gme_hes_type [], gme_kss_type [], gme_nsf_type [], gme_nsfe_type [], - gme_sap_type [], gme_spc_type [], gme_vgm_type [], gme_vgz_type []; -typedef struct gme_type_t_ const* gme_type_t; +/******** Basic operations ********/ -/* Determine likely game music type based on first four bytes of file. Returns -string containing proper file suffix (i.e. "NSF", "SPC", etc.) or "" if -file header is not recognized. */ -const char* gme_identify_header( void const* header ); - -/* Pointer to array of music types, with NULL entry at end. Allows a player linked -to this library to support new music types without having to be updated. */ -gme_type_t const* gme_type_list(); - -/* Get corresponding music type for file extension passed in. Types points to -an array of gme_type_t elements with a NULL terminator at the end. */ -gme_type_t gme_identify_extension( const char* extension, gme_type_t const* types ); - -/* Determine file type based on file's extension or header (if extension isn't recognized) */ -gme_err_t gme_identify_file( const char* path, gme_type_t const* types, gme_type_t* type_out ); +/* Create emulator and load game music file/data into it. Sets *out to new emulator. */ +gme_err_t gme_open_file( const char* path, Music_Emu** out, long sample_rate ); -/* gme_type_t is a pointer to this structure. For example, gme_nsf_type->system is -"Nintendo NES" and gme_nsf_type->new_emu() is equilvant to new Nsf_Emu (in C++). */ -struct gme_type_t_ -{ - const char* system; /* name of system this music file type is generally for */ - int track_count; /* non-zero for formats with a fixed number of tracks */ - Music_Emu* (*new_emu)(); /* Create new emulator for this type (useful in C++ only) */ - Music_Emu* (*new_info)(); /* Create new info reader for this type */ - - /* internal */ - const char* extension_; - int flags_; -}; - +/* Number of tracks available */ +int gme_track_count( Music_Emu const* ); -/******** Emulator creation/cleanup ********/ - -/* Create new emulator and set sample rate. Returns NULL if out of memory. */ -Music_Emu* gme_new_emu( gme_type_t, long sample_rate ); +/* Start a track, where 0 is the first track */ +gme_err_t gme_start_track( Music_Emu*, int index ); -/* Create new music file info reader. Same as gme_new_emu() except it does not -support playback functions. It *does* support an m3u playlist. */ -Music_Emu* gme_new_info( gme_type_t ); - -/* Error returned if file is wrong type */ -extern const char gme_wrong_file_type []; - -/* Type of this emulator */ -gme_type_t gme_type( Music_Emu const* ); +/* Generate 'count' 16-bit signed samples info 'out'. Output is in stereo. */ +gme_err_t gme_play( Music_Emu*, long count, short* out ); /* Finish using emulator and free memory */ void gme_delete( Music_Emu* ); -/******** File loading ********/ +/******** Track position/length ********/ + +/* Set time to start fading track out. Once fade ends track_ended() returns true. +Fade time can be changed while track is playing. */ +void gme_set_fade( Music_Emu*, long start_msec ); -/* Load music file */ -gme_err_t gme_load_file( Music_Emu*, const char* path ); +/* True if a track has reached its end */ +int gme_track_ended( Music_Emu const* ); + +/* Number of milliseconds (1000 = one second) played since beginning of track */ +long gme_tell( Music_Emu const* ); -/* Load music file from memory. Makes a copy of data passed. */ -gme_err_t gme_load_data( Music_Emu*, const void* data, long size ); +/* Seek to new time in track. Seeking backwards or far forward can take a while. */ +gme_err_t gme_seek( Music_Emu*, long msec ); + + +/******** Informational ********/ -/* Load music file using custom data reader function that will be called to -read file data. Most emulators load the entire file in one call. */ -typedef gme_err_t (*gme_reader_t)( void* your_data, void* out, long count ); -gme_err_t gme_load_custom( Music_Emu*, gme_reader_t, long file_size, void* your_data ); +/* If you only need track information from a music file, pass gme_info_only for +sample_rate to open/load. */ +enum { gme_info_only = -1 }; -/* Load m3u playlist file (must be done after loading file) */ +/* Most recent warning string, or NULL if none. Clears current warning after returning. +Warning is also cleared when loading a file and starting a track. */ +const char* gme_warning( Music_Emu* ); + +/* Load m3u playlist file (must be done after loading music) */ gme_err_t gme_load_m3u( Music_Emu*, const char* path ); -/* Load m3u playlist file from memory (must be done after loading file) */ -gme_err_t gme_load_m3u_data( Music_Emu*, const void* data, long size ); - -/* Clears any loaded m3u playlist and any internal playlist that the music format +/* Clear any loaded m3u playlist and any internal playlist that the music format supports (NSFE for example). */ void gme_clear_playlist( Music_Emu* ); -/* Most recent warning string, or NULL if none. Clears current warning after returning. */ -const char* gme_warning( Music_Emu* ); - - -/******** Track information ********/ - -/* Number of tracks in file/playlist */ -int gme_track_count( Music_Emu const* ); - /* Get information for a particular track (length, name, author, etc.) */ typedef struct track_info_t track_info_t; gme_err_t gme_track_info( Music_Emu const*, track_info_t* out, int track ); -enum { gme_max_field = 255 }; struct track_info_t { long track_count; @@ -128,29 +88,7 @@ char comment [256]; char dumper [256]; }; - - -/******** Basic playback ********/ - -/* Start a track, where 0 is the first track. Also clears warning string. */ -gme_err_t gme_start_track( Music_Emu*, int index ); - -/* Set start time and length of track fade out. Once fade ends track_ended() returns -true. Fade time can be changed while track is playing. */ -void gme_set_fade( Music_Emu*, long start_msec ); - -/* Generate 'count' samples info 'buf'. Output is in stereo. Any emulation -errors set warning string, and major errors also end track. */ -gme_err_t gme_play( Music_Emu*, long sample_count, short* out ); - -/* True if a track has reached its end */ -int gme_track_ended( Music_Emu const* ); - -/* Number of milliseconds (1000 msec = 1 second) played since beginning of track */ -long gme_tell( Music_Emu const* ); - -/* Seek to new time in track. Seeking backwards or far forward can take a while. */ -gme_err_t gme_seek( Music_Emu*, long msec ); +enum { gme_max_field = 255 }; /******** Advanced playback ********/ @@ -194,6 +132,89 @@ void gme_set_equalizer( Music_Emu*, gme_equalizer_t const* eq ); + +/******** Game music types ********/ + +/* Emulator type constants for each supported file type */ +extern struct gme_type_t_ const gme_ay_type [], gme_gbs_type [], gme_gym_type [], + gme_hes_type [], gme_kss_type [], gme_nsf_type [], gme_nsfe_type [], + gme_sap_type [], gme_spc_type [], gme_vgm_type [], gme_vgz_type []; +typedef struct gme_type_t_ const* gme_type_t; + +/* Type of this emulator */ +gme_type_t gme_type( Music_Emu const* ); + +/* gme_type_t is a pointer to this structure. For example, gme_nsf_type->system is +"Nintendo NES" and gme_nsf_type->new_emu() is equilvant to new Nsf_Emu (in C++). */ +struct gme_type_t_ +{ + const char* system; /* name of system this music file type is generally for */ + int track_count; /* non-zero for formats with a fixed number of tracks */ + Music_Emu* (*new_emu)(); /* Create new emulator for this type (useful in C++ only) */ + Music_Emu* (*new_info)(); /* Create new info reader for this type */ + + /* internal */ + const char* extension_; + int flags_; +}; + +/* Pointer to array of all music types, with NULL entry at end. Allows a player linked +to this library to support new music types without having to be updated. */ +gme_type_t const* gme_type_list(); + + +/******** Advanced file loading ********/ + +/* Error returned if file type is not supported */ +extern const char gme_wrong_file_type []; + +/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. */ +gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, long sample_rate ); + +/* Determine likely game music type based on first four bytes of file. Returns +string containing proper file suffix (i.e. "NSF", "SPC", etc.) or "" if +file header is not recognized. */ +const char* gme_identify_header( void const* header ); + +/* Get corresponding music type for file path or extension passed in. */ +gme_type_t gme_identify_extension( const char* path_or_extension ); + +/* Determine file type based on file's extension or header (if extension isn't recognized). +Sets *type_out to type, or 0 if unrecognized or error. */ +gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ); + +/* Create new emulator and set sample rate. Returns NULL if out of memory. If you only need +track information, pass gme_info_only for sample_rate. */ +Music_Emu* gme_new_emu( gme_type_t, long sample_rate ); + +/* Load music file into emulator */ +gme_err_t gme_load_file( Music_Emu*, const char* path ); + +/* Load music file from memory into emulator. Makes a copy of data passed. */ +gme_err_t gme_load_data( Music_Emu*, void const* data, long size ); + +/* Load music file using custom data reader function that will be called to +read file data. Most emulators load the entire file in one read call. */ +typedef gme_err_t (*gme_reader_t)( void* your_data, void* out, long count ); +gme_err_t gme_load_custom( Music_Emu*, gme_reader_t, long file_size, void* your_data ); + +/* Load m3u playlist file from memory (must be done after loading music) */ +gme_err_t gme_load_m3u_data( Music_Emu*, void const* data, long size ); + + +/******** User data ********/ + +/* Set/get pointer to data you want to associate with this emulator. +You can use this for whatever you want. */ +void gme_set_user_data( Music_Emu*, void* new_user_data ); +void* gme_user_data( Music_Emu const* ); + +/* Register cleanup function to be called when deleting emulator, or NULL to +clear it. Passes user_data to cleanup function. */ +typedef void (*gme_user_cleanup_t)( void* user_data ); +void gme_set_user_cleanup( Music_Emu*, gme_user_cleanup_t func ); + + #ifdef __cplusplus } #endif
--- a/src/console/gme_design.txt Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/gme_design.txt Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -Game_Music_Emu 0.5.1 Design +Game_Music_Emu 0.5.2 Design --------------------------- Architecture
--- a/src/console/gme_notes.txt Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/gme_notes.txt Thu Dec 07 15:20:41 2006 -0800 @@ -1,4 +1,4 @@ -Game_Music_Emu 0.5.1 +Game_Music_Emu 0.5.2 -------------------- Author : Shay Green <gblargg@gmail.com> Website: http://www.slack.net/~ant/libs/ @@ -31,6 +31,7 @@ interface. Each can load a game music file and play from any track. To play a track from a game music file, do the following: +** TODO: things are simpler now * Determine file's type (i.e. gme_nsf_emu, gme_spc_emu, etc.) * Create appropriate emulator and set sample rate with gme_new_emu() * Load file into emulator with gme_load()
--- a/src/console/gme_readme.txt Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/gme_readme.txt Thu Dec 07 15:20:41 2006 -0800 @@ -1,9 +1,9 @@ -Game_Music_Emu 0.5.1: Game Music Emulators +Game_Music_Emu 0.5.2: Game Music Emulators ------------------------------------------ Game_Music_Emu is a collection of video game music file emulators that support the following formats and systems: -AY Sinclair Spectrum +AY ZX Spectrum/Amstrad CPC GBS Nintendo Game Boy GYM Sega Genesis/Mega Drive HES NEC TurboGrafx-16/PC Engine @@ -79,7 +79,6 @@ gme.h C interface gme.cpp - gme_type_list.cpp gme_type_list() support Gme_File.h File loading and track information Music_Emu.h Track playback and adjustments @@ -91,7 +90,7 @@ M3u_Playlist.h M3U playlist support M3u_Playlist.cpp - Ay_Emu.h Sinclair Spectrum AY emulator + Ay_Emu.h ZX Spectrum AY emulator Ay_Emu.cpp Ay_Apu.cpp Ay_Apu.h
--- a/src/console/nes_cpu_io.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/nes_cpu_io.h Thu Dec 07 15:20:41 2006 -0800 @@ -61,6 +61,7 @@ if ( unsigned (addr - Nes_Apu::start_addr) <= Nes_Apu::end_addr - Nes_Apu::start_addr ) { + GME_APU_HOOK( this, addr - Nes_Apu::start_addr, data ); apu.write_register( cpu::time(), addr, data ); return; } @@ -68,10 +69,10 @@ unsigned bank = addr - bank_select_addr; if ( bank < bank_count ) { - blargg_long addr = rom.mask_addr( data * (blargg_long) bank_size ); - if ( addr >= rom.size() ) + blargg_long offset = rom.mask_addr( data * (blargg_long) bank_size ); + if ( offset >= rom.size() ) set_warning( "Invalid bank" ); - cpu::map_code( (bank + 8) * bank_size, bank_size, rom.at_addr( addr ) ); + cpu::map_code( (bank + 8) * bank_size, bank_size, rom.at_addr( offset ) ); return; }
--- a/src/console/readme.txt Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/readme.txt Thu Dec 07 15:20:41 2006 -0800 @@ -4,7 +4,7 @@ 1990s. It uses the Game_Music_Emu sound engine and supports the following file formats: -AY Sinclair Spectrum +AY ZX Spectrum/Amstrad CPC GBS Nintendo Game Boy GYM Sega Genesis/Mega Drive HES NEC TurboGrafx-16/PC Engine @@ -43,18 +43,15 @@ Things not supported -------------------- -* Gzipped files (Audacious needs to support these in its VFS layer) - * KSS: FM sound -* GYM: files without a header - * HES: ADPCM samples (used in only a few soundtracks, if that) * SAP: "digimusic samples" and more obscure tracker formats * VGM/VGZ: original Sega Master System FM sound chip (Sega Genesis/Mega -Drive music plays fine) +Drive music plays fine). See the following discussion for a way to add +support for this: http://www.smspower.org/forums/viewtopic.php?t=9321 Source code notes @@ -65,21 +62,13 @@ * See TODO comments in Audacious_Config.cxx and Audacious_Driver.cxx for things which would be good to address. -* The library does not use C++ exceptions, so you could disable them in -the makefile to reduce code size a bit. - -* The cause of errors and also warnings about possible problems are -logged in log_err() and log_warning() using printf(). +* C++ exceptions and RTTI are not used or needed. -* The vfs_* functions are used for file access, so gzipped game music -files must currently be decompressed before playback (in particular, -.vgz files). - -* File types are determined based on the first four bytes of a file; the -file extension is never examined. Some .gym files don't have any header, -so these won't play. +* File types are determined based on the first four bytes of a file +except for .gym files, some of which have no file header at all. * Some music formats have more than one track in a file. This track is specified to the console plugin by appending ?i to the end of the file path, where i is the track index, starting at 0. For example, foo.nsf?2 -specifies the third track. +specifies the third track. This is an internal thing, as the user should +never see these modified paths.
--- a/src/console/sap_cpu_io.h Wed Dec 06 07:57:05 2006 -0800 +++ b/src/console/sap_cpu_io.h Thu Dec 07 15:20:41 2006 -0800 @@ -7,7 +7,7 @@ void Sap_Emu::cpu_write( sap_addr_t addr, int data ) { - mem [addr] = data; + mem.ram [addr] = data; if ( (addr >> 8) == 0xD2 ) cpu_write_( addr, data ); } @@ -21,6 +21,6 @@ { if ( (addr & 0xF900) == 0xD000 ) dprintf( "Unmapped read $%04X\n", addr ); - return mem [addr]; + return mem.ram [addr]; } #endif