diff --git a/Core/Cpu/Z80.cs b/Core/Cpu/Z80.cs index dc68aa4..2bddbad 100644 --- a/Core/Cpu/Z80.cs +++ b/Core/Cpu/Z80.cs @@ -1352,6 +1352,10 @@ namespace Core.Cpu case 0x4D: // RETI Does not affect IFF1 or IFF2 PC = Pop(); return 14; + case 0x51: // OUT (C), D + // BC.Word goes to the address bus, D (DE.High) goes to the data bus + _simpleIoBus.WritePort(BC.Word, DE.High); + return 12; // 2 M-cycles, 12 T-States case 0x53: // LD (nn), DE ushort dest53 = FetchWord(); WriteMemory(dest53, DE.Low); @@ -1372,6 +1376,10 @@ namespace Core.Cpu AF.Low = flags58; return 12; + case 0x59: // OUT (C), E + // BC.Word goes to the address bus, E (DE.Low) goes to the data bus + _simpleIoBus.WritePort(BC.Word, DE.Low); + return 12; // 2 M-cycles, 12 T-States case 0x5B: // LD DE, (nn) ushort src5B = FetchWord(); DE.Low = ReadMemory(src5B); diff --git a/Core/Io/IO_Bus.cs b/Core/Io/IO_Bus.cs index d0c88a2..9c9f2b1 100644 --- a/Core/Io/IO_Bus.cs +++ b/Core/Io/IO_Bus.cs @@ -19,7 +19,7 @@ namespace Core.Io public byte ReadPort(ushort portAddress) { // The Spectrum ULA responds to any even port address (where the lowest bit is 0) - if ((portAddress & 0x01) == 0) + if ((portAddress & 0x01) == 0) //Port 0xFE) { byte highByte = (byte)(portAddress >> 8); // The B register! byte result = 0xFF; // Start assuming no keys are pressed @@ -40,11 +40,17 @@ namespace Core.Io //return result; // The top 3 bits (5, 6, 7) are unused by the keyboard and usually return 1 on a real Spectrum - return (byte)(result | 0xE0); + return (byte)(result | 0xA0); + } + + // Kempston Joystick Port + if ((portAddress & 0xFF) == 0x1F) + { + return 0x00; // 0x00 means no joystick connected/no buttons pressed } // Return 0xFF for unhandled ports - return 0xFF; + return 0x00; } public void WritePort(ushort portAddress, byte portValue) diff --git a/Core/Io/TapManager.cs b/Core/Io/TapManager.cs index c59b6b7..bdc4722 100644 --- a/Core/Io/TapManager.cs +++ b/Core/Io/TapManager.cs @@ -46,6 +46,11 @@ namespace Core.Io } } + public void Stop() + { + _state = TapeState.Idle; + } + private void LoadNextBlock() { if (_blocks.Count == 0) @@ -142,7 +147,8 @@ namespace Core.Io break; case TapeState.Pause: - LoadNextBlock(); + _state = TapeState.Idle; + //LoadNextBlock(); break; } } @@ -157,54 +163,28 @@ namespace Core.Io } // --- FAST LOAD METHODS (For ROM Hijack) --- + //public byte[] GetNextBlock() + //{ + // return _blocks.Count > 0 ? _blocks.Dequeue() : null; + //} + + //public bool HasBlocks => _blocks.Count > 0; + // Change this line: + public bool HasBlocks => _blocks.Count > 0 || _currentBlock != null; + public byte[] GetNextBlock() { + // If a block is loaded into the tape deck, yank it immediately + if (_currentBlock != null) + { + byte[] blockToReturn = _currentBlock; + _state = TapeState.Idle; // Ensure the tape deck is stopped + _currentBlock = null; + return blockToReturn; + } + + // Otherwise, pull directly from the queue return _blocks.Count > 0 ? _blocks.Dequeue() : null; } - - public bool HasBlocks => _blocks.Count > 0; } -} - - - - -//using System; -//using System.Collections.Generic; - -//namespace Core.Io -//{ -// public class TapManager -// { -// private Queue _blocks = new Queue(); - - -// public void LoadTapData(byte[] fileData) -// { -// _blocks.Clear(); -// int position = 0; - -// while (position < fileData.Length) -// { -// // 1. Read the 16-bit block length (Little Endian) -// int blockLength = fileData[position] | (fileData[position + 1] << 8); -// position += 2; - -// // 2. Extract the block payload -// byte[] blockData = new byte[blockLength]; -// Array.Copy(fileData, position, blockData, 0, blockLength); -// position += blockLength; - -// // 3. Queue it up -// _blocks.Enqueue(blockData); -// } -// } - -// public byte[] GetNextBlock() -// { -// return _blocks.Count > 0 ? _blocks.Dequeue() : null; -// } - -// public bool HasBlocks => _blocks.Count > 0; -// } -//} +} \ No newline at end of file diff --git a/Desktop/DebuggerForm.cs b/Desktop/DebuggerForm.cs index 2602a3e..df416af 100644 --- a/Desktop/DebuggerForm.cs +++ b/Desktop/DebuggerForm.cs @@ -266,6 +266,8 @@ namespace Desktop mnemonic = $"LD E, 0x{val1E:X2}"; instructionLength = 2; break; + case 0x1F: mnemonic = $"RRA"; + break; case 0x20: sbyte jrOffset = (sbyte)_memoryBus.Read((ushort)(currentPc + 1)); ushort destination = (ushort)(currentPc + 2 + jrOffset); diff --git a/Desktop/Form1.Designer.cs b/Desktop/Form1.Designer.cs index 0a8fef2..99d3316 100644 --- a/Desktop/Form1.Designer.cs +++ b/Desktop/Form1.Designer.cs @@ -36,24 +36,25 @@ sNAToolStripMenuItem1 = new ToolStripMenuItem(); tAPToolStripMenuItem = new ToolStripMenuItem(); sNAToolStripMenuItem = new ToolStripMenuItem(); + saveSnapshotToolStripMenuItem = new ToolStripMenuItem(); exitToolStripMenuItem = new ToolStripMenuItem(); - viewToolStripMenuItem = new ToolStripMenuItem(); - debuggerToolStripMenuItem = new ToolStripMenuItem(); machineToolStripMenuItem = new ToolStripMenuItem(); runToolStripMenuItem = new ToolStripMenuItem(); resetToolStripMenuItem = new ToolStripMenuItem(); stepToolStripMenuItem = new ToolStripMenuItem(); resetToolStripMenuItem1 = new ToolStripMenuItem(); + viewToolStripMenuItem = new ToolStripMenuItem(); + debuggerToolStripMenuItem = new ToolStripMenuItem(); optionsToolStripMenuItem = new ToolStripMenuItem(); HighSpeedToolStripMenuItem = new ToolStripMenuItem(); - saveSnapshotToolStripMenuItem = new ToolStripMenuItem(); + playTapeToolStripMenuItem = new ToolStripMenuItem(); menuStrip1.SuspendLayout(); SuspendLayout(); // // menuStrip1 // menuStrip1.ImageScalingSize = new Size(24, 24); - menuStrip1.Items.AddRange(new ToolStripItem[] { fileToolStripMenuItem, machineToolStripMenuItem, viewToolStripMenuItem, optionsToolStripMenuItem }); + menuStrip1.Items.AddRange(new ToolStripItem[] { fileToolStripMenuItem, machineToolStripMenuItem, viewToolStripMenuItem, optionsToolStripMenuItem, playTapeToolStripMenuItem }); menuStrip1.Location = new Point(0, 0); menuStrip1.Name = "menuStrip1"; menuStrip1.Padding = new Padding(5, 2, 0, 2); @@ -108,6 +109,13 @@ sNAToolStripMenuItem.Text = "SNA"; sNAToolStripMenuItem.Click += openSNAToolStripMenuItem_Click; // + // saveSnapshotToolStripMenuItem + // + saveSnapshotToolStripMenuItem.Name = "saveSnapshotToolStripMenuItem"; + saveSnapshotToolStripMenuItem.Size = new Size(224, 26); + saveSnapshotToolStripMenuItem.Text = "Save Snapshot"; + saveSnapshotToolStripMenuItem.Click += SaveSNAMenuItem_Click; + // // exitToolStripMenuItem // exitToolStripMenuItem.Name = "exitToolStripMenuItem"; @@ -115,6 +123,41 @@ exitToolStripMenuItem.Text = "Exit"; exitToolStripMenuItem.Click += btnExit_Click; // + // machineToolStripMenuItem + // + machineToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { runToolStripMenuItem, resetToolStripMenuItem, stepToolStripMenuItem, resetToolStripMenuItem1 }); + machineToolStripMenuItem.Name = "machineToolStripMenuItem"; + machineToolStripMenuItem.Size = new Size(79, 24); + machineToolStripMenuItem.Text = "Machine"; + // + // runToolStripMenuItem + // + runToolStripMenuItem.Name = "runToolStripMenuItem"; + runToolStripMenuItem.Size = new Size(129, 26); + runToolStripMenuItem.Text = "Run"; + runToolStripMenuItem.Click += btnRun_Click; + // + // resetToolStripMenuItem + // + resetToolStripMenuItem.Name = "resetToolStripMenuItem"; + resetToolStripMenuItem.Size = new Size(129, 26); + resetToolStripMenuItem.Text = "Pause"; + resetToolStripMenuItem.Click += btnPause_Click; + // + // stepToolStripMenuItem + // + stepToolStripMenuItem.Name = "stepToolStripMenuItem"; + stepToolStripMenuItem.Size = new Size(129, 26); + stepToolStripMenuItem.Text = "Step"; + stepToolStripMenuItem.Click += btnStep_Click; + // + // resetToolStripMenuItem1 + // + resetToolStripMenuItem1.Name = "resetToolStripMenuItem1"; + resetToolStripMenuItem1.Size = new Size(129, 26); + resetToolStripMenuItem1.Text = "Reset"; + resetToolStripMenuItem1.Click += btnReset_Click; + // // viewToolStripMenuItem // viewToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { debuggerToolStripMenuItem }); @@ -129,41 +172,6 @@ debuggerToolStripMenuItem.Text = "Debugger"; debuggerToolStripMenuItem.Click += openDebuggerToolStripMenuItem_Click; // - // machineToolStripMenuItem - // - machineToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { runToolStripMenuItem, resetToolStripMenuItem, stepToolStripMenuItem, resetToolStripMenuItem1 }); - machineToolStripMenuItem.Name = "machineToolStripMenuItem"; - machineToolStripMenuItem.Size = new Size(79, 24); - machineToolStripMenuItem.Text = "Machine"; - // - // runToolStripMenuItem - // - runToolStripMenuItem.Name = "runToolStripMenuItem"; - runToolStripMenuItem.Size = new Size(224, 26); - runToolStripMenuItem.Text = "Run"; - runToolStripMenuItem.Click += btnRun_Click; - // - // resetToolStripMenuItem - // - resetToolStripMenuItem.Name = "resetToolStripMenuItem"; - resetToolStripMenuItem.Size = new Size(224, 26); - resetToolStripMenuItem.Text = "Pause"; - resetToolStripMenuItem.Click += btnPause_Click; - // - // stepToolStripMenuItem - // - stepToolStripMenuItem.Name = "stepToolStripMenuItem"; - stepToolStripMenuItem.Size = new Size(224, 26); - stepToolStripMenuItem.Text = "Step"; - stepToolStripMenuItem.Click += btnStep_Click; - // - // resetToolStripMenuItem1 - // - resetToolStripMenuItem1.Name = "resetToolStripMenuItem1"; - resetToolStripMenuItem1.Size = new Size(224, 26); - resetToolStripMenuItem1.Text = "Reset"; - resetToolStripMenuItem1.Click += btnReset_Click; - // // optionsToolStripMenuItem // optionsToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { HighSpeedToolStripMenuItem }); @@ -178,12 +186,12 @@ HighSpeedToolStripMenuItem.Text = "High Speed"; HighSpeedToolStripMenuItem.Click += btnHighSpeedToggle_Click; // - // saveSnapshotToolStripMenuItem + // playTapeToolStripMenuItem // - saveSnapshotToolStripMenuItem.Name = "saveSnapshotToolStripMenuItem"; - saveSnapshotToolStripMenuItem.Size = new Size(224, 26); - saveSnapshotToolStripMenuItem.Text = "Save Snapshot"; - saveSnapshotToolStripMenuItem.Click += SaveSNAMenuItem_Click; + playTapeToolStripMenuItem.Name = "playTapeToolStripMenuItem"; + playTapeToolStripMenuItem.Size = new Size(85, 24); + playTapeToolStripMenuItem.Text = "Play Tape"; + playTapeToolStripMenuItem.Click += playTapeToolStripMenuItem_Click; // // Form1 // @@ -221,5 +229,6 @@ private ToolStripMenuItem tAPToolStripMenuItem1; private ToolStripMenuItem sNAToolStripMenuItem1; private ToolStripMenuItem saveSnapshotToolStripMenuItem; + private ToolStripMenuItem playTapeToolStripMenuItem; } } diff --git a/Desktop/Form1.cs b/Desktop/Form1.cs index 2ea7136..ff68cfc 100644 --- a/Desktop/Form1.cs +++ b/Desktop/Form1.cs @@ -118,10 +118,10 @@ namespace Desktop long tStatesBefore = _cpu.TotalTStates; // --- HARDWARE INTERCEPTS --- - if (_cpu.PC == 0x0556 && _tapManager.HasBlocks) + if ((_cpu.PC == 0x0556 || _cpu.PC == 0x0558) && _tapManager.HasBlocks) { - HandleInstantTapeLoad(); - _cpu.TotalTStates += 100; // Charge some arbitrary time for the fast load + HandleInstantTapeLoad(); + _cpu.TotalTStates += 100; // Charge some arbitrary time for the fast load } // --- Execute Instruction --- @@ -130,7 +130,7 @@ namespace Desktop int elapsedTStates = (int)(_cpu.TotalTStates - tStatesBefore); _tapManager.Update(elapsedTStates); - if(highSpeed) + if (highSpeed) { wasHighSpeed = true; } @@ -186,7 +186,7 @@ namespace Desktop this.Text = $"{_baseTitle} - FPS: {FramesPerSecond:F1} - Tape Loaded: {tapeLoaded.ToString()}"; }); } - + } else { @@ -219,7 +219,7 @@ namespace Desktop FrameTime = TotalFrameTime / 50.0; TotalFrameTime = 0; } - + fpsStopwatch.Restart(); } } @@ -231,7 +231,7 @@ namespace Desktop this.Invoke((System.Windows.Forms.MethodInvoker)delegate { MessageBox.Show(ex.Message, "CPU Crash", MessageBoxButtons.OK, MessageBoxIcon.Error); - + }); } }); @@ -357,6 +357,7 @@ namespace Desktop // Feed it directly to your existing TapManager! _tapManager.LoadTapData(tapBytes); tapeLoaded = true; + _tapManager.Play(); } } } @@ -487,12 +488,13 @@ namespace Desktop byte[] tapBytes = File.ReadAllBytes(ofd.FileName); _tapManager.LoadTapData(tapBytes); tapeLoaded = true; + _tapManager.Play(); } } _isPaused = false; } - + private void openSNAToolStripMenuItem_Click(object sender, EventArgs e) { _isPaused = true; @@ -544,7 +546,7 @@ namespace Desktop _debugger.BringToFront(); } } - + private void UpdateMatrix(int row, int col, bool isPressed) { if (isPressed) @@ -559,14 +561,14 @@ namespace Desktop } } - + protected override void OnKeyDown(KeyEventArgs e) { HandleKey(e.KeyCode, true); base.OnKeyDown(e); } - + protected override void OnKeyUp(KeyEventArgs e) { HandleKey(e.KeyCode, false); @@ -634,5 +636,20 @@ namespace Desktop case Keys.B: UpdateMatrix(7, 4, isPressed); break; } } + + private void playTapeToolStripMenuItem_Click(object sender, EventArgs e) + { + if (playTapeToolStripMenuItem.Text == "Play Tape") + { + playTapeToolStripMenuItem.Text = "Stop Tape"; + _tapManager.Play(); + } + else + { + playTapeToolStripMenuItem.Text = "Play Tape"; + _tapManager.Stop(); + } + + } } } \ No newline at end of file diff --git a/Desktop/ROMS/Snapshot/Dizzy - Trasure Island.sna b/Desktop/ROMS/Snapshot/Dizzy - Trasure Island.sna new file mode 100644 index 0000000..efc7b3e Binary files /dev/null and b/Desktop/ROMS/Snapshot/Dizzy - Trasure Island.sna differ