diff options
author | Ta180m | 2020-05-17 20:53:50 -0500 |
---|---|---|
committer | Ta180m | 2020-05-17 20:53:50 -0500 |
commit | 22fa8ebd178edb2067bb80c17b841a9885cd0864 (patch) | |
tree | 0011edd9dec286f39ed439d856fe65d66d470d35 | |
parent | 7f2509e1e272eb94c8fab0c829754671daba0450 (diff) |
Removed sound
58 files changed, 57 insertions, 6204 deletions
diff --git a/lib/Blip_Buffer.cpp b/lib/Blip_Buffer.cpp deleted file mode 100644 index db72188..0000000 --- a/lib/Blip_Buffer.cpp +++ /dev/null @@ -1,398 +0,0 @@ - -// Blip_Buffer 0.3.3. http://www.slack.net/~ant/libs/ - -#include "Blip_Buffer.h" - -#include <string.h> -#include <math.h> - -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -Blip_Buffer::Blip_Buffer() -{ - samples_per_sec = 44100; - buffer_ = NULL; - - // try to cause assertion failure if buffer is used before these are set - clocks_per_sec = 0; - factor_ = ~0ul; - offset_ = 0; - buffer_size_ = 0; - length_ = 0; - - bass_freq_ = 16; -} - -void Blip_Buffer::clear( bool entire_buffer ) -{ - long count = (entire_buffer ? buffer_size_ : samples_avail()); - offset_ = 0; - reader_accum = 0; - memset( buffer_, sample_offset & 0xFF, (count + widest_impulse_) * sizeof (buf_t_) ); -} - -blargg_err_t Blip_Buffer::sample_rate( long new_rate, int msec ) -{ - unsigned new_size = (UINT_MAX >> BLIP_BUFFER_ACCURACY) + 1 - widest_impulse_ - 64; - if ( msec != blip_default_length ) - { - size_t s = (new_rate * (msec + 1) + 999) / 1000; - if ( s < new_size ) - new_size = s; - else - require( false ); // requested buffer length exceeds limit - } - - if ( buffer_size_ != new_size ) - { - delete [] buffer_; - buffer_ = NULL; // allow for exception in allocation below - buffer_size_ = 0; - offset_ = 0; - - buffer_ = BLARGG_NEW buf_t_ [new_size + widest_impulse_]; - BLARGG_CHECK_ALLOC( buffer_ ); - } - - buffer_size_ = new_size; - length_ = new_size * 1000 / new_rate - 1; - if ( msec ) - assert( length_ == msec ); // ensure length is same as that passed in - - samples_per_sec = new_rate; - if ( clocks_per_sec ) - clock_rate( clocks_per_sec ); // recalculate factor - - bass_freq( bass_freq_ ); // recalculate shift - - clear(); - - return blargg_success; -} - -void Blip_Buffer::clock_rate( long cps ) -{ - clocks_per_sec = cps; - factor_ = (unsigned long) floor( (double) samples_per_sec / cps * - (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); - require( factor_ > 0 ); // clock_rate/sample_rate ratio is too large -} - -Blip_Buffer::~Blip_Buffer() -{ - delete [] buffer_; -} - -void Blip_Buffer::bass_freq( int freq ) -{ - bass_freq_ = freq; - if ( freq == 0 ) { - bass_shift = 31; // 32 or greater invokes undefined behavior elsewhere - return; - } - bass_shift = 1 + (int) floor( 1.442695041 * log( 0.124 * samples_per_sec / freq ) ); - if ( bass_shift < 0 ) - bass_shift = 0; - if ( bass_shift > 24 ) - bass_shift = 24; -} - -long Blip_Buffer::count_samples( blip_time_t t ) const { - return (resampled_time( t ) >> BLIP_BUFFER_ACCURACY) - (offset_ >> BLIP_BUFFER_ACCURACY); -} - -void Blip_Impulse_::init( blip_pair_t_* imps, int w, int r, int fb ) -{ - fine_bits = fb; - width = w; - impulses = (imp_t*) imps; - generate = true; - volume_unit_ = -1.0; - res = r; - buf = NULL; - - impulse = &impulses [width * res * 2 * (fine_bits ? 2 : 1)]; - offset = 0; -} - -const int impulse_bits = 15; -const long impulse_amp = 1L << impulse_bits; -const long impulse_offset = impulse_amp / 2; - -void Blip_Impulse_::scale_impulse( int unit, imp_t* imp_in ) const -{ - long offset = ((long) unit << impulse_bits) - impulse_offset * unit + - (1 << (impulse_bits - 1)); - imp_t* imp = imp_in; - imp_t* fimp = impulse; - for ( int n = res / 2 + 1; n--; ) - { - int error = unit; - for ( int nn = width; nn--; ) - { - long a = ((long) *fimp++ * unit + offset) >> impulse_bits; - error -= a - unit; - *imp++ = (imp_t) a; - } - - // add error to middle - imp [-width / 2 - 1] += (imp_t) error; - } - - if ( res > 2 ) { - // second half is mirror-image - const imp_t* rev = imp - width - 1; - for ( int nn = (res / 2 - 1) * width - 1; nn--; ) - *imp++ = *--rev; - *imp++ = (imp_t) unit; - } - - // copy to odd offset - *imp++ = (imp_t) unit; - memcpy( imp, imp_in, (res * width - 1) * sizeof *imp ); -} - -const int max_res = 1 << blip_res_bits_; - -void Blip_Impulse_::fine_volume_unit() -{ - // to do: find way of merging in-place without temporary buffer - - imp_t temp [max_res * 2 * Blip_Buffer::widest_impulse_]; - scale_impulse( (offset & 0xffff) << fine_bits, temp ); - imp_t* imp2 = impulses + res * 2 * width; - scale_impulse( offset & 0xffff, imp2 ); - - // merge impulses - imp_t* imp = impulses; - imp_t* src2 = temp; - for ( int n = res / 2 * 2 * width; n--; ) { - *imp++ = *imp2++; - *imp++ = *imp2++; - *imp++ = *src2++; - *imp++ = *src2++; - } -} - -void Blip_Impulse_::volume_unit( double new_unit ) -{ - if ( new_unit == volume_unit_ ) - return; - - if ( generate ) - treble_eq( blip_eq_t( -8.87, 8800, 44100 ) ); - - volume_unit_ = new_unit; - - offset = 0x10001 * (unsigned long) floor( volume_unit_ * 0x10000 + 0.5 ); - - if ( fine_bits ) - fine_volume_unit(); - else - scale_impulse( offset & 0xffff, impulses ); -} - -static const double pi = 3.1415926535897932384626433832795029L; - -void Blip_Impulse_::treble_eq( const blip_eq_t& new_eq ) -{ - if ( !generate && new_eq.treble == eq.treble && new_eq.cutoff == eq.cutoff && - new_eq.sample_rate == eq.sample_rate ) - return; // already calculated with same parameters - - generate = false; - eq = new_eq; - - double treble = pow( 10.0, 1.0 / 20 * eq.treble ); // dB (-6dB = 0.50) - if ( treble < 0.000005 ) - treble = 0.000005; - - const double treble_freq = 22050.0; // treble level at 22 kHz harmonic - const double sample_rate = eq.sample_rate; - const double pt = treble_freq * 2 / sample_rate; - double cutoff = eq.cutoff * 2 / sample_rate; - if ( cutoff >= pt * 0.95 || cutoff >= 0.95 ) { - cutoff = 0.5; - treble = 1.0; - } - - // DSF Synthesis (See T. Stilson & J. Smith (1996), - // Alias-free digital synthesis of classic analog waveforms) - - // reduce adjacent impulse interference by using small part of wide impulse - const double n_harm = 4096; - const double rolloff = pow( treble, 1.0 / (n_harm * pt - n_harm * cutoff) ); - const double rescale = 1.0 / pow( rolloff, n_harm * cutoff ); - - const double pow_a_n = rescale * pow( rolloff, n_harm ); - const double pow_a_nc = rescale * pow( rolloff, n_harm * cutoff ); - - double total = 0.0; - const double to_angle = pi / 2 / n_harm / max_res; - - float buf [max_res * (Blip_Buffer::widest_impulse_ - 2) / 2]; - const int size = max_res * (width - 2) / 2; - for ( int i = size; i--; ) - { - double angle = (i * 2 + 1) * to_angle; - - // equivalent - //double y = dsf( angle, n_harm * cutoff, 1.0 ); - //y -= rescale * dsf( angle, n_harm * cutoff, rolloff ); - //y += rescale * dsf( angle, n_harm, rolloff ); - - const double cos_angle = cos( angle ); - const double cos_nc_angle = cos( n_harm * cutoff * angle ); - const double cos_nc1_angle = cos( (n_harm * cutoff - 1.0) * angle ); - - double b = 2.0 - 2.0 * cos_angle; - double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; - - double d = 1.0 + rolloff * (rolloff - 2.0 * cos_angle); - double c = pow_a_n * rolloff * cos( (n_harm - 1.0) * angle ) - - pow_a_n * cos( n_harm * angle ) - - pow_a_nc * rolloff * cos_nc1_angle + - pow_a_nc * cos_nc_angle; - - // optimization of a / b + c / d - double y = (a * d + c * b) / (b * d); - - // fixed window which affects wider impulses more - if ( width > 12 ) { - double window = cos( n_harm / 1.25 / Blip_Buffer::widest_impulse_ * angle ); - y *= window * window; - } - - total += (float) y; - buf [i] = (float) y; - } - - // integrate runs of length 'max_res' - double factor = impulse_amp * 0.5 / total; // 0.5 accounts for other mirrored half - imp_t* imp = impulse; - const int step = max_res / res; - int offset = res > 1 ? max_res : max_res / 2; - for ( int n = res / 2 + 1; n--; offset -= step ) - { - for ( int w = -width / 2; w < width / 2; w++ ) - { - double sum = 0; - for ( int i = max_res; i--; ) - { - int index = w * max_res + offset + i; - if ( index < 0 ) - index = -index - 1; - if ( index < size ) - sum += buf [index]; - } - *imp++ = (imp_t) floor( sum * factor + (impulse_offset + 0.5) ); - } - } - - // rescale - double unit = volume_unit_; - if ( unit >= 0 ) { - volume_unit_ = -1; - volume_unit( unit ); - } -} - -void Blip_Buffer::remove_samples( long count ) -{ - require( buffer_ ); // sample rate must have been set - - if ( !count ) // optimization - return; - - remove_silence( count ); - - // Allows synthesis slightly past time passed to end_frame(), as long as it's - // not more than an output sample. - // to do: kind of hacky, could add run_until() which keeps track of extra synthesis - int const copy_extra = 1; - - // copy remaining samples to beginning and clear old samples - long remain = samples_avail() + widest_impulse_ + copy_extra; - if ( count >= remain ) - memmove( buffer_, buffer_ + count, remain * sizeof (buf_t_) ); - else - memcpy( buffer_, buffer_ + count, remain * sizeof (buf_t_) ); - memset( buffer_ + remain, sample_offset & 0xFF, count * sizeof (buf_t_) ); -} - -#include BLARGG_ENABLE_OPTIMIZER - -long Blip_Buffer::read_samples( blip_sample_t* out, long max_samples, bool stereo ) -{ - require( buffer_ ); // sample rate must have been set - - long count = samples_avail(); - if ( count > max_samples ) - count = max_samples; - - if ( !count ) - return 0; // optimization - - int sample_offset = this->sample_offset; - int bass_shift = this->bass_shift; - buf_t_* buf = buffer_; - long accum = reader_accum; - - if ( !stereo ) { - for ( long n = count; n--; ) { - long s = accum >> accum_fract; - accum -= accum >> bass_shift; - accum += (long (*buf++) - sample_offset) << accum_fract; - *out++ = (blip_sample_t) s; - - // clamp sample - if ( (BOOST::int16_t) s != s ) - out [-1] = blip_sample_t (0x7FFF - (s >> 24)); - } - } - else { - for ( long n = count; n--; ) { - long s = accum >> accum_fract; - accum -= accum >> bass_shift; - accum += (long (*buf++) - sample_offset) << accum_fract; - *out = (blip_sample_t) s; - out += 2; - - // clamp sample - if ( (BOOST::int16_t) s != s ) - out [-2] = blip_sample_t (0x7FFF - (s >> 24)); - } - } - - reader_accum = accum; - - remove_samples( count ); - - return count; -} - -void Blip_Buffer::mix_samples( const blip_sample_t* in, long count ) -{ - buf_t_* buf = &buffer_ [(offset_ >> BLIP_BUFFER_ACCURACY) + (widest_impulse_ / 2 - 1)]; - - int prev = 0; - while ( count-- ) { - int s = *in++; - *buf += s - prev; - prev = s; - ++buf; - } - *buf -= *--in; -} - diff --git a/lib/Multi_Buffer.cpp b/lib/Multi_Buffer.cpp deleted file mode 100644 index dd459eb..0000000 --- a/lib/Multi_Buffer.cpp +++ /dev/null @@ -1,201 +0,0 @@ - -// Blip_Buffer 0.3.3. http://www.slack.net/~ant/libs/ - -#include "Multi_Buffer.h" - -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) -{ - length_ = 0; - sample_rate_ = 0; -} - -blargg_err_t Multi_Buffer::set_channel_count( int ) -{ - return blargg_success; -} - -Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) -{ -} - -Mono_Buffer::~Mono_Buffer() -{ -} - -blargg_err_t Mono_Buffer::sample_rate( long rate, int msec ) -{ - BLARGG_RETURN_ERR( buf.sample_rate( rate, msec ) ); - return Multi_Buffer::sample_rate( buf.sample_rate(), buf.length() ); -} - -Mono_Buffer::channel_t Mono_Buffer::channel( int index ) -{ - channel_t ch; - ch.center = &buf; - ch.left = &buf; - ch.right = &buf; - return ch; -} - -void Mono_Buffer::end_frame( blip_time_t t, bool ) -{ - buf.end_frame( t ); -} - -Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) -{ - chan.center = &bufs [0]; - chan.left = &bufs [1]; - chan.right = &bufs [2]; -} - -Stereo_Buffer::~Stereo_Buffer() -{ -} - -blargg_err_t Stereo_Buffer::sample_rate( long rate, int msec ) -{ - for ( int i = 0; i < buf_count; i++ ) - BLARGG_RETURN_ERR( bufs [i].sample_rate( rate, msec ) ); - return Multi_Buffer::sample_rate( bufs [0].sample_rate(), bufs [0].length() ); -} - -void Stereo_Buffer::clock_rate( long rate ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clock_rate( rate ); -} - -void Stereo_Buffer::bass_freq( int bass ) -{ - for ( unsigned i = 0; i < buf_count; i++ ) - bufs [i].bass_freq( bass ); -} - -void Stereo_Buffer::clear() -{ - stereo_added = false; - was_stereo = false; - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clear(); -} - -void Stereo_Buffer::end_frame( blip_time_t clock_count, bool stereo ) -{ - for ( unsigned i = 0; i < buf_count; i++ ) - bufs [i].end_frame( clock_count ); - - stereo_added |= stereo; -} - -long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) -{ - require( !(count & 1) ); // count must be even - count = (unsigned) count / 2; - - long avail = bufs [0].samples_avail(); - if ( count > avail ) - count = avail; - if ( count ) - { - if ( stereo_added || was_stereo ) - { - mix_stereo( out, count ); - - bufs [0].remove_samples( count ); - bufs [1].remove_samples( count ); - bufs [2].remove_samples( count ); - } - else - { - mix_mono( out, count ); - - bufs [0].remove_samples( count ); - - bufs [1].remove_silence( count ); - bufs [2].remove_silence( count ); - } - - // to do: this might miss opportunities for optimization - if ( !bufs [0].samples_avail() ) { - was_stereo = stereo_added; - stereo_added = false; - } - } - - return count * 2; -} - -#include BLARGG_ENABLE_OPTIMIZER - -void Stereo_Buffer::mix_stereo( blip_sample_t* out, long count ) -{ - Blip_Reader left; - Blip_Reader right; - Blip_Reader center; - - left.begin( bufs [1] ); - right.begin( bufs [2] ); - int bass = center.begin( bufs [0] ); - - while ( count-- ) - { - int c = center.read(); - long l = c + left.read(); - long r = c + right.read(); - center.next( bass ); - out [0] = l; - out [1] = r; - out += 2; - - if ( (BOOST::int16_t) l != l ) - out [-2] = 0x7FFF - (l >> 24); - - left.next( bass ); - right.next( bass ); - - if ( (BOOST::int16_t) r != r ) - out [-1] = 0x7FFF - (r >> 24); - } - - center.end( bufs [0] ); - right.end( bufs [2] ); - left.end( bufs [1] ); -} - -void Stereo_Buffer::mix_mono( blip_sample_t* out, long count ) -{ - Blip_Reader in; - int bass = in.begin( bufs [0] ); - - while ( count-- ) - { - long s = in.read(); - in.next( bass ); - out [0] = s; - out [1] = s; - out += 2; - - if ( (BOOST::int16_t) s != s ) { - s = 0x7FFF - (s >> 24); - out [-2] = s; - out [-1] = s; - } - } - - in.end( bufs [0] ); -} - diff --git a/lib/Nes_Apu.cpp b/lib/Nes_Apu.cpp deleted file mode 100644 index 5b1fdc5..0000000 --- a/lib/Nes_Apu.cpp +++ /dev/null @@ -1,341 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/ - -#include "Nes_Apu.h" - -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -Nes_Apu::Nes_Apu() -{ - dmc.apu = this; - dmc.rom_reader = NULL; - square1.synth = &square_synth; - square2.synth = &square_synth; - irq_notifier_ = NULL; - - oscs [0] = &square1; - oscs [1] = &square2; - oscs [2] = ▵ - oscs [3] = &noise; - oscs [4] = &dmc; - - output( NULL ); - volume( 1.0 ); - reset( false ); -} - -Nes_Apu::~Nes_Apu() -{ -} - -void Nes_Apu::treble_eq( const blip_eq_t& eq ) -{ - square_synth.treble_eq( eq ); - triangle.synth.treble_eq( eq ); - noise.synth.treble_eq( eq ); - dmc.synth.treble_eq( eq ); -} - -void Nes_Apu::buffer_cleared() -{ - square1.last_amp = 0; - square2.last_amp = 0; - triangle.last_amp = 0; - noise.last_amp = 0; - dmc.last_amp = 0; -} - -void Nes_Apu::enable_nonlinear( double v ) -{ - dmc.nonlinear = true; - square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 * v ); - - const double tnd = 0.75 / 202 * 0.48; - triangle.synth.volume_unit( 3 * tnd ); - noise.synth.volume_unit( 2 * tnd ); - dmc.synth.volume_unit( tnd ); - - buffer_cleared(); -} - -void Nes_Apu::volume( double v ) -{ - dmc.nonlinear = false; - square_synth.volume( 0.1128 * v ); - triangle.synth.volume( 0.12765 * v ); - noise.synth.volume( 0.0741 * v ); - dmc.synth.volume( 0.42545 * v ); -} - -void Nes_Apu::output( Blip_Buffer* buffer ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buffer ); -} - -void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) -{ - // to do: time pal frame periods exactly - frame_period = pal_mode ? 8314 : 7458; - dmc.pal_mode = pal_mode; - - square1.reset(); - square2.reset(); - triangle.reset(); - noise.reset(); - dmc.reset(); - - last_time = 0; - osc_enables = 0; - irq_flag = false; - earliest_irq_ = no_irq; - frame_delay = 1; - write_register( 0, 0x4017, 0x00 ); - write_register( 0, 0x4015, 0x00 ); - - for ( cpu_addr_t addr = start_addr; addr <= 0x4013; addr++ ) - write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); - - dmc.dac = initial_dmc_dac; - if ( !dmc.nonlinear ) - dmc.last_amp = initial_dmc_dac; // prevent output transition -} - -void Nes_Apu::irq_changed() -{ - cpu_time_t new_irq = dmc.next_irq; - if ( dmc.irq_flag | irq_flag ) { - new_irq = 0; - } - else if ( new_irq > next_irq ) { - new_irq = next_irq; - } - - if ( new_irq != earliest_irq_ ) { - earliest_irq_ = new_irq; - if ( irq_notifier_ ) - irq_notifier_( irq_data ); - } -} - -// frames - -void Nes_Apu::run_until( cpu_time_t end_time ) -{ - require( end_time >= last_time ); - - if ( end_time == last_time ) - return; - - while ( true ) - { - // earlier of next frame time or end time - cpu_time_t time = last_time + frame_delay; - if ( time > end_time ) - time = end_time; - frame_delay -= time - last_time; - - // run oscs to present - square1.run( last_time, time ); - square2.run( last_time, time ); - triangle.run( last_time, time ); - noise.run( last_time, time ); - dmc.run( last_time, time ); - last_time = time; - - if ( time == end_time ) - break; // no more frames to run - - // take frame-specific actions - frame_delay = frame_period; - switch ( frame++ ) - { - case 0: - if ( !(frame_mode & 0xc0) ) { - next_irq = time + frame_period * 4 + 1; - irq_flag = true; - } - // fall through - case 2: - // clock length and sweep on frames 0 and 2 - square1.clock_length( 0x20 ); - square2.clock_length( 0x20 ); - noise.clock_length( 0x20 ); - triangle.clock_length( 0x80 ); // different bit for halt flag on triangle - - square1.clock_sweep( -1 ); - square2.clock_sweep( 0 ); - break; - - case 1: - // frame 1 is slightly shorter - frame_delay -= 2; - break; - - case 3: - frame = 0; - - // frame 3 is almost twice as long in mode 1 - if ( frame_mode & 0x80 ) - frame_delay += frame_period - 6; - break; - } - - // clock envelopes and linear counter every frame - triangle.clock_linear_counter(); - square1.clock_envelope(); - square2.clock_envelope(); - noise.clock_envelope(); - } -} - -void Nes_Apu::end_frame( cpu_time_t end_time ) -{ - if ( end_time > last_time ) - run_until( end_time ); - - // make times relative to new frame - last_time -= end_time; - require( last_time >= 0 ); - - if ( next_irq != no_irq ) { - next_irq -= end_time; - assert( next_irq >= 0 ); - } - if ( dmc.next_irq != no_irq ) { - dmc.next_irq -= end_time; - assert( dmc.next_irq >= 0 ); - } - if ( earliest_irq_ != no_irq ) { - earliest_irq_ -= end_time; - if ( earliest_irq_ < 0 ) - earliest_irq_ = 0; - } -} - -// registers - -static const unsigned char length_table [0x20] = { - 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, - 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, - 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, - 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E -}; - -void Nes_Apu::write_register( cpu_time_t time, cpu_addr_t addr, int data ) -{ - require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx) - require( (unsigned) data <= 0xff ); - - // Ignore addresses outside range - if ( addr < start_addr || end_addr < addr ) - return; - - run_until( time ); - - if ( addr < 0x4014 ) - { - // Write to channel - int osc_index = (addr - start_addr) >> 2; - Nes_Osc* osc = oscs [osc_index]; - - int reg = addr & 3; - osc->regs [reg] = data; - osc->reg_written [reg] = true; - - if ( osc_index == 4 ) - { - // handle DMC specially - dmc.write_register( reg, data ); - } - else if ( reg == 3 ) - { - // load length counter - if ( (osc_enables >> osc_index) & 1 ) - osc->length_counter = length_table [(data >> 3) & 0x1f]; - - // reset square phase - if ( osc_index < 2 ) - ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1; - } - } - else if ( addr == 0x4015 ) - { - // Channel enables - for ( int i = osc_count; i--; ) - if ( !((data >> i) & 1) ) - oscs [i]->length_counter = 0; - - bool recalc_irq = dmc.irq_flag; - dmc.irq_flag = false; - - int old_enables = osc_enables; - osc_enables = data; - if ( !(data & 0x10) ) { - dmc.next_irq = no_irq; - recalc_irq = true; - } - else if ( !(old_enables & 0x10) ) { - dmc.start(); // dmc just enabled - } - - if ( recalc_irq ) - irq_changed(); - } - else if ( addr == 0x4017 ) - { - // Frame mode - frame_mode = data; - - bool irq_enabled = !(data & 0x40); - irq_flag &= irq_enabled; - next_irq = no_irq; - - // mode 1 - frame_delay = (frame_delay & 1); - frame = 0; - - if ( !(data & 0x80) ) - { - // mode 0 - frame = 1; - frame_delay += frame_period; - if ( irq_enabled ) - next_irq = time + frame_delay + frame_period * 3; - } - - irq_changed(); - } -} - -int Nes_Apu::read_status( cpu_time_t time ) -{ - run_until( time - 1 ); - - int result = (dmc.irq_flag << 7) | (irq_flag << 6); - - for ( int i = 0; i < osc_count; i++ ) - if ( oscs [i]->length_counter ) - result |= 1 << i; - - run_until( time ); - - if ( irq_flag ) { - irq_flag = false; - irq_changed(); - } - - return result; -} - diff --git a/lib/Nes_Namco.cpp b/lib/Nes_Namco.cpp deleted file mode 100644 index 89fdf86..0000000 --- a/lib/Nes_Namco.cpp +++ /dev/null @@ -1,160 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/ - -#include "Nes_Namco.h" - -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -Nes_Namco::Nes_Namco() -{ - output( NULL ); - volume( 1.0 ); - reset(); -} - -Nes_Namco::~Nes_Namco() -{ -} - -void Nes_Namco::reset() -{ - addr_reg = 0; - - int i; - for ( i = 0; i < reg_count; i++ ) - reg [i] = 0; - - for ( i = 0; i < osc_count; i++ ) - { - Namco_Osc& osc = oscs [i]; - osc.delay = 0; - osc.last_amp = 0; - osc.wave_pos = 0; - } -} - -void Nes_Namco::output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); -} - -BOOST::uint8_t& Nes_Namco::access() -{ - int addr = addr_reg & 0x7f; - if ( addr_reg & 0x80 ) - addr_reg = (addr + 1) | 0x80; - return reg [addr]; -} - -/* -void Nes_Namco::reflect_state( Tagged_Data& data ) -{ - reflect_int16( data, 'ADDR', &addr_reg ); - - static const char hex [17] = "0123456789ABCDEF"; - int i; - for ( i = 0; i < reg_count; i++ ) - reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], ® [i] ); - - for ( i = 0; i < osc_count; i++ ) - { - reflect_int32( data, 'DLY0' + i, &oscs [i].delay ); - reflect_int16( data, 'POS0' + i, &oscs [i].wave_pos ); - } -} -*/ - -void Nes_Namco::end_frame( cpu_time_t time ) -{ - if ( time > last_time ) - run_until( time ); - - last_time -= time; - assert( last_time >= 0 ); -} - -#include BLARGG_ENABLE_OPTIMIZER - -void Nes_Namco::run_until( cpu_time_t nes_end_time ) -{ - int active_oscs = ((reg [0x7f] >> 4) & 7) + 1; - for ( int i = osc_count - active_oscs; i < osc_count; i++ ) - { - Namco_Osc& osc = oscs [i]; - Blip_Buffer* output = osc.output; - if ( !output ) - continue; - - Blip_Buffer::resampled_time_t time = - output->resampled_time( last_time ) + osc.delay; - Blip_Buffer::resampled_time_t end_time = output->resampled_time( nes_end_time ); - osc.delay = 0; - if ( time < end_time ) - { - const BOOST::uint8_t* osc_reg = ® [i * 8 + 0x40]; - if ( !(osc_reg [4] & 0xe0) ) - continue; - - int volume = osc_reg [7] & 15; - if ( !volume ) - continue; - - long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0]; - if ( !freq ) - continue; - Blip_Buffer::resampled_time_t period = - output->resampled_duration( 983040 ) / freq * active_oscs; - - int wave_size = (8 - ((osc_reg [4] >> 2) & 7)) * 4; - if ( !wave_size ) - continue; - - int last_amp = osc.last_amp; - int wave_pos = osc.wave_pos; - - do - { - // read wave sample - int addr = wave_pos + osc_reg [6]; - int sample = reg [addr >> 1]; - wave_pos++; - if ( addr & 1 ) - sample >>= 4; - sample = (sample & 15) * volume; - - // output impulse if amplitude changed - int delta = sample - last_amp; - if ( delta ) - { - last_amp = sample; - synth.offset_resampled( time, delta, output ); - } - - // next sample - time += period; - if ( wave_pos >= wave_size ) - wave_pos = 0; - } - while ( time < end_time ); - - osc.wave_pos = wave_pos; - osc.last_amp = last_amp; - } - osc.delay = time - end_time; - } - - last_time = nes_end_time; -} - diff --git a/lib/Nes_Oscs.cpp b/lib/Nes_Oscs.cpp deleted file mode 100644 index 272f62f..0000000 --- a/lib/Nes_Oscs.cpp +++ /dev/null @@ -1,498 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/ - -#include "Nes_Apu.h" - -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -// Nes_Osc - -void Nes_Osc::clock_length( int halt_mask ) -{ - if ( length_counter && !(regs [0] & halt_mask) ) - length_counter--; -} - -void Nes_Envelope::clock_envelope() -{ - int period = regs [0] & 15; - if ( reg_written [3] ) { - reg_written [3] = false; - env_delay = period; - envelope = 15; - } - else if ( --env_delay < 0 ) { - env_delay = period; - if ( envelope | (regs [0] & 0x20) ) - envelope = (envelope - 1) & 15; - } -} - -int Nes_Envelope::volume() const -{ - return length_counter == 0 ? 0 : (regs [0] & 0x10) ? (regs [0] & 15) : envelope; -} - -// Nes_Square - -void Nes_Square::clock_sweep( int negative_adjust ) -{ - int sweep = regs [1]; - - if ( --sweep_delay < 0 ) - { - reg_written [1] = true; - - int period = this->period(); - int shift = sweep & shift_mask; - if ( shift && (sweep & 0x80) && period >= 8 ) - { - int offset = period >> shift; - - if ( sweep & negate_flag ) - offset = negative_adjust - offset; - - if ( period + offset < 0x800 ) - { - period += offset; - // rewrite period - regs [2] = period & 0xff; - regs [3] = (regs [3] & ~7) | ((period >> 8) & 7); - } - } - } - - if ( reg_written [1] ) { - reg_written [1] = false; - sweep_delay = (sweep >> 4) & 7; - } -} - -void Nes_Square::run( cpu_time_t time, cpu_time_t end_time ) -{ - if ( !output ) - return; - - const int volume = this->volume(); - const int period = this->period(); - int offset = period >> (regs [1] & shift_mask); - if ( regs [1] & negate_flag ) - offset = 0; - - const int timer_period = (period + 1) * 2; - if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) - { - if ( last_amp ) { - synth->offset( time, -last_amp, output ); - last_amp = 0; - } - - time += delay; - if ( time < end_time ) - { - // maintain proper phase - int count = (end_time - time + timer_period - 1) / timer_period; - phase = (phase + count) & (phase_range - 1); - time += (long) count * timer_period; - } - } - else - { - // handle duty select - int duty_select = (regs [0] >> 6) & 3; - int duty = 1 << duty_select; // 1, 2, 4, 2 - int amp = 0; - if ( duty_select == 3 ) { - duty = 2; // negated 25% - amp = volume; - } - if ( phase < duty ) - amp ^= volume; - - int delta = update_amp( amp ); - if ( delta ) - synth->offset( time, delta, output ); - - time += delay; - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - const Synth* synth = this->synth; - int delta = amp * 2 - volume; - int phase = this->phase; - - do { - phase = (phase + 1) & (phase_range - 1); - if ( phase == 0 || phase == duty ) { - delta = -delta; - synth->offset_inline( time, delta, output ); - } - time += timer_period; - } - while ( time < end_time ); - - last_amp = (delta + volume) >> 1; - this->phase = phase; - } - } - - delay = time - end_time; -} - -// Nes_Triangle - -void Nes_Triangle::clock_linear_counter() -{ - if ( reg_written [3] ) - linear_counter = regs [0] & 0x7f; - else if ( linear_counter ) - linear_counter--; - - if ( !(regs [0] & 0x80) ) - reg_written [3] = false; -} - -inline int Nes_Triangle::calc_amp() const -{ - int amp = phase_range - phase; - if ( amp < 0 ) - amp = phase - (phase_range + 1); - return amp; -} - -void Nes_Triangle::run( cpu_time_t time, cpu_time_t end_time ) -{ - if ( !output ) - return; - - // to do: track phase when period < 3 - // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks. - - int delta = update_amp( calc_amp() ); - if ( delta ) - synth.offset( time, delta, output ); - - time += delay; - const int timer_period = period() + 1; - if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) - { - time = end_time; - } - else if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - - int phase = this->phase; - int volume = 1; - if ( phase > phase_range ) { - phase -= phase_range; - volume = -volume; - } - - do { - if ( --phase == 0 ) { - phase = phase_range; - volume = -volume; - } - else { - synth.offset_inline( time, volume, output ); - } - - time += timer_period; - } - while ( time < end_time ); - - if ( volume < 0 ) - phase += phase_range; - this->phase = phase; - last_amp = calc_amp(); - } - delay = time - end_time; -} - -// Nes_Dmc - -void Nes_Dmc::reset() -{ - address = 0; - dac = 0; - buf = 0; - bits_remain = 1; - bits = 0; - buf_empty = true; - silence = true; - next_irq = Nes_Apu::no_irq; - irq_flag = false; - irq_enabled = false; - - Nes_Osc::reset(); - period = 0x036; -} - -void Nes_Dmc::recalc_irq() -{ - cpu_time_t irq = Nes_Apu::no_irq; - if ( irq_enabled && length_counter ) - irq = apu->last_time + delay + - ((length_counter - 1) * 8 + bits_remain - 1) * cpu_time_t (period) + 1; - if ( irq != next_irq ) { - next_irq = irq; - apu->irq_changed(); - } -} - -int Nes_Dmc::count_reads( cpu_time_t time, cpu_time_t* last_read ) const -{ - if ( last_read ) - *last_read = time; - - if ( length_counter == 0 ) - return 0; // not reading - - long first_read = apu->last_time + delay + long (bits_remain - 1) * period; - long avail = time - first_read; - if ( avail <= 0 ) - return 0; - - int count = (avail - 1) / (period * 8) + 1; - if ( !(regs [0] & loop_flag) && count > length_counter ) - count = length_counter; - - if ( last_read ) { - *last_read = first_read + (count - 1) * (period * 8) + 1; - assert( *last_read <= time ); - assert( count == count_reads( *last_read, NULL ) ); - assert( count - 1 == count_reads( *last_read - 1, NULL ) ); - } - - return count; -} - -static const short dmc_period_table [2] [16] = { - 0x1ac, 0x17c, 0x154, 0x140, 0x11e, 0x0fe, 0x0e2, 0x0d6, // NTSC - 0x0be, 0x0a0, 0x08e, 0x080, 0x06a, 0x054, 0x048, 0x036, - - 0x18e, 0x161, 0x13c, 0x129, 0x10a, 0x0ec, 0x0d2, 0x0c7, // PAL (totally untested) - 0x0b1, 0x095, 0x084, 0x077, 0x062, 0x04e, 0x043, 0x032 // to do: verify PAL periods -}; - -inline void Nes_Dmc::reload_sample() -{ - address = 0x4000 + regs [2] * 0x40; - length_counter = regs [3] * 0x10 + 1; -} - -static const unsigned char dac_table [128] = { - 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 8, 9, - 10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 15, 16, 17, 17, 18, 18, - 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, - 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 32, 33, 33, 34, - 34, 35, 35, 35, 36, 36, 37, 37, 38, 38, 38, 39, 39, 40, 40, 40, - 41, 41, 42, 42, 42, 43, 43, 44, 44, 44, 45, 45, 45, 46, 46, 47, - 47, 47, 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, 51, 51, 52, 52, - 52, 53, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 57, 57, 57 -}; - -void Nes_Dmc::write_register( int addr, int data ) -{ - if ( addr == 0 ) { - period = dmc_period_table [pal_mode] [data & 15]; - irq_enabled = (data & 0xc0) == 0x80; // enabled only if loop disabled - irq_flag &= irq_enabled; - recalc_irq(); - } - else if ( addr == 1 ) - { - if ( !nonlinear ) - { - // adjust last_amp so that "pop" amplitude will be properly non-linear - // with respect to change in dac - int old_amp = dac_table [dac]; - dac = data & 0x7F; - int diff = dac_table [dac] - old_amp; - last_amp = dac - diff; - } - - dac = data & 0x7F; - } -} - -void Nes_Dmc::start() -{ - reload_sample(); - fill_buffer(); - recalc_irq(); -} - -void Nes_Dmc::fill_buffer() -{ - if ( buf_empty && length_counter ) - { - require( rom_reader ); // rom_reader must be set - buf = rom_reader( rom_reader_data, 0x8000u + address ); - address = (address + 1) & 0x7FFF; - buf_empty = false; - if ( --length_counter == 0 ) - { - if ( regs [0] & loop_flag ) { - reload_sample(); - } - else { - apu->osc_enables &= ~0x10; - irq_flag = irq_enabled; - next_irq = Nes_Apu::no_irq; - apu->irq_changed(); - } - } - } -} - -void Nes_Dmc::run( cpu_time_t time, cpu_time_t end_time ) -{ - if ( !output ) - return; - - int delta = update_amp( dac ); - if ( delta ) - synth.offset( time, delta, output ); - - time += delay; - if ( time < end_time ) - { - int bits_remain = this->bits_remain; - if ( silence && buf_empty ) - { - int count = (end_time - time + period - 1) / period; - bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1; - time += count * period; - } - else - { - Blip_Buffer* const output = this->output; - const int period = this->period; - int bits = this->bits; - int dac = this->dac; - - do - { - if ( !silence ) - { - const int step = (bits & 1) * 4 - 2; - bits >>= 1; - if ( unsigned (dac + step) <= 0x7F ) { - dac += step; - synth.offset_inline( time, step, output ); - } - } - - time += period; - - if ( --bits_remain == 0 ) - { - bits_remain = 8; - if ( buf_empty ) { - silence = true; - } - else { - silence = false; - bits = buf; - buf_empty = true; - fill_buffer(); - } - } - } - while ( time < end_time ); - - this->dac = dac; - this->last_amp = dac; - this->bits = bits; - } - this->bits_remain = bits_remain; - } - delay = time - end_time; -} - -// Nes_Noise - -#include BLARGG_ENABLE_OPTIMIZER - -static const short noise_period_table [16] = { - 0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0, - 0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4 -}; - -void Nes_Noise::run( cpu_time_t time, cpu_time_t end_time ) -{ - if ( !output ) - return; - - const int volume = this->volume(); - int amp = (noise & 1) ? volume : 0; - int delta = update_amp( amp ); - if ( delta ) - synth.offset( time, delta, output ); - - time += delay; - if ( time < end_time ) - { - const int mode_flag = 0x80; - - int period = noise_period_table [regs [2] & 15]; - if ( !volume ) - { - // round to next multiple of period - time += (end_time - time + period - 1) / period * period; - - // approximate noise cycling while muted, by shuffling up noise register - // to do: precise muted noise cycling? - if ( !(regs [2] & mode_flag) ) { - int feedback = (noise << 13) ^ (noise << 14); - noise = (feedback & 0x4000) | (noise >> 1); - } - } - else - { - Blip_Buffer* const output = this->output; - - // using resampled time avoids conversion in synth.offset() - Blip_Buffer::resampled_time_t rperiod = output->resampled_duration( period ); - Blip_Buffer::resampled_time_t rtime = output->resampled_time( time ); - - int noise = this->noise; - int delta = amp * 2 - volume; - const int tap = (regs [2] & mode_flag ? 8 : 13); - - do { - int feedback = (noise << tap) ^ (noise << 14); - time += period; - - if ( (noise + 1) & 2 ) { - // bits 0 and 1 of noise differ - delta = -delta; - synth.offset_resampled( rtime, delta, output ); - } - - rtime += rperiod; - noise = (feedback & 0x4000) | (noise >> 1); - } - while ( time < end_time ); - - last_amp = (delta + volume) >> 1; - this->noise = noise; - } - } - - delay = time - end_time; -} - diff --git a/lib/Nes_Vrc6.cpp b/lib/Nes_Vrc6.cpp deleted file mode 100644 index e57e8a5..0000000 --- a/lib/Nes_Vrc6.cpp +++ /dev/null @@ -1,231 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/ - -#include "Nes_Vrc6.h" - -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -Nes_Vrc6::Nes_Vrc6() -{ - output( NULL ); - volume( 1.0 ); - reset(); -} - -Nes_Vrc6::~Nes_Vrc6() -{ -} - -void Nes_Vrc6::reset() -{ - last_time = 0; - for ( int i = 0; i < osc_count; i++ ) - { - Vrc6_Osc& osc = oscs [i]; - for ( int j = 0; j < reg_count; j++ ) - osc.regs [j] = 0; - osc.delay = 0; - osc.last_amp = 0; - osc.phase = 1; - osc.amp = 0; - } -} - -void Nes_Vrc6::volume( double v ) -{ - v *= 0.0967 * 2; - saw_synth.volume( v ); - square_synth.volume( v * 0.5 ); -} - -void Nes_Vrc6::treble_eq( blip_eq_t const& eq ) -{ - saw_synth.treble_eq( eq ); - square_synth.treble_eq( eq ); -} - -void Nes_Vrc6::output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); -} - -void Nes_Vrc6::run_until( cpu_time_t time ) -{ - require( time >= last_time ); - run_square( oscs [0], time ); - run_square( oscs [1], time ); - run_saw( time ); - last_time = time; -} - -void Nes_Vrc6::write_osc( cpu_time_t time, int osc_index, int reg, int data ) -{ - require( (unsigned) osc_index < osc_count ); - require( (unsigned) reg < reg_count ); - - run_until( time ); - oscs [osc_index].regs [reg] = data; -} - -void Nes_Vrc6::end_frame( cpu_time_t time ) -{ - if ( time > last_time ) - run_until( time ); - last_time -= time; - assert( last_time >= 0 ); -} - -void Nes_Vrc6::save_snapshot( vrc6_snapshot_t* out ) const -{ - out->saw_amp = oscs [2].amp; - for ( int i = 0; i < osc_count; i++ ) - { - Vrc6_Osc const& osc = oscs [i]; - for ( int r = 0; r < reg_count; r++ ) - out->regs [i] [r] = osc.regs [r]; - - out->delays [i] = osc.delay; - out->phases [i] = osc.phase; - } -} - -void Nes_Vrc6::load_snapshot( vrc6_snapshot_t const& in ) -{ - reset(); - oscs [2].amp = in.saw_amp; - for ( int i = 0; i < osc_count; i++ ) - { - Vrc6_Osc& osc = oscs [i]; - for ( int r = 0; r < reg_count; r++ ) - osc.regs [r] = in.regs [i] [r]; - - osc.delay = in.delays [i]; - osc.phase = in.phases [i]; - } - if ( !oscs [2].phase ) - oscs [2].phase = 1; -} - -#include BLARGG_ENABLE_OPTIMIZER - -void Nes_Vrc6::run_square( Vrc6_Osc& osc, cpu_time_t end_time ) -{ - Blip_Buffer* output = osc.output; - if ( !output ) - return; - - int volume = osc.regs [0] & 15; - if ( !(osc.regs [2] & 0x80) ) - volume = 0; - - int gate = osc.regs [0] & 0x80; - int duty = ((osc.regs [0] >> 4) & 7) + 1; - int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp; - cpu_time_t time = last_time; - if ( delta ) - { - osc.last_amp += delta; - square_synth.offset( time, delta, output ); - } - - time += osc.delay; - osc.delay = 0; - int period = osc.period(); - if ( volume && !gate && period > 4 ) - { - if ( time < end_time ) - { - int phase = osc.phase; - - do - { - phase++; - if ( phase == 16 ) - { - phase = 0; - osc.last_amp = volume; - square_synth.offset( time, volume, output ); - } - if ( phase == duty ) - { - osc.last_amp = 0; - square_synth.offset( time, -volume, output ); - } - time += period; - } - while ( time < end_time ); - - osc.phase = phase; - } - osc.delay = time - end_time; - } -} - -void Nes_Vrc6::run_saw( cpu_time_t end_time ) -{ - Vrc6_Osc& osc = oscs [2]; - Blip_Buffer* output = osc.output; - if ( !output ) - return; - - int amp = osc.amp; - int amp_step = osc.regs [0] & 0x3F; - cpu_time_t time = last_time; - int last_amp = osc.last_amp; - if ( !(osc.regs [2] & 0x80) || !(amp_step | amp) ) - { - osc.delay = 0; - int delta = (amp >> 3) - last_amp; - last_amp = amp >> 3; - saw_synth.offset( time, delta, output ); - } - else - { - time += osc.delay; - if ( time < end_time ) - { - int period = osc.period() * 2; - int phase = osc.phase; - - do - { - if ( --phase == 0 ) - { - phase = 7; - amp = 0; - } - - int delta = (amp >> 3) - last_amp; - if ( delta ) - { - last_amp = amp >> 3; - saw_synth.offset( time, delta, output ); - } - - time += period; - amp = (amp + amp_step) & 0xFF; - } - while ( time < end_time ); - - osc.phase = phase; - osc.amp = amp; - } - - osc.delay = time - end_time; - } - - osc.last_amp = last_amp; -} - diff --git a/lib/Nonlinear_Buffer.cpp b/lib/Nonlinear_Buffer.cpp deleted file mode 100644 index 447318e..0000000 --- a/lib/Nonlinear_Buffer.cpp +++ /dev/null @@ -1,189 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/ - -#include "Nonlinear_Buffer.h" - -#include "Nes_Apu.h" - -/* Library Copyright (C) 2003-2005 Shay Green. This library 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 library; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -// Nonlinear_Buffer - -Nonlinear_Buffer::Nonlinear_Buffer() : - Multi_Buffer( 1 ) -{ -} - -Nonlinear_Buffer::~Nonlinear_Buffer() -{ -} - -void Nonlinear_Buffer::enable_nonlinearity( Nes_Apu& apu, bool b ) -{ - if ( b ) - clear(); - nonlinearizer.enable( apu, b ); - for ( int i = 0; i < apu.osc_count; i++ ) - apu.osc_output( i, (i >= 2 ? &tnd : &buf) ); -} - -blargg_err_t Nonlinear_Buffer::sample_rate( long rate, int msec ) -{ - BLARGG_RETURN_ERR( buf.sample_rate( rate, msec ) ); - BLARGG_RETURN_ERR( tnd.sample_rate( rate, msec ) ); - return Multi_Buffer::sample_rate( buf.sample_rate(), buf.length() ); -} - -void Nonlinear_Buffer::clock_rate( long rate ) -{ - buf.clock_rate( rate ); - tnd.clock_rate( rate ); -} - -void Nonlinear_Buffer::bass_freq( int freq ) -{ - buf.bass_freq( freq ); - tnd.bass_freq( freq ); -} - -void Nonlinear_Buffer::clear() -{ - nonlinearizer.clear(); - buf.clear(); - tnd.clear(); -} - -Nonlinear_Buffer::channel_t Nonlinear_Buffer::channel( int i ) -{ - channel_t c; - c.center = &buf; - if ( 2 <= i && i <= 4 ) - c.center = &tnd; // only use for triangle, noise, and dmc - c.left = c.center; - c.right = c.center; - return c; -} - -void Nonlinear_Buffer::end_frame( blip_time_t length, bool ) -{ - buf.end_frame( length ); - tnd.end_frame( length ); -} - -long Nonlinear_Buffer::samples_avail() const -{ - return buf.samples_avail(); -} - -#include BLARGG_ENABLE_OPTIMIZER - -long Nonlinear_Buffer::read_samples( blip_sample_t* out, long count ) -{ - count = nonlinearizer.make_nonlinear( tnd, count ); - if ( count ) - { - Blip_Reader lin; - Blip_Reader nonlin; - - int lin_bass = lin.begin( buf ); - int nonlin_bass = nonlin.begin( tnd ); - - for ( int n = count; n--; ) - { - int s = lin.read() + nonlin.read(); - lin.next( lin_bass ); - nonlin.next( nonlin_bass ); - *out++ = s; - - if ( (BOOST::int16_t) s != s ) - out [-1] = 0x7FFF - (s >> 24); - } - - lin.end( buf ); - nonlin.end( tnd ); - - buf.remove_samples( count ); - tnd.remove_samples( count ); - } - - return count; -} - -// Nes_Nonlinearizer - -Nes_Nonlinearizer::Nes_Nonlinearizer() -{ - nonlinear = false; - - double gain = 0x7fff * 1.3; - // don't use entire range, so any overflow will stay within table - int const range = half * 0.75; // to do: must match that in Nes_Apu.cpp - for ( int i = 0; i < half * 2; i++ ) - { - int out = i << shift; - if ( i > half ) - { - int j = i - half; - if ( j >= range ) - j = range - 1; - double n = 202.0 / (range - 1) * j; - double d = 163.67 / (24329.0 / n + 100); - out = int (d * gain) + 0x8000; - assert( out < 0x10000 ); - } - table [i] = out; - } - clear(); -} - -void Nes_Nonlinearizer::enable( Nes_Apu& apu, bool b ) -{ - nonlinear = b; - if ( b ) - apu.enable_nonlinear( 1.0 ); - else - apu.volume( 1.0 ); -} - -long Nes_Nonlinearizer::make_nonlinear( Blip_Buffer& buf, long count ) -{ - long avail = buf.samples_avail(); - if ( count > avail ) - count = avail; - - if ( count && nonlinear ) - { - const int zero_offset = 0x7f7f; // to do: use private constant from Blip_Buffer.h - - #define ENTRY( s ) (table [((s) >> shift) & entry_mask]) - - BOOST::uint16_t* p = buf.buffer_; - unsigned prev = ENTRY( accum ); - long accum = this->accum; - - for ( unsigned n = count; n--; ) - { - accum += (long) *p - zero_offset; - check( (accum >> shift) < half * 2 ); - unsigned entry = ENTRY( accum ); - *p++ = entry - prev + zero_offset; - prev = entry; - } - - this->accum = accum; - } - - return count; -} - diff --git a/lib/Sound_Queue.cpp b/lib/Sound_Queue.cpp deleted file mode 100644 index 4577681..0000000 --- a/lib/Sound_Queue.cpp +++ /dev/null @@ -1,138 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/ - -#include "Sound_Queue.h" - -#include <assert.h> -#include <string.h> - -/* Copyright (C) 2005 by Shay Green. Permission is hereby granted, free of -charge, to any person obtaining a copy of this software module and associated -documentation files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and -to permit persons to whom the Software is furnished to do so, subject to the -following conditions: The above copyright notice and this permission notice -shall be included in all copies or substantial portions of the Software. THE -SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ - -// Return current SDL_GetError() string, or str if SDL didn't have a string -static const char* sdl_error( const char* str ) -{ - const char* sdl_str = SDL_GetError(); - if ( sdl_str && *sdl_str ) - str = sdl_str; - return str; -} - -Sound_Queue::Sound_Queue() -{ - bufs = NULL; - free_sem = NULL; - write_buf = 0; - write_pos = 0; - read_buf = 0; - sound_open = false; -} - -Sound_Queue::~Sound_Queue() -{ - if ( sound_open ) - { - SDL_PauseAudio( true ); - SDL_CloseAudio(); - } - - if ( free_sem ) - SDL_DestroySemaphore( free_sem ); - - delete [] bufs; -} - -int Sound_Queue::sample_count() const -{ - int buf_free = SDL_SemValue( free_sem ) * buf_size + (buf_size - write_pos); - return buf_size * buf_count - buf_free; -} - -const char* Sound_Queue::init( long sample_rate, int chan_count ) -{ - assert( !bufs ); // can only be initialized once - - bufs = new sample_t [(long) buf_size * buf_count]; - if ( !bufs ) - return "Out of memory"; - - free_sem = SDL_CreateSemaphore( buf_count - 1 ); - if ( !free_sem ) - return sdl_error( "Couldn't create semaphore" ); - - SDL_AudioSpec as; - as.freq = sample_rate; - as.format = AUDIO_S16SYS; - as.channels = chan_count; - as.silence = 0; - as.samples = buf_size; - as.size = 0; - as.callback = fill_buffer_; - as.userdata = this; - if ( SDL_OpenAudio( &as, NULL ) < 0 ) - return sdl_error( "Couldn't open SDL audio" ); - SDL_PauseAudio( false ); - sound_open = true; - - return NULL; -} - -inline Sound_Queue::sample_t* Sound_Queue::buf( int index ) -{ - assert( (unsigned) index < buf_count ); - return bufs + (long) index * buf_size; -} - -void Sound_Queue::write( const sample_t* in, int count ) -{ - while ( count ) - { - int n = buf_size - write_pos; - if ( n > count ) - n = count; - - memcpy( buf( write_buf ) + write_pos, in, n * sizeof (sample_t) ); - in += n; - write_pos += n; - count -= n; - - if ( write_pos >= buf_size ) - { - write_pos = 0; - write_buf = (write_buf + 1) % buf_count; - SDL_SemWait( free_sem ); - } - } -} - -void Sound_Queue::fill_buffer( Uint8* out, int count ) -{ - if ( SDL_SemValue( free_sem ) < buf_count - 1 ) - { - memcpy( out, buf( read_buf ), count ); - read_buf = (read_buf + 1) % buf_count; - SDL_SemPost( free_sem ); - } - else - { - memset( out, 0, count ); - } -} - -void Sound_Queue::fill_buffer_( void* user_data, Uint8* out, int count ) -{ - ((Sound_Queue*) user_data)->fill_buffer( out, count ); -} - diff --git a/lib/apu_snapshot.cpp b/lib/apu_snapshot.cpp deleted file mode 100644 index bfb277c..0000000 --- a/lib/apu_snapshot.cpp +++ /dev/null @@ -1,124 +0,0 @@ - -// Nes_Snd_Emu 0.1.7. http://www.slack.net/~ant/libs/ - -#include "apu_snapshot.h" -#include "Nes_Apu.h" - -/* Copyright (C) 2003-2005 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for -more details. You should have received a copy of the GNU Lesser General -Public License along with this module; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -#include BLARGG_SOURCE_BEGIN - -template<int mode> -struct apu_reflection -{ - #define REFLECT( apu, state ) (mode ? void (apu = state) : void (state = apu)) - - static void reflect_env( apu_snapshot_t::env_t& state, Nes_Envelope& osc ) - { - REFLECT( state [0], osc.env_delay ); - REFLECT( state [1], osc.envelope ); - REFLECT( state [2], osc.reg_written [3] ); - } - - static void reflect_square( apu_snapshot_t::square_t& state, Nes_Square& osc ) - { - reflect_env( state.env, osc ); - REFLECT( state.delay, osc.delay ); - REFLECT( state.length, osc.length_counter ); - REFLECT( state.phase, osc.phase ); - REFLECT( state.swp_delay, osc.sweep_delay ); - REFLECT( state.swp_reset, osc.reg_written [1] ); - } - - static void reflect_triangle( apu_snapshot_t::triangle_t& state, Nes_Triangle& osc ) - { - REFLECT( state.delay, osc.delay ); - REFLECT( state.length, osc.length_counter ); - REFLECT( state.linear_counter, osc.linear_counter ); - REFLECT( state.linear_mode, osc.reg_written [3] ); - } - - static void reflect_noise( apu_snapshot_t::noise_t& state, Nes_Noise& osc ) - { - reflect_env( state.env, osc ); - REFLECT( state.delay, osc.delay ); - REFLECT( state.length, osc.length_counter ); - REFLECT( state.shift_reg, osc.noise ); - } - - static void reflect_dmc( apu_snapshot_t::dmc_t& state, Nes_Dmc& osc ) - { - REFLECT( state.delay, osc.delay ); - REFLECT( state.remain, osc.length_counter ); - REFLECT( state.buf, osc.buf ); - REFLECT( state.bits_remain, osc.bits_remain ); - REFLECT( state.bits, osc.bits ); - REFLECT( state.buf_empty, osc.buf_empty ); - REFLECT( state.silence, osc.silence ); - REFLECT( state.irq_flag, osc.irq_flag ); - if ( mode ) - state.addr = osc.address | 0x8000; - else - osc.address = state.addr & 0x7fff; - } -}; - -void Nes_Apu::save_snapshot( apu_snapshot_t* state ) const -{ - for ( int i = 0; i < osc_count * 4; i++ ) - state->w40xx [i] = oscs [i >> 2]->regs [i & 3]; - state->w40xx [0x11] = dmc.dac; - - state->w4015 = osc_enables; - state->w4017 = frame_mode; - state->delay = frame_delay; - state->step = frame; - state->irq_flag = irq_flag; - - typedef apu_reflection<1> refl; - Nes_Apu& apu = *(Nes_Apu*) this; // const_cast - refl::reflect_square ( state->square1, apu.square1 ); - refl::reflect_square ( state->square2, apu.square2 ); - refl::reflect_triangle( state->triangle, apu.triangle ); - refl::reflect_noise ( state->noise, apu.noise ); - refl::reflect_dmc ( state->dmc, apu.dmc ); -} - -void Nes_Apu::load_snapshot( apu_snapshot_t const& state ) -{ - reset(); - - write_register( 0, 0x4017, state.w4017 ); - write_register( 0, 0x4015, state.w4015 ); - - for ( int i = 0; i < osc_count * 4; i++ ) - { - int n = state.w40xx [i]; - oscs [i >> 2]->regs [i & 3] = n; - write_register( 0, 0x4000 + i, n ); - } - - frame_delay = state.delay; - frame = state.step; - irq_flag = state.irq_flag; - - typedef apu_reflection<0> refl; - apu_snapshot_t& st = (apu_snapshot_t&) state; // const_cast - refl::reflect_square ( st.square1, square1 ); - refl::reflect_square ( st.square2, square2 ); - refl::reflect_triangle( st.triangle, triangle ); - refl::reflect_noise ( st.noise, noise ); - refl::reflect_dmc ( st.dmc, dmc ); - dmc.recalc_irq(); - dmc.last_amp = dmc.dac; -} - diff --git a/lib/include/Blip_Buffer.h b/lib/include/Blip_Buffer.h deleted file mode 100644 index 1982268..0000000 --- a/lib/include/Blip_Buffer.h +++ /dev/null @@ -1,253 +0,0 @@ - -// Buffer of sound samples into which band-limited waveforms can be synthesized -// using Blip_Wave or Blip_Synth. - -// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef BLIP_BUFFER_H -#define BLIP_BUFFER_H - -#include "blargg_common.h" - -class Blip_Reader; - -// Source time unit. -typedef long blip_time_t; - -// Type of sample produced. Signed 16-bit format. -typedef BOOST::int16_t blip_sample_t; - -// Make buffer as large as possible (currently about 65000 samples) -const int blip_default_length = 0; - -class Blip_Buffer { -public: - // Construct an empty buffer. - Blip_Buffer(); - ~Blip_Buffer(); - - // Set output sample rate and buffer length in milliseconds (1/1000 sec), - // then clear buffer. If length is not specified, make as large as possible. - // If there is insufficient memory for the buffer, sets the buffer length - // to 0 and returns error string (or propagates exception if compiler supports it). - blargg_err_t sample_rate( long samples_per_sec, int msec_length = blip_default_length ); - // to do: rename to set_sample_rate - - // Length of buffer, in milliseconds - int length() const; - - // Current output sample rate - long sample_rate() const; - - // Number of source time units per second - void clock_rate( long ); - long clock_rate() const; - - // Set frequency at which high-pass filter attenuation passes -3dB - void bass_freq( int frequency ); - - // Remove all available samples and clear buffer to silence. If 'entire_buffer' is - // false, just clear out any samples waiting rather than the entire buffer. - void clear( bool entire_buffer = true ); - - // to do: - // Notify Blip_Buffer that synthesis has been performed until specified time - //void run_until( blip_time_t ); - - // End current time frame of specified duration and make its samples available - // (along with any still-unread samples) for reading with read_samples(). Begin - // a new time frame at the end of the current frame. All transitions must have - // been added before 'time'. - void end_frame( blip_time_t time ); - - // Number of samples available for reading with read_samples() - long samples_avail() const; - - // Read at most 'max_samples' out of buffer into 'dest', removing them from from - // the buffer. Return number of samples actually read and removed. If stereo is - // true, increment 'dest' one extra time after writing each sample, to allow - // easy interleving of two channels into a stereo output buffer. - long read_samples( blip_sample_t* dest, long max_samples, bool stereo = false ); - - // Remove 'count' samples from those waiting to be read - void remove_samples( long count ); - - // Number of samples delay from synthesis to samples read out - int output_latency() const; - - - // Experimental external buffer mixing support - - // Number of raw samples that can be mixed within frame of specified duration - long count_samples( blip_time_t duration ) const; - - // Mix 'count' samples from 'buf' into buffer. - void mix_samples( const blip_sample_t* buf, long count ); - - - // not documented yet - - void remove_silence( long count ); - - typedef unsigned long resampled_time_t; - - resampled_time_t resampled_time( blip_time_t t ) const { - return t * resampled_time_t (factor_) + offset_; - } - - resampled_time_t resampled_duration( int t ) const { - return t * resampled_time_t (factor_); - } - -private: - // noncopyable - Blip_Buffer( const Blip_Buffer& ); - Blip_Buffer& operator = ( const Blip_Buffer& ); - - // Don't use the following members. They are public only for technical reasons. - public: - enum { widest_impulse_ = 24 }; - typedef BOOST::uint16_t buf_t_; - - unsigned long factor_; - resampled_time_t offset_; - buf_t_* buffer_; - unsigned buffer_size_; - private: - long reader_accum; - int bass_shift; - long samples_per_sec; - long clocks_per_sec; - int bass_freq_; - int length_; - - enum { accum_fract = 15 }; // less than 16 to give extra sample range - enum { sample_offset = 0x7F7F }; // repeated byte allows memset to clear buffer - - friend class Blip_Reader; -}; - -// Low-pass equalization parameters (see notes.txt) -class blip_eq_t { -public: - blip_eq_t( double treble = 0 ); - blip_eq_t( double treble, long cutoff, long sample_rate ); -private: - double treble; - long cutoff; - long sample_rate; - friend class Blip_Impulse_; -}; - -// not documented yet (see Multi_Buffer.cpp for an example of use) -class Blip_Reader { - const Blip_Buffer::buf_t_* buf; - long accum; - #ifdef __MWERKS__ - void operator = ( struct foobar ); // helps optimizer - #endif -public: - // avoid anything which might cause optimizer to put object in memory - - int begin( Blip_Buffer& blip_buf ) { - buf = blip_buf.buffer_; - accum = blip_buf.reader_accum; - return blip_buf.bass_shift; - } - - int read() const { - return accum >> Blip_Buffer::accum_fract; - } - - void next( int bass_shift = 9 ) { - accum -= accum >> bass_shift; - accum += ((long) *buf++ - Blip_Buffer::sample_offset) << Blip_Buffer::accum_fract; - } - - void end( Blip_Buffer& blip_buf ) { - blip_buf.reader_accum = accum; - } -}; - - - -// End of public interface - -#ifndef BLIP_BUFFER_ACCURACY - #define BLIP_BUFFER_ACCURACY 16 -#endif - -const int blip_res_bits_ = 5; - -typedef BOOST::uint32_t blip_pair_t_; - -class Blip_Impulse_ { - typedef BOOST::uint16_t imp_t; - - blip_eq_t eq; - double volume_unit_; - imp_t* impulses; - imp_t* impulse; - int width; - int fine_bits; - int res; - bool generate; - - void fine_volume_unit(); - void scale_impulse( int unit, imp_t* ) const; -public: - Blip_Buffer* buf; - BOOST::uint32_t offset; - - void init( blip_pair_t_* impulses, int width, int res, int fine_bits = 0 ); - void volume_unit( double ); - void treble_eq( const blip_eq_t& ); -}; - -inline blip_eq_t::blip_eq_t( double t ) : - treble( t ), cutoff( 0 ), sample_rate( 44100 ) { -} - -inline blip_eq_t::blip_eq_t( double t, long c, long sr ) : - treble( t ), cutoff( c ), sample_rate( sr ) { -} - -inline int Blip_Buffer::length() const { - return length_; -} - -inline long Blip_Buffer::samples_avail() const { - return long (offset_ >> BLIP_BUFFER_ACCURACY); -} - -inline long Blip_Buffer::sample_rate() const { - return samples_per_sec; -} - -inline void Blip_Buffer::end_frame( blip_time_t t ) { - offset_ += t * factor_; - assert(( "Blip_Buffer::end_frame(): Frame went past end of buffer", - samples_avail() <= (long) buffer_size_ )); -} - -inline void Blip_Buffer::remove_silence( long count ) { - assert(( "Blip_Buffer::remove_silence(): Tried to remove more samples than available", - count <= samples_avail() )); - offset_ -= resampled_time_t (count) << BLIP_BUFFER_ACCURACY; -} - -inline int Blip_Buffer::output_latency() const { - return widest_impulse_ / 2; -} - -inline long Blip_Buffer::clock_rate() const { - return clocks_per_sec; -} - -// MSVC6 fix -typedef Blip_Buffer::resampled_time_t blip_resampled_time_t; - -#include "Blip_Synth.h" - -#endif - diff --git a/lib/include/Blip_Synth.h b/lib/include/Blip_Synth.h deleted file mode 100644 index b166574..0000000 --- a/lib/include/Blip_Synth.h +++ /dev/null @@ -1,203 +0,0 @@ - -// Blip_Synth and Blip_Wave are waveform transition synthesizers for adding -// waveforms to a Blip_Buffer. - -// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef BLIP_SYNTH_H -#define BLIP_SYNTH_H - -#ifndef BLIP_BUFFER_H - #include "Blip_Buffer.h" -#endif - -// Quality level. Higher levels are slower, and worse in a few cases. -// Use blip_good_quality as a starting point. -const int blip_low_quality = 1; -const int blip_med_quality = 2; -const int blip_good_quality = 3; -const int blip_high_quality = 4; - -// Blip_Synth is a transition waveform synthesizer which adds band-limited -// offsets (transitions) into a Blip_Buffer. For a simpler interface, use -// Blip_Wave (below). -// -// Range specifies the greatest expected offset that will occur. For a -// waveform that goes between +amp and -amp, range should be amp * 2 (half -// that if it only goes between +amp and 0). When range is large, a higher -// accuracy scheme is used; to force this even when range is small, pass -// the negative of range (i.e. -range). -template<int quality,int range> -class Blip_Synth { - BOOST_STATIC_ASSERT( 1 <= quality && quality <= 5 ); - BOOST_STATIC_ASSERT( -32768 <= range && range <= 32767 ); - enum { - abs_range = (range < 0) ? -range : range, - fine_mode = (range > 512 || range < 0), - width = (quality < 5 ? quality * 4 : Blip_Buffer::widest_impulse_), - res = 1 << blip_res_bits_, - impulse_size = width / 2 * (fine_mode + 1), - base_impulses_size = width / 2 * (res / 2 + 1), - fine_bits = (fine_mode ? (abs_range <= 64 ? 2 : abs_range <= 128 ? 3 : - abs_range <= 256 ? 4 : abs_range <= 512 ? 5 : abs_range <= 1024 ? 6 : - abs_range <= 2048 ? 7 : 8) : 0) - }; - blip_pair_t_ impulses [impulse_size * res * 2 + base_impulses_size]; - Blip_Impulse_ impulse; -public: - Blip_Synth() { impulse.init( impulses, width, res, fine_bits ); } - - // Configure low-pass filter (see notes.txt). Not optimized for real-time control - void treble_eq( const blip_eq_t& eq ) { impulse.treble_eq( eq ); } - - // Set volume of a transition at amplitude 'range' by setting volume_unit - // to v / range - void volume( double v ) { impulse.volume_unit( v * (1.0 / abs_range) ); } - - // Set base volume unit of transitions, where 1.0 is a full swing between the - // positive and negative extremes. Not optimized for real-time control. - void volume_unit( double unit ) { impulse.volume_unit( unit ); } - - // Default Blip_Buffer used for output when none is specified for a given call - Blip_Buffer* output() const { return impulse.buf; } - void output( Blip_Buffer* b ) { impulse.buf = b; } - - // Add an amplitude offset (transition) with a magnitude of delta * volume_unit - // into the specified buffer (default buffer if none specified) at the - // specified source time. Delta can be positive or negative. To increase - // performance by inlining code at the call site, use offset_inline(). - void offset( blip_time_t, int delta, Blip_Buffer* ) const; - - void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; - void offset_resampled( blip_resampled_time_t t, int o ) const { - offset_resampled( t, o, impulse.buf ); - } - void offset( blip_time_t t, int delta ) const { - offset( t, delta, impulse.buf ); - } - void offset_inline( blip_time_t time, int delta, Blip_Buffer* buf ) const { - offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); - } - void offset_inline( blip_time_t time, int delta ) const { - offset_inline( time, delta, impulse.buf ); - } -}; - -// Blip_Wave is a synthesizer for adding a *single* waveform to a Blip_Buffer. -// A wave is built from a series of delays and new amplitudes. This provides a -// simpler interface than Blip_Synth, nothing more. -template<int quality,int range> -class Blip_Wave { - Blip_Synth<quality,range> synth; - blip_time_t time_; - int last_amp; -public: - // Start wave at time 0 and amplitude 0 - Blip_Wave() : time_( 0 ), last_amp( 0 ) { } - - // See Blip_Synth for description - void volume( double v ) { synth.volume( v ); } - void volume_unit( double v ) { synth.volume_unit( v ); } - void treble_eq( const blip_eq_t& eq){ synth.treble_eq( eq ); } - Blip_Buffer* output() const { return synth.output(); } - void output( Blip_Buffer* b ) { synth.output( b ); if ( !b ) time_ = last_amp = 0; } - - // Current time in frame - blip_time_t time() const { return time_; } - void time( blip_time_t t ) { time_ = t; } - - // Current amplitude of wave - int amplitude() const { return last_amp; } - void amplitude( int ); - - // Move forward by 't' time units - void delay( blip_time_t t ) { time_ += t; } - - // End time frame of specified duration. Localize time to new frame. - void end_frame( blip_time_t duration ) { - assert(( "Blip_Wave::end_frame(): Wave hadn't yet been run for entire frame", - duration <= time_ )); - time_ -= duration; - } -}; - - -// End of public interface - -template<int quality,int range> -void Blip_Wave<quality,range>::amplitude( int amp ) { - int delta = amp - last_amp; - last_amp = amp; - synth.offset_inline( time_, delta ); -} - -template<int quality,int range> -inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time, - int delta, Blip_Buffer* blip_buf ) const -{ - typedef blip_pair_t_ pair_t; - - unsigned sample_index = (time >> BLIP_BUFFER_ACCURACY) & ~1; - assert(( "Blip_Synth/Blip_wave: Went past end of buffer", - sample_index < blip_buf->buffer_size_ )); - enum { const_offset = Blip_Buffer::widest_impulse_ / 2 - width / 2 }; - pair_t* buf = (pair_t*) &blip_buf->buffer_ [const_offset + sample_index]; - - enum { shift = BLIP_BUFFER_ACCURACY - blip_res_bits_ }; - enum { mask = res * 2 - 1 }; - const pair_t* imp = &impulses [((time >> shift) & mask) * impulse_size]; - - pair_t offset = impulse.offset * delta; - - if ( !fine_bits ) - { - // normal mode - for ( int n = width / 4; n; --n ) - { - pair_t t0 = buf [0] - offset; - pair_t t1 = buf [1] - offset; - - t0 += imp [0] * delta; - t1 += imp [1] * delta; - imp += 2; - - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - } - else - { - // fine mode - enum { sub_range = 1 << fine_bits }; - delta += sub_range / 2; - int delta2 = (delta & (sub_range - 1)) - sub_range / 2; - delta >>= fine_bits; - - for ( int n = width / 4; n; --n ) - { - pair_t t0 = buf [0] - offset; - pair_t t1 = buf [1] - offset; - - t0 += imp [0] * delta2; - t0 += imp [1] * delta; - - t1 += imp [2] * delta2; - t1 += imp [3] * delta; - - imp += 4; - - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - } -} - -template<int quality,int range> -void Blip_Synth<quality,range>::offset( blip_time_t time, int delta, Blip_Buffer* buf ) const { - offset_resampled( time * buf->factor_ + buf->offset_, delta, buf ); -} - -#endif - diff --git a/lib/include/Multi_Buffer.h b/lib/include/Multi_Buffer.h deleted file mode 100644 index 633c7d2..0000000 --- a/lib/include/Multi_Buffer.h +++ /dev/null @@ -1,157 +0,0 @@ - -// Multi-channel sound buffer interface, and basic mono and stereo buffers - -// Blip_Buffer 0.3.3. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef MULTI_BUFFER_H -#define MULTI_BUFFER_H - -#include "Blip_Buffer.h" - -// Multi_Buffer is an interface to one or more Blip_Buffers mapped to one or -// more channels consisting of left, center, and right buffers. -class Multi_Buffer { -public: - Multi_Buffer( int samples_per_frame ); - virtual ~Multi_Buffer() { } - - // Set the number of channels available - virtual blargg_err_t set_channel_count( int ); - - // Get indexed channel, from 0 to channel count - 1 - struct channel_t { - Blip_Buffer* center; - Blip_Buffer* left; - Blip_Buffer* right; - }; - virtual channel_t channel( int index ) = 0; - - // See Blip_Buffer.h - // to do: rename to set_sample_rate - virtual blargg_err_t sample_rate( long rate, int msec = blip_default_length ) = 0; - virtual void clock_rate( long ) = 0; - virtual void bass_freq( int ) = 0; - virtual void clear() = 0; - long sample_rate() const; - - // Length of buffer, in milliseconds - int length() const; - - // See Blip_Buffer.h. For optimal operation, pass false for 'added_stereo' - // if nothing was added to the left and right buffers of any channel for - // this time frame. - virtual void end_frame( blip_time_t, bool added_stereo = true ) = 0; - - // Number of samples per output frame (1 = mono, 2 = stereo) - int samples_per_frame() const; - - // See Blip_Buffer.h - virtual long read_samples( blip_sample_t*, long ) = 0; - virtual long samples_avail() const = 0; - -private: - // noncopyable - Multi_Buffer( const Multi_Buffer& ); - Multi_Buffer& operator = ( const Multi_Buffer& ); - - long sample_rate_; - int length_; - int const samples_per_frame_; -}; - -// Mono_Buffer uses a single buffer and outputs mono samples. -class Mono_Buffer : public Multi_Buffer { - Blip_Buffer buf; -public: - Mono_Buffer(); - ~Mono_Buffer(); - - // Buffer used for all channels - Blip_Buffer* center(); - - // See Multi_Buffer - blargg_err_t sample_rate( long rate, int msec = blip_default_length ); - using Multi_Buffer::sample_rate; - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int ); - void end_frame( blip_time_t, bool unused = true ); - long samples_avail() const; - long read_samples( blip_sample_t*, long ); -}; - -// Stereo_Buffer uses three buffers (one for center) and outputs stereo sample pairs. -class Stereo_Buffer : public Multi_Buffer { -public: - Stereo_Buffer(); - ~Stereo_Buffer(); - - // Buffers used for all channels - Blip_Buffer* center(); - Blip_Buffer* left(); - Blip_Buffer* right(); - - // See Multi_Buffer - blargg_err_t sample_rate( long, int msec = blip_default_length ); - using Multi_Buffer::sample_rate; - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int index ); - void end_frame( blip_time_t, bool added_stereo = true ); - - long samples_avail() const; - long read_samples( blip_sample_t*, long ); - -private: - enum { buf_count = 3 }; - Blip_Buffer bufs [buf_count]; - channel_t chan; - bool stereo_added; - bool was_stereo; - - void mix_stereo( blip_sample_t*, long ); - void mix_mono( blip_sample_t*, long ); -}; - - -// End of public interface - -inline blargg_err_t Multi_Buffer::sample_rate( long rate, int msec ) -{ - sample_rate_ = rate; - length_ = msec; - return blargg_success; -} - -inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } - -inline Blip_Buffer* Stereo_Buffer::left() { return &bufs [1]; } - -inline Blip_Buffer* Stereo_Buffer::center() { return &bufs [0]; } - -inline Blip_Buffer* Stereo_Buffer::right() { return &bufs [2]; } - -inline long Stereo_Buffer::samples_avail() const { return bufs [0].samples_avail() * 2; } - -inline Stereo_Buffer::channel_t Stereo_Buffer::channel( int index ) { return chan; } - -inline long Multi_Buffer::sample_rate() const { return sample_rate_; } - -inline int Multi_Buffer::length() const { return length_; } - -inline Blip_Buffer* Mono_Buffer::center() { return &buf; } - -inline void Mono_Buffer::clock_rate( long rate ) { buf.clock_rate( rate ); } - -inline void Mono_Buffer::clear() { buf.clear(); } - -inline void Mono_Buffer::bass_freq( int freq ) { buf.bass_freq( freq ); } - -inline long Mono_Buffer::read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } - -inline long Mono_Buffer::samples_avail() const { return buf.samples_avail(); } - -#endif - diff --git a/lib/include/Nes_Apu.h b/lib/include/Nes_Apu.h deleted file mode 100644 index 6ce4c98..0000000 --- a/lib/include/Nes_Apu.h +++ /dev/null @@ -1,162 +0,0 @@ - -// NES 2A03 APU sound chip emulator - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef NES_APU_H -#define NES_APU_H - -typedef long cpu_time_t; // CPU clock cycle count -typedef unsigned cpu_addr_t; // 16-bit memory address - -#include "Nes_Oscs.h" - -struct apu_snapshot_t; -class Nonlinear_Buffer; - -class Nes_Apu { -public: - Nes_Apu(); - ~Nes_Apu(); - - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); - - // Set memory reader callback used by DMC oscillator to fetch samples. - // When callback is invoked, 'user_data' is passed unchanged as the - // first parameter. - void dmc_reader( int (*callback)( void* user_data, cpu_addr_t ), void* user_data = NULL ); - - // All time values are the number of CPU clock cycles relative to the - // beginning of the current time frame. Before resetting the CPU clock - // count, call end_frame( last_cpu_time ). - - // Write to register (0x4000-0x4017, except 0x4014 and 0x4016) - enum { start_addr = 0x4000 }; - enum { end_addr = 0x4017 }; - void write_register( cpu_time_t, cpu_addr_t, int data ); - - // Read from status register at 0x4015 - enum { status_addr = 0x4015 }; - int read_status( cpu_time_t ); - - // Run all oscillators up to specified time, end current time frame, then - // start a new time frame at time 0. Time frames have no effect on emulation - // and each can be whatever length is convenient. - void end_frame( cpu_time_t ); - -// Additional optional features (can be ignored without any problem) - - // Reset internal frame counter, registers, and all oscillators. - // Use PAL timing if pal_timing is true, otherwise use NTSC timing. - // Set the DMC oscillator's initial DAC value to initial_dmc_dac without - // any audible click. - void reset( bool pal_timing = false, int initial_dmc_dac = 0 ); - - // Save/load snapshot of exact emulation state - void save_snapshot( apu_snapshot_t* out ) const; - void load_snapshot( apu_snapshot_t const& ); - - // Set overall volume (default is 1.0) - void volume( double ); - - // Reset oscillator amplitudes. Must be called when clearing buffer while - // using non-linear sound. - void buffer_cleared(); - - // Set treble equalization (see notes.txt). - void treble_eq( const blip_eq_t& ); - - // Set sound output of specific oscillator to buffer. If buffer is NULL, - // the specified oscillator is muted and emulation accuracy is reduced. - // The oscillators are indexed as follows: 0) Square 1, 1) Square 2, - // 2) Triangle, 3) Noise, 4) DMC. - enum { osc_count = 5 }; - void osc_output( int index, Blip_Buffer* buffer ); - - // Set IRQ time callback that is invoked when the time of earliest IRQ - // may have changed, or NULL to disable. When callback is invoked, - // 'user_data' is passed unchanged as the first parameter. - void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); - - // Get time that APU-generated IRQ will occur if no further register reads - // or writes occur. If IRQ is already pending, returns irq_waiting. If no - // IRQ will occur, returns no_irq. - enum { no_irq = LONG_MAX / 2 + 1 }; - enum { irq_waiting = 0 }; - cpu_time_t earliest_irq() const; - - // Count number of DMC reads that would occur if 'run_until( t )' were executed. - // If last_read is not NULL, set *last_read to the earliest time that - // 'count_dmc_reads( time )' would result in the same result. - int count_dmc_reads( cpu_time_t t, cpu_time_t* last_read = NULL ) const; - - // Run APU until specified time, so that any DMC memory reads can be - // accounted for (i.e. inserting CPU wait states). - void run_until( cpu_time_t ); - -// End of public interface. -private: - friend class Nes_Nonlinearizer; - void enable_nonlinear( double volume ); -private: - // noncopyable - Nes_Apu( const Nes_Apu& ); - Nes_Apu& operator = ( const Nes_Apu& ); - - Nes_Osc* oscs [osc_count]; - Nes_Square square1; - Nes_Square square2; - Nes_Noise noise; - Nes_Triangle triangle; - Nes_Dmc dmc; - - cpu_time_t last_time; // has been run until this time in current frame - cpu_time_t earliest_irq_; - cpu_time_t next_irq; - int frame_period; - int frame_delay; // cycles until frame counter runs next - int frame; // current frame (0-3) - int osc_enables; - int frame_mode; - bool irq_flag; - void (*irq_notifier_)( void* user_data ); - void* irq_data; - Nes_Square::Synth square_synth; // shared by squares - - void irq_changed(); - void state_restored(); - - friend struct Nes_Dmc; -}; - -inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf ) -{ - assert(( "Nes_Apu::osc_output(): Index out of range", 0 <= osc && osc < osc_count )); - oscs [osc]->output = buf; -} - -inline cpu_time_t Nes_Apu::earliest_irq() const -{ - return earliest_irq_; -} - -inline void Nes_Apu::dmc_reader( int (*func)( void*, cpu_addr_t ), void* user_data ) -{ - dmc.rom_reader_data = user_data; - dmc.rom_reader = func; -} - -inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data ) -{ - irq_notifier_ = func; - irq_data = user_data; -} - -inline int Nes_Apu::count_dmc_reads( cpu_time_t time, cpu_time_t* last_read ) const -{ - return dmc.count_reads( time, last_read ); -} - -#endif - diff --git a/lib/include/Nes_Namco.h b/lib/include/Nes_Namco.h deleted file mode 100644 index 87d6585..0000000 --- a/lib/include/Nes_Namco.h +++ /dev/null @@ -1,86 +0,0 @@ - -// Namco 106 sound chip emulator - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef NES_NAMCO_H -#define NES_NAMCO_H - -#include "Nes_Apu.h" - -struct namco_snapshot_t; - -class Nes_Namco { -public: - Nes_Namco(); - ~Nes_Namco(); - - // See Nes_Apu.h for reference. - void volume( double ); - void treble_eq( const blip_eq_t& ); - void output( Blip_Buffer* ); - enum { osc_count = 8 }; - void osc_output( int index, Blip_Buffer* ); - void reset(); - void end_frame( cpu_time_t ); - - // Read/write data register is at 0x4800 - enum { data_reg_addr = 0x4800 }; - void write_data( cpu_time_t, int ); - int read_data(); - - // Write-only address register is at 0xF800 - enum { addr_reg_addr = 0xF800 }; - void write_addr( int ); - - // to do: implement save/restore - void save_snapshot( namco_snapshot_t* out ); - void load_snapshot( namco_snapshot_t const& ); - -private: - // noncopyable - Nes_Namco( const Nes_Namco& ); - Nes_Namco& operator = ( const Nes_Namco& ); - - struct Namco_Osc { - long delay; - Blip_Buffer* output; - short last_amp; - short wave_pos; - }; - - Namco_Osc oscs [osc_count]; - - cpu_time_t last_time; - int addr_reg; - - enum { reg_count = 0x80 }; - BOOST::uint8_t reg [reg_count]; - Blip_Synth<blip_good_quality,15> synth; - - BOOST::uint8_t& access(); - void run_until( cpu_time_t ); -}; - -inline void Nes_Namco::volume( double v ) { synth.volume( 0.10 / osc_count * v ); } - -inline void Nes_Namco::treble_eq( const blip_eq_t& eq ) { synth.treble_eq( eq ); } - -inline void Nes_Namco::write_addr( int v ) { addr_reg = v; } - -inline int Nes_Namco::read_data() { return access(); } - -inline void Nes_Namco::osc_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -inline void Nes_Namco::write_data( cpu_time_t time, int data ) -{ - run_until( time ); - access() = data; -} - -#endif - diff --git a/lib/include/Nes_Oscs.h b/lib/include/Nes_Oscs.h deleted file mode 100644 index c189304..0000000 --- a/lib/include/Nes_Oscs.h +++ /dev/null @@ -1,142 +0,0 @@ - -// Private oscillators used by Nes_Apu - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef NES_OSCS_H -#define NES_OSCS_H - -#include "Blip_Buffer.h" - -class Nes_Apu; - -struct Nes_Osc -{ - unsigned char regs [4]; - bool reg_written [4]; - Blip_Buffer* output; - int length_counter;// length counter (0 if unused by oscillator) - int delay; // delay until next (potential) transition - int last_amp; // last amplitude oscillator was outputting - - void clock_length( int halt_mask ); - int period() const { - return (regs [3] & 7) * 0x100 + (regs [2] & 0xff); - } - void reset() { - delay = 0; - last_amp = 0; - } - int update_amp( int amp ) { - int delta = amp - last_amp; - last_amp = amp; - return delta; - } -}; - -struct Nes_Envelope : Nes_Osc -{ - int envelope; - int env_delay; - - void clock_envelope(); - int volume() const; - void reset() { - envelope = 0; - env_delay = 0; - Nes_Osc::reset(); - } -}; - -// Nes_Square -struct Nes_Square : Nes_Envelope -{ - enum { negate_flag = 0x08 }; - enum { shift_mask = 0x07 }; - enum { phase_range = 8 }; - int phase; - int sweep_delay; - - typedef Blip_Synth<blip_good_quality,15> Synth; - const Synth* synth; // shared between squares - - void clock_sweep( int adjust ); - void run( cpu_time_t, cpu_time_t ); - void reset() { - sweep_delay = 0; - Nes_Envelope::reset(); - } -}; - -// Nes_Triangle -struct Nes_Triangle : Nes_Osc -{ - enum { phase_range = 16 }; - int phase; - int linear_counter; - Blip_Synth<blip_good_quality,15> synth; - - int calc_amp() const; - void run( cpu_time_t, cpu_time_t ); - void clock_linear_counter(); - void reset() { - linear_counter = 0; - phase = phase_range; - Nes_Osc::reset(); - } -}; - -// Nes_Noise -struct Nes_Noise : Nes_Envelope -{ - int noise; - Blip_Synth<blip_med_quality,15> synth; - - void run( cpu_time_t, cpu_time_t ); - void reset() { - noise = 1 << 14; - Nes_Envelope::reset(); - } -}; - -// Nes_Dmc -struct Nes_Dmc : Nes_Osc -{ - int address; // address of next byte to read - int period; - //int length_counter; // bytes remaining to play (already defined in Nes_Osc) - int buf; - int bits_remain; - int bits; - bool buf_empty; - bool silence; - - enum { loop_flag = 0x40 }; - - int dac; - - cpu_time_t next_irq; - bool irq_enabled; - bool irq_flag; - bool pal_mode; - bool nonlinear; - - int (*rom_reader)( void*, cpu_addr_t ); // needs to be initialized to rom read function - void* rom_reader_data; - - Nes_Apu* apu; - - Blip_Synth<blip_med_quality,127> synth; - - void start(); - void write_register( int, int ); - void run( cpu_time_t, cpu_time_t ); - void recalc_irq(); - void fill_buffer(); - void reload_sample(); - void reset(); - int count_reads( cpu_time_t, cpu_time_t* ) const; -}; - -#endif - diff --git a/lib/include/Nes_Vrc6.h b/lib/include/Nes_Vrc6.h deleted file mode 100644 index 038cd58..0000000 --- a/lib/include/Nes_Vrc6.h +++ /dev/null @@ -1,85 +0,0 @@ - -// Konami VRC6 sound chip emulator - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef NES_VRC6_H -#define NES_VRC6_H - -#include "Nes_Apu.h" - -struct vrc6_snapshot_t; - -class Nes_Vrc6 { -public: - Nes_Vrc6(); - ~Nes_Vrc6(); - - // See Nes_Apu.h for reference - void reset(); - void volume( double ); - void treble_eq( blip_eq_t const& ); - void output( Blip_Buffer* ); - enum { osc_count = 3 }; - void osc_output( int index, Blip_Buffer* ); - void end_frame( cpu_time_t ); - void save_snapshot( vrc6_snapshot_t* ) const; - void load_snapshot( vrc6_snapshot_t const& ); - - // Oscillator 0 write-only registers are at $9000-$9002 - // Oscillator 1 write-only registers are at $A000-$A002 - // Oscillator 2 write-only registers are at $B000-$B002 - enum { reg_count = 3 }; - enum { base_addr = 0x9000 }; - enum { addr_step = 0x1000 }; - void write_osc( cpu_time_t, int osc, int reg, int data ); - -private: - // noncopyable - Nes_Vrc6( const Nes_Vrc6& ); - Nes_Vrc6& operator = ( const Nes_Vrc6& ); - - struct Vrc6_Osc - { - BOOST::uint8_t regs [3]; - Blip_Buffer* output; - int delay; - int last_amp; - int phase; - int amp; // only used by saw - - int period() const - { - return (regs [2] & 0x0f) * 0x100L + regs [1] + 1; - } - }; - - Vrc6_Osc oscs [osc_count]; - cpu_time_t last_time; - - Blip_Synth<blip_med_quality,31> saw_synth; - Blip_Synth<blip_good_quality,15> square_synth; - - void run_until( cpu_time_t ); - void run_square( Vrc6_Osc& osc, cpu_time_t ); - void run_saw( cpu_time_t ); -}; - -struct vrc6_snapshot_t -{ - BOOST::uint8_t regs [3] [3]; - BOOST::uint8_t saw_amp; - BOOST::uint16_t delays [3]; - BOOST::uint8_t phases [3]; - BOOST::uint8_t unused; -}; -BOOST_STATIC_ASSERT( sizeof (vrc6_snapshot_t) == 20 ); - -inline void Nes_Vrc6::osc_output( int i, Blip_Buffer* buf ) -{ - assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -#endif - diff --git a/lib/include/Nonlinear_Buffer.h b/lib/include/Nonlinear_Buffer.h deleted file mode 100644 index 9497d2a..0000000 --- a/lib/include/Nonlinear_Buffer.h +++ /dev/null @@ -1,65 +0,0 @@ - -// NES non-linear audio output handling. - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef NONLINEAR_BUFFER_H -#define NONLINEAR_BUFFER_H - -#include "Multi_Buffer.h" -class Nes_Apu; - -// Use to make samples non-linear in Blip_Buffer used for triangle, noise, and DMC only -class Nes_Nonlinearizer { -public: - Nes_Nonlinearizer(); - - // Must be called when buffer is cleared - void clear() { accum = 0x8000; } - - // Enable/disable non-linear output - void enable( Nes_Apu&, bool = true ); - - // Make at most 'count' samples in buffer non-linear and return number - // of samples modified. This many samples must then be read out of the buffer. - long make_nonlinear( Blip_Buffer&, long count ); - -private: - enum { shift = 5 }; - enum { half = 0x8000 >> shift }; - enum { entry_mask = half * 2 - 1 }; - BOOST::uint16_t table [half * 2]; - long accum; - bool nonlinear; -}; - -class Nonlinear_Buffer : public Multi_Buffer { -public: - Nonlinear_Buffer(); - ~Nonlinear_Buffer(); - - // Enable/disable non-linear output - void enable_nonlinearity( Nes_Apu&, bool = true ); - - // Blip_Buffer to output other sound chips to - Blip_Buffer* buffer() { return &buf; } - - // See Multi_Buffer.h - blargg_err_t sample_rate( long rate, int msec = blip_default_length ); - using Multi_Buffer::sample_rate; - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int ); - void end_frame( blip_time_t, bool unused = true ); - long samples_avail() const; - long read_samples( blip_sample_t*, long ); - -private: - Blip_Buffer buf; - Blip_Buffer tnd; - Nes_Nonlinearizer nonlinearizer; -}; - -#endif - diff --git a/lib/include/Sound_Queue.h b/lib/include/Sound_Queue.h deleted file mode 100644 index c720648..0000000 --- a/lib/include/Sound_Queue.h +++ /dev/null @@ -1,44 +0,0 @@ - -// Simple sound queue for synchronous sound handling in SDL - -// Copyright (C) 2005 Shay Green. MIT license. - -#ifndef SOUND_QUEUE_H -#define SOUND_QUEUE_H - -#include <SDL2/SDL.h> - -// Simple SDL sound wrapper that has a synchronous interface -class Sound_Queue { -public: - Sound_Queue(); - ~Sound_Queue(); - - // Initialize with specified sample rate and channel count. - // Returns NULL on success, otherwise error string. - const char* init( long sample_rate, int chan_count = 1 ); - - // Number of samples in buffer waiting to be played - int sample_count() const; - - // Write samples to buffer and block until enough space is available - typedef short sample_t; - void write( const sample_t*, int count ); - -private: - enum { buf_size = 2048 }; - enum { buf_count = 3 }; - sample_t* volatile bufs; - SDL_sem* volatile free_sem; - int volatile read_buf; - int write_buf; - int write_pos; - bool sound_open; - - sample_t* buf( int index ); - void fill_buffer( Uint8*, int ); - static void fill_buffer_( void*, Uint8*, int ); -}; - -#endif - diff --git a/lib/include/apu_snapshot.h b/lib/include/apu_snapshot.h deleted file mode 100644 index 5b894cc..0000000 --- a/lib/include/apu_snapshot.h +++ /dev/null @@ -1,75 +0,0 @@ - -// NES APU snapshot support - -// Nes_Snd_Emu 0.1.7. Copyright (C) 2003-2005 Shay Green. GNU LGPL license. - -#ifndef APU_SNAPSHOT_H -#define APU_SNAPSHOT_H - -#include "blargg_common.h" - -struct apu_snapshot_t -{ - typedef BOOST::uint8_t byte; - - typedef byte env_t [3]; - /*struct env_t { - byte delay; - byte env;3 - byte written; - };*/ - - byte w40xx [0x14]; // $4000-$4013 - byte w4015; // enables - byte w4017; // mode - BOOST::uint16_t delay; - byte step; - byte irq_flag; - - struct square_t { - BOOST::uint16_t delay; - env_t env; - byte length; - byte phase; - byte swp_delay; - byte swp_reset; - byte unused [1]; - }; - - square_t square1; - square_t square2; - - struct triangle_t { - BOOST::uint16_t delay; - byte length; - byte phase; - byte linear_counter; - byte linear_mode; - } triangle; - - struct noise_t { - BOOST::uint16_t delay; - env_t env; - byte length; - BOOST::uint16_t shift_reg; - } noise; - - struct dmc_t { - BOOST::uint16_t delay; - BOOST::uint16_t remain; - BOOST::uint16_t addr; - byte buf; - byte bits_remain; - byte bits; - byte buf_empty; - byte silence; - byte irq_flag; - } dmc; - - enum { tag = 'APUR' }; - void swap(); -}; -BOOST_STATIC_ASSERT( sizeof (apu_snapshot_t) == 72 ); - -#endif - diff --git a/lib/include/blargg_common.h b/lib/include/blargg_common.h deleted file mode 100644 index db3c6e6..0000000 --- a/lib/include/blargg_common.h +++ /dev/null @@ -1,180 +0,0 @@ - -// Sets up common environment for Shay Green's libraries. -// -// Don't modify this file directly; #define HAVE_CONFIG_H and put your -// configuration into "config.h". - -// Copyright (C) 2004-2005 Shay Green. - -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -// Allow prefix configuration file *which can re-include blargg_common.h* -// (probably indirectly). -#ifdef HAVE_CONFIG_H - #undef BLARGG_COMMON_H - #include "config.h" - #define BLARGG_COMMON_H -#endif - -// Source files use #include BLARGG_ENABLE_OPTIMIZER before performance-critical code -#ifndef BLARGG_ENABLE_OPTIMIZER - #define BLARGG_ENABLE_OPTIMIZER "blargg_common.h" -#endif - -// Source files have #include BLARGG_SOURCE_BEGIN at the beginning -#ifndef BLARGG_SOURCE_BEGIN - #define BLARGG_SOURCE_BEGIN "blargg_source.h" -#endif - -// Determine compiler's language support - -#if defined (__MWERKS__) - // Metrowerks CodeWarrior - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #if !__option(bool) - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - -#elif defined (_MSC_VER) - // Microsoft Visual C++ - #if _MSC_VER < 1100 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - -#elif defined (__GNUC__) - // GNU C++ - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #define BLARGG_COMPILER_HAS_BOOL 1 - -#elif defined (__MINGW32__) - // Mingw? - #define BLARGG_COMPILER_HAS_BOOL 1 - -#elif __cplusplus < 199711 - // Pre-ISO C++ compiler - #define BLARGG_COMPILER_HAS_BOOL 0 - #define BLARGG_NEW new - #define STATIC_CAST( type ) (type) - -#endif - -// STATIC_CAST(T) (expr) -> static_cast< T > (expr) -#ifndef STATIC_CAST - #define STATIC_CAST( type ) static_cast< type > -#endif - -// Set up boost -#include "boost/config.hpp" -#ifndef BOOST_MINIMAL - #define BOOST boost - #ifndef BLARGG_COMPILER_HAS_NAMESPACE - #define BLARGG_COMPILER_HAS_NAMESPACE 1 - #endif - #ifndef BLARGG_COMPILER_HAS_BOOL - #define BLARGG_COMPILER_HAS_BOOL 1 - #endif -#endif - -// Bool support -#ifndef BLARGG_COMPILER_HAS_BOOL - #define BLARGG_COMPILER_HAS_BOOL 1 -#elif !BLARGG_COMPILER_HAS_BOOL - typedef int bool; - const bool true = 1; - const bool false = 0; -#endif - -// Set up namespace support - -#ifndef BLARGG_COMPILER_HAS_NAMESPACE - #define BLARGG_COMPILER_HAS_NAMESPACE 0 -#endif - -#ifndef BLARGG_USE_NAMESPACE - #define BLARGG_USE_NAMESPACE BLARGG_COMPILER_HAS_NAMESPACE -#endif - -#ifndef BOOST - #if BLARGG_USE_NAMESPACE - #define BOOST boost - #else - #define BOOST - #endif -#endif - -#undef BLARGG_BEGIN_NAMESPACE -#undef BLARGG_END_NAMESPACE -#if BLARGG_USE_NAMESPACE - #define BLARGG_BEGIN_NAMESPACE( name ) namespace name { - #define BLARGG_END_NAMESPACE } -#else - #define BLARGG_BEGIN_NAMESPACE( name ) - #define BLARGG_END_NAMESPACE -#endif - -#if BLARGG_USE_NAMESPACE - #define STD std -#else - #define STD -#endif - -// BOOST::uint8_t, BOOST::int16_t, etc. -#include "boost/cstdint.hpp" - -// BOOST_STATIC_ASSERT( expr ) -#include "boost/static_assert.hpp" - -// Common standard headers -#if BLARGG_COMPILER_HAS_NAMESPACE - #include <cstddef> - #include <cassert> - #include <new> -#else - #include <stddef.h> - #include <assert.h> -#endif - -// blargg_err_t (NULL on success, otherwise error string) -typedef const char* blargg_err_t; -const blargg_err_t blargg_success = 0; - -// BLARGG_NEW is used in place of 'new' to create objects. By default, -// nothrow new is used. -#ifndef BLARGG_NEW - #define BLARGG_NEW new (STD::nothrow) -#endif - -// BLARGG_BIG_ENDIAN and BLARGG_LITTLE_ENDIAN -// Only needed if modules are used which must know byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) - #if defined (__powerc) || defined (macintosh) - #define BLARGG_BIG_ENDIAN 1 - - #elif defined (_MSC_VER) && defined (_M_IX86) - #define BLARGG_LITTLE_ENDIAN 1 - - #endif -#endif - -// BLARGG_NONPORTABLE (allow use of nonportable optimizations/features) -#ifndef BLARGG_NONPORTABLE - #define BLARGG_NONPORTABLE 0 -#endif -#ifdef BLARGG_MOST_PORTABLE - #error "BLARGG_MOST_PORTABLE has been removed; use BLARGG_NONPORTABLE." -#endif - -// BLARGG_CPU_* -#if !defined (BLARGG_CPU_POWERPC) && !defined (BLARGG_CPU_X86) - #if defined (__powerc) - #define BLARGG_CPU_POWERPC 1 - - #elif defined (_MSC_VER) && defined (_M_IX86) - #define BLARGG_CPU_X86 1 - - #endif -#endif - -#endif - diff --git a/lib/include/blargg_source.h b/lib/include/blargg_source.h deleted file mode 100644 index f74f311..0000000 --- a/lib/include/blargg_source.h +++ /dev/null @@ -1,43 +0,0 @@ - -// By default, #included at beginning of library source files - -// Copyright (C) 2005 Shay Green. - -#ifndef BLARGG_SOURCE_H -#define BLARGG_SOURCE_H - -// If debugging is enabled, abort program if expr is false. Meant for checking -// internal state and consistency. A failed assertion indicates a bug in the module. -// void assert( bool expr ); -#include <assert.h> - -// If debugging is enabled and expr is false, abort program. Meant for checking -// caller-supplied parameters and operations that are outside the control of the -// module. A failed requirement indicates a bug outside the module. -// void require( bool expr ); -#undef require -#define require( expr ) assert(( "unmet requirement", expr )) - -// Like printf() except output goes to debug log file. Might be defined to do -// nothing (not even evaluate its arguments). -// void dprintf( const char* format, ... ); -#undef dprintf -#define dprintf (1) ? ((void) 0) : (void) - -// If enabled, evaluate expr and if false, make debug log entry with source file -// and line. Meant for finding situations that should be examined further, but that -// don't indicate a problem. In all cases, execution continues normally. -#undef check -#define check( expr ) ((void) 0) - -// If expr returns non-NULL error string, return it from current function, otherwise continue. -#define BLARGG_RETURN_ERR( expr ) do { \ - blargg_err_t blargg_return_err_ = (expr); \ - if ( blargg_return_err_ ) return blargg_return_err_; \ - } while ( 0 ) - -// If ptr is NULL, return out of memory error string. -#define BLARGG_CHECK_ALLOC( ptr ) do { if ( !(ptr) ) return "Out of memory"; } while ( 0 ) - -#endif - diff --git a/lib/include/boost/config.hpp b/lib/include/boost/config.hpp deleted file mode 100644 index f271715..0000000 --- a/lib/include/boost/config.hpp +++ /dev/null @@ -1,13 +0,0 @@ - -// Boost substitute. For full boost library see http://boost.org - -#ifndef BOOST_CONFIG_HPP -#define BOOST_CONFIG_HPP - -#define BOOST_MINIMAL 1 - -#define BLARGG_BEGIN_NAMESPACE( name ) -#define BLARGG_END_NAMESPACE - -#endif - diff --git a/lib/include/boost/cstdint.hpp b/lib/include/boost/cstdint.hpp deleted file mode 100644 index e446dfd..0000000 --- a/lib/include/boost/cstdint.hpp +++ /dev/null @@ -1,42 +0,0 @@ - -// Boost substitute. For full boost library see http://boost.org - -#ifndef BOOST_CSTDINT_HPP -#define BOOST_CSTDINT_HPP - -#if BLARGG_USE_NAMESPACE - #include <climits> -#else - #include <limits.h> -#endif - -BLARGG_BEGIN_NAMESPACE( boost ) - -#if UCHAR_MAX != 0xFF || SCHAR_MAX != 0x7F -# error "No suitable 8-bit type available" -#endif - -typedef unsigned char uint8_t; -typedef signed char int8_t; - -#if USHRT_MAX != 0xFFFF -# error "No suitable 16-bit type available" -#endif - -typedef short int16_t; -typedef unsigned short uint16_t; - -#if ULONG_MAX == 0xFFFFFFFF - typedef long int32_t; - typedef unsigned long uint32_t; -#elif UINT_MAX == 0xFFFFFFFF - typedef int int32_t; - typedef unsigned int uint32_t; -#else -# error "No suitable 32-bit type available" -#endif - -BLARGG_END_NAMESPACE - -#endif - diff --git a/lib/include/boost/static_assert.hpp b/lib/include/boost/static_assert.hpp deleted file mode 100644 index 66927cc..0000000 --- a/lib/include/boost/static_assert.hpp +++ /dev/null @@ -1,22 +0,0 @@ - -// Boost substitute. For full boost library see http://boost.org - -#ifndef BOOST_STATIC_ASSERT_HPP -#define BOOST_STATIC_ASSERT_HPP - -#if defined (_MSC_VER) && _MSC_VER <= 1200 - // MSVC6 can't handle the ##line concatenation - #define BOOST_STATIC_ASSERT( expr ) struct { int n [1 / ((expr) ? 1 : 0)]; } - -#else - #define BOOST_STATIC_ASSERT3( expr, line ) \ - typedef int boost_static_assert_##line [1 / ((expr) ? 1 : 0)] - - #define BOOST_STATIC_ASSERT2( expr, line ) BOOST_STATIC_ASSERT3( expr, line ) - - #define BOOST_STATIC_ASSERT( expr ) BOOST_STATIC_ASSERT2( expr, __LINE__ ) - -#endif - -#endif - @@ -9,22 +9,18 @@ */ #include <bits/stdc++.h> -#include <sys/stat.h> -#include <dirent.h> -#include <unistd.h> #include <SDL2/SDL.h> -#include "NES_Apu.h" -#include "Sound_Queue.h" #define NTH_BIT(x, n) (((x) >> (n)) & 1) typedef uint8_t u8; typedef int8_t s8; typedef uint16_t u16; typedef int16_t s16; typedef uint32_t u32; typedef int32_t s32; typedef uint64_t u64; typedef int64_t s64; +typedef long cpu_time_t; // CPU clock cycle count +typedef unsigned cpu_addr_t; // 16-bit memory address // Initial declarations namespace APU { template <bool write> u8 access(int elapsed, u16 addr, u8 v = 0); - void run_frame(int elapsed), reset(), init(); } namespace CPU { enum IntType { NMI, RESET, IRQ, BRK }; // Interrupt type. @@ -98,15 +94,13 @@ namespace Cartridge { void signal_scanline(), load(const char *fileName); } namespace Joypad { - u8 read_state(int n); - void write_strobe(bool v); + u8 read_state(int n); void write_strobe(bool v); } namespace GUI { const unsigned WIDTH = 256, HEIGHT = 240; // Screen size - const int TEXT_CENTER = -1, TEXT_RIGHT = -2; int query_button(); void init(), run(); - void render_texture(SDL_Texture* texture, int x, int y), new_frame(u32* pixels), new_samples(const blip_sample_t* samples, size_t count); + void new_frame(u32* pixels); u8 get_joypad_state(int n); SDL_Scancode query_key(); } @@ -290,24 +284,9 @@ public: // Actual code namespace APU { - Nes_Apu apu; Blip_Buffer buf; const int OUT_SIZE = 4096; - blip_sample_t outBuf[OUT_SIZE]; - void init() { - buf.sample_rate(96000); buf.clock_rate(1789773); - apu.output(&buf); apu.dmc_reader(CPU::dmc_read); - } - void reset() { apu.reset(); buf.clear(); } - template <bool write> u8 access(int elapsed, u16 addr, u8 v) { - if (write) apu.write_register(elapsed, addr, v); - else if (addr == apu.status_addr) v = apu.read_status(elapsed); - return v; - } + template <bool write> u8 access(int elapsed, u16 addr, u8 v) { return v; } template u8 access<0>(int, u16, u8); template u8 access<1>(int, u16, u8); - void run_frame(int elapsed) { - apu.end_frame(elapsed); buf.end_frame(elapsed); - if (buf.samples_avail() >= OUT_SIZE) GUI::new_samples(outBuf, buf.read_samples(outBuf, OUT_SIZE)); - } } namespace CPU { u8 A, X, Y, S, ram[0x800]; u16 PC; Flags P; bool nmi, irq; // CPU state @@ -429,85 +408,46 @@ namespace CPU { // Execute a CPU instruction void exec() { switch (rd(PC++)) { // Fetch the opcode and select the right function to emulate the instruction: - case 0x00: return INT<BRK>() ; case 0x01: return ORA<izx>() ; - case 0x05: return ORA<zp>() ; case 0x06: return ASL<zp>() ; - case 0x08: return PHP() ; case 0x09: return ORA<imm>() ; - case 0x0A: return ASL_A() ; case 0x0D: return ORA<abs>() ; - case 0x0E: return ASL<abs>() ; case 0x10: return br<N,0>() ; - case 0x11: return ORA<izy>() ; case 0x15: return ORA<zpx>() ; - case 0x16: return ASL<zpx>() ; case 0x18: return flag<C,0>() ; - case 0x19: return ORA<aby>() ; case 0x1D: return ORA<abx>() ; - case 0x1E: return ASL<_abx>() ; case 0x20: return JSR() ; - case 0x21: return AND<izx>() ; case 0x24: return BIT<zp>() ; - case 0x25: return AND<zp>() ; case 0x26: return ROL<zp>() ; - case 0x28: return PLP() ; case 0x29: return AND<imm>() ; - case 0x2A: return ROL_A() ; case 0x2C: return BIT<abs>() ; - case 0x2D: return AND<abs>() ; case 0x2E: return ROL<abs>() ; - case 0x30: return br<N,1>() ; case 0x31: return AND<izy>() ; - case 0x35: return AND<zpx>() ; case 0x36: return ROL<zpx>() ; - case 0x38: return flag<C,1>() ; case 0x39: return AND<aby>() ; - case 0x3D: return AND<abx>() ; case 0x3E: return ROL<_abx>() ; - case 0x40: return RTI() ; case 0x41: return EOR<izx>() ; - case 0x45: return EOR<zp>() ; case 0x46: return LSR<zp>() ; - case 0x48: return PHA() ; case 0x49: return EOR<imm>() ; - case 0x4A: return LSR_A() ; case 0x4C: return JMP() ; - case 0x4D: return EOR<abs>() ; case 0x4E: return LSR<abs>() ; - case 0x50: return br<V,0>() ; case 0x51: return EOR<izy>() ; - case 0x55: return EOR<zpx>() ; case 0x56: return LSR<zpx>() ; - case 0x58: return flag<I,0>() ; case 0x59: return EOR<aby>() ; - case 0x5D: return EOR<abx>() ; case 0x5E: return LSR<_abx>() ; - case 0x60: return RTS() ; case 0x61: return ADC<izx>() ; - case 0x65: return ADC<zp>() ; case 0x66: return ROR<zp>() ; - case 0x68: return PLA() ; case 0x69: return ADC<imm>() ; - case 0x6A: return ROR_A() ; case 0x6C: return JMP_IND() ; - case 0x6D: return ADC<abs>() ; case 0x6E: return ROR<abs>() ; - case 0x70: return br<V,1>() ; case 0x71: return ADC<izy>() ; - case 0x75: return ADC<zpx>() ; case 0x76: return ROR<zpx>() ; - case 0x78: return flag<I,1>() ; case 0x79: return ADC<aby>() ; - case 0x7D: return ADC<abx>() ; case 0x7E: return ROR<_abx>() ; - case 0x81: return st<A,izx>() ; case 0x84: return st<Y,zp>() ; - case 0x85: return st<A,zp>() ; case 0x86: return st<X,zp>() ; - case 0x88: return dec<Y>() ; case 0x8A: return tr<X,A>() ; - case 0x8C: return st<Y,abs>() ; case 0x8D: return st<A,abs>() ; - case 0x8E: return st<X,abs>() ; case 0x90: return br<C,0>() ; - case 0x91: return st<A,izy>() ; case 0x94: return st<Y,zpx>() ; - case 0x95: return st<A,zpx>() ; case 0x96: return st<X,zpy>() ; - case 0x98: return tr<Y,A>() ; case 0x99: return st<A,aby>() ; - case 0x9A: return tr<X,S>() ; case 0x9D: return st<A,abx>() ; - case 0xA0: return ld<Y,imm>() ; case 0xA1: return ld<A,izx>() ; - case 0xA2: return ld<X,imm>() ; case 0xA4: return ld<Y,zp>() ; - case 0xA5: return ld<A,zp>() ; case 0xA6: return ld<X,zp>() ; - case 0xA8: return tr<A,Y>() ; case 0xA9: return ld<A,imm>() ; - case 0xAA: return tr<A,X>() ; case 0xAC: return ld<Y,abs>() ; - case 0xAD: return ld<A,abs>() ; case 0xAE: return ld<X,abs>() ; - case 0xB0: return br<C,1>() ; case 0xB1: return ld<A,izy>() ; - case 0xB4: return ld<Y,zpx>() ; case 0xB5: return ld<A,zpx>() ; - case 0xB6: return ld<X,zpy>() ; case 0xB8: return flag<V,0>() ; - case 0xB9: return ld<A,aby>() ; case 0xBA: return tr<S,X>() ; - case 0xBC: return ld<Y,abx>() ; case 0xBD: return ld<A,abx>() ; - case 0xBE: return ld<X,aby>() ; case 0xC0: return cmp<Y,imm>(); - case 0xC1: return cmp<A,izx>(); case 0xC4: return cmp<Y,zp>() ; - case 0xC5: return cmp<A,zp>() ; case 0xC6: return DEC<zp>() ; - case 0xC8: return inc<Y>() ; case 0xC9: return cmp<A,imm>(); - case 0xCA: return dec<X>() ; case 0xCC: return cmp<Y,abs>(); - case 0xCD: return cmp<A,abs>(); case 0xCE: return DEC<abs>() ; - case 0xD0: return br<Z,0>() ; case 0xD1: return cmp<A,izy>(); - case 0xD5: return cmp<A,zpx>(); case 0xD6: return DEC<zpx>() ; - case 0xD8: return flag<D,0>() ; case 0xD9: return cmp<A,aby>(); - case 0xDD: return cmp<A,abx>(); case 0xDE: return DEC<_abx>() ; - case 0xE0: return cmp<X,imm>(); case 0xE1: return SBC<izx>() ; - case 0xE4: return cmp<X,zp>() ; case 0xE5: return SBC<zp>() ; - case 0xE6: return INC<zp>() ; case 0xE8: return inc<X>() ; - case 0xE9: return SBC<imm>() ; case 0xEA: return NOP() ; - case 0xEC: return cmp<X,abs>(); case 0xED: return SBC<abs>() ; - case 0xEE: return INC<abs>() ; case 0xF0: return br<Z,1>() ; - case 0xF1: return SBC<izy>() ; case 0xF5: return SBC<zpx>() ; - case 0xF6: return INC<zpx>() ; case 0xF8: return flag<D,1>() ; - case 0xF9: return SBC<aby>() ; case 0xFD: return SBC<abx>() ; - case 0xFE: return INC<_abx>() ; - default: - std::cout << "Invalid Opcode! PC: " << PC << " Opcode: 0x" << std::hex << (int)(rd(PC - 1)) << "\n"; - return NOP(); + case 0x00: return INT<BRK>() ; case 0x01: return ORA<izx>() ; case 0x05: return ORA<zp>() ; case 0x06: return ASL<zp>() ; + case 0x08: return PHP() ; case 0x09: return ORA<imm>() ; case 0x0A: return ASL_A() ; case 0x0D: return ORA<abs>() ; + case 0x0E: return ASL<abs>() ; case 0x10: return br<N,0>() ; case 0x11: return ORA<izy>() ; case 0x15: return ORA<zpx>() ; + case 0x16: return ASL<zpx>() ; case 0x18: return flag<C,0>() ; case 0x19: return ORA<aby>() ; case 0x1D: return ORA<abx>() ; + case 0x1E: return ASL<_abx>() ; case 0x20: return JSR() ; case 0x21: return AND<izx>() ; case 0x24: return BIT<zp>() ; + case 0x25: return AND<zp>() ; case 0x26: return ROL<zp>() ; case 0x28: return PLP() ; case 0x29: return AND<imm>() ; + case 0x2A: return ROL_A() ; case 0x2C: return BIT<abs>() ; case 0x2D: return AND<abs>() ; case 0x2E: return ROL<abs>() ; + case 0x30: return br<N,1>() ; case 0x31: return AND<izy>() ; case 0x35: return AND<zpx>() ; case 0x36: return ROL<zpx>() ; + case 0x38: return flag<C,1>() ; case 0x39: return AND<aby>() ; case 0x3D: return AND<abx>() ; case 0x3E: return ROL<_abx>() ; + case 0x40: return RTI() ; case 0x41: return EOR<izx>() ; case 0x45: return EOR<zp>() ; case 0x46: return LSR<zp>() ; + case 0x48: return PHA() ; case 0x49: return EOR<imm>() ; case 0x4A: return LSR_A() ; case 0x4C: return JMP() ; + case 0x4D: return EOR<abs>() ; case 0x4E: return LSR<abs>() ; case 0x50: return br<V,0>() ; case 0x51: return EOR<izy>() ; + case 0x55: return EOR<zpx>() ; case 0x56: return LSR<zpx>() ; case 0x58: return flag<I,0>() ; case 0x59: return EOR<aby>() ; + case 0x5D: return EOR<abx>() ; case 0x5E: return LSR<_abx>() ; case 0x60: return RTS() ; case 0x61: return ADC<izx>() ; + case 0x65: return ADC<zp>() ; case 0x66: return ROR<zp>() ; case 0x68: return PLA() ; case 0x69: return ADC<imm>() ; + case 0x6A: return ROR_A() ; case 0x6C: return JMP_IND() ; case 0x6D: return ADC<abs>() ; case 0x6E: return ROR<abs>() ; + case 0x70: return br<V,1>() ; case 0x71: return ADC<izy>() ; case 0x75: return ADC<zpx>() ; case 0x76: return ROR<zpx>() ; + case 0x78: return flag<I,1>() ; case 0x79: return ADC<aby>() ; case 0x7D: return ADC<abx>() ; case 0x7E: return ROR<_abx>() ; + case 0x81: return st<A,izx>() ; case 0x84: return st<Y,zp>() ; case 0x85: return st<A,zp>() ; case 0x86: return st<X,zp>() ; + case 0x88: return dec<Y>() ; case 0x8A: return tr<X,A>() ; case 0x8C: return st<Y,abs>() ; case 0x8D: return st<A,abs>() ; + case 0x8E: return st<X,abs>() ; case 0x90: return br<C,0>() ; case 0x91: return st<A,izy>() ; case 0x94: return st<Y,zpx>() ; + case 0x95: return st<A,zpx>() ; case 0x96: return st<X,zpy>() ; case 0x98: return tr<Y,A>() ; case 0x99: return st<A,aby>() ; + case 0x9A: return tr<X,S>() ; case 0x9D: return st<A,abx>() ; case 0xA0: return ld<Y,imm>() ; case 0xA1: return ld<A,izx>() ; + case 0xA2: return ld<X,imm>() ; case 0xA4: return ld<Y,zp>() ; case 0xA5: return ld<A,zp>() ; case 0xA6: return ld<X,zp>() ; + case 0xA8: return tr<A,Y>() ; case 0xA9: return ld<A,imm>() ; case 0xAA: return tr<A,X>() ; case 0xAC: return ld<Y,abs>() ; + case 0xAD: return ld<A,abs>() ; case 0xAE: return ld<X,abs>() ; case 0xB0: return br<C,1>() ; case 0xB1: return ld<A,izy>() ; + case 0xB4: return ld<Y,zpx>() ; case 0xB5: return ld<A,zpx>() ; case 0xB6: return ld<X,zpy>() ; case 0xB8: return flag<V,0>() ; + case 0xB9: return ld<A,aby>() ; case 0xBA: return tr<S,X>() ; case 0xBC: return ld<Y,abx>() ; case 0xBD: return ld<A,abx>() ; + case 0xBE: return ld<X,aby>() ; case 0xC0: return cmp<Y,imm>(); case 0xC1: return cmp<A,izx>(); case 0xC4: return cmp<Y,zp>() ; + case 0xC5: return cmp<A,zp>() ; case 0xC6: return DEC<zp>() ; case 0xC8: return inc<Y>() ; case 0xC9: return cmp<A,imm>(); + case 0xCA: return dec<X>() ; case 0xCC: return cmp<Y,abs>(); case 0xCD: return cmp<A,abs>(); case 0xCE: return DEC<abs>() ; + case 0xD0: return br<Z,0>() ; case 0xD1: return cmp<A,izy>(); case 0xD5: return cmp<A,zpx>(); case 0xD6: return DEC<zpx>() ; + case 0xD8: return flag<D,0>() ; case 0xD9: return cmp<A,aby>(); case 0xDD: return cmp<A,abx>(); case 0xDE: return DEC<_abx>() ; + case 0xE0: return cmp<X,imm>(); case 0xE1: return SBC<izx>() ; case 0xE4: return cmp<X,zp>() ; case 0xE5: return SBC<zp>() ; + case 0xE6: return INC<zp>() ; case 0xE8: return inc<X>() ; case 0xE9: return SBC<imm>() ; case 0xEA: return NOP() ; + case 0xEC: return cmp<X,abs>(); case 0xED: return SBC<abs>() ; case 0xEE: return INC<abs>() ; case 0xF0: return br<Z,1>() ; + case 0xF1: return SBC<izy>() ; case 0xF5: return SBC<zpx>() ; case 0xF6: return INC<zpx>() ; case 0xF8: return flag<D,1>() ; + case 0xF9: return SBC<aby>() ; case 0xFD: return SBC<abx>() ; case 0xFE: return INC<_abx>() ; default: + std::cout << "Invalid Opcode! PC: " << PC << " Opcode: 0x" << std::hex << (int)(rd(PC - 1)) << "\n"; + return NOP(); } } void set_nmi(bool v) { nmi = v; } @@ -530,11 +470,17 @@ namespace CPU { else if (irq and !P[I]) INT<IRQ>(); exec(); } - APU::run_frame(elapsed()); } } namespace PPU { - #include "palette.inc" + u32 nesRgb[] = { 0x7C7C7C, 0x0000FC, 0x0000BC, 0x4428BC, 0x940084, 0xA80020, 0xA81000, 0x881400, + 0x503000, 0x007800, 0x006800, 0x005800, 0x004058, 0x000000, 0x000000, 0x000000, + 0xBCBCBC, 0x0078F8, 0x0058F8, 0x6844FC, 0xD800CC, 0xE40058, 0xF83800, 0xE45C10, + 0xAC7C00, 0x00B800, 0x00A800, 0x00A844, 0x008888, 0x000000, 0x000000, 0x000000, + 0xF8F8F8, 0x3CBCFC, 0x6888FC, 0x9878F8, 0xF878F8, 0xF85898, 0xF87858, 0xFCA044, + 0xF8B800, 0xB8F818, 0x58D854, 0x58F898, 0x00E8D8, 0x787878, 0x000000, 0x000000, + 0xFCFCFC, 0xA4E4FC, 0xB8B8F8, 0xD8B8F8, 0xF8B8F8, 0xF8A4C0, 0xF0D0B0, 0xFCE0A8, + 0xF8D878, 0xD8F878, 0xB8F8B8, 0xB8F8D8, 0x00FCFC, 0xF8D8F8, 0x000000, 0x000000 }; Mirroring mirroring; // Mirroring mode u8 ciRam[0x800], cgRam[0x20], oamMem[0x100]; // VRAM for nametables, palettes, sprite properties Sprite oam[8], secOam[8]; // Sprite buffers @@ -796,7 +742,7 @@ namespace Cartridge { case 3: mapper = new Mapper3(rom); break; case 4: mapper = new Mapper4(rom); break; } - CPU::power(), PPU::reset(), APU::reset(); + CPU::power(), PPU::reset(); } } namespace Joypad { @@ -821,7 +767,6 @@ namespace GUI { SDL_Renderer* renderer; SDL_Texture* gameTexture; u8 const* keys; - Sound_Queue* soundQueue; SDL_Scancode KEY_A = SDL_SCANCODE_A, KEY_B = SDL_SCANCODE_S, KEY_SELECT = SDL_SCANCODE_SPACE, KEY_START = SDL_SCANCODE_RETURN; SDL_Scancode KEY_UP = SDL_SCANCODE_UP, KEY_DOWN = SDL_SCANCODE_DOWN, KEY_LEFT = SDL_SCANCODE_LEFT, KEY_RIGHT = SDL_SCANCODE_RIGHT; // Initialize GUI @@ -829,43 +774,23 @@ namespace GUI { // Initialize graphics system SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); - APU::init(); - soundQueue = new Sound_Queue; - soundQueue->init(96000); // Initialize graphics structures - window = SDL_CreateWindow ("BadNES", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIDTH, HEIGHT, 0); + window = SDL_CreateWindow("BadNES", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WIDTH, HEIGHT, 0); renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); SDL_RenderSetLogicalSize(renderer, WIDTH, HEIGHT); gameTexture = SDL_CreateTexture (renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT); keys = SDL_GetKeyboardState(0); } - // Render a texture on screen - void render_texture(SDL_Texture* texture, int x, int y) { - int w, h; SDL_Rect dest; - SDL_QueryTexture(texture, NULL, NULL, &dest.w, &dest.h); - if (x == TEXT_CENTER) dest.x = WIDTH/2 - dest.w/2; - else if (x == TEXT_RIGHT) dest.x = WIDTH - dest.w - 10; - else dest.x = x + 10; - dest.y = y + 5; - SDL_RenderCopy(renderer, texture, NULL, &dest); - } // Get the joypad state from SDL u8 get_joypad_state(int n) { const int DEAD_ZONE = 8000; u8 j = 0; - j |= keys[KEY_A] << 0; - j |= keys[KEY_B] << 1; - j |= keys[KEY_SELECT] << 2; - j |= keys[KEY_START] << 3; - j |= keys[KEY_UP] << 4; - j |= keys[KEY_DOWN] << 5; - j |= keys[KEY_LEFT] << 6; - j |= keys[KEY_RIGHT] << 7; + j |= keys[KEY_A] << 0; j |= keys[KEY_B] << 1; j |= keys[KEY_SELECT] << 2; j |= keys[KEY_START] << 3; + j |= keys[KEY_UP] << 4; j |= keys[KEY_DOWN] << 5; j |= keys[KEY_LEFT] << 6; j |= keys[KEY_RIGHT] << 7; return j; } // Send the rendered frame to the GUI void new_frame(u32* pixels) { SDL_UpdateTexture(gameTexture, NULL, pixels, WIDTH * sizeof(u32)); } - void new_samples(const blip_sample_t* samples, size_t count) { soundQueue->write(samples, count); } // Render the screen void render() { SDL_RenderClear(renderer); diff --git a/old/apu.cpp b/old/apu.cpp deleted file mode 100644 index 1a22e10..0000000 --- a/old/apu.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "gui.hpp" -#include "cpu.hpp" -#include "apu.hpp" - -namespace APU { - - -Nes_Apu apu; -Blip_Buffer buf; - -const int OUT_SIZE = 4096; -blip_sample_t outBuf[OUT_SIZE]; - -void init() -{ - buf.sample_rate(96000); - buf.clock_rate(1789773); - - apu.output(&buf); - apu.dmc_reader(CPU::dmc_read); -} - -void reset() -{ - apu.reset(); - buf.clear(); -} - -template <bool write> u8 access(int elapsed, u16 addr, u8 v) -{ - if (write) - apu.write_register(elapsed, addr, v); - else if (addr == apu.status_addr) - v = apu.read_status(elapsed); - - return v; -} -template u8 access<0>(int, u16, u8); template u8 access<1>(int, u16, u8); - -void run_frame(int elapsed) -{ - apu.end_frame(elapsed); - buf.end_frame(elapsed); - - if (buf.samples_avail() >= OUT_SIZE) - GUI::new_samples(outBuf, buf.read_samples(outBuf, OUT_SIZE)); -} - - -} diff --git a/old/cartridge.cpp b/old/cartridge.cpp deleted file mode 100644 index 29673ca..0000000 --- a/old/cartridge.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include <cstdio> -#include "apu.hpp" -#include "cpu.hpp" -#include "mappers/mapper0.hpp" -#include "mappers/mapper1.hpp" -#include "mappers/mapper2.hpp" -#include "mappers/mapper3.hpp" -#include "mappers/mapper4.hpp" -#include "ppu.hpp" -#include "cartridge.hpp" - -namespace Cartridge { - - -Mapper* mapper = nullptr; // Mapper chip. - -/* PRG-ROM access */ -template <bool wr> u8 access(u16 addr, u8 v) -{ - if (!wr) return mapper->read(addr); - else return mapper->write(addr, v); -} -template u8 access<0>(u16, u8); template u8 access<1>(u16, u8); - -/* CHR-ROM/RAM access */ -template <bool wr> u8 chr_access(u16 addr, u8 v) -{ - if (!wr) return mapper->chr_read(addr); - else return mapper->chr_write(addr, v); -} -template u8 chr_access<0>(u16, u8); template u8 chr_access<1>(u16, u8); - -void signal_scanline() -{ - mapper->signal_scanline(); -} - -/* Load the ROM from a file. */ -void load(const char* fileName) -{ - FILE* f = fopen(fileName, "rb"); - - fseek(f, 0, SEEK_END); - int size = ftell(f); - fseek(f, 0, SEEK_SET); - - u8* rom = new u8[size]; - fread(rom, size, 1, f); - fclose(f); - - int mapperNum = (rom[7] & 0xF0) | (rom[6] >> 4); - if (loaded()) delete mapper; - switch (mapperNum) - { - case 0: mapper = new Mapper0(rom); break; - case 1: mapper = new Mapper1(rom); break; - case 2: mapper = new Mapper2(rom); break; - case 3: mapper = new Mapper3(rom); break; - case 4: mapper = new Mapper4(rom); break; - } - - CPU::power(); - PPU::reset(); - APU::reset(); -} - -bool loaded() -{ - return mapper != nullptr; -} - - -} diff --git a/old/config.cpp b/old/config.cpp deleted file mode 100644 index 3172e0e..0000000 --- a/old/config.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include <cstdlib> -#include <SimpleIni.h> -#include "config.hpp" -#include "gui.hpp" - -namespace GUI { - -/* Settings */ -CSimpleIniA ini(true, false, false); - -/* Window settings */ -int last_window_size = 1; - -/* Controls settings */ -SDL_Scancode KEY_A [] = { SDL_SCANCODE_A, SDL_SCANCODE_ESCAPE }; -SDL_Scancode KEY_B [] = { SDL_SCANCODE_S, SDL_SCANCODE_ESCAPE }; -SDL_Scancode KEY_SELECT[] = { SDL_SCANCODE_SPACE, SDL_SCANCODE_ESCAPE }; -SDL_Scancode KEY_START [] = { SDL_SCANCODE_RETURN, SDL_SCANCODE_ESCAPE }; -SDL_Scancode KEY_UP [] = { SDL_SCANCODE_UP, SDL_SCANCODE_ESCAPE }; -SDL_Scancode KEY_DOWN [] = { SDL_SCANCODE_DOWN, SDL_SCANCODE_ESCAPE }; -SDL_Scancode KEY_LEFT [] = { SDL_SCANCODE_LEFT, SDL_SCANCODE_ESCAPE }; -SDL_Scancode KEY_RIGHT [] = { SDL_SCANCODE_RIGHT, SDL_SCANCODE_ESCAPE }; -int BTN_UP [] = { -1, -1 }; -int BTN_DOWN [] = { -1, -1 }; -int BTN_LEFT [] = { -1, -1 }; -int BTN_RIGHT [] = { -1, -1 }; -int BTN_A [] = { -1, -1 }; -int BTN_B [] = { -1, -1 }; -int BTN_SELECT[] = { -1, -1 }; -int BTN_START [] = { -1, -1 }; -bool useJoystick[] = { false, false }; - - - -/* Ensure config directory exists */ -const char* get_config_path(char* buf, int buflen) -{ - /* Bail on the complex stuff if we don't need it */ - if (!USE_CONFIG_DIR) - return CONFIG_FALLBACK; - - /* First, get the home directory */ - char homepath[CONFIG_PATH_MAX]; - char path[CONFIG_PATH_MAX]; - char * home = getenv("HOME"); - if (home == NULL) - return CONFIG_FALLBACK; - - snprintf(homepath, sizeof(homepath), "%s/.config", home); - - /* Then, .config as a folder */ - int res = mkdir(homepath, CONFIG_DIR_DEFAULT_MODE); - int err = errno; - - if (res == -1 && err != EEXIST) - return CONFIG_FALLBACK; - - snprintf(path, sizeof(path), "%s/%s", homepath, CONFIG_DIR_NAME); - - /* Finally, CONFIG_DIR_NAME as a sub-folder */ - res = mkdir(path, CONFIG_DIR_DEFAULT_MODE); - err = errno; - - if (res == -1 && err != EEXIST) - return CONFIG_FALLBACK; - - snprintf(buf, buflen, "%s/settings", path); - - return buf; -} - - -/* Load settings */ -void load_settings() -{ - /* Files */ - char path[CONFIG_PATH_MAX]; - ini.LoadFile(get_config_path(path, sizeof(path))); - - /* Screen settings */ - int screen_size = atoi(ini.GetValue("screen", "size", "1")); - if (screen_size < 1 || screen_size > 4) - screen_size = 1; - - set_size(screen_size); - - /* Control settings */ - for (int p = 0; p <= 1; p++) - { - const char* section = (p == 0) ? "controls p1" : "controls p2"; - - useJoystick[p] = (ini.GetValue(section, "usejoy", "no"))[0] == 'y'; - if (useJoystick[p]) - { - BTN_UP[p] = atoi(ini.GetValue(section, "UP", "-1")); - BTN_DOWN[p] = atoi(ini.GetValue(section, "DOWN", "-1")); - BTN_LEFT[p] = atoi(ini.GetValue(section, "LEFT", "-1")); - BTN_RIGHT[p] = atoi(ini.GetValue(section, "RIGHT", "-1")); - BTN_A[p] = atoi(ini.GetValue(section, "A", "-1")); - BTN_B[p] = atoi(ini.GetValue(section, "B", "-1")); - BTN_SELECT[p] = atoi(ini.GetValue(section, "SELECT", "-1")); - BTN_START[p] = atoi(ini.GetValue(section, "START", "-1")); - } - else - { - KEY_UP[p] = (SDL_Scancode)atoi(ini.GetValue(section, "UP", "82")); - KEY_DOWN[p] = (SDL_Scancode)atoi(ini.GetValue(section, "DOWN", "81")); - KEY_LEFT[p] = (SDL_Scancode)atoi(ini.GetValue(section, "LEFT", "80")); - KEY_RIGHT[p] = (SDL_Scancode)atoi(ini.GetValue(section, "RIGHT", "79")); - KEY_A[p] = (SDL_Scancode)atoi(ini.GetValue(section, "A", "4")); - KEY_B[p] = (SDL_Scancode)atoi(ini.GetValue(section, "B", "22")); - KEY_SELECT[p] = (SDL_Scancode)atoi(ini.GetValue(section, "SELECT", "44")); - KEY_START[p] = (SDL_Scancode)atoi(ini.GetValue(section, "START", "40")); - } - } -} - - -/* Save settings */ -void save_settings() -{ - /* Screen settings */ - char buf[10]; - sprintf(buf, "%d", last_window_size); - ini.SetValue("screen", "size", buf); - - /* Control settings */ - for (int p = 0; p < 2; p++) - { - const char* section = (p == 0) ? "controls p1" : "controls p2"; - - sprintf(buf, "%d", useJoystick[p] ? BTN_UP[p] : KEY_UP[p]); - ini.SetValue(section, "UP", buf); - sprintf(buf, "%d", useJoystick[p] ? BTN_DOWN[p] : KEY_DOWN[p]); - ini.SetValue(section, "DOWN", buf); - sprintf(buf, "%d", useJoystick[p] ? BTN_LEFT[p] : KEY_LEFT[p]); - ini.SetValue(section, "LEFT", buf); - sprintf(buf, "%d", useJoystick[p] ? BTN_RIGHT[p] : KEY_RIGHT[p]); - ini.SetValue(section, "RIGHT", buf); - sprintf(buf, "%d", useJoystick[p] ? BTN_A[p] : KEY_A[p]); - ini.SetValue(section, "A", buf); - sprintf(buf, "%d", useJoystick[p] ? BTN_B[p] : KEY_B[p]); - ini.SetValue(section, "B", buf); - sprintf(buf, "%d", useJoystick[p] ? BTN_SELECT[p] : KEY_SELECT[p]); - ini.SetValue(section, "SELECT", buf); - sprintf(buf, "%d", useJoystick[p] ? BTN_START[p] : KEY_START[p]); - ini.SetValue(section, "START", buf); - ini.SetValue(section, "usejoy", useJoystick[p] ? "yes" : "no"); - } - - char path[CONFIG_PATH_MAX]; - ini.SaveFile(get_config_path(path, sizeof(path))); -} - -} diff --git a/old/cpu.cpp b/old/cpu.cpp deleted file mode 100644 index 444cfdf..0000000 --- a/old/cpu.cpp +++ /dev/null @@ -1,282 +0,0 @@ -#include <cstdlib> -#include <cstring> -#include <iostream> -#include "apu.hpp" -#include "cartridge.hpp" -#include "joypad.hpp" -#include "ppu.hpp" -#include "cpu.hpp" - -namespace CPU { - - -/* CPU state */ -u8 ram[0x800]; -u8 A, X, Y, S; -u16 PC; -Flags P; -bool nmi, irq; - -// Remaining clocks to end frame: -const int TOTAL_CYCLES = 29781; -int remainingCycles; -inline int elapsed() { return TOTAL_CYCLES - remainingCycles; } - -/* Cycle emulation */ -#define T tick() -inline void tick() { PPU::step(); PPU::step(); PPU::step(); remainingCycles--; } - -/* Flags updating */ -inline void upd_cv(u8 x, u8 y, s16 r) { P[C] = (r>0xFF); P[V] = ~(x^y) & (x^r) & 0x80; } -inline void upd_nz(u8 x) { P[N] = x & 0x80; P[Z] = (x == 0); } -// Does adding I to A cross a page? -inline bool cross(u16 a, u8 i) { return ((a+i) & 0xFF00) != ((a & 0xFF00)); } - -/* Memory access */ -void dma_oam(u8 bank); -template<bool wr> inline u8 access(u16 addr, u8 v = 0) -{ - u8* r; - switch (addr) - { - case 0x0000 ... 0x1FFF: r = &ram[addr % 0x800]; if (wr) *r = v; return *r; // RAM. - case 0x2000 ... 0x3FFF: return PPU::access<wr>(addr % 8, v); // PPU. - - // APU: - case 0x4000 ... 0x4013: - case 0x4015: return APU::access<wr>(elapsed(), addr, v); - case 0x4017: if (wr) return APU::access<wr>(elapsed(), addr, v); - else return Joypad::read_state(1); // Joypad 1. - - case 0x4014: if (wr) dma_oam(v); break; // OAM DMA. - case 0x4016: if (wr) { Joypad::write_strobe(v & 1); break; } // Joypad strobe. - else return Joypad::read_state(0); // Joypad 0. - case 0x4018 ... 0xFFFF: return Cartridge::access<wr>(addr, v); // Cartridge. - } - return 0; -} -inline u8 wr(u16 a, u8 v) { T; return access<1>(a, v); } -inline u8 rd(u16 a) { T; return access<0>(a); } -inline u16 rd16_d(u16 a, u16 b) { return rd(a) | (rd(b) << 8); } // Read from A and B and merge. -inline u16 rd16(u16 a) { return rd16_d(a, a+1); } -inline u8 push(u8 v) { return wr(0x100 + (S--), v); } -inline u8 pop() { return rd(0x100 + (++S)); } -void dma_oam(u8 bank) { for (int i = 0; i < 256; i++) wr(0x2014, rd(bank*0x100 + i)); } - -/* Addressing modes */ -inline u16 imm() { return PC++; } -inline u16 imm16() { PC += 2; return PC - 2; } -inline u16 abs() { return rd16(imm16()); } -inline u16 _abx() { T; return abs() + X; } // Exception. -inline u16 abx() { u16 a = abs(); if (cross(a, X)) T; return a + X; } -inline u16 aby() { u16 a = abs(); if (cross(a, Y)) T; return a + Y; } -inline u16 zp() { return rd(imm()); } -inline u16 zpx() { T; return (zp() + X) % 0x100; } -inline u16 zpy() { T; return (zp() + Y) % 0x100; } -inline u16 izx() { u8 i = zpx(); return rd16_d(i, (i+1) % 0x100); } -inline u16 _izy() { u8 i = zp(); return rd16_d(i, (i+1) % 0x100) + Y; } // Exception. -inline u16 izy() { u16 a = _izy(); if (cross(a-Y, Y)) T; return a; } - -/* STx */ -template<u8& r, Mode m> void st() { wr( m() , r); } -template<> void st<A,izy>() { T; wr(_izy() , A); } // Exceptions. -template<> void st<A,abx>() { T; wr( abs() + X, A); } // ... -template<> void st<A,aby>() { T; wr( abs() + Y, A); } // ... - -#define G u16 a = m(); u8 p = rd(a) /* Fetch parameter */ -template<u8& r, Mode m> void ld() { G; upd_nz(r = p); } // LDx -template<u8& r, Mode m> void cmp() { G; upd_nz(r - p); P[C] = (r >= p); } // CMP, CPx -/* Arithmetic and bitwise */ -template<Mode m> void ADC() { G ; s16 r = A + p + P[C]; upd_cv(A, p, r); upd_nz(A = r); } -template<Mode m> void SBC() { G ^ 0xFF; s16 r = A + p + P[C]; upd_cv(A, p, r); upd_nz(A = r); } -template<Mode m> void BIT() { G; P[Z] = !(A & p); P[N] = p & 0x80; P[V] = p & 0x40; } -template<Mode m> void AND() { G; upd_nz(A &= p); } -template<Mode m> void EOR() { G; upd_nz(A ^= p); } -template<Mode m> void ORA() { G; upd_nz(A |= p); } -/* Read-Modify-Write */ -template<Mode m> void ASL() { G; P[C] = p & 0x80; T; upd_nz(wr(a, p << 1)); } -template<Mode m> void LSR() { G; P[C] = p & 0x01; T; upd_nz(wr(a, p >> 1)); } -template<Mode m> void ROL() { G; u8 c = P[C] ; P[C] = p & 0x80; T; upd_nz(wr(a, (p << 1) | c) ); } -template<Mode m> void ROR() { G; u8 c = P[C] << 7; P[C] = p & 0x01; T; upd_nz(wr(a, c | (p >> 1)) ); } -template<Mode m> void DEC() { G; T; upd_nz(wr(a, --p)); } -template<Mode m> void INC() { G; T; upd_nz(wr(a, ++p)); } -#undef G - -/* DEx, INx */ -template<u8& r> void dec() { upd_nz(--r); T; } -template<u8& r> void inc() { upd_nz(++r); T; } -/* Bit shifting on the accumulator */ -void ASL_A() { P[C] = A & 0x80; upd_nz(A <<= 1); T; } -void LSR_A() { P[C] = A & 0x01; upd_nz(A >>= 1); T; } -void ROL_A() { u8 c = P[C] ; P[C] = A & 0x80; upd_nz(A = ((A << 1) | c) ); T; } -void ROR_A() { u8 c = P[C] << 7; P[C] = A & 0x01; upd_nz(A = (c | (A >> 1)) ); T; } - -/* Txx (move values between registers) */ -template<u8& s, u8& d> void tr() { upd_nz(d = s); T; } -template<> void tr<X,S>() { S = X; T; } // TSX, exception. - -/* Stack operations */ -void PLP() { T; T; P.set(pop()); } -void PHP() { T; push(P.get() | (1 << 4)); } // B flag set. -void PLA() { T; T; A = pop(); upd_nz(A); } -void PHA() { T; push(A); } - -/* Flow control (branches, jumps) */ -template<Flag f, bool v> void br() -{ - s8 j = rd(imm()); - if (P[f] == v) { - if (cross(PC, j)) T; - T; PC += j; - } -} -void JMP_IND() { u16 i = rd16(imm16()); PC = rd16_d(i, (i&0xFF00) | ((i+1) % 0x100)); } -void JMP() { PC = rd16(imm16()); } -void JSR() { u16 t = PC+1; T; push(t >> 8); push(t); PC = rd16(imm16()); } - -/* Return instructions */ -void RTS() { T; T; PC = (pop() | (pop() << 8)) + 1; T; } -void RTI() { PLP(); PC = pop() | (pop() << 8); } - -template<Flag f, bool v> void flag() { P[f] = v; T; } // Clear and set flags. -template<IntType t> void INT() -{ - T; if (t != BRK) T; // BRK already performed the fetch. - if (t != RESET) // Writes on stack are inhibited on RESET. - { - push(PC >> 8); push(PC & 0xFF); - push(P.get() | ((t == BRK) << 4)); // Set B if BRK. - } - else { S -= 3; T; T; T; } - P[I] = true; - /* NMI Reset IRQ BRK */ - constexpr u16 vect[] = { 0xFFFA, 0xFFFC, 0xFFFE, 0xFFFE }; - PC = rd16(vect[t]); - if (t == NMI) nmi = false; -} -void NOP() { T; } - -/* Execute a CPU instruction */ -void exec() -{ - switch (rd(PC++)) // Fetch the opcode. - { - // Select the right function to emulate the instruction: - case 0x00: return INT<BRK>() ; case 0x01: return ORA<izx>() ; - case 0x05: return ORA<zp>() ; case 0x06: return ASL<zp>() ; - case 0x08: return PHP() ; case 0x09: return ORA<imm>() ; - case 0x0A: return ASL_A() ; case 0x0D: return ORA<abs>() ; - case 0x0E: return ASL<abs>() ; case 0x10: return br<N,0>() ; - case 0x11: return ORA<izy>() ; case 0x15: return ORA<zpx>() ; - case 0x16: return ASL<zpx>() ; case 0x18: return flag<C,0>() ; - case 0x19: return ORA<aby>() ; case 0x1D: return ORA<abx>() ; - case 0x1E: return ASL<_abx>() ; case 0x20: return JSR() ; - case 0x21: return AND<izx>() ; case 0x24: return BIT<zp>() ; - case 0x25: return AND<zp>() ; case 0x26: return ROL<zp>() ; - case 0x28: return PLP() ; case 0x29: return AND<imm>() ; - case 0x2A: return ROL_A() ; case 0x2C: return BIT<abs>() ; - case 0x2D: return AND<abs>() ; case 0x2E: return ROL<abs>() ; - case 0x30: return br<N,1>() ; case 0x31: return AND<izy>() ; - case 0x35: return AND<zpx>() ; case 0x36: return ROL<zpx>() ; - case 0x38: return flag<C,1>() ; case 0x39: return AND<aby>() ; - case 0x3D: return AND<abx>() ; case 0x3E: return ROL<_abx>() ; - case 0x40: return RTI() ; case 0x41: return EOR<izx>() ; - case 0x45: return EOR<zp>() ; case 0x46: return LSR<zp>() ; - case 0x48: return PHA() ; case 0x49: return EOR<imm>() ; - case 0x4A: return LSR_A() ; case 0x4C: return JMP() ; - case 0x4D: return EOR<abs>() ; case 0x4E: return LSR<abs>() ; - case 0x50: return br<V,0>() ; case 0x51: return EOR<izy>() ; - case 0x55: return EOR<zpx>() ; case 0x56: return LSR<zpx>() ; - case 0x58: return flag<I,0>() ; case 0x59: return EOR<aby>() ; - case 0x5D: return EOR<abx>() ; case 0x5E: return LSR<_abx>() ; - case 0x60: return RTS() ; case 0x61: return ADC<izx>() ; - case 0x65: return ADC<zp>() ; case 0x66: return ROR<zp>() ; - case 0x68: return PLA() ; case 0x69: return ADC<imm>() ; - case 0x6A: return ROR_A() ; case 0x6C: return JMP_IND() ; - case 0x6D: return ADC<abs>() ; case 0x6E: return ROR<abs>() ; - case 0x70: return br<V,1>() ; case 0x71: return ADC<izy>() ; - case 0x75: return ADC<zpx>() ; case 0x76: return ROR<zpx>() ; - case 0x78: return flag<I,1>() ; case 0x79: return ADC<aby>() ; - case 0x7D: return ADC<abx>() ; case 0x7E: return ROR<_abx>() ; - case 0x81: return st<A,izx>() ; case 0x84: return st<Y,zp>() ; - case 0x85: return st<A,zp>() ; case 0x86: return st<X,zp>() ; - case 0x88: return dec<Y>() ; case 0x8A: return tr<X,A>() ; - case 0x8C: return st<Y,abs>() ; case 0x8D: return st<A,abs>() ; - case 0x8E: return st<X,abs>() ; case 0x90: return br<C,0>() ; - case 0x91: return st<A,izy>() ; case 0x94: return st<Y,zpx>() ; - case 0x95: return st<A,zpx>() ; case 0x96: return st<X,zpy>() ; - case 0x98: return tr<Y,A>() ; case 0x99: return st<A,aby>() ; - case 0x9A: return tr<X,S>() ; case 0x9D: return st<A,abx>() ; - case 0xA0: return ld<Y,imm>() ; case 0xA1: return ld<A,izx>() ; - case 0xA2: return ld<X,imm>() ; case 0xA4: return ld<Y,zp>() ; - case 0xA5: return ld<A,zp>() ; case 0xA6: return ld<X,zp>() ; - case 0xA8: return tr<A,Y>() ; case 0xA9: return ld<A,imm>() ; - case 0xAA: return tr<A,X>() ; case 0xAC: return ld<Y,abs>() ; - case 0xAD: return ld<A,abs>() ; case 0xAE: return ld<X,abs>() ; - case 0xB0: return br<C,1>() ; case 0xB1: return ld<A,izy>() ; - case 0xB4: return ld<Y,zpx>() ; case 0xB5: return ld<A,zpx>() ; - case 0xB6: return ld<X,zpy>() ; case 0xB8: return flag<V,0>() ; - case 0xB9: return ld<A,aby>() ; case 0xBA: return tr<S,X>() ; - case 0xBC: return ld<Y,abx>() ; case 0xBD: return ld<A,abx>() ; - case 0xBE: return ld<X,aby>() ; case 0xC0: return cmp<Y,imm>(); - case 0xC1: return cmp<A,izx>(); case 0xC4: return cmp<Y,zp>() ; - case 0xC5: return cmp<A,zp>() ; case 0xC6: return DEC<zp>() ; - case 0xC8: return inc<Y>() ; case 0xC9: return cmp<A,imm>(); - case 0xCA: return dec<X>() ; case 0xCC: return cmp<Y,abs>(); - case 0xCD: return cmp<A,abs>(); case 0xCE: return DEC<abs>() ; - case 0xD0: return br<Z,0>() ; case 0xD1: return cmp<A,izy>(); - case 0xD5: return cmp<A,zpx>(); case 0xD6: return DEC<zpx>() ; - case 0xD8: return flag<D,0>() ; case 0xD9: return cmp<A,aby>(); - case 0xDD: return cmp<A,abx>(); case 0xDE: return DEC<_abx>() ; - case 0xE0: return cmp<X,imm>(); case 0xE1: return SBC<izx>() ; - case 0xE4: return cmp<X,zp>() ; case 0xE5: return SBC<zp>() ; - case 0xE6: return INC<zp>() ; case 0xE8: return inc<X>() ; - case 0xE9: return SBC<imm>() ; case 0xEA: return NOP() ; - case 0xEC: return cmp<X,abs>(); case 0xED: return SBC<abs>() ; - case 0xEE: return INC<abs>() ; case 0xF0: return br<Z,1>() ; - case 0xF1: return SBC<izy>() ; case 0xF5: return SBC<zpx>() ; - case 0xF6: return INC<zpx>() ; case 0xF8: return flag<D,1>() ; - case 0xF9: return SBC<aby>() ; case 0xFD: return SBC<abx>() ; - case 0xFE: return INC<_abx>() ; - default: - std::cout << "Invalid Opcode! PC: " << PC << " Opcode: 0x" << std::hex << (int)(rd(PC - 1)) << "\n"; - return NOP(); - } -} - -void set_nmi(bool v) { nmi = v; } -void set_irq(bool v) { irq = v; } - -int dmc_read(void*, cpu_addr_t addr) { return access<0>(addr); } - -/* Turn on the CPU */ -void power() -{ - remainingCycles = 0; - - P.set(0x04); - A = X = Y = S = 0x00; - memset(ram, 0xFF, sizeof(ram)); - - nmi = irq = false; - INT<RESET>(); -} - -/* Run the CPU for roughly a frame */ -void run_frame() -{ - remainingCycles += TOTAL_CYCLES; - - while (remainingCycles > 0) - { - if (nmi) INT<NMI>(); - else if (irq and !P[I]) INT<IRQ>(); - - exec(); - } - - APU::run_frame(elapsed()); -} - - -} diff --git a/old/gui.cpp b/old/gui.cpp deleted file mode 100644 index dd0692f..0000000 --- a/old/gui.cpp +++ /dev/null @@ -1,308 +0,0 @@ -#include <csignal> -#include <SDL2/SDL_image.h> -#include <SDL2/SDL_ttf.h> -#include "Sound_Queue.h" -#include "apu.hpp" -#include "cartridge.hpp" -#include "cpu.hpp" -#include "menu.hpp" -#include "gui.hpp" -#include "config.hpp" - -namespace GUI { - -// SDL structures: -SDL_Window* window; -SDL_Renderer* renderer; -SDL_Texture* gameTexture; -SDL_Texture* background; -TTF_Font* font; -u8 const* keys; -Sound_Queue* soundQueue; -SDL_Joystick* joystick[] = { nullptr, nullptr }; - -// Menus: -Menu* menu; -Menu* mainMenu; -Menu* settingsMenu; -Menu* videoMenu; -Menu* keyboardMenu[2]; -Menu* joystickMenu[2]; -FileMenu* fileMenu; - -bool pause = true; - -/* Set the window size multiplier */ -void set_size(int mul) -{ - last_window_size = mul; - SDL_SetWindowSize(window, WIDTH * mul, HEIGHT * mul); - SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); -} - -/* Initialize GUI */ -void init() -{ - // Initialize graphics system: - SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); - TTF_Init(); - - for (int i = 0; i < SDL_NumJoysticks(); i++) - joystick[i] = SDL_JoystickOpen(i); - - APU::init(); - soundQueue = new Sound_Queue; - soundQueue->init(96000); - - // Initialize graphics structures: - window = SDL_CreateWindow ("LaiNES", - SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - WIDTH * last_window_size, HEIGHT * last_window_size, 0); - - renderer = SDL_CreateRenderer(window, -1, - SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); - SDL_RenderSetLogicalSize(renderer, WIDTH, HEIGHT); - - gameTexture = SDL_CreateTexture (renderer, - SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, - WIDTH, HEIGHT); - - font = TTF_OpenFont("res/font.ttf", FONT_SZ); - keys = SDL_GetKeyboardState(0); - - // Initial background: - SDL_Surface* backSurface = IMG_Load("res/init.png"); - background = SDL_CreateTextureFromSurface(renderer, backSurface); - SDL_SetTextureColorMod(background, 60, 60, 60); - SDL_FreeSurface(backSurface); - - // Menus: - mainMenu = new Menu; - mainMenu->add(new Entry("Load ROM", []{ menu = fileMenu; })); - mainMenu->add(new Entry("Settings", []{ menu = settingsMenu; })); - mainMenu->add(new Entry("Exit", []{ exit(0); })); - - settingsMenu = new Menu; - settingsMenu->add(new Entry("<", []{ menu = mainMenu; })); - settingsMenu->add(new Entry("Video", []{ menu = videoMenu; })); - settingsMenu->add(new Entry("Controller 1", []{ menu = useJoystick[0] ? joystickMenu[0] : keyboardMenu[0]; })); - settingsMenu->add(new Entry("Controller 2", []{ menu = useJoystick[1] ? joystickMenu[1] : keyboardMenu[1]; })); - settingsMenu->add(new Entry("Save Settings", []{ save_settings(); menu = mainMenu; })); - - videoMenu = new Menu; - videoMenu->add(new Entry("<", []{ menu = settingsMenu; })); - videoMenu->add(new Entry("Size 1x", []{ set_size(1); })); - videoMenu->add(new Entry("Size 2x", []{ set_size(2); })); - videoMenu->add(new Entry("Size 3x", []{ set_size(3); })); - videoMenu->add(new Entry("Size 4x", []{ set_size(4); })); - - for (int i = 0; i < 2; i++) - { - keyboardMenu[i] = new Menu; - keyboardMenu[i]->add(new Entry("<", []{ menu = settingsMenu; })); - if (joystick[i] != nullptr) - keyboardMenu[i]->add(new Entry("Joystick >", [=]{ menu = joystickMenu[i]; useJoystick[i] = true; })); - keyboardMenu[i]->add(new ControlEntry("Up", &KEY_UP[i])); - keyboardMenu[i]->add(new ControlEntry("Down", &KEY_DOWN[i])); - keyboardMenu[i]->add(new ControlEntry("Left", &KEY_LEFT[i])); - keyboardMenu[i]->add(new ControlEntry("Right", &KEY_RIGHT[i])); - keyboardMenu[i]->add(new ControlEntry("A", &KEY_A[i])); - keyboardMenu[i]->add(new ControlEntry("B", &KEY_B[i])); - keyboardMenu[i]->add(new ControlEntry("Start", &KEY_START[i])); - keyboardMenu[i]->add(new ControlEntry("Select", &KEY_SELECT[i])); - - if (joystick[i] != nullptr) - { - joystickMenu[i] = new Menu; - joystickMenu[i]->add(new Entry("<", []{ menu = settingsMenu; })); - joystickMenu[i]->add(new Entry("< Keyboard", [=]{ menu = keyboardMenu[i]; useJoystick[i] = false; })); - joystickMenu[i]->add(new ControlEntry("Up", &BTN_UP[i])); - joystickMenu[i]->add(new ControlEntry("Down", &BTN_DOWN[i])); - joystickMenu[i]->add(new ControlEntry("Left", &BTN_LEFT[i])); - joystickMenu[i]->add(new ControlEntry("Right", &BTN_RIGHT[i])); - joystickMenu[i]->add(new ControlEntry("A", &BTN_A[i])); - joystickMenu[i]->add(new ControlEntry("B", &BTN_B[i])); - joystickMenu[i]->add(new ControlEntry("Start", &BTN_START[i])); - joystickMenu[i]->add(new ControlEntry("Select", &BTN_SELECT[i])); - } - } - - fileMenu = new FileMenu; - - menu = mainMenu; -} - -/* Render a texture on screen */ -void render_texture(SDL_Texture* texture, int x, int y) -{ - int w, h; - SDL_Rect dest; - - SDL_QueryTexture(texture, NULL, NULL, &dest.w, &dest.h); - if (x == TEXT_CENTER) - dest.x = WIDTH/2 - dest.w/2; - else if (x == TEXT_RIGHT) - dest.x = WIDTH - dest.w - 10; - else - dest.x = x + 10; - dest.y = y + 5; - - SDL_RenderCopy(renderer, texture, NULL, &dest); -} - -/* Generate a texture from text */ -SDL_Texture* gen_text(std::string text, SDL_Color color) -{ - SDL_Surface* surface = TTF_RenderText_Blended(font, text.c_str(), color); - SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface); - - SDL_FreeSurface(surface); - return texture; -} - -/* Get the joypad state from SDL */ -u8 get_joypad_state(int n) -{ - const int DEAD_ZONE = 8000; - - u8 j = 0; - if (useJoystick[n]) - { - j |= (SDL_JoystickGetButton(joystick[n], BTN_A[n])) << 0; // A. - j |= (SDL_JoystickGetButton(joystick[n], BTN_B[n])) << 1; // B. - j |= (SDL_JoystickGetButton(joystick[n], BTN_SELECT[n])) << 2; // Select. - j |= (SDL_JoystickGetButton(joystick[n], BTN_START[n])) << 3; // Start. - - j |= (SDL_JoystickGetButton(joystick[n], BTN_UP[n])) << 4; // Up. - j |= (SDL_JoystickGetAxis(joystick[n], 1) < -DEAD_ZONE) << 4; - j |= (SDL_JoystickGetButton(joystick[n], BTN_DOWN[n])) << 5; // Down. - j |= (SDL_JoystickGetAxis(joystick[n], 1) > DEAD_ZONE) << 5; - j |= (SDL_JoystickGetButton(joystick[n], BTN_LEFT[n])) << 6; // Left. - j |= (SDL_JoystickGetAxis(joystick[n], 0) < -DEAD_ZONE) << 6; - j |= (SDL_JoystickGetButton(joystick[n], BTN_RIGHT[n])) << 7; // Right. - j |= (SDL_JoystickGetAxis(joystick[n], 0) > DEAD_ZONE) << 7; - } - else - { - j |= (keys[KEY_A[n]]) << 0; - j |= (keys[KEY_B[n]]) << 1; - j |= (keys[KEY_SELECT[n]]) << 2; - j |= (keys[KEY_START[n]]) << 3; - j |= (keys[KEY_UP[n]]) << 4; - j |= (keys[KEY_DOWN[n]]) << 5; - j |= (keys[KEY_LEFT[n]]) << 6; - j |= (keys[KEY_RIGHT[n]]) << 7; - } - return j; -} - -/* Send the rendered frame to the GUI */ -void new_frame(u32* pixels) -{ - SDL_UpdateTexture(gameTexture, NULL, pixels, WIDTH * sizeof(u32)); -} - -void new_samples(const blip_sample_t* samples, size_t count) -{ - soundQueue->write(samples, count); -} - -/* Render the screen */ -void render() -{ - SDL_RenderClear(renderer); - - // Draw the NES screen: - if (Cartridge::loaded()) - SDL_RenderCopy(renderer, gameTexture, NULL, NULL); - else - SDL_RenderCopy(renderer, background, NULL, NULL); - - // Draw the menu: - if (pause) menu->render(); - - SDL_RenderPresent(renderer); -} - -/* Play/stop the game */ -void toggle_pause() -{ - pause = not pause; - menu = mainMenu; - - if (pause) - SDL_SetTextureColorMod(gameTexture, 60, 60, 60); - else - SDL_SetTextureColorMod(gameTexture, 255, 255, 255); -} - -/* Prompt for a key, return the scancode */ -SDL_Scancode query_key() -{ - SDL_Texture* prompt = gen_text("Press a key...", { 255, 255, 255 }); - render_texture(prompt, TEXT_CENTER, HEIGHT - FONT_SZ*4); - SDL_RenderPresent(renderer); - - SDL_Event e; - while (true) - { - SDL_PollEvent(&e); - if (e.type == SDL_KEYDOWN) - return e.key.keysym.scancode; - } -} - -int query_button() -{ - SDL_Texture* prompt = gen_text("Press a button...", { 255, 255, 255 }); - render_texture(prompt, TEXT_CENTER, HEIGHT - FONT_SZ*4); - SDL_RenderPresent(renderer); - - SDL_Event e; - while (true) - { - SDL_PollEvent(&e); - if (e.type == SDL_JOYBUTTONDOWN) - return e.jbutton.button; - } -} - -/* Run the emulator */ -void run() -{ - SDL_Event e; - - // Framerate control: - u32 frameStart, frameTime; - const int FPS = 60; - const int DELAY = 1000.0f / FPS; - - while (true) - { - frameStart = SDL_GetTicks(); - - // Handle events: - while (SDL_PollEvent(&e)) - switch (e.type) - { - case SDL_QUIT: return; - case SDL_KEYDOWN: - if (keys[SDL_SCANCODE_ESCAPE] and Cartridge::loaded()) - toggle_pause(); - else if (pause) - menu->update(keys); - } - - if (not pause) CPU::run_frame(); - render(); - - // Wait to mantain framerate: - frameTime = SDL_GetTicks() - frameStart; - if (frameTime < DELAY) - SDL_Delay((int)(DELAY - frameTime)); - } -} - - -} diff --git a/old/include/apu.hpp b/old/include/apu.hpp deleted file mode 100644 index a5d15cb..0000000 --- a/old/include/apu.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "common.hpp" - -namespace APU { - - -template <bool write> u8 access(int elapsed, u16 addr, u8 v = 0); -void run_frame(int elapsed); -void reset(); -void init(); - - -} diff --git a/old/include/cartridge.hpp b/old/include/cartridge.hpp deleted file mode 100644 index c49ec21..0000000 --- a/old/include/cartridge.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include "common.hpp" - -namespace Cartridge { - - -template <bool wr> u8 access(u16 addr, u8 v = 0); -template <bool wr> u8 chr_access(u16 addr, u8 v = 0); -void signal_scanline(); -void load(const char* fileName); -bool loaded(); - - -} diff --git a/old/include/common.hpp b/old/include/common.hpp deleted file mode 100644 index 5d5dc79..0000000 --- a/old/include/common.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include <cstdint> - -#define NTH_BIT(x, n) (((x) >> (n)) & 1) - -/* Integer type shortcuts */ -typedef uint8_t u8; typedef int8_t s8; -typedef uint16_t u16; typedef int16_t s16; -typedef uint32_t u32; typedef int32_t s32; -typedef uint64_t u64; typedef int64_t s64; diff --git a/old/include/config.hpp b/old/include/config.hpp deleted file mode 100644 index 03344df..0000000 --- a/old/include/config.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include <cerrno> -#include <sys/stat.h> -#include <SDL2/SDL.h> - -#define CONFIG_DIR_DEFAULT_MODE S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH -#define USE_CONFIG_DIR true -#define CONFIG_DIR_NAME "LaiNES" -#define CONFIG_FALLBACK ".laines-settings" -/* PATH_MAX is a portability nightmare. */ -#define CONFIG_PATH_MAX 1024 - -namespace GUI { - -/* Loading and saving */ -void load_settings(); -void save_settings(); -const char* get_config_path(char * buf, int buflen); - -extern int last_window_size; -extern SDL_Scancode KEY_A []; -extern SDL_Scancode KEY_B []; -extern SDL_Scancode KEY_SELECT[]; -extern SDL_Scancode KEY_START []; -extern SDL_Scancode KEY_UP []; -extern SDL_Scancode KEY_DOWN []; -extern SDL_Scancode KEY_LEFT []; -extern SDL_Scancode KEY_RIGHT []; -extern int BTN_UP []; -extern int BTN_DOWN []; -extern int BTN_LEFT []; -extern int BTN_RIGHT []; -extern int BTN_A []; -extern int BTN_B []; -extern int BTN_SELECT[]; -extern int BTN_START []; -extern bool useJoystick[]; - -} diff --git a/old/include/cpu.hpp b/old/include/cpu.hpp deleted file mode 100644 index 8b15e19..0000000 --- a/old/include/cpu.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include "common.hpp" -#include <Nes_Apu.h> - -namespace CPU { - - -enum IntType { NMI, RESET, IRQ, BRK }; // Interrupt type. -typedef u16 (*Mode)(void); // Addressing mode. - -/* Processor flags */ -enum Flag {C, Z, I, D, V, N}; -class Flags -{ - bool f[6]; - - public: - bool& operator[] (const int i) { return f[i]; } - - u8 get() { return f[C] | f[Z] << 1 | f[I] << 2 | f[D] << 3 | 1 << 5 | f[V] << 6 | f[N] << 7; } - void set(u8 p) { f[C] = NTH_BIT(p, 0); f[Z] = NTH_BIT(p, 1); f[I] = NTH_BIT(p, 2); - f[D] = NTH_BIT(p, 3); f[V] = NTH_BIT(p, 6); f[N] = NTH_BIT(p, 7); } -}; - -void set_nmi(bool v = true); -void set_irq(bool v = true); -int dmc_read(void*, cpu_addr_t addr); -void power(); -void run_frame(); - - -} diff --git a/old/include/gui.hpp b/old/include/gui.hpp deleted file mode 100644 index 297d34e..0000000 --- a/old/include/gui.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once -#include <SDL2/SDL.h> -#include <string> -#include <Nes_Apu.h> -#include "common.hpp" - -namespace GUI { - -// Screen size: -const unsigned WIDTH = 256; -const unsigned HEIGHT = 240; -const int TEXT_CENTER = -1; -const int TEXT_RIGHT = -2; -const unsigned FONT_SZ = 15; - -void init(); -void toggle_pause(); -SDL_Scancode query_key(); -int query_button(); -void run(); - -SDL_Texture* gen_text(std::string text, SDL_Color color); -void render_texture(SDL_Texture* texture, int x, int y); - -u8 get_joypad_state(int n); -void new_frame(u32* pixels); -void new_samples(const blip_sample_t* samples, size_t count); -void set_size(int mul); - -} diff --git a/old/include/joypad.hpp b/old/include/joypad.hpp deleted file mode 100644 index fac0332..0000000 --- a/old/include/joypad.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include "common.hpp" - -namespace Joypad { - - -u8 read_state(int n); -void write_strobe(bool v); - - -} diff --git a/old/include/mapper.hpp b/old/include/mapper.hpp deleted file mode 100644 index 76f441d..0000000 --- a/old/include/mapper.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once -#include <cstring> -#include "common.hpp" - - -class Mapper -{ - u8* rom; - bool chrRam = false; - - protected: - u32 prgMap[4]; - u32 chrMap[8]; - - u8 *prg, *chr, *prgRam; - u32 prgSize, chrSize, prgRamSize; - - template <int pageKBs> void map_prg(int slot, int bank); - template <int pageKBs> void map_chr(int slot, int bank); - - public: - Mapper(u8* rom); - ~Mapper(); - - u8 read(u16 addr); - virtual u8 write(u16 addr, u8 v) { return v; } - - u8 chr_read(u16 addr); - virtual u8 chr_write(u16 addr, u8 v) { return v; } - - virtual void signal_scanline() {} -}; diff --git a/old/include/mappers/mapper0.hpp b/old/include/mappers/mapper0.hpp deleted file mode 100644 index e5d5950..0000000 --- a/old/include/mappers/mapper0.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once -#include "mapper.hpp" - - -class Mapper0 : public Mapper -{ - public: - Mapper0(u8* rom) : Mapper(rom) - { - map_prg<32>(0, 0); - map_chr<8> (0, 0); - } -}; diff --git a/old/include/mappers/mapper1.hpp b/old/include/mappers/mapper1.hpp deleted file mode 100644 index 756b21b..0000000 --- a/old/include/mappers/mapper1.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once -#include "mapper.hpp" - - -class Mapper1 : public Mapper -{ - int writeN; - u8 tmpReg; - u8 regs[4]; - - void apply(); - - public: - Mapper1(u8* rom) : Mapper(rom) - { - regs[0] = 0x0C; - writeN = tmpReg = regs[1] = regs[2] = regs[3] = 0; - apply(); - } - - u8 write(u16 addr, u8 v); - u8 chr_write(u16 addr, u8 v); -}; diff --git a/old/include/mappers/mapper2.hpp b/old/include/mappers/mapper2.hpp deleted file mode 100644 index 0deea6e..0000000 --- a/old/include/mappers/mapper2.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once -#include "mapper.hpp" - - -class Mapper2 : public Mapper -{ - u8 regs[1]; - bool vertical_mirroring; - - void apply(); - - public: - Mapper2(u8* rom) : Mapper(rom) - { - regs[0] = 0; - vertical_mirroring = rom[6] & 0x01; - apply(); - } - - u8 write(u16 addr, u8 v); - u8 chr_write(u16 addr, u8 v); -}; diff --git a/old/include/mappers/mapper3.hpp b/old/include/mappers/mapper3.hpp deleted file mode 100644 index b159549..0000000 --- a/old/include/mappers/mapper3.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "mapper.hpp" - - -class Mapper3 : public Mapper -{ - u8 regs[1]; - bool vertical_mirroring; - bool PRG_size_16k; - void apply(); - - public: - Mapper3(u8* rom) : Mapper(rom) - { - PRG_size_16k = rom[4] == 1; - vertical_mirroring = rom[6] & 0x01; - regs[0] = 0; - apply(); - } - - u8 write(u16 addr, u8 v); - u8 chr_write(u16 addr, u8 v); -}; - diff --git a/old/include/mappers/mapper4.hpp b/old/include/mappers/mapper4.hpp deleted file mode 100644 index 5cb605c..0000000 --- a/old/include/mappers/mapper4.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once -#include "mapper.hpp" - - -class Mapper4 : public Mapper -{ - u8 reg8000; - u8 regs[8]; - bool horizMirroring; - - u8 irqPeriod; - u8 irqCounter; - bool irqEnabled; - - void apply(); - - public: - Mapper4(u8* rom) : Mapper(rom) - { - for (int i = 0; i < 8; i++) - regs[i] = 0; - - horizMirroring = true; - irqEnabled = false; - irqPeriod = irqCounter = 0; - - map_prg<8>(3, -1); - apply(); - } - - u8 write(u16 addr, u8 v); - u8 chr_write(u16 addr, u8 v); - - void signal_scanline(); -}; diff --git a/old/include/menu.hpp b/old/include/menu.hpp deleted file mode 100644 index 612ce91..0000000 --- a/old/include/menu.hpp +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once -#include <functional> -#include <SDL2/SDL.h> -#include <string> -#include <vector> -#include "gui.hpp" - -namespace GUI { - - -class Entry -{ - std::string label; - std::function<void()> callback; - - bool selected = false; - SDL_Texture* whiteTexture = nullptr; - SDL_Texture* redTexture = nullptr; - - public: - Entry(std::string label, std::function<void()> callback = []{}); - ~Entry(); - - void set_label(std::string label); - inline std::string& get_label() { return label; } - - virtual void select() { selected = true; }; - virtual void unselect() { selected = false; }; - void trigger() { callback(); }; - virtual void render(int x, int y); -}; - -class ControlEntry : public Entry -{ - SDL_Scancode* key; - int* button; - Entry* keyEntry; - - public: - ControlEntry(std::string action, SDL_Scancode* key); - ControlEntry(std::string action, int* button); - void select() { Entry::select(); keyEntry->select(); } - void unselect() { Entry::unselect(); keyEntry->unselect(); } - void render(int x, int y) { Entry::render(x, y); keyEntry->render(TEXT_RIGHT, y); } -}; - -class Menu -{ - const int MAX_ENTRY = GUI::HEIGHT / FONT_SZ - 1; - int cursor = 0; - int top = 0; - int bottom = MAX_ENTRY; - - public: - std::vector<Entry*> entries; - - void add(Entry* entry); - void clear(); - void sort_by_label(); - void update(u8 const* keys); - void render(); -}; - -class FileMenu : public Menu -{ - void change_dir(std::string dir); - - public: - FileMenu(); -}; - - -} diff --git a/old/include/ppu.hpp b/old/include/ppu.hpp deleted file mode 100644 index e3f5e14..0000000 --- a/old/include/ppu.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once -#include "common.hpp" - -namespace PPU { - - -enum Scanline { VISIBLE, POST, NMI, PRE }; -enum Mirroring { VERTICAL, HORIZONTAL }; - -/* Sprite buffer */ -struct Sprite -{ - u8 id; // Index in OAM. - u8 x; // X position. - u8 y; // Y position. - u8 tile; // Tile index. - u8 attr; // Attributes. - u8 dataL; // Tile data (low). - u8 dataH; // Tile data (high). -}; - -/* PPUCTRL ($2000) register */ -union Ctrl -{ - struct - { - unsigned nt : 2; // Nametable ($2000 / $2400 / $2800 / $2C00). - unsigned incr : 1; // Address increment (1 / 32). - unsigned sprTbl : 1; // Sprite pattern table ($0000 / $1000). - unsigned bgTbl : 1; // BG pattern table ($0000 / $1000). - unsigned sprSz : 1; // Sprite size (8x8 / 8x16). - unsigned slave : 1; // PPU master/slave. - unsigned nmi : 1; // Enable NMI. - }; - u8 r; -}; - -/* PPUMASK ($2001) register */ -union Mask -{ - struct - { - unsigned gray : 1; // Grayscale. - unsigned bgLeft : 1; // Show background in leftmost 8 pixels. - unsigned sprLeft : 1; // Show sprite in leftmost 8 pixels. - unsigned bg : 1; // Show background. - unsigned spr : 1; // Show sprites. - unsigned red : 1; // Intensify reds. - unsigned green : 1; // Intensify greens. - unsigned blue : 1; // Intensify blues. - }; - u8 r; -}; - -/* PPUSTATUS ($2002) register */ -union Status -{ - struct - { - unsigned bus : 5; // Not significant. - unsigned sprOvf : 1; // Sprite overflow. - unsigned sprHit : 1; // Sprite 0 Hit. - unsigned vBlank : 1; // In VBlank? - }; - u8 r; -}; - -/* Loopy's VRAM address */ -union Addr -{ - struct - { - unsigned cX : 5; // Coarse X. - unsigned cY : 5; // Coarse Y. - unsigned nt : 2; // Nametable. - unsigned fY : 3; // Fine Y. - }; - struct - { - unsigned l : 8; - unsigned h : 7; - }; - unsigned addr : 14; - unsigned r : 15; -}; - -template <bool write> u8 access(u16 index, u8 v = 0); -void set_mirroring(Mirroring mode); -void step(); -void reset(); - - -} diff --git a/old/joypad.cpp b/old/joypad.cpp deleted file mode 100644 index 35ae822..0000000 --- a/old/joypad.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "gui.hpp" - -namespace Joypad { - - -u8 joypad_bits[2]; // Joypad shift registers. -bool strobe; // Joypad strobe latch. - -/* Read joypad state (NES register format) */ -u8 read_state(int n) -{ - // When strobe is high, it keeps reading A: - if (strobe) - return 0x40 | (GUI::get_joypad_state(n) & 1); - - // Get the status of a button and shift the register: - u8 j = 0x40 | (joypad_bits[n] & 1); - joypad_bits[n] = 0x80 | (joypad_bits[n] >> 1); - return j; -} - -void write_strobe(bool v) -{ - // Read the joypad data on strobe's transition 1 -> 0. - if (strobe and !v) - for (int i = 0; i < 2; i++) - joypad_bits[i] = GUI::get_joypad_state(i); - - strobe = v; -} - - -} diff --git a/old/main.cpp b/old/main.cpp deleted file mode 100644 index 787b006..0000000 --- a/old/main.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "gui.hpp" -#include "config.hpp" - -int main(int argc, char *argv[]) -{ - GUI::load_settings(); - GUI::init(); - GUI::run(); - - return 0; -} diff --git a/old/mapper.cpp b/old/mapper.cpp deleted file mode 100644 index e7a2628..0000000 --- a/old/mapper.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "ppu.hpp" -#include "mapper.hpp" - - -Mapper::Mapper(u8* rom) : rom(rom) -{ - // Read infos from header: - prgSize = rom[4] * 0x4000; - chrSize = rom[5] * 0x2000; - prgRamSize = rom[8] ? rom[8] * 0x2000 : 0x2000; - set_mirroring((rom[6] & 1) ? PPU::VERTICAL : PPU::HORIZONTAL); - - this->prg = rom + 16; - this->prgRam = new u8[prgRamSize]; - - // CHR ROM: - if (chrSize) - this->chr = rom + 16 + prgSize; - // CHR RAM: - else - { - chrRam = true; - chrSize = 0x2000; - this->chr = new u8[chrSize]; - } -} - -Mapper::~Mapper() -{ - delete rom; - delete prgRam; - if (chrRam) - delete chr; -} - -/* Access to memory */ -u8 Mapper::read(u16 addr) -{ - if (addr >= 0x8000) - return prg[prgMap[(addr - 0x8000) / 0x2000] + ((addr - 0x8000) % 0x2000)]; - else - return prgRam[addr - 0x6000]; -} - -u8 Mapper::chr_read(u16 addr) -{ - return chr[chrMap[addr / 0x400] + (addr % 0x400)]; -} - -/* PRG mapping functions */ -template <int pageKBs> void Mapper::map_prg(int slot, int bank) -{ - if (bank < 0) - bank = (prgSize / (0x400*pageKBs)) + bank; - - for (int i = 0; i < (pageKBs/8); i++) - prgMap[(pageKBs/8) * slot + i] = (pageKBs*0x400*bank + 0x2000*i) % prgSize; -} -template void Mapper::map_prg<32>(int, int); -template void Mapper::map_prg<16>(int, int); -template void Mapper::map_prg<8> (int, int); - -/* CHR mapping functions */ -template <int pageKBs> void Mapper::map_chr(int slot, int bank) -{ - for (int i = 0; i < pageKBs; i++) - chrMap[pageKBs*slot + i] = (pageKBs*0x400*bank + 0x400*i) % chrSize; -} -template void Mapper::map_chr<8>(int, int); -template void Mapper::map_chr<4>(int, int); -template void Mapper::map_chr<2>(int, int); -template void Mapper::map_chr<1>(int, int); diff --git a/old/mappers/mapper1.cpp b/old/mappers/mapper1.cpp deleted file mode 100644 index 827962d..0000000 --- a/old/mappers/mapper1.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include "ppu.hpp" -#include "mappers/mapper1.hpp" - - -/* Apply the registers state */ -void Mapper1::apply() -{ - // 16KB PRG: - if (regs[0] & 0b1000) - { - // 0x8000 swappable, 0xC000 fixed to bank 0x0F: - if (regs[0] & 0b100) - { - map_prg<16>(0, regs[3] & 0xF); - map_prg<16>(1, 0xF); - } - // 0x8000 fixed to bank 0x00, 0xC000 swappable: - else - { - map_prg<16>(0, 0); - map_prg<16>(1, regs[3] & 0xF); - } - } - // 32KB PRG: - else - map_prg<32>(0, (regs[3] & 0xF) >> 1); - - // 4KB CHR: - if (regs[0] & 0b10000) - { - map_chr<4>(0, regs[1]); - map_chr<4>(1, regs[2]); - } - // 8KB CHR: - else - map_chr<8>(0, regs[1] >> 1); - - // Set mirroring: - switch (regs[0] & 0b11) - { - case 2: set_mirroring(PPU::VERTICAL); break; - case 3: set_mirroring(PPU::HORIZONTAL); break; - } -} - -u8 Mapper1::write(u16 addr, u8 v) -{ - // PRG RAM write; - if (addr < 0x8000) - prgRam[addr - 0x6000] = v; - // Mapper register write: - else if (addr & 0x8000) - { - // Reset: - if (v & 0x80) - { - writeN = 0; - tmpReg = 0; - regs[0] |= 0x0C; - apply(); - } - else - { - // Write a bit into the temporary register: - tmpReg = ((v & 1) << 4) | (tmpReg >> 1); - // Finished writing all the bits: - if (++writeN == 5) - { - regs[(addr >> 13) & 0b11] = tmpReg; - writeN = 0; - tmpReg = 0; - apply(); - } - } - } - return v; -} - -u8 Mapper1::chr_write(u16 addr, u8 v) -{ - return chr[addr] = v; -} diff --git a/old/mappers/mapper2.cpp b/old/mappers/mapper2.cpp deleted file mode 100644 index 818cd80..0000000 --- a/old/mappers/mapper2.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "ppu.hpp" -#include "mappers/mapper2.hpp" - -/* Based off of https://wiki.nesdev.com/w/index.php/UxROM */ - -/* Apply the registers state */ -void Mapper2::apply() -{ - /* - * 16 kb PRG ROM Banks - * 0x8000 - 0xBFFF swappable - * 0xC000 - 0xFFFF fixed - */ - map_prg<16>(0, regs[0] & 0xF); - map_prg<16>(1, 0xF); - - /* 8k of CHR */ - map_chr<8>(0, 0); - - /* mirroring is based on the header (soldered) */ - set_mirroring(vertical_mirroring?PPU::VERTICAL:PPU::HORIZONTAL); -} - -u8 Mapper2::write(u16 addr, u8 v) -{ - /* check for bus contingency? (addr & 0x8000 == v?) nah */ - - /* bank switching */ - if (addr & 0x8000) - { - regs[0] = v; - apply(); - } - return v; -} - -u8 Mapper2::chr_write(u16 addr, u8 v) -{ - return chr[addr] = v; -} diff --git a/old/mappers/mapper3.cpp b/old/mappers/mapper3.cpp deleted file mode 100644 index 429a6a8..0000000 --- a/old/mappers/mapper3.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "ppu.hpp" -#include "mappers/mapper3.hpp" - -/* Based off of https://wiki.nesdev.com/w/index.php/INES_Mapper_003 */ - -/* Apply the registers state */ -void Mapper3::apply() -{ - if (PRG_size_16k) - { - /* - * mirror the bottom on the top - * 0x8000 - 0xBFFF == - * 0xC000 - 0xFFFF - */ - map_prg<16>(0,0); - map_prg<16>(1,0); - } - else - { - /* no mirroring */ - map_prg<16>(0,0); - map_prg<16>(1,1); - } - - /* 8k bankswitched CHR */ - map_chr<8>(0, regs[0] & 0b11); - - /* mirroring is based on the header (soldered) */ - set_mirroring(vertical_mirroring?PPU::VERTICAL:PPU::HORIZONTAL); -} - -u8 Mapper3::write(u16 addr, u8 v) -{ - /* check for bus contingency? */ - - /* chr bank switching */ - if (addr & 0x8000) - { - regs[0] = v; - apply(); - } - return v; -} - -u8 Mapper3::chr_write(u16 addr, u8 v) -{ - return chr[addr] = v; -} - diff --git a/old/mappers/mapper4.cpp b/old/mappers/mapper4.cpp deleted file mode 100644 index 3d40228..0000000 --- a/old/mappers/mapper4.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "cpu.hpp" -#include "ppu.hpp" -#include "mappers/mapper4.hpp" - - -void Mapper4::apply() -{ - map_prg<8>(1, regs[7]); - - // PRG Mode 0: - if (!(reg8000 & (1 << 6))) - { - map_prg<8>(0, regs[6]); - map_prg<8>(2, -2); - } - // PRG Mode 1: - else - { - map_prg<8>(0, -2); - map_prg<8>(2, regs[6]); - } - - // CHR Mode 0: - if (!(reg8000 & (1 << 7))) - { - map_chr<2>(0, regs[0] >> 1); - map_chr<2>(1, regs[1] >> 1); - for (int i = 0; i < 4; i++) - map_chr<1>(4 + i, regs[2 + i]); - } - // CHR Mode 1: - else - { - for (int i = 0; i < 4; i++) - map_chr<1>(i, regs[2 + i]); - map_chr<2>(2, regs[0] >> 1); - map_chr<2>(3, regs[1] >> 1); - } - - set_mirroring(horizMirroring ? PPU::HORIZONTAL : PPU::VERTICAL); -} - -u8 Mapper4::write(u16 addr, u8 v) -{ - if (addr < 0x8000) - prgRam[addr - 0x6000] = v; - else if (addr & 0x8000) - { - switch (addr & 0xE001) - { - case 0x8000: reg8000 = v; break; - case 0x8001: regs[reg8000 & 0b111] = v; break; - case 0xA000: horizMirroring = v & 1; break; - case 0xC000: irqPeriod = v; break; - case 0xC001: irqCounter = 0; break; - case 0xE000: CPU::set_irq(irqEnabled = false); break; - case 0xE001: irqEnabled = true; break; - } - apply(); - } - return v; -} - -u8 Mapper4::chr_write(u16 addr, u8 v) -{ - return chr[addr] = v; -} - -void Mapper4::signal_scanline() -{ - if (irqCounter == 0) - irqCounter = irqPeriod; - else - irqCounter--; - - if (irqEnabled and irqCounter == 0) - CPU::set_irq(); -} diff --git a/old/menu.cpp b/old/menu.cpp deleted file mode 100644 index 7882ea0..0000000 --- a/old/menu.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include <algorithm> -#include <dirent.h> -#include <unistd.h> -#include "cartridge.hpp" -#include "menu.hpp" - -namespace GUI { - -using namespace std; - - -Entry::Entry(string label, function<void()> callback) : callback(callback) -{ - set_label(label); -} - -Entry::~Entry() -{ - SDL_DestroyTexture(whiteTexture); - SDL_DestroyTexture(redTexture); -} - -void Entry::set_label(string label) -{ - this->label = label; - - if (whiteTexture != nullptr) SDL_DestroyTexture(whiteTexture); - if (redTexture != nullptr) SDL_DestroyTexture(redTexture); - - whiteTexture = gen_text(label, { 255, 255, 255 }); - redTexture = gen_text(label, { 255, 0, 0 }); -} - -void Entry::render(int x, int y) { - render_texture(selected ? redTexture : whiteTexture, x, y); -} - -ControlEntry::ControlEntry(string action, SDL_Scancode* key) : key(key), - Entry::Entry( - action, - [&]{ keyEntry->set_label(SDL_GetScancodeName(*(this->key) = query_key())); }) -{ - this->keyEntry = new Entry(SDL_GetScancodeName(*key), []{}); -} - -ControlEntry::ControlEntry(string action, int* button) : button(button), - Entry::Entry( - action, - [&]{ keyEntry->set_label(to_string(*(this->button) = query_button())); }) -{ - this->keyEntry = new Entry(to_string(*button), []{}); -} - - -void Menu::add(Entry* entry) -{ - if (entries.empty()) - entry->select(); - entries.push_back(entry); -} - -void Menu::clear() -{ - for (auto entry : entries) - delete entry; - entries.clear(); - cursor = 0; -} - -void Menu::sort_by_label() -{ - if (entries.empty()) - return; - entries[0]->unselect(); - sort(entries.begin(), entries.end(), [](Entry* a, Entry* b) { - return a->get_label() < b->get_label(); - }); - entries[0]->select(); -} - -void Menu::update(u8 const* keys) -{ - int oldCursor = cursor; - - if (keys[SDL_SCANCODE_DOWN] and cursor < entries.size() - 1) - { - cursor++; - if (cursor == bottom) { - bottom += 1; - top += 1; - } - } - else if (keys[SDL_SCANCODE_UP] and cursor > 0) - { - cursor--; - if (cursor < top) { - top -= 1; - bottom -= 1; - } - } - entries[oldCursor]->unselect(); - entries[cursor]->select(); - - if (keys[SDL_SCANCODE_RETURN]) - entries[cursor]->trigger(); -} - -void Menu::render() -{ - for (int i = top; i < entries.size() && i < bottom; ++i) - { - int y = (i - top) * FONT_SZ; - entries[i]->render(TEXT_CENTER, y); - } -} - -void FileMenu::change_dir(string dir) -{ - clear(); - - struct dirent* dirp; - DIR* dp = opendir(dir.c_str()); - - while ((dirp = readdir(dp)) != NULL) - { - string name = dirp->d_name; - string path = dir + "/" + name; - - if (name[0] == '.' and name != "..") continue; - - if (dirp->d_type == DT_DIR) - add(new Entry(name + "/", - [=]{ change_dir(path); })); - - else if (name.size() > 4 and name.substr(name.size() - 4) == ".nes") - add(new Entry(name, - [=]{ Cartridge::load(path.c_str()); toggle_pause(); })); - } - closedir(dp); - sort_by_label(); -} - -FileMenu::FileMenu() -{ - char cwd[512]; - - change_dir(getcwd(cwd, 512)); -} - - -} diff --git a/old/palette.inc b/old/palette.inc deleted file mode 100644 index b869d86..0000000 --- a/old/palette.inc +++ /dev/null @@ -1,9 +0,0 @@ -u32 nesRgb[] = -{ 0x7C7C7C, 0x0000FC, 0x0000BC, 0x4428BC, 0x940084, 0xA80020, 0xA81000, 0x881400, - 0x503000, 0x007800, 0x006800, 0x005800, 0x004058, 0x000000, 0x000000, 0x000000, - 0xBCBCBC, 0x0078F8, 0x0058F8, 0x6844FC, 0xD800CC, 0xE40058, 0xF83800, 0xE45C10, - 0xAC7C00, 0x00B800, 0x00A800, 0x00A844, 0x008888, 0x000000, 0x000000, 0x000000, - 0xF8F8F8, 0x3CBCFC, 0x6888FC, 0x9878F8, 0xF878F8, 0xF85898, 0xF87858, 0xFCA044, - 0xF8B800, 0xB8F818, 0x58D854, 0x58F898, 0x00E8D8, 0x787878, 0x000000, 0x000000, - 0xFCFCFC, 0xA4E4FC, 0xB8B8F8, 0xD8B8F8, 0xF8B8F8, 0xF8A4C0, 0xF0D0B0, 0xFCE0A8, - 0xF8D878, 0xD8F878, 0xB8F8B8, 0xB8F8D8, 0x00FCFC, 0xF8D8F8, 0x000000, 0x000000 }; diff --git a/old/ppu.cpp b/old/ppu.cpp deleted file mode 100644 index 4ec4ec5..0000000 --- a/old/ppu.cpp +++ /dev/null @@ -1,353 +0,0 @@ -#include "cartridge.hpp" -#include "cpu.hpp" -#include "gui.hpp" -#include "ppu.hpp" - -namespace PPU { -#include "palette.inc" - - -Mirroring mirroring; // Mirroring mode. -u8 ciRam[0x800]; // VRAM for nametables. -u8 cgRam[0x20]; // VRAM for palettes. -u8 oamMem[0x100]; // VRAM for sprite properties. -Sprite oam[8], secOam[8]; // Sprite buffers. -u32 pixels[256 * 240]; // Video buffer. - -Addr vAddr, tAddr; // Loopy V, T. -u8 fX; // Fine X. -u8 oamAddr; // OAM address. - -Ctrl ctrl; // PPUCTRL ($2000) register. -Mask mask; // PPUMASK ($2001) register. -Status status; // PPUSTATUS ($2002) register. - -// Background latches: -u8 nt, at, bgL, bgH; -// Background shift registers: -u8 atShiftL, atShiftH; u16 bgShiftL, bgShiftH; -bool atLatchL, atLatchH; - -// Rendering counters: -int scanline, dot; -bool frameOdd; - -inline bool rendering() { return mask.bg || mask.spr; } -inline int spr_height() { return ctrl.sprSz ? 16 : 8; } - -/* Get CIRAM address according to mirroring */ -u16 nt_mirror(u16 addr) -{ - switch (mirroring) - { - case VERTICAL: return addr % 0x800; - case HORIZONTAL: return ((addr / 2) & 0x400) + (addr % 0x400); - default: return addr - 0x2000; - } -} -void set_mirroring(Mirroring mode) { mirroring = mode; } - -/* Access PPU memory */ -u8 rd(u16 addr) -{ - switch (addr) - { - case 0x0000 ... 0x1FFF: return Cartridge::chr_access<0>(addr); // CHR-ROM/RAM. - case 0x2000 ... 0x3EFF: return ciRam[nt_mirror(addr)]; // Nametables. - case 0x3F00 ... 0x3FFF: // Palettes: - if ((addr & 0x13) == 0x10) addr &= ~0x10; - return cgRam[addr & 0x1F] & (mask.gray ? 0x30 : 0xFF); - default: return 0; - } -} -void wr(u16 addr, u8 v) -{ - switch (addr) - { - case 0x0000 ... 0x1FFF: Cartridge::chr_access<1>(addr, v); break; // CHR-ROM/RAM. - case 0x2000 ... 0x3EFF: ciRam[nt_mirror(addr)] = v; break; // Nametables. - case 0x3F00 ... 0x3FFF: // Palettes: - if ((addr & 0x13) == 0x10) addr &= ~0x10; - cgRam[addr & 0x1F] = v; break; - } -} - -/* Access PPU through registers. */ -template <bool write> u8 access(u16 index, u8 v) -{ - static u8 res; // Result of the operation. - static u8 buffer; // VRAM read buffer. - static bool latch; // Detect second reading. - - /* Write into register */ - if (write) - { - res = v; - - switch (index) - { - case 0: ctrl.r = v; tAddr.nt = ctrl.nt; break; // PPUCTRL ($2000). - case 1: mask.r = v; break; // PPUMASK ($2001). - case 3: oamAddr = v; break; // OAMADDR ($2003). - case 4: oamMem[oamAddr++] = v; break; // OAMDATA ($2004). - case 5: // PPUSCROLL ($2005). - if (!latch) { fX = v & 7; tAddr.cX = v >> 3; } // First write. - else { tAddr.fY = v & 7; tAddr.cY = v >> 3; } // Second write. - latch = !latch; break; - case 6: // PPUADDR ($2006). - if (!latch) { tAddr.h = v & 0x3F; } // First write. - else { tAddr.l = v; vAddr.r = tAddr.r; } // Second write. - latch = !latch; break; - case 7: wr(vAddr.addr, v); vAddr.addr += ctrl.incr ? 32 : 1; // PPUDATA ($2007). - } - } - /* Read from register */ - else - switch (index) - { - // PPUSTATUS ($2002): - case 2: res = (res & 0x1F) | status.r; status.vBlank = 0; latch = 0; break; - case 4: res = oamMem[oamAddr]; break; // OAMDATA ($2004). - case 7: // PPUDATA ($2007). - if (vAddr.addr <= 0x3EFF) - { - res = buffer; - buffer = rd(vAddr.addr); - } - else - res = buffer = rd(vAddr.addr); - vAddr.addr += ctrl.incr ? 32 : 1; - } - return res; -} -template u8 access<0>(u16, u8); template u8 access<1>(u16, u8); - -/* Calculate graphics addresses */ -inline u16 nt_addr() { return 0x2000 | (vAddr.r & 0xFFF); } -inline u16 at_addr() { return 0x23C0 | (vAddr.nt << 10) | ((vAddr.cY / 4) << 3) | (vAddr.cX / 4); } -inline u16 bg_addr() { return (ctrl.bgTbl * 0x1000) + (nt * 16) + vAddr.fY; } -/* Increment the scroll by one pixel */ -inline void h_scroll() { if (!rendering()) return; if (vAddr.cX == 31) vAddr.r ^= 0x41F; else vAddr.cX++; } -inline void v_scroll() -{ - if (!rendering()) return; - if (vAddr.fY < 7) vAddr.fY++; - else - { - vAddr.fY = 0; - if (vAddr.cY == 31) vAddr.cY = 0; - else if (vAddr.cY == 29) { vAddr.cY = 0; vAddr.nt ^= 0b10; } - else vAddr.cY++; - } -} -/* Copy scrolling data from loopy T to loopy V */ -inline void h_update() { if (!rendering()) return; vAddr.r = (vAddr.r & ~0x041F) | (tAddr.r & 0x041F); } -inline void v_update() { if (!rendering()) return; vAddr.r = (vAddr.r & ~0x7BE0) | (tAddr.r & 0x7BE0); } -/* Put new data into the shift registers */ -inline void reload_shift() -{ - bgShiftL = (bgShiftL & 0xFF00) | bgL; - bgShiftH = (bgShiftH & 0xFF00) | bgH; - - atLatchL = (at & 1); - atLatchH = (at & 2); -} - -/* Clear secondary OAM */ -void clear_oam() -{ - for (int i = 0; i < 8; i++) - { - secOam[i].id = 64; - secOam[i].y = 0xFF; - secOam[i].tile = 0xFF; - secOam[i].attr = 0xFF; - secOam[i].x = 0xFF; - secOam[i].dataL = 0; - secOam[i].dataH = 0; - } -} - -/* Fill secondary OAM with the sprite infos for the next scanline */ -void eval_sprites() -{ - int n = 0; - for (int i = 0; i < 64; i++) - { - int line = (scanline == 261 ? -1 : scanline) - oamMem[i*4 + 0]; - // If the sprite is in the scanline, copy its properties into secondary OAM: - if (line >= 0 and line < spr_height()) - { - secOam[n].id = i; - secOam[n].y = oamMem[i*4 + 0]; - secOam[n].tile = oamMem[i*4 + 1]; - secOam[n].attr = oamMem[i*4 + 2]; - secOam[n].x = oamMem[i*4 + 3]; - - if (++n >= 8) - { - status.sprOvf = true; - break; - } - } - } -} - -/* Load the sprite info into primary OAM and fetch their tile data. */ -void load_sprites() -{ - u16 addr; - for (int i = 0; i < 8; i++) - { - oam[i] = secOam[i]; // Copy secondary OAM into primary. - - // Different address modes depending on the sprite height: - if (spr_height() == 16) - addr = ((oam[i].tile & 1) * 0x1000) + ((oam[i].tile & ~1) * 16); - else - addr = ( ctrl.sprTbl * 0x1000) + ( oam[i].tile * 16); - - unsigned sprY = (scanline - oam[i].y) % spr_height(); // Line inside the sprite. - if (oam[i].attr & 0x80) sprY ^= spr_height() - 1; // Vertical flip. - addr += sprY + (sprY & 8); // Select the second tile if on 8x16. - - oam[i].dataL = rd(addr + 0); - oam[i].dataH = rd(addr + 8); - } -} - -/* Process a pixel, draw it if it's on screen */ -void pixel() -{ - u8 palette = 0, objPalette = 0; - bool objPriority = 0; - int x = dot - 2; - - if (scanline < 240 and x >= 0 and x < 256) - { - if (mask.bg and not (!mask.bgLeft && x < 8)) - { - // Background: - palette = (NTH_BIT(bgShiftH, 15 - fX) << 1) | - NTH_BIT(bgShiftL, 15 - fX); - if (palette) - palette |= ((NTH_BIT(atShiftH, 7 - fX) << 1) | - NTH_BIT(atShiftL, 7 - fX)) << 2; - } - // Sprites: - if (mask.spr and not (!mask.sprLeft && x < 8)) - for (int i = 7; i >= 0; i--) - { - if (oam[i].id == 64) continue; // Void entry. - unsigned sprX = x - oam[i].x; - if (sprX >= 8) continue; // Not in range. - if (oam[i].attr & 0x40) sprX ^= 7; // Horizontal flip. - - u8 sprPalette = (NTH_BIT(oam[i].dataH, 7 - sprX) << 1) | - NTH_BIT(oam[i].dataL, 7 - sprX); - if (sprPalette == 0) continue; // Transparent pixel. - - if (oam[i].id == 0 && palette && x != 255) status.sprHit = true; - sprPalette |= (oam[i].attr & 3) << 2; - objPalette = sprPalette + 16; - objPriority = oam[i].attr & 0x20; - } - // Evaluate priority: - if (objPalette && (palette == 0 || objPriority == 0)) palette = objPalette; - - pixels[scanline*256 + x] = nesRgb[rd(0x3F00 + (rendering() ? palette : 0))]; - } - // Perform background shifts: - bgShiftL <<= 1; bgShiftH <<= 1; - atShiftL = (atShiftL << 1) | atLatchL; - atShiftH = (atShiftH << 1) | atLatchH; -} - -/* Execute a cycle of a scanline */ -template<Scanline s> void scanline_cycle() -{ - static u16 addr; - - if (s == NMI and dot == 1) { status.vBlank = true; if (ctrl.nmi) CPU::set_nmi(); } - else if (s == POST and dot == 0) GUI::new_frame(pixels); - else if (s == VISIBLE or s == PRE) - { - // Sprites: - switch (dot) - { - case 1: clear_oam(); if (s == PRE) { status.sprOvf = status.sprHit = false; } break; - case 257: eval_sprites(); break; - case 321: load_sprites(); break; - } - // Background: - switch (dot) - { - case 2 ... 255: case 322 ... 337: - pixel(); - switch (dot % 8) - { - // Nametable: - case 1: addr = nt_addr(); reload_shift(); break; - case 2: nt = rd(addr); break; - // Attribute: - case 3: addr = at_addr(); break; - case 4: at = rd(addr); if (vAddr.cY & 2) at >>= 4; - if (vAddr.cX & 2) at >>= 2; break; - // Background (low bits): - case 5: addr = bg_addr(); break; - case 6: bgL = rd(addr); break; - // Background (high bits): - case 7: addr += 8; break; - case 0: bgH = rd(addr); h_scroll(); break; - } break; - case 256: pixel(); bgH = rd(addr); v_scroll(); break; // Vertical bump. - case 257: pixel(); reload_shift(); h_update(); break; // Update horizontal position. - case 280 ... 304: if (s == PRE) v_update(); break; // Update vertical position. - - // No shift reloading: - case 1: addr = nt_addr(); if (s == PRE) status.vBlank = false; break; - case 321: case 339: addr = nt_addr(); break; - // Nametable fetch instead of attribute: - case 338: nt = rd(addr); break; - case 340: nt = rd(addr); if (s == PRE && rendering() && frameOdd) dot++; - } - // Signal scanline to mapper: - if (dot == 260 && rendering()) Cartridge::signal_scanline(); - } -} - -/* Execute a PPU cycle. */ -void step() -{ - switch (scanline) - { - case 0 ... 239: scanline_cycle<VISIBLE>(); break; - case 240: scanline_cycle<POST>(); break; - case 241: scanline_cycle<NMI>(); break; - case 261: scanline_cycle<PRE>(); break; - } - // Update dot and scanline counters: - if (++dot > 340) - { - dot %= 341; - if (++scanline > 261) - { - scanline = 0; - frameOdd ^= 1; - } - } -} - -void reset() -{ - frameOdd = false; - scanline = dot = 0; - ctrl.r = mask.r = status.r = 0; - - memset(pixels, 0x00, sizeof(pixels)); - memset(ciRam, 0xFF, sizeof(ciRam)); - memset(oamMem, 0x00, sizeof(oamMem)); -} - - -} diff --git a/res/font.ttf b/res/font.ttf Binary files differdeleted file mode 100644 index 9acccd4..0000000 --- a/res/font.ttf +++ /dev/null diff --git a/res/init.png b/res/init.png Binary files differdeleted file mode 100644 index e5a9785..0000000 --- a/res/init.png +++ /dev/null diff --git a/src/palette.inc b/src/palette.inc deleted file mode 100644 index b869d86..0000000 --- a/src/palette.inc +++ /dev/null @@ -1,9 +0,0 @@ -u32 nesRgb[] = -{ 0x7C7C7C, 0x0000FC, 0x0000BC, 0x4428BC, 0x940084, 0xA80020, 0xA81000, 0x881400, - 0x503000, 0x007800, 0x006800, 0x005800, 0x004058, 0x000000, 0x000000, 0x000000, - 0xBCBCBC, 0x0078F8, 0x0058F8, 0x6844FC, 0xD800CC, 0xE40058, 0xF83800, 0xE45C10, - 0xAC7C00, 0x00B800, 0x00A800, 0x00A844, 0x008888, 0x000000, 0x000000, 0x000000, - 0xF8F8F8, 0x3CBCFC, 0x6888FC, 0x9878F8, 0xF878F8, 0xF85898, 0xF87858, 0xFCA044, - 0xF8B800, 0xB8F818, 0x58D854, 0x58F898, 0x00E8D8, 0x787878, 0x000000, 0x000000, - 0xFCFCFC, 0xA4E4FC, 0xB8B8F8, 0xD8B8F8, 0xF8B8F8, 0xF8A4C0, 0xF0D0B0, 0xFCE0A8, - 0xF8D878, 0xD8F878, 0xB8F8B8, 0xB8F8D8, 0x00FCFC, 0xF8D8F8, 0x000000, 0x000000 }; |