Chuckie Egg runs fully from a snapshot file! Fully playable!
This commit is contained in:
206
Core/Cpu/Z80.cs
206
Core/Cpu/Z80.cs
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user