mirror of
https://github.com/livingcomputermuseum/ContrAlto.git
synced 2026-01-26 04:01:07 +00:00
Adding files that VS mysteriously excluded. I love tools.
This commit is contained in:
451
Contralto/CPU/CPU.cs
Normal file
451
Contralto/CPU/CPU.cs
Normal file
@@ -0,0 +1,451 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Contralto.CPU
|
||||
{
|
||||
|
||||
|
||||
public class AltoCPU
|
||||
{
|
||||
public AltoCPU()
|
||||
{
|
||||
_tasks[0] = new EmulatorTask(this);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
// Reset registers
|
||||
_r = new ushort[32];
|
||||
_s = new ushort[32];
|
||||
_t = 0;
|
||||
_l = 0;
|
||||
_ir = 0;
|
||||
_aluC0 = 0;
|
||||
|
||||
// Reset tasks.
|
||||
for(int i=0;i<_tasks.Length;i++)
|
||||
{
|
||||
if (_tasks[i] != null)
|
||||
{
|
||||
_tasks[i].Reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the initial task switch.
|
||||
TaskSwitch();
|
||||
|
||||
_currentTask = _nextTask;
|
||||
_nextTask = null;
|
||||
}
|
||||
|
||||
public 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
|
||||
for (int i = _tasks.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (_tasks[i] != null && _tasks[i].Wakeup)
|
||||
{
|
||||
_nextTask = _tasks[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
private class Task
|
||||
{
|
||||
public Task(AltoCPU cpu)
|
||||
{
|
||||
_wakeup = false;
|
||||
_mpc = 0xffff; // invalid, for sanity checking
|
||||
_priority = -1; // invalid
|
||||
_cpu = cpu;
|
||||
}
|
||||
|
||||
public int Priority
|
||||
{
|
||||
get { return _priority; }
|
||||
}
|
||||
|
||||
public bool Wakeup
|
||||
{
|
||||
get { return _wakeup; }
|
||||
}
|
||||
|
||||
public virtual void Reset()
|
||||
{
|
||||
// From The Alto Hardware Manual (section 2, "Initialization"):
|
||||
// "...each task start[s] at the location which is its task number"
|
||||
//
|
||||
_mpc = (ushort)_priority;
|
||||
}
|
||||
|
||||
public virtual void Block()
|
||||
{
|
||||
// Used only by hardware interfaces, where applicable
|
||||
_wakeup = false;
|
||||
}
|
||||
|
||||
public bool ExecuteNext()
|
||||
{
|
||||
// TODO: cache microinstructions (or pre-decode them) to save consing all these up every time.
|
||||
MicroInstruction instruction = new MicroInstruction(UCodeMemory.UCodeROM[_mpc]);
|
||||
return ExecuteInstruction(instruction);
|
||||
}
|
||||
|
||||
public virtual string DisassembleInstruction(ushort addr)
|
||||
{
|
||||
return String.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ExecuteInstruction causes the Task to execute the next instruction (the one
|
||||
/// _mpc is pointing to). The base implementation covers non-task specific logic; subclasses may
|
||||
/// provide their own overrides.
|
||||
/// </summary>
|
||||
/// <returns>True if a task switch has been requested by a TASK instruction, false otherwise.</returns>
|
||||
protected virtual bool ExecuteInstruction(MicroInstruction instruction)
|
||||
{
|
||||
ushort busData = 0; // from BUS
|
||||
ushort aluData = 0; // from ALU
|
||||
bool nextTask = false;
|
||||
ushort nextModifier = 0; // for branches (OR'd into NEXT field)
|
||||
|
||||
// Select BUS data.
|
||||
if (instruction.F1 != SpecialFunction1.Constant &&
|
||||
instruction.F2 != SpecialFunction2.Constant)
|
||||
{
|
||||
// Normal BUS data (not constant ROM access).
|
||||
switch (instruction.BS)
|
||||
{
|
||||
case BusSource.ReadR:
|
||||
busData = _cpu._r[instruction.RSELECT];
|
||||
break;
|
||||
|
||||
case BusSource.LoadR:
|
||||
busData = 0; // "Loading R forces the BUS to 0 so that an ALU function of 0 and T may be executed simultaneously"
|
||||
break;
|
||||
|
||||
case BusSource.None:
|
||||
busData = 0xffff; // "Enables no source to the BUS, leaving it all ones"
|
||||
break;
|
||||
|
||||
case BusSource.TaskSpecific1:
|
||||
case BusSource.TaskSpecific2:
|
||||
busData = GetBusSource((int)instruction.BS); // task specific -- call into specific implementation
|
||||
break;
|
||||
|
||||
case BusSource.ReadMD:
|
||||
busData = Memory.MD;
|
||||
break;
|
||||
|
||||
case BusSource.ReadMouse:
|
||||
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;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException(String.Format("Unhandled bus source {0}", instruction.BS));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
busData = ConstantMemory.ConstantROM[instruction.RSELECT | ((uint)instruction.BS << 5)];
|
||||
}
|
||||
|
||||
// Constant ROM access:
|
||||
// The constant memory is gated to the bus by F1=7, F2=7, or BS>4. The constant memory is addressed by the
|
||||
// (8 bit) concatenation of RSELECT and BS. The intent in enabling constants with BS>4 is to provide a masking
|
||||
// facility, particularly for the <-MOUSE and <-DISP bus sources. This works because the processor bus ANDs if
|
||||
// more than one source is gated to it. Up to 32 such mask contans can be provided for each of the four bus sources
|
||||
// > 4.
|
||||
if ((int)instruction.BS > 4 ||
|
||||
instruction.F1 == SpecialFunction1.Constant ||
|
||||
instruction.F2 == SpecialFunction2.Constant)
|
||||
{
|
||||
busData &= ConstantMemory.ConstantROM[instruction.RSELECT | ((uint)instruction.BS << 5)];
|
||||
}
|
||||
|
||||
// Do ALU operation
|
||||
aluData = ALU.Execute(instruction.ALUF, busData, _cpu._t);
|
||||
|
||||
// Reset shifter op
|
||||
Shifter.SetOperation(ShifterOp.None, 0);
|
||||
|
||||
//
|
||||
// Do Special Functions
|
||||
//
|
||||
switch(instruction.F1)
|
||||
{
|
||||
case SpecialFunction1.None:
|
||||
// Do nothing. Well, that was easy.
|
||||
break;
|
||||
|
||||
case SpecialFunction1.LoadMAR:
|
||||
Memory.LoadMAR(aluData); // Start main memory reference
|
||||
break;
|
||||
|
||||
case SpecialFunction1.Task:
|
||||
nextTask = true; // Yield to other more important tasks
|
||||
break;
|
||||
|
||||
case SpecialFunction1.Block:
|
||||
// "...this function is reserved by convention only; it is *not* done by the microprocessor"
|
||||
throw new InvalidOperationException("BLOCK should never be invoked by microcode.");
|
||||
break;
|
||||
|
||||
case SpecialFunction1.LLSH1:
|
||||
Shifter.SetOperation(ShifterOp.ShiftLeft, 1);
|
||||
break;
|
||||
|
||||
case SpecialFunction1.LRSH1:
|
||||
Shifter.SetOperation(ShifterOp.ShiftRight, 1);
|
||||
break;
|
||||
|
||||
case SpecialFunction1.LLCY8:
|
||||
Shifter.SetOperation(ShifterOp.RotateLeft, 8);
|
||||
break;
|
||||
|
||||
case SpecialFunction1.Constant:
|
||||
// Ignored here; handled by Constant ROM access logic above.
|
||||
break;
|
||||
|
||||
default:
|
||||
// Let the specific task implementation take a crack at this.
|
||||
ExecuteSpecialFunction1((int)instruction.F1);
|
||||
break;
|
||||
}
|
||||
|
||||
switch(instruction.F2)
|
||||
{
|
||||
case SpecialFunction2.None:
|
||||
// Nothing!
|
||||
break;
|
||||
|
||||
case SpecialFunction2.BusEq0:
|
||||
if (busData == 0)
|
||||
{
|
||||
nextModifier = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case SpecialFunction2.ShLt0:
|
||||
//
|
||||
// Note:
|
||||
// "the value of SHIFTER OUTPUT is determined by the value of L as the microinstruction
|
||||
// *begins* execution and the shifter function specified during the *current* microinstruction.
|
||||
//
|
||||
// Since we haven't modifed L yet, and we've selected the shifter function above, we're good to go here.
|
||||
//
|
||||
if ((short)Shifter.DoOperation(_cpu._l) < 0)
|
||||
{
|
||||
nextModifier = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case SpecialFunction2.ShEq0:
|
||||
// See note above.
|
||||
if (Shifter.DoOperation(_cpu._l) == 0)
|
||||
{
|
||||
nextModifier = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case SpecialFunction2.Bus:
|
||||
// Select bits 6-15 (bits 0-9 in modern parlance) of the bus
|
||||
nextModifier = (ushort)(busData & 0x3ff);
|
||||
break;
|
||||
|
||||
case SpecialFunction2.ALUCY:
|
||||
// ALUC0 is the carry produced by the ALU during the most recent microinstruction
|
||||
// that loaded L. It is *not* the carry produced during the execution of the microinstruction
|
||||
// that contains the ALUCY function.
|
||||
nextModifier = _cpu._aluC0;
|
||||
break;
|
||||
|
||||
case SpecialFunction2.StoreMD:
|
||||
Memory.StoreMD(busData);
|
||||
break;
|
||||
|
||||
case SpecialFunction2.Constant:
|
||||
// Ignored here; handled by Constant ROM access logic above.
|
||||
break;
|
||||
|
||||
default:
|
||||
// Let the specific task implementation take a crack at this.
|
||||
ExecuteSpecialFunction2((int)instruction.F1);
|
||||
}
|
||||
|
||||
//
|
||||
// Write back to registers:
|
||||
//
|
||||
|
||||
// Load T
|
||||
if (instruction.LoadT)
|
||||
{
|
||||
// Does this operation change the source for T?
|
||||
bool loadTFromALU = false;
|
||||
switch(instruction.ALUF)
|
||||
{
|
||||
case AluFunction.Bus:
|
||||
case AluFunction.BusOrT:
|
||||
case AluFunction.BusPlus1:
|
||||
case AluFunction.BusMinus1:
|
||||
case AluFunction.BusPlusTPlus1:
|
||||
case AluFunction.BusPlusSkip:
|
||||
case AluFunction.AluBusAndT:
|
||||
loadTFromALU = true;
|
||||
break;
|
||||
}
|
||||
|
||||
_cpu._t = loadTFromALU ? aluData : busData;
|
||||
}
|
||||
|
||||
// Load L
|
||||
if (instruction.LoadL)
|
||||
{
|
||||
_cpu._l = aluData;
|
||||
|
||||
// Save ALUC0 for use in the next ALUCY special function.
|
||||
_cpu._aluC0 = ALU.Carry;
|
||||
}
|
||||
|
||||
// Do shifter
|
||||
|
||||
// Do writeback to selected R register from shifter output
|
||||
_cpu._r[instruction.RSELECT] = Shifter.DoOperation(_cpu._l);
|
||||
|
||||
//
|
||||
// Select next address
|
||||
//
|
||||
_mpc = (ushort)(instruction.NEXT | nextModifier);
|
||||
return nextTask;
|
||||
}
|
||||
|
||||
|
||||
protected abstract ushort GetBusSource(int bs);
|
||||
protected abstract void ExecuteSpecialFunction1(int f1);
|
||||
protected abstract void ExecuteSpecialFunction2(int f2);
|
||||
|
||||
protected AltoCPU _cpu;
|
||||
protected ushort _mpc;
|
||||
protected int _priority;
|
||||
protected bool _wakeup;
|
||||
}
|
||||
|
||||
private class EmulatorTask : Task
|
||||
{
|
||||
public EmulatorTask(AltoCPU cpu) : base(cpu)
|
||||
{
|
||||
_priority = 0;
|
||||
|
||||
// The Wakeup signal is always true for the Emulator task.
|
||||
_wakeup = true;
|
||||
}
|
||||
|
||||
public override string DisassembleInstruction(ushort addr)
|
||||
{
|
||||
return base.DisassembleInstruction(addr);
|
||||
}
|
||||
|
||||
protected override ushort GetBusSource(int bs)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void ExecuteSpecialFunction1(int f1)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void ExecuteSpecialFunction2(int f2)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
enum EmulatorF1
|
||||
{
|
||||
SWMODE = 8,
|
||||
WRTRAM = 9,
|
||||
RDRAM = 10,
|
||||
LoadRMR = 11,
|
||||
Unused = 12,
|
||||
LoadESRB = 13,
|
||||
RSNF = 14,
|
||||
STARTF = 15,
|
||||
}
|
||||
|
||||
enum EmulatorF2
|
||||
{
|
||||
BUSODD = 8,
|
||||
MAGIC = 9,
|
||||
LoadDNS = 10,
|
||||
ACDEST = 11,
|
||||
LoadIR = 12,
|
||||
IDISP = 13,
|
||||
ACSOURCE = 14,
|
||||
Unused = 15,
|
||||
}
|
||||
|
||||
enum EmulatorBusSource
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// AltoCPU registers
|
||||
ushort _t;
|
||||
ushort _l;
|
||||
ushort _m;
|
||||
ushort _mar;
|
||||
ushort _ir;
|
||||
|
||||
ushort[] _r;
|
||||
ushort[] _s;
|
||||
|
||||
// Stores the last carry from the ALU on a Load L
|
||||
private ushort _aluC0;
|
||||
|
||||
// Task data
|
||||
private Task _nextTask; // The task to switch two after the next microinstruction
|
||||
private Task _currentTask; // The currently executing task
|
||||
private Task[] _tasks = new Task[16];
|
||||
|
||||
private long _clocks;
|
||||
|
||||
}
|
||||
}
|
||||
91
Contralto/CPU/MicroInstruction.cs
Normal file
91
Contralto/CPU/MicroInstruction.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Contralto.CPU
|
||||
{
|
||||
//
|
||||
// From Alto Hardware Manual, Section 2.1.
|
||||
// These are the non-task specific definitions.
|
||||
//
|
||||
public enum BusSource
|
||||
{
|
||||
ReadR = 0,
|
||||
LoadR = 1,
|
||||
None = 2,
|
||||
TaskSpecific1 = 3,
|
||||
TaskSpecific2 = 4,
|
||||
ReadMD = 5,
|
||||
ReadMouse = 6 ,
|
||||
ReadDisp = 7,
|
||||
}
|
||||
|
||||
public enum SpecialFunction1
|
||||
{
|
||||
None = 0,
|
||||
LoadMAR = 1,
|
||||
Task = 2,
|
||||
Block = 3,
|
||||
LLSH1 = 4,
|
||||
LRSH1 = 5,
|
||||
LLCY8 = 6,
|
||||
Constant = 7,
|
||||
}
|
||||
|
||||
public enum SpecialFunction2
|
||||
{
|
||||
None = 0,
|
||||
BusEq0 = 1,
|
||||
ShLt0 = 2,
|
||||
ShEq0 = 3,
|
||||
Bus = 4,
|
||||
ALUCY = 5,
|
||||
StoreMD = 6,
|
||||
Constant = 7,
|
||||
}
|
||||
|
||||
public enum AluFunction
|
||||
{
|
||||
Bus = 0,
|
||||
BusOrT = 1,
|
||||
BusAndT = 2,
|
||||
BusXorT = 3,
|
||||
BusPlus1 = 4,
|
||||
BusMinus1 = 5,
|
||||
BusPlusT = 6,
|
||||
BusMinusT = 7,
|
||||
BusMinusTMinus1 = 8,
|
||||
BusPlusTPlus1 = 9,
|
||||
BusPlusSkip = 10,
|
||||
AluBusAndT = 11,
|
||||
BusAndNotT = 12,
|
||||
Undefined1 = 13,
|
||||
Undefined2 = 14,
|
||||
}
|
||||
|
||||
public class MicroInstruction
|
||||
{
|
||||
public MicroInstruction(UInt32 code)
|
||||
{
|
||||
RSELECT = (code & 0xf8000000) >> 27;
|
||||
ALUF = (AluFunction)((code & 0x07800000) >> 23);
|
||||
BS = (BusSource)((code & 0x00700000) >> 20);
|
||||
F1 = (SpecialFunction1)((code & 0x000f0000) >> 16);
|
||||
F2 = (SpecialFunction2)((code & 0x0000f000) >> 12);
|
||||
LoadT = ((code & 0x00000800) >> 11) == 0 ? false : true;
|
||||
LoadL = ((code & 0x00000400) >> 10) == 0 ? false : true;
|
||||
NEXT = (ushort)(code & 0x3ff);
|
||||
}
|
||||
|
||||
public UInt32 RSELECT;
|
||||
public AluFunction ALUF;
|
||||
public BusSource BS;
|
||||
public SpecialFunction1 F1;
|
||||
public SpecialFunction2 F2;
|
||||
public bool LoadT;
|
||||
public bool LoadL;
|
||||
public ushort NEXT;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user