diff --git a/Contralto/AboutBox.Designer.cs b/Contralto/AboutBox.Designer.cs index 07e2de6..f2c2b2f 100644 --- a/Contralto/AboutBox.Designer.cs +++ b/Contralto/AboutBox.Designer.cs @@ -33,12 +33,14 @@ this.label2 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.OkButton = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.linkLabel1 = new System.Windows.Forms.LinkLabel(); this.SuspendLayout(); // // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(53, 18); + this.label1.Location = new System.Drawing.Point(88, 19); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(74, 13); this.label1.TabIndex = 0; @@ -47,16 +49,17 @@ // label2 // this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(12, 66); + this.label2.Location = new System.Drawing.Point(45, 68); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(168, 13); this.label2.TabIndex = 1; this.label2.Text = "(c) 2015 Living Computer Museum"; + this.label2.Click += new System.EventHandler(this.label2_Click); // // label3 // this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(39, 41); + this.label3.Location = new System.Drawing.Point(68, 43); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(109, 13); this.label3.TabIndex = 2; @@ -64,7 +67,7 @@ // // OkButton // - this.OkButton.Location = new System.Drawing.Point(52, 96); + this.OkButton.Location = new System.Drawing.Point(87, 161); this.OkButton.Name = "OkButton"; this.OkButton.Size = new System.Drawing.Size(75, 23); this.OkButton.TabIndex = 3; @@ -72,11 +75,32 @@ this.OkButton.UseVisualStyleBackColor = true; this.OkButton.Click += new System.EventHandler(this.OkButton_Click); // + // label4 + // + this.label4.Location = new System.Drawing.Point(23, 94); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(231, 18); + this.label4.TabIndex = 4; + this.label4.Text = "Bug reports, comments and miscellanea to"; + // + // linkLabel1 + // + this.linkLabel1.AutoSize = true; + this.linkLabel1.Location = new System.Drawing.Point(45, 123); + this.linkLabel1.Name = "linkLabel1"; + this.linkLabel1.Size = new System.Drawing.Size(168, 13); + this.linkLabel1.TabIndex = 5; + this.linkLabel1.TabStop = true; + this.linkLabel1.Text = "joshd@livingcomputermuseum.org"; + this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked); + // // AboutBox // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(191, 131); + this.ClientSize = new System.Drawing.Size(256, 196); + this.Controls.Add(this.linkLabel1); + this.Controls.Add(this.label4); this.Controls.Add(this.OkButton); this.Controls.Add(this.label3); this.Controls.Add(this.label2); @@ -86,6 +110,7 @@ this.MinimizeBox = false; this.Name = "AboutBox"; this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "About ContrAlto"; this.ResumeLayout(false); this.PerformLayout(); @@ -98,5 +123,7 @@ private System.Windows.Forms.Label label2; private System.Windows.Forms.Label label3; private System.Windows.Forms.Button OkButton; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.LinkLabel linkLabel1; } } \ No newline at end of file diff --git a/Contralto/AboutBox.cs b/Contralto/AboutBox.cs index 05b602a..93a4c91 100644 --- a/Contralto/AboutBox.cs +++ b/Contralto/AboutBox.cs @@ -21,5 +21,15 @@ namespace Contralto { this.Close(); } + + private void label2_Click(object sender, EventArgs e) + { + + } + + private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) + { + System.Diagnostics.Process.Start("mailto:joshd@livingcomputermuseum.org"); + } } } diff --git a/Contralto/AltoSystem.cs b/Contralto/AltoSystem.cs index 3d59e48..fb0222f 100644 --- a/Contralto/AltoSystem.cs +++ b/Contralto/AltoSystem.cs @@ -19,15 +19,16 @@ namespace Contralto public AltoSystem() { _scheduler = new Scheduler(); - - _cpu = new AltoCPU(this); + _memBus = new MemoryBus(); _mem = new Memory.Memory(); _keyboard = new Keyboard(); _diskController = new DiskController(this); _displayController = new DisplayController(this); _mouse = new Mouse(); - //_fakeDisplayController = new FakeDisplayController(this); + _ethernetController = new EthernetController(this); + + _cpu = new AltoCPU(this); // Attach memory-mapped devices to the bus _memBus.AddDevice(_mem); @@ -37,8 +38,7 @@ namespace Contralto // Register devices that need clocks _clockableDevices = new List(); _clockableDevices.Add(_memBus); - _clockableDevices.Add(_displayController); - //_clockableDevices.Add(_fakeDisplayController); + _clockableDevices.Add(_displayController); _clockableDevices.Add(_cpu); Reset(); @@ -53,8 +53,7 @@ namespace Contralto public void Reset() { _scheduler.Reset(); - - _cpu.Reset(); + _memBus.Reset(); _mem.Reset(); ALU.Reset(); @@ -62,7 +61,11 @@ namespace Contralto _diskController.Reset(); _displayController.Reset(); _keyboard.Reset(); - _mouse.Reset(); + _mouse.Reset(); + _cpu.Reset(); + _ethernetController.Reset(); + + UCodeMemory.Reset(); } /// @@ -98,7 +101,7 @@ namespace Contralto using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read)) { - newPack.Load(fs, false); + newPack.Load(fs, path, false); fs.Close(); } @@ -145,6 +148,11 @@ namespace Contralto get { return _mouse; } } + public EthernetController EthernetController + { + get { return _ethernetController; } + } + public Scheduler Scheduler { get { return _scheduler; } @@ -162,7 +170,7 @@ namespace Contralto private void T_Elapsed(object sender, ElapsedEventArgs e) { - System.Console.WriteLine("{0} CPU clocks/sec %{1}. {2} fields/sec", _clocks, ((double)_clocks / 5882353.0) * 100.0, _displayController.Fields); + //System.Console.WriteLine("{0} CPU clocks/sec %{1}. {2} fields/sec", _clocks, ((double)_clocks / 5882353.0) * 100.0, _displayController.Fields); _clocks = 0; _displayController.Fields = 0; } @@ -174,7 +182,7 @@ namespace Contralto private Mouse _mouse; private DiskController _diskController; private DisplayController _displayController; - // private FakeDisplayController _fakeDisplayController; + private EthernetController _ethernetController; private Scheduler _scheduler; private ulong _clocks; diff --git a/Contralto/AltoWindow.Designer.cs b/Contralto/AltoWindow.Designer.cs index 38d0bf6..52688db 100644 --- a/Contralto/AltoWindow.Designer.cs +++ b/Contralto/AltoWindow.Designer.cs @@ -47,7 +47,11 @@ this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.StatusLine = new System.Windows.Forms.StatusStrip(); - this.StatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.CaptureStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.Drive0ImageName = new System.Windows.Forms.ToolStripMenuItem(); + this.Drive1ImageName = new System.Windows.Forms.ToolStripMenuItem(); + this.SystemStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.DiskStatusLabel = new System.Windows.Forms.ToolStripStatusLabel(); ((System.ComponentModel.ISupportInitialize)(this.DisplayBox)).BeginInit(); this.menuStrip1.SuspendLayout(); this.StatusLine.SuspendLayout(); @@ -88,7 +92,7 @@ // exitToolStripMenuItem // this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; - this.exitToolStripMenuItem.Size = new System.Drawing.Size(92, 22); + this.exitToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.exitToolStripMenuItem.Text = "Exit"; this.exitToolStripMenuItem.Click += new System.EventHandler(this.OnFileExitClick); // @@ -108,7 +112,9 @@ // SystemStartMenuItem // this.SystemStartMenuItem.Name = "SystemStartMenuItem"; - this.SystemStartMenuItem.Size = new System.Drawing.Size(152, 22); + this.SystemStartMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Alt) + | System.Windows.Forms.Keys.S))); + this.SystemStartMenuItem.Size = new System.Drawing.Size(210, 22); this.SystemStartMenuItem.Text = "Start"; this.SystemStartMenuItem.Click += new System.EventHandler(this.OnSystemStartMenuClick); // @@ -116,7 +122,9 @@ // this.SystemResetMenuItem.Enabled = false; this.SystemResetMenuItem.Name = "SystemResetMenuItem"; - this.SystemResetMenuItem.Size = new System.Drawing.Size(152, 22); + this.SystemResetMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Alt) + | System.Windows.Forms.Keys.R))); + this.SystemResetMenuItem.Size = new System.Drawing.Size(210, 22); this.SystemResetMenuItem.Text = "Reset"; this.SystemResetMenuItem.Click += new System.EventHandler(this.OnSystemResetMenuClick); // @@ -124,22 +132,25 @@ // this.drive0ToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.loadToolStripMenuItem1, - this.unloadToolStripMenuItem1}); + this.unloadToolStripMenuItem1, + this.Drive0ImageName}); this.drive0ToolStripMenuItem.Name = "drive0ToolStripMenuItem"; - this.drive0ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.drive0ToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.drive0ToolStripMenuItem.Text = "Drive 0"; // // loadToolStripMenuItem1 // this.loadToolStripMenuItem1.Name = "loadToolStripMenuItem1"; - this.loadToolStripMenuItem1.Size = new System.Drawing.Size(119, 22); + this.loadToolStripMenuItem1.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Alt) + | System.Windows.Forms.Keys.L))); + this.loadToolStripMenuItem1.Size = new System.Drawing.Size(167, 22); this.loadToolStripMenuItem1.Text = "Load..."; this.loadToolStripMenuItem1.Click += new System.EventHandler(this.OnSystemDrive0LoadClick); // // unloadToolStripMenuItem1 // this.unloadToolStripMenuItem1.Name = "unloadToolStripMenuItem1"; - this.unloadToolStripMenuItem1.Size = new System.Drawing.Size(119, 22); + this.unloadToolStripMenuItem1.Size = new System.Drawing.Size(167, 22); this.unloadToolStripMenuItem1.Text = "Unload..."; this.unloadToolStripMenuItem1.Click += new System.EventHandler(this.OnSystemDrive0UnloadClick); // @@ -147,35 +158,39 @@ // this.drive1ToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.loadToolStripMenuItem, - this.unloadToolStripMenuItem}); + this.unloadToolStripMenuItem, + this.Drive1ImageName}); this.drive1ToolStripMenuItem.Name = "drive1ToolStripMenuItem"; - this.drive1ToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.drive1ToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.drive1ToolStripMenuItem.Text = "Drive 1"; // // loadToolStripMenuItem // this.loadToolStripMenuItem.Name = "loadToolStripMenuItem"; - this.loadToolStripMenuItem.Size = new System.Drawing.Size(119, 22); + this.loadToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.loadToolStripMenuItem.Text = "Load..."; this.loadToolStripMenuItem.Click += new System.EventHandler(this.OnSystemDrive1LoadClick); // // unloadToolStripMenuItem // this.unloadToolStripMenuItem.Name = "unloadToolStripMenuItem"; - this.unloadToolStripMenuItem.Size = new System.Drawing.Size(119, 22); + this.unloadToolStripMenuItem.Size = new System.Drawing.Size(152, 22); this.unloadToolStripMenuItem.Text = "Unload..."; this.unloadToolStripMenuItem.Click += new System.EventHandler(this.OnSystemDrive1UnloadClick); // // optionsToolStripMenuItem // + this.optionsToolStripMenuItem.Enabled = false; this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem"; - this.optionsToolStripMenuItem.Size = new System.Drawing.Size(152, 22); + this.optionsToolStripMenuItem.Size = new System.Drawing.Size(210, 22); this.optionsToolStripMenuItem.Text = "Options..."; // // SystemShowDebuggerMenuItem // this.SystemShowDebuggerMenuItem.Name = "SystemShowDebuggerMenuItem"; - this.SystemShowDebuggerMenuItem.Size = new System.Drawing.Size(152, 22); + this.SystemShowDebuggerMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Alt) + | System.Windows.Forms.Keys.D))); + this.SystemShowDebuggerMenuItem.Size = new System.Drawing.Size(210, 22); this.SystemShowDebuggerMenuItem.Text = "Show Debugger"; this.SystemShowDebuggerMenuItem.Click += new System.EventHandler(this.OnDebuggerShowClick); // @@ -197,7 +212,9 @@ // StatusLine // this.StatusLine.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.StatusLabel}); + this.CaptureStatusLabel, + this.SystemStatusLabel, + this.DiskStatusLabel}); this.StatusLine.Location = new System.Drawing.Point(0, 837); this.StatusLine.Name = "StatusLine"; this.StatusLine.Size = new System.Drawing.Size(608, 22); @@ -205,11 +222,37 @@ this.StatusLine.Text = "statusStrip1"; this.StatusLine.KeyDown += new System.Windows.Forms.KeyEventHandler(this.OnKeyDown); // - // StatusLabel + // CaptureStatusLabel // - this.StatusLabel.Name = "StatusLabel"; - this.StatusLabel.Size = new System.Drawing.Size(63, 17); - this.StatusLabel.Text = "StatusLabel"; + this.CaptureStatusLabel.Name = "CaptureStatusLabel"; + this.CaptureStatusLabel.Size = new System.Drawing.Size(102, 17); + this.CaptureStatusLabel.Text = "CaptureStatusLabel"; + // + // Drive0ImageName + // + this.Drive0ImageName.Enabled = false; + this.Drive0ImageName.Name = "Drive0ImageName"; + this.Drive0ImageName.Size = new System.Drawing.Size(167, 22); + this.Drive0ImageName.Text = "Image Name"; + // + // Drive1ImageName + // + this.Drive1ImageName.Enabled = false; + this.Drive1ImageName.Name = "Drive1ImageName"; + this.Drive1ImageName.Size = new System.Drawing.Size(152, 22); + this.Drive1ImageName.Text = "Image Name"; + // + // SystemStatusLabel + // + this.SystemStatusLabel.Name = "SystemStatusLabel"; + this.SystemStatusLabel.Size = new System.Drawing.Size(98, 17); + this.SystemStatusLabel.Text = "SystemStatusLabel"; + // + // DiskStatusLabel + // + this.DiskStatusLabel.Name = "DiskStatusLabel"; + this.DiskStatusLabel.Size = new System.Drawing.Size(82, 17); + this.DiskStatusLabel.Text = "DiskStatusLabel"; // // AltoWindow // @@ -260,7 +303,11 @@ private System.Windows.Forms.ToolStripMenuItem unloadToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem; private System.Windows.Forms.StatusStrip StatusLine; - private System.Windows.Forms.ToolStripStatusLabel StatusLabel; + private System.Windows.Forms.ToolStripStatusLabel CaptureStatusLabel; private System.Windows.Forms.ToolStripMenuItem SystemShowDebuggerMenuItem; + private System.Windows.Forms.ToolStripMenuItem Drive0ImageName; + private System.Windows.Forms.ToolStripMenuItem Drive1ImageName; + private System.Windows.Forms.ToolStripStatusLabel SystemStatusLabel; + private System.Windows.Forms.ToolStripStatusLabel DiskStatusLabel; } } \ No newline at end of file diff --git a/Contralto/CPU/CPU.cs b/Contralto/CPU/CPU.cs index ca9985d..fd763fc 100644 --- a/Contralto/CPU/CPU.cs +++ b/Contralto/CPU/CPU.cs @@ -31,6 +31,7 @@ namespace Contralto.CPU _tasks[(int)TaskType.DisplayVertical] = new DisplayVerticalTask(this); _tasks[(int)TaskType.Cursor] = new CursorTask(this); _tasks[(int)TaskType.MemoryRefresh] = new MemoryRefreshTask(this); + _tasks[(int)TaskType.Ethernet] = new EthernetTask(this); Reset(); } @@ -98,6 +99,7 @@ namespace Contralto.CPU _ir = 0; _aluC0 = 0; _rb = 0; + _rmr = 0xffff; // Start all tasks in ROM0 // Reset tasks. for (int i=0;i<_tasks.Length;i++) @@ -113,8 +115,7 @@ namespace Contralto.CPU _currentTask = _nextTask; _nextTask = null; - - } + } public void Clock() { @@ -135,6 +136,35 @@ namespace Contralto.CPU } } + /// + /// "Silent Boot": (See Section 9.2.2) + /// Used when a BOOT STARTF is invoked; resets task MPCs and sets + /// the starting bank (RAM0 or ROM0) appropriately based on the contents + /// of RMR. + /// All other register contents are left as-is. + /// + public void SoftReset() + { + // Reset tasks. + for (int i = 0; i < _tasks.Length; i++) + { + if (_tasks[i] != null) + { + _tasks[i].Reset(); + } + } + + UCodeMemory.LoadBanksFromRMR(_rmr); + + // Execute the initial task switch. + TaskSwitch(); + + _currentTask = _nextTask; + _nextTask = null; + + Logging.Log.Write(Logging.LogComponent.CPU, "Silent Boot; microcode banks initialized to {0}", Conversion.ToOctal(_rmr)); + } + /// /// Used by hardware devices to cause a specific task to have its /// "wakeup" signal triggered @@ -196,19 +226,22 @@ namespace Contralto.CPU } // AltoCPU registers - ushort _t; - ushort _l; - ushort _m; - ushort _ir; + private ushort _t; + private ushort _l; + private ushort _m; + private ushort _ir; // R and S register files and bank select - ushort[] _r; - ushort[][] _s; - ushort _rb; // S register bank select + private ushort[] _r; + private ushort[][] _s; + private ushort _rb; // S register bank select // Stores the last carry from the ALU on a Load L private ushort _aluC0; + // RMR (Reset Mode Register) + ushort _rmr; + // Task data private Task _nextTask; // The task to switch two after the next microinstruction private Task _currentTask; // The currently executing task diff --git a/Contralto/CPU/MicroInstruction.cs b/Contralto/CPU/MicroInstruction.cs index 1a3fff7..8365d7b 100644 --- a/Contralto/CPU/MicroInstruction.cs +++ b/Contralto/CPU/MicroInstruction.cs @@ -154,6 +154,29 @@ namespace Contralto.CPU LoadCSR = 9, } + enum EthernetBusSource + { + EIDFCT = 4, + } + + enum EthernetF1 + { + EILFCT = 11, + EPFCT = 12, + EWFCT = 13, + } + + enum EthernetF2 + { + EODFCT = 8, + EOSFCT = 9, + ERBFCT = 10, + EEFCT = 11, + EBFCT = 12, + ECBFCT = 13, + EISFCT = 14, + } + public class MicroInstruction { public MicroInstruction(UInt32 code) diff --git a/Contralto/CPU/Tasks/CursorTask.cs b/Contralto/CPU/Tasks/CursorTask.cs index edee40c..d3748a1 100644 --- a/Contralto/CPU/Tasks/CursorTask.cs +++ b/Contralto/CPU/Tasks/CursorTask.cs @@ -5,7 +5,7 @@ namespace Contralto.CPU public partial class AltoCPU { /// - /// DisplayWordTask provides functionality for the DWT task + /// CursorTask provides functionality for the Cursor-specific task functions /// private sealed class CursorTask : Task { @@ -34,7 +34,7 @@ namespace Contralto.CPU break; case CursorF2.LoadCSR: - // TODO: load cursor shift register from bus + // Load cursor shift register from bus _cpu._system.DisplayController.LoadCSR(_busData); break; diff --git a/Contralto/CPU/Tasks/DiskTask.cs b/Contralto/CPU/Tasks/DiskTask.cs index de333cf..01073d3 100644 --- a/Contralto/CPU/Tasks/DiskTask.cs +++ b/Contralto/CPU/Tasks/DiskTask.cs @@ -1,4 +1,5 @@ -using System; +using Contralto.IO; +using System; namespace Contralto.CPU { @@ -15,11 +16,8 @@ namespace Contralto.CPU { _taskType = diskSectorTask ? TaskType.DiskSector : TaskType.DiskWord; _wakeup = false; - } - public override void WakeupTask() - { - base.WakeupTask(); + _diskController = _cpu._system.DiskController; } protected override bool ExecuteInstruction(MicroInstruction instruction) @@ -33,8 +31,7 @@ namespace Contralto.CPU if (_taskType == TaskType.DiskSector) { // Sector task is running; clear enable for seclate signal - _cpu._system.DiskController.DisableSeclate(); - + _diskController.DisableSeclate(); } /* @@ -53,10 +50,10 @@ namespace Contralto.CPU switch (dbs) { case DiskBusSource.ReadKSTAT: - return _cpu._system.DiskController.KSTAT; + return _diskController.KSTAT; case DiskBusSource.ReadKDATA: - return _cpu._system.DiskController.KDATA; + return _diskController.KDATA; default: throw new InvalidOperationException(String.Format("Unhandled bus source {0}", bs)); @@ -71,26 +68,26 @@ namespace Contralto.CPU { case DiskF1.LoadKDATA: // "The KDATA register is loaded from BUS[0-15]." - _cpu._system.DiskController.KDATA = _busData; + _diskController.KDATA = _busData; break; case DiskF1.LoadKADR: // "This causes the KADR register to be loaded from BUS[8-14]. // in addition, it causes the head address bit to be loaded from KDATA[13]." // (the latter is done by DiskController) - _cpu._system.DiskController.KADR = (ushort)((_busData & 0xff)); + _diskController.KADR = (ushort)((_busData & 0xff)); break; case DiskF1.LoadKCOMM: - _cpu._system.DiskController.KCOM = (ushort)((_busData & 0x7c00) >> 10); + _diskController.KCOM = (ushort)((_busData & 0x7c00) >> 10); break; case DiskF1.CLRSTAT: - _cpu._system.DiskController.ClearStatus(); + _diskController.ClearStatus(); break; case DiskF1.INCRECNO: - _cpu._system.DiskController.IncrementRecord(); + _diskController.IncrementRecord(); break; case DiskF1.LoadKSTAT: @@ -104,11 +101,11 @@ namespace Contralto.CPU int modifiedBusData = (_busData & 0xb) | ((~_busData) & 0x4); // OR in BUS[12-15] after masking in KSTAT[13] so it is ORed in properly. - _cpu._system.DiskController.KSTAT = (ushort)(((_cpu._system.DiskController.KSTAT & 0xfff4)) | modifiedBusData); + _diskController.KSTAT = (ushort)(((_diskController.KSTAT & 0xfff4)) | modifiedBusData); break; case DiskF1.STROBE: - _cpu._system.DiskController.Strobe(); + _diskController.Strobe(); break; default: @@ -131,7 +128,7 @@ namespace Contralto.CPU // current record to be checked THEN 2 ELSE 0.") // Current record is in bits 8-9 of the command register; this is shifted // by INCREC by the microcode to present the next set of bits. - int command = (_cpu._system.DiskController.KADR & 0x00c0) >> 6; + int command = (_diskController.KADR & 0x00c0) >> 6; _nextModifier |= GetInitModifier(instruction); @@ -158,7 +155,7 @@ namespace Contralto.CPU // "NEXT <- NEXT OR (IF current command wants data transfer THEN 1 ELSE 0) _nextModifier |= GetInitModifier(instruction); - if (_cpu._system.DiskController.DataXfer) + if (_diskController.DataXfer) { _nextModifier |= 0x1; } @@ -166,7 +163,7 @@ namespace Contralto.CPU case DiskF2.RECNO: _nextModifier |= GetInitModifier(instruction); - _nextModifier |= _cpu._system.DiskController.RECNO; + _nextModifier |= _diskController.RECNO; break; case DiskF2.NFER: @@ -179,7 +176,7 @@ namespace Contralto.CPU 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) + if ((_diskController.KSTAT & 0x0040) == 0x0040) { _nextModifier |= 0x1; } @@ -189,7 +186,7 @@ namespace Contralto.CPU // "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); - if (!_cpu._system.DiskController.Ready) + if (!_diskController.Ready) { _nextModifier |= 1; } @@ -209,7 +206,7 @@ namespace Contralto.CPU // if (_taskType == TaskType.DiskWord) { - _cpu._system.DiskController.WDINIT = false; + _diskController.WDINIT = false; } } @@ -236,8 +233,10 @@ namespace Contralto.CPU // 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; - } + return (_taskType == TaskType.DiskWord && _diskController.WDINIT) ? (ushort)0x1f : (ushort)0x0; + } + + private DiskController _diskController; } } } diff --git a/Contralto/CPU/Tasks/DisplayHorizontalTask.cs b/Contralto/CPU/Tasks/DisplayHorizontalTask.cs index eb8a6c5..790f9ac 100644 --- a/Contralto/CPU/Tasks/DisplayHorizontalTask.cs +++ b/Contralto/CPU/Tasks/DisplayHorizontalTask.cs @@ -1,4 +1,5 @@ -using System; +using Contralto.Display; +using System; namespace Contralto.CPU { @@ -13,6 +14,8 @@ namespace Contralto.CPU { _taskType = TaskType.DisplayHorizontal; _wakeup = false; + + _displayController = _cpu._system.DisplayController; } protected override bool ExecuteInstruction(MicroInstruction instruction) @@ -29,11 +32,11 @@ namespace Contralto.CPU switch (dh2) { case DisplayHorizontalF2.EVENFIELD: - _nextModifier |= (ushort)(_cpu._system.DisplayController.EVENFIELD ? 1 : 0); + _nextModifier |= (ushort)(_displayController.EVENFIELD ? 1 : 0); break; case DisplayHorizontalF2.SETMODE: - _cpu._system.DisplayController.SETMODE(_busData); + _displayController.SETMODE(_busData); // "If bit 0 = 1, the bit clock rate is set to 100ns period (at the start of the next scan line), // and a 1 is merged into NEXT[9]." @@ -50,8 +53,10 @@ namespace Contralto.CPU protected override void ExecuteBlock() { - _cpu._system.DisplayController.DHTBLOCK = true; + _displayController.DHTBLOCK = true; } + + private DisplayController _displayController; } } } diff --git a/Contralto/CPU/Tasks/DisplayVerticalTask.cs b/Contralto/CPU/Tasks/DisplayVerticalTask.cs index f0a5c95..b4d73f4 100644 --- a/Contralto/CPU/Tasks/DisplayVerticalTask.cs +++ b/Contralto/CPU/Tasks/DisplayVerticalTask.cs @@ -1,4 +1,5 @@ -using System; +using Contralto.Display; +using System; namespace Contralto.CPU { @@ -13,6 +14,8 @@ namespace Contralto.CPU { _taskType = TaskType.DisplayVertical; _wakeup = false; + + _displayController = _cpu._system.DisplayController; } protected override bool ExecuteInstruction(MicroInstruction instruction) @@ -29,13 +32,15 @@ namespace Contralto.CPU switch (dv2) { case DisplayVerticalF2.EVENFIELD: - _nextModifier |= (ushort)(_cpu._system.DisplayController.EVENFIELD ? 1 : 0); + _nextModifier |= (ushort)(_displayController.EVENFIELD ? 1 : 0); break; default: throw new InvalidOperationException(String.Format("Unhandled display vertical F2 {0}.", dv2)); } } + + private DisplayController _displayController; } } } diff --git a/Contralto/CPU/Tasks/DisplayWordTask.cs b/Contralto/CPU/Tasks/DisplayWordTask.cs index 46b0464..89b7163 100644 --- a/Contralto/CPU/Tasks/DisplayWordTask.cs +++ b/Contralto/CPU/Tasks/DisplayWordTask.cs @@ -1,4 +1,5 @@ -using System; +using Contralto.Display; +using System; namespace Contralto.CPU @@ -13,16 +14,18 @@ namespace Contralto.CPU public DisplayWordTask(AltoCPU cpu) : base(cpu) { _taskType = TaskType.DisplayWord; - _wakeup = false; + _wakeup = false; + + _displayController = _cpu._system.DisplayController; } protected override bool ExecuteInstruction(MicroInstruction instruction) { // We remove our wakeup only if there isn't a wakeup being generated for us by the // display controller. - _wakeup = (!_cpu._system.DisplayController.FIFOFULL && - !_cpu._system.DisplayController.DHTBLOCK && - !_cpu._system.DisplayController.DWTBLOCK); + _wakeup = (!_displayController.FIFOFULL && + !_displayController.DHTBLOCK && + !_displayController.DWTBLOCK); return base.ExecuteInstruction(instruction); } @@ -33,7 +36,7 @@ namespace Contralto.CPU switch (dw2) { case DisplayWordF2.LoadDDR: - _cpu._system.DisplayController.LoadDDR(_busData); + _displayController.LoadDDR(_busData); break; default: @@ -43,16 +46,18 @@ namespace Contralto.CPU protected override void ExecuteBlock() { - _cpu._system.DisplayController.DWTBLOCK = true; + _displayController.DWTBLOCK = true; // // Wake up DHT if it has not blocked itself. // - if (!_cpu._system.DisplayController.DHTBLOCK) + if (!_displayController.DHTBLOCK) { _cpu.WakeupTask(TaskType.DisplayHorizontal); } } + + private DisplayController _displayController; } } } diff --git a/Contralto/CPU/Tasks/EmulatorTask.cs b/Contralto/CPU/Tasks/EmulatorTask.cs index a2c4263..f7bc740 100644 --- a/Contralto/CPU/Tasks/EmulatorTask.cs +++ b/Contralto/CPU/Tasks/EmulatorTask.cs @@ -55,31 +55,72 @@ namespace Contralto.CPU } } - protected override void ExecuteSpecialFunction1(MicroInstruction instruction) + protected override void ExecuteSpecialFunction1Early(MicroInstruction instruction) { EmulatorF1 ef1 = (EmulatorF1)instruction.F1; switch (ef1) { case EmulatorF1.RSNF: - // TODO: make configurable + // + // Early: // "...decoded by the Ethernet interface, which gates the host address wired on the // backplane onto BUS[8-15]. BUS[0-7] is not driven and will therefore be -1. If // no Ethernet interface is present, BUS will be -1. // - _busData &= (0xff00 | 0x42); + _busData &= (ushort)((0xff00 | _cpu._system.EthernetController.Address)); + break; + } + } + + protected override void ExecuteSpecialFunction1(MicroInstruction instruction) + { + EmulatorF1 ef1 = (EmulatorF1)instruction.F1; + switch (ef1) + { + case EmulatorF1.LoadRMR: + // + // "The emulator F1 RMR<- causes the reset mode register to be loaded from the processor bus. The 16 bits of the + // processor bus correspond to the 16 Alto tasks in the following way: the low order bit of the processor + // bus specifies the initial mode of task 0, the lowest priority task (emulator), and the high-order bit of the + // bus specifies the initial mode of task 15, the highest priority task(recall that task i starts at location i; the + // reset mode register determines only which microinstruction bank will be used at the outset). A task will + // commence in ROM0 if its associated bit in the reset mode register contains the value 1; otherwise it will + // start in RAM0.Upon initial power - up of the Alto, and after each reset operation, the reset mode register + // is automatically set to all ones, corresponding to starting all tasks in ROM0." + // + _cpu._rmr = _busData; + break; + + case EmulatorF1.RSNF: + // Handled in the Early handler. break; case EmulatorF1.STARTF: - // Dispatch function to Ethernet I/O based on contents of AC0... (TBD: what are these?) - // For now do nothing, since we have no Ethernet implemented - //throw new NotImplementedException(); + // Dispatch function to Ethernet I/O based on contents of AC0. if ((_busData & 0x8000) != 0) { - Console.WriteLine("Emulator STARTF -- boot"); + // + // BOOT (soft-reset) operation. + // Reset the CPU using the current RMR (start tasks in RAM or ROM as specified.) + _cpu.SoftReset(); } else if(_busData != 0) { - Console.WriteLine("Emulator STARTF -- {0}", Conversion.ToOctal(_busData)); + // + // Dispatch to the appropriate device. + // The Ethernet controller is the only common device that is documented + // to have used STARTF, so we'll just go there directly; if other + // hardware is discovered we'll put together a more flexible dispatch. + // + if (_busData < 4) + { + _cpu._system.EthernetController.STARTF(_busData); + } + else + { + Logging.Log.Write(Logging.LogType.Warning, Logging.LogComponent.EmulatorTask, "STARTF for non-Ethernet device (code {0})", + Conversion.ToOctal(_busData)); + } } break; diff --git a/Contralto/CPU/Tasks/EthernetTask.cs b/Contralto/CPU/Tasks/EthernetTask.cs new file mode 100644 index 0000000..07bf69a --- /dev/null +++ b/Contralto/CPU/Tasks/EthernetTask.cs @@ -0,0 +1,173 @@ +using Contralto.IO; +using Contralto.Logging; +using System; + +namespace Contralto.CPU +{ + public partial class AltoCPU + { + /// + /// EthernetTask implements Ethernet-specific task function + /// + private sealed class EthernetTask : Task + { + public EthernetTask(AltoCPU cpu) : base(cpu) + { + _taskType = TaskType.Ethernet; + _wakeup = false; + + _ethernetController = _cpu._system.EthernetController; + } + + protected override bool ExecuteInstruction(MicroInstruction instruction) + { + // The Ethernet task only remains awake if there are pending data wakeups + if (_ethernetController.CountdownWakeup) + { + // + // The resulting [Countdown] wakeup is cleared when the Ether task next runs. + _ethernetController.CountdownWakeup = false; + _wakeup = false; + } + + return base.ExecuteInstruction(instruction); + } + + protected override ushort GetBusSource(int bs) + { + EthernetBusSource ebs = (EthernetBusSource)bs; + + switch(ebs) + { + case EthernetBusSource.EIDFCT: + // Input Data Function. Gates the contents of the FIFO to BUS[0-15], and + // increments the read pointer at the end of the cycle. + return _ethernetController.ReadInputFifo(false /* increment read pointer */); + + default: + throw new NotImplementedException(String.Format("Unimplemented Ethernet BS {0}", ebs)); + } + } + + protected override void ExecuteSpecialFunction1(MicroInstruction instruction) + { + EthernetF1 ef1 = (EthernetF1)instruction.F1; + switch(ef1) + { + case EthernetF1.EILFCT: + // Input Look Function. Gates the contents of the FIFO to BUS[0-15] but does + // not increment the read pointer. + _busData &= _ethernetController.ReadInputFifo(true /* do not increment read pointer */); + break; + + case EthernetF1.EPFCT: + // Post Function. Gates interface status to BUS[8-15]. Resets the interface at + // the end of the cycle and removes wakeup for this task. + _busData &= _ethernetController.Status; + _ethernetController.ResetInterface(); + _wakeup = false; + Log.Write(LogComponent.EthernetController, "EPFCT: Status {0}, bus now {1}", + Conversion.ToOctal(_ethernetController.Status), + Conversion.ToOctal(_busData)); + break; + + case EthernetF1.EWFCT: + // Countdown Wakeup Function. Sets a flip flop in the interface that will + // cause a wakeup to the Ether task on the next tick of SWAKMRT. This + // function must be issued in the instruction after a TASK. The resulting + // wakeup is cleared when the Ether task next runs. + Log.Write(LogComponent.EthernetController, "Enabling countdown wakeups."); + _ethernetController.CountdownWakeup = true; + break; + + default: + throw new NotImplementedException(String.Format("Unimplemented Ethernet F1 {0}", ef1)); + + } + } + + protected override void ExecuteSpecialFunction2(MicroInstruction instruction) + { + EthernetF2 ef2 = (EthernetF2)instruction.F2; + switch (ef2) + { + case EthernetF2.EODFCT: + // Output Data Function. Loads the FIFO from BUS[0-15], then increments the + // write pointer at the end of the cycle. + _ethernetController.WriteOutputFifo(_busData); + break; + + case EthernetF2.EOSFCT: + // Output Start Function. Sets the OBusy flip flop in the interface, starting + // data wakeups to fill the FIFO for output. When the FIFO is full, or EEFct has + // been issued, the interface will wait for silence on the Ether and begin + // transmitting. + _ethernetController.StartOutput(); + break; + + case EthernetF2.ERBFCT: + // Reset Branch Funct ion. This command dispatch function merges the ICMD + // and OCMD flip flops, into NEXT[6-7]. These flip flops are the means of + // communication between the emulator task and the Ethernet task. The + // emulator task sets them from BUS[14-15] with the STARTF function, causing + // the Ethernet task to wakeup, dispatch on them and then reset them with + // EPFCT. + Log.Write(LogComponent.EthernetController, "EBRBFCT: SIO is {0}.", _ethernetController.IOCMD); + _nextModifier |= (ushort)((_ethernetController.IOCMD << 2)); + break; + + case EthernetF2.EEFCT: + // End of transmission Function. This function is issued when all of the main + // memory output buffer has been transferred to the FIFO. EEFCT disables + // further data wakeups. + _ethernetController.EndTransmission(); + break; + + case EthernetF2.EBFCT: + // Branch Function. ORs a one into NEXT[7] if an input data late is detected, + // or an SIO with AC0[14:15] non-zero is issued, or if the transmitter or receiver + // goes done. ORs a one into NEXT[6] if a collision is detected. + + if (_ethernetController.DataLate || + _ethernetController.IOCMD != 0 || + _ethernetController.OperationDone) + { + Log.Write(LogComponent.EthernetController, "EBFCT: DataLate {0} IOCMD {1} Done {2}", _ethernetController.DataLate, _ethernetController.IOCMD, _ethernetController.OperationDone); + _nextModifier |= 0x4; + } + + if (_ethernetController.Collision) + { + Log.Write(LogComponent.EthernetController, "EBFCT: Collision"); + _nextModifier |= 0x8; + } + break; + + case EthernetF2.ECBFCT: + // Countdown Branch Function. ORs a one into NEXT[7] if the FIFO is not + // empty. + if (!_ethernetController.FIFOEmpty) + { + Log.Write(LogComponent.EthernetController, "ECBFCT: FIFO empty"); + _nextModifier |= 0x4; + } + break; + + case EthernetF2.EISFCT: + // Input Start Function. Sets the IBusy flip flop in the interface, causing it to + // hunt for the beginning of a packet: silence on the Ether followed by a + // transition. When the interface has collected two words, it will begin + // generating data wakeups to the microcode. + _ethernetController.StartInput(); + break; + + default: + throw new NotImplementedException(String.Format("Unimplemented Ethernet F2 {0}", ef2)); + + } + } + + private EthernetController _ethernetController; + } + } +} diff --git a/Contralto/CPU/Tasks/Task.cs b/Contralto/CPU/Tasks/Task.cs index 0bf521b..d9a916f 100644 --- a/Contralto/CPU/Tasks/Task.cs +++ b/Contralto/CPU/Tasks/Task.cs @@ -103,8 +103,7 @@ namespace Contralto.CPU // // Wait for memory state machine if a memory operation is requested by this instruction and - // the memory isn't ready yet. - // TODO: this needs to be seriously cleaned up. + // the memory isn't ready yet. // if (instruction.MemoryAccess) { @@ -186,11 +185,11 @@ namespace Contralto.CPU } // Constant ROM access: - // The constant memory is gated to the bus by F1=7, F2=7, or BS>4. The constant memory is addressed by the + // "The constant memory is gated to the bus by F1=7, F2=7, or BS>4. The constant memory is addressed by the // (8 bit) concatenation of RSELECT and BS. The intent in enabling constants with BS>4 is to provide a masking // facility, particularly for the <-MOUSE and <-DISP bus sources. This works because the processor bus ANDs if // more than one source is gated to it. Up to 32 such mask contans can be provided for each of the four bus sources - // > 4. + // > 4." // NOTE also: // "Note that the [emulator task F2] functions which replace the low bits of RSELECT with IR affect only the // selection of R; they do not affect the address supplied to the constant ROM." @@ -208,7 +207,13 @@ namespace Contralto.CPU { _busData &= UCodeMemory.ReadRAM(); _rdRam = false; - } + } + + // + // Let F1s that need to modify bus data before the ALU runs do their thing + // (this is just used by the emulator RSNF...) + // + ExecuteSpecialFunction1Early(instruction); // Do ALU operation aluData = ALU.Execute(instruction.ALUF, _busData, _cpu._t, _skip); @@ -408,7 +413,7 @@ namespace Contralto.CPU if (swMode) { //Console.WriteLine("SWMODE NEXT {0} Modifier {1}", Conversion.ToOctal(instruction.NEXT), Conversion.ToOctal(nextModifier)); - UCodeMemory.SwitchMode((ushort)(instruction.NEXT | nextModifier)); + UCodeMemory.SwitchMode((ushort)(instruction.NEXT | nextModifier), _taskType); } // @@ -433,6 +438,11 @@ namespace Contralto.CPU return 0; } + protected virtual void ExecuteSpecialFunction1Early(MicroInstruction instruction) + { + // Nothing by default + } + protected virtual void ExecuteSpecialFunction1(MicroInstruction instruction) { // Nothing by default diff --git a/Contralto/CPU/UCodeMemory.cs b/Contralto/CPU/UCodeMemory.cs index 7da167a..7e6d8c2 100644 --- a/Contralto/CPU/UCodeMemory.cs +++ b/Contralto/CPU/UCodeMemory.cs @@ -33,6 +33,11 @@ namespace Contralto.CPU Init(); } + public static void Reset() + { + Init(); + } + private static void Init() { // @@ -56,14 +61,22 @@ namespace Contralto.CPU UpdateRAMCache(i); } - // Start in ROM0 -- TODO: need to implement reset logic - _microcodeBank = MicrocodeBank.ROM0; + // Start in ROM0 + _microcodeBank = new MicrocodeBank[16]; _ramAddr = 0; _ramBank = 0; _ramSelect = true; _lowHalfsel = true; } + public static void LoadBanksFromRMR(ushort rmr) + { + for(int i=0;i<16;i++) + { + _microcodeBank[i] = (rmr & (1 << i)) == 0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM0; + } + } + /// /// Exposes the raw contents of the Microcode ROM /// @@ -82,7 +95,8 @@ namespace Contralto.CPU public static MicrocodeBank Bank { - get { return _microcodeBank; } + // Just return the Bank for the Emulator task for now + get { return _microcodeBank[(int)TaskType.Emulator]; } } public static void LoadControlRAMAddress(ushort address) @@ -96,32 +110,33 @@ namespace Contralto.CPU /// /// Implements the SWMODE F1 logic; selects the proper uCode bank (from /// RAM or ROM) based on the supplied NEXT value. + /// Technically this is only supported for the Emulator task. /// /// - public static void SwitchMode(ushort nextAddress) + public static void SwitchMode(ushort nextAddress, TaskType task) { - Logging.Log.Write(Logging.LogComponent.Microcode, "SWMODE: Current Bank {0}", _microcodeBank); + Logging.Log.Write(Logging.LogComponent.Microcode, "SWMODE: Current Bank {0}", _microcodeBank[(int)task]); // 2K ROM - switch(_microcodeBank) + switch(_microcodeBank[(int)task]) { case MicrocodeBank.ROM0: - _microcodeBank = (nextAddress & 0x100) == 0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM1; + _microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM1; break; case MicrocodeBank.ROM1: - _microcodeBank = (nextAddress & 0x100) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.RAM0; + _microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.RAM0; break; case MicrocodeBank.RAM0: - _microcodeBank = (nextAddress & 0x100) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.ROM1; + _microcodeBank[(int)task] = (nextAddress & 0x100) == 0 ? MicrocodeBank.ROM0 : MicrocodeBank.ROM1; break; } - - // for 1K ROM - //_microcodeBank = _microcodeBank == MicrocodeBank.ROM0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM0; - Logging.Log.Write(Logging.LogComponent.Microcode, "SWMODE: New Bank {0}", _microcodeBank); + // for 1K ROM + //_microcodeBank[(int)task] = _microcodeBank[(int)task] == MicrocodeBank.ROM0 ? MicrocodeBank.RAM0 : MicrocodeBank.ROM0; + + Logging.Log.Write(Logging.LogComponent.Microcode, "SWMODE: New Bank {0} for Task {1}", _microcodeBank[(int)task], task); } public static ushort ReadRAM() @@ -193,18 +208,8 @@ namespace Contralto.CPU /// /// public static MicroInstruction GetInstruction(ushort address, TaskType task) - { - // Only RAM-enabled tasks can execute from anything other than ROM (right now) - if (task == TaskType.Emulator) - { - // banked - return _decodeCache[address + (int)_microcodeBank * 1024]; - } - else - { - // ROM only - return _decodeCache[address]; - } + { + return _decodeCache[address + (int)_microcodeBank[(int)task] * 1024]; } private static void LoadMicrocode(RomFile[] romInfo) @@ -316,7 +321,7 @@ namespace Contralto.CPU private static MicroInstruction[] _decodeCache; - private static MicrocodeBank _microcodeBank; + private static MicrocodeBank[] _microcodeBank; private static int _ramBank; private static bool _ramSelect; diff --git a/Contralto/Contralto.csproj b/Contralto/Contralto.csproj index 38c3677..9b14e91 100644 --- a/Contralto/Contralto.csproj +++ b/Contralto/Contralto.csproj @@ -80,6 +80,7 @@ + @@ -102,9 +103,10 @@ - + + diff --git a/Contralto/Debugger.cs b/Contralto/Debugger.cs index cba9543..0f9f6f9 100644 --- a/Contralto/Debugger.cs +++ b/Contralto/Debugger.cs @@ -185,7 +185,9 @@ namespace Contralto case ExecutionState.InternalError: ExecutionStateLabel.Text = String.Format("Stopped (error {0})", _lastExceptionText); break; - } + } + + this.BringToFront(); } private void RefreshMicrocodeDisassembly() diff --git a/Contralto/Display/DisplayController.cs b/Contralto/Display/DisplayController.cs index d955adc..3a37ae8 100644 --- a/Contralto/Display/DisplayController.cs +++ b/Contralto/Display/DisplayController.cs @@ -69,7 +69,7 @@ namespace Contralto.Display _wordWakeup = new Event(_wordDuration, null, WordCallback); // Kick things off - FieldStart(); + _system.Scheduler.Schedule(_verticalBlankScanlineWakeup); } private void FieldStart() @@ -102,7 +102,13 @@ namespace Contralto.Display // Run MRT _system.CPU.WakeupTask(TaskType.MemoryRefresh); - + + // Run Ethernet if a countdown wakeup is in progress + if (_system.EthernetController.CountdownWakeup) + { + _system.CPU.WakeupTask(TaskType.Ethernet); + } + if (_vblankScanlineCount > (_evenField ? 33 : 34)) { // End of vblank: diff --git a/Contralto/ExecutionController.cs b/Contralto/ExecutionController.cs index 5e6359f..6e5a5a4 100644 --- a/Contralto/ExecutionController.cs +++ b/Contralto/ExecutionController.cs @@ -40,7 +40,19 @@ namespace Contralto public void Reset() { + bool running = IsRunning; + + if (running) + { + StopExecution(); + } _system.Reset(); + + if (running) + { + StartExecution(); + } + } public bool IsRunning diff --git a/Contralto/IO/Diablo30Drive.cs b/Contralto/IO/DiabloDrive.cs similarity index 97% rename from Contralto/IO/Diablo30Drive.cs rename to Contralto/IO/DiabloDrive.cs index 0bc4f15..6d473e7 100644 --- a/Contralto/IO/Diablo30Drive.cs +++ b/Contralto/IO/DiabloDrive.cs @@ -40,9 +40,9 @@ namespace Contralto.IO /// Encapsulates logic that belongs to the drive, including loading/saving packs, /// seeking and reading sector data. /// - public class Diablo30Drive + public class DiabloDrive { - public Diablo30Drive(AltoSystem system) + public DiabloDrive(AltoSystem system) { _system = system; Reset(); @@ -71,9 +71,14 @@ namespace Contralto.IO Reset(); } - public bool IsLoaded() + public bool IsLoaded { - return _pack != null; + get { return _pack != null; } + } + + public DiabloPack Pack + { + get { return _pack; } } public int Sector diff --git a/Contralto/IO/DiabloPack.cs b/Contralto/IO/DiabloPack.cs index 0438e22..743d076 100644 --- a/Contralto/IO/DiabloPack.cs +++ b/Contralto/IO/DiabloPack.cs @@ -74,6 +74,7 @@ namespace Contralto.IO public DiabloPack(DiabloDiskType type) { _diskType = type; + _packName = null; _geometry = new DiskGeometry(type == DiabloDiskType.Diablo31 ? 203 : 406, 2, 12); _sectors = new DiabloDiskSector[_geometry.Cylinders, _geometry.Tracks, _geometry.Sectors]; } @@ -83,8 +84,14 @@ namespace Contralto.IO get { return _geometry; } } - public void Load(Stream imageStream, bool reverseByteOrder) + public string PackName { + get { return _packName; } + } + + public void Load(Stream imageStream, string path, bool reverseByteOrder) + { + _packName = path; for(int cylinder = 0; cylinder < _geometry.Cylinders; cylinder++) { for(int track = 0; track < _geometry.Tracks; track++) @@ -150,6 +157,7 @@ namespace Contralto.IO } } + private string _packName; // The file from whence the data came private DiabloDiskSector[,,] _sectors; private DiabloDiskType _diskType; private DiskGeometry _geometry; diff --git a/Contralto/IO/DiskController.cs b/Contralto/IO/DiskController.cs index da5654b..bce6428 100644 --- a/Contralto/IO/DiskController.cs +++ b/Contralto/IO/DiskController.cs @@ -12,9 +12,9 @@ namespace Contralto.IO _system = system; // Load the drives - _drives = new Diablo30Drive[2]; - _drives[0] = new Diablo30Drive(_system); - _drives[1] = new Diablo30Drive(_system); + _drives = new DiabloDrive[2]; + _drives[0] = new DiabloDrive(_system); + _drives[1] = new DiabloDrive(_system); Reset(); } @@ -113,12 +113,7 @@ namespace Contralto.IO if (_sendAdr & (_kDataWrite & 0x2) != 0) { // Select disk if _sendAdr is true - _disk = (_kAdr & 0x1); - - if (_disk != 0) - { - Console.WriteLine("*** DISK 1 SELECTED ***"); - } + _disk = (_kAdr & 0x1); } } @@ -196,7 +191,7 @@ namespace Contralto.IO } } - public Diablo30Drive[] Drives + public DiabloDrive[] Drives { get { return _drives; } } @@ -225,13 +220,12 @@ namespace Contralto.IO // Reset drives _drives[0].Reset(); - _drives[1].Reset(); - - // Wakeup the sector task first thing - _system.CPU.WakeupTask(CPU.TaskType.DiskSector); + _drives[1].Reset(); // Create events to be reused during execution - _sectorEvent = new Event(_sectorDuration, null, SectorCallback); + + // Schedule the first sector immediately. + _sectorEvent = new Event(0, null, SectorCallback); _wordEvent = new Event(_wordDuration, null, WordCallback); _seclateEvent = new Event(_seclateDuration, null, SeclateCallback); _seekEvent = new Event(_seekDuration, null, SeekCallback); @@ -305,7 +299,7 @@ namespace Contralto.IO } else { - // // Schedule next sector pulse immediately + // Schedule next sector pulse immediately _sectorEvent.TimestampNsec = skewNsec; _system.Scheduler.Schedule(_sectorEvent); } @@ -370,6 +364,7 @@ namespace Contralto.IO { // set "seek fail" bit based on selected cylinder (if out of bounds) and do not // commence a seek if so. + // TODO: handle Model-44 cylinder count (and packs, for that matter) if (destCylinder > 202) { _kStat |= 0x0080; @@ -580,7 +575,7 @@ namespace Contralto.IO return (ulong)(seekTimeMsec * Conversion.MsecToNsec); } - private Diablo30Drive SelectedDrive + private DiabloDrive SelectedDrive { get { return _drives[_disk]; } } @@ -661,7 +656,7 @@ namespace Contralto.IO private Event _seclateEvent; // Attached drives - private Diablo30Drive[] _drives; + private DiabloDrive[] _drives; private AltoSystem _system; diff --git a/Contralto/IO/EthernetController.cs b/Contralto/IO/EthernetController.cs new file mode 100644 index 0000000..7e670c8 --- /dev/null +++ b/Contralto/IO/EthernetController.cs @@ -0,0 +1,258 @@ +using Contralto.CPU; +using Contralto.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contralto.IO +{ + public class EthernetController + { + public EthernetController(AltoSystem system) + { + _system = system; + + // TODO: make this configurable + _ethernetAddress = 0x22; + + _fifo = new Queue(); + Reset(); + + _fifoWakeupEvent = new Event(_fifoTransmitDuration, null, FIFOCallback); + } + + public void Reset() + { + ResetInterface(); + } + + public byte Address + { + get { return _ethernetAddress; } + } + + /// + /// The ICMD and OCMD flip-flops, combined into a single value + /// as written by STARTF. + /// (bit 15 = OCMD, bit 14 = ICMD) + /// + public int IOCMD + { + get { return _ioCmd; } + } + + public bool FIFOEmpty + { + get { return _fifo.Count == 0; } + } + + public bool OperationDone + { + get { return !_oBusy && !_iBusy; } + } + + public bool Collision + { + get { return _collision; } + } + + public bool DataLate + { + get { return _dataLate; } + } + + public ushort Status + { + get + { + return _status; + } + } + + public bool CountdownWakeup + { + get { return _countdownWakeup; } + set { _countdownWakeup = value; } + } + + public void ResetInterface() + { + // Latch status before resetting + _status = (ushort)( + (0xffc0) | // bits always set + (_dataLate ? 0x00 : 0x20) | + (_collision ? 0x00 : 0x10) | + (_crcBad ? 0x00 : 0x08) | + ((~_ioCmd & 0x3) << 1) | + (_incomplete ? 0x00 : 0x01)); + + _ioCmd = 0; + _oBusy = false; + _iBusy = false; + _dataLate = false; + _collision = false; + _crcBad = false; + _incomplete = false; + _fifo.Clear(); + + if (_system.CPU != null) + { + _system.CPU.BlockTask(TaskType.Ethernet); + } + + Log.Write(LogComponent.EthernetController, "Interface reset."); + } + + public ushort ReadInputFifo(bool lookOnly) + { + if (FIFOEmpty) + { + Log.Write(LogComponent.EthernetController, "Read from empty Ethernet FIFO, returning 0."); + return 0; + } + + if (lookOnly) + { + return _fifo.Peek(); + } + else + { + return _fifo.Dequeue(); + } + } + + public void WriteOutputFifo(ushort data) + { + if (_fifo.Count == 16) + { + Log.Write(LogComponent.EthernetController, "Write to full Ethernet FIFO, losing first entry."); + _fifo.Dequeue(); + } + + _fifo.Enqueue(data); + + // If the FIFO is full, start transmitting and clear Wakeups + if (_fifo.Count == 16) + { + if (_oBusy) + { + TransmitFIFO(false /* not end */); + } + _system.CPU.BlockTask(TaskType.Ethernet); + } + + Log.Write(LogComponent.EthernetController, "FIFO written with {0}, length now {1}", data, _fifo.Count); + } + + public void StartOutput() + { + // Sets the OBusy flip-flop in the interface + _oBusy = true; + + // Enables wakeups to fill the FIFO + _system.CPU.WakeupTask(TaskType.Ethernet); + + Log.Write(LogComponent.EthernetController, "Output started."); + } + + public void StartInput() + { + InitializeReceiver(); + + Log.Write(LogComponent.EthernetController, "Input started."); + } + + public void EndTransmission() + { + // Clear FIFO wakeup and transmit the remainder of the data in the FIFO + TransmitFIFO(true /* end */); + _system.CPU.BlockTask(TaskType.Ethernet); + Log.Write(LogComponent.EthernetController, "Transmission ended."); + } + + public void STARTF(ushort busData) + { + Log.Write(LogComponent.EthernetController, "Ethernet STARTF {0}", Conversion.ToOctal(busData)); + + // + // HW Manual, p. 54: + // "The emulator task sets [the ICMD and OCMD flip flops] from BUS[14 - 15] with + // the STARTF function, causing the Ethernet task to wakeup, dispatch on them + // and then reset them with EPFCT." + // + _ioCmd = busData & 0x3; + _system.CPU.WakeupTask(TaskType.Ethernet); + } + + private void TransmitFIFO(bool end) + { + // Schedule a callback to pick up the data and shuffle it out the host interface. + _fifoWakeupEvent.Context = end; + _fifoWakeupEvent.TimestampNsec = _fifoTransmitDuration; + _system.Scheduler.Schedule(_fifoWakeupEvent); + } + + private void FIFOCallback(ulong timeNsec, ulong skewNsec, object context) + { + bool end = (bool)context; + + if (!_oBusy) + { + // If OBUSY is no longer set then the interface was reset before + // we got to run; abandon this operation. + Log.Write(LogComponent.EthernetController, "FIFO callback after reset, abandoning."); + return; + } + + Log.Write(LogComponent.EthernetController, "Sending {0} words from fifo.", _fifo.Count); + + // TODO: actually transmit data in FIFO rather than bit-bucketing it. + _fifo.Clear(); + + if (!end) + { + // Enable FIFO microcode wakeups for next batch of data + _system.CPU.WakeupTask(TaskType.Ethernet); + } + else + { + // This is the last of the data, clear the OBUSY flipflop, the transmitter is done. + Log.Write(LogComponent.EthernetController, "Packet complete."); + _oBusy = false; + + // Wakeup at end of transmission. ("OUTGONE Post wakeup.") + _system.CPU.WakeupTask(TaskType.Ethernet); + } + } + + private void InitializeReceiver() + { + // TODO: pull next packet off host ethernet interface that's destined for the Alto, and start the + // process of putting into the FIFO and generating wakeups for the microcode. + } + + private Queue _fifo; + + // Bits in Status register + private int _ioCmd; + private bool _dataLate; + private bool _collision; + private bool _crcBad; + private bool _incomplete; + private ushort _status; + + private byte _ethernetAddress; + private bool _countdownWakeup; + + private bool _oBusy; + private bool _iBusy; + + // FIFO scheduling + private ulong _fifoTransmitDuration = 87075; // ~87000 nsec to transmit 16 words at 3mbit, assuming no collision + private Event _fifoWakeupEvent; + + private AltoSystem _system; + } +} diff --git a/Contralto/IO/Keyboard.cs b/Contralto/IO/Keyboard.cs index 0f8576f..6522cc0 100644 --- a/Contralto/IO/Keyboard.cs +++ b/Contralto/IO/Keyboard.cs @@ -8,7 +8,8 @@ namespace Contralto.IO public enum AltoKey { - A = 0, + None = 0, + A, B, C, D, diff --git a/Contralto/Logging/Log.cs b/Contralto/Logging/Log.cs index ddfd896..8a737b5 100644 --- a/Contralto/Logging/Log.cs +++ b/Contralto/Logging/Log.cs @@ -18,6 +18,9 @@ namespace Contralto.Logging Keyboard = 0x40, Display = 0x80, Microcode = 0x100, + CPU = 0x200, + EthernetController = 0x400, + EthernetTask = 0x800, All = 0x7fffffff } @@ -44,7 +47,7 @@ namespace Contralto.Logging static Log() { // TODO: make configurable - _components = LogComponent.DiskController | LogComponent.DiskSectorTask; + _components = LogComponent.None; // LogComponent.EthernetController; // | LogComponent.Microcode | LogComponent.Memory | LogComponent.CPU; _type = LogType.Normal | LogType.Warning | LogType.Error; } diff --git a/Contralto/Memory/MemoryBus.cs b/Contralto/Memory/MemoryBus.cs index de1f2cb..db17e90 100644 --- a/Contralto/Memory/MemoryBus.cs +++ b/Contralto/Memory/MemoryBus.cs @@ -54,6 +54,8 @@ namespace Contralto.Memory _memoryCycle = 0; _memoryAddress = 0; _memoryData = 0; + _doubleWordStore = false; + _doubleWordMixed = false; _memoryOperationActive = false; _extendedMemoryReference = false; } @@ -162,6 +164,7 @@ namespace Contralto.Memory { _memoryOperationActive = true; _doubleWordStore = false; + _doubleWordMixed = false; _memoryAddress = address; _extendedMemoryReference = extendedMemoryReference; _task = task; @@ -204,14 +207,16 @@ namespace Contralto.Memory // ("Because the Alto II latches memory contents, it is possible to execute _MD anytime after // cycle 5 of a reference and obtain the results of the read operation") // If this is memory cycle 6 we will return the last half of the doubleword to complete a double-word read. - if (_memoryCycle == 6) + if (_memoryCycle == 6 || (_memoryCycle == 5 && _doubleWordMixed)) { - + //Log.Write(LogType.Verbose, LogComponent.Memory, "Double-word read of {0} from {1} (cycle 6)", Conversion.ToOctal(_memoryData2), Conversion.ToOctal(_memoryAddress ^ 1)); + _doubleWordMixed = false; return _memoryData2; } else - { + { + _doubleWordMixed = false; //Log.Write(LogType.Verbose, LogComponent.Memory, "Single-word read of {0} from {1} (post cycle 6)", Conversion.ToOctal(_memoryData), Conversion.ToOctal(_memoryAddress)); return _memoryData; } @@ -235,6 +240,7 @@ namespace Contralto.Memory // Start of doubleword write: WriteToBus(_memoryAddress, data, _task, _extendedMemoryReference); _doubleWordStore = true; + _doubleWordMixed = true; /* Log.Write( @@ -348,5 +354,8 @@ namespace Contralto.Memory // Indicates a double-word store (started on cycle 3) private bool _doubleWordStore; + + // Indicates a mixed double-word store/load (started in cycle 3) + private bool _doubleWordMixed; } } diff --git a/Contralto/Notes.txt b/Contralto/Notes.txt index 1d58cd8..f2047f7 100644 --- a/Contralto/Notes.txt +++ b/Contralto/Notes.txt @@ -64,3 +64,19 @@ Project is on backburner while working on SUPDUP; notes for myself on current st - Disk controller is currently running 75% slower than normal to allow it to work properly; need to figure out why this is necessary -- are my timing calculations off or are there issues with the microcode engine? - + + + 12/17/15: + + Bug found: + + Following code (hit while booting bravox.dsk) gets stuck forever: + + 0001 SUB 3,0 + 0002 DSZ@ AC3+0 + 0003 JMP AC3+1 + + regs 0 and 3 are both zero; location 0 contains 177777, this is outside of the RAM area so cannot be modified; this loops forever. + unsure if this is intentional... (i.e. marks a non-bootable pack) + + diff --git a/Contralto/Program.cs b/Contralto/Program.cs index 12cf1e4..dba263f 100644 --- a/Contralto/Program.cs +++ b/Contralto/Program.cs @@ -10,12 +10,15 @@ namespace Contralto { AltoSystem system = new AltoSystem(); - // for now everything is driven through the debugger + if (args.Length > 0) + { + system.LoadDrive(0, args[0]); + } AltoWindow mainWindow = new AltoWindow(); mainWindow.AttachSystem(system); - + /* Debugger d = new Debugger(system); system.AttachDisplay(d); diff --git a/Contralto/Scheduler.cs b/Contralto/Scheduler.cs index 0f1ca5d..c18e2c4 100644 --- a/Contralto/Scheduler.cs +++ b/Contralto/Scheduler.cs @@ -40,7 +40,8 @@ namespace Contralto /// public object Context { - get { return _context; } + get { return _context; } + set { _context = value; } } /// @@ -155,10 +156,6 @@ namespace Contralto public void Push(Event e) { - if (_queue.Count > 10) - { - Console.WriteLine("Count {0}", _queue.Count); - } // Degenerate case: list is empty or new entry is earlier than the head of the list. if (_queue.Count == 0 || _queue.First.Value.TimestampNsec >= e.TimestampNsec) {