diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs
index 80c8ade..eb197f9 100644
--- a/Core/Cpu/Z80.cs
+++ b/Core/Cpu/Z80.cs
@@ -160,6 +160,37 @@ namespace Core.Cpu
if (result < 0) AF.Low |= 0x01;
}
+ private byte Dec8(byte value)
+ {
+ byte result = (byte)(value - 1);
+
+ // Store the existing Carry flag so we can preserve it
+ byte carry = (byte)(AF.Low & 0x01);
+
+ // Clear all flags
+ AF.Low = 0;
+
+ // Sign Flag (Bit 7)
+ if ((result & 0x80) != 0) AF.Low |= 0x80;
+
+ // Zero Flag (Bit 6)
+ if (result == 0) AF.Low |= 0x40;
+
+ // Half-Carry Flag (Bit 4) - Set if borrow from bit 4 (happens if the lower nibble was 0)
+ if ((value & 0x0F) == 0) AF.Low |= 0x10;
+
+ // Parity/Overflow Flag (Bit 2) - Set if the original value was 0x80 (maximum negative)
+ if (value == 0x80) AF.Low |= 0x04;
+
+ // Subtract Flag (Bit 1) - ALWAYS SET for decrements
+ AF.Low |= 0x02;
+
+ // Restore the original Carry Flag (Bit 0)
+ AF.Low |= carry;
+
+ return result;
+ }
+
private void Cp(byte value)
{
byte a = AF.High;
@@ -262,7 +293,17 @@ namespace Core.Cpu
return 7;
case 0x23: // INC HL
HL.Word++;
- return 6;
+ return 6;
+ case 0x28: // JR Z, e
+ offset = (sbyte)FetchByte();
+
+ // Check if the Zero Flag (Bit 6) IS set
+ if ((AF.Low & 0x40) != 0)
+ {
+ PC = (ushort)(PC + offset);
+ return 12; // Jump taken
+ }
+ return 7; // Jump not taken
case 0x2B: // DEC HL
HL.Word--;
return 6;
@@ -276,6 +317,17 @@ namespace Core.Cpu
return 12; // Jump taken
}
return 7; // Jump not taken
+ case 0x35: // DEC (HL)
+ // Read the current byte from memory
+ byte memValue = _memory.Read(HL.Word);
+
+ // Decrement it and update flags
+ byte decremented = Dec8(memValue);
+
+ // Write the new value back to memory
+ _memory.Write(HL.Word, decremented);
+
+ return 11; // Takes 11 T-States
case 0x36: // LD (HL), n
byte nValue = FetchByte();
_memory.Write(HL.Word, nValue);
@@ -310,6 +362,20 @@ namespace Core.Cpu
_ioBus.Write(portAddress, AF.High);
return 11; // Takes 11 T-States
+ case 0xD9: // EXX
+ ushort tempBC = BC.Word;
+ BC.Word = BC_Prime.Word;
+ BC_Prime.Word = tempBC;
+
+ ushort tempDE = DE.Word;
+ DE.Word = DE_Prime.Word;
+ DE_Prime.Word = tempDE;
+
+ ushort tempHL = HL.Word;
+ HL.Word = HL_Prime.Word;
+ HL_Prime.Word = tempHL;
+
+ return 4; // Takes 4 T-States
case 0xDE: // SBC A, n
Sbc(FetchByte());
return 7;
diff --git a/Desktop/DebuggerForm.Designer.cs b/Desktop/DebuggerForm.Designer.cs
index db37b5d..689e36f 100644
--- a/Desktop/DebuggerForm.Designer.cs
+++ b/Desktop/DebuggerForm.Designer.cs
@@ -44,6 +44,10 @@
lstDisassembly = new ListBox();
lstStack = new ListBox();
btnExit = new Button();
+ saveFileDialog1 = new SaveFileDialog();
+ label1 = new Label();
+ txtBreakpoint = new TextBox();
+ label2 = new Label();
SuspendLayout();
//
// lblAF
@@ -128,7 +132,7 @@
//
// txtMemoryStart
//
- txtMemoryStart.Location = new Point(238, 10);
+ txtMemoryStart.Location = new Point(300, 21);
txtMemoryStart.Margin = new Padding(2);
txtMemoryStart.Name = "txtMemoryStart";
txtMemoryStart.Size = new Size(121, 27);
@@ -161,7 +165,7 @@
//
// btnRefreshMemory
//
- btnRefreshMemory.Location = new Point(376, 7);
+ btnRefreshMemory.Location = new Point(425, 21);
btnRefreshMemory.Margin = new Padding(2);
btnRefreshMemory.Name = "btnRefreshMemory";
btnRefreshMemory.Size = new Size(90, 27);
@@ -208,11 +212,39 @@
btnExit.UseVisualStyleBackColor = true;
btnExit.Click += btnExit_Click;
//
+ // label1
+ //
+ label1.AutoSize = true;
+ label1.Location = new Point(289, 288);
+ label1.Name = "label1";
+ label1.Size = new Size(81, 20);
+ label1.TabIndex = 19;
+ label1.Text = "Breakpoint";
+ //
+ // txtBreakpoint
+ //
+ txtBreakpoint.Location = new Point(376, 281);
+ 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";
+ //
// DebuggerForm
//
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(928, 350);
+ Controls.Add(label2);
+ Controls.Add(txtBreakpoint);
+ Controls.Add(label1);
Controls.Add(btnExit);
Controls.Add(lstStack);
Controls.Add(lstDisassembly);
@@ -254,6 +286,10 @@
private ListBox lstStack;
private Button btnExit;
public ListBox lstDisassembly;
+ private SaveFileDialog saveFileDialog1;
+ private Label label1;
+ private TextBox txtBreakpoint;
+ private Label label2;
//private TextBox textBox4;
}
}
\ No newline at end of file
diff --git a/Desktop/DebuggerForm.cs b/Desktop/DebuggerForm.cs
index 169b671..4124c19 100644
--- a/Desktop/DebuggerForm.cs
+++ b/Desktop/DebuggerForm.cs
@@ -11,6 +11,7 @@ namespace Desktop
private readonly Z80 _cpu;
private readonly MemoryBus _memoryBus;
private bool _isRunning = false;
+ private ushort? _breakpoint = null;
public DebuggerForm(Z80 cpu, MemoryBus memoryBus)
{
@@ -57,18 +58,45 @@ namespace Desktop
return;
}
+ // --- NEW: Parse the Breakpoint ---
+ if (!string.IsNullOrWhiteSpace(txtBreakpoint.Text))
+ {
+ if (ushort.TryParse(txtBreakpoint.Text, System.Globalization.NumberStyles.HexNumber, null, out ushort parsedBp))
+ {
+ _breakpoint = parsedBp;
+ }
+ else
+ {
+ MessageBox.Show("Invalid hex address in breakpoint!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
+ return;
+ }
+ }
+ else
+ {
+ _breakpoint = null; // No text means no breakpoint
+ }
+ // ---------------------------------
+
// Start the run state
_isRunning = true;
btnRun.Text = "Stop";
- // Fire up a background thread so the Windows UI doesn't freeze
+ // Fire up a background thread
await Task.Run(() =>
{
try
{
- // Free-run the CPU until the flag is flipped or it crashes!
while (_isRunning)
{
+ // --- NEW: Breakpoint Check ---
+ // We check BEFORE stepping so it stops exactly on the instruction
+ if (_breakpoint.HasValue && _cpu.PC == _breakpoint.Value)
+ {
+ _isRunning = false;
+ break; // Cleanly exit the while loop
+ }
+ // -----------------------------
+
_cpu.Step();
}
}
@@ -76,22 +104,17 @@ namespace Desktop
{
_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();
- }
+ // Whether it stopped because of a breakpoint, a crash, or the user clicking "Stop",
+ // we MUST update the UI when the background thread finishes!
+ btnRun.Text = "Run";
+ UpdateDisplay();
}
private void btnExit_Click(object sender, EventArgs e)
@@ -186,8 +209,6 @@ 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;
@@ -220,6 +241,12 @@ namespace Desktop
case 0x23:
mnemonic = "INC HL";
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 0x2B:
mnemonic = "DEC HL";
break;
@@ -229,6 +256,9 @@ namespace Desktop
mnemonic = $"JR NC, 0x{dest:X4}";
instructionLength = 2;
break;
+ case 0x35:
+ mnemonic = "DEC (HL)";
+ break;
case 0x36:
byte memValue = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"LD (HL), 0x{memValue:X2}";
@@ -268,6 +298,9 @@ namespace Desktop
mnemonic = $"OUT (0x{outPort:X2}), A";
instructionLength = 2;
break;
+ case 0xD9:
+ mnemonic = "EXX";
+ break;
case 0xDE:
byte sbcValue = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"SBC A, 0x{sbcValue:X2}";
diff --git a/Desktop/DebuggerForm.resx b/Desktop/DebuggerForm.resx
index 8b2ff64..5261a14 100644
--- a/Desktop/DebuggerForm.resx
+++ b/Desktop/DebuggerForm.resx
@@ -117,4 +117,7 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ 17, 17
+
\ No newline at end of file