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
|
//T-State counter
|
||||||
public long TotalTStates { get; set; }
|
public long TotalTStates { get; set; }
|
||||||
|
|
||||||
@@ -55,19 +54,14 @@ namespace Core.Cpu
|
|||||||
// The Memory Bus
|
// The Memory Bus
|
||||||
private readonly IMemory _memory;
|
private readonly IMemory _memory;
|
||||||
private readonly IO_Bus _simpleIoBus;
|
private readonly IO_Bus _simpleIoBus;
|
||||||
public TapManager _tapManager;
|
|
||||||
|
|
||||||
//External Timing interface
|
//External Timing interface
|
||||||
public Func<ushort, long, int>? WaitStateCallback { get; set; }
|
public Func<ushort, long, int>? WaitStateCallback { get; set; }
|
||||||
|
|
||||||
//Misc Variables
|
public Z80(IMemory memory, IO_Bus ioBus)
|
||||||
public bool EnableFastLoad { get; set; } = true;
|
|
||||||
|
|
||||||
public Z80(IMemory memory, IO_Bus ioBus, TapManager tapManager)
|
|
||||||
{
|
{
|
||||||
_memory = memory;
|
_memory = memory;
|
||||||
_simpleIoBus = ioBus;
|
_simpleIoBus = ioBus;
|
||||||
_tapManager = tapManager;
|
|
||||||
Reset();
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,45 +188,6 @@ namespace Core.Cpu
|
|||||||
|
|
||||||
public int Step()
|
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
|
// Fetch the next opcode and increment the Program Counter
|
||||||
byte opcode = ReadMemory(PC++);
|
byte opcode = ReadMemory(PC++);
|
||||||
@@ -282,54 +237,7 @@ namespace Core.Cpu
|
|||||||
PC = Pop();
|
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()
|
public string GetFlagsString()
|
||||||
{
|
{
|
||||||
|
|||||||
13
Desktop/Form1.Designer.cs
generated
13
Desktop/Form1.Designer.cs
generated
@@ -43,7 +43,6 @@
|
|||||||
stepToolStripMenuItem = new ToolStripMenuItem();
|
stepToolStripMenuItem = new ToolStripMenuItem();
|
||||||
resetToolStripMenuItem1 = new ToolStripMenuItem();
|
resetToolStripMenuItem1 = new ToolStripMenuItem();
|
||||||
optionsToolStripMenuItem = new ToolStripMenuItem();
|
optionsToolStripMenuItem = new ToolStripMenuItem();
|
||||||
fastLoadingToolStripMenuItem = new ToolStripMenuItem();
|
|
||||||
HighSpeedToolStripMenuItem = new ToolStripMenuItem();
|
HighSpeedToolStripMenuItem = new ToolStripMenuItem();
|
||||||
((System.ComponentModel.ISupportInitialize)picScreen).BeginInit();
|
((System.ComponentModel.ISupportInitialize)picScreen).BeginInit();
|
||||||
menuStrip1.SuspendLayout();
|
menuStrip1.SuspendLayout();
|
||||||
@@ -156,20 +155,11 @@
|
|||||||
//
|
//
|
||||||
// optionsToolStripMenuItem
|
// optionsToolStripMenuItem
|
||||||
//
|
//
|
||||||
optionsToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { fastLoadingToolStripMenuItem, HighSpeedToolStripMenuItem });
|
optionsToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { HighSpeedToolStripMenuItem });
|
||||||
optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
|
optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
|
||||||
optionsToolStripMenuItem.Size = new Size(75, 24);
|
optionsToolStripMenuItem.Size = new Size(75, 24);
|
||||||
optionsToolStripMenuItem.Text = "Options";
|
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
|
||||||
//
|
//
|
||||||
HighSpeedToolStripMenuItem.Name = "HighSpeedToolStripMenuItem";
|
HighSpeedToolStripMenuItem.Name = "HighSpeedToolStripMenuItem";
|
||||||
@@ -212,7 +202,6 @@
|
|||||||
private ToolStripMenuItem stepToolStripMenuItem;
|
private ToolStripMenuItem stepToolStripMenuItem;
|
||||||
private ToolStripMenuItem resetToolStripMenuItem1;
|
private ToolStripMenuItem resetToolStripMenuItem1;
|
||||||
private ToolStripMenuItem optionsToolStripMenuItem;
|
private ToolStripMenuItem optionsToolStripMenuItem;
|
||||||
private ToolStripMenuItem fastLoadingToolStripMenuItem;
|
|
||||||
private ToolStripMenuItem HighSpeedToolStripMenuItem;
|
private ToolStripMenuItem HighSpeedToolStripMenuItem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace Desktop
|
|||||||
_memoryBus.CrapRAMData();
|
_memoryBus.CrapRAMData();
|
||||||
byte[] romData = RomLoader.Load("48.rom");
|
byte[] romData = RomLoader.Load("48.rom");
|
||||||
_memoryBus.LoadRom(romData);
|
_memoryBus.LoadRom(romData);
|
||||||
_cpu = new Z80(_memoryBus, _simpleIoBus, _tapManager);
|
_cpu = new Z80(_memoryBus, _simpleIoBus);
|
||||||
_cpu.WaitStateCallback = _ula.GetContentionDelay;
|
_cpu.WaitStateCallback = _ula.GetContentionDelay;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -112,6 +112,13 @@ namespace Desktop
|
|||||||
|
|
||||||
long tStatesBefore = _cpu.TotalTStates;
|
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 ---
|
// --- Execute Instruction ---
|
||||||
_cpu.Step();
|
_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()
|
private void UpdateScreenBitmap()
|
||||||
@@ -246,21 +299,14 @@ namespace Desktop
|
|||||||
if (ofd.ShowDialog() == DialogResult.OK)
|
if (ofd.ShowDialog() == DialogResult.OK)
|
||||||
{
|
{
|
||||||
byte[] tapBytes = File.ReadAllBytes(ofd.FileName);
|
byte[] tapBytes = File.ReadAllBytes(ofd.FileName);
|
||||||
_cpu._tapManager.LoadTapData(tapBytes);
|
_tapManager.LoadTapData(tapBytes);
|
||||||
tapeLoaded = true;
|
tapeLoaded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_isPaused = false;
|
_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)
|
private void openSNAToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
_isPaused = true;
|
_isPaused = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user