Implemented a load of Z80 OpCodes. Added SimpleIOBus.
This commit is contained in:
121
Core/Cpu/Z80.cs
121
Core/Cpu/Z80.cs
@@ -8,6 +8,10 @@ namespace Core.Cpu
|
|||||||
//T-State counter
|
//T-State counter
|
||||||
public long TotalTStates { get; set; }
|
public long TotalTStates { get; set; }
|
||||||
|
|
||||||
|
// Interrupt Flip-Flops
|
||||||
|
public bool IFF1;
|
||||||
|
public bool IFF2;
|
||||||
|
|
||||||
// Main Register Set
|
// Main Register Set
|
||||||
public RegisterPair AF;
|
public RegisterPair AF;
|
||||||
public RegisterPair BC;
|
public RegisterPair BC;
|
||||||
@@ -32,11 +36,13 @@ namespace Core.Cpu
|
|||||||
|
|
||||||
// The Memory Bus
|
// The Memory Bus
|
||||||
private readonly IMemory _memory;
|
private readonly IMemory _memory;
|
||||||
|
private readonly IIoBus _ioBus;
|
||||||
|
|
||||||
public Z80(IMemory memory)
|
public Z80(IMemory memory, IIoBus ioBus)
|
||||||
{
|
{
|
||||||
_memory = memory;
|
_memory = memory;
|
||||||
Reset();
|
_ioBus = ioBus;
|
||||||
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Reset()
|
public void Reset()
|
||||||
@@ -62,6 +68,19 @@ namespace Core.Cpu
|
|||||||
return tStates;
|
return tStates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reads a 16-bit word from the current PC (Little-Endian) and advances PC by 2
|
||||||
|
private ushort FetchWord()
|
||||||
|
{
|
||||||
|
byte low = _memory.Read(PC++);
|
||||||
|
byte high = _memory.Read(PC++);
|
||||||
|
return (ushort)((high << 8) | low);
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte FetchByte()
|
||||||
|
{
|
||||||
|
return _memory.Read(PC++);
|
||||||
|
}
|
||||||
|
|
||||||
public string GetFlagsString()
|
public string GetFlagsString()
|
||||||
{
|
{
|
||||||
byte f = AF.Low;
|
byte f = AF.Low;
|
||||||
@@ -75,18 +94,110 @@ namespace Core.Cpu
|
|||||||
$"C:{f & 1}";
|
$"C:{f & 1}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Sbc(byte value)
|
||||||
|
{
|
||||||
|
byte a = AF.High;
|
||||||
|
byte carry = (byte)(AF.Low & 0x01); // Get the current Carry flag (Bit 0)
|
||||||
|
|
||||||
|
// Calculate the raw integer result to check for borrows/underflows
|
||||||
|
int result = a - value - carry;
|
||||||
|
|
||||||
|
// Update the Accumulator
|
||||||
|
AF.High = (byte)result;
|
||||||
|
|
||||||
|
// --- Update Flags (F Register) ---
|
||||||
|
AF.Low = 0; // Clear all flags
|
||||||
|
|
||||||
|
// Sign Flag (Bit 7)
|
||||||
|
if ((result & 0x80) != 0) AF.Low |= 0x80;
|
||||||
|
|
||||||
|
// Zero Flag (Bit 6)
|
||||||
|
if ((byte)result == 0) AF.Low |= 0x40;
|
||||||
|
|
||||||
|
// Half-Carry Flag (Bit 4) - Set if borrow from bit 4
|
||||||
|
if (((a & 0x0F) - (value & 0x0F) - carry) < 0) AF.Low |= 0x10;
|
||||||
|
|
||||||
|
// Overflow Flag (Bit 2) - Set if operands have different signs and result sign changes
|
||||||
|
if ((((a ^ value) & 0x80) != 0) && (((a ^ result) & 0x80) != 0)) AF.Low |= 0x04;
|
||||||
|
|
||||||
|
// Subtract Flag (Bit 1) - ALWAYS set for subtraction
|
||||||
|
AF.Low |= 0x02;
|
||||||
|
|
||||||
|
// Carry Flag (Bit 0) - Set if the overall result dropped below 0
|
||||||
|
if (result < 0) AF.Low |= 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
private int ExecuteOpcode(byte opcode)
|
private int ExecuteOpcode(byte opcode)
|
||||||
{
|
{
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
case 0x00: // NOP (No Operation)
|
case 0x00: // NOP
|
||||||
return 4; // Takes 4 T-states
|
return 4;
|
||||||
|
case 0x11: //LD DE, nn
|
||||||
|
DE.Word = FetchWord();
|
||||||
|
return 10;
|
||||||
|
case 0x2B: // DEC HL
|
||||||
|
HL.Word--;
|
||||||
|
return 6;
|
||||||
|
case 0x36: // LD (HL), n
|
||||||
|
byte nValue = FetchByte();
|
||||||
|
_memory.Write(HL.Word, nValue);
|
||||||
|
return 10;
|
||||||
|
case 0x3E: //LD A, n
|
||||||
|
AF.High = FetchByte();
|
||||||
|
return 7;
|
||||||
|
case 0x47: // LD B, A
|
||||||
|
BC.High = AF.High;
|
||||||
|
return 4;
|
||||||
|
case 0x62: // LD H, D
|
||||||
|
HL.High = DE.High;
|
||||||
|
return 4;
|
||||||
|
case 0x6B: // LD L, E
|
||||||
|
HL.Low = DE.Low;
|
||||||
|
return 4;
|
||||||
|
case 0xC3:
|
||||||
|
PC = FetchWord();
|
||||||
|
return 10;
|
||||||
|
case 0xD3: // OUT (n), A
|
||||||
|
byte portOffset = FetchByte();
|
||||||
|
|
||||||
// We will expand this massive list soon!
|
// The Z80 puts 'A' on the top 8 bits, and 'n' on the bottom 8 bits of the port address
|
||||||
|
ushort portAddress = (ushort)((AF.High << 8) | portOffset);
|
||||||
|
|
||||||
|
_ioBus.Write(portAddress, AF.High);
|
||||||
|
|
||||||
|
return 11; // Takes 11 T-States
|
||||||
|
case 0xDE: // SBC A, n
|
||||||
|
Sbc(FetchByte());
|
||||||
|
return 7;
|
||||||
|
case 0xED:
|
||||||
|
return ExecuteExtendedPrefix();
|
||||||
|
case 0xF3: // DI (Disable Interrupts)
|
||||||
|
IFF1 = false;
|
||||||
|
IFF2 = false;
|
||||||
|
return 4;
|
||||||
|
case 0xAF: // XOR A
|
||||||
|
AF.High = 0;
|
||||||
|
AF.Low = 0x44;
|
||||||
|
return 4;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Opcode 0x{opcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
|
throw new NotImplementedException($"Opcode 0x{opcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private int ExecuteExtendedPrefix()
|
||||||
|
{
|
||||||
|
// Fetch the actual extended instruction
|
||||||
|
byte extendedOpcode = _memory.Read(PC++);
|
||||||
|
|
||||||
|
switch (extendedOpcode)
|
||||||
|
{
|
||||||
|
case 0x47: // LD I, A
|
||||||
|
I = AF.High;
|
||||||
|
return 9;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"Extended ED Opcode 0x{extendedOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
8
Core/Interfaces/IIoBus.cs
Normal file
8
Core/Interfaces/IIoBus.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Core.Interfaces
|
||||||
|
{
|
||||||
|
public interface IIoBus
|
||||||
|
{
|
||||||
|
byte Read(ushort port);
|
||||||
|
void Write(ushort port, byte value);
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Core/Io/SimpleIoBus.cs
Normal file
20
Core/Io/SimpleIoBus.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using Core.Interfaces;
|
||||||
|
|
||||||
|
namespace Core.Io
|
||||||
|
{
|
||||||
|
public class SimpleIoBus : IIoBus
|
||||||
|
{
|
||||||
|
public byte Read(ushort port)
|
||||||
|
{
|
||||||
|
// If the CPU reads an unconnected port, the Z80 usually sees 0xFF
|
||||||
|
return 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Write(ushort port, byte value)
|
||||||
|
{
|
||||||
|
// For now, let's just log it to the Visual Studio Output window
|
||||||
|
Debug.WriteLine($"Hardware I/O Write -> Port: 0x{port:X4}, Value: 0x{value:X2}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,15 +24,14 @@ namespace Core.Memory
|
|||||||
|
|
||||||
if (address < 0x4000)
|
if (address < 0x4000)
|
||||||
{
|
{
|
||||||
// Attempted to write to the ROM area.
|
// Cannot write to ROM - Do nothing - maybe throw an exception
|
||||||
// We simply ignore the write command, just like real hardware.
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_memory[address] = value;
|
_memory[address] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper method to load the original Sinclair ROM file
|
// Load the ROM file
|
||||||
public void LoadRom(byte[] romData)
|
public void LoadRom(byte[] romData)
|
||||||
{
|
{
|
||||||
if (romData.Length > 0x4000)
|
if (romData.Length > 0x4000)
|
||||||
@@ -40,7 +39,7 @@ namespace Core.Memory
|
|||||||
throw new ArgumentException("ROM file exceeds the 16KB capacity of Bank 0.");
|
throw new ArgumentException("ROM file exceeds the 16KB capacity of Bank 0.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the ROM data into the very beginning of the memory array
|
// Copy the ROM
|
||||||
Array.Copy(romData, 0, _memory, 0, romData.Length);
|
Array.Copy(romData, 0, _memory, 0, romData.Length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
Desktop/DebuggerForm.Designer.cs
generated
23
Desktop/DebuggerForm.Designer.cs
generated
@@ -129,7 +129,7 @@
|
|||||||
// txtMemoryStart
|
// txtMemoryStart
|
||||||
//
|
//
|
||||||
txtMemoryStart.Location = new Point(238, 10);
|
txtMemoryStart.Location = new Point(238, 10);
|
||||||
txtMemoryStart.Margin = new Padding(2, 2, 2, 2);
|
txtMemoryStart.Margin = new Padding(2);
|
||||||
txtMemoryStart.Name = "txtMemoryStart";
|
txtMemoryStart.Name = "txtMemoryStart";
|
||||||
txtMemoryStart.Size = new Size(121, 27);
|
txtMemoryStart.Size = new Size(121, 27);
|
||||||
txtMemoryStart.TabIndex = 9;
|
txtMemoryStart.TabIndex = 9;
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
// btnStep
|
// btnStep
|
||||||
//
|
//
|
||||||
btnStep.Location = new Point(9, 315);
|
btnStep.Location = new Point(9, 315);
|
||||||
btnStep.Margin = new Padding(2, 2, 2, 2);
|
btnStep.Margin = new Padding(2);
|
||||||
btnStep.Name = "btnStep";
|
btnStep.Name = "btnStep";
|
||||||
btnStep.Size = new Size(90, 27);
|
btnStep.Size = new Size(90, 27);
|
||||||
btnStep.TabIndex = 12;
|
btnStep.TabIndex = 12;
|
||||||
@@ -151,17 +151,18 @@
|
|||||||
// btnRun
|
// btnRun
|
||||||
//
|
//
|
||||||
btnRun.Location = new Point(122, 315);
|
btnRun.Location = new Point(122, 315);
|
||||||
btnRun.Margin = new Padding(2, 2, 2, 2);
|
btnRun.Margin = new Padding(2);
|
||||||
btnRun.Name = "btnRun";
|
btnRun.Name = "btnRun";
|
||||||
btnRun.Size = new Size(90, 27);
|
btnRun.Size = new Size(90, 27);
|
||||||
btnRun.TabIndex = 13;
|
btnRun.TabIndex = 13;
|
||||||
btnRun.Text = "Run";
|
btnRun.Text = "Run";
|
||||||
btnRun.UseVisualStyleBackColor = true;
|
btnRun.UseVisualStyleBackColor = true;
|
||||||
|
btnRun.Click += btnRun_Click;
|
||||||
//
|
//
|
||||||
// btnRefreshMemory
|
// btnRefreshMemory
|
||||||
//
|
//
|
||||||
btnRefreshMemory.Location = new Point(376, 7);
|
btnRefreshMemory.Location = new Point(376, 7);
|
||||||
btnRefreshMemory.Margin = new Padding(2, 2, 2, 2);
|
btnRefreshMemory.Margin = new Padding(2);
|
||||||
btnRefreshMemory.Name = "btnRefreshMemory";
|
btnRefreshMemory.Name = "btnRefreshMemory";
|
||||||
btnRefreshMemory.Size = new Size(90, 27);
|
btnRefreshMemory.Size = new Size(90, 27);
|
||||||
btnRefreshMemory.TabIndex = 14;
|
btnRefreshMemory.TabIndex = 14;
|
||||||
@@ -172,7 +173,7 @@
|
|||||||
// txtMemoryView
|
// txtMemoryView
|
||||||
//
|
//
|
||||||
txtMemoryView.Location = new Point(88, 80);
|
txtMemoryView.Location = new Point(88, 80);
|
||||||
txtMemoryView.Margin = new Padding(2, 2, 2, 2);
|
txtMemoryView.Margin = new Padding(2);
|
||||||
txtMemoryView.Name = "txtMemoryView";
|
txtMemoryView.Name = "txtMemoryView";
|
||||||
txtMemoryView.Size = new Size(416, 195);
|
txtMemoryView.Size = new Size(416, 195);
|
||||||
txtMemoryView.TabIndex = 15;
|
txtMemoryView.TabIndex = 15;
|
||||||
@@ -182,7 +183,7 @@
|
|||||||
//
|
//
|
||||||
lstDisassembly.FormattingEnabled = true;
|
lstDisassembly.FormattingEnabled = true;
|
||||||
lstDisassembly.Location = new Point(533, 11);
|
lstDisassembly.Location = new Point(533, 11);
|
||||||
lstDisassembly.Margin = new Padding(2, 2, 2, 2);
|
lstDisassembly.Margin = new Padding(2);
|
||||||
lstDisassembly.Name = "lstDisassembly";
|
lstDisassembly.Name = "lstDisassembly";
|
||||||
lstDisassembly.Size = new Size(186, 264);
|
lstDisassembly.Size = new Size(186, 264);
|
||||||
lstDisassembly.TabIndex = 16;
|
lstDisassembly.TabIndex = 16;
|
||||||
@@ -191,7 +192,7 @@
|
|||||||
//
|
//
|
||||||
lstStack.FormattingEnabled = true;
|
lstStack.FormattingEnabled = true;
|
||||||
lstStack.Location = new Point(723, 11);
|
lstStack.Location = new Point(723, 11);
|
||||||
lstStack.Margin = new Padding(2, 2, 2, 2);
|
lstStack.Margin = new Padding(2);
|
||||||
lstStack.Name = "lstStack";
|
lstStack.Name = "lstStack";
|
||||||
lstStack.Size = new Size(186, 264);
|
lstStack.Size = new Size(186, 264);
|
||||||
lstStack.TabIndex = 17;
|
lstStack.TabIndex = 17;
|
||||||
@@ -199,7 +200,7 @@
|
|||||||
// btnExit
|
// btnExit
|
||||||
//
|
//
|
||||||
btnExit.Location = new Point(818, 313);
|
btnExit.Location = new Point(818, 313);
|
||||||
btnExit.Margin = new Padding(2, 2, 2, 2);
|
btnExit.Margin = new Padding(2);
|
||||||
btnExit.Name = "btnExit";
|
btnExit.Name = "btnExit";
|
||||||
btnExit.Size = new Size(90, 27);
|
btnExit.Size = new Size(90, 27);
|
||||||
btnExit.TabIndex = 18;
|
btnExit.TabIndex = 18;
|
||||||
@@ -228,9 +229,9 @@
|
|||||||
Controls.Add(lblDE);
|
Controls.Add(lblDE);
|
||||||
Controls.Add(lblBC);
|
Controls.Add(lblBC);
|
||||||
Controls.Add(lblAF);
|
Controls.Add(lblAF);
|
||||||
Margin = new Padding(2, 2, 2, 2);
|
Margin = new Padding(2);
|
||||||
Name = "DebuggerForm";
|
Name = "DebuggerForm";
|
||||||
Text = "DebuggerForm";
|
Text = "Debugger";
|
||||||
ResumeLayout(false);
|
ResumeLayout(false);
|
||||||
PerformLayout();
|
PerformLayout();
|
||||||
}
|
}
|
||||||
@@ -250,9 +251,9 @@
|
|||||||
private Button btnRun;
|
private Button btnRun;
|
||||||
private Button btnRefreshMemory;
|
private Button btnRefreshMemory;
|
||||||
private RichTextBox txtMemoryView;
|
private RichTextBox txtMemoryView;
|
||||||
private ListBox lstDisassembly;
|
|
||||||
private ListBox lstStack;
|
private ListBox lstStack;
|
||||||
private Button btnExit;
|
private Button btnExit;
|
||||||
|
public ListBox lstDisassembly;
|
||||||
//private TextBox textBox4;
|
//private TextBox textBox4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,7 @@ namespace Desktop
|
|||||||
{
|
{
|
||||||
private readonly Z80 _cpu;
|
private readonly Z80 _cpu;
|
||||||
private readonly MemoryBus _memoryBus;
|
private readonly MemoryBus _memoryBus;
|
||||||
|
private bool _isRunning = false;
|
||||||
|
|
||||||
public DebuggerForm(Z80 cpu, MemoryBus memoryBus)
|
public DebuggerForm(Z80 cpu, MemoryBus memoryBus)
|
||||||
{
|
{
|
||||||
@@ -46,6 +47,53 @@ namespace Desktop
|
|||||||
UpdateDisplay();
|
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)
|
private void btnExit_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
@@ -68,6 +116,8 @@ namespace Desktop
|
|||||||
|
|
||||||
// 3. Update Memory Viewer
|
// 3. Update Memory Viewer
|
||||||
UpdateMemoryView();
|
UpdateMemoryView();
|
||||||
|
UpdateStackView();
|
||||||
|
UpdateDisassemblyView();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateMemoryView()
|
private void UpdateMemoryView()
|
||||||
@@ -108,9 +158,6 @@ namespace Desktop
|
|||||||
private void UpdateStackView()
|
private void UpdateStackView()
|
||||||
{
|
{
|
||||||
lstStack.Items.Clear();
|
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;
|
int itemsToShow = 5;
|
||||||
ushort currentSp = _cpu.SP;
|
ushort currentSp = _cpu.SP;
|
||||||
|
|
||||||
@@ -139,36 +186,103 @@ namespace Desktop
|
|||||||
{
|
{
|
||||||
lstDisassembly.Items.Clear();
|
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;
|
ushort currentPc = _cpu.PC;
|
||||||
|
|
||||||
int instructionsToShow = 8;
|
int instructionsToShow = 8;
|
||||||
|
|
||||||
for (int i = 0; i < instructionsToShow; i++)
|
for (int i = 0; i < instructionsToShow; i++)
|
||||||
{
|
{
|
||||||
byte opcode = _memoryBus.Read(currentPc);
|
byte opcode = _memoryBus.Read(currentPc);
|
||||||
string mnemonic;
|
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)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
case 0x00:
|
case 0x00: mnemonic = "NOP"; break;
|
||||||
mnemonic = "NOP";
|
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;
|
break;
|
||||||
case 0x3E:
|
case 0x3E:
|
||||||
// LD A, n (Loads the next byte into register A)
|
mnemonic = $"LD A, 0x{_memoryBus.Read((ushort)(currentPc + 1)):X2}";
|
||||||
byte nextByte = _memoryBus.Read((ushort)(currentPc + 1));
|
instructionLength = 2;
|
||||||
mnemonic = $"LD A, 0x{nextByte:X2}";
|
break;
|
||||||
instructionLength = 2; // This instruction takes up 2 bytes
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
mnemonic = $"UNKNOWN (0x{opcode:X2})";
|
mnemonic = $"UNKNOWN (0x{opcode:X2})";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add to the list box
|
|
||||||
lstDisassembly.Items.Add($"{currentPc:X4}: {mnemonic}");
|
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;
|
currentPc += (ushort)instructionLength;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
3
Desktop/Form1.Designer.cs
generated
3
Desktop/Form1.Designer.cs
generated
@@ -36,8 +36,7 @@
|
|||||||
AutoScaleMode = AutoScaleMode.Font;
|
AutoScaleMode = AutoScaleMode.Font;
|
||||||
ClientSize = new Size(800, 450);
|
ClientSize = new Size(800, 450);
|
||||||
Name = "Form1";
|
Name = "Form1";
|
||||||
Text = "Form1";
|
Text = "Parsons Sinclair ZX Spectrum 48K - 2026";
|
||||||
//this.Load += new System.EventHandler(this.Form1_Load);
|
|
||||||
ResumeLayout(false);
|
ResumeLayout(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Core.Cpu;
|
using Core.Cpu;
|
||||||
|
using Core.Io;
|
||||||
using Core.Memory;
|
using Core.Memory;
|
||||||
|
|
||||||
namespace Desktop
|
namespace Desktop
|
||||||
@@ -9,6 +10,7 @@ namespace Desktop
|
|||||||
{
|
{
|
||||||
private Z80 _cpu = null!;
|
private Z80 _cpu = null!;
|
||||||
private MemoryBus _memoryBus = null!;
|
private MemoryBus _memoryBus = null!;
|
||||||
|
private SimpleIoBus _simpleIoBus = null!;
|
||||||
|
|
||||||
public Form1()
|
public Form1()
|
||||||
{
|
{
|
||||||
@@ -22,16 +24,16 @@ namespace Desktop
|
|||||||
{
|
{
|
||||||
// 1. Initialize the memory bus
|
// 1. Initialize the memory bus
|
||||||
_memoryBus = new MemoryBus();
|
_memoryBus = new MemoryBus();
|
||||||
|
_simpleIoBus = new SimpleIoBus();
|
||||||
|
|
||||||
// 2. Load the ROM from disk
|
// 2. Load the ROM
|
||||||
// Make sure "48.rom" matches the name of the file you added to your project
|
|
||||||
byte[] romData = RomLoader.Load("48.rom");
|
byte[] romData = RomLoader.Load("48.rom");
|
||||||
|
|
||||||
// 3. Inject the ROM into the memory bus
|
// 3. Inject the ROM into the memory bus
|
||||||
_memoryBus.LoadRom(romData);
|
_memoryBus.LoadRom(romData);
|
||||||
|
|
||||||
// 4. Initialize the CPU with the populated memory
|
// 4. Initialize the CPU with the populated memory
|
||||||
_cpu = new Z80(_memoryBus);
|
_cpu = new Z80(_memoryBus, _simpleIoBus);
|
||||||
|
|
||||||
DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus);
|
DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus);
|
||||||
debugger.Show();
|
debugger.Show();
|
||||||
|
|||||||
Reference in New Issue
Block a user