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:
parent
2c99250ded
commit
bfda781576
@ -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.
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.");
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user