using System; using ParsonsMegadrive.Core.Interfaces; namespace ParsonsMegaDrive.Core { public partial class M68000 { public delegate int InstructionHandler(ushort opcode); // 2. The master lookup table private readonly InstructionHandler[] _instructionTable = new InstructionHandler[65536]; // --- PUBLIC STATE FOR DEBUGGER --- public uint[] D { get; private set; } = new uint[8]; public uint[] A { get; private set; } = new uint[8]; public uint PC { get; private set; } public ushort SR { get; private set; } public long TotalCycles { get; private set; } // --- STATUS REGISTER (SR) FLAGS --- public bool FlagC { get => (SR & 0x0001) != 0; internal set => SR = (ushort)(value ? (SR | 0x0001) : (SR & ~0x0001)); } public bool FlagV { get => (SR & 0x0002) != 0; internal set => SR = (ushort)(value ? (SR | 0x0002) : (SR & ~0x0002)); } public bool FlagZ { get => (SR & 0x0004) != 0; internal set => SR = (ushort)(value ? (SR | 0x0004) : (SR & ~0x0004)); } public bool FlagN { get => (SR & 0x0008) != 0; internal set => SR = (ushort)(value ? (SR | 0x0008) : (SR & ~0x0008)); } public bool FlagX { get => (SR & 0x0010) != 0; internal set => SR = (ushort)(value ? (SR | 0x0010) : (SR & ~0x0010)); } private readonly IMemoryBus _memory; public M68000(IMemoryBus memory) { _memory = memory; BuildOpcodeTable(); Reset(); } private void BuildOpcodeTable() { for (int i = 0; i < 65536; i++) { ushort opcode = (ushort)i; int line = (opcode >> 12) & 0x0F; // Extract Bits 12-15 if (line == 0x07) { // Line 7 is MOVEQ! // However, bit 8 MUST be 0. If it's 1, it's a completely different instruction. if ((opcode & 0x0100) == 0) { _instructionTable[opcode] = ExecuteMoveQ; continue; } } // If it matches nothing, point it to our fallback method _instructionTable[opcode] = ExecuteUnknown; } } public void Reset() { Array.Clear(D, 0, D.Length); Array.Clear(A, 0, A.Length); PC = 0; SR = 0x2700; // Supervisor mode on, Interrupts masked to level 7 TotalCycles = 0; BuildOpcodeTable(); } // --- INSTRUCTION FETCHING --- // Fetches a 16-bit word from the current Program Counter and advances it. public ushort FetchWord() { ushort word = _memory.Read16(PC); PC += 2; return word; } // Sometimes an instruction needs a 32-bit immediate value! public uint FetchLong() { uint highWord = FetchWord(); uint lowWord = FetchWord(); return (highWord << 16) | lowWord; } // --- EXECUTION LOOP --- public int Step() { ushort opcode = FetchWord(); // Absolute O(1) execution. No switch statements! int cyclesTaken = _instructionTable[opcode](opcode); TotalCycles += cyclesTaken; return cyclesTaken; } // Extracts bits 0-2 (The target register: 0-7) private static int GetEARegister(ushort opcode) { return opcode & 0x07; } // Extracts bits 3-5 (The addressing mode: e.g., Address Indirect, Immediate, etc.) private static int GetEAMode(ushort opcode) { return (opcode >> 3) & 0x07; } // Extracts bits 6-7 (Often used to define if an operation is Byte, Word, or Long) private static int GetSize(ushort opcode) { return (opcode >> 6) & 0x03; } // Extracts bits 9-11 (Often used to specify a destination Data/Address register) private static int GetDestinationRegister(ushort opcode) { return (opcode >> 9) & 0x07; } private int ExecuteMoveQ(ushort opcode) { // Bit 8 must be 0 for a valid MOVEQ. If it's 1, it's a different instruction. if ((opcode & 0x0100) != 0) return ExecuteUnknown(opcode); // 1. Where is it going? (Bits 9-11) int registerIndex = GetDestinationRegister(opcode); // 2. What is the data? (Bits 0-7). MOVEQ sign-extends the 8-bit data to 32-bits! sbyte data = (sbyte)(opcode & 0xFF); // 3. Execute D[registerIndex] = (uint)data; // 4. Update Flags (MOVEQ clears V and C, and updates N and Z based on the data) FlagV = false; FlagC = false; FlagN = data < 0; FlagZ = data == 0; // MOVEQ always takes 4 cycles return 4; } private int ExecuteUnknown(ushort opcode) { throw new NotImplementedException($"Opcode 0x{opcode:X4} at PC 0x{(PC - 2):X8} is not implemented."); } private int ExecuteOpcode(ushort opcode) { // This is where the magic happens! // We will use bit-masking to figure out what this opcode is. throw new NotImplementedException($"Opcode 0x{opcode:X4} at PC 0x{(PC - 2):X8} is not implemented."); } } }