Chuckie Egg runs fully from a snapshot file! Fully playable!

This commit is contained in:
2026-04-20 12:51:04 +01:00
parent e53661ce8a
commit 7464b29fca
2 changed files with 256 additions and 37 deletions

View File

@@ -43,6 +43,10 @@ namespace Core.Cpu
private readonly IO_Bus _simpleIoBus;
public TapManager _tapManager;
//Misc Variables
byte newFlags = 0;
int result = 0;
public Z80(IMemory memory, IO_Bus ioBus, TapManager tapManager)
{
_memory = memory;
@@ -278,7 +282,7 @@ namespace Core.Cpu
private void Sub(byte value)
{
byte a = AF.High;
int result = a - value;
result = a - value;
// Save the result back to the Accumulator
AF.High = (byte)result;
@@ -311,7 +315,7 @@ namespace Core.Cpu
byte carry = (byte)(AF.Low & 0x01); // Get the current Carry flag (Bit 0)
// Calculate the raw integer result to check for borrows/underflows
int result = a - value - carry;
result = a - value - carry;
// Update the Accumulator
AF.High = (byte)result;
@@ -344,7 +348,7 @@ namespace Core.Cpu
int carry = AF.Low & 0x01;
// Calculate the raw integer result to check for underflows
int result = hl - value - carry;
result = hl - value - carry;
// Update the HL register
HL.Word = (ushort)result;
@@ -435,7 +439,7 @@ namespace Core.Cpu
private void Cp(byte value)
{
byte a = AF.High;
int result = a - value;
result = a - value;
// --- Update Flags (F Register) ---
AF.Low = 0; // Clear all flags
@@ -519,7 +523,7 @@ namespace Core.Cpu
private void Add16(ushort value)
{
int hl = HL.Word;
int result = hl + value;
result = hl + value;
// Update the HL register
HL.Word = (ushort)result;
@@ -531,11 +535,30 @@ namespace Core.Cpu
// Carry Flag (Bit 0) - Set if the result overflows 16 bits
if (result > 0xFFFF) AF.Low |= 0x01;
}
private void Add16IX(ushort value)
{
int ixVal = IX.Word;
result = ixVal + value;
// --- 16-Bit ADD IX Flag Calculation ---
// Preserve S (Bit 7), Z (Bit 6), and P/V (Bit 2).
// This perfectly resets N (Bit 1) to 0 at the same time.
newFlags = (byte)(AF.Low & 0xC4);
// Half-Carry (H - Bit 4): Set if carry from Bit 11
if (((ixVal & 0x0FFF) + (value & 0x0FFF)) > 0x0FFF) newFlags |= 0x10;
// Carry (C - Bit 0): Set if the total result overflows 16 bits
if (result > 0xFFFF) newFlags |= 0x01;
AF.Low = newFlags;
IX.Word = (ushort)result;
}
private void Add(byte value)
{
byte a = AF.High;
int result = a + value;
result = a + value;
// Save the result back to the Accumulator
AF.High = (byte)result;
@@ -565,7 +588,7 @@ namespace Core.Cpu
int aVal = AF.High;
int carryIn = AF.Low & 0x01;
int result = aVal + operand + carryIn;
result = aVal + operand + carryIn;
byte newFlags = 0;
@@ -585,7 +608,7 @@ namespace Core.Cpu
int carry = AF.Low & 0x01;
// Calculate the raw integer result to check for overflows
int result = hl + value + carry;
result = hl + value + carry;
// --- Update Flags (F Register) ---
byte newFlags = 0; // Clear all flags (which forces N to 0, correctly!)
@@ -612,7 +635,7 @@ namespace Core.Cpu
private void AddA(byte operand)
{
byte a = AF.High;
int result = a + operand;
result = a + operand;
AF.High = (byte)result;
@@ -1487,8 +1510,6 @@ namespace Core.Cpu
// Fetch the actual extended instruction
byte extendedOpcode = _memory.Read(PC++);
byte val = 0;
byte newFlags = 0;
int result = 0;
switch (extendedOpcode)
{
@@ -1561,6 +1582,10 @@ namespace Core.Cpu
DE.Low = _memory.Read(src5B);
DE.High = _memory.Read((ushort)(src5B + 1));
return 20;
case 0x5E: // IM 2
// Set the CPU's internal interrupt mode state
InterruptMode = 2;
return 8;
case 0x5F: // LD A, R
// 1. Load the Refresh register into the Accumulator
AF.High = R;
@@ -1920,25 +1945,16 @@ namespace Core.Cpu
switch (ddOpcode)
{
case 0x09: // ADD IX, BC
int result = IX.Word + BC.Word;
// --- 16-Bit Flag Calculation ---
// Preserve S (Bit 7), Z (Bit 6), and P/V (Bit 2).
byte newFlags = (byte)(AF.Low & 0xC4);
// Half-Carry (H - Bit 4): Set if carry from Bit 11
if (((IX.Word & 0x0FFF) + (BC.Word & 0x0FFF)) > 0x0FFF)
newFlags |= 0x10;
// Carry (C - Bit 0): Set if the total result overflows 16 bits
if (result > 0xFFFF)
newFlags |= 0x01;
// (N - Bit 1 is left at 0 because the bitwise AND above cleared it)
AF.Low = newFlags;
IX.Word = (ushort)result;
Add16IX(BC.Word);
return 15;
case 0x19: // ADD IX, DE
Add16IX(DE.Word);
return 15;
case 0x29: // ADD IX, IX
Add16IX(IX.Word); // Multiplies IX by 2!
return 15;
case 0x39: // ADD IX, SP
Add16IX(SP);
return 15;
case 0x21: // LD IX, nn
byte low = FetchByte();
@@ -2002,6 +2018,40 @@ namespace Core.Cpu
// Fetch the immediate 8-bit value and drop it straight into the low byte of IX
IX.Low = FetchByte();
return 11;
case 0x34: // INC (IX+d)
// 1. Fetch the displacement byte and cast to a signed sbyte
sbyte offset34 = (sbyte)FetchByte();
// 2. Calculate the target memory address
ushort address34 = (ushort)(IX.Word + offset34);
// 3. Read the value from memory
byte val34 = _memory.Read(address34);
// 4. Pass it through your helper to increment and set the flags perfectly
byte result34 = Inc8(val34);
// 5. Write the incremented value back to memory
_memory.Write(address34, result34);
return 23;
case 0x35: // DEC (IX+d)
// 1. Fetch the displacement byte and cast to a signed sbyte
sbyte offset35 = (sbyte)FetchByte();
// 2. Calculate the target memory address
ushort address35 = (ushort)(IX.Word + offset35);
// 3. Read the value from memory
byte val35 = _memory.Read(address35);
// 4. Pass it through your helper to decrement and set the flags
byte result35 = Dec8(val35);
// 5. Write the decremented value back to memory
_memory.Write(address35, result35);
return 23;
case 0x36: // LD (IX+d), n
// 1. Fetch the displacement byte first
sbyte offset36 = (sbyte)FetchByte();
@@ -2071,9 +2121,22 @@ namespace Core.Cpu
HL.High = _memory.Read(address66);
return 19;
case 0x67: // LD IXH, A
// Load the Accumulator (AF.High) directly into the high byte of IX
IX.High = AF.High;
return 8;
case 0x68: // LD IXL, B
IX.Low = BC.High;
return 8;
case 0x69: // LD IXL, C
// Load the C register (BC.Low) into the low byte of IX
IX.Low = BC.Low;
return 8;
case 0x6A: // LD IXL, D
// Load the D register (DE.High) into the low byte of IX
IX.Low = DE.High;
return 8;
case 0x6E: // LD L, (IX+d)
// 1. Fetch the displacement byte and cast it to a signed sbyte
sbyte offset6E = (sbyte)FetchByte();
@@ -2085,7 +2148,29 @@ namespace Core.Cpu
HL.Low = _memory.Read(address6E);
return 19;
case 0x72: // LD (IX+d), D
// 1. Fetch the displacement byte and cast to a signed sbyte
sbyte offset72 = (sbyte)FetchByte();
// 2. Calculate the target memory address
ushort address72 = (ushort)(IX.Word + offset72);
// 3. Write the D register (DE.High) to memory
_memory.Write(address72, DE.High);
return 19; // 19 T-States
case 0x73: // LD (IX+d), E
// 1. Fetch the displacement byte and cast to a signed sbyte
sbyte offset73 = (sbyte)FetchByte();
// 2. Calculate the target memory address
ushort address73 = (ushort)(IX.Word + offset73);
// 3. Write the E register (DE.Low) to memory
_memory.Write(address73, DE.Low);
return 19;
case 0x74: // LD (IX+d), H
// 1. Fetch the displacement byte and cast it to a signed sbyte
sbyte offset74 = (sbyte)FetchByte();
@@ -2119,6 +2204,10 @@ namespace Core.Cpu
_memory.Write(address77, AF.High);
return 19;
case 0x7C: // LD A, IXH
// Load the high byte of IX directly into the Accumulator
AF.High = IX.High;
return 8;
case 0x7E: // LD A, (IX+d)
// 1. Fetch the displacement byte and cast it to a signed sbyte
sbyte offset7E = (sbyte)FetchByte();
@@ -2129,6 +2218,21 @@ namespace Core.Cpu
// 3. Read the byte from memory and drop it straight into the Accumulator (A)
AF.High = _memory.Read(address7E);
return 19;
case 0x86: // ADD A, (IX+d)
sbyte offset86 = (sbyte)FetchByte();
ushort address86 = (ushort)(IX.Word + offset86);
// Read the memory and pass it straight into your flawless helper!
Add(_memory.Read(address86));
return 19;
case 0x96: // SUB (IX+d)
sbyte offset96 = (sbyte)FetchByte();
ushort address96 = (ushort)(IX.Word + offset96);
// Read the memory and pass it straight into your flawless helper!
Sub(_memory.Read(address96));
return 19;
case 0xBE: // CP (IX+d)
// 1. Fetch the displacement byte and calculate the address
@@ -2167,7 +2271,45 @@ namespace Core.Cpu
// CRITICAL: Notice we do NOT update AF.High! The Accumulator is preserved.
return 19; // 19 T-States
return 19;
case 0xCB: // The DD CB nested prefix
{
// 1. Fetch the displacement byte first
sbyte displacement = (sbyte)FetchByte();
// 2. Fetch the actual operation opcode (like your 0x72) second
byte cbOpcode = FetchByte();
ushort targetAddress = (ushort)(IX.Word + displacement);
byte memVal = _memory.Read(targetAddress);
// Extract the mathematical properties of the opcode
int operation = cbOpcode >> 6; // 01 = BIT, 10 = RES, 11 = SET
int bitIndex = (cbOpcode >> 3) & 0x07; // Extracts a number 0-7
byte bitMask = (byte)(1 << bitIndex); // Creates the bitmask
switch (operation)
{
case 1: // ALL BIT Instructions
AF.Low &= 0x01; // Preserve ONLY Carry
AF.Low |= 0x10; // Set Half-Carry
if ((memVal & bitMask) == 0)
{
AF.Low |= 0x44; // Set Zero (Bit 6) and P/V (Bit 2)
}
else if (bitIndex == 7)
{
AF.Low |= 0x80; // If testing Bit 7 and it is 1, set Sign (Bit 7)
}
return 20; // 20 T-States
// (You can copy your RES and SET logic from ExecuteFDPrefix here later!)
default:
throw new NotImplementedException($"DD CB opcode {cbOpcode:X2} not fully implemented!");
}
}
case 0xE1: // POP IX
// 1. Read the low byte from the top of the stack
byte popLow = _memory.Read(SP);

View File

@@ -832,11 +832,10 @@ namespace Desktop
case 0xDD:
{
byte ddOpcode = _memoryBus.Read((ushort)(currentPc + 1));
if (ddOpcode == 0x09) // ADD IX, BC
{
mnemonic = "ADD IX, BC";
instructionLength = 2;
}
if (ddOpcode == 0x09) { mnemonic = "ADD IX, BC"; instructionLength = 2; }
else if (ddOpcode == 0x19) { mnemonic = "ADD IX, DE"; instructionLength = 2; }
else if (ddOpcode == 0x29) { mnemonic = "ADD IX, IX"; instructionLength = 2; }
else if (ddOpcode == 0x39) { mnemonic = "ADD IX, SP"; instructionLength = 2; }
else if (ddOpcode == 0x21) // LD IX, nn
{
ushort ixVal = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
@@ -892,6 +891,20 @@ namespace Desktop
mnemonic = $"LD IXL, 0x{nValue:X2}";
instructionLength = 3;
}
else if (ddOpcode == 0x34) // INC (IX+d)
{
sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
string sign = offset >= 0 ? "+" : "";
mnemonic = $"INC (IX{sign}{offset})";
instructionLength = 3;
}
else if (ddOpcode == 0x35) // DEC (IX+d)
{
sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
string sign = offset >= 0 ? "+" : "";
mnemonic = $"DEC (IX{sign}{offset})";
instructionLength = 3;
}
else if (ddOpcode == 0x36) // LD (IX+d), n
{
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
@@ -936,11 +949,26 @@ namespace Desktop
mnemonic = $"LD H, (IX{sign}{d})";
instructionLength = 3;
}
else if (ddOpcode == 0x67) // LD IXH, A
{
mnemonic = "LD IXH, A";
instructionLength = 2;
}
else if (ddOpcode == 0x68) // LD IXL, B
{
mnemonic = "LD IXL, B";
instructionLength = 2;
}
else if (ddOpcode == 0x69) // LD IXL, C
{
mnemonic = "LD IXL, C";
instructionLength = 2;
}
else if (ddOpcode == 0x6A) // LD IXL, D
{
mnemonic = "LD IXL, D";
instructionLength = 2;
}
else if (ddOpcode == 0x6E) // LD L, (IX+d)
{
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
@@ -948,6 +976,20 @@ namespace Desktop
mnemonic = $"LD L, (IX{sign}{d})";
instructionLength = 3;
}
else if (ddOpcode == 0x72) // LD (IX+d), D
{
sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
string sign = offset >= 0 ? "+" : "";
mnemonic = $"LD (IX{sign}{offset}), D";
instructionLength = 3;
}
else if (ddOpcode == 0x73) // LD (IX+d), E
{
sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
string sign = offset >= 0 ? "+" : "";
mnemonic = $"LD (IX{sign}{offset}), E";
instructionLength = 3;
}
else if (ddOpcode == 0x74) // LD (IX+d), H
{
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
@@ -973,6 +1015,11 @@ namespace Desktop
instructionLength = 3;
}
else if (ddOpcode == 0x7C) // LD A, IXH
{
mnemonic = "LD A, IXH";
instructionLength = 2;
}
else if (ddOpcode == 0x7E) // LD A, (IX+d)
{
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
@@ -980,6 +1027,35 @@ namespace Desktop
mnemonic = $"LD A, (IX{sign}{d})";
instructionLength = 3;
}
else if (ddOpcode == 0x86)
{
sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
string sign = offset >= 0 ? "+" : "";
mnemonic = $"ADD A, (IX{sign}{offset})";
instructionLength = 3;
}
else if (ddOpcode == 0x96)
{
sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
string sign = offset >= 0 ? "+" : "";
mnemonic = $"SUB (IX{sign}{offset})";
instructionLength = 3;
}
else if (ddOpcode == 0xCB)
{
// DD CB instructions are 4 bytes long!
sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
cbOp = _memoryBus.Read((ushort)(currentPc + 3));
int operation = cbOp >> 6;
int bitIndex = (cbOp >> 3) & 0x07;
string sign = offset >= 0 ? "+" : "";
if (operation == 1) mnemonic = $"BIT {bitIndex}, (IX{sign}{offset})";
else mnemonic = $"DD CB (IX{sign}{offset}) {cbOp:X2}"; // Fallback
instructionLength = 4;
}
else if (ddOpcode == 0xBE) // CP (IX+d)
{
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
@@ -1081,6 +1157,7 @@ namespace Desktop
mnemonic = $"LD DE, (0x{addr5B:X4})";
instructionLength = 4;
break;
case 0x5E: mnemonic = "IM 2"; instructionLength = 2; break;
case 0x5F: mnemonic = "LD A, R"; instructionLength = 2; break;
case 0x6A: mnemonic = "ADC HL, HL"; instructionLength = 2; break;
case 0x62: mnemonic = "SBC HL, HL"; instructionLength = 2; break;