Almost runs Chuckie Egg. LDI to implement next
This commit is contained in:
116
Core/Cpu/Z80.cs
116
Core/Cpu/Z80.cs
@@ -14,6 +14,7 @@ namespace Core.Cpu
|
|||||||
// Interrupt Flip-Flops
|
// Interrupt Flip-Flops
|
||||||
public bool IFF1 { get; private set; } = false;
|
public bool IFF1 { get; private set; } = false;
|
||||||
public bool IFF2 { get; private set; } = false;
|
public bool IFF2 { get; private set; } = false;
|
||||||
|
public bool InterruptRequested { get; private set; } = false;
|
||||||
|
|
||||||
// Main Register Set
|
// Main Register Set
|
||||||
public RegisterPair AF;
|
public RegisterPair AF;
|
||||||
@@ -85,6 +86,7 @@ namespace Core.Cpu
|
|||||||
|
|
||||||
public int RequestInterrupt()
|
public int RequestInterrupt()
|
||||||
{
|
{
|
||||||
|
InterruptRequested = true;
|
||||||
// 1. If the ROM has disabled interrupts (DI), ignore the request
|
// 1. If the ROM has disabled interrupts (DI), ignore the request
|
||||||
if (!IFF1) return 0;
|
if (!IFF1) return 0;
|
||||||
|
|
||||||
@@ -887,6 +889,53 @@ namespace Core.Cpu
|
|||||||
case 0x26: // LD H, n
|
case 0x26: // LD H, n
|
||||||
HL.High = FetchByte();
|
HL.High = FetchByte();
|
||||||
return 7;
|
return 7;
|
||||||
|
case 0x27: // DAA
|
||||||
|
byte a = AF.High;
|
||||||
|
int correction = 0;
|
||||||
|
byte flags = AF.Low;
|
||||||
|
|
||||||
|
bool carry = (flags & 0x01) != 0;
|
||||||
|
bool halfCarry = (flags & 0x10) != 0;
|
||||||
|
bool isSub = (flags & 0x02) != 0; // The N flag tells us if we should add or subtract!
|
||||||
|
|
||||||
|
// 1. Check if the lower nibble needs adjustment
|
||||||
|
if (halfCarry || (a & 0x0F) > 9)
|
||||||
|
{
|
||||||
|
correction |= 0x06;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Check if the upper nibble needs adjustment
|
||||||
|
if (carry || a > 0x99 || (a >= 0x90 && (a & 0x0F) > 9))
|
||||||
|
{
|
||||||
|
correction |= 0x60;
|
||||||
|
carry = true; // The final carry flag will be true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Apply the correction and calculate the new Half-Carry
|
||||||
|
bool newHalfCarry = false;
|
||||||
|
if (isSub)
|
||||||
|
{
|
||||||
|
newHalfCarry = halfCarry && (a & 0x0F) < 0x06;
|
||||||
|
a = (byte)(a - correction);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newHalfCarry = ((a & 0x0F) + (correction & 0x0F)) > 0x0F;
|
||||||
|
a = (byte)(a + correction);
|
||||||
|
}
|
||||||
|
|
||||||
|
AF.High = a;
|
||||||
|
|
||||||
|
// 4. Build the new flags
|
||||||
|
flags &= 0x02; // Wipe everything except the N flag (which is strictly preserved)
|
||||||
|
if (carry) flags |= 0x01;
|
||||||
|
if (newHalfCarry) flags |= 0x10;
|
||||||
|
if ((a & 0x80) != 0) flags |= 0x80; // S flag
|
||||||
|
if (a == 0) flags |= 0x40; // Z flag
|
||||||
|
if (CalculateParity(a)) flags |= 0x04; // P/V flag
|
||||||
|
|
||||||
|
AF.Low = flags;
|
||||||
|
return 4;
|
||||||
case 0x28: // JR Z, e
|
case 0x28: // JR Z, e
|
||||||
offset = (sbyte)FetchByte();
|
offset = (sbyte)FetchByte();
|
||||||
// Check if the Zero Flag is set
|
// Check if the Zero Flag is set
|
||||||
@@ -1034,6 +1083,17 @@ namespace Core.Cpu
|
|||||||
case 0x73: _memory.Write(HL.Word, DE.Low); return 7;
|
case 0x73: _memory.Write(HL.Word, DE.Low); return 7;
|
||||||
case 0x74: _memory.Write(HL.Word, HL.High); return 7;
|
case 0x74: _memory.Write(HL.Word, HL.High); return 7;
|
||||||
case 0x75: _memory.Write(HL.Word, HL.Low); return 7;
|
case 0x75: _memory.Write(HL.Word, HL.Low); return 7;
|
||||||
|
case 0x76: //HALT
|
||||||
|
if (!InterruptRequested)
|
||||||
|
{
|
||||||
|
PC--;
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InterruptRequested = false;
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
case 0x77: _memory.Write(HL.Word, AF.High); return 7;
|
case 0x77: _memory.Write(HL.Word, AF.High); return 7;
|
||||||
|
|
||||||
// --- LD A, r ---
|
// --- LD A, r ---
|
||||||
@@ -1501,6 +1561,27 @@ namespace Core.Cpu
|
|||||||
DE.Low = _memory.Read(src5B);
|
DE.Low = _memory.Read(src5B);
|
||||||
DE.High = _memory.Read((ushort)(src5B + 1));
|
DE.High = _memory.Read((ushort)(src5B + 1));
|
||||||
return 20;
|
return 20;
|
||||||
|
case 0x5F: // LD A, R
|
||||||
|
// 1. Load the Refresh register into the Accumulator
|
||||||
|
AF.High = R;
|
||||||
|
|
||||||
|
// 2. Calculate Flags
|
||||||
|
// CRITICAL: Preserve the existing Carry flag (Bit 0).
|
||||||
|
// H (Bit 4) and N (Bit 1) are forcefully reset to 0.
|
||||||
|
newFlags = (byte)(AF.Low & 0x01);
|
||||||
|
|
||||||
|
// S Flag (Bit 7): Set if the result is negative
|
||||||
|
if ((AF.High & 0x80) != 0) newFlags |= 0x80;
|
||||||
|
|
||||||
|
// Z Flag (Bit 6): Set if the result is zero
|
||||||
|
if (AF.High == 0) newFlags |= 0x40;
|
||||||
|
|
||||||
|
// P/V Flag (Bit 2): Set if IFF2 is true (This is the interrupt check hack!)
|
||||||
|
if (IFF2) newFlags |= 0x04;
|
||||||
|
|
||||||
|
AF.Low = newFlags;
|
||||||
|
|
||||||
|
return 9;
|
||||||
case 0x62: // SBC HL, HL
|
case 0x62: // SBC HL, HL
|
||||||
Sbc16(HL.Word);
|
Sbc16(HL.Word);
|
||||||
return 15;
|
return 15;
|
||||||
@@ -1854,6 +1935,18 @@ namespace Core.Cpu
|
|||||||
_memory.Write((ushort)(address22 + 1), IX.High);
|
_memory.Write((ushort)(address22 + 1), IX.High);
|
||||||
|
|
||||||
return 20;
|
return 20;
|
||||||
|
case 0x24: // INC IXH
|
||||||
|
// Increment the high byte of IX and let the helper perfectly map the flags
|
||||||
|
IX.High = Inc8(IX.High);
|
||||||
|
return 8;
|
||||||
|
case 0x25: // DEC IXH
|
||||||
|
// Decrement the high byte of IX and let the helper handle all the Z80 flags
|
||||||
|
IX.High = Dec8(IX.High);
|
||||||
|
return 8;
|
||||||
|
case 0x26: // LD IXH, n
|
||||||
|
// Fetch the immediate 8-bit value and drop it straight into the high byte of IX
|
||||||
|
IX.High = FetchByte();
|
||||||
|
return 11;
|
||||||
case 0x2A: // LD IX, (nn)
|
case 0x2A: // LD IX, (nn)
|
||||||
// 1. Fetch the absolute 16-bit memory address from the instruction stream
|
// 1. Fetch the absolute 16-bit memory address from the instruction stream
|
||||||
byte addrLow2A = FetchByte();
|
byte addrLow2A = FetchByte();
|
||||||
@@ -1869,7 +1962,14 @@ namespace Core.Cpu
|
|||||||
// 4. Combine them and drop them into the IX register pair
|
// 4. Combine them and drop them into the IX register pair
|
||||||
IX.Word = (ushort)((ixHigh << 8) | ixLow);
|
IX.Word = (ushort)((ixHigh << 8) | ixLow);
|
||||||
|
|
||||||
return 20; // 20 T-States
|
return 20;
|
||||||
|
case 0x2D: // DEC IXL
|
||||||
|
IX.Low = Dec8(IX.Low);
|
||||||
|
return 8;
|
||||||
|
case 0x2E: // LD IXL, n
|
||||||
|
// Fetch the immediate 8-bit value and drop it straight into the low byte of IX
|
||||||
|
IX.Low = FetchByte();
|
||||||
|
return 11;
|
||||||
case 0x36: // LD (IX+d), n
|
case 0x36: // LD (IX+d), n
|
||||||
// 1. Fetch the displacement byte first
|
// 1. Fetch the displacement byte first
|
||||||
sbyte offset36 = (sbyte)FetchByte();
|
sbyte offset36 = (sbyte)FetchByte();
|
||||||
@@ -1939,6 +2039,9 @@ namespace Core.Cpu
|
|||||||
HL.High = _memory.Read(address66);
|
HL.High = _memory.Read(address66);
|
||||||
|
|
||||||
return 19;
|
return 19;
|
||||||
|
case 0x68: // LD IXL, B
|
||||||
|
IX.Low = BC.High;
|
||||||
|
return 8;
|
||||||
case 0x6E: // LD L, (IX+d)
|
case 0x6E: // LD L, (IX+d)
|
||||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||||
sbyte offset6E = (sbyte)FetchByte();
|
sbyte offset6E = (sbyte)FetchByte();
|
||||||
@@ -1972,6 +2075,17 @@ namespace Core.Cpu
|
|||||||
// 3. Write the contents of the L register (Low byte of HL) into memory
|
// 3. Write the contents of the L register (Low byte of HL) into memory
|
||||||
_memory.Write(address75, HL.Low);
|
_memory.Write(address75, HL.Low);
|
||||||
|
|
||||||
|
return 19;
|
||||||
|
case 0x77: // LD (IX+d), A
|
||||||
|
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||||
|
sbyte offset77 = (sbyte)FetchByte();
|
||||||
|
|
||||||
|
// 2. Calculate the exact memory address (IX + offset)
|
||||||
|
ushort address77 = (ushort)(IX.Word + offset77);
|
||||||
|
|
||||||
|
// 3. Write the Accumulator (AF.High) into memory at that address
|
||||||
|
_memory.Write(address77, AF.High);
|
||||||
|
|
||||||
return 19;
|
return 19;
|
||||||
case 0x7E: // LD A, (IX+d)
|
case 0x7E: // LD A, (IX+d)
|
||||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||||
|
|||||||
@@ -433,6 +433,10 @@ namespace Desktop
|
|||||||
mnemonic = $"LD H, 0x{hImm:X2}";
|
mnemonic = $"LD H, 0x{hImm:X2}";
|
||||||
instructionLength = 2;
|
instructionLength = 2;
|
||||||
break;
|
break;
|
||||||
|
case 0x27: // DAA
|
||||||
|
mnemonic = "DAA";
|
||||||
|
instructionLength = 1;
|
||||||
|
break;
|
||||||
case 0x28:
|
case 0x28:
|
||||||
sbyte jrZOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
|
sbyte jrZOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
|
||||||
ushort jrZDest = (ushort)(currentPc + 2 + jrZOffset);
|
ushort jrZDest = (ushort)(currentPc + 2 + jrZOffset);
|
||||||
@@ -845,12 +849,39 @@ namespace Desktop
|
|||||||
mnemonic = $"LD (0x{nn:X4}), IX";
|
mnemonic = $"LD (0x{nn:X4}), IX";
|
||||||
instructionLength = 4;
|
instructionLength = 4;
|
||||||
}
|
}
|
||||||
|
else if (ddOpcode == 0x24) // INC IXH
|
||||||
|
{
|
||||||
|
mnemonic = "INC IXH";
|
||||||
|
instructionLength = 2;
|
||||||
|
}
|
||||||
|
else if (ddOpcode == 0x25) // DEC IXH
|
||||||
|
{
|
||||||
|
mnemonic = "DEC IXH";
|
||||||
|
instructionLength = 2;
|
||||||
|
}
|
||||||
|
else if (ddOpcode == 0x26) // LD IXH, n
|
||||||
|
{
|
||||||
|
byte nValue = _memoryBus.Read((ushort)(currentPc + 2));
|
||||||
|
mnemonic = $"LD IXH, 0x{nValue:X2}";
|
||||||
|
instructionLength = 3;
|
||||||
|
}
|
||||||
else if (ddOpcode == 0x2A) // LD IX, (nn)
|
else if (ddOpcode == 0x2A) // LD IX, (nn)
|
||||||
{
|
{
|
||||||
ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
ushort nn = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||||
mnemonic = $"LD IX, (0x{nn:X4})";
|
mnemonic = $"LD IX, (0x{nn:X4})";
|
||||||
instructionLength = 4;
|
instructionLength = 4;
|
||||||
}
|
}
|
||||||
|
else if (ddOpcode == 0x2D) // DEC IXL
|
||||||
|
{
|
||||||
|
mnemonic = "DEC IXL";
|
||||||
|
instructionLength = 2;
|
||||||
|
}
|
||||||
|
else if (ddOpcode == 0x2E) // LD IXL, n
|
||||||
|
{
|
||||||
|
byte nValue = _memoryBus.Read((ushort)(currentPc + 2));
|
||||||
|
mnemonic = $"LD IXL, 0x{nValue:X2}";
|
||||||
|
instructionLength = 3;
|
||||||
|
}
|
||||||
else if (ddOpcode == 0x36) // LD (IX+d), n
|
else if (ddOpcode == 0x36) // LD (IX+d), n
|
||||||
{
|
{
|
||||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
@@ -895,6 +926,11 @@ namespace Desktop
|
|||||||
mnemonic = $"LD H, (IX{sign}{d})";
|
mnemonic = $"LD H, (IX{sign}{d})";
|
||||||
instructionLength = 3;
|
instructionLength = 3;
|
||||||
}
|
}
|
||||||
|
else if (ddOpcode == 0x68) // LD IXL, B
|
||||||
|
{
|
||||||
|
mnemonic = "LD IXL, B";
|
||||||
|
instructionLength = 2;
|
||||||
|
}
|
||||||
else if (ddOpcode == 0x6E) // LD L, (IX+d)
|
else if (ddOpcode == 0x6E) // LD L, (IX+d)
|
||||||
{
|
{
|
||||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
@@ -916,6 +952,17 @@ namespace Desktop
|
|||||||
mnemonic = $"LD (IX{sign}{d}), L";
|
mnemonic = $"LD (IX{sign}{d}), L";
|
||||||
instructionLength = 3;
|
instructionLength = 3;
|
||||||
}
|
}
|
||||||
|
else if (ddOpcode == 0x77) // LD (IX+d), A
|
||||||
|
{
|
||||||
|
// Read the 3rd byte (the displacement)
|
||||||
|
sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
|
|
||||||
|
// Format nicely with a + or - sign
|
||||||
|
string sign = offset >= 0 ? "+" : "";
|
||||||
|
mnemonic = $"LD (IX{sign}{offset}), A";
|
||||||
|
|
||||||
|
instructionLength = 3;
|
||||||
|
}
|
||||||
else if (ddOpcode == 0x7E) // LD A, (IX+d)
|
else if (ddOpcode == 0x7E) // LD A, (IX+d)
|
||||||
{
|
{
|
||||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
@@ -1024,6 +1071,7 @@ namespace Desktop
|
|||||||
mnemonic = $"LD DE, (0x{addr5B:X4})";
|
mnemonic = $"LD DE, (0x{addr5B:X4})";
|
||||||
instructionLength = 4;
|
instructionLength = 4;
|
||||||
break;
|
break;
|
||||||
|
case 0x5F: mnemonic = "LD A, R"; instructionLength = 2; break;
|
||||||
case 0x6A: mnemonic = "ADC HL, HL"; instructionLength = 2; break;
|
case 0x6A: mnemonic = "ADC HL, HL"; instructionLength = 2; break;
|
||||||
case 0x62: mnemonic = "SBC HL, HL"; instructionLength = 2; break;
|
case 0x62: mnemonic = "SBC HL, HL"; instructionLength = 2; break;
|
||||||
case 0x73:
|
case 0x73:
|
||||||
|
|||||||
Reference in New Issue
Block a user