From 9496c260047702a2d74d1fa17da7da1228910225 Mon Sep 17 00:00:00 2001 From: Marc Parsons Date: Mon, 13 Apr 2026 22:32:05 +0100 Subject: [PATCH] Implemented many more OpCodes - again! --- Core/Cpu/Z80.cs | 124 +++++++++++++++++++++++++++++++++++++++- Desktop/DebuggerForm.cs | 59 +++++++++++++++++++ 2 files changed, 180 insertions(+), 3 deletions(-) diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs index 48c416e..7e57470 100644 --- a/Core/Cpu/Z80.cs +++ b/Core/Cpu/Z80.cs @@ -322,6 +322,23 @@ namespace Core.Cpu // Subtract Flag (N) and Carry Flag (C) are ALWAYS 0 } + private void Or(byte value) + { + AF.High = (byte)(AF.High | value); + + // --- Update Flags --- + AF.Low = 0; // Clear all flags (H, N, and C are always 0 for OR) + + // 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; @@ -338,6 +355,34 @@ namespace Core.Cpu if (result > 0xFFFF) AF.Low |= 0x01; } + private void Add(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 (This also correctly resets the N flag to 0) + + // 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 carry from bit 3 + if (((a & 0x0F) + (value & 0x0F)) > 0x0F) AF.Low |= 0x10; + + // Overflow/Parity Flag (Bit 2) - For addition, overflow happens if two numbers + // with the SAME sign are added and produce a result with a DIFFERENT sign. + if ((((a ^ ~value) & 0x80) != 0) && (((a ^ result) & 0x80) != 0)) AF.Low |= 0x04; + + // Carry Flag (Bit 0) - Set if the result is greater than 255 + if (result > 0xFF) AF.Low |= 0x01; + } + private bool HasEvenParity(byte value) { int bits = 0; @@ -406,7 +451,10 @@ namespace Core.Cpu return 7; case 0x19: // ADD HL, DE Add16(DE.Word); - return 11; + return 11; + case 0x1B: // DEC DE + DE.Word--; + return 6; case 0x20: // JR NZ, e offset = (sbyte)FetchByte(); if ((AF.Low & 0x40) == 0) @@ -426,6 +474,9 @@ namespace Core.Cpu case 0x23: // INC HL HL.Word++; return 6; + case 0x26: // LD H, n + HL.High = FetchByte(); + return 7; case 0x28: // JR Z, e offset = (sbyte)FetchByte(); @@ -477,13 +528,25 @@ namespace Core.Cpu byte nValue = FetchByte(); _memory.Write(HL.Word, nValue); return 10; + case 0x37: // SCF + AF.Low |= 0x01; // Force Carry Flag (Bit 0) to 1 + AF.Low &= 0xED; + return 4; // Takes 4 T-States case 0x3E: //LD A, n AF.High = FetchByte(); return 7; 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 + case 0x56: // LD D, (HL) + DE.High = _memory.Read(HL.Word); + return 7; // Takes 7 T-States + case 0x5E: // LD E, (HL) + DE.Low = _memory.Read(HL.Word); + return 7; // Takes 7 T-States case 0x62: // LD H, D HL.High = DE.High; return 4; @@ -493,25 +556,54 @@ namespace Core.Cpu case 0x77: // LD (HL), A _memory.Write(HL.Word, AF.High); return 7; + case 0x7A: // LD A, D + AF.High = DE.High; + return 4; + case 0x7E: // LD A, (HL) + AF.High = _memory.Read(HL.Word); + return 7; // Takes 7 T-States + case 0x87: // ADD A, A + Add(AF.High); + return 4; case 0x91: // SUB C Sub(BC.Low); - return 4; // Takes 4 T-States + return 4; case 0xA7: // AND A And(AF.High); return 4; case 0x5F: // LD E, A DE.Low = AF.High; return 4; + case 0x6F: // LD L, A + HL.Low = AF.High; + return 4; case 0xAF: // XOR A AF.High = 0; AF.Low = 0x44; return 4; + case 0xB3: // OR E + Or(DE.Low); + return 4; + case 0xB9: // CP C + Cp(BC.Low); + return 4; // Takes 4 T-States case 0xBC: // CP H Cp(HL.High); return 4; case 0xC3: PC = FetchWord(); return 10; + case 0xC6: // ADD A, n + Add(FetchByte()); + return 7; + case 0xC8: // RET Z + // Check if the Zero Flag (Bit 6) IS set + if ((AF.Low & 0x40) != 0) + { + PC = Pop(); + return 11; // Condition met, took the return + } + return 5; // Condition not met, skipped case 0xC9: // RET PC = Pop(); return 10; @@ -520,6 +612,14 @@ namespace Core.Cpu Push(PC); PC = callAddress; return 17; + case 0xD0: // RET NC + // Check if the Carry Flag (Bit 0) is NOT set (0) + if ((AF.Low & 0x01) == 0) + { + PC = Pop(); + return 11; // Condition met, took the return + } + return 5; // Condition not met, skipped case 0xD3: // OUT (n), A byte portOffset = FetchByte(); @@ -730,6 +830,15 @@ namespace Core.Cpu return 20; // Takes 20 T-States } + case 0x86: // RES 0, (IY+d) + byte memValRes0 = _memory.Read(targetAddress); + + // 0xFE is Binary 1111 1110. + // ANDing preserves all bits except Bit 0, which becomes 0. + memValRes0 &= 0xFE; + + _memory.Write(targetAddress, memValRes0); + return 23; // Takes 23 T-States case 0x8E: // RES 1, (IY+d) byte memValRes = _memory.Read(targetAddress); @@ -738,6 +847,15 @@ namespace Core.Cpu memValRes &= 0xFD; _memory.Write(targetAddress, memValRes); return 23; + case 0xA6: // RES 4, (IY+d) + byte memValRes4 = _memory.Read(targetAddress); + + // 0xEF is Binary 1110 1111 + // ANDing preserves all bits except Bit 4, which becomes 0. + memValRes4 &= 0xEF; + + _memory.Write(targetAddress, memValRes4); + return 23; case 0xCE: // SET 1, (IY+d) memVal = _memory.Read(targetAddress); memVal |= 0x02; // 0x02 is Binary 0000 0010 (Bit 1) diff --git a/Desktop/DebuggerForm.cs b/Desktop/DebuggerForm.cs index 516720f..11bc6a3 100644 --- a/Desktop/DebuggerForm.cs +++ b/Desktop/DebuggerForm.cs @@ -285,6 +285,9 @@ namespace Desktop case 0x19: mnemonic = "ADD HL, DE"; break; + case 0x1B: + mnemonic = "DEC DE"; + break; case 0x20: sbyte jrOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1)); ushort destination = (ushort)(currentPc + 2 + jrOffset); @@ -306,6 +309,11 @@ namespace Desktop case 0x23: mnemonic = "INC HL"; break; + case 0x26: + byte hImm = _memoryBus.Read((ushort)(currentPc + 1)); + mnemonic = $"LD H, 0x{hImm:X2}"; + instructionLength = 2; + break; case 0x28: sbyte jrZOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1)); ushort jrZDest = (ushort)(currentPc + 2 + jrZOffset); @@ -343,6 +351,9 @@ namespace Desktop mnemonic = $"LD (HL), 0x{memValue:X2}"; instructionLength = 2; break; + case 0x37: + mnemonic = "SCF"; + break; case 0x3E: mnemonic = $"LD A, 0x{_memoryBus.Read((ushort)(currentPc + 1)):X2}"; instructionLength = 2; @@ -350,6 +361,15 @@ namespace Desktop case 0x47: mnemonic = "LD B, A"; break; + case 0x4E: + mnemonic = "LD C, (HL)"; + break; + case 0x56: + mnemonic = "LD D, (HL)"; + break; + case 0x5E: + mnemonic = "LD E, (HL)"; + break; case 0x5F: mnemonic = "LD E, A"; break; @@ -359,9 +379,21 @@ namespace Desktop case 0x6B: mnemonic = "LD L, E"; break; + case 0x6F: + mnemonic = "LD L, A"; + break; case 0x77: mnemonic = "LD (HL), A"; break; + case 0x7A: + mnemonic = "LD A, D"; + break; + case 0x7E: + mnemonic = "LD A, (HL)"; + break; + case 0x87: + mnemonic = "ADD A, A"; + break; case 0x91: mnemonic = "SUB C"; break; @@ -371,6 +403,12 @@ namespace Desktop case 0xAF: mnemonic = "XOR A"; break; + case 0xB3: + mnemonic = "OR E"; + break; + case 0xB9: + mnemonic = "CP C"; + break; case 0xBC: mnemonic = "CP H"; break; @@ -381,6 +419,16 @@ namespace Desktop mnemonic = $"JP 0x{jpHigh:X2}{jpLow:X2}"; instructionLength = 3; break; + case 0xC6: + { + byte addImm = _memoryBus.Read((ushort)(currentPc + 1)); + mnemonic = $"ADD A, 0x{addImm:X2}"; + instructionLength = 2; + break; + } + case 0xC8: + mnemonic = "RET Z"; + break; case 0xC9: mnemonic = "RET"; break; @@ -389,6 +437,9 @@ namespace Desktop mnemonic = $"CALL 0x{callDest:X4}"; instructionLength = 3; break; + case 0xD0: + mnemonic = "RET NC"; + break; case 0xD3: byte outPort = _memoryBus.Read((ushort)(currentPc + 1)); mnemonic = $"OUT (0x{outPort:X2}), A"; @@ -490,6 +541,14 @@ namespace Desktop { mnemonic = $"BIT 1, (IY{sign}{d})"; } + else if (cbOpcode == 0x86) + { + mnemonic = $"RES 0, (IY{sign}{d})"; + } + else if (cbOpcode == 0xA6) + { + mnemonic = $"RES 4, (IY{sign}{d})"; + } else if (cbOpcode == 0xCE) { mnemonic = $"SET 1, (IY{sign}{d})";