Implemented a few more OpCodes. Manic Miner sounds great!
This commit is contained in:
@@ -1776,6 +1776,45 @@ namespace Core.Cpu
|
||||
return 21; // Looping
|
||||
}
|
||||
return 16;
|
||||
case 0xB1: // CPIR
|
||||
// 1. Read the memory byte at HL
|
||||
byte memValB1 = ReadMemory(HL.Word);
|
||||
|
||||
// 2. Calculate the difference (A - (HL)) to set the flags
|
||||
byte resultB1 = (byte)(AF.High - memValB1);
|
||||
|
||||
// 3. Increment HL and Decrement BC
|
||||
HL.Word++;
|
||||
BC.Word--;
|
||||
|
||||
// 4. Update the Flags (F Register / AF.Low)
|
||||
// CPIR modifies S, Z, H, P/V, and N, but strictly PRESERVES the C flag.
|
||||
byte currentCarry = (byte)(AF.Low & 0x01);
|
||||
byte newFlagsB1 = currentCarry;
|
||||
|
||||
newFlagsB1 |= 0x02; // N flag is always set to 1 for CPIR
|
||||
|
||||
if (BC.Word != 0) newFlagsB1 |= 0x04; // P/V is set if BC is not 0 (Counter not empty)
|
||||
if (((AF.High ^ memValB1 ^ resultB1) & 0x10) != 0) newFlagsB1 |= 0x10; // H flag (Half-borrow)
|
||||
if (resultB1 == 0) newFlagsB1 |= 0x40; // Z flag (Match found!)
|
||||
if ((resultB1 & 0x80) != 0) newFlagsB1 |= 0x80; // S flag (Sign)
|
||||
|
||||
// (Note: Undocumented bits 3 and 5 are ignored here as they are highly esoteric for CPIR,
|
||||
// but you can add `newFlagsB1 |= (byte)(resultB1 & 0x28);` if your engine tracks them strictly).
|
||||
|
||||
AF.Low = newFlagsB1;
|
||||
|
||||
// 5. The Repeat Check
|
||||
// If we haven't hit 0 in BC, AND we didn't find a match (result != 0)...
|
||||
if (BC.Word != 0 && resultB1 != 0)
|
||||
{
|
||||
// Rewind the Program Counter by 2 bytes (back to the 0xED prefix)
|
||||
PC -= 2;
|
||||
return 21; // 21 T-States for a repeating loop
|
||||
}
|
||||
|
||||
// If we found the byte or BC hit 0, the loop ends.
|
||||
return 16; // 16 T-States when the loop terminates
|
||||
case 0xB8: // LDDR
|
||||
// 1. Read byte from (HL)
|
||||
val = ReadMemory(HL.Word);
|
||||
@@ -2182,6 +2221,9 @@ namespace Core.Cpu
|
||||
HL.Low = ReadMemory(address6E);
|
||||
|
||||
return 19;
|
||||
case 0x6F: // LD IXL, A
|
||||
IX.Low = AF.High;
|
||||
return 8;
|
||||
case 0x70: // LD (IX+d), B
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset70 = (sbyte)FetchByte();
|
||||
@@ -2409,6 +2451,10 @@ namespace Core.Cpu
|
||||
case 0x21: // LD IY, nn
|
||||
IY.Word = FetchWord();
|
||||
return 14;
|
||||
case 0x23: // INC IY
|
||||
// Increment the full 16-bit register. The F register remains completely untouched.
|
||||
IY.Word++;
|
||||
return 10;
|
||||
case 0x34: // INC (IY+d)
|
||||
// 1. Fetch displacement and calculate memory address
|
||||
sbyte offset34 = (sbyte)FetchByte();
|
||||
@@ -2556,6 +2602,17 @@ namespace Core.Cpu
|
||||
targetAddress = (ushort)(IY.Word + offset75);
|
||||
// Write the low byte of HL to memory
|
||||
WriteMemory(targetAddress, HL.Low);
|
||||
return 19;
|
||||
case 0x7E: // LD A, (IY+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset7E = (sbyte)FetchByte();
|
||||
|
||||
// 2. Calculate the exact memory address (IY + offset)
|
||||
ushort address7E = (ushort)(IY.Word + offset7E);
|
||||
|
||||
// 3. Read the byte from memory and drop it straight into the Accumulator (A)
|
||||
AF.High = ReadMemory(address7E);
|
||||
|
||||
return 19;
|
||||
case 0x86: // ADD A, (IY+d)
|
||||
{
|
||||
@@ -2604,6 +2661,43 @@ namespace Core.Cpu
|
||||
AF.Low = newFlags;
|
||||
AF.High = (byte)result;
|
||||
|
||||
return 19;
|
||||
case 0xA6: // AND (IY+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offsetA6 = (sbyte)FetchByte();
|
||||
|
||||
// 2. Calculate the exact memory address (IY + offset)
|
||||
ushort addressA6 = (ushort)(IY.Word + offsetA6);
|
||||
|
||||
// 3. Read the operand from memory
|
||||
byte operandA6 = ReadMemory(addressA6);
|
||||
|
||||
// 4. Perform the logical AND with the Accumulator
|
||||
AF.High &= operandA6;
|
||||
|
||||
// 5. Update the Flags Register (F)
|
||||
byte flagsA6 = 0;
|
||||
|
||||
if ((AF.High & 0x80) != 0) flagsA6 |= 0x80; // S: Sign flag (Set if result is negative)
|
||||
if (AF.High == 0) flagsA6 |= 0x40; // Z: Zero flag (Set if result is 0)
|
||||
|
||||
flagsA6 |= 0x10; // H: Half-carry is ALWAYS set to 1 for Z80 AND instructions
|
||||
|
||||
// P/V: Parity flag. We collapse the bits to check if the number of 1s is even
|
||||
byte p = AF.High;
|
||||
p ^= (byte)(p >> 4);
|
||||
p ^= (byte)(p >> 2);
|
||||
p ^= (byte)(p >> 1);
|
||||
if ((p & 1) == 0) flagsA6 |= 0x04; // Set if Parity is Even
|
||||
|
||||
// Undocumented bits 3 and 5 are copied directly from the resulting Accumulator
|
||||
flagsA6 |= (byte)(AF.High & 0x28);
|
||||
|
||||
// N (Subtract) and C (Carry) are always reset to 0 for AND instructions,
|
||||
// which happens naturally since we started with flagsA6 = 0.
|
||||
|
||||
AF.Low = flagsA6;
|
||||
|
||||
return 19;
|
||||
case 0xBE: // CP (IY+d)
|
||||
// 1. Fetch the displacement byte and calculate the address using IY
|
||||
|
||||
@@ -835,6 +835,11 @@ namespace Desktop
|
||||
mnemonic = $"LD L, (IX{sign}{d})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (ddOpcode == 0x6F) // LD IXL, A
|
||||
{
|
||||
mnemonic = $"LD IXL, A)";
|
||||
instructionLength = 2;
|
||||
}
|
||||
else if (ddOpcode == 0x71) // LD (IX+d), B
|
||||
{
|
||||
sbyte offset = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
@@ -1058,6 +1063,10 @@ namespace Desktop
|
||||
mnemonic = "LDIR";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xB1:
|
||||
mnemonic = "CPIR";
|
||||
instructionLength = 2;
|
||||
break;
|
||||
case 0xB8:
|
||||
mnemonic = "LDDR";
|
||||
instructionLength = 2;
|
||||
@@ -1102,6 +1111,13 @@ namespace Desktop
|
||||
mnemonic = $"LD IY, 0x{iyVal:X4}";
|
||||
instructionLength = 4;
|
||||
}
|
||||
else if (fdOpcode == 0x34) // INC IY
|
||||
{
|
||||
//sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
//string sign = d >= 0 ? "+" : "";
|
||||
mnemonic = $"INC IY";
|
||||
instructionLength = 2;
|
||||
}
|
||||
else if (fdOpcode == 0x34) // INC (IY+d)
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
@@ -1182,6 +1198,13 @@ namespace Desktop
|
||||
mnemonic = $"LD (IY{sign}{d}), H";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (fdOpcode == 0x7E) // LD A, (IY+d)
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
string sign = d >= 0 ? "+" : "";
|
||||
mnemonic = $"LD A, (IY{sign}{d})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (fdOpcode == 0x86) // ADD A, (IY+d)
|
||||
{
|
||||
sbyte dAdd = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
@@ -1197,6 +1220,13 @@ namespace Desktop
|
||||
mnemonic = $"SUB (IY{sign}{d})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (fdOpcode == 0xA6) //AND (IY+d)
|
||||
{
|
||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||
string sign = d >= 0 ? "+" : "";
|
||||
mnemonic = $"AND (IY{sign}{d})";
|
||||
instructionLength = 3;
|
||||
}
|
||||
else if (fdOpcode == 0xCB) // FD CB prefix
|
||||
{
|
||||
cbOp = _memoryBus.Read((ushort)(currentPc + 1));
|
||||
|
||||
Reference in New Issue
Block a user