diff --git a/Contralto/AltoSystem.cs b/Contralto/AltoSystem.cs new file mode 100644 index 0000000..782740d --- /dev/null +++ b/Contralto/AltoSystem.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Contralto.CPU; +using Contralto.Memory; + +namespace Contralto +{ + /// + /// Encapsulates all Alto hardware; represents a complete Alto system. + /// Provides interfaces for controlling and debugging the system externally. + /// + public class AltoSystem + { + public AltoSystem() + { + _cpu = new AltoCPU(this); + _mem = new MemoryBus(); + Reset(); + } + + public void Reset() + { + _cpu.Reset(); + _mem.Reset(); + } + + public void SingleStep() + { + _mem.Clock(); + _cpu.ExecuteNext(); + } + + public AltoCPU CPU + { + get { return _cpu; } + } + + public MemoryBus MemoryBus + { + get { return _mem; } + } + + private AltoCPU _cpu; + private MemoryBus _mem; + } +} diff --git a/Contralto/CPU/CPU.cs b/Contralto/CPU/CPU.cs index dafc92e..dcbde12 100644 --- a/Contralto/CPU/CPU.cs +++ b/Contralto/CPU/CPU.cs @@ -9,25 +9,76 @@ namespace Contralto.CPU { public enum TaskType { + Invalid = -1, Emulator = 0, DiskSector = 4, - Ethernet = 5, - DiskWord = 9, + Ethernet = 7, + MemoryRefresh = 8, + DisplayWord = 9, Cursor = 10, DisplayHorizontal = 11, DisplayVertical = 12, - Refresh = 14, + Parity = 13, + DiskWord = 14, } public class AltoCPU { - public AltoCPU() + public AltoCPU(AltoSystem system) { + _system = system; + _tasks[(int)TaskType.Emulator] = new EmulatorTask(this); Reset(); } + public Task[] Tasks + { + get { return _tasks; } + } + + public Task CurrentTask + { + get { return _currentTask; } + } + + public ushort[] R + { + get { return _r; } + } + + public ushort[][] S + { + get { return _s; } + } + + public ushort T + { + get { return _t; } + } + + public ushort L + { + get { return _l; } + } + + public ushort M + { + get { return _m; } + } + + public ushort IR + { + get { return _ir; } + } + + public ushort ALUC0 + { + get { return _aluC0; } + } + + public void Reset() { // Reset registers @@ -126,7 +177,7 @@ namespace Contralto.CPU // 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. - private abstract class Task + public abstract class Task { public Task(AltoCPU cpu) { @@ -146,6 +197,11 @@ namespace Contralto.CPU get { return _wakeup; } } + public ushort MPC + { + get { return _mpc; } + } + public virtual void Reset() { // From The Alto Hardware Manual (section 2, "Initialization"): @@ -183,6 +239,7 @@ namespace Contralto.CPU bool nextTask = false; bool loadR = false; ushort aluData = 0; + ushort nextModifier = 0; _loadS = false; _rSelect = 0; _busData = 0; @@ -193,16 +250,39 @@ namespace Contralto.CPU // // Wait for memory state machine if a memory operation is requested by this instruction and // the memory isn't ready yet. + // TODO: this needs to be seriously cleaned up. // - if ((instruction.BS == BusSource.ReadMD || + if (instruction.BS == BusSource.ReadMD || instruction.F1 == SpecialFunction1.LoadMAR || - instruction.F2 == SpecialFunction2.StoreMD) - && !MemoryBus.Ready(MemoryOperation.Load)) //TODO: fix + instruction.F2 == SpecialFunction2.StoreMD) { - // Suspend operation for this cycle. - return false; + + MemoryOperation op; + + if (instruction.BS == BusSource.ReadMD) + { + op = MemoryOperation.Read; + } + else if(instruction.F1 == SpecialFunction1.LoadMAR) + { + op = MemoryOperation.LoadAddress; + } + else + { + op = MemoryOperation.Store; + } + + if (!_cpu._system.MemoryBus.Ready(op)) + { + // Suspend operation for this cycle. + return false; + } } + // If we have a modified next field from the last instruction, make sure it gets applied to this one. + nextModifier = _nextModifier; + _nextModifier = 0; + _rSelect = instruction.RSELECT; // Give tasks the chance to modify parameters early on (like RSELECT) @@ -234,17 +314,31 @@ namespace Contralto.CPU break; case BusSource.ReadMD: - _busData = MemoryBus.ReadMD(); + _busData = _cpu._system.MemoryBus.ReadMD(); break; case BusSource.ReadMouse: - throw new NotImplementedException("ReadMouse bus source not implemented."); - _busData = 0; // TODO: implement + //throw new NotImplementedException("ReadMouse bus source not implemented."); + _busData = 0; // TODO: implement; break; case BusSource.ReadDisp: - throw new NotImplementedException("ReadDisp bus source not implemented."); - _busData = 0; // TODO: implement; + // "The high-order bits of IR cannot be read directly, but the displacement field of IR (8 low order bits), + // may be read with the <-DISP bus source. If the X field of the instruction is zero (i.e. it specifies page 0 + // addressing) then the DISP field of the instruction is put on BUS[8-15] and BUS[0-7] is zeroed. If the X + // field of the instruction is nonzero (i.e. it specifies PC-relative or base-register addressing) then the DISP + // field is sign-extended and put on the bus." + // NB: the "X" field of the NOVA instruction is IR[6-7] + _busData = (ushort)(_cpu._ir & 0xff); + + if ((_cpu._ir & 0x300) != 0) + { + // sign extend if necessary + if ((_cpu._ir & 0x80) == 0x80) + { + _busData |= (0xff00); + } + } break; default: @@ -291,7 +385,7 @@ namespace Contralto.CPU break; case SpecialFunction1.LoadMAR: - MemoryBus.LoadMAR(aluData); // Start main memory reference + _cpu._system.MemoryBus.LoadMAR(aluData); // Start main memory reference break; case SpecialFunction1.Task: @@ -373,7 +467,7 @@ namespace Contralto.CPU break; case SpecialFunction2.StoreMD: - MemoryBus.LoadMD(_busData); + _cpu._system.MemoryBus.LoadMD(_busData); break; case SpecialFunction2.Constant: @@ -382,7 +476,7 @@ namespace Contralto.CPU default: // Let the specific task implementation take a crack at this. - ExecuteSpecialFunction2((int)instruction.F1); + ExecuteSpecialFunction2((int)instruction.F2); break; } @@ -434,9 +528,9 @@ namespace Contralto.CPU } // - // Select next address -- TODO: this is incorrect! _nextModifer should be applied to the *NEXT* instruction, not the current one! + // Select next address, using the address modifier from the last instruction. // - _mpc = (ushort)(instruction.NEXT | _nextModifier); + _mpc = (ushort)(instruction.NEXT | nextModifier); return nextTask; } @@ -743,5 +837,8 @@ namespace Contralto.CPU private long _clocks; + // The system this CPU belongs to + private AltoSystem _system; + } } diff --git a/Contralto/Contralto.csproj b/Contralto/Contralto.csproj index f4792ef..c4710ec 100644 --- a/Contralto/Contralto.csproj +++ b/Contralto/Contralto.csproj @@ -34,6 +34,8 @@ + + @@ -41,6 +43,7 @@ + @@ -48,6 +51,12 @@ + + Form + + + Debugger.cs + @@ -170,6 +179,11 @@ PreserveNewest + + + Debugger.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/Contralto/Memory/Memory.cs b/Contralto/Memory/Memory.cs index e643195..c01d535 100644 --- a/Contralto/Memory/Memory.cs +++ b/Contralto/Memory/Memory.cs @@ -10,7 +10,7 @@ namespace Contralto.Memory { public Memory() { - _mem = new ushort[0xffff]; + _mem = new ushort[0x10000]; } public ushort Read(int address) diff --git a/Contralto/Memory/MemoryBus.cs b/Contralto/Memory/MemoryBus.cs index 8664e3b..70b7d30 100644 --- a/Contralto/Memory/MemoryBus.cs +++ b/Contralto/Memory/MemoryBus.cs @@ -10,21 +10,46 @@ namespace Contralto.Memory { LoadAddress, Read, - Load + Store } - public static class MemoryBus + public class MemoryBus { - static MemoryBus() + public MemoryBus() { _mem = new Memory(); + Reset(); + } + + public void Reset() + { _memoryCycle = 0; _memoryAddress = 0; _memoryData = 0; _memoryOperationActive = false; } - public static void Clock() + public ushort MAR + { + get { return _memoryAddress; } + } + + public ushort MD + { + get { return _memoryData; } + } + + public int Cycle + { + get { return _memoryCycle; } + } + + public bool Active + { + get { return _memoryOperationActive; } + } + + public void Clock() { _memoryCycle++; if (_memoryOperationActive) @@ -50,7 +75,7 @@ namespace Contralto.Memory } } - public static bool Ready(MemoryOperation op) + public bool Ready(MemoryOperation op) { if (_memoryOperationActive) { @@ -64,7 +89,7 @@ namespace Contralto.Memory // Read operations take place on cycles 5 and 6 return _memoryCycle > 4; - case MemoryOperation.Load: + case MemoryOperation.Store: // Write operations take place on cycles 3 and 4 return _memoryCycle > 2; @@ -79,7 +104,7 @@ namespace Contralto.Memory } } - public static void LoadMAR(ushort address) + public void LoadMAR(ushort address) { if (_memoryOperationActive) { @@ -95,7 +120,7 @@ namespace Contralto.Memory } } - public static ushort ReadMD() + public ushort ReadMD() { if (_memoryOperationActive) { @@ -141,7 +166,7 @@ namespace Contralto.Memory } } - public static void LoadMD(ushort data) + public void LoadMD(ushort data) { if (_memoryOperationActive) { @@ -154,12 +179,14 @@ namespace Contralto.Memory throw new InvalidOperationException("Unexpected microcode behavior -- LoadMD during incorrect memory cycle."); case 3: + _memoryData = data; // Only really necessary to show in debugger // Start of doubleword write: WriteToBus(_memoryAddress, data); _doubleWordStore = true; break; - case 4: + case 4: + _memoryData = data; // Only really necessary to show in debugger WriteToBus(_doubleWordStore ? (ushort)(_memoryAddress ^ 1) : _memoryAddress, data); break; } @@ -172,7 +199,7 @@ namespace Contralto.Memory /// /// /// - private static ushort ReadFromBus(ushort address) + private ushort ReadFromBus(ushort address) { // TODO: actually dispatch to I/O return _mem.Read(address); @@ -184,22 +211,22 @@ namespace Contralto.Memory /// /// /// - private static void WriteToBus(ushort address, ushort data) + private void WriteToBus(ushort address, ushort data) { _mem.Load(address, data); } - private static Memory _mem; - private static bool _memoryOperationActive; - private static int _memoryCycle; - private static ushort _memoryAddress; + private Memory _mem; + private bool _memoryOperationActive; + private int _memoryCycle; + private ushort _memoryAddress; // Buffered read data (on cycles 3 and 4) - private static ushort _memoryData; - private static ushort _memoryData2; + private ushort _memoryData; + private ushort _memoryData2; // Indicates a double-word store (started on cycle 3) - private static bool _doubleWordStore; + private bool _doubleWordStore; } } diff --git a/Contralto/OctalHelpers.cs b/Contralto/OctalHelpers.cs index 0abf7c0..34b8e9f 100644 --- a/Contralto/OctalHelpers.cs +++ b/Contralto/OctalHelpers.cs @@ -12,5 +12,11 @@ namespace Contralto { return Convert.ToString(i, 8); } + + public static string ToOctal(int i, int digits) + { + string octalString = Convert.ToString(i, 8); + return new String('0', digits - octalString.Length) + octalString; + } } } diff --git a/Contralto/Program.cs b/Contralto/Program.cs index 913182b..646d01b 100644 --- a/Contralto/Program.cs +++ b/Contralto/Program.cs @@ -13,20 +13,13 @@ namespace Contralto { static void Main(string[] args) { - AltoCPU cpu = new AltoCPU(); - for(int i=0;i<2048;i++) - { - MicroInstruction inst = new MicroInstruction(UCodeMemory.UCodeROM[i]); + AltoSystem system = new AltoSystem(); - Console.WriteLine("{0}: {1}", OctalHelpers.ToOctal(i), Disassembler.DisassembleInstruction(inst, TaskType.Emulator)); - } - - while(true) - { - MemoryBus.Clock(); - cpu.ExecuteNext(); - } + // for now everything is driven through the debugger + Debugger d = new Debugger(system); + d.LoadSourceCode("Disassembly\\altoIIcode3.mu"); + d.ShowDialog(); } }