Implemented many more OpCodes

This commit is contained in:
2026-04-13 21:19:05 +01:00
parent c642f7a6c6
commit 2b40960496
4 changed files with 222 additions and 52 deletions

View File

@@ -50,13 +50,33 @@ namespace Core.Cpu
public void Reset()
{
PC = 0x0000;
// The Z80 initializes SP to 0xFFFF on boot
SP = 0xFFFF;
SP = 0xFFFF; // The Z80 initializes SP to 0xFFFF on boot
// Main Registers
AF.Word = 0;
BC.Word = 0;
DE.Word = 0;
HL.Word = 0;
// Alternate Registers
AF_Prime.Word = 0;
BC_Prime.Word = 0;
DE_Prime.Word = 0;
HL_Prime.Word = 0;
// Index Registers
IX.Word = 0;
IY.Word = 0;
// Internal Registers
I = 0;
R = 0;
// Hardware State
IFF1 = false;
IFF2 = false;
InterruptMode = 0;
TotalTStates = 0; // Reset the system clock!
}
public int Step()
@@ -96,6 +116,36 @@ namespace Core.Cpu
$"C:{f & 1}";
}
private void Sub(byte value)
{
byte a = AF.High;
int result = a - value;
// Save the result back to 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)) < 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 CP/SUB
AF.Low |= 0x02;
// Carry Flag (Bit 0) - Set if the overall result dropped below 0
if (result < 0) AF.Low |= 0x01;
}
private void Sbc(byte value)
{
byte a = AF.High;
@@ -309,6 +359,17 @@ namespace Core.Cpu
_memory.Write(SP, (byte)(value & 0xFF));
}
private ushort Pop()
{
// The Z80 is Little-Endian. Low byte comes off the stack first.
byte low = _memory.Read(SP++);
// High byte comes off second.
byte high = _memory.Read(SP++);
return (ushort)((high << 8) | low);
}
private int ExecuteOpcode(byte opcode)
{
sbyte offset = 0;
@@ -340,6 +401,9 @@ namespace Core.Cpu
case 0x11: //LD DE, nn
DE.Word = FetchWord();
return 10;
case 0x16: // LD D, n
DE.High = FetchByte();
return 7;
case 0x19: // ADD HL, DE
Add16(DE.Word);
return 11;
@@ -429,9 +493,15 @@ namespace Core.Cpu
case 0x77: // LD (HL), A
_memory.Write(HL.Word, AF.High);
return 7;
case 0x91: // SUB C
Sub(BC.Low);
return 4; // Takes 4 T-States
case 0xA7: // AND A
And(AF.High);
return 4;
case 0x5F: // LD E, A
DE.Low = AF.High;
return 4;
case 0xAF: // XOR A
AF.High = 0;
AF.Low = 0x44;
@@ -442,6 +512,9 @@ namespace Core.Cpu
case 0xC3:
PC = FetchWord();
return 10;
case 0xC9: // RET
PC = Pop();
return 10;
case 0xCD: // CALL nn
ushort callAddress = FetchWord();
Push(PC);
@@ -604,6 +677,24 @@ namespace Core.Cpu
byte decVal = Dec8(memVal);
_memory.Write(targetAddress, decVal);
return 23;
case 0x36: // LD (IY+d), n
{
sbyte offset36 = (sbyte)FetchByte();
byte nValue = FetchByte();
targetAddress = (ushort)(IY.Word + offset36);
_memory.Write(targetAddress, nValue);
return 19; // Takes 19 T-States
}
case 0x71: // LD (IY+d), C
{
sbyte offset71 = (sbyte)FetchByte();
targetAddress = (ushort)(IY.Word + offset71);
// Write the C register (low byte of BC) to memory
_memory.Write(targetAddress, BC.Low);
return 19; // Takes 19 T-States
}
case 0x75: // LD (IY+d), L
sbyte offset75 = (sbyte)FetchByte();
targetAddress = (ushort)(IY.Word + offset75);

View File

@@ -42,5 +42,10 @@ namespace Core.Memory
// Copy the ROM
Array.Copy(romData, 0, _memory, 0, romData.Length);
}
public void ClearRam()
{
//To Do
}
}
}

View File

