cog/Frameworks/GME/gme/Sgc_Impl.cpp

226 lines
5.5 KiB
C++

// 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;
}