From 1ccacb55d55629cc364950dd6c673c6bdd2939dc Mon Sep 17 00:00:00 2001 From: Marc Parsons Date: Sun, 19 Apr 2026 01:38:53 +0100 Subject: [PATCH] Almost runs Chuckie Egg. LDI to implement next --- Core/Cpu/Z80.cs | 116 +++++++++++++++++++++++++++++++++++++++- Desktop/DebuggerForm.cs | 48 +++++++++++++++++ 2 files changed, 163 insertions(+), 1 deletion(-) diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs index fefe95f..ca88462 100644 --- a/Core/Cpu/Z80.cs +++ b/Core/Cpu/Z80.cs @@ -14,6 +14,7 @@ namespace Core.Cpu // Interrupt Flip-Flops public bool IFF1 { get; private set; } = false; public bool IFF2 { get; private set; } = false; + public bool InterruptRequested { get; private set; } = false; // Main Register Set public RegisterPair AF; @@ -85,6 +86,7 @@ namespace Core.Cpu public int RequestInterrupt() { + InterruptRequested = true; // 1. If the ROM has disabled interrupts (DI), ignore the request if (!IFF1) return 0; @@ -887,6 +889,53 @@ namespace Core.Cpu case 0x26: // LD H, n HL.High = FetchByte(); return 7; + case 0x27: // DAA + byte a = AF.High; + int correction = 0; + byte flags = AF.Low; + + bool carry = (flags & 0x01) != 0; + bool halfCarry = (flags & 0x10) != 0; + bool isSub = (flags & 0x02) != 0; // The N flag tells us if we should add or subtract! + + // 1. Check if the lower nibble needs adjustment + if (halfCarry || (a & 0x0F) > 9) + { + correction |= 0x06; + } + + // 2. Check if the upper nibble needs adjustment + if (carry || a > 0x99 || (a >= 0x90 && (a & 0x0F) > 9)) + { + correction |= 0x60; + carry = true; // The final carry flag will be true + } + + // 3. Apply the correction and calculate the new Half-Carry + bool newHalfCarry = false; + if (isSub) + { + newHalfCarry = halfCarry && (a & 0x0F) < 0x06; + a = (byte)(a - correction); + } + else + { + newHalfCarry = ((a & 0x0F) + (correction & 0x0F)) > 0x0F; + a = (byte)(a + correction); + } + + AF.High = a; + + // 4. Build the new flags + flags &= 0x02; // Wipe everything except the N flag (which is strictly preserved) + if (carry) flags |= 0x01; + if (newHalfCarry) flags |= 0x10; + if ((a & 0x80) != 0) flags |= 0x80; // S flag + if (a == 0) flags |= 0x40; // Z flag + if (CalculateParity(a)) flags |= 0x04; // P/V flag + + AF.Low = flags; + return 4; case 0x28: // JR Z, e offset = (sbyte)FetchByte(); // Check if the Zero Flag is set @@ -1034,6 +1083,17 @@ namespace Core.Cpu case 0x73: _memory.Write(HL.Word, DE.Low); return 7; case 0x74: _memory.Write(HL.Word, HL.High); return 7; case 0x75: _memory.Write(HL.Word, HL.Low); return 7; + case 0x76: //HALT + if (!InterruptRequested) + { + PC--; + return 4; + } + else + { + InterruptRequested = false; + return 4; + } case 0x77: _memory.Write(HL.Word, AF.High); return 7; // --- LD A, r --- @@ -1501,6 +1561,27 @@ namespace Core.Cpu DE.Low = _memory.Read(src5B); DE.High = _memory.Read((ushort)(src5B + 1)); return 20; + case 0x5F: // LD A, R + // 1. Load the Refresh register into the Accumulator + AF.High = R; + + // 2. Calculate Flags + // CRITICAL: Preserve the existing Carry flag (Bit 0). + // H (Bit 4) and N (Bit 1) are forcefully reset to 0. + newFlags = (byte)(AF.Low & 0x01); + + // S Flag (Bit 7): Set if the result is negative + if ((AF.High & 0x80) != 0) newFlags |= 0x80; + + // Z Flag (Bit 6): Set if the result is zero + if (AF.High == 0) newFlags |= 0x40; + + // P/V Flag (Bit 2): Set if IFF2 is true (This is the interrupt check hack!) + if (IFF2) newFlags |= 0x04; + + AF.Low = newFlags; + + return 9; case 0x62: // SBC HL, HL Sbc16(HL.Word); return 15; @@ -1854,6 +1935,18 @@ namespace Core.Cpu _memory.Write((ushort)(address22 + 1), IX.High); return 20; + case 0x24: // INC IXH + // Increment the high byte of IX and let the helper perfectly map the flags + IX.High = Inc8(IX.High); + return 8; + case 0x25: // DEC IXH + // Decrement the high byte of IX and let the helper handle all the Z80 flags + IX.High = Dec8(IX.High); + return 8; + case 0x26: // LD IXH, n + // Fetch the immediate 8-bit value and drop it straight into the high byte of IX + IX.High = FetchByte(); + return 11; case 0x2A: // LD IX, (nn) // 1. Fetch the absolute 16-bit memory address from the instruction stream byte addrLow2A = FetchByte(); @@ -1869,7 +1962,14 @@ namespace Core.Cpu // 4. Combine them and drop them into the IX register pair IX.Word = (ushort)((ixHigh << 8) | ixLow); - return 20; // 20 T-States + return 20; + case 0x2D: // DEC IXL + IX.Low = Dec8(IX.Low); + return 8; + case 0x2E: // LD IXL, n + // Fetch the immediate 8-bit value and drop it straight into the low byte of IX + IX.Low = FetchByte(); + return 11; case 0x36: // LD (IX+d), n // 1. Fetch the displacement byte first sbyte offset36 = (sbyte)FetchByte(); @@ -1939,6 +2039,9 @@ namespace Core.Cpu HL.High = _memory.Read(address66); return 19; + case 0x68: // LD IXL, B + IX.Low = BC.High; + return 8; case 0x6E: // LD L, (IX+d) // 1. Fetch the displacement byte and cast it to a signed sbyte sbyte offset6E = (sbyte)FetchByte(); @@ -1972,6 +2075,17 @@ namespace Core.Cpu // 3. Write the contents of the L register (Low byte of HL) into memory _memory.Write(address75, HL.Low); + return 19; + case 0x77: // LD (IX+d), A + // 1. Fetch the displacement byte and cast it to a signed sbyte + sbyte offset77 = (sbyte)FetchByte(); + + // 2. Calculate the exact memory address (IX + offset) + ushort address77 = (ushort)(IX.Word + offset77); + + // 3. Write the Accumulator (AF.High) into memory at that address + _memory.Write(address77, AF.High); + return 19; case 0x7E: // LD A, (IX+d) // 1. Fetch the displacement byte and cast it to a signed sbyte diff --git a/Desktop/DebuggerForm.cs b/Desktop/DebuggerForm.cs index 3230fe3..93e5546 100644 --- a/Desktop/DebuggerForm.cs +++ b/Desktop/DebuggerForm.cs @@ -433,6 +433,10 @@ namespace Desktop mnemonic = $"LD H, 0x{hImm:X2}"; instructionLength = 2; break; + case 0x27: // DAA + mnemonic = "DAA"; + instructionLength = 1; + break; case 0x28: sbyte jrZOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1)); ushort jrZDest = (ushort)(currentPc + 2 + jrZOffset); @@ -845,12 +849,39 @@ namespace Desktop mnemonic = $"LD (0x{nn:X4}), IX"; instructionLength = 4; } + else if (ddOpcode == 0x24) // INC IXH + { + mnemonic = "INC IXH"; + instructionLength = 2; + } + else if (ddOpcode == 0x25) // DEC IXH + { + mnemonic = "DEC IXH"; + instructionLength = 2; + } + else if (ddOpcode == 0x26) // LD IXH, n + { + byte nValue = _memoryBus.Read((ushort)(currentPc + 2)); + mnemonic = $"LD IXH, 0x{nValue:X2}"; + instructionLength = 3; + } else if (ddOpcode == 0x2A) // LD IX, (nn) { ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8)); mnemonic = $"LD IX, (0x{nn:X4})"; instructionLength = 4; } + else if (ddOpcode == 0x2D) // DEC IXL + { + mnemonic = "DEC IXL"; + instructionLength = 2; + } + else if (ddOpcode == 0x2E) // LD IXL, n + { + byte nValue = _memoryBus.Read((ushort)(currentPc + 2)); + mnemonic = $"LD IXL, 0x{nValue:X2}"; + instructionLength = 3; + } else if (ddOpcode == 0x36) // LD (IX+d), n { sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); @@ -895,6 +926,11 @@ namespace Desktop mnemonic = $"LD H, (IX{sign}{d})"; instructionLength = 3; } + else if (ddOpcode == 0x68) // LD IXL, B + { + mnemonic = "LD IXL, B"; + instructionLength = 2; + } else if (ddOpcode == 0x6E) // LD L, (IX+d) { sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); @@ -916,6 +952,17 @@ namespace Desktop mnemonic = $"LD (IX{sign}{d}), L"; instructionLength = 3; } + else if (ddOpcode == 0x77) // LD (IX+d), A + { + // Read the 3rd byte (the displacement) + sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); + + // Format nicely with a + or - sign + string sign = offset >= 0 ? "+" : ""; + mnemonic = $"LD (IX{sign}{offset}), A"; + + instructionLength = 3; + } else if (ddOpcode == 0x7E) // LD A, (IX+d) { sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); @@ -1024,6 +1071,7 @@ namespace Desktop mnemonic = $"LD DE, (0x{addr5B:X4})"; instructionLength = 4; break; + case 0x5F: mnemonic = "LD A, R"; instructionLength = 2; break; case 0x6A: mnemonic = "ADC HL, HL"; instructionLength = 2; break; case 0x62: mnemonic = "SBC HL, HL"; instructionLength = 2; break; case 0x73: