Fixed fast and slow tape loading and play_stop tape

This commit is contained in:
2026-04-29 14:33:39 +01:00
parent 8ef5e1f023
commit 142db4d004
5 changed files with 100 additions and 53 deletions

View File

@@ -145,10 +145,24 @@ namespace Core.Io
CalculateNextDataPulse();
}
break;
//case TapeState.Pause:
// // The .TAP Format Auto-Stop Heuristic:
// if (_currentBlock != null && _currentBlock.Length > 0 && _currentBlock[0] == 0x00)
// {
// // 1. It was a Header block. The ROM is waiting for the Data right now! Keep spinning.
// LoadNextBlock();
// }
// else
// {
// // 2. It was a Data block (or custom). The "file" is done.
// // Auto-Stop the tape deck so we don't accidentally play the next level into the void.
// _state = TapeState.Idle;
// _currentBlock = null;
// }
// break;
case TapeState.Pause:
_state = TapeState.Idle;
//LoadNextBlock();
LoadNextBlock();
break;
}
}
@@ -170,12 +184,14 @@ namespace Core.Io
//public bool HasBlocks => _blocks.Count > 0;
// Change this line:
public bool HasBlocks => _blocks.Count > 0 || _currentBlock != null;
//public bool HasBlocks => _blocks.Count > 0 || _currentBlock != null;
// Only consider _currentBlock valid if we are actively playing it!
public bool HasBlocks => _blocks.Count > 0 || (_currentBlock != null && _state != TapeState.Idle && _state != TapeState.Pause);
public byte[] GetNextBlock()
{
// If a block is loaded into the tape deck, yank it immediately
if (_currentBlock != null)
// Yank the current block ONLY if it is actively playing
if (_currentBlock != null && _state != TapeState.Idle && _state != TapeState.Pause)
{
byte[] blockToReturn = _currentBlock;
_state = TapeState.Idle; // Ensure the tape deck is stopped
@@ -183,7 +199,9 @@ namespace Core.Io
return blockToReturn;
}
// Otherwise, pull directly from the queue
// Otherwise, pull directly from the unplayed queue
_state = TapeState.Idle; // Stop the tape deck just in case
_currentBlock = null;
return _blocks.Count > 0 ? _blocks.Dequeue() : null;
}
}

View File

