2011-01-11 02:55:51 +00:00
using System ;
2011-01-21 03:59:50 +00:00
using System.Collections.Generic ;
2011-01-11 02:55:51 +00:00
using System.Globalization ;
using System.IO ;
using BizHawk.Emulation.CPUs.H6280 ;
using BizHawk.Emulation.Sound ;
2011-07-15 02:08:18 +00:00
using BizHawk.Disc ;
2011-01-11 02:55:51 +00:00
namespace BizHawk.Emulation.Consoles.TurboGrafx
{
public enum NecSystemType
{
TurboGrafx ,
TurboCD ,
SuperGrafx
}
public sealed partial class PCEngine : IEmulator
{
// ROM
public byte [ ] RomData ;
2011-02-26 23:02:34 +00:00
public int RomLength ;
2011-01-11 02:55:51 +00:00
// Machine
public NecSystemType Type ;
public HuC6280 Cpu ;
public VDC VDC1 , VDC2 ;
public VCE VCE ;
public HuC6280PSG PSG ;
public VPC VPC ;
private bool TurboGrafx { get { return Type = = NecSystemType . TurboGrafx ; } }
private bool SuperGrafx { get { return Type = = NecSystemType . SuperGrafx ; } }
private bool TurboCD { get { return Type = = NecSystemType . TurboCD ; } }
2011-06-19 01:37:09 +00:00
// BRAM
private bool BramEnabled = false ;
private bool BramLocked = true ;
private byte [ ] BRAM ;
2011-01-11 02:55:51 +00:00
// Memory system
public byte [ ] Ram ;
2011-07-15 02:08:18 +00:00
// Disc
//private Disc.Disc disc = Disc.Disc.FromCuePath("d:/lib/roms/Turbo CD/Cosmic Fantasy II/Cosmic Fantasy II [U][CD][WTG990301][Telenet Japan][1992][PCE][thx-1138-darkwater].cue");
2011-01-11 02:55:51 +00:00
// PC Engine timings:
// 21,477,270 Machine clocks / sec
// 7,159,090 Cpu cycles / sec
public PCEngine ( NecSystemType type )
{
2011-06-11 22:15:08 +00:00
CoreOutputComm = new CoreOutputComm ( ) ;
2011-01-11 02:55:51 +00:00
Type = type ;
Controller = NullController . GetNullController ( ) ;
Cpu = new HuC6280 ( ) ;
VCE = new VCE ( ) ;
VDC1 = new VDC ( Cpu , VCE ) ;
PSG = new HuC6280PSG ( ) ;
2011-01-21 07:14:42 +00:00
2011-01-11 02:55:51 +00:00
if ( TurboGrafx | | TurboCD )
{
Ram = new byte [ 0x2000 ] ;
2011-02-26 23:02:34 +00:00
Cpu . ReadMemory21 = ReadMemory ;
Cpu . WriteMemory21 = WriteMemory ;
2011-01-11 02:55:51 +00:00
Cpu . WriteVDC = VDC1 . WriteVDC ;
}
if ( SuperGrafx )
{
VDC2 = new VDC ( Cpu , VCE ) ;
VPC = new VPC ( VDC1 , VDC2 , VCE , Cpu ) ;
Ram = new byte [ 0x8000 ] ;
2011-02-26 23:02:34 +00:00
Cpu . ReadMemory21 = ReadMemorySGX ;
Cpu . WriteMemory21 = WriteMemorySGX ;
2011-01-11 02:55:51 +00:00
Cpu . WriteVDC = VDC1 . WriteVDC ;
}
}
2011-01-21 07:14:42 +00:00
2011-01-11 02:55:51 +00:00
public void LoadGame ( IGame game )
{
if ( game . GetRomData ( ) . Length = = 0x60000 )
{
// 384k roms require special loading code. Why ;_;
// In memory, 384k roms look like [1st 256k][Then full 384k]
RomData = new byte [ 0xA0000 ] ;
var origRom = game . GetRomData ( ) ;
for ( int i = 0 ; i < 0x40000 ; i + + )
RomData [ i ] = origRom [ i ] ;
for ( int i = 0 ; i < 0x60000 ; i + + )
RomData [ i + 0x40000 ] = origRom [ i ] ;
2011-02-26 23:02:34 +00:00
RomLength = RomData . Length ;
2011-01-11 02:55:51 +00:00
} else if ( game . GetRomData ( ) . Length > 1024 * 1024 ) {
// If the rom is bigger than 1 megabyte, switch to Street Fighter 2 mapper
2011-02-26 23:02:34 +00:00
Cpu . ReadMemory21 = ReadMemorySF2 ;
Cpu . WriteMemory21 = WriteMemorySF2 ;
2011-01-11 02:55:51 +00:00
RomData = game . GetRomData ( ) ;
2011-02-26 23:02:34 +00:00
RomLength = RomData . Length ;
2011-01-11 02:55:51 +00:00
} else {
// normal rom.
RomData = game . GetRomData ( ) ;
2011-02-26 23:02:34 +00:00
RomLength = RomData . Length ;
2011-01-11 02:55:51 +00:00
}
2011-06-19 01:37:09 +00:00
if ( game . GetOptions ( ) . Contains ( "BRAM" ) )
{
BramEnabled = true ;
BRAM = new byte [ 2048 ] ;
Console . WriteLine ( "ENABLE BRAM!" ) ;
// pre-format BRAM
BRAM [ 0 ] = 0x48 ; BRAM [ 1 ] = 0x55 ; BRAM [ 2 ] = 0x42 ; BRAM [ 3 ] = 0x4D ;
BRAM [ 4 ] = 0x00 ; BRAM [ 5 ] = 0x88 ; BRAM [ 6 ] = 0x10 ; BRAM [ 7 ] = 0x80 ;
}
2011-07-15 02:08:18 +00:00
if ( game . GetOptions ( ) . Contains ( "PopulousSRAM" ) )
{
PopulousRAM = new byte [ 0x8000 ] ;
Cpu . ReadMemory21 = ReadMemoryPopulous ;
Cpu . WriteMemory21 = WriteMemoryPopulous ;
}
2011-01-11 02:55:51 +00:00
Cpu . ResetPC ( ) ;
2011-01-21 03:59:50 +00:00
SetupMemoryDomains ( ) ;
2011-01-11 02:55:51 +00:00
}
2011-05-01 14:51:59 +00:00
private int _lagcount = 0 ;
private bool lagged = true ;
2011-05-01 16:04:53 +00:00
private bool islag = false ;
2011-01-11 02:55:51 +00:00
public int Frame { get ; set ; }
2011-05-01 16:04:53 +00:00
public int LagCount { get { return _lagcount ; } set { _lagcount = value ; } }
public bool IsLagFrame { get { return islag ; } }
2011-01-11 02:55:51 +00:00
public void FrameAdvance ( bool render )
{
2011-05-01 14:51:59 +00:00
lagged = true ;
2011-02-05 05:40:19 +00:00
Controller . UpdateControls ( Frame + + ) ;
2011-03-15 03:36:02 +00:00
2011-01-11 02:55:51 +00:00
PSG . BeginFrame ( Cpu . TotalExecutedCycles ) ;
if ( SuperGrafx )
VPC . ExecFrame ( ) ; // TODO supergrafx frameskipping (waiting on a larger update of VPC frame timing, once I get VDC timing correct)
else
VDC1 . ExecFrame ( render ) ;
PSG . EndFrame ( Cpu . TotalExecutedCycles ) ;
2011-05-01 14:51:59 +00:00
if ( lagged )
2011-05-01 16:04:53 +00:00
{
2011-05-01 14:51:59 +00:00
_lagcount + + ;
2011-05-01 16:04:53 +00:00
islag = true ;
}
else
islag = false ;
2011-01-11 02:55:51 +00:00
}
2011-06-11 22:15:08 +00:00
public CoreInputComm CoreInputComm { get ; set ; }
public CoreOutputComm CoreOutputComm { get ; private set ; }
2011-01-11 02:55:51 +00:00
public IVideoProvider VideoProvider
{
get { return ( IVideoProvider ) VPC ? ? VDC1 ; }
}
public ISoundProvider SoundProvider
{
get { return PSG ; }
}
2011-02-16 04:45:59 +00:00
public string SystemId { get { return "PCE" ; } }
2011-01-11 02:55:51 +00:00
public string Region { get ; set ; }
public bool DeterministicEmulation { get ; set ; }
public byte [ ] SaveRam
{
2011-06-19 01:37:09 +00:00
get { return BRAM ; }
2011-01-11 02:55:51 +00:00
}
2011-06-19 01:37:09 +00:00
public bool SaveRamModified { get ; set ; }
2011-01-11 02:55:51 +00:00
public void SaveStateText ( TextWriter writer )
{
writer . WriteLine ( "[PCEngine]" ) ;
writer . Write ( "RAM " ) ;
Ram . SaveAsHex ( writer ) ;
2011-07-15 02:08:18 +00:00
if ( PopulousRAM ! = null )
{
writer . Write ( "PopulousRAM " ) ;
PopulousRAM . SaveAsHex ( writer ) ;
}
2011-01-11 02:55:51 +00:00
writer . WriteLine ( "Frame " + Frame ) ;
2011-05-01 14:51:59 +00:00
writer . WriteLine ( "Lag " + _lagcount ) ;
2011-02-26 23:02:34 +00:00
if ( Cpu . ReadMemory21 = = ReadMemorySF2 )
2011-01-11 02:55:51 +00:00
writer . WriteLine ( "SF2MapperLatch " + SF2MapperLatch ) ;
writer . WriteLine ( "IOBuffer {0:X2}" , IOBuffer ) ;
writer . WriteLine ( ) ;
if ( SuperGrafx )
{
Cpu . SaveStateText ( writer ) ;
VPC . SaveStateText ( writer ) ;
VCE . SaveStateText ( writer ) ;
VDC1 . SaveStateText ( writer , 1 ) ;
VDC2 . SaveStateText ( writer , 2 ) ;
PSG . SaveStateText ( writer ) ;
}
else
{
Cpu . SaveStateText ( writer ) ;
VCE . SaveStateText ( writer ) ;
VDC1 . SaveStateText ( writer , 1 ) ;
PSG . SaveStateText ( writer ) ;
}
writer . WriteLine ( "[/PCEngine]" ) ;
}
public void LoadStateText ( TextReader reader )
{
while ( true )
{
string [ ] args = reader . ReadLine ( ) . Split ( ' ' ) ;
if ( args [ 0 ] . Trim ( ) = = "" ) continue ;
if ( args [ 0 ] = = "[PCEngine]" ) continue ;
if ( args [ 0 ] = = "[/PCEngine]" ) break ;
if ( args [ 0 ] = = "Frame" )
Frame = int . Parse ( args [ 1 ] ) ;
2011-05-01 14:51:59 +00:00
else if ( args [ 0 ] = = "Lag" )
_lagcount = int . Parse ( args [ 1 ] ) ;
2011-01-11 02:55:51 +00:00
else if ( args [ 0 ] = = "SF2MapperLatch" )
SF2MapperLatch = byte . Parse ( args [ 1 ] ) ;
else if ( args [ 0 ] = = "IOBuffer" )
IOBuffer = byte . Parse ( args [ 1 ] , NumberStyles . HexNumber ) ;
else if ( args [ 0 ] = = "RAM" )
Ram . ReadFromHex ( args [ 1 ] ) ;
2011-07-15 02:08:18 +00:00
else if ( args [ 0 ] = = "PopulousRAM" & & PopulousRAM ! = null )
PopulousRAM . ReadFromHex ( args [ 1 ] ) ;
2011-01-11 02:55:51 +00:00
else if ( args [ 0 ] = = "[HuC6280]" )
Cpu . LoadStateText ( reader ) ;
else if ( args [ 0 ] = = "[PSG]" )
PSG . LoadStateText ( reader ) ;
else if ( args [ 0 ] = = "[VCE]" )
VCE . LoadStateText ( reader ) ;
else if ( args [ 0 ] = = "[VPC]" )
VPC . LoadStateText ( reader ) ;
else if ( args [ 0 ] = = "[VDC1]" )
2011-05-01 14:51:59 +00:00
VDC1 . LoadStateText ( reader , 1 ) ;
2011-01-11 02:55:51 +00:00
else if ( args [ 0 ] = = "[VDC2]" )
2011-05-01 14:51:59 +00:00
VDC2 . LoadStateText ( reader , 2 ) ;
2011-01-11 02:55:51 +00:00
else
Console . WriteLine ( "Skipping unrecognized identifier " + args [ 0 ] ) ;
}
}
public void SaveStateBinary ( BinaryWriter writer )
{
if ( SuperGrafx = = false )
{
writer . Write ( Ram ) ;
2011-07-15 02:08:18 +00:00
if ( PopulousRAM ! = null )
writer . Write ( PopulousRAM ) ;
2011-01-11 02:55:51 +00:00
writer . Write ( Frame ) ;
2011-07-15 02:08:18 +00:00
writer . Write ( _lagcount ) ;
2011-01-11 02:55:51 +00:00
writer . Write ( SF2MapperLatch ) ;
writer . Write ( IOBuffer ) ;
Cpu . SaveStateBinary ( writer ) ;
VCE . SaveStateBinary ( writer ) ;
VDC1 . SaveStateBinary ( writer ) ;
PSG . SaveStateBinary ( writer ) ;
} else {
writer . Write ( Ram ) ;
writer . Write ( Frame ) ;
2011-07-15 02:08:18 +00:00
writer . Write ( _lagcount ) ;
2011-01-11 02:55:51 +00:00
writer . Write ( IOBuffer ) ;
Cpu . SaveStateBinary ( writer ) ;
VCE . SaveStateBinary ( writer ) ;
VPC . SaveStateBinary ( writer ) ;
VDC1 . SaveStateBinary ( writer ) ;
VDC2 . SaveStateBinary ( writer ) ;
PSG . SaveStateBinary ( writer ) ;
}
}
public void LoadStateBinary ( BinaryReader reader )
{
if ( SuperGrafx = = false )
{
Ram = reader . ReadBytes ( 0x2000 ) ;
2011-07-15 02:08:18 +00:00
if ( PopulousRAM ! = null )
PopulousRAM = reader . ReadBytes ( 0x8000 ) ;
2011-01-11 02:55:51 +00:00
Frame = reader . ReadInt32 ( ) ;
2011-07-15 02:08:18 +00:00
_lagcount = reader . ReadInt32 ( ) ;
2011-01-11 02:55:51 +00:00
SF2MapperLatch = reader . ReadByte ( ) ;
IOBuffer = reader . ReadByte ( ) ;
Cpu . LoadStateBinary ( reader ) ;
VCE . LoadStateBinary ( reader ) ;
VDC1 . LoadStateBinary ( reader ) ;
PSG . LoadStateBinary ( reader ) ;
} else {
Ram = reader . ReadBytes ( 0x8000 ) ;
Frame = reader . ReadInt32 ( ) ;
2011-07-15 02:08:18 +00:00
_lagcount = reader . ReadInt32 ( ) ;
2011-01-11 02:55:51 +00:00
IOBuffer = reader . ReadByte ( ) ;
Cpu . LoadStateBinary ( reader ) ;
VCE . LoadStateBinary ( reader ) ;
VPC . LoadStateBinary ( reader ) ;
VDC1 . LoadStateBinary ( reader ) ;
VDC2 . LoadStateBinary ( reader ) ;
PSG . LoadStateBinary ( reader ) ;
}
}
public byte [ ] SaveStateBinary ( )
{
2011-07-15 02:08:18 +00:00
int buflen = SuperGrafx ? 166556 : 75858 ;
2011-06-19 01:37:09 +00:00
if ( BramEnabled ) buflen + = 2048 ;
2011-07-15 02:08:18 +00:00
if ( PopulousRAM ! = null ) buflen + = 0x8000 ;
2011-06-19 01:37:09 +00:00
var buf = new byte [ buflen ] ;
2011-01-11 02:55:51 +00:00
var stream = new MemoryStream ( buf ) ;
var writer = new BinaryWriter ( stream ) ;
SaveStateBinary ( writer ) ;
//Console.WriteLine("LENGTH " + stream.Position);
writer . Close ( ) ;
return buf ;
}
2011-01-21 03:59:50 +00:00
private void SetupMemoryDomains ( )
{
var domains = new List < MemoryDomain > ( 1 ) ;
var MainMemoryDomain = new MemoryDomain ( "Main Memory" , Ram . Length , Endian . Little ,
addr = > Ram [ addr & 0x7FFF ] ,
( addr , value ) = > Ram [ addr & 0x7FFF ] = value ) ;
2011-02-26 23:02:34 +00:00
var SystemBusDomain = new MemoryDomain ( "System Bus" , 0x2F0000 , Endian . Little ,
addr = > Cpu . ReadMemory21 ( addr ) ,
( addr , value ) = > Cpu . WriteMemory21 ( addr , value ) ) ;
2011-01-21 03:59:50 +00:00
domains . Add ( MainMemoryDomain ) ;
2011-02-26 23:02:34 +00:00
domains . Add ( SystemBusDomain ) ;
2011-01-21 03:59:50 +00:00
memoryDomains = domains . AsReadOnly ( ) ;
}
private IList < MemoryDomain > memoryDomains ;
public IList < MemoryDomain > MemoryDomains { get { return memoryDomains ; } }
public MemoryDomain MainMemory { get { return memoryDomains [ 0 ] ; } }
2011-02-21 09:48:53 +00:00
2011-06-02 02:59:18 +00:00
public void Dispose ( ) { }
2011-01-11 02:55:51 +00:00
}
}