Ammendments to Z80 to make it system agnostic.

This commit is contained in:
2026-04-24 14:27:08 +01:00
parent 0e8462c8a5
commit 2842af182f
3 changed files with 61 additions and 118 deletions

View File

@@ -19,7 +19,6 @@ namespace Core.Cpu
}
}
public bool IsZexDocMode { get; set; } = false;
//T-State counter
public long TotalTStates { get; set; }
@@ -55,19 +54,14 @@ namespace Core.Cpu
// The Memory Bus
private readonly IMemory _memory;
private readonly IO_Bus _simpleIoBus;
public TapManager _tapManager;
//External Timing interface
public Func<ushort, long, int>? WaitStateCallback { get; set; }
//Misc Variables
public bool EnableFastLoad { get; set; } = true;
public Z80(IMemory memory, IO_Bus ioBus, TapManager tapManager)
public Z80(IMemory memory, IO_Bus ioBus)
{
_memory = memory;
_simpleIoBus = ioBus;
_tapManager = tapManager;
Reset();
}
@@ -193,46 +187,7 @@ namespace Core.Cpu
}
public int Step()
{
if (IsZexDocMode && PC == 0x0005)
{
// CP/M System Call Hook
if (BC.Low == 2) // C = 2: Print a single character stored in register E
{
System.Diagnostics.Debug.Write((char)DE.Low);
}
else if (BC.Low == 9) // C = 9: Print a string starting at memory address DE, terminated by '$'
{
ushort addr = DE.Word;
while (true)
{
char c = (char)ReadMemory(addr++);
if (c == '$') break;
System.Diagnostics.Debug.Write(c);
}
}
// Emulate a 'RET' instruction to return from the CALL 0x0005
byte retLow = ReadMemory(SP);
SP++;
byte retHigh = ReadMemory(SP);
SP++;
PC = (ushort)((retHigh << 8) | retLow);
return 10; // Skip normal execution for this cycle
}
if (PC == 0x0556)
{
if (EnableFastLoad)
{
HandleInstantTapeLoad();
return 100;
}
else
{
_tapManager.Play();
}
}
{
// Fetch the next opcode and increment the Program Counter
byte opcode = ReadMemory(PC++);
@@ -280,56 +235,9 @@ namespace Core.Cpu
// It was PUSHED to the stack exactly 1 instruction before the snapshot was saved.
// So, we just pop it off the stack to resume execution!
PC = Pop();
}
}
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++)
{
WriteMemory((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. Simulate the Checksum Match (Accumulator becomes 0)
AF.High = 0x00;
// 6. Set Carry Flag (Bit 0) for Success, and Zero Flag (Bit 6) for Checksum Match
AF.Low |= 0x41; // 0x41 is binary 0100 0001 (Zero and Carry both set)
// 7. 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 = ReadMemory(SP);
SP++;
byte pcHigh = ReadMemory(SP);
SP++;
PC = (ushort)((pcHigh << 8) | pcLow);
}
public string GetFlagsString()
{