226 lines
5.5 KiB
C++
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;
|
||
|
}
|