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