Implemented a load of Z80 OpCodes. Added SimpleIOBus.

This commit is contained in:
2026-04-09 14:35:38 +01:00
parent f14d2c4ccc
commit 340583d663
8 changed files with 292 additions and 38 deletions

View File

@@ -129,7 +129,7 @@
// txtMemoryStart
//
txtMemoryStart.Location = new Point(238, 10);
txtMemoryStart.Margin = new Padding(2, 2, 2, 2);
txtMemoryStart.Margin = new Padding(2);
txtMemoryStart.Name = "txtMemoryStart";
txtMemoryStart.Size = new Size(121, 27);
txtMemoryStart.TabIndex = 9;
@@ -140,7 +140,7 @@
// btnStep
//
btnStep.Location = new Point(9, 315);
btnStep.Margin = new Padding(2, 2, 2, 2);
btnStep.Margin = new Padding(2);
btnStep.Name = "btnStep";
btnStep.Size = new Size(90, 27);
btnStep.TabIndex = 12;
@@ -151,17 +151,18 @@
// btnRun
//
btnRun.Location = new Point(122, 315);
btnRun.Margin = new Padding(2, 2, 2, 2);
btnRun.Margin = new Padding(2);
btnRun.Name = "btnRun";
btnRun.Size = new Size(90, 27);
btnRun.TabIndex = 13;
btnRun.Text = "Run";
btnRun.UseVisualStyleBackColor = true;
btnRun.Click += btnRun_Click;
//
// btnRefreshMemory
//
btnRefreshMemory.Location = new Point(376, 7);
btnRefreshMemory.Margin = new Padding(2, 2, 2, 2);
btnRefreshMemory.Margin = new Padding(2);
btnRefreshMemory.Name = "btnRefreshMemory";
btnRefreshMemory.Size = new Size(90, 27);
btnRefreshMemory.TabIndex = 14;
@@ -172,7 +173,7 @@
// txtMemoryView
//
txtMemoryView.Location = new Point(88, 80);
txtMemoryView.Margin = new Padding(2, 2, 2, 2);
txtMemoryView.Margin = new Padding(2);
txtMemoryView.Name = "txtMemoryView";
txtMemoryView.Size = new Size(416, 195);
txtMemoryView.TabIndex = 15;
@@ -182,7 +183,7 @@
//
lstDisassembly.FormattingEnabled = true;
lstDisassembly.Location = new Point(533, 11);
lstDisassembly.Margin = new Padding(2, 2, 2, 2);
lstDisassembly.Margin = new Padding(2);
lstDisassembly.Name = "lstDisassembly";
lstDisassembly.Size = new Size(186, 264);
lstDisassembly.TabIndex = 16;
@@ -191,7 +192,7 @@
//
lstStack.FormattingEnabled = true;
lstStack.Location = new Point(723, 11);
lstStack.Margin = new Padding(2, 2, 2, 2);
lstStack.Margin = new Padding(2);
lstStack.Name = "lstStack";
lstStack.Size = new Size(186, 264);
lstStack.TabIndex = 17;
@@ -199,7 +200,7 @@
// btnExit
//
btnExit.Location = new Point(818, 313);
btnExit.Margin = new Padding(2, 2, 2, 2);
btnExit.Margin = new Padding(2);
btnExit.Name = "btnExit";
btnExit.Size = new Size(90, 27);
btnExit.TabIndex = 18;
@@ -228,9 +229,9 @@
Controls.Add(lblDE);
Controls.Add(lblBC);
Controls.Add(lblAF);
Margin = new Padding(2, 2, 2, 2);
Margin = new Padding(2);
Name = "DebuggerForm";
Text = "DebuggerForm";
Text = "Debugger";
ResumeLayout(false);
PerformLayout();
}
@@ -250,9 +251,9 @@
private Button btnRun;
private Button btnRefreshMemory;
private RichTextBox txtMemoryView;
private ListBox lstDisassembly;
private ListBox lstStack;
private Button btnExit;
public ListBox lstDisassembly;
//private TextBox textBox4;
}
}

View File

@@ -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;
}
}

View File

@@ -36,8 +36,7 @@
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(800, 450);
Name = "Form1";
Text = "Form1";
//this.Load += new System.EventHandler(this.Form1_Load);
Text = "Parsons Sinclair ZX Spectrum 48K - 2026";
ResumeLayout(false);
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Windows.Forms;
using Core.Cpu;
using Core.Io;
using Core.Memory;
namespace Desktop
@@ -9,6 +10,7 @@ namespace Desktop
{
private Z80 _cpu = null!;
private MemoryBus _memoryBus = null!;
private SimpleIoBus _simpleIoBus = null!;
public Form1()
{
@@ -22,16 +24,16 @@ namespace Desktop
{
// 1. Initialize the memory bus
_memoryBus = new MemoryBus();
_simpleIoBus = new SimpleIoBus();
// 2. Load the ROM from disk
// Make sure "48.rom" matches the name of the file you added to your project
// 2. Load the ROM
byte[] romData = RomLoader.Load("48.rom");
// 3. Inject the ROM into the memory bus
_memoryBus.LoadRom(romData);
// 4. Initialize the CPU with the populated memory
_cpu = new Z80(_memoryBus);
_cpu = new Z80(_memoryBus, _simpleIoBus);
DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus);
debugger.Show();