Changed the structure of the emulator

This commit is contained in:
2026-04-20 14:31:06 +01:00
parent 7464b29fca
commit adbf64a84d
6 changed files with 296 additions and 240 deletions

View File

@@ -61,9 +61,5 @@ namespace Core.Memory
Array.Copy(romData, 0, _memory, 0, romData.Length); Array.Copy(romData, 0, _memory, 0, romData.Length);
} }
public void ClearRam()
{
//To Do
}
} }
} }

View File

@@ -28,6 +28,7 @@
/// </summary> /// </summary>
private void InitializeComponent() private void InitializeComponent()
{ {
components = new System.ComponentModel.Container();
lblAF = new Label(); lblAF = new Label();
lblBC = new Label(); lblBC = new Label();
lblDE = new Label(); lblDE = new Label();
@@ -54,94 +55,95 @@
lblIff2 = new Label(); lblIff2 = new Label();
lblIE = new Label(); lblIE = new Label();
btnReset = new Button(); btnReset = new Button();
uiUpdateTimer = new System.Windows.Forms.Timer(components);
SuspendLayout(); SuspendLayout();
// //
// lblAF // lblAF
// //
lblAF.AutoSize = true; lblAF.AutoSize = true;
lblAF.Location = new Point(12, 9); lblAF.Location = new Point(10, 7);
lblAF.Margin = new Padding(2, 0, 2, 0); lblAF.Margin = new Padding(2, 0, 2, 0);
lblAF.Name = "lblAF"; lblAF.Name = "lblAF";
lblAF.Size = new Size(33, 25); lblAF.Size = new Size(26, 20);
lblAF.TabIndex = 0; lblAF.TabIndex = 0;
lblAF.Text = "AF"; lblAF.Text = "AF";
// //
// lblBC // lblBC
// //
lblBC.AutoSize = true; lblBC.AutoSize = true;
lblBC.Location = new Point(11, 60); lblBC.Location = new Point(9, 48);
lblBC.Margin = new Padding(2, 0, 2, 0); lblBC.Margin = new Padding(2, 0, 2, 0);
lblBC.Name = "lblBC"; lblBC.Name = "lblBC";
lblBC.Size = new Size(33, 25); lblBC.Size = new Size(27, 20);
lblBC.TabIndex = 1; lblBC.TabIndex = 1;
lblBC.Text = "BC"; lblBC.Text = "BC";
// //
// lblDE // lblDE
// //
lblDE.AutoSize = true; lblDE.AutoSize = true;
lblDE.Location = new Point(12, 125); lblDE.Location = new Point(10, 100);
lblDE.Margin = new Padding(2, 0, 2, 0); lblDE.Margin = new Padding(2, 0, 2, 0);
lblDE.Name = "lblDE"; lblDE.Name = "lblDE";
lblDE.Size = new Size(34, 25); lblDE.Size = new Size(28, 20);
lblDE.TabIndex = 2; lblDE.TabIndex = 2;
lblDE.Text = "DE"; lblDE.Text = "DE";
// //
// lblHL // lblHL
// //
lblHL.AutoSize = true; lblHL.AutoSize = true;
lblHL.Location = new Point(12, 188); lblHL.Location = new Point(10, 150);
lblHL.Margin = new Padding(2, 0, 2, 0); lblHL.Margin = new Padding(2, 0, 2, 0);
lblHL.Name = "lblHL"; lblHL.Name = "lblHL";
lblHL.Size = new Size(33, 25); lblHL.Size = new Size(27, 20);
lblHL.TabIndex = 3; lblHL.TabIndex = 3;
lblHL.Text = "HL"; lblHL.Text = "HL";
// //
// lblPC // lblPC
// //
lblPC.AutoSize = true; lblPC.AutoSize = true;
lblPC.Location = new Point(11, 250); lblPC.Location = new Point(9, 200);
lblPC.Margin = new Padding(2, 0, 2, 0); lblPC.Margin = new Padding(2, 0, 2, 0);
lblPC.Name = "lblPC"; lblPC.Name = "lblPC";
lblPC.Size = new Size(33, 25); lblPC.Size = new Size(26, 20);
lblPC.TabIndex = 4; lblPC.TabIndex = 4;
lblPC.Text = "PC"; lblPC.Text = "PC";
// //
// lblSP // lblSP
// //
lblSP.AutoSize = true; lblSP.AutoSize = true;
lblSP.Location = new Point(11, 315); lblSP.Location = new Point(9, 252);
lblSP.Margin = new Padding(2, 0, 2, 0); lblSP.Margin = new Padding(2, 0, 2, 0);
lblSP.Name = "lblSP"; lblSP.Name = "lblSP";
lblSP.Size = new Size(32, 25); lblSP.Size = new Size(25, 20);
lblSP.TabIndex = 6; lblSP.TabIndex = 6;
lblSP.Text = "SP"; lblSP.Text = "SP";
// //
// lblFlags // lblFlags
// //
lblFlags.AutoSize = true; lblFlags.AutoSize = true;
lblFlags.Location = new Point(110, 65); lblFlags.Location = new Point(88, 52);
lblFlags.Margin = new Padding(2, 0, 2, 0); lblFlags.Margin = new Padding(2, 0, 2, 0);
lblFlags.Name = "lblFlags"; lblFlags.Name = "lblFlags";
lblFlags.Size = new Size(53, 25); lblFlags.Size = new Size(43, 20);
lblFlags.TabIndex = 7; lblFlags.TabIndex = 7;
lblFlags.Text = "Flags"; lblFlags.Text = "Flags";
// //
// lblTStates // lblTStates
// //
lblTStates.AutoSize = true; lblTStates.AutoSize = true;
lblTStates.Location = new Point(110, 9); lblTStates.Location = new Point(88, 7);
lblTStates.Margin = new Padding(2, 0, 2, 0); lblTStates.Margin = new Padding(2, 0, 2, 0);
lblTStates.Name = "lblTStates"; lblTStates.Name = "lblTStates";
lblTStates.Size = new Size(75, 25); lblTStates.Size = new Size(63, 20);
lblTStates.TabIndex = 8; lblTStates.TabIndex = 8;
lblTStates.Text = "T-States"; lblTStates.Text = "T-States";
// //
// txtMemoryStart // txtMemoryStart
// //
txtMemoryStart.Location = new Point(375, 26); txtMemoryStart.Location = new Point(300, 21);
txtMemoryStart.Margin = new Padding(2); txtMemoryStart.Margin = new Padding(2);
txtMemoryStart.Name = "txtMemoryStart"; txtMemoryStart.Name = "txtMemoryStart";
txtMemoryStart.Size = new Size(150, 31); txtMemoryStart.Size = new Size(121, 27);
txtMemoryStart.TabIndex = 9; txtMemoryStart.TabIndex = 9;
txtMemoryStart.Text = "Memory Start"; txtMemoryStart.Text = "Memory Start";
txtMemoryStart.TextAlign = HorizontalAlignment.Center; txtMemoryStart.TextAlign = HorizontalAlignment.Center;
@@ -149,10 +151,10 @@
// //
// btnStep // btnStep
// //
btnStep.Location = new Point(8, 522); btnStep.Location = new Point(6, 418);
btnStep.Margin = new Padding(2); btnStep.Margin = new Padding(2);
btnStep.Name = "btnStep"; btnStep.Name = "btnStep";
btnStep.Size = new Size(112, 34); btnStep.Size = new Size(90, 27);
btnStep.TabIndex = 12; btnStep.TabIndex = 12;
btnStep.Text = "Step"; btnStep.Text = "Step";
btnStep.UseVisualStyleBackColor = true; btnStep.UseVisualStyleBackColor = true;
@@ -160,21 +162,20 @@
// //
// btnRun // btnRun
// //
btnRun.Location = new Point(149, 522); btnRun.Location = new Point(119, 418);
btnRun.Margin = new Padding(2); btnRun.Margin = new Padding(2);
btnRun.Name = "btnRun"; btnRun.Name = "btnRun";
btnRun.Size = new Size(112, 34); btnRun.Size = new Size(90, 27);
btnRun.TabIndex = 13; btnRun.TabIndex = 13;
btnRun.Text = "Run"; btnRun.Text = "Run";
btnRun.UseVisualStyleBackColor = true; btnRun.UseVisualStyleBackColor = true;
btnRun.Click += btnRun_Click;
// //
// btnRefreshMemory // btnRefreshMemory
// //
btnRefreshMemory.Location = new Point(531, 26); btnRefreshMemory.Location = new Point(425, 21);
btnRefreshMemory.Margin = new Padding(2); btnRefreshMemory.Margin = new Padding(2);
btnRefreshMemory.Name = "btnRefreshMemory"; btnRefreshMemory.Name = "btnRefreshMemory";
btnRefreshMemory.Size = new Size(112, 34); btnRefreshMemory.Size = new Size(90, 27);
btnRefreshMemory.TabIndex = 14; btnRefreshMemory.TabIndex = 14;
btnRefreshMemory.Text = "Refresh Memory"; btnRefreshMemory.Text = "Refresh Memory";
btnRefreshMemory.UseVisualStyleBackColor = true; btnRefreshMemory.UseVisualStyleBackColor = true;
@@ -182,137 +183,134 @@
// //
// txtMemoryView // txtMemoryView
// //
txtMemoryView.Location = new Point(110, 100); txtMemoryView.Location = new Point(88, 80);
txtMemoryView.Margin = new Padding(2); txtMemoryView.Margin = new Padding(2);
txtMemoryView.Name = "txtMemoryView"; txtMemoryView.Name = "txtMemoryView";
txtMemoryView.Size = new Size(519, 243); txtMemoryView.Size = new Size(416, 195);
txtMemoryView.TabIndex = 15; txtMemoryView.TabIndex = 15;
txtMemoryView.Text = "Memory View Window"; txtMemoryView.Text = "Memory View Window";
// //
// lstDisassembly // lstDisassembly
// //
lstDisassembly.FormattingEnabled = true; lstDisassembly.FormattingEnabled = true;
lstDisassembly.ItemHeight = 25; lstDisassembly.Location = new Point(523, 11);
lstDisassembly.Location = new Point(654, 14);
lstDisassembly.Margin = new Padding(2); lstDisassembly.Margin = new Padding(2);
lstDisassembly.Name = "lstDisassembly"; lstDisassembly.Name = "lstDisassembly";
lstDisassembly.Size = new Size(314, 329); lstDisassembly.Size = new Size(252, 264);
lstDisassembly.TabIndex = 16; lstDisassembly.TabIndex = 16;
// //
// lstStack // lstStack
// //
lstStack.FormattingEnabled = true; lstStack.FormattingEnabled = true;
lstStack.ItemHeight = 25; lstStack.Location = new Point(779, 11);
lstStack.Location = new Point(974, 14);
lstStack.Margin = new Padding(2); lstStack.Margin = new Padding(2);
lstStack.Name = "lstStack"; lstStack.Name = "lstStack";
lstStack.Size = new Size(162, 329); lstStack.Size = new Size(130, 264);
lstStack.TabIndex = 17; lstStack.TabIndex = 17;
// //
// btnExit // btnExit
// //
btnExit.Location = new Point(1019, 520); btnExit.Location = new Point(815, 416);
btnExit.Margin = new Padding(2); btnExit.Margin = new Padding(2);
btnExit.Name = "btnExit"; btnExit.Name = "btnExit";
btnExit.Size = new Size(112, 34); btnExit.Size = new Size(90, 27);
btnExit.TabIndex = 18; btnExit.TabIndex = 18;
btnExit.Text = "Full Exit"; btnExit.Text = "Full Exit";
btnExit.UseVisualStyleBackColor = true; btnExit.UseVisualStyleBackColor = true;
btnExit.Click += btnExit_Click;
// //
// label1 // label1
// //
label1.AutoSize = true; label1.AutoSize = true;
label1.Location = new Point(250, 348); label1.Location = new Point(289, 284);
label1.Margin = new Padding(4, 0, 4, 0);
label1.Name = "label1"; label1.Name = "label1";
label1.Size = new Size(97, 25); label1.Size = new Size(81, 20);
label1.TabIndex = 19; label1.TabIndex = 19;
label1.Text = "Breakpoint"; label1.Text = "Breakpoint";
// //
// txtBreakpoint // txtBreakpoint
// //
txtBreakpoint.Location = new Point(470, 351); txtBreakpoint.Location = new Point(376, 281);
txtBreakpoint.Margin = new Padding(4);
txtBreakpoint.Name = "txtBreakpoint"; txtBreakpoint.Name = "txtBreakpoint";
txtBreakpoint.Size = new Size(155, 31); txtBreakpoint.Size = new Size(125, 27);
txtBreakpoint.TabIndex = 20; txtBreakpoint.TabIndex = 20;
// //
// label2 // label2
// //
label2.AutoSize = true; label2.AutoSize = true;
label2.Location = new Point(291, 26); label2.Location = new Point(233, 21);
label2.Margin = new Padding(4, 0, 4, 0);
label2.Name = "label2"; label2.Name = "label2";
label2.Size = new Size(77, 25); label2.Size = new Size(62, 20);
label2.TabIndex = 21; label2.TabIndex = 21;
label2.Text = "Address"; label2.Text = "Address";
// //
// lblIX // lblIX
// //
lblIX.AutoSize = true; lblIX.AutoSize = true;
lblIX.Location = new Point(11, 372); lblIX.Location = new Point(9, 298);
lblIX.Margin = new Padding(2, 0, 2, 0); lblIX.Margin = new Padding(2, 0, 2, 0);
lblIX.Name = "lblIX"; lblIX.Name = "lblIX";
lblIX.Size = new Size(28, 25); lblIX.Size = new Size(22, 20);
lblIX.TabIndex = 22; lblIX.TabIndex = 22;
lblIX.Text = "IX"; lblIX.Text = "IX";
// //
// lblIY // lblIY
// //
lblIY.AutoSize = true; lblIY.AutoSize = true;
lblIY.Location = new Point(12, 430); lblIY.Location = new Point(10, 344);
lblIY.Margin = new Padding(2, 0, 2, 0); lblIY.Margin = new Padding(2, 0, 2, 0);
lblIY.Name = "lblIY"; lblIY.Name = "lblIY";
lblIY.Size = new Size(27, 25); lblIY.Size = new Size(21, 20);
lblIY.TabIndex = 23; lblIY.TabIndex = 23;
lblIY.Text = "IY"; lblIY.Text = "IY";
// //
// lblIff1 // lblIff1
// //
lblIff1.AutoSize = true; lblIff1.AutoSize = true;
lblIff1.Location = new Point(110, 372); lblIff1.Location = new Point(88, 298);
lblIff1.Margin = new Padding(4, 0, 4, 0);
lblIff1.Name = "lblIff1"; lblIff1.Name = "lblIff1";
lblIff1.Size = new Size(45, 25); lblIff1.Size = new Size(35, 20);
lblIff1.TabIndex = 24; lblIff1.TabIndex = 24;
lblIff1.Text = "IFF1"; lblIff1.Text = "IFF1";
// //
// lblIff2 // lblIff2
// //
lblIff2.AutoSize = true; lblIff2.AutoSize = true;
lblIff2.Location = new Point(110, 431); lblIff2.Location = new Point(88, 345);
lblIff2.Margin = new Padding(4, 0, 4, 0);
lblIff2.Name = "lblIff2"; lblIff2.Name = "lblIff2";
lblIff2.Size = new Size(45, 25); lblIff2.Size = new Size(35, 20);
lblIff2.TabIndex = 25; lblIff2.TabIndex = 25;
lblIff2.Text = "IFF2"; lblIff2.Text = "IFF2";
// //
// lblIE // lblIE
// //
lblIE.AutoSize = true; lblIE.AutoSize = true;
lblIE.Location = new Point(250, 431); lblIE.Location = new Point(200, 345);
lblIE.Margin = new Padding(4, 0, 4, 0);
lblIE.Name = "lblIE"; lblIE.Name = "lblIE";
lblIE.Size = new Size(133, 25); lblIE.Size = new Size(109, 20);
lblIE.TabIndex = 26; lblIE.TabIndex = 26;
lblIE.Text = "Interrupt Mode"; lblIE.Text = "Interrupt Mode";
// //
// btnReset // btnReset
// //
btnReset.Location = new Point(883, 520); btnReset.Location = new Point(372, 313);
btnReset.Margin = new Padding(2);
btnReset.Name = "btnReset"; btnReset.Name = "btnReset";
btnReset.Size = new Size(112, 34); btnReset.Size = new Size(132, 27);
btnReset.TabIndex = 27; btnReset.TabIndex = 27;
btnReset.Text = "Hard Reset"; btnReset.Text = "Set Breakpoint";
btnReset.UseVisualStyleBackColor = true; btnReset.UseVisualStyleBackColor = true;
btnReset.Click += btnReset_Click; btnReset.Click += btnSetBreakpoint_Click;
//
// uiUpdateTimer
//
uiUpdateTimer.Enabled = true;
uiUpdateTimer.Tick += uiUpdateTimer_Tick;
// //
// DebuggerForm // DebuggerForm
// //
AutoScaleDimensions = new SizeF(10F, 25F); AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1160, 568); ClientSize = new Size(928, 454);
Controls.Add(btnReset); Controls.Add(btnReset);
Controls.Add(lblIE); Controls.Add(lblIE);
Controls.Add(lblIff2); Controls.Add(lblIff2);
@@ -373,6 +371,7 @@
private Label lblIff2; private Label lblIff2;
private Label lblIE; private Label lblIE;
private Button btnReset; private Button btnReset;
private System.Windows.Forms.Timer uiUpdateTimer;
//private TextBox textBox4; //private TextBox textBox4;
} }
} }

