Implemented many more OpCodes - 11_04_2026__02_23
This commit is contained in:
118
Core/Cpu/Z80.cs
118
Core/Cpu/Z80.cs
@@ -8,6 +8,8 @@ namespace Core.Cpu
|
||||
//T-State counter
|
||||
public long TotalTStates { get; set; }
|
||||
|
||||
public int InterruptMode { get; private set; } = 0; // Defaults to 0 on power-up
|
||||
|
||||
// Interrupt Flip-Flops
|
||||
public bool IFF1;
|
||||
public bool IFF2;
|
||||
@@ -191,6 +193,36 @@ namespace Core.Cpu
|
||||
return result;
|
||||
}
|
||||
|
||||
private byte Inc8(byte value)
|
||||
{
|
||||
byte result = (byte)(value + 1);
|
||||
|
||||
// Store the existing Carry flag so we can preserve it
|
||||
byte carry = (byte)(AF.Low & 0x01);
|
||||
|
||||
// Clear all flags
|
||||
AF.Low = 0;
|
||||
|
||||
// Sign Flag (Bit 7)
|
||||
if ((result & 0x80) != 0) AF.Low |= 0x80;
|
||||
|
||||
// Zero Flag (Bit 6)
|
||||
if (result == 0) AF.Low |= 0x40;
|
||||
|
||||
// Half-Carry Flag (Bit 4) - Set if carry from bit 3 (happens if lower nibble was 0x0F)
|
||||
if ((value & 0x0F) == 0x0F) AF.Low |= 0x10;
|
||||
|
||||
// Parity/Overflow Flag (Bit 2) - Set if the original value was 0x7F (maximum positive)
|
||||
if (value == 0x7F) AF.Low |= 0x04;
|
||||
|
||||
// Subtract Flag (Bit 1) - ALWAYS 0 for increments (already 0 because we cleared AF.Low)
|
||||
|
||||
// Restore the original Carry Flag (Bit 0)
|
||||
AF.Low |= carry;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void Cp(byte value)
|
||||
{
|
||||
byte a = AF.High;
|
||||
@@ -247,10 +279,6 @@ namespace Core.Cpu
|
||||
|
||||
// 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
|
||||
@@ -277,6 +305,12 @@ namespace Core.Cpu
|
||||
{
|
||||
case 0x00: // NOP
|
||||
return 4;
|
||||
case 0x01: // LD BC, nn
|
||||
BC.Word = FetchWord();
|
||||
return 10; // Takes 10 T-States
|
||||
case 0x04: // INC B
|
||||
BC.High = Inc8(BC.High);
|
||||
return 4;
|
||||
case 0x11: //LD DE, nn
|
||||
DE.Word = FetchWord();
|
||||
return 10;
|
||||
@@ -291,6 +325,14 @@ namespace Core.Cpu
|
||||
return 12;
|
||||
}
|
||||
return 7;
|
||||
case 0x21: // LD HL, nn
|
||||
HL.Word = FetchWord();
|
||||
return 10;
|
||||
case 0x22: // LD (nn), HL
|
||||
ushort dest22 = FetchWord();
|
||||
_memory.Write(dest22, HL.Low);
|
||||
_memory.Write((ushort)(dest22 + 1), HL.High);
|
||||
return 16;
|
||||
case 0x23: // INC HL
|
||||
HL.Word++;
|
||||
return 6;
|
||||
@@ -304,6 +346,13 @@ namespace Core.Cpu
|
||||
return 12; // Jump taken
|
||||
}
|
||||
return 7; // Jump not taken
|
||||
case 0x2A: // LD HL, (nn)
|
||||
{
|
||||
ushort srcAddress = FetchWord();
|
||||
HL.Low = _memory.Read(srcAddress);
|
||||
HL.High = _memory.Read((ushort)(srcAddress + 1));
|
||||
return 16; // Takes 16 T-States
|
||||
}
|
||||
case 0x2B: // DEC HL
|
||||
HL.Word--;
|
||||
return 6;
|
||||
@@ -338,6 +387,7 @@ namespace Core.Cpu
|
||||
case 0x47: // LD B, A
|
||||
BC.High = AF.High;
|
||||
return 4;
|
||||
|
||||
case 0x62: // LD H, D
|
||||
HL.High = DE.High;
|
||||
return 4;
|
||||
@@ -347,6 +397,10 @@ namespace Core.Cpu
|
||||
case 0xA7: // AND A
|
||||
And(AF.High);
|
||||
return 4;
|
||||
case 0xAF: // XOR A
|
||||
AF.High = 0;
|
||||
AF.Low = 0x44;
|
||||
return 4;
|
||||
case 0xBC: // CP H
|
||||
Cp(HL.High);
|
||||
return 4;
|
||||
@@ -379,17 +433,20 @@ namespace Core.Cpu
|
||||
case 0xDE: // SBC A, n
|
||||
Sbc(FetchByte());
|
||||
return 7;
|
||||
case 0xEB: // EX DE, HL
|
||||
ushort tempEx = DE.Word;
|
||||
DE.Word = HL.Word;
|
||||
HL.Word = tempEx;
|
||||
return 4; // Takes 4 T-States
|
||||
case 0xED:
|
||||
return ExecuteExtendedPrefix();
|
||||
case 0xF3: // DI (Disable Interrupts)
|
||||
IFF1 = false;
|
||||
IFF2 = false;
|
||||
return 4;
|
||||
case 0xAF: // XOR A
|
||||
AF.High = 0;
|
||||
AF.Low = 0x44;
|
||||
return 4;
|
||||
|
||||
case 0xF9: // LD SP, HL
|
||||
SP = HL.Word; // (Use SP.Word = HL.Word if you made SP a RegisterPair)
|
||||
return 6;
|
||||
default:
|
||||
throw new NotImplementedException($"Opcode 0x{opcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
|
||||
}
|
||||
@@ -401,12 +458,53 @@ namespace Core.Cpu
|
||||
|
||||
switch (extendedOpcode)
|
||||
{
|
||||
case 0x43: // LD (nn), BC
|
||||
ushort dest43 = FetchWord();
|
||||
_memory.Write(dest43, BC.Low);
|
||||
_memory.Write((ushort)(dest43 + 1), BC.High);
|
||||
return 20;
|
||||
case 0x47: // LD I, A
|
||||
I = AF.High;
|
||||
return 9;
|
||||
case 0x52: // SBC HL, DE
|
||||
Sbc16(DE.Word);
|
||||
return 15; // Takes 15 T-States
|
||||
return 15;
|
||||
case 0x53: // LD (nn), DE
|
||||
ushort dest53 = FetchWord();
|
||||
_memory.Write(dest53, DE.Low);
|
||||
_memory.Write((ushort)(dest53 + 1), DE.High);
|
||||
return 20;
|
||||
case 0x56: // IM 1
|
||||
InterruptMode = 1;
|
||||
return 8; // Takes 8 T-States
|
||||
case 0xB8: // LDDR
|
||||
// 1. Read byte from (HL)
|
||||
byte val = _memory.Read(HL.Word);
|
||||
|
||||
// 2. Write byte to (DE)
|
||||
_memory.Write(DE.Word, val);
|
||||
|
||||
// 3. Decrement all three pointers
|
||||
HL.Word--;
|
||||
DE.Word--;
|
||||
BC.Word--;
|
||||
|
||||
// 4. Update Flags
|
||||
// Preserve S (0x80), Z (0x40), and C (0x01).
|
||||
// H (0x10) and N (0x02) are always reset to 0.
|
||||
AF.Low &= 0xC1;
|
||||
|
||||
// P/V Flag (Bit 2) is set to 1 if BC is not 0
|
||||
if (BC.Word != 0)
|
||||
{
|
||||
AF.Low |= 0x04;
|
||||
|
||||
// Rewind the PC so the CPU executes this instruction again!
|
||||
PC -= 2;
|
||||
return 21; // Looping
|
||||
}
|
||||
|
||||
return 16; // Finished!
|
||||
default:
|
||||
throw new NotImplementedException($"Extended ED Opcode 0x{extendedOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
|
||||
}
|
||||
|
||||
@@ -222,6 +222,14 @@ namespace Desktop
|
||||
switch (opcode)
|
||||
{
|
||||
case 0x00: mnemonic = "NOP"; break;
|
||||
case 0x01:
|
||||
ushort bcVal = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
|
||||
mnemonic = $"LD BC, 0x{bcVal:X4}";
|
||||
instructionLength = 3;
|
||||
break;
|
||||
case 0x04:
|
||||
mnemonic = "INC B";
|
||||
break;
|
||||
case 0x11:
|
||||
// LD DE, nn
|
||||
byte deLow = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
@@ -238,6 +246,18 @@ namespace Desktop
|
||||
mnemonic = $"JR NZ, 0x{destination:X4}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0x21:
|
||||
{
|
||||
ushort hlImm = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
|
||||
mnemonic = $"LD HL, 0x{hlImm:X4}";
|
||||
instructionLength = 3;
|
||||
break;
|
||||
}
|
||||
case 0x22:
|
||||
ushort hlAddr = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
|
||||
mnemonic = $"LD (0x{hlAddr:X4}), HL";
|
||||
instructionLength = 3;
|
||||
break;
|
||||
case 0x23:
|
||||
mnemonic = "INC HL";
|
||||
break;
|
||||
@@ -247,6 +267,13 @@ namespace Desktop
|
||||
mnemonic = $"JR Z, 0x{jrZDest:X4}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0x2A:
|
||||
{
|
||||
ushort addr2A = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
|
||||
mnemonic = $"LD HL, (0x{addr2A:X4})";
|
||||
instructionLength = 3;
|
||||
break;
|
||||
}
|
||||
case 0x2B:
|
||||
mnemonic = "DEC HL";
|
||||
break;
|
||||
@@ -306,12 +333,19 @@ namespace Desktop
|
||||
mnemonic = $"SBC A, 0x{sbcValue:X2}";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xEB:
|
||||
mnemonic = "EX DE, HL";
|
||||
break;
|
||||
case 0xED:
|
||||
byte extendedOp = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
|
||||
switch (extendedOp)
|
||||
{
|
||||
// Example: ED 47 is LD I, A
|
||||
case 0x43:
|
||||
ushort bcAddr = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||
mnemonic = $"LD (0x{bcAddr:X4}), BC";
|
||||
instructionLength = 4;
|
||||
break;
|
||||
case 0x47:
|
||||
mnemonic = "LD I, A";
|
||||
instructionLength = 2; // 0xED + 0x47
|
||||
@@ -320,11 +354,23 @@ namespace Desktop
|
||||
mnemonic = "SBC HL, DE";
|
||||
instructionLength = 2; // ED 52
|
||||
break;
|
||||
// Example: ED B0 is LDIR (a massive block copy instruction)
|
||||
case 0x53:
|
||||
ushort deAddr = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||
mnemonic = $"LD (0x{deAddr:X4}), DE";
|
||||
instructionLength = 4;
|
||||
break;
|
||||
case 0x56:
|
||||
mnemonic = "IM 1";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xB0:
|
||||
mnemonic = "LDIR";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xB8:
|
||||
mnemonic = "LDDR";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
mnemonic = $"EXT UNKNOWN (ED {extendedOp:X2})";
|
||||
@@ -335,6 +381,9 @@ namespace Desktop
|
||||
case 0xF3:
|
||||
mnemonic = "DI";
|
||||
break;
|
||||
case 0xF9:
|
||||
mnemonic = "LD SP, HL";
|
||||
break;
|
||||
default:
|
||||
mnemonic = $"UNKNOWN (0x{opcode:X2})";
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user