Implemented more OpCodes again!
This commit is contained in:
437
Core/Cpu/Z80.cs
437
Core/Cpu/Z80.cs
@@ -702,7 +702,8 @@ namespace Core.Cpu
|
|||||||
else
|
else
|
||||||
AF.Low &= 0xEF;
|
AF.Low &= 0xEF;
|
||||||
return 4;
|
return 4;
|
||||||
case 0x40: BC.High = BC.High; return 4;
|
case 0x40: //BC.High = BC.High;
|
||||||
|
return 4;
|
||||||
case 0x41: BC.High = BC.Low; return 4;
|
case 0x41: BC.High = BC.Low; return 4;
|
||||||
case 0x42: BC.High = DE.High; return 4;
|
case 0x42: BC.High = DE.High; return 4;
|
||||||
case 0x43: BC.High = DE.Low; return 4;
|
case 0x43: BC.High = DE.Low; return 4;
|
||||||
@@ -713,7 +714,8 @@ namespace Core.Cpu
|
|||||||
|
|
||||||
// --- LD C, r ---
|
// --- LD C, r ---
|
||||||
case 0x48: BC.Low = BC.High; return 4;
|
case 0x48: BC.Low = BC.High; return 4;
|
||||||
case 0x49: BC.Low = BC.Low; return 4;
|
case 0x49: //BC.Low = BC.Low;
|
||||||
|
return 4;
|
||||||
case 0x4A: BC.Low = DE.High; return 4;
|
case 0x4A: BC.Low = DE.High; return 4;
|
||||||
case 0x4B: BC.Low = DE.Low; return 4;
|
case 0x4B: BC.Low = DE.Low; return 4;
|
||||||
case 0x4C: BC.Low = HL.High; return 4;
|
case 0x4C: BC.Low = HL.High; return 4;
|
||||||
@@ -724,7 +726,8 @@ namespace Core.Cpu
|
|||||||
// --- LD D, r ---
|
// --- LD D, r ---
|
||||||
case 0x50: DE.High = BC.High; return 4;
|
case 0x50: DE.High = BC.High; return 4;
|
||||||
case 0x51: DE.High = BC.Low; return 4;
|
case 0x51: DE.High = BC.Low; return 4;
|
||||||
case 0x52: DE.High = DE.High; return 4;
|
case 0x52: //DE.High = DE.High;
|
||||||
|
return 4;
|
||||||
case 0x53: DE.High = DE.Low; return 4;
|
case 0x53: DE.High = DE.Low; return 4;
|
||||||
case 0x54: DE.High = HL.High; return 4;
|
case 0x54: DE.High = HL.High; return 4;
|
||||||
case 0x55: DE.High = HL.Low; return 4;
|
case 0x55: DE.High = HL.Low; return 4;
|
||||||
@@ -735,7 +738,8 @@ namespace Core.Cpu
|
|||||||
case 0x58: DE.Low = BC.High; return 4;
|
case 0x58: DE.Low = BC.High; return 4;
|
||||||
case 0x59: DE.Low = BC.Low; return 4;
|
case 0x59: DE.Low = BC.Low; return 4;
|
||||||
case 0x5A: DE.Low = DE.High; return 4;
|
case 0x5A: DE.Low = DE.High; return 4;
|
||||||
case 0x5B: DE.Low = DE.Low; return 4;
|
case 0x5B: //DE.Low = DE.Low;
|
||||||
|
return 4;
|
||||||
case 0x5C: DE.Low = HL.High; return 4;
|
case 0x5C: DE.Low = HL.High; return 4;
|
||||||
case 0x5D: DE.Low = HL.Low; return 4;
|
case 0x5D: DE.Low = HL.Low; return 4;
|
||||||
case 0x5E: DE.Low = _memory.Read(HL.Word); return 7;
|
case 0x5E: DE.Low = _memory.Read(HL.Word); return 7;
|
||||||
@@ -746,7 +750,8 @@ namespace Core.Cpu
|
|||||||
case 0x61: HL.High = BC.Low; return 4;
|
case 0x61: HL.High = BC.Low; return 4;
|
||||||
case 0x62: HL.High = DE.High; return 4;
|
case 0x62: HL.High = DE.High; return 4;
|
||||||
case 0x63: HL.High = DE.Low; return 4;
|
case 0x63: HL.High = DE.Low; return 4;
|
||||||
case 0x64: HL.High = HL.High; return 4;
|
case 0x64: //HL.High = HL.High;
|
||||||
|
return 4;
|
||||||
case 0x65: HL.High = HL.Low; return 4;
|
case 0x65: HL.High = HL.Low; return 4;
|
||||||
case 0x66: HL.High = _memory.Read(HL.Word); return 7;
|
case 0x66: HL.High = _memory.Read(HL.Word); return 7;
|
||||||
case 0x67: HL.High = AF.High; return 4;
|
case 0x67: HL.High = AF.High; return 4;
|
||||||
@@ -757,7 +762,8 @@ namespace Core.Cpu
|
|||||||
case 0x6A: HL.Low = DE.High; return 4;
|
case 0x6A: HL.Low = DE.High; return 4;
|
||||||
case 0x6B: HL.Low = DE.Low; return 4;
|
case 0x6B: HL.Low = DE.Low; return 4;
|
||||||
case 0x6C: HL.Low = HL.High; return 4;
|
case 0x6C: HL.Low = HL.High; return 4;
|
||||||
case 0x6D: HL.Low = HL.Low; return 4;
|
case 0x6D: //HL.Low = HL.Low;
|
||||||
|
return 4;
|
||||||
case 0x6E: HL.Low = _memory.Read(HL.Word); return 7;
|
case 0x6E: HL.Low = _memory.Read(HL.Word); return 7;
|
||||||
case 0x6F: HL.Low = AF.High; return 4;
|
case 0x6F: HL.Low = AF.High; return 4;
|
||||||
|
|
||||||
@@ -778,7 +784,8 @@ namespace Core.Cpu
|
|||||||
case 0x7C: AF.High = HL.High; return 4;
|
case 0x7C: AF.High = HL.High; return 4;
|
||||||
case 0x7D: AF.High = HL.Low; return 4;
|
case 0x7D: AF.High = HL.Low; return 4;
|
||||||
case 0x7E: AF.High = _memory.Read(HL.Word); return 7;
|
case 0x7E: AF.High = _memory.Read(HL.Word); return 7;
|
||||||
case 0x7F: AF.High = AF.High; return 4;
|
case 0x7F: //AF.High = AF.High;
|
||||||
|
return 4;
|
||||||
case 0x80: Add(BC.High); return 4; // ADD A, B
|
case 0x80: Add(BC.High); return 4; // ADD A, B
|
||||||
case 0x81: Add(BC.Low); return 4; // ADD A, C
|
case 0x81: Add(BC.Low); return 4; // ADD A, C
|
||||||
case 0x82: Add(DE.High); return 4; // ADD A, D
|
case 0x82: Add(DE.High); return 4; // ADD A, D
|
||||||
@@ -1144,6 +1151,11 @@ 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 0x73: // LD (nn), SP
|
||||||
|
ushort dest73 = FetchWord();
|
||||||
|
_memory.Write(dest73, (byte)SP);
|
||||||
|
_memory.Write((ushort)(dest73 + 1), (byte)(SP >> 8));
|
||||||
|
return 20;
|
||||||
case 0xB0: // LDIR
|
case 0xB0: // LDIR
|
||||||
// 1. Read byte from (HL)
|
// 1. Read byte from (HL)
|
||||||
val = _memory.Read(HL.Word);
|
val = _memory.Read(HL.Word);
|
||||||
@@ -1207,67 +1219,77 @@ namespace Core.Cpu
|
|||||||
private int ExecuteCBPrefix()
|
private int ExecuteCBPrefix()
|
||||||
{
|
{
|
||||||
byte cbOpcode = FetchByte();
|
byte cbOpcode = FetchByte();
|
||||||
byte memVal;
|
|
||||||
|
|
||||||
switch (cbOpcode)
|
// Extract the exact same mathematical properties
|
||||||
|
int operation = cbOpcode >> 6; // 00 = Shift, 01 = BIT, 10 = RES, 11 = SET
|
||||||
|
int bitIndex = (cbOpcode >> 3) & 0x07; // Extracts a number 0-7
|
||||||
|
int regIndex = cbOpcode & 0x07; // Extracts register index 0-7
|
||||||
|
|
||||||
|
byte bitMask = (byte)(1 << bitIndex);
|
||||||
|
|
||||||
|
// --- PHASE 1: Fetch the target value ---
|
||||||
|
byte val = 0;
|
||||||
|
switch (regIndex)
|
||||||
{
|
{
|
||||||
case 0x7E: // BIT 7, (HL)
|
case 0: val = BC.High; break;
|
||||||
byte memValBit7 = _memory.Read(HL.Word);
|
case 1: val = BC.Low; break;
|
||||||
|
case 2: val = DE.High; break;
|
||||||
|
case 3: val = DE.Low; break;
|
||||||
|
case 4: val = HL.High; break;
|
||||||
|
case 5: val = HL.Low; break;
|
||||||
|
case 6: val = _memory.Read(HL.Word); break; // The 0x110 (HL) exception
|
||||||
|
case 7: val = AF.High; break;
|
||||||
|
}
|
||||||
|
|
||||||
// Preserve ONLY the Carry Flag (Bit 0). This clears N and everything else.
|
// --- PHASE 2: Perform the bitwise math ---
|
||||||
AF.Low &= 0x01;
|
switch (operation)
|
||||||
|
{
|
||||||
|
case 1: // ALL BIT Instructions
|
||||||
|
AF.Low &= 0x01; // Preserve ONLY Carry
|
||||||
|
AF.Low |= 0x10; // Set Half-Carry
|
||||||
|
|
||||||
// Half-Carry (Bit 4) is ALWAYS set to 1 for Z80 BIT instructions
|
if ((val & bitMask) == 0)
|
||||||
AF.Low |= 0x10;
|
|
||||||
|
|
||||||
// Test Bit 7 (0x80 is Binary 1000 0000)
|
|
||||||
if ((memValBit7 & 0x80) == 0)
|
|
||||||
{
|
{
|
||||||
// If the bit is 0, turn ON the Zero Flag (Bit 6)
|
AF.Low |= 0x44; // Set Zero and P/V
|
||||||
AF.Low |= 0x40;
|
|
||||||
|
|
||||||
// Parity/Overflow (Bit 2) is historically set along with the Zero flag on BIT
|
|
||||||
AF.Low |= 0x04;
|
|
||||||
}
|
}
|
||||||
else
|
else if (bitIndex == 7)
|
||||||
{
|
{
|
||||||
// If testing Bit 7 and it is 1, the Sign Flag (Bit 7) perfectly mirrors it
|
AF.Low |= 0x80; // If testing Bit 7 and it is 1, set Sign
|
||||||
AF.Low |= 0x80;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 12;
|
// BIT (HL) takes 12 T-States. Standard register BIT takes 8.
|
||||||
case 0x86: // RES 0, (HL)
|
return (regIndex == 6) ? 12 : 8;
|
||||||
memVal = _memory.Read(HL.Word);
|
|
||||||
|
|
||||||
// 0xFE is Binary 1111 1110.
|
case 2: // ALL RES Instructions
|
||||||
// ANDing preserves all bits except Bit 0, which becomes 0.
|
val &= (byte)(~bitMask);
|
||||||
memVal &= 0xFE;
|
break; // Proceed to write-back
|
||||||
|
|
||||||
_memory.Write(HL.Word, memVal);
|
case 3: // ALL SET Instructions
|
||||||
return 15;
|
val |= bitMask;
|
||||||
case 0xAE: // RES 5, (HL)
|
break; // Proceed to write-back
|
||||||
memVal = _memory.Read(HL.Word);
|
|
||||||
|
|
||||||
// 0xDF is Binary 1101 1111
|
case 0: // ALL Shift/Rotate Instructions
|
||||||
// ANDing preserves all other bits while forcing Bit 5 to 0
|
throw new NotImplementedException($"CB Shift/Rotate opcode {cbOpcode:X2} at PC 0x{(PC - 1):X4} not implemented!");
|
||||||
memVal &= 0xDF;
|
|
||||||
|
|
||||||
_memory.Write(HL.Word, memVal);
|
|
||||||
return 15;
|
|
||||||
|
|
||||||
case 0xC6: // SET 0, (HL)
|
|
||||||
memVal = _memory.Read(HL.Word);
|
|
||||||
|
|
||||||
// 0x01 is Binary 0000 0001
|
|
||||||
// ORing forces Bit 0 to 1 and perfectly preserves all other bits
|
|
||||||
memVal |= 0x01;
|
|
||||||
|
|
||||||
_memory.Write(HL.Word, memVal);
|
|
||||||
return 15;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"CB prefix opcode 0x{cbOpcode:X2} at PC 0x{(PC - 1):X4} is not implemented.");
|
throw new Exception("Invalid CB operation.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- PHASE 3: Write back the modified value (RES and SET only) ---
|
||||||
|
switch (regIndex)
|
||||||
|
{
|
||||||
|
case 0: BC.High = val; break;
|
||||||
|
case 1: BC.Low = val; break;
|
||||||
|
case 2: DE.High = val; break;
|
||||||
|
case 3: DE.Low = val; break;
|
||||||
|
case 4: HL.High = val; break;
|
||||||
|
case 5: HL.Low = val; break;
|
||||||
|
case 6: _memory.Write(HL.Word, val); break;
|
||||||
|
case 7: AF.High = val; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RES/SET (HL) takes 15 T-States. Standard register RES/SET takes 8.
|
||||||
|
return (regIndex == 6) ? 15 : 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int ExecuteFDPrefix()
|
private int ExecuteFDPrefix()
|
||||||
@@ -1323,11 +1345,11 @@ namespace Core.Cpu
|
|||||||
return 19; // Takes 19 T-States
|
return 19; // Takes 19 T-States
|
||||||
}
|
}
|
||||||
case 0x75: // LD (IY+d), L
|
case 0x75: // LD (IY+d), L
|
||||||
sbyte offset75 = (sbyte)FetchByte();
|
sbyte offset75 = (sbyte)FetchByte();
|
||||||
targetAddress = (ushort)(IY.Word + offset75);
|
targetAddress = (ushort)(IY.Word + offset75);
|
||||||
// Write the low byte of HL to memory
|
// Write the low byte of HL to memory
|
||||||
_memory.Write(targetAddress, HL.Low);
|
_memory.Write(targetAddress, HL.Low);
|
||||||
return 19;
|
return 19;
|
||||||
case 0x86: // ADD A, (IY+d)
|
case 0x86: // ADD A, (IY+d)
|
||||||
{
|
{
|
||||||
sbyte displacementAdd = (sbyte)FetchByte();
|
sbyte displacementAdd = (sbyte)FetchByte();
|
||||||
@@ -1340,110 +1362,48 @@ namespace Core.Cpu
|
|||||||
}
|
}
|
||||||
case 0xCB: // The FD CB nested prefix
|
case 0xCB: // The FD CB nested prefix
|
||||||
{
|
{
|
||||||
sbyte offsetCB = (sbyte)FetchByte(); // This is the '01'
|
sbyte displacement = (sbyte)FetchByte();
|
||||||
byte bitOpcode = FetchByte(); // This is the 'CE'
|
byte cbOpcode = FetchByte();
|
||||||
targetAddress = (ushort)(IY.Word + offsetCB);
|
targetAddress = (ushort)(IY.Word + displacement);
|
||||||
|
memVal = _memory.Read(targetAddress);
|
||||||
|
|
||||||
switch (bitOpcode)
|
// 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 (e.g., 0x01, 0x02, 0x80)
|
||||||
|
|
||||||
|
switch (operation)
|
||||||
{
|
{
|
||||||
case 0x46: // BIT 0, (IY+d)
|
case 1: // ALL BIT Instructions
|
||||||
byte memValBit0 = _memory.Read(targetAddress);
|
AF.Low &= 0x01; // Preserve ONLY Carry
|
||||||
|
AF.Low |= 0x10; // Set Half-Carry
|
||||||
|
|
||||||
// Preserve the existing Carry Flag (Bit 0)
|
if ((memVal & bitMask) == 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 |= 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
AF.Low = newFlags;
|
|
||||||
return 20;
|
return 20;
|
||||||
case 0x4E: // BIT 1, (IY+d)
|
|
||||||
{
|
|
||||||
byte memValBit = _memory.Read(targetAddress);
|
|
||||||
|
|
||||||
// Check if bit 1 is 0
|
case 2: // ALL RES Instructions
|
||||||
bool bitIsZero = (memValBit & 0x02) == 0;
|
memVal &= (byte)(~bitMask); // Invert mask and AND it to clear the bit
|
||||||
|
|
||||||
// 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 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);
|
_memory.Write(targetAddress, memVal);
|
||||||
return 23;
|
return 23;
|
||||||
case 0xE6: // SET 4, (IY+d)
|
|
||||||
byte memValSet4 = _memory.Read(targetAddress);
|
|
||||||
|
|
||||||
// 0x10 is Binary 0001 0000
|
case 3: // ALL SET Instructions
|
||||||
// ORing perfectly preserves all other bits while forcing Bit 4 to 1
|
memVal |= bitMask; // OR the mask to force the bit to 1
|
||||||
memValSet4 |= 0x10;
|
_memory.Write(targetAddress, memVal);
|
||||||
|
|
||||||
_memory.Write(targetAddress, memValSet4);
|
|
||||||
return 23;
|
return 23;
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
// Shift/Rotate instructions will go here later
|
||||||
|
throw new NotImplementedException($"FD CB Shift/Rotate opcode {cbOpcode:X2} at PC 0x{(PC - 1):X4} not implemented!");
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"FD CB opcode {bitOpcode:X2} at PC 0x{(PC - 1):X4} not implemented!");
|
throw new Exception("Invalid bitwise operation.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -1452,3 +1412,182 @@ 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!");
|
||||||
|
//}
|
||||||
@@ -249,6 +249,7 @@ namespace Desktop
|
|||||||
byte opcode = _memoryBus.Read(currentPc);
|
byte opcode = _memoryBus.Read(currentPc);
|
||||||
string mnemonic;
|
string mnemonic;
|
||||||
int instructionLength = 1; // Default to 1
|
int instructionLength = 1; // Default to 1
|
||||||
|
byte cbOp = 0;
|
||||||
|
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
@@ -668,7 +669,7 @@ namespace Desktop
|
|||||||
mnemonic = "RET";
|
mnemonic = "RET";
|
||||||
break;
|
break;
|
||||||
case 0xCB:
|
case 0xCB:
|
||||||
byte cbOp = _memoryBus.Read((ushort)(currentPc + 1));
|
cbOp = _memoryBus.Read((ushort)(currentPc + 1));
|
||||||
if (cbOp == 0x7E)
|
if (cbOp == 0x7E)
|
||||||
{
|
{
|
||||||
mnemonic = "BIT 7, (HL)";
|
mnemonic = "BIT 7, (HL)";
|
||||||
@@ -709,7 +710,6 @@ namespace Desktop
|
|||||||
case 0xD9:
|
case 0xD9:
|
||||||
mnemonic = "EXX";
|
mnemonic = "EXX";
|
||||||
break;
|
break;
|
||||||
break;
|
|
||||||
case 0xd5:
|
case 0xd5:
|
||||||
mnemonic = "PUSH DE";
|
mnemonic = "PUSH DE";
|
||||||
break;
|
break;
|
||||||
@@ -781,6 +781,11 @@ namespace Desktop
|
|||||||
mnemonic = $"LD DE, (0x{addr5B:X4})";
|
mnemonic = $"LD DE, (0x{addr5B:X4})";
|
||||||
instructionLength = 4;
|
instructionLength = 4;
|
||||||
break;
|
break;
|
||||||
|
case 0x73:
|
||||||
|
ushort addr73 = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||||
|
mnemonic = $"LD (0x{addr73:X4}), SP";
|
||||||
|
instructionLength = 4;
|
||||||
|
break;
|
||||||
case 0xB0:
|
case 0xB0:
|
||||||
mnemonic = "LDIR";
|
mnemonic = "LDIR";
|
||||||
instructionLength = 2;
|
instructionLength = 2;
|
||||||
@@ -865,34 +870,22 @@ namespace Desktop
|
|||||||
}
|
}
|
||||||
else if (fdOpcode == 0xCB) // FD CB prefix
|
else if (fdOpcode == 0xCB) // FD CB prefix
|
||||||
{
|
{
|
||||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
cbOp = _memoryBus.Read((ushort)(currentPc + 1));
|
||||||
byte cbOpcode = _memoryBus.Read((ushort)(currentPc + 3));
|
|
||||||
string sign = d >= 0 ? "+" : "";
|
int opGroup = cbOp >> 6;
|
||||||
if (cbOpcode == 0x46)
|
int targetBit = (cbOp >> 3) & 0x07;
|
||||||
{
|
int regIdx = cbOp & 0x07;
|
||||||
mnemonic = $"BIT 0, (IY{sign}{d})";
|
|
||||||
}
|
// Map the 0-7 index directly to the Z80 register names
|
||||||
else if (cbOpcode == 0x4E)
|
string[] regNames = { "B", "C", "D", "E", "H", "L", "(HL)", "A" };
|
||||||
{
|
string targetReg = regNames[regIdx];
|
||||||
mnemonic = $"BIT 1, (IY{sign}{d})";
|
|
||||||
}
|
if (opGroup == 1) mnemonic = $"BIT {targetBit}, {targetReg}";
|
||||||
else if (cbOpcode == 0x86)
|
else if (opGroup == 2) mnemonic = $"RES {targetBit}, {targetReg}";
|
||||||
{
|
else if (opGroup == 3) mnemonic = $"SET {targetBit}, {targetReg}";
|
||||||
mnemonic = $"RES 0, (IY{sign}{d})";
|
else mnemonic = $"CB SHIFT/ROTATE (0x{cbOp:X2})";
|
||||||
}
|
|
||||||
else if (cbOpcode == 0xA6)
|
instructionLength = 2;
|
||||||
{
|
|
||||||
mnemonic = $"RES 4, (IY{sign}{d})";
|
|
||||||
}
|
|
||||||
else if (cbOpcode == 0xCE)
|
|
||||||
{
|
|
||||||
mnemonic = $"SET 1, (IY{sign}{d})";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mnemonic = $"FD CB {d:X2} {cbOpcode:X2}"; // Fallback
|
|
||||||
}
|
|
||||||
instructionLength = 4;
|
|
||||||
}
|
}
|
||||||
else if (fdOpcode == 0x71) // LD (IY+d), C
|
else if (fdOpcode == 0x71) // LD (IY+d), C
|
||||||
{
|
{
|
||||||
@@ -908,37 +901,6 @@ namespace Desktop
|
|||||||
mnemonic = $"LD (IY{sign}{d}), L";
|
mnemonic = $"LD (IY{sign}{d}), L";
|
||||||
instructionLength = 3;
|
instructionLength = 3;
|
||||||
}
|
}
|
||||||
else if (fdOpcode == 0xCB) // FD CB prefix
|
|
||||||
{
|
|
||||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
|
||||||
byte cbOpcode = _memoryBus.Read((ushort)(currentPc + 3));
|
|
||||||
string sign = d >= 0 ? "+" : "";
|
|
||||||
if (cbOpcode == 0x8E)
|
|
||||||
{
|
|
||||||
mnemonic = $"RES 1, (IY{sign}{d})";
|
|
||||||
}
|
|
||||||
else if (cbOpcode == 0xAE)
|
|
||||||
{
|
|
||||||
mnemonic = $"RES 5, (IY{sign}{d})";
|
|
||||||
}
|
|
||||||
else if (cbOpcode == 0xC6)
|
|
||||||
{
|
|
||||||
mnemonic = $"SET 0, (IY{sign}{d})";
|
|
||||||
}
|
|
||||||
else if (cbOpcode == 0xCE)
|
|
||||||
{
|
|
||||||
mnemonic = $"SET 1, (IY{sign}{d})";
|
|
||||||
}
|
|
||||||
else if (cbOpcode == 0xE6)
|
|
||||||
{
|
|
||||||
mnemonic = $"SET 4, (IY{sign}{d})";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mnemonic = $"FD CB {d:X2} {cbOpcode:X2}"; // Fallback
|
|
||||||
}
|
|
||||||
instructionLength = 4;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mnemonic = $"FD PREFIX UNKNOWN (0x{fdOpcode:X2})";
|
mnemonic = $"FD PREFIX UNKNOWN (0x{fdOpcode:X2})";
|
||||||
|
|||||||
Reference in New Issue
Block a user