diff --git a/Contralto/AltoSystem.cs b/Contralto/AltoSystem.cs
index 42782ec..9344db5 100644
--- a/Contralto/AltoSystem.cs
+++ b/Contralto/AltoSystem.cs
@@ -65,14 +65,11 @@ namespace Contralto
///
/// Attaches an emulated display device to the system.
- /// TODO: This is currently tightly-coupled with the Debugger, make
- /// more general.
///
///
- public void AttachDisplay(Debugger d)
+ public void AttachDisplay(IAltoDisplay d)
{
- _displayController.AttachDisplay(d);
- // _fakeDisplayController.AttachDisplay(d);
+ _displayController.AttachDisplay(d);
}
public void SingleStep()
diff --git a/Contralto/AltoWindow.Designer.cs b/Contralto/AltoWindow.Designer.cs
new file mode 100644
index 0000000..44454ce
--- /dev/null
+++ b/Contralto/AltoWindow.Designer.cs
@@ -0,0 +1,263 @@
+namespace Contralto
+{
+ partial class AltoWindow
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.DisplayBox = new System.Windows.Forms.PictureBox();
+ this.menuStrip1 = new System.Windows.Forms.MenuStrip();
+ this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.SystemStartMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.SystemResetMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.drive0ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.loadToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
+ this.unloadToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
+ this.drive1ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.loadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.unloadToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.optionsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.debuggerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.showDebuggerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ 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();
+ ((System.ComponentModel.ISupportInitialize)(this.DisplayBox)).BeginInit();
+ this.menuStrip1.SuspendLayout();
+ this.StatusLine.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // DisplayBox
+ //
+ this.DisplayBox.BackColor = System.Drawing.SystemColors.Window;
+ this.DisplayBox.Location = new System.Drawing.Point(0, 27);
+ this.DisplayBox.Name = "DisplayBox";
+ this.DisplayBox.Size = new System.Drawing.Size(606, 808);
+ this.DisplayBox.TabIndex = 1;
+ this.DisplayBox.TabStop = false;
+ this.DisplayBox.MouseDown += new System.Windows.Forms.MouseEventHandler(this.OnDisplayMouseDown);
+ this.DisplayBox.MouseMove += new System.Windows.Forms.MouseEventHandler(this.OnDisplayMouseMove);
+ this.DisplayBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.OnDisplayMouseUp);
+ //
+ // menuStrip1
+ //
+ this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.fileToolStripMenuItem,
+ this.settingsToolStripMenuItem,
+ this.debuggerToolStripMenuItem,
+ this.helpToolStripMenuItem});
+ this.menuStrip1.Location = new System.Drawing.Point(0, 0);
+ this.menuStrip1.Name = "menuStrip1";
+ this.menuStrip1.Size = new System.Drawing.Size(608, 24);
+ this.menuStrip1.TabIndex = 2;
+ this.menuStrip1.Text = "menuStrip1";
+ //
+ // fileToolStripMenuItem
+ //
+ this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.exitToolStripMenuItem});
+ this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
+ this.fileToolStripMenuItem.Size = new System.Drawing.Size(35, 20);
+ this.fileToolStripMenuItem.Text = "File";
+ //
+ // exitToolStripMenuItem
+ //
+ this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
+ this.exitToolStripMenuItem.Size = new System.Drawing.Size(92, 22);
+ this.exitToolStripMenuItem.Text = "Exit";
+ //
+ // settingsToolStripMenuItem
+ //
+ this.settingsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.SystemStartMenuItem,
+ this.SystemResetMenuItem,
+ this.drive0ToolStripMenuItem,
+ this.drive1ToolStripMenuItem,
+ this.optionsToolStripMenuItem});
+ this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
+ this.settingsToolStripMenuItem.Size = new System.Drawing.Size(54, 20);
+ this.settingsToolStripMenuItem.Text = "System";
+ //
+ // SystemStartMenuItem
+ //
+ this.SystemStartMenuItem.Name = "SystemStartMenuItem";
+ this.SystemStartMenuItem.Size = new System.Drawing.Size(152, 22);
+ this.SystemStartMenuItem.Text = "Start";
+ this.SystemStartMenuItem.Click += new System.EventHandler(this.OnSystemStartMenuClick);
+ //
+ // SystemResetMenuItem
+ //
+ this.SystemResetMenuItem.Enabled = false;
+ this.SystemResetMenuItem.Name = "SystemResetMenuItem";
+ this.SystemResetMenuItem.Size = new System.Drawing.Size(152, 22);
+ this.SystemResetMenuItem.Text = "Reset";
+ //
+ // drive0ToolStripMenuItem
+ //
+ this.drive0ToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.loadToolStripMenuItem1,
+ this.unloadToolStripMenuItem1});
+ this.drive0ToolStripMenuItem.Name = "drive0ToolStripMenuItem";
+ this.drive0ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
+ this.drive0ToolStripMenuItem.Text = "Drive 0";
+ //
+ // loadToolStripMenuItem1
+ //
+ this.loadToolStripMenuItem1.Name = "loadToolStripMenuItem1";
+ this.loadToolStripMenuItem1.Size = new System.Drawing.Size(119, 22);
+ this.loadToolStripMenuItem1.Text = "Load...";
+ //
+ // unloadToolStripMenuItem1
+ //
+ this.unloadToolStripMenuItem1.Name = "unloadToolStripMenuItem1";
+ this.unloadToolStripMenuItem1.Size = new System.Drawing.Size(119, 22);
+ this.unloadToolStripMenuItem1.Text = "Unload...";
+ //
+ // drive1ToolStripMenuItem
+ //
+ this.drive1ToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.loadToolStripMenuItem,
+ this.unloadToolStripMenuItem});
+ this.drive1ToolStripMenuItem.Name = "drive1ToolStripMenuItem";
+ this.drive1ToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
+ this.drive1ToolStripMenuItem.Text = "Drive 1";
+ //
+ // loadToolStripMenuItem
+ //
+ this.loadToolStripMenuItem.Name = "loadToolStripMenuItem";
+ this.loadToolStripMenuItem.Size = new System.Drawing.Size(119, 22);
+ this.loadToolStripMenuItem.Text = "Load...";
+ //
+ // unloadToolStripMenuItem
+ //
+ this.unloadToolStripMenuItem.Name = "unloadToolStripMenuItem";
+ this.unloadToolStripMenuItem.Size = new System.Drawing.Size(119, 22);
+ this.unloadToolStripMenuItem.Text = "Unload...";
+ //
+ // optionsToolStripMenuItem
+ //
+ this.optionsToolStripMenuItem.Name = "optionsToolStripMenuItem";
+ this.optionsToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
+ this.optionsToolStripMenuItem.Text = "Options...";
+ //
+ // debuggerToolStripMenuItem
+ //
+ this.debuggerToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.showDebuggerToolStripMenuItem});
+ this.debuggerToolStripMenuItem.Name = "debuggerToolStripMenuItem";
+ this.debuggerToolStripMenuItem.Size = new System.Drawing.Size(66, 20);
+ this.debuggerToolStripMenuItem.Text = "Debugger";
+ //
+ // showDebuggerToolStripMenuItem
+ //
+ this.showDebuggerToolStripMenuItem.Name = "showDebuggerToolStripMenuItem";
+ this.showDebuggerToolStripMenuItem.Size = new System.Drawing.Size(150, 22);
+ this.showDebuggerToolStripMenuItem.Text = "Show Debugger";
+ //
+ // helpToolStripMenuItem
+ //
+ this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.aboutToolStripMenuItem});
+ this.helpToolStripMenuItem.Name = "helpToolStripMenuItem";
+ this.helpToolStripMenuItem.Size = new System.Drawing.Size(40, 20);
+ this.helpToolStripMenuItem.Text = "Help";
+ //
+ // aboutToolStripMenuItem
+ //
+ this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem";
+ this.aboutToolStripMenuItem.Size = new System.Drawing.Size(103, 22);
+ this.aboutToolStripMenuItem.Text = "About";
+ //
+ // StatusLine
+ //
+ this.StatusLine.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.StatusLabel});
+ this.StatusLine.Location = new System.Drawing.Point(0, 837);
+ this.StatusLine.Name = "StatusLine";
+ this.StatusLine.Size = new System.Drawing.Size(608, 22);
+ this.StatusLine.TabIndex = 3;
+ this.StatusLine.Text = "statusStrip1";
+ this.StatusLine.KeyDown += new System.Windows.Forms.KeyEventHandler(this.OnKeyDown);
+ //
+ // StatusLabel
+ //
+ this.StatusLabel.Name = "StatusLabel";
+ this.StatusLabel.Size = new System.Drawing.Size(63, 17);
+ this.StatusLabel.Text = "StatusLabel";
+ //
+ // AltoWindow
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(608, 859);
+ this.Controls.Add(this.StatusLine);
+ this.Controls.Add(this.DisplayBox);
+ this.Controls.Add(this.menuStrip1);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
+ this.MainMenuStrip = this.menuStrip1;
+ this.MinimizeBox = false;
+ this.Name = "AltoWindow";
+ this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
+ this.Text = "ContrAlto";
+ this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.OnKeyDown);
+ this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.OnKeyUp);
+ ((System.ComponentModel.ISupportInitialize)(this.DisplayBox)).EndInit();
+ this.menuStrip1.ResumeLayout(false);
+ this.menuStrip1.PerformLayout();
+ this.StatusLine.ResumeLayout(false);
+ this.StatusLine.PerformLayout();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.PictureBox DisplayBox;
+ private System.Windows.Forms.MenuStrip menuStrip1;
+ private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem SystemStartMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem SystemResetMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem drive0ToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem loadToolStripMenuItem1;
+ private System.Windows.Forms.ToolStripMenuItem unloadToolStripMenuItem1;
+ private System.Windows.Forms.ToolStripMenuItem drive1ToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem loadToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem unloadToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem debuggerToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem showDebuggerToolStripMenuItem;
+ private System.Windows.Forms.StatusStrip StatusLine;
+ private System.Windows.Forms.ToolStripStatusLabel StatusLabel;
+ }
+}
\ No newline at end of file
diff --git a/Contralto/AltoWindow.resx b/Contralto/AltoWindow.resx
new file mode 100644
index 0000000..501e458
--- /dev/null
+++ b/Contralto/AltoWindow.resx
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
+ 126, 17
+
+
\ No newline at end of file
diff --git a/Contralto/Contralto.csproj b/Contralto/Contralto.csproj
index 0a43734..d3be055 100644
--- a/Contralto/Contralto.csproj
+++ b/Contralto/Contralto.csproj
@@ -64,6 +64,12 @@
+
+ Form
+
+
+ AltoWindow.cs
+
@@ -88,7 +94,9 @@
+
+
@@ -114,6 +122,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
@@ -260,6 +271,9 @@
+
+ AltoWindow.cs
+
Debugger.cs
diff --git a/Contralto/Debugger.cs b/Contralto/Debugger.cs
index 7dc2021..a21b622 100644
--- a/Contralto/Debugger.cs
+++ b/Contralto/Debugger.cs
@@ -12,13 +12,14 @@ using Contralto.CPU;
using System.Threading;
using System.Drawing.Imaging;
using Contralto.IO;
+using Contralto.Display;
namespace Contralto
{
///
/// A basic & hacky debugger. To be improved.
///
- public partial class Debugger : Form
+ public partial class Debugger : Form, IAltoDisplay
{
public Debugger(AltoSystem system)
{
@@ -76,7 +77,7 @@ namespace Contralto
RefreshUI();
}
- public void RefreshAltoDisplay()
+ public void Render()
{
BeginInvoke(new StepDelegate(RefreshDisplayBox));
}
diff --git a/Contralto/Disk/bravox.dsk b/Contralto/Disk/bravox.dsk
new file mode 100644
index 0000000..1b2a22e
Binary files /dev/null and b/Contralto/Disk/bravox.dsk differ
diff --git a/Contralto/Display/DisplayController.cs b/Contralto/Display/DisplayController.cs
index 3d081ac..d955adc 100644
--- a/Contralto/Display/DisplayController.cs
+++ b/Contralto/Display/DisplayController.cs
@@ -16,7 +16,7 @@ namespace Contralto.Display
Reset();
}
- public void AttachDisplay(Debugger display)
+ public void AttachDisplay(IAltoDisplay display)
{
_display = display;
}
@@ -207,7 +207,7 @@ namespace Contralto.Display
// Done with field.
// Draw the completed field to the emulated display.
- _display.RefreshAltoDisplay();
+ _display.Render();
// And start over
FieldStart();
@@ -370,7 +370,7 @@ namespace Contralto.Display
private Queue _dataBuffer = new Queue(16);
private AltoSystem _system;
- private Debugger _display;
+ private IAltoDisplay _display;
private int _fields;
diff --git a/Contralto/Display/FakeDisplayController.cs b/Contralto/Display/FakeDisplayController.cs
index f0e8cd6..2d7f071 100644
--- a/Contralto/Display/FakeDisplayController.cs
+++ b/Contralto/Display/FakeDisplayController.cs
@@ -32,7 +32,7 @@
_clocks -= _frameClocks;
RenderDisplay();
- _display.RefreshAltoDisplay();
+ _display.Render();
}
}
@@ -51,7 +51,7 @@
}
}
- _display.RefreshAltoDisplay();
+ _display.Render();
return;
}
@@ -89,7 +89,7 @@
_display.DrawDisplayWord(scanline, wordOffset, (ushort)(dcb.whiteOnBlack ? 0x0 : 0xffff), false);
}
- _display.RefreshAltoDisplay();
+ _display.Render();
// decrement scan line counter for this DCB, if < 0, grab next DCB.
dcb.scanlineCount--;
@@ -144,7 +144,7 @@
private double _clocks;
private AltoSystem _system;
- private Debugger _display;
+ private IAltoDisplay _display;
// Timing constants
// 38uS per scanline; 4uS for hblank.
diff --git a/Contralto/Display/IAltoDisplay.cs b/Contralto/Display/IAltoDisplay.cs
new file mode 100644
index 0000000..d2d5cd2
--- /dev/null
+++ b/Contralto/Display/IAltoDisplay.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Contralto.Display
+{
+ public interface IAltoDisplay
+ {
+ ///
+ /// Renders a word's worth of data to the specified scanline and word offset.
+ ///
+ ///
+ ///
+ ///
+ ///
+ void DrawDisplayWord(int scanline, int wordOffset, ushort dataWord, bool lowRes);
+
+ ///
+ /// Causes the display to be rendered
+ ///
+ void Render();
+ }
+}
diff --git a/Contralto/IO/Diablo30Drive.cs b/Contralto/IO/Diablo30Drive.cs
new file mode 100644
index 0000000..10b449f
--- /dev/null
+++ b/Contralto/IO/Diablo30Drive.cs
@@ -0,0 +1,328 @@
+using Contralto.Logging;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Contralto.IO
+{
+
+ // The data for the current sector
+ public enum CellType
+ {
+ Data,
+ Gap,
+ Sync,
+ }
+
+ public struct DataCell
+ {
+ public DataCell(ushort data, CellType type)
+ {
+ Data = data;
+ Type = type;
+ }
+
+ public ushort Data;
+ public CellType Type;
+
+ public override string ToString()
+ {
+ return String.Format("{0} {1}", Data, Type);
+ }
+ }
+
+ ///
+ /// Encapsulates logic that belongs to the drive, including loading/saving packs,
+ /// seeking and reading sector data.
+ ///
+ public class Diablo30Drive
+ {
+ public Diablo30Drive(AltoSystem system)
+ {
+ _system = system;
+ Reset();
+ }
+
+ public void Reset()
+ {
+ _sector = 0;
+ _cylinder = 0;
+ _head = 0;
+
+ InitSector();
+ LoadSector();
+ }
+
+ public void LoadPack(DiabloPack pack)
+ {
+ _pack = pack;
+ }
+
+ public void UnloadPack()
+ {
+ _pack = null;
+ }
+
+ public bool IsLoaded()
+ {
+ return _pack != null;
+ }
+
+ public int Sector
+ {
+ get { return _sector; }
+ set
+ {
+ // If the last sector was modified,
+ // commit it before moving to the next.
+ if (_sectorModified)
+ {
+ CommitSector();
+ _sectorModified = false;
+ }
+
+ _sector = value;
+ LoadSector();
+ }
+ }
+
+ public int Head
+ {
+ get { return _head; }
+ set
+ {
+ if (value != _head)
+ {
+ // If we switch heads, we need to reload the sector.
+
+ // If the last sector was modified,
+ // commit it before moving to the next.
+ if (_sectorModified)
+ {
+ CommitSector();
+ _sectorModified = false;
+ }
+
+ _head = value;
+ LoadSector();
+ }
+ }
+ }
+
+ public int Cylinder
+ {
+ get { return _cylinder; }
+ set
+ {
+ if (value != _cylinder)
+ {
+ // If we switch cylinders, we need to reload the sector.
+ // If the last sector was modified,
+ // commit it before moving to the next.
+ if (_sectorModified)
+ {
+ CommitSector();
+ _sectorModified = false;
+ }
+
+ _cylinder = value;
+ LoadSector();
+ }
+ }
+ }
+
+ public DataCell ReadWord(int index)
+ {
+ return _sectorData[index];
+ }
+
+ public void WriteWord(int index, ushort data)
+ {
+ if (index < _sectorData.Length)
+ {
+ if (_sectorData[index].Type == CellType.Data)
+ {
+ _sectorData[index].Data = data;
+ }
+ else
+ {
+ Log.Write(LogType.Warning, LogComponent.DiskController, "Data written to non-data section (Sector {0} Word {1} Rec {2} Data {3})", _sector, index, 666, Conversion.ToOctal(data));
+ }
+
+ _sectorModified = true;
+ }
+ }
+
+
+ private void LoadSector()
+ {
+ if (_pack == null)
+ {
+ return;
+ }
+
+ //
+ // Pull data off disk and pack it into our faked-up sector.
+ // Note that this data is packed in in REVERSE ORDER because that's
+ // how it gets written out and it's how the Alto expects it to be read back in.
+ //
+ DiabloDiskSector sector = _pack.GetSector(_cylinder, _head, _sector);
+
+ // Header (2 words data, 1 word cksum)
+ for (int i = _headerOffset + 1, j = 1; i < _headerOffset + 3; i++, j--)
+ {
+ // actual data to be loaded from disk / cksum calculated
+ _sectorData[i] = new DataCell(sector.Header[j], CellType.Data);
+ }
+
+ ushort checksum = CalculateChecksum(_sectorData, _headerOffset + 1, 2);
+ _sectorData[_headerOffset + 3].Data = checksum;
+ Log.Write(LogType.Verbose, LogComponent.DiskController, "Header checksum for C/H/S {0}/{1}/{2} is {3}", _cylinder, _head, _sector, Conversion.ToOctal(checksum));
+
+ // Label (8 words data, 1 word cksum)
+ for (int i = _labelOffset + 1, j = 7; i < _labelOffset + 9; i++, j--)
+ {
+ // actual data to be loaded from disk / cksum calculated
+ _sectorData[i] = new DataCell(sector.Label[j], CellType.Data);
+ }
+
+ checksum = CalculateChecksum(_sectorData, _labelOffset + 1, 8);
+ _sectorData[_labelOffset + 9].Data = checksum;
+ Log.Write(LogType.Verbose, LogComponent.DiskController, "Label checksum for C/H/S {0}/{1}/{2} is {3}", _cylinder, _head, _sector, Conversion.ToOctal(checksum));
+
+ // sector data (256 words data, 1 word cksum)
+ for (int i = _dataOffset + 1, j = 255; i < _dataOffset + 257; i++, j--)
+ {
+ // actual data to be loaded from disk / cksum calculated
+ _sectorData[i] = new DataCell(sector.Data[j], CellType.Data);
+ }
+
+ checksum = CalculateChecksum(_sectorData, _dataOffset + 1, 256);
+ _sectorData[_dataOffset + 257].Data = checksum;
+ Log.Write(LogType.Verbose, LogComponent.DiskController, "Data checksum for C/H/S {0}/{1}/{2} is {3}", _cylinder, _head, _sector, Conversion.ToOctal(checksum));
+
+ }
+
+
+
+ ///
+ /// Commits modified sector data back to the emulated disk.
+ /// Intended to be called at the end of the sector / beginning of the next.
+ /// TODO: we should modify this so that checksums are persisted, possibly...
+ ///
+ private void CommitSector()
+ {
+ if (_pack == null)
+ {
+ return;
+ }
+
+ DiabloDiskSector sector = _pack.GetSector(_cylinder, _head, _sector);
+
+ // Header (2 words data, 1 word cksum)
+ for (int i = _headerOffset + 1, j = 1; i < _headerOffset + 3; i++, j--)
+ {
+ // actual data to be loaded from disk / cksum calculated
+ sector.Header[j] = _sectorData[i].Data;
+ }
+
+ // Label (8 words data, 1 word cksum)
+ for (int i = _labelOffset + 1, j = 7; i < _labelOffset + 9; i++, j--)
+ {
+ // actual data to be loaded from disk / cksum calculated
+ sector.Label[j] = _sectorData[i].Data;
+ }
+
+ // sector data (256 words data, 1 word cksum)
+ for (int i = _dataOffset + 1, j = 255; i < _dataOffset + 257; i++, j--)
+ {
+ // actual data to be loaded from disk / cksum calculated
+ sector.Data[j] = _sectorData[i].Data;
+ }
+ }
+
+ private void InitSector()
+ {
+ // Fill in sector with default data (basically, fill in non-data areas).
+
+ //
+ // header delay, 22 words
+ for (int i = 0; i < _headerOffset; i++)
+ {
+ _sectorData[i] = new DataCell(0, CellType.Gap);
+ }
+
+ _sectorData[_headerOffset] = new DataCell(1, CellType.Sync);
+ // inter-reccord delay between header & label (10 words)
+ for (int i = _headerOffset + 4; i < _labelOffset; i++)
+ {
+ _sectorData[i] = new DataCell(0, CellType.Gap);
+ }
+
+ _sectorData[_labelOffset] = new DataCell(1, CellType.Sync);
+ // inter-reccord delay between label & data (10 words)
+ for (int i = _labelOffset + 10; i < _dataOffset; i++)
+ {
+ _sectorData[i] = new DataCell(0, CellType.Gap);
+ }
+
+ _sectorData[_dataOffset] = new DataCell(1, CellType.Sync);
+ // read-postamble
+ for (int i = _dataOffset + 258; i < _sectorWordCount; i++)
+ {
+ _sectorData[i] = new DataCell(0, CellType.Gap);
+ }
+ }
+
+ private ushort CalculateChecksum(DataCell[] sectorData, int offset, int length)
+ {
+ //
+ // From the uCode, the Alto's checksum algorithm is:
+ // 1. Load checksum with constant value of 521B (0x151)
+ // 2. For each word in the record, cksum <- word XOR cksum
+ // 3. Profit
+ //
+ ushort checksum = 0x151;
+
+ for (int i = offset; i < offset + length; i++)
+ {
+ // Sanity check that we're checksumming actual data
+ if (sectorData[i].Type != CellType.Data)
+ {
+ throw new InvalidOperationException("Attempt to checksum non-data area of sector.");
+ }
+
+ checksum = (ushort)(checksum ^ sectorData[i].Data);
+ }
+
+ return checksum;
+ }
+
+ private AltoSystem _system;
+
+ //
+ // Current disk position
+ //
+ private int _cylinder;
+ private int _head;
+ private int _sector;
+
+ // offsets in words for start of data in sector
+ private const int _headerOffset = 22;
+ private const int _labelOffset = _headerOffset + 14;
+ private const int _dataOffset = _labelOffset + 20;
+
+ private bool _sectorModified;
+
+ private static int _sectorWordCount = 269 + 22 + 34;
+
+ private DataCell[] _sectorData = new DataCell[_sectorWordCount];
+
+
+ // The pack loaded into the drive
+ DiabloPack _pack;
+ }
+}
diff --git a/Contralto/IO/DiabloPack.cs b/Contralto/IO/DiabloPack.cs
index 2e06379..0438e22 100644
--- a/Contralto/IO/DiabloPack.cs
+++ b/Contralto/IO/DiabloPack.cs
@@ -83,7 +83,7 @@ namespace Contralto.IO
get { return _geometry; }
}
- public void Load(Stream imageStream)
+ public void Load(Stream imageStream, bool reverseByteOrder)
{
for(int cylinder = 0; cylinder < _geometry.Cylinders; cylinder++)
{
@@ -117,6 +117,13 @@ namespace Contralto.IO
throw new InvalidOperationException("Short read while reading sector data.");
}
+ if (reverseByteOrder)
+ {
+ SwapBytes(header);
+ SwapBytes(label);
+ SwapBytes(data);
+ }
+
_sectors[cylinder, track, sector] = new DiabloDiskSector(header, label, data);
}
}
@@ -133,6 +140,16 @@ namespace Contralto.IO
return _sectors[cylinder, track, sector];
}
+ private void SwapBytes(byte[] data)
+ {
+ for(int i=0;i
/// TODO: this is messy; the read and write sides of KDATA are distinct hardware.
/// According to docs, on a Write, eventually it appears on the Read side during an actual write to the disk
- /// but not right away. For now, this never happens (since we don't yet support writing).
+ /// but not right away.
///
public ushort KDATA
{
@@ -52,14 +64,9 @@ namespace Contralto.IO
_syncWordWritten = false;
// "In addition, it causes the head address bit to be loaded from KDATA[13]."
- int newHead = (_kDataWrite & 0x4) >> 2;
+ int newHead = (_kDataWrite & 0x4) >> 2;
- if (newHead != _head)
- {
- // If we switch heads, we need to reload the sector
- _head = newHead;
- LoadSector();
- }
+ SelectedDrive.Head = newHead;
// "0 normally, 1 if the command is to terminate immediately after the correct cylinder
// position is reached (before any data is transferred)."
@@ -76,12 +83,17 @@ namespace Contralto.IO
Log.Write(LogComponent.DiskController, " -Disk Address ({0}) is C/H/S {1}/{2}/{3}, Drive {4} Restore {5}",
Conversion.ToOctal(_kDataWrite),
(_kDataWrite & 0x0ff8) >> 3,
- _head,
+ newHead,
(_kDataWrite & 0xf000) >> 12,
(_kDataWrite & 0x2) >> 1,
(_kDataWrite & 0x1));
- Log.Write(LogComponent.DiskController, " -Selected disk is {0}", _disk);
+ //if ((_kAdr & 0x1) != 0)
+ {
+ //_disk = ((_kDataWrite & 0x2) >> 1);
+ }
+
+ Log.Write(LogComponent.DiskController, " -Selected disk is {0}", _disk);
if ((_kDataWrite & 0x1) != 0)
{
@@ -113,10 +125,11 @@ namespace Contralto.IO
_wdInit = true;
}
- if (_sendAdr)
+
+ if (_sendAdr & (_kDataWrite & 0x2) != 0)
{
// Select disk if _sendAdr is true
- _disk = ((_kDataWrite & 0x2) >> 1) ^ (_kAdr & 0x1);
+ _disk = (_kAdr & 0x1);
if (_disk != 0)
{
@@ -139,50 +152,50 @@ namespace Contralto.IO
public ushort KSTAT
{
get
- {
+ {
// Bits 4-7 of KSTAT are always 1s (it's a shortcut allowing the disk microcode to write
// "-1" to bits 4-7 of the disk status word at 522 without extra code.)
return (ushort)(_kStat | (0x0f00));
}
set
{
- _kStat = value;
+ _kStat = value;
}
}
public ushort RECNO
{
- get { return _recMap[_recNo]; }
+ get { return _recMap[_recNo]; }
}
public bool DataXfer
{
get { return _dataXfer; }
}
-
+
public int Cylinder
{
- get { return _cylinder; }
+ get { return SelectedDrive.Cylinder; }
}
public int SeekCylinder
{
- get { return _destCylinder; }
+ get { return _destCylinder; }
}
public int Head
{
- get { return _head; }
+ get { return SelectedDrive.Head; }
}
public int Sector
{
- get { return _sector; }
+ get { return SelectedDrive.Sector; }
}
public int Drive
{
- get { return 0; }
+ get { return 0; }
}
public double ClocksUntilNextSector
@@ -202,10 +215,8 @@ namespace Contralto.IO
public void Reset()
{
ClearStatus();
- _recNo = 0;
- _cylinder = _destCylinder = 0;
+ _recNo = 0;
_sector = 0;
- _head = 0;
_disk = 0;
_kStat = 0;
_kDataRead = 0;
@@ -219,21 +230,22 @@ namespace Contralto.IO
_wdInit = false;
_syncWordWritten = false;
- _sectorModified = false;
_diskBitCounterEnable = false;
- _sectorWordIndex = 0;
+ _sectorWordIndex = 0;
- InitSector();
+ // Reset drives
+ _drives[0].Reset();
+ _drives[1].Reset();
// Wakeup the sector task first thing
_system.CPU.WakeupTask(CPU.TaskType.DiskSector);
-
+
// Create events to be reused during execution
_sectorEvent = new Event(_sectorDuration, null, SectorCallback);
_wordEvent = new Event(_wordDuration, null, WordCallback);
- _seekEvent = new Event(0, null, SeekCallback);
_seclateEvent = new Event(_seclateDuration, null, SeclateCallback);
+ _seekEvent = new Event(_seekDuration, null, SeekCallback);
// And schedule the first sector pulse.
_system.Scheduler.Schedule(_sectorEvent);
@@ -249,17 +261,10 @@ namespace Contralto.IO
private void SectorCallback(ulong timeNsec, ulong skewNsec, object context)
{
- // Write last sector out if it was modified
- if (_sectorModified)
- {
- CommitSector();
- _sectorModified = false;
- }
-
//
// Next sector; move to next sector and wake up Disk Sector task.
//
- _sector = (_sector + 1) % 12;
+ _sector = (_sector + 1) % 12;
_kStat = (ushort)((_kStat & 0x0fff) | (_sector << 12));
@@ -267,15 +272,15 @@ namespace Contralto.IO
_sectorWordIndex = 0;
_syncWordWritten = false;
- _kDataRead = 0;
+ _kDataRead = 0;
// Load new sector in
- LoadSector();
+ SelectedDrive.Sector = _sector;
// Only wake up if not actively seeking.
if ((_kStat & 0x0040) == 0)
{
- Log.Write(LogType.Verbose, LogComponent.DiskController, "Waking up sector task for C/H/S {0}/{1}/{2}", _cylinder, _head, _sector);
+ Log.Write(LogType.Verbose, LogComponent.DiskController, "Waking up sector task for C/H/S {0}/{1}/{2}", SelectedDrive.Cylinder, SelectedDrive.Head, _sector);
_system.CPU.WakeupTask(CPU.TaskType.DiskSector);
// Reset SECLATE
@@ -295,7 +300,7 @@ namespace Contralto.IO
{
// Schedule next sector pulse
_sectorEvent.TimestampNsec = _sectorDuration - skewNsec;
- _system.Scheduler.Schedule(_sectorEvent);
+ _system.Scheduler.Schedule(_sectorEvent);
}
}
@@ -313,49 +318,19 @@ namespace Contralto.IO
{
// // Schedule next sector pulse immediately
_sectorEvent.TimestampNsec = skewNsec;
- _system.Scheduler.Schedule(_sectorEvent);
- }
- }
-
- private void SeekCallback(ulong timeNsec, ulong skewNsec, object context)
- {
- if (_cylinder < _destCylinder)
- {
- _cylinder++;
- }
- else if (_cylinder > _destCylinder)
- {
- _cylinder--;
- }
-
- Log.Write(LogComponent.DiskController, "Seek progress: cylinder {0} reached.", _cylinder);
-
- // Are we *there* yet?
- if (_cylinder == _destCylinder)
- {
- // clear Seek bit
- _kStat &= 0xffbf;
-
- Log.Write(LogComponent.DiskController, "Seek to {0} completed.", _cylinder);
- }
- else
- {
- // Nope.
- // Schedule next seek step.
- _seekEvent.TimestampNsec = _seekDuration - skewNsec;
- _system.Scheduler.Schedule(_seekEvent);
+ _system.Scheduler.Schedule(_sectorEvent);
}
}
private void SeclateCallback(ulong timeNsec, ulong skewNsec, object context)
{
if (_seclateEnable)
- {
+ {
_seclate = true;
_kStat |= 0x0010; // TODO: move to constant field!
Log.Write(LogComponent.DiskSectorTask, "SECLATE for sector {0}.", _sector);
}
- }
+ }
public void ClearStatus()
{
@@ -388,7 +363,7 @@ namespace Contralto.IO
// "Initiates a disk seek operation. The KDATA register must have been loaded previously,
// and the SENDADR bit of the KCOMM register previously set to 1."
//
-
+
// sanity check: see if SENDADR bit is set, if not we'll signal an error (since I'm trusting that
// the official Xerox uCode is doing the right thing, this will help ferret out emulation issues.
// eventually this can be removed.)
@@ -398,25 +373,24 @@ namespace Contralto.IO
}
Log.Write(LogComponent.DiskController, "STROBE: Seek initialized.");
-
- InitSeek((_kDataWrite & 0x0ff8) >> 3);
+
+ InitSeek((_kDataWrite & 0x0ff8) >> 3);
}
private void InitSeek(int destCylinder)
{
- _destCylinder = destCylinder;
-
// set "seek fail" bit based on selected cylinder (if out of bounds) and do not
// commence a seek if so.
- if (_destCylinder > 202)
+ if (destCylinder > 202)
{
_kStat |= 0x0080;
- Log.Write(LogComponent.DiskController, "Seek failed, specified cylinder {0} is out of range.", _destCylinder);
+ Log.Write(LogComponent.DiskController, "Seek failed, specified cylinder {0} is out of range.", destCylinder);
}
else
{
// Otherwise, start a seek.
+ _destCylinder = destCylinder;
// Clear the fail bit.
_kStat &= 0xff7f;
@@ -425,29 +399,15 @@ namespace Contralto.IO
_kStat |= 0x0040;
// And figure out how long this will take.
- _seekDuration = (ulong)(CalculateSeekTime() / (ulong)(Math.Abs(_destCylinder - _cylinder) + 1));
+ _seekDuration = (ulong)(CalculateSeekTime() / (ulong)(Math.Abs(_destCylinder - SelectedDrive.Cylinder) + 1));
_seekEvent.TimestampNsec = _seekDuration;
_system.Scheduler.Schedule(_seekEvent);
- Log.Write(LogComponent.DiskController, "Seek to {0} from {1} commencing. Will take {2} nsec.", _destCylinder, _cylinder, _seekDuration);
+ Log.Write(LogComponent.DiskController, "Seek to {0} from {1} commencing. Will take {2} nsec.", _destCylinder, SelectedDrive.Cylinder, _seekDuration);
}
}
- private ulong CalculateSeekTime()
- {
- // How many cylinders are we moving?
- int dt = Math.Abs(_destCylinder - _cylinder);
-
- //
- // From the Hardware Manual, pg 43:
- // "Seek time (approx.): 15 + 8.6 * sqrt(dt) (msec)
- //
- double seekTimeMsec = 15.0 + 8.6 * Math.Sqrt(dt);
-
- return (ulong)(seekTimeMsec * Conversion.MsecToNsec);
- }
-
///
/// "Rotates" the emulated disk platter one clock's worth.
///
@@ -478,7 +438,7 @@ namespace Contralto.IO
// and we may not actually end up doing anything with it, but we may
// need it to decide whether to do anything at all.
//
- ushort diskWord = _sectorData[_sectorWordIndex].Data;
+ DataCell diskWord = SelectedDrive.ReadWord(_sectorWordIndex);
bool bWakeup = false;
//
@@ -486,8 +446,8 @@ namespace Contralto.IO
// then we will wake up the word task now.
//
if (!_seclate && !_wdInhib && !_bClkSource)
- {
- bWakeup = true;
+ {
+ bWakeup = true;
}
//
@@ -509,9 +469,9 @@ namespace Contralto.IO
Log.Write(LogType.Warning, LogComponent.DiskController, "--- missed sector word {0}({1}) ---", _sectorWordIndex, _kDataRead);
}
- Log.Write(LogType.Verbose, LogComponent.DiskWordTask, "Sector {0} Word {1} read into KDATA", _sector, Conversion.ToOctal(diskWord));
- _kDataRead = diskWord;
- _debugRead = _sectorData[_sectorWordIndex].Type == CellType.Data;
+ Log.Write(LogType.Verbose, LogComponent.DiskWordTask, "Sector {0} Word {1} read into KDATA", _sector, Conversion.ToOctal(diskWord.Data));
+ _kDataRead = diskWord.Data;
+ _debugRead = diskWord.Type == CellType.Data;
}
else
{
@@ -524,18 +484,10 @@ namespace Contralto.IO
_kDataWriteLatch = false;
}
- if (_syncWordWritten && _sectorWordIndex < _sectorData.Length)
+ if (_syncWordWritten)
{
- if (_sectorData[_sectorWordIndex].Type == CellType.Data)
- {
- _sectorData[_sectorWordIndex].Data = _kDataWrite;
- }
- else
- {
- Log.Write(LogType.Warning, LogComponent.DiskController, "Data written to non-data section (Sector {0} Word {1} Rec {2} Data {3})", _sector, _sectorWordIndex, _recNo, Conversion.ToOctal(_kDataWrite));
- }
-
- _sectorModified = true;
+ // Commit actual data to disk now that the sync word has been laid down
+ SelectedDrive.WriteWord(_sectorWordIndex, _kDataWrite);
}
}
}
@@ -552,8 +504,8 @@ namespace Contralto.IO
// the clock. This occurs late in the cycle so that the NEXT word
// (not the sync word) is actually read. TODO: this should only happen on reads.
//
- if (!IsWrite() && !_wffo && diskWord == 1)
- {
+ if (!IsWrite() && !_wffo && diskWord.Data == 1)
+ {
_diskBitCounterEnable = true;
}
else if (IsWrite() && _wffo && _kDataWrite == 1 && !_syncWordWritten)
@@ -563,7 +515,7 @@ namespace Contralto.IO
// "Adjust" the write index to the start of the data area for the current record.
// This is cheating.
- switch(_recNo)
+ switch (_recNo)
{
case 0:
_sectorWordIndex = _headerOffset;
@@ -583,143 +535,11 @@ namespace Contralto.IO
{
Log.Write(LogType.Verbose, LogComponent.DiskWordTask, "Word task awoken for word {0}.", _sectorWordIndex);
_system.CPU.WakeupTask(TaskType.DiskWord);
- }
+ }
// Last, move to the next word.
_sectorWordIndex++;
-
- }
- private void LoadSector()
- {
- //
- // Pull data off disk and pack it into our faked-up sector.
- // Note that this data is packed in in REVERSE ORDER because that's
- // how it gets written out and it's how the Alto expects it to be read back in.
- //
- DiabloDiskSector sector = _pack.GetSector(_cylinder, _head, _sector);
-
- // Header (2 words data, 1 word cksum)
- for (int i = _headerOffset + 1, j = 1; i < _headerOffset + 3; i++, j--)
- {
- // actual data to be loaded from disk / cksum calculated
- _sectorData[i] = new DataCell(sector.Header[j], CellType.Data);
- }
-
- ushort checksum = CalculateChecksum(_sectorData, _headerOffset + 1, 2);
- _sectorData[_headerOffset + 3].Data = checksum;
- Log.Write(LogType.Verbose, LogComponent.DiskController, "Header checksum for C/H/S {0}/{1}/{2} is {3}", _cylinder, _head, _sector, Conversion.ToOctal(checksum));
-
- // Label (8 words data, 1 word cksum)
- for (int i = _labelOffset + 1, j = 7; i < _labelOffset + 9; i++, j--)
- {
- // actual data to be loaded from disk / cksum calculated
- _sectorData[i] = new DataCell(sector.Label[j], CellType.Data);
- }
-
- checksum = CalculateChecksum(_sectorData, _labelOffset + 1, 8);
- _sectorData[_labelOffset + 9].Data = checksum;
- Log.Write(LogType.Verbose, LogComponent.DiskController, "Label checksum for C/H/S {0}/{1}/{2} is {3}", _cylinder, _head, _sector, Conversion.ToOctal(checksum));
-
- // sector data (256 words data, 1 word cksum)
- for (int i = _dataOffset + 1, j = 255; i < _dataOffset + 257; i++, j--)
- {
- // actual data to be loaded from disk / cksum calculated
- _sectorData[i] = new DataCell(sector.Data[j], CellType.Data);
- }
-
- checksum = CalculateChecksum(_sectorData, _dataOffset + 1, 256);
- _sectorData[_dataOffset + 257].Data = checksum;
- Log.Write(LogType.Verbose, LogComponent.DiskController, "Data checksum for C/H/S {0}/{1}/{2} is {3}", _cylinder, _head, _sector, Conversion.ToOctal(checksum));
-
- }
-
- ///
- /// Commits modified sector data back to the emulated disk.
- /// Intended to be called at the end of the sector / beginning of the next.
- /// TODO: we should modify this so that checksums are persisted, possibly...
- ///
- private void CommitSector()
- {
- DiabloDiskSector sector = _pack.GetSector(_cylinder, _head, _sector);
-
- // Header (2 words data, 1 word cksum)
- for (int i = _headerOffset + 1, j = 1; i < _headerOffset + 3; i++, j--)
- {
- // actual data to be loaded from disk / cksum calculated
- sector.Header[j] = _sectorData[i].Data;
- }
-
- // Label (8 words data, 1 word cksum)
- for (int i = _labelOffset + 1, j = 7; i < _labelOffset + 9; i++, j--)
- {
- // actual data to be loaded from disk / cksum calculated
- sector.Label[j] = _sectorData[i].Data;
- }
-
- // sector data (256 words data, 1 word cksum)
- for (int i = _dataOffset + 1, j = 255; i < _dataOffset + 257; i++, j--)
- {
- // actual data to be loaded from disk / cksum calculated
- sector.Data[j] = _sectorData[i].Data;
- }
- }
-
- private void InitSector()
- {
- // Fill in sector with default data (basically, fill in non-data areas).
-
- //
- // header delay, 22 words
- for (int i=0; i < _headerOffset; i++)
- {
- _sectorData[i] = new DataCell(0, CellType.Gap);
- }
-
- _sectorData[_headerOffset] = new DataCell(1, CellType.Sync);
- // inter-reccord delay between header & label (10 words)
- for (int i = _headerOffset + 4; i < _labelOffset; i++)
- {
- _sectorData[i] = new DataCell(0, CellType.Gap);
- }
-
- _sectorData[_labelOffset] = new DataCell(1, CellType.Sync);
- // inter-reccord delay between label & data (10 words)
- for (int i = _labelOffset + 10; i < _dataOffset; i++)
- {
- _sectorData[i] = new DataCell(0, CellType.Gap);
- }
-
- _sectorData[_dataOffset] = new DataCell(1, CellType.Sync);
- // read-postamble
- for (int i = _dataOffset + 258; i < _sectorWordCount;i++)
- {
- _sectorData[i] = new DataCell(0, CellType.Gap);
- }
- }
-
- private ushort CalculateChecksum(DataCell[] sectorData, int offset, int length)
- {
- //
- // From the uCode, the Alto's checksum algorithm is:
- // 1. Load checksum with constant value of 521B (0x151)
- // 2. For each word in the record, cksum <- word XOR cksum
- // 3. Profit
- //
- ushort checksum = 0x151;
-
- for(int i = offset; i < offset + length;i++)
- {
- // Sanity check that we're checksumming actual data
- if (sectorData[i].Type != CellType.Data)
- {
- throw new InvalidOperationException("Attempt to checksum non-data area of sector.");
- }
-
- checksum = (ushort)(checksum ^ sectorData[i].Data);
- }
-
- return checksum;
}
private bool IsWrite()
@@ -727,6 +547,55 @@ namespace Contralto.IO
return ((_kAdr & 0x00c0) >> 6) == 2 || ((_kAdr & 0x00c0) >> 6) == 3;
}
+ private void SeekCallback(ulong timeNsec, ulong skewNsec, object context)
+ {
+ if (SelectedDrive.Cylinder < _destCylinder)
+ {
+ SelectedDrive.Cylinder++;
+ }
+ else if (SelectedDrive.Cylinder > _destCylinder)
+ {
+ SelectedDrive.Cylinder--;
+ }
+
+ Log.Write(LogComponent.DiskController, "Seek progress: cylinder {0} reached.", SelectedDrive.Cylinder);
+
+ // Are we *there* yet?
+ if (SelectedDrive.Cylinder == _destCylinder)
+ {
+ // clear Seek bit
+ _kStat &= 0xffbf;
+
+ Log.Write(LogComponent.DiskController, "Seek to {0} completed.", SelectedDrive.Cylinder);
+ }
+ else
+ {
+ // Nope.
+ // Schedule next seek step.
+ _seekEvent.TimestampNsec = _seekDuration - skewNsec;
+ _system.Scheduler.Schedule(_seekEvent);
+ }
+ }
+
+ private ulong CalculateSeekTime()
+ {
+ // How many cylinders are we moving?
+ int dt = Math.Abs(_destCylinder - SelectedDrive.Cylinder);
+
+ //
+ // From the Hardware Manual, pg 43:
+ // "Seek time (approx.): 15 + 8.6 * sqrt(dt) (msec)
+ //
+ double seekTimeMsec = 15.0 + 8.6 * Math.Sqrt(dt);
+
+ return (ulong)(seekTimeMsec * Conversion.MsecToNsec);
+ }
+
+ private Diablo30Drive SelectedDrive
+ {
+ get { return _drives[_disk]; }
+ }
+
private ushort _kDataRead;
private ushort _kDataWrite;
private bool _kDataWriteLatch;
@@ -750,12 +619,16 @@ namespace Contralto.IO
// Transfer bit
private bool _dataXfer;
- // Current disk position
- private int _cylinder;
- private int _destCylinder;
- private int _head;
+ // Current sector
private int _sector;
+ //
+ // Seek state
+ //
+ private int _destCylinder;
+ private ulong _seekDuration;
+ private Event _seekEvent;
+
// Selected disk
private int _disk;
@@ -766,7 +639,6 @@ namespace Contralto.IO
private bool _wdInit;
private bool _syncWordWritten;
- private bool _sectorModified;
// Sector timing. Based on table on pg. 43 of the Alto Hardware Manual
@@ -785,7 +657,7 @@ namespace Contralto.IO
private Event _sectorEvent;
private Event _wordEvent;
-
+
// offsets in words for start of data in sector
private const int _headerOffset = 22;
@@ -797,44 +669,10 @@ namespace Contralto.IO
private static ulong _seclateDuration = (ulong)(85.0 * Conversion.UsecToNsec * _scale);
private bool _seclateEnable;
private bool _seclate;
- private Event _seclateEvent;
+ private Event _seclateEvent;
- // Cylinder seek time (in nsec) Again, see the manual.
- // Timing varies based on how many cylinders are being traveled during a seek; see
- // CalculateSeekTime() for more.
- private ulong _seekDuration;
- private Event _seekEvent;
-
- // The data for the current sector
- private enum CellType
- {
- Data,
- Gap,
- Sync,
- }
-
- private struct DataCell
- {
- public DataCell(ushort data, CellType type)
- {
- Data = data;
- Type = type;
- }
-
- public ushort Data;
- public CellType Type;
-
- public override string ToString()
- {
- return String.Format("{0} {1}", Data, Type);
- }
- }
-
- private DataCell[] _sectorData = new DataCell[_sectorWordCount];
-
-
- // The pack loaded into the drive
- DiabloPack _pack;
+ // Attached drives
+ private Diablo30Drive[] _drives;
private AltoSystem _system;
diff --git a/Contralto/Logging/Log.cs b/Contralto/Logging/Log.cs
index 721d9a6..ddfd896 100644
--- a/Contralto/Logging/Log.cs
+++ b/Contralto/Logging/Log.cs
@@ -44,7 +44,7 @@ namespace Contralto.Logging
static Log()
{
// TODO: make configurable
- _components = LogComponent.None; // LogComponent.DiskController | LogComponent.DiskSectorTask;
+ _components = LogComponent.DiskController | LogComponent.DiskSectorTask;
_type = LogType.Normal | LogType.Warning | LogType.Error;
}
diff --git a/Contralto/Program.cs b/Contralto/Program.cs
index d79f369..1e0eb53 100644
--- a/Contralto/Program.cs
+++ b/Contralto/Program.cs
@@ -6,14 +6,23 @@ namespace Contralto
{
static void Main(string[] args)
{
- AltoSystem system = new AltoSystem();
+ AltoSystem system = new AltoSystem();
- // for now everything is driven through the debugger
+ // for now everything is driven through the debugger
+
+ AltoWindow mainWindow = new AltoWindow();
+
+ mainWindow.AttachSystem(system);
+
+ /*
Debugger d = new Debugger(system);
system.AttachDisplay(d);
d.LoadSourceCode(MicrocodeBank.ROM0, "Disassembly\\altoIIcode3.mu");
d.LoadSourceCode(MicrocodeBank.ROM1, "Disassembly\\MesaROM.mu");
d.ShowDialog();
+ */
+ mainWindow.ShowDialog();
+
}
}