Snapshot and TAP quick loading working. Manic Miner fully emulated

This commit is contained in:
2026-04-22 11:46:57 +01:00
parent e52cdeac54
commit b50f7a79da
2 changed files with 114 additions and 2 deletions

View File

@@ -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

View File

@@ -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));