diff --git a/Frameworks/GME/GME.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/GME Framework.xcscheme b/Frameworks/GME/GME.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/GME Framework.xcscheme
new file mode 100644
index 000000000..265fe4847
--- /dev/null
+++ b/Frameworks/GME/GME.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/GME Framework.xcscheme
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Frameworks/GME/GME.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/xcschememanagement.plist b/Frameworks/GME/GME.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 000000000..958883c18
--- /dev/null
+++ b/Frameworks/GME/GME.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,22 @@
+
+
+
+
+ SchemeUserState
+
+ GME Framework.xcscheme
+
+ orderHint
+ 12
+
+
+ SuppressBuildableAutocreation
+
+ 8DC2EF4F0486A6940098B216
+
+ primary
+
+
+
+
+
diff --git a/Frameworks/GME/gme/Ay_Core.cpp b/Frameworks/GME/gme/Ay_Core.cpp
new file mode 100644
index 000000000..c54a73e9a
--- /dev/null
+++ b/Frameworks/GME/gme/Ay_Core.cpp
@@ -0,0 +1,190 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Ay_Core.h"
+
+/* Copyright (C) 2006-2009 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"
+
+inline void Ay_Core::disable_beeper()
+{
+ beeper_mask = 0;
+ last_beeper = 0;
+}
+
+Ay_Core::Ay_Core()
+{
+ beeper_output = NULL;
+ disable_beeper();
+}
+
+Ay_Core::~Ay_Core() { }
+
+void Ay_Core::set_beeper_output( Blip_Buffer* b )
+{
+ beeper_output = b;
+ if ( b && !cpc_mode )
+ beeper_mask = 0x10;
+ else
+ disable_beeper();
+}
+
+void Ay_Core::start_track( registers_t const& r, addr_t play )
+{
+ play_addr = play;
+
+ memset( mem_.padding1, 0xFF, sizeof mem_.padding1 );
+
+ int const mirrored = 0x80; // this much is mirrored after end of memory
+ memset( mem_.ram + mem_size + mirrored, 0xFF, sizeof mem_.ram - mem_size - mirrored );
+ memcpy( mem_.ram + mem_size, mem_.ram, mirrored ); // some code wraps around (ugh)
+
+ cpu.reset( mem_.padding1, mem_.padding1 );
+ cpu.map_mem( 0, mem_size, mem_.ram, mem_.ram );
+ cpu.r = r;
+
+ beeper_delta = (int) (apu_.amp_range * 0.8);
+ last_beeper = 0;
+ next_play = play_period;
+ spectrum_mode = false;
+ cpc_mode = false;
+ cpc_latch = 0;
+ set_beeper_output( beeper_output );
+ apu_.reset();
+
+ // a few tunes rely on channels having tone enabled at the beginning
+ apu_.write_addr( 7 );
+ apu_.write_data( 0, 0x38 );
+
+}
+
+// Emulation
+
+void Ay_Core::cpu_out_( time_t time, addr_t addr, int data )
+{
+ // Spectrum
+ if ( !cpc_mode )
+ {
+ switch ( addr & 0xFEFF )
+ {
+ case 0xFEFD:
+ spectrum_mode = true;
+ apu_.write_addr( data );
+ return;
+
+ case 0xBEFD:
+ spectrum_mode = true;
+ apu_.write_data( time, data );
+ return;
+ }
+ }
+
+ // CPC
+ if ( !spectrum_mode )
+ {
+ switch ( addr >> 8 )
+ {
+ case 0xF6:
+ switch ( data & 0xC0 )
+ {
+ case 0xC0:
+ apu_.write_addr( cpc_latch );
+ goto enable_cpc;
+
+ case 0x80:
+ apu_.write_data( time, 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;
+ disable_beeper();
+ set_cpc_callback.f( set_cpc_callback.data );
+ }
+}
+
+int Ay_Core::cpu_in( addr_t addr )
+{
+ // keyboard read and other things
+ if ( (addr & 0xFF) == 0xFE )
+ return 0xFF; // other values break some beeper tunes
+
+ dprintf( "Unmapped IN : $%04X\n", addr );
+ return 0xFF;
+}
+
+void Ay_Core::end_frame( time_t* end )
+{
+ cpu.set_time( 0 );
+
+ // Since detection of CPC mode will halve clock rate during the frame
+ // and thus generate up to twice as much sound, we must generate half
+ // as much until mode is known.
+ if ( !(spectrum_mode | cpc_mode) )
+ *end /= 2;
+
+ while ( cpu.time() < *end )
+ {
+ run_cpu( min( *end, next_play ) );
+
+ if ( cpu.time() >= next_play )
+ {
+ // next frame
+ next_play += play_period;
+
+ if ( cpu.r.iff1 )
+ {
+ // interrupt enabled
+
+ if ( mem_.ram [cpu.r.pc] == 0x76 )
+ cpu.r.pc++; // advance past HALT instruction
+
+ cpu.r.iff1 = 0;
+ cpu.r.iff2 = 0;
+
+ mem_.ram [--cpu.r.sp] = byte (cpu.r.pc >> 8);
+ mem_.ram [--cpu.r.sp] = byte (cpu.r.pc);
+
+ // fixed interrupt
+ cpu.r.pc = 0x38;
+ cpu.adjust_time( 12 );
+
+ if ( cpu.r.im == 2 )
+ {
+ // vectored interrupt
+ addr_t addr = cpu.r.i * 0x100 + 0xFF;
+ cpu.r.pc = mem_.ram [(addr + 1) & 0xFFFF] * 0x100 + mem_.ram [addr];
+ cpu.adjust_time( 6 );
+ }
+ }
+ }
+ }
+
+ // End time frame
+ *end = cpu.time();
+ next_play -= *end;
+ check( next_play >= 0 );
+ cpu.adjust_time( -*end );
+ apu_.end_frame( *end );
+}
diff --git a/Frameworks/GME/gme/Ay_Core.h b/Frameworks/GME/gme/Ay_Core.h
new file mode 100644
index 000000000..96c57c0e0
--- /dev/null
+++ b/Frameworks/GME/gme/Ay_Core.h
@@ -0,0 +1,81 @@
+// Sinclair Spectrum AY music emulator core
+
+// Game_Music_Emu $vers
+#ifndef AY_CORE_H
+#define AY_CORE_H
+
+#include "Z80_Cpu.h"
+#include "Ay_Apu.h"
+
+class Ay_Core {
+public:
+
+ // Clock count
+ typedef int time_t;
+
+ // Sound chip access, to assign it to Blip_Buffer etc.
+ Ay_Apu& apu() { return apu_; }
+
+ // Sets beeper sound buffer, or NULL to mute it. Volume and treble EQ of
+ // beeper are set by APU.
+ void set_beeper_output( Blip_Buffer* );
+
+ // Sets time between calls to play routine. Can be changed while playing.
+ void set_play_period( time_t p ) { play_period = p; }
+
+ // 64K memory to load code and data into before starting track. Caller
+ // must parse the AY file.
+ BOOST::uint8_t* mem() { return mem_.ram; }
+ enum { mem_size = 0x10000 };
+ enum { ram_addr = 0x4000 }; // where official RAM starts
+
+ // Starts track using specified register values, and sets play routine that
+ // is called periodically
+ typedef Z80_Cpu::registers_t registers_t;
+ typedef int addr_t;
+ void start_track( registers_t const&, addr_t play );
+
+ // Ends time frame of at most *end clocks and sets *end to number of clocks
+ // emulated. Until Spectrum/CPC mode is determined, *end is HALVED.
+ void end_frame( time_t* end );
+
+ // Called when CPC hardware is first accessed. AY file format doesn't specify
+ // which sound hardware is used, so it must be determined during playback
+ // based on which sound port is first used.
+ blargg_callback set_cpc_callback;
+
+// Implementation
+public:
+ Ay_Core();
+ ~Ay_Core();
+
+private:
+ Blip_Buffer* beeper_output;
+ int beeper_delta;
+ int last_beeper;
+ int beeper_mask;
+
+ addr_t play_addr;
+ time_t play_period;
+ time_t next_play;
+
+ int cpc_latch;
+ bool spectrum_mode;
+ bool cpc_mode;
+
+ // large items
+ Z80_Cpu cpu;
+ struct {
+ BOOST::uint8_t padding1 [0x100];
+ BOOST::uint8_t ram [mem_size + 0x100];
+ } mem_;
+ Ay_Apu apu_;
+
+ int cpu_in( addr_t );
+ void cpu_out( time_t, addr_t, int data );
+ void cpu_out_( time_t, addr_t, int data );
+ bool run_cpu( time_t end );
+ void disable_beeper();
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Blip_Buffer_impl.h b/Frameworks/GME/gme/Blip_Buffer_impl.h
new file mode 100644
index 000000000..aebaa1af0
--- /dev/null
+++ b/Frameworks/GME/gme/Blip_Buffer_impl.h
@@ -0,0 +1,135 @@
+// Internal stuff here to keep public header uncluttered
+
+// Blip_Buffer $vers
+#ifndef BLIP_BUFFER_IMPL_H
+#define BLIP_BUFFER_IMPL_H
+
+typedef unsigned blip_resampled_time_t;
+
+#ifndef BLIP_MAX_QUALITY
+ #define BLIP_MAX_QUALITY 32
+#endif
+
+#ifndef BLIP_BUFFER_ACCURACY
+ #define BLIP_BUFFER_ACCURACY 16
+#endif
+
+#ifndef BLIP_PHASE_BITS
+ #define BLIP_PHASE_BITS 6
+#endif
+
+class blip_eq_t;
+class Blip_Buffer;
+
+#if BLIP_BUFFER_FAST
+ // linear interpolation needs 8 bits
+ #undef BLIP_PHASE_BITS
+ #define BLIP_PHASE_BITS 8
+
+ #undef BLIP_MAX_QUALITY
+ #define BLIP_MAX_QUALITY 2
+#endif
+
+int const blip_res = 1 << BLIP_PHASE_BITS;
+int const blip_buffer_extra_ = BLIP_MAX_QUALITY + 2;
+
+class Blip_Buffer_ {
+public:
+// Writer
+
+ typedef int clocks_t;
+
+ // Properties of fixed-point sample position
+ typedef unsigned fixed_t; // unsigned for more range, optimized shifts
+ enum { fixed_bits = BLIP_BUFFER_ACCURACY }; // bits in fraction
+ enum { fixed_unit = 1 << fixed_bits }; // 1.0 samples
+
+ // Converts clock count to fixed-point sample position
+ fixed_t to_fixed( clocks_t t ) const { return t * factor_ + offset_; }
+
+ // Deltas in buffer are fixed-point with this many fraction bits.
+ // Less than 16 for extra range.
+ enum { delta_bits = 14 };
+
+ // Pointer to first committed delta sample
+ typedef int delta_t;
+
+ // Pointer to delta corresponding to fixed-point sample position
+ delta_t* delta_at( fixed_t );
+
+// Reader
+
+ delta_t* read_pos() { return buffer_; }
+
+ void clear_modified() { modified_ = false; }
+ int highpass_shift() const { return bass_shift_; }
+ int integrator() const { return reader_accum_; }
+ void set_integrator( int n ) { reader_accum_ = n; }
+
+public: //friend class Tracked_Blip_Buffer; private:
+ bool modified() const { return modified_; }
+ void remove_silence( int count );
+
+private:
+ unsigned factor_;
+ fixed_t offset_;
+ delta_t* buffer_center_;
+ int buffer_size_;
+ int reader_accum_;
+ int bass_shift_;
+ delta_t* buffer_;
+ int sample_rate_;
+ int clock_rate_;
+ int bass_freq_;
+ int length_;
+ bool modified_;
+
+ friend class Blip_Buffer;
+};
+
+class Blip_Synth_Fast_ {
+public:
+ int delta_factor;
+ int last_amp;
+ Blip_Buffer* buf;
+
+ void volume_unit( double );
+ void treble_eq( blip_eq_t const& ) { }
+ Blip_Synth_Fast_();
+};
+
+class Blip_Synth_ {
+public:
+ int delta_factor;
+ int last_amp;
+ Blip_Buffer* buf;
+
+ void volume_unit( double );
+ void treble_eq( blip_eq_t const& );
+ Blip_Synth_( short phases [], int width );
+private:
+ double volume_unit_;
+ short* const phases;
+ int const width;
+ int kernel_unit;
+
+ void adjust_impulse();
+ void rescale_kernel( int shift );
+ int impulses_size() const { return blip_res / 2 * width; }
+};
+
+class blip_buffer_state_t
+{
+ blip_resampled_time_t offset_;
+ int reader_accum_;
+ int buf [blip_buffer_extra_];
+ friend class Blip_Buffer;
+};
+
+inline Blip_Buffer_::delta_t* Blip_Buffer_::delta_at( fixed_t f )
+{
+ assert( (f >> fixed_bits) < (unsigned) buffer_size_ );
+ return buffer_center_ + (f >> fixed_bits);
+}
+
+#endif
diff --git a/Frameworks/GME/gme/Blip_Buffer_impl2.h b/Frameworks/GME/gme/Blip_Buffer_impl2.h
new file mode 100644
index 000000000..0775ca637
--- /dev/null
+++ b/Frameworks/GME/gme/Blip_Buffer_impl2.h
@@ -0,0 +1,282 @@
+// Internal stuff here to keep public header uncluttered
+
+// Blip_Buffer $vers
+#ifndef BLIP_BUFFER_IMPL2_H
+#define BLIP_BUFFER_IMPL2_H
+
+//// Compatibility
+
+BLARGG_DEPRECATED( int const blip_low_quality = 8; )
+BLARGG_DEPRECATED( int const blip_med_quality = 8; )
+BLARGG_DEPRECATED( int const blip_good_quality = 12; )
+BLARGG_DEPRECATED( int const blip_high_quality = 16; )
+
+BLARGG_DEPRECATED( int const blip_sample_max = 32767; )
+
+// Number of bits in raw sample that covers normal output range. Less than 32 bits to give
+// extra amplitude range. That is,
+// +1 << (blip_sample_bits-1) = +1.0
+// -1 << (blip_sample_bits-1) = -1.0
+int const blip_sample_bits = 30;
+
+//// BLIP_READER_
+
+//// Optimized reading from Blip_Buffer, for use in custom sample buffer or mixer
+
+// Begins reading from buffer. Name should be unique to the current {} block.
+#define BLIP_READER_BEGIN( name, blip_buffer ) \
+ const Blip_Buffer::delta_t* BLARGG_RESTRICT name##_reader_buf = (blip_buffer).read_pos();\
+ int name##_reader_accum = (blip_buffer).integrator()
+
+// Gets value to pass to BLIP_READER_NEXT()
+#define BLIP_READER_BASS( blip_buffer ) (blip_buffer).highpass_shift()
+
+// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal
+// code at the cost of having no bass_freq() functionality
+int const blip_reader_default_bass = 9;
+
+// Current sample as 16-bit signed value
+#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16))
+
+// Current raw sample in full internal resolution
+#define BLIP_READER_READ_RAW( name ) (name##_reader_accum)
+
+// Advances to next sample
+#define BLIP_READER_NEXT( name, bass ) \
+ (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass)))
+
+// Ends reading samples from buffer. The number of samples read must now be removed
+// using Blip_Buffer::remove_samples().
+#define BLIP_READER_END( name, blip_buffer ) \
+ (void) ((blip_buffer).set_integrator( name##_reader_accum ))
+
+#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset)
+
+int const blip_reader_idx_factor = sizeof (Blip_Buffer::delta_t);
+
+#define BLIP_READER_NEXT_IDX_( name, bass, idx ) {\
+ name##_reader_accum -= name##_reader_accum >> (bass);\
+ name##_reader_accum += name##_reader_buf [(idx)];\
+}
+
+#define BLIP_READER_NEXT_RAW_IDX_( name, bass, idx ) {\
+ name##_reader_accum -= name##_reader_accum >> (bass);\
+ name##_reader_accum +=\
+ *(Blip_Buffer::delta_t const*) ((char const*) name##_reader_buf + (idx));\
+}
+
+//// BLIP_CLAMP
+
+#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
+ defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
+ #define BLIP_X86 1
+ #define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
+#else
+ #define BLIP_CLAMP_( in ) (blip_sample_t) in != in
+#endif
+
+// Clamp sample to blip_sample_t range
+#define BLIP_CLAMP( sample, out )\
+ { if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 31) ^ 0x7FFF; }
+
+
+//// Blip_Synth
+
+// (in >> sh & mask) * mul
+#define BLIP_SH_AND_MUL( in, sh, mask, mul ) \
+((int) (in) / ((1U << (sh)) / (mul)) & (unsigned) ((mask) * (mul)))
+
+// (T*) ptr + (off >> sh)
+#define BLIP_PTR_OFF_SH( T, ptr, off, sh ) \
+ ((T*) (BLIP_SH_AND_MUL( off, sh, -1, sizeof (T) ) + (char*) (ptr)))
+
+template
+inline void Blip_Synth::offset_resampled( blip_resampled_time_t time,
+ int delta, Blip_Buffer* blip_buf ) const
+{
+#if BLIP_BUFFER_FAST
+ int const half_width = 1;
+#else
+ int const half_width = quality / 2;
+#endif
+
+ Blip_Buffer::delta_t* BLARGG_RESTRICT buf = blip_buf->delta_at( time );
+
+ delta *= impl.delta_factor;
+
+ int const phase_shift = BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS;
+ int const phase = (half_width & (half_width - 1)) ?
+ (int) BLIP_SH_AND_MUL( time, phase_shift, blip_res - 1, sizeof (coeff_t) ) * half_width :
+ (int) BLIP_SH_AND_MUL( time, phase_shift, blip_res - 1, sizeof (coeff_t) * half_width );
+
+#if BLIP_BUFFER_FAST
+ int left = buf [0] + delta;
+
+ // Kind of crappy, but doing shift after multiply results in overflow.
+ // Alternate way of delaying multiply by delta_factor results in worse
+ // sub-sample resolution.
+ int right = (delta >> BLIP_PHASE_BITS) * phase;
+ #if BLIP_BUFFER_NOINTERP
+ // TODO: remove? (just a hack to see how it sounds)
+ right = 0;
+ #endif
+ left -= right;
+ right += buf [1];
+
+ buf [0] = left;
+ buf [1] = right;
+#else
+
+ int const fwd = -quality / 2;
+ int const rev = fwd + quality - 2;
+
+ coeff_t const* BLARGG_RESTRICT imp = (coeff_t const*) ((char const*) phases + phase);
+ int const phase2 = phase + phase - (blip_res - 1) * half_width * sizeof (coeff_t);
+
+ #define BLIP_MID_IMP imp = (coeff_t const*) ((char const*) imp - phase2);
+
+ #if BLIP_MAX_QUALITY > 16
+ // General version for any quality
+ if ( quality != 8 && quality != 12 && quality != 16 )
+ {
+ buf += fwd;
+
+ // left half
+ for ( int n = half_width / 2; --n >= 0; )
+ {
+ buf [0] += imp [0] * delta;
+ buf [1] += imp [1] * delta;
+ imp += 2;
+ buf += 2;
+ }
+
+ // mirrored right half
+ BLIP_MID_IMP
+ for ( int n = half_width / 2; --n >= 0; )
+ {
+ buf [0] += imp [-1] * delta;
+ buf [1] += *(imp -= 2) * delta;
+ buf += 2;
+ }
+
+ return;
+ }
+ #endif
+
+ // Unrolled versions for qualities 8, 12, and 16
+
+ #if BLIP_X86
+ // This gives better code for x86
+ #define BLIP_ADD( out, in ) \
+ buf [out] += imp [in] * delta
+
+ #define BLIP_FWD( i ) {\
+ BLIP_ADD( fwd + i, i );\
+ BLIP_ADD( fwd + 1 + i, i + 1 );\
+ }
+
+ #define BLIP_REV( r ) {\
+ BLIP_ADD( rev - r, r + 1 );\
+ BLIP_ADD( rev + 1 - r, r );\
+ }
+
+ BLIP_FWD( 0 )
+ BLIP_FWD( 2 )
+ if ( quality > 8 ) BLIP_FWD( 4 )
+ if ( quality > 12 ) BLIP_FWD( 6 )
+ BLIP_MID_IMP
+ if ( quality > 12 ) BLIP_REV( 6 )
+ if ( quality > 8 ) BLIP_REV( 4 )
+ BLIP_REV( 2 )
+ BLIP_REV( 0 )
+
+ #else
+ // Help RISC processors and simplistic compilers by reading ahead of writes
+ #define BLIP_FWD( i ) {\
+ int t0 = i0 * delta + buf [fwd + i];\
+ int t1 = imp [i + 1] * delta + buf [fwd + 1 + i];\
+ i0 = imp [i + 2];\
+ buf [fwd + i] = t0;\
+ buf [fwd + 1 + i] = t1;\
+ }
+
+ #define BLIP_REV( r ) {\
+ int t0 = i0 * delta + buf [rev - r];\
+ int t1 = imp [r] * delta + buf [rev + 1 - r];\
+ i0 = imp [r - 1];\
+ buf [rev - r] = t0;\
+ buf [rev + 1 - r] = t1;\
+ }
+
+ int i0 = *imp;
+ BLIP_FWD( 0 )
+ if ( quality > 8 ) BLIP_FWD( 2 )
+ if ( quality > 12 ) BLIP_FWD( 4 )
+ {
+ int const mid = half_width - 1;
+ int t0 = i0 * delta + buf [fwd + mid - 1];
+ int t1 = imp [mid] * delta + buf [fwd + mid ];
+ BLIP_MID_IMP
+ i0 = imp [mid];
+ buf [fwd + mid - 1] = t0;
+ buf [fwd + mid ] = t1;
+ }
+ if ( quality > 12 ) BLIP_REV( 6 )
+ if ( quality > 8 ) BLIP_REV( 4 )
+ BLIP_REV( 2 )
+
+ int t0 = i0 * delta + buf [rev ];
+ int t1 = *imp * delta + buf [rev + 1];
+ buf [rev ] = t0;
+ buf [rev + 1] = t1;
+ #endif
+
+#endif
+}
+
+template
+#if BLIP_BUFFER_FAST
+ inline
+#endif
+void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const
+{
+ offset_resampled( buf->to_fixed( t ), delta, buf );
+}
+
+template
+#if BLIP_BUFFER_FAST
+ inline
+#endif
+void Blip_Synth::update( blip_time_t t, int amp )
+{
+ int delta = amp - impl.last_amp;
+ impl.last_amp = amp;
+ offset_resampled( impl.buf->to_fixed( t ), delta, impl.buf );
+}
+
+
+//// blip_eq_t
+
+inline blip_eq_t::blip_eq_t( double t ) :
+ treble( t ), kaiser( 5.2 ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { }
+inline blip_eq_t::blip_eq_t( double t, int rf, int sr, int cf, double k ) :
+ treble( t ), kaiser( k ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { }
+
+
+//// Blip_Buffer
+
+inline int Blip_Buffer::length() const { return length_; }
+inline int Blip_Buffer::samples_avail() const { return (int) (offset_ >> BLIP_BUFFER_ACCURACY); }
+inline int Blip_Buffer::sample_rate() const { return sample_rate_; }
+inline int Blip_Buffer::output_latency() const { return BLIP_MAX_QUALITY / 2; }
+inline int Blip_Buffer::clock_rate() const { return clock_rate_; }
+inline void Blip_Buffer::clock_rate( int cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); }
+
+inline void Blip_Buffer::remove_silence( int count )
+{
+ // fails if you try to remove more samples than available
+ assert( count <= samples_avail() );
+ offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+}
+
+#endif
diff --git a/Frameworks/GME/gme/Bml_Parser.cpp b/Frameworks/GME/gme/Bml_Parser.cpp
new file mode 100644
index 000000000..1735ec47a
--- /dev/null
+++ b/Frameworks/GME/gme/Bml_Parser.cpp
@@ -0,0 +1,268 @@
+#include
+#include
+#include
+#include
+
+#include "Bml_Parser.h"
+
+Bml_Node Bml_Node::emptyNode;
+
+Bml_Node::Bml_Node()
+{
+ name = 0;
+ value = 0;
+}
+
+Bml_Node::Bml_Node(const Bml_Node &in)
+{
+ size_t length;
+ name = 0;
+ if (in.name)
+ {
+ length = strlen(in.name);
+ name = new char[length + 1];
+ memcpy(name, in.name, length + 1);
+ }
+ value = 0;
+ if (in.value)
+ {
+ length = strlen(in.value);
+ value = new char[length + 1];
+ memcpy(value, in.value, length + 1);
+ }
+ children = in.children;
+}
+
+Bml_Node::~Bml_Node()
+{
+ delete [] name;
+ delete [] value;
+}
+
+void Bml_Node::clear()
+{
+ delete [] name;
+ delete [] value;
+
+ name = 0;
+ value = 0;
+ children.resize( 0 );
+}
+
+void Bml_Node::setLine(const char *line)
+{
+ delete [] name;
+ delete [] value;
+
+ name = 0;
+ value = 0;
+
+ const char * line_end = strchr(line, '\n');
+ if ( !line_end ) line_end = line + strlen(line);
+
+ const char * first_letter = line;
+ while ( first_letter < line_end && *first_letter <= 0x20 ) first_letter++;
+
+ const char * colon = strchr(first_letter, ':');
+ if (colon >= line_end) colon = 0;
+ const char * last_letter = line_end - 1;
+
+ if (colon)
+ {
+ const char * first_value_letter = colon + 1;
+ while (first_value_letter < line_end && *first_value_letter <= 0x20) first_value_letter++;
+ last_letter = line_end - 1;
+ while (last_letter > first_value_letter && *last_letter <= 0x20) last_letter--;
+
+ value = new char[last_letter - first_value_letter + 2];
+ memcpy(value, first_value_letter, last_letter - first_value_letter + 1);
+ value[last_letter - first_value_letter + 1] = '\0';
+
+ last_letter = colon - 1;
+ }
+
+ while (last_letter > first_letter && *last_letter <= 0x20) last_letter--;
+
+ name = new char[last_letter - first_letter + 2];
+ memcpy(name, first_letter, last_letter - first_letter + 1);
+ name[last_letter - first_letter + 1] = '\0';
+}
+
+void Bml_Node::addChild(const Bml_Node &child)
+{
+ children.push_back(child);
+}
+
+const char * Bml_Node::getName() const
+{
+ return name;
+}
+
+const char * Bml_Node::getValue() const
+{
+ return value;
+}
+
+size_t Bml_Node::getChildCount() const
+{
+ return children.size();
+}
+
+Bml_Node const& Bml_Node::getChild(size_t index) const
+{
+ return children[index];
+}
+
+Bml_Node & Bml_Node::walkToNode(const char *path)
+{
+ Bml_Node * node = this;
+ while ( *path )
+ {
+ bool item_found = false;
+ const char * next_separator = strchr( path, ':' );
+ if ( !next_separator ) next_separator = path + strlen(path);
+ for ( std::vector::iterator it = node->children.end(); it != node->children.begin(); )
+ {
+ --it;
+ if ( next_separator - path == strlen(it->name) &&
+ strncmp( it->name, path, next_separator - path ) == 0 )
+ {
+ node = &(*it);
+ item_found = true;
+ break;
+ }
+ }
+ if ( !item_found ) return emptyNode;
+ if ( *next_separator )
+ {
+ path = next_separator + 1;
+ }
+ else break;
+ }
+ return *node;
+}
+
+Bml_Node const& Bml_Node::walkToNode(const char *path) const
+{
+ Bml_Node const* next_node;
+ Bml_Node const* node = this;
+ while ( *path )
+ {
+ bool item_found = false;
+ size_t array_index = ~0;
+ const char * array_index_start = strchr( path, '[' );
+ const char * next_separator = strchr( path, ':' );
+ if ( !next_separator ) next_separator = path + strlen(path);
+ if ( array_index_start && array_index_start < next_separator )
+ {
+ char * temp;
+ array_index = strtoul( array_index_start + 1, &temp, 10 );
+ }
+ else
+ {
+ array_index_start = next_separator;
+ }
+ for ( std::vector::const_iterator it = node->children.begin(), ite = node->children.end(); it != ite; ++it )
+ {
+ if ( array_index_start - path == strlen(it->name) &&
+ strncmp( it->name, path, array_index_start - path ) == 0 )
+ {
+ next_node = &(*it);
+ item_found = true;
+ if ( array_index == 0 ) break;
+ --array_index;
+ }
+ }
+ if ( !item_found ) return emptyNode;
+ node = next_node;
+ if ( *next_separator )
+ {
+ path = next_separator + 1;
+ }
+ else break;
+ }
+ return *node;
+}
+
+void Bml_Parser::parseDocument( const char * source )
+{
+ std::vector indents;
+ std::string last_name;
+ std::string current_path;
+
+ document.clear();
+
+ size_t last_indent = ~0;
+
+ Bml_Node node;
+
+ while ( *source )
+ {
+ const char * line_end = strchr( source, '\n' );
+ if ( !line_end ) line_end = source + strlen( source );
+
+ if ( node.getName() ) last_name = node.getName();
+
+ node.setLine( source );
+
+ size_t indent = 0;
+ while ( source < line_end && *source <= 0x20 )
+ {
+ source++;
+ indent++;
+ }
+
+ if ( last_indent == ~0 ) last_indent = indent;
+
+ if ( indent > last_indent )
+ {
+ indents.push_back( last_indent );
+ last_indent = indent;
+ if ( current_path.length() ) current_path += ":";
+ current_path += last_name;
+ }
+ else if ( indent < last_indent )
+ {
+ while ( last_indent > indent && indents.size() )
+ {
+ last_indent = *(indents.end() - 1);
+ indents.pop_back();
+ size_t colon = current_path.find_last_of( ':' );
+ if ( colon != ~0 ) current_path.resize( colon );
+ else current_path.resize( 0 );
+ }
+ last_indent = indent;
+ }
+
+ document.walkToNode( current_path.c_str() ).addChild( node );
+
+ source = line_end;
+ while ( *source && *source == '\n' ) source++;
+ }
+}
+
+const char * Bml_Parser::enumValue(const char *path) const
+{
+ return document.walkToNode(path).getValue();
+}
+
+#if 0
+void Bml_Parser::print(Bml_Node const* node, unsigned int indent) const
+{
+ if (node == 0) node = &document;
+
+ for (unsigned i = 0; i < indent; ++i) printf(" ");
+
+ printf("%s", node->getName());
+ if (node->getValue()) printf(":%s", node->getValue());
+ printf("\n");
+
+ indent++;
+
+ for (unsigned i = 0, j = node->getChildCount(); i < j; ++i)
+ {
+ Bml_Node const& child = node->getChild(i);
+ print( &child, indent );
+ }
+}
+#endif
diff --git a/Frameworks/GME/gme/Bml_Parser.h b/Frameworks/GME/gme/Bml_Parser.h
new file mode 100644
index 000000000..c46c51146
--- /dev/null
+++ b/Frameworks/GME/gme/Bml_Parser.h
@@ -0,0 +1,52 @@
+#ifndef BML_PARSER_H
+#define BML_PARSER_H
+
+#include
+
+class Bml_Node
+{
+ char * name;
+ char * value;
+
+ std::vector children;
+
+ static Bml_Node emptyNode;
+
+public:
+ Bml_Node();
+ Bml_Node(Bml_Node const& in);
+
+ ~Bml_Node();
+
+ void clear();
+
+ void setLine(const char * line);
+ void addChild(Bml_Node const& child);
+
+ const char * getName() const;
+ const char * getValue() const;
+
+ size_t getChildCount() const;
+ Bml_Node const& getChild(size_t index) const;
+
+ Bml_Node & walkToNode( const char * path );
+ Bml_Node const& walkToNode( const char * path ) const;
+};
+
+class Bml_Parser
+{
+ Bml_Node document;
+
+public:
+ Bml_Parser() { }
+
+ void parseDocument(const char * document);
+
+ const char * enumValue(const char * path) const;
+
+#if 0
+ void print(Bml_Node const* node = 0, unsigned int indent = 0) const;
+#endif
+};
+
+#endif // BML_PARSER_H
diff --git a/Frameworks/GME/gme/C140_Emu.cpp b/Frameworks/GME/gme/C140_Emu.cpp
new file mode 100644
index 000000000..c34c09b6f
--- /dev/null
+++ b/Frameworks/GME/gme/C140_Emu.cpp
@@ -0,0 +1,77 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "C140_Emu.h"
+#include "c140.h"
+
+C140_Emu::C140_Emu() { chip = 0; }
+
+C140_Emu::~C140_Emu()
+{
+ if ( chip ) device_stop_c140( chip );
+}
+
+int C140_Emu::set_rate( int type, double sample_rate, double clock_rate )
+{
+ if ( chip )
+ {
+ device_stop_c140( chip );
+ chip = 0;
+ }
+
+ chip = device_start_c140( sample_rate, clock_rate, type );
+ if ( !chip )
+ return 1;
+
+ reset();
+ return 0;
+}
+
+void C140_Emu::reset()
+{
+ device_reset_c140( chip );
+ c140_set_mute_mask( chip, 0 );
+}
+
+void C140_Emu::write( int addr, int data )
+{
+ c140_w( chip, addr, data );
+}
+
+void C140_Emu::write_rom( int size, int start, int length, void * data )
+{
+ c140_write_rom( chip, size, start, length, (const UINT8 *) data );
+}
+
+void C140_Emu::mute_voices( int mask )
+{
+ c140_set_mute_mask( chip, mask );
+}
+
+void C140_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ c140_update( chip, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/C140_Emu.h b/Frameworks/GME/gme/C140_Emu.h
new file mode 100644
index 000000000..8011e91a6
--- /dev/null
+++ b/Frameworks/GME/gme/C140_Emu.h
@@ -0,0 +1,36 @@
+// C140 sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef C140_EMU_H
+#define C140_EMU_H
+
+class C140_Emu {
+ void* chip;
+public:
+ C140_Emu();
+ ~C140_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int type, double sample_rate, double clock_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 24 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Scales ROM size, then writes length bytes from data at start offset
+ void write_rom( int size, int start, int length, void * data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Chip_Resampler.h b/Frameworks/GME/gme/Chip_Resampler.h
new file mode 100644
index 000000000..276a50379
--- /dev/null
+++ b/Frameworks/GME/gme/Chip_Resampler.h
@@ -0,0 +1,147 @@
+// Fir_Resampler chip emulator container that mixes into the output buffer
+
+// Game_Music_Emu $vers
+#ifndef CHIP_RESAMPLER_H
+#define CHIP_RESAMPLER_H
+
+#include "blargg_source.h"
+
+#include "Fir_Resampler.h"
+typedef Fir_Resampler_Norm Chip_Resampler_Downsampler;
+
+int const resampler_extra = 0; //34;
+
+template
+class Chip_Resampler_Emu : public Emu {
+ int last_time;
+ short* out;
+ typedef short dsample_t;
+ enum { disabled_time = -1 };
+ enum { gain_bits = 14 };
+ blargg_vector sample_buf;
+ int sample_buf_size;
+ int oversamples_per_frame;
+ int buf_pos;
+ int buffered;
+ int resampler_size;
+ int gain_;
+
+ Chip_Resampler_Downsampler resampler;
+
+ void mix_samples( short * buf, int count )
+ {
+ dsample_t * inptr = sample_buf.begin();
+ for ( unsigned i = 0; i < count * 2; i++ )
+ {
+ int sample = inptr[i];
+ sample += buf[i];
+ if ((short)sample != sample) sample = 0x7FFF ^ (sample >> 31);
+ buf[i] = sample;
+ }
+ }
+
+public:
+ Chip_Resampler_Emu() { last_time = disabled_time; out = NULL; }
+ blargg_err_t setup( double oversample, double rolloff, double gain )
+ {
+ gain_ = (int) ((1 << gain_bits) * gain);
+ RETURN_ERR( resampler.set_rate( oversample ) );
+ return reset_resampler();
+ }
+
+ blargg_err_t reset()
+ {
+ Emu::reset();
+ resampler.clear();
+ return blargg_ok;
+ }
+
+ blargg_err_t reset_resampler()
+ {
+ unsigned int pairs;
+ double rate = resampler.rate();
+ if ( rate >= 1.0 ) pairs = 64.0 * rate;
+ else pairs = 64.0 / rate;
+ RETURN_ERR( sample_buf.resize( (pairs + (pairs >> 2)) * 2 ) );
+ resize( pairs );
+ resampler_size = oversamples_per_frame + (oversamples_per_frame >> 2);
+ RETURN_ERR( resampler.resize_buffer( resampler_size ) );
+ return blargg_ok;
+ }
+
+ void resize( int pairs )
+ {
+ int new_sample_buf_size = pairs * 2;
+ //new_sample_buf_size = new_sample_buf_size / 4 * 4; // TODO: needed only for 3:2 downsampler
+ if ( sample_buf_size != new_sample_buf_size )
+ {
+ if ( (unsigned) new_sample_buf_size > sample_buf.size() )
+ {
+ check( false );
+ return;
+ }
+ sample_buf_size = new_sample_buf_size;
+ oversamples_per_frame = int (pairs * resampler.rate()) * 2 + 2;
+ clear();
+ }
+ }
+
+ void clear()
+ {
+ buf_pos = buffered = 0;
+ resampler.clear();
+ }
+
+ void enable( bool b = true ) { last_time = b ? 0 : disabled_time; }
+ bool enabled() const { return last_time != disabled_time; }
+ void begin_frame( short* buf ) { out = buf; last_time = 0; }
+
+ int run_until( int time )
+ {
+ int count = time - last_time;
+ while ( count > 0 )
+ {
+ if ( last_time < 0 )
+ return false;
+ last_time = time;
+ if ( buffered )
+ {
+ int samples_to_copy = buffered;
+ if ( samples_to_copy > count ) samples_to_copy = count;
+ memcpy( out, sample_buf.begin(), samples_to_copy * sizeof(short) * 2 );
+ memcpy( sample_buf.begin(), sample_buf.begin() + samples_to_copy * 2, ( buffered - samples_to_copy ) * 2 * sizeof(short) );
+ buffered -= samples_to_copy;
+ count -= samples_to_copy;
+ continue;
+ }
+ int sample_count = oversamples_per_frame - resampler.written() + resampler_extra;
+ memset( resampler.buffer(), 0, sample_count * sizeof(*resampler.buffer()) );
+ Emu::run( sample_count >> 1, resampler.buffer() );
+ for ( unsigned i = 0; i < sample_count; i++ )
+ {
+ dsample_t * ptr = resampler.buffer() + i;
+ *ptr = ( *ptr * gain_ ) >> gain_bits;
+ }
+ short* p = out;
+ resampler.write( sample_count );
+ sample_count = resampler.read( sample_buf.begin(), count * 2 > sample_buf_size ? sample_buf_size : count * 2 ) >> 1;
+ if ( sample_count > count )
+ {
+ out += count * Emu::out_chan_count;
+ mix_samples( p, count );
+ memmove( sample_buf.begin(), sample_buf.begin() + count * 2, (sample_count - count) * 2 * sizeof(short) );
+ buffered = sample_count - count;
+ return true;
+ }
+ else if (!sample_count) return true;
+ out += sample_count * Emu::out_chan_count;
+ mix_samples( p, sample_count );
+ count -= sample_count;
+ }
+ return true;
+ }
+};
+
+
+
+#endif
diff --git a/Frameworks/GME/gme/Downsampler.cpp b/Frameworks/GME/gme/Downsampler.cpp
new file mode 100644
index 000000000..9df5eb2be
--- /dev/null
+++ b/Frameworks/GME/gme/Downsampler.cpp
@@ -0,0 +1,74 @@
+// $package. http://www.slack.net/~ant/
+
+#include "Downsampler.h"
+
+/* Copyright (C) 2004-2008 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"
+
+int const shift = 14;
+int const unit = 1 << shift;
+
+void Downsampler::clear_()
+{
+ pos = 0;
+ Resampler::clear_();
+}
+
+Downsampler::Downsampler()
+{
+ clear();
+}
+
+blargg_err_t Downsampler::set_rate_( double new_factor )
+{
+ step = (int) (new_factor * unit + 0.5);
+ return Resampler::set_rate_( 1.0 / unit * step );
+}
+
+Resampler::sample_t const* Downsampler::resample_( sample_t** out_,
+ sample_t const* out_end, sample_t const in [], int in_size )
+{
+ in_size -= write_offset;
+ if ( in_size > 0 )
+ {
+ sample_t* BLARGG_RESTRICT out = *out_;
+ sample_t const* const in_end = in + in_size;
+
+ int const step = this->step;
+ int pos = this->pos;
+
+ // TODO: IIR filter, then linear resample
+ // TODO: detect skipped sample, allowing merging of IIR and resample?
+
+ do
+ {
+ #define INTERP( i, out )\
+ out = (in [0 + i] * (unit - pos) + ((in [2 + i] + in [4 + i] + in [6 + i]) << shift) +\
+ in [8 + i] * pos) >> (shift + 2);
+
+ int out_0;
+ INTERP( 0, out_0 )
+ INTERP( 1, out [0] = out_0; out [1] )
+ out += stereo;
+
+ pos += step;
+ in += ((unsigned) pos >> shift) * stereo;
+ pos &= unit - 1;
+ }
+ while ( in < in_end && out < out_end );
+
+ this->pos = pos;
+ *out_ = out;
+ }
+ return in;
+}
diff --git a/Frameworks/GME/gme/Downsampler.h b/Frameworks/GME/gme/Downsampler.h
new file mode 100644
index 000000000..a26dec466
--- /dev/null
+++ b/Frameworks/GME/gme/Downsampler.h
@@ -0,0 +1,25 @@
+// Linear downsampler with pre-low-pass
+
+// $package
+#ifndef DOWNSAMPLER_H
+#define DOWNSAMPLER_H
+
+#include "Resampler.h"
+
+class Downsampler : public Resampler {
+public:
+ Downsampler();
+
+protected:
+ virtual blargg_err_t set_rate_( double );
+ virtual void clear_();
+ virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int );
+
+private:
+ enum { stereo = 2 };
+ enum { write_offset = 8 * stereo };
+ int pos;
+ int step;
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Gb_Cpu_run.h b/Frameworks/GME/gme/Gb_Cpu_run.h
new file mode 100644
index 000000000..69ac8f68f
--- /dev/null
+++ b/Frameworks/GME/gme/Gb_Cpu_run.h
@@ -0,0 +1,1183 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#if 0
+/* Define these macros in the source file before #including this file.
+- Parameters might be expressions, so they are best evaluated only once,
+though they NEVER have side-effects, so multiple evaluation is OK.
+- Output parameters might be a multiple-assignment expression like "a=x",
+so they must NOT be parenthesized.
+- Macros "returning" void may use a {} statement block. */
+
+ // 0 <= addr <= 0xFFFF + page_size
+ // time functions can be used
+ int READ_MEM( addr_t );
+ void WRITE_MEM( addr_t, int data );
+
+ // Access of 0xFF00 + offset
+ // 0 <= offset <= 0xFF
+ int READ_IO( int offset );
+ void WRITE_IO( int offset, int data );
+
+ // Often-used instructions use this instead of READ_MEM
+ void READ_FAST( addr_t, int& out );
+
+// The following can be used within macros:
+
+ // Current time
+ cpu_time_t TIME();
+#endif
+
+/* Copyright (C) 2003-2009 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 */
+
+// Common instructions:
+//
+// 365880 FA LD A,(nn)
+// 355863 20 JR NZ
+// 313655 21 LD HL,nn
+// 274580 28 JR Z
+// 252878 FE CP n
+// 230541 7E LD A,(HL)
+// 226209 2A LD A,(HL+)
+// 217467 CD CALL
+// 212034 C9 RET
+// 208376 CB CB prefix
+//
+// 27486 CB 7E BIT 7,(HL)
+// 15925 CB 76 BIT 6,(HL)
+// 13035 CB 19 RR C
+// 11557 CB 7F BIT 7,A
+// 10898 CB 37 SWAP A
+// 10208 CB 66 BIT 4,(HL)
+
+// Allows MWCW debugger to step through code properly
+#ifdef CPU_BEGIN
+ CPU_BEGIN
+#endif
+
+#define TIME() s.time
+
+#define CODE_PAGE( addr ) s.code_map [GB_CPU_PAGE( addr )]
+#define READ_CODE( addr ) (CODE_PAGE( addr ) [GB_CPU_OFFSET( addr )])
+
+// Flags with hex value for clarity when used as mask.
+// Stored in indicated variable during emulation.
+int const z80 = 0x80; // cz
+int const n40 = 0x40; // ph
+int const h20 = 0x20; // ph
+int const c10 = 0x10; // cz
+
+#define SET_FLAGS( in )\
+{\
+ cz = ((in) << 4 & 0x100) + (~(in) >> 7 & 1);\
+ ph = (~(in) << 2 & 0x100) + ((in) >> 1 & 0x10);\
+}
+
+// random bits in cz to catch misuse of them
+#define SET_FLAGS_DEBUG( in )\
+{\
+ cz = ((in) << 4 & 0x100) | (rand() & ~0x1FF) | ((in) & 0x80 ? 0 : (rand() & 0xFF) | 1);\
+ ph = (~(in) << 2 & 0x100) | (((in) >> 1 & 0x10) ^ BYTE( cz ));\
+}
+
+#define GET_FLAGS( out )\
+{\
+ out = (cz >> 4 & c10);\
+ out += ~ph >> 2 & n40;\
+ out += (ph ^ cz) << 1 & h20;\
+ if ( !BYTE( cz ) )\
+ out += z80;\
+}
+
+#define CC_NZ() ( BYTE( cz ))
+#define CC_Z() (!BYTE( cz ))
+#define CC_NC() (!(cz & 0x100))
+#define CC_C() ( cz & 0x100 )
+
+// Truncation
+#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */
+#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
+#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */
+
+{
+ Gb_Cpu::cpu_state_t s;
+ CPU.cpu_state = &s;
+ memcpy( &s, &CPU.cpu_state_, sizeof s );
+
+ union {
+ struct {
+ #if BLARGG_BIG_ENDIAN
+ byte b, c, d, e, h, l, flags, a;
+ #else
+ byte c, b, e, d, l, h, a, flags;
+ #endif
+ } rg; // individual registers
+ Gb_Cpu::core_regs_t rp; // pairs
+
+ byte r8_ [8]; // indexed registers (use R8 macro due to endian dependence)
+ BOOST::uint16_t r16 [4]; // indexed pairs
+ };
+ BLARGG_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 );
+
+ #if BLARGG_BIG_ENDIAN
+ #define R8( n ) (r8_ [n])
+ #elif BLARGG_LITTLE_ENDIAN
+ #define R8( n ) (r8_ [(n) ^ 1])
+ #else
+ // Be sure "blargg_endian.h" has been #included in the file that #includes this
+ #error "Byte order of CPU must be known"
+ #endif
+
+ rp = CPU.r;
+ int pc = CPU.r.pc;
+ int sp = CPU.r.sp;
+ int ph;
+ int cz;
+ SET_FLAGS( rg.flags );
+
+ int time = s.time;
+
+loop:
+
+ check( (unsigned) pc < 0x10000 + 1 ); // +1 so emulator can catch wrap-around
+ check( (unsigned) sp < 0x10000 );
+
+ byte const* instr = CODE_PAGE( pc );
+ int op;
+
+ if ( GB_CPU_OFFSET(~0) == ~0 )
+ {
+ op = instr [pc];
+ pc++;
+ instr += pc;
+ }
+ else
+ {
+ instr += GB_CPU_OFFSET( pc );
+ op = *instr++;
+ pc++;
+ }
+
+#define GET_ADDR() GET_LE16( instr )
+
+ static byte const instr_times [256*2] = {
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 4,12, 8, 8, 4, 4, 8, 4,20, 8, 8, 8, 4, 4, 8, 4,// 0
+ 4,12, 8, 8, 4, 4, 8, 4,12, 8, 8, 8, 4, 4, 8, 4,// 1
+ 8,12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4,// 2
+ 8,12, 8, 8,12,12,12, 4, 8, 8, 8, 8, 4, 4, 8, 4,// 3
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 4
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 5
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 6
+ 8, 8, 8, 8, 8, 8, 0, 8, 4, 4, 4, 4, 4, 4, 8, 4,// 7
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 8
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 9
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// A
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// B
+ 8,12,16,16,12,16, 8,16, 8,16,16, 0,12,24, 8,16,// C
+ 8,12,16, 0,12,16, 8,16, 8,16,16, 0,12, 0, 8,16,// D
+ 12,12, 8, 0, 0,16, 8,16,16, 4,16, 0, 0, 0, 8,16,// E
+ 12,12, 8, 4, 0,16, 8,16,12, 8,16, 4, 0, 0, 8,16,// F
+
+ // CB prefixed
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 0
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 1
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 2
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 3
+ 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 4
+ 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 5
+ 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 6
+ 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 7
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 8
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 9
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// A
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// B
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// C
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// D
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// E
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// F
+ };
+
+ if ( time >= 0 )
+ goto stop;
+
+ time += instr_times [op];
+
+ int data;
+ data = *instr;
+ s.time = time;
+
+ #ifdef CPU_INSTR_HOOK
+ { CPU_INSTR_HOOK( (pc-1), (instr-1), rg.a, rp.bc, rp.de, rp.hl, sp ); }
+ #endif
+
+ switch ( op )
+ {
+
+// TODO: more efficient way to handle negative branch that wraps PC around
+#define BRANCH_( cond, clocks )\
+{\
+ pc++;\
+ if ( !(cond) )\
+ goto loop;\
+ pc = WORD( pc + SBYTE( data ) );\
+ time += clocks;\
+ goto loop;\
+}
+
+#define BRANCH( cond ) BRANCH_( cond, 4 )
+
+// Most Common
+
+ case 0x20: // JR NZ
+ BRANCH( CC_NZ() )
+
+ case 0x21: // LD HL,IMM (common)
+ rp.hl = GET_ADDR();
+ pc += 2;
+ goto loop;
+
+ case 0x28: // JR Z
+ BRANCH( CC_Z() )
+
+ case 0xF2: // LD A,(0xFF00+C)
+ READ_IO( rg.c, rg.a );
+ goto loop;
+
+ case 0xF0: // LD A,(0xFF00+imm)
+ pc++;
+ READ_IO( data, rg.a );
+ goto loop;
+
+ {
+ int temp;
+ case 0x0A: // LD A,(BC)
+ temp = rp.bc;
+ goto ld_a_ind_comm;
+
+ case 0x3A: // LD A,(HL-)
+ temp = rp.hl;
+ rp.hl = temp - 1;
+ goto ld_a_ind_comm;
+
+ case 0x1A: // LD A,(DE)
+ temp = rp.de;
+ goto ld_a_ind_comm;
+
+ case 0x2A: // LD A,(HL+) (common)
+ temp = rp.hl;
+ rp.hl = temp + 1;
+ goto ld_a_ind_comm;
+
+ case 0xFA: // LD A,IND16 (common)
+ temp = GET_ADDR();
+ pc += 2;
+ ld_a_ind_comm:
+ READ_FAST( temp, rg.a );
+ goto loop;
+ }
+
+ {
+ int temp;
+ case 0xBE: // CP (HL)
+ temp = READ_MEM( rp.hl );
+ goto cmp_comm;
+
+ case 0xB8: // CP B
+ case 0xB9: // CP C
+ case 0xBA: // CP D
+ case 0xBB: // CP E
+ case 0xBC: // CP H
+ case 0xBD: // CP L
+ case 0xBF: // CP A
+ temp = R8( op & 7 );
+ cmp_comm:
+ ph = rg.a ^ temp; // N=1 H=*
+ cz = rg.a - temp; // C=* Z=*
+ goto loop;
+ }
+
+ case 0xFE: // CP IMM
+ pc++;
+ ph = rg.a ^ data; // N=1 H=*
+ cz = rg.a - data; // C=* Z=*
+ goto loop;
+
+ case 0x46: // LD B,(HL)
+ case 0x4E: // LD C,(HL)
+ case 0x56: // LD D,(HL)
+ case 0x5E: // LD E,(HL)
+ case 0x66: // LD H,(HL)
+ case 0x6E: // LD L,(HL)
+ case 0x7E:{// LD A,(HL)
+ int addr = rp.hl;
+ READ_FAST( addr, R8( op >> 3 & 7 ) );
+ goto loop;
+ }
+
+ case 0xC4: // CNZ (next-most-common)
+ pc += 2;
+ if ( CC_Z() )
+ goto loop;
+ call:
+ time += 12;
+ pc -= 2;
+ case 0xCD: // CALL (most-common)
+ data = pc + 2;
+ pc = GET_ADDR();
+ push: {
+ int addr = WORD( sp - 1 );
+ WRITE_MEM( addr, (data >> 8) );
+ sp = WORD( sp - 2 );
+ WRITE_MEM( sp, data );
+ goto loop;
+ }
+
+ case 0xC8: // RET Z (next-most-common)
+ if ( CC_NZ() )
+ goto loop;
+ ret:
+ time += 12;
+ case 0xD9: // RETI
+ case 0xC9:{// RET (most common)
+ pc = READ_MEM( sp );
+ int addr = sp + 1;
+ sp = WORD( sp + 2 );
+ pc += 0x100 * READ_MEM( addr );
+ goto loop;
+ }
+
+ case 0x00: // NOP
+ case 0x40: // LD B,B
+ case 0x49: // LD C,C
+ case 0x52: // LD D,D
+ case 0x5B: // LD E,E
+ case 0x64: // LD H,H
+ case 0x6D: // LD L,L
+ case 0x7F: // LD A,A
+ goto loop;
+
+// CB Instructions
+
+ case 0xCB:
+ time += (instr_times + 256) [data];
+ pc++;
+ // now data is the opcode
+ switch ( data ) {
+
+ case 0x46: // BIT b,(HL)
+ case 0x4E:
+ case 0x56:
+ case 0x5E:
+ case 0x66:
+ case 0x6E:
+ case 0x76:
+ case 0x7E: {
+ int addr = rp.hl;
+ READ_FAST( addr, op );
+ goto bit_comm;
+ }
+
+ case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r
+ case 0x44: case 0x45: case 0x47: case 0x48:
+ case 0x49: case 0x4A: case 0x4B: case 0x4C:
+ case 0x4D: case 0x4F: case 0x50: case 0x51:
+ case 0x52: case 0x53: case 0x54: case 0x55:
+ case 0x57: case 0x58: case 0x59: case 0x5A:
+ case 0x5B: case 0x5C: case 0x5D: case 0x5F:
+ case 0x60: case 0x61: case 0x62: case 0x63:
+ case 0x64: case 0x65: case 0x67: case 0x68:
+ case 0x69: case 0x6A: case 0x6B: case 0x6C:
+ case 0x6D: case 0x6F: case 0x70: case 0x71:
+ case 0x72: case 0x73: case 0x74: case 0x75:
+ case 0x77: case 0x78: case 0x79: case 0x7A:
+ case 0x7B: case 0x7C: case 0x7D: case 0x7F:
+ op = R8( data & 7 );
+ bit_comm:
+ ph = op >> (data >> 3 & 7) & 1;
+ cz = (cz & 0x100) + ph;
+ ph ^= 0x110; // N=0 H=1
+ goto loop;
+
+ case 0x86: // RES b,(HL)
+ case 0x8E:
+ case 0x96:
+ case 0x9E:
+ case 0xA6:
+ case 0xAE:
+ case 0xB6:
+ case 0xBE: {
+ int temp = READ_MEM( rp.hl );
+ temp &= ~(1 << (data >> 3 & 7));
+ WRITE_MEM( rp.hl, temp );
+ goto loop;
+ }
+
+ case 0xC6: // SET b,(HL)
+ case 0xCE:
+ case 0xD6:
+ case 0xDE:
+ case 0xE6:
+ case 0xEE:
+ case 0xF6:
+ case 0xFE: {
+ int temp = READ_MEM( rp.hl );
+ temp |= 1 << (data >> 3 & 7);
+ WRITE_MEM( rp.hl, temp );
+ goto loop;
+ }
+
+ case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r
+ case 0xC4: case 0xC5: case 0xC7: case 0xC8:
+ case 0xC9: case 0xCA: case 0xCB: case 0xCC:
+ case 0xCD: case 0xCF: case 0xD0: case 0xD1:
+ case 0xD2: case 0xD3: case 0xD4: case 0xD5:
+ case 0xD7: case 0xD8: case 0xD9: case 0xDA:
+ case 0xDB: case 0xDC: case 0xDD: case 0xDF:
+ case 0xE0: case 0xE1: case 0xE2: case 0xE3:
+ case 0xE4: case 0xE5: case 0xE7: case 0xE8:
+ case 0xE9: case 0xEA: case 0xEB: case 0xEC:
+ case 0xED: case 0xEF: case 0xF0: case 0xF1:
+ case 0xF2: case 0xF3: case 0xF4: case 0xF5:
+ case 0xF7: case 0xF8: case 0xF9: case 0xFA:
+ case 0xFB: case 0xFC: case 0xFD: case 0xFF:
+ R8( data & 7 ) |= 1 << (data >> 3 & 7);
+ goto loop;
+
+ case 0x80: case 0x81: case 0x82: case 0x83: // RES b,r
+ case 0x84: case 0x85: case 0x87: case 0x88:
+ case 0x89: case 0x8A: case 0x8B: case 0x8C:
+ case 0x8D: case 0x8F: case 0x90: case 0x91:
+ case 0x92: case 0x93: case 0x94: case 0x95:
+ case 0x97: case 0x98: case 0x99: case 0x9A:
+ case 0x9B: case 0x9C: case 0x9D: case 0x9F:
+ case 0xA0: case 0xA1: case 0xA2: case 0xA3:
+ case 0xA4: case 0xA5: case 0xA7: case 0xA8:
+ case 0xA9: case 0xAA: case 0xAB: case 0xAC:
+ case 0xAD: case 0xAF: case 0xB0: case 0xB1:
+ case 0xB2: case 0xB3: case 0xB4: case 0xB5:
+ case 0xB7: case 0xB8: case 0xB9: case 0xBA:
+ case 0xBB: case 0xBC: case 0xBD: case 0xBF:
+ R8( data & 7 ) &= ~(1 << (data >> 3 & 7));
+ goto loop;
+
+ case 0x36: // SWAP (HL)
+ op = READ_MEM( rp.hl );
+ goto swap_comm;
+
+ case 0x30: // SWAP B
+ case 0x31: // SWAP C
+ case 0x32: // SWAP D
+ case 0x33: // SWAP E
+ case 0x34: // SWAP H
+ case 0x35: // SWAP L
+ case 0x37: // SWAP A
+ op = R8( data & 7 );
+ swap_comm:
+ op = (op >> 4) + (op << 4);
+ cz = BYTE( op );
+ ph = cz + 0x100;
+ if ( data == 0x36 )
+ goto write_hl_op_ff;
+ R8( data & 7 ) = op;
+ goto loop;
+
+// Shift/Rotate
+
+ case 0x26: // SLA (HL)
+ cz = 0;
+ case 0x16: // RL (HL)
+ cz = (cz >> 8 & 1) + (READ_MEM( rp.hl ) << 1);
+ goto rl_hl_common;
+
+ case 0x06: // RLC (HL)
+ cz = READ_MEM( rp.hl );
+ cz = (cz << 1) + (cz >> 7 & 1);
+ rl_hl_common:
+ // Z=* C=*
+ ph = cz | 0x100; // N=0 H=0
+ WRITE_MEM( rp.hl, cz );
+ goto loop;
+
+ case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA r
+ cz = 0;
+ case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL r
+ cz = (cz >> 8 & 1) + (R8( data & 7 ) << 1);
+ goto rl_common;
+
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC r
+ cz = R8( data & 7 );
+ cz = (cz << 1) + (cz >> 7 & 1);
+ rl_common:
+ // Z=* C=*
+ ph = cz | 0x100; // N=0 H=0
+ R8( data & 7 ) = cz;
+ goto loop;
+
+ case 0x0E: // RRC (HL)
+ cz = READ_MEM( rp.hl );
+ cz += cz << 8 & 0x100;
+ goto rr_hl_common;
+
+ case 0x2E: // SRA (HL)
+ cz = READ_MEM( rp.hl );
+ cz += cz << 1 & 0x100;
+ goto rr_hl_common;
+
+ case 0x3E: // SRL (HL)
+ cz = 0;
+ case 0x1E: // RR (HL)
+ cz = (cz & 0x100) + READ_MEM( rp.hl );
+ rr_hl_common:
+ cz = (cz << 8) + (cz >> 1); // Z=* C=*
+ ph = cz | 0x100; // N=0 H=0
+ WRITE_MEM( rp.hl, cz );
+ goto loop;
+
+ case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC r
+ cz = R8( data & 7 );
+ cz += cz << 8 & 0x100;
+ goto rr_common;
+
+ case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA r
+ cz = R8( data & 7 );
+ cz += cz << 1 & 0x100;
+ goto rr_common;
+
+ case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL r
+ cz = 0;
+ case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR r
+ cz = (cz & 0x100) + R8( data & 7 );
+ rr_common:
+ cz = (cz << 8) + (cz >> 1); // Z=* C=*
+ ph = cz | 0x100; // N=0 H=0
+ R8( data & 7 ) = cz;
+ goto loop;
+
+ } // CB op
+ assert( false ); // unhandled CB op
+
+ case 0x07: // RLCA
+ cz = rg.a >> 7;
+ goto rlc_common;
+ case 0x17: // RLA
+ cz = cz >> 8 & 1;
+ rlc_common:
+ cz += rg.a << 1;
+ ph = cz | 0x100;
+ rg.a = BYTE( cz );
+ cz |= 1;
+ goto loop;
+
+ case 0x0F: // RRCA
+ ph = rg.a << 8;
+ goto rrc_common;
+ case 0x1F: // RRA
+ ph = cz;
+ rrc_common:
+ cz = (rg.a << 8) + 1; // Z=0 C=*
+ rg.a = ((ph & 0x100) + rg.a) >> 1;
+ ph = 0x100; // N=0 H=0
+ goto loop;
+
+// Load
+
+ case 0x70: // LD (HL),B
+ case 0x71: // LD (HL),C
+ case 0x72: // LD (HL),D
+ case 0x73: // LD (HL),E
+ case 0x74: // LD (HL),H
+ case 0x75: // LD (HL),L
+ case 0x77: // LD (HL),A
+ op = R8( op & 7 );
+ write_hl_op_ff:
+ WRITE_MEM( rp.hl, op );
+ goto loop;
+
+ case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x47: // LD r,r
+ case 0x48: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4F:
+ case 0x50: case 0x51: case 0x53: case 0x54: case 0x55: case 0x57:
+ case 0x58: case 0x59: case 0x5A: case 0x5C: case 0x5D: case 0x5F:
+ case 0x60: case 0x61: case 0x62: case 0x63: case 0x65: case 0x67:
+ case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6F:
+ case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D:
+ R8( op >> 3 & 7 ) = R8( op & 7 );
+ goto loop;
+
+ case 0x08: // LD IND16,SP
+ data = GET_ADDR();
+ pc += 2;
+ WRITE_MEM( data, sp );
+ data++;
+ WRITE_MEM( data, (sp >> 8) );
+ goto loop;
+
+ case 0xF9: // LD SP,HL
+ sp = rp.hl;
+ goto loop;
+
+ case 0x31: // LD SP,IMM
+ sp = GET_ADDR();
+ pc += 2;
+ goto loop;
+
+ case 0x01: // LD BC,IMM
+ case 0x11: // LD DE,IMM
+ r16 [(unsigned) op >> 4] = GET_ADDR();
+ pc += 2;
+ goto loop;
+
+ case 0xE2: // LD (0xFF00+C),A
+ WRITE_IO( rg.c, rg.a );
+ goto loop;
+
+ case 0xE0: // LD (0xFF00+imm),A
+ pc++;
+ WRITE_IO( data, rg.a );
+ goto loop;
+
+ {
+ int temp;
+ case 0x32: // LD (HL-),A
+ temp = rp.hl;
+ rp.hl = temp - 1;
+ goto write_data_rg_a;
+
+ case 0x02: // LD (BC),A
+ temp = rp.bc;
+ goto write_data_rg_a;
+
+ case 0x12: // LD (DE),A
+ temp = rp.de;
+ goto write_data_rg_a;
+
+ case 0x22: // LD (HL+),A
+ temp = rp.hl;
+ rp.hl = temp + 1;
+ goto write_data_rg_a;
+
+ case 0xEA: // LD IND16,A (common)
+ temp = GET_ADDR();
+ pc += 2;
+ write_data_rg_a:
+ WRITE_MEM( temp, rg.a );
+ goto loop;
+ }
+
+ case 0x06: // LD B,IMM
+ rg.b = data;
+ pc++;
+ goto loop;
+
+ case 0x0E: // LD C,IMM
+ rg.c = data;
+ pc++;
+ goto loop;
+
+ case 0x16: // LD D,IMM
+ rg.d = data;
+ pc++;
+ goto loop;
+
+ case 0x1E: // LD E,IMM
+ rg.e = data;
+ pc++;
+ goto loop;
+
+ case 0x26: // LD H,IMM
+ rg.h = data;
+ pc++;
+ goto loop;
+
+ case 0x2E: // LD L,IMM
+ rg.l = data;
+ pc++;
+ goto loop;
+
+ case 0x36: // LD (HL),IMM
+ WRITE_MEM( rp.hl, data );
+ pc++;
+ goto loop;
+
+ case 0x3E: // LD A,IMM
+ rg.a = data;
+ pc++;
+ goto loop;
+
+// Increment/decrement
+
+ case 0x03: // INC BC
+ case 0x13: // INC DE
+ case 0x23: // INC HL
+ r16 [(unsigned) op >> 4]++;
+ goto loop;
+
+ case 0x33: // INC SP
+ sp = WORD( sp + 1 );
+ goto loop;
+
+ case 0x0B: // DEC BC
+ case 0x1B: // DEC DE
+ case 0x2B: // DEC HL
+ r16 [(unsigned) op >> 4]--;
+ goto loop;
+
+ case 0x3B: // DEC SP
+ sp = WORD( sp - 1 );
+ goto loop;
+
+ case 0x34: // INC (HL)
+ op = rp.hl;
+ data = READ_MEM( op );
+ data++;
+ WRITE_MEM( op, data );
+ goto inc_comm;
+
+ case 0x04: // INC B
+ case 0x0C: // INC C (common)
+ case 0x14: // INC D
+ case 0x1C: // INC E
+ case 0x24: // INC H
+ case 0x2C: // INC L
+ case 0x3C: // INC A
+ op = op >> 3 & 7;
+ data = R8( op ) + 1;
+ R8( op ) = data;
+ inc_comm:
+ ph = data - 0x101; // N=0 H=*
+ cz = (cz & 0x100) + BYTE( data ); // C=- Z=*
+ goto loop;
+
+ case 0x35: // DEC (HL)
+ op = rp.hl;
+ data = READ_MEM( op );
+ data--;
+ WRITE_MEM( op, data );
+ goto dec_comm;
+
+ case 0x05: // DEC B
+ case 0x0D: // DEC C
+ case 0x15: // DEC D
+ case 0x1D: // DEC E
+ case 0x25: // DEC H
+ case 0x2D: // DEC L
+ case 0x3D: // DEC A
+ op = op >> 3 & 7;
+ data = R8( op ) - 1;
+ R8( op ) = data;
+ dec_comm:
+ ph = data + 1; // N=1 H=*
+ cz = (cz & 0x100) + BYTE( data ); // C=- Z=*
+ goto loop;
+
+// Add 16-bit
+
+ case 0xF8: // LD HL,SP+n
+ case 0xE8:{// ADD SP,n
+ pc++;
+ int t = WORD( sp + SBYTE( data ) );
+ cz = ((BYTE( sp ) + data) & 0x100) + 1; // Z=0 C=*
+ ph = (sp ^ data ^ t) | 0x100; // N=0 H=*
+ if ( op == 0xF8 )
+ {
+ rp.hl = t;
+ goto loop;
+ }
+ sp = t;
+ goto loop;
+ }
+
+ case 0x39: // ADD HL,SP
+ data = sp;
+ goto add_hl_comm;
+
+ case 0x09: // ADD HL,BC
+ case 0x19: // ADD HL,DE
+ case 0x29: // ADD HL,HL
+ data = r16 [(unsigned) op >> 4];
+ add_hl_comm:
+ ph = rp.hl ^ data;
+ data += rp.hl;
+ rp.hl = WORD( data );
+ ph ^= data;
+ cz = BYTE( cz ) + (data >> 8 & 0x100); // C=* Z=-
+ ph = ((ph >> 8) ^ cz) | 0x100; // N=0 H=*
+ goto loop;
+
+ case 0x86: // ADD (HL)
+ data = READ_MEM( rp.hl );
+ goto add_comm;
+
+ case 0x80: // ADD B
+ case 0x81: // ADD C
+ case 0x82: // ADD D
+ case 0x83: // ADD E
+ case 0x84: // ADD H
+ case 0x85: // ADD L
+ case 0x87: // ADD A
+ data = R8( op & 7 );
+ goto add_comm;
+
+ case 0xC6: // ADD IMM
+ pc++;
+ add_comm:
+ ph = (rg.a ^ data) | 0x100; // N=1 H=*
+ cz = rg.a + data; // C=* Z=*
+ rg.a = cz;
+ goto loop;
+
+// Add/Subtract
+
+ case 0x8E: // ADC (HL)
+ data = READ_MEM( rp.hl );
+ goto adc_comm;
+
+ case 0x88: // ADC B
+ case 0x89: // ADC C
+ case 0x8A: // ADC D
+ case 0x8B: // ADC E
+ case 0x8C: // ADC H
+ case 0x8D: // ADC L
+ case 0x8F: // ADC A
+ data = R8( op & 7 );
+ goto adc_comm;
+
+ case 0xCE: // ADC IMM
+ pc++;
+ adc_comm:
+ ph = (rg.a ^ data) | 0x100; // N=1 H=*
+ cz = rg.a + data + (cz >> 8 & 1); // C=* Z=*
+ rg.a = cz;
+ goto loop;
+
+ case 0x96: // SUB (HL)
+ data = READ_MEM( rp.hl );
+ goto sub_comm;
+
+ case 0x90: // SUB B
+ case 0x91: // SUB C
+ case 0x92: // SUB D
+ case 0x93: // SUB E
+ case 0x94: // SUB H
+ case 0x95: // SUB L
+ case 0x97: // SUB A
+ data = R8( op & 7 );
+ goto sub_comm;
+
+ case 0xD6: // SUB IMM
+ pc++;
+ sub_comm:
+ ph = rg.a ^ data; // N=1 H=*
+ cz = rg.a - data; // C=* Z=*
+ rg.a = cz;
+ goto loop;
+
+ case 0x9E: // SBC (HL)
+ data = READ_MEM( rp.hl );
+ goto sbc_comm;
+
+ case 0x98: // SBC B
+ case 0x99: // SBC C
+ case 0x9A: // SBC D
+ case 0x9B: // SBC E
+ case 0x9C: // SBC H
+ case 0x9D: // SBC L
+ case 0x9F: // SBC A
+ data = R8( op & 7 );
+ goto sbc_comm;
+
+ case 0xDE: // SBC IMM
+ pc++;
+ sbc_comm:
+ ph = rg.a ^ data; // N=1 H=*
+ cz = rg.a - data - (cz >> 8 & 1); // C=* Z=*
+ rg.a = cz;
+ goto loop;
+
+// Logical
+
+ case 0xA0: // AND B
+ case 0xA1: // AND C
+ case 0xA2: // AND D
+ case 0xA3: // AND E
+ case 0xA4: // AND H
+ case 0xA5: // AND L
+ data = R8( op & 7 );
+ goto and_comm;
+
+ case 0xA6: // AND (HL)
+ data = READ_MEM( rp.hl );
+ goto and_comm;
+ case 0xE6: // AND IMM
+ pc++;
+ and_comm:
+ cz = rg.a & data; // C=0 Z=*
+ ph = ~cz; // N=0 H=1
+ rg.a = cz;
+ goto loop;
+
+ case 0xA7: // AND A
+ cz = rg.a; // C=0 Z=*
+ ph = ~rg.a; // N=0 H=1
+ goto loop;
+
+ case 0xB0: // OR B
+ case 0xB1: // OR C
+ case 0xB2: // OR D
+ case 0xB3: // OR E
+ case 0xB4: // OR H
+ case 0xB5: // OR L
+ data = R8( op & 7 );
+ goto or_comm;
+
+ case 0xB6: // OR (HL)
+ data = READ_MEM( rp.hl );
+ goto or_comm;
+ case 0xF6: // OR IMM
+ pc++;
+ or_comm:
+ cz = rg.a | data; // C=0 Z=*
+ ph = cz | 0x100; // N=0 H=0
+ rg.a = cz;
+ goto loop;
+
+ case 0xB7: // OR A
+ cz = rg.a; // C=0 Z=*
+ ph = rg.a + 0x100; // N=0 H=0
+ goto loop;
+
+ case 0xA8: // XOR B
+ case 0xA9: // XOR C
+ case 0xAA: // XOR D
+ case 0xAB: // XOR E
+ case 0xAC: // XOR H
+ case 0xAD: // XOR L
+ data = R8( op & 7 );
+ goto xor_comm;
+
+ case 0xAE: // XOR (HL)
+ data = READ_MEM( rp.hl );
+ pc--;
+ case 0xEE: // XOR IMM
+ pc++;
+ xor_comm:
+ cz = rg.a ^ data; // C=0 Z=*
+ ph = cz + 0x100; // N=0 H=0
+ rg.a = cz;
+ goto loop;
+
+ case 0xAF: // XOR A
+ rg.a = 0;
+ cz = 0; // C=0 Z=*
+ ph = 0x100; // N=0 H=0
+ goto loop;
+
+// Stack
+
+ case 0xF1: // POP AF
+ case 0xC1: // POP BC
+ case 0xD1: // POP DE
+ case 0xE1: // POP HL (common)
+ data = READ_MEM( sp );
+ r16 [op >> 4 & 3] = data + 0x100 * READ_MEM( (sp + 1) );
+ sp = WORD( sp + 2 );
+ if ( op != 0xF1 )
+ goto loop;
+
+ SET_FLAGS( rg.a );
+ rg.a = rg.flags;
+ goto loop;
+
+ case 0xC5: // PUSH BC
+ data = rp.bc;
+ goto push;
+
+ case 0xD5: // PUSH DE
+ data = rp.de;
+ goto push;
+
+ case 0xE5: // PUSH HL
+ data = rp.hl;
+ goto push;
+
+ case 0xF5: // PUSH AF
+ GET_FLAGS( data );
+ data += rg.a << 8;
+ goto push;
+
+// Flow control
+
+ case 0xFF: case 0xC7: case 0xCF: case 0xD7: // RST
+ case 0xDF: case 0xE7: case 0xEF: case 0xF7:
+ data = pc;
+ pc = (op & 0x38) + CPU.rst_base;
+ goto push;
+
+ case 0xCC: // CALL Z
+ pc += 2;
+ if ( CC_Z() )
+ goto call;
+ goto loop;
+
+ case 0xD4: // CALL NC
+ pc += 2;
+ if ( CC_NC() )
+ goto call;
+ goto loop;
+
+ case 0xDC: // CALL C
+ pc += 2;
+ if ( CC_C() )
+ goto call;
+ goto loop;
+
+ case 0xC0: // RET NZ
+ if ( CC_NZ() )
+ goto ret;
+ goto loop;
+
+ case 0xD0: // RET NC
+ if ( CC_NC() )
+ goto ret;
+ goto loop;
+
+ case 0xD8: // RET C
+ if ( CC_C() )
+ goto ret;
+ goto loop;
+
+ case 0x18: // JR
+ BRANCH_( true, 0 )
+
+ case 0x30: // JR NC
+ BRANCH( CC_NC() )
+
+ case 0x38: // JR C
+ BRANCH( CC_C() )
+
+ case 0xE9: // LD PC,HL
+ pc = rp.hl;
+ goto loop;
+
+ case 0xC3: // JP (next-most-common)
+ pc = GET_ADDR();
+ goto loop;
+
+ case 0xC2: // JP NZ
+ pc += 2;
+ if ( CC_NZ() )
+ goto jp_taken;
+ time -= 4;
+ goto loop;
+
+ case 0xCA: // JP Z (most common)
+ pc += 2;
+ if ( CC_Z() )
+ goto jp_taken;
+ time -= 4;
+ goto loop;
+
+ jp_taken:
+ pc -= 2;
+ pc = GET_ADDR();
+ goto loop;
+
+ case 0xD2: // JP NC
+ pc += 2;
+ if ( CC_NC() )
+ goto jp_taken;
+ time -= 4;
+ goto loop;
+
+ case 0xDA: // JP C
+ pc += 2;
+ if ( CC_C() )
+ goto jp_taken;
+ time -= 4;
+ goto loop;
+
+// Flags
+
+ case 0x2F: // CPL
+ rg.a = ~rg.a;
+ ph = BYTE( ~cz ); // N=1 H=1
+ goto loop;
+
+ case 0x3F: // CCF
+ ph = cz | 0x100; // N=0 H=0
+ cz ^= 0x100; // C=* Z=-
+ goto loop;
+
+ case 0x37: // SCF
+ ph = cz | 0x100; // N=0 H=0
+ cz |= 0x100; // C=1 Z=-
+ goto loop;
+
+ case 0xF3: // DI
+ goto loop;
+
+ case 0xFB: // EI
+ goto loop;
+
+ case 0x27:{// DAA
+ unsigned a = rg.a;
+ int h = ph ^ cz;
+ if ( ph & 0x100 )
+ {
+ if ( (h & 0x10) || (a & 0x0F) > 9 )
+ a += 6;
+
+ if ( (cz & 0x100) || a > 0x9F )
+ a += 0x60;
+ }
+ else
+ {
+ if ( h & 0x10 )
+ a = (a - 6) & 0xFF;
+
+ if ( cz & 0x100 )
+ a -= 0x60;
+ }
+ cz = (cz & 0x100) | a; // C=- Z=*
+ rg.a = a;
+ ph = (ph & 0x100) + BYTE( a ); // N=- H=0
+ goto loop;
+ }
+
+// Special
+
+ case 0x76: // HALT
+ case 0x10: // STOP
+ case 0xD3: case 0xDB: case 0xDD: // Illegal
+ case 0xE3: case 0xE4: case 0xEB: case 0xEC: case 0xED: // (all freeze CPU)
+ case 0xF4: case 0xFC: case 0xFD:
+ goto stop;
+ }
+
+ // If this fails then an opcode isn't handled above
+ assert( false );
+
+stop:
+ pc--;
+
+ // copy state back
+ CPU.cpu_state_.time = time;
+ CPU.r.pc = pc;
+ CPU.r.sp = sp;
+ {
+ int t;
+ GET_FLAGS( t );
+ rg.flags = t;
+ }
+ CPU.cpu_state = &CPU.cpu_state_;
+ STATIC_CAST(Gb_Cpu::core_regs_t&,CPU.r) = rp;
+}
diff --git a/Frameworks/GME/gme/Gbs_Core.cpp b/Frameworks/GME/gme/Gbs_Core.cpp
new file mode 100644
index 000000000..5703db69b
--- /dev/null
+++ b/Frameworks/GME/gme/Gbs_Core.cpp
@@ -0,0 +1,208 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Gbs_Core.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2009 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"
+
+int const tempo_unit = 16;
+int const idle_addr = 0xF00D;
+int const bank_size = 0x4000;
+
+Gbs_Core::Gbs_Core() : rom( bank_size )
+{
+ tempo = tempo_unit;
+ assert( offsetof (header_t,copyright [32]) == header_t::size );
+}
+
+Gbs_Core::~Gbs_Core() { }
+
+void Gbs_Core::unload()
+{
+ header_.timer_mode = 0; // set_tempo() reads this
+ rom.clear();
+ Gme_Loader::unload();
+}
+
+bool Gbs_Core::header_t::valid_tag() const
+{
+ return 0 == memcmp( tag, "GBS", 3 );
+}
+
+blargg_err_t Gbs_Core::load_( Data_Reader& in )
+{
+ RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) );
+
+ if ( !header_.valid_tag() )
+ return blargg_err_file_type;
+
+ if ( header_.vers != 1 )
+ set_warning( "Unknown file version" );
+
+ if ( header_.timer_mode & 0x78 )
+ set_warning( "Invalid timer mode" );
+
+ addr_t load_addr = get_le16( header_.load_addr );
+ if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F ||
+ load_addr < 0x400 )
+ set_warning( "Invalid load/init/play address" );
+
+ cpu.rst_base = load_addr;
+ rom.set_addr( load_addr );
+
+ return blargg_ok;
+}
+
+void Gbs_Core::set_bank( int n )
+{
+ addr_t addr = rom.mask_addr( n * bank_size );
+ if ( addr == 0 && rom.size() > bank_size )
+ addr = bank_size; // MBC1&2 behavior, bank 0 acts like bank 1
+ cpu.map_code( bank_size, bank_size, rom.at_addr( addr ) );
+}
+
+void Gbs_Core::update_timer()
+{
+ play_period_ = 70224 / tempo_unit; // 59.73 Hz
+
+ if ( header_.timer_mode & 0x04 )
+ {
+ // Using custom rate
+ static byte const rates [4] = { 6, 0, 2, 4 };
+ // TODO: emulate double speed CPU mode rather than halving timer rate
+ int double_speed = header_.timer_mode >> 7;
+ int shift = rates [ram [hi_page + 7] & 3] - double_speed;
+ play_period_ = (256 - ram [hi_page + 6]) << shift;
+ }
+
+ play_period_ *= tempo;
+}
+
+void Gbs_Core::set_tempo( double t )
+{
+ tempo = (int) (tempo_unit / t + 0.5);
+ apu_.set_tempo( t );
+ update_timer();
+}
+
+// Jumps to routine, given pointer to address in file header. Pushes idle_addr
+// as return address, NOT old PC.
+void Gbs_Core::jsr_then_stop( byte const addr [] )
+{
+ check( cpu.r.sp == get_le16( header_.stack_ptr ) );
+ cpu.r.pc = get_le16( addr );
+ write_mem( --cpu.r.sp, idle_addr >> 8 );
+ write_mem( --cpu.r.sp, idle_addr );
+}
+
+blargg_err_t Gbs_Core::start_track( int track, Gb_Apu::mode_t mode )
+{
+ // Reset APU to state expected by most rips
+ static byte const sound_data [] = {
+ 0x80, 0xBF, 0x00, 0x00, 0xB8, // square 1 DAC disabled
+ 0x00, 0x3F, 0x00, 0x00, 0xB8, // square 2 DAC disabled
+ 0x7F, 0xFF, 0x9F, 0x00, 0xB8, // wave DAC disabled
+ 0x00, 0xFF, 0x00, 0x00, 0xB8, // noise DAC disabled
+ 0x77, 0xFF, 0x80, // max volume, all chans in center, power on
+ };
+ apu_.reset( mode );
+ apu_.write_register( 0, 0xFF26, 0x80 ); // power on
+ for ( int i = 0; i < (int) sizeof sound_data; i++ )
+ apu_.write_register( 0, i + apu_.io_addr, sound_data [i] );
+ apu_.end_frame( 1 ); // necessary to get click out of the way
+
+ // Init memory and I/O registers
+ memset( ram, 0, 0x4000 );
+ memset( ram + 0x4000, 0xFF, 0x1F80 );
+ memset( ram + 0x5F80, 0, sizeof ram - 0x5F80 );
+ ram [hi_page] = 0; // joypad reads back as 0
+ ram [idle_addr - ram_addr] = 0xED; // illegal instruction
+ ram [hi_page + 6] = header_.timer_modulo;
+ ram [hi_page + 7] = header_.timer_mode;
+
+ // Map memory
+ cpu.reset( rom.unmapped() );
+ cpu.map_code( ram_addr, 0x10000 - ram_addr, ram );
+ cpu.map_code( 0, bank_size, rom.at_addr( 0 ) );
+ set_bank( rom.size() > bank_size );
+
+ // CPU registers, timing
+ update_timer();
+ next_play = play_period_;
+ cpu.r.fa = track;
+ cpu.r.sp = get_le16( header_.stack_ptr );
+ jsr_then_stop( header_.init_addr );
+
+ return blargg_ok;
+}
+
+blargg_err_t Gbs_Core::run_until( int end )
+{
+ end_time = end;
+ cpu.set_time( cpu.time() - end );
+ while ( true )
+ {
+ run_cpu();
+ if ( cpu.time() >= 0 )
+ break;
+
+ if ( cpu.r.pc == idle_addr )
+ {
+ if ( next_play > end_time )
+ {
+ cpu.set_time( 0 );
+ break;
+ }
+
+ if ( cpu.time() < next_play - end_time )
+ cpu.set_time( next_play - end_time );
+ next_play += play_period_;
+ jsr_then_stop( header_.play_addr );
+ }
+ else if ( cpu.r.pc > 0xFFFF )
+ {
+ dprintf( "PC wrapped around\n" );
+ cpu.r.pc &= 0xFFFF;
+ }
+ else
+ {
+ set_warning( "Emulation error (illegal/unsupported instruction)" );
+ dprintf( "Bad opcode $%02X at $%04X\n",
+ (int) *cpu.get_code( cpu.r.pc ), (int) cpu.r.pc );
+ cpu.r.pc = (cpu.r.pc + 1) & 0xFFFF;
+ cpu.set_time( cpu.time() + 6 );
+ }
+ }
+
+ return blargg_ok;
+}
+
+blargg_err_t Gbs_Core::end_frame( int end )
+{
+ RETURN_ERR( run_until( end ) );
+
+ next_play -= end;
+ if ( next_play < 0 ) // happens when play routine takes too long
+ {
+ #if !GBS_IGNORE_STARVED_PLAY
+ check( false );
+ #endif
+ next_play = 0;
+ }
+
+ apu_.end_frame( end );
+
+ return blargg_ok;
+}
diff --git a/Frameworks/GME/gme/Gbs_Core.h b/Frameworks/GME/gme/Gbs_Core.h
new file mode 100644
index 000000000..833f807e6
--- /dev/null
+++ b/Frameworks/GME/gme/Gbs_Core.h
@@ -0,0 +1,109 @@
+// Nintendo Game Boy GBS music file emulator core
+
+// Game_Music_Emu $vers
+#ifndef GBS_CORE_H
+#define GBS_CORE_H
+
+#include "Gme_Loader.h"
+#include "Rom_Data.h"
+#include "Gb_Cpu.h"
+#include "Gb_Apu.h"
+
+class Gbs_Core : public Gme_Loader {
+public:
+
+ // GBS file header
+ struct header_t
+ {
+ enum { size = 112 };
+
+ char tag [ 3];
+ byte vers;
+ byte track_count;
+ byte first_track;
+ byte load_addr [ 2];
+ byte init_addr [ 2];
+ byte play_addr [ 2];
+ byte stack_ptr [ 2];
+ byte timer_modulo;
+ byte timer_mode;
+ char game [32]; // strings can be 32 chars, NOT terminated
+ char author [32];
+ char copyright [32];
+
+ // True if header has valid file signature
+ bool valid_tag() const;
+ };
+
+ // Header for currently loaded file
+ header_t const& header() const { return header_; }
+
+ // Sound chip
+ Gb_Apu& apu() { return apu_; }
+
+ // ROM data
+ Rom_Data const& rom_() const { return rom; }
+
+ // Adjusts music tempo, where 1.0 is normal. Can be changed while playing.
+ void set_tempo( double );
+
+ // Starts track, where 0 is the first. Uses specified APU mode.
+ blargg_err_t start_track( int, Gb_Apu::mode_t = Gb_Apu::mode_cgb );
+
+ // Ends time frame at time t
+ typedef int time_t; // clock count
+ blargg_err_t end_frame( time_t t );
+
+ // Clocks between calls to play routine
+ time_t play_period() const { return play_period_; }
+
+protected:
+ typedef int addr_t;
+
+ // Current time
+ time_t time() const { return cpu.time() + end_time; }
+
+ // Runs emulator to time t
+ blargg_err_t run_until( time_t t );
+
+ // Runs CPU until time becomes >= 0
+ void run_cpu();
+
+ // Reads/writes memory and I/O
+ int read_mem( addr_t );
+ void write_mem( addr_t, int );
+
+// Implementation
+public:
+ Gbs_Core();
+ ~Gbs_Core();
+ virtual void unload();
+
+protected:
+ virtual blargg_err_t load_( Data_Reader& );
+
+private:
+ enum { ram_addr = 0xA000 };
+ enum { io_base = 0xFF00 };
+ enum { hi_page = io_base - ram_addr };
+
+ Rom_Data rom;
+ int tempo;
+ time_t end_time;
+ time_t play_period_;
+ time_t next_play;
+ header_t header_;
+ Gb_Cpu cpu;
+ Gb_Apu apu_;
+ byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding];
+
+ void update_timer();
+ void jsr_then_stop( byte const [] );
+ void set_bank( int n );
+ void write_io_inline( int offset, int data, int base );
+ void write_io_( int offset, int data );
+ int read_io( int offset );
+ void write_io( int offset, int data );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Gbs_Cpu.cpp b/Frameworks/GME/gme/Gbs_Cpu.cpp
new file mode 100644
index 000000000..4f5751aa0
--- /dev/null
+++ b/Frameworks/GME/gme/Gbs_Cpu.cpp
@@ -0,0 +1,134 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Gbs_Core.h"
+
+#include "blargg_endian.h"
+
+//#include "gb_cpu_log.h"
+
+/* Copyright (C) 2003-2009 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"
+
+#ifndef LOG_MEM
+ #define LOG_MEM( addr, str, data ) data
+#endif
+
+int Gbs_Core::read_mem( addr_t addr )
+{
+ int result = *cpu.get_code( addr );
+ if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size )
+ result = apu_.read_register( time(), addr );
+
+#ifndef NDEBUG
+ else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 )
+ dprintf( "Unmapped read $%04X\n", (unsigned) addr );
+ else if ( unsigned (addr - 0xFF01) < 0xFF80 - 0xFF01 && addr != 0xFF70 && addr != 0xFF05 )
+ dprintf( "Unmapped read $%04X\n", (unsigned) addr );
+#endif
+
+ return LOG_MEM( addr, ">", result );
+}
+
+inline void Gbs_Core::write_io_inline( int offset, int data, int base )
+{
+ if ( (unsigned) (offset - (apu_.io_addr - base)) < apu_.io_size )
+ apu_.write_register( time(), offset + base, data & 0xFF );
+ else if ( (unsigned) (offset - (0xFF06 - base)) < 2 )
+ update_timer();
+ else if ( offset == io_base - base )
+ ram [base - ram_addr + offset] = 0; // keep joypad return value 0
+ else
+ ram [base - ram_addr + offset] = 0xFF;
+
+ //if ( offset == 0xFFFF - base )
+ // dprintf( "Wrote interrupt mask\n" );
+}
+
+void Gbs_Core::write_mem( addr_t addr, int data )
+{
+ (void) LOG_MEM( addr, "<", data );
+
+ int offset = addr - ram_addr;
+ if ( (unsigned) offset < 0x10000 - ram_addr )
+ {
+ ram [offset] = data;
+
+ offset -= 0xE000 - ram_addr;
+ if ( (unsigned) offset < 0x1F80 )
+ write_io_inline( offset, data, 0xE000 );
+ }
+ else if ( (unsigned) (offset - (0x2000 - ram_addr)) < 0x2000 )
+ {
+ set_bank( data & 0xFF );
+ }
+#ifndef NDEBUG
+ else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 )
+ {
+ dprintf( "Unmapped write $%04X\n", (unsigned) addr );
+ }
+#endif
+}
+
+void Gbs_Core::write_io_( int offset, int data )
+{
+ write_io_inline( offset, data, io_base );
+}
+
+inline void Gbs_Core::write_io( int offset, int data )
+{
+ (void) LOG_MEM( offset + io_base, "<", data );
+
+ ram [io_base - ram_addr + offset] = data;
+ if ( (unsigned) offset < 0x80 )
+ write_io_( offset, data );
+}
+
+int Gbs_Core::read_io( int offset )
+{
+ int const io_base = 0xFF00;
+ int result = ram [io_base - ram_addr + offset];
+
+ if ( (unsigned) (offset - (apu_.io_addr - io_base)) < apu_.io_size )
+ {
+ result = apu_.read_register( time(), offset + io_base );
+ (void) LOG_MEM( offset + io_base, ">", result );
+ }
+ else
+ {
+ check( result == read_mem( offset + io_base ) );
+ }
+ return result;
+}
+
+#define READ_FAST( addr, out ) \
+{\
+ out = READ_CODE( addr );\
+ if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size )\
+ out = LOG_MEM( addr, ">", apu_.read_register( TIME() + end_time, addr ) );\
+ else\
+ check( out == read_mem( addr ) );\
+}
+
+#define READ_MEM( addr ) read_mem( addr )
+#define WRITE_MEM( addr, data ) write_mem( addr, data )
+
+#define WRITE_IO( addr, data ) write_io( addr, data )
+#define READ_IO( addr, out ) out = read_io( addr )
+
+#define CPU cpu
+
+#define CPU_BEGIN \
+void Gbs_Core::run_cpu()\
+{
+ #include "Gb_Cpu_run.h"
+}
diff --git a/Frameworks/GME/gme/Gme_Loader.cpp b/Frameworks/GME/gme/Gme_Loader.cpp
new file mode 100644
index 000000000..8f340a138
--- /dev/null
+++ b/Frameworks/GME/gme/Gme_Loader.cpp
@@ -0,0 +1,86 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Gme_Loader.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2008 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 Gme_Loader::unload()
+{
+ file_begin_ = NULL;
+ file_end_ = NULL;
+ file_data.clear();
+}
+
+Gme_Loader::Gme_Loader()
+{
+ warning_ = NULL;
+ Gme_Loader::unload();
+ blargg_verify_byte_order(); // used by most emulator types, so save them the trouble
+}
+
+Gme_Loader::~Gme_Loader() { }
+
+blargg_err_t Gme_Loader::load_mem_( byte const data [], int size )
+{
+ require( data != file_data.begin() ); // load_mem_() or load_() must be overridden
+ Mem_File_Reader in( data, size );
+ return load_( in );
+}
+
+inline blargg_err_t Gme_Loader::load_mem_wrapper( byte const data [], int size )
+{
+ file_begin_ = data;
+ file_end_ = data + size;
+ return load_mem_( data, size );
+}
+
+blargg_err_t Gme_Loader::load_( Data_Reader& in )
+{
+ RETURN_ERR( file_data.resize( in.remain() ) );
+ RETURN_ERR( in.read( file_data.begin(), file_data.size() ) );
+ return load_mem_wrapper( file_data.begin(), file_data.size() );
+}
+
+blargg_err_t Gme_Loader::post_load_( blargg_err_t err )
+{
+ if ( err )
+ {
+ unload();
+ return err;
+ }
+
+ return post_load();
+}
+
+blargg_err_t Gme_Loader::load_mem( void const* in, long size )
+{
+ pre_load();
+ return post_load_( load_mem_wrapper( (byte const*) in, (int) size ) );
+}
+
+blargg_err_t Gme_Loader::load( Data_Reader& in )
+{
+ pre_load();
+ return post_load_( load_( in ) );
+}
+
+blargg_err_t Gme_Loader::load_file( const char path [] )
+{
+ pre_load();
+ GME_FILE_READER in;
+ RETURN_ERR( in.open( path ) );
+ return post_load_( load_( in ) );
+}
diff --git a/Frameworks/GME/gme/Gme_Loader.h b/Frameworks/GME/gme/Gme_Loader.h
new file mode 100644
index 000000000..e59431c9a
--- /dev/null
+++ b/Frameworks/GME/gme/Gme_Loader.h
@@ -0,0 +1,92 @@
+// Common interface for loading file data from various sources
+
+// Game_Music_Emu $vers
+#ifndef GME_LOADER_H
+#define GME_LOADER_H
+
+#include "blargg_common.h"
+#include "Data_Reader.h"
+
+class Gme_Loader {
+public:
+
+ // Each loads game music data from a file and returns an error if
+ // file is wrong type or is seriously corrupt. Minor problems are
+ // reported using warning().
+
+ // Loads from file on disk
+ blargg_err_t load_file( const char path [] );
+
+ // Loads from custom data source (see Data_Reader.h)
+ blargg_err_t load( Data_Reader& );
+
+ // Loads from file already read into memory. Object might keep pointer to
+ // data; if it does, you MUST NOT free it until you're done with the file.
+ blargg_err_t load_mem( void const* data, long size );
+
+ // Most recent warning string, or NULL if none. Clears current warning after
+ // returning.
+ const char* warning();
+
+ // Unloads file from memory
+ virtual void unload();
+
+ virtual ~Gme_Loader();
+
+protected:
+ typedef BOOST::uint8_t byte;
+
+ // File data in memory, or 0 if data was loaded with load_()
+ byte const* file_begin() const { return file_begin_; }
+ byte const* file_end() const { return file_end_; }
+ int file_size() const { return (int) (file_end_ - file_begin_); }
+
+ // Sets warning string
+ void set_warning( const char s [] ) { warning_ = s; }
+
+ // At least one must be overridden
+ virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_()
+ virtual blargg_err_t load_mem_( byte const data [], int size ); // use data in memory
+
+ // Optionally overridden
+ virtual void pre_load() { unload(); } // called before load_()/load_mem_()
+ virtual blargg_err_t post_load() { return blargg_ok; } // called after load_()/load_mem_() succeeds
+
+private:
+ // noncopyable
+ Gme_Loader( const Gme_Loader& );
+ Gme_Loader& operator = ( const Gme_Loader& );
+
+// Implementation
+public:
+ Gme_Loader();
+ BLARGG_DISABLE_NOTHROW
+
+ blargg_vector file_data; // used only when loading from file to load_mem_()
+ byte const* file_begin_;
+ byte const* file_end_;
+ const char* warning_;
+
+ blargg_err_t load_mem_wrapper( byte const [], int );
+ blargg_err_t post_load_( blargg_err_t err );
+};
+
+// Files are read with GME_FILE_READER. Default supports gzip if zlib is available.
+#ifndef GME_FILE_READER
+ #ifdef HAVE_ZLIB_H
+ #define GME_FILE_READER Gzip_File_Reader
+ #else
+ #define GME_FILE_READER Std_File_Reader
+ #endif
+#elif defined (GME_FILE_READER_INCLUDE)
+ #include GME_FILE_READER_INCLUDE
+#endif
+
+inline const char* Gme_Loader::warning()
+{
+ const char* s = warning_;
+ warning_ = NULL;
+ return s;
+}
+
+#endif
diff --git a/Frameworks/GME/gme/Hes_Apu_Adpcm.cpp b/Frameworks/GME/gme/Hes_Apu_Adpcm.cpp
new file mode 100644
index 000000000..edd955054
--- /dev/null
+++ b/Frameworks/GME/gme/Hes_Apu_Adpcm.cpp
@@ -0,0 +1,309 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Hes_Apu_Adpcm.h"
+
+/* Copyright (C) 2006-2008 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"
+
+Hes_Apu_Adpcm::Hes_Apu_Adpcm()
+{
+ output = NULL;
+
+ memset( &state, 0, sizeof( state ) );
+
+ reset();
+}
+
+void Hes_Apu_Adpcm::reset()
+{
+ last_time = 0;
+ next_timer = 0;
+ last_amp = 0;
+
+ memset( &state.pcmbuf, 0, sizeof(state.pcmbuf) );
+ memset( &state.port, 0, sizeof(state.port) );
+
+ state.ad_sample = 0;
+ state.ad_ref_index = 0;
+
+ state.addr = 0;
+ state.freq = 0;
+ state.writeptr = 0;
+ state.readptr = 0;
+ state.playflag = 0;
+ state.repeatflag = 0;
+ state.length = 0;
+ state.volume = 0xFF;
+ state.fadetimer = 0;
+ state.fadecount = 0;
+}
+
+void Hes_Apu_Adpcm::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+ // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
+ require( !center || (center && !left && !right) || (center && left && right) );
+ require( (unsigned) i < osc_count ); // fails if you pass invalid osc index
+
+ if ( !center || !left || !right )
+ {
+ left = center;
+ right = center;
+ }
+
+ output = center;
+}
+
+void Hes_Apu_Adpcm::run_until( blip_time_t end_time )
+{
+ int volume = state.volume;
+ int fadetimer = state.fadetimer;
+ int fadecount = state.fadecount;
+ int last_time = this->last_time;
+ double next_timer = this->next_timer;
+ int last_amp = this->last_amp;
+
+ Blip_Buffer* output = this->output; // cache often-used values
+
+ while ( state.playflag && last_time < end_time )
+ {
+ while ( last_time >= next_timer )
+ {
+ if ( fadetimer )
+ {
+ if ( fadecount > 0 )
+ {
+ fadecount--;
+ volume = 0xFF * fadecount / fadetimer;
+ }
+ else if ( fadecount < 0 )
+ {
+ fadecount++;
+ volume = 0xFF - ( 0xFF * fadecount / fadetimer );
+ }
+ }
+ next_timer += 7159.091;
+ }
+ int amp;
+ if ( state.ad_low_nibble )
+ {
+ amp = adpcm_decode( state.pcmbuf[ state.playptr ] & 0x0F );
+ state.ad_low_nibble = false;
+ state.playptr++;
+ state.playedsamplecount++;
+ if ( state.playedsamplecount == state.playlength )
+ {
+ state.playflag = 0;
+ }
+ }
+ else
+ {
+ amp = adpcm_decode( state.pcmbuf[ state.playptr ] >> 4 );
+ state.ad_low_nibble = true;
+ }
+ amp = amp * volume / 0xFF;
+ int delta = amp - last_amp;
+ if ( output && delta )
+ {
+ last_amp = amp;
+ synth.offset_inline( last_time, delta, output );
+ }
+ last_time += state.freq;
+ }
+
+ if ( !state.playflag )
+ {
+ while ( next_timer <= end_time ) next_timer += 7159.091;
+ last_time = end_time;
+ }
+
+ this->last_time = last_time;
+ this->next_timer = next_timer;
+ this->last_amp = last_amp;
+ state.volume = volume;
+ state.fadetimer = fadetimer;
+ state.fadecount = fadecount;
+}
+
+void Hes_Apu_Adpcm::write_data( blip_time_t time, int addr, int data )
+{
+ if ( time > last_time ) run_until( time );
+
+ data &= 0xFF;
+ state.port[ addr & 15 ] = data;
+ switch ( addr & 15 )
+ {
+ case 8:
+ state.addr &= 0xFF00;
+ state.addr |= data;
+ break;
+ case 9:
+ state.addr &= 0xFF;
+ state.addr |= data << 8;
+ break;
+ case 10:
+ state.pcmbuf[ state.writeptr++ ] = data;
+ state.playlength ++;
+ break;
+ case 11:
+ dprintf("ADPCM DMA 0x%02X", data);
+ break;
+ case 13:
+ if ( data & 0x80 )
+ {
+ state.addr = 0;
+ state.freq = 0;
+ state.writeptr = 0;
+ state.readptr = 0;
+ state.playflag = 0;
+ state.repeatflag = 0;
+ state.length = 0;
+ state.volume = 0xFF;
+ }
+ if ( ( data & 3 ) == 3 )
+ {
+ state.writeptr = state.addr;
+ }
+ if ( data & 8 )
+ {
+ state.readptr = state.addr ? state.addr - 1 : state.addr;
+ }
+ if ( data & 0x10 )
+ {
+ state.length = state.addr;
+ }
+ state.repeatflag = data & 0x20;
+ state.playflag = data & 0x40;
+ if ( state.playflag )
+ {
+ state.playptr = state.readptr;
+ state.playlength = state.length + 1;
+ state.playedsamplecount = 0;
+ state.ad_sample = 0;
+ state.ad_low_nibble = false;
+ }
+ break;
+ case 14:
+ state.freq = 7159091 / ( 32000 / ( 16 - ( data & 15 ) ) );
+ break;
+ case 15:
+ switch ( data & 15 )
+ {
+ case 0:
+ case 8:
+ case 12:
+ state.fadetimer = -100;
+ state.fadecount = state.fadetimer;
+ break;
+ case 10:
+ state.fadetimer = 5000;
+ state.fadecount = state.fadetimer;
+ break;
+ case 14:
+ state.fadetimer = 1500;
+ state.fadecount = state.fadetimer;
+ break;
+ }
+ break;
+ }
+}
+
+int Hes_Apu_Adpcm::read_data( blip_time_t time, int addr )
+{
+ if ( time > last_time ) run_until( time );
+
+ switch ( addr & 15 )
+ {
+ case 10:
+ return state.pcmbuf [state.readptr++];
+ case 11:
+ return state.port [11] & ~1;
+ case 12:
+ if (!state.playflag)
+ {
+ state.port [12] |= 1;
+ state.port [12] &= ~8;
+ }
+ else
+ {
+ state.port [12] &= ~1;
+ state.port [12] |= 8;
+ }
+ return state.port [12];
+ case 13:
+ return state.port [13];
+ }
+
+ return 0xFF;
+}
+
+void Hes_Apu_Adpcm::end_frame( blip_time_t end_time )
+{
+ run_until( end_time );
+ last_time -= end_time;
+ next_timer -= (double)end_time;
+ check( last_time >= 0 );
+ if ( output )
+ output->set_modified();
+}
+
+static short stepsize[49] = {
+ 16, 17, 19, 21, 23, 25, 28,
+ 31, 34, 37, 41, 45, 50, 55,
+ 60, 66, 73, 80, 88, 97, 107,
+ 118, 130, 143, 157, 173, 190, 209,
+ 230, 253, 279, 307, 337, 371, 408,
+ 449, 494, 544, 598, 658, 724, 796,
+ 876, 963,1060,1166,1282,1411,1552
+};
+
+int Hes_Apu_Adpcm::adpcm_decode( int code )
+{
+ int step = stepsize[state.ad_ref_index];
+ int delta;
+ int c = code & 7;
+#if 1
+ delta = 0;
+ if ( c & 4 ) delta += step;
+ step >>= 1;
+ if ( c & 2 ) delta += step;
+ step >>= 1;
+ if ( c & 1 ) delta += step;
+ step >>= 1;
+ delta += step;
+#else
+ delta = ( ( c + c + 1 ) * step ) / 8; // maybe faster, but introduces rounding
+#endif
+ if ( c != code )
+ {
+ state.ad_sample -= delta;
+ if ( state.ad_sample < -2048 )
+ state.ad_sample = -2048;
+ }
+ else
+ {
+ state.ad_sample += delta;
+ if ( state.ad_sample > 2047 )
+ state.ad_sample = 2047;
+ }
+
+ static int const steps [8] = {
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+ state.ad_ref_index += steps [c];
+ if ( state.ad_ref_index < 0 )
+ state.ad_ref_index = 0;
+ else if ( state.ad_ref_index > 48 )
+ state.ad_ref_index = 48;
+
+ return state.ad_sample;
+}
diff --git a/Frameworks/GME/gme/Hes_Apu_Adpcm.h b/Frameworks/GME/gme/Hes_Apu_Adpcm.h
new file mode 100644
index 000000000..6abd80869
--- /dev/null
+++ b/Frameworks/GME/gme/Hes_Apu_Adpcm.h
@@ -0,0 +1,94 @@
+// Turbo Grafx 16 (PC Engine) ADPCM sound chip emulator
+
+// Game_Music_Emu $vers
+#ifndef HES_APU_ADPCM_H
+#define HES_APU_ADPCM_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+class Hes_Apu_Adpcm {
+public:
+// Basics
+
+ // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0,
+ // output is mono.
+ void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL );
+
+ // Emulates to time t, then writes data to addr
+ void write_data( blip_time_t t, int addr, int data );
+
+ // Emulates to time t, then reads from addr
+ int read_data( blip_time_t t, int addr );
+
+ // Emulates to time t, then subtracts t from the current time.
+ // OK if previous write call had time slightly after t.
+ void end_frame( blip_time_t t );
+
+// More features
+
+ // Resets sound chip
+ void reset();
+
+ // Same as set_output(), but for a particular channel
+ enum { osc_count = 1 }; // 0 <= chan < osc_count
+ void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL );
+
+ // Sets treble equalization
+ void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+
+ // Sets overall volume, where 1.0 is normal
+ void volume( double v ) { synth.volume( 0.6 / osc_count / amp_range * v ); }
+
+ // Registers are at io_addr to io_addr+io_size-1
+ enum { io_addr = 0x1800 };
+ enum { io_size = 0x400 };
+
+// Implementation
+public:
+ Hes_Apu_Adpcm();
+ typedef BOOST::uint8_t byte;
+
+private:
+ enum { amp_range = 2048 };
+
+ struct State
+ {
+ byte pcmbuf [0x10000];
+ byte port [0x10];
+ int ad_sample;
+ int ad_ref_index;
+ bool ad_low_nibble;
+ int freq;
+ unsigned short addr;
+ unsigned short writeptr;
+ unsigned short readptr;
+ unsigned short playptr;
+ byte playflag;
+ byte repeatflag;
+ int length;
+ int playlength;
+ int playedsamplecount;
+ int volume;
+ int fadetimer;
+ int fadecount;
+ };
+ State state;
+ Blip_Synth_Fast synth;
+
+ Blip_Buffer* output;
+ blip_time_t last_time;
+ double next_timer;
+ int last_amp;
+
+ void run_until( blip_time_t );
+
+ int adpcm_decode( int );
+};
+
+inline void Hes_Apu_Adpcm::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+ set_output( 0, c, l, r );
+}
+
+#endif
diff --git a/Frameworks/GME/gme/Hes_Core.cpp b/Frameworks/GME/gme/Hes_Core.cpp
new file mode 100644
index 000000000..273787ca9
--- /dev/null
+++ b/Frameworks/GME/gme/Hes_Core.cpp
@@ -0,0 +1,408 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Hes_Core.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2006-2008 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"
+
+int const timer_mask = 0x04;
+int const vdp_mask = 0x02;
+int const i_flag_mask = 0x04;
+int const unmapped = 0xFF;
+
+int const period_60hz = 262 * 455; // scanlines * clocks per scanline
+
+Hes_Core::Hes_Core() : rom( Hes_Cpu::page_size )
+{
+ timer.raw_load = 0;
+}
+
+Hes_Core::~Hes_Core() { }
+
+void Hes_Core::unload()
+{
+ rom.clear();
+ Gme_Loader::unload();
+}
+
+bool Hes_Core::header_t::valid_tag() const
+{
+ return 0 == memcmp( tag, "HESM", 4 );
+}
+
+blargg_err_t Hes_Core::load_( Data_Reader& in )
+{
+ assert( offsetof (header_t,unused [4]) == header_t::size );
+ RETURN_ERR( rom.load( in, header_t::size, &header_, unmapped ) );
+
+ if ( !header_.valid_tag() )
+ return blargg_err_file_type;
+
+ if ( header_.vers != 0 )
+ set_warning( "Unknown file version" );
+
+ if ( memcmp( header_.data_tag, "DATA", 4 ) )
+ set_warning( "Data header missing" );
+
+ if ( memcmp( header_.unused, "\0\0\0\0", 4 ) )
+ set_warning( "Unknown header data" );
+
+ // File spec supports multiple blocks, but I haven't found any, and
+ // many files have bad sizes in the only block, so it's simpler to
+ // just try to load the damn data as best as possible.
+
+ int addr = get_le32( header_.addr );
+ int size = get_le32( header_.data_size );
+ int const rom_max = 0x100000;
+ if ( (unsigned) addr >= (unsigned) rom_max )
+ {
+ set_warning( "Invalid address" );
+ addr &= rom_max - 1;
+ }
+ if ( (unsigned) (addr + size) > (unsigned) rom_max )
+ set_warning( "Invalid size" );
+
+ if ( size != rom.file_size() )
+ {
+ if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) )
+ set_warning( "Multiple DATA not supported" );
+ else if ( size < rom.file_size() )
+ set_warning( "Extra file data" );
+ else
+ set_warning( "Missing file data" );
+ }
+
+ rom.set_addr( addr );
+
+ return blargg_ok;
+}
+
+void Hes_Core::recalc_timer_load()
+{
+ timer.load = timer.raw_load * timer_base + 1;
+}
+
+void Hes_Core::set_tempo( double t )
+{
+ play_period = (time_t) (period_60hz / t);
+ timer_base = (int) (1024 / t);
+ recalc_timer_load();
+}
+
+blargg_err_t Hes_Core::start_track( int track )
+{
+ memset( ram, 0, sizeof ram ); // some HES music relies on zero fill
+ memset( sgx, 0, sizeof sgx );
+
+ apu_.reset();
+ adpcm_.reset();
+ cpu.reset();
+
+ for ( int i = 0; i < (int) sizeof header_.banks; i++ )
+ set_mmr( i, header_.banks [i] );
+ set_mmr( cpu.page_count, 0xFF ); // unmapped beyond end of address space
+
+ irq.disables = timer_mask | vdp_mask;
+ irq.timer = cpu.future_time;
+ irq.vdp = cpu.future_time;
+
+ timer.enabled = false;
+ timer.raw_load = 0x80;
+ timer.count = timer.load;
+ timer.fired = false;
+ timer.last_time = 0;
+
+ vdp.latch = 0;
+ vdp.control = 0;
+ vdp.next_vbl = 0;
+
+ ram [0x1FF] = (idle_addr - 1) >> 8;
+ ram [0x1FE] = (idle_addr - 1) & 0xFF;
+ cpu.r.sp = 0xFD;
+ cpu.r.pc = get_le16( header_.init_addr );
+ cpu.r.a = track;
+
+ recalc_timer_load();
+
+ return blargg_ok;
+}
+
+// Hardware
+
+void Hes_Core::run_until( time_t present )
+{
+ while ( vdp.next_vbl < present )
+ vdp.next_vbl += play_period;
+
+ time_t elapsed = present - timer.last_time;
+ if ( elapsed > 0 )
+ {
+ if ( timer.enabled )
+ {
+ timer.count -= elapsed;
+ if ( timer.count <= 0 )
+ timer.count += timer.load;
+ }
+ timer.last_time = present;
+ }
+}
+
+void Hes_Core::write_vdp( int addr, int data )
+{
+ switch ( addr )
+ {
+ case 0:
+ vdp.latch = data & 0x1F;
+ break;
+
+ case 2:
+ if ( vdp.latch == 5 )
+ {
+ if ( data & 0x04 )
+ set_warning( "Scanline interrupt unsupported" );
+ run_until( cpu.time() );
+ vdp.control = data;
+ irq_changed();
+ }
+ else
+ {
+ dprintf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data );
+ }
+ break;
+
+ case 3:
+ dprintf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data );
+ break;
+ }
+}
+
+void Hes_Core::write_mem_( addr_t addr, int data )
+{
+ time_t time = cpu.time();
+ if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size )
+ {
+ // Avoid going way past end when a long block xfer is writing to I/O space.
+ // Not a problem for other registers below because they don't write to
+ // Blip_Buffer.
+ time_t t = min( time, cpu.end_time() + 8 );
+ apu_.write_data( t, addr, data );
+ return;
+ }
+ if ( (unsigned) (addr - adpcm_.io_addr) < adpcm_.io_size )
+ {
+ time_t t = min( time, cpu.end_time() + 6 );
+ adpcm_.write_data( t, addr, data );
+ return;
+ }
+
+ switch ( addr )
+ {
+ case 0x0000:
+ case 0x0002:
+ case 0x0003:
+ write_vdp( addr, data );
+ return;
+
+ case 0x0C00: {
+ run_until( time );
+ timer.raw_load = (data & 0x7F) + 1;
+ recalc_timer_load();
+ timer.count = timer.load;
+ break;
+ }
+
+ case 0x0C01:
+ data &= 1;
+ if ( timer.enabled == data )
+ return;
+ run_until( time );
+ timer.enabled = data;
+ if ( data )
+ timer.count = timer.load;
+ break;
+
+ case 0x1402:
+ run_until( time );
+ irq.disables = data;
+ if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values
+ dprintf( "Int mask: $%02X\n", data );
+ break;
+
+ case 0x1403:
+ run_until( time );
+ if ( timer.enabled )
+ timer.count = timer.load;
+ timer.fired = false;
+ break;
+
+#ifndef NDEBUG
+ case 0x1000: // I/O port
+ case 0x0402: // palette
+ case 0x0403:
+ case 0x0404:
+ case 0x0405:
+ return;
+
+ default:
+ dprintf( "unmapped write $%04X <- $%02X\n", addr, data );
+ return;
+#endif
+ }
+
+ irq_changed();
+}
+
+int Hes_Core::read_mem_( addr_t addr )
+{
+ time_t time = cpu.time();
+ addr &= cpu.page_size - 1;
+ switch ( addr )
+ {
+ case 0x0000:
+ if ( irq.vdp > time )
+ return 0;
+ irq.vdp = cpu.future_time;
+ run_until( time );
+ irq_changed();
+ return 0x20;
+
+ case 0x0002:
+ case 0x0003:
+ dprintf( "VDP read not supported: %d\n", addr );
+ return 0;
+
+ case 0x0C01:
+ //return timer.enabled; // TODO: remove?
+ case 0x0C00:
+ run_until( time );
+ dprintf( "Timer count read\n" );
+ return (unsigned) (timer.count - 1) / timer_base;
+
+ case 0x1402:
+ return irq.disables;
+
+ case 0x1403:
+ {
+ int status = 0;
+ if ( irq.timer <= time ) status |= timer_mask;
+ if ( irq.vdp <= time ) status |= vdp_mask;
+ return status;
+ }
+
+ case 0x180A:
+ case 0x180B:
+ case 0x180C:
+ case 0x180D:
+ return adpcm_.read_data( time, addr );
+
+ #ifndef NDEBUG
+ case 0x1000: // I/O port
+ //case 0x180C: // CD-ROM
+ //case 0x180D:
+ break;
+
+ default:
+ dprintf( "unmapped read $%04X\n", addr );
+ #endif
+ }
+
+ return unmapped;
+}
+
+void Hes_Core::irq_changed()
+{
+ time_t present = cpu.time();
+
+ if ( irq.timer > present )
+ {
+ irq.timer = cpu.future_time;
+ if ( timer.enabled && !timer.fired )
+ irq.timer = present + timer.count;
+ }
+
+ if ( irq.vdp > present )
+ {
+ irq.vdp = cpu.future_time;
+ if ( vdp.control & 0x08 )
+ irq.vdp = vdp.next_vbl;
+ }
+
+ time_t time = cpu.future_time;
+ if ( !(irq.disables & timer_mask) ) time = irq.timer;
+ if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp );
+
+ cpu.set_irq_time( time );
+}
+
+int Hes_Core::cpu_done()
+{
+ check( cpu.time() >= cpu.end_time() ||
+ (!(cpu.r.flags & i_flag_mask) && cpu.time() >= cpu.irq_time()) );
+
+ if ( !(cpu.r.flags & i_flag_mask) )
+ {
+ time_t present = cpu.time();
+
+ if ( irq.timer <= present && !(irq.disables & timer_mask) )
+ {
+ timer.fired = true;
+ irq.timer = cpu.future_time;
+ irq_changed(); // overkill, but not worth writing custom code
+ return 0x0A;
+ }
+
+ if ( irq.vdp <= present && !(irq.disables & vdp_mask) )
+ {
+ // work around for bugs with music not acknowledging VDP
+ //run_until( present );
+ //irq.vdp = cpu.future_time;
+ //irq_changed();
+ return 0x08;
+ }
+ }
+ return -1;
+}
+
+static void adjust_time( Hes_Core::time_t& time, Hes_Core::time_t delta )
+{
+ if ( time < Hes_Cpu::future_time )
+ {
+ time -= delta;
+ if ( time < 0 )
+ time = 0;
+ }
+}
+
+blargg_err_t Hes_Core::end_frame( time_t duration )
+{
+ if ( run_cpu( duration ) )
+ set_warning( "Emulation error (illegal instruction)" );
+
+ check( cpu.time() >= duration );
+ //check( time() - duration < 20 ); // Txx instruction could cause going way over
+
+ run_until( duration );
+
+ // end time frame
+ timer.last_time -= duration;
+ vdp.next_vbl -= duration;
+ cpu.end_frame( duration );
+ ::adjust_time( irq.timer, duration );
+ ::adjust_time( irq.vdp, duration );
+ apu_.end_frame( duration );
+ adpcm_.end_frame( duration );
+
+ return blargg_ok;
+}
diff --git a/Frameworks/GME/gme/Hes_Core.h b/Frameworks/GME/gme/Hes_Core.h
new file mode 100644
index 000000000..80b0a5fd5
--- /dev/null
+++ b/Frameworks/GME/gme/Hes_Core.h
@@ -0,0 +1,120 @@
+// TurboGrafx-16/PC Engine HES music file emulator core
+
+// Game_Music_Emu $vers
+#ifndef HES_CORE_H
+#define HES_CORE_H
+
+#include "Gme_Loader.h"
+#include "Rom_Data.h"
+#include "Hes_Apu.h"
+#include "Hes_Apu_Adpcm.h"
+#include "Hes_Cpu.h"
+
+class Hes_Core : public Gme_Loader {
+public:
+
+ // HES file header
+ enum { info_offset = 0x20 };
+ struct header_t
+ {
+ enum { size = 0x20 };
+
+ byte tag [4];
+ byte vers;
+ byte first_track;
+ byte init_addr [2];
+ byte banks [8];
+ byte data_tag [4];
+ byte data_size [4];
+ byte addr [4];
+ byte unused [4];
+
+ // True if header has valid file signature
+ bool valid_tag() const;
+ };
+
+ // Header for currently loaded file
+ header_t const& header() const { return header_; }
+
+ // Pointer to ROM data, for getting track information from
+ byte const* data() const { return rom.begin(); }
+ int data_size() const { return rom.file_size(); }
+
+ // Adjusts rate play routine is called at, where 1.0 is normal.
+ // Can be changed while track is playing.
+ void set_tempo( double );
+
+ // Sound chip
+ Hes_Apu& apu() { return apu_; }
+
+ Hes_Apu_Adpcm& adpcm() { return adpcm_; }
+
+ // Starts track
+ blargg_err_t start_track( int );
+
+ // Ends time frame at time t
+ typedef int time_t;
+ blargg_err_t end_frame( time_t );
+
+// Implementation
+public:
+ Hes_Core();
+ ~Hes_Core();
+ virtual void unload();
+
+protected:
+ virtual blargg_err_t load_( Data_Reader& );
+
+private:
+ enum { idle_addr = 0x1FFF };
+
+ typedef int addr_t;
+ Hes_Cpu cpu;
+ Rom_Data rom;
+ header_t header_;
+ time_t play_period;
+ int timer_base;
+
+ struct {
+ time_t last_time;
+ int count;
+ int load;
+ int raw_load;
+ byte enabled;
+ byte fired;
+ } timer;
+
+ struct {
+ time_t next_vbl;
+ byte latch;
+ byte control;
+ } vdp;
+
+ struct {
+ time_t timer;
+ time_t vdp;
+ byte disables;
+ } irq;
+
+ void recalc_timer_load();
+
+ // large items
+ byte* write_pages [Hes_Cpu::page_count + 1]; // 0 if unmapped or I/O space
+ Hes_Apu apu_;
+ Hes_Apu_Adpcm adpcm_;
+ byte ram [Hes_Cpu::page_size];
+ byte sgx [3 * Hes_Cpu::page_size + Hes_Cpu::cpu_padding];
+
+ void irq_changed();
+ void run_until( time_t );
+ bool run_cpu( time_t end );
+ int read_mem_( addr_t );
+ int read_mem( addr_t );
+ void write_mem_( addr_t, int data );
+ void write_mem( addr_t, int );
+ void write_vdp( int addr, int data );
+ void set_mmr( int reg, int bank );
+ int cpu_done();
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Hes_Cpu_run.h b/Frameworks/GME/gme/Hes_Cpu_run.h
new file mode 100644
index 000000000..2b9c2ae06
--- /dev/null
+++ b/Frameworks/GME/gme/Hes_Cpu_run.h
@@ -0,0 +1,1342 @@
+// $package. http://www.slack.net/~ant/
+
+#if 0
+/* Define these macros in the source file before #including this file.
+- Parameters might be expressions, so they are best evaluated only once,
+though they NEVER have side-effects, so multiple evaluation is OK.
+- Output parameters might be a multiple-assignment expression like "a=x",
+so they must NOT be parenthesized.
+- Except where noted, time() and related functions will NOT work
+correctly inside a macro. TIME() is always correct, and FLUSH_TIME() and
+CACHE_TIME() allow the time changing functions to work.
+- Macros "returning" void may use a {} statement block. */
+
+ // 0 <= addr <= 0xFFFF + page_size
+ // time functions can be used
+ int READ_MEM( addr_t );
+ void WRITE_MEM( addr_t, int data );
+
+ // 0 <= addr <= 0x1FF
+ int READ_LOW( addr_t );
+ void WRITE_LOW( addr_t, int data );
+
+ // 0 <= addr <= 0xFFFF + page_size
+ // Used by common instructions.
+ int READ_FAST( addr_t, int& out );
+ void WRITE_FAST( addr_t, int data );
+
+ // 0 <= addr <= 2
+ // ST0, ST1, ST2 instructions
+ void WRITE_VDP( int addr, int data );
+
+// The following can be used within macros:
+
+ // Current time
+ time_t TIME();
+
+ // Allows use of time functions
+ void FLUSH_TIME();
+
+ // Must be used before end of macro if FLUSH_TIME() was used earlier
+ void CACHE_TIME();
+
+// Configuration (optional; commented behavior if defined)
+
+ // Expanded just before beginning of code, to help debugger
+ #define CPU_BEGIN void my_run_cpu() {
+#endif
+
+/* Copyright (C) 2003-2008 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 */
+
+// TODO: support T flag, including clearing it at appropriate times?
+
+// all zero-page should really use whatever is at page 1, but that would
+// reduce efficiency quite a bit
+int const ram_addr = 0x2000;
+
+void Hes_Cpu::reset()
+{
+ check( cpu_state == &cpu_state_ );
+ cpu_state = &cpu_state_;
+
+ cpu_state_.time = 0;
+ cpu_state_.base = 0;
+ irq_time_ = future_time;
+ end_time_ = future_time;
+
+ r.flags = 0x04;
+ r.sp = 0;
+ r.pc = 0;
+ r.a = 0;
+ r.x = 0;
+ r.y = 0;
+
+ // Be sure "blargg_endian.h" has been #included
+ blargg_verify_byte_order();
+}
+
+// Allows MWCW debugger to step through code properly
+#ifdef CPU_BEGIN
+ CPU_BEGIN
+#endif
+
+// Time
+#define TIME() (s_time + s.base)
+#define FLUSH_TIME() {s.time = s_time;}
+#define CACHE_TIME() {s_time = s.time;}
+
+// Memory
+#define READ_STACK READ_LOW
+#define WRITE_STACK WRITE_LOW
+
+#define CODE_PAGE( addr ) s.code_map [HES_CPU_PAGE( addr )]
+#define CODE_OFFSET( addr ) HES_CPU_OFFSET( addr )
+#define READ_CODE( addr ) CODE_PAGE( addr ) [CODE_OFFSET( addr )]
+
+// Stack
+#define SET_SP( v ) (sp = ((v) + 1) | 0x100)
+#define GET_SP() ((sp - 1) & 0xFF)
+#define SP( o ) ((sp + (o - (o>0)*0x100)) | 0x100)
+
+// Truncation
+#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */
+#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
+#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */
+
+// Flags with hex value for clarity when used as mask.
+// Stored in indicated variable during emulation.
+int const n80 = 0x80; // nz
+int const v40 = 0x40; // flags
+//int const t20 = 0x20;
+int const b10 = 0x10;
+int const d08 = 0x08; // flags
+int const i04 = 0x04; // flags
+int const z02 = 0x02; // nz
+int const c01 = 0x01; // c
+
+#define IS_NEG (nz & 0x8080)
+
+#define GET_FLAGS( out ) \
+{\
+ out = flags & (v40 | d08 | i04);\
+ out += ((nz >> 8) | nz) & n80;\
+ out += c >> 8 & c01;\
+ if ( !BYTE( nz ) )\
+ out += z02;\
+}
+
+#define SET_FLAGS( in ) \
+{\
+ flags = in & (v40 | d08 | i04);\
+ c = nz = in << 8;\
+ nz += ~in & z02;\
+}
+
+bool illegal_encountered = false;
+{
+ Hes_Cpu::cpu_state_t s = CPU.cpu_state_;
+ CPU.cpu_state = &s;
+ // even on x86, using s.time in place of s_time was slower
+ int s_time = s.time;
+
+ // registers
+ int pc = CPU.r.pc;
+ int a = CPU.r.a;
+ int x = CPU.r.x;
+ int y = CPU.r.y;
+ int sp;
+ SET_SP( CPU.r.sp );
+
+ // Flags
+ int flags;
+ int c; // carry set if (c & 0x100) != 0
+ int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
+ {
+ int temp = CPU.r.flags;
+ SET_FLAGS( temp );
+ }
+
+loop:
+
+ #ifndef NDEBUG
+ {
+ time_t correct = CPU.end_time_;
+ if ( !(flags & i04) && correct > CPU.irq_time_ )
+ correct = CPU.irq_time_;
+ check( s.base == correct );
+ /*
+ static int count;
+ if ( count == 1844 ) Debugger();
+ if ( s.base != correct ) dprintf( "%ld\n", count );
+ count++;
+ */
+ }
+ #endif
+
+ // Check all values
+ check( (unsigned) sp - 0x100 < 0x100 );
+ check( (unsigned) pc < 0x10000 + 0x100 ); // +0x100 so emulator can catch wrap-around
+ check( (unsigned) a < 0x100 );
+ check( (unsigned) x < 0x100 );
+ check( (unsigned) y < 0x100 );
+
+ // Read instruction
+ byte const* instr = CODE_PAGE( pc );
+ int opcode;
+
+ if ( CODE_OFFSET(~0) == ~0 )
+ {
+ opcode = instr [pc];
+ pc++;
+ instr += pc;
+ }
+ else
+ {
+ instr += CODE_OFFSET( pc );
+ opcode = *instr++;
+ pc++;
+ }
+
+ // TODO: each reference lists slightly different timing values, ugh
+ static byte const clock_table [256] =
+ {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,4,// 0
+ 2,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,4,// 1
+ 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,4,// 2
+ 2,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,4,// 3
+ 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,4,// 4
+ 2,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,4,// 5
+ 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,4,// 6
+ 2,7,7,17,4,4,6,7,2,5,4,2,7,5,7,4,// 7
+ 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,4,// 8
+ 2,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,4,// 9
+ 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,4,// A
+ 2,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,4,// B
+ 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,4,// C
+ 2,7,7,17,2,4,6,7,2,5,3,2,2,5,7,4,// D
+ 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,4,// E
+ 2,7,7,17,2,4,6,7,2,5,4,2,2,5,7,4 // F
+ }; // 0x00 was 8
+
+ // Update time
+ if ( s_time >= 0 )
+ goto out_of_time;
+
+ #ifdef HES_CPU_LOG_H
+ log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2],
+ instr [3], instr [4], instr [5], a, x, y );
+ //log_opcode( opcode );
+ #endif
+
+ s_time += clock_table [opcode];
+
+ int data;
+ data = *instr;
+
+ switch ( opcode )
+ {
+// Macros
+
+#define GET_MSB() (instr [1])
+#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB());
+#define GET_ADDR() GET_LE16( instr )
+
+// TODO: is the penalty really always added? the original 6502 was much better
+//#define PAGE_PENALTY( lsb ) (void) (s_time += (lsb) >> 8)
+#define PAGE_PENALTY( lsb )
+
+// Branch
+
+// TODO: more efficient way to handle negative branch that wraps PC around
+#define BRANCH_( cond, adj )\
+{\
+ pc++;\
+ if ( !(cond) ) goto loop;\
+ pc = (BOOST::uint16_t) (pc + SBYTE( data ));\
+ s_time += adj;\
+ goto loop;\
+}
+
+#define BRANCH( cond ) BRANCH_( cond, 2 )
+
+ case 0xF0: // BEQ
+ BRANCH( !BYTE( nz ) );
+
+ case 0xD0: // BNE
+ BRANCH( BYTE( nz ) );
+
+ case 0x10: // BPL
+ BRANCH( !IS_NEG );
+
+ case 0x90: // BCC
+ BRANCH( !(c & 0x100) )
+
+ case 0x30: // BMI
+ BRANCH( IS_NEG )
+
+ case 0x50: // BVC
+ BRANCH( !(flags & v40) )
+
+ case 0x70: // BVS
+ BRANCH( flags & v40 )
+
+ case 0xB0: // BCS
+ BRANCH( c & 0x100 )
+
+ case 0x80: // BRA
+ branch_taken:
+ BRANCH_( true, 0 );
+
+ case 0xFF:
+ #ifdef IDLE_ADDR
+ if ( pc == IDLE_ADDR + 1 )
+ goto idle_done;
+ #endif
+
+ pc = (BOOST::uint16_t) pc;
+
+ case 0x0F: // BBRn
+ case 0x1F:
+ case 0x2F:
+ case 0x3F:
+ case 0x4F:
+ case 0x5F:
+ case 0x6F:
+ case 0x7F:
+ case 0x8F: // BBSn
+ case 0x9F:
+ case 0xAF:
+ case 0xBF:
+ case 0xCF:
+ case 0xDF:
+ case 0xEF: {
+ // Make two copies of bits, one negated
+ int t = 0x101 * READ_LOW( data );
+ t ^= 0xFF;
+ pc++;
+ data = GET_MSB();
+ BRANCH( t & (1 << (opcode >> 4)) )
+ }
+
+ case 0x4C: // JMP abs
+ pc = GET_ADDR();
+ goto loop;
+
+ case 0x7C: // JMP (ind+X)
+ data += x;
+ case 0x6C:{// JMP (ind)
+ data += 0x100 * GET_MSB();
+ pc = GET_LE16( &READ_CODE( data ) );
+ goto loop;
+ }
+
+// Subroutine
+
+ case 0x44: // BSR
+ WRITE_STACK( SP( -1 ), pc >> 8 );
+ sp = SP( -2 );
+ WRITE_STACK( sp, pc );
+ goto branch_taken;
+
+ case 0x20: { // JSR
+ int temp = pc + 1;
+ pc = GET_ADDR();
+ WRITE_STACK( SP( -1 ), temp >> 8 );
+ sp = SP( -2 );
+ WRITE_STACK( sp, temp );
+ goto loop;
+ }
+
+ case 0x60: // RTS
+ pc = 1 + READ_STACK( sp );
+ pc += 0x100 * READ_STACK( SP( 1 ) );
+ sp = SP( 2 );
+ goto loop;
+
+ case 0x00: // BRK
+ goto handle_brk;
+
+// Common
+
+ case 0xBD:{// LDA abs,X
+ PAGE_PENALTY( data + x );
+ int addr = GET_ADDR() + x;
+ pc += 2;
+ READ_FAST( addr, nz );
+ a = nz;
+ goto loop;
+ }
+
+ case 0x9D:{// STA abs,X
+ int addr = GET_ADDR() + x;
+ pc += 2;
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+
+ case 0x95: // STA zp,x
+ data = BYTE( data + x );
+ case 0x85: // STA zp
+ pc++;
+ WRITE_LOW( data, a );
+ goto loop;
+
+ case 0xAE:{// LDX abs
+ int addr = GET_ADDR();
+ pc += 2;
+ READ_FAST( addr, nz );
+ x = nz;
+ goto loop;
+ }
+
+ case 0xA5: // LDA zp
+ a = nz = READ_LOW( data );
+ pc++;
+ goto loop;
+
+// Load/store
+
+ {
+ int addr;
+ case 0x91: // STA (ind),Y
+ addr = 0x100 * READ_LOW( BYTE( data + 1 ) );
+ addr += READ_LOW( data ) + y;
+ pc++;
+ goto sta_ptr;
+
+ case 0x81: // STA (ind,X)
+ data = BYTE( data + x );
+ case 0x92: // STA (ind)
+ addr = 0x100 * READ_LOW( BYTE( data + 1 ) );
+ addr += READ_LOW( data );
+ pc++;
+ goto sta_ptr;
+
+ case 0x99: // STA abs,Y
+ data += y;
+ case 0x8D: // STA abs
+ addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ sta_ptr:
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+
+ {
+ int addr;
+ case 0xA1: // LDA (ind,X)
+ data = BYTE( data + x );
+ case 0xB2: // LDA (ind)
+ addr = 0x100 * READ_LOW( BYTE( data + 1 ) );
+ addr += READ_LOW( data );
+ pc++;
+ goto a_nz_read_addr;
+
+ case 0xB1:// LDA (ind),Y
+ addr = READ_LOW( data ) + y;
+ PAGE_PENALTY( addr );
+ addr += 0x100 * READ_LOW( BYTE( data + 1 ) );
+ pc++;
+ goto a_nz_read_addr;
+
+ case 0xB9: // LDA abs,Y
+ data += y;
+ PAGE_PENALTY( data );
+ case 0xAD: // LDA abs
+ addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ a_nz_read_addr:
+ READ_FAST( addr, nz );
+ a = nz;
+ goto loop;
+ }
+
+ case 0xBE:{// LDX abs,y
+ PAGE_PENALTY( data + y );
+ int addr = GET_ADDR() + y;
+ pc += 2;
+ FLUSH_TIME();
+ x = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ case 0xB5: // LDA zp,x
+ a = nz = READ_LOW( BYTE( data + x ) );
+ pc++;
+ goto loop;
+
+ case 0xA9: // LDA #imm
+ pc++;
+ a = data;
+ nz = data;
+ goto loop;
+
+// Bit operations
+
+ case 0x3C: // BIT abs,x
+ data += x;
+ case 0x2C:{// BIT abs
+ int addr;
+ ADD_PAGE( addr );
+ FLUSH_TIME();
+ nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto bit_common;
+ }
+ case 0x34: // BIT zp,x
+ data = BYTE( data + x );
+ case 0x24: // BIT zp
+ data = READ_LOW( data );
+ case 0x89: // BIT imm
+ nz = data;
+ bit_common:
+ pc++;
+ flags = (flags & ~v40) + (nz & v40);
+ if ( nz & a )
+ goto loop; // Z should be clear, and nz must be non-zero if nz & a is
+ nz <<= 8; // set Z flag without affecting N flag
+ goto loop;
+
+ {
+ int addr;
+
+ case 0xB3: // TST abs,x
+ addr = GET_MSB() + x;
+ goto tst_abs;
+
+ case 0x93: // TST abs
+ addr = GET_MSB();
+ tst_abs:
+ addr += 0x100 * instr [2];
+ pc++;
+ FLUSH_TIME();
+ nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto tst_common;
+ }
+
+ case 0xA3: // TST zp,x
+ nz = READ_LOW( BYTE( GET_MSB() + x ) );
+ goto tst_common;
+
+ case 0x83: // TST zp
+ nz = READ_LOW( GET_MSB() );
+ tst_common:
+ pc += 2;
+ flags = (flags & ~v40) + (nz & v40);
+ if ( nz & data )
+ goto loop; // Z should be clear, and nz must be non-zero if nz & data is
+ nz <<= 8; // set Z flag without affecting N flag
+ goto loop;
+
+ {
+ int addr;
+ case 0x0C: // TSB abs
+ case 0x1C: // TRB abs
+ addr = GET_ADDR();
+ pc++;
+ goto txb_addr;
+
+ // TODO: everyone lists different behaviors for the flags flags, ugh
+ case 0x04: // TSB zp
+ case 0x14: // TRB zp
+ addr = data + ram_addr;
+ txb_addr:
+ FLUSH_TIME();
+ nz = a | READ_MEM( addr );
+ if ( opcode & 0x10 )
+ nz ^= a; // bits from a will already be set, so this clears them
+ flags = (flags & ~v40) + (nz & v40);
+ pc++;
+ WRITE_MEM( addr, nz );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ case 0x07: // RMBn
+ case 0x17:
+ case 0x27:
+ case 0x37:
+ case 0x47:
+ case 0x57:
+ case 0x67:
+ case 0x77:
+ pc++;
+ READ_LOW( data ) &= ~(1 << (opcode >> 4));
+ goto loop;
+
+ case 0x87: // SMBn
+ case 0x97:
+ case 0xA7:
+ case 0xB7:
+ case 0xC7:
+ case 0xD7:
+ case 0xE7:
+ case 0xF7:
+ pc++;
+ READ_LOW( data ) |= 1 << ((opcode >> 4) - 8);
+ goto loop;
+
+// Load/store
+
+ case 0x9E: // STZ abs,x
+ data += x;
+ case 0x9C: // STZ abs
+ ADD_PAGE( data );
+ pc++;
+ FLUSH_TIME();
+ WRITE_MEM( data, 0 );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x74: // STZ zp,x
+ data = BYTE( data + x );
+ case 0x64: // STZ zp
+ pc++;
+ WRITE_LOW( data, 0 );
+ goto loop;
+
+ case 0x94: // STY zp,x
+ data = BYTE( data + x );
+ case 0x84: // STY zp
+ pc++;
+ WRITE_LOW( data, y );
+ goto loop;
+
+ case 0x96: // STX zp,y
+ data = BYTE( data + y );
+ case 0x86: // STX zp
+ pc++;
+ WRITE_LOW( data, x );
+ goto loop;
+
+ case 0xB6: // LDX zp,y
+ data = BYTE( data + y );
+ case 0xA6: // LDX zp
+ data = READ_LOW( data );
+ case 0xA2: // LDX #imm
+ pc++;
+ x = data;
+ nz = data;
+ goto loop;
+
+ case 0xB4: // LDY zp,x
+ data = BYTE( data + x );
+ case 0xA4: // LDY zp
+ data = READ_LOW( data );
+ case 0xA0: // LDY #imm
+ pc++;
+ y = data;
+ nz = data;
+ goto loop;
+
+ case 0xBC: // LDY abs,X
+ data += x;
+ PAGE_PENALTY( data );
+ case 0xAC:{// LDY abs
+ int addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ FLUSH_TIME();
+ y = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ {
+ int temp;
+ case 0x8C: // STY abs
+ temp = y;
+ if ( 0 )
+ case 0x8E: // STX abs
+ temp = x;
+ int addr = GET_ADDR();
+ pc += 2;
+ FLUSH_TIME();
+ WRITE_MEM( addr, temp );
+ CACHE_TIME();
+ goto loop;
+ }
+
+// Compare
+
+ case 0xEC:{// CPX abs
+ int addr = GET_ADDR();
+ pc++;
+ FLUSH_TIME();
+ data = READ_MEM( addr );
+ CACHE_TIME();
+ goto cpx_data;
+ }
+
+ case 0xE4: // CPX zp
+ data = READ_LOW( data );
+ case 0xE0: // CPX #imm
+ cpx_data:
+ nz = x - data;
+ pc++;
+ c = ~nz;
+ nz = BYTE( nz );
+ goto loop;
+
+ case 0xCC:{// CPY abs
+ int addr = GET_ADDR();
+ pc++;
+ FLUSH_TIME();
+ data = READ_MEM( addr );
+ CACHE_TIME();
+ goto cpy_data;
+ }
+
+ case 0xC4: // CPY zp
+ data = READ_LOW( data );
+ case 0xC0: // CPY #imm
+ cpy_data:
+ nz = y - data;
+ pc++;
+ c = ~nz;
+ nz = BYTE( nz );
+ goto loop;
+
+// Logical
+
+#define ARITH_ADDR_MODES( op )\
+ case op - 0x04: /* (ind,x) */\
+ data = BYTE( data + x );\
+ case op + 0x0D: /* (ind) */\
+ data = 0x100 * READ_LOW( BYTE( data + 1 ) ) + READ_LOW( data );\
+ goto ptr##op;\
+ case op + 0x0C:{/* (ind),y */\
+ int temp = READ_LOW( data ) + y;\
+ PAGE_PENALTY( temp );\
+ data = temp + 0x100 * READ_LOW( BYTE( data + 1 ) );\
+ goto ptr##op;\
+ }\
+ case op + 0x10: /* zp,X */\
+ data = BYTE( 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:\
+ PAGE_PENALTY( data );\
+ case op + 0x08: /* abs */\
+ ADD_PAGE( data );\
+ ptr##op:\
+ FLUSH_TIME();\
+ data = READ_MEM( data );\
+ CACHE_TIME();\
+ case op + 0x04: /* imm */\
+ imm##op:
+
+ ARITH_ADDR_MODES( 0xC5 ) // CMP
+ nz = a - data;
+ pc++;
+ c = ~nz;
+ nz = BYTE( nz );
+ goto loop;
+
+ ARITH_ADDR_MODES( 0x25 ) // AND
+ nz = (a &= data);
+ pc++;
+ goto loop;
+
+ ARITH_ADDR_MODES( 0x45 ) // EOR
+ nz = (a ^= data);
+ pc++;
+ goto loop;
+
+ ARITH_ADDR_MODES( 0x05 ) // ORA
+ nz = (a |= data);
+ pc++;
+ goto loop;
+
+// Add/subtract
+
+ ARITH_ADDR_MODES( 0xE5 ) // SBC
+ data ^= 0xFF;
+ goto adc_imm;
+
+ ARITH_ADDR_MODES( 0x65 ) // ADC
+ adc_imm: {
+ if ( flags & d08 )
+ dprintf( "Decimal mode not supported\n" );
+ int carry = c >> 8 & 1;
+ int ov = (a ^ 0x80) + carry + SBYTE( data );
+ flags = (flags & ~v40) + (ov >> 2 & v40);
+ c = nz = a + data + carry;
+ pc++;
+ a = BYTE( nz );
+ goto loop;
+ }
+
+// Shift/rotate
+
+ case 0x4A: // LSR A
+ c = 0;
+ case 0x6A: // ROR A
+ nz = c >> 1 & 0x80;
+ c = a << 8;
+ nz += a >> 1;
+ a = nz;
+ goto loop;
+
+ case 0x0A: // ASL A
+ nz = a << 1;
+ c = nz;
+ a = BYTE( nz );
+ goto loop;
+
+ case 0x2A: { // ROL A
+ nz = a << 1;
+ int temp = c >> 8 & 1;
+ c = nz;
+ nz += temp;
+ a = BYTE( nz );
+ goto loop;
+ }
+
+ case 0x5E: // LSR abs,X
+ data += x;
+ case 0x4E: // LSR abs
+ c = 0;
+ case 0x6E: // ROR abs
+ ror_abs: {
+ ADD_PAGE( data );
+ FLUSH_TIME();
+ int temp = READ_MEM( data );
+ nz = (c >> 1 & 0x80) + (temp >> 1);
+ c = temp << 8;
+ goto rotate_common;
+ }
+
+ case 0x3E: // ROL abs,X
+ data += x;
+ goto rol_abs;
+
+ case 0x1E: // ASL abs,X
+ data += x;
+ case 0x0E: // ASL abs
+ c = 0;
+ case 0x2E: // ROL abs
+ rol_abs:
+ ADD_PAGE( data );
+ nz = c >> 8 & 1;
+ FLUSH_TIME();
+ nz += (c = READ_MEM( data ) << 1);
+ rotate_common:
+ pc++;
+ WRITE_MEM( data, BYTE( nz ) );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x7E: // ROR abs,X
+ data += x;
+ goto ror_abs;
+
+ case 0x76: // ROR zp,x
+ data = BYTE( data + x );
+ goto ror_zp;
+
+ case 0x56: // LSR zp,x
+ data = BYTE( data + x );
+ case 0x46: // LSR zp
+ c = 0;
+ case 0x66: // ROR zp
+ ror_zp: {
+ int temp = READ_LOW( data );
+ nz = (c >> 1 & 0x80) + (temp >> 1);
+ c = temp << 8;
+ goto write_nz_zp;
+ }
+
+ case 0x36: // ROL zp,x
+ data = BYTE( data + x );
+ goto rol_zp;
+
+ case 0x16: // ASL zp,x
+ data = BYTE( data + x );
+ case 0x06: // ASL zp
+ c = 0;
+ case 0x26: // ROL zp
+ rol_zp:
+ nz = c >> 8 & 1;
+ nz += (c = READ_LOW( data ) << 1);
+ goto write_nz_zp;
+
+// Increment/decrement
+
+#define INC_DEC( reg, n ) reg = BYTE( nz = reg + n ); goto loop;
+
+ case 0x1A: // INA
+ INC_DEC( a, +1 )
+
+ case 0xE8: // INX
+ INC_DEC( x, +1 )
+
+ case 0xC8: // INY
+ INC_DEC( y, +1 )
+
+ case 0x3A: // DEA
+ INC_DEC( a, -1 )
+
+ case 0xCA: // DEX
+ INC_DEC( x, -1 )
+
+ case 0x88: // DEY
+ INC_DEC( y, -1 )
+
+ case 0xF6: // INC zp,x
+ data = BYTE( data + x );
+ case 0xE6: // INC zp
+ nz = 1;
+ goto add_nz_zp;
+
+ case 0xD6: // DEC zp,x
+ data = BYTE( data + x );
+ case 0xC6: // DEC zp
+ nz = -1;
+ add_nz_zp:
+ nz += READ_LOW( data );
+ write_nz_zp:
+ pc++;
+ WRITE_LOW( data, nz );
+ goto loop;
+
+ case 0xFE: // INC abs,x
+ data = x + GET_ADDR();
+ goto inc_ptr;
+
+ case 0xEE: // INC abs
+ data = GET_ADDR();
+ inc_ptr:
+ nz = 1;
+ goto inc_common;
+
+ case 0xDE: // DEC abs,x
+ data = x + GET_ADDR();
+ goto dec_ptr;
+
+ case 0xCE: // DEC abs
+ data = GET_ADDR();
+ dec_ptr:
+ nz = -1;
+ inc_common:
+ FLUSH_TIME();
+ pc += 2;
+ nz += READ_MEM( data );
+ WRITE_MEM( data, BYTE( nz ) );
+ CACHE_TIME();
+ goto loop;
+
+// Transfer
+
+ case 0xA8: // TAY
+ y = nz = a;
+ goto loop;
+
+ case 0x98: // TYA
+ a = nz = y;
+ goto loop;
+
+ case 0xAA: // TAX
+ x = nz = a;
+ goto loop;
+
+ case 0x8A: // TXA
+ a = nz = x;
+ goto loop;
+
+ case 0x9A: // TXS
+ SET_SP( x ); // verified (no flag change)
+ goto loop;
+
+ case 0xBA: // TSX
+ x = nz = GET_SP();
+ goto loop;
+
+ #define SWAP_REGS( r1, r2 ) {\
+ int t = r1;\
+ r1 = r2;\
+ r2 = t;\
+ goto loop;\
+ }
+
+ case 0x02: // SXY
+ SWAP_REGS( x, y );
+
+ case 0x22: // SAX
+ SWAP_REGS( a, x );
+
+ case 0x42: // SAY
+ SWAP_REGS( a, y );
+
+ case 0x62: // CLA
+ a = 0;
+ goto loop;
+
+ case 0x82: // CLX
+ x = 0;
+ goto loop;
+
+ case 0xC2: // CLY
+ y = 0;
+ goto loop;
+
+// Stack
+
+ case 0x48: // PHA
+ sp = SP( -1 );
+ WRITE_STACK( sp, a );
+ goto loop;
+
+ case 0x68: // PLA
+ a = nz = READ_STACK( sp );
+ sp = SP( 1 );
+ goto loop;
+
+ case 0xDA: // PHX
+ sp = SP( -1 );
+ WRITE_STACK( sp, x );
+ goto loop;
+
+ case 0x5A: // PHY
+ sp = SP( -1 );
+ WRITE_STACK( sp, y );
+ goto loop;
+
+ case 0x40:{// RTI
+ pc = READ_STACK( SP( 1 ) );
+ pc += READ_STACK( SP( 2 ) ) * 0x100;
+ int temp = READ_STACK( sp );
+ sp = SP( 3 );
+ data = flags;
+ SET_FLAGS( temp );
+ CPU.r.flags = flags; // update externally-visible I flag
+ if ( (data ^ flags) & i04 )
+ {
+ time_t new_time = CPU.end_time_;
+ if ( !(flags & i04) && new_time > CPU.irq_time_ )
+ new_time = CPU.irq_time_;
+ int delta = s.base - new_time;
+ s.base = new_time;
+ s_time += delta;
+ }
+ goto loop;
+ }
+
+ case 0xFA: // PLX
+ x = nz = READ_STACK( sp );
+ sp = SP( 1 );
+ goto loop;
+
+ case 0x7A: // PLY
+ y = nz = READ_STACK( sp );
+ sp = SP( 1 );
+ goto loop;
+
+ case 0x28:{// PLP
+ int temp = READ_STACK( sp );
+ sp = SP( 1 );
+ int changed = flags ^ temp;
+ SET_FLAGS( temp );
+ if ( !(changed & i04) )
+ goto loop; // I flag didn't change
+ if ( flags & i04 )
+ goto handle_sei;
+ goto handle_cli;
+ }
+
+ case 0x08:{// PHP
+ int temp;
+ GET_FLAGS( temp );
+ sp = SP( -1 );
+ WRITE_STACK( sp, temp | b10 );
+ goto loop;
+ }
+
+// Flags
+
+ case 0x38: // SEC
+ c = 0x100;
+ goto loop;
+
+ case 0x18: // CLC
+ c = 0;
+ goto loop;
+
+ case 0xB8: // CLV
+ flags &= ~v40;
+ goto loop;
+
+ case 0xD8: // CLD
+ flags &= ~d08;
+ goto loop;
+
+ case 0xF8: // SED
+ flags |= d08;
+ goto loop;
+
+ case 0x58: // CLI
+ if ( !(flags & i04) )
+ goto loop;
+ flags &= ~i04;
+ handle_cli: {
+ //dprintf( "CLI at %d\n", TIME );
+ CPU.r.flags = flags; // update externally-visible I flag
+ int delta = s.base - CPU.irq_time_;
+ if ( delta <= 0 )
+ {
+ if ( TIME() < CPU.irq_time_ )
+ goto loop;
+ goto delayed_cli;
+ }
+ s.base = CPU.irq_time_;
+ s_time += delta;
+ if ( s_time < 0 )
+ goto loop;
+
+ if ( delta >= s_time + 1 )
+ {
+ // delayed irq until after next instruction
+ s.base += s_time + 1;
+ s_time = -1;
+ CPU.irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop
+ goto loop;
+ }
+
+ // TODO: implement
+ delayed_cli:
+ dprintf( "Delayed CLI not supported\n" );
+ goto loop;
+ }
+
+ case 0x78: // SEI
+ if ( flags & i04 )
+ goto loop;
+ flags |= i04;
+ handle_sei: {
+ CPU.r.flags = flags; // update externally-visible I flag
+ int delta = s.base - CPU.end_time_;
+ s.base = CPU.end_time_;
+ s_time += delta;
+ if ( s_time < 0 )
+ goto loop;
+
+ dprintf( "Delayed SEI not supported\n" );
+ goto loop;
+ }
+
+// Special
+
+ case 0x53:{// TAM
+ int bits = data; // avoid using data across function call
+ pc++;
+ for ( int i = 0; i < 8; i++ )
+ if ( bits & (1 << i) )
+ SET_MMR( i, a );
+ goto loop;
+ }
+
+ case 0x43:{// TMA
+ pc++;
+ byte const* in = CPU.mmr;
+ do
+ {
+ if ( data & 1 )
+ a = *in;
+ in++;
+ }
+ while ( (data >>= 1) != 0 );
+ goto loop;
+ }
+
+ case 0x03: // ST0
+ case 0x13: // ST1
+ case 0x23:{// ST2
+ int addr = opcode >> 4;
+ if ( addr )
+ addr++;
+ pc++;
+ FLUSH_TIME();
+ WRITE_VDP( addr, data );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ case 0xEA: // NOP
+ goto loop;
+
+ case 0x54: // CSL
+ dprintf( "CSL not supported\n" );
+ illegal_encountered = true;
+ goto loop;
+
+ case 0xD4: // CSH
+ goto loop;
+
+ case 0xF4: { // SET
+ //int operand = GET_MSB();
+ dprintf( "SET not handled\n" );
+ //switch ( data )
+ //{
+ //}
+ illegal_encountered = true;
+ goto loop;
+ }
+
+// Block transfer
+
+ {
+ int in_alt;
+ int in_inc;
+ int out_alt;
+ int out_inc;
+
+ case 0xE3: // TIA
+ in_alt = 0;
+ goto bxfer_alt;
+
+ case 0xF3: // TAI
+ in_alt = 1;
+ bxfer_alt:
+ in_inc = in_alt ^ 1;
+ out_alt = in_inc;
+ out_inc = in_alt;
+ goto bxfer;
+
+ case 0xD3: // TIN
+ in_inc = 1;
+ out_inc = 0;
+ goto bxfer_no_alt;
+
+ case 0xC3: // TDD
+ in_inc = -1;
+ out_inc = -1;
+ goto bxfer_no_alt;
+
+ case 0x73: // TII
+ in_inc = 1;
+ out_inc = 1;
+ bxfer_no_alt:
+ in_alt = 0;
+ out_alt = 0;
+ bxfer:
+ int in = GET_LE16( instr + 0 );
+ int out = GET_LE16( instr + 2 );
+ int count = GET_LE16( instr + 4 );
+ if ( !count )
+ count = 0x10000;
+ pc += 6;
+ WRITE_STACK( SP( -1 ), y );
+ WRITE_STACK( SP( -2 ), a );
+ WRITE_STACK( SP( -3 ), x );
+ FLUSH_TIME();
+ do
+ {
+ // TODO: reads from $0800-$1400 in I/O page should do I/O
+ int t = READ_MEM( in );
+ in = WORD( in + in_inc );
+ s.time += 6;
+ if ( in_alt )
+ in_inc = -in_inc;
+ WRITE_MEM( out, t );
+ out = WORD( out + out_inc );
+ if ( out_alt )
+ out_inc = -out_inc;
+ }
+ while ( --count );
+ CACHE_TIME();
+ goto loop;
+ }
+
+// Illegal
+
+ default:
+ check( (unsigned) opcode <= 0xFF );
+ dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 );
+ illegal_encountered = true;
+ goto loop;
+ }
+ assert( false ); // catch missing 'goto loop' or accidental 'break'
+
+ int result_;
+handle_brk:
+ pc++;
+ result_ = 6;
+
+interrupt:
+ {
+ s_time += 7;
+
+ // Save PC and read vector
+ WRITE_STACK( SP( -1 ), pc >> 8 );
+ WRITE_STACK( SP( -2 ), pc );
+ pc = GET_LE16( &READ_CODE( 0xFFF0 ) + result_ );
+
+ // Save flags
+ int temp;
+ GET_FLAGS( temp );
+ if ( result_ == 6 )
+ temp |= b10; // BRK sets B bit
+ sp = SP( -3 );
+ WRITE_STACK( sp, temp );
+
+ // Update I flag in externally-visible flags
+ flags &= ~d08;
+ CPU.r.flags = (flags |= i04);
+
+ // Update time
+ int delta = s.base - CPU.end_time_;
+ if ( delta >= 0 )
+ goto loop;
+ s_time += delta;
+ s.base = CPU.end_time_;
+ goto loop;
+ }
+
+idle_done:
+ s_time = 0;
+
+out_of_time:
+ pc--;
+
+ // Optional action that triggers interrupt or changes irq/end time
+ #ifdef CPU_DONE
+ {
+ CPU_DONE( result_ );
+ if ( result_ >= 0 )
+ goto interrupt;
+ if ( s_time < 0 )
+ goto loop;
+ }
+ #endif
+
+ // Flush cached state
+ CPU.r.pc = pc;
+ CPU.r.sp = GET_SP();
+ CPU.r.a = a;
+ CPU.r.x = x;
+ CPU.r.y = y;
+
+ int temp;
+ GET_FLAGS( temp );
+ CPU.r.flags = temp;
+
+ CPU.cpu_state_.base = s.base;
+ CPU.cpu_state_.time = s_time;
+ CPU.cpu_state = &CPU.cpu_state_;
+}
diff --git a/Frameworks/GME/gme/K051649_Emu.cpp b/Frameworks/GME/gme/K051649_Emu.cpp
new file mode 100644
index 000000000..26aaa6794
--- /dev/null
+++ b/Frameworks/GME/gme/K051649_Emu.cpp
@@ -0,0 +1,73 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "K051649_Emu.h"
+#include "k051649.h"
+
+K051649_Emu::K051649_Emu() { SCC = 0; }
+
+K051649_Emu::~K051649_Emu()
+{
+ if ( SCC ) device_stop_k051649( SCC );
+}
+
+int K051649_Emu::set_rate( int clock_rate )
+{
+ if ( SCC )
+ {
+ device_stop_k051649( SCC );
+ SCC = 0;
+ }
+
+ SCC = device_start_k051649( clock_rate );
+ if ( !SCC )
+ return 1;
+
+ reset();
+ return 0;
+}
+
+void K051649_Emu::reset()
+{
+ device_reset_k051649( SCC );
+ k051649_set_mute_mask( SCC, 0 );
+}
+
+void K051649_Emu::write( int port, int offset, int data )
+{
+ k051649_w( SCC, (port << 1) | 0x00, offset);
+ k051649_w( SCC, (port << 1) | 0x01, data);
+}
+
+void K051649_Emu::mute_voices( int mask )
+{
+ k051649_set_mute_mask( SCC, mask );
+}
+
+void K051649_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ k051649_update( SCC, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/K051649_Emu.h b/Frameworks/GME/gme/K051649_Emu.h
new file mode 100644
index 000000000..f45214db6
--- /dev/null
+++ b/Frameworks/GME/gme/K051649_Emu.h
@@ -0,0 +1,33 @@
+// K051649 sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef K051649_EMU_H
+#define K051649_EMU_H
+
+class K051649_Emu {
+ void* SCC;
+public:
+ K051649_Emu();
+ ~K051649_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int clock_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 5 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int port, int offset, int data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/K053260_Emu.cpp b/Frameworks/GME/gme/K053260_Emu.cpp
new file mode 100644
index 000000000..8444b8c4d
--- /dev/null
+++ b/Frameworks/GME/gme/K053260_Emu.cpp
@@ -0,0 +1,77 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "K053260_Emu.h"
+#include "k053260.h"
+
+K053260_Emu::K053260_Emu() { chip = 0; }
+
+K053260_Emu::~K053260_Emu()
+{
+ if ( chip ) device_stop_k053260( chip );
+}
+
+int K053260_Emu::set_rate( int clock_rate )
+{
+ if ( chip )
+ {
+ device_stop_k053260( chip );
+ chip = 0;
+ }
+
+ chip = device_start_k053260( clock_rate );
+ if ( !chip )
+ return 1;
+
+ reset();
+ return 0;
+}
+
+void K053260_Emu::reset()
+{
+ device_reset_k053260( chip );
+ k053260_set_mute_mask( chip, 0 );
+}
+
+void K053260_Emu::write( int addr, int data )
+{
+ k053260_w( chip, addr, data);
+}
+
+void K053260_Emu::write_rom( int size, int start, int length, void * data )
+{
+ k053260_write_rom( chip, size, start, length, (const UINT8 *) data );
+}
+
+void K053260_Emu::mute_voices( int mask )
+{
+ k053260_set_mute_mask( chip, mask );
+}
+
+void K053260_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ k053260_update( chip, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/K053260_Emu.h b/Frameworks/GME/gme/K053260_Emu.h
new file mode 100644
index 000000000..af346c1cc
--- /dev/null
+++ b/Frameworks/GME/gme/K053260_Emu.h
@@ -0,0 +1,36 @@
+// K053260 sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef K053260_EMU_H
+#define K053260_EMU_H
+
+class K053260_Emu {
+ void* chip;
+public:
+ K053260_Emu();
+ ~K053260_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int clock_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 5 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Scales ROM size, then writes length bytes from data at start offset
+ void write_rom( int size, int start, int length, void * data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/K054539_Emu.cpp b/Frameworks/GME/gme/K054539_Emu.cpp
new file mode 100644
index 000000000..3c25eb633
--- /dev/null
+++ b/Frameworks/GME/gme/K054539_Emu.cpp
@@ -0,0 +1,79 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "K054539_Emu.h"
+#include "k054539.h"
+
+K054539_Emu::K054539_Emu() { chip = 0; }
+
+K054539_Emu::~K054539_Emu()
+{
+ if ( chip ) device_stop_k054539( chip );
+}
+
+int K054539_Emu::set_rate( int clock_rate, int flags )
+{
+ if ( chip )
+ {
+ device_stop_k054539( chip );
+ chip = 0;
+ }
+
+ chip = device_start_k054539( clock_rate );
+ if ( !chip )
+ return 1;
+
+ k054539_init_flags( chip, flags );
+
+ reset();
+ return 0;
+}
+
+void K054539_Emu::reset()
+{
+ device_reset_k054539( chip );
+ k054539_set_mute_mask( chip, 0 );
+}
+
+void K054539_Emu::write( int addr, int data )
+{
+ k054539_w( chip, addr, data);
+}
+
+void K054539_Emu::write_rom( int size, int start, int length, void * data )
+{
+ k054539_write_rom( chip, size, start, length, (const UINT8 *) data );
+}
+
+void K054539_Emu::mute_voices( int mask )
+{
+ k054539_set_mute_mask( chip, mask );
+}
+
+void K054539_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ k054539_update( chip, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/K054539_Emu.h b/Frameworks/GME/gme/K054539_Emu.h
new file mode 100644
index 000000000..1af976212
--- /dev/null
+++ b/Frameworks/GME/gme/K054539_Emu.h
@@ -0,0 +1,36 @@
+// K054539 sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef K054539_EMU_H
+#define K054539_EMU_H
+
+class K054539_Emu {
+ void* chip;
+public:
+ K054539_Emu();
+ ~K054539_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int clock_rate, int flags );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 5 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Scales ROM size, then writes length bytes from data at start offset
+ void write_rom( int size, int start, int length, void * data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Kss_Core.cpp b/Frameworks/GME/gme/Kss_Core.cpp
new file mode 100644
index 000000000..97ac2ded5
--- /dev/null
+++ b/Frameworks/GME/gme/Kss_Core.cpp
@@ -0,0 +1,214 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Kss_Core.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2006-2009 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"
+
+Kss_Core::Kss_Core() : rom( Kss_Cpu::page_size )
+{
+ memset( unmapped_read, 0xFF, sizeof unmapped_read );
+}
+
+Kss_Core::~Kss_Core() { }
+
+void Kss_Core::unload()
+{
+ rom.clear();
+}
+
+static blargg_err_t check_kss_header( void const* header )
+{
+ if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) )
+ return blargg_err_file_type;
+ return blargg_ok;
+}
+
+blargg_err_t Kss_Core::load_( Data_Reader& in )
+{
+ memset( &header_, 0, sizeof header_ );
+ assert( offsetof (header_t,msx_audio_vol) == header_t::size - 1 );
+ RETURN_ERR( rom.load( in, header_t::base_size, &header_, 0 ) );
+
+ RETURN_ERR( check_kss_header( header_.tag ) );
+
+ header_.last_track [0] = 255;
+ if ( header_.tag [3] == 'C' )
+ {
+ if ( header_.extra_header )
+ {
+ header_.extra_header = 0;
+ set_warning( "Unknown data in header" );
+ }
+ if ( header_.device_flags & ~0x0F )
+ {
+ header_.device_flags &= 0x0F;
+ set_warning( "Unknown data in header" );
+ }
+ }
+ else if ( header_.extra_header )
+ {
+ if ( header_.extra_header != header_.ext_size )
+ {
+ header_.extra_header = 0;
+ set_warning( "Invalid extra_header_size" );
+ }
+ else
+ {
+ memcpy( header_.data_size, rom.begin(), header_.ext_size );
+ }
+ }
+
+ #ifndef NDEBUG
+ {
+ int ram_mode = header_.device_flags & 0x84; // MSX
+ if ( header_.device_flags & 0x02 ) // SMS
+ ram_mode = (header_.device_flags & 0x88);
+
+ if ( ram_mode )
+ dprintf( "RAM not supported\n" ); // TODO: support
+ }
+ #endif
+
+ return blargg_ok;
+}
+
+void Kss_Core::jsr( byte const (&addr) [2] )
+{
+ ram [--cpu.r.sp] = idle_addr >> 8;
+ ram [--cpu.r.sp] = idle_addr & 0xFF;
+ cpu.r.pc = get_le16( addr );
+}
+
+blargg_err_t Kss_Core::start_track( int track )
+{
+ memset( ram, 0xC9, 0x4000 );
+ memset( ram + 0x4000, 0, sizeof ram - 0x4000 );
+
+ // copy driver code to lo RAM
+ static byte const bios [] = {
+ 0xD3, 0xA0, 0xF5, 0x7B, 0xD3, 0xA1, 0xF1, 0xC9, // $0001: WRTPSG
+ 0xD3, 0xA0, 0xDB, 0xA2, 0xC9 // $0009: RDPSG
+ };
+ static byte const vectors [] = {
+ 0xC3, 0x01, 0x00, // $0093: WRTPSG vector
+ 0xC3, 0x09, 0x00, // $0096: RDPSG vector
+ };
+ memcpy( ram + 0x01, bios, sizeof bios );
+ memcpy( ram + 0x93, vectors, sizeof vectors );
+
+ // copy non-banked data into RAM
+ int load_addr = get_le16( header_.load_addr );
+ int orig_load_size = get_le16( header_.load_size );
+ int load_size = min( orig_load_size, rom.file_size() );
+ load_size = min( load_size, (int) mem_size - load_addr );
+ if ( load_size != orig_load_size )
+ set_warning( "Excessive data size" );
+ memcpy( ram + load_addr, rom.begin() + header_.extra_header, load_size );
+
+ rom.set_addr( -load_size - header_.extra_header );
+
+ // check available bank data
+ int const bank_size = this->bank_size();
+ int max_banks = (rom.file_size() - load_size + bank_size - 1) / bank_size;
+ bank_count = header_.bank_mode & 0x7F;
+ if ( bank_count > max_banks )
+ {
+ bank_count = max_banks;
+ set_warning( "Bank data missing" );
+ }
+ //dprintf( "load_size : $%X\n", load_size );
+ //dprintf( "bank_size : $%X\n", bank_size );
+ //dprintf( "bank_count: %d (%d claimed)\n", bank_count, header_.bank_mode & 0x7F );
+
+ ram [idle_addr] = 0xFF;
+ cpu.reset( unmapped_write, unmapped_read );
+ cpu.map_mem( 0, mem_size, ram, ram );
+
+ cpu.r.sp = 0xF380;
+ cpu.r.b.a = track;
+ cpu.r.b.h = 0;
+ next_play = play_period;
+ gain_updated = false;
+ jsr( header_.init_addr );
+
+ return blargg_ok;
+}
+
+void Kss_Core::set_bank( int logical, int physical )
+{
+ int const bank_size = this->bank_size();
+
+ int addr = 0x8000;
+ if ( logical && bank_size == 8 * 1024 )
+ addr = 0xA000;
+
+ physical -= header_.first_bank;
+ if ( (unsigned) physical >= (unsigned) bank_count )
+ {
+ byte* data = ram + addr;
+ cpu.map_mem( addr, bank_size, data, data );
+ }
+ else
+ {
+ int phys = physical * bank_size;
+ for ( int offset = 0; offset < bank_size; offset += cpu.page_size )
+ cpu.map_mem( addr + offset, cpu.page_size,
+ unmapped_write, rom.at_addr( phys + offset ) );
+ }
+}
+
+void Kss_Core::cpu_out( time_t, addr_t addr, int data )
+{
+ dprintf( "OUT $%04X,$%02X\n", addr, data );
+}
+
+int Kss_Core::cpu_in( time_t, addr_t addr )
+{
+ dprintf( "IN $%04X\n", addr );
+ return 0xFF;
+}
+
+blargg_err_t Kss_Core::end_frame( time_t end )
+{
+ while ( cpu.time() < end )
+ {
+ time_t next = min( end, next_play );
+ run_cpu( next );
+ if ( cpu.r.pc == idle_addr )
+ cpu.set_time( next );
+
+ if ( cpu.time() >= next_play )
+ {
+ next_play += play_period;
+ if ( cpu.r.pc == idle_addr )
+ {
+ if ( !gain_updated )
+ {
+ gain_updated = true;
+ update_gain();
+ }
+
+ jsr( header_.play_addr );
+ }
+ }
+ }
+
+ next_play -= end;
+ check( next_play >= 0 );
+ cpu.adjust_time( -end );
+
+ return blargg_ok;
+}
diff --git a/Frameworks/GME/gme/Kss_Core.h b/Frameworks/GME/gme/Kss_Core.h
new file mode 100644
index 000000000..67ec94e20
--- /dev/null
+++ b/Frameworks/GME/gme/Kss_Core.h
@@ -0,0 +1,100 @@
+// MSX computer KSS music file emulator
+
+// Game_Music_Emu $vers
+#ifndef KSS_CORE_H
+#define KSS_CORE_H
+
+#include "Gme_Loader.h"
+#include "Rom_Data.h"
+#include "Z80_Cpu.h"
+
+class Kss_Core : public Gme_Loader {
+public:
+ // KSS file header
+ struct header_t
+ {
+ enum { size = 0x20 };
+ enum { base_size = 0x10 };
+ enum { ext_size = size - base_size };
+
+ byte tag [4];
+ byte load_addr [2];
+ byte load_size [2];
+ byte init_addr [2];
+ byte play_addr [2];
+ byte first_bank;
+ byte bank_mode;
+ byte extra_header;
+ byte device_flags;
+
+ // KSSX extended data, if extra_header==0x10
+ byte data_size [4];
+ byte unused [4];
+ byte first_track [2];
+ byte last_track [2]; // if no extended data, we set this to 0xFF
+ byte psg_vol;
+ byte scc_vol;
+ byte msx_music_vol;
+ byte msx_audio_vol;
+ };
+
+ // Header for currently loaded file
+ header_t const& header() const { return header_; }
+
+ // ROM data
+ Rom_Data const& rom_() const { return rom; }
+
+ typedef int time_t;
+ void set_play_period( time_t p ) { play_period = p; }
+
+ blargg_err_t start_track( int );
+
+ blargg_err_t end_frame( time_t );
+
+protected:
+ typedef Z80_Cpu Kss_Cpu;
+ Kss_Cpu cpu;
+
+ void set_bank( int logical, int physical );
+
+ typedef int addr_t;
+ virtual void cpu_write( addr_t, int ) = 0;
+ virtual int cpu_in( time_t, addr_t );
+ virtual void cpu_out( time_t, addr_t, int );
+
+ // Called after one frame of emulation
+ virtual void update_gain() = 0;
+
+// Implementation
+public:
+ Kss_Core();
+ virtual ~Kss_Core();
+
+protected:
+ virtual blargg_err_t load_( Data_Reader& );
+ virtual void unload();
+
+private:
+ enum { idle_addr = 0xFFFF };
+
+ Rom_Data rom;
+ header_t header_;
+ bool gain_updated;
+ int bank_count;
+ time_t play_period;
+ time_t next_play;
+
+ // large items
+ enum { mem_size = 0x10000 };
+ byte ram [mem_size + Kss_Cpu::cpu_padding];
+ byte unmapped_read [0x100]; // TODO: why isn't this page_size?
+ // because CPU can't read beyond this in last page? or because it will spill into unmapped_write?
+
+ byte unmapped_write [Kss_Cpu::page_size];
+
+ int bank_size() const { return (16 * 1024) >> (header_.bank_mode >> 7 & 1); }
+ bool run_cpu( time_t end );
+ void jsr( byte const (&addr) [2] );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Nes_Cpu_run.h b/Frameworks/GME/gme/Nes_Cpu_run.h
new file mode 100644
index 000000000..0973b1852
--- /dev/null
+++ b/Frameworks/GME/gme/Nes_Cpu_run.h
@@ -0,0 +1,1121 @@
+// NES 6502 CPU emulator run function
+
+#if 0
+/* Define these macros in the source file before #including this file.
+- Parameters might be expressions, so they are best evaluated only once,
+though they NEVER have side-effects, so multiple evaluation is OK.
+- Output parameters might be a multiple-assignment expression like "a=x",
+so they must NOT be parenthesized.
+- Except where noted, time() and related functions will NOT work
+correctly inside a macro. TIME() is always correct, and FLUSH_TIME() and
+CACHE_TIME() allow the time changing functions to work.
+- Macros "returning" void may use a {} statement block. */
+
+ // 0 <= addr <= 0xFFFF + page_size
+ // time functions can be used
+ int READ_MEM( addr_t );
+ void WRITE_MEM( addr_t, int data );
+ // 0 <= READ_MEM() <= 0xFF
+
+ // 0 <= addr <= 0x1FF
+ int READ_LOW( addr_t );
+ void WRITE_LOW( addr_t, int data );
+ // 0 <= READ_LOW() <= 0xFF
+
+ // Often-used instructions attempt these before using a normal memory access.
+ // Optional; defaults to READ_MEM() and WRITE_MEM()
+ bool CAN_READ_FAST( addr_t ); // if true, uses result of READ_FAST
+ void READ_FAST( addr_t, int& out ); // ALWAYS called BEFORE CAN_READ_FAST
+ bool CAN_WRITE_FAST( addr_t ); // if true, uses WRITE_FAST instead of WRITE_MEM
+ void WRITE_FAST( addr_t, int data );
+
+ // Used by instructions most often used to access the NES PPU (LDA abs and BIT abs).
+ // Optional; defaults to READ_MEM.
+ void READ_PPU( addr_t, int& out );
+ // 0 <= out <= 0xFF
+
+// The following can be used within macros:
+
+ // Current time
+ time_t TIME();
+
+ // Allows use of time functions
+ void FLUSH_TIME();
+
+ // Must be used before end of macro if FLUSH_TIME() was used earlier
+ void CACHE_TIME();
+
+// Configuration (optional; commented behavior if defined)
+
+ // Emulates dummy reads for indexed instructions
+ #define NES_CPU_DUMMY_READS 1
+
+ // Optimizes as if map_code( 0, 0x10000 + cpu_padding, FLAT_MEM ) is always in effect
+ #define FLAT_MEM my_mem_array
+
+ // Expanded just before beginning of code, to help debugger
+ #define CPU_BEGIN void my_run_cpu() {
+
+#endif
+
+/* Copyright (C) 2003-2008 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 */
+
+// Allows MWCW debugger to step through code properly
+#ifdef CPU_BEGIN
+ CPU_BEGIN
+#endif
+
+// Time
+#define TIME() (s_time + s.base)
+#define FLUSH_TIME() {s.time = s_time - time_offset;}
+#define CACHE_TIME() {s_time = s.time + time_offset;}
+
+// Defaults
+#ifndef CAN_WRITE_FAST
+ #define CAN_WRITE_FAST( addr ) 0
+ #define WRITE_FAST( addr, data )
+#endif
+
+#ifndef CAN_READ_FAST
+ #define CAN_READ_FAST( addr ) 0
+ #define READ_FAST( addr, out )
+#endif
+
+#ifndef READ_PPU
+ #define READ_PPU( addr, out )\
+ {\
+ FLUSH_TIME();\
+ out = READ_MEM( addr );\
+ CACHE_TIME();\
+ }
+#endif
+
+#define READ_STACK READ_LOW
+#define WRITE_STACK WRITE_LOW
+
+// Dummy reads
+#if NES_CPU_DUMMY_READS
+ // TODO: optimize time handling
+ #define DUMMY_READ( addr, idx ) \
+ if ( (addr & 0xFF) < idx )\
+ {\
+ int const time_offset = 1;\
+ FLUSH_TIME();\
+ READ_MEM( (addr - 0x100) );\
+ CACHE_TIME();\
+ }
+#else
+ #define DUMMY_READ( addr, idx )
+#endif
+
+// Code
+#ifdef FLAT_MEM
+ #define CODE_PAGE( addr ) (FLAT_MEM)
+ #define CODE_OFFSET( addr ) (addr)
+#else
+ #define CODE_PAGE( addr ) (s.code_map [NES_CPU_PAGE( addr )])
+ #define CODE_OFFSET( addr ) NES_CPU_OFFSET( addr )
+#endif
+#define READ_CODE( addr ) (CODE_PAGE( addr ) [CODE_OFFSET( addr )])
+
+// Stack
+#define SET_SP( v ) (sp = ((v) + 1) | 0x100)
+#define GET_SP() ((sp - 1) & 0xFF)
+#define SP( o ) ((sp + (o - (o>0)*0x100)) | 0x100)
+
+// Truncation
+#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */
+#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
+#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */
+
+// Flags with hex value for clarity when used as mask.
+// Stored in indicated variable during emulation.
+int const n80 = 0x80; // nz
+int const v40 = 0x40; // flags
+int const r20 = 0x20;
+int const b10 = 0x10;
+int const d08 = 0x08; // flags
+int const i04 = 0x04; // flags
+int const z02 = 0x02; // nz
+int const c01 = 0x01; // c
+
+#define IS_NEG (nz & 0x8080)
+
+#define GET_FLAGS( out ) \
+{\
+ out = flags & (v40 | d08 | i04);\
+ out += ((nz >> 8) | nz) & n80;\
+ out += c >> 8 & c01;\
+ if ( !BYTE( nz ) )\
+ out += z02;\
+}
+
+#define SET_FLAGS( in ) \
+{\
+ flags = in & (v40 | d08 | i04);\
+ c = nz = in << 8;\
+ nz += ~in & z02;\
+}
+
+{
+ int const time_offset = 0;
+
+ // Local state
+ Nes_Cpu::cpu_state_t s;
+ #ifdef FLAT_MEM
+ s.base = CPU.cpu_state_.base;
+ #else
+ s = CPU.cpu_state_;
+ #endif
+ CPU.cpu_state = &s;
+ int s_time = CPU.cpu_state_.time; // helps even on x86
+
+ // Registers
+ int pc = CPU.r.pc;
+ int a = CPU.r.a;
+ int x = CPU.r.x;
+ int y = CPU.r.y;
+ int sp;
+ SET_SP( CPU.r.sp );
+
+ // Flags
+ int flags;
+ int c; // carry set if (c & 0x100) != 0
+ int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
+ {
+ int temp = CPU.r.flags;
+ SET_FLAGS( temp );
+ }
+
+loop:
+
+ // Check all values
+ check( (unsigned) sp - 0x100 < 0x100 );
+ check( (unsigned) pc < 0x10000 );
+ check( (unsigned) a < 0x100 );
+ check( (unsigned) x < 0x100 );
+ check( (unsigned) y < 0x100 );
+
+ // Read instruction
+ byte const* instr = CODE_PAGE( pc );
+ int opcode;
+
+ if ( CODE_OFFSET(~0) == ~0 )
+ {
+ opcode = instr [pc];
+ pc++;
+ instr += pc;
+ }
+ else
+ {
+ instr += CODE_OFFSET( pc );
+ opcode = *instr++;
+ pc++;
+ }
+
+ // local to function in case it helps optimizer
+ static byte const clock_table [256] =
+ {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
+ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
+ 6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
+ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3
+ 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4
+ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5
+ 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6
+ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7
+ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8
+ 2,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9
+ 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A
+ 2,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B
+ 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C
+ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D
+ 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
+ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
+ }; // 0x00 was 7 and 0x22 was 2
+
+ // Update time
+ if ( s_time >= 0 )
+ goto out_of_time;
+
+ #ifdef CPU_INSTR_HOOK
+ { CPU_INSTR_HOOK( (pc-1), (&instr [-1]), a, x, y, GET_SP(), TIME() ); }
+ #endif
+
+ s_time += clock_table [opcode];
+
+ int data;
+ data = *instr;
+
+ switch ( opcode )
+ {
+
+// Macros
+
+#define GET_MSB() (instr [1])
+#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB())
+#define GET_ADDR() GET_LE16( instr )
+
+#define PAGE_PENALTY( lsb ) s_time += (lsb) >> 8;
+
+#define INC_DEC( reg, n ) reg = BYTE( nz = reg + n ); goto loop;
+
+#define IND_Y( cross, out ) {\
+ int temp = READ_LOW( data ) + y;\
+ out = temp + 0x100 * READ_LOW( BYTE( data + 1 ) );\
+ cross( temp );\
+ }
+
+#define IND_X( out ) {\
+ int temp = data + x;\
+ out = 0x100 * READ_LOW( BYTE( temp + 1 ) ) + READ_LOW( BYTE( temp ) );\
+ }
+
+#define ARITH_ADDR_MODES( op )\
+case op - 0x04: /* (ind,x) */\
+ IND_X( data )\
+ goto ptr##op;\
+case op + 0x0C: /* (ind),y */\
+ IND_Y( PAGE_PENALTY, data )\
+ goto ptr##op;\
+case op + 0x10: /* zp,X */\
+ data = BYTE( 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:\
+ PAGE_PENALTY( data );\
+case op + 0x08: /* abs */\
+ ADD_PAGE( data );\
+ptr##op:\
+ FLUSH_TIME();\
+ data = READ_MEM( data );\
+ CACHE_TIME();\
+case op + 0x04: /* imm */\
+imm##op:
+
+// TODO: more efficient way to handle negative branch that wraps PC around
+#define BRANCH( cond )\
+{\
+ ++pc;\
+ if ( !(cond) ) goto loop;\
+ s_time++;\
+ int offset = SBYTE( data );\
+ s_time += (BYTE(pc) + offset) >> 8 & 1;\
+ pc = WORD( pc + offset );\
+ goto loop;\
+}
+
+// Often-Used
+
+ case 0xB5: // LDA zp,x
+ a = nz = READ_LOW( BYTE( data + x ) );
+ pc++;
+ goto loop;
+
+ case 0xA5: // LDA zp
+ a = nz = READ_LOW( data );
+ pc++;
+ goto loop;
+
+ case 0xD0: // BNE
+ BRANCH( BYTE( nz ) );
+
+ case 0x20: { // JSR
+ int temp = pc + 1;
+ pc = GET_ADDR();
+ WRITE_STACK( SP( -1 ), temp >> 8 );
+ sp = SP( -2 );
+ WRITE_STACK( sp, temp );
+ goto loop;
+ }
+
+ case 0x4C: // JMP abs
+ pc = GET_ADDR();
+ goto loop;
+
+ case 0xE8: // INX
+ INC_DEC( x, 1 )
+
+ case 0x10: // BPL
+ BRANCH( !IS_NEG )
+
+ ARITH_ADDR_MODES( 0xC5 ) // CMP
+ nz = a - data;
+ pc++;
+ c = ~nz;
+ nz &= 0xFF;
+ goto loop;
+
+ case 0x30: // BMI
+ BRANCH( IS_NEG )
+
+ case 0xF0: // BEQ
+ BRANCH( !BYTE( nz ) );
+
+ case 0x95: // STA zp,x
+ data = BYTE( data + x );
+ case 0x85: // STA zp
+ pc++;
+ WRITE_LOW( data, a );
+ goto loop;
+
+ case 0xC8: // INY
+ INC_DEC( y, 1 )
+
+ case 0xA8: // TAY
+ y = a;
+ nz = a;
+ goto loop;
+
+ case 0x98: // TYA
+ a = y;
+ nz = y;
+ goto loop;
+
+ case 0xAD:{// LDA abs
+ int addr = GET_ADDR();
+ pc += 2;
+ READ_PPU( addr, a = nz );
+ goto loop;
+ }
+
+ case 0x60: // RTS
+ pc = 1 + READ_STACK( sp );
+ pc += 0x100 * READ_STACK( SP( 1 ) );
+ sp = SP( 2 );
+ goto loop;
+
+ {
+ int addr;
+
+ case 0x8D: // STA abs
+ addr = GET_ADDR();
+ pc += 2;
+ if ( CAN_WRITE_FAST( addr ) )
+ {
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+ sta_ptr:
+ FLUSH_TIME();
+ WRITE_MEM( addr, a );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x99: // STA abs,Y
+ addr = y + GET_ADDR();
+ pc += 2;
+ if ( CAN_WRITE_FAST( addr ) )
+ {
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+ goto sta_abs_x;
+
+ case 0x9D: // STA abs,X (slightly more common than STA abs)
+ addr = x + GET_ADDR();
+ pc += 2;
+ if ( CAN_WRITE_FAST( addr ) )
+ {
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+ DUMMY_READ( addr, x );
+ sta_abs_x:
+ FLUSH_TIME();
+ WRITE_MEM( addr, a );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x91: // STA (ind),Y
+ #define NO_PAGE_PENALTY( lsb )
+ IND_Y( NO_PAGE_PENALTY, addr )
+ pc++;
+ DUMMY_READ( addr, y );
+ goto sta_ptr;
+
+ case 0x81: // STA (ind,X)
+ IND_X( addr )
+ pc++;
+ goto sta_ptr;
+
+ }
+
+ case 0xA9: // LDA #imm
+ pc++;
+ a = data;
+ nz = data;
+ goto loop;
+
+ // common read instructions
+ {
+ int addr;
+
+ case 0xA1: // LDA (ind,X)
+ IND_X( addr )
+ pc++;
+ goto a_nz_read_addr;
+
+ case 0xB1:// LDA (ind),Y
+ addr = READ_LOW( data ) + y;
+ PAGE_PENALTY( addr );
+ addr += 0x100 * READ_LOW( BYTE( data + 1 ) );
+ pc++;
+ READ_FAST( addr, a = nz );
+ if ( CAN_READ_FAST( addr ) )
+ goto loop;
+ DUMMY_READ( addr, y );
+ goto a_nz_read_addr;
+
+ case 0xB9: // LDA abs,Y
+ PAGE_PENALTY( data + y );
+ addr = GET_ADDR() + y;
+ pc += 2;
+ READ_FAST( addr, a = nz );
+ if ( CAN_READ_FAST( addr ) )
+ goto loop;
+ goto a_nz_read_addr;
+
+ case 0xBD: // LDA abs,X
+ PAGE_PENALTY( data + x );
+ addr = GET_ADDR() + x;
+ pc += 2;
+ READ_FAST( addr, a = nz );
+ if ( CAN_READ_FAST( addr ) )
+ goto loop;
+ DUMMY_READ( addr, x );
+ a_nz_read_addr:
+ FLUSH_TIME();
+ a = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+
+ }
+
+// Branch
+
+ case 0x50: // BVC
+ BRANCH( !(flags & v40) )
+
+ case 0x70: // BVS
+ BRANCH( flags & v40 )
+
+ case 0xB0: // BCS
+ BRANCH( c & 0x100 )
+
+ case 0x90: // BCC
+ BRANCH( !(c & 0x100) )
+
+// Load/store
+
+ case 0x94: // STY zp,x
+ data = BYTE( data + x );
+ case 0x84: // STY zp
+ pc++;
+ WRITE_LOW( data, y );
+ goto loop;
+
+ case 0x96: // STX zp,y
+ data = BYTE( data + y );
+ case 0x86: // STX zp
+ pc++;
+ WRITE_LOW( data, x );
+ goto loop;
+
+ case 0xB6: // LDX zp,y
+ data = BYTE( data + y );
+ case 0xA6: // LDX zp
+ data = READ_LOW( data );
+ case 0xA2: // LDX #imm
+ pc++;
+ x = data;
+ nz = data;
+ goto loop;
+
+ case 0xB4: // LDY zp,x
+ data = BYTE( data + x );
+ case 0xA4: // LDY zp
+ data = READ_LOW( data );
+ case 0xA0: // LDY #imm
+ pc++;
+ y = data;
+ nz = data;
+ goto loop;
+
+ case 0xBC: // LDY abs,X
+ data += x;
+ PAGE_PENALTY( data );
+ case 0xAC:{// LDY abs
+ int addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ FLUSH_TIME();
+ y = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ case 0xBE: // LDX abs,y
+ data += y;
+ PAGE_PENALTY( data );
+ case 0xAE:{// LDX abs
+ int addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ FLUSH_TIME();
+ x = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ {
+ int temp;
+ case 0x8C: // STY abs
+ temp = y;
+ goto store_abs;
+
+ case 0x8E: // STX abs
+ temp = x;
+ store_abs:
+ int addr = GET_ADDR();
+ pc += 2;
+ if ( CAN_WRITE_FAST( addr ) )
+ {
+ WRITE_FAST( addr, temp );
+ goto loop;
+ }
+ FLUSH_TIME();
+ WRITE_MEM( addr, temp );
+ CACHE_TIME();
+ goto loop;
+ }
+
+// Compare
+
+ case 0xEC:{// CPX abs
+ int addr = GET_ADDR();
+ pc++;
+ FLUSH_TIME();
+ data = READ_MEM( addr );
+ CACHE_TIME();
+ goto cpx_data;
+ }
+
+ case 0xE4: // CPX zp
+ data = READ_LOW( data );
+ case 0xE0: // CPX #imm
+ cpx_data:
+ nz = x - data;
+ pc++;
+ c = ~nz;
+ nz &= 0xFF;
+ goto loop;
+
+ case 0xCC:{// CPY abs
+ int addr = GET_ADDR();
+ pc++;
+ FLUSH_TIME();
+ data = READ_MEM( addr );
+ CACHE_TIME();
+ goto cpy_data;
+ }
+
+ case 0xC4: // CPY zp
+ data = READ_LOW( data );
+ case 0xC0: // CPY #imm
+ cpy_data:
+ nz = y - data;
+ pc++;
+ c = ~nz;
+ nz &= 0xFF;
+ goto loop;
+
+// Logical
+
+ ARITH_ADDR_MODES( 0x25 ) // AND
+ nz = (a &= data);
+ pc++;
+ goto loop;
+
+ ARITH_ADDR_MODES( 0x45 ) // EOR
+ nz = (a ^= data);
+ pc++;
+ goto loop;
+
+ ARITH_ADDR_MODES( 0x05 ) // ORA
+ nz = (a |= data);
+ pc++;
+ goto loop;
+
+ case 0x2C:{// BIT abs
+ int addr = GET_ADDR();
+ pc += 2;
+ READ_PPU( addr, nz );
+ flags = (flags & ~v40) + (nz & v40);
+ if ( a & nz )
+ goto loop;
+ nz <<= 8; // result must be zero, even if N bit is set
+ goto loop;
+ }
+
+ case 0x24: // BIT zp
+ nz = READ_LOW( data );
+ pc++;
+ flags = (flags & ~v40) + (nz & v40);
+ if ( a & nz )
+ goto loop; // Z should be clear, and nz must be non-zero if nz & a is
+ nz <<= 8; // set Z flag without affecting N flag
+ goto loop;
+
+// Add/subtract
+
+ ARITH_ADDR_MODES( 0xE5 ) // SBC
+ case 0xEB: // unofficial equivalent
+ data ^= 0xFF;
+ goto adc_imm;
+
+ ARITH_ADDR_MODES( 0x65 ) // ADC
+ adc_imm: {
+ int carry = c >> 8 & 1;
+ int ov = (a ^ 0x80) + carry + SBYTE( data );
+ flags = (flags & ~v40) + (ov >> 2 & v40);
+ c = nz = a + data + carry;
+ pc++;
+ a = BYTE( nz );
+ goto loop;
+ }
+
+// Shift/rotate
+
+ case 0x4A: // LSR A
+ c = 0;
+ case 0x6A: // ROR A
+ nz = c >> 1 & 0x80;
+ c = a << 8;
+ nz += a >> 1;
+ a = nz;
+ goto loop;
+
+ case 0x0A: // ASL A
+ nz = a << 1;
+ c = nz;
+ a = BYTE( nz );
+ goto loop;
+
+ case 0x2A: { // ROL A
+ nz = a << 1;
+ int temp = c >> 8 & 1;
+ c = nz;
+ nz += temp;
+ a = BYTE( nz );
+ goto loop;
+ }
+
+ case 0x5E: // LSR abs,X
+ data += x;
+ case 0x4E: // LSR abs
+ c = 0;
+ case 0x6E: // ROR abs
+ ror_abs: {
+ ADD_PAGE( data );
+ FLUSH_TIME();
+ int temp = READ_MEM( data );
+ nz = (c >> 1 & 0x80) + (temp >> 1);
+ c = temp << 8;
+ goto rotate_common;
+ }
+
+ case 0x3E: // ROL abs,X
+ data += x;
+ goto rol_abs;
+
+ case 0x1E: // ASL abs,X
+ data += x;
+ case 0x0E: // ASL abs
+ c = 0;
+ case 0x2E: // ROL abs
+ rol_abs:
+ ADD_PAGE( data );
+ nz = c >> 8 & 1;
+ FLUSH_TIME();
+ nz += (c = READ_MEM( data ) << 1);
+ rotate_common:
+ pc++;
+ WRITE_MEM( data, BYTE( nz ) );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x7E: // ROR abs,X
+ data += x;
+ goto ror_abs;
+
+ case 0x76: // ROR zp,x
+ data = BYTE( data + x );
+ goto ror_zp;
+
+ case 0x56: // LSR zp,x
+ data = BYTE( data + x );
+ case 0x46: // LSR zp
+ c = 0;
+ case 0x66: // ROR zp
+ ror_zp: {
+ int temp = READ_LOW( data );
+ nz = (c >> 1 & 0x80) + (temp >> 1);
+ c = temp << 8;
+ goto write_nz_zp;
+ }
+
+ case 0x36: // ROL zp,x
+ data = BYTE( data + x );
+ goto rol_zp;
+
+ case 0x16: // ASL zp,x
+ data = BYTE( data + x );
+ case 0x06: // ASL zp
+ c = 0;
+ case 0x26: // ROL zp
+ rol_zp:
+ nz = c >> 8 & 1;
+ nz += (c = READ_LOW( data ) << 1);
+ goto write_nz_zp;
+
+// Increment/decrement
+
+ case 0xCA: // DEX
+ INC_DEC( x, -1 )
+
+ case 0x88: // DEY
+ INC_DEC( y, -1 )
+
+ case 0xF6: // INC zp,x
+ data = BYTE( data + x );
+ case 0xE6: // INC zp
+ nz = 1;
+ goto add_nz_zp;
+
+ case 0xD6: // DEC zp,x
+ data = BYTE( data + x );
+ case 0xC6: // DEC zp
+ nz = -1;
+ add_nz_zp:
+ nz += READ_LOW( data );
+ write_nz_zp:
+ pc++;
+ WRITE_LOW( data, nz );
+ goto loop;
+
+ case 0xFE: // INC abs,x
+ data = x + GET_ADDR();
+ goto inc_ptr;
+
+ case 0xEE: // INC abs
+ data = GET_ADDR();
+ inc_ptr:
+ nz = 1;
+ goto inc_common;
+
+ case 0xDE: // DEC abs,x
+ data = x + GET_ADDR();
+ goto dec_ptr;
+
+ case 0xCE: // DEC abs
+ data = GET_ADDR();
+ dec_ptr:
+ nz = -1;
+ inc_common:
+ FLUSH_TIME();
+ pc += 2;
+ nz += READ_MEM( data );
+ WRITE_MEM( data, BYTE( nz ) );
+ CACHE_TIME();
+ goto loop;
+
+// Transfer
+
+ case 0xAA: // TAX
+ x = nz = a;
+ goto loop;
+
+ case 0x8A: // TXA
+ a = nz = x;
+ goto loop;
+
+ case 0x9A: // TXS
+ SET_SP( x ); // verified (no flag change)
+ goto loop;
+
+ case 0xBA: // TSX
+ x = nz = GET_SP();
+ goto loop;
+
+// Stack
+
+ case 0x48: // PHA
+ sp = SP( -1 );
+ WRITE_STACK( sp, a );
+ goto loop;
+
+ case 0x68: // PLA
+ a = nz = READ_STACK( sp );
+ sp = SP( 1 );
+ goto loop;
+
+ case 0x40:{// RTI
+ pc = READ_STACK( SP( 1 ) );
+ pc += READ_STACK( SP( 2 ) ) * 0x100;
+ int temp = READ_STACK( sp );
+ sp = SP( 3 );
+ data = flags;
+ SET_FLAGS( temp );
+ CPU.r.flags = flags; // update externally-visible I flag
+ int delta = s.base - CPU.irq_time_;
+ if ( delta <= 0 ) goto loop; // end_time < irq_time
+ if ( flags & i04 ) goto loop;
+ s_time += delta;
+ s.base = CPU.irq_time_;
+ goto loop;
+ }
+
+ case 0x28:{// PLP
+ int temp = READ_STACK( sp );
+ sp = SP( 1 );
+ int changed = flags ^ temp;
+ SET_FLAGS( temp );
+ if ( !(changed & i04) )
+ goto loop; // I flag didn't change
+ if ( flags & i04 )
+ goto handle_sei;
+ goto handle_cli;
+ }
+
+ case 0x08:{// PHP
+ int temp;
+ GET_FLAGS( temp );
+ sp = SP( -1 );
+ WRITE_STACK( sp, temp | (b10 | r20) );
+ goto loop;
+ }
+
+ case 0x6C:{// JMP (ind)
+ data = GET_ADDR();
+ byte const* page = CODE_PAGE( data );
+ pc = page [CODE_OFFSET( data )];
+ data = (data & 0xFF00) + ((data + 1) & 0xFF);
+ pc += page [CODE_OFFSET( data )] * 0x100;
+ goto loop;
+ }
+
+ case 0x00: // BRK
+ goto handle_brk;
+
+// Flags
+
+ case 0x38: // SEC
+ c = 0x100;
+ goto loop;
+
+ case 0x18: // CLC
+ c = 0;
+ goto loop;
+
+ case 0xB8: // CLV
+ flags &= ~v40;
+ goto loop;
+
+ case 0xD8: // CLD
+ flags &= ~d08;
+ goto loop;
+
+ case 0xF8: // SED
+ flags |= d08;
+ goto loop;
+
+ case 0x58: // CLI
+ if ( !(flags & i04) )
+ goto loop;
+ flags &= ~i04;
+ handle_cli: {
+ //dprintf( "CLI at %d\n", TIME );
+ CPU.r.flags = flags; // update externally-visible I flag
+ int delta = s.base - CPU.irq_time_;
+ if ( delta <= 0 )
+ {
+ if ( TIME() < CPU.irq_time_ )
+ goto loop;
+ goto delayed_cli;
+ }
+ s.base = CPU.irq_time_;
+ s_time += delta;
+ if ( s_time < 0 )
+ goto loop;
+
+ if ( delta >= s_time + 1 )
+ {
+ // delayed irq until after next instruction
+ s.base += s_time + 1;
+ s_time = -1;
+ goto loop;
+ }
+
+ // TODO: implement
+ delayed_cli:
+ dprintf( "Delayed CLI not emulated\n" );
+ goto loop;
+ }
+
+ case 0x78: // SEI
+ if ( flags & i04 )
+ goto loop;
+ flags |= i04;
+ handle_sei: {
+ CPU.r.flags = flags; // update externally-visible I flag
+ int delta = s.base - CPU.end_time_;
+ s.base = CPU.end_time_;
+ s_time += delta;
+ if ( s_time < 0 )
+ goto loop;
+
+ dprintf( "Delayed SEI not emulated\n" );
+ goto loop;
+ }
+
+// Unofficial
+
+ // SKW - skip word
+ case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:
+ PAGE_PENALTY( data + x );
+ case 0x0C:
+ pc++;
+ // SKB - skip byte
+ case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64:
+ case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
+ pc++;
+ goto loop;
+
+ // NOP
+ case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA:
+ goto loop;
+
+ case Nes_Cpu::halt_opcode: // HLT - halt processor
+ if ( pc-- > 0x10000 )
+ {
+ // handle wrap-around (assumes caller has put page of HLT at 0x10000)
+ pc = WORD( pc );
+ goto loop;
+ }
+ case 0x02: case 0x12: case 0x32: case 0x42: case 0x52:
+ case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2:
+ goto stop;
+
+// Unimplemented
+
+ case 0xFF: // force 256-entry jump table for optimization purposes
+ c |= 1; // compiler doesn't know that this won't affect anything
+ default:
+ check( (unsigned) opcode < 0x100 );
+
+ #ifdef UNIMPL_INSTR
+ UNIMPL_INSTR();
+ #endif
+
+ // At least skip over proper number of bytes instruction uses
+ static unsigned char const illop_lens [8] = {
+ 0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0
+ };
+ int opcode = instr [-1];
+ int len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3;
+ if ( opcode == 0x9C )
+ len = 2;
+ pc += len;
+ CPU.error_count_++;
+
+ // Account for extra clock
+ if ( (opcode >> 4) == 0x0B )
+ {
+ if ( opcode == 0xB3 )
+ data = READ_LOW( data );
+ if ( opcode != 0xB7 )
+ PAGE_PENALTY( data + y );
+ }
+ goto loop;
+ }
+ assert( false ); // catch missing 'goto loop' or accidental 'break'
+
+ int result_;
+handle_brk:
+ pc++;
+ result_ = b10 | 4;
+
+#ifdef CPU_DONE
+interrupt:
+#endif
+ {
+ s_time += 7;
+
+ // Save PC and read vector
+ WRITE_STACK( SP( -1 ), pc >> 8 );
+ WRITE_STACK( SP( -2 ), pc );
+ pc = GET_LE16( &READ_CODE( 0xFFFA ) + (result_ & 4) );
+
+ // Save flags
+ int temp;
+ GET_FLAGS( temp );
+ temp |= r20 + (result_ & b10); // B flag set for BRK
+ sp = SP( -3 );
+ WRITE_STACK( sp, temp );
+
+ // Update I flag in externally-visible flags
+ CPU.r.flags = (flags |= i04);
+
+ // Update time
+ int delta = s.base - CPU.end_time_;
+ if ( delta >= 0 )
+ goto loop;
+ s_time += delta;
+ s.base = CPU.end_time_;
+ goto loop;
+ }
+
+out_of_time:
+ pc--;
+
+ // Optional action that triggers interrupt or changes irq/end time
+ #ifdef CPU_DONE
+ {
+ CPU_DONE( result_ );
+ if ( result_ >= 0 )
+ goto interrupt;
+ if ( s_time < 0 )
+ goto loop;
+ }
+ #endif
+stop:
+
+ // Flush cached state
+ CPU.r.pc = pc;
+ CPU.r.sp = GET_SP();
+ CPU.r.a = a;
+ CPU.r.x = x;
+ CPU.r.y = y;
+
+ int temp;
+ GET_FLAGS( temp );
+ CPU.r.flags = temp;
+
+ CPU.cpu_state_.base = s.base;
+ CPU.cpu_state_.time = s_time;
+ CPU.cpu_state = &CPU.cpu_state_;
+}
diff --git a/Frameworks/GME/gme/Nes_Fds_Apu.cpp b/Frameworks/GME/gme/Nes_Fds_Apu.cpp
new file mode 100644
index 000000000..69fe2c082
--- /dev/null
+++ b/Frameworks/GME/gme/Nes_Fds_Apu.cpp
@@ -0,0 +1,280 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Nes_Fds_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"
+
+int const fract_range = 65536;
+
+void Nes_Fds_Apu::reset()
+{
+ memset( regs_, 0, sizeof regs_ );
+ memset( mod_wave, 0, sizeof mod_wave );
+
+ last_time = 0;
+ env_delay = 0;
+ sweep_delay = 0;
+ wave_pos = 0;
+ last_amp = 0;
+ wave_fract = fract_range;
+ mod_fract = fract_range;
+ mod_pos = 0;
+ mod_write_pos = 0;
+
+ static byte const initial_regs [0x0B] = {
+ 0x80, // disable envelope
+ 0, 0, 0xC0, // disable wave and lfo
+ 0x80, // disable sweep
+ 0, 0, 0x80, // disable modulation
+ 0, 0, 0xFF // LFO period // TODO: use 0xE8 as FDS ROM does?
+ };
+ for ( int i = 0; i < (int) sizeof initial_regs; i++ )
+ {
+ // two writes to set both gain and period for envelope registers
+ write_( io_addr + wave_size + i, 0 );
+ write_( io_addr + wave_size + i, initial_regs [i] );
+ }
+}
+
+void Nes_Fds_Apu::write_( unsigned addr, int data )
+{
+ unsigned reg = addr - io_addr;
+ if ( reg < io_size )
+ {
+ if ( reg < wave_size )
+ {
+ if ( regs (0x4089) & 0x80 )
+ regs_ [reg] = data & wave_sample_max;
+ }
+ else
+ {
+ regs_ [reg] = data;
+ switch ( addr )
+ {
+ case 0x4080:
+ if ( data & 0x80 )
+ env_gain = data & 0x3F;
+ else
+ env_speed = (data & 0x3F) + 1;
+ break;
+
+ case 0x4084:
+ if ( data & 0x80 )
+ sweep_gain = data & 0x3F;
+ else
+ sweep_speed = (data & 0x3F) + 1;
+ break;
+
+ case 0x4085:
+ mod_pos = mod_write_pos;
+ regs (0x4085) = data & 0x7F;
+ break;
+
+ case 0x4088:
+ if ( regs (0x4087) & 0x80 )
+ {
+ int pos = mod_write_pos;
+ data &= 0x07;
+ mod_wave [pos ] = data;
+ mod_wave [pos + 1] = data;
+ mod_write_pos = (pos + 2) & (wave_size - 1);
+ mod_pos = (mod_pos + 2) & (wave_size - 1);
+ }
+ break;
+ }
+ }
+ }
+}
+
+void Nes_Fds_Apu::set_tempo( double t )
+{
+ lfo_tempo = lfo_base_tempo;
+ if ( t != 1.0 )
+ {
+ lfo_tempo = int ((double) lfo_base_tempo / t + 0.5);
+ if ( lfo_tempo <= 0 )
+ lfo_tempo = 1;
+ }
+}
+
+void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
+{
+ int const wave_freq = (regs (0x4083) & 0x0F) * 0x100 + regs (0x4082);
+ Blip_Buffer* const output_ = this->output_;
+ if ( wave_freq && output_ && !((regs (0x4089) | regs (0x4083)) & 0x80) )
+ {
+ output_->set_modified();
+
+ // master_volume
+ #define MVOL_ENTRY( percent ) (master_vol_max * percent + 50) / 100
+ static unsigned char const master_volumes [4] = {
+ MVOL_ENTRY( 100 ), MVOL_ENTRY( 67 ), MVOL_ENTRY( 50 ), MVOL_ENTRY( 40 )
+ };
+ int const master_volume = master_volumes [regs (0x4089) & 0x03];
+
+ // lfo_period
+ blip_time_t lfo_period = regs (0x408A) * lfo_tempo;
+ if ( regs (0x4083) & 0x40 )
+ lfo_period = 0;
+
+ // sweep setup
+ blip_time_t sweep_time = last_time + sweep_delay;
+ blip_time_t const sweep_period = lfo_period * sweep_speed;
+ if ( !sweep_period || regs (0x4084) & 0x80 )
+ sweep_time = final_end_time;
+
+ // envelope setup
+ blip_time_t env_time = last_time + env_delay;
+ blip_time_t const env_period = lfo_period * env_speed;
+ if ( !env_period || regs (0x4080) & 0x80 )
+ env_time = final_end_time;
+
+ // modulation
+ int mod_freq = 0;
+ if ( !(regs (0x4087) & 0x80) )
+ mod_freq = (regs (0x4087) & 0x0F) * 0x100 + regs (0x4086);
+
+ blip_time_t end_time = last_time;
+ do
+ {
+ // sweep
+ if ( sweep_time <= end_time )
+ {
+ sweep_time += sweep_period;
+ int mode = regs (0x4084) >> 5 & 2;
+ int new_sweep_gain = sweep_gain + mode - 1;
+ if ( (unsigned) new_sweep_gain <= (unsigned) 0x80 >> mode )
+ sweep_gain = new_sweep_gain;
+ else
+ regs (0x4084) |= 0x80; // optimization only
+ }
+
+ // envelope
+ if ( env_time <= end_time )
+ {
+ env_time += env_period;
+ int mode = regs (0x4080) >> 5 & 2;
+ int new_env_gain = env_gain + mode - 1;
+ if ( (unsigned) new_env_gain <= (unsigned) 0x80 >> mode )
+ env_gain = new_env_gain;
+ else
+ regs (0x4080) |= 0x80; // optimization only
+ }
+
+ // new end_time
+ blip_time_t const start_time = end_time;
+ end_time = final_end_time;
+ if ( end_time > env_time ) end_time = env_time;
+ if ( end_time > sweep_time ) end_time = sweep_time;
+
+ // frequency modulation
+ int freq = wave_freq;
+ if ( mod_freq )
+ {
+ // time of next modulation clock
+ blip_time_t mod_time = start_time + (mod_fract + mod_freq - 1) / mod_freq;
+ if ( end_time > mod_time )
+ end_time = mod_time;
+
+ // run modulator up to next clock and save old sweep_bias
+ int sweep_bias = regs (0x4085);
+ mod_fract -= (end_time - start_time) * mod_freq;
+ if ( mod_fract <= 0 )
+ {
+ mod_fract += fract_range;
+ check( (unsigned) mod_fract <= fract_range );
+
+ static short const mod_table [8] = { 0, +1, +2, +4, 0, -4, -2, -1 };
+ int mod = mod_wave [mod_pos];
+ mod_pos = (mod_pos + 1) & (wave_size - 1);
+ int new_sweep_bias = (sweep_bias + mod_table [mod]) & 0x7F;
+ if ( mod == 4 )
+ new_sweep_bias = 0;
+ regs (0x4085) = new_sweep_bias;
+ }
+
+ // apply frequency modulation
+ sweep_bias = (sweep_bias ^ 0x40) - 0x40;
+ int factor = sweep_bias * sweep_gain;
+ int extra = factor & 0x0F;
+ factor >>= 4;
+ if ( extra )
+ {
+ factor--;
+ if ( sweep_bias >= 0 )
+ factor += 3;
+ }
+ if ( factor > 193 ) factor -= 258;
+ if ( factor < -64 ) factor += 256;
+ freq += (freq * factor) >> 6;
+ if ( freq <= 0 )
+ continue;
+ }
+
+ // wave
+ int wave_fract = this->wave_fract;
+ blip_time_t delay = (wave_fract + freq - 1) / freq;
+ blip_time_t time = start_time + delay;
+
+ if ( time <= end_time )
+ {
+ // at least one wave clock within start_time...end_time
+
+ blip_time_t const min_delay = fract_range / freq;
+ int wave_pos = this->wave_pos;
+
+ int volume = env_gain;
+ if ( volume > vol_max )
+ volume = vol_max;
+ volume *= master_volume;
+
+ int const min_fract = min_delay * freq;
+
+ do
+ {
+ // clock wave
+ int amp = regs_ [wave_pos] * volume;
+ wave_pos = (wave_pos + 1) & (wave_size - 1);
+ int delta = amp - last_amp;
+ if ( delta )
+ {
+ last_amp = amp;
+ synth.offset_inline( time, delta, output_ );
+ }
+
+ wave_fract += fract_range - delay * freq;
+ check( unsigned (fract_range - wave_fract) < freq );
+
+ // delay until next clock
+ delay = min_delay;
+ if ( wave_fract > min_fract )
+ delay++;
+ check( delay && delay == (wave_fract + freq - 1) / freq );
+
+ time += delay;
+ }
+ while ( time <= end_time ); // TODO: using < breaks things, but <= is wrong
+
+ this->wave_pos = wave_pos;
+ }
+ this->wave_fract = wave_fract - (end_time - (time - delay)) * freq;
+ check( this->wave_fract > 0 );
+ }
+ while ( end_time < final_end_time );
+
+ env_delay = env_time - final_end_time; check( env_delay >= 0 );
+ sweep_delay = sweep_time - final_end_time; check( sweep_delay >= 0 );
+ }
+ last_time = final_end_time;
+}
diff --git a/Frameworks/GME/gme/Nes_Fds_Apu.h b/Frameworks/GME/gme/Nes_Fds_Apu.h
new file mode 100644
index 000000000..21176b2db
--- /dev/null
+++ b/Frameworks/GME/gme/Nes_Fds_Apu.h
@@ -0,0 +1,139 @@
+// NES FDS sound chip emulator
+
+// $package
+#ifndef NES_FDS_APU_H
+#define NES_FDS_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+class Nes_Fds_Apu {
+public:
+ // setup
+ void set_tempo( double );
+ enum { osc_count = 1 };
+ void set_output( Blip_Buffer* buf );
+ void volume( double );
+ void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+
+ // emulation
+ void reset();
+ enum { io_addr = 0x4040 };
+ enum { io_size = 0x53 };
+ void write( blip_time_t time, unsigned addr, int data );
+ int read( blip_time_t time, unsigned addr );
+ void end_frame( blip_time_t );
+
+public:
+ Nes_Fds_Apu();
+ void write_( unsigned addr, int data );
+ BLARGG_DISABLE_NOTHROW
+
+ void set_output( int index, Blip_Buffer* center,
+ Blip_Buffer* left_ignored = NULL, Blip_Buffer* right_ignored = NULL );
+ BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x4040 }; )
+ BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x4092 }; )
+ BLARGG_DEPRECATED_TEXT( enum { reg_count = end_addr - start_addr + 1 }; )
+ void osc_output( int, Blip_Buffer* );
+private:
+ enum { wave_size = 0x40 };
+ enum { master_vol_max = 10 };
+ enum { vol_max = 0x20 };
+ enum { wave_sample_max = 0x3F };
+
+ unsigned char regs_ [io_size];// last written value to registers
+
+ enum { lfo_base_tempo = 8 };
+ int lfo_tempo; // normally 8; adjusted by set_tempo()
+
+ int env_delay;
+ int env_speed;
+ int env_gain;
+
+ int sweep_delay;
+ int sweep_speed;
+ int sweep_gain;
+
+ int wave_pos;
+ int last_amp;
+ blip_time_t wave_fract;
+
+ int mod_fract;
+ int mod_pos;
+ int mod_write_pos;
+ unsigned char mod_wave [wave_size];
+
+ // synthesis
+ blip_time_t last_time;
+ Blip_Buffer* output_;
+ Blip_Synth_Fast synth;
+
+ // allow access to registers by absolute address (i.e. 0x4080)
+ unsigned char& regs( unsigned addr ) { return regs_ [addr - io_addr]; }
+
+ void run_until( blip_time_t );
+};
+
+inline void Nes_Fds_Apu::volume( double v )
+{
+ synth.volume( 0.14 / master_vol_max / vol_max / wave_sample_max * v );
+}
+
+inline void Nes_Fds_Apu::set_output( Blip_Buffer* b )
+{
+ output_ = b;
+}
+
+inline void Nes_Fds_Apu::set_output( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* )
+{
+ assert( (unsigned) i < osc_count );
+ output_ = buf;
+}
+
+inline void Nes_Fds_Apu::end_frame( blip_time_t end_time )
+{
+ if ( end_time > last_time )
+ run_until( end_time );
+ last_time -= end_time;
+ assert( last_time >= 0 );
+}
+
+inline void Nes_Fds_Apu::write( blip_time_t time, unsigned addr, int data )
+{
+ run_until( time );
+ write_( addr, data );
+}
+
+inline int Nes_Fds_Apu::read( blip_time_t time, unsigned addr )
+{
+ run_until( time );
+
+ int result = 0xFF;
+ switch ( addr )
+ {
+ case 0x4090:
+ result = env_gain;
+ break;
+
+ case 0x4092:
+ result = sweep_gain;
+ break;
+
+ default:
+ unsigned i = addr - io_addr;
+ if ( i < wave_size )
+ result = regs_ [i];
+ }
+
+ return result | 0x40;
+}
+
+inline Nes_Fds_Apu::Nes_Fds_Apu()
+{
+ lfo_tempo = lfo_base_tempo;
+ set_output( NULL );
+ volume( 1.0 );
+ reset();
+}
+
+#endif
diff --git a/Frameworks/GME/gme/Nes_Mmc5_Apu.h b/Frameworks/GME/gme/Nes_Mmc5_Apu.h
new file mode 100644
index 000000000..717658d3e
--- /dev/null
+++ b/Frameworks/GME/gme/Nes_Mmc5_Apu.h
@@ -0,0 +1,70 @@
+// NES MMC5 sound chip emulator
+
+// Nes_Snd_Emu $vers
+#ifndef NES_MMC5_APU_H
+#define NES_MMC5_APU_H
+
+#include "blargg_common.h"
+#include "Nes_Apu.h"
+
+class Nes_Mmc5_Apu : public Nes_Apu {
+public:
+ enum { regs_addr = 0x5000 };
+ enum { regs_size = 0x16 };
+
+ enum { osc_count = 3 };
+ void write_register( blip_time_t, unsigned addr, int data );
+ void set_output( Blip_Buffer* );
+ void set_output( int index, Blip_Buffer* );
+
+ enum { exram_size = 1024 };
+ unsigned char exram [exram_size];
+
+ BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x5000 }; )
+ BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x5015 }; )
+};
+
+inline void Nes_Mmc5_Apu::set_output( int i, Blip_Buffer* b )
+{
+ // in: square 1, square 2, PCM
+ // out: square 1, square 2, skipped, skipped, PCM
+ if ( i > 1 )
+ i += 2;
+ Nes_Apu::set_output( i, b );
+}
+
+inline void Nes_Mmc5_Apu::set_output( Blip_Buffer* b )
+{
+ set_output( 0, b );
+ set_output( 1, b );
+ set_output( 2, b );
+}
+
+inline void Nes_Mmc5_Apu::write_register( blip_time_t time, unsigned addr, int data )
+{
+ switch ( addr )
+ {
+ case 0x5015: // channel enables
+ data &= 0x03; // enable the square waves only
+ // fall through
+ case 0x5000: // Square 1
+ case 0x5002:
+ case 0x5003:
+ case 0x5004: // Square 2
+ case 0x5006:
+ case 0x5007:
+ case 0x5011: // DAC
+ Nes_Apu::write_register( time, addr - 0x1000, data );
+ break;
+
+ case 0x5010: // some things write to this for some reason
+ break;
+
+#ifdef BLARGG_DEBUG_H
+ default:
+ dprintf( "Unmapped MMC5 APU write: $%04X <- $%02X\n", addr, data );
+#endif
+ }
+}
+
+#endif
diff --git a/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp b/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp
new file mode 100644
index 000000000..033061d6a
--- /dev/null
+++ b/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp
@@ -0,0 +1,206 @@
+#include "Nes_Vrc7_Apu.h"
+
+#include "ym2413.h"
+#include
+
+#include "blargg_source.h"
+
+int const period = 36; // NES CPU clocks per FM clock
+
+Nes_Vrc7_Apu::Nes_Vrc7_Apu()
+{
+ opll = 0;
+}
+
+blargg_err_t Nes_Vrc7_Apu::init()
+{
+ CHECK_ALLOC( opll = ym2413_init( 3579545, 3579545 / 72, 1 ) );
+
+ set_output( 0 );
+ volume( 1.0 );
+ reset();
+ return 0;
+}
+
+Nes_Vrc7_Apu::~Nes_Vrc7_Apu()
+{
+ if ( opll )
+ ym2413_shutdown( opll );
+}
+
+void Nes_Vrc7_Apu::set_output( Blip_Buffer* buf )
+{
+ for ( int i = 0; i < osc_count; ++i )
+ oscs [i].output = buf;
+ output_changed();
+}
+
+void Nes_Vrc7_Apu::output_changed()
+{
+ mono.output = oscs [0].output;
+ for ( int i = osc_count; --i; )
+ {
+ if ( mono.output != oscs [i].output )
+ {
+ mono.output = 0;
+ break;
+ }
+ }
+
+ if ( mono.output )
+ {
+ for ( int i = osc_count; --i; )
+ {
+ mono.last_amp += oscs [i].last_amp;
+ oscs [i].last_amp = 0;
+ }
+ }
+}
+
+void Nes_Vrc7_Apu::reset()
+{
+ addr = 0;
+ next_time = 0;
+ mono.last_amp = 0;
+
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Vrc7_Osc& osc = oscs [i];
+ osc.last_amp = 0;
+ for ( int j = 0; j < 3; ++j )
+ osc.regs [j] = 0;
+ }
+
+ ym2413_reset_chip( opll );
+}
+
+void Nes_Vrc7_Apu::write_reg( int data )
+{
+ addr = data;
+}
+
+void Nes_Vrc7_Apu::write_data( blip_time_t time, int data )
+{
+ int type = (addr >> 4) - 1;
+ int chan = addr & 15;
+ if ( (unsigned) type < 3 && chan < osc_count )
+ oscs [chan].regs [type] = data;
+
+ if ( time > next_time )
+ run_until( time );
+ ym2413_write( opll, 0, addr );
+ ym2413_write( opll, 1, data );
+}
+
+void Nes_Vrc7_Apu::end_frame( blip_time_t time )
+{
+ if ( time > next_time )
+ run_until( time );
+
+ next_time -= time;
+ assert( next_time >= 0 );
+
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Blip_Buffer* output = oscs [i].output;
+ if ( output )
+ output->set_modified();
+ }
+}
+
+void Nes_Vrc7_Apu::save_snapshot( vrc7_snapshot_t* out ) const
+{
+ out->latch = addr;
+ out->delay = next_time;
+ for ( int i = osc_count; --i >= 0; )
+ {
+ for ( int j = 0; j < 3; ++j )
+ out->regs [i] [j] = oscs [i].regs [j];
+ }
+ memcpy( out->inst, ym2413_get_inst0( opll ), 8 );
+}
+
+void Nes_Vrc7_Apu::load_snapshot( vrc7_snapshot_t const& in )
+{
+ assert( offsetof (vrc7_snapshot_t,delay) == 28 - 1 );
+
+ reset();
+ next_time = in.delay;
+ write_reg( in.latch );
+ int i;
+ for ( i = 0; i < osc_count; ++i )
+ {
+ for ( int j = 0; j < 3; ++j )
+ oscs [i].regs [j] = in.regs [i] [j];
+ }
+
+ for ( i = 0; i < 8; ++i )
+ {
+ ym2413_write( opll, 0, i );
+ ym2413_write( opll, 1, in.inst [i] );
+ }
+
+ for ( i = 0; i < 3; ++i )
+ {
+ for ( int j = 0; j < 6; ++j )
+ {
+ ym2413_write( opll, 0, 0x10 + i * 0x10 + j );
+ ym2413_write( opll, 1, oscs [j].regs [i] );
+ }
+ }
+}
+
+void Nes_Vrc7_Apu::run_until( blip_time_t end_time )
+{
+ require( end_time > next_time );
+
+ blip_time_t time = next_time;
+ void* opll = this->opll; // cache
+ Blip_Buffer* const mono_output = mono.output;
+ if ( mono_output )
+ {
+ // optimal case
+ do
+ {
+ ym2413_advance_lfo( opll );
+ int amp = 0;
+ for ( int i = 0; i < osc_count; i++ )
+ amp += ym2413_calcch( opll, i );
+ ym2413_advance( opll );
+ int delta = amp - mono.last_amp;
+ if ( delta )
+ {
+ mono.last_amp = amp;
+ synth.offset_inline( time, delta, mono_output );
+ }
+ time += period;
+ }
+ while ( time < end_time );
+ }
+ else
+ {
+ mono.last_amp = 0;
+ do
+ {
+ ym2413_advance_lfo( opll );
+ for ( int i = 0; i < osc_count; ++i )
+ {
+ Vrc7_Osc& osc = oscs [i];
+ if ( osc.output )
+ {
+ int amp = ym2413_calcch( opll, i );
+ int delta = amp - osc.last_amp;
+ if ( delta )
+ {
+ osc.last_amp = amp;
+ synth.offset( time, delta, osc.output );
+ }
+ }
+ }
+ ym2413_advance( opll );
+ time += period;
+ }
+ while ( time < end_time );
+ }
+ next_time = time;
+}
diff --git a/Frameworks/GME/gme/Nes_Vrc7_Apu.h b/Frameworks/GME/gme/Nes_Vrc7_Apu.h
new file mode 100644
index 000000000..60ea16312
--- /dev/null
+++ b/Frameworks/GME/gme/Nes_Vrc7_Apu.h
@@ -0,0 +1,80 @@
+// Konami VRC7 sound chip emulator
+
+#ifndef NES_VRC7_APU_H
+#define NES_VRC7_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+struct vrc7_snapshot_t;
+
+class Nes_Vrc7_Apu {
+public:
+ blargg_err_t init();
+
+ // See Nes_Apu.h for reference
+ void reset();
+ void volume( double );
+ void treble_eq( blip_eq_t const& );
+ void set_output( Blip_Buffer* );
+ enum { osc_count = 6 };
+ void set_output( int index, Blip_Buffer* );
+ void end_frame( blip_time_t );
+ void save_snapshot( vrc7_snapshot_t* ) const;
+ void load_snapshot( vrc7_snapshot_t const& );
+
+ void write_reg( int reg );
+ void write_data( blip_time_t, int data );
+
+public:
+ Nes_Vrc7_Apu();
+ ~Nes_Vrc7_Apu();
+ BLARGG_DISABLE_NOTHROW
+private:
+ // noncopyable
+ Nes_Vrc7_Apu( const Nes_Vrc7_Apu& );
+ Nes_Vrc7_Apu& operator = ( const Nes_Vrc7_Apu& );
+
+ struct Vrc7_Osc
+ {
+ BOOST::uint8_t regs [3];
+ Blip_Buffer* output;
+ int last_amp;
+ };
+
+ Vrc7_Osc oscs [osc_count];
+ void* opll;
+ int addr;
+ blip_time_t next_time;
+ struct {
+ Blip_Buffer* output;
+ int last_amp;
+ } mono;
+
+ Blip_Synth_Fast synth;
+
+ void run_until( blip_time_t );
+ void output_changed();
+};
+
+struct vrc7_snapshot_t
+{
+ BOOST::uint8_t latch;
+ BOOST::uint8_t inst [8];
+ BOOST::uint8_t regs [6] [3];
+ BOOST::uint8_t delay;
+};
+
+inline void Nes_Vrc7_Apu::set_output( int i, Blip_Buffer* buf )
+{
+ assert( (unsigned) i < osc_count );
+ oscs [i].output = buf;
+ output_changed();
+}
+
+// DB2LIN_AMP_BITS == 11, * 2
+inline void Nes_Vrc7_Apu::volume( double v ) { synth.volume( 1.0 / 3 / 4096 * v ); }
+
+inline void Nes_Vrc7_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+
+#endif
diff --git a/Frameworks/GME/gme/Nsf_Core.cpp b/Frameworks/GME/gme/Nsf_Core.cpp
new file mode 100644
index 000000000..4fa732bf6
--- /dev/null
+++ b/Frameworks/GME/gme/Nsf_Core.cpp
@@ -0,0 +1,302 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Nsf_Core.h"
+
+#include "blargg_endian.h"
+
+#if !NSF_EMU_APU_ONLY
+ #include "Nes_Namco_Apu.h"
+ #include "Nes_Vrc6_Apu.h"
+ #include "Nes_Fme7_Apu.h"
+ #include "Nes_Fds_Apu.h"
+ #include "Nes_Mmc5_Apu.h"
+ #include "Nes_Vrc7_Apu.h"
+#endif
+
+/* Copyright (C) 2003-2008 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"
+
+Nsf_Core::Nsf_Core()
+{
+ fds = NULL;
+ fme7 = NULL;
+ mmc5 = NULL;
+ namco = NULL;
+ vrc6 = NULL;
+ vrc7 = NULL;
+}
+
+Nsf_Core::~Nsf_Core()
+{
+ unload();
+}
+
+void Nsf_Core::unload()
+{
+#if !NSF_EMU_APU_ONLY
+ delete fds;
+ fds = NULL;
+
+ delete fme7;
+ fme7 = NULL;
+
+ delete namco;
+ namco = NULL;
+
+ delete mmc5;
+ mmc5 = NULL;
+
+ delete vrc6;
+ vrc6 = NULL;
+
+ delete vrc7;
+ vrc7 = NULL;
+#endif
+
+ Nsf_Impl::unload();
+}
+
+void Nsf_Core::set_tempo( double t )
+{
+ set_play_period( (int) (header().play_period() / t) );
+ nes_apu()->set_tempo( t );
+#if !NSF_EMU_APU_ONLY
+ if ( fds )
+ fds->set_tempo( t );
+#endif
+}
+
+blargg_err_t Nsf_Core::post_load()
+{
+ int chip_flags = header().chip_flags;
+
+ #if !NSF_EMU_APU_ONLY
+ if ( chip_flags & header_t::fds_mask )
+ CHECK_ALLOC( fds = BLARGG_NEW Nes_Fds_Apu );
+
+ if ( chip_flags & header_t::fme7_mask )
+ CHECK_ALLOC( fme7 = BLARGG_NEW Nes_Fme7_Apu );
+
+ if ( chip_flags & header_t::mmc5_mask )
+ CHECK_ALLOC( mmc5 = BLARGG_NEW Nes_Mmc5_Apu );
+
+ if ( chip_flags & header_t::namco_mask )
+ CHECK_ALLOC( namco = BLARGG_NEW Nes_Namco_Apu );
+
+ if ( chip_flags & header_t::vrc6_mask )
+ CHECK_ALLOC( vrc6 = BLARGG_NEW Nes_Vrc6_Apu );
+
+ if ( chip_flags & header_t::vrc7_mask )
+ {
+ #if NSF_EMU_NO_VRC7
+ chip_flags = ~chips_mask; // give warning rather than error
+ #else
+ CHECK_ALLOC( vrc7 = BLARGG_NEW Nes_Vrc7_Apu );
+ RETURN_ERR( vrc7->init() );
+ #endif
+ }
+ #endif
+
+ set_tempo( 1.0 );
+
+ if ( chip_flags & ~chips_mask )
+ set_warning( "Uses unsupported audio expansion hardware" );
+
+ return Nsf_Impl::post_load();
+}
+
+int Nsf_Core::cpu_read( addr_t addr )
+{
+ #if !NSF_EMU_APU_ONLY
+ {
+ if ( addr == Nes_Namco_Apu::data_reg_addr && namco )
+ return namco->read_data();
+
+ if ( (unsigned) (addr - Nes_Fds_Apu::io_addr) < Nes_Fds_Apu::io_size && fds )
+ return fds->read( time(), addr );
+
+ int i = addr - 0x5C00;
+ if ( (unsigned) i < mmc5->exram_size && mmc5 )
+ return mmc5->exram [i];
+
+ int m = addr - 0x5205;
+ if ( (unsigned) m < 2 && mmc5 )
+ return (mmc5_mul [0] * mmc5_mul [1]) >> (m * 8) & 0xFF;
+ }
+ #endif
+
+ return Nsf_Impl::cpu_read( addr );
+}
+
+int Nsf_Core::unmapped_read( addr_t addr )
+{
+ switch ( addr )
+ {
+ case 0x2002:
+ case 0x4016:
+ case 0x4017:
+ return addr >> 8;
+ }
+
+ return Nsf_Impl::unmapped_read( addr );
+}
+
+void Nsf_Core::cpu_write( addr_t addr, int data )
+{
+ #if !NSF_EMU_APU_ONLY
+ {
+ if ( (unsigned) (addr - fds->io_addr) < fds->io_size && fds )
+ {
+ fds->write( time(), addr, data );
+ return;
+ }
+
+ if ( namco )
+ {
+ if ( addr == namco->addr_reg_addr )
+ {
+ namco->write_addr( data );
+ return;
+ }
+
+ if ( addr == namco->data_reg_addr )
+ {
+ namco->write_data( time(), data );
+ return;
+ }
+ }
+
+ if ( vrc6 )
+ {
+ int reg = addr & (vrc6->addr_step - 1);
+ int osc = (unsigned) (addr - vrc6->base_addr) / vrc6->addr_step;
+ if ( (unsigned) osc < vrc6->osc_count && (unsigned) reg < vrc6->reg_count )
+ {
+ vrc6->write_osc( time(), osc, reg, data );
+ return;
+ }
+ }
+
+ if ( addr >= fme7->latch_addr && fme7 )
+ {
+ switch ( addr & fme7->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;
+ }
+ }
+
+ if ( mmc5 )
+ {
+ if ( (unsigned) (addr - mmc5->regs_addr) < mmc5->regs_size )
+ {
+ mmc5->write_register( time(), addr, data );
+ return;
+ }
+
+ int m = addr - 0x5205;
+ if ( (unsigned) m < 2 )
+ {
+ mmc5_mul [m] = data;
+ return;
+ }
+
+ int i = addr - 0x5C00;
+ if ( (unsigned) i < mmc5->exram_size )
+ {
+ mmc5->exram [i] = data;
+ return;
+ }
+ }
+
+ if ( vrc7 )
+ {
+ if ( addr == 0x9010 )
+ {
+ vrc7->write_reg( data );
+ return;
+ }
+
+ if ( (unsigned) (addr - 0x9028) <= 0x08 )
+ {
+ vrc7->write_data( time(), data );
+ return;
+ }
+ }
+ }
+ #endif
+
+ return Nsf_Impl::cpu_write( addr, data );
+}
+
+void Nsf_Core::unmapped_write( addr_t addr, int data )
+{
+ switch ( addr )
+ {
+ case 0x8000: // some write to $8000 and $8001 repeatedly
+ case 0x8001:
+ case 0x4800: // probably namco sound mistakenly turned on in MCK
+ case 0xF800:
+ case 0xFFF8: // memory mapper?
+ return;
+ }
+
+ if ( mmc5 && addr == 0x5115 ) return;
+
+ // FDS memory
+ if ( fds && (unsigned) (addr - 0x8000) < 0x6000 ) return;
+
+ Nsf_Impl::unmapped_write( addr, data );
+}
+
+blargg_err_t Nsf_Core::start_track( int track )
+{
+ #if !NSF_EMU_APU_ONLY
+ if ( mmc5 )
+ {
+ mmc5_mul [0] = 0;
+ mmc5_mul [1] = 0;
+ memset( mmc5->exram, 0, mmc5->exram_size );
+ }
+ #endif
+
+ #if !NSF_EMU_APU_ONLY
+ if ( fds ) fds ->reset();
+ if ( fme7 ) fme7 ->reset();
+ if ( mmc5 ) mmc5 ->reset();
+ if ( namco ) namco->reset();
+ if ( vrc6 ) vrc6 ->reset();
+ if ( vrc7 ) vrc7 ->reset();
+ #endif
+
+ return Nsf_Impl::start_track( track );
+}
+
+void Nsf_Core::end_frame( time_t end )
+{
+ Nsf_Impl::end_frame( end );
+
+ #if !NSF_EMU_APU_ONLY
+ if ( fds ) fds ->end_frame( end );
+ if ( fme7 ) fme7 ->end_frame( end );
+ if ( mmc5 ) mmc5 ->end_frame( end );
+ if ( namco ) namco->end_frame( end );
+ if ( vrc6 ) vrc6 ->end_frame( end );
+ if ( vrc7 ) vrc7 ->end_frame( end );
+ #endif
+}
diff --git a/Frameworks/GME/gme/Nsf_Core.h b/Frameworks/GME/gme/Nsf_Core.h
new file mode 100644
index 000000000..4e5fd9e5a
--- /dev/null
+++ b/Frameworks/GME/gme/Nsf_Core.h
@@ -0,0 +1,68 @@
+// Loads NSF file and emulates CPU and sound chips
+
+// Game_Music_Emu $vers
+#ifndef NSF_CORE_H
+#define NSF_CORE_H
+
+#include "Nsf_Impl.h"
+
+class Nes_Namco_Apu;
+class Nes_Vrc6_Apu;
+class Nes_Fme7_Apu;
+class Nes_Mmc5_Apu;
+class Nes_Vrc7_Apu;
+class Nes_Fds_Apu;
+
+class Nsf_Core : public Nsf_Impl {
+public:
+
+ // Adjusts music tempo, where 1.0 is normal. Can be changed while playing.
+ // Loading a file resets tempo to 1.0.
+ void set_tempo( double );
+
+ // Pointer to sound chip, or NULL if not used by current file.
+ // Must be assigned to a Blip_Buffer to get any sound.
+ Nes_Fds_Apu * fds_apu () { return fds; }
+ Nes_Fme7_Apu * fme7_apu () { return fme7; }
+ Nes_Mmc5_Apu * mmc5_apu () { return mmc5; }
+ Nes_Namco_Apu* namco_apu() { return namco; }
+ Nes_Vrc6_Apu * vrc6_apu () { return vrc6; }
+ Nes_Vrc7_Apu * vrc7_apu () { return vrc7; }
+
+ // Mask for which chips are supported
+ #if NSF_EMU_APU_ONLY
+ enum { chips_mask = 0 };
+ #else
+ enum { chips_mask = header_t::all_mask };
+ #endif
+
+protected:
+ virtual int unmapped_read( addr_t );
+ virtual void unmapped_write( addr_t, int data );
+
+
+// Implementation
+public:
+ Nsf_Core();
+ ~Nsf_Core();
+ virtual void unload();
+ virtual blargg_err_t start_track( int );
+ virtual void end_frame( time_t );
+
+protected:
+ virtual blargg_err_t post_load();
+ virtual int cpu_read( addr_t );
+ virtual void cpu_write( addr_t, int );
+
+private:
+ byte mmc5_mul [2];
+
+ Nes_Fds_Apu* fds;
+ Nes_Fme7_Apu* fme7;
+ Nes_Mmc5_Apu* mmc5;
+ Nes_Namco_Apu* namco;
+ Nes_Vrc6_Apu* vrc6;
+ Nes_Vrc7_Apu* vrc7;
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Nsf_Cpu.cpp b/Frameworks/GME/gme/Nsf_Cpu.cpp
new file mode 100644
index 000000000..3bb5984b2
--- /dev/null
+++ b/Frameworks/GME/gme/Nsf_Cpu.cpp
@@ -0,0 +1,116 @@
+// Normal CPU for NSF emulator
+
+// $package. http://www.slack.net/~ant/
+
+#include "Nsf_Impl.h"
+
+#include "blargg_endian.h"
+
+#ifdef BLARGG_DEBUG_H
+ //#define CPU_LOG_START 1000000
+ //#include "nes_cpu_log.h"
+ #undef LOG_MEM
+#endif
+
+/* Copyright (C) 2003-2008 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"
+
+#ifndef LOG_MEM
+ #define LOG_MEM( addr, str, data ) data
+#endif
+
+int Nsf_Impl::read_mem( addr_t addr )
+{
+ int result = low_ram [addr & (low_ram_size-1)]; // also handles wrap-around
+ if ( addr & 0xE000 )
+ {
+ result = *cpu.get_code( addr );
+ if ( addr < sram_addr )
+ {
+ if ( addr == apu.status_addr )
+ result = apu.read_status( time() );
+ else
+ result = cpu_read( addr );
+ }
+ }
+ return LOG_MEM( addr, ">", result );
+}
+
+void Nsf_Impl::write_mem( addr_t addr, int data )
+{
+ (void) LOG_MEM( addr, "<", data );
+
+ int offset = addr - sram_addr;
+ if ( (unsigned) offset < sram_size )
+ {
+ sram() [offset] = data;
+ }
+ else
+ {
+ // after sram because CPU handles most low_ram accesses internally already
+ int temp = addr & (low_ram_size-1); // also handles wrap-around
+ if ( !(addr & 0xE000) )
+ {
+ low_ram [temp] = data;
+ }
+ else
+ {
+ int bank = addr - banks_addr;
+ if ( (unsigned) bank < bank_count )
+ {
+ write_bank( bank, data );
+ }
+ else if ( (unsigned) (addr - apu.io_addr) < apu.io_size )
+ {
+ apu.write_register( time(), addr, data );
+ }
+ else
+ {
+ #if !NSF_EMU_APU_ONLY
+ // 0x8000-0xDFFF is writable
+ int i = addr - 0x8000;
+ if ( (unsigned) i < fdsram_size && fds_enabled() )
+ fdsram() [i] = data;
+ else
+ #endif
+ cpu_write( addr, data );
+ }
+ }
+ }
+}
+
+#define READ_LOW( addr ) (LOG_MEM( addr, ">", low_ram [addr] ))
+#define WRITE_LOW( addr, data ) (LOG_MEM( addr, "<", low_ram [addr] = data ))
+
+#define CAN_WRITE_FAST( addr ) (addr < low_ram_size)
+#define WRITE_FAST WRITE_LOW
+
+// addr < 0x2000 || addr >= 0x8000
+#define CAN_READ_FAST( addr ) ((addr ^ 0x8000) < 0xA000)
+#define READ_FAST( addr, out ) (LOG_MEM( addr, ">", out = READ_CODE( addr ) ))
+
+#define READ_MEM( addr ) read_mem( addr )
+#define WRITE_MEM( addr, data ) write_mem( addr, data )
+
+#define CPU cpu
+
+#define CPU_BEGIN \
+bool Nsf_Impl::run_cpu_until( time_t end )\
+{\
+ cpu.set_end_time( end );\
+ if ( *cpu.get_code( cpu.r.pc ) != cpu.halt_opcode )\
+ {
+ #include "Nes_Cpu_run.h"
+ }
+ return cpu.time_past_end() < 0;
+}
diff --git a/Frameworks/GME/gme/Nsf_Impl.cpp b/Frameworks/GME/gme/Nsf_Impl.cpp
new file mode 100644
index 000000000..62c255152
--- /dev/null
+++ b/Frameworks/GME/gme/Nsf_Impl.cpp
@@ -0,0 +1,328 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Nsf_Impl.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2008 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"
+
+// number of frames until play interrupts init
+int const initial_play_delay = 7; // KikiKaikai needed this to work
+int const bank_size = 0x1000;
+int const rom_addr = 0x8000;
+
+int Nsf_Impl::read_code( addr_t addr ) const
+{
+ return *cpu.get_code( addr );
+}
+
+int Nsf_Impl::pcm_read( void* self, int addr )
+{
+ return STATIC_CAST(Nsf_Impl*,self)->read_code( addr );
+}
+
+Nsf_Impl::Nsf_Impl() : rom( bank_size ), enable_w4011( true )
+{
+ apu.dmc_reader( pcm_read, this );
+ assert( offsetof (header_t,unused [4]) == header_t::size );
+}
+
+void Nsf_Impl::unload()
+{
+ rom.clear();
+ high_ram.clear();
+ Gme_Loader::unload();
+}
+
+Nsf_Impl::~Nsf_Impl() { unload(); }
+
+bool nsf_header_t::valid_tag() const
+{
+ return 0 == memcmp( tag, "NESM\x1A", 5 );
+}
+
+double nsf_header_t::clock_rate() const
+{
+ return pal_only() ? 1662607.125 : 1789772.727272727;
+}
+
+int nsf_header_t::play_period() const
+{
+ // NTSC
+ int clocks = 29780;
+ int value = 0x411A;
+ byte const* rate_ptr = ntsc_speed;
+
+ // PAL
+ if ( pal_only() )
+ {
+ clocks = 33247;
+ value = 0x4E20;
+ rate_ptr = pal_speed;
+ }
+
+ // Default rate
+ int rate = get_le16( rate_ptr );
+ if ( rate == 0 )
+ rate = value;
+
+ // Custom rate
+ if ( rate != value )
+ clocks = (int) (rate * clock_rate() * (1.0/1000000.0));
+
+ return clocks;
+}
+
+// Gets address, given pointer to it in file header. If zero, returns rom_addr.
+Nsf_Impl::addr_t Nsf_Impl::get_addr( byte const in [] )
+{
+ addr_t addr = get_le16( in );
+ if ( addr == 0 )
+ addr = rom_addr;
+ return addr;
+}
+
+blargg_err_t Nsf_Impl::load_( Data_Reader& in )
+{
+ // pad ROM data with 0
+ RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) );
+
+ if ( !header_.valid_tag() )
+ return blargg_err_file_type;
+
+ RETURN_ERR( high_ram.resize( (fds_enabled() ? fdsram_offset + fdsram_size : fdsram_offset) ) );
+
+ addr_t load_addr = get_addr( header_.load_addr );
+ if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) )
+ set_warning( "Load address is too low" );
+
+ rom.set_addr( load_addr % bank_size );
+
+ if ( header_.vers != 1 )
+ set_warning( "Unknown file version" );
+
+ set_play_period( header_.play_period() );
+
+ return blargg_ok;
+}
+
+void Nsf_Impl::write_bank( int bank, int data )
+{
+ // Find bank in ROM
+ int offset = rom.mask_addr( data * bank_size );
+ if ( offset >= rom.size() )
+ special_event( "invalid bank" );
+ void const* rom_data = rom.at_addr( offset );
+
+ #if !NSF_EMU_APU_ONLY
+ if ( bank < bank_count - fds_banks && fds_enabled() )
+ {
+ // TODO: FDS bank switching is kind of hacky, might need to
+ // treat ROM as RAM so changes won't get lost when switching.
+ byte* out = sram();
+ if ( bank >= fds_banks )
+ {
+ out = fdsram();
+ bank -= fds_banks;
+ }
+ memcpy( &out [bank * bank_size], rom_data, bank_size );
+ return;
+ }
+ #endif
+
+ if ( bank >= fds_banks )
+ cpu.map_code( (bank + 6) * bank_size, bank_size, rom_data );
+}
+
+void Nsf_Impl::map_memory()
+{
+ // Map standard things
+ cpu.reset( unmapped_code() );
+ cpu.map_code( 0, 0x2000, low_ram, low_ram_size ); // mirrored four times
+ cpu.map_code( sram_addr, sram_size, sram() );
+
+ // Determine initial banks
+ byte banks [bank_count];
+ static byte const zero_banks [sizeof header_.banks] = { 0 };
+ if ( memcmp( header_.banks, zero_banks, sizeof zero_banks ) )
+ {
+ banks [0] = header_.banks [6];
+ banks [1] = header_.banks [7];
+ memcpy( banks + fds_banks, header_.banks, sizeof header_.banks );
+ }
+ else
+ {
+ // No initial banks, so assign them based on load_addr
+ int first_bank = (get_addr( header_.load_addr ) - sram_addr) / bank_size;
+ unsigned total_banks = rom.size() / bank_size;
+ for ( int i = bank_count; --i >= 0; )
+ {
+ int bank = i - first_bank;
+ if ( (unsigned) bank >= total_banks )
+ bank = 0;
+ banks [i] = bank;
+ }
+ }
+
+ // Map banks
+ for ( int i = (fds_enabled() ? 0 : fds_banks); i < bank_count; ++i )
+ write_bank( i, banks [i] );
+
+ // Map FDS RAM
+ if ( fds_enabled() )
+ cpu.map_code( rom_addr, fdsram_size, fdsram() );
+}
+
+inline void Nsf_Impl::push_byte( int b )
+{
+ low_ram [0x100 + cpu.r.sp--] = b;
+}
+
+// Jumps to routine, given pointer to address in file header. Pushes idle_addr
+// as return address, NOT old PC.
+void Nsf_Impl::jsr_then_stop( byte const addr [] )
+{
+ cpu.r.pc = get_addr( addr );
+ push_byte( (idle_addr - 1) >> 8 );
+ push_byte( (idle_addr - 1) );
+}
+
+blargg_err_t Nsf_Impl::start_track( int track )
+{
+ int speed_flags = 0;
+ #if NSF_EMU_EXTRA_FLAGS
+ speed_flags = header().speed_flags;
+ #endif
+
+ apu.reset( header().pal_only(), (speed_flags & 0x20) ? 0x3F : 0 );
+ apu.enable_w4011_( enable_w4011 );
+ apu.write_register( 0, 0x4015, 0x0F );
+ apu.write_register( 0, 0x4017, (speed_flags & 0x10) ? 0x80 : 0 );
+
+ // Clear memory
+ memset( unmapped_code(), Nes_Cpu::halt_opcode, unmapped_size );
+ memset( low_ram, 0, low_ram_size );
+ memset( sram(), 0, sram_size );
+
+ map_memory();
+
+ // Arrange time of first call to play routine
+ play_extra = 0;
+ next_play = play_period;
+
+ play_delay = initial_play_delay;
+ saved_state.pc = idle_addr;
+
+ // Setup for call to init routine
+ cpu.r.a = track;
+ cpu.r.x = header_.pal_only();
+ cpu.r.sp = 0xFF;
+ jsr_then_stop( header_.init_addr );
+ if ( cpu.r.pc < get_addr( header_.load_addr ) )
+ set_warning( "Init address < load address" );
+
+ return blargg_ok;
+}
+
+void Nsf_Impl::unmapped_write( addr_t addr, int data )
+{
+ dprintf( "Unmapped write $%04X <- %02X\n", (int) addr, data );
+}
+
+int Nsf_Impl::unmapped_read( addr_t addr )
+{
+ dprintf( "Unmapped read $%04X\n", (int) addr );
+ return addr >> 8;
+}
+
+void Nsf_Impl::special_event( const char str [] )
+{
+ dprintf( "%s\n", str );
+}
+
+void Nsf_Impl::run_once( time_t end )
+{
+ // Emulate until next play call if possible
+ if ( run_cpu_until( min( next_play, end ) ) )
+ {
+ // Halt instruction encountered
+
+ if ( cpu.r.pc != idle_addr )
+ {
+ special_event( "illegal instruction" );
+ cpu.count_error();
+ cpu.set_time( cpu.end_time() );
+ return;
+ }
+
+ // Init/play routine returned
+ play_delay = 1; // play can now be called regularly
+
+ if ( saved_state.pc == idle_addr )
+ {
+ // nothing to run
+ time_t t = cpu.end_time();
+ if ( cpu.time() < t )
+ cpu.set_time( t );
+ }
+ else
+ {
+ // continue init routine that was interrupted by play routine
+ cpu.r = saved_state;
+ saved_state.pc = idle_addr;
+ }
+ }
+
+ if ( time() >= next_play )
+ {
+ // Calculate time of next call to play routine
+ play_extra ^= 1; // extra clock every other call
+ next_play += play_period + play_extra;
+
+ // Call routine if ready
+ if ( play_delay && !--play_delay )
+ {
+ // Save state if init routine is still running
+ if ( cpu.r.pc != idle_addr )
+ {
+ check( saved_state.pc == idle_addr );
+ saved_state = cpu.r;
+ special_event( "play called during init" );
+ }
+
+ jsr_then_stop( header_.play_addr );
+ }
+ }
+}
+
+void Nsf_Impl::run_until( time_t end )
+{
+ while ( time() < end )
+ run_once( end );
+}
+
+void Nsf_Impl::end_frame( time_t end )
+{
+ if ( time() < end )
+ run_until( end );
+ cpu.adjust_time( -end );
+
+ // Localize to new time frame
+ next_play -= end;
+ check( next_play >= 0 );
+ if ( next_play < 0 )
+ next_play = 0;
+
+ apu.end_frame( end );
+}
diff --git a/Frameworks/GME/gme/Nsf_Impl.h b/Frameworks/GME/gme/Nsf_Impl.h
new file mode 100644
index 000000000..fcb28f7f7
--- /dev/null
+++ b/Frameworks/GME/gme/Nsf_Impl.h
@@ -0,0 +1,194 @@
+// Loads NSF file and emulates CPU and RAM, no sound chips
+
+// Game_Music_Emu $vers
+#ifndef NSF_IMPL_H
+#define NSF_IMPL_H
+
+#include "Gme_Loader.h"
+#include "Nes_Cpu.h"
+#include "Rom_Data.h"
+#include "Nes_Apu.h"
+
+// NSF file header
+struct nsf_header_t
+{
+ typedef unsigned char byte;
+ enum { size = 0x80 };
+
+ char tag [ 5];
+ byte vers;
+ byte track_count;
+ byte first_track;
+ byte load_addr [ 2];
+ byte init_addr [ 2];
+ byte play_addr [ 2];
+ char game [32]; // NOT null-terminated if 32 chars in length
+ char author [32];
+ char copyright [32];
+ byte ntsc_speed [ 2];
+ byte banks [ 8];
+ byte pal_speed [ 2];
+ byte speed_flags;
+ byte chip_flags;
+ byte unused [ 4];
+
+ // Sound chip masks
+ enum {
+ vrc6_mask = 1 << 0,
+ vrc7_mask = 1 << 1,
+ fds_mask = 1 << 2,
+ mmc5_mask = 1 << 3,
+ namco_mask = 1 << 4,
+ fme7_mask = 1 << 5,
+ all_mask = (1 << 6) - 1
+ };
+
+ // True if header has proper NSF file signature
+ bool valid_tag() const;
+
+ // True if file supports only PAL speed
+ bool pal_only() const { return (speed_flags & 3) == 1; }
+
+ // Clocks per second
+ double clock_rate() const;
+
+ // Clocks between calls to play routine
+ int play_period() const;
+};
+
+/* Loads NSF file into memory, then emulates CPU, RAM, and ROM.
+Non-memory accesses are routed through cpu_read() and cpu_write(). */
+class Nsf_Impl : public Gme_Loader {
+public:
+
+ // Sound chip
+ Nes_Apu* nes_apu() { return &apu; }
+
+ // Starts track, where 0 is the first
+ virtual blargg_err_t start_track( int );
+
+ // Emulates to at least time t, then begins new time frame at
+ // time t. Might emulate a few clocks extra, so after returning,
+ // time() may not be zero.
+ typedef int time_t; // clock count
+ virtual void end_frame( time_t n );
+
+// Finer control
+
+ // Header for currently loaded file
+ typedef nsf_header_t header_t;
+ header_t const& header() const { return header_; }
+
+ // Sets clocks between calls to play routine to p + 1/2 clock
+ void set_play_period( int p ) { play_period = p; }
+
+ // Time play routine will next be called
+ time_t play_time() const { return next_play; }
+
+ // Emulates to at least time t. Might emulate a few clocks extra.
+ virtual void run_until( time_t t );
+
+ // Time emulated to
+ time_t time() const { return cpu.time(); }
+
+ void enable_w4011_(bool enable = true) { enable_w4011 = enable; }
+
+ Rom_Data const& rom_() const { return rom; }
+
+protected:
+// Nsf_Core use
+
+ typedef int addr_t;
+
+ // Called for unmapped accesses. Default just prints info if debugging.
+ virtual void unmapped_write( addr_t, int data );
+ virtual int unmapped_read( addr_t );
+
+ // Override in derived class
+ // Bank writes and RAM at 0-$7FF and $6000-$7FFF are handled internally
+ virtual int cpu_read( addr_t a ) { return unmapped_read( a ); }
+ virtual void cpu_write( addr_t a, int data ){ unmapped_write( a, data ); }
+
+ // Reads byte as CPU would when executing code. Only works for RAM/ROM,
+ // NOT I/O like sound chips.
+ int read_code( addr_t addr ) const;
+
+// Debugger services
+
+ enum { mem_size = 0x10000 };
+
+ // CPU sits here when waiting for next call to play routine
+ enum { idle_addr = 0x5FF6 };
+
+ Nes_Cpu cpu;
+
+ // Runs CPU to at least time t and returns false, or returns true
+ // if it encounters illegal instruction (halt).
+ virtual bool run_cpu_until( time_t t );
+
+ // CPU calls through to these to access memory (except instructions)
+ int read_mem( addr_t );
+ void write_mem( addr_t, int );
+
+ // Address of play routine
+ addr_t play_addr() const { return get_addr( header_.play_addr ); }
+
+ // Same as run_until, except emulation stops for any event (routine returned,
+ // play routine called, illegal instruction).
+ void run_once( time_t );
+
+ // Make a note of event
+ virtual void special_event( const char str [] );
+
+
+// Implementation
+public:
+ Nsf_Impl();
+ ~Nsf_Impl();
+
+protected:
+ virtual blargg_err_t load_( Data_Reader& );
+ virtual void unload();
+
+private:
+ enum { low_ram_size = 0x800 };
+ enum { fdsram_size = 0x6000 };
+ enum { sram_size = 0x2000 };
+ enum { unmapped_size= Nes_Cpu::page_size + 8 };
+ enum { fds_banks = 2 };
+ enum { bank_count = fds_banks + 8 };
+ enum { banks_addr = idle_addr };
+ enum { sram_addr = 0x6000 };
+
+ blargg_vector high_ram;
+ Rom_Data rom;
+
+ // Play routine timing
+ time_t next_play;
+ time_t play_period;
+ int play_extra;
+ int play_delay;
+ bool enable_w4011;
+ Nes_Cpu::registers_t saved_state; // of interrupted init routine
+
+ // Large objects after others
+ header_t header_;
+ Nes_Apu apu;
+ byte low_ram [low_ram_size];
+
+ // Larger RAM areas allocated separately
+ enum { fdsram_offset = sram_size + unmapped_size };
+ byte* sram() { return high_ram.begin(); }
+ byte* unmapped_code() { return &high_ram [sram_size]; }
+ byte* fdsram() { return &high_ram [fdsram_offset]; }
+ int fds_enabled() const { return header_.chip_flags & header_t::fds_mask; }
+
+ void map_memory();
+ void write_bank( int index, int data );
+ void jsr_then_stop( byte const addr [] );
+ void push_byte( int );
+ static addr_t get_addr( byte const [] );
+ static int pcm_read( void*, int );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Okim6258_Emu.cpp b/Frameworks/GME/gme/Okim6258_Emu.cpp
new file mode 100644
index 000000000..dce5c6552
--- /dev/null
+++ b/Frameworks/GME/gme/Okim6258_Emu.cpp
@@ -0,0 +1,66 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Okim6258_Emu.h"
+#include "okim6258.h"
+
+Okim6258_Emu::Okim6258_Emu() { chip = 0; }
+
+Okim6258_Emu::~Okim6258_Emu()
+{
+ if ( chip ) device_stop_okim6258( chip );
+}
+
+int Okim6258_Emu::set_rate( int clock, int divider, int adpcm_type, int output_12bits )
+{
+ if ( chip )
+ {
+ device_stop_okim6258( chip );
+ chip = 0;
+ }
+
+ chip = device_start_okim6258( clock, divider, adpcm_type, output_12bits );
+ if ( !chip )
+ return 0;
+
+ reset();
+ return okim6258_get_vclk( chip );
+}
+
+void Okim6258_Emu::reset()
+{
+ device_reset_okim6258( chip );
+}
+
+void Okim6258_Emu::write( int addr, int data )
+{
+ okim6258_write( chip, addr, data );
+}
+
+void Okim6258_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ okim6258_update( chip, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/Okim6258_Emu.h b/Frameworks/GME/gme/Okim6258_Emu.h
new file mode 100644
index 000000000..9e91d5c79
--- /dev/null
+++ b/Frameworks/GME/gme/Okim6258_Emu.h
@@ -0,0 +1,29 @@
+// OKIM6258 sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef OKIM6258_EMU_H
+#define OKIM6258_EMU_H
+
+class Okim6258_Emu {
+ void* chip;
+public:
+ Okim6258_Emu();
+ ~Okim6258_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int clock, int divider, int adpcm_type, int output_12bits );
+
+ // Resets to power-up state
+ void reset();
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Okim6295_Emu.cpp b/Frameworks/GME/gme/Okim6295_Emu.cpp
new file mode 100644
index 000000000..9d9c3e6c2
--- /dev/null
+++ b/Frameworks/GME/gme/Okim6295_Emu.cpp
@@ -0,0 +1,77 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Okim6295_Emu.h"
+#include "okim6295.h"
+
+Okim6295_Emu::Okim6295_Emu() { chip = 0; }
+
+Okim6295_Emu::~Okim6295_Emu()
+{
+ if ( chip ) device_stop_okim6295( chip );
+}
+
+int Okim6295_Emu::set_rate( int clock_rate )
+{
+ if ( chip )
+ {
+ device_stop_okim6295( chip );
+ chip = 0;
+ }
+
+ chip = device_start_okim6295( clock_rate );
+ if ( !chip )
+ return 0;
+
+ reset();
+ return (clock_rate & 0x7FFFFFFF) / ((clock_rate & 0x80000000) ? 132 : 165);
+}
+
+void Okim6295_Emu::reset()
+{
+ device_reset_okim6295( chip );
+ okim6295_set_mute_mask( chip, 0 );
+}
+
+void Okim6295_Emu::write( int addr, int data )
+{
+ okim6295_w( chip, addr, data );
+}
+
+void Okim6295_Emu::write_rom( int size, int start, int length, void * data )
+{
+ okim6295_write_rom( chip, size, start, length, (const UINT8 *) data );
+}
+
+void Okim6295_Emu::mute_voices( int mask )
+{
+ okim6295_set_mute_mask( chip, mask );
+}
+
+void Okim6295_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ okim6295_update( chip, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/Okim6295_Emu.h b/Frameworks/GME/gme/Okim6295_Emu.h
new file mode 100644
index 000000000..3f307b085
--- /dev/null
+++ b/Frameworks/GME/gme/Okim6295_Emu.h
@@ -0,0 +1,36 @@
+// OKIM6295 sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef OKIM6295_EMU_H
+#define OKIM6295_EMU_H
+
+class Okim6295_Emu {
+ void* chip;
+public:
+ Okim6295_Emu();
+ ~Okim6295_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int clock_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 4 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Scales ROM size, then writes length bytes from data at start offset
+ void write_rom( int size, int start, int length, void * data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Opl_Apu.cpp b/Frameworks/GME/gme/Opl_Apu.cpp
new file mode 100644
index 000000000..0a3c45dd5
--- /dev/null
+++ b/Frameworks/GME/gme/Opl_Apu.cpp
@@ -0,0 +1,271 @@
+#include "Opl_Apu.h"
+
+#include "blargg_source.h"
+
+#include "ym2413.h"
+#include "fmopl.h"
+
+Opl_Apu::Opl_Apu() { opl = 0; opl_memory = 0; }
+
+blargg_err_t Opl_Apu::init( long clock, long rate, blip_time_t period, type_t type )
+{
+ type_ = type;
+ clock_ = clock;
+ rate_ = rate;
+ period_ = period;
+ set_output( 0, 0 );
+ volume( 1.0 );
+ switch (type)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ opl = ym2413_init( clock, rate, 0 );
+ break;
+
+ case type_vrc7:
+ opl = ym2413_init( clock, rate, 1 );
+ break;
+
+ case type_opl:
+ opl = ym3526_init( clock, rate );
+ break;
+
+ case type_msxaudio:
+ //logfile = fopen("c:\\temp\\msxaudio.log", "wb");
+ opl = y8950_init( clock, rate );
+ opl_memory = malloc( 32768 );
+ y8950_set_delta_t_memory( opl, opl_memory, 32768 );
+ break;
+
+ case type_opl2:
+ opl = ym3812_init( clock, rate );
+ break;
+ }
+ reset();
+ return 0;
+}
+
+Opl_Apu::~Opl_Apu()
+{
+ if (opl)
+ {
+ switch (type_)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ case type_vrc7:
+ ym2413_shutdown( opl );
+ break;
+
+ case type_opl:
+ ym3526_shutdown( opl );
+ break;
+
+ case type_msxaudio:
+ y8950_shutdown( opl );
+ free( opl_memory );
+ //fclose( logfile );
+ break;
+
+ case type_opl2:
+ ym3812_shutdown( opl );
+ break;
+ }
+ }
+}
+
+void Opl_Apu::reset()
+{
+ addr = 0;
+ next_time = 0;
+ last_amp = 0;
+
+ switch (type_)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ case type_vrc7:
+ ym2413_reset_chip( opl );
+ break;
+
+ case type_opl:
+ ym3526_reset_chip( opl );
+ break;
+
+ case type_msxaudio:
+ y8950_reset_chip( opl );
+ break;
+
+ case type_opl2:
+ ym3812_reset_chip( opl );
+ break;
+ }
+}
+
+void Opl_Apu::write_data( blip_time_t time, int data )
+{
+ run_until( time );
+ switch (type_)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ case type_vrc7:
+ ym2413_write( opl, 0, addr );
+ ym2413_write( opl, 1, data );
+ break;
+
+ case type_opl:
+ ym3526_write( opl, 0, addr );
+ ym3526_write( opl, 1, data );
+ break;
+
+ case type_msxaudio:
+ /*if ( addr >= 7 && addr <= 7 + 11 )
+ {
+ unsigned char temp [2] = { addr - 7, data };
+ fwrite( &temp, 1, 2, logfile );
+ }*/
+ y8950_write( opl, 0, addr );
+ y8950_write( opl, 1, data );
+ break;
+
+ case type_opl2:
+ ym3812_write( opl, 0, addr );
+ ym3812_write( opl, 1, data );
+ break;
+ }
+}
+
+int Opl_Apu::read( blip_time_t time, int port )
+{
+ run_until( time );
+ switch (type_)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ case type_vrc7:
+ return ym2413_read( opl, port );
+
+ case type_opl:
+ return ym3526_read( opl, port );
+
+ case type_msxaudio:
+ {
+ int ret = y8950_read( opl, port );
+ /*unsigned char temp [2] = { port + 0x80, ret };
+ fwrite( &temp, 1, 2, logfile );*/
+ return ret;
+ }
+
+ case type_opl2:
+ return ym3812_read( opl, port );
+ }
+
+ return 0;
+}
+
+void Opl_Apu::end_frame( blip_time_t time )
+{
+ run_until( time );
+ next_time -= time;
+
+ if ( output_ )
+ output_->set_modified();
+}
+
+void Opl_Apu::run_until( blip_time_t end_time )
+{
+ if ( end_time > next_time )
+ {
+ blip_time_t time_delta = end_time - next_time;
+ blip_time_t time = next_time;
+ unsigned count = time_delta / period_ + 1;
+ switch (type_)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ case type_vrc7:
+ {
+ SAMP bufMO[ 1024 ];
+ SAMP bufRO[ 1024 ];
+ SAMP * buffers[2] = { bufMO, bufRO };
+
+ while ( count > 0 )
+ {
+ unsigned todo = count;
+ if ( todo > 1024 ) todo = 1024;
+ ym2413_update_one( opl, buffers, todo );
+
+ if ( output_ )
+ {
+ int last_amp = this->last_amp;
+ for ( unsigned i = 0; i < todo; i++ )
+ {
+ int amp = bufMO [i] + bufRO [i];
+ int delta = amp - last_amp;
+ if ( delta )
+ {
+ last_amp = amp;
+ synth.offset_inline( time, delta, output_ );
+ }
+ time += period_;
+ }
+ this->last_amp = last_amp;
+ }
+ else time += period_ * todo;
+
+ count -= todo;
+ }
+ }
+ break;
+
+ case type_opl:
+ case type_msxaudio:
+ case type_opl2:
+ {
+ OPLSAMPLE buffer[ 1024 ];
+
+ while ( count > 0 )
+ {
+ unsigned todo = count;
+ if ( todo > 1024 ) todo = 1024;
+ switch (type_)
+ {
+ case type_opl: ym3526_update_one( opl, buffer, todo ); break;
+ case type_msxaudio: y8950_update_one( opl, buffer, todo ); break;
+ case type_opl2: ym3812_update_one( opl, buffer, todo ); break;
+ default: break;
+ }
+
+ if ( output_ )
+ {
+ int last_amp = this->last_amp;
+ for ( unsigned i = 0; i < todo; i++ )
+ {
+ int amp = buffer [i];
+ int delta = amp - last_amp;
+ if ( delta )
+ {
+ last_amp = amp;
+ synth.offset_inline( time, delta, output_ );
+ }
+ time += period_;
+ }
+ this->last_amp = last_amp;
+ }
+ else time += period_ * todo;
+
+ count -= todo;
+ }
+ }
+ break;
+ }
+ next_time = time;
+ }
+}
\ No newline at end of file
diff --git a/Frameworks/GME/gme/Opl_Apu.h b/Frameworks/GME/gme/Opl_Apu.h
new file mode 100644
index 000000000..86d73d748
--- /dev/null
+++ b/Frameworks/GME/gme/Opl_Apu.h
@@ -0,0 +1,63 @@
+#ifndef OPL_APU_H
+#define OPL_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+#include
+
+class Opl_Apu {
+public:
+ Opl_Apu();
+ ~Opl_Apu();
+
+ enum type_t { type_opll = 0x10, type_msxmusic = 0x11, type_smsfmunit = 0x12,
+ type_vrc7 = 0x13, type_opl = 0x20, type_msxaudio = 0x21, type_opl2 = 0x22 };
+ blargg_err_t init( long clock, long rate, blip_time_t period, type_t );
+
+ void reset();
+ void volume( double v ) { synth.volume( 1.0 / (4096 * 6) * v ); }
+ void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+ enum { osc_count = 1 };
+ void osc_output( int index, Blip_Buffer* );
+ void set_output( int i, Blip_Buffer* buf, Blip_Buffer* = NULL, Blip_Buffer* = NULL ) { osc_output( 0, buf ); }
+ void end_frame( blip_time_t );
+
+ void write_addr( int data ) { addr = data; }
+ void write_data( blip_time_t, int data );
+
+ int read( blip_time_t, int port );
+
+ static bool supported() { return true; }
+
+private:
+ // noncopyable
+ Opl_Apu( const Opl_Apu& );
+ Opl_Apu& operator = ( const Opl_Apu& );
+
+ Blip_Buffer* output_;
+ type_t type_;
+ void* opl;
+ void* opl_memory;
+ //FILE* logfile;
+ unsigned char regs[ 0x100 ];
+ blip_time_t next_time;
+ int last_amp;
+ int addr;
+
+ long clock_;
+ long rate_;
+ blip_time_t period_;
+
+ Blip_Synth_Fast synth;
+
+ void run_until( blip_time_t );
+};
+
+inline void Opl_Apu::osc_output( int i, Blip_Buffer* buf )
+{
+ assert( (unsigned) i < osc_count );
+ output_ = buf;
+}
+
+#endif
diff --git a/Frameworks/GME/gme/Pwm_Emu.cpp b/Frameworks/GME/gme/Pwm_Emu.cpp
new file mode 100644
index 000000000..7bb9722d1
--- /dev/null
+++ b/Frameworks/GME/gme/Pwm_Emu.cpp
@@ -0,0 +1,66 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Pwm_Emu.h"
+#include "pwm.h"
+
+Pwm_Emu::Pwm_Emu() { chip = 0; }
+
+Pwm_Emu::~Pwm_Emu()
+{
+ if ( chip ) device_stop_pwm( chip );
+}
+
+int Pwm_Emu::set_rate( int clock )
+{
+ if ( chip )
+ {
+ device_stop_pwm( chip );
+ chip = 0;
+ }
+
+ chip = device_start_pwm( clock );
+ if ( !chip )
+ return 1;
+
+ reset();
+ return 0;
+}
+
+void Pwm_Emu::reset()
+{
+ device_reset_pwm( chip );
+}
+
+void Pwm_Emu::write( int channel, int data )
+{
+ pwm_chn_w( chip, channel, data );
+}
+
+void Pwm_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ pwm_update( chip, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/Pwm_Emu.h b/Frameworks/GME/gme/Pwm_Emu.h
new file mode 100644
index 000000000..1ba1074cd
--- /dev/null
+++ b/Frameworks/GME/gme/Pwm_Emu.h
@@ -0,0 +1,33 @@
+// PWM sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef PWM_EMU_H
+#define PWM_EMU_H
+
+class Pwm_Emu {
+ void* chip;
+public:
+ Pwm_Emu();
+ ~Pwm_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int clock );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 24 };
+ void mute_voices( int mask );
+
+ // Writes data to channel
+ void write( int channel, int data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Qsound_Apu.cpp b/Frameworks/GME/gme/Qsound_Apu.cpp
new file mode 100644
index 000000000..8a530dc81
--- /dev/null
+++ b/Frameworks/GME/gme/Qsound_Apu.cpp
@@ -0,0 +1,83 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Qsound_Apu.h"
+#include "qmix.h"
+
+Qsound_Apu::Qsound_Apu() { chip = 0; rom = 0; rom_size = 0; sample_rate = 0; }
+
+Qsound_Apu::~Qsound_Apu()
+{
+ if ( chip ) free( chip );
+ if ( rom ) free( rom );
+}
+
+int Qsound_Apu::set_rate( int clock_rate )
+{
+ if ( chip )
+ {
+ free( chip );
+ chip = 0;
+ }
+
+ chip = malloc( _qmix_get_state_size() );
+ if ( !chip )
+ return 0;
+
+ reset();
+
+ return clock_rate / 166;
+}
+
+void Qsound_Apu::set_sample_rate( int sample_rate )
+{
+ this->sample_rate = sample_rate;
+ if ( chip ) _qmix_set_sample_rate( chip, sample_rate );
+}
+
+void Qsound_Apu::reset()
+{
+ _qmix_clear_state( chip );
+ _qmix_set_sample_rate( chip, sample_rate );
+ if ( rom ) _qmix_set_sample_rom( chip, rom, rom_size );
+}
+
+void Qsound_Apu::write( int addr, int data )
+{
+ _qmix_command( chip, addr, data );
+}
+
+void Qsound_Apu::write_rom( int size, int start, int length, void const* data )
+{
+ if ( size > rom_size )
+ {
+ rom_size = size;
+ rom = realloc( rom, size );
+ }
+ if ( start > size ) start = size;
+ if ( start + length > size ) length = size - start;
+ memcpy( (uint8*)rom + start, data, length );
+ if ( chip ) _qmix_set_sample_rom( chip, rom, rom_size );
+}
+
+void Qsound_Apu::run( int pair_count, sample_t* out )
+{
+ sint16 buf[ 1024 * 2 ];
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ _qmix_render( chip, buf, todo );
+
+ for (int i = 0; i < todo * 2; i++)
+ {
+ int output = buf [i];
+ output += out [0];
+ if ( (short)output != output ) output = 0x7FFF ^ ( output >> 31 );
+ out [0] = output;
+ out++;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/Qsound_Apu.h b/Frameworks/GME/gme/Qsound_Apu.h
new file mode 100644
index 000000000..8cbea22b1
--- /dev/null
+++ b/Frameworks/GME/gme/Qsound_Apu.h
@@ -0,0 +1,36 @@
+// Capcom QSound sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef QSOUND_APU_H
+#define QSOUND_APU_H
+
+class Qsound_Apu {
+ void* chip;
+ void* rom;
+ int rom_size;
+ int sample_rate;
+public:
+ Qsound_Apu();
+ ~Qsound_Apu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int clock_rate );
+ void set_sample_rate( int sample_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Scales ROM size, then writes length bytes from data at start offset
+ void write_rom( int size, int start, int length, void const* data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Resampler.cpp b/Frameworks/GME/gme/Resampler.cpp
new file mode 100644
index 000000000..16a0626b8
--- /dev/null
+++ b/Frameworks/GME/gme/Resampler.cpp
@@ -0,0 +1,79 @@
+// $package. http://www.slack.net/~ant/
+
+#include "Resampler.h"
+
+/* Copyright (C) 2004-2008 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"
+
+Resampler::Resampler()
+{
+ write_pos = 0;
+ rate_ = 0;
+}
+
+Resampler::~Resampler() { }
+
+void Resampler::clear()
+{
+ write_pos = 0;
+ clear_();
+}
+
+inline int Resampler::resample_wrapper( sample_t out [], int* out_size,
+ sample_t const in [], int in_size )
+{
+ assert( rate() );
+
+ sample_t* out_ = out;
+ int result = resample_( &out_, out + *out_size, in, in_size ) - in;
+ assert( out_ <= out + *out_size );
+ assert( result <= in_size );
+
+ *out_size = out_ - out;
+ return result;
+}
+
+int Resampler::resample( sample_t out [], int out_size, sample_t const in [], int* in_size )
+{
+ *in_size = resample_wrapper( out, &out_size, in, *in_size );
+ return out_size;
+}
+
+
+//// Buffering
+
+blargg_err_t Resampler::resize_buffer( int new_size )
+{
+ RETURN_ERR( buf.resize( new_size ) );
+ clear();
+ return blargg_ok;
+}
+
+int Resampler::skip_input( int count )
+{
+ write_pos -= count;
+ if ( write_pos < 0 ) // occurs when downsampling
+ {
+ count += write_pos;
+ write_pos = 0;
+ }
+ memmove( buf.begin(), &buf [count], write_pos * sizeof buf [0] );
+ return count;
+}
+
+int Resampler::read( sample_t out [], int out_size )
+{
+ if ( out_size )
+ skip_input( resample_wrapper( out, &out_size, buf.begin(), write_pos ) );
+ return out_size;
+}
diff --git a/Frameworks/GME/gme/Resampler.h b/Frameworks/GME/gme/Resampler.h
new file mode 100644
index 000000000..ff285121d
--- /dev/null
+++ b/Frameworks/GME/gme/Resampler.h
@@ -0,0 +1,110 @@
+// Common interface for resamplers
+
+// $package
+#ifndef RESAMPLER_H
+#define RESAMPLER_H
+
+#include "blargg_common.h"
+
+class Resampler {
+public:
+
+ virtual ~Resampler();
+
+ // Sets input/output resampling ratio
+ blargg_err_t set_rate( double );
+
+ // Current input/output ratio
+ double rate() const { return rate_; }
+
+ // Samples are 16-bit signed
+ typedef short sample_t;
+
+// One of two different buffering schemes can be used, as decided by the caller:
+
+// External buffering (caller provides input buffer)
+
+ // Resamples in to at most n out samples and returns number of samples actually
+ // written. Sets *in_size to number of input samples that aren't needed anymore
+ // and should be removed from input.
+ int resample( sample_t out [], int n, sample_t const in [], int* in_size );
+
+// Internal buffering (resampler manages buffer)
+
+ // Resizes input buffer to n samples, then clears it
+ blargg_err_t resize_buffer( int n );
+
+ // Clears input buffer
+ void clear();
+
+ // Writes at most n samples to input buffer and returns number actually written.
+ // Result will be less than n if there isn't enough free space in buffer.
+ int write( sample_t const in [], int n );
+
+ // Number of input samples in buffer
+ int written() const { return write_pos; }
+
+ // Removes first n input samples from buffer, fewer if there aren't that many.
+ // Returns number of samples actually removed.
+ int skip_input( int n );
+
+ // Resamples input to at most n output samples. Returns number of samples
+ // actually written to out. Result will be less than n if there aren't
+ // enough input samples in buffer.
+ int read( sample_t out [], int n );
+
+// Direct writing to input buffer, instead of using write( in, n ) above
+
+ // Pointer to place to write input samples
+ sample_t* buffer() { return &buf [write_pos]; }
+
+ // Number of samples that can be written to buffer()
+ int buffer_free() const { return buf.size() - write_pos; }
+
+ // Notifies resampler that n input samples have been written to buffer().
+ // N must not be greater than buffer_free().
+ void write( int n );
+
+// Derived interface
+protected:
+ virtual blargg_err_t set_rate_( double rate ) BLARGG_PURE( ; )
+
+ virtual void clear_() { }
+
+ // Resample as many available in samples as will fit within out_size and
+ // return pointer past last input sample read and set *out just past
+ // the last output sample.
+ virtual sample_t const* resample_( sample_t** out, sample_t const* out_end,
+ sample_t const in [], int in_size ) BLARGG_PURE( { return in; } )
+
+// Implementation
+public:
+ Resampler();
+
+private:
+ blargg_vector buf;
+ int write_pos;
+ double rate_;
+
+ int resample_wrapper( sample_t out [], int* out_size,
+ sample_t const in [], int in_size );
+};
+
+inline void Resampler::write( int count )
+{
+ write_pos += count;
+ assert( (unsigned) write_pos <= buf.size() );
+}
+
+inline blargg_err_t Resampler::set_rate_( double r )
+{
+ rate_ = r;
+ return blargg_ok;
+}
+
+inline blargg_err_t Resampler::set_rate( double r )
+{
+ return set_rate_( r );
+}
+
+#endif
diff --git a/Frameworks/GME/gme/Rf5C164_Emu.cpp b/Frameworks/GME/gme/Rf5C164_Emu.cpp
new file mode 100644
index 000000000..605729c6d
--- /dev/null
+++ b/Frameworks/GME/gme/Rf5C164_Emu.cpp
@@ -0,0 +1,82 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Rf5C164_Emu.h"
+#include "scd_pcm.h"
+
+Rf5C164_Emu::Rf5C164_Emu() { chip = 0; }
+
+Rf5C164_Emu::~Rf5C164_Emu()
+{
+ if ( chip ) device_stop_rf5c164( chip );
+}
+
+int Rf5C164_Emu::set_rate( int clock )
+{
+ if ( chip )
+ {
+ device_stop_rf5c164( chip );
+ chip = 0;
+ }
+
+ chip = device_start_rf5c164( clock );
+ if ( !chip )
+ return 1;
+
+ reset();
+ return 0;
+}
+
+void Rf5C164_Emu::reset()
+{
+ device_reset_rf5c164( chip );
+ rf5c164_set_mute_mask( chip, 0 );
+}
+
+void Rf5C164_Emu::write( int addr, int data )
+{
+ rf5c164_w( chip, addr, data );
+}
+
+void Rf5C164_Emu::write_mem( int addr, int data )
+{
+ rf5c164_mem_w( chip, addr, data );
+}
+
+void Rf5C164_Emu::write_ram( int start, int length, void * data )
+{
+ rf5c164_write_ram( chip, start, length, (const UINT8 *) data );
+}
+
+void Rf5C164_Emu::mute_voices( int mask )
+{
+ rf5c164_set_mute_mask( chip, mask );
+}
+
+void Rf5C164_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ rf5c164_update( chip, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/Rf5C164_Emu.h b/Frameworks/GME/gme/Rf5C164_Emu.h
new file mode 100644
index 000000000..25abf8dac
--- /dev/null
+++ b/Frameworks/GME/gme/Rf5C164_Emu.h
@@ -0,0 +1,39 @@
+// RF5C164 sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef RF5C164_EMU_H
+#define RF5C164_EMU_H
+
+class Rf5C164_Emu {
+ void* chip;
+public:
+ Rf5C164_Emu();
+ ~Rf5C164_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int clock );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 8 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Writes to memory
+ void write_mem( int addr, int data );
+
+ // Writes length bytes from data at start offset in RAM
+ void write_ram( int start, int length, void * data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Rf5C68_Emu.cpp b/Frameworks/GME/gme/Rf5C68_Emu.cpp
new file mode 100644
index 000000000..46d452438
--- /dev/null
+++ b/Frameworks/GME/gme/Rf5C68_Emu.cpp
@@ -0,0 +1,82 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Rf5C68_Emu.h"
+#include "rf5c68.h"
+
+Rf5C68_Emu::Rf5C68_Emu() { chip = 0; }
+
+Rf5C68_Emu::~Rf5C68_Emu()
+{
+ if ( chip ) device_stop_rf5c68( chip );
+}
+
+int Rf5C68_Emu::set_rate()
+{
+ if ( chip )
+ {
+ device_stop_rf5c68( chip );
+ chip = 0;
+ }
+
+ chip = device_start_rf5c68();
+ if ( !chip )
+ return 1;
+
+ reset();
+ return 0;
+}
+
+void Rf5C68_Emu::reset()
+{
+ device_reset_rf5c68( chip );
+ rf5c68_set_mute_mask( chip, 0 );
+}
+
+void Rf5C68_Emu::write( int addr, int data )
+{
+ rf5c68_w( chip, addr, data );
+}
+
+void Rf5C68_Emu::write_mem( int addr, int data )
+{
+ rf5c68_mem_w( chip, addr, data );
+}
+
+void Rf5C68_Emu::write_ram( int start, int length, void * data )
+{
+ rf5c68_write_ram( chip, start, length, (const UINT8 *) data );
+}
+
+void Rf5C68_Emu::mute_voices( int mask )
+{
+ rf5c68_set_mute_mask( chip, mask );
+}
+
+void Rf5C68_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ rf5c68_update( chip, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/Rf5C68_Emu.h b/Frameworks/GME/gme/Rf5C68_Emu.h
new file mode 100644
index 000000000..e03332d9e
--- /dev/null
+++ b/Frameworks/GME/gme/Rf5C68_Emu.h
@@ -0,0 +1,39 @@
+// RF5C68 sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef RF5C68_EMU_H
+#define RF5C68_EMU_H
+
+class Rf5C68_Emu {
+ void* chip;
+public:
+ Rf5C68_Emu();
+ ~Rf5C68_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate();
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 8 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Writes to memory
+ void write_mem( int addr, int data );
+
+ // Writes length bytes from data at start offset in RAM
+ void write_ram( int start, int length, void * data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Rom_Data.cpp b/Frameworks/GME/gme/Rom_Data.cpp
new file mode 100644
index 000000000..0fa5f2d2c
--- /dev/null
+++ b/Frameworks/GME/gme/Rom_Data.cpp
@@ -0,0 +1,99 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Rom_Data.h"
+
+/* Copyright (C) 2003-2009 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 Rom_Data::clear()
+{
+ file_size_ = 0;
+ rom_addr = 0;
+ mask = 0;
+ rom.clear();
+}
+
+Rom_Data::Rom_Data( int page_size ) :
+ pad_size( page_size + pad_extra )
+{
+ // page_size should be power of 2
+ check( (page_size & (page_size - 1)) == 0 );
+
+ clear();
+}
+
+Rom_Data::~Rom_Data()
+{ }
+
+// Reads file into array, placing file_offset bytes of padding before the beginning, and pad_size after the end
+blargg_err_t Rom_Data::load_( Data_Reader& in, int header_size, int file_offset )
+{
+ clear();
+ file_size_ = in.remain();
+ if ( file_size_ <= header_size ) // <= because there must be data after header
+ return blargg_err_file_type;
+
+ RETURN_ERR( rom.resize( file_offset + file_size_ + pad_size ) );
+
+ return in.read( rom.begin() + file_offset, file_size_ );
+}
+
+blargg_err_t Rom_Data::load( Data_Reader& in, int header_size,
+ void* header_out, int fill )
+{
+ int file_offset = pad_size - header_size;
+ blargg_err_t err = load_( in, header_size, file_offset );
+ if ( err )
+ {
+ clear();
+ return err;
+ }
+
+ file_size_ -= header_size;
+ memcpy( header_out, &rom [file_offset], header_size );
+
+ memset( rom.begin() , fill, pad_size );
+ memset( rom.end() - pad_size, fill, pad_size );
+
+ return blargg_ok;
+}
+
+void Rom_Data::set_addr( int addr )
+{
+ int const page_size = pad_size - pad_extra;
+
+ // Minimum size that contains all bytes and is a multiple of page_size
+ int const size = (addr + file_size_ + page_size - 1) / page_size * page_size;
+
+ // Find lowest power of 2 that is >= size
+ int power2 = 1;
+ while ( power2 < size )
+ power2 *= 2;
+
+ mask = power2 - 1;
+
+ // Address of first byte of ROM (possibly negative)
+ rom_addr = addr - page_size - pad_extra;
+
+ if ( rom.resize( size - rom_addr + pad_extra ) ) { } // OK if shrink fails
+}
+
+byte* Rom_Data::at_addr( int addr )
+{
+ int offset = mask_addr( addr ) - rom_addr;
+
+ if ( (unsigned) offset > (unsigned) (rom.size() - pad_size) )
+ offset = 0; // unmapped
+
+ return &rom [offset];
+}
diff --git a/Frameworks/GME/gme/Rom_Data.h b/Frameworks/GME/gme/Rom_Data.h
new file mode 100644
index 000000000..7b39dfaf9
--- /dev/null
+++ b/Frameworks/GME/gme/Rom_Data.h
@@ -0,0 +1,94 @@
+// Manages ROM data loaded from file in an efficient manner
+
+// Game_Music_Emu $vers
+#ifndef ROM_DATA_H
+#define ROM_DATA_H
+
+#include "blargg_common.h"
+#include "Data_Reader.h"
+
+/* Loads a ROM file into memory and allows access to it in page-sized chunks.
+
+* ROM file consists of header followed by ROM data. Instead of storing the entire
+ROM contents, the file only stores the occupied portion, with the bytes before and
+after that cleared to some value. The size and format of the header is up to the
+caller, as is the starting address of the ROM data following it. File loading is
+performed with a single read, rather than two or more that might otherwise be
+required.
+
+* Once ROM data is loaded and its address specified, a pointer to any "page" can
+be obtained. ROM data is mirrored using smallest power of 2 that contains it.
+Addresses not aligned to pages can also be used, but this might cause unexpected
+results.
+
+Example with file data of size 0x0C put at address 0x0F, with page size of 8:
+
+---------------0123456789AB--------------------0123456789AB---------...
+^ ^ ^ ^ ^ ^ ^ ^ ^
+0 0x08 0x10 0x18 0x20 0x28 0x30 0x38 0x40
+
+at_addr(0x00) = pointer to 8 bytes of fill.
+at_addr(0x08) = pointer to 7 bytes of fill, followed by first byte of file.
+at_addr(0x10) = pointer to next 8 bytes of file.
+at_addr(0x18) = pointer to last 3 bytes of file, followed by 5 bytes of fill.
+at_addr(0x20) = pointer to 8 bytes of fill.
+at_addr(0x28) = pointer to 7 bytes of fill, followed by first byte of file.
+etc. */
+
+class Rom_Data {
+ enum { pad_extra = 8 };
+public:
+ typedef unsigned char byte;
+
+ // Page_size should be a power of 2
+ Rom_Data( int page_size );
+
+ // Loads file into memory, then copies header to *header_out and fills
+ // unmapped bank and file data padding with fill. Returns blargg_err_file_type
+ // if in.remain() <= header_size.
+ blargg_err_t load( Data_Reader& in, int header_size, void* header_out, int fill );
+
+ // Below, "file data" refers to data AFTER the header
+
+ // Size of file data
+ int file_size() const { return file_size_; }
+
+ // Pointer to beginning of file data
+ byte * begin() { return rom.begin() + pad_size; }
+ byte const* begin() const { return rom.begin() + pad_size; }
+
+ // Pointer to unmapped page cleared with fill value
+ byte* unmapped() { return rom.begin(); }
+
+ // Sets address that file data will start at. Must be set before using following
+ // functions, and cannot be set more than once.
+ void set_addr( int addr );
+
+ // Address of first empty page (file size + addr rounded up to multiple of page_size)
+ int size() const { return rom.size() - pad_extra + rom_addr; }
+
+ // Masks address to nearest power of two greater than size()
+ int mask_addr( int addr ) const { return addr & mask; }
+
+ // Pointer to page beginning at addr, or unmapped() if outside data.
+ // Mirrored using mask_addr().
+ byte* at_addr( int addr );
+
+ // Frees memory
+ void clear();
+
+// Implementation
+public:
+ ~Rom_Data();
+
+protected:
+ blargg_vector rom;
+ int mask;
+ int rom_addr;
+ int const pad_size;
+ int file_size_;
+
+ blargg_err_t load_( Data_Reader& in, int header_size, int file_offset );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Sap_Core.cpp b/Frameworks/GME/gme/Sap_Core.cpp
new file mode 100644
index 000000000..daaaf4ddc
--- /dev/null
+++ b/Frameworks/GME/gme/Sap_Core.cpp
@@ -0,0 +1,192 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Sap_Core.h"
+
+/* Copyright (C) 2006-2008 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"
+
+int const idle_addr = 0xD2D2;
+
+Sap_Core::Sap_Core()
+{
+ set_tempo( 1 );
+}
+
+void Sap_Core::push( int b )
+{
+ mem.ram [0x100 + cpu.r.sp--] = (byte) b;
+}
+
+void Sap_Core::jsr_then_stop( addr_t addr )
+{
+ cpu.r.pc = addr;
+
+ // Some rips pop three bytes off stack before RTS.
+ push( (idle_addr - 1) >> 8 );
+ push( idle_addr - 1 );
+
+ // 3 bytes so that RTI or RTS will jump to idle_addr.
+ // RTI will use the first two bytes as the address, 0xD2D2.
+ // RTS will use the last two bytes, 0xD2D1, which it internally increments.
+ push( (idle_addr - 1) >> 8 );
+ push( (idle_addr - 1) >> 8 );
+ push( idle_addr - 1 );
+}
+
+// Runs routine and allows it up to one second to return
+void Sap_Core::run_routine( addr_t addr )
+{
+ jsr_then_stop( addr );
+ run_cpu( lines_per_frame * base_scanline_period * 60 );
+ check( cpu.r.pc == idle_addr );
+ check( cpu.r.sp >= 0xFF - 6 );
+}
+
+inline void Sap_Core::call_init( int track )
+{
+ cpu.r.a = track;
+
+ switch ( info.type )
+ {
+ case 'B':
+ run_routine( info.init_addr );
+ break;
+
+ case 'C':
+ cpu.r.a = 0x70;
+ cpu.r.x = info.music_addr&0xFF;
+ cpu.r.y = info.music_addr >> 8;
+ run_routine( info.play_addr + 3 );
+ cpu.r.a = 0;
+ cpu.r.x = track;
+ run_routine( info.play_addr + 3 );
+ break;
+
+ case 'D':
+ check( info.fastplay == lines_per_frame );
+ jsr_then_stop( info.init_addr );
+ break;
+ }
+}
+
+void Sap_Core::setup_ram()
+{
+ memset( &mem, 0, sizeof mem );
+
+ ram() [idle_addr] = cpu.halt_opcode;
+
+ addr_t const irq_addr = idle_addr - 1;
+ ram() [irq_addr] = cpu.halt_opcode;
+ ram() [0xFFFE] = (byte) irq_addr;
+ ram() [0xFFFF] = irq_addr >> 8;
+}
+
+blargg_err_t Sap_Core::start_track( int track, info_t const& new_info )
+{
+ info = new_info;
+
+ check( ram() [idle_addr] == cpu.halt_opcode );
+
+ apu_ .reset( &apu_impl_ );
+ apu2_.reset( &apu_impl_ );
+
+ cpu.reset( ram() );
+
+ frame_start = 0;
+ next_play = play_period() * 4;
+ saved_state.pc = idle_addr;
+
+ time_mask = 0; // disables sound during init
+ call_init( track );
+ time_mask = ~0;
+
+ return blargg_ok;
+}
+
+blargg_err_t Sap_Core::run_until( time_t end )
+{
+ while ( cpu.time() < end )
+ {
+ time_t next = min( next_play, end );
+ if ( (run_cpu( next ) && cpu.r.pc != idle_addr) || cpu.error_count() )
+ // TODO: better error
+ return BLARGG_ERR( BLARGG_ERR_GENERIC, "Emulation error (illegal instruction)" );
+
+ if ( cpu.r.pc == idle_addr )
+ {
+ if ( saved_state.pc == idle_addr )
+ {
+ // no code to run until next play call
+ cpu.set_time( next );
+ }
+ else
+ {
+ // play had interrupted non-returning init, so restore registers
+ // init routine was running
+ check( cpu.r.sp == saved_state.sp - 3 );
+ cpu.r = saved_state;
+ saved_state.pc = idle_addr;
+ }
+ }
+
+ if ( cpu.time() >= next_play )
+ {
+ next_play += play_period();
+
+ if ( cpu.r.pc == idle_addr || info.type == 'D' )
+ {
+ // Save state if init routine is still running
+ if ( cpu.r.pc != idle_addr )
+ {
+ check( info.type == 'D' );
+ check( saved_state.pc == idle_addr );
+ saved_state = cpu.r;
+ }
+
+ addr_t addr = info.play_addr;
+ if ( info.type == 'C' )
+ addr += 6;
+ jsr_then_stop( addr );
+ }
+ else
+ {
+ dprintf( "init/play hadn't returned before next play call\n" );
+ }
+ }
+ }
+ return blargg_ok;
+}
+
+blargg_err_t Sap_Core::end_frame( time_t end )
+{
+ RETURN_ERR( run_until( end ) );
+
+ cpu.adjust_time( -end );
+
+ time_t frame_time = lines_per_frame * scanline_period;
+ while ( frame_start < end )
+ frame_start += frame_time;
+ frame_start -= end + frame_time;
+
+ if ( (next_play -= end) < 0 )
+ {
+ next_play = 0;
+ check( false );
+ }
+
+ apu_.end_frame( end );
+ if ( info.stereo )
+ apu2_.end_frame( end );
+
+ return blargg_ok;
+}
diff --git a/Frameworks/GME/gme/Sap_Core.h b/Frameworks/GME/gme/Sap_Core.h
new file mode 100644
index 000000000..e5703575a
--- /dev/null
+++ b/Frameworks/GME/gme/Sap_Core.h
@@ -0,0 +1,91 @@
+// Atari XL/XE SAP core CPU and RAM emulator
+
+// Game_Music_Emu $vers
+#ifndef SAP_CORE_H
+#define SAP_CORE_H
+
+#include "Sap_Apu.h"
+#include "Nes_Cpu.h"
+
+class Sap_Core {
+public:
+
+ // Sound chips and common state
+ Sap_Apu& apu() { return apu_; }
+ Sap_Apu& apu2() { return apu2_; }
+ Sap_Apu_Impl& apu_impl() { return apu_impl_; }
+
+ // Adjusts music tempo, where 1.0 is normal. Can be changed while playing.
+ void set_tempo( double );
+
+ // Clears RAM and sets up default vectors, etc.
+ void setup_ram();
+
+ // 64K RAM to load file data blocks into
+ BOOST::uint8_t* ram() { return mem.ram; }
+
+ // Calls init routine and configures playback. RAM must have been
+ // set up already.
+ struct info_t {
+ int init_addr;
+ int play_addr;
+ int music_addr;
+ int type;
+ int fastplay;
+ bool stereo;
+ };
+ blargg_err_t start_track( int track, info_t const& );
+
+ // Ends time frame at time t, then begins new at time 0
+ typedef Nes_Cpu::time_t time_t; // Clock count
+ blargg_err_t end_frame( time_t t );
+
+
+// Implementation
+public:
+ Sap_Core();
+
+private:
+ enum { base_scanline_period = 114 };
+ enum { lines_per_frame = 312 };
+ typedef Nes_Cpu::addr_t addr_t;
+
+ time_t scanline_period;
+ time_t next_play;
+ time_t time_mask;
+ time_t frame_start;
+ Nes_Cpu cpu;
+ Nes_Cpu::registers_t saved_state;
+ info_t info;
+ Sap_Apu apu_;
+ Sap_Apu apu2_;
+
+ // large items
+ struct {
+ BOOST::uint8_t padding1 [ 0x100];
+ BOOST::uint8_t ram [0x10000];
+ BOOST::uint8_t padding2 [ 0x100];
+ } mem; // TODO: put on freestore
+ Sap_Apu_Impl apu_impl_;
+
+ void push( int b );
+ void jsr_then_stop( addr_t );
+ void run_routine( addr_t );
+ void call_init( int track );
+ bool run_cpu( time_t end );
+ int play_addr();
+ int read_d40b();
+ int read_mem( addr_t );
+ void write_D2xx( int d2xx, int data );
+
+ time_t time() const { return cpu.time() & time_mask; }
+ blargg_err_t run_until( time_t t );
+ time_t play_period() const { return info.fastplay * scanline_period; }
+};
+
+inline void Sap_Core::set_tempo( double t )
+{
+ scanline_period = (int) (base_scanline_period / t + 0.5);
+}
+
+#endif
diff --git a/Frameworks/GME/gme/SegaPcm_Emu.cpp b/Frameworks/GME/gme/SegaPcm_Emu.cpp
new file mode 100644
index 000000000..8c18898dc
--- /dev/null
+++ b/Frameworks/GME/gme/SegaPcm_Emu.cpp
@@ -0,0 +1,77 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "SegaPcm_Emu.h"
+#include "segapcm.h"
+
+SegaPcm_Emu::SegaPcm_Emu() { chip = 0; }
+
+SegaPcm_Emu::~SegaPcm_Emu()
+{
+ if ( chip ) device_stop_segapcm( chip );
+}
+
+int SegaPcm_Emu::set_rate( int intf_type )
+{
+ if ( chip )
+ {
+ device_stop_segapcm( chip );
+ chip = 0;
+ }
+
+ chip = device_start_segapcm( intf_type );
+ if ( !chip )
+ return 1;
+
+ reset();
+ return 0;
+}
+
+void SegaPcm_Emu::reset()
+{
+ device_reset_segapcm( chip );
+ segapcm_set_mute_mask( chip, 0 );
+}
+
+void SegaPcm_Emu::write( int addr, int data )
+{
+ sega_pcm_w( chip, addr, data );
+}
+
+void SegaPcm_Emu::write_rom( int size, int start, int length, void * data )
+{
+ sega_pcm_write_rom( chip, size, start, length, (const UINT8 *) data );
+}
+
+void SegaPcm_Emu::mute_voices( int mask )
+{
+ segapcm_set_mute_mask( chip, mask );
+}
+
+void SegaPcm_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ SEGAPCM_update( chip, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/SegaPcm_Emu.h b/Frameworks/GME/gme/SegaPcm_Emu.h
new file mode 100644
index 000000000..ee2722b42
--- /dev/null
+++ b/Frameworks/GME/gme/SegaPcm_Emu.h
@@ -0,0 +1,36 @@
+// Sega PCM sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef SEGAPCM_EMU_H
+#define SEGAPCM_EMU_H
+
+class SegaPcm_Emu {
+ void* chip;
+public:
+ SegaPcm_Emu();
+ ~SegaPcm_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int intf_type );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 16 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Scales ROM size, then writes length bytes from data at start offset
+ void write_rom( int size, int start, int length, void * data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Sgc_Core.cpp b/Frameworks/GME/gme/Sgc_Core.cpp
new file mode 100644
index 000000000..bcb5f3a57
--- /dev/null
+++ b/Frameworks/GME/gme/Sgc_Core.cpp
@@ -0,0 +1,108 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Sgc_Core.h"
+
+/* Copyright (C) 2009 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 Sgc_Core::set_tempo( double t )
+{
+ set_play_period( clock_rate() / (header().rate ? 50 : 60) / t );
+}
+
+blargg_err_t Sgc_Core::load_( Data_Reader& dr )
+{
+ RETURN_ERR( Sgc_Impl::load_( dr ) );
+
+ if ( sega_mapping() && fm_apu_.supported() )
+ RETURN_ERR( fm_apu_.init( clock_rate(), clock_rate() / 72 ) );
+
+ set_tempo( 1.0 );
+ return blargg_ok;
+}
+
+blargg_err_t Sgc_Core::start_track( int t )
+{
+ if ( sega_mapping() )
+ {
+ apu_.reset();
+ fm_apu_.reset();
+ fm_accessed = false;
+ }
+ else
+ {
+ apu_.reset( 0x0003, 15 );
+ }
+
+ return Sgc_Impl::start_track( t );
+}
+
+blargg_err_t Sgc_Core::end_frame( time_t t )
+{
+ RETURN_ERR( Sgc_Impl::end_frame( t ) );
+ apu_.end_frame( t );
+ if ( sega_mapping() && fm_accessed )
+ {
+ if ( fm_apu_.supported() )
+ fm_apu_.end_frame( t );
+ else
+ set_warning( "FM sound not supported" );
+ }
+
+ return blargg_ok;
+}
+
+Sgc_Core::Sgc_Core()
+{ }
+
+Sgc_Core::~Sgc_Core()
+{ }
+
+void Sgc_Core::cpu_out( time_t time, addr_t addr, int data )
+{
+ int port = addr & 0xFF;
+
+ if ( sega_mapping() )
+ {
+ switch ( port )
+ {
+ case 0x06:
+ apu_.write_ggstereo( time, data );
+ return;
+
+ case 0x7E:
+ case 0x7F:
+ apu_.write_data( time, data ); dprintf( "$7E<-%02X\n", data );
+ return;
+
+ case 0xF0:
+ fm_accessed = true;
+ if ( fm_apu_.supported() )
+ fm_apu_.write_addr( data );//, dprintf( "$F0<-%02X\n", data );
+ return;
+
+ case 0xF1:
+ fm_accessed = true;
+ if ( fm_apu_.supported() )
+ fm_apu_.write_data( time, data );//, dprintf( "$F1<-%02X\n", data );
+ return;
+ }
+ }
+ else if ( port >= 0xE0 )
+ {
+ apu_.write_data( time, data );
+ return;
+ }
+
+ Sgc_Impl::cpu_out( time, addr, data );
+}
diff --git a/Frameworks/GME/gme/Sgc_Core.h b/Frameworks/GME/gme/Sgc_Core.h
new file mode 100644
index 000000000..23c11cbd3
--- /dev/null
+++ b/Frameworks/GME/gme/Sgc_Core.h
@@ -0,0 +1,44 @@
+// Sega/Game Gear/Coleco SGC music file emulator core
+
+// Game_Music_Emu $vers
+#ifndef SGC_CORE_H
+#define SGC_CORE_H
+
+#include "Sgc_Impl.h"
+#include "Sms_Fm_Apu.h"
+#include "Sms_Apu.h"
+
+class Sgc_Core : public Sgc_Impl {
+public:
+
+ // Adjusts music tempo, where 1.0 is normal. Can be changed while playing.
+ // Resets to 1.0 when loading file.
+ void set_tempo( double );
+
+ // Starts track, where 0 is the first.
+ blargg_err_t start_track( int );
+
+ // Ends time frame at time t
+ blargg_err_t end_frame( time_t t );
+
+ // SN76489 sound chip
+ Sms_Apu& apu() { return apu_; }
+ Sms_Fm_Apu& fm_apu() { return fm_apu_; }
+
+protected:
+ // Overrides
+ virtual void cpu_out( time_t, addr_t, int data );
+ virtual blargg_err_t load_( Data_Reader& );
+
+// Implementation
+public:
+ Sgc_Core();
+ ~Sgc_Core();
+
+private:
+ bool fm_accessed;
+ Sms_Apu apu_;
+ Sms_Fm_Apu fm_apu_;
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Sgc_Cpu.cpp b/Frameworks/GME/gme/Sgc_Cpu.cpp
new file mode 100644
index 000000000..b63e42b83
--- /dev/null
+++ b/Frameworks/GME/gme/Sgc_Cpu.cpp
@@ -0,0 +1,36 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Sgc_Impl.h"
+
+#include "blargg_endian.h"
+//#include "z80_cpu_log.h"
+
+/* Copyright (C) 2009 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"
+
+#define OUT_PORT( addr, data ) cpu_out( TIME(), addr, data )
+#define IN_PORT( addr ) cpu_in( addr )
+#define WRITE_MEM( addr, data ) cpu_write( addr, data )
+#define IDLE_ADDR idle_addr
+#define CPU cpu
+#define RST_BASE vectors_addr
+
+#define CPU_BEGIN \
+bool Sgc_Impl::run_cpu( time_t end_time )\
+{\
+ cpu.set_end_time( end_time );
+
+ #include "Z80_Cpu_run.h"
+
+ return warning;
+}
diff --git a/Frameworks/GME/gme/Sgc_Emu.cpp b/Frameworks/GME/gme/Sgc_Emu.cpp
new file mode 100644
index 000000000..010ee71d6
--- /dev/null
+++ b/Frameworks/GME/gme/Sgc_Emu.cpp
@@ -0,0 +1,167 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Sgc_Emu.h"
+
+/* Copyright (C) 2009 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"
+
+int const osc_count = Sms_Apu::osc_count + Sms_Fm_Apu::osc_count;
+
+Sgc_Emu::Sgc_Emu()
+{
+ set_type( gme_sgc_type );
+ set_silence_lookahead( 6 );
+ set_gain( 1.2 );
+}
+
+Sgc_Emu::~Sgc_Emu() { }
+
+void Sgc_Emu::unload()
+{
+ core_.unload();
+ Music_Emu::unload();
+}
+
+// Track info
+
+static void copy_sgc_fields( Sgc_Emu::header_t const& h, track_info_t* out )
+{
+ GME_COPY_FIELD( h, out, game );
+ GME_COPY_FIELD( h, out, author );
+ GME_COPY_FIELD( h, out, copyright );
+}
+
+static void hash_sgc_file( Sgc_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out )
+{
+ out.hash_( &h.vers, sizeof(h.vers) );
+ out.hash_( &h.rate, sizeof(h.rate) );
+ out.hash_( &h.reserved1[0], sizeof(h.reserved1) );
+ out.hash_( &h.load_addr[0], sizeof(h.load_addr) );
+ out.hash_( &h.init_addr[0], sizeof(h.init_addr) );
+ out.hash_( &h.play_addr[0], sizeof(h.play_addr) );
+ out.hash_( &h.stack_ptr[0], sizeof(h.stack_ptr) );
+ out.hash_( &h.reserved2[0], sizeof(h.reserved2) );
+ out.hash_( &h.rst_addrs[0], sizeof(h.rst_addrs) );
+ out.hash_( &h.mapping[0], sizeof(h.mapping) );
+ out.hash_( &h.first_song, sizeof(h.first_song) );
+ out.hash_( &h.song_count, sizeof(h.song_count) );
+ out.hash_( &h.first_effect, sizeof(h.first_effect) );
+ out.hash_( &h.last_effect, sizeof(h.last_effect) );
+ out.hash_( &h.system, sizeof(h.system) );
+ out.hash_( &h.reserved3[0], sizeof(h.reserved3) );
+ out.hash_( data, data_size );
+}
+
+blargg_err_t Sgc_Emu::track_info_( track_info_t* out, int ) const
+{
+ copy_sgc_fields( header(), out );
+ return blargg_ok;
+}
+
+struct Sgc_File : Gme_Info_
+{
+ Sgc_Emu::header_t const* h;
+
+ Sgc_File() { set_type( gme_sgc_type ); }
+
+ blargg_err_t load_mem_( byte const begin [], int size )
+ {
+ h = ( Sgc_Emu::header_t const* ) begin;
+
+ set_track_count( h->song_count );
+ if ( !h->valid_tag() )
+ return blargg_err_file_type;
+
+ return blargg_ok;
+ }
+
+ blargg_err_t track_info_( track_info_t* out, int ) const
+ {
+ copy_sgc_fields( *h, out );
+ return blargg_ok;
+ }
+
+ blargg_err_t hash_( Hash_Function& out ) const
+ {
+ hash_sgc_file( *h, file_begin() + h->size, file_end() - file_begin() - h->size, out );
+ return blargg_ok;
+ }
+};
+
+static Music_Emu* new_sgc_emu () { return BLARGG_NEW Sgc_Emu ; }
+static Music_Emu* new_sgc_file() { return BLARGG_NEW Sgc_File; }
+
+gme_type_t_ const gme_sgc_type [1] = {{ "Z80 PSG", 0, &new_sgc_emu, &new_sgc_file, "SGC", 1 }};
+
+// Setup
+
+blargg_err_t Sgc_Emu::load_( Data_Reader& in )
+{
+ RETURN_ERR( core_.load( in ) );
+ set_warning( core_.warning() );
+ set_track_count( header().song_count );
+ set_voice_count( core_.sega_mapping() ? osc_count : core_.apu().osc_count );
+
+ core_.apu ().volume( gain() );
+ core_.fm_apu().volume( gain() );
+
+ static const char* const names [osc_count + 1] = {
+ "Square 1", "Square 2", "Square 3", "Noise", "FM"
+ };
+ set_voice_names( names );
+
+ static int const types [osc_count + 1] = {
+ wave_type+1, wave_type+2, wave_type+3, mixed_type+1, mixed_type+2
+ };
+ set_voice_types( types );
+
+ return setup_buffer( core_.clock_rate() );
+}
+
+void Sgc_Emu::update_eq( blip_eq_t const& eq )
+{
+ core_.apu ().treble_eq( eq );
+ core_.fm_apu().treble_eq( eq );
+}
+
+void Sgc_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+ if ( i < core_.apu().osc_count )
+ core_.apu().set_output( i, c, l, r );
+ else
+ core_.fm_apu().set_output( c, l, r );
+}
+
+void Sgc_Emu::set_tempo_( double t )
+{
+ core_.set_tempo( t );
+}
+
+blargg_err_t Sgc_Emu::start_track_( int track )
+{
+ RETURN_ERR( core_.start_track( track ) );
+ return Classic_Emu::start_track_( track );
+}
+
+blargg_err_t Sgc_Emu::run_clocks( blip_time_t& duration, int )
+{
+ RETURN_ERR( core_.end_frame( duration ) );
+ set_warning( core_.warning() );
+ return blargg_ok;
+}
+
+blargg_err_t Sgc_Emu::hash_( Hash_Function& out ) const
+{
+ hash_sgc_file( header(), core_.rom_().begin(), core_.rom_().file_size(), out );
+ return blargg_ok;
+}
\ No newline at end of file
diff --git a/Frameworks/GME/gme/Sgc_Emu.h b/Frameworks/GME/gme/Sgc_Emu.h
new file mode 100644
index 000000000..112f950d7
--- /dev/null
+++ b/Frameworks/GME/gme/Sgc_Emu.h
@@ -0,0 +1,45 @@
+// Sega/Game Gear/Coleco SGC music file emulator
+
+// Game_Music_Emu $vers
+#ifndef SGC_EMU_H
+#define SGC_EMU_H
+
+#include "Classic_Emu.h"
+#include "Sgc_Core.h"
+
+class Sgc_Emu : public Classic_Emu {
+public:
+ // SGC file header (see Sgc_Impl.h)
+ typedef Sgc_Core::header_t header_t;
+
+ // Header for currently loaded file
+ header_t const& header() const { return core_.header(); }
+
+ blargg_err_t hash_( Hash_Function& ) const;
+
+ // Sets 0x2000-byte Coleco BIOS. Necessary to play Coleco tracks.
+ static void set_coleco_bios( void const* p ){ Sgc_Core::set_coleco_bios( p ); }
+
+ static gme_type_t static_type() { return gme_sgc_type; }
+
+// Internal
+public:
+ Sgc_Emu();
+ ~Sgc_Emu();
+
+protected:
+ // Classic_Emu overrides
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t load_( Data_Reader& );
+ virtual blargg_err_t start_track_( int );
+ virtual blargg_err_t run_clocks( blip_time_t&, int );
+ virtual void set_tempo_( double );
+ virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+ virtual void update_eq( blip_eq_t const& );
+ virtual void unload();
+
+private:
+ Sgc_Core core_;
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Sgc_Impl.cpp b/Frameworks/GME/gme/Sgc_Impl.cpp
new file mode 100644
index 000000000..497966ab8
--- /dev/null
+++ b/Frameworks/GME/gme/Sgc_Impl.cpp
@@ -0,0 +1,225 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Sgc_Impl.h"
+
+/* Copyright (C) 2006-2009 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 const* Sgc_Impl::coleco_bios;
+
+Sgc_Impl::Sgc_Impl() :
+ rom( bank_size )
+{
+ assert( offsetof (header_t,copyright [32]) == header_t::size );
+}
+
+Sgc_Impl::~Sgc_Impl()
+{ }
+
+bool Sgc_Impl::header_t::valid_tag() const
+{
+ return 0 == memcmp( tag, "SGC\x1A", 4 );
+}
+
+blargg_err_t Sgc_Impl::load_( Data_Reader& in )
+{
+ RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) );
+
+ if ( !header_.valid_tag() )
+ return blargg_err_file_type;
+
+ if ( header_.vers != 1 )
+ set_warning( "Unknown file version" );
+
+ if ( header_.system > 2 )
+ set_warning( "Unknown system" );
+
+ addr_t load_addr = get_le16( header_.load_addr );
+ if ( load_addr < 0x400 )
+ set_warning( "Invalid load address" );
+
+ rom.set_addr( load_addr );
+ play_period = clock_rate() / 60;
+
+ if ( sega_mapping() )
+ {
+ RETURN_ERR( ram.resize( 0x2000 + Sgc_Cpu::page_padding ) );
+ RETURN_ERR( ram2.resize( bank_size + Sgc_Cpu::page_padding ) );
+ }
+ else
+ {
+ RETURN_ERR( ram.resize( 0x400 + Sgc_Cpu::page_padding ) );
+ }
+
+ RETURN_ERR( vectors.resize( Sgc_Cpu::page_size + Sgc_Cpu::page_padding ) );
+
+ // TODO: doesn't need to be larger than page size, if we do mapping calls right
+ RETURN_ERR( unmapped_write.resize( bank_size ) );
+
+ return blargg_ok;
+}
+
+void Sgc_Impl::unload()
+{
+ rom.clear();
+ vectors.clear();
+ ram.clear();
+ ram2.clear();
+ unmapped_write.clear();
+ Gme_Loader::unload();
+}
+
+blargg_err_t Sgc_Impl::start_track( int track )
+{
+ memset( ram .begin(), 0, ram .size() );
+ memset( ram2.begin(), 0, ram2.size() );
+ memset( vectors.begin(), 0xFF, vectors.size() );
+ cpu.reset( unmapped_write.begin(), rom.unmapped() );
+
+ if ( sega_mapping() )
+ {
+ vectors_addr = 0x10000 - Sgc_Cpu::page_size;
+ idle_addr = vectors_addr;
+ for ( int i = 1; i < 8; ++i )
+ {
+ vectors [i*8 + 0] = 0xC3; // JP addr
+ vectors [i*8 + 1] = header_.rst_addrs [i*2 + 0];
+ vectors [i*8 + 2] = header_.rst_addrs [i*2 + 1];
+ }
+
+ cpu.map_mem( 0xC000, 0x2000, ram.begin() );
+ cpu.map_mem( vectors_addr, cpu.page_size, unmapped_write.begin(), vectors.begin() );
+
+ bank2 = NULL;
+ for ( int i = 0; i < 4; ++i )
+ cpu_write( 0xFFFC + i, header_.mapping [i] );
+ }
+ else
+ {
+ if ( !coleco_bios )
+ return BLARGG_ERR( BLARGG_ERR_CALLER, "Coleco BIOS not set" );
+
+ vectors_addr = 0;
+ cpu.map_mem( 0, 0x2000, unmapped_write.begin(), coleco_bios );
+ for ( int i = 0; i < 8; ++i )
+ cpu.map_mem( 0x6000 + i*0x400, 0x400, ram.begin() );
+
+ idle_addr = 0x2000;
+ cpu.map_mem( 0x2000, cpu.page_size, unmapped_write.begin(), vectors.begin() );
+
+ for ( int i = 0; i < 0x8000 / bank_size; ++i )
+ {
+ int addr = 0x8000 + i*bank_size;
+ cpu.map_mem( addr, bank_size, unmapped_write.begin(), rom.at_addr( addr ) );
+ }
+ }
+
+ cpu.r.sp = get_le16( header_.stack_ptr );
+ cpu.r.b.a = track;
+ next_play = play_period;
+
+ jsr( header_.init_addr );
+
+ return blargg_ok;
+}
+
+// Emulation
+
+void Sgc_Impl::jsr( byte const (&addr) [2] )
+{
+ *cpu.write( --cpu.r.sp ) = idle_addr >> 8;
+ *cpu.write( --cpu.r.sp ) = idle_addr & 0xFF;
+ cpu.r.pc = get_le16( addr );
+}
+
+void Sgc_Impl::set_bank( int bank, void const* data )
+{
+ //dprintf( "map bank %d to %p\n", bank, (byte*) data - rom.at_addr( 0 ) );
+ cpu.map_mem( bank * bank_size, bank_size, unmapped_write.begin(), data );
+}
+
+void Sgc_Impl::cpu_write( addr_t addr, int data )
+{
+ if ( (addr ^ 0xFFFC) > 3 || !sega_mapping() )
+ {
+ *cpu.write( addr ) = data;
+ return;
+ }
+
+ switch ( addr )
+ {
+ case 0xFFFC:
+ cpu.map_mem( 2 * bank_size, bank_size, ram2.begin() );
+ if ( data & 0x08 )
+ break;
+
+ bank2 = ram2.begin();
+ // FALL THROUGH
+
+ case 0xFFFF: {
+ bool rom_mapped = (cpu.read( 2 * bank_size ) == bank2);
+ bank2 = rom.at_addr( data * bank_size );
+ if ( rom_mapped )
+ set_bank( 2, bank2 );
+ break;
+ }
+
+ case 0xFFFD:
+ set_bank( 0, rom.at_addr( data * bank_size ) );
+ break;
+
+ case 0xFFFE:
+ set_bank( 1, rom.at_addr( data * bank_size ) );
+ break;
+ }
+}
+
+int Sgc_Impl::cpu_in( addr_t addr )
+{
+ dprintf( "in %02X\n", addr );
+ return 0;
+}
+
+void Sgc_Impl::cpu_out( time_t, addr_t addr, int )
+{
+ dprintf( "out %02X\n", addr & 0xFF );
+}
+
+blargg_err_t Sgc_Impl::end_frame( time_t end )
+{
+ while ( cpu.time() < end )
+ {
+ time_t next = min( end, next_play );
+ if ( run_cpu( next ) )
+ {
+ set_warning( "Unsupported CPU instruction" );
+ cpu.set_time( next );
+ }
+
+ if ( cpu.r.pc == idle_addr )
+ cpu.set_time( next );
+
+ if ( cpu.time() >= next_play )
+ {
+ next_play += play_period;
+ if ( cpu.r.pc == idle_addr )
+ jsr( header_.play_addr );
+ }
+ }
+
+ next_play -= end;
+ check( next_play >= 0 );
+ cpu.adjust_time( -end );
+
+ return blargg_ok;
+}
diff --git a/Frameworks/GME/gme/Sgc_Impl.h b/Frameworks/GME/gme/Sgc_Impl.h
new file mode 100644
index 000000000..e9b873c53
--- /dev/null
+++ b/Frameworks/GME/gme/Sgc_Impl.h
@@ -0,0 +1,116 @@
+// Sega/Game Gear/Coleco SGC music file emulator implementation internals
+
+// Game_Music_Emu $vers
+#ifndef SGC_IMPL_H
+#define SGC_IMPL_H
+
+#include "Gme_Loader.h"
+#include "Rom_Data.h"
+#include "Z80_Cpu.h"
+
+class Sgc_Impl : public Gme_Loader {
+public:
+
+ // SGC file header
+ struct header_t
+ {
+ enum { size = 0xA0 };
+
+ char tag [4]; // "SGC\x1A"
+ byte vers; // 0x01
+ byte rate; // 0=NTSC 1=PAL
+ byte reserved1 [2];
+ byte load_addr [2];
+ byte init_addr [2];
+ byte play_addr [2];
+ byte stack_ptr [2];
+ byte reserved2 [2];
+ byte rst_addrs [7*2];
+ byte mapping [4]; // Used by Sega only
+ byte first_song; // Song to start playing first
+ byte song_count;
+ byte first_effect;
+ byte last_effect;
+ byte system; // 0=Master System 1=Game Gear 2=Colecovision
+ byte reserved3 [23];
+ char game [32]; // strings can be 32 chars, NOT terminated
+ char author [32];
+ char copyright [32];
+
+ // True if header has valid file signature
+ bool valid_tag() const;
+
+ int effect_count() const { return last_effect ? last_effect - first_effect + 1 : 0; }
+ };
+
+ // Header for currently loaded file
+ header_t const& header() const { return header_; }
+
+ Rom_Data const& rom_() const { return rom; }
+
+ int clock_rate() const { return header_.rate ? 3546893 : 3579545; }
+
+ // 0x2000 bytes
+ static void set_coleco_bios( void const* p ) { coleco_bios = p; }
+
+ // Clocks between calls to play routine
+ typedef int time_t;
+ void set_play_period( time_t p ) { play_period = p; }
+
+ // 0 = first track
+ blargg_err_t start_track( int );
+
+ // Runs for t clocks
+ blargg_err_t end_frame( time_t t );
+
+ // True if Master System or Game Gear
+ bool sega_mapping() const;
+
+protected:
+ typedef Z80_Cpu Sgc_Cpu;
+ Sgc_Cpu cpu;
+
+ typedef int addr_t;
+ virtual void cpu_out( time_t, addr_t, int data ) BLARGG_PURE( ; )
+
+// Implementation
+public:
+ Sgc_Impl();
+ ~Sgc_Impl();
+ virtual void unload();
+
+protected:
+ virtual blargg_err_t load_( Data_Reader& );
+
+private:
+ enum { bank_size = 0x4000 };
+
+ Rom_Data rom;
+ time_t play_period;
+ time_t next_play;
+ void const* bank2; // ROM selected for bank 2, in case RAM is currently hiding it
+ addr_t vectors_addr; // RST vectors start here
+ addr_t idle_addr; // return address for init/play routines
+ static void const* coleco_bios;
+
+ // large items
+ header_t header_;
+ blargg_vector vectors;
+ blargg_vector ram;
+ blargg_vector ram2;
+ blargg_vector unmapped_write;
+
+ bool run_cpu( time_t end );
+ void jsr( byte const (&addr) [2] );
+ void cpu_write( addr_t, int data );
+ int cpu_in( addr_t );
+
+ void set_bank( int bank, void const* data );
+};
+
+inline bool Sgc_Impl::sega_mapping() const
+{
+ return header_.system <= 1;
+}
+
+#endif
diff --git a/Frameworks/GME/gme/Sms_Fm_Apu.cpp b/Frameworks/GME/gme/Sms_Fm_Apu.cpp
new file mode 100644
index 000000000..9cd7e5518
--- /dev/null
+++ b/Frameworks/GME/gme/Sms_Fm_Apu.cpp
@@ -0,0 +1,80 @@
+#include "Sms_Fm_Apu.h"
+
+#include "blargg_source.h"
+
+Sms_Fm_Apu::Sms_Fm_Apu()
+{ }
+
+Sms_Fm_Apu::~Sms_Fm_Apu()
+{ }
+
+blargg_err_t Sms_Fm_Apu::init( double clock_rate, double sample_rate )
+{
+ period_ = clock_rate / sample_rate + 0.5;
+ CHECK_ALLOC( !apu.set_rate( sample_rate, clock_rate ) );
+
+ set_output( 0 );
+ volume( 1.0 );
+ reset();
+ return blargg_ok;
+}
+
+void Sms_Fm_Apu::reset()
+{
+ addr = 0;
+ next_time = 0;
+ last_amp = 0;
+
+ apu.reset();
+}
+
+void Sms_Fm_Apu::write_data( blip_time_t time, int data )
+{
+ if ( time > next_time )
+ run_until( time );
+
+ apu.write( addr, data );
+}
+
+void Sms_Fm_Apu::run_until( blip_time_t end_time )
+{
+ assert( end_time > next_time );
+
+ Blip_Buffer* const output = this->output_;
+ if ( !output )
+ {
+ next_time = end_time;
+ return;
+ }
+
+ blip_time_t time = next_time;
+ do
+ {
+ Ym2413_Emu::sample_t samples [2] = {0};
+ apu.run( 1, samples );
+ int amp = (samples [0] + samples [1]) >> 1;
+
+ int delta = amp - last_amp;
+ if ( delta )
+ {
+ last_amp = amp;
+ synth.offset_inline( time, delta, output );
+ }
+ time += period_;
+ }
+ while ( time < end_time );
+
+ next_time = time;
+}
+
+void Sms_Fm_Apu::end_frame( blip_time_t time )
+{
+ if ( time > next_time )
+ run_until( time );
+
+ next_time -= time;
+ assert( next_time >= 0 );
+
+ if ( output_ )
+ output_->set_modified();
+}
diff --git a/Frameworks/GME/gme/Sms_Fm_Apu.h b/Frameworks/GME/gme/Sms_Fm_Apu.h
new file mode 100644
index 000000000..5fc2ea1ee
--- /dev/null
+++ b/Frameworks/GME/gme/Sms_Fm_Apu.h
@@ -0,0 +1,47 @@
+#ifndef SMS_FM_APU_H
+#define SMS_FM_APU_H
+
+#include "Blip_Buffer.h"
+#include "Ym2413_Emu.h"
+
+class Sms_Fm_Apu {
+public:
+ static bool supported() { return Ym2413_Emu::supported(); }
+ blargg_err_t init( double clock_rate, double sample_rate );
+
+ void set_output( Blip_Buffer* b, Blip_Buffer* = NULL, Blip_Buffer* = NULL ) { output_ = b; }
+ void volume( double v ) { synth.volume( 0.4 / 4096 * v ); }
+ void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+
+ void reset();
+
+ void write_addr( int data ) { addr = data; }
+ void write_data( blip_time_t, int data );
+
+ void end_frame( blip_time_t t );
+
+// Implementation
+public:
+ Sms_Fm_Apu();
+ ~Sms_Fm_Apu();
+ BLARGG_DISABLE_NOTHROW
+ enum { osc_count = 1 };
+ void set_output( int i, Blip_Buffer* b, Blip_Buffer* = NULL, Blip_Buffer* = NULL ) { output_ = b; }
+
+private:
+ Blip_Buffer* output_;
+ blip_time_t next_time;
+ int last_amp;
+ int addr;
+
+ int clock_;
+ int rate_;
+ blip_time_t period_;
+
+ Blip_Synth_Norm synth;
+ Ym2413_Emu apu;
+
+ void run_until( blip_time_t );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Spc_Filter.cpp b/Frameworks/GME/gme/Spc_Filter.cpp
new file mode 100644
index 000000000..746af8ccd
--- /dev/null
+++ b/Frameworks/GME/gme/Spc_Filter.cpp
@@ -0,0 +1,81 @@
+// snes_spc $vers. http://www.slack.net/~ant/
+
+#include "Spc_Filter.h"
+
+/* Copyright (C) 2007 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 Spc_Filter::clear() { memset( ch, 0, sizeof ch ); }
+
+Spc_Filter::Spc_Filter()
+{
+ enabled = true;
+ gain = gain_unit;
+ bass = bass_norm;
+ clear();
+}
+
+void Spc_Filter::run( short io [], int count )
+{
+ require( (count & 1) == 0 ); // must be even
+
+ int const gain = this->gain;
+ if ( enabled )
+ {
+ int const bass = this->bass;
+ chan_t* c = &ch [2];
+ do
+ {
+ // cache in registers
+ int sum = (--c)->sum;
+ int pp1 = c->pp1;
+ int p1 = c->p1;
+
+ for ( int i = 0; i < count; i += 2 )
+ {
+ // Low-pass filter (two point FIR with coeffs 0.25, 0.75)
+ int f = io [i] + p1;
+ p1 = io [i] * 3;
+
+ // High-pass filter ("leaky integrator")
+ int delta = f - pp1;
+ pp1 = f;
+ int s = sum >> (gain_bits + 2);
+ sum += (delta * gain) - (sum >> bass);
+
+ // Clamp to 16 bits
+ if ( (short) s != s )
+ s = (s >> 31) ^ 0x7FFF;
+
+ io [i] = (short) s;
+ }
+
+ c->p1 = p1;
+ c->pp1 = pp1;
+ c->sum = sum;
+ ++io;
+ }
+ while ( c != ch );
+ }
+ else if ( gain != gain_unit )
+ {
+ short* const end = io + count;
+ while ( io < end )
+ {
+ int s = (*io * gain) >> gain_bits;
+ if ( (short) s != s )
+ s = (s >> 31) ^ 0x7FFF;
+ *io++ = (short) s;
+ }
+ }
+}
diff --git a/Frameworks/GME/gme/Spc_Filter.h b/Frameworks/GME/gme/Spc_Filter.h
new file mode 100644
index 000000000..06758c971
--- /dev/null
+++ b/Frameworks/GME/gme/Spc_Filter.h
@@ -0,0 +1,53 @@
+// Simple low-pass and high-pass filter to better match sound output of a SNES
+
+// snes_spc $vers
+#ifndef SPC_FILTER_H
+#define SPC_FILTER_H
+
+#include "blargg_common.h"
+
+struct Spc_Filter {
+public:
+
+ // Filters count samples of stereo sound in place. Count must be a multiple of 2.
+ typedef short sample_t;
+ void run( sample_t io [], int count );
+
+// Optional features
+
+ // Clears filter to silence
+ void clear();
+
+ // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit
+ // are fine, since output is clamped to 16-bit sample range.
+ enum { gain_unit = 0x100 };
+ void set_gain( int gain );
+
+ // Enables/disables filtering (when disabled, gain is still applied)
+ void enable( bool b );
+
+ // Sets amount of bass (logarithmic scale)
+ enum { bass_none = 0 };
+ enum { bass_norm = 8 }; // normal amount
+ enum { bass_max = 31 };
+ void set_bass( int bass );
+
+public:
+ Spc_Filter();
+ BLARGG_DISABLE_NOTHROW
+private:
+ enum { gain_bits = 8 };
+ int gain;
+ int bass;
+ bool enabled;
+ struct chan_t { int p1, pp1, sum; };
+ chan_t ch [2];
+};
+
+inline void Spc_Filter::enable( bool b ) { enabled = b; }
+
+inline void Spc_Filter::set_gain( int g ) { gain = g; }
+
+inline void Spc_Filter::set_bass( int b ) { bass = b; }
+
+#endif
diff --git a/Frameworks/GME/gme/Spc_Sfm.cpp b/Frameworks/GME/gme/Spc_Sfm.cpp
new file mode 100644
index 000000000..91ce894ac
--- /dev/null
+++ b/Frameworks/GME/gme/Spc_Sfm.cpp
@@ -0,0 +1,430 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Spc_Sfm.h"
+
+#include "blargg_endian.h"
+
+#include
+
+/* Copyright (C) 2004-2013 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"
+
+// TODO: support Spc_Filter's bass
+
+Sfm_Emu::Sfm_Emu()
+{
+ set_type( gme_sfm_type );
+ set_gain( 1.4 );
+}
+
+Sfm_Emu::~Sfm_Emu() { }
+
+// Track info
+
+static void hash_sfm_file( byte const* data, int data_size, Music_Emu::Hash_Function& out )
+{
+ out.hash_( data, data_size );
+}
+
+blargg_err_t Sfm_Emu::track_info_( track_info_t* out, int ) const
+{
+ const char * title = metadata.enumValue("information:title");
+ if (title) strncpy( out->song, title, 255 );
+ else out->song[0] = 0;
+ out->song[255] = '\0';
+ return blargg_ok;
+}
+
+static blargg_err_t check_sfm_header( void const* header )
+{
+ if ( memcmp( header, "SFM1", 4 ) )
+ return blargg_err_file_type;
+ return blargg_ok;
+}
+
+struct Sfm_File : Gme_Info_
+{
+ blargg_vector data;
+ Bml_Parser metadata;
+
+ Sfm_File() { set_type( gme_sfm_type ); }
+
+ blargg_err_t load_( Data_Reader& in )
+ {
+ int file_size = in.remain();
+ if ( file_size < Sfm_Emu::sfm_min_file_size )
+ return blargg_err_file_type;
+ RETURN_ERR( data.resize( file_size ) );
+ RETURN_ERR( in.read( data.begin(), data.end() - data.begin() ) );
+ RETURN_ERR( check_sfm_header( data.begin() ) );
+ int metadata_size = get_le32( data.begin() + 4 );
+ byte temp = data[ 8 + metadata_size ];
+ data[ 8 + metadata_size ] = '\0';
+ metadata.parseDocument( (const char *)data.begin() + 8 );
+ data[ 8 + metadata_size ] = temp;
+ return blargg_ok;
+ }
+
+ blargg_err_t track_info_( track_info_t* out, int ) const
+ {
+ const char * title = metadata.enumValue("information:title");
+ if (title) strncpy( out->song, title, 255 );
+ else out->song[0] = 0;
+ out->song[255] = '\0';
+ return blargg_ok;
+ }
+
+ blargg_err_t hash_( Hash_Function& out ) const
+ {
+ hash_sfm_file( data.begin(), data.end() - data.begin(), out );
+ return blargg_ok;
+ }
+};
+
+static Music_Emu* new_sfm_emu () { return BLARGG_NEW Sfm_Emu ; }
+static Music_Emu* new_sfm_file() { return BLARGG_NEW Sfm_File; }
+
+gme_type_t_ const gme_sfm_type [1] = {{ "Super Nintendo with log", 1, &new_sfm_emu, &new_sfm_file, "SFM", 0 }};
+
+// Setup
+
+blargg_err_t Sfm_Emu::set_sample_rate_( int sample_rate )
+{
+ RETURN_ERR( apu.init() );
+ if ( sample_rate != native_sample_rate )
+ {
+ RETURN_ERR( resampler.resize_buffer( native_sample_rate / 20 * 2 ) );
+ RETURN_ERR( resampler.set_rate( (double) native_sample_rate / sample_rate ) ); // 0.9965 rolloff
+ }
+ return blargg_ok;
+}
+
+void Sfm_Emu::mute_voices_( int m )
+{
+ Music_Emu::mute_voices_( m );
+ apu.mute_voices( m );
+}
+
+blargg_err_t Sfm_Emu::load_mem_( byte const in [], int size )
+{
+ set_voice_count( Spc_Dsp::voice_count );
+ if ( size < Sfm_Emu::sfm_min_file_size )
+ return blargg_err_file_type;
+
+ static const char* const names [Spc_Dsp::voice_count] = {
+ "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8"
+ };
+ set_voice_names( names );
+
+ return check_sfm_header( in );
+}
+
+// Emulation
+
+void Sfm_Emu::set_tempo_( double t )
+{
+ apu.set_tempo( (int) (t * Snes_Spc::tempo_unit) );
+}
+
+// (n ? n : 256)
+#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
+
+#define META_ENUM_INT(n) (value = metadata.enumValue(n), value ? strtoul(value, &end, 10) : 0)
+
+static const byte ipl_rom[0x40] =
+{
+ 0xCD, 0xEF, 0xBD, 0xE8, 0x00, 0xC6, 0x1D, 0xD0,
+ 0xFC, 0x8F, 0xAA, 0xF4, 0x8F, 0xBB, 0xF5, 0x78,
+ 0xCC, 0xF4, 0xD0, 0xFB, 0x2F, 0x19, 0xEB, 0xF4,
+ 0xD0, 0xFC, 0x7E, 0xF4, 0xD0, 0x0B, 0xE4, 0xF5,
+ 0xCB, 0xF4, 0xD7, 0x00, 0xFC, 0xD0, 0xF3, 0xAB,
+ 0x01, 0x10, 0xEF, 0x7E, 0xF4, 0x10, 0xEB, 0xBA,
+ 0xF6, 0xDA, 0x00, 0xBA, 0xF4, 0xC4, 0xF4, 0xDD,
+ 0x5D, 0xD0, 0xDB, 0x1F, 0x00, 0x00, 0xC0, 0xFF
+};
+
+blargg_err_t Sfm_Emu::start_track_( int track )
+{
+ RETURN_ERR( Music_Emu::start_track_( track ) );
+ resampler.clear();
+ filter.clear();
+ const byte * ptr = file_begin();
+ int metadata_size = get_le32(ptr + 4);
+ if ( file_size() < metadata_size + Sfm_Emu::sfm_min_file_size )
+ return "SFM file too small";
+ char * temp = new char[metadata_size + 1];
+ temp[metadata_size] = '\0';
+ memcpy(temp, ptr + 8, metadata_size);
+ metadata.parseDocument(temp);
+ delete [] temp;
+
+ apu.init_rom( ipl_rom );
+
+ apu.reset();
+
+ memcpy( apu.m.ram.ram, ptr + 8 + metadata_size, 65536 );
+
+ memcpy( apu.dsp.m.regs, ptr + 8 + metadata_size + 65536, 128 );
+
+ apu.set_sfm_queue( ptr + 8 + metadata_size + 65536 + 128, ptr + file_size() );
+
+ byte regs[Snes_Spc::reg_count] = {0};
+
+ char * end;
+ const char * value;
+
+ regs[Snes_Spc::r_test] = META_ENUM_INT("smp:test");
+ regs[Snes_Spc::r_control] |= META_ENUM_INT("smp:iplrom") ? 0x80 : 0;
+ regs[Snes_Spc::r_dspaddr] = META_ENUM_INT("smp:dspaddr");
+
+ value = metadata.enumValue("smp:ram");
+ if (value)
+ {
+ regs[Snes_Spc::r_f8] = strtoul(value, &end, 10);
+ if (*end)
+ {
+ value = end + 1;
+ regs[Snes_Spc::r_f9] = strtoul(value, &end, 10);
+ }
+ }
+
+ char temp_path[256];
+ for (int i = 0; i < 3; ++i)
+ {
+ sprintf(temp_path, "smp:timer[%u]:", i);
+ size_t length = strlen(temp_path);
+ strcpy(temp_path + length, "enable");
+ value = metadata.enumValue(temp_path);
+ if (value)
+ {
+ regs[Snes_Spc::r_control] |= strtoul(value, &end, 10) ? 1 << i : 0;
+ }
+ strcpy(temp_path + length, "target");
+ value = metadata.enumValue(temp_path);
+ if (value)
+ {
+ regs[Snes_Spc::r_t0target + i] = strtoul(value, &end, 10);
+ }
+ strcpy(temp_path + length, "stage");
+ value = metadata.enumValue(temp_path);
+ if (value)
+ {
+ for (int j = 0; j < 3; ++j)
+ {
+ if (value) value = strchr(value, ',');
+ if (value) ++value;
+ }
+ if (value)
+ {
+ regs[Snes_Spc::r_t0out + i] = strtoul(value, &end, 10);
+ }
+ }
+ }
+
+ apu.load_regs( regs );
+ apu.m.rom_enabled = 0;
+ apu.regs_loaded();
+
+ for (int i = 0; i < 3; ++i)
+ {
+ sprintf(temp_path, "smp:timer[%u]:", i);
+ size_t length = strlen(temp_path);
+ strcpy(temp_path + length, "stage");
+ value = metadata.enumValue(temp_path);
+ if (value)
+ {
+ const char * stage = value;
+ apu.m.timers[i].next_time = strtoul(stage, &end, 10) + 1;
+ for (int j = 0; j < 2; ++j)
+ {
+ if (stage) stage = strchr(stage, ',');
+ if (stage) ++stage;
+ }
+ if (stage)
+ {
+ apu.m.timers[i].divider = strtoul(value, &end, 10);
+ }
+ }
+ }
+
+ apu.dsp.m.echo_hist_pos = &apu.dsp.m.echo_hist[META_ENUM_INT("dsp:echohistaddr")];
+
+ value = metadata.enumValue("dsp:echohistdata");
+ if (value)
+ {
+ for (int i = 0; i < 8; ++i)
+ {
+ apu.dsp.m.echo_hist[i][0] = strtoul(value, &end, 10);
+ value = strchr(value, ',');
+ if (!value) break;
+ ++value;
+ apu.dsp.m.echo_hist[i][1] = strtoul(value, &end, 10);
+ value = strchr(value, ',');
+ if (!value) break;
+ ++value;
+ }
+ }
+
+ apu.dsp.m.phase = META_ENUM_INT("dsp:sample");
+ apu.dsp.m.kon = META_ENUM_INT("dsp:kon");
+ apu.dsp.m.noise = META_ENUM_INT("dsp:noise");
+ apu.dsp.m.counter = META_ENUM_INT("dsp:counter");
+ apu.dsp.m.echo_offset = META_ENUM_INT("dsp:echooffset");
+ apu.dsp.m.echo_length = META_ENUM_INT("dsp:echolength");
+ apu.dsp.m.new_kon = META_ENUM_INT("dsp:koncache");
+ apu.dsp.m.endx_buf = META_ENUM_INT("dsp:endx");
+ apu.dsp.m.envx_buf = META_ENUM_INT("dsp:envx");
+ apu.dsp.m.outx_buf = META_ENUM_INT("dsp:outx");
+ apu.dsp.m.t_pmon = META_ENUM_INT("dsp:pmon");
+ apu.dsp.m.t_non = META_ENUM_INT("dsp:non");
+ apu.dsp.m.t_eon = META_ENUM_INT("dsp:eon");
+ apu.dsp.m.t_dir = META_ENUM_INT("dsp:dir");
+ apu.dsp.m.t_koff = META_ENUM_INT("dsp:koff");
+ apu.dsp.m.t_brr_next_addr = META_ENUM_INT("dsp:brrnext");
+ apu.dsp.m.t_adsr0 = META_ENUM_INT("dsp:adsr0");
+ apu.dsp.m.t_brr_header = META_ENUM_INT("dsp:brrheader");
+ apu.dsp.m.t_brr_byte = META_ENUM_INT("dsp:brrdata");
+ apu.dsp.m.t_srcn = META_ENUM_INT("dsp:srcn");
+ apu.dsp.m.t_esa = META_ENUM_INT("dsp:esa");
+ apu.dsp.m.t_echo_enabled = !META_ENUM_INT("dsp:echodisable");
+ apu.dsp.m.t_dir_addr = META_ENUM_INT("dsp:diraddr");
+ apu.dsp.m.t_pitch = META_ENUM_INT("dsp:pitch");
+ apu.dsp.m.t_output = META_ENUM_INT("dsp:output");
+ apu.dsp.m.t_looped = META_ENUM_INT("dsp:looped");
+ apu.dsp.m.t_echo_ptr = META_ENUM_INT("dsp:echoaddr");
+
+
+#define META_ENUM_LEVELS(n, o) \
+ value = metadata.enumValue(n); \
+ if (value) \
+ { \
+ (o)[0] = strtoul(value, &end, 10); \
+ if (*end) \
+ { \
+ value = end + 1; \
+ (o)[1] = strtoul(value, &end, 10); \
+ } \
+ }
+
+ META_ENUM_LEVELS("dsp:mainout", apu.dsp.m.t_main_out);
+ META_ENUM_LEVELS("dsp:echoout", apu.dsp.m.t_echo_out);
+ META_ENUM_LEVELS("dsp:echoin", apu.dsp.m.t_echo_in);
+
+#undef META_ENUM_LEVELS
+
+ for (int i = 0; i < 8; ++i)
+ {
+ sprintf(temp_path, "dsp:voice[%u]:", i);
+ size_t length = strlen(temp_path);
+ Spc_Dsp::voice_t & voice = apu.dsp.m.voices[i];
+ strcpy(temp_path + length, "brrhistaddr");
+ value = metadata.enumValue(temp_path);
+ if (value)
+ {
+ voice.buf_pos = strtoul(value, &end, 10);
+ }
+ strcpy(temp_path + length, "brrhistdata");
+ value = metadata.enumValue(temp_path);
+ if (value)
+ {
+ for (int j = 0; j < Spc_Dsp::brr_buf_size; ++j)
+ {
+ voice.buf[j] = voice.buf[j + Spc_Dsp::brr_buf_size] = strtoul(value, &end, 10);
+ if (!*end) break;
+ value = end + 1;
+ }
+ }
+ strcpy(temp_path + length, "interpaddr");
+ voice.interp_pos = META_ENUM_INT(temp_path);
+ strcpy(temp_path + length, "brraddr");
+ voice.brr_addr = META_ENUM_INT(temp_path);
+ strcpy(temp_path + length, "brroffset");
+ voice.brr_offset = META_ENUM_INT(temp_path);
+ strcpy(temp_path + length, "vbit");
+ voice.vbit = META_ENUM_INT(temp_path);
+ strcpy(temp_path + length, "vidx");
+ voice.regs = &apu.dsp.m.regs[META_ENUM_INT(temp_path)];
+ strcpy(temp_path + length, "kondelay");
+ voice.kon_delay = META_ENUM_INT(temp_path);
+ strcpy(temp_path + length, "envmode");
+ voice.env_mode = (Spc_Dsp::env_mode_t) META_ENUM_INT(temp_path);
+ strcpy(temp_path + length, "env");
+ voice.env = META_ENUM_INT(temp_path);
+ strcpy(temp_path + length, "envxout");
+ voice.t_envx_out = META_ENUM_INT(temp_path);
+ strcpy(temp_path + length, "envcache");
+ voice.hidden_env = META_ENUM_INT(temp_path);
+ }
+
+ filter.set_gain( (int) (gain() * Spc_Filter::gain_unit) );
+ apu.clear_echo( true );
+ return blargg_ok;
+}
+
+#undef META_ENUM_INT
+
+blargg_err_t Sfm_Emu::play_and_filter( int count, sample_t out [] )
+{
+ RETURN_ERR( apu.play( count, out ) );
+ filter.run( out, count );
+ return blargg_ok;
+}
+
+blargg_err_t Sfm_Emu::skip_( int count )
+{
+ if ( sample_rate() != native_sample_rate )
+ {
+ count = (int) (count * resampler.rate()) & ~1;
+ count -= resampler.skip_input( count );
+ }
+
+ // TODO: shouldn't skip be adjusted for the 64 samples read afterwards?
+
+ if ( count > 0 )
+ {
+ RETURN_ERR( apu.skip( count ) );
+ filter.clear();
+ }
+
+ // eliminate pop due to resampler
+ const int resampler_latency = 64;
+ sample_t buf [resampler_latency];
+ return play_( resampler_latency, buf );
+}
+
+blargg_err_t Sfm_Emu::play_( int count, sample_t out [] )
+{
+ if ( sample_rate() == native_sample_rate )
+ return play_and_filter( count, out );
+
+ int remain = count;
+ while ( remain > 0 )
+ {
+ remain -= resampler.read( &out [count - remain], remain );
+ if ( remain > 0 )
+ {
+ int n = resampler.buffer_free();
+ RETURN_ERR( play_and_filter( n, resampler.buffer() ) );
+ resampler.write( n );
+ }
+ }
+ check( remain == 0 );
+ return blargg_ok;
+}
+
+blargg_err_t Sfm_Emu::hash_( Hash_Function& out ) const
+{
+ hash_sfm_file( file_begin(), file_size(), out );
+ return blargg_ok;
+}
+
diff --git a/Frameworks/GME/gme/Spc_Sfm.h b/Frameworks/GME/gme/Spc_Sfm.h
new file mode 100644
index 000000000..d81318b02
--- /dev/null
+++ b/Frameworks/GME/gme/Spc_Sfm.h
@@ -0,0 +1,67 @@
+// Super Nintendo SFM music file emulator
+
+// Game_Music_Emu $vers
+#ifndef SPC_SFM_H
+#define SPC_SFM_H
+
+#include "Music_Emu.h"
+#include "Snes_Spc.h"
+#include "Spc_Filter.h"
+
+#include "Bml_Parser.h"
+
+#if GME_SPC_FAST_RESAMPLER
+ #include "Upsampler.h"
+ typedef Upsampler Spc_Emu_Resampler;
+#else
+ #include "Fir_Resampler.h"
+ typedef Fir_Resampler<24> Spc_Emu_Resampler;
+#endif
+
+class Sfm_Emu : public Music_Emu {
+public:
+ // Minimum allowed file size
+ enum { sfm_min_file_size = 8 + 65536 + 128 };
+
+ // The Super Nintendo hardware samples at 32kHz. Other sample rates are
+ // handled by resampling the 32kHz output; emulation accuracy is not affected.
+ enum { native_sample_rate = 32000 };
+
+ // Disables annoying pseudo-surround effect some music uses
+ void disable_surround( bool disable = true ) { apu.disable_surround( disable ); }
+
+ // Enables gaussian, cubic or sinc interpolation
+ void interpolation_level( int level = 0 ) { apu.interpolation_level( level ); }
+
+ const Snes_Spc * get_apu() const;
+
+ blargg_err_t hash_( Hash_Function& ) const;
+
+ static gme_type_t static_type() { return gme_sfm_type; }
+
+// Implementation
+public:
+ Sfm_Emu();
+ ~Sfm_Emu();
+
+protected:
+ virtual blargg_err_t load_mem_( byte const [], int );
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t set_sample_rate_( int );
+ virtual blargg_err_t start_track_( int );
+ virtual blargg_err_t play_( int, sample_t [] );
+ virtual blargg_err_t skip_( int );
+ virtual void mute_voices_( int );
+ virtual void set_tempo_( double );
+
+private:
+ Spc_Emu_Resampler resampler;
+ Spc_Filter filter;
+ Snes_Spc apu;
+
+ Bml_Parser metadata;
+
+ blargg_err_t play_and_filter( int count, sample_t out [] );
+};
+
+#endif // SPC_SFM_H
diff --git a/Frameworks/GME/gme/Track_Filter.cpp b/Frameworks/GME/gme/Track_Filter.cpp
new file mode 100644
index 000000000..693af10f2
--- /dev/null
+++ b/Frameworks/GME/gme/Track_Filter.cpp
@@ -0,0 +1,293 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Track_Filter.h"
+
+/* Copyright (C) 2003-2008 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"
+
+int const fade_block_size = 512;
+int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
+int const silence_threshold = 8;
+
+blargg_err_t Track_Filter::init( callbacks_t* c )
+{
+ callbacks = c;
+ return buf.resize( buf_size );
+}
+
+void Track_Filter::clear_time_vars()
+{
+ emu_time = buf_remain;
+ out_time = 0;
+ silence_time = 0;
+ silence_count = 0;
+}
+
+void Track_Filter::stop()
+{
+ emu_track_ended_ = true;
+ track_ended_ = true;
+ fade_start = indefinite_count;
+ fade_step = 1;
+ buf_remain = 0;
+ emu_error = NULL;
+ clear_time_vars();
+}
+
+Track_Filter::Track_Filter() : setup_()
+{
+ callbacks = NULL;
+ setup_.max_silence = indefinite_count;
+ silence_ignored_ = false;
+ stop();
+}
+
+Track_Filter::~Track_Filter() { }
+
+blargg_err_t Track_Filter::start_track()
+{
+ emu_error = NULL;
+ stop();
+
+ emu_track_ended_ = false;
+ track_ended_ = false;
+
+ if ( !silence_ignored_ )
+ {
+ // play until non-silence or end of track
+ while ( emu_time < setup_.max_initial )
+ {
+ fill_buf();
+ if ( buf_remain | emu_track_ended_ )
+ break;
+ }
+ }
+
+ clear_time_vars();
+ return emu_error;
+}
+
+void Track_Filter::end_track_if_error( blargg_err_t err )
+{
+ if ( err )
+ {
+ emu_error = err;
+ emu_track_ended_ = true;
+ }
+}
+
+blargg_err_t Track_Filter::skip( int count )
+{
+ emu_error = NULL;
+ out_time += count;
+
+ // remove from silence and buf first
+ {
+ int n = min( count, silence_count );
+ silence_count -= n;
+ count -= n;
+
+ n = min( count, buf_remain );
+ buf_remain -= n;
+ count -= n;
+ }
+
+ if ( count && !emu_track_ended_ )
+ {
+ emu_time += count;
+ silence_time = emu_time; // would otherwise be invalid
+ end_track_if_error( callbacks->skip_( count ) );
+ }
+
+ if ( !(silence_count | buf_remain) ) // caught up to emulator, so update track ended
+ track_ended_ |= emu_track_ended_;
+
+ return emu_error;
+}
+
+blargg_err_t Track_Filter::skip_( int count )
+{
+ while ( count && !emu_track_ended_ )
+ {
+ int n = buf_size;
+ if ( n > count )
+ n = count;
+ count -= n;
+ RETURN_ERR( callbacks->play_( n, buf.begin() ) );
+ }
+ return blargg_ok;
+}
+
+// Fading
+
+void Track_Filter::set_fade( int start, int length )
+{
+ fade_start = start;
+ fade_step = length / (fade_block_size * fade_shift);
+ if ( fade_step < 1 )
+ fade_step = 1;
+}
+
+bool Track_Filter::is_fading() const
+{
+ return out_time >= fade_start && fade_start != indefinite_count;
+}
+
+// unit / pow( 2.0, (double) x / step )
+static int int_log( int x, int step, int unit )
+{
+ int shift = x / step;
+ int fraction = (x - shift * step) * unit / step;
+ return ((unit - fraction) + (fraction >> 1)) >> shift;
+}
+
+void Track_Filter::handle_fade( sample_t out [], int out_count )
+{
+ for ( int i = 0; i < out_count; i += fade_block_size )
+ {
+ int const shift = 14;
+ int const unit = 1 << shift;
+ int gain = int_log( (out_time + i - fade_start) / fade_block_size,
+ fade_step, unit );
+ if ( gain < (unit >> fade_shift) )
+ track_ended_ = emu_track_ended_ = true;
+
+ sample_t* io = &out [i];
+ for ( int count = min( fade_block_size, out_count - i ); count; --count )
+ {
+ *io = sample_t ((*io * gain) >> shift);
+ ++io;
+ }
+ }
+}
+
+// Silence detection
+
+void Track_Filter::emu_play( sample_t out [], int count )
+{
+ emu_time += count;
+ if ( !emu_track_ended_ )
+ end_track_if_error( callbacks->play_( count, out ) );
+ else
+ memset( out, 0, count * sizeof *out );
+}
+
+// number of consecutive silent samples at end
+static int count_silence( Track_Filter::sample_t begin [], int size )
+{
+ Track_Filter::sample_t first = *begin;
+ *begin = silence_threshold * 2; // sentinel
+ Track_Filter::sample_t* p = begin + size;
+ while ( (unsigned) (*--p + silence_threshold) <= (unsigned) silence_threshold * 2 ) { }
+ *begin = first;
+ return size - (p - begin);
+}
+
+// fill internal buffer and check it for silence
+void Track_Filter::fill_buf()
+{
+ assert( !buf_remain );
+ if ( !emu_track_ended_ )
+ {
+ emu_play( buf.begin(), buf_size );
+ int silence = count_silence( buf.begin(), buf_size );
+ if ( silence < buf_size )
+ {
+ silence_time = emu_time - silence;
+ buf_remain = buf_size;
+ return;
+ }
+ }
+ silence_count += buf_size;
+}
+
+blargg_err_t Track_Filter::play( int out_count, sample_t out [] )
+{
+ emu_error = NULL;
+ if ( track_ended_ )
+ {
+ memset( out, 0, out_count * sizeof *out );
+ }
+ else
+ {
+ assert( emu_time >= out_time );
+
+ // prints nifty graph of how far ahead we are when searching for silence
+ //dprintf( "%*s \n", int ((emu_time - out_time) * 7 / 44100), "*" );
+
+ // use any remaining silence samples
+ int pos = 0;
+ if ( silence_count )
+ {
+ if ( !silence_ignored_ )
+ {
+ // during a run of silence, run emulator at >=2x speed so it gets ahead
+ int ahead_time = setup_.lookahead * (out_time + out_count - silence_time) +
+ silence_time;
+ while ( emu_time < ahead_time && !(buf_remain | emu_track_ended_) )
+ fill_buf();
+
+ // end track if sufficient silence has been found
+ if ( emu_time - silence_time > setup_.max_silence )
+ {
+ track_ended_ = emu_track_ended_ = true;
+ silence_count = out_count;
+ buf_remain = 0;
+ }
+ }
+
+ // fill from remaining silence
+ pos = min( silence_count, out_count );
+ memset( out, 0, pos * sizeof *out );
+ silence_count -= pos;
+ }
+
+ // use any remaining samples from buffer
+ if ( buf_remain )
+ {
+ int n = min( buf_remain, (int) (out_count - pos) );
+ memcpy( out + pos, buf.begin() + (buf_size - buf_remain), n * sizeof *out );
+ buf_remain -= n;
+ pos += n;
+ }
+
+ // generate remaining samples normally
+ int remain = out_count - pos;
+ if ( remain )
+ {
+ emu_play( out + pos, remain );
+ track_ended_ |= emu_track_ended_;
+
+ if ( silence_ignored_ && !is_fading() )
+ {
+ // if left unupdated, ahead_time could become too large
+ silence_time = emu_time;
+ }
+ else
+ {
+ // check end for a new run of silence
+ int silence = count_silence( out + pos, remain );
+ if ( silence < remain )
+ silence_time = emu_time - silence;
+
+ if ( emu_time - silence_time >= buf_size )
+ fill_buf(); // cause silence detection on next play()
+ }
+ }
+
+ if ( is_fading() )
+ handle_fade( out, out_count );
+ }
+ out_time += out_count;
+ return emu_error;
+}
diff --git a/Frameworks/GME/gme/Track_Filter.h b/Frameworks/GME/gme/Track_Filter.h
new file mode 100644
index 000000000..e0389f0bc
--- /dev/null
+++ b/Frameworks/GME/gme/Track_Filter.h
@@ -0,0 +1,105 @@
+// Removes silence from beginning of track, fades end of track. Also looks ahead
+// for excessive silence, and if found, ends track.
+
+// Game_Music_Emu $vers
+#ifndef TRACK_FILTER_H
+#define TRACK_FILTER_H
+
+#include "blargg_common.h"
+
+class Track_Filter {
+public:
+ typedef int sample_count_t;
+ typedef short sample_t;
+
+ enum { indefinite_count = INT_MAX/2 + 1 };
+
+ struct callbacks_t {
+ // Samples may be stereo or mono
+ virtual blargg_err_t play_( int count, sample_t* out ) BLARGG_PURE( { return blargg_ok; } )
+ virtual blargg_err_t skip_( int count ) BLARGG_PURE( { return blargg_ok; } )
+ virtual ~callbacks_t() { } // avoids silly "non-virtual dtor" warning
+ };
+
+ // Initializes filter. Must be done once before using object.
+ blargg_err_t init( callbacks_t* );
+
+ struct setup_t {
+ sample_count_t max_initial; // maximum silence to strip from beginning of track
+ sample_count_t max_silence; // maximum silence in middle of track without it ending
+ int lookahead; // internal speed when looking ahead for silence (2=200% etc.)
+ };
+
+ // Gets/sets setup
+ setup_t const& setup() const { return setup_; }
+ void setup( setup_t const& s ) { setup_ = s; }
+
+ // Disables automatic end-of-track detection and skipping of silence at beginning
+ void ignore_silence( bool disable = true ) { silence_ignored_ = disable; }
+
+ // Clears state and skips initial silence in track
+ blargg_err_t start_track();
+
+ // Sets time that fade starts, and how long until track ends.
+ void set_fade( sample_count_t start, sample_count_t length );
+
+ // Generates n samples into buf
+ blargg_err_t play( int n, sample_t buf [] );
+
+ // Skips n samples
+ blargg_err_t skip( int n );
+
+ // Number of samples played/skipped since start_track()
+ int sample_count() const { return out_time; }
+
+ // True if track ended. Causes are end of source samples, end of fade,
+ // or excessive silence.
+ bool track_ended() const { return track_ended_; }
+
+ // Clears state
+ void stop();
+
+// For use by callbacks
+
+ // Sets internal "track ended" flag and stops generation of further source samples
+ void set_track_ended() { emu_track_ended_ = true; }
+
+ // For use by skip_() callback
+ blargg_err_t skip_( int count );
+
+// Implementation
+public:
+ Track_Filter();
+ ~Track_Filter();
+
+private:
+ callbacks_t* callbacks;
+ setup_t setup_;
+ const char* emu_error;
+ bool silence_ignored_;
+
+ // Timing
+ int out_time; // number of samples played since start of track
+ int emu_time; // number of samples emulator has generated since start of track
+ int emu_track_ended_; // emulator has reached end of track
+ volatile int track_ended_;
+ void clear_time_vars();
+ void end_track_if_error( blargg_err_t );
+
+ // Fading
+ int fade_start;
+ int fade_step;
+ bool is_fading() const;
+ void handle_fade( sample_t out [], int count );
+
+ // Silence detection
+ int silence_time; // absolute number of samples where most recent silence began
+ int silence_count; // number of samples of silence to play before using buf
+ int buf_remain; // number of samples left in silence buffer
+ enum { buf_size = 2048 };
+ blargg_vector buf;
+ void fill_buf();
+ void emu_play( sample_t out [], int count );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Upsampler.cpp b/Frameworks/GME/gme/Upsampler.cpp
new file mode 100644
index 000000000..5331176d0
--- /dev/null
+++ b/Frameworks/GME/gme/Upsampler.cpp
@@ -0,0 +1,73 @@
+// $package. http://www.slack.net/~ant/
+
+#include "Upsampler.h"
+
+/* Copyright (C) 2004-2008 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"
+
+int const shift = 15;
+int const unit = 1 << shift;
+
+void Upsampler::clear_()
+{
+ pos = 0;
+ Resampler::clear_();
+}
+
+Upsampler::Upsampler()
+{
+ clear();
+}
+
+blargg_err_t Upsampler::set_rate_( double new_factor )
+{
+ step = (int) (new_factor * unit + 0.5);
+ return Resampler::set_rate_( 1.0 / unit * step );
+}
+
+Resampler::sample_t const* Upsampler::resample_( sample_t** out_, sample_t const* out_end,
+ sample_t const in [], int in_size )
+{
+ in_size -= write_offset;
+ if ( in_size > 0 )
+ {
+ sample_t* BLARGG_RESTRICT out = *out_;
+ sample_t const* const in_end = in + in_size;
+
+ int const step = this->step;
+ int pos = this->pos;
+
+ do
+ {
+ #define INTERP( i, out )\
+ {\
+ int t = in [0 + i] * (unit - pos) + in [stereo + i] * pos;\
+ out = t >> shift;\
+ }
+
+ int out_0;
+ INTERP( 0, out_0 )
+ INTERP( 1, out [0] = out_0; out [1] )
+ out += stereo;
+
+ pos += step;
+ in += ((unsigned) pos >> shift) * stereo;
+ pos &= unit - 1;
+ }
+ while ( in < in_end && out < out_end );
+
+ this->pos = pos;
+ *out_ = out;
+ }
+ return in;
+}
diff --git a/Frameworks/GME/gme/Upsampler.h b/Frameworks/GME/gme/Upsampler.h
new file mode 100644
index 000000000..7159d67d1
--- /dev/null
+++ b/Frameworks/GME/gme/Upsampler.h
@@ -0,0 +1,25 @@
+// Increases sampling rate using linear interpolation
+
+// $package
+#ifndef UPSAMPLER_H
+#define UPSAMPLER_H
+
+#include "Resampler.h"
+
+class Upsampler : public Resampler {
+public:
+ Upsampler();
+
+protected:
+ virtual blargg_err_t set_rate_( double );
+ virtual void clear_();
+ virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int );
+
+protected:
+ enum { stereo = 2 };
+ enum { write_offset = 2 * stereo };
+ int pos;
+ int step;
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Vgm_Core.cpp b/Frameworks/GME/gme/Vgm_Core.cpp
new file mode 100644
index 000000000..7bfded31e
--- /dev/null
+++ b/Frameworks/GME/gme/Vgm_Core.cpp
@@ -0,0 +1,2242 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Vgm_Core.h"
+
+#include "dac_control.h"
+
+#include "blargg_endian.h"
+#include
+
+// Needed for OKIM6295 system detection
+#include "Vgm_Emu.h"
+
+/* Copyright (C) 2003-2008 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"
+
+int const stereo = 2;
+int const fm_time_bits = 12;
+int const blip_time_bits = 12;
+
+enum {
+ cmd_gg_stereo = 0x4F,
+ cmd_gg_stereo_2 = 0x3F,
+ cmd_psg = 0x50,
+ cmd_psg_2 = 0x30,
+ cmd_ym2413 = 0x51,
+ cmd_ym2413_2 = 0xA1,
+ cmd_ym2612_port0 = 0x52,
+ cmd_ym2612_2_port0 = 0xA2,
+ cmd_ym2612_port1 = 0x53,
+ cmd_ym2612_2_port1 = 0xA3,
+ cmd_ym2151 = 0x54,
+ cmd_ym2151_2 = 0xA4,
+ cmd_ym2203 = 0x55,
+ cmd_ym2203_2 = 0xA5,
+ cmd_ym2608_port0 = 0x56,
+ cmd_ym2608_2_port0 = 0xA6,
+ cmd_ym2608_port1 = 0x57,
+ cmd_ym2608_2_port1 = 0xA7,
+ cmd_ym2610_port0 = 0x58,
+ cmd_ym2610_2_port0 = 0xA8,
+ cmd_ym2610_port1 = 0x59,
+ cmd_ym2610_2_port1 = 0xA9,
+ cmd_ym3812 = 0x5A,
+ cmd_ym3812_2 = 0xAA,
+ cmd_ymz280b = 0x5D,
+ cmd_ymf262_port0 = 0x5E,
+ cmd_ymf262_2_port0 = 0xAE,
+ cmd_ymf262_port1 = 0x5F,
+ cmd_ymf262_2_port1 = 0xAF,
+ cmd_delay = 0x61,
+ cmd_delay_735 = 0x62,
+ cmd_delay_882 = 0x63,
+ cmd_byte_delay = 0x64,
+ cmd_end = 0x66,
+ cmd_data_block = 0x67,
+ cmd_ram_block = 0x68,
+ cmd_short_delay = 0x70,
+ cmd_pcm_delay = 0x80,
+ cmd_dacctl_setup = 0x90,
+ cmd_dacctl_data = 0x91,
+ cmd_dacctl_freq = 0x92,
+ cmd_dacctl_play = 0x93,
+ cmd_dacctl_stop = 0x94,
+ cmd_dacctl_playblock= 0x95,
+ cmd_ay8910 = 0xA0,
+ cmd_rf5c68 = 0xB0,
+ cmd_rf5c164 = 0xB1,
+ cmd_pwm = 0xB2,
+ cmd_okim6258_write = 0xB7,
+ cmd_okim6295_write = 0xB8,
+ cmd_huc6280_write = 0xB9,
+ cmd_k053260_write = 0xBA,
+ cmd_segapcm_write = 0xC0,
+ cmd_rf5c68_mem = 0xC1,
+ cmd_rf5c164_mem = 0xC2,
+ cmd_qsound_write = 0xC4,
+ cmd_k051649_write = 0xD2,
+ cmd_k054539_write = 0xD3,
+ cmd_c140 = 0xD4,
+ cmd_pcm_seek = 0xE0,
+
+ rf5c68_ram_block = 0x01,
+ rf5c164_ram_block = 0x02,
+
+ pcm_block_type = 0x00,
+ pcm_aux_block_type = 0x40,
+ rom_block_type = 0x80,
+ ram_block_type = 0xC0,
+
+ rom_segapcm = 0x80,
+ rom_ym2608_deltat = 0x81,
+ rom_ym2610_adpcm = 0x82,
+ rom_ym2610_deltat = 0x83,
+ rom_ymz280b = 0x86,
+ rom_okim6295 = 0x8B,
+ rom_k054539 = 0x8C,
+ rom_c140 = 0x8D,
+ rom_k053260 = 0x8E,
+ rom_qsound = 0x8F,
+
+ ram_rf5c68 = 0xC0,
+ ram_rf5c164 = 0xC1,
+ ram_nesapu = 0xC2,
+
+ ym2612_dac_port = 0x2A,
+ ym2612_dac_pan_port = 0xB6
+};
+
+inline int command_len( int command )
+{
+ static byte const lens [0x10] = {
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 1,1,1,2,2,3,1,1,1,1,3,3,4,4,5,5
+ };
+ int len = lens [command >> 4];
+ check( len != 1 );
+ return len;
+}
+
+int Vgm_Core::run_ym2151( int chip, int time )
+{
+ return ym2151[!!chip].run_until( time );
+}
+
+int Vgm_Core::run_ym2203( int chip, int time )
+{
+ return ym2203[!!chip].run_until( time );
+}
+
+int Vgm_Core::run_ym2413( int chip, int time )
+{
+ return ym2413[!!chip].run_until( time );
+}
+
+int Vgm_Core::run_ym2612( int chip, int time )
+{
+ return ym2612[!!chip].run_until( time );
+}
+
+int Vgm_Core::run_ym2610( int chip, int time )
+{
+ return ym2610[!!chip].run_until( time );
+}
+
+int Vgm_Core::run_ym2608( int chip, int time )
+{
+ return ym2608[!!chip].run_until( time );
+}
+
+int Vgm_Core::run_ym3812( int chip, int time )
+{
+ return ym3812[!!chip].run_until( time );
+}
+
+int Vgm_Core::run_ymf262( int chip, int time )
+{
+ return ymf262[!!chip].run_until( time );
+}
+
+int Vgm_Core::run_ymz280b( int time )
+{
+ return ymz280b.run_until( time );
+}
+
+int Vgm_Core::run_c140( int time )
+{
+ return c140.run_until( time );
+}
+
+int Vgm_Core::run_segapcm( int time )
+{
+ return segapcm.run_until( time );
+}
+
+int Vgm_Core::run_rf5c68( int time )
+{
+ return rf5c68.run_until( time );
+}
+
+int Vgm_Core::run_rf5c164( int time )
+{
+ return rf5c164.run_until( time );
+}
+
+int Vgm_Core::run_pwm( int time )
+{
+ return pwm.run_until( time );
+}
+
+int Vgm_Core::run_okim6258( int time )
+{
+ return okim6258.run_until( time );
+}
+
+int Vgm_Core::run_okim6295( int chip, int time )
+{
+ return okim6295[!!chip].run_until( time );
+}
+
+int Vgm_Core::run_k051649( int time )
+{
+ return k051649.run_until( time );
+}
+
+int Vgm_Core::run_k053260( int time )
+{
+ return k053260.run_until( time );
+}
+
+int Vgm_Core::run_k054539( int time )
+{
+ return k054539.run_until( time );
+}
+
+int Vgm_Core::run_qsound( int chip, int time )
+{
+ return qsound[!!chip].run_until( time );
+}
+
+/* Recursive fun starts here! */
+int Vgm_Core::run_dac_control( int time )
+{
+ if (dac_control_recursion) return 1;
+
+ ++dac_control_recursion;
+ for ( unsigned i = 0; i < DacCtrlUsed; i++ )
+ {
+ int time_start = DacCtrlTime[DacCtrlMap[i]];
+ if ( time > time_start )
+ {
+ DacCtrlTime[DacCtrlMap[i]] = time;
+ daccontrol_update( dac_control [i], time_start, time - time_start );
+ }
+ }
+ --dac_control_recursion;
+
+ return 1;
+}
+
+Vgm_Core::Vgm_Core()
+{
+ blip_buf[0] = stereo_buf[0].center();
+ blip_buf[1] = blip_buf[0];
+ has_looped = false;
+ DacCtrlUsed = 0;
+ dac_control = NULL;
+ memset( PCMBank, 0, sizeof( PCMBank ) );
+ memset( &PCMTbl, 0, sizeof( PCMTbl ) );
+ memset( DacCtrl, 0, sizeof( DacCtrl ) );
+ memset( DacCtrlTime, 0, sizeof( DacCtrlTime ) );
+}
+
+Vgm_Core::~Vgm_Core()
+{
+ for (unsigned i = 0; i < DacCtrlUsed; i++) device_stop_daccontrol( dac_control [i] );
+ if ( dac_control ) free( dac_control );
+ for (unsigned i = 0; i < PCM_BANK_COUNT; i++)
+ {
+ if ( PCMBank [i].Bank ) free( PCMBank [i].Bank );
+ if ( PCMBank [i].Data ) free( PCMBank [i].Data );
+ }
+ if ( PCMTbl.Entries ) free( PCMTbl.Entries );
+}
+
+typedef unsigned int FUINT8;
+typedef unsigned int FUINT16;
+
+void Vgm_Core::ReadPCMTable(unsigned DataSize, const byte* Data)
+{
+ byte ValSize;
+ unsigned TblSize;
+
+ PCMTbl.ComprType = Data[0x00];
+ PCMTbl.CmpSubType = Data[0x01];
+ PCMTbl.BitDec = Data[0x02];
+ PCMTbl.BitCmp = Data[0x03];
+ PCMTbl.EntryCount = get_le16( Data + 0x04 );
+
+ ValSize = (PCMTbl.BitDec + 7) / 8;
+ TblSize = PCMTbl.EntryCount * ValSize;
+
+ PCMTbl.Entries = realloc(PCMTbl.Entries, TblSize);
+ memcpy(PCMTbl.Entries, Data + 0x06, TblSize);
+}
+
+bool Vgm_Core::DecompressDataBlk(VGM_PCM_DATA* Bank, unsigned DataSize, const byte* Data)
+{
+ UINT8 ComprType;
+ UINT8 BitDec;
+ FUINT8 BitCmp;
+ UINT8 CmpSubType;
+ UINT16 AddVal;
+ const UINT8* InPos;
+ const UINT8* InDataEnd;
+ UINT8* OutPos;
+ const UINT8* OutDataEnd;
+ FUINT16 InVal;
+ FUINT16 OutVal = 0;
+ FUINT8 ValSize;
+ FUINT8 InShift;
+ FUINT8 OutShift;
+ UINT8* Ent1B;
+ UINT16* Ent2B;
+
+ // ReadBits Variables
+ FUINT8 BitsToRead;
+ FUINT8 BitReadVal;
+ FUINT8 InValB;
+ FUINT8 BitMask;
+ FUINT8 OutBit;
+
+ // Variables for DPCM
+ UINT16 OutMask;
+
+ ComprType = Data[0x00];
+ Bank->DataSize = get_le32( Data + 0x01 );
+
+ switch(ComprType)
+ {
+ case 0x00: // n-Bit compression
+ BitDec = Data[0x05];
+ BitCmp = Data[0x06];
+ CmpSubType = Data[0x07];
+ AddVal = get_le16( Data + 0x08 );
+
+ if (CmpSubType == 0x02)
+ {
+ Ent1B = (UINT8*)PCMTbl.Entries;
+ Ent2B = (UINT16*)PCMTbl.Entries;
+ if (! PCMTbl.EntryCount)
+ {
+ Bank->DataSize = 0x00;
+ return false;
+ }
+ else if (BitDec != PCMTbl.BitDec || BitCmp != PCMTbl.BitCmp)
+ {
+ Bank->DataSize = 0x00;
+ return false;
+ }
+ }
+
+ ValSize = (BitDec + 7) / 8;
+ InPos = Data + 0x0A;
+ InDataEnd = Data + DataSize;
+ InShift = 0;
+ OutShift = BitDec - BitCmp;
+ OutDataEnd = Bank->Data + Bank->DataSize;
+
+ for (OutPos = Bank->Data; OutPos < OutDataEnd && InPos < InDataEnd; OutPos += ValSize)
+ {
+ //InVal = ReadBits(Data, InPos, &InShift, BitCmp);
+ // inlined - is 30% faster
+ OutBit = 0x00;
+ InVal = 0x0000;
+ BitsToRead = BitCmp;
+ while(BitsToRead)
+ {
+ BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead;
+ BitsToRead -= BitReadVal;
+ BitMask = (1 << BitReadVal) - 1;
+
+ InShift += BitReadVal;
+ InValB = (*InPos << InShift >> 8) & BitMask;
+ if (InShift >= 8)
+ {
+ InShift -= 8;
+ InPos ++;
+ if (InShift)
+ InValB |= (*InPos << InShift >> 8) & BitMask;
+ }
+
+ InVal |= InValB << OutBit;
+ OutBit += BitReadVal;
+ }
+
+ switch(CmpSubType)
+ {
+ case 0x00: // Copy
+ OutVal = InVal + AddVal;
+ break;
+ case 0x01: // Shift Left
+ OutVal = (InVal << OutShift) + AddVal;
+ break;
+ case 0x02: // Table
+ switch(ValSize)
+ {
+ case 0x01:
+ OutVal = Ent1B[InVal];
+ break;
+ case 0x02:
+ OutVal = Ent2B[InVal];
+ break;
+ }
+ break;
+ }
+
+ //memcpy(OutPos, &OutVal, ValSize);
+ if (ValSize == 0x01)
+ *((UINT8*)OutPos) = (UINT8)OutVal;
+ else //if (ValSize == 0x02)
+ *((UINT16*)OutPos) = (UINT16)OutVal;
+ }
+ break;
+ case 0x01: // Delta-PCM
+ BitDec = Data[0x05];
+ BitCmp = Data[0x06];
+ OutVal = get_le16( Data + 0x08 );
+
+ Ent1B = (UINT8*)PCMTbl.Entries;
+ Ent2B = (UINT16*)PCMTbl.Entries;
+ if (! PCMTbl.EntryCount)
+ {
+ Bank->DataSize = 0x00;
+ return false;
+ }
+ else if (BitDec != PCMTbl.BitDec || BitCmp != PCMTbl.BitCmp)
+ {
+ Bank->DataSize = 0x00;
+ return false;
+ }
+
+ ValSize = (BitDec + 7) / 8;
+ OutMask = (1 << BitDec) - 1;
+ InPos = Data + 0x0A;
+ InDataEnd = Data + DataSize;
+ InShift = 0;
+ OutShift = BitDec - BitCmp;
+ OutDataEnd = Bank->Data + Bank->DataSize;
+ AddVal = 0x0000;
+
+ for (OutPos = Bank->Data; OutPos < OutDataEnd && InPos < InDataEnd; OutPos += ValSize)
+ {
+ //InVal = ReadBits(Data, InPos, &InShift, BitCmp);
+ // inlined - is 30% faster
+ OutBit = 0x00;
+ InVal = 0x0000;
+ BitsToRead = BitCmp;
+ while(BitsToRead)
+ {
+ BitReadVal = (BitsToRead >= 8) ? 8 : BitsToRead;
+ BitsToRead -= BitReadVal;
+ BitMask = (1 << BitReadVal) - 1;
+
+ InShift += BitReadVal;
+ InValB = (*InPos << InShift >> 8) & BitMask;
+ if (InShift >= 8)
+ {
+ InShift -= 8;
+ InPos ++;
+ if (InShift)
+ InValB |= (*InPos << InShift >> 8) & BitMask;
+ }
+
+ InVal |= InValB << OutBit;
+ OutBit += BitReadVal;
+ }
+
+ switch(ValSize)
+ {
+ case 0x01:
+ AddVal = Ent1B[InVal];
+ OutVal += AddVal;
+ OutVal &= OutMask;
+ *((UINT8*)OutPos) = (UINT8)OutVal;
+ break;
+ case 0x02:
+ AddVal = Ent2B[InVal];
+ OutVal += AddVal;
+ OutVal &= OutMask;
+ *((UINT16*)OutPos) = (UINT16)OutVal;
+ break;
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+void Vgm_Core::AddPCMData(byte Type, unsigned DataSize, const byte* Data)
+{
+ unsigned CurBnk;
+ VGM_PCM_BANK* TempPCM;
+ VGM_PCM_DATA* TempBnk;
+ unsigned BankSize;
+ bool RetVal;
+
+
+ if ((Type & 0x3F) >= PCM_BANK_COUNT || has_looped)
+ return;
+
+ if (Type == 0x7F)
+ {
+ ReadPCMTable( DataSize, Data );
+ return;
+ }
+
+ TempPCM = &PCMBank[Type & 0x3F];
+ CurBnk = TempPCM->BankCount;
+ TempPCM->BankCount ++;
+ TempPCM->BnkPos ++;
+ if (TempPCM->BnkPos < TempPCM->BankCount)
+ return; // Speed hack (for restarting playback)
+ TempPCM->Bank = (VGM_PCM_DATA*)realloc(TempPCM->Bank,
+ sizeof(VGM_PCM_DATA) * TempPCM->BankCount);
+
+ if (! (Type & 0x40))
+ BankSize = DataSize;
+ else
+ BankSize = get_le32( Data + 1 );
+ TempPCM->Data = ( byte * ) realloc(TempPCM->Data, TempPCM->DataSize + BankSize);
+ TempBnk = &TempPCM->Bank[CurBnk];
+ TempBnk->DataStart = TempPCM->DataSize;
+ if (! (Type & 0x40))
+ {
+ TempBnk->DataSize = DataSize;
+ TempBnk->Data = TempPCM->Data + TempBnk->DataStart;
+ memcpy(TempBnk->Data, Data, DataSize);
+ }
+ else
+ {
+ TempBnk->Data = TempPCM->Data + TempBnk->DataStart;
+ RetVal = DecompressDataBlk(TempBnk, DataSize, Data);
+ if (! RetVal)
+ {
+ TempBnk->Data = NULL;
+ TempBnk->DataSize = 0x00;
+ return;
+ }
+ }
+ TempPCM->DataSize += BankSize;
+}
+
+const byte* Vgm_Core::GetPointerFromPCMBank(byte Type, unsigned DataPos)
+{
+ if (Type >= PCM_BANK_COUNT)
+ return NULL;
+
+ if (DataPos >= PCMBank[Type].DataSize)
+ return NULL;
+
+ return &PCMBank[Type].Data[DataPos];
+}
+
+void Vgm_Core::dac_control_grow(byte chip_id)
+{
+ for ( unsigned i = 0; i < DacCtrlUsed; i++ )
+ {
+ if ( DacCtrlUsg [i] == chip_id )
+ {
+ device_reset_daccontrol( dac_control [i] );
+ return;
+ }
+ }
+ unsigned chip_mapped = DacCtrlUsed;
+ DacCtrlUsg [DacCtrlUsed++] = chip_id;
+ DacCtrlMap [chip_id] = chip_mapped;
+ dac_control = (void**) realloc( dac_control, DacCtrlUsed * sizeof(void*) );
+ dac_control [chip_mapped] = device_start_daccontrol( vgm_rate, this );
+ device_reset_daccontrol( dac_control [chip_mapped] );
+}
+
+extern "C" void chip_reg_write(void * context, UINT32 Sample, UINT8 ChipType, UINT8 ChipID, UINT8 Port, UINT8 Offset, UINT8 Data)
+{
+ Vgm_Core * core = (Vgm_Core *) context;
+ core->chip_reg_write(Sample, ChipType, ChipID, Port, Offset, Data);
+}
+
+void Vgm_Core::chip_reg_write(unsigned Sample, byte ChipType, byte ChipID, byte Port, byte Offset, byte Data)
+{
+ run_dac_control( Sample ); /* Let's get recursive! */
+ ChipID = !!ChipID;
+ switch (ChipType)
+ {
+ case 0x02:
+ switch (Port)
+ {
+ case 0:
+ if ( Offset == ym2612_dac_port )
+ {
+ write_pcm( Sample, ChipID, Data );
+ }
+ else if ( run_ym2612( ChipID, to_fm_time( Sample ) ) )
+ {
+ if ( Offset == 0x2B )
+ {
+ dac_disabled[ChipID] = (Data >> 7 & 1) - 1;
+ dac_amp[ChipID] |= dac_disabled[ChipID];
+ }
+ ym2612[ChipID].write0( Offset, Data );
+ }
+ break;
+
+ case 1:
+ if ( run_ym2612( ChipID, to_fm_time( Sample ) ) )
+ {
+ if ( Offset == ym2612_dac_pan_port )
+ {
+ Blip_Buffer * blip_buf = NULL;
+ switch ( Data >> 6 )
+ {
+ case 0: blip_buf = NULL; break;
+ case 1: blip_buf = stereo_buf[0].right(); break;
+ case 2: blip_buf = stereo_buf[0].left(); break;
+ case 3: blip_buf = stereo_buf[0].center(); break;
+ }
+ /*if ( this->blip_buf != blip_buf )
+ {
+ blip_time_t blip_time = to_psg_time( vgm_time );
+ if ( this->blip_buf ) pcm.offset_inline( blip_time, -dac_amp, this->blip_buf );
+ if ( blip_buf ) pcm.offset_inline( blip_time, dac_amp, blip_buf );
+ }*/
+ this->blip_buf[ChipID] = blip_buf;
+ }
+ ym2612[ChipID].write1( Offset, Data );
+ }
+ break;
+ }
+ break;
+
+ case 0x11:
+ if ( run_pwm( to_fm_time( Sample ) ) )
+ pwm.write( Port, ( ( Offset ) << 8 ) + Data );
+ break;
+
+ case 0x00:
+ psg[ChipID].write_data( to_psg_time( Sample ), Data );
+ break;
+
+ case 0x01:
+ if ( run_ym2413( ChipID, to_fm_time( Sample ) ) )
+ ym2413[ChipID].write( Offset, Data );
+ break;
+
+ case 0x03:
+ if ( run_ym2151( ChipID, to_fm_time( Sample ) ) )
+ ym2151[ChipID].write( Offset, Data );
+ break;
+
+ case 0x06:
+ if ( run_ym2203( ChipID, to_fm_time( Sample ) ) )
+ ym2203[ChipID].write( Offset, Data );
+ break;
+
+ case 0x07:
+ if ( run_ym2608( ChipID, to_fm_time( Sample ) ) )
+ {
+ switch (Port)
+ {
+ case 0: ym2608[ChipID].write0( Offset, Data ); break;
+ case 1: ym2608[ChipID].write1( Offset, Data ); break;
+ }
+ }
+ break;
+
+ case 0x08:
+ if ( run_ym2610( ChipID, to_fm_time( Sample ) ) )
+ {
+ switch (Port)
+ {
+ case 0: ym2610[ChipID].write0( Offset, Data ); break;
+ case 1: ym2610[ChipID].write1( Offset, Data ); break;
+ }
+ }
+ break;
+
+ case 0x09:
+ if ( run_ym3812( ChipID, to_fm_time( Sample ) ) )
+ ym3812[ChipID].write( Offset, Data );
+ break;
+
+ case 0x0C:
+ if ( run_ymf262( ChipID, to_fm_time( Sample ) ) )
+ {
+ switch (Port)
+ {
+ case 0: ymf262[ChipID].write0( Offset, Data ); break;
+ case 1: ymf262[ChipID].write1( Offset, Data ); break;
+ }
+ }
+ break;
+
+ case 0x0F:
+ if ( run_ymz280b( to_fm_time( Sample ) ) )
+ ymz280b.write( Offset, Data );
+ break;
+
+ case 0x12:
+ ay[ChipID].write_addr( Offset );
+ ay[ChipID].write_data( to_ay_time( Sample ), Data );
+ break;
+
+ case 0x17:
+ if ( run_okim6258( to_fm_time( Sample ) ) )
+ okim6258.write( Offset, Data );
+ break;
+
+ case 0x18:
+ if ( run_okim6295( ChipID, to_fm_time( Sample ) ) )
+ okim6295[ChipID].write( Offset, Data );
+ break;
+
+ case 0x19:
+ if ( run_k051649( to_fm_time( Sample ) ) )
+ k051649.write( Port, Offset, Data );
+ break;
+
+ case 0x1A:
+ if ( run_k054539( to_fm_time( Sample ) ) )
+ k054539.write( ( Port << 8 ) | Offset, Data );
+ break;
+
+ case 0x1B:
+ huc6280[ChipID].write_data( to_huc6280_time( Sample ), 0x800 + Offset, Data );
+ break;
+
+ case 0x1D:
+ if ( run_k053260( to_fm_time( Sample ) ) )
+ k053260.write( Offset, Data );
+ break;
+
+ case 0x1F:
+ if ( run_qsound( ChipID, Sample ) )
+ qsound[ ChipID ].write( Data, ( Port << 8 ) + Offset );
+ break;
+ }
+}
+
+void Vgm_Core::set_tempo( double t )
+{
+ if ( file_begin() )
+ {
+ vgm_rate = (int) (44100 * t + 0.5);
+ blip_time_factor = (int) ((double)
+ (1 << blip_time_bits) / vgm_rate * stereo_buf[0].center()->clock_rate() + 0.5);
+ blip_ay_time_factor = (int) ((double)
+ (1 << blip_time_bits) / vgm_rate * stereo_buf[1].center()->clock_rate() + 0.5);
+ blip_huc6280_time_factor = (int) ((double)
+ (1 << blip_time_bits) / vgm_rate * stereo_buf[2].center()->clock_rate() + 0.5);
+ //dprintf( "blip_time_factor: %ld\n", blip_time_factor );
+ //dprintf( "vgm_rate: %ld\n", vgm_rate );
+ // TODO: remove? calculates vgm_rate more accurately (above differs at most by one Hz only)
+ //blip_time_factor = (int) floor( double (1 << blip_time_bits) * psg_rate_ / 44100 / t + 0.5 );
+ //vgm_rate = (int) floor( double (1 << blip_time_bits) * psg_rate_ / blip_time_factor + 0.5 );
+
+ fm_time_factor = 2 + (int) (fm_rate * (1 << fm_time_bits) / vgm_rate + 0.5);
+ }
+}
+
+bool Vgm_Core::header_t::valid_tag() const
+{
+ return !memcmp( tag, "Vgm ", 4 );
+}
+
+int Vgm_Core::header_t::size() const
+{
+ unsigned int version = get_le32( this->version );
+ unsigned int data_offset;
+ if ( version >= 0x150 )
+ {
+ data_offset = get_le32( this->data_offset );
+ if ( data_offset ) data_offset += offsetof( header_t, data_offset );
+ }
+ else data_offset = 0x40;
+ unsigned expected_size = ( version > 0x150 ) ? ( ( version > 0x160 ) ? unsigned(size_max) : unsigned(size_151) ) : unsigned(size_min);
+ if ( expected_size > data_offset ) expected_size = data_offset ? (data_offset > unsigned(size_max) ? unsigned(size_max) : data_offset) : unsigned(size_min);
+ return expected_size;
+}
+
+void Vgm_Core::header_t::cleanup()
+{
+ unsigned int version = get_le32( this->version );
+
+ if ( size() < size_max ) memset( ((byte*)this) + size(), 0, size_max - size() );
+
+ if ( version < 0x161 )
+ {
+ memset( this->gbdmg_rate, 0, size_max - offsetof(header_t, gbdmg_rate) );
+ }
+
+ if ( version < 0x160 )
+ {
+ volume_modifier = 0;
+ reserved = 0;
+ loop_base = 0;
+ }
+
+ if ( version < 0x151 ) memset( this->rf5c68_rate, 0, size_max - size_min );
+
+ if ( version < 0x150 )
+ {
+ set_le32( data_offset, size_min - offsetof(header_t, data_offset) );
+ sn76489_flags = 0;
+ set_le32( segapcm_rate, 0 );
+ set_le32( segapcm_reg, 0 );
+ }
+
+ if ( version < 0x110 )
+ {
+ set_le16( noise_feedback, 0 );
+ noise_width = 0;
+ unsigned int rate = get_le32( ym2413_rate );
+ set_le32( ym2612_rate, rate );
+ set_le32( ym2151_rate, rate );
+ }
+
+ if ( version < 0x101 )
+ {
+ set_le32( frame_rate, 0 );
+ }
+}
+
+blargg_err_t Vgm_Core::load_mem_( byte const data [], int size )
+{
+ assert( offsetof (header_t, rf5c68_rate) == header_t::size_min );
+ assert( offsetof (header_t, extra_offset[4]) == header_t::size_max );
+
+ if ( size <= header_t::size_min )
+ return blargg_err_file_type;
+
+ memcpy( &_header, data, header_t::size_min );
+
+ header_t const& h = header();
+
+ if ( !h.valid_tag() )
+ return blargg_err_file_type;
+
+ int version = get_le32( h.version );
+
+ check( version < 0x100 );
+
+ if ( version > 0x150 )
+ {
+ if ( size < header().size() )
+ return "Invalid header";
+
+ memcpy( &_header.rf5c68_rate, data + offsetof (header_t, rf5c68_rate), header().size() - header_t::size_min );
+ }
+
+ _header.cleanup();
+
+ // Get loop
+ loop_begin = file_end();
+ if ( get_le32( h.loop_offset ) )
+ loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)];
+
+ // PSG rate
+ int psg_rate = get_le32( h.psg_rate ) & 0x3FFFFFFF;
+ if ( !psg_rate )
+ psg_rate = 3579545;
+ stereo_buf[0].clock_rate( psg_rate );
+
+ int ay_rate = get_le32( h.ay8910_rate ) & 0xBFFFFFFF;
+ if ( !ay_rate )
+ ay_rate = 2000000;
+ stereo_buf[1].clock_rate( ay_rate * 2 );
+ ay[0].set_type( (Ay_Apu::Ay_Apu_Type) header().ay8910_type );
+ ay[1].set_type( (Ay_Apu::Ay_Apu_Type) header().ay8910_type );
+
+ int huc6280_rate = get_le32( h.huc6280_rate ) & 0xBFFFFFFF;
+ if ( !huc6280_rate )
+ huc6280_rate = 3579545;
+ stereo_buf[2].clock_rate( huc6280_rate * 2 );
+
+ // Disable FM
+ fm_rate = 0;
+ ymz280b.enable( false );
+ ymf262[0].enable( false );
+ ymf262[1].enable( false );
+ ym3812[0].enable( false );
+ ym3812[1].enable( false );
+ ym2612[0].enable( false );
+ ym2612[1].enable( false );
+ ym2610[0].enable( false );
+ ym2610[1].enable( false );
+ ym2608[0].enable( false );
+ ym2608[1].enable( false );
+ ym2413[0].enable( false );
+ ym2413[1].enable( false );
+ ym2203[0].enable( false );
+ ym2203[1].enable( false );
+ ym2151[0].enable( false );
+ ym2151[1].enable( false );
+ c140.enable( false );
+ segapcm.enable( false );
+ rf5c68.enable( false );
+ rf5c164.enable( false );
+ pwm.enable( false );
+ okim6258.enable( false );
+ okim6295[0].enable( false );
+ okim6295[1].enable( false );
+ k051649.enable( false );
+ k053260.enable( false );
+ k054539.enable( false );
+ qsound[0].enable( false );
+ qsound[1].enable( false );
+
+ set_tempo( 1 );
+
+ return blargg_ok;
+}
+
+// Update pre-1.10 header FM rates by scanning commands
+void Vgm_Core::update_fm_rates( int* ym2151_rate, int* ym2413_rate, int* ym2612_rate ) const
+{
+ byte const* p = file_begin() + header().size();
+ int data_offset = get_le32( header().data_offset );
+ check( data_offset );
+ if ( data_offset )
+ p += data_offset + offsetof( header_t, data_offset ) - header().size();
+ while ( p < file_end() )
+ {
+ switch ( *p )
+ {
+ case cmd_end:
+ return;
+
+ case cmd_psg:
+ case cmd_byte_delay:
+ p += 2;
+ break;
+
+ case cmd_delay:
+ p += 3;
+ break;
+
+ case cmd_data_block:
+ p += 7 + get_le32( p + 3 );
+ break;
+
+ case cmd_ram_block:
+ p += 12;
+ break;
+
+ case cmd_ym2413:
+ *ym2151_rate = 0;
+ *ym2612_rate = 0;
+ return;
+
+ case cmd_ym2612_port0:
+ case cmd_ym2612_port1:
+ *ym2612_rate = *ym2413_rate;
+ *ym2413_rate = 0;
+ *ym2151_rate = 0;
+ return;
+
+ case cmd_ym2151:
+ *ym2151_rate = *ym2413_rate;
+ *ym2413_rate = 0;
+ *ym2612_rate = 0;
+ return;
+
+ default:
+ p += command_len( *p );
+ }
+ }
+}
+
+blargg_err_t Vgm_Core::init_chips( double* rate, bool reinit )
+{
+ int ymz280b_rate = get_le32( header().ymz280b_rate ) & 0xBFFFFFFF;
+ int ymf262_rate = get_le32( header().ymf262_rate ) & 0xBFFFFFFF;
+ int ym3812_rate = get_le32( header().ym3812_rate ) & 0xBFFFFFFF;
+ int ym2612_rate = get_le32( header().ym2612_rate ) & 0xBFFFFFFF;
+ int ym2610_rate = get_le32( header().ym2610_rate ) & 0x3FFFFFFF;
+ int ym2608_rate = get_le32( header().ym2608_rate ) & 0x3FFFFFFF;
+ int ym2413_rate = get_le32( header().ym2413_rate ) & 0xBFFFFFFF;
+ int ym2203_rate = get_le32( header().ym2203_rate ) & 0xBFFFFFFF;
+ int ym2151_rate = get_le32( header().ym2151_rate ) & 0xBFFFFFFF;
+ int c140_rate = get_le32( header().c140_rate ) & 0xBFFFFFFF;
+ int segapcm_rate = get_le32( header().segapcm_rate ) & 0xBFFFFFFF;
+ int rf5c68_rate = get_le32( header().rf5c68_rate ) & 0xBFFFFFFF;
+ int rf5c164_rate = get_le32( header().rf5c164_rate ) & 0xBFFFFFFF;
+ int pwm_rate = get_le32( header().pwm_rate ) & 0xBFFFFFFF;
+ int okim6258_rate = get_le32( header().okim6258_rate ) & 0xBFFFFFFF;
+ int okim6295_rate = get_le32( header().okim6295_rate ) & 0xBFFFFFFF;
+ int k051649_rate = get_le32( header().k051649_rate ) & 0xBFFFFFFF;
+ int k053260_rate = get_le32( header().k053260_rate ) & 0xBFFFFFFF;
+ int k054539_rate = get_le32( header().k054539_rate ) & 0xBFFFFFFF;
+ int qsound_rate = get_le32( header().qsound_rate ) & 0xBFFFFFFF;
+ if ( ym2413_rate && get_le32( header().version ) < 0x110 )
+ update_fm_rates( &ym2151_rate, &ym2413_rate, &ym2612_rate );
+
+ *rate = vgm_rate;
+
+ if ( ymf262_rate )
+ {
+ bool dual_chip = !!(header().ymf262_rate[3] & 0x40);
+ double gain = dual_chip ? 0.5 : 1.0;
+ double fm_rate = ymf262_rate / 288.0;
+ int result;
+ if ( !reinit )
+ {
+ result = ymf262[0].set_rate( fm_rate, ymf262_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ymf262[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ymf262[0].enable();
+ if ( dual_chip )
+ {
+ if ( !reinit )
+ {
+ result = ymf262[1].set_rate( fm_rate, ymf262_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ymf262[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ymf262[1].enable();
+ }
+ }
+ if ( ym3812_rate )
+ {
+ bool dual_chip = !!(header().ym3812_rate[3] & 0x40);
+ double gain = dual_chip ? 0.5 : 1.0;
+ double fm_rate = ym3812_rate / 72.0;
+ int result;
+ if ( !reinit )
+ {
+ result = ym3812[0].set_rate( fm_rate, ym3812_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ym3812[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym3812[0].enable();
+ if ( dual_chip )
+ {
+ if ( !reinit )
+ {
+ result = ym3812[1].set_rate( fm_rate, ym3812_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ym3812[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym3812[1].enable();
+ }
+ }
+ if ( ym2612_rate )
+ {
+ bool dual_chip = !!(header().ym2612_rate[3] & 0x40);
+ double gain = dual_chip ? 0.5 : 1.0;
+ double fm_rate = ym2612_rate / 144.0;
+ if ( !reinit )
+ {
+ RETURN_ERR( ym2612[0].set_rate( fm_rate, ym2612_rate ) );
+ }
+ RETURN_ERR( ym2612[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2612[0].enable();
+ if ( dual_chip )
+ {
+ if ( !reinit )
+ {
+ RETURN_ERR( ym2612[1].set_rate( fm_rate, ym2612_rate ) );
+ }
+ RETURN_ERR( ym2612[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2612[1].enable();
+ }
+ }
+ if ( ym2610_rate )
+ {
+ bool dual_chip = !!(header().ym2610_rate[3] & 0x40);
+ bool is_2610b = !!(header().ym2610_rate[3] & 0x80);
+ double gain = dual_chip ? 0.5 : 1.0;
+ double fm_rate = ym2610_rate / 72.0;
+ int result;
+ if ( !reinit )
+ {
+ result = ym2610[0].set_rate( fm_rate, ym2610_rate, is_2610b );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ym2610[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2610[0].enable();
+ if ( dual_chip )
+ {
+ if ( !reinit )
+ {
+ result = ym2610[1].set_rate( fm_rate, ym2610_rate, is_2610b );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ym2610[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2610[1].enable();
+ }
+ }
+ if ( ym2608_rate )
+ {
+ bool dual_chip = !!(header().ym2610_rate[3] & 0x40);
+ double gain = dual_chip ? 1.0 : 2.0;
+ double fm_rate = ym2608_rate / 72.0;
+ int result;
+ if ( !reinit )
+ {
+ result = ym2608[0].set_rate( fm_rate, ym2608_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ym2608[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2608[0].enable();
+ if ( dual_chip )
+ {
+ if ( !reinit )
+ {
+ result = ym2608[1].set_rate( fm_rate, ym2608_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ym2608[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2608[1].enable();
+ }
+ }
+ if ( ym2413_rate )
+ {
+ bool dual_chip = !!(header().ym2413_rate[3] & 0x40);
+ double gain = dual_chip ? 0.5 : 1.0;
+ double fm_rate = ym2413_rate / 72.0;
+ int result;
+ if ( !reinit )
+ {
+ result = ym2413[0].set_rate( fm_rate, ym2413_rate );
+ if ( result == 2 )
+ return "YM2413 FM sound not supported";
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ym2413[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2413[0].enable();
+ if ( dual_chip )
+ {
+ if ( !reinit )
+ {
+ result = ym2413[1].set_rate( fm_rate, ym2413_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ym2413[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2413[1].enable();
+ }
+ }
+ if ( ym2151_rate )
+ {
+ bool dual_chip = !!(header().ym2151_rate[3] & 0x40);
+ double gain = dual_chip ? 0.5 : 1.0;
+ double fm_rate = ym2151_rate / 64.0;
+ int result;
+ if ( !reinit )
+ {
+ result = ym2151[0].set_rate( fm_rate, ym2151_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ym2151[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2151[0].enable();
+ if ( dual_chip )
+ {
+ if ( !reinit )
+ {
+ result = ym2151[1].set_rate( fm_rate, ym2151_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( ym2151[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2151[1].enable();
+ }
+ }
+ if ( ym2203_rate )
+ {
+ bool dual_chip = !!(header().ym2203_rate[3] & 0x40);
+ double gain = dual_chip ? 0.5 : 1.0;
+ double fm_rate = ym2203_rate / 72.0;
+ int result;
+ if ( !reinit )
+ {
+ result = ym2203[0].set_rate( fm_rate, ym2203_rate );
+ CHECK_ALLOC ( !result );
+ }
+ RETURN_ERR( ym2203[0].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2203[0].enable();
+ if ( dual_chip )
+ {
+ if ( !reinit )
+ {
+ result = ym2203[1].set_rate( fm_rate, ym2203_rate );
+ CHECK_ALLOC ( !result );
+ }
+ RETURN_ERR( ym2203[1].setup( fm_rate / vgm_rate, 0.85, gain ) );
+ ym2203[1].enable();
+ }
+ }
+
+ if ( segapcm_rate )
+ {
+ double pcm_rate = segapcm_rate / 128.0;
+ if ( !reinit )
+ {
+ int result = segapcm.set_rate( get_le32( header().segapcm_reg ) );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( segapcm.setup( pcm_rate / vgm_rate, 0.85, 1.5 ) );
+ segapcm.enable();
+ }
+ if ( rf5c68_rate )
+ {
+ double pcm_rate = rf5c68_rate / 384.0;
+ if ( !reinit )
+ {
+ int result = rf5c68.set_rate();
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( rf5c68.setup( pcm_rate / vgm_rate, 0.85, 0.6875 ) );
+ rf5c68.enable();
+ }
+ if ( rf5c164_rate )
+ {
+ double pcm_rate = rf5c164_rate / 384.0;
+ if ( !reinit )
+ {
+ int result = rf5c164.set_rate( rf5c164_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( rf5c164.setup( pcm_rate / vgm_rate, 0.85, 0.5 ) );
+ rf5c164.enable();
+ }
+ if ( pwm_rate )
+ {
+ double pcm_rate = 22020.0;
+ if ( !reinit )
+ {
+ int result = pwm.set_rate( pwm_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( pwm.setup( pcm_rate / vgm_rate, 0.85, 0.875 ) );
+ pwm.enable();
+ }
+ if ( okim6258_rate )
+ {
+ if ( !reinit )
+ {
+ okim6258_hz = okim6258.set_rate( okim6258_rate, header().okim6258_flags & 0x03, ( header().okim6258_flags & 0x04 ) >> 2, ( header().okim6258_flags & 0x08 ) >> 3 );
+ CHECK_ALLOC( okim6258_hz );
+ }
+ RETURN_ERR( okim6258.setup( (double)okim6258_hz / vgm_rate, 0.85, 1.0 ) );
+ okim6258.enable();
+ }
+ if ( okim6295_rate )
+ {
+ // moo
+ Mem_File_Reader rdr( file_begin(), file_size() );
+ Music_Emu * vgm = gme_vgm_type->new_info();
+ track_info_t info;
+ vgm->load( rdr );
+ vgm->track_info( &info, 0 );
+ delete vgm;
+
+ bool is_cp_system = strncmp( info.system, "CP", 2 ) == 0;
+ bool dual_chip = !!( header().okim6295_rate[3] & 0x40 );
+ double gain = is_cp_system ? 0.4296875 : 1.0;
+ if ( dual_chip ) gain *= 0.5;
+ if ( !reinit )
+ {
+ okim6295_hz = okim6295[0].set_rate( okim6295_rate );
+ CHECK_ALLOC( okim6295_hz );
+ }
+ RETURN_ERR( okim6295[0].setup( (double)okim6295_hz / vgm_rate, 0.85, gain ) );
+ okim6295[0].enable();
+ if ( dual_chip )
+ {
+ if ( !reinit )
+ {
+ int result = okim6295[1].set_rate( okim6295_rate );
+ CHECK_ALLOC( result );
+ }
+ RETURN_ERR( okim6295[1].setup( (double)okim6295_hz / vgm_rate, 0.85, gain ) );
+ okim6295[1].enable();
+ }
+ }
+ if ( c140_rate )
+ {
+ double pcm_rate = c140_rate;
+ if ( !reinit )
+ {
+ int result = c140.set_rate( header().c140_type, c140_rate, c140_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( c140.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) );
+ c140.enable();
+ }
+ if ( k051649_rate )
+ {
+ double pcm_rate = k051649_rate / 16.0;
+ if ( !reinit )
+ {
+ int result = k051649.set_rate( k051649_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( k051649.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) );
+ k051649.enable();
+ }
+ if ( k053260_rate )
+ {
+ double pcm_rate = k053260_rate / 32.0;
+ if ( !reinit )
+ {
+ int result = k053260.set_rate( k053260_rate );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( k053260.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) );
+ k053260.enable();
+ }
+ if ( k054539_rate )
+ {
+ double pcm_rate = k054539_rate;
+ if ( !reinit )
+ {
+ int result = k054539.set_rate( k054539_rate, header().k054539_flags );
+ CHECK_ALLOC( !result );
+ }
+ RETURN_ERR( k054539.setup( pcm_rate / vgm_rate, 0.85, 1.0 ) );
+ k054539.enable();
+ }
+ if ( ymz280b_rate )
+ {
+ if ( !reinit )
+ {
+ ymz280b_hz = ymz280b.set_rate( ymz280b_rate );
+ CHECK_ALLOC( ymz280b_hz );
+ }
+ RETURN_ERR( ymz280b.setup( (double)ymz280b_hz / vgm_rate, 0.85, 0.59375 ) );
+ ymz280b.enable();
+ }
+ if ( qsound_rate )
+ {
+ double pcm_rate = (double)qsound_rate / 166.0;
+ if ( !reinit )
+ {
+ int result = qsound[0].set_rate( qsound_rate );
+ CHECK_ALLOC( result );
+ }
+ qsound[0].set_sample_rate( vgm_rate );
+ RETURN_ERR( qsound[0].setup( 1.0, 0.85, 1.0 ) );
+ qsound[0].enable();
+ }
+
+ fm_rate = *rate;
+
+ return blargg_ok;
+}
+
+void Vgm_Core::start_track()
+{
+ psg[0].reset( get_le16( header().noise_feedback ), header().noise_width );
+ psg[1].reset( get_le16( header().noise_feedback ), header().noise_width );
+ ay[0].reset();
+ ay[1].reset();
+ huc6280[0].reset();
+ huc6280[1].reset();
+
+ blip_buf[0] = stereo_buf[0].center();
+ blip_buf[1] = blip_buf[0];
+
+ dac_disabled[0] = -1;
+ dac_disabled[1] = -1;
+ pos = file_begin() + header().size();
+ dac_amp[0] = -1;
+ dac_amp[1] = -1;
+ vgm_time = 0;
+ int data_offset = get_le32( header().data_offset );
+ check( data_offset );
+ if ( data_offset )
+ pos += data_offset + offsetof (header_t,data_offset) - header().size();
+ pcm_pos = pos;
+
+ if ( uses_fm() )
+ {
+ if ( rf5c68.enabled() )
+ rf5c68.reset();
+
+ if ( rf5c164.enabled() )
+ rf5c164.reset();
+
+ if ( segapcm.enabled() )
+ segapcm.reset();
+
+ if ( pwm.enabled() )
+ pwm.reset();
+
+ if ( okim6258.enabled() )
+ okim6258.reset();
+
+ if ( okim6295[0].enabled() )
+ okim6295[0].reset();
+
+ if ( okim6295[1].enabled() )
+ okim6295[1].reset();
+
+ if ( k051649.enabled() )
+ k051649.reset();
+
+ if ( k053260.enabled() )
+ k053260.reset();
+
+ if ( k054539.enabled() )
+ k054539.reset();
+
+ if ( c140.enabled() )
+ c140.reset();
+
+ if ( ym2151[0].enabled() )
+ ym2151[0].reset();
+
+ if ( ym2151[1].enabled() )
+ ym2151[1].reset();
+
+ if ( ym2203[0].enabled() )
+ ym2203[0].reset();
+
+ if ( ym2203[1].enabled() )
+ ym2203[1].reset();
+
+ if ( ym2413[0].enabled() )
+ ym2413[0].reset();
+
+ if ( ym2413[1].enabled() )
+ ym2413[1].reset();
+
+ if ( ym2612[0].enabled() )
+ ym2612[0].reset();
+
+ if ( ym2612[1].enabled() )
+ ym2612[1].reset();
+
+ if ( ym2610[0].enabled() )
+ ym2610[0].reset();
+
+ if ( ym2610[1].enabled() )
+ ym2610[1].reset();
+
+ if ( ym2608[0].enabled() )
+ ym2608[0].reset();
+
+ if ( ym2608[1].enabled() )
+ ym2608[0].reset();
+
+ if ( ym3812[0].enabled() )
+ ym3812[0].reset();
+
+ if ( ym3812[1].enabled() )
+ ym3812[1].reset();
+
+ if ( ymf262[0].enabled() )
+ ymf262[0].reset();
+
+ if ( ymf262[1].enabled() )
+ ymf262[1].reset();
+
+ if ( ymz280b.enabled() )
+ ymz280b.reset();
+
+ if ( qsound[0].enabled() )
+ qsound[0].reset();
+
+ if ( qsound[1].enabled() )
+ qsound[1].reset();
+
+ stereo_buf[0].clear();
+ stereo_buf[1].clear();
+ stereo_buf[2].clear();
+ }
+
+ for ( unsigned i = 0; i < DacCtrlUsed; i++ )
+ {
+ device_reset_daccontrol( dac_control [i] );
+ DacCtrlTime[DacCtrlMap[i]] = 0;
+ }
+
+ for ( unsigned i = 0; i < PCM_BANK_COUNT; i++)
+ {
+ // reset PCM Bank, but not the data
+ // (this way I don't need to decompress the data again when restarting)
+ PCMBank [i].DataPos = 0;
+ PCMBank [i].BnkPos = 0;
+ }
+ PCMTbl.EntryCount = 0;
+
+ fm_time_offset = 0;
+ ay_time_offset = 0;
+ huc6280_time_offset = 0;
+
+ dac_control_recursion = 0;
+}
+
+inline Vgm_Core::fm_time_t Vgm_Core::to_fm_time( vgm_time_t t ) const
+{
+ return (t * fm_time_factor + fm_time_offset) >> fm_time_bits;
+}
+
+inline blip_time_t Vgm_Core::to_psg_time( vgm_time_t t ) const
+{
+ return (t * blip_time_factor) >> blip_time_bits;
+}
+
+inline blip_time_t Vgm_Core::to_ay_time( vgm_time_t t ) const
+{
+ return (t * blip_ay_time_factor) >> blip_time_bits;
+}
+
+inline blip_time_t Vgm_Core::to_huc6280_time( vgm_time_t t ) const
+{
+ return (t * blip_huc6280_time_factor) >> blip_time_bits;
+}
+
+void Vgm_Core::write_pcm( vgm_time_t vgm_time, int chip, int amp )
+{
+ chip = !!chip;
+ if ( blip_buf[chip] )
+ {
+ check( amp >= 0 );
+ blip_time_t blip_time = to_psg_time( vgm_time );
+ int old = dac_amp[chip];
+ int delta = amp - old;
+ dac_amp[chip] = amp;
+ blip_buf[chip]->set_modified();
+ if ( old >= 0 ) // first write is ignored, to avoid click
+ pcm.offset_inline( blip_time, delta, blip_buf[chip] );
+ else
+ dac_amp[chip] |= dac_disabled[chip];
+ }
+}
+
+blip_time_t Vgm_Core::run( vgm_time_t end_time )
+{
+ vgm_time_t vgm_time = this->vgm_time;
+ vgm_time_t vgm_loop_time = ~0;
+ int ChipID;
+ byte const* pos = this->pos;
+ if ( pos > file_end() )
+ set_warning( "Stream lacked end event" );
+
+ while ( vgm_time < end_time && pos < file_end() )
+ {
+ // TODO: be sure there are enough bytes left in stream for particular command
+ // so we don't read past end
+ switch ( *pos++ )
+ {
+ case cmd_end:
+ if ( vgm_loop_time == ~0 ) vgm_loop_time = vgm_time;
+ else if ( vgm_loop_time == vgm_time ) loop_begin = file_end(); // XXX some files may loop forever on a region without any delay commands
+ pos = loop_begin; // if not looped, loop_begin == file_end()
+ if ( pos != file_end() ) has_looped = true;
+ break;
+
+ case cmd_delay_735:
+ vgm_time += 735;
+ break;
+
+ case cmd_delay_882:
+ vgm_time += 882;
+ break;
+
+ case cmd_gg_stereo:
+ psg[0].write_ggstereo( to_psg_time( vgm_time ), *pos++ );
+ break;
+
+ case cmd_gg_stereo_2:
+ psg[1].write_ggstereo( to_psg_time( vgm_time ), *pos++ );
+ break;
+
+ case cmd_psg:
+ psg[0].write_data( to_psg_time( vgm_time ), *pos++ );
+ break;
+
+ case cmd_psg_2:
+ psg[1].write_data( to_psg_time( vgm_time ), *pos++ );
+ break;
+
+ case cmd_ay8910:
+ ChipID = !!(pos [0] & 0x80);
+ chip_reg_write( vgm_time, 0x12, ChipID, 0x00, pos [0] & 0x7F, pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_delay:
+ vgm_time += pos [1] * 0x100 + pos [0];
+ pos += 2;
+ break;
+
+ case cmd_byte_delay:
+ vgm_time += *pos++;
+ break;
+
+ case cmd_segapcm_write:
+ if ( get_le32( header().segapcm_rate ) > 0 )
+ if ( run_segapcm( to_fm_time( vgm_time ) ) )
+ segapcm.write( get_le16( pos ), pos [2] );
+ pos += 3;
+ break;
+
+ case cmd_rf5c68:
+ if ( run_rf5c68( to_fm_time( vgm_time ) ) )
+ rf5c68.write( pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_rf5c68_mem:
+ if ( run_rf5c68( to_fm_time( vgm_time ) ) )
+ rf5c68.write_mem( get_le16( pos ), pos [2] );
+ pos += 3;
+ break;
+
+ case cmd_rf5c164:
+ if ( run_rf5c164( to_fm_time( vgm_time ) ) )
+ rf5c164.write( pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_rf5c164_mem:
+ if ( run_rf5c164( to_fm_time( vgm_time ) ) )
+ rf5c164.write_mem( get_le16( pos ), pos [2] );
+ pos += 3;
+ break;
+
+ case cmd_pwm:
+ chip_reg_write( vgm_time, 0x11, 0x00, pos [0] >> 4, pos [0] & 0x0F, pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_c140:
+ if ( get_le32( header().c140_rate ) > 0 )
+ if ( run_c140( to_fm_time( vgm_time ) ) )
+ c140.write( get_be16( pos ), pos [2] );
+ pos += 3;
+ break;
+
+ case cmd_ym2151:
+ chip_reg_write( vgm_time, 0x03, 0x00, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2151_2:
+ chip_reg_write( vgm_time, 0x03, 0x01, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2203:
+ chip_reg_write( vgm_time, 0x06, 0x00, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2203_2:
+ chip_reg_write( vgm_time, 0x06, 0x01, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2413:
+ chip_reg_write( vgm_time, 0x01, 0x00, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2413_2:
+ chip_reg_write( vgm_time, 0x01, 0x01, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym3812:
+ chip_reg_write( vgm_time, 0x09, 0x00, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym3812_2:
+ chip_reg_write( vgm_time, 0x09, 0x01, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ymf262_port0:
+ chip_reg_write( vgm_time, 0x0C, 0x00, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ymf262_2_port0:
+ chip_reg_write( vgm_time, 0x0C, 0x01, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ymf262_port1:
+ chip_reg_write( vgm_time, 0x0C, 0x00, 0x01, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ymf262_2_port1:
+ chip_reg_write( vgm_time, 0x0C, 0x01, 0x01, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ymz280b:
+ chip_reg_write( vgm_time, 0x0F, 0x00, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2612_port0:
+ chip_reg_write( vgm_time, 0x02, 0x00, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2612_2_port0:
+ chip_reg_write( vgm_time, 0x02, 0x01, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2612_port1:
+ chip_reg_write( vgm_time, 0x02, 0x00, 0x01, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2612_2_port1:
+ chip_reg_write( vgm_time, 0x02, 0x01, 0x01, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2610_port0:
+ chip_reg_write( vgm_time, 0x08, 0x00, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2610_2_port0:
+ chip_reg_write( vgm_time, 0x08, 0x01, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2610_port1:
+ chip_reg_write( vgm_time, 0x08, 0x00, 0x01, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2610_2_port1:
+ chip_reg_write( vgm_time, 0x08, 0x01, 0x01, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2608_port0:
+ chip_reg_write( vgm_time, 0x07, 0x00, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2608_2_port0:
+ chip_reg_write( vgm_time, 0x07, 0x01, 0x00, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2608_port1:
+ chip_reg_write( vgm_time, 0x07, 0x00, 0x01, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_ym2608_2_port1:
+ chip_reg_write( vgm_time, 0x07, 0x01, 0x01, pos [0], pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_okim6258_write:
+ chip_reg_write( vgm_time, 0x17, 0x00, 0x00, pos [0] & 0x7F, pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_okim6295_write:
+ ChipID = (pos [0] & 0x80) ? 1 : 0;
+ chip_reg_write( vgm_time, 0x18, ChipID, 0x00, pos [0] & 0x7F, pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_huc6280_write:
+ ChipID = (pos [0] & 0x80) ? 1 : 0;
+ chip_reg_write( vgm_time, 0x1B, ChipID, 0x00, pos [0] & 0x7F, pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_k051649_write:
+ chip_reg_write( vgm_time, 0x19, 0x00, pos [0] & 0x7F, pos [1], pos [2] );
+ pos += 3;
+ break;
+
+ case cmd_k053260_write:
+ chip_reg_write( vgm_time, 0x1D, 0x00, 0x00, pos [0] & 0x7F, pos [1] );
+ pos += 2;
+ break;
+
+ case cmd_k054539_write:
+ chip_reg_write( vgm_time, 0x1A, 0x00, pos [0] & 0x7F, pos [1], pos [2] );
+ pos += 3;
+ break;
+
+ case cmd_qsound_write:
+ chip_reg_write( vgm_time, 0x1F, 0x00, pos [0], pos [1], pos [2] );
+ pos += 3;
+ break;
+
+ case cmd_dacctl_setup:
+ if ( run_dac_control( vgm_time ) )
+ {
+ unsigned chip = pos [0];
+ if ( chip < 0xFF )
+ {
+ if ( ! DacCtrl [chip].Enable )
+ {
+ dac_control_grow( chip );
+ DacCtrl [chip].Enable = true;
+ }
+ daccontrol_setup_chip( dac_control [DacCtrlMap [chip]], pos [1] & 0x7F, ( pos [1] & 0x80 ) >> 7, get_be16( pos + 2 ) );
+ }
+ }
+ pos += 4;
+ break;
+
+ case cmd_dacctl_data:
+ if ( run_dac_control( vgm_time ) )
+ {
+ unsigned chip = pos [0];
+ if ( chip < 0xFF && DacCtrl [chip].Enable )
+ {
+ DacCtrl [chip].Bank = pos [1];
+ if ( DacCtrl [chip].Bank >= 0x40 )
+ DacCtrl [chip].Bank = 0x00;
+
+ VGM_PCM_BANK * TempPCM = &PCMBank [DacCtrl [chip].Bank];
+ daccontrol_set_data( dac_control [DacCtrlMap [chip]], TempPCM->Data, TempPCM->DataSize, pos [2], pos [3] );
+ }
+ }
+ pos += 4;
+ break;
+ case cmd_dacctl_freq:
+ if ( run_dac_control( vgm_time ) )
+ {
+ unsigned chip = pos [0];
+ if ( chip < 0xFF && DacCtrl [chip].Enable )
+ {
+ daccontrol_set_frequency( dac_control [DacCtrlMap [chip]], get_le32( pos + 1 ) );
+ }
+ }
+ pos += 5;
+ break;
+ case cmd_dacctl_play:
+ if ( run_dac_control( vgm_time ) )
+ {
+ unsigned chip = pos [0];
+ if ( chip < 0xFF && DacCtrl [chip].Enable && PCMBank [DacCtrl [chip].Bank].BankCount )
+ {
+ daccontrol_start( dac_control [DacCtrlMap [chip]], get_le32( pos + 1 ), pos [5], get_le32( pos + 6 ) );
+ }
+ }
+ pos += 10;
+ break;
+ case cmd_dacctl_stop:
+ if ( run_dac_control( vgm_time ) )
+ {
+ unsigned chip = pos [0];
+ if ( chip < 0xFF && DacCtrl [chip].Enable )
+ {
+ daccontrol_stop( dac_control [DacCtrlMap [chip]] );
+ }
+ else if ( chip == 0xFF )
+ {
+ for ( unsigned i = 0; i < DacCtrlUsed; i++ )
+ {
+ daccontrol_stop( dac_control [i] );
+ }
+ }
+ }
+ pos++;
+ break;
+ case cmd_dacctl_playblock:
+ if ( run_dac_control( vgm_time ) )
+ {
+ unsigned chip = pos [0];
+ if ( chip < 0xFF && DacCtrl [chip].Enable && PCMBank [DacCtrl [chip].Bank].BankCount )
+ {
+ VGM_PCM_BANK * TempPCM = &PCMBank [DacCtrl [chip].Bank];
+ unsigned block_number = get_le16( pos + 1 );
+ if ( block_number >= TempPCM->BankCount )
+ block_number = 0;
+ VGM_PCM_DATA * TempBnk = &TempPCM->Bank [block_number];
+ unsigned flags = DCTRL_LMODE_BYTES | ((pos [4] & 1) << 7);
+ daccontrol_start( dac_control [DacCtrlMap [chip]], TempBnk->DataStart, flags, TempBnk->DataSize );
+ }
+ }
+ pos += 4;
+ break;
+
+ case cmd_data_block: {
+ check( *pos == cmd_end );
+ int type = pos [1];
+ int size = get_le32( pos + 2 );
+ int chipid = 0;
+ if ( size & 0x80000000 )
+ {
+ size &= 0x7FFFFFFF;
+ chipid = 1;
+ }
+ pos += 6;
+ switch ( type & 0xC0 )
+ {
+ case pcm_block_type:
+ case pcm_aux_block_type:
+ AddPCMData( type, size, pos );
+ break;
+
+ case rom_block_type:
+ if ( size >= 8 )
+ {
+ int rom_size = get_le32( pos );
+ int data_start = get_le32( pos + 4 );
+ int data_size = size - 8;
+ void * rom_data = ( void * ) ( pos + 8 );
+
+ switch ( type )
+ {
+ case rom_segapcm:
+ if ( segapcm.enabled() )
+ segapcm.write_rom( rom_size, data_start, data_size, rom_data );
+ break;
+
+ case rom_ym2608_deltat:
+ if ( ym2608[chipid].enabled() )
+ {
+ ym2608[chipid].write_rom( 0x02, rom_size, data_start, data_size, rom_data );
+ }
+ break;
+
+ case rom_ym2610_adpcm:
+ case rom_ym2610_deltat:
+ if ( ym2610[chipid].enabled() )
+ {
+ int rom_id = 0x01 + ( type - rom_ym2610_adpcm );
+ ym2610[chipid].write_rom( rom_id, rom_size, data_start, data_size, rom_data );
+ }
+ break;
+
+ case rom_ymz280b:
+ if ( ymz280b.enabled() )
+ ymz280b.write_rom( rom_size, data_start, data_size, rom_data );
+ break;
+
+ case rom_okim6295:
+ if ( okim6295[chipid].enabled() )
+ okim6295[chipid].write_rom( rom_size, data_start, data_size, rom_data );
+ break;
+
+ case rom_k054539:
+ if ( k054539.enabled() )
+ k054539.write_rom( rom_size, data_start, data_size, rom_data );
+ break;
+
+ case rom_c140:
+ if ( c140.enabled() )
+ c140.write_rom( rom_size, data_start, data_size, rom_data );
+ break;
+
+ case rom_k053260:
+ if ( k053260.enabled() )
+ k053260.write_rom( rom_size, data_start, data_size, rom_data );
+ break;
+
+ case rom_qsound:
+ if ( qsound[chipid].enabled() )
+ qsound[chipid].write_rom( rom_size, data_start, data_size, rom_data );
+ break;
+ }
+ }
+ break;
+
+ case ram_block_type:
+ if ( size >= 2 )
+ {
+ int data_start = get_le16( pos );
+ int data_size = size - 2;
+ void * ram_data = ( void * ) ( pos + 2 );
+
+ switch ( type )
+ {
+ case ram_rf5c68:
+ if ( rf5c68.enabled() )
+ rf5c68.write_ram( data_start, data_size, ram_data );
+ break;
+
+ case ram_rf5c164:
+ if ( rf5c164.enabled() )
+ rf5c164.write_ram( data_start, data_size, ram_data );
+ break;
+ }
+ }
+ break;
+ }
+ pos += size;
+ break;
+ }
+
+ case cmd_ram_block: {
+ check( *pos == cmd_end );
+ int type = pos[ 1 ];
+ int data_start = get_le24( pos + 2 );
+ int data_addr = get_le24( pos + 5 );
+ int data_size = get_le24( pos + 8 );
+ if ( !data_size ) data_size += 0x01000000;
+ void * data_ptr = (void *) GetPointerFromPCMBank( type, data_start );
+ switch ( type )
+ {
+ case rf5c68_ram_block:
+ if ( rf5c68.enabled() )
+ rf5c68.write_ram( data_addr, data_size, data_ptr );
+ break;
+
+ case rf5c164_ram_block:
+ if ( rf5c164.enabled() )
+ rf5c164.write_ram( data_addr, data_size, data_ptr );
+ break;
+ }
+ pos += 11;
+ break;
+ }
+
+ case cmd_pcm_seek:
+ pcm_pos = GetPointerFromPCMBank( 0, get_le32( pos ) );
+ pos += 4;
+ break;
+
+ default:
+ int cmd = pos [-1];
+ switch ( cmd & 0xF0 )
+ {
+ case cmd_pcm_delay:
+ chip_reg_write( vgm_time, 0x02, 0x00, 0x00, ym2612_dac_port, *pcm_pos++ );
+ vgm_time += cmd & 0x0F;
+ break;
+
+ case cmd_short_delay:
+ vgm_time += (cmd & 0x0F) + 1;
+ break;
+
+ case 0x50:
+ pos += 2;
+ break;
+
+ default:
+ pos += command_len( cmd ) - 1;
+ set_warning( "Unknown stream event" );
+ }
+ }
+ }
+ vgm_time -= end_time;
+ this->pos = pos;
+ this->vgm_time = vgm_time;
+
+ return to_psg_time( end_time );
+}
+
+blip_time_t Vgm_Core::run_psg( int msec )
+{
+ blip_time_t t = run( msec * vgm_rate / 1000 );
+ psg[0].end_frame( t );
+ psg[1].end_frame( t );
+ return t;
+}
+
+int Vgm_Core::play_frame( blip_time_t blip_time, int sample_count, blip_sample_t out [] )
+{
+ // to do: timing is working mostly by luck
+ int min_pairs = (unsigned) sample_count / 2;
+ int vgm_time = (min_pairs << fm_time_bits) / fm_time_factor - 1;
+ assert( to_fm_time( vgm_time ) <= min_pairs );
+ int pairs;
+ while ( (pairs = to_fm_time( vgm_time )) < min_pairs )
+ vgm_time++;
+ //dprintf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs );
+
+ memset( out, 0, pairs * stereo * sizeof *out );
+
+ if ( ymf262[0].enabled() )
+ {
+ ymf262[0].begin_frame( out );
+ if ( ymf262[1].enabled() )
+ {
+ ymf262[1].begin_frame( out );
+ }
+ }
+ if ( ym3812[0].enabled() )
+ {
+ ym3812[0].begin_frame( out );
+ if ( ym3812[1].enabled() )
+ {
+ ym3812[1].begin_frame( out );
+ }
+ }
+ if ( ym2612[0].enabled() )
+ {
+ ym2612[0].begin_frame( out );
+ if ( ym2612[1].enabled() )
+ {
+ ym2612[1].begin_frame( out );
+ }
+ }
+ if ( ym2610[0].enabled() )
+ {
+ ym2610[0].begin_frame( out );
+ if ( ym2610[1].enabled() )
+ {
+ ym2610[1].begin_frame( out );
+ }
+ }
+ if ( ym2608[0].enabled() )
+ {
+ ym2608[0].begin_frame( out );
+ if ( ym2608[1].enabled() )
+ {
+ ym2608[1].begin_frame( out );
+ }
+ }
+ if ( ym2413[0].enabled() )
+ {
+ ym2413[0].begin_frame( out );
+ if ( ym2413[1].enabled() )
+ {
+ ym2413[1].begin_frame( out );
+ }
+ }
+ if ( ym2203[0].enabled() )
+ {
+ ym2203[0].begin_frame( out );
+ if ( ym2203[1].enabled() )
+ {
+ ym2203[1].begin_frame( out );
+ }
+ }
+ if ( ym2151[0].enabled() )
+ {
+ ym2151[0].begin_frame( out );
+ if ( ym2151[1].enabled() )
+ {
+ ym2151[1].begin_frame( out );
+ }
+ }
+
+ if ( c140.enabled() )
+ {
+ c140.begin_frame( out );
+ }
+ if ( segapcm.enabled() )
+ {
+ segapcm.begin_frame( out );
+ }
+ if ( rf5c68.enabled() )
+ {
+ rf5c68.begin_frame( out );
+ }
+ if ( rf5c164.enabled() )
+ {
+ rf5c164.begin_frame( out );
+ }
+ if ( pwm.enabled() )
+ {
+ pwm.begin_frame( out );
+ }
+ if ( okim6258.enabled() )
+ {
+ okim6258.begin_frame( out );
+ }
+ if ( okim6295[0].enabled() )
+ {
+ okim6295[0].begin_frame( out );
+ if ( okim6295[1].enabled() )
+ {
+ okim6295[1].begin_frame( out );
+ }
+ }
+ if ( k051649.enabled() )
+ {
+ k051649.begin_frame( out );
+ }
+ if ( k053260.enabled() )
+ {
+ k053260.begin_frame( out );
+ }
+ if ( k054539.enabled() )
+ {
+ k054539.begin_frame( out );
+ }
+ if ( ymz280b.enabled() )
+ {
+ ymz280b.begin_frame( out );
+ }
+ if ( qsound[0].enabled() )
+ {
+ qsound[0].begin_frame( out );
+ if ( qsound[1].enabled() )
+ {
+ qsound[1].begin_frame( out );
+ }
+ }
+
+ run( vgm_time );
+
+ run_dac_control( vgm_time );
+
+ run_ymf262( 0, pairs ); run_ymf262( 1, pairs );
+ run_ym3812( 0, pairs ); run_ym3812( 1, pairs );
+ run_ym2612( 0, pairs ); run_ym2612( 1, pairs );
+ run_ym2610( 0, pairs ); run_ym2610( 1, pairs );
+ run_ym2608( 0, pairs ); run_ym2608( 1, pairs );
+ run_ym2413( 0, pairs ); run_ym2413( 1, pairs );
+ run_ym2203( 0, pairs ); run_ym2203( 1, pairs );
+ run_ym2151( 0, pairs ); run_ym2151( 1, pairs );
+ run_c140( pairs );
+ run_segapcm( pairs );
+ run_rf5c68( pairs );
+ run_rf5c164( pairs );
+ run_pwm( pairs );
+ run_okim6258( pairs );
+ run_okim6295( 0, pairs ); run_okim6295( 1, pairs );
+ run_k051649( pairs );
+ run_k053260( pairs );
+ run_k054539( pairs );
+ run_ymz280b( pairs );
+ run_qsound( 0, pairs ); run_qsound( 1, pairs );
+
+ fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) - (pairs << fm_time_bits);
+
+ psg[0].end_frame( blip_time );
+ psg[1].end_frame( blip_time );
+
+ ay_time_offset = (vgm_time * blip_ay_time_factor + ay_time_offset) - (pairs << blip_time_bits);
+
+ blip_time_t ay_end_time = to_ay_time( vgm_time );
+ ay[0].end_frame( ay_end_time );
+ ay[1].end_frame( ay_end_time );
+
+ huc6280_time_offset = (vgm_time * blip_huc6280_time_factor + huc6280_time_offset) - (pairs << blip_time_bits);
+
+ blip_time_t huc6280_end_time = to_huc6280_time( vgm_time );
+ huc6280[0].end_frame( huc6280_end_time );
+ huc6280[1].end_frame( huc6280_end_time );
+
+ memset( DacCtrlTime, 0, sizeof(DacCtrlTime) );
+
+ return pairs * stereo;
+}
diff --git a/Frameworks/GME/gme/Vgm_Core.h b/Frameworks/GME/gme/Vgm_Core.h
new file mode 100644
index 000000000..c5c53ea7c
--- /dev/null
+++ b/Frameworks/GME/gme/Vgm_Core.h
@@ -0,0 +1,346 @@
+// Sega VGM music file emulator core
+
+// Game_Music_Emu $vers
+#ifndef VGM_CORE_H
+#define VGM_CORE_H
+
+#include "Gme_Loader.h"
+#include "Ymz280b_Emu.h"
+#include "Ymf262_Emu.h"
+#include "Ym2612_Emu.h"
+#include "Ym2610b_Emu.h"
+#include "Ym2608_Emu.h"
+#include "Ym3812_Emu.h"
+#include "Ym2413_Emu.h"
+#include "Ym2151_Emu.h"
+#include "C140_Emu.h"
+#include "SegaPcm_Emu.h"
+#include "Rf5C68_Emu.h"
+#include "Rf5C164_Emu.h"
+#include "Pwm_Emu.h"
+#include "Okim6258_Emu.h"
+#include "Okim6295_Emu.h"
+#include "K051649_Emu.h"
+#include "K053260_Emu.h"
+#include "K054539_Emu.h"
+#include "Qsound_Apu.h"
+#include "Ym2203_Emu.h"
+#include "Ay_Apu.h"
+#include "Hes_Apu.h"
+#include "Sms_Apu.h"
+#include "Multi_Buffer.h"
+#include "Chip_Resampler.h"
+
+ template
+ class Chip_Emu : public Emu {
+ int last_time;
+ short* out;
+ enum { disabled_time = -1 };
+ public:
+ Chip_Emu() { last_time = disabled_time; out = NULL; }
+ void enable( bool b = true ) { last_time = b ? 0 : disabled_time; }
+ bool enabled() const { return last_time != disabled_time; }
+ void begin_frame( short* buf ) { out = buf; last_time = 0; }
+
+ int run_until( int time )
+ {
+ int count = time - last_time;
+ if ( count > 0 )
+ {
+ if ( last_time < 0 )
+ return false;
+ last_time = time;
+ short* p = out;
+ out += count * Emu::out_chan_count;
+ Emu::run( count, p );
+ }
+ return true;
+ }
+ };
+
+class Vgm_Core : public Gme_Loader {
+public:
+
+ // VGM file header
+ struct header_t
+ {
+ enum { size_min = 0x40 };
+ enum { size_151 = 0x80 };
+ enum { size_max = 0xC0 };
+
+ char tag [4]; // 0x00
+ byte data_size [4]; // 0x04
+ byte version [4]; // 0x08
+ byte psg_rate [4]; // 0x0C
+ byte ym2413_rate [4]; // 0x10
+ byte gd3_offset [4]; // 0x14
+ byte track_duration [4]; // 0x18
+ byte loop_offset [4]; // 0x1C
+ byte loop_duration [4]; // 0x20
+ byte frame_rate [4]; // 0x24 v1.01 V
+ byte noise_feedback [2]; // 0x28 v1.10 V
+ byte noise_width; // 0x2A
+ byte sn76489_flags; // 0x2B v1.51 <
+ byte ym2612_rate [4]; // 0x2C v1.10 V
+ byte ym2151_rate [4]; // 0x30
+ byte data_offset [4]; // 0x34 v1.50 V
+ byte segapcm_rate [4]; // 0x38 v1.51 V
+ byte segapcm_reg [4]; // 0x3C
+ byte rf5c68_rate [4]; // 0x40
+ byte ym2203_rate [4]; // 0x44
+ byte ym2608_rate [4]; // 0x48
+ byte ym2610_rate [4]; // 0x4C
+ byte ym3812_rate [4]; // 0x50
+ byte ym3526_rate [4]; // 0x54
+ byte y8950_rate [4]; // 0x58
+ byte ymf262_rate [4]; // 0x5C
+ byte ymf278b_rate [4]; // 0x60
+ byte ymf271_rate [4]; // 0x64
+ byte ymz280b_rate [4]; // 0x68
+ byte rf5c164_rate [4]; // 0x6C
+ byte pwm_rate [4]; // 0x70
+ byte ay8910_rate [4]; // 0x74
+ byte ay8910_type; // 0x78
+ byte ay8910_flags; // 0x79
+ byte ym2203_ay8910_flags;// 0x7A
+ byte ym2608_ay8910_flags;// 0x7B
+ byte volume_modifier; // 0x7C v1.60 V
+ byte reserved; // 0x7D
+ byte loop_base; // 0x7E
+ byte loop_modifier; // 0x7F v1.51 <
+ byte gbdmg_rate [4]; // 0x80 v1.61 V
+ byte nesapu_rate [4]; // 0x84
+ byte multipcm_rate [4]; // 0x88
+ byte upd7759_rate [4]; // 0x8C
+ byte okim6258_rate [4]; // 0x90
+ byte okim6258_flags; // 0x94
+ byte k054539_flags; // 0x95
+ byte c140_type; // 0x96
+ byte reserved_flags; // 0x97
+ byte okim6295_rate [4]; // 0x98
+ byte k051649_rate [4]; // 0x9C
+ byte k054539_rate [4]; // 0xA0
+ byte huc6280_rate [4]; // 0xA4
+ byte c140_rate [4]; // 0xA8
+ byte k053260_rate [4]; // 0xAC
+ byte pokey_rate [4]; // 0xB0
+ byte qsound_rate [4]; // 0xB4
+ byte reserved2 [4]; // 0xB8
+ byte extra_offset [4]; // 0xBC
+
+ // True if header has valid file signature
+ bool valid_tag() const;
+ int size() const;
+ void cleanup();
+ };
+
+ // Header for currently loaded file
+ header_t const& header() const { return _header; }
+
+ // Raw file data, for parsing GD3 tags
+ byte const* file_begin() const { return Gme_Loader::file_begin(); }
+ byte const* file_end () const { return Gme_Loader::file_end(); }
+
+ // If file uses FM, initializes FM sound emulator using *sample_rate. If
+ // *sample_rate is zero, sets *sample_rate to the proper accurate rate and
+ // uses that. The output of the FM sound emulator is resampled to the
+ // final sampling rate.
+ blargg_err_t init_chips( double* fm_rate, bool reinit = false );
+
+ // True if any FM chips are used by file. Always false until init_fm()
+ // is called.
+ bool uses_fm() const { return ym2612[0].enabled() || ym2413[0].enabled() || ym2151[0].enabled() || c140.enabled() ||
+ segapcm.enabled() || rf5c68.enabled() || rf5c164.enabled() || pwm.enabled() || okim6258.enabled() || okim6295[0].enabled() ||
+ k051649.enabled() || k053260.enabled() || k054539.enabled() || ym2203[0].enabled() || ym3812[0].enabled() || ymf262[0].enabled() ||
+ ymz280b.enabled() || ym2610[0].enabled() || ym2608[0].enabled() || qsound[0].enabled() ||
+ (header().ay8910_rate[0] | header().ay8910_rate[1] | header().ay8910_rate[2] | header().ay8910_rate[3]) ||
+ (header().huc6280_rate[0] | header().huc6280_rate[1] | header().huc6280_rate[2] | header().huc6280_rate[3]); }
+
+ // Adjusts music tempo, where 1.0 is normal. Can be changed while playing.
+ // Loading a file resets tempo to 1.0.
+ void set_tempo( double );
+
+ void set_sample_rate( int r ) { sample_rate = r; }
+
+ // Starts track
+ void start_track();
+
+ // Runs PSG-only VGM for msec and returns number of clocks it ran for
+ blip_time_t run_psg( int msec );
+
+ // Plays FM for at most count samples into *out, and returns number of
+ // samples actually generated (always even). Also runs PSG for blip_time.
+ int play_frame( blip_time_t blip_time, int count, blip_sample_t out [] );
+
+ // True if all of file data has been played
+ bool track_ended() const { return pos >= file_end(); }
+
+ // 0 for PSG and YM2612 DAC, 1 for AY, 2 for HuC6280
+ Stereo_Buffer stereo_buf[3];
+
+ // PCM sound is always generated here
+ Blip_Buffer * blip_buf[2];
+
+ // PSG sound chips, for assigning to Blip_Buffer, and setting volume and EQ
+ Sms_Apu psg[2];
+ Ay_Apu ay[2];
+ Hes_Apu huc6280[2];
+
+ // PCM synth, for setting volume and EQ
+ Blip_Synth_Fast pcm;
+
+ // FM sound chips
+ Chip_Resampler_Emu ymf262[2];
+ Chip_Resampler_Emu ym3812[2];
+ Chip_Resampler_Emu ym2612[2];
+ Chip_Resampler_Emu ym2610[2];
+ Chip_Resampler_Emu ym2608[2];
+ Chip_Resampler_Emu ym2413[2];
+ Chip_Resampler_Emu ym2151[2];
+ Chip_Resampler_Emu ym2203[2];
+
+ // PCM sound chips
+ Chip_Resampler_Emu c140;
+ Chip_Resampler_Emu segapcm;
+ Chip_Resampler_Emu rf5c68;
+ Chip_Resampler_Emu rf5c164;
+ Chip_Resampler_Emu pwm;
+ Chip_Resampler_Emu okim6258; int okim6258_hz;
+ Chip_Resampler_Emu okim6295[2]; int okim6295_hz;
+ Chip_Resampler_Emu k051649;
+ Chip_Resampler_Emu k053260;
+ Chip_Resampler_Emu k054539;
+ Chip_Resampler_Emu ymz280b; int ymz280b_hz;
+ Chip_Resampler_Emu qsound[2];
+
+ // DAC control
+ typedef struct daccontrol_data
+ {
+ bool Enable;
+ byte Bank;
+ } DACCTRL_DATA;
+
+ byte DacCtrlUsed;
+ byte DacCtrlUsg[0xFF];
+ DACCTRL_DATA DacCtrl[0xFF];
+ byte DacCtrlMap[0xFF];
+ int DacCtrlTime[0xFF];
+ void ** dac_control;
+
+ void dac_control_grow(byte chip_id);
+
+ int dac_control_recursion;
+
+ int run_dac_control( int time );
+
+public:
+ void chip_reg_write(unsigned Sample, byte ChipType, byte ChipID, byte Port, byte Offset, byte Data);
+
+// Implementation
+public:
+ Vgm_Core();
+ ~Vgm_Core();
+
+protected:
+ virtual blargg_err_t load_mem_( byte const [], int );
+
+private:
+ // blip_time_t // PSG clocks
+ typedef int vgm_time_t; // 44100 per second, REGARDLESS of sample rate
+ typedef int fm_time_t; // FM sample count
+
+ int sample_rate;
+ int vgm_rate; // rate of log, 44100 normally, adjusted by tempo
+ double fm_rate; // FM samples per second
+
+ header_t _header;
+
+ // VGM to FM time
+ int fm_time_factor;
+ int fm_time_offset;
+ fm_time_t to_fm_time( vgm_time_t ) const;
+
+ // VGM to PSG time
+ int blip_time_factor;
+ blip_time_t to_psg_time( vgm_time_t ) const;
+
+ int blip_ay_time_factor;
+ int ay_time_offset;
+ blip_time_t to_ay_time( vgm_time_t ) const;
+
+ int blip_huc6280_time_factor;
+ int huc6280_time_offset;
+ blip_time_t to_huc6280_time( vgm_time_t ) const;
+
+ // Current time and position in log
+ vgm_time_t vgm_time;
+ byte const* pos;
+ byte const* loop_begin;
+ bool has_looped;
+
+ // PCM
+ enum { PCM_BANK_COUNT = 0x40 };
+ typedef struct _vgm_pcm_bank_data
+ {
+ unsigned DataSize;
+ byte* Data;
+ unsigned DataStart;
+ } VGM_PCM_DATA;
+ typedef struct _vgm_pcm_bank
+ {
+ unsigned BankCount;
+ VGM_PCM_DATA* Bank;
+ unsigned DataSize;
+ byte* Data;
+ unsigned DataPos;
+ unsigned BnkPos;
+ } VGM_PCM_BANK;
+
+ typedef struct pcmbank_table
+ {
+ byte ComprType;
+ byte CmpSubType;
+ byte BitDec;
+ byte BitCmp;
+ unsigned EntryCount;
+ void* Entries;
+ } PCMBANK_TBL;
+
+ VGM_PCM_BANK PCMBank[PCM_BANK_COUNT];
+ PCMBANK_TBL PCMTbl;
+
+ void ReadPCMTable(unsigned DataSize, const byte* Data);
+ void AddPCMData(byte Type, unsigned DataSize, const byte* Data);
+ bool DecompressDataBlk(VGM_PCM_DATA* Bank, unsigned DataSize, const byte* Data);
+ const byte* GetPointerFromPCMBank(byte Type, unsigned DataPos);
+
+ byte const* pcm_pos; // current position in PCM data
+ int dac_amp[2];
+ int dac_disabled[2]; // -1 if disabled
+ void write_pcm( vgm_time_t, int chip, int amp );
+
+ blip_time_t run( vgm_time_t );
+ int run_ym2151( int chip, int time );
+ int run_ym2203( int chip, int time );
+ int run_ym2413( int chip, int time );
+ int run_ym2612( int chip, int time );
+ int run_ym3812( int chip, int time );
+ int run_ymf262( int chip, int time );
+ int run_ym2610( int chip, int time );
+ int run_ym2608( int chip, int time );
+ int run_ymz280b( int time );
+ int run_c140( int time );
+ int run_segapcm( int time );
+ int run_rf5c68( int time );
+ int run_rf5c164( int time );
+ int run_pwm( int time );
+ int run_okim6258( int time );
+ int run_okim6295( int chip, int time );
+ int run_k051649( int time );
+ int run_k053260( int time );
+ int run_k054539( int time );
+ int run_qsound( int chip, int time );
+ void update_fm_rates( int* ym2151_rate, int* ym2413_rate, int* ym2612_rate ) const;
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Ym2151_Emu.cpp b/Frameworks/GME/gme/Ym2151_Emu.cpp
new file mode 100644
index 000000000..b7703e7f4
--- /dev/null
+++ b/Frameworks/GME/gme/Ym2151_Emu.cpp
@@ -0,0 +1,75 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Ym2151_Emu.h"
+#include "ym2151.h"
+
+Ym2151_Emu::Ym2151_Emu() { PSG = 0; }
+
+Ym2151_Emu::~Ym2151_Emu()
+{
+ if ( PSG ) ym2151_shutdown( PSG );
+}
+
+int Ym2151_Emu::set_rate( double sample_rate, double clock_rate )
+{
+ if ( PSG )
+ {
+ ym2151_shutdown( PSG );
+ PSG = 0;
+ }
+
+ PSG = ym2151_init( clock_rate, sample_rate );
+ if ( !PSG )
+ return 1;
+
+ reset();
+ return 0;
+}
+
+void Ym2151_Emu::reset()
+{
+ ym2151_reset_chip( PSG );
+ ym2151_set_mask( PSG, 0 );
+}
+
+static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL};
+
+void Ym2151_Emu::write( int addr, int data )
+{
+ ym2151_update_one( PSG, DUMMYBUF, 0 );
+ ym2151_write_reg( PSG, addr, data );
+}
+
+void Ym2151_Emu::mute_voices( int mask )
+{
+ ym2151_set_mask( PSG, mask );
+}
+
+void Ym2151_Emu::run( int pair_count, sample_t* out )
+{
+ SAMP bufL[ 1024 ];
+ SAMP bufR[ 1024 ];
+ SAMP * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ ym2151_update_one( PSG, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/Ym2151_Emu.h b/Frameworks/GME/gme/Ym2151_Emu.h
new file mode 100644
index 000000000..a55c6f894
--- /dev/null
+++ b/Frameworks/GME/gme/Ym2151_Emu.h
@@ -0,0 +1,33 @@
+// YM2151 FM sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef YM2151_EMU_H
+#define YM2151_EMU_H
+
+class Ym2151_Emu {
+ void* PSG;
+public:
+ Ym2151_Emu();
+ ~Ym2151_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( double sample_rate, double clock_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 8 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Ym2203_Emu.cpp b/Frameworks/GME/gme/Ym2203_Emu.cpp
new file mode 100644
index 000000000..a8aad8291
--- /dev/null
+++ b/Frameworks/GME/gme/Ym2203_Emu.cpp
@@ -0,0 +1,155 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Ym2203_Emu.h"
+#include "fm.h"
+#include
+
+static void psg_set_clock(void *param, int clock)
+{
+ Ym2203_Emu *info = (Ym2203_Emu *)param;
+ info->psg_set_clock( clock );
+}
+
+static void psg_write(void *param, int address, int data)
+{
+ Ym2203_Emu *info = (Ym2203_Emu *)param;
+ info->psg_write( address, data );
+}
+
+static int psg_read(void *param)
+{
+ Ym2203_Emu *info = (Ym2203_Emu *)param;
+ return info->psg_read();
+}
+
+static void psg_reset(void *param)
+{
+ Ym2203_Emu *info = (Ym2203_Emu *)param;
+ info->psg_reset();
+}
+
+static const ssg_callbacks psgintf =
+{
+ psg_set_clock,
+ psg_write,
+ psg_read,
+ psg_reset
+};
+
+Ym2203_Emu::Ym2203_Emu() { opn = 0; psg.set_type( Ay_Apu::Ym2203 ); }
+
+Ym2203_Emu::~Ym2203_Emu()
+{
+ if ( opn ) ym2203_shutdown( opn );
+}
+
+int Ym2203_Emu::set_rate( int sample_rate, int clock_rate )
+{
+ if ( opn )
+ {
+ ym2203_shutdown( opn );
+ opn = 0;
+ }
+
+ opn = ym2203_init( this, clock_rate, sample_rate, &psgintf );
+ if ( !opn )
+ return 1;
+
+ this->sample_rate = sample_rate;
+ psg_clock = clock_rate * 2;
+
+ buffer.set_sample_rate( sample_rate );
+ buffer.clock_rate( psg_clock );
+
+ psg.volume( 1.0 );
+
+ reset();
+ return 0;
+}
+
+void Ym2203_Emu::reset()
+{
+ psg.reset();
+ ym2203_reset_chip( opn );
+ mute_voices( 0 );
+}
+
+static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL};
+
+void Ym2203_Emu::write( int addr, int data )
+{
+ ym2203_update_one( opn, DUMMYBUF, 0 );
+ ym2203_write( opn, 0, addr );
+ ym2203_write( opn, 1, data );
+}
+
+void Ym2203_Emu::mute_voices( int mask )
+{
+ ym2203_set_mutemask( opn, mask );
+ for ( unsigned i = 0, j = 1 << 3; i < 3; i++, j <<= 1)
+ {
+ Blip_Buffer * buf = ( mask & j ) ? NULL : &buffer;
+ psg.set_output( i, buf );
+ }
+}
+
+void Ym2203_Emu::run( int pair_count, sample_t* out )
+{
+ blip_sample_t buf[ 1024 ];
+ FMSAMPLE bufL[ 1024 ];
+ FMSAMPLE bufR[ 1024 ];
+ FMSAMPLE * buffers[2] = { bufL, bufR };
+
+ blip_time_t psg_end_time = pair_count * psg_clock / sample_rate;
+ psg.end_frame( psg_end_time );
+ buffer.end_frame( psg_end_time );
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ ym2203_update_one( opn, buffers, todo );
+
+ int sample_count = buffer.read_samples( buf, todo );
+ memset( buf + sample_count, 0, ( todo - sample_count ) * sizeof( blip_sample_t ) );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l, output_r;
+ int output = buf [i];
+ output_l = output + bufL [i];
+ output_r = output + bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
+
+void Ym2203_Emu::psg_set_clock( int clock )
+{
+ psg_clock = clock * 2;
+ buffer.clock_rate( psg_clock );
+}
+
+void Ym2203_Emu::psg_write( int addr, int data )
+{
+ if ( !(addr & 1) ) psg.write_addr( data );
+ else psg.write_data( 0, data );
+}
+
+int Ym2203_Emu::psg_read()
+{
+ return psg.read();
+}
+
+void Ym2203_Emu::psg_reset()
+{
+ psg.reset();
+}
diff --git a/Frameworks/GME/gme/Ym2203_Emu.h b/Frameworks/GME/gme/Ym2203_Emu.h
new file mode 100644
index 000000000..248a70e58
--- /dev/null
+++ b/Frameworks/GME/gme/Ym2203_Emu.h
@@ -0,0 +1,45 @@
+// YM2203 FM sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef YM2203_EMU_H
+#define YM2203_EMU_H
+
+#include "Ay_Apu.h"
+
+class Ym2203_Emu {
+ void* opn;
+ Ay_Apu psg;
+ Blip_Buffer buffer;
+ unsigned sample_rate;
+ unsigned psg_clock;
+public:
+ Ym2203_Emu();
+ ~Ym2203_Emu();
+
+ // Sets output chip clock rate, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int sample_rate, int clock_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 6 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+
+ // SSG interface
+ inline void psg_set_clock( int clock );
+ inline void psg_write( int addr, int data );
+ inline int psg_read();
+ inline void psg_reset();
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Ym2608_Emu.cpp b/Frameworks/GME/gme/Ym2608_Emu.cpp
new file mode 100644
index 000000000..fd0680dab
--- /dev/null
+++ b/Frameworks/GME/gme/Ym2608_Emu.cpp
@@ -0,0 +1,167 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Ym2608_Emu.h"
+#include "fm.h"
+#include
+
+static void psg_set_clock(void *param, int clock)
+{
+ Ym2608_Emu *info = (Ym2608_Emu *)param;
+ info->psg_set_clock( clock );
+}
+
+static void psg_write(void *param, int address, int data)
+{
+ Ym2608_Emu *info = (Ym2608_Emu *)param;
+ info->psg_write( address, data );
+}
+
+static int psg_read(void *param)
+{
+ Ym2608_Emu *info = (Ym2608_Emu *)param;
+ return info->psg_read();
+}
+
+static void psg_reset(void *param)
+{
+ Ym2608_Emu *info = (Ym2608_Emu *)param;
+ info->psg_reset();
+}
+
+static const ssg_callbacks psgintf =
+{
+ psg_set_clock,
+ psg_write,
+ psg_read,
+ psg_reset
+};
+
+Ym2608_Emu::Ym2608_Emu() { opn = 0; psg.set_type( Ay_Apu::Ym2608 ); }
+
+Ym2608_Emu::~Ym2608_Emu()
+{
+ if ( opn ) ym2608_shutdown( opn );
+}
+
+int Ym2608_Emu::set_rate( int sample_rate, int clock_rate )
+{
+ if ( opn )
+ {
+ ym2608_shutdown( opn );
+ opn = 0;
+ }
+
+ opn = ym2608_init( this, clock_rate, sample_rate, &psgintf );
+ if ( !opn )
+ return 1;
+
+ this->sample_rate = sample_rate;
+ psg_clock = clock_rate * 2;
+
+ buffer.set_sample_rate( sample_rate );
+ buffer.clock_rate( psg_clock );
+
+ psg.volume( 1.0 );
+
+ reset();
+ return 0;
+}
+
+void Ym2608_Emu::reset()
+{
+ psg.reset();
+ ym2608_reset_chip( opn );
+ mute_voices( 0 );
+}
+
+static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL};
+
+void Ym2608_Emu::write0( int addr, int data )
+{
+ ym2608_update_one( opn, DUMMYBUF, 0 );
+ ym2608_write( opn, 0, addr );
+ ym2608_write( opn, 1, data );
+}
+
+void Ym2608_Emu::write1( int addr, int data )
+{
+ ym2608_update_one( opn, DUMMYBUF, 0 );
+ ym2608_write( opn, 2, addr );
+ ym2608_write( opn, 3, data );
+}
+
+void Ym2608_Emu::write_rom( int rom_id, int size, int start, int length, void * data )
+{
+ ym2608_write_pcmrom( opn, rom_id, size, start, length, (const UINT8 *) data );
+}
+
+void Ym2608_Emu::mute_voices( int mask )
+{
+ ym2608_set_mutemask( opn, mask );
+ for ( unsigned i = 0, j = 1 << 6; i < 3; i++, j <<= 1)
+ {
+ Blip_Buffer * buf = ( mask & j ) ? NULL : &buffer;
+ psg.set_output( i, buf );
+ }
+}
+
+void Ym2608_Emu::run( int pair_count, sample_t* out )
+{
+ blip_sample_t buf[ 1024 ];
+ FMSAMPLE bufL[ 1024 ];
+ FMSAMPLE bufR[ 1024 ];
+ FMSAMPLE * buffers[2] = { bufL, bufR };
+
+ blip_time_t psg_end_time = pair_count * psg_clock / sample_rate;
+ psg.end_frame( psg_end_time );
+ buffer.end_frame( psg_end_time );
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ ym2608_update_one( opn, buffers, todo );
+
+ int sample_count = buffer.read_samples( buf, todo );
+ memset( buf + sample_count, 0, ( todo - sample_count ) * sizeof( blip_sample_t ) );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l, output_r;
+ int output = buf [i];
+ output_l = output + bufL [i];
+ output_r = output + bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
+
+void Ym2608_Emu::psg_set_clock( int clock )
+{
+ psg_clock = clock * 2;
+ buffer.clock_rate( psg_clock );
+}
+
+void Ym2608_Emu::psg_write( int addr, int data )
+{
+ if ( !(addr & 1) ) psg.write_addr( data );
+ else psg.write_data( 0, data );
+}
+
+int Ym2608_Emu::psg_read()
+{
+ return psg.read();
+}
+
+void Ym2608_Emu::psg_reset()
+{
+ psg.reset();
+}
diff --git a/Frameworks/GME/gme/Ym2608_Emu.h b/Frameworks/GME/gme/Ym2608_Emu.h
new file mode 100644
index 000000000..49a44ba4a
--- /dev/null
+++ b/Frameworks/GME/gme/Ym2608_Emu.h
@@ -0,0 +1,49 @@
+// YM2608 FM sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef YM2608_EMU_H
+#define YM2608_EMU_H
+
+#include "Ay_Apu.h"
+
+class Ym2608_Emu {
+ void* opn;
+ Ay_Apu psg;
+ Blip_Buffer buffer;
+ unsigned sample_rate;
+ unsigned psg_clock;
+public:
+ Ym2608_Emu();
+ ~Ym2608_Emu();
+
+ // Sets output chip clock rate, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int sample_rate, int clock_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 9 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write0( int addr, int data );
+ void write1( int addr, int data );
+
+ // Sets ROM type, scales ROM size, then writes length bytes from data at start offset
+ void write_rom( int rom_id, int size, int start, int length, void * data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+
+ // SSG interface
+ inline void psg_set_clock( int clock );
+ inline void psg_write( int addr, int data );
+ inline int psg_read();
+ inline void psg_reset();
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Ym2610b_Emu.cpp b/Frameworks/GME/gme/Ym2610b_Emu.cpp
new file mode 100644
index 000000000..ff4b8c7ee
--- /dev/null
+++ b/Frameworks/GME/gme/Ym2610b_Emu.cpp
@@ -0,0 +1,173 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Ym2610b_Emu.h"
+#include "fm.h"
+#include
+
+static void psg_set_clock(void *param, int clock)
+{
+ Ym2610b_Emu *info = (Ym2610b_Emu *)param;
+ info->psg_set_clock( clock );
+}
+
+static void psg_write(void *param, int address, int data)
+{
+ Ym2610b_Emu *info = (Ym2610b_Emu *)param;
+ info->psg_write( address, data );
+}
+
+static int psg_read(void *param)
+{
+ Ym2610b_Emu *info = (Ym2610b_Emu *)param;
+ return info->psg_read();
+}
+
+static void psg_reset(void *param)
+{
+ Ym2610b_Emu *info = (Ym2610b_Emu *)param;
+ info->psg_reset();
+}
+
+static const ssg_callbacks psgintf =
+{
+ psg_set_clock,
+ psg_write,
+ psg_read,
+ psg_reset
+};
+
+Ym2610b_Emu::Ym2610b_Emu() { opn = 0; }
+
+Ym2610b_Emu::~Ym2610b_Emu()
+{
+ if ( opn ) ym2610_shutdown( opn );
+}
+
+int Ym2610b_Emu::set_rate( int sample_rate, int clock_rate, bool is_2610b )
+{
+ if ( opn )
+ {
+ ym2610_shutdown( opn );
+ opn = 0;
+ }
+
+ psg.set_type( is_2610b ? Ay_Apu::Ym2610b : Ay_Apu::Ym2610 );
+
+ opn = ym2610_init( this, clock_rate, sample_rate, &psgintf );
+ if ( !opn )
+ return 1;
+
+ this->sample_rate = sample_rate;
+ psg_clock = clock_rate * 2;
+ this->is_2610b = is_2610b;
+
+ buffer.set_sample_rate( sample_rate );
+ buffer.clock_rate( psg_clock );
+
+ psg.volume( 1.0 );
+
+ reset();
+ return 0;
+}
+
+void Ym2610b_Emu::reset()
+{
+ psg.reset();
+ ym2610_reset_chip( opn );
+ mute_voices( 0 );
+}
+
+static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL};
+
+void Ym2610b_Emu::write0( int addr, int data )
+{
+ if ( is_2610b ) ym2610b_update_one( opn, DUMMYBUF, 0 );
+ else ym2610_update_one( opn, DUMMYBUF, 0 );
+ ym2610_write( opn, 0, addr );
+ ym2610_write( opn, 1, data );
+}
+
+void Ym2610b_Emu::write1( int addr, int data )
+{
+ if ( is_2610b ) ym2610b_update_one( opn, DUMMYBUF, 0 );
+ else ym2610_update_one( opn, DUMMYBUF, 0 );
+ ym2610_write( opn, 2, addr );
+ ym2610_write( opn, 3, data );
+}
+
+void Ym2610b_Emu::write_rom( int rom_id, int size, int start, int length, void * data )
+{
+ ym2610_write_pcmrom( opn, rom_id, size, start, length, (const UINT8 *) data );
+}
+
+void Ym2610b_Emu::mute_voices( int mask )
+{
+ ym2610_set_mutemask( opn, mask );
+ for ( unsigned i = 0, j = 1 << 6; i < 3; i++, j <<= 1)
+ {
+ Blip_Buffer * buf = ( mask & j ) ? NULL : &buffer;
+ psg.set_output( i, buf );
+ }
+}
+
+void Ym2610b_Emu::run( int pair_count, sample_t* out )
+{
+ blip_sample_t buf[ 1024 ];
+ FMSAMPLE bufL[ 1024 ];
+ FMSAMPLE bufR[ 1024 ];
+ FMSAMPLE * buffers[2] = { bufL, bufR };
+
+ blip_time_t psg_end_time = pair_count * psg_clock / sample_rate;
+ psg.end_frame( psg_end_time );
+ buffer.end_frame( psg_end_time );
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ if ( is_2610b ) ym2610b_update_one( opn, buffers, todo );
+ else ym2610_update_one( opn, buffers, todo );
+
+ int sample_count = buffer.read_samples( buf, todo );
+ memset( buf + sample_count, 0, ( todo - sample_count ) * sizeof( blip_sample_t ) );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l, output_r;
+ int output = buf [i];
+ output_l = output + bufL [i];
+ output_r = output + bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
+
+void Ym2610b_Emu::psg_set_clock( int clock )
+{
+ psg_clock = clock * 2;
+ buffer.clock_rate( psg_clock );
+}
+
+void Ym2610b_Emu::psg_write( int addr, int data )
+{
+ if ( !(addr & 1) ) psg.write_addr( data );
+ else psg.write_data( 0, data );
+}
+
+int Ym2610b_Emu::psg_read()
+{
+ return psg.read();
+}
+
+void Ym2610b_Emu::psg_reset()
+{
+ psg.reset();
+}
diff --git a/Frameworks/GME/gme/Ym2610b_Emu.h b/Frameworks/GME/gme/Ym2610b_Emu.h
new file mode 100644
index 000000000..634ffb1ff
--- /dev/null
+++ b/Frameworks/GME/gme/Ym2610b_Emu.h
@@ -0,0 +1,50 @@
+// YM2610B FM sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef YM2610B_EMU_H
+#define YM2610B_EMU_H
+
+#include "Ay_Apu.h"
+
+class Ym2610b_Emu {
+ void* opn;
+ Ay_Apu psg;
+ Blip_Buffer buffer;
+ unsigned sample_rate;
+ unsigned psg_clock;
+ bool is_2610b;
+public:
+ Ym2610b_Emu();
+ ~Ym2610b_Emu();
+
+ // Sets output chip clock rate, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int sample_rate, int clock_rate, bool is_2610b );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 9 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write0( int addr, int data );
+ void write1( int addr, int data );
+
+ // Sets ROM type, scales ROM size, then writes length bytes from data at start offset
+ void write_rom( int rom_id, int size, int start, int length, void * data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+
+ // SSG interface
+ inline void psg_set_clock( int clock );
+ inline void psg_write( int addr, int data );
+ inline int psg_read();
+ inline void psg_reset();
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Ym2612_Emu_Gens.cpp b/Frameworks/GME/gme/Ym2612_Emu_Gens.cpp
new file mode 100644
index 000000000..514e7a3d3
--- /dev/null
+++ b/Frameworks/GME/gme/Ym2612_Emu_Gens.cpp
@@ -0,0 +1,1299 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+// Based on Gens 2.10 ym2612.c
+
+#include "Ym2612_Emu.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* Copyright (C) 2002 Stéphane Dallongeville (gens AT consolemul.com) */
+/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+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 */
+
+// This is mostly the original source in its C style and all.
+//
+// Somewhat optimized and simplified. Uses a template to generate the many
+// variants of Update_Chan. Rewrote header file. In need of full rewrite by
+// someone more familiar with FM sound and the YM2612. Has some inaccuracies
+// compared to the Sega Genesis sound, particularly being mixed at such a
+// high sample accuracy (the Genesis sounds like it has onn(ly 8 bit samples).
+// - Shay
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+ #include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+const int output_bits = 14;
+
+struct slot_t
+{
+ const int *DT; // parametre detune
+ int MUL; // parametre "multiple de frequence"
+ int TL; // Total Level = volume lorsque l'enveloppe est au plus haut
+ int TLL; // Total Level ajusted
+ int SLL; // Sustin Level (ajusted) = volume où l'enveloppe termine sa premiere phase de regression
+ int KSR_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe
+ int KSR; // Key Scale Rate = cette valeur est calculee par rapport à la frequence actuelle, elle va influer
+ // sur les differents parametres de l'enveloppe comme l'attaque, le decay ... comme dans la realite !
+ int SEG; // Type enveloppe SSG
+ const int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR [KSR])
+ const int *DR; // Decay Rate (table pointeur) = Taux pour la regression (DR [KSR])
+ const int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR [KSR])
+ const int *RR; // Release Rate (table pointeur) = Taux pour le rel'chement (RR [KSR])
+ int Fcnt; // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN [Finc >> 16])
+ int Finc; // frequency step = pas d'incrementation du compteur-frequence
+ // plus le pas est grand, plus la frequence est aïgu (ou haute)
+ int Ecurp; // Envelope current phase = cette variable permet de savoir dans quelle phase
+ // de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ...
+ // en fonction de la valeur de cette variable, on va appeler une fonction permettant
+ // de mettre à jour l'enveloppe courante.
+ int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe
+ int Einc; // Envelope step courant
+ int Ecmp; // Envelope counter limite pour la prochaine phase
+ int EincA; // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque
+ // cette valeur est egal à AR [KSR]
+ int EincD; // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression
+ // cette valeur est egal à DR [KSR]
+ int EincS; // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue
+ // cette valeur est egal à SR [KSR]
+ int EincR; // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement
+ // cette valeur est egal à RR [KSR]
+ int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entree
+ // d'un autre ou carrement à la sortie de la voie
+ int INd; // input data of the slot = donnees en entree du slot
+ int ChgEnM; // Change envelop mask.
+ int AMS; // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO
+ int AMSon; // AMS enable flag = drapeau d'activation de l'AMS
+};
+
+struct channel_t
+{
+ int S0_OUT [4]; // anciennes sorties slot 0 (pour le feed back)
+ int LEFT; // LEFT enable flag
+ int RIGHT; // RIGHT enable flag
+ int ALGO; // Algorythm = determine les connections entre les operateurs
+ int FB; // shift count of self feed back = degre de "Feed-Back" du SLOT 1 (il est son unique entree)
+ int FMS; // Frequency Modulation Sensitivity of channel = degre de modulation de la frequence sur la voie par le LFO
+ int AMS; // Amplitude Modulation Sensitivity of channel = degre de modulation de l'amplitude sur la voie par le LFO
+ int FNUM [4]; // hauteur frequence de la voie (+ 3 pour le mode special)
+ int FOCT [4]; // octave de la voie (+ 3 pour le mode special)
+ int KC [4]; // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S)
+ slot_t SLOT [4]; // four slot.operators = les 4 slots de la voie
+ int FFlag; // Frequency step recalculation flag
+};
+
+struct state_t
+{
+ int TimerBase; // TimerBase calculation
+ int Status; // YM2612 Status (timer overflow)
+ int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter
+ int TimerAL;
+ int TimerAcnt; // timerA counter = valeur courante du Timer A
+ int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter
+ int TimerBL;
+ int TimerBcnt; // timerB counter = valeur courante du Timer B
+ int Mode; // Mode actuel des voie 3 et 6 (normal / special)
+ int DAC; // DAC enabled flag
+ channel_t CHANNEL [Ym2612_Emu::channel_count]; // Les 6 voies du YM2612
+ int REG [2] [0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif
+ // cela nous rend le debuggage plus facile
+};
+
+#undef PI
+#define PI 3.14159265358979323846
+
+#define ATTACK 0
+#define DECAY 1
+#define SUBSTAIN 2
+#define RELEASE 3
+
+// SIN_LBITS <= 16
+// LFO_HBITS <= 16
+// (SIN_LBITS + SIN_HBITS) <= 26
+// (ENV_LBITS + ENV_HBITS) <= 28
+// (LFO_LBITS + LFO_HBITS) <= 28
+
+#define SIN_HBITS 12 // Sinus phase counter int part
+#define SIN_LBITS (26 - SIN_HBITS) // Sinus phase counter float part (best setting)
+
+#if (SIN_LBITS > 16)
+#define SIN_LBITS 16 // Can't be greater than 16 bits
+#endif
+
+#define ENV_HBITS 12 // Env phase counter int part
+#define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting)
+
+#define LFO_HBITS 10 // LFO phase counter int part
+#define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting)
+
+#define SIN_LENGHT (1 << SIN_HBITS)
+#define ENV_LENGHT (1 << ENV_HBITS)
+#define LFO_LENGHT (1 << LFO_HBITS)
+
+#define TL_LENGHT (ENV_LENGHT * 3) // Env + TL scaling + LFO
+
+#define SIN_MASK (SIN_LENGHT - 1)
+#define ENV_MASK (ENV_LENGHT - 1)
+#define LFO_MASK (LFO_LENGHT - 1)
+
+#define ENV_STEP (96.0 / ENV_LENGHT) // ENV_MAX = 96 dB
+
+#define ENV_ATTACK ((ENV_LENGHT * 0) << ENV_LBITS)
+#define ENV_DECAY ((ENV_LENGHT * 1) << ENV_LBITS)
+#define ENV_END ((ENV_LENGHT * 2) << ENV_LBITS)
+
+#define MAX_OUT_BITS (SIN_HBITS + SIN_LBITS + 2) // Modulation = -4 <--> +4
+#define MAX_OUT ((1 << MAX_OUT_BITS) - 1)
+
+#define PG_CUT_OFF ((int) (78.0 / ENV_STEP))
+//#define ENV_CUT_OFF ((int) (68.0 / ENV_STEP))
+
+#define AR_RATE 399128
+#define DR_RATE 5514396
+
+//#define AR_RATE 426136
+//#define DR_RATE (AR_RATE * 12)
+
+#define LFO_FMS_LBITS 9 // FIXED (LFO_FMS_BASE gives somethink as 1)
+#define LFO_FMS_BASE ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS)))
+
+#define S0 0 // Stupid typo of the YM2612
+#define S1 2
+#define S2 1
+#define S3 3
+
+struct tables_t
+{
+ short SIN_TAB [SIN_LENGHT]; // SINUS TABLE (offset into TL TABLE)
+ int LFOcnt; // LFO counter = compteur-frequence pour le LFO
+ int LFOinc; // LFO step counter = pas d'incrementation du compteur-frequence du LFO
+ // plus le pas est grand, plus la frequence est grande
+ unsigned int AR_TAB [128]; // Attack rate table
+ unsigned int DR_TAB [96]; // Decay rate table
+ unsigned int DT_TAB [8] [32]; // Detune table
+ unsigned int SL_TAB [16]; // Substain level table
+ unsigned int NULL_RATE [32]; // Table for NULL rate
+ int LFO_INC_TAB [8]; // LFO step table
+
+ short ENV_TAB [2 * ENV_LENGHT + 8]; // ENV CURVE TABLE (attack & decay)
+
+ short LFO_ENV_TAB [LFO_LENGHT]; // LFO AMS TABLE (adjusted for 11.8 dB)
+ short LFO_FREQ_TAB [LFO_LENGHT]; // LFO FMS TABLE
+ int TL_TAB [TL_LENGHT * 2]; // TOTAL LEVEL TABLE (positif and minus)
+ unsigned int DECAY_TO_ATTACK [ENV_LENGHT]; // Conversion from decay to attack phase
+ unsigned int FINC_TAB [2048]; // Frequency step table
+};
+
+static const unsigned char DT_DEF_TAB [4 * 32] =
+{
+// FD = 0
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+// FD = 1
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
+
+// FD = 2
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
+ 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16,
+
+// FD = 3
+ 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
+ 8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22
+};
+
+static const unsigned char FKEY_TAB [16] =
+{
+ 0, 0, 0, 0,
+ 0, 0, 0, 1,
+ 2, 3, 3, 3,
+ 3, 3, 3, 3
+};
+
+static const unsigned char LFO_AMS_TAB [4] =
+{
+ 31, 4, 1, 0
+};
+
+static const unsigned char LFO_FMS_TAB [8] =
+{
+ LFO_FMS_BASE * 0, LFO_FMS_BASE * 1,
+ LFO_FMS_BASE * 2, LFO_FMS_BASE * 3,
+ LFO_FMS_BASE * 4, LFO_FMS_BASE * 6,
+ LFO_FMS_BASE * 12, LFO_FMS_BASE * 24
+};
+
+inline void YM2612_Special_Update() { }
+
+struct Ym2612_Impl
+{
+ enum { channel_count = Ym2612_Emu::channel_count };
+
+ state_t YM2612;
+ int mute_mask;
+ tables_t g;
+
+ void KEY_ON( channel_t&, int );
+ void KEY_OFF( channel_t&, int );
+ int SLOT_SET( int, int );
+ int CHANNEL_SET( int, int );
+ int YM_SET( int, int );
+
+ void set_rate( double sample_rate, double clock_factor );
+ void reset();
+ void write0( int addr, int data );
+ void write1( int addr, int data );
+ void run_timer( int );
+ void run( int pair_count, Ym2612_Emu::sample_t [] );
+};
+
+void Ym2612_Impl::KEY_ON( channel_t& ch, int nsl)
+{
+ slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot
+
+ if (SL->Ecurp == RELEASE) // la touche est-elle rel'chee ?
+ {
+ SL->Fcnt = 0;
+
+ // Fix Ecco 2 splash sound
+
+ SL->Ecnt = (g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM;
+ SL->ChgEnM = ~0;
+
+// SL->Ecnt = g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK;
+// SL->Ecnt = 0;
+
+ SL->Einc = SL->EincA;
+ SL->Ecmp = ENV_DECAY;
+ SL->Ecurp = ATTACK;
+ }
+}
+
+
+void Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl)
+{
+ slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot
+
+ if (SL->Ecurp != RELEASE) // la touche est-elle appuyee ?
+ {
+ if (SL->Ecnt < ENV_DECAY) // attack phase ?
+ {
+ SL->Ecnt = (g.ENV_TAB [SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY;
+ }
+
+ SL->Einc = SL->EincR;
+ SL->Ecmp = ENV_END;
+ SL->Ecurp = RELEASE;
+ }
+}
+
+
+int Ym2612_Impl::SLOT_SET( int Adr, int data )
+{
+ int nch = Adr & 3;
+ if ( nch == 3 )
+ return 1;
+
+ channel_t& ch = YM2612.CHANNEL [nch + (Adr & 0x100 ? 3 : 0)];
+ slot_t& sl = ch.SLOT [(Adr >> 2) & 3];
+
+ switch ( Adr & 0xF0 )
+ {
+ case 0x30:
+ if ( (sl.MUL = (data & 0x0F)) != 0 ) sl.MUL <<= 1;
+ else sl.MUL = 1;
+
+ sl.DT = (int*) g.DT_TAB [(data >> 4) & 7];
+
+ ch.SLOT [0].Finc = -1;
+
+ break;
+
+ case 0x40:
+ sl.TL = data & 0x7F;
+
+ // SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound...
+ YM2612_Special_Update();
+
+#if ((ENV_HBITS - 7) < 0)
+ sl.TLL = sl.TL >> (7 - ENV_HBITS);
+#else
+ sl.TLL = sl.TL << (ENV_HBITS - 7);
+#endif
+
+ break;
+
+ case 0x50:
+ sl.KSR_S = 3 - (data >> 6);
+
+ ch.SLOT [0].Finc = -1;
+
+ if (data &= 0x1F) sl.AR = (int*) &g.AR_TAB [data << 1];
+ else sl.AR = (int*) &g.NULL_RATE [0];
+
+ sl.EincA = sl.AR [sl.KSR];
+ if (sl.Ecurp == ATTACK) sl.Einc = sl.EincA;
+ break;
+
+ case 0x60:
+ if ( (sl.AMSon = (data & 0x80)) != 0 ) sl.AMS = ch.AMS;
+ else sl.AMS = 31;
+
+ if (data &= 0x1F) sl.DR = (int*) &g.DR_TAB [data << 1];
+ else sl.DR = (int*) &g.NULL_RATE [0];
+
+ sl.EincD = sl.DR [sl.KSR];
+ if (sl.Ecurp == DECAY) sl.Einc = sl.EincD;
+ break;
+
+ case 0x70:
+ if (data &= 0x1F) sl.SR = (int*) &g.DR_TAB [data << 1];
+ else sl.SR = (int*) &g.NULL_RATE [0];
+
+ sl.EincS = sl.SR [sl.KSR];
+ if ((sl.Ecurp == SUBSTAIN) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincS;
+ break;
+
+ case 0x80:
+ sl.SLL = g.SL_TAB [data >> 4];
+
+ sl.RR = (int*) &g.DR_TAB [((data & 0xF) << 2) + 2];
+
+ sl.EincR = sl.RR [sl.KSR];
+ if ((sl.Ecurp == RELEASE) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincR;
+ break;
+
+ case 0x90:
+ // SSG-EG envelope shapes :
+ /*
+ E At Al H
+
+ 1 0 0 0 \\\\
+ 1 0 0 1 \___
+ 1 0 1 0 \/\/
+ 1 0 1 1 \
+ 1 1 0 0 ////
+ 1 1 0 1 /
+ 1 1 1 0 /\/\
+ 1 1 1 1 /___
+
+ E = SSG-EG enable
+ At = Start negate
+ Al = Altern
+ H = Hold */
+
+ if(data & 0x08) sl.SEG = data & 0x0F;
+ else sl.SEG = 0;
+ break;
+ }
+
+ return 0;
+}
+
+
+int Ym2612_Impl::CHANNEL_SET( int Adr, int data )
+{
+ int num = Adr & 3;
+ if ( num == 3 )
+ return 1;
+
+ channel_t& ch = YM2612.CHANNEL [num + (Adr & 0x100 ? 3 : 0)];
+
+ switch ( Adr & 0xFC )
+ {
+ case 0xA0:
+ YM2612_Special_Update();
+
+ ch.FNUM [0] = (ch.FNUM [0] & 0x700) + data;
+ ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7];
+
+ ch.SLOT [0].Finc = -1;
+ break;
+
+ case 0xA4:
+ YM2612_Special_Update();
+
+ ch.FNUM [0] = (ch.FNUM [0] & 0x0FF) + ((data & 0x07) << 8);
+ ch.FOCT [0] = (data & 0x38) >> 3;
+ ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7];
+
+ ch.SLOT [0].Finc = -1;
+ break;
+
+ case 0xA8:
+ if ( Adr < 0x100 )
+ {
+ num++;
+
+ YM2612_Special_Update();
+
+ YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x700) + data;
+ YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) |
+ FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7];
+
+ YM2612.CHANNEL [2].SLOT [0].Finc = -1;
+ }
+ break;
+
+ case 0xAC:
+ if ( Adr < 0x100 )
+ {
+ num++;
+
+ YM2612_Special_Update();
+
+ YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x0FF) + ((data & 0x07) << 8);
+ YM2612.CHANNEL [2].FOCT [num] = (data & 0x38) >> 3;
+ YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) |
+ FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7];
+
+ YM2612.CHANNEL [2].SLOT [0].Finc = -1;
+ }
+ break;
+
+ case 0xB0:
+ if ( ch.ALGO != (data & 7) )
+ {
+ // Fix VectorMan 2 heli sound (level 1)
+ YM2612_Special_Update();
+
+ ch.ALGO = data & 7;
+
+ ch.SLOT [0].ChgEnM = 0;
+ ch.SLOT [1].ChgEnM = 0;
+ ch.SLOT [2].ChgEnM = 0;
+ ch.SLOT [3].ChgEnM = 0;
+ }
+
+ ch.FB = 9 - ((data >> 3) & 7); // Real thing ?
+
+// if (ch.FB = ((data >> 3) & 7)) ch.FB = 9 - ch.FB; // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound...
+// else ch.FB = 31;
+ break;
+
+ case 0xB4: {
+ YM2612_Special_Update();
+
+ ch.LEFT = 0 - ((data >> 7) & 1);
+ ch.RIGHT = 0 - ((data >> 6) & 1);
+
+ ch.AMS = LFO_AMS_TAB [(data >> 4) & 3];
+ ch.FMS = LFO_FMS_TAB [data & 7];
+
+ for ( int i = 0; i < 4; i++ )
+ {
+ slot_t& sl = ch.SLOT [i];
+ sl.AMS = (sl.AMSon ? ch.AMS : 31);
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+int Ym2612_Impl::YM_SET(int Adr, int data)
+{
+ switch ( Adr )
+ {
+ case 0x22:
+ if (data & 8) // LFO enable
+ {
+ // Cool Spot music 1, LFO modified severals time which
+ // distord the sound, have to check that on a real genesis...
+
+ g.LFOinc = g.LFO_INC_TAB [data & 7];
+ }
+ else
+ {
+ g.LFOinc = g.LFOcnt = 0;
+ }
+ break;
+
+ case 0x24:
+ YM2612.TimerA = (YM2612.TimerA & 0x003) | (((int) data) << 2);
+
+ if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12)
+ {
+ YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12;
+ }
+ break;
+
+ case 0x25:
+ YM2612.TimerA = (YM2612.TimerA & 0x3FC) | (data & 3);
+
+ if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12)
+ {
+ YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12;
+ }
+ break;
+
+ case 0x26:
+ YM2612.TimerB = data;
+
+ if (YM2612.TimerBL != (256 - YM2612.TimerB) << (4 + 12))
+ {
+ YM2612.TimerBcnt = YM2612.TimerBL = (256 - YM2612.TimerB) << (4 + 12);
+ }
+ break;
+
+ case 0x27:
+ // Parametre divers
+ // b7 = CSM MODE
+ // b6 = 3 slot mode
+ // b5 = reset b
+ // b4 = reset a
+ // b3 = timer enable b
+ // b2 = timer enable a
+ // b1 = load b
+ // b0 = load a
+
+ if ((data ^ YM2612.Mode) & 0x40)
+ {
+ // We changed the channel 2 mode, so recalculate phase step
+ // This fix the punch sound in Street of Rage 2
+
+ YM2612_Special_Update();
+
+ YM2612.CHANNEL [2].SLOT [0].Finc = -1; // recalculate phase step
+ }
+
+// if ((data & 2) && (YM2612.Status & 2)) YM2612.TimerBcnt = YM2612.TimerBL;
+// if ((data & 1) && (YM2612.Status & 1)) YM2612.TimerAcnt = YM2612.TimerAL;
+
+// YM2612.Status &= (~data >> 4); // Reset du Status au cas ou c'est demande
+ YM2612.Status &= (~data >> 4) & (data >> 2); // Reset Status
+
+ YM2612.Mode = data;
+ break;
+
+ case 0x28: {
+ int nch = data & 3;
+ if ( nch == 3 )
+ return 1;
+ if ( data & 4 )
+ nch += 3;
+ channel_t& ch = YM2612.CHANNEL [nch];
+
+ YM2612_Special_Update();
+
+ if (data & 0x10) KEY_ON(ch, S0); // On appuie sur la touche pour le slot 1
+ else KEY_OFF(ch, S0); // On rel'che la touche pour le slot 1
+ if (data & 0x20) KEY_ON(ch, S1); // On appuie sur la touche pour le slot 3
+ else KEY_OFF(ch, S1); // On rel'che la touche pour le slot 3
+ if (data & 0x40) KEY_ON(ch, S2); // On appuie sur la touche pour le slot 2
+ else KEY_OFF(ch, S2); // On rel'che la touche pour le slot 2
+ if (data & 0x80) KEY_ON(ch, S3); // On appuie sur la touche pour le slot 4
+ else KEY_OFF(ch, S3); // On rel'che la touche pour le slot 4
+ break;
+ }
+
+ case 0x2B:
+ if (YM2612.DAC ^ (data & 0x80)) YM2612_Special_Update();
+
+ YM2612.DAC = data & 0x80; // activation/desactivation du DAC
+ break;
+ }
+
+ return 0;
+}
+
+void Ym2612_Impl::set_rate( double sample_rate, double clock_rate )
+{
+ assert( sample_rate );
+ assert( !clock_rate || clock_rate > sample_rate );
+
+ int i;
+
+ // 144 = 12 * (prescale * 2) = 12 * 6 * 2
+ // prescale set to 6 by default
+
+ double Frequence = (clock_rate ? clock_rate / sample_rate / 144.0 : 1.0);
+ if ( fabs( Frequence - 1.0 ) < 0.0000001 )
+ Frequence = 1.0;
+ YM2612.TimerBase = int (Frequence * 4096.0);
+
+ // Tableau TL :
+ // [0 - 4095] = +output [4095 - ...] = +output overflow (fill with 0)
+ // [12288 - 16383] = -output [16384 - ...] = -output overflow (fill with 0)
+
+ for ( i = 0; i < TL_LENGHT; i++ )
+ {
+ if (i >= PG_CUT_OFF) // YM2612 cut off sound after 78 dB (14 bits output ?)
+ {
+ g.TL_TAB [TL_LENGHT + i] = g.TL_TAB [i] = 0;
+ }
+ else
+ {
+ // Decibel -> Voltage
+ g.TL_TAB [i] = int (MAX_OUT / pow( 10.0, ENV_STEP / 20.0f * i ));
+ g.TL_TAB [TL_LENGHT + i] = -g.TL_TAB [i];
+ }
+ }
+
+ // Tableau SIN :
+ // g.SIN_TAB [x] [y] = sin(x) * y;
+ // x = phase and y = volume
+
+ g.SIN_TAB [0] = g.SIN_TAB [SIN_LENGHT / 2] = PG_CUT_OFF;
+
+ for ( i = 1; i <= SIN_LENGHT / 4; i++ )
+ {
+ // Sinus in dB
+ double x = 20 * log10( 1 / sin( 2.0 * PI * i / SIN_LENGHT ) ); // convert to dB
+
+ int j = (int) (x / ENV_STEP); // Get TL range
+
+ if (j > PG_CUT_OFF) j = (int) PG_CUT_OFF;
+
+ g.SIN_TAB [i] = g.SIN_TAB [(SIN_LENGHT / 2) - i] = j;
+ g.SIN_TAB [(SIN_LENGHT / 2) + i] = g.SIN_TAB [SIN_LENGHT - i] = TL_LENGHT + j;
+ }
+
+ // Tableau LFO (LFO wav) :
+
+ for ( i = 0; i < LFO_LENGHT; i++ )
+ {
+ double x = 1 + sin( 2.0 * PI * i * (1.0 / LFO_LENGHT) ); // Sinus
+ x *= 11.8 / ENV_STEP / 2; // ajusted to MAX enveloppe modulation
+
+ g.LFO_ENV_TAB [i] = (int) x;
+
+ x = sin( 2.0 * PI * i * (1.0 / LFO_LENGHT) ); // Sinus
+ x *= (1 << (LFO_HBITS - 1)) - 1;
+
+ g.LFO_FREQ_TAB [i] = (int) x;
+ }
+
+ // Tableau Enveloppe :
+ // g.ENV_TAB [0] -> g.ENV_TAB [ENV_LENGHT - 1] = attack curve
+ // g.ENV_TAB [ENV_LENGHT] -> g.ENV_TAB [2 * ENV_LENGHT - 1] = decay curve
+
+ for ( i = 0; i < ENV_LENGHT; i++ )
+ {
+ // Attack curve (x^8 - music level 2 Vectorman 2)
+ double x = pow( ((ENV_LENGHT - 1) - i) / (double) ENV_LENGHT, 8.0 );
+ x *= ENV_LENGHT;
+
+ g.ENV_TAB [i] = (int) x;
+
+ // Decay curve (just linear)
+ g.ENV_TAB [ENV_LENGHT + i] = i;
+ }
+ for ( i = 0; i < 8; i++ )
+ g.ENV_TAB [i + ENV_LENGHT * 2] = 0;
+
+ g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1; // for the stopped state
+
+ // Tableau pour la conversion Attack -> Decay and Decay -> Attack
+
+ int j = ENV_LENGHT - 1;
+ for ( i = 0; i < ENV_LENGHT; i++ )
+ {
+ while ( j && g.ENV_TAB [j] < i )
+ j--;
+
+ g.DECAY_TO_ATTACK [i] = j << ENV_LBITS;
+ }
+
+ // Tableau pour le Substain Level
+
+ for ( i = 0; i < 15; i++ )
+ {
+ double x = i * 3 / ENV_STEP; // 3 and not 6 (Mickey Mania first music for test)
+
+ 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
+
+ // Tableau Frequency Step
+ {
+ // 0.5 because MUL = value * 2
+ #if SIN_LBITS + SIN_HBITS - (21 - 7) < 0
+ double const factor = 0.5 / (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS)) * Frequence;
+ #else
+ double const factor = 0.5 * (1 << (SIN_LBITS + SIN_HBITS - (21 - 7))) * Frequence;
+ #endif
+ for ( i = 0; i < 2048; i++ )
+ g.FINC_TAB [i] = unsigned (i * factor);
+ }
+
+ // Tableaux Attack & Decay Rate
+
+ for ( i = 0; i < 4; i++ )
+ {
+ g.AR_TAB [i] = 0;
+ g.DR_TAB [i] = 0;
+ }
+
+ for ( i = 0; i < 60; i++ )
+ {
+ double x =
+ (1.0 + ((i & 3) * 0.25)) * // bits 0-1 : x1.00, x1.25, x1.50, x1.75
+ (ENV_LENGHT << ENV_LBITS) * // on ajuste pour le tableau g.ENV_TAB
+ Frequence *
+ (1 << (i >> 2)); // bits 2-5 : shift bits (x2^0 - x2^15)
+
+ g.AR_TAB [i + 4] = (unsigned int) (x / AR_RATE);
+ g.DR_TAB [i + 4] = (unsigned int) (x / DR_RATE);
+ }
+
+ for ( i = 64; i < 96; i++ )
+ {
+ g.AR_TAB [i] = g.AR_TAB [63];
+ g.DR_TAB [i] = g.DR_TAB [63];
+
+ g.NULL_RATE [i - 64] = 0;
+ }
+
+ for ( i = 96; i < 128; i++ )
+ g.AR_TAB [i] = 0;
+
+ // Tableau Detune
+ {
+ #if SIN_LBITS + SIN_HBITS - 21 < 0
+ double const factor = 1.0 / (1 << (21 - SIN_LBITS - SIN_HBITS)) * Frequence;
+ #else
+ double const factor = (1 << (SIN_LBITS + SIN_HBITS - 21)) * Frequence;
+ #endif
+ for ( i = 0; i < 4; i++ )
+ {
+ for ( int j = 0; j < 32; j++ )
+ {
+ double y = DT_DEF_TAB [(i << 5) + j] * factor;
+
+ g.DT_TAB [i + 0] [j] = (int) y;
+ g.DT_TAB [i + 4] [j] = (int) -y;
+ }
+ }
+ }
+
+ // Tableau LFO
+ g.LFO_INC_TAB [0] = unsigned (3.98 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+ g.LFO_INC_TAB [1] = unsigned (5.56 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+ g.LFO_INC_TAB [2] = unsigned (6.02 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+ g.LFO_INC_TAB [3] = unsigned (6.37 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+ g.LFO_INC_TAB [4] = unsigned (6.88 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+ g.LFO_INC_TAB [5] = unsigned (9.63 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+ g.LFO_INC_TAB [6] = unsigned (48.1 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+ g.LFO_INC_TAB [7] = unsigned (72.2 * (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate);
+
+ reset();
+}
+
+const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate )
+{
+ if ( !impl )
+ {
+ impl = (Ym2612_Impl*) malloc( sizeof *impl );
+ if ( !impl )
+ return "Out of memory";
+ impl->mute_mask = 0;
+ }
+ memset( &impl->YM2612, 0, sizeof impl->YM2612 );
+
+ impl->set_rate( sample_rate, clock_rate );
+
+ return 0;
+}
+
+Ym2612_Emu::~Ym2612_Emu()
+{
+ free( impl );
+}
+
+inline void Ym2612_Impl::write0( int opn_addr, int data )
+{
+ assert( (unsigned) data <= 0xFF );
+
+ if ( opn_addr < 0x30 )
+ {
+ YM2612.REG [0] [opn_addr] = data;
+ YM_SET( opn_addr, data );
+ }
+ else if ( YM2612.REG [0] [opn_addr] != data )
+ {
+ YM2612.REG [0] [opn_addr] = data;
+
+ if ( opn_addr < 0xA0 )
+ SLOT_SET( opn_addr, data );
+ else
+ CHANNEL_SET( opn_addr, data );
+ }
+}
+
+inline void Ym2612_Impl::write1( int opn_addr, int data )
+{
+ assert( (unsigned) data <= 0xFF );
+
+ if ( opn_addr >= 0x30 && YM2612.REG [1] [opn_addr] != data )
+ {
+ YM2612.REG [1] [opn_addr] = data;
+
+ if ( opn_addr < 0xA0 )
+ SLOT_SET( opn_addr + 0x100, data );
+ else
+ CHANNEL_SET( opn_addr + 0x100, data );
+ }
+}
+
+void Ym2612_Emu::reset()
+{
+ impl->reset();
+}
+
+void Ym2612_Impl::reset()
+{
+ g.LFOcnt = 0;
+ YM2612.TimerA = 0;
+ YM2612.TimerAL = 0;
+ YM2612.TimerAcnt = 0;
+ YM2612.TimerB = 0;
+ YM2612.TimerBL = 0;
+ YM2612.TimerBcnt = 0;
+ YM2612.DAC = 0;
+
+ YM2612.Status = 0;
+
+ int i;
+ for ( i = 0; i < channel_count; i++ )
+ {
+ channel_t& ch = YM2612.CHANNEL [i];
+
+ ch.LEFT = ~0;
+ ch.RIGHT = ~0;
+ ch.ALGO = 0;
+ ch.FB = 31;
+ ch.FMS = 0;
+ ch.AMS = 0;
+
+ for ( int j = 0 ;j < 4 ; j++ )
+ {
+ ch.S0_OUT [j] = 0;
+ ch.FNUM [j] = 0;
+ ch.FOCT [j] = 0;
+ ch.KC [j] = 0;
+
+ ch.SLOT [j].Fcnt = 0;
+ ch.SLOT [j].Finc = 0;
+ ch.SLOT [j].Ecnt = ENV_END; // Put it at the end of Decay phase...
+ ch.SLOT [j].Einc = 0;
+ ch.SLOT [j].Ecmp = 0;
+ ch.SLOT [j].Ecurp = RELEASE;
+
+ ch.SLOT [j].ChgEnM = 0;
+ }
+ }
+
+ for ( i = 0; i < 0x100; i++ )
+ {
+ YM2612.REG [0] [i] = -1;
+ YM2612.REG [1] [i] = -1;
+ }
+
+ for ( i = 0xB6; i >= 0xB4; i-- )
+ {
+ write0( i, 0xC0 );
+ write1( i, 0xC0 );
+ }
+
+ for ( i = 0xB2; i >= 0x22; i-- )
+ {
+ write0( i, 0 );
+ write1( i, 0 );
+ }
+
+ write0( 0x2A, 0x80 );
+}
+
+void Ym2612_Emu::write0( int addr, int data )
+{
+ impl->write0( addr, data );
+}
+
+void Ym2612_Emu::write1( int addr, int data )
+{
+ impl->write1( addr, data );
+}
+
+void Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; }
+
+static void update_envelope_( slot_t* sl )
+{
+ switch ( sl->Ecurp )
+ {
+ case 0:
+ // Env_Attack_Next
+
+ // Verified with Gynoug even in HQ (explode SFX)
+ sl->Ecnt = ENV_DECAY;
+
+ sl->Einc = sl->EincD;
+ sl->Ecmp = sl->SLL;
+ sl->Ecurp = DECAY;
+ break;
+
+ case 1:
+ // Env_Decay_Next
+
+ // Verified with Gynoug even in HQ (explode SFX)
+ sl->Ecnt = sl->SLL;
+
+ sl->Einc = sl->EincS;
+ sl->Ecmp = ENV_END;
+ sl->Ecurp = SUBSTAIN;
+ break;
+
+ case 2:
+ // Env_Substain_Next(slot_t *SL)
+ if(sl->SEG & 8) // SSG envelope type
+ {
+ if(sl->SEG & 1)
+ {
+ sl->Ecnt = ENV_END;
+ sl->Einc = 0;
+ sl->Ecmp = ENV_END + 1;
+ }
+ else
+ {
+ // re KEY ON
+
+ // SL->Fcnt = 0;
+ // SL->ChgEnM = 0xFFFFFFFF;
+
+ sl->Ecnt = 0;
+ sl->Einc = sl->EincA;
+ sl->Ecmp = ENV_DECAY;
+ sl->Ecurp = ATTACK;
+ }
+
+ sl->SEG ^= (sl->SEG & 2) << 1;
+
+ break;
+ }
+ // fall through
+
+ case 3:
+ // Env_Release_Next
+ sl->Ecnt = ENV_END;
+ sl->Einc = 0;
+ sl->Ecmp = ENV_END + 1;
+ break;
+
+ // default: no op
+ }
+}
+
+inline void update_envelope( slot_t& sl )
+{
+ int ecmp = sl.Ecmp;
+ if ( (sl.Ecnt += sl.Einc) >= ecmp )
+ update_envelope_( &sl );
+}
+
+template
+struct ym2612_update_chan {
+ static void func( tables_t&, channel_t&, Ym2612_Emu::sample_t [], int );
+};
+
+typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int );
+
+template
+void ym2612_update_chan::func( tables_t& g, channel_t& ch,
+ Ym2612_Emu::sample_t* buf, int length )
+{
+ int not_end = ch.SLOT [S3].Ecnt - ENV_END;
+
+ // algo is a compile-time constant, so all conditions based on it are resolved
+ // during compilation
+
+ // special cases
+ if ( algo == 7 )
+ not_end |= ch.SLOT [S0].Ecnt - ENV_END;
+
+ if ( algo >= 5 )
+ not_end |= ch.SLOT [S2].Ecnt - ENV_END;
+
+ if ( algo >= 4 )
+ not_end |= ch.SLOT [S1].Ecnt - ENV_END;
+
+ int CH_S0_OUT_1 = ch.S0_OUT [1];
+
+ int in0 = ch.SLOT [S0].Fcnt;
+ int in1 = ch.SLOT [S1].Fcnt;
+ int in2 = ch.SLOT [S2].Fcnt;
+ int in3 = ch.SLOT [S3].Fcnt;
+
+ int YM2612_LFOinc = g.LFOinc;
+ int YM2612_LFOcnt = g.LFOcnt + YM2612_LFOinc;
+
+ if ( !not_end )
+ return;
+
+ do
+ {
+ // envelope
+ int const env_LFO = g.LFO_ENV_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK];
+
+ short const* const ENV_TAB = g.ENV_TAB;
+
+ #define CALC_EN( x ) \
+ int temp##x = ENV_TAB [ch.SLOT [S##x].Ecnt >> ENV_LBITS]; \
+ int en##x; \
+ if (ch.SLOT [S##x].SEG & 4) { \
+ if (temp##x > ENV_MASK) en##x = 0; \
+ else en##x = (temp##x ^ ENV_MASK) + ch.SLOT [S##x].TLL + (env_LFO >> ch.SLOT [S##x].AMS); \
+ } else en##x = temp##x + ch.SLOT [S##x].TLL + (env_LFO >> ch.SLOT [S##x].AMS);
+
+ CALC_EN( 0 )
+ CALC_EN( 1 )
+ CALC_EN( 2 )
+ CALC_EN( 3 )
+
+ int const* const TL_TAB = g.TL_TAB;
+
+ #define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)])
+
+ // feedback
+ int CH_S0_OUT_0 = ch.S0_OUT [0];
+ {
+ int temp = in0 + ((CH_S0_OUT_0 + CH_S0_OUT_1) >> ch.FB);
+ CH_S0_OUT_1 = CH_S0_OUT_0;
+ CH_S0_OUT_0 = SINT( (temp >> SIN_LBITS) & SIN_MASK, en0 );
+ }
+
+ int CH_OUTd;
+ if ( algo == 0 )
+ {
+ int temp = in1 + CH_S0_OUT_1;
+ temp = in2 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 );
+ temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
+ CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+ }
+ else if ( algo == 1 )
+ {
+ int temp = in2 + CH_S0_OUT_1 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 );
+ temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
+ CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+ }
+ else if ( algo == 2 )
+ {
+ int temp = in2 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 );
+ temp = in3 + CH_S0_OUT_1 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 );
+ CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+ }
+ else if ( algo == 3 )
+ {
+ int temp = in1 + CH_S0_OUT_1;
+ temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ) +
+ SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
+ CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 );
+ }
+ else if ( algo == 4 )
+ {
+ int temp = in3 + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
+ CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ) +
+ SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 );
+ //DO_LIMIT
+ }
+ else if ( algo == 5 )
+ {
+ int temp = CH_S0_OUT_1;
+ CH_OUTd = SINT( ((in3 + temp) >> SIN_LBITS) & SIN_MASK, en3 ) +
+ SINT( ((in1 + temp) >> SIN_LBITS) & SIN_MASK, en1 ) +
+ SINT( ((in2 + temp) >> SIN_LBITS) & SIN_MASK, en2 );
+ //DO_LIMIT
+ }
+ else if ( algo == 6 )
+ {
+ CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) +
+ SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ) +
+ SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 );
+ //DO_LIMIT
+ }
+ else if ( algo == 7 )
+ {
+ CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) +
+ SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ) +
+ SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ) + CH_S0_OUT_1;
+ //DO_LIMIT
+ }
+
+ CH_OUTd *= 3;
+ CH_OUTd >>= MAX_OUT_BITS - output_bits + 2;
+
+ // update phase
+ unsigned freq_LFO = ((g.LFO_FREQ_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK] *
+ ch.FMS) >> (LFO_HBITS - 1 + 1)) + (1 << (LFO_FMS_LBITS - 1));
+ YM2612_LFOcnt += YM2612_LFOinc;
+ in0 += (ch.SLOT [S0].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
+ in1 += (ch.SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
+ in2 += (ch.SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
+ in3 += (ch.SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1);
+
+ int t0 = buf [0] + (CH_OUTd & ch.LEFT);
+ int t1 = buf [1] + (CH_OUTd & ch.RIGHT);
+
+ update_envelope( ch.SLOT [0] );
+ update_envelope( ch.SLOT [1] );
+ update_envelope( ch.SLOT [2] );
+ update_envelope( ch.SLOT [3] );
+
+ ch.S0_OUT [0] = CH_S0_OUT_0;
+ buf [0] = t0;
+ buf [1] = t1;
+ buf += 2;
+ }
+ while ( --length );
+
+ ch.S0_OUT [1] = CH_S0_OUT_1;
+
+ ch.SLOT [S0].Fcnt = in0;
+ ch.SLOT [S1].Fcnt = in1;
+ ch.SLOT [S2].Fcnt = in2;
+ ch.SLOT [S3].Fcnt = in3;
+}
+
+static const ym2612_update_chan_t UPDATE_CHAN [8] = {
+ &ym2612_update_chan<0>::func,
+ &ym2612_update_chan<1>::func,
+ &ym2612_update_chan<2>::func,
+ &ym2612_update_chan<3>::func,
+ &ym2612_update_chan<4>::func,
+ &ym2612_update_chan<5>::func,
+ &ym2612_update_chan<6>::func,
+ &ym2612_update_chan<7>::func
+};
+
+void Ym2612_Impl::run_timer( int length )
+{
+ int const step = 6;
+ int remain = length;
+ do
+ {
+ int n = step;
+ if ( n > remain )
+ n = remain;
+ remain -= n;
+
+ int i = n * YM2612.TimerBase;
+ if (YM2612.Mode & 1) // Timer A ON ?
+ {
+ // if ((YM2612.TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL)
+ if ((YM2612.TimerAcnt -= i) <= 0)
+ {
+ // timer a overflow
+
+ YM2612.Status |= (YM2612.Mode & 0x04) >> 2;
+ YM2612.TimerAcnt += YM2612.TimerAL;
+
+ if (YM2612.Mode & 0x80)
+ {
+ KEY_ON( YM2612.CHANNEL [2], 0 );
+ KEY_ON( YM2612.CHANNEL [2], 1 );
+ KEY_ON( YM2612.CHANNEL [2], 2 );
+ KEY_ON( YM2612.CHANNEL [2], 3 );
+ }
+ }
+ }
+
+ if (YM2612.Mode & 2) // Timer B ON ?
+ {
+ // if ((YM2612.TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL)
+ if ((YM2612.TimerBcnt -= i) <= 0)
+ {
+ // timer b overflow
+ YM2612.Status |= (YM2612.Mode & 0x08) >> 2;
+ YM2612.TimerBcnt += YM2612.TimerBL;
+ }
+ }
+ }
+ while ( remain > 0 );
+}
+
+void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t out [] )
+{
+ if ( pair_count <= 0 )
+ return;
+
+ if ( YM2612.Mode & 3 )
+ run_timer( pair_count );
+
+ // Mise à jour des pas des compteurs-frequences s'ils ont ete modifies
+
+ for ( int chi = 0; chi < channel_count; chi++ )
+ {
+ channel_t& ch = YM2612.CHANNEL [chi];
+ if ( ch.SLOT [0].Finc != -1 )
+ continue;
+
+ int i2 = 0;
+ if ( chi == 2 && (YM2612.Mode & 0x40) )
+ i2 = 2;
+
+ for ( int i = 0; i < 4; i++ )
+ {
+ // static int seq [4] = { 2, 1, 3, 0 };
+ // if ( i2 ) i2 = seq [i];
+
+ slot_t& sl = ch.SLOT [i];
+ int finc = g.FINC_TAB [ch.FNUM [i2]] >> (7 - ch.FOCT [i2]);
+ int ksr = ch.KC [i2] >> sl.KSR_S; // keycode attenuation
+ sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL;
+ if (sl.KSR != ksr) // si le KSR a change alors
+ { // les differents taux pour l'enveloppe sont mis à jour
+ sl.KSR = ksr;
+
+ sl.EincA = sl.AR [ksr];
+ sl.EincD = sl.DR [ksr];
+ sl.EincS = sl.SR [ksr];
+ sl.EincR = sl.RR [ksr];
+
+ if (sl.Ecurp == ATTACK)
+ {
+ sl.Einc = sl.EincA;
+ }
+ else if (sl.Ecurp == DECAY)
+ {
+ sl.Einc = sl.EincD;
+ }
+ else if (sl.Ecnt < ENV_END)
+ {
+ if (sl.Ecurp == SUBSTAIN)
+ sl.Einc = sl.EincS;
+ else if (sl.Ecurp == RELEASE)
+ sl.Einc = sl.EincR;
+ }
+ }
+
+ if ( i2 )
+ i2 = (i2 ^ 2) ^ (i2 >> 1);
+ }
+ }
+
+ for ( int i = 0; i < channel_count; i++ )
+ {
+ if ( !(mute_mask & (1 << i)) && (i != 5 || !YM2612.DAC) )
+ UPDATE_CHAN [YM2612.CHANNEL [i].ALGO]( g, YM2612.CHANNEL [i], out, pair_count );
+ }
+
+ g.LFOcnt += g.LFOinc * pair_count;
+}
+
+void Ym2612_Emu::run( int pair_count, sample_t out [] ) { impl->run( pair_count, out ); }
diff --git a/Frameworks/GME/gme/Ym2612_Emu_MAME.cpp b/Frameworks/GME/gme/Ym2612_Emu_MAME.cpp
new file mode 100644
index 000000000..8765d2906
--- /dev/null
+++ b/Frameworks/GME/gme/Ym2612_Emu_MAME.cpp
@@ -0,0 +1,87 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Ym2612_Emu.h"
+#include "fm.h"
+
+#include "blargg_errors.h"
+
+// Ym2612_Emu
+
+Ym2612_Emu::~Ym2612_Emu()
+{
+ if ( impl )
+ ym2612_shutdown( impl );
+}
+
+const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate )
+{
+ if ( impl )
+ {
+ ym2612_shutdown( impl );
+ impl = 0;
+ }
+
+ if ( !clock_rate )
+ clock_rate = sample_rate * 144.;
+
+ impl = ym2612_init( (long) (clock_rate + 0.5), (long) (sample_rate + 0.5) );
+ if ( !impl )
+ return blargg_err_memory;
+
+ return 0;
+}
+
+void Ym2612_Emu::reset()
+{
+ ym2612_reset_chip( impl );
+}
+
+static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL};
+
+void Ym2612_Emu::write0( int addr, int data )
+{
+ ym2612_update_one( impl, DUMMYBUF, 0 );
+ ym2612_write( impl, 0, addr );
+ ym2612_write( impl, 1, data );
+}
+
+void Ym2612_Emu::write1( int addr, int data )
+{
+ ym2612_update_one( impl, DUMMYBUF, 0 );
+ ym2612_write( impl, 2, addr );
+ ym2612_write( impl, 3, data );
+}
+
+void Ym2612_Emu::mute_voices( int mask )
+{
+ ym2612_set_mutemask( impl, mask );
+}
+
+void Ym2612_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ ym2612_update_one( impl, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/Ym3812_Emu.cpp b/Frameworks/GME/gme/Ym3812_Emu.cpp
new file mode 100644
index 000000000..ddec0357b
--- /dev/null
+++ b/Frameworks/GME/gme/Ym3812_Emu.cpp
@@ -0,0 +1,66 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Ym3812_Emu.h"
+
+#include
+#include "dbopl.h"
+
+Ym3812_Emu::Ym3812_Emu() { opl = 0; }
+
+Ym3812_Emu::~Ym3812_Emu()
+{
+ delete opl;
+}
+
+int Ym3812_Emu::set_rate( int sample_rate, int clock_rate )
+{
+ delete opl;
+ opl = 0;
+
+ opl = new DBOPL::Chip;
+ if ( !opl )
+ return 1;
+
+ this->sample_rate = sample_rate;
+ this->clock_rate = clock_rate * 4;
+
+ reset();
+ return 0;
+}
+
+void Ym3812_Emu::reset()
+{
+ opl->Setup( clock_rate, sample_rate );
+}
+
+void Ym3812_Emu::write( int addr, int data )
+{
+ opl->WriteReg( addr, data );
+}
+
+void Ym3812_Emu::run( int pair_count, sample_t* out )
+{
+ Bit32s buf[ 1024 ];
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ opl->GenerateBlock2( todo, buf );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l, output_r;
+ int output = buf [i];
+ output_l = output + out [0];
+ output_r = output + out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/Ym3812_Emu.h b/Frameworks/GME/gme/Ym3812_Emu.h
new file mode 100644
index 000000000..a3db4d85a
--- /dev/null
+++ b/Frameworks/GME/gme/Ym3812_Emu.h
@@ -0,0 +1,35 @@
+// YM3812 FM sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef YM3812_EMU_H
+#define YM3812_EMU_H
+
+namespace DBOPL {
+ struct Chip;
+}
+
+class Ym3812_Emu {
+ DBOPL::Chip * opl;
+ unsigned sample_rate;
+ unsigned clock_rate;
+public:
+ Ym3812_Emu();
+ ~Ym3812_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int sample_rate, int clock_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Ymf262_Emu.cpp b/Frameworks/GME/gme/Ymf262_Emu.cpp
new file mode 100644
index 000000000..f96ab6a36
--- /dev/null
+++ b/Frameworks/GME/gme/Ymf262_Emu.cpp
@@ -0,0 +1,93 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Ymf262_Emu.h"
+
+#include
+#include "dbopl.h"
+
+Ymf262_Emu::Ymf262_Emu() { opl = 0; }
+
+Ymf262_Emu::~Ymf262_Emu()
+{
+ delete opl;
+}
+
+int Ymf262_Emu::set_rate( int sample_rate, int clock_rate )
+{
+ delete opl;
+ opl = 0;
+
+ opl = new DBOPL::Chip;
+ if ( !opl )
+ return 1;
+
+ this->sample_rate = sample_rate;
+ this->clock_rate = clock_rate;
+
+ reset();
+ return 0;
+}
+
+void Ymf262_Emu::reset()
+{
+ opl->Setup( clock_rate, sample_rate );
+}
+
+void Ymf262_Emu::write0( int addr, int data )
+{
+ opl->WriteReg( addr, data );
+}
+
+void Ymf262_Emu::write1( int addr, int data )
+{
+ opl->WriteReg( 0x100 + addr, data );
+}
+
+void Ymf262_Emu::run( int pair_count, sample_t* out )
+{
+ Bit32s buf[ 2048 ];
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+
+ if ( opl->opl3Active )
+ {
+ opl->GenerateBlock3( todo, buf );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l, output_r;
+ output_l = buf [i * 2];
+ output_r = buf [i * 2 + 1];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+ }
+ else
+ {
+ opl->GenerateBlock2( todo, buf );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l, output_r;
+ int output = buf [i];
+ output_l = output + out [0];
+ output_r = output + out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/Ymf262_Emu.h b/Frameworks/GME/gme/Ymf262_Emu.h
new file mode 100644
index 000000000..c44cb3d3c
--- /dev/null
+++ b/Frameworks/GME/gme/Ymf262_Emu.h
@@ -0,0 +1,36 @@
+// YMF262 FM sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef YMF262_EMU_H
+#define YMF262_EMU_H
+
+namespace DBOPL {
+ struct Chip;
+}
+
+class Ymf262_Emu {
+ DBOPL::Chip * opl;
+ unsigned sample_rate;
+ unsigned clock_rate;
+public:
+ Ymf262_Emu();
+ ~Ymf262_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int sample_rate, int clock_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Writes data to addr
+ void write0( int addr, int data );
+ void write1( int addr, int data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Ymz280b_Emu.cpp b/Frameworks/GME/gme/Ymz280b_Emu.cpp
new file mode 100644
index 000000000..3bce2505e
--- /dev/null
+++ b/Frameworks/GME/gme/Ymz280b_Emu.cpp
@@ -0,0 +1,78 @@
+// Game_Music_Emu $vers. http://www.slack.net/~ant/
+
+#include "Ymz280b_Emu.h"
+#include "ymz280b.h"
+
+Ymz280b_Emu::Ymz280b_Emu() { chip = 0; }
+
+Ymz280b_Emu::~Ymz280b_Emu()
+{
+ if ( chip ) device_stop_ymz280b( chip );
+}
+
+int Ymz280b_Emu::set_rate( int clock_rate )
+{
+ if ( chip )
+ {
+ device_stop_ymz280b( chip );
+ chip = 0;
+ }
+
+ chip = device_start_ymz280b( clock_rate );
+ if ( !chip )
+ return 0;
+
+ reset();
+ return clock_rate * 2 / 384;
+}
+
+void Ymz280b_Emu::reset()
+{
+ device_reset_ymz280b( chip );
+ ymz280b_set_mute_mask( chip, 0 );
+}
+
+void Ymz280b_Emu::write( int addr, int data )
+{
+ ymz280b_w( chip, 0, addr );
+ ymz280b_w( chip, 1, data );
+}
+
+void Ymz280b_Emu::write_rom( int size, int start, int length, void * data )
+{
+ ymz280b_write_rom( chip, size, start, length, (const UINT8 *) data );
+}
+
+void Ymz280b_Emu::mute_voices( int mask )
+{
+ ymz280b_set_mute_mask( chip, mask );
+}
+
+void Ymz280b_Emu::run( int pair_count, sample_t* out )
+{
+ stream_sample_t bufL[ 1024 ];
+ stream_sample_t bufR[ 1024 ];
+ stream_sample_t * buffers[2] = { bufL, bufR };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ ymz280b_update( chip, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output_l = bufL [i];
+ int output_r = bufR [i];
+ output_l += out [0];
+ output_r += out [1];
+ if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 );
+ if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 );
+ out [0] = output_l;
+ out [1] = output_r;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/Frameworks/GME/gme/Ymz280b_Emu.h b/Frameworks/GME/gme/Ymz280b_Emu.h
new file mode 100644
index 000000000..8c899d3ad
--- /dev/null
+++ b/Frameworks/GME/gme/Ymz280b_Emu.h
@@ -0,0 +1,36 @@
+// YMZ280B sound chip emulator interface
+
+// Game_Music_Emu $vers
+#ifndef YMZ280B_EMU_H
+#define YMZ280B_EMU_H
+
+class Ymz280b_Emu {
+ void* chip;
+public:
+ Ymz280b_Emu();
+ ~Ymz280b_Emu();
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( int clock_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 8 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Scales ROM size, then writes length bytes from data at start offset
+ void write_rom( int size, int start, int length, void * data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/Frameworks/GME/gme/Z80_Cpu.cpp b/Frameworks/GME/gme/Z80_Cpu.cpp
new file mode 100644
index 000000000..80fb28dea
--- /dev/null
+++ b/Frameworks/GME/gme/Z80_Cpu.cpp
@@ -0,0 +1,82 @@
+// $package. http://www.slack.net/~ant/
+
+#include "Z80_Cpu.h"
+
+/* Copyright (C) 2006-2008 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"
+
+// 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;
+
+Z80_Cpu::Z80_Cpu()
+{
+ cpu_state = &cpu_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;
+}
+
+inline void Z80_Cpu::set_page( int i, void* write, void const* read )
+{
+ int offset = Z80_CPU_OFFSET( i * page_size );
+ byte * write2 = STATIC_CAST(byte *,write) - offset;
+ byte const* read2 = STATIC_CAST(byte const*,read ) - offset;
+ cpu_state_.write [i] = write2;
+ cpu_state_.read [i] = read2;
+ cpu_state->write [i] = write2;
+ cpu_state->read [i] = read2;
+}
+
+void Z80_Cpu::reset( void* unmapped_write, void const* unmapped_read )
+{
+ check( cpu_state == &cpu_state_ );
+ cpu_state = &cpu_state_;
+ cpu_state_.time = 0;
+ cpu_state_.base = 0;
+ end_time_ = 0;
+
+ for ( int i = 0; i < page_count + 1; i++ )
+ set_page( i, unmapped_write, unmapped_read );
+
+ memset( &r, 0, sizeof r );
+}
+
+void Z80_Cpu::map_mem( addr_t start, int size, void* write, void const* read )
+{
+ // address range must begin and end on page boundaries
+ require( start % page_size == 0 );
+ require( size % page_size == 0 );
+ require( start + size <= 0x10000 );
+
+ for ( int offset = 0; offset < size; offset += page_size )
+ set_page( (start + offset) >> page_bits,
+ STATIC_CAST(char *,write) + offset,
+ STATIC_CAST(char const*,read ) + offset );
+}
diff --git a/Frameworks/GME/gme/Z80_Cpu.h b/Frameworks/GME/gme/Z80_Cpu.h
new file mode 100644
index 000000000..b5f6f4d51
--- /dev/null
+++ b/Frameworks/GME/gme/Z80_Cpu.h
@@ -0,0 +1,122 @@
+// Z80 CPU emulator
+
+// $package
+#ifndef Z80_CPU_H
+#define Z80_CPU_H
+
+#include "blargg_endian.h"
+
+class Z80_Cpu {
+public:
+ typedef int time_t;
+ typedef int addr_t;
+ typedef BOOST::uint8_t byte;
+
+ // Clears registers and maps all pages to unmapped
+ void reset( void* unmapped_write, void const* unmapped_read );
+
+ // TODO: split mapping out of CPU
+
+ // Maps memory. Start and size must be multiple of page_size.
+ enum { page_bits = 10 };
+ enum { page_size = 1 << page_bits };
+ void map_mem( addr_t addr, int size, void* write, void const* read );
+ void map_mem( addr_t addr, int size, void* read_write );
+
+ // Maps address to pointer to that byte
+ byte * write( addr_t addr );
+ byte const* read( addr_t addr );
+
+ // Time of beginning of next instruction
+ time_t time() const { return cpu_state->time + cpu_state->base; }
+
+ // Alter current time
+ void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; }
+ void adjust_time( int delta ) { cpu_state->time += delta; }
+
+ #if BLARGG_BIG_ENDIAN
+ struct regs_t { byte b,c, d,e, h,l, flags,a; };
+ #else
+ struct regs_t { byte c,b, e,d, l,h, a,flags; };
+ #endif
+ BLARGG_STATIC_ASSERT( sizeof (regs_t) == 8 );
+
+ struct pairs_t { BOOST::uint16_t bc, de, hl, fa; };
+
+ // Registers are not updated until run() returns
+ struct registers_t {
+ BOOST::uint16_t pc;
+ BOOST::uint16_t sp;
+ BOOST::uint16_t ix;
+ BOOST::uint16_t iy;
+ union {
+ regs_t b; // b.b, b.c, b.d, b.e, b.h, b.l, b.flags, b.a
+ pairs_t w; // w.bc, w.de, w.hl. w.fa
+ };
+ union {
+ regs_t b;
+ pairs_t w;
+ } alt;
+ byte iff1;
+ byte iff2;
+ byte r;
+ byte i;
+ byte im;
+ };
+ //registers_t r; (below for efficiency)
+
+ // can read this far past end of memory
+ enum { cpu_padding = 0x100 };
+
+ // Can read this many bytes past end of a page
+ enum { page_padding = 4 };
+
+ void set_end_time( time_t t );
+public:
+ Z80_Cpu();
+
+ enum { page_count = 0x10000 / page_size };
+ byte szpc [0x200];
+ time_t end_time_;
+ struct cpu_state_t {
+ byte const* read [page_count + 1];
+ byte * write [page_count + 1];
+ time_t base;
+ time_t time;
+ };
+ cpu_state_t* cpu_state; // points to cpu_state_ or a local copy within run()
+ cpu_state_t cpu_state_;
+ void set_page( int i, void* write, void const* read );
+public:
+ registers_t r;
+};
+
+#if BLARGG_NONPORTABLE
+ #define Z80_CPU_OFFSET( addr ) (addr)
+#else
+ #define Z80_CPU_OFFSET( addr ) ((addr) & (Z80_Cpu::page_size - 1))
+#endif
+
+inline Z80_Cpu::byte* Z80_Cpu::write( addr_t addr )
+{
+ return cpu_state->write [(unsigned) addr >> page_bits] + Z80_CPU_OFFSET( addr );
+}
+
+inline Z80_Cpu::byte const* Z80_Cpu::read( addr_t addr )
+{
+ return cpu_state->read [(unsigned) addr >> page_bits] + Z80_CPU_OFFSET( addr );
+}
+
+inline void Z80_Cpu::map_mem( addr_t addr, int size, void* p )
+{
+ map_mem( addr, size, p, p );
+}
+
+inline void Z80_Cpu::set_end_time( time_t t )
+{
+ time_t delta = cpu_state->base - t;
+ cpu_state->base = t;
+ cpu_state->time += delta;
+}
+
+#endif
diff --git a/Frameworks/GME/gme/Z80_Cpu_run.h b/Frameworks/GME/gme/Z80_Cpu_run.h
new file mode 100644
index 000000000..7bb490b77
--- /dev/null
+++ b/Frameworks/GME/gme/Z80_Cpu_run.h
@@ -0,0 +1,1701 @@
+// $package. http://www.slack.net/~ant/
+
+// Last validated with zexall 2009.12.05.
+// Doesn't implement the R register or immediate interrupt after EI.
+// Address wrap-around isn't completely correct, but is prevented from crashing emulator.
+// 16-bit memory accesses are made directly to mapped memory, instead of using macro.
+
+#if 0
+/* Define these macros in the source file before #including this file.
+- Parameters might be expressions, so they are best evaluated only once,
+though they NEVER have side-effects, so multiple evaluation is OK.
+- Output parameters might be a multiple-assignment expression like "a=x",
+so they must NOT be parenthesized.
+- Except where noted, time() and related functions will NOT work
+correctly inside a macro. TIME() is always correct, and between FLUSH_TIME() and
+CACHE_TIME() the normal time changing functions can be used.
+- Macros "returning" void may use a {} statement block. */
+
+ // 0 <= addr <= 0xFFFF + 0x100
+ // Optional; default uses whatever was set with map_mem()
+ int READ_CODE( addr_t );
+
+ // 0 <= addr <= 0xFFFF + 0x100
+ // Optional; default uses whatever was set with map_mem()
+ int READ_MEM( addr_t );
+ void WRITE_MEM( addr_t, int data );
+
+ // 0 <= port <= 0xFFFF (apparently upper 8 bits are output by hardware)
+ void OUT_PORT( int port, int data );
+ int IN_PORT int port );
+
+ // Reference to Z80_Cpu object used for emulation
+ #define CPU cpu
+
+// The following can be used within macros:
+
+ // Current time
+ time_t TIME();
+
+ // Allows use of time functions
+ void FLUSH_TIME();
+
+ // Must be used before end of macro if FLUSH_TIME() was used earlier
+ void CACHE_TIME();
+
+// Configuration (optional; commented behavior if defined)
+
+ // Optimizes as if map_mem( 0, 0x10000, FLAT_MEM, FLAT_MEM ) is always in effect
+ #define FLAT_MEM my_mem_array
+
+ // If RST 7 ($FF) is encountered and PC = IDLE_ADDR, stops execution
+ #define IDLE_ADDR 0x1234
+
+ // Expanded just before beginning of code, to help debugger
+ #define CPU_BEGIN void my_run_cpu() {
+
+#endif
+
+/* Copyright (C) 2006-2008 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 */
+
+#ifdef CPU_BEGIN
+ CPU_BEGIN
+#endif
+
+#define R CPU.r
+
+// 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 ) CPU.szpc [n]
+#define SZ28PC( n ) CPU.szpc [n]
+#define SZ28C( n ) (CPU.szpc [n] & ~P04)
+#define SZ28( n ) SZ28C( n )
+
+#define SET_R( n ) (void) (R.r = n)
+#define GET_R() (R.r)
+
+// Time
+#define TIME() (s_time + s.base)
+#define FLUSH_TIME() {s.time = s_time;}
+#define CACHE_TIME() {s_time = s.time;}
+
+// Memory
+#define RW_MEM( addr, rw ) RW_PAGE( addr, rw ) [RW_OFFSET( addr )]
+#ifndef READ_CODE
+ #define READ_CODE( addr ) RW_MEM( addr, read )
+#endif
+
+#ifdef FLAT_MEM
+ #define RW_PAGE( addr, rw ) FLAT_MEM
+ #define RW_OFFSET( addr ) (addr)
+ #define INSTR( off, addr ) READ_CODE( addr )
+#else
+ #define RW_PAGE( addr, rw ) s.rw [(unsigned) (addr) >> Z80_Cpu::page_bits]
+ #define RW_OFFSET( addr ) Z80_CPU_OFFSET( addr )
+ #define INSTR( off, addr ) instr [off]
+#endif
+
+#ifndef READ_MEM
+ #define READ_MEM( addr ) RW_MEM( addr, read )
+#endif
+
+#ifndef WRITE_MEM
+ #define WRITE_MEM( addr, data ) (RW_MEM( addr, write ) = data)
+#endif
+
+#define READ_WORD( addr ) GET_LE16( &RW_MEM( addr, read ) )
+#define WRITE_WORD( addr, data ) SET_LE16( &RW_MEM( addr, write ), data )
+
+// Truncation
+#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */
+#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
+#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */
+
+// Misc
+#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
+
+#if BLARGG_BIG_ENDIAN
+ #define R8( n, offset ) ((r.r8_ - offset) [n])
+#elif BLARGG_LITTLE_ENDIAN
+ #define R8( n, offset ) ((r.r8_ - offset) [(n) ^ 1])
+#else
+ #error "Byte order of CPU must be known"
+#endif
+
+#define R16( n, shift, offset ) (r.r16_ [((unsigned) (n) >> shift) - (offset >> shift)])
+
+#define EX( x, y ) \
+ {\
+ int temp = x;\
+ x = y;\
+ y = temp;\
+ }
+
+#define EXX( name ) \
+ EX( R.alt.name, r.name )
+
+bool warning = false;
+{
+ Z80_Cpu::cpu_state_t s;
+ #ifdef FLAT_MEM
+ s.base = CPU.cpu_state_.base;
+ #else
+ s = CPU.cpu_state_;
+ #endif
+ CPU.cpu_state = &s;
+
+
+ union r_t {
+ Z80_Cpu::regs_t b;
+ Z80_Cpu::pairs_t w;
+ byte r8_ [8]; // indexed
+ BOOST::uint16_t r16_ [4];
+ } r;
+ r.b = R.b;
+
+ Z80_Cpu::time_t s_time = CPU.cpu_state_.time;
+ int pc = R.pc;
+ int sp = R.sp;
+ int ix = R.ix; // TODO: keep in memory for direct access?
+ int iy = R.iy;
+ int flags = R.b.flags;
+
+ //goto loop; // confuses optimizer
+ s_time += 7;
+ pc -= 2;
+
+call_not_taken:
+ s_time -= 7;
+jp_not_taken:
+ pc += 2;
+loop:
+
+ check( (unsigned) pc < 0x10000 + 1 ); // +1 so emulator can catch wrap-around
+ check( (unsigned) sp < 0x10000 );
+ check( (unsigned) flags < 0x100 );
+ check( (unsigned) ix < 0x10000 );
+ check( (unsigned) iy < 0x10000 );
+
+ byte const* instr = RW_PAGE( pc, read );
+
+ int opcode;
+
+ if ( RW_OFFSET( ~0 ) == ~0 )
+ {
+ opcode = instr [RW_OFFSET( pc )];
+ pc++;
+ instr += RW_OFFSET( pc );
+ }
+ else
+ {
+ instr += RW_OFFSET( pc );
+ opcode = *instr++;
+ pc++;
+ }
+
+ static byte const clock_table [256 * 2] = {
+ // 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
+ 8,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1
+ 7,10,16, 6, 4, 4, 7, 4, 7,11,16, 6, 4, 4, 7, 4, // 2
+ 7,10,13, 6,11,11,10, 4, 7,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
+
+ // high four bits are $ED time - 8, low four bits are $DD/$FD time - 8
+ //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,
+ };
+
+ if ( s_time >= 0 )
+ goto out_of_time;
+ s_time += clock_table [opcode];
+
+ #ifdef Z80_CPU_LOG_H
+ //log_opcode( opcode, READ_CODE( pc ) );
+ z80_cpu_log( "log.txt", pc - 1, opcode, READ_CODE( pc ),
+ READ_CODE( pc + 1 ), READ_CODE( pc + 2 ) );
+ z80_log_regs( r.b.a, r.w.bc, r.w.de, r.w.hl, sp, ix, iy );
+ #endif
+
+#define GET_ADDR() GET_LE16( &INSTR( 0, pc ) )
+
+ int data;
+ data = INSTR( 0, pc );
+
+ switch ( opcode )
+ {
+// Common
+
+ case 0x00: // NOP
+ CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc.
+ goto loop;
+
+ case 0x08:{// EX AF,AF'
+ EXX( b.a );
+ EX( R.alt.b.flags, flags );
+ goto loop;
+ }
+
+ case 0xD3: // OUT (imm),A
+ pc++;
+ OUT_PORT( (data + r.b.a * 0x100), r.b.a );
+ goto loop;
+
+ case 0x2E: // LD L,imm
+ pc++;
+ r.b.l = data;
+ goto loop;
+
+ case 0x3E: // LD A,imm
+ pc++;
+ r.b.a = data;
+ goto loop;
+
+ case 0x3A:{// LD A,(addr)
+ int addr = GET_ADDR();
+ pc += 2;
+ r.b.a = READ_MEM( addr );
+ goto loop;
+ }
+
+// Conditional
+
+#define ZERO (flags & Z40)
+#define CARRY (flags & C01)
+#define EVEN (flags & P04)
+#define MINUS (flags & S80)
+
+// JR
+// TODO: more efficient way to handle negative branch that wraps PC around
+#define JR_( cond, clocks ) {\
+ pc++;\
+ if ( !(cond) )\
+ goto loop;\
+ int offset = SBYTE( data );\
+ pc = WORD( pc + offset );\
+ s_time += clocks;\
+ goto loop;\
+}
+
+#define JR( cond ) JR_( cond, 5 )
+
+ case 0x20: JR( !ZERO ) // JR NZ,disp
+ case 0x28: JR( ZERO ) // JR Z,disp
+ case 0x30: JR( !CARRY ) // JR NC,disp
+ case 0x38: JR( CARRY ) // JR C,disp
+ case 0x18: JR_( true,0) // JR disp
+
+ case 0x10:{// DJNZ disp
+ int temp = r.b.b - 1;
+ r.b.b = temp;
+ JR( temp )
+ }
+
+// JP
+#define JP( cond ) \
+ if ( !(cond) )\
+ goto jp_not_taken;\
+ pc = GET_ADDR();\
+ goto loop;
+
+ case 0xC2: JP( !ZERO ) // JP NZ,addr
+ case 0xCA: JP( ZERO ) // JP Z,addr
+ case 0xD2: JP( !CARRY ) // JP NC,addr
+ case 0xDA: JP( CARRY ) // JP C,addr
+ case 0xE2: JP( !EVEN ) // JP PO,addr
+ case 0xEA: JP( EVEN ) // JP PE,addr
+ case 0xF2: JP( !MINUS ) // JP P,addr
+ case 0xFA: JP( MINUS ) // JP M,addr
+
+ case 0xC3: // JP addr
+ pc = GET_ADDR();
+ goto loop;
+
+ case 0xE9: // JP HL
+ pc = r.w.hl;
+ goto loop;
+
+// RET
+#define RET( cond ) \
+ if ( cond )\
+ goto ret_taken;\
+ s_time -= 6;\
+ goto loop;
+
+ case 0xC0: RET( !ZERO ) // RET NZ
+ case 0xC8: RET( ZERO ) // RET Z
+ case 0xD0: RET( !CARRY ) // RET NC
+ case 0xD8: RET( CARRY ) // RET C
+ case 0xE0: RET( !EVEN ) // RET PO
+ case 0xE8: RET( EVEN ) // RET PE
+ case 0xF0: RET( !MINUS ) // RET P
+ case 0xF8: RET( MINUS ) // RET M
+
+ case 0xC9: // RET
+ ret_taken:
+ pc = READ_WORD( sp );
+ sp = WORD( sp + 2 );
+ goto loop;
+
+// CALL
+#define CALL( cond ) \
+ if ( cond )\
+ goto call_taken;\
+ goto call_not_taken;
+
+ case 0xC4: CALL( !ZERO ) // CALL NZ,addr
+ case 0xCC: CALL( ZERO ) // CALL Z,addr
+ case 0xD4: CALL( !CARRY ) // CALL NC,addr
+ case 0xDC: CALL( CARRY ) // CALL C,addr
+ case 0xE4: CALL( !EVEN ) // CALL PO,addr
+ case 0xEC: CALL( EVEN ) // CALL PE,addr
+ case 0xF4: CALL( !MINUS ) // CALL P,addr
+ case 0xFC: CALL( MINUS ) // CALL M,addr
+
+ case 0xCD:{// CALL addr
+ call_taken:
+ int addr = pc + 2;
+ pc = GET_ADDR();
+ sp = WORD( sp - 2 );
+ WRITE_WORD( sp, addr );
+ goto loop;
+ }
+
+ case 0xFF: // RST
+ #ifdef IDLE_ADDR
+ if ( pc == IDLE_ADDR + 1 )
+ goto hit_idle_addr;
+ #else
+ if ( pc > 0x10000 )
+ {
+ pc = WORD( pc - 1 );
+ s_time -= 11;
+ goto loop;
+ }
+ #endif
+ CASE7( C7, CF, D7, DF, E7, EF, F7 ):
+ data = pc;
+ pc = opcode & 0x38;
+ #ifdef RST_BASE
+ pc += RST_BASE;
+ #endif
+ goto push_data;
+
+// PUSH/POP
+ case 0xF5: // PUSH AF
+ data = r.b.a * 0x100u + flags;
+ goto push_data;
+
+ case 0xC5: // PUSH BC
+ case 0xD5: // PUSH DE
+ case 0xE5: // PUSH HL
+ data = R16( opcode, 4, 0xC5 );
+ push_data:
+ sp = WORD( sp - 2 );
+ WRITE_WORD( sp, data );
+ goto loop;
+
+ case 0xF1: // POP AF
+ flags = READ_MEM( sp );
+ r.b.a = READ_MEM( (sp + 1) );
+ sp = WORD( sp + 2 );
+ goto loop;
+
+ case 0xC1: // POP BC
+ case 0xD1: // POP DE
+ case 0xE1: // POP HL
+ R16( opcode, 4, 0xC1 ) = READ_WORD( sp );
+ sp = WORD( sp + 2 );
+ goto loop;
+
+// ADC/ADD/SBC/SUB
+ case 0x96: // SUB (HL)
+ case 0x86: // ADD (HL)
+ flags &= ~C01;
+ case 0x9E: // SBC (HL)
+ case 0x8E: // ADC (HL)
+ data = READ_MEM( r.w.hl );
+ goto adc_data;
+
+ case 0xD6: // SUB A,imm
+ case 0xC6: // ADD imm
+ flags &= ~C01;
+ case 0xDE: // SBC A,imm
+ case 0xCE: // ADC imm
+ pc++;
+ goto adc_data;
+
+ CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r
+ CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r
+ flags &= ~C01;
+ CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r
+ CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r
+ data = R8( opcode & 7, 0 );
+ adc_data: {
+ int result = data + (flags & C01);
+ data ^= r.b.a;
+ flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes
+ if ( flags )
+ result = -result;
+ result += r.b.a;
+ data ^= result;
+ flags +=(data & H10) +
+ ((data + 0x80) >> 6 & V04) +
+ SZ28C( result & 0x1FF );
+ r.b.a = result;
+ goto loop;
+ }
+
+// CP
+ case 0xBE: // CP (HL)
+ data = READ_MEM( r.w.hl );
+ goto cp_data;
+
+ case 0xFE: // CP imm
+ pc++;
+ goto cp_data;
+
+ CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r
+ data = R8( opcode, 0xB8 );
+ cp_data: {
+ int result = r.b.a - data;
+ flags = N02 + (data & (F20 | F08)) + (result >> 8 & C01);
+ data ^= r.b.a;
+ flags +=(((result ^ r.b.a) & data) >> 5 & V04) +
+ (((data & H10) ^ result) & (S80 | H10));
+ if ( BYTE( result ) )
+ goto loop;
+ flags += Z40;
+ goto loop;
+ }
+
+// ADD HL,r.w
+
+ case 0x39: // ADD HL,SP
+ data = sp;
+ goto add_hl_data;
+
+ case 0x09: // ADD HL,BC
+ case 0x19: // ADD HL,DE
+ case 0x29: // ADD HL,HL
+ data = R16( opcode, 4, 0x09 );
+ add_hl_data: {
+ int sum = r.w.hl + data;
+ data ^= r.w.hl;
+ r.w.hl = sum;
+ flags = (flags & (S80 | Z40 | V04)) +
+ (sum >> 16) +
+ (sum >> 8 & (F20 | F08)) +
+ ((data ^ sum) >> 8 & H10);
+ goto loop;
+ }
+
+ case 0x27:{// DAA
+ int a = r.b.a;
+ if ( a > 0x99 )
+ flags |= C01;
+
+ int adjust = 0x60 * (flags & C01);
+
+ if ( flags & H10 || (a & 0x0F) > 9 )
+ adjust += 0x06;
+
+ if ( flags & N02 )
+ adjust = -adjust;
+ a += adjust;
+
+ flags = (flags & (C01 | N02)) +
+ ((r.b.a ^ a) & H10) +
+ SZ28P( BYTE( a ) );
+ r.b.a = a;
+ goto loop;
+ }
+
+// INC/DEC
+ case 0x34: // INC (HL)
+ data = READ_MEM( r.w.hl ) + 1;
+ WRITE_MEM( r.w.hl, data );
+ goto inc_set_flags;
+
+ CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r
+ data = ++R8( opcode >> 3, 0 );
+ inc_set_flags:
+ flags = (flags & C01) +
+ (((data & 0x0F) - 1) & H10) +
+ SZ28( BYTE( data ) );
+ if ( data != 0x80 )
+ goto loop;
+ flags += V04;
+ goto loop;
+
+ case 0x35: // DEC (HL)
+ data = READ_MEM( r.w.hl ) - 1;
+ WRITE_MEM( r.w.hl, data );
+ goto dec_set_flags;
+
+ CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r
+ data = --R8( opcode >> 3, 0 );
+ dec_set_flags:
+ flags = (flags & C01) + N02 +
+ (((data & 0x0F) + 1) & H10) +
+ SZ28( BYTE( data ) );
+ if ( data != 0x7F )
+ goto loop;
+ flags += V04;
+ goto loop;
+
+ case 0x03: // INC BC
+ case 0x13: // INC DE
+ case 0x23: // INC HL
+ R16( opcode, 4, 0x03 )++;
+ goto loop;
+
+ case 0x33: // INC SP
+ sp = WORD( sp + 1 );
+ goto loop;
+
+ case 0x0B: // DEC BC
+ case 0x1B: // DEC DE
+ case 0x2B: // DEC HL
+ R16( opcode, 4, 0x0B )--;
+ goto loop;
+
+ case 0x3B: // DEC SP
+ sp = WORD( sp - 1 );
+ goto loop;
+
+// AND
+ case 0xA6: // AND (HL)
+ data = READ_MEM( r.w.hl );
+ goto and_data;
+
+ case 0xE6: // AND imm
+ pc++;
+ goto and_data;
+
+ CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r
+ data = R8( opcode, 0xA0 );
+ and_data:
+ r.b.a &= data;
+ flags = SZ28P( r.b.a ) + H10;
+ goto loop;
+
+// OR
+ case 0xB6: // OR (HL)
+ data = READ_MEM( r.w.hl );
+ goto or_data;
+
+ case 0xF6: // OR imm
+ pc++;
+ goto or_data;
+
+ CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r
+ data = R8( opcode, 0xB0 );
+ or_data:
+ r.b.a |= data;
+ flags = SZ28P( r.b.a );
+ goto loop;
+
+// XOR
+ case 0xAE: // XOR (HL)
+ data = READ_MEM( r.w.hl );
+ goto xor_data;
+
+ case 0xEE: // XOR imm
+ pc++;
+ goto xor_data;
+
+ CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r
+ data = R8( opcode, 0xA8 );
+ xor_data:
+ r.b.a ^= data;
+ flags = SZ28P( r.b.a );
+ goto loop;
+
+// LD
+ CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r
+ WRITE_MEM( r.w.hl, R8( opcode, 0x70 ) );
+ goto loop;
+
+ CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r
+ CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r
+ CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r
+ CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r
+ CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r
+ CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r
+ CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r
+ R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 );
+ goto loop;
+
+ CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm
+ R8( opcode >> 3, 0 ) = data;
+ pc++;
+ goto loop;
+
+ case 0x36: // LD (HL),imm
+ pc++;
+ WRITE_MEM( r.w.hl, data );
+ goto loop;
+
+ CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL)
+ R8( opcode >> 3, 8 ) = READ_MEM( r.w.hl );
+ goto loop;
+
+ case 0x01: // LD r.w,imm
+ case 0x11:
+ case 0x21:
+ R16( opcode, 4, 0x01 ) = GET_ADDR();
+ pc += 2;
+ goto loop;
+
+ case 0x31: // LD sp,imm
+ sp = GET_ADDR();
+ pc += 2;
+ goto loop;
+
+ case 0x2A:{// LD HL,(addr)
+ int addr = GET_ADDR();
+ pc += 2;
+ r.w.hl = READ_WORD( addr );
+ goto loop;
+ }
+
+ case 0x32:{// LD (addr),A
+ int addr = GET_ADDR();
+ pc += 2;
+ WRITE_MEM( addr, r.b.a );
+ goto loop;
+ }
+
+ case 0x22:{// LD (addr),HL
+ int addr = GET_ADDR();
+ pc += 2;
+ WRITE_WORD( addr, r.w.hl );
+ goto loop;
+ }
+
+ case 0x02: // LD (BC),A
+ case 0x12: // LD (DE),A
+ WRITE_MEM( R16( opcode, 4, 0x02 ), r.b.a );
+ goto loop;
+
+ case 0x0A: // LD A,(BC)
+ case 0x1A: // LD A,(DE)
+ r.b.a = READ_MEM( R16( opcode, 4, 0x0A ) );
+ goto loop;
+
+ case 0xF9: // LD SP,HL
+ sp = r.w.hl;
+ goto loop;
+
+// Rotate
+
+ case 0x07:{// RLCA
+ int temp = r.b.a;
+ temp = (temp << 1) + (temp >> 7);
+ flags = (flags & (S80 | Z40 | P04)) +
+ (temp & (F20 | F08 | C01));
+ r.b.a = temp;
+ goto loop;
+ }
+
+ case 0x0F:{// RRCA
+ int temp = r.b.a;
+ flags = (flags & (S80 | Z40 | P04)) +
+ (temp & C01);
+ temp = (temp << 7) + (temp >> 1);
+ flags += temp & (F20 | F08);
+ r.b.a = temp;
+ goto loop;
+ }
+
+ case 0x17:{// RLA
+ int temp = (r.b.a << 1) + (flags & C01);
+ flags = (flags & (S80 | Z40 | P04)) +
+ (temp & (F20 | F08)) +
+ (temp >> 8);
+ r.b.a = temp;
+ goto loop;
+ }
+
+ case 0x1F:{// RRA
+ int temp = (flags << 7) + (r.b.a >> 1);
+ flags = (flags & (S80 | Z40 | P04)) +
+ (temp & (F20 | F08)) +
+ (r.b.a & C01);
+ r.b.a = temp;
+ goto loop;
+ }
+
+// Misc
+ case 0x2F:{// CPL
+ int temp = ~r.b.a;
+ flags = (flags & (S80 | Z40 | P04 | C01)) +
+ (temp & (F20 | F08)) +
+ (H10 | N02);
+ r.b.a = temp;
+ goto loop;
+ }
+
+ case 0x3F:{// CCF
+ flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) +
+ (flags << 4 & H10) +
+ (r.b.a & (F20 | F08));
+ goto loop;
+ }
+
+ case 0x37: // SCF
+ flags = (flags & (S80 | Z40 | P04)) | C01 +
+ (r.b.a & (F20 | F08));
+ goto loop;
+
+ case 0xDB: // IN A,(imm)
+ pc++;
+ r.b.a = IN_PORT( (data + r.b.a * 0x100) );
+ goto loop;
+
+ case 0xE3:{// EX (SP),HL
+ int temp = READ_WORD( sp );
+ WRITE_WORD( sp, r.w.hl );
+ r.w.hl = temp;
+ goto loop;
+ }
+
+ case 0xEB: // EX DE,HL
+ EX( r.w.hl, r.w.de );
+ goto loop;
+
+ case 0xD9: // EXX DE,HL
+ EXX( w.bc );
+ EXX( w.de );
+ EXX( w.hl );
+ goto loop;
+
+ case 0xF3: // DI
+ R.iff1 = 0;
+ R.iff2 = 0;
+ goto loop;
+
+ case 0xFB: // EI
+ R.iff1 = 1;
+ R.iff2 = 1;
+ // TODO: delayed effect
+ goto loop;
+
+ case 0x76: // HALT
+ goto halt;
+
+//////////////////////////////////////// CB prefix
+ {
+ case 0xCB:
+ pc++;
+ switch ( data )
+ {
+
+ // Rotate left
+
+ #define RLC( read, write ) {\
+ int result = read;\
+ result = BYTE( result << 1 ) + (result >> 7);\
+ flags = SZ28P( result ) + (result & C01);\
+ write;\
+ goto loop;\
+ }
+
+ case 0x06: // RLC (HL)
+ s_time += 7;
+ data = r.w.hl;
+ rlc_data_addr:
+ RLC( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r
+ byte& reg = R8( data, 0 );
+ RLC( reg, reg = result )
+ }
+
+ #define RL( read, write ) {\
+ int result = (read << 1) + (flags & C01);\
+ flags = SZ28PC( result );\
+ write;\
+ goto loop;\
+ }
+
+ case 0x16: // RL (HL)
+ s_time += 7;
+ data = r.w.hl;
+ rl_data_addr:
+ RL( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r
+ byte& reg = R8( data, 0x10 );
+ RL( reg, reg = result )
+ }
+
+ #define SLA( read, low_bit, write ) {\
+ int result = (read << 1) + low_bit;\
+ flags = SZ28PC( result );\
+ write;\
+ goto loop;\
+ }
+
+ case 0x26: // SLA (HL)
+ s_time += 7;
+ data = r.w.hl;
+ sla_data_addr:
+ SLA( READ_MEM( data ), 0, WRITE_MEM( data, result ) )
+
+ CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r
+ byte& reg = R8( data, 0x20 );
+ SLA( reg, 0, reg = result )
+ }
+
+ case 0x36: // SLL (HL)
+ s_time += 7;
+ data = r.w.hl;
+ sll_data_addr:
+ SLA( READ_MEM( data ), 1, WRITE_MEM( data, result ) )
+
+ CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r
+ byte& reg = R8( data, 0x30 );
+ SLA( reg, 1, reg = result )
+ }
+
+ // Rotate right
+
+ #define RRC( read, write ) {\
+ int result = read;\
+ flags = result & C01;\
+ result = BYTE( result << 7 ) + (result >> 1);\
+ flags += SZ28P( result );\
+ write;\
+ goto loop;\
+ }
+
+ case 0x0E: // RRC (HL)
+ s_time += 7;
+ data = r.w.hl;
+ rrc_data_addr:
+ RRC( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r
+ byte& reg = R8( data, 0x08 );
+ RRC( reg, reg = result )
+ }
+
+ #define RR( read, write ) {\
+ int result = read;\
+ int temp = result & C01;\
+ result = BYTE( flags << 7 ) + (result >> 1);\
+ flags = SZ28P( result ) + temp;\
+ write;\
+ goto loop;\
+ }
+
+ case 0x1E: // RR (HL)
+ s_time += 7;
+ data = r.w.hl;
+ rr_data_addr:
+ RR( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r
+ byte& reg = R8( data, 0x18 );
+ RR( reg, reg = result )
+ }
+
+ #define SRA( read, write ) {\
+ int result = read;\
+ flags = result & C01;\
+ result = (result & 0x80) + (result >> 1);\
+ flags += SZ28P( result );\
+ write;\
+ goto loop;\
+ }
+
+ case 0x2E: // SRA (HL)
+ data = r.w.hl;
+ s_time += 7;
+ sra_data_addr:
+ SRA( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r
+ byte& reg = R8( data, 0x28 );
+ SRA( reg, reg = result )
+ }
+
+ #define SRL( read, write ) {\
+ int result = read;\
+ flags = result & C01;\
+ result >>= 1;\
+ flags += SZ28P( result );\
+ write;\
+ goto loop;\
+ }
+
+ case 0x3E: // SRL (HL)
+ s_time += 7;
+ data = r.w.hl;
+ srl_data_addr:
+ SRL( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r
+ byte& reg = R8( data, 0x38 );
+ SRL( reg, reg = result )
+ }
+
+ // BIT
+ {
+ int temp;
+ CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL)
+ s_time += 4;
+ temp = READ_MEM( r.w.hl );
+ flags &= C01;
+ goto bit_temp;
+ CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r
+ CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r
+ CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r
+ CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r
+ CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r
+ CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r
+ CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r
+ CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r
+ temp = R8( data & 7, 0 );
+ flags = (flags & C01) + (temp & (F20 | F08));
+ bit_temp:
+ temp = temp & (1 << (data >> 3 & 7));
+ flags += (temp & S80) + H10;
+ flags += (unsigned) --temp >> 8 & (Z40 | P04);
+ goto loop;
+ }
+
+ // SET/RES
+ CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL)
+ CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL)
+ s_time += 7;
+ int temp = READ_MEM( r.w.hl );
+ int bit = 1 << (data >> 3 & 7);
+ temp |= bit; // SET
+ if ( !(data & 0x40) )
+ temp ^= bit; // RES
+ WRITE_MEM( r.w.hl, temp );
+ goto loop;
+ }
+
+ CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r
+ CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r
+ CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r
+ CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r
+ CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r
+ CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r
+ CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r
+ CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r
+ R8( data & 7, 0 ) |= 1 << (data >> 3 & 7);
+ goto loop;
+
+ CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r
+ CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r
+ CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r
+ CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r
+ CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r
+ CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r
+ CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r
+ CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r
+ R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7));
+ goto loop;
+ }
+ assert( false );
+ }
+
+#undef GET_ADDR
+#define GET_ADDR() GET_LE16( &INSTR( 1, pc ) )
+
+//////////////////////////////////////// ED prefix
+ {
+ case 0xED:
+ pc++;
+ s_time += (clock_table + 256) [data] >> 4;
+ switch ( data )
+ {
+ {
+ int temp;
+ case 0x72: // SBC HL,SP
+ case 0x7A: // ADC HL,SP
+ temp = sp;
+ if ( 0 )
+ case 0x42: // SBC HL,BC
+ case 0x52: // SBC HL,DE
+ case 0x62: // SBC HL,HL
+ case 0x4A: // ADC HL,BC
+ case 0x5A: // ADC HL,DE
+ case 0x6A: // ADC HL,HL
+ temp = R16( data >> 3 & 6, 1, 0 );
+ int sum = temp + (flags & C01);
+ flags = ~data >> 2 & N02;
+ if ( flags )
+ sum = -sum;
+ sum += r.w.hl;
+ temp ^= r.w.hl;
+ temp ^= sum;
+ flags +=(sum >> 16 & C01) +
+ (temp >> 8 & H10) +
+ (sum >> 8 & (S80 | F20 | F08)) +
+ ((temp + 0x8000) >> 14 & V04);
+ r.w.hl = sum;
+ if ( WORD( sum ) )
+ goto loop;
+ flags += Z40;
+ goto loop;
+ }
+
+ CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C)
+ int temp = IN_PORT( r.w.bc );
+ R8( data >> 3, 8 ) = temp;
+ flags = (flags & C01) + SZ28P( temp );
+ goto loop;
+ }
+
+ case 0x71: // OUT (C),0
+ r.b.flags = 0;
+ CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r
+ OUT_PORT( r.w.bc, R8( data >> 3, 8 ) );
+ goto loop;
+
+ {
+ int temp;
+ case 0x73: // LD (ADDR),SP
+ temp = sp;
+ if ( 0 )
+ case 0x43: // LD (ADDR),BC
+ case 0x53: // LD (ADDR),DE
+ temp = R16( data, 4, 0x43 );
+ int addr = GET_ADDR();
+ pc += 2;
+ WRITE_WORD( addr, temp );
+ goto loop;
+ }
+
+ case 0x4B: // LD BC,(ADDR)
+ case 0x5B:{// LD DE,(ADDR)
+ int addr = GET_ADDR();
+ pc += 2;
+ R16( data, 4, 0x4B ) = READ_WORD( addr );
+ goto loop;
+ }
+
+ case 0x7B:{// LD SP,(ADDR)
+ int addr = GET_ADDR();
+ pc += 2;
+ sp = READ_WORD( addr );
+ goto loop;
+ }
+
+ case 0x67:{// RRD
+ int temp = READ_MEM( r.w.hl );
+ WRITE_MEM( r.w.hl, ((r.b.a << 4) + (temp >> 4)) );
+ temp = (r.b.a & 0xF0) + (temp & 0x0F);
+ flags = (flags & C01) + SZ28P( temp );
+ r.b.a = temp;
+ goto loop;
+ }
+
+ case 0x6F:{// RLD
+ int temp = READ_MEM( r.w.hl );
+ WRITE_MEM( r.w.hl, ((temp << 4) + (r.b.a & 0x0F)) );
+ temp = (r.b.a & 0xF0) + (temp >> 4);
+ flags = (flags & C01) + SZ28P( temp );
+ r.b.a = temp;
+ goto loop;
+ }
+
+ CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG
+ opcode = 0x10; // flag to do SBC instead of ADC
+ flags &= ~C01;
+ data = r.b.a;
+ r.b.a = 0;
+ goto adc_data;
+
+ {
+ int inc;
+ case 0xA9: // CPD
+ case 0xB9: // CPDR
+ inc = -1;
+ if ( 0 )
+ case 0xA1: // CPI
+ case 0xB1: // CPIR
+ inc = +1;
+ int addr = r.w.hl;
+ r.w.hl = addr + inc;
+ int temp = READ_MEM( addr );
+
+ int result = r.b.a - temp;
+ flags = (flags & C01) + N02 +
+ ((((temp ^ r.b.a) & H10) ^ result) & (S80 | H10));
+
+ if ( !BYTE( result ) )
+ flags += Z40;
+ result -= (flags & H10) >> 4;
+ flags += result & F08;
+ flags += result << 4 & F20;
+ if ( !--r.w.bc )
+ goto loop;
+
+ flags += V04;
+ if ( flags & Z40 || data < 0xB0 )
+ goto loop;
+
+ pc -= 2;
+ s_time += 5;
+ goto loop;
+ }
+
+ {
+ int inc;
+ case 0xA8: // LDD
+ case 0xB8: // LDDR
+ inc = -1;
+ if ( 0 )
+ case 0xA0: // LDI
+ case 0xB0: // LDIR
+ inc = +1;
+ int addr = r.w.hl;
+ r.w.hl = addr + inc;
+ int temp = READ_MEM( addr );
+
+ addr = r.w.de;
+ r.w.de = addr + inc;
+ WRITE_MEM( addr, temp );
+
+ temp += r.b.a;
+ flags = (flags & (S80 | Z40 | C01)) +
+ (temp & F08) + (temp << 4 & F20);
+ if ( !--r.w.bc )
+ goto loop;
+
+ flags += V04;
+ if ( data < 0xB0 )
+ goto loop;
+
+ pc -= 2;
+ s_time += 5;
+ goto loop;
+ }
+
+ {
+ int inc;
+ case 0xAB: // OUTD
+ case 0xBB: // OTDR
+ inc = -1;
+ if ( 0 )
+ case 0xA3: // OUTI
+ case 0xB3: // OTIR
+ inc = +1;
+ int addr = r.w.hl;
+ r.w.hl = addr + inc;
+ int temp = READ_MEM( addr );
+
+ int b = --r.b.b;
+ flags = (temp >> 6 & N02) + SZ28( b );
+ if ( b && data >= 0xB0 )
+ {
+ pc -= 2;
+ s_time += 5;
+ }
+
+ OUT_PORT( r.w.bc, temp );
+ goto loop;
+ }
+
+ {
+ int inc;
+ case 0xAA: // IND
+ case 0xBA: // INDR
+ inc = -1;
+ if ( 0 )
+ case 0xA2: // INI
+ case 0xB2: // INIR
+ inc = +1;
+
+ int addr = r.w.hl;
+ r.w.hl = addr + inc;
+
+ int temp = IN_PORT( r.w.bc );
+
+ int b = --r.b.b;
+ flags = (temp >> 6 & N02) + SZ28( b );
+ if ( b && data >= 0xB0 )
+ {
+ pc -= 2;
+ s_time += 5;
+ }
+
+ WRITE_MEM( addr, temp );
+ goto loop;
+ }
+
+ case 0x47: // LD I,A
+ R.i = r.b.a;
+ goto loop;
+
+ case 0x4F: // LD R,A
+ SET_R( r.b.a );
+ dprintf( "LD R,A not supported\n" );
+ warning = true;
+ goto loop;
+
+ case 0x57: // LD A,I
+ r.b.a = R.i;
+ goto ld_ai_common;
+
+ case 0x5F: // LD A,R
+ r.b.a = GET_R();
+ dprintf( "LD A,R not supported\n" );
+ warning = true;
+ ld_ai_common:
+ flags = (flags & C01) + SZ28( r.b.a ) + (R.iff2 << 2 & V04);
+ goto loop;
+
+ CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN
+ R.iff1 = R.iff2;
+ goto ret_taken;
+
+ case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0
+ R.im = 0;
+ goto loop;
+
+ case 0x56: case 0x76: // IM 1
+ R.im = 1;
+ goto loop;
+
+ case 0x5E: case 0x7E: // IM 2
+ R.im = 2;
+ goto loop;
+
+ default:
+ dprintf( "Opcode $ED $%02X not supported\n", data );
+ warning = true;
+ goto loop;
+ }
+ assert( false );
+ }
+
+//////////////////////////////////////// DD/FD prefix
+ {
+ int ixy;
+ case 0xDD:
+ ixy = ix;
+ goto ix_prefix;
+ case 0xFD:
+ ixy = iy;
+ ix_prefix:
+ pc++;
+ int data2 = READ_CODE( pc );
+ s_time += (clock_table + 256) [data] & 0x0F;
+ switch ( data )
+ {
+ // TODO: more efficient way of avoid negative address
+ // TODO: avoid using this as argument to READ_MEM() since it is evaluated twice
+ #define IXY_DISP( ixy, disp ) WORD( (ixy ) + (disp))
+
+ #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in;
+
+ // ADD/ADC/SUB/SBC
+
+ case 0x96: // SUB (IXY+disp)
+ case 0x86: // ADD (IXY+disp)
+ flags &= ~C01;
+ case 0x9E: // SBC (IXY+disp)
+ case 0x8E: // ADC (IXY+disp)
+ pc++;
+ opcode = data;
+ data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );
+ goto adc_data;
+
+ case 0x94: // SUB HXY
+ case 0x84: // ADD HXY
+ flags &= ~C01;
+ case 0x9C: // SBC HXY
+ case 0x8C: // ADC HXY
+ opcode = data;
+ data = ixy >> 8;
+ goto adc_data;
+
+ case 0x95: // SUB LXY
+ case 0x85: // ADD LXY
+ flags &= ~C01;
+ case 0x9D: // SBC LXY
+ case 0x8D: // ADC LXY
+ opcode = data;
+ data = BYTE( ixy );
+ goto adc_data;
+
+ {
+ int temp;
+ case 0x39: // ADD IXY,SP
+ temp = sp;
+ goto add_ixy_data;
+
+ case 0x29: // ADD IXY,HL
+ temp = ixy;
+ goto add_ixy_data;
+
+ case 0x09: // ADD IXY,BC
+ case 0x19: // ADD IXY,DE
+ temp = R16( data, 4, 0x09 );
+ add_ixy_data: {
+ int sum = ixy + temp;
+ temp ^= ixy;
+ ixy = WORD( sum );
+ flags = (flags & (S80 | Z40 | V04)) +
+ (sum >> 16) +
+ (sum >> 8 & (F20 | F08)) +
+ ((temp ^ sum) >> 8 & H10);
+ goto set_ixy;
+ }
+ }
+
+ // AND
+ case 0xA6: // AND (IXY+disp)
+ pc++;
+ data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );
+ goto and_data;
+
+ case 0xA4: // AND HXY
+ data = ixy >> 8;
+ goto and_data;
+
+ case 0xA5: // AND LXY
+ data = BYTE( ixy );
+ goto and_data;
+
+ // OR
+ case 0xB6: // OR (IXY+disp)
+ pc++;
+ data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );
+ goto or_data;
+
+ case 0xB4: // OR HXY
+ data = ixy >> 8;
+ goto or_data;
+
+ case 0xB5: // OR LXY
+ data = BYTE( ixy );
+ goto or_data;
+
+ // XOR
+ case 0xAE: // XOR (IXY+disp)
+ pc++;
+ data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );
+ goto xor_data;
+
+ case 0xAC: // XOR HXY
+ data = ixy >> 8;
+ goto xor_data;
+
+ case 0xAD: // XOR LXY
+ data = BYTE( ixy );
+ goto xor_data;
+
+ // CP
+ case 0xBE: // CP (IXY+disp)
+ pc++;
+ data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );
+ goto cp_data;
+
+ case 0xBC: // CP HXY
+ data = ixy >> 8;
+ goto cp_data;
+
+ case 0xBD: // CP LXY
+ data = BYTE( ixy );
+ goto cp_data;
+
+ // LD
+ CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r
+ data = R8( data, 0x70 );
+ if ( 0 )
+ case 0x36: // LD (IXY+disp),imm
+ pc++, data = READ_CODE( pc );
+ pc++;
+ WRITE_MEM( IXY_DISP( ixy, SBYTE( data2 ) ), data );
+ goto loop;
+
+ CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY
+ R8( data >> 3, 8 ) = ixy >> 8;
+ goto loop;
+
+ case 0x64: // LD HXY,HXY
+ case 0x6D: // LD LXY,LXY
+ goto loop;
+
+ CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY
+ R8( data >> 3, 8 ) = ixy;
+ goto loop;
+
+ CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp)
+ pc++;
+ R8( data >> 3, 8 ) = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );
+ goto loop;
+
+ case 0x26: // LD HXY,imm
+ pc++;
+ goto ld_hxy_data;
+
+ case 0x65: // LD HXY,LXY
+ data2 = BYTE( ixy );
+ goto ld_hxy_data;
+
+ CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r
+ data2 = R8( data, 0x60 );
+ ld_hxy_data:
+ ixy = BYTE( ixy ) + (data2 << 8);
+ goto set_ixy;
+
+ case 0x2E: // LD LXY,imm
+ pc++;
+ goto ld_lxy_data;
+
+ case 0x6C: // LD LXY,HXY
+ data2 = ixy >> 8;
+ goto ld_lxy_data;
+
+ CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r
+ data2 = R8( data, 0x68 );
+ ld_lxy_data:
+ ixy = (ixy & 0xFF00) + data2;
+ set_ixy:
+ if ( opcode == 0xDD )
+ {
+ ix = ixy;
+ goto loop;
+ }
+ iy = ixy;
+ goto loop;
+
+ case 0xF9: // LD SP,IXY
+ sp = ixy;
+ goto loop;
+
+ case 0x22:{// LD (ADDR),IXY
+ int addr = GET_ADDR();
+ pc += 2;
+ WRITE_WORD( addr, ixy );
+ goto loop;
+ }
+
+ case 0x21: // LD IXY,imm
+ ixy = GET_ADDR();
+ pc += 2;
+ goto set_ixy;
+
+ case 0x2A:{// LD IXY,(addr)
+ int addr = GET_ADDR();
+ ixy = READ_WORD( addr );
+ pc += 2;
+ goto set_ixy;
+ }
+
+ // DD/FD CB prefix
+ case 0xCB: {
+ data = IXY_DISP( ixy, SBYTE( data2 ) );
+ pc++;
+ data2 = READ_CODE( pc );
+ pc++;
+ switch ( data2 )
+ {
+ case 0x06: goto rlc_data_addr; // RLC (IXY)
+ case 0x16: goto rl_data_addr; // RL (IXY)
+ case 0x26: goto sla_data_addr; // SLA (IXY)
+ case 0x36: goto sll_data_addr; // SLL (IXY)
+ case 0x0E: goto rrc_data_addr; // RRC (IXY)
+ case 0x1E: goto rr_data_addr; // RR (IXY)
+ case 0x2E: goto sra_data_addr; // SRA (IXY)
+ case 0x3E: goto srl_data_addr; // SRL (IXY)
+
+ CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp)
+ int temp = READ_MEM( data );
+ temp = temp & (1 << (data2 >> 3 & 7));
+ flags = (flags & C01) + H10 + (temp & S80);
+ flags += (unsigned) --temp >> 8 & (Z40 | P04);
+ goto loop;
+ }
+
+ CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp)
+ CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp)
+ int temp = READ_MEM( data );
+ int bit = 1 << (data2 >> 3 & 7);
+ temp |= bit; // SET
+ if ( !(data2 & 0x40) )
+ temp ^= bit; // RES
+ WRITE_MEM( data, temp );
+ goto loop;
+ }
+
+ default:
+ dprintf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 );
+ warning = true;
+ goto loop;
+ }
+ assert( false );
+ }
+
+ // INC/DEC
+ case 0x23: // INC IXY
+ ixy = WORD( ixy + 1 );
+ goto set_ixy;
+
+ case 0x2B: // DEC IXY
+ ixy = WORD( ixy - 1 );
+ goto set_ixy;
+
+ case 0x34: // INC (IXY+disp)
+ ixy = IXY_DISP( ixy, SBYTE( data2 ) );
+ pc++;
+ data = READ_MEM( ixy ) + 1;
+ WRITE_MEM( ixy, data );
+ goto inc_set_flags;
+
+ case 0x35: // DEC (IXY+disp)
+ ixy = IXY_DISP( ixy, SBYTE( data2 ) );
+ pc++;
+ data = READ_MEM( ixy ) - 1;
+ WRITE_MEM( ixy, data );
+ goto dec_set_flags;
+
+ case 0x24: // INC HXY
+ ixy = WORD( ixy + 0x100 );
+ data = ixy >> 8;
+ goto inc_xy_common;
+
+ case 0x2C: // INC LXY
+ data = BYTE( ixy + 1 );
+ ixy = (ixy & 0xFF00) + data;
+ inc_xy_common:
+ if ( opcode == 0xDD )
+ {
+ ix = ixy;
+ goto inc_set_flags;
+ }
+ iy = ixy;
+ goto inc_set_flags;
+
+ case 0x25: // DEC HXY
+ ixy = WORD( ixy - 0x100 );
+ data = ixy >> 8;
+ goto dec_xy_common;
+
+ case 0x2D: // DEC LXY
+ data = BYTE( ixy - 1 );
+ ixy = (ixy & 0xFF00) + data;
+ dec_xy_common:
+ if ( opcode == 0xDD )
+ {
+ ix = ixy;
+ goto dec_set_flags;
+ }
+ iy = ixy;
+ goto dec_set_flags;
+
+ // PUSH/POP
+ case 0xE5: // PUSH IXY
+ data = ixy;
+ goto push_data;
+
+ case 0xE1:{// POP IXY
+ ixy = READ_WORD( sp );
+ sp = WORD( sp + 2 );
+ goto set_ixy;
+ }
+
+ // Misc
+
+ case 0xE9: // JP (IXY)
+ pc = ixy;
+ goto loop;
+
+ case 0xE3:{// EX (SP),IXY
+ int temp = READ_WORD( sp );
+ WRITE_WORD( sp, ixy );
+ ixy = temp;
+ goto set_ixy;
+ }
+
+ default:
+ dprintf( "Unnecessary DD/FD prefix encountered\n" );
+ warning = true;
+ pc--;
+ goto loop;
+ }
+ assert( false );
+ }
+
+ }
+ dprintf( "Unhandled main opcode: $%02X\n", opcode );
+ assert( false );
+
+#ifdef IDLE_ADDR
+hit_idle_addr:
+ s_time -= 11;
+ goto out_of_time;
+#endif
+halt:
+ s_time &= 3; // increment by multiple of 4
+out_of_time:
+ pc--;
+
+ r.b.flags = flags;
+ R.ix = ix;
+ R.iy = iy;
+ R.sp = sp;
+ R.pc = pc;
+ R.b = r.b;
+
+ CPU.cpu_state_.base = s.base;
+ CPU.cpu_state_.time = s_time;
+ CPU.cpu_state = &CPU.cpu_state_;
+}
diff --git a/Frameworks/GME/gme/adlib.h b/Frameworks/GME/gme/adlib.h
new file mode 100644
index 000000000..7d5ef6846
--- /dev/null
+++ b/Frameworks/GME/gme/adlib.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2002-2009 The DOSBox Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* $Id: adlib.h,v 1.4 2009/04/26 10:33:53 harekiet Exp $ */
+
+#ifndef DOSBOX_ADLIB_H
+#define DOSBOX_ADLIB_H
+
+/*#include "dosbox.h"
+#include "mixer.h"
+#include "inout.h"
+#include "mixer.h"
+#include "setup.h"
+#include "pic.h"
+#include "hardware.h"*/
+
+
+namespace Adlib {
+
+struct Timer {
+ double start;
+ double delay;
+ bool enabled, overflow, masked;
+ Bit8u counter;
+ Timer() {
+ masked = false;
+ overflow = false;
+ enabled = false;
+ counter = 0;
+ delay = 0;
+ }
+ //Call update before making any further changes
+ void Update( double time ) {
+ if ( !enabled || !delay )
+ return;
+ double deltaStart = time - start;
+ //Only set the overflow flag when not masked
+ if ( deltaStart >= 0 && !masked ) {
+ overflow = 1;
+ }
+ }
+ //On a reset make sure the start is in sync with the next cycle
+ void Reset(const double& time ) {
+ overflow = false;
+ if ( !delay || !enabled )
+ return;
+ double delta = (time - start);
+ double rem = fmod( delta, delay );
+ double next = delay - rem;
+ start = time + next;
+ }
+ void Stop( ) {
+ enabled = false;
+ }
+ void Start( const double& time, Bits scale ) {
+ //Don't enable again
+ if ( enabled ) {
+ return;
+ }
+ enabled = true;
+ delay = 0.001 * (256 - counter ) * scale;
+ start = time + delay;
+ }
+
+};
+
+struct Chip {
+ //Last selected register
+ Timer timer[2];
+ //Check for it being a write to the timer
+ bool Write( Bit32u addr, Bit8u val );
+ //Read the current timer state, will use current double
+ Bit8u Read( );
+};
+
+//The type of handler this is
+typedef enum {
+ MODE_OPL2,
+ MODE_DUALOPL2,
+ MODE_OPL3
+} Mode;
+
+/*class Handler {
+public:
+ //Write an address to a chip, returns the address the chip sets
+ virtual Bit32u WriteAddr( Bit32u port, Bit8u val ) = 0;
+ //Write to a specific register in the chip
+ virtual void WriteReg( Bit32u addr, Bit8u val ) = 0;
+ //Generate a certain amount of samples
+ virtual void Generate( MixerChannel* chan, Bitu samples ) = 0;
+ //Initialize at a specific sample rate and mode
+ virtual void Init( Bitu rate ) = 0;
+ virtual ~Handler() {
+ }
+};*/
+
+//The cache for 2 chips or an opl3
+typedef Bit8u RegisterCache[512];
+
+//Internal class used for dro capturing
+class Capture;
+
+/*class Module: public Module_base {
+ IO_ReadHandleObject ReadHandler[3];
+ IO_WriteHandleObject WriteHandler[3];
+ MixerObject mixerObject;
+
+ //Mode we're running in
+ Mode mode;
+ //Last selected address in the chip for the different modes
+ union {
+ Bit32u normal;
+ Bit8u dual[2];
+ } reg;
+ void CacheWrite( Bit32u reg, Bit8u val );
+ void DualWrite( Bit8u index, Bit8u reg, Bit8u val );
+public:
+ static OPL_Mode oplmode;
+ MixerChannel* mixerChan;
+ Bit32u lastUsed; //Ticks when adlib was last used to turn of mixing after a few second
+
+ Handler* handler; //Handler that will generate the sound
+ RegisterCache cache;
+ Capture* capture;
+ Chip chip[2];
+
+ //Handle port writes
+ void PortWrite( Bitu port, Bitu val, Bitu iolen );
+ Bitu PortRead( Bitu port, Bitu iolen );
+ void Init( Mode m );
+
+ Module( Section* configuration);
+ ~Module();
+};*/
+
+
+} //Adlib namespace
+
+#endif
diff --git a/Frameworks/GME/gme/blargg_common.cpp b/Frameworks/GME/gme/blargg_common.cpp
new file mode 100644
index 000000000..ed548c4ca
--- /dev/null
+++ b/Frameworks/GME/gme/blargg_common.cpp
@@ -0,0 +1,58 @@
+// $package. http://www.slack.net/~ant/
+
+#include "blargg_common.h"
+
+/* Copyright (C) 2008-2009 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"
+
+BLARGG_NAMESPACE_BEGIN
+
+// defined here to avoid need for blargg_errors.cpp in simple programs
+blargg_err_def_t blargg_err_memory = BLARGG_ERR_MEMORY;
+
+void blargg_vector_::init()
+{
+ begin_ = NULL;
+ size_ = 0;
+}
+
+void blargg_vector_::clear()
+{
+ void* p = begin_;
+ begin_ = NULL;
+ size_ = 0;
+ free( p );
+}
+
+blargg_err_t blargg_vector_::resize_( size_t n, size_t elem_size )
+{
+ if ( n != size_ )
+ {
+ if ( n == 0 )
+ {
+ // Simpler to handle explicitly. Realloc will handle a size of 0,
+ // but then we have to avoid raising an error for a NULL return.
+ clear();
+ }
+ else
+ {
+ void* p = realloc( begin_, n * elem_size );
+ CHECK_ALLOC( p );
+ begin_ = p;
+ size_ = n;
+ }
+ }
+ return blargg_ok;
+}
+
+BLARGG_NAMESPACE_END
diff --git a/Frameworks/GME/gme/blargg_errors.cpp b/Frameworks/GME/gme/blargg_errors.cpp
new file mode 100644
index 000000000..552415659
--- /dev/null
+++ b/Frameworks/GME/gme/blargg_errors.cpp
@@ -0,0 +1,117 @@
+// $package. http://www.slack.net/~ant/
+
+#include "blargg_errors.h"
+
+/* Copyright (C) 2009 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"
+
+BLARGG_NAMESPACE_BEGIN
+
+blargg_err_def_t blargg_err_generic = BLARGG_ERR_GENERIC;
+// blargg_err_memory is defined in blargg_common.cpp
+blargg_err_def_t blargg_err_caller = BLARGG_ERR_CALLER;
+blargg_err_def_t blargg_err_internal = BLARGG_ERR_INTERNAL;
+blargg_err_def_t blargg_err_limitation = BLARGG_ERR_LIMITATION;
+
+blargg_err_def_t blargg_err_file_missing = BLARGG_ERR_FILE_MISSING;
+blargg_err_def_t blargg_err_file_read = BLARGG_ERR_FILE_READ;
+blargg_err_def_t blargg_err_file_write = BLARGG_ERR_FILE_WRITE;
+blargg_err_def_t blargg_err_file_io = BLARGG_ERR_FILE_IO;
+blargg_err_def_t blargg_err_file_full = BLARGG_ERR_FILE_FULL;
+blargg_err_def_t blargg_err_file_eof = BLARGG_ERR_FILE_EOF;
+
+blargg_err_def_t blargg_err_file_type = BLARGG_ERR_FILE_TYPE;
+blargg_err_def_t blargg_err_file_feature = BLARGG_ERR_FILE_FEATURE;
+blargg_err_def_t blargg_err_file_corrupt = BLARGG_ERR_FILE_CORRUPT;
+
+const char* blargg_err_str( blargg_err_t err )
+{
+ if ( !err )
+ return "";
+
+ if ( *err == BLARGG_ERR_TYPE("")[0] )
+ return err + 1;
+
+ return err;
+}
+
+bool blargg_is_err_type( blargg_err_t err, const char type [] )
+{
+ if ( err )
+ {
+ // True if first strlen(type) characters of err match type
+ char const* p = err;
+ while ( *type && *type == *p )
+ {
+ type++;
+ p++;
+ }
+
+ if ( !*type )
+ return true;
+ }
+
+ return false;
+}
+
+const char* blargg_err_details( blargg_err_t err )
+{
+ const char* p = err;
+ if ( !p )
+ {
+ p = "";
+ }
+ else if ( *p == BLARGG_ERR_TYPE("")[0] )
+ {
+ while ( *p && *p != ';' )
+ p++;
+
+ // Skip ; and space after it
+ if ( *p )
+ {
+ p++;
+
+ check( *p == ' ' );
+ if ( *p )
+ p++;
+ }
+ }
+ return p;
+}
+
+int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const codes [] )
+{
+ if ( !err )
+ return 0;
+
+ while ( codes->str && !blargg_is_err_type( err, codes->str ) )
+ codes++;
+
+ return codes->code;
+}
+
+blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const codes [] )
+{
+ if ( !code )
+ return blargg_ok;
+
+ while ( codes->str && codes->code != code )
+ codes++;
+
+ if ( !codes->str )
+ return blargg_err_generic;
+
+ return codes->str;
+}
+
+BLARGG_NAMESPACE_END
diff --git a/Frameworks/GME/gme/blargg_errors.h b/Frameworks/GME/gme/blargg_errors.h
new file mode 100644
index 000000000..10dd52884
--- /dev/null
+++ b/Frameworks/GME/gme/blargg_errors.h
@@ -0,0 +1,84 @@
+// Error strings and conversion functions
+
+// $package
+#ifndef BLARGG_ERRORS_H
+#define BLARGG_ERRORS_H
+
+#ifndef BLARGG_COMMON_H
+ #include "blargg_common.h"
+#endif
+
+BLARGG_NAMESPACE_BEGIN
+
+typedef const char blargg_err_def_t [];
+
+// Basic errors
+extern blargg_err_def_t blargg_err_generic;
+extern blargg_err_def_t blargg_err_memory;
+extern blargg_err_def_t blargg_err_caller;
+extern blargg_err_def_t blargg_err_internal;
+extern blargg_err_def_t blargg_err_limitation;
+
+// File low-level
+extern blargg_err_def_t blargg_err_file_missing; // not found
+extern blargg_err_def_t blargg_err_file_read;
+extern blargg_err_def_t blargg_err_file_write;
+extern blargg_err_def_t blargg_err_file_io;
+extern blargg_err_def_t blargg_err_file_full;
+extern blargg_err_def_t blargg_err_file_eof;
+
+// File high-level
+extern blargg_err_def_t blargg_err_file_type; // wrong file type
+extern blargg_err_def_t blargg_err_file_feature;
+extern blargg_err_def_t blargg_err_file_corrupt;
+
+// C string describing error, or "" if err == NULL
+const char* blargg_err_str( blargg_err_t err );
+
+// True iff error is of given type, or false if err == NULL
+bool blargg_is_err_type( blargg_err_t, const char type [] );
+
+// Details of error without describing main cause, or "" if err == NULL
+const char* blargg_err_details( blargg_err_t err );
+
+// Converts error string to integer code using mapping table. Calls blargg_is_err_type()
+// for each str and returns code on first match. Returns 0 if err == NULL.
+struct blargg_err_to_code_t {
+ const char* str;
+ int code;
+};
+int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const [] );
+
+// Converts error code back to string. If code == 0, returns NULL. If not in table,
+// returns blargg_err_generic.
+blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const [] );
+
+// Generates error string literal with details of cause
+#define BLARGG_ERR( type, str ) (type "; " str)
+
+// Extra space to make it clear when blargg_err_str() isn't called to get
+// printable version of error. At some point, I might prefix error strings
+// with a code, to speed conversion to a code.
+#define BLARGG_ERR_TYPE( str ) " " str
+
+// Error types to pass to BLARGG_ERR macro
+#define BLARGG_ERR_GENERIC BLARGG_ERR_TYPE( "operation failed" )
+#define BLARGG_ERR_MEMORY BLARGG_ERR_TYPE( "out of memory" )
+#define BLARGG_ERR_CALLER BLARGG_ERR_TYPE( "internal usage bug" )
+#define BLARGG_ERR_INTERNAL BLARGG_ERR_TYPE( "internal bug" )
+#define BLARGG_ERR_LIMITATION BLARGG_ERR_TYPE( "exceeded limitation" )
+
+#define BLARGG_ERR_FILE_MISSING BLARGG_ERR_TYPE( "file not found" )
+#define BLARGG_ERR_FILE_READ BLARGG_ERR_TYPE( "couldn't open file" )
+#define BLARGG_ERR_FILE_WRITE BLARGG_ERR_TYPE( "couldn't modify file" )
+#define BLARGG_ERR_FILE_IO BLARGG_ERR_TYPE( "read/write error" )
+#define BLARGG_ERR_FILE_FULL BLARGG_ERR_TYPE( "disk full" )
+#define BLARGG_ERR_FILE_EOF BLARGG_ERR_TYPE( "truncated file" )
+
+#define BLARGG_ERR_FILE_TYPE BLARGG_ERR_TYPE( "wrong file type" )
+#define BLARGG_ERR_FILE_FEATURE BLARGG_ERR_TYPE( "unsupported file feature" )
+#define BLARGG_ERR_FILE_CORRUPT BLARGG_ERR_TYPE( "corrupt file" )
+
+BLARGG_NAMESPACE_END
+
+#endif
diff --git a/Frameworks/GME/gme/c140.c b/Frameworks/GME/gme/c140.c
new file mode 100644
index 000000000..9ebb520d9
--- /dev/null
+++ b/Frameworks/GME/gme/c140.c
@@ -0,0 +1,620 @@
+/*
+C140.c
+
+Simulator based on AMUSE sources.
+The C140 sound chip is used by Namco System 2 and System 21
+The 219 ASIC (which incorporates a modified C140) is used by Namco NA-1 and NA-2
+This chip controls 24 channels (C140) or 16 (219) of PCM.
+16 bytes are associated with each channel.
+Channels can be 8 bit signed PCM, or 12 bit signed PCM.
+
+Timer behavior is not yet handled.
+
+Unmapped registers:
+ 0x1f8:timer interval? (Nx0.1 ms)
+ 0x1fa:irq ack? timer restart?
+ 0x1fe:timer switch?(0:off 1:on)
+
+--------------
+
+ ASIC "219" notes
+
+ On the 219 ASIC used on NA-1 and NA-2, the high registers have the following
+ meaning instead:
+ 0x1f7: bank for voices 0-3
+ 0x1f1: bank for voices 4-7
+ 0x1f3: bank for voices 8-11
+ 0x1f5: bank for voices 12-15
+
+ Some games (bkrtmaq, xday2) write to 0x1fd for voices 12-15 instead. Probably the bank registers
+ mirror at 1f8, in which case 1ff is also 0-3, 1f9 is also 4-7, 1fb is also 8-11, and 1fd is also 12-15.
+
+ Each bank is 0x20000 (128k), and the voice addresses on the 219 are all multiplied by 2.
+ Additionally, the 219's base pitch is the same as the C352's (42667). But these changes
+ are IMO not sufficient to make this a separate file - all the other registers are
+ fully compatible.
+
+ Finally, the 219 only has 16 voices.
+*/
+/*
+ 2000.06.26 CAB fixed compressed pcm playback
+ 2002.07.20 R.Belmont added support for multiple banking types
+ 2006.01.08 R.Belmont added support for NA-1/2 "219" derivative
+*/
+
+
+//#include "emu.h"
+#include
+#include
+#include "c140.h"
+
+#define NULL ((void *)0)
+
+#define MAX_VOICE 24
+
+struct voice_registers
+{
+ UINT8 volume_right;
+ UINT8 volume_left;
+ UINT8 frequency_msb;
+ UINT8 frequency_lsb;
+ UINT8 bank;
+ UINT8 mode;
+ UINT8 start_msb;
+ UINT8 start_lsb;
+ UINT8 end_msb;
+ UINT8 end_lsb;
+ UINT8 loop_msb;
+ UINT8 loop_lsb;
+ UINT8 reserved[4];
+};
+
+typedef struct
+{
+ long ptoffset;
+ long pos;
+ long key;
+ //--work
+ long lastdt;
+ long prevdt;
+ long dltdt;
+ //--reg
+ long rvol;
+ long lvol;
+ long frequency;
+ long bank;
+ long mode;
+
+ long sample_start;
+ long sample_end;
+ long sample_loop;
+ UINT8 Muted;
+} VOICE;
+
+typedef struct _c140_state c140_state;
+struct _c140_state
+{
+ int sample_rate;
+ //sound_stream *stream;
+ int banking_type;
+ /* internal buffers */
+ INT16 *mixer_buffer_left;
+ INT16 *mixer_buffer_right;
+
+ int baserate;
+ UINT32 pRomSize;
+ void *pRom;
+ UINT8 REG[0x200];
+
+ INT16 pcmtbl[8]; //2000.06.26 CAB
+
+ VOICE voi[MAX_VOICE];
+};
+
+/*INLINE c140_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == C140);
+ return (c140_state *)downcast(device)->token();
+}*/
+
+
+static void init_voice( VOICE *v )
+{
+ v->key=0;
+ v->ptoffset=0;
+ v->rvol=0;
+ v->lvol=0;
+ v->frequency=0;
+ v->bank=0;
+ v->mode=0;
+ v->sample_start=0;
+ v->sample_end=0;
+ v->sample_loop=0;
+}
+//READ8_DEVICE_HANDLER( c140_r )
+UINT8 c140_r(void *chip, offs_t offset)
+{
+ //c140_state *info = get_safe_token(device);
+ c140_state *info = (c140_state *) chip;
+ offset&=0x1ff;
+ return info->REG[offset];
+}
+
+/*
+ find_sample: compute the actual address of a sample given it's
+ address and banking registers, as well as the board type.
+
+ I suspect in "real life" this works like the Sega MultiPCM where the banking
+ is done by a small PAL or GAL external to the sound chip, which can be switched
+ per-game or at least per-PCB revision as addressing range needs grow.
+ */
+static long find_sample(c140_state *info, long adrs, long bank, int voice)
+{
+ long newadr = 0;
+
+ static const INT16 asic219banks[4] = { 0x1f7, 0x1f1, 0x1f3, 0x1f5 };
+
+ adrs=(bank<<16)+adrs;
+
+ switch (info->banking_type)
+ {
+ case C140_TYPE_SYSTEM2:
+ // System 2 banking
+ newadr = ((adrs&0x200000)>>2)|(adrs&0x7ffff);
+ break;
+
+ case C140_TYPE_SYSTEM21_A:
+ // System 21 type A (simple) banking.
+ // similar to System 2's.
+ newadr = ((adrs&0x300000)>>1)+(adrs&0x7ffff);
+ break;
+
+ case C140_TYPE_SYSTEM21_B:
+ // System 21 type B (chip select) banking
+
+ // get base address of sample inside the bank
+ newadr = ((adrs&0x100000)>>2) + (adrs&0x3ffff);
+
+ // now add the starting bank offsets based on the 2
+ // chip select bits.
+ // 0x40000 picks individual 512k ROMs
+ if (adrs & 0x40000)
+ {
+ newadr += 0x80000;
+ }
+
+ // and 0x200000 which group of chips...
+ if (adrs & 0x200000)
+ {
+ newadr += 0x100000;
+ }
+ break;
+
+ case C140_TYPE_ASIC219:
+ // ASIC219's banking is fairly simple
+ newadr = ((info->REG[asic219banks[voice/4]]&0x3) * 0x20000) + adrs;
+ break;
+ }
+
+ return (newadr);
+}
+//WRITE8_DEVICE_HANDLER( c140_w )
+void c140_w(void *chip, offs_t offset, UINT8 data)
+{
+ //c140_state *info = get_safe_token(device);
+ c140_state *info = (c140_state *) chip;
+ //info->stream->update();
+
+ offset&=0x1ff;
+
+ // mirror the bank registers on the 219, fixes bkrtmaq (and probably xday2 based on notes in the HLE)
+ if ((offset >= 0x1f8) && (info->banking_type == C140_TYPE_ASIC219))
+ {
+ offset -= 8;
+ }
+
+ info->REG[offset]=data;
+ if( offset<0x180 )
+ {
+ VOICE *v = &info->voi[offset>>4];
+
+ if( (offset&0xf)==0x5 )
+ {
+ if( data&0x80 )
+ {
+ const struct voice_registers *vreg = (struct voice_registers *) &info->REG[offset&0x1f0];
+ v->key=1;
+ v->ptoffset=0;
+ v->pos=0;
+ v->lastdt=0;
+ v->prevdt=0;
+ v->dltdt=0;
+ v->bank = vreg->bank;
+ v->mode = data;
+
+ // on the 219 asic, addresses are in words
+ if (info->banking_type == C140_TYPE_ASIC219)
+ {
+ v->sample_loop = (vreg->loop_msb*256 + vreg->loop_lsb)*2;
+ v->sample_start = (vreg->start_msb*256 + vreg->start_lsb)*2;
+ v->sample_end = (vreg->end_msb*256 + vreg->end_lsb)*2;
+
+ #if 0
+ logerror("219: play v %d mode %02x start %x loop %x end %x\n",
+ offset>>4, v->mode,
+ find_sample(info, v->sample_start, v->bank, offset>>4),
+ find_sample(info, v->sample_loop, v->bank, offset>>4),
+ find_sample(info, v->sample_end, v->bank, offset>>4));
+ #endif
+ }
+ else
+ {
+ v->sample_loop = vreg->loop_msb*256 + vreg->loop_lsb;
+ v->sample_start = vreg->start_msb*256 + vreg->start_lsb;
+ v->sample_end = vreg->end_msb*256 + vreg->end_lsb;
+ }
+ }
+ else
+ {
+ v->key=0;
+ }
+ }
+ }
+}
+
+//void c140_set_base(device_t *device, void *base)
+void c140_set_base(void *chip, void *base)
+{
+ //c140_state *info = get_safe_token(device);
+ c140_state *info = (c140_state *) chip;
+ info->pRom = base;
+}
+
+/*INLINE int limit(INT32 in)
+{
+ if(in>0x7fff) return 0x7fff;
+ else if(in<-0x8000) return -0x8000;
+ return in;
+}*/
+
+//static STREAM_UPDATE( update_stereo )
+void c140_update(void *chip, stream_sample_t **outputs, int samples)
+{
+ //c140_state *info = (c140_state *)param;
+ c140_state *info = (c140_state *) chip;
+ int i,j;
+
+ INT32 rvol,lvol;
+ INT32 dt;
+ INT32 sdt;
+ INT32 st,ed,sz;
+
+ INT8 *pSampleData;
+ INT32 frequency,delta,offset,pos;
+ INT32 cnt, voicecnt;
+ INT32 lastdt,prevdt,dltdt;
+ float pbase=(float)info->baserate*2.0f / (float)info->sample_rate;
+
+ INT16 *lmix, *rmix;
+
+ if(samples>info->sample_rate) samples=info->sample_rate;
+
+ /* zap the contents of the mixer buffer */
+ memset(info->mixer_buffer_left, 0, samples * sizeof(INT16));
+ memset(info->mixer_buffer_right, 0, samples * sizeof(INT16));
+ if (info->pRom == NULL)
+ return;
+
+ /* get the number of voices to update */
+ voicecnt = (info->banking_type == C140_TYPE_ASIC219) ? 16 : 24;
+
+ //--- audio update
+ for( i=0;ivoi[i];
+ const struct voice_registers *vreg = (struct voice_registers *)&info->REG[i*16];
+
+ if( v->key && ! v->Muted)
+ {
+ frequency= vreg->frequency_msb*256 + vreg->frequency_lsb;
+
+ /* Abort voice if no frequency value set */
+ if(frequency==0) continue;
+
+ /* Delta = frequency * ((8MHz/374)*2 / sample rate) */
+ delta=(long)((float)frequency * pbase);
+
+ /* Calculate left/right channel volumes */
+ lvol=(vreg->volume_left*32)/MAX_VOICE; //32ch -> 24ch
+ rvol=(vreg->volume_right*32)/MAX_VOICE;
+
+ /* Set mixer outputs base pointers */
+ lmix = info->mixer_buffer_left;
+ rmix = info->mixer_buffer_right;
+
+ /* Retrieve sample start/end and calculate size */
+ st=v->sample_start;
+ ed=v->sample_end;
+ sz=ed-st;
+
+ /* Retrieve base pointer to the sample data */
+ //pSampleData=(signed char*)((FPTR)info->pRom + find_sample(info, st, v->bank, i));
+ pSampleData = (INT8*)info->pRom + find_sample(info, st, v->bank, i);
+
+ /* Fetch back previous data pointers */
+ offset=v->ptoffset;
+ pos=v->pos;
+ lastdt=v->lastdt;
+ prevdt=v->prevdt;
+ dltdt=v->dltdt;
+
+ /* Switch on data type - compressed PCM is only for C140 */
+ if ((v->mode&8) && (info->banking_type != C140_TYPE_ASIC219))
+ {
+ //compressed PCM (maybe correct...)
+ /* Loop for enough to fill sample buffer as requested */
+ for(j=0;j>16)&0x7fff;
+ offset &= 0xffff;
+ pos+=cnt;
+ //for(;cnt>0;cnt--)
+ {
+ /* Check for the end of the sample */
+ if(pos >= sz)
+ {
+ /* Check if its a looping sample, either stop or loop */
+ if(v->mode&0x10)
+ {
+ pos = (v->sample_loop - st);
+ }
+ else
+ {
+ v->key=0;
+ break;
+ }
+ }
+
+ /* Read the chosen sample byte */
+ dt=pSampleData[pos];
+
+ /* decompress to 13bit range */ //2000.06.26 CAB
+ sdt=dt>>3; //signed
+ if(sdt<0) sdt = (sdt<<(dt&7)) - info->pcmtbl[dt&7];
+ else sdt = (sdt<<(dt&7)) + info->pcmtbl[dt&7];
+
+ prevdt=lastdt;
+ lastdt=sdt;
+ dltdt=(lastdt - prevdt);
+ }
+
+ /* Caclulate the sample value */
+ dt=((dltdt*offset)>>16)+prevdt;
+
+ /* Write the data to the sample buffers */
+ *lmix++ +=(dt*lvol)>>(5+5);
+ *rmix++ +=(dt*rvol)>>(5+5);
+ }
+ }
+ else
+ {
+ /* linear 8bit signed PCM */
+ for(j=0;j>16)&0x7fff;
+ offset &= 0xffff;
+ pos += cnt;
+ /* Check for the end of the sample */
+ if(pos >= sz)
+ {
+ /* Check if its a looping sample, either stop or loop */
+ if( v->mode&0x10 )
+ {
+ pos = (v->sample_loop - st);
+ }
+ else
+ {
+ v->key=0;
+ break;
+ }
+ }
+
+ if( cnt )
+ {
+ prevdt=lastdt;
+
+ if (info->banking_type == C140_TYPE_ASIC219)
+ {
+ //lastdt = pSampleData[BYTE_XOR_BE(pos)];
+ lastdt = pSampleData[pos ^ 0x01];
+
+ // Sign + magnitude format
+ if ((v->mode & 0x01) && (lastdt & 0x80))
+ lastdt = -(lastdt & 0x7f);
+
+ // Sign flip
+ if (v->mode & 0x40)
+ lastdt = -lastdt;
+ }
+ else
+ {
+ lastdt=pSampleData[pos];
+ }
+
+ dltdt = (lastdt - prevdt);
+ }
+
+ /* Caclulate the sample value */
+ dt=((dltdt*offset)>>16)+prevdt;
+
+ /* Write the data to the sample buffers */
+ *lmix++ +=(dt*lvol)>>5;
+ *rmix++ +=(dt*rvol)>>5;
+ }
+ }
+
+ /* Save positional data for next callback */
+ v->ptoffset=offset;
+ v->pos=pos;
+ v->lastdt=lastdt;
+ v->prevdt=prevdt;
+ v->dltdt=dltdt;
+ }
+ }
+
+ /* render to MAME's stream buffer */
+ lmix = info->mixer_buffer_left;
+ rmix = info->mixer_buffer_right;
+ {
+ stream_sample_t *dest1 = outputs[0];
+ stream_sample_t *dest2 = outputs[1];
+ for (i = 0; i < samples; i++)
+ {
+ //*dest1++ = limit(8*(*lmix++));
+ //*dest2++ = limit(8*(*rmix++));
+ *dest1++ = 8 * (*lmix ++);
+ *dest2++ = 8 * (*rmix ++);
+ }
+ }
+}
+
+//static DEVICE_START( c140 )
+void * device_start_c140(int sample_rate, int clock, int banking_type)
+{
+ //const c140_interface *intf = (const c140_interface *)device->static_config();
+ //c140_state *info = get_safe_token(device);
+ c140_state *info;
+ int i;
+
+ info = (c140_state *) malloc(sizeof(c140_state));
+ if (!info) return info;
+
+ //info->sample_rate=info->baserate=device->clock();
+ info->sample_rate = sample_rate;
+ info->baserate = clock;
+
+ //info->banking_type = intf->banking_type;
+ info->banking_type = banking_type;
+
+ //info->stream = device->machine().sound().stream_alloc(*device,0,2,info->sample_rate,info,update_stereo);
+
+ //info->pRom=*device->region();
+ info->pRomSize = 0x00;
+ info->pRom = NULL;
+
+ /* make decompress pcm table */ //2000.06.26 CAB
+ {
+ INT32 segbase=0;
+ for(i=0;i<8;i++)
+ {
+ info->pcmtbl[i]=segbase; //segment base value
+ segbase += 16<REG,0,sizeof(info->REG));
+ {
+ int i;
+ for(i=0;ivoi[i] );
+ }*/
+
+ /* allocate a pair of buffers to mix into - 1 second's worth should be more than enough */
+ //info->mixer_buffer_left = auto_alloc_array(device->machine(), INT16, 2 * info->sample_rate);
+ info->mixer_buffer_left = (INT16*)malloc(sizeof(INT16) * 2 * info->sample_rate);
+ info->mixer_buffer_right = info->mixer_buffer_left + info->sample_rate;
+
+ for (i = 0; i < MAX_VOICE; i ++)
+ info->voi[i].Muted = 0x00;
+
+ return info;
+}
+
+void device_stop_c140(void *chip)
+{
+ c140_state *info = (c140_state *) chip;
+
+ free(info->pRom); info->pRom = NULL;
+ free(info->mixer_buffer_left);
+ free(info);
+}
+
+void device_reset_c140(void *chip)
+{
+ c140_state *info = (c140_state *) chip;
+ int i;
+
+ memset(info->REG, 0, sizeof(info->REG));
+
+ for(i = 0; i < MAX_VOICE; i ++)
+ init_voice( &info->voi[i] );
+
+ return;
+}
+
+void c140_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
+ const UINT8* ROMData)
+{
+ c140_state *info = (c140_state *) chip;
+
+ if (info->pRomSize != ROMSize)
+ {
+ info->pRom = (UINT8*)realloc(info->pRom, ROMSize);
+ info->pRomSize = ROMSize;
+ memset(info->pRom, 0xFF, ROMSize);
+ }
+ if (DataStart > ROMSize)
+ return;
+ if (DataStart + DataLength > ROMSize)
+ DataLength = ROMSize - DataStart;
+
+ memcpy((INT8*)info->pRom + DataStart, ROMData, DataLength);
+
+ return;
+}
+
+
+void c140_set_mute_mask(void *chip, UINT32 MuteMask)
+{
+ c140_state *info = (c140_state *) chip;
+ UINT8 CurChn;
+
+ for (CurChn = 0; CurChn < MAX_VOICE; CurChn ++)
+ info->voi[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
+
+ return;
+}
+
+
+
+
+/**************************************************************************
+ * Generic get_info
+ **************************************************************************/
+
+/*DEVICE_GET_INFO( c140 )
+{
+ switch (state)
+ {
+ // --- the following bits of info are returned as 64-bit signed integers --- //
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(c140_state); break;
+
+ // --- the following bits of info are returned as pointers to data or functions --- //
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( c140 ); break;
+ case DEVINFO_FCT_STOP: // nothing // break;
+ case DEVINFO_FCT_RESET: // nothing // break;
+
+ // --- the following bits of info are returned as NULL-terminated strings --- //
+ case DEVINFO_STR_NAME: strcpy(info->s, "C140"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Namco PCM"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
+ }
+}
+
+
+DEFINE_LEGACY_SOUND_DEVICE(C140, c140);*/
diff --git a/Frameworks/GME/gme/c140.h b/Frameworks/GME/gme/c140.h
new file mode 100644
index 000000000..9345a38ed
--- /dev/null
+++ b/Frameworks/GME/gme/c140.h
@@ -0,0 +1,58 @@
+/* C140.h */
+
+#pragma once
+
+#ifndef __OSDCOMM_H__
+#define __OSDCOMM_H__
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+
+typedef INT32 stream_sample_t;
+typedef UINT32 offs_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void c140_update(void * chip, stream_sample_t **outputs, int samples);
+void * device_start_c140(int sample_rate, int clock, int banking_type);
+void device_stop_c140(void * chip);
+void device_reset_c140(void * chip);
+
+//READ8_DEVICE_HANDLER( c140_r );
+//WRITE8_DEVICE_HANDLER( c140_w );
+UINT8 c140_r(void * chip, offs_t offset);
+void c140_w(void * chip, offs_t offset, UINT8 data);
+
+//void c140_set_base(device_t *device, void *base);
+void c140_set_base(void *chip, void *base);
+
+enum
+{
+ C140_TYPE_SYSTEM2,
+ C140_TYPE_SYSTEM21_A,
+ C140_TYPE_SYSTEM21_B,
+ C140_TYPE_ASIC219
+};
+
+/*typedef struct _c140_interface c140_interface;
+struct _c140_interface {
+ int banking_type;
+};*/
+
+
+void c140_write_rom(void * chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
+ const UINT8* ROMData);
+
+void c140_set_mute_mask(void * chip, UINT32 MuteMask);
+
+#ifdef __cplusplus
+}
+#endif
+
+//DECLARE_LEGACY_SOUND_DEVICE(C140, c140);
diff --git a/Frameworks/GME/gme/dac_control.c b/Frameworks/GME/gme/dac_control.c
new file mode 100644
index 000000000..867e4d858
--- /dev/null
+++ b/Frameworks/GME/gme/dac_control.c
@@ -0,0 +1,367 @@
+ /************************
+ * DAC Stream Control *
+ ***********************/
+// (Custom Driver to handle PCM Streams of YM2612 DAC and PWM.)
+//
+// Written on 3 February 2011 by Valley Bell
+// Last Update: 25 April 2011
+//
+// Only for usage in non-commercial, VGM file related software.
+
+/* How it basically works:
+
+1. send command X with data Y at frequency F to chip C
+2. do that until you receive a STOP command, or until you sent N commands
+
+*/
+
+#include "dac_control.h"
+
+#include
+
+#define INLINE static __inline
+
+void chip_reg_write(void * context, UINT32 Sample, UINT8 ChipType, UINT8 ChipID, UINT8 Port, UINT8 Offset, UINT8 Data);
+
+typedef struct _dac_control
+{
+ UINT32 SampleRate;
+
+ // Commands sent to dest-chip
+ UINT8 DstChipType;
+ UINT8 DstChipID;
+ UINT16 DstCommand;
+ UINT8 CmdSize;
+
+ UINT32 Frequency; // Frequency (Hz) at which the commands are sent
+ UINT32 DataLen; // to protect from reading beyond End Of Data
+ const UINT8* Data;
+ UINT32 DataStart; // Position where to start
+ UINT8 StepSize; // usually 1, set to 2 for L/R interleaved data
+ UINT8 StepBase; // usually 0, set to 0/1 for L/R interleaved data
+ UINT32 CmdsToSend;
+
+ // Running Bits: 0 (01) - is playing
+ // 2 (04) - loop sample (simple loop from start to end)
+ // 4 (10) - already sent this command
+ // 7 (80) - disabled
+ UINT8 Running;
+ UINT32 Step;
+ UINT32 Pos;
+ UINT32 RemainCmds;
+ UINT8 DataStep; // always StepSize * CmdSize
+
+ void * context; // context data sent to chip_reg_write
+} dac_control;
+
+#ifndef NULL
+#define NULL (void*)0
+#endif
+
+static void daccontrol_SendCommand(dac_control *chip, UINT32 Sample)
+{
+ UINT8 Port;
+ UINT8 Command;
+ UINT8 Data;
+ const UINT8* ChipData;
+
+ if (chip->Running & 0x10) // command already sent
+ return;
+ if (chip->DataStart + chip->Pos >= chip->DataLen)
+ return;
+
+ ChipData = chip->Data + (chip->DataStart + chip->Pos);
+ switch(chip->DstChipType)
+ {
+ // Support for the important chips
+ case 0x02: // YM2612
+ Port = (chip->DstCommand & 0xFF00) >> 8;
+ Command = (chip->DstCommand & 0x00FF) >> 0;
+ Data = ChipData[0x00];
+ chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, Port, Command, Data);
+ break;
+ case 0x11: // PWM
+ Port = (chip->DstCommand & 0x000F) >> 0;
+ Command = ChipData[0x01] & 0x0F;
+ Data = ChipData[0x00];
+ chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, Port, Command, Data);
+ break;
+ // (Generic) Support for other chips (just for completeness)
+ case 0x00: // SN76496
+ Command = (chip->DstCommand & 0x00F0) >> 0;
+ Data = ChipData[0x00] & 0x0F;
+ if (Command & 0x10)
+ {
+ // Volume Change (4-Bit value)
+ chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, 0x00, 0x00, Command | Data);
+ }
+ else
+ {
+ // Frequency Write (10-Bit value)
+ Port = ((ChipData[0x01] & 0x03) << 4) | ((ChipData[0x00] & 0xF0) >> 4);
+ chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, 0x00, 0x00, Command | Data);
+ chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, 0x00, 0x00, Port);
+ }
+ break;
+ case 0x01: // YM2413
+ case 0x03: // YM2151
+ case 0x09: // YM3812
+ case 0x0A: // YM3526
+ case 0x0B: // Y8950
+ case 0x0F: // YMZ280B
+ case 0x12: // AY8910
+ Command = (chip->DstCommand & 0x00FF) >> 0;
+ Data = ChipData[0x00];
+ chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, 0x00, Command, Data);
+ break;
+ case 0x06: // YM2203
+ case 0x07: // YM2608
+ case 0x08: // YM2610/B
+ case 0x0C: // YMF262
+ case 0x0D: // YMF278B
+ case 0x0E: // YMF271
+ Port = (chip->DstCommand & 0xFF00) >> 8;
+ Command = (chip->DstCommand & 0x00FF) >> 0;
+ Data = ChipData[0x00];
+ chip_reg_write(chip->context, Sample, chip->DstChipType, chip->DstChipID, Port, Command, Data);
+ break;
+ }
+ chip->Running |= 0x10;
+
+ return;
+}
+
+INLINE UINT32 muldiv64round(UINT32 Multiplicand, UINT32 Multiplier, UINT32 Divisor)
+{
+ // Yes, I'm correctly rounding the values.
+ return (UINT32)(((UINT64)Multiplicand * Multiplier + Multiplier / 2) / Divisor);
+}
+
+void daccontrol_update(void *_chip, UINT32 base_clock, UINT32 samples)
+{
+ dac_control *chip = (dac_control *) _chip;
+ UINT32 NewPos;
+ UINT32 Sample;
+
+ if (chip->Running & 0x80) // disabled
+ return;
+ if (! (chip->Running & 0x01)) // stopped
+ return;
+
+ /*if (samples > 0x20)
+ {
+ // very effective Speed Hack for fast seeking
+ NewPos = chip->Step + (samples - 0x10);
+ NewPos = muldiv64round(NewPos * chip->DataStep, chip->Frequency, chip->SampleRate);
+ while(chip->RemainCmds && chip->Pos < NewPos)
+ {
+ chip->Pos += chip->DataStep;
+ chip->RemainCmds --;
+ }
+ }*/
+
+ Sample = 0;
+ chip->Step += samples;
+ // Formula: Step * Freq / SampleRate
+ NewPos = muldiv64round(chip->Step * chip->DataStep, chip->Frequency, chip->SampleRate);
+
+ while(chip->RemainCmds && chip->Pos < NewPos)
+ {
+ daccontrol_SendCommand(chip, base_clock + muldiv64round(Sample, chip->SampleRate, chip->Frequency));
+ Sample++;
+ chip->Pos += chip->DataStep;
+ chip->Running &= ~0x10;
+ chip->RemainCmds --;
+ }
+
+ if (! chip->RemainCmds && (chip->Running & 0x04))
+ {
+ // loop back to start
+ chip->RemainCmds = chip->CmdsToSend;
+ chip->Step = 0x00;
+ chip->Pos = 0x00;
+ }
+
+ if (! chip->RemainCmds)
+ chip->Running &= ~0x01; // stop
+
+ return;
+}
+
+void * device_start_daccontrol(UINT32 samplerate, void * context)
+{
+ dac_control *chip;
+
+ chip = (dac_control *) calloc(1, sizeof(dac_control));
+
+ chip->SampleRate = samplerate;
+ chip->context = context;
+
+ chip->DstChipType = 0xFF;
+ chip->DstChipID = 0x00;
+ chip->DstCommand = 0x0000;
+
+ chip->Running = 0xFF; // disable all actions (except setup_chip)
+
+ return chip;
+}
+
+void device_stop_daccontrol(void *_chip)
+{
+ dac_control *chip = (dac_control *) _chip;
+
+ free( chip );
+}
+
+void device_reset_daccontrol(void *_chip)
+{
+ dac_control *chip = (dac_control *) _chip;
+
+ chip->DstChipType = 0x00;
+ chip->DstChipID = 0x00;
+ chip->DstCommand = 0x00;
+ chip->CmdSize = 0x00;
+
+ chip->Frequency = 0;
+ chip->DataLen = 0x00;
+ chip->Data = NULL;
+ chip->DataStart = 0x00;
+ chip->StepSize = 0x00;
+ chip->StepBase = 0x00;
+
+ chip->Running = 0x00;
+ chip->Step = 0x00;
+ chip->Pos = 0x00;
+ chip->RemainCmds = 0x00;
+ chip->DataStep = 0x00;
+
+ return;
+}
+
+void daccontrol_setup_chip(void *_chip, UINT8 ChType, UINT8 ChNum, UINT16 Command)
+{
+ dac_control *chip = (dac_control *) _chip;
+
+ chip->DstChipType = ChType; // TypeID (e.g. 0x02 for YM2612)
+ chip->DstChipID = ChNum; // chip number (to send commands to 1st or 2nd chip)
+ chip->DstCommand = Command; // Port and Command (would be 0x02A for YM2612)
+
+ switch(chip->DstChipType)
+ {
+ case 0x00: // SN76496
+ if (chip->DstCommand & 0x0010)
+ chip->CmdSize = 0x01; // Volume Write
+ else
+ chip->CmdSize = 0x02; // Frequency Write
+ break;
+ case 0x02: // YM2612
+ chip->CmdSize = 0x01;
+ break;
+ case 0x11: // PWM
+ chip->CmdSize = 0x02;
+ break;
+ default:
+ chip->CmdSize = 0x01;
+ break;
+ }
+ chip->DataStep = chip->CmdSize * chip->StepSize;
+
+ return;
+}
+
+void daccontrol_set_data(void *_chip, const UINT8* Data, UINT32 DataLen, UINT8 StepSize, UINT8 StepBase)
+{
+ dac_control *chip = (dac_control *) _chip;
+
+ if (chip->Running & 0x80)
+ return;
+
+ if (DataLen && Data != NULL)
+ {
+ chip->DataLen = DataLen;
+ chip->Data = Data;
+ }
+ else
+ {
+ chip->DataLen = 0x00;
+ chip->Data = NULL;
+ }
+ chip->StepSize = StepSize ? StepSize : 1;
+ chip->StepBase = StepBase;
+ chip->DataStep = chip->CmdSize * chip->StepSize;
+
+ return;
+}
+
+void daccontrol_set_frequency(void *_chip, UINT32 Frequency)
+{
+ dac_control *chip = (dac_control *) _chip;
+
+ if (chip->Running & 0x80)
+ return;
+
+ chip->Frequency = Frequency;
+
+ return;
+}
+
+void daccontrol_start(void *_chip, UINT32 DataPos, UINT8 LenMode, UINT32 Length)
+{
+ dac_control *chip = (dac_control *) _chip;
+ UINT16 CmdStepBase;
+
+ if (chip->Running & 0x80)
+ return;
+
+ CmdStepBase = chip->CmdSize * chip->StepBase;
+ if (DataPos != 0xFFFFFFFF) // skip setting DataStart, if Pos == -1
+ {
+ chip->DataStart = DataPos + CmdStepBase;
+ if (chip->DataStart > chip->DataLen) // catch bad value and force silence
+ chip->DataStart = chip->DataLen;
+ }
+
+ switch(LenMode & 0x0F)
+ {
+ case DCTRL_LMODE_IGNORE: // Length is already set - ignore
+ break;
+ case DCTRL_LMODE_CMDS: // Length = number of commands
+ chip->CmdsToSend = Length;
+ break;
+ case DCTRL_LMODE_MSEC: // Length = time in msec
+ chip->CmdsToSend = 1000 * Length / chip->Frequency;
+ break;
+ case DCTRL_LMODE_TOEND: // play unti stop-command is received (or data-end is reached)
+ chip->CmdsToSend = (chip->DataLen - (chip->DataStart - CmdStepBase)) / chip->DataStep;
+ break;
+ case DCTRL_LMODE_BYTES: // raw byte count
+ chip->CmdsToSend = Length / chip->DataStep;
+ break;
+ default:
+ chip->CmdsToSend = 0x00;
+ break;
+ }
+ chip->RemainCmds = chip->CmdsToSend;
+ chip->Step = 0x00;
+ chip->Pos = 0x00;
+
+ chip->Running &= ~0x04;
+ chip->Running |= (LenMode & 0x80) ? 0x04 : 0x00; // set loop mode
+
+ chip->Running |= 0x01; // start
+ chip->Running &= ~0x10; // command isn't yet sent
+
+ return;
+}
+
+void daccontrol_stop(void *_chip)
+{
+ dac_control *chip = (dac_control *) _chip;
+
+ if (chip->Running & 0x80)
+ return;
+
+ chip->Running &= ~0x01; // stop
+
+ return;
+}
diff --git a/Frameworks/GME/gme/dac_control.h b/Frameworks/GME/gme/dac_control.h
new file mode 100644
index 000000000..4cf75656a
--- /dev/null
+++ b/Frameworks/GME/gme/dac_control.h
@@ -0,0 +1,44 @@
+#ifndef _DAC_CONTROL_H_
+#define _DAC_CONTROL_H_
+
+#ifndef __OSDCOMM_H__
+#define __OSDCOMM_H__
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef unsigned long long UINT64; /* unsigned 64bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+typedef signed long long INT64; /* signed 64bit */
+
+typedef INT32 stream_sample_t;
+typedef UINT32 offs_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void * device_start_daccontrol(UINT32 samplerate, void *context);
+void device_stop_daccontrol(void * chip);
+
+void daccontrol_update(void * chip, UINT32 base_clock, UINT32 samples);
+void device_reset_daccontrol(void * chip);
+void daccontrol_setup_chip(void * chip, UINT8 ChType, UINT8 ChNum, UINT16 Command);
+void daccontrol_set_data(void *chip, const UINT8* Data, UINT32 DataLen, UINT8 StepSize, UINT8 StepBase);
+void daccontrol_set_frequency(void *chip, UINT32 Frequency);
+void daccontrol_start(void *chip, UINT32 DataPos, UINT8 LenMode, UINT32 Length);
+void daccontrol_stop(void *chip);
+
+#define DCTRL_LMODE_IGNORE 0x00
+#define DCTRL_LMODE_CMDS 0x01
+#define DCTRL_LMODE_MSEC 0x02
+#define DCTRL_LMODE_TOEND 0x03
+#define DCTRL_LMODE_BYTES 0x0F
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Frameworks/GME/gme/dbopl.cpp b/Frameworks/GME/gme/dbopl.cpp
new file mode 100644
index 000000000..3dbe669b2
--- /dev/null
+++ b/Frameworks/GME/gme/dbopl.cpp
@@ -0,0 +1,1526 @@
+/*
+ * Copyright (C) 2002-2009 The DOSBox Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ DOSBox implementation of a combined Yamaha YMF262 and Yamaha YM3812 emulator.
+ Enabling the opl3 bit will switch the emulator to stereo opl3 output instead of regular mono opl2
+ Except for the table generation it's all integer math
+ Can choose different types of generators, using muls and bigger tables, try different ones for slower platforms
+ The generation was based on the MAME implementation but tried to have it use less memory and be faster in general
+ MAME uses much bigger envelope tables and this will be the biggest cause of it sounding different at times
+
+ //TODO Don't delay first operator 1 sample in opl3 mode
+ //TODO Maybe not use class method pointers but a regular function pointers with operator as first parameter
+ //TODO Fix panning for the Percussion channels, would any opl3 player use it and actually really change it though?
+ //TODO Check if having the same accuracy in all frequency multipliers sounds better or not
+
+ //DUNNO Keyon in 4op, switch to 2op without keyoff.
+*/
+
+/* $Id$ */
+
+
+#include
+#include
+#include
+//#include "dosbox.h"
+#include "dbopl.h"
+
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+namespace DBOPL {
+
+#define OPLRATE ((double)(14318180.0 / 288.0))
+#define TREMOLO_TABLE 52
+
+//Try to use most precision for frequencies
+//Else try to keep different waves in synch
+//#define WAVE_PRECISION 1
+#ifndef WAVE_PRECISION
+//Wave bits available in the top of the 32bit range
+//Original adlib uses 10.10, we use 10.22
+#define WAVE_BITS 10
+#else
+//Need some extra bits at the top to have room for octaves and frequency multiplier
+//We support to 8 times lower rate
+//128 * 15 * 8 = 15350, 2^13.9, so need 14 bits
+#define WAVE_BITS 14
+#endif
+#define WAVE_SH ( 32 - WAVE_BITS )
+#define WAVE_MASK ( ( 1 << WAVE_SH ) - 1 )
+
+//Use the same accuracy as the waves
+#define LFO_SH ( WAVE_SH - 10 )
+//LFO is controlled by our tremolo 256 sample limit
+#define LFO_MAX ( 256 << ( LFO_SH ) )
+
+
+//Maximum amount of attenuation bits
+//Envelope goes to 511, 9 bits
+#if (DBOPL_WAVE == WAVE_TABLEMUL )
+//Uses the value directly
+#define ENV_BITS ( 9 )
+#else
+//Add 3 bits here for more accuracy and would have to be shifted up either way
+#define ENV_BITS ( 9 )
+#endif
+//Limits of the envelope with those bits and when the envelope goes silent
+#define ENV_MIN 0
+#define ENV_EXTRA ( ENV_BITS - 9 )
+#define ENV_MAX ( 511 << ENV_EXTRA )
+#define ENV_LIMIT ( ( 12 * 256) >> ( 3 - ENV_EXTRA ) )
+#define ENV_SILENT( _X_ ) ( (_X_) >= ENV_LIMIT )
+
+//Attack/decay/release rate counter shift
+#define RATE_SH 24
+#define RATE_MASK ( ( 1 << RATE_SH ) - 1 )
+//Has to fit within 16bit lookuptable
+#define MUL_SH 16
+
+//Check some ranges
+#if ENV_EXTRA > 3
+#error Too many envelope bits
+#endif
+
+
+//How much to substract from the base value for the final attenuation
+static const Bit8u KslCreateTable[16] = {
+ //0 will always be be lower than 7 * 8
+ 64, 32, 24, 19,
+ 16, 12, 11, 10,
+ 8, 6, 5, 4,
+ 3, 2, 1, 0,
+};
+
+#define M(_X_) ((Bit8u)( (_X_) * 2))
+static const Bit8u FreqCreateTable[16] = {
+ M(0.5), M(1 ), M(2 ), M(3 ), M(4 ), M(5 ), M(6 ), M(7 ),
+ M(8 ), M(9 ), M(10), M(10), M(12), M(12), M(15), M(15)
+};
+#undef M
+
+//We're not including the highest attack rate, that gets a special value
+static const Bit8u AttackSamplesTable[13] = {
+ 69, 55, 46, 40,
+ 35, 29, 23, 20,
+ 19, 15, 11, 10,
+ 9
+};
+//On a real opl these values take 8 samples to reach and are based upon larger tables
+static const Bit8u EnvelopeIncreaseTable[13] = {
+ 4, 5, 6, 7,
+ 8, 10, 12, 14,
+ 16, 20, 24, 28,
+ 32,
+};
+
+#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG )
+static Bit16u ExpTable[ 256 ];
+#endif
+
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+//PI table used by WAVEHANDLER
+static Bit16u SinTable[ 512 ];
+#endif
+
+#if ( DBOPL_WAVE > WAVE_HANDLER )
+//Layout of the waveform table in 512 entry intervals
+//With overlapping waves we reduce the table to half it's size
+
+// | |//\\|____|WAV7|//__|/\ |____|/\/\|
+// |\\//| | |WAV7| | \/| | |
+// |06 |0126|17 |7 |3 |4 |4 5 |5 |
+
+//6 is just 0 shifted and masked
+
+static Bit16s WaveTable[ 8 * 512 ];
+//Distance into WaveTable the wave starts
+static const Bit16u WaveBaseTable[8] = {
+ 0x000, 0x200, 0x200, 0x800,
+ 0xa00, 0xc00, 0x100, 0x400,
+
+};
+//Mask the counter with this
+static const Bit16u WaveMaskTable[8] = {
+ 1023, 1023, 511, 511,
+ 1023, 1023, 512, 1023,
+};
+
+//Where to start the counter on at keyon
+static const Bit16u WaveStartTable[8] = {
+ 512, 0, 0, 0,
+ 0, 512, 512, 256,
+};
+#endif
+
+#if ( DBOPL_WAVE == WAVE_TABLEMUL )
+static Bit16u MulTable[ 384 ];
+#endif
+
+static Bit8u KslTable[ 8 * 16 ];
+static Bit8u TremoloTable[ TREMOLO_TABLE ];
+//Start of a channel behind the chip struct start
+static Bit16u ChanOffsetTable[32];
+//Start of an operator behind the chip struct start
+static Bit16u OpOffsetTable[64];
+
+//The lower bits are the shift of the operator vibrato value
+//The highest bit is right shifted to generate -1 or 0 for negation
+//So taking the highest input value of 7 this gives 3, 7, 3, 0, -3, -7, -3, 0
+static const Bit8s VibratoTable[ 8 ] = {
+ 1 - 0x00, 0 - 0x00, 1 - 0x00, 30 - 0x00,
+ 1 - 0x80, 0 - 0x80, 1 - 0x80, 30 - 0x80
+};
+
+//Shift strength for the ksl value determined by ksl strength
+static const Bit8u KslShiftTable[4] = {
+ 31,1,2,0
+};
+
+//Generate a table index and table shift value using input value from a selected rate
+static void EnvelopeSelect( Bit8u val, Bit8u& index, Bit8u& shift ) {
+ if ( val < 13 * 4 ) { //Rate 0 - 12
+ shift = 12 - ( val >> 2 );
+ index = val & 3;
+ } else if ( val < 15 * 4 ) { //rate 13 - 14
+ shift = 0;
+ index = val - 12 * 4;
+ } else { //rate 15 and up
+ shift = 0;
+ index = 12;
+ }
+}
+
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+/*
+ Generate the different waveforms out of the sine/exponetial table using handlers
+*/
+static inline Bits MakeVolume( Bitu wave, Bitu volume ) {
+ Bitu total = wave + volume;
+ Bitu index = total & 0xff;
+ Bitu sig = ExpTable[ index ];
+ Bitu exp = total >> 8;
+#if 0
+ //Check if we overflow the 31 shift limit
+ if ( exp >= 32 ) {
+ LOG_MSG( "WTF %d %d", total, exp );
+ }
+#endif
+ return (sig >> exp);
+};
+
+static Bits DB_FASTCALL WaveForm0( Bitu i, Bitu volume ) {
+ Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
+ Bitu wave = SinTable[i & 511];
+ return (MakeVolume( wave, volume ) ^ neg) - neg;
+}
+static Bits DB_FASTCALL WaveForm1( Bitu i, Bitu volume ) {
+ Bit32u wave = SinTable[i & 511];
+ wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm2( Bitu i, Bitu volume ) {
+ Bitu wave = SinTable[i & 511];
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm3( Bitu i, Bitu volume ) {
+ Bitu wave = SinTable[i & 255];
+ wave |= ( ( (i ^ 256 ) & 256) - 1) >> ( 32 - 12 );
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm4( Bitu i, Bitu volume ) {
+ //Twice as fast
+ i <<= 1;
+ Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
+ Bitu wave = SinTable[i & 511];
+ wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
+ return (MakeVolume( wave, volume ) ^ neg) - neg;
+}
+static Bits DB_FASTCALL WaveForm5( Bitu i, Bitu volume ) {
+ //Twice as fast
+ i <<= 1;
+ Bitu wave = SinTable[i & 511];
+ wave |= ( ( (i ^ 512 ) & 512) - 1) >> ( 32 - 12 );
+ return MakeVolume( wave, volume );
+}
+static Bits DB_FASTCALL WaveForm6( Bitu i, Bitu volume ) {
+ Bits neg = 0 - (( i >> 9) & 1);//Create ~0 or 0
+ return (MakeVolume( 0, volume ) ^ neg) - neg;
+}
+static Bits DB_FASTCALL WaveForm7( Bitu i, Bitu volume ) {
+ //Negative is reversed here
+ Bits neg = (( i >> 9) & 1) - 1;
+ Bitu wave = (i << 3);
+ //When negative the volume also runs backwards
+ wave = ((wave ^ neg) - neg) & 4095;
+ return (MakeVolume( wave, volume ) ^ neg) - neg;
+}
+
+static const WaveHandler WaveHandlerTable[8] = {
+ WaveForm0, WaveForm1, WaveForm2, WaveForm3,
+ WaveForm4, WaveForm5, WaveForm6, WaveForm7
+};
+
+#endif
+
+/*
+ Operator
+*/
+
+//We zero out when rate == 0
+inline void Operator::UpdateAttack( const Chip* chip ) {
+ Bit8u rate = reg60 >> 4;
+ if ( rate ) {
+ Bit8u val = (rate << 2) + ksr;
+ attackAdd = chip->attackRates[ val ];
+ rateZero &= ~(1 << ATTACK);
+ } else {
+ attackAdd = 0;
+ rateZero |= (1 << ATTACK);
+ }
+}
+inline void Operator::UpdateDecay( const Chip* chip ) {
+ Bit8u rate = reg60 & 0xf;
+ if ( rate ) {
+ Bit8u val = (rate << 2) + ksr;
+ decayAdd = chip->linearRates[ val ];
+ rateZero &= ~(1 << DECAY);
+ } else {
+ decayAdd = 0;
+ rateZero |= (1 << DECAY);
+ }
+}
+inline void Operator::UpdateRelease( const Chip* chip ) {
+ Bit8u rate = reg80 & 0xf;
+ if ( rate ) {
+ Bit8u val = (rate << 2) + ksr;
+ releaseAdd = chip->linearRates[ val ];
+ rateZero &= ~(1 << RELEASE);
+ if ( !(reg20 & MASK_SUSTAIN ) ) {
+ rateZero &= ~( 1 << SUSTAIN );
+ }
+ } else {
+ rateZero |= (1 << RELEASE);
+ releaseAdd = 0;
+ if ( !(reg20 & MASK_SUSTAIN ) ) {
+ rateZero |= ( 1 << SUSTAIN );
+ }
+ }
+}
+
+inline void Operator::UpdateAttenuation( ) {
+ Bit8u kslBase = (Bit8u)((chanData >> SHIFT_KSLBASE) & 0xff);
+ Bit32u tl = reg40 & 0x3f;
+ Bit8u kslShift = KslShiftTable[ reg40 >> 6 ];
+ //Make sure the attenuation goes to the right bits
+ totalLevel = tl << ( ENV_BITS - 7 ); //Total level goes 2 bits below max
+ totalLevel += ( kslBase << ENV_EXTRA ) >> kslShift;
+}
+
+void Operator::UpdateFrequency( ) {
+ Bit32u freq = chanData & (( 1 << 10 ) - 1);
+ Bit32u block = (chanData >> 10) & 0xff;
+
+#ifdef WAVE_PRECISION
+ block = 7 - block;
+ waveAdd = ( freq * freqMul ) >> block;
+#else
+ waveAdd = (freq << block) * freqMul;
+#endif
+ if ( reg20 & MASK_VIBRATO ) {
+ vibStrength = (Bit8u)(freq >> 7);
+#ifdef WAVE_PRECISION
+ vibrato = ( vibStrength * freqMul ) >> block;
+#else
+ vibrato = ( vibStrength << block ) * freqMul;
+#endif
+ } else {
+ vibStrength = 0;
+ vibrato = 0;
+ }
+}
+
+void Operator::UpdateRates( const Chip* chip ) {
+ //Mame seems to reverse this where enabling ksr actually lowers
+ //the rate, but pdf manuals says otherwise?
+ Bit8u newKsr = (Bit8u)((chanData >> SHIFT_KEYCODE) & 0xff);
+ if ( !( reg20 & MASK_KSR ) ) {
+ newKsr >>= 2;
+ }
+ if ( ksr == newKsr )
+ return;
+ ksr = newKsr;
+ UpdateAttack( chip );
+ UpdateDecay( chip );
+ UpdateRelease( chip );
+}
+
+INLINE Bit32s Operator::RateForward( Bit32u add ) {
+ rateIndex += add;
+ Bit32s ret = rateIndex >> RATE_SH;
+ rateIndex = rateIndex & RATE_MASK;
+ return ret;
+}
+
+template< Operator::State yes>
+Bits Operator::TemplateVolume( ) {
+ Bit32s vol = volume;
+ Bit32s change;
+ switch ( yes ) {
+ case OFF:
+ return ENV_MAX;
+ case ATTACK:
+ change = RateForward( attackAdd );
+ if ( !change )
+ return vol;
+ vol += ( (~vol) * change ) >> 3;
+ if ( vol < ENV_MIN ) {
+ volume = ENV_MIN;
+ rateIndex = 0;
+ SetState( DECAY );
+ return ENV_MIN;
+ }
+ break;
+ case DECAY:
+ vol += RateForward( decayAdd );
+ if ( vol >= sustainLevel ) {
+ //Check if we didn't overshoot max attenuation, then just go off
+ if ( vol >= ENV_MAX ) {
+ volume = ENV_MAX;
+ SetState( OFF );
+ return ENV_MAX;
+ }
+ //Continue as sustain
+ rateIndex = 0;
+ SetState( SUSTAIN );
+ }
+ break;
+ case SUSTAIN:
+ if ( reg20 & MASK_SUSTAIN ) {
+ return vol;
+ }
+ //In sustain phase, but not sustaining, do regular release
+ case RELEASE:
+ vol += RateForward( releaseAdd );;
+ if ( vol >= ENV_MAX ) {
+ volume = ENV_MAX;
+ SetState( OFF );
+ return ENV_MAX;
+ }
+ break;
+ }
+ volume = vol;
+ return vol;
+}
+
+static const VolumeHandler VolumeHandlerTable[5] = {
+ &Operator::TemplateVolume< Operator::OFF >,
+ &Operator::TemplateVolume< Operator::RELEASE >,
+ &Operator::TemplateVolume< Operator::SUSTAIN >,
+ &Operator::TemplateVolume< Operator::DECAY >,
+ &Operator::TemplateVolume< Operator::ATTACK >
+};
+
+INLINE Bitu Operator::ForwardVolume() {
+ return currentLevel + (this->*volHandler)();
+}
+
+
+INLINE Bitu Operator::ForwardWave() {
+ waveIndex += waveCurrent;
+ return waveIndex >> WAVE_SH;
+}
+
+void Operator::Write20( const Chip* chip, Bit8u val ) {
+ Bit8u change = (reg20 ^ val );
+ if ( !change )
+ return;
+ reg20 = val;
+ //Shift the tremolo bit over the entire register, saved a branch, YES!
+ tremoloMask = (Bit8s)(val) >> 7;
+ tremoloMask &= ~(( 1 << ENV_EXTRA ) -1);
+ //Update specific features based on changes
+ if ( change & MASK_KSR ) {
+ UpdateRates( chip );
+ }
+ //With sustain enable the volume doesn't change
+ if ( reg20 & MASK_SUSTAIN || ( !releaseAdd ) ) {
+ rateZero |= ( 1 << SUSTAIN );
+ } else {
+ rateZero &= ~( 1 << SUSTAIN );
+ }
+ //Frequency multiplier or vibrato changed
+ if ( change & (0xf | MASK_VIBRATO) ) {
+ freqMul = chip->freqMul[ val & 0xf ];
+ UpdateFrequency();
+ }
+}
+
+void Operator::Write40( const Chip* /*chip*/, Bit8u val ) {
+ if (!(reg40 ^ val ))
+ return;
+ reg40 = val;
+ UpdateAttenuation( );
+}
+
+void Operator::Write60( const Chip* chip, Bit8u val ) {
+ Bit8u change = reg60 ^ val;
+ reg60 = val;
+ if ( change & 0x0f ) {
+ UpdateDecay( chip );
+ }
+ if ( change & 0xf0 ) {
+ UpdateAttack( chip );
+ }
+}
+
+void Operator::Write80( const Chip* chip, Bit8u val ) {
+ Bit8u change = (reg80 ^ val );
+ if ( !change )
+ return;
+ reg80 = val;
+ Bit8u sustain = val >> 4;
+ //Turn 0xf into 0x1f
+ sustain |= ( sustain + 1) & 0x10;
+ sustainLevel = sustain << ( ENV_BITS - 5 );
+ if ( change & 0x0f ) {
+ UpdateRelease( chip );
+ }
+}
+
+void Operator::WriteE0( const Chip* chip, Bit8u val ) {
+ if ( !(regE0 ^ val) )
+ return;
+ //in opl3 mode you can always selet 7 waveforms regardless of waveformselect
+ Bit8u waveForm = val & ( ( 0x3 & chip->waveFormMask ) | (0x7 & chip->opl3Active ) );
+ regE0 = val;
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+ waveHandler = WaveHandlerTable[ waveForm ];
+#else
+ waveBase = WaveTable + WaveBaseTable[ waveForm ];
+ waveStart = WaveStartTable[ waveForm ] << WAVE_SH;
+ waveMask = WaveMaskTable[ waveForm ];
+#endif
+}
+
+INLINE void Operator::SetState( Bit8u s ) {
+ state = s;
+ volHandler = VolumeHandlerTable[ s ];
+}
+
+INLINE bool Operator::Silent() const {
+ if ( !ENV_SILENT( totalLevel + volume ) )
+ return false;
+ if ( !(rateZero & ( 1 << state ) ) )
+ return false;
+ return true;
+}
+
+ INLINE void Operator::Prepare( const Chip* chip ) {
+ currentLevel = totalLevel + (chip->tremoloValue & tremoloMask);
+ waveCurrent = waveAdd;
+ if ( vibStrength >> chip->vibratoShift ) {
+ Bit32s add = vibrato >> chip->vibratoShift;
+ //Sign extend over the shift value
+ Bit32s neg = chip->vibratoSign;
+ //Negate the add with -1 or 0
+ add = ( add ^ neg ) - neg;
+ waveCurrent += add;
+ }
+}
+
+void Operator::KeyOn( Bit8u mask ) {
+ if ( !keyOn ) {
+ //Restart the frequency generator
+#if ( DBOPL_WAVE > WAVE_HANDLER )
+ waveIndex = waveStart;
+#else
+ waveIndex = 0;
+#endif
+ rateIndex = 0;
+ SetState( ATTACK );
+ }
+ keyOn |= mask;
+}
+
+void Operator::KeyOff( Bit8u mask ) {
+ keyOn &= ~mask;
+ if ( !keyOn ) {
+ if ( state != OFF ) {
+ SetState( RELEASE );
+ }
+ }
+}
+
+INLINE Bits Operator::GetWave( Bitu index, Bitu vol ) {
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+ return waveHandler( index, vol << ( 3 - ENV_EXTRA ) );
+#elif ( DBOPL_WAVE == WAVE_TABLEMUL )
+ return (waveBase[ index & waveMask ] * MulTable[ vol >> ENV_EXTRA ]) >> MUL_SH;
+#elif ( DBOPL_WAVE == WAVE_TABLELOG )
+ Bit32s wave = waveBase[ index & waveMask ];
+ Bit32u total = ( wave & 0x7fff ) + vol << ( 3 - ENV_EXTRA );
+ Bit32s sig = ExpTable[ total & 0xff ];
+ Bit32u exp = total >> 8;
+ Bit32s neg = wave >> 16;
+ return ((sig ^ neg) - neg) >> exp;
+#else
+#error "No valid wave routine"
+#endif
+}
+
+Bits INLINE Operator::GetSample( Bits modulation ) {
+ Bitu vol = ForwardVolume();
+ if ( ENV_SILENT( vol ) ) {
+ //Simply forward the wave
+ waveIndex += waveCurrent;
+ return 0;
+ } else {
+ Bitu index = ForwardWave();
+ index += modulation;
+ return GetWave( index, vol );
+ }
+}
+
+Operator::Operator() {
+ chanData = 0;
+ freqMul = 0;
+ waveIndex = 0;
+ waveAdd = 0;
+ waveCurrent = 0;
+ keyOn = 0;
+ ksr = 0;
+ reg20 = 0;
+ reg40 = 0;
+ reg60 = 0;
+ reg80 = 0;
+ regE0 = 0;
+ SetState( OFF );
+ rateZero = (1 << OFF);
+ sustainLevel = ENV_MAX;
+ currentLevel = ENV_MAX;
+ totalLevel = ENV_MAX;
+ volume = ENV_MAX;
+}
+
+/*
+ Channel
+*/
+
+Channel::Channel() {
+ old[0] = old[1] = 0;
+ chanData = 0;
+ regB0 = 0;
+ regC0 = 0;
+ maskLeft = -1;
+ maskRight = -1;
+ feedback = 31;
+ fourMask = 0;
+ synthHandler = &Channel::BlockTemplate< sm2FM >;
+}
+
+void Channel::SetChanData( const Chip* chip, Bit32u data ) {
+ Bit32u change = chanData ^ data;
+ chanData = data;
+ Op( 0 )->chanData = data;
+ Op( 1 )->chanData = data;
+ //Since a frequency update triggered this, always update frequency
+ Op( 0 )->UpdateFrequency();
+ Op( 1 )->UpdateFrequency();
+ if ( change & ( 0xff << SHIFT_KSLBASE ) ) {
+ Op( 0 )->UpdateAttenuation();
+ Op( 1 )->UpdateAttenuation();
+ }
+ if ( change & ( 0xff << SHIFT_KEYCODE ) ) {
+ Op( 0 )->UpdateRates( chip );
+ Op( 1 )->UpdateRates( chip );
+ }
+}
+
+void Channel::UpdateFrequency( const Chip* chip, Bit8u fourOp ) {
+ //Extrace the frequency bits
+ Bit32u data = chanData & 0xffff;
+ Bit32u kslBase = KslTable[ data >> 6 ];
+ Bit32u keyCode = ( data & 0x1c00) >> 9;
+ if ( chip->reg08 & 0x40 ) {
+ keyCode |= ( data & 0x100)>>8; /* notesel == 1 */
+ } else {
+ keyCode |= ( data & 0x200)>>9; /* notesel == 0 */
+ }
+ //Add the keycode and ksl into the highest bits of chanData
+ data |= (keyCode << SHIFT_KEYCODE) | ( kslBase << SHIFT_KSLBASE );
+ ( this + 0 )->SetChanData( chip, data );
+ if ( fourOp & 0x3f ) {
+ ( this + 1 )->SetChanData( chip, data );
+ }
+}
+
+void Channel::WriteA0( const Chip* chip, Bit8u val ) {
+ Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask;
+ //Don't handle writes to silent fourop channels
+ if ( fourOp > 0x80 )
+ return;
+ Bit32u change = (chanData ^ val ) & 0xff;
+ if ( change ) {
+ chanData ^= change;
+ UpdateFrequency( chip, fourOp );
+ }
+}
+
+void Channel::WriteB0( const Chip* chip, Bit8u val ) {
+ Bit8u fourOp = chip->reg104 & chip->opl3Active & fourMask;
+ //Don't handle writes to silent fourop channels
+ if ( fourOp > 0x80 )
+ return;
+ Bitu change = (chanData ^ ( val << 8 ) ) & 0x1f00;
+ if ( change ) {
+ chanData ^= change;
+ UpdateFrequency( chip, fourOp );
+ }
+ //Check for a change in the keyon/off state
+ if ( !(( val ^ regB0) & 0x20))
+ return;
+ regB0 = val;
+ if ( val & 0x20 ) {
+ Op(0)->KeyOn( 0x1 );
+ Op(1)->KeyOn( 0x1 );
+ if ( fourOp & 0x3f ) {
+ ( this + 1 )->Op(0)->KeyOn( 1 );
+ ( this + 1 )->Op(1)->KeyOn( 1 );
+ }
+ } else {
+ Op(0)->KeyOff( 0x1 );
+ Op(1)->KeyOff( 0x1 );
+ if ( fourOp & 0x3f ) {
+ ( this + 1 )->Op(0)->KeyOff( 1 );
+ ( this + 1 )->Op(1)->KeyOff( 1 );
+ }
+ }
+}
+
+void Channel::WriteC0( const Chip* chip, Bit8u val ) {
+ Bit8u change = val ^ regC0;
+ if ( !change )
+ return;
+ regC0 = val;
+ feedback = ( val >> 1 ) & 7;
+ if ( feedback ) {
+ //We shift the input to the right 10 bit wave index value
+ feedback = 9 - feedback;
+ } else {
+ feedback = 31;
+ }
+ //Select the new synth mode
+ if ( chip->opl3Active ) {
+ //4-op mode enabled for this channel
+ if ( (chip->reg104 & fourMask) & 0x3f ) {
+ Channel* chan0, *chan1;
+ //Check if it's the 2nd channel in a 4-op
+ if ( !(fourMask & 0x80 ) ) {
+ chan0 = this;
+ chan1 = this + 1;
+ } else {
+ chan0 = this - 1;
+ chan1 = this;
+ }
+
+ Bit8u synth = ( (chan0->regC0 & 1) << 0 )| (( chan1->regC0 & 1) << 1 );
+ switch ( synth ) {
+ case 0:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3FMFM >;
+ break;
+ case 1:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3AMFM >;
+ break;
+ case 2:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3FMAM >;
+ break;
+ case 3:
+ chan0->synthHandler = &Channel::BlockTemplate< sm3AMAM >;
+ break;
+ }
+ //Disable updating percussion channels
+ } else if ((fourMask & 0x40) && ( chip->regBD & 0x20) ) {
+
+ //Regular dual op, am or fm
+ } else if ( val & 1 ) {
+ synthHandler = &Channel::BlockTemplate< sm3AM >;
+ } else {
+ synthHandler = &Channel::BlockTemplate< sm3FM >;
+ }
+ maskLeft = ( val & 0x10 ) ? -1 : 0;
+ maskRight = ( val & 0x20 ) ? -1 : 0;
+ //opl2 active
+ } else {
+ //Disable updating percussion channels
+ if ( (fourMask & 0x40) && ( chip->regBD & 0x20 ) ) {
+
+ //Regular dual op, am or fm
+ } else if ( val & 1 ) {
+ synthHandler = &Channel::BlockTemplate< sm2AM >;
+ } else {
+ synthHandler = &Channel::BlockTemplate< sm2FM >;
+ }
+ }
+}
+
+void Channel::ResetC0( const Chip* chip ) {
+ Bit8u val = regC0;
+ regC0 ^= 0xff;
+ WriteC0( chip, val );
+}
+
+template< bool opl3Mode>
+void Channel::GeneratePercussion( Chip* chip, Bit32s* output ) {
+ Channel* chan = this;
+
+ //BassDrum
+ Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback;
+ old[0] = old[1];
+ old[1] = Op(0)->GetSample( mod );
+
+ //When bassdrum is in AM mode first operator is ignoed
+ if ( chan->regC0 & 1 ) {
+ mod = 0;
+ } else {
+ mod = old[0];
+ }
+ Bit32s sample = Op(1)->GetSample( mod );
+
+
+ //Precalculate stuff used by other outputs
+ Bit32u noiseBit = chip->ForwardNoise() & 0x1;
+ Bit32u c2 = Op(2)->ForwardWave();
+ Bit32u c5 = Op(5)->ForwardWave();
+ Bit32u phaseBit = (((c2 & 0x88) ^ ((c2<<5) & 0x80)) | ((c5 ^ (c5<<2)) & 0x20)) ? 0x02 : 0x00;
+
+ //Hi-Hat
+ Bit32u hhVol = Op(2)->ForwardVolume();
+ if ( !ENV_SILENT( hhVol ) ) {
+ Bit32u hhIndex = (phaseBit<<8) | (0x34 << ( phaseBit ^ (noiseBit << 1 )));
+ sample += Op(2)->GetWave( hhIndex, hhVol );
+ }
+ //Snare Drum
+ Bit32u sdVol = Op(3)->ForwardVolume();
+ if ( !ENV_SILENT( sdVol ) ) {
+ Bit32u sdIndex = ( 0x100 + (c2 & 0x100) ) ^ ( noiseBit << 8 );
+ sample += Op(3)->GetWave( sdIndex, sdVol );
+ }
+ //Tom-tom
+ sample += Op(4)->GetSample( 0 );
+
+ //Top-Cymbal
+ Bit32u tcVol = Op(5)->ForwardVolume();
+ if ( !ENV_SILENT( tcVol ) ) {
+ Bit32u tcIndex = (1 + phaseBit) << 8;
+ sample += Op(5)->GetWave( tcIndex, tcVol );
+ }
+ sample <<= 1;
+ if ( opl3Mode ) {
+ output[0] += sample;
+ output[1] += sample;
+ } else {
+ output[0] += sample;
+ }
+}
+
+template
+Channel* Channel::BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output ) {
+ switch( mode ) {
+ case sm2AM:
+ case sm3AM:
+ if ( Op(0)->Silent() && Op(1)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 1);
+ }
+ break;
+ case sm2FM:
+ case sm3FM:
+ if ( Op(1)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 1);
+ }
+ break;
+ case sm3FMFM:
+ if ( Op(3)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+ break;
+ case sm3AMFM:
+ if ( Op(0)->Silent() && Op(3)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+ break;
+ case sm3FMAM:
+ if ( Op(1)->Silent() && Op(3)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+ break;
+ case sm3AMAM:
+ if ( Op(0)->Silent() && Op(2)->Silent() && Op(3)->Silent() ) {
+ old[0] = old[1] = 0;
+ return (this + 2);
+ }
+ break;
+ default:
+ break;
+ }
+ //Init the operators with the the current vibrato and tremolo values
+ Op( 0 )->Prepare( chip );
+ Op( 1 )->Prepare( chip );
+ if ( mode > sm4Start ) {
+ Op( 2 )->Prepare( chip );
+ Op( 3 )->Prepare( chip );
+ }
+ if ( mode > sm6Start ) {
+ Op( 4 )->Prepare( chip );
+ Op( 5 )->Prepare( chip );
+ }
+ for ( Bitu i = 0; i < samples; i++ ) {
+ //Early out for percussion handlers
+ if ( mode == sm2Percussion ) {
+ GeneratePercussion( chip, output + i );
+ continue; //Prevent some unitialized value bitching
+ } else if ( mode == sm3Percussion ) {
+ GeneratePercussion( chip, output + i * 2 );
+ continue; //Prevent some unitialized value bitching
+ }
+
+ //Do unsigned shift so we can shift out all bits but still stay in 10 bit range otherwise
+ Bit32s mod = (Bit32u)((old[0] + old[1])) >> feedback;
+ old[0] = old[1];
+ old[1] = Op(0)->GetSample( mod );
+ Bit32s sample;
+ Bit32s out0 = old[0];
+ if ( mode == sm2AM || mode == sm3AM ) {
+ sample = out0 + Op(1)->GetSample( 0 );
+ } else if ( mode == sm2FM || mode == sm3FM ) {
+ sample = Op(1)->GetSample( out0 );
+ } else if ( mode == sm3FMFM ) {
+ Bits next = Op(1)->GetSample( out0 );
+ next = Op(2)->GetSample( next );
+ sample = Op(3)->GetSample( next );
+ } else if ( mode == sm3AMFM ) {
+ sample = out0;
+ Bits next = Op(1)->GetSample( 0 );
+ next = Op(2)->GetSample( next );
+ sample += Op(3)->GetSample( next );
+ } else if ( mode == sm3FMAM ) {
+ sample = Op(1)->GetSample( out0 );
+ Bits next = Op(2)->GetSample( 0 );
+ sample += Op(3)->GetSample( next );
+ } else if ( mode == sm3AMAM ) {
+ sample = out0;
+ Bits next = Op(1)->GetSample( 0 );
+ sample += Op(2)->GetSample( next );
+ sample += Op(3)->GetSample( 0 );
+ }
+ switch( mode ) {
+ case sm2AM:
+ case sm2FM:
+ output[ i ] += sample;
+ break;
+ case sm3AM:
+ case sm3FM:
+ case sm3FMFM:
+ case sm3AMFM:
+ case sm3FMAM:
+ case sm3AMAM:
+ output[ i * 2 + 0 ] += sample & maskLeft;
+ output[ i * 2 + 1 ] += sample & maskRight;
+ break;
+ default:
+ break;
+ }
+ }
+ switch( mode ) {
+ case sm2AM:
+ case sm2FM:
+ case sm3AM:
+ case sm3FM:
+ return ( this + 1 );
+ case sm3FMFM:
+ case sm3AMFM:
+ case sm3FMAM:
+ case sm3AMAM:
+ return( this + 2 );
+ case sm2Percussion:
+ case sm3Percussion:
+ return( this + 3 );
+ }
+ return 0;
+}
+
+/*
+ Chip
+*/
+
+Chip::Chip() {
+ reg08 = 0;
+ reg04 = 0;
+ regBD = 0;
+ reg104 = 0;
+ opl3Active = 0;
+}
+
+INLINE Bit32u Chip::ForwardNoise() {
+ noiseCounter += noiseAdd;
+ Bitu count = noiseCounter >> LFO_SH;
+ noiseCounter &= WAVE_MASK;
+ for ( ; count > 0; --count ) {
+ //Noise calculation from mame
+ noiseValue ^= ( 0x800302 ) & ( 0 - (noiseValue & 1 ) );
+ noiseValue >>= 1;
+ }
+ return noiseValue;
+}
+
+Bit32u Chip::ForwardLFO( Bit32u samples ) {
+ //Current vibrato value, runs 4x slower than tremolo
+ vibratoSign = ( VibratoTable[ vibratoIndex >> 2] ) >> 7;
+ vibratoShift = ( VibratoTable[ vibratoIndex >> 2] & 7) + vibratoStrength;
+ tremoloValue = TremoloTable[ tremoloIndex ] >> tremoloStrength;
+
+ //Check hom many samples there can be done before the value changes
+ Bit32u todo = LFO_MAX - lfoCounter;
+ Bit32u count = (todo + lfoAdd - 1) / lfoAdd;
+ if ( count > samples ) {
+ count = samples;
+ lfoCounter += count * lfoAdd;
+ } else {
+ lfoCounter += count * lfoAdd;
+ lfoCounter &= (LFO_MAX - 1);
+ //Maximum of 7 vibrato value * 4
+ vibratoIndex = ( vibratoIndex + 1 ) & 31;
+ //Clip tremolo to the the table size
+ if ( tremoloIndex + 1 < TREMOLO_TABLE )
+ ++tremoloIndex;
+ else
+ tremoloIndex = 0;
+ }
+ return count;
+}
+
+void Chip::WriteBD( Bit8u val ) {
+ Bit8u change = regBD ^ val;
+ if ( !change )
+ return;
+ regBD = val;
+ //TODO could do this with shift and xor?
+ vibratoStrength = (val & 0x40) ? 0x00 : 0x01;
+ tremoloStrength = (val & 0x80) ? 0x00 : 0x02;
+ if ( val & 0x20 ) {
+ //Drum was just enabled, make sure channel 6 has the right synth
+ if ( change & 0x20 ) {
+ if ( opl3Active ) {
+ chan[6].synthHandler = &Channel::BlockTemplate< sm3Percussion >;
+ } else {
+ chan[6].synthHandler = &Channel::BlockTemplate< sm2Percussion >;
+ }
+ }
+ //Bass Drum
+ if ( val & 0x10 ) {
+ chan[6].op[0].KeyOn( 0x2 );
+ chan[6].op[1].KeyOn( 0x2 );
+ } else {
+ chan[6].op[0].KeyOff( 0x2 );
+ chan[6].op[1].KeyOff( 0x2 );
+ }
+ //Hi-Hat
+ if ( val & 0x1 ) {
+ chan[7].op[0].KeyOn( 0x2 );
+ } else {
+ chan[7].op[0].KeyOff( 0x2 );
+ }
+ //Snare
+ if ( val & 0x8 ) {
+ chan[7].op[1].KeyOn( 0x2 );
+ } else {
+ chan[7].op[1].KeyOff( 0x2 );
+ }
+ //Tom-Tom
+ if ( val & 0x4 ) {
+ chan[8].op[0].KeyOn( 0x2 );
+ } else {
+ chan[8].op[0].KeyOff( 0x2 );
+ }
+ //Top Cymbal
+ if ( val & 0x2 ) {
+ chan[8].op[1].KeyOn( 0x2 );
+ } else {
+ chan[8].op[1].KeyOff( 0x2 );
+ }
+ //Toggle keyoffs when we turn off the percussion
+ } else if ( change & 0x20 ) {
+ //Trigger a reset to setup the original synth handler
+ chan[6].ResetC0( this );
+ chan[6].op[0].KeyOff( 0x2 );
+ chan[6].op[1].KeyOff( 0x2 );
+ chan[7].op[0].KeyOff( 0x2 );
+ chan[7].op[1].KeyOff( 0x2 );
+ chan[8].op[0].KeyOff( 0x2 );
+ chan[8].op[1].KeyOff( 0x2 );
+ }
+}
+
+
+#define REGOP( _FUNC_ ) \
+ index = ( ( reg >> 3) & 0x20 ) | ( reg & 0x1f ); \
+ if ( OpOffsetTable[ index ] ) { \
+ Operator* regOp = (Operator*)( ((char *)this ) + OpOffsetTable[ index ] ); \
+ regOp->_FUNC_( this, val ); \
+ }
+
+#define REGCHAN( _FUNC_ ) \
+ index = ( ( reg >> 4) & 0x10 ) | ( reg & 0xf ); \
+ if ( ChanOffsetTable[ index ] ) { \
+ Channel* regChan = (Channel*)( ((char *)this ) + ChanOffsetTable[ index ] ); \
+ regChan->_FUNC_( this, val ); \
+ }
+
+void Chip::WriteReg( Bit32u reg, Bit8u val ) {
+ Bitu index;
+ switch ( (reg & 0xf0) >> 4 ) {
+ case 0x00 >> 4:
+ if ( reg == 0x01 ) {
+ waveFormMask = ( val & 0x20 ) ? 0x7 : 0x0;
+ } else if ( reg == 0x104 ) {
+ //Only detect changes in lowest 6 bits
+ if ( !((reg104 ^ val) & 0x3f) )
+ return;
+ //Always keep the highest bit enabled, for checking > 0x80
+ reg104 = 0x80 | ( val & 0x3f );
+ } else if ( reg == 0x105 ) {
+ //MAME says the real opl3 doesn't reset anything on opl3 disable/enable till the next write in another register
+ if ( !((opl3Active ^ val) & 1 ) )
+ return;
+ opl3Active = ( val & 1 ) ? 0xff : 0;
+ //Update the 0xc0 register for all channels to signal the switch to mono/stereo handlers
+ for ( int i = 0; i < 18;i++ ) {
+ chan[i].ResetC0( this );
+ }
+ } else if ( reg == 0x08 ) {
+ reg08 = val;
+ }
+ case 0x10 >> 4:
+ break;
+ case 0x20 >> 4:
+ case 0x30 >> 4:
+ REGOP( Write20 );
+ break;
+ case 0x40 >> 4:
+ case 0x50 >> 4:
+ REGOP( Write40 );
+ break;
+ case 0x60 >> 4:
+ case 0x70 >> 4:
+ REGOP( Write60 );
+ break;
+ case 0x80 >> 4:
+ case 0x90 >> 4:
+ REGOP( Write80 );
+ break;
+ case 0xa0 >> 4:
+ REGCHAN( WriteA0 );
+ break;
+ case 0xb0 >> 4:
+ if ( reg == 0xbd ) {
+ WriteBD( val );
+ } else {
+ REGCHAN( WriteB0 );
+ }
+ break;
+ case 0xc0 >> 4:
+ REGCHAN( WriteC0 );
+ case 0xd0 >> 4:
+ break;
+ case 0xe0 >> 4:
+ case 0xf0 >> 4:
+ REGOP( WriteE0 );
+ break;
+ }
+}
+
+
+Bit32u Chip::WriteAddr( Bit32u port, Bit8u val ) {
+ switch ( port & 3 ) {
+ case 0:
+ return val;
+ case 2:
+ if ( opl3Active || (val == 0x05) )
+ return 0x100 | val;
+ else
+ return val;
+ }
+ return 0;
+}
+
+void Chip::GenerateBlock2( Bitu total, Bit32s* output ) {
+ while ( total > 0 ) {
+ Bit32u samples = ForwardLFO( total );
+ for ( Bitu i = 0; i < samples; i++ ) {
+ output[i] = 0;
+ }
+ int count = 0;
+ for( Channel* ch = chan; ch < chan + 9; ) {
+ count++;
+ ch = (ch->*(ch->synthHandler))( this, samples, output );
+ }
+ total -= samples;
+ output += samples;
+ }
+}
+
+void Chip::GenerateBlock3( Bitu total, Bit32s* output ) {
+ while ( total > 0 ) {
+ Bit32u samples = ForwardLFO( total );
+ for ( Bitu i = 0; i < samples; i++ ) {
+ output[i * 2 + 0 ] = 0;
+ output[i * 2 + 1 ] = 0;
+ }
+ int count = 0;
+ for( Channel* ch = chan; ch < chan + 18; ) {
+ count++;
+ ch = (ch->*(ch->synthHandler))( this, samples, output );
+ }
+ total -= samples;
+ output += samples * 2;
+ }
+}
+
+void Chip::Setup( Bit32u clock, Bit32u rate ) {
+ double original = (double)clock / 288.0;
+ double scale = original / (double)rate;
+ if (fabs(scale - 1.0) < 0.00001)
+ scale = 1.0;
+
+ //Noise counter is run at the same precision as general waves
+ noiseAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) );
+ noiseCounter = 0;
+ noiseValue = 1; //Make sure it triggers the noise xor the first time
+ //The low frequency oscillation counter
+ //Every time his overflows vibrato and tremoloindex are increased
+ lfoAdd = (Bit32u)( 0.5 + scale * ( 1 << LFO_SH ) );
+ lfoCounter = 0;
+ vibratoIndex = 0;
+ tremoloIndex = 0;
+
+ //With higher octave this gets shifted up
+ //-1 since the freqCreateTable = *2
+#ifdef WAVE_PRECISION
+ double freqScale = ( 1 << 7 ) * scale * ( 1 << ( WAVE_SH - 1 - 10));
+ for ( int i = 0; i < 16; i++ ) {
+ //Use rounding with 0.5
+ freqMul[i] = (Bit32u)( 0.5 + freqScale * FreqCreateTable[ i ] );
+ }
+#else
+ Bit32u freqScale = (Bit32u)( 0.5 + scale * ( 1 << ( WAVE_SH - 1 - 10)));
+ for ( int i = 0; i < 16; i++ ) {
+ freqMul[i] = freqScale * FreqCreateTable[ i ];
+ }
+#endif
+
+ //-3 since the real envelope takes 8 steps to reach the single value we supply
+ for ( Bit8u i = 0; i < 76; i++ ) {
+ Bit8u index, shift;
+ EnvelopeSelect( i, index, shift );
+ linearRates[i] = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH + ENV_EXTRA - shift - 3 )));
+ }
+ //Generate the best matching attack rate
+ for ( Bit8u i = 0; i < 62; i++ ) {
+ Bit8u index, shift;
+ EnvelopeSelect( i, index, shift );
+ //Original amount of samples the attack would take
+ Bit32s original = (Bit32u)( (AttackSamplesTable[ index ] << shift) / scale);
+
+ Bit32s guessAdd = (Bit32u)( scale * (EnvelopeIncreaseTable[ index ] << ( RATE_SH - shift - 3 )));
+ Bit32s bestAdd = guessAdd;
+ Bit32u bestDiff = 1 << 30;
+ for( Bit32u passes = 0; passes < 16; passes ++ ) {
+ Bit32s volume = ENV_MAX;
+ Bit32s samples = 0;
+ Bit32u count = 0;
+ while ( volume > 0 && samples < original * 2 ) {
+ count += guessAdd;
+ Bit32s change = count >> RATE_SH;
+ count &= RATE_MASK;
+ if ( change ) {
+ volume += ( ~volume * change ) >> 3;
+ }
+ samples++;
+
+ }
+ Bit32s diff = original - samples;
+ Bit32u lDiff = labs( diff );
+ //Init last on first pass
+ if ( lDiff < bestDiff ) {
+ bestDiff = lDiff;
+ bestAdd = guessAdd;
+ if ( !bestDiff )
+ break;
+ }
+ //Below our target
+ if ( diff < 0 ) {
+ //Better than the last time
+ Bit32s mul = ((original - diff) << 12) / original;
+ guessAdd = ((guessAdd * mul) >> 12);
+ guessAdd++;
+ } else if ( diff > 0 ) {
+ Bit32s mul = ((original - diff) << 12) / original;
+ guessAdd = (guessAdd * mul) >> 12;
+ guessAdd--;
+ }
+ }
+ attackRates[i] = bestAdd;
+ }
+ for ( Bit8u i = 62; i < 76; i++ ) {
+ //This should provide instant volume maximizing
+ attackRates[i] = 8 << RATE_SH;
+ }
+ //Setup the channels with the correct four op flags
+ //Channels are accessed through a table so they appear linear here
+ chan[ 0].fourMask = 0x00 | ( 1 << 0 );
+ chan[ 1].fourMask = 0x80 | ( 1 << 0 );
+ chan[ 2].fourMask = 0x00 | ( 1 << 1 );
+ chan[ 3].fourMask = 0x80 | ( 1 << 1 );
+ chan[ 4].fourMask = 0x00 | ( 1 << 2 );
+ chan[ 5].fourMask = 0x80 | ( 1 << 2 );
+
+ chan[ 9].fourMask = 0x00 | ( 1 << 3 );
+ chan[10].fourMask = 0x80 | ( 1 << 3 );
+ chan[11].fourMask = 0x00 | ( 1 << 4 );
+ chan[12].fourMask = 0x80 | ( 1 << 4 );
+ chan[13].fourMask = 0x00 | ( 1 << 5 );
+ chan[14].fourMask = 0x80 | ( 1 << 5 );
+
+ //mark the percussion channels
+ chan[ 6].fourMask = 0x40;
+ chan[ 7].fourMask = 0x40;
+ chan[ 8].fourMask = 0x40;
+
+ //Clear Everything in opl3 mode
+ WriteReg( 0x105, 0x1 );
+ for ( int i = 0; i < 512; i++ ) {
+ if ( i == 0x105 )
+ continue;
+ WriteReg( i, 0xff );
+ WriteReg( i, 0x0 );
+ }
+ WriteReg( 0x105, 0x0 );
+ //Clear everything in opl2 mode
+ for ( int i = 0; i < 256; i++ ) {
+ WriteReg( i, 0xff );
+ WriteReg( i, 0x0 );
+ }
+}
+
+static bool doneTables = false;
+void InitTables( void ) {
+ if ( doneTables )
+ return;
+ doneTables = true;
+#if ( DBOPL_WAVE == WAVE_HANDLER ) || ( DBOPL_WAVE == WAVE_TABLELOG )
+ //Exponential volume table, same as the real adlib
+ for ( int i = 0; i < 256; i++ ) {
+ //Save them in reverse
+ ExpTable[i] = (int)( 0.5 + ( pow(2.0, ( 255 - i) * ( 1.0 /256 ) )-1) * 1024 );
+ ExpTable[i] += 1024; //or remove the -1 oh well :)
+ //Preshift to the left once so the final volume can shift to the right
+ ExpTable[i] *= 2;
+ }
+#endif
+#if ( DBOPL_WAVE == WAVE_HANDLER )
+ //Add 0.5 for the trunc rounding of the integer cast
+ //Do a PI sinetable instead of the original 0.5 PI
+ for ( int i = 0; i < 512; i++ ) {
+ SinTable[i] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 );
+ }
+#endif
+#if ( DBOPL_WAVE == WAVE_TABLEMUL )
+ //Multiplication based tables
+ for ( int i = 0; i < 384; i++ ) {
+ int s = i * 8;
+ //TODO maybe keep some of the precision errors of the original table?
+ double val = ( 0.5 + ( pow(2.0, -1.0 + ( 255 - s) * ( 1.0 /256 ) )) * ( 1 << MUL_SH ));
+ MulTable[i] = (Bit16u)(val);
+ }
+
+ //Sine Wave Base
+ for ( int i = 0; i < 512; i++ ) {
+ WaveTable[ 0x0200 + i ] = (Bit16s)(sin( (i + 0.5) * (PI / 512.0) ) * 4084);
+ WaveTable[ 0x0000 + i ] = -WaveTable[ 0x200 + i ];
+ }
+ //Exponential wave
+ for ( int i = 0; i < 256; i++ ) {
+ WaveTable[ 0x700 + i ] = (Bit16s)( 0.5 + ( pow(2.0, -1.0 + ( 255 - i * 8) * ( 1.0 /256 ) ) ) * 4085 );
+ WaveTable[ 0x6ff - i ] = -WaveTable[ 0x700 + i ];
+ }
+#endif
+#if ( DBOPL_WAVE == WAVE_TABLELOG )
+ //Sine Wave Base
+ for ( int i = 0; i < 512; i++ ) {
+ WaveTable[ 0x0200 + i ] = (Bit16s)( 0.5 - log10( sin( (i + 0.5) * (PI / 512.0) ) ) / log10(2.0)*256 );
+ WaveTable[ 0x0000 + i ] = ((Bit16s)0x8000) | WaveTable[ 0x200 + i];
+ }
+ //Exponential wave
+ for ( int i = 0; i < 256; i++ ) {
+ WaveTable[ 0x700 + i ] = i * 8;
+ WaveTable[ 0x6ff - i ] = ((Bit16s)0x8000) | i * 8;
+ }
+#endif
+
+ // | |//\\|____|WAV7|//__|/\ |____|/\/\|
+ // |\\//| | |WAV7| | \/| | |
+ // |06 |0126|27 |7 |3 |4 |4 5 |5 |
+
+#if (( DBOPL_WAVE == WAVE_TABLELOG ) || ( DBOPL_WAVE == WAVE_TABLEMUL ))
+ for ( int i = 0; i < 256; i++ ) {
+ //Fill silence gaps
+ WaveTable[ 0x400 + i ] = WaveTable[0];
+ WaveTable[ 0x500 + i ] = WaveTable[0];
+ WaveTable[ 0x900 + i ] = WaveTable[0];
+ WaveTable[ 0xc00 + i ] = WaveTable[0];
+ WaveTable[ 0xd00 + i ] = WaveTable[0];
+ //Replicate sines in other pieces
+ WaveTable[ 0x800 + i ] = WaveTable[ 0x200 + i ];
+ //double speed sines
+ WaveTable[ 0xa00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ WaveTable[ 0xb00 + i ] = WaveTable[ 0x000 + i * 2 ];
+ WaveTable[ 0xe00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ WaveTable[ 0xf00 + i ] = WaveTable[ 0x200 + i * 2 ];
+ }
+#endif
+
+ //Create the ksl table
+ for ( int oct = 0; oct < 8; oct++ ) {
+ int base = oct * 8;
+ for ( int i = 0; i < 16; i++ ) {
+ int val = base - KslCreateTable[i];
+ if ( val < 0 )
+ val = 0;
+ //*4 for the final range to match attenuation range
+ KslTable[ oct * 16 + i ] = val * 4;
+ }
+ }
+ //Create the Tremolo table, just increase and decrease a triangle wave
+ for ( Bit8u i = 0; i < TREMOLO_TABLE / 2; i++ ) {
+ Bit8u val = i << ENV_EXTRA;
+ TremoloTable[i] = val;
+ TremoloTable[TREMOLO_TABLE - 1 - i] = val;
+ }
+ //Create a table with offsets of the channels from the start of the chip
+ DBOPL::Chip* chip = 0;
+ for ( Bitu i = 0; i < 32; i++ ) {
+ Bitu index = i & 0xf;
+ if ( index >= 9 ) {
+ ChanOffsetTable[i] = 0;
+ continue;
+ }
+ //Make sure the four op channels follow eachother
+ if ( index < 6 ) {
+ index = (index % 3) * 2 + ( index / 3 );
+ }
+ //Add back the bits for highest ones
+ if ( i >= 16 )
+ index += 9;
+ Bitu blah = static_cast( reinterpret_cast( &(chip->chan[ index ]) ) );
+ ChanOffsetTable[i] = blah;
+ }
+ //Same for operators
+ for ( Bitu i = 0; i < 64; i++ ) {
+ if ( i % 8 >= 6 || ( (i / 8) % 4 == 3 ) ) {
+ OpOffsetTable[i] = 0;
+ continue;
+ }
+ Bitu chNum = (i / 8) * 3 + (i % 8) % 3;
+ //Make sure we use 16 and up for the 2nd range to match the chanoffset gap
+ if ( chNum >= 12 )
+ chNum += 16 - 12;
+ Bitu opNum = ( i % 8 ) / 3;
+ DBOPL::Channel* chan = 0;
+ Bitu blah = static_cast( reinterpret_cast ( &(chan->op[opNum]) ) );
+ OpOffsetTable[i] = ChanOffsetTable[ chNum ] + blah;
+ }
+#if 0
+ //Stupid checks if table's are correct
+ for ( Bitu i = 0; i < 18; i++ ) {
+ Bit32u find = (Bit16u)( &(chip->chan[ i ]) );
+ for ( Bitu c = 0; c < 32; c++ ) {
+ if ( ChanOffsetTable[c] == find ) {
+ find = 0;
+ break;
+ }
+ }
+ if ( find ) {
+ find = find;
+ }
+ }
+ for ( Bitu i = 0; i < 36; i++ ) {
+ Bit32u find = (Bit16u)( &(chip->chan[ i / 2 ].op[i % 2]) );
+ for ( Bitu c = 0; c < 64; c++ ) {
+ if ( OpOffsetTable[c] == find ) {
+ find = 0;
+ break;
+ }
+ }
+ if ( find ) {
+ find = find;
+ }
+ }
+#endif
+}
+
+/*Bit32u Handler::WriteAddr( Bit32u port, Bit8u val ) {
+ return chip.WriteAddr( port, val );
+
+}
+void Handler::WriteReg( Bit32u addr, Bit8u val ) {
+ chip.WriteReg( addr, val );
+}
+
+void Handler::Generate( MixerChannel* chan, Bitu samples ) {
+ Bit32s buffer[ 512 * 2 ];
+ if ( samples > 512 )
+ samples = 512;
+ if ( !chip.opl3Active ) {
+ chip.GenerateBlock2( samples, buffer );
+ chan->AddSamples_m32( samples, buffer );
+ } else {
+ chip.GenerateBlock3( samples, buffer );
+ chan->AddSamples_s32( samples, buffer );
+ }
+}
+
+void Handler::Init( Bitu rate ) {
+ InitTables();
+ chip.Setup( rate );
+}*/
+
+static class init {
+public:
+ init() { InitTables(); }
+} init_stuff;
+
+
+} //Namespace DBOPL
diff --git a/Frameworks/GME/gme/dbopl.h b/Frameworks/GME/gme/dbopl.h
new file mode 100644
index 000000000..7e24cc10e
--- /dev/null
+++ b/Frameworks/GME/gme/dbopl.h
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2002-2009 The DOSBox Team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef _WIN32
+#define INLINE __forceinline
+#define DB_FASTCALL __fastcall
+#else
+#define INLINE inline
+#define DB_FASTCALL __attribute__((fastcall))
+#endif
+
+typedef double Real64;
+/* The internal types */
+
+#ifdef HAVE_STDINT_H
+#include
+
+typedef uint8_t Bit8u;
+typedef int8_t Bit8s;
+typedef uint16_t Bit16u;
+typedef int16_t Bit16s;
+typedef uint32_t Bit32u;
+typedef int32_t Bit32s;
+typedef uint64_t Bit64u;
+typedef int64_t Bit64s;
+#else
+typedef unsigned char Bit8u;
+typedef signed char Bit8s;
+typedef unsigned short Bit16u;
+typedef signed short Bit16s;
+typedef unsigned long Bit32u;
+typedef signed long Bit32s;
+typedef unsigned __int64 Bit64u;
+typedef signed __int64 Bit64s;
+#endif
+
+typedef unsigned int Bitu;
+typedef signed int Bits;
+
+#include "adlib.h"
+//#include "dosbox.h"
+
+//Use 8 handlers based on a small logatirmic wavetabe and an exponential table for volume
+#define WAVE_HANDLER 10
+//Use a logarithmic wavetable with an exponential table for volume
+#define WAVE_TABLELOG 11
+//Use a linear wavetable with a multiply table for volume
+#define WAVE_TABLEMUL 12
+
+//Select the type of wave generator routine
+#define DBOPL_WAVE WAVE_TABLEMUL
+
+namespace DBOPL {
+
+struct Chip;
+struct Operator;
+struct Channel;
+
+#if (DBOPL_WAVE == WAVE_HANDLER)
+typedef Bits ( DB_FASTCALL *WaveHandler) ( Bitu i, Bitu volume );
+#endif
+
+typedef Bits ( DBOPL::Operator::*VolumeHandler) ( );
+typedef Channel* ( DBOPL::Channel::*SynthHandler) ( Chip* chip, Bit32u samples, Bit32s* output );
+
+//Different synth modes that can generate blocks of data
+typedef enum {
+ sm2AM,
+ sm2FM,
+ sm3AM,
+ sm3FM,
+ sm4Start,
+ sm3FMFM,
+ sm3AMFM,
+ sm3FMAM,
+ sm3AMAM,
+ sm6Start,
+ sm2Percussion,
+ sm3Percussion
+} SynthMode;
+
+//Shifts for the values contained in chandata variable
+enum {
+ SHIFT_KSLBASE = 16,
+ SHIFT_KEYCODE = 24
+};
+
+struct Operator {
+public:
+ //Masks for operator 20 values
+ enum {
+ MASK_KSR = 0x10,
+ MASK_SUSTAIN = 0x20,
+ MASK_VIBRATO = 0x40,
+ MASK_TREMOLO = 0x80
+ };
+
+ typedef enum {
+ OFF,
+ RELEASE,
+ SUSTAIN,
+ DECAY,
+ ATTACK
+ } State;
+
+ VolumeHandler volHandler;
+
+#if (DBOPL_WAVE == WAVE_HANDLER)
+ WaveHandler waveHandler; //Routine that generate a wave
+#else
+ Bit16s* waveBase;
+ Bit32u waveMask;
+ Bit32u waveStart;
+#endif
+ Bit32u waveIndex; //WAVE_BITS shifted counter of the frequency index
+ Bit32u waveAdd; //The base frequency without vibrato
+ Bit32u waveCurrent; //waveAdd + vibratao
+
+ Bit32u chanData; //Frequency/octave and derived data coming from whatever channel controls this
+ Bit32u freqMul; //Scale channel frequency with this, TODO maybe remove?
+ Bit32u vibrato; //Scaled up vibrato strength
+ Bit32s sustainLevel; //When stopping at sustain level stop here
+ Bit32s totalLevel; //totalLevel is added to every generated volume
+ Bit32u currentLevel; //totalLevel + tremolo
+ Bit32s volume; //The currently active volume
+
+ Bit32u attackAdd; //Timers for the different states of the envelope
+ Bit32u decayAdd;
+ Bit32u releaseAdd;
+ Bit32u rateIndex; //Current position of the evenlope
+
+ Bit8u rateZero; //Bits for the different states of the envelope having no changes
+ Bit8u keyOn; //Bitmask of different values that can generate keyon
+ //Registers, also used to check for changes
+ Bit8u reg20, reg40, reg60, reg80, regE0;
+ //Active part of the envelope we're in
+ Bit8u state;
+ //0xff when tremolo is enabled
+ Bit8u tremoloMask;
+ //Strength of the vibrato
+ Bit8u vibStrength;
+ //Keep track of the calculated KSR so we can check for changes
+ Bit8u ksr;
+private:
+ void SetState( Bit8u s );
+ void UpdateAttack( const Chip* chip );
+ void UpdateRelease( const Chip* chip );
+ void UpdateDecay( const Chip* chip );
+public:
+ void UpdateAttenuation();
+ void UpdateRates( const Chip* chip );
+ void UpdateFrequency( );
+
+ void Write20( const Chip* chip, Bit8u val );
+ void Write40( const Chip* chip, Bit8u val );
+ void Write60( const Chip* chip, Bit8u val );
+ void Write80( const Chip* chip, Bit8u val );
+ void WriteE0( const Chip* chip, Bit8u val );
+
+ bool Silent() const;
+ void Prepare( const Chip* chip );
+
+ void KeyOn( Bit8u mask);
+ void KeyOff( Bit8u mask);
+
+ template< State state>
+ Bits TemplateVolume( );
+
+ Bit32s RateForward( Bit32u add );
+ Bitu ForwardWave();
+ Bitu ForwardVolume();
+
+ Bits GetSample( Bits modulation );
+ Bits GetWave( Bitu index, Bitu vol );
+public:
+ Operator();
+};
+
+struct Channel {
+ Operator op[2];
+ inline Operator* Op( Bitu index ) {
+ return &( ( this + (index >> 1) )->op[ index & 1 ]);
+ }
+ SynthHandler synthHandler;
+ Bit32u chanData; //Frequency/octave and derived values
+ Bit32s old[2]; //Old data for feedback
+
+ Bit8u feedback; //Feedback shift
+ Bit8u regB0; //Register values to check for changes
+ Bit8u regC0;
+ //This should correspond with reg104, bit 6 indicates a Percussion channel, bit 7 indicates a silent channel
+ Bit8u fourMask;
+ Bit8s maskLeft; //Sign extended values for both channel's panning
+ Bit8s maskRight;
+
+ //Forward the channel data to the operators of the channel
+ void SetChanData( const Chip* chip, Bit32u data );
+ //Change in the chandata, check for new values and if we have to forward to operators
+ void UpdateFrequency( const Chip* chip, Bit8u fourOp );
+ void WriteA0( const Chip* chip, Bit8u val );
+ void WriteB0( const Chip* chip, Bit8u val );
+ void WriteC0( const Chip* chip, Bit8u val );
+ void ResetC0( const Chip* chip );
+
+ //call this for the first channel
+ template< bool opl3Mode >
+ void GeneratePercussion( Chip* chip, Bit32s* output );
+
+ //Generate blocks of data in specific modes
+ template
+ Channel* BlockTemplate( Chip* chip, Bit32u samples, Bit32s* output );
+ Channel();
+};
+
+struct Chip {
+ //This is used as the base counter for vibrato and tremolo
+ Bit32u lfoCounter;
+ Bit32u lfoAdd;
+
+
+ Bit32u noiseCounter;
+ Bit32u noiseAdd;
+ Bit32u noiseValue;
+
+ //Frequency scales for the different multiplications
+ Bit32u freqMul[16];
+ //Rates for decay and release for rate of this chip
+ Bit32u linearRates[76];
+ //Best match attack rates for the rate of this chip
+ Bit32u attackRates[76];
+
+ //18 channels with 2 operators each
+ Channel chan[18];
+
+ Bit8u reg104;
+ Bit8u reg08;
+ Bit8u reg04;
+ Bit8u regBD;
+ Bit8u vibratoIndex;
+ Bit8u tremoloIndex;
+ Bit8s vibratoSign;
+ Bit8u vibratoShift;
+ Bit8u tremoloValue;
+ Bit8u vibratoStrength;
+ Bit8u tremoloStrength;
+ //Mask for allowed wave forms
+ Bit8u waveFormMask;
+ //0 or -1 when enabled
+ Bit8s opl3Active;
+
+ //Return the maximum amount of samples before and LFO change
+ Bit32u ForwardLFO( Bit32u samples );
+ Bit32u ForwardNoise();
+
+ void WriteBD( Bit8u val );
+ void WriteReg(Bit32u reg, Bit8u val );
+
+ Bit32u WriteAddr( Bit32u port, Bit8u val );
+
+ void GenerateBlock2( Bitu samples, Bit32s* output );
+ void GenerateBlock3( Bitu samples, Bit32s* output );
+
+ void Setup( Bit32u c, Bit32u r );
+
+ Chip();
+};
+
+/*struct Handler : public Adlib::Handler {
+ DBOPL::Chip chip;
+ virtual Bit32u WriteAddr( Bit32u port, Bit8u val );
+ virtual void WriteReg( Bit32u addr, Bit8u val );
+ virtual void Generate( MixerChannel* chan, Bitu samples );
+ virtual void Init( Bitu rate );
+};*/
+
+
+} //Namespace
diff --git a/Frameworks/GME/gme/divfix.h b/Frameworks/GME/gme/divfix.h
new file mode 100644
index 000000000..21212df3f
--- /dev/null
+++ b/Frameworks/GME/gme/divfix.h
@@ -0,0 +1,18 @@
+
+static Uint32 DivFix(Uint32 p1, Uint32 p2, Uint32 fix)
+{
+ Uint32 ret;
+ ret = p1 / p2;
+ p1 = p1 % p2;/* p1 = p1 - p2 * ret; */
+ while (fix--)
+ {
+ p1 += p1;
+ ret += ret;
+ if (p1 >= p2)
+ {
+ p1 -= p2;
+ ret++;
+ }
+ }
+ return ret;
+}
diff --git a/Frameworks/GME/gme/emuconfig.h b/Frameworks/GME/gme/emuconfig.h
new file mode 100644
index 000000000..9250e1cf9
--- /dev/null
+++ b/Frameworks/GME/gme/emuconfig.h
@@ -0,0 +1,78 @@
+/////////////////////////////////////////////////////////////////////////////
+//
+// Configuration for emulation libraries
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef __EMUCONFIG_H__
+#define __EMUCONFIG_H__
+
+/////////////////////////////////////////////////////////////////////////////
+
+#include
+#include
+#include
+#include
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// WIN32 native project definitions
+//
+/////////////////////////////////////////////////////////////////////////////
+#if defined(WIN32) && !defined(__GNUC__)
+
+#define EMU_CALL __fastcall
+#define EMU_CALL_ __cdecl
+#define EMU_INLINE __inline
+
+#define uint8 unsigned char
+#define uint16 unsigned short
+#define uint32 unsigned int
+#define uint64 unsigned __int64
+#define sint8 signed char
+#define sint16 signed short
+#define sint32 signed int
+#define sint64 signed __int64
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// LINUX / other platform definitions
+//
+/////////////////////////////////////////////////////////////////////////////
+#else
+
+//#if defined(__GNUC__) && defined(__i386__)
+//#define EMU_CALL __attribute__((__regparm__(2)))
+//#else
+#define EMU_CALL
+//#endif
+
+#define EMU_CALL_
+#define EMU_INLINE __inline
+
+#ifdef HAVE_STDINT_H
+#include
+#define uint8 uint8_t
+#define uint16 uint16_t
+#define uint32 uint32_t
+#define uint64 uint64_t
+#define sint8 int8_t
+#define sint16 int16_t
+#define sint32 int32_t
+#define sint64 int64_t
+#else
+#define uint8 unsigned char
+#define uint16 unsigned short
+#define uint32 unsigned int
+#define uint64 unsigned long long
+#define sint8 signed char
+#define sint16 signed short
+#define sint32 signed int
+#define sint64 signed long long
+#endif
+
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/Frameworks/GME/gme/fm.c b/Frameworks/GME/gme/fm.c
new file mode 100644
index 000000000..f069f396e
--- /dev/null
+++ b/Frameworks/GME/gme/fm.c
@@ -0,0 +1,4501 @@
+#define YM2610B_WARNING
+
+/*
+**
+** File: fm.c -- software implementation of Yamaha FM sound generator
+**
+** Copyright Jarek Burczynski (bujar at mame dot net)
+** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+**
+** Version 1.4.2 (final beta)
+**
+*/
+
+/*
+** History:
+**
+** 2006-2008 Eke-Eke (Genesis Plus GX), MAME backport by R. Belmont.
+** - implemented PG overflow, aka "detune bug" (Ariel, Comix Zone, Shaq Fu, Spiderman,...), credits to Nemesis
+** - fixed SSG-EG support, credits to Nemesis and additional fixes from Alone Coder
+** - modified EG rates and frequency, tested by Nemesis on real hardware
+** - implemented LFO phase update for CH3 special mode (Warlock birds, Alladin bug sound)
+** - fixed Attack Rate update (Batman & Robin intro)
+** - fixed attenuation level at the start of Substain (Gynoug explosions)
+** - fixed EG decay->substain transition to handle special cases, like SL=0 and Decay rate is very slow (Mega Turrican tracks 03,09...)
+**
+** 06-23-2007 Zsolt Vasvari:
+** - changed the timing not to require the use of floating point calculations
+**
+** 03-08-2003 Jarek Burczynski:
+** - fixed YM2608 initial values (after the reset)
+** - fixed flag and irqmask handling (YM2608)
+** - fixed BUFRDY flag handling (YM2608)
+**
+** 14-06-2003 Jarek Burczynski:
+** - implemented all of the YM2608 status register flags
+** - implemented support for external memory read/write via YM2608
+** - implemented support for deltat memory limit register in YM2608 emulation
+**
+** 22-05-2003 Jarek Burczynski:
+** - fixed LFO PM calculations (copy&paste bugfix)
+**
+** 08-05-2003 Jarek Burczynski:
+** - fixed SSG support
+**
+** 22-04-2003 Jarek Burczynski:
+** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608)
+**
+** 15-04-2003 Jarek Burczynski:
+** - added support for YM2608's register 0x110 - status mask
+**
+** 01-12-2002 Jarek Burczynski:
+** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608)
+** The addressing patch used for early Neo-Geo games can be removed now.
+**
+** 26-11-2002 Jarek Burczynski, Nicola Salmoria:
+** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to:
+** - added emulation of YM2608 drums.
+** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608)
+**
+** 16-08-2002 Jarek Burczynski:
+** - binary exact Envelope Generator (verified on real YM2203);
+** identical to YM2151
+** - corrected 'off by one' error in feedback calculations (when feedback is off)
+** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610)
+**
+** 18-12-2001 Jarek Burczynski:
+** - added SSG-EG support (verified on real YM2203)
+**
+** 12-08-2001 Jarek Burczynski:
+** - corrected sin_tab and tl_tab data (verified on real chip)
+** - corrected feedback calculations (verified on real chip)
+** - corrected phase generator calculations (verified on real chip)
+** - corrected envelope generator calculations (verified on real chip)
+** - corrected FM volume level (YM2610 and YM2610B).
+** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) :
+** this was needed to calculate YM2610 FM channels output correctly.
+** (Each FM channel is calculated as in other chips, but the output of the channel
+** gets shifted right by one *before* sending to accumulator. That was impossible to do
+** with previous implementation).
+**
+** 23-07-2001 Jarek Burczynski, Nicola Salmoria:
+** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip)
+**
+** 11-06-2001 Jarek Burczynski:
+** - corrected end of sample bug in ADPCMA_calc_cha().
+** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits).
+**
+** 08-12-98 hiro-shi:
+** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA
+** move ROM limit check.(CALC_CH? -> 2610Write1/2)
+** test program (ADPCMB_TEST)
+** move ADPCM A/B end check.
+** ADPCMB repeat flag(no check)
+** change ADPCM volume rate (8->16) (32->48).
+**
+** 09-12-98 hiro-shi:
+** change ADPCM volume. (8->16, 48->64)
+** replace ym2610 ch0/3 (YM-2610B)
+** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff.
+** add ADPCM_SHIFT_MASK
+** change ADPCMA_DECODE_MIN/MAX.
+*/
+
+
+
+
+/************************************************************************/
+/* comment of hiro-shi(Hiromitsu Shioya) */
+/* YM2610(B) = OPN-B */
+/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */
+/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */
+/************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include "mathdefs.h"
+
+
+#include "mamedef.h"
+//#ifndef __RAINE__
+//#include "sndintrf.h" /* use M.A.M.E. */
+//#else
+//#include "deftypes.h" /* use RAINE */
+//#include "support.h" /* use RAINE */
+//#endif
+#include "fm.h"
+
+
+/* include external DELTA-T unit (when needed) */
+#if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B)
+ #include "ymdeltat.h"
+#endif
+
+/* shared function building option */
+#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B)
+#define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608)
+
+
+/* globals */
+#define TYPE_SSG 0x01 /* SSG support */
+#define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */
+#define TYPE_6CH 0x04 /* FM 6CH / 3CH */
+#define TYPE_DAC 0x08 /* YM2612's DAC device */
+#define TYPE_ADPCM 0x10 /* two ADPCM units */
+#define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */
+
+
+#define TYPE_YM2203 (TYPE_SSG)
+#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM)
+#define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610)
+
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<>3)
+
+/* sin waveform table in 'decibel' scale */
+static unsigned int sin_tab[SIN_LEN];
+
+/* sustain level table (3dB per step) */
+/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */
+/* 1, 2, 4, 8, 16, 32, 64 (value)*/
+/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/
+
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) )
+static const UINT32 sl_table[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const UINT8 eg_inc[19*RATE_STEPS]={
+
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */
+/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */
+/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */
+/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */
+
+/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */
+/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */
+/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(17) in this table - it's directly in the code */
+static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */
+/* 32 infinite time rates */
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+
+/* rates 00-11 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 12 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 13 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 14 */
+O(12),O(13),O(14),O(15),
+
+/* rate 15 */
+O(16),O(16),O(16),O(16),
+
+/* 32 dummy rates (same as 15 3) */
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16)
+
+};
+
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/
+/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */
+/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */
+/* 32 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-11 */
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 12 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 32 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0)
+
+};
+#undef O
+
+static const UINT8 dt_tab[4 * 32]={
+/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/
+/* FD=0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* FD=1 */
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
+/* FD=2 */
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
+ 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16,
+/* FD=3 */
+ 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
+ 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22
+};
+
+
+/* OPN key frequency number -> key code follow table */
+/* fnum higher 4bit -> keycode lower 2bit */
+static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3};
+
+
+/* 8 LFO speed parameters */
+/* each value represents number of samples that one LFO level will last for */
+static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5};
+
+
+
+/*There are 4 different LFO AM depths available, they are:
+ 0 dB, 1.4 dB, 5.9 dB, 11.8 dB
+ Here is how it is generated (in EG steps):
+
+ 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0
+ 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0
+ 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0
+
+ (1.4 dB is loosing precision as you can see)
+
+ It's implemented as generator from 0..126 with step 2 then a shift
+ right N times, where N is:
+ 8 for 0 dB
+ 3 for 1.4 dB
+ 1 for 5.9 dB
+ 0 for 11.8 dB
+*/
+static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0};
+
+
+
+/*There are 8 different LFO PM depths available, they are:
+ 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents)
+
+ Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10
+ (bits 8,9,10 = FNUM MSB from OCT/FNUM register)
+
+ Here we store only first quarter (positive one) of full waveform.
+ Full table (lfo_pm_table) containing all 128 waveforms is build
+ at run (init) time.
+
+ One value in table below represents 4 (four) basic LFO steps
+ (1 PM step = 4 AM steps).
+
+ For example:
+ at LFO SPEED=0 (which is 108 samples per basic LFO step)
+ one value from "lfo_pm_output" table lasts for 432 consecutive
+ samples (4*108=432) and one full LFO waveform cycle lasts for 13824
+ samples (32*432=13824; 32 because we store only a quarter of whole
+ waveform in the table below)
+*/
+static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */
+/* FNUM BIT 4: 000 0001xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1},
+
+/* FNUM BIT 5: 000 0010xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3},
+
+/* FNUM BIT 6: 000 0100xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1},
+/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3},
+/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6},
+
+/* FNUM BIT 7: 000 1000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1},
+/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2},
+/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3},
+/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6},
+/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+
+/* FNUM BIT 8: 001 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2},
+/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3},
+/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4},
+/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6},
+/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+
+/* FNUM BIT 9: 010 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2},
+/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4},
+/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6},
+/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8},
+/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
+
+/* FNUM BIT10: 100 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4},
+/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8},
+/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc},
+/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10},
+/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
+/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60},
+
+};
+
+/* all 128 LFO PM waveforms */
+static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */
+
+
+
+
+
+/* register number to channel number , slot offset */
+#define OPN_CHAN(N) (N&3)
+#define OPN_SLOT(N) ((N>>2)&3)
+
+/* slot number */
+#define SLOT1 0
+#define SLOT2 2
+#define SLOT3 1
+#define SLOT4 3
+
+/* bit0 = Right enable , bit1 = Left enable */
+#define OUTD_RIGHT 1
+#define OUTD_LEFT 2
+#define OUTD_CENTER 3
+
+
+/* save output as raw 16-bit sample */
+/* #define SAVE_SAMPLE */
+
+#ifdef SAVE_SAMPLE
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = rt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+
+/* struct describing a single operator (SLOT) */
+typedef struct
+{
+ INT32 *DT; /* detune :dt_tab[DT] */
+ UINT8 KSR; /* key scale rate :3-KSR */
+ UINT32 ar; /* attack rate */
+ UINT32 d1r; /* decay rate */
+ UINT32 d2r; /* sustain rate */
+ UINT32 rr; /* release rate */
+ UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */
+ UINT32 mul; /* multiple :ML_TABLE[ML] */
+
+ /* Phase Generator */
+ UINT32 phase; /* phase counter */
+ INT32 Incr; /* phase step */
+
+ /* Envelope Generator */
+ UINT8 state; /* phase type */
+ UINT32 tl; /* total level: TL << 3 */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level:sl_table[SL] */
+ UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */
+
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_d1r; /* (decay state) */
+ UINT8 eg_sel_d1r; /* (decay state) */
+ UINT8 eg_sh_d2r; /* (sustain state) */
+ UINT8 eg_sel_d2r; /* (sustain state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+
+ UINT8 ssg; /* SSG-EG waveform */
+ UINT8 ssgn; /* SSG-EG negated output */
+
+ UINT32 key; /* 0=last key was KEY OFF, 1=KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* AM enable flag */
+
+} FM_SLOT;
+
+typedef struct
+{
+ FM_SLOT SLOT[4]; /* four SLOTs (operators) */
+
+ UINT8 ALGO; /* algorithm */
+ UINT8 FB; /* feedback shift */
+ INT32 op1_out[2]; /* op1 output for feedback */
+
+ INT32 *connect1; /* SLOT1 output pointer */
+ INT32 *connect3; /* SLOT3 output pointer */
+ INT32 *connect2; /* SLOT2 output pointer */
+ INT32 *connect4; /* SLOT4 output pointer */
+
+ INT32 *mem_connect;/* where to put the delayed sample (MEM) */
+ INT32 mem_value; /* delayed sample (MEM) value */
+
+ INT32 pms; /* channel PMS */
+ UINT8 ams; /* channel AMS */
+
+ UINT32 fc; /* fnum,blk:adjusted to sample rate */
+ UINT8 kcode; /* key code: */
+ UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
+ UINT8 Muted;
+} FM_CH;
+
+
+typedef struct
+{
+ //const device_config *device;
+ void * param; /* this chip parameter */
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ int timer_prescaler; /* timer prescaler */
+#if FM_BUSY_FLAG_SUPPORT
+ TIME_TYPE busy_expiry_time; /* expiry time of the busy status */
+#endif
+ UINT8 address; /* address register */
+ UINT8 irq; /* interrupt level */
+ UINT8 irqmask; /* irq mask */
+ UINT8 status; /* status flag */
+ UINT32 mode; /* mode CSM / 3SLOT */
+ UINT8 prescaler_sel; /* prescaler selector */
+ UINT8 fn_h; /* freq latch */
+ INT32 TA; /* timer a */
+ INT32 TAC; /* timer a counter */
+ UINT8 TB; /* timer b */
+ INT32 TBC; /* timer b counter */
+ /* local time tables */
+ INT32 dt_tab[8][32]; /* DeTune table */
+ /* Extention Timer and IRQ handler */
+ const ssg_callbacks *SSG;
+} FM_ST;
+
+
+
+/***********************************************************/
+/* OPN unit */
+/***********************************************************/
+
+/* OPN 3slot struct */
+typedef struct
+{
+ UINT32 fc[3]; /* fnum3,blk3: calculated */
+ UINT8 fn_h; /* freq3 latch */
+ UINT8 kcode[3]; /* key code */
+ UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
+} FM_3SLOT;
+
+/* OPN/A/B common state */
+typedef struct
+{
+ UINT8 type; /* chip type */
+ FM_ST ST; /* general state */
+ FM_3SLOT SL3; /* 3 slot mode state */
+ FM_CH *P_CH; /* pointer of CH */
+ unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */
+
+
+ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers
+ but LFO works with one more bit of a precision so we really need 4096 elements */
+
+ UINT32 fn_table[4096]; /* fnumber->increment counter */
+ UINT32 fn_max; /* maximal phase increment (used for phase overflow) */
+
+ /* LFO */
+ UINT32 LFO_AM; /* runtime LFO calculations helper */
+ INT32 LFO_PM; /* runtime LFO calculations helper */
+
+ UINT32 lfo_cnt;
+ UINT32 lfo_inc;
+
+ UINT32 lfo_freq[8]; /* LFO FREQ table */
+
+ INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */
+ INT32 mem; /* one sample delay memory */
+
+ INT32 out_fm[8]; /* outputs of working channels */
+
+#if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B)
+ INT32 out_adpcm[4]; /* channel output NONE,LEFT,RIGHT or CENTER for YM2608/YM2610 ADPCM */
+ INT32 out_delta[4]; /* channel output NONE,LEFT,RIGHT or CENTER for YM2608/YM2610 DELTAT*/
+#endif
+} FM_OPN;
+
+
+
+/* log output level */
+#define LOG_ERR 3 /* ERROR */
+#define LOG_WAR 2 /* WARNING */
+#define LOG_INF 1 /* INFORMATION */
+#define LOG_LEVEL LOG_INF
+
+#ifndef __RAINE__
+#define LOG(n,x) do { if( (n)>=LOG_LEVEL ) logerror x; } while (0)
+#endif
+
+/* limitter */
+#define Limit(val, max,min) { \
+ if ( val > max ) val = max; \
+ else if ( val < min ) val = min; \
+}
+
+
+/* status set and IRQ handling */
+INLINE void FM_STATUS_SET(FM_ST *ST,int flag)
+{
+ /* set status flag */
+ ST->status |= flag;
+ if ( !(ST->irq) && (ST->status & ST->irqmask) )
+ {
+ ST->irq = 1;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void FM_STATUS_RESET(FM_ST *ST,int flag)
+{
+ /* reset status flag */
+ ST->status &=~flag;
+ if ( (ST->irq) && !(ST->status & ST->irqmask) )
+ {
+ ST->irq = 0;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ }
+}
+
+/* IRQ mask set */
+INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag)
+{
+ ST->irqmask = flag;
+ /* IRQ handling check */
+ FM_STATUS_SET(ST,0);
+ FM_STATUS_RESET(ST,0);
+}
+
+/* OPN Mode Register Write */
+INLINE void set_timers( FM_ST *ST, void *n, int v )
+{
+ /* b7 = CSM MODE */
+ /* b6 = 3 slot mode */
+ /* b5 = reset b */
+ /* b4 = reset a */
+ /* b3 = timer enable b */
+ /* b2 = timer enable a */
+ /* b1 = load b */
+ /* b0 = load a */
+ ST->mode = v;
+
+ /* reset Timer b flag */
+ if( v & 0x20 )
+ FM_STATUS_RESET(ST,0x02);
+ /* reset Timer a flag */
+ if( v & 0x10 )
+ FM_STATUS_RESET(ST,0x01);
+ /* load b */
+ if( v & 0x02 )
+ {
+ if( ST->TBC == 0 )
+ {
+ ST->TBC = ( 256-ST->TB)<<4;
+ /* External timer handler */
+ }
+ }
+ else
+ { /* stop timer b */
+ if( ST->TBC != 0 )
+ {
+ ST->TBC = 0;
+ }
+ }
+ /* load a */
+ if( v & 0x01 )
+ {
+ if( ST->TAC == 0 )
+ {
+ ST->TAC = (1024-ST->TA);
+ /* External timer handler */
+ }
+ }
+ else
+ { /* stop timer a */
+ if( ST->TAC != 0 )
+ {
+ ST->TAC = 0;
+ }
+ }
+}
+
+
+/* Timer A Overflow */
+INLINE void TimerAOver(FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01);
+ /* clear or reload the counter */
+ ST->TAC = (1024-ST->TA);
+}
+/* Timer B Overflow */
+INLINE void TimerBOver(FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02);
+ /* clear or reload the counter */
+ ST->TBC = ( 256-ST->TB)<<4;
+}
+
+
+#if FM_INTERNAL_TIMER
+/* ----- internal timer mode , update timer */
+
+/* ---------- calculate timer A ---------- */
+ #define INTERNAL_TIMER_A(ST,CSM_CH) \
+ { \
+ if( (ST)->TAC && (1) ) \
+ if( ((ST)->TAC -= (int)((ST)->freqbase*4096)) <= 0 ) \
+ { \
+ TimerAOver( ST ); \
+ /* CSM mode total level latch and auto key on */ \
+ if( (ST)->mode & 0x80 ) \
+ CSMKeyControll( OPN->type, CSM_CH ); \
+ } \
+ }
+/* ---------- calculate timer B ---------- */
+ #define INTERNAL_TIMER_B(ST,step) \
+ { \
+ if( (ST)->TBC && (1) ) \
+ if( ((ST)->TBC -= (int)((ST)->freqbase*4096*step)) <= 0 ) \
+ TimerBOver( ST ); \
+ }
+#else /* FM_INTERNAL_TIMER */
+/* external timer mode */
+#define INTERNAL_TIMER_A(ST,CSM_CH)
+#define INTERNAL_TIMER_B(ST,step)
+#endif /* FM_INTERNAL_TIMER */
+
+
+
+#if FM_BUSY_FLAG_SUPPORT
+#define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME)
+INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST)
+{
+ if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 )
+ {
+ if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(ST->device->machine)) > 0)
+ return ST->status | 0x80; /* with busy */
+ /* expire */
+ FM_BUSY_CLEAR(ST);
+ }
+ return ST->status;
+}
+INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock )
+{
+ TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(ATTOTIME_IN_HZ(ST->clock), busyclock * ST->timer_prescaler);
+ ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(ST->device->machine), expiry_period);
+}
+#else
+#define FM_STATUS_FLAG(ST) ((ST)->status)
+#define FM_BUSY_SET(ST,bclock) {}
+#define FM_BUSY_CLEAR(ST) {}
+#endif
+
+
+
+
+INLINE void FM_KEYON(UINT8 type, FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+ if( !SLOT->key )
+ {
+ SLOT->key = 1;
+ SLOT->phase = 0; /* restart Phase Generator */
+ SLOT->ssgn = (SLOT->ssg & 0x04) >> 1;
+ SLOT->state = EG_ATT;
+ }
+}
+
+INLINE void FM_KEYOFF(FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+ if( SLOT->key )
+ {
+ SLOT->key = 0;
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;/* phase -> Release */
+ }
+}
+
+/* set algorithm connection */
+static void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch )
+{
+ INT32 *carrier = &OPN->out_fm[ch];
+
+ INT32 **om1 = &CH->connect1;
+ INT32 **om2 = &CH->connect3;
+ INT32 **oc1 = &CH->connect2;
+
+ INT32 **memc = &CH->mem_connect;
+
+ switch( CH->ALGO )
+ {
+ case 0:
+ /* M1---C1---MEM---M2---C2---OUT */
+ *om1 = &OPN->c1;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 1:
+ /* M1------+-MEM---M2---C2---OUT */
+ /* C1-+ */
+ *om1 = &OPN->mem;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 2:
+ /* M1-----------------+-C2---OUT */
+ /* C1---MEM---M2-+ */
+ *om1 = &OPN->c2;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 3:
+ /* M1---C1---MEM------+-C2---OUT */
+ /* M2-+ */
+ *om1 = &OPN->c1;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->c2;
+ break;
+ case 4:
+ /* M1---C1-+-OUT */
+ /* M2---C2-+ */
+ /* MEM: not used */
+ *om1 = &OPN->c1;
+ *oc1 = carrier;
+ *om2 = &OPN->c2;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ case 5:
+ /* +----C1----+ */
+ /* M1-+-MEM---M2-+-OUT */
+ /* +----C2----+ */
+ *om1 = 0; /* special mark */
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->m2;
+ break;
+ case 6:
+ /* M1---C1-+ */
+ /* M2-+-OUT */
+ /* C2-+ */
+ /* MEM: not used */
+ *om1 = &OPN->c1;
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ case 7:
+ /* M1-+ */
+ /* C1-+-OUT */
+ /* M2-+ */
+ /* C2-+ */
+ /* MEM: not used*/
+ *om1 = carrier;
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ }
+
+ CH->connect4 = carrier;
+}
+
+/* set detune & multiple */
+INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v)
+{
+ SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1;
+ SLOT->DT = ST->dt_tab[(v>>4)&7];
+ CH->SLOT[SLOT1].Incr=-1;
+}
+
+/* set total level */
+INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v)
+{
+ SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */
+}
+
+/* set attack rate & key scale */
+INLINE void set_ar_ksr(UINT8 type, FM_CH *CH,FM_SLOT *SLOT,int v)
+{
+ UINT8 old_KSR = SLOT->KSR;
+
+ SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->KSR = 3-(v>>6);
+ if (SLOT->KSR != old_KSR)
+ {
+ CH->SLOT[SLOT1].Incr=-1;
+ }
+
+ /* refresh Attack rate */
+ if ((SLOT->ar + SLOT->ksr) < 32+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 17*RATE_STEPS;
+ }
+}
+
+/* set decay rate */
+INLINE void set_dr(UINT8 type, FM_SLOT *SLOT,int v)
+{
+ SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr];
+}
+
+/* set sustain rate */
+INLINE void set_sr(UINT8 type, FM_SLOT *SLOT,int v)
+{
+ SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr];
+}
+
+/* set release rate */
+INLINE void set_sl_rr(UINT8 type, FM_SLOT *SLOT,int v)
+{
+ SLOT->sl = sl_table[ v>>4 ];
+
+ SLOT->rr = 34 + ((v&0x0f)<<2);
+
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr];
+}
+
+
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(FM_OPN *OPN)
+{
+ UINT8 pos;
+
+ if (OPN->lfo_inc) /* LFO enabled ? */
+ {
+ OPN->lfo_cnt += OPN->lfo_inc;
+
+ pos = (OPN->lfo_cnt >> LFO_SH) & 127;
+
+
+ /* update AM when LFO output changes */
+
+ /* actually I can't optimize is this way without rewritting chan_calc()
+ to use chip->lfo_am instead of global lfo_am */
+ {
+
+ /* triangle */
+ /* AM: 0 to 126 step +2, 126 to 0 step -2 */
+ if (pos<64)
+ OPN->LFO_AM = (pos&63) * 2;
+ else
+ OPN->LFO_AM = 126 - ((pos&63) * 2);
+ }
+
+ /* PM works with 4 times slower clock */
+ pos >>= 2;
+ /* update PM when LFO output changes */
+ /*if (prev_pos != pos)*/ /* can't use global lfo_pm for this optimization, must be chip->lfo_pm instead*/
+ {
+ OPN->LFO_PM = pos;
+ }
+
+ }
+ else
+ {
+ OPN->LFO_AM = 0;
+ OPN->LFO_PM = 0;
+ }
+}
+
+/* changed from INLINE to static here to work around gcc 4.2.1 codegen bug */
+static void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT)
+{
+ unsigned int out;
+ unsigned int swap_flag = 0;
+ unsigned int i;
+
+
+ i = 4; /* four operators per channel */
+ do
+ {
+ /* reset SSG-EG swap flag */
+ swap_flag = 0;
+
+ switch(SLOT->state)
+ {
+ case EG_ATT: /* attack phase */
+ if ( !(OPN->eg_cnt & ((1<eg_sh_ar)-1) ) )
+ {
+ SLOT->volume += (~SLOT->volume *
+ (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)])
+ ) >>4;
+
+ if (SLOT->volume <= MIN_ATT_INDEX)
+ {
+ SLOT->volume = MIN_ATT_INDEX;
+ SLOT->state = EG_DEC;
+ }
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ {
+ if (SLOT->ssg&0x08) /* SSG EG type envelope selected */
+ {
+ if ( !(OPN->eg_cnt & ((1<eg_sh_d1r)-1) ) )
+ {
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)];
+
+ if ( SLOT->volume >= (INT32)(SLOT->sl) )
+ SLOT->state = EG_SUS;
+ }
+ }
+ else
+ {
+ if ( !(OPN->eg_cnt & ((1<eg_sh_d1r)-1) ) )
+ {
+ SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)];
+
+ if ( SLOT->volume >= (INT32)(SLOT->sl) )
+ SLOT->state = EG_SUS;
+ }
+ }
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+ if (SLOT->ssg&0x08) /* SSG EG type envelope selected */
+ {
+ if ( !(OPN->eg_cnt & ((1<eg_sh_d2r)-1) ) )
+ {
+
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)];
+
+ if ( SLOT->volume >= ENV_QUIET )
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+
+ if (SLOT->ssg&0x01) /* bit 0 = hold */
+ {
+ if (SLOT->ssgn&1) /* have we swapped once ??? */
+ {
+ /* yes, so do nothing, just hold current level */
+ }
+ else
+ swap_flag = (SLOT->ssg&0x02) | 1 ; /* bit 1 = alternate */
+
+ }
+ else
+ {
+ /* same as KEY-ON operation */
+
+ /* restart of the Phase Generator should be here */
+ SLOT->phase = 0;
+
+ {
+ /* phase -> Attack */
+ SLOT->volume = 511;
+ SLOT->state = EG_ATT;
+ }
+
+ swap_flag = (SLOT->ssg&0x02); /* bit 1 = alternate */
+ }
+ }
+ }
+ }
+ else
+ {
+ if ( !(OPN->eg_cnt & ((1<eg_sh_d2r)-1) ) )
+ {
+ SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)];
+
+ if ( SLOT->volume >= MAX_ATT_INDEX )
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ /* do not change SLOT->state (verified on real chip) */
+ }
+ }
+
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if ( !(OPN->eg_cnt & ((1<eg_sh_rr)-1) ) )
+ {
+ /* SSG-EG affects Release phase also (Nemesis) */
+ SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)];
+
+ if ( SLOT->volume >= MAX_ATT_INDEX )
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+ }
+ break;
+
+ }
+
+
+ out = ((UINT32)SLOT->volume);
+
+ /* negate output (changes come from alternate bit, init comes from attack bit) */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL))
+ out ^= MAX_ATT_INDEX;
+
+ /* we need to store the result here because we are going to change ssgn
+ in next instruction */
+ SLOT->vol_out = out + SLOT->tl;
+
+ /* reverse SLOT inversion flag */
+ SLOT->ssgn ^= swap_flag;
+
+ SLOT++;
+ i--;
+ }while (i);
+
+}
+
+
+
+#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask))
+
+INLINE void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum)
+{
+ UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
+ INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ UINT8 blk;
+ UINT32 fn;
+ int kc, fc;
+
+ block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
+
+ blk = (block_fnum&0x7000) >> 12;
+ fn = block_fnum & 0xfff;
+
+ /* keyscale code */
+ kc = (blk<<2) | opn_fktable[fn >> 8];
+
+ /* phase increment counter */
+ fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc];
+
+ /* detects frequency overflow (credits to Nemesis) */
+ if (fc < 0) fc += OPN->fn_max;
+
+ /* update phase */
+ SLOT->phase += (fc * SLOT->mul) >> 1;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ SLOT->phase += SLOT->Incr;
+ }
+}
+
+INLINE void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH)
+{
+ UINT32 block_fnum = CH->block_fnum;
+
+ UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
+ INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ UINT8 blk;
+ UINT32 fn;
+ int kc, fc, finc;
+
+ block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
+
+ blk = (block_fnum&0x7000) >> 12;
+ fn = block_fnum & 0xfff;
+
+ /* keyscale code */
+ kc = (blk<<2) | opn_fktable[fn >> 8];
+
+ /* phase increment counter */
+ fc = (OPN->fn_table[fn]>>(7-blk));
+
+ /* detects frequency overflow (credits to Nemesis) */
+ finc = fc + CH->SLOT[SLOT1].DT[kc];
+
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT2].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT3].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT4].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
+ CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
+ CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
+ CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
+ }
+}
+
+INLINE void chan_calc(FM_OPN *OPN, FM_CH *CH, int chnum)
+{
+ unsigned int eg_out;
+
+ UINT32 AM = OPN->LFO_AM >> CH->ams;
+
+ if (CH->Muted)
+ return;
+
+
+ OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0;
+
+ *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
+
+ eg_out = volume_calc(&CH->SLOT[SLOT1]);
+ {
+ INT32 out = CH->op1_out[0] + CH->op1_out[1];
+ CH->op1_out[0] = CH->op1_out[1];
+
+ if( !CH->connect1 )
+ {
+ /* algorithm 5 */
+ OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0];
+ }
+ else
+ {
+ /* other algorithms */
+ *CH->connect1 += CH->op1_out[0];
+ }
+
+ CH->op1_out[1] = 0;
+ if( eg_out < ENV_QUIET ) /* SLOT 1 */
+ {
+ if (!CH->FB)
+ out=0;
+
+ CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<FB) );
+ }
+ }
+
+ eg_out = volume_calc(&CH->SLOT[SLOT3]);
+ if( eg_out < ENV_QUIET ) /* SLOT 3 */
+ *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2);
+
+ eg_out = volume_calc(&CH->SLOT[SLOT2]);
+ if( eg_out < ENV_QUIET ) /* SLOT 2 */
+ *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1);
+
+ eg_out = volume_calc(&CH->SLOT[SLOT4]);
+ if( eg_out < ENV_QUIET ) /* SLOT 4 */
+ *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2);
+
+
+ /* store current MEM */
+ CH->mem_value = OPN->mem;
+
+ /* update phase counters AFTER output calculations */
+ if(CH->pms)
+ {
+ /* add support for 3 slot mode */
+ if ((OPN->ST.mode & 0xC0) && (chnum == 2))
+ {
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum);
+ }
+ else update_phase_lfo_channel(OPN, CH);
+ }
+ else /* no LFO phase modulation */
+ {
+ CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
+ CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
+ CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
+ CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
+ }
+}
+
+/* update phase increment and envelope generator */
+INLINE void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc )
+{
+ int ksr = kc >> SLOT->KSR;
+
+ fc += SLOT->DT[kc];
+
+ /* detects frequency overflow (credits to Nemesis) */
+ if (fc < 0) fc += OPN->fn_max;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = (fc * SLOT->mul) >> 1;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 32+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 17*RATE_STEPS;
+ }
+
+ SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
+
+ SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr];
+ }
+}
+
+/* update phase increment counters */
+/* Changed from INLINE to static to work around gcc 4.2.1 codegen bug */
+static void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH )
+{
+ if( CH->SLOT[SLOT1].Incr==-1)
+ {
+ int fc = CH->fc;
+ int kc = CH->kcode;
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc );
+ }
+}
+
+/* initialize time tables */
+static void init_timetables( FM_ST *ST , const UINT8 *dttable )
+{
+ int i,d;
+ double rate;
+
+#if 0
+ logerror("FM.C: samplerate=%8i chip clock=%8i freqbase=%f \n",
+ ST->rate, ST->clock, ST->freqbase );
+#endif
+
+ /* DeTune table */
+ for (d = 0;d <= 3;d++)
+ {
+ for (i = 0;i <= 31;i++)
+ {
+ rate = ((double)dttable[d*32 + i]) * SIN_LEN * ST->freqbase * (1<dt_tab[d][i] = (INT32) rate;
+ ST->dt_tab[d+4][i] = -ST->dt_tab[d][i];
+#if 0
+ logerror("FM.C: DT [%2i %2i] = %8x \n", d, i, ST->dt_tab[d][i] );
+#endif
+ }
+ }
+
+}
+
+
+static void reset_channels( FM_ST *ST , FM_CH *CH , int num )
+{
+ int c,s;
+
+ ST->mode = 0; /* normal mode */
+ ST->TA = 0;
+ ST->TAC = 0;
+ ST->TB = 0;
+ ST->TBC = 0;
+
+ for( c = 0 ; c < num ; c++ )
+ {
+ //memset(&CH[c], 0x00, sizeof(FM_CH));
+ CH[c].mem_value = 0;
+ CH[c].op1_out[0] = 0;
+ CH[c].op1_out[1] = 0;
+ CH[c].fc = 0;
+ for(s = 0 ; s < 4 ; s++ )
+ {
+ //memset(&CH[c].SLOT[s], 0x00, sizeof(FM_SLOT));
+ CH[c].SLOT[s].Incr = -1;
+ CH[c].SLOT[s].key = 0;
+ CH[c].SLOT[s].phase = 0;
+ CH[c].SLOT[s].ssg = 0;
+ CH[c].SLOT[s].ssgn = 0;
+ CH[c].SLOT[s].state= EG_OFF;
+ CH[c].SLOT[s].volume = MAX_ATT_INDEX;
+ CH[c].SLOT[s].vol_out= MAX_ATT_INDEX;
+ }
+ }
+}
+
+/* initialize generic tables */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+ for (x=0; x>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 2; /* 13 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<13; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x);
+ for (i=0; i<13; i++)
+ logerror(", [%02i] %4x", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ]);
+ logerror("\n");
+ #endif
+ }
+ /*logerror("FM.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+
+
+ for (i=0; i0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+ /*logerror("FM.C: sin [%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[i],tl_tab[sin_tab[i]]);*/
+ }
+
+ /*logerror("FM.C: ENV_QUIET= %08x\n",ENV_QUIET );*/
+
+
+ /* build LFO PM modulation table */
+ for(i = 0; i < 8; i++) /* 8 PM depths */
+ {
+ UINT8 fnum;
+ for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */
+ {
+ UINT8 value;
+ UINT8 step;
+ UINT32 offset_depth = i;
+ UINT32 offset_fnum_bit;
+ UINT32 bit_tmp;
+
+ for (step=0; step<8; step++)
+ {
+ value = 0;
+ for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */
+ {
+ if (fnum & (1<SLOT[SLOT1].key)
+ {
+ FM_KEYON(type, CH,SLOT1);
+ FM_KEYOFF(CH, SLOT1);
+ }
+ if (!CH->SLOT[SLOT2].key)
+ {
+ FM_KEYON(type, CH,SLOT2);
+ FM_KEYOFF(CH, SLOT2);
+ }
+ if (!CH->SLOT[SLOT3].key)
+ {
+ FM_KEYON(type, CH,SLOT3);
+ FM_KEYOFF(CH, SLOT3);
+ }
+ if (!CH->SLOT[SLOT4].key)
+ {
+ FM_KEYON(type, CH,SLOT4);
+ FM_KEYOFF(CH, SLOT4);
+ }
+}
+
+#ifdef __STATE_H__
+/* FM channel save , internal state only */
+static void FMsave_state_channel(const device_config *device,FM_CH *CH,int num_ch)
+{
+ int slot , ch;
+
+ for(ch=0;chop1_out);
+ state_save_register_device_item(device, ch, CH->fc);
+ /* slots */
+ for(slot=0;slot<4;slot++)
+ {
+ FM_SLOT *SLOT = &CH->SLOT[slot];
+ state_save_register_device_item(device, ch * 4 + slot, SLOT->phase);
+ state_save_register_device_item(device, ch * 4 + slot, SLOT->state);
+ state_save_register_device_item(device, ch * 4 + slot, SLOT->volume);
+ }
+ }
+}
+
+static void FMsave_state_st(const device_config *device,FM_ST *ST)
+{
+#if FM_BUSY_FLAG_SUPPORT
+ state_save_register_device_item(device, 0, ST->busy_expiry_time.seconds );
+ state_save_register_device_item(device, 0, ST->busy_expiry_time.attoseconds );
+#endif
+ state_save_register_device_item(device, 0, ST->address );
+ state_save_register_device_item(device, 0, ST->irq );
+ state_save_register_device_item(device, 0, ST->irqmask );
+ state_save_register_device_item(device, 0, ST->status );
+ state_save_register_device_item(device, 0, ST->mode );
+ state_save_register_device_item(device, 0, ST->prescaler_sel );
+ state_save_register_device_item(device, 0, ST->fn_h );
+ state_save_register_device_item(device, 0, ST->TA );
+ state_save_register_device_item(device, 0, ST->TAC );
+ state_save_register_device_item(device, 0, ST->TB );
+ state_save_register_device_item(device, 0, ST->TBC );
+}
+#endif /* _STATE_H */
+
+#if BUILD_OPN
+
+
+
+/* prescaler set (and make time tables) */
+static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres)
+{
+ int i;
+
+ /* frequency base */
+ OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0;
+
+#if 0
+ OPN->ST.rate = (double)OPN->ST.clock / pres;
+ OPN->ST.freqbase = 1.0;
+#endif
+
+ OPN->eg_timer_add = (1<ST.freqbase;
+ OPN->eg_timer_overflow = ( 3 ) * (1<ST.timer_prescaler = timer_prescaler;
+
+ /* SSG part prescaler set */
+ if( SSGpres ) (*OPN->ST.SSG->set_clock)( OPN->ST.param, OPN->ST.clock * 2 / SSGpres );
+
+ /* make time tables */
+ init_timetables( &OPN->ST, dt_tab );
+
+ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers
+ but LFO works with one more bit of a precision so we really need 4096 elements */
+ /* calculate fnumber -> increment counter table */
+ for(i = 0; i < 4096; i++)
+ {
+ /* freq table for octave 7 */
+ /* OPN phase increment counter = 20bit */
+ OPN->fn_table[i] = (UINT32)( (double)i * 32 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+#if 0
+ logerror("FM.C: fn_table[%4i] = %08x (dec=%8i)\n",
+ i, OPN->fn_table[i]>>6,OPN->fn_table[i]>>6 );
+#endif
+ }
+
+ /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */
+ OPN->fn_max = (UINT32)( (double)0x20000 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) );
+
+ /* LFO freq. table */
+ for(i = 0; i < 8; i++)
+ {
+ /* Amplitude modulation: 64 output levels (triangle waveform); 1 level lasts for one of "lfo_samples_per_step" samples */
+ /* Phase modulation: one entry from lfo_pm_output lasts for one of 4 * "lfo_samples_per_step" samples */
+ OPN->lfo_freq[i] = (1.0 / lfo_samples_per_step[i]) * (1<ST.freqbase;
+#if 0
+ logerror("FM.C: lfo_freq[%i] = %08x (dec=%8i)\n",
+ i, OPN->lfo_freq[i],OPN->lfo_freq[i] );
+#endif
+ }
+}
+
+
+
+/* write a OPN mode register 0x20-0x2f */
+static void OPNWriteMode(FM_OPN *OPN, int r, int v)
+{
+ UINT8 c;
+ FM_CH *CH;
+
+ switch(r)
+ {
+ case 0x21: /* Test */
+ break;
+ case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */
+ if( OPN->type & TYPE_LFOPAN )
+ {
+ if (v&0x08) /* LFO enabled ? */
+ {
+ OPN->lfo_inc = OPN->lfo_freq[v&7];
+ }
+ else
+ {
+ OPN->lfo_inc = 0;
+ }
+ }
+ break;
+ case 0x24: /* timer A High 8*/
+ OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2);
+ break;
+ case 0x25: /* timer A Low 2*/
+ OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3);
+ break;
+ case 0x26: /* timer B */
+ OPN->ST.TB = v;
+ break;
+ case 0x27: /* mode, timer control */
+ set_timers( &(OPN->ST),OPN->ST.param,v );
+ break;
+ case 0x28: /* key on / off */
+ c = v & 0x03;
+ if( c == 3 ) break;
+ if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3;
+ CH = OPN->P_CH;
+ CH = &CH[c];
+ if(v&0x10) FM_KEYON(OPN->type,CH,SLOT1); else FM_KEYOFF(CH,SLOT1);
+ if(v&0x20) FM_KEYON(OPN->type,CH,SLOT2); else FM_KEYOFF(CH,SLOT2);
+ if(v&0x40) FM_KEYON(OPN->type,CH,SLOT3); else FM_KEYOFF(CH,SLOT3);
+ if(v&0x80) FM_KEYON(OPN->type,CH,SLOT4); else FM_KEYOFF(CH,SLOT4);
+ break;
+ }
+}
+
+/* write a OPN register (0x30-0xff) */
+static void OPNWriteReg(FM_OPN *OPN, int r, int v)
+{
+ FM_CH *CH;
+ FM_SLOT *SLOT;
+
+ UINT8 c = OPN_CHAN(r);
+
+ if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */
+
+ if (r >= 0x100) c+=3;
+
+ CH = OPN->P_CH;
+ CH = &CH[c];
+
+ SLOT = &(CH->SLOT[OPN_SLOT(r)]);
+
+ switch( r & 0xf0 )
+ {
+ case 0x30: /* DET , MUL */
+ set_det_mul(&OPN->ST,CH,SLOT,v);
+ break;
+
+ case 0x40: /* TL */
+ set_tl(CH,SLOT,v);
+ break;
+
+ case 0x50: /* KS, AR */
+ set_ar_ksr(OPN->type,CH,SLOT,v);
+ break;
+
+ case 0x60: /* bit7 = AM ENABLE, DR */
+ set_dr(OPN->type, SLOT,v);
+
+ if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */
+ {
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ }
+ break;
+
+ case 0x70: /* SR */
+ set_sr(OPN->type,SLOT,v);
+ break;
+
+ case 0x80: /* SL, RR */
+ set_sl_rr(OPN->type,SLOT,v);
+ break;
+
+ case 0x90: /* SSG-EG */
+ SLOT->ssg = v&0x0f;
+ SLOT->ssgn = (v&0x04)>>1; /* bit 1 in ssgn = attack */
+
+ /* SSG-EG envelope shapes :
+
+ E AtAlH
+ 1 0 0 0 \\\\
+
+ 1 0 0 1 \___
+
+ 1 0 1 0 \/\/
+ ___
+ 1 0 1 1 \
+
+ 1 1 0 0 ////
+ ___
+ 1 1 0 1 /
+
+ 1 1 1 0 /\/\
+
+ 1 1 1 1 /___
+
+
+ E = SSG-EG enable
+
+
+ The shapes are generated using Attack, Decay and Sustain phases.
+
+ Each single character in the diagrams above represents this whole
+ sequence:
+
+ - when KEY-ON = 1, normal Attack phase is generated (*without* any
+ difference when compared to normal mode),
+
+ - later, when envelope level reaches minimum level (max volume),
+ the EG switches to Decay phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - later when envelope level passes the SL level,
+ the EG swithes to Sustain phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - finally when envelope level reaches maximum level (min volume),
+ the EG switches to Attack phase again (depends on actual waveform).
+
+ Important is that when switch to Attack phase occurs, the phase counter
+ of that operator will be zeroed-out (as in normal KEY-ON) but not always.
+ (I havent found the rule for that - perhaps only when the output level is low)
+
+ The difference (when compared to normal Envelope Generator mode) is
+ that the resolution in Decay and Sustain phases is 4 times lower;
+ this results in only 256 steps instead of normal 1024.
+ In other words:
+ when SSG-EG is disabled, the step inside of the EG is one,
+ when SSG-EG is enabled, the step is four (in Decay and Sustain phases).
+
+ Times between the level changes are the same in both modes.
+
+
+ Important:
+ Decay 1 Level (so called SL) is compared to actual SSG-EG output, so
+ it is the same in both SSG and no-SSG modes, with this exception:
+
+ when the SSG-EG is enabled and is generating raising levels
+ (when the EG output is inverted) the SL will be found at wrong level !!!
+ For example, when SL=02:
+ 0 -6 = -6dB in non-inverted EG output
+ 96-6 = -90dB in inverted EG output
+ Which means that EG compares its level to SL as usual, and that the
+ output is simply inverted afterall.
+
+
+ The Yamaha's manuals say that AR should be set to 0x1f (max speed).
+ That is not necessary, but then EG will be generating Attack phase.
+
+ */
+
+
+ break;
+
+ case 0xa0:
+ switch( OPN_SLOT(r) )
+ {
+ case 0: /* 0xa0-0xa2 : FNUM1 */
+ {
+ UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v;
+ UINT8 blk = OPN->ST.fn_h>>3;
+ /* keyscale code */
+ CH->kcode = (blk<<2) | opn_fktable[fn >> 7];
+ /* phase increment counter */
+ CH->fc = OPN->fn_table[fn*2]>>(7-blk);
+
+ /* store fnum in clear form for LFO PM calculations */
+ CH->block_fnum = (blk<<11) | fn;
+
+ CH->SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 1: /* 0xa4-0xa6 : FNUM2,BLK */
+ OPN->ST.fn_h = v&0x3f;
+ break;
+ case 2: /* 0xa8-0xaa : 3CH FNUM1 */
+ if(r < 0x100)
+ {
+ UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v;
+ UINT8 blk = OPN->SL3.fn_h>>3;
+ /* keyscale code */
+ OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7];
+ /* phase increment counter */
+ OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk);
+ OPN->SL3.block_fnum[c] = (blk<<11) | fn;
+ (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 3: /* 0xac-0xae : 3CH FNUM2,BLK */
+ if(r < 0x100)
+ OPN->SL3.fn_h = v&0x3f;
+ break;
+ }
+ break;
+
+ case 0xb0:
+ switch( OPN_SLOT(r) )
+ {
+ case 0: /* 0xb0-0xb2 : FB,ALGO */
+ {
+ int feedback = (v>>3)&7;
+ CH->ALGO = v&7;
+ CH->FB = feedback ? feedback+6 : 0;
+ setup_connection( OPN, CH, c );
+ }
+ break;
+ case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */
+ if( OPN->type & TYPE_LFOPAN)
+ {
+ /* b0-2 PMS */
+ CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */
+
+ /* b4-5 AMS */
+ CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03];
+
+ /* PAN : b7 = L, b6 = R */
+ OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0;
+ OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0;
+
+ }
+ break;
+ }
+ break;
+ }
+}
+
+#endif /* BUILD_OPN */
+
+#if BUILD_OPN_PRESCALER
+/*
+ prescaler circuit (best guess to verified chip behaviour)
+
+ +--------------+ +-sel2-+
+ | +--|in20 |
+ +---+ | +-sel1-+ | |
+M-CLK -+-|1/2|-+--|in10 | +---+ | out|--INT_CLOCK
+ | +---+ | out|-|1/3|-|in21 |
+ +----------|in11 | +---+ +------+
+ +------+
+
+reg.2d : sel2 = in21 (select sel2)
+reg.2e : sel1 = in11 (select sel1)
+reg.2f : sel1 = in10 , sel2 = in20 (clear selector)
+reset : sel1 = in11 , sel2 = in21 (clear both)
+
+*/
+static void OPNPrescaler_w(FM_OPN *OPN , int addr, int pre_divider)
+{
+ static const int opn_pres[4] = { 2*12 , 2*12 , 6*12 , 3*12 };
+ static const int ssg_pres[4] = { 1 , 1 , 4 , 2 };
+ int sel;
+
+ switch(addr)
+ {
+ case 0: /* when reset */
+ OPN->ST.prescaler_sel = 2;
+ break;
+ case 1: /* when postload */
+ break;
+ case 0x2d: /* divider sel : select 1/1 for 1/3line */
+ OPN->ST.prescaler_sel |= 0x02;
+ break;
+ case 0x2e: /* divider sel , select 1/3line for output */
+ OPN->ST.prescaler_sel |= 0x01;
+ break;
+ case 0x2f: /* divider sel , clear both selector to 1/2,1/2 */
+ OPN->ST.prescaler_sel = 0;
+ break;
+ }
+ sel = OPN->ST.prescaler_sel & 3;
+ /* update prescaler */
+ OPNSetPres( OPN, opn_pres[sel]*pre_divider,
+ opn_pres[sel]*pre_divider,
+ ssg_pres[sel]*pre_divider );
+}
+#endif /* BUILD_OPN_PRESCALER */
+
+#if BUILD_YM2203
+/*****************************************************************************/
+/* YM2203 local section */
+/*****************************************************************************/
+
+/* here's the virtual YM2203(OPN) */
+typedef struct
+{
+ UINT8 REGS[256]; /* registers */
+ FM_OPN OPN; /* OPN state */
+ FM_CH CH[3]; /* channel state */
+} YM2203;
+
+/* Generate samples for one of the YM2203s */
+void ym2203_update_one(void *chip, FMSAMPLE **buffer, int length)
+{
+ YM2203 *F2203 = (YM2203 *)chip;
+ FM_OPN *OPN = &F2203->OPN;
+ int i;
+ FMSAMPLE *bufL = buffer[0];
+ FMSAMPLE *bufR = buffer[1];
+ FM_CH *cch[3];
+
+ cch[0] = &F2203->CH[0];
+ cch[1] = &F2203->CH[1];
+ cch[2] = &F2203->CH[2];
+
+
+ /* refresh PG and EG */
+ refresh_fc_eg_chan( OPN, cch[0] );
+ refresh_fc_eg_chan( OPN, cch[1] );
+ if( (F2203->OPN.ST.mode & 0xc0) )
+ {
+ /* 3SLOT MODE */
+ if( cch[2]->SLOT[SLOT1].Incr==-1)
+ {
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode );
+ }
+ }
+ else
+ refresh_fc_eg_chan( OPN, cch[2] );
+
+
+ /* YM2203 doesn't have LFO so we must keep these globals at 0 level */
+ OPN->LFO_AM = 0;
+ OPN->LFO_PM = 0;
+
+ /* buffering */
+ for (i=0; i < length ; i++)
+ {
+ /* clear outputs */
+ OPN->out_fm[0] = 0;
+ OPN->out_fm[1] = 0;
+ OPN->out_fm[2] = 0;
+
+ /* advance envelope generator */
+ OPN->eg_timer += OPN->eg_timer_add;
+ while (OPN->eg_timer >= OPN->eg_timer_overflow)
+ {
+ OPN->eg_timer -= OPN->eg_timer_overflow;
+ OPN->eg_cnt++;
+
+ advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]);
+ }
+
+ /* calculate FM */
+ chan_calc(OPN, cch[0], 0 );
+ chan_calc(OPN, cch[1], 1 );
+ chan_calc(OPN, cch[2], 2 );
+
+ /* buffering */
+ {
+ int lt;
+
+ lt = OPN->out_fm[0] + OPN->out_fm[1] + OPN->out_fm[2];
+
+ lt >>= FINAL_SH;
+
+ //Limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ SAVE_ALL_CHANNELS
+ #endif
+
+ /* buffering */
+ bufL[i] = lt;
+ bufR[i] = lt;
+ }
+
+ /* timer A control */
+ INTERNAL_TIMER_A( &F2203->OPN.ST , cch[2] )
+ }
+ INTERNAL_TIMER_B(&F2203->OPN.ST,length)
+}
+
+/* ---------- reset one of chip ---------- */
+void ym2203_reset_chip(void *chip)
+{
+ int i;
+ YM2203 *F2203 = (YM2203 *)chip;
+ FM_OPN *OPN = &F2203->OPN;
+
+ /* Reset Prescaler */
+ OPNPrescaler_w(OPN, 0 , 1 );
+ /* reset SSG section */
+ (*OPN->ST.SSG->reset)(OPN->ST.param);
+ /* status clear */
+ FM_IRQMASK_SET(&OPN->ST,0x03);
+ FM_BUSY_CLEAR(&OPN->ST);
+ OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */
+
+ OPN->eg_timer = 0;
+ OPN->eg_cnt = 0;
+
+ FM_STATUS_RESET(&OPN->ST, 0xff);
+
+ reset_channels( &OPN->ST , F2203->CH , 3 );
+ /* reset OPerator paramater */
+ for(i = 0xb2 ; i >= 0x30 ; i-- ) OPNWriteReg(OPN,i,0);
+ for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
+}
+
+#ifdef __STATE_H__
+static void YM2203_save_state(YM2203 *F2203, const device_config *device)
+{
+ state_save_register_device_item_array(device, 0, F2203->REGS);
+ FMsave_state_st(device,&F2203->OPN.ST);
+ FMsave_state_channel(device,F2203->CH,3);
+ /* 3slots */
+ state_save_register_device_item_array (device, 0, F2203->OPN.SL3.fc);
+ state_save_register_device_item (device, 0, F2203->OPN.SL3.fn_h);
+ state_save_register_device_item_array (device, 0, F2203->OPN.SL3.kcode);
+}
+#endif /* _STATE_H */
+
+/* ---------- Initialize YM2203 emulator(s) ----------
+ 'num' is the number of virtual YM2203s to allocate
+ 'clock' is the chip clock in Hz
+ 'rate' is sampling rate
+*/
+//void * ym2203_init(void *param, const device_config *device, int clock, int rate,
+// FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg)
+void * ym2203_init(void *param, int clock, int rate, const ssg_callbacks *ssg)
+{
+ YM2203 *F2203;
+
+ /* allocate ym2203 state space */
+ if( (F2203 = (YM2203 *)malloc(sizeof(YM2203)))==NULL)
+ return NULL;
+ /* clear */
+ memset(F2203,0,sizeof(YM2203));
+
+ if( !init_tables() )
+ {
+ free( F2203 );
+ return NULL;
+ }
+
+ F2203->OPN.ST.param = param;
+ F2203->OPN.type = TYPE_YM2203;
+ F2203->OPN.P_CH = F2203->CH;
+ //F2203->OPN.ST.device = device;
+ F2203->OPN.ST.clock = clock;
+ F2203->OPN.ST.rate = rate;
+
+ F2203->OPN.ST.SSG = ssg;
+
+#ifdef __STATE_H__
+ YM2203_save_state(F2203, device);
+#endif
+ return F2203;
+}
+
+/* shut down emulator */
+void ym2203_shutdown(void *chip)
+{
+ YM2203 *FM2203 = (YM2203 *)chip;
+
+ FMCloseTable();
+ free(FM2203);
+}
+
+/* YM2203 I/O interface */
+int ym2203_write(void *chip,int a,UINT8 v)
+{
+ YM2203 *F2203 = (YM2203 *)chip;
+ FM_OPN *OPN = &F2203->OPN;
+
+ if( !(a&1) )
+ { /* address port */
+ OPN->ST.address = (v &= 0xff);
+
+ /* Write register to SSG emulator */
+ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v);
+
+ /* prescaler select : 2d,2e,2f */
+ if( v >= 0x2d && v <= 0x2f )
+ OPNPrescaler_w(OPN , v , 1);
+ }
+ else
+ { /* data port */
+ int addr = OPN->ST.address;
+ F2203->REGS[addr] = v;
+ switch( addr & 0xf0 )
+ {
+ case 0x00: /* 0x00-0x0f : SSG section */
+ /* Write data to SSG emulator */
+ (*OPN->ST.SSG->write)(OPN->ST.param,a,v);
+ break;
+ case 0x20: /* 0x20-0x2f : Mode section */
+ /* write register */
+ OPNWriteMode(OPN,addr,v);
+ break;
+ default: /* 0x30-0xff : OPN section */
+ /* write register */
+ OPNWriteReg(OPN,addr,v);
+ }
+ FM_BUSY_SET(&OPN->ST,1);
+ }
+ return OPN->ST.irq;
+}
+
+UINT8 ym2203_read(void *chip,int a)
+{
+ YM2203 *F2203 = (YM2203 *)chip;
+ int addr = F2203->OPN.ST.address;
+ UINT8 ret = 0;
+
+ if( !(a&1) )
+ { /* status port */
+ ret = FM_STATUS_FLAG(&F2203->OPN.ST);
+ }
+ else
+ { /* data port (only SSG) */
+ if( addr < 16 ) ret = (*F2203->OPN.ST.SSG->read)(F2203->OPN.ST.param);
+ }
+ return ret;
+}
+
+int ym2203_timer_over(void *chip,int c)
+{
+ YM2203 *F2203 = (YM2203 *)chip;
+
+ if( c )
+ { /* Timer B */
+ TimerBOver( &(F2203->OPN.ST) );
+ }
+ else
+ { /* Timer A */
+ /* timer update */
+ TimerAOver( &(F2203->OPN.ST) );
+ /* CSM mode key,TL control */
+ if( F2203->OPN.ST.mode & 0x80 )
+ { /* CSM mode auto key on */
+ CSMKeyControll( F2203->OPN.type, &(F2203->CH[2]) );
+ }
+ }
+ return F2203->OPN.ST.irq;
+}
+
+void ym2203_set_mutemask(void *chip, UINT32 MuteMask)
+{
+ YM2203 *F2203 = (YM2203 *)chip;
+ UINT8 CurChn;
+
+ for (CurChn = 0; CurChn < 3; CurChn ++)
+ F2203->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
+
+ return;
+}
+#endif /* BUILD_YM2203 */
+
+
+
+#if (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B)
+
+/* ADPCM type A channel struct */
+typedef struct
+{
+ UINT8 flag; /* port state */
+ UINT8 flagMask; /* arrived flag mask */
+ UINT8 now_data; /* current ROM data */
+ UINT32 now_addr; /* current ROM address */
+ UINT32 now_step;
+ UINT32 step;
+ UINT32 start; /* sample data start address*/
+ UINT32 end; /* sample data end address */
+ UINT8 IL; /* Instrument Level */
+ INT32 adpcm_acc; /* accumulator */
+ INT32 adpcm_step; /* step */
+ INT32 adpcm_out; /* (speedup) hiro-shi!! */
+ INT8 vol_mul; /* volume in "0.75dB" steps */
+ UINT8 vol_shift; /* volume in "-6dB" steps */
+ INT32 *pan; /* &out_adpcm[OPN_xxxx] */
+ UINT8 Muted;
+} ADPCM_CH;
+
+/* here's the virtual YM2610 */
+typedef struct
+{
+ UINT8 REGS[512]; /* registers */
+ FM_OPN OPN; /* OPN state */
+ FM_CH CH[6]; /* channel state */
+ UINT8 addr_A1; /* address line A1 */
+
+ /* ADPCM-A unit */
+ //const UINT8 *pcmbuf; /* pcm rom buffer */
+ UINT8 *pcmbuf; /* pcm rom buffer */
+ UINT32 pcm_size; /* size of pcm rom */
+ UINT8 adpcmTL; /* adpcmA total level */
+ ADPCM_CH adpcm[6]; /* adpcm channels */
+ UINT32 adpcmreg[0x30]; /* registers */
+ UINT8 adpcm_arrivedEndAddress;
+ YM_DELTAT deltaT; /* Delta-T ADPCM unit */
+ UINT8 MuteDeltaT;
+
+ UINT8 flagmask; /* YM2608 only */
+ UINT8 irqmask; /* YM2608 only */
+} YM2610;
+
+/* here is the virtual YM2608 */
+typedef YM2610 YM2608;
+
+
+/**** YM2610 ADPCM defines ****/
+#define ADPCM_SHIFT (16) /* frequency step rate */
+#define ADPCMA_ADDRESS_SHIFT 8 /* adpcm A address shift */
+
+/* Algorithm and tables verified on real YM2608 and YM2610 */
+
+/* usual ADPCM table (16 * 1.1^N) */
+static const int steps[49] =
+{
+ 16, 17, 19, 21, 23, 25, 28,
+ 31, 34, 37, 41, 45, 50, 55,
+ 60, 66, 73, 80, 88, 97, 107,
+ 118, 130, 143, 157, 173, 190, 209,
+ 230, 253, 279, 307, 337, 371, 408,
+ 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552
+};
+
+/* different from the usual ADPCM table */
+static const int step_inc[8] = { -1*16, -1*16, -1*16, -1*16, 2*16, 5*16, 7*16, 9*16 };
+
+/* speedup purposes only */
+static int jedi_table[ 49*16 ];
+
+
+static void Init_ADPCMATable(void)
+{
+ int step, nib;
+
+ for (step = 0; step < 49; step++)
+ {
+ /* loop over all nibbles and compute the difference */
+ for (nib = 0; nib < 16; nib++)
+ {
+ int value = (2*(nib & 0x07) + 1) * steps[step] / 8;
+ jedi_table[step*16 + nib] = (nib&0x08) ? -value : value;
+ }
+ }
+}
+
+/* ADPCM A (Non control type) : calculate one channel output */
+INLINE void ADPCMA_calc_chan( YM2610 *F2610, ADPCM_CH *ch )
+{
+ UINT32 step;
+ UINT8 data;
+
+ if (ch->Muted)
+ return;
+
+
+ ch->now_step += ch->step;
+ if ( ch->now_step >= (1<now_step >> ADPCM_SHIFT;
+ ch->now_step &= (1< instead of == */
+ /* YM2610 checks lower 20 bits only, the 4 MSB bits are sample bank */
+ /* Here we use 1<<21 to compensate for nibble calculations */
+
+ if ( (ch->now_addr & ((1<<21)-1)) == ((ch->end<<1) & ((1<<21)-1)) )
+ {
+ ch->flag = 0;
+ F2610->adpcm_arrivedEndAddress |= ch->flagMask;
+ return;
+ }
+#if 0
+ if ( ch->now_addr > (F2610->pcmsizeA<<1) )
+ {
+#ifdef _DEBUG
+ LOG(LOG_WAR,("YM2610: Attempting to play past adpcm rom size!\n" ));
+#endif
+ return;
+ }
+#endif
+ if ( ch->now_addr&1 )
+ data = ch->now_data & 0x0f;
+ else
+ {
+ ch->now_data = *(F2610->pcmbuf+(ch->now_addr>>1));
+ data = (ch->now_data >> 4) & 0x0f;
+ }
+
+ ch->now_addr++;
+
+ ch->adpcm_acc += jedi_table[ch->adpcm_step + data];
+
+ /* extend 12-bit signed int */
+ if (ch->adpcm_acc & ~0x7ff)
+ ch->adpcm_acc |= ~0xfff;
+ else
+ ch->adpcm_acc &= 0xfff;
+
+ ch->adpcm_step += step_inc[data & 7];
+ Limit( ch->adpcm_step, 48*16, 0*16 );
+
+ }while(--step);
+
+ /* calc pcm * volume data */
+ ch->adpcm_out = ((ch->adpcm_acc * ch->vol_mul) >> ch->vol_shift) & ~3; /* multiply, shift and mask out 2 LSB bits */
+ }
+
+ /* output for work of output channels (out_adpcm[OPNxxxx])*/
+ *(ch->pan) += ch->adpcm_out;
+}
+
+/* ADPCM type A Write */
+static void FM_ADPCMAWrite(YM2610 *F2610,int r,int v)
+{
+ ADPCM_CH *adpcm = F2610->adpcm;
+ UINT8 c = r&0x07;
+
+ F2610->adpcmreg[r] = v&0xff; /* stock data */
+ switch( r )
+ {
+ case 0x00: /* DM,--,C5,C4,C3,C2,C1,C0 */
+ if( !(v&0x80) )
+ {
+ /* KEY ON */
+ for( c = 0; c < 6; c++ )
+ {
+ if( (v>>c)&1 )
+ {
+ /**** start adpcm ****/
+ adpcm[c].step = (UINT32)((float)(1<OPN.ST.freqbase)/3.0);
+ adpcm[c].now_addr = adpcm[c].start<<1;
+ adpcm[c].now_step = 0;
+ adpcm[c].adpcm_acc = 0;
+ adpcm[c].adpcm_step= 0;
+ adpcm[c].adpcm_out = 0;
+ adpcm[c].flag = 1;
+
+ if(F2610->pcmbuf==NULL)
+ { /* Check ROM Mapped */
+#ifdef _DEBUG
+ logerror("YM2608-YM2610: ADPCM-A rom not mapped\n");
+#endif
+ adpcm[c].flag = 0;
+ }
+ else
+ {
+ if(adpcm[c].end >= F2610->pcm_size)
+ { /* Check End in Range */
+#ifdef _DEBUG
+ logerror("YM2610: ADPCM-A end out of range: $%08x\n",adpcm[c].end);
+#endif
+ /*adpcm[c].end = F2610->pcm_size-1;*/ /* JB: DO NOT uncomment this, otherwise you will break the comparison in the ADPCM_CALC_CHA() */
+ }
+ if(adpcm[c].start >= F2610->pcm_size) /* Check Start in Range */
+ {
+#ifdef _DEBUG
+ logerror("YM2608-YM2610: ADPCM-A start out of range: $%08x\n",adpcm[c].start);
+#endif
+ adpcm[c].flag = 0;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* KEY OFF */
+ for( c = 0; c < 6; c++ )
+ if( (v>>c)&1 )
+ adpcm[c].flag = 0;
+ }
+ break;
+ case 0x01: /* B0-5 = TL */
+ F2610->adpcmTL = (v & 0x3f) ^ 0x3f;
+ for( c = 0; c < 6; c++ )
+ {
+ int volume = F2610->adpcmTL + adpcm[c].IL;
+
+ if ( volume >= 63 ) /* This is correct, 63 = quiet */
+ {
+ adpcm[c].vol_mul = 0;
+ adpcm[c].vol_shift = 0;
+ }
+ else
+ {
+ adpcm[c].vol_mul = 15 - (volume & 7); /* so called 0.75 dB */
+ adpcm[c].vol_shift = 1 + (volume >> 3); /* Yamaha engineers used the approximation: each -6 dB is close to divide by two (shift right) */
+ }
+
+ /* calc pcm * volume data */
+ adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc * adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask out low 2 bits */
+ }
+ break;
+ default:
+ c = r&0x07;
+ if( c >= 0x06 ) return;
+ switch( r&0x38 )
+ {
+ case 0x08: /* B7=L,B6=R, B4-0=IL */
+ {
+ int volume;
+
+ adpcm[c].IL = (v & 0x1f) ^ 0x1f;
+
+ volume = F2610->adpcmTL + adpcm[c].IL;
+
+ if ( volume >= 63 ) /* This is correct, 63 = quiet */
+ {
+ adpcm[c].vol_mul = 0;
+ adpcm[c].vol_shift = 0;
+ }
+ else
+ {
+ adpcm[c].vol_mul = 15 - (volume & 7); /* so called 0.75 dB */
+ adpcm[c].vol_shift = 1 + (volume >> 3); /* Yamaha engineers used the approximation: each -6 dB is close to divide by two (shift right) */
+ }
+
+ adpcm[c].pan = &F2610->OPN.out_adpcm[(v>>6)&0x03];
+
+ /* calc pcm * volume data */
+ adpcm[c].adpcm_out = ((adpcm[c].adpcm_acc * adpcm[c].vol_mul) >> adpcm[c].vol_shift) & ~3; /* multiply, shift and mask out low 2 bits */
+ }
+ break;
+ case 0x10:
+ case 0x18:
+ adpcm[c].start = ( (F2610->adpcmreg[0x18 + c]*0x0100 | F2610->adpcmreg[0x10 + c]) << ADPCMA_ADDRESS_SHIFT);
+ break;
+ case 0x20:
+ case 0x28:
+ adpcm[c].end = ( (F2610->adpcmreg[0x28 + c]*0x0100 | F2610->adpcmreg[0x20 + c]) << ADPCMA_ADDRESS_SHIFT);
+ adpcm[c].end += (1<flag);
+ state_save_register_device_item(device, ch, adpcm->now_data);
+ state_save_register_device_item(device, ch, adpcm->now_addr);
+ state_save_register_device_item(device, ch, adpcm->now_step);
+ state_save_register_device_item(device, ch, adpcm->adpcm_acc);
+ state_save_register_device_item(device, ch, adpcm->adpcm_step);
+ state_save_register_device_item(device, ch, adpcm->adpcm_out);
+ }
+}
+#endif /* _STATE_H */
+
+#endif /* (BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B) */
+
+
+#if BUILD_YM2608
+/*****************************************************************************/
+/* YM2608 local section */
+/*****************************************************************************/
+
+
+
+static const unsigned int YM2608_ADPCM_ROM_addr[2*6] = {
+0x0000, 0x01bf, /* bass drum */
+0x01c0, 0x043f, /* snare drum */
+0x0440, 0x1b7f, /* top cymbal */
+0x1b80, 0x1cff, /* high hat */
+0x1d00, 0x1f7f, /* tom tom */
+0x1f80, 0x1fff /* rim shot */
+};
+
+
+/*
+ This data is derived from the chip's output - internal ROM can't be read.
+ It was verified, using real YM2608, that this ADPCM stream produces 100% correct output signal.
+*/
+
+static const unsigned char YM2608_ADPCM_ROM[0x2000] = {
+
+/* Source: 01BD.ROM */
+/* Length: 448 / 0x000001C0 */
+
+0x88,0x08,0x08,0x08,0x00,0x88,0x16,0x76,0x99,0xB8,0x22,0x3A,0x84,0x3C,0xB1,0x54,
+0x10,0xA9,0x98,0x32,0x80,0x33,0x9A,0xA7,0x4A,0xB4,0x58,0xBC,0x15,0x29,0x8A,0x97,
+0x9B,0x44,0xAC,0x80,0x12,0xDE,0x13,0x1B,0xC0,0x58,0xC8,0x11,0x0A,0xA2,0x1A,0xA0,
+0x00,0x98,0x0B,0x93,0x9E,0x92,0x0A,0x88,0xBE,0x14,0x1B,0x98,0x08,0xA1,0x4A,0xC1,
+0x30,0xD9,0x33,0x98,0x10,0x89,0x17,0x1A,0x82,0x29,0x37,0x0C,0x83,0x50,0x9A,0x24,
+0x1A,0x83,0x10,0x23,0x19,0xB3,0x72,0x8A,0x16,0x10,0x0A,0x93,0x70,0x99,0x23,0x99,
+0x02,0x20,0x91,0x18,0x02,0x41,0xAB,0x24,0x18,0x81,0x99,0x4A,0xE8,0x28,0x9A,0x99,
+0xA1,0x2F,0xA8,0x9D,0x90,0x08,0xCC,0xA3,0x1D,0xCA,0x82,0x0B,0xD8,0x08,0xB9,0x09,
+0xBC,0xB8,0x00,0xBE,0x90,0x1B,0xCA,0x00,0x9B,0x8A,0xA8,0x91,0x0F,0xB3,0x3D,0xB8,
+0x31,0x0B,0xA5,0x0A,0x11,0xA1,0x48,0x92,0x10,0x50,0x91,0x30,0x23,0x09,0x37,0x39,
+0xA2,0x72,0x89,0x92,0x30,0x83,0x1C,0x96,0x28,0xB9,0x24,0x8C,0xA1,0x31,0xAD,0xA9,
+0x13,0x9C,0xBA,0xA8,0x0B,0xBF,0xB8,0x9B,0xCA,0x88,0xDB,0xB8,0x19,0xFC,0x92,0x0A,
+0xBA,0x89,0xAB,0xB8,0xAB,0xD8,0x08,0xAD,0xBA,0x33,0x9D,0xAA,0x83,0x3A,0xC0,0x40,
+0xB9,0x15,0x39,0xA2,0x52,0x89,0x02,0x63,0x88,0x13,0x23,0x03,0x52,0x02,0x54,0x00,
+0x11,0x23,0x23,0x35,0x20,0x01,0x44,0x41,0x80,0x24,0x40,0xA9,0x45,0x19,0x81,0x12,
+0x81,0x02,0x11,0x21,0x19,0x02,0x61,0x8A,0x13,0x3A,0x10,0x12,0x23,0x8B,0x37,0x18,
+0x91,0x24,0x10,0x81,0x34,0x20,0x05,0x32,0x82,0x53,0x20,0x14,0x33,0x31,0x34,0x52,
+0x00,0x43,0x32,0x13,0x52,0x22,0x13,0x52,0x11,0x43,0x11,0x32,0x32,0x32,0x22,0x02,
+0x13,0x12,0x89,0x22,0x19,0x81,0x81,0x08,0xA8,0x08,0x8B,0x90,0x1B,0xBA,0x8A,0x9B,
+0xB9,0x89,0xCA,0xB9,0xAB,0xCA,0x9B,0xCA,0xB9,0xAB,0xDA,0x99,0xAC,0xBB,0x9B,0xAC,
+0xAA,0xBA,0xAC,0xAB,0x9A,0xAA,0xAA,0xBA,0xB8,0xA9,0xBA,0x99,0xA9,0x9A,0xA0,0x8A,
+0xA9,0x08,0x8A,0xA9,0x00,0x99,0x89,0x88,0x98,0x08,0x99,0x00,0x89,0x80,0x08,0x98,
+0x00,0x88,0x88,0x80,0x90,0x80,0x90,0x80,0x81,0x99,0x08,0x88,0x99,0x09,0x00,0x1A,
+0xA8,0x10,0x9A,0x88,0x08,0x0A,0x8A,0x89,0x99,0xA8,0x98,0xA9,0x99,0x99,0xA9,0x99,
+0xAA,0x8A,0xAA,0x9B,0x8A,0x9A,0xA9,0x9A,0xBA,0x99,0x9A,0xAA,0x99,0x89,0xA9,0x99,
+0x98,0x9A,0x98,0x88,0x09,0x89,0x09,0x08,0x08,0x09,0x18,0x18,0x00,0x12,0x00,0x11,
+0x11,0x11,0x12,0x12,0x21,0x21,0x22,0x22,0x22,0x22,0x22,0x22,0x32,0x31,0x32,0x31,
+0x32,0x32,0x21,0x31,0x21,0x32,0x21,0x12,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,
+
+/* Source: 02SD.ROM */
+/* Length: 640 / 0x00000280 */
+
+0x0A,0xDC,0x14,0x0B,0xBA,0xBC,0x01,0x0F,0xF5,0x2F,0x87,0x19,0xC9,0x24,0x1B,0xA1,
+0x31,0x99,0x90,0x32,0x32,0xFE,0x83,0x48,0xA8,0xA9,0x23,0x19,0xBC,0x91,0x02,0x41,
+0xDE,0x81,0x28,0xA8,0x0A,0xB1,0x72,0xDA,0x23,0xBC,0x04,0x19,0xB8,0x21,0x8A,0x03,
+0x29,0xBA,0x14,0x21,0x0B,0xC0,0x43,0x08,0x91,0x50,0x93,0x0F,0x86,0x1A,0x91,0x18,
+0x21,0xCB,0x27,0x0A,0xA1,0x42,0x8C,0xA9,0x21,0x10,0x08,0xAB,0x94,0x2A,0xDA,0x02,
+0x8B,0x91,0x09,0x98,0xAE,0x80,0xA9,0x02,0x0A,0xE9,0x21,0xBB,0x15,0x20,0xBE,0x92,
+0x42,0x09,0xA9,0x11,0x34,0x08,0x12,0x0A,0x27,0x29,0xA1,0x52,0x12,0x8E,0x92,0x28,
+0x92,0x2B,0xD1,0x23,0xBF,0x81,0x10,0x99,0xA8,0x0A,0xC4,0x3B,0xB9,0xB0,0x00,0x62,
+0xCF,0x92,0x29,0x92,0x2B,0xB1,0x1C,0xB2,0x72,0xAA,0x88,0x11,0x18,0x80,0x13,0x9E,
+0x03,0x18,0xB0,0x60,0xA1,0x28,0x88,0x08,0x04,0x10,0x8F,0x96,0x19,0x90,0x01,0x09,
+0xC8,0x50,0x91,0x8A,0x01,0xAB,0x03,0x50,0xBA,0x9D,0x93,0x68,0xBA,0x80,0x22,0xCB,
+0x41,0xBC,0x92,0x60,0xB9,0x1A,0x95,0x4A,0xC8,0x20,0x88,0x33,0xAC,0x92,0x38,0x83,
+0x09,0x80,0x16,0x09,0x29,0xD0,0x54,0x8C,0xA2,0x28,0x91,0x89,0x93,0x60,0xCD,0x85,
+0x1B,0xA1,0x49,0x90,0x8A,0x80,0x34,0x0C,0xC9,0x14,0x19,0x98,0xA0,0x40,0xA9,0x21,
+0xD9,0x34,0x0A,0xA9,0x10,0x23,0xCB,0x25,0xAA,0x25,0x9B,0x13,0xCD,0x16,0x09,0xA0,
+0x80,0x01,0x19,0x90,0x88,0x21,0xAC,0x33,0x8B,0xD8,0x27,0x3B,0xB8,0x81,0x31,0x80,
+0xAF,0x97,0x0A,0x82,0x0A,0xA0,0x21,0x89,0x8A,0xA2,0x32,0x8D,0xBB,0x87,0x19,0x21,
+0xC9,0xBC,0x45,0x09,0x90,0x09,0xA1,0x24,0x1A,0xD0,0x10,0x08,0x11,0xA9,0x21,0xE8,
+0x60,0xA9,0x14,0x0C,0xD1,0x32,0xAB,0x04,0x0C,0x81,0x90,0x29,0x83,0x9B,0x01,0x8F,
+0x97,0x0B,0x82,0x18,0x88,0xBA,0x06,0x39,0xC8,0x23,0xBC,0x04,0x09,0x92,0x08,0x1A,
+0xBB,0x74,0x8C,0x81,0x18,0x81,0x9D,0x83,0x41,0xCD,0x81,0x40,0x9A,0x90,0x10,0x12,
+0x9C,0xA1,0x68,0xD8,0x33,0x9C,0x91,0x01,0x12,0xBE,0x02,0x09,0x12,0x99,0x9A,0x36,
+0x0A,0xB0,0x30,0x88,0xA3,0x2D,0x12,0xBC,0x03,0x3A,0x11,0xBD,0x08,0xC8,0x62,0x80,
+0x8B,0xD8,0x23,0x38,0xF9,0x12,0x08,0x99,0x91,0x21,0x99,0x85,0x2F,0xB2,0x30,0x90,
+0x88,0xD9,0x53,0xAC,0x82,0x19,0x91,0x20,0xCC,0x96,0x29,0xC9,0x24,0x89,0x80,0x99,
+0x12,0x08,0x18,0x88,0x99,0x23,0xAB,0x73,0xCB,0x33,0x9F,0x04,0x2B,0xB1,0x08,0x03,
+0x1B,0xC9,0x21,0x32,0xFA,0x33,0xDB,0x02,0x33,0xAE,0xB9,0x54,0x8B,0xA1,0x20,0x89,
+0x90,0x11,0x88,0x09,0x98,0x23,0xBE,0x37,0x8D,0x81,0x20,0xAA,0x34,0xBB,0x13,0x18,
+0xB9,0x40,0xB1,0x18,0x83,0x8E,0xB2,0x72,0xBC,0x82,0x30,0xA9,0x9A,0x24,0x8B,0x27,
+0x0E,0x91,0x20,0x90,0x08,0xB0,0x32,0xB9,0x21,0xB0,0xAC,0x45,0x9A,0xA1,0x50,0xA9,
+0x80,0x0A,0x26,0x9B,0x11,0xBB,0x23,0x71,0xCB,0x12,0x10,0xB8,0x40,0xA9,0xA5,0x39,
+0xC0,0x30,0xB2,0x20,0xAA,0xBA,0x76,0x1C,0xC1,0x48,0x98,0x80,0x18,0x81,0xAA,0x23,
+0x9C,0xA2,0x32,0xAC,0x9A,0x43,0x9C,0x12,0xAD,0x82,0x72,0xBC,0x00,0x82,0x39,0xD1,
+0x3A,0xB8,0x35,0x9B,0x10,0x40,0xF9,0x22,0x0A,0xC0,0x51,0xB9,0x82,0x18,0x98,0xA3,
+0x79,0xD0,0x20,0x88,0x09,0x01,0x99,0x82,0x11,0x38,0xFC,0x33,0x09,0xC8,0x40,0xA9,
+0x11,0x29,0xAA,0x94,0x3A,0xC2,0x4A,0xC0,0x89,0x52,0xBC,0x11,0x08,0x09,0xB8,0x71,
+0xA9,0x08,0xA8,0x62,0x8D,0x92,0x10,0x00,0x9E,0x94,0x38,0xBA,0x13,0x88,0x90,0x4A,
+0xE2,0x30,0xBA,0x02,0x00,0x19,0xD9,0x62,0xBB,0x04,0x0B,0xA3,0x68,0xB9,0x21,0x88,
+0x9D,0x04,0x10,0x8C,0xC8,0x62,0x99,0xAA,0x24,0x1A,0x80,0x9A,0x14,0x9B,0x26,0x8C,
+0x92,0x30,0xB9,0x09,0xA3,0x71,0xBB,0x10,0x19,0x82,0x39,0xDB,0x02,0x44,0x9F,0x10,
+
+/* Source: 04TOP.ROM */
+/* Length: 5952 / 0x00001740 */
+
+0x07,0xFF,0x7C,0x3C,0x31,0xC6,0xC4,0xBB,0x7F,0x7F,0x7B,0x82,0x8A,0x4D,0x5F,0x7C,
+0x3E,0x44,0xD2,0xB3,0xA0,0x19,0x1B,0x6C,0x81,0x28,0xC4,0xA1,0x1C,0x4B,0x18,0x00,
+0x2A,0xA2,0x0A,0x7C,0x2A,0x00,0x01,0x89,0x98,0x48,0x8A,0x3C,0x28,0x2A,0x5B,0x3E,
+0x3A,0x1A,0x3B,0x3D,0x4B,0x3B,0x4A,0x08,0x2A,0x1A,0x2C,0x4A,0x3B,0x82,0x99,0x3C,
+0x5D,0x29,0x2B,0x39,0x0B,0x23,0xAB,0x1A,0x4C,0x79,0xA3,0x01,0xC1,0x2A,0x0A,0x38,
+0xA7,0xB9,0x12,0x1F,0x29,0x08,0x82,0xA1,0x08,0xA9,0x42,0xAA,0x95,0xB3,0x90,0x81,
+0x09,0xD4,0x1A,0x80,0x1B,0x07,0xB8,0x12,0x8E,0x49,0x81,0x92,0xD3,0x90,0xA1,0x2A,
+0x02,0xE1,0xA3,0x99,0x02,0xB3,0x94,0xB3,0xB0,0xF4,0x98,0x93,0x90,0x13,0xE1,0x81,
+0x99,0x38,0x91,0xA6,0xD3,0x99,0x94,0xC1,0x83,0xB1,0x92,0x98,0x49,0xC4,0xB2,0xA4,
+0xA3,0xD0,0x1A,0x30,0xBA,0x59,0x02,0xD4,0xA0,0xA4,0xA2,0x8A,0x01,0x00,0xB7,0xA8,
+0x18,0x2A,0x2B,0x1E,0x23,0xC8,0x1A,0x00,0x39,0xA0,0x18,0x92,0x4F,0x2D,0x5A,0x10,
+0x89,0x81,0x2A,0x8B,0x6A,0x02,0x09,0xB3,0x8D,0x48,0x1B,0x80,0x19,0x34,0xF8,0x29,
+0x0A,0x7B,0x2A,0x28,0x81,0x0C,0x02,0x1E,0x29,0x09,0x12,0xC2,0x94,0xE1,0x18,0x98,
+0x02,0xC4,0x89,0x91,0x1A,0x20,0xA9,0x02,0x1B,0x48,0x8E,0x20,0x88,0x2D,0x08,0x59,
+0x1B,0x02,0xA3,0xB1,0x8A,0x1E,0x58,0x80,0xC2,0xB6,0x88,0x91,0x88,0x11,0xA1,0xA3,
+0xE2,0x01,0xB0,0x19,0x11,0x09,0xF4,0x88,0x09,0x88,0x19,0x89,0x12,0xF1,0x2A,0x28,
+0x8C,0x25,0x99,0xA4,0x98,0x39,0xA1,0x00,0xD0,0x58,0xAA,0x59,0x01,0x0C,0x00,0x2B,
+0x00,0x08,0x89,0x6B,0x69,0x90,0x01,0x90,0x98,0x12,0xB3,0xF3,0xA0,0x89,0x02,0x3B,
+0x0C,0x50,0xA9,0x4E,0x6B,0x19,0x28,0x09,0xA2,0x08,0x2F,0x20,0x88,0x92,0x8A,0x11,
+0xC4,0x93,0xF1,0x18,0x88,0x11,0xF2,0x80,0x92,0xA8,0x02,0xA8,0xB7,0xB3,0xA3,0xA0,
+0x88,0x1A,0x40,0xE2,0x91,0x19,0x88,0x18,0x91,0x83,0xC1,0xB5,0x92,0xA9,0xC6,0x90,
+0x01,0xC2,0x81,0x98,0x03,0xF0,0x00,0x2C,0x2A,0x92,0x2C,0x83,0x1F,0x3A,0x29,0x00,
+0xB8,0x70,0xAB,0x69,0x18,0x89,0x10,0x0D,0x12,0x0B,0x88,0x4A,0x3A,0x9B,0x70,0xA8,
+0x28,0x2F,0x2A,0x3A,0x1B,0x85,0x88,0x8B,0x6A,0x29,0x00,0x91,0x91,0x1B,0x7C,0x29,
+0x01,0x88,0x90,0x19,0x2B,0x2B,0x00,0x39,0xA8,0x5E,0x21,0x89,0x91,0x09,0x3A,0x6F,
+0x2A,0x18,0x18,0x8B,0x50,0x89,0x2B,0x19,0x49,0x88,0x29,0xF5,0x89,0x08,0x09,0x12,
+0xAA,0x15,0xB0,0x82,0xAC,0x38,0x00,0x3F,0x81,0x10,0xB0,0x49,0xA2,0x81,0x3A,0xC8,
+0x87,0x90,0xC4,0xA3,0x99,0x19,0x83,0xE1,0x84,0xE2,0xA2,0x90,0x80,0x93,0xB5,0xC4,
+0xB3,0xA1,0x0A,0x18,0x92,0xC4,0xA0,0x93,0x0C,0x3A,0x18,0x01,0x1E,0x20,0xB1,0x82,
+0x8C,0x03,0xB5,0x2E,0x82,0x19,0xB2,0x1B,0x1B,0x6B,0x4C,0x19,0x12,0x8B,0x5A,0x11,
+0x0C,0x3A,0x2C,0x18,0x3D,0x08,0x2A,0x5C,0x18,0x00,0x88,0x3D,0x29,0x80,0x2A,0x09,
+0x00,0x7A,0x0A,0x10,0x0B,0x69,0x98,0x10,0x81,0x3F,0x00,0x18,0x19,0x91,0xB7,0x9A,
+0x28,0x8A,0x48,0x92,0xF3,0xA2,0x88,0x98,0x87,0xA1,0x88,0x80,0x81,0x95,0xD1,0xA3,
+0x1B,0x1C,0x39,0x10,0xA1,0x2A,0x0B,0x7A,0x4B,0x80,0x13,0xC1,0xD1,0x2B,0x2A,0x85,
+0xB2,0xA2,0x93,0xB2,0xD3,0x80,0xD1,0x18,0x08,0x08,0xB7,0x98,0x81,0x3F,0x01,0x88,
+0x01,0xE2,0x00,0x9A,0x59,0x08,0x10,0xC3,0x99,0x84,0xA9,0xA5,0x91,0x91,0x91,0x80,
+0xB5,0x94,0xC0,0x01,0x98,0x09,0x84,0xB0,0x80,0x7A,0x08,0x18,0x90,0xA8,0x6A,0x1C,
+0x39,0x2A,0xB7,0x98,0x19,0x10,0x2A,0xA1,0x10,0xBD,0x39,0x18,0x2D,0x39,0x3F,0x10,
+0x3F,0x01,0x09,0x19,0x0A,0x38,0x8C,0x40,0xB3,0xB4,0x93,0xAD,0x20,0x2B,0xD4,0x81,
+0xC3,0xB0,0x39,0xA0,0x23,0xD8,0x04,0xB1,0x9B,0xA7,0x1A,0x92,0x08,0xA5,0x88,0x81,
+0xE2,0x01,0xB8,0x01,0x81,0xC1,0xC7,0x90,0x92,0x80,0xA1,0x97,0xA0,0xA2,0x82,0xB8,
+0x18,0x00,0x9C,0x78,0x98,0x83,0x0B,0x0B,0x32,0x7D,0x19,0x10,0xA1,0x19,0x09,0x0A,
+0x78,0xA8,0x10,0x1B,0x29,0x29,0x1A,0x14,0x2F,0x88,0x4A,0x1B,0x10,0x10,0xAB,0x79,
+0x0D,0x49,0x18,0xA0,0x02,0x1F,0x19,0x3A,0x2B,0x11,0x8A,0x88,0x79,0x8A,0x20,0x49,
+0x9B,0x58,0x0B,0x28,0x18,0xA9,0x3A,0x7D,0x00,0x29,0x88,0x82,0x3D,0x1A,0x38,0xBA,
+0x15,0x09,0xAA,0x51,0x8B,0x83,0x3C,0x8A,0x58,0x1B,0xB5,0x01,0xBB,0x50,0x19,0x99,
+0x24,0xCA,0x21,0x1B,0xA2,0x87,0xA8,0xB1,0x68,0xA1,0xA6,0xA2,0xA8,0x29,0x8B,0x24,
+0xB4,0xE2,0x92,0x8A,0x00,0x19,0x93,0xB5,0xB4,0xB1,0x81,0xB1,0x03,0x9A,0x82,0xA7,
+0x90,0xD6,0xA0,0x80,0x1B,0x29,0x01,0xA4,0xE1,0x18,0x0A,0x2A,0x29,0x92,0xC7,0xA8,
+0x81,0x19,0x89,0x30,0x10,0xE0,0x30,0xB8,0x10,0x0C,0x1A,0x79,0x1B,0xA7,0x80,0xA0,
+0x00,0x0B,0x28,0x18,0xB1,0x85,0x1E,0x00,0x20,0xA9,0x18,0x18,0x1C,0x13,0xBC,0x15,
+0x99,0x2E,0x12,0x00,0xE1,0x00,0x0B,0x3B,0x21,0x90,0x06,0xC9,0x2A,0x49,0x0A,0x18,
+0x20,0xD1,0x3C,0x08,0x00,0x83,0xC9,0x41,0x8E,0x18,0x08,0x02,0xA0,0x09,0xA4,0x7B,
+0x90,0x19,0x2A,0x10,0x2A,0xA8,0x71,0xBA,0x10,0x4A,0x0E,0x22,0xB2,0xB2,0x1B,0x8C,
+0x78,0x1A,0xB5,0x93,0xA9,0x1B,0x49,0x19,0x29,0xA3,0xC6,0x88,0xAA,0x32,0x0D,0x1B,
+0x22,0x08,0xC2,0x18,0xB9,0x79,0x3F,0x01,0x10,0xA9,0x84,0x1C,0x09,0x21,0xB0,0xA7,
+0x0A,0x99,0x50,0x0C,0x81,0x28,0x8B,0x48,0x2E,0x00,0x08,0x99,0x38,0x5B,0x88,0x14,
+0xA9,0x08,0x11,0xAA,0x72,0xC1,0xB3,0x09,0x8A,0x05,0x91,0xF2,0x81,0xA1,0x09,0x02,
+0xF2,0x92,0x99,0x1A,0x49,0x80,0xC5,0x90,0x90,0x18,0x09,0x12,0xA1,0xF2,0x81,0x98,
+0xC6,0x91,0xA0,0x11,0xA0,0x94,0xB4,0xF2,0x81,0x8B,0x03,0x80,0xD2,0x93,0xA8,0x88,
+0x69,0xA0,0x03,0xB8,0x88,0x32,0xBC,0x97,0x80,0xB1,0x3B,0x1A,0xA6,0x00,0xD1,0x01,
+0x0B,0x3B,0x30,0x9B,0x31,0x3E,0x92,0x19,0x8A,0xD3,0x5C,0x1B,0x41,0xA0,0x93,0xA2,
+0xAF,0x39,0x4C,0x01,0x92,0xA8,0x81,0x3C,0x0D,0x78,0x98,0x00,0x19,0x0A,0x20,0x2D,
+0x29,0x3C,0x1B,0x48,0x88,0x99,0x7A,0x2D,0x29,0x2A,0x82,0x80,0xA8,0x49,0x3E,0x19,
+0x11,0x98,0x82,0x9A,0x3B,0x28,0x2F,0x20,0x4C,0x90,0x29,0x19,0x9A,0x7A,0x29,0x28,
+0x98,0x88,0x33,0xCD,0x11,0x3A,0xC1,0xA4,0xA0,0xC4,0x82,0xC8,0x50,0x98,0xB2,0x21,
+0xC0,0xB6,0x98,0x82,0x80,0x9C,0x23,0x00,0xF8,0x30,0xA8,0x1A,0x68,0xA8,0x86,0x9A,
+0x01,0x2A,0x0A,0x97,0x91,0xC1,0x18,0x89,0x02,0x83,0xE0,0x01,0x8B,0x29,0x30,0xE2,
+0x91,0x0B,0x18,0x3B,0x1C,0x11,0x28,0xAC,0x78,0x80,0x93,0x91,0xA9,0x49,0x8B,0x87,
+0x90,0x99,0x3D,0x5A,0x81,0x08,0xA1,0x11,0x2F,0x1A,0x21,0x9B,0x15,0xA2,0xB0,0x11,
+0xC0,0x91,0x5B,0x98,0x24,0xA2,0xF2,0x92,0x8B,0x6A,0x18,0x81,0xB5,0xB1,0x88,0x4C,
+0x00,0x00,0xA4,0xC1,0x2B,0x1A,0x59,0x0A,0x02,0x80,0x1E,0x02,0x08,0xB3,0x80,0x9A,
+0x23,0xB8,0xF2,0x84,0xAB,0x01,0x48,0x90,0xA7,0x90,0x0A,0x29,0x09,0x95,0x99,0xA0,
+0x59,0x2B,0x00,0x97,0xB0,0x29,0x89,0x2A,0x03,0xD0,0xB7,0x1B,0x81,0x00,0xA6,0xB1,
+0x90,0x09,0x48,0xC0,0x11,0x00,0x8A,0x00,0x5B,0x83,0x9A,0x18,0x2F,0x3C,0x18,0x11,
+0xA9,0x04,0x1A,0x4F,0x01,0x98,0x81,0x09,0x09,0x4A,0x18,0xB4,0xA2,0x0B,0x59,0x90,
+0x3B,0x49,0xBC,0x40,0x6A,0x88,0x3A,0x08,0x3E,0x3A,0x80,0x93,0xB0,0xE1,0x5A,0x00,
+0xA4,0xB3,0xE3,0x90,0x0D,0x38,0x09,0x82,0xC4,0xA1,0xB1,0x4C,0x18,0x10,0x91,0xB2,
+0x13,0xEA,0x34,0x99,0x88,0xA6,0x89,0x92,0x91,0xC1,0x20,0xB2,0xC2,0x86,0xD2,0xB3,
+0x80,0xB2,0x08,0x09,0x87,0x91,0xC0,0x11,0x89,0x90,0x28,0xB9,0x79,0x19,0xA4,0x82,
+0xD0,0x03,0x0C,0xA3,0xA5,0xB2,0xB2,0x1B,0x29,0x13,0xF1,0xB4,0x81,0x9D,0x38,0x00,
+0xC4,0xA1,0x89,0x59,0x1A,0x81,0xA4,0xA9,0x1C,0x6A,0x19,0x02,0xB1,0x1A,0x4A,0x0B,
+0x78,0x89,0x81,0x1C,0x2A,0x29,0x4A,0xA3,0x3E,0x1C,0x49,0x1A,0x08,0x21,0xAE,0x28,
+0x4B,0x19,0x20,0x8C,0x10,0x3A,0xAB,0x26,0x8B,0x18,0x59,0x99,0x13,0xA2,0xAB,0x79,
+0x2F,0x18,0x10,0xB2,0x80,0x1B,0x4D,0x5A,0x80,0x82,0x98,0x81,0x80,0x09,0xA5,0x90,
+0x91,0x03,0xC2,0xE2,0x81,0xA8,0x82,0x09,0xC6,0xA3,0xB1,0x08,0x5B,0x08,0x05,0xD1,
+0xA2,0x89,0x2A,0x28,0x91,0xA6,0x88,0xB0,0x49,0x80,0x09,0x08,0x88,0x07,0xB8,0x05,
+0x99,0x81,0x88,0x18,0xE2,0x00,0xC3,0x18,0x0D,0x10,0x30,0xD0,0x93,0x8A,0x09,0x10,
+0x2F,0x11,0x90,0xA1,0x20,0x9B,0xB1,0x73,0xC8,0x94,0x98,0x3B,0x01,0x0C,0x30,0x19,
+0xF8,0x12,0x90,0xBA,0x78,0x0A,0x11,0x98,0xA0,0x79,0x8A,0x30,0x2B,0xC2,0x11,0x0D,
+0x09,0x7A,0x00,0x82,0xB9,0x01,0x7A,0x89,0x21,0x09,0xA1,0x0A,0x7C,0x10,0x88,0xB5,
+0x88,0x0A,0x2B,0x69,0x1A,0x10,0xA0,0x5B,0x19,0x1A,0x10,0x19,0x1A,0x6C,0x20,0x90,
+0xA5,0x98,0x1B,0x0A,0x69,0x82,0xD1,0x18,0x09,0x19,0x2A,0x93,0xD4,0x9A,0x01,0x49,
+0xA2,0xA2,0x82,0xD8,0x22,0xAA,0x97,0xA9,0x2D,0x38,0x2A,0xB6,0x80,0x90,0x0A,0x3C,
+0x82,0x94,0xB8,0x21,0x0E,0x2A,0x22,0xB8,0x00,0x4F,0x2B,0x3A,0x81,0xA1,0x29,0x2C,
+0x6A,0x13,0xD1,0xA2,0x98,0x28,0x0C,0x01,0xD5,0x08,0xA9,0x31,0xB3,0xB0,0xA7,0xB0,
+0x29,0x1B,0x87,0xA2,0xA1,0xB2,0x4A,0x89,0x11,0xC3,0xF3,0x98,0x08,0x03,0xA0,0xA3,
+0xC5,0x90,0xB3,0xB5,0xB4,0xB8,0x02,0x91,0x91,0xD3,0xA4,0xC1,0x1B,0x82,0x28,0xA4,
+0xD1,0x94,0x8A,0x28,0x08,0x03,0xE0,0x80,0xD4,0x90,0x91,0xA1,0x3B,0x3D,0x02,0xE4,
+0xA1,0x92,0x89,0x1A,0x4B,0x95,0xB3,0x90,0x99,0x6A,0x0A,0x30,0xA1,0x93,0xA6,0xA9,
+0x85,0x8B,0x82,0x10,0xB1,0xA3,0x94,0xF8,0x38,0x9A,0x30,0x1A,0x8B,0xA7,0x89,0x01,
+0x5B,0x19,0x18,0x11,0xF0,0x18,0x1C,0x39,0x19,0x0C,0x12,0x1C,0x2A,0x7B,0x3A,0x88,
+0x2B,0x18,0x2B,0x5C,0x20,0x92,0x8D,0x38,0x8A,0x3A,0x5B,0x2E,0x3A,0x2B,0x10,0x12,
+0xBB,0x6A,0x4D,0x18,0x10,0xB1,0x81,0x2A,0x8B,0x79,0x80,0x01,0x0A,0x09,0x5B,0x2D,
+0x84,0x8A,0x08,0x02,0xA2,0x91,0x82,0xE8,0x50,0x9B,0x85,0xA3,0xB0,0xA3,0x1B,0x02,
+0x18,0xF3,0xA2,0x88,0xAB,0x53,0xD1,0xB4,0xA3,0x09,0x09,0x18,0xD4,0x08,0xB0,0x09,
+0x58,0xD1,0x82,0x89,0x81,0x1A,0x18,0x05,0xB9,0xC3,0x30,0xC0,0x95,0x80,0xC3,0x89,
+0x89,0x13,0x88,0xF2,0x93,0x0E,0x18,0x01,0x92,0xA5,0xB8,0x2A,0x39,0xAA,0x33,0x9A,
+0xB1,0x11,0xF5,0xA1,0xA1,0x0A,0x50,0xB8,0x03,0xC4,0xA0,0x4E,0x29,0x10,0x88,0xC2,
+0x1A,0x39,0x1D,0x28,0x98,0x94,0x0E,0x10,0x2A,0x3C,0x02,0x2D,0x1B,0x4B,0x3B,0x49,
+0x19,0xA9,0x48,0x2F,0x29,0x10,0x89,0x02,0x0C,0x10,0x09,0xB9,0x70,0x1B,0x8A,0x50,
+0xA8,0x2B,0x49,0x89,0x69,0x88,0x95,0x89,0x90,0x92,0x4C,0x19,0x82,0xC1,0x01,0x80,
+0xA0,0x2B,0x7A,0x81,0x10,0xC2,0xB7,0x98,0x88,0x19,0x2C,0x03,0xB1,0xA4,0xA1,0x0C,
+0x3B,0x78,0x88,0x85,0xB1,0xA0,0x1B,0x3A,0x4A,0x08,0x94,0x81,0xF1,0x80,0x00,0x0C,
+0x59,0x09,0x18,0x90,0xA6,0x92,0x8C,0x1A,0x79,0x92,0xA8,0x00,0x81,0x2E,0x2A,0x13,
+0xA2,0xB0,0xA5,0x88,0x88,0x89,0x11,0x19,0xA0,0xF3,0x82,0xB0,0x83,0x5F,0x2A,0x01,
+0xA1,0x94,0xB0,0x09,0x78,0x98,0xA3,0xA6,0xA0,0x91,0x80,0x93,0x98,0xC1,0x12,0x18,
+0xC9,0x17,0xA0,0xA0,0x1A,0x21,0x80,0x99,0xD4,0x30,0x9D,0x00,0x10,0x2F,0x08,0x1C,
+0x21,0x08,0xB4,0xC3,0x2B,0xA9,0x52,0xD2,0xA3,0xD1,0x09,0x10,0x8B,0x24,0x92,0xD1,
+0x80,0x19,0xA0,0x2C,0x12,0x49,0xAA,0xB6,0x95,0xB8,0x08,0x3A,0x2B,0x01,0xF3,0xB3,
+0x0B,0x09,0x79,0x18,0xA2,0xA4,0xA0,0x18,0x0C,0x20,0x08,0xA9,0x16,0x0C,0x00,0x1B,
+0x08,0x2B,0x7B,0x01,0x01,0xB9,0x59,0x19,0x8B,0x45,0xA8,0x80,0x0C,0x1A,0x41,0x1E,
+0x00,0x28,0xA8,0x5A,0x00,0xC1,0x49,0x99,0x21,0x1D,0x08,0x85,0x99,0x95,0x89,0x90,
+0x11,0x90,0xD1,0x28,0xB2,0xA7,0x99,0x81,0x02,0xAC,0x13,0x81,0xB2,0xA6,0xA9,0x28,
+0x1C,0xB1,0x33,0xD1,0xC1,0x58,0xA8,0x14,0xB0,0xB7,0x91,0xA0,0x82,0x89,0xC2,0x28,
+0xA1,0xB2,0x49,0xD2,0x94,0xC8,0x12,0x80,0x99,0x85,0x08,0xD3,0x09,0xA2,0xB3,0x1E,
+0x08,0x21,0xB9,0x23,0xB4,0xAB,0x41,0xAC,0x87,0x09,0xA2,0xC5,0x0B,0x2A,0x5A,0x91,
+0x20,0x9A,0x89,0x78,0x9B,0x31,0x89,0x80,0x29,0x0A,0xB7,0x3C,0x98,0x48,0x1D,0x00,
+0x01,0xB0,0x20,0x2F,0x29,0x4A,0x89,0x94,0x1C,0x88,0x28,0x2B,0x10,0x88,0x9A,0x71,
+0x9A,0x08,0x4A,0x2F,0x18,0x2B,0x18,0x02,0xA8,0x4B,0x7A,0x99,0x48,0x80,0xA8,0x20,
+0x1D,0x40,0xA8,0x10,0x08,0xA8,0xC5,0x88,0xC2,0x18,0x88,0x2A,0x12,0xF3,0x82,0xD8,
+0x20,0x0A,0x09,0xA6,0x98,0x04,0xB9,0x11,0x18,0xC3,0xE1,0x29,0xA1,0x11,0xC1,0x03,
+0xE2,0x9A,0x33,0xA9,0xB5,0x98,0x92,0xA1,0x02,0xF8,0x21,0xA8,0x10,0x02,0xC1,0xB7,
+0x1B,0x90,0x5B,0x3C,0x83,0x93,0xE0,0x19,0x1A,0x11,0x11,0xF1,0x92,0x89,0x19,0x2C,
+0x2C,0x41,0x99,0x92,0x90,0x3F,0x18,0x4B,0x00,0x08,0xD2,0x01,0xB2,0xAA,0x78,0x09,
+0x01,0x91,0xA2,0x98,0x2F,0x3A,0x2C,0x01,0x00,0x93,0xE0,0x28,0x2C,0x2B,0x01,0x12,
+0xE1,0x80,0xB3,0x3D,0x3A,0x0A,0x50,0x98,0xC2,0xA0,0x11,0xAA,0x30,0x87,0x90,0xC2,
+0x29,0x88,0x38,0xC8,0xB5,0x90,0xBA,0x70,0x1A,0x02,0x94,0xD0,0x80,0x1A,0x82,0xA6,
+0xB0,0x91,0x18,0xB3,0x00,0x13,0xF1,0xA2,0xC1,0x82,0xB0,0x00,0x15,0x0B,0xD3,0x02,
+0xA8,0x91,0x2B,0x1F,0x49,0x88,0xA6,0x80,0x88,0x08,0x1B,0xA5,0x80,0xB9,0x06,0x0B,
+0x90,0x21,0x9D,0x48,0x18,0xA0,0x15,0xC9,0x82,0x2B,0x1A,0x42,0x9A,0xC4,0x39,0xBC,
+0x69,0x00,0xA0,0x29,0x8C,0x39,0x59,0x08,0x09,0x49,0xA9,0x6B,0x81,0x00,0x98,0xB0,
+0x68,0x3D,0x81,0x88,0x18,0x19,0x1D,0x12,0x80,0xB2,0x3A,0x3F,0x85,0x92,0xD0,0x00,
+0x0A,0x19,0x12,0xF1,0x02,0x9B,0x19,0x40,0xB9,0x11,0x02,0xF2,0x1A,0x08,0x94,0x0A,
+0xC2,0x83,0x0B,0xB4,0xA4,0xC0,0x32,0xD8,0x86,0x98,0x90,0x95,0x89,0xA3,0x83,0xC2,
+0x92,0xE1,0x92,0x82,0xD9,0x03,0x08,0xA9,0x85,0x92,0xA2,0x80,0xE0,0x30,0x8B,0xB3,
+0x87,0x89,0x90,0x83,0xA0,0x08,0x92,0x93,0x3E,0xAB,0x43,0x89,0xE3,0x80,0x83,0x2F,
+0x00,0xA3,0x80,0xC9,0x22,0x3F,0x08,0x81,0x0B,0x33,0x9A,0xA3,0x7B,0x0C,0x29,0x4A,
+0x1B,0x21,0xAA,0x70,0x1B,0x0D,0x48,0x1A,0x81,0x88,0xB1,0x39,0x3F,0x08,0x58,0xA0,
+0x81,0x1A,0x1A,0x2B,0x6D,0x11,0x0A,0x91,0x01,0x1A,0x98,0x5A,0x0C,0x03,0xB1,0x84,
+0xA3,0xAD,0x58,0x2A,0xA1,0x84,0xB1,0xA0,0x5C,0x2B,0x13,0xA8,0x95,0x83,0xE8,0x10,
+0x81,0xB0,0x00,0xC2,0x96,0xA0,0x91,0x00,0x2C,0x90,0x30,0xF2,0x80,0xA8,0x39,0x21,
+0xC1,0x03,0xAC,0x39,0x7C,0x29,0x91,0x1A,0x00,0x19,0x2C,0x3A,0x93,0xB0,0x29,0x8F,
+0x28,0x02,0x93,0xF3,0xA9,0x01,0x03,0xE0,0x08,0x09,0x1D,0x58,0xA1,0x83,0xA9,0x6B,
+0x2A,0x3C,0x21,0x89,0xC2,0x2C,0x4B,0x8A,0x50,0x81,0x98,0xA8,0x32,0x0C,0x8E,0x24,
+0x0B,0x1A,0x81,0x92,0xA1,0x4F,0x18,0x3A,0x0A,0xB4,0x18,0x2E,0x39,0x82,0x19,0xD3,
+0xD0,0x28,0x1B,0x11,0x98,0x07,0xAA,0x28,0x00,0x88,0xB4,0x89,0x1B,0x1F,0x22,0x00,
+0xB3,0xC9,0x33,0xAB,0x2B,0xB5,0x48,0x98,0x98,0xA7,0x10,0xD2,0xC1,0x23,0xCA,0x93,
+0xC6,0x80,0xA1,0x88,0x02,0x89,0xE2,0x09,0x38,0xBA,0x40,0x89,0x21,0xD8,0x49,0x10,
+0x8D,0x02,0x90,0xC3,0x9A,0x24,0x89,0x08,0x84,0xA5,0x9C,0x10,0x11,0x9C,0x88,0x30,
+0x3C,0xA1,0x94,0x58,0x8C,0x0B,0x69,0x29,0x9A,0x81,0x12,0x2B,0x8B,0x79,0x94,0xB0,
+0xC1,0x84,0xC2,0x99,0x25,0x99,0x11,0xA2,0x93,0xE4,0x99,0x80,0x0A,0x00,0x10,0xB7,
+0xB0,0x31,0xBA,0x3C,0x21,0xB3,0xF1,0x18,0xA0,0x2A,0x20,0xA3,0x06,0xE8,0x28,0xA1,
+0xB4,0x08,0x0B,0x11,0x4B,0xB7,0x90,0xA5,0x98,0x3D,0x19,0x02,0xA1,0xC4,0xB2,0x19,
+0x28,0xC0,0xA5,0x92,0xB1,0xA3,0x0A,0x0A,0x08,0x2B,0x70,0xC4,0xB3,0x00,0xBC,0x4B,
+0x39,0x12,0xE3,0xA0,0x00,0x3F,0x18,0x29,0x94,0xD1,0x19,0x09,0x00,0xA1,0x83,0x99,
+0x9B,0x35,0x80,0xC4,0xB1,0x6A,0x1A,0x1C,0x29,0x38,0x0E,0x19,0x5A,0x1A,0x82,0x8A,
+0x59,0x2A,0x2E,0x20,0x88,0xA8,0x3A,0x38,0x3D,0x00,0xB3,0x29,0xAD,0x49,0x10,0x0C,
+0x01,0x01,0xA3,0x8F,0x85,0x09,0x1B,0x88,0x10,0xA3,0xD2,0x90,0x3C,0x5C,0x39,0x03,
+0xD1,0xA0,0x00,0x2A,0x0B,0x04,0xA7,0x90,0xA0,0x11,0x90,0x99,0x83,0xB4,0xB1,0xF1,
+0x84,0x88,0x90,0x18,0x18,0xD3,0xD2,0xB3,0xA0,0x1A,0x21,0xA7,0xB2,0xB3,0x92,0x9A,
+0x22,0xB9,0x28,0x38,0xBD,0x87,0x2A,0xB1,0x13,0x0D,0x0A,0x38,0xC9,0x24,0xC0,0x19,
+0x23,0x0F,0x01,0x88,0xC0,0x2A,0x82,0x18,0x28,0xF0,0x18,0x2A,0x29,0x4B,0x35,0xB8,
+0xA3,0x9D,0x18,0x1B,0x40,0x00,0x9A,0x5C,0x3A,0x09,0x2F,0x38,0x8A,0x3B,0x3B,0x11,
+0x5C,0x19,0x2B,0x4A,0x08,0x0A,0x3D,0x20,0x4F,0x3A,0x19,0x2A,0x18,0x4D,0x1B,0x3A,
+0x11,0x0D,0x3A,0x3C,0x4B,0x93,0x81,0xAA,0x6B,0x4A,0x18,0x00,0xC3,0xC3,0x9A,0x59,
+0x2A,0x1B,0xA7,0xA1,0x81,0x88,0x88,0x58,0xB2,0xB1,0x2B,0x83,0xD4,0x81,0x08,0x0F,
+0x00,0x20,0xC2,0xE2,0x80,0x08,0x1C,0x29,0x04,0xB1,0xA2,0x01,0x1C,0x91,0x00,0x0C,
+0x49,0xB0,0x43,0xF2,0x99,0x39,0x3F,0x00,0x81,0x94,0xC1,0x09,0x1A,0x69,0x90,0x80,
+0x94,0xAA,0x20,0x2A,0x91,0xB1,0x39,0x7A,0x38,0xD1,0x10,0x8A,0x8C,0x5A,0x01,0xB5,
+0x98,0x80,0x2A,0x0B,0x32,0x92,0xF1,0x81,0x9A,0x23,0x8A,0xA3,0xB7,0x09,0x03,0x08,
+0xD0,0x94,0x9A,0x09,0x01,0x93,0xB7,0xC2,0x8C,0x3A,0x83,0x99,0x05,0xA0,0x0B,0x29,
+0x93,0xE5,0x80,0x89,0x38,0x90,0x8A,0xD7,0xA1,0x19,0x1B,0x48,0x98,0x92,0xC3,0xA1,
+0x09,0x3F,0x02,0x0C,0x22,0xC3,0xB2,0xA1,0x01,0x9F,0x4A,0x01,0xA3,0xD3,0xB0,0x28,
+0x3F,0x29,0x20,0xA2,0xC2,0xB1,0x08,0x5A,0x98,0x13,0xD2,0xC1,0x01,0xB2,0x80,0x3D,
+0x03,0xC1,0x89,0x96,0x90,0x90,0x3A,0x1A,0x9A,0x32,0xB6,0xA2,0x8E,0x4A,0x28,0x8A,
+0x84,0xA2,0x8A,0x2D,0x49,0x09,0x88,0x18,0x30,0x9D,0x2C,0x23,0xB1,0x0C,0x92,0x2D,
+0x39,0x82,0xC4,0x2E,0x10,0x1A,0x10,0xB9,0x48,0x19,0x39,0xBA,0x34,0xDA,0x2D,0x48,
+0x1A,0xA6,0x98,0x83,0x9A,0x1D,0x38,0x04,0xD0,0x18,0x90,0x2C,0x11,0x93,0xD3,0x9A,
+0x11,0x08,0x82,0xF1,0x01,0xA0,0x2A,0x93,0xD3,0xB4,0xB8,0x82,0x2F,0x11,0xA3,0xB3,
+0xA8,0x3B,0x09,0x23,0x96,0xC8,0x3B,0x3F,0x93,0x82,0xA1,0x90,0x3F,0x28,0x81,0xD1,
+0x93,0x08,0x2D,0x18,0x91,0xB3,0xB5,0x98,0x2A,0x2B,0x84,0xB1,0x5B,0x8A,0x31,0x18,
+0x80,0x8B,0x7E,0x39,0x2B,0x02,0xC1,0x8B,0x6C,0x49,0x09,0x10,0xA1,0x08,0x01,0x0C,
+0x20,0xA1,0x09,0x4F,0x18,0x00,0x01,0xA0,0x5C,0x1B,0x5B,0x10,0x92,0x90,0x2B,0x5A,
+0x3D,0x18,0x91,0x19,0x98,0x2D,0x39,0x89,0x2D,0x3A,0x48,0x2C,0x11,0xB5,0x9A,0x19,
+0x5B,0x28,0x90,0x95,0x98,0x89,0x2B,0x40,0x08,0x90,0xF3,0x0A,0x08,0xA6,0x80,0x91,
+0xB2,0xA0,0x02,0xF2,0xA1,0xB7,0x89,0x81,0x82,0x91,0xB1,0x21,0xAB,0x32,0xE9,0x04,
+0xA2,0x8D,0x12,0x91,0xA3,0xA3,0xD2,0x8B,0x39,0xD1,0x84,0xE2,0x90,0x00,0x2B,0x29,
+0xA3,0xD4,0xA1,0x91,0x1D,0x5A,0x08,0x19,0x11,0x99,0x08,0x18,0x49,0x0F,0x18,0x10,
+0x82,0xF1,0x00,0x89,0x2F,0x3A,0x01,0xB3,0xC2,0x81,0x3F,0x29,0x08,0x10,0xA1,0xA1,
+0x3B,0x5D,0x19,0x28,0x0B,0x38,0x82,0x91,0x19,0xBD,0x3B,0x7A,0x80,0x12,0xB3,0xE0,
+0x0B,0x6A,0x01,0x88,0xA4,0x08,0x0B,0x08,0x59,0x80,0x80,0x1D,0x49,0x89,0x00,0x84,
+0x99,0x1A,0x2B,0x32,0xE3,0xB4,0xA9,0x3A,0x99,0x31,0xE3,0xAA,0x58,0x3B,0x88,0x95,
+0xC0,0x18,0x4A,0x09,0x30,0xF2,0xA3,0x1C,0x1B,0x49,0x00,0xD3,0xB2,0xA0,0x18,0x11,
+0x92,0xD3,0xB2,0x91,0x80,0xE7,0xA1,0x91,0x98,0x19,0x22,0xC2,0xD2,0x18,0x8D,0x3B,
+0x10,0xA5,0x91,0x98,0x02,0x3E,0x80,0x01,0x90,0xAA,0x13,0xF1,0x02,0xD1,0x08,0x19,
+0x49,0xB4,0x91,0xB4,0x99,0x2A,0x0C,0x32,0xC0,0x05,0x88,0x0B,0x80,0x2C,0x81,0x10,
+0x0B,0x51,0xA9,0x19,0x05,0xBF,0x28,0x20,0xE1,0x90,0x80,0x28,0x19,0x08,0x26,0xB1,
+0xA1,0x18,0x88,0x2A,0xF0,0x12,0x8A,0xB3,0x14,0x1B,0xD4,0xD8,0x10,0x08,0x8A,0x17,
+0xA0,0x98,0x2B,0x3A,0x29,0x48,0xA4,0x99,0x0E,0x4A,0x12,0x8B,0x31,0x8B,0x4E,0x1A,
+0x11,0xB5,0x89,0x91,0x29,0x89,0xC2,0x97,0x90,0x0A,0x19,0x11,0x91,0xC1,0xD5,0x08,
+0x89,0x20,0x91,0xB1,0x1A,0x2D,0x18,0x29,0xD2,0x3B,0x3E,0x3A,0x2A,0x90,0x82,0x1C,
+0x49,0x3B,0x93,0xB6,0xC8,0x4C,0x02,0x91,0x93,0xF2,0x88,0x2D,0x28,0x81,0x82,0xC1,
+0x89,0x2D,0x6B,0x19,0x82,0x80,0x18,0x8B,0x39,0x39,0xC8,0x3A,0x6A,0x0A,0x22,0xD2,
+0x09,0x2C,0x1A,0x68,0x92,0xE2,0x89,0x2A,0x2A,0x30,0xC2,0xA3,0xB4,0x1D,0x2A,0x09,
+0x93,0x18,0xF2,0x89,0x28,0xB3,0x01,0x8F,0x18,0x11,0xA1,0x93,0x90,0xD1,0x7A,0x20,
+0xC3,0xA2,0xA8,0x88,0x1D,0x28,0xA5,0xA2,0xA2,0x0B,0x29,0x2B,0x87,0xC1,0x80,0x0A,
+0x19,0x01,0x12,0xF1,0x10,0x80,0x0A,0x18,0x08,0x2F,0x4A,0x02,0x89,0x1B,0x29,0x5D,
+0x4C,0x08,0x82,0xA1,0x0A,0x3A,0x4B,0x29,0xC6,0xC3,0x09,0x09,0x88,0x39,0x98,0x82,
+0xA5,0x1A,0x30,0x11,0xBD,0x3F,0x12,0x8B,0x28,0xC3,0x88,0x3F,0x2B,0x3B,0x48,0xA1,
+0x80,0x8A,0x4D,0x39,0x01,0x93,0xA2,0xF1,0x19,0x19,0x0A,0x02,0xB2,0x8B,0x24,0xD2,
+0x4B,0x12,0xC8,0x2E,0x10,0xB5,0x89,0x01,0x09,0x1C,0x2A,0x03,0xD4,0x91,0x98,0x99,
+0x11,0x2B,0xE4,0x00,0x00,0x01,0xE0,0xA5,0x89,0x99,0x31,0x18,0xD0,0xB7,0x98,0x18,
+0x0A,0x10,0x94,0xC2,0x90,0x18,0x00,0x99,0x87,0xA0,0x90,0x2A,0x3C,0x02,0xB8,0xC1,
+0x79,0x1A,0x20,0x08,0xA1,0xD2,0x1C,0x29,0x03,0xD1,0x29,0x99,0x2C,0x50,0xB3,0xD1,
+0x08,0x09,0x3C,0x10,0x04,0xB2,0x0D,0x2B,0x59,0x80,0x90,0x01,0x0F,0x3A,0x18,0x01,
+0xA2,0x9B,0x5B,0x3D,0x81,0x03,0xD2,0x98,0x59,0x90,0x81,0x92,0xB4,0x8B,0x1B,0x40,
+0xB2,0xB5,0x08,0x4B,0x01,0x09,0xD1,0x91,0x8B,0x7A,0x10,0xB3,0xC3,0x99,0x49,0x1A,
+0x29,0xB5,0xA2,0xAB,0x40,0x81,0x19,0xB7,0xB0,0x20,0x2B,0xD4,0x88,0xA1,0x91,0x3C,
+0x82,0x37,0xD3,0xB1,0x8A,0x1B,0x30,0xB3,0xF4,0xA1,0x91,0x09,0x10,0x03,0xD0,0x83,
+0xA9,0x8F,0x10,0x01,0x90,0x18,0x80,0x20,0x2B,0xF1,0x28,0x99,0x2A,0x41,0xF0,0x12,
+0xAA,0x83,0x82,0xD1,0xC1,0x08,0x89,0x59,0x09,0x83,0x87,0xB0,0x2A,0x4D,0x18,0x09,
+0x19,0xB3,0x4B,0x3F,0x39,0x19,0x09,0x01,0x89,0x03,0x1F,0x00,0x1A,0x0B,0x10,0x68,
+0xA0,0x18,0x8C,0x6A,0x09,0x08,0x97,0xA1,0x81,0x1B,0x2B,0x4C,0x03,0xB4,0xA8,0x92,
+0x4B,0x3C,0xA1,0x81,0x95,0xA8,0x81,0x12,0xBB,0x92,0x45,0xB9,0x93,0xF4,0x88,0x0A,
+0x2D,0x28,0x00,0xA3,0xA3,0x8A,0x3F,0x48,0xB1,0x92,0xB4,0xA8,0x30,0x80,0xD3,0x80,
+0xD1,0x19,0x3B,0xC4,0x81,0xC1,0x29,0x0D,0x20,0x13,0xC8,0xB4,0x4C,0x09,0x00,0x82,
+0xC2,0x3B,0x0D,0x30,0x0B,0x12,0xF0,0x1B,0x20,0x0A,0xA6,0x80,0x0A,0x4A,0x4A,0x80,
+0x94,0xB1,0x2E,0x3B,0x1A,0x10,0x93,0x10,0x4C,0x3D,0x08,0x82,0xC9,0x19,0x6A,0x2B,
+0x38,0xD1,0x08,0x19,0x2A,0x5A,0x82,0xB1,0x8D,0x29,0x78,0x09,0x82,0x0A,0x2C,0x1B,
+0x19,0x41,0xB8,0x8C,0x79,0x2B,0x11,0x88,0x82,0x91,0xDC,0x28,0x11,0xB0,0x11,0x18,
+0xC9,0x62,0xA1,0x91,0x98,0x3B,0x3A,0xB0,0xF4,0x01,0xC0,0x29,0x39,0xF8,0x95,0x91,
+0x88,0x88,0x91,0x03,0xA1,0xE2,0x18,0x82,0xD1,0xA2,0xD1,0x80,0x19,0x20,0x83,0xB1,
+0xE3,0x80,0x91,0x4D,0x1A,0x03,0xB2,0x09,0x18,0xD1,0x19,0x09,0x92,0xA6,0xA0,0xB6,
+0xB2,0x8B,0x38,0x10,0x42,0xD3,0xD0,0xA8,0x20,0x2C,0x10,0x01,0xB1,0xB4,0xAB,0x5B,
+0x79,0x80,0x10,0x1A,0xA8,0x3D,0x18,0x20,0xB3,0x8F,0x18,0x01,0x00,0x09,0xF3,0x89,
+0x69,0x88,0x81,0x91,0x08,0xE1,0x1A,0x08,0x11,0x81,0x1E,0x29,0xA0,0x01,0x00,0x90,
+0x3E,0x7B,0x18,0x82,0xC3,0xA1,0x2A,0x2C,0x5B,0x81,0xA5,0x90,0x81,0x00,0x0B,0x1A,
+0x1C,0x2C,0x32,0xC0,0xF3,0x80,0x2D,0x2A,0x10,0x02,0xE4,0xC1,0x89,0x4A,0x09,0x01,
+0x03,0xD2,0x98,0x2A,0x39,0x8A,0x89,0x26,0xB1,0xB2,0x12,0xC0,0x0A,0x5A,0x18,0x98,
+0xF3,0x92,0x99,0x99,0x79,0x01,0xB5,0xA1,0x80,0x80,0x90,0x83,0xA0,0xE2,0x81,0x29,
+0x93,0x8A,0x0A,0x6A,0x1F,0x18,0x02,0xC8,0x01,0x19,0x3B,0x4A,0x98,0x17,0xA8,0x0D,
+0x38,0xA1,0x91,0x10,0xA2,0x2B,0x4C,0xA6,0x81,0xBA,0x21,0x4C,0x80,0x21,0xD1,0x92,
+0x2C,0x08,0x30,0x9F,0x93,0x2A,0x89,0x03,0x8B,0x87,0x0A,0x0D,0x12,0x98,0xA4,0x93,
+0xBB,0x59,0x18,0xA1,0x32,0xE9,0x84,0x08,0x8A,0x02,0xA1,0x91,0x4B,0xB4,0x20,0x88,
+0xF0,0x3A,0x1A,0x88,0x87,0xB1,0x92,0x0A,0x08,0x6B,0x83,0xC3,0x91,0xC0,0x2B,0x79,
+0x08,0x8A,0x84,0xA0,0x89,0x40,0x1B,0xA1,0x39,0x98,0x17,0xC2,0xA2,0x12,0xCD,0x20,
+0x89,0x92,0x25,0xB0,0x2D,0x3A,0x8B,0x58,0x2A,0xA0,0x4C,0x08,0x30,0xAE,0x82,0x59,
+0x89,0x1A,0x10,0xC2,0x18,0x2C,0x40,0x1E,0x01,0xA3,0x8A,0x81,0x2C,0x29,0x29,0xA9,
+0x13,0x51,0xAD,0x12,0x89,0x8F,0x18,0x2C,0x39,0x00,0xC1,0x10,0x3C,0x2A,0x41,0xC8,
+0xA2,0x91,0x0A,0x6C,0x10,0x12,0x88,0xE8,0x30,0x91,0x81,0xD8,0x01,0x1B,0x0D,0x07,
+0x00,0xA8,0x92,0x0A,0x28,0xD2,0xC3,0x02,0xAA,0x94,0x81,0xB4,0xB3,0x1A,0x0B,0x13,
+0xF9,0x16,0xA1,0x8A,0x59,0x19,0x02,0xC1,0x91,0x8B,0x3D,0x18,0x3B,0xA4,0x94,0x80,
+0x99,0x88,0x1C,0x79,0x0A,0x02,0x03,0xF8,0x90,0x39,0x5B,0x19,0x02,0xC3,0x90,0xBB,
+0x58,0x6A,0x09,0x02,0x89,0x91,0x88,0x1A,0x69,0x8A,0x19,0x15,0xA0,0xA2,0x00,0x9A,
+0x6B,0x49,0x88,0xA3,0x92,0xBB,0x6B,0x3D,0x38,0x01,0x98,0x91,0x3F,0x09,0x18,0x20,
+0x90,0x80,0xAC,0x70,0x91,0x9B,0x51,0x09,0x88,0x99,0x14,0x8B,0x98,0x83,0x79,0xA0,
+0x99,0x13,0x01,0x19,0xE0,0x83,0x0B,0xB0,0x0C,0x31,0x95,0xB5,0xC2,0x8A,0x39,0x20,
+0x80,0x39,0xF3,0xB1,0x10,0x88,0x5E,0x18,0x94,0xA1,0x88,0xA1,0x98,0x15,0xAA,0x39,
+0xD4,0x84,0xC0,0xA2,0xA2,0x0C,0x81,0x86,0xB5,0xA1,0xB1,0x14,0x1B,0xB1,0x02,0x92,
+0xC3,0xE0,0x88,0x11,0xAA,0x69,0x18,0x81,0xA3,0xB0,0x01,0xBF,0x2A,0x31,0x93,0xF1,
+0x00,0x89,0x18,0x19,0x11,0xD3,0xE0,0x10,0x18,0xB1,0x18,0x24,0x9A,0x2B,0xA4,0xC0,
+0xB0,0x31,0x6C,0x19,0xB4,0x12,0xA8,0xEA,0x58,0x10,0x8B,0x93,0x82,0x88,0x9A,0x41,
+0x10,0xC3,0xEA,0x41,0xA9,0x9C,0x34,0xA1,0x2A,0x79,0xA2,0x01,0xA8,0xB3,0x28,0xCC,
+0x41,0x9A,0xB3,0x4B,0xB3,0x27,0x8B,0x83,0x2B,0x2F,0x08,0x28,0xB2,0x80,0x2C,0x30,
+0x5E,0x09,0x12,0x9B,0x09,0x22,0x5B,0x19,0x8A,0x11,0x59,0x99,0xA4,0x32,0xCD,0x18,
+0x08,0x10,0x85,0xB3,0xB4,0x1E,0x88,0x28,0x8A,0x11,0x09,0xC0,0x79,0x80,0x91,0x3B,
+0x80,0x10,0x0F,0x01,0x80,0x91,0x19,0x3D,0x92,0x28,0xA8,0x37,0x9A,0x0A,0x3A,0x8A,
+0x45,0xA9,0xA4,0x00,0xAA,0x09,0x3D,0x59,0x20,0xE1,0x08,0x98,0x90,0x59,0x10,0x09,
+0xA3,0xC3,0x93,0x99,0x2B,0x69,0x11,0xD1,0xB1,0xA4,0x91,0x3C,0x89,0x83,0xF0,0x10,
+0x91,0xA1,0x89,0x59,0x05,0x99,0x93,0x94,0xC8,0x08,0x0A,0x09,0x17,0xB1,0x83,0xC1,
+0x91,0x40,0xA2,0xC2,0x98,0xC3,0xBA,0x28,0x23,0x0F,0x80,0x50,0xB8,0x19,0x10,0x96,
+0x98,0x8C,0x05,0x98,0x19,0x29,0x2B,0x3B,0x0A,0xE2,0x01,0x0F,0x3C,0x38,0x08,0x09,
+0x81,0x4A,0x6C,0x08,0x00,0x88,0x98,0x38,0x2C,0x5A,0x1B,0x20,0x1A,0x39,0xB0,0x09,
+0xCB,0x5B,0x49,0x09,0x71,0x00,0xC1,0x0E,0x08,0x38,0x0C,0x02,0x10,0x0E,0x10,0x8A,
+0x48,0x19,0x90,0x92,0x0D,0xA3,0x98,0x3B,0x79,0x19,0x01,0x10,0xE1,0x80,0x19,0x2B,
+0x10,0xF2,0x02,0xAB,0x84,0x9A,0x29,0xB4,0x80,0x92,0x03,0x88,0x95,0xD0,0x03,0x90,
+0xA0,0xC7,0xA1,0xB0,0xA2,0x02,0x18,0xB5,0xD4,0x01,0xC0,0x08,0xA2,0x93,0xA8,0xA0,
+0xC3,0x20,0xF3,0x90,0x00,0xD5,0x08,0x89,0xA5,0x80,0xA0,0x81,0x82,0xC2,0x09,0xD1,
+0x13,0xCB,0x03,0x84,0x91,0xE1,0x1B,0x12,0x08,0xAB,0x87,0x18,0xAB,0x58,0x89,0x28,
+0x81,0xC9,0x33,0xA9,0x80,0x2E,0x20,0x83,0xB9,0x20,0x3B,0x9E,0x7A,0x08,0x81,0x18,
+0x0B,0x88,0x79,0x80,0x8B,0x00,0x12,0x0E,0x89,0x51,0x1B,0x81,0xA0,0x3A,0x01,0xAF,
+0x11,0x28,0xBA,0x35,0x98,0x88,0x52,0xC0,0x83,0x2F,0xA9,0x11,0x0A,0x19,0x25,0xD0,
+0x30,0x9C,0x08,0x21,0x98,0x81,0x2A,0xF3,0x2A,0x80,0xB6,0x2B,0x08,0x93,0xE9,0x02,
+0x81,0x8C,0x21,0x00,0xA6,0xA9,0x94,0x01,0x8F,0x80,0x94,0x98,0x93,0xB4,0x00,0x08,
+0xC0,0x14,0x98,0xB3,0xB4,0xC1,0x09,0x18,0xA7,0x00,0xA3,0xC8,0x0A,0x3C,0x19,0x96,
+0x83,0xC1,0x99,0x19,0x4A,0x85,0x80,0xC1,0x91,0x99,0x90,0x2A,0x17,0x95,0x99,0x88,
+0x12,0xAE,0x39,0x08,0x92,0x84,0xB0,0xA8,0x79,0x09,0x19,0x01,0xB2,0xA3,0x8F,0x28,
+0x2B,0xA2,0x40,0x82,0xA0,0x4C,0xA9,0x39,0x8D,0x81,0x70,0x88,0xA0,0x1A,0x49,0x2D,
+0x1A,0x26,0xA8,0x98,0x08,0x29,0x0B,0x12,0x96,0xB1,0xB2,0x3A,0x13,0x9B,0x60,0xA0,
+0x88,0xB2,0x34,0xEA,0x1A,0x2A,0x79,0x98,0x10,0x04,0x8C,0x1C,0x81,0x04,0x8C,0x83,
+0x19,0x2F,0x81,0x93,0x98,0x10,0x08,0x30,0x2A,0xFA,0x05,0x08,0x2A,0x89,0x91,0xA3,
+0xFA,0x11,0x11,0x00,0x8C,0x04,0x8A,0x2A,0xB5,0x10,0xA9,0xC2,0x3D,0x1B,0x32,0x04,
+0x0A,0x1A,0x09,0x40,0x1F,0x92,0x1D,0x2A,0x91,0x10,0x30,0x2F,0x0B,0x68,0x99,0xA2,
+0x92,0x88,0x78,0xA9,0x20,0x28,0xE2,0x92,0x1A,0x99,0x4B,0x19,0x22,0xA1,0xE2,0x21,
+0x2F,0x98,0x29,0x18,0x91,0x08,0xB0,0x79,0x1A,0x82,0x3B,0xB1,0xA7,0x8A,0xB3,0x98,
+0x5B,0x23,0xCA,0x42,0x83,0xF0,0x90,0x18,0x98,0x08,0xB4,0x20,0xA3,0xC0,0x43,0xD8,
+0x80,0x81,0xA3,0x99,0xD9,0xA7,0x19,0x90,0x10,0x05,0xB1,0x8B,0x02,0xA4,0xBD,0x23,
+0x93,0x8A,0x99,0x4B,0x03,0xC1,0xF8,0x38,0x09,0x2B,0x14,0xD0,0x03,0x8A,0x2A,0x39,
+0xB9,0x97,0x90,0xAA,0x50,0x01,0x99,0x51,0xD1,0x09,0x1A,0xB5,0x00,0x8B,0x93,0x08,
+0x98,0x11,0xF9,0x85,0x2B,0x08,0x96,0x89,0x90,0x2A,0x12,0x4A,0xD8,0x85,0x2B,0x0E,
+0x10,0x00,0x01,0xB1,0x9B,0x69,0x1A,0x90,0x40,0xB8,0x01,0x08,0x0A,0x2C,0x09,0x14,
+0x4B,0xE2,0x82,0x88,0xB1,0x78,0x0A,0x01,0xC2,0x93,0x19,0xCE,0x20,0x3C,0x82,0xB4,
+0x1B,0x20,0x8C,0x3B,0x29,0xAB,0x86,0x23,0xD8,0x81,0x9A,0x5A,0x49,0xB0,0x16,0xA0,
+0xB0,0x28,0x1B,0x13,0x93,0xE4,0xA2,0xA9,0x08,0x5A,0xB3,0x12,0xC1,0xE1,0x10,0x88,
+0x01,0x0C,0x92,0x08,0x89,0xB7,0x88,0x81,0x10,0x9A,0x17,0xA0,0xB0,0x13,0x99,0xE0,
+0x39,0x31,0xD2,0xB2,0x80,0x0B,0x2D,0x49,0x80,0x01,0xB0,0x06,0x09,0x0C,0x3A,0x69,
+0xA0,0x08,0xB2,0xA1,0x69,0x2B,0x5A,0x81,0x92,0xBA,0x21,0xB1,0x7D,0x10,0x80,0x08,
+0x88,0x82,0x32,0x0D,0xB0,0x1A,0x1C,0x21,0x94,0xA9,0x58,0xB9,0x5A,0x4A,0xA0,0x13,
+0xA9,0x80,0x7C,0x00,0x20,0x8A,0x04,0x0C,0x00,0x82,0x2A,0xB2,0xAC,0x4B,0x69,0xA0,
+0xA6,0x81,0x9B,0x19,0x38,0x8B,0x17,0xB2,0x81,0x2A,0xBB,0x94,0x29,0xA2,0x15,0xBA,
+0x97,0xA3,0xB9,0x79,0x01,0xB2,0x02,0xF1,0x90,0x0A,0x29,0x11,0x88,0xE5,0xA0,0x81,
+0x19,0x91,0x90,0x28,0xB3,0x14,0xD0,0xB5,0x91,0x9A,0x29,0x0B,0x07,0xA2,0xB3,0x01,
+0x9D,0x28,0x41,0xD0,0x91,0x90,0x82,0x1A,0xA8,0x44,0x9A,0xA9,0x21,0xE3,0xA9,0x4B,
+0x19,0x78,0x89,0x83,0xA3,0xB9,0x5A,0x3D,0x80,0x82,0xA2,0xA0,0x6C,0x10,0x20,0x8B,
+0x93,0x8B,0x0E,0x33,0xA9,0xB1,0x68,0x8A,0x31,0xAC,0x94,0xB4,0x8B,0x32,0x0B,0xB4,
+0x81,0x91,0x1D,0x33,0xD9,0x31,0xE1,0x8B,0x3B,0x30,0x12,0x49,0xD2,0x8E,0x29,0x18,
+0x8A,0x92,0x02,0xAA,0x59,0x1C,0x32,0x88,0x01,0x23,0xFB,0x83,0x29,0xDA,0x59,0x01,
+0x81,0x92,0xE1,0x18,0x8A,0x1D,0x30,0x93,0xF1,0x00,0x01,0x0B,0x39,0x92,0x89,0xA0,
+0x11,0x5B,0xE0,0x82,0x09,0x13,0xAA,0xB4,0x16,0xD8,0x91,0x2A,0x29,0x84,0x1B,0xC5,
+0x98,0x98,0x31,0x98,0x99,0x17,0xA9,0x20,0x92,0xC3,0x18,0x9D,0x20,0x3D,0x89,0x94,
+0xA2,0x1C,0x5C,0x29,0x39,0xA0,0xB3,0x00,0x0C,0x4C,0x48,0x92,0x0A,0x91,0x85,0x9A,
+0x01,0x82,0x1F,0x10,0x99,0x15,0xC1,0xA0,0x39,0x1A,0x1D,0x85,0xB4,0x90,0x1A,0x2A,
+0x4B,0x01,0xB2,0x93,0xBE,0x12,0x83,0xC9,0x18,0x09,0x20,0x78,0xF1,0x08,0x19,0x88,
+0x3A,0x83,0xB3,0xA9,0x93,0x7A,0x0A,0x96,0x98,0x00,0xA8,0x3A,0x30,0x92,0xF2,0x9B,
+0x3D,0x38,0x92,0x92,0xC3,0xB8,0x6B,0x29,0x01,0x01,0xB2,0x2F,0x09,0x19,0x18,0x01,
+0x3B,0x7B,0x10,0xA1,0x90,0x39,0x0F,0x38,0x0A,0xB5,0xA4,0x89,0x8B,0x6A,0x2B,0x12,
+0xC8,0x90,0x40,0x2A,0x9E,0x22,0x88,0x18,0x09,0x3A,0xC3,0xE8,0x09,0x59,0x08,0x12,
+0x94,0xD0,0x1A,0x2C,0x38,0x00,0xA1,0x83,0xE8,0x08,0x3A,0x08,0x10,0x9E,0x83,0x1D,
+0x92,0x19,0x2C,0x39,0x3B,0x59,0x04,0xE1,0x80,0x08,0x8D,0x21,0x81,0xB2,0xB2,0x02,
+0x99,0x91,0xA4,0xD6,0x98,0x99,0x03,0x80,0x98,0xA7,0x91,0x09,0xA1,0xB2,0xB3,0xE1,
+0x12,0x92,0xB1,0x81,0x06,0x99,0x0A,0x23,0xC4,0xB1,0xF2,0x89,0x19,0x3A,0x94,0x82,
+0xE0,0x89,0x38,0x0B,0xA4,0xA5,0x80,0x80,0x8C,0x34,0xB9,0xA9,0x23,0x13,0xB9,0xC1,
+0xC7,0x1B,0x89,0x10,0x20,0x11,0xE3,0xA8,0x4B,0x0B,0x40,0x91,0x90,0x1B,0x5F,0x2A,
+0x18,0x82,0x91,0x0B,0x4A,0x28,0xCA,0x40,0x80,0x5B,0x2C,0x13,0xB0,0x8A,0xA9,0x5A,
+0x58,0x89,0x82,0x88,0x2E,0x3B,0x31,0xA1,0x9B,0x01,0x7A,0x2C,0x01,0x91,0x93,0x3F,
+0x88,0x39,0x10,0xF1,0x91,0x8B,0x48,0x0A,0x12,0xE3,0xA8,0x18,0x28,0x92,0x97,0x98,
+0x99,0x19,0xA1,0x11,0xB6,0x88,0x3B,0x10,0xD3,0xC3,0xA1,0x2A,0x8A,0x49,0x04,0xF1,
+0x91,0x02,0x8A,0x89,0x04,0xF1,0x98,0x80,0x18,0x12,0xE3,0x81,0x98,0x80,0x01,0xB3,
+0xF2,0x99,0x12,0x2A,0xB5,0xB3,0x92,0xAA,0x19,0x50,0xB2,0xC3,0x92,0xD0,0x2B,0x68,
+0x93,0x99,0xC0,0x2C,0x3E,0x80,0x20,0x08,0x93,0x0D,0x2A,0x31,0x8D,0x02,0x2B,0x91,
+0x08,0x0A,0x03,0x2C,0x3C,0x52,0xB9,0xA0,0x12,0xBF,0x3A,0x29,0x01,0x88,0xC0,0x6A,
+0x3C,0x0A,0x49,0x18,0x0B,0x39,0x2B,0x69,0x0A,0x84,0x2A,0x2A,0x1C,0x2A,0xC3,0x8C,
+0x19,0x50,0x09,0x91,0xA7,0x8D,0x18,0x1A,0x28,0x00,0xA0,0x94,0x10,0x1F,0x20,0x90,
+0x8A,0x12,0xD0,0x1A,0x5A,0x81,0x04,0xBC,0x23,0x10,0xE0,0x90,0x90,0x18,0x1A,0xA6,
+0x12,0xB1,0xD0,0x4A,0x08,0x82,0x92,0xB6,0x9A,0x0A,0x12,0x88,0xC3,0xC5,0x8A,0x89,
+0x20,0xB5,0x93,0x0B,0x18,0x00,0x09,0xF2,0x88,0x2A,0x4A,0x08,0x05,0xB2,0xA9,0x3B,
+0x5D,0x28,0xA4,0xB1,0x00,0x19,0x19,0x7A,0xA3,0xB3,0x0A,0x90,0xA1,0xC4,0x80,0xBA,
+0x50,0x13,0xC1,0xC2,0x9A,0x2A,0x7B,0x28,0x84,0xC1,0x09,0x3B,0x4E,0x20,0x91,0xA1,
+0x18,0xAB,0x79,0x10,0xB4,0x08,0x9A,0x11,0x2B,0xF0,0x93,0xAA,0x01,0x6A,0x01,0x93,
+0x80,0xB8,0x2A,0x5B,0x10,0x80,0x89,0x4A,0x5B,0x92,0x15,0xB2,0xA0,0x2F,0x19,0x93,
+0xB8,0x95,0x80,0x1C,0x21,0xA9,0x02,0x0B,0xA0,0x5A,0x18,0x98,0x39,0x1B,0x68,0x00,
+0x91,0x91,0x9C,0x39,0x3E,0x18,0x84,0xB3,0x9B,0x7A,0x08,0x18,0x0A,0xB5,0x91,0x0B,
+0x28,0x39,0x19,0x90,0x0A,0x50,0xAC,0x11,0x01,0xAB,0x88,0x52,0x1B,0x83,0xC4,0xA2,
+0x9A,0xAB,0x03,0x90,0x19,0x93,0x81,0x08,0x92,0x9A,0x68,0x98,0x19,0x39,0xC1,0x92,
+0x8A,0x38,0x4E,0x02,0xB1,0x90,0xC3,0x18,0x2B,0x04,0xC3,0xD2,0x91,0x90,0x81,0x89,
+0x13,0xF1,0x88,0x93,0xA2,0x00,0x91,0xC0,0x5B,0x21,0x99,0x93,0x06,0x9A,0x1B,0x48,
+0x99,0xB7,0x90,0x89,0x18,0x1B,0x11,0xA4,0xB2,0x81,0x9A,0x08,0x97,0x98,0x91,0x10,
+0xB8,0x06,0xA2,0xA0,0x29,0x2B,0x21,0xC2,0xD1,0x10,0x1A,0x4A,0x29,0xF1,0x98,0x29,
+0x1B,0x31,0x10,0xA0,0xA1,0x1D,0x5A,0x29,0xB2,0x82,0xA8,0x0F,0x28,0x21,0x09,0x91,
+0x82,0x4D,0x10,0xA3,0xB0,0x89,0x4C,0x39,0xA0,0xA4,0xA1,0x89,0x1E,0x28,0x29,0xA3,
+0xC3,0x2D,0x19,0x01,0x49,0x01,0x9B,0x0C,0x21,0xC2,0xA2,0x93,0x7C,0x2A,0x10,0x90,
+
+/* Source: 08HH.ROM */
+/* Length: 384 / 0x00000180 */
+
+0x75,0xF2,0xAB,0x7D,0x7E,0x5C,0x3B,0x4B,0x3C,0x4D,0x4A,0x02,0xB3,0xC5,0xE7,0xE3,
+0x92,0xB3,0xC4,0xB3,0xC3,0x8A,0x3B,0x5D,0x5C,0x3A,0x84,0xC2,0x91,0xA4,0xE7,0xF7,
+0xF7,0xF4,0xA1,0x1B,0x49,0xA5,0xB1,0x1E,0x7F,0x5A,0x00,0x89,0x39,0xB7,0xA8,0x3D,
+0x4A,0x84,0xE7,0xF7,0xE2,0x2D,0x4C,0x3A,0x4E,0x7D,0x04,0xB0,0x2D,0x4B,0x10,0x80,
+0xA3,0x99,0x10,0x0E,0x59,0x93,0xC4,0xB1,0x81,0xC4,0xA2,0xB2,0x88,0x08,0x3F,0x3B,
+0x28,0xA6,0xC3,0xA2,0xA2,0xC5,0xC1,0x3F,0x7E,0x39,0x81,0x93,0xC2,0xA3,0xE5,0xD2,
+0x80,0x93,0xB8,0x6D,0x49,0x82,0xD4,0xA1,0x90,0x01,0xA0,0x09,0x04,0xE3,0xB2,0x91,
+0xB7,0xB3,0xA8,0x2A,0x03,0xF3,0xA1,0x92,0xC5,0xC3,0xB2,0x0B,0x30,0xB3,0x8E,0x6D,
+0x4A,0x01,0xB4,0xB4,0xC4,0xC3,0x99,0x3B,0x12,0xE3,0xA1,0x88,0x82,0xB4,0x9A,0x5C,
+0x3A,0x18,0x93,0xC3,0xB3,0xB4,0xA8,0x19,0x04,0xF3,0xA8,0x3B,0x10,0xA2,0x88,0xA5,
+0xB2,0x0B,0x6D,0x4B,0x10,0x91,0x89,0x3C,0x18,0x18,0xA6,0xC4,0xC3,0x98,0x19,0x2B,
+0x20,0x91,0xA0,0x4E,0x28,0x93,0xB3,0xC2,0x92,0xA9,0x5A,0x96,0xC4,0xC2,0x09,0x01,
+0xC4,0xA1,0x92,0xC4,0xA1,0x89,0x10,0xA3,0xA1,0x90,0x1C,0x5A,0x01,0xC5,0xA1,0x92,
+0xD4,0xB3,0xC4,0xC4,0xC3,0xA1,0x88,0x1A,0x28,0x89,0x3C,0x3A,0x3D,0x29,0x00,0x93,
+0xB0,0x3D,0x28,0x80,0x91,0x82,0xE3,0x99,0x2A,0x11,0xD6,0xC3,0x99,0x29,0x82,0xC4,
+0xC3,0xA1,0x0A,0x3B,0x3D,0x3A,0x02,0xC3,0xA2,0x99,0x3B,0x2C,0x7C,0x28,0x81,0xA3,
+0xB2,0xA3,0xB1,0x08,0x1A,0x3C,0x18,0x2E,0x4C,0x39,0xA5,0xB3,0xB4,0xC2,0x88,0x08,
+0x19,0x0A,0x49,0xB7,0xB3,0xA2,0xA1,0x92,0xA1,0x93,0xB1,0x0C,0x7D,0x39,0x93,0xB3,
+0xB1,0x1A,0x19,0x5D,0x28,0xA6,0xC4,0xB2,0x90,0x09,0x2A,0x18,0x1B,0x5B,0x28,0x88,
+0x2C,0x29,0x82,0xA0,0x18,0x91,0x2D,0x29,0x2B,0x5C,0x4C,0x3B,0x4C,0x28,0x80,0x92,
+0x90,0x09,0x2B,0x28,0x1D,0x6B,0x11,0xC5,0xB2,0x0B,0x39,0x09,0x4D,0x28,0x88,0x00,
+0x1B,0x28,0x94,0xE3,0xA0,0x1A,0x28,0xB5,0xB4,0xB3,0xB2,0x93,0xE2,0x91,0x92,0xD4,
+0xA0,0x1B,0x4A,0x01,0xA1,0x88,0x2D,0x5C,0x3B,0x28,0x08,0x93,0xD4,0xB2,0x91,0xB4,
+0xA0,0x3E,0x3B,0x4B,0x3B,0x29,0x08,0x93,0x9B,0x7B,0x3A,0x19,0x00,0x80,0x80,0xA0,
+
+/* Source: 10TOM.ROM */
+/* Length: 640 / 0x00000280 */
+
+0x77,0x27,0x87,0x01,0x2D,0x4F,0xC3,0xC1,0x92,0x91,0x89,0x59,0x83,0x1A,0x32,0xC2,
+0x95,0xB1,0x81,0x88,0x81,0x4A,0x3D,0x11,0x9E,0x0B,0x88,0x0C,0x18,0x3B,0x11,0x11,
+0x91,0x00,0xA0,0xE2,0x0A,0x48,0x13,0x24,0x81,0x48,0x1B,0x39,0x1C,0x83,0x84,0xA1,
+0xD1,0x8E,0x8A,0x0B,0xC0,0x98,0x92,0xB8,0x39,0x90,0x10,0x92,0xF0,0xB5,0x88,0x32,
+0x49,0x51,0x21,0x03,0x82,0x10,0x8A,0x7A,0x09,0x00,0xA2,0xCA,0x1B,0xCC,0x1C,0xB9,
+0x8E,0x89,0x89,0xA1,0x89,0x92,0x29,0x11,0x60,0x40,0x14,0x22,0x32,0x78,0x40,0x01,
+0x02,0x90,0x81,0xAB,0x0B,0x00,0xAF,0x99,0xCC,0xAB,0xDA,0xA9,0x99,0x1B,0x30,0x14,
+0x92,0x22,0x19,0x68,0x32,0x14,0x26,0x13,0x23,0x23,0x20,0x12,0x9A,0xA8,0xB9,0xFA,
+0xAA,0xCA,0xCC,0x0C,0xA8,0xAE,0x88,0xB9,0x88,0xA0,0x02,0x21,0x50,0x43,0x03,0x81,
+0x2A,0x11,0x34,0x63,0x24,0x33,0x22,0x38,0x8B,0xEA,0xAE,0x99,0xA0,0x90,0x82,0x00,
+0x89,0xBF,0x8A,0xE8,0xA9,0x90,0x01,0x12,0x13,0x12,0x08,0xA9,0xAA,0xC9,0x22,0x63,
+0x63,0x12,0x44,0x00,0x10,0x88,0x9C,0x98,0xA1,0x85,0x03,0x32,0x36,0x80,0x89,0xDB,
+0xDB,0xBB,0xB9,0xBA,0x01,0x81,0x28,0x19,0xCB,0xFA,0xBC,0x09,0x13,0x37,0x34,0x34,
+0x23,0x31,0x20,0x10,0x00,0x00,0x28,0x38,0x10,0x88,0xEC,0x8D,0xCB,0xBC,0xCC,0xBB,
+0xBB,0xC9,0x99,0x00,0x00,0x33,0x11,0x22,0x81,0x07,0x41,0x54,0x34,0x34,0x22,0x31,
+0x00,0x88,0x9A,0x9B,0x98,0xAB,0x8E,0x9B,0xBD,0x9C,0xBC,0xBB,0xDA,0xAA,0xA9,0x99,
+0x18,0x38,0x60,0x20,0x31,0x13,0x13,0x51,0x14,0x31,0x53,0x33,0x35,0x22,0x01,0x8A,
+0x9C,0xA9,0xCA,0xC9,0xA8,0x00,0x10,0x81,0x9C,0x9E,0xAB,0xCC,0xAB,0xBA,0x98,0x30,
+0x52,0x03,0x81,0x08,0x9C,0xAC,0xAC,0x18,0x11,0x03,0x51,0x61,0x41,0x31,0x31,0x02,
+0x01,0x20,0x24,0x43,0x44,0x40,0x30,0x10,0xBC,0xBE,0xCB,0xDB,0xAB,0xBA,0x99,0x98,
+0x99,0xAA,0xBD,0xAA,0xC8,0x90,0x11,0x53,0x37,0x23,0x43,0x34,0x33,0x33,0x33,0x11,
+0x28,0x00,0x19,0xA9,0x9A,0xCB,0xCE,0xBB,0xEB,0xBC,0xBB,0xCA,0xBA,0xA8,0x88,0x11,
+0x12,0x21,0x20,0x22,0x26,0x26,0x23,0x23,0x43,0x24,0x22,0x32,0x20,0x31,0x81,0x9A,
+0xBC,0xBC,0xCB,0xBD,0x9A,0xA9,0x90,0x98,0xBA,0xCC,0xCB,0xBC,0x8B,0x88,0x22,0x35,
+0x23,0x12,0x99,0x8B,0xAA,0xAA,0x89,0x82,0x93,0x31,0x42,0x23,0x23,0x21,0x32,0x11,
+0x20,0x13,0x13,0x24,0x24,0x24,0x22,0x11,0x8A,0x9E,0xAC,0xAC,0xAA,0xBA,0xAA,0xAB,
+0xBD,0xBC,0xCB,0xCB,0xA9,0xA8,0x91,0x12,0x44,0x43,0x44,0x34,0x34,0x42,0x33,0x42,
+0x21,0x11,0x11,0x88,0x80,0xAA,0x0B,0xAC,0xCB,0xEC,0xAC,0xBA,0xCA,0xAB,0x9A,0x99,
+0x80,0x91,0x09,0x08,0x10,0x22,0x44,0x43,0x44,0x33,0x43,0x22,0x13,0x21,0x22,0x20,
+0x09,0x88,0xB9,0xC8,0xBB,0xAB,0xAB,0xA9,0xA9,0x9B,0x9B,0x99,0x90,0x90,0x00,0x81,
+0x00,0x08,0x09,0x8A,0x9A,0xAA,0xA9,0xA9,0x99,0x90,0x80,0x01,0x80,0x00,0x09,0x31,
+0x32,0x44,0x33,0x43,0x34,0x33,0x24,0x22,0x23,0x12,0x10,0x09,0x9B,0xAB,0xCA,0xCC,
+0xBB,0xCB,0xDA,0xCA,0xAB,0xCA,0xAB,0xA9,0xA8,0x92,0x12,0x43,0x53,0x35,0x23,0x33,
+0x43,0x43,0x52,0x22,0x22,0x21,0x01,0x09,0x89,0xA9,0xBB,0xBD,0xBC,0xCB,0xDA,0xAB,
+0xAB,0xAB,0xAA,0xA9,0x99,0xA8,0x09,0x01,0x11,0x34,0x25,0x23,0x33,0x51,0x22,0x31,
+0x12,0x20,0x21,0x12,0x10,0x80,0x99,0x9A,0x99,0x99,0x88,0x08,0x00,0x88,0xA9,0x99,
+0x99,0x80,0x80,0x10,0x01,0x00,0x9A,0xAA,0xBB,0xBA,0xBA,0xA9,0x99,0x99,0x89,0x99,
+0x99,0x00,0x01,0x33,0x35,0x24,0x23,0x34,0x23,0x33,0x34,0x33,0x43,0x32,0x21,0x88,
+0xAB,0xBD,0xBB,0xDB,0xAB,0xBA,0xBB,0xDA,0xBB,0xCB,0xBB,0xBC,0xA8,0x90,0x01,0x12,
+0x23,0x43,0x53,0x34,0x34,0x39,0x80,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,
+
+/* Source: 20RIM.ROM */
+/* Length: 128 / 0x00000080 */
+
+0x0F,0xFF,0x73,0x8E,0x71,0xCD,0x00,0x49,0x10,0x90,0x21,0x49,0xA0,0xDB,0x02,0x3A,
+0xE3,0x0A,0x50,0x98,0xC0,0x59,0xA2,0x99,0x09,0x22,0xA2,0x80,0x10,0xA8,0x5B,0xD2,
+0x88,0x21,0x09,0x96,0xA8,0x10,0x0A,0xE0,0x08,0x48,0x19,0xAB,0x52,0xA8,0x92,0x0C,
+0x03,0x19,0xE2,0x0A,0x12,0xC2,0x81,0x1E,0x01,0xD0,0x48,0x88,0x98,0x01,0x49,0x91,
+0xAA,0x2C,0x25,0x89,0x88,0xB5,0x81,0xA2,0x9A,0x12,0x9E,0x38,0x3B,0x81,0x9B,0x59,
+0x01,0x93,0xCA,0x4A,0x21,0xA0,0x3D,0x0A,0x39,0x3D,0x12,0xA8,0x3F,0x18,0x01,0x92,
+0x1C,0x00,0xB2,0x48,0xB9,0x94,0xA3,0x19,0x4F,0x19,0xB2,0x32,0x90,0xBA,0x01,0xE6,
+0x91,0x80,0xC1,0xA4,0x2A,0x08,0xA1,0xB1,0x25,0xD2,0x88,0x99,0x21,0x80,0x88,0x80,
+};
+
+
+
+/* flag enable control 0x110 */
+INLINE void YM2608IRQFlagWrite(FM_OPN *OPN, YM2608 *F2608, int v)
+{
+ if( v & 0x80 )
+ { /* Reset IRQ flag */
+ FM_STATUS_RESET(&OPN->ST, 0xf7); /* don't touch BUFRDY flag otherwise we'd have to call ymdeltat module to set the flag back */
+ }
+ else
+ { /* Set status flag mask */
+ F2608->flagmask = (~(v&0x1f));
+ FM_IRQMASK_SET(&OPN->ST, (F2608->irqmask & F2608->flagmask) );
+ }
+}
+
+/* compatible mode & IRQ enable control 0x29 */
+INLINE void YM2608IRQMaskWrite(FM_OPN *OPN, YM2608 *F2608, int v)
+{
+ /* SCH,xx,xxx,EN_ZERO,EN_BRDY,EN_EOS,EN_TB,EN_TA */
+
+ /* extend 3ch. enable/disable */
+ if(v&0x80)
+ OPN->type |= TYPE_6CH; /* OPNA mode - 6 FM channels */
+ else
+ OPN->type &= ~TYPE_6CH; /* OPN mode - 3 FM channels */
+
+ /* IRQ MASK store and set */
+ F2608->irqmask = v&0x1f;
+ FM_IRQMASK_SET(&OPN->ST, (F2608->irqmask & F2608->flagmask) );
+}
+
+/* Generate samples for one of the YM2608s */
+void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+ FM_OPN *OPN = &F2608->OPN;
+ YM_DELTAT *DELTAT = &F2608->deltaT;
+ int i,j;
+ FMSAMPLE *bufL,*bufR;
+ FM_CH *cch[6];
+ INT32 *out_fm = OPN->out_fm;
+
+ /* set bufer */
+ bufL = buffer[0];
+ bufR = buffer[1];
+
+ cch[0] = &F2608->CH[0];
+ cch[1] = &F2608->CH[1];
+ cch[2] = &F2608->CH[2];
+ cch[3] = &F2608->CH[3];
+ cch[4] = &F2608->CH[4];
+ cch[5] = &F2608->CH[5];
+
+ /* refresh PG and EG */
+ refresh_fc_eg_chan( OPN, cch[0] );
+ refresh_fc_eg_chan( OPN, cch[1] );
+ if( (OPN->ST.mode & 0xc0) )
+ {
+ /* 3SLOT MODE */
+ if( cch[2]->SLOT[SLOT1].Incr==-1)
+ {
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode );
+ }
+ }
+ else
+ refresh_fc_eg_chan( OPN, cch[2] );
+ refresh_fc_eg_chan( OPN, cch[3] );
+ refresh_fc_eg_chan( OPN, cch[4] );
+ refresh_fc_eg_chan( OPN, cch[5] );
+
+
+ /* buffering */
+ for(i=0; i < length ; i++)
+ {
+
+ advance_lfo(OPN);
+
+ /* clear output acc. */
+ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0;
+ OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0;
+ /* clear outputs */
+ out_fm[0] = 0;
+ out_fm[1] = 0;
+ out_fm[2] = 0;
+ out_fm[3] = 0;
+ out_fm[4] = 0;
+ out_fm[5] = 0;
+
+ /* calculate FM */
+ chan_calc(OPN, cch[0], 0 );
+ chan_calc(OPN, cch[1], 1 );
+ chan_calc(OPN, cch[2], 2 );
+ chan_calc(OPN, cch[3], 3 );
+ chan_calc(OPN, cch[4], 4 );
+ chan_calc(OPN, cch[5], 5 );
+
+ /* deltaT ADPCM */
+ if( DELTAT->portstate&0x80 && ! F2608->MuteDeltaT )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+
+ /* ADPCMA */
+ for( j = 0; j < 6; j++ )
+ {
+ if( F2608->adpcm[j].flag )
+ ADPCMA_calc_chan( F2608, &F2608->adpcm[j]);
+ }
+
+ /* advance envelope generator */
+ OPN->eg_timer += OPN->eg_timer_add;
+ while (OPN->eg_timer >= OPN->eg_timer_overflow)
+ {
+ OPN->eg_timer -= OPN->eg_timer_overflow;
+ OPN->eg_cnt++;
+
+ advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]);
+ }
+
+ /* buffering */
+ {
+ int lt,rt;
+
+ /*lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER];
+ rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER];
+ lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9;
+ rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9;
+ lt += ((out_fm[0]>>1) & OPN->pan[0]); // shift right verified on real YM2608
+ rt += ((out_fm[0]>>1) & OPN->pan[1]);
+ lt += ((out_fm[1]>>1) & OPN->pan[2]);
+ rt += ((out_fm[1]>>1) & OPN->pan[3]);
+ lt += ((out_fm[2]>>1) & OPN->pan[4]);
+ rt += ((out_fm[2]>>1) & OPN->pan[5]);
+ lt += ((out_fm[3]>>1) & OPN->pan[6]);
+ rt += ((out_fm[3]>>1) & OPN->pan[7]);
+ lt += ((out_fm[4]>>1) & OPN->pan[8]);
+ rt += ((out_fm[4]>>1) & OPN->pan[9]);
+ lt += ((out_fm[5]>>1) & OPN->pan[10]);
+ rt += ((out_fm[5]>>1) & OPN->pan[11]);*/
+ // this way it's louder (and more accurate)
+ lt = (OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]) << 1;
+ rt = (OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]) << 1;
+ lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>8;
+ rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>8;
+ lt += (out_fm[0] & OPN->pan[0]);
+ rt += (out_fm[0] & OPN->pan[1]);
+ lt += (out_fm[1] & OPN->pan[2]);
+ rt += (out_fm[1] & OPN->pan[3]);
+ lt += (out_fm[2] & OPN->pan[4]);
+ rt += (out_fm[2] & OPN->pan[5]);
+ lt += (out_fm[3] & OPN->pan[6]);
+ rt += (out_fm[3] & OPN->pan[7]);
+ lt += (out_fm[4] & OPN->pan[8]);
+ rt += (out_fm[4] & OPN->pan[9]);
+ lt += (out_fm[5] & OPN->pan[10]);
+ rt += (out_fm[5] & OPN->pan[11]);
+
+ lt >>= FINAL_SH;
+ rt >>= FINAL_SH;
+
+ //Limit( lt, MAXOUT, MINOUT );
+ //Limit( rt, MAXOUT, MINOUT );
+ /* buffering */
+ bufL[i] = lt;
+ bufR[i] = rt;
+
+ #ifdef SAVE_SAMPLE
+ SAVE_ALL_CHANNELS
+ #endif
+
+ }
+
+ /* timer A control */
+ INTERNAL_TIMER_A( &OPN->ST , cch[2] )
+ }
+ INTERNAL_TIMER_B(&OPN->ST,length)
+
+
+ /* check IRQ for DELTA-T EOS */
+ FM_STATUS_SET(&OPN->ST, 0);
+
+}
+
+static void YM2608_deltat_status_set(void *chip, UINT8 changebits)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+ FM_STATUS_SET(&(F2608->OPN.ST), changebits);
+}
+static void YM2608_deltat_status_reset(void *chip, UINT8 changebits)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+ FM_STATUS_RESET(&(F2608->OPN.ST), changebits);
+}
+/* YM2608(OPNA) */
+//void * ym2608_init(void *param, const device_config *device, int clock, int rate,
+// void *pcmrom,int pcmsize,
+// FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg)
+void * ym2608_init(void *param, int clock, int rate, const ssg_callbacks *ssg)
+{
+ YM2608 *F2608;
+
+ /* allocate extend state space */
+ if( (F2608 = (YM2608 *)malloc(sizeof(YM2608)))==NULL)
+ return NULL;
+ /* clear */
+ memset(F2608,0,sizeof(YM2608));
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ free( F2608 );
+ return NULL;
+ }
+
+ F2608->OPN.ST.param = param;
+ F2608->OPN.type = TYPE_YM2608;
+ F2608->OPN.P_CH = F2608->CH;
+ //F2608->OPN.ST.device = device;
+ F2608->OPN.ST.clock = clock;
+ F2608->OPN.ST.rate = rate;
+
+ /* External handlers */
+ F2608->OPN.ST.SSG = ssg;
+
+ /* DELTA-T */
+ //F2608->deltaT.memory = (UINT8 *)pcmrom;
+ //F2608->deltaT.memory_size = pcmsize;
+ F2608->deltaT.memory = NULL;
+ F2608->deltaT.memory_size = 0x00;
+
+ /*F2608->deltaT.write_time = 20.0 / clock;*/ /* a single byte write takes 20 cycles of main clock */
+ /*F2608->deltaT.read_time = 18.0 / clock;*/ /* a single byte read takes 18 cycles of main clock */
+
+ F2608->deltaT.status_set_handler = YM2608_deltat_status_set;
+ F2608->deltaT.status_reset_handler = YM2608_deltat_status_reset;
+ F2608->deltaT.status_change_which_chip = F2608;
+ F2608->deltaT.status_change_EOS_bit = 0x04; /* status flag: set bit2 on End Of Sample */
+ F2608->deltaT.status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY */
+ F2608->deltaT.status_change_ZERO_bit = 0x10; /* status flag: set bit4 if silence continues for more than 290 miliseconds while recording the ADPCM */
+
+ /* ADPCM Rhythm */
+ F2608->pcmbuf = (UINT8*)YM2608_ADPCM_ROM;
+ F2608->pcm_size = 0x2000;
+
+ Init_ADPCMATable();
+
+#ifdef __STATE_H__
+ YM2608_save_state(F2608, device);
+#endif
+ return F2608;
+}
+
+/* shut down emulator */
+void ym2608_shutdown(void *chip)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+
+ free(F2608->deltaT.memory); F2608->deltaT.memory = NULL;
+
+ FMCloseTable();
+ free(F2608);
+}
+
+/* reset one of chips */
+void ym2608_reset_chip(void *chip)
+{
+ int i;
+ YM2608 *F2608 = (YM2608 *)chip;
+ FM_OPN *OPN = &F2608->OPN;
+ YM_DELTAT *DELTAT = &F2608->deltaT;
+
+ /* Reset Prescaler */
+ OPNPrescaler_w(OPN , 0 , 2);
+ F2608->deltaT.freqbase = OPN->ST.freqbase;
+ /* reset SSG section */
+ (*OPN->ST.SSG->reset)(OPN->ST.param);
+
+ /* status clear */
+ FM_BUSY_CLEAR(&OPN->ST);
+
+ /* register 0x29 - default value after reset is:
+ enable only 3 FM channels and enable all the status flags */
+ YM2608IRQMaskWrite(OPN, F2608, 0x1f ); /* default value for D4-D0 is 1 */
+
+ /* register 0x10, A1=1 - default value is 1 for D4, D3, D2, 0 for the rest */
+ YM2608IRQFlagWrite(OPN, F2608, 0x1c ); /* default: enable timer A and B, disable EOS, BRDY and ZERO */
+
+ OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */
+
+ OPN->eg_timer = 0;
+ OPN->eg_cnt = 0;
+
+ FM_STATUS_RESET(&OPN->ST, 0xff);
+
+ reset_channels( &OPN->ST , F2608->CH , 6 );
+ /* reset OPerator paramater */
+ for(i = 0xb6 ; i >= 0xb4 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0xc0);
+ OPNWriteReg(OPN,i|0x100,0xc0);
+ }
+ for(i = 0xb2 ; i >= 0x30 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0);
+ OPNWriteReg(OPN,i|0x100,0);
+ }
+ for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
+
+ /* ADPCM - percussion sounds */
+ for( i = 0; i < 6; i++ )
+ {
+ if (i<=3) /* channels 0,1,2,3 */
+ F2608->adpcm[i].step = (UINT32)((float)(1<OPN.ST.freqbase)/3.0);
+ else /* channels 4 and 5 work with slower clock */
+ F2608->adpcm[i].step = (UINT32)((float)(1<OPN.ST.freqbase)/6.0);
+
+ F2608->adpcm[i].start = YM2608_ADPCM_ROM_addr[i*2];
+ F2608->adpcm[i].end = YM2608_ADPCM_ROM_addr[i*2+1];
+
+ F2608->adpcm[i].now_addr = 0;
+ F2608->adpcm[i].now_step = 0;
+ /* F2608->adpcm[i].delta = 21866; */
+ F2608->adpcm[i].vol_mul = 0;
+ F2608->adpcm[i].pan = &OPN->out_adpcm[OUTD_CENTER]; /* default center */
+ F2608->adpcm[i].flagMask = 0;
+ F2608->adpcm[i].flag = 0;
+ F2608->adpcm[i].adpcm_acc = 0;
+ F2608->adpcm[i].adpcm_step= 0;
+ F2608->adpcm[i].adpcm_out = 0;
+ }
+ F2608->adpcmTL = 0x3f;
+
+ F2608->adpcm_arrivedEndAddress = 0; /* not used */
+
+ /* DELTA-T unit */
+ DELTAT->freqbase = OPN->ST.freqbase;
+ DELTAT->output_pointer = OPN->out_delta;
+ DELTAT->portshift = 5; /* always 5bits shift */ /* ASG */
+ DELTAT->output_range = 1<<23;
+ YM_DELTAT_ADPCM_Reset(DELTAT,OUTD_CENTER,YM_DELTAT_EMULATION_MODE_NORMAL);
+}
+
+/* YM2608 write */
+/* n = number */
+/* a = address */
+/* v = value */
+int ym2608_write(void *chip, int a,UINT8 v)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+ FM_OPN *OPN = &F2608->OPN;
+ int addr;
+
+ v &= 0xff; /*adjust to 8 bit bus */
+
+
+ switch(a&3)
+ {
+ case 0: /* address port 0 */
+ OPN->ST.address = v;
+ F2608->addr_A1 = 0;
+
+ /* Write register to SSG emulator */
+ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v);
+ /* prescaler selecter : 2d,2e,2f */
+ if( v >= 0x2d && v <= 0x2f )
+ {
+ OPNPrescaler_w(OPN , v , 2);
+ F2608->deltaT.freqbase = OPN->ST.freqbase;
+ }
+ break;
+
+ case 1: /* data port 0 */
+ if (F2608->addr_A1 != 0)
+ break; /* verified on real YM2608 */
+
+ addr = OPN->ST.address;
+ F2608->REGS[addr] = v;
+ switch(addr & 0xf0)
+ {
+ case 0x00: /* SSG section */
+ /* Write data to SSG emulator */
+ (*OPN->ST.SSG->write)(OPN->ST.param,a,v);
+ break;
+ case 0x10: /* 0x10-0x1f : Rhythm section */
+ FM_ADPCMAWrite(F2608,addr-0x10,v);
+ break;
+ case 0x20: /* Mode Register */
+ switch(addr)
+ {
+ case 0x29: /* SCH,xx,xxx,EN_ZERO,EN_BRDY,EN_EOS,EN_TB,EN_TA */
+ YM2608IRQMaskWrite(OPN, F2608, v);
+ break;
+ default:
+ OPNWriteMode(OPN,addr,v);
+ }
+ break;
+ default: /* OPN section */
+ OPNWriteReg(OPN,addr,v);
+ }
+ break;
+
+ case 2: /* address port 1 */
+ OPN->ST.address = v;
+ F2608->addr_A1 = 1;
+ break;
+
+ case 3: /* data port 1 */
+ if (F2608->addr_A1 != 1)
+ break; /* verified on real YM2608 */
+
+ addr = OPN->ST.address;
+ F2608->REGS[addr | 0x100] = v;
+ switch( addr & 0xf0 )
+ {
+ case 0x00: /* DELTAT PORT */
+ switch( addr )
+ {
+ case 0x0e: /* DAC data */
+#ifdef _DEBUG
+ logerror("YM2608: write to DAC data (unimplemented) value=%02x\n",v);
+#endif
+ break;
+ default:
+ /* 0x00-0x0d */
+ YM_DELTAT_ADPCM_Write(&F2608->deltaT,addr,v);
+ }
+ break;
+ case 0x10: /* IRQ Flag control */
+ if( addr == 0x10 )
+ {
+ YM2608IRQFlagWrite(OPN, F2608, v);
+ }
+ break;
+ default:
+ OPNWriteReg(OPN,addr | 0x100,v);
+ }
+ }
+ return OPN->ST.irq;
+}
+
+UINT8 ym2608_read(void *chip,int a)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+ int addr = F2608->OPN.ST.address;
+ UINT8 ret = 0;
+
+ switch( a&3 )
+ {
+ case 0: /* status 0 : YM2203 compatible */
+ /* BUSY:x:x:x:x:x:FLAGB:FLAGA */
+ ret = FM_STATUS_FLAG(&F2608->OPN.ST) & 0x83;
+ break;
+
+ case 1: /* status 0, ID */
+ if( addr < 16 ) ret = (*F2608->OPN.ST.SSG->read)(F2608->OPN.ST.param);
+ else if(addr == 0xff) ret = 0x01; /* ID code */
+ break;
+
+ case 2: /* status 1 : status 0 + ADPCM status */
+ /* BUSY : x : PCMBUSY : ZERO : BRDY : EOS : FLAGB : FLAGA */
+ ret = (FM_STATUS_FLAG(&F2608->OPN.ST) & (F2608->flagmask|0x80)) | ((F2608->deltaT.PCM_BSY & 1)<<5) ;
+ break;
+
+ case 3:
+ if(addr == 0x08)
+ {
+ ret = YM_DELTAT_ADPCM_Read(&F2608->deltaT);
+ }
+ else
+ {
+ if(addr == 0x0f)
+ {
+#ifdef _DEBUG
+ logerror("YM2608 A/D convertion is accessed but not implemented !\n");
+#endif
+ ret = 0x80; /* 2's complement PCM data - result from A/D convertion */
+ }
+ }
+ break;
+ }
+ return ret;
+}
+
+int ym2608_timer_over(void *chip,int c)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+
+ switch(c)
+ {
+#if 0
+ case 2:
+ { /* BUFRDY flag */
+ YM_DELTAT_BRDY_callback( &F2608->deltaT );
+ }
+ break;
+#endif
+ case 1:
+ { /* Timer B */
+ TimerBOver( &(F2608->OPN.ST) );
+ }
+ break;
+ case 0:
+ { /* Timer A */
+ /* timer update */
+ TimerAOver( &(F2608->OPN.ST) );
+ /* CSM mode key,TL controll */
+ if( F2608->OPN.ST.mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ CSMKeyControll( F2608->OPN.type, &(F2608->CH[2]) );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return F2608->OPN.ST.irq;
+}
+
+void ym2608_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart,
+ offs_t DataLength, const UINT8* ROMData)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+
+ switch(rom_id)
+ {
+ case 0x01: // ADPCM
+ // unused, it's constant
+ break;
+ case 0x02: // DELTA-T
+ if (F2608->deltaT.memory_size != ROMSize)
+ {
+ F2608->deltaT.memory = (UINT8*)realloc(F2608->deltaT.memory, ROMSize);
+ F2608->deltaT.memory_size = ROMSize;
+ memset(F2608->deltaT.memory, 0xFF, ROMSize);
+ }
+ if (DataStart > ROMSize)
+ return;
+ if (DataStart + DataLength > ROMSize)
+ DataLength = ROMSize - DataStart;
+
+ memcpy(F2608->deltaT.memory + DataStart, ROMData, DataLength);
+ break;
+ }
+
+ return;
+}
+
+void ym2608_set_mutemask(void *chip, UINT32 MuteMask)
+{
+ YM2608 *F2608 = (YM2608 *)chip;
+ UINT8 CurChn;
+
+ for (CurChn = 0; CurChn < 6; CurChn ++)
+ F2608->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
+ for (CurChn = 0; CurChn < 6; CurChn ++)
+ F2608->adpcm[CurChn].Muted = (MuteMask >> (CurChn + 6)) & 0x01;
+ F2608->MuteDeltaT = (MuteMask >> 12) & 0x01;
+
+ return;
+}
+#endif /* BUILD_YM2608 */
+
+
+
+#if (BUILD_YM2610||BUILD_YM2610B)
+/* YM2610(OPNB) */
+
+/* Generate samples for one of the YM2610s */
+void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ FM_OPN *OPN = &F2610->OPN;
+ YM_DELTAT *DELTAT = &F2610->deltaT;
+ int i,j;
+ FMSAMPLE *bufL,*bufR;
+ FM_CH *cch[4];
+ INT32 *out_fm = OPN->out_fm;
+
+ /* buffer setup */
+ bufL = buffer[0];
+ bufR = buffer[1];
+
+ cch[0] = &F2610->CH[1];
+ cch[1] = &F2610->CH[2];
+ cch[2] = &F2610->CH[4];
+ cch[3] = &F2610->CH[5];
+
+#ifdef YM2610B_WARNING
+#define FM_KEY_IS(SLOT) ((SLOT)->key)
+#define FM_MSG_YM2610B "YM2610-%p.CH%d is playing,Check whether the type of the chip is YM2610B\n"
+ /* Check YM2610B warning message */
+ if( FM_KEY_IS(&F2610->CH[0].SLOT[3]) )
+ {
+ LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,0));
+ FM_KEY_IS(&F2610->CH[3].SLOT[3]) = 0;
+ }
+ if( FM_KEY_IS(&F2610->CH[3].SLOT[3]) )
+ {
+ LOG(LOG_WAR,(FM_MSG_YM2610B,F2610->OPN.ST.param,3));
+ FM_KEY_IS(&F2610->CH[3].SLOT[3]) = 0;
+ }
+#endif
+
+ /* refresh PG and EG */
+ refresh_fc_eg_chan( OPN, cch[0] );
+ if( (OPN->ST.mode & 0xc0) )
+ {
+ /* 3SLOT MODE */
+ if( cch[1]->SLOT[SLOT1].Incr==-1)
+ {
+ refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
+ refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
+ refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
+ refresh_fc_eg_slot(OPN, &cch[1]->SLOT[SLOT4] , cch[1]->fc , cch[1]->kcode );
+ }
+ }
+ else
+ refresh_fc_eg_chan( OPN, cch[1] );
+ refresh_fc_eg_chan( OPN, cch[2] );
+ refresh_fc_eg_chan( OPN, cch[3] );
+
+ /* buffering */
+ for(i=0; i < length ; i++)
+ {
+
+ advance_lfo(OPN);
+
+ /* clear output acc. */
+ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0;
+ OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0;
+ /* clear outputs */
+ out_fm[1] = 0;
+ out_fm[2] = 0;
+ out_fm[4] = 0;
+ out_fm[5] = 0;
+
+ /* advance envelope generator */
+ OPN->eg_timer += OPN->eg_timer_add;
+ while (OPN->eg_timer >= OPN->eg_timer_overflow)
+ {
+ OPN->eg_timer -= OPN->eg_timer_overflow;
+ OPN->eg_cnt++;
+
+ advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]);
+ }
+
+ /* calculate FM */
+ chan_calc(OPN, cch[0], 1 ); /*remapped to 1*/
+ chan_calc(OPN, cch[1], 2 ); /*remapped to 2*/
+ chan_calc(OPN, cch[2], 4 ); /*remapped to 4*/
+ chan_calc(OPN, cch[3], 5 ); /*remapped to 5*/
+
+ /* deltaT ADPCM */
+ if( DELTAT->portstate&0x80 && ! F2610->MuteDeltaT )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+
+ /* ADPCMA */
+ for( j = 0; j < 6; j++ )
+ {
+ if( F2610->adpcm[j].flag )
+ ADPCMA_calc_chan( F2610, &F2610->adpcm[j]);
+ }
+
+ /* buffering */
+ {
+ int lt,rt;
+
+ /*lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER];
+ rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER];
+ lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9;
+ rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9;
+
+
+ lt += ((out_fm[1]>>1) & OPN->pan[2]); // the shift right was verified on real chip
+ rt += ((out_fm[1]>>1) & OPN->pan[3]);
+ lt += ((out_fm[2]>>1) & OPN->pan[4]);
+ rt += ((out_fm[2]>>1) & OPN->pan[5]);
+
+ lt += ((out_fm[4]>>1) & OPN->pan[8]);
+ rt += ((out_fm[4]>>1) & OPN->pan[9]);
+ lt += ((out_fm[5]>>1) & OPN->pan[10]);
+ rt += ((out_fm[5]>>1) & OPN->pan[11]);*/
+
+ lt = (OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]) << 1;
+ rt = (OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]) << 1;
+ lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>8;
+ rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>8;
+
+
+ lt += (out_fm[1] & OPN->pan[2]);
+ rt += (out_fm[1] & OPN->pan[3]);
+ lt += (out_fm[2] & OPN->pan[4]);
+ rt += (out_fm[2] & OPN->pan[5]);
+
+ lt += (out_fm[4] & OPN->pan[8]);
+ rt += (out_fm[4] & OPN->pan[9]);
+ lt += (out_fm[5] & OPN->pan[10]);
+ rt += (out_fm[5] & OPN->pan[11]);
+
+
+ lt >>= FINAL_SH;
+ rt >>= FINAL_SH;
+
+ //Limit( lt, MAXOUT, MINOUT );
+ //Limit( rt, MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ SAVE_ALL_CHANNELS
+ #endif
+
+ /* buffering */
+ bufL[i] = lt;
+ bufR[i] = rt;
+ }
+
+ /* timer A control */
+ INTERNAL_TIMER_A( &OPN->ST , cch[1] )
+ }
+ INTERNAL_TIMER_B(&OPN->ST,length)
+
+}
+
+#if BUILD_YM2610B
+/* Generate samples for one of the YM2610Bs */
+void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ FM_OPN *OPN = &F2610->OPN;
+ YM_DELTAT *DELTAT = &F2610->deltaT;
+ int i,j;
+ FMSAMPLE *bufL,*bufR;
+ FM_CH *cch[6];
+ INT32 *out_fm = OPN->out_fm;
+
+ /* buffer setup */
+ bufL = buffer[0];
+ bufR = buffer[1];
+
+ cch[0] = &F2610->CH[0];
+ cch[1] = &F2610->CH[1];
+ cch[2] = &F2610->CH[2];
+ cch[3] = &F2610->CH[3];
+ cch[4] = &F2610->CH[4];
+ cch[5] = &F2610->CH[5];
+
+ /* refresh PG and EG */
+ refresh_fc_eg_chan( OPN, cch[0] );
+ refresh_fc_eg_chan( OPN, cch[1] );
+ if( (OPN->ST.mode & 0xc0) )
+ {
+ /* 3SLOT MODE */
+ if( cch[2]->SLOT[SLOT1].Incr==-1)
+ {
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode );
+ }
+ }
+ else
+ refresh_fc_eg_chan( OPN, cch[2] );
+ refresh_fc_eg_chan( OPN, cch[3] );
+ refresh_fc_eg_chan( OPN, cch[4] );
+ refresh_fc_eg_chan( OPN, cch[5] );
+
+ /* buffering */
+ for(i=0; i < length ; i++)
+ {
+
+ advance_lfo(OPN);
+
+ /* clear output acc. */
+ OPN->out_adpcm[OUTD_LEFT] = OPN->out_adpcm[OUTD_RIGHT] = OPN->out_adpcm[OUTD_CENTER] = 0;
+ OPN->out_delta[OUTD_LEFT] = OPN->out_delta[OUTD_RIGHT] = OPN->out_delta[OUTD_CENTER] = 0;
+ /* clear outputs */
+ out_fm[0] = 0;
+ out_fm[1] = 0;
+ out_fm[2] = 0;
+ out_fm[3] = 0;
+ out_fm[4] = 0;
+ out_fm[5] = 0;
+
+ /* advance envelope generator */
+ OPN->eg_timer += OPN->eg_timer_add;
+ while (OPN->eg_timer >= OPN->eg_timer_overflow)
+ {
+ OPN->eg_timer -= OPN->eg_timer_overflow;
+ OPN->eg_cnt++;
+
+ advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]);
+ }
+
+ /* calculate FM */
+ chan_calc(OPN, cch[0], 0 );
+ chan_calc(OPN, cch[1], 1 );
+ chan_calc(OPN, cch[2], 2 );
+ chan_calc(OPN, cch[3], 3 );
+ chan_calc(OPN, cch[4], 4 );
+ chan_calc(OPN, cch[5], 5 );
+
+ /* deltaT ADPCM */
+ if( DELTAT->portstate&0x80 && ! F2610->MuteDeltaT )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+
+ /* ADPCMA */
+ for( j = 0; j < 6; j++ )
+ {
+ if( F2610->adpcm[j].flag )
+ ADPCMA_calc_chan( F2610, &F2610->adpcm[j]);
+ }
+
+ /* buffering */
+ {
+ int lt,rt;
+
+ /*lt = OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER];
+ rt = OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER];
+ lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>9;
+ rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>9;
+
+ lt += ((out_fm[0]>>1) & OPN->pan[0]); // the shift right is verified on YM2610
+ rt += ((out_fm[0]>>1) & OPN->pan[1]);
+ lt += ((out_fm[1]>>1) & OPN->pan[2]);
+ rt += ((out_fm[1]>>1) & OPN->pan[3]);
+ lt += ((out_fm[2]>>1) & OPN->pan[4]);
+ rt += ((out_fm[2]>>1) & OPN->pan[5]);
+ lt += ((out_fm[3]>>1) & OPN->pan[6]);
+ rt += ((out_fm[3]>>1) & OPN->pan[7]);
+ lt += ((out_fm[4]>>1) & OPN->pan[8]);
+ rt += ((out_fm[4]>>1) & OPN->pan[9]);
+ lt += ((out_fm[5]>>1) & OPN->pan[10]);
+ rt += ((out_fm[5]>>1) & OPN->pan[11]);*/
+ lt = (OPN->out_adpcm[OUTD_LEFT] + OPN->out_adpcm[OUTD_CENTER]) << 1;
+ rt = (OPN->out_adpcm[OUTD_RIGHT] + OPN->out_adpcm[OUTD_CENTER]) << 1;
+ lt += (OPN->out_delta[OUTD_LEFT] + OPN->out_delta[OUTD_CENTER])>>8;
+ rt += (OPN->out_delta[OUTD_RIGHT] + OPN->out_delta[OUTD_CENTER])>>8;
+
+ lt += (out_fm[0] & OPN->pan[0]);
+ rt += (out_fm[0] & OPN->pan[1]);
+ lt += (out_fm[1] & OPN->pan[2]);
+ rt += (out_fm[1] & OPN->pan[3]);
+ lt += (out_fm[2] & OPN->pan[4]);
+ rt += (out_fm[2] & OPN->pan[5]);
+ lt += (out_fm[3] & OPN->pan[6]);
+ rt += (out_fm[3] & OPN->pan[7]);
+ lt += (out_fm[4] & OPN->pan[8]);
+ rt += (out_fm[4] & OPN->pan[9]);
+ lt += (out_fm[5] & OPN->pan[10]);
+ rt += (out_fm[5] & OPN->pan[11]);
+
+
+ lt >>= FINAL_SH;
+ rt >>= FINAL_SH;
+
+ //Limit( lt, MAXOUT, MINOUT );
+ //Limit( rt, MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ SAVE_ALL_CHANNELS
+ #endif
+
+ /* buffering */
+ bufL[i] = lt;
+ bufR[i] = rt;
+ }
+
+ /* timer A control */
+ INTERNAL_TIMER_A( &OPN->ST , cch[2] )
+ }
+ INTERNAL_TIMER_B(&OPN->ST,length)
+
+}
+#endif /* BUILD_YM2610B */
+
+
+static void YM2610_deltat_status_set(void *chip, UINT8 changebits)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ F2610->adpcm_arrivedEndAddress |= changebits;
+}
+static void YM2610_deltat_status_reset(void *chip, UINT8 changebits)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ F2610->adpcm_arrivedEndAddress &= (~changebits);
+}
+
+//void *ym2610_init(void *param, const device_config *device, int clock, int rate,
+// void *pcmroma,int pcmsizea,void *pcmromb,int pcmsizeb,
+// FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg)
+void *ym2610_init(void *param, int clock, int rate, const ssg_callbacks *ssg)
+{
+ YM2610 *F2610;
+
+ /* allocate extend state space */
+ if( (F2610 = (YM2610 *)malloc(sizeof(YM2610)))==NULL)
+ return NULL;
+ /* clear */
+ memset(F2610,0,sizeof(YM2610));
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ free( F2610 );
+ return NULL;
+ }
+
+ /* FM */
+ F2610->OPN.ST.param = param;
+ F2610->OPN.type = TYPE_YM2610;
+ F2610->OPN.P_CH = F2610->CH;
+ //F2610->OPN.ST.device = device;
+ F2610->OPN.ST.clock = clock;
+ F2610->OPN.ST.rate = rate;
+ /* Extend handler */
+ F2610->OPN.ST.SSG = ssg;
+ /* ADPCM */
+ //F2610->pcmbuf = (const UINT8 *)pcmroma;
+ //F2610->pcm_size = pcmsizea;
+ F2610->pcmbuf = NULL;
+ F2610->pcm_size = 0x00;
+ /* DELTA-T */
+ //F2610->deltaT.memory = (UINT8 *)pcmromb;
+ //F2610->deltaT.memory_size = pcmsizeb;
+ F2610->deltaT.memory = NULL;
+ F2610->deltaT.memory_size = 0x00;
+
+ F2610->deltaT.status_set_handler = YM2610_deltat_status_set;
+ F2610->deltaT.status_reset_handler = YM2610_deltat_status_reset;
+ F2610->deltaT.status_change_which_chip = F2610;
+ F2610->deltaT.status_change_EOS_bit = 0x80; /* status flag: set bit7 on End Of Sample */
+
+ Init_ADPCMATable();
+#ifdef __STATE_H__
+ YM2610_save_state(F2610, device);
+#endif
+ return F2610;
+}
+
+/* shut down emulator */
+void ym2610_shutdown(void *chip)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+
+ free(F2610->pcmbuf); F2610->pcmbuf = NULL;
+ free(F2610->deltaT.memory); F2610->deltaT.memory = NULL;
+
+ FMCloseTable();
+ free(F2610);
+}
+
+/* reset one of chip */
+void ym2610_reset_chip(void *chip)
+{
+ int i;
+ YM2610 *F2610 = (YM2610 *)chip;
+ FM_OPN *OPN = &F2610->OPN;
+ YM_DELTAT *DELTAT = &F2610->deltaT;
+
+ /*astring name;
+ device_t* dev = F2610->OPN.ST.device;*/
+
+ /* setup PCM buffers again */
+ /*name.printf("%s",dev->tag());
+ F2610->pcmbuf = (const UINT8 *)dev->machine->region(name)->base();
+ F2610->pcm_size = dev->machine->region(name)->bytes();
+ name.printf("%s.deltat",dev->tag());
+ F2610->deltaT.memory = (UINT8 *)dev->machine->region(name)->base();
+ if(F2610->deltaT.memory == NULL)
+ {
+ F2610->deltaT.memory = (UINT8*)F2610->pcmbuf;
+ F2610->deltaT.memory_size = F2610->pcm_size;
+ }
+ else
+ F2610->deltaT.memory_size = dev->machine->region(name)->bytes();*/
+ F2610->pcmbuf = NULL;
+ F2610->pcm_size = 0x00;
+ F2610->deltaT.memory = NULL;
+ F2610->deltaT.memory_size = 0x00;
+
+ /* Reset Prescaler */
+ OPNSetPres( OPN, 6*24, 6*24, 4*2); /* OPN 1/6 , SSG 1/4 */
+ /* reset SSG section */
+ (*OPN->ST.SSG->reset)(OPN->ST.param);
+ /* status clear */
+ FM_IRQMASK_SET(&OPN->ST,0x03);
+ FM_BUSY_CLEAR(&OPN->ST);
+ OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */
+
+ OPN->eg_timer = 0;
+ OPN->eg_cnt = 0;
+
+ FM_STATUS_RESET(&OPN->ST, 0xff);
+
+ reset_channels( &OPN->ST , F2610->CH , 6 );
+ /* reset OPerator paramater */
+ for(i = 0xb6 ; i >= 0xb4 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0xc0);
+ OPNWriteReg(OPN,i|0x100,0xc0);
+ }
+ for(i = 0xb2 ; i >= 0x30 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0);
+ OPNWriteReg(OPN,i|0x100,0);
+ }
+ for(i = 0x26 ; i >= 0x20 ; i-- ) OPNWriteReg(OPN,i,0);
+ /**** ADPCM work initial ****/
+ for( i = 0; i < 6 ; i++ )
+ {
+ F2610->adpcm[i].step = (UINT32)((float)(1<OPN.ST.freqbase)/3.0);
+ F2610->adpcm[i].now_addr = 0;
+ F2610->adpcm[i].now_step = 0;
+ F2610->adpcm[i].start = 0;
+ F2610->adpcm[i].end = 0;
+ /* F2610->adpcm[i].delta = 21866; */
+ F2610->adpcm[i].vol_mul = 0;
+ F2610->adpcm[i].pan = &OPN->out_adpcm[OUTD_CENTER]; /* default center */
+ F2610->adpcm[i].flagMask = 1<adpcm[i].flag = 0;
+ F2610->adpcm[i].adpcm_acc = 0;
+ F2610->adpcm[i].adpcm_step= 0;
+ F2610->adpcm[i].adpcm_out = 0;
+ }
+ F2610->adpcmTL = 0x3f;
+
+ F2610->adpcm_arrivedEndAddress = 0;
+
+ /* DELTA-T unit */
+ DELTAT->freqbase = OPN->ST.freqbase;
+ DELTAT->output_pointer = OPN->out_delta;
+ DELTAT->portshift = 8; /* allways 8bits shift */
+ DELTAT->output_range = 1<<23;
+ YM_DELTAT_ADPCM_Reset(DELTAT,OUTD_CENTER,YM_DELTAT_EMULATION_MODE_YM2610);
+}
+
+/* YM2610 write */
+/* n = number */
+/* a = address */
+/* v = value */
+int ym2610_write(void *chip, int a, UINT8 v)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ FM_OPN *OPN = &F2610->OPN;
+ int addr;
+ int ch;
+
+ v &= 0xff; /* adjust to 8 bit bus */
+
+ switch( a&3 )
+ {
+ case 0: /* address port 0 */
+ OPN->ST.address = v;
+ F2610->addr_A1 = 0;
+
+ /* Write register to SSG emulator */
+ if( v < 16 ) (*OPN->ST.SSG->write)(OPN->ST.param,0,v);
+ break;
+
+ case 1: /* data port 0 */
+ if (F2610->addr_A1 != 0)
+ break; /* verified on real YM2608 */
+
+ addr = OPN->ST.address;
+ F2610->REGS[addr] = v;
+ switch(addr & 0xf0)
+ {
+ case 0x00: /* SSG section */
+ /* Write data to SSG emulator */
+ (*OPN->ST.SSG->write)(OPN->ST.param,a,v);
+ break;
+ case 0x10: /* DeltaT ADPCM */
+ switch(addr)
+ {
+ case 0x10: /* control 1 */
+ case 0x11: /* control 2 */
+ case 0x12: /* start address L */
+ case 0x13: /* start address H */
+ case 0x14: /* stop address L */
+ case 0x15: /* stop address H */
+
+ case 0x19: /* delta-n L */
+ case 0x1a: /* delta-n H */
+ case 0x1b: /* volume */
+ {
+ YM_DELTAT_ADPCM_Write(&F2610->deltaT,addr-0x10,v);
+ }
+ break;
+
+ case 0x1c: /* FLAG CONTROL : Extend Status Clear/Mask */
+ {
+ UINT8 statusmask = ~v;
+ /* set arrived flag mask */
+ for(ch=0;ch<6;ch++)
+ F2610->adpcm[ch].flagMask = statusmask&(1<deltaT.status_change_EOS_bit = statusmask & 0x80; /* status flag: set bit7 on End Of Sample */
+
+ /* clear arrived flag */
+ F2610->adpcm_arrivedEndAddress &= statusmask;
+ }
+ break;
+
+ default:
+#ifdef _DEBUG
+ logerror("YM2610: write to unknown deltat register %02x val=%02x\n",addr,v);
+#endif
+ break;
+ }
+
+ break;
+ case 0x20: /* Mode Register */
+ OPNWriteMode(OPN,addr,v);
+ break;
+ default: /* OPN section */
+ /* write register */
+ OPNWriteReg(OPN,addr,v);
+ }
+ break;
+
+ case 2: /* address port 1 */
+ OPN->ST.address = v;
+ F2610->addr_A1 = 1;
+ break;
+
+ case 3: /* data port 1 */
+ if (F2610->addr_A1 != 1)
+ break; /* verified on real YM2608 */
+
+ addr = OPN->ST.address;
+ F2610->REGS[addr | 0x100] = v;
+ if( addr < 0x30 )
+ /* 100-12f : ADPCM A section */
+ FM_ADPCMAWrite(F2610,addr,v);
+ else
+ OPNWriteReg(OPN,addr | 0x100,v);
+ }
+ return OPN->ST.irq;
+}
+
+UINT8 ym2610_read(void *chip,int a)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ int addr = F2610->OPN.ST.address;
+ UINT8 ret = 0;
+
+ switch( a&3)
+ {
+ case 0: /* status 0 : YM2203 compatible */
+ ret = FM_STATUS_FLAG(&F2610->OPN.ST) & 0x83;
+ break;
+ case 1: /* data 0 */
+ if( addr < 16 ) ret = (*F2610->OPN.ST.SSG->read)(F2610->OPN.ST.param);
+ if( addr == 0xff ) ret = 0x01;
+ break;
+ case 2: /* status 1 : ADPCM status */
+ /* ADPCM STATUS (arrived End Address) */
+ /* B,--,A5,A4,A3,A2,A1,A0 */
+ /* B = ADPCM-B(DELTA-T) arrived end address */
+ /* A0-A5 = ADPCM-A arrived end address */
+ ret = F2610->adpcm_arrivedEndAddress;
+ break;
+ case 3:
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+int ym2610_timer_over(void *chip,int c)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+
+ if( c )
+ { /* Timer B */
+ TimerBOver( &(F2610->OPN.ST) );
+ }
+ else
+ { /* Timer A */
+ /* timer update */
+ TimerAOver( &(F2610->OPN.ST) );
+ /* CSM mode key,TL controll */
+ if( F2610->OPN.ST.mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ CSMKeyControll( F2610->OPN.type, &(F2610->CH[2]) );
+ }
+ }
+ return F2610->OPN.ST.irq;
+}
+
+void ym2610_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart,
+ offs_t DataLength, const UINT8* ROMData)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+
+ switch(rom_id)
+ {
+ case 0x01: // ADPCM
+ if (F2610->pcm_size != ROMSize)
+ {
+ F2610->pcmbuf = (UINT8*)realloc(F2610->pcmbuf, ROMSize);
+ F2610->pcm_size = ROMSize;
+ memset(F2610->pcmbuf, 0xFF, ROMSize);
+ }
+ if (DataStart > ROMSize)
+ return;
+ if (DataStart + DataLength > ROMSize)
+ DataLength = ROMSize - DataStart;
+
+ memcpy(F2610->pcmbuf + DataStart, ROMData, DataLength);
+ break;
+ case 0x02: // DELTA-T
+ if (F2610->deltaT.memory_size != ROMSize)
+ {
+ F2610->deltaT.memory = (UINT8*)realloc(F2610->deltaT.memory, ROMSize);
+ F2610->deltaT.memory_size = ROMSize;
+ memset(F2610->deltaT.memory, 0xFF, ROMSize);
+ }
+ if (DataStart > ROMSize)
+ return;
+ if (DataStart + DataLength > ROMSize)
+ DataLength = ROMSize - DataStart;
+
+ memcpy(F2610->deltaT.memory + DataStart, ROMData, DataLength);
+ break;
+ }
+
+ return;
+}
+
+void ym2610_set_mutemask(void *chip, UINT32 MuteMask)
+{
+ YM2610 *F2610 = (YM2610 *)chip;
+ UINT8 CurChn;
+
+ for (CurChn = 0; CurChn < 6; CurChn ++)
+ F2610->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
+ for (CurChn = 0; CurChn < 6; CurChn ++)
+ F2610->adpcm[CurChn].Muted = (MuteMask >> (CurChn + 6)) & 0x01;
+ F2610->MuteDeltaT = (MuteMask >> 12) & 0x01;
+
+ return;
+}
+#endif /* (BUILD_YM2610||BUILD_YM2610B) */
diff --git a/Frameworks/GME/gme/fm.h b/Frameworks/GME/gme/fm.h
new file mode 100644
index 000000000..bee9f9095
--- /dev/null
+++ b/Frameworks/GME/gme/fm.h
@@ -0,0 +1,178 @@
+/*
+ File: fm.h -- header file for software emulation for FM sound generator
+
+*/
+
+#pragma once
+
+#include "mamedef.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BUILD_YM2203 1
+#define BUILD_YM2608 1
+#define BUILD_YM2610 1
+#define BUILD_YM2610B 1
+#define BUILD_YM2612 1
+#define BUILD_YM3438 1
+
+/* select bit size of output : 8 or 16 */
+#define FM_SAMPLE_BITS 16
+
+/* select timer system internal or external */
+#define FM_INTERNAL_TIMER 1
+
+/* --- speedup optimize --- */
+/* busy flag enulation , The definition of FM_GET_TIME_NOW() is necessary. */
+//#define FM_BUSY_FLAG_SUPPORT 1
+
+
+
+typedef stream_sample_t FMSAMPLE;
+/*
+#if (FM_SAMPLE_BITS==16)
+typedef INT16 FMSAMPLE;
+#endif
+#if (FM_SAMPLE_BITS==8)
+typedef unsigned char FMSAMPLE;
+#endif
+*/
+
+/* FM_TIMERHANDLER : Stop or Start timer */
+/* int n = chip number */
+/* int c = Channel 0=TimerA,1=TimerB */
+/* int count = timer count (0=stop) */
+/* doube stepTime = step time of one count (sec.)*/
+
+/* FM_IRQHHANDLER : IRQ level changing sense */
+/* int n = chip number */
+/* int irq = IRQ level 0=OFF,1=ON */
+
+typedef struct _ssg_callbacks ssg_callbacks;
+struct _ssg_callbacks
+{
+ void (*set_clock)(void *param, int clock);
+ void (*write)(void *param, int address, int data);
+ int (*read)(void *param);
+ void (*reset)(void *param);
+};
+
+#if BUILD_YM2203
+/* -------------------- YM2203(OPN) Interface -------------------- */
+
+/*
+** Initialize YM2203 emulator(s).
+**
+** 'num' is the number of virtual YM2203's to allocate
+** 'baseclock'
+** 'rate' is sampling rate
+** 'TimerHandler' timer callback handler when timer start and clear
+** 'IRQHandler' IRQ callback handler when changed IRQ level
+** return 0 = success
+*/
+//void * ym2203_init(void *param, const device_config *device, int baseclock, int rate,
+// FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);
+void * ym2203_init(void *param, int baseclock, int rate, const ssg_callbacks *ssg);
+
+/*
+** shutdown the YM2203 emulators
+*/
+void ym2203_shutdown(void *chip);
+
+/*
+** reset all chip registers for YM2203 number 'num'
+*/
+void ym2203_reset_chip(void *chip);
+
+/*
+** update one of chip
+*/
+void ym2203_update_one(void *chip, FMSAMPLE **buffer, int length);
+
+/*
+** Write
+** return : InterruptLevel
+*/
+int ym2203_write(void *chip,int a,unsigned char v);
+
+/*
+** Read
+** return : InterruptLevel
+*/
+unsigned char ym2203_read(void *chip,int a);
+
+/*
+** Timer OverFlow
+*/
+int ym2203_timer_over(void *chip, int c);
+
+/*
+** State Save
+*/
+void ym2203_postload(void *chip);
+
+void ym2203_set_mutemask(void *chip, UINT32 MuteMask);
+#endif /* BUILD_YM2203 */
+
+#if BUILD_YM2608
+/* -------------------- YM2608(OPNA) Interface -------------------- */
+//void * ym2608_init(void *param, const device_config *device, int baseclock, int rate,
+// void *pcmroma,int pcmsizea,
+// FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);
+void * ym2608_init(void *param, int baseclock, int rate, const ssg_callbacks *ssg);
+void ym2608_shutdown(void *chip);
+void ym2608_reset_chip(void *chip);
+void ym2608_update_one(void *chip, FMSAMPLE **buffer, int length);
+
+int ym2608_write(void *chip, int a,unsigned char v);
+unsigned char ym2608_read(void *chip,int a);
+void ym2608_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart,
+ offs_t DataLength, const UINT8* ROMData);
+
+void ym2608_set_mutemask(void *chip, UINT32 MuteMask);
+#endif /* BUILD_YM2608 */
+
+#if (BUILD_YM2610||BUILD_YM2610B)
+/* -------------------- YM2610(OPNB) Interface -------------------- */
+//void * ym2610_init(void *param, const device_config *device, int baseclock, int rate,
+// void *pcmroma,int pcmasize,void *pcmromb,int pcmbsize,
+// FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler, const ssg_callbacks *ssg);
+void * ym2610_init(void *param, int baseclock, int rate, const ssg_callbacks *ssg);
+void ym2610_shutdown(void *chip);
+void ym2610_reset_chip(void *chip);
+void ym2610_update_one(void *chip, FMSAMPLE **buffer, int length);
+
+#if BUILD_YM2610B
+void ym2610b_update_one(void *chip, FMSAMPLE **buffer, int length);
+#endif /* BUILD_YM2610B */
+
+int ym2610_write(void *chip, int a,unsigned char v);
+unsigned char ym2610_read(void *chip,int a);
+int ym2610_timer_over(void *chip, int c );
+void ym2610_postload(void *chip);
+void ym2610_write_pcmrom(void *chip, UINT8 rom_id, offs_t ROMSize, offs_t DataStart,
+ offs_t DataLength, const UINT8* ROMData);
+
+void ym2610_set_mutemask(void *chip, UINT32 MuteMask);
+#endif /* (BUILD_YM2610||BUILD_YM2610B) */
+
+#if (BUILD_YM2612||BUILD_YM3438)
+//void * ym2612_init(void *param, const device_config *device, int baseclock, int rate,
+// FM_TIMERHANDLER TimerHandler,FM_IRQHANDLER IRQHandler);
+void * ym2612_init(int baseclock, int rate);
+void ym2612_shutdown(void *chip);
+void ym2612_reset_chip(void *chip);
+void ym2612_update_one(void *chip, FMSAMPLE **buffer, int length);
+
+int ym2612_write(void *chip, int a,unsigned char v);
+unsigned char ym2612_read(void *chip,int a);
+
+void ym2612_set_mutemask(void *chip, UINT32 MuteMask);
+void ym2612_setoptions(void *chip, UINT8 Flags);
+#endif /* (BUILD_YM2612||BUILD_YM3438) */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Frameworks/GME/gme/fm2612.c b/Frameworks/GME/gme/fm2612.c
new file mode 100644
index 000000000..72621f94c
--- /dev/null
+++ b/Frameworks/GME/gme/fm2612.c
@@ -0,0 +1,2634 @@
+/*
+**
+** File: fm2612.c -- software implementation of Yamaha YM2612 FM sound generator
+** Split from fm.c to keep 2612 fixes from infecting other OPN chips
+**
+** Copyright Jarek Burczynski (bujar at mame dot net)
+** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+**
+** Version 1.5.1 (Genesis Plus GX ym2612.c rev. 368)
+**
+*/
+
+/*
+** History:
+**
+** 2006~2009 Eke-Eke (Genesis Plus GX):
+** Huge thanks to Nemesis, lot of those fixes came from his tests on Sega Genesis hardware
+** More informations at http://gendev.spritesmind.net/forum/viewtopic.php?t=386
+**
+** TODO:
+**
+** - core documentation
+** - BUSY flag support
+**
+** CHANGELOG:
+**
+** - fixed LFO implementation:
+** .added support for CH3 special mode: fixes various sound effects (birds in Warlock, bug sound in Aladdin...)
+** .modified LFO behavior when switched off (AM/PM current level is held) and on (LFO step is reseted): fixes intro in Spider-Man & Venom : Separation Anxiety
+** .improved LFO timing accuracy: now updated AFTER sample output, like EG/PG updates, and without any precision loss anymore.
+** - improved internal timers emulation
+** - adjusted lowest EG rates increment values
+** - fixed Attack Rate not being updated in some specific cases (Batman & Robin intro)
+** - fixed EG behavior when Attack Rate is maximal
+** - fixed EG behavior when SL=0 (Mega Turrican tracks 03,09...) or/and Key ON occurs at minimal attenuation
+** - implemented EG output immediate changes on register writes
+** - fixed YM2612 initial values (after the reset): fixes missing intro in B.O.B
+** - implemented Detune overflow (Ariel, Comix Zone, Shaq Fu, Spiderman & many other games using GEMS sound engine)
+** - implemented accurate CSM mode emulation
+** - implemented accurate SSG-EG emulation (Asterix, Beavis&Butthead, Bubba'n Stix & many other games)
+** - implemented accurate address/data ports behavior
+**
+** 06-23-2007 Zsolt Vasvari:
+** - changed the timing not to require the use of floating point calculations
+**
+** 03-08-2003 Jarek Burczynski:
+** - fixed YM2608 initial values (after the reset)
+** - fixed flag and irqmask handling (YM2608)
+** - fixed BUFRDY flag handling (YM2608)
+**
+** 14-06-2003 Jarek Burczynski:
+** - implemented all of the YM2608 status register flags
+** - implemented support for external memory read/write via YM2608
+** - implemented support for deltat memory limit register in YM2608 emulation
+**
+** 22-05-2003 Jarek Burczynski:
+** - fixed LFO PM calculations (copy&paste bugfix)
+**
+** 08-05-2003 Jarek Burczynski:
+** - fixed SSG support
+**
+** 22-04-2003 Jarek Burczynski:
+** - implemented 100% correct LFO generator (verified on real YM2610 and YM2608)
+**
+** 15-04-2003 Jarek Burczynski:
+** - added support for YM2608's register 0x110 - status mask
+**
+** 01-12-2002 Jarek Burczynski:
+** - fixed register addressing in YM2608, YM2610, YM2610B chips. (verified on real YM2608)
+** The addressing patch used for early Neo-Geo games can be removed now.
+**
+** 26-11-2002 Jarek Burczynski, Nicola Salmoria:
+** - recreated YM2608 ADPCM ROM using data from real YM2608's output which leads to:
+** - added emulation of YM2608 drums.
+** - output of YM2608 is two times lower now - same as YM2610 (verified on real YM2608)
+**
+** 16-08-2002 Jarek Burczynski:
+** - binary exact Envelope Generator (verified on real YM2203);
+** identical to YM2151
+** - corrected 'off by one' error in feedback calculations (when feedback is off)
+** - corrected connection (algorithm) calculation (verified on real YM2203 and YM2610)
+**
+** 18-12-2001 Jarek Burczynski:
+** - added SSG-EG support (verified on real YM2203)
+**
+** 12-08-2001 Jarek Burczynski:
+** - corrected sin_tab and tl_tab data (verified on real chip)
+** - corrected feedback calculations (verified on real chip)
+** - corrected phase generator calculations (verified on real chip)
+** - corrected envelope generator calculations (verified on real chip)
+** - corrected FM volume level (YM2610 and YM2610B).
+** - changed YMxxxUpdateOne() functions (YM2203, YM2608, YM2610, YM2610B, YM2612) :
+** this was needed to calculate YM2610 FM channels output correctly.
+** (Each FM channel is calculated as in other chips, but the output of the channel
+** gets shifted right by one *before* sending to accumulator. That was impossible to do
+** with previous implementation).
+**
+** 23-07-2001 Jarek Burczynski, Nicola Salmoria:
+** - corrected YM2610 ADPCM type A algorithm and tables (verified on real chip)
+**
+** 11-06-2001 Jarek Burczynski:
+** - corrected end of sample bug in ADPCMA_calc_cha().
+** Real YM2610 checks for equality between current and end addresses (only 20 LSB bits).
+**
+** 08-12-98 hiro-shi:
+** rename ADPCMA -> ADPCMB, ADPCMB -> ADPCMA
+** move ROM limit check.(CALC_CH? -> 2610Write1/2)
+** test program (ADPCMB_TEST)
+** move ADPCM A/B end check.
+** ADPCMB repeat flag(no check)
+** change ADPCM volume rate (8->16) (32->48).
+**
+** 09-12-98 hiro-shi:
+** change ADPCM volume. (8->16, 48->64)
+** replace ym2610 ch0/3 (YM-2610B)
+** change ADPCM_SHIFT (10->8) missing bank change 0x4000-0xffff.
+** add ADPCM_SHIFT_MASK
+** change ADPCMA_DECODE_MIN/MAX.
+*/
+
+
+
+
+/************************************************************************/
+/* comment of hiro-shi(Hiromitsu Shioya) */
+/* YM2610(B) = OPN-B */
+/* YM2610 : PSG:3ch FM:4ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */
+/* YM2610B : PSG:3ch FM:6ch ADPCM(18.5KHz):6ch DeltaT ADPCM:1ch */
+/************************************************************************/
+
+//#include "emu.h"
+#include
+#include
+#include "mathdefs.h"
+#include "mamedef.h"
+#include "fm.h"
+
+/* shared function building option */
+#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B||BUILD_YM2612||BUILD_YM3438)
+#define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608)
+
+
+/* globals */
+#define TYPE_SSG 0x01 /* SSG support */
+#define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */
+#define TYPE_6CH 0x04 /* FM 6CH / 3CH */
+#define TYPE_DAC 0x08 /* YM2612's DAC device */
+#define TYPE_ADPCM 0x10 /* two ADPCM units */
+#define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */
+
+
+#define TYPE_YM2203 (TYPE_SSG)
+#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM)
+#define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610)
+#define TYPE_YM2612 (TYPE_DAC |TYPE_LFOPAN |TYPE_6CH)
+
+
+/* globals */
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<>3)
+
+/* sin waveform table in 'decibel' scale */
+static unsigned int sin_tab[SIN_LEN];
+
+/* sustain level table (3dB per step) */
+/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */
+/* 1, 2, 4, 8, 16, 32, 64 (value)*/
+/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/
+
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+/* attenuation value (10 bits) = (SL << 2) << 3 */
+#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) )
+static const UINT32 sl_table[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const UINT8 eg_inc[19*RATE_STEPS]={
+
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */
+/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */
+/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */
+/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */
+
+/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */
+/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */
+/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(17) in this table - it's directly in the code */
+static const UINT8 eg_rate_select2612[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */
+/* 32 infinite time rates (same as Rate 0) */
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+
+/* rates 00-11 */
+/*
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+*/
+O(18),O(18),O( 0),O( 0),
+O( 0),O( 0),O( 2),O( 2), // Nemesis's tests
+
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 12 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 13 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 14 */
+O(12),O(13),O(14),O(15),
+
+/* rate 15 */
+O(16),O(16),O(16),O(16),
+
+/* 32 dummy rates (same as 15 3) */
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16)
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/
+/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */
+/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */
+/* 32 infinite time rates */
+/* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */
+
+/* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+
+/* rates 00-11 */
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 12 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 32 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0)
+
+};
+#undef O
+
+static const UINT8 dt_tab[4 * 32]={
+/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/
+/* FD=0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+/* FD=1 */
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
+/* FD=2 */
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
+ 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16,
+/* FD=3 */
+ 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
+ 8 , 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22
+};
+
+
+/* OPN key frequency number -> key code follow table */
+/* fnum higher 4bit -> keycode lower 2bit */
+static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3};
+
+
+/* 8 LFO speed parameters */
+/* each value represents number of samples that one LFO level will last for */
+static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5};
+
+
+
+/*There are 4 different LFO AM depths available, they are:
+ 0 dB, 1.4 dB, 5.9 dB, 11.8 dB
+ Here is how it is generated (in EG steps):
+
+ 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0
+ 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0
+ 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0
+
+ (1.4 dB is loosing precision as you can see)
+
+ It's implemented as generator from 0..126 with step 2 then a shift
+ right N times, where N is:
+ 8 for 0 dB
+ 3 for 1.4 dB
+ 1 for 5.9 dB
+ 0 for 11.8 dB
+*/
+static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0};
+
+
+
+/*There are 8 different LFO PM depths available, they are:
+ 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents)
+
+ Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10
+ (bits 8,9,10 = FNUM MSB from OCT/FNUM register)
+
+ Here we store only first quarter (positive one) of full waveform.
+ Full table (lfo_pm_table) containing all 128 waveforms is build
+ at run (init) time.
+
+ One value in table below represents 4 (four) basic LFO steps
+ (1 PM step = 4 AM steps).
+
+ For example:
+ at LFO SPEED=0 (which is 108 samples per basic LFO step)
+ one value from "lfo_pm_output" table lasts for 432 consecutive
+ samples (4*108=432) and one full LFO waveform cycle lasts for 13824
+ samples (32*432=13824; 32 because we store only a quarter of whole
+ waveform in the table below)
+*/
+static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */
+/* FNUM BIT 4: 000 0001xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1},
+
+/* FNUM BIT 5: 000 0010xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3},
+
+/* FNUM BIT 6: 000 0100xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1},
+/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3},
+/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6},
+
+/* FNUM BIT 7: 000 1000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1},
+/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2},
+/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3},
+/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6},
+/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+
+/* FNUM BIT 8: 001 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2},
+/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3},
+/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4},
+/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6},
+/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+
+/* FNUM BIT 9: 010 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2},
+/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4},
+/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6},
+/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8},
+/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
+
+/* FNUM BIT10: 100 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4},
+/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8},
+/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc},
+/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10},
+/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
+/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60},
+
+};
+
+/* all 128 LFO PM waveforms */
+static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */
+
+/* register number to channel number , slot offset */
+#define OPN_CHAN(N) (N&3)
+#define OPN_SLOT(N) ((N>>2)&3)
+
+/* slot number */
+#define SLOT1 0
+#define SLOT2 2
+#define SLOT3 1
+#define SLOT4 3
+
+/* bit0 = Right enable , bit1 = Left enable */
+#define OUTD_RIGHT 1
+#define OUTD_LEFT 2
+#define OUTD_CENTER 3
+
+
+/* save output as raw 16-bit sample */
+/* #define SAVE_SAMPLE */
+
+#ifdef SAVE_SAMPLE
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = rt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+
+/* struct describing a single operator (SLOT) */
+typedef struct
+{
+ INT32 *DT; /* detune :dt_tab[DT] */
+ UINT8 KSR; /* key scale rate :3-KSR */
+ UINT32 ar; /* attack rate */
+ UINT32 d1r; /* decay rate */
+ UINT32 d2r; /* sustain rate */
+ UINT32 rr; /* release rate */
+ UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */
+ UINT32 mul; /* multiple :ML_TABLE[ML] */
+
+ /* Phase Generator */
+ UINT32 phase; /* phase counter */
+ INT32 Incr; /* phase step */
+
+ /* Envelope Generator */
+ UINT8 state; /* phase type */
+ UINT32 tl; /* total level: TL << 3 */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level:sl_table[SL] */
+ UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */
+
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_d1r; /* (decay state) */
+ UINT8 eg_sel_d1r; /* (decay state) */
+ UINT8 eg_sh_d2r; /* (sustain state) */
+ UINT8 eg_sel_d2r; /* (sustain state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+
+ UINT8 ssg; /* SSG-EG waveform */
+ UINT8 ssgn; /* SSG-EG negated output */
+
+ UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* AM enable flag */
+
+} FM_SLOT;
+
+typedef struct
+{
+ FM_SLOT SLOT[4]; /* four SLOTs (operators) */
+
+ UINT8 ALGO; /* algorithm */
+ UINT8 FB; /* feedback shift */
+ INT32 op1_out[2]; /* op1 output for feedback */
+
+ INT32 *connect1; /* SLOT1 output pointer */
+ INT32 *connect3; /* SLOT3 output pointer */
+ INT32 *connect2; /* SLOT2 output pointer */
+ INT32 *connect4; /* SLOT4 output pointer */
+
+ INT32 *mem_connect;/* where to put the delayed sample (MEM) */
+ INT32 mem_value; /* delayed sample (MEM) value */
+
+ INT32 pms; /* channel PMS */
+ UINT8 ams; /* channel AMS */
+
+ UINT32 fc; /* fnum,blk:adjusted to sample rate */
+ UINT8 kcode; /* key code: */
+ UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
+ UINT8 Muted;
+} FM_CH;
+
+
+typedef struct
+{
+ //running_device *device;
+ double freqbase; /* frequency base */
+ int timer_prescaler; /* timer prescaler */
+ UINT8 irq; /* interrupt level */
+ UINT8 irqmask; /* irq mask */
+#if FM_BUSY_FLAG_SUPPORT
+ TIME_TYPE busy_expiry_time; /* expiry time of the busy status */
+#endif
+ UINT32 clock; /* master clock (Hz) */
+ UINT32 rate; /* sampling rate (Hz) */
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT32 mode; /* mode CSM / 3SLOT */
+ UINT8 fn_h; /* freq latch */
+ UINT8 prescaler_sel; /* prescaler selector */
+ INT32 TA; /* timer a */
+ INT32 TAC; /* timer a counter */
+ UINT8 TB; /* timer b */
+ INT32 TBC; /* timer b counter */
+ /* local time tables */
+ INT32 dt_tab[8][32]; /* DeTune table */
+ /* Extention Timer and IRQ handler */
+} FM_ST;
+
+
+
+/***********************************************************/
+/* OPN unit */
+/***********************************************************/
+
+/* OPN 3slot struct */
+typedef struct
+{
+ UINT32 fc[3]; /* fnum3,blk3: calculated */
+ UINT8 fn_h; /* freq3 latch */
+ UINT8 kcode[3]; /* key code */
+ UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
+ UINT8 key_csm; /* CSM mode Key-ON flag */
+} FM_3SLOT;
+
+/* OPN/A/B common state */
+typedef struct
+{
+ UINT8 type; /* chip type */
+ FM_ST ST; /* general state */
+ FM_3SLOT SL3; /* 3 slot mode state */
+ FM_CH *P_CH; /* pointer of CH */
+ unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */
+
+
+ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers
+ but LFO works with one more bit of a precision so we really need 4096 elements */
+ UINT32 fn_table[4096]; /* fnumber->increment counter */
+ UINT32 fn_max; /* maximal phase increment (used for phase overflow) */
+
+ /* LFO */
+ UINT8 lfo_cnt; /* current LFO phase (out of 128) */
+ UINT32 lfo_timer; /* current LFO phase runs at LFO frequency */
+ UINT32 lfo_timer_add; /* step of lfo_timer */
+ UINT32 lfo_timer_overflow; /* LFO timer overflows every N samples (depends on LFO frequency) */
+ UINT32 LFO_AM; /* current LFO AM step */
+ UINT32 LFO_PM; /* current LFO PM step */
+
+ INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */
+ INT32 mem; /* one sample delay memory */
+ INT32 out_fm[6]; /* outputs of working channels */
+
+ UINT8 LFOResetZero;
+
+} FM_OPN;
+
+/* here's the virtual YM2612 */
+typedef struct
+{
+ UINT8 REGS[512]; /* registers */
+ FM_OPN OPN; /* OPN state */
+ FM_CH CH[6]; /* channel state */
+ UINT8 addr_A1; /* address line A1 */
+
+ /* dac output (YM2612) */
+ //int dacen;
+ UINT8 dacen;
+ UINT8 dac_test;
+ INT32 dacout;
+ UINT8 MuteDAC;
+
+ UINT8 WaveOutMode;
+ INT32 WaveL;
+ INT32 WaveR;
+} YM2612;
+
+/* log output level */
+#define LOG_ERR 3 /* ERROR */
+#define LOG_WAR 2 /* WARNING */
+#define LOG_INF 1 /* INFORMATION */
+#define LOG_LEVEL LOG_INF
+
+#ifndef __RAINE__
+#define LOG(n,x) do { if( (n)>=LOG_LEVEL ) logerror x; } while (0)
+#endif
+
+/* limitter */
+#define Limit(val, max,min) { \
+ if ( val > max ) val = max; \
+ else if ( val < min ) val = min; \
+}
+
+/*#include
+static FILE* hFile;
+static UINT32 FileSample;*/
+
+/* status set and IRQ handling */
+INLINE void FM_STATUS_SET(FM_ST *ST,int flag)
+{
+ /* set status flag */
+ ST->status |= flag;
+ if ( !(ST->irq) && (ST->status & ST->irqmask) )
+ {
+ ST->irq = 1;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void FM_STATUS_RESET(FM_ST *ST,int flag)
+{
+ /* reset status flag */
+ ST->status &=~flag;
+ if ( (ST->irq) && !(ST->status & ST->irqmask) )
+ {
+ ST->irq = 0;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ }
+}
+
+/* IRQ mask set */
+INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag)
+{
+ ST->irqmask = flag;
+ /* IRQ handling check */
+ FM_STATUS_SET(ST,0);
+ FM_STATUS_RESET(ST,0);
+}
+
+INLINE void FM_KEYON(FM_OPN *OPN, FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+
+ if( !SLOT->key && !OPN->SL3.key_csm)
+ {
+ /* restart Phase Generator */
+ SLOT->phase = 0;
+
+ /* reset SSG-EG inversion flag */
+ SLOT->ssgn = 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT;
+ }
+ else
+ {
+ /* force attenuation level to 0 */
+ SLOT->volume = MIN_ATT_INDEX;
+
+ /* directly switch to Decay (or Sustain) */
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
+ }
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ SLOT->key = 1;
+}
+
+INLINE void FM_KEYOFF(FM_OPN *OPN, FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+
+ if (SLOT->key && !OPN->SL3.key_csm)
+ {
+ if (SLOT->state>EG_REL)
+ {
+ SLOT->state = EG_REL; /* phase -> Release */
+
+ /* SSG-EG specific update */
+ if (SLOT->ssg&0x08)
+ {
+ /* convert EG attenuation level */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04))
+ SLOT->volume = (0x200 - SLOT->volume);
+
+ /* force EG attenuation level */
+ if (SLOT->volume >= 0x200)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ }
+
+ SLOT->key = 0;
+}
+
+INLINE void FM_KEYON_CSM(FM_OPN *OPN, FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+
+ if( !SLOT->key && !OPN->SL3.key_csm)
+ {
+ /* restart Phase Generator */
+ SLOT->phase = 0;
+
+ /* reset SSG-EG inversion flag */
+ SLOT->ssgn = 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT;
+ }
+ else
+ {
+ /* force attenuation level to 0 */
+ SLOT->volume = MIN_ATT_INDEX;
+
+ /* directly switch to Decay (or Sustain) */
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
+ }
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+}
+
+INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+ if (!SLOT->key)
+ {
+ if (SLOT->state>EG_REL)
+ {
+ SLOT->state = EG_REL; /* phase -> Release */
+
+ /* SSG-EG specific update */
+ if (SLOT->ssg&0x08)
+ {
+ /* convert EG attenuation level */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04))
+ SLOT->volume = (0x200 - SLOT->volume);
+
+ /* force EG attenuation level */
+ if (SLOT->volume >= 0x200)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ }
+}
+
+/* OPN Mode Register Write */
+INLINE void set_timers( FM_OPN *OPN, FM_ST *ST, int v )
+{
+ /* b7 = CSM MODE */
+ /* b6 = 3 slot mode */
+ /* b5 = reset b */
+ /* b4 = reset a */
+ /* b3 = timer enable b */
+ /* b2 = timer enable a */
+ /* b1 = load b */
+ /* b0 = load a */
+
+ if ((OPN->ST.mode ^ v) & 0xC0)
+ {
+ /* phase increment need to be recalculated */
+ OPN->P_CH[2].SLOT[SLOT1].Incr=-1;
+
+ /* CSM mode disabled and CSM key ON active*/
+ if (((v & 0xC0) != 0x80) && OPN->SL3.key_csm)
+ {
+ /* CSM Mode Key OFF (verified by Nemesis on real hardware) */
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT1);
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT2);
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT3);
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT4);
+ OPN->SL3.key_csm = 0;
+ }
+ }
+
+ /* reset Timer b flag */
+ if( v & 0x20 )
+ FM_STATUS_RESET(ST,0x02);
+ /* reset Timer a flag */
+ if( v & 0x10 )
+ FM_STATUS_RESET(ST,0x01);
+ /* load b */
+ if( v & 0x02 )
+ {
+ if( ST->TBC == 0 )
+ {
+ ST->TBC = ( 256-ST->TB)<<4;
+ /* External timer handler */
+ }
+ }
+ else
+ { /* stop timer b */
+ if( ST->TBC != 0 )
+ {
+ ST->TBC = 0;
+ }
+ }
+ /* load a */
+ if( v & 0x01 )
+ {
+ if( ST->TAC == 0 )
+ {
+ ST->TAC = (1024-ST->TA);
+ /* External timer handler */
+ }
+ }
+ else
+ { /* stop timer a */
+ if( ST->TAC != 0 )
+ {
+ ST->TAC = 0;
+ }
+ }
+ ST->mode = v;
+}
+
+
+/* Timer A Overflow */
+INLINE void TimerAOver(FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01);
+ /* clear or reload the counter */
+ ST->TAC = (1024-ST->TA);
+}
+/* Timer B Overflow */
+INLINE void TimerBOver(FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02);
+ /* clear or reload the counter */
+ ST->TBC = ( 256-ST->TB)<<4;
+}
+
+
+#if FM_INTERNAL_TIMER
+/* ----- internal timer mode , update timer */
+// Valley Bell: defines fixed
+
+/* ---------- calculate timer A ---------- */
+ #define INTERNAL_TIMER_A(ST,CSM_CH) \
+ { \
+ if( (ST)->TAC && ((ST)->timer_handler==0) ) \
+ if( ((ST)->TAC -= (int)((ST)->freqbase*4096)) <= 0 ) \
+ { \
+ TimerAOver( ST ); \
+ /* CSM mode total level latch and auto key on */ \
+ if( (ST)->mode & 0x80 ) \
+ CSMKeyControll( OPN, CSM_CH ); \
+ } \
+ }
+/* ---------- calculate timer B ---------- */
+ #define INTERNAL_TIMER_B(ST,step) \
+ { \
+ if( (ST)->TBC && ((ST)->timer_handler==0) ) \
+ if( ((ST)->TBC -= (int)((ST)->freqbase*4096*step)) <= 0 ) \
+ TimerBOver( ST ); \
+ }
+#else /* FM_INTERNAL_TIMER */
+/* external timer mode */
+#define INTERNAL_TIMER_A(ST,CSM_CH)
+#define INTERNAL_TIMER_B(ST,step)
+#endif /* FM_INTERNAL_TIMER */
+
+
+
+#if FM_BUSY_FLAG_SUPPORT
+#define FM_BUSY_CLEAR(ST) ((ST)->busy_expiry_time = UNDEFINED_TIME)
+INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST)
+{
+ if( COMPARE_TIMES(ST->busy_expiry_time, UNDEFINED_TIME) != 0 )
+ {
+ if (COMPARE_TIMES(ST->busy_expiry_time, FM_GET_TIME_NOW(ST->device->machine)) > 0)
+ return ST->status | 0x80; /* with busy */
+ /* expire */
+ FM_BUSY_CLEAR(ST);
+ }
+ return ST->status;
+}
+INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock )
+{
+ TIME_TYPE expiry_period = MULTIPLY_TIME_BY_INT(ATTOTIME_IN_HZ(ST->clock), busyclock * ST->timer_prescaler);
+ ST->busy_expiry_time = ADD_TIMES(FM_GET_TIME_NOW(ST->device->machine), expiry_period);
+}
+#else
+#define FM_STATUS_FLAG(ST) ((ST)->status)
+#define FM_BUSY_SET(ST,bclock) {}
+#define FM_BUSY_CLEAR(ST) {}
+#endif
+
+
+/* set algorithm connection */
+INLINE void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch )
+{
+ INT32 *carrier = &OPN->out_fm[ch];
+
+ INT32 **om1 = &CH->connect1;
+ INT32 **om2 = &CH->connect3;
+ INT32 **oc1 = &CH->connect2;
+
+ INT32 **memc = &CH->mem_connect;
+
+ switch( CH->ALGO )
+ {
+ case 0:
+ /* M1---C1---MEM---M2---C2---OUT */
+ *om1 = &OPN->c1;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 1:
+ /* M1------+-MEM---M2---C2---OUT */
+ /* C1-+ */
+ *om1 = &OPN->mem;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 2:
+ /* M1-----------------+-C2---OUT */
+ /* C1---MEM---M2-+ */
+ *om1 = &OPN->c2;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 3:
+ /* M1---C1---MEM------+-C2---OUT */
+ /* M2-+ */
+ *om1 = &OPN->c1;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->c2;
+ break;
+ case 4:
+ /* M1---C1-+-OUT */
+ /* M2---C2-+ */
+ /* MEM: not used */
+ *om1 = &OPN->c1;
+ *oc1 = carrier;
+ *om2 = &OPN->c2;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ case 5:
+ /* +----C1----+ */
+ /* M1-+-MEM---M2-+-OUT */
+ /* +----C2----+ */
+ *om1 = 0; /* special mark */
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->m2;
+ break;
+ case 6:
+ /* M1---C1-+ */
+ /* M2-+-OUT */
+ /* C2-+ */
+ /* MEM: not used */
+ *om1 = &OPN->c1;
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ case 7:
+ /* M1-+ */
+ /* C1-+-OUT */
+ /* M2-+ */
+ /* C2-+ */
+ /* MEM: not used*/
+ *om1 = carrier;
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ }
+
+ CH->connect4 = carrier;
+}
+
+/* set detune & multiple */
+INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v)
+{
+ SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1;
+ SLOT->DT = ST->dt_tab[(v>>4)&7];
+ CH->SLOT[SLOT1].Incr=-1;
+}
+
+/* set total level */
+INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v)
+{
+ SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+}
+
+/* set attack rate & key scale */
+INLINE void set_ar_ksr(UINT8 type, FM_CH *CH,FM_SLOT *SLOT,int v)
+{
+ UINT8 old_KSR = SLOT->KSR;
+
+ SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->KSR = 3-(v>>6);
+ if (SLOT->KSR != old_KSR)
+ {
+ CH->SLOT[SLOT1].Incr=-1;
+ }
+
+ /* Even if it seems unnecessary, in some odd case, KSR and KC are both modified */
+ /* and could result in SLOT->kc remaining unchanged. */
+ /* In such case, AR values would not be recalculated despite SLOT->ar has changed */
+ /* This fixes the introduction music of Batman & Robin (Eke-Eke) */
+ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware */
+ }
+}
+
+/* set decay rate */
+INLINE void set_dr(UINT8 type, FM_SLOT *SLOT,int v)
+{
+ SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr];
+}
+
+/* set sustain rate */
+INLINE void set_sr(UINT8 type, FM_SLOT *SLOT,int v)
+{
+ SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr];
+}
+
+/* set release rate */
+INLINE void set_sl_rr(UINT8 type, FM_SLOT *SLOT,int v)
+{
+ SLOT->sl = sl_table[ v>>4 ];
+
+ /* check EG state changes */
+ if ((SLOT->state == EG_DEC) && (SLOT->volume >= (INT32)(SLOT->sl)))
+ SLOT->state = EG_SUS;
+
+ SLOT->rr = 34 + ((v&0x0f)<<2);
+
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
+ SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr];
+}
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(FM_OPN *OPN)
+{
+ if (OPN->lfo_timer_overflow) /* LFO enabled ? */
+ {
+ /* increment LFO timer */
+ OPN->lfo_timer += OPN->lfo_timer_add;
+
+ /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */
+ while (OPN->lfo_timer >= OPN->lfo_timer_overflow)
+ {
+ OPN->lfo_timer -= OPN->lfo_timer_overflow;
+
+ /* There are 128 LFO steps */
+ OPN->lfo_cnt = ( OPN->lfo_cnt + 1 ) & 127;
+
+ /* triangle */
+ /* AM: 0 to 126 step +2, 126 to 0 step -2 */
+ if (OPN->lfo_cnt<64)
+ OPN->LFO_AM = OPN->lfo_cnt * 2;
+ else
+ OPN->LFO_AM = 126 - ((OPN->lfo_cnt&63) * 2);
+
+ /* PM works with 4 times slower clock */
+ OPN->LFO_PM = OPN->lfo_cnt >> 2;
+ }
+ }
+}
+
+INLINE void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT)
+{
+ //unsigned int out;
+ unsigned int i = 4; /* four operators per channel */
+
+ do
+ {
+ switch(SLOT->state)
+ {
+ case EG_ATT: /* attack phase */
+ if (!(OPN->eg_cnt & ((1<eg_sh_ar)-1)))
+ {
+ /* update attenuation level */
+ SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4;
+
+ /* check phase transition*/
+ if (SLOT->volume <= MIN_ATT_INDEX)
+ {
+ SLOT->volume = MIN_ATT_INDEX;
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */
+ }
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if (!(OPN->eg_cnt & ((1<eg_sh_d1r)-1)))
+ {
+ /* SSG EG type */
+ if (SLOT->ssg&0x08)
+ {
+ /* update attenuation level */
+ if (SLOT->volume < 0x200)
+ {
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)];
+
+ /* recalculate EG output */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ }
+ else
+ {
+ /* update attenuation level */
+ SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)];
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ /* check phase transition*/
+ if (SLOT->volume >= (INT32)(SLOT->sl))
+ SLOT->state = EG_SUS;
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+ if (!(OPN->eg_cnt & ((1<eg_sh_d2r)-1)))
+ {
+ /* SSG EG type */
+ if (SLOT->ssg&0x08)
+ {
+ /* update attenuation level */
+ if (SLOT->volume < 0x200)
+ {
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)];
+
+ /* recalculate EG output */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ else
+ {
+ /* update attenuation level */
+ SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)];
+
+ /* check phase transition*/
+ if ( SLOT->volume >= MAX_ATT_INDEX )
+ SLOT->volume = MAX_ATT_INDEX;
+ /* do not change SLOT->state (verified on real chip) */
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if (!(OPN->eg_cnt & ((1<eg_sh_rr)-1)))
+ {
+ /* SSG EG type */
+ if (SLOT->ssg&0x08)
+ {
+ /* update attenuation level */
+ if (SLOT->volume < 0x200)
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)];
+ /* check phase transition */
+ if (SLOT->volume >= 0x200)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+ }
+ else
+ {
+ /* update attenuation level */
+ SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)];
+
+ /* check phase transition*/
+ if (SLOT->volume >= MAX_ATT_INDEX)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+ }
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+
+ }
+ break;
+ }
+
+ // Valley Bell: These few lines are missing in Genesis Plus GX' ym2612 core file.
+ // Disabling them fixes the SSG-EG.
+ // Additional Note: Asterix and the Great Rescue: Level 1 sounds "better" with these lines,
+ // but less accurate.
+ /*out = ((UINT32)SLOT->volume);
+
+ // negate output (changes come from alternate bit, init comes from attack bit)
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn&2) && (SLOT->state > EG_REL))
+ out ^= MAX_ATT_INDEX;
+
+ // we need to store the result here because we are going to change ssgn
+ // in next instruction
+ SLOT->vol_out = out + SLOT->tl;*/
+
+ SLOT++;
+ i--;
+ }while (i);
+
+}
+
+/* SSG-EG update process */
+/* The behavior is based upon Nemesis tests on real hardware */
+/* This is actually executed before each samples */
+INLINE void update_ssg_eg_channel(FM_SLOT *SLOT)
+{
+ unsigned int i = 4; /* four operators per channel */
+
+ do
+ {
+ /* detect SSG-EG transition */
+ /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */
+ /* if an Attack Phase is programmed, inversion can occur on each sample */
+ if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL))
+ {
+ if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */
+ {
+ /* set inversion flag */
+ if (SLOT->ssg & 0x02)
+ SLOT->ssgn = 4;
+
+ /* force attenuation level during decay phases */
+ if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04)))
+ SLOT->volume = MAX_ATT_INDEX;
+ }
+ else /* loop SSG-EG */
+ {
+ /* toggle output inversion flag or reset Phase Generator */
+ if (SLOT->ssg & 0x02)
+ SLOT->ssgn ^= 4;
+ else
+ SLOT->phase = 0;
+
+ /* same as Key ON */
+ if (SLOT->state != EG_ATT)
+ {
+ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT;
+ }
+ else
+ {
+ /* Attack Rate is maximal: directly switch to Decay or Substain */
+ SLOT->volume = MIN_ATT_INDEX;
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
+ }
+ }
+ }
+
+ /* recalculate EG output */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ /* next slot */
+ SLOT++;
+ i--;
+ } while (i);
+}
+
+
+INLINE void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum)
+{
+ UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
+ INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ];
+
+ block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ UINT8 blk = (block_fnum&0x7000) >> 12;
+ UINT32 fn = block_fnum & 0xfff;
+
+ /* recalculate keyscale code */
+ /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/
+ /* This really stupid bug caused a read outside of the
+ array [size 0x10] and returned invalid values.
+ This caused an annoying vibrato for some notes.
+ (Note: seems to be a copy-and-paste from OPNWriteReg -> case 0xA0)
+ Why are MAME cores always SOO buggy ?! */
+ /* Oh, and before I forget: it's correct in fm.c */
+ int kc = (blk<<2) | opn_fktable[fn >> 8];
+ /* Thanks to Blargg - his patch that helped me to find this bug */
+
+ /* recalculate (frequency) phase increment counter */
+ int fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc];
+
+ /* (frequency) phase overflow (credits to Nemesis) */
+ if (fc < 0) fc += OPN->fn_max;
+
+ /* update phase */
+ SLOT->phase += (fc * SLOT->mul) >> 1;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ SLOT->phase += SLOT->Incr;
+ }
+}
+
+INLINE void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH)
+{
+ UINT32 block_fnum = CH->block_fnum;
+
+ UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
+ INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ];
+
+ block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ UINT8 blk = (block_fnum&0x7000) >> 12;
+ UINT32 fn = block_fnum & 0xfff;
+
+ /* recalculate keyscale code */
+ /*int kc = (blk<<2) | opn_fktable[fn >> 7];*/
+ /* the same stupid bug as above */
+ int kc = (blk<<2) | opn_fktable[fn >> 8];
+
+ /* recalculate (frequency) phase increment counter */
+ int fc = (OPN->fn_table[fn]>>(7-blk));
+
+ /* (frequency) phase overflow (credits to Nemesis) */
+ int finc = fc + CH->SLOT[SLOT1].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT2].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT3].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT4].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
+ CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
+ CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
+ CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
+ }
+}
+
+/* update phase increment and envelope generator */
+INLINE void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc )
+{
+ int ksr = kc >> SLOT->KSR;
+
+ fc += SLOT->DT[kc];
+
+ /* detects frequency overflow (credits to Nemesis) */
+ if (fc < 0) fc += OPN->fn_max;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = (fc * SLOT->mul) >> 1;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 32+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select2612[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware (Attack phase is blocked) */
+ }
+
+ SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
+
+ SLOT->eg_sel_d1r= eg_rate_select2612[SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sel_d2r= eg_rate_select2612[SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sel_rr = eg_rate_select2612[SLOT->rr + SLOT->ksr];
+ }
+}
+
+/* update phase increment counters */
+INLINE void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH )
+{
+ if( CH->SLOT[SLOT1].Incr==-1)
+ {
+ int fc = CH->fc;
+ int kc = CH->kcode;
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc );
+ }
+}
+
+#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask))
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE void chan_calc(YM2612 *F2612, FM_OPN *OPN, FM_CH *CH)
+{
+ UINT32 AM = OPN->LFO_AM >> CH->ams;
+ unsigned int eg_out;
+
+ if (CH->Muted)
+ return;
+
+ OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0;
+
+ *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
+
+ eg_out = volume_calc(&CH->SLOT[SLOT1]);
+ {
+ INT32 out = CH->op1_out[0] + CH->op1_out[1];
+ CH->op1_out[0] = CH->op1_out[1];
+
+ if( !CH->connect1 )
+ {
+ /* algorithm 5 */
+ OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0];
+ }
+ else
+ {
+ /* other algorithms */
+ *CH->connect1 += CH->op1_out[0];
+ }
+
+
+ CH->op1_out[1] = 0;
+ if( eg_out < ENV_QUIET ) /* SLOT 1 */
+ {
+ if (!CH->FB)
+ out=0;
+
+ CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<FB) );
+ }
+ }
+
+ eg_out = volume_calc(&CH->SLOT[SLOT3]);
+ if( eg_out < ENV_QUIET ) /* SLOT 3 */
+ *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2);
+
+ eg_out = volume_calc(&CH->SLOT[SLOT2]);
+ if( eg_out < ENV_QUIET ) /* SLOT 2 */
+ *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1);
+
+ eg_out = volume_calc(&CH->SLOT[SLOT4]);
+ if( eg_out < ENV_QUIET ) /* SLOT 4 */
+ *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2);
+
+
+ /* store current MEM */
+ CH->mem_value = OPN->mem;
+
+ /* update phase counters AFTER output calculations */
+ if(CH->pms)
+ {
+ /* add support for 3 slot mode */
+ if ((OPN->ST.mode & 0xC0) && (CH == &F2612->CH[2]))
+ {
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum);
+ }
+ else update_phase_lfo_channel(OPN, CH);
+ }
+ else /* no LFO phase modulation */
+ {
+ CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
+ CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
+ CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
+ CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
+ }
+}
+
+static void FMCloseTable( void )
+{
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+ return;
+}
+
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(FM_OPN *OPN, FM_CH *CH)
+{
+ /* all key ON (verified by Nemesis on real hardware) */
+ FM_KEYON_CSM(OPN,CH,SLOT1);
+ FM_KEYON_CSM(OPN,CH,SLOT2);
+ FM_KEYON_CSM(OPN,CH,SLOT3);
+ FM_KEYON_CSM(OPN,CH,SLOT4);
+ OPN->SL3.key_csm = 1;
+}
+
+#ifdef __STATE_H__
+/* FM channel save , internal state only */
+static void FMsave_state_channel(running_device *device,FM_CH *CH,int num_ch)
+{
+ int slot , ch;
+
+ for(ch=0;chop1_out);
+ state_save_register_device_item(device, ch, CH->fc);
+ /* slots */
+ for(slot=0;slot<4;slot++)
+ {
+ FM_SLOT *SLOT = &CH->SLOT[slot];
+ state_save_register_device_item(device, ch * 4 + slot, SLOT->phase);
+ state_save_register_device_item(device, ch * 4 + slot, SLOT->state);
+ state_save_register_device_item(device, ch * 4 + slot, SLOT->volume);
+ }
+ }
+}
+
+static void FMsave_state_st(running_device *device,FM_ST *ST)
+{
+#if FM_BUSY_FLAG_SUPPORT
+ state_save_register_device_item(device, 0, ST->busy_expiry_time.seconds );
+ state_save_register_device_item(device, 0, ST->busy_expiry_time.attoseconds );
+#endif
+ state_save_register_device_item(device, 0, ST->address );
+ state_save_register_device_item(device, 0, ST->irq );
+ state_save_register_device_item(device, 0, ST->irqmask );
+ state_save_register_device_item(device, 0, ST->status );
+ state_save_register_device_item(device, 0, ST->mode );
+ state_save_register_device_item(device, 0, ST->prescaler_sel );
+ state_save_register_device_item(device, 0, ST->fn_h );
+ state_save_register_device_item(device, 0, ST->TA );
+ state_save_register_device_item(device, 0, ST->TAC );
+ state_save_register_device_item(device, 0, ST->TB );
+ state_save_register_device_item(device, 0, ST->TBC );
+}
+#endif /* _STATE_H */
+
+#if BUILD_OPN
+/* write a OPN mode register 0x20-0x2f */
+static void OPNWriteMode(FM_OPN *OPN, int r, int v)
+{
+ UINT8 c;
+ FM_CH *CH;
+
+ switch(r)
+ {
+ case 0x21: /* Test */
+ break;
+ case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */
+ if (v&8) /* LFO enabled ? */
+ {
+ if (!OPN->lfo_timer_overflow)
+ {
+ /* restart LFO */
+ OPN->lfo_cnt = 0;
+ OPN->lfo_timer = 0;
+ OPN->LFO_AM = 0;
+ OPN->LFO_PM = 0;
+ }
+
+ OPN->lfo_timer_overflow = lfo_samples_per_step[v&7] << LFO_SH;
+ }
+ else
+ {
+ OPN->lfo_timer_overflow = 0;
+ if (OPN->LFOResetZero)
+ {
+ OPN->LFO_AM = 0;
+ OPN->LFO_PM = 0;
+ }
+ }
+ break;
+ case 0x24: /* timer A High 8*/
+ OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2);
+ break;
+ case 0x25: /* timer A Low 2*/
+ OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3);
+ break;
+ case 0x26: /* timer B */
+ OPN->ST.TB = v;
+ break;
+ case 0x27: /* mode, timer control */
+ set_timers( OPN, &(OPN->ST),v );
+ break;
+ case 0x28: /* key on / off */
+ c = v & 0x03;
+ if( c == 3 ) break;
+ if( (v&0x04) && (OPN->type & TYPE_6CH) ) c+=3;
+ CH = OPN->P_CH;
+ CH = &CH[c];
+ if(v&0x10) FM_KEYON(OPN,CH,SLOT1); else FM_KEYOFF(OPN,CH,SLOT1);
+ if(v&0x20) FM_KEYON(OPN,CH,SLOT2); else FM_KEYOFF(OPN,CH,SLOT2);
+ if(v&0x40) FM_KEYON(OPN,CH,SLOT3); else FM_KEYOFF(OPN,CH,SLOT3);
+ if(v&0x80) FM_KEYON(OPN,CH,SLOT4); else FM_KEYOFF(OPN,CH,SLOT4);
+ break;
+ }
+}
+
+/* write a OPN register (0x30-0xff) */
+static void OPNWriteReg(FM_OPN *OPN, int r, int v)
+{
+ FM_CH *CH;
+ FM_SLOT *SLOT;
+
+ UINT8 c = OPN_CHAN(r);
+
+ if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */
+
+ if (r >= 0x100) c+=3;
+
+ CH = OPN->P_CH;
+ CH = &CH[c];
+
+ SLOT = &(CH->SLOT[OPN_SLOT(r)]);
+
+ switch( r & 0xf0 ) {
+ case 0x30: /* DET , MUL */
+ set_det_mul(&OPN->ST,CH,SLOT,v);
+ break;
+
+ case 0x40: /* TL */
+ set_tl(CH,SLOT,v);
+ break;
+
+ case 0x50: /* KS, AR */
+ set_ar_ksr(OPN->type,CH,SLOT,v);
+ break;
+
+ case 0x60: /* bit7 = AM ENABLE, DR */
+ set_dr(OPN->type, SLOT,v);
+
+ if(OPN->type & TYPE_LFOPAN) /* YM2608/2610/2610B/2612 */
+ {
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ }
+ break;
+
+ case 0x70: /* SR */
+ set_sr(OPN->type,SLOT,v);
+ break;
+
+ case 0x80: /* SL, RR */
+ set_sl_rr(OPN->type,SLOT,v);
+ break;
+
+ case 0x90: /* SSG-EG */
+ SLOT->ssg = v&0x0f;
+
+ /* recalculate EG output */
+ if (SLOT->state > EG_REL)
+ {
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ /* SSG-EG envelope shapes :
+
+ E AtAlH
+ 1 0 0 0 \\\\
+
+ 1 0 0 1 \___
+
+ 1 0 1 0 \/\/
+ ___
+ 1 0 1 1 \
+
+ 1 1 0 0 ////
+ ___
+ 1 1 0 1 /
+
+ 1 1 1 0 /\/\
+
+ 1 1 1 1 /___
+
+
+ E = SSG-EG enable
+
+
+ The shapes are generated using Attack, Decay and Sustain phases.
+
+ Each single character in the diagrams above represents this whole
+ sequence:
+
+ - when KEY-ON = 1, normal Attack phase is generated (*without* any
+ difference when compared to normal mode),
+
+ - later, when envelope level reaches minimum level (max volume),
+ the EG switches to Decay phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - later when envelope level passes the SL level,
+ the EG swithes to Sustain phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - finally when envelope level reaches maximum level (min volume),
+ the EG switches to Attack phase again (depends on actual waveform).
+
+ Important is that when switch to Attack phase occurs, the phase counter
+ of that operator will be zeroed-out (as in normal KEY-ON) but not always.
+ (I havent found the rule for that - perhaps only when the output level is low)
+
+ The difference (when compared to normal Envelope Generator mode) is
+ that the resolution in Decay and Sustain phases is 4 times lower;
+ this results in only 256 steps instead of normal 1024.
+ In other words:
+ when SSG-EG is disabled, the step inside of the EG is one,
+ when SSG-EG is enabled, the step is four (in Decay and Sustain phases).
+
+ Times between the level changes are the same in both modes.
+
+
+ Important:
+ Decay 1 Level (so called SL) is compared to actual SSG-EG output, so
+ it is the same in both SSG and no-SSG modes, with this exception:
+
+ when the SSG-EG is enabled and is generating raising levels
+ (when the EG output is inverted) the SL will be found at wrong level !!!
+ For example, when SL=02:
+ 0 -6 = -6dB in non-inverted EG output
+ 96-6 = -90dB in inverted EG output
+ Which means that EG compares its level to SL as usual, and that the
+ output is simply inverted afterall.
+
+
+ The Yamaha's manuals say that AR should be set to 0x1f (max speed).
+ That is not necessary, but then EG will be generating Attack phase.
+
+ */
+
+
+ break;
+
+ case 0xa0:
+ switch( OPN_SLOT(r) )
+ {
+ case 0: /* 0xa0-0xa2 : FNUM1 */
+ {
+ UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v;
+ UINT8 blk = OPN->ST.fn_h>>3;
+ /* keyscale code */
+ CH->kcode = (blk<<2) | opn_fktable[fn >> 7];
+ /* phase increment counter */
+ CH->fc = OPN->fn_table[fn*2]>>(7-blk);
+
+ /* store fnum in clear form for LFO PM calculations */
+ CH->block_fnum = (blk<<11) | fn;
+
+ CH->SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 1: /* 0xa4-0xa6 : FNUM2,BLK */
+ OPN->ST.fn_h = v&0x3f;
+ break;
+ case 2: /* 0xa8-0xaa : 3CH FNUM1 */
+ if(r < 0x100)
+ {
+ UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v;
+ UINT8 blk = OPN->SL3.fn_h>>3;
+ /* keyscale code */
+ OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7];
+ /* phase increment counter */
+ OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk);
+ OPN->SL3.block_fnum[c] = (blk<<11) | fn;
+ (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 3: /* 0xac-0xae : 3CH FNUM2,BLK */
+ if(r < 0x100)
+ OPN->SL3.fn_h = v&0x3f;
+ break;
+ }
+ break;
+
+ case 0xb0:
+ switch( OPN_SLOT(r) )
+ {
+ case 0: /* 0xb0-0xb2 : FB,ALGO */
+ {
+ int feedback = (v>>3)&7;
+ CH->ALGO = v&7;
+ CH->FB = feedback ? feedback+6 : 0;
+ setup_connection( OPN, CH, c );
+ }
+ break;
+ case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */
+ if( OPN->type & TYPE_LFOPAN)
+ {
+ /* b0-2 PMS */
+ CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */
+
+ /* b4-5 AMS */
+ CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03];
+
+ /* PAN : b7 = L, b6 = R */
+ OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0;
+ OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0;
+
+ }
+ break;
+ }
+ break;
+ }
+}
+
+/* initialize time tables */
+static void init_timetables(FM_OPN *OPN, double freqbase)
+{
+ int i,d;
+ double rate;
+
+ /* DeTune table */
+ for (d = 0;d <= 3;d++)
+ {
+ for (i = 0;i <= 31;i++)
+ {
+ rate = ((double)dt_tab[d*32 + i]) * freqbase * (1<<(FREQ_SH-10)); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+ OPN->ST.dt_tab[d][i] = (INT32) rate;
+ OPN->ST.dt_tab[d+4][i] = -OPN->ST.dt_tab[d][i];
+ }
+ }
+
+ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers
+ but LFO works with one more bit of a precision so we really need 4096 elements */
+ /* calculate fnumber -> increment counter table */
+ for(i = 0; i < 4096; i++)
+ {
+ /* freq table for octave 7 */
+ /* OPN phase increment counter = 20bit */
+ /* the correct formula is : F-Number = (144 * fnote * 2^20 / M) / 2^(B-1) */
+ /* where sample clock is M/144 */
+ /* this means the increment value for one clock sample is FNUM * 2^(B-1) = FNUM * 64 for octave 7 */
+ /* we also need to handle the ratio between the chip frequency and the emulated frequency (can be 1.0) */
+ OPN->fn_table[i] = (UINT32)( (double)i * 32 * freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+ }
+
+ /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */
+ OPN->fn_max = (UINT32)( (double)0x20000 * freqbase * (1<<(FREQ_SH-10)) );
+}
+
+/* prescaler set (and make time tables) */
+static void OPNSetPres(FM_OPN *OPN, int pres, int timer_prescaler, int SSGpres)
+{
+ /* frequency base */
+ OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0;
+ if ( fabs( OPN->ST.freqbase - 1.0 ) < 0.0001 )
+ OPN->ST.freqbase = 1.0;
+
+ /* EG is updated every 3 samples */
+ OPN->eg_timer_add = (UINT32)((1<ST.freqbase);
+ OPN->eg_timer_overflow = ( 3 ) * (1<lfo_timer_add = (UINT32)((1<ST.freqbase);
+
+ /* Timer base time */
+ OPN->ST.timer_prescaler = timer_prescaler;
+
+ /* make time tables */
+ init_timetables(OPN, OPN->ST.freqbase);
+}
+
+static void reset_channels( FM_ST *ST , FM_CH *CH , int num )
+{
+ int c,s;
+
+ for( c = 0 ; c < num ; c++ )
+ {
+ //memset(&CH[c], 0x00, sizeof(FM_CH));
+ CH[c].mem_value = 0;
+ CH[c].op1_out[0] = 0;
+ CH[c].op1_out[1] = 0;
+ CH[c].fc = 0;
+ for(s = 0 ; s < 4 ; s++ )
+ {
+ //memset(&CH[c].SLOT[s], 0x00, sizeof(FM_SLOT));
+ CH[c].SLOT[s].Incr = -1;
+ CH[c].SLOT[s].key = 0;
+ CH[c].SLOT[s].phase = 0;
+ CH[c].SLOT[s].ssg = 0;
+ CH[c].SLOT[s].ssgn = 0;
+ CH[c].SLOT[s].state= EG_OFF;
+ CH[c].SLOT[s].volume = MAX_ATT_INDEX;
+ CH[c].SLOT[s].vol_out= MAX_ATT_INDEX;
+ }
+ }
+}
+
+/* initialize generic tables */
+static void init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+ /* build Linear Power Table */
+ for (x=0; x>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 2; /* 13 bits here (as in real chip) */
+
+
+ /* 14 bits (with sign bit) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ /* one entry in the 'Power' table use the following format, xxxxxyyyyyyyys with: */
+ /* s = sign bit */
+ /* yyyyyyyy = 8-bits decimal part (0-TL_RES_LEN) */
+ /* xxxxx = 5-bits integer 'shift' value (0-31) but, since Power table output is 13 bits, */
+ /* any value above 13 (included) would be discarded. */
+ for (i=1; i<13; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ }
+
+ /* build Logarithmic Sinus table */
+ for (i=0; i0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ /* 13-bits (8.5) value is formatted for above 'Power' table */
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+ }
+
+ /* build LFO PM modulation table */
+ for(i = 0; i < 8; i++) /* 8 PM depths */
+ {
+ UINT8 fnum;
+ for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */
+ {
+ UINT8 value;
+ UINT8 step;
+ UINT32 offset_depth = i;
+ UINT32 offset_fnum_bit;
+ UINT32 bit_tmp;
+
+ for (step=0; step<8; step++)
+ {
+ value = 0;
+ for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */
+ {
+ if (fnum & (1<OPN;
+ INT32 *out_fm = OPN->out_fm;
+ int i;
+ FMSAMPLE *bufL,*bufR;
+ INT32 dacout;
+ FM_CH *cch[6];
+ int lt,rt;
+
+ /* set bufer */
+ bufL = buffer[0];
+ bufR = buffer[1];
+
+ cch[0] = &F2612->CH[0];
+ cch[1] = &F2612->CH[1];
+ cch[2] = &F2612->CH[2];
+ cch[3] = &F2612->CH[3];
+ cch[4] = &F2612->CH[4];
+ cch[5] = &F2612->CH[5];
+
+ if (! F2612->MuteDAC)
+ dacout = F2612->dacout;
+ else
+ dacout = 0;
+
+ /* refresh PG and EG */
+ refresh_fc_eg_chan( OPN, cch[0] );
+ refresh_fc_eg_chan( OPN, cch[1] );
+ if( (OPN->ST.mode & 0xc0) )
+ {
+ /* 3SLOT MODE */
+ if( cch[2]->SLOT[SLOT1].Incr==-1)
+ {
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode );
+ }
+ }else refresh_fc_eg_chan( OPN, cch[2] );
+ refresh_fc_eg_chan( OPN, cch[3] );
+ refresh_fc_eg_chan( OPN, cch[4] );
+ refresh_fc_eg_chan( OPN, cch[5] );
+ if (! length)
+ {
+ update_ssg_eg_channel(&cch[0]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[1]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[2]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[3]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[4]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[5]->SLOT[SLOT1]);
+ }
+
+
+ /* buffering */
+ for(i=0; i < length ; i++)
+ {
+ /* clear outputs */
+ out_fm[0] = 0;
+ out_fm[1] = 0;
+ out_fm[2] = 0;
+ out_fm[3] = 0;
+ out_fm[4] = 0;
+ out_fm[5] = 0;
+
+ /* update SSG-EG output */
+ update_ssg_eg_channel(&cch[0]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[1]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[2]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[3]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[4]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[5]->SLOT[SLOT1]);
+
+ /* calculate FM */
+ if (! F2612->dac_test)
+ {
+ chan_calc(F2612, OPN, cch[0]);
+ chan_calc(F2612, OPN, cch[1]);
+ chan_calc(F2612, OPN, cch[2]);
+ chan_calc(F2612, OPN, cch[3]);
+ chan_calc(F2612, OPN, cch[4]);
+ if( F2612->dacen )
+ *cch[5]->connect4 += dacout;
+ else
+ chan_calc(F2612, OPN, cch[5]);
+ }
+ else
+ {
+ out_fm[0] = out_fm[1] = dacout;
+ out_fm[2] = out_fm[3] = dacout;
+ out_fm[5] = dacout;
+ }
+
+ /* advance LFO */
+ advance_lfo(OPN);
+
+ /* advance envelope generator */
+ OPN->eg_timer += OPN->eg_timer_add;
+ while (OPN->eg_timer >= OPN->eg_timer_overflow)
+ {
+ OPN->eg_timer -= OPN->eg_timer_overflow;
+ OPN->eg_cnt++;
+
+ advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]);
+ }
+
+ /*fprintf(hFile, "%u", FileSample, out_fm[0]);
+ for (lt = 0; lt < 6; lt ++)
+ fprintf(hFile, "\t%d", out_fm[lt]);
+ fprintf(hFile, "\n");
+ FileSample ++;*/
+
+ if (out_fm[0] > 8192) out_fm[0] = 8192;
+ else if (out_fm[0] < -8192) out_fm[0] = -8192;
+ if (out_fm[1] > 8192) out_fm[1] = 8192;
+ else if (out_fm[1] < -8192) out_fm[1] = -8192;
+ if (out_fm[2] > 8192) out_fm[2] = 8192;
+ else if (out_fm[2] < -8192) out_fm[2] = -8192;
+ if (out_fm[3] > 8192) out_fm[3] = 8192;
+ else if (out_fm[3] < -8192) out_fm[3] = -8192;
+ if (out_fm[4] > 8192) out_fm[4] = 8192;
+ else if (out_fm[4] < -8192) out_fm[4] = -8192;
+ if (out_fm[5] > 8192) out_fm[5] = 8192;
+ else if (out_fm[5] < -8192) out_fm[5] = -8192;
+
+ /* 6-channels mixing */
+ lt = ((out_fm[0]>>0) & OPN->pan[0]);
+ rt = ((out_fm[0]>>0) & OPN->pan[1]);
+ lt += ((out_fm[1]>>0) & OPN->pan[2]);
+ rt += ((out_fm[1]>>0) & OPN->pan[3]);
+ lt += ((out_fm[2]>>0) & OPN->pan[4]);
+ rt += ((out_fm[2]>>0) & OPN->pan[5]);
+ lt += ((out_fm[3]>>0) & OPN->pan[6]);
+ rt += ((out_fm[3]>>0) & OPN->pan[7]);
+ if (! F2612->dac_test)
+ {
+ lt += ((out_fm[4]>>0) & OPN->pan[8]);
+ rt += ((out_fm[4]>>0) & OPN->pan[9]);
+ }
+ else
+ {
+ lt += dacout;
+ lt += dacout;
+ }
+ lt += ((out_fm[5]>>0) & OPN->pan[10]);
+ rt += ((out_fm[5]>>0) & OPN->pan[11]);
+
+// Limit( lt, MAXOUT, MINOUT );
+// Limit( rt, MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ SAVE_ALL_CHANNELS
+ #endif
+
+ /* buffering */
+ if (F2612->WaveOutMode & 0x01)
+ F2612->WaveL = lt;
+ if (F2612->WaveOutMode & 0x02)
+ F2612->WaveR = rt;
+ if (F2612->WaveOutMode ^ 0x03)
+ F2612->WaveOutMode ^= 0x03;
+ bufL[i] = F2612->WaveL;
+ bufR[i] = F2612->WaveR;
+
+ /* CSM mode: if CSM Key ON has occured, CSM Key OFF need to be sent */
+ /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */
+ OPN->SL3.key_csm <<= 1;
+
+ /* CSM Mode Key ON still disabled */
+ if (OPN->SL3.key_csm & 2)
+ {
+ /* CSM Mode Key OFF (verified by Nemesis on real hardware) */
+ FM_KEYOFF_CSM(cch[2],SLOT1);
+ FM_KEYOFF_CSM(cch[2],SLOT2);
+ FM_KEYOFF_CSM(cch[2],SLOT3);
+ FM_KEYOFF_CSM(cch[2],SLOT4);
+ OPN->SL3.key_csm = 0;
+ }
+ }
+}
+
+/* initialize YM2612 emulator(s) */
+//void * ym2612_init(void *param, running_device *device, int clock, int rate,
+// FM_TIMERHANDLER timer_handler,FM_IRQHANDLER IRQHandler)
+void * ym2612_init(int clock, int rate)
+{
+ YM2612 *F2612;
+
+ /* allocate extend state space */
+ //F2612 = auto_alloc_clear(device->machine, YM2612);
+ F2612 = (YM2612 *)malloc(sizeof(YM2612));
+ if (F2612 == NULL)
+ return NULL;
+ memset(F2612, 0x00, sizeof(YM2612));
+ /* allocate total level table (128kb space) */
+ init_tables();
+
+ F2612->OPN.type = TYPE_YM2612;
+ F2612->OPN.P_CH = F2612->CH;
+ //F2612->OPN.ST.device = device;
+ F2612->OPN.ST.clock = clock;
+ F2612->OPN.ST.rate = rate;
+ /* F2612->OPN.ST.irq = 0; */
+ /* F2612->OPN.ST.status = 0; */
+ /* Extend handler */
+
+ if (rate > clock / 128)
+ F2612->WaveOutMode = 0x01;
+ else
+ F2612->WaveOutMode = 0x03;
+ /*hFile = fopen("YM2612.log", "wt");
+ fprintf(hFile, "Clock: %d, Sample Rate: %d\n", clock, rate);
+ fprintf(hFile, "Sample\tCh 0\tCh 1\tCh 2\tCh 3\tCh 4\tCh 5\n");
+ FileSample = 0;*/
+
+ return F2612;
+}
+
+/* shut down emulator */
+void ym2612_shutdown(void *chip)
+{
+ YM2612 *F2612 = (YM2612 *)chip;
+ //fclose(hFile);
+
+ FMCloseTable();
+ //auto_free(F2612->OPN.ST.device->machine, F2612);
+ free(F2612);
+}
+
+/* reset one of chip */
+void ym2612_reset_chip(void *chip)
+{
+ int i;
+ YM2612 *F2612 = (YM2612 *)chip;
+ FM_OPN *OPN = &F2612->OPN;
+
+ OPNSetPres( OPN, 6*24, 6*24, 0);
+ /* status clear */
+ FM_IRQMASK_SET(&OPN->ST,0x03);
+ FM_BUSY_CLEAR(&OPN->ST);
+ //OPNWriteMode(OPN,0x27,0x30); /* mode 0 , timer reset */
+
+ OPN->eg_timer = 0;
+ OPN->eg_cnt = 0;
+
+ OPN->lfo_timer = 0;
+ OPN->lfo_cnt = 0;
+ OPN->LFO_AM = 0;
+ OPN->LFO_PM = 0;
+
+ OPN->ST.TAC = 0;
+ OPN->ST.TBC = 0;
+
+ OPN->SL3.key_csm = 0;
+
+ OPN->ST.status = 0;
+ OPN->ST.mode = 0;
+
+ memset(F2612->REGS, 0x00, sizeof(UINT8) * 512);
+
+ OPNWriteMode(OPN,0x22,0x00);
+
+ OPNWriteMode(OPN,0x27,0x30);
+ OPNWriteMode(OPN,0x26,0x00);
+ OPNWriteMode(OPN,0x25,0x00);
+ OPNWriteMode(OPN,0x24,0x00);
+
+ reset_channels( &OPN->ST , &F2612->CH[0] , 6 );
+
+ for(i = 0xb6 ; i >= 0xb4 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0xc0);
+ OPNWriteReg(OPN,i|0x100,0xc0);
+ }
+ for(i = 0xb2 ; i >= 0x30 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0);
+ OPNWriteReg(OPN,i|0x100,0);
+ }
+
+ /* DAC mode clear */
+ F2612->dacen = 0;
+ F2612->dac_test = 0;
+ F2612->dacout = 0;
+
+ if (F2612->WaveOutMode == 0x02)
+ F2612->WaveOutMode >>= 1;
+}
+
+/* YM2612 write */
+/* n = number */
+/* a = address */
+/* v = value */
+int ym2612_write(void *chip, int a, UINT8 v)
+{
+ YM2612 *F2612 = (YM2612 *)chip;
+ int addr;
+
+ v &= 0xff; /* adjust to 8 bit bus */
+
+ switch( a&3)
+ {
+ case 0: /* address port 0 */
+ F2612->OPN.ST.address = v;
+ F2612->addr_A1 = 0;
+ break;
+
+ case 1: /* data port 0 */
+ if (F2612->addr_A1 != 0)
+ break; /* verified on real YM2608 */
+
+ addr = F2612->OPN.ST.address;
+ F2612->REGS[addr] = v;
+ switch( addr & 0xf0 )
+ {
+ case 0x20: /* 0x20-0x2f Mode */
+ switch( addr )
+ {
+ case 0x2a: /* DAC data (YM2612) */
+ F2612->dacout = ((int)v - 0x80) << 6; /* level unknown */
+ break;
+ case 0x2b: /* DAC Sel (YM2612) */
+ /* b7 = dac enable */
+ F2612->dacen = v & 0x80;
+ break;
+ case 0x2C: // undocumented: DAC Test Reg
+ // b5 = volume enable
+ F2612->dac_test = v & 0x20;
+ break;
+ default: /* OPN section */
+ /* write register */
+ OPNWriteMode(&(F2612->OPN),addr,v);
+ }
+ break;
+ default: /* 0x30-0xff OPN section */
+ /* write register */
+ OPNWriteReg(&(F2612->OPN),addr,v);
+ }
+ break;
+
+ case 2: /* address port 1 */
+ F2612->OPN.ST.address = v;
+ F2612->addr_A1 = 1;
+ break;
+
+ case 3: /* data port 1 */
+ if (F2612->addr_A1 != 1)
+ break; /* verified on real YM2608 */
+
+ addr = F2612->OPN.ST.address;
+ F2612->REGS[addr | 0x100] = v;
+ OPNWriteReg(&(F2612->OPN),addr | 0x100,v);
+ break;
+ }
+ return F2612->OPN.ST.irq;
+}
+
+UINT8 ym2612_read(void *chip,int a)
+{
+ YM2612 *F2612 = (YM2612 *)chip;
+
+ switch( a&3)
+ {
+ case 0: /* status 0 */
+ return FM_STATUS_FLAG(&F2612->OPN.ST);
+ case 1:
+ case 2:
+ case 3:
+ //LOG(LOG_WAR,("YM2612 #%p:A=%d read unmapped area\n",F2612->OPN.ST.param,a));
+ return FM_STATUS_FLAG(&F2612->OPN.ST);
+ }
+ return 0;
+}
+
+int ym2612_timer_over(void *chip,int c)
+{
+ YM2612 *F2612 = (YM2612 *)chip;
+
+ if( c )
+ { /* Timer B */
+ TimerBOver( &(F2612->OPN.ST) );
+ }
+ else
+ { /* Timer A */
+ /* timer update */
+ TimerAOver( &(F2612->OPN.ST) );
+ /* CSM mode key,TL controll */
+ if ((F2612->OPN.ST.mode & 0xc0) == 0x80)
+ { /* CSM mode total level latch and auto key on */
+ CSMKeyControll( &F2612->OPN, &(F2612->CH[2]) );
+ }
+ }
+ return F2612->OPN.ST.irq;
+}
+
+
+void ym2612_set_mutemask(void *chip, UINT32 MuteMask)
+{
+ YM2612 *F2612 = (YM2612 *)chip;
+ UINT8 CurChn;
+
+ for (CurChn = 0; CurChn < 6; CurChn ++)
+ F2612->CH[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
+ F2612->MuteDAC = (MuteMask >> 6) & 0x01;
+
+ return;
+}
+
+void ym2612_setoptions(void *chip, UINT8 Flags)
+{
+ YM2612 *F2612 = (YM2612 *)chip;
+ F2612->OPN.LFOResetZero = (Flags >> 2) & 0x01;
+
+ return;
+}
+#endif /* (BUILD_YM2612||BUILD_YM3238) */
diff --git a/Frameworks/GME/gme/fmopl.cpp b/Frameworks/GME/gme/fmopl.cpp
new file mode 100644
index 000000000..179410ff8
--- /dev/null
+++ b/Frameworks/GME/gme/fmopl.cpp
@@ -0,0 +1,2621 @@
+/*
+**
+** File: fmopl.c - software implementation of FM sound generator
+** types OPL and OPL2
+**
+** Copyright Jarek Burczynski (bujar at mame dot net)
+** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+**
+** Version 0.72
+**
+
+Revision History:
+
+04-08-2003 Jarek Burczynski:
+ - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip
+ handles memory read/write or during the adpcm synthesis when the chip
+ requests another byte of ADPCM data.
+
+24-07-2003 Jarek Burczynski:
+ - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after
+ some (unknown) delay). Right now it's always set.
+
+14-06-2003 Jarek Burczynski:
+ - implemented all of the status register flags in Y8950 emulation
+ - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since
+ they can be either RAM or ROM
+
+08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip)
+ - corrected ym3526_read() to always set bit 2 and bit 1
+ to HIGH state - identical to ym3812_read (verified on real YM3526)
+
+04-28-2002 Jarek Burczynski:
+ - binary exact Envelope Generator (verified on real YM3812);
+ compared to YM2151: the EG clock is equal to internal_clock,
+ rates are 2 times slower and volume resolution is one bit less
+ - modified interface functions (they no longer return pointer -
+ that's internal to the emulator now):
+ - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init()
+ - corrected 'off by one' error in feedback calculations (when feedback is off)
+ - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22)
+ - speeded up noise generator calculations (Nicola Salmoria)
+
+03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip)
+ Complete rewrite (all verified on real YM3812):
+ - corrected sin_tab and tl_tab data
+ - corrected operator output calculations
+ - corrected waveform_select_enable register;
+ simply: ignore all writes to waveform_select register when
+ waveform_select_enable == 0 and do not change the waveform previously selected.
+ - corrected KSR handling
+ - corrected Envelope Generator: attack shape, Sustain mode and
+ Percussive/Non-percussive modes handling
+ - Envelope Generator rates are two times slower now
+ - LFO amplitude (tremolo) and phase modulation (vibrato)
+ - rhythm sounds phase generation
+ - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm)
+ - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM)
+ - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1)
+
+12-28-2001 Acho A. Tang
+ - reflected Delta-T EOS status on Y8950 status port.
+ - fixed subscription range of attack/decay tables
+
+
+ To do:
+ add delay before key off in CSM mode (see CSMKeyControll)
+ verify volume of the FM part on the Y8950
+*/
+
+#include
+#include
+#define _USE_MATH_DEFINES
+#include
+#include "fmopl.h"
+#include "ymdeltat.h"
+
+#ifndef INLINE
+#define INLINE __inline
+#endif
+#ifndef NULL
+ #define NULL ((void *)0)
+#endif
+#ifndef logerror
+#define logerror (void)
+#endif
+
+#ifndef M_PI
+ #define M_PI 3.14159265358979323846
+#endif
+
+/* output final shift */
+#if (OPL_SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<=0)
+ {
+ if (value < 0x0200)
+ return (value & ~0);
+ if (value < 0x0400)
+ return (value & ~1);
+ if (value < 0x0800)
+ return (value & ~3);
+ if (value < 0x1000)
+ return (value & ~7);
+ if (value < 0x2000)
+ return (value & ~15);
+ if (value < 0x4000)
+ return (value & ~31);
+ return (value & ~63);
+ }
+ /*else value < 0*/
+ if (value > -0x0200)
+ return (~abs(value) & ~0);
+ if (value > -0x0400)
+ return (~abs(value) & ~1);
+ if (value > -0x0800)
+ return (~abs(value) & ~3);
+ if (value > -0x1000)
+ return (~abs(value) & ~7);
+ if (value > -0x2000)
+ return (~abs(value) & ~15);
+ if (value > -0x4000)
+ return (~abs(value) & ~31);
+ return (~abs(value) & ~63);
+}
+
+
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = acc_calc(lt); \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = rt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+#define LOG_CYM_FILE 0
+//static FILE * cymfile = NULL;
+
+
+
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
+#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
+#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
+#define OPL_TYPE_IO 0x08 /* I/O port */
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
+
+
+
+typedef struct{
+ UINT32 ar; /* attack rate: AR<<2 */
+ UINT32 dr; /* decay rate: DR<<2 */
+ UINT32 rr; /* release rate:RR<<2 */
+ UINT8 KSR; /* key scale rate */
+ UINT8 ksl; /* keyscale level */
+ UINT8 ksr; /* key scale rate: kcode>>KSR */
+ UINT8 mul; /* multiple: mul_tab[ML] */
+
+ /* Phase Generator */
+ UINT32 Cnt; /* frequency counter */
+ UINT32 Incr; /* frequency counter step */
+ UINT8 FB; /* feedback shift value */
+ INT32 *connect1; /* slot1 output pointer */
+ INT32 op1_out[2]; /* slot1 output for feedback */
+ UINT8 CON; /* connection (algorithm) type */
+
+ /* Envelope Generator */
+ UINT8 eg_type; /* percussive/non-percussive mode */
+ UINT8 state; /* phase type */
+ UINT32 TL; /* total level: TL << 2 */
+ INT32 TLL; /* adjusted now TL */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level: sl_tab[SL] */
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_dr; /* (decay state) */
+ UINT8 eg_sel_dr; /* (decay state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
+ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/
+
+ /* waveform select */
+ UINT16 wavetable;
+} OPL_SLOT;
+
+typedef struct{
+ OPL_SLOT SLOT[2];
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum */
+ UINT32 fc; /* Freq. Increment base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 kcode; /* key code (for key scaling) */
+} OPL_CH;
+
+/* OPL state */
+typedef struct fm_opl_f {
+ /* FM channel slots */
+ OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */
+
+ UINT8 rhythm; /* Rhythm mode */
+
+ UINT32 fn_tab[1024]; /* fnumber->increment counter */
+
+ /* LFO */
+ UINT8 lfo_am_depth;
+ UINT8 lfo_pm_depth_range;
+ UINT32 lfo_am_cnt;
+ UINT32 lfo_am_inc;
+ UINT32 lfo_pm_cnt;
+ UINT32 lfo_pm_inc;
+
+ UINT32 noise_rng; /* 23 bit noise shift register */
+ UINT32 noise_p; /* current noise 'phase' */
+ UINT32 noise_f; /* current noise period */
+
+ UINT8 wavesel; /* waveform select enable flag */
+
+ UINT32 T[2]; /* timer counters */
+ UINT8 st[2]; /* timer enable */
+
+#if BUILD_Y8950
+ /* Delta-T ADPCM unit (Y8950) */
+
+ YM_DELTAT *deltat;
+
+ /* Keyboard and I/O ports interface */
+ UINT8 portDirection;
+ UINT8 portLatch;
+ OPL_PORTHANDLER_R porthandler_r;
+ OPL_PORTHANDLER_W porthandler_w;
+ void * port_param;
+ OPL_PORTHANDLER_R keyboardhandler_r;
+ OPL_PORTHANDLER_W keyboardhandler_w;
+ void * keyboard_param;
+#endif
+
+ /* external event callback handlers */
+ //OPL_TIMERHANDLER timer_handler; /* TIMER handler */
+ void *TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ void *IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */
+ void *UpdateParam; /* stream update parameter */
+
+ UINT8 type; /* chip type */
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT8 statusmask; /* status mask */
+ UINT8 mode; /* Reg.08 : CSM,notesel,etc. */
+
+ UINT32 clock; /* master clock (Hz) */
+ UINT32 rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ //attotime TimerBase; /* Timer base time (==sampling time)*/
+
+ OPL_SLOT *SLOT7_1, *SLOT7_2, *SLOT8_1, *SLOT8_2;
+
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+ signed int output[1];
+
+#if BUILD_Y8950
+ INT32 output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */
+#endif
+
+ UINT32 LFO_AM;
+ INT32 LFO_PM;
+} FM_OPL;
+
+
+
+/* mapping of register number (offset) to slot number used by the emulator */
+static const int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1
+};
+
+/* key scale level */
+/* table is 3dB/octave , DV converts this into 6dB/octave */
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
+#define DV (0.1875/2.0)
+static const UINT32 ksl_tab[8*16]=
+{
+ /* OCT 0 */
+ UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV),
+ UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV),
+ UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV),
+ UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV),
+ /* OCT 1 */
+ UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV),
+ UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV),
+ UINT32( 0.000/DV), UINT32( 0.750/DV), UINT32( 1.125/DV), UINT32( 1.500/DV),
+ UINT32( 1.875/DV), UINT32( 2.250/DV), UINT32( 2.625/DV), UINT32( 3.000/DV),
+ /* OCT 2 */
+ UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV),
+ UINT32( 0.000/DV), UINT32( 1.125/DV), UINT32( 1.875/DV), UINT32( 2.625/DV),
+ UINT32( 3.000/DV), UINT32( 3.750/DV), UINT32( 4.125/DV), UINT32( 4.500/DV),
+ UINT32( 4.875/DV), UINT32( 5.250/DV), UINT32( 5.625/DV), UINT32( 6.000/DV),
+ /* OCT 3 */
+ UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 1.875/DV),
+ UINT32( 3.000/DV), UINT32( 4.125/DV), UINT32( 4.875/DV), UINT32( 5.625/DV),
+ UINT32( 6.000/DV), UINT32( 6.750/DV), UINT32( 7.125/DV), UINT32( 7.500/DV),
+ UINT32( 7.875/DV), UINT32( 8.250/DV), UINT32( 8.625/DV), UINT32( 9.000/DV),
+ /* OCT 4 */
+ UINT32( 0.000/DV), UINT32( 0.000/DV), UINT32( 3.000/DV), UINT32( 4.875/DV),
+ UINT32( 6.000/DV), UINT32( 7.125/DV), UINT32( 7.875/DV), UINT32( 8.625/DV),
+ UINT32( 9.000/DV), UINT32( 9.750/DV), UINT32(10.125/DV), UINT32(10.500/DV),
+ UINT32(10.875/DV), UINT32(11.250/DV), UINT32(11.625/DV), UINT32(12.000/DV),
+ /* OCT 5 */
+ UINT32( 0.000/DV), UINT32( 3.000/DV), UINT32( 6.000/DV), UINT32( 7.875/DV),
+ UINT32( 9.000/DV), UINT32(10.125/DV), UINT32(10.875/DV), UINT32(11.625/DV),
+ UINT32(12.000/DV), UINT32(12.750/DV), UINT32(13.125/DV), UINT32(13.500/DV),
+ UINT32(13.875/DV), UINT32(14.250/DV), UINT32(14.625/DV), UINT32(15.000/DV),
+ /* OCT 6 */
+ UINT32( 0.000/DV), UINT32( 6.000/DV), UINT32( 9.000/DV), UINT32(10.875/DV),
+ UINT32(12.000/DV), UINT32(13.125/DV), UINT32(13.875/DV), UINT32(14.625/DV),
+ UINT32(15.000/DV), UINT32(15.750/DV), UINT32(16.125/DV), UINT32(16.500/DV),
+ UINT32(16.875/DV), UINT32(17.250/DV), UINT32(17.625/DV), UINT32(18.000/DV),
+ /* OCT 7 */
+ UINT32( 0.000/DV), UINT32( 9.000/DV), UINT32(12.000/DV), UINT32(13.875/DV),
+ UINT32(15.000/DV), UINT32(16.125/DV), UINT32(16.875/DV), UINT32(17.625/DV),
+ UINT32(18.000/DV), UINT32(18.750/DV), UINT32(19.125/DV), UINT32(19.500/DV),
+ UINT32(19.875/DV), UINT32(20.250/DV), UINT32(20.625/DV), UINT32(21.000/DV)
+};
+#undef DV
+
+/* sustain level table (3dB per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (UINT32) ( db * (2.0/ENV_STEP) )
+static const UINT32 sl_tab[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const unsigned char eg_inc[15*RATE_STEPS]={
+
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(13) in this table - it's directly in the code */
+static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+
+/* rates 00-12 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 13 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 14 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 15 */
+O(12),O(12),O(12),O(12),
+
+/* 16 dummy rates (same as 15 3) */
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
+/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */
+/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-12 */
+O(12),O(12),O(12),O(12),
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 16 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+
+};
+#undef O
+
+
+/* multiple table */
+#define ML 2
+static const UINT8 mul_tab[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
+ UINT8( 0.50*ML),UINT8( 1.00*ML),UINT8( 2.00*ML),UINT8( 3.00*ML),UINT8( 4.00*ML),UINT8( 5.00*ML),UINT8( 6.00*ML),UINT8( 7.00*ML),
+ UINT8( 8.00*ML),UINT8( 9.00*ML),UINT8(10.00*ML),UINT8(10.00*ML),UINT8(12.00*ML),UINT8(12.00*ML),UINT8(15.00*ML),UINT8(15.00*ML)
+};
+#undef ML
+
+/* TL_TAB_LEN is calculated as:
+* 12 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (12*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>4)
+
+/* sin waveform table in 'decibel' scale */
+/* four waveforms on OPL2 type chips */
+static unsigned int sin_tab[SIN_LEN * 4];
+
+
+/* LFO Amplitude Modulation table (verified on real YM3812)
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
+
+ Length: 210 elements.
+
+ Each of the elements has to be repeated
+ exactly 64 times (on 64 consecutive samples).
+ The whole table takes: 64 * 210 = 13440 samples.
+
+ When AM = 1 data is used directly
+ When AM = 0 data is divided by 4 before being used (loosing precision is important)
+*/
+
+#define LFO_AM_TAB_ELEMENTS 210
+
+static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
+0,0,0,0,0,0,0,
+1,1,1,1,
+2,2,2,2,
+3,3,3,3,
+4,4,4,4,
+5,5,5,5,
+6,6,6,6,
+7,7,7,7,
+8,8,8,8,
+9,9,9,9,
+10,10,10,10,
+11,11,11,11,
+12,12,12,12,
+13,13,13,13,
+14,14,14,14,
+15,15,15,15,
+16,16,16,16,
+17,17,17,17,
+18,18,18,18,
+19,19,19,19,
+20,20,20,20,
+21,21,21,21,
+22,22,22,22,
+23,23,23,23,
+24,24,24,24,
+25,25,25,25,
+26,26,26,
+25,25,25,25,
+24,24,24,24,
+23,23,23,23,
+22,22,22,22,
+21,21,21,21,
+20,20,20,20,
+19,19,19,19,
+18,18,18,18,
+17,17,17,17,
+16,16,16,16,
+15,15,15,15,
+14,14,14,14,
+13,13,13,13,
+12,12,12,12,
+11,11,11,11,
+10,10,10,10,
+9,9,9,9,
+8,8,8,8,
+7,7,7,7,
+6,6,6,6,
+5,5,5,5,
+4,4,4,4,
+3,3,3,3,
+2,2,2,2,
+1,1,1,1
+};
+
+/* LFO Phase Modulation table (verified on real YM3812) */
+static const INT8 lfo_pm_table[8*8*2] = {
+
+/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/
+};
+
+
+/* lock level of common table */
+//static int num_lock = 0;
+
+
+//static void *cur_chip = NULL; /* current chip pointer */
+
+INLINE int limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)
+{
+ /* set status flag */
+ OPL->status |= flag;
+ if(!(OPL->status & 0x80))
+ {
+ if(OPL->status & OPL->statusmask)
+ { /* IRQ on */
+ OPL->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
+{
+ /* reset status flag */
+ OPL->status &=~flag;
+ if((OPL->status & 0x80))
+ {
+ if (!(OPL->status & OPL->statusmask) )
+ {
+ OPL->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
+{
+ OPL->statusmask = flag;
+ /* IRQ handling check */
+ OPL_STATUS_SET(OPL,0);
+ OPL_STATUS_RESET(OPL,0);
+}
+
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(FM_OPL *OPL)
+{
+ UINT8 tmp;
+
+ /* LFO */
+ OPL->lfo_am_cnt += OPL->lfo_am_inc;
+ if (OPL->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt >> LFO_SH ];
+
+ if (OPL->lfo_am_depth)
+ OPL->LFO_AM = tmp;
+ else
+ OPL->LFO_AM = tmp>>2;
+
+ OPL->lfo_pm_cnt += OPL->lfo_pm_inc;
+ OPL->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range;
+}
+
+/* advance to next sample */
+INLINE void advance(FM_OPL *OPL)
+{
+ OPL_CH *CH;
+ OPL_SLOT *op;
+ int i;
+
+ OPL->eg_timer += OPL->eg_timer_add;
+
+ while (OPL->eg_timer >= OPL->eg_timer_overflow)
+ {
+ OPL->eg_timer -= OPL->eg_timer_overflow;
+
+ OPL->eg_cnt++;
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &OPL->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Envelope Generator */
+ switch(op->state)
+ {
+ case EG_ATT: /* attack phase */
+ if ( !(OPL->eg_cnt & ((1<eg_sh_ar)-1) ) )
+ {
+ op->volume += (~op->volume *
+ (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>3;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if ( !(OPL->eg_cnt & ((1<eg_sh_dr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)];
+
+ if ( op->volume >= op->sl )
+ op->state = EG_SUS;
+
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+
+ /* this is important behaviour:
+ one can change percusive/non-percussive modes on the fly and
+ the chip will remain in sustain phase - verified on real YM3812 */
+
+ if(op->eg_type) /* non-percussive mode */
+ {
+ /* do nothing */
+ }
+ else /* percussive mode */
+ {
+ /* during sustain phase chip adds Release Rate (in percussive mode) */
+ if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ op->volume = MAX_ATT_INDEX;
+ }
+ /* else do nothing in sustain phase */
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if ( !(OPL->eg_cnt & ((1<eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &OPL->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Phase Generator */
+ if(op->vib)
+ {
+ UINT8 block;
+ unsigned int block_fnum = CH->block_fnum;
+
+ unsigned int fnum_lfo = (block_fnum&0x0380) >> 7;
+
+ signed int lfo_fn_table_index_offset = lfo_pm_table[OPL->LFO_PM + 16*fnum_lfo ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum += lfo_fn_table_index_offset;
+ block = (block_fnum&0x1c00) >> 10;
+ op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+ else /* LFO phase modulation disabled for this operator */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+
+ /* The Noise Generator of the YM3812 is 23-bit shift register.
+ * Period is equal to 2^23-2 samples.
+ * Register works at sampling frequency of the chip, so output
+ * can change on every sample.
+ *
+ * Output of the register and input to the bit 22 is:
+ * bit0 XOR bit14 XOR bit15 XOR bit22
+ *
+ * Simply use bit 22 as the noise output.
+ */
+
+ OPL->noise_p += OPL->noise_f;
+ i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */
+ OPL->noise_p &= FREQ_MASK;
+ while (i)
+ {
+ /*
+ UINT32 j;
+ j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1;
+ OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1);
+ */
+
+ /*
+ Instead of doing all the logic operations above, we
+ use a trick here (and use bit 0 as the noise output).
+ The difference is only that the noise bit changes one
+ step ahead. This doesn't matter since we don't know
+ what is real state of the noise_rng after the reset.
+ */
+
+ if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302;
+ OPL->noise_rng >>= 1;
+
+ i--;
+ }
+}
+
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+
+#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (OPL->LFO_AM & (OP)->AMmask))
+
+/* calculate output */
+INLINE void OPL_CALC_CH( FM_OPL *OPL, OPL_CH *CH )
+{
+ OPL_SLOT *SLOT;
+ unsigned int env;
+ signed int out;
+
+ OPL->phase_modulation = 0;
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+ *SLOT->connect1 += SLOT->op1_out[0];
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable);
+}
+
+/*
+ operators used in the rhythm sounds generation process:
+
+ Envelope Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
+ 6 / 0 12 50 70 90 f0 +
+ 6 / 1 15 53 73 93 f3 +
+ 7 / 0 13 51 71 91 f1 +
+ 7 / 1 16 54 74 94 f4 +
+ 8 / 0 14 52 72 92 f2 +
+ 8 / 1 17 55 75 95 f5 +
+
+ Phase Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number MULTIPLE Drum Hat Drum Tom Cymbal
+ 6 / 0 12 30 +
+ 6 / 1 15 33 +
+ 7 / 0 13 31 + + +
+ 7 / 1 16 34 ----- n o t u s e d -----
+ 8 / 0 14 32 +
+ 8 / 1 17 35 + +
+
+channel operator register number Bass High Snare Tom Top
+number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
+ 6 12,15 B6 A6 +
+
+ 7 13,16 B7 A7 + + +
+
+ 8 14,17 B8 A8 + + +
+
+*/
+
+/* calculate rhythm */
+
+INLINE void OPL_CALC_RH( FM_OPL *OPL, OPL_CH *CH, unsigned int noise )
+{
+ OPL_SLOT *SLOT;
+ signed int out;
+ unsigned int env;
+
+
+ /* Bass Drum (verified on real YM3812):
+ - depends on the channel 6 'connect' register:
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
+ - output sample always is multiplied by 2
+ */
+
+ OPL->phase_modulation = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env = volume_calc(SLOT);
+
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+
+ if (!SLOT->CON)
+ OPL->phase_modulation = SLOT->op1_out[0];
+ /* else ignore output of operator 1 */
+
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable) * 2;
+
+
+ /* Phase generation is based on: */
+ /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */
+ /* SD (16) channel 7->slot 1 */
+ /* TOM (14) channel 8->slot 1 */
+ /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */
+
+ /* Envelope generation based on: */
+ /* HH channel 7->slot1 */
+ /* SD channel 7->slot2 */
+ /* TOM channel 8->slot1 */
+ /* TOP channel 8->slot2 */
+
+
+ /* The following formulas can be well optimized.
+ I leave them in direct form for now (in case I've missed something).
+ */
+
+ /* High Hat (verified on real YM3812) */
+ env = volume_calc(OPL->SLOT7_1);
+ if( env < ENV_QUIET )
+ {
+
+ /* high hat phase generation:
+ phase = d0 or 234 (based on frequency only)
+ phase = 34 or 2d0 (based on noise)
+ */
+
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0xd0; */
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */
+ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */
+ if (res2)
+ phase = (0x200|(0xd0>>2));
+
+
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */
+ if (phase&0x200)
+ {
+ if (noise)
+ phase = 0x200|0xd0;
+ }
+ else
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */
+ {
+ if (noise)
+ phase = 0xd0>>2;
+ }
+
+ OPL->output[0] += op_calc(phase<SLOT7_1->wavetable) * 2;
+ }
+
+ /* Snare Drum (verified on real YM3812) */
+ env = volume_calc(OPL->SLOT7_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit8 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>8)&1;
+
+ /* when bit8 = 0 phase = 0x100; */
+ /* when bit8 = 1 phase = 0x200; */
+ UINT32 phase = bit8 ? 0x200 : 0x100;
+
+ /* Noise bit XOR'es phase by 0x100 */
+ /* when noisebit = 0 pass the phase from calculation above */
+ /* when noisebit = 1 phase ^= 0x100; */
+ /* in other words: phase ^= (noisebit<<8); */
+ if (noise)
+ phase ^= 0x100;
+
+ OPL->output[0] += op_calc(phase<SLOT7_2->wavetable) * 2;
+ }
+
+ /* Tom Tom (verified on real YM3812) */
+ env = volume_calc(OPL->SLOT8_1);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(OPL->SLOT8_1->Cnt, env, 0, OPL->SLOT8_1->wavetable) * 2;
+
+ /* Top Cymbal (verified on real YM3812) */
+ env = volume_calc(OPL->SLOT8_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0x100; */
+ /* when res1 = 1 phase = 0x200 | 0x100; */
+ UINT32 phase = res1 ? 0x300 : 0x100;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | 0x100; */
+ if (res2)
+ phase = 0x300;
+
+ OPL->output[0] += op_calc(phase<SLOT8_2->wavetable) * 2;
+ }
+
+}
+
+
+/* generic table initialize */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+
+ for (x=0; x>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 1; /* 12 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<12; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<12; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] );
+ logerror("\n");
+ #endif
+ }
+ /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+
+
+ for (i=0; i0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+
+ /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/
+ }
+
+ for (i=0; i>1) ];
+
+ /* waveform 3: _ _ _ _ */
+ /* / |_/ |_/ |_/ |_*/
+ /* abs(output only first quarter of the sinus waveform) */
+
+ if (i & (1<<(SIN_BITS-2)) )
+ sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];
+
+ /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );
+ logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] );
+ logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/
+ }
+ /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/
+
+
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+
+ return 1;
+}
+
+static void OPLCloseTable( void )
+{
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+}
+
+
+
+static void OPL_initalize(FM_OPL *OPL)
+{
+ int i;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / 72.0) / OPL->rate : 0;
+#if 0
+ OPL->rate = (double)OPL->clock / 72.0;
+ OPL->freqbase = 1.0;
+#endif
+
+ /*logerror("freqbase=%f\n", OPL->freqbase);*/
+
+ /* Timer base time */
+ //OPL->TimerBase = attotime_mul(ATTOTIME_IN_HZ(OPL->clock), 72);
+
+ /* make fnumber -> increment counter table */
+ for( i=0 ; i < 1024 ; i++ )
+ {
+ /* opn phase increment counter = 20bit */
+ OPL->fn_tab[i] = (UINT32)( (double)i * 64 * OPL->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+#if 0
+ logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n",
+ i, OPL->fn_tab[i]>>6, OPL->fn_tab[i]>>6 );
+#endif
+ }
+
+#if 0
+ for( i=0 ; i < 16 ; i++ )
+ {
+ logerror("FMOPL.C: sl_tab[%i] = %08x\n",
+ i, sl_tab[i] );
+ }
+ for( i=0 ; i < 8 ; i++ )
+ {
+ int j;
+ logerror("FMOPL.C: ksl_tab[oct=%2i] =",i);
+ for (j=0; j<16; j++)
+ {
+ logerror("%08x ", ksl_tab[i*16+j] );
+ }
+ logerror("\n");
+ }
+#endif
+
+
+ /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */
+ OPL->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase;
+
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */
+ OPL->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase;
+
+ /*logerror ("OPL->lfo_am_inc = %8x ; OPL->lfo_pm_inc = %8x\n", OPL->lfo_am_inc, OPL->lfo_pm_inc);*/
+
+ /* Noise generator: a step takes 1 sample */
+ OPL->noise_f = (1.0 / 1.0) * (1<freqbase;
+
+ OPL->eg_timer_add = (1<freqbase;
+ OPL->eg_timer_overflow = ( 1 ) * (1<eg_timer_add, OPL->eg_timer_overflow);*/
+
+}
+
+INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set)
+{
+ if( !SLOT->key )
+ {
+ /* restart Phase Generator */
+ SLOT->Cnt = 0;
+ /* phase -> Attack */
+ SLOT->state = EG_ATT;
+ }
+ SLOT->key |= key_set;
+}
+
+INLINE void FM_KEYOFF(OPL_SLOT *SLOT, UINT32 key_clr)
+{
+ if( SLOT->key )
+ {
+ SLOT->key &= key_clr;
+
+ if( !SLOT->key )
+ {
+ /* phase -> Release */
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;
+ }
+ }
+}
+
+/* update phase increment counter of operator (also update the EG rates if necessary) */
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+ int ksr;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+ }
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = mul_tab[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_type = (v&0x20);
+ SLOT->vib = (v&0x40);
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ksl = v>>6; /* 0 / 1.5 / 3.0 / 6.0 dB/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */
+
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+
+ SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->sl = sl_tab[ v>>4 ];
+
+ SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+}
+
+
+/* write a value v to register r on OPL chip */
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+ OPL_CH *CH;
+ int slot;
+ UINT32 block_fnum;
+
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+ /*if (LOG_CYM_FILE && (cymfile) && (r!=0) )
+ {
+ fputc( (unsigned char)r, cymfile );
+ fputc( (unsigned char)v, cymfile );
+ }*/
+
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:control */
+ switch(r&0x1f)
+ {
+ case 0x01: /* waveform select enable */
+ if(OPL->type&OPL_TYPE_WAVESEL)
+ {
+ OPL->wavesel = v&0x20;
+ /* do not change the waveform previously selected */
+ }
+ break;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v)*16;
+ break;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flag clear */
+ OPL_STATUS_RESET(OPL,0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */
+ }
+ else
+ { /* set IRQ mask ,timer enable*/
+ UINT8 st1 = v&1;
+ UINT8 st2 = (v>>1)&1;
+
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ OPL_STATUS_RESET(OPL, v & (0x78-0x08) );
+ OPL_STATUSMASK_SET(OPL, (~v) & 0x78 );
+
+ /* timer 2 */
+ /*if(OPL->st[1] != st2)
+ {
+ attotime period = st2 ? attotime_mul(OPL->TimerBase, OPL->T[1]) : attotime_zero;
+ OPL->st[1] = st2;
+ if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,1,period);
+ }*/
+ /* timer 1 */
+ /*if(OPL->st[0] != st1)
+ {
+ attotime period = st1 ? attotime_mul(OPL->TimerBase, OPL->T[0]) : attotime_zero;
+ OPL->st[0] = st1;
+ if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,0,period);
+ }*/
+ }
+ break;
+#if BUILD_Y8950
+ case 0x06: /* Key Board OUT */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_w)
+ OPL->keyboardhandler_w(OPL->keyboard_param,v);
+ else
+ logerror("Y8950: write unmapped KEYBOARD port\n");
+ }
+ break;
+ case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ break;
+#endif
+ case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+ OPL->mode = v;
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */
+#endif
+ break;
+
+#if BUILD_Y8950
+ case 0x09: /* START ADD */
+ case 0x0a:
+ case 0x0b: /* STOP ADD */
+ case 0x0c:
+ case 0x0d: /* PRESCALE */
+ case 0x0e:
+ case 0x0f: /* ADPCM data write */
+ case 0x10: /* DELTA-N */
+ case 0x11: /* DELTA-N */
+ case 0x12: /* ADPCM volume */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ break;
+
+ case 0x15: /* DAC data high 8 bits (F7,F6...F2) */
+ case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */
+ case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */
+ logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v);
+ break;
+
+ case 0x18: /* I/O CTRL (Direction) */
+ if(OPL->type&OPL_TYPE_IO)
+ OPL->portDirection = v&0x0f;
+ break;
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ OPL->portLatch = v;
+ if(OPL->porthandler_w)
+ OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);
+ }
+ break;
+#endif
+ default:
+ logerror("FMOPL.C: write to unknown register: %02x\n",r);
+ break;
+ }
+ break;
+ case 0x20: /* am ON, vib ON, ksr, eg_type, mul */
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_mul(OPL,slot,v);
+ break;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ksl_tl(OPL,slot,v);
+ break;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ar_dr(OPL,slot,v);
+ break;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_sl_rr(OPL,slot,v);
+ break;
+ case 0xa0:
+ if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */
+ {
+ OPL->lfo_am_depth = v & 0x80;
+ OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0;
+
+ OPL->rhythm = v&0x3f;
+
+ if(OPL->rhythm&0x20)
+ {
+ /* BD key on/off */
+ if(v&0x10)
+ {
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2);
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2);
+ }
+ else
+ {
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);
+ }
+ /* HH key on/off */
+ if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key on/off */
+ if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key on/off */
+ if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY key on/off */
+ if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);
+ }
+ else
+ {
+ /* BD key off */
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);
+ /* HH key off */
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key off */
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key off */
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY off */
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ UINT8 block = block_fnum >> 10;
+
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = ksl_tab[block_fnum>>6];
+ CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block);
+
+ /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */
+ CH->kcode = (CH->block_fnum&0x1c00)>>9;
+
+ /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */
+ /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */
+ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */
+ if (OPL->mode&0x40)
+ CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */
+ else
+ CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */
+
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ break;
+ case 0xc0:
+ /* FB,C */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;
+ CH->SLOT[SLOT1].CON = v&1;
+ CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &OPL->output[0] : &OPL->phase_modulation;
+ break;
+ case 0xe0: /* waveform select */
+ /* simply ignore write to the waveform select register if selecting not enabled in test register */
+ if(OPL->wavesel)
+ {
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ CH = &OPL->P_CH[slot/2];
+
+ CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN;
+ }
+ break;
+ }
+}
+
+/*static TIMER_CALLBACK( cymfile_callback )
+{
+ if (cymfile)
+ {
+ fputc( (unsigned char)0, cymfile );
+ }
+}*/
+
+/* lock/unlock for common table */
+#if 0
+static int OPL_LockTable(/*const device_config *device*/)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+
+ /* first time */
+
+ cur_chip = NULL;
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ num_lock--;
+ return -1;
+ }
+
+#if 0
+ if (LOG_CYM_FILE)
+ {
+ cymfile = fopen("3812_.cym","wb");
+ if (cymfile)
+ timer_pulse ( device->machine, ATTOTIME_IN_HZ(110), NULL, 0, cymfile_callback); /*110 Hz pulse timer*/
+ else
+ logerror("Could not create file 3812_.cym\n");
+ }
+#endif
+
+ return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+
+ /* last time */
+
+ cur_chip = NULL;
+ OPLCloseTable();
+
+ /*if (cymfile)
+ fclose (cymfile);
+ cymfile = NULL;*/
+}
+#endif
+
+static void OPLResetChip(FM_OPL *OPL)
+{
+ int c,s;
+ int i;
+
+ OPL->eg_timer = 0;
+ OPL->eg_cnt = 0;
+
+ OPL->noise_rng = 1; /* noise shift register */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL,0x7f);
+
+ /* reset with register write */
+ OPLWriteReg(OPL,0x01,0); /* wavesel disable */
+ OPLWriteReg(OPL,0x02,0); /* Timer1 */
+ OPLWriteReg(OPL,0x03,0); /* Timer2 */
+ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
+ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
+
+ /* reset operator parameters */
+ for( c = 0 ; c < 9 ; c++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[c];
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = 0;
+ CH->SLOT[s].state = EG_OFF;
+ CH->SLOT[s].volume = MAX_ATT_INDEX;
+ }
+ }
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ DELTAT->freqbase = OPL->freqbase;
+ DELTAT->output_pointer = &OPL->output_deltat[0];
+ DELTAT->portshift = 5;
+ DELTAT->output_range = 1<<23;
+ YM_DELTAT_ADPCM_Reset(DELTAT,0,YM_DELTAT_EMULATION_MODE_NORMAL);
+ }
+#endif
+}
+
+
+#if 0
+static STATE_POSTLOAD( OPL_postload )
+{
+ FM_OPL *OPL = (FM_OPL *)param;
+ int slot, ch;
+
+ for( ch=0 ; ch < 9 ; ch++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[ch];
+
+ /* Look up key scale level */
+ UINT32 block_fnum = CH->block_fnum;
+ CH->ksl_base = ksl_tab[block_fnum >> 6];
+ CH->fc = OPL->fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10));
+
+ for( slot=0 ; slot < 2 ; slot++ )
+ {
+ OPL_SLOT *SLOT = &CH->SLOT[slot];
+
+ /* Calculate key scale rate */
+ SLOT->ksr = CH->kcode >> SLOT->KSR;
+
+ /* Calculate attack, decay and release rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+
+ /* Calculate phase increment */
+ SLOT->Incr = CH->fc * SLOT->mul;
+
+ /* Total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl);
+
+ /* Connect output */
+ SLOT->connect1 = SLOT->CON ? &OPL->output[0] : &OPL->phase_modulation;
+ }
+ }
+#if BUILD_Y8950
+ if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )
+ {
+ // We really should call the postlod function for the YM_DELTAT, but it's hard without registers
+ // (see the way the YM2610 does it)
+ //YM_DELTAT_postload(OPL->deltat, REGS);
+ }
+#endif
+}
+
+
+static void OPLsave_state_channel(const device_config *device, OPL_CH *CH)
+{
+ int slot, ch;
+
+ for( ch=0 ; ch < 9 ; ch++, CH++ )
+ {
+ /* channel */
+ state_save_register_device_item(device, ch, CH->block_fnum);
+ state_save_register_device_item(device, ch, CH->kcode);
+ /* slots */
+ for( slot=0 ; slot < 2 ; slot++ )
+ {
+ OPL_SLOT *SLOT = &CH->SLOT[slot];
+
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->ar);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->dr);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->rr);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->KSR);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->ksl);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->mul);
+
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->Cnt);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->FB);
+ state_save_register_device_item_array(device, ch * 2 + slot, SLOT->op1_out);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->CON);
+
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->eg_type);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->state);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->TL);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->volume);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->sl);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->key);
+
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->AMmask);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->vib);
+
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->wavetable);
+ }
+ }
+}
+
+
+/* Register savestate for a virtual YM3812/YM3526Y8950 */
+
+static void OPL_save_state(FM_OPL *OPL, const device_config *device)
+{
+ OPLsave_state_channel(device, OPL->P_CH);
+
+ state_save_register_device_item(device, 0, OPL->eg_cnt);
+ state_save_register_device_item(device, 0, OPL->eg_timer);
+
+ state_save_register_device_item(device, 0, OPL->rhythm);
+
+ state_save_register_device_item(device, 0, OPL->lfo_am_depth);
+ state_save_register_device_item(device, 0, OPL->lfo_pm_depth_range);
+ state_save_register_device_item(device, 0, OPL->lfo_am_cnt);
+ state_save_register_device_item(device, 0, OPL->lfo_pm_cnt);
+
+ state_save_register_device_item(device, 0, OPL->noise_rng);
+ state_save_register_device_item(device, 0, OPL->noise_p);
+
+ if( OPL->type & OPL_TYPE_WAVESEL )
+ {
+ state_save_register_device_item(device, 0, OPL->wavesel);
+ }
+
+ state_save_register_device_item_array(device, 0, OPL->T);
+ state_save_register_device_item_array(device, 0, OPL->st);
+
+#if BUILD_Y8950
+ if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )
+ {
+ YM_DELTAT_savestate(device, OPL->deltat);
+ }
+
+ if ( OPL->type & OPL_TYPE_IO )
+ {
+ state_save_register_device_item(device, 0, OPL->portDirection);
+ state_save_register_device_item(device, 0, OPL->portLatch);
+ }
+#endif
+
+ state_save_register_device_item(device, 0, OPL->address);
+ state_save_register_device_item(device, 0, OPL->status);
+ state_save_register_device_item(device, 0, OPL->statusmask);
+ state_save_register_device_item(device, 0, OPL->mode);
+
+ state_save_register_postload(device->machine, OPL_postload, OPL);
+}
+#endif
+
+
+/* Create one of virtual YM3812/YM3526/Y8950 */
+/* 'clock' is chip clock in Hz */
+/* 'rate' is sampling rate */
+static FM_OPL *OPLCreate(UINT32 clock, UINT32 rate, int type)
+{
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+
+ //if (OPL_LockTable(device) == -1) return NULL;
+ init_tables();
+
+ /* calculate OPL state size */
+ state_size = sizeof(FM_OPL);
+
+#if BUILD_Y8950
+ if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
+#endif
+
+ /* allocate memory block */
+ ptr = (char *)malloc(state_size);
+
+ if (ptr==NULL)
+ return 0;
+
+ /* clear */
+ memset(ptr,0,state_size);
+
+ OPL = (FM_OPL *)ptr;
+
+ ptr += sizeof(FM_OPL);
+
+#if BUILD_Y8950
+ if (type&OPL_TYPE_ADPCM)
+ {
+ OPL->deltat = (YM_DELTAT *)ptr;
+ }
+ ptr += sizeof(YM_DELTAT);
+#endif
+
+ OPL->type = type;
+ OPL->clock = clock;
+ OPL->rate = rate;
+
+ /* init global tables */
+ OPL_initalize(OPL);
+
+ return OPL;
+}
+
+/* Destroy one of virtual YM3812 */
+static void OPLDestroy(FM_OPL *OPL)
+{
+ //OPL_UnLockTable();
+ free(OPL);
+}
+
+/* Optional handlers */
+
+/*static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,void *param)
+{
+ OPL->timer_handler = timer_handler;
+ OPL->TimerParam = param;
+}*/
+static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+
+static int OPLWrite(FM_OPL *OPL,int a,int v)
+{
+ if( !(a&1) )
+ { /* address port */
+ OPL->address = v & 0xff;
+ }
+ else
+ { /* data port */
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ OPLWriteReg(OPL,OPL->address,v);
+ }
+ return OPL->status>>7;
+}
+
+static unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+ if( !(a&1) )
+ {
+ /* status port */
+
+ #if BUILD_Y8950
+
+ if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */
+ {
+ return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1);
+ }
+
+ #endif
+
+ /* OPL and OPL2 */
+ return OPL->status & (OPL->statusmask|0x80);
+ }
+
+#if BUILD_Y8950
+ /* data port */
+ switch(OPL->address)
+ {
+ case 0x05: /* KeyBoard IN */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_r)
+ return OPL->keyboardhandler_r(OPL->keyboard_param);
+ else
+ logerror("Y8950: read unmapped KEYBOARD port\n");
+ }
+ return 0;
+
+ case 0x0f: /* ADPCM-DATA */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ UINT8 val;
+
+ val = YM_DELTAT_ADPCM_Read(OPL->deltat);
+ /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/
+ return val;
+ }
+ return 0;
+
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ if(OPL->porthandler_r)
+ return OPL->porthandler_r(OPL->port_param);
+ else
+ logerror("Y8950:read unmapped I/O port\n");
+ }
+ return 0;
+ case 0x1a: /* PCM-DATA */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ logerror("Y8950 A/D convertion is accessed but not implemented !\n");
+ return 0x80; /* 2's complement PCM data - result from A/D convertion */
+ }
+ return 0;
+ }
+#endif
+
+ return 0xff;
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH)
+{
+ FM_KEYON (&CH->SLOT[SLOT1], 4);
+ FM_KEYON (&CH->SLOT[SLOT2], 4);
+
+ /* The key off should happen exactly one sample later - not implemented correctly yet */
+
+ FM_KEYOFF(&CH->SLOT[SLOT1], ~4);
+ FM_KEYOFF(&CH->SLOT[SLOT2], ~4);
+}
+
+
+static int OPLTimerOver(FM_OPL *OPL,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL_STATUS_SET(OPL,0x20);
+ }
+ else
+ { /* Timer A */
+ OPL_STATUS_SET(OPL,0x40);
+ /* CSM mode key,TL controll */
+ if( OPL->mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ int ch;
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ for(ch=0; ch<9; ch++)
+ CSMKeyControll( &OPL->P_CH[ch] );
+ }
+ }
+ /* reload timer */
+ //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,attotime_mul(OPL->TimerBase, OPL->T[c]));
+ return OPL->status>>7;
+}
+
+
+#define MAX_OPL_CHIPS 2
+
+
+#if (BUILD_YM3812)
+
+void * ym3812_init(UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *YM3812 = OPLCreate(clock,rate,OPL_TYPE_YM3812);
+ if (YM3812)
+ {
+ //OPL_save_state(YM3812);
+ ym3812_reset_chip(YM3812);
+ }
+ return YM3812;
+}
+
+void ym3812_shutdown(void *chip)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+
+ /* emulator shutdown */
+ OPLDestroy(YM3812);
+}
+void ym3812_reset_chip(void *chip)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLResetChip(YM3812);
+}
+
+int ym3812_write(void *chip, int a, int v)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ return OPLWrite(YM3812, a, v);
+}
+
+unsigned char ym3812_read(void *chip, int a)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ /* YM3812 always returns bit2 and bit1 in HIGH state */
+ return OPLRead(YM3812, a) | 0x06 ;
+}
+int ym3812_timer_over(void *chip, int c)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ return OPLTimerOver(YM3812, c);
+}
+
+/*void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetTimerHandler(YM3812, timer_handler, param);
+}*/
+void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetIRQHandler(YM3812, IRQHandler, param);
+}
+void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(YM3812, UpdateHandler, param);
+}
+
+
+/*
+** Generate samples for one of the YM3812's
+**
+** 'which' is the virtual YM3812 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ OPLSAMPLE *buf = buffer;
+ int i;
+
+ /* rhythm slots */
+ OPL->SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1];
+ OPL->SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2];
+ OPL->SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1];
+ OPL->SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2];
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0];
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+#endif /* BUILD_YM3812 */
+
+
+
+#if (BUILD_YM3526)
+
+void *ym3526_init(UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *YM3526 = OPLCreate(clock,rate,OPL_TYPE_YM3526);
+ if (YM3526)
+ {
+ /*OPL_save_state(YM3526);*/
+ ym3526_reset_chip(YM3526);
+ }
+ return YM3526;
+}
+
+void ym3526_shutdown(void *chip)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ /* emulator shutdown */
+ OPLDestroy(YM3526);
+}
+void ym3526_reset_chip(void *chip)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLResetChip(YM3526);
+}
+
+int ym3526_write(void *chip, int a, int v)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ return OPLWrite(YM3526, a, v);
+}
+
+unsigned char ym3526_read(void *chip, int a)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ /* YM3526 always returns bit2 and bit1 in HIGH state */
+ return OPLRead(YM3526, a) | 0x06 ;
+}
+int ym3526_timer_over(void *chip, int c)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ return OPLTimerOver(YM3526, c);
+}
+
+/*void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetTimerHandler(YM3526, timer_handler, param);
+}*/
+void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetIRQHandler(YM3526, IRQHandler, param);
+}
+void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(YM3526, UpdateHandler, param);
+}
+
+
+/*
+** Generate samples for one of the YM3526's
+**
+** 'which' is the virtual YM3526 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ OPLSAMPLE *buf = buffer;
+ int i;
+
+ /* rhythm slots */
+ OPL->SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1];
+ OPL->SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2];
+ OPL->SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1];
+ OPL->SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2];
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0];
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+#endif /* BUILD_YM3526 */
+
+
+
+
+#if BUILD_Y8950
+
+static void Y8950_deltat_status_set(void *chip, UINT8 changebits)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPL_STATUS_SET(Y8950, changebits);
+}
+static void Y8950_deltat_status_reset(void *chip, UINT8 changebits)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPL_STATUS_RESET(Y8950, changebits);
+}
+
+void *y8950_init(UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *Y8950 = OPLCreate(clock,rate,OPL_TYPE_Y8950);
+ if (Y8950)
+ {
+ Y8950->deltat->status_set_handler = Y8950_deltat_status_set;
+ Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset;
+ Y8950->deltat->status_change_which_chip = Y8950;
+ Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */
+ Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */
+
+ /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */
+ /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */
+ /* reset */
+ /*OPL_save_state(Y8950);*/
+ y8950_reset_chip(Y8950);
+ }
+
+ return Y8950;
+}
+
+void y8950_shutdown(void *chip)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ /* emulator shutdown */
+ OPLDestroy(Y8950);
+}
+void y8950_reset_chip(void *chip)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLResetChip(Y8950);
+}
+
+int y8950_write(void *chip, int a, int v)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLWrite(Y8950, a, v);
+}
+
+unsigned char y8950_read(void *chip, int a)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLRead(Y8950, a);
+}
+int y8950_timer_over(void *chip, int c)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLTimerOver(Y8950, c);
+}
+
+/*void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetTimerHandler(Y8950, timer_handler, param);
+}*/
+void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetIRQHandler(Y8950, IRQHandler, param);
+}
+void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(Y8950, UpdateHandler, param);
+}
+
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size )
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->deltat->memory = (UINT8 *)(deltat_mem_ptr);
+ OPL->deltat->memory_size = deltat_mem_size;
+}
+
+/*
+** Generate samples for one of the Y8950's
+**
+** 'which' is the virtual Y8950 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ int i;
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ YM_DELTAT *DELTAT = OPL->deltat;
+ OPLSAMPLE *buf = buffer;
+
+ /* rhythm slots */
+ OPL->SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1];
+ OPL->SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2];
+ OPL->SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1];
+ OPL->SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2];
+
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+ OPL->output_deltat[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* deltaT ADPCM */
+ if( DELTAT->portstate&0x80 )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0] + (OPL->output_deltat[0]>>11);
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+
+void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,void * param)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->porthandler_w = PortHandler_w;
+ OPL->porthandler_r = PortHandler_r;
+ OPL->port_param = param;
+}
+
+void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,void * param)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->keyboardhandler_w = KeyboardHandler_w;
+ OPL->keyboardhandler_r = KeyboardHandler_r;
+ OPL->keyboard_param = param;
+}
+
+#endif
+
diff --git a/Frameworks/GME/gme/fmopl.h b/Frameworks/GME/gme/fmopl.h
new file mode 100644
index 000000000..868d38bec
--- /dev/null
+++ b/Frameworks/GME/gme/fmopl.h
@@ -0,0 +1,116 @@
+#pragma once
+
+#ifndef __FMOPL_H__
+#define __FMOPL_H__
+
+/* --- select emulation chips --- */
+#define BUILD_YM3812 (1)
+#define BUILD_YM3526 (1)
+#define BUILD_Y8950 (1)
+
+/* select output bits size of output : 8 or 16 */
+#define OPL_SAMPLE_BITS 16
+
+/* compiler dependence */
+#ifndef __OSDCOMM_H__
+#define __OSDCOMM_H__
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+
+typedef INT32 stream_sample_t;
+
+#endif /* __OSDCOMM_H__ */
+
+typedef stream_sample_t OPLSAMPLE;
+/*
+#if (OPL_SAMPLE_BITS==16)
+typedef INT16 OPLSAMPLE;
+#endif
+#if (OPL_SAMPLE_BITS==8)
+typedef INT8 OPLSAMPLE;
+#endif
+*/
+
+//typedef void (*OPL_TIMERHANDLER)(void *param,int timer,attotime period);
+typedef void (*OPL_IRQHANDLER)(void *param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(void *param,int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(void *param,unsigned char data);
+typedef unsigned char (*OPL_PORTHANDLER_R)(void *param);
+
+
+#if BUILD_YM3812
+
+void *ym3812_init(UINT32 clock, UINT32 rate);
+void ym3812_shutdown(void *chip);
+void ym3812_reset_chip(void *chip);
+int ym3812_write(void *chip, int a, int v);
+unsigned char ym3812_read(void *chip, int a);
+int ym3812_timer_over(void *chip, int c);
+void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+//void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);
+void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#endif /* BUILD_YM3812 */
+
+
+#if BUILD_YM3526
+
+/*
+** Initialize YM3526 emulator(s).
+**
+** 'num' is the number of virtual YM3526's to allocate
+** 'clock' is the chip clock in Hz
+** 'rate' is sampling rate
+*/
+void *ym3526_init(UINT32 clock, UINT32 rate);
+/* shutdown the YM3526 emulators*/
+void ym3526_shutdown(void *chip);
+void ym3526_reset_chip(void *chip);
+int ym3526_write(void *chip, int a, int v);
+unsigned char ym3526_read(void *chip, int a);
+int ym3526_timer_over(void *chip, int c);
+/*
+** Generate samples for one of the YM3526's
+**
+** 'which' is the virtual YM3526 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+//void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);
+void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#endif /* BUILD_YM3526 */
+
+
+#if BUILD_Y8950
+
+/* Y8950 port handlers */
+void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, void *param);
+void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, void *param);
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size );
+
+void * y8950_init(UINT32 clock, UINT32 rate);
+void y8950_shutdown(void *chip);
+void y8950_reset_chip(void *chip);
+int y8950_write(void *chip, int a, int v);
+unsigned char y8950_read (void *chip, int a);
+int y8950_timer_over(void *chip, int c);
+void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+//void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);
+void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#endif /* BUILD_Y8950 */
+
+
+#endif /* __FMOPL_H__ */
diff --git a/Frameworks/GME/gme/gme_custom_dprintf.c b/Frameworks/GME/gme/gme_custom_dprintf.c
new file mode 100644
index 000000000..0b4503948
--- /dev/null
+++ b/Frameworks/GME/gme/gme_custom_dprintf.c
@@ -0,0 +1,4 @@
+#include "gme_custom_dprintf.h"
+
+
+gme_custom_dprintf_callback gme_custom_dprintf = 0;
diff --git a/Frameworks/GME/gme/gme_custom_dprintf.h b/Frameworks/GME/gme/gme_custom_dprintf.h
new file mode 100644
index 000000000..9f71baf1c
--- /dev/null
+++ b/Frameworks/GME/gme/gme_custom_dprintf.h
@@ -0,0 +1,28 @@
+#ifndef GME_CUSTOM_DPRINTF_H
+#define GME_CUSTOM_DPRINTF_H
+
+
+#ifdef CUSTOM_DPRINTF_FUNCTION
+
+
+#include
+
+
+#ifdef _cplusplus
+extern "C" {
+#endif
+
+
+typedef void (*gme_custom_dprintf_callback)( const char * fmt, va_list vl );
+extern gme_custom_dprintf_callback gme_custom_dprintf;
+
+
+#ifdef _cplusplus
+}
+#endif
+
+
+#endif
+
+
+#endif
diff --git a/Frameworks/GME/gme/i_fmpac.h b/Frameworks/GME/gme/i_fmpac.h
new file mode 100644
index 000000000..fecba35d8
--- /dev/null
+++ b/Frameworks/GME/gme/i_fmpac.h
@@ -0,0 +1,38 @@
+ 0x49, 0x4C, 0x4C, 0x32, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x33, 0x21, 0x09, 0x0E, 0x94, 0x90, 0x48, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x13, 0x41, 0x0F, 0x0D, 0xCE, 0xD3, 0x43, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x12, 0x1B, 0x06, 0xFF, 0xD2, 0x00, 0x32,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x1B, 0x07, 0xAF, 0x63, 0x20, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x21, 0x15, 0x00, 0x93, 0x94, 0x20, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x10, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x21, 0x20, 0x1F, 0xC0, 0x71, 0x07, 0x47,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x25, 0x31, 0x26, 0x05, 0x64, 0x41, 0x18, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x17, 0x21, 0x28, 0x07, 0xFF, 0x83, 0x02, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x97, 0x81, 0x25, 0x07, 0xCF, 0xC8, 0x02, 0x14,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x21, 0x54, 0x0F, 0x80, 0x7F, 0x07, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x56, 0x03, 0xD3, 0xB2, 0x43, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x21, 0x0C, 0x03, 0x82, 0xC0, 0x40, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x01, 0x0C, 0x03, 0xD4, 0xD3, 0x40, 0x84,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x21, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x22, 0x00, 0x00, 0xA8, 0xF8, 0xF8, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x25, 0x18, 0x00, 0x00, 0xF8, 0xA9, 0xF8, 0x55,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/Frameworks/GME/gme/i_fmunit.h b/Frameworks/GME/gme/i_fmunit.h
new file mode 100644
index 000000000..3030810e5
--- /dev/null
+++ b/Frameworks/GME/gme/i_fmunit.h
@@ -0,0 +1,38 @@
+ 0x49, 0x4C, 0x4C, 0x32, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x1E, 0x07, 0xF0, 0x7E, 0x07, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x13, 0x41, 0x0F, 0x1D, 0xCE, 0xD2, 0x43, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x01, 0x99, 0x04, 0xFF, 0xC3, 0x03, 0x73,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x61, 0x1B, 0x07, 0xAF, 0x63, 0x40, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x61, 0x1D, 0x07, 0x32, 0x81, 0x10, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x21, 0x2D, 0x16, 0xC0, 0x70, 0x07, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x21, 0x1B, 0x06, 0x64, 0x65, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x0C, 0x18, 0x85, 0xA0, 0x79, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x21, 0x87, 0x11, 0xF0, 0xA4, 0x00, 0xF7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x97, 0xE1, 0x28, 0x07, 0xFF, 0xF3, 0x02, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x10, 0x0C, 0x05, 0xF2, 0xC4, 0x40, 0xC8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x56, 0x03, 0xB4, 0xB2, 0x23, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x41, 0x89, 0x03, 0xF1, 0xF4, 0xF0, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x21, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x02, 0x00, 0x00, 0xA7, 0xF7, 0x07, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x1F, 0x00, 0x00, 0xF8, 0xA9, 0x08, 0x05,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/Frameworks/GME/gme/i_vrc7.h b/Frameworks/GME/gme/i_vrc7.h
new file mode 100644
index 000000000..56bbfa9d4
--- /dev/null
+++ b/Frameworks/GME/gme/i_vrc7.h
@@ -0,0 +1,38 @@
+ 0x49, 0x4C, 0x4C, 0x32, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x1E, 0x07, 0xF0, 0x7E, 0x07, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x13, 0x41, 0x0F, 0x1D, 0xCE, 0xD2, 0x43, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x01, 0x99, 0x04, 0xFF, 0xC3, 0x03, 0x73,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x61, 0x1B, 0x07, 0xAF, 0x63, 0x40, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x61, 0x1D, 0x07, 0x32, 0x81, 0x10, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x21, 0x2D, 0x16, 0xC0, 0x70, 0x07, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x21, 0x1B, 0x06, 0x64, 0x65, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x0C, 0x18, 0x85, 0xA0, 0x79, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x21, 0x87, 0x11, 0xF0, 0xA4, 0x00, 0xF7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x97, 0xE1, 0x28, 0x07, 0xFF, 0xF3, 0x02, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x10, 0x0C, 0x05, 0xF2, 0xC4, 0x40, 0xC8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x56, 0x03, 0xB4, 0xB2, 0x23, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x41, 0x89, 0x03, 0xF1, 0xF4, 0xF0, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x21, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x21, 0x00, 0x00, 0xA8, 0xF8, 0xF8, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x25, 0x18, 0x00, 0x00, 0xF8, 0xA9, 0xF8, 0x55,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/Frameworks/GME/gme/k051649.c b/Frameworks/GME/gme/k051649.c
new file mode 100644
index 000000000..599d9ecd2
--- /dev/null
+++ b/Frameworks/GME/gme/k051649.c
@@ -0,0 +1,298 @@
+/***************************************************************************
+
+ Konami 051649 - SCC1 sound as used in Haunted Castle, City Bomber
+
+ This file is pieced together by Bryan McPhail from a combination of
+ Namco Sound, Amuse by Cab, Haunted Castle schematics and whoever first
+ figured out SCC!
+
+ The 051649 is a 5 channel sound generator, each channel gets it's
+ waveform from RAM (32 bytes per waveform, 8 bit signed data).
+
+ This sound chip is the same as the sound chip in some Konami
+ megaROM cartridges for the MSX. It is actually well researched
+ and documented:
+
+ http://www.msxnet.org/tech/scc
+
+ Thanks to Sean Young (sean@mess.org) for some bugfixes.
+
+ K052539 is equivalent to this chip except channel 5 does not share
+ waveforms with channel 4.
+
+***************************************************************************/
+
+#include "mamedef.h"
+#include
+#include
+//#include "emu.h"
+//#include "streams.h"
+#include "k051649.h"
+
+#define FREQBASEBITS 16
+
+/* this structure defines the parameters for a channel */
+typedef struct
+{
+ unsigned long counter;
+ int frequency;
+ int volume;
+ int key;
+ signed char waveform[32]; /* 19991207.CAB */
+ UINT8 Muted;
+} k051649_sound_channel;
+
+typedef struct _k051649_state k051649_state;
+struct _k051649_state
+{
+ k051649_sound_channel channel_list[5];
+
+ /* global sound parameters */
+ //sound_stream * stream;
+ int mclock,rate;
+
+ /* mixer tables and internal buffers */
+ INT16 *mixer_table;
+ INT16 *mixer_lookup;
+ short *mixer_buffer;
+
+ int f[10];
+ int cur_reg;
+};
+
+/*INLINE k051649_state *get_safe_token(running_device *device)
+{
+ assert(device != NULL);
+ assert(device->type() == K051649);
+ return (k051649_state *)downcast(device)->token();
+}*/
+
+/* build a table to divide by the number of voices */
+static void make_mixer_table(/*running_machine *machine,*/ k051649_state *info, int voices)
+{
+ int count = voices * 256;
+ int i;
+ int gain = 8;
+
+ /* allocate memory */
+ //info->mixer_table = auto_alloc_array(machine, INT16, 512 * voices);
+ info->mixer_table = (INT16*)malloc(sizeof(INT16) * 512 * voices);
+
+ /* find the middle of the table */
+ info->mixer_lookup = info->mixer_table + (256 * voices);
+
+ /* fill in the table - 16 bit case */
+ for (i = 0; i < count; i++)
+ {
+ int val = i * gain * 16 / voices;
+ if (val > 32767) val = 32767;
+ info->mixer_lookup[ i] = val;
+ info->mixer_lookup[-i] = -val;
+ }
+}
+
+
+/* generate sound to the mix buffer */
+//static STREAM_UPDATE( k051649_update )
+void k051649_update(void *chip, stream_sample_t **outputs, int samples)
+{
+ k051649_state *info = (k051649_state *) chip;
+ k051649_sound_channel *voice=info->channel_list;
+ stream_sample_t *buffer = outputs[0];
+ stream_sample_t *buffer2 = outputs[1];
+ short *mix;
+ int i,v,f,j,k;
+
+ /* zap the contents of the mixer buffer */
+ memset(info->mixer_buffer, 0, samples * sizeof(short));
+
+ for (j=0; j<5; j++) {
+ v=voice[j].volume;
+ f=voice[j].frequency;
+ k=voice[j].key;
+ /* SY 20040109: the SCC produces no sound for freq < 9 */
+ if (v && f > 8 && k && ! voice[j].Muted)
+ {
+ const signed char *w = voice[j].waveform; /* 19991207.CAB */
+ int c=voice[j].counter;
+
+ mix = info->mixer_buffer;
+
+ /* add our contribution */
+ for (i = 0; i < samples; i++)
+ {
+ int offs;
+
+ /* Amuse source: Cab suggests this method gives greater resolution */
+ /* Sean Young 20010417: the formula is really: f = clock/(16*(f+1))*/
+ c+=(long)((((float)info->mclock / (float)((f+1) * 16))*(float)(1<rate / 32));
+ offs = (c >> 16) & 0x1f;
+ *mix++ += (w[offs] * v)>>3;
+ }
+
+ /* update the counter for this voice */
+ voice[j].counter = c;
+ }
+ }
+
+ /* mix it down */
+ mix = info->mixer_buffer;
+ for (i = 0; i < samples; i++)
+ *buffer++ = *buffer2++ = info->mixer_lookup[*mix++];
+}
+
+//static DEVICE_START( k051649 )
+void * device_start_k051649(int clock)
+{
+ //k051649_state *info = get_safe_token(device);
+ k051649_state *info;
+ UINT8 CurChn;
+
+ info = (k051649_state *) calloc(1, sizeof(k051649_state));
+ /* get stream channels */
+ //info->rate = device->clock()/16;
+ info->rate = clock/16;
+ //info->stream = stream_create(device, 0, 1, info->rate, info, k051649_update);
+ //info->mclock = device->clock();
+ info->mclock = clock;
+
+ /* allocate a buffer to mix into - 1 second's worth should be more than enough */
+ //info->mixer_buffer = auto_alloc_array(device->machine, short, 2 * info->rate);
+ info->mixer_buffer = (short*)malloc(sizeof(short) * info->rate);
+
+ /* build the mixer table */
+ //make_mixer_table(device->machine, info, 5);
+ make_mixer_table(info, 5);
+
+ for (CurChn = 0; CurChn < 5; CurChn ++)
+ info->channel_list[CurChn].Muted = 0x00;
+
+ return info; //return info->rate;
+}
+
+void device_stop_k051649(void *chip)
+{
+ k051649_state *info = (k051649_state *) chip;
+
+ free(info->mixer_buffer);
+ free(info->mixer_table);
+ free(info);
+}
+
+//static DEVICE_RESET( k051649 )
+void device_reset_k051649(void *chip)
+{
+ k051649_state *info = (k051649_state *) chip;
+ k051649_sound_channel *voice = info->channel_list;
+ int i;
+
+ /* reset all the voices */
+ for (i = 0; i < 5; i++) {
+ voice[i].frequency = 0;
+ voice[i].volume = 0;
+ voice[i].counter = 0;
+ }
+}
+
+/********************************************************************************/
+
+//WRITE8_DEVICE_HANDLER( k051649_waveform_w )
+void k051649_waveform_w(void *chip, offs_t offset, UINT8 data)
+{
+ k051649_state *info = (k051649_state *) chip;
+ //stream_update(info->stream);
+ info->channel_list[offset>>5].waveform[offset&0x1f]=data;
+ /* SY 20001114: Channel 5 shares the waveform with channel 4 */
+ if (offset >= 0x60)
+ info->channel_list[4].waveform[offset&0x1f]=data;
+}
+
+//READ8_DEVICE_HANDLER ( k051649_waveform_r )
+UINT8 k051649_waveform_r(void *chip, offs_t offset)
+{
+ k051649_state *info = (k051649_state *) chip;
+ return info->channel_list[offset>>5].waveform[offset&0x1f];
+}
+
+/* SY 20001114: Channel 5 doesn't share the waveform with channel 4 on this chip */
+//WRITE8_DEVICE_HANDLER( k052539_waveform_w )
+void k052539_waveform_w(void *chip, offs_t offset, UINT8 data)
+{
+ k051649_state *info = (k051649_state *) chip;
+ //stream_update(info->stream);
+ info->channel_list[offset>>5].waveform[offset&0x1f]=data;
+}
+
+//WRITE8_DEVICE_HANDLER( k051649_volume_w )
+void k051649_volume_w(void *chip, offs_t offset, UINT8 data)
+{
+ k051649_state *info = (k051649_state *) chip;
+ //stream_update(info->stream);
+ info->channel_list[offset&0x7].volume=data&0xf;
+}
+
+//WRITE8_DEVICE_HANDLER( k051649_frequency_w )
+void k051649_frequency_w(void *chip, offs_t offset, UINT8 data)
+{
+ k051649_state *info = (k051649_state *) chip;
+ info->f[offset]=data;
+
+ //stream_update(info->stream);
+ info->channel_list[offset>>1].frequency=(info->f[offset&0xe] + (info->f[offset|1]<<8))&0xfff;
+}
+
+//WRITE8_DEVICE_HANDLER( k051649_keyonoff_w )
+void k051649_keyonoff_w(void *chip, offs_t offset, UINT8 data)
+{
+ k051649_state *info = (k051649_state *) chip;
+ //stream_update(info->stream);
+ info->channel_list[0].key=data&1;
+ info->channel_list[1].key=data&2;
+ info->channel_list[2].key=data&4;
+ info->channel_list[3].key=data&8;
+ info->channel_list[4].key=data&16;
+}
+
+void k051649_w(void *chip, offs_t offset, UINT8 data)
+{
+ k051649_state *info = (k051649_state *) chip;
+
+ switch(offset & 1)
+ {
+ case 0x00:
+ info->cur_reg = data;
+ break;
+ case 0x01:
+ switch(offset >> 1)
+ {
+ case 0x00:
+ k051649_waveform_w(info, info->cur_reg, data);
+ break;
+ case 0x01:
+ k051649_frequency_w(info, info->cur_reg, data);
+ break;
+ case 0x02:
+ k051649_volume_w(info, info->cur_reg, data);
+ break;
+ case 0x03:
+ k051649_keyonoff_w(info, info->cur_reg, data);
+ break;
+ case 0x04:
+ k052539_waveform_w(info, info->cur_reg, data);
+ break;
+ }
+ break;
+ }
+
+ return;
+}
+
+
+void k051649_set_mute_mask(void *chip, UINT32 MuteMask)
+{
+ k051649_state *info = (k051649_state *) chip;
+ UINT8 CurChn;
+
+ for (CurChn = 0; CurChn < 5; CurChn ++)
+ info->channel_list[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
+}
diff --git a/Frameworks/GME/gme/k051649.h b/Frameworks/GME/gme/k051649.h
new file mode 100644
index 000000000..584bb7ff7
--- /dev/null
+++ b/Frameworks/GME/gme/k051649.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include "mamedef.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//#ifndef __K051649_H__
+//#define __K051649_H__
+
+//#include "devlegcy.h"
+
+/*WRITE8_DEVICE_HANDLER( k051649_waveform_w );
+READ8_DEVICE_HANDLER( k051649_waveform_r );
+WRITE8_DEVICE_HANDLER( k051649_volume_w );
+WRITE8_DEVICE_HANDLER( k051649_frequency_w );
+WRITE8_DEVICE_HANDLER( k051649_keyonoff_w );
+
+WRITE8_DEVICE_HANDLER( k052539_waveform_w );
+
+DECLARE_LEGACY_SOUND_DEVICE(K051649, k051649);*/
+
+void k051649_update(void *, stream_sample_t **outputs, int samples);
+void * device_start_k051649(int clock);
+void device_stop_k051649(void *);
+void device_reset_k051649(void *);
+
+void k051649_waveform_w(void *, offs_t offset, UINT8 data);
+UINT8 k051649_waveform_r(void *, offs_t offset);
+void k051649_volume_w(void *, offs_t offset, UINT8 data);
+void k051649_frequency_w(void *, offs_t offset, UINT8 data);
+void k051649_keyonoff_w(void *, offs_t offset, UINT8 data);
+
+void k052539_waveform_w(void *, offs_t offset, UINT8 data);
+
+void k051649_w(void *, offs_t offset, UINT8 data);
+
+void k051649_set_mute_mask(void *, UINT32 MuteMask);
+
+//#endif /* __K051649_H__ */
+
+#ifdef __cplusplus
+};
+#endif
diff --git a/Frameworks/GME/gme/k053260.c b/Frameworks/GME/gme/k053260.c
new file mode 100644
index 000000000..c00087957
--- /dev/null
+++ b/Frameworks/GME/gme/k053260.c
@@ -0,0 +1,507 @@
+/*********************************************************
+
+ Konami 053260 PCM Sound Chip
+
+*********************************************************/
+
+#include "mamedef.h"
+//#include "emu.h"
+#ifdef _DEBUG
+#include
+#endif
+#include
+#include
+#include "k053260.h"
+
+#define NULL ((void *)0)
+
+/* 2004-02-28: Fixed PPCM decoding. Games sound much better now.*/
+
+#define LOG 0
+
+#define BASE_SHIFT 16
+
+typedef struct _k053260_channel k053260_channel;
+struct _k053260_channel
+{
+ UINT32 rate;
+ UINT32 size;
+ UINT32 start;
+ UINT32 bank;
+ UINT32 volume;
+ int play;
+ UINT32 pan;
+ UINT32 pos;
+ int loop;
+ int ppcm; /* packed PCM ( 4 bit signed ) */
+ int ppcm_data;
+ UINT8 Muted;
+};
+
+typedef struct _k053260_state k053260_state;
+struct _k053260_state
+{
+ //sound_stream * channel;
+ int mode;
+ int regs[0x30];
+ UINT8 *rom;
+ //int rom_size;
+ UINT32 rom_size;
+ UINT32 *delta_table;
+ k053260_channel channels[4];
+ //const k053260_interface *intf;
+ //device_t *device;
+};
+
+/*INLINE k053260_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == K053260);
+ return (k053260_state *)downcast(device)->token();
+}*/
+
+
+static void InitDeltaTable( k053260_state *ic, int rate, int clock )
+{
+ int i;
+ double base = ( double )rate;
+ double max = (double)(clock); /* Hz */
+ UINT32 val;
+
+ for( i = 0; i < 0x1000; i++ ) {
+ double v = ( double )( 0x1000 - i );
+ double target = (max) / v;
+ double fixed = ( double )( 1 << BASE_SHIFT );
+
+ if ( target && base ) {
+ target = fixed / ( base / target );
+ val = ( UINT32 )target;
+ if ( val == 0 )
+ val = 1;
+ } else
+ val = 1;
+
+ ic->delta_table[i] = val;
+ }
+}
+
+//static DEVICE_RESET( k053260 )
+void device_reset_k053260(void *chip)
+{
+ //k053260_state *ic = get_safe_token(device);
+ k053260_state *ic = (k053260_state *) chip;
+ int i;
+
+ for( i = 0; i < 4; i++ ) {
+ ic->channels[i].rate = 0;
+ ic->channels[i].size = 0;
+ ic->channels[i].start = 0;
+ ic->channels[i].bank = 0;
+ ic->channels[i].volume = 0;
+ ic->channels[i].play = 0;
+ ic->channels[i].pan = 0;
+ ic->channels[i].pos = 0;
+ ic->channels[i].loop = 0;
+ ic->channels[i].ppcm = 0;
+ ic->channels[i].ppcm_data = 0;
+ }
+}
+
+INLINE int limit( int val, int max, int min )
+{
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+//#define MAXOUT 0x7fff
+#define MAXOUT +0x8000
+#define MINOUT -0x8000
+
+//static STREAM_UPDATE( k053260_update )
+void k053260_update(void *chip, stream_sample_t **outputs, int samples)
+{
+ static const long dpcmcnv[] = { 0,1,2,4,8,16,32,64, -128, -64, -32, -16, -8, -4, -2, -1};
+
+ int i, j, lvol[4], rvol[4], play[4], loop[4], ppcm_data[4], ppcm[4];
+ unsigned char *rom[4];
+ UINT32 delta[4], end[4], pos[4];
+ int dataL, dataR;
+ signed char d;
+ k053260_state *ic = (k053260_state *) chip;
+
+ /* precache some values */
+ for ( i = 0; i < 4; i++ ) {
+ rom[i]= &ic->rom[ic->channels[i].start + ( ic->channels[i].bank << 16 )];
+ delta[i] = ic->delta_table[ic->channels[i].rate];
+ lvol[i] = ic->channels[i].volume * ic->channels[i].pan;
+ rvol[i] = ic->channels[i].volume * ( 8 - ic->channels[i].pan );
+ end[i] = ic->channels[i].size;
+ pos[i] = ic->channels[i].pos;
+ play[i] = ic->channels[i].play;
+ loop[i] = ic->channels[i].loop;
+ ppcm[i] = ic->channels[i].ppcm;
+ ppcm_data[i] = ic->channels[i].ppcm_data;
+ if ( ppcm[i] )
+ delta[i] /= 2;
+ }
+
+ for ( j = 0; j < samples; j++ ) {
+
+ dataL = dataR = 0;
+
+ for ( i = 0; i < 4; i++ ) {
+ /* see if the voice is on */
+ if ( play[i] ) {
+ /* see if we're done */
+ if ( ( pos[i] >> BASE_SHIFT ) >= end[i] ) {
+
+ ppcm_data[i] = 0;
+ if ( loop[i] )
+ pos[i] = 0;
+ else {
+ play[i] = 0;
+ continue;
+ }
+ }
+
+ if ( ppcm[i] ) { /* Packed PCM */
+ /* we only update the signal if we're starting or a real sound sample has gone by */
+ /* this is all due to the dynamic sample rate convertion */
+ if ( pos[i] == 0 || ( ( pos[i] ^ ( pos[i] - delta[i] ) ) & 0x8000 ) == 0x8000 )
+
+ {
+ int newdata;
+ if ( pos[i] & 0x8000 ){
+
+ newdata = ((rom[i][pos[i] >> BASE_SHIFT]) >> 4) & 0x0f; /*high nybble*/
+ }
+ else{
+ newdata = ( ( rom[i][pos[i] >> BASE_SHIFT] ) ) & 0x0f; /*low nybble*/
+ }
+
+ ppcm_data[i] = (( ( ppcm_data[i] * 62 ) >> 6 ) + dpcmcnv[newdata]);
+
+ if ( ppcm_data[i] > 127 )
+ ppcm_data[i] = 127;
+ else
+ if ( ppcm_data[i] < -128 )
+ ppcm_data[i] = -128;
+ }
+
+
+
+ d = ppcm_data[i];
+
+ pos[i] += delta[i];
+ } else { /* PCM */
+ d = rom[i][pos[i] >> BASE_SHIFT];
+
+ pos[i] += delta[i];
+ }
+
+ if ( ic->mode & 2 ) {
+ dataL += ( d * lvol[i] ) >> 2;
+ dataR += ( d * rvol[i] ) >> 2;
+ }
+ }
+ }
+
+ outputs[1][j] = limit( dataL, MAXOUT, MINOUT );
+ outputs[0][j] = limit( dataR, MAXOUT, MINOUT );
+ }
+
+ /* update the regs now */
+ for ( i = 0; i < 4; i++ ) {
+ ic->channels[i].pos = pos[i];
+ ic->channels[i].play = play[i];
+ ic->channels[i].ppcm_data = ppcm_data[i];
+ }
+}
+
+//static DEVICE_START( k053260 )
+void * device_start_k053260(int clock)
+{
+ //static const k053260_interface defintrf = { 0 };
+ //k053260_state *ic = get_safe_token(device);
+ k053260_state *ic;
+ //int rate = device->clock() / 32;
+ int rate = clock / 32;
+ int i;
+
+ ic = (k053260_state *) calloc(1, sizeof(k053260_state));
+
+ /* Initialize our chip structure */
+ //ic->device = device;
+ //ic->intf = (device->static_config() != NULL) ? (const k053260_interface *)device->static_config() : &defintrf;
+
+ ic->mode = 0;
+
+ //const memory_region *region = (ic->intf->rgnoverride != NULL) ? device->machine().region(ic->intf->rgnoverride) : device->region();
+
+ //ic->rom = *region;
+ //ic->rom_size = region->bytes();
+ ic->rom = NULL;
+ ic->rom_size = 0x00;
+
+ // has to be done by the player after calling device_start
+ //DEVICE_RESET_CALL(k053260);
+
+ for ( i = 0; i < 0x30; i++ )
+ ic->regs[i] = 0;
+
+ //ic->delta_table = auto_alloc_array( device->machine(), UINT32, 0x1000 );
+ ic->delta_table = (UINT32*)malloc(0x1000 * sizeof(UINT32));
+
+ //ic->channel = device->machine().sound().stream_alloc( *device, 0, 2, rate, ic, k053260_update );
+
+ //InitDeltaTable( ic, rate, device->clock() );
+ InitDeltaTable( ic, rate, clock );
+
+ /* register with the save state system */
+ /*device->save_item(NAME(ic->mode));
+ device->save_item(NAME(ic->regs));
+
+ for ( i = 0; i < 4; i++ )
+ {
+ device->save_item(NAME(ic->channels[i].rate), i);
+ device->save_item(NAME(ic->channels[i].size), i);
+ device->save_item(NAME(ic->channels[i].start), i);
+ device->save_item(NAME(ic->channels[i].bank), i);
+ device->save_item(NAME(ic->channels[i].volume), i);
+ device->save_item(NAME(ic->channels[i].play), i);
+ device->save_item(NAME(ic->channels[i].pan), i);
+ device->save_item(NAME(ic->channels[i].pos), i);
+ device->save_item(NAME(ic->channels[i].loop), i);
+ device->save_item(NAME(ic->channels[i].ppcm), i);
+ device->save_item(NAME(ic->channels[i].ppcm_data), i);
+ }*/
+
+ /* setup SH1 timer if necessary */
+ //if ( ic->intf->irq )
+ // device->machine().scheduler().timer_pulse( attotime::from_hz(device->clock()) * 32, ic->intf->irq, "ic->intf->irq" );
+
+ return ic; //return rate;
+}
+
+void device_stop_k053260(void *chip)
+{
+ k053260_state *ic = (k053260_state *) chip;
+
+ free(ic->delta_table);
+ free(ic->rom); ic->rom = NULL;
+ free(ic);
+}
+
+INLINE void check_bounds( k053260_state *ic, int channel )
+{
+
+ UINT32 channel_start = ( ic->channels[channel].bank << 16 ) + ic->channels[channel].start;
+ UINT32 channel_end = channel_start + ic->channels[channel].size - 1;
+
+ if ( channel_start > ic->rom_size ) {
+ logerror("K53260: Attempting to start playing past the end of the ROM ( start = %06x, end = %06x ).\n", channel_start, channel_end );
+
+ ic->channels[channel].play = 0;
+
+ return;
+ }
+
+ if ( channel_end > ic->rom_size ) {
+ logerror("K53260: Attempting to play past the end of the ROM ( start = %06x, end = %06x ).\n", channel_start, channel_end );
+
+ ic->channels[channel].size = ic->rom_size - channel_start;
+ }
+ if (LOG) logerror("K053260: Sample Start = %06x, Sample End = %06x, Sample rate = %04x, PPCM = %s\n", channel_start, channel_end, ic->channels[channel].rate, ic->channels[channel].ppcm ? "yes" : "no" );
+}
+
+//WRITE8_DEVICE_HANDLER( k053260_w )
+void k053260_w(void *chip, offs_t offset, UINT8 data)
+{
+ int i, t;
+ int r = offset;
+ int v = data;
+
+ k053260_state *ic = (k053260_state *) chip;
+
+ if ( r > 0x2f ) {
+ logerror("K053260: Writing past registers\n" );
+ return;
+ }
+
+ //ic->channel->update();
+
+ /* before we update the regs, we need to check for a latched reg */
+ if ( r == 0x28 ) {
+ t = ic->regs[r] ^ v;
+
+ for ( i = 0; i < 4; i++ ) {
+ if ( t & ( 1 << i ) ) {
+ if ( v & ( 1 << i ) ) {
+ ic->channels[i].play = 1;
+ ic->channels[i].pos = 0;
+ ic->channels[i].ppcm_data = 0;
+ check_bounds( ic, i );
+ } else
+ ic->channels[i].play = 0;
+ }
+ }
+
+ ic->regs[r] = v;
+ return;
+ }
+
+ /* update regs */
+ ic->regs[r] = v;
+
+ /* communication registers */
+ if ( r < 8 )
+ return;
+
+ /* channel setup */
+ if ( r < 0x28 ) {
+ int channel = ( r - 8 ) / 8;
+
+ switch ( ( r - 8 ) & 0x07 ) {
+ case 0: /* sample rate low */
+ ic->channels[channel].rate &= 0x0f00;
+ ic->channels[channel].rate |= v;
+ break;
+
+ case 1: /* sample rate high */
+ ic->channels[channel].rate &= 0x00ff;
+ ic->channels[channel].rate |= ( v & 0x0f ) << 8;
+ break;
+
+ case 2: /* size low */
+ ic->channels[channel].size &= 0xff00;
+ ic->channels[channel].size |= v;
+ break;
+
+ case 3: /* size high */
+ ic->channels[channel].size &= 0x00ff;
+ ic->channels[channel].size |= v << 8;
+ break;
+
+ case 4: /* start low */
+ ic->channels[channel].start &= 0xff00;
+ ic->channels[channel].start |= v;
+ break;
+
+ case 5: /* start high */
+ ic->channels[channel].start &= 0x00ff;
+ ic->channels[channel].start |= v << 8;
+ break;
+
+ case 6: /* bank */
+ ic->channels[channel].bank = v & 0xff;
+ break;
+
+ case 7: /* volume is 7 bits. Convert to 8 bits now. */
+ ic->channels[channel].volume = ( ( v & 0x7f ) << 1 ) | ( v & 1 );
+ break;
+ }
+
+ return;
+ }
+
+ switch( r ) {
+ case 0x2a: /* loop, ppcm */
+ for ( i = 0; i < 4; i++ )
+ ic->channels[i].loop = ( v & ( 1 << i ) ) != 0;
+
+ for ( i = 4; i < 8; i++ )
+ ic->channels[i-4].ppcm = ( v & ( 1 << i ) ) != 0;
+ break;
+
+ case 0x2c: /* pan */
+ ic->channels[0].pan = v & 7;
+ ic->channels[1].pan = ( v >> 3 ) & 7;
+ break;
+
+ case 0x2d: /* more pan */
+ ic->channels[2].pan = v & 7;
+ ic->channels[3].pan = ( v >> 3 ) & 7;
+ break;
+
+ case 0x2f: /* control */
+ ic->mode = v & 7;
+ /* bit 0 = read ROM */
+ /* bit 1 = enable sound output */
+ /* bit 2 = unknown */
+ break;
+ }
+}
+
+//READ8_DEVICE_HANDLER( k053260_r )
+UINT8 k053260_r(void *chip, offs_t offset)
+{
+ k053260_state *ic = (k053260_state *) chip;
+
+ switch ( offset ) {
+ case 0x29: /* channel status */
+ {
+ int i, status = 0;
+
+ for ( i = 0; i < 4; i++ )
+ status |= ic->channels[i].play << i;
+
+ return status;
+ }
+ break;
+
+ case 0x2e: /* read ROM */
+ if ( ic->mode & 1 )
+ {
+ UINT32 offs = ic->channels[0].start + ( ic->channels[0].pos >> BASE_SHIFT ) + ( ic->channels[0].bank << 16 );
+
+ ic->channels[0].pos += ( 1 << 16 );
+
+ if ( offs > ic->rom_size ) {
+ //logerror("%s: K53260: Attempting to read past ROM size in ROM Read Mode (offs = %06x, size = %06x).\n", device->machine().describe_context(),offs,ic->rom_size );
+ logerror("K53260: Attempting to read past ROM size in ROM Read Mode (offs = %06x, size = %06x).\n", offs,ic->rom_size );
+
+ return 0;
+ }
+
+ return ic->rom[offs];
+ }
+ break;
+ }
+
+ return ic->regs[offset];
+}
+
+void k053260_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
+ const UINT8* ROMData)
+{
+ k053260_state *info = (k053260_state *) chip;
+
+ if (info->rom_size != ROMSize)
+ {
+ info->rom = (UINT8*)realloc(info->rom, ROMSize);
+ info->rom_size = ROMSize;
+ memset(info->rom, 0xFF, ROMSize);
+ }
+ if (DataStart > ROMSize)
+ return;
+ if (DataStart + DataLength > ROMSize)
+ DataLength = ROMSize - DataStart;
+
+ memcpy(info->rom + DataStart, ROMData, DataLength);
+
+ return;
+}
+
+
+void k053260_set_mute_mask(void *chip, UINT32 MuteMask)
+{
+ k053260_state *info = (k053260_state *) chip;
+ UINT8 CurChn;
+
+ for (CurChn = 0; CurChn < 4; CurChn ++)
+ info->channels[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
+}
diff --git a/Frameworks/GME/gme/k053260.h b/Frameworks/GME/gme/k053260.h
new file mode 100644
index 000000000..25eb198ad
--- /dev/null
+++ b/Frameworks/GME/gme/k053260.h
@@ -0,0 +1,42 @@
+/*********************************************************
+
+ Konami 053260 PCM/ADPCM Sound Chip
+
+*********************************************************/
+
+#pragma once
+
+#include "mamedef.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//#include "devlegcy.h"
+
+/*typedef struct _k053260_interface k053260_interface;
+struct _k053260_interface {
+ const char *rgnoverride;
+ timer_expired_func irq; // called on SH1 complete cycle ( clock / 32 ) //
+};*/
+
+
+void k053260_update(void *, stream_sample_t **outputs, int samples);
+void * device_start_k053260(int clock);
+void device_stop_k053260(void *);
+void device_reset_k053260(void *);
+
+//WRITE8_DEVICE_HANDLER( k053260_w );
+//READ8_DEVICE_HANDLER( k053260_r );
+void k053260_w(void *, offs_t offset, UINT8 data);
+UINT8 k053260_r(void *, offs_t offset);
+
+void k053260_write_rom(void *, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
+ const UINT8* ROMData);
+void k053260_set_mute_mask(void *, UINT32 MuteMask);
+
+//DECLARE_LEGACY_SOUND_DEVICE(K053260, k053260);
+
+#ifdef __cplusplus
+};
+#endif
diff --git a/Frameworks/GME/gme/k054539.c b/Frameworks/GME/gme/k054539.c
new file mode 100644
index 000000000..373c90bf0
--- /dev/null
+++ b/Frameworks/GME/gme/k054539.c
@@ -0,0 +1,787 @@
+/*********************************************************
+
+ Konami 054539 PCM Sound Chip
+
+ A lot of information comes from Amuse.
+ Big thanks to them.
+
+
+
+CHANNEL_DEBUG enables the following keys:
+
+ PAD. : toggle debug mode
+ PAD0 : toggle chip (0 / 1)
+ PAD4,6 : select channel (0 - 7)
+ PAD8,2 : adjust gain (00=0.0 10=1.0, 20=2.0, etc.)
+ PAD5 : reset gain factor to 1.0
+
+*********************************************************/
+
+//#include "emu.h"
+#include
+#include
+#define _USE_MATH_DEFINES
+#include
+#include "mamedef.h"
+#ifdef _DEBUG
+#include
+#endif
+#include "k054539.h"
+
+#define NULL ((void *)0)
+
+#define CHANNEL_DEBUG 0
+#define VERBOSE 0
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+
+/* Registers:
+ 00..ff: 20 bytes/channel, 8 channels
+ 00..02: pitch (lsb, mid, msb)
+ 03: volume (0=max, 0x40=-36dB)
+ 04: reverb volume (idem)
+ 05: pan (1-f right, 10 middle, 11-1f left)
+ 06..07: reverb delay (0=max, current computation non-trusted)
+ 08..0a: loop (lsb, mid, msb)
+ 0c..0e: start (lsb, mid, msb) (and current position ?)
+
+ 100.1ff: effects?
+ 13f: pan of the analog input (1-1f)
+
+ 200..20f: 2 bytes/channel, 8 channels
+ 00: type (b2-3), reverse (b5)
+ 01: loop (b0)
+
+ 214: keyon (b0-7 = channel 0-7)
+ 215: keyoff ""
+ 22c: channel active? ""
+ 22d: data read/write port
+ 22e: rom/ram select (00..7f == rom banks, 80 = ram)
+ 22f: enable pcm (b0), disable register ram updating (b7)
+
+ The chip has a 0x4000 bytes reverb buffer (the ram from 0x22e).
+ The reverb delay is actually an offset in this buffer. This driver
+ uses some tricks (doubling the buffer size so that the longest
+ reverbs don't fold over the sound to output, and adding a space at
+ the end to fold back overflows in) to be able to do frame-based
+ rendering instead of sample-based.
+*/
+
+typedef struct _k054539_channel k054539_channel;
+struct _k054539_channel {
+ UINT32 pos;
+ UINT32 pfrac;
+ INT32 val;
+ INT32 pval;
+};
+
+typedef struct _k054539_state k054539_state;
+struct _k054539_state {
+ //const k054539_interface *intf;
+ //device_t *device;
+ double voltab[256];
+ double pantab[0xf];
+
+ double k054539_gain[8];
+ UINT8 k054539_posreg_latch[8][3];
+ int k054539_flags;
+
+ unsigned char regs[0x230];
+ unsigned char *ram;
+ int reverb_pos;
+
+ INT32 cur_ptr;
+ int cur_limit;
+ unsigned char *cur_zone;
+ unsigned char *rom;
+ UINT32 rom_size;
+ UINT32 rom_mask;
+ //sound_stream * stream;
+
+ k054539_channel channels[8];
+ UINT8 Muted[8];
+
+ int clock;
+};
+
+/*INLINE k054539_state *get_safe_token(device_t *device)
+{
+ assert(device != NULL);
+ assert(device->type() == K054539);
+ return (k054539_state *)downcast(device)->token();
+}*/
+
+//*
+
+//void k054539_init_flags(device_t *device, int flags)
+void k054539_init_flags(void *chip, int flags)
+{
+ //k054539_state *info = get_safe_token(device);
+ k054539_state *info = (k054539_state *) chip;
+ info->k054539_flags = flags;
+}
+
+//void k054539_set_gain(device_t *device, int channel, double gain)
+void k054539_set_gain(void *chip, int channel, double gain)
+{
+ //k054539_state *info = get_safe_token(device);
+ k054539_state *info = (k054539_state *) chip;
+ if (gain >= 0) info->k054539_gain[channel] = gain;
+}
+//*
+
+static int k054539_regupdate(k054539_state *info)
+{
+ return !(info->regs[0x22f] & 0x80);
+}
+
+static void k054539_keyon(k054539_state *info, int channel)
+{
+ if(k054539_regupdate(info))
+ info->regs[0x22c] |= 1 << channel;
+}
+
+static void k054539_keyoff(k054539_state *info, int channel)
+{
+ if(k054539_regupdate(info))
+ info->regs[0x22c] &= ~(1 << channel);
+}
+
+//static STREAM_UPDATE( k054539_update )
+void k054539_update(void *chip, stream_sample_t **outputs, int samples)
+{
+ //k054539_state *info = (k054539_state *)param;
+ k054539_state *info = (k054539_state *) chip;
+#define VOL_CAP 1.80
+
+ static const INT16 dpcm[16] = {
+ 0<<8, 1<<8, 4<<8, 9<<8, 16<<8, 25<<8, 36<<8, 49<<8,
+ -64<<8, -49<<8, -36<<8, -25<<8, -16<<8, -9<<8, -4<<8, -1<<8
+ };
+
+ int ch, reverb_pos;
+ short *rbase;
+ unsigned char *rom;
+ UINT32 rom_mask;
+
+ unsigned char *base1, *base2;
+ k054539_channel *chan;
+ stream_sample_t *bufl, *bufr;
+ UINT32 cur_pos, cur_pfrac;
+ int cur_val, cur_pval;
+ int delta, rdelta, fdelta, pdelta;
+ int vol, bval, pan, i;
+
+ double gain, lvol, rvol, rbvol;
+
+ reverb_pos = info->reverb_pos;
+ rbase = (short *)(info->ram);
+
+ memset(outputs[0], 0, samples*sizeof(*outputs[0]));
+ memset(outputs[1], 0, samples*sizeof(*outputs[1]));
+
+ rom = info->rom;
+ rom_mask = info->rom_mask;
+
+ if(!(info->regs[0x22f] & 1)) return;
+
+ info->reverb_pos = (reverb_pos + samples) & 0x3fff;
+
+
+ for(ch=0; ch<8; ch++)
+ if ((info->regs[0x22c] & (1<Muted[ch])
+ {
+ base1 = info->regs + 0x20*ch;
+ base2 = info->regs + 0x200 + 0x2*ch;
+ chan = info->channels + ch;
+//*
+ delta = base1[0x00] | (base1[0x01] << 8) | (base1[0x02] << 16);
+
+ vol = base1[0x03];
+
+ bval = vol + base1[0x04];
+ if (bval > 255) bval = 255;
+
+ pan = base1[0x05];
+// DJ Main: 81-87 right, 88 middle, 89-8f left
+if (pan >= 0x81 && pan <= 0x8f)
+pan -= 0x81;
+else
+ if (pan >= 0x11 && pan <= 0x1f) pan -= 0x11; else pan = 0x18 - 0x11;
+
+ gain = info->k054539_gain[ch];
+
+ lvol = info->voltab[vol] * info->pantab[pan] * gain;
+ if (lvol > VOL_CAP) lvol = VOL_CAP;
+
+ rvol = info->voltab[vol] * info->pantab[0xe - pan] * gain;
+ if (rvol > VOL_CAP) rvol = VOL_CAP;
+
+ rbvol= info->voltab[bval] * gain / 2;
+ if (rbvol > VOL_CAP) rbvol = VOL_CAP;
+
+/*
+ INT x FLOAT could be interpreted as INT x (int)FLOAT instead of (float)INT x FLOAT on some compilers
+ causing precision loss. (rdelta - 0x2000) wraps around on zero reverb and the scale factor should
+ actually be 1/freq_ratio because the target is an offset to the reverb buffer not sample source.
+*/
+ rdelta = (base1[6] | (base1[7] << 8)) >> 3;
+// rdelta = (reverb_pos + (int)((rdelta - 0x2000) * info->freq_ratio)) & 0x3fff;
+ rdelta = (int)(rdelta + reverb_pos) & 0x3fff;
+
+ cur_pos = (base1[0x0c] | (base1[0x0d] << 8) | (base1[0x0e] << 16)) & rom_mask;
+
+ bufl = outputs[0];
+ bufr = outputs[1];
+//*
+
+ if(base2[0] & 0x20) {
+ delta = -delta;
+ fdelta = +0x10000;
+ pdelta = -1;
+ } else {
+ fdelta = -0x10000;
+ pdelta = +1;
+ }
+
+ if(cur_pos != chan->pos) {
+ chan->pos = cur_pos;
+ cur_pfrac = 0;
+ cur_val = 0;
+ cur_pval = 0;
+ } else {
+ cur_pfrac = chan->pfrac;
+ cur_val = chan->val;
+ cur_pval = chan->pval;
+ }
+
+#define UPDATE_CHANNELS \
+ do { \
+ *bufl++ += (INT16)(cur_val*lvol); \
+ *bufr++ += (INT16)(cur_val*rvol); \
+ rbase[rdelta++] += (INT16)(cur_val*rbvol); \
+ rdelta &= 0x3fff; \
+ } while(0)
+
+ switch(base2[0] & 0xc) {
+ case 0x0: { // 8bit pcm
+ for(i=0; i>1];
+ if(cur_val == 0x88) {
+ if(base2[1] & 1) {
+ cur_pos = ((base1[0x08] | (base1[0x09] << 8) | (base1[0x0a] << 16)) & rom_mask) << 1;
+ cur_val = rom[cur_pos>>1];
+ if(cur_val != 0x88)
+ goto next_iter;
+ }
+ k054539_keyoff(info, ch);
+ goto end_channel_8;
+ }
+ next_iter:
+ if(cur_pos & 1)
+ cur_val >>= 4;
+ else
+ cur_val &= 15;
+ cur_val = cur_pval + dpcm[cur_val];
+ if(cur_val < -32768)
+ cur_val = -32768;
+ else if(cur_val > 32767)
+ cur_val = 32767;
+ }
+
+ UPDATE_CHANNELS;
+ }
+ end_channel_8:
+ cur_pfrac >>= 1;
+ if(cur_pos & 1)
+ cur_pfrac |= 0x8000;
+ cur_pos >>= 1;
+ break;
+ }
+ default:
+ LOG(("Unknown sample type %x for channel %d\n", base2[0] & 0xc, ch));
+ break;
+ }
+ chan->pos = cur_pos;
+ chan->pfrac = cur_pfrac;
+ chan->pval = cur_pval;
+ chan->val = cur_val;
+ if(k054539_regupdate(info)) {
+ base1[0x0c] = cur_pos & 0xff;
+ base1[0x0d] = cur_pos>> 8 & 0xff;
+ base1[0x0e] = cur_pos>>16 & 0xff;
+ }
+ }
+
+ //* drivers should be given the option to disable reverb when things go terribly wrong
+ if(!(info->k054539_flags & K054539_DISABLE_REVERB))
+ {
+ for(i=0; i 0x4000) {
+ i = 0x4000 - reverb_pos;
+ memset(rbase + reverb_pos, 0, i*2);
+ memset(rbase, 0, (samples-i)*2);
+ } else
+ memset(rbase + reverb_pos, 0, samples*2);
+
+ #if CHANNEL_DEBUG
+ {
+ static const char gc_msg[32] = "chip : ";
+ static int gc_active=0, gc_chip=0, gc_pos[2]={0,0};
+ double *gc_fptr;
+ char *gc_cptr;
+ double gc_f0;
+ int gc_i, gc_j, gc_k, gc_l;
+
+ if (device->machine().input().code_pressed_once(KEYCODE_DEL_PAD))
+ {
+ gc_active ^= 1;
+ if (!gc_active) popmessage(NULL);
+ }
+
+ if (gc_active)
+ {
+ if (device->machine().input().code_pressed_once(KEYCODE_0_PAD)) gc_chip ^= 1;
+
+ gc_i = gc_pos[gc_chip];
+ gc_j = 0;
+ if (device->machine().input().code_pressed_once(KEYCODE_4_PAD)) { gc_i--; gc_j = 1; }
+ if (device->machine().input().code_pressed_once(KEYCODE_6_PAD)) { gc_i++; gc_j = 1; }
+ if (gc_j) { gc_i &= 7; gc_pos[gc_chip] = gc_i; }
+
+ if (device->machine().input().code_pressed_once(KEYCODE_5_PAD))
+ info->k054539_gain[gc_i] = 1.0;
+ else
+ {
+ gc_fptr = &info->k054539_gain[gc_i];
+ gc_f0 = *gc_fptr;
+ gc_j = 0;
+ if (device->machine().input().code_pressed_once(KEYCODE_2_PAD)) { gc_f0 -= 0.1; gc_j = 1; }
+ if (device->machine().input().code_pressed_once(KEYCODE_8_PAD)) { gc_f0 += 0.1; gc_j = 1; }
+ if (gc_j) { if (gc_f0 < 0) gc_f0 = 0; *gc_fptr = gc_f0; }
+ }
+
+ gc_fptr = &info->k054539_gain[0] + 8;
+ gc_cptr = gc_msg + 7;
+ for (gc_j=-8; gc_j; gc_j++)
+ {
+ gc_k = (int)(gc_fptr[gc_j] * 10);
+ gc_l = gc_k / 10;
+ gc_k = gc_k % 10;
+ gc_cptr[0] = gc_l + '0';
+ gc_cptr[1] = gc_k + '0';
+ gc_cptr += 3;
+ }
+ gc_i = (gc_i + gc_i*2 + 6);
+ gc_msg[4] = gc_chip + '0';
+ gc_msg[gc_i ] = '[';
+ gc_msg[gc_i+3] = ']';
+ popmessage("%s", gc_msg);
+ gc_msg[gc_i+3] = gc_msg[gc_i] = ' ';
+ }
+ }
+ #endif
+}
+
+
+/*static TIMER_CALLBACK( k054539_irq )
+{
+ k054539_state *info = (k054539_state *)ptr;
+ if(info->regs[0x22f] & 0x20)
+ info->intf->irq(info->device);
+}*/
+
+//static void k054539_init_chip(device_t *device, k054539_state *info)
+static int k054539_init_chip(k054539_state *info, int clock)
+{
+ //int i;
+
+ info->clock = clock;
+ // most of these are done in device_reset
+// memset(info->regs, 0, sizeof(info->regs));
+// memset(info->k054539_posreg_latch, 0, sizeof(info->k054539_posreg_latch)); //*
+ info->k054539_flags |= K054539_UPDATE_AT_KEYON; //* make it default until proven otherwise
+
+ // Real size of 0x4000, the addon is to simplify the reverb buffer computations
+ //info->ram = auto_alloc_array(device->machine(), unsigned char, 0x4000*2+device->clock()/50*2);
+ info->ram = (unsigned char*)malloc(0x4000 * 2 + info->clock / 50 * 2);
+// info->reverb_pos = 0;
+// info->cur_ptr = 0;
+ //memset(info->ram, 0, 0x4000*2+device->clock()/50*2);
+// memset(info->ram, 0, 0x4000 * 2 + info->clock / 50 * 2);
+
+ /*const memory_region *region = (info->intf->rgnoverride != NULL) ? device->machine().region(info->intf->rgnoverride) : device->region();
+ info->rom = *region;
+ info->rom_size = region->bytes();
+ info->rom_mask = 0xffffffffU;
+ for(i=0; i<32; i++)
+ if((1U<= info->rom_size) {
+ info->rom_mask = (1U<rom = NULL;
+ info->rom_size = 0;
+ info->rom_mask = 0x00;
+
+ //if(info->intf->irq)
+ // One or more of the registers must be the timer period
+ // And anyway, this particular frequency is probably wrong
+ // 480 hz is TRUSTED by gokuparo disco stage - the looping sample doesn't line up otherwise
+ // device->machine().scheduler().timer_pulse(attotime::from_hz(480), FUNC(k054539_irq), 0, info);
+
+ //info->stream = device->machine().sound().stream_alloc(*device, 0, 2, device->clock(), info, k054539_update);
+
+ //device->save_item(NAME(info->regs));
+ //device->save_pointer(NAME(info->ram), 0x4000);
+ //device->save_item(NAME(info->cur_ptr));
+
+ return info->clock;
+}
+
+//WRITE8_DEVICE_HANDLER( k054539_w )
+void k054539_w(void *chip, offs_t offset, UINT8 data)
+{
+ //k054539_state *info = get_safe_token(device);
+ k054539_state *info = (k054539_state *) chip;
+
+#if 0
+ int voice, reg;
+
+ /* The K054539 has behavior like many other wavetable chips including
+ the Ensoniq 550x and Gravis GF-1: if a voice is active, writing
+ to it's current position is silently ignored.
+
+ Dadandaan depends on this or the vocals go wrong.
+ */
+ if (offset < 8*0x20)
+ {
+ voice = offset / 0x20;
+ reg = offset & ~0x20;
+
+ if(info->regs[0x22c] & (1<= 0xc && reg <= 0xe)
+ {
+ return;
+ }
+ }
+ }
+#endif
+
+ int latch, offs, ch, pan;
+ UINT8 *regbase, *regptr, *posptr;
+
+ regbase = info->regs;
+ latch = (info->k054539_flags & K054539_UPDATE_AT_KEYON) && (regbase[0x22f] & 1);
+
+ if (latch && offset < 0x100)
+ {
+ offs = (offset & 0x1f) - 0xc;
+ ch = offset >> 5;
+
+ if (offs >= 0 && offs <= 2)
+ {
+ // latch writes to the position index registers
+ info->k054539_posreg_latch[ch][offs] = data;
+ return;
+ }
+ }
+
+ else switch(offset)
+ {
+ case 0x13f:
+ pan = data >= 0x11 && data <= 0x1f ? data - 0x11 : 0x18 - 0x11;
+ //if(info->intf->apan)
+ // info->intf->apan(info->device, info->pantab[pan], info->pantab[0xe - pan]);
+ break;
+
+ case 0x214:
+ if (latch)
+ {
+ for(ch=0; ch<8; ch++)
+ {
+ if(data & (1<k054539_posreg_latch[ch][0];
+ regptr = regbase + (ch<<5) + 0xc;
+
+ // update the chip at key-on
+ regptr[0] = posptr[0];
+ regptr[1] = posptr[1];
+ regptr[2] = posptr[2];
+
+ k054539_keyon(info, ch);
+ }
+ }
+ }
+ else
+ {
+ for(ch=0; ch<8; ch++)
+ if(data & (1<cur_zone[info->cur_ptr] = data;
+ info->cur_ptr++;
+ if(info->cur_ptr == info->cur_limit)
+ info->cur_ptr = 0;
+ break;
+
+ case 0x22e:
+ info->cur_zone =
+ data == 0x80 ? info->ram :
+ info->rom + 0x20000*data;
+ info->cur_limit = data == 0x80 ? 0x4000 : 0x20000;
+ info->cur_ptr = 0;
+ break;
+
+ default:
+#if 0
+ if(regbase[offset] != data) {
+ if((offset & 0xff00) == 0) {
+ chanoff = offset & 0x1f;
+ if(chanoff < 4 || chanoff == 5 ||
+ (chanoff >=8 && chanoff <= 0xa) ||
+ (chanoff >= 0xc && chanoff <= 0xe))
+ break;
+ }
+ if(1 || ((offset >= 0x200) && (offset <= 0x210)))
+ break;
+ logerror("K054539 %03x = %02x\n", offset, data);
+ }
+#endif
+ break;
+ }
+
+ regbase[offset] = data;
+}
+
+static void reset_zones(k054539_state *info)
+{
+ int data = info->regs[0x22e];
+ info->cur_zone =
+ data == 0x80 ? info->ram :
+ info->rom + 0x20000*data;
+ info->cur_limit = data == 0x80 ? 0x4000 : 0x20000;
+}
+
+//READ8_DEVICE_HANDLER( k054539_r )
+UINT8 k054539_r(void *chip, offs_t offset)
+{
+ //k054539_state *info = get_safe_token(device);
+ k054539_state *info = (k054539_state *) chip;
+ switch(offset) {
+ case 0x22d:
+ if(info->regs[0x22f] & 0x10) {
+ UINT8 res = info->cur_zone[info->cur_ptr];
+ info->cur_ptr++;
+ if(info->cur_ptr == info->cur_limit)
+ info->cur_ptr = 0;
+ return res;
+ } else
+ return 0;
+ case 0x22c:
+ break;
+ default:
+ LOG(("K054539 read %03x\n", offset));
+ break;
+ }
+ return info->regs[offset];
+}
+
+//static DEVICE_START( k054539 )
+void * device_start_k054539(int clock)
+{
+ //static const k054539_interface defintrf = { 0 };
+ int i;
+ //k054539_state *info = get_safe_token(device);
+ k054539_state *info;
+
+ info = (k054539_state *) calloc(1, sizeof(k054539_state));
+ //info->device = device;
+
+ for (i = 0; i < 8; i++)
+ info->k054539_gain[i] = 1.0;
+ info->k054539_flags = K054539_RESET_FLAGS;
+
+ //info->intf = (device->static_config() != NULL) ? (const k054539_interface *)device->static_config() : &defintrf;
+
+ /*
+ I've tried various equations on volume control but none worked consistently.
+ The upper four channels in most MW/GX games simply need a significant boost
+ to sound right. For example, the bass and smash sound volumes in Violent Storm
+ have roughly the same values and the voices in Tokimeki Puzzledama are given
+ values smaller than those of the hihats. Needless to say the two K054539 chips
+ in Mystic Warriors are completely out of balance. Rather than forcing a
+ "one size fits all" function to the voltab the current invert exponential
+ appraoch seems most appropriate.
+ */
+ // Factor the 1/4 for the number of channels in the volume (1/8 is too harsh, 1/2 gives clipping)
+ // vol=0 -> no attenuation, vol=0x40 -> -36dB
+ for(i=0; i<256; i++)
+ info->voltab[i] = pow(10.0, (-36.0 * (double)i / (double)0x40) / 20.0) / 4.0;
+
+ // Pan table for the left channel
+ // Right channel is identical with inverted index
+ // Formula is such that pan[i]**2+pan[0xe-i]**2 = 1 (constant output power)
+ // and pan[0xe] = 1 (full panning)
+ for(i=0; i<0xf; i++)
+ info->pantab[i] = sqrt((double)i) / sqrt((double)0xe);
+
+ //k054539_init_chip(device, info);
+
+ //device->machine().save().register_postload(save_prepost_delegate(FUNC(reset_zones), info));
+
+ for (i = 0; i < 8; i ++)
+ info->Muted[i] = 0x00;
+
+ k054539_init_chip(info, clock);
+
+ return info;
+}
+
+void device_stop_k054539(void *chip)
+{
+ k054539_state *info = (k054539_state *) chip;
+
+ free(info->rom); info->rom = NULL;
+ free(info->ram);
+ free(info);
+}
+
+void device_reset_k054539(void *chip)
+{
+ k054539_state *info = (k054539_state *) chip;
+
+ memset(info->regs, 0, sizeof(info->regs));
+ memset(info->k054539_posreg_latch, 0, sizeof(info->k054539_posreg_latch));
+ //info->k054539_flags |= K054539_UPDATE_AT_KEYON;
+
+ info->reverb_pos = 0;
+ info->cur_ptr = 0;
+ memset(info->ram, 0, 0x4000 * 2 + info->clock / 50 * 2);
+}
+
+void k054539_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
+ const UINT8* ROMData)
+{
+ k054539_state *info = (k054539_state *) chip;
+
+ if (info->rom_size != ROMSize)
+ {
+ UINT8 i;
+
+ info->rom = (UINT8*)realloc(info->rom, ROMSize);
+ info->rom_size = ROMSize;
+ memset(info->rom, 0xFF, ROMSize);
+
+ info->rom_mask = 0xFFFFFFFF;
+ for (i = 0; i < 32; i ++)
+ {
+ if ((1U << i) >= info->rom_size)
+ {
+ info->rom_mask = (1 << i) - 1;
+ break;
+ }
+ }
+ }
+ if (DataStart > ROMSize)
+ return;
+ if (DataStart + DataLength > ROMSize)
+ DataLength = ROMSize - DataStart;
+
+ memcpy(info->rom + DataStart, ROMData, DataLength);
+}
+
+
+void k054539_set_mute_mask(void *chip, UINT32 MuteMask)
+{
+ k054539_state *info = (k054539_state *) chip;
+ UINT8 CurChn;
+
+ for (CurChn = 0; CurChn < 8; CurChn ++)
+ info->Muted[CurChn] = (MuteMask >> CurChn) & 0x01;
+
+ return;
+}
diff --git a/Frameworks/GME/gme/k054539.h b/Frameworks/GME/gme/k054539.h
new file mode 100644
index 000000000..5090403a4
--- /dev/null
+++ b/Frameworks/GME/gme/k054539.h
@@ -0,0 +1,70 @@
+/*********************************************************
+
+ Konami 054539 PCM Sound Chip
+
+*********************************************************/
+
+#pragma once
+
+#include "mamedef.h"
+
+//#include "devlegcy.h"
+
+/*typedef struct _k054539_interface k054539_interface;
+struct _k054539_interface
+{
+ const char *rgnoverride;
+ void (*apan)(device_t *, double, double); // Callback for analog output mixing levels (0..1 for each channel)
+ void (*irq)(device_t *);
+};*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void k054539_update(void *, stream_sample_t **outputs, int samples);
+void * device_start_k054539(int clock);
+void device_stop_k054539(void *);
+void device_reset_k054539(void *);
+
+
+//WRITE8_DEVICE_HANDLER( k054539_w );
+//READ8_DEVICE_HANDLER( k054539_r );
+void k054539_w(void *, offs_t offset, UINT8 data);
+UINT8 k054539_r(void *, offs_t offset);
+
+//* control flags, may be set at DRIVER_INIT().
+#define K054539_RESET_FLAGS 0
+#define K054539_REVERSE_STEREO 1
+#define K054539_DISABLE_REVERB 2
+#define K054539_UPDATE_AT_KEYON 4
+
+//void k054539_init_flags(device_t *device, int flags);
+void k054539_init_flags(void *, int flags);
+
+/*
+ Note that the eight PCM channels of a K054539 do not have separate
+ volume controls. Considering the global attenuation equation may not
+ be entirely accurate, k054539_set_gain() provides means to control
+ channel gain. It can be called anywhere but preferrably from
+ DRIVER_INIT().
+
+ Parameters:
+ chip : 0 / 1
+ channel : 0 - 7
+ gain : 0.0=silent, 1.0=no gain, 2.0=twice as loud, etc.
+*/
+//void k054539_set_gain(device_t *device, int channel, double gain);
+void k054539_set_gain(void *, int channel, double gain);
+
+
+void k054539_write_rom(void *, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
+ const UINT8* ROMData);
+void k054539_set_mute_mask(void *, UINT32 MuteMask);
+
+
+//DECLARE_LEGACY_SOUND_DEVICE(K054539, k054539);
+
+#ifdef __cplusplus
+};
+#endif
diff --git a/Frameworks/GME/gme/kmsnddev.h b/Frameworks/GME/gme/kmsnddev.h
new file mode 100644
index 000000000..ec7552b1b
--- /dev/null
+++ b/Frameworks/GME/gme/kmsnddev.h
@@ -0,0 +1,31 @@
+/* libnezp by Mamiya */
+
+#ifndef KMSNDDEV_H__
+#define KMSNDDEV_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "nestypes.h"
+
+typedef struct KMIF_SOUND_DEVICE {
+ void *ctx;
+ void (*release)(void *ctx);
+ void (*reset)(void *ctx, Uint32 clock, Uint32 freq);
+ int (*synth)(void *ctx);
+ void (*volume)(void *ctx, Int32 v);
+ void (*write)(void *ctx, Uint32 a, Uint32 v);
+ Uint32 (*read)(void *ctx, Uint32 a);
+ void (*setinst)(void *ctx, Uint32 n, void *p, Uint32 l);
+#if 0
+ void (*setrate)(void *ctx, Uint32 clock, Uint32 freq);
+ void (*getinfo)(void *ctx, KMCH_INFO *cip, );
+ void (*volume2)(void *ctx, Uint8 *volp, Uint32 numch);
+ /* 0x00(mute),0x70(x1/2),0x80(x1),0x90(x2) */
+#endif
+} KMIF_SOUND_DEVICE;
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* KMSNDDEV_H__ */
diff --git a/Frameworks/GME/gme/mamedef.h b/Frameworks/GME/gme/mamedef.h
new file mode 100644
index 000000000..3c5ad267d
--- /dev/null
+++ b/Frameworks/GME/gme/mamedef.h
@@ -0,0 +1,55 @@
+#ifndef GME_MAMEDEFS_H
+#define GME_MAMEDEFS_H
+
+
+// typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h)
+/* 8-bit values */
+typedef unsigned char UINT8;
+typedef signed char INT8;
+
+/* 16-bit values */
+typedef unsigned short UINT16;
+typedef signed short INT16;
+
+/* 32-bit values */
+#ifndef _WINDOWS_H
+typedef unsigned int UINT32;
+typedef signed int INT32;
+#endif
+
+/* 64-bit values */
+#ifndef _WINDOWS_H
+#ifdef _MSC_VER
+typedef signed __int64 INT64;
+typedef unsigned __int64 UINT64;
+#else
+__extension__ typedef unsigned long long UINT64;
+__extension__ typedef signed long long INT64;
+#endif
+#endif
+
+/* offsets and addresses are 32-bit (for now...) */
+typedef UINT32 offs_t;
+
+/* stream_sample_t is used to represent a single sample in a sound stream */
+typedef INT32 stream_sample_t;
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+#if defined(_MSC_VER)
+//#define INLINE static __forceinline
+#define INLINE static __inline
+#elif defined(__GNUC__)
+#define INLINE static __inline__
+#else
+#define INLINE static inline
+#endif
+#define _USE_MATH_DEFINES
+#include
+
+#define logerror
+
+
+#endif
diff --git a/Frameworks/GME/gme/mathdefs.h b/Frameworks/GME/gme/mathdefs.h
new file mode 100644
index 000000000..b12dfd0c7
--- /dev/null
+++ b/Frameworks/GME/gme/mathdefs.h
@@ -0,0 +1,15 @@
+#ifndef MATHDEFS_H
+#define MATHDEFS_H
+
+
+#ifndef _USE_MATH_DEFINES
+#define _USE_MATH_DEFINES
+#endif
+#include
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+
+#endif
diff --git a/Frameworks/GME/gme/nestypes.h b/Frameworks/GME/gme/nestypes.h
new file mode 100644
index 000000000..1fd3582b0
--- /dev/null
+++ b/Frameworks/GME/gme/nestypes.h
@@ -0,0 +1,39 @@
+#ifndef NESTYPES_H__
+#define NESTYPES_H__
+
+#if defined(_MSC_VER)
+#define NEVER_REACH __assume(0);
+#define inline __inline
+#elif defined(__BORLANDC__)
+#define __fastcall __msfastcall
+#elif defined(__GNUC__)
+#define __inline __inline__
+#define __fastcall
+#else
+#define __inline
+#define __fastcall
+#endif
+#ifndef NEVER_REACH
+#define NEVER_REACH
+#endif
+
+typedef int Int;
+typedef unsigned int Uint;
+typedef signed int Int32;
+typedef unsigned int Uint32;
+typedef signed short Int16;
+typedef unsigned short Uint16;
+typedef signed char Int8;
+typedef unsigned char Uint8;
+typedef char Char;
+
+#include
+
+#define XSLEEP(n) ((void)0)
+#define XMALLOC(s) malloc(s)
+#define XREALLOC(p,s) realloc(p,s)
+#define XFREE(p) free(p)
+#define XMEMCPY(d,s,n) memcpy(d,s,n)
+#define XMEMSET(d,c,n) memset(d,c,n)
+
+#endif /* NESTYPES_H__ */
diff --git a/Frameworks/GME/gme/okim6258.c b/Frameworks/GME/gme/okim6258.c
new file mode 100644
index 000000000..e333f247b
--- /dev/null
+++ b/Frameworks/GME/gme/okim6258.c
@@ -0,0 +1,446 @@
+/**********************************************************************************************
+ *
+ * OKI MSM6258 ADPCM
+ *
+ * TODO:
+ * 3-bit ADPCM support
+ * Recording?
+ *
+ **********************************************************************************************/
+
+
+//#include "emu.h"
+#include "mamedef.h"
+#ifdef _DEBUG
+#include
+#endif
+//#include "streams.h"
+#include
+#include
+#include "okim6258.h"
+
+#define COMMAND_STOP (1 << 0)
+#define COMMAND_PLAY (1 << 1)
+#define COMMAND_RECORD (1 << 2)
+
+#define STATUS_PLAYING (1 << 1)
+#define STATUS_RECORDING (1 << 2)
+
+static const int dividers[4] = { 1024, 768, 512, 512 };
+
+typedef struct _okim6258_state okim6258_state;
+struct _okim6258_state
+{
+ UINT8 status;
+
+ UINT32 master_clock; /* master clock frequency */
+ UINT32 divider; /* master clock divider */
+ UINT8 adpcm_type; /* 3/4 bit ADPCM select */
+ UINT8 data_in; /* ADPCM data-in register */
+ UINT8 nibble_shift; /* nibble select */
+ //sound_stream *stream; /* which stream are we playing on? */
+
+ UINT8 output_bits;
+
+ INT32 signal;
+ INT32 step;
+
+ UINT8 clock_buffer[0x04];
+};
+
+/* step size index shift table */
+static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
+
+/* lookup table for the precomputed difference */
+static int diff_lookup[49*16];
+
+/* tables computed? */
+static int tables_computed = 0;
+
+/*INLINE okim6258_state *get_safe_token(running_device *device)
+{
+ assert(device != NULL);
+ assert(device->type() == OKIM6258);
+ return (okim6258_state *)downcast(device)->token();
+}*/
+
+/**********************************************************************************************
+
+ compute_tables -- compute the difference tables
+
+***********************************************************************************************/
+
+static void compute_tables(void)
+{
+ /* nibble to bit map */
+ static const int nbl2bit[16][4] =
+ {
+ { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
+ { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
+ {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
+ {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
+ };
+
+ int step, nib;
+
+ if (tables_computed)
+ return;
+
+ /* loop over all possible steps */
+ for (step = 0; step <= 48; step++)
+ {
+ /* compute the step value */
+ int stepval = floor(16.0 * pow(11.0 / 10.0, (double)step));
+
+ /* loop over all nibbles and compute the difference */
+ for (nib = 0; nib < 16; nib++)
+ {
+ diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
+ (stepval * nbl2bit[nib][1] +
+ stepval/2 * nbl2bit[nib][2] +
+ stepval/4 * nbl2bit[nib][3] +
+ stepval/8);
+ }
+ }
+
+ tables_computed = 1;
+}
+
+
+static INT16 clock_adpcm(okim6258_state *chip, UINT8 nibble)
+{
+ INT32 max = (1 << (chip->output_bits - 1)) - 1;
+ INT32 min = -(1 << (chip->output_bits - 1));
+
+ chip->signal += diff_lookup[chip->step * 16 + (nibble & 15)];
+
+ /* clamp to the maximum */
+ if (chip->signal > max)
+ chip->signal = max;
+ else if (chip->signal < min)
+ chip->signal = min;
+
+ /* adjust the step size and clamp */
+ chip->step += index_shift[nibble & 7];
+ if (chip->step > 48)
+ chip->step = 48;
+ else if (chip->step < 0)
+ chip->step = 0;
+
+ /* return the signal scaled up to 32767 */
+ return chip->signal << 4;
+}
+
+/**********************************************************************************************
+
+ okim6258_update -- update the sound chip so that it is in sync with CPU execution
+
+***********************************************************************************************/
+
+//static STREAM_UPDATE( okim6258_update )
+void okim6258_update(void *_chip, stream_sample_t **outputs, int samples)
+{
+ okim6258_state *chip = (okim6258_state *)_chip;
+ //stream_sample_t *buffer = outputs[0];
+ stream_sample_t *bufL = outputs[0];
+ stream_sample_t *bufR = outputs[1];
+
+ //memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+
+ if (chip->status & STATUS_PLAYING)
+ {
+ int nibble_shift = chip->nibble_shift;
+
+ while (samples)
+ {
+ /* Compute the new amplitude and update the current step */
+ int nibble = (chip->data_in >> nibble_shift) & 0xf;
+
+ /* Output to the buffer */
+ INT16 sample = clock_adpcm(chip, nibble);
+
+ nibble_shift ^= 4;
+
+ //*buffer++ = sample;
+ *bufL++ = sample;
+ *bufR++ = sample;
+ samples--;
+ }
+
+ /* Update the parameters */
+ chip->nibble_shift = nibble_shift;
+ }
+ else
+ {
+ /* Fill with 0 */
+ while (samples--)
+ {
+ //*buffer++ = 0;
+ *bufL++ = 0;
+ *bufR++ = 0;
+ }
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ state save support for MAME
+
+***********************************************************************************************/
+
+/*static void okim6258_state_save_register(okim6258_state *info, running_device *device)
+{
+ state_save_register_device_item(device, 0, info->status);
+ state_save_register_device_item(device, 0, info->master_clock);
+ state_save_register_device_item(device, 0, info->divider);
+ state_save_register_device_item(device, 0, info->data_in);
+ state_save_register_device_item(device, 0, info->nibble_shift);
+ state_save_register_device_item(device, 0, info->signal);
+ state_save_register_device_item(device, 0, info->step);
+}*/
+
+
+/**********************************************************************************************
+
+ OKIM6258_start -- start emulation of an OKIM6258-compatible chip
+
+***********************************************************************************************/
+
+//static DEVICE_START( okim6258 )
+void * device_start_okim6258(int clock, int divider, int adpcm_type, int output_12bits)
+{
+ //const okim6258_interface *intf = (const okim6258_interface *)device->baseconfig().static_config();
+ //okim6258_state *info = get_safe_token(device);
+ okim6258_state *info;
+
+ info = (okim6258_state *) calloc(1, sizeof(okim6258_state));
+
+ compute_tables();
+
+ //info->master_clock = device->clock();
+ info->master_clock = clock;
+ info->adpcm_type = /*intf->*/adpcm_type;
+ info->clock_buffer[0x00] = (clock & 0x000000FF) >> 0;
+ info->clock_buffer[0x01] = (clock & 0x0000FF00) >> 8;
+ info->clock_buffer[0x02] = (clock & 0x00FF0000) >> 16;
+ info->clock_buffer[0x03] = (clock & 0xFF000000) >> 24;
+
+ /* D/A precision is 10-bits but 12-bit data can be output serially to an external DAC */
+ info->output_bits = /*intf->*/output_12bits ? 12 : 10;
+ info->divider = dividers[/*intf->*/divider];
+
+ //info->stream = stream_create(device, 0, 1, device->clock()/info->divider, info, okim6258_update);
+
+ info->signal = -2;
+ info->step = 0;
+
+ //okim6258_state_save_register(info, device);
+
+ return info; //return info->master_clock / info->divider;
+}
+
+
+/**********************************************************************************************
+
+ OKIM6258_stop -- stop emulation of an OKIM6258-compatible chip
+
+***********************************************************************************************/
+
+void device_stop_okim6258(void *chip)
+{
+ okim6258_state *info = (okim6258_state *) chip;
+
+ free(info);
+}
+
+//static DEVICE_RESET( okim6258 )
+void device_reset_okim6258(void *chip)
+{
+ //okim6258_state *info = get_safe_token(device);
+ okim6258_state *info = (okim6258_state *) chip;
+
+ //stream_update(info->stream);
+
+ info->signal = -2;
+ info->step = 0;
+ info->status = 0;
+}
+
+
+/**********************************************************************************************
+
+ okim6258_set_divider -- set the master clock divider
+
+***********************************************************************************************/
+
+//void okim6258_set_divider(running_device *device, int val)
+void okim6258_set_divider(void *chip, int val)
+{
+ okim6258_state *info = (okim6258_state *) chip;
+ int divider = dividers[val];
+
+ info->divider = dividers[val];
+ //stream_set_sample_rate(info->stream, info->master_clock / divider);
+}
+
+
+/**********************************************************************************************
+
+ okim6258_set_clock -- set the master clock
+
+***********************************************************************************************/
+
+//void okim6258_set_clock(running_device *device, int val)
+void okim6258_set_clock(void *chip, int val)
+{
+ okim6258_state *info = (okim6258_state *) chip;
+
+ if (val)
+ {
+ info->master_clock = val;
+ }
+ else
+ {
+ info->master_clock = (info->clock_buffer[0x00] << 0) |
+ (info->clock_buffer[0x01] << 8) |
+ (info->clock_buffer[0x02] << 16) |
+ (info->clock_buffer[0x03] << 24);
+ }
+}
+
+
+/**********************************************************************************************
+
+ okim6258_get_vclk -- get the VCLK/sampling frequency
+
+***********************************************************************************************/
+
+//int okim6258_get_vclk(running_device *device)
+int okim6258_get_vclk(void *chip)
+{
+ okim6258_state *info = (okim6258_state *) chip;
+
+ return (info->master_clock / info->divider);
+}
+
+
+/**********************************************************************************************
+
+ okim6258_status_r -- read the status port of an OKIM6258-compatible chip
+
+***********************************************************************************************/
+
+//READ8_DEVICE_HANDLER( okim6258_status_r )
+UINT8 okim6258_status_r(void *chip, offs_t offset)
+{
+ //okim6258_state *info = get_safe_token(device);
+ okim6258_state *info = (okim6258_state *) chip;
+
+ //stream_update(info->stream);
+
+ return (info->status & STATUS_PLAYING) ? 0x00 : 0x80;
+}
+
+
+/**********************************************************************************************
+
+ okim6258_data_w -- write to the control port of an OKIM6258-compatible chip
+
+***********************************************************************************************/
+//WRITE8_DEVICE_HANDLER( okim6258_data_w )
+void okim6258_data_w(void *chip, offs_t offset, UINT8 data)
+{
+ //okim6258_state *info = get_safe_token(device);
+ okim6258_state *info = (okim6258_state *) chip;
+
+ /* update the stream */
+ //stream_update(info->stream);
+
+ info->data_in = data;
+ info->nibble_shift = 0;
+}
+
+
+/**********************************************************************************************
+
+ okim6258_ctrl_w -- write to the control port of an OKIM6258-compatible chip
+
+***********************************************************************************************/
+
+//WRITE8_DEVICE_HANDLER( okim6258_ctrl_w )
+void okim6258_ctrl_w(void *chip, offs_t offset, UINT8 data)
+{
+ //okim6258_state *info = get_safe_token(device);
+ okim6258_state *info = (okim6258_state *) chip;
+
+ //stream_update(info->stream);
+
+ if (data & COMMAND_STOP)
+ {
+ info->status &= ~(STATUS_PLAYING | STATUS_RECORDING);
+ return;
+ }
+
+ if (data & COMMAND_PLAY)
+ {
+ if (!(info->status & STATUS_PLAYING))
+ {
+ info->status |= STATUS_PLAYING;
+
+ /* Also reset the ADPCM parameters */
+ info->signal = -2;
+ info->step = 0;
+ info->nibble_shift = 0;
+ }
+ }
+ else
+ {
+ info->status &= ~STATUS_PLAYING;
+ }
+
+ if (data & COMMAND_RECORD)
+ {
+ logerror("M6258: Record enabled\n");
+ info->status |= STATUS_RECORDING;
+ }
+ else
+ {
+ info->status &= ~STATUS_RECORDING;
+ }
+}
+
+void okim6258_set_clock_byte(void *chip, UINT8 Byte, UINT8 val)
+{
+ okim6258_state *info = (okim6258_state *) chip;
+
+ info->clock_buffer[Byte] = val;
+
+ return;
+}
+
+void okim6258_write(void *chip, UINT8 Port, UINT8 Data)
+{
+ switch(Port)
+ {
+ case 0x00:
+ okim6258_ctrl_w(chip, 0x00, Data);
+ break;
+ case 0x01:
+ okim6258_data_w(chip, 0x00, Data);
+ break;
+ case 0x08:
+ case 0x09:
+ case 0x0A:
+ okim6258_set_clock_byte(chip, Port & 0x03, Data);
+ break;
+ case 0x0B:
+ okim6258_set_clock_byte(chip, Port & 0x03, Data);
+ okim6258_set_clock(chip, 0);
+ break;
+ case 0x0C:
+ okim6258_set_divider(chip, Data);
+ break;
+ }
+}
diff --git a/Frameworks/GME/gme/okim6258.h b/Frameworks/GME/gme/okim6258.h
new file mode 100644
index 000000000..1383a2b70
--- /dev/null
+++ b/Frameworks/GME/gme/okim6258.h
@@ -0,0 +1,57 @@
+#pragma once
+
+#include "mamedef.h"
+
+//#include "devlegcy.h"
+
+/* an interface for the OKIM6258 and similar chips */
+
+/*typedef struct _okim6258_interface okim6258_interface;
+struct _okim6258_interface
+{
+ int divider;
+ int adpcm_type;
+ int output_12bits;
+};*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FOSC_DIV_BY_1024 0
+#define FOSC_DIV_BY_768 1
+#define FOSC_DIV_BY_512 2
+
+#define TYPE_3BITS 0
+#define TYPE_4BITS 1
+
+#define OUTPUT_10BITS 0
+#define OUTPUT_12BITS 1
+
+void okim6258_update(void *, stream_sample_t **outputs, int samples);
+void * device_start_okim6258(int clock, int divider, int adpcm_type, int output_12bits);
+void device_stop_okim6258(void *);
+void device_reset_okim6258(void *);
+
+//void okim6258_set_divider(running_device *device, int val);
+//void okim6258_set_clock(running_device *device, int val);
+//int okim6258_get_vclk(running_device *device);
+
+void okim6258_set_divider(void *, int val);
+void okim6258_set_clock(void *, int val);
+int okim6258_get_vclk(void *);
+
+//READ8_DEVICE_HANDLER( okim6258_status_r );
+//WRITE8_DEVICE_HANDLER( okim6258_data_w );
+//WRITE8_DEVICE_HANDLER( okim6258_ctrl_w );
+
+UINT8 okim6258_status_r(void *, offs_t offset);
+void okim6258_data_w(void *, offs_t offset, UINT8 data);
+void okim6258_ctrl_w(void *, offs_t offset, UINT8 data);
+void okim6258_write(void *, UINT8 Port, UINT8 Data);
+
+//DECLARE_LEGACY_SOUND_DEVICE(OKIM6258, okim6258);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Frameworks/GME/gme/okim6295.c b/Frameworks/GME/gme/okim6295.c
new file mode 100644
index 000000000..1c2c2de21
--- /dev/null
+++ b/Frameworks/GME/gme/okim6295.c
@@ -0,0 +1,709 @@
+/**********************************************************************************************
+ *
+ * streaming ADPCM driver
+ * by Aaron Giles
+ *
+ * Library to transcode from an ADPCM source to raw PCM.
+ * Written by Buffoni Mirko in 08/06/97
+ * References: various sources and documents.
+ *
+ * HJB 08/31/98
+ * modified to use an automatically selected oversampling factor
+ * for the current sample rate
+ *
+ * Mish 21/7/99
+ * Updated to allow multiple OKI chips with different sample rates
+ *
+ * R. Belmont 31/10/2003
+ * Updated to allow a driver to use both MSM6295s and "raw" ADPCM voices (gcpinbal)
+ * Also added some error trapping for MAME_DEBUG builds
+ *
+ **********************************************************************************************/
+
+
+#include "mamedef.h"
+//#include "emu.h"
+//#include "streams.h"
+#include
+#include
+#include
+#define _USE_MATH_DEFINES
+#include
+#include "okim6295.h"
+
+#define FALSE 0
+#define TRUE 1
+
+//#define MAX_SAMPLE_CHUNK 10000
+#define MAX_SAMPLE_CHUNK 0x10 // that's enough for VGMPlay's update rate
+
+
+/* struct describing a single playing ADPCM voice */
+struct ADPCMVoice
+{
+ UINT8 playing; /* 1 if we are actively playing */
+
+ UINT32 base_offset; /* pointer to the base memory location */
+ UINT32 sample; /* current sample number */
+ UINT32 count; /* total samples to play */
+
+ struct adpcm_state adpcm;/* current ADPCM state */
+ UINT32 volume; /* output volume */
+ UINT8 Muted;
+};
+
+typedef struct _okim6295_state okim6295_state;
+struct _okim6295_state
+{
+ #define OKIM6295_VOICES 4
+ struct ADPCMVoice voice[OKIM6295_VOICES];
+ //running_device *device;
+ INT32 command;
+ UINT8 bank_installed;
+ INT32 bank_offs;
+ UINT8 pin7_state;
+ //sound_stream *stream; /* which stream are we playing on? */
+ UINT32 master_clock; /* master clock frequency */
+
+ UINT32 ROMSize;
+ UINT8* ROM;
+};
+
+/* step size index shift table */
+static const int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
+
+/* lookup table for the precomputed difference */
+static int diff_lookup[49*16];
+
+/* volume lookup table. The manual lists only 9 steps, ~3dB per step. Given the dB values,
+ that seems to map to a 5-bit volume control. Any volume parameter beyond the 9th index
+ results in silent playback. */
+static const int volume_table[16] =
+{
+ 0x20, // 0 dB
+ 0x16, // -3.2 dB
+ 0x10, // -6.0 dB
+ 0x0b, // -9.2 dB
+ 0x08, // -12.0 dB
+ 0x06, // -14.5 dB
+ 0x04, // -18.0 dB
+ 0x03, // -20.5 dB
+ 0x02, // -24.0 dB
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+};
+
+/* tables computed? */
+static int tables_computed = 0;
+
+/* useful interfaces */
+//const okim6295_interface okim6295_interface_pin7high = { 1 };
+//const okim6295_interface okim6295_interface_pin7low = { 0 };
+
+/* default address map */
+/*static ADDRESS_MAP_START( okim6295, 0, 8 )
+ AM_RANGE(0x00000, 0x3ffff) AM_ROM
+ADDRESS_MAP_END*/
+
+
+/*INLINE okim6295_state *get_safe_token(running_device *device)
+{
+ assert(device != NULL);
+ assert(device->token != NULL);
+ assert(device->type == SOUND);
+ assert(sound_get_type(device) == SOUND_OKIM6295);
+ return (okim6295_state *)device->token;
+}*/
+
+
+/**********************************************************************************************
+
+ compute_tables -- compute the difference tables
+
+***********************************************************************************************/
+
+static void compute_tables(void)
+{
+ /* nibble to bit map */
+ static const int nbl2bit[16][4] =
+ {
+ { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1},
+ { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1},
+ {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1},
+ {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1}
+ };
+
+ int step, nib;
+
+ /* loop over all possible steps */
+ for (step = 0; step <= 48; step++)
+ {
+ /* compute the step value */
+ int stepval = (int)floor(16.0 * pow(11.0 / 10.0, (double)step));
+
+ /* loop over all nibbles and compute the difference */
+ for (nib = 0; nib < 16; nib++)
+ {
+ diff_lookup[step*16 + nib] = nbl2bit[nib][0] *
+ (stepval * nbl2bit[nib][1] +
+ stepval/2 * nbl2bit[nib][2] +
+ stepval/4 * nbl2bit[nib][3] +
+ stepval/8);
+ }
+ }
+
+ tables_computed = 1;
+}
+
+
+
+/**********************************************************************************************
+
+ reset_adpcm -- reset the ADPCM stream
+
+***********************************************************************************************/
+
+void reset_adpcm(struct adpcm_state *state)
+{
+ /* make sure we have our tables */
+ if (!tables_computed)
+ compute_tables();
+
+ /* reset the signal/step */
+ state->signal = -2;
+ state->step = 0;
+}
+
+
+
+/**********************************************************************************************
+
+ clock_adpcm -- clock the next ADPCM byte
+
+***********************************************************************************************/
+
+INT16 clock_adpcm(struct adpcm_state *state, UINT8 nibble)
+{
+ state->signal += diff_lookup[state->step * 16 + (nibble & 15)];
+
+ /* clamp to the maximum */
+ if (state->signal > 2047)
+ state->signal = 2047;
+ else if (state->signal < -2048)
+ state->signal = -2048;
+
+ /* adjust the step size and clamp */
+ state->step += index_shift[nibble & 7];
+ if (state->step > 48)
+ state->step = 48;
+ else if (state->step < 0)
+ state->step = 0;
+
+ /* return the signal */
+ return state->signal;
+}
+
+
+
+/**********************************************************************************************
+
+ generate_adpcm -- general ADPCM decoding routine
+
+***********************************************************************************************/
+
+static UINT8 memory_raw_read_byte(okim6295_state *info, offs_t offset)
+{
+ offs_t CurOfs;
+
+ CurOfs = info->bank_offs | offset;
+ if (CurOfs < info->ROMSize)
+ return info->ROM[CurOfs];
+ else
+ return 0x00;
+}
+
+static void generate_adpcm(okim6295_state *chip, struct ADPCMVoice *voice, INT16 *buffer, int samples)
+{
+ /* if this voice is active */
+ if (voice->playing)
+ {
+ offs_t base = voice->base_offset;
+ int sample = voice->sample;
+ int count = voice->count;
+
+ /* loop while we still have samples to generate */
+ while (samples)
+ {
+ /* compute the new amplitude and update the current step */
+ //int nibble = memory_raw_read_byte(chip->device->space(), base + sample / 2) >> (((sample & 1) << 2) ^ 4);
+ UINT8 nibble = memory_raw_read_byte(chip, base + sample / 2) >> (((sample & 1) << 2) ^ 4);
+
+ /* output to the buffer, scaling by the volume */
+ /* signal in range -2048..2047, volume in range 2..32 => signal * volume / 2 in range -32768..32767 */
+ *buffer++ = clock_adpcm(&voice->adpcm, nibble) * voice->volume / 2;
+ samples--;
+
+ /* next! */
+ if (++sample >= count)
+ {
+ voice->playing = 0;
+ break;
+ }
+ }
+
+ /* update the parameters */
+ voice->sample = sample;
+ }
+
+ /* fill the rest with silence */
+ while (samples--)
+ *buffer++ = 0;
+}
+
+
+
+/**********************************************************************************************
+ *
+ * OKIM 6295 ADPCM chip:
+ *
+ * Command bytes are sent:
+ *
+ * 1xxx xxxx = start of 2-byte command sequence, xxxxxxx is the sample number to trigger
+ * abcd vvvv = second half of command; one of the abcd bits is set to indicate which voice
+ * the v bits seem to be volumed
+ *
+ * 0abc d000 = stop playing; one or more of the abcd bits is set to indicate which voice(s)
+ *
+ * Status is read:
+ *
+ * ???? abcd = one bit per voice, set to 0 if nothing is playing, or 1 if it is active
+ *
+***********************************************************************************************/
+
+
+/**********************************************************************************************
+
+ okim6295_update -- update the sound chip so that it is in sync with CPU execution
+
+***********************************************************************************************/
+
+//static STREAM_UPDATE( okim6295_update )
+void okim6295_update(void *_chip, stream_sample_t **outputs, int samples)
+{
+ okim6295_state *chip = (okim6295_state *)_chip;
+ int i;
+
+ memset(outputs[0], 0, samples * sizeof(*outputs[0]));
+
+ for (i = 0; i < OKIM6295_VOICES; i++)
+ {
+ struct ADPCMVoice *voice = &chip->voice[i];
+ if (! voice->Muted)
+ {
+ stream_sample_t *buffer = outputs[0];
+ INT16 sample_data[MAX_SAMPLE_CHUNK];
+ int remaining = samples;
+
+ /* loop while we have samples remaining */
+ while (remaining)
+ {
+ int samples = (remaining > MAX_SAMPLE_CHUNK) ? MAX_SAMPLE_CHUNK : remaining;
+ int samp;
+
+ generate_adpcm(chip, voice, sample_data, samples);
+ for (samp = 0; samp < samples; samp++)
+ *buffer++ += sample_data[samp];
+
+ remaining -= samples;
+ }
+ }
+ }
+
+ memcpy(outputs[1], outputs[0], samples * sizeof(*outputs[0]));
+}
+
+
+
+/**********************************************************************************************
+
+ state save support for MAME
+
+***********************************************************************************************/
+
+/*static void adpcm_state_save_register(struct ADPCMVoice *voice, running_device *device, int index)
+{
+ state_save_register_device_item(device, index, voice->playing);
+ state_save_register_device_item(device, index, voice->sample);
+ state_save_register_device_item(device, index, voice->count);
+ state_save_register_device_item(device, index, voice->adpcm.signal);
+ state_save_register_device_item(device, index, voice->adpcm.step);
+ state_save_register_device_item(device, index, voice->volume);
+ state_save_register_device_item(device, index, voice->base_offset);
+}
+
+static STATE_POSTLOAD( okim6295_postload )
+{
+ running_device *device = (running_device *)param;
+ okim6295_state *info = get_safe_token(device);
+ okim6295_set_bank_base(device, info->bank_offs);
+}
+
+static void okim6295_state_save_register(okim6295_state *info, running_device *device)
+{
+ int j;
+
+ state_save_register_device_item(device, 0, info->command);
+ state_save_register_device_item(device, 0, info->bank_offs);
+ for (j = 0; j < OKIM6295_VOICES; j++)
+ adpcm_state_save_register(&info->voice[j], device, j);
+
+ state_save_register_postload(device->machine, okim6295_postload, (void *)device);
+}*/
+
+
+
+/**********************************************************************************************
+
+ DEVICE_START( okim6295 ) -- start emulation of an OKIM6295-compatible chip
+
+***********************************************************************************************/
+
+//static DEVICE_START( okim6295 )
+void * device_start_okim6295(int clock)
+{
+ //const okim6295_interface *intf = (const okim6295_interface *)device->baseconfig().static_config;
+ //okim6295_state *info = get_safe_token(device);
+ okim6295_state *info;
+ //int divisor = intf->pin7 ? 132 : 165;
+ int divisor;
+ //int voice;
+
+ info = (okim6295_state *) calloc(1, sizeof(okim6295_state));
+
+ compute_tables();
+
+ info->command = -1;
+ info->bank_installed = FALSE;
+ info->bank_offs = 0;
+ //info->device = device;
+
+ //info->master_clock = device->clock;
+ info->master_clock = clock & 0x7FFFFFFF;
+ info->pin7_state = (clock & 0x80000000) >> 31;
+
+ /* generate the name and create the stream */
+ divisor = info->pin7_state ? 132 : 165;
+ //info->stream = stream_create(device, 0, 1, device->clock/divisor, info, okim6295_update);
+
+ // moved to device_reset
+ /*// initialize the voices //
+ for (voice = 0; voice < OKIM6295_VOICES; voice++)
+ {
+ // initialize the rest of the structure //
+ info->voice[voice].volume = 0;
+ reset_adpcm(&info->voice[voice].adpcm);
+ }*/
+
+ //okim6295_state_save_register(info, device);
+
+ return info; //return info->master_clock / divisor;
+}
+
+
+
+void device_stop_okim6295(void *_chip)
+{
+ okim6295_state* chip = (okim6295_state *) _chip;
+
+ free(chip->ROM); chip->ROM = NULL;
+ chip->ROMSize = 0x00;
+
+ free(chip);
+}
+
+/**********************************************************************************************
+
+ DEVICE_RESET( okim6295 ) -- stop emulation of an OKIM6295-compatible chip
+
+***********************************************************************************************/
+
+//static DEVICE_RESET( okim6295 )
+void device_reset_okim6295(void *chip)
+{
+ okim6295_state *info = (okim6295_state *) chip;
+ int voice;
+
+ for (voice = 0; voice < OKIM6295_VOICES; voice++)
+ {
+ info->voice[voice].volume = 0;
+ reset_adpcm(&info->voice[voice].adpcm);
+
+ info->voice[voice].playing = 0;
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ okim6295_set_bank_base -- set the base of the bank for a given voice on a given chip
+
+***********************************************************************************************/
+
+//void okim6295_set_bank_base(running_device *device, int base)
+void okim6295_set_bank_base(okim6295_state *info, int base)
+{
+ //okim6295_state *info = get_safe_token(device);
+ //stream_update(info->stream);
+
+ /* if we are setting a non-zero base, and we have no bank, allocate one */
+ if (!info->bank_installed && base != 0)
+ {
+ /* override our memory map with a bank */
+ //memory_install_read_bank(device->space(), 0x00000, 0x3ffff, 0, 0, device->tag());
+ info->bank_installed = TRUE;
+ }
+
+ /* if we have a bank number, set the base pointer */
+ if (info->bank_installed)
+ {
+ info->bank_offs = base;
+ //memory_set_bankptr(device->machine, device->tag(), device->region->base.u8 + base);
+ }
+}
+
+
+
+/**********************************************************************************************
+
+ okim6295_set_pin7 -- adjust pin 7, which controls the internal clock division
+
+***********************************************************************************************/
+
+static void okim6295_clock_changed(okim6295_state *info)
+{
+ int divisor;
+ divisor = info->pin7_state ? 132 : 165;
+ //stream_set_sample_rate(info->stream, info->master_clock/divisor);
+}
+
+//void okim6295_set_pin7(running_device *device, int pin7)
+static void okim6295_set_pin7(okim6295_state *info, int pin7)
+{
+ //okim6295_state *info = get_safe_token(device);
+ //int divisor = pin7 ? 132 : 165;
+
+ info->pin7_state = pin7;
+ //stream_set_sample_rate(info->stream, info->master_clock/divisor);
+ okim6295_clock_changed(info);
+}
+
+
+/**********************************************************************************************
+
+ okim6295_status_r -- read the status port of an OKIM6295-compatible chip
+
+***********************************************************************************************/
+
+//READ8_DEVICE_HANDLER( okim6295_r )
+UINT8 okim6295_r(void *chip, offs_t offset)
+{
+ //okim6295_state *info = get_safe_token(device);
+ okim6295_state *info = (okim6295_state *) chip;
+ int i, result;
+
+ result = 0xf0; /* naname expects bits 4-7 to be 1 */
+
+ /* set the bit to 1 if something is playing on a given channel */
+ //stream_update(info->stream);
+ for (i = 0; i < OKIM6295_VOICES; i++)
+ {
+ struct ADPCMVoice *voice = &info->voice[i];
+
+ /* set the bit if it's playing */
+ if (voice->playing)
+ result |= 1 << i;
+ }
+
+ return result;
+}
+
+
+
+/**********************************************************************************************
+
+ okim6295_data_w -- write to the data port of an OKIM6295-compatible chip
+
+***********************************************************************************************/
+
+//WRITE8_DEVICE_HANDLER( okim6295_w )
+void okim6295_write_command(okim6295_state *info, UINT8 data)
+{
+ //okim6295_state *info = get_safe_token(device);
+
+ /* if a command is pending, process the second half */
+ if (info->command != -1)
+ {
+ int temp = data >> 4, i, start, stop;
+ offs_t base;
+
+ /* the manual explicitly says that it's not possible to start multiple voices at the same time */
+ if (temp != 0 && temp != 1 && temp != 2 && temp != 4 && temp != 8)
+ printf("OKI6295 start %x contact MAMEDEV\n", temp);
+
+ /* update the stream */
+ //stream_update(info->stream);
+
+ /* determine which voice(s) (voice is set by a 1 bit in the upper 4 bits of the second byte) */
+ for (i = 0; i < OKIM6295_VOICES; i++, temp >>= 1)
+ {
+ if (temp & 1)
+ {
+ struct ADPCMVoice *voice = &info->voice[i];
+
+ /* determine the start/stop positions */
+ base = info->command * 8;
+
+ //start = memory_raw_read_byte(device->space(), base + 0) << 16;
+ start = memory_raw_read_byte(info, base + 0) << 16;
+ start |= memory_raw_read_byte(info, base + 1) << 8;
+ start |= memory_raw_read_byte(info, base + 2) << 0;
+ start &= 0x3ffff;
+
+ stop = memory_raw_read_byte(info, base + 3) << 16;
+ stop |= memory_raw_read_byte(info, base + 4) << 8;
+ stop |= memory_raw_read_byte(info, base + 5) << 0;
+ stop &= 0x3ffff;
+
+ /* set up the voice to play this sample */
+ if (start < stop)
+ {
+ if (!voice->playing) /* fixes Got-cha and Steel Force */
+ {
+ voice->playing = 1;
+ voice->base_offset = start;
+ voice->sample = 0;
+ voice->count = 2 * (stop - start + 1);
+
+ /* also reset the ADPCM parameters */
+ reset_adpcm(&voice->adpcm);
+ voice->volume = volume_table[data & 0x0f];
+ }
+ else
+ {
+ //logerror("OKIM6295:'%s' requested to play sample %02x on non-stopped voice\n",device->tag(),info->command);
+ // just displays warnings when seeking
+ //logerror("OKIM6295: Voice %u requested to play sample %02x on non-stopped voice\n",i,info->command);
+ }
+ }
+ /* invalid samples go here */
+ else
+ {
+ //logerror("OKIM6295:'%s' requested to play invalid sample %02x\n",device->tag(),info->command);
+ logerror("OKIM6295: Voice %u requested to play invalid sample %02x\n",i,info->command);
+ voice->playing = 0;
+ }
+ }
+ }
+
+ /* reset the command */
+ info->command = -1;
+ }
+
+ /* if this is the start of a command, remember the sample number for next time */
+ else if (data & 0x80)
+ {
+ info->command = data & 0x7f;
+ }
+
+ /* otherwise, see if this is a silence command */
+ else
+ {
+ int temp = data >> 3, i;
+
+ /* update the stream, then turn it off */
+ //stream_update(info->stream);
+
+ /* determine which voice(s) (voice is set by a 1 bit in bits 3-6 of the command */
+ for (i = 0; i < OKIM6295_VOICES; i++, temp >>= 1)
+ {
+ if (temp & 1)
+ {
+ struct ADPCMVoice *voice = &info->voice[i];
+
+ voice->playing = 0;
+ }
+ }
+ }
+}
+
+void okim6295_w(void *_chip, offs_t offset, UINT8 data)
+{
+ okim6295_state* chip = (okim6295_state *) _chip;
+
+ switch(offset)
+ {
+ case 0x00:
+ okim6295_write_command(chip, data);
+ break;
+ case 0x08:
+ chip->master_clock &= ~0x000000FF;
+ chip->master_clock |= data << 0;
+ break;
+ case 0x09:
+ chip->master_clock &= ~0x0000FF00;
+ chip->master_clock |= data << 8;
+ break;
+ case 0x0A:
+ chip->master_clock &= ~0x00FF0000;
+ chip->master_clock |= data << 16;
+ break;
+ case 0x0B:
+ chip->master_clock &= ~0xFF000000;
+ chip->master_clock |= data << 24;
+ okim6295_clock_changed(chip);
+ break;
+ case 0x0C:
+ okim6295_set_pin7(chip, data);
+ break;
+ case 0x0F:
+ okim6295_set_bank_base(chip, data << 18);
+ break;
+ }
+
+ return;
+}
+
+void okim6295_write_rom(void *_chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
+ const UINT8* ROMData)
+{
+ okim6295_state *chip = (okim6295_state *) _chip;
+
+ if (chip->ROMSize != ROMSize)
+ {
+ chip->ROM = (UINT8*)realloc(chip->ROM, ROMSize);
+ chip->ROMSize = ROMSize;
+ memset(chip->ROM, 0xFF, ROMSize);
+ }
+ if (DataStart > ROMSize)
+ return;
+ if (DataStart + DataLength > ROMSize)
+ DataLength = ROMSize - DataStart;
+
+ memcpy(chip->ROM + DataStart, ROMData, DataLength);
+}
+
+
+void okim6295_set_mute_mask(void *_chip, UINT32 MuteMask)
+{
+ okim6295_state *chip = (okim6295_state *) _chip;
+ UINT8 CurChn;
+
+ for (CurChn = 0; CurChn < OKIM6295_VOICES; CurChn ++)
+ chip->voice[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
+}
diff --git a/Frameworks/GME/gme/okim6295.h b/Frameworks/GME/gme/okim6295.h
new file mode 100644
index 000000000..0ca8fc510
--- /dev/null
+++ b/Frameworks/GME/gme/okim6295.h
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "mamedef.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* an interface for the OKIM6295 and similar chips */
+
+/*
+ Note about the playback frequency: the external clock is internally divided,
+ depending on pin 7, by 132 (high) or 165 (low).
+*/
+/*typedef struct _okim6295_interface okim6295_interface;
+struct _okim6295_interface
+{
+ int pin7;
+};
+
+extern const okim6295_interface okim6295_interface_pin7high;
+extern const okim6295_interface okim6295_interface_pin7low;*/
+
+
+
+//void okim6295_set_bank_base(running_device *device, int base);
+//void okim6295_set_pin7(running_device *device, int pin7);
+
+void okim6295_update(void *, stream_sample_t **outputs, int samples);
+void * device_start_okim6295(int clock);
+void device_stop_okim6295(void *);
+void device_reset_okim6295(void *);
+
+//READ8_DEVICE_HANDLER( okim6295_r );
+//WRITE8_DEVICE_HANDLER( okim6295_w );
+void okim6295_w(void *, offs_t offset, UINT8 data);
+
+void okim6295_write_rom(void *, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
+ const UINT8* ROMData);
+void okim6295_set_mute_mask(void *, UINT32 MuteMask);
+
+
+/*
+ To help the various custom ADPCM generators out there,
+ the following routines may be used.
+*/
+struct adpcm_state
+{
+ INT32 signal;
+ INT32 step;
+};
+void reset_adpcm(struct adpcm_state *state);
+INT16 clock_adpcm(struct adpcm_state *state, UINT8 nibble);
+
+//DEVICE_GET_INFO( okim6295 );
+//#define SOUND_OKIM6295 DEVICE_GET_INFO_NAME( okim6295 )
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Frameworks/GME/gme/pwm.c b/Frameworks/GME/gme/pwm.c
new file mode 100644
index 000000000..b1c78129f
--- /dev/null
+++ b/Frameworks/GME/gme/pwm.c
@@ -0,0 +1,444 @@
+/***************************************************************************
+ * Gens: PWM audio emulator. *
+ * *
+ * Copyright (c) 1999-2002 by Stéphane Dallongeville *
+ * Copyright (c) 2003-2004 by Stéphane Akhoun *
+ * Copyright (c) 2008-2009 by David Korth *
+ * *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms of the GNU General Public License as published by the *
+ * Free Software Foundation; either version 2 of the License, or (at your *
+ * option) any later version. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "pwm.h"
+
+#include
+#include
+
+#ifndef INLINE
+#define INLINE static __inline
+#endif
+
+//#include "gens_core/mem/mem_sh2.h"
+//#include "gens_core/cpu/sh2/sh2.h"
+
+#define CHILLY_WILLY_SCALE 1
+
+#if PWM_BUF_SIZE == 8
+unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE] =
+{
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x40,
+};
+#elif PWM_BUF_SIZE == 4
+unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE] =
+{
+ 0x40, 0x00, 0x00, 0x80,
+ 0x80, 0x40, 0x00, 0x00,
+ 0x00, 0x80, 0x40, 0x00,
+ 0x00, 0x00, 0x80, 0x40,
+};
+#else
+#error PWM_BUF_SIZE must equal 4 or 8.
+#endif /* PWM_BUF_SIZE */
+
+typedef struct _pwm_chip
+{
+ unsigned short PWM_FIFO_R[8];
+ unsigned short PWM_FIFO_L[8];
+ unsigned int PWM_RP_R;
+ unsigned int PWM_WP_R;
+ unsigned int PWM_RP_L;
+ unsigned int PWM_WP_L;
+ unsigned int PWM_Cycles;
+ unsigned int PWM_Cycle;
+ unsigned int PWM_Cycle_Cnt;
+ unsigned int PWM_Int;
+ unsigned int PWM_Int_Cnt;
+ unsigned int PWM_Mode;
+ //unsigned int PWM_Enable;
+ unsigned int PWM_Out_R;
+ unsigned int PWM_Out_L;
+
+ unsigned int PWM_Cycle_Tmp;
+ unsigned int PWM_Cycles_Tmp;
+ unsigned int PWM_Int_Tmp;
+ unsigned int PWM_FIFO_L_Tmp;
+ unsigned int PWM_FIFO_R_Tmp;
+
+#if CHILLY_WILLY_SCALE
+// TODO: Fix Chilly Willy's new scaling algorithm.
+ /* PWM scaling variables. */
+ int PWM_Offset;
+ int PWM_Scale;
+ //int PWM_Loudness;
+#endif
+
+ int clock;
+} pwm_chip;
+#if CHILLY_WILLY_SCALE
+// TODO: Fix Chilly Willy's new scaling algorithm.
+#define PWM_Loudness 0
+#endif
+
+void PWM_Init(pwm_chip* chip);
+void PWM_Recalc_Scale(pwm_chip* chip);
+
+void PWM_Set_Cycle(pwm_chip* chip, unsigned int cycle);
+void PWM_Set_Int(pwm_chip* chip, unsigned int int_time);
+
+void PWM_Update(pwm_chip* chip, int **buf, int length);
+
+
+/**
+ * PWM_Init(): Initialize the PWM audio emulator.
+ */
+void PWM_Init(pwm_chip* chip)
+{
+ chip->PWM_Mode = 0;
+ chip->PWM_Out_R = 0;
+ chip->PWM_Out_L = 0;
+
+ memset(chip->PWM_FIFO_R, 0x00, sizeof(chip->PWM_FIFO_R));
+ memset(chip->PWM_FIFO_L, 0x00, sizeof(chip->PWM_FIFO_L));
+
+ chip->PWM_RP_R = 0;
+ chip->PWM_WP_R = 0;
+ chip->PWM_RP_L = 0;
+ chip->PWM_WP_L = 0;
+ chip->PWM_Cycle_Tmp = 0;
+ chip->PWM_Int_Tmp = 0;
+ chip->PWM_FIFO_L_Tmp = 0;
+ chip->PWM_FIFO_R_Tmp = 0;
+
+ //PWM_Loudness = 0;
+ PWM_Set_Cycle(chip, 0);
+ PWM_Set_Int(chip, 0);
+}
+
+
+#if CHILLY_WILLY_SCALE
+// TODO: Fix Chilly Willy's new scaling algorithm.
+void PWM_Recalc_Scale(pwm_chip* chip)
+{
+ chip->PWM_Offset = (chip->PWM_Cycle / 2) + 1;
+ chip->PWM_Scale = 0x7FFF00 / chip->PWM_Offset;
+}
+#endif
+
+
+void PWM_Set_Cycle(pwm_chip* chip, unsigned int cycle)
+{
+ cycle--;
+ chip->PWM_Cycle = (cycle & 0xFFF);
+ chip->PWM_Cycle_Cnt = chip->PWM_Cycles;
+
+#if CHILLY_WILLY_SCALE
+ // TODO: Fix Chilly Willy's new scaling algorithm.
+ PWM_Recalc_Scale(chip);
+#endif
+}
+
+
+void PWM_Set_Int(pwm_chip* chip, unsigned int int_time)
+{
+ int_time &= 0x0F;
+ if (int_time)
+ chip->PWM_Int = chip->PWM_Int_Cnt = int_time;
+ else
+ chip->PWM_Int = chip->PWM_Int_Cnt = 16;
+}
+
+
+void PWM_Clear_Timer(pwm_chip* chip)
+{
+ chip->PWM_Cycle_Cnt = 0;
+}
+
+
+/**
+ * PWM_SHIFT(): Shift PWM data.
+ * @param src: Channel (L or R) with the source data.
+ * @param dest Channel (L or R) for the destination.
+ */
+#define PWM_SHIFT(src, dest) \
+{ \
+ /* Make sure the source FIFO isn't empty. */ \
+ if (PWM_RP_##src != PWM_WP_##src) \
+ { \
+ /* Get destination channel output from the source channel FIFO. */ \
+ PWM_Out_##dest = PWM_FIFO_##src[PWM_RP_##src]; \
+ \
+ /* Increment the source channel read pointer, resetting to 0 if it overflows. */ \
+ PWM_RP_##src = (PWM_RP_##src + 1) & (PWM_BUF_SIZE - 1); \
+ } \
+}
+
+
+/*static void PWM_Shift_Data(void)
+{
+ switch (PWM_Mode & 0x0F)
+ {
+ case 0x01:
+ case 0x0D:
+ // Rx_LL: Right -> Ignore, Left -> Left
+ PWM_SHIFT(L, L);
+ break;
+
+ case 0x02:
+ case 0x0E:
+ // Rx_LR: Right -> Ignore, Left -> Right
+ PWM_SHIFT(L, R);
+ break;
+
+ case 0x04:
+ case 0x07:
+ // RL_Lx: Right -> Left, Left -> Ignore
+ PWM_SHIFT(R, L);
+ break;
+
+ case 0x05:
+ case 0x09:
+ // RR_LL: Right -> Right, Left -> Left
+ PWM_SHIFT(L, L);
+ PWM_SHIFT(R, R);
+ break;
+
+ case 0x06:
+ case 0x0A:
+ // RL_LR: Right -> Left, Left -> Right
+ PWM_SHIFT(L, R);
+ PWM_SHIFT(R, L);
+ break;
+
+ case 0x08:
+ case 0x0B:
+ // RR_Lx: Right -> Right, Left -> Ignore
+ PWM_SHIFT(R, R);
+ break;
+
+ case 0x00:
+ case 0x03:
+ case 0x0C:
+ case 0x0F:
+ default:
+ // Rx_Lx: Right -> Ignore, Left -> Ignore
+ break;
+ }
+}
+
+
+void PWM_Update_Timer(unsigned int cycle)
+{
+ // Don't do anything if PWM is disabled in the Sound menu.
+
+ // Don't do anything if PWM isn't active.
+ if ((PWM_Mode & 0x0F) == 0x00)
+ return;
+
+ if (PWM_Cycle == 0x00 || (PWM_Cycle_Cnt > cycle))
+ return;
+
+ PWM_Shift_Data();
+
+ PWM_Cycle_Cnt += PWM_Cycle;
+
+ PWM_Int_Cnt--;
+ if (PWM_Int_Cnt == 0)
+ {
+ PWM_Int_Cnt = PWM_Int;
+
+ if (PWM_Mode & 0x0080)
+ {
+ // RPT => generate DREQ1 as well as INT
+ SH2_DMA1_Request(&M_SH2, 1);
+ SH2_DMA1_Request(&S_SH2, 1);
+ }
+
+ if (_32X_MINT & 1)
+ SH2_Interrupt(&M_SH2, 6);
+ if (_32X_SINT & 1)
+ SH2_Interrupt(&S_SH2, 6);
+ }
+}*/
+
+
+INLINE int PWM_Update_Scale(pwm_chip* chip, int PWM_In)
+{
+ if (PWM_In == 0)
+ return 0;
+
+ // TODO: Chilly Willy's new scaling algorithm breaks drx's Sonic 1 32X (with PWM drums).
+#ifdef CHILLY_WILLY_SCALE
+ //return (((PWM_In & 0xFFF) - chip->PWM_Offset) * chip->PWM_Scale) >> (8 - PWM_Loudness);
+ // Knuckles' Chaotix: Tachy Touch uses the values 0xF?? for negative values
+ // This small modification fixes the terrible pops.
+ PWM_In &= 0xFFF;
+ if (PWM_In & 0x800)
+ PWM_In |= ~0xFFF;
+ return ((PWM_In - chip->PWM_Offset) * chip->PWM_Scale) >> (8 - PWM_Loudness);
+#else
+ const int PWM_adjust = ((chip->PWM_Cycle >> 1) + 1);
+ int PWM_Ret = ((chip->PWM_In & 0xFFF) - PWM_adjust);
+
+ // Increase PWM volume so it's audible.
+ PWM_Ret <<= (5+2);
+
+ // Make sure the PWM isn't oversaturated.
+ if (PWM_Ret > 32767)
+ PWM_Ret = 32767;
+ else if (PWM_Ret < -32768)
+ PWM_Ret = -32768;
+
+ return PWM_Ret;
+#endif
+}
+
+
+void PWM_Update(pwm_chip* chip, int **buf, int length)
+{
+ int tmpOutL;
+ int tmpOutR;
+ int i;
+
+ //if (!PWM_Enable)
+ // return;
+
+ if (chip->PWM_Out_L == 0 && chip->PWM_Out_R == 0)
+ {
+ memset(buf[0], 0x00, length * sizeof(int));
+ memset(buf[1], 0x00, length * sizeof(int));
+ return;
+ }
+
+ // New PWM scaling algorithm provided by Chilly Willy on the Sonic Retro forums.
+ tmpOutL = PWM_Update_Scale(chip, (int)chip->PWM_Out_L);
+ tmpOutR = PWM_Update_Scale(chip, (int)chip->PWM_Out_R);
+
+ for (i = 0; i < length; i ++)
+ {
+ buf[0][i] = tmpOutL;
+ buf[1][i] = tmpOutR;
+ }
+}
+
+
+void pwm_update(void *_chip, stream_sample_t **outputs, int samples)
+{
+ pwm_chip *chip = (pwm_chip *) _chip;
+
+ PWM_Update(chip, outputs, samples);
+}
+
+void * device_start_pwm(int clock)
+{
+ /* allocate memory for the chip */
+ //pwm_state *chip = get_safe_token(device);
+ pwm_chip *chip;
+ int rate;
+
+ chip = (pwm_chip *) malloc(sizeof(pwm_chip));
+ if (!chip) return chip;
+
+ rate = 22020; // that's the rate the PWM is mostly used
+ chip->clock = clock;
+
+ PWM_Init(chip);
+ /* allocate the stream */
+ //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update);
+
+ return chip;
+}
+
+void device_stop_pwm(void *chip)
+{
+ //pwm_chip *chip = &PWM_Chip[ChipID];
+ //free(chip->ram);
+ free(chip);
+}
+
+void device_reset_pwm(void *_chip)
+{
+ pwm_chip *chip = (pwm_chip *) _chip;
+ PWM_Init(chip);
+}
+
+void pwm_chn_w(void *_chip, UINT8 Channel, UINT16 data)
+{
+ pwm_chip *chip = (pwm_chip *) _chip;
+
+ if (chip->clock == 1)
+ { // old-style commands
+ switch(Channel)
+ {
+ case 0x00:
+ chip->PWM_Out_L = data;
+ break;
+ case 0x01:
+ chip->PWM_Out_R = data;
+ break;
+ case 0x02:
+ PWM_Set_Cycle(chip, data);
+ break;
+ case 0x03:
+ chip->PWM_Out_L = data;
+ chip->PWM_Out_R = data;
+ break;
+ }
+ }
+ else
+ {
+ switch(Channel)
+ {
+ case 0x00/2: // control register
+ PWM_Set_Int(chip, data >> 8);
+ break;
+ case 0x02/2: // cycle register
+ PWM_Set_Cycle(chip, data);
+ break;
+ case 0x04/2: // l ch
+ chip->PWM_Out_L = data;
+ break;
+ case 0x06/2: // r ch
+ chip->PWM_Out_R = data;
+ if (! chip->PWM_Mode)
+ {
+ if (chip->PWM_Out_L == chip->PWM_Out_R)
+ {
+ // fixes these terrible pops when
+ // starting/stopping/pausing the song
+ chip->PWM_Offset = data;
+ chip->PWM_Mode = 0x01;
+ }
+ }
+ break;
+ case 0x08/2: // mono ch
+ chip->PWM_Out_L = data;
+ chip->PWM_Out_R = data;
+ if (! chip->PWM_Mode)
+ {
+ chip->PWM_Offset = data;
+ chip->PWM_Mode = 0x01;
+ }
+ break;
+ }
+ }
+
+ return;
+}
diff --git a/Frameworks/GME/gme/pwm.h b/Frameworks/GME/gme/pwm.h
new file mode 100644
index 000000000..98ccdbc7d
--- /dev/null
+++ b/Frameworks/GME/gme/pwm.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Gens: PWM audio emulator. *
+ * *
+ * Copyright (c) 1999-2002 by Stéphane Dallongeville *
+ * Copyright (c) 2003-2004 by Stéphane Akhoun *
+ * Copyright (c) 2008 by David Korth *
+ * *
+ * This program is free software; you can redistribute it and/or modify it *
+ * under the terms of the GNU General Public License as published by the *
+ * Free Software Foundation; either version 2 of the License, or (at your *
+ * option) any later version. *
+ * *
+ * This program 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 General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License along *
+ * with this program; if not, write to the Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#define PWM_BUF_SIZE 4
+
+#include "mamedef.h"
+
+/*extern unsigned char PWM_FULL_TAB[PWM_BUF_SIZE * PWM_BUF_SIZE];
+
+extern unsigned short PWM_FIFO_R[8];
+extern unsigned short PWM_FIFO_L[8];
+extern unsigned int PWM_RP_R;
+extern unsigned int PWM_WP_R;
+extern unsigned int PWM_RP_L;
+extern unsigned int PWM_WP_L;
+extern unsigned int PWM_Cycles;
+extern unsigned int PWM_Cycle;
+extern unsigned int PWM_Cycle_Cnt;
+extern unsigned int PWM_Int;
+extern unsigned int PWM_Int_Cnt;
+extern unsigned int PWM_Mode;
+extern unsigned int PWM_Enable;
+extern unsigned int PWM_Out_R;
+extern unsigned int PWM_Out_L;*/
+
+//void PWM_Init(void);
+//void PWM_Recalc_Scale(void);
+
+/* Functions called by x86 asm. */
+//void PWM_Set_Cycle(unsigned int cycle);
+//void PWM_Set_Int(unsigned int int_time);
+
+/* Functions called by C/C++ code only. */
+//void PWM_Clear_Timer(void);
+//void PWM_Update_Timer(unsigned int cycle);
+//void PWM_Update(int **buf, int length);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void pwm_update(void *chip, stream_sample_t **outputs, int samples);
+
+void * device_start_pwm(int clock);
+void device_stop_pwm(void *chip);
+void device_reset_pwm(void *chip);
+
+void pwm_chn_w(void *chip, UINT8 Channel, UINT16 data);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Frameworks/GME/gme/qmix.c b/Frameworks/GME/gme/qmix.c
new file mode 100644
index 000000000..7a855eec3
--- /dev/null
+++ b/Frameworks/GME/gme/qmix.c
@@ -0,0 +1,462 @@
+/////////////////////////////////////////////////////////////////////////////
+//
+// qmix - QSound mixer
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#include "qmix.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define ANTICLICK_TIME (64)
+#define ANTICLICK_THRESHHOLD (32)
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define RENDERMAX (200)
+
+/////////////////////////////////////////////////////////////////////////////
+
+static const sint32 gauss_shuffled_reverse_table[1024] = {
+ 366,1305, 374, 0, 362,1304, 378, 0, 358,1304, 381, 0, 354,1304, 385, 0, 351,1304, 389, 0, 347,1304, 393, 0, 343,1303, 397, 0, 339,1303, 401, 0,
+ 336,1303, 405, 0, 332,1302, 410, 0, 328,1302, 414, 0, 325,1301, 418, 0, 321,1300, 422, 0, 318,1300, 426, 0, 314,1299, 430, 0, 311,1298, 434, 0,
+ 307,1297, 439, 1, 304,1297, 443, 1, 300,1296, 447, 1, 297,1295, 451, 1, 293,1294, 456, 1, 290,1293, 460, 1, 286,1292, 464, 1, 283,1291, 469, 1,
+ 280,1290, 473, 1, 276,1288, 477, 1, 273,1287, 482, 1, 270,1286, 486, 2, 267,1284, 491, 2, 263,1283, 495, 2, 260,1282, 499, 2, 257,1280, 504, 2,
+ 254,1279, 508, 2, 251,1277, 513, 2, 248,1275, 517, 3, 245,1274, 522, 3, 242,1272, 527, 3, 239,1270, 531, 3, 236,1269, 536, 3, 233,1267, 540, 4,
+ 230,1265, 545, 4, 227,1263, 550, 4, 224,1261, 554, 4, 221,1259, 559, 4, 218,1257, 563, 5, 215,1255, 568, 5, 212,1253, 573, 5, 210,1251, 577, 5,
+ 207,1248, 582, 6, 204,1246, 587, 6, 201,1244, 592, 6, 199,1241, 596, 6, 196,1239, 601, 7, 193,1237, 606, 7, 191,1234, 611, 7, 188,1232, 615, 8,
+ 186,1229, 620, 8, 183,1227, 625, 8, 180,1224, 630, 9, 178,1221, 635, 9, 175,1219, 640, 9, 173,1216, 644, 10, 171,1213, 649, 10, 168,1210, 654, 10,
+ 166,1207, 659, 11, 163,1205, 664, 11, 161,1202, 669, 11, 159,1199, 674, 12, 156,1196, 678, 12, 154,1193, 683, 13, 152,1190, 688, 13, 150,1186, 693, 14,
+ 147,1183, 698, 14, 145,1180, 703, 15, 143,1177, 708, 15, 141,1174, 713, 15, 139,1170, 718, 16, 137,1167, 723, 16, 134,1164, 728, 17, 132,1160, 732, 17,
+ 130,1157, 737, 18, 128,1153, 742, 19, 126,1150, 747, 19, 124,1146, 752, 20, 122,1143, 757, 20, 120,1139, 762, 21, 118,1136, 767, 21, 117,1132, 772, 22,
+ 115,1128, 777, 23, 113,1125, 782, 23, 111,1121, 787, 24, 109,1117, 792, 24, 107,1113, 797, 25, 106,1109, 802, 26, 104,1106, 806, 27, 102,1102, 811, 27,
+ 100,1098, 816, 28, 99,1094, 821, 29, 97,1090, 826, 29, 95,1086, 831, 30, 94,1082, 836, 31, 92,1078, 841, 32, 90,1074, 846, 32, 89,1070, 851, 33,
+ 87,1066, 855, 34, 86,1061, 860, 35, 84,1057, 865, 36, 83,1053, 870, 36, 81,1049, 875, 37, 80,1045, 880, 38, 78,1040, 884, 39, 77,1036, 889, 40,
+ 76,1032, 894, 41, 74,1027, 899, 42, 73,1023, 904, 43, 71,1019, 908, 44, 70,1014, 913, 45, 69,1010, 918, 46, 67,1005, 923, 47, 66,1001, 927, 48,
+ 65, 997, 932, 49, 64, 992, 937, 50, 62, 988, 941, 51, 61, 983, 946, 52, 60, 978, 951, 53, 59, 974, 955, 54, 58, 969, 960, 55, 56, 965, 965, 56,
+ 55, 960, 969, 58, 54, 955, 974, 59, 53, 951, 978, 60, 52, 946, 983, 61, 51, 941, 988, 62, 50, 937, 992, 64, 49, 932, 997, 65, 48, 927,1001, 66,
+ 47, 923,1005, 67, 46, 918,1010, 69, 45, 913,1014, 70, 44, 908,1019, 71, 43, 904,1023, 73, 42, 899,1027, 74, 41, 894,1032, 76, 40, 889,1036, 77,
+ 39, 884,1040, 78, 38, 880,1045, 80, 37, 875,1049, 81, 36, 870,1053, 83, 36, 865,1057, 84, 35, 860,1061, 86, 34, 855,1066, 87, 33, 851,1070, 89,
+ 32, 846,1074, 90, 32, 841,1078, 92, 31, 836,1082, 94, 30, 831,1086, 95, 29, 826,1090, 97, 29, 821,1094, 99, 28, 816,1098, 100, 27, 811,1102, 102,
+ 27, 806,1106, 104, 26, 802,1109, 106, 25, 797,1113, 107, 24, 792,1117, 109, 24, 787,1121, 111, 23, 782,1125, 113, 23, 777,1128, 115, 22, 772,1132, 117,
+ 21, 767,1136, 118, 21, 762,1139, 120, 20, 757,1143, 122, 20, 752,1146, 124, 19, 747,1150, 126, 19, 742,1153, 128, 18, 737,1157, 130, 17, 732,1160, 132,
+ 17, 728,1164, 134, 16, 723,1167, 137, 16, 718,1170, 139, 15, 713,1174, 141, 15, 708,1177, 143, 15, 703,1180, 145, 14, 698,1183, 147, 14, 693,1186, 150,
+ 13, 688,1190, 152, 13, 683,1193, 154, 12, 678,1196, 156, 12, 674,1199, 159, 11, 669,1202, 161, 11, 664,1205, 163, 11, 659,1207, 166, 10, 654,1210, 168,
+ 10, 649,1213, 171, 10, 644,1216, 173, 9, 640,1219, 175, 9, 635,1221, 178, 9, 630,1224, 180, 8, 625,1227, 183, 8, 620,1229, 186, 8, 615,1232, 188,
+ 7, 611,1234, 191, 7, 606,1237, 193, 7, 601,1239, 196, 6, 596,1241, 199, 6, 592,1244, 201, 6, 587,1246, 204, 6, 582,1248, 207, 5, 577,1251, 210,
+ 5, 573,1253, 212, 5, 568,1255, 215, 5, 563,1257, 218, 4, 559,1259, 221, 4, 554,1261, 224, 4, 550,1263, 227, 4, 545,1265, 230, 4, 540,1267, 233,
+ 3, 536,1269, 236, 3, 531,1270, 239, 3, 527,1272, 242, 3, 522,1274, 245, 3, 517,1275, 248, 2, 513,1277, 251, 2, 508,1279, 254, 2, 504,1280, 257,
+ 2, 499,1282, 260, 2, 495,1283, 263, 2, 491,1284, 267, 2, 486,1286, 270, 1, 482,1287, 273, 1, 477,1288, 276, 1, 473,1290, 280, 1, 469,1291, 283,
+ 1, 464,1292, 286, 1, 460,1293, 290, 1, 456,1294, 293, 1, 451,1295, 297, 1, 447,1296, 300, 1, 443,1297, 304, 1, 439,1297, 307, 0, 434,1298, 311,
+ 0, 430,1299, 314, 0, 426,1300, 318, 0, 422,1300, 321, 0, 418,1301, 325, 0, 414,1302, 328, 0, 410,1302, 332, 0, 405,1303, 336, 0, 401,1303, 339,
+ 0, 397,1303, 343, 0, 393,1304, 347, 0, 389,1304, 351, 0, 385,1304, 354, 0, 381,1304, 358, 0, 378,1304, 362, 0, 374,1305, 366, 0, 370,1305, 370,
+};
+
+static const sint32 pan_table[33] = {
+ 0, 724,1024,1254,1448,1619,1774,1916,
+2048,2172,2290,2401,2508,2611,2709,2804,
+2896,2985,3072,3156,3238,3318,3396,3473,
+3547,3620,3692,3762,3831,3899,3966,4031,
+4096};
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Static information
+//
+sint32 EMU_CALL _qmix_init(void) { return 0; }
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// State information
+//
+#define QMIXSTATE ((struct QMIX_STATE*)(state))
+
+struct QMIX_CHAN {
+ uint32 on;
+ uint32 startbank;
+ uint32 startaddr;
+ uint32 curbank;
+ uint32 curaddr;
+ uint32 startloop;
+ uint32 startend;
+ uint32 curloop;
+ uint32 curend;
+ uint32 phase;
+ uint32 pitch;
+ uint32 vol;
+ uint32 pan;
+ sint32 current_mix_l;
+ sint32 current_mix_r;
+ sint32 sample[4];
+ sint32 sample_last_l;
+ sint32 sample_last_r;
+ sint32 sample_anticlick_l;
+ sint32 sample_anticlick_r;
+ sint32 sample_anticlick_remaining_l;
+ sint32 sample_anticlick_remaining_r;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+static EMU_INLINE void get_anticlicked_samples(
+ struct QMIX_CHAN *chan, sint32 *l, sint32 *r
+) {
+ sint32 out, remain;
+ remain = chan->sample_anticlick_remaining_l;
+ if(remain) {
+ sint32 diff = chan->sample_last_l - chan->sample_anticlick_l;
+ if(diff < 0) { diff = -diff; }
+ if(diff < ANTICLICK_THRESHHOLD) {
+ out = chan->sample_last_l;
+ chan->sample_anticlick_remaining_l = 0;
+ } else {
+ out = (
+ chan->sample_last_l * (ANTICLICK_TIME-remain) +
+ chan->sample_anticlick_l * (remain)
+ ) / ANTICLICK_TIME;
+ chan->sample_anticlick_remaining_l--;
+ }
+ } else {
+ out = chan->sample_last_l;
+ }
+ *l = out;
+
+ remain = chan->sample_anticlick_remaining_r;
+ if(remain) {
+ sint32 diff = chan->sample_last_r - chan->sample_anticlick_r;
+ if(diff < 0) { diff = -diff; }
+ if(diff < ANTICLICK_THRESHHOLD) {
+ out = chan->sample_last_r;
+ chan->sample_anticlick_remaining_r = 0;
+ } else {
+ out = (
+ chan->sample_last_r * (ANTICLICK_TIME-remain) +
+ chan->sample_anticlick_r * (remain)
+ ) / ANTICLICK_TIME;
+ chan->sample_anticlick_remaining_r--;
+ }
+ } else {
+ out = chan->sample_last_r;
+ }
+ *r = out;
+
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static EMU_INLINE void anticlick(struct QMIX_CHAN *chan) {
+ sint32 l, r;
+ get_anticlicked_samples(chan, &l, &r);
+ chan->sample_anticlick_l = l;
+ chan->sample_anticlick_r = r;
+ chan->sample_anticlick_remaining_l = ANTICLICK_TIME;
+ chan->sample_anticlick_remaining_r = ANTICLICK_TIME;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+struct QMIX_STATE {
+ uint8 *sample_rom;
+ uint32 sample_rom_size;
+ uint32 pitchscaler;
+ struct QMIX_CHAN chan[16];
+ sint32 last_in_l;
+ sint32 last_in_r;
+ sint32 last_out_l;
+ sint32 last_out_r;
+ sint32 acc_l;
+ sint32 acc_r;
+};
+
+uint32 EMU_CALL _qmix_get_state_size(void) {
+ return sizeof(struct QMIX_STATE);
+}
+
+void EMU_CALL _qmix_clear_state(void *state) {
+ memset(state, 0, sizeof(struct QMIX_STATE));
+
+
+}
+
+void EMU_CALL _qmix_set_sample_rom(void *state, void *rom, uint32 size) {
+ QMIXSTATE->sample_rom = rom;
+ QMIXSTATE->sample_rom_size = size;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static EMU_INLINE void chan_advance(
+ struct QMIX_STATE *state,
+ struct QMIX_CHAN *chan
+) {
+ uint32 rom_addr = chan->curbank + chan->curaddr;
+ if(rom_addr >= state->sample_rom_size) rom_addr = 0;
+ chan->sample[0] = chan->sample[1];
+ chan->sample[1] = chan->sample[2];
+ chan->sample[2] = chan->sample[3];
+ chan->sample[3] = (sint32)((sint8)(state->sample_rom[rom_addr]));
+ chan->curaddr++;
+// FIXME: MAME thinks this is >=, but is it > ?
+ if(chan->curaddr >= chan->curend) {
+// if(!chan->curloop) {
+// chan->on = 0;
+// chan->curaddr--;
+// } else {
+ chan->curaddr = chan->curend - chan->curloop;
+// chan->curaddr -= 1 + chan->curloop;
+// }
+ }
+ chan->curaddr &= 0xFFFF;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static EMU_INLINE sint32 chan_get_resampled(
+ struct QMIX_STATE *state,
+ struct QMIX_CHAN *chan
+) {
+ sint32 sum;
+ sint32 phase = chan->phase & 0xFFF;
+
+// sum = chan->sample[2];
+// sum <<= 8;
+
+ const sint32 *gauss = (sint32*)
+ (((sint8*)gauss_shuffled_reverse_table) + (phase & 0x0FF0));
+ sum = chan->sample[0] * gauss[0];
+ sum += chan->sample[1] * gauss[1];
+ sum += chan->sample[2] * gauss[2];
+ sum += chan->sample[3] * gauss[3];
+ sum /= 8;
+
+// sum = chan->sample[1] * (0x1000-phase);
+// sum += chan->sample[2] * ( phase);
+// sum >>= 4;
+
+ chan->phase += chan->pitch;
+ while(chan->phase >= 0x1000) {
+ chan_advance(state, chan);
+ chan->phase -= 0x1000;
+ }
+
+ return sum;
+}
+
+static EMU_INLINE void chan_get_stereo_anticlicked(
+ struct QMIX_STATE *state,
+ struct QMIX_CHAN *chan,
+ sint32 *l,
+ sint32 *r
+) {
+ if(!chan->on) {
+ chan->sample_last_l = 0;
+ chan->sample_last_r = 0;
+ } else {
+ sint32 out = chan_get_resampled(state, chan);
+ chan->sample_last_l = (out * chan->current_mix_l) / 0x8000;
+ chan->sample_last_r = (out * chan->current_mix_r) / 0x8000;
+ // if we suddenly keyed off, perform an anticlick here
+ if(!chan->on) { anticlick(chan); }
+ }
+ get_anticlicked_samples(chan, l, r);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+static void recalc_mix(struct QMIX_CHAN *chan) {
+ sint32 realpan = (chan->pan & 0x3F) - 0x10;
+ sint32 realvol = chan->vol & 0xFFFF;
+ if(realpan < 0x00) realpan = 0x00;
+ if(realpan > 0x20) realpan = 0x20;
+
+// chan->current_mix_l = realvol << 3;
+// chan->current_mix_r = realvol << 3;
+// if(realpan < 0x10) {
+// chan->current_mix_r *= realpan;
+// chan->current_mix_r >>= 4;
+// }
+// if(realpan > 0x10) {
+// chan->current_mix_l *= (0x20-realpan);
+// chan->current_mix_l >>= 4;
+// }
+
+// chan->current_mix_l = ((0x20-realpan) * realvol) >> 1;
+// chan->current_mix_r = (( realpan) * realvol) >> 1;
+
+ chan->current_mix_l = (realvol * pan_table[0x20-realpan]) / 0x2000;
+ chan->current_mix_r = (realvol * pan_table[ realpan]) / 0x2000;
+
+ // perform anticlick
+ //anticlick(chan);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Command handling
+//
+//#include
+
+void EMU_CALL _qmix_command(void *state, uint8 cmd, uint16 data) {
+ struct QMIX_CHAN *chan;
+ uint32 ch = 0;
+ uint32 reg = 99;
+//printf("qmix command 0x%02X:0x%04X\n",cmd,data);
+ if(cmd < 0x80) {
+ reg = cmd & 7;
+ ch = cmd >> 3;
+ } else if(cmd < 0x90) {
+ reg = 8;
+ ch = cmd - 0x80;
+ } else if(cmd >= 0xBA && cmd < 0xCA) {
+ reg = 9;
+ ch = cmd - 0xBA;
+ } else {
+ reg = 99;
+ ch = 0;
+ }
+ chan = QMIXSTATE->chan + ch;
+ switch(reg) {
+ case 0: // bank
+ ch = (ch+1) & 0xF; chan = QMIXSTATE->chan + ch;
+ //printf("qmix: bank ch%X = %04X\n",ch,data);
+ chan->startbank = (((uint32)data) & 0x7F) << 16;
+ break;
+ case 1: // start
+ //printf("qmix: start ch%X = %04X\n",ch,data);
+ chan->startaddr = ((uint32)data) & 0xFFFF;
+ break;
+ case 2: // pitch
+ //printf("qmix: pitch ch%X = %04X\n",ch,data);
+ chan->pitch = (((uint32)(data & 0xFFFF)) * QMIXSTATE->pitchscaler) / 0x10000;
+ if (chan->pitch == 0) {
+ chan->on = 0;
+ anticlick(chan);
+ }
+ break;
+ case 3: // unknown
+ break;
+ case 4: // loop start
+ //printf("qmix: loop ch%X = %04X\n",ch,data);
+ chan->startloop = data;
+ break;
+ case 5: // end
+ //printf("qmix: end ch%X = %04X\n",ch,data);
+ chan->startend = data;
+ break;
+ case 6: // volume
+ //printf("qmix: vol ch%X = %04X\n",ch,data);
+//printf("volume=%04X\n",data);
+// if(!data) {
+// chan->on = 0;
+// } else {
+// chan->on = 1;
+// chan->address = chan->start;
+// chan->phase = 0;
+// }
+ //printf("qmix: unknown reg3 ch%X = %04X\n",ch,data);
+ if(data == 0) {
+ chan->on = 0;
+ anticlick(chan);
+ } else if (chan->on == 0) {
+ chan->on = 1;
+ chan->curbank = chan->startbank;
+ chan->curaddr = chan->startaddr;
+ chan->curloop = chan->startloop;
+ chan->curend = chan->startend;
+ chan->phase = 0;
+ chan->sample[0] = 0;
+ chan->sample[1] = 0;
+ chan->sample[2] = 0;
+ chan->sample[3] = 0;
+ anticlick(chan);
+ }
+
+ chan->vol = data;
+ recalc_mix(chan);
+ break;
+ case 7: // unknown
+ //printf("qmix: unknown reg7 ch%X = %04X\n",ch,data);
+ break;
+ case 8: // pan (0x110-0x130)
+ //printf("qmix: pan ch%X = %04X\n",ch,data);
+//printf("pan=%04X\n",data);
+ chan->pan = data;
+ recalc_mix(chan);
+ break;
+ case 9: // ADSR?
+ //printf("qmix: unknown reg9 ch%X = %04X\n",ch,data);
+ break;
+ default:
+ //printf("qmix: unknown reg %02X = %04X\n",cmd,data);
+ break;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Rendering
+//
+static void render(
+ struct QMIX_STATE *state,
+ sint16 *buf,
+ uint32 samples
+) {
+ sint32 buf_l[RENDERMAX];
+ sint32 buf_r[RENDERMAX];
+ sint32 l, r;
+ uint32 s;
+ int ch;
+ memset(buf_l, 0, 4 * samples);
+ memset(buf_r, 0, 4 * samples);
+ for(ch = 0; ch < 16; ch++) {
+ struct QMIX_CHAN *chan = state->chan + ch;
+ for(s = 0; s < samples; s++) {
+ chan_get_stereo_anticlicked(state, chan, &l, &r);
+ buf_l[s] += l;
+ buf_r[s] += r;
+ }
+ }
+ if(!buf) return;
+ for(s = 0; s < samples; s++) {
+ sint32 diff_l = buf_l[s] - state->last_in_l;
+ sint32 diff_r = buf_r[s] - state->last_in_r;
+ state->last_in_l = buf_l[s];
+ state->last_in_r = buf_r[s];
+ l = ((state->last_out_l * 255) / 256) + diff_l;
+ r = ((state->last_out_r * 255) / 256) + diff_r;
+ state->last_out_l = l;
+ state->last_out_r = r;
+// l /= 2;
+// r /= 2;
+ l *= 8;
+ r *= 8;
+ if(l > ( 32767)) l = ( 32767);
+ if(l < (-32768)) l = (-32768);
+ if(r > ( 32767)) r = ( 32767);
+ if(r < (-32768)) r = (-32768);
+ *buf++ = l;
+ *buf++ = r;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void EMU_CALL _qmix_render(void *state, sint16 *buf, uint32 samples) {
+//printf("qmix render %u samples\n",samples);
+ for(; samples >= RENDERMAX; samples -= RENDERMAX) {
+ render(QMIXSTATE, buf, RENDERMAX);
+ if(buf) buf += 2 * RENDERMAX;
+ }
+ if(samples) {
+ render(QMIXSTATE, buf, samples);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void EMU_CALL _qmix_set_sample_rate(void *state, uint32 rate) {
+ if(rate < 1) rate = 1;
+ QMIXSTATE->pitchscaler = (65536 * 24000) / rate;
+}
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/Frameworks/GME/gme/qmix.h b/Frameworks/GME/gme/qmix.h
new file mode 100644
index 000000000..2ce3ebf8d
--- /dev/null
+++ b/Frameworks/GME/gme/qmix.h
@@ -0,0 +1,29 @@
+/////////////////////////////////////////////////////////////////////////////
+//
+// qmix - QSound mixer
+//
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef __Q_QMIX_H__
+#define __Q_QMIX_H__
+
+#include "emuconfig.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+sint32 EMU_CALL _qmix_init(void);
+uint32 EMU_CALL _qmix_get_state_size(void);
+void EMU_CALL _qmix_clear_state(void *state);
+
+void EMU_CALL _qmix_set_sample_rate(void *state, uint32 rate);
+void EMU_CALL _qmix_set_sample_rom(void *state, void *rom, uint32 size);
+void EMU_CALL _qmix_command(void *state, uint8 cmd, uint16 data);
+void EMU_CALL _qmix_render(void *state, sint16 *buf, uint32 samples);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Frameworks/GME/gme/rf5c68.c b/Frameworks/GME/gme/rf5c68.c
new file mode 100644
index 000000000..69286ee70
--- /dev/null
+++ b/Frameworks/GME/gme/rf5c68.c
@@ -0,0 +1,447 @@
+/*********************************************************/
+/* ricoh RF5C68(or clone) PCM controller */
+/*********************************************************/
+
+#include
+#include
+//#include "sndintrf.h"
+//#include "streams.h"
+#include "rf5c68.h"
+#include
+
+#define NULL ((void *)0)
+
+
+#define NUM_CHANNELS (8)
+#define WRITES_PER_SAMPLE 12
+
+
+
+typedef struct _pcm_channel pcm_channel;
+struct _pcm_channel
+{
+ UINT8 enable;
+ UINT8 env;
+ UINT8 pan;
+ UINT8 start;
+ UINT32 addr;
+ UINT16 step;
+ UINT16 loopst;
+ UINT8 Muted;
+};
+
+typedef struct _mem_stream mem_stream;
+struct _mem_stream
+{
+ UINT32 BaseAddr;
+ UINT32 EndAddr;
+ UINT32 CurAddr;
+ const UINT8* MemPnt;
+};
+
+
+typedef struct _rf5c68_state rf5c68_state;
+struct _rf5c68_state
+{
+ //sound_stream * stream;
+ pcm_channel chan[NUM_CHANNELS];
+ UINT8 cbank;
+ UINT8 wbank;
+ UINT8 enable;
+ UINT32 datasize;
+ UINT8* data;
+ //void (*sample_callback)(running_device* device,int channel);
+ mem_stream memstrm;
+};
+
+
+static void rf5c68_mem_stream_flush(rf5c68_state *chip);
+
+/*INLINE rf5c68_state *get_safe_token(const device_config *device)
+{
+ assert(device != NULL);
+ assert(device->token != NULL);
+ assert(device->type == SOUND);
+ assert(sound_get_type(device) == SOUND_RF5C68);
+ return (rf5c68_state *)device->token;
+}*/
+
+/************************************************/
+/* RF5C68 stream update */
+/************************************************/
+
+static void memstream_sample_check(rf5c68_state *chip, UINT32 addr)
+{
+ mem_stream* ms = &chip->memstrm;
+
+ if (addr >= ms->CurAddr)
+ {
+ if (addr - ms->CurAddr <= WRITES_PER_SAMPLE * 5)
+ {
+ ms->CurAddr -= WRITES_PER_SAMPLE * 2;
+ if (ms->CurAddr < ms->BaseAddr)
+ ms->CurAddr = ms->BaseAddr;
+ }
+ }
+ else
+ {
+ if (ms->CurAddr - addr <= WRITES_PER_SAMPLE * 4)
+ {
+ rf5c68_mem_stream_flush(chip);
+ }
+ }
+
+ return;
+}
+
+//static STREAM_UPDATE( rf5c68_update )
+void rf5c68_update(void *_chip, stream_sample_t **outputs, int samples)
+{
+ //rf5c68_state *chip = (rf5c68_state *)param;
+ rf5c68_state *chip = (rf5c68_state *) _chip;
+ mem_stream* ms = &chip->memstrm;
+ stream_sample_t *left = outputs[0];
+ stream_sample_t *right = outputs[1];
+ int i, j;
+
+ /* start with clean buffers */
+ memset(left, 0, samples * sizeof(*left));
+ memset(right, 0, samples * sizeof(*right));
+
+ /* bail if not enabled */
+ if (!chip->enable)
+ return;
+
+ /* loop over channels */
+ for (i = 0; i < NUM_CHANNELS; i++)
+ {
+ pcm_channel *chan = &chip->chan[i];
+
+ /* if this channel is active, accumulate samples */
+ if (chan->enable && ! chan->Muted)
+ {
+ int lv = (chan->pan & 0x0f) * chan->env;
+ int rv = ((chan->pan >> 4) & 0x0f) * chan->env;
+
+ /* loop over the sample buffer */
+ for (j = 0; j < samples; j++)
+ {
+ int sample;
+
+ /* trigger sample callback */
+ /*if(chip->sample_callback)
+ {
+ if(((chan->addr >> 11) & 0xfff) == 0xfff)
+ chip->sample_callback(chip->device,((chan->addr >> 11)/0x2000));
+ }*/
+
+ memstream_sample_check(chip, (chan->addr >> 11) & 0xffff);
+ /* fetch the sample and handle looping */
+ sample = chip->data[(chan->addr >> 11) & 0xffff];
+ if (sample == 0xff)
+ {
+ chan->addr = chan->loopst << 11;
+ sample = chip->data[(chan->addr >> 11) & 0xffff];
+
+ /* if we loop to a loop point, we're effectively dead */
+ if (sample == 0xff)
+ break;
+ }
+ chan->addr += chan->step;
+
+ /* add to the buffer */
+ if (sample & 0x80)
+ {
+ sample &= 0x7f;
+ left[j] += (sample * lv) >> 5;
+ right[j] += (sample * rv) >> 5;
+ }
+ else
+ {
+ left[j] -= (sample * lv) >> 5;
+ right[j] -= (sample * rv) >> 5;
+ }
+ }
+ }
+ }
+
+ if (samples && ms->CurAddr < ms->EndAddr)
+ {
+ i = WRITES_PER_SAMPLE * samples;
+ if (ms->CurAddr + i > ms->EndAddr)
+ i = ms->EndAddr - ms->CurAddr;
+
+ memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), i);
+ ms->CurAddr += i;
+ }
+
+ // I think, this is completely useless
+ /* now clamp and shift the result (output is only 10 bits) */
+ /*for (j = 0; j < samples; j++)
+ {
+ stream_sample_t temp;
+
+ temp = left[j];
+ if (temp > 32767) temp = 32767;
+ else if (temp < -32768) temp = -32768;
+ left[j] = temp & ~0x3f;
+
+ temp = right[j];
+ if (temp > 32767) temp = 32767;
+ else if (temp < -32768) temp = -32768;
+ right[j] = temp & ~0x3f;
+ }*/
+}
+
+
+/************************************************/
+/* RF5C68 start */
+/************************************************/
+
+//static DEVICE_START( rf5c68 )
+void * device_start_rf5c68()
+{
+ //const rf5c68_interface* intf = (const rf5c68_interface*)device->baseconfig().static_config();
+
+ /* allocate memory for the chip */
+ //rf5c68_state *chip = get_safe_token(device);
+ rf5c68_state *chip;
+ int chn;
+
+ chip = (rf5c68_state *) malloc(sizeof(rf5c68_state));
+ if (!chip) return chip;
+
+ chip->datasize = 0x10000;
+ chip->data = (UINT8*)malloc(chip->datasize);
+
+ /* allocate the stream */
+ //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update);
+
+ /* set up callback */
+ /*if(intf != NULL)
+ chip->sample_callback = intf->sample_end_callback;
+ else
+ chip->sample_callback = NULL;*/
+ for (chn = 0; chn < NUM_CHANNELS; chn ++)
+ chip->chan[chn].Muted = 0x00;
+
+ return chip;
+}
+
+void device_stop_rf5c68(void *_chip)
+{
+ rf5c68_state *chip = (rf5c68_state *) _chip;
+ free(chip->data); chip->data = NULL;
+ free(chip);
+}
+
+void device_reset_rf5c68(void *_chip)
+{
+ rf5c68_state *chip = (rf5c68_state *) _chip;
+ int i;
+ pcm_channel* chan;
+ mem_stream* ms = &chip->memstrm;
+
+ // Clear the PCM memory.
+ memset(chip->data, 0x00, chip->datasize);
+
+ chip->enable = 0;
+ chip->cbank = 0;
+ chip->wbank = 0;
+
+ /* clear channel registers */
+ for (i = 0; i < NUM_CHANNELS; i ++)
+ {
+ chan = &chip->chan[i];
+ chan->enable = 0;
+ chan->env = 0;
+ chan->pan = 0;
+ chan->start = 0;
+ chan->addr = 0;
+ chan->step = 0;
+ chan->loopst = 0;
+ }
+
+ ms->BaseAddr = 0x0000;
+ ms->CurAddr = 0x0000;
+ ms->EndAddr = 0x0000;
+ ms->MemPnt = NULL;
+}
+
+/************************************************/
+/* RF5C68 write register */
+/************************************************/
+
+//WRITE8_DEVICE_HANDLER( rf5c68_w )
+void rf5c68_w(void *_chip, offs_t offset, UINT8 data)
+{
+ //rf5c68_state *chip = get_safe_token(device);
+ rf5c68_state *chip = (rf5c68_state *) _chip;
+ pcm_channel *chan = &chip->chan[chip->cbank];
+ int i;
+
+ /* force the stream to update first */
+ //stream_update(chip->stream);
+
+ /* switch off the address */
+ switch (offset)
+ {
+ case 0x00: /* envelope */
+ chan->env = data;
+ break;
+
+ case 0x01: /* pan */
+ chan->pan = data;
+ break;
+
+ case 0x02: /* FDL */
+ chan->step = (chan->step & 0xff00) | (data & 0x00ff);
+ break;
+
+ case 0x03: /* FDH */
+ chan->step = (chan->step & 0x00ff) | ((data << 8) & 0xff00);
+ break;
+
+ case 0x04: /* LSL */
+ chan->loopst = (chan->loopst & 0xff00) | (data & 0x00ff);
+ break;
+
+ case 0x05: /* LSH */
+ chan->loopst = (chan->loopst & 0x00ff) | ((data << 8) & 0xff00);
+ break;
+
+ case 0x06: /* ST */
+ chan->start = data;
+ if (!chan->enable)
+ chan->addr = chan->start << (8 + 11);
+ break;
+
+ case 0x07: /* control reg */
+ chip->enable = (data >> 7) & 1;
+ if (data & 0x40)
+ chip->cbank = data & 7;
+ else
+ chip->wbank = data & 15;
+ break;
+
+ case 0x08: /* channel on/off reg */
+ for (i = 0; i < 8; i++)
+ {
+ chip->chan[i].enable = (~data >> i) & 1;
+ if (!chip->chan[i].enable)
+ chip->chan[i].addr = chip->chan[i].start << (8 + 11);
+ }
+ break;
+ }
+}
+
+
+/************************************************/
+/* RF5C68 read memory */
+/************************************************/
+
+//READ8_DEVICE_HANDLER( rf5c68_mem_r )
+UINT8 rf5c68_mem_r(void *_chip, offs_t offset)
+{
+ //rf5c68_state *chip = get_safe_token(device);
+ rf5c68_state *chip = (rf5c68_state *) _chip;
+ return chip->data[chip->wbank * 0x1000 + offset];
+}
+
+
+/************************************************/
+/* RF5C68 write memory */
+/************************************************/
+
+//WRITE8_DEVICE_HANDLER( rf5c68_mem_w )
+void rf5c68_mem_w(void *_chip, offs_t offset, UINT8 data)
+{
+ //rf5c68_state *chip = get_safe_token(device);
+ rf5c68_state *chip = (rf5c68_state *) _chip;
+ rf5c68_mem_stream_flush(chip);
+ chip->data[chip->wbank * 0x1000 | offset] = data;
+}
+
+static void rf5c68_mem_stream_flush(rf5c68_state *chip)
+{
+ mem_stream* ms = &chip->memstrm;
+
+ if (ms->CurAddr >= ms->EndAddr)
+ return;
+
+ memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), ms->EndAddr - ms->CurAddr);
+ ms->CurAddr = ms->EndAddr;
+
+ return;
+}
+
+void rf5c68_write_ram(void *_chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData)
+{
+ rf5c68_state *chip = (rf5c68_state *) _chip;
+ mem_stream* ms = &chip->memstrm;
+ UINT16 BytCnt;
+
+ if (DataStart >= chip->datasize)
+ return;
+ if (DataStart + DataLength > chip->datasize)
+ DataLength = chip->datasize - DataStart;
+
+ //memcpy(chip->data + (chip->wbank * 0x1000 | DataStart), RAMData, DataLength);
+
+ rf5c68_mem_stream_flush(chip);
+
+ ms->BaseAddr = chip->wbank * 0x1000 | DataStart;
+ ms->CurAddr = ms->BaseAddr;
+ ms->EndAddr = ms->BaseAddr + DataLength;
+ ms->MemPnt = RAMData;
+
+ BytCnt = WRITES_PER_SAMPLE;
+ if (ms->CurAddr + BytCnt > ms->EndAddr)
+ BytCnt = ms->EndAddr - ms->CurAddr;
+
+ memcpy(chip->data + ms->CurAddr, ms->MemPnt + (ms->CurAddr - ms->BaseAddr), BytCnt);
+ ms->CurAddr += BytCnt;
+
+ return;
+}
+
+
+void rf5c68_set_mute_mask(void *_chip, UINT32 MuteMask)
+{
+ rf5c68_state *chip = (rf5c68_state *) _chip;
+ unsigned char CurChn;
+
+ for (CurChn = 0; CurChn < NUM_CHANNELS; CurChn ++)
+ chip->chan[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
+
+ return;
+}
+
+
+
+/**************************************************************************
+ * Generic get_info
+ **************************************************************************/
+
+/*DEVICE_GET_INFO( rf5c68 )
+{
+ switch (state)
+ {
+ // --- the following bits of info are returned as 64-bit signed integers ---
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(rf5c68_state); break;
+
+ // --- the following bits of info are returned as pointers to data or functions ---
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( rf5c68 ); break;
+ case DEVINFO_FCT_STOP: // Nothing break;
+ case DEVINFO_FCT_RESET: // Nothing break;
+
+ // --- the following bits of info are returned as NULL-terminated strings ---
+ case DEVINFO_STR_NAME: strcpy(info->s, "RF5C68"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Ricoh PCM"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
+ }
+}*/
+
+/**************** end of file ****************/
diff --git a/Frameworks/GME/gme/rf5c68.h b/Frameworks/GME/gme/rf5c68.h
new file mode 100644
index 000000000..c2f3d07dc
--- /dev/null
+++ b/Frameworks/GME/gme/rf5c68.h
@@ -0,0 +1,38 @@
+/*********************************************************/
+/* ricoh RF5C68(or clone) PCM controller */
+/*********************************************************/
+
+#pragma once
+
+#include "mamedef.h"
+
+/******************************************/
+/*WRITE8_DEVICE_HANDLER( rf5c68_w );
+
+READ8_DEVICE_HANDLER( rf5c68_mem_r );
+WRITE8_DEVICE_HANDLER( rf5c68_mem_w );
+
+DEVICE_GET_INFO( rf5c68 );
+#define SOUND_RF5C68 DEVICE_GET_INFO_NAME( rf5c68 )*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void rf5c68_update(void *chip, stream_sample_t **outputs, int samples);
+
+void * device_start_rf5c68();
+void device_stop_rf5c68(void *chip);
+void device_reset_rf5c68(void *chip);
+
+void rf5c68_w(void *chip, offs_t offset, UINT8 data);
+
+UINT8 rf5c68_mem_r(void *chip, offs_t offset);
+void rf5c68_mem_w(void *chip, offs_t offset, UINT8 data);
+void rf5c68_write_ram(void *chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData);
+
+void rf5c68_set_mute_mask(void *chip, UINT32 MuteMask);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Frameworks/GME/gme/s_deltat.c b/Frameworks/GME/gme/s_deltat.c
new file mode 100644
index 000000000..8684005b3
--- /dev/null
+++ b/Frameworks/GME/gme/s_deltat.c
@@ -0,0 +1,281 @@
+#include "kmsnddev.h"
+#include "divfix.h"
+#include "s_logtbl.h"
+#include "s_deltat.h"
+#include
+
+#define CPS_SHIFT 16
+#define PHASE_SHIFT 16 /* 16(fix) */
+
+typedef struct {
+ KMIF_SOUND_DEVICE kmif;
+ KMIF_LOGTABLE *logtbl;
+ struct YMDELTATPCMSOUND_COMMON_TAG {
+ Int32 mastervolume;
+ Int32 step;
+ Int32 output;
+ Uint32 cnt;
+ Uint32 cps;
+ Uint32 phase;
+ Uint32 deltan;
+ Uint32 scale;
+ Uint32 mem;
+ Uint32 play;
+ Uint32 start;
+ Uint32 stop;
+ Int32 level32;
+ Uint8 key;
+ Uint8 level;
+ Uint8 granuality;
+ Uint8 pad4_3;
+ Uint8 regs[0x10];
+ } common;
+ Uint8 *romrambuf;
+ Uint32 romrammask;
+ Uint8 *rambuf;
+ Uint32 rammask;
+ Uint8 *rombuf;
+ Uint32 rommask;
+ Uint8 ymdeltatpcm_type;
+ Uint8 memshift;
+} YMDELTATPCMSOUND;
+
+static Uint8 const table_step[8] =
+{
+ 1, 3, 5, 7, 9, 11, 13, 15,
+};
+static Uint8 const table_scale[16] =
+{
+ 57, 57, 57, 57, 77, 102, 128, 153,
+ 57, 57, 57, 57, 77, 102, 128, 153,
+};
+
+__inline static void writeram(YMDELTATPCMSOUND *sndp, Uint32 v)
+{
+ sndp->rambuf[(sndp->common.mem >> 1) & sndp->rammask] = v;
+ sndp->common.mem += 1 << 1;
+}
+
+__inline static Uint32 readram(YMDELTATPCMSOUND *sndp)
+{
+ Uint32 v;
+ v = sndp->romrambuf[(sndp->common.play >> 1) & sndp->romrammask];
+ if (sndp->common.play & 1)
+ v &= 0x0F;
+ else
+ v >>= 4;
+ sndp->common.play += 1;
+ if (sndp->common.play >= sndp->common.stop)
+ {
+ if (sndp->common.regs[0] & 0x10)
+ {
+ sndp->common.play = sndp->common.start;
+ sndp->common.step = 0;
+ sndp->common.scale = 127;
+ }
+ else
+ {
+ sndp->common.key = 0;
+ }
+ }
+ return v;
+}
+
+__inline static void DelrtatStep(YMDELTATPCMSOUND *sndp, Uint32 data)
+{
+ if (data & 8)
+ sndp->common.step -= (table_step[data & 7] * sndp->common.scale) >> 3;
+ else
+ sndp->common.step += (table_step[data & 7] * sndp->common.scale) >> 3;
+ if (sndp->common.step > ((1 << 15) - 1)) sndp->common.step = ((1 << 15) - 1);
+ if (sndp->common.step < -(1 << 15)) sndp->common.step = -(1 << 15);
+ sndp->common.scale = (sndp->common.scale * table_scale[data]) >> 6;
+ if (sndp->common.scale > 24576) sndp->common.scale = 24576;
+ if (sndp->common.scale < 127) sndp->common.scale = 127;
+}
+
+#if (((-1) >> 1) == -1)
+#define SSR(x, y) (((Int32)x) >> (y))
+#else
+#define SSR(x, y) (((x) >= 0) ? ((x) >> (y)) : (-((-(x) - 1) >> (y)) - 1))
+#endif
+
+static int sndsynth(YMDELTATPCMSOUND *sndp )
+{
+ if (!sndp->common.key)
+ return 0;
+ {
+ Uint32 step;
+ sndp->common.cnt += sndp->common.cps;
+ step = sndp->common.cnt >> CPS_SHIFT;
+ sndp->common.cnt &= (1 << CPS_SHIFT) - 1;
+ sndp->common.phase += step * sndp->common.deltan;
+ step = sndp->common.phase >> PHASE_SHIFT;
+ sndp->common.phase &= (1 << PHASE_SHIFT) - 1;
+ if (step)
+ {
+ do
+ {
+ DelrtatStep(sndp, readram(sndp));
+ } while (--step);
+ sndp->common.output = sndp->common.step * sndp->common.level32;
+ sndp->common.output = SSR(sndp->common.output, 8 + 2);
+ }
+ }
+ return sndp->common.output;
+}
+
+
+
+static void sndwrite(YMDELTATPCMSOUND *sndp, Uint32 a, Uint32 v)
+{
+ sndp->common.regs[a] = v;
+ switch (a)
+ {
+ /* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */
+ case 0x00: /* Control Register 1 */
+ if ((v & 0x80) && !sndp->common.key)
+ {
+ sndp->common.key = 1;
+ sndp->common.play = sndp->common.start;
+ sndp->common.step = 0;
+ sndp->common.scale = 127;
+ }
+ if (v & 1) sndp->common.key = 0;
+ break;
+ /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */
+ case 0x01: /* Control Register 2 */
+ sndp->romrambuf = (sndp->common.regs[1] & 1) ? sndp->rombuf : sndp->rambuf;
+ sndp->romrammask = (sndp->common.regs[1] & 1) ? sndp->rommask : sndp->rammask;
+ break;
+ case 0x02: /* Start Address L */
+ case 0x03: /* Start Address H */
+ sndp->common.granuality = (v & 2) ? 1 : 4;
+ sndp->common.start = ((sndp->common.regs[3] << 8) + sndp->common.regs[2]) << (sndp->memshift + 1);
+ sndp->common.mem = sndp->common.start;
+ break;
+ case 0x04: /* Stop Address L */
+ case 0x05: /* Stop Address H */
+ sndp->common.stop = ((sndp->common.regs[5] << 8) + sndp->common.regs[4]) << (sndp->memshift + 1);
+ break;
+ case 0x06: /* Prescale L */
+ case 0x07: /* Prescale H */
+ break;
+ case 0x08: /* Data */
+ if ((sndp->common.regs[0] & 0x60) == 0x60) writeram(sndp, v);
+ break;
+ case 0x09: /* Delta-N L */
+ case 0x0A: /* Delta-N H */
+ sndp->common.deltan = (sndp->common.regs[0xA] << 8) + sndp->common.regs[0x9];
+ if (sndp->common.deltan < 0x100) sndp->common.deltan = 0x100;
+ break;
+ case 0x0B: /* Level Control */
+ sndp->common.level = v;
+ sndp->common.level32 = ((Int32)(sndp->common.level * LogToLin(sndp->logtbl, sndp->common.mastervolume, LOG_LIN_BITS - 15))) >> 7;
+ sndp->common.output = sndp->common.step * sndp->common.level32;
+ sndp->common.output = SSR(sndp->common.output, 8 + 2);
+ break;
+ }
+}
+
+static Uint32 sndread(YMDELTATPCMSOUND *sndp, Uint32 a)
+{
+ return 0;
+}
+
+static void sndreset(YMDELTATPCMSOUND *sndp, Uint32 clock, Uint32 freq)
+{
+ XMEMSET(&sndp->common, 0, sizeof(sndp->common));
+ sndp->common.cps = DivFix(clock, 72 * freq, CPS_SHIFT);
+ sndp->romrambuf = (sndp->common.regs[1] & 1) ? sndp->rombuf : sndp->rambuf;
+ sndp->romrammask = (sndp->common.regs[1] & 1) ? sndp->rommask : sndp->rammask;
+ sndp->common.granuality = 4;
+}
+
+static void sndvolume(YMDELTATPCMSOUND *sndp, Int32 volume)
+{
+ volume = (volume << (LOG_BITS - 8)) << 1;
+ sndp->common.mastervolume = volume;
+ sndp->common.level32 = ((Int32)(sndp->common.level * LogToLin(sndp->logtbl, sndp->common.mastervolume, LOG_LIN_BITS - 15))) >> 7;
+ sndp->common.output = sndp->common.step * sndp->common.level32;
+ sndp->common.output = SSR(sndp->common.output, 8 + 2);
+}
+
+static void sndrelease(YMDELTATPCMSOUND *sndp)
+{
+ if (sndp->logtbl) sndp->logtbl->release(sndp->logtbl->ctx);
+ XFREE(sndp);
+}
+
+static void setinst(YMDELTATPCMSOUND *sndp, Uint32 n, void *p, Uint32 l)
+{
+ if (n) return;
+ if (p)
+ {
+ sndp->rombuf = (Uint8*) p;
+ sndp->rommask = l - 1;
+ sndp->romrambuf = (sndp->common.regs[1] & 1) ? sndp->rombuf : sndp->rambuf;
+ sndp->romrammask = (sndp->common.regs[1] & 1) ? sndp->rommask : sndp->rammask;
+ }
+ else
+ {
+ sndp->rombuf = 0;
+ sndp->rommask = 0;
+ }
+
+}
+
+KMIF_SOUND_DEVICE *YMDELTATPCMSoundAlloc(Uint32 ymdeltatpcm_type)
+{
+ Uint32 ram_size;
+ YMDELTATPCMSOUND *sndp;
+ switch (ymdeltatpcm_type)
+ {
+ case YMDELTATPCM_TYPE_Y8950:
+ ram_size = 32 * 1024;
+ break;
+ case YMDELTATPCM_TYPE_YM2608:
+ ram_size = 256 * 1024;
+ break;
+ default:
+ ram_size = 0;
+ break;
+ }
+ sndp = (YMDELTATPCMSOUND*) XMALLOC(sizeof(YMDELTATPCMSOUND) + ram_size);
+ if (!sndp) return 0;
+ sndp->ymdeltatpcm_type = ymdeltatpcm_type;
+ switch (ymdeltatpcm_type)
+ {
+ case YMDELTATPCM_TYPE_Y8950:
+ sndp->memshift = 2;
+ break;
+ case YMDELTATPCM_TYPE_YM2608:
+ /* OPNA */
+ sndp->memshift = 6;
+ break;
+ case YMDELTATPCM_TYPE_YM2610:
+ sndp->memshift = 9;
+ break;
+ }
+ sndp->kmif.ctx = sndp;
+ sndp->kmif.release = (void (*)( void* )) sndrelease;
+ sndp->kmif.synth = (int (*)( void* )) sndsynth;
+ sndp->kmif.volume = (void (*)( void*, int )) sndvolume;
+ sndp->kmif.reset = (void (*)( void*, Uint32, Uint32 )) sndreset;
+ sndp->kmif.write = (void (*)( void*, Uint32, Uint32 )) sndwrite;
+ sndp->kmif.read = (Uint32 (*)( void*, Uint32 )) sndread;
+ sndp->kmif.setinst = (void (*)( void*, Uint32, void*, Uint32 )) setinst;
+ /* RAM */
+ sndp->rambuf = ram_size ? (Uint8 *)(sndp + 1) : 0;
+ sndp->rammask = ram_size ? (ram_size - 1) : 0;
+ /* ROM */
+ sndp->rombuf = 0;
+ sndp->rommask = 0;
+ sndp->logtbl = LogTableAddRef();
+ if (!sndp->logtbl)
+ {
+ sndrelease(sndp);
+ return 0;
+ }
+ return &sndp->kmif;
+}
diff --git a/Frameworks/GME/gme/s_deltat.h b/Frameworks/GME/gme/s_deltat.h
new file mode 100644
index 000000000..89c9242c8
--- /dev/null
+++ b/Frameworks/GME/gme/s_deltat.h
@@ -0,0 +1,23 @@
+#ifndef S_DELTAT_H__
+#define S_DELTAT_H__
+
+#include "kmsnddev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ /* MSX-AUDIO */ YMDELTATPCM_TYPE_Y8950,
+ /* OPNA ADPCM */ YMDELTATPCM_TYPE_YM2608,
+ /* OPNB ADPCMB */ YMDELTATPCM_TYPE_YM2610
+};
+
+KMIF_SOUND_DEVICE *YMDELTATPCMSoundAlloc(Uint32 ymdeltatpcm_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* S_DELTAT_H__ */
+
diff --git a/Frameworks/GME/gme/s_logtbl.c b/Frameworks/GME/gme/s_logtbl.c
new file mode 100644
index 000000000..0f455f2b7
--- /dev/null
+++ b/Frameworks/GME/gme/s_logtbl.c
@@ -0,0 +1,88 @@
+#include "nestypes.h"
+#include "s_logtbl.h"
+
+#if STATIC_TABLES
+
+static void LogTableRelease(void *ctx)
+{
+}
+
+static KMIF_LOGTABLE log_static_tables = {
+ &log_static_tables;
+ LogTableRelease,
+#include "s_logt.h"
+};
+
+
+KMIF_LOGTABLE *LogTableAddRef(void)
+{
+ log_static_tables.release = LogTableRelease;
+ return &log_static_tables;
+}
+
+#else
+
+#include
+
+static volatile Uint32 log_tables_mutex = 0;
+static Uint32 log_tables_refcount = 0;
+static KMIF_LOGTABLE *log_tables = 0;
+
+static void LogTableRelease(void *ctx)
+{
+ ++log_tables_mutex;
+ while (log_tables_mutex != 1)
+ {
+ XSLEEP(0);
+ }
+ log_tables_refcount--;
+ if (!log_tables_refcount)
+ {
+ XFREE(ctx);
+ log_tables = 0;
+ }
+ --log_tables_mutex;
+}
+
+static void LogTableCalc(KMIF_LOGTABLE *kmif_lt)
+{
+ Uint32 i;
+ double a;
+ for (i = 0; i < (1 << LOG_BITS); i++)
+ {
+ a = (1 << LOG_LIN_BITS) / pow(2, i / (double)(1 << LOG_BITS));
+ kmif_lt->logtbl[i] = (Uint32)a;
+ }
+ kmif_lt->lineartbl[0] = LOG_LIN_BITS << LOG_BITS;
+ for (i = 1; i < (1 << LIN_BITS) + 1; i++)
+ {
+ Uint32 ua;
+ a = i << (LOG_LIN_BITS - LIN_BITS);
+ ua = (Uint32)((LOG_LIN_BITS - (log(a) / log(2))) * (1 << LOG_BITS));
+ kmif_lt->lineartbl[i] = ua << 1;
+ }
+}
+
+KMIF_LOGTABLE *LogTableAddRef(void)
+{
+ ++log_tables_mutex;
+ while (log_tables_mutex != 1)
+ {
+ XSLEEP(0);
+ }
+ if (!log_tables_refcount)
+ {
+ log_tables = (KMIF_LOGTABLE*) XMALLOC(sizeof(KMIF_LOGTABLE));
+ if (log_tables)
+ {
+ log_tables->ctx = log_tables;
+ log_tables->release = LogTableRelease;
+ LogTableCalc(log_tables);
+ }
+ }
+ if (log_tables) log_tables_refcount++;
+ --log_tables_mutex;
+ return log_tables;
+}
+
+#endif
diff --git a/Frameworks/GME/gme/s_logtbl.h b/Frameworks/GME/gme/s_logtbl.h
new file mode 100644
index 000000000..3abc79c11
--- /dev/null
+++ b/Frameworks/GME/gme/s_logtbl.h
@@ -0,0 +1,43 @@
+#ifndef S_LOGTBL_H__
+#define S_LOGTBL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LOG_BITS 12
+#define LIN_BITS 7
+#define LOG_LIN_BITS 30
+
+typedef struct
+{
+ void *ctx;
+ void (*release)(void *ctx);
+ Uint32 lineartbl[(1 << LIN_BITS) + 1];
+ Uint32 logtbl[1 << LOG_BITS];
+} KMIF_LOGTABLE;
+
+KMIF_LOGTABLE *LogTableAddRef(void);
+
+__inline static Uint32 LinToLog(KMIF_LOGTABLE *kmif_lt, Int32 l)
+{
+ return (l < 0) ? (kmif_lt->lineartbl[-l] + 1) : kmif_lt->lineartbl[l];
+}
+
+__inline static Int32 LogToLin(KMIF_LOGTABLE *kmif_lt, Int32 l, Uint32 sft)
+{
+ Int32 ret;
+ Uint32 ofs;
+ ofs = l + (sft << (LOG_BITS + 1));
+ sft = ofs >> (LOG_BITS + 1);
+ if (sft >= LOG_LIN_BITS) return 0;
+ ofs = (ofs >> 1) & ((1 << LOG_BITS) - 1);
+ ret = kmif_lt->logtbl[ofs] >> sft;
+ return (l & 1) ? -ret : ret;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* S_LOGTBL_H__ */
diff --git a/Frameworks/GME/gme/s_opl.c b/Frameworks/GME/gme/s_opl.c
new file mode 100644
index 000000000..83b33e6e8
--- /dev/null
+++ b/Frameworks/GME/gme/s_opl.c
@@ -0,0 +1,1244 @@
+/*
+ s_opl.c -- YM2413/Y8950/YM3526/YM3812 emulator by Mamiya, 2001.
+
+ References:
+ fmopl.c -- 1999,2000 written by Tatsuyuki Satoh (MAME development).
+ fmopl.c(fixed) -- 2000 modified by mamiya (NEZplug development).
+ fmgen.cpp -- 1999-2001 written by cisc.
+ emu2413.c -- a YM2413 emulator : written by Mitsutaka Okazaki 2001
+ fmpac.ill -- 2000 created by sama.
+ fmpac.ill -- 2000 created by NARUTO.
+ fmpac.ill -- 2001 created by Okazaki.
+ YM2413 application manual
+*/
+
+#include "kmsnddev.h"
+#include "divfix.h"
+#include "s_logtbl.h"
+#include "s_opltbl.h"
+#include "s_opl.h"
+#include "s_deltat.h"
+#include
+
+#define PG_SHIFT 10 /* fix */
+#define CPS_SHIFTE 20
+#define CPS_SHIFTP 14
+#define LFO_SHIFT 16
+
+#define OPLL_INST_WORK 0x40
+#define OPLL_INST_WORK2 (OPLL_INST_WORK + 8 * 0x13)
+
+#define AR_BITS 6 /* fix */
+#define AR_SHIFT 14 /* fix */
+#define EG_SHIFT 15 /* fix */
+
+#define AR_PHASEMAX (((1 << AR_BITS) - 1) << AR_SHIFT)
+#define EG_PHASEMAX (127 << EG_SHIFT)
+#define EG_KEYOFF (128 << EG_SHIFT)
+#define LOG_KEYOFF (31 << (LOG_BITS + 1))
+
+#if 0
+ 0-48dB
+ AR_BITS=6
+ AR_SHIFT=14
+ x = FC000h(20.7618(sec) * 3579545 / 72)cycles 63 * 4000h
+ x / 3 * 4 1730.15(ms)
+ x / 3 * 256 27.03(ms)
+
+ EG_BITS=7
+ EG_SHIFT=15
+ x = 3F8000h(83.7064(sec) * 3579545 / 72)cycles 127 * 8000h
+ x / 4 = 20926.6(ms)
+#endif
+
+
+#define TESTING_OPTIMIZE_AME 1
+#define USE_FBBUF 1
+
+typedef struct
+{
+ Uint32 phase;
+ Uint32 spd;
+ Uint32 rng;
+} OPL_PG;
+
+enum {
+ EG_MODE_ATTACK,
+ EG_MODE_DECAY,
+ EG_MODE_SUSTINE,
+ EG_MODE_RELEASE,
+ EG_MODE_NUM,
+ EG_MODE_SUSHOLD,
+ EG_MODE_OFF = EG_MODE_NUM
+};
+
+typedef struct
+{
+ Uint32 phasear;
+ Uint32 phase;
+ Uint32 spd [EG_MODE_NUM];
+ Uint32 dr_phasemax;
+ Uint8 mode;
+ Uint8 pad4_1;
+ Uint8 pad4_2;
+ Uint8 pad4_3;
+} OPL_EG;
+
+enum
+{
+ FLAG_AME = (1 << 0),
+ FLAG_PME = (1 << 1),
+ FLAG_EGT = (1 << 2),
+ FLAG_KSR = (1 << 3)
+};
+
+typedef struct
+{
+ OPL_PG pg;
+ OPL_EG eg;
+ Int32 input;
+#if USE_FBBUF
+ Int32 fbbuf;
+#endif
+#if TESTING_OPTIMIZE_AME
+ Uint32* amin;
+#endif
+ Uint32 tl_ofs;
+ Uint32* sintable;
+
+ Uint8 modcar; /* 1:m 0:c */
+ Uint8 fb;
+ Uint8 lvl;
+ Uint8 nst;
+
+ Uint8 tll;
+ Uint8 key;
+ Uint8 rkey;
+ Uint8 prevkey;
+
+ Uint8 enable;
+ Uint8 __enablehold__;
+ Uint8 flag;
+ Uint8 ksr;
+
+ Uint8 mul;
+ Uint8 ksl;
+ Uint8 ar;
+ Uint8 dr;
+ Uint8 sl;
+ Uint8 rr;
+ Uint8 tl;
+ Uint8 wf;
+} OPL_OP;
+
+typedef struct
+{
+ OPL_OP op [2];
+ Uint8 con;
+ Uint8 freql;
+ Uint8 freqh;
+ Uint8 blk;
+ Uint8 kcode;
+ Uint8 sus;
+ Uint8 ksb;
+ Uint8 pad4_3;
+} OPL_CH;
+
+enum
+{
+ LFO_UNIT_AM,
+ LFO_UNIT_PM,
+ LFO_UNIT_NUM
+};
+
+typedef struct
+{
+ Uint32 output;
+ Uint32 cnt;
+ Uint32 sps; /* step per sample */
+ Uint32 adr;
+ Uint32 adrmask;
+ Uint32* table;
+} OPL_LFO;
+
+typedef struct
+{
+ KMIF_SOUND_DEVICE kmif;
+ KMIF_SOUND_DEVICE* deltatpcm;
+ KMIF_LOGTABLE* logtbl;
+ KMIF_OPLTABLE* opltbl;
+ OPL_CH ch [9];
+ OPL_LFO lfo [LFO_UNIT_NUM];
+ struct OPLSOUND_COMMON_TAG
+ {
+ Uint32 cpsp;
+ Uint32 cnt;
+ Uint32* ar_table;
+ Uint32* tll2logtbl;
+#if TESTING_OPTIMIZE_AME
+ Uint32 amzero;
+#endif
+ Int32 mastervolume;
+ Uint32 sintablemask;
+ Uint32 ratetbla [4];
+ Uint32 ratetbl [4];
+ Uint8 adr;
+ Uint8 wfe;
+ Uint8 rc;
+ Uint8 rmode;
+ Uint8 enable;
+ } common;
+ Uint8 regs [0x100];
+ Uint8 opl_type;
+} OPLSOUND;
+
+static Uint8 romtone [3] [16 * 19] =
+{
+ {
+#include "i_fmpac.h"
+ },
+ {
+#include "i_fmunit.h"
+ },
+ {
+#include "i_vrc7.h"
+ },
+};
+
+static void SetOpOff(OPL_OP* opp )
+{
+ opp->eg.mode = EG_MODE_OFF;
+ opp->eg.phase = EG_KEYOFF;
+ opp->enable = 0;
+}
+
+inline static void EgStep( OPLSOUND* sndp, OPL_OP* opp )
+{
+ switch ( opp->eg.mode )
+ {
+ default:
+ NEVER_REACH
+
+ case EG_MODE_ATTACK:
+ opp->eg.phase = sndp->common.ar_table [opp->eg.phasear >> (AR_SHIFT + AR_BITS - ARTBL_BITS)] >> (ARTBL_SHIFT - EG_SHIFT);
+ opp->eg.phasear += opp->eg.spd [EG_MODE_ATTACK];
+ if ( opp->eg.phasear >= AR_PHASEMAX )
+ {
+ opp->eg.mode = EG_MODE_DECAY;
+ opp->eg.phase = 0;
+ }
+ break;
+
+ case EG_MODE_DECAY:
+ opp->eg.phase += opp->eg.spd [EG_MODE_DECAY];
+ if ( opp->eg.phase >= opp->eg.dr_phasemax )
+ {
+ opp->eg.phase = opp->eg.dr_phasemax;
+ opp->eg.mode = (opp->flag & FLAG_EGT) ? EG_MODE_SUSHOLD : EG_MODE_SUSTINE;
+ }
+ break;
+
+ case EG_MODE_SUSTINE:
+ case EG_MODE_RELEASE:
+ opp->eg.phase += opp->eg.spd [opp->eg.mode];
+ if ( opp->eg.phase >= EG_PHASEMAX )
+ SetOpOff(opp);
+ break;
+
+ case EG_MODE_SUSHOLD:
+ case EG_MODE_OFF:
+ break;
+ }
+}
+
+static void OpStep( OPLSOUND* sndp, OPL_OP* opp )
+{
+ int step;
+ EgStep(sndp, opp);
+ step = opp->pg.spd;
+ if ( opp->flag & FLAG_PME )
+ step = (step * sndp->lfo [LFO_UNIT_PM].output) >> PM_SHIFT;
+ opp->pg.phase += step;
+}
+
+__inline static void OpStepNG( OPLSOUND* sndp, OPL_OP* opp )
+{
+ Uint32 step;
+ EgStep(sndp, opp);
+ opp->pg.phase += opp->pg.spd;
+ step = opp->pg.phase >> opp->nst/*(PG_SHIFT + 5)*/;
+ opp->pg.phase &= (1 << opp->nst/*(PG_SHIFT + 5)*/) - 1;
+ while ( step-- )
+ {
+ opp->pg.rng ^= ((opp->pg.rng & 1) << 16) + ((opp->pg.rng & 1) << 13);
+ opp->pg.rng >>= 1;
+ }
+}
+
+#if -1 >> 1 == -1
+/* RIGHT SHIFT IS SIGNED */
+#define SSR(x, y) ((Int32)(x) >> (y))
+#else
+/* RIGHT SHIFT IS UNSIGNED */
+#define SSR(x, y) (((x) >= 0) ? ((x) >> (y)) : (-((-(x) - 1) >> (y)) - 1))
+#endif
+
+
+inline static void OpSynthMod( OPLSOUND* sndp, OPL_OP* opp )
+{
+ if ( opp->enable )
+ {
+ Uint32 tll;
+ Int32 output;
+ OpStep(sndp, opp);
+ tll = opp->tll + (opp->eg.phase >> EG_SHIFT);
+ tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll];
+ tll += opp->tl_ofs;
+#if TESTING_OPTIMIZE_AME
+ tll += *opp->amin;
+#else
+ if ( opp->flag & FLAG_AME )
+ tll += sndp->lfo [LFO_UNIT_AM].output;
+#endif
+ tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))];
+ output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2)));
+ if ( opp->fb )
+ {
+#if USE_FBBUF
+ Int32 fbtmp;
+ fbtmp = opp->fbbuf + output;
+ opp->fbbuf = output;
+ opp->input = SSR(fbtmp, (9 - opp->fb));
+#else
+ opp->input = SSR(output, (8 - opp->fb));
+#endif
+ }
+ opp [1].input = output;
+ }
+}
+
+inline static Int32 OpSynthCarFb( OPLSOUND* sndp, OPL_OP* opp )
+{
+ if ( opp->enable )
+ {
+ Uint32 tll;
+ OpStep(sndp, opp);
+ tll = opp->tll + (opp->eg.phase >> EG_SHIFT);
+ tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll];
+ tll += opp->tl_ofs;
+#if TESTING_OPTIMIZE_AME
+ tll +=* opp->amin;
+#else
+ if ( opp->flag & FLAG_AME) tll += sndp->lfo [LFO_UNIT_AM].output;
+#endif
+ tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))];
+ if ( opp->fb )
+ {
+#if USE_FBBUF
+ Int32 output, fbtmp;
+ output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2)));
+ fbtmp = opp->fbbuf + output;
+ opp->fbbuf = output;
+ opp->input = SSR(fbtmp, (9 - opp->fb));
+#else
+ Int32 output;
+ output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2)));
+ opp->input = SSR(output, (8 - opp->fb));
+#endif
+ }
+ return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl);
+ }
+ return 0;
+}
+
+inline static Int32 OpSynthCar( OPLSOUND* sndp, OPL_OP* opp )
+{
+ if ( opp->enable )
+ {
+ Uint32 tll;
+ OpStep(sndp, opp);
+ tll = opp->tll + (opp->eg.phase >> EG_SHIFT);
+ tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll];
+ tll += opp->tl_ofs;
+#if TESTING_OPTIMIZE_AME
+ tll += *opp->amin;
+#else
+ if ( opp->flag & FLAG_AME )
+ tll += sndp->lfo [LFO_UNIT_AM].output;
+#endif
+ tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))];
+ return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl);
+ }
+ return 0;
+}
+
+inline static Int32 OpSynthTom( OPLSOUND* sndp, OPL_OP* opp )
+{
+ if ( opp->enable )
+ {
+ Uint32 tll;
+ OpStep(sndp, opp);
+ tll = opp->tll + (opp->eg.phase >> EG_SHIFT);
+ tll = (tll >= 128 - 16) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll];
+ tll += opp->tl_ofs;
+ tll += opp->sintable [sndp->common.sintablemask & (opp->pg.phase >> PG_SHIFT)];
+ return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl);
+ }
+ return 0;
+}
+
+
+static Int32 OpSynthRym( OPLSOUND* sndp, OPL_OP* opp )
+{
+ if ( opp->enable )
+ {
+ Uint32 tll;
+ OpStepNG(sndp, opp);
+ tll = opp->tll + (opp->eg.phase >> EG_SHIFT) + 0x10/* +6dB */;
+ tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll];
+ tll += opp->tl_ofs;
+ tll += (opp->pg.rng & 1);
+ return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl);
+ }
+ return 0;
+}
+
+inline static void LfoStep(OPL_LFO* lfop )
+{
+ lfop->cnt += lfop->sps;
+ lfop->adr += lfop->cnt >> LFO_SHIFT;
+ lfop->cnt &= (1 << LFO_SHIFT) - 1;
+ lfop->output = lfop->table [lfop->adr & lfop->adrmask];
+}
+
+static int sndsynth( OPLSOUND* sndp )
+{
+ Int32 accum = 0;
+ if ( sndp->common.enable )
+ {
+ Uint32 i, rch;
+ for ( i = 0; i < LFO_UNIT_NUM; i++ )
+ LfoStep(&sndp->lfo [i]);
+
+ rch = sndp->common.rmode ? 7 : 9;
+ for ( i = 0; i < rch; i++ )
+ {
+ if ( sndp->ch [i].op [0].modcar )
+ OpSynthMod(sndp, &sndp->ch [i].op [0]);
+ else
+ accum += OpSynthCarFb(sndp, &sndp->ch [i].op [0]);
+ accum += OpSynthCar(sndp, &sndp->ch [i].op [1]);
+ }
+ if ( sndp->common.rmode )
+ {
+ accum += OpSynthRym(sndp, &sndp->ch [7].op [0]);
+ accum += OpSynthRym(sndp, &sndp->ch [7].op [1]);
+ accum += OpSynthTom(sndp, &sndp->ch [8].op [0]);
+ accum += OpSynthRym(sndp, &sndp->ch [8].op [1]);
+ }
+ }
+ if ( sndp->deltatpcm )
+ {
+ accum += sndp->deltatpcm->synth(sndp->deltatpcm->ctx);
+ }
+#if 0
+ /* NISE DAC */
+ if ( accum >= 0 )
+ accum = (Int32)(((Uint32) accum) & (((1 << 8) - 1) << (23 - 8)));
+ else
+ accum = -(Int32)(((Uint32)-accum) & (((1 << 8) - 1) << (23 - 8)));
+#endif
+ return accum;
+}
+
+static void sndvolume( OPLSOUND* sndp, Int32 volume )
+{
+ if ( sndp->deltatpcm) sndp->deltatpcm->volume(sndp->deltatpcm->ctx, volume);
+ volume = (volume << (LOG_BITS - 8)) << 1;
+ sndp->common.mastervolume = volume;
+}
+
+static Uint8 const op_table [0x20]=
+{
+ 0, 2, 4, 1, 3, 5,0xFF,0xFF,
+ 6, 8, 10, 7, 9, 11,0xFF,0xFF,
+ 12, 14, 16, 13, 15, 17,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+};
+
+static Uint8 const mul_table [0x10]=
+{
+ 1+0, 2+0, 4+0, 2+4, 8+0, 8+2, 8+4,16-2,
+ 16+0,16+2,16+4,16+4,16+8,16+8,32-2,32-2,
+};
+
+#define DB2TLL(x) (x * 2 / 375 )
+static Uint8 const ksl_table [8] [16]=
+{
+ {
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ },{
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 750), DB2TLL( 1125), DB2TLL( 1500),
+ DB2TLL( 1875), DB2TLL( 2250), DB2TLL( 2625), DB2TLL( 3000),
+ },{
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 1125), DB2TLL( 1875), DB2TLL( 2625),
+ DB2TLL( 3000), DB2TLL( 3750), DB2TLL( 4125), DB2TLL( 4500),
+ DB2TLL( 4875), DB2TLL( 5250), DB2TLL( 5625), DB2TLL( 6000),
+ },{
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 1875),
+ DB2TLL( 3000), DB2TLL( 4125), DB2TLL( 4875), DB2TLL( 5625),
+ DB2TLL( 6000), DB2TLL( 6750), DB2TLL( 7125), DB2TLL( 7500),
+ DB2TLL( 7875), DB2TLL( 8250), DB2TLL( 8625), DB2TLL( 9000),
+ },{
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 3000), DB2TLL( 4875),
+ DB2TLL( 6000), DB2TLL( 7125), DB2TLL( 7875), DB2TLL( 8625),
+ DB2TLL( 9000), DB2TLL( 9750), DB2TLL(10125), DB2TLL(10500),
+ DB2TLL(10875), DB2TLL(11250), DB2TLL(11625), DB2TLL(12000),
+ },{
+ DB2TLL( 0), DB2TLL( 3000), DB2TLL( 6000), DB2TLL( 7875),
+ DB2TLL( 9000), DB2TLL(10125), DB2TLL(10875), DB2TLL(11625),
+ DB2TLL(12000), DB2TLL(12750), DB2TLL(13125), DB2TLL(13500),
+ DB2TLL(13875), DB2TLL(14250), DB2TLL(14625), DB2TLL(15000),
+ },{
+ DB2TLL( 0), DB2TLL( 6000), DB2TLL( 9000), DB2TLL(10875),
+ DB2TLL(12000), DB2TLL(13125), DB2TLL(13875), DB2TLL(14625),
+ DB2TLL(15000), DB2TLL(15750), DB2TLL(16125), DB2TLL(16500),
+ DB2TLL(16875), DB2TLL(17250), DB2TLL(17625), DB2TLL(18000),
+ },{
+ DB2TLL( 0), DB2TLL( 9000), DB2TLL(12000), DB2TLL(13875),
+ DB2TLL(15000), DB2TLL(16125), DB2TLL(16875), DB2TLL(17625),
+ DB2TLL(18000), DB2TLL(18750), DB2TLL(19125), DB2TLL(19500),
+ DB2TLL(19875), DB2TLL(20250), DB2TLL(20625), DB2TLL(21000),
+ }
+};
+#undef DB2TLL
+
+static Uint32 rateconvAR( OPLSOUND* sndp, Uint32 rrr, Uint32 ksr )
+{
+ if ( !rrr )
+ return 0;
+ rrr = rrr + (ksr >> 2);
+ if ( rrr >= 15)
+ return AR_PHASEMAX;
+ return sndp->common.ratetbla [ksr & 3] >> (CPS_SHIFTE + 1 - rrr);
+}
+
+static Uint32 rateconv( OPLSOUND* sndp, Uint32 rrr, Uint32 ksr )
+{
+ if ( !rrr )
+ return 0;
+ rrr = rrr + (ksr >> 2);
+ if ( rrr > 15 )
+ rrr = 15;
+ return sndp->common.ratetbl [ksr & 3] >> (CPS_SHIFTE + 1 - rrr);
+}
+
+static void OpUpdateWF( OPLSOUND* sndp, OPL_OP* opp )
+{
+ opp->sintable = sndp->opltbl->sin_table [opp->wf & sndp->common.wfe];
+}
+
+static void OpUpdatePG( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp )
+{
+ opp->pg.spd = (((chp->freqh << 8) + chp->freql) * opp->mul * sndp->common.cpsp) >> (CPS_SHIFTP - chp->blk);
+}
+
+static void OpUpdateEG( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp )
+{
+ Uint32 sr, rr;
+ opp->ksr = chp->kcode >> ((opp->flag & FLAG_KSR) ? 0 : 2);
+ opp->eg.dr_phasemax = opp->sl << (1 + 2 + EG_SHIFT); /* 3dB->eg */
+ opp->eg.spd [EG_MODE_ATTACK] = rateconvAR(sndp, opp->ar, opp->ksr);
+ opp->eg.spd [EG_MODE_DECAY] = rateconv(sndp, opp->dr, opp->ksr);
+ if ( opp->flag & FLAG_EGT )
+ {
+ if ( opp->eg.mode == EG_MODE_SUSTINE )
+ opp->eg.mode = EG_MODE_SUSHOLD;
+ sr = 0;
+ rr = opp->rr;
+ }
+ else
+ {
+ if ( opp->eg.mode == EG_MODE_SUSHOLD )
+ opp->eg.mode = EG_MODE_SUSTINE;
+ sr = opp->rr;
+ rr = 7;
+ }
+ if ( chp->sus )
+ {
+ rr = 5;
+ }
+ opp->eg.spd [EG_MODE_SUSTINE] = rateconv(sndp, sr, opp->ksr);
+ opp->eg.spd [EG_MODE_RELEASE] = rateconv(sndp, rr, opp->ksr);
+}
+
+static void OpUpdateTLL( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp )
+{
+ opp->tll = (opp->tl + (chp->ksb >> opp->ksl)) << 1;
+}
+
+
+
+static void oplsetopmul( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->flag &= ~(FLAG_AME | FLAG_PME | FLAG_EGT | FLAG_KSR);
+#if TESTING_OPTIMIZE_AME
+ if ( v & 0x80 )
+ opp->amin = &sndp->lfo [LFO_UNIT_AM].output; else opp->amin = &sndp->common.amzero;
+#else
+ if ( v & 0x80 )
+ opp->flag |= FLAG_AME;
+#endif
+ if ( v & 0x40 ) opp->flag |= FLAG_PME;
+ if ( v & 0x20 ) opp->flag |= FLAG_EGT;
+ if ( v & 0x10 ) opp->flag |= FLAG_KSR;
+ opp->mul = mul_table [v & 0x0F];
+ OpUpdateEG(sndp, chp, opp);
+ OpUpdatePG(sndp, chp, opp);
+}
+
+static void oplsetopkstl( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->ksl = (v >> 6) ? (3 - (v >> 6)) : 15; /* 0 / 1.5 / 3 / 6 db/OCT */
+ opp->tl = v & 0x3F; /* 0.75 db */
+ OpUpdateTLL(sndp, chp, opp);
+}
+
+static void oplsetopardr( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->ar = v >> 4;
+ opp->dr = v & 0xF;
+ OpUpdateEG(sndp, chp, opp);
+}
+
+static void oplsetopslrr( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->sl = v >> 4;
+ opp->rr = v & 0xF;
+ OpUpdateEG(sndp, chp, opp);
+}
+
+static void oplsetopwf( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->wf = v & 0x3;
+ OpUpdateWF(sndp, opp);
+}
+
+static void oplsetopkey( OPLSOUND* sndp, OPL_OP* opp )
+{
+ Uint8 nextkey = ((sndp->common.rmode) && opp->rkey) || opp->key;
+ if ( opp->prevkey ^ nextkey )
+ {
+ opp->prevkey = nextkey;
+ if ( nextkey )
+ {
+ sndp->common.enable = 1;
+ opp->eg.mode = EG_MODE_ATTACK;
+ opp->eg.phase = EG_KEYOFF;
+ opp->enable = 1;
+ opp->eg.phasear = 0;
+ }
+ else if ( !opp->modcar && opp->eg.mode != EG_MODE_OFF )
+ opp->eg.mode = EG_MODE_RELEASE;
+ }
+}
+
+static void oplsetchfreql( OPLSOUND* sndp, OPL_CH* chp, Uint32 v )
+{
+ chp->freql = v & 0xFF;
+ chp->ksb = ksl_table [chp->blk] [(chp->freqh << 2) + (chp->freql >> 6)];
+ OpUpdatePG(sndp, chp, &chp->op [0]);
+ OpUpdatePG(sndp, chp, &chp->op [1]);
+ OpUpdateTLL(sndp, chp, &chp->op [0]);
+ OpUpdateTLL(sndp, chp, &chp->op [1]);
+}
+
+static void oplsetchfreqh( OPLSOUND* sndp, OPL_CH* chp, Uint32 v )
+{
+ Uint32 key = v & 0x20;
+ chp->kcode = (v >> 1) & 15;
+ chp->freqh = v & 3;
+ chp->blk = (v >> 2) & 7;
+ chp->op [0].key = chp->op [1].key = key;
+ oplsetopkey(sndp, &chp->op [0]);
+ oplsetopkey(sndp, &chp->op [1]);
+ chp->sus = 0;
+ chp->ksb = ksl_table [chp->blk] [(chp->freqh << 2) + (chp->freql >> 6)];
+ OpUpdateEG(sndp, chp, &chp->op [0]);
+ OpUpdateEG(sndp, chp, &chp->op [1]);
+ OpUpdatePG(sndp, chp, &chp->op [0]);
+ OpUpdatePG(sndp, chp, &chp->op [1]);
+ OpUpdateTLL(sndp, chp, &chp->op [0]);
+ OpUpdateTLL(sndp, chp, &chp->op [1]);
+}
+
+static void oplsetchfbcon( OPLSOUND* sndp, OPL_CH* chp, Uint32 v )
+{
+ chp->op [0].fb = (v >> 1) & 7;
+#if USE_FBBUF
+ chp->op [0].fbbuf = 0;
+#endif
+ chp->con = v & 1;
+ chp->op [0].modcar = (chp->con) ? 0 : 1;
+ OpUpdateEG(sndp, chp, &chp->op [0]);
+ chp->op [1].input = 0;
+}
+
+static void opllsetopvolume( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->tl = v;
+ OpUpdateTLL(sndp, chp, opp);
+}
+
+static void opllsetchfreql( OPLSOUND* sndp, OPL_CH* chp, Uint32 v )
+{
+ chp->freql = v & 0xFF;
+ chp->ksb = ksl_table [chp->blk] [(chp->freqh << 3) + (chp->freql >> 5)];
+ OpUpdatePG(sndp, chp, &chp->op [0]);
+ OpUpdatePG(sndp, chp, &chp->op [1]);
+ OpUpdateTLL(sndp, chp, &chp->op [0]);
+ OpUpdateTLL(sndp, chp, &chp->op [1]);
+}
+
+static void opllsetchfreqh( OPLSOUND* sndp, OPL_CH* chp, Uint32 v )
+{
+ Uint32 key = v & 0x10;
+ chp->kcode = v & 15;
+ chp->freqh = v & 1;
+ chp->blk = (v >> 1) & 7;
+ chp->op [0].key = chp->op [1].key = key;
+ oplsetopkey(sndp, &chp->op [0]);
+ oplsetopkey(sndp, &chp->op [1]);
+ chp->sus = v & 0x20;
+ chp->ksb = ksl_table [chp->blk] [(chp->freqh << 3) + (chp->freql >> 5)];
+ OpUpdateEG(sndp, chp, &chp->op [0]);
+ OpUpdateEG(sndp, chp, &chp->op [1]);
+ OpUpdatePG(sndp, chp, &chp->op [0]);
+ OpUpdatePG(sndp, chp, &chp->op [1]);
+ OpUpdateTLL(sndp, chp, &chp->op [0]);
+ OpUpdateTLL(sndp, chp, &chp->op [1]);
+}
+
+static void SetOpTone( OPLSOUND* sndp, OPL_OP* opp, Uint8* tonep )
+{
+ opp->flag &= ~(FLAG_AME | FLAG_PME | FLAG_EGT | FLAG_KSR);
+#if TESTING_OPTIMIZE_AME
+ if ( tonep [0] & 0x80 ) opp->amin = &sndp->lfo [LFO_UNIT_AM].output; else opp->amin = &sndp->common.amzero;
+#else
+ if ( tonep [0] & 0x80 ) opp->flag |= FLAG_AME;
+#endif
+ if ( tonep [0] & 0x40 ) opp->flag |= FLAG_PME;
+ if ( tonep [0] & 0x20 ) opp->flag |= FLAG_EGT;
+ if ( tonep [0] & 0x10 ) opp->flag |= FLAG_KSR;
+ opp->mul = mul_table [tonep [0] & 0x0F] << 1;
+ opp->ksl = (tonep [2] >> 6) ? (3 - (tonep [2] >> 6)) : 15;
+ opp->ar = tonep [4] >> 4;
+ opp->dr = tonep [4] & 0xF;
+ opp->sl = tonep [6] >> 4;
+ opp->rr = tonep [6] & 0xF;
+}
+static void SetChTone( OPLSOUND* sndp, OPL_CH* chp, Uint8* tonep, Uint8* tlofsp )
+{
+ Uint32 op;
+ for ( op = 0; op < 2; op++ )
+ SetOpTone(sndp, &chp->op [op], &tonep [op]);
+ chp->op [0].tl_ofs = (tlofsp [0] ^ 0x80) << (LOG_BITS - 4 + 1);
+ chp->op [1].tl_ofs = (tlofsp [1] ^ 0x80) << (LOG_BITS - 4 + 1);
+ chp->op [0].tl = tonep [2] & 0x3F;
+ chp->op [0].fb = tonep [3] & 0x7;
+ chp->op [0].wf = (tonep [3] >> 3) & 1;
+ chp->op [1].wf = (tonep [3] >> 4) & 1;
+#if USE_FBBUF
+ chp->op [0].fbbuf = 0;
+#endif
+ chp->op [1].input = 0;
+ OpUpdateWF(sndp, &chp->op [0]);
+ OpUpdateWF(sndp, &chp->op [1]);
+ OpUpdateEG(sndp, chp, &chp->op [0]);
+ OpUpdateEG(sndp, chp, &chp->op [1]);
+ OpUpdatePG(sndp, chp, &chp->op [0]);
+ OpUpdatePG(sndp, chp, &chp->op [1]);
+ OpUpdateTLL(sndp, chp, &chp->op [0]);
+ OpUpdateTLL(sndp, chp, &chp->op [1]);
+}
+
+static void opllsetchtone( OPLSOUND* sndp, OPL_CH* chp, Uint32 tone )
+{
+ SetChTone(sndp, chp, &sndp->regs [OPLL_INST_WORK + (tone << 3)], &sndp->regs [OPLL_INST_WORK2 + (tone << 1) + 0]);
+}
+
+static void recovercon( OPLSOUND* sndp, OPL_CH* chp )
+{
+ chp->op [0].modcar = (chp->con) ? 0 : 1;
+ chp->op [0].lvl = chp->con ? 1 : 0;
+ chp->op [1].lvl = 1;
+ OpUpdateEG(sndp, chp, &chp->op [0]);
+ chp->op [1].input = 0;
+}
+
+static void initrc_common( OPLSOUND* sndp, Uint32 rmode )
+{
+ if ( rmode )
+ {
+ /* BD */
+ sndp->ch [6].op [0].modcar = 1;
+ sndp->ch [6].op [0].lvl = 0;
+ OpUpdateEG(sndp, &sndp->ch [6], &sndp->ch [6].op [0]);
+ sndp->ch [6].op [1].input = 0;
+ sndp->ch [6].op [1].lvl = 2;
+ /* CYM */
+ sndp->ch [7].op [0].modcar = 0;
+ sndp->ch [7].op [0].lvl = 1;
+ OpUpdateEG(sndp, &sndp->ch [7], &sndp->ch [7].op [0]);
+ /* SD */
+ sndp->ch [7].op [1].input = 0;
+ sndp->ch [7].op [1].lvl = 2;
+ /* TOM */
+ sndp->ch [8].op [0].modcar = 0;
+ sndp->ch [8].op [0].lvl = 2;
+ OpUpdateEG(sndp, &sndp->ch [8], &sndp->ch [8].op [0]);
+ /* HH */
+ sndp->ch [8].op [1].input = 0;
+ sndp->ch [8].op [1].lvl = 1;
+ }
+ else
+ {
+ recovercon(sndp, &sndp->ch [6]);
+ if ( !sndp->ch [6].op [0].key ) SetOpOff(&sndp->ch [6].op [0]);
+ if ( !sndp->ch [6].op [1].key ) SetOpOff(&sndp->ch [6].op [1]);
+ recovercon(sndp, &sndp->ch [7]);
+ if ( !sndp->ch [7].op [0].key ) SetOpOff(&sndp->ch [7].op [0]);
+ if ( !sndp->ch [7].op [1].key ) SetOpOff(&sndp->ch [7].op [1]);
+ recovercon(sndp, &sndp->ch [8]);
+ if ( !sndp->ch [8].op [0].key ) SetOpOff(&sndp->ch [8].op [0]);
+ if ( !sndp->ch [8].op [1].key ) SetOpOff(&sndp->ch [8].op [1]);
+ }
+}
+
+static void oplsetrc( OPLSOUND* sndp, Uint32 rc )
+{
+ sndp->lfo [LFO_UNIT_AM].table = (rc & 0x80) ? sndp->opltbl->am_table1 : sndp->opltbl->am_table2;
+ sndp->lfo [LFO_UNIT_PM].table = (rc & 0x40) ? sndp->opltbl->pm_table1 : sndp->opltbl->pm_table2;
+ if ( (sndp->common.rmode ^ rc) & 0x20 )
+ {
+ if ( rc & 0x20 )
+ {
+#if 0
+ static Uint8 volini [2] = { 0, 0 };
+ static Uint8 bdtone [8] = { 0x04, 0x20, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8 };
+ SetChTone(sndp, &sndp->ch [6], bdtone, volini);
+ SetChTone(sndp, &sndp->ch [7], &romtone [0] [0x11 << 4], volini);
+ SetChTone(sndp, &sndp->ch [8], &romtone [0] [0x12 << 4], volini);
+#endif
+ sndp->ch [7].op [0].nst = PG_SHIFT + 4;
+ sndp->ch [7].op [1].nst = PG_SHIFT + 6;
+ sndp->ch [8].op [1].nst = PG_SHIFT + 5;
+ }
+ initrc_common(sndp, rc & 0x20);
+ }
+ sndp->common.rmode = rc & 0x20;
+ sndp->common.rc = rc & 0x1F;
+ /* BD */
+ sndp->ch [6].op [0].rkey = sndp->ch [6].op [1].rkey = rc & 0x10;
+ oplsetopkey(sndp, &sndp->ch [6].op [0]);
+ oplsetopkey(sndp, &sndp->ch [6].op [1]);
+ /* CYM */
+ sndp->ch [7].op [0].rkey = rc & 0x01;
+ oplsetopkey(sndp, &sndp->ch [7].op [0]);
+ /* SD */
+ sndp->ch [7].op [1].rkey = rc & 0x08;
+ oplsetopkey(sndp, &sndp->ch [7].op [1]);
+ /* TOM */
+ sndp->ch [8].op [0].rkey = rc & 0x04;
+ oplsetopkey(sndp, &sndp->ch [8].op [0]);
+ /* HH */
+ sndp->ch [8].op [1].rkey = rc & 0x02;
+ oplsetopkey(sndp, &sndp->ch [8].op [1]);
+}
+
+static void opllsetrc( OPLSOUND* sndp, Uint32 rc )
+{
+ if ( (sndp->common.rmode ^ rc) & 0x20 )
+ {
+ if ( rc & 0x20 )
+ {
+ opllsetchtone(sndp, &sndp->ch [6], 0x10);
+ opllsetchtone(sndp, &sndp->ch [7], 0x11);
+ opllsetchtone(sndp, &sndp->ch [8], 0x12);
+ opllsetopvolume(sndp, &sndp->ch [7], &sndp->ch [7].op [0], (sndp->regs [0x37] & 0xF0) >> 2);
+ opllsetopvolume(sndp, &sndp->ch [8], &sndp->ch [8].op [0], (sndp->regs [0x38] & 0xF0) >> 2);
+ sndp->ch [7].op [0].nst = PG_SHIFT + 5;
+ sndp->ch [7].op [1].nst = PG_SHIFT + 5;
+ sndp->ch [8].op [1].nst = PG_SHIFT + 5;
+ }
+ else
+ {
+ opllsetchtone(sndp, &sndp->ch [6], sndp->regs [0x36]>>4);
+ opllsetchtone(sndp, &sndp->ch [7], sndp->regs [0x37]>>4);
+ opllsetchtone(sndp, &sndp->ch [8], sndp->regs [0x38]>>4);
+ }
+ initrc_common(sndp, rc & 0x20);
+ }
+ sndp->common.rmode = rc & 0x20;
+ sndp->common.rc = rc & 0x1F;
+ /* BD */
+ sndp->ch [6].op [0].rkey = sndp->ch [6].op [1].rkey = rc & 0x10;
+ oplsetopkey(sndp, &sndp->ch [6].op [0]);
+ oplsetopkey(sndp, &sndp->ch [6].op [1]);
+ /* CYM */
+ sndp->ch [7].op [0].rkey = rc & 0x01;
+ oplsetopkey(sndp, &sndp->ch [7].op [0]);
+ /* SD */
+ sndp->ch [7].op [1].rkey = rc & 0x08;
+ oplsetopkey(sndp, &sndp->ch [7].op [1]);
+ /* TOM */
+ sndp->ch [8].op [0].rkey = rc & 0x04;
+ oplsetopkey(sndp, &sndp->ch [8].op [0]);
+ /* HH */
+ sndp->ch [8].op [1].rkey = rc & 0x02;
+ oplsetopkey(sndp, &sndp->ch [8].op [1]);
+}
+
+#define OPLSETOP(func) { \
+ Uint32 op = op_table [a & 0x1F]; \
+ if ( op != 0xFF) func(sndp, &sndp->ch [op >> 1], &sndp->ch [op >> 1].op [op & 1], v); \
+}
+
+__inline static void oplwritereg( OPLSOUND* sndp, Uint32 a, Uint32 v )
+{
+ switch ( a >> 5 )
+ {
+ default:
+ NEVER_REACH
+
+ case 0:
+ switch ( a & 0x1F )
+ {
+ case 0x01:
+ if ( sndp->opl_type == OPL_TYPE_OPL2 )
+ {
+ Uint32 i;
+ sndp->common.wfe = (v & 0x20) ? 3 : 0;
+ for ( i = 0; i < 9; i++ )
+ {
+ OpUpdateWF(sndp, &sndp->ch [i].op [0]);
+ OpUpdateWF(sndp, &sndp->ch [i].op [1]);
+ }
+ }
+ break;
+
+ case 0x08:
+ /* CSM mode */
+ case 0x07: case 0x09: case 0x0A: case 0x0B: case 0x0C:
+ case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12:
+ if ( sndp->deltatpcm )
+ sndp->deltatpcm->write(sndp->deltatpcm->ctx, a - 0x07, v);
+ break;
+ }
+ break;
+
+ case 1: OPLSETOP(oplsetopmul); break;
+ case 2: OPLSETOP(oplsetopkstl); break;
+ case 3: OPLSETOP(oplsetopardr); break;
+ case 4: OPLSETOP(oplsetopslrr); break;
+ case 7: OPLSETOP(oplsetopwf); break;
+ case 5:
+ if ( (a & 0x1F) == (0xBD & 0x1F) )
+ oplsetrc(sndp, v);
+ else if ( (a & 0x1F) < 9 )
+ oplsetchfreql(sndp, &sndp->ch [a & 0xF], v);
+ else if ( (a & 0xF) < 9 )
+ oplsetchfreqh(sndp, &sndp->ch [a & 0xF], v);
+ break;
+
+ case 6:
+ if ( (a & 0x1F) < 9) oplsetchfbcon(sndp, &sndp->ch [a & 0xF], v);
+ break;
+ }
+}
+
+static void oplwrite( OPLSOUND* sndp, Uint32 a, Uint32 v )
+{
+ if ( a & 1 )
+ {
+ sndp->regs [sndp->common.adr] = v;
+ oplwritereg(sndp, sndp->common.adr, v);
+ }
+ else
+ sndp->common.adr = v;
+}
+
+static Uint32 oplread( OPLSOUND* sndp, Uint32 a )
+{
+ if ( a & 1 )
+ return sndp->regs [sndp->common.adr];
+ else
+ return 0x80;
+}
+
+__inline static void opllwritereg( OPLSOUND* sndp, Uint32 a, Uint32 v )
+{
+ switch ( a >> 3 )
+ {
+ default:
+ NEVER_REACH
+ case 0:
+ sndp->regs [OPLL_INST_WORK + (a & 7)] = v;
+ break;
+
+ case 1:
+ if ( a == 0xE) opllsetrc(sndp, v & 0x3F);
+ break;
+
+ case 2:
+ case 3:
+ a &= 0xF;
+ if ( a < 9) opllsetchfreql(sndp, &sndp->ch [a], v);
+ break;
+
+ case 4:
+ case 5:
+ a &= 0xF;
+ if ( a < 9) opllsetchfreqh(sndp, &sndp->ch [a], v);
+ break;
+
+ case 6:
+ case 7:
+ a &= 0xF;
+ if ( a < 9 )
+ {
+ if ( (sndp->common.rmode) && (a >= 6) )
+ {
+ if ( a != 6) opllsetopvolume(sndp, &sndp->ch [a], &sndp->ch [a].op [0], (v & 0xF0) >> 2);
+ }
+ else
+ {
+ opllsetchtone(sndp, &sndp->ch [a], (v & 0xF0) >> 4);
+ }
+ opllsetopvolume(sndp, &sndp->ch [a], &sndp->ch [a].op [1], (v & 0xF) << 2);
+ }
+ break;
+ }
+}
+
+static void opllwrite( OPLSOUND* sndp, Uint32 a, Uint32 v )
+{
+ if ( a & 1 )
+ {
+ if ( sndp->common.adr < 0x40 )
+ {
+ sndp->regs [sndp->common.adr] = v;
+ opllwritereg(sndp, sndp->common.adr, v);
+ }
+ }
+ else
+ sndp->common.adr = v;
+}
+
+static Uint32 opllread( OPLSOUND* sndp, Uint32 a )
+{
+ return 0xFF;
+}
+
+static void opreset( OPLSOUND* sndp, OPL_OP* opp )
+{
+ /* XMEMSET(opp, 0, sizeof(OPL_OP)); */
+ SetOpOff(opp);
+ opp->tl_ofs = 0x80 << (LOG_BITS - 4 + 1);
+#if TESTING_OPTIMIZE_AME
+ opp->amin = &sndp->common.amzero;
+#endif
+ opp->pg.rng = 0xFFFF;
+}
+
+static void chreset( OPLSOUND* sndp, OPL_CH* chp, Uint32 clock, Uint32 freq )
+{
+ Uint32 op;
+ XMEMSET(chp, 0, sizeof(OPL_CH));
+ for ( op = 0; op < 2; op++ )
+ {
+ opreset(sndp, &chp->op [op]);
+ }
+ recovercon(sndp, chp);
+}
+
+static void sndreset( OPLSOUND* sndp, Uint32 clock, Uint32 freq )
+{
+ Uint32 i, cpse;
+ XMEMSET(&sndp->common, 0, sizeof(sndp->common));
+ XMEMSET(&sndp->lfo [LFO_UNIT_AM], 0, sizeof(OPL_LFO));
+ sndp->lfo [LFO_UNIT_AM].sps = DivFix(37 * (1 << AMTBL_BITS), freq * 10, LFO_SHIFT);
+ sndp->lfo [LFO_UNIT_AM].adrmask = (1 << AMTBL_BITS) - 1;
+ sndp->lfo [LFO_UNIT_AM].table = sndp->opltbl->am_table1;
+ XMEMSET(&sndp->lfo [LFO_UNIT_PM], 0, sizeof(OPL_LFO));
+ sndp->lfo [LFO_UNIT_PM].sps = DivFix(64 * (1 << PMTBL_BITS), freq * 10, LFO_SHIFT);
+ sndp->lfo [LFO_UNIT_PM].adrmask = (1 << PMTBL_BITS) - 1;
+ sndp->lfo [LFO_UNIT_PM].table = sndp->opltbl->pm_table1;
+ sndp->common.cpsp = DivFix(clock, 72 * freq, CPS_SHIFTP);
+ cpse = DivFix(clock, 72 * freq, CPS_SHIFTE);
+ for ( i = 0; i < 4; i++ )
+ {
+ sndp->common.ratetbl [i] = (i + 4) * cpse;
+ sndp->common.ratetbla [i] = 3 * sndp->common.ratetbl [i];
+ }
+ sndp->common.tll2logtbl = sndp->opltbl->tll2log_table;
+ sndp->common.sintablemask = (1 << SINTBL_BITS) - 1;
+
+ for ( i = 0; i < 9; i++ )
+ chreset(sndp, &sndp->ch [i], clock, freq);
+
+ if ( sndp->deltatpcm )
+ sndp->deltatpcm->reset(sndp->deltatpcm->ctx, clock, freq);
+
+ if ( sndp->opl_type & OPL_TYPE_OPL )
+ {
+ XMEMSET(&sndp->regs, 0, 0x100);
+ sndp->common.ar_table = sndp->opltbl->ar_tablepow;
+ sndp->common.sintablemask -= (1 << (SINTBL_BITS - 11)) - 1;
+ for ( i = 0x0; i < 0x100; i++ )
+ {
+ oplwrite(sndp, 0, i);
+ oplwrite(sndp, 1, 0x00);
+ }
+ for ( i = 0xA0; i < 0xA9; i++ )
+ {
+ oplwrite(sndp, 0, 0xA0 + i);
+ oplwrite(sndp, 1, 0x40);
+ oplwrite(sndp, 0, 0xB0 + i);
+ oplwrite(sndp, 1, 0x0E);
+ }
+ }
+ else
+ {
+ static Uint8 const fmbios_initdata [9] = "\x30\x10\x20\x20\xfb\xb2\xf3\xf3";
+ XMEMSET(&sndp->regs, 0, 0x40);
+ sndp->common.ar_table = sndp->opltbl->ar_tablelog;
+ sndp->common.wfe = 1;
+ sndp->common.sintablemask -= (1 << (SINTBL_BITS - 8)) - 1;
+ for ( i = 0; i < sizeof(fmbios_initdata)-1; i++ )
+ {
+ opllwrite(sndp, 0, i);
+ opllwrite(sndp, 1, fmbios_initdata [i]);
+ }
+ opllwrite(sndp, 0, 0x0E);
+ opllwrite(sndp, 1, 0x00);
+ opllwrite(sndp, 0, 0x0F);
+ opllwrite(sndp, 1, 0x00);
+ for ( i = 0; i < 9; i++ )
+ {
+ opllwrite(sndp, 0, 0x10 + i);
+ opllwrite(sndp, 1, 0x20);
+ opllwrite(sndp, 0, 0x20 + i);
+ opllwrite(sndp, 1, 0x07);
+ opllwrite(sndp, 0, 0x30 + i);
+ opllwrite(sndp, 1, 0xB3);
+ }
+ }
+}
+
+static void oplsetinst( OPLSOUND* sndp, Uint32 n, void* p, Uint32 l )
+{
+ if ( sndp->deltatpcm) sndp->deltatpcm->setinst(sndp->deltatpcm->ctx, n, p, l);
+}
+
+__inline static Uint32 GetDwordLE(Uint8* p )
+{
+ return p [0] | (p [1] << 8) | (p [2] << 16) | (p [3] << 24);
+}
+#define GetDwordLEM(p) (Uint32)((((Uint8* )p) [0] | (((Uint8* )p) [1] << 8) | (((Uint8* )p) [2] << 16) | (((Uint8* )p) [3] << 24)) )
+
+static void opllsetinst( OPLSOUND* sndp, Uint32 n, Uint8* p, Uint32 l )
+{
+ Int32 i, j, sb = 9;
+ if ( n )
+ return;
+ if ( (GetDwordLE(p) & 0xF0FFFFFF) == GetDwordLEM("ILL0") )
+ {
+ if ( 0 < p [4] && p [4] <= SINTBL_BITS) sb = p [4];
+ for ( j = 1; j < 16 + 3; j++ )
+ for ( i = 0; i < 8; i++ )
+ sndp->regs [OPLL_INST_WORK + (j << 3) + i] = p [(j << 4) + i];
+ for ( j = 0; j < 16 + 3; j++ )
+ {
+ sndp->regs [OPLL_INST_WORK2 + (j << 1) + 0] = p [(j << 4) + 8];
+ sndp->regs [OPLL_INST_WORK2 + (j << 1) + 1] = p [(j << 4) + 9];
+ }
+ }
+ else
+ {
+ for ( j = 1; j < 16; j++ )
+ for ( i = 0; i < 8; i++ )
+ sndp->regs [OPLL_INST_WORK + (j << 3) + i] = p [((j - 1) << 3) + i];
+ }
+ sndp->common.sintablemask = (1 << SINTBL_BITS) - 1;
+ sndp->common.sintablemask -= (1 << (SINTBL_BITS - sb)) - 1;
+}
+
+static void sndrelease( OPLSOUND* sndp )
+{
+ if ( sndp->logtbl) sndp->logtbl->release(sndp->logtbl->ctx);
+ if ( sndp->opltbl) sndp->opltbl->release(sndp->opltbl->ctx);
+ if ( sndp->deltatpcm) sndp->deltatpcm->release(sndp->deltatpcm->ctx);
+ XFREE(sndp);
+}
+
+KMIF_SOUND_DEVICE* OPLSoundAlloc(Uint32 opl_type )
+{
+ OPLSOUND* sndp;
+ sndp = (OPLSOUND*) XMALLOC(sizeof(OPLSOUND));
+ if ( !sndp) return 0;
+ sndp->opl_type = opl_type;
+ sndp->kmif.ctx = sndp;
+ sndp->kmif.release = (void (*)( void* )) sndrelease;
+ sndp->kmif.volume = (void (*)( void*, int )) sndvolume;
+ sndp->kmif.reset = (void (*)( void*, Uint32, Uint32 )) sndreset;
+ sndp->kmif.synth = (int (*)( void* )) sndsynth;
+ if ( sndp->opl_type == OPL_TYPE_MSXAUDIO )
+ {
+ sndp->deltatpcm = YMDELTATPCMSoundAlloc(YMDELTATPCM_TYPE_Y8950);
+ }
+ else
+ sndp->deltatpcm = 0;
+ if ( sndp->opl_type & OPL_TYPE_OPL )
+ {
+ sndp->kmif.write = (void (*)( void*, Uint32, Uint32 )) oplwrite;
+ sndp->kmif.read = (Uint32 (*)( void*, Uint32 )) oplread;
+ sndp->kmif.setinst = (void (*)( void*, Uint32, void*, Uint32 )) oplsetinst;
+ }
+ else
+ {
+ sndp->kmif.write = (void (*)( void*, Uint32, Uint32 )) opllwrite;
+ sndp->kmif.read = (Uint32 (*)( void*, Uint32 )) opllread;
+ sndp->kmif.setinst = (void (*)( void*, Uint32, void*, Uint32 )) opllsetinst;
+ switch ( sndp->opl_type )
+ {
+ case OPL_TYPE_OPLL:
+ case OPL_TYPE_MSXMUSIC:
+ opllsetinst(sndp, 0, romtone [0], 16 * 19);
+ break;
+
+ case OPL_TYPE_SMSFMUNIT:
+ opllsetinst(sndp, 0, romtone [1], 16 * 19);
+ break;
+
+ case OPL_TYPE_VRC7:
+ opllsetinst(sndp, 0, romtone [2], 16 * 19);
+ break;
+ }
+ }
+ sndp->logtbl = LogTableAddRef();
+ sndp->opltbl = OplTableAddRef();
+ if ( !sndp->logtbl || !sndp->opltbl )
+ {
+ sndrelease(sndp);
+ return 0;
+ }
+
+ return &sndp->kmif;
+}
diff --git a/Frameworks/GME/gme/s_opl.h b/Frameworks/GME/gme/s_opl.h
new file mode 100644
index 000000000..227c03c3c
--- /dev/null
+++ b/Frameworks/GME/gme/s_opl.h
@@ -0,0 +1,26 @@
+#ifndef S_OPL_H__
+#define S_OPL_H__
+
+#include "kmsnddev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ OPL_TYPE_OPLL = 0x10, /* YAMAHA YM2413 */
+ OPL_TYPE_MSXMUSIC = 0x11, /* YAMAHA YM2413 */
+ OPL_TYPE_SMSFMUNIT = 0x12, /* YAMAHA YM2413? */
+ OPL_TYPE_VRC7 = 0x13, /* KONAMI 053982 VRV VII */
+ OPL_TYPE_OPL = 0x20, /* YAMAHA YM3526 */
+ OPL_TYPE_MSXAUDIO = 0x21, /* YAMAHA Y8950 */
+ OPL_TYPE_OPL2 = 0x22 /* YAMAHA YM3812 */
+};
+
+KMIF_SOUND_DEVICE *OPLSoundAlloc(Uint32 opl_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* S_OPL_H__ */
diff --git a/Frameworks/GME/gme/s_opltbl.c b/Frameworks/GME/gme/s_opltbl.c
new file mode 100644
index 000000000..35e54edbd
--- /dev/null
+++ b/Frameworks/GME/gme/s_opltbl.c
@@ -0,0 +1,136 @@
+#include "nestypes.h"
+#include "s_logtbl.h"
+#include "s_opltbl.h"
+
+#if STATIC_TABLES
+
+static void OplTableRelease(void *ctx)
+{
+}
+
+static KMIF_OPLTABLE opl_static_tables = {
+ &opl_static_tables;
+ OplTableRelease,
+#include "s_oplt.h"
+};
+
+KMIF_OPLTABLE *OplTableAddRef(void)
+{
+ opl_static_tables.release = OplTableRelease;
+ return &opl_static_tables;
+}
+
+#else
+
+#include
+#ifndef M_PI
+#ifdef PI
+#define M_PI PI
+#else
+#define M_PI 3.14159265358979323846
+#endif
+#endif
+
+#define AM_DEPTH1 4.8 /* dB */
+#define AM_DEPTH2 1.0 /* dB */
+#define PM_DEPTH1 14.0 /* cent */
+#define PM_DEPTH2 7.0 /* cent */
+#define LOG_KEYOFF (15 << (LOG_BITS + 1))
+
+#define DB0375_TO_LOG(x) ((Uint32)(0.375 * (1 << (LOG_BITS + x)) / 10))
+
+#define AR_OFF (128 << ARTBL_SHIFT)
+#define AR_MAX (127 << ARTBL_SHIFT)
+
+
+static volatile Uint32 opl_tables_mutex = 0;
+static Uint32 opl_tables_refcount = 0;
+static KMIF_OPLTABLE *opl_tables = 0;
+
+static void OplTableRelease(void *ctx)
+{
+ ++opl_tables_mutex;
+ while (opl_tables_mutex != 1)
+ {
+ XSLEEP(0);
+ }
+ opl_tables_refcount--;
+ if (!opl_tables_refcount)
+ {
+ XFREE(ctx);
+ opl_tables = 0;
+ }
+ --opl_tables_mutex;
+}
+
+static void OplTableCalc(KMIF_OPLTABLE *tbl)
+{
+ Uint32 u, u2, i;
+ tbl->sin_table[0][0] = tbl->sin_table[0][1 << (SINTBL_BITS - 1)] = LOG_KEYOFF;
+ for (i = 1 ;i < (1 << (SINTBL_BITS - 1)); i++)
+ {
+ double d;
+ d = (1 << LOG_BITS) * -(log(sin(2.0 * M_PI * ((double)i) / (1 << SINTBL_BITS))) / log(2));
+ if (d > (LOG_KEYOFF >> 1)) d = (LOG_KEYOFF >> 1);
+ tbl->sin_table[0][i] = ((Uint32)d) << 1;
+ tbl->sin_table[0][i + (1 << (SINTBL_BITS - 1))] = (((Uint32)d) << 1) + 1;
+ }
+ for (i = 0 ;i < (1 << SINTBL_BITS); i++)
+ {
+ tbl->sin_table[1][i] = (tbl->sin_table[0][i] & 1) ? tbl->sin_table[0][0] : tbl->sin_table[0][i];
+ tbl->sin_table[2][i] = tbl->sin_table[0][i] & ~1;
+ tbl->sin_table[3][i] = (i & (1 << (SINTBL_BITS - 2))) ? LOG_KEYOFF : tbl->sin_table[2][i];
+ }
+ for (i = 0; i < (1 << TLLTBL_BITS); i++)
+ {
+ tbl->tll2log_table[i] = (i * DB0375_TO_LOG(0)) << 1;
+ }
+ for (i = 0; i < (1 << AMTBL_BITS); i++)
+ {
+ u = (Uint32)((1 + sin(2 * M_PI * ((double)i) / (1 << AMTBL_BITS))) * ((1 << LOG_BITS) * AM_DEPTH1 / 20.0));
+ u2 = (Uint32)((1 + sin(2 * M_PI * ((double)i) / (1 << AMTBL_BITS))) * ((1 << LOG_BITS) * AM_DEPTH2 / 20.0));
+ tbl->am_table1[i] = u << 1;
+ tbl->am_table2[i] = u2 << 1;
+ }
+ for (i = 0; i < (1 << PMTBL_BITS); i++)
+ {
+ u = (Uint32)((1 << PM_SHIFT) * pow(2, sin(2 * M_PI * ((double)i) / (1 << PMTBL_BITS)) * PM_DEPTH1 / 1200.0));
+ u2 = (Uint32)((1 << PM_SHIFT) * pow(2, sin(2 * M_PI * ((double)i) / (1 << PMTBL_BITS)) * PM_DEPTH2 / 1200.0));
+ tbl->pm_table1[i] = u;
+ tbl->pm_table2[i] = u2;
+ }
+
+ for (i = 0; i < (1 << ARTBL_BITS); i++)
+ {
+ u = (Uint32)(((double)AR_MAX) * (1 - log(1 + i) / log(1 << ARTBL_BITS)));
+ tbl->ar_tablelog[i] = u;
+#if 1
+ u = (Uint32)(((double)AR_MAX) * (pow(1 - i / (double)(1 << ARTBL_BITS), 8)));
+ tbl->ar_tablepow[i] = u;
+#endif
+ }
+}
+
+KMIF_OPLTABLE *OplTableAddRef(void)
+{
+ ++opl_tables_mutex;
+ while (opl_tables_mutex != 1)
+ {
+ XSLEEP(0);
+ }
+ if (!opl_tables_refcount)
+ {
+ opl_tables = (KMIF_OPLTABLE*) XMALLOC(sizeof(KMIF_OPLTABLE));
+ if (opl_tables)
+ {
+ opl_tables->ctx = opl_tables;
+ opl_tables->release = OplTableRelease;
+ OplTableCalc(opl_tables);
+ }
+ }
+ if (opl_tables) opl_tables_refcount++;
+ --opl_tables_mutex;
+ return opl_tables;
+}
+
+#endif
diff --git a/Frameworks/GME/gme/s_opltbl.h b/Frameworks/GME/gme/s_opltbl.h
new file mode 100644
index 000000000..7a3f805f7
--- /dev/null
+++ b/Frameworks/GME/gme/s_opltbl.h
@@ -0,0 +1,38 @@
+#ifndef S_OPLTBL_H__
+#define S_OPLTBL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SINTBL_BITS 11
+#define AMTBL_BITS 8
+#define PMTBL_BITS 8
+#define PM_SHIFT 9
+#define ARTBL_BITS 7
+#define ARTBL_SHIFT 20
+#define TLLTBL_BITS 7
+
+typedef struct
+{
+ void *ctx;
+ void (*release)(void *ctx);
+ Uint32 sin_table[4][1 << SINTBL_BITS];
+ Uint32 tll2log_table[1 << TLLTBL_BITS];
+ Uint32 ar_tablelog[1 << ARTBL_BITS];
+ Uint32 am_table1[1 << AMTBL_BITS];
+ Uint32 pm_table1[1 << PMTBL_BITS];
+#if 1
+ Uint32 ar_tablepow[1 << ARTBL_BITS];
+#endif
+ Uint32 am_table2[1 << AMTBL_BITS];
+ Uint32 pm_table2[1 << PMTBL_BITS];
+} KMIF_OPLTABLE;
+
+KMIF_OPLTABLE *OplTableAddRef(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* S_OPLTBL_H__ */
diff --git a/Frameworks/GME/gme/scd_pcm.c b/Frameworks/GME/gme/scd_pcm.c
new file mode 100644
index 000000000..ee3d4701f
--- /dev/null
+++ b/Frameworks/GME/gme/scd_pcm.c
@@ -0,0 +1,498 @@
+/***********************************************************/
+/* */
+/* PCM.C : PCM RF5C164 emulator */
+/* */
+/* This source is a part of Gens project */
+/* Written by Stéphane Dallongeville (gens@consolemul.com) */
+/* Copyright (c) 2002 by Stéphane Dallongeville */
+/* */
+/***********************************************************/
+
+#include
+#include
+#include
+
+#include "scd_pcm.h"
+int PCM_Init(void *chip, int Rate);
+void PCM_Set_Rate(void *chip, int Rate);
+void PCM_Reset(void *chip);
+void PCM_Write_Reg(void *chip, unsigned int Reg, unsigned int Data);
+int PCM_Update(void *chip, int **buf, int Length);
+
+#define PCM_STEP_SHIFT 11
+
+static unsigned char VolTabIsInit = 0x00;
+static int PCM_Volume_Tab[256 * 256];
+
+//unsigned char Ram_PCM[64 * 1024];
+//int PCM_Enable;
+
+
+/**
+ * PCM_Init(): Initialize the PCM chip.
+ * @param Rate Sample rate.
+ * @return 0 if successful.
+ */
+int PCM_Init(void *_chip, int Rate)
+{
+ struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip;
+ int i, j, out;
+
+ if (! VolTabIsInit)
+ {
+ for (i = 0; i < 0x100; i++)
+ {
+ for (j = 0; j < 0x100; j++)
+ {
+ if (i & 0x80)
+ {
+ out = -(i & 0x7F);
+ out *= j;
+ PCM_Volume_Tab[(j << 8) + i] = out;
+ }
+ else
+ {
+ out = i * j;
+ PCM_Volume_Tab[(j << 8) + i] = out;
+ }
+ }
+ }
+ VolTabIsInit = 0x01;
+ }
+
+ for (i = 0; i < 8; i ++)
+ chip->Channel[i].Muted = 0x00;
+
+ chip->RAMSize = 64 * 1024;
+ chip->RAM = (unsigned char*)malloc(chip->RAMSize);
+ PCM_Reset(chip);
+ PCM_Set_Rate(chip, Rate);
+
+ return 0;
+}
+
+
+/**
+ * PCM_Reset(): Reset the PCM chip.
+ */
+void PCM_Reset(void *_chip)
+{
+ struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip;
+ int i;
+ struct pcm_chan_* chan;
+
+ // Clear the PCM memory.
+ memset(chip->RAM, 0x00, chip->RAMSize);
+
+ chip->Enable = 0;
+ chip->Cur_Chan = 0;
+ chip->Bank = 0;
+
+ /* clear channel registers */
+ for (i = 0; i < 8; i++)
+ {
+ chan = &chip->Channel[i];
+ chan->Enable = 0;
+ chan->ENV = 0;
+ chan->PAN = 0;
+ chan->St_Addr = 0;
+ chan->Addr = 0;
+ chan->Loop_Addr = 0;
+ chan->Step = 0;
+ chan->Step_B = 0;
+ chan->Data = 0;
+ }
+}
+
+
+/**
+ * PCM_Set_Rate(): Change the PCM sample rate.
+ * @param Rate New sample rate.
+ */
+void PCM_Set_Rate(void *_chip, int Rate)
+{
+ struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip;
+ int i;
+
+ if (Rate == 0)
+ return;
+
+ //chip->Rate = (float) (32 * 1024) / (float) Rate;
+ chip->Rate = (float) (31.8 * 1024) / (float) Rate;
+
+ for (i = 0; i < 8; i++)
+ {
+ chip->Channel[i].Step =
+ (int) ((float) chip->Channel[i].Step_B * chip->Rate);
+ }
+}
+
+
+/**
+ * PCM_Write_Reg(): Write to a PCM register.
+ * @param Reg Register ID.
+ * @param Data Data to write.
+ */
+void PCM_Write_Reg(void *_chip, unsigned int Reg, unsigned int Data)
+{
+ struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip;
+ int i;
+ struct pcm_chan_* chan = &chip->Channel[chip->Cur_Chan];
+
+ Data &= 0xFF;
+
+ switch (Reg)
+ {
+ case 0x00:
+ /* evelope register */
+ chan->ENV = Data;
+ chan->MUL_L = (Data * (chan->PAN & 0x0F)) >> 5;
+ chan->MUL_R = (Data * (chan->PAN >> 4)) >> 5;
+ break;
+
+ case 0x01:
+ /* pan register */
+ chan->PAN = Data;
+ chan->MUL_L = ((Data & 0x0F) * chan->ENV) >> 5;
+ chan->MUL_R = ((Data >> 4) * chan->ENV) >> 5;
+ break;
+
+ case 0x02:
+ /* frequency step (LB) registers */
+ chan->Step_B &= 0xFF00;
+ chan->Step_B += Data;
+ chan->Step = (int)((float)chan->Step_B * chip->Rate);
+
+ //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
+ // "Step low = %.2X Step calculated = %.8X",
+ // Data, chan->Step);
+ break;
+
+ case 0x03:
+ /* frequency step (HB) registers */
+ chan->Step_B &= 0x00FF;
+ chan->Step_B += Data << 8;
+ chan->Step = (int)((float)chan->Step_B * chip->Rate);
+
+ //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
+ // "Step high = %.2X Step calculated = %.8X",
+ // Data, chan->Step);
+ break;
+
+ case 0x04:
+ chan->Loop_Addr &= 0xFF00;
+ chan->Loop_Addr += Data;
+
+ //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
+ // "Loop low = %.2X Loop = %.8X",
+ // Data, chan->Loop_Addr);
+ break;
+
+ case 0x05:
+ /* loop address registers */
+ chan->Loop_Addr &= 0x00FF;
+ chan->Loop_Addr += Data << 8;
+
+ //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
+ // "Loop high = %.2X Loop = %.8X",
+ // Data, chan->Loop_Addr);
+ break;
+
+ case 0x06:
+ /* start address registers */
+ chan->St_Addr = Data << (PCM_STEP_SHIFT + 8);
+ //chan->Addr = chan->St_Addr;
+
+ //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
+ // "Start addr = %.2X New Addr = %.8X",
+ // Data, chan->Addr);
+ break;
+
+ case 0x07:
+ /* control register */
+ /* mod is H */
+ if (Data & 0x40)
+ {
+ /* select channel */
+ chip->Cur_Chan = Data & 0x07;
+ }
+ /* mod is L */
+ else
+ {
+ /* pcm ram bank select */
+ chip->Bank = (Data & 0x0F) << 12;
+ }
+
+ /* sounding bit */
+ if (Data & 0x80)
+ chip->Enable = 0xFF; // Used as mask
+ else
+ chip->Enable = 0;
+
+ //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
+ // "General Enable = %.2X", Data);
+ break;
+
+ case 0x08:
+ /* sound on/off register */
+ Data ^= 0xFF;
+
+ //LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
+ // "Channel Enable = %.2X", Data);
+
+ for (i = 0; i < 8; i++)
+ {
+ chan = &chip->Channel[i];
+ if (!chan->Enable)
+ chan->Addr = chan->St_Addr;
+ }
+
+ for (i = 0; i < 8; i++)
+ {
+ chip->Channel[i].Enable = Data & (1 << i);
+ }
+ }
+}
+
+
+/**
+ * PCM_Update(): Update the PCM buffer.
+ * @param buf PCM buffer.
+ * @param Length Buffer length.
+ */
+int PCM_Update(void *_chip, int **buf, int Length)
+{
+ struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip;
+ int i, j;
+ int *bufL, *bufR; //, *volL, *volR;
+ unsigned int Addr, k;
+ struct pcm_chan_ *CH;
+
+ bufL = buf[0];
+ bufR = buf[1];
+
+ // clear buffers
+ memset(bufL, 0, Length * sizeof(int));
+ memset(bufR, 0, Length * sizeof(int));
+
+ // if PCM disable, no sound
+ if (!chip->Enable)
+ return 1;
+
+#if 0
+ // faster for short update
+ for (j = 0; j < Length; j++)
+ {
+ for (i = 0; i < 8; i++)
+ {
+ CH = &(chip->Channel[i]);
+
+ // only loop when sounding and on
+ if (CH->Enable && ! CH->Muted)
+ {
+ Addr = CH->Addr >> PCM_STEP_SHIFT;
+
+ if (Addr & 0x10000)
+ {
+ for(k = CH->Old_Addr; k < 0x10000; k++)
+ {
+ if (chip->RAM[k] == 0xFF)
+ {
+ CH->Old_Addr = Addr = CH->Loop_Addr;
+ CH->Addr = Addr << PCM_STEP_SHIFT;
+ break;
+ }
+ }
+
+ if (Addr & 0x10000)
+ {
+ //CH->Addr -= CH->Step;
+ CH->Enable = 0;
+ break;
+ }
+ }
+ else
+ {
+ for(k = CH->Old_Addr; k <= Addr; k++)
+ {
+ if (chip->RAM[k] == 0xFF)
+ {
+ CH->Old_Addr = Addr = CH->Loop_Addr;
+ CH->Addr = Addr << PCM_STEP_SHIFT;
+ break;
+ }
+ }
+ }
+
+ // test for loop signal
+ if (chip->RAM[Addr] == 0xFF)
+ {
+ Addr = CH->Loop_Addr;
+ CH->Addr = Addr << PCM_STEP_SHIFT;
+ }
+
+ if (chip->RAM[Addr] & 0x80)
+ {
+ CH->Data = chip->RAM[Addr] & 0x7F;
+ bufL[j] -= CH->Data * CH->MUL_L;
+ bufR[j] -= CH->Data * CH->MUL_R;
+ }
+ else
+ {
+ CH->Data = chip->RAM[Addr];
+ bufL[j] += CH->Data * CH->MUL_L;
+ bufR[j] += CH->Data * CH->MUL_R;
+ }
+
+ // update address register
+ //CH->Addr = (CH->Addr + CH->Step) & 0x7FFFFFF;
+ CH->Addr += CH->Step;
+ CH->Old_Addr = Addr + 1;
+ }
+ }
+ }
+#endif
+
+#if 1
+ // for long update
+ for (i = 0; i < 8; i++)
+ {
+ CH = &(chip->Channel[i]);
+
+ // only loop when sounding and on
+ if (CH->Enable && ! CH->Muted)
+ {
+ Addr = CH->Addr >> PCM_STEP_SHIFT;
+ //volL = &(PCM_Volume_Tab[CH->MUL_L << 8]);
+ //volR = &(PCM_Volume_Tab[CH->MUL_R << 8]);
+
+ for (j = 0; j < Length; j++)
+ {
+ // test for loop signal
+ if (chip->RAM[Addr] == 0xFF)
+ {
+ CH->Addr = (Addr = CH->Loop_Addr) << PCM_STEP_SHIFT;
+ if (chip->RAM[Addr] == 0xFF)
+ break;
+ else
+ j--;
+ }
+ else
+ {
+ if (chip->RAM[Addr] & 0x80)
+ {
+ CH->Data = chip->RAM[Addr] & 0x7F;
+ bufL[j] -= CH->Data * CH->MUL_L;
+ bufR[j] -= CH->Data * CH->MUL_R;
+ }
+ else
+ {
+ CH->Data = chip->RAM[Addr];
+ bufL[j] += CH->Data * CH->MUL_L;
+ bufR[j] += CH->Data * CH->MUL_R;
+ }
+
+ // update address register
+ k = Addr + 1;
+ CH->Addr = (CH->Addr + CH->Step) & 0x7FFFFFF;
+ Addr = CH->Addr >> PCM_STEP_SHIFT;
+
+ for (; k < Addr; k++)
+ {
+ if (chip->RAM[k] == 0xFF)
+ {
+ CH->Addr = (Addr = CH->Loop_Addr) << PCM_STEP_SHIFT;
+ break;
+ }
+ }
+ }
+ }
+
+ if (chip->RAM[Addr] == 0xFF)
+ {
+ CH->Addr = CH->Loop_Addr << PCM_STEP_SHIFT;
+ }
+ }
+ }
+#endif
+
+ return 0;
+}
+
+
+void rf5c164_update(void *_chip, stream_sample_t **outputs, int samples)
+{
+ struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip;
+
+ PCM_Update(chip, outputs, samples);
+}
+
+void * device_start_rf5c164( int clock )
+{
+ /* allocate memory for the chip */
+ //rf5c164_state *chip = get_safe_token(device);
+ struct pcm_chip_ *chip;
+ int rate;
+
+ chip = (struct pcm_chip_ *) malloc(sizeof(struct pcm_chip_));
+ if (!chip) return chip;
+
+ rate = clock / 384;
+
+ PCM_Init(chip, rate);
+ /* allocate the stream */
+ //chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update);
+
+ return chip;
+}
+
+void device_stop_rf5c164(void *_chip)
+{
+ struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip;
+ free(chip->RAM); chip->RAM = NULL;
+ free(chip);
+}
+
+void device_reset_rf5c164(void *chip)
+{
+ //struct pcm_chip_ *chip = &PCM_Chip[ChipID];
+ PCM_Reset(chip);
+}
+
+void rf5c164_w(void *chip, offs_t offset, UINT8 data)
+{
+ //struct pcm_chip_ *chip = &PCM_Chip[ChipID];
+ PCM_Write_Reg(chip, offset, data);
+}
+
+void rf5c164_mem_w(void *_chip, offs_t offset, UINT8 data)
+{
+ struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip;
+ chip->RAM[chip->Bank | offset] = data;
+}
+
+void rf5c164_write_ram(void *_chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData)
+{
+ struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip;
+
+ if (DataStart >= chip->RAMSize)
+ return;
+ if (DataStart + DataLength > chip->RAMSize)
+ DataLength = chip->RAMSize - DataStart;
+
+ memcpy(chip->RAM + (chip->Bank | DataStart), RAMData, DataLength);
+
+ return;
+}
+
+
+void rf5c164_set_mute_mask(void *_chip, UINT32 MuteMask)
+{
+ struct pcm_chip_ *chip = (struct pcm_chip_ *) _chip;
+ unsigned char CurChn;
+
+ for (CurChn = 0; CurChn < 8; CurChn ++)
+ chip->Channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
+
+ return;
+}
diff --git a/Frameworks/GME/gme/scd_pcm.h b/Frameworks/GME/gme/scd_pcm.h
new file mode 100644
index 000000000..aab658868
--- /dev/null
+++ b/Frameworks/GME/gme/scd_pcm.h
@@ -0,0 +1,57 @@
+#include "mamedef.h"
+
+struct pcm_chan_ {
+ unsigned int ENV; /* envelope register */
+ unsigned int PAN; /* pan register */
+ unsigned int MUL_L; /* envelope & pan product letf */
+ unsigned int MUL_R; /* envelope & pan product right */
+ unsigned int St_Addr; /* start address register */
+ unsigned int Loop_Addr; /* loop address register */
+ unsigned int Addr; /* current address register */
+ unsigned int Step; /* frequency register */
+ unsigned int Step_B; /* frequency register binaire */
+ unsigned int Enable; /* channel on/off register */
+ int Data; /* wave data */
+ unsigned int Muted;
+};
+
+struct pcm_chip_
+{
+ float Rate;
+ int Enable;
+ int Cur_Chan;
+ int Bank;
+
+ struct pcm_chan_ Channel[8];
+
+ unsigned long int RAMSize;
+ unsigned char* RAM;
+};
+
+//extern struct pcm_chip_ PCM_Chip;
+//extern unsigned char Ram_PCM[64 * 1024];
+//extern int PCM_Enable;
+
+//int PCM_Init(int Rate);
+//void PCM_Set_Rate(int Rate);
+//void PCM_Reset(void);
+//void PCM_Write_Reg(unsigned int Reg, unsigned int Data);
+//int PCM_Update(int **buf, int Length);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void rf5c164_update(void *chip, stream_sample_t **outputs, int samples);
+void * device_start_rf5c164(int clock);
+void device_stop_rf5c164(void *chip);
+void device_reset_rf5c164(void *chip);
+void rf5c164_w(void *chip, offs_t offset, UINT8 data);
+void rf5c164_mem_w(void *chip, offs_t offset, UINT8 data);
+void rf5c164_write_ram(void *chip, offs_t DataStart, offs_t DataLength, const UINT8* RAMData);
+
+void rf5c164_set_mute_mask(void *chip, UINT32 MuteMask);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Frameworks/GME/gme/segapcm.c b/Frameworks/GME/gme/segapcm.c
new file mode 100644
index 000000000..7370c369e
--- /dev/null
+++ b/Frameworks/GME/gme/segapcm.c
@@ -0,0 +1,384 @@
+/*********************************************************/
+/* SEGA 16ch 8bit PCM */
+/*********************************************************/
+
+#include
+#include
+#include
+//#include "sndintrf.h"
+//#include "streams.h"
+#include "segapcm.h"
+
+
+typedef struct _segapcm_state segapcm_state;
+struct _segapcm_state
+{
+ UINT8 *ram;
+ UINT8 low[16];
+ UINT32 ROMSize;
+ UINT8 *rom;
+#ifdef _DEBUG
+ UINT8 *romusage;
+#endif
+ int bankshift;
+ int bankmask;
+ int rgnmask;
+ sega_pcm_interface intf;
+ UINT8 Muted[16];
+ //sound_stream * stream;
+};
+
+UINT8 SegaPCM_NewCore = 0x00;
+
+/*INLINE segapcm_state *get_safe_token(const device_config *device)
+{
+ assert(device != NULL);
+ assert(device->token != NULL);
+ assert(device->type == SOUND);
+ assert(sound_get_type(device) == SOUND_SEGAPCM);
+ return (segapcm_state *)device->token;
+}*/
+
+//static STREAM_UPDATE( SEGAPCM_update )
+void SEGAPCM_update(void *chip, stream_sample_t **outputs, int samples)
+{
+ //segapcm_state *spcm = (segapcm_state *)param;
+ segapcm_state *spcm = (segapcm_state *) chip;
+ int rgnmask = spcm->rgnmask;
+ int ch;
+
+ /* clear the buffers */
+ memset(outputs[0], 0, samples*sizeof(stream_sample_t));
+ memset(outputs[1], 0, samples*sizeof(stream_sample_t));
+
+ // reg function
+ // ------------------------------------------------
+ // 0x00 ?
+ // 0x01 ?
+ // 0x02 volume left
+ // 0x03 volume right
+ // 0x04 loop address (08-15)
+ // 0x05 loop address (16-23)
+ // 0x06 end address
+ // 0x07 address delta
+ // 0x80 ?
+ // 0x81 ?
+ // 0x82 ?
+ // 0x83 ?
+ // 0x84 current address (08-15), 00-07 is internal?
+ // 0x85 current address (16-23)
+ // 0x86 bit 0: channel disable?
+ // bit 1: loop disable
+ // other bits: bank
+ // 0x87 ?
+
+ /* loop over channels */
+ for (ch = 0; ch < 16; ch++)
+ {
+if (! SegaPCM_NewCore)
+{
+ /* only process active channels */
+ if (!(spcm->ram[0x86+8*ch] & 1) && ! spcm->Muted[ch])
+ {
+ UINT8 *base = spcm->ram+8*ch;
+ UINT8 flags = base[0x86];
+ const UINT8 *rom = spcm->rom + ((flags & spcm->bankmask) << spcm->bankshift);
+#ifdef _DEBUG
+ const UINT8 *romusage = spcm->romusage + ((flags & spcm->bankmask) << spcm->bankshift);
+#endif
+ UINT32 addr = (base[5] << 16) | (base[4] << 8) | spcm->low[ch];
+ UINT16 loop = (base[0x85] << 8) | base[0x84];
+ UINT8 end = base[6] + 1;
+ UINT8 delta = base[7];
+ UINT8 voll = base[2];
+ UINT8 volr = base[3];
+ int i;
+
+ /* loop over samples on this channel */
+ for (i = 0; i < samples; i++)
+ {
+ INT8 v = 0;
+
+ /* handle looping if we've hit the end */
+ if ((addr >> 16) == end)
+ {
+ if (!(flags & 2))
+ addr = loop << 8;
+ else
+ {
+ flags |= 1;
+ break;
+ }
+ }
+
+ /* fetch the sample */
+ v = rom[(addr >> 8) & rgnmask] - 0x80;
+#ifdef _DEBUG
+ if (romusage[(addr >> 8) & rgnmask] == 0xFF && (voll || volr))
+ printf("Access to empty ROM section! (0x%06lX)\n",
+ ((flags & spcm->bankmask) << spcm->bankshift) + (addr >> 8) & rgnmask);
+#endif
+
+ /* apply panning and advance */
+ outputs[0][i] += v * voll;
+ outputs[1][i] += v * volr;
+ addr += delta;
+ }
+
+ /* store back the updated address and info */
+ base[0x86] = flags;
+ base[4] = addr >> 8;
+ base[5] = addr >> 16;
+ spcm->low[ch] = flags & 1 ? 0 : addr;
+ }
+}
+else
+{
+ UINT8 *regs = spcm->ram+8*ch;
+
+ /* only process active channels */
+ if (!(regs[0x86] & 1) && ! spcm->Muted[ch])
+ {
+ const UINT8 *rom = spcm->rom + ((regs[0x86] & spcm->bankmask) << spcm->bankshift);
+#ifdef _DEBUG
+ const UINT8 *romusage = spcm->romusage + ((regs[0x86] & spcm->bankmask) << spcm->bankshift);
+#endif
+ UINT32 addr = (regs[0x85] << 16) | (regs[0x84] << 8) | spcm->low[ch];
+ UINT32 loop = (regs[0x05] << 16) | (regs[0x04] << 8);
+ UINT8 end = regs[6] + 1;
+ int i;
+
+ /* loop over samples on this channel */
+ for (i = 0; i < samples; i++)
+ {
+ INT8 v = 0;
+
+ /* handle looping if we've hit the end */
+ if ((addr >> 16) == end)
+ {
+ if (regs[0x86] & 2)
+ {
+ regs[0x86] |= 1;
+ break;
+ }
+ else addr = loop;
+ }
+
+ /* fetch the sample */
+ v = rom[(addr >> 8) & rgnmask] - 0x80;
+#ifdef _DEBUG
+ if (romusage[(addr >> 8) & rgnmask] == 0xFF && (regs[2] || regs[3]))
+ printf("Access to empty ROM section! (0x%06lX)\n",
+ ((regs[0x86] & spcm->bankmask) << spcm->bankshift) + (addr >> 8) & rgnmask);
+#endif
+
+ /* apply panning and advance */
+ outputs[0][i] += v * regs[2];
+ outputs[1][i] += v * regs[3];
+ addr = (addr + regs[7]) & 0xffffff;
+ }
+
+ /* store back the updated address */
+ regs[0x84] = addr >> 8;
+ regs[0x85] = addr >> 16;
+ spcm->low[ch] = regs[0x86] & 1 ? 0 : addr;
+ }
+}
+ }
+}
+
+//static DEVICE_START( segapcm )
+void * device_start_segapcm(int intf_bank)
+{
+ const UINT32 STD_ROM_SIZE = 0x80000;
+ //const sega_pcm_interface *intf = (const sega_pcm_interface *)device->static_config;
+ sega_pcm_interface *intf;
+ int mask, rom_mask, len;
+ //segapcm_state *spcm = get_safe_token(device);
+ segapcm_state *spcm;
+
+ spcm = (segapcm_state *) malloc(sizeof(segapcm_state));
+ if (!spcm) return spcm;
+
+ intf = &spcm->intf;
+ intf->bank = intf_bank;
+
+ //spcm->rom = (const UINT8 *)device->region;
+ //spcm->ram = auto_alloc_array(device->machine, UINT8, 0x800);
+ spcm->ROMSize = STD_ROM_SIZE;
+ spcm->rom = (UINT8*) malloc(STD_ROM_SIZE);
+#ifdef _DEBUG
+ spcm->romusage = (UINT8*) malloc(STD_ROM_SIZE);
+#endif
+ spcm->ram = (UINT8*) malloc(0x800);
+
+ memset(spcm->rom, 0xFF, STD_ROM_SIZE);
+#ifdef _DEBUG
+ memset(spcm->romusage, 0xFF, STD_ROM_SIZE);
+#endif
+ //memset(spcm->ram, 0xff, 0x800); // RAM Clear is done at device_reset
+
+ spcm->bankshift = (UINT8)(intf->bank);
+ mask = intf->bank >> 16;
+ if(!mask)
+ mask = BANK_MASK7>>16;
+
+ len = STD_ROM_SIZE;
+ spcm->rgnmask = len - 1;
+ for(rom_mask = 1; rom_mask < len; rom_mask *= 2);
+ rom_mask--;
+
+ spcm->bankmask = mask & (rom_mask >> spcm->bankshift);
+
+ //spcm->stream = stream_create(device, 0, 2, device->clock / 128, spcm, SEGAPCM_update);
+
+ //state_save_register_device_item_array(device, 0, spcm->low);
+ //state_save_register_device_item_pointer(device, 0, spcm->ram, 0x800);
+
+ for (mask = 0; mask < 16; mask ++)
+ spcm->Muted[mask] = 0x00;
+
+ return spcm;
+}
+
+//static DEVICE_STOP( segapcm )
+void device_stop_segapcm(void *chip)
+{
+ //segapcm_state *spcm = get_safe_token(device);
+ segapcm_state *spcm = (segapcm_state *) chip;
+ free(spcm->rom); spcm->rom = NULL;
+#ifdef _DEBUG
+ free(spcm->romusage);
+#endif
+ free(spcm->ram);
+
+ free(spcm);
+}
+
+//static DEVICE_RESET( segapcm )
+void device_reset_segapcm(void *chip)
+{
+ //segapcm_state *spcm = get_safe_token(device);
+ segapcm_state *spcm = (segapcm_state *) chip;
+
+ memset(spcm->ram, 0xFF, 0x800);
+
+ return;
+}
+
+
+//WRITE8_DEVICE_HANDLER( sega_pcm_w )
+void sega_pcm_w(void *chip, offs_t offset, UINT8 data)
+{
+ //segapcm_state *spcm = get_safe_token(device);
+ segapcm_state *spcm = (segapcm_state *) chip;
+ //stream_update(spcm->stream);
+
+ spcm->ram[offset & 0x07ff] = data;
+}
+
+//READ8_DEVICE_HANDLER( sega_pcm_r )
+UINT8 sega_pcm_r(void *chip, offs_t offset)
+{
+ //segapcm_state *spcm = get_safe_token(device);
+ segapcm_state *spcm = (segapcm_state *) chip;
+ //stream_update(spcm->stream);
+ return spcm->ram[offset & 0x07ff];
+}
+
+void sega_pcm_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
+ const UINT8* ROMData)
+{
+ segapcm_state *spcm = (segapcm_state *) chip;
+
+ if (spcm->ROMSize != ROMSize)
+ {
+ unsigned long int mask, rom_mask;
+
+ spcm->rom = (UINT8*)realloc(spcm->rom, ROMSize);
+#ifdef _DEBUG
+ spcm->romusage = (UINT8*)realloc(spcm->romusage, ROMSize);
+#endif
+ spcm->ROMSize = ROMSize;
+ memset(spcm->rom, 0xFF, ROMSize);
+#ifdef _DEBUG
+ memset(spcm->romusage, 0xFF, ROMSize);
+#endif
+
+ // recalculate bankmask
+ mask = spcm->intf.bank >> 16;
+ if (! mask)
+ mask = BANK_MASK7 >> 16;
+
+ spcm->rgnmask = ROMSize - 1;
+ for (rom_mask = 1; rom_mask < ROMSize; rom_mask *= 2);
+ rom_mask --;
+
+ spcm->bankmask = mask & (rom_mask >> spcm->bankshift);
+ }
+ if (DataStart > ROMSize)
+ return;
+ if (DataStart + DataLength > ROMSize)
+ DataLength = ROMSize - DataStart;
+
+ memcpy(spcm->rom + DataStart, ROMData, DataLength);
+#ifdef _DEBUG
+ memset(spcm->romusage + DataStart, 0x00, DataLength);
+#endif
+
+ return;
+}
+
+
+/*void sega_pcm_fwrite_romusage(UINT8 ChipID)
+{
+ segapcm_state *spcm = &SPCMData[ChipID];
+
+ FILE* hFile;
+
+ hFile = fopen("SPCM_ROMUsage.bin", "wb");
+ if (hFile == NULL)
+ return;
+
+ fwrite(spcm->romusage, 0x01, spcm->ROMSize, hFile);
+
+ fclose(hFile);
+ return;
+}*/
+
+void segapcm_set_mute_mask(void *chip, UINT32 MuteMask)
+{
+ segapcm_state *spcm = (segapcm_state *) chip;
+ unsigned char CurChn;
+
+ for (CurChn = 0; CurChn < 16; CurChn ++)
+ spcm->Muted[CurChn] = (MuteMask >> CurChn) & 0x01;
+
+ return;
+}
+
+
+/**************************************************************************
+ * Generic get_info
+ **************************************************************************/
+
+/*DEVICE_GET_INFO( segapcm )
+{
+ switch (state)
+ {
+ // --- the following bits of info are returned as 64-bit signed integers ---
+ case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(segapcm_state); break;
+
+ // --- the following bits of info are returned as pointers to data or functions ---
+ case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( segapcm ); break;
+ case DEVINFO_FCT_STOP: // Nothing break;
+ case DEVINFO_FCT_RESET: // Nothing break;
+
+ // --- the following bits of info are returned as NULL-terminated strings ---
+ case DEVINFO_STR_NAME: strcpy(info->s, "Sega PCM"); break;
+ case DEVINFO_STR_FAMILY: strcpy(info->s, "Sega custom"); break;
+ case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
+ case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
+ case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
+ }
+}*/
diff --git a/Frameworks/GME/gme/segapcm.h b/Frameworks/GME/gme/segapcm.h
new file mode 100644
index 000000000..0053e1548
--- /dev/null
+++ b/Frameworks/GME/gme/segapcm.h
@@ -0,0 +1,48 @@
+/*********************************************************/
+/* SEGA 8bit PCM */
+/*********************************************************/
+
+#pragma once
+
+#include "mamedef.h"
+
+#define BANK_256 (11)
+#define BANK_512 (12)
+#define BANK_12M (13)
+#define BANK_MASK7 (0x70<<16)
+#define BANK_MASKF (0xf0<<16)
+#define BANK_MASKF8 (0xf8<<16)
+
+typedef struct _sega_pcm_interface sega_pcm_interface;
+struct _sega_pcm_interface
+{
+ int bank;
+};
+
+/*WRITE8_DEVICE_HANDLER( sega_pcm_w );
+READ8_DEVICE_HANDLER( sega_pcm_r );
+
+DEVICE_GET_INFO( segapcm );
+#define SOUND_SEGAPCM DEVICE_GET_INFO_NAME( segapcm )*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void SEGAPCM_update(void *chip, stream_sample_t **outputs, int samples);
+
+void * device_start_segapcm(int intf_bank);
+void device_stop_segapcm(void *chip);
+void device_reset_segapcm(void *chip);
+
+void sega_pcm_w(void *chip, offs_t offset, UINT8 data);
+UINT8 sega_pcm_r(void *chip, offs_t offset);
+void sega_pcm_write_rom(void *chip, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
+ const UINT8* ROMData);
+
+//void sega_pcm_fwrite_romusage(UINT8 ChipID);
+void segapcm_set_mute_mask(void *chip, UINT32 MuteMask);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/Frameworks/GME/gme/ym2151.c b/Frameworks/GME/gme/ym2151.c
new file mode 100644
index 000000000..342d1be4c
--- /dev/null
+++ b/Frameworks/GME/gme/ym2151.c
@@ -0,0 +1,2010 @@
+/*****************************************************************************
+*
+* Yamaha YM2151 driver (version 2.150 final beta)
+*
+******************************************************************************/
+
+#include "mathdefs.h"
+#include
+#include
+#include
+#include "mamedef.h"
+#include "ym2151.h"
+
+#ifndef logerror
+#define logerror (void)
+#endif
+
+
+/* struct describing a single operator */
+typedef struct
+{
+ UINT32 phase; /* accumulated operator phase */
+ UINT32 freq; /* operator frequency count */
+ INT32 dt1; /* current DT1 (detune 1 phase inc/decrement) value */
+ UINT32 mul; /* frequency count multiply */
+ UINT32 dt1_i; /* DT1 index * 32 */
+ UINT32 dt2; /* current DT2 (detune 2) value */
+
+ signed int *connect; /* operator output 'direction' */
+
+ /* only M1 (operator 0) is filled with this data: */
+ signed int *mem_connect; /* where to put the delayed sample (MEM) */
+ INT32 mem_value; /* delayed sample (MEM) value */
+
+ /* channel specific data; note: each operator number 0 contains channel specific data */
+ UINT32 fb_shift; /* feedback shift value for operators 0 in each channel */
+ INT32 fb_out_curr; /* operator feedback value (used only by operators 0) */
+ INT32 fb_out_prev; /* previous feedback value (used only by operators 0) */
+ UINT32 kc; /* channel KC (copied to all operators) */
+ UINT32 kc_i; /* just for speedup */
+ UINT32 pms; /* channel PMS */
+ UINT32 ams; /* channel AMS */
+ /* end of channel specific data */
+
+ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
+ UINT32 state; /* Envelope state: 4-attack(AR) 3-decay(D1R) 2-sustain(D2R) 1-release(RR) 0-off */
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT32 tl; /* Total attenuation Level */
+ INT32 volume; /* current envelope attenuation level */
+ UINT8 eg_sh_d1r; /* (decay state) */
+ UINT8 eg_sel_d1r; /* (decay state) */
+ UINT32 d1l; /* envelope switches to sustain state after reaching this level */
+ UINT8 eg_sh_d2r; /* (sustain state) */
+ UINT8 eg_sel_d2r; /* (sustain state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+
+ UINT32 key; /* 0=last key was KEY OFF, 1=last key was KEY ON */
+
+ UINT32 ks; /* key scale */
+ UINT32 ar; /* attack rate */
+ UINT32 d1r; /* decay rate */
+ UINT32 d2r; /* sustain rate */
+ UINT32 rr; /* release rate */
+
+ UINT32 reserved0; /**/
+ UINT32 reserved1; /**/
+
+} YM2151Operator;
+
+
+typedef struct
+{
+ signed int chanout[8];
+ signed int m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */
+ signed int mem; /* one sample delay memory */
+
+ YM2151Operator oper[32]; /* the 32 operators */
+
+ UINT32 pan[16]; /* channels output masks (0xffffffff = enable) */
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/64/3 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 3 samples (on real chip) */
+
+ UINT32 lfo_phase; /* accumulated LFO phase (0 to 255) */
+ UINT32 lfo_timer; /* LFO timer */
+ UINT32 lfo_timer_add; /* step of lfo_timer */
+ UINT32 lfo_overflow; /* LFO generates new output when lfo_timer reaches this value */
+ UINT32 lfo_counter; /* LFO phase increment counter */
+ UINT32 lfo_counter_add; /* step of lfo_counter */
+ UINT8 lfo_wsel; /* LFO waveform (0-saw, 1-square, 2-triangle, 3-random noise) */
+ UINT8 amd; /* LFO Amplitude Modulation Depth */
+ INT8 pmd; /* LFO Phase Modulation Depth */
+ UINT32 lfa; /* LFO current AM output */
+ INT32 lfp; /* LFO current PM output */
+
+ UINT8 test; /* TEST register */
+ UINT8 ct; /* output control pins (bit1-CT2, bit0-CT1) */
+
+ UINT32 noise; /* noise enable/period register (bit 7 - noise enable, bits 4-0 - noise period */
+ UINT32 noise_rng; /* 17 bit noise shift register */
+ UINT32 noise_p; /* current noise 'phase'*/
+ UINT32 noise_f; /* current noise period */
+
+ UINT32 csm_req; /* CSM KEY ON / KEY OFF sequence request */
+
+ UINT32 irq_enable; /* IRQ enable for timer B (bit 3) and timer A (bit 2); bit 7 - CSM mode (keyon to all slots, everytime timer A overflows) */
+ UINT32 status; /* chip status (BUSY, IRQ Flags) */
+ UINT8 connect[8]; /* channels connections */
+
+ /* Frequency-deltas to get the closest frequency possible.
+ * There are 11 octaves because of DT2 (max 950 cents over base frequency)
+ * and LFO phase modulation (max 800 cents below AND over base frequency)
+ * Summary: octave explanation
+ * 0 note code - LFO PM
+ * 1 note code
+ * 2 note code
+ * 3 note code
+ * 4 note code
+ * 5 note code
+ * 6 note code
+ * 7 note code
+ * 8 note code
+ * 9 note code + DT2 + LFO PM
+ * 10 note code + DT2 + LFO PM
+ */
+ UINT32 freq[11*768]; /* 11 octaves, 768 'cents' per octave */
+
+ /* Frequency deltas for DT1. These deltas alter operator frequency
+ * after it has been taken from frequency-deltas table.
+ */
+ INT32 dt1_freq[8*32]; /* 8 DT1 levels, 32 KC values */
+
+ UINT32 noise_tab[32]; /* 17bit Noise Generator periods */
+
+ unsigned int clock; /* chip clock in Hz (passed from 2151intf.c) */
+ unsigned int sampfreq; /* sampling frequency in Hz (passed from 2151intf.c) */
+
+ UINT32 mask;
+} YM2151;
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */
+#define LFO_SH 10 /* 22.10 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<>3)
+
+/* sin waveform table in 'decibel' scale */
+static unsigned int sin_tab[SIN_LEN];
+
+
+/* translate from D1L to volume index (16 D1L levels) */
+static UINT32 d1l_tab[16];
+
+
+#define RATE_STEPS (8)
+static const UINT8 eg_inc[19*RATE_STEPS]={
+
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */
+/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */
+/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */
+/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */
+
+/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */
+/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */
+/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(17) in this table - it's directly in the code */
+static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) */
+/* 32 dummy (infinite time) rates */
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+
+/* rates 00-11 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 12 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 13 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 14 */
+O(12),O(13),O(14),O(15),
+
+/* rate 15 */
+O(16),O(16),O(16),O(16),
+
+/* 32 dummy rates (same as 15 3) */
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16)
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/
+/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */
+/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */
+/* 32 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+
+/* rates 00-11 */
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 12 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 32 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0)
+
+};
+#undef O
+
+/* DT2 defines offset in cents from base note
+*
+* This table defines offset in frequency-deltas table.
+* User's Manual page 22
+*
+* Values below were calculated using formula: value = orig.val / 1.5625
+*
+* DT2=0 DT2=1 DT2=2 DT2=3
+* 0 600 781 950
+*/
+static const UINT32 dt2_tab[4] = { 0, 384, 500, 608 };
+
+/* DT1 defines offset in Hertz from base note
+* This table is converted while initialization...
+* Detune table shown in YM2151 User's Manual is wrong (verified on the real chip)
+*/
+
+static const UINT8 dt1_tab[4*32] = { /* 4*32 DT1 values */
+/* DT1=0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+
+/* DT1=1 */
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8,
+
+/* DT1=2 */
+ 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5,
+ 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16,
+
+/* DT1=3 */
+ 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7,
+ 8, 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22
+};
+
+static const UINT16 phaseinc_rom[768]={
+1299,1300,1301,1302,1303,1304,1305,1306,1308,1309,1310,1311,1313,1314,1315,1316,
+1318,1319,1320,1321,1322,1323,1324,1325,1327,1328,1329,1330,1332,1333,1334,1335,
+1337,1338,1339,1340,1341,1342,1343,1344,1346,1347,1348,1349,1351,1352,1353,1354,
+1356,1357,1358,1359,1361,1362,1363,1364,1366,1367,1368,1369,1371,1372,1373,1374,
+1376,1377,1378,1379,1381,1382,1383,1384,1386,1387,1388,1389,1391,1392,1393,1394,
+1396,1397,1398,1399,1401,1402,1403,1404,1406,1407,1408,1409,1411,1412,1413,1414,
+1416,1417,1418,1419,1421,1422,1423,1424,1426,1427,1429,1430,1431,1432,1434,1435,
+1437,1438,1439,1440,1442,1443,1444,1445,1447,1448,1449,1450,1452,1453,1454,1455,
+1458,1459,1460,1461,1463,1464,1465,1466,1468,1469,1471,1472,1473,1474,1476,1477,
+1479,1480,1481,1482,1484,1485,1486,1487,1489,1490,1492,1493,1494,1495,1497,1498,
+1501,1502,1503,1504,1506,1507,1509,1510,1512,1513,1514,1515,1517,1518,1520,1521,
+1523,1524,1525,1526,1528,1529,1531,1532,1534,1535,1536,1537,1539,1540,1542,1543,
+1545,1546,1547,1548,1550,1551,1553,1554,1556,1557,1558,1559,1561,1562,1564,1565,
+1567,1568,1569,1570,1572,1573,1575,1576,1578,1579,1580,1581,1583,1584,1586,1587,
+1590,1591,1592,1593,1595,1596,1598,1599,1601,1602,1604,1605,1607,1608,1609,1610,
+1613,1614,1615,1616,1618,1619,1621,1622,1624,1625,1627,1628,1630,1631,1632,1633,
+1637,1638,1639,1640,1642,1643,1645,1646,1648,1649,1651,1652,1654,1655,1656,1657,
+1660,1661,1663,1664,1666,1667,1669,1670,1672,1673,1675,1676,1678,1679,1681,1682,
+1685,1686,1688,1689,1691,1692,1694,1695,1697,1698,1700,1701,1703,1704,1706,1707,
+1709,1710,1712,1713,1715,1716,1718,1719,1721,1722,1724,1725,1727,1728,1730,1731,
+1734,1735,1737,1738,1740,1741,1743,1744,1746,1748,1749,1751,1752,1754,1755,1757,
+1759,1760,1762,1763,1765,1766,1768,1769,1771,1773,1774,1776,1777,1779,1780,1782,
+1785,1786,1788,1789,1791,1793,1794,1796,1798,1799,1801,1802,1804,1806,1807,1809,
+1811,1812,1814,1815,1817,1819,1820,1822,1824,1825,1827,1828,1830,1832,1833,1835,
+1837,1838,1840,1841,1843,1845,1846,1848,1850,1851,1853,1854,1856,1858,1859,1861,
+1864,1865,1867,1868,1870,1872,1873,1875,1877,1879,1880,1882,1884,1885,1887,1888,
+1891,1892,1894,1895,1897,1899,1900,1902,1904,1906,1907,1909,1911,1912,1914,1915,
+1918,1919,1921,1923,1925,1926,1928,1930,1932,1933,1935,1937,1939,1940,1942,1944,
+1946,1947,1949,1951,1953,1954,1956,1958,1960,1961,1963,1965,1967,1968,1970,1972,
+1975,1976,1978,1980,1982,1983,1985,1987,1989,1990,1992,1994,1996,1997,1999,2001,
+2003,2004,2006,2008,2010,2011,2013,2015,2017,2019,2021,2022,2024,2026,2028,2029,
+2032,2033,2035,2037,2039,2041,2043,2044,2047,2048,2050,2052,2054,2056,2058,2059,
+2062,2063,2065,2067,2069,2071,2073,2074,2077,2078,2080,2082,2084,2086,2088,2089,
+2092,2093,2095,2097,2099,2101,2103,2104,2107,2108,2110,2112,2114,2116,2118,2119,
+2122,2123,2125,2127,2129,2131,2133,2134,2137,2139,2141,2142,2145,2146,2148,2150,
+2153,2154,2156,2158,2160,2162,2164,2165,2168,2170,2172,2173,2176,2177,2179,2181,
+2185,2186,2188,2190,2192,2194,2196,2197,2200,2202,2204,2205,2208,2209,2211,2213,
+2216,2218,2220,2222,2223,2226,2227,2230,2232,2234,2236,2238,2239,2242,2243,2246,
+2249,2251,2253,2255,2256,2259,2260,2263,2265,2267,2269,2271,2272,2275,2276,2279,
+2281,2283,2285,2287,2288,2291,2292,2295,2297,2299,2301,2303,2304,2307,2308,2311,
+2315,2317,2319,2321,2322,2325,2326,2329,2331,2333,2335,2337,2338,2341,2342,2345,
+2348,2350,2352,2354,2355,2358,2359,2362,2364,2366,2368,2370,2371,2374,2375,2378,
+2382,2384,2386,2388,2389,2392,2393,2396,2398,2400,2402,2404,2407,2410,2411,2414,
+2417,2419,2421,2423,2424,2427,2428,2431,2433,2435,2437,2439,2442,2445,2446,2449,
+2452,2454,2456,2458,2459,2462,2463,2466,2468,2470,2472,2474,2477,2480,2481,2484,
+2488,2490,2492,2494,2495,2498,2499,2502,2504,2506,2508,2510,2513,2516,2517,2520,
+2524,2526,2528,2530,2531,2534,2535,2538,2540,2542,2544,2546,2549,2552,2553,2556,
+2561,2563,2565,2567,2568,2571,2572,2575,2577,2579,2581,2583,2586,2589,2590,2593
+};
+
+
+/*
+ Noise LFO waveform.
+
+ Here are just 256 samples out of much longer data.
+
+ It does NOT repeat every 256 samples on real chip and I wasnt able to find
+ the point where it repeats (even in strings as long as 131072 samples).
+
+ I only put it here because its better than nothing and perhaps
+ someone might be able to figure out the real algorithm.
+
+
+ Note that (due to the way the LFO output is calculated) it is quite
+ possible that two values: 0x80 and 0x00 might be wrong in this table.
+ To be exact:
+ some 0x80 could be 0x81 as well as some 0x00 could be 0x01.
+*/
+
+static const UINT8 lfo_noise_waveform[256] = {
+0xFF,0xEE,0xD3,0x80,0x58,0xDA,0x7F,0x94,0x9E,0xE3,0xFA,0x00,0x4D,0xFA,0xFF,0x6A,
+0x7A,0xDE,0x49,0xF6,0x00,0x33,0xBB,0x63,0x91,0x60,0x51,0xFF,0x00,0xD8,0x7F,0xDE,
+0xDC,0x73,0x21,0x85,0xB2,0x9C,0x5D,0x24,0xCD,0x91,0x9E,0x76,0x7F,0x20,0xFB,0xF3,
+0x00,0xA6,0x3E,0x42,0x27,0x69,0xAE,0x33,0x45,0x44,0x11,0x41,0x72,0x73,0xDF,0xA2,
+
+0x32,0xBD,0x7E,0xA8,0x13,0xEB,0xD3,0x15,0xDD,0xFB,0xC9,0x9D,0x61,0x2F,0xBE,0x9D,
+0x23,0x65,0x51,0x6A,0x84,0xF9,0xC9,0xD7,0x23,0xBF,0x65,0x19,0xDC,0x03,0xF3,0x24,
+0x33,0xB6,0x1E,0x57,0x5C,0xAC,0x25,0x89,0x4D,0xC5,0x9C,0x99,0x15,0x07,0xCF,0xBA,
+0xC5,0x9B,0x15,0x4D,0x8D,0x2A,0x1E,0x1F,0xEA,0x2B,0x2F,0x64,0xA9,0x50,0x3D,0xAB,
+
+0x50,0x77,0xE9,0xC0,0xAC,0x6D,0x3F,0xCA,0xCF,0x71,0x7D,0x80,0xA6,0xFD,0xFF,0xB5,
+0xBD,0x6F,0x24,0x7B,0x00,0x99,0x5D,0xB1,0x48,0xB0,0x28,0x7F,0x80,0xEC,0xBF,0x6F,
+0x6E,0x39,0x90,0x42,0xD9,0x4E,0x2E,0x12,0x66,0xC8,0xCF,0x3B,0x3F,0x10,0x7D,0x79,
+0x00,0xD3,0x1F,0x21,0x93,0x34,0xD7,0x19,0x22,0xA2,0x08,0x20,0xB9,0xB9,0xEF,0x51,
+
+0x99,0xDE,0xBF,0xD4,0x09,0x75,0xE9,0x8A,0xEE,0xFD,0xE4,0x4E,0x30,0x17,0xDF,0xCE,
+0x11,0xB2,0x28,0x35,0xC2,0x7C,0x64,0xEB,0x91,0x5F,0x32,0x0C,0x6E,0x00,0xF9,0x92,
+0x19,0xDB,0x8F,0xAB,0xAE,0xD6,0x12,0xC4,0x26,0x62,0xCE,0xCC,0x0A,0x03,0xE7,0xDD,
+0xE2,0x4D,0x8A,0xA6,0x46,0x95,0x0F,0x8F,0xF5,0x15,0x97,0x32,0xD4,0x28,0x1E,0x55
+};
+
+
+
+
+/* save output as raw 16-bit sample */
+/* #define SAVE_SAMPLE */
+/* #define SAVE_SEPARATE_CHANNELS */
+#if defined SAVE_SAMPLE || defined SAVE_SEPARATE_CHANNELS
+static FILE *sample[9];
+#endif
+
+
+
+
+static void init_tables(void)
+{
+ signed int i,x,n;
+ double o,m;
+
+ for (x=0; x>= 4; /* 12 bits here */
+ if (n&1) /* round to closest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 2; /* 13 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<13; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<13; i++)
+ logerror(", [%02i] %4i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ]);
+ logerror("\n");
+ #endif
+ }
+ /*logerror("TL_TAB_LEN = %i (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+ /*logerror("ENV_QUIET= %i\n",ENV_QUIET );*/
+
+
+ for (i=0; i0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to closest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+ /*logerror("sin [0x%4x]= %4i (tl_tab value=%8x)\n", i, sin_tab[i],tl_tab[sin_tab[i]]);*/
+ }
+
+
+ /* calculate d1l_tab table */
+ for (i=0; i<16; i++)
+ {
+ m = (i!=15 ? i : i+16) * (4.0/ENV_STEP); /* every 3 'dB' except for all bits = 1 = 45+48 'dB' */
+ d1l_tab[i] = m;
+ /*logerror("d1l_tab[%02x]=%08x\n",i,d1l_tab[i] );*/
+ }
+
+#ifdef SAVE_SAMPLE
+ sample[8]=fopen("sampsum.pcm","wb");
+#endif
+#ifdef SAVE_SEPARATE_CHANNELS
+ sample[0]=fopen("samp0.pcm","wb");
+ sample[1]=fopen("samp1.pcm","wb");
+ sample[2]=fopen("samp2.pcm","wb");
+ sample[3]=fopen("samp3.pcm","wb");
+ sample[4]=fopen("samp4.pcm","wb");
+ sample[5]=fopen("samp5.pcm","wb");
+ sample[6]=fopen("samp6.pcm","wb");
+ sample[7]=fopen("samp7.pcm","wb");
+#endif
+}
+
+
+static void init_chip_tables(YM2151 *chip)
+{
+ int i,j;
+ double mult,phaseinc,Hz;
+ double scaler;
+
+ scaler = ( (double)chip->clock / 64.0 ) / ( (double)chip->sampfreq );
+ if ( fabs( scaler - 1.0 ) < 0.0000001 ) scaler = 1.0;
+ /*logerror("scaler = %20.15f\n", scaler);*/
+
+
+ /* this loop calculates Hertz values for notes from c-0 to b-7 */
+ /* including 64 'cents' (100/64 that is 1.5625 of real cent) per note */
+ /* i*100/64/1200 is equal to i/768 */
+
+ /* real chip works with 10 bits fixed point values (10.10) */
+ mult = (1<<(FREQ_SH-10)); /* -10 because phaseinc_rom table values are already in 10.10 format */
+
+ for (i=0; i<768; i++)
+ {
+ /* 3.4375 Hz is note A; C# is 4 semitones higher */
+ Hz = 1000;
+#if 0
+/* Hz is close, but not perfect */
+ //Hz = scaler * 3.4375 * pow (2, (i + 4 * 64 ) / 768.0 );
+ /* calculate phase increment */
+ phaseinc = (Hz*SIN_LEN) / (double)chip->sampfreq;
+#endif
+
+ phaseinc = phaseinc_rom[i]; /* real chip phase increment */
+ phaseinc *= scaler; /* adjust */
+
+
+ /* octave 2 - reference octave */
+ chip->freq[ 768+2*768+i ] = ((int)(phaseinc*mult)) & 0xffffffc0; /* adjust to X.10 fixed point */
+ /* octave 0 and octave 1 */
+ for (j=0; j<2; j++)
+ {
+ chip->freq[768 + j*768 + i] = (chip->freq[ 768+2*768+i ] >> (2-j) ) & 0xffffffc0; /* adjust to X.10 fixed point */
+ }
+ /* octave 3 to 7 */
+ for (j=3; j<8; j++)
+ {
+ chip->freq[768 + j*768 + i] = chip->freq[ 768+2*768+i ] << (j-2);
+ }
+ }
+
+ /* octave -1 (all equal to: oct 0, _KC_00_, _KF_00_) */
+ for (i=0; i<768; i++)
+ {
+ chip->freq[ 0*768 + i ] = chip->freq[1*768+0];
+ }
+
+ /* octave 8 and 9 (all equal to: oct 7, _KC_14_, _KF_63_) */
+ for (j=8; j<10; j++)
+ {
+ for (i=0; i<768; i++)
+ {
+ chip->freq[768+ j*768 + i ] = chip->freq[768 + 8*768 -1];
+ }
+ }
+
+ mult = (1<clock/64.0) ) / (double)(1<<20);
+
+ /*calculate phase increment*/
+ phaseinc = (Hz*SIN_LEN) / (double)chip->sampfreq;
+
+ /*positive and negative values*/
+ chip->dt1_freq[ (j+0)*32 + i ] = phaseinc * mult;
+ chip->dt1_freq[ (j+4)*32 + i ] = -chip->dt1_freq[ (j+0)*32 + i ];
+
+#if 0
+ {
+ int x = j*32 + i;
+ pom = (double)chip->dt1_freq[x] / mult;
+ pom = pom * (double)chip->sampfreq / (double)SIN_LEN;
+ logerror("DT1(%03i)[%02i %02i][%08x]= real %19.15f Hz emul %19.15f Hz\n",
+ x, j, i, chip->dt1_freq[x], Hz, pom);
+ }
+#endif
+ }
+ }
+
+
+ /* calculate noise periods table */
+ scaler = ( (double)chip->clock / 64.0 ) / ( (double)chip->sampfreq );
+ if ( fabs( scaler - 1.0 ) < 0.0000001 ) scaler = 1.0;
+ for (i=0; i<32; i++)
+ {
+ j = (i!=31 ? i : 30); /* rate 30 and 31 are the same */
+ j = 32-j;
+ j = (65536.0 / (double)(j*32.0)); /* number of samples per one shift of the shift register */
+ /*chip->noise_tab[i] = j * 64;*/ /* number of chip clock cycles per one shift */
+ chip->noise_tab[i] = j * 64 * scaler;
+ /*logerror("noise_tab[%02x]=%08x\n", i, chip->noise_tab[i]);*/
+ }
+}
+
+#define KEY_ON(op, key_set){ \
+ if (!(op)->key) \
+ { \
+ (op)->phase = 0; /* clear phase */ \
+ (op)->state = EG_ATT; /* KEY ON = attack */ \
+ (op)->volume += (~(op)->volume * \
+ (eg_inc[(op)->eg_sel_ar + ((PSG->eg_cnt>>(op)->eg_sh_ar)&7)]) \
+ ) >>4; \
+ if ((op)->volume <= MIN_ATT_INDEX) \
+ { \
+ (op)->volume = MIN_ATT_INDEX; \
+ (op)->state = EG_DEC; \
+ } \
+ } \
+ (op)->key |= key_set; \
+}
+
+#define KEY_OFF(op, key_clr){ \
+ if ((op)->key) \
+ { \
+ (op)->key &= key_clr; \
+ if (!(op)->key) \
+ { \
+ if ((op)->state>EG_REL) \
+ (op)->state = EG_REL;/* KEY OFF = release */\
+ } \
+ } \
+}
+
+INLINE void envelope_KONKOFF(YM2151 *PSG, YM2151Operator * op, int v)
+{
+ if (v&0x08) /* M1 */
+ KEY_ON (op+0, 1)
+ else
+ KEY_OFF(op+0,~1)
+
+ if (v&0x20) /* M2 */
+ KEY_ON (op+1, 1)
+ else
+ KEY_OFF(op+1,~1)
+
+ if (v&0x10) /* C1 */
+ KEY_ON (op+2, 1)
+ else
+ KEY_OFF(op+2,~1)
+
+ if (v&0x40) /* C2 */
+ KEY_ON (op+3, 1)
+ else
+ KEY_OFF(op+3,~1)
+}
+
+
+INLINE void set_connect(YM2151 *PSG, YM2151Operator *om1, int cha, int v)
+{
+ YM2151Operator *om2 = om1+1;
+ YM2151Operator *oc1 = om1+2;
+
+ /* set connect algorithm */
+
+ /* MEM is simply one sample delay */
+
+ switch( v&7 )
+ {
+ case 0:
+ /* M1---C1---MEM---M2---C2---OUT */
+ om1->connect = &PSG->c1;
+ oc1->connect = &PSG->mem;
+ om2->connect = &PSG->c2;
+ om1->mem_connect = &PSG->m2;
+ break;
+
+ case 1:
+ /* M1------+-MEM---M2---C2---OUT */
+ /* C1-+ */
+ om1->connect = &PSG->mem;
+ oc1->connect = &PSG->mem;
+ om2->connect = &PSG->c2;
+ om1->mem_connect = &PSG->m2;
+ break;
+
+ case 2:
+ /* M1-----------------+-C2---OUT */
+ /* C1---MEM---M2-+ */
+ om1->connect = &PSG->c2;
+ oc1->connect = &PSG->mem;
+ om2->connect = &PSG->c2;
+ om1->mem_connect = &PSG->m2;
+ break;
+
+ case 3:
+ /* M1---C1---MEM------+-C2---OUT */
+ /* M2-+ */
+ om1->connect = &PSG->c1;
+ oc1->connect = &PSG->mem;
+ om2->connect = &PSG->c2;
+ om1->mem_connect = &PSG->c2;
+ break;
+
+ case 4:
+ /* M1---C1-+-OUT */
+ /* M2---C2-+ */
+ /* MEM: not used */
+ om1->connect = &PSG->c1;
+ oc1->connect = &PSG->chanout[cha];
+ om2->connect = &PSG->c2;
+ om1->mem_connect = &PSG->mem; /* store it anywhere where it will not be used */
+ break;
+
+ case 5:
+ /* +----C1----+ */
+ /* M1-+-MEM---M2-+-OUT */
+ /* +----C2----+ */
+ om1->connect = 0; /* special mark */
+ oc1->connect = &PSG->chanout[cha];
+ om2->connect = &PSG->chanout[cha];
+ om1->mem_connect = &PSG->m2;
+ break;
+
+ case 6:
+ /* M1---C1-+ */
+ /* M2-+-OUT */
+ /* C2-+ */
+ /* MEM: not used */
+ om1->connect = &PSG->c1;
+ oc1->connect = &PSG->chanout[cha];
+ om2->connect = &PSG->chanout[cha];
+ om1->mem_connect = &PSG->mem; /* store it anywhere where it will not be used */
+ break;
+
+ case 7:
+ /* M1-+ */
+ /* C1-+-OUT */
+ /* M2-+ */
+ /* C2-+ */
+ /* MEM: not used*/
+ om1->connect = &PSG->chanout[cha];
+ oc1->connect = &PSG->chanout[cha];
+ om2->connect = &PSG->chanout[cha];
+ om1->mem_connect = &PSG->mem; /* store it anywhere where it will not be used */
+ break;
+ }
+}
+
+
+INLINE void refresh_EG(YM2151Operator * op)
+{
+ UINT32 kc;
+ UINT32 v;
+
+ kc = op->kc;
+
+ /* v = 32 + 2*RATE + RKS = max 126 */
+
+ v = kc >> op->ks;
+ if ((op->ar+v) < 32+62)
+ {
+ op->eg_sh_ar = eg_rate_shift [op->ar + v ];
+ op->eg_sel_ar = eg_rate_select[op->ar + v ];
+ }
+ else
+ {
+ op->eg_sh_ar = 0;
+ op->eg_sel_ar = 17*RATE_STEPS;
+ }
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + v];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + v];
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + v];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + v];
+ op->eg_sh_rr = eg_rate_shift [op->rr + v];
+ op->eg_sel_rr = eg_rate_select[op->rr + v];
+
+
+ op+=1;
+
+ v = kc >> op->ks;
+ if ((op->ar+v) < 32+62)
+ {
+ op->eg_sh_ar = eg_rate_shift [op->ar + v ];
+ op->eg_sel_ar = eg_rate_select[op->ar + v ];
+ }
+ else
+ {
+ op->eg_sh_ar = 0;
+ op->eg_sel_ar = 17*RATE_STEPS;
+ }
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + v];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + v];
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + v];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + v];
+ op->eg_sh_rr = eg_rate_shift [op->rr + v];
+ op->eg_sel_rr = eg_rate_select[op->rr + v];
+
+ op+=1;
+
+ v = kc >> op->ks;
+ if ((op->ar+v) < 32+62)
+ {
+ op->eg_sh_ar = eg_rate_shift [op->ar + v ];
+ op->eg_sel_ar = eg_rate_select[op->ar + v ];
+ }
+ else
+ {
+ op->eg_sh_ar = 0;
+ op->eg_sel_ar = 17*RATE_STEPS;
+ }
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + v];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + v];
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + v];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + v];
+ op->eg_sh_rr = eg_rate_shift [op->rr + v];
+ op->eg_sel_rr = eg_rate_select[op->rr + v];
+
+ op+=1;
+
+ v = kc >> op->ks;
+ if ((op->ar+v) < 32+62)
+ {
+ op->eg_sh_ar = eg_rate_shift [op->ar + v ];
+ op->eg_sel_ar = eg_rate_select[op->ar + v ];
+ }
+ else
+ {
+ op->eg_sh_ar = 0;
+ op->eg_sel_ar = 17*RATE_STEPS;
+ }
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + v];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + v];
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + v];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + v];
+ op->eg_sh_rr = eg_rate_shift [op->rr + v];
+ op->eg_sel_rr = eg_rate_select[op->rr + v];
+}
+
+
+/* write a register on YM2151 chip number 'n' */
+void ym2151_write_reg(void *_chip, int r, int v)
+{
+ YM2151 *chip = (YM2151 *)_chip;
+ YM2151Operator *op = &chip->oper[ (r&0x07)*4+((r&0x18)>>3) ];
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+#if 0
+ /* There is no info on what YM2151 really does when busy flag is set */
+ if ( chip->status & 0x80 ) return;
+ timer_set ( attotime::from_hz(chip->clock) * 64, chip, 0, timer_callback_chip_busy);
+ chip->status |= 0x80; /* set busy flag for 64 chip clock cycles */
+#endif
+
+
+ switch(r & 0xe0)
+ {
+ case 0x00:
+ switch(r){
+ case 0x01: /* LFO reset(bit 1), Test Register (other bits) */
+ chip->test = v;
+ if (v&2) chip->lfo_phase = 0;
+ break;
+
+ case 0x08:
+ envelope_KONKOFF(chip, &chip->oper[ (v&7)*4 ], v );
+ break;
+
+ case 0x0f: /* noise mode enable, noise period */
+ chip->noise = v;
+ chip->noise_f = chip->noise_tab[ v & 0x1f ];
+ break;
+
+ case 0x10: /* timer A hi */
+ break;
+
+ case 0x11: /* timer A low */
+ break;
+
+ case 0x12: /* timer B */
+ break;
+
+ case 0x14: /* CSM, irq flag reset, irq enable, timer start/stop */
+
+ chip->irq_enable = v; /* bit 3-timer B, bit 2-timer A, bit 7 - CSM */
+
+ break;
+
+ case 0x18: /* LFO frequency */
+ {
+ chip->lfo_overflow = ( 1 << ((15-(v>>4))+3) ) * (1<lfo_counter_add = 0x10 + (v & 0x0f);
+ }
+ break;
+
+ case 0x19: /* PMD (bit 7==1) or AMD (bit 7==0) */
+ if (v&0x80)
+ chip->pmd = v & 0x7f;
+ else
+ chip->amd = v & 0x7f;
+ break;
+
+ case 0x1b: /* CT2, CT1, LFO waveform */
+ chip->ct = v >> 6;
+ chip->lfo_wsel = v & 3;
+ break;
+
+ default:
+ logerror("YM2151 Write %02x to undocumented register #%02x\n",v,r);
+ break;
+ }
+ break;
+
+ case 0x20:
+ op = &chip->oper[ (r&7) * 4 ];
+ switch(r & 0x18)
+ {
+ case 0x00: /* RL enable, Feedback, Connection */
+ op->fb_shift = ((v>>3)&7) ? ((v>>3)&7)+6:0;
+ chip->pan[ (r&7)*2 ] = (v & 0x40) ? ~0 : 0;
+ chip->pan[ (r&7)*2 +1 ] = (v & 0x80) ? ~0 : 0;
+ chip->connect[r&7] = v&7;
+ set_connect(chip, op, r&7, v&7);
+ break;
+
+ case 0x08: /* Key Code */
+ v &= 0x7f;
+ if (v != (int)(op->kc))
+ {
+ UINT32 kc, kc_channel;
+
+ kc_channel = (v - (v>>2))*64;
+ kc_channel += 768;
+ kc_channel |= (op->kc_i & 63);
+
+ (op+0)->kc = v;
+ (op+0)->kc_i = kc_channel;
+ (op+1)->kc = v;
+ (op+1)->kc_i = kc_channel;
+ (op+2)->kc = v;
+ (op+2)->kc_i = kc_channel;
+ (op+3)->kc = v;
+ (op+3)->kc_i = kc_channel;
+
+ kc = v>>2;
+
+ (op+0)->dt1 = chip->dt1_freq[ (op+0)->dt1_i + kc ];
+ (op+0)->freq = ( (chip->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1;
+
+ (op+1)->dt1 = chip->dt1_freq[ (op+1)->dt1_i + kc ];
+ (op+1)->freq = ( (chip->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1;
+
+ (op+2)->dt1 = chip->dt1_freq[ (op+2)->dt1_i + kc ];
+ (op+2)->freq = ( (chip->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1;
+
+ (op+3)->dt1 = chip->dt1_freq[ (op+3)->dt1_i + kc ];
+ (op+3)->freq = ( (chip->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1;
+
+ refresh_EG( op );
+ }
+ break;
+
+ case 0x10: /* Key Fraction */
+ v >>= 2;
+ if (v != (int)(op->kc_i & 63))
+ {
+ UINT32 kc_channel;
+
+ kc_channel = v;
+ kc_channel |= (op->kc_i & ~63);
+
+ (op+0)->kc_i = kc_channel;
+ (op+1)->kc_i = kc_channel;
+ (op+2)->kc_i = kc_channel;
+ (op+3)->kc_i = kc_channel;
+
+ (op+0)->freq = ( (chip->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1;
+ (op+1)->freq = ( (chip->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1;
+ (op+2)->freq = ( (chip->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1;
+ (op+3)->freq = ( (chip->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1;
+ }
+ break;
+
+ case 0x18: /* PMS, AMS */
+ op->pms = (v>>4) & 7;
+ op->ams = (v & 3);
+ break;
+ }
+ break;
+
+ case 0x40: /* DT1, MUL */
+ {
+ UINT32 olddt1_i = op->dt1_i;
+ UINT32 oldmul = op->mul;
+
+ op->dt1_i = (v&0x70)<<1;
+ op->mul = (v&0x0f) ? (v&0x0f)<<1: 1;
+
+ if (olddt1_i != op->dt1_i)
+ op->dt1 = chip->dt1_freq[ op->dt1_i + (op->kc>>2) ];
+
+ if ( (olddt1_i != op->dt1_i) || (oldmul != op->mul) )
+ op->freq = ( (chip->freq[ op->kc_i + op->dt2 ] + op->dt1) * op->mul ) >> 1;
+ }
+ break;
+
+ case 0x60: /* TL */
+ op->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */
+ break;
+
+ case 0x80: /* KS, AR */
+ {
+ UINT32 oldks = op->ks;
+ UINT32 oldar = op->ar;
+
+ op->ks = 5-(v>>6);
+ op->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ if ( (op->ar != oldar) || (op->ks != oldks) )
+ {
+ if ((op->ar + (op->kc>>op->ks)) < 32+62)
+ {
+ op->eg_sh_ar = eg_rate_shift [op->ar + (op->kc>>op->ks) ];
+ op->eg_sel_ar = eg_rate_select[op->ar + (op->kc>>op->ks) ];
+ }
+ else
+ {
+ op->eg_sh_ar = 0;
+ op->eg_sel_ar = 17*RATE_STEPS;
+ }
+ }
+
+ if (op->ks != oldks)
+ {
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc>>op->ks) ];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + (op->kc>>op->ks) ];
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc>>op->ks) ];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + (op->kc>>op->ks) ];
+ op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc>>op->ks) ];
+ op->eg_sel_rr = eg_rate_select[op->rr + (op->kc>>op->ks) ];
+ }
+ }
+ break;
+
+ case 0xa0: /* LFO AM enable, D1R */
+ op->AMmask = (v&0x80) ? ~0 : 0;
+ op->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+ op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc>>op->ks) ];
+ op->eg_sel_d1r= eg_rate_select[op->d1r + (op->kc>>op->ks) ];
+ break;
+
+ case 0xc0: /* DT2, D2R */
+ {
+ UINT32 olddt2 = op->dt2;
+ op->dt2 = dt2_tab[ v>>6 ];
+ if (op->dt2 != olddt2)
+ op->freq = ( (chip->freq[ op->kc_i + op->dt2 ] + op->dt1) * op->mul ) >> 1;
+ }
+ op->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+ op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc>>op->ks) ];
+ op->eg_sel_d2r= eg_rate_select[op->d2r + (op->kc>>op->ks) ];
+ break;
+
+ case 0xe0: /* D1L, RR */
+ op->d1l = d1l_tab[ v>>4 ];
+ op->rr = 34 + ((v&0x0f)<<2);
+ op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc>>op->ks) ];
+ op->eg_sel_rr = eg_rate_select[op->rr + (op->kc>>op->ks) ];
+ break;
+ }
+}
+
+
+/*
+* Initialize YM2151 emulator(s).
+*
+* 'num' is the number of virtual YM2151's to allocate
+* 'clock' is the chip clock in Hz
+* 'rate' is sampling rate
+*/
+void * ym2151_init(int clock, int rate)
+{
+ YM2151 *PSG;
+
+ PSG = (YM2151 *) malloc(sizeof(YM2151));
+
+ memset(PSG, 0, sizeof(YM2151));
+
+ init_tables();
+
+ PSG->clock = clock;
+ /*rate = clock/64;*/
+ PSG->sampfreq = rate ? rate : 44100; /* avoid division by 0 in init_chip_tables() */
+ init_chip_tables( PSG );
+
+ PSG->lfo_timer_add = (1<sampfreq;
+
+ PSG->eg_timer_add = (1<sampfreq;
+ PSG->eg_timer_overflow = ( 3 ) * (1<eg_timer_add, PSG->eg_timer_overflow);*/
+
+ ym2151_reset_chip(PSG);
+ /*logerror("YM2151[init] clock=%i sampfreq=%i\n", PSG->clock, PSG->sampfreq);*/
+
+ return PSG;
+}
+
+
+
+void ym2151_shutdown(void *_chip)
+{
+ YM2151 *chip = (YM2151 *)_chip;
+
+ free (chip);
+
+#ifdef SAVE_SAMPLE
+ fclose(sample[8]);
+#endif
+#ifdef SAVE_SEPARATE_CHANNELS
+ fclose(sample[0]);
+ fclose(sample[1]);
+ fclose(sample[2]);
+ fclose(sample[3]);
+ fclose(sample[4]);
+ fclose(sample[5]);
+ fclose(sample[6]);
+ fclose(sample[7]);
+#endif
+}
+
+
+
+/*
+* Reset chip number 'n'.
+*/
+void ym2151_reset_chip(void *_chip)
+{
+ int i;
+ YM2151 *chip = (YM2151 *)_chip;
+
+
+ /* initialize hardware registers */
+ for (i=0; i<32; i++)
+ {
+ memset(&chip->oper[i],'\0',sizeof(YM2151Operator));
+ chip->oper[i].volume = MAX_ATT_INDEX;
+ chip->oper[i].kc_i = 768; /* min kc_i value */
+ }
+
+ chip->eg_timer = 0;
+ chip->eg_cnt = 0;
+
+ chip->lfo_timer = 0;
+ chip->lfo_counter= 0;
+ chip->lfo_phase = 0;
+ chip->lfo_wsel = 0;
+ chip->pmd = 0;
+ chip->amd = 0;
+ chip->lfa = 0;
+ chip->lfp = 0;
+
+ chip->test= 0;
+
+ chip->irq_enable = 0;
+
+ chip->noise = 0;
+ chip->noise_rng = 0;
+ chip->noise_p = 0;
+ chip->noise_f = chip->noise_tab[0];
+
+ chip->csm_req = 0;
+ chip->status = 0;
+
+ ym2151_write_reg(chip, 0x1b, 0); /* only because of CT1, CT2 output pins */
+ ym2151_write_reg(chip, 0x18, 0); /* set LFO frequency */
+ for (i=0x20; i<0x100; i++) /* set the operators */
+ {
+ ym2151_write_reg(chip, i, 0);
+ }
+}
+
+
+
+INLINE signed int op_calc(YM2151Operator * OP, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((OP->phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(YM2151Operator * OP, unsigned int env, signed int pm)
+{
+ UINT32 p;
+ INT32 i;
+
+
+ i = (OP->phase & ~FREQ_MASK) + pm;
+
+/*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, OP->phase>>FREQ_SH, pm);*/
+
+ p = (env<<3) + sin_tab[ (i>>FREQ_SH) & SIN_MASK];
+
+/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+
+ return tl_tab[p];
+}
+
+
+
+#define volume_calc(OP) ((OP)->tl + ((UINT32)(OP)->volume) + (AM & (OP)->AMmask))
+
+INLINE void chan_calc(YM2151 *PSG, unsigned int chan)
+{
+ YM2151Operator *op;
+ unsigned int env;
+ UINT32 AM = 0;
+
+ PSG->m2 = PSG->c1 = PSG->c2 = PSG->mem = 0;
+ op = &PSG->oper[chan*4]; /* M1 */
+
+ *op->mem_connect = op->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
+
+ if (op->ams)
+ AM = PSG->lfa << (op->ams-1);
+ env = volume_calc(op);
+ {
+ INT32 out = op->fb_out_prev + op->fb_out_curr;
+ op->fb_out_prev = op->fb_out_curr;
+
+ if (!op->connect)
+ /* algorithm 5 */
+ PSG->mem = PSG->c1 = PSG->c2 = op->fb_out_prev;
+ else
+ /* other algorithms */
+ *op->connect = op->fb_out_prev;
+
+ op->fb_out_curr = 0;
+ if (env < ENV_QUIET)
+ {
+ if (!op->fb_shift)
+ out=0;
+ op->fb_out_curr = op_calc1(op, env, (out<fb_shift) );
+ }
+ }
+
+ env = volume_calc(op+1); /* M2 */
+ if (env < ENV_QUIET)
+ *(op+1)->connect += op_calc(op+1, env, PSG->m2);
+
+ env = volume_calc(op+2); /* C1 */
+ if (env < ENV_QUIET)
+ *(op+2)->connect += op_calc(op+2, env, PSG->c1);
+
+ env = volume_calc(op+3); /* C2 */
+ if (env < ENV_QUIET)
+ PSG->chanout[chan] += op_calc(op+3, env, PSG->c2);
+
+ /* M1 */
+ op->mem_value = PSG->mem;
+}
+
+INLINE void chan7_calc(YM2151 *PSG)
+{
+ YM2151Operator *op;
+ unsigned int env;
+ UINT32 AM = 0;
+
+ PSG->m2 = PSG->c1 = PSG->c2 = PSG->mem = 0;
+ op = &PSG->oper[7*4]; /* M1 */
+
+ *op->mem_connect = op->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
+
+ if (op->ams)
+ AM = PSG->lfa << (op->ams-1);
+ env = volume_calc(op);
+ {
+ INT32 out = op->fb_out_prev + op->fb_out_curr;
+ op->fb_out_prev = op->fb_out_curr;
+
+ if (!op->connect)
+ /* algorithm 5 */
+ PSG->mem = PSG->c1 = PSG->c2 = op->fb_out_prev;
+ else
+ /* other algorithms */
+ *op->connect = op->fb_out_prev;
+
+ op->fb_out_curr = 0;
+ if (env < ENV_QUIET)
+ {
+ if (!op->fb_shift)
+ out=0;
+ op->fb_out_curr = op_calc1(op, env, (out<fb_shift) );
+ }
+ }
+
+ env = volume_calc(op+1); /* M2 */
+ if (env < ENV_QUIET)
+ *(op+1)->connect += op_calc(op+1, env, PSG->m2);
+
+ env = volume_calc(op+2); /* C1 */
+ if (env < ENV_QUIET)
+ *(op+2)->connect += op_calc(op+2, env, PSG->c1);
+
+ env = volume_calc(op+3); /* C2 */
+ if (PSG->noise & 0x80)
+ {
+ UINT32 noiseout;
+
+ noiseout = 0;
+ if (env < 0x3ff)
+ noiseout = (env ^ 0x3ff) * 2; /* range of the YM2151 noise output is -2044 to 2040 */
+ PSG->chanout[7] += ((PSG->noise_rng&0x10000) ? noiseout: -noiseout); /* bit 16 -> output */
+ }
+ else
+ {
+ if (env < ENV_QUIET)
+ PSG->chanout[7] += op_calc(op+3, env, PSG->c2);
+ }
+ /* M1 */
+ op->mem_value = PSG->mem;
+}
+
+
+
+
+
+
+/*
+The 'rate' is calculated from following formula (example on decay rate):
+ rks = notecode after key scaling (a value from 0 to 31)
+ DR = value written to the chip register
+ rate = 2*DR + rks; (max rate = 2*31+31 = 93)
+Four MSBs of the 'rate' above are the 'main' rate (from 00 to 15)
+Two LSBs of the 'rate' above are the value 'x' (the shape type).
+(eg. '11 2' means that 'rate' is 11*4+2=46)
+
+NOTE: A 'sample' in the description below is actually 3 output samples,
+thats because the Envelope Generator clock is equal to internal_clock/3.
+
+Single '-' (minus) character in the diagrams below represents one sample
+on the output; this is for rates 11 x (11 0, 11 1, 11 2 and 11 3)
+
+these 'main' rates:
+00 x: single '-' = 2048 samples; (ie. level can change every 2048 samples)
+01 x: single '-' = 1024 samples;
+02 x: single '-' = 512 samples;
+03 x: single '-' = 256 samples;
+04 x: single '-' = 128 samples;
+05 x: single '-' = 64 samples;
+06 x: single '-' = 32 samples;
+07 x: single '-' = 16 samples;
+08 x: single '-' = 8 samples;
+09 x: single '-' = 4 samples;
+10 x: single '-' = 2 samples;
+11 x: single '-' = 1 sample; (ie. level can change every 1 sample)
+
+Shapes for rates 11 x look like this:
+rate: step:
+11 0 01234567
+
+level:
+0 --
+1 --
+2 --
+3 --
+
+rate: step:
+11 1 01234567
+
+level:
+0 --
+1 --
+2 -
+3 -
+4 --
+
+rate: step:
+11 2 01234567
+
+level:
+0 --
+1 -
+2 -
+3 --
+4 -
+5 -
+
+rate: step:
+11 3 01234567
+
+level:
+0 --
+1 -
+2 -
+3 -
+4 -
+5 -
+6 -
+
+
+For rates 12 x, 13 x, 14 x and 15 x output level changes on every
+sample - this means that the waveform looks like this: (but the level
+changes by different values on different steps)
+12 3 01234567
+
+0 -
+2 -
+4 -
+8 -
+10 -
+12 -
+14 -
+18 -
+20 -
+
+Notes about the timing:
+----------------------
+
+1. Synchronism
+
+Output level of each two (or more) voices running at the same 'main' rate
+(eg 11 0 and 11 1 in the diagram below) will always be changing in sync,
+even if there're started with some delay.
+
+Note that, in the diagram below, the decay phase in channel 0 starts at
+sample #2, while in channel 1 it starts at sample #6. Anyway, both channels
+will always change their levels at exactly the same (following) samples.
+
+(S - start point of this channel, A-attack phase, D-decay phase):
+
+step:
+01234567012345670123456
+
+channel 0:
+ --
+ | --
+ | -
+ | -
+ | --
+ | --
+| --
+| -
+| -
+| --
+AADDDDDDDDDDDDDDDD
+S
+
+01234567012345670123456
+channel 1:
+ -
+ | -
+ | --
+ | --
+ | --
+ | -
+ | -
+ | --
+ | --
+ | --
+ AADDDDDDDDDDDDDDDD
+ S
+01234567012345670123456
+
+
+2. Shifted (delayed) synchronism
+
+Output of each two (or more) voices running at different 'main' rate
+(9 1, 10 1 and 11 1 in the diagrams below) will always be changing
+in 'delayed-sync' (even if there're started with some delay as in "1.")
+
+Note that the shapes are delayed by exactly one sample per one 'main' rate
+increment. (Normally one would expect them to start at the same samples.)
+
+See diagram below (* - start point of the shape).
+
+cycle:
+0123456701234567012345670123456701234567012345670123456701234567
+
+rate 09 1
+*-------
+ --------
+ ----
+ ----
+ --------
+ *-------
+ | --------
+ | ----
+ | ----
+ | --------
+rate 10 1 |
+-- |
+ *--- |
+ ---- |
+ -- |
+ -- |
+ ---- |
+ *--- |
+ | ---- |
+ | -- | | <- one step (two samples) delay between 9 1 and 10 1
+ | -- | |
+ | ----|
+ | *---
+ | ----
+ | --
+ | --
+ | ----
+rate 11 1 |
+- |
+ -- |
+ *- |
+ -- |
+ - |
+ - |
+ -- |
+ *- |
+ -- |
+ - || <- one step (one sample) delay between 10 1 and 11 1
+ - ||
+ --|
+ *-
+ --
+ -
+ -
+ --
+ *-
+ --
+ -
+ -
+ --
+*/
+
+INLINE void advance_eg(YM2151 *PSG)
+{
+ YM2151Operator *op;
+ unsigned int i;
+
+
+
+ PSG->eg_timer += PSG->eg_timer_add;
+
+ while (PSG->eg_timer >= PSG->eg_timer_overflow)
+ {
+ PSG->eg_timer -= PSG->eg_timer_overflow;
+
+ PSG->eg_cnt++;
+
+ /* envelope generator */
+ op = &PSG->oper[0]; /* CH 0 M1 */
+ i = 32;
+ do
+ {
+ switch(op->state)
+ {
+ case EG_ATT: /* attack phase */
+ if ( !(PSG->eg_cnt & ((1<eg_sh_ar)-1) ) )
+ {
+ op->volume += (~op->volume *
+ (eg_inc[op->eg_sel_ar + ((PSG->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>4;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if ( !(PSG->eg_cnt & ((1<eg_sh_d1r)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_d1r + ((PSG->eg_cnt>>op->eg_sh_d1r)&7)];
+
+ if ( op->volume >= (INT32)(op->d1l) )
+ op->state = EG_SUS;
+
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+ if ( !(PSG->eg_cnt & ((1<eg_sh_d2r)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_d2r + ((PSG->eg_cnt>>op->eg_sh_d2r)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if ( !(PSG->eg_cnt & ((1<eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((PSG->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+
+ }
+ break;
+ }
+ op++;
+ i--;
+ }while (i);
+ }
+}
+
+
+INLINE void advance(YM2151 *PSG)
+{
+ YM2151Operator *op;
+ unsigned int i;
+ int a,p;
+
+ /* LFO */
+ if (PSG->test&2)
+ PSG->lfo_phase = 0;
+ else
+ {
+ PSG->lfo_timer += PSG->lfo_timer_add;
+ if (PSG->lfo_timer >= PSG->lfo_overflow)
+ {
+ PSG->lfo_timer -= PSG->lfo_overflow;
+ PSG->lfo_counter += PSG->lfo_counter_add;
+ PSG->lfo_phase += (PSG->lfo_counter>>4);
+ PSG->lfo_phase &= 255;
+ PSG->lfo_counter &= 15;
+ }
+ }
+
+ i = PSG->lfo_phase;
+ /* calculate LFO AM and PM waveform value (all verified on real chip, except for noise algorithm which is impossible to analyse)*/
+ switch (PSG->lfo_wsel)
+ {
+ case 0:
+ /* saw */
+ /* AM: 255 down to 0 */
+ /* PM: 0 to 127, -127 to 0 (at PMD=127: LFP = 0 to 126, -126 to 0) */
+ a = 255 - i;
+ if (i<128)
+ p = i;
+ else
+ p = i - 255;
+ break;
+ case 1:
+ /* square */
+ /* AM: 255, 0 */
+ /* PM: 128,-128 (LFP = exactly +PMD, -PMD) */
+ if (i<128)
+ {
+ a = 255;
+ p = 128;
+ }
+ else
+ {
+ a = 0;
+ p = -128;
+ }
+ break;
+ case 2:
+ /* triangle */
+ /* AM: 255 down to 1 step -2; 0 up to 254 step +2 */
+ /* PM: 0 to 126 step +2, 127 to 1 step -2, 0 to -126 step -2, -127 to -1 step +2*/
+ if (i<128)
+ a = 255 - (i*2);
+ else
+ a = (i*2) - 256;
+
+ if (i<64) /* i = 0..63 */
+ p = i*2; /* 0 to 126 step +2 */
+ else if (i<128) /* i = 64..127 */
+ p = 255 - i*2; /* 127 to 1 step -2 */
+ else if (i<192) /* i = 128..191 */
+ p = 256 - i*2; /* 0 to -126 step -2*/
+ else /* i = 192..255 */
+ p = i*2 - 511; /*-127 to -1 step +2*/
+ break;
+ case 3:
+ default: /*keep the compiler happy*/
+ /* random */
+ /* the real algorithm is unknown !!!
+ We just use a snapshot of data from real chip */
+
+ /* AM: range 0 to 255 */
+ /* PM: range -128 to 127 */
+
+ a = lfo_noise_waveform[i];
+ p = a-128;
+ break;
+ }
+ PSG->lfa = a * PSG->amd / 128;
+ PSG->lfp = p * PSG->pmd / 128;
+
+
+ /* The Noise Generator of the YM2151 is 17-bit shift register.
+ * Input to the bit16 is negated (bit0 XOR bit3) (EXNOR).
+ * Output of the register is negated (bit0 XOR bit3).
+ * Simply use bit16 as the noise output.
+ */
+ PSG->noise_p += PSG->noise_f;
+ i = (PSG->noise_p>>16); /* number of events (shifts of the shift register) */
+ PSG->noise_p &= 0xffff;
+ while (i)
+ {
+ UINT32 j;
+ j = ( (PSG->noise_rng ^ (PSG->noise_rng>>3) ) & 1) ^ 1;
+ PSG->noise_rng = (j<<16) | (PSG->noise_rng>>1);
+ i--;
+ }
+
+
+ /* phase generator */
+ op = &PSG->oper[0]; /* CH 0 M1 */
+ i = 8;
+ do
+ {
+ if (op->pms) /* only when phase modulation from LFO is enabled for this channel */
+ {
+ INT32 mod_ind = PSG->lfp; /* -128..+127 (8bits signed) */
+ if (op->pms < 6)
+ mod_ind >>= (6 - op->pms);
+ else
+ mod_ind <<= (op->pms - 5);
+
+ if (mod_ind)
+ {
+ UINT32 kc_channel = op->kc_i + mod_ind;
+ (op+0)->phase += ( (PSG->freq[ kc_channel + (op+0)->dt2 ] + (op+0)->dt1) * (op+0)->mul ) >> 1;
+ (op+1)->phase += ( (PSG->freq[ kc_channel + (op+1)->dt2 ] + (op+1)->dt1) * (op+1)->mul ) >> 1;
+ (op+2)->phase += ( (PSG->freq[ kc_channel + (op+2)->dt2 ] + (op+2)->dt1) * (op+2)->mul ) >> 1;
+ (op+3)->phase += ( (PSG->freq[ kc_channel + (op+3)->dt2 ] + (op+3)->dt1) * (op+3)->mul ) >> 1;
+ }
+ else /* phase modulation from LFO is equal to zero */
+ {
+ (op+0)->phase += (op+0)->freq;
+ (op+1)->phase += (op+1)->freq;
+ (op+2)->phase += (op+2)->freq;
+ (op+3)->phase += (op+3)->freq;
+ }
+ }
+ else /* phase modulation from LFO is disabled */
+ {
+ (op+0)->phase += (op+0)->freq;
+ (op+1)->phase += (op+1)->freq;
+ (op+2)->phase += (op+2)->freq;
+ (op+3)->phase += (op+3)->freq;
+ }
+
+ op+=4;
+ i--;
+ }while (i);
+
+
+ /* CSM is calculated *after* the phase generator calculations (verified on real chip)
+ * CSM keyon line seems to be ORed with the KO line inside of the chip.
+ * The result is that it only works when KO (register 0x08) is off, ie. 0
+ *
+ * Interesting effect is that when timer A is set to 1023, the KEY ON happens
+ * on every sample, so there is no KEY OFF at all - the result is that
+ * the sound played is the same as after normal KEY ON.
+ */
+
+ if (PSG->csm_req) /* CSM KEYON/KEYOFF seqeunce request */
+ {
+ if (PSG->csm_req==2) /* KEY ON */
+ {
+ op = &PSG->oper[0]; /* CH 0 M1 */
+ i = 32;
+ do
+ {
+ KEY_ON(op, 2);
+ op++;
+ i--;
+ }while (i);
+ PSG->csm_req = 1;
+ }
+ else /* KEY OFF */
+ {
+ op = &PSG->oper[0]; /* CH 0 M1 */
+ i = 32;
+ do
+ {
+ KEY_OFF(op,~2);
+ op++;
+ i--;
+ }while (i);
+ PSG->csm_req = 0;
+ }
+ }
+}
+
+/* first macro saves left and right channels to mono file */
+/* second macro saves left and right channels to stereo file */
+#if 0 /*MONO*/
+ #ifdef SAVE_SEPARATE_CHANNELS
+ #define SAVE_SINGLE_CHANNEL(j) \
+ { signed int pom= -(chanout[j] & PSG->pan[j*2]); \
+ if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \
+ fputc((unsigned short)pom&0xff,sample[j]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[j]); \
+ }
+ #else
+ #define SAVE_SINGLE_CHANNEL(j)
+ #endif
+#else /*STEREO*/
+ #ifdef SAVE_SEPARATE_CHANNELS
+ #define SAVE_SINGLE_CHANNEL(j) \
+ { signed int pom = -(chanout[j] & PSG->pan[j*2]); \
+ if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \
+ fputc((unsigned short)pom&0xff,sample[j]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[j]); \
+ pom = -(chanout[j] & PSG->pan[j*2+1]); \
+ if (pom > 32767) pom = 32767; else if (pom < -32768) pom = -32768; \
+ fputc((unsigned short)pom&0xff,sample[j]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[j]); \
+ }
+ #else
+ #define SAVE_SINGLE_CHANNEL(j)
+ #endif
+#endif
+
+/* first macro saves left and right channels to mono file */
+/* second macro saves left and right channels to stereo file */
+#if 1 /*MONO*/
+ #ifdef SAVE_SAMPLE
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = outl; \
+ /*pom = acc_calc(pom);*/ \
+ /*fprintf(sample[8]," %i\n",pom);*/ \
+ fputc((unsigned short)pom&0xff,sample[8]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \
+ }
+ #else
+ #define SAVE_ALL_CHANNELS
+ #endif
+#else /*STEREO*/
+ #ifdef SAVE_SAMPLE
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = outl; \
+ fputc((unsigned short)pom&0xff,sample[8]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \
+ pom = outr; \
+ fputc((unsigned short)pom&0xff,sample[8]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[8]); \
+ }
+ #else
+ #define SAVE_ALL_CHANNELS
+ #endif
+#endif
+
+
+/* Generate samples for one of the YM2151's
+*
+* 'num' is the number of virtual YM2151
+* '**buffers' is table of pointers to the buffers: left and right
+* 'length' is the number of samples that should be generated
+*/
+void ym2151_update_one(void *chip, SAMP **buffers, int length)
+{
+ YM2151 *PSG = (YM2151 *)chip;
+ signed int *chanout = PSG->chanout;
+ int i;
+ UINT32 mask = ~PSG->mask;
+ signed int outl,outr;
+ SAMP *bufL, *bufR;
+
+ bufL = buffers[0];
+ bufR = buffers[1];
+
+ for (i=0; ipan[0];
+ if (mask & 1) outr += chanout[0] & PSG->pan[1];
+ if (mask & 2) outl += (chanout[1] & PSG->pan[2]);
+ if (mask & 2) outr += (chanout[1] & PSG->pan[3]);
+ if (mask & 4) outl += (chanout[2] & PSG->pan[4]);
+ if (mask & 4) outr += (chanout[2] & PSG->pan[5]);
+ if (mask & 8) outl += (chanout[3] & PSG->pan[6]);
+ if (mask & 8) outr += (chanout[3] & PSG->pan[7]);
+ if (mask & 16) outl += (chanout[4] & PSG->pan[8]);
+ if (mask & 16) outr += (chanout[4] & PSG->pan[9]);
+ if (mask & 32) outl += (chanout[5] & PSG->pan[10]);
+ if (mask & 32) outr += (chanout[5] & PSG->pan[11]);
+ if (mask & 64) outl += (chanout[6] & PSG->pan[12]);
+ if (mask & 64) outr += (chanout[6] & PSG->pan[13]);
+ if (mask & 128) outl += (chanout[7] & PSG->pan[14]);
+ if (mask & 128) outr += (chanout[7] & PSG->pan[15]);
+
+ outl >>= FINAL_SH;
+ outr >>= FINAL_SH;
+ if (outl > MAXOUT) outl = MAXOUT;
+ else if (outl < MINOUT) outl = MINOUT;
+ if (outr > MAXOUT) outr = MAXOUT;
+ else if (outr < MINOUT) outr = MINOUT;
+ ((SAMP*)bufL)[i] = (SAMP)outl;
+ ((SAMP*)bufR)[i] = (SAMP)outr;
+
+ SAVE_ALL_CHANNELS
+
+ advance(PSG);
+ }
+}
+
+void ym2151_set_mask(void *_chip, UINT32 mask)
+{
+ YM2151 *PSG = (YM2151 *)_chip;
+
+ PSG->mask = mask;
+}
diff --git a/Frameworks/GME/gme/ym2151.h b/Frameworks/GME/gme/ym2151.h
new file mode 100644
index 000000000..834dfe1bd
--- /dev/null
+++ b/Frameworks/GME/gme/ym2151.h
@@ -0,0 +1,89 @@
+/*
+** File: ym2151.h - header file for software implementation of YM2151
+** FM Operator Type-M(OPM)
+**
+** (c) 1997-2002 Jarek Burczynski (s0246@poczta.onet.pl, bujar@mame.net)
+** Some of the optimizing ideas by Tatsuyuki Satoh
+**
+** Version 2.150 final beta May, 11th 2002
+**
+**
+** I would like to thank following people for making this project possible:
+**
+** Beauty Planets - for making a lot of real YM2151 samples and providing
+** additional informations about the chip. Also for the time spent making
+** the samples and the speed of replying to my endless requests.
+**
+** Shigeharu Isoda - for general help, for taking time to scan his YM2151
+** Japanese Manual first of all, and answering MANY of my questions.
+**
+** Nao - for giving me some info about YM2151 and pointing me to Shigeharu.
+** Also for creating fmemu (which I still use to test the emulator).
+**
+** Aaron Giles and Chris Hardy - they made some samples of one of my favourite
+** arcade games so I could compare it to my emulator.
+**
+** Bryan McPhail and Tim (powerjaw) - for making some samples.
+**
+** Ishmair - for the datasheet and motivation.
+*/
+
+#pragma once
+
+#ifndef __YM2151_H__
+#define __YM2151_H__
+
+
+/* 16- and 8-bit samples (signed) are supported*/
+#define SAMPLE_BITS 16
+
+#include "mamedef.h"
+
+typedef stream_sample_t SAMP;
+/*
+#if (SAMPLE_BITS==16)
+ typedef INT16 SAMP;
+#endif
+#if (SAMPLE_BITS==8)
+ typedef signed char SAMP;
+#endif
+*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+** Initialize YM2151 emulator(s).
+**
+** 'num' is the number of virtual YM2151's to allocate
+** 'clock' is the chip clock in Hz
+** 'rate' is sampling rate
+*/
+void *ym2151_init(int clock, int rate);
+
+/* shutdown the YM2151 emulators*/
+void ym2151_shutdown(void *chip);
+
+/* reset all chip registers for YM2151 number 'num'*/
+void ym2151_reset_chip(void *chip);
+
+/*
+** Generate samples for one of the YM2151's
+**
+** 'num' is the number of virtual YM2151
+** '**buffers' is table of pointers to the buffers: left and right
+** 'length' is the number of samples that should be generated
+*/
+void ym2151_update_one(void *chip, SAMP **buffers, int length);
+
+/* write 'v' to register 'r' on YM2151 chip number 'n'*/
+void ym2151_write_reg(void *chip, int r, int v);
+
+void ym2151_set_mask(void *chip, UINT32 mask);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__YM2151_H__*/
diff --git a/Frameworks/GME/gme/ym2413.c b/Frameworks/GME/gme/ym2413.c
new file mode 100644
index 000000000..af225d237
--- /dev/null
+++ b/Frameworks/GME/gme/ym2413.c
@@ -0,0 +1,2108 @@
+/*
+**
+** File: ym2413.c - software implementation of YM2413
+** FM sound generator type OPLL
+**
+** Copyright Jarek Burczynski
+**
+** Version 1.0
+**
+
+ Features as listed in LSI-212413A2 data sheet:
+ 1. FM Sound Generator for real sound creation.
+ 2. Two Selectable modes: 9 simultaneous sounds or 6 melody sounds plus 5 rhythm sounds
+ (different tones can be used together in either case).
+ 3. Built-in Instruments data (15 melody tones, 5 rhythm tones, "CAPTAIN and TELETEXT applicalbe tones).
+ 4. Built-in DA Converter.
+ 5. Built-in Quartz Oscillator.
+ 6. Built-in Vibrato Oscillator/AM Oscillator
+ 7. TTL Compatible Input.
+ 8. Si-Gate NMOS LSI
+ 9. A single 5V power source.
+
+to do:
+
+- make sure of the sinus amplitude bits
+
+- make sure of the EG resolution bits (looks like the biggest
+ modulation index generated by the modulator is 123, 124 = no modulation)
+- find proper algorithm for attack phase of EG
+
+- tune up instruments ROM
+
+- support sample replay in test mode (it is NOT as simple as setting bit 0
+ in register 0x0f and using register 0x10 for sample data).
+ Which games use this feature ?
+
+
+*/
+
+#define _USE_MATH_DEFINES
+#include
+#include
+#include
+#include "mamedef.h"
+#include "ym2413.h"
+
+#ifndef INLINE
+#define INLINE static __inline
+#endif
+#ifndef logerror
+#define logerror (void)
+#endif
+
+#ifndef M_PI
+ #define M_PI 3.14159265358979323846
+#endif
+
+/* output final shift */
+#if (SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+
+#define FREQ_MASK ((1<>KSR */
+ UINT8 mul; /* multiple: mul_tab[ML] */
+
+ /* Phase Generator */
+ UINT32 phase; /* frequency counter */
+ UINT32 freq; /* frequency counter step */
+ UINT8 fb_shift; /* feedback shift value */
+ INT32 op1_out[2]; /* slot1 output for feedback */
+
+ /* Envelope Generator */
+ UINT8 eg_type; /* percussive/nonpercussive mode*/
+ UINT8 state; /* phase type */
+ UINT32 TL; /* total level: TL << 2 */
+ INT32 TLL; /* adjusted now TL */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level: sl_tab[SL] */
+
+ UINT8 eg_sh_dp; /* (dump state) */
+ UINT8 eg_sel_dp; /* (dump state) */
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_dr; /* (decay state) */
+ UINT8 eg_sel_dr; /* (decay state) */
+ UINT8 eg_sh_rr; /* (release state for non-perc.)*/
+ UINT8 eg_sel_rr; /* (release state for non-perc.)*/
+ UINT8 eg_sh_rs; /* (release state for perc.mode)*/
+ UINT8 eg_sel_rs; /* (release state for perc.mode)*/
+
+ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
+ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/
+
+ /* waveform select */
+ unsigned int wavetable;
+} OPLL_SLOT;
+
+#define OPLL_MASK_CH(x) (1<<(x))
+#define OPLL_MASK_HH (1<<(9))
+#define OPLL_MASK_CYM (1<<(10))
+#define OPLL_MASK_TOM (1<<(11))
+#define OPLL_MASK_SD (1<<(12))
+#define OPLL_MASK_BD (1<<(13))
+#define OPLL_MASK_RHYTHM ( OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD )
+
+typedef struct{
+ OPLL_SLOT SLOT[2];
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum */
+ UINT32 fc; /* Freq. freqement base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 kcode; /* key code (for key scaling) */
+ UINT8 sus; /* sus on/off (release speed in percussive mode)*/
+} OPLL_CH;
+
+/* chip state */
+typedef struct {
+ OPLL_CH P_CH[9]; /* OPLL chips have 9 channels*/
+ UINT8 instvol_r[9]; /* instrument/volume (or volume/volume in percussive mode)*/
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */
+
+ UINT8 rhythm; /* Rhythm mode */
+
+ /* LFO */
+ UINT32 lfo_am_cnt;
+ UINT32 lfo_am_inc;
+ UINT32 lfo_pm_cnt;
+ UINT32 lfo_pm_inc;
+
+ UINT32 noise_rng; /* 23 bit noise shift register */
+ UINT32 noise_p; /* current noise 'phase' */
+ UINT32 noise_f; /* current noise period */
+
+
+/* instrument settings */
+/*
+ 0-user instrument
+ 1-15 - fixed instruments
+ 16 -bass drum settings
+ 17,18 - other percussion instruments
+*/
+ UINT8 inst_tab[19][8];
+
+ /* external event callback handlers */
+ OPLL_UPDATEHANDLER UpdateHandler; /* stream update handler */
+ void * UpdateParam; /* stream update parameter */
+
+ UINT32 fn_tab[1024]; /* fnumber->increment counter */
+
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+
+ /* work table */
+ OPLL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2;
+
+ signed int output[2];
+
+ UINT32 LFO_AM;
+ INT32 LFO_PM;
+
+ int chip_type;
+ UINT32 mask;
+} YM2413;
+
+/* key scale level */
+/* table is 3dB/octave, DV converts this into 6dB/octave */
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
+#define DV (0.1875/1.0)
+static const UINT32 ksl_tab[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* sustain level table (3dB per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,45 (dB)*/
+#define SC(db) (UINT32) ( db * (1.0/ENV_STEP) )
+static const UINT32 sl_tab[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(15)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const unsigned char eg_inc[15*RATE_STEPS]={
+
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(13) in this table - it's directly in the code */
+static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+
+/* rates 00-12 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 13 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 14 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 15 */
+O(12),O(12),O(12),O(12),
+
+/* 16 dummy rates (same as 15 3) */
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
+/*shift 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0 */
+/*mask 8191, 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-12 */
+O(13),O(13),O(13),O(13),
+O(12),O(12),O(12),O(12),
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 16 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+
+};
+#undef O
+
+
+/* multiple table */
+#define ML 2
+static const UINT8 mul_tab[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
+ 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
+ 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML
+};
+#undef ML
+
+/* TL_TAB_LEN is calculated as:
+* 11 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (11*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>5)
+
+/* sin waveform table in 'decibel' scale */
+/* two waveforms on OPLL type chips */
+static unsigned int sin_tab[SIN_LEN * 2];
+
+
+/* LFO Amplitude Modulation table (verified on real YM3812)
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
+
+ Length: 210 elements.
+
+ Each of the elements has to be repeated
+ exactly 64 times (on 64 consecutive samples).
+ The whole table takes: 64 * 210 = 13440 samples.
+
+We use data>>1, until we find what it really is on real chip...
+
+*/
+
+#define LFO_AM_TAB_ELEMENTS 210
+
+static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
+0,0,0,0,0,0,0,
+1,1,1,1,
+2,2,2,2,
+3,3,3,3,
+4,4,4,4,
+5,5,5,5,
+6,6,6,6,
+7,7,7,7,
+8,8,8,8,
+9,9,9,9,
+10,10,10,10,
+11,11,11,11,
+12,12,12,12,
+13,13,13,13,
+14,14,14,14,
+15,15,15,15,
+16,16,16,16,
+17,17,17,17,
+18,18,18,18,
+19,19,19,19,
+20,20,20,20,
+21,21,21,21,
+22,22,22,22,
+23,23,23,23,
+24,24,24,24,
+25,25,25,25,
+26,26,26,
+25,25,25,25,
+24,24,24,24,
+23,23,23,23,
+22,22,22,22,
+21,21,21,21,
+20,20,20,20,
+19,19,19,19,
+18,18,18,18,
+17,17,17,17,
+16,16,16,16,
+15,15,15,15,
+14,14,14,14,
+13,13,13,13,
+12,12,12,12,
+11,11,11,11,
+10,10,10,10,
+9,9,9,9,
+8,8,8,8,
+7,7,7,7,
+6,6,6,6,
+5,5,5,5,
+4,4,4,4,
+3,3,3,3,
+2,2,2,2,
+1,1,1,1
+};
+
+/* LFO Phase Modulation table (verified on real YM2413) */
+static const INT8 lfo_pm_table[8*8] = {
+
+/* FNUM2/FNUM = 0 00xxxxxx (0x0000) */
+0, 0, 0, 0, 0, 0, 0, 0,
+
+/* FNUM2/FNUM = 0 01xxxxxx (0x0040) */
+1, 0, 0, 0,-1, 0, 0, 0,
+
+/* FNUM2/FNUM = 0 10xxxxxx (0x0080) */
+2, 1, 0,-1,-2,-1, 0, 1,
+
+/* FNUM2/FNUM = 0 11xxxxxx (0x00C0) */
+3, 1, 0,-1,-3,-1, 0, 1,
+
+/* FNUM2/FNUM = 1 00xxxxxx (0x0100) */
+4, 2, 0,-2,-4,-2, 0, 2,
+
+/* FNUM2/FNUM = 1 01xxxxxx (0x0140) */
+5, 2, 0,-2,-5,-2, 0, 2,
+
+/* FNUM2/FNUM = 1 10xxxxxx (0x0180) */
+6, 3, 0,-3,-6,-3, 0, 3,
+
+/* FNUM2/FNUM = 1 11xxxxxx (0x01C0) */
+7, 3, 0,-3,-7,-3, 0, 3,
+};
+
+
+
+
+
+
+/* This is not 100% perfect yet but very close */
+/*
+ - multi parameters are 100% correct (instruments and drums)
+ - LFO PM and AM enable are 100% correct
+ - waveform DC and DM select are 100% correct
+*/
+
+static const unsigned char table[19][8] = {
+/* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */
+/* 0 1 2 3 4 5 6 7 */
+ {0x49, 0x4c, 0x4c, 0x12, 0x00, 0x00, 0x00, 0x00 }, /* 0 */
+
+ {0x61, 0x61, 0x1e, 0x17, 0xf0, 0x78, 0x00, 0x17 }, /* 1 */
+ {0x13, 0x41, 0x1e, 0x0d, 0xd7, 0xf7, 0x13, 0x13 }, /* 2 */
+ {0x13, 0x01, 0x99, 0x04, 0xf2, 0xf4, 0x11, 0x23 }, /* 3 */
+ {0x21, 0x61, 0x1b, 0x07, 0xaf, 0x64, 0x40, 0x27 }, /* 4 */
+
+/* {0x22, 0x21, 0x1e, 0x09, 0xf0, 0x76, 0x08, 0x28 }, //5 */
+ {0x22, 0x21, 0x1e, 0x06, 0xf0, 0x75, 0x08, 0x18 }, /* 5 */
+
+/* {0x31, 0x22, 0x16, 0x09, 0x90, 0x7f, 0x00, 0x08 }, //6 */
+ {0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x13 }, /* 6 */
+
+ {0x21, 0x61, 0x1d, 0x07, 0x82, 0x80, 0x10, 0x17 }, /* 7 */
+ {0x23, 0x21, 0x2d, 0x16, 0xc0, 0x70, 0x07, 0x07 }, /* 8 */
+ {0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17 }, /* 9 */
+
+/* {0x61, 0x61, 0x0c, 0x08, 0x85, 0xa0, 0x79, 0x07 }, //A */
+ {0x61, 0x61, 0x0c, 0x18, 0x85, 0xf0, 0x70, 0x07 }, /* A */
+
+ {0x23, 0x01, 0x07, 0x11, 0xf0, 0xa4, 0x00, 0x22 }, /* B */
+ {0x97, 0xc1, 0x24, 0x07, 0xff, 0xf8, 0x22, 0x12 }, /* C */
+
+/* {0x61, 0x10, 0x0c, 0x08, 0xf2, 0xc4, 0x40, 0xc8 }, //D */
+ {0x61, 0x10, 0x0c, 0x05, 0xf2, 0xf4, 0x40, 0x44 }, /* D */
+
+ {0x01, 0x01, 0x55, 0x03, 0xf3, 0x92, 0xf3, 0xf3 }, /* E */
+ {0x61, 0x41, 0x89, 0x03, 0xf1, 0xf4, 0xf0, 0x13 }, /* F */
+
+/* drum instruments definitions */
+/* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */
+/* 0 1 2 3 4 5 6 7 */
+ {0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d },/* BD(multi verified, modTL verified, mod env - verified(close), carr. env verifed) */
+ {0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 },/* HH(multi verified), SD(multi not used) */
+ {0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 },/* TOM(multi,env verified), TOP CYM(multi verified, env verified) */
+};
+
+static const unsigned char table_vrc7[15][8] =
+{
+/* VRC7 instruments, January 17, 2004 update -Xodnizel */
+ {0x03, 0x21, 0x04, 0x06, 0x8D, 0xF2, 0x42, 0x17},
+ {0x13, 0x41, 0x05, 0x0E, 0x99, 0x96, 0x63, 0x12},
+ {0x31, 0x11, 0x10, 0x0A, 0xF0, 0x9C, 0x32, 0x02},
+ {0x21, 0x61, 0x1D, 0x07, 0x9F, 0x64, 0x20, 0x27},
+ {0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28},
+ {0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0x95},
+ {0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x16, 0x07},
+ {0x23, 0x21, 0x1A, 0x17, 0xEF, 0x82, 0x25, 0x15},
+ {0x25, 0x11, 0x1F, 0x00, 0x86, 0x41, 0x20, 0x11},
+ {0x85, 0x01, 0x1F, 0x0F, 0xE4, 0xA2, 0x11, 0x12},
+ {0x07, 0xC1, 0x2B, 0x45, 0xB4, 0xF1, 0x24, 0xF4},
+ {0x61, 0x23, 0x11, 0x06, 0x96, 0x96, 0x13, 0x16},
+ {0x01, 0x02, 0xD3, 0x05, 0x82, 0xA2, 0x31, 0x51},
+ {0x61, 0x22, 0x0D, 0x02, 0xC3, 0x7F, 0x24, 0x05},
+ {0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x44, 0x17},
+
+};
+
+INLINE int limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(YM2413 *chip)
+{
+ /* LFO */
+ chip->lfo_am_cnt += chip->lfo_am_inc;
+ if (chip->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<LFO_AM = lfo_am_table[ chip->lfo_am_cnt >> LFO_SH ] >> 1;
+
+ chip->lfo_pm_cnt += chip->lfo_pm_inc;
+ chip->LFO_PM = (chip->lfo_pm_cnt>>LFO_SH) & 7;
+}
+
+/* advance to next sample */
+INLINE void advance(YM2413 *chip)
+{
+ OPLL_CH *CH;
+ OPLL_SLOT *op;
+ unsigned int i;
+
+ /* Envelope Generator */
+ chip->eg_timer += chip->eg_timer_add;
+
+ while (chip->eg_timer >= chip->eg_timer_overflow)
+ {
+ chip->eg_timer -= chip->eg_timer_overflow;
+
+ chip->eg_cnt++;
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &chip->P_CH[i/2];
+
+ op = &CH->SLOT[i&1];
+
+ switch(op->state)
+ {
+
+ case EG_DMP: /* dump phase */
+ /*dump phase is performed by both operators in each channel*/
+ /*when CARRIER envelope gets down to zero level,
+ ** phases in BOTH opearators are reset (at the same time ?)
+ */
+ if ( !(chip->eg_cnt & ((1<eg_sh_dp)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_dp + ((chip->eg_cnt>>op->eg_sh_dp)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_ATT;
+ /* restart Phase Generator */
+ op->phase = 0;
+ }
+ }
+ break;
+
+ case EG_ATT: /* attack phase */
+ if ( !(chip->eg_cnt & ((1<eg_sh_ar)-1) ) )
+ {
+ op->volume += (~op->volume *
+ (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>2;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if ( !(chip->eg_cnt & ((1<eg_sh_dr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)];
+
+ if ( op->volume >= (INT32)(op->sl) )
+ op->state = EG_SUS;
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+ /* this is important behaviour:
+ one can change percusive/non-percussive modes on the fly and
+ the chip will remain in sustain phase - verified on real YM3812 */
+
+ if(op->eg_type) /* non-percussive mode (sustained tone) */
+ {
+ /* do nothing */
+ }
+ else /* percussive mode */
+ {
+ /* during sustain phase chip adds Release Rate (in percussive mode) */
+ if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ op->volume = MAX_ATT_INDEX;
+ }
+ /* else do nothing in sustain phase */
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ /* exclude modulators in melody channels from performing anything in this mode*/
+ /* allowed are only carriers in melody mode and rhythm slots in rhythm mode */
+
+ /*This table shows which operators and on what conditions are allowed to perform EG_REL:
+ (a) - always perform EG_REL
+ (n) - never perform EG_REL
+ (r) - perform EG_REL in Rhythm mode ONLY
+ 0: 0 (n), 1 (a)
+ 1: 2 (n), 3 (a)
+ 2: 4 (n), 5 (a)
+ 3: 6 (n), 7 (a)
+ 4: 8 (n), 9 (a)
+ 5: 10(n), 11(a)
+ 6: 12(r), 13(a)
+ 7: 14(r), 15(a)
+ 8: 16(r), 17(a)
+ */
+ if ( (i&1) || ((chip->rhythm&0x20) && (i>=12)) )/* exclude modulators */
+ {
+ if(op->eg_type) /* non-percussive mode (sustained tone) */
+ /*this is correct: use RR when SUS = OFF*/
+ /*and use RS when SUS = ON*/
+ {
+ if (CH->sus)
+ {
+ if ( !(chip->eg_cnt & ((1<eg_sh_rs)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)];
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+ }
+ }
+ else
+ {
+ if ( !(chip->eg_cnt & ((1<eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)];
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+ }
+ }
+ }
+ else /* percussive mode */
+ {
+ if ( !(chip->eg_cnt & ((1<eg_sh_rs)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)];
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &chip->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Phase Generator */
+ if(op->vib)
+ {
+ UINT8 block;
+
+ unsigned int fnum_lfo = 8*((CH->block_fnum&0x01c0) >> 6);
+ unsigned int block_fnum = CH->block_fnum * 2;
+ signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + fnum_lfo ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum += lfo_fn_table_index_offset;
+ block = (block_fnum&0x1c00) >> 10;
+ op->phase += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ op->phase += op->freq;
+ }
+ }
+ else /* LFO phase modulation disabled for this operator */
+ {
+ op->phase += op->freq;
+ }
+ }
+
+ /* The Noise Generator of the YM3812 is 23-bit shift register.
+ * Period is equal to 2^23-2 samples.
+ * Register works at sampling frequency of the chip, so output
+ * can change on every sample.
+ *
+ * Output of the register and input to the bit 22 is:
+ * bit0 XOR bit14 XOR bit15 XOR bit22
+ *
+ * Simply use bit 22 as the noise output.
+ */
+
+ chip->noise_p += chip->noise_f;
+ i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */
+ chip->noise_p &= FREQ_MASK;
+ while (i)
+ {
+ /*
+ UINT32 j;
+ j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1;
+ chip->noise_rng = (j<<22) | (chip->noise_rng>>1);
+ */
+
+ /*
+ Instead of doing all the logic operations above, we
+ use a trick here (and use bit 0 as the noise output).
+ The difference is only that the noise bit changes one
+ step ahead. This doesn't matter since we don't know
+ what is real state of the noise_rng after the reset.
+ */
+
+ if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302;
+ chip->noise_rng >>= 1;
+
+ i--;
+ }
+}
+
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<5) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<17))) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+ INT32 i;
+
+ i = (phase & ~FREQ_MASK) + pm;
+
+/*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, phase>>FREQ_SH, pm);*/
+
+ p = (env<<5) + sin_tab[ wave_tab + ((i>>FREQ_SH) & SIN_MASK)];
+
+/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+
+#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask))
+
+/* calculate output */
+INLINE void chan_calc( YM2413 *chip, OPLL_CH *CH )
+{
+ OPLL_SLOT *SLOT;
+ unsigned int env;
+ signed int out;
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+ phase_modulation = SLOT->op1_out[0];
+
+ SLOT->op1_out[1] = 0;
+
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->fb_shift)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<fb_shift), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ {
+ signed int outp = op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable);
+ chip->output[0] += outp;
+ /* output[0] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable); */
+ }
+}
+
+/*
+ operators used in the rhythm sounds generation process:
+
+ Envelope Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
+ 6 / 0 12 50 70 90 f0 +
+ 6 / 1 15 53 73 93 f3 +
+ 7 / 0 13 51 71 91 f1 +
+ 7 / 1 16 54 74 94 f4 +
+ 8 / 0 14 52 72 92 f2 +
+ 8 / 1 17 55 75 95 f5 +
+
+ Phase Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number MULTIPLE Drum Hat Drum Tom Cymbal
+ 6 / 0 12 30 +
+ 6 / 1 15 33 +
+ 7 / 0 13 31 + + +
+ 7 / 1 16 34 ----- n o t u s e d -----
+ 8 / 0 14 32 +
+ 8 / 1 17 35 + +
+
+channel operator register number Bass High Snare Tom Top
+number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
+ 6 12,15 B6 A6 +
+
+ 7 13,16 B7 A7 + + +
+
+ 8 14,17 B8 A8 + + +
+
+*/
+
+/* calculate rhythm */
+
+INLINE void rhythm_calc( YM2413 *chip, OPLL_CH *CH, unsigned int noise )
+{
+ OPLL_SLOT *SLOT;
+ signed int out;
+ unsigned int env;
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+
+
+ /* Bass Drum (verified on real YM3812):
+ - depends on the channel 6 'connect' register:
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
+ - output sample always is multiplied by 2
+ */
+
+
+ /* SLOT 1 */
+ if ( !(chip->mask & OPLL_MASK_BD) )
+ {
+ SLOT = &CH[6].SLOT[SLOT1];
+ env = volume_calc(SLOT);
+
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+
+ phase_modulation = SLOT->op1_out[0];
+
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->fb_shift)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<fb_shift), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ chip->output[1] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable) * 2;
+ }
+
+
+ /* Phase generation is based on:
+ HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases)
+ SD (16) channel 7->slot 1
+ TOM (14) channel 8->slot 1
+ TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */
+
+ /* Envelope generation based on:
+ HH channel 7->slot1
+ SD channel 7->slot2
+ TOM channel 8->slot1
+ TOP channel 8->slot2 */
+
+
+ /* The following formulas can be well optimized.
+ I leave them in direct form for now (in case I've missed something).
+ */
+
+ /* High Hat (verified on real YM3812) */
+ if ( !(chip->mask & OPLL_MASK_HH) )
+ {
+ env = volume_calc(chip->SLOT7_1);
+ if( env < ENV_QUIET )
+ {
+
+ /* high hat phase generation:
+ phase = d0 or 234 (based on frequency only)
+ phase = 34 or 2d0 (based on noise)
+ */
+
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((chip->SLOT7_1->phase>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((chip->SLOT7_1->phase>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((chip->SLOT7_1->phase>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0xd0; */
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */
+ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((chip->SLOT8_2->phase>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((chip->SLOT8_2->phase>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e | bit5e);
+
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */
+ if (res2)
+ phase = (0x200|(0xd0>>2));
+
+
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */
+ if (phase&0x200)
+ {
+ if (noise)
+ phase = 0x200|0xd0;
+ }
+ else
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */
+ {
+ if (noise)
+ phase = 0xd0>>2;
+ }
+
+ chip->output[1] += op_calc(phase<SLOT7_1->wavetable) * 2;
+ }
+ }
+
+ /* Snare Drum (verified on real YM3812) */
+ if ( !(chip->mask & OPLL_MASK_SD) )
+ {
+ env = volume_calc(chip->SLOT7_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit8 = ((chip->SLOT7_1->phase>>FREQ_SH)>>8)&1;
+
+ /* when bit8 = 0 phase = 0x100; */
+ /* when bit8 = 1 phase = 0x200; */
+ UINT32 phase = bit8 ? 0x200 : 0x100;
+
+ /* Noise bit XOR'es phase by 0x100 */
+ /* when noisebit = 0 pass the phase from calculation above */
+ /* when noisebit = 1 phase ^= 0x100; */
+ /* in other words: phase ^= (noisebit<<8); */
+ if (noise)
+ phase ^= 0x100;
+
+ chip->output[1] += op_calc(phase<SLOT7_2->wavetable) * 2;
+ }
+ }
+
+ /* Tom Tom (verified on real YM3812) */
+ if ( !(chip->mask & OPLL_MASK_TOM) )
+ {
+ env = volume_calc(chip->SLOT8_1);
+ if( env < ENV_QUIET )
+ chip->output[1] += op_calc(chip->SLOT8_1->phase, env, 0, chip->SLOT8_1->wavetable) * 2;
+ }
+
+ /* Top Cymbal (verified on real YM2413) */
+ if ( !(chip->mask & OPLL_MASK_CYM) )
+ {
+ env = volume_calc(chip->SLOT8_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((chip->SLOT7_1->phase>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((chip->SLOT7_1->phase>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((chip->SLOT7_1->phase>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0x100; */
+ /* when res1 = 1 phase = 0x200 | 0x100; */
+ UINT32 phase = res1 ? 0x300 : 0x100;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((chip->SLOT8_2->phase>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((chip->SLOT8_2->phase>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e | bit5e);
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | 0x100; */
+ if (res2)
+ phase = 0x300;
+
+ chip->output[1] += op_calc(phase<SLOT8_2->wavetable) * 2;
+ }
+ }
+}
+
+
+/* generic table initialize */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+
+ for (x=0; x>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<11; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<11; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] );
+ logerror("\n");
+ #endif
+ }
+ /*logerror("ym2413.c: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+
+
+ for (i=0; i0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ /* waveform 0: standard sinus */
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+
+ /*logerror("ym2413.c: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/
+
+
+ /* waveform 1: __ __ */
+ /* / \____/ \____*/
+ /* output only first half of the sinus waveform (positive one) */
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[1*SIN_LEN+i] = sin_tab[i];
+
+ /*logerror("ym2413.c: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );*/
+ }
+#if 0
+ logerror("YM2413.C: ENV_QUIET= %08x (*32=%08x)\n", ENV_QUIET, ENV_QUIET*32 );
+ for (i=0; ifreqbase = (chip->rate) ? ((double)chip->clock / 72.0) / chip->rate : 0;
+ if ( fabs( chip->freqbase - 1.0 ) < 0.0000001 )
+ chip->freqbase = 1.0;
+#if 0
+ chip->rate = (double)chip->clock / 72.0;
+ chip->freqbase = 1.0;
+ logerror("freqbase=%f\n", chip->freqbase);
+#endif
+
+
+
+ /* make fnumber -> increment counter table */
+ for( i = 0 ; i < 1024; i++ )
+ {
+ /* OPLL (YM2413) phase increment counter = 18bit */
+
+ chip->fn_tab[i] = (UINT32)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+#if 0
+ logerror("ym2413.c: fn_tab[%4i] = %08x (dec=%8i)\n",
+ i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 );
+#endif
+ }
+
+#if 0
+ for( i=0 ; i < 16 ; i++ )
+ {
+ logerror("ym2413.c: sl_tab[%i] = %08x\n", i, sl_tab[i] );
+ }
+ for( i=0 ; i < 8 ; i++ )
+ {
+ int j;
+ logerror("ym2413.c: ksl_tab[oct=%2i] =",i);
+ for (j=0; j<16; j++)
+ {
+ logerror("%08x ", ksl_tab[i*16+j] );
+ }
+ logerror("\n");
+ }
+#endif
+
+
+ /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */
+ chip->lfo_am_inc = (1.0 / 64.0 ) * (1<freqbase;
+
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */
+ chip->lfo_pm_inc = (1.0 / 1024.0) * (1<freqbase;
+
+ /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/
+
+ /* Noise generator: a step takes 1 sample */
+ chip->noise_f = (1.0 / 1.0) * (1<freqbase;
+ /*logerror("YM2413init noise_f=%8x\n", chip->noise_f);*/
+
+ chip->eg_timer_add = (1<freqbase;
+ chip->eg_timer_overflow = ( 1 ) * (1<