summaryrefslogtreecommitdiff
path: root/plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.cpp')
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.cpp192
1 files changed, 192 insertions, 0 deletions
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.cpp
new file mode 100644
index 00000000..fa132458
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.cpp
@@ -0,0 +1,192 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Sap_Core.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const idle_addr = 0xD2D2;
+
+Sap_Core::Sap_Core()
+{
+ set_tempo( 1 );
+}
+
+void Sap_Core::push( int b )
+{
+ mem.ram [0x100 + cpu.r.sp--] = (byte) b;
+}
+
+void Sap_Core::jsr_then_stop( addr_t addr )
+{
+ cpu.r.pc = addr;
+
+ // Some rips pop three bytes off stack before RTS.
+ push( (idle_addr - 1) >> 8 );
+ push( idle_addr - 1 );
+
+ // 3 bytes so that RTI or RTS will jump to idle_addr.
+ // RTI will use the first two bytes as the address, 0xD2D2.
+ // RTS will use the last two bytes, 0xD2D1, which it internally increments.
+ push( (idle_addr - 1) >> 8 );
+ push( (idle_addr - 1) >> 8 );
+ push( idle_addr - 1 );
+}
+
+// Runs routine and allows it up to one second to return
+void Sap_Core::run_routine( addr_t addr )
+{
+ jsr_then_stop( addr );
+ run_cpu( lines_per_frame * base_scanline_period * 60 );
+ check( cpu.r.pc == idle_addr );
+ check( cpu.r.sp >= 0xFF - 6 );
+}
+
+inline void Sap_Core::call_init( int track )
+{
+ cpu.r.a = track;
+
+ switch ( info.type )
+ {
+ case 'B':
+ run_routine( info.init_addr );
+ break;
+
+ case 'C':
+ cpu.r.a = 0x70;
+ cpu.r.x = info.music_addr&0xFF;
+ cpu.r.y = info.music_addr >> 8;
+ run_routine( info.play_addr + 3 );
+ cpu.r.a = 0;
+ cpu.r.x = track;
+ run_routine( info.play_addr + 3 );
+ break;
+
+ case 'D':
+ check( info.fastplay == lines_per_frame );
+ jsr_then_stop( info.init_addr );
+ break;
+ }
+}
+
+void Sap_Core::setup_ram()
+{
+ memset( &mem, 0, sizeof mem );
+
+ ram() [idle_addr] = cpu.halt_opcode;
+
+ addr_t const irq_addr = idle_addr - 1;
+ ram() [irq_addr] = cpu.halt_opcode;
+ ram() [0xFFFE] = (byte) irq_addr;
+ ram() [0xFFFF] = irq_addr >> 8;
+}
+
+blargg_err_t Sap_Core::start_track( int track, info_t const& new_info )
+{
+ info = new_info;
+
+ check( ram() [idle_addr] == cpu.halt_opcode );
+
+ apu_ .reset( &apu_impl_ );
+ apu2_.reset( &apu_impl_ );
+
+ cpu.reset( ram() );
+
+ frame_start = 0;
+ next_play = play_period() * 4;
+ saved_state.pc = idle_addr;
+
+ time_mask = 0; // disables sound during init
+ call_init( track );
+ time_mask = ~0;
+
+ return blargg_ok;
+}
+
+blargg_err_t Sap_Core::run_until( time_t end )
+{
+ while ( cpu.time() < end )
+ {
+ time_t next = min( next_play, end );
+ if ( (run_cpu( next ) && cpu.r.pc != idle_addr) || cpu.error_count() )
+ // TODO: better error
+ return BLARGG_ERR( BLARGG_ERR_GENERIC, "Emulation error (illegal instruction)" );
+
+ if ( cpu.r.pc == idle_addr )
+ {
+ if ( saved_state.pc == idle_addr )
+ {
+ // no code to run until next play call
+ cpu.set_time( next );
+ }
+ else
+ {
+ // play had interrupted non-returning init, so restore registers
+ // init routine was running
+ check( cpu.r.sp == saved_state.sp - 3 );
+ cpu.r = saved_state;
+ saved_state.pc = idle_addr;
+ }
+ }
+
+ if ( cpu.time() >= next_play )
+ {
+ next_play += play_period();
+
+ if ( cpu.r.pc == idle_addr || info.type == 'D' )
+ {
+ // Save state if init routine is still running
+ if ( cpu.r.pc != idle_addr )
+ {
+ check( info.type == 'D' );
+ check( saved_state.pc == idle_addr );
+ saved_state = cpu.r;
+ }
+
+ addr_t addr = info.play_addr;
+ if ( info.type == 'C' )
+ addr += 6;
+ jsr_then_stop( addr );
+ }
+ else
+ {
+ dprintf( "init/play hadn't returned before next play call\n" );
+ }
+ }
+ }
+ return blargg_ok;
+}
+
+blargg_err_t Sap_Core::end_frame( time_t end )
+{
+ RETURN_ERR( run_until( end ) );
+
+ cpu.adjust_time( -end );
+
+ time_t frame_time = lines_per_frame * scanline_period;
+ while ( frame_start < end )
+ frame_start += frame_time;
+ frame_start -= end + frame_time;
+
+ if ( (next_play -= end) < 0 )
+ {
+ next_play = 0;
+ check( false );
+ }
+
+ apu_.end_frame( end );
+ if ( info.stereo )
+ apu2_.end_frame( end );
+
+ return blargg_ok;
+}