mirror of
https://github.com/livingcomputermuseum/ContrAlto.git
synced 2026-01-17 00:23:24 +00:00
Fixed Trident drive select issues, corrected issue with extended memory bank registers. IFS now runs.
This commit is contained in:
parent
523a4bb27f
commit
38124350fb
@ -94,7 +94,7 @@ namespace Contralto
|
||||
_displayController.DetachDisplay();
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
public void Shutdown(bool commitDisks)
|
||||
{
|
||||
// Kill any host interface threads that are running.
|
||||
if (_ethernetController.HostInterface != null)
|
||||
@ -107,15 +107,18 @@ namespace Contralto
|
||||
//
|
||||
_audioDAC.Shutdown();
|
||||
|
||||
//
|
||||
// Save disk contents
|
||||
//
|
||||
_diskController.CommitDisk(0);
|
||||
_diskController.CommitDisk(1);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (commitDisks)
|
||||
{
|
||||
_tridentController.CommitDisk(i);
|
||||
//
|
||||
// Save disk contents
|
||||
//
|
||||
_diskController.CommitDisk(0);
|
||||
_diskController.CommitDisk(1);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
_tridentController.CommitDisk(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -210,11 +213,11 @@ namespace Contralto
|
||||
//
|
||||
switch (Path.GetExtension(path).ToLowerInvariant())
|
||||
{
|
||||
case ".t80":
|
||||
case ".dsk80":
|
||||
geometry = DiskGeometry.TridentT80;
|
||||
break;
|
||||
|
||||
case ".t300":
|
||||
case ".dsk300":
|
||||
geometry = DiskGeometry.TridentT300;
|
||||
break;
|
||||
|
||||
@ -228,11 +231,11 @@ namespace Contralto
|
||||
|
||||
if (newImage)
|
||||
{
|
||||
newPack = FileBackedDiskPack.CreateEmpty(geometry, path);
|
||||
newPack = InMemoryDiskPack.CreateEmpty(geometry, path);
|
||||
}
|
||||
else
|
||||
{
|
||||
newPack = FileBackedDiskPack.Load(geometry, path);
|
||||
newPack = InMemoryDiskPack.Load(geometry, path);
|
||||
}
|
||||
|
||||
_tridentController.Drives[drive].LoadPack(newPack);
|
||||
@ -273,6 +276,11 @@ namespace Contralto
|
||||
get { return _cpu; }
|
||||
}
|
||||
|
||||
public Memory.Memory Memory
|
||||
{
|
||||
get { return _mem; }
|
||||
}
|
||||
|
||||
public MemoryBus MemoryBus
|
||||
{
|
||||
get { return _memBus; }
|
||||
|
||||
@ -190,6 +190,9 @@ namespace Contralto.CPU
|
||||
Log.Write(LogComponent.CPU, "Silent Boot; microcode banks initialized to {0}", Conversion.ToOctal(_rmr));
|
||||
UCodeMemory.LoadBanksFromRMR(_rmr);
|
||||
|
||||
// Booting / soft-reset of the Alto resets the XM bank registers to zero.
|
||||
_system.Memory.SoftReset();
|
||||
|
||||
// Reset RMR after reset.
|
||||
_rmr = 0xffff;
|
||||
|
||||
|
||||
@ -126,7 +126,14 @@ namespace Contralto.CPU
|
||||
/// <returns>An InstructionCompletion indicating whether this instruction calls for a task switch or not.</returns>
|
||||
public InstructionCompletion ExecuteNext()
|
||||
{
|
||||
MicroInstruction instruction = UCodeMemory.GetInstruction(_mpc, _taskType);
|
||||
MicroInstruction instruction = UCodeMemory.GetInstruction(_mpc, _taskType);
|
||||
|
||||
/*
|
||||
if (_taskType == TaskType.Emulator && UCodeMemory.GetBank(_taskType) == MicrocodeBank.RAM0)
|
||||
{
|
||||
Console.WriteLine("{0}: {1}", Conversion.ToOctal(_mpc), UCodeDisassembler.DisassembleInstruction(instruction, _taskType));
|
||||
}*/
|
||||
|
||||
return ExecuteInstruction(instruction);
|
||||
}
|
||||
|
||||
@ -313,12 +320,12 @@ namespace Contralto.CPU
|
||||
// Do nothing. Well, that was easy.
|
||||
break;
|
||||
|
||||
case SpecialFunction1.LoadMAR:
|
||||
case SpecialFunction1.LoadMAR:
|
||||
// Do MAR or XMAR reference based on whether F2 is MD<- (for Alto IIs), indicating an extended memory reference.
|
||||
_cpu._system.MemoryBus.LoadMAR(
|
||||
aluData,
|
||||
_taskType,
|
||||
_systemType == SystemType.AltoI ? false : instruction.F2 == SpecialFunction2.StoreMD);
|
||||
aluData,
|
||||
_taskType,
|
||||
_systemType == SystemType.AltoI ? false : instruction.F2 == SpecialFunction2.StoreMD);
|
||||
break;
|
||||
|
||||
case SpecialFunction1.Task:
|
||||
|
||||
@ -70,15 +70,15 @@ namespace Contralto.CPU
|
||||
break;
|
||||
|
||||
case BusSource.ReadMD:
|
||||
source = "←MD ";
|
||||
source = "<-MD ";
|
||||
break;
|
||||
|
||||
case BusSource.ReadMouse:
|
||||
source = "←MOUSE ";
|
||||
source = "<-MOUSE ";
|
||||
break;
|
||||
|
||||
case BusSource.ReadDisp:
|
||||
source = "←DISP ";
|
||||
source = "<-DISP ";
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -164,11 +164,11 @@ namespace Contralto.CPU
|
||||
|
||||
if (instruction.F2 == SpecialFunction2.StoreMD)
|
||||
{
|
||||
f1 = "XMAR← ";
|
||||
f1 = "XMAR<- ";
|
||||
}
|
||||
else
|
||||
{
|
||||
f1 = "MAR← ";
|
||||
f1 = "MAR<- ";
|
||||
}
|
||||
break;
|
||||
|
||||
@ -181,15 +181,15 @@ namespace Contralto.CPU
|
||||
break;
|
||||
|
||||
case SpecialFunction1.LLSH1:
|
||||
f1 = "←L LSH 1 ";
|
||||
f1 = "<-L LSH 1 ";
|
||||
break;
|
||||
|
||||
case SpecialFunction1.LRSH1:
|
||||
f1 = "←L RSH 1 ";
|
||||
f1 = "<-L RSH 1 ";
|
||||
break;
|
||||
|
||||
case SpecialFunction1.LLCY8:
|
||||
f1 = "←L LCY 8 ";
|
||||
f1 = "<-L LCY 8 ";
|
||||
break;
|
||||
|
||||
case SpecialFunction1.Constant:
|
||||
@ -233,11 +233,11 @@ namespace Contralto.CPU
|
||||
if ((task == TaskType.TridentInput || task == TaskType.TridentOutput) &&
|
||||
instruction.BS == BusSource.None)
|
||||
{
|
||||
f2 = "MD← KDTA ";
|
||||
f2 = "MD<- KDTA ";
|
||||
}
|
||||
else if (instruction.F1 != SpecialFunction1.LoadMAR)
|
||||
{
|
||||
f2 = "MD← ";
|
||||
f2 = "MD<- ";
|
||||
}
|
||||
break;
|
||||
|
||||
@ -273,7 +273,7 @@ namespace Contralto.CPU
|
||||
break;
|
||||
}
|
||||
|
||||
load = String.Format("T← {0}", loadTFromALU ? operation : source);
|
||||
load = String.Format("T<- {0}", loadTFromALU ? operation : source);
|
||||
}
|
||||
|
||||
// Load L (and M) from ALU
|
||||
@ -282,24 +282,24 @@ namespace Contralto.CPU
|
||||
if (string.IsNullOrEmpty(load))
|
||||
{
|
||||
// T not loaded at all, L loaded from ALU
|
||||
load = String.Format("L← {0}", operation);
|
||||
load = String.Format("L<- {0}", operation);
|
||||
}
|
||||
else if (loadTFromALU)
|
||||
{
|
||||
// T loaded from ALU, L loaded from ALU
|
||||
load = String.Format("L← {0}", load);
|
||||
load = String.Format("L<- {0}", load);
|
||||
}
|
||||
else
|
||||
{
|
||||
// T loaded from bus source, L loaded from ALU
|
||||
load = String.Format("L← {0}, {1}", operation, load);
|
||||
load = String.Format("L<- {0}, {1}", operation, load);
|
||||
}
|
||||
}
|
||||
|
||||
// Do writeback to selected R register from shifter output
|
||||
if (loadR)
|
||||
{
|
||||
load = String.Format("$R{0}← {1}",
|
||||
load = String.Format("$R{0}<- {1}",
|
||||
Conversion.ToOctal((int)rSelect),
|
||||
!string.IsNullOrEmpty(load) ? load : operation);
|
||||
}
|
||||
@ -309,12 +309,12 @@ namespace Contralto.CPU
|
||||
{
|
||||
if (string.IsNullOrEmpty(load))
|
||||
{
|
||||
load = String.Format("$S{0}← M",
|
||||
load = String.Format("$S{0}<- M",
|
||||
Conversion.ToOctal((int)rSelect));
|
||||
}
|
||||
else
|
||||
{
|
||||
load = String.Format("$S{0}← M, {1}",
|
||||
load = String.Format("$S{0}<- M, {1}",
|
||||
Conversion.ToOctal((int)rSelect),
|
||||
load);
|
||||
}
|
||||
@ -448,10 +448,10 @@ namespace Contralto.CPU
|
||||
return "RDRAM ";
|
||||
|
||||
case EmulatorF1.LoadRMR:
|
||||
return "RMR← ";
|
||||
return "RMR<- ";
|
||||
|
||||
case EmulatorF1.LoadESRB:
|
||||
return "ESRB← ";
|
||||
return "ESRB<- ";
|
||||
|
||||
case EmulatorF1.RSNF:
|
||||
return "RSNF ";
|
||||
@ -481,13 +481,13 @@ namespace Contralto.CPU
|
||||
return "MAGIC ";
|
||||
|
||||
case EmulatorF2.LoadDNS:
|
||||
return "DNS← ";
|
||||
return "DNS<- ";
|
||||
|
||||
case EmulatorF2.BUSODD:
|
||||
return "BUSODD ";
|
||||
|
||||
case EmulatorF2.LoadIR:
|
||||
return "IR← ";
|
||||
return "IR<- ";
|
||||
|
||||
case EmulatorF2.IDISP:
|
||||
return "IDISP ";
|
||||
@ -507,13 +507,13 @@ namespace Contralto.CPU
|
||||
return "OrbitBlock ";
|
||||
|
||||
case OrbitF1.OrbitDeltaWC:
|
||||
return "←OrbitDeltaWC ";
|
||||
return "<-OrbitDeltaWC ";
|
||||
|
||||
case OrbitF1.OrbitDBCWidthRead:
|
||||
return "←OrbitDBCWidthRead ";
|
||||
return "<-OrbitDBCWidthRead ";
|
||||
|
||||
case OrbitF1.OrbitStatus:
|
||||
return "←OrbitStatus ";
|
||||
return "<-OrbitStatus ";
|
||||
|
||||
default:
|
||||
return String.Format("Orbit F1 {0}", Conversion.ToOctal((int)of1));
|
||||
@ -527,25 +527,25 @@ namespace Contralto.CPU
|
||||
switch (of2)
|
||||
{
|
||||
case OrbitF2.OrbitDBCWidthSet:
|
||||
return "OrbitDBCWidthSet← ";
|
||||
return "OrbitDBCWidthSet<- ";
|
||||
|
||||
case OrbitF2.OrbitXY:
|
||||
return "OrbitXY← ";
|
||||
return "OrbitXY<- ";
|
||||
|
||||
case OrbitF2.OrbitHeight:
|
||||
return "OrbitHeight← ";
|
||||
return "OrbitHeight<- ";
|
||||
|
||||
case OrbitF2.OrbitFontData:
|
||||
return "OrbitFontData← ";
|
||||
return "OrbitFontData<- ";
|
||||
|
||||
case OrbitF2.OrbitInk:
|
||||
return "OrbitInk← ";
|
||||
return "OrbitInk<- ";
|
||||
|
||||
case OrbitF2.OrbitControl:
|
||||
return "OrbitControl← ";
|
||||
return "OrbitControl<- ";
|
||||
|
||||
case OrbitF2.OrbitROSCommand:
|
||||
return "OrbitROSCommand← ";
|
||||
return "OrbitROSCommand<- ";
|
||||
|
||||
default:
|
||||
return String.Format("Orbit F2 {0}", Conversion.ToOctal((int)of2));
|
||||
@ -562,10 +562,10 @@ namespace Contralto.CPU
|
||||
return "EMPTY ";
|
||||
|
||||
case TridentF2.KTAG:
|
||||
return "KTAG← ";
|
||||
return "KTAG<- ";
|
||||
|
||||
case TridentF2.ReadKDTA:
|
||||
return "←KDTA ";
|
||||
return "<-KDTA ";
|
||||
|
||||
case TridentF2.RESET:
|
||||
return "RESET ";
|
||||
@ -578,7 +578,7 @@ namespace Contralto.CPU
|
||||
return "WAIT ";
|
||||
|
||||
case TridentF2.WriteKDTA:
|
||||
return "KDTA← ";
|
||||
return "KDTA<- ";
|
||||
|
||||
default:
|
||||
return String.Format("Trident F2 {0}", Conversion.ToOctal((int)tf2));
|
||||
|
||||
@ -134,6 +134,11 @@ namespace Contralto
|
||||
//
|
||||
// If configuration specifies fewer than 8 Trident disks, we need to pad the list out to 8 with empty entries.
|
||||
//
|
||||
if (TridentImages == null)
|
||||
{
|
||||
TridentImages = new StringCollection();
|
||||
}
|
||||
|
||||
int start = TridentImages.Count;
|
||||
for (int i = start; i < 8; i++)
|
||||
{
|
||||
|
||||
@ -447,7 +447,7 @@ namespace Contralto.IO
|
||||
for (int head = 0; head < _geometry.Heads; head++)
|
||||
{
|
||||
for (int sector = 0; sector < _geometry.Sectors; sector++)
|
||||
{
|
||||
{
|
||||
_sectors[cylinder, head, sector].WriteToStream(imageStream);
|
||||
}
|
||||
}
|
||||
@ -601,9 +601,8 @@ namespace Contralto.IO
|
||||
|
||||
private long GetOffsetForSector(int cylinder, int head, int sector)
|
||||
{
|
||||
int sectorNumber = (cylinder * _geometry.Heads * _geometry.Sectors) +
|
||||
(head * _geometry.Sectors) +
|
||||
sector;
|
||||
int sectorNumber = (cylinder * _geometry.Heads + head) * _geometry.Sectors + // total sectors for current track
|
||||
sector; // + current sector
|
||||
|
||||
return sectorNumber * _geometry.SectorGeometry.GetSectorSizeBytes();
|
||||
}
|
||||
|
||||
@ -38,6 +38,11 @@ namespace Contralto.IO
|
||||
{
|
||||
_system = system;
|
||||
|
||||
//
|
||||
// We initialize 16 drives even though the
|
||||
// controller only technically supports 8.
|
||||
// TODO: detail
|
||||
//
|
||||
_drives = new TridentDrive[16];
|
||||
|
||||
for(int i=0;i<_drives.Length;i++)
|
||||
@ -55,9 +60,7 @@ namespace Contralto.IO
|
||||
_seekIncomplete = false;
|
||||
_headOverflow = false;
|
||||
_deviceCheck = false;
|
||||
_notSelected = false;
|
||||
_notOnline = false;
|
||||
_notReady = false;
|
||||
_notSelected = false;
|
||||
_sectorOverflow = false;
|
||||
_outputLate = false;
|
||||
_inputLate = false;
|
||||
@ -79,7 +82,6 @@ namespace Contralto.IO
|
||||
|
||||
_sectorEvent = new Event(0, null, SectorCallback);
|
||||
_outputFifoEvent = new Event(0, null, OutputFifoCallback);
|
||||
_seekEvent = new Event(0, null, SeekCallback);
|
||||
_readWordEvent = new Event(0, null, ReadWordCallback);
|
||||
|
||||
// And schedule the first sector pulse.
|
||||
@ -95,8 +97,6 @@ namespace Contralto.IO
|
||||
_headOverflow = false;
|
||||
_deviceCheck = false;
|
||||
_notSelected = false;
|
||||
_notOnline = false;
|
||||
_notReady = false;
|
||||
_sectorOverflow = false;
|
||||
_outputLate = false;
|
||||
_inputLate = false;
|
||||
@ -249,7 +249,7 @@ namespace Contralto.IO
|
||||
(_headOverflow ? 0x4000 : 0) |
|
||||
(_deviceCheck ? 0x2000 : 0) |
|
||||
(_notSelected ? 0x1000 : 0) |
|
||||
(_notOnline ? 0x0800 : 0) |
|
||||
(NotOnline() ? 0x0800 : 0) |
|
||||
(NotReady() ? 0x0400 : 0) |
|
||||
(_sectorOverflow ? 0x0200 : 0) |
|
||||
(_outputLate ? 0x0100 : 0) |
|
||||
@ -287,7 +287,14 @@ namespace Contralto.IO
|
||||
|
||||
private bool NotReady()
|
||||
{
|
||||
return _notReady | !SelectedDrive.IsLoaded;
|
||||
return SelectedDrive.NotReady |
|
||||
!SelectedDrive.IsLoaded |
|
||||
_selectedDrive > 7;
|
||||
}
|
||||
|
||||
private bool NotOnline()
|
||||
{
|
||||
return _selectedDrive < 8 ? !SelectedDrive.IsLoaded : true;
|
||||
}
|
||||
|
||||
private void WriteOutputFifo(int value)
|
||||
@ -550,7 +557,21 @@ namespace Contralto.IO
|
||||
break;
|
||||
|
||||
case 3: // Set Drive
|
||||
// We take all four drive-select bits even though only 8 drives are actually supported.
|
||||
// The high bit is used by many trident utilities to select an invalid drive to test for
|
||||
// the presence of the 8-drive multiplexer.
|
||||
_selectedDrive = fifoWord & 0xf;
|
||||
|
||||
if (_selectedDrive > 7)
|
||||
{
|
||||
_system.CPU.BlockTask(TaskType.TridentOutput);
|
||||
_notSelected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_notSelected = false;
|
||||
}
|
||||
|
||||
Log.Write(LogComponent.TridentController, "Command is Set Drive {0}", _selectedDrive);
|
||||
break;
|
||||
}
|
||||
@ -636,48 +657,7 @@ namespace Contralto.IO
|
||||
|
||||
private void InitSeek(int destCylinder)
|
||||
{
|
||||
if (destCylinder > SelectedDrive.Pack.Geometry.Cylinders - 1)
|
||||
{
|
||||
Log.Write(LogType.Error, LogComponent.TridentController, "Specified cylinder {0} is out of range. Seek aborted.", destCylinder);
|
||||
_deviceCheck = true;
|
||||
return;
|
||||
}
|
||||
|
||||
int currentCylinder = SelectedDrive.Cylinder;
|
||||
if (destCylinder != currentCylinder)
|
||||
{
|
||||
// Do a seek.
|
||||
_notReady = true;
|
||||
|
||||
_destCylinder = destCylinder;
|
||||
|
||||
//
|
||||
// I can't find a specific formula for seek timing; the Century manual says:
|
||||
// "Positioning time for seeking to the next cylinder is normally 6ms, and
|
||||
// for full seeks (814 cylinder differerence) it is 55ms."
|
||||
//
|
||||
// I'm just going to fudge this for now and assume a linear ramp; this is not
|
||||
// accurate but it's not all that important.
|
||||
//
|
||||
_seekDuration = (ulong)((6.0 + 0.602 * Math.Abs(currentCylinder - destCylinder)) * Conversion.MsecToNsec);
|
||||
|
||||
Log.Write(LogComponent.TridentController, "Commencing seek from {0} to {1}. Seek time is {2}ns", destCylinder, currentCylinder, _seekDuration);
|
||||
|
||||
_seekEvent.TimestampNsec = _seekDuration;
|
||||
_system.Scheduler.Schedule(_seekEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Write(LogComponent.TridentController, "Seek is a no-op.");
|
||||
}
|
||||
}
|
||||
|
||||
private void SeekCallback(ulong timeNsec, ulong skewNsec, object context)
|
||||
{
|
||||
Log.Write(LogComponent.TridentDisk, "Seek to {0} complete.", _destCylinder);
|
||||
|
||||
SelectedDrive.Cylinder = _destCylinder;
|
||||
_notReady = false;
|
||||
_deviceCheck = !SelectedDrive.Seek(destCylinder);
|
||||
}
|
||||
|
||||
private void InitRead()
|
||||
@ -786,7 +766,7 @@ namespace Contralto.IO
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Write(LogComponent.TridentController, "CHS {0}/{1}/{2} {3} read complete.", SelectedDrive.Cylinder, SelectedDrive.Head, _sector, _sectorBlock);
|
||||
Log.Write(LogComponent.TridentController, "CHS {0}/{1}/{2} {3} read from drive {4} complete.", SelectedDrive.Cylinder, SelectedDrive.Head, _sector, _sectorBlock, _selectedDrive);
|
||||
_readState = ReadState.Idle;
|
||||
|
||||
_sectorBlock++;
|
||||
@ -847,7 +827,7 @@ namespace Contralto.IO
|
||||
_writeWordCount--;
|
||||
if (_writeWordCount <= 0)
|
||||
{
|
||||
Log.Write(LogComponent.TridentController, "CHS {0}/{1}/{2} {3} write complete. {4} words written.", SelectedDrive.Cylinder, SelectedDrive.Head, _sector, _sectorBlock, _writeIndex);
|
||||
Console.WriteLine( "CHS {0}/{1}/{2} {3} write to drive {4} complete. {5} words written.", SelectedDrive.Cylinder, SelectedDrive.Head, _sector, _sectorBlock, _selectedDrive, _writeIndex);
|
||||
_writeState = WriteState.Idle;
|
||||
_writeIndex = 0;
|
||||
|
||||
@ -857,19 +837,24 @@ namespace Contralto.IO
|
||||
}
|
||||
|
||||
private void SectorCallback(ulong timeNsec, ulong skewNsec, object context)
|
||||
{
|
||||
{
|
||||
// Move to the next sector.
|
||||
_sector = (_sector + 1) % 9;
|
||||
SelectedDrive.Sector = _sector;
|
||||
|
||||
// Reset to the first block (header) in the sector.
|
||||
_sectorBlock = SectorBlock.Header;
|
||||
|
||||
// Wake up the Output task if RunEnable is true and the drive is ready.
|
||||
if (_runEnable && !NotReady())
|
||||
{
|
||||
_sector = (_sector + 1) % 9;
|
||||
SelectedDrive.Sector = _sector;
|
||||
|
||||
// Reset to the first block (header) in the sector.
|
||||
_sectorBlock = SectorBlock.Header;
|
||||
|
||||
// Wake up the Output task
|
||||
_system.CPU.WakeupTask(TaskType.TridentOutput);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep the output task asleep.
|
||||
_system.CPU.BlockTask(TaskType.TridentOutput);
|
||||
}
|
||||
|
||||
//
|
||||
// Schedule the next sector pulse
|
||||
@ -894,6 +879,7 @@ namespace Contralto.IO
|
||||
{
|
||||
get { return _drives[_selectedDrive]; }
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Input FIFO semantics
|
||||
@ -1033,9 +1019,7 @@ namespace Contralto.IO
|
||||
private bool _seekIncomplete;
|
||||
private bool _headOverflow;
|
||||
private bool _deviceCheck;
|
||||
private bool _notSelected;
|
||||
private bool _notOnline;
|
||||
private bool _notReady;
|
||||
private bool _notSelected;
|
||||
private bool _sectorOverflow;
|
||||
private bool _outputLate;
|
||||
private bool _inputLate;
|
||||
@ -1044,20 +1028,13 @@ namespace Contralto.IO
|
||||
private bool _offset;
|
||||
|
||||
// Current sector
|
||||
private int _sector;
|
||||
private int _sector;
|
||||
|
||||
/// <summary>
|
||||
/// Drive timings
|
||||
/// </summary>
|
||||
private static ulong _sectorDuration = (ulong)((16.66666 / 9.0) * Conversion.MsecToNsec); // time in nsec for one sector -- 9 sectors, 16.66ms per rotation
|
||||
private Event _sectorEvent;
|
||||
|
||||
//
|
||||
// Seek timings
|
||||
//
|
||||
private static ulong _seekDuration = (6 * Conversion.MsecToNsec);
|
||||
private Event _seekEvent;
|
||||
private int _destCylinder;
|
||||
private Event _sectorEvent;
|
||||
|
||||
// Output FIFO. This is actually 17-bits wide (the 17th bit is the Tag bit).
|
||||
private Queue<int> _outputFifo;
|
||||
|
||||
@ -29,6 +29,8 @@ namespace Contralto.IO
|
||||
public TridentDrive(AltoSystem system)
|
||||
{
|
||||
_system = system;
|
||||
|
||||
_seekEvent = new Event(0, null, SeekCallback);
|
||||
Reset();
|
||||
}
|
||||
|
||||
@ -38,6 +40,8 @@ namespace Contralto.IO
|
||||
_cylinder = 0;
|
||||
_head = 0;
|
||||
|
||||
_notReady = false;
|
||||
|
||||
UpdateTrackCache();
|
||||
}
|
||||
|
||||
@ -82,6 +86,11 @@ namespace Contralto.IO
|
||||
get { return _pack != null; }
|
||||
}
|
||||
|
||||
public bool NotReady
|
||||
{
|
||||
get { return _notReady; }
|
||||
}
|
||||
|
||||
public IDiskPack Pack
|
||||
{
|
||||
get { return _pack; }
|
||||
@ -124,6 +133,45 @@ namespace Contralto.IO
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool Seek(int destCylinder)
|
||||
{
|
||||
if (destCylinder > _pack.Geometry.Cylinders - 1)
|
||||
{
|
||||
Log.Write(LogType.Error, LogComponent.TridentController, "Specified cylinder {0} is out of range. Seek aborted, device check raised.", destCylinder);
|
||||
return false;
|
||||
}
|
||||
|
||||
int currentCylinder = _cylinder;
|
||||
if (destCylinder != currentCylinder)
|
||||
{
|
||||
// Do a seek.
|
||||
_notReady = true;
|
||||
|
||||
_destCylinder = destCylinder;
|
||||
|
||||
//
|
||||
// I can't find a specific formula for seek timing; the Century manual says:
|
||||
// "Positioning time for seeking to the next cylinder is normally 6ms, and
|
||||
// for full seeks (814 cylinder differerence) it is 55ms."
|
||||
//
|
||||
// I'm just going to fudge this for now and assume a linear ramp; this is not
|
||||
// accurate but it's not all that important.
|
||||
//
|
||||
_seekDuration = (ulong)((6.0 + 0.602 * Math.Abs(currentCylinder - destCylinder)) * Conversion.MsecToNsec);
|
||||
|
||||
Log.Write(LogComponent.TridentController, "Commencing seek from {0} to {1}. Seek time is {2}ns", destCylinder, currentCylinder, _seekDuration);
|
||||
|
||||
_seekEvent.TimestampNsec = _seekDuration;
|
||||
_system.Scheduler.Schedule(_seekEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Write(LogComponent.TridentController, "Seek is a no-op.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public ushort ReadHeader(int wordOffset)
|
||||
{
|
||||
if (wordOffset >= SectorGeometry.Trident.HeaderSize)
|
||||
@ -223,6 +271,14 @@ namespace Contralto.IO
|
||||
}
|
||||
}
|
||||
|
||||
private void SeekCallback(ulong timeNsec, ulong skewNsec, object context)
|
||||
{
|
||||
Log.Write(LogComponent.TridentDisk, "Seek to {0} complete.", _destCylinder);
|
||||
|
||||
Cylinder = _destCylinder;
|
||||
_notReady = false;
|
||||
}
|
||||
|
||||
//
|
||||
// Sector management. We load in an entire track's worth of sectors at a time.
|
||||
// When the head or cylinder changes, UpdateTrackCache must be called.
|
||||
@ -276,6 +332,15 @@ namespace Contralto.IO
|
||||
// The pack loaded into the drive
|
||||
IDiskPack _pack;
|
||||
|
||||
// Drive status
|
||||
private bool _notReady;
|
||||
|
||||
// Seek status and control
|
||||
private static ulong _seekDuration;
|
||||
private Event _seekEvent;
|
||||
private int _destCylinder;
|
||||
|
||||
|
||||
//
|
||||
// The track cache
|
||||
//
|
||||
|
||||
@ -56,24 +56,42 @@ namespace Contralto.Memory
|
||||
get { return _memTop; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Full reset, clears all memory.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
// 4 64K banks, regardless of system type. (Alto Is just won't use the extra memory.)
|
||||
_mem = new ushort[0x40000];
|
||||
_xmBanks = new ushort[16];
|
||||
_xmBanksAlternate = new int[16];
|
||||
_xmBanksNormal = new int[16];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Soft reset, clears XM bank registers.
|
||||
/// </summary>
|
||||
public void SoftReset()
|
||||
{
|
||||
_xmBanks = new ushort[16];
|
||||
_xmBanksAlternate = new int[16];
|
||||
_xmBanksNormal = new int[16];
|
||||
}
|
||||
|
||||
public ushort Read(int address, TaskType task, bool extendedMemory)
|
||||
{
|
||||
// Check for XM registers; this occurs regardless of XM flag since it's in the I/O page.
|
||||
if (address >= _xmBanksStart && address < _xmBanksStart + 16)
|
||||
{
|
||||
{
|
||||
// NB: While not specified in documentatino, some code (IFS in particular) relies on the fact that
|
||||
// the upper 12 bits of the bank registers are all 1s.
|
||||
return (ushort)(0xfff0 | _xmBanks[address - _xmBanksStart]);
|
||||
}
|
||||
else
|
||||
{
|
||||
address += 0x10000 * GetBankNumber(task, extendedMemory);
|
||||
return _mem[address];
|
||||
|
||||
return _mem[address];
|
||||
}
|
||||
}
|
||||
|
||||
@ -81,17 +99,22 @@ namespace Contralto.Memory
|
||||
{
|
||||
// Check for XM registers; this occurs regardless of XM flag since it's in the I/O page.
|
||||
if (address >= _xmBanksStart && address < _xmBanksStart + 16)
|
||||
{
|
||||
{
|
||||
_xmBanks[address - _xmBanksStart] = data;
|
||||
|
||||
// Precalc bank numbers to speed memory accesses that use them
|
||||
_xmBanksAlternate[address - _xmBanksStart] = data & 0x3;
|
||||
_xmBanksNormal[address - _xmBanksStart] = (data & 0xc) >> 2;
|
||||
|
||||
Log.Write(LogComponent.Memory, "XM register for task {0} set to bank {1} (normal), {2} (xm)",
|
||||
(TaskType)(address - _xmBanksStart),
|
||||
(data & 0xc) >> 2,
|
||||
(data & 0x3));
|
||||
(data & 0x3));
|
||||
}
|
||||
else
|
||||
{
|
||||
address += 0x10000 * GetBankNumber(task, extendedMemory);
|
||||
_mem[address] = data;
|
||||
_mem[address] = data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,7 +125,7 @@ namespace Contralto.Memory
|
||||
|
||||
private int GetBankNumber(TaskType task, bool extendedMemory)
|
||||
{
|
||||
return extendedMemory ? (_xmBanks[(int)task]) & 0x3 : ((_xmBanks[(int)task]) & 0xc) >> 2;
|
||||
return extendedMemory ? _xmBanksAlternate[(int)task] : _xmBanksNormal[(int)task];
|
||||
}
|
||||
|
||||
private readonly MemoryRange[] _addresses;
|
||||
@ -112,6 +135,8 @@ namespace Contralto.Memory
|
||||
|
||||
private ushort[] _mem;
|
||||
|
||||
private ushort[] _xmBanks;
|
||||
private ushort[] _xmBanks;
|
||||
private int[] _xmBanksAlternate;
|
||||
private int[] _xmBanksNormal;
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +128,7 @@ namespace Contralto
|
||||
{
|
||||
Console.WriteLine("Exiting...");
|
||||
|
||||
_system.Shutdown();
|
||||
_system.Shutdown(false /* don't commit disks */);
|
||||
|
||||
//
|
||||
// Commit current configuration to disk
|
||||
|
||||
@ -87,7 +87,7 @@ namespace Contralto.SdlUI
|
||||
//
|
||||
_controller.StopExecution();
|
||||
|
||||
_system.Shutdown();
|
||||
_system.Shutdown(true /* commit disk contents */);
|
||||
|
||||
//
|
||||
// The Alto window was closed, shut down the CLI.
|
||||
|
||||
@ -408,7 +408,7 @@ namespace Contralto
|
||||
// Halt the system and detach our display
|
||||
_controller.StopExecution();
|
||||
_system.DetachDisplay();
|
||||
_system.Shutdown();
|
||||
_system.Shutdown(true /* commit disk contents */);
|
||||
|
||||
//
|
||||
// Commit current configuration to disk
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user