diff --git a/Contralto.sln b/Contralto.sln
index d5d444c..06a43cd 100644
--- a/Contralto.sln
+++ b/Contralto.sln
@@ -8,6 +8,9 @@ EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "ContraltoSetup", "ContraltoSetup\ContraltoSetup.wixproj", "{47BBC195-80C5-43F3-B691-7D27B0803B84}"
EndProject
Global
+ GlobalSection(Performance) = preSolution
+ HasPerformanceSessions = true
+ EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
diff --git a/Contralto/Disk/allgames.dsk b/Contralto/Disk/allgames.dsk
index da8bc5c..16f95c5 100644
Binary files a/Contralto/Disk/allgames.dsk and b/Contralto/Disk/allgames.dsk differ
diff --git a/Contralto/IO/DiabloDrive.cs b/Contralto/IO/DiabloDrive.cs
index daccb5b..32147c5 100644
--- a/Contralto/IO/DiabloDrive.cs
+++ b/Contralto/IO/DiabloDrive.cs
@@ -59,6 +59,14 @@ namespace Contralto.IO
LoadSector();
}
+ // Offsets in words for start of data in sector
+ public static readonly int HeaderOffset = 44;
+ public static readonly int LabelOffset = HeaderOffset + 14;
+ public static readonly int DataOffset = LabelOffset + 20;
+
+ // Total "words" (really timeslices) per sector.
+ public static readonly int SectorWordCount = 269 + HeaderOffset + 34;
+
public void LoadPack(DiabloPack pack)
{
_pack = pack;
@@ -189,36 +197,36 @@ namespace Contralto.IO
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--)
+ 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;
+ 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--)
+ 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;
+ 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--)
+ 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;
+ 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));
}
@@ -237,21 +245,21 @@ namespace Contralto.IO
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--)
+ 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--)
+ 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--)
+ 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;
@@ -264,28 +272,28 @@ namespace Contralto.IO
//
// header delay, 22 words
- for (int i = 0; i < _headerOffset; i++)
+ for (int i = 0; i < HeaderOffset; i++)
{
_sectorData[i] = new DataCell(0, CellType.Gap);
}
- _sectorData[_headerOffset] = new DataCell(1, CellType.Sync);
+ _sectorData[HeaderOffset] = new DataCell(1, CellType.Sync);
// inter-reccord delay between header & label (10 words)
- for (int i = _headerOffset + 4; i < _labelOffset; i++)
+ for (int i = HeaderOffset + 4; i < LabelOffset; i++)
{
_sectorData[i] = new DataCell(0, CellType.Gap);
}
- _sectorData[_labelOffset] = new DataCell(1, CellType.Sync);
+ _sectorData[LabelOffset] = new DataCell(1, CellType.Sync);
// inter-reccord delay between label & data (10 words)
- for (int i = _labelOffset + 10; i < _dataOffset; i++)
+ for (int i = LabelOffset + 10; i < DataOffset; i++)
{
_sectorData[i] = new DataCell(0, CellType.Gap);
}
- _sectorData[_dataOffset] = new DataCell(1, CellType.Sync);
+ _sectorData[DataOffset] = new DataCell(1, CellType.Sync);
// read-postamble
- for (int i = _dataOffset + 258; i < _sectorWordCount; i++)
+ for (int i = DataOffset + 258; i < SectorWordCount; i++)
{
_sectorData[i] = new DataCell(0, CellType.Gap);
}
@@ -324,16 +332,9 @@ namespace Contralto.IO
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 bool _sectorModified;
-
- private static int _sectorWordCount = 269 + 22 + 34;
-
- private DataCell[] _sectorData = new DataCell[_sectorWordCount];
+ private DataCell[] _sectorData = new DataCell[SectorWordCount];
// The pack loaded into the drive
diff --git a/Contralto/IO/DiskController.cs b/Contralto/IO/DiskController.cs
index 720cb93..14a4a2d 100644
--- a/Contralto/IO/DiskController.cs
+++ b/Contralto/IO/DiskController.cs
@@ -322,7 +322,7 @@ namespace Contralto.IO
SpinDisk();
// Schedule next word if this wasn't the last word this sector.
- if (_sectorWordIndex < _sectorWordCount)
+ if (_sectorWordIndex < DiabloDrive.SectorWordCount)
{
_wordEvent.TimestampNsec = _wordDuration - skewNsec;
_system.Scheduler.Schedule(_wordEvent);
@@ -341,14 +341,16 @@ namespace Contralto.IO
{
_seclate = true;
_kStat |= SECLATE;
+ Console.WriteLine("SECLATE for sector {0}.", _sector);
Log.Write(LogComponent.DiskSectorTask, "SECLATE for sector {0}.", _sector);
}
}
public void ClearStatus()
{
- // "...clears KSTAT[13]." (chksum error flag)
+ // "Causes all error latches in disk controller hardware to reset, clears clears KSTAT[13]." (chksum error flag)
_kStat &= 0xff4b;
+ _seclate = false;
}
public void IncrementRecord()
@@ -415,7 +417,7 @@ namespace Contralto.IO
_seeking = true;
// And figure out how long this will take.
- _seekDuration = 0; // (ulong)(CalculateSeekTime() / (ulong)(Math.Abs(_destCylinder - SelectedDrive.Cylinder) + 1));
+ _seekDuration = (ulong)(CalculateSeekTime() / (ulong)(Math.Abs(_destCylinder - SelectedDrive.Cylinder) + 1));
_seekEvent.TimestampNsec = _seekDuration;
_system.Scheduler.Schedule(_seekEvent);
@@ -488,6 +490,7 @@ namespace Contralto.IO
// microcode via KDATA, log it.
if (_debugRead)
{
+ Console.WriteLine("--- missed sector word {0}({1}) ---", _sectorWordIndex, _kDataRead);
Log.Write(LogType.Warning, LogComponent.DiskController, "--- missed sector word {0}({1}) ---", _sectorWordIndex, _kDataRead);
}
@@ -540,15 +543,15 @@ namespace Contralto.IO
switch (_recNo)
{
case 0:
- _sectorWordIndex = _headerOffset;
+ _sectorWordIndex = DiabloDrive.HeaderOffset;
break;
case 1:
- _sectorWordIndex = _labelOffset;
+ _sectorWordIndex = DiabloDrive.LabelOffset;
break;
case 2:
- _sectorWordIndex = _dataOffset;
+ _sectorWordIndex = DiabloDrive.DataOffset;
break;
}
}
@@ -674,24 +677,21 @@ namespace Contralto.IO
// $MIR0BL $177775; DISK INTERRECORD PREAMBLE IS 3 WORDS <<-- writing
// $MRPAL $177775; DISK READ POSTAMBLE LENGTH IS 3 WORDS
// $MWPAL $177773; DISK WRITE POSTAMBLE LENGTH IS 5 WORDS <<-- writing, clearly.
- private static double _scale = 1.75;
- private static ulong _sectorDuration = (ulong)((40.0 / 12.0) * Conversion.MsecToNsec * _scale); // time in nsec for one sector
- private static int _sectorWordCount = 269 + 22 + 34; // Based on : 269 data words (+ cksums) / sector, + X words for delay / preamble / sync
- private static ulong _wordDuration = (ulong)((_sectorDuration / (ulong)(_sectorWordCount)) * _scale); // time in nsec for one word
+ private static double _scale = 1.0;
+ private static ulong _sectorDuration = (ulong)((40.0 / 12.0) * Conversion.MsecToNsec * _scale); // time in nsec for one sector
+ private static ulong _wordDuration = (ulong)((_sectorDuration / (ulong)(DiabloDrive.SectorWordCount)) * _scale); // time in nsec for one word
private int _sectorWordIndex; // current word being read
private Event _sectorEvent;
- private Event _wordEvent;
-
-
- // 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 Event _wordEvent;
+ //
// SECLATE data.
- // 8.5uS for seclate delay (approx. 50 clocks)
- private static ulong _seclateDuration = (ulong)(20.0 * Conversion.UsecToNsec * _scale);
+ // 86uS for SECLATE delay (approx. 505 clocks)
+ // This is based on the R/C network connected to the 74123 (monostable vibrator) at 31 on the disk control board.
+ // R = 30K, C = .01uF (10000pF). T(nsec) = .28 * R * C * (1 + (0.7/R)) => .28 * 30 * 10000 * (1 + (0.7/30)) = 85959.9999nsec; 86usec.
+ //
+ private static ulong _seclateDuration = (ulong)(86.0 * Conversion.UsecToNsec * _scale);
private bool _seclateEnable;
private bool _seclate;
private Event _seclateEvent;
diff --git a/Contralto/IO/EthernetController.cs b/Contralto/IO/EthernetController.cs
index ea1634f..34e7618 100644
--- a/Contralto/IO/EthernetController.cs
+++ b/Contralto/IO/EthernetController.cs
@@ -413,7 +413,7 @@ namespace Contralto.IO
//
// Read the packet length (in words) (first word of the packet as provided by the sending emulator). Convert to bytes.
//
- _incomingPacketLength = ((_incomingPacket.ReadByte()) | (_incomingPacket.ReadByte() << 8)) * 2;
+ _incomingPacketLength = ((_incomingPacket.ReadByte() << 8) | (_incomingPacket.ReadByte())) * 2;
// Add one word to the count for the checksum.
// NOTE: This is not provided by the sending emulator and is not computed here either.
@@ -427,8 +427,8 @@ namespace Contralto.IO
if (_incomingPacketLength > _incomingPacket.Length ||
(_incomingPacketLength % 2) != 0)
{
- throw new InvalidOperationException(
- String.Format("Invalid 3mbit packet length header ({0} vs {1}.", _incomingPacketLength, _incomingPacket.Length));
+ throw new InvalidOperationException(
+ String.Format("Invalid 3mbit packet length header ({0} vs {1}.", _incomingPacketLength, _incomingPacket.Length));
}
Log.Write(LogComponent.EthernetPacket, "Accepting incoming packet (length {0}).", _incomingPacketLength);
@@ -454,7 +454,7 @@ namespace Contralto.IO
if (_incomingPacketLength >= 2)
{
// Stuff 1 word into the FIFO, if we run out of data to send then we clear _iBusy further down.
- ushort nextWord = (ushort)((_incomingPacket.ReadByte()) | (_incomingPacket.ReadByte() << 8));
+ ushort nextWord = (ushort)((_incomingPacket.ReadByte() << 8) | (_incomingPacket.ReadByte()));
_fifo.Enqueue(nextWord);
_incomingPacketLength -= 2;
diff --git a/Contralto/IO/HostEthernetEncapsulation.cs b/Contralto/IO/HostEthernetEncapsulation.cs
index 3ff7821..a9785a1 100644
--- a/Contralto/IO/HostEthernetEncapsulation.cs
+++ b/Contralto/IO/HostEthernetEncapsulation.cs
@@ -108,25 +108,25 @@ namespace Contralto.IO
// First two bytes include the length of the 3mbit packet; since 10mbit packets have a minimum length of 46
// bytes, and 3mbit packets have no minimum length this is necessary so the receiver can pull out the
// correct amount of data.
- //
- packetBytes[0] = (byte)(length);
- packetBytes[1] = (byte)((length) >> 8);
+ //
+ packetBytes[0] = (byte)((length) >> 8);
+ packetBytes[1] = (byte)(length);
//
// Do this annoying dance to stuff the ushorts into bytes because this is C#.
//
for (int i = 0; i < length; i++)
- {
- packetBytes[i * 2 + 2] = (byte)(packet[i]);
- packetBytes[i * 2 + 3] = (byte)(packet[i] >> 8);
+ {
+ packetBytes[i * 2 + 2] = (byte)(packet[i] >> 8);
+ packetBytes[i * 2 + 3] = (byte)(packet[i]);
}
//
// Grab the source and destination host addresses from the packet we're sending
// and build 10mbit versions.
//
- byte destinationHost = packetBytes[3];
- byte sourceHost = packetBytes[2];
+ byte destinationHost = packetBytes[2];
+ byte sourceHost = packetBytes[3];
Log.Write(LogComponent.HostNetworkInterface, "Sending packet; source {0} destination {1}, length {2} words.",
Conversion.ToOctal(sourceHost),
@@ -162,9 +162,7 @@ namespace Contralto.IO
//
// Filter out packets intended for the emulator, forward them on, drop everything else.
//
- if ((int)p.Ethernet.EtherType == _3mbitFrameType && // encapsulated 3mbit frames
- ((p.Ethernet.Destination.ToValue() & 0xffffffffff00) == _10mbitMACPrefix || // addressed to any emulator OR
- p.Ethernet.Destination.ToValue() == _10mbitBroadcast) && // broadcast
+ if ((int)p.Ethernet.EtherType == _3mbitFrameType && // encapsulated 3mbit frames
(p.Ethernet.Source.ToValue() != (UInt48)(_10mbitMACPrefix | Configuration.HostAddress))) // and not sent by this emulator
{
Log.Write(LogComponent.HostNetworkInterface, "Received encapsulated 3mbit packet.");
diff --git a/Contralto/IO/UDPEncapsulation.cs b/Contralto/IO/UDPEncapsulation.cs
index 1e6b426..9cd6956 100644
--- a/Contralto/IO/UDPEncapsulation.cs
+++ b/Contralto/IO/UDPEncapsulation.cs
@@ -120,22 +120,22 @@ namespace Contralto.IO
// First two bytes include the length of the 3mbit packet; since 10mbit packets have a minimum length of 46
// bytes, and 3mbit packets have no minimum length this is necessary so the receiver can pull out the
// correct amount of data.
- //
- packetBytes[0] = (byte)(length);
- packetBytes[1] = (byte)((length) >> 8);
+ //
+ packetBytes[0] = (byte)((length) >> 8);
+ packetBytes[1] = (byte)(length);
//
// Do this annoying dance to stuff the ushorts into bytes because this is C#.
//
for (int i = 0; i < length; i++)
- {
- packetBytes[i * 2 + 2] = (byte)(packet[i]);
- packetBytes[i * 2 + 3] = (byte)(packet[i] >> 8);
+ {
+ packetBytes[i * 2 + 2] = (byte)(packet[i] >> 8);
+ packetBytes[i * 2 + 3] = (byte)(packet[i]);
}
Log.Write(LogType.Verbose, LogComponent.HostNetworkInterface, "Sending packet via UDP; source {0} destination {1}, length {2} words.",
- Conversion.ToOctal(packetBytes[2]),
Conversion.ToOctal(packetBytes[3]),
+ Conversion.ToOctal(packetBytes[2]),
length);
_udpClient.Send(packetBytes, packetBytes.Length, _broadcastEndpoint);
diff --git a/Contralto/Scheduler.cs b/Contralto/Scheduler.cs
index c18e2c4..c62a5b0 100644
--- a/Contralto/Scheduler.cs
+++ b/Contralto/Scheduler.cs
@@ -143,23 +143,17 @@ namespace Contralto
{
get
{
- if (_queue.Count > 0)
- {
- return _queue.First.Value;
- }
- else
- {
- return null;
- }
+ return _top;
}
}
public void Push(Event e)
{
// 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)
+ if (_queue.Count == 0 || _top.TimestampNsec >= e.TimestampNsec)
{
_queue.AddFirst(e);
+ _top = e;
return;
}
@@ -189,20 +183,29 @@ namespace Contralto
public Event Pop()
{
- Event e = _queue.First.Value;
+ Event e = _top;
_queue.RemoveFirst();
+ _top = _queue.First.Value;
+
return e;
}
public void Remove(Event e)
{
- _queue.Remove(e);
+ _queue.Remove(e);
+ _top = _queue.First.Value;
+
}
///
/// TODO: provide more optimal data structure here once profiling can be done.
///
private LinkedList _queue;
+
+ ///
+ /// The Top of the queue (null if queue is empty).
+ ///
+ private Event _top;
}
}