Implemented a load more Z80 OpCodes. Added a breakpoint to the debugger
This commit is contained in:
@@ -160,6 +160,37 @@ namespace Core.Cpu
|
|||||||
if (result < 0) AF.Low |= 0x01;
|
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)
|
private void Cp(byte value)
|
||||||
{
|
{
|
||||||
byte a = AF.High;
|
byte a = AF.High;
|
||||||
@@ -263,6 +294,16 @@ namespace Core.Cpu
|
|||||||
case 0x23: // INC HL
|
case 0x23: // INC HL
|
||||||
HL.Word++;
|
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
|
case 0x2B: // DEC HL
|
||||||
HL.Word--;
|
HL.Word--;
|
||||||
return 6;
|
return 6;
|
||||||
@@ -276,6 +317,17 @@ namespace Core.Cpu
|
|||||||
return 12; // Jump taken
|
return 12; // Jump taken
|
||||||
}
|
}
|
||||||
return 7; // Jump not 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
|
case 0x36: // LD (HL), n
|
||||||
byte nValue = FetchByte();
|
byte nValue = FetchByte();
|
||||||
_memory.Write(HL.Word, nValue);
|
_memory.Write(HL.Word, nValue);
|
||||||
@@ -310,6 +362,20 @@ namespace Core.Cpu
|
|||||||
_ioBus.Write(portAddress, AF.High);
|
_ioBus.Write(portAddress, AF.High);
|
||||||
|
|
||||||
return 11; // Takes 11 T-States
|
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
|
case 0xDE: // SBC A, n
|
||||||
Sbc(FetchByte());
|
Sbc(FetchByte());
|
||||||
return 7;
|
return 7;
|
||||||
|
|||||||
40
Desktop/DebuggerForm.Designer.cs
generated
40
Desktop/DebuggerForm.Designer.cs
generated
@@ -44,6 +44,10 @@
|
|||||||
lstDisassembly = new ListBox();
|
lstDisassembly = new ListBox();
|
||||||
lstStack = new ListBox();
|
lstStack = new ListBox();
|
||||||
btnExit = new Button();
|
btnExit = new Button();
|
||||||
|
saveFileDialog1 = new SaveFileDialog();
|
||||||
|
label1 = new Label();
|
||||||
|
txtBreakpoint = new TextBox();
|
||||||
|
label2 = new Label();
|
||||||
SuspendLayout();
|
SuspendLayout();
|
||||||
//
|
//
|
||||||
// lblAF
|
// lblAF
|
||||||
@@ -128,7 +132,7 @@
|
|||||||
//
|
//
|
||||||
// txtMemoryStart
|
// txtMemoryStart
|
||||||
//
|
//
|
||||||
txtMemoryStart.Location = new Point(238, 10);
|
txtMemoryStart.Location = new Point(300, 21);
|
||||||
txtMemoryStart.Margin = new Padding(2);
|
txtMemoryStart.Margin = new Padding(2);
|
||||||
txtMemoryStart.Name = "txtMemoryStart";
|
txtMemoryStart.Name = "txtMemoryStart";
|
||||||
txtMemoryStart.Size = new Size(121, 27);
|
txtMemoryStart.Size = new Size(121, 27);
|
||||||
@@ -161,7 +165,7 @@
|
|||||||
//
|
//
|
||||||
// btnRefreshMemory
|
// btnRefreshMemory
|
||||||
//
|
//
|
||||||
btnRefreshMemory.Location = new Point(376, 7);
|
btnRefreshMemory.Location = new Point(425, 21);
|
||||||
btnRefreshMemory.Margin = new Padding(2);
|
btnRefreshMemory.Margin = new Padding(2);
|
||||||
btnRefreshMemory.Name = "btnRefreshMemory";
|
btnRefreshMemory.Name = "btnRefreshMemory";
|
||||||
btnRefreshMemory.Size = new Size(90, 27);
|
btnRefreshMemory.Size = new Size(90, 27);
|
||||||
@@ -208,11 +212,39 @@
|
|||||||
btnExit.UseVisualStyleBackColor = true;
|
btnExit.UseVisualStyleBackColor = true;
|
||||||
btnExit.Click += btnExit_Click;
|
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
|
// DebuggerForm
|
||||||
//
|
//
|
||||||
AutoScaleDimensions = new SizeF(8F, 20F);
|
AutoScaleDimensions = new SizeF(8F, 20F);
|
||||||
AutoScaleMode = AutoScaleMode.Font;
|
AutoScaleMode = AutoScaleMode.Font;
|
||||||
ClientSize = new Size(928, 350);
|
ClientSize = new Size(928, 350);
|
||||||
|
Controls.Add(label2);
|
||||||
|
Controls.Add(txtBreakpoint);
|
||||||
|
Controls.Add(label1);
|
||||||
Controls.Add(btnExit);
|
Controls.Add(btnExit);
|
||||||
Controls.Add(lstStack);
|
Controls.Add(lstStack);
|
||||||
Controls.Add(lstDisassembly);
|
Controls.Add(lstDisassembly);
|
||||||
@@ -254,6 +286,10 @@
|
|||||||
private ListBox lstStack;
|
private ListBox lstStack;
|
||||||
private Button btnExit;
|
private Button btnExit;
|
||||||
public ListBox lstDisassembly;
|
public ListBox lstDisassembly;
|
||||||
|
private SaveFileDialog saveFileDialog1;
|
||||||
|
private Label label1;
|
||||||
|
private TextBox txtBreakpoint;
|
||||||
|
private Label label2;
|
||||||
//private TextBox textBox4;
|
//private TextBox textBox4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ namespace Desktop
|
|||||||
private readonly Z80 _cpu;
|
private readonly Z80 _cpu;
|
||||||
private readonly MemoryBus _memoryBus;
|
private readonly MemoryBus _memoryBus;
|
||||||
private bool _isRunning = false;
|
private bool _isRunning = false;
|
||||||
|
private ushort? _breakpoint = null;
|
||||||
|
|
||||||
public DebuggerForm(Z80 cpu, MemoryBus memoryBus)
|
public DebuggerForm(Z80 cpu, MemoryBus memoryBus)
|
||||||
{
|
{
|
||||||
@@ -57,18 +58,45 @@ namespace Desktop
|
|||||||
return;
|
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
|
// Start the run state
|
||||||
_isRunning = true;
|
_isRunning = true;
|
||||||
btnRun.Text = "Stop";
|
btnRun.Text = "Stop";
|
||||||
|
|
||||||
// Fire up a background thread so the Windows UI doesn't freeze
|
// Fire up a background thread
|
||||||
await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Free-run the CPU until the flag is flipped or it crashes!
|
|
||||||
while (_isRunning)
|
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();
|
_cpu.Step();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,22 +104,17 @@ namespace Desktop
|
|||||||
{
|
{
|
||||||
_isRunning = false;
|
_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
|
this.Invoke((MethodInvoker)delegate
|
||||||
{
|
{
|
||||||
btnRun.Text = "Run";
|
|
||||||
UpdateDisplay();
|
|
||||||
MessageBox.Show(ex.Message, "CPU Break", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
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
|
// Whether it stopped because of a breakpoint, a crash, or the user clicking "Stop",
|
||||||
if (!_isRunning)
|
// we MUST update the UI when the background thread finishes!
|
||||||
{
|
btnRun.Text = "Run";
|
||||||
UpdateDisplay();
|
UpdateDisplay();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void btnExit_Click(object sender, EventArgs e)
|
private void btnExit_Click(object sender, EventArgs e)
|
||||||
@@ -186,8 +209,6 @@ 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;
|
||||||
@@ -220,6 +241,12 @@ namespace Desktop
|
|||||||
case 0x23:
|
case 0x23:
|
||||||
mnemonic = "INC HL";
|
mnemonic = "INC HL";
|
||||||
break;
|
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:
|
case 0x2B:
|
||||||
mnemonic = "DEC HL";
|
mnemonic = "DEC HL";
|
||||||
break;
|
break;
|
||||||
@@ -229,6 +256,9 @@ namespace Desktop
|
|||||||
mnemonic = $"JR NC, 0x{dest:X4}";
|
mnemonic = $"JR NC, 0x{dest:X4}";
|
||||||
instructionLength = 2;
|
instructionLength = 2;
|
||||||
break;
|
break;
|
||||||
|
case 0x35:
|
||||||
|
mnemonic = "DEC (HL)";
|
||||||
|
break;
|
||||||
case 0x36:
|
case 0x36:
|
||||||
byte memValue = _memoryBus.Read((ushort)(currentPc + 1));
|
byte memValue = _memoryBus.Read((ushort)(currentPc + 1));
|
||||||
mnemonic = $"LD (HL), 0x{memValue:X2}";
|
mnemonic = $"LD (HL), 0x{memValue:X2}";
|
||||||
@@ -268,6 +298,9 @@ namespace Desktop
|
|||||||
mnemonic = $"OUT (0x{outPort:X2}), A";
|
mnemonic = $"OUT (0x{outPort:X2}), A";
|
||||||
instructionLength = 2;
|
instructionLength = 2;
|
||||||
break;
|
break;
|
||||||
|
case 0xD9:
|
||||||
|
mnemonic = "EXX";
|
||||||
|
break;
|
||||||
case 0xDE:
|
case 0xDE:
|
||||||
byte sbcValue = _memoryBus.Read((ushort)(currentPc + 1));
|
byte sbcValue = _memoryBus.Read((ushort)(currentPc + 1));
|
||||||
mnemonic = $"SBC A, 0x{sbcValue:X2}";
|
mnemonic = $"SBC A, 0x{sbcValue:X2}";
|
||||||
|
|||||||
@@ -117,4 +117,7 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
|
<metadata name="saveFileDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<value>17, 17</value>
|
||||||
|
</metadata>
|
||||||
</root>
|
</root>
|
||||||
Reference in New Issue
Block a user