Implemented a shit load more OpCodes

This commit is contained in:
2026-04-15 01:47:23 +01:00
parent 7e7453691f
commit f52180aeb3
2 changed files with 270 additions and 26 deletions

View File

@@ -474,15 +474,67 @@ namespace Core.Cpu
case 0x03: // INC BC case 0x03: // INC BC
BC.Word++; BC.Word++;
return 6; return 6;
case 0x04: // INC B // --- 8-Bit Increments ---
BC.High = Inc8(BC.High); case 0x04: BC.High = Inc8(BC.High); return 4; // INC B
return 4; case 0x0C: BC.Low = Inc8(BC.Low); return 4; // INC C
case 0x14: DE.High = Inc8(DE.High); return 4; // INC D
case 0x1C: DE.Low = Inc8(DE.Low); return 4; // INC E
case 0x24: HL.High = Inc8(HL.High); return 4; // INC H
case 0x2C: HL.Low = Inc8(HL.Low); return 4; // INC L
case 0x34:
_memory.Write(HL.Word, Inc8(_memory.Read(HL.Word)));
return 11; // INC (HL) takes 11 T-States
case 0x3C: AF.High = Inc8(AF.High); return 4; // INC A
// --- 8-Bit Decrements ---
case 0x05: BC.High = Dec8(BC.High); return 4; // DEC B
case 0x0D: BC.Low = Dec8(BC.Low); return 4; // DEC C
case 0x15: DE.High = Dec8(DE.High); return 4; // DEC D
case 0x1D: DE.Low = Dec8(DE.Low); return 4; // DEC E
case 0x25: HL.High = Dec8(HL.High); return 4; // DEC H
case 0x2D: HL.Low = Dec8(HL.Low); return 4; // DEC L
case 0x35:
_memory.Write(HL.Word, Dec8(_memory.Read(HL.Word)));
return 11; // DEC (HL) takes 11 T-States
case 0x3D: AF.High = Dec8(AF.High); return 4; // DEC A
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 case 0x0B: // DEC BC
BC.Word--; BC.Word--;
return 6; return 6;
case 0x0E: // LD C, n case 0x0E: // LD C, n
BC.Low = FetchByte(); BC.Low = FetchByte();
return 7; // Takes 7 T-States return 7;
case 0x0F: // RRCA
// 1. Grab the bit that is about to fall off
byte bit0 = (byte)(AF.High & 0x01);
// 2. Shift right, and force the old Bit 0 into the Bit 7 position
AF.High = (byte)((AF.High >> 1) | (bit0 << 7));
// 3. Update Flags
// S (0x80), Z (0x40), and P/V (0x04) are completely PRESERVED.
// H (0x10) and N (0x02) are forcefully RESET to 0.
// ANDing with 0xC4 (Binary 1100 0100) does exactly this.
AF.Low &= 0xC4;
// Set the Carry Flag (Bit 0) to whatever fell off
AF.Low |= bit0;
return 4;
case 0x10: // DJNZ d case 0x10: // DJNZ d
sbyte djnzOffset = (sbyte)FetchByte(); sbyte djnzOffset = (sbyte)FetchByte();
@@ -512,9 +564,9 @@ namespace Core.Cpu
PC = (ushort)(PC + jumpDistance); PC = (ushort)(PC + jumpDistance);
return 12; return 12;
case 0x19: // ADD HL, DE case 0x1A: // LD A, (DE)
Add16(DE.Word); AF.High = _memory.Read(DE.Word);
return 11; return 7;
case 0x1B: // DEC DE case 0x1B: // DEC DE
DE.Word--; DE.Word--;
return 6; return 6;
@@ -579,17 +631,6 @@ namespace Core.Cpu
case 0x33: // INC SP case 0x33: // INC SP
SP++; SP++;
return 6; return 6;
case 0x35: // DEC (HL)
// Read the current byte from memory
byte memValue = _memory.Read(HL.Word);
// Decrement it and update flags
byte decremented = Dec8(memValue);
// Write the new value back to memory
_memory.Write(HL.Word, decremented);
return 11; // Takes 11 T-States
case 0x36: // LD (HL), n case 0x36: // LD (HL), n
byte nValue = FetchByte(); byte nValue = FetchByte();
_memory.Write(HL.Word, nValue); _memory.Write(HL.Word, nValue);
@@ -607,6 +648,10 @@ namespace Core.Cpu
return 12; return 12;
} }
return 7; return 7;
case 0x3A: // LD A, (nn)
ushort address3A = FetchWord();
AF.High = _memory.Read(address3A);
return 13;
case 0x3B: // DEC SP case 0x3B: // DEC SP
SP--; SP--;
return 6; return 6;
@@ -764,6 +809,16 @@ namespace Core.Cpu
case 0xC6: // ADD A, n case 0xC6: // ADD A, n
Add(FetchByte()); Add(FetchByte());
return 7; return 7;
// --- RST Instructions (11 T-States) ---
// An RST is effectively a 1-byte CALL to a fixed Page 0 address.
case 0xC7: Push(PC); PC = 0x0000; return 11; // RST 00h (Equivalent to a hardware reset)
case 0xCF: Push(PC); PC = 0x0008; return 11; // RST 08h (Spectrum Error handler)
case 0xD7: Push(PC); PC = 0x0010; return 11; // RST 10h (Spectrum Print Character)
case 0xDF: Push(PC); PC = 0x0018; return 11; // RST 18h (Spectrum Collect Next Char)
case 0xE7: Push(PC); PC = 0x0020; return 11; // RST 20h (Spectrum Collect Next Char/Space)
case 0xEF: Push(PC); PC = 0x0028; return 11; // RST 28h (Spectrum Floating Point Calculator)
case 0xF7: Push(PC); PC = 0x0030; return 11; // RST 30h (Spectrum Make BC Spaces)
case 0xFF: Push(PC); PC = 0x0038; return 11; // RST 38h (Maskable Interrupt Handler)
case 0xC8: // RET Z case 0xC8: // RET Z
// Check if the Zero Flag (Bit 6) IS set // Check if the Zero Flag (Bit 6) IS set
if ((AF.Low & 0x40) != 0) if ((AF.Low & 0x40) != 0)
@@ -775,6 +830,8 @@ namespace Core.Cpu
case 0xC9: // RET case 0xC9: // RET
PC = Pop(); PC = Pop();
return 10; return 10;
case 0xCB:
return ExecuteCBPrefix();
case 0xCD: // CALL nn case 0xCD: // CALL nn
ushort callAddress = FetchWord(); ushort callAddress = FetchWord();
Push(PC); Push(PC);
@@ -806,6 +863,14 @@ namespace Core.Cpu
case 0xD6: // SUB n case 0xD6: // SUB n
Sub(FetchByte()); Sub(FetchByte());
return 7; return 7;
case 0xD8: // RET C
// Check if the Carry Flag (Bit 0) IS set (1)
if ((AF.Low & 0x01) != 0)
{
PC = Pop();
return 11; // Condition met, took the return
}
return 5;
case 0xD9: // EXX case 0xD9: // EXX
ushort tempBC = BC.Word; ushort tempBC = BC.Word;
BC.Word = BC_Prime.Word; BC.Word = BC_Prime.Word;
@@ -826,6 +891,20 @@ namespace Core.Cpu
case 0xE1: // POP HL case 0xE1: // POP HL
HL.Word = Pop(); HL.Word = Pop();
return 10; return 10;
case 0xE3: // EX (SP), HL
// 1. Read the 16-bit value currently on top of the stack
byte spLow = _memory.Read(SP);
byte spHigh = _memory.Read((ushort)(SP + 1));
// 2. Write the current HL registers onto the stack in its place
_memory.Write(SP, HL.Low);
_memory.Write((ushort)(SP + 1), HL.High);
// 3. Update HL with the data we pulled off the stack
HL.Low = spLow;
HL.High = spHigh;
return 19;
case 0xe5: //push bc case 0xe5: //push bc
Push(HL.Word); Push(HL.Word);
return 11; return 11;
@@ -852,6 +931,9 @@ namespace Core.Cpu
case 0xf5: //push bc case 0xf5: //push bc
Push(AF.Word); Push(AF.Word);
return 11; return 11;
case 0xF6: // OR n
Or(FetchByte());
return 7;
case 0xF9: // LD SP, HL case 0xF9: // LD SP, HL
SP = HL.Word; // (Use SP.Word = HL.Word if you made SP a RegisterPair) SP = HL.Word; // (Use SP.Word = HL.Word if you made SP a RegisterPair)
return 6; return 6;
@@ -861,6 +943,9 @@ namespace Core.Cpu
return 4; return 4;
case 0xFD: case 0xFD:
return ExecuteFDPrefix(); return ExecuteFDPrefix();
case 0xFE: // CP n
Cp(FetchByte());
return 7;
default: default:
throw new NotImplementedException($"Opcode 0x{opcode:X2} at PC 0x{(PC - 1):X4} is not implemented."); throw new NotImplementedException($"Opcode 0x{opcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
} }
@@ -881,6 +966,11 @@ namespace Core.Cpu
case 0x47: // LD I, A case 0x47: // LD I, A
I = AF.High; I = AF.High;
return 9; return 9;
case 0x4B: // LD BC, (nn)
ushort src4B = FetchWord();
BC.Low = _memory.Read(src4B);
BC.High = _memory.Read((ushort)(src4B + 1));
return 20; // Takes 20 T-States
case 0x52: // SBC HL, DE case 0x52: // SBC HL, DE
Sbc16(DE.Word); Sbc16(DE.Word);
return 15; return 15;
@@ -952,6 +1042,63 @@ namespace Core.Cpu
} }
} }
private int ExecuteCBPrefix()
{
byte cbOpcode = FetchByte();
byte memVal;
switch (cbOpcode)
{
case 0x7E: // BIT 7, (HL)
byte memValBit7 = _memory.Read(HL.Word);
// Preserve ONLY the Carry Flag (Bit 0). This clears N and everything else.
AF.Low &= 0x01;
// Half-Carry (Bit 4) is ALWAYS set to 1 for Z80 BIT instructions
AF.Low |= 0x10;
// Test Bit 7 (0x80 is Binary 1000 0000)
if ((memValBit7 & 0x80) == 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;
}
else
{
// If testing Bit 7 and it is 1, the Sign Flag (Bit 7) perfectly mirrors it
AF.Low |= 0x80;
}
return 12;
case 0xAE: // RES 5, (HL)
memVal = _memory.Read(HL.Word);
// 0xDF is Binary 1101 1111
// ANDing preserves all other bits while forcing Bit 5 to 0
memVal &= 0xDF;
_memory.Write(HL.Word, memVal);
return 15;
case 0xC6: // SET 0, (HL)
memVal = _memory.Read(HL.Word);
// 0x01 is Binary 0000 0001
// ORing forces Bit 0 to 1 and perfectly preserves all other bits
memVal |= 0x01;
_memory.Write(HL.Word, memVal);
return 15;
default:
throw new NotImplementedException($"CB prefix opcode 0x{cbOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
}
}
private int ExecuteFDPrefix() private int ExecuteFDPrefix()
{ {
byte opcode = FetchByte(); byte opcode = FetchByte();
@@ -981,6 +1128,14 @@ namespace Core.Cpu
_memory.Write(targetAddress, nValue); _memory.Write(targetAddress, nValue);
return 19; // Takes 19 T-States return 19; // Takes 19 T-States
} }
case 0x46: // LD B, (IY+d)
{
sbyte displacement = (sbyte)FetchByte();
targetAddress = (ushort)(IY.Word + displacement);
BC.High = _memory.Read(targetAddress);
return 19; // Takes 19 T-States
}
case 0x6E: // LD L, (IY+d) case 0x6E: // LD L, (IY+d)
sbyte displacementVal = (sbyte)FetchByte(); sbyte displacementVal = (sbyte)FetchByte();
ushort targetAddr = (ushort)(IY.Word + displacementVal); ushort targetAddr = (ushort)(IY.Word + displacementVal);

View File

@@ -264,14 +264,43 @@ namespace Desktop
// --- 16-Bit Decrements --- // --- 16-Bit Decrements ---
case 0x0B: mnemonic = "DEC BC"; break; case 0x0B: mnemonic = "DEC BC"; break;
case 0x3B: mnemonic = "DEC SP"; break; case 0x3B: mnemonic = "DEC SP"; break;
case 0x04: // --- 8-Bit Increments ---
mnemonic = "INC B"; case 0x04: mnemonic = "INC B"; break;
case 0x0C: mnemonic = "INC C"; break;
case 0x14: mnemonic = "INC D"; break;
case 0x1C: mnemonic = "INC E"; break;
case 0x24: mnemonic = "INC H"; break;
case 0x2C: mnemonic = "INC L"; break;
case 0x34: mnemonic = "INC (HL)"; break;
case 0x3C: mnemonic = "INC A"; break;
// --- 8-Bit Decrements ---
case 0x05: mnemonic = "DEC B"; break;
case 0x0D: mnemonic = "DEC C"; break;
case 0x15: mnemonic = "DEC D"; break;
case 0x1D: mnemonic = "DEC E"; break;
case 0x25: mnemonic = "DEC H"; break;
case 0x2D: mnemonic = "DEC L"; break;
case 0x35: mnemonic = "DEC (HL)"; break;
case 0x3D: mnemonic = "DEC A"; break;
case 0x06:
byte bImm = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"LD B, 0x{bImm:X2}";
instructionLength = 2;
break; break;
// --- ADD HL, rr ---
case 0x09: mnemonic = "ADD HL, BC"; break;
case 0x19: mnemonic = "ADD HL, DE"; break;
case 0x29: mnemonic = "ADD HL, HL"; break;
case 0x39: mnemonic = "ADD HL, SP"; break;
case 0x0E: case 0x0E:
byte cImm = _memoryBus.Read((ushort)(currentPc + 1)); byte cImm = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"LD C, 0x{cImm:X2}"; mnemonic = $"LD C, 0x{cImm:X2}";
instructionLength = 2; instructionLength = 2;
break; break;
case 0x0F:
mnemonic = "RRCA";
break;
case 0x10: case 0x10:
sbyte djnzOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1)); sbyte djnzOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
ushort djnzDest = (ushort)(currentPc + 2 + djnzOffset); ushort djnzDest = (ushort)(currentPc + 2 + djnzOffset);
@@ -298,8 +327,8 @@ namespace Desktop
mnemonic = $"JR 0x{targetAddressUnconditional:X4}"; mnemonic = $"JR 0x{targetAddressUnconditional:X4}";
instructionLength = 2; instructionLength = 2;
break; break;
case 0x19: case 0x1A:
mnemonic = "ADD HL, DE"; mnemonic = "LD A, (DE)";
break; break;
case 0x1B: case 0x1B:
mnemonic = "DEC DE"; mnemonic = "DEC DE";
@@ -359,9 +388,6 @@ namespace Desktop
instructionLength = 3; instructionLength = 3;
break; break;
} }
case 0x35:
mnemonic = "DEC (HL)";
break;
case 0x36: case 0x36:
byte memValue = _memoryBus.Read((ushort)(currentPc + 1)); byte memValue = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"LD (HL), 0x{memValue:X2}"; mnemonic = $"LD (HL), 0x{memValue:X2}";
@@ -377,6 +403,11 @@ namespace Desktop
mnemonic = $"JR C, 0x{targetC:X4}"; mnemonic = $"JR C, 0x{targetC:X4}";
instructionLength = 2; instructionLength = 2;
break; break;
case 0x3A:
ushort addr3A = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
mnemonic = $"LD A, (0x{addr3A:X4})";
instructionLength = 3;
break;
case 0x3E: case 0x3E:
mnemonic = $"LD A, 0x{_memoryBus.Read((ushort)(currentPc + 1)):X2}"; mnemonic = $"LD A, 0x{_memoryBus.Read((ushort)(currentPc + 1)):X2}";
instructionLength = 2; instructionLength = 2;
@@ -540,12 +571,42 @@ namespace Desktop
instructionLength = 2; instructionLength = 2;
break; break;
} }
// --- RST Instructions ---
case 0xC7: mnemonic = "RST 00h"; break;
case 0xCF: mnemonic = "RST 08h"; break;
case 0xD7: mnemonic = "RST 10h"; break;
case 0xDF: mnemonic = "RST 18h"; break;
case 0xE7: mnemonic = "RST 20h"; break;
case 0xEF: mnemonic = "RST 28h"; break;
case 0xF7: mnemonic = "RST 30h"; break;
case 0xFF: mnemonic = "RST 38h"; break;
case 0xC8: case 0xC8:
mnemonic = "RET Z"; mnemonic = "RET Z";
break; break;
case 0xC9: case 0xC9:
mnemonic = "RET"; mnemonic = "RET";
break; break;
case 0xCB:
byte cbOp = _memoryBus.Read((ushort)(currentPc + 1));
if (cbOp == 0x7E)
{
mnemonic = "BIT 7, (HL)";
}
else if (cbOp == 0xAE)
{
mnemonic = "RES 5, (HL)";
}
else if (cbOp == 0xC6)
{
mnemonic = "SET 0, (HL)";
}
else
{
mnemonic = $"CB UNKNOWN (0x{cbOp:X2})";
}
instructionLength = 2;
break;
case 0xCD: case 0xCD:
ushort callDest = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8)); ushort callDest = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
mnemonic = $"CALL 0x{callDest:X4}"; mnemonic = $"CALL 0x{callDest:X4}";
@@ -572,12 +633,18 @@ namespace Desktop
mnemonic = $"SUB 0x{subImm:X2}"; mnemonic = $"SUB 0x{subImm:X2}";
instructionLength = 2; instructionLength = 2;
break; break;
case 0xD8:
mnemonic = "RET C";
break;
case 0xDE: case 0xDE:
byte sbcValue = _memoryBus.Read((ushort)(currentPc + 1)); byte sbcValue = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"SBC A, 0x{sbcValue:X2}"; mnemonic = $"SBC A, 0x{sbcValue:X2}";
instructionLength = 2; instructionLength = 2;
break; break;
case 0xE1: mnemonic = "POP HL"; break; case 0xE1: mnemonic = "POP HL"; break;
case 0xE3:
mnemonic = "EX (SP), HL";
break;
case 0xE5: case 0xE5:
mnemonic = "PUSH HL"; mnemonic = "PUSH HL";
break; break;
@@ -606,6 +673,11 @@ namespace Desktop
mnemonic = "LD I, A"; mnemonic = "LD I, A";
instructionLength = 2; // 0xED + 0x47 instructionLength = 2; // 0xED + 0x47
break; break;
case 0x4B:
ushort addr4B = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
mnemonic = $"LD BC, (0x{addr4B:X4})";
instructionLength = 4;
break;
case 0x52: case 0x52:
mnemonic = "SBC HL, DE"; mnemonic = "SBC HL, DE";
instructionLength = 2; // ED 52 instructionLength = 2; // ED 52
@@ -638,10 +710,14 @@ namespace Desktop
case 0xF3: case 0xF3:
mnemonic = "DI"; mnemonic = "DI";
break; break;
break;
case 0xf5: case 0xf5:
mnemonic = "PUSH AF"; mnemonic = "PUSH AF";
break; break;
case 0xF6:
byte orImm = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"OR 0x{orImm:X2}";
instructionLength = 2;
break;
case 0xF9: case 0xF9:
mnemonic = "LD SP, HL"; mnemonic = "LD SP, HL";
break; break;
@@ -673,6 +749,14 @@ namespace Desktop
mnemonic = $"LD (IY{sign}{d}), 0x{n:X2}"; mnemonic = $"LD (IY{sign}{d}), 0x{n:X2}";
instructionLength = 4; instructionLength = 4;
} }
else if (fdOpcode == 0x46) // LD B, (IY+d)
{
sbyte dB = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
string signB = dB >= 0 ? "+" : "";
mnemonic = $"LD B, (IY{signB}{dB})";
instructionLength = 3;
}
else if (fdOpcode == 0x6E) else if (fdOpcode == 0x6E)
{ {
sbyte offsetL = (sbyte)_memoryBus.Read((ushort)(currentPc + 2)); sbyte offsetL = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
@@ -772,6 +856,11 @@ namespace Desktop
} }
break; break;
} }
case 0xFE:
byte cpImm = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"CP 0x{cpImm:X2}";
instructionLength = 2;
break;
default: default:
mnemonic = $"UNKNOWN (0x{opcode:X2})"; mnemonic = $"UNKNOWN (0x{opcode:X2})";
break; break;