From ea5a5f22ec4c0713dfe1f522c7bd22143bfbc995 Mon Sep 17 00:00:00 2001 From: Josh Dersch Date: Tue, 20 Oct 2015 15:32:26 -0700 Subject: [PATCH] Improvements to Disk Word Task. --- Contralto/CPU/CPU.cs | 9 + Contralto/CPU/Tasks/DiskTask.cs | 76 ++++-- Contralto/CPU/Tasks/EmulatorTask.cs | 12 +- Contralto/CPU/Tasks/Task.cs | 26 +- Contralto/Debugger.Designer.cs | 150 ++++++++++-- Contralto/Debugger.cs | 74 ++++-- Contralto/Debugger.resx | 6 + Contralto/Disassembly/altoIIcode3.mu | 2 +- Contralto/IO/DiskController.cs | 351 +++++++++++++++++---------- Contralto/Memory/MemoryBus.cs | 8 +- Contralto/Notes.txt | 50 +++- Contralto/Program.cs | 21 ++ 12 files changed, 581 insertions(+), 204 deletions(-) diff --git a/Contralto/CPU/CPU.cs b/Contralto/CPU/CPU.cs index c88aafc..75b6bca 100644 --- a/Contralto/CPU/CPU.cs +++ b/Contralto/CPU/CPU.cs @@ -146,6 +146,15 @@ namespace Contralto.CPU } } + /// + /// Used by the debugger to determine if a task switch is taking + /// place. + /// + public Task NextTask + { + get { return _nextTask; } + } + private void ExecuteNext() { if (_currentTask.ExecuteNext()) diff --git a/Contralto/CPU/Tasks/DiskTask.cs b/Contralto/CPU/Tasks/DiskTask.cs index 6ac9da2..7f39f8d 100644 --- a/Contralto/CPU/Tasks/DiskTask.cs +++ b/Contralto/CPU/Tasks/DiskTask.cs @@ -23,6 +23,18 @@ namespace Contralto.CPU _wakeup = false; } + public override void WakeupTask() + { + base.WakeupTask(); + } + + protected override bool ExecuteInstruction(MicroInstruction instruction) + { + bool task = base.ExecuteInstruction(instruction); + + return task; + } + protected override ushort GetBusSource(int bs) { DiskBusSource dbs = (DiskBusSource)bs; @@ -33,6 +45,7 @@ namespace Contralto.CPU return _cpu._system.DiskController.KSTAT; case DiskBusSource.ReadKDATA: + Console.WriteLine("kdata read"); return _cpu._system.DiskController.KDATA; default: @@ -40,9 +53,9 @@ namespace Contralto.CPU } } - protected override void ExecuteSpecialFunction1(int f1) + protected override void ExecuteSpecialFunction1(MicroInstruction instruction) { - DiskF1 df1 = (DiskF1)f1; + DiskF1 df1 = (DiskF1)instruction.F1; switch (df1) { @@ -87,25 +100,15 @@ namespace Contralto.CPU } } - protected override void ExecuteSpecialFunction2(int f2) + protected override void ExecuteSpecialFunction2(MicroInstruction instruction) { - DiskF2 df2 = (DiskF2)f2; + DiskF2 df2 = (DiskF2)instruction.F2; switch (df2) { - case DiskF2.INIT: - // "NEXT<-NEXT OR (if WDTASKACT AND WDINIT) then 37B else 0 - // TODO: figure out how WDTASKACT and WDINIT work. - - // From the US Patent (4148098): - // "..two multiplexers...allow the setting of the next field bits NEXT(05)-NEXT(09) - // to 1 after an error condition is detected and as soon as the word task active signal - // WDTASKACT is generated..." - - // Is this always an error condition? - Console.WriteLine("Warning: assuming 0 for Disk F2 INIT (unimplemented stub)"); - break; - + case DiskF2.INIT: + _nextModifier |= GetInitModifier(instruction); + break; case DiskF2.RWC: // "NEXT<-NEXT OR (IF current record to be written THEN 3 ELSE IF @@ -114,7 +117,9 @@ namespace Contralto.CPU // by INCREC by the microcode to present the next set of bits. int command = (_cpu._system.DiskController.KADR & 0x00c0) >> 6; - switch(command) + _nextModifier |= GetInitModifier(instruction); + + switch (command) { case 0: // read, no modification. @@ -135,8 +140,8 @@ namespace Contralto.CPU case DiskF2.XFRDAT: // "NEXT <- NEXT OR (IF current command wants data transfer THEN 1 ELSE 0) - // TODO: need to get unshifted bit 14 of the command register. Disk controller should - // save this off somewhere. + _nextModifier |= GetInitModifier(instruction); + if (_cpu._system.DiskController.DataXfer) { _nextModifier |= 0x1; @@ -144,17 +149,20 @@ namespace Contralto.CPU break; case DiskF2.RECNO: + _nextModifier |= GetInitModifier(instruction); _nextModifier |= _cpu._system.DiskController.RECNO; break; case DiskF2.NFER: // "NEXT <- NEXT OR (IF fatal error in latches THEN 0 ELSE 1)" // We assume success for now... + _nextModifier |= GetInitModifier(instruction); _nextModifier |= 0x1; break; case DiskF2.STROBON: // "NEXT <- NEXT OR (IF seek strobe still on THEN 1 ELSE 0)" + _nextModifier |= GetInitModifier(instruction); if ((_cpu._system.DiskController.KSTAT & 0x0040) == 0x0040) { _nextModifier |= 0x1; @@ -164,13 +172,39 @@ namespace Contralto.CPU case DiskF2.SWRNRDY: // "NEXT <- NEXT OR (IF disk not ready to accept command THEN 1 ELSE 0) // for now, always zero (not sure when this would be 1 yet) + _nextModifier |= GetInitModifier(instruction); break; default: throw new InvalidOperationException(String.Format("Unhandled disk special function 2 {0}", df2)); } } - } + /// + /// The status of the INIT flag + /// + /// + private ushort GetInitModifier(MicroInstruction instruction) + { + // + // "NEXT<-NEXT OR (if WDTASKACT AND WDINIT) then 37B else 0." + // + + // + // A brief discussion of the INIT signal since it isn't really covered in the Alto Hardware docs in any depth + // (and in fact is completely skipped over in the description of RWC, a rather important detail!) + // This is where the Alto ref's suggestion to have the uCode *and* the schematic on hand is really quite a + // valid recommendation. + // + // WDINIT is initially set whenever the WDINHIB bit (set via KCOM<-) is cleared (this is the WDALLOW signal). + // This signals that the microcode is "INITializing" a data transfer (so to speak). During this period, + // INIT or RWC instructions in the Disk Word task will OR in 37B to the Next field, causing the uCode to jump + // to the requisite initialization paths. WDINIT is cleared whenever a BLOCK instruction occurs during the Disk Word task, + // causing INIT to OR in 0 and RWC to or in 0, 2 or 3 (For read, check, or write respectively.) + // + + return (_taskType == TaskType.DiskWord && _cpu._system.DiskController.WDINIT) ? (ushort)0x1f : (ushort)0x0; + } + } } } diff --git a/Contralto/CPU/Tasks/EmulatorTask.cs b/Contralto/CPU/Tasks/EmulatorTask.cs index 4139ca7..65b4d09 100644 --- a/Contralto/CPU/Tasks/EmulatorTask.cs +++ b/Contralto/CPU/Tasks/EmulatorTask.cs @@ -51,9 +51,9 @@ namespace Contralto.CPU } } - protected override void ExecuteSpecialFunction1(int f1) + protected override void ExecuteSpecialFunction1(MicroInstruction instruction) { - EmulatorF1 ef1 = (EmulatorF1)f1; + EmulatorF1 ef1 = (EmulatorF1)instruction.F1; switch (ef1) { case EmulatorF1.RSNF: @@ -79,9 +79,9 @@ namespace Contralto.CPU } } - protected override void ExecuteSpecialFunction2Early(int f2) + protected override void ExecuteSpecialFunction2Early(MicroInstruction instruction) { - EmulatorF2 ef2 = (EmulatorF2)f2; + EmulatorF2 ef2 = (EmulatorF2)instruction.F2; switch (ef2) { case EmulatorF2.ACSOURCE: @@ -102,9 +102,9 @@ namespace Contralto.CPU } } - protected override void ExecuteSpecialFunction2(int f2) + protected override void ExecuteSpecialFunction2(MicroInstruction instruction) { - EmulatorF2 ef2 = (EmulatorF2)f2; + EmulatorF2 ef2 = (EmulatorF2)instruction.F2; switch (ef2) { case EmulatorF2.LoadIR: diff --git a/Contralto/CPU/Tasks/Task.cs b/Contralto/CPU/Tasks/Task.cs index d1a9a9f..5911fee 100644 --- a/Contralto/CPU/Tasks/Task.cs +++ b/Contralto/CPU/Tasks/Task.cs @@ -22,6 +22,8 @@ namespace Contralto.CPU _mpc = 0xffff; // invalid, for sanity checking _taskType = TaskType.Invalid; _cpu = cpu; + + _block = false; } public int Priority @@ -39,6 +41,15 @@ namespace Contralto.CPU get { return _mpc; } } + /// + /// Indicates whether the current uInstruction asserts BLOCK. + /// Used by hardware for various tasks. + /// + public bool BLOCK + { + get { return _block; } + } + public virtual void Reset() { // From The Alto Hardware Manual (section 2, "Initialization"): @@ -61,6 +72,7 @@ namespace Contralto.CPU { // TODO: cache microinstructions (or pre-decode them) to save consing all these up every time. MicroInstruction instruction = new MicroInstruction(UCodeMemory.UCodeROM[_mpc]); + _block = instruction.F1 == SpecialFunction1.Block; return ExecuteInstruction(instruction); } @@ -122,7 +134,7 @@ namespace Contralto.CPU _rSelect = instruction.RSELECT; // Give tasks the chance to modify parameters early on (like RSELECT) - ExecuteSpecialFunction2Early((int)instruction.F2); + ExecuteSpecialFunction2Early(instruction); // Select BUS data. if (instruction.F1 != SpecialFunction1.Constant && @@ -253,7 +265,7 @@ namespace Contralto.CPU default: // Let the specific task implementation take a crack at this. - ExecuteSpecialFunction1((int)instruction.F1); + ExecuteSpecialFunction1(instruction); break; } @@ -314,7 +326,7 @@ namespace Contralto.CPU default: // Let the specific task implementation take a crack at this. - ExecuteSpecialFunction2((int)instruction.F2); + ExecuteSpecialFunction2(instruction); break; } @@ -372,18 +384,18 @@ namespace Contralto.CPU } protected abstract ushort GetBusSource(int bs); - protected abstract void ExecuteSpecialFunction1(int f1); + protected abstract void ExecuteSpecialFunction1(MicroInstruction instruction); /// /// Used to allow Task-specific F2s that need to modify RSELECT to do so. /// /// - protected virtual void ExecuteSpecialFunction2Early(int f2) + protected virtual void ExecuteSpecialFunction2Early(MicroInstruction instruction) { // Nothing by default. } - protected abstract void ExecuteSpecialFunction2(int f2); + protected abstract void ExecuteSpecialFunction2(MicroInstruction instruction); // // Per uInstruction Task Data: @@ -405,6 +417,8 @@ namespace Contralto.CPU protected TaskType _taskType; protected bool _wakeup; + protected bool _block; + // Emulator Task-specific data. This is placed here because it is used by the ALU and it's easier to reference in the // base class even if it does break encapsulation. See notes in the EmulatorTask class for meaning. protected int _skip; diff --git a/Contralto/Debugger.Designer.cs b/Contralto/Debugger.Designer.cs index 3fb3032..3c21969 100644 --- a/Contralto/Debugger.Designer.cs +++ b/Contralto/Debugger.Designer.cs @@ -43,7 +43,11 @@ System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle13 = new System.Windows.Forms.DataGridViewCellStyle(); System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle14 = new System.Windows.Forms.DataGridViewCellStyle(); System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle15 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle16 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle17 = new System.Windows.Forms.DataGridViewCellStyle(); this.Microcode = new System.Windows.Forms.GroupBox(); + this.label2 = new System.Windows.Forms.Label(); + this.JumpToAddress = new System.Windows.Forms.TextBox(); this._sourceViewer = new System.Windows.Forms.DataGridView(); this.Breakpoint = new System.Windows.Forms.DataGridViewCheckBoxColumn(); this.T = new System.Windows.Forms.DataGridViewTextBoxColumn(); @@ -73,12 +77,16 @@ this.Data = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.label1 = new System.Windows.Forms.Label(); this.ExecutionStateLabel = new System.Windows.Forms.Label(); - this.JumpToAddress = new System.Windows.Forms.TextBox(); - this.label2 = new System.Windows.Forms.Label(); this.groupBox5 = new System.Windows.Forms.GroupBox(); this._diskData = new System.Windows.Forms.DataGridView(); this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.ResetButton = new System.Windows.Forms.Button(); + this.groupBox6 = new System.Windows.Forms.GroupBox(); + this._debugTasks = new System.Windows.Forms.DataGridView(); + this.dataGridViewTextBoxColumn3 = new System.Windows.Forms.DataGridViewCheckBoxColumn(); + this.dataGridViewTextBoxColumn4 = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.RunToNextTaskButton = new System.Windows.Forms.Button(); this.Microcode.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this._sourceViewer)).BeginInit(); this.groupBox1.SuspendLayout(); @@ -91,6 +99,8 @@ ((System.ComponentModel.ISupportInitialize)(this._memoryData)).BeginInit(); this.groupBox5.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this._diskData)).BeginInit(); + this.groupBox6.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this._debugTasks)).BeginInit(); this.SuspendLayout(); // // Microcode @@ -105,6 +115,23 @@ this.Microcode.TabStop = false; this.Microcode.Text = "Microcode Source"; // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(11, 599); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(47, 13); + this.label2.TabIndex = 13; + this.label2.Text = "Jump to:"; + // + // JumpToAddress + // + this.JumpToAddress.Location = new System.Drawing.Point(59, 596); + this.JumpToAddress.Name = "JumpToAddress"; + this.JumpToAddress.Size = new System.Drawing.Size(48, 20); + this.JumpToAddress.TabIndex = 12; + this.JumpToAddress.KeyDown += new System.Windows.Forms.KeyEventHandler(this.OnJumpAddressKeyDown); + // // _sourceViewer // this._sourceViewer.AllowUserToAddRows = false; @@ -294,7 +321,7 @@ // this.RunButton.Location = new System.Drawing.Point(103, 954); this.RunButton.Name = "RunButton"; - this.RunButton.Size = new System.Drawing.Size(53, 23); + this.RunButton.Size = new System.Drawing.Size(41, 23); this.RunButton.TabIndex = 5; this.RunButton.Text = "Run"; this.RunButton.UseVisualStyleBackColor = true; @@ -302,9 +329,9 @@ // // StopButton // - this.StopButton.Location = new System.Drawing.Point(164, 954); + this.StopButton.Location = new System.Drawing.Point(207, 954); this.StopButton.Name = "StopButton"; - this.StopButton.Size = new System.Drawing.Size(49, 23); + this.StopButton.Size = new System.Drawing.Size(43, 23); this.StopButton.TabIndex = 6; this.StopButton.Text = "Stop"; this.StopButton.UseVisualStyleBackColor = true; @@ -524,27 +551,10 @@ this.ExecutionStateLabel.TabIndex = 10; this.ExecutionStateLabel.Text = "unset"; // - // JumpToAddress - // - this.JumpToAddress.Location = new System.Drawing.Point(59, 596); - this.JumpToAddress.Name = "JumpToAddress"; - this.JumpToAddress.Size = new System.Drawing.Size(48, 20); - this.JumpToAddress.TabIndex = 12; - this.JumpToAddress.KeyDown += new System.Windows.Forms.KeyEventHandler(this.OnJumpAddressKeyDown); - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(11, 599); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(47, 13); - this.label2.TabIndex = 13; - this.label2.Text = "Jump to:"; - // // groupBox5 // this.groupBox5.Controls.Add(this._diskData); - this.groupBox5.Location = new System.Drawing.Point(3, 634); + this.groupBox5.Location = new System.Drawing.Point(150, 634); this.groupBox5.Name = "groupBox5"; this.groupBox5.Size = new System.Drawing.Size(163, 298); this.groupBox5.TabIndex = 11; @@ -602,11 +612,97 @@ this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2"; this.dataGridViewTextBoxColumn2.ReadOnly = true; // + // ResetButton + // + this.ResetButton.Location = new System.Drawing.Point(256, 955); + this.ResetButton.Name = "ResetButton"; + this.ResetButton.Size = new System.Drawing.Size(57, 23); + this.ResetButton.TabIndex = 12; + this.ResetButton.Text = "Reset"; + this.ResetButton.UseVisualStyleBackColor = true; + this.ResetButton.Click += new System.EventHandler(this.ResetButton_Click); + // + // groupBox6 + // + this.groupBox6.Controls.Add(this._debugTasks); + this.groupBox6.Location = new System.Drawing.Point(3, 634); + this.groupBox6.Name = "groupBox6"; + this.groupBox6.Size = new System.Drawing.Size(141, 298); + this.groupBox6.TabIndex = 12; + this.groupBox6.TabStop = false; + this.groupBox6.Text = "Debug Tasks"; + // + // _debugTasks + // + this._debugTasks.AllowUserToAddRows = false; + this._debugTasks.AllowUserToDeleteRows = false; + dataGridViewCellStyle16.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); + this._debugTasks.AlternatingRowsDefaultCellStyle = dataGridViewCellStyle16; + this._debugTasks.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this._debugTasks.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.dataGridViewTextBoxColumn3, + this.dataGridViewTextBoxColumn4}); + dataGridViewCellStyle17.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle17.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle17.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle17.ForeColor = System.Drawing.SystemColors.ControlText; + dataGridViewCellStyle17.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle17.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle17.WrapMode = System.Windows.Forms.DataGridViewTriState.False; + this._debugTasks.DefaultCellStyle = dataGridViewCellStyle17; + this._debugTasks.EditMode = System.Windows.Forms.DataGridViewEditMode.EditProgrammatically; + this._debugTasks.Location = new System.Drawing.Point(6, 19); + this._debugTasks.MultiSelect = false; + this._debugTasks.Name = "_debugTasks"; + this._debugTasks.ReadOnly = true; + this._debugTasks.RowHeadersVisible = false; + this._debugTasks.RowTemplate.DefaultCellStyle.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this._debugTasks.RowTemplate.Height = 18; + this._debugTasks.SelectionMode = System.Windows.Forms.DataGridViewSelectionMode.FullRowSelect; + this._debugTasks.ShowCellErrors = false; + this._debugTasks.ShowCellToolTips = false; + this._debugTasks.ShowEditingIcon = false; + this._debugTasks.ShowRowErrors = false; + this._debugTasks.Size = new System.Drawing.Size(129, 273); + this._debugTasks.TabIndex = 1; + // + // dataGridViewTextBoxColumn3 + // + this.dataGridViewTextBoxColumn3.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.AllCellsExceptHeader; + this.dataGridViewTextBoxColumn3.HeaderText = "Debug"; + this.dataGridViewTextBoxColumn3.MinimumWidth = 16; + this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3"; + this.dataGridViewTextBoxColumn3.ReadOnly = true; + this.dataGridViewTextBoxColumn3.Resizable = System.Windows.Forms.DataGridViewTriState.True; + this.dataGridViewTextBoxColumn3.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + this.dataGridViewTextBoxColumn3.Width = 16; + // + // dataGridViewTextBoxColumn4 + // + this.dataGridViewTextBoxColumn4.AutoSizeMode = System.Windows.Forms.DataGridViewAutoSizeColumnMode.Fill; + this.dataGridViewTextBoxColumn4.HeaderText = "Task"; + this.dataGridViewTextBoxColumn4.MinimumWidth = 16; + this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4"; + this.dataGridViewTextBoxColumn4.ReadOnly = true; + // + // RunToNextTaskButton + // + this.RunToNextTaskButton.Location = new System.Drawing.Point(150, 954); + this.RunToNextTaskButton.Name = "RunToNextTaskButton"; + this.RunToNextTaskButton.Size = new System.Drawing.Size(51, 23); + this.RunToNextTaskButton.TabIndex = 13; + this.RunToNextTaskButton.Text = "Run T"; + this.RunToNextTaskButton.UseVisualStyleBackColor = true; + this.RunToNextTaskButton.Click += new System.EventHandler(this.RunToNextTaskButton_Click); + // // Debugger // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(753, 997); + this.Controls.Add(this.RunToNextTaskButton); + this.Controls.Add(this.groupBox6); + this.Controls.Add(this.ResetButton); this.Controls.Add(this.groupBox5); this.Controls.Add(this.ExecutionStateLabel); this.Controls.Add(this.label1); @@ -635,6 +731,8 @@ ((System.ComponentModel.ISupportInitialize)(this._memoryData)).EndInit(); this.groupBox5.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this._diskData)).EndInit(); + this.groupBox6.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this._debugTasks)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); @@ -678,5 +776,11 @@ private System.Windows.Forms.DataGridView _diskData; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1; private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2; + private System.Windows.Forms.Button ResetButton; + private System.Windows.Forms.GroupBox groupBox6; + private System.Windows.Forms.DataGridView _debugTasks; + private System.Windows.Forms.DataGridViewCheckBoxColumn dataGridViewTextBoxColumn3; + private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn4; + private System.Windows.Forms.Button RunToNextTaskButton; } } \ No newline at end of file diff --git a/Contralto/Debugger.cs b/Contralto/Debugger.cs index 31cecc1..5d8ebc4 100644 --- a/Contralto/Debugger.cs +++ b/Contralto/Debugger.cs @@ -37,7 +37,7 @@ namespace Contralto StreamReader sr = new StreamReader(path); - while(!sr.EndOfStream) + while (!sr.EndOfStream) { string line = sr.ReadLine(); @@ -45,12 +45,12 @@ namespace Contralto int i = _sourceViewer.Rows.Add( false, // breakpoint - GetTextForTask(src.Task), - src.Address, + GetTextForTask(src.Task), + src.Address, src.Text); // Give the row a color based on the task - _sourceViewer.Rows[i].DefaultCellStyle.BackColor = GetColorForTask(src.Task); + _sourceViewer.Rows[i].DefaultCellStyle.BackColor = GetColorForTask(src.Task); // Tag the row based on the PROM address (if any) to make it easy to find. if (!String.IsNullOrEmpty(src.Address)) @@ -97,7 +97,7 @@ namespace Contralto _otherRegs.Rows[4].Cells[1].Value = OctalHelpers.ToOctal(_system.CPU.ALUC0, 1); _otherRegs.Rows[5].Cells[1].Value = OctalHelpers.ToOctal(_system.MemoryBus.MAR, 6); _otherRegs.Rows[6].Cells[1].Value = OctalHelpers.ToOctal(_system.MemoryBus.MD, 6); - _otherRegs.Rows[7].Cells[1].Value = OctalHelpers.ToOctal(_system.MemoryBus.Cycle, 2); + _otherRegs.Rows[7].Cells[1].Value = OctalHelpers.ToOctal(_system.MemoryBus.Cycle & 0x3f, 2); // Disk info _diskData.Rows[0].Cells[1].Value = _system.DiskController.ClocksUntilNextSector.ToString("0.00"); @@ -182,6 +182,11 @@ namespace Contralto _diskData.Rows.Add("KSTAT", "0"); _diskData.Rows.Add("RECNO", "0"); + for (int i=0;i<16;i++) + { + _debugTasks.Rows.Add(true, GetTextForTask((TaskType)i)); + } + } /// @@ -492,6 +497,20 @@ namespace Contralto } } + private void RunToNextTaskButton_Click(object sender, EventArgs e) + { + // + // Continuously execute until the next task switch but do not update UI + // until the "Stop" button is pressed or something bad happens. + // + //if (_execThread == null) + { + _execThread = new Thread(new System.Threading.ParameterizedThreadStart(ExecuteProc)); + _execThread.Start(ExecutionType.NextTask); + SetExecutionState(ExecutionState.Running); + } + } + private void OnStopButtonClicked(object sender, EventArgs e) { if (_execThread != null && @@ -509,6 +528,11 @@ namespace Contralto SetExecutionState(ExecutionState.Stopped); } + private void ResetButton_Click(object sender, EventArgs e) + { + _system.Reset(); + } + private void ExecuteStep() { _system.SingleStep(); @@ -522,25 +546,34 @@ namespace Contralto StepDelegate refUI = new StepDelegate(RefreshUI); StepDelegate inv = new StepDelegate(Invalidate); while (true) - { - if (execType == ExecutionType.Auto) + { + switch (execType) { - // Execute a single step, then update UI and - // sleep to give messages time to run. - _system.SingleStep(); - - this.BeginInvoke(refUI); - this.BeginInvoke(inv); - System.Threading.Thread.Sleep(10); - } - else - { - // Just execute one step, do not update UI. - _system.SingleStep(); + case ExecutionType.Auto: + { + // Execute a single step, then update UI and + // sleep to give messages time to run. + _system.SingleStep(); + + this.BeginInvoke(refUI); + this.BeginInvoke(inv); + System.Threading.Thread.Sleep(10); + } + break; + + case ExecutionType.Step: + case ExecutionType.Normal: + case ExecutionType.NextTask: + { + // Just execute one step, do not update UI. + _system.SingleStep(); + } + break; } if (_execAbort || - _breakpointEnabled[_system.CPU.CurrentTask.MPC]) + _breakpointEnabled[_system.CPU.CurrentTask.MPC] || + (execType == ExecutionType.NextTask && _system.CPU.NextTask != null && _system.CPU.NextTask != _system.CPU.CurrentTask)) { // Stop here as we've hit a breakpoint or have been stopped Update UI // to indicate where we stopped. @@ -571,6 +604,7 @@ namespace Contralto Step, Auto, Normal, + NextTask, } private enum ExecutionState diff --git a/Contralto/Debugger.resx b/Contralto/Debugger.resx index 1689f08..5a5826b 100644 --- a/Contralto/Debugger.resx +++ b/Contralto/Debugger.resx @@ -165,4 +165,10 @@ True + + True + + + True + \ No newline at end of file diff --git a/Contralto/Disassembly/altoIIcode3.mu b/Contralto/Disassembly/altoIIcode3.mu index 372847c..e2f99f7 100644 --- a/Contralto/Disassembly/altoIIcode3.mu +++ b/Contralto/Disassembly/altoIIcode3.mu @@ -1947,7 +1947,7 @@ KW01710> MAR<-DCBR+T+1, :REC0RC; ; INIT MAPS REC0RC TO KWD KW01722> REC0RC: T<-MFRRDL,BLOCK, :REC12A; FIRST RECORD READ DELAY -KE01723> REC0W: T<-MFR0BL,BLOCK, :REC12A; FIRST RECORD 0'S BLOCK LENGTH +KW01723> REC0W: T<-MFR0BL,BLOCK, :REC12A; FIRST RECORD 0'S BLOCK LENGTH KW01714> REC1: L<-10, INCRECNO; LENGTH OF RECORD 1 KW01715> T<-4, :REC12; diff --git a/Contralto/IO/DiskController.cs b/Contralto/IO/DiskController.cs index ba4a88c..87cf758 100644 --- a/Contralto/IO/DiskController.cs +++ b/Contralto/IO/DiskController.cs @@ -21,7 +21,10 @@ namespace Contralto.IO public ushort KDATA { - get { return _kData; } + get + { + return _kData; + } set { _kData = value; } } @@ -56,17 +59,43 @@ namespace Contralto.IO _wffo = (_kCom & 0x02) == 0x02; _sendAdr = (_kCom & 0x01) == 0x01; - if (!_wdInhib && !_xferOff) + Console.WriteLine( + "sst {0}, xferOff {1}, wdInhib {2}, bClkSource {3}, wffo {4}, sendAdr {5}", + _elapsedSectorStateTime, + _xferOff, + _wdInhib, + _bClkSource, + _wffo, + _sendAdr); + + // Update WDINIT state based on _wdInhib. + if (_wdInhib) { - Console.WriteLine("enabled at sst {0}", _elapsedSectorStateTime); + _wdInit = true; } } } + /// + /// Used by the DiskTask code to check the WDINIT signal for dispatch. + /// + public bool WDINIT + { + get { return _wdInit; } + } + public ushort KSTAT { - get { return _kStat; } - set { _kStat = value; } + get + { + Console.WriteLine("kstat read {0}", _kStat); + return _kStat; + } + set + { + _kStat = value; + Console.WriteLine("kstat write {0}", _kStat); + } } public ushort RECNO @@ -79,6 +108,14 @@ namespace Contralto.IO get { return _dataXfer; } } + /// + /// This is a hack to see how the microcode expects INIT to work + /// + public bool RecordInit + { + get { return _elapsedSectorStateTime < 10; } + } + public int Cylinder { get { return _cylinder; } @@ -121,6 +158,8 @@ namespace Contralto.IO _wdInhib = true; _xferOff = true; + + _wdInit = false; } public void Clock() @@ -140,11 +179,15 @@ namespace Contralto.IO _kStat = (ushort)((_kStat & 0x0fff) | (_sector << 12)); + // TODO: seclate semantics. Looks like if the sector task was BLOCKed when a new sector is signaled + // then the seclate flag is set. + // Reset internal state machine for sector data - _sectorState = SectorState.Leadin; + _sectorState = SectorState.HeaderReadDelay; _sectorWordIndex = 0; _elapsedSectorStateTime = 0.0; - Console.WriteLine("New sector ({0}), switching to LeadIn state.", _sector); + Console.WriteLine("New sector ({0}), switching to HeaderReadDelay state.", _sector); + _kData = 13; _system.CPU.WakeupTask(CPU.TaskType.DiskSector); @@ -196,127 +239,177 @@ namespace Contralto.IO // that is, the microcode expects to be woken up on a per-word basis, and it only reads in one word // per wakeup. // - // pseudocode so far: - // if (elapsed word time > word time) - // { - // elapsed word time = 0 (modulo remainder) - // _kData = next word - // if (!_wdInhib) DiskSectorTask.Wakeup(); - // } - _elapsedSectorStateTime++; - switch(_sectorState) + + + if (!_wdInhib) { - case SectorState.Leadin: - if (_elapsedSectorStateTime > _leadinDuration) - { - _elapsedSectorStateTime -= _leadinDuration; - _sectorState = SectorState.Header; - Console.WriteLine("Switching to Header state."); - } - break; + + _elapsedSectorStateTime++; - case SectorState.Header: - if (_sectorWordIndex > 1) // two words - { - _elapsedSectorStateTime -= 2.0 * _wordDuration; - _sectorState = SectorState.HeaderGap; - _sectorWordIndex = 0; - Console.WriteLine("Switching to HeaderGap state."); - } - else if (_elapsedSectorStateTime > _wordDuration) - { - _elapsedSectorStateTime -= _wordDuration; - - // Put next word into KDATA if not inhibited from doing so. - if (!_xferOff) - { - _kData = 0xdead; // placeholder - Console.WriteLine(" Header word {0} is {1}", _sectorWordIndex, OctalHelpers.ToOctal(_kData)); - } - _sectorWordIndex++; - - if (!_wdInhib) + switch (_sectorState) + { + case SectorState.HeaderReadDelay: + if (_sectorWordIndex > 19) { + _sectorState = SectorState.Header; + _sectorWordIndex = 0; + Console.WriteLine("Switching to HeaderPreamble state."); + _kData = 1; + } + else if (_elapsedSectorStateTime > _wordDuration) + { + _elapsedSectorStateTime -= _wordDuration; + + _sectorWordIndex++; + + _kData = 0xfefe; // unused, just for debugging + _system.CPU.WakeupTask(CPU.TaskType.DiskWord); + Console.WriteLine("delay wakeup"); + } - } - break; + break; - case SectorState.HeaderGap: - if (_elapsedSectorStateTime > _headerGapDuration) - { - _elapsedSectorStateTime -= _headerGapDuration; - _sectorState = SectorState.Label; - Console.WriteLine("Switching to Label state."); - } - break; - - case SectorState.Label: - if (_sectorWordIndex > 7) // eight words - { - _elapsedSectorStateTime -= 8.0 * _wordDuration; - _sectorState = SectorState.LabelGap; - _sectorWordIndex = 0; - Console.WriteLine("Switching to LabelGap state."); - } - else if(_elapsedSectorStateTime > _wordDuration) - { - _elapsedSectorStateTime -= _wordDuration; - // Put next word into KDATA if not inhibited from doing so. - if (!_xferOff) + case SectorState.HeaderPreamble: + if (_sectorWordIndex > 32) { - _kData = 0xbeef; // placeholder - Console.WriteLine(" Label word {0} is {1}", _sectorWordIndex, OctalHelpers.ToOctal(_kData)); + _sectorState = SectorState.Header; + _sectorWordIndex = 0; + Console.WriteLine("Switching to Header state."); + _kData = 2; } - _sectorWordIndex++; - - if (!_wdInhib) + else if (_elapsedSectorStateTime > _wordDuration) { + _elapsedSectorStateTime -= _wordDuration; + + _sectorWordIndex++; + + _kData = 0xfeff; // unused, just for debugging _system.CPU.WakeupTask(CPU.TaskType.DiskWord); + Console.WriteLine("preamble wakeup"); } - } - break; + break; - case SectorState.LabelGap: - if (_elapsedSectorStateTime > _labelGapDuration) - { - _elapsedSectorStateTime -= _labelGapDuration; - _sectorState = SectorState.Data; - Console.WriteLine("Switching to Data state."); - } - break; - - case SectorState.Data: - if (_sectorWordIndex > 255) // 256 words - { - _elapsedSectorStateTime -= 256.0 * _wordDuration; - _sectorState = SectorState.Leadout; - _sectorWordIndex = 0; - Console.WriteLine("Switching to Leadout state."); - } - else if (_elapsedSectorStateTime > _wordDuration) - { - _elapsedSectorStateTime -= _wordDuration; - // Put next word into KDATA if not inhibited from doing so. - if (!_xferOff) + case SectorState.Header: + if (_sectorWordIndex > 2) // two words + sync { - _kData = 0xda1a; // placeholder - Console.WriteLine(" Sector word {0} is {1}", _sectorWordIndex, OctalHelpers.ToOctal(_kData)); + //_elapsedSectorStateTime -= 2.0 * _wordDuration; + _sectorState = SectorState.HeaderInterrecord; + _sectorWordIndex = 0; + Console.WriteLine("Switching to HeaderGap state."); + _kData = 3; } - _sectorWordIndex++; - - if (!_wdInhib) + else if (_elapsedSectorStateTime > _wordDuration) { - _system.CPU.WakeupTask(CPU.TaskType.DiskWord); + _elapsedSectorStateTime -= _wordDuration; + + // Put next word into KDATA if not inhibited from doing so. + if (!_xferOff) + { + _kData = 0xdead; // placeholder + Console.WriteLine(" Header word {0} is {1}", _sectorWordIndex, OctalHelpers.ToOctal(_kData)); + } + _sectorWordIndex++; + + if (!_wdInhib) + { + Console.WriteLine("header wakeup"); + _system.CPU.WakeupTask(CPU.TaskType.DiskWord); + } } - } - break; + break; - case SectorState.Leadout: - // Just stay here forever. We will get reset at the start of the next sector. - break; + case SectorState.HeaderInterrecord: + if (_elapsedSectorStateTime > _interRecordDelay) + { + _elapsedSectorStateTime -= _interRecordDelay; + _sectorState = SectorState.Label; + Console.WriteLine("Switching to Label state."); + _kData = 4; + } + break; + case SectorState.Label: + if (_sectorWordIndex > 8) // eight words + sync + { + //_elapsedSectorStateTime -= 8.0 * _wordDuration; + _sectorState = SectorState.LabelInterrecord; + _sectorWordIndex = 0; + Console.WriteLine("Switching to LabelGap state."); + _kData = 5; + } + else if (_elapsedSectorStateTime > _wordDuration) + { + _elapsedSectorStateTime -= _wordDuration; + // Put next word into KDATA if not inhibited from doing so. + if (!_xferOff) + { + _kData = 0xbeef; // placeholder + Console.WriteLine(" Label word {0} is {1}", _sectorWordIndex, OctalHelpers.ToOctal(_kData)); + } + _sectorWordIndex++; + + if (!_wdInhib) + { + _system.CPU.WakeupTask(CPU.TaskType.DiskWord); + } + } + break; + + case SectorState.LabelInterrecord: + if (_elapsedSectorStateTime > _interRecordDelay) + { + _elapsedSectorStateTime -= _interRecordDelay; + _sectorState = SectorState.Data; + Console.WriteLine("Switching to Data state."); + _kData = 6; + } + break; + + case SectorState.Data: + if (_sectorWordIndex > 256) // 256 words + sync + { + //_elapsedSectorStateTime -= 256.0 * _wordDuration; + _sectorState = SectorState.Postamble; + _sectorWordIndex = 0; + Console.WriteLine("Switching to Leadout state."); + _kData = 7; + } + else if (_elapsedSectorStateTime > _wordDuration) + { + _elapsedSectorStateTime -= _wordDuration; + // Put next word into KDATA if not inhibited from doing so. + if (!_xferOff) + { + _kData = 0xda1a; // placeholder + Console.WriteLine(" Sector word {0} is {1}", _sectorWordIndex, OctalHelpers.ToOctal(_kData)); + } + _sectorWordIndex++; + + if (!_wdInhib) + { + _system.CPU.WakeupTask(CPU.TaskType.DiskWord); + } + } + break; + + case SectorState.Postamble: + // Just stay here forever. We will get reset at the start of the next sector. + break; + + } } + + // + // Update the WDINIT signal; this is based on WDALLOW (!_wdInhib) which sets WDINIT (this is done + // in KCOM way above). + // WDINIT is reset when BLOCK (a BLOCK F1 is being executed) and WDTSKACT (the disk word task is running) are 1. + // + if (_system.CPU.CurrentTask.Priority == (int)CPU.TaskType.DiskWord && + _system.CPU.CurrentTask.BLOCK) + { + _wdInit = false; + } } public void ClearStatus() @@ -422,6 +515,9 @@ namespace Contralto.IO private int _head; private int _sector; + // WDINIT signal + private bool _wdInit; + // Sector timing. Based on table on pg. 43 of the Alto Hardware Manual private double _elapsedSectorTime; // elapsed time in this sector (in clocks) private const double _sectorDuration = (40.0 / 12.0); // time in msec for one sector @@ -430,24 +526,35 @@ namespace Contralto.IO // Sector data timing and associated state. Timings based on educated guesses at the moment. private enum SectorState { - Leadin = 0, // gap between sector mark and first Header word - Header, // Header; two words - HeaderGap, // gap between end of Header and first Label word - Label, // Label; 8 words - LabelGap, // gap betweeen the end of Label and first Data word - Data, // Data; 256 words - Leadout // gap between the end of Data and the next sector mark + HeaderReadDelay = 0, // gap between sector mark and first Header word + HeaderPreamble, + Header, // Header; two words + HeaderInterrecord,// gap between end of Header and first Label word + Label, // Label; 8 words + LabelInterrecord, // gap betweeen the end of Label and first Data word + Data, // Data; 256 words + Postamble // gap between the end of Data and the next sector mark } private SectorState _sectorState; private double _elapsedSectorStateTime; private int _sectorWordIndex; - private const double _wordDuration = (_sectorClocks / (266.0 + 94.0)); // Based on : 266 words / sector, + 94 "words" for gaps (made up) - private const double _leadinDuration = (_wordDuration * 70.0); - private const double _headerGapDuration = (_wordDuration * 8.0); - private const double _labelGapDuration = (_wordDuration * 8.0); - private const double _leadoutDuration = (_wordDuration * 8.0); - + + // From altoconsts23.mu: + // $MFRRDL $177757; DISK HEADER READ DELAY IS 21 WORDS + // $MFR0BL $177744; DISK HEADER PREAMBLE IS 34 WORDS + // $MIRRDL $177774; DISK INTERRECORD READ DELAY IS 4 WORDS + // $MIR0BL $177775; DISK INTERRECORD PREAMBLE IS 3 WORDS + // $MRPAL $177775; DISK READ POSTAMBLE LENGTH IS 3 WORDS + // $MWPAL $177773; DISK WRITE POSTAMBLE LENGTH IS 5 WORDS + private const double _wordDuration = (_sectorClocks / (266.0 + 21 + 34 + (4 + 3) * 3)); // Based on : 266 words / sector, + X words for delay / preamble + private const double _headerReadDelay = (_wordDuration * 21); + private const double _headerPreamble = (_wordDuration * 34); + private const double _interRecordDelay = (_wordDuration * 4); + private const double _interRecordPreamble = (_wordDuration * 3); + + + // Cylinder seek timing. Again, see the manual. // Timing varies based on how many cylinders are being traveled during a seek; see diff --git a/Contralto/Memory/MemoryBus.cs b/Contralto/Memory/MemoryBus.cs index 05207a7..3a11145 100644 --- a/Contralto/Memory/MemoryBus.cs +++ b/Contralto/Memory/MemoryBus.cs @@ -242,8 +242,8 @@ namespace Contralto.Memory } else { - throw new NotImplementedException(String.Format("Read from unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address))); - //Console.WriteLine("Read from unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address)); + //throw new NotImplementedException(String.Format("Read from unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address))); + Console.WriteLine("Read from unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address)); return 0; } } @@ -264,8 +264,8 @@ namespace Contralto.Memory } else { - throw new NotImplementedException(String.Format("Write to unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address))); - //Console.WriteLine("Write to unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address)); + // throw new NotImplementedException(String.Format("Write to unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address))); + Console.WriteLine("Write to unimplemented memory-mapped I/O device at {0}.", OctalHelpers.ToOctal(address)); } } diff --git a/Contralto/Notes.txt b/Contralto/Notes.txt index 4ca0f46..67ca201 100644 --- a/Contralto/Notes.txt +++ b/Contralto/Notes.txt @@ -1,5 +1,53 @@ -Current issue: + +9/15/15: +Current issue: DCB @ 521 is set to 1 by emulator uCode; then clobbered once sector task runs (set to zero) so the disk controller never picks up a control block and never does anything. +9/16: +"solved" previous issue by issuing a Wake for the KS task on init; this causes the sector task to run first, the Emulator uCode then sets things up correctly. +Not sure if this is how it's supposed to work. + +New issue: Emulator uCode sets up a DCB starting at 1 that looks like: + +DCB: 1 +DCB+1: 1 +DCB+2: 44000 +DCB+3: 402
+DCB+4: 402