From 695db41f6048870a0f1d8a88e934e4b3796c5fe1 Mon Sep 17 00:00:00 2001 From: Marc Parsons Date: Tue, 14 Apr 2026 09:56:26 +0100 Subject: [PATCH] Implemented a load more Z80 OpCodes --- Core/Cpu/Z80.cs | 86 ++++++++++++++++++++++++++++++++++++++--- Desktop/DebuggerForm.cs | 43 ++++++++++++++++++++- 2 files changed, 121 insertions(+), 8 deletions(-) diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs index 247542e..38a95c5 100644 --- a/Core/Cpu/Z80.cs +++ b/Core/Cpu/Z80.cs @@ -339,6 +339,24 @@ namespace Core.Cpu if (HasEvenParity(AF.High)) AF.Low |= 0x04; } + private void Xor(byte value) + { + // The caret (^) is the C# Bitwise XOR operator + AF.High = (byte)(AF.High ^ value); + + // --- Update Flags --- + AF.Low = 0; // Clear all flags (H, N, and C are always 0 for XOR) + + // Sign Flag (Bit 7) - Set if the highest bit is 1 + if ((AF.High & 0x80) != 0) AF.Low |= 0x80; + + // Zero Flag (Bit 6) - Set if the result is 0 + if (AF.High == 0) AF.Low |= 0x40; + + // Parity Flag (Bit 2) - Set if the result has an even number of 1 bits + if (HasEvenParity(AF.High)) AF.Low |= 0x04; + } + private void Add16(ushort value) { int hl = HL.Word; @@ -539,16 +557,35 @@ namespace Core.Cpu case 0x37: // SCF AF.Low |= 0x01; // Force Carry Flag (Bit 0) to 1 AF.Low &= 0xED; - return 4; // Takes 4 T-States + return 4; + case 0x38: // JR C, d + sbyte jrCOffset = (sbyte)FetchByte(); + // Check if the Carry Flag (Bit 0) IS set (1) + if ((AF.Low & 0x01) != 0) + { + PC = (ushort)(PC + jrCOffset); + return 12; + } + return 7; case 0x3E: //LD A, n AF.High = FetchByte(); - return 7; + return 7; + case 0x3F: // CCF + bool previousCarry = (AF.Low & 0x01) != 0; + AF.Low ^= 0x01; // Invert Carry Flag (Bit 0) + AF.Low &= 0xFD; // Force Subtract Flag (N, Bit 1) to 0 + // Set Half-Carry (H, Bit 4) to the previous Carry state + if (previousCarry) + AF.Low |= 0x10; + else + AF.Low &= 0xEF; + return 4; case 0x47: // LD B, A BC.High = AF.High; return 4; case 0x4E: // LD C, (HL) BC.Low = _memory.Read(HL.Word); - return 7; // Takes 7 T-States + return 7; case 0x56: // LD D, (HL) DE.High = _memory.Read(HL.Word); return 7; // Takes 7 T-States @@ -558,9 +595,18 @@ namespace Core.Cpu case 0x62: // LD H, D HL.High = DE.High; return 4; + case 0x67: // LD H, A + HL.High = AF.High; + return 4; case 0x6B: // LD L, E HL.Low = DE.Low; return 4; + case 0x72: // LD (HL), D + _memory.Write(HL.Word, DE.High); + return 7; + case 0x73: // LD (HL), E + _memory.Write(HL.Word, DE.Low); + return 7; case 0x77: // LD (HL), A _memory.Write(HL.Word, AF.High); return 7; @@ -585,6 +631,9 @@ namespace Core.Cpu case 0x6F: // LD L, A HL.Low = AF.High; return 4; + case 0xAE: // XOR (HL) + Xor(_memory.Read(HL.Word)); + return 7; case 0xAF: // XOR A AF.High = 0; AF.Low = 0x44; @@ -654,6 +703,9 @@ namespace Core.Cpu case 0xDE: // SBC A, n Sbc(FetchByte()); return 7; + case 0xE6: // AND n + And(FetchByte()); + return 7; case 0xE9: // JP (HL) PC = HL.Word; return 4; // Takes 4 T-States @@ -797,6 +849,12 @@ namespace Core.Cpu _memory.Write(targetAddress, nValue); return 19; // Takes 19 T-States } + case 0x6E: // LD L, (IY+d) + sbyte displacementVal = (sbyte)FetchByte(); + ushort targetAddr = (ushort)(IY.Word + displacementVal); + + HL.Low = _memory.Read(targetAddr); + return 19; case 0x71: // LD (IY+d), C { sbyte offset71 = (sbyte)FetchByte(); @@ -820,6 +878,22 @@ namespace Core.Cpu switch (bitOpcode) { + case 0x46: // BIT 0, (IY+d) + byte memValBit0 = _memory.Read(targetAddress); + + // Preserve the existing Carry Flag (Bit 0) + byte newFlags = (byte)(AF.Low & 0x01); + + newFlags |= 0x10; // Force Half-Carry (Bit 4) to 1 + + // Test Bit 0. If it is 0, turn ON the Zero Flag (Bit 6) + if ((memValBit0 & 0x01) == 0) + { + newFlags |= 0x40; + } + + AF.Low = newFlags; + return 20; case 0x4E: // BIT 1, (IY+d) { byte memValBit = _memory.Read(targetAddress); @@ -839,7 +913,7 @@ namespace Core.Cpu AF.Low |= 0x04; // Set P/V Flag (Bit 2) } - return 20; // Takes 20 T-States + return 20; } case 0x86: // RES 0, (IY+d) byte memValRes0 = _memory.Read(targetAddress); @@ -901,11 +975,11 @@ namespace Core.Cpu return 23; default: - throw new NotImplementedException($"FD CB opcode {bitOpcode:X2} not implemented!"); + throw new NotImplementedException($"FD CB opcode {bitOpcode:X2} at PC 0x{(PC - 1):X4} not implemented!"); } } default: - throw new NotImplementedException($"FD prefix opcode {opcode:X2} not implemented!"); + throw new NotImplementedException($"FD prefix opcode {opcode:X2} at PC 0x{(PC - 2):X4} not implemented!"); } } } diff --git a/Desktop/DebuggerForm.cs b/Desktop/DebuggerForm.cs index 1154405..49ddd6e 100644 --- a/Desktop/DebuggerForm.cs +++ b/Desktop/DebuggerForm.cs @@ -362,10 +362,20 @@ namespace Desktop case 0x37: mnemonic = "SCF"; break; + case 0x38: + sbyte dC = (sbyte)_memoryBus.Read((ushort)(currentPc + 1)); + ushort targetC = (ushort)(currentPc + 2 + dC); + + mnemonic = $"JR C, 0x{targetC:X4}"; + instructionLength = 2; + break; case 0x3E: mnemonic = $"LD A, 0x{_memoryBus.Read((ushort)(currentPc + 1)):X2}"; instructionLength = 2; break; + case 0x3F: + mnemonic = "CCF"; + break; case 0x47: mnemonic = "LD B, A"; break; @@ -384,12 +394,21 @@ namespace Desktop case 0x62: mnemonic = "LD H, D"; break; + case 0x67: + mnemonic = "LD H, A"; + break; case 0x6B: mnemonic = "LD L, E"; break; case 0x6F: mnemonic = "LD L, A"; break; + case 0x72: + mnemonic = "LD (HL), D"; + break; + case 0x73: + mnemonic = "LD (HL), E"; + break; case 0x77: mnemonic = "LD (HL), A"; break; @@ -408,6 +427,9 @@ namespace Desktop case 0xA7: mnemonic = "AND A"; break; + case 0xAE: + mnemonic = "XOR (HL)"; + break; case 0xAF: mnemonic = "XOR A"; break; @@ -461,6 +483,11 @@ namespace Desktop mnemonic = $"SBC A, 0x{sbcValue:X2}"; instructionLength = 2; break; + case 0xE6: + byte andImm = _memoryBus.Read((ushort)(currentPc + 1)); + mnemonic = $"AND 0x{andImm:X2}"; + instructionLength = 2; + break; case 0xE9: mnemonic = "JP (HL)"; break; @@ -543,12 +570,24 @@ namespace Desktop mnemonic = $"LD (IY{sign}{d}), 0x{n:X2}"; instructionLength = 4; } + else if (fdOpcode == 0x6E) + { + sbyte offsetL = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); + string signL = offsetL >= 0 ? "+" : ""; + + mnemonic = $"LD L, (IY{signL}{offsetL})"; + instructionLength = 3; + } else if (fdOpcode == 0xCB) // FD CB prefix { sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); byte cbOpcode = _memoryBus.Read((ushort)(currentPc + 3)); string sign = d >= 0 ? "+" : ""; - if (cbOpcode == 0x4E) + if (cbOpcode == 0x46) + { + mnemonic = $"BIT 0, (IY{sign}{d})"; + } + else if (cbOpcode == 0x4E) { mnemonic = $"BIT 1, (IY{sign}{d})"; } @@ -608,7 +647,7 @@ namespace Desktop else if (cbOpcode == 0xE6) { mnemonic = $"SET 4, (IY{sign}{d})"; - } + } else { mnemonic = $"FD CB {d:X2} {cbOpcode:X2}"; // Fallback