diff --git a/Core/Core.csproj b/Core/Core.csproj
index 6886036..5ca139d 100644
--- a/Core/Core.csproj
+++ b/Core/Core.csproj
@@ -9,7 +9,6 @@
-
diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs
index 8ff61be..9c20215 100644
--- a/Core/Cpu/Z80.cs
+++ b/Core/Cpu/Z80.cs
@@ -53,12 +53,12 @@ namespace Core.Cpu
// The Memory Bus
private readonly IMemory _memory;
- private readonly IO_Bus _simpleIoBus;
+ private readonly IIoBus _simpleIoBus;
//External Timing interface
public Func? WaitStateCallback { get; set; }
- public Z80(IMemory memory, IO_Bus ioBus)
+ public Z80(IMemory memory, IIoBus ioBus)
{
_memory = memory;
_simpleIoBus = ioBus;
diff --git a/Core/Interfaces/IIoBus.cs b/Core/Interfaces/IIoBus.cs
index c28821e..ebf3894 100644
--- a/Core/Interfaces/IIoBus.cs
+++ b/Core/Interfaces/IIoBus.cs
@@ -2,7 +2,7 @@
{
public interface IIoBus
{
- byte Read(ushort port);
- void Write(ushort port, byte value);
+ byte ReadPort(ushort port);
+ void WritePort(ushort port, byte value);
}
}
diff --git a/Core/Io/SmsIoBus.cs b/Core/Io/SmsIoBus.cs
new file mode 100644
index 0000000..8a88fcc
--- /dev/null
+++ b/Core/Io/SmsIoBus.cs
@@ -0,0 +1,62 @@
+using Core.Interfaces;
+
+namespace Core.Io
+{
+ public class SmsIoBus : IIoBus
+ {
+ // We will wire these up in the next phases!
+ // public Vdp VideoProcessor { get; set; }
+ // public Psg AudioProcessor { get; set; }
+
+ // Joypad State (0xFF means no buttons pressed - the SMS uses Active-Low logic!)
+ public byte Joypad1State { get; set; } = 0xFF;
+ public byte Joypad2State { get; set; } = 0xFF;
+
+ public byte ReadPort(ushort port)
+ {
+ // The Z80 can output 16-bit port addresses, but the Master System
+ // hardware only physically wires up the bottom 8 bits.
+ byte lowerPort = (byte)(port & 0xFF);
+
+ if (lowerPort >= 0x80 && lowerPort <= 0xBF)
+ {
+ // VDP Read (Usually 0xBE for VRAM Data, 0xBF for Status Flags)
+ // return VideoProcessor.ReadPort(lowerPort);
+ return 0x00;
+ }
+ if (lowerPort == 0xDC)
+ {
+ // Port 0xDC: Player 1 (Up, Down, Left, Right, 1, 2) + Player 2 (Up, Down)
+ return Joypad1State;
+ }
+ if (lowerPort == 0xDD)
+ {
+ // Port 0xDD: Player 2 (Left, Right, 1, 2) + Reset Button
+ return Joypad2State;
+ }
+
+ return 0xFF; // Floating bus
+ }
+
+ public void WritePort(ushort port, byte value)
+ {
+ byte lowerPort = (byte)(port & 0xFF);
+
+ if (lowerPort >= 0x40 && lowerPort <= 0x7F)
+ {
+ // PSG Audio Write (Usually written exactly to 0x7F)
+ // AudioProcessor.WriteData(value);
+ }
+ else if (lowerPort >= 0x80 && lowerPort <= 0xBF)
+ {
+ // VDP Write (Usually 0xBE for VRAM Data, 0xBF for Control Registers)
+ // VideoProcessor.WritePort(lowerPort, value);
+ }
+ else if (lowerPort <= 0x3F)
+ {
+ // Port 0x3E is used by the BIOS to enable/disable the cartridge slot
+ // We can usually ignore this if we are just directly booting game ROMs!
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Core/SmsMachine.cs b/Core/SmsMachine.cs
new file mode 100644
index 0000000..f4b56bb
--- /dev/null
+++ b/Core/SmsMachine.cs
@@ -0,0 +1,61 @@
+using Core.Cpu;
+using Core.Io;
+using Core.Memory;
+
+namespace Core
+{
+ public class SmsMachine
+ {
+ public Z80 Cpu { get; private set; }
+ public SmsMemoryBus MemoryBus { get; private set; }
+ public SmsIoBus IoBus { get; private set; }
+ public ushort? Breakpoint { get; set; } = null;
+
+ // NTSC SMS T-States per frame
+ public const int TStatesPerFrame = 59736;
+ public long TotalFrameCount { get; private set; } = 0;
+ public double FramesPerSecond { get; private set; } = 0;
+ public double FrameTime { get; private set; } = 0;
+
+ public SmsMachine()
+ {
+ MemoryBus = new SmsMemoryBus();
+ IoBus = new SmsIoBus();
+ Cpu = new Z80(MemoryBus, IoBus);
+ }
+
+ public void LoadCartridge(byte[] romData)
+ {
+ MemoryBus.LoadCartridge(romData);
+ Reset();
+ }
+
+ public void Reset()
+ {
+ MemoryBus.CleanRAMData();
+ Cpu.Reset();
+
+ // We will reset the VDP and PSG here later!
+ }
+
+ public void RunFrame()
+ {
+ long currentFrameTStates = 0;
+
+ while (currentFrameTStates < TStatesPerFrame)
+ {
+ int tStates = Cpu.Step();
+ currentFrameTStates += tStates;
+
+ // --- FUTURE EXPANSION ---
+ // VideoProcessor.Update(tStates);
+ // AudioProcessor.Update(tStates);
+
+ // if (VideoProcessor.IsVBlanking && VideoProcessor.InterruptsEnabled)
+ // {
+ // Cpu.RequestInterrupt();
+ // }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Desktop/DebuggerForm.Designer.cs b/Desktop/DebuggerForm.Designer.cs
new file mode 100644
index 0000000..1a8b7d6
--- /dev/null
+++ b/Desktop/DebuggerForm.Designer.cs
@@ -0,0 +1,414 @@
+namespace Desktop
+{
+ partial class DebuggerForm
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ components = new System.ComponentModel.Container();
+ lblAF = new Label();
+ lblBC = new Label();
+ lblDE = new Label();
+ lblHL = new Label();
+ lblPC = new Label();
+ lblSP = new Label();
+ lblFlags = new Label();
+ lblTStates = new Label();
+ txtMemoryStart = new TextBox();
+ btnRefreshMemory = new Button();
+ txtMemoryView = new RichTextBox();
+ lstDisassembly = new ListBox();
+ lstStack = new ListBox();
+ label1 = new Label();
+ txtBreakpoint = new TextBox();
+ label2 = new Label();
+ lblIX = new Label();
+ lblIY = new Label();
+ lblIff1 = new Label();
+ lblIff2 = new Label();
+ lblIE = new Label();
+ btnReset = new Button();
+ uiUpdateTimer = new System.Windows.Forms.Timer(components);
+ lblFrames = new Label();
+ lblFPS = new Label();
+ lblFrameTime = new Label();
+ richTextBox1 = new RichTextBox();
+ button1 = new Button();
+ button2 = new Button();
+ SuspendLayout();
+ //
+ // lblAF
+ //
+ lblAF.AutoSize = true;
+ lblAF.Location = new Point(10, 7);
+ lblAF.Margin = new Padding(2, 0, 2, 0);
+ lblAF.Name = "lblAF";
+ lblAF.Size = new Size(26, 20);
+ lblAF.TabIndex = 0;
+ lblAF.Text = "AF";
+ //
+ // lblBC
+ //
+ lblBC.AutoSize = true;
+ lblBC.Location = new Point(9, 48);
+ lblBC.Margin = new Padding(2, 0, 2, 0);
+ lblBC.Name = "lblBC";
+ lblBC.Size = new Size(27, 20);
+ lblBC.TabIndex = 1;
+ lblBC.Text = "BC";
+ //
+ // lblDE
+ //
+ lblDE.AutoSize = true;
+ lblDE.Location = new Point(10, 100);
+ lblDE.Margin = new Padding(2, 0, 2, 0);
+ lblDE.Name = "lblDE";
+ lblDE.Size = new Size(28, 20);
+ lblDE.TabIndex = 2;
+ lblDE.Text = "DE";
+ //
+ // lblHL
+ //
+ lblHL.AutoSize = true;
+ lblHL.Location = new Point(10, 150);
+ lblHL.Margin = new Padding(2, 0, 2, 0);
+ lblHL.Name = "lblHL";
+ lblHL.Size = new Size(27, 20);
+ lblHL.TabIndex = 3;
+ lblHL.Text = "HL";
+ //
+ // lblPC
+ //
+ lblPC.AutoSize = true;
+ lblPC.Location = new Point(9, 200);
+ lblPC.Margin = new Padding(2, 0, 2, 0);
+ lblPC.Name = "lblPC";
+ lblPC.Size = new Size(26, 20);
+ lblPC.TabIndex = 4;
+ lblPC.Text = "PC";
+ //
+ // lblSP
+ //
+ lblSP.AutoSize = true;
+ lblSP.Location = new Point(9, 252);
+ lblSP.Margin = new Padding(2, 0, 2, 0);
+ lblSP.Name = "lblSP";
+ lblSP.Size = new Size(25, 20);
+ lblSP.TabIndex = 6;
+ lblSP.Text = "SP";
+ //
+ // lblFlags
+ //
+ lblFlags.AutoSize = true;
+ lblFlags.Location = new Point(88, 52);
+ lblFlags.Margin = new Padding(2, 0, 2, 0);
+ lblFlags.Name = "lblFlags";
+ lblFlags.Size = new Size(43, 20);
+ lblFlags.TabIndex = 7;
+ lblFlags.Text = "Flags";
+ //
+ // lblTStates
+ //
+ lblTStates.AutoSize = true;
+ lblTStates.Location = new Point(88, 7);
+ lblTStates.Margin = new Padding(2, 0, 2, 0);
+ lblTStates.Name = "lblTStates";
+ lblTStates.Size = new Size(63, 20);
+ lblTStates.TabIndex = 8;
+ lblTStates.Text = "T-States";
+ //
+ // txtMemoryStart
+ //
+ txtMemoryStart.Location = new Point(300, 21);
+ txtMemoryStart.Margin = new Padding(2);
+ txtMemoryStart.Name = "txtMemoryStart";
+ txtMemoryStart.Size = new Size(121, 27);
+ txtMemoryStart.TabIndex = 9;
+ txtMemoryStart.Text = "Memory Start";
+ txtMemoryStart.TextAlign = HorizontalAlignment.Center;
+ txtMemoryStart.TextChanged += btnRefreshMemory_Click;
+ //
+ // btnRefreshMemory
+ //
+ btnRefreshMemory.Location = new Point(425, 21);
+ btnRefreshMemory.Margin = new Padding(2);
+ btnRefreshMemory.Name = "btnRefreshMemory";
+ btnRefreshMemory.Size = new Size(90, 27);
+ btnRefreshMemory.TabIndex = 14;
+ btnRefreshMemory.Text = "Refresh Memory";
+ btnRefreshMemory.UseVisualStyleBackColor = true;
+ btnRefreshMemory.Click += btnRefreshMemory_Click;
+ //
+ // txtMemoryView
+ //
+ txtMemoryView.Location = new Point(88, 80);
+ txtMemoryView.Margin = new Padding(2);
+ txtMemoryView.Name = "txtMemoryView";
+ txtMemoryView.Size = new Size(443, 895);
+ txtMemoryView.TabIndex = 15;
+ txtMemoryView.Text = "Memory View Window";
+ //
+ // lstDisassembly
+ //
+ lstDisassembly.FormattingEnabled = true;
+ lstDisassembly.Location = new Point(567, 8);
+ lstDisassembly.Margin = new Padding(2);
+ lstDisassembly.Name = "lstDisassembly";
+ lstDisassembly.Size = new Size(252, 264);
+ lstDisassembly.TabIndex = 16;
+ //
+ // lstStack
+ //
+ lstStack.FormattingEnabled = true;
+ lstStack.Location = new Point(823, 8);
+ lstStack.Margin = new Padding(2);
+ lstStack.Name = "lstStack";
+ lstStack.Size = new Size(130, 264);
+ lstStack.TabIndex = 17;
+ //
+ // label1
+ //
+ label1.AutoSize = true;
+ label1.Location = new Point(568, 298);
+ label1.Name = "label1";
+ label1.Size = new Size(81, 20);
+ label1.TabIndex = 19;
+ label1.Text = "Breakpoint";
+ //
+ // txtBreakpoint
+ //
+ txtBreakpoint.Location = new Point(655, 295);
+ txtBreakpoint.Name = "txtBreakpoint";
+ txtBreakpoint.Size = new Size(125, 27);
+ txtBreakpoint.TabIndex = 20;
+ //
+ // label2
+ //
+ label2.AutoSize = true;
+ label2.Location = new Point(233, 21);
+ label2.Name = "label2";
+ label2.Size = new Size(62, 20);
+ label2.TabIndex = 21;
+ label2.Text = "Address";
+ //
+ // lblIX
+ //
+ lblIX.AutoSize = true;
+ lblIX.Location = new Point(9, 298);
+ lblIX.Margin = new Padding(2, 0, 2, 0);
+ lblIX.Name = "lblIX";
+ lblIX.Size = new Size(22, 20);
+ lblIX.TabIndex = 22;
+ lblIX.Text = "IX";
+ //
+ // lblIY
+ //
+ lblIY.AutoSize = true;
+ lblIY.Location = new Point(10, 344);
+ lblIY.Margin = new Padding(2, 0, 2, 0);
+ lblIY.Name = "lblIY";
+ lblIY.Size = new Size(21, 20);
+ lblIY.TabIndex = 23;
+ lblIY.Text = "IY";
+ //
+ // lblIff1
+ //
+ lblIff1.AutoSize = true;
+ lblIff1.Location = new Point(567, 411);
+ lblIff1.Name = "lblIff1";
+ lblIff1.Size = new Size(35, 20);
+ lblIff1.TabIndex = 24;
+ lblIff1.Text = "IFF1";
+ //
+ // lblIff2
+ //
+ lblIff2.AutoSize = true;
+ lblIff2.Location = new Point(567, 458);
+ lblIff2.Name = "lblIff2";
+ lblIff2.Size = new Size(35, 20);
+ lblIff2.TabIndex = 25;
+ lblIff2.Text = "IFF2";
+ //
+ // lblIE
+ //
+ lblIE.AutoSize = true;
+ lblIE.Location = new Point(568, 370);
+ lblIE.Name = "lblIE";
+ lblIE.Size = new Size(109, 20);
+ lblIE.TabIndex = 26;
+ lblIE.Text = "Interrupt Mode";
+ //
+ // btnReset
+ //
+ btnReset.Location = new Point(651, 327);
+ btnReset.Margin = new Padding(2);
+ btnReset.Name = "btnReset";
+ btnReset.Size = new Size(132, 27);
+ btnReset.TabIndex = 27;
+ btnReset.Text = "Set Breakpoint";
+ btnReset.UseVisualStyleBackColor = true;
+ btnReset.Click += btnSetBreakpoint_Click;
+ //
+ // uiUpdateTimer
+ //
+ uiUpdateTimer.Enabled = true;
+ uiUpdateTimer.Interval = 1;
+ uiUpdateTimer.Tick += uiUpdateTimer_Tick;
+ //
+ // lblFrames
+ //
+ lblFrames.AutoSize = true;
+ lblFrames.Location = new Point(567, 682);
+ lblFrames.Margin = new Padding(2, 0, 2, 0);
+ lblFrames.Name = "lblFrames";
+ lblFrames.Size = new Size(124, 20);
+ lblFrames.TabIndex = 28;
+ lblFrames.Text = "Frames Rendered";
+ //
+ // lblFPS
+ //
+ lblFPS.AutoSize = true;
+ lblFPS.Location = new Point(659, 759);
+ lblFPS.Margin = new Padding(2, 0, 2, 0);
+ lblFPS.Name = "lblFPS";
+ lblFPS.Size = new Size(32, 20);
+ lblFPS.TabIndex = 29;
+ lblFPS.Text = "FPS";
+ //
+ // lblFrameTime
+ //
+ lblFrameTime.AutoSize = true;
+ lblFrameTime.Location = new Point(604, 718);
+ lblFrameTime.Margin = new Padding(2, 0, 2, 0);
+ lblFrameTime.Name = "lblFrameTime";
+ lblFrameTime.Size = new Size(87, 20);
+ lblFrameTime.TabIndex = 30;
+ lblFrameTime.Text = "Frame Time";
+ //
+ // richTextBox1
+ //
+ richTextBox1.Enabled = false;
+ richTextBox1.Location = new Point(567, 509);
+ richTextBox1.Name = "richTextBox1";
+ richTextBox1.ReadOnly = true;
+ richTextBox1.Size = new Size(304, 128);
+ richTextBox1.TabIndex = 32;
+ richTextBox1.Text = "Sega Master System Memory Map:\n0x0000 - 0x3FFF: ROM Slot 0 (16KB)\n0x4000 - 0x7FFF: ROM Slot 1 (16KB)\n0x8000 - 0xBFFF: ROM Slot 2 (16KB)\n0xC000 - 0xDFFF: System RAM (8KB)\n0xE000 - 0xFFFF: RAM Mirror";
+ //
+ // button1
+ //
+ button1.Location = new Point(555, 824);
+ button1.Name = "button1";
+ button1.Size = new Size(94, 29);
+ button1.TabIndex = 33;
+ button1.Text = "CPU Step";
+ button1.UseVisualStyleBackColor = true;
+ button1.Click += btnStep_Click;
+ //
+ // button2
+ //
+ button2.Location = new Point(555, 883);
+ button2.Name = "button2";
+ button2.Size = new Size(94, 29);
+ button2.TabIndex = 34;
+ button2.Text = "CPU Run";
+ button2.UseVisualStyleBackColor = true;
+ button2.Click += btnStep_Click;
+ //
+ // DebuggerForm
+ //
+ AutoScaleDimensions = new SizeF(8F, 20F);
+ AutoScaleMode = AutoScaleMode.Font;
+ ClientSize = new Size(965, 990);
+ Controls.Add(button2);
+ Controls.Add(button1);
+ Controls.Add(richTextBox1);
+ Controls.Add(lblFrameTime);
+ Controls.Add(lblFPS);
+ Controls.Add(lblFrames);
+ Controls.Add(btnReset);
+ Controls.Add(lblIE);
+ Controls.Add(lblIff2);
+ Controls.Add(lblIff1);
+ Controls.Add(lblIY);
+ Controls.Add(lblIX);
+ Controls.Add(label2);
+ Controls.Add(txtBreakpoint);
+ Controls.Add(label1);
+ Controls.Add(lstStack);
+ Controls.Add(lstDisassembly);
+ Controls.Add(txtMemoryView);
+ Controls.Add(btnRefreshMemory);
+ Controls.Add(txtMemoryStart);
+ Controls.Add(lblTStates);
+ Controls.Add(lblFlags);
+ Controls.Add(lblSP);
+ Controls.Add(lblPC);
+ Controls.Add(lblHL);
+ Controls.Add(lblDE);
+ Controls.Add(lblBC);
+ Controls.Add(lblAF);
+ Margin = new Padding(2);
+ Name = "DebuggerForm";
+ Text = "Debugger";
+ ResumeLayout(false);
+ PerformLayout();
+ }
+
+ #endregion
+
+ private Label lblAF;
+ private Label lblBC;
+ private Label lblDE;
+ private Label lblHL;
+ private Label lblPC;
+ private Label lblSP;
+ private Label lblFlags;
+ private Label lblTStates;
+ private TextBox txtMemoryStart;
+ private Button btnRefreshMemory;
+ private RichTextBox txtMemoryView;
+ private ListBox lstStack;
+ public ListBox lstDisassembly;
+ private Label label1;
+ private TextBox txtBreakpoint;
+ private Label label2;
+ private Label lblIX;
+ private Label lblIY;
+ private Label lblIff1;
+ private Label lblIff2;
+ private Label lblIE;
+ private Button btnReset;
+ private System.Windows.Forms.Timer uiUpdateTimer;
+ private Label lblFrames;
+ private Label lblFPS;
+ private Label lblFrameTime;
+ private RichTextBox richTextBox1;
+ private Button button1;
+ private Button button2;
+ //private TextBox textBox4;
+ }
+}
\ No newline at end of file
diff --git a/Desktop/DebuggerForm.cs b/Desktop/DebuggerForm.cs
new file mode 100644
index 0000000..76a0482
--- /dev/null
+++ b/Desktop/DebuggerForm.cs
@@ -0,0 +1,1346 @@
+using System;
+using System.Text;
+using System.Windows.Forms;
+using Core.Cpu;
+using Core.Memory;
+
+namespace Desktop
+{
+ public partial class DebuggerForm : Form
+ {
+ private readonly Z80 _cpu;
+ private readonly SmsMemoryBus _memoryBus;
+ private readonly Form1 _mainForm;
+
+ public DebuggerForm(Z80 cpu, SmsMemoryBus memoryBus, Form1 mainForm)
+ {
+ InitializeComponent();
+ _cpu = cpu;
+ _memoryBus = memoryBus;
+ _mainForm = mainForm;
+
+ // Set default memory view address
+ txtMemoryStart.Text = "0000";
+ UpdateDisplay();
+ UpdateStackView();
+ UpdateDisassemblyView();
+ _mainForm = mainForm;
+ }
+
+ private void btnStep_Click(object sender, EventArgs e)
+ {
+ try
+ {
+ _cpu.Step();
+ UpdateDisplay();
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.Message, "CPU Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
+ }
+ }
+
+ private void btnSetBreakpoint_Click(object sender, EventArgs e) // Hook this to a button or text changed event
+ {
+ if (ushort.TryParse(txtBreakpoint.Text, System.Globalization.NumberStyles.HexNumber, null, out ushort parsedBp))
+ {
+ _mainForm.Breakpoint = parsedBp;
+ }
+ else
+ {
+ _mainForm.Breakpoint = null;
+ }
+ }
+
+ private void btnRefreshMemory_Click(object sender, EventArgs e)
+ {
+ UpdateDisplay();
+ }
+
+ private void uiUpdateTimer_Tick(object sender, EventArgs e)
+ {
+ UpdateDisplay();
+ }
+
+ // Current Emulator State
+ private void UpdateDisplay()
+ {
+ lblAF.Text = $"AF: {_cpu.AF.Word:X4}";
+ lblBC.Text = $"BC: {_cpu.BC.Word:X4}";
+ lblDE.Text = $"DE: {_cpu.DE.Word:X4}";
+ lblHL.Text = $"HL: {_cpu.HL.Word:X4}";
+ lblPC.Text = $"PC: {_cpu.PC:X4}";
+ lblSP.Text = $"SP: {_cpu.SP:X4}";
+ lblIX.Text = $"IX: {_cpu.IX.Word:X4}";
+ lblIY.Text = $"IY: {_cpu.IY.Word:X4}";
+ lblIff1.Text = $"IFF1: {_cpu.IFF1}";
+ lblIff2.Text = $"IFF2: {_cpu.IFF2}";
+ lblIE.Text = $"Interrupt Mode: {_cpu.InterruptMode}";
+ lblFlags.Text = $"Flags: {_cpu.GetFlagsString()}";
+ lblTStates.Text = $"T-States: {_cpu.TotalTStates}";
+ //lblFrames.Text = $"Frames Rendered: {_mainForm.TotalFrameCount}";
+ //lblFrameTime.Text = $"Frame Time: {((float)_mainForm.FrameTime):F1}ms";
+ //lblFPS.Text = $"FPS: {_mainForm.FramesPerSecond:F2}";
+ UpdateMemoryView();
+ UpdateStackView();
+ UpdateDisassemblyView();
+ }
+
+ private void UpdateMemoryView()
+ {
+ int count = 40;
+ // Try to parse the hex string the user typed in
+ if (!ushort.TryParse(txtMemoryStart.Text, System.Globalization.NumberStyles.HexNumber, null, out ushort startAddress))
+ {
+ txtMemoryView.Text = "Invalid Hex Address!";
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ // Read 100 bytes (or roughly 6 lines of 16 bytes)
+ for (int line = 0; line < count; line++)
+ {
+ ushort currentAddr = (ushort)(startAddress + (line * 16));
+
+ // Print the address header for this line (e.g., "0000: ")
+ sb.Append($"{currentAddr:X4}: ");
+
+ // Print 16 bytes across
+ for (int i = 0; i < 16; i++)
+ {
+ // Careful not to overflow the 64k address space!
+ if (currentAddr + i <= 0xFFFF)
+ {
+ byte b = _memoryBus.Read((ushort)(currentAddr + i));
+ sb.Append($"{b:X2} ");
+ }
+ }
+ sb.AppendLine();
+ }
+
+ txtMemoryView.Text = sb.ToString();
+ }
+
+ private void UpdateStackView()
+ {
+ lstStack.Items.Clear();
+ int itemsToShow = 5;
+ ushort currentSp = _cpu.SP;
+
+ for (int i = 0; i < itemsToShow; i++)
+ {
+ // Prevent reading past 0xFFFF
+ if (currentSp >= 0xFFFE)
+ {
+ lstStack.Items.Add($"{currentSp:X4}: [End of Mem]");
+ break;
+ }
+
+ // Read the 16-bit value (Little-Endian: Low byte first, then High byte)
+ byte low = _memoryBus.Read(currentSp);
+ byte high = _memoryBus.Read((ushort)(currentSp + 1));
+ ushort value = (ushort)((high << 8) | low);
+
+ lstStack.Items.Add($"{currentSp:X4}: {value:X4}");
+
+ // Move to the next 16-bit word on the stack
+ currentSp += 2;
+ }
+ }
+
+ private void UpdateDisassemblyView()
+ {
+ lstDisassembly.Items.Clear();
+
+ ushort currentPc = _cpu.PC;
+
+ int instructionsToShow = 8;
+
+ for (int i = 0; i < instructionsToShow; i++)
+ {
+ byte opcode = _memoryBus.Read(currentPc);
+ string mnemonic;
+ int instructionLength = 1; // Default to 1
+ byte cbOp = 0;
+ int opGroup = 0;
+ int targetBit = 0;
+ int regIdx = 0;
+ string[] regNames = { "B", "C", "D", "E", "H", "L", "(HL)", "A" };
+ string targetReg = "";
+
+ switch (opcode)
+ {
+ case 0x00: mnemonic = "NOP"; break;
+ case 0x01:
+ ushort bcVal = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
+ mnemonic = $"LD BC, 0x{bcVal:X4}";
+ instructionLength = 3;
+ break;
+ case 0x02: mnemonic = "LD (BC), A"; break;
+ // --- 16-Bit Increments ---
+ case 0x03: mnemonic = "INC BC"; break;
+ case 0x08:
+ mnemonic = "EX AF, AF'";
+ break;
+ case 0x0A: mnemonic = "LD A, (BC)"; break;
+ case 0x12: mnemonic = "LD (DE), A"; break;
+ case 0x13: mnemonic = "INC DE"; break;
+ case 0x33: mnemonic = "INC SP"; break;
+
+ // --- 16-Bit Decrements ---
+ case 0x0B: mnemonic = "DEC BC"; break;
+ case 0x3B: mnemonic = "DEC SP"; break;
+ // --- 8-Bit Increments ---
+ case 0x04: mnemonic = "INC B"; break;
+ case 0x0C: mnemonic = "INC C"; break;
+ case 0x14: mnemonic = "INC D"; break;
+ case 0x1C: mnemonic = "INC E"; break;
+ case 0x24: mnemonic = "INC H"; break;
+ case 0x2C: mnemonic = "INC L"; break;
+ case 0x34: mnemonic = "INC (HL)"; break;
+ case 0x3C: mnemonic = "INC A"; break;
+
+ // --- 8-Bit Decrements ---
+ case 0x05: mnemonic = "DEC B"; break;
+ case 0x0D: mnemonic = "DEC C"; break;
+ case 0x15: mnemonic = "DEC D"; break;
+ case 0x1D: mnemonic = "DEC E"; break;
+ case 0x25: mnemonic = "DEC H"; break;
+ case 0x2D: mnemonic = "DEC L"; break;
+ case 0x35: mnemonic = "DEC (HL)"; break;
+ case 0x3D: mnemonic = "DEC A"; break;
+ case 0x06:
+ byte bImm = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"LD B, 0x{bImm:X2}";
+ instructionLength = 2;
+ break;
+ // --- ADD HL, rr ---
+ case 0x09: mnemonic = "ADD HL, BC"; break;
+ case 0x19: mnemonic = "ADD HL, DE"; break;
+ case 0x29: mnemonic = "ADD HL, HL"; break;
+ case 0x39: mnemonic = "ADD HL, SP"; break;
+ case 0x0E:
+ byte cImm = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"LD C, 0x{cImm:X2}";
+ instructionLength = 2;
+ break;
+ case 0x0F:
+ mnemonic = "RRCA";
+ break;
+ case 0x10:
+ sbyte djnzOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
+ ushort djnzDest = (ushort)(currentPc + 2 + djnzOffset);
+ mnemonic = $"DJNZ 0x{djnzDest:X4}";
+ instructionLength = 2;
+ break;
+ case 0x11:
+ // LD DE, nn
+ byte deLow = _memoryBus.Read((ushort)(currentPc + 1));
+ byte deHigh = _memoryBus.Read((ushort)(currentPc + 2));
+ mnemonic = $"LD DE, 0x{deHigh:X2}{deLow:X2}";
+ instructionLength = 3;
+ break;
+ case 0x16:
+ byte dImm = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"LD D, 0x{dImm:X2}";
+ instructionLength = 2;
+ break;
+ case 0x17: // RLA
+ mnemonic = "RLA";
+ instructionLength = 1;
+ break;
+ case 0x18:
+ sbyte dUnconditional = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
+ ushort targetAddressUnconditional = (ushort)(currentPc + 2 + dUnconditional);
+ mnemonic = $"JR 0x{targetAddressUnconditional:X4}";
+ instructionLength = 2;
+ break;
+ case 0x1A:
+ mnemonic = "LD A, (DE)";
+ break;
+ case 0x1B:
+ mnemonic = "DEC DE";
+ break;
+ case 0x1E:
+ byte val1E = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"LD E, 0x{val1E:X2}";
+ instructionLength = 2;
+ break;
+ case 0x1F:
+ mnemonic = $"RRA";
+ break;
+ case 0x20:
+ sbyte jrOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
+ ushort destination = (ushort)(currentPc + 2 + jrOffset);
+ mnemonic = $"JR NZ, 0x{destination:X4}";
+ instructionLength = 2;
+ break;
+ case 0x21:
+ {
+ ushort hlImm = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
+ mnemonic = $"LD HL, 0x{hlImm:X4}";
+ instructionLength = 3;
+ break;
+ }
+ case 0x22:
+ ushort hlAddr = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
+ mnemonic = $"LD (0x{hlAddr:X4}), HL";
+ instructionLength = 3;
+ break;
+ case 0x23:
+ mnemonic = "INC HL";
+ break;
+ case 0x26:
+ byte hImm = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"LD H, 0x{hImm:X2}";
+ instructionLength = 2;
+ break;
+ case 0x27: // DAA
+ mnemonic = "DAA";
+ instructionLength = 1;
+ break;
+ case 0x28:
+ sbyte jrZOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
+ ushort jrZDest = (ushort)(currentPc + 2 + jrZOffset);
+ mnemonic = $"JR Z, 0x{jrZDest:X4}";
+ instructionLength = 2;
+ break;
+ case 0x2A:
+ {
+ ushort addr2A = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
+ mnemonic = $"LD HL, (0x{addr2A:X4})";
+ instructionLength = 3;
+ break;
+ }
+ case 0x2B:
+ mnemonic = "DEC HL";
+ break;
+ case 0x2E:
+ byte lImm = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"LD L, 0x{lImm:X2}";
+ instructionLength = 2;
+ break;
+ case 0x2F:
+ mnemonic = "CPL";
+ break;
+ case 0x30:
+ sbyte jrNcOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
+ ushort dest = (ushort)(currentPc + 2 + jrNcOffset);
+ mnemonic = $"JR NC, 0x{dest:X4}";
+ instructionLength = 2;
+ break;
+ case 0x31:
+ mnemonic = "LD SP, nn";
+ instructionLength = 3;
+ break;
+ case 0x32:
+ {
+ ushort addr32 = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
+ mnemonic = $"LD (0x{addr32:X4}), A";
+ instructionLength = 3;
+ break;
+ }
+ case 0x36:
+ byte memValue = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"LD (HL), 0x{memValue:X2}";
+ instructionLength = 2;
+ break;
+ case 0x37:
+ mnemonic = "SCF";
+ break;
+ case 0x38:
+ sbyte dC = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
+ ushort targetC = (ushort)(currentPc + 2 + dC);
+
+ mnemonic = $"JR C, 0x{targetC:X4}";
+ instructionLength = 2;
+ break;
+ case 0x3A:
+ ushort addr3A = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
+ mnemonic = $"LD A, (0x{addr3A:X4})";
+ instructionLength = 3;
+ break;
+ case 0x3E:
+ mnemonic = $"LD A, 0x{_memoryBus.Read((ushort)(currentPc + 1)):X2}";
+ instructionLength = 2;
+ break;
+ case 0x3F:
+ mnemonic = "CCF";
+ break;
+ // --- LD B, r ---
+ case 0x40: mnemonic = "LD B, B"; break;
+ case 0x41: mnemonic = "LD B, C"; break;
+ case 0x42: mnemonic = "LD B, D"; break;
+ case 0x43: mnemonic = "LD B, E"; break;
+ case 0x44: mnemonic = "LD B, H"; break;
+ case 0x45: mnemonic = "LD B, L"; break;
+ case 0x46: mnemonic = "LD B, (HL)"; break;
+ case 0x47: mnemonic = "LD B, A"; break;
+
+ // --- LD C, r ---
+ case 0x48: mnemonic = "LD C, B"; break;
+ case 0x49: mnemonic = "LD C, C"; break;
+ case 0x4A: mnemonic = "LD C, D"; break;
+ case 0x4B: mnemonic = "LD C, E"; break;
+ case 0x4C: mnemonic = "LD C, H"; break;
+ case 0x4D: mnemonic = "LD C, L"; break;
+ case 0x4E: mnemonic = "LD C, (HL)"; break;
+ case 0x4F: mnemonic = "LD C, A"; break;
+
+ // --- LD D, r ---
+ case 0x50: mnemonic = "LD D, B"; break;
+ case 0x51: mnemonic = "LD D, C"; break;
+ case 0x52: mnemonic = "LD D, D"; break;
+ case 0x53: mnemonic = "LD D, E"; break;
+ case 0x54: mnemonic = "LD D, H"; break;
+ case 0x55: mnemonic = "LD D, L"; break;
+ case 0x56: mnemonic = "LD D, (HL)"; break;
+ case 0x57: mnemonic = "LD D, A"; break;
+
+ // --- LD E, r ---
+ case 0x58: mnemonic = "LD E, B"; break;
+ case 0x59: mnemonic = "LD E, C"; break;
+ case 0x5A: mnemonic = "LD E, D"; break;
+ case 0x5B: mnemonic = "LD E, E"; break;
+ case 0x5C: mnemonic = "LD E, H"; break;
+ case 0x5D: mnemonic = "LD E, L"; break;
+ case 0x5E: mnemonic = "LD E, (HL)"; break;
+ case 0x5F: mnemonic = "LD E, A"; break;
+
+ // --- LD H, r ---
+ case 0x60: mnemonic = "LD H, B"; break;
+ case 0x61: mnemonic = "LD H, C"; break;
+ case 0x62: mnemonic = "LD H, D"; break;
+ case 0x63: mnemonic = "LD H, E"; break;
+ case 0x64: mnemonic = "LD H, H"; break;
+ case 0x65: mnemonic = "LD H, L"; break;
+ case 0x66: mnemonic = "LD H, (HL)"; break;
+ case 0x67: mnemonic = "LD H, A"; break;
+
+ // --- LD L, r ---
+ case 0x68: mnemonic = "LD L, B"; break;
+ case 0x69: mnemonic = "LD L, C"; break;
+ case 0x6A: mnemonic = "LD L, D"; break;
+ case 0x6B: mnemonic = "LD L, E"; break;
+ case 0x6C: mnemonic = "LD L, H"; break;
+ case 0x6D: mnemonic = "LD L, L"; break;
+ case 0x6E: mnemonic = "LD L, (HL)"; break;
+ case 0x6F: mnemonic = "LD L, A"; break;
+
+ // --- LD (HL), r --- (Note: 0x76 is HALT, so it is skipped)
+ case 0x70: mnemonic = "LD (HL), B"; break;
+ case 0x71: mnemonic = "LD (HL), C"; break;
+ case 0x72: mnemonic = "LD (HL), D"; break;
+ case 0x73: mnemonic = "LD (HL), E"; break;
+ case 0x74: mnemonic = "LD (HL), H"; break;
+ case 0x75: mnemonic = "LD (HL), L"; break;
+ case 0x77: mnemonic = "LD (HL), A"; break;
+
+ // --- LD A, r ---
+ case 0x78: mnemonic = "LD A, B"; break;
+ case 0x79: mnemonic = "LD A, C"; break;
+ case 0x7A: mnemonic = "LD A, D"; break;
+ case 0x7B: mnemonic = "LD A, E"; break;
+ case 0x7C: mnemonic = "LD A, H"; break;
+ case 0x7D: mnemonic = "LD A, L"; break;
+ case 0x7E: mnemonic = "LD A, (HL)"; break;
+ case 0x7F: mnemonic = "LD A, A"; break;
+
+ // --- ADD A, r ---
+ case 0x80: mnemonic = "ADD A, B"; break;
+ case 0x81: mnemonic = "ADD A, C"; break;
+ case 0x82: mnemonic = "ADD A, D"; break;
+ case 0x83: mnemonic = "ADD A, E"; break;
+ case 0x84: mnemonic = "ADD A, H"; break;
+ case 0x85: mnemonic = "ADD A, L"; break;
+ case 0x86: mnemonic = "ADD A, (HL)"; break;
+ case 0x87: mnemonic = "ADD A, A"; break;
+ case 0x88:
+ case 0x89:
+ case 0x8A:
+ case 0x8B:
+ case 0x8C:
+ case 0x8D:
+ case 0x8E:
+ case 0x8F:
+ string[] registers = { "B", "C", "D", "E", "H", "L", "(HL)", "A" };
+ mnemonic = $"ADC A, {registers[opcode - 0x88]}";
+ instructionLength = 1;
+ break;
+ // --- SUB r ---
+ case 0x90: mnemonic = "SUB B"; break;
+ case 0x91: mnemonic = "SUB C"; break;
+ case 0x92: mnemonic = "SUB D"; break;
+ case 0x93: mnemonic = "SUB E"; break;
+ case 0x94: mnemonic = "SUB H"; break;
+ case 0x95: mnemonic = "SUB L"; break;
+ case 0x96: mnemonic = "SUB (HL)"; break;
+ case 0x97: mnemonic = "SUB A"; break;
+
+ // --- SBC A, r ---
+ case 0x98: mnemonic = "SBC A, B"; break;
+ case 0x99: mnemonic = "SBC A, C"; break;
+ case 0x9A: mnemonic = "SBC A, D"; break;
+ case 0x9B: mnemonic = "SBC A, E"; break;
+ case 0x9C: mnemonic = "SBC A, H"; break;
+ case 0x9D: mnemonic = "SBC A, L"; break;
+ case 0x9E: mnemonic = "SBC A, (HL)"; break;
+ case 0x9F: mnemonic = "SBC A, A"; break;
+
+ // --- AND r ---
+ case 0xA0: mnemonic = "AND B"; break;
+ case 0xA1: mnemonic = "AND C"; break;
+ case 0xA2: mnemonic = "AND D"; break;
+ case 0xA3: mnemonic = "AND E"; break;
+ case 0xA4: mnemonic = "AND H"; break;
+ case 0xA5: mnemonic = "AND L"; break;
+ case 0xA6: mnemonic = "AND (HL)"; break;
+ case 0xA7: mnemonic = "AND A"; break;
+
+ // --- XOR r ---
+ case 0xA8: mnemonic = "XOR B"; break;
+ case 0xA9: mnemonic = "XOR C"; break;
+ case 0xAA: mnemonic = "XOR D"; break;
+ case 0xAB: mnemonic = "XOR E"; break;
+ case 0xAC: mnemonic = "XOR H"; break;
+ case 0xAD: mnemonic = "XOR L"; break;
+ case 0xAE: mnemonic = "XOR (HL)"; break;
+ case 0xAF: mnemonic = "XOR A"; break;
+
+ // --- OR r ---
+ case 0xB0: mnemonic = "OR B"; break;
+ case 0xB1: mnemonic = "OR C"; break;
+ case 0xB2: mnemonic = "OR D"; break;
+ case 0xB3: mnemonic = "OR E"; break;
+ case 0xB4: mnemonic = "OR H"; break;
+ case 0xB5: mnemonic = "OR L"; break;
+ case 0xB6: mnemonic = "OR (HL)"; break;
+ case 0xB7: mnemonic = "OR A"; break;
+
+ // --- CP r ---
+ case 0xB8: mnemonic = "CP B"; break;
+ case 0xB9: mnemonic = "CP C"; break;
+ case 0xBA: mnemonic = "CP D"; break;
+ case 0xBB: mnemonic = "CP E"; break;
+ case 0xBC: mnemonic = "CP H"; break;
+ case 0xBD: mnemonic = "CP L"; break;
+ case 0xBE: mnemonic = "CP (HL)"; break;
+ case 0xBF: mnemonic = "CP A"; break;
+ case 0xC1: mnemonic = "POP BC"; break;
+ case 0xC2:
+ case 0xCA:
+ case 0xD2:
+ case 0xDA:
+ case 0xE2:
+ case 0xEA:
+ case 0xF2:
+ case 0xFA:
+ {
+ // Read the 16-bit target address
+ ushort jumpAddr = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
+ string condition = "";
+
+ switch (opcode)
+ {
+ case 0xC2: condition = "NZ"; break;
+ case 0xCA: condition = "Z"; break;
+ case 0xD2: condition = "NC"; break;
+ case 0xDA: condition = "C"; break;
+ case 0xE2: condition = "PO"; break;
+ case 0xEA: condition = "PE"; break;
+ case 0xF2: condition = "P"; break;
+ case 0xFA: condition = "M"; break;
+ }
+
+ mnemonic = $"JP {condition}, 0x{jumpAddr:X4}";
+ instructionLength = 3;
+ break;
+ }
+ // --- Conditional Returns ---
+ case 0xC0: mnemonic = "RET NZ"; break;
+ case 0xE0: mnemonic = "RET PO"; break;
+ case 0xE8: mnemonic = "RET PE"; break;
+ case 0xF0: mnemonic = "RET P"; break;
+ case 0xF8: mnemonic = "RET M"; break;
+ case 0xC3:
+ // JP nn
+ byte jpLow = _memoryBus.Read((ushort)(currentPc + 1));
+ byte jpHigh = _memoryBus.Read((ushort)(currentPc + 2));
+ mnemonic = $"JP 0x{jpHigh:X2}{jpLow:X2}";
+ instructionLength = 3;
+ break;
+ case 0xC4:
+ case 0xCC:
+ case 0xD4:
+ case 0xDC:
+ case 0xE4:
+ case 0xEC:
+ case 0xF4:
+ case 0xFC:
+ {
+ // Read the 16-bit target address
+ ushort callAddr = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
+ string condition = "";
+
+ switch (opcode)
+ {
+ case 0xC4: condition = "NZ"; break;
+ case 0xCC: condition = "Z"; break;
+ case 0xD4: condition = "NC"; break;
+ case 0xDC: condition = "C"; break;
+ case 0xE4: condition = "PO"; break;
+ case 0xEC: condition = "PE"; break;
+ case 0xF4: condition = "P"; break;
+ case 0xFC: condition = "M"; break;
+ }
+
+ mnemonic = $"CALL {condition}, 0x{callAddr:X4}";
+ instructionLength = 3;
+ break;
+ }
+ case 0xc5:
+ mnemonic = "PUSH BC";
+ break;
+ case 0xC6:
+ {
+ byte addImm = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"ADD A, 0x{addImm:X2}";
+ instructionLength = 2;
+ break;
+ }
+ // --- RST Instructions ---
+ case 0xC7: mnemonic = "RST 00h"; break;
+ case 0xCF: mnemonic = "RST 08h"; break;
+ case 0xD7: mnemonic = "RST 10h"; break;
+ case 0xDF: mnemonic = "RST 18h"; break;
+ case 0xE7: mnemonic = "RST 20h"; break;
+ case 0xEF: mnemonic = "RST 28h"; break;
+ case 0xF7: mnemonic = "RST 30h"; break;
+ case 0xFF: mnemonic = "RST 38h"; break;
+ case 0xC8:
+ mnemonic = "RET Z";
+ break;
+ case 0xC9:
+ mnemonic = "RET";
+ break;
+ case 0xCB:
+ cbOp = _memoryBus.Read((ushort)(currentPc + 1));
+
+ opGroup = cbOp >> 6; // 00 = Shift, 01 = BIT, 10 = RES, 11 = SET
+ targetBit = (cbOp >> 3) & 0x07; // Extracts a number 0-7
+ regIdx = cbOp & 0x07; // Extracts register index 0-7
+
+ // Map the 0-7 index directly to the Z80 register names
+ targetReg = regNames[regIdx];
+
+ if (opGroup == 0) // Shift/Rotate Group (0x00 to 0x3F)
+ {
+ string[] shiftNames = { "RLC", "RRC", "RL", "RR", "SLA", "SRA", "SLL", "SRL" };
+ string shiftOp = shiftNames[(cbOp >> 3) & 0x07];
+ mnemonic = $"{shiftOp} {targetReg}";
+ }
+ else if (opGroup == 1) // BIT Group (0x40 to 0x7F)
+ {
+ mnemonic = $"BIT {targetBit}, {targetReg}";
+ }
+ else if (opGroup == 2) // RES Group (0x80 to 0xBF)
+ {
+ mnemonic = $"RES {targetBit}, {targetReg}";
+ }
+ else if (opGroup == 3) // SET Group (0xC0 to 0xFF)
+ {
+ mnemonic = $"SET {targetBit}, {targetReg}";
+ }
+ else
+ {
+ mnemonic = $"CB UNKNOWN (CB {cbOp:X2})";
+ }
+ instructionLength = 2;
+ break;
+ case 0xCD:
+ ushort callDest = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
+ mnemonic = $"CALL 0x{callDest:X4}";
+ instructionLength = 3;
+ break;
+ case 0xCE: // ADC A, n
+ byte n = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"ADC A, 0x{n:X2}";
+ instructionLength = 2;
+ break;
+ case 0xD0:
+ mnemonic = "RET NC";
+ break;
+ case 0xD1: mnemonic = "POP DE"; break;
+ case 0xD3:
+ byte outPort = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"OUT (0x{outPort:X2}), A";
+ instructionLength = 2;
+ break;
+ case 0xD9:
+ mnemonic = "EXX";
+ break;
+ case 0xd5:
+ mnemonic = "PUSH DE";
+ break;
+ case 0xD6:
+ byte subImm = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"SUB 0x{subImm:X2}";
+ instructionLength = 2;
+ break;
+ case 0xD8:
+ mnemonic = "RET C";
+ break;
+ case 0xDB: // IN A, (n)
+ n = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"IN A, (0x{n:X2})";
+ instructionLength = 2;
+ break;
+ case 0xDD:
+ {
+ byte ddOpcode = _memoryBus.Read((ushort)(currentPc + 1));
+ if (ddOpcode == 0x09) { mnemonic = "ADD IX, BC"; instructionLength = 2; }
+ else if (ddOpcode == 0x19) { mnemonic = "ADD IX, DE"; instructionLength = 2; }
+ else if (ddOpcode == 0x29) { mnemonic = "ADD IX, IX"; instructionLength = 2; }
+ else if (ddOpcode == 0x39) { mnemonic = "ADD IX, SP"; instructionLength = 2; }
+ else if (ddOpcode == 0x21) // LD IX, nn
+ {
+ ushort ixVal = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
+ mnemonic = $"LD IX, 0x{ixVal:X4}";
+ instructionLength = 4;
+ }
+ else if (ddOpcode == 0x22) // LD (nn), IX
+ {
+ ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
+ mnemonic = $"LD (0x{nn:X4}), IX";
+ instructionLength = 4;
+ }
+ else if (ddOpcode == 0x23) // INC IX
+ {
+ mnemonic = "INC IX";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0x24) // INC IXH
+ {
+ mnemonic = "INC IXH";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0x25) // DEC IXH
+ {
+ mnemonic = "DEC IXH";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0x26) // LD IXH, n
+ {
+ byte nValue = _memoryBus.Read((ushort)(currentPc + 2));
+ mnemonic = $"LD IXH, 0x{nValue:X2}";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x2A) // LD IX, (nn)
+ {
+ ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
+ mnemonic = $"LD IX, (0x{nn:X4})";
+ instructionLength = 4;
+ }
+ else if (ddOpcode == 0x2B) // DEC IX
+ {
+ mnemonic = "DEC IX";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0x2D) // DEC IXL
+ {
+ mnemonic = "DEC IXL";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0x2E) // LD IXL, n
+ {
+ byte nValue = _memoryBus.Read((ushort)(currentPc + 2));
+ mnemonic = $"LD IXL, 0x{nValue:X2}";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x34) // INC (IX+d)
+ {
+ sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = offset >= 0 ? "+" : "";
+ mnemonic = $"INC (IX{sign}{offset})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x35) // DEC (IX+d)
+ {
+ sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = offset >= 0 ? "+" : "";
+ mnemonic = $"DEC (IX{sign}{offset})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x36) // LD (IX+d), n
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ n = _memoryBus.Read((ushort)(currentPc + 3));
+
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD (IX{sign}{d}), 0x{n:X2}";
+ instructionLength = 4;
+ }
+ else if (ddOpcode == 0x46) // LD B, (IX+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD B, (IX{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x4E) // LD C, (IX+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD C, (IX{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x56) // LD D, (IX+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD D, (IX{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x5E) // LD E, (IX+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD E, (IX{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x66) // LD H, (IX+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD H, (IX{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x67) // LD IXH, A
+ {
+ mnemonic = "LD IXH, A";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0x68) // LD IXL, B
+ {
+ mnemonic = "LD IXL, B";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0x69) // LD IXL, C
+ {
+ mnemonic = "LD IXL, C";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0x6A) // LD IXL, D
+ {
+ mnemonic = "LD IXL, D";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0x6E) // LD L, (IX+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD L, (IX{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x6F) // LD IXL, A
+ {
+ mnemonic = $"LD IXL, A)";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0x71) // LD (IX+d), B
+ {
+ sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = offset >= 0 ? "+" : "";
+ mnemonic = $"LD (IX{sign}{offset}), B";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x71) // LD (IX+d), C
+ {
+ sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = offset >= 0 ? "+" : "";
+ mnemonic = $"LD (IX{sign}{offset}), C";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x72) // LD (IX+d), D
+ {
+ sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = offset >= 0 ? "+" : "";
+ mnemonic = $"LD (IX{sign}{offset}), D";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x73) // LD (IX+d), E
+ {
+ sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = offset >= 0 ? "+" : "";
+ mnemonic = $"LD (IX{sign}{offset}), E";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x74) // LD (IX+d), H
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD (IX{sign}{d}), H";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x75) // LD (IX+d), L
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD (IX{sign}{d}), L";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x77) // LD (IX+d), A
+ {
+ // Read the 3rd byte (the displacement)
+ sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+
+ // Format nicely with a + or - sign
+ string sign = offset >= 0 ? "+" : "";
+ mnemonic = $"LD (IX{sign}{offset}), A";
+
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x7C) // LD A, IXH
+ {
+ mnemonic = "LD A, IXH";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0x7E) // LD A, (IX+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD A, (IX{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x86)
+ {
+ sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = offset >= 0 ? "+" : "";
+ mnemonic = $"ADD A, (IX{sign}{offset})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0x96)
+ {
+ sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = offset >= 0 ? "+" : "";
+ mnemonic = $"SUB (IX{sign}{offset})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0xCB)
+ {
+ // DD CB instructions are 4 bytes long!
+ sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ cbOp = _memoryBus.Read((ushort)(currentPc + 3));
+
+ int operation = cbOp >> 6;
+ int bitIndex = (cbOp >> 3) & 0x07;
+ string sign = offset >= 0 ? "+" : "";
+
+ if (operation == 1) mnemonic = $"BIT {bitIndex}, (IX{sign}{offset})";
+ else mnemonic = $"DD CB (IX{sign}{offset}) {cbOp:X2}"; // Fallback
+
+ instructionLength = 4;
+ }
+ else if (ddOpcode == 0xBE) // CP (IX+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"CP (IX{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (ddOpcode == 0xE1) // POP IX
+ {
+ mnemonic = "POP IX";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0xE5) // PUSH IX
+ {
+ mnemonic = "PUSH IX";
+ instructionLength = 2;
+ }
+ else if (ddOpcode == 0xE9) // JP (IX)
+ {
+ mnemonic = "JP (IX)";
+ instructionLength = 2;
+ }
+ else
+ {
+ mnemonic = $"DD PREFIX UNKNOWN (0x{ddOpcode:X2})";
+ instructionLength = 2; // Fallback to prevent UI freezing
+ }
+ break;
+ }
+ case 0xDE:
+ byte sbcValue = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"SBC A, 0x{sbcValue:X2}";
+ instructionLength = 2;
+ break;
+ case 0xE1: mnemonic = "POP HL"; break;
+ case 0xE3:
+ mnemonic = "EX (SP), HL";
+ break;
+ case 0xE5:
+ mnemonic = "PUSH HL";
+ break;
+ case 0xE6:
+ byte andImm = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"AND 0x{andImm:X2}";
+ instructionLength = 2;
+ break;
+ case 0xE9:
+ mnemonic = "JP (HL)";
+ break;
+ case 0xEB:
+ mnemonic = "EX DE, HL";
+ break;
+ case 0xED:
+ byte extendedOp = _memoryBus.Read((ushort)(currentPc + 1));
+
+ switch (extendedOp)
+ {
+ case 0x42: mnemonic = "SBC HL, BC"; instructionLength = 2; break;
+ case 0x43:
+ ushort bcAddr = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
+ mnemonic = $"LD (0x{bcAddr:X4}), BC";
+ instructionLength = 4;
+ break;
+ case 0x44: // NEG
+ mnemonic = "NEG";
+ instructionLength = 2;
+ break;
+ case 0x47:
+ mnemonic = "LD I, A";
+ instructionLength = 2; // 0xED + 0x47
+ break;
+ // Inside your ED prefix switch statement in the debugger:
+ case 0x4A: mnemonic = "ADC HL, BC"; instructionLength = 2; break;
+ case 0x4B:
+ ushort addr4B = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
+ mnemonic = $"LD BC, (0x{addr4B:X4})";
+ instructionLength = 4;
+ break;
+ case 0x4D:
+ mnemonic = "RETI";
+ instructionLength = 2;
+ break;
+ case 0x52:
+ mnemonic = "SBC HL, DE";
+ instructionLength = 2; // ED 52
+ break;
+ case 0x53:
+ ushort deAddr = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
+ mnemonic = $"LD (0x{deAddr:X4}), DE";
+ instructionLength = 4;
+ break;
+ case 0x56:
+ mnemonic = "IM 1";
+ instructionLength = 2;
+ break;
+ case 0x58:
+ mnemonic = "IN E, (C)";
+ instructionLength = 2;
+ break;
+ case 0x5A: mnemonic = "ADC HL, DE"; instructionLength = 2; break;
+ case 0x5B:
+ ushort addr5B = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
+ mnemonic = $"LD DE, (0x{addr5B:X4})";
+ instructionLength = 4;
+ break;
+ case 0x5E: mnemonic = "IM 2"; instructionLength = 2; break;
+ case 0x5F: mnemonic = "LD A, R"; instructionLength = 2; break;
+ case 0x6A: mnemonic = "ADC HL, HL"; instructionLength = 2; break;
+ case 0x62: mnemonic = "SBC HL, HL"; instructionLength = 2; break;
+ case 0x73:
+ ushort addr73 = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
+ mnemonic = $"LD (0x{addr73:X4}), SP";
+ instructionLength = 4;
+ break;
+ case 0x72: // SBC HL, SP
+ mnemonic = "SBC HL, SP";
+ instructionLength = 2;
+ break;
+ case 0x78:
+ mnemonic = "IN A, (C)";
+ instructionLength = 2;
+ break;
+ case 0x7A: mnemonic = "ADC HL, SP"; instructionLength = 2; break;
+ case 0x7B: // LD SP, (nn)
+ ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
+ mnemonic = $"LD SP, (0x{nn:X4})";
+ instructionLength = 4;
+ break;
+ case 0xA0: mnemonic = "LDI"; instructionLength = 2; break;
+ case 0xB0:
+ mnemonic = "LDIR";
+ instructionLength = 2;
+ break;
+ case 0xB1:
+ mnemonic = "CPIR";
+ instructionLength = 2;
+ break;
+ case 0xB8:
+ mnemonic = "LDDR";
+ instructionLength = 2;
+ break;
+
+ default:
+ mnemonic = $"EXT UNKNOWN (ED {extendedOp:X2})";
+ instructionLength = 2; // Most ED instructions are 2 bytes, but some have operands!
+ break;
+ }
+ break;
+ case 0xEE:
+ byte xorVal = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"XOR 0x{xorVal:X2}";
+ instructionLength = 2;
+ break;
+ case 0xF1: mnemonic = "POP AF"; break;
+ case 0xF3:
+ mnemonic = "DI";
+ break;
+ case 0xf5:
+ mnemonic = "PUSH AF";
+ break;
+ case 0xF6:
+ byte orImm = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"OR 0x{orImm:X2}";
+ instructionLength = 2;
+ break;
+ case 0xF9:
+ mnemonic = "LD SP, HL";
+ break;
+ case 0xFB:
+ mnemonic = "EI";
+ break;
+ case 0xFD:
+ {
+ byte fdOpcode = _memoryBus.Read((ushort)(currentPc + 1));
+ if (fdOpcode == 0x09) // ADD IY, BC
+ {
+ mnemonic = $"ADD IY, BC";
+ instructionLength = 2;
+ }
+ else if (fdOpcode == 0x19) // ADD IY, DE
+ {
+ mnemonic = $"ADD IY, DE";
+ instructionLength = 2;
+ }
+ else if (fdOpcode == 0x21) // LD IY, nn
+ {
+ ushort iyVal = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
+ mnemonic = $"LD IY, 0x{iyVal:X4}";
+ instructionLength = 4;
+ }
+ else if (fdOpcode == 0x29) //Add IY, IY
+ {
+ mnemonic = $"ADD IY, IY";
+ instructionLength = 2;
+ }
+ else if (fdOpcode == 0x34) // INC IY
+ {
+ //sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ //string sign = d >= 0 ? "+" : "";
+ mnemonic = $"INC IY";
+ instructionLength = 2;
+ }
+ else if (fdOpcode == 0x34) // INC (IY+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"INC (IY{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x35) // DEC (IY+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : ""; // Gives us a clean + or -
+ mnemonic = $"DEC (IY{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x36) // LD (IY+d), n
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ n = _memoryBus.Read((ushort)(currentPc + 3));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD (IY{sign}{d}), 0x{n:X2}";
+ instructionLength = 4;
+ }
+ else if (fdOpcode == 0x46) // LD B, (IY+d)
+ {
+ sbyte dB = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string signB = dB >= 0 ? "+" : "";
+
+ mnemonic = $"LD B, (IY{signB}{dB})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x4E) // LD C, (IY+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD C, (IY{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x56) // LD D, (IY+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD D, (IY{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x5E) // LD E, (IY+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD E, (IY{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x66)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD H, (IY{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x6E)
+ {
+ sbyte offsetL = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string signL = offsetL >= 0 ? "+" : "";
+
+ mnemonic = $"LD L, (IY{signL}{offsetL})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x72) // LD (IY+d), D
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD (IY{sign}{d}), D";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x73) // LD (IY+d), E
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD (IY{sign}{d}), E";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x74) // LD (IY+d), H
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD (IY{sign}{d}), H";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x77) // LD (IY+d), A
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD (IY{sign}{d}), A";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x7E) // LD A, (IY+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD A, (IY{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x84) // ADD A, IYH
+ {
+
+ mnemonic = $"ADD A, IYH";
+ instructionLength = 2;
+ }
+ else if (fdOpcode == 0x85) // ADD A, IYL
+ {
+ mnemonic = $"ADD A, IYL";
+ instructionLength = 2;
+ }
+ else if (fdOpcode == 0x86) // ADD A, (IY+d)
+ {
+ sbyte dAdd = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string signAdd = dAdd >= 0 ? "+" : "";
+
+ mnemonic = $"ADD A, (IY{signAdd}{dAdd})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x96) // SUB (IY+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"SUB (IY{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0xA6) //AND (IY+d)
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"AND (IY{sign}{d})";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0xCB) // FD CB prefix
+ {
+ cbOp = _memoryBus.Read((ushort)(currentPc + 1));
+
+ opGroup = cbOp >> 6;
+ targetBit = (cbOp >> 3) & 0x07;
+ regIdx = cbOp & 0x07;
+
+ targetReg = regNames[regIdx];
+
+ if (opGroup == 1) mnemonic = $"BIT {targetBit}, {targetReg}";
+ else if (opGroup == 2) mnemonic = $"RES {targetBit}, {targetReg}";
+ else if (opGroup == 3) mnemonic = $"SET {targetBit}, {targetReg}";
+ else mnemonic = $"CB SHIFT/ROTATE (0x{cbOp:X2})";
+
+ instructionLength = 2;
+ }
+ else if (fdOpcode == 0x71) // LD (IY+d), C
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD (IY{sign}{d}), C";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0x75) // LD (IY+d), L
+ {
+ sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
+ string sign = d >= 0 ? "+" : "";
+ mnemonic = $"LD (IY{sign}{d}), L";
+ instructionLength = 3;
+ }
+ else if (fdOpcode == 0xE1)
+ {
+ mnemonic = "POP IY";
+ instructionLength = 2;
+ }
+ else if (fdOpcode == 0xE5)
+ {
+ mnemonic = "PUSH IY";
+ instructionLength = 2;
+ }
+ else
+ {
+ mnemonic = $"FD PREFIX UNKNOWN (0x{fdOpcode:X2})";
+ instructionLength = 2; // Fallback so we don't freeze the UI
+ }
+ break;
+ }
+ case 0xFE:
+ byte cpImm = _memoryBus.Read((ushort)(currentPc + 1));
+ mnemonic = $"CP 0x{cpImm:X2}";
+ instructionLength = 2;
+ break;
+ default:
+ mnemonic = $"UNKNOWN (0x{opcode:X2})";
+ break;
+ }
+
+ lstDisassembly.Items.Add($"{currentPc:X4}: {mnemonic}");
+
+ // Advance the fake PC just for drawing the next line in the UI
+ currentPc += (ushort)instructionLength;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Desktop/DebuggerForm.resx b/Desktop/DebuggerForm.resx
new file mode 100644
index 0000000..7ac8c1e
--- /dev/null
+++ b/Desktop/DebuggerForm.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/Desktop/Desktop.csproj b/Desktop/Desktop.csproj
index 1d41958..878ff67 100644
--- a/Desktop/Desktop.csproj
+++ b/Desktop/Desktop.csproj
@@ -9,11 +9,21 @@
-
+
-
+
+ Always
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Desktop/Form1.Designer.cs b/Desktop/Form1.Designer.cs
index 7cf6955..885ec6a 100644
--- a/Desktop/Form1.Designer.cs
+++ b/Desktop/Form1.Designer.cs
@@ -28,12 +28,33 @@
///
private void InitializeComponent()
{
- this.components = new System.ComponentModel.Container();
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(800, 450);
- this.Text = "Form1";
+ button1 = new Button();
+ SuspendLayout();
+ //
+ // button1
+ //
+ button1.Location = new Point(304, 268);
+ button1.Name = "button1";
+ button1.Size = new Size(94, 29);
+ button1.TabIndex = 0;
+ button1.Text = "button1";
+ button1.UseVisualStyleBackColor = true;
+ button1.Click += button1_Click;
+ //
+ // Form1
+ //
+ AutoScaleDimensions = new SizeF(8F, 20F);
+ AutoScaleMode = AutoScaleMode.Font;
+ ClientSize = new Size(800, 450);
+ Controls.Add(button1);
+ Name = "Form1";
+ Text = "Form1";
+ Click += button1_Click;
+ ResumeLayout(false);
}
#endregion
+
+ private Button button1;
}
}
diff --git a/Desktop/Form1.cs b/Desktop/Form1.cs
index 3612dfe..41e458f 100644
--- a/Desktop/Form1.cs
+++ b/Desktop/Form1.cs
@@ -1,10 +1,50 @@
+using System.Reflection;
+using System.Reflection.PortableExecutable;
+using Core;
namespace Desktop
{
public partial class Form1 : Form
{
+ private SmsMachine _machine = null!;
+ private DebuggerForm _debugger;
+
+ public ushort? Breakpoint
+ {
+ get => _machine?.Breakpoint;
+ set { if (_machine != null) _machine.Breakpoint = value; }
+ }
+
public Form1()
{
InitializeComponent();
+ _machine = new SmsMachine();
+ }
+
+ private void button1_Click(object sender, EventArgs e)
+ {
+ // 1. Load a commercial Master System ROM!
+
+ byte[] rom = File.ReadAllBytes(@"C:\Parsons\Local Code Projects\ParsonsMasterSystem2026\Desktop\ROMS\Golden Axe Warrior.sms");
+
+
+ try
+ {
+
+ // 2. Jam it into the Sega Mapper
+ _machine.LoadCartridge(rom);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show(ex.Message);
+ }
+
+ // 3. Open the Debugger to look around
+ if (_debugger == null || _debugger.IsDisposed)
+ {
+ _debugger = new DebuggerForm(_machine.Cpu, _machine.MemoryBus, this);
+ _debugger.Show();
+ }
+
}
}
}
diff --git a/Desktop/Form1.resx b/Desktop/Form1.resx
index 1af7de1..8b2ff64 100644
--- a/Desktop/Form1.resx
+++ b/Desktop/Form1.resx
@@ -1,17 +1,17 @@
-
diff --git a/Desktop/ROMS/Golden Axe Warrior.sms b/Desktop/ROMS/Golden Axe Warrior.sms
new file mode 100644
index 0000000..0f253de
Binary files /dev/null and b/Desktop/ROMS/Golden Axe Warrior.sms differ