@@ -55,6 +55,7 @@
lblFrames = new Label();
lblFPS = new Label();
lblFrameTime = new Label();
richTextBox1 = new RichTextBox();
SuspendLayout();
//
// lblAF
@@ -164,14 +165,14 @@
txtMemoryView.Location = new Point(88, 80);
txtMemoryView.Margin = new Padding(2);
txtMemoryView.Name = "txtMemoryView";
txtMemoryView.Size = new Size(416, 195);
txtMemoryView.Size = new Size(443, 895);
txtMemoryView.TabIndex = 15;
txtMemoryView.Text = "Memory View Window";
//
// lstDisassembly
//
lstDisassembly.FormattingEnabled = true;
lstDisassembly.Location = new Point(523, 11);
lstDisassembly.Location = new Point(567, 8);
lstDisassembly.Margin = new Padding(2);
lstDisassembly.Name = "lstDisassembly";
lstDisassembly.Size = new Size(252, 264);
@@ -180,7 +181,7 @@
// lstStack
//
lstStack.FormattingEnabled = true;
lstStack.Location = new Point(779, 11);
lstStack.Location = new Point(823, 8);
lstStack.Margin = new Padding(2);
lstStack.Name = "lstStack";
lstStack.Size = new Size(130, 264);
@@ -189,7 +190,7 @@
// label1
//
label1.AutoSize = true;
label1.Location = new Point(289, 284);
label1.Location = new Point(568, 298);
label1.Name = "label1";
label1.Size = new Size(81, 20);
label1.TabIndex = 19;
@@ -197,7 +198,7 @@
//
// txtBreakpoint
//
txtBreakpoint.Location = new Point(376, 281);
txtBreakpoint.Location = new Point(655, 295);
txtBreakpoint.Name = "txtBreakpoint";
txtBreakpoint.Size = new Size(125, 27);
txtBreakpoint.TabIndex = 20;
@@ -234,7 +235,7 @@
// lblIff1
//
lblIff1.AutoSize = true;
lblIff1.Location = new Point(88, 298);
lblIff1.Location = new Point(567, 411);
lblIff1.Name = "lblIff1";
lblIff1.Size = new Size(35, 20);
lblIff1.TabIndex = 24;
@@ -243,7 +244,7 @@
// lblIff2
//
lblIff2.AutoSize = true;
lblIff2.Location = new Point(88, 345);
lblIff2.Location = new Point(567, 458);
lblIff2.Name = "lblIff2";
lblIff2.Size = new Size(35, 20);
lblIff2.TabIndex = 25;
@@ -252,7 +253,7 @@
// lblIE
//
lblIE.AutoSize = true;
lblIE.Location = new Point(88, 397);
lblIE.Location = new Point(568, 370);
lblIE.Name = "lblIE";
lblIE.Size = new Size(109, 20);
lblIE.TabIndex = 26;
@@ -260,7 +261,7 @@
//
// btnReset
//
btnReset.Location = new Point(372, 313);
btnReset.Location = new Point(651, 327);
btnReset.Margin = new Padding(2);
btnReset.Name = "btnReset";
btnReset.Size = new Size(132, 27);
@@ -278,7 +279,7 @@
// lblFrames
//
lblFrames.AutoSize = true;
lblFrames.Location = new Point(538, 320);
lblFrames.Location = new Point(568, 651);
lblFrames.Margin = new Padding(2, 0, 2, 0);
lblFrames.Name = "lblFrames";
lblFrames.Size = new Size(124, 20);
@@ -288,7 +289,7 @@
// lblFPS
//
lblFPS.AutoSize = true;
lblFPS.Location = new Point(630, 397);
lblFPS.Location = new Point(660, 728);
lblFPS.Margin = new Padding(2, 0, 2, 0);
lblFPS.Name = "lblFPS";
lblFPS.Size = new Size(32, 20);
@@ -298,18 +299,29 @@
// lblFrameTime
//
lblFrameTime.AutoSize = true;
lblFrameTime.Location = new Point(575, 356);
lblFrameTime.Location = new Point(605, 687);
lblFrameTime.Margin = new Padding(2, 0, 2, 0);
lblFrameTime.Name = "lblFrameTime";
lblFrameTime.Size = new Size(87, 20);
lblFrameTime.TabIndex = 30;
lblFrameTime.Text = "Frame Time";
//
// richTextBox1
//
richTextBox1.Enabled = false;
richTextBox1.Location = new Point(568, 500);
richTextBox1.Name = "richTextBox1";
richTextBox1.ReadOnly = true;
richTextBox1.Size = new Size(304, 113);
richTextBox1.TabIndex = 32;
richTextBox1.Text = "ZX Spectrum 48K Memory Map:\n0x0000 - 0x3FFF: ROM (16KB)\n0x4000 - 0x57FF: Display File (Screen Pixels)\n0x5800 - 0x5AFF: Colour Attributes\n0x5B00 - 0xFFFF: General Purpose RAM";
//
// DebuggerForm
//
AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(928, 454);
ClientSize = new Size(965, 990);
Controls.Add(richTextBox1);
Controls.Add(lblFrameTime);
Controls.Add(lblFPS);
Controls.Add(lblFrames);
@@ -370,6 +382,7 @@
private Label lblFrames;
private Label lblFPS;
private Label lblFrameTime;
private RichTextBox richTextBox1;
//private TextBox textBox4;
}
}

View File

