242 lines
6.0 KiB
C#
242 lines
6.0 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
using System.Collections.Concurrent;
|
|
|
|
namespace BizHawk.Emulation.CPUs.M6502
|
|
{
|
|
/// <summary>
|
|
/// maintains a managed 6502X and an unmanaged 6502X, running them alongside and ensuring consistency
|
|
/// by taking savestates every cycle (!). slow.
|
|
/// </summary>
|
|
public class MOS6502XDouble
|
|
{
|
|
MOS6502X m;
|
|
MOS6502X_CPP n;
|
|
|
|
public MOS6502XDouble(Action<System.Runtime.InteropServices.GCHandle> DisposeBuilder)
|
|
{
|
|
m = new MOS6502X(DisposeBuilder);
|
|
n = new MOS6502X_CPP(DisposeBuilder);
|
|
BCD_Enabled = true;
|
|
m.SetCallbacks(
|
|
delegate(ushort addr)
|
|
{
|
|
byte ret = ReadMemory(addr);
|
|
reads.Enqueue(ret);
|
|
return ret;
|
|
},
|
|
delegate(ushort addr)
|
|
{
|
|
byte ret = DummyReadMemory(addr);
|
|
reads.Enqueue(ret);
|
|
return ret;
|
|
},
|
|
delegate(ushort addr)
|
|
{
|
|
byte ret = PeekMemory(addr);
|
|
reads.Enqueue(ret);
|
|
return ret;
|
|
},
|
|
delegate(ushort addr, byte value)
|
|
{
|
|
writes.Enqueue(value);
|
|
WriteMemory(addr, value);
|
|
}, DisposeBuilder);
|
|
n.SetCallbacks(
|
|
delegate(ushort addr)
|
|
{
|
|
if (reads.Count > 0)
|
|
return reads.Dequeue();
|
|
else
|
|
{
|
|
PreCrash();
|
|
throw new Exception("native did extra read!");
|
|
}
|
|
},
|
|
delegate(ushort addr)
|
|
{
|
|
if (reads.Count > 0)
|
|
return reads.Dequeue();
|
|
else
|
|
{
|
|
PreCrash();
|
|
throw new Exception("native did extra read!");
|
|
}
|
|
},
|
|
delegate(ushort addr, byte value)
|
|
{
|
|
if (writes.Count > 0)
|
|
{
|
|
byte test = writes.Dequeue();
|
|
if (test != value)
|
|
{
|
|
PreCrash();
|
|
throw new Exception(string.Format("writes were different! managed {0} native {1}", test, value));
|
|
}
|
|
// ignore because the write already happened
|
|
}
|
|
else
|
|
{
|
|
PreCrash();
|
|
throw new Exception("native did extra write!");
|
|
}
|
|
}, DisposeBuilder);
|
|
|
|
SyncUp();
|
|
}
|
|
Queue<byte> reads = new Queue<byte>();
|
|
Queue<byte> writes = new Queue<byte>();
|
|
|
|
private bool _BCD_Enabled;
|
|
public bool BCD_Enabled { get { return _BCD_Enabled; } set { _BCD_Enabled = value; m.BCD_Enabled = value; n.BCD_Enabled = value; } }
|
|
public bool debug { get; private set; }
|
|
public bool throw_unhandled { get; private set; }
|
|
|
|
public byte A { get; private set; }
|
|
public byte X { get; private set; }
|
|
public byte Y { get; private set; }
|
|
byte _P;
|
|
public byte P { get { return _P; } set { _P = value; m.P = value; n.P = value; SyncUp(); } }
|
|
ushort _PC;
|
|
public ushort PC { get { return _PC; } set { _PC = value; m.PC = value; n.PC = value; SyncUp(); } }
|
|
byte _S;
|
|
public byte S { get { return _S; } set { _S = value; m.S = value; n.S = value; SyncUp(); } }
|
|
|
|
bool _IRQ;
|
|
public bool IRQ { get { return _IRQ; } set { _IRQ = value; m.IRQ = value; n.IRQ = value; } }
|
|
bool _NMI;
|
|
public bool NMI { get { return _NMI; } set { _NMI = value; m.NMI = value; n.NMI = value; } }
|
|
|
|
public int TotalExecutedCycles { get; private set; }
|
|
|
|
public Func<ushort, byte> ReadMemory; //{ set { m.ReadMemory = value; n.ReadMemory = value; } }
|
|
public Func<ushort, byte> DummyReadMemory; //{ set { m.DummyReadMemory = value; n.DummyReadMemory = value; } }
|
|
public Func<ushort, byte> PeekMemory; //{ set { m.ReadMemory = value; n.ReadMemory = value; } }
|
|
public Action<ushort, byte> WriteMemory; //{ set { m.WriteMemory = value; n.WriteMemory = value; } }
|
|
|
|
public void SetCallbacks
|
|
(
|
|
Func<ushort, byte> ReadMemory,
|
|
Func<ushort, byte> DummyReadMemory,
|
|
Func<ushort, byte> PeekMemory,
|
|
Action<ushort, byte> WriteMemory,
|
|
Action<System.Runtime.InteropServices.GCHandle> DisposeBuilder
|
|
)
|
|
{
|
|
this.ReadMemory = ReadMemory;
|
|
this.DummyReadMemory = DummyReadMemory;
|
|
this.PeekMemory = PeekMemory;
|
|
this.WriteMemory = WriteMemory;
|
|
}
|
|
|
|
|
|
string oldleft = "";
|
|
string oldright = "";
|
|
void PreCrash()
|
|
{
|
|
System.Windows.Forms.MessageBox.Show(string.Format("about to crash! cached:\n==managed==\n{0}\n==native==\n{1}\n", oldleft, oldright));
|
|
}
|
|
void TestQueue()
|
|
{
|
|
if (reads.Count > 0)
|
|
{
|
|
PreCrash();
|
|
throw new Exception("managed did extra read!");
|
|
}
|
|
if (writes.Count > 0)
|
|
{
|
|
PreCrash();
|
|
throw new Exception("managed did extra write!");
|
|
}
|
|
}
|
|
|
|
void SyncUp()
|
|
{
|
|
TestQueue();
|
|
StringWriter sm = new StringWriter();
|
|
Serializer ssm = new Serializer(sm);
|
|
m.SyncState(ssm);
|
|
sm.Flush();
|
|
|
|
StringWriter sn = new StringWriter();
|
|
Serializer ssn = new Serializer(sn);
|
|
n.SyncState(ssn);
|
|
sn.Flush();
|
|
|
|
string sssm = sm.ToString();
|
|
string sssn = sn.ToString();
|
|
|
|
if (sssm != sssn)
|
|
{
|
|
PreCrash();
|
|
throw new Exception(string.Format("save mismatch!\n==managed==\n{0}\n==native==\n{1}\n", sssm, sssn));
|
|
}
|
|
A = m.A;
|
|
X = m.X;
|
|
Y = m.Y;
|
|
_P = m.P;
|
|
_PC = m.PC;
|
|
_S = m.S;
|
|
_IRQ = m.IRQ;
|
|
_NMI = m.NMI;
|
|
oldleft = sssm;
|
|
oldright = sssn;
|
|
}
|
|
|
|
void MtoN()
|
|
{
|
|
StringWriter sm = new StringWriter();
|
|
Serializer ssm = new Serializer(sm);
|
|
m.SyncState(ssm);
|
|
sm.Flush();
|
|
StringReader sn = new StringReader(sm.ToString());
|
|
Serializer ssn = new Serializer(sn);
|
|
n.SyncState(ssn);
|
|
}
|
|
|
|
public string State()
|
|
{
|
|
SyncUp();
|
|
return m.State();
|
|
}
|
|
|
|
public void NESSoftReset()
|
|
{
|
|
m.NESSoftReset();
|
|
n.NESSoftReset();
|
|
SyncUp();
|
|
}
|
|
|
|
public void ExecuteOne()
|
|
{
|
|
m.ExecuteOne();
|
|
n.ExecuteOne();
|
|
SyncUp();
|
|
}
|
|
|
|
public void SyncState(Serializer ser)
|
|
{
|
|
// doesn't work quite as expected because you can't rewind a ser
|
|
SyncUp();
|
|
if (ser.IsWriter)
|
|
{
|
|
// since SyncUp() guarantees that states are the same, we only need write one
|
|
m.SyncState(ser);
|
|
}
|
|
else
|
|
{
|
|
// daisy chain the state
|
|
m.SyncState(ser);
|
|
MtoN();
|
|
}
|
|
SyncUp();
|
|
}
|
|
|
|
public string Disassemble(ushort pc, out int bytesToAdvance) { bytesToAdvance = 1; return "FOOBAR"; }
|
|
|
|
}
|
|
}
|