Commits

Anonymous committed 1acd3de

Import version 0.5.2

Comments (0)

Files changed (116)

+Game_Music_Emu Change Log
+-------------------------
+
+Game_Music_Emu 0.5.2
+--------------------
+- *TONS* of changes and improvements. You should re-read the new header
+files and documentation as the changes will allow you to simplify your
+code a lot (it might even be simpler to just rewrite it). Existing code
+should continue to work without changes in most cases (see Deprecated
+features in gme.txt).
+
+- New file formats: AY, HES, KSS, SAP, NSFE
+
+- All-new comprehensive C interface (also usable from C++). Simplifies
+many things, especially file loading, and brings everything together in
+one header file (gme.h).
+
+- Information tags and track names and times can be accessed for all
+game music formats
+
+- New features supported by all emulators: end of track fading,
+automatic silence detection, adjustable song tempo, seek to new time in
+track
+
+- Updated mini player example to support track names and times, echo,
+tempo, and channel muting, and added visual waveform display
+
+- Improved configuration to use blargg_config.h, which you can modify
+and keep when you update to a newer libary version. Includes flag for
+library to automatically handle gzipped files using zlib (so you don't
+need to use Gzip_File_Reader anymore).
+
+- GBS: Fixed wave channel to not reset waveform when APU is powered off
+(affected Garfield). Also improved invalid bank selection (affected Game
+& Watch and others).
+
+- VGM: Added support for alternate noise shifter register
+configurations, used by other systems like the BBC Micro.
+
+- SPC: Removed IPL ROM dump from emulator, as none of the SPC files I
+scanned needed it, and an SPC file can include a copy if necessary. Also
+re-enabled supposed clamping in gaussian interpolation between the third
+and fourth lookups, though I don't know whether it matters
+
+- Added Music_Emu::load_mem() to use music data already in memory
+(without copying it)
+
+- Added Music_Emu::warning(), which reports minor problems when loading
+and playing a music file
+
+- Added Music_Emu::set_gain() for uniform adjustment of gain. Can only
+be set during initialization, so not useful as a general volume control.
+
+- Added custom operator new to ensure that no exceptions are thrown in
+the library (I'd use std::nothrow if it were part of pre-ISO (ARM) C++)
+
+- Added BLIP_BUFFER_FAST flag to blargg_config.h to use a lower quality
+bandlimited synthesis in "classic" emulators, which might help
+performance on ancient processors (measure first!). Don't use this
+unless absolutely necessary, as quality suffers.
+
+- Improved performance a bit for x86 platforms
+
+- Text files now in DOS newline format so they will open in Notepad
+properly
+
+- Removed requirement that file header structures not have any padding
+added to the end
+
+- Fixed common bug in all CPU emulators where negative program counter
+could crash emulator (occurred during a negative branch from the
+beginning of memory). Also fixed related bug in Z80 emulator for
+IX/IY+displacement mode.
+
+- Eliminated all warnings when compiling on gcc 4.0. The following
+generates no diagnostics:
+
+	gcc -S gme/*.cpp -o /dev/null -ansi -fno-gnu-keywords
+	-fno-nonansi-builtins -pedantic -W -Wabi -Wall -Wcast-align
+	-Wcast-qual -Wchar-subscripts -Wdisabled-optimization -Werror
+	-Winline -Wlong-long -Wmultichar -Winvalid-offsetof
+	-Wnon-virtual-dtor -Woverloaded-virtual -Wparentheses
+	-Wpointer-arith -Wredundant-decls -Wreorder -Wsign-compare
+	-Wsign-promo -Wunknown-pragmas -Wwrite-strings
+
+
+Game_Music_Emu 0.3.0
+--------------------
+- Added more demos, including music player using the SDL multimedia
+library for sound, and improved documentation
+
+- All: Improved interface to emulators to allow simpler setup and
+loading. Instead of various init() functions, all now support
+set_sample_rate( long rate ) and load( const char* file_path ).
+
+- All: Removed error return from start_track() and play(), and added
+error_count() to get the total number of emulation errors since the
+track was last started. See demos for examples of new usage.
+
+- All: Fixed mute_voices() muting to be preserved after loading files
+and starting tracks, instead of being cleared as it was whenever a track
+was started
+
+- VGM: Rewrote Vgm_Emu to support Sega Genesis/Mega Drive FM sound at
+any sample rate with optional FM oversampling, support for alternate
+YM2612 sound cores, and support for optional YM2413
+
+- VGM: Added tempo control, useful for slowing 60Hz NTSC Sega Genesis
+music to 50Hz PAL
+
+- VGM: Removed Vgm_Emu::track_data(), since I realized that this
+information is already present in the VGM header (oops!)
+
+- GYM: Changed Gym_Emu::track_length() operation (see Gym_Emu.h)
+
+- NSF: Added support for Sunsoft FME-7 sound chip used by Gimmick
+soundtrack
+
+- NSF: Fixed Namco 106 problems with Final Lap and others
+
+- Moved library sources to gme/ directory to reduce clutter, and merged
+boost/ functionality into blargg_common.h
+
+- Added Gzip_File_Reader for transparently using gzipped files
+
+
+Game_Music_Emu 0.2.4
+--------------------
+- Created a discussion forum for problems and feedback:
+http://groups-beta.google.com/group/blargg-sound-libs
+
+- Changed error return value of Blip_Buffer::sample_rate() (also for
+Stereo_Buffer, Effects_Buffer, etc.) to blargg_err_t (defined in
+blargg_common.h), to make error reporting consistent with other
+functions. This means the "no error" return value is the opposite of
+what it was before, which will break current code which checks the error
+return value:
+
+	// current code (broken)
+	if ( !buf.sample_rate( samples_per_sec ) )
+		out_of_memory();
+	
+	// quick-and-dirty fix (just remove the ! operation)
+	if ( buf.sample_rate( samples_per_sec ) )
+		out_of_memory();
+	
+	// proper fix
+	blargg_err_t error = buf.sample_rate( samples_per_sec );
+	if ( error )
+		report_error( error );
+
+- Implemented workaround for MSVC++ 6 compiler limitations, allowing it
+to work on that compiler again
+
+- Added sample clamping to avoid wrap-around at high volumes, allowing
+higher volume with little distortion
+
+- Added to-do list and design notes
+
+- Added Music_Emu::skip( long sample_count ) to skip ahead in current
+track
+
+- Added Gym_Emu::track_length() and Vgm_Emu::track_length() for
+determining the length of non-looped GYM and VGM files
+
+- Partially implemented DMC non-linearity when its value is directly set
+using $4011, which reduces previously over-emphasized "popping" of
+percussion on some games (TMNT II in particular)
+
+- Fixed Fir_Resampler, used for SPC and GYM playback (was incorrectly
+using abs() instead of fabs()...argh)
+
+- Fixed SPC emulation bugs: eliminated clicks in Plok! soundtrack and
+now stops sample slightly earlier than the end, as the SNES does. Fixed
+a totally broken CPU addressing mode.
+
+- Fixed Konami VRC6 saw wave (was very broken before). Now VRC6 music
+sounds decent
+
+- Fixed a minor GBS emulation bug
+
+- Fixed GYM loop point bug when track was restarted before loop point
+had been reached
+
+- Made default GBS frequency equalization less muffled
+
+- Added pseudo-surround effect removal for SPC files
+
+- Added Music_Emu::voice_names() which returns names for each voice.
+
+- Added BLARGG_SOURCE_BEGIN which allows custom compiler options to be
+easily set for library sources
+
+- Changed assignment of expansion sound chips in Nsf_Emu to be spread
+more evenly when using Effects_Buffer
+
+- Changed 'size_t' values in Blip_Buffer interface to 'long'
+
+- Changed demo to generate a WAVE sound file rather than an AIFF file
+
+
+Game_Music_Emu 0.2.0
+--------------------
+- Redid framework and rewrote/cleaned up emulators
+
+- Changed licensing to GNU Lesser General Public License (LGPL)
+
+- Added Sega Genesis GYM and Super Nintendo SPC emulators
+
+- Added Namco-106 and Konami VRC6 sound chip support to NSF emulator
+
+- Eliminated use of static mutable data in emulators, allowing
+multi-instance safety
+
+
+Game_Music_Emu 0.1.0
+--------------------
+- First release

demo/Wave_Writer.cpp

+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "Wave_Writer.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+/* Copyright (C) 2003-2006 by Shay Green. Permission is hereby granted, free
+of charge, to any person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+to permit persons to whom the Software is furnished to do so, subject to the
+following conditions: The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software. THE
+SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+
+const int header_size = 0x2C;
+
+static void exit_with_error( const char* str )
+{
+	printf( "Error: %s\n", str ); getchar();
+	exit( EXIT_FAILURE );
+}
+
+Wave_Writer::Wave_Writer( long sample_rate, const char* filename )
+{
+	sample_count_ = 0;
+	rate = sample_rate;
+	buf_pos = header_size;
+	chan_count = 1;
+	
+	buf = (unsigned char*) malloc( buf_size * sizeof *buf );
+	if ( !buf )
+		exit_with_error( "Out of memory" );
+	
+	file = fopen( filename, "wb" );
+	if ( !file )
+		exit_with_error( "Couldn't open WAVE file for writing" );
+	
+	setvbuf( file, 0, _IOFBF, 32 * 1024L );
+}
+
+void Wave_Writer::flush()
+{
+	if ( buf_pos && !fwrite( buf, buf_pos, 1, file ) )
+		exit_with_error( "Couldn't write WAVE data" );
+	buf_pos = 0;
+}
+
+void Wave_Writer::write( const sample_t* in, long remain, int skip )
+{
+	sample_count_ += remain;
+	while ( remain )
+	{
+		if ( buf_pos >= buf_size )
+			flush();
+		
+		long n = (buf_size - buf_pos) / sizeof (sample_t);
+		if ( n > remain )
+			n = remain;
+		remain -= n;
+		
+		// convert to lsb first format
+		unsigned char* p = &buf [buf_pos];
+		while ( n-- )
+		{
+			int s = *in;
+			in += skip;
+			*p++ = (unsigned char) s;
+			*p++ = (unsigned char) (s >> 8);
+		}
+		
+		buf_pos = p - buf;
+		assert( buf_pos <= buf_size );
+	}
+}
+
+
+void Wave_Writer::write( const float* in, long remain, int skip )
+{
+	sample_count_ += remain;
+	while ( remain )
+	{
+		if ( buf_pos >= buf_size )
+			flush();
+		
+		long n = (buf_size - buf_pos) / sizeof (sample_t);
+		if ( n > remain )
+			n = remain;
+		remain -= n;
+		
+		// convert to lsb first format
+		unsigned char* p = &buf [buf_pos];
+		while ( n-- )
+		{
+			long s = (long) (*in * 0x7FFF);
+			in += skip;
+			if ( (short) s != s )
+				s = 0x7FFF - (s >> 24); // clamp to 16 bits
+			*p++ = (unsigned char) s;
+			*p++ = (unsigned char) (s >> 8);
+		}
+		
+		buf_pos = p - buf;
+		assert( buf_pos <= buf_size );
+	}
+}
+
+void Wave_Writer::close()
+{
+	if ( file )
+	{
+		flush();
+		
+		// generate header
+		long ds = sample_count_ * sizeof (sample_t);
+		long rs = header_size - 8 + ds;
+		int frame_size = chan_count * sizeof (sample_t);
+		long bps = rate * frame_size;
+		unsigned char header [header_size] =
+		{
+			'R','I','F','F',
+			rs,rs>>8,           // length of rest of file
+			rs>>16,rs>>24,
+			'W','A','V','E',
+			'f','m','t',' ',
+			0x10,0,0,0,         // size of fmt chunk
+			1,0,                // uncompressed format
+			chan_count,0,       // channel count
+			rate,rate >> 8,     // sample rate
+			rate>>16,rate>>24,
+			bps,bps>>8,         // bytes per second
+			bps>>16,bps>>24,
+			frame_size,0,       // bytes per sample frame
+			16,0,               // bits per sample
+			'd','a','t','a',
+			ds,ds>>8,ds>>16,ds>>24// size of sample data
+			// ...              // sample data
+		};
+		
+		// write header
+		fseek( file, 0, SEEK_SET );
+		fwrite( header, sizeof header, 1, file );
+		
+		fclose( file );
+		file = 0;
+		free( buf );
+	}
+}
+
+Wave_Writer::~Wave_Writer()
+{
+	close();
+}
+
+// C interface
+
+static Wave_Writer* ww;
+
+void wave_open( long sample_rate, const char* filename )
+{
+	ww = new Wave_Writer( sample_rate, filename );
+	assert( ww );
+}
+
+void wave_enable_stereo() { ww->enable_stereo(); }
+
+long wave_sample_count() { return ww->sample_count(); }
+ 
+void wave_write( const short* buf, long count ) { ww->write( buf, count ); }
+
+void wave_close()
+{
+	delete ww;
+	ww = 0;
+}

demo/Wave_Writer.h

+/* WAVE sound file writer for recording 16-bit output during program development */
+
+#ifndef WAVE_WRITER_H
+#define WAVE_WRITER_H
+
+#ifdef __cplusplus
+	extern "C" {
+#endif
+
+/* C interface */
+void wave_open( long sample_rate, const char* filename );
+void wave_enable_stereo( void );
+void wave_write( const short* buf, long count );
+long wave_sample_count( void );
+void wave_close( void );
+
+#ifdef __cplusplus
+	}
+#endif
+
+#ifdef __cplusplus
+#include <stddef.h>
+#include <stdio.h>
+
+/* C++ interface */
+class Wave_Writer {
+public:
+	typedef short sample_t;
+	
+	// Create sound file with given sample rate (in Hz) and filename.
+	// Exits program if there's an error.
+	Wave_Writer( long sample_rate, char const* filename = "out.wav" );
+	
+	// Enable stereo output
+	void enable_stereo();
+	
+	// Append 'count' samples to file. Use every 'skip'th source sample; allows
+	// one channel of stereo sample pairs to be written by specifying a skip of 2.
+	void write( const sample_t*, long count, int skip = 1 );
+	
+	// Append 'count' floating-point samples to file. Use every 'skip'th source sample;
+	// allows one channel of stereo sample pairs to be written by specifying a skip of 2.
+	void write( const float*, long count, int skip = 1 );
+	
+	// Number of samples written so far
+	long sample_count() const;
+	
+	// Finish writing sound file and close it
+	void close();
+	
+	~Wave_Writer();
+public:
+	// Deprecated
+	void stereo( bool b ) { chan_count = b ? 2 : 1; }
+private:
+	enum { buf_size = 32768 * 2 };
+	unsigned char* buf;
+	FILE*   file;
+	long    sample_count_;
+	long    rate;
+	long    buf_pos;
+	int     chan_count;
+	
+	void flush();
+};
+
+inline void Wave_Writer::enable_stereo() { chan_count = 2; }
+
+inline long Wave_Writer::sample_count() const { return sample_count_; }
+
+#endif
+
+#endif
+/* C example that opens a game music file and records 10 seconds to "out.wav" */
+
+static char filename [] = "test.nsf"; /* opens this file (can be any music type) */
+
+#include "gme/gme.h"
+
+#include "Wave_Writer.h" /* wave_ functions for writing sound file */
+#include <stdlib.h>
+#include <stdio.h>
+
+void handle_error( const char* str );
+
+int main()
+{
+	long sample_rate = 44100; /* number of samples per second */
+	int track = 0; /* index of track to play (0 = first) */
+	
+	/* Open music file in new emulator */
+	Music_Emu* emu;
+	handle_error( gme_open_file( filename, &emu, sample_rate ) );
+	
+	/* Start track */
+	handle_error( gme_start_track( emu, track ) );
+	
+	/* Begin writing to wave file */
+	wave_open( sample_rate, "out.wav" );
+	wave_enable_stereo();
+	
+	/* Record 10 seconds of track */
+	while ( gme_tell( emu ) < 10 * 1000L )
+	{
+		/* Sample buffer */
+		#define buf_size 1024 /* can be any multiple of 2 */
+		short buf [buf_size];
+		
+		/* Fill sample buffer */
+		handle_error( gme_play( emu, buf_size, buf ) );
+		
+		/* Write samples to wave file */
+		wave_write( buf, buf_size );
+	}
+	
+	/* Cleanup */
+	gme_delete( emu );
+	wave_close();
+	
+	return 0;
+}
+
+void handle_error( const char* str )
+{
+	if ( str )
+	{
+		printf( "Error: %s\n", str ); getchar();
+		exit( EXIT_FAILURE );
+	}
+}

demo/cpp_basics.cpp

+// C++ example that opens a game music file and records 10 seconds to "out.wav"
+
+static char filename [] = "test.nsf"; /* opens this file (can be any music type) */
+
+#include "gme/Music_Emu.h"
+
+#include "Wave_Writer.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+void handle_error( const char* str );
+
+int main()
+{
+	long sample_rate = 44100; // number of samples per second
+	int track = 0; // index of track to play (0 = first)
+	
+	// Determine file type
+	gme_type_t file_type;
+	handle_error( gme_identify_file( filename, &file_type ) );
+	if ( !file_type )
+		handle_error( "Unsupported music type" );
+	
+	// Create emulator and set sample rate
+	Music_Emu* emu = file_type->new_emu();
+	if ( !emu )
+		handle_error( "Out of memory" );
+	handle_error( emu->set_sample_rate( sample_rate ) );
+	
+	// Load music file into emulator
+	handle_error( emu->load_file( filename ) );
+	
+	// Start track
+	handle_error( emu->start_track( track ) );
+	
+	// Begin writing to wave file
+	Wave_Writer wave( sample_rate, "out.wav" );
+	wave.enable_stereo();
+	
+	// Record 10 seconds of track
+	while ( emu->tell() < 10 * 1000L )
+	{
+		// Sample buffer
+		const long size = 1024; // can be any multiple of 2
+		short buf [size];
+		
+		// Fill buffer
+		handle_error( emu->play( size, buf ) );
+		
+		// Write samples to wave file
+		wave.write( buf, size );
+	}
+	
+	// Cleanup
+	delete emu;
+	
+	return 0;
+}
+
+void handle_error( const char* str )
+{
+	if ( str )
+	{
+		printf( "Error: %s\n", str ); getchar();
+		exit( EXIT_FAILURE );
+	}
+}
+/* C example that opens any music file type, opens an m3u playlist if present,
+prints its info and voice names, customizes the sound, and fades a track out.
+Records to "out.wav". */
+
+static char filename [] = "test.nsf"; /* opens this file (can be any music type) */
+static char playlist [] = "test.m3u"; /* uses this playlist, if present*/
+
+#include "gme/gme.h"
+
+#include "Wave_Writer.h" /* wave_ functions for writing sound file */
+#include <stdlib.h>
+#include <stdio.h>
+
+void handle_error( const char* );
+
+/* Example of loading from memory, which would be useful if using a zip file or
+other custom format. In this example it's silly because we could just use
+gme_load( &emu, sample_rate, path, 0 ).  */
+Music_Emu* load_file( const char* path, long sample_rate )
+{
+	Music_Emu* emu;
+	char* data;
+	long  size;
+	
+	/* Read file data into memory. You might read the data from a zip file or
+	other compressed format. */
+	FILE* in = fopen( path, "rb" );
+	if ( !in )
+		handle_error( "Couldn't open file" );
+	fseek( in, 0, SEEK_END );
+	size = ftell( in );
+	rewind( in );
+	
+	data = malloc( size );
+	if ( !data )
+		handle_error( "Out of memory" );
+	if ( fread( data, size, 1, in ) <= 0 )
+		handle_error( "Read error" );
+	fclose( in );
+	
+	handle_error( gme_open_data( data, size, &emu, sample_rate ) );
+	free( data ); /* a copy is made of the data */
+	return emu;
+}
+
+/* Print any warning for most recent emulator action (load, start_track, play) */
+void print_warning( Music_Emu* emu )
+{
+	const char* warning = gme_warning( emu );
+	if ( warning )
+		printf( "**** Warning: %s\n\n", warning );
+}
+
+static char my_data [] = "Our cleanup function was called";
+
+/* Example cleanup function automatically called when emulator is deleted. */
+static void my_cleanup( void* my_data )
+{
+	printf( "\n%s\n", (char*) my_data );
+}
+
+int main()
+{
+	long sample_rate = 44100;
+	int track = 0; /* index of track to play (0 = first) */
+	int i;
+	
+	/* Load file into emulator */
+	Music_Emu* emu = load_file( filename, sample_rate );
+	print_warning( emu );
+	
+	/* Register cleanup function and confirmation string as data */
+	gme_set_user_data( emu, my_data );
+	gme_set_user_cleanup( emu, my_cleanup );
+	
+	/* Load .m3u playlist file. All tracks are assumed to use current file.
+	We ignore error here in case there is no m3u file present. */
+	gme_load_m3u( emu, playlist );
+	print_warning( emu );
+	
+	/* Get and print main info for track */
+	{
+		track_info_t info;
+		handle_error( gme_track_info( emu, &info, track ) );
+		printf( "System   : %s\n", info.system );
+		printf( "Game     : %s\n", info.game );
+		printf( "Author   : %s\n", info.author );
+		printf( "Copyright: %s\n", info.copyright );
+		printf( "Comment  : %s\n", info.comment );
+		printf( "Dumper   : %s\n", info.dumper );
+		printf( "Tracks   : %d\n", (int) info.track_count );
+		printf( "\n" );
+		printf( "Track    : %d\n", (int) track + 1 );
+		printf( "Name     : %s\n", info.song );
+		printf( "Length   : %ld:%02ld",
+				(long) info.length / 1000 / 60, (long) info.length / 1000 % 60 );
+		if ( info.loop_length != 0 )
+			printf( " (endless)" );
+		printf( "\n\n" );
+	}
+	
+	/* Print voice names */
+	for ( i = 0; i < gme_voice_count( emu ); i++ )
+		printf( "Voice %d: %s\n", i, gme_voice_names( emu ) [i] );
+	
+	/* Add some stereo enhancement */
+	gme_set_stereo_depth( emu, 0.20 );
+	
+	/* Adjust equalizer for crisp, bassy sound */
+	{
+		gme_equalizer_t eq;
+		eq.treble = 0.0;
+		eq.bass   = 20;
+		gme_set_equalizer( emu, &eq );
+	}
+	
+	/* Start track and begin fade at 10 seconds */
+	handle_error( gme_start_track( emu, track ) );
+	print_warning( emu );
+	gme_set_fade( emu, 10 * 1000L );
+	
+	/* Record track until it ends */
+	wave_open( sample_rate, "out.wav" );
+	wave_enable_stereo();
+	while ( !gme_track_ended( emu ) )
+	{
+		#define buf_size 1024
+		short buf [buf_size];
+		handle_error( gme_play( emu, buf_size, buf ) );
+		print_warning( emu );
+		wave_write( buf, buf_size );
+	}
+	
+	/* Cleanup */
+	gme_delete( emu );
+	wave_close();
+	
+	getchar();
+	return 0;
+}
+
+void handle_error( const char* str )
+{
+	if ( str )
+	{
+		printf( "Error: %s\n", str ); getchar();
+		exit( EXIT_FAILURE );
+	}
+}
+Game_Music_Emu 0.5.2 Design
+---------------------------
+This might be slightly out-of-date at times, but will be a big help in
+understanding the library implementation.
+
+
+Architecture
+------------
+The library is essentially a bunch of independent game music file
+emulators unified with a common interface.
+
+Gme_File and Music_Emu provide a common interface to the emulators. The
+virtual functions are protected rather than public to allow pre- and
+post-processing of arguments and data in one place. This allows the
+emulator classes to assume that everything is set up properly when
+starting a track and playing samples.
+
+All file input is done with the Data_Reader interface. Many derived
+classes are present, for the usual disk-based file and block of memory,
+to specialized adaptors for things like reading a subset of data or
+combining a block of memory with a Data_Reader to the remaining data.
+This makes the library much more flexible with regard to the source of
+game music file data. I still added a specialized load_mem() function to
+have the emulator keep a pointer to data already read in memory, for
+those formats whose files can be absolutely huge (GYM, some VGMs). This
+is important if for some reason the caller must load the data ahead of
+time, but doesn't want the emulator needlessly making a copy.
+
+Since silence checking and fading are relatively complex, they are kept
+separate from basic file loading and track information, which are
+handled in the base class Gme_File. My original intent was to use
+Gme_File as the common base class for full emulators and track
+information-only readers, but implementing the C interface was much
+simpler if both derived from Music_Emu. User C++ code can still benefit
+from static checking by using Gme_File where only track information will
+be accessed.
+
+Each emulator generally has three components: main emulator, CPU
+emulator, and sound chip emulator(s). Each component has minimal
+coupling, so use in a full emulator or stand alone is fairly easy. This
+modularity really helps reduce complexity. Blip_Buffer helps a lot with
+simplifying the APU interfaces and implementation.
+
+The "classic" emulators derive from Classic_Emu, which handles
+Blip_Buffer filling and multiple channels. It uses Multi_Buffer for
+output, allowing you to derive a custom buffer that could output each
+voice to a separate sound channel and do different processing on each.
+At some point I'm going to implement a better Effects_Buffer that allows
+individual control of every channel.
+
+In implementing the C interface, I wanted a way to specify an emulator
+type that didn't require linking in all the emulators. For each emulator
+type there is a global object with pointers to functions to create the
+emulator or a track information reader. The emulator type is thus a
+pointer to this, which conveniently allows for a NULL value. The user
+referencing this emulator type object is what ultimately links the
+emulator in (unless new Foo_Emu is used in C++, of course). This type
+also serves as a useful substitute for RTTI on older C++ compilers.
+
+Addendum: I have since added gme_type_list(), which causes all listed
+emulators to be linked in. To avoid this, I make the list itself
+editable in blargg_config.h. Having a built-in list allows
+gme_load_file() to take a path and give back an emulator with the file
+loaded, which is extremely useful for new users.
+
+
+Interface conventions
+----------------------
+If a function retains a pointer to or replaces the value of an object
+passed, it takes a pointer so that it will be clear in the caller's
+source code that care is required.
+
+Multi-word names have an underscore '_' separator between individual
+words.
+
+Functions are named with lowercase words. Functions which perform an
+action with side-effects are named with a verb phrase (i.e. load, move,
+run). Functions which return the value of a piece of state are named
+using a noun phrase (i.e. loaded, moved, running).
+
+Classes are named with capitalized words. Only the first letter of an
+acronym is capitalized. Class names are nouns, sometimes suggestive of
+what they do (i.e. File_Scanner).
+
+Structure, enumeration, and typedefs to these and built-in types are
+named using lowercase words with a _t suffix.
+
+Macros are named with all-uppercase words.
+
+Internal names which can't be hidden due to technical reasons have an
+underscore '_' suffix.
+
+
+Managing Complexity
+-------------------
+Complexity has been a factor in most library decisions. Many features
+have been passed by due to the complexity they would add. Once
+complexity goes past a certain level, it mentally grasping the library
+in its entirety, at which point more defects will occur and be hard to
+find.
+
+I chose 16-bit signed samples because it seems to be the most common
+format. Supporting multiple formats would add too much complexity to be
+worth it. Other formats can be obtained via conversion.
+
+I've kept interfaces fairly lean, leaving many possible features
+untapped but easy to add if necessary. For example the classic emulators
+could have volume and frequency equalization adjusted separately for
+each channel, since they each have an associated Blip_Synth.
+
+Source files of 400 lines or less seem to be the best size to limit
+complexity. In a few cases there is no reasonable way to split longer
+files, or there is benefit from having the source together in one file.
+
+
+Preventing Bugs
+---------------
+I've done many things to reduce the opportunity for defects. A general
+principle is to write code so that defects will be as visible as
+possible. I've used several techniques to achieve this.
+
+I put assertions at key points where defects seem likely or where
+corruption due to a defect is likely to be visible. I've also put
+assertions where violations of the interface are likely. In emulators
+where I am unsure of exact hardware operation in a particular case, I
+output a debug-only message noting that this has occurred; many times I
+haven't implemented a hardware feature because nothing uses it. I've
+made code brittle where there is no clear reason flexibility; code
+written to handle every possibility sacrifices quality and reliability
+to handle vaguely defined situations.
+
+
+Flexibility through indirection
+-------------------------------
+I've tried to allow the most flexibility of modules by using indirection
+to allow extension by the user. This keeps each module simpler and more
+focused on its unique task.
+
+The classic emulators use Multi_Buffer, which potentially allows a
+separate Blip_Buffer for each channel. This keeps emulators free of
+typical code to allow output in mono, stereo, panning, etc.
+
+All emulators use a reader object to access file data, allowing it to be
+stored in a regular file, compressed archive, memory, or generated
+on-the-fly. Again, the library can be kept free of the particulars of
+file access and changes required to support new formats.
+
+
+Emulators in general
+--------------------
+When I wrote the first NES sound emulator, I stored most of the state in
+an emulator-specific format, with significant redundancy. In the
+register write function I decoded everything into named variables. I
+became tired of the verbosity and wanted to more closely model the
+hardware, so I moved to a style of storing the last written value to
+each register, along with as little other state as possible, mostly the
+internal hardware registers. While this involves slightly more
+recalculation, in most cases the emulation code is of comparable size.
+It also makes state save/restore (for use in a full emulator) much
+simpler. Finally, it makes debugging easier since the hardware registers
+used in emulation are obvious.
+
+
+CPU Cores
+---------
+I've spent lots of time coming up with techniques to optimize the CPU
+cores. Some of the most important: execute multiple instructions during
+an emulation call, keep state in local variables to allow register
+assignment, optimize state representation for most common instructions,
+defer status flag calculation until actually needed, read program code
+directly without a call to the memory read function, always pre-fetch
+the operand byte before decoding instruction, and emulate instructions
+using common blocks of code.
+
+I've successfully used Nes_Cpu in a fairly complete NES emulator, and
+I'd like to make all the CPU emulators suitable for use in emulators. It
+seems a waste for them to be used only for the small amount of emulation
+necessary for game music files.
+
+I debugged the CPU cores by writing a test shell that ran them in
+parallel with other CPU cores and compared all memory accesses and
+processor states at each step. This provided good value at little cost.
+
+The CPU mapping page size is adjustable to allow the best tradeoff
+between memory/cache usage and handler granularity. The interface allows
+code to be somewhat independent of the page size.
+
+I optimize program memory accesses to direct reads rather than calls to
+the memory read function. My assumption is that it would be difficult to
+get useful code out of hardware I/O addresses, so no software will
+intentionally execute out of I/O space. Since the page size can be
+changed easily, most program memory mapping schemes can be accommodated.
+This greatly reduces memory access function calls.
+
+Game_Music_Emu 0.5.2
+--------------------
+Author : Shay Green <gblargg@gmail.com>
+Website: http://www.slack.net/~ant/libs/
+Forum  : http://groups.google.com/group/blargg-sound-libs
+License: GNU Lesser General Public License (LGPL)
+
+Contents
+--------
+* Overview
+* C and C++ interfaces
+* Function reference
+* Error handling
+* Emulator types
+* M3U playlist support
+* Information fields
+* Track length
+* Loading file data
+* Sound parameters
+* VGM/GYM YM2413 & YM2612 FM sound
+* Modular construction
+* Obscure features
+* Solving problems
+* Deprecated features
+* Thanks
+
+
+Overview
+--------
+This library can open game music files, play tracks, and read game and
+track information tags. To play a game music file, do the following:
+
+* Open the file with gme_open_file()
+* Start a track with gme_start_track();
+* Generate samples as needed with gme_play()
+* Play samples through speaker using your operating system
+* Delete emulator when done with gme_delete()
+
+Your code must arrange for the generated samples to be played through
+the computer's speaker using whatever method your operating system
+requires.
+
+There are many additional features available; you can:
+
+* Determine of the type of a music file without opening it with
+gme_identify_*()
+* Load just the file's information tags with gme_info_only
+* Load from a block of memory rather than a file with gme_load_data()
+* Arrange for a fade-out at a particular time with gme_set_fade
+* Find when a track has ended with gme_track_ended()
+* Seek to a new time in the track with gme_seek()
+* Load an extended m3u playlist with gme_load_m3u()
+* Get a list of the voices (channels) and mute them individually with
+gme_voice_names() and gme_mute_voice()
+* Change the playback tempo without affecting pitch with gme_set_tempo()
+* Adjust treble/bass equalization with gme_set_equalizer()
+* Associate your own data with an emulator and later get it back with
+gme_set_user_data()
+* Register a function of yours to be called back when the emulator is
+deleted with gme_set_user_cleanup()
+
+Refer to gme.h for a comprehensive summary of features.
+
+
+C and C++ interfaces
+--------------------
+While the library is written in C++, an extensive C interface is
+provided in gme.h. This C interface will be referred to throughout this
+documentation unless a feature is only available in the full C++
+interface. All C interface functions and other names have the gme_
+prefix, so you can recognize a C++-only feature by the lack of gme_ in
+the names used (contact me if you'd like a feature added to the C
+interface). If you're building a shared library, I highly recommend
+sticking to the C interface only, because it will be more stable between
+releases of the library than the C++ interface. Finally, the C and C++
+interfaces can be freely mixed without problems. Compare demo/basics.c
+with demo/cpp_basics.cpp to see how the C and C++ interfaces translate
+between each other.
+
+
+Function reference
+------------------
+Read the following header files for a complete reference to functions
+and features. The second group of header files can only be used in C++.
+
+blargg_config.h     Library configuration
+gme.h               C interface (also usable from C++)
+
+Gme_File.h          File loading and track information
+Music_Emu.h         Track playback and adjustments
+Data_Reader.h       Custom data readers
+Effects_Buffer.h    Sound buffer with adjustable stereo echo and panning
+M3u_Playlist.h      M3U playlist support
+Gbs_Emu.h           GBS equalizer settings
+Nsf_Emu.h           NSF equalizer settings
+Spc_Emu.h           SPC surround disable
+Vgm_Emu.h           VGM oversampling disable and custom buffer query
+
+
+Error handling
+--------------
+Functions which can fail have a return type of gme_err_t (blargg_err_t
+in the C++ interfaces), which is a pointer to an error string (const
+char*). If a function is successful it returns NULL. Errors that you can
+easily avoid are checked with debug assertions; gme_err_t return values
+are only used for genuine run-time errors that can't be easily predicted
+in advance (out of memory, I/O errors, incompatible file data). Your
+code should check all error values.
+
+To improve usability for C programmers, C++ programmers unfamiliar with
+exceptions, and compatibility with older C++ compilers, the library does
+*not* throw any C++ exceptions and uses malloc() instead of the standard
+operator new. This means that you *must* check for NULL when creating a
+library object with the new operator.
+
+When loading a music file in the wrong emulator or trying to load a
+non-music file, gme_wrong_file_type is returned. You can check for this
+error in C++ like this:
+
+	gme_err_t err = gme_open_file( path, &emu );
+	if ( err == gme_wrong_file_type )
+		...
+
+To check for minor problems, call gme_warning() to get a string
+describing the last warning. Your player should allow the user some way
+of knowing when this is the case, since these minor errors could affect
+playback. Without this information the user can't solve problems as
+well. When playing a track, gme_warning() returns minor playback-related
+problems (major playback problems end the track immediately and set the
+warning string).
+
+
+Emulator types
+--------------
+The library includes several game music emulators that each support a
+different file type. Each is identified by a gme_type_t constant defined
+in gme.h, for example gme_nsf_emu is for the NSF emulator. If you use
+gme_open_file() or gme_open_data(), the library does the work of
+determining the file type and creating an appropriate emulator. If you
+want more control over this process, read on.
+
+There are two basic ways to identify a game music file's type: look at
+its file extension, or read the header data. The library includes
+functions to help with both methods. The first is preferable because it
+is fast and the most common way to identify files. Sometimes the
+extension is lost or wrong, so the header must be read.
+
+Use gme_identify_extension() to find the correct game music type based
+on a filename. To identify a file based on its extension and header
+contents, use gme_identify_file(). If you read the header data yourself,
+use gme_identify_header().
+
+If you want to remove support for some music types to reduce your
+executable size, edit GME_TYPE_LIST in blargg_config.h. For example, to
+support just NSF and GBS, use this:
+
+	#define GME_TYPE_LIST gme_nsf_type, gme_gbs_type
+
+
+M3U playlist support
+--------------------
+The library supports playlists in an extended m3u format with
+gme_load_m3u() to give track names and times to multi-song formats: AY,
+GBS, HES, KSS, NSF, NSFE, and SAP. Some aspects of the file format
+itself is not well-defined so some m3u files won't work properly
+(particularly those provided with KSS files). Only m3u files referencing
+a single file are supported; your code must handle m3u files covering
+more than one game music file, though it can use the built-in m3u
+parsing provided by the library.
+
+
+Information fields
+------------------
+Support is provided for the various text fields and length information
+in a file with gme_track_info(). If you just need track information for
+a file (for example, building a playlist), use gme_new_info() in place
+of gme_new_emu(), load the file normally, then you can access the track
+count and info, but nothing else.
+
+             M3U  VGM  GYM  SPC  SAP  NSFE  NSF  AY  GBS  HES  KSS
+             -------------------------------------------------------
+Track Count | *    *    *    *    *    *    *    *    *
+            |
+System      |      *    *    *    *    *    *    *    *    *    *
+            |
+Game        |      *    *    *         *    *         *    *
+            |
+Song        | *    *    *    *    *    *         *
+            |
+Author      |      *         *    *    *    *    *    *    *
+            |
+Copyright   |      *    *    *    *    *    *         *    *
+            |
+Comment     |      *    *    *                   *
+            |
+Dumper      |      *    *    *         *
+            |
+Length      | *    *    *    *    *    *
+            |
+Intro Length| *    *    *
+            |
+Loop Length | *    *    *
+
+As listed above, the HES and KSS file formats don't include a track
+count, and tracks are often scattered over the 0-255 range, so an m3u
+playlist for these is a must.
+
+Unavailable text fields are set to an empty string and times to -1. Your
+code should be prepared for any combination of available and unavailable
+fields, as a particular music file might not use all of the supported
+fields listed above.
+
+Currently text fields are truncated to 255 characters. Obscure fields of
+some formats are not currently decoded; contact me if you want one
+added.
+
+
+Track length
+------------
+The library leaves it up to you as to when to stop playing a track. You
+can ask for available length information and then tell the library what
+time it should start fading the track with gme_set_fade(). By default it
+also continually checks for 6 or more seconds of silence to mark the end
+of a track. Here is a reasonable algorithm you can use to decide how
+long to play a track:
+
+* If the track length is > 0, use it
+* If the loop length > 0, play for intro + loop * 2
+* Otherwise, default to 2.5 minutes (150000 msec)
+
+If you want to play a track longer than normal, be sure the loop length
+isn't zero. See Music_Player.cpp around line 145 for example code.
+
+By default, the library skips silence at the beginning of a track. It
+also continually checks for the end of a non-looping track by watching
+for 6 seconds of unbroken silence. When doing this is scans *ahead* by
+several seconds so it can report the end of the track after only one
+second of silence has actually played. This feature can be disabled with
+gme_ignore_silence().
+
+
+Loading file data
+-----------------
+The library allows file data to be loaded in many different ways. All
+load functions return an error which you should check. The following
+examples assume these variables:
+
+	Music_Emu* emu;
+	gme_err_t error;
+
+If you're letting the library determine a file's type, you can use
+either gme_open_file() or gme_open_data():
+	
+	error = gme_open_file( pathname, &emu );
+	error = gme_open_data( pointer, size, &emu );
+
+If you're manually determining file type and using used gme_new_emu() to
+create an emulator, you can use the following methods of loading:
+
+* From a block of memory:
+
+	error = gme_load_data( emu, pointer, size );
+
+* Have library call your function to read data:
+
+	gme_err_t my_read( void* my_data, void* out, long count )
+	{
+		// code that reads 'count' bytes into 'out' buffer
+		// and return 0 if no error
+	}
+	
+	error = gme_load_custom( emu, my_read, file_size, my_data );
+
+* If you must load the file data into memory yourself, you can have the
+library use your data directly *without* making a copy. If you do this,
+you must not free the data until you're done playing the file.
+
+	error = emu->load_mem( pointer, size );
+
+* If you've already read the first bytes of a file (perhaps to determine
+the file type) and want to avoid seeking back to the beginning for
+performance reasons, use Remaining_Reader:
+
+	Std_File_Reader in;
+	error = in.open( file_path );
+	
+	char header [4];
+	error = in.read( &header, sizeof header );
+	...
+	
+	Remaining_Reader rem( &header, sizeof header, &in );
+	error = emu->load( rem );
+
+If you merely need access to a file's header after loading, use the
+emulator-specific header() functions, after casting the Music_Emu
+pointer to the specific emulator's type. This example examines the
+chip_flags field of the header if it's an NSF file:
+
+	if ( music_emu->type() == gme_nsf_type )
+	{
+		Nsf_Emu* nsf_emu = (Nsf_Emu*) music_emu;
+		if ( nsf_emu->header().chip_flags & 0x01 )
+			...
+	}
+
+Contact me if you want more information about loading files.
+
+
+Sound parameters
+----------------
+All emulators support an arbitrary output sampling rate. A rate of 44100
+Hz should work well on most systems. Since band-limited synthesis is
+used, a sampling rate above 48000 Hz is not necessary and will actually
+reduce sound quality and performance.
+
+All emulators also support adjustable gain, mainly for the purpose of
+getting consistent volume between different music formats and avoiding
+excessive modulation. The gain can only be set *before* setting the
+emulator's sampling rate, so it's not useful as a general volume
+control. The default gains of emulators are set so that they give
+generally similar volumes, though some soundtracks are significantly
+louder or quieter than normal.
+
+Some emulators support adjustable treble and bass frequency equalization
+(AY, GBS, HES, KSS, NSF, NSFE, SAP, VGM) using set_equalizer().
+Parameters are specified using gme_equalizer_t eq = { treble_dB,
+bass_freq }. Treble_dB sets the treble level (in dB), where 0.0 dB gives
+normal treble; -200.0 dB is quite muffled, and 5.0 dB emphasizes treble
+for an extra crisp sound. Bass_freq sets the frequency where bass
+response starts to diminish; 15 Hz is normal, 0 Hz gives maximum bass,
+and 15000 Hz removes all bass. For example, the following makes the
+sound extra-crisp but lacking bass:
+
+	gme_equalizer_t eq = { 5.0, 1000 };
+	gme_set_equalizer( music_emu, &eq );
+
+Each emulator's equalization defaults to approximate the particular
+console's sound quality; this default can be determined by calling
+equalizer() just after creating the emulator. The Music_Emu::tv_eq
+profile gives sound as if coming from a TV speaker, and some emulators
+include other profiles for different versions of the system. For
+example, to use Famicom sound equalization with the NSF emulator, do the
+following:
+
+	music_emu->set_equalizer( Nsf_Emu::famicom_eq );
+
+
+VGM/GYM YM2413 & YM2612 FM sound
+--------------------------------
+The library plays Sega Genesis/Mega Drive music using a YM2612 FM sound
+chip emulator based on the Gens project. Because this has some
+inaccuracies, other YM2612 emulators can be used in its place by
+re-implementing the interface in YM2612_Emu.h. Available on my website
+is a modified version of MAME's YM2612 emulator, which sounds better in
+some ways and whose author is still making improvements.
+
+VGM music files using the YM2413 FM sound chip are also supported, but a
+YM2413 emulator isn't included with the library due to technical
+reasons. I have put one of the available YM2413 emulators on my website
+that can be used directly.
+
+
+Modular construction
+--------------------
+The library is made of many fairly independent modules. If you're using
+only one music file emulator, you can eliminate many of the library
+sources from your program. Refer to the files list in readme.txt to get
+a general idea of what can be removed, and be sure to edit GME_TYPE_LIST
+(see "Emulator types" above). Post to the forum if you'd like me to put
+together a smaller version for a particular use, as this only takes me a
+few minutes to do.
+
+If you want to use one of the individual sound chip emulators (or CPU
+cores) in your own console emulator, first check the libraries page on
+my website since I have released several of them as stand alone
+libraries with included documentation and examples on their use. If you
+don't find it as a standalone library, contact me and I'll consider
+separating it.
+
+The "classic" sound chips use my Blip_Buffer library, which greatly
+simplifies their implementation and efficiently handles band-limited
+synthesis. It is also available as a stand alone library with
+documentation and many examples.
+
+
+Obscure features
+----------------
+The library's flexibility allows many possibilities. Contact me if you
+want help implementing ideas or removing limitations.
+
+* Uses no global/static variables, allowing multiple instances of any
+emulator. This is useful in a music player if you want to allow
+simultaneous recording or scanning of other tracks while one is already
+playing. This will also be useful if your platform disallows global
+data.
+
+* Emulators that support a custom sound buffer can have *every* voice
+routed to a different Blip_Buffer, allowing custom processing on each
+voice. For example you could record a Game Boy track as a 4-channel
+sound file.
+
+* Defining BLIP_BUFFER_FAST uses lower quality, less-multiply-intensive
+synthesis on "classic" emulators, which might help on some really old
+processors. This significantly lowers sound quality and prevents treble
+equalization. Try this if your platform's processor isn't fast enough
+for normal quality. Even on my ten-year-old 400 MHz Mac, this reduces
+processor usage at most by about 0.6% (from 4% to 3.4%), hardly worth
+the quality loss.
+
+
+Solving problems
+----------------
+If you're having problems, try the following:
+
+* If you're getting garbled sound, try this simple siren generator in
+place of your call to play(). This will quickly tell whether the problem
+is in the library or in your code.
+
+	static void play_siren( long count, short* out )
+	{
+		static double a, a2;
+		while ( count-- )
+			*out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) );
+	}
+
+* Enable debugging support in your environment. This enables assertions
+and other run-time checks.
+
+* Turn the compiler's optimizer is off. Sometimes an optimizer generates
+bad code.
+
+* If multiple threads are being used, ensure that only one at a time is
+accessing a given set of objects from the library. This library is not
+in general thread-safe, though independent objects can be used in
+separate threads.
+
+* If all else fails, see if the demos work.
+
+
+Deprecated features
+-------------------
+The following functions and other features have been deprecated and will
+be removed in a future release of the library. Alternatives to the
+deprecated features are listed to the right.
+
+Music_Emu::error_count()        warning()
+load( header, reader )          see "Loading file data" above
+Spc_Emu::trailer()              track_info()
+Spc_Emu::trailer_size()
+Gym_Emu::track_length()         track_info()
+Vgm_Emu::gd3_data()             track_info()
+Nsfe_Emu::disable_playlist()    clear_playlist()
+
+
+Thanks
+------
+Big thanks to Chris Moeller (kode54) for help with library testing and
+feedback, for maintaining the Foobar2000 plugin foo_gep based on it, and
+for original work on openspc++ that was used when developing Spc_Emu.
+Brad Martin's excellent OpenSPC SNES DSP emulator worked well from the
+start. Also thanks to Richard Bannister, Mahendra Tallur, Shazz,
+nenolod, theHobbit, Johan Samuelsson, and nes6502 for testing, using,
+and giving feedback for the library in their respective game music
+players.
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+#include "Ay_Apu.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"
+
+// Emulation inaccuracies:
+// * Noise isn't run when not in use
+// * Changes to envelope and noise periods are delayed until next reload
+// * Super-sonic tone should attenuate output to about 60%, not 50%
+
+// Tones above this frequency are treated as disabled tone at half volume.
+// Power of two is more efficient (avoids division).
+unsigned const inaudible_freq = 16384;
+
+int const period_factor = 16;
+
+static byte const amp_table [16] =
+{
+#define ENTRY( n ) byte (n * Ay_Apu::amp_range + 0.5)
+	// With channels tied together and 1K resistor to ground (as datasheet recommends),
+	// output nearly matches logarithmic curve as claimed. Approx. 1.5 dB per step.
+	ENTRY(0.000000),ENTRY(0.007813),ENTRY(0.011049),ENTRY(0.015625),
+	ENTRY(0.022097),ENTRY(0.031250),ENTRY(0.044194),ENTRY(0.062500),
+	ENTRY(0.088388),ENTRY(0.125000),ENTRY(0.176777),ENTRY(0.250000),
+	ENTRY(0.353553),ENTRY(0.500000),ENTRY(0.707107),ENTRY(1.000000),
+	
+	/*
+	// Measured from an AY-3-8910A chip with date code 8611.
+	
+	// Direct voltages without any load (very linear)
+	ENTRY(0.000000),ENTRY(0.046237),ENTRY(0.064516),ENTRY(0.089785),
+	ENTRY(0.124731),ENTRY(0.173118),ENTRY(0.225806),ENTRY(0.329032),
+	ENTRY(0.360215),ENTRY(0.494624),ENTRY(0.594624),ENTRY(0.672043),
+	ENTRY(0.766129),ENTRY(0.841935),ENTRY(0.926882),ENTRY(1.000000),
+	// With only some load
+	ENTRY(0.000000),ENTRY(0.011940),ENTRY(0.017413),ENTRY(0.024876),
+	ENTRY(0.036318),ENTRY(0.054229),ENTRY(0.072637),ENTRY(0.122388),
+	ENTRY(0.174129),ENTRY(0.239303),ENTRY(0.323881),ENTRY(0.410945),
+	ENTRY(0.527363),ENTRY(0.651741),ENTRY(0.832338),ENTRY(1.000000),
+	*/
+#undef ENTRY
+};
+
+static byte const modes [8] =
+{
+#define MODE( a0,a1, b0,b1, c0,c1 ) \
+		(a0 | a1<<1 | b0<<2 | b1<<3 | c0<<4 | c1<<5)
+	MODE( 1,0, 1,0, 1,0 ),
+	MODE( 1,0, 0,0, 0,0 ),
+	MODE( 1,0, 0,1, 1,0 ),
+	MODE( 1,0, 1,1, 1,1 ),
+	MODE( 0,1, 0,1, 0,1 ),
+	MODE( 0,1, 1,1, 1,1 ),
+	MODE( 0,1, 1,0, 0,1 ),
+	MODE( 0,1, 0,0, 0,0 ),
+};
+
+Ay_Apu::Ay_Apu()
+{
+	// build full table of the upper 8 envelope waveforms
+	for ( int m = 8; m--; )
+	{
+		byte* out = env.modes [m];
+		int flags = modes [m];
+		for ( int x = 3; --x >= 0; )
+		{
+			int amp = flags & 1;
+			int end = flags >> 1 & 1;
+			int step = end - amp;
+			amp *= 15;
+			for ( int y = 16; --y >= 0; )
+			{
+				*out++ = amp_table [amp];
+				amp += step;
+			}
+			flags >>= 2;
+		}
+	}
+	
+	output( 0 );
+	volume( 1.0 );
+	reset();
+}
+
+void Ay_Apu::reset()
+{
+	last_time   = 0;
+	noise.delay = 0;
+	noise.lfsr  = 1;
+	
+	osc_t* osc = &oscs [osc_count];
+	do
+	{
+		osc--;
+		osc->period   = period_factor;
+		osc->delay    = 0;
+		osc->last_amp = 0;
+		osc->phase    = 0;
+	}
+	while ( osc != oscs );
+	
+	for ( int i = sizeof regs; --i >= 0; )
+		regs [i] = 0;
+	regs [7] = 0xFF;
+	write_data_( 13, 0 );
+}
+
+void Ay_Apu::write_data_( int addr, int data )
+{
+	assert( (unsigned) addr < reg_count );
+	
+	if ( (unsigned) addr >= 14 )
+	{
+		#ifdef dprintf
+			dprintf( "Wrote to I/O port %02X\n", (int) addr );
+		#endif
+	}
+	
+	// envelope mode
+	if ( addr == 13 )
+	{
+		if ( !(data & 8) ) // convert modes 0-7 to proper equivalents
+			data = (data & 4) ? 15 : 9;
+		env.wave = env.modes [data - 7];
+		env.pos = -48;
+		env.delay = 0; // will get set to envelope period in run_until()
+	}
+	regs [addr] = data;
+	
+	// handle period changes accurately
+	int i = addr >> 1;
+	if ( i < osc_count )
+	{
+		blip_time_t period = (regs [i * 2 + 1] & 0x0F) * (0x100L * period_factor) +
+				regs [i * 2] * period_factor;
+		if ( !period )
+			period = period_factor;
+		
+		// adjust time of next timer expiration based on change in period
+		osc_t& osc = oscs [i];
+		if ( (osc.delay += period - osc.period) < 0 )
+			osc.delay = 0;
+		osc.period = period;
+	}
+	
+	// TODO: same as above for envelope timer, and it also has a divide by two after it
+}
+
+int const noise_off = 0x08;
+int const tone_off  = 0x01;
+
+void Ay_Apu::run_until( blip_time_t final_end_time )
+{
+	require( final_end_time >= last_time );
+	
+	// noise period and initial values
+	blip_time_t const noise_period_factor = period_factor * 2; // verified
+	blip_time_t noise_period = (regs [6] & 0x1F) * noise_period_factor;
+	if ( !noise_period )
+		noise_period = noise_period_factor;
+	blip_time_t const old_noise_delay = noise.delay;
+	blargg_ulong const old_noise_lfsr = noise.lfsr;
+	
+	// envelope period
+	blip_time_t const env_period_factor = period_factor * 2; // verified
+	blip_time_t env_period = (regs [12] * 0x100L + regs [11]) * env_period_factor;
+	if ( !env_period )
+		env_period = env_period_factor; // same as period 1 on my AY chip
+	if ( !env.delay )
+		env.delay = env_period;
+	
+	// run each osc separately
+	for ( int index = 0; index < osc_count; index++ )
+	{
+		osc_t* const osc = &oscs [index];
+		int osc_mode = regs [7] >> index;
+		
+		// output
+		Blip_Buffer* const osc_output = osc->output;
+		if ( !osc_output )
+			continue;
+		osc_output->set_modified();
+		
+		// period
+		int half_vol = 0;
+		blip_time_t inaudible_period = (blargg_ulong) (osc_output->clock_rate() +
+				inaudible_freq) / (inaudible_freq * 2);
+		if ( osc->period <= inaudible_period && !(osc_mode & tone_off) )
+		{
+			half_vol = 1; // Actually around 60%, but 50% is close enough
+			osc_mode |= tone_off;
+		}
+		
+		// envelope
+		blip_time_t start_time = last_time;
+		blip_time_t end_time   = final_end_time;
+		int const vol_mode = regs [0x08 + index];
+		int volume = amp_table [vol_mode & 0x0F] >> half_vol;
+		int osc_env_pos = env.pos;
+		if ( vol_mode & 0x10 )
+		{
+			volume = env.wave [osc_env_pos] >> half_vol;
+			// use envelope only if it's a repeating wave or a ramp that hasn't finished
+			if ( !(regs [13] & 1) || osc_env_pos < -32 )
+			{
+				end_time = start_time + env.delay;
+				if ( end_time >= final_end_time )
+					end_time = final_end_time;
+				
+				//if ( !(regs [12] | regs [11]) )
+				//  dprintf( "Used envelope period 0\n" );
+			}
+			else if ( !volume )
+			{
+				osc_mode = noise_off | tone_off;
+			}
+		}
+		else if ( !volume )
+		{
+			osc_mode = noise_off | tone_off;
+		}
+		
+		// tone time
+		blip_time_t const period = osc->period;
+		blip_time_t time = start_time + osc->delay;
+		if ( osc_mode & tone_off ) // maintain tone's phase when off
+		{
+			blargg_long count = (final_end_time - time + period - 1) / period;
+			time += count * period;
+			osc->phase ^= count & 1;
+		}
+		
+		// noise time
+		blip_time_t ntime = final_end_time;
+		blargg_ulong noise_lfsr = 1;
+		if ( !(osc_mode & noise_off) )
+		{
+			ntime = start_time + old_noise_delay;
+			noise_lfsr = old_noise_lfsr;
+			//if ( (regs [6] & 0x1F) == 0 )
+			//  dprintf( "Used noise period 0\n" );
+		}
+		
+		// The following efficiently handles several cases (least demanding first):
+		// * Tone, noise, and envelope disabled, where channel acts as 4-bit DAC
+		// * Just tone or just noise, envelope disabled
+		// * Envelope controlling tone and/or noise
+		// * Tone and noise disabled, envelope enabled with high frequency
+		// * Tone and noise together
+		// * Tone and noise together with envelope
+		
+		// This loop only runs one iteration if envelope is disabled. If envelope
+		// is being used as a waveform (tone and noise disabled), this loop will
+		// still be reasonably efficient since the bulk of it will be skipped.
+		while ( 1 )
+		{
+			// current amplitude
+			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 );
+				}
+			}
+			
+			// Run wave and noise interleved with each catching up to the other.
+			// If one or both are disabled, their "current time" will be past end time,
+			// so there will be no significant performance hit.
+			if ( ntime < end_time || time < end_time )
+			{
+				// Since amplitude was updated above, delta will always be +/- volume,
+				// so we can avoid using last_amp every time to calculate the delta.
+				int delta = amp * 2 - volume;
+				int delta_non_zero = delta != 0;
+				int phase = osc->phase | (osc_mode & tone_off); assert( tone_off == 0x01 );
+				do
+				{
+					// run noise
+					blip_time_t end = end_time;
+					if ( end_time > time ) end = time;
+					if ( phase & delta_non_zero )
+					{
+						while ( ntime <= end ) // must advance *past* time to avoid hang
+						{
+							int changed = noise_lfsr + 1;
+							noise_lfsr = (-(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1);
+							if ( changed & 2 )
+							{
+								delta = -delta;
+								synth_.offset( ntime, delta, osc_output );
+							}
+							ntime += noise_period;
+						}
+					}
+					else
+					{
+						// 20 or more noise periods on average for some music
+						blargg_long remain = end - ntime;
+						blargg_long count = remain / noise_period;
+						if ( remain >= 0 )
+							ntime += noise_period + count * noise_period;
+					}
+					
+					// run tone
+					end = end_time;
+					if ( end_time > ntime ) end = ntime;
+					if ( noise_lfsr & delta_non_zero )
+					{
+						while ( time < end )
+						{
+							delta = -delta;
+							synth_.offset( time, delta, osc_output );
+							time += period;
+							//phase ^= 1;
+						}
+						//assert( phase == (delta > 0) );
+						phase = unsigned (-delta) >> (CHAR_BIT * sizeof (unsigned) - 1);
+						// (delta > 0)
+					}
+					else
+					{
+						// loop usually runs less than once
+						//SUB_CASE_COUNTER( (time < end) * (end - time + period - 1) / period );
+						
+						while ( time < end )
+						{
+							time += period;
+							phase ^= 1;
+						}
+					}
+				}
+				while ( time < end_time || ntime < end_time );
+				
+				osc->last_amp = (delta + volume) >> 1;
+				if ( !(osc_mode & tone_off) )
+					osc->phase = phase;
+			}
+			
+			if ( end_time >= final_end_time )
+				break; // breaks first time when envelope is disabled
+			
+			// next envelope step
+			if ( ++osc_env_pos >= 0 )
+				osc_env_pos -= 32;
+			volume = env.wave [osc_env_pos] >> half_vol;
+			
+			start_time = end_time;
+			end_time += env_period;
+			if ( end_time > final_end_time )
+				end_time = final_end_time;
+		}
+		osc->delay = time - final_end_time;
+		
+		if ( !(osc_mode & noise_off) )
+		{
+			noise.delay = ntime - final_end_time;
+			noise.lfsr = noise_lfsr;
+		}
+	}
+	
+	// TODO: optimized saw wave envelope?
+	
+	// maintain envelope phase
+	blip_time_t remain = final_end_time - last_time - env.delay;
+	if ( remain >= 0 )
+	{
+		blargg_long count = (remain + env_period) / env_period;
+		env.pos += count;
+		if ( env.pos >= 0 )
+			env.pos = (env.pos & 31) - 32;
+		remain -= count * env_period;
+		assert( -remain <= env_period );
+	}
+	env.delay = -remain;
+	assert( env.delay > 0 );
+	assert( env.pos < 0 );
+	
+	last_time = final_end_time;
+}
+// AY-3-8910 sound chip emulator
+
+// Game_Music_Emu 0.5.2
+#ifndef AY_APU_H
+#define AY_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+class Ay_Apu {
+public:
+	// Set buffer to generate all sound into, or disable sound if NULL
+	void output( Blip_Buffer* );
+	
+	// Reset sound chip
+	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
+	// time frame at time 0. Time frames have no effect on emulation and each
+	// can be whatever length is convenient.
+	void end_frame( blip_time_t length );
+	
+// Additional features
+	
+	// Set sound output of specific oscillator to buffer, where index is
+	// 0, 1, or 2. If buffer is NULL, the specified oscillator is muted.
+	enum { osc_count = 3 };
+	void osc_output( int index, Blip_Buffer* );
+	
+	// Set overall volume (default is 1.0)
+	void volume( double );
+	
+	// Set treble equalization (see documentation)
+	void treble_eq( blip_eq_t const& );
+	
+public:
+	Ay_Apu();
+	typedef unsigned char byte;
+private:
+	struct osc_t
+	{
+		blip_time_t period;
+		blip_time_t delay;
+		short last_amp;
+		short phase;
+		Blip_Buffer* output;
+	} oscs [osc_count];
+	blip_time_t last_time;
+	byte latch;
+	byte regs [reg_count];
+	
+	struct {
+		blip_time_t delay;
+		blargg_ulong lfsr;
+	} noise;
+	
+	struct {
+		blip_time_t delay;
+		byte const* wave;
+		int pos;
+		byte modes [8] [48]; // values already passed through volume table
+	} env;
+	
+	void run_until( blip_time_t );
+	void write_data_( int addr, int data );
+public:
+	enum { amp_range = 255 };
+	Blip_Synth<blip_good_quality,1> synth_;
+};
+
+inline void Ay_Apu::volume( double v ) { synth_.volume( 0.7 / osc_count / amp_range * v ); }
+
+inline void Ay_Apu::treble_eq( blip_eq_t const& eq ) { synth_.treble_eq( eq ); }
+
+inline void Ay_Apu::write( blip_time_t time, int addr, int data )
+{
+	run_until( time );
+	write_data_( addr, data );
+}
+
+inline void Ay_Apu::osc_output( int i, Blip_Buffer* buf )
+{
+	assert( (unsigned) i < osc_count );
+	oscs [i].output = buf;
+}
+
+inline void Ay_Apu::output( Blip_Buffer* buf )
+{
+	osc_output( 0, buf );
+	osc_output( 1, buf );
+	osc_output( 2, buf );
+}
+
+inline void Ay_Apu::end_frame( blip_time_t time )
+{
+	if ( time > last_time )
+		run_until( time );
+	
+	assert( last_time >= time );
+	last_time -= time;
+}
+
+#endif
+// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/
+
+/*
+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"
+
+#include "blargg_endian.h"
+#include <string.h>
+
+//#include "z80_cpu_log.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 */
+
+#define SYNC_TIME()     (void) (s.time = s_time)
+#define RELOAD_TIME()   (void) (s_time = s.time)
+
+// Callbacks to emulator
+
+#define CPU_OUT( cpu, addr, data, TIME )\
+	ay_cpu_out( cpu, TIME, addr, data )
+
+#define CPU_IN( cpu, addr, TIME )\
+	ay_cpu_in( cpu, addr )
+
+#include "blargg_source.h"
+
+// flags, named with hex value for clarity
+int const S80 = 0x80;
+int const Z40 = 0x40;
+int const F20 = 0x20;
+int const H10 = 0x10;
+int const F08 = 0x08;
+int const V04 = 0x04;
+int const P04 = 0x04;
+int const N02 = 0x02;
+int const C01 = 0x01;
+
+#define SZ28P( n )  szpc [n]
+#define SZ28PC( n ) szpc [n]
+#define SZ28C( n )  (szpc [n] & ~P04)
+#define SZ28( n )   SZ28C( n )
+
+#define SET_R( n )  (void) (r.r = n)
+#define GET_R()     (r.r)
+
+Ay_Cpu::Ay_Cpu()
+{
+	state = &state_;
+	for ( int i = 0x100; --i >= 0; )
+	{
+		int even = 1;
+		for ( int p = i; p; p >>= 1 )
+			even ^= p;
+		int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04);
+		szpc [i] = n;
+		szpc [i + 0x100] = n | C01;
+	}
+	szpc [0x000] |= Z40;
+	szpc [0x100] |= Z40;
+}
+
+void Ay_Cpu::reset( void* m )
+{
+	mem = (uint8_t*) m;
+	
+	check( state == &state_ );
+	state = &state_;
+	state_.time = 0;
+	state_.base = 0;
+	end_time_   = 0;
+	
+	memset( &r, 0, sizeof r );
+}
+
+#define TIME                        (s_time + s.base)
+#define READ_PROG( addr )           (mem [addr])
+#define INSTR( offset )             READ_PROG( pc + (offset) )
+#define GET_ADDR()                  GET_LE16( &READ_PROG( pc ) )
+#define READ( addr )                READ_PROG( addr )
+#define WRITE( addr, data )         (void) (READ_PROG( addr ) = data)
+#define READ_WORD( addr )           GET_LE16( &READ_PROG( addr ) )
+#define WRITE_WORD( addr, data )    SET_LE16( &READ_PROG( addr ), data )
+#define IN( addr )                  CPU_IN( this, addr, TIME )
+#define OUT( addr, data )           CPU_OUT( this, addr, data, TIME )
+
+#if BLARGG_BIG_ENDIAN
+	#define R8( n, offset ) ((r8_ - offset) [n]) 
+#elif BLARGG_LITTLE_ENDIAN
+	#define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) 
+#else
+	#error "Byte order of CPU must be known"
+#endif
+
+//#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 )\
+	(*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1))))
+
+#define CASE5( a, b, c, d, e          ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e
+#define CASE6( a, b, c, d, e, f       ) CASE5( a, b, c, d, e       ): case 0x##f
+#define CASE7( a, b, c, d, e, f, g    ) CASE6( a, b, c, d, e, f    ): case 0x##g
+#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h
+
+// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8
+static byte const ed_dd_timing [0x100] = {
+//0    1    2    3    4    5    6    7    8    9    A    B    C    D    E    F
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00,
+0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,
+0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,
+0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,
+0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,
+0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,
+0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,
+0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,
+};
+
+// even on x86, using short and unsigned char was slower
+typedef int         fint16;
+typedef unsigned    fuint16;
+typedef unsigned    fuint8;
+
+bool Ay_Cpu::run( cpu_time_t end_time )
+{
+	set_end_time( end_time );
+	state_t s = this->state_;
+	this->state = &s;
+	bool warning = false;
+	
+	typedef BOOST::int8_t int8_t;
+	
+	union {
+		regs_t rg;
+		pairs_t rp;
+		uint8_t r8_ [8]; // indexed
+		uint16_t r16_ [4];
+	};
+	rg = this->r.b;
+	
+	cpu_time_t s_time = s.time;
+	uint8_t* const mem = this->mem; // cache
+	fuint16 pc = r.pc;
+	fuint16 sp = r.sp;
+	fuint16 ix = r.ix; // TODO: keep in memory for direct access?
+	fuint16 iy = r.iy;
+	int flags = r.b.flags;
+	
+	goto loop;
+jr_not_taken:
+	s_time -= 5;
+	goto loop;
+call_not_taken:
+	s_time -= 7; 
+jp_not_taken:
+	pc += 2;
+loop:
+	
+	check( (unsigned long) pc < 0x10000 );
+	check( (unsigned long) sp < 0x10000 );
+	check( (unsigned) flags < 0x100 );
+	check( (unsigned) ix < 0x10000 );
+	check( (unsigned) iy < 0x10000 );
+	
+	fuint8 opcode;
+	opcode = READ_PROG( pc );
+	pc++;
+	
+	static byte const base_timing [0x100] = {
+	//   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F
+		 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0
+		13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1
+		12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2
+		12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3
+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4
+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5
+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6
+		 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7
+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8
+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9
+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A
+		 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B
+		11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C
+		11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D
+		11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E
+		11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F
+	};
+	
+	fuint16 data;
+	data = base_timing [opcode];
+	if ( (s_time += data) >= 0 )
+		goto possibly_out_of_time;
+almost_out_of_time:
+	
+	data = READ_PROG( pc );
+	
+	#ifdef Z80_CPU_LOG_H
+		//log_opcode( opcode, READ_PROG( pc ) );
+		z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy );
+		z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ),
+				READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) );
+	#endif
+	
+	switch ( opcode )
+	{
+possibly_out_of_time:
+		if ( s_time < (int) data )