@@ -88,6 +88,7 @@ namespace Desktop
private void UpdateMemoryView()
{
int count = 40;
// Try to parse the hex string the user typed in
if (!ushort.TryParse(txtMemoryStart.Text, System.Globalization.NumberStyles.HexNumber, null, out ushort startAddress))
{
@@ -98,7 +99,7 @@ namespace Desktop
StringBuilder sb = new StringBuilder();
// Read 100 bytes (or roughly 6 lines of 16 bytes)
for (int line = 0; line < 7; line++)
for (int line = 0; line < count; line++)
{
ushort currentAddr = (ushort)(startAddress + (line * 16));
@@ -246,8 +247,8 @@ namespace Desktop
instructionLength = 2;
break;
case 0x17: // RLA
mnemonic = "RLA";
instructionLength = 1;
mnemonic = "RLA";
instructionLength = 1;
break;
case 0x18:
sbyte dUnconditional = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
@@ -266,7 +267,8 @@ namespace Desktop
mnemonic = $"LD E, 0x{val1E:X2}";
instructionLength = 2;
break;
case 0x1F: mnemonic = $"RRA";
case 0x1F:
mnemonic = $"RRA";
break;
case 0x20:
sbyte jrOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
@@ -295,8 +297,8 @@ namespace Desktop
instructionLength = 2;
break;
case 0x27: // DAA
mnemonic = "DAA";
instructionLength = 1;
mnemonic = "DAA";
instructionLength = 1;
break;
case 0x28:
sbyte jrZOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1));
@@ -462,9 +464,9 @@ namespace Desktop
case 0x8D:
case 0x8E:
case 0x8F:
string[] registers = { "B", "C", "D", "E", "H", "L", "(HL)", "A" };
mnemonic = $"ADC A, {registers[opcode - 0x88]}";
instructionLength = 1;
string[] registers = { "B", "C", "D", "E", "H", "L", "(HL)", "A" };
mnemonic = $"ADC A, {registers[opcode - 0x88]}";
instructionLength = 1;
break;
// --- SUB r ---
case 0x90: mnemonic = "SUB B"; break;
@@ -690,9 +692,9 @@ namespace Desktop
mnemonic = "RET C";
break;
case 0xDB: // IN A, (n)
n = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"IN A, (0x{n:X2})";
instructionLength = 2;
n = _memoryBus.Read((ushort)(currentPc + 1));
mnemonic = $"IN A, (0x{n:X2})";
instructionLength = 2;
break;
case 0xDD:
{

View File

@@ -47,6 +47,7 @@
debuggerToolStripMenuItem = new ToolStripMenuItem();
optionsToolStripMenuItem = new ToolStripMenuItem();
HighSpeedToolStripMenuItem = new ToolStripMenuItem();
fastLoadToolStripMenuItem = new ToolStripMenuItem();
playTapeToolStripMenuItem = new ToolStripMenuItem();
menuStrip1.SuspendLayout();
SuspendLayout();
@@ -73,7 +74,7 @@
//
openToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { includedToolStripMenuItem, tAPToolStripMenuItem, sNAToolStripMenuItem });
openToolStripMenuItem.Name = "openToolStripMenuItem";
openToolStripMenuItem.Size = new Size(224, 26);
openToolStripMenuItem.Size = new Size(188, 26);
openToolStripMenuItem.Text = "Open";
//
// includedToolStripMenuItem
@@ -112,14 +113,14 @@
// saveSnapshotToolStripMenuItem
//
saveSnapshotToolStripMenuItem.Name = "saveSnapshotToolStripMenuItem";
saveSnapshotToolStripMenuItem.Size = new Size(224, 26);
saveSnapshotToolStripMenuItem.Size = new Size(188, 26);
saveSnapshotToolStripMenuItem.Text = "Save Snapshot";
saveSnapshotToolStripMenuItem.Click += SaveSNAMenuItem_Click;
//
// exitToolStripMenuItem
//
exitToolStripMenuItem.Name = "exitToolStripMenuItem";
exitToolStripMenuItem.Size = new Size(224, 26);
exitToolStripMenuItem.Size = new Size(188, 26);
exitToolStripMenuItem.Text = "Exit";
exitToolStripMenuItem.Click += btnExit_Click;
//
@@ -174,7 +175,7 @@
//
// optionsToolStripMenuItem
//
optionsToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { HighSpeedToolStripMenuItem });
optionsToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { HighSpeedToolStripMenuItem, fastLoadToolStripMenuItem });
optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
optionsToolStripMenuItem.Size = new Size(75, 24);
optionsToolStripMenuItem.Text = "Options";
@@ -182,10 +183,19 @@
// HighSpeedToolStripMenuItem
//
HighSpeedToolStripMenuItem.Name = "HighSpeedToolStripMenuItem";
HighSpeedToolStripMenuItem.Size = new Size(170, 26);
HighSpeedToolStripMenuItem.Size = new Size(224, 26);
HighSpeedToolStripMenuItem.Text = "High Speed";
HighSpeedToolStripMenuItem.Click += btnHighSpeedToggle_Click;
//
// fastLoadToolStripMenuItem
//
fastLoadToolStripMenuItem.Checked = true;
fastLoadToolStripMenuItem.CheckState = CheckState.Checked;
fastLoadToolStripMenuItem.Name = "fastLoadToolStripMenuItem";
fastLoadToolStripMenuItem.Size = new Size(224, 26);
fastLoadToolStripMenuItem.Text = "Fast Loading";
fastLoadToolStripMenuItem.Click += fastLoadToolStripMenuItem_Click;
//
// playTapeToolStripMenuItem
//
playTapeToolStripMenuItem.Name = "playTapeToolStripMenuItem";
@@ -230,5 +240,6 @@
private ToolStripMenuItem sNAToolStripMenuItem1;
private ToolStripMenuItem saveSnapshotToolStripMenuItem;
private ToolStripMenuItem playTapeToolStripMenuItem;
private ToolStripMenuItem fastLoadToolStripMenuItem;
}
}

