328 lines
10 KiB
C#
328 lines
10 KiB
C#
![]() |
using System;
|
|||
|
|
|||
|
|
|||
|
using BizHawk.Common;
|
|||
|
|
|||
|
using System.Runtime.InteropServices;
|
|||
|
using System.Security;
|
|||
|
|
|||
|
using System.Reflection;
|
|||
|
using System.Reflection.Emit;
|
|||
|
|
|||
|
namespace BizHawk.Emulation.Cores.Components.M6502
|
|||
|
{
|
|||
|
[StructLayout(LayoutKind.Explicit)] // LayoutKind.Sequential doesn't work right on the managed form of non-blittable structs
|
|||
|
public sealed partial class MOS6502X
|
|||
|
{
|
|||
|
[FieldOffset(0)]
|
|||
|
private int _anchor;
|
|||
|
|
|||
|
[SuppressUnmanagedCodeSecurity]
|
|||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|||
|
public delegate byte ReadDel(ushort addr);
|
|||
|
[SuppressUnmanagedCodeSecurity]
|
|||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|||
|
public delegate void WriteDel(ushort addr, byte val);
|
|||
|
[SuppressUnmanagedCodeSecurity]
|
|||
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|||
|
public delegate void AddrDel(ushort addr);
|
|||
|
|
|||
|
// interface
|
|||
|
[FieldOffset(16)]private ReadDel _ReadMemory;
|
|||
|
[FieldOffset(20)]private ReadDel _DummyReadMemory;
|
|||
|
[FieldOffset(24)]private ReadDel _PeekMemory;
|
|||
|
[FieldOffset(28)]private WriteDel _WriteMemory;
|
|||
|
[FieldOffset(32)]private AddrDel _OnExecFetch; // this only calls when the first byte of an instruction is fetched.
|
|||
|
|
|||
|
public ReadDel ReadMemory
|
|||
|
{
|
|||
|
set { _rmp = Marshal.GetFunctionPointerForDelegate(value); _ReadMemory = value; }
|
|||
|
get { return _ReadMemory; }
|
|||
|
}
|
|||
|
public ReadDel DummyReadMemory
|
|||
|
{
|
|||
|
set { _dmp = Marshal.GetFunctionPointerForDelegate(value); _DummyReadMemory = value; }
|
|||
|
get { return _DummyReadMemory; }
|
|||
|
}
|
|||
|
public ReadDel PeekMemory
|
|||
|
{
|
|||
|
set { _pmp = Marshal.GetFunctionPointerForDelegate(value); _PeekMemory = value; }
|
|||
|
get { return _PeekMemory; }
|
|||
|
}
|
|||
|
public WriteDel WriteMemory
|
|||
|
{
|
|||
|
set { _wmp = Marshal.GetFunctionPointerForDelegate(value); _WriteMemory = value; }
|
|||
|
get { return _WriteMemory; }
|
|||
|
}
|
|||
|
public AddrDel OnExecFetch
|
|||
|
{
|
|||
|
set { _exp = IntPtr.Zero; /* Marshal.GetFunctionPointerForDelegate(value); */ _OnExecFetch = value; }
|
|||
|
get { return _OnExecFetch; }
|
|||
|
}
|
|||
|
|
|||
|
[FieldOffset(36)]public Action<string> TraceCallback; // TODOOO
|
|||
|
|
|||
|
[FieldOffset(40)]private IntPtr _rmp;
|
|||
|
[FieldOffset(44)]private IntPtr _dmp;
|
|||
|
[FieldOffset(48)]private IntPtr _pmp;
|
|||
|
[FieldOffset(52)]private IntPtr _wmp;
|
|||
|
[FieldOffset(56)]private IntPtr _exp;
|
|||
|
|
|||
|
// config
|
|||
|
[FieldOffset(60)]public bool BCD_Enabled = true;
|
|||
|
[FieldOffset(61)]public bool debug = false;
|
|||
|
|
|||
|
// state
|
|||
|
[FieldOffset(62)]public byte A;
|
|||
|
[FieldOffset(63)]public byte X;
|
|||
|
[FieldOffset(64)]public byte Y;
|
|||
|
//public byte P;
|
|||
|
/// <summary>Carry Flag</summary>
|
|||
|
[FieldOffset(65)]public bool FlagC;
|
|||
|
/// <summary>Zero Flag</summary>
|
|||
|
[FieldOffset(66)]public bool FlagZ;
|
|||
|
/// <summary>Interrupt Disable Flag</summary>
|
|||
|
[FieldOffset(67)]public bool FlagI;
|
|||
|
/// <summary>Decimal Mode Flag</summary>
|
|||
|
[FieldOffset(68)]public bool FlagD;
|
|||
|
/// <summary>Break Flag</summary>
|
|||
|
[FieldOffset(69)]public bool FlagB;
|
|||
|
/// <summary>T... Flag</summary>
|
|||
|
[FieldOffset(70)]public bool FlagT;
|
|||
|
/// <summary>Overflow Flag</summary>
|
|||
|
[FieldOffset(71)]public bool FlagV;
|
|||
|
/// <summary>Negative Flag</summary>
|
|||
|
[FieldOffset(72)]public bool FlagN;
|
|||
|
|
|||
|
[FieldOffset(74)]public ushort PC;
|
|||
|
[FieldOffset(76)]public byte S;
|
|||
|
|
|||
|
[FieldOffset(77)]public bool IRQ;
|
|||
|
[FieldOffset(78)]public bool NMI;
|
|||
|
[FieldOffset(79)]public bool RDY;
|
|||
|
|
|||
|
[FieldOffset(80)]public int TotalExecutedCycles;
|
|||
|
|
|||
|
//opcode bytes.. theoretically redundant with the temp variables? who knows.
|
|||
|
[FieldOffset(84)]int opcode;
|
|||
|
[FieldOffset(88)]byte opcode2;
|
|||
|
[FieldOffset(89)]byte opcode3;
|
|||
|
|
|||
|
[FieldOffset(92)]int ea;
|
|||
|
[FieldOffset(96)]int alu_temp; //cpu internal temp variables
|
|||
|
[FieldOffset(100)]int mi; //microcode index
|
|||
|
[FieldOffset(104)]bool iflag_pending; //iflag must be stored after it is checked in some cases (CLI and SEI).
|
|||
|
[FieldOffset(105)]bool rdy_freeze; //true if the CPU must be frozen
|
|||
|
|
|||
|
//tracks whether an interrupt condition has popped up recently.
|
|||
|
//not sure if this is real or not but it helps with the branch_irq_hack
|
|||
|
[FieldOffset(106)]bool interrupt_pending;
|
|||
|
[FieldOffset(107)]bool branch_irq_hack; //see Uop.RelBranch_Stage3 for more details
|
|||
|
|
|||
|
// transient state
|
|||
|
[FieldOffset(108)]byte value8;
|
|||
|
[FieldOffset(109)]byte temp8;
|
|||
|
[FieldOffset(110)]ushort value16;
|
|||
|
[FieldOffset(112)]bool branch_taken = false;
|
|||
|
[FieldOffset(113)]bool my_iflag;
|
|||
|
[FieldOffset(114)]bool booltemp;
|
|||
|
[FieldOffset(116)]int tempint;
|
|||
|
[FieldOffset(120)]int lo;
|
|||
|
[FieldOffset(124)]int hi;
|
|||
|
|
|||
|
public byte P
|
|||
|
{
|
|||
|
// NVTB DIZC
|
|||
|
get
|
|||
|
{
|
|||
|
byte ret = 0;
|
|||
|
if (FlagC) ret |= 1;
|
|||
|
if (FlagZ) ret |= 2;
|
|||
|
if (FlagI) ret |= 4;
|
|||
|
if (FlagD) ret |= 8;
|
|||
|
if (FlagB) ret |= 16;
|
|||
|
if (FlagT) ret |= 32;
|
|||
|
if (FlagV) ret |= 64;
|
|||
|
if (FlagN) ret |= 128;
|
|||
|
return ret;
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
FlagC = (value & 1) != 0;
|
|||
|
FlagZ = (value & 2) != 0;
|
|||
|
FlagI = (value & 4) != 0;
|
|||
|
FlagD = (value & 8) != 0;
|
|||
|
FlagB = (value & 16) != 0;
|
|||
|
FlagT = (value & 32) != 0;
|
|||
|
FlagV = (value & 64) != 0;
|
|||
|
FlagN = (value & 128) != 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public MOS6502X()
|
|||
|
{
|
|||
|
InitOpcodeHandlers();
|
|||
|
InitNative();
|
|||
|
Reset();
|
|||
|
}
|
|||
|
|
|||
|
public void SyncState(Serializer ser)
|
|||
|
{
|
|||
|
ser.BeginSection("MOS6502X");
|
|||
|
ser.Sync("A", ref A);
|
|||
|
ser.Sync("X", ref X);
|
|||
|
ser.Sync("Y", ref Y);
|
|||
|
{
|
|||
|
byte tmp = P;
|
|||
|
ser.Sync("P", ref tmp);
|
|||
|
P = tmp;
|
|||
|
}
|
|||
|
ser.Sync("PC", ref PC);
|
|||
|
ser.Sync("S", ref S);
|
|||
|
ser.Sync("NMI", ref NMI);
|
|||
|
ser.Sync("IRQ", ref IRQ);
|
|||
|
ser.Sync("RDY", ref RDY);
|
|||
|
ser.Sync("TotalExecutedCycles", ref TotalExecutedCycles);
|
|||
|
ser.Sync("opcode", ref opcode);
|
|||
|
ser.Sync("opcode2", ref opcode2);
|
|||
|
ser.Sync("opcode3", ref opcode3);
|
|||
|
ser.Sync("ea", ref ea);
|
|||
|
ser.Sync("alu_temp", ref alu_temp);
|
|||
|
ser.Sync("mi", ref mi);
|
|||
|
ser.Sync("iflag_pending", ref iflag_pending);
|
|||
|
ser.Sync("rdy_freeze", ref rdy_freeze);
|
|||
|
ser.Sync("interrupt_pending", ref interrupt_pending);
|
|||
|
ser.Sync("branch_irq_hack", ref branch_irq_hack);
|
|||
|
ser.EndSection();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
public void Reset()
|
|||
|
{
|
|||
|
A = 0;
|
|||
|
X = 0;
|
|||
|
Y = 0;
|
|||
|
P = 0;
|
|||
|
S = 0;
|
|||
|
PC = 0;
|
|||
|
TotalExecutedCycles = 0;
|
|||
|
mi = 0;
|
|||
|
opcode = 256;
|
|||
|
iflag_pending = true;
|
|||
|
RDY = true;
|
|||
|
}
|
|||
|
|
|||
|
public void NESSoftReset()
|
|||
|
{
|
|||
|
opcode = VOP_RESET;
|
|||
|
mi = 0;
|
|||
|
iflag_pending = true;
|
|||
|
FlagI = true;
|
|||
|
}
|
|||
|
|
|||
|
public string State(bool disassemble = true)
|
|||
|
{
|
|||
|
int notused;
|
|||
|
string a = string.Format("{0:X4} {1:X2} {2} ", PC, _PeekMemory(PC), disassemble ? Disassemble(PC, out notused) : "---").PadRight(30);
|
|||
|
string b = string.Format("A:{0:X2} X:{1:X2} Y:{2:X2} P:{3:X2} SP:{4:X2} Cy:{5}", A, X, Y, P, S, TotalExecutedCycles);
|
|||
|
string val = a + b + " ";
|
|||
|
if (FlagN) val = val + "N";
|
|||
|
if (FlagV) val = val + "V";
|
|||
|
if (FlagT) val = val + "T";
|
|||
|
if (FlagB) val = val + "B";
|
|||
|
if (FlagD) val = val + "D";
|
|||
|
if (FlagI) val = val + "I";
|
|||
|
if (FlagZ) val = val + "Z";
|
|||
|
if (FlagC) val = val + "C";
|
|||
|
return val;
|
|||
|
}
|
|||
|
|
|||
|
public string TraceState()
|
|||
|
{
|
|||
|
// only disassemble when we're at the beginning of an opcode
|
|||
|
return State(opcode == VOP_Fetch1 || Microcode[opcode][mi] >= Uop.End);
|
|||
|
}
|
|||
|
|
|||
|
public const ushort NMIVector = 0xFFFA;
|
|||
|
public const ushort ResetVector = 0xFFFC;
|
|||
|
public const ushort BRKVector = 0xFFFE;
|
|||
|
public const ushort IRQVector = 0xFFFE;
|
|||
|
|
|||
|
enum ExceptionType
|
|||
|
{
|
|||
|
BRK, NMI, IRQ
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#region native interop
|
|||
|
|
|||
|
|
|||
|
private void InitNative()
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
[SuppressUnmanagedCodeSecurity]
|
|||
|
[DllImport("6502XXX.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint="_ZN3CPU10ExecuteOneEv")]
|
|||
|
//[DllImport("6502XXX.dll", CallingConvention = CallingConvention.ThisCall, EntryPoint="?ExecuteOne@CPU@@QAEXXZ")]
|
|||
|
private static extern void ExecuteOneNativeInternal(IntPtr o);
|
|||
|
|
|||
|
public unsafe void ExecuteOneNative()
|
|||
|
{
|
|||
|
fixed (int* p = &_anchor)
|
|||
|
{
|
|||
|
ExecuteOneNativeInternal((IntPtr)p);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
public void SetCallbacks
|
|||
|
(
|
|||
|
ReadDel ReadMemory,
|
|||
|
ReadDel DummyReadMemory,
|
|||
|
ReadDel PeekMemory,
|
|||
|
WriteDel WriteMemory
|
|||
|
)
|
|||
|
{
|
|||
|
this.ReadMemory = ReadMemory;
|
|||
|
this.DummyReadMemory = DummyReadMemory;
|
|||
|
this.PeekMemory = PeekMemory;
|
|||
|
this.WriteMemory = WriteMemory;
|
|||
|
}
|
|||
|
|
|||
|
public ushort ReadWord(ushort address)
|
|||
|
{
|
|||
|
byte l = _ReadMemory(address);
|
|||
|
byte h = _ReadMemory(++address);
|
|||
|
return (ushort)((h << 8) | l);
|
|||
|
}
|
|||
|
|
|||
|
public ushort PeekWord(ushort address)
|
|||
|
{
|
|||
|
byte l = _PeekMemory(address);
|
|||
|
byte h = _PeekMemory(++address);
|
|||
|
return (ushort)((h << 8) | l);
|
|||
|
}
|
|||
|
|
|||
|
private static readonly byte[] TableNZ =
|
|||
|
{
|
|||
|
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|||
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|||
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|||
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|||
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|||
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|||
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|||
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|||
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
|
|||
|
};
|
|||
|
}
|
|||
|
}
|