mirror of
https://github.com/livingcomputermuseum/ContrAlto.git
synced 2026-01-17 08:34:15 +00:00
Ethernet implemented, fixed a bug in "mixed" double word stores (store followed by fetch rather than two stores). Fixed a few UI issues, fixed Reset behavior.
This commit is contained in:
parent
42947488e9
commit
3c8a64bac8
37
Contralto/AboutBox.Designer.cs
generated
37
Contralto/AboutBox.Designer.cs
generated
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<IClockable>();
|
||||
_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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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;
|
||||
|
||||
87
Contralto/AltoWindow.Designer.cs
generated
87
Contralto/AltoWindow.Designer.cs
generated
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// "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.
|
||||
/// </summary>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -5,7 +5,7 @@ namespace Contralto.CPU
|
||||
public partial class AltoCPU
|
||||
{
|
||||
/// <summary>
|
||||
/// DisplayWordTask provides functionality for the DWT task
|
||||
/// CursorTask provides functionality for the Cursor-specific task functions
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
173
Contralto/CPU/Tasks/EthernetTask.cs
Normal file
173
Contralto/CPU/Tasks/EthernetTask.cs
Normal file
@ -0,0 +1,173 @@
|
||||
using Contralto.IO;
|
||||
using Contralto.Logging;
|
||||
using System;
|
||||
|
||||
namespace Contralto.CPU
|
||||
{
|
||||
public partial class AltoCPU
|
||||
{
|
||||
/// <summary>
|
||||
/// EthernetTask implements Ethernet-specific task function
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exposes the raw contents of the Microcode ROM
|
||||
/// </summary>
|
||||
@ -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
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="nextAddress"></param>
|
||||
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
|
||||
/// <param name="address"></param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
|
||||
@ -80,6 +80,7 @@
|
||||
<Compile Include="CPU\ConstantMemory.cs" />
|
||||
<Compile Include="CPU\CPU.cs" />
|
||||
<Compile Include="CPU\NovaDisassembler.cs" />
|
||||
<Compile Include="CPU\Tasks\EthernetTask.cs" />
|
||||
<Compile Include="CPU\Tasks\DisplayVerticalTask.cs" />
|
||||
<Compile Include="CPU\Tasks\DisplayHorizontalTask.cs" />
|
||||
<Compile Include="CPU\Tasks\CursorTask.cs" />
|
||||
@ -102,9 +103,10 @@
|
||||
<Compile Include="Display\DisplayController.cs" />
|
||||
<Compile Include="Display\IAltoDisplay.cs" />
|
||||
<Compile Include="IClockable.cs" />
|
||||
<Compile Include="IO\Diablo30Drive.cs" />
|
||||
<Compile Include="IO\DiabloDrive.cs" />
|
||||
<Compile Include="IO\DiskController.cs" />
|
||||
<Compile Include="IO\DiabloPack.cs" />
|
||||
<Compile Include="IO\EthernetController.cs" />
|
||||
<Compile Include="IO\Keyboard.cs" />
|
||||
<Compile Include="IO\Mouse.cs" />
|
||||
<Compile Include="Logging\Log.cs" />
|
||||
|
||||
@ -185,7 +185,9 @@ namespace Contralto
|
||||
case ExecutionState.InternalError:
|
||||
ExecutionStateLabel.Text = String.Format("Stopped (error {0})", _lastExceptionText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.BringToFront();
|
||||
}
|
||||
|
||||
private void RefreshMicrocodeDisassembly()
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -40,7 +40,19 @@ namespace Contralto
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
bool running = IsRunning;
|
||||
|
||||
if (running)
|
||||
{
|
||||
StopExecution();
|
||||
}
|
||||
_system.Reset();
|
||||
|
||||
if (running)
|
||||
{
|
||||
StartExecution();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public bool IsRunning
|
||||
|
||||
@ -40,9 +40,9 @@ namespace Contralto.IO
|
||||
/// Encapsulates logic that belongs to the drive, including loading/saving packs,
|
||||
/// seeking and reading sector data.
|
||||
/// </summary>
|
||||
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
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
258
Contralto/IO/EthernetController.cs
Normal file
258
Contralto/IO/EthernetController.cs
Normal file
@ -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<ushort>();
|
||||
Reset();
|
||||
|
||||
_fifoWakeupEvent = new Event(_fifoTransmitDuration, null, FIFOCallback);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
ResetInterface();
|
||||
}
|
||||
|
||||
public byte Address
|
||||
{
|
||||
get { return _ethernetAddress; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The ICMD and OCMD flip-flops, combined into a single value
|
||||
/// as written by STARTF.
|
||||
/// (bit 15 = OCMD, bit 14 = ICMD)
|
||||
/// </summary>
|
||||
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<ushort> _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;
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,8 @@ namespace Contralto.IO
|
||||
|
||||
public enum AltoKey
|
||||
{
|
||||
A = 0,
|
||||
None = 0,
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
D,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -40,7 +40,8 @@ namespace Contralto
|
||||
/// </summary>
|
||||
public object Context
|
||||
{
|
||||
get { return _context; }
|
||||
get { return _context; }
|
||||
set { _context = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -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)
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user