View File

@@ -30,7 +30,9 @@ namespace Desktop
public double FrameTime = 0;
public bool highSpeed = false;
public bool tapeLoaded = false;
public bool tapePlaying = false;
private volatile bool _pendingReset = false;
private bool _enableFastLoad = true;
// Comment to push a new commit
public Form1()
@@ -57,6 +59,8 @@ namespace Desktop
_memoryBus.LoadRom(romData);
_cpu = new Z80(_memoryBus, _simpleIoBus);
_cpu.WaitStateCallback = _ula.GetContentionDelay;
fastLoadToolStripMenuItem.Checked = _enableFastLoad;
}
catch (Exception ex)
@@ -118,10 +122,10 @@ namespace Desktop
long tStatesBefore = _cpu.TotalTStates;
// --- HARDWARE INTERCEPTS ---
if ((_cpu.PC == 0x0556 || _cpu.PC == 0x0558) && _tapManager.HasBlocks)
if (_enableFastLoad && _cpu.PC == 0x0556 && _tapManager.HasBlocks)
{
HandleInstantTapeLoad();
_cpu.TotalTStates += 100; // Charge some arbitrary time for the fast load
_cpu.TotalTStates += 100;
}
// --- Execute Instruction ---
@@ -302,37 +306,34 @@ namespace Desktop
private void PopulateIncludedTapsMenu()
{
// 1. Get the current assembly (your .exe)
Assembly assembly = Assembly.GetExecutingAssembly();
// 2. Find all embedded resources
string[] resourceNames = assembly.GetManifestResourceNames();
foreach (string resourceName in resourceNames)
{
// Check if it is a TAP file in our TestRoms folder
// (Embedded resources use dot-notation, e.g., "Desktop.TestRoms.ZEXALL.TAP")
if (resourceName.Contains("Desktop.ROMS.TAP.") && resourceName.EndsWith(".TAP", StringComparison.OrdinalIgnoreCase))
{
// Clean up the name for the menu (e.g., "Desktop.TestRoms.ZEXALL.TAP" -> "ZEXALL")
string[] parts = resourceName.Split('.');
string displayName = parts[parts.Length - 2];
// Create the new menu item
ToolStripMenuItem item = new ToolStripMenuItem(displayName);
// Store the full internal path in the Tag so we know what to load when clicked
item.Tag = resourceName;
// Wire up the click event
item.Click += IncludedTapMenuItem_Click;
// Add it to the "Open Included..." dropdown
tAPToolStripMenuItem1.DropDownItems.Add(item);
}
}
}
private void fastLoadToolStripMenuItem_Click(object sender, EventArgs e)
{
this.fastLoadToolStripMenuItem.Checked = !this.fastLoadToolStripMenuItem.Checked;
_enableFastLoad = this.fastLoadToolStripMenuItem.Checked;
}
private void IncludedTapMenuItem_Click(object? sender, EventArgs e)
{
if (sender is ToolStripMenuItem item && item.Tag is string resourceName)
@@ -357,7 +358,7 @@ namespace Desktop
// Feed it directly to your existing TapManager!
_tapManager.LoadTapData(tapBytes);
tapeLoaded = true;
_tapManager.Play();
//_tapManager.Play();
}
}
}
@@ -488,7 +489,7 @@ namespace Desktop
byte[] tapBytes = File.ReadAllBytes(ofd.FileName);
_tapManager.LoadTapData(tapBytes);
tapeLoaded = true;
_tapManager.Play();
//_tapManager.Play();
}
}
_isPaused = false;
@@ -643,11 +644,13 @@ namespace Desktop
{
playTapeToolStripMenuItem.Text = "Stop Tape";
_tapManager.Play();
tapePlaying = true;
}
else
{
playTapeToolStripMenuItem.Text = "Play Tape";
_tapManager.Stop();
tapePlaying = false;
}
}