1
0
mirror of https://github.com/livingcomputermuseum/ContrAlto.git synced 2026-01-16 16:19:33 +00:00

Fixed disk controller logic (really disk timing logic) so that disk can run at 100% speed (rather than 50% speed). Changed endian-ness for ethernet packet words to match 3mbit bridge. (Similar changes also made to IFS). Removed some dead code.

This commit is contained in:
Josh Dersch 2016-03-23 16:48:56 -07:00
parent 2c99250ded
commit bfda781576
8 changed files with 85 additions and 80 deletions

View File

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

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
}
/// <summary>
/// TODO: provide more optimal data structure here once profiling can be done.
/// </summary>
private LinkedList<Event> _queue;
/// <summary>
/// The Top of the queue (null if queue is empty).
/// </summary>
private Event _top;
}
}