Snapshot and TAP quick loading working. Manic Miner fully emulated
This commit is contained in:
@@ -1021,7 +1021,12 @@ namespace Core.Cpu
|
|||||||
PC = (ushort)(PC + offset);
|
PC = (ushort)(PC + offset);
|
||||||
return 12; // Jump taken
|
return 12; // Jump taken
|
||||||
}
|
}
|
||||||
return 7; // Jump not taken
|
return 7;
|
||||||
|
case 0x31: // LD SP, nn
|
||||||
|
{
|
||||||
|
SP = FetchWord();
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
case 0x32: // LD (nn), A
|
case 0x32: // LD (nn), A
|
||||||
{
|
{
|
||||||
ushort destAddress = FetchWord();
|
ushort destAddress = FetchWord();
|
||||||
@@ -1608,6 +1613,34 @@ namespace Core.Cpu
|
|||||||
case 0x56: // IM 1
|
case 0x56: // IM 1
|
||||||
InterruptMode = 1;
|
InterruptMode = 1;
|
||||||
return 8;
|
return 8;
|
||||||
|
case 0x58: // IN E, (C)
|
||||||
|
// 1. Read from the I/O port.
|
||||||
|
// CRITICAL: We must pass the FULL BC register, not just C!
|
||||||
|
byte inVal58 = ReadPort(BC.Word);
|
||||||
|
|
||||||
|
// 2. Store the hardware data in register E (Low byte of DE)
|
||||||
|
DE.Low = inVal58;
|
||||||
|
|
||||||
|
// 3. Update the Flags Register (F)
|
||||||
|
// The Carry flag (C) is strictly preserved. H and N are always reset to 0.
|
||||||
|
byte flags58 = (byte)(AF.Low & 0x01);
|
||||||
|
|
||||||
|
if ((inVal58 & 0x80) != 0) flags58 |= 0x80; // S: Sign flag
|
||||||
|
if (inVal58 == 0) flags58 |= 0x40; // Z: Zero flag
|
||||||
|
|
||||||
|
// P/V: Parity flag. Collapse the bits to check if the number of 1s is even
|
||||||
|
byte p58 = inVal58;
|
||||||
|
p58 ^= (byte)(p58 >> 4);
|
||||||
|
p58 ^= (byte)(p58 >> 2);
|
||||||
|
p58 ^= (byte)(p58 >> 1);
|
||||||
|
if ((p58 & 1) == 0) flags58 |= 0x04; // Set if Parity is Even
|
||||||
|
|
||||||
|
// Undocumented bits 3 and 5 are copied directly from the input byte
|
||||||
|
flags58 |= (byte)(inVal58 & 0x28);
|
||||||
|
|
||||||
|
AF.Low = flags58;
|
||||||
|
|
||||||
|
return 12;
|
||||||
case 0x5A: // ADC HL, DE
|
case 0x5A: // ADC HL, DE
|
||||||
Adc16(DE.Word);
|
Adc16(DE.Word);
|
||||||
return 15;
|
return 15;
|
||||||
@@ -2448,6 +2481,36 @@ namespace Core.Cpu
|
|||||||
|
|
||||||
switch (opcode)
|
switch (opcode)
|
||||||
{
|
{
|
||||||
|
case 0x19: // ADD IY, DE
|
||||||
|
// 1. Perform the 16-bit addition using a 32-bit integer to catch the carry
|
||||||
|
int result19 = IY.Word + DE.Word;
|
||||||
|
|
||||||
|
// 2. Update the Flags Register (F)
|
||||||
|
// We start by stripping out N, H, C, and bits 3/5, but strictly PRESERVING S, Z, and P/V (0xC4)
|
||||||
|
byte flags19 = (byte)(AF.Low & 0xC4);
|
||||||
|
|
||||||
|
// H: Set if there is a carry from bit 11
|
||||||
|
if (((IY.Word & 0x0FFF) + (DE.Word & 0x0FFF)) > 0x0FFF)
|
||||||
|
{
|
||||||
|
flags19 |= 0x10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// C: Set if the result overflows 16 bits (carry from bit 15)
|
||||||
|
if (result19 > 0xFFFF)
|
||||||
|
{
|
||||||
|
flags19 |= 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Undocumented bits 3 and 5 are copied directly from the high byte of the result
|
||||||
|
flags19 |= (byte)((result19 >> 8) & 0x28);
|
||||||
|
|
||||||
|
// N (Subtract) is naturally left as 0 because of our initial bitmask
|
||||||
|
AF.Low = flags19;
|
||||||
|
|
||||||
|
// 3. Store the clean 16-bit result back into IY
|
||||||
|
IY.Word = (ushort)result19;
|
||||||
|
|
||||||
|
return 15;
|
||||||
case 0x21: // LD IY, nn
|
case 0x21: // LD IY, nn
|
||||||
IY.Word = FetchWord();
|
IY.Word = FetchWord();
|
||||||
return 14;
|
return 14;
|
||||||
@@ -2550,6 +2613,17 @@ namespace Core.Cpu
|
|||||||
// 3. Read the memory and store it in E (the low byte of DE)
|
// 3. Read the memory and store it in E (the low byte of DE)
|
||||||
DE.Low = ReadMemory(address5E);
|
DE.Low = ReadMemory(address5E);
|
||||||
|
|
||||||
|
return 19;
|
||||||
|
case 0x66: // LD H, (IY+d)
|
||||||
|
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||||
|
sbyte offset66 = (sbyte)FetchByte();
|
||||||
|
|
||||||
|
// 2. Calculate the exact memory address (IY + offset)
|
||||||
|
ushort address66 = (ushort)(IY.Word + offset66);
|
||||||
|
|
||||||
|
// 3. Read the byte from memory and drop it into the H register (High byte of HL)
|
||||||
|
HL.High = ReadMemory(address66);
|
||||||
|
|
||||||
return 19;
|
return 19;
|
||||||
case 0x6E: // LD L, (IY+d)
|
case 0x6E: // LD L, (IY+d)
|
||||||
sbyte displacementVal = (sbyte)FetchByte();
|
sbyte displacementVal = (sbyte)FetchByte();
|
||||||
@@ -2602,6 +2676,17 @@ namespace Core.Cpu
|
|||||||
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
|
||||||
WriteMemory(targetAddress, HL.Low);
|
WriteMemory(targetAddress, HL.Low);
|
||||||
|
return 19;
|
||||||
|
case 0x77: // LD (IY+d), A
|
||||||
|
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||||
|
sbyte offset77 = (sbyte)FetchByte();
|
||||||
|
|
||||||
|
// 2. Calculate the exact memory address (IY + offset)
|
||||||
|
ushort address77 = (ushort)(IY.Word + offset77);
|
||||||
|
|
||||||
|
// 3. Write the Accumulator (A) into memory
|
||||||
|
WriteMemory(address77, AF.High);
|
||||||
|
|
||||||
return 19;
|
return 19;
|
||||||
case 0x7E: // LD A, (IY+d)
|
case 0x7E: // LD A, (IY+d)
|
||||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||||
|
|||||||
@@ -326,6 +326,10 @@ namespace Desktop
|
|||||||
mnemonic = $"JR NC, 0x{dest:X4}";
|
mnemonic = $"JR NC, 0x{dest:X4}";
|
||||||
instructionLength = 2;
|
instructionLength = 2;
|
||||||
break;
|
break;
|
||||||
|
case 0x31:
|
||||||
|
mnemonic = "LD SP, nn";
|
||||||
|
instructionLength = 3;
|
||||||
|
break;
|
||||||
case 0x32:
|
case 0x32:
|
||||||
{
|
{
|
||||||
ushort addr32 = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
|
ushort addr32 = (ushort)(_memoryBus.Read((ushort)(currentPc + 1)) | (_memoryBus.Read((ushort)(currentPc + 2)) << 8));
|
||||||
@@ -1029,6 +1033,10 @@ namespace Desktop
|
|||||||
mnemonic = "IM 1";
|
mnemonic = "IM 1";
|
||||||
instructionLength = 2;
|
instructionLength = 2;
|
||||||
break;
|
break;
|
||||||
|
case 0x58:
|
||||||
|
mnemonic = "IN E, (C)";
|
||||||
|
instructionLength = 2;
|
||||||
|
break;
|
||||||
case 0x5A: mnemonic = "ADC HL, DE"; instructionLength = 2; break;
|
case 0x5A: mnemonic = "ADC HL, DE"; instructionLength = 2; break;
|
||||||
case 0x5B:
|
case 0x5B:
|
||||||
ushort addr5B = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
ushort addr5B = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||||
@@ -1105,7 +1113,12 @@ namespace Desktop
|
|||||||
{
|
{
|
||||||
byte fdOpcode = _memoryBus.Read((ushort)(currentPc + 1));
|
byte fdOpcode = _memoryBus.Read((ushort)(currentPc + 1));
|
||||||
|
|
||||||
if (fdOpcode == 0x21) // LD IY, nn
|
if (fdOpcode == 0x19) // ADD IY, DE
|
||||||
|
{
|
||||||
|
mnemonic = $"ADD IY, DE";
|
||||||
|
instructionLength = 2;
|
||||||
|
}
|
||||||
|
else if (fdOpcode == 0x21) // LD IY, nn
|
||||||
{
|
{
|
||||||
ushort iyVal = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
ushort iyVal = (ushort)(_memoryBus.Read((ushort)(currentPc + 2)) | (_memoryBus.Read((ushort)(currentPc + 3)) << 8));
|
||||||
mnemonic = $"LD IY, 0x{iyVal:X4}";
|
mnemonic = $"LD IY, 0x{iyVal:X4}";
|
||||||
@@ -1169,6 +1182,13 @@ namespace Desktop
|
|||||||
mnemonic = $"LD E, (IY{sign}{d})";
|
mnemonic = $"LD E, (IY{sign}{d})";
|
||||||
instructionLength = 3;
|
instructionLength = 3;
|
||||||
}
|
}
|
||||||
|
else if (fdOpcode == 0x66)
|
||||||
|
{
|
||||||
|
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
|
string sign = d >= 0 ? "+" : "";
|
||||||
|
mnemonic = $"LD H, (IY{sign}{d})";
|
||||||
|
instructionLength = 3;
|
||||||
|
}
|
||||||
else if (fdOpcode == 0x6E)
|
else if (fdOpcode == 0x6E)
|
||||||
{
|
{
|
||||||
sbyte offsetL = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
sbyte offsetL = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
@@ -1198,6 +1218,13 @@ namespace Desktop
|
|||||||
mnemonic = $"LD (IY{sign}{d}), H";
|
mnemonic = $"LD (IY{sign}{d}), H";
|
||||||
instructionLength = 3;
|
instructionLength = 3;
|
||||||
}
|
}
|
||||||
|
else if (fdOpcode == 0x77) // LD (IY+d), A
|
||||||
|
{
|
||||||
|
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
|
string sign = d >= 0 ? "+" : "";
|
||||||
|
mnemonic = $"LD (IY{sign}{d}), A";
|
||||||
|
instructionLength = 3;
|
||||||
|
}
|
||||||
else if (fdOpcode == 0x7E) // LD A, (IY+d)
|
else if (fdOpcode == 0x7E) // LD A, (IY+d)
|
||||||
{
|
{
|
||||||
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
sbyte d = (sbyte)_memoryBus.Read((ushort)(currentPc + 2));
|
||||||
|
|||||||
Reference in New Issue
Block a user