Implemented a shit load more OpCodes
This commit is contained in:
191
Core/Cpu/Z80.cs
191
Core/Cpu/Z80.cs
@@ -474,15 +474,67 @@ namespace Core.Cpu
|
||||
case 0x03: // INC BC
|
||||
BC.Word++;
|
||||
return 6;
|
||||
case 0x04: // INC B
|
||||
BC.High = Inc8(BC.High);
|
||||
return 4;
|
||||
// --- 8-Bit Increments ---
|
||||
case 0x04: BC.High = Inc8(BC.High); return 4; // INC B
|
||||
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
|
||||
BC.Word--;
|
||||
return 6;
|
||||
case 0x0E: // LD C, n
|
||||
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
|
||||
sbyte djnzOffset = (sbyte)FetchByte();
|
||||
|
||||
@@ -512,9 +564,9 @@ namespace Core.Cpu
|
||||
PC = (ushort)(PC + jumpDistance);
|
||||
|
||||
return 12;
|
||||
case 0x19: // ADD HL, DE
|
||||
Add16(DE.Word);
|
||||
return 11;
|
||||
case 0x1A: // LD A, (DE)
|
||||
AF.High = _memory.Read(DE.Word);
|
||||
return 7;
|
||||
case 0x1B: // DEC DE
|
||||
DE.Word--;
|
||||
return 6;
|
||||
@@ -579,17 +631,6 @@ namespace Core.Cpu
|
||||
case 0x33: // INC SP
|
||||
SP++;
|
||||
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
|
||||
byte nValue = FetchByte();
|
||||
_memory.Write(HL.Word, nValue);
|
||||
@@ -607,6 +648,10 @@ namespace Core.Cpu
|
||||
return 12;
|
||||
}
|
||||
return 7;
|
||||
case 0x3A: // LD A, (nn)
|
||||
ushort address3A = FetchWord();
|
||||
AF.High = _memory.Read(address3A);
|
||||
return 13;
|
||||
case 0x3B: // DEC SP
|
||||
SP--;
|
||||
return 6;
|
||||
@@ -764,6 +809,16 @@ namespace Core.Cpu
|
||||
case 0xC6: // ADD A, n
|
||||
Add(FetchByte());
|
||||
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
|
||||
// Check if the Zero Flag (Bit 6) IS set
|
||||
if ((AF.Low & 0x40) != 0)
|
||||
@@ -775,6 +830,8 @@ namespace Core.Cpu
|
||||
case 0xC9: // RET
|
||||
PC = Pop();
|
||||
return 10;
|
||||
case 0xCB:
|
||||
return ExecuteCBPrefix();
|
||||
case 0xCD: // CALL nn
|
||||
ushort callAddress = FetchWord();
|
||||
Push(PC);
|
||||
@@ -806,6 +863,14 @@ namespace Core.Cpu
|
||||
case 0xD6: // SUB n
|
||||
Sub(FetchByte());
|
||||
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
|
||||
ushort tempBC = BC.Word;
|
||||
BC.Word = BC_Prime.Word;
|
||||
@@ -826,6 +891,20 @@ namespace Core.Cpu
|
||||
case 0xE1: // POP HL
|
||||
HL.Word = Pop();
|
||||
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
|
||||
Push(HL.Word);
|
||||
return 11;
|
||||
@@ -852,6 +931,9 @@ namespace Core.Cpu
|
||||
case 0xf5: //push bc
|
||||
Push(AF.Word);
|
||||
return 11;
|
||||
case 0xF6: // OR n
|
||||
Or(FetchByte());
|
||||
return 7;
|
||||
case 0xF9: // LD SP, HL
|
||||
SP = HL.Word; // (Use SP.Word = HL.Word if you made SP a RegisterPair)
|
||||
return 6;
|
||||
@@ -861,6 +943,9 @@ namespace Core.Cpu
|
||||
return 4;
|
||||
case 0xFD:
|
||||
return ExecuteFDPrefix();
|
||||
case 0xFE: // CP n
|
||||
Cp(FetchByte());
|
||||
return 7;
|
||||
default:
|
||||
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
|
||||
I = AF.High;
|
||||
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
|
||||
Sbc16(DE.Word);
|
||||
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()
|
||||
{
|
||||
byte opcode = FetchByte();
|
||||
@@ -981,6 +1128,14 @@ namespace Core.Cpu
|
||||
_memory.Write(targetAddress, nValue);
|
||||
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)
|
||||
sbyte displacementVal = (sbyte)FetchByte();
|
||||
ushort targetAddr = (ushort)(IY.Word + displacementVal);
|
||||
|
||||
@@ -264,14 +264,43 @@ namespace Desktop
|
||||
// --- 16-Bit Decrements ---
|
||||
case 0x0B: mnemonic = "DEC BC"; break;
|
||||
case 0x3B: mnemonic = "DEC SP"; break;
|
||||
case 0x04:
|
||||
mnemonic = "INC B";
|
||||
// --- 8-Bit Increments ---
|
||||
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;
|
||||
// --- 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:
|
||||
byte cImm = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
mnemonic = $"LD C, 0x{cImm:X2}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0x0F:
|
||||
mnemonic = "RRCA";
|
||||
break;
|
||||
case 0x10:
|
||||
sbyte djnzOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
|
||||
ushort djnzDest = (ushort)(currentPc + 2 + djnzOffset);
|
||||
@@ -298,8 +327,8 @@ namespace Desktop
|
||||
mnemonic = $"JR 0x{targetAddressUnconditional:X4}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0x19:
|
||||
mnemonic = "ADD HL, DE";
|
||||
case 0x1A:
|
||||
mnemonic = "LD A, (DE)";
|
||||
break;
|
||||
case 0x1B:
|
||||
mnemonic = "DEC DE";
|
||||
@@ -359,9 +388,6 @@ namespace Desktop
|
||||
instructionLength = 3;
|
||||
break;
|
||||
}
|
||||
case 0x35:
|
||||
mnemonic = "DEC (HL)";
|
||||
break;
|
||||
case 0x36:
|
||||
byte memValue = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
mnemonic = $"LD (HL), 0x{memValue:X2}";
|
||||
@@ -377,6 +403,11 @@ namespace Desktop
|
||||
mnemonic = $"JR C, 0x{targetC:X4}";
|
||||
instructionLength = 2;
|
||||
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:
|
||||
mnemonic = $"LD A, 0x{_memoryBus.Read((ushort)(currentPc + 1)):X2}";
|
||||
instructionLength = 2;
|
||||
@@ -540,12 +571,42 @@ namespace Desktop
|
||||
instructionLength = 2;
|
||||
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:
|
||||
mnemonic = "RET Z";
|
||||
break;
|
||||
case 0xC9:
|
||||
mnemonic = "RET";
|
||||
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:
|
||||
ushort callDest = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
|
||||
mnemonic = $"CALL 0x{callDest:X4}";
|
||||
@@ -572,12 +633,18 @@ namespace Desktop
|
||||
mnemonic = $"SUB 0x{subImm:X2}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xD8:
|
||||
mnemonic = "RET C";
|
||||
break;
|
||||
case 0xDE:
|
||||
byte sbcValue = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
mnemonic = $"SBC A, 0x{sbcValue:X2}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xE1: mnemonic = "POP HL"; break;
|
||||
case 0xE3:
|
||||
mnemonic = "EX (SP), HL";
|
||||
break;
|
||||
case 0xE5:
|
||||
mnemonic = "PUSH HL";
|
||||
break;
|
||||
@@ -606,6 +673,11 @@ namespace Desktop
|
||||
mnemonic = "LD I, A";
|
||||
instructionLength = 2; // 0xED + 0x47
|
||||
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:
|
||||
mnemonic = "SBC HL, DE";
|
||||
instructionLength = 2; // ED 52
|
||||
@@ -638,10 +710,14 @@ namespace Desktop
|
||||
case 0xF3:
|
||||
mnemonic = "DI";
|
||||
break;
|
||||
break;
|
||||
case 0xf5:
|
||||
mnemonic = "PUSH AF";
|
||||
break;
|
||||
case 0xF6:
|
||||
byte orImm = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
mnemonic = $"OR 0x{orImm:X2}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xF9:
|
||||
mnemonic = "LD SP, HL";
|
||||
break;
|
||||
@@ -673,6 +749,14 @@ namespace Desktop
|
||||
mnemonic = $"LD (IY{sign}{d}), 0x{n:X2}";
|
||||
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)
|
||||
{
|
||||
sbyte offsetL = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
@@ -772,6 +856,11 @@ namespace Desktop
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0xFE:
|
||||
byte cpImm = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
mnemonic = $"CP 0x{cpImm:X2}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
default:
|
||||
mnemonic = $"UNKNOWN (0x{opcode:X2})";
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user