1
0
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:
Josh Dersch
2015-08-20 09:55:26 -07:00
parent 3b77ba875d
commit 5719ec4815
2 changed files with 542 additions and 0 deletions

451
Contralto/CPU/CPU.cs Normal file
View 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;
}
}

View 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;
}
}