Got Chuckie Egg TAP to load to the title screen!
This commit is contained in:
273
Core/Cpu/Z80.cs
273
Core/Cpu/Z80.cs
@@ -585,6 +585,7 @@ namespace Core.Cpu
|
||||
private int ExecuteOpcode(byte opcode)
|
||||
{
|
||||
sbyte offset = 0;
|
||||
byte oldCarry = 0;
|
||||
switch (opcode)
|
||||
{
|
||||
case 0x00: // NOP
|
||||
@@ -723,6 +724,24 @@ namespace Core.Cpu
|
||||
case 0x16: // LD D, n
|
||||
DE.High = FetchByte();
|
||||
return 7;
|
||||
case 0x17: // RLA
|
||||
// 1. Grab the current Carry flag (Bit 0 of AF.Low)
|
||||
oldCarry = (byte)(AF.Low & 0x01);
|
||||
|
||||
// 2. See if Bit 7 of the Accumulator is about to fall off
|
||||
bool newCarry = (AF.High & 0x80) != 0;
|
||||
|
||||
// 3. Shift A left, and drop the OLD carry directly into Bit 0
|
||||
AF.High = (byte)((AF.High << 1) | oldCarry);
|
||||
|
||||
// 4. Update the flags
|
||||
// Preserve S (Bit 7), Z (Bit 6), and P/V (Bit 2) while wiping H and N.
|
||||
AF.Low &= 0xC4;
|
||||
|
||||
// 5. Apply the new Carry flag if necessary
|
||||
if (newCarry) AF.Low |= 0x01;
|
||||
|
||||
return 4; // 4 T-States
|
||||
case 0x18: // JR d
|
||||
sbyte jumpDistance = (sbyte)FetchByte();
|
||||
|
||||
@@ -740,7 +759,7 @@ namespace Core.Cpu
|
||||
case 0x1F: // RRA
|
||||
{
|
||||
// 1. Grab the current Carry Flag (0 or 1)
|
||||
byte oldCarry = (byte)(AF.Low & 0x01);
|
||||
oldCarry = (byte)(AF.Low & 0x01);
|
||||
|
||||
// 2. Grab the bit that is about to fall off the Accumulator
|
||||
byte bit0 = (byte)(AF.High & 0x01);
|
||||
@@ -1583,6 +1602,14 @@ namespace Core.Cpu
|
||||
// Shift left, and loop the falling bit back into Bit 0
|
||||
val = (byte)((val << 1) | (carryOut ? 1 : 0));
|
||||
break;
|
||||
case 4: // SLA (Shift Left Arithmetic)
|
||||
// 1. Grab Bit 7 before it falls off to set the Carry flag
|
||||
carryOut = (val & 0x80) != 0;
|
||||
|
||||
// 2. Shift the byte left by 1.
|
||||
// (In C#, a standard left shift automatically pads Bit 0 with a 0)
|
||||
val = (byte)(val << 1);
|
||||
break;
|
||||
case 7: // SRL (Shift Right Logical)
|
||||
// 1. Grab Bit 0 before it falls off to set the Carry flag
|
||||
carryOut = (val & 0x01) != 0;
|
||||
@@ -1664,6 +1691,35 @@ namespace Core.Cpu
|
||||
IX.Word = (ushort)((high << 8) | low);
|
||||
|
||||
return 14;
|
||||
case 0x22: // LD (nn), IX
|
||||
// 1. Fetch the absolute 16-bit memory address from the instruction stream
|
||||
byte addrLow22 = FetchByte();
|
||||
byte addrHigh22 = FetchByte();
|
||||
ushort address22 = (ushort)((addrHigh22 << 8) | addrLow22);
|
||||
|
||||
// 2. Write the LOW byte of IX to the exact address
|
||||
_memory.Write(address22, IX.Low);
|
||||
|
||||
// 3. Write the HIGH byte of IX to the address + 1
|
||||
_memory.Write((ushort)(address22 + 1), IX.High);
|
||||
|
||||
return 20;
|
||||
case 0x2A: // LD IX, (nn)
|
||||
// 1. Fetch the absolute 16-bit memory address from the instruction stream
|
||||
byte addrLow2A = FetchByte();
|
||||
byte addrHigh2A = FetchByte();
|
||||
ushort address2A = (ushort)((addrHigh2A << 8) | addrLow2A);
|
||||
|
||||
// 2. Read the LOW byte from that specific memory location
|
||||
byte ixLow = _memory.Read(address2A);
|
||||
|
||||
// 3. Read the HIGH byte from the next consecutive memory location
|
||||
byte ixHigh = _memory.Read((ushort)(address2A + 1));
|
||||
|
||||
// 4. Combine them and drop them into the IX register pair
|
||||
IX.Word = (ushort)((ixHigh << 8) | ixLow);
|
||||
|
||||
return 20; // 20 T-States
|
||||
case 0x36: // LD (IX+d), n
|
||||
// 1. Fetch the displacement byte first
|
||||
sbyte offset36 = (sbyte)FetchByte();
|
||||
@@ -1677,6 +1733,28 @@ namespace Core.Cpu
|
||||
// 4. Write the immediate value directly into memory
|
||||
_memory.Write(address36, n36);
|
||||
|
||||
return 19;
|
||||
case 0x46: // LD B, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset46 = (sbyte)FetchByte();
|
||||
|
||||
// 2. Calculate the exact memory address (IX + offset)
|
||||
ushort address46 = (ushort)(IX.Word + offset46);
|
||||
|
||||
// 3. Read the byte from memory and drop it into the C register (Low byte of BC)
|
||||
BC.High = _memory.Read(address46);
|
||||
|
||||
return 19;
|
||||
case 0x4E: // LD C, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset4E = (sbyte)FetchByte();
|
||||
|
||||
// 2. Calculate the exact memory address (IX + offset)
|
||||
ushort address4E = (ushort)(IX.Word + offset4E);
|
||||
|
||||
// 3. Read the byte from memory and drop it into the C register (Low byte of BC)
|
||||
BC.Low = _memory.Read(address4E);
|
||||
|
||||
return 19;
|
||||
case 0x56: // LD D, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
@@ -1699,6 +1777,17 @@ namespace Core.Cpu
|
||||
// 3. Read the byte from memory and drop it into the E register
|
||||
DE.Low = _memory.Read(address5E);
|
||||
|
||||
return 19;
|
||||
case 0x66: // LD H, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset66 = (sbyte)FetchByte();
|
||||
|
||||
// 2. Calculate the exact memory address (IX + offset)
|
||||
ushort address66 = (ushort)(IX.Word + offset66);
|
||||
|
||||
// 3. Read the byte from memory and drop it into the H register (High byte of HL)
|
||||
HL.High = _memory.Read(address66);
|
||||
|
||||
return 19;
|
||||
case 0x6E: // LD L, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
@@ -1711,6 +1800,7 @@ namespace Core.Cpu
|
||||
HL.Low = _memory.Read(address6E);
|
||||
|
||||
return 19;
|
||||
|
||||
case 0x74: // LD (IX+d), H
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset74 = (sbyte)FetchByte();
|
||||
@@ -2098,183 +2188,4 @@ namespace Core.Cpu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//sbyte offsetCB = (sbyte)FetchByte(); // This is the '01'
|
||||
//byte bitOpcode = FetchByte(); // This is the 'CE'
|
||||
//targetAddress = (ushort)(IY.Word + offsetCB);
|
||||
|
||||
//switch (bitOpcode)
|
||||
//{
|
||||
// case 0x46: // BIT 0, (IY+d)
|
||||
// byte memValBit0 = _memory.Read(targetAddress);
|
||||
|
||||
// // Preserve the existing Carry Flag (Bit 0)
|
||||
// byte newFlags = (byte)(AF.Low & 0x01);
|
||||
|
||||
// newFlags |= 0x10; // Force Half-Carry (Bit 4) to 1
|
||||
|
||||
// // Test Bit 0. If it is 0, turn ON the Zero Flag (Bit 6)
|
||||
// if ((memValBit0 & 0x01) == 0)
|
||||
// {
|
||||
// newFlags |= 0x40;
|
||||
// }
|
||||
|
||||
// AF.Low = newFlags;
|
||||
// return 20;
|
||||
// case 0x4E: // BIT 1, (IY+d)
|
||||
// {
|
||||
// byte memValBit = _memory.Read(targetAddress);
|
||||
|
||||
// // Check if bit 1 is 0
|
||||
// bool bitIsZero = (memValBit & 0x02) == 0;
|
||||
|
||||
// // Preserve the Carry flag (Bit 0), clear everything else
|
||||
// AF.Low &= 0x01;
|
||||
|
||||
// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT
|
||||
// AF.Low |= 0x10;
|
||||
|
||||
// if (bitIsZero)
|
||||
// {
|
||||
// AF.Low |= 0x40; // Set Zero Flag (Bit 6)
|
||||
// AF.Low |= 0x04; // Set P/V Flag (Bit 2)
|
||||
// }
|
||||
|
||||
// return 20;
|
||||
// }
|
||||
// case 0x66: // BIT 4, (IY+d)
|
||||
// byte memValBit4 = _memory.Read(targetAddress);
|
||||
|
||||
// // Preserve ONLY the Carry flag (Bit 0). This clears N and everything else.
|
||||
// AF.Low &= 0x01;
|
||||
|
||||
// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT instructions
|
||||
// AF.Low |= 0x10;
|
||||
|
||||
// // Test Bit 4 (0x10 is Binary 0001 0000)
|
||||
// if ((memValBit4 & 0x10) == 0)
|
||||
// {
|
||||
// // If the bit is 0, turn ON the Zero Flag (Bit 6)
|
||||
// AF.Low |= 0x40;
|
||||
|
||||
// // Parity/Overflow (Bit 2) is historically set along with the Zero flag on BIT
|
||||
// AF.Low |= 0x04;
|
||||
// }
|
||||
// // (Note: The Sign Flag remains 0 because we are not testing Bit 7)
|
||||
|
||||
// return 20;
|
||||
// case 0x6E: // BIT 5, (IY+d)
|
||||
// byte memValBit5 = _memory.Read(targetAddress);
|
||||
|
||||
// // Preserve ONLY the Carry flag (Bit 0). This clears N and everything else.
|
||||
// AF.Low &= 0x01;
|
||||
|
||||
// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT instructions
|
||||
// AF.Low |= 0x10;
|
||||
|
||||
// // Test Bit 5 (0x20 is Binary 0010 0000)
|
||||
// if ((memValBit5 & 0x20) == 0)
|
||||
// {
|
||||
// // If the bit is 0, turn ON the Zero Flag (Bit 6)
|
||||
// AF.Low |= 0x40;
|
||||
|
||||
// // Parity/Overflow (Bit 2) is historically set along with the Zero flag on BIT
|
||||
// AF.Low |= 0x04;
|
||||
// }
|
||||
// // (Note: The Sign Flag remains 0 because we are not testing Bit 7)
|
||||
|
||||
// return 20;
|
||||
// case 0x76: // BIT 6, (IY+d)
|
||||
// byte memValBit6 = _memory.Read(targetAddress);
|
||||
|
||||
// // Preserve ONLY the Carry flag (Bit 0). This clears N and everything else.
|
||||
// AF.Low &= 0x01;
|
||||
|
||||
// // Set Half-Carry (Bit 4) - Standard Z80 behavior for BIT
|
||||
// AF.Low |= 0x10;
|
||||
|
||||
// // Test Bit 6 (0x40 is Binary 0100 0000)
|
||||
// if ((memValBit6 & 0x40) == 0)
|
||||
// {
|
||||
// // If the bit is 0, turn ON the Zero Flag (Bit 6)
|
||||
// AF.Low |= 0x40;
|
||||
|
||||
// // Parity/Overflow (Bit 2) is historically set along with the Zero flag on BIT
|
||||
// AF.Low |= 0x04;
|
||||
// }
|
||||
// // (Note: The Sign Flag remains 0 because we are not testing Bit 7)
|
||||
|
||||
// return 20;
|
||||
// case 0x86: // RES 0, (IY+d)
|
||||
// byte memValRes0 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0xFE is Binary 1111 1110.
|
||||
// // ANDing preserves all bits except Bit 0, which becomes 0.
|
||||
// memValRes0 &= 0xFE;
|
||||
|
||||
// _memory.Write(targetAddress, memValRes0);
|
||||
// return 23; // Takes 23 T-States
|
||||
// case 0x8E: // RES 1, (IY+d)
|
||||
// byte memValRes = _memory.Read(targetAddress);
|
||||
|
||||
// // 0xFD is Binary 1111 1101.
|
||||
// // ANDing with this preserves all bits except Bit 1, which becomes 0.
|
||||
// memValRes &= 0xFD;
|
||||
// _memory.Write(targetAddress, memValRes);
|
||||
// return 23;
|
||||
// case 0xA6: // RES 4, (IY+d)
|
||||
// byte memValRes4 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0xEF is Binary 1110 1111
|
||||
// // ANDing preserves all bits except Bit 4, which becomes 0.
|
||||
// memValRes4 &= 0xEF;
|
||||
|
||||
// _memory.Write(targetAddress, memValRes4);
|
||||
// return 23;
|
||||
// case 0xAE: // RES 5, (IY+d)
|
||||
// byte memValRes5 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0xDF is Binary 1101 1111
|
||||
// // ANDing perfectly preserves all other bits while forcing Bit 5 to 0
|
||||
// memValRes5 &= 0xDF;
|
||||
|
||||
// _memory.Write(targetAddress, memValRes5);
|
||||
// return 23;
|
||||
// case 0xC6: // SET 0, (IY+d)
|
||||
// byte memValSet0 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0x01 is Binary 0000 0001
|
||||
// // ORing forces Bit 0 to 1 and perfectly preserves all other bits
|
||||
// memValSet0 |= 0x01;
|
||||
|
||||
// _memory.Write(targetAddress, memValSet0);
|
||||
// return 23; // Takes 23 T-States
|
||||
// case 0xCE: // SET 1, (IY+d)
|
||||
// memVal = _memory.Read(targetAddress);
|
||||
// memVal |= 0x02; // 0x02 is Binary 0000 0010 (Bit 1)
|
||||
// _memory.Write(targetAddress, memVal);
|
||||
// return 23;
|
||||
// case 0xE6: // SET 4, (IY+d)
|
||||
// byte memValSet4 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0x10 is Binary 0001 0000
|
||||
// // ORing perfectly preserves all other bits while forcing Bit 4 to 1
|
||||
// memValSet4 |= 0x10;
|
||||
|
||||
// _memory.Write(targetAddress, memValSet4);
|
||||
// return 23;
|
||||
// case 0xEE: // SET 5, (IY+d)
|
||||
// byte memValSet5 = _memory.Read(targetAddress);
|
||||
|
||||
// // 0x20 is Binary 0010 0000
|
||||
// // ORing perfectly preserves all other bits while forcing Bit 5 to 1
|
||||
// memValSet5 |= 0x20;
|
||||
|
||||
// _memory.Write(targetAddress, memValSet5);
|
||||
// return 23;
|
||||
|
||||
// default:
|
||||
// throw new NotImplementedException($"FD CB opcode {bitOpcode:X2} at PC 0x{(PC - 1):X4} not implemented!");
|
||||
//}
|
||||
}
|
||||
Reference in New Issue
Block a user