diff options
Diffstat (limited to 'plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.cpp')
-rw-r--r-- | plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.cpp | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.cpp new file mode 100644 index 00000000..4c56f56a --- /dev/null +++ b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.cpp @@ -0,0 +1,190 @@ +// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/ + +#include "Ay_Core.h" + +/* Copyright (C) 2006-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +inline void Ay_Core::disable_beeper() +{ + beeper_mask = 0; + last_beeper = 0; +} + +Ay_Core::Ay_Core() +{ + beeper_output = NULL; + disable_beeper(); +} + +Ay_Core::~Ay_Core() { } + +void Ay_Core::set_beeper_output( Blip_Buffer* b ) +{ + beeper_output = b; + if ( b && !cpc_mode ) + beeper_mask = 0x10; + else + disable_beeper(); +} + +void Ay_Core::start_track( registers_t const& r, addr_t play ) +{ + play_addr = play; + + memset( mem_.padding1, 0xFF, sizeof mem_.padding1 ); + + int const mirrored = 0x80; // this much is mirrored after end of memory + memset( mem_.ram + mem_size + mirrored, 0xFF, sizeof mem_.ram - mem_size - mirrored ); + memcpy( mem_.ram + mem_size, mem_.ram, mirrored ); // some code wraps around (ugh) + + cpu.reset( mem_.padding1, mem_.padding1 ); + cpu.map_mem( 0, mem_size, mem_.ram, mem_.ram ); + cpu.r = r; + + beeper_delta = (int) (apu_.amp_range * 0.8); + last_beeper = 0; + next_play = play_period; + spectrum_mode = false; + cpc_mode = false; + cpc_latch = 0; + set_beeper_output( beeper_output ); + apu_.reset(); + + // a few tunes rely on channels having tone enabled at the beginning + apu_.write_addr( 7 ); + apu_.write_data( 0, 0x38 ); + +} + +// Emulation + +void Ay_Core::cpu_out_( time_t time, addr_t addr, int data ) +{ + // Spectrum + if ( !cpc_mode ) + { + switch ( addr & 0xFEFF ) + { + case 0xFEFD: + spectrum_mode = true; + apu_.write_addr( data ); + return; + + case 0xBEFD: + spectrum_mode = true; + apu_.write_data( time, data ); + return; + } + } + + // CPC + if ( !spectrum_mode ) + { + switch ( addr >> 8 ) + { + case 0xF6: + switch ( data & 0xC0 ) + { + case 0xC0: + apu_.write_addr( cpc_latch ); + goto enable_cpc; + + case 0x80: + apu_.write_data( time, cpc_latch ); + goto enable_cpc; + } + break; + + case 0xF4: + cpc_latch = data; + goto enable_cpc; + } + } + + dprintf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); + return; + +enable_cpc: + if ( !cpc_mode ) + { + cpc_mode = true; + disable_beeper(); + set_cpc_callback.f( set_cpc_callback.data ); + } +} + +int Ay_Core::cpu_in( addr_t addr ) +{ + // keyboard read and other things + if ( (addr & 0xFF) == 0xFE ) + return 0xFF; // other values break some beeper tunes + + dprintf( "Unmapped IN : $%04X\n", addr ); + return 0xFF; +} + +void Ay_Core::end_frame( time_t* end ) +{ + cpu.set_time( 0 ); + + // Since detection of CPC mode will halve clock rate during the frame + // and thus generate up to twice as much sound, we must generate half + // as much until mode is known. + if ( !(spectrum_mode | cpc_mode) ) + *end /= 2; + + while ( cpu.time() < *end ) + { + run_cpu( min( *end, next_play ) ); + + if ( cpu.time() >= next_play ) + { + // next frame + next_play += play_period; + + if ( cpu.r.iff1 ) + { + // interrupt enabled + + if ( mem_.ram [cpu.r.pc] == 0x76 ) + cpu.r.pc++; // advance past HALT instruction + + cpu.r.iff1 = 0; + cpu.r.iff2 = 0; + + mem_.ram [--cpu.r.sp] = byte (cpu.r.pc >> 8); + mem_.ram [--cpu.r.sp] = byte (cpu.r.pc); + + // fixed interrupt + cpu.r.pc = 0x38; + cpu.adjust_time( 12 ); + + if ( cpu.r.im == 2 ) + { + // vectored interrupt + addr_t addr = cpu.r.i * 0x100 + 0xFF; + cpu.r.pc = mem_.ram [(addr + 1) & 0xFFFF] * 0x100 + mem_.ram [addr]; + cpu.adjust_time( 6 ); + } + } + } + } + + // End time frame + *end = cpu.time(); + next_play -= *end; + check( next_play >= 0 ); + cpu.adjust_time( -*end ); + apu_.end_frame( *end ); +} |