736 lines
22 KiB
C#
736 lines
22 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
|
|
using BizHawk.Common;
|
|
using BizHawk.Common.BufferExtensions;
|
|
|
|
using BizHawk.Emulation.Common;
|
|
using BizHawk.Emulation.Common.Components;
|
|
|
|
using BizHawk.Emulation.Cores.Components.H6280;
|
|
using BizHawk.Emulation.DiscSystem;
|
|
|
|
namespace BizHawk.Emulation.Cores.PCEngine
|
|
{
|
|
public enum NecSystemType { TurboGrafx, TurboCD, SuperGrafx }
|
|
|
|
[CoreAttributes(
|
|
"PCEHawk",
|
|
"Vecna",
|
|
isPorted: false,
|
|
isReleased: true
|
|
)]
|
|
public sealed partial class PCEngine : IEmulator, ISaveRam, IStatable, IInputPollable,
|
|
IDebuggable, ISettable<PCEngine.PCESettings, PCEngine.PCESyncSettings>, IDriveLight, ICodeDataLogger
|
|
{
|
|
// ROM
|
|
public byte[] RomData;
|
|
public int RomLength;
|
|
Disc disc;
|
|
|
|
// Machine
|
|
public NecSystemType Type;
|
|
public HuC6280 Cpu;
|
|
public VDC VDC1, VDC2;
|
|
public VCE VCE;
|
|
public VPC VPC;
|
|
public ScsiCDBus SCSI;
|
|
public ADPCM ADPCM;
|
|
|
|
public HuC6280PSG PSG;
|
|
public CDAudio CDAudio;
|
|
public SoundMixer SoundMixer;
|
|
public MetaspuSoundProvider SoundSynchronizer;
|
|
|
|
bool TurboGrafx { get { return Type == NecSystemType.TurboGrafx; } }
|
|
bool SuperGrafx { get { return Type == NecSystemType.SuperGrafx; } }
|
|
bool TurboCD { get { return Type == NecSystemType.TurboCD; } }
|
|
|
|
// BRAM
|
|
bool BramEnabled = false;
|
|
bool BramLocked = true;
|
|
byte[] BRAM;
|
|
|
|
// Memory system
|
|
public byte[] Ram; // PCE= 8K base ram, SGX= 64k base ram
|
|
public byte[] CDRam; // TurboCD extra 64k of ram
|
|
public byte[] SuperRam; // Super System Card 192K of additional RAM
|
|
public byte[] ArcadeRam; // Arcade Card 2048K of additional RAM
|
|
|
|
string systemid = "PCE";
|
|
bool ForceSpriteLimit;
|
|
|
|
// 21,477,270 Machine clocks / sec
|
|
// 7,159,090 Cpu cycles / sec
|
|
|
|
[CoreConstructor("PCE", "SGX")]
|
|
public PCEngine(CoreComm comm, GameInfo game, byte[] rom, object Settings, object syncSettings)
|
|
{
|
|
|
|
MemoryCallbacks = new MemoryCallbackSystem();
|
|
CoreComm = comm;
|
|
|
|
switch (game.System)
|
|
{
|
|
case "PCE":
|
|
systemid = "PCE";
|
|
Type = NecSystemType.TurboGrafx;
|
|
break;
|
|
case "SGX":
|
|
systemid = "SGX";
|
|
Type = NecSystemType.SuperGrafx;
|
|
break;
|
|
}
|
|
this._settings = (PCESettings)Settings ?? new PCESettings();
|
|
_syncSettings = (PCESyncSettings)syncSettings ?? new PCESyncSettings();
|
|
Init(game, rom);
|
|
SetControllerButtons();
|
|
}
|
|
|
|
public IEmulatorServiceProvider ServiceProvider { get; private set; }
|
|
|
|
public string BoardName { get { return null; } }
|
|
|
|
private ITraceable Tracer { get; set; }
|
|
public IMemoryCallbackSystem MemoryCallbacks { get; private set; }
|
|
|
|
public PCEngine(CoreComm comm, GameInfo game, Disc disc, object Settings, object syncSettings)
|
|
{
|
|
CoreComm = comm;
|
|
MemoryCallbacks = new MemoryCallbackSystem();
|
|
DriveLightEnabled = true;
|
|
systemid = "PCECD";
|
|
Type = NecSystemType.TurboCD;
|
|
this.disc = disc;
|
|
this._settings = (PCESettings)Settings ?? new PCESettings();
|
|
_syncSettings = (PCESyncSettings)syncSettings ?? new PCESyncSettings();
|
|
|
|
GameInfo biosInfo;
|
|
byte[] rom = CoreComm.CoreFileProvider.GetFirmwareWithGameInfo("PCECD", "Bios", true, out biosInfo,
|
|
"PCE-CD System Card not found. Please check the BIOS settings in Config->Firmwares.");
|
|
|
|
if (biosInfo.Status == RomStatus.BadDump)
|
|
{
|
|
CoreComm.ShowMessage(
|
|
"The PCE-CD System Card you have selected is known to be a bad dump. This may cause problems playing PCE-CD games.\n\n"
|
|
+ "It is recommended that you find a good dump of the system card. Sorry to be the bearer of bad news!");
|
|
}
|
|
else if (biosInfo.NotInDatabase)
|
|
{
|
|
CoreComm.ShowMessage(
|
|
"The PCE-CD System Card you have selected is not recognized in our database. That might mean it's a bad dump, or isn't the correct rom.");
|
|
}
|
|
else if (biosInfo["BIOS"] == false)
|
|
{
|
|
//zeromus says: someone please write a note about how this could possibly happen.
|
|
//it seems like this is a relic of using gameDB for storing whether something is a bios? firmwareDB should be handling it now.
|
|
CoreComm.ShowMessage(
|
|
"The PCE-CD System Card you have selected is not a BIOS image. You may have selected the wrong rom. FYI-Please report this to developers, I don't think this error message should happen.");
|
|
}
|
|
|
|
if (biosInfo["SuperSysCard"])
|
|
{
|
|
game.AddOption("SuperSysCard");
|
|
}
|
|
|
|
if (game["NeedSuperSysCard"] && game["SuperSysCard"] == false)
|
|
{
|
|
CoreComm.ShowMessage(
|
|
"This game requires a version 3.0 System card and won't run with the system card you've selected. Try selecting a 3.0 System Card in the firmware configuration.");
|
|
throw new Exception();
|
|
}
|
|
|
|
game.FirmwareHash = rom.HashSHA1();
|
|
|
|
Init(game, rom);
|
|
// the default RomStatusDetails don't do anything with Disc
|
|
CoreComm.RomStatusDetails = string.Format("{0}\r\nDisk partial hash:{1}", game.Name, new DiscSystem.DiscHasher(disc).OldHash());
|
|
SetControllerButtons();
|
|
}
|
|
|
|
public bool DriveLightEnabled { get; private set; }
|
|
public bool DriveLightOn { get; internal set; }
|
|
|
|
void Init(GameInfo game, byte[] rom)
|
|
{
|
|
Controller = NullController.GetNullController();
|
|
Cpu = new HuC6280(MemoryCallbacks);
|
|
VCE = new VCE();
|
|
VDC1 = new VDC(this, Cpu, VCE);
|
|
PSG = new HuC6280PSG();
|
|
SCSI = new ScsiCDBus(this, disc);
|
|
|
|
Cpu.Logger = (s) => Tracer.Put(s);
|
|
|
|
if (TurboGrafx)
|
|
{
|
|
Ram = new byte[0x2000];
|
|
Cpu.ReadMemory21 = ReadMemory;
|
|
Cpu.WriteMemory21 = WriteMemory;
|
|
Cpu.WriteVDC = VDC1.WriteVDC;
|
|
soundProvider = PSG;
|
|
CDAudio = new CDAudio(null, 0);
|
|
}
|
|
|
|
else if (SuperGrafx)
|
|
{
|
|
VDC2 = new VDC(this, Cpu, VCE);
|
|
VPC = new VPC(this, VDC1, VDC2, VCE, Cpu);
|
|
Ram = new byte[0x8000];
|
|
Cpu.ReadMemory21 = ReadMemorySGX;
|
|
Cpu.WriteMemory21 = WriteMemorySGX;
|
|
Cpu.WriteVDC = VDC1.WriteVDC;
|
|
soundProvider = PSG;
|
|
CDAudio = new CDAudio(null, 0);
|
|
}
|
|
|
|
else if (TurboCD)
|
|
{
|
|
Ram = new byte[0x2000];
|
|
CDRam = new byte[0x10000];
|
|
ADPCM = new ADPCM(this, SCSI);
|
|
Cpu.ReadMemory21 = ReadMemoryCD;
|
|
Cpu.WriteMemory21 = WriteMemoryCD;
|
|
Cpu.WriteVDC = VDC1.WriteVDC;
|
|
CDAudio = new CDAudio(disc);
|
|
SetCDAudioCallback();
|
|
PSG.MaxVolume = short.MaxValue * 3 / 4;
|
|
SoundMixer = new SoundMixer(PSG, CDAudio, ADPCM);
|
|
SoundSynchronizer = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
|
|
soundProvider = SoundSynchronizer;
|
|
Cpu.ThinkAction = (cycles) => { SCSI.Think(); ADPCM.Think(cycles); };
|
|
}
|
|
|
|
if (rom.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 = rom;
|
|
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 (rom.Length > 1024 * 1024)
|
|
{
|
|
// If the rom is bigger than 1 megabyte, switch to Street Fighter 2 mapper
|
|
Cpu.ReadMemory21 = ReadMemorySF2;
|
|
Cpu.WriteMemory21 = WriteMemorySF2;
|
|
RomData = rom;
|
|
RomLength = RomData.Length;
|
|
// user request: current value of the SF2MapperLatch on the tracelogger
|
|
Cpu.Logger = (s) => Tracer.Put(new TraceInfo
|
|
{
|
|
Disassembly = string.Format("{0:X1}:{1}", SF2MapperLatch, s),
|
|
RegisterInfo = ""
|
|
});
|
|
}
|
|
else
|
|
{
|
|
// normal rom.
|
|
RomData = rom;
|
|
RomLength = RomData.Length;
|
|
}
|
|
|
|
if (game["BRAM"] || Type == NecSystemType.TurboCD)
|
|
{
|
|
BramEnabled = true;
|
|
BRAM = new byte[2048];
|
|
|
|
// pre-format BRAM. damn are we helpful.
|
|
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["SuperSysCard"])
|
|
SuperRam = new byte[0x30000];
|
|
|
|
if (game["ArcadeCard"])
|
|
{
|
|
ArcadeRam = new byte[0x200000];
|
|
ArcadeCard = true;
|
|
ArcadeCardRewindHack = _settings.ArcadeCardRewindHack;
|
|
for (int i = 0; i < 4; i++)
|
|
ArcadePage[i] = new ArcadeCardPage();
|
|
}
|
|
|
|
if (game["PopulousSRAM"])
|
|
{
|
|
PopulousRAM = new byte[0x8000];
|
|
Cpu.ReadMemory21 = ReadMemoryPopulous;
|
|
Cpu.WriteMemory21 = WriteMemoryPopulous;
|
|
}
|
|
|
|
// the gamedb can force sprite limit on, ignoring settings
|
|
if (game["ForceSpriteLimit"] || game.NotInDatabase)
|
|
ForceSpriteLimit = true;
|
|
|
|
if (game["CdVol"])
|
|
CDAudio.MaxVolume = int.Parse(game.OptionValue("CdVol"));
|
|
if (game["PsgVol"])
|
|
PSG.MaxVolume = int.Parse(game.OptionValue("PsgVol"));
|
|
if (game["AdpcmVol"])
|
|
ADPCM.MaxVolume = int.Parse(game.OptionValue("AdpcmVol"));
|
|
// the gamedb can also force equalizevolumes on
|
|
if (TurboCD && (_settings.EqualizeVolume || game["EqualizeVolumes"] || game.NotInDatabase))
|
|
SoundMixer.EqualizeVolumes();
|
|
|
|
// Ok, yes, HBlankPeriod's only purpose is game-specific hax.
|
|
// 1) At least they're not coded directly into the emulator, but instead data-driven.
|
|
// 2) The games which have custom HBlankPeriods work without it, the override only
|
|
// serves to clean up minor gfx anomalies.
|
|
// 3) There's no point in haxing the timing with incorrect values in an attempt to avoid this.
|
|
// The proper fix is cycle-accurate/bus-accurate timing. That isn't coming to the C#
|
|
// version of this core. Let's just acknolwedge that the timing is imperfect and fix
|
|
// it in the least intrusive and most honest way we can.
|
|
|
|
if (game["HBlankPeriod"])
|
|
VDC1.HBlankCycles = game.GetIntValue("HBlankPeriod");
|
|
|
|
// This is also a hack. Proper multi-res/TV emulation will be a native-code core feature.
|
|
|
|
if (game["MultiResHack"])
|
|
VDC1.MultiResHack = game.GetIntValue("MultiResHack");
|
|
|
|
Cpu.ResetPC();
|
|
|
|
Tracer = new TraceBuffer { Header = Cpu.TraceHeader };
|
|
var ser = new BasicServiceProvider(this);
|
|
ServiceProvider = ser;
|
|
ser.Register<ITraceable>(Tracer);
|
|
ser.Register<IDisassemblable>(Cpu);
|
|
ser.Register<IVideoProvider>((IVideoProvider)VPC ?? VDC1);
|
|
SetupMemoryDomains();
|
|
}
|
|
|
|
int lagCount;
|
|
int frame;
|
|
bool lagged = true;
|
|
bool isLag = false;
|
|
public int Frame { get { return frame; } set { frame = value; } }
|
|
public int LagCount { get { return lagCount; } set { lagCount = value; } }
|
|
public bool IsLagFrame { get { return isLag; } set { isLag = value; } }
|
|
|
|
private readonly InputCallbackSystem _inputCallbacks = new InputCallbackSystem();
|
|
public IInputCallbackSystem InputCallbacks { get { return _inputCallbacks; } }
|
|
|
|
void ICodeDataLogger.SetCDL(CodeDataLog cdl)
|
|
{
|
|
Cpu.CDL = cdl;
|
|
}
|
|
|
|
void ICodeDataLogger.NewCDL(CodeDataLog cdl)
|
|
{
|
|
InitCDLMappings();
|
|
var mm = this.Cpu.Mappings;
|
|
foreach (var kvp in SizesFromHuMap(mm))
|
|
{
|
|
cdl[kvp.Key] = new byte[kvp.Value];
|
|
}
|
|
|
|
cdl.SubType = "PCE";
|
|
cdl.SubVer = 0;
|
|
}
|
|
|
|
void ICodeDataLogger.DisassembleCDL(Stream s, CodeDataLog cdl)
|
|
{
|
|
Cpu.DisassembleCDL(s, cdl, memoryDomains);
|
|
}
|
|
|
|
private static Dictionary<string, int> SizesFromHuMap(IEnumerable<HuC6280.MemMapping> mm)
|
|
{
|
|
Dictionary<string, int> sizes = new Dictionary<string, int>();
|
|
foreach (var m in mm)
|
|
{
|
|
if (!sizes.ContainsKey(m.Name) || m.MaxOffs >= sizes[m.Name])
|
|
sizes[m.Name] = m.MaxOffs;
|
|
}
|
|
|
|
List<string> keys = new List<string>(sizes.Keys);
|
|
foreach (var key in keys)
|
|
{
|
|
// becase we were looking at offsets, and each bank is 8192 big, we need to add that size
|
|
sizes[key] += 8192;
|
|
}
|
|
return sizes;
|
|
}
|
|
|
|
public void ResetCounters()
|
|
{
|
|
// this should just be a public setter instead of a new method.
|
|
Frame = 0;
|
|
lagCount = 0;
|
|
isLag = false;
|
|
}
|
|
|
|
public void FrameAdvance(bool render, bool rendersound)
|
|
{
|
|
lagged = true;
|
|
DriveLightOn = false;
|
|
Frame++;
|
|
CheckSpriteLimit();
|
|
PSG.BeginFrame(Cpu.TotalExecutedCycles);
|
|
|
|
Cpu.Debug = Tracer.Enabled;
|
|
|
|
if (SuperGrafx)
|
|
VPC.ExecFrame(render);
|
|
else
|
|
VDC1.ExecFrame(render);
|
|
|
|
PSG.EndFrame(Cpu.TotalExecutedCycles);
|
|
if (TurboCD)
|
|
SoundSynchronizer.PullSamples(SoundMixer);
|
|
|
|
if (lagged)
|
|
{
|
|
lagCount++;
|
|
isLag = true;
|
|
}
|
|
else
|
|
isLag = false;
|
|
}
|
|
|
|
void CheckSpriteLimit()
|
|
{
|
|
bool spriteLimit = ForceSpriteLimit | _settings.SpriteLimit;
|
|
VDC1.PerformSpriteLimit = spriteLimit;
|
|
if (VDC2 != null)
|
|
VDC2.PerformSpriteLimit = spriteLimit;
|
|
}
|
|
|
|
public CoreComm CoreComm { get; private set; }
|
|
|
|
ISoundProvider soundProvider;
|
|
public ISoundProvider SoundProvider
|
|
{
|
|
get { return soundProvider; }
|
|
}
|
|
public ISyncSoundProvider SyncSoundProvider { get { return new FakeSyncSound(soundProvider, 735); } }
|
|
public bool StartAsyncSound() { return true; }
|
|
public void EndAsyncSound() { }
|
|
|
|
public string SystemId { get { return systemid; } }
|
|
public string Region { get; set; }
|
|
public bool DeterministicEmulation { get { return true; } }
|
|
|
|
public byte[] CloneSaveRam()
|
|
{
|
|
if (BRAM != null)
|
|
return (byte[])BRAM.Clone();
|
|
else
|
|
return null;
|
|
}
|
|
|
|
public void StoreSaveRam(byte[] data)
|
|
{
|
|
if (BRAM != null)
|
|
Array.Copy(data, BRAM, data.Length);
|
|
}
|
|
|
|
public bool SaveRamModified { get; private set; }
|
|
|
|
public bool BinarySaveStatesPreferred { get { return false; } }
|
|
public void SaveStateBinary(BinaryWriter bw) { SyncState(Serializer.CreateBinaryWriter(bw)); }
|
|
public void LoadStateBinary(BinaryReader br) { SyncState(Serializer.CreateBinaryReader(br)); }
|
|
public void SaveStateText(TextWriter tw) { SyncState(Serializer.CreateTextWriter(tw)); }
|
|
public void LoadStateText(TextReader tr) { SyncState(Serializer.CreateTextReader(tr)); }
|
|
|
|
void SyncState(Serializer ser)
|
|
{
|
|
ser.BeginSection("PCEngine");
|
|
Cpu.SyncState(ser);
|
|
VCE.SyncState(ser);
|
|
VDC1.SyncState(ser, 1);
|
|
PSG.SyncState(ser);
|
|
|
|
if (SuperGrafx)
|
|
{
|
|
VPC.SyncState(ser);
|
|
VDC2.SyncState(ser, 2);
|
|
}
|
|
|
|
if (TurboCD)
|
|
{
|
|
ADPCM.SyncState(ser);
|
|
CDAudio.SyncState(ser);
|
|
SCSI.SyncState(ser);
|
|
|
|
ser.Sync("CDRAM", ref CDRam, false);
|
|
if (SuperRam != null)
|
|
ser.Sync("SuperRAM", ref SuperRam, false);
|
|
if (ArcadeCard)
|
|
ArcadeCardSyncState(ser);
|
|
}
|
|
|
|
ser.Sync("RAM", ref Ram, false);
|
|
ser.Sync("IOBuffer", ref IOBuffer);
|
|
ser.Sync("CdIoPorts", ref CdIoPorts, false);
|
|
ser.Sync("BramLocked", ref BramLocked);
|
|
|
|
ser.Sync("Frame", ref frame);
|
|
ser.Sync("Lag", ref lagCount);
|
|
ser.Sync("IsLag", ref isLag);
|
|
if (Cpu.ReadMemory21 == ReadMemorySF2)
|
|
ser.Sync("SF2MapperLatch", ref SF2MapperLatch);
|
|
if (PopulousRAM != null)
|
|
ser.Sync("PopulousRAM", ref PopulousRAM, false);
|
|
if (BRAM != null)
|
|
ser.Sync("BRAM", ref BRAM, false);
|
|
|
|
ser.EndSection();
|
|
}
|
|
|
|
byte[] stateBuffer;
|
|
public byte[] SaveStateBinary()
|
|
{
|
|
if (stateBuffer == null)
|
|
{
|
|
var stream = new MemoryStream();
|
|
var writer = new BinaryWriter(stream);
|
|
SaveStateBinary(writer);
|
|
writer.Flush();
|
|
stateBuffer = stream.ToArray();
|
|
writer.Close();
|
|
return stateBuffer;
|
|
}
|
|
else
|
|
{
|
|
var stream = new MemoryStream(stateBuffer);
|
|
var writer = new BinaryWriter(stream);
|
|
SaveStateBinary(writer);
|
|
writer.Flush();
|
|
writer.Close();
|
|
return stateBuffer;
|
|
}
|
|
}
|
|
|
|
void SetupMemoryDomains()
|
|
{
|
|
var domains = new List<MemoryDomain>(10);
|
|
int mainmemorymask = Ram.Length - 1;
|
|
var MainMemoryDomain = new MemoryDomain("Main Memory", Ram.Length, MemoryDomain.Endian.Little,
|
|
addr => Ram[addr],
|
|
(addr, value) => Ram[addr] = value,
|
|
byteSize: 2);
|
|
domains.Add(MainMemoryDomain);
|
|
|
|
var SystemBusDomain = new MemoryDomain("System Bus (21 bit)", 0x200000, MemoryDomain.Endian.Little,
|
|
(addr) =>
|
|
{
|
|
if (addr < 0 || addr >= 0x200000)
|
|
throw new ArgumentOutOfRangeException();
|
|
return Cpu.ReadMemory21((int)addr);
|
|
},
|
|
(addr, value) =>
|
|
{
|
|
if (addr < 0 || addr >= 0x200000)
|
|
throw new ArgumentOutOfRangeException();
|
|
Cpu.WriteMemory21((int)addr, value);
|
|
},
|
|
byteSize: 2);
|
|
domains.Add(SystemBusDomain);
|
|
|
|
var CpuBusDomain = new MemoryDomain("System Bus", 0x10000, MemoryDomain.Endian.Little,
|
|
(addr) =>
|
|
{
|
|
if (addr < 0 || addr >= 0x10000)
|
|
throw new ArgumentOutOfRangeException();
|
|
return Cpu.ReadMemory((ushort)addr);
|
|
},
|
|
(addr, value) =>
|
|
{
|
|
if (addr < 0 || addr >= 0x10000)
|
|
throw new ArgumentOutOfRangeException();
|
|
Cpu.WriteMemory((ushort)addr, value);
|
|
},
|
|
byteSize: 2);
|
|
domains.Add(CpuBusDomain);
|
|
|
|
var RomDomain = new MemoryDomain("ROM", RomLength, MemoryDomain.Endian.Little,
|
|
addr => RomData[addr],
|
|
(addr, value) => RomData[addr] = value,
|
|
byteSize: 2);
|
|
domains.Add(RomDomain);
|
|
|
|
if (BRAM != null)
|
|
{
|
|
var BRAMMemoryDomain = new MemoryDomain("Battery RAM", Ram.Length, MemoryDomain.Endian.Little,
|
|
addr => BRAM[addr],
|
|
(addr, value) => BRAM[addr] = value,
|
|
byteSize: 2);
|
|
domains.Add(BRAMMemoryDomain);
|
|
}
|
|
|
|
if (TurboCD)
|
|
{
|
|
var CDRamMemoryDomain = new MemoryDomain("TurboCD RAM", CDRam.Length, MemoryDomain.Endian.Little,
|
|
addr => CDRam[addr],
|
|
(addr, value) => CDRam[addr] = value,
|
|
byteSize: 2);
|
|
domains.Add(CDRamMemoryDomain);
|
|
|
|
var AdpcmMemoryDomain = new MemoryDomain("ADPCM RAM", ADPCM.RAM.Length, MemoryDomain.Endian.Little,
|
|
addr => ADPCM.RAM[addr],
|
|
(addr, value) => ADPCM.RAM[addr] = value,
|
|
byteSize: 2);
|
|
domains.Add(AdpcmMemoryDomain);
|
|
|
|
if (SuperRam != null)
|
|
{
|
|
var SuperRamMemoryDomain = new MemoryDomain("Super System Card RAM", SuperRam.Length, MemoryDomain.Endian.Little,
|
|
addr => SuperRam[addr],
|
|
(addr, value) => SuperRam[addr] = value,
|
|
byteSize: 2);
|
|
domains.Add(SuperRamMemoryDomain);
|
|
}
|
|
}
|
|
|
|
if (ArcadeCard)
|
|
{
|
|
var ArcadeRamMemoryDomain = new MemoryDomain("Arcade Card RAM", ArcadeRam.Length, MemoryDomain.Endian.Little,
|
|
addr => ArcadeRam[addr],
|
|
(addr, value) => ArcadeRam[addr] = value,
|
|
byteSize: 2);
|
|
domains.Add(ArcadeRamMemoryDomain);
|
|
}
|
|
|
|
if (PopulousRAM != null)
|
|
{
|
|
var PopulusRAMDomain = new MemoryDomain("Cart Battery RAM", PopulousRAM.Length, MemoryDomain.Endian.Little,
|
|
addr => PopulousRAM[addr],
|
|
(addr, value) => PopulousRAM[addr] = value,
|
|
byteSize: 2);
|
|
domains.Add(PopulusRAMDomain);
|
|
}
|
|
|
|
memoryDomains = new MemoryDomainList(domains);
|
|
(ServiceProvider as BasicServiceProvider).Register<IMemoryDomains>(memoryDomains);
|
|
}
|
|
|
|
MemoryDomainList memoryDomains;
|
|
|
|
public IDictionary<string, RegisterValue> GetCpuFlagsAndRegisters()
|
|
{
|
|
return new Dictionary<string, RegisterValue>
|
|
{
|
|
{ "A", Cpu.A },
|
|
{ "X", Cpu.X },
|
|
{ "Y", Cpu.Y },
|
|
{ "PC", Cpu.PC },
|
|
{ "S", Cpu.S },
|
|
{ "MPR-0", Cpu.MPR[0] },
|
|
{ "MPR-1", Cpu.MPR[1] },
|
|
{ "MPR-2", Cpu.MPR[2] },
|
|
{ "MPR-3", Cpu.MPR[3] },
|
|
{ "MPR-4", Cpu.MPR[4] },
|
|
{ "MPR-5", Cpu.MPR[5] },
|
|
{ "MPR-6", Cpu.MPR[6] },
|
|
{ "MPR-7", Cpu.MPR[7] }
|
|
};
|
|
}
|
|
|
|
public bool CanStep(StepType type) { return false; }
|
|
|
|
[FeatureNotImplemented]
|
|
public void Step(StepType type) { throw new NotImplementedException(); }
|
|
|
|
[FeatureNotImplemented]
|
|
public void SetCpuRegister(string register, int value)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (disc != null)
|
|
disc.Dispose();
|
|
}
|
|
|
|
public PCESettings _settings;
|
|
private PCESyncSettings _syncSettings;
|
|
|
|
public PCESettings GetSettings() { return _settings.Clone(); }
|
|
public PCESyncSettings GetSyncSettings() { return _syncSettings.Clone(); }
|
|
public bool PutSettings(PCESettings o)
|
|
{
|
|
bool ret;
|
|
if (o.ArcadeCardRewindHack != _settings.ArcadeCardRewindHack ||
|
|
o.EqualizeVolume != _settings.EqualizeVolume)
|
|
ret = true;
|
|
else
|
|
ret = false;
|
|
|
|
_settings = o;
|
|
return ret;
|
|
}
|
|
|
|
public bool PutSyncSettings(PCESyncSettings o)
|
|
{
|
|
bool ret = PCESyncSettings.NeedsReboot(o, _syncSettings);
|
|
_syncSettings = o;
|
|
// SetControllerButtons(); // not safe to change the controller during emulation, so instead make it a reboot event
|
|
return ret;
|
|
}
|
|
|
|
public class PCESettings
|
|
{
|
|
public bool ShowBG1 = true;
|
|
public bool ShowOBJ1 = true;
|
|
public bool ShowBG2 = true;
|
|
public bool ShowOBJ2 = true;
|
|
|
|
// these three require core reboot to use
|
|
public bool SpriteLimit = false;
|
|
public bool EqualizeVolume = false;
|
|
public bool ArcadeCardRewindHack = true;
|
|
|
|
public PCESettings Clone()
|
|
{
|
|
return (PCESettings)MemberwiseClone();
|
|
}
|
|
}
|
|
|
|
public class PCESyncSettings
|
|
{
|
|
public ControllerSetting[] Controllers =
|
|
{
|
|
new ControllerSetting { IsConnected = true },
|
|
new ControllerSetting { IsConnected = false },
|
|
new ControllerSetting { IsConnected = false },
|
|
new ControllerSetting { IsConnected = false },
|
|
new ControllerSetting { IsConnected = false }
|
|
};
|
|
|
|
public PCESyncSettings Clone()
|
|
{
|
|
var ret = new PCESyncSettings();
|
|
for (int i = 0; i < Controllers.Length; i++)
|
|
{
|
|
ret.Controllers[i].IsConnected = Controllers[i].IsConnected;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
public class ControllerSetting
|
|
{
|
|
public bool IsConnected { get; set; }
|
|
}
|
|
|
|
public static bool NeedsReboot(PCESyncSettings x, PCESyncSettings y)
|
|
{
|
|
for (int i = 0; i < x.Controllers.Length; i++)
|
|
{
|
|
if (x.Controllers[i].IsConnected != y.Controllers[i].IsConnected)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|