1
0
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:
Josh Dersch 2015-12-17 16:11:03 -08:00
parent 42947488e9
commit 3c8a64bac8
29 changed files with 872 additions and 164 deletions

View File

@ -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;
}
}

View File

@ -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");
}
}
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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

View File

@ -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)

View File

@ -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;

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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;

View 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;
}
}
}

View File

@ -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

View File

@ -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;

View File

@ -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" />

View File

@ -185,7 +185,9 @@ namespace Contralto
case ExecutionState.InternalError:
ExecutionStateLabel.Text = String.Format("Stopped (error {0})", _lastExceptionText);
break;
}
}
this.BringToFront();
}
private void RefreshMicrocodeDisassembly()

View File

@ -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:

View File

@ -40,7 +40,19 @@ namespace Contralto
public void Reset()
{
bool running = IsRunning;
if (running)
{
StopExecution();
}
_system.Reset();
if (running)
{
StartExecution();
}
}
public bool IsRunning

View File

@ -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

View File

@ -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;

View File

@ -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;

View 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;
}
}

View File

@ -8,7 +8,8 @@ namespace Contralto.IO
public enum AltoKey
{
A = 0,
None = 0,
A,
B,
C,
D,

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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);

View File

@ -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)
{