@@ -53,94 +53,95 @@
lblIff1 = new Label();
lblIff2 = new Label();
lblIE = new Label();
btnReset = new Button();
SuspendLayout();
//
// lblAF
//
lblAF.AutoSize = true;
lblAF.Location = new Point(10, 7);
lblAF.Location = new Point(12, 9);
lblAF.Margin = new Padding(2, 0, 2, 0);
lblAF.Name = "lblAF";
lblAF.Size = new Size(26, 20);
lblAF.Size = new Size(33, 25);
lblAF.TabIndex = 0;
lblAF.Text = "AF";
//
// lblBC
//
lblBC.AutoSize = true;
lblBC.Location = new Point(9, 48);
lblBC.Location = new Point(11, 60);
lblBC.Margin = new Padding(2, 0, 2, 0);
lblBC.Name = "lblBC";
lblBC.Size = new Size(27, 20);
lblBC.Size = new Size(33, 25);
lblBC.TabIndex = 1;
lblBC.Text = "BC";
//
// lblDE
//
lblDE.AutoSize = true;
lblDE.Location = new Point(10, 100);
lblDE.Location = new Point(12, 125);
lblDE.Margin = new Padding(2, 0, 2, 0);
lblDE.Name = "lblDE";
lblDE.Size = new Size(28, 20);
lblDE.Size = new Size(34, 25);
lblDE.TabIndex = 2;
lblDE.Text = "DE";
//
// lblHL
//
lblHL.AutoSize = true;
lblHL.Location = new Point(10, 150);
lblHL.Location = new Point(12, 188);
lblHL.Margin = new Padding(2, 0, 2, 0);
lblHL.Name = "lblHL";
lblHL.Size = new Size(27, 20);
lblHL.Size = new Size(33, 25);
lblHL.TabIndex = 3;
lblHL.Text = "HL";
//
// lblPC
//
lblPC.AutoSize = true;
lblPC.Location = new Point(9, 200);
lblPC.Location = new Point(11, 250);
lblPC.Margin = new Padding(2, 0, 2, 0);
lblPC.Name = "lblPC";
lblPC.Size = new Size(26, 20);
lblPC.Size = new Size(33, 25);
lblPC.TabIndex = 4;
lblPC.Text = "PC";
//
// lblSP
//
lblSP.AutoSize = true;
lblSP.Location = new Point(9, 252);
lblSP.Location = new Point(11, 315);
lblSP.Margin = new Padding(2, 0, 2, 0);
lblSP.Name = "lblSP";
lblSP.Size = new Size(25, 20);
lblSP.Size = new Size(32, 25);
lblSP.TabIndex = 6;
lblSP.Text = "SP";
//
// lblFlags
//
lblFlags.AutoSize = true;
lblFlags.Location = new Point(88, 52);
lblFlags.Location = new Point(110, 65);
lblFlags.Margin = new Padding(2, 0, 2, 0);
lblFlags.Name = "lblFlags";
lblFlags.Size = new Size(43, 20);
lblFlags.Size = new Size(53, 25);
lblFlags.TabIndex = 7;
lblFlags.Text = "Flags";
//
// lblTStates
//
lblTStates.AutoSize = true;
lblTStates.Location = new Point(88, 7);
lblTStates.Location = new Point(110, 9);
lblTStates.Margin = new Padding(2, 0, 2, 0);
lblTStates.Name = "lblTStates";
lblTStates.Size = new Size(63, 20);
lblTStates.Size = new Size(75, 25);
lblTStates.TabIndex = 8;
lblTStates.Text = "T-States";
//
// txtMemoryStart
//
txtMemoryStart.Location = new Point(300, 21);
txtMemoryStart.Location = new Point(375, 26);
txtMemoryStart.Margin = new Padding(2);
txtMemoryStart.Name = "txtMemoryStart";
txtMemoryStart.Size = new Size(121, 27);
txtMemoryStart.Size = new Size(150, 31);
txtMemoryStart.TabIndex = 9;
txtMemoryStart.Text = "Memory Start";
txtMemoryStart.TextAlign = HorizontalAlignment.Center;
@@ -148,10 +149,10 @@
//
// btnStep
//
btnStep.Location = new Point(6, 418);
btnStep.Location = new Point(8, 522);
btnStep.Margin = new Padding(2);
btnStep.Name = "btnStep";
btnStep.Size = new Size(90, 27);
btnStep.Size = new Size(112, 34);
btnStep.TabIndex = 12;
btnStep.Text = "Step";
btnStep.UseVisualStyleBackColor = true;
@@ -159,10 +160,10 @@
//
// btnRun
//
btnRun.Location = new Point(119, 418);
btnRun.Location = new Point(149, 522);
btnRun.Margin = new Padding(2);
btnRun.Name = "btnRun";
btnRun.Size = new Size(90, 27);
btnRun.Size = new Size(112, 34);
btnRun.TabIndex = 13;
btnRun.Text = "Run";
btnRun.UseVisualStyleBackColor = true;
@@ -170,10 +171,10 @@
//
// btnRefreshMemory
//
btnRefreshMemory.Location = new Point(425, 21);
btnRefreshMemory.Location = new Point(531, 26);
btnRefreshMemory.Margin = new Padding(2);
btnRefreshMemory.Name = "btnRefreshMemory";
btnRefreshMemory.Size = new Size(90, 27);
btnRefreshMemory.Size = new Size(112, 34);
btnRefreshMemory.TabIndex = 14;
btnRefreshMemory.Text = "Refresh Memory";
btnRefreshMemory.UseVisualStyleBackColor = true;
@@ -181,37 +182,39 @@
//
// txtMemoryView
//
txtMemoryView.Location = new Point(88, 80);
txtMemoryView.Location = new Point(110, 100);
txtMemoryView.Margin = new Padding(2);
txtMemoryView.Name = "txtMemoryView";
txtMemoryView.Size = new Size(416, 195);
txtMemoryView.Size = new Size(519, 243);
txtMemoryView.TabIndex = 15;
txtMemoryView.Text = "Memory View Window";
//
// lstDisassembly
//
lstDisassembly.FormattingEnabled = true;
lstDisassembly.Location = new Point(523, 11);
lstDisassembly.ItemHeight = 25;
lstDisassembly.Location = new Point(654, 14);
lstDisassembly.Margin = new Padding(2);
lstDisassembly.Name = "lstDisassembly";
lstDisassembly.Size = new Size(252, 264);
lstDisassembly.Size = new Size(314, 329);
lstDisassembly.TabIndex = 16;
//
// lstStack
//
lstStack.FormattingEnabled = true;
lstStack.Location = new Point(779, 11);
lstStack.ItemHeight = 25;
lstStack.Location = new Point(974, 14);
lstStack.Margin = new Padding(2);
lstStack.Name = "lstStack";
lstStack.Size = new Size(130, 264);
lstStack.Size = new Size(162, 329);
lstStack.TabIndex = 17;
//
// btnExit
//
btnExit.Location = new Point(815, 416);
btnExit.Location = new Point(1019, 520);
btnExit.Margin = new Padding(2);
btnExit.Name = "btnExit";
btnExit.Size = new Size(90, 27);
btnExit.Size = new Size(112, 34);
btnExit.TabIndex = 18;
btnExit.Text = "Full Exit";
btnExit.UseVisualStyleBackColor = true;
@@ -220,80 +223,97 @@
// label1
//
label1.AutoSize = true;
label1.Location = new Point(200, 278);
label1.Location = new Point(250, 348);
label1.Margin = new Padding(4, 0, 4, 0);
label1.Name = "label1";
label1.Size = new Size(81, 20);
label1.Size = new Size(97, 25);
label1.TabIndex = 19;
label1.Text = "Breakpoint";
//
// txtBreakpoint
//
txtBreakpoint.Location = new Point(376, 281);
txtBreakpoint.Location = new Point(470, 351);
txtBreakpoint.Margin = new Padding(4);
txtBreakpoint.Name = "txtBreakpoint";
txtBreakpoint.Size = new Size(125, 27);
txtBreakpoint.Size = new Size(155, 31);
txtBreakpoint.TabIndex = 20;
//
// label2
//
label2.AutoSize = true;
label2.Location = new Point(233, 21);
label2.Location = new Point(291, 26);
label2.Margin = new Padding(4, 0, 4, 0);
label2.Name = "label2";
label2.Size = new Size(62, 20);
label2.Size = new Size(77, 25);
label2.TabIndex = 21;
label2.Text = "Address";
//
// lblIX
//
lblIX.AutoSize = true;
lblIX.Location = new Point(9, 298);
lblIX.Location = new Point(11, 372);
lblIX.Margin = new Padding(2, 0, 2, 0);
lblIX.Name = "lblIX";
lblIX.Size = new Size(22, 20);
lblIX.Size = new Size(28, 25);
lblIX.TabIndex = 22;
lblIX.Text = "IX";
//
// lblIY
//
lblIY.AutoSize = true;
lblIY.Location = new Point(10, 344);
lblIY.Location = new Point(12, 430);
lblIY.Margin = new Padding(2, 0, 2, 0);
lblIY.Name = "lblIY";
lblIY.Size = new Size(21, 20);
lblIY.Size = new Size(27, 25);
lblIY.TabIndex = 23;
lblIY.Text = "IY";
//
// lblIff1
//
lblIff1.AutoSize = true;
lblIff1.Location = new Point(88, 298);
lblIff1.Location = new Point(110, 372);
lblIff1.Margin = new Padding(4, 0, 4, 0);
lblIff1.Name = "lblIff1";
lblIff1.Size = new Size(35, 20);
lblIff1.Size = new Size(45, 25);
lblIff1.TabIndex = 24;
lblIff1.Text = "IFF1";
//
// lblIff2
//
lblIff2.AutoSize = true;
lblIff2.Location = new Point(88, 345);
lblIff2.Location = new Point(110, 431);
lblIff2.Margin = new Padding(4, 0, 4, 0);
lblIff2.Name = "lblIff2";
lblIff2.Size = new Size(35, 20);
lblIff2.Size = new Size(45, 25);
lblIff2.TabIndex = 25;
lblIff2.Text = "IFF2";
//
// lblIE
//
lblIE.AutoSize = true;
lblIE.Location = new Point(200, 345);
lblIE.Location = new Point(250, 431);
lblIE.Margin = new Padding(4, 0, 4, 0);
lblIE.Name = "lblIE";
lblIE.Size = new Size(109, 20);
lblIE.Size = new Size(133, 25);
lblIE.TabIndex = 26;
lblIE.Text = "Interrupt Mode";
//
// btnReset
//
btnReset.Location = new Point(883, 520);
btnReset.Name = "btnReset";
btnReset.Size = new Size(112, 34);
btnReset.TabIndex = 27;
btnReset.Text = "Hard Reset";
btnReset.UseVisualStyleBackColor = true;
btnReset.Click += btnReset_Click;
//
// DebuggerForm
//
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleDimensions = new SizeF(10F, 25F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(928, 454);
ClientSize = new Size(1160, 568);
Controls.Add(btnReset);
Controls.Add(lblIE);
Controls.Add(lblIff2);
Controls.Add(lblIff1);
@@ -352,6 +372,7 @@
private Label lblIff1;
private Label lblIff2;
private Label lblIE;
private Button btnReset;
//private TextBox textBox4;
}
}

