diff --git a/Contralto/AltoSystem.cs b/Contralto/AltoSystem.cs
index 989f06a..58d523d 100644
--- a/Contralto/AltoSystem.cs
+++ b/Contralto/AltoSystem.cs
@@ -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; }
diff --git a/Contralto/CPU/CPU.cs b/Contralto/CPU/CPU.cs
index 22201f8..124ffda 100644
--- a/Contralto/CPU/CPU.cs
+++ b/Contralto/CPU/CPU.cs
@@ -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;
diff --git a/Contralto/CPU/Tasks/Task.cs b/Contralto/CPU/Tasks/Task.cs
index 3b01a14..cdde852 100644
--- a/Contralto/CPU/Tasks/Task.cs
+++ b/Contralto/CPU/Tasks/Task.cs
@@ -126,7 +126,14 @@ namespace Contralto.CPU
/// An InstructionCompletion indicating whether this instruction calls for a task switch or not.
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:
diff --git a/Contralto/CPU/UCodeDisassembler.cs b/Contralto/CPU/UCodeDisassembler.cs
index e082ecf..5d81da5 100644
--- a/Contralto/CPU/UCodeDisassembler.cs
+++ b/Contralto/CPU/UCodeDisassembler.cs
@@ -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));
diff --git a/Contralto/Configuration.cs b/Contralto/Configuration.cs
index 7df5fbc..d805586 100644
--- a/Contralto/Configuration.cs
+++ b/Contralto/Configuration.cs
@@ -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++)
{
diff --git a/Contralto/IO/DiskPack.cs b/Contralto/IO/DiskPack.cs
index 8f89854..ce09d44 100644
--- a/Contralto/IO/DiskPack.cs
+++ b/Contralto/IO/DiskPack.cs
@@ -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();
}
diff --git a/Contralto/IO/TridentController.cs b/Contralto/IO/TridentController.cs
index f2f2504..1007bc8 100644
--- a/Contralto/IO/TridentController.cs
+++ b/Contralto/IO/TridentController.cs
@@ -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;
///
/// Drive timings
///
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 _outputFifo;
diff --git a/Contralto/IO/TridentDrive.cs b/Contralto/IO/TridentDrive.cs
index d256cac..e9e7788 100644
--- a/Contralto/IO/TridentDrive.cs
+++ b/Contralto/IO/TridentDrive.cs
@@ -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
//
diff --git a/Contralto/Memory/Memory.cs b/Contralto/Memory/Memory.cs
index 8cf4274..950bcd3 100644
--- a/Contralto/Memory/Memory.cs
+++ b/Contralto/Memory/Memory.cs
@@ -56,24 +56,42 @@ namespace Contralto.Memory
get { return _memTop; }
}
+ ///
+ /// Full reset, clears all memory.
+ ///
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];
+ }
+
+ ///
+ /// Soft reset, clears XM bank registers.
+ ///
+ 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;
}
}
diff --git a/Contralto/Program.cs b/Contralto/Program.cs
index 1e35f04..20cbf01 100644
--- a/Contralto/Program.cs
+++ b/Contralto/Program.cs
@@ -128,7 +128,7 @@ namespace Contralto
{
Console.WriteLine("Exiting...");
- _system.Shutdown();
+ _system.Shutdown(false /* don't commit disks */);
//
// Commit current configuration to disk
diff --git a/Contralto/SdlUI/SdlConsole.cs b/Contralto/SdlUI/SdlConsole.cs
index f1f7d85..cdd1f2b 100644
--- a/Contralto/SdlUI/SdlConsole.cs
+++ b/Contralto/SdlUI/SdlConsole.cs
@@ -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.
diff --git a/Contralto/UI/AltoWindow.cs b/Contralto/UI/AltoWindow.cs
index 0a6b702..4459231 100644
--- a/Contralto/UI/AltoWindow.cs
+++ b/Contralto/UI/AltoWindow.cs
@@ -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