347 lines
12 KiB
C#
347 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using BizHawk.Emulation.CPUs.H6280;
|
|
using BizHawk.Emulation.Sound;
|
|
using BizHawk.Disc;
|
|
|
|
namespace BizHawk.Emulation.Consoles.TurboGrafx
|
|
{
|
|
public enum NecSystemType
|
|
{
|
|
TurboGrafx,
|
|
TurboCD,
|
|
SuperGrafx
|
|
}
|
|
|
|
public sealed partial class PCEngine : IEmulator
|
|
{
|
|
// ROM
|
|
public byte[] RomData;
|
|
public int RomLength;
|
|
|
|
// 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; } }
|
|
|
|
// BRAM
|
|
private bool BramEnabled = false;
|
|
private bool BramLocked = true;
|
|
private byte[] BRAM;
|
|
|
|
// Memory system
|
|
public byte[] Ram;
|
|
|
|
// 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");
|
|
|
|
// PC Engine timings:
|
|
// 21,477,270 Machine clocks / sec
|
|
// 7,159,090 Cpu cycles / sec
|
|
|
|
public PCEngine(NecSystemType type)
|
|
{
|
|
CoreOutputComm = new CoreOutputComm();
|
|
Type = type;
|
|
Controller = NullController.GetNullController();
|
|
Cpu = new HuC6280();
|
|
VCE = new VCE();
|
|
VDC1 = new VDC(Cpu, VCE);
|
|
PSG = new HuC6280PSG();
|
|
|
|
if (TurboGrafx || TurboCD)
|
|
{
|
|
Ram = new byte[0x2000];
|
|
Cpu.ReadMemory21 = ReadMemory;
|
|
Cpu.WriteMemory21 = WriteMemory;
|
|
Cpu.WriteVDC = VDC1.WriteVDC;
|
|
}
|
|
|
|
if (SuperGrafx)
|
|
{
|
|
VDC2 = new VDC(Cpu, VCE);
|
|
VPC = new VPC(VDC1, VDC2, VCE, Cpu);
|
|
Ram = new byte[0x8000];
|
|
Cpu.ReadMemory21 = ReadMemorySGX;
|
|
Cpu.WriteMemory21 = WriteMemorySGX;
|
|
Cpu.WriteVDC = VDC1.WriteVDC;
|
|
}
|
|
}
|
|
|
|
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];
|
|
RomLength = RomData.Length;
|
|
} else if (game.GetRomData().Length > 1024 * 1024) {
|
|
// If the rom is bigger than 1 megabyte, switch to Street Fighter 2 mapper
|
|
Cpu.ReadMemory21 = ReadMemorySF2;
|
|
Cpu.WriteMemory21 = WriteMemorySF2;
|
|
RomData = game.GetRomData();
|
|
RomLength = RomData.Length;
|
|
} else {
|
|
// normal rom.
|
|
RomData = game.GetRomData();
|
|
RomLength = RomData.Length;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (game.GetOptions().Contains("PopulousSRAM"))
|
|
{
|
|
PopulousRAM = new byte[0x8000];
|
|
Cpu.ReadMemory21 = ReadMemoryPopulous;
|
|
Cpu.WriteMemory21 = WriteMemoryPopulous;
|
|
}
|
|
|
|
Cpu.ResetPC();
|
|
SetupMemoryDomains();
|
|
}
|
|
|
|
private int _lagcount = 0;
|
|
private bool lagged = true;
|
|
private bool islag = false;
|
|
public int Frame { get; set; }
|
|
public int LagCount { get { return _lagcount; } set { _lagcount = value; } }
|
|
public bool IsLagFrame { get { return islag; } }
|
|
|
|
public void FrameAdvance(bool render)
|
|
{
|
|
lagged = true;
|
|
Controller.UpdateControls(Frame++);
|
|
|
|
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);
|
|
if (lagged)
|
|
{
|
|
_lagcount++;
|
|
islag = true;
|
|
}
|
|
else
|
|
islag = false;
|
|
}
|
|
|
|
public CoreInputComm CoreInputComm { get; set; }
|
|
public CoreOutputComm CoreOutputComm { get; private set; }
|
|
|
|
public IVideoProvider VideoProvider
|
|
{
|
|
get { return (IVideoProvider) VPC ?? VDC1; }
|
|
}
|
|
|
|
public ISoundProvider SoundProvider
|
|
{
|
|
get { return PSG; }
|
|
}
|
|
|
|
public string SystemId { get { return "PCE"; } }
|
|
public string Region { get; set; }
|
|
public bool DeterministicEmulation { get; set; }
|
|
|
|
public byte[] SaveRam
|
|
{
|
|
get { return BRAM; }
|
|
}
|
|
|
|
public bool SaveRamModified { get; set; }
|
|
|
|
public void SaveStateText(TextWriter writer)
|
|
{
|
|
writer.WriteLine("[PCEngine]");
|
|
writer.Write("RAM ");
|
|
Ram.SaveAsHex(writer);
|
|
if (PopulousRAM != null)
|
|
{
|
|
writer.Write("PopulousRAM ");
|
|
PopulousRAM.SaveAsHex(writer);
|
|
}
|
|
writer.WriteLine("Frame " + Frame);
|
|
writer.WriteLine("Lag " + _lagcount);
|
|
if (Cpu.ReadMemory21 == ReadMemorySF2)
|
|
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]);
|
|
else if (args[0] == "Lag")
|
|
_lagcount = int.Parse(args[1]);
|
|
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]);
|
|
else if (args[0] == "PopulousRAM" && PopulousRAM != null)
|
|
PopulousRAM.ReadFromHex(args[1]);
|
|
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]")
|
|
VDC1.LoadStateText(reader, 1);
|
|
else if (args[0] == "[VDC2]")
|
|
VDC2.LoadStateText(reader, 2);
|
|
else
|
|
Console.WriteLine("Skipping unrecognized identifier " + args[0]);
|
|
}
|
|
}
|
|
|
|
public void SaveStateBinary(BinaryWriter writer)
|
|
{
|
|
if (SuperGrafx == false)
|
|
{
|
|
writer.Write(Ram);
|
|
if (PopulousRAM != null)
|
|
writer.Write(PopulousRAM);
|
|
writer.Write(Frame);
|
|
writer.Write(_lagcount);
|
|
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);
|
|
writer.Write(_lagcount);
|
|
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);
|
|
if (PopulousRAM != null)
|
|
PopulousRAM = reader.ReadBytes(0x8000);
|
|
Frame = reader.ReadInt32();
|
|
_lagcount = reader.ReadInt32();
|
|
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();
|
|
_lagcount = reader.ReadInt32();
|
|
IOBuffer = reader.ReadByte();
|
|
Cpu.LoadStateBinary(reader);
|
|
VCE.LoadStateBinary(reader);
|
|
VPC.LoadStateBinary(reader);
|
|
VDC1.LoadStateBinary(reader);
|
|
VDC2.LoadStateBinary(reader);
|
|
PSG.LoadStateBinary(reader);
|
|
}
|
|
}
|
|
|
|
public byte[] SaveStateBinary()
|
|
{
|
|
int buflen = SuperGrafx ? 166556 : 75858;
|
|
if (BramEnabled) buflen += 2048;
|
|
if (PopulousRAM != null) buflen += 0x8000;
|
|
|
|
var buf = new byte[buflen];
|
|
var stream = new MemoryStream(buf);
|
|
var writer = new BinaryWriter(stream);
|
|
SaveStateBinary(writer);
|
|
//Console.WriteLine("LENGTH " + stream.Position);
|
|
writer.Close();
|
|
return buf;
|
|
}
|
|
|
|
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);
|
|
var SystemBusDomain = new MemoryDomain("System Bus", 0x2F0000, Endian.Little,
|
|
addr => Cpu.ReadMemory21(addr),
|
|
(addr, value) => Cpu.WriteMemory21(addr, value));
|
|
domains.Add(MainMemoryDomain);
|
|
domains.Add(SystemBusDomain);
|
|
memoryDomains = domains.AsReadOnly();
|
|
}
|
|
|
|
private IList<MemoryDomain> memoryDomains;
|
|
public IList<MemoryDomain> MemoryDomains { get { return memoryDomains; } }
|
|
public MemoryDomain MainMemory { get { return memoryDomains[0]; } }
|
|
|
|
public void Dispose() {}
|
|
}
|
|
}
|