diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs index cc7551b..ccc07a3 100644 --- a/Core/Cpu/Z80.cs +++ b/Core/Cpu/Z80.cs @@ -445,6 +445,34 @@ namespace Core.Cpu if (result < 0) AF.Low |= 0x01; } + private void SbcHl(ushort value) + { + int op1 = HL.Word; + int op2 = value; + int carry = AF.Low & 0x01; // Current C flag + int result = op1 - op2 - carry; + + byte flags = 0x02; // N: Always 1 (Subtract) + + if (((result >> 8) & 0x80) != 0) flags |= 0x80; // S: Sign flag (Bit 15) + if ((result & 0xFFFF) == 0) flags |= 0x40; // Z: Zero flag + + // H: Half-borrow from Bit 12 + if ((((op1 & 0x0FFF) - (op2 & 0x0FFF) - carry) & 0x1000) != 0) flags |= 0x10; + + // P/V: 16-bit Overflow logic + if ((((op1 ^ op2) & (op1 ^ result)) & 0x8000) != 0) flags |= 0x04; + + // C: Borrow from Bit 15 + if (result < 0) flags |= 0x01; + + // Undocumented bits 3 and 5 come from the High byte of the calculated result + flags |= (byte)((result >> 8) & 0x28); + + AF.Low = flags; + HL.Word = (ushort)(result & 0xFFFF); + } + private byte Dec8(byte value) { byte result = (byte)(value - 1); @@ -702,10 +730,67 @@ namespace Core.Cpu HL.Word = (ushort)result; } + private void AdcHl(ushort value) + { + int op1 = HL.Word; + int op2 = value; + int carry = AF.Low & 0x01; // Current C flag + int result = op1 + op2 + carry; + + byte flags = 0; + + if (((result >> 8) & 0x80) != 0) flags |= 0x80; // S: Sign flag (Bit 15) + if ((result & 0xFFFF) == 0) flags |= 0x40; // Z: Zero flag + + // H: Half-carry from Bit 11 + if ((((op1 & 0x0FFF) + (op2 & 0x0FFF) + carry) & 0x1000) != 0) flags |= 0x10; + + // P/V: 16-bit Overflow logic + if ((((op1 ^ ~op2) & (op1 ^ result)) & 0x8000) != 0) flags |= 0x04; + + // N: Always 0 for Add + + // C: Carry from Bit 15 + if ((result & 0x10000) != 0) flags |= 0x01; + + // Undocumented bits 3 and 5 come from the High byte of the calculated result + flags |= (byte)((result >> 8) & 0x28); + + AF.Low = flags; + HL.Word = (ushort)(result & 0xFFFF); + } + + private void AddHl(ushort value) + { + int result = HL.Word + value; + + // 1. Preserve S (0x80), Z (0x40), and P/V (0x04) from the current flag register + byte flags = (byte)(AF.Low & 0xC4); + + // 2. Calculate H (Half-carry from bit 11) + if (((HL.Word & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) + { + flags |= 0x10; + } + + // 3. Calculate C (Carry from bit 15) + if (result > 0xFFFF) + { + flags |= 0x01; + } + + // 4. Undocumented bits 3 and 5 come from the High byte of the calculated result + flags |= (byte)((result >> 8) & 0x28); + + // N is naturally left as 0 because of our initial bitmask + AF.Low = flags; + HL.Word = (ushort)result; + } + private void AddA(byte operand) { byte a = AF.High; - result = a + operand; + int result = a + operand; // Use a local int to easily catch the carry AF.High = (byte)result; @@ -728,8 +813,49 @@ namespace Core.Cpu // Carry Flag (Bit 0) - Check if the whole 8-bit addition overflowed if (result > 0xFF) AF.Low |= 0x01; + + // UNDOCUMENTED FLAGS (Bits 3 and 5) - Copied directly from the result + AF.Low |= (byte)(AF.High & 0x28); + } + private void AddIx(ushort value) + { + int result = IX.Word + value; + + // Preserve S, Z, and P/V + byte flags = (byte)(AF.Low & 0xC4); + + // Calculate H (Half-carry from bit 11) + if (((IX.Word & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) flags |= 0x10; + + // Calculate C (Carry from bit 15) + if (result > 0xFFFF) flags |= 0x01; + + // Undocumented bits 3 and 5 from the High byte of the result + flags |= (byte)((result >> 8) & 0x28); + + AF.Low = flags; + IX.Word = (ushort)result; } + private void AddIy(ushort value) + { + int result = IY.Word + value; + + // Preserve S, Z, and P/V + byte flags = (byte)(AF.Low & 0xC4); + + // Calculate H (Half-carry from bit 11) + if (((IY.Word & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) flags |= 0x10; + + // Calculate C (Carry from bit 15) + if (result > 0xFFFF) flags |= 0x01; + + // Undocumented bits 3 and 5 from the High byte of the result + flags |= (byte)((result >> 8) & 0x28); + + AF.Low = flags; + IY.Word = (ushort)result; + } private bool HasEvenParity(byte value) { int bits = 0; @@ -805,6 +931,8 @@ namespace Core.Cpu AF.Word = AF_Prime.Word; AF_Prime.Word = tempAF; return 4; + // Inside your base switch(opcode) statement: + case 0x09: AddHl(BC.Word); return 11; case 0x0A: //LD A (BC) AF.High = ReadMemory(BC.Word); return 7; @@ -813,9 +941,11 @@ namespace Core.Cpu WriteMemory(DE.Word, AF.High); return 7; case 0x14: DE.High = Inc8(DE.High); return 4; // INC D + case 0x19: AddHl(DE.Word); return 11; case 0x1C: DE.Low = Inc8(DE.Low); return 4; // INC E case 0x1E: DE.Low = FetchByte(); // LD E, n - return 7; + return 7; + case 0x29: AddHl(HL.Word); return 11; case 0x24: HL.High = Inc8(HL.High); return 4; // INC H case 0x2C: HL.Low = Inc8(HL.Low); return 4; // INC L case 0x2E: // LD L, n @@ -824,6 +954,7 @@ namespace Core.Cpu case 0x34: WriteMemory(HL.Word, Inc8(ReadMemory(HL.Word))); return 11; // INC (HL) takes 11 T-States + case 0x39: AddHl(SP); return 11; case 0x3C: AF.High = Inc8(AF.High); return 4; // INC A // --- 8-Bit Decrements --- @@ -849,19 +980,6 @@ namespace Core.Cpu case 0x06: // LD B, n BC.High = FetchByte(); return 7; - // --- ADD HL, rr (16-bit Addition) --- - case 0x09: - Add16(BC.Word); - return 11; - case 0x19: - Add16(DE.Word); - return 11; - case 0x29: - Add16(HL.Word); // This perfectly multiplies HL by 2! - return 11; - case 0x39: - Add16(SP); - return 11; case 0x0B: // DEC BC BC.Word--; return 6; @@ -1588,9 +1706,6 @@ namespace Core.Cpu switch (extendedOpcode) { - case 0x42: // SBC HL, BC - Sbc16(BC.Word); - return 15; case 0x43: // LD (nn), BC ushort dest43 = FetchWord(); WriteMemory(dest43, BC.Low); @@ -1626,10 +1741,7 @@ namespace Core.Cpu return 8; // 8 T-States case 0x47: // LD I, A I = AF.High; - return 9; - case 0x4A: // ADC HL, BC - Adc16(BC.Word); - return 15; + return 9; case 0x4B: // LD BC, (nn) ushort src4B = FetchWord(); BC.Low = ReadMemory(src4B); @@ -1638,9 +1750,6 @@ namespace Core.Cpu case 0x4D: // RETI Does not affect IFF1 or IFF2 PC = Pop(); return 14; - case 0x52: // SBC HL, DE - Sbc16(DE.Word); - return 15; case 0x53: // LD (nn), DE ushort dest53 = FetchWord(); WriteMemory(dest53, DE.Low); @@ -1677,9 +1786,6 @@ namespace Core.Cpu AF.Low = flags58; return 12; - case 0x5A: // ADC HL, DE - Adc16(DE.Word); - return 15; case 0x5B: // LD DE, (nn) ushort src5B = FetchWord(); DE.Low = ReadMemory(src5B); @@ -1710,46 +1816,11 @@ namespace Core.Cpu AF.Low = newFlags; return 9; - case 0x62: // SBC HL, HL - Sbc16(HL.Word); - return 15; - case 0x6A: // ADC HL, HL - Adc16(HL.Word); - return 15; - case 0x72: // SBC HL, SP - int carryIn = AF.Low & 0x01; - int hlVal = HL.Word; - int spVal = SP; - - // Perform the full 16-bit subtraction including the carry flag - result = hlVal - spVal - carryIn; - - newFlags = 0; - - // S Flag (Bit 7): Set if the 16-bit result is negative (Bit 15 is 1) - if ((result & 0x8000) != 0) newFlags |= 0x80; - - // Z Flag (Bit 6): Set if the full 16-bit result is exactly 0 - if ((result & 0xFFFF) == 0) newFlags |= 0x40; - - // H Flag (Bit 4): Set if there was a borrow from Bit 11 - if (((hlVal & 0x0FFF) - (spVal & 0x0FFF) - carryIn) < 0) newFlags |= 0x10; - - // P/V Flag (Bit 2): Set on Overflow - // Overflow happens if the signs of the operands are different, - // AND the sign of the result is different from the original HL - if ((((hlVal ^ spVal) & (hlVal ^ result)) & 0x8000) != 0) newFlags |= 0x04; - - // N Flag (Bit 1): Always set to 1 for a subtraction - newFlags |= 0x02; - - // C Flag (Bit 0): Set if the total result underflows 0 (Borrow from Bit 15) - if (result < 0) newFlags |= 0x01; - - AF.Low = newFlags; - HL.Word = (ushort)result; - - return 15; // 15 T-States + // --- SBC HL, rr --- + case 0x42: SbcHl(BC.Word); return 15; + case 0x52: SbcHl(DE.Word); return 15; + case 0x62: SbcHl(HL.Word); return 15; + case 0x72: SbcHl(SP); return 15; case 0x73: // LD (nn), SP ushort dest73 = FetchWord(); WriteMemory(dest73, (byte)SP); @@ -1777,9 +1848,11 @@ namespace Core.Cpu case 0x79: // OUT (C), A _simpleIoBus.WritePort(BC.Word, AF.High); return 12; - case 0x7A: // ADC HL, SP - Adc16(SP); - return 15; + // --- ADC HL, rr --- + case 0x4A: AdcHl(BC.Word); return 15; + case 0x5A: AdcHl(DE.Word); return 15; + case 0x6A: AdcHl(HL.Word); return 15; + case 0x7A: AdcHl(SP); return 15; case 0x7B: // LD SP, (nn) // 1. Fetch the absolute 16-bit memory address from the instruction stream byte addrLow = FetchByte(); @@ -2386,6 +2459,15 @@ namespace Core.Cpu AF.High = ReadMemory(address7E); return 19; + // Inside ExecuteDDPrefix(): + + case 0x84: // ADD A, IXH + AddA(IX.High); // Assuming your 16-bit register struct has a .High property + return 8; + + case 0x85: // ADD A, IXL + AddA(IX.Low); + return 8; case 0x86: // ADD A, (IX+d) sbyte offset86 = (sbyte)FetchByte(); ushort address86 = (ushort)(IX.Word + offset86); @@ -2517,36 +2599,9 @@ namespace Core.Cpu switch (opcode) { - case 0x19: // ADD IY, DE - // 1. Perform the 16-bit addition using a 32-bit integer to catch the carry - int result19 = IY.Word + DE.Word; - - // 2. Update the Flags Register (F) - // We start by stripping out N, H, C, and bits 3/5, but strictly PRESERVING S, Z, and P/V (0xC4) - byte flags19 = (byte)(AF.Low & 0xC4); - - // H: Set if there is a carry from bit 11 - if (((IY.Word & 0x0FFF) + (DE.Word & 0x0FFF)) > 0x0FFF) - { - flags19 |= 0x10; - } - - // C: Set if the result overflows 16 bits (carry from bit 15) - if (result19 > 0xFFFF) - { - flags19 |= 0x01; - } - - // Undocumented bits 3 and 5 are copied directly from the high byte of the result - flags19 |= (byte)((result19 >> 8) & 0x28); - - // N (Subtract) is naturally left as 0 because of our initial bitmask - AF.Low = flags19; - - // 3. Store the clean 16-bit result back into IY - IY.Word = (ushort)result19; - - return 15; + // Inside ExecuteFDPrefix() + case 0x09: AddIy(BC.Word); return 15; + case 0x19: AddIy(DE.Word); return 15; // This is the exact instruction that crashed! case 0x21: // LD IY, nn IY.Word = FetchWord(); return 14; @@ -2554,6 +2609,8 @@ namespace Core.Cpu // Increment the full 16-bit register. The F register remains completely untouched. IY.Word++; return 10; + + case 0x29: AddIy(IY.Word); return 15; case 0x34: // INC (IY+d) // 1. Fetch displacement and calculate memory address sbyte offset34 = (sbyte)FetchByte(); @@ -2609,6 +2666,8 @@ namespace Core.Cpu WriteMemory(targetAddress, nValue); return 19; // Takes 19 T-States } + + case 0x39: AddIy(SP); return 15; case 0x46: // LD B, (IY+d) { sbyte displacement = (sbyte)FetchByte(); diff --git a/Desktop/DebuggerForm.cs b/Desktop/DebuggerForm.cs index 6a541af..8327f9f 100644 --- a/Desktop/DebuggerForm.cs +++ b/Desktop/DebuggerForm.cs @@ -1112,8 +1112,12 @@ namespace Desktop case 0xFD: { byte fdOpcode = _memoryBus.Read((ushort)(currentPc + 1)); - - if (fdOpcode == 0x19) // ADD IY, DE + if (fdOpcode == 0x09) // ADD IY, BC + { + mnemonic = $"ADD IY, BC"; + instructionLength = 2; + } + else if (fdOpcode == 0x19) // ADD IY, DE { mnemonic = $"ADD IY, DE"; instructionLength = 2; @@ -1124,6 +1128,11 @@ namespace Desktop mnemonic = $"LD IY, 0x{iyVal:X4}"; instructionLength = 4; } + else if (fdOpcode == 0x29) //Add IY, IY + { + mnemonic = $"ADD IY, IY"; + instructionLength = 2; + } else if (fdOpcode == 0x34) // INC IY { //sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); @@ -1290,7 +1299,7 @@ namespace Desktop mnemonic = "POP IY"; instructionLength = 2; } - else if(fdOpcode == 0xE5) + else if (fdOpcode == 0xE5) { mnemonic = "PUSH IY"; instructionLength = 2; diff --git a/Desktop/Form1.cs b/Desktop/Form1.cs index 7422b48..78c4216 100644 --- a/Desktop/Form1.cs +++ b/Desktop/Form1.cs @@ -102,12 +102,12 @@ namespace Desktop _tapManager.Update(elapsedTStates); //Process audio at the correct time - while (_cpu.TotalTStates >= (long)(audioSampleCount * 79.365)) - { - bool finalAudioOutput = _simpleIoBus.BeeperState ^ _tapManager.EarBit; - _beeper.AddSample(finalAudioOutput); - audioSampleCount++; - } + //while (_cpu.TotalTStates >= (long)(audioSampleCount * 79.365)) + //{ + // bool finalAudioOutput = _simpleIoBus.BeeperState ^ _tapManager.EarBit; + // _beeper.AddSample(finalAudioOutput); + // audioSampleCount++; + //} // --- Check for End of Frame --- if (_cpu.TotalTStates >= nextScanlineTarget) @@ -131,13 +131,13 @@ namespace Desktop TotalFrameCount++; // Throttle to real-time (50 FPS = 20ms) - long targetTimeMs = (scanlineCount / 312) * 20; - long elapsedMs = stopwatch.ElapsedMilliseconds; + //long targetTimeMs = (scanlineCount / 312) * 20; + //long elapsedMs = stopwatch.ElapsedMilliseconds; - if (elapsedMs < targetTimeMs) - { - //Thread.Sleep((int)(targetTimeMs - elapsedMs)); - } + //if (elapsedMs < targetTimeMs) + //{ + // Thread.Sleep((int)(targetTimeMs - elapsedMs)); + //} TotalFrameTime += fpsStopwatch.Elapsed.TotalMilliseconds; if (TotalFrameCount % 50 == 0) {