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