View File

@@ -117,6 +117,30 @@ namespace Desktop
UpdateDisplay();
}
private void btnReset_Click(object sender, EventArgs e)
{
// 1. Safely stop the emulator if it is currently in a Run loop
if (_isRunning)
{
_isRunning = false;
btnRun.Text = "Run";
}
// 2. Power cycle the CPU
_cpu.Reset();
// Note: A true hard reset also wipes the RAM.
// If you add a RAM clear method to your MemoryBus later, call it here!
_memoryBus.ClearRam(); //To Do
// 3. Clear the UI tracking lists
lstDisassembly.Items.Clear();
lstStack.Items.Clear();
// 4. Force a full UI refresh to show the clean slate
UpdateDisplay();
}
private void btnExit_Click(object sender, EventArgs e)
{
Environment.Exit(0);
@@ -253,6 +277,11 @@ namespace Desktop
mnemonic = $"LD DE, 0x{deHigh:X2}{deLow:X2}";
instructionLength = 3;
break;
case 0x16:
byte dImm = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"LD D, 0x{dImm:X2}";
instructionLength = 2;
break;
case 0x19:
mnemonic = "ADD HL, DE";
break;
@@ -321,6 +350,9 @@ namespace Desktop
case 0x47:
mnemonic = "LD B, A";
break;
case 0x5F:
mnemonic = "LD E, A";
break;
case 0x62:
mnemonic = "LD H, D";
break;
@@ -330,6 +362,9 @@ namespace Desktop
case 0x77:
mnemonic = "LD (HL), A";
break;
case 0x91:
mnemonic = "SUB C";
break;
case 0xA7:
mnemonic = "AND A";
break;
@@ -346,6 +381,9 @@ namespace Desktop
mnemonic = $"JP 0x{jpHigh:X2}{jpLow:X2}";
instructionLength = 3;
break;
case 0xC9:
mnemonic = "RET";
break;
case 0xCD:
ushort callDest = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
mnemonic = $"CALL 0x{callDest:X4}";
@@ -435,6 +473,14 @@ namespace Desktop
mnemonic = $"DEC (IY{sign}{d})";
instructionLength = 3;
}
else if (fdOpcode == 0x36) // LD (IY+d), n
{
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
byte n = _memoryBus.Read((ushort)(currentPc + 3));
string sign = d >= 0 ? "+" : "";
mnemonic = $"LD (IY{sign}{d}), 0x{n:X2}";
instructionLength = 4;
}
else if (fdOpcode == 0xCB) // FD CB prefix
{
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
@@ -454,6 +500,13 @@ namespace Desktop
}
instructionLength = 4;
}
else if (fdOpcode == 0x71) // LD (IY+d), C
{
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
string sign = d >= 0 ? "+" : "";
mnemonic = $"LD (IY{sign}{d}), C";
instructionLength = 3;
}
else if (fdOpcode == 0x75) // LD (IY+d), L
{
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));