diff --git a/Core/Memory/MemoryBus.cs b/Core/Memory/MemoryBus.cs index 43d613a..b5295fa 100644 --- a/Core/Memory/MemoryBus.cs +++ b/Core/Memory/MemoryBus.cs @@ -60,10 +60,6 @@ namespace Core.Memory // Copy the ROM Array.Copy(romData, 0, _memory, 0, romData.Length); } - - public void ClearRam() - { - //To Do - } + } } \ No newline at end of file diff --git a/Desktop/DebuggerForm.Designer.cs b/Desktop/DebuggerForm.Designer.cs index 64e9acd..211b446 100644 --- a/Desktop/DebuggerForm.Designer.cs +++ b/Desktop/DebuggerForm.Designer.cs @@ -28,6 +28,7 @@ /// private void InitializeComponent() { + components = new System.ComponentModel.Container(); lblAF = new Label(); lblBC = new Label(); lblDE = new Label(); @@ -54,94 +55,95 @@ lblIff2 = new Label(); lblIE = new Label(); btnReset = new Button(); + uiUpdateTimer = new System.Windows.Forms.Timer(components); SuspendLayout(); // // lblAF // lblAF.AutoSize = true; - lblAF.Location = new Point(12, 9); + lblAF.Location = new Point(10, 7); lblAF.Margin = new Padding(2, 0, 2, 0); lblAF.Name = "lblAF"; - lblAF.Size = new Size(33, 25); + lblAF.Size = new Size(26, 20); lblAF.TabIndex = 0; lblAF.Text = "AF"; // // lblBC // lblBC.AutoSize = true; - lblBC.Location = new Point(11, 60); + lblBC.Location = new Point(9, 48); lblBC.Margin = new Padding(2, 0, 2, 0); lblBC.Name = "lblBC"; - lblBC.Size = new Size(33, 25); + lblBC.Size = new Size(27, 20); lblBC.TabIndex = 1; lblBC.Text = "BC"; // // lblDE // lblDE.AutoSize = true; - lblDE.Location = new Point(12, 125); + lblDE.Location = new Point(10, 100); lblDE.Margin = new Padding(2, 0, 2, 0); lblDE.Name = "lblDE"; - lblDE.Size = new Size(34, 25); + lblDE.Size = new Size(28, 20); lblDE.TabIndex = 2; lblDE.Text = "DE"; // // lblHL // lblHL.AutoSize = true; - lblHL.Location = new Point(12, 188); + lblHL.Location = new Point(10, 150); lblHL.Margin = new Padding(2, 0, 2, 0); lblHL.Name = "lblHL"; - lblHL.Size = new Size(33, 25); + lblHL.Size = new Size(27, 20); lblHL.TabIndex = 3; lblHL.Text = "HL"; // // lblPC // lblPC.AutoSize = true; - lblPC.Location = new Point(11, 250); + lblPC.Location = new Point(9, 200); lblPC.Margin = new Padding(2, 0, 2, 0); lblPC.Name = "lblPC"; - lblPC.Size = new Size(33, 25); + lblPC.Size = new Size(26, 20); lblPC.TabIndex = 4; lblPC.Text = "PC"; // // lblSP // lblSP.AutoSize = true; - lblSP.Location = new Point(11, 315); + lblSP.Location = new Point(9, 252); lblSP.Margin = new Padding(2, 0, 2, 0); lblSP.Name = "lblSP"; - lblSP.Size = new Size(32, 25); + lblSP.Size = new Size(25, 20); lblSP.TabIndex = 6; lblSP.Text = "SP"; // // lblFlags // lblFlags.AutoSize = true; - lblFlags.Location = new Point(110, 65); + lblFlags.Location = new Point(88, 52); lblFlags.Margin = new Padding(2, 0, 2, 0); lblFlags.Name = "lblFlags"; - lblFlags.Size = new Size(53, 25); + lblFlags.Size = new Size(43, 20); lblFlags.TabIndex = 7; lblFlags.Text = "Flags"; // // lblTStates // lblTStates.AutoSize = true; - lblTStates.Location = new Point(110, 9); + lblTStates.Location = new Point(88, 7); lblTStates.Margin = new Padding(2, 0, 2, 0); lblTStates.Name = "lblTStates"; - lblTStates.Size = new Size(75, 25); + lblTStates.Size = new Size(63, 20); lblTStates.TabIndex = 8; lblTStates.Text = "T-States"; // // txtMemoryStart // - txtMemoryStart.Location = new Point(375, 26); + txtMemoryStart.Location = new Point(300, 21); txtMemoryStart.Margin = new Padding(2); txtMemoryStart.Name = "txtMemoryStart"; - txtMemoryStart.Size = new Size(150, 31); + txtMemoryStart.Size = new Size(121, 27); txtMemoryStart.TabIndex = 9; txtMemoryStart.Text = "Memory Start"; txtMemoryStart.TextAlign = HorizontalAlignment.Center; @@ -149,10 +151,10 @@ // // btnStep // - btnStep.Location = new Point(8, 522); + btnStep.Location = new Point(6, 418); btnStep.Margin = new Padding(2); btnStep.Name = "btnStep"; - btnStep.Size = new Size(112, 34); + btnStep.Size = new Size(90, 27); btnStep.TabIndex = 12; btnStep.Text = "Step"; btnStep.UseVisualStyleBackColor = true; @@ -160,21 +162,20 @@ // // btnRun // - btnRun.Location = new Point(149, 522); + btnRun.Location = new Point(119, 418); btnRun.Margin = new Padding(2); btnRun.Name = "btnRun"; - btnRun.Size = new Size(112, 34); + btnRun.Size = new Size(90, 27); btnRun.TabIndex = 13; btnRun.Text = "Run"; btnRun.UseVisualStyleBackColor = true; - btnRun.Click += btnRun_Click; // // btnRefreshMemory // - btnRefreshMemory.Location = new Point(531, 26); + btnRefreshMemory.Location = new Point(425, 21); btnRefreshMemory.Margin = new Padding(2); btnRefreshMemory.Name = "btnRefreshMemory"; - btnRefreshMemory.Size = new Size(112, 34); + btnRefreshMemory.Size = new Size(90, 27); btnRefreshMemory.TabIndex = 14; btnRefreshMemory.Text = "Refresh Memory"; btnRefreshMemory.UseVisualStyleBackColor = true; @@ -182,137 +183,134 @@ // // txtMemoryView // - txtMemoryView.Location = new Point(110, 100); + txtMemoryView.Location = new Point(88, 80); txtMemoryView.Margin = new Padding(2); txtMemoryView.Name = "txtMemoryView"; - txtMemoryView.Size = new Size(519, 243); + txtMemoryView.Size = new Size(416, 195); txtMemoryView.TabIndex = 15; txtMemoryView.Text = "Memory View Window"; // // lstDisassembly // lstDisassembly.FormattingEnabled = true; - lstDisassembly.ItemHeight = 25; - lstDisassembly.Location = new Point(654, 14); + lstDisassembly.Location = new Point(523, 11); lstDisassembly.Margin = new Padding(2); lstDisassembly.Name = "lstDisassembly"; - lstDisassembly.Size = new Size(314, 329); + lstDisassembly.Size = new Size(252, 264); lstDisassembly.TabIndex = 16; // // lstStack // lstStack.FormattingEnabled = true; - lstStack.ItemHeight = 25; - lstStack.Location = new Point(974, 14); + lstStack.Location = new Point(779, 11); lstStack.Margin = new Padding(2); lstStack.Name = "lstStack"; - lstStack.Size = new Size(162, 329); + lstStack.Size = new Size(130, 264); lstStack.TabIndex = 17; // // btnExit // - btnExit.Location = new Point(1019, 520); + btnExit.Location = new Point(815, 416); btnExit.Margin = new Padding(2); btnExit.Name = "btnExit"; - btnExit.Size = new Size(112, 34); + btnExit.Size = new Size(90, 27); btnExit.TabIndex = 18; btnExit.Text = "Full Exit"; btnExit.UseVisualStyleBackColor = true; - btnExit.Click += btnExit_Click; // // label1 // label1.AutoSize = true; - label1.Location = new Point(250, 348); - label1.Margin = new Padding(4, 0, 4, 0); + label1.Location = new Point(289, 284); label1.Name = "label1"; - label1.Size = new Size(97, 25); + label1.Size = new Size(81, 20); label1.TabIndex = 19; label1.Text = "Breakpoint"; // // txtBreakpoint // - txtBreakpoint.Location = new Point(470, 351); - txtBreakpoint.Margin = new Padding(4); + txtBreakpoint.Location = new Point(376, 281); txtBreakpoint.Name = "txtBreakpoint"; - txtBreakpoint.Size = new Size(155, 31); + txtBreakpoint.Size = new Size(125, 27); txtBreakpoint.TabIndex = 20; // // label2 // label2.AutoSize = true; - label2.Location = new Point(291, 26); - label2.Margin = new Padding(4, 0, 4, 0); + label2.Location = new Point(233, 21); label2.Name = "label2"; - label2.Size = new Size(77, 25); + label2.Size = new Size(62, 20); label2.TabIndex = 21; label2.Text = "Address"; // // lblIX // lblIX.AutoSize = true; - lblIX.Location = new Point(11, 372); + lblIX.Location = new Point(9, 298); lblIX.Margin = new Padding(2, 0, 2, 0); lblIX.Name = "lblIX"; - lblIX.Size = new Size(28, 25); + lblIX.Size = new Size(22, 20); lblIX.TabIndex = 22; lblIX.Text = "IX"; // // lblIY // lblIY.AutoSize = true; - lblIY.Location = new Point(12, 430); + lblIY.Location = new Point(10, 344); lblIY.Margin = new Padding(2, 0, 2, 0); lblIY.Name = "lblIY"; - lblIY.Size = new Size(27, 25); + lblIY.Size = new Size(21, 20); lblIY.TabIndex = 23; lblIY.Text = "IY"; // // lblIff1 // lblIff1.AutoSize = true; - lblIff1.Location = new Point(110, 372); - lblIff1.Margin = new Padding(4, 0, 4, 0); + lblIff1.Location = new Point(88, 298); lblIff1.Name = "lblIff1"; - lblIff1.Size = new Size(45, 25); + lblIff1.Size = new Size(35, 20); lblIff1.TabIndex = 24; lblIff1.Text = "IFF1"; // // lblIff2 // lblIff2.AutoSize = true; - lblIff2.Location = new Point(110, 431); - lblIff2.Margin = new Padding(4, 0, 4, 0); + lblIff2.Location = new Point(88, 345); lblIff2.Name = "lblIff2"; - lblIff2.Size = new Size(45, 25); + lblIff2.Size = new Size(35, 20); lblIff2.TabIndex = 25; lblIff2.Text = "IFF2"; // // lblIE // lblIE.AutoSize = true; - lblIE.Location = new Point(250, 431); - lblIE.Margin = new Padding(4, 0, 4, 0); + lblIE.Location = new Point(200, 345); lblIE.Name = "lblIE"; - lblIE.Size = new Size(133, 25); + lblIE.Size = new Size(109, 20); lblIE.TabIndex = 26; lblIE.Text = "Interrupt Mode"; // // btnReset // - btnReset.Location = new Point(883, 520); + btnReset.Location = new Point(372, 313); + btnReset.Margin = new Padding(2); btnReset.Name = "btnReset"; - btnReset.Size = new Size(112, 34); + btnReset.Size = new Size(132, 27); btnReset.TabIndex = 27; - btnReset.Text = "Hard Reset"; + btnReset.Text = "Set Breakpoint"; btnReset.UseVisualStyleBackColor = true; - btnReset.Click += btnReset_Click; + btnReset.Click += btnSetBreakpoint_Click; + // + // uiUpdateTimer + // + uiUpdateTimer.Enabled = true; + uiUpdateTimer.Tick += uiUpdateTimer_Tick; // // DebuggerForm // - AutoScaleDimensions = new SizeF(10F, 25F); + AutoScaleDimensions = new SizeF(8F, 20F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(1160, 568); + ClientSize = new Size(928, 454); Controls.Add(btnReset); Controls.Add(lblIE); Controls.Add(lblIff2); @@ -373,6 +371,7 @@ private Label lblIff2; private Label lblIE; private Button btnReset; + private System.Windows.Forms.Timer uiUpdateTimer; //private TextBox textBox4; } } \ No newline at end of file diff --git a/Desktop/DebuggerForm.cs b/Desktop/DebuggerForm.cs index 0a2fce5..40a8f0e 100644 --- a/Desktop/DebuggerForm.cs +++ b/Desktop/DebuggerForm.cs @@ -40,9 +40,17 @@ namespace Desktop { MessageBox.Show(ex.Message, "CPU Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } - finally + } + + private void btnSetBreakpoint_Click(object sender, EventArgs e) // Hook this to a button or text changed event + { + if (ushort.TryParse(txtBreakpoint.Text, System.Globalization.NumberStyles.HexNumber, null, out ushort parsedBp)) { - UpdateDisplay(); + _mainForm.Breakpoint = parsedBp; + } + else + { + _mainForm.Breakpoint = null; } } @@ -51,156 +59,13 @@ namespace Desktop UpdateDisplay(); } - private async void btnRun_Click(object sender, EventArgs e) + private void uiUpdateTimer_Tick(object sender, EventArgs e) { - //Stops - if (_isRunning) - { - _isRunning = false; - btnRun.Text = "Run"; - return; - } - - //Parse the Breakpoint - if (!string.IsNullOrWhiteSpace(txtBreakpoint.Text)) - { - if (ushort.TryParse(txtBreakpoint.Text, System.Globalization.NumberStyles.HexNumber, null, out ushort parsedBp)) - { - _breakpoint = parsedBp; - } - else - { - MessageBox.Show("Invalid hex address in breakpoint!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); - return; - } - } - else - { - _breakpoint = null; - } - - // Start the run state - _isRunning = true; - btnRun.Text = "Stop"; - - //Background thread - await Task.Run(() => - { - try - { - // 1. Setup Frame Timing Variables - const int TStatesPerFrame = 69888; - long nextFrameTargetTStates = _cpu.TotalTStates + TStatesPerFrame; - - // 2. Setup Real-World Throttling - var stopwatch = System.Diagnostics.Stopwatch.StartNew(); - long frameCount = 0; - - while (_isRunning) - { - // --- Breakpoint Check --- - if (_breakpoint.HasValue && _cpu.PC == _breakpoint.Value) - { - _isRunning = false; - break; - } - - // --- Execute Instruction --- - _cpu.Step(); - //if (_cpu.TotalTStates % 1000 == 0) - //{ - // this.Invoke((MethodInvoker)delegate - // { - // UpdateDisplay(); - // }); - //} - - // --- Check for End of Frame --- - if (_cpu.TotalTStates >= nextFrameTargetTStates) - { - // 1. Fire the 50Hz Interrupt! - _cpu.RequestInterrupt(); - - // 2. Advance the target to the next frame - nextFrameTargetTStates += TStatesPerFrame; - frameCount++; - - //3 Render the screen - _mainForm.Invoke((MethodInvoker)delegate - { - _mainForm.RenderScreen(); - }); - - // 4. Throttle to real-time (50 frames per second = 20ms per frame) - long targetTimeMs = frameCount * 20; - long elapsedMs = stopwatch.ElapsedMilliseconds; - - if (elapsedMs < targetTimeMs) - { - // The CPU ran too fast! Put the thread to sleep to let reality catch up. - System.Threading.Thread.Sleep((int)(targetTimeMs - elapsedMs)); - } - - // Optional: Update the UI every 10 frames so you can watch it run safely - // without overwhelming the WinForms rendering engine. - if (frameCount % 1 == 0) - { - this.Invoke((MethodInvoker)delegate - { - UpdateDisplay(); - }); - } - } - //this.Invoke((MethodInvoker)delegate { - // UpdateDisplay(); - // }); - } - } - catch (Exception ex) - { - _isRunning = false; - - this.Invoke((MethodInvoker)delegate - { - MessageBox.Show(ex.Message, "CPU Break", MessageBoxButtons.OK, MessageBoxIcon.Information); - }); - } - }); - - //update the UI when the background thread finishes - btnRun.Text = "Run"; + // Every 100ms, take a snapshot of the CPU state and draw it to the screen. + // This makes the registers look like they are spinning matrix-style while running! UpdateDisplay(); } - private void btnReset_Click(object sender, EventArgs e) - { - // 1. Safely stop the emulator if it is currently in a Run loop - if (_isRunning) - { - _isRunning = false; - btnRun.Text = "Run"; - } - - // 2. Power cycle the CPU - _cpu.Reset(); - - // Note: A true hard reset also wipes the RAM. - // If you add a RAM clear method to your MemoryBus later, call it here! - _memoryBus.ClearRam(); //To Do - - // 3. Clear the UI tracking lists - lstDisassembly.Items.Clear(); - lstStack.Items.Clear(); - - // 4. Force a full UI refresh to show the clean slate - UpdateDisplay(); - } - - private void btnExit_Click(object sender, EventArgs e) - { - Environment.Exit(0); - } - // This is the master function that pulls state from the CPU private void UpdateDisplay() { diff --git a/Desktop/DebuggerForm.resx b/Desktop/DebuggerForm.resx index 5261a14..7d0a62d 100644 --- a/Desktop/DebuggerForm.resx +++ b/Desktop/DebuggerForm.resx @@ -120,4 +120,7 @@ 17, 17 + + 182, 17 + \ No newline at end of file diff --git a/Desktop/Form1.Designer.cs b/Desktop/Form1.Designer.cs index 39f7353..076b9ad 100644 --- a/Desktop/Form1.Designer.cs +++ b/Desktop/Form1.Designer.cs @@ -32,7 +32,16 @@ menuStrip1 = new MenuStrip(); fileToolStripMenuItem = new ToolStripMenuItem(); openToolStripMenuItem = new ToolStripMenuItem(); - openSnapshotToolStripMenuItem = new ToolStripMenuItem(); + tAPToolStripMenuItem = new ToolStripMenuItem(); + sNAToolStripMenuItem = 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(); ((System.ComponentModel.ISupportInitialize)picScreen).BeginInit(); menuStrip1.SuspendLayout(); SuspendLayout(); @@ -40,10 +49,9 @@ // picScreen // picScreen.BackColor = Color.Black; - picScreen.Location = new Point(13, 33); - picScreen.Margin = new Padding(4); + picScreen.Location = new Point(10, 26); picScreen.Name = "picScreen"; - picScreen.Size = new Size(900, 720); + picScreen.Size = new Size(720, 576); picScreen.SizeMode = PictureBoxSizeMode.Zoom; picScreen.TabIndex = 0; picScreen.TabStop = false; @@ -51,44 +59,107 @@ // menuStrip1 // menuStrip1.ImageScalingSize = new Size(24, 24); - menuStrip1.Items.AddRange(new ToolStripItem[] { fileToolStripMenuItem }); + menuStrip1.Items.AddRange(new ToolStripItem[] { fileToolStripMenuItem, viewToolStripMenuItem, machineToolStripMenuItem }); menuStrip1.Location = new Point(0, 0); menuStrip1.Name = "menuStrip1"; - menuStrip1.Size = new Size(922, 33); + menuStrip1.Padding = new Padding(5, 2, 0, 2); + menuStrip1.Size = new Size(738, 28); menuStrip1.TabIndex = 1; menuStrip1.Text = "menuStrip1"; // // fileToolStripMenuItem // - fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { openToolStripMenuItem, openSnapshotToolStripMenuItem }); + fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { openToolStripMenuItem, exitToolStripMenuItem }); fileToolStripMenuItem.Name = "fileToolStripMenuItem"; - fileToolStripMenuItem.Size = new Size(54, 29); + fileToolStripMenuItem.Size = new Size(46, 24); fileToolStripMenuItem.Text = "File"; // // openToolStripMenuItem // + openToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { tAPToolStripMenuItem, sNAToolStripMenuItem }); openToolStripMenuItem.Name = "openToolStripMenuItem"; - openToolStripMenuItem.Size = new Size(270, 34); - openToolStripMenuItem.Text = "Open TAP"; - openToolStripMenuItem.Click += loadTAPToolStripMenuItem_Click; + openToolStripMenuItem.Size = new Size(128, 26); + openToolStripMenuItem.Text = "Open"; // - // openSnapshotToolStripMenuItem + // tAPToolStripMenuItem // - openSnapshotToolStripMenuItem.Name = "openSnapshotToolStripMenuItem"; - openSnapshotToolStripMenuItem.Size = new Size(270, 34); - openSnapshotToolStripMenuItem.Text = "Open Snapshot"; - openSnapshotToolStripMenuItem.Click += openSNAToolStripMenuItem_Click; + tAPToolStripMenuItem.Name = "tAPToolStripMenuItem"; + tAPToolStripMenuItem.Size = new Size(121, 26); + tAPToolStripMenuItem.Text = "TAP"; + tAPToolStripMenuItem.Click += loadTAPToolStripMenuItem_Click; + // + // sNAToolStripMenuItem + // + sNAToolStripMenuItem.Name = "sNAToolStripMenuItem"; + sNAToolStripMenuItem.Size = new Size(121, 26); + sNAToolStripMenuItem.Text = "SNA"; + sNAToolStripMenuItem.Click += openSNAToolStripMenuItem_Click; + // + // exitToolStripMenuItem + // + exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + exitToolStripMenuItem.Size = new Size(128, 26); + exitToolStripMenuItem.Text = "Exit"; + exitToolStripMenuItem.Click += btnExit_Click; + // + // viewToolStripMenuItem + // + viewToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { debuggerToolStripMenuItem }); + viewToolStripMenuItem.Name = "viewToolStripMenuItem"; + viewToolStripMenuItem.Size = new Size(55, 24); + viewToolStripMenuItem.Text = "View"; + // + // debuggerToolStripMenuItem + // + debuggerToolStripMenuItem.Name = "debuggerToolStripMenuItem"; + debuggerToolStripMenuItem.Size = new Size(224, 26); + 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(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; // // Form1 // - AutoScaleDimensions = new SizeF(10F, 25F); + AutoScaleDimensions = new SizeF(8F, 20F); AutoScaleMode = AutoScaleMode.Font; - ClientSize = new Size(922, 760); + ClientSize = new Size(738, 608); Controls.Add(picScreen); Controls.Add(menuStrip1); KeyPreview = true; MainMenuStrip = menuStrip1; - Margin = new Padding(4); Name = "Form1"; Text = "Parsons Sinclair ZX Spectrum 48K - 2026"; ((System.ComponentModel.ISupportInitialize)picScreen).EndInit(); @@ -104,6 +175,15 @@ private MenuStrip menuStrip1; private ToolStripMenuItem fileToolStripMenuItem; private ToolStripMenuItem openToolStripMenuItem; - private ToolStripMenuItem openSnapshotToolStripMenuItem; + private ToolStripMenuItem tAPToolStripMenuItem; + private ToolStripMenuItem sNAToolStripMenuItem; + private ToolStripMenuItem exitToolStripMenuItem; + private ToolStripMenuItem viewToolStripMenuItem; + private ToolStripMenuItem debuggerToolStripMenuItem; + private ToolStripMenuItem machineToolStripMenuItem; + private ToolStripMenuItem runToolStripMenuItem; + private ToolStripMenuItem resetToolStripMenuItem; + private ToolStripMenuItem stepToolStripMenuItem; + private ToolStripMenuItem resetToolStripMenuItem1; } } diff --git a/Desktop/Form1.cs b/Desktop/Form1.cs index 7a14a07..82eb89a 100644 --- a/Desktop/Form1.cs +++ b/Desktop/Form1.cs @@ -16,6 +16,11 @@ namespace Desktop private IO_Bus _simpleIoBus = null!; private TapManager _tapManager = null!; private int _ulaFrameCount = 0; + private bool _isRunning = false; + private bool _isPaused = false; + private bool _resetFlag = false; + public ushort? Breakpoint = null; // Public so the debugger can set it! + private DebuggerForm _debugger = null; // The 16 physical colors of the ZX Spectrum (ARGB format) private readonly int[] SpectrumColors = new int[] @@ -61,16 +66,88 @@ namespace Desktop _cpu = new Z80(_memoryBus, _simpleIoBus, _tapManager); // Pass 'this' so the DebuggerForm can talk back to this main window - DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus, this); - debugger.Show(); + //DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus, this); + //debugger.Show(); } catch (Exception ex) { MessageBox.Show($"Failed to initialize emulator:\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } + StartEmulationLoop(); + } + + private void StartEmulationLoop() + { + if (_isRunning) return; + _isRunning = true; + _isPaused = false; + + Task.Run(() => + { + try + { + const int TStatesPerFrame = 69888; + long nextFrameTargetTStates = _cpu.TotalTStates + TStatesPerFrame; + var stopwatch = System.Diagnostics.Stopwatch.StartNew(); + long frameCount = 0; + + while (_isRunning) + { + //if(_resetFlag) + //{ + // _resetFlag = false; + // stopwatch.Reset(); + // nextFrameTargetTStates = _cpu.TotalTStates + TStatesPerFrame; + // frameCount = 0; + //} + if (_isPaused) + { + System.Threading.Thread.Sleep(10); // Don't melt the host CPU while paused + continue; + } + + // --- Breakpoint Check --- + if (Breakpoint.HasValue && _cpu.PC == Breakpoint.Value) + { + _isPaused = true; + // Optional: You could force the debugger to open here! + continue; + } + + // --- Execute Instruction --- + _cpu.Step(); + + // --- Check for End of Frame --- + if (_cpu.TotalTStates >= nextFrameTargetTStates) + { + _cpu.RequestInterrupt(); + nextFrameTargetTStates += TStatesPerFrame; + frameCount++; + + // Render the screen + this.Invoke((MethodInvoker)delegate { RenderScreen(); }); + + // Throttle to real-time (50 FPS = 20ms) + long targetTimeMs = frameCount * 20; + long elapsedMs = stopwatch.ElapsedMilliseconds; + + if (elapsedMs < targetTimeMs) + { + System.Threading.Thread.Sleep((int)(targetTimeMs - elapsedMs)); + } + } + } + } + catch (Exception ex) + { + _isPaused = true; + this.Invoke((MethodInvoker)delegate { + MessageBox.Show(ex.Message, "CPU Crash", MessageBoxButtons.OK, MessageBoxIcon.Error); + }); + } + }); } - // Inside Desktop/Form1.cs private void loadTAPToolStripMenuItem_Click(object sender, EventArgs e) { using (OpenFileDialog ofd = new OpenFileDialog()) @@ -101,6 +178,42 @@ namespace Desktop } } + private void btnRun_Click(object sender, EventArgs e) => _isPaused = false; + + private void btnPause_Click(object sender, EventArgs e) => _isPaused = true; + + private void btnStep_Click(object sender, EventArgs e) + { + if (_isPaused) _cpu.Step(); + } + + private void btnReset_Click(object sender, EventArgs e) + { + //_resetFlag = true; + _isPaused = true; + _cpu.Reset(); + _memoryBus.CleanRAMData(); + _isPaused = false; + } + + private void btnExit_Click(object sender, EventArgs e) + { + Environment.Exit(0); + } + + private void openDebuggerToolStripMenuItem_Click(object sender, EventArgs e) + { + if (_debugger == null || _debugger.IsDisposed) + { + _debugger = new DebuggerForm(_cpu, _memoryBus, this); + _debugger.Show(); + } + else + { + _debugger.BringToFront(); + } + } + // Public so the Debugger's background thread can call it 50 times a second public void RenderScreen() {