From ee7c7fb0357fa9950309196d75e8771f1a642053 Mon Sep 17 00:00:00 2001 From: Josh Dersch Date: Wed, 16 Sep 2015 16:27:16 -0700 Subject: [PATCH] Implemented more Disk functionality, fixed bug in ACSOURCE dispatch in Emulator task. --- Contralto/AltoSystem.cs | 22 +++- Contralto/CPU/CPU.cs | 43 +++---- Contralto/CPU/ConstantMemory.cs | 58 +++++++++- Contralto/CPU/Disassembler.cs | 2 +- Contralto/CPU/Tasks/DiskTask.cs | 70 +++++++++++- Contralto/CPU/Tasks/EmulatorTask.cs | 56 ++------- Contralto/CPU/Tasks/Task.cs | 4 +- Contralto/Contralto.csproj | 7 ++ Contralto/Debugger.cs | 12 +- Contralto/IClockable.cs | 17 +++ Contralto/IO/DiskController.cs | 171 +++++++++++++++++++++++++++- Contralto/Memory/MemoryBus.cs | 9 +- Contralto/Notes.txt | 5 + Contralto/ROM/2kctl.u3 | Bin 0 -> 256 bytes 14 files changed, 387 insertions(+), 89 deletions(-) create mode 100644 Contralto/IClockable.cs create mode 100644 Contralto/Notes.txt create mode 100644 Contralto/ROM/2kctl.u3 diff --git a/Contralto/AltoSystem.cs b/Contralto/AltoSystem.cs index 1d080ec..3b61850 100644 --- a/Contralto/AltoSystem.cs +++ b/Contralto/AltoSystem.cs @@ -17,7 +17,7 @@ namespace Contralto public class AltoSystem { public AltoSystem() - { + { _cpu = new AltoCPU(this); _memBus = new MemoryBus(); _mem = new Memory.Memory(); @@ -27,7 +27,13 @@ namespace Contralto // Attach memory-mapped devices to the bus _memBus.AddDevice(_mem); _memBus.AddDevice(_keyboard); - + + // Register devices that need clocks + _clockableDevices = new List(); + _clockableDevices.Add(_memBus); + _clockableDevices.Add(_diskController); + _clockableDevices.Add(_cpu); + Reset(); } @@ -35,13 +41,16 @@ namespace Contralto { _cpu.Reset(); _memBus.Reset(); + _diskController.Reset(); } public void SingleStep() { - _memBus.Clock(); - _diskController.Clock(); - _cpu.ExecuteNext(); + // Run every device that needs attention for a single clock cycle. + for (int i = 0; i < _clockableDevices.Count; i++) + { + _clockableDevices[i].Clock(); + } } public AltoCPU CPU @@ -62,6 +71,7 @@ namespace Contralto /// /// Time (in msec) for one system clock /// + /// public static double ClockInterval { get { return 0.00017; } // appx 170nsec, TODO: more accurate value? @@ -72,5 +82,7 @@ namespace Contralto private Contralto.Memory.Memory _mem; private Keyboard _keyboard; private DiskController _diskController; + + private List _clockableDevices; } } diff --git a/Contralto/CPU/CPU.cs b/Contralto/CPU/CPU.cs index 9175c04..c88aafc 100644 --- a/Contralto/CPU/CPU.cs +++ b/Contralto/CPU/CPU.cs @@ -22,7 +22,7 @@ namespace Contralto.CPU DiskWord = 14, } - public partial class AltoCPU + public partial class AltoCPU : IClockable { public AltoCPU(AltoSystem system) { @@ -115,25 +115,9 @@ namespace Contralto.CPU } - public void ExecuteNext() + public void Clock() { - if (_currentTask.ExecuteNext()) - { - // Invoke the task switch, this will take effect after - // the NEXT instruction, not this one. - TaskSwitch(); - } - else - { - // If we have a new task, switch to it now. - if (_nextTask != null) - { - _currentTask = _nextTask; - _nextTask = null; - } - } - - _clocks++; + ExecuteNext(); } /// @@ -162,6 +146,27 @@ namespace Contralto.CPU } } + private void ExecuteNext() + { + if (_currentTask.ExecuteNext()) + { + // Invoke the task switch, this will take effect after + // the NEXT instruction, not this one. + TaskSwitch(); + } + else + { + // If we have a new task, switch to it now. + if (_nextTask != null) + { + _currentTask = _nextTask; + _nextTask = null; + } + } + + _clocks++; + } + private void TaskSwitch() { // Select the highest-priority eligible task diff --git a/Contralto/CPU/ConstantMemory.cs b/Contralto/CPU/ConstantMemory.cs index 6e4a15d..1f6a6b8 100644 --- a/Contralto/CPU/ConstantMemory.cs +++ b/Contralto/CPU/ConstantMemory.cs @@ -7,9 +7,9 @@ using System.Threading.Tasks; namespace Contralto.CPU { - static class ConstantMemory + static class ControlROM { - static ConstantMemory() + static ControlROM() { Init(); } @@ -17,6 +17,7 @@ namespace Contralto.CPU private static void Init() { LoadConstants(_constantRoms); + LoadACSource(_acSourceRoms); } public static ushort[] ConstantROM @@ -24,6 +25,11 @@ namespace Contralto.CPU get { return _constantRom; } } + public static byte[] ACSourceROM + { + get { return _acSourceRom; } + } + private static void LoadConstants(RomFile[] romInfo) { _constantRom = new ushort[256]; @@ -47,7 +53,7 @@ namespace Contralto.CPU // OR in the data for (int i = 0; i < length; i++) { - _constantRom[file.StartingAddress + i] |= (ushort)((DataMap(data[AddressMap(i)]) & 0xf) << file.BitPosition); + _constantRom[file.StartingAddress + i] |= (ushort)((DataMapConstantRom(data[AddressMapConstantRom(i)]) & 0xf) << file.BitPosition); } } } @@ -59,7 +65,29 @@ namespace Contralto.CPU } } - private static int AddressMap(int address) + private static void LoadACSource(RomFile romInfo) + { + _acSourceRom = new byte[256]; + + using (FileStream fs = new FileStream(Path.Combine("ROM", romInfo.Filename), FileMode.Open, FileAccess.Read)) + { + int length = (int)fs.Length; + if (length != 256) + { + throw new InvalidOperationException("ROM file should be 256 bytes in length"); + } + byte[] data = new byte[fs.Length]; + fs.Read(data, 0, (int)fs.Length); + + // Copy in the data, modifying the address as required. + for (int i = 0; i < length; i++) + { + _acSourceRom[i] = (byte)((~data[AddressMapACSourceRom(i)]) & 0xf); + } + } + } + + private static int AddressMapConstantRom(int address) { // Descramble the address bits as they are in no sane order. // (See 05a_AIM.pdf, pg. 5 (Page 9 of the orginal docs)) @@ -77,7 +105,7 @@ namespace Contralto.CPU return mappedAddress; } - private static int DataMap(int data) + private static int DataMapConstantRom(int data) { // Reverse bits 0-4. int mappedData = 0; @@ -93,6 +121,23 @@ namespace Contralto.CPU return mappedData; } + private static int AddressMapACSourceRom(int data) + { + // Reverse bits 0-7. + int mappedData = 0; + + for (int i = 0; i < 8; i++) + { + if ((data & (1 << i)) != 0) + { + mappedData |= (1 << (7 - i)); + } + } + + // And invert. + return (~mappedData) & 0xff; + } + private static RomFile[] _constantRoms = { new RomFile("c0", 0x000, 12), @@ -101,6 +146,9 @@ namespace Contralto.CPU new RomFile("c3", 0x000, 0), }; + private static RomFile _acSourceRoms = new RomFile("2kctl.u3", 0x000, 0); + private static ushort[] _constantRom; + private static byte[] _acSourceRom; } } diff --git a/Contralto/CPU/Disassembler.cs b/Contralto/CPU/Disassembler.cs index 2473ceb..ab30c6f 100644 --- a/Contralto/CPU/Disassembler.cs +++ b/Contralto/CPU/Disassembler.cs @@ -73,7 +73,7 @@ namespace Contralto.CPU instruction.F2 == SpecialFunction2.Constant) { source += String.Format("C({0})", - OctalHelpers.ToOctal(ConstantMemory.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)])); + OctalHelpers.ToOctal(ControlROM.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)])); } switch (instruction.ALUF) diff --git a/Contralto/CPU/Tasks/DiskTask.cs b/Contralto/CPU/Tasks/DiskTask.cs index 6ae6374..6ac9da2 100644 --- a/Contralto/CPU/Tasks/DiskTask.cs +++ b/Contralto/CPU/Tasks/DiskTask.cs @@ -96,7 +96,75 @@ namespace Contralto.CPU case DiskF2.INIT: // "NEXT<-NEXT OR (if WDTASKACT AND WDINIT) then 37B else 0 // TODO: figure out how WDTASKACT and WDINIT work. - throw new NotImplementedException("INIT not implemented."); + + // From the US Patent (4148098): + // "..two multiplexers...allow the setting of the next field bits NEXT(05)-NEXT(09) + // to 1 after an error condition is detected and as soon as the word task active signal + // WDTASKACT is generated..." + + // Is this always an error condition? + Console.WriteLine("Warning: assuming 0 for Disk F2 INIT (unimplemented stub)"); + break; + + + case DiskF2.RWC: + // "NEXT<-NEXT OR (IF current record to be written THEN 3 ELSE IF + // current record to be checked THEN 2 ELSE 0.") + // Current record is in bits 8-9 of the command register; this is shifted + // by INCREC by the microcode to present the next set of bits. + int command = (_cpu._system.DiskController.KADR & 0x00c0) >> 6; + + switch(command) + { + case 0: + // read, no modification. + break; + + case 1: + // check, OR in 2 + _nextModifier |= 0x2; + break; + + case 2: + case 3: + // write, OR in 3 + _nextModifier |= 0x3; + break; + } + break; + + case DiskF2.XFRDAT: + // "NEXT <- NEXT OR (IF current command wants data transfer THEN 1 ELSE 0) + // TODO: need to get unshifted bit 14 of the command register. Disk controller should + // save this off somewhere. + if (_cpu._system.DiskController.DataXfer) + { + _nextModifier |= 0x1; + } + break; + + case DiskF2.RECNO: + _nextModifier |= _cpu._system.DiskController.RECNO; + break; + + case DiskF2.NFER: + // "NEXT <- NEXT OR (IF fatal error in latches THEN 0 ELSE 1)" + // We assume success for now... + _nextModifier |= 0x1; + break; + + case DiskF2.STROBON: + // "NEXT <- NEXT OR (IF seek strobe still on THEN 1 ELSE 0)" + if ((_cpu._system.DiskController.KSTAT & 0x0040) == 0x0040) + { + _nextModifier |= 0x1; + } + break; + + case DiskF2.SWRNRDY: + // "NEXT <- NEXT OR (IF disk not ready to accept command THEN 1 ELSE 0) + // for now, always zero (not sure when this would be 1 yet) + break; default: throw new InvalidOperationException(String.Format("Unhandled disk special function 2 {0}", df2)); diff --git a/Contralto/CPU/Tasks/EmulatorTask.cs b/Contralto/CPU/Tasks/EmulatorTask.cs index a05377c..4139ca7 100644 --- a/Contralto/CPU/Tasks/EmulatorTask.cs +++ b/Contralto/CPU/Tasks/EmulatorTask.cs @@ -192,54 +192,14 @@ namespace Contralto.CPU // if IR[3-7] = 16B 1 CONVERT // if IR[3-7] = 37B 17B ROMTRAP -- used by Swat, the debugger // else 16B ROMTRAP - if ((_cpu._ir & 0x8000) != 0) - { - _nextModifier = (ushort)(3 - ((_cpu._ir & 0xc0) >> 6)); - } - else if ((_cpu._ir & 0xc000) == 0xc000) - { - _nextModifier = (ushort)((_cpu._ir & 0x400) >> 10); - } - else if ((_cpu._ir & 0x1f00) == 0) - { - _nextModifier = 2; - } - else if ((_cpu._ir & 0x1f00) == 0x0100) - { - _nextModifier = 5; - } - else if ((_cpu._ir & 0x1f00) == 0x0200) - { - _nextModifier = 3; - } - else if ((_cpu._ir & 0x1f00) == 0x0300) - { - _nextModifier = 6; - } - else if ((_cpu._ir & 0x1f00) == 0x0400) - { - _nextModifier = 7; - } - else if ((_cpu._ir & 0x1f00) == 0x0900) - { - _nextModifier = 4; - } - else if ((_cpu._ir & 0x1f00) == 0x0a00) - { - _nextModifier = 4; - } - else if ((_cpu._ir & 0x1f00) == 0x0e00) - { - _nextModifier = 1; - } - else if ((_cpu._ir & 0x1f00) == 0x1f00) - { - _nextModifier = 0xf; - } - else - { - _nextModifier = 0xe; - } + + // + // NOTE: the above table from the Hardware Manual is incorrect (or at least incomplete / misleading). + // There is considerably more that goes into determining the dispatch, which is controlled by a 256x8 + // PROM. We just use the PROM rather than implementing the above logic (because it works.) + // + _nextModifier = ControlROM.ACSourceROM[(_cpu._ir & 0xff00) >> 8]; + break; case EmulatorF2.ACDEST: diff --git a/Contralto/CPU/Tasks/Task.cs b/Contralto/CPU/Tasks/Task.cs index 1518d18..d1a9a9f 100644 --- a/Contralto/CPU/Tasks/Task.cs +++ b/Contralto/CPU/Tasks/Task.cs @@ -185,7 +185,7 @@ namespace Contralto.CPU else { // See also comments below. - _busData = ConstantMemory.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)]; + _busData = ControlROM.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)]; } // Constant ROM access: @@ -202,7 +202,7 @@ namespace Contralto.CPU instruction.F1 == SpecialFunction1.Constant || instruction.F2 == SpecialFunction2.Constant) { - _busData &= ConstantMemory.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)]; + _busData &= ControlROM.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)]; } // Do ALU operation diff --git a/Contralto/Contralto.csproj b/Contralto/Contralto.csproj index 21ce8e3..6a64338 100644 --- a/Contralto/Contralto.csproj +++ b/Contralto/Contralto.csproj @@ -60,6 +60,7 @@ Debugger.cs + @@ -75,6 +76,9 @@ PreserveNewest + + Always + Always @@ -189,6 +193,9 @@ Debugger.cs + + +