// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Sgc_Impl.h" /* Copyright (C) 2006-2009 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" void const* Sgc_Impl::coleco_bios; Sgc_Impl::Sgc_Impl() : rom( bank_size ) { assert( offsetof (header_t,copyright [32]) == header_t::size ); } Sgc_Impl::~Sgc_Impl() { } bool Sgc_Impl::header_t::valid_tag() const { return 0 == memcmp( tag, "SGC\x1A", 4 ); } blargg_err_t Sgc_Impl::load_( Data_Reader& in ) { RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) ); if ( !header_.valid_tag() ) return blargg_err_file_type; if ( header_.vers != 1 ) set_warning( "Unknown file version" ); if ( header_.system > 2 ) set_warning( "Unknown system" ); addr_t load_addr = get_le16( header_.load_addr ); if ( load_addr < 0x400 ) set_warning( "Invalid load address" ); rom.set_addr( load_addr ); play_period = clock_rate() / 60; if ( sega_mapping() ) { RETURN_ERR( ram.resize( 0x2000 + Sgc_Cpu::page_padding ) ); RETURN_ERR( ram2.resize( bank_size + Sgc_Cpu::page_padding ) ); } else { RETURN_ERR( ram.resize( 0x400 + Sgc_Cpu::page_padding ) ); } RETURN_ERR( vectors.resize( Sgc_Cpu::page_size + Sgc_Cpu::page_padding ) ); // TODO: doesn't need to be larger than page size, if we do mapping calls right RETURN_ERR( unmapped_write.resize( bank_size ) ); return blargg_ok; } void Sgc_Impl::unload() { rom.clear(); vectors.clear(); ram.clear(); ram2.clear(); unmapped_write.clear(); Gme_Loader::unload(); } blargg_err_t Sgc_Impl::start_track( int track ) { memset( ram .begin(), 0, ram .size() ); memset( ram2.begin(), 0, ram2.size() ); memset( vectors.begin(), 0xFF, vectors.size() ); cpu.reset( unmapped_write.begin(), rom.unmapped() ); if ( sega_mapping() ) { vectors_addr = 0x10000 - Sgc_Cpu::page_size; idle_addr = vectors_addr; for ( int i = 1; i < 8; ++i ) { vectors [i*8 + 0] = 0xC3; // JP addr vectors [i*8 + 1] = header_.rst_addrs [i*2 + 0]; vectors [i*8 + 2] = header_.rst_addrs [i*2 + 1]; } cpu.map_mem( 0xC000, 0x2000, ram.begin() ); cpu.map_mem( vectors_addr, cpu.page_size, unmapped_write.begin(), vectors.begin() ); bank2 = NULL; for ( int i = 0; i < 4; ++i ) cpu_write( 0xFFFC + i, header_.mapping [i] ); } else { if ( !coleco_bios ) return BLARGG_ERR( BLARGG_ERR_CALLER, "Coleco BIOS not set" ); vectors_addr = 0; cpu.map_mem( 0, 0x2000, unmapped_write.begin(), coleco_bios ); for ( int i = 0; i < 8; ++i ) cpu.map_mem( 0x6000 + i*0x400, 0x400, ram.begin() ); idle_addr = 0x2000; cpu.map_mem( 0x2000, cpu.page_size, unmapped_write.begin(), vectors.begin() ); for ( int i = 0; i < 0x8000 / bank_size; ++i ) { int addr = 0x8000 + i*bank_size; cpu.map_mem( addr, bank_size, unmapped_write.begin(), rom.at_addr( addr ) ); } } cpu.r.sp = get_le16( header_.stack_ptr ); cpu.r.b.a = track; next_play = play_period; jsr( header_.init_addr ); return blargg_ok; } // Emulation void Sgc_Impl::jsr( byte const (&addr) [2] ) { *cpu.write( --cpu.r.sp ) = idle_addr >> 8; *cpu.write( --cpu.r.sp ) = idle_addr & 0xFF; cpu.r.pc = get_le16( addr ); } void Sgc_Impl::set_bank( int bank, void const* data ) { //dprintf( "map bank %d to %p\n", bank, (byte*) data - rom.at_addr( 0 ) ); cpu.map_mem( bank * bank_size, bank_size, unmapped_write.begin(), data ); } void Sgc_Impl::cpu_write( addr_t addr, int data ) { if ( (addr ^ 0xFFFC) > 3 || !sega_mapping() ) { *cpu.write( addr ) = data; return; } switch ( addr ) { case 0xFFFC: cpu.map_mem( 2 * bank_size, bank_size, ram2.begin() ); if ( data & 0x08 ) break; bank2 = ram2.begin(); // FALL THROUGH case 0xFFFF: { bool rom_mapped = (cpu.read( 2 * bank_size ) == bank2); bank2 = rom.at_addr( data * bank_size ); if ( rom_mapped ) set_bank( 2, bank2 ); break; } case 0xFFFD: set_bank( 0, rom.at_addr( data * bank_size ) ); break; case 0xFFFE: set_bank( 1, rom.at_addr( data * bank_size ) ); break; } } int Sgc_Impl::cpu_in( addr_t addr ) { dprintf( "in %02X\n", addr ); return 0; } void Sgc_Impl::cpu_out( time_t, addr_t addr, int ) { dprintf( "out %02X\n", addr & 0xFF ); } blargg_err_t Sgc_Impl::end_frame( time_t end ) { while ( cpu.time() < end ) { time_t next = min( end, next_play ); if ( run_cpu( next ) ) { set_warning( "Unsupported CPU instruction" ); cpu.set_time( next ); } if ( cpu.r.pc == idle_addr ) cpu.set_time( next ); if ( cpu.time() >= next_play ) { next_play += play_period; if ( cpu.r.pc == idle_addr ) jsr( header_.play_addr ); } } next_play -= end; check( next_play >= 0 ); cpu.adjust_time( -end ); return blargg_ok; }