using System; using System.Globalization; using System.IO; namespace BizHawk.Emulation.CPUs.H6280 { public sealed partial class HuC6280 { public HuC6280() { Reset(); } public void Reset() { A = 0; X = 0; Y = 0; //P = 0x14; // Set I and B P = 0x04; // Set I S = 0; PC = 0; PendingCycles = 0; TotalExecutedCycles = 0; LagIFlag = true; LowSpeed = true; } public void ResetPC() { PC = ReadWord(ResetVector); } // ==== CPU State ==== public byte A; public byte X; public byte Y; public byte P; public ushort PC; public byte S; public byte[] MPR = new byte[8]; public bool LagIFlag; public bool IRQ1Assert; public bool IRQ2Assert; public bool TimerAssert; public byte IRQControlByte, IRQNextControlByte; public long TotalExecutedCycles; public int PendingCycles; public bool LowSpeed; private bool InBlockTransfer = false; private ushort btFrom; private ushort btTo; private ushort btLen; private int btAlternator; // -- Timer Support -- public int TimerTickCounter; public byte TimerReloadValue; public byte TimerValue; public bool TimerEnabled; public void SaveStateText(TextWriter writer) { writer.WriteLine("[HuC6280]"); writer.WriteLine("A {0:X2}", A); writer.WriteLine("X {0:X2}", X); writer.WriteLine("Y {0:X2}", Y); writer.WriteLine("P {0:X2}", P); writer.WriteLine("PC {0:X4}", PC); writer.WriteLine("S {0:X2}", S); writer.Write("MPR "); MPR.SaveAsHex(writer); writer.WriteLine("IRQ1Assert {0}", IRQ1Assert); writer.WriteLine("TimerAssert {0}", TimerAssert); writer.WriteLine("IRQControlByte {0:X2}", IRQControlByte); writer.WriteLine("IRQNextControlByte {0:X2}", IRQNextControlByte); writer.WriteLine("ExecutedCycles {0}", TotalExecutedCycles); writer.WriteLine("PendingCycles {0}", PendingCycles); writer.WriteLine("LowSpeed {0}", LowSpeed); writer.WriteLine("TimerTickCounter {0}", TimerTickCounter); writer.WriteLine("TimerReloadValue {0}", TimerReloadValue); writer.WriteLine("TimerValue {0}", TimerValue); writer.WriteLine("TimerEnabled {0}", TimerEnabled); writer.WriteLine("InBlockTransfer {0}", InBlockTransfer); writer.WriteLine("BTFrom {0}", btFrom); writer.WriteLine("BTTo {0}", btTo); writer.WriteLine("BTLen {0}", btLen); writer.WriteLine("BTAlternator {0}", btAlternator); writer.WriteLine("[/HuC6280]\n"); } public void LoadStateText(TextReader reader) { while (true) { string[] args = reader.ReadLine().Split(' '); if (args[0].Trim() == "") continue; if (args[0] == "[/HuC6280]") break; if (args[0] == "A") A = byte.Parse(args[1], NumberStyles.HexNumber); else if (args[0] == "X") X = byte.Parse(args[1], NumberStyles.HexNumber); else if (args[0] == "Y") Y = byte.Parse(args[1], NumberStyles.HexNumber); else if (args[0] == "P") P = byte.Parse(args[1], NumberStyles.HexNumber); else if (args[0] == "PC") PC = ushort.Parse(args[1], NumberStyles.HexNumber); else if (args[0] == "S") S = byte.Parse(args[1], NumberStyles.HexNumber); else if (args[0] == "MPR") MPR.ReadFromHex(args[1]); else if (args[0] == "IRQ1Assert") IRQ1Assert = bool.Parse(args[1]); else if (args[0] == "TimerAssert") TimerAssert = bool.Parse(args[1]); else if (args[0] == "IRQControlByte") IRQControlByte = byte.Parse(args[1], NumberStyles.HexNumber); else if (args[0] == "IRQNextControlByte") IRQNextControlByte = byte.Parse(args[1], NumberStyles.HexNumber); else if (args[0] == "ExecutedCycles") TotalExecutedCycles = long.Parse(args[1]); else if (args[0] == "PendingCycles") PendingCycles = int.Parse(args[1]); else if (args[0] == "LowSpeed") LowSpeed = bool.Parse(args[1]); else if (args[0] == "TimerTickCounter") TimerTickCounter = int.Parse(args[1]); else if (args[0] == "TimerReloadValue") TimerReloadValue = byte.Parse(args[1]); else if (args[0] == "TimerValue") TimerValue = byte.Parse(args[1]); else if (args[0] == "TimerEnabled") TimerEnabled = bool.Parse(args[1]); else if (args[0] == "InBlockTransfer") InBlockTransfer = bool.Parse(args[1]); else if (args[0] == "BTFrom") btFrom = ushort.Parse(args[1]); else if (args[0] == "BTTo") btTo = ushort.Parse(args[1]); else if (args[0] == "BTLen") btLen = ushort.Parse(args[1]); else if (args[0] == "BTAlternator") btAlternator = int.Parse(args[1]); else Console.WriteLine("Skipping unrecognized identifier " + args[0]); } } public void SaveStateBinary(BinaryWriter writer) { writer.Write(A); writer.Write(X); writer.Write(Y); writer.Write(P); writer.Write(PC); writer.Write(S); writer.Write(MPR); writer.Write(IRQ1Assert); writer.Write(TimerAssert); writer.Write(IRQControlByte); writer.Write(IRQNextControlByte); writer.Write(TotalExecutedCycles); writer.Write(PendingCycles); writer.Write(LowSpeed); writer.Write(TimerTickCounter); writer.Write(TimerReloadValue); writer.Write(TimerValue); writer.Write(TimerEnabled); writer.Write(InBlockTransfer); writer.Write(btFrom); writer.Write(btTo); writer.Write(btLen); writer.Write((byte)btAlternator); } public void LoadStateBinary(BinaryReader reader) { A = reader.ReadByte(); X = reader.ReadByte(); Y = reader.ReadByte(); P = reader.ReadByte(); PC = reader.ReadUInt16(); S = reader.ReadByte(); MPR = reader.ReadBytes(8); IRQ1Assert = reader.ReadBoolean(); TimerAssert = reader.ReadBoolean(); IRQControlByte = reader.ReadByte(); IRQNextControlByte = reader.ReadByte(); TotalExecutedCycles = reader.ReadInt64(); PendingCycles = reader.ReadInt32(); LowSpeed = reader.ReadBoolean(); TimerTickCounter = reader.ReadInt32(); TimerReloadValue = reader.ReadByte(); TimerValue = reader.ReadByte(); TimerEnabled = reader.ReadBoolean(); InBlockTransfer = reader.ReadBoolean(); btFrom = reader.ReadUInt16(); btTo = reader.ReadUInt16(); btLen = reader.ReadUInt16(); btAlternator = reader.ReadByte(); } // ==== Interrupts ==== private const ushort ResetVector = 0xFFFE; private const ushort NMIVector = 0xFFFC; private const ushort TimerVector = 0xFFFA; private const ushort IRQ1Vector = 0xFFF8; private const ushort IRQ2Vector = 0xFFF6; private const byte IRQ2Selector = 0x01; private const byte IRQ1Selector = 0x02; private const byte TimerSelector = 0x04; public void WriteIrqControl(byte value) { // There is a single-instruction delay before writes to the IRQ Control Byte take effect. value &= 7; IRQNextControlByte = value; } public void WriteIrqStatus() { TimerAssert = false; } public byte ReadIrqStatus() { byte status = 0; if (IRQ2Assert) status |= 1; if (IRQ1Assert) status |= 2; if (TimerAssert) status |= 4; return status; } public void WriteTimer(byte value) { value &= 0x7F; TimerReloadValue = value; } public void WriteTimerEnable(byte value) { if (TimerEnabled == false && (value & 1) == 1) { TimerValue = TimerReloadValue; // timer value is reset when toggled from off to on TimerTickCounter = 0; } TimerEnabled = (value & 1) == 1; } public byte ReadTimerValue() { if (TimerTickCounter + 5 > 1024) { // There exists a slight delay between when the timer counter is decremented and when // the interrupt fires; games can detect it, so we hack it this way. return (byte) ((TimerValue - 1) & 0x7F); } return TimerValue; } // ==== Flags ==== /// Carry Flag private bool FlagC { get { return (P & 0x01) != 0; } set { P = (byte)((P & ~0x01) | (value ? 0x01 : 0x00)); } } /// Zero Flag private bool FlagZ { get { return (P & 0x02) != 0; } set { P = (byte)((P & ~0x02) | (value ? 0x02 : 0x00)); } } /// Interrupt Disable Flag private bool FlagI { get { return (P & 0x04) != 0; } set { P = (byte)((P & ~0x04) | (value ? 0x04 : 0x00)); } } /// Decimal Mode Flag private bool FlagD { get { return (P & 0x08) != 0; } set { P = (byte)((P & ~0x08) | (value ? 0x08 : 0x00)); } } /// Break Flag private bool FlagB { get { return (P & 0x10) != 0; } set { P = (byte)((P & ~0x10) | (value ? 0x10 : 0x00)); } } /// T... Flag private bool FlagT { get { return (P & 0x20) != 0; } set { P = (byte)((P & ~0x20) | (value ? 0x20 : 0x00)); } } /// Overflow Flag private bool FlagV { get { return (P & 0x40) != 0; } set { P = (byte)((P & ~0x40) | (value ? 0x40 : 0x00)); } } /// Negative Flag private bool FlagN { get { return (P & 0x80) != 0; } set { P = (byte)((P & ~0x80) | (value ? 0x80 : 0x00)); } } // ==== Memory ==== public Func ReadMemory21; public Action WriteMemory21; public Action WriteVDC; public Action ThinkAction = delegate { }; public byte ReadMemory(ushort address) { byte page = MPR[address >> 13]; return ReadMemory21((page << 13) | (address & 0x1FFF)); } public void WriteMemory(ushort address, byte value) { byte page = MPR[address >> 13]; WriteMemory21((page << 13) | (address & 0x1FFF), value); } private ushort ReadWord(ushort address) { byte l = ReadMemory(address); byte h = ReadMemory(++address); return (ushort)((h << 8) | l); } private void WriteWord(ushort address, ushort value) { byte l = (byte)(value & 0xFF); byte h = (byte)(value >> 8); WriteMemory(address, l); WriteMemory(++address, h); } private ushort ReadWordPageWrap(ushort address) { ushort highAddress = (ushort)((address & 0xFF00) + ((address + 1) & 0xFF)); return (ushort)(ReadMemory(address) | (ReadMemory(highAddress) << 8)); } public string State() { int notused; string a = string.Format("{0:X4} {1:X2} {2} ", PC, ReadMemory(PC), Disassemble(PC, out notused)).PadRight(41); 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; } 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 }; } }