Files
2026-06-06 01:49:33 +01:00

183 lines
5.6 KiB
C#

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.");
}
}
}