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