View File

@@ -40,9 +40,17 @@ namespace Desktop
{ {
MessageBox.Show(ex.Message, "CPU Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 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
{ {
UpdateDisplay(); if (ushort.TryParse(txtBreakpoint.Text, System.Globalization.NumberStyles.HexNumber, null, out ushort parsedBp))
{
_mainForm.Breakpoint = parsedBp;
}
else
{
_mainForm.Breakpoint = null;
} }
} }
@@ -51,154 +59,11 @@ namespace Desktop
UpdateDisplay(); 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
{ {
// 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(); 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";
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 // This is the master function that pulls state from the CPU

View File

@@ -120,4 +120,7 @@
<metadata name="saveFileDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="saveFileDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value> <value>17, 17</value>
</metadata> </metadata>
<metadata name="uiUpdateTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>182, 17</value>
</metadata>
</root> </root>

View File

@@ -32,7 +32,16 @@
menuStrip1 = new MenuStrip(); menuStrip1 = new MenuStrip();
fileToolStripMenuItem = new ToolStripMenuItem(); fileToolStripMenuItem = new ToolStripMenuItem();
openToolStripMenuItem = 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(); ((System.ComponentModel.ISupportInitialize)picScreen).BeginInit();
menuStrip1.SuspendLayout(); menuStrip1.SuspendLayout();
SuspendLayout(); SuspendLayout();
@@ -40,10 +49,9 @@
// picScreen // picScreen
// //
picScreen.BackColor = Color.Black; picScreen.BackColor = Color.Black;
picScreen.Location = new Point(13, 33); picScreen.Location = new Point(10, 26);
picScreen.Margin = new Padding(4);
picScreen.Name = "picScreen"; picScreen.Name = "picScreen";
picScreen.Size = new Size(900, 720); picScreen.Size = new Size(720, 576);
picScreen.SizeMode = PictureBoxSizeMode.Zoom; picScreen.SizeMode = PictureBoxSizeMode.Zoom;
picScreen.TabIndex = 0; picScreen.TabIndex = 0;
picScreen.TabStop = false; picScreen.TabStop = false;
@@ -51,44 +59,107 @@
// menuStrip1 // menuStrip1
// //
menuStrip1.ImageScalingSize = new Size(24, 24); 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.Location = new Point(0, 0);
menuStrip1.Name = "menuStrip1"; 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.TabIndex = 1;
menuStrip1.Text = "menuStrip1"; menuStrip1.Text = "menuStrip1";
// //
// fileToolStripMenuItem // fileToolStripMenuItem
// //
fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { openToolStripMenuItem, openSnapshotToolStripMenuItem }); fileToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { openToolStripMenuItem, exitToolStripMenuItem });
fileToolStripMenuItem.Name = "fileToolStripMenuItem"; fileToolStripMenuItem.Name = "fileToolStripMenuItem";
fileToolStripMenuItem.Size = new Size(54, 29); fileToolStripMenuItem.Size = new Size(46, 24);
fileToolStripMenuItem.Text = "File"; fileToolStripMenuItem.Text = "File";
// //
// openToolStripMenuItem // openToolStripMenuItem
// //
openToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { tAPToolStripMenuItem, sNAToolStripMenuItem });
openToolStripMenuItem.Name = "openToolStripMenuItem"; openToolStripMenuItem.Name = "openToolStripMenuItem";
openToolStripMenuItem.Size = new Size(270, 34); openToolStripMenuItem.Size = new Size(128, 26);
openToolStripMenuItem.Text = "Open TAP"; openToolStripMenuItem.Text = "Open";
openToolStripMenuItem.Click += loadTAPToolStripMenuItem_Click;
// //
// openSnapshotToolStripMenuItem // tAPToolStripMenuItem
// //
openSnapshotToolStripMenuItem.Name = "openSnapshotToolStripMenuItem"; tAPToolStripMenuItem.Name = "tAPToolStripMenuItem";
openSnapshotToolStripMenuItem.Size = new Size(270, 34); tAPToolStripMenuItem.Size = new Size(121, 26);
openSnapshotToolStripMenuItem.Text = "Open Snapshot"; tAPToolStripMenuItem.Text = "TAP";
openSnapshotToolStripMenuItem.Click += openSNAToolStripMenuItem_Click; 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 // Form1
// //
AutoScaleDimensions = new SizeF(10F, 25F); AutoScaleDimensions = new SizeF(8F, 20F);
AutoScaleMode = AutoScaleMode.Font; AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(922, 760); ClientSize = new Size(738, 608);
Controls.Add(picScreen); Controls.Add(picScreen);
Controls.Add(menuStrip1); Controls.Add(menuStrip1);
KeyPreview = true; KeyPreview = true;
MainMenuStrip = menuStrip1; MainMenuStrip = menuStrip1;
Margin = new Padding(4);
Name = "Form1"; Name = "Form1";
Text = "Parsons Sinclair ZX Spectrum 48K - 2026"; Text = "Parsons Sinclair ZX Spectrum 48K - 2026";
((System.ComponentModel.ISupportInitialize)picScreen).EndInit(); ((System.ComponentModel.ISupportInitialize)picScreen).EndInit();
@@ -104,6 +175,15 @@
private MenuStrip menuStrip1; private MenuStrip menuStrip1;
private ToolStripMenuItem fileToolStripMenuItem; private ToolStripMenuItem fileToolStripMenuItem;
private ToolStripMenuItem openToolStripMenuItem; 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;
} }
} }

View File

@@ -16,6 +16,11 @@ namespace Desktop
private IO_Bus _simpleIoBus = null!; private IO_Bus _simpleIoBus = null!;
private TapManager _tapManager = null!; private TapManager _tapManager = null!;
private int _ulaFrameCount = 0; 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) // The 16 physical colors of the ZX Spectrum (ARGB format)
private readonly int[] SpectrumColors = new int[] private readonly int[] SpectrumColors = new int[]
@@ -61,16 +66,88 @@ namespace Desktop
_cpu = new Z80(_memoryBus, _simpleIoBus, _tapManager); _cpu = new Z80(_memoryBus, _simpleIoBus, _tapManager);
// Pass 'this' so the DebuggerForm can talk back to this main window // Pass 'this' so the DebuggerForm can talk back to this main window
DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus, this); //DebuggerForm debugger = new DebuggerForm(_cpu, _memoryBus, this);
debugger.Show(); //debugger.Show();
} }
catch (Exception ex) catch (Exception ex)
{ {
MessageBox.Show($"Failed to initialize emulator:\n{ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 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) private void loadTAPToolStripMenuItem_Click(object sender, EventArgs e)
{ {
using (OpenFileDialog ofd = new OpenFileDialog()) 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 so the Debugger's background thread can call it 50 times a second
public void RenderScreen() public void RenderScreen()
{ {