Ammendments to Z80 to make it system agnostic.
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
@@ -194,45 +188,6 @@ 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++);
|
||||
@@ -282,54 +237,7 @@ namespace Core.Cpu
|
||||
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()
|
||||
{
|
||||
|
||||
13
Desktop/Form1.Designer.cs
generated
13
Desktop/Form1.Designer.cs
generated
@@ -43,7 +43,6 @@
|
||||
stepToolStripMenuItem = new ToolStripMenuItem();
|
||||
resetToolStripMenuItem1 = new ToolStripMenuItem();
|
||||
optionsToolStripMenuItem = new ToolStripMenuItem();
|
||||
fastLoadingToolStripMenuItem = new ToolStripMenuItem();
|
||||
HighSpeedToolStripMenuItem = new ToolStripMenuItem();
|
||||
((System.ComponentModel.ISupportInitialize)picScreen).BeginInit();
|
||||
menuStrip1.SuspendLayout();
|
||||
@@ -156,20 +155,11 @@
|
||||
//
|
||||
// optionsToolStripMenuItem
|
||||
//
|
||||
optionsToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { fastLoadingToolStripMenuItem, HighSpeedToolStripMenuItem });
|
||||
optionsToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { HighSpeedToolStripMenuItem });
|
||||
optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
|
||||
optionsToolStripMenuItem.Size = new Size(75, 24);
|
||||
optionsToolStripMenuItem.Text = "Options";
|
||||
//
|
||||
// fastLoadingToolStripMenuItem
|
||||
//
|
||||
fastLoadingToolStripMenuItem.Checked = true;
|
||||
fastLoadingToolStripMenuItem.CheckState = CheckState.Checked;
|
||||
fastLoadingToolStripMenuItem.Name = "fastLoadingToolStripMenuItem";
|
||||
fastLoadingToolStripMenuItem.Size = new Size(224, 26);
|
||||
fastLoadingToolStripMenuItem.Text = "Fast TAP Loading";
|
||||
fastLoadingToolStripMenuItem.Click += fastLoadingToolStripMenuItem_Click;
|
||||
//
|
||||
// HighSpeedToolStripMenuItem
|
||||
//
|
||||
HighSpeedToolStripMenuItem.Name = "HighSpeedToolStripMenuItem";
|
||||
@@ -212,7 +202,6 @@
|
||||
private ToolStripMenuItem stepToolStripMenuItem;
|
||||
private ToolStripMenuItem resetToolStripMenuItem1;
|
||||
private ToolStripMenuItem optionsToolStripMenuItem;
|
||||
private ToolStripMenuItem fastLoadingToolStripMenuItem;
|
||||
private ToolStripMenuItem HighSpeedToolStripMenuItem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Desktop
|
||||
_memoryBus.CrapRAMData();
|
||||
byte[] romData = RomLoader.Load("48.rom");
|
||||
_memoryBus.LoadRom(romData);
|
||||
_cpu = new Z80(_memoryBus, _simpleIoBus, _tapManager);
|
||||
_cpu = new Z80(_memoryBus, _simpleIoBus);
|
||||
_cpu.WaitStateCallback = _ula.GetContentionDelay;
|
||||
|
||||
}
|
||||
@@ -112,6 +112,13 @@ namespace Desktop
|
||||
|
||||
long tStatesBefore = _cpu.TotalTStates;
|
||||
|
||||
// --- HARDWARE INTERCEPTS ---
|
||||
if (_cpu.PC == 0x0556 && _tapManager.HasBlocks)
|
||||
{
|
||||
HandleInstantTapeLoad();
|
||||
_cpu.TotalTStates += 100; // Charge some arbitrary time for the fast load
|
||||
}
|
||||
|
||||
// --- Execute Instruction ---
|
||||
_cpu.Step();
|
||||
|
||||
@@ -218,6 +225,52 @@ namespace Desktop
|
||||
});
|
||||
}
|
||||
|
||||
private void HandleInstantTapeLoad()
|
||||
{
|
||||
byte[] block = _tapManager.GetNextBlock(); // Your original Queue.Dequeue() method
|
||||
if (block == null) return;
|
||||
|
||||
byte expectedFlag = _cpu.AF.High;
|
||||
if (block[0] != expectedFlag)
|
||||
{
|
||||
// Block mismatch (e.g. found data when looking for a header)
|
||||
// Clear the carry flag to simulate a tape loading error
|
||||
_cpu.AF.Low &= unchecked((byte)~0x01);
|
||||
ForceRet();
|
||||
return;
|
||||
}
|
||||
|
||||
int bytesToCopy = _cpu.DE.Word;
|
||||
|
||||
// Safety check just in case the TAP file is malformed
|
||||
int actualBytes = Math.Min(bytesToCopy, block.Length - 1);
|
||||
|
||||
// Directly inject the payload into the RAM
|
||||
for (int i = 0; i < actualBytes; i++)
|
||||
{
|
||||
_memoryBus.Write((ushort)(_cpu.IX.Word + i), block[i + 1]);
|
||||
}
|
||||
|
||||
// --- Update Registers to match a PERFECT ROM Load ---
|
||||
_cpu.IX.Word = (ushort)(_cpu.IX.Word + actualBytes);
|
||||
_cpu.DE.Word = (ushort)(bytesToCopy - actualBytes); // Should hit 0
|
||||
_cpu.HL.Word = 0x0000; // The ROM zeroes this out after calculating checksums
|
||||
|
||||
// Checksum Zero (A = 0x00), Success Flag / Zero Flag Set (F = 0x41)
|
||||
_cpu.AF.Word = 0x0041;
|
||||
|
||||
ForceRet();
|
||||
}
|
||||
|
||||
private void ForceRet()
|
||||
{
|
||||
// Pop the return address off the stack just like a real RET instruction
|
||||
byte pcLow = _memoryBus.Read(_cpu.SP);
|
||||
_cpu.SP++;
|
||||
byte pcHigh = _memoryBus.Read(_cpu.SP);
|
||||
_cpu.SP++;
|
||||
_cpu.PC = (ushort)((pcHigh << 8) | pcLow);
|
||||
}
|
||||
|
||||
|
||||
private void UpdateScreenBitmap()
|
||||
@@ -246,21 +299,14 @@ namespace Desktop
|
||||
if (ofd.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
byte[] tapBytes = File.ReadAllBytes(ofd.FileName);
|
||||
_cpu._tapManager.LoadTapData(tapBytes);
|
||||
_tapManager.LoadTapData(tapBytes);
|
||||
tapeLoaded = true;
|
||||
}
|
||||
}
|
||||
_isPaused = false;
|
||||
}
|
||||
|
||||
private void fastLoadingToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
// Toggle the checkmark UI
|
||||
fastLoadingToolStripMenuItem.Checked = !fastLoadingToolStripMenuItem.Checked;
|
||||
|
||||
// Tell the CPU to enable or disable the ROM hijack
|
||||
_cpu.EnableFastLoad = fastLoadingToolStripMenuItem.Checked;
|
||||
}
|
||||
private void openSNAToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
_isPaused = true;
|
||||
|
||||
Reference in New Issue
Block a user