Added TAP file injection. Still incomplete.
This commit is contained in:
152
Core/Cpu/Z80.cs
152
Core/Cpu/Z80.cs
@@ -40,11 +40,13 @@ namespace Core.Cpu
|
||||
// The Memory Bus
|
||||
private readonly IMemory _memory;
|
||||
private readonly IO_Bus _simpleIoBus;
|
||||
public TapManager _tapManager;
|
||||
|
||||
public Z80(IMemory memory, IO_Bus ioBus)
|
||||
public Z80(IMemory memory, IO_Bus ioBus, TapManager tapManager)
|
||||
{
|
||||
_memory = memory;
|
||||
_simpleIoBus = ioBus;
|
||||
_tapManager = tapManager;
|
||||
Reset();
|
||||
}
|
||||
|
||||
@@ -129,6 +131,12 @@ namespace Core.Cpu
|
||||
|
||||
public int Step()
|
||||
{
|
||||
if (PC == 0x0556 && _tapManager.HasBlocks)
|
||||
{
|
||||
HandleInstantTapeLoad();
|
||||
return 100; // Return a dummy number of T-States for the hijacking
|
||||
}
|
||||
|
||||
// Fetch the next opcode and increment the Program Counter
|
||||
byte opcode = _memory.Read(PC++);
|
||||
int tStates = ExecuteOpcode(opcode);
|
||||
@@ -138,6 +146,52 @@ namespace Core.Cpu
|
||||
return tStates;
|
||||
}
|
||||
|
||||
private void HandleInstantTapeLoad()
|
||||
{
|
||||
// 1. Grab the next block from the virtual cassette
|
||||
byte[] block = _tapManager.GetNextBlock();
|
||||
if (block == null) return; // Tape ended unexpectedly
|
||||
|
||||
// 2. Verify the block type.
|
||||
// The ROM passes the expected flag (0x00 for Header, 0xFF for Data) in the A register.
|
||||
byte expectedFlag = AF.High;
|
||||
if (block[0] != expectedFlag)
|
||||
{
|
||||
// Tape loading error! Simulate a failure by clearing the Carry flag and returning.
|
||||
AF.Low &= unchecked((byte)~0x01);
|
||||
ExecuteRet();
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Copy the data block straight into the Spectrum's RAM!
|
||||
// DE holds the number of bytes the ROM *wants*. We copy that much, skipping the Flag byte.
|
||||
int bytesToCopy = DE.Word;
|
||||
for (int i = 0; i < bytesToCopy; i++)
|
||||
{
|
||||
_memory.Write((ushort)(IX.Word + i), block[i + 1]);
|
||||
}
|
||||
|
||||
// 4. Update the registers exactly how the ROM would after a successful load
|
||||
IX.Word = (ushort)(IX.Word + bytesToCopy);
|
||||
DE.Word = 0;
|
||||
|
||||
// 5. Set the Carry Flag to 1 (Success)
|
||||
AF.Low |= 0x01;
|
||||
|
||||
// 6. Simulate a standard 'RET' instruction to exit the LD-BYTES routine safely
|
||||
ExecuteRet();
|
||||
}
|
||||
|
||||
// A quick helper to simulate a RET instruction manually
|
||||
private void ExecuteRet()
|
||||
{
|
||||
byte pcLow = _memory.Read(SP);
|
||||
SP++;
|
||||
byte pcHigh = _memory.Read(SP);
|
||||
SP++;
|
||||
PC = (ushort)((pcHigh << 8) | pcLow);
|
||||
}
|
||||
|
||||
// Reads a 16-bit word from the current PC (Little-Endian) and advances PC by 2
|
||||
private ushort FetchWord()
|
||||
{
|
||||
@@ -1624,6 +1678,17 @@ namespace Core.Cpu
|
||||
_memory.Write(address36, n36);
|
||||
|
||||
return 19;
|
||||
case 0x5E: // LD E, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset5E = (sbyte)FetchByte();
|
||||
|
||||
// 2. Calculate the exact memory address (IX + offset)
|
||||
ushort address5E = (ushort)(IX.Word + offset5E);
|
||||
|
||||
// 3. Read the byte from memory and drop it into the E register
|
||||
DE.Low = _memory.Read(address5E);
|
||||
|
||||
return 19; // 19 T-States
|
||||
case 0x74: // LD (IX+d), H
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset74 = (sbyte)FetchByte();
|
||||
@@ -1646,6 +1711,55 @@ namespace Core.Cpu
|
||||
_memory.Write(address75, HL.Low);
|
||||
|
||||
return 19;
|
||||
case 0x7E: // LD A, (IX+d)
|
||||
// 1. Fetch the displacement byte and cast it to a signed sbyte
|
||||
sbyte offset7E = (sbyte)FetchByte();
|
||||
|
||||
// 2. Calculate the exact memory address (IX + offset)
|
||||
ushort address7E = (ushort)(IX.Word + offset7E);
|
||||
|
||||
// 3. Read the byte from memory and drop it straight into the Accumulator (A)
|
||||
AF.High = _memory.Read(address7E);
|
||||
|
||||
return 19;
|
||||
case 0xBE: // CP (IX+d)
|
||||
// 1. Fetch the displacement byte and calculate the address
|
||||
sbyte offsetBE = (sbyte)FetchByte();
|
||||
ushort addressBE = (ushort)(IX.Word + offsetBE);
|
||||
|
||||
// 2. Read the value from memory
|
||||
byte cpVal = _memory.Read(addressBE);
|
||||
|
||||
// 3. Perform the phantom subtraction
|
||||
int aVal = AF.High;
|
||||
result = aVal - cpVal;
|
||||
|
||||
// --- 8-Bit Compare Flag Calculation (Identical to SUB) ---
|
||||
newFlags = 0;
|
||||
|
||||
// S Flag (Bit 7): Set if the phantom result is negative
|
||||
if ((result & 0x80) != 0) newFlags |= 0x80;
|
||||
|
||||
// Z Flag (Bit 6): Set if A perfectly matches the memory value (A - value == 0)
|
||||
if ((result & 0xFF) == 0) newFlags |= 0x40;
|
||||
|
||||
// H Flag (Bit 4): Set if there was a borrow from Bit 3
|
||||
if (((aVal & 0x0F) - (cpVal & 0x0F)) < 0) newFlags |= 0x10;
|
||||
|
||||
// P/V Flag (Bit 2): Set on Overflow
|
||||
if ((((aVal ^ cpVal) & (aVal ^ result)) & 0x80) != 0) newFlags |= 0x04;
|
||||
|
||||
// N Flag (Bit 1): Always set to 1 for Subtractions/Compares
|
||||
newFlags |= 0x02;
|
||||
|
||||
// C Flag (Bit 0): Set if A was smaller than the memory value
|
||||
if (aVal < cpVal) newFlags |= 0x01;
|
||||
|
||||
AF.Low = newFlags;
|
||||
|
||||
// CRITICAL: Notice we do NOT update AF.High! The Accumulator is preserved.
|
||||
|
||||
return 19; // 19 T-States
|
||||
case 0xE1: // POP IX
|
||||
// 1. Read the low byte from the top of the stack
|
||||
byte popLow = _memory.Read(SP);
|
||||
@@ -1875,6 +1989,42 @@ namespace Core.Cpu
|
||||
AF.High = (byte)result;
|
||||
|
||||
return 19;
|
||||
case 0xBE: // CP (IY+d)
|
||||
// 1. Fetch the displacement byte and calculate the address using IY
|
||||
sbyte offsetBE = (sbyte)FetchByte();
|
||||
ushort addressBE = (ushort)(IY.Word + offsetBE);
|
||||
|
||||
// 2. Read the value from memory
|
||||
byte cpVal = _memory.Read(addressBE);
|
||||
|
||||
// 3. Perform the phantom subtraction
|
||||
aVal = AF.High;
|
||||
result = aVal - cpVal;
|
||||
|
||||
// --- 8-Bit Compare Flag Calculation (Identical to SUB) ---
|
||||
newFlags = 0;
|
||||
|
||||
// S Flag (Bit 7): Set if the phantom result is negative
|
||||
if ((result & 0x80) != 0) newFlags |= 0x80;
|
||||
|
||||
// Z Flag (Bit 6): Set if A perfectly matches the memory value
|
||||
if ((result & 0xFF) == 0) newFlags |= 0x40;
|
||||
|
||||
// H Flag (Bit 4): Set if there was a borrow from Bit 3
|
||||
if (((aVal & 0x0F) - (cpVal & 0x0F)) < 0) newFlags |= 0x10;
|
||||
|
||||
// P/V Flag (Bit 2): Set on Overflow
|
||||
if ((((aVal ^ cpVal) & (aVal ^ result)) & 0x80) != 0) newFlags |= 0x04;
|
||||
|
||||
// N Flag (Bit 1): Always set to 1 for Subtractions/Compares
|
||||
newFlags |= 0x02;
|
||||
|
||||
// C Flag (Bit 0): Set if A was smaller than the memory value
|
||||
if (aVal < cpVal) newFlags |= 0x01;
|
||||
|
||||
AF.Low = newFlags;
|
||||
|
||||
return 19; // 19 T-States
|
||||
case 0xCB: // The FD CB nested prefix
|
||||
{
|
||||
sbyte displacement = (sbyte)FetchByte();
|
||||
|
||||
Reference in New Issue
Block a user