176 lines
5.7 KiB
C#
176 lines
5.7 KiB
C#
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 MemoryBus _memoryBus;
|
|
|
|
public DebuggerForm(Z80 cpu, MemoryBus memoryBus)
|
|
{
|
|
InitializeComponent();
|
|
_cpu = cpu;
|
|
_memoryBus = memoryBus;
|
|
|
|
// Set default memory view address
|
|
txtMemoryStart.Text = "0000";
|
|
UpdateDisplay();
|
|
UpdateStackView();
|
|
UpdateDisassemblyView();
|
|
}
|
|
|
|
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);
|
|
}
|
|
finally
|
|
{
|
|
UpdateDisplay();
|
|
}
|
|
}
|
|
|
|
private void btnRefreshMemory_Click(object sender, EventArgs e)
|
|
{
|
|
UpdateDisplay();
|
|
}
|
|
|
|
private void btnExit_Click(object sender, EventArgs e)
|
|
{
|
|
Environment.Exit(0);
|
|
}
|
|
|
|
// This is the master function that pulls state from the CPU
|
|
private void UpdateDisplay()
|
|
{
|
|
// 1. Update Registers (Formatting as 4-character Hex strings)
|
|
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}";
|
|
|
|
// 2. Update Flags & T-States
|
|
lblFlags.Text = $"Flags: {_cpu.GetFlagsString()}";
|
|
lblTStates.Text = $"T-States: {_cpu.TotalTStates}";
|
|
|
|
// 3. Update Memory Viewer
|
|
UpdateMemoryView();
|
|
}
|
|
|
|
private void UpdateMemoryView()
|
|
{
|
|
// 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 < 7; 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();
|
|
|
|
// The Z80 stack starts at 0xFFFF and grows downwards.
|
|
// If SP is at the very top (e.g., 0xFFFF), we don't want to read past the end of memory and crash!
|
|
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 long
|
|
|
|
// This switch statement will grow as you add more opcodes to the CPU!
|
|
switch (opcode)
|
|
{
|
|
case 0x00:
|
|
mnemonic = "NOP";
|
|
break;
|
|
case 0x3E:
|
|
// LD A, n (Loads the next byte into register A)
|
|
byte nextByte = _memoryBus.Read((ushort)(currentPc + 1));
|
|
mnemonic = $"LD A, 0x{nextByte:X2}";
|
|
instructionLength = 2; // This instruction takes up 2 bytes
|
|
break;
|
|
default:
|
|
mnemonic = $"UNKNOWN (0x{opcode:X2})";
|
|
break;
|
|
}
|
|
|
|
// Add to the list box
|
|
lstDisassembly.Items.Add($"{currentPc:X4}: {mnemonic}");
|
|
|
|
// Advance to the start of the next instruction
|
|
currentPc += (ushort)instructionLength;
|
|
}
|
|
}
|
|
}
|
|
} |