diff --git a/Contralto/CPU/Tasks/DiskTask.cs b/Contralto/CPU/Tasks/DiskTask.cs index ecb2b61..d5c4f53 100644 --- a/Contralto/CPU/Tasks/DiskTask.cs +++ b/Contralto/CPU/Tasks/DiskTask.cs @@ -22,9 +22,7 @@ namespace Contralto.CPU } protected override InstructionCompletion ExecuteInstruction(MicroInstruction instruction) - { - // Log.Write(LogComponent.Debug, "{0}: {1}", Conversion.ToOctal(_mpc), UCodeDisassembler.DisassembleInstruction(instruction, _taskType)); - + { InstructionCompletion completion = base.ExecuteInstruction(instruction); // Deal with SECLATE semantics: If the Disk Sector task wakes up and runs before @@ -89,7 +87,7 @@ namespace Contralto.CPU case DiskF1.LoadKSTAT: // "KSTAT[12-15] are loaded from BUS[12-15]. (Actually BUS[13] is ORed onto - // KSTAT[13].)" + // KSTAT[13].)" // From the schematic (and ucode source, based on the values it actually uses for BUS[13]), BUS[13] // is also inverted. So there's that, too. @@ -170,11 +168,7 @@ namespace Contralto.CPU if (!_diskController.FatalError) { _nextModifier |= 0x1; - } - else - { - Console.WriteLine("fatal disk error on disk {0}", _diskController.Drive); - } + } break; case DiskF2.STROBON: @@ -190,8 +184,7 @@ namespace Contralto.CPU // "NEXT <- NEXT OR (IF disk not ready to accept command THEN 1 ELSE 0) _nextModifier |= GetInitModifier(instruction); if (!_diskController.Ready) - { - Console.WriteLine("disk {0} not ready", _diskController.Drive); + { _nextModifier |= 0x1; } break; diff --git a/Contralto/CPU/Tasks/DisplayHorizontalTask.cs b/Contralto/CPU/Tasks/DisplayHorizontalTask.cs index bc39f0a..4a7d16e 100644 --- a/Contralto/CPU/Tasks/DisplayHorizontalTask.cs +++ b/Contralto/CPU/Tasks/DisplayHorizontalTask.cs @@ -36,10 +36,10 @@ namespace Contralto.CPU break; case DisplayHorizontalF2.SETMODE: - _displayController.SETMODE(_busData); - // "If bit 0 = 1, the bit clock rate is set to 100ns period (at the start of the next scan line), // and a 1 is merged into NEXT[9]." + _displayController.SETMODE(_busData); + if ((_busData & 0x8000) != 0) { _nextModifier |= 1; diff --git a/Contralto/CPU/Tasks/EmulatorTask.cs b/Contralto/CPU/Tasks/EmulatorTask.cs index b2bc3d6..3444e1a 100644 --- a/Contralto/CPU/Tasks/EmulatorTask.cs +++ b/Contralto/CPU/Tasks/EmulatorTask.cs @@ -18,6 +18,12 @@ namespace Contralto.CPU _wakeup = true; } + public override void Reset() + { + base.Reset(); + _wakeup = true; + } + public override void BlockTask() { throw new InvalidOperationException("The emulator task cannot be blocked."); @@ -176,14 +182,11 @@ namespace Contralto.CPU // "...causes (IR[3-4] XOR 3) to be used as the low-order two bits of the RSELECT field. // This address the accumulators from the destination field of the instruction. The selected // register may be loaded or read." - _rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x1800) >> 11) ^ 3); - break; - case EmulatorF2.LoadDNS: // // "...DNS also addresses R from (3-IR[3 - 4])..." // - _rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x1800) >> 11) ^ 3); + _rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x1800) >> 11) ^ 3); break; } @@ -195,12 +198,11 @@ namespace Contralto.CPU switch (ef2) { case EmulatorF2.LoadIR: - // based on block diagram, this always comes from the bus + // Load IR from the bus _cpu._ir = _busData; // "IR<- also merges bus bits 0, 5, 6 and 7 into NEXT[6-9] which does a first level - // instruction dispatch." - // Assuming for now this is an OR operation like everything else that modifies NEXT. + // instruction dispatch." _nextModifier = (ushort)(((_busData & 0x8000) >> 12) | ((_busData & 0x0700) >> 8)); // "IR<- clears SKIP" diff --git a/Contralto/CPU/Tasks/EthernetTask.cs b/Contralto/CPU/Tasks/EthernetTask.cs index 6c0cd55..257763c 100644 --- a/Contralto/CPU/Tasks/EthernetTask.cs +++ b/Contralto/CPU/Tasks/EthernetTask.cs @@ -7,7 +7,7 @@ namespace Contralto.CPU public partial class AltoCPU { /// - /// EthernetTask implements Ethernet-specific task function + /// EthernetTask implements Ethernet-specific task functions /// private sealed class EthernetTask : Task { diff --git a/Contralto/CPU/Tasks/MemoryRefreshTask.cs b/Contralto/CPU/Tasks/MemoryRefreshTask.cs index 61e9900..406b7d1 100644 --- a/Contralto/CPU/Tasks/MemoryRefreshTask.cs +++ b/Contralto/CPU/Tasks/MemoryRefreshTask.cs @@ -3,7 +3,7 @@ public partial class AltoCPU { /// - /// DisplayWordTask provides functionality for the Memory Refresh task + /// DisplayWordTask provides functionality for the Memory Refresh task. /// private sealed class MemoryRefreshTask : Task { @@ -12,18 +12,16 @@ _taskType = TaskType.MemoryRefresh; _wakeup = false; - } - - /* - protected override InstructionCompletion ExecuteInstruction(MicroInstruction instruction) - { - // - // Based on readings of the MRT microcode, the MRT keeps its wakeup - // until it executes a BLOCK. - // "; This version assumes MRTACT is cleared by BLOCK, not MAR<- R37" - // - return base.ExecuteInstruction(instruction); - }*/ + } + + // + // MRT has no special functions or special behavior, but here's a note regarding the MRT + // wakeup behavior, for future reference: + // + // Based on readings of the MRT microcode, the MRT keeps its wakeup + // until it executes a BLOCK. (i.e. no special wakeup handling at all.) + // "; This version assumes MRTACT is cleared by BLOCK, not MAR<- R37" + // } } } diff --git a/Contralto/CPU/Tasks/Task.cs b/Contralto/CPU/Tasks/Task.cs index de3962f..65dd5b0 100644 --- a/Contralto/CPU/Tasks/Task.cs +++ b/Contralto/CPU/Tasks/Task.cs @@ -14,10 +14,11 @@ namespace Contralto.CPU MemoryWait, } - // Task: - // Base task class: provides implementation for non-task-specific microcode execution and - // state. Task subclasses implement and execute Task-specific behavior and are called into - // by the base class as necessary. + /// + /// Base task class: provides implementation for non-task-specific microcode execution and + /// state. Task subclasses implement and execute Task-specific behavior and are called into + /// by the base class as necessary. + /// public abstract class Task { public Task(AltoCPU cpu) @@ -25,9 +26,7 @@ namespace Contralto.CPU _wakeup = false; _mpc = 0xffff; // invalid, for sanity checking _taskType = TaskType.Invalid; - _cpu = cpu; - - _block = false; + _cpu = cpu; } public int Priority @@ -61,15 +60,6 @@ namespace Contralto.CPU set { _firstInstructionAfterSwitch = value; } } - /// - /// Indicates whether the current uInstruction asserts BLOCK. - /// Used by hardware for various tasks. - /// - public bool BLOCK - { - get { return _block; } - } - public virtual void Reset() { // From The Alto Hardware Manual (section 2, "Initialization"): @@ -82,6 +72,8 @@ namespace Contralto.CPU _swMode = false; _wrtRam = false; + _wakeup = false; + _skip = 0; } public virtual void SoftReset() @@ -104,11 +96,7 @@ namespace Contralto.CPU public InstructionCompletion ExecuteNext() { - MicroInstruction instruction = UCodeMemory.GetInstruction(_mpc, _taskType); - - // Grab BLOCK bit so that other tasks / hardware can look at it - _block = instruction.F1 == SpecialFunction1.Block; - + MicroInstruction instruction = UCodeMemory.GetInstruction(_mpc, _taskType); return ExecuteInstruction(instruction); } @@ -309,7 +297,7 @@ namespace Contralto.CPU // It also doensn't appear to affect the execution of the standard Alto uCode in any significant // way, but is included here for correctness. // - //if (!_firstInstructionAfterSwitch) + if (!_firstInstructionAfterSwitch) { // Yield to other more important tasks completion = InstructionCompletion.TaskSwitch; @@ -572,8 +560,6 @@ namespace Contralto.CPU protected bool _wakeup; protected bool _firstInstructionAfterSwitch; - protected bool _block; - // Emulator Task-specific data. This is placed here because it is used by the ALU and it's easier to reference in the // base class even if it does break encapsulation. See notes in the EmulatorTask class for meaning. protected int _skip; diff --git a/Contralto/Disk/allgames.dsk b/Contralto/Disk/allgames.dsk index 16f95c5..d25af00 100644 Binary files a/Contralto/Disk/allgames.dsk and b/Contralto/Disk/allgames.dsk differ diff --git a/Contralto/Disk/diag.dsk b/Contralto/Disk/diag.dsk index f6f9374..d79a068 100644 Binary files a/Contralto/Disk/diag.dsk and b/Contralto/Disk/diag.dsk differ diff --git a/Contralto/Disk/st76boot.dsk b/Contralto/Disk/st76boot.dsk index 4580fd7..2add0c3 100644 Binary files a/Contralto/Disk/st76boot.dsk and b/Contralto/Disk/st76boot.dsk differ diff --git a/Contralto/IO/DiskController.cs b/Contralto/IO/DiskController.cs index 14a4a2d..96aea6f 100644 --- a/Contralto/IO/DiskController.cs +++ b/Contralto/IO/DiskController.cs @@ -55,13 +55,25 @@ namespace Contralto.IO // position is reached (before any data is transferred)." _dataXfer = (_kAdr & 0x2) != 0x2; + // + // Select disk from bit 14 of KDATA. + // The HW reference claims that the drive is selected by bit 14 of KDATA XOR'd with bit 15 + // of KADR but I can find no evidence in the schematics that this is actually so. + // Page 18 of the controller schematic ("DISK ADDRESSING") shows that the current DATA(14) (KDATA bit 14) + // value is gated into the DISK select lines (running to the drive) whenever a KADR<- F1 is executed. + // It is possible that the HW ref is telling the truth but the XORing is done by the Sector Task uCode + // and not the hardware, but where this is actually occurring is not obvious. + // At any rate: The below behavior appears to work correctly, so I'm sticking with it. + // + _disk = ((_kDataWrite & 0x2) >> 1); + Log.Write(LogComponent.DiskController, "KADR set to {0} (Header {1}, Label {2}, Data {3}, Xfer {4}, Drive {5})", Conversion.ToOctal(_kAdr), Conversion.ToOctal((_kAdr & 0xc0) >> 6), Conversion.ToOctal((_kAdr & 0x30) >> 4), Conversion.ToOctal((_kAdr & 0xc) >> 2), _dataXfer, - _kAdr & 0x1); + _disk); Log.Write(LogComponent.DiskController, " -Disk Address ({0}) is C/H/S {1}/{2}/{3}, Drive {4} Restore {5}", Conversion.ToOctal(_kDataWrite), @@ -77,7 +89,7 @@ namespace Contralto.IO { // Restore operation to cyl. 0: InitSeek(0); - } + } } } @@ -106,19 +118,7 @@ namespace Contralto.IO if (_sendAdr & (_kDataWrite & 0x2) != 0) { - // Select disk if _sendAdr is true - _disk = (_kAdr & 0x1); - _seeking = false; - - // Clear the NOTREADY flag depending on whether the drive is loaded or not - if (_drives[_disk].IsLoaded) - { - _kStat &= (ushort)~NOTREADY; - } - else - { - _kStat |= NOTREADY; - } + _seeking = false; } } @@ -214,7 +214,8 @@ namespace Contralto.IO // return (_kStat & SECLATE) != 0 || (_kStat & SEEKFAIL) != 0 || - (_kStat & NOTREADY) != 0; + (_kStat & NOTREADY) != 0 || + (!Ready); } } @@ -279,6 +280,16 @@ namespace Contralto.IO _kStat = (ushort)((_kStat & 0x0fff) | (_sector << 12)); + // Clear the NOTREADY flag depending on whether the selected drive is loaded or not + if (_drives[_disk].IsLoaded) + { + _kStat &= (ushort)~NOTREADY; + } + else + { + _kStat |= NOTREADY; + } + // Reset internal state machine for sector data _sectorWordIndex = 0; _syncWordWritten = false; @@ -397,7 +408,7 @@ namespace Contralto.IO // // Set "seek fail" bit based on selected cylinder (if out of bounds) and do not // commence a seek if so. - if (destCylinder > SelectedDrive.Pack.Geometry.Cylinders - 1) + if (!SelectedDrive.IsLoaded || destCylinder > SelectedDrive.Pack.Geometry.Cylinders - 1) { _kStat |= SEEKFAIL; @@ -703,11 +714,19 @@ namespace Contralto.IO private bool _debugRead; - // KSTAT bitfields - public static readonly ushort SECLATE = 0x10; - public static readonly ushort NOTREADY = 0x20; - public static readonly ushort STROBE = 0x40; - public static readonly ushort SEEKFAIL = 0x80; + // + // KSTAT bitfields. + // Note that in reality the SECLATE status bit (bit 11) is a bit more nuanced; it's actually the OR of two signals: + // 1) SECLATE while the Sector Task is enabled (meaning that the sector task missed the beginning of a sector and that's bad). + // This is emulated. + // 2) CARRY while the Word Task is enabled. CARRY in this case is the carry out from the disk word shift register, signaling + // a completed word. If the Word Task is still running while a word is completed, this indicates that the word task missed that + // word and that's a fault. This is not currently emulated. + // + public static readonly ushort SECLATE = 0x10; // status bit 11 + public static readonly ushort NOTREADY = 0x20; // 10 + public static readonly ushort STROBE = 0x40; // 9 + public static readonly ushort SEEKFAIL = 0x80; // 8 } }