diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs index a52b541..80c8ade 100644 --- a/Core/Cpu/Z80.cs +++ b/Core/Cpu/Z80.cs @@ -127,8 +127,121 @@ namespace Core.Cpu if (result < 0) AF.Low |= 0x01; } + private void Sbc16(ushort value) + { + int hl = HL.Word; + int carry = AF.Low & 0x01; + + // Calculate the raw integer result to check for underflows + int result = hl - value - carry; + + // Update the HL register + HL.Word = (ushort)result; + + // --- Update Flags (F Register) --- + AF.Low = 0; // Clear all flags + + // Sign Flag (Bit 7) - Set if the 16-bit result is negative (bit 15 is 1) + if ((result & 0x8000) != 0) AF.Low |= 0x80; + + // Zero Flag (Bit 6) - Set if the entire 16-bit result is exactly 0 + if ((ushort)result == 0) AF.Low |= 0x40; + + // Half-Carry Flag (Bit 4) - Set if borrow from bit 11 + if (((hl & 0x0FFF) - (value & 0x0FFF) - carry) < 0) AF.Low |= 0x10; + + // Overflow Flag (Bit 2) - Set if operands have different signs and result sign changes + if ((((hl ^ value) & 0x8000) != 0) && (((hl ^ result) & 0x8000) != 0)) AF.Low |= 0x04; + + // Subtract Flag (Bit 1) - ALWAYS set for subtraction + AF.Low |= 0x02; + + // Carry Flag (Bit 0) - Set if the overall 16-bit result dropped below 0 + if (result < 0) AF.Low |= 0x01; + } + + private void Cp(byte value) + { + byte a = AF.High; + int result = a - value; + + // --- 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 And(byte value) + { + AF.High = (byte)(AF.High & value); + + // --- Update Flags --- + AF.Low = 0; // Clear all flags + + // 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; + + // Half-Carry Flag (Bit 4) - ALWAYS SET to 1 for Z80 AND instructions! + AF.Low |= 0x10; + + // Parity Flag (Bit 2) - Set if the result has an even number of 1 bits + if (HasEvenParity(AF.High)) AF.Low |= 0x04; + + // Subtract Flag (N) and Carry Flag (C) are ALWAYS 0 + } + + private void Add16(ushort value) + { + int hl = HL.Word; + int result = hl + value; + + // Update the HL register + HL.Word = (ushort)result; + + // --- Update Flags (F Register) --- + // 16-bit ADD preserves S, Z, P/V (and the undocumented X/Y flags). + // We clear H (Bit 4), N (Bit 1), and C (Bit 0) using a bitwise AND mask (0xEC = 1110 1100) + AF.Low &= 0xEC; + + // Half-Carry Flag (Bit 4) - Set if there is a carry from bit 11 + if (((hl & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) AF.Low |= 0x10; + + // Carry Flag (Bit 0) - Set if the result overflows 16 bits + if (result > 0xFFFF) AF.Low |= 0x01; + } + + private bool HasEvenParity(byte value) + { + int bits = 0; + for (int i = 0; i < 8; i++) + { + if ((value & (1 << i)) != 0) bits++; + } + return (bits % 2) == 0; + } + private int ExecuteOpcode(byte opcode) { + sbyte offset = 0; switch (opcode) { case 0x00: // NOP @@ -136,9 +249,33 @@ namespace Core.Cpu case 0x11: //LD DE, nn DE.Word = FetchWord(); return 10; + case 0x19: // ADD HL, DE + Add16(DE.Word); + return 11; + case 0x20: // JR NZ, e + offset = (sbyte)FetchByte(); + if ((AF.Low & 0x40) == 0) + { + PC = (ushort)(PC + offset); + return 12; + } + return 7; + case 0x23: // INC HL + HL.Word++; + return 6; case 0x2B: // DEC HL HL.Word--; return 6; + case 0x30: // JR NC, e + offset = (sbyte)FetchByte(); + + // Check if the Carry Flag (Bit 0) is NOT set + if ((AF.Low & 0x01) == 0) + { + PC = (ushort)(PC + offset); + return 12; // Jump taken + } + return 7; // Jump not taken case 0x36: // LD (HL), n byte nValue = FetchByte(); _memory.Write(HL.Word, nValue); @@ -155,6 +292,12 @@ namespace Core.Cpu case 0x6B: // LD L, E HL.Low = DE.Low; return 4; + case 0xA7: // AND A + And(AF.High); + return 4; + case 0xBC: // CP H + Cp(HL.High); + return 4; case 0xC3: PC = FetchWord(); return 10; @@ -195,6 +338,9 @@ namespace Core.Cpu case 0x47: // LD I, A I = AF.High; return 9; + case 0x52: // SBC HL, DE + Sbc16(DE.Word); + return 15; // Takes 15 T-States default: throw new NotImplementedException($"Extended ED Opcode 0x{extendedOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented."); } diff --git a/Desktop/DebuggerForm.Designer.cs b/Desktop/DebuggerForm.Designer.cs index ab2cf75..db37b5d 100644 --- a/Desktop/DebuggerForm.Designer.cs +++ b/Desktop/DebuggerForm.Designer.cs @@ -182,19 +182,19 @@ // lstDisassembly // lstDisassembly.FormattingEnabled = true; - lstDisassembly.Location = new Point(533, 11); + lstDisassembly.Location = new Point(523, 11); lstDisassembly.Margin = new Padding(2); lstDisassembly.Name = "lstDisassembly"; - lstDisassembly.Size = new Size(186, 264); + lstDisassembly.Size = new Size(252, 264); lstDisassembly.TabIndex = 16; // // lstStack // lstStack.FormattingEnabled = true; - lstStack.Location = new Point(723, 11); + lstStack.Location = new Point(779, 11); lstStack.Margin = new Padding(2); lstStack.Name = "lstStack"; - lstStack.Size = new Size(186, 264); + lstStack.Size = new Size(130, 264); lstStack.TabIndex = 17; // // btnExit @@ -212,7 +212,7 @@ // AutoScaleDimensions = new SizeF(8F, 20F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(927, 350); + ClientSize = new Size(928, 350); Controls.Add(btnExit); Controls.Add(lstStack); Controls.Add(lstDisassembly); diff --git a/Desktop/DebuggerForm.cs b/Desktop/DebuggerForm.cs index 29e40fb..169b671 100644 --- a/Desktop/DebuggerForm.cs +++ b/Desktop/DebuggerForm.cs @@ -208,9 +208,27 @@ namespace Desktop mnemonic = $"LD DE, 0x{deHigh:X2}{deLow:X2}"; instructionLength = 3; break; + case 0x19: + mnemonic = "ADD HL, DE"; + break; + case 0x20: + sbyte jrOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1)); + ushort destination = (ushort)(currentPc + 2 + jrOffset); + mnemonic = $"JR NZ, 0x{destination:X4}"; + instructionLength = 2; + break; + case 0x23: + mnemonic = "INC HL"; + break; case 0x2B: mnemonic = "DEC HL"; break; + case 0x30: + sbyte jrNcOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1)); + ushort dest = (ushort)(currentPc + 2 + jrNcOffset); + mnemonic = $"JR NC, 0x{dest:X4}"; + instructionLength = 2; + break; case 0x36: byte memValue = _memoryBus.Read((ushort)(currentPc + 1)); mnemonic = $"LD (HL), 0x{memValue:X2}"; @@ -229,9 +247,15 @@ namespace Desktop case 0x6B: mnemonic = "LD L, E"; break; + case 0xA7: + mnemonic = "AND A"; + break; case 0xAF: mnemonic = "XOR A"; break; + case 0xBC: + mnemonic = "CP H"; + break; case 0xC3: // JP nn byte jpLow = _memoryBus.Read((ushort)(currentPc + 1)); @@ -259,7 +283,10 @@ namespace Desktop mnemonic = "LD I, A"; instructionLength = 2; // 0xED + 0x47 break; - + case 0x52: + mnemonic = "SBC HL, DE"; + instructionLength = 2; // ED 52 + break; // Example: ED B0 is LDIR (a massive block copy instruction) case 0xB0: mnemonic = "LDIR";