mirror of
https://github.com/livingcomputermuseum/ContrAlto.git
synced 2026-01-26 04:01:07 +00:00
Fixed drive select logic (multiple drives now work properly -- fingers crossed) and some minor cleanup and dead-code removal.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace Contralto.CPU
|
||||
public partial class AltoCPU
|
||||
{
|
||||
/// <summary>
|
||||
/// EthernetTask implements Ethernet-specific task function
|
||||
/// EthernetTask implements Ethernet-specific task functions
|
||||
/// </summary>
|
||||
private sealed class EthernetTask : Task
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
public partial class AltoCPU
|
||||
{
|
||||
/// <summary>
|
||||
/// DisplayWordTask provides functionality for the Memory Refresh task
|
||||
/// DisplayWordTask provides functionality for the Memory Refresh task.
|
||||
/// </summary>
|
||||
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"
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current uInstruction asserts BLOCK.
|
||||
/// Used by hardware for various tasks.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user