Implemented a load of Z80 OpCodes. Added SimpleIOBus.
This commit is contained in:
@@ -10,6 +10,7 @@ namespace Desktop
|
||||
{
|
||||
private readonly Z80 _cpu;
|
||||
private readonly MemoryBus _memoryBus;
|
||||
private bool _isRunning = false;
|
||||
|
||||
public DebuggerForm(Z80 cpu, MemoryBus memoryBus)
|
||||
{
|
||||
@@ -46,6 +47,53 @@ namespace Desktop
|
||||
UpdateDisplay();
|
||||
}
|
||||
|
||||
private async void btnRun_Click(object sender, EventArgs e)
|
||||
{
|
||||
// If it is already running, this button acts as a STOP button
|
||||
if (_isRunning)
|
||||
{
|
||||
_isRunning = false;
|
||||
btnRun.Text = "Run";
|
||||
return;
|
||||
}
|
||||
|
||||
// Start the run state
|
||||
_isRunning = true;
|
||||
btnRun.Text = "Stop";
|
||||
|
||||
// Fire up a background thread so the Windows UI doesn't freeze
|
||||
await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Free-run the CPU until the flag is flipped or it crashes!
|
||||
while (_isRunning)
|
||||
{
|
||||
_cpu.Step();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_isRunning = false;
|
||||
|
||||
// We are on a background thread. We MUST use Invoke to tell the
|
||||
// main UI thread to update the labels and show the message box!
|
||||
this.Invoke((MethodInvoker)delegate
|
||||
{
|
||||
btnRun.Text = "Run";
|
||||
UpdateDisplay();
|
||||
MessageBox.Show(ex.Message, "CPU Break", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// If the user clicked Stop manually (no exception thrown), we still want to update the UI
|
||||
if (!_isRunning)
|
||||
{
|
||||
UpdateDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
private void btnExit_Click(object sender, EventArgs e)
|
||||
{
|
||||
Environment.Exit(0);
|
||||
@@ -68,6 +116,8 @@ namespace Desktop
|
||||
|
||||
// 3. Update Memory Viewer
|
||||
UpdateMemoryView();
|
||||
UpdateStackView();
|
||||
UpdateDisassemblyView();
|
||||
}
|
||||
|
||||
private void UpdateMemoryView()
|
||||
@@ -108,9 +158,6 @@ namespace Desktop
|
||||
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;
|
||||
|
||||
@@ -139,36 +186,103 @@ namespace Desktop
|
||||
{
|
||||
lstDisassembly.Items.Clear();
|
||||
|
||||
// THIS is the critical link! It forces the top of the list
|
||||
// to always be exactly where the CPU currently is.
|
||||
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
|
||||
int instructionLength = 1; // Default to 1
|
||||
|
||||
// This switch statement will grow as you add more opcodes to the CPU!
|
||||
switch (opcode)
|
||||
{
|
||||
case 0x00:
|
||||
mnemonic = "NOP";
|
||||
case 0x00: mnemonic = "NOP"; 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 0x2B:
|
||||
mnemonic = "DEC HL";
|
||||
break;
|
||||
case 0x36:
|
||||
byte memValue = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
mnemonic = $"LD (HL), 0x{memValue:X2}";
|
||||
instructionLength = 2;
|
||||
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
|
||||
mnemonic = $"LD A, 0x{_memoryBus.Read((ushort)(currentPc + 1)):X2}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0x47:
|
||||
mnemonic = "LD B, A";
|
||||
break;
|
||||
case 0x62:
|
||||
mnemonic = "LD H, D";
|
||||
break;
|
||||
case 0x6B:
|
||||
mnemonic = "LD L, E";
|
||||
break;
|
||||
case 0xAF:
|
||||
mnemonic = "XOR A";
|
||||
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 0xD3:
|
||||
byte outPort = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
mnemonic = $"OUT (0x{outPort:X2}), A";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xDE:
|
||||
byte sbcValue = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
mnemonic = $"SBC A, 0x{sbcValue:X2}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xED:
|
||||
byte extendedOp = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
|
||||
switch (extendedOp)
|
||||
{
|
||||
// Example: ED 47 is LD I, A
|
||||
case 0x47:
|
||||
mnemonic = "LD I, A";
|
||||
instructionLength = 2; // 0xED + 0x47
|
||||
break;
|
||||
|
||||
// Example: ED B0 is LDIR (a massive block copy instruction)
|
||||
case 0xB0:
|
||||
mnemonic = "LDIR";
|
||||
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 0xF3:
|
||||
mnemonic = "DI";
|
||||
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
|
||||
// Advance the fake PC just for drawing the next line in the UI
|
||||
currentPc += (ushort)instructionLength;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user