diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs index 27bd517..f5fa896 100644 --- a/Core/Cpu/Z80.cs +++ b/Core/Cpu/Z80.cs @@ -585,6 +585,7 @@ namespace Core.Cpu private int ExecuteOpcode(byte opcode) { sbyte offset = 0; + byte oldCarry = 0; switch (opcode) { case 0x00: // NOP @@ -723,6 +724,24 @@ namespace Core.Cpu case 0x16: // LD D, n DE.High = FetchByte(); return 7; + case 0x17: // RLA + // 1. Grab the current Carry flag (Bit 0 of AF.Low) + oldCarry = (byte)(AF.Low & 0x01); + + // 2. See if Bit 7 of the Accumulator is about to fall off + bool newCarry = (AF.High & 0x80) != 0; + + // 3. Shift A left, and drop the OLD carry directly into Bit 0 + AF.High = (byte)((AF.High << 1) | oldCarry); + + // 4. Update the flags + // Preserve S (Bit 7), Z (Bit 6), and P/V (Bit 2) while wiping H and N. + AF.Low &= 0xC4; + + // 5. Apply the new Carry flag if necessary + if (newCarry) AF.Low |= 0x01; + + return 4; // 4 T-States case 0x18: // JR d sbyte jumpDistance = (sbyte)FetchByte(); @@ -740,7 +759,7 @@ namespace Core.Cpu case 0x1F: // RRA { // 1. Grab the current Carry Flag (0 or 1) - byte oldCarry = (byte)(AF.Low & 0x01); + oldCarry = (byte)(AF.Low & 0x01); // 2. Grab the bit that is about to fall off the Accumulator byte bit0 = (byte)(AF.High & 0x01); @@ -1583,6 +1602,14 @@ namespace Core.Cpu // Shift left, and loop the falling bit back into Bit 0 val = (byte)((val << 1) | (carryOut ? 1 : 0)); break; + case 4: // SLA (Shift Left Arithmetic) + // 1. Grab Bit 7 before it falls off to set the Carry flag + carryOut = (val & 0x80) != 0; + + // 2. Shift the byte left by 1. + // (In C#, a standard left shift automatically pads Bit 0 with a 0) + val = (byte)(val << 1); + break; case 7: // SRL (Shift Right Logical) // 1. Grab Bit 0 before it falls off to set the Carry flag carryOut = (val & 0x01) != 0; @@ -1664,6 +1691,35 @@ namespace Core.Cpu IX.Word = (ushort)((high << 8) | low); return 14; + case 0x22: // LD (nn), IX + // 1. Fetch the absolute 16-bit memory address from the instruction stream + byte addrLow22 = FetchByte(); + byte addrHigh22 = FetchByte(); + ushort address22 = (ushort)((addrHigh22 << 8) | addrLow22); + + // 2. Write the LOW byte of IX to the exact address + _memory.Write(address22, IX.Low); + + // 3. Write the HIGH byte of IX to the address + 1 + _memory.Write((ushort)(address22 + 1), IX.High); + + return 20; + case 0x2A: // LD IX, (nn) + // 1. Fetch the absolute 16-bit memory address from the instruction stream + byte addrLow2A = FetchByte(); + byte addrHigh2A = FetchByte(); + ushort address2A = (ushort)((addrHigh2A << 8) | addrLow2A); + + // 2. Read the LOW byte from that specific memory location + byte ixLow = _memory.Read(address2A); + + // 3. Read the HIGH byte from the next consecutive memory location + byte ixHigh = _memory.Read((ushort)(address2A + 1)); + + // 4. Combine them and drop them into the IX register pair + IX.Word = (ushort)((ixHigh << 8) | ixLow); + + return 20; // 20 T-States case 0x36: // LD (IX+d), n // 1. Fetch the displacement byte first sbyte offset36 = (sbyte)FetchByte(); @@ -1677,6 +1733,28 @@ namespace Core.Cpu // 4. Write the immediate value directly into memory _memory.Write(address36, n36); + return 19; + case 0x46: // LD B, (IX+d) + // 1. Fetch the displacement byte and cast it to a signed sbyte + sbyte offset46 = (sbyte)FetchByte(); + + // 2. Calculate the exact memory address (IX + offset) + ushort address46 = (ushort)(IX.Word + offset46); + + // 3. Read the byte from memory and drop it into the C register (Low byte of BC) + BC.High = _memory.Read(address46); + + return 19; + case 0x4E: // LD C, (IX+d) + // 1. Fetch the displacement byte and cast it to a signed sbyte + sbyte offset4E = (sbyte)FetchByte(); + + // 2. Calculate the exact memory address (IX + offset) + ushort address4E = (ushort)(IX.Word + offset4E); + + // 3. Read the byte from memory and drop it into the C register (Low byte of BC) + BC.Low = _memory.Read(address4E); + return 19; case 0x56: // LD D, (IX+d) // 1. Fetch the displacement byte and cast it to a signed sbyte @@ -1699,6 +1777,17 @@ namespace Core.Cpu // 3. Read the byte from memory and drop it into the E register DE.Low = _memory.Read(address5E); + return 19; + case 0x66: // LD H, (IX+d) + // 1. Fetch the displacement byte and cast it to a signed sbyte + sbyte offset66 = (sbyte)FetchByte(); + + // 2. Calculate the exact memory address (IX + offset) + ushort address66 = (ushort)(IX.Word + offset66); + + // 3. Read the byte from memory and drop it into the H register (High byte of HL) + HL.High = _memory.Read(address66); + return 19; case 0x6E: // LD L, (IX+d) // 1. Fetch the displacement byte and cast it to a signed sbyte @@ -1711,6 +1800,7 @@ namespace Core.Cpu HL.Low = _memory.Read(address6E); return 19; + case 0x74: // LD (IX+d), H // 1. Fetch the displacement byte and cast it to a signed sbyte sbyte offset74 = (sbyte)FetchByte(); @@ -2098,183 +2188,4 @@ namespace Core.Cpu } } } -} - - -//sbyte offsetCB = (sbyte)FetchByte(); // This is the '01' -//byte bitOpcode = FetchByte(); // This is the 'CE' -//targetAddress = (ushort)(IY.Word + offsetCB); - -//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); - -// // Check if bit 1 is 0 -// bool bitIsZero = (memValBit & 0x02) == 0; - -// // Preserve the Carry flag (Bit 0), clear everything else -// AF.Low &= 0x01; - -// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT -// AF.Low |= 0x10; - -// if (bitIsZero) -// { -// AF.Low |= 0x40; // Set Zero Flag (Bit 6) -// AF.Low |= 0x04; // Set P/V Flag (Bit 2) -// } - -// return 20; -// } -// case 0x66: // BIT 4, (IY+d) -// byte memValBit4 = _memory.Read(targetAddress); - -// // Preserve ONLY the Carry flag (Bit 0). This clears N and everything else. -// AF.Low &= 0x01; - -// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT instructions -// AF.Low |= 0x10; - -// // Test Bit 4 (0x10 is Binary 0001 0000) -// if ((memValBit4 & 0x10) == 0) -// { -// // If the bit is 0, turn ON the Zero Flag (Bit 6) -// AF.Low |= 0x40; - -// // Parity/Overflow (Bit 2) is historically set along with the Zero flag on BIT -// AF.Low |= 0x04; -// } -// // (Note: The Sign Flag remains 0 because we are not testing Bit 7) - -// return 20; -// case 0x6E: // BIT 5, (IY+d) -// byte memValBit5 = _memory.Read(targetAddress); - -// // Preserve ONLY the Carry flag (Bit 0). This clears N and everything else. -// AF.Low &= 0x01; - -// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT instructions -// AF.Low |= 0x10; - -// // Test Bit 5 (0x20 is Binary 0010 0000) -// if ((memValBit5 & 0x20) == 0) -// { -// // If the bit is 0, turn ON the Zero Flag (Bit 6) -// AF.Low |= 0x40; - -// // Parity/Overflow (Bit 2) is historically set along with the Zero flag on BIT -// AF.Low |= 0x04; -// } -// // (Note: The Sign Flag remains 0 because we are not testing Bit 7) - -// return 20; -// case 0x76: // BIT 6, (IY+d) -// byte memValBit6 = _memory.Read(targetAddress); - -// // Preserve ONLY the Carry flag (Bit 0). This clears N and everything else. -// AF.Low &= 0x01; - -// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT -// AF.Low |= 0x10; - -// // Test Bit 6 (0x40 is Binary 0100 0000) -// if ((memValBit6 & 0x40) == 0) -// { -// // If the bit is 0, turn ON the Zero Flag (Bit 6) -// AF.Low |= 0x40; - -// // Parity/Overflow (Bit 2) is historically set along with the Zero flag on BIT -// AF.Low |= 0x04; -// } -// // (Note: The Sign Flag remains 0 because we are not testing Bit 7) - -// return 20; -// 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); - -// // 0xFD is Binary 1111 1101. -// // ANDing with this preserves all bits except Bit 1, which becomes 0. -// 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 0xAE: // RES 5, (IY+d) -// byte memValRes5 = _memory.Read(targetAddress); - -// // 0xDF is Binary 1101 1111 -// // ANDing perfectly preserves all other bits while forcing Bit 5 to 0 -// memValRes5 &= 0xDF; - -// _memory.Write(targetAddress, memValRes5); -// return 23; -// case 0xC6: // SET 0, (IY+d) -// byte memValSet0 = _memory.Read(targetAddress); - -// // 0x01 is Binary 0000 0001 -// // ORing forces Bit 0 to 1 and perfectly preserves all other bits -// memValSet0 |= 0x01; - -// _memory.Write(targetAddress, memValSet0); -// return 23; // Takes 23 T-States -// case 0xCE: // SET 1, (IY+d) -// memVal = _memory.Read(targetAddress); -// memVal |= 0x02; // 0x02 is Binary 0000 0010 (Bit 1) -// _memory.Write(targetAddress, memVal); -// return 23; -// case 0xE6: // SET 4, (IY+d) -// byte memValSet4 = _memory.Read(targetAddress); - -// // 0x10 is Binary 0001 0000 -// // ORing perfectly preserves all other bits while forcing Bit 4 to 1 -// memValSet4 |= 0x10; - -// _memory.Write(targetAddress, memValSet4); -// return 23; -// case 0xEE: // SET 5, (IY+d) -// byte memValSet5 = _memory.Read(targetAddress); - -// // 0x20 is Binary 0010 0000 -// // ORing perfectly preserves all other bits while forcing Bit 5 to 1 -// memValSet5 |= 0x20; - -// _memory.Write(targetAddress, memValSet5); -// return 23; - -// default: -// throw new NotImplementedException($"FD CB opcode {bitOpcode:X2} at PC 0x{(PC - 1):X4} not implemented!"); -//} \ No newline at end of file +} \ No newline at end of file diff --git a/Desktop/DebuggerForm.cs b/Desktop/DebuggerForm.cs index b9cddd8..0cd7524 100644 --- a/Desktop/DebuggerForm.cs +++ b/Desktop/DebuggerForm.cs @@ -385,6 +385,10 @@ namespace Desktop mnemonic = $"LD D, 0x{dImm:X2}"; instructionLength = 2; break; + case 0x17: // RLA + mnemonic = "RLA"; + instructionLength = 1; + break; case 0x18: sbyte dUnconditional = (sbyte)_memoryBus.Read((ushort)(currentPc + 1)); // Calculate the target address based on the PC *after* this 2-byte instruction @@ -753,12 +757,11 @@ namespace Desktop case 0xCB: cbOp = _memoryBus.Read((ushort)(currentPc + 1)); - // --- THE MISSING MATH EXTRACTION --- opGroup = cbOp >> 6; // 00 = Shift, 01 = BIT, 10 = RES, 11 = SET targetBit = (cbOp >> 3) & 0x07; // Extracts a number 0-7 regIdx = cbOp & 0x07; // Extracts register index 0-7 - // Map the 0-7 index directly to the Z80 register names + // Map the 0-7 index directly to the Z80 register names targetReg = regNames[regIdx]; if (opGroup == 0) // Shift/Rotate Group (0x00 to 0x3F) @@ -779,13 +782,9 @@ namespace Desktop { mnemonic = $"SET {targetBit}, {targetReg}"; } - else if (opGroup == 7) // SRL Group (0x38 to 0x3F) - { - mnemonic = $"SET {targetBit}, {targetReg}"; - } else { - mnemonic = $"EXT UNKNOWN (ED {cbOp:X2})"; + mnemonic = $"CB UNKNOWN (CB {cbOp:X2})"; } instructionLength = 2; break; @@ -841,6 +840,18 @@ namespace Desktop mnemonic = $"LD IX, 0x{ixVal:X4}"; instructionLength = 4; } + else if (ddOpcode == 0x22) // LD (nn), IX + { + ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8)); + mnemonic = $"LD (0x{nn:X4}), IX"; + instructionLength = 4; + } + 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 == 0x36) // LD (IX+d), n { sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); @@ -850,6 +861,20 @@ namespace Desktop mnemonic = $"LD (IX{sign}{d}), 0x{n:X2}"; instructionLength = 4; } + else if (ddOpcode == 0x46) // LD B, (IX+d) + { + sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); + string sign = d >= 0 ? "+" : ""; + mnemonic = $"LD B, (IX{sign}{d})"; + instructionLength = 3; + } + else if (ddOpcode == 0x4E) // LD C, (IX+d) + { + sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); + string sign = d >= 0 ? "+" : ""; + mnemonic = $"LD C, (IX{sign}{d})"; + instructionLength = 3; + } else if (ddOpcode == 0x56) // LD D, (IX+d) { sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); @@ -864,6 +889,13 @@ namespace Desktop mnemonic = $"LD E, (IX{sign}{d})"; instructionLength = 3; } + else if (ddOpcode == 0x66) // LD H, (IX+d) + { + sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); + string sign = d >= 0 ? "+" : ""; + mnemonic = $"LD H, (IX{sign}{d})"; + instructionLength = 3; + } else if (ddOpcode == 0x6E) // LD L, (IX+d) { sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); diff --git a/Desktop/Form1.cs b/Desktop/Form1.cs index b3d34ad..2c83384 100644 --- a/Desktop/Form1.cs +++ b/Desktop/Form1.cs @@ -84,7 +84,7 @@ namespace Desktop // The pure Core logic processes the bytes _cpu._tapManager.LoadTapData(tapBytes); - MessageBox.Show("Tape inserted! Type LOAD \"\" and press Enter.", "Tape Deck"); + //MessageBox.Show("Tape inserted! Type LOAD \"\" and press Enter.", "Tape Deck"); } } } @@ -198,16 +198,14 @@ namespace Desktop { switch (key) { - //1:6 - - - //Row 0: CAPS_SHIFT, Z, X, C, V + //Row 0: case Keys.ShiftKey: UpdateMatrix(0, 0, isPressed); break; case Keys.Z: UpdateMatrix(0, 1, isPressed); break; case Keys.X: UpdateMatrix(0, 2, isPressed); break; case Keys.C: UpdateMatrix(0, 3, isPressed); break; case Keys.V: UpdateMatrix(0, 4, isPressed); break; - // Row 1: A, S, D, F, G + // Row 1 case Keys.A: UpdateMatrix(1, 0, isPressed); break; case Keys.S: UpdateMatrix(1, 1, isPressed); break; case Keys.D: UpdateMatrix(1, 2, isPressed); break; @@ -242,14 +240,14 @@ namespace Desktop case Keys.U: UpdateMatrix(5, 3, isPressed); break; case Keys.Y: UpdateMatrix(5, 4, isPressed); break; - // Row 6: ENTER, L, K, J, H + // Row 6 case Keys.Enter: UpdateMatrix(6, 0, isPressed); break; case Keys.L: UpdateMatrix(6, 1, isPressed); break; case Keys.K: UpdateMatrix(6, 2, isPressed); break; case Keys.J: UpdateMatrix(6, 3, isPressed); break; case Keys.H: UpdateMatrix(6, 4, isPressed); break; - // Row 7: SPACE, SYM SHIFT, M, N, B + // Row 7 case Keys.Space: UpdateMatrix(7, 0, isPressed); break; case Keys.ControlKey: UpdateMatrix(7, 1, isPressed); break; // Symbol Shift case Keys.M: UpdateMatrix(7, 2, isPressed); break;