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);
|
||||
|
||||
Reference in New Issue
Block a user