1
0
mirror of https://github.com/livingcomputermuseum/ContrAlto.git synced 2026-01-18 17:07:52 +00:00

Refinement to CPU, implemented very rough diassembler and began annotation of official Xerox ucode sources with PROM addresses.

This commit is contained in:
Josh Dersch 2015-08-28 18:07:59 -07:00
parent 9c3ee3afac
commit 59d98d1909
11 changed files with 3086 additions and 87 deletions

View File

@ -13,6 +13,11 @@ namespace Contralto.CPU
static class ALU
{
static ALU()
{
Reset();
}
public static void Reset()
{
_carry = 0;
}

View File

@ -6,14 +6,24 @@ using System.Text;
using System.Threading.Tasks;
namespace Contralto.CPU
{
{
public enum TaskType
{
Emulator = 0,
DiskSector = 4,
Ethernet = 5,
DiskWord = 9,
Cursor = 10,
DisplayHorizontal = 11,
DisplayVertical = 12,
Refresh = 14,
}
public class AltoCPU
{
public AltoCPU()
{
_tasks[0] = new EmulatorTask(this);
_tasks[(int)TaskType.Emulator] = new EmulatorTask(this);
Reset();
}
@ -22,14 +32,22 @@ namespace Contralto.CPU
{
// Reset registers
_r = new ushort[32];
_s = new ushort[32];
_s = new ushort[8][];
for(int i=0;i<_s.Length;i++)
{
_s[i] = new ushort[32];
}
_t = 0;
_l = 0;
_m = 0;
_ir = 0;
_aluC0 = 0;
_rb = 0;
// Reset tasks.
for(int i=0;i<_tasks.Length;i++)
for (int i=0;i<_tasks.Length;i++)
{
if (_tasks[i] != null)
{
@ -66,6 +84,32 @@ namespace Contralto.CPU
_clocks++;
}
/// <summary>
/// Used by hardware devices to cause a specific task to have its
/// "wakeup" signal triggered
/// </summary>
/// <param name="task"></param>
public void WakeupTask(int task)
{
if (_tasks[task] != null)
{
_tasks[task].WakeupTask();
}
}
/// <summary>
/// Used by hardware devices to cause a specific task to have its
/// "wakeup" signal cleared
/// </summary>
/// <param name="task"></param>
public void BlockTask(int task)
{
if (_tasks[task] != null)
{
_tasks[task].BlockTask();
}
}
private void TaskSwitch()
{
// Select the highest-priority eligible task
@ -110,12 +154,17 @@ namespace Contralto.CPU
_mpc = (ushort)_priority;
}
public virtual void Block()
public virtual void BlockTask()
{
// Used only by hardware interfaces, where applicable
_wakeup = false;
}
public virtual void WakeupTask()
{
_wakeup = true;
}
public bool ExecuteNext()
{
// TODO: cache microinstructions (or pre-decode them) to save consing all these up every time.
@ -123,11 +172,6 @@ namespace Contralto.CPU
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
@ -135,12 +179,17 @@ namespace Contralto.CPU
/// </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)
bool loadR = false;
ushort aluData = 0;
_loadS = false;
_rSelect = 0;
_busData = 0;
Shifter.SetMagic(false);
//
// Wait for memory state machine if a memory operation is requested by this instruction and
// the memory isn't ready yet.
@ -148,12 +197,17 @@ namespace Contralto.CPU
if ((instruction.BS == BusSource.ReadMD ||
instruction.F1 == SpecialFunction1.LoadMAR ||
instruction.F2 == SpecialFunction2.StoreMD)
&& !MemoryBus.Ready())
&& !MemoryBus.Ready(MemoryOperation.Load)) //TODO: fix
{
// Suspend operation for this cycle.
return false;
}
_rSelect = instruction.RSELECT;
// Give tasks the chance to modify parameters early on (like RSELECT)
ExecuteSpecialFunction2Early((int)instruction.F2);
// Select BUS data.
if (instruction.F1 != SpecialFunction1.Constant &&
instruction.F2 != SpecialFunction2.Constant)
@ -162,44 +216,46 @@ namespace Contralto.CPU
switch (instruction.BS)
{
case BusSource.ReadR:
busData = _cpu._r[instruction.RSELECT];
_busData = _cpu._r[_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"
_busData = 0; // "Loading R forces the BUS to 0 so that an ALU function of 0 and T may be executed simultaneously"
loadR = true;
break;
case BusSource.None:
busData = 0xffff; // "Enables no source to the BUS, leaving it all ones"
_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
_busData = GetBusSource((int)instruction.BS); // task specific -- call into specific implementation
break;
case BusSource.ReadMD:
// TODO: wait for MD if not ready.
busData = MemoryBus.ReadMD();
_busData = MemoryBus.ReadMD();
break;
case BusSource.ReadMouse:
throw new NotImplementedException("ReadMouse bus source not implemented.");
busData = 0; // TODO: implement
_busData = 0; // TODO: implement
break;
case BusSource.ReadDisp:
throw new NotImplementedException("ReadDisp bus source not implemented.");
busData = 0; // TODO: implement;
_busData = 0; // TODO: implement;
break;
default:
throw new InvalidOperationException(String.Format("Unhandled bus source {0}", instruction.BS));
throw new InvalidOperationException(String.Format("Unhandled bus source {0}.", instruction.BS));
break;
}
}
else
{
busData = ConstantMemory.ConstantROM[instruction.RSELECT | ((uint)instruction.BS << 5)];
// See also comments below.
_busData = ConstantMemory.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)];
}
// Constant ROM access:
@ -208,15 +264,19 @@ namespace Contralto.CPU
// 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.
// NOTE also:
// "Note that the [emulator task F2] functions which replace the low bits of RSELECT with IR aaffect only the
// selection of R; they do not affect the address supplied to the constant ROM."
// Hence we use the unmodified RSELECT value here and above.
if ((int)instruction.BS > 4 ||
instruction.F1 == SpecialFunction1.Constant ||
instruction.F2 == SpecialFunction2.Constant)
{
busData &= ConstantMemory.ConstantROM[instruction.RSELECT | ((uint)instruction.BS << 5)];
_busData &= ConstantMemory.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)];
}
// Do ALU operation
aluData = ALU.Execute(instruction.ALUF, busData, _cpu._t);
aluData = ALU.Execute(instruction.ALUF, _busData, _cpu._t);
// Reset shifter op
Shifter.SetOperation(ShifterOp.None, 0);
@ -272,9 +332,9 @@ namespace Contralto.CPU
break;
case SpecialFunction2.BusEq0:
if (busData == 0)
if (_busData == 0)
{
nextModifier = 1;
_nextModifier = 1;
}
break;
@ -286,34 +346,34 @@ namespace Contralto.CPU
//
// 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)
if ((short)Shifter.DoOperation(_cpu._l, _cpu._t) < 0)
{
nextModifier = 1;
_nextModifier = 1;
}
break;
case SpecialFunction2.ShEq0:
// See note above.
if (Shifter.DoOperation(_cpu._l) == 0)
if (Shifter.DoOperation(_cpu._l, _cpu._t) == 0)
{
nextModifier = 1;
_nextModifier = 1;
}
break;
case SpecialFunction2.Bus:
// Select bits 6-15 (bits 0-9 in modern parlance) of the bus
nextModifier = (ushort)(busData & 0x3ff);
_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;
_nextModifier = _cpu._aluC0;
break;
case SpecialFunction2.StoreMD:
MemoryBus.LoadMD(busData);
MemoryBus.LoadMD(_busData);
break;
case SpecialFunction2.Constant:
@ -348,41 +408,76 @@ namespace Contralto.CPU
break;
}
_cpu._t = loadTFromALU ? aluData : busData;
_cpu._t = loadTFromALU ? aluData : _busData;
}
// Load L
// Load L (and M) from ALU
if (instruction.LoadL)
{
_cpu._l = aluData;
_cpu._m = aluData;
// Save ALUC0 for use in the next ALUCY special function.
_cpu._aluC0 = (ushort)ALU.Carry;
}
// Do shifter
// Do writeback to selected R register from shifter output
_cpu._r[instruction.RSELECT] = Shifter.DoOperation(_cpu._l);
if (loadR)
{
_cpu._r[_rSelect] = Shifter.DoOperation(_cpu._l, _cpu._t);
}
// Do writeback to selected R register from M
if (_loadS)
{
_cpu._s[_cpu._rb][_rSelect] = _cpu._m;
}
//
// Select next address
// Select next address -- TODO: this is incorrect! _nextModifer should be applied to the *NEXT* instruction, not the current one!
//
_mpc = (ushort)(instruction.NEXT | nextModifier);
_mpc = (ushort)(instruction.NEXT | _nextModifier);
return nextTask;
}
}
protected abstract ushort GetBusSource(int bs);
protected abstract void ExecuteSpecialFunction1(int f1);
/// <summary>
/// Used to allow Task-specific F2s that need to modify RSELECT to do so.
/// </summary>
/// <param name="f2"></param>
protected virtual void ExecuteSpecialFunction2Early(int f2)
{
// Nothing by default.
}
protected abstract void ExecuteSpecialFunction2(int f2);
//
// Per uInstruction Task Data:
// Modified by both the base Task implementation and any subclasses
//
// TODO: maybe instead of these being shared (which feels kinda bad)
// these could be encapsulated in an object and passed to subclass implementations?
protected ushort _busData; // Data placed onto the bus (ANDed from multiple sources)
protected ushort _nextModifier; // Bits ORed onto the NEXT field of the current instruction
protected uint _rSelect; // RSELECT field from current instruction, potentially modified by task
protected bool _loadS; // Whether to load S from M at and of cycle
//
// Global Task Data
//
protected AltoCPU _cpu;
protected ushort _mpc;
protected int _priority;
protected bool _wakeup;
}
/// <summary>
/// EmulatorTask provides emulator (NOVA instruction set) specific operations.
/// </summary>
private class EmulatorTask : Task
{
public EmulatorTask(AltoCPU cpu) : base(cpu)
@ -393,66 +488,250 @@ namespace Contralto.CPU
_wakeup = true;
}
public override string DisassembleInstruction(ushort addr)
public override void BlockTask()
{
return base.DisassembleInstruction(addr);
throw new InvalidOperationException("The emulator task cannot be blocked.");
}
public override void WakeupTask()
{
throw new InvalidOperationException("The emulator task is always in wakeup state.");
}
protected override ushort GetBusSource(int bs)
{
throw new NotImplementedException();
EmulatorBusSource ebs = (EmulatorBusSource)bs;
switch(ebs)
{
case EmulatorBusSource.ReadSLocation:
return _cpu._s[_cpu._rb][_rSelect];
case EmulatorBusSource.LoadSLocation:
_loadS = true;
return 0; // TODO: technically this is an "undefined value" not zero.
default:
throw new InvalidOperationException(String.Format("Unhandled bus source {0}", bs));
}
}
protected override void ExecuteSpecialFunction1(int f1)
{
throw new NotImplementedException();
EmulatorF1 ef1 = (EmulatorF1)f1;
switch (ef1)
{
case EmulatorF1.RSNF:
// TODO: make configurable
// "...decoded by the Ethernet interface, which gates the host address wired on the
// backplane onto BUS[8-15]. BUS[0-7] is not driven and will therefore be -1. If
// no Ethernet interface is present, BUS will be -1.
//
_busData &= (0xff00 | 0x42);
break;
case EmulatorF1.STARTF:
// Dispatch function to I/O based on contents of AC0... (TBD: what are these?)
throw new NotImplementedException();
break;
default:
throw new InvalidOperationException(String.Format("Unhandled emulator F1 {0}.", ef1));
}
}
protected override void ExecuteSpecialFunction2Early(int f2)
{
EmulatorF2 ef2 = (EmulatorF2)f2;
switch (ef2)
{
case EmulatorF2.ACSOURCE:
// Early: modify R select field:
// "...it replaces the two-low order bits of the R select field with
// the complement of the SrcAC field of IR, (IR[1-2] XOR 3), allowing the emulator
// to address its accumulators (which are assigned to R0-R3)."
_rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x6000) >> 13) ^ 3);
break;
case EmulatorF2.ACDEST:
// "...causes (IR[3-4] XOR 3) to be used as the low-order two bits of the RSELECT field.
// This address the accumulators from the destination field of the instruction. The selected
// register may be loaded or read."
_rSelect = (_rSelect & 0xfffc) | ((((uint)_cpu._ir & 0x1800) >> 11) ^ 3);
break;
}
}
protected override void ExecuteSpecialFunction2(int f2)
{
throw new NotImplementedException();
}
EmulatorF2 ef2 = (EmulatorF2)f2;
switch (ef2)
{
case EmulatorF2.LoadIR:
// based on block diagram, this always comes from the bus
_cpu._ir = _busData;
enum EmulatorF1
{
SWMODE = 8,
WRTRAM = 9,
RDRAM = 10,
LoadRMR = 11,
Unused = 12,
LoadESRB = 13,
RSNF = 14,
STARTF = 15,
}
// "IR<- also merges bus bits 0, 5, 6 and 7 into NEXT[6-9] which does a first level
// instruction dispatch."
// TODO: is this an AND or an OR operation? (how is the "merge" done?)
// Assuming for now this is an OR operation like everything else that modifies NEXT.
_nextModifier = (ushort)(((_busData & 0x8000) >> 6) | ((_busData & 0x0700) >> 2));
break;
enum EmulatorF2
{
BUSODD = 8,
MAGIC = 9,
LoadDNS = 10,
ACDEST = 11,
LoadIR = 12,
IDISP = 13,
ACSOURCE = 14,
Unused = 15,
}
case EmulatorF2.IDISP:
// "The IDISP function (F2=15B) does a 16 way dispatch under control of a PROM and a
// multiplexer. The values are tabulated below:
// Conditions ORed onto NEXT Comment
//
// if IR[0] = 1 3-IR[8-9] complement of SH field of IR
// elseif IR[1-2] = 0 IR[3-4] JMP, JSR, ISZ, DSZ
// elseif IR[1-2] = 1 4 LDA
// elseif IR[1-2] = 2 5 STA
// elseif IR[4-7] = 0 1
// elseif IR[4-7] = 1 0
// elseif IR[4-7] = 6 16B CONVERT
// elseif IR[4-7] = 16B 6
// else IR[4-7]
// NB: as always, Xerox labels bits in the opposite order from modern convention;
// (bit 0 is bit 15...)
if ((_cpu._ir & 0x8000) != 0)
{
_nextModifier = (ushort)(3 - ((_cpu._ir & 0xc0) >> 6));
}
else if((_cpu._ir & 0x6000) == 0)
{
_nextModifier = (ushort)((_cpu._ir & 0x1800) >> 11);
}
else if((_cpu._ir & 0x6000) == 0x4000)
{
_nextModifier = 4;
}
else if ((_cpu._ir & 0x6000) == 0x6000)
{
_nextModifier = 5;
}
else if ((_cpu._ir & 0x0f00) == 0)
{
_nextModifier = 1;
}
else if ((_cpu._ir & 0x0f00) == 0x0100)
{
_nextModifier = 0;
}
else if ((_cpu._ir & 0x0f00) == 0x0600)
{
_nextModifier = 0xe;
}
else if ((_cpu._ir & 0x0f00) == 0x0e00)
{
_nextModifier = 0x6;
}
else
{
_nextModifier = (ushort)((_cpu._ir & 0x0f00) >> 8);
}
break;
enum EmulatorBusSource
{
case EmulatorF2.ACSOURCE:
// Late:
// "...a dispatch is performed:
// Conditions ORed onto NEXT Comment
//
// if IR[0] = 1 3-IR[8-9] complement of SH field of IR
// if IR[1-2] = 3 IR[5] the Indirect bit of R
// if IR[3-7] = 0 2 CYCLE
// if IR[3-7] = 1 5 RAMTRAP
// if IR[3-7] = 2 3 NOPAR -- parameterless opcode group
// if IR[3-7] = 3 6 RAMTRAP
// if IR[3-7] = 4 7 RAMTRAP
// if IR[3-7] = 11B 4 JSRII
// if IR[3-7] = 12B 4 JSRIS
// 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;
}
break;
}
case EmulatorF2.ACDEST:
// Handled in early handler
break;
case EmulatorF2.BUSODD:
// "...merges BUS[15] into NEXT[9]."
// TODO: is this an AND or an OR?
_nextModifier = (ushort)((_nextModifier & 0xffbf) | ((_busData & 0x1) << 6));
break;
case EmulatorF2.MAGIC:
Shifter.SetMagic(true);
break;
default:
throw new InvalidOperationException(String.Format("Unhandled emulator F2 {0}.", ef2));
}
}
}
// AltoCPU registers
ushort _t;
ushort _l;
ushort _m;
ushort _mar;
ushort _m;
ushort _ir;
// R and S register files and bank select
ushort[] _r;
ushort[] _s;
ushort[][] _s;
ushort _rb; // S register bank select
// Stores the last carry from the ALU on a Load L
private ushort _aluC0;

View File

@ -7,8 +7,6 @@ using System.Threading.Tasks;
namespace Contralto.CPU
{
static class ConstantMemory
{
static ConstantMemory()

View File

@ -0,0 +1,410 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Contralto.CPU
{
// BUG: register assignments should come from L (not 0)
public static class Disassembler
{
/// <summary>
/// Disassembles the specified microinstruction for the specified Task type.
/// </summary>
/// <param name="instruction">The microinstruction to disassemble</param>
/// <param name="task">The task to interpret the microinstruction for</param>
public static string DisassembleInstruction(MicroInstruction instruction, TaskType task)
{
StringBuilder disassembly = new StringBuilder();
uint rSelect = instruction.RSELECT;
bool loadR = false;
bool loadS = false;
string source = string.Empty;
string operation = string.Empty;
string f1 = string.Empty;
string f2 = string.Empty;
string loadT = string.Empty;
string loadL = string.Empty;
// 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:
source = String.Format("R[{0}] ", OctalHelpers.ToOctal((int)rSelect));
break;
case BusSource.LoadR:
source = "0 ";
loadR = true;
break;
case BusSource.None:
source = "177777 ";
break;
case BusSource.TaskSpecific1:
case BusSource.TaskSpecific2:
source = DisassembleBusSource(instruction, task, out loadS); // task specific -- call into specific implementation
break;
case BusSource.ReadMD:
source = "MD ";
break;
case BusSource.ReadMouse:
source = "MOUSE ";
break;
case BusSource.ReadDisp:
source = "DISP ";
break;
}
}
if ((int)instruction.BS > 4 ||
instruction.F1 == SpecialFunction1.Constant ||
instruction.F2 == SpecialFunction2.Constant)
{
source += String.Format("C({0})",
OctalHelpers.ToOctal(ConstantMemory.ConstantROM[(instruction.RSELECT << 3) | ((uint)instruction.BS)]));
}
switch (instruction.ALUF)
{
case AluFunction.Bus:
operation = source;
break;
case AluFunction.T:
operation = "T ";
break;
case AluFunction.BusOrT:
operation = String.Format("{0} or T ", source);
break;
case AluFunction.BusAndT:
operation = String.Format("{0} and T ", source);
break;
case AluFunction.BusXorT:
operation = String.Format("{0} xor T ", source);
break;
case AluFunction.BusPlus1:
operation = String.Format("{0} + 1 ", source);
break;
case AluFunction.BusMinus1:
operation = String.Format("{0} - 1 ", source);
break;
case AluFunction.BusPlusT:
operation = String.Format("{0} + T ", source);
break;
case AluFunction.BusMinusT:
operation = String.Format("{0} - T ", source);
break;
case AluFunction.BusMinusTMinus1:
operation = String.Format("{0} - T - 1 ", source);
break;
case AluFunction.BusPlusTPlus1:
operation = String.Format("{0} + T + 1 ", source);
break;
case AluFunction.BusPlusSkip:
operation = String.Format("{0} + SKIP ", source);
break;
case AluFunction.AluBusAndT:
operation = String.Format("{0}.T ", source);
break;
case AluFunction.BusAndNotT:
operation = String.Format("{0} and not T ", source);
break;
default:
operation = "Undefined ALU operation ";
break;
}
switch (instruction.F1)
{
case SpecialFunction1.None:
f1 = string.Empty;
break;
case SpecialFunction1.LoadMAR:
f1 = "MAR<- ";
break;
case SpecialFunction1.Task:
f1 = "TASK ";
break;
case SpecialFunction1.Block:
// "...this function is reserved by convention only; it is *not* done by the microprocessor"
f1 = "BLOCK "; // throw new InvalidOperationException("BLOCK should never be invoked by microcode.");
break;
case SpecialFunction1.LLSH1:
f1 = "<-L LSH 1 ";
break;
case SpecialFunction1.LRSH1:
f1 = "<-L RSH 1 ";
break;
case SpecialFunction1.LLCY8:
f1 = "<-L LCY 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.
f1 = DisassembleSpecialFunction1(instruction, task);
break;
}
switch (instruction.F2)
{
case SpecialFunction2.None:
f2 = string.Empty;
break;
case SpecialFunction2.BusEq0:
f2 = "BUS=0 ";
break;
case SpecialFunction2.ShLt0:
f2 = "SH<0 ";
break;
case SpecialFunction2.ShEq0:
f2 = "SH=0 ";
break;
case SpecialFunction2.Bus:
f2 = "BUS ";
break;
case SpecialFunction2.ALUCY:
f2 = "ALUCY ";
break;
case SpecialFunction2.StoreMD:
f2 = "MD<- ";
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.
f2 = DisassembleSpecialFunction2(instruction, task);
break;
}
//
// 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;
}
loadT = String.Format("T<- {0}", loadTFromALU ? operation : source);
}
// Load L (and M) from ALU
if (instruction.LoadL)
{
if (string.IsNullOrEmpty(loadT))
{
loadL = String.Format("L<- {0}", operation);
}
else
{
loadL = String.Format("L<- {0}", loadT);
}
}
// Do writeback to selected R register from shifter output
if (loadR)
{
loadL = String.Format("R[{0}]<- {1}", OctalHelpers.ToOctal((int)rSelect), loadL != String.Empty ? loadL : operation);
}
// Do writeback to selected S register from M
if (loadS)
{
loadL = String.Format("S[{0}]<- {1}", OctalHelpers.ToOctal((int)rSelect), loadL);
}
if (!string.IsNullOrEmpty(loadL) || !string.IsNullOrEmpty(loadT))
{
disassembly.AppendFormat("{0}{1}{2}{3} :{4}", f1, f2, loadT, loadL, OctalHelpers.ToOctal(instruction.NEXT));
}
else
{
disassembly.AppendFormat("{0}{1}{2} :{3}", f1, f2, operation, OctalHelpers.ToOctal(instruction.NEXT));
}
return disassembly.ToString();
}
private static string DisassembleBusSource(MicroInstruction instruction, TaskType task, out bool loadS)
{
switch(task)
{
case TaskType.Emulator:
return DisassembleEmulatorBusSource(instruction, out loadS);
default:
loadS = false;
return String.Format("BS{0}", OctalHelpers.ToOctal((int)instruction.BS));
}
}
private static string DisassembleSpecialFunction1(MicroInstruction instruction, TaskType task)
{
switch (task)
{
case TaskType.Emulator:
return DisassembleEmulatorSpecialFunction1(instruction);
default:
return String.Format("F1{0}", OctalHelpers.ToOctal((int)instruction.F1));
}
}
private static string DisassembleSpecialFunction2(MicroInstruction instruction, TaskType task)
{
switch (task)
{
case TaskType.Emulator:
return DisassembleEmulatorSpecialFunction2(instruction);
default:
return String.Format("F2{0}", OctalHelpers.ToOctal((int)instruction.F2));
}
}
private static string DisassembleEmulatorBusSource(MicroInstruction instruction, out bool loadS)
{
EmulatorBusSource bs = (EmulatorBusSource)instruction.BS;
switch(bs)
{
case EmulatorBusSource.ReadSLocation:
loadS = false;
return String.Format("<-S[{0}]", OctalHelpers.ToOctal((int)instruction.RSELECT));
case EmulatorBusSource.LoadSLocation:
loadS = true;
return String.Empty;
default:
loadS = false;
throw new InvalidOperationException(String.Format("Unhandled Emulator BS {0}", bs));
}
}
private static string DisassembleEmulatorSpecialFunction1(MicroInstruction instruction)
{
EmulatorF1 ef1 = (EmulatorF1)instruction.F1;
switch(ef1)
{
case EmulatorF1.SWMODE:
return "SWMODE ";
case EmulatorF1.WRTRAM:
return "WRTRAM ";
case EmulatorF1.RDRAM:
return "RDRAM ";
case EmulatorF1.LoadRMR:
return "RMR<- ";
case EmulatorF1.LoadESRB:
return "ESRB<- ";
case EmulatorF1.RSNF:
return "RSNF ";
case EmulatorF1.STARTF:
return "STARTF ";
default:
return String.Format("F1{0}", OctalHelpers.ToOctal((int)ef1));
}
}
private static string DisassembleEmulatorSpecialFunction2(MicroInstruction instruction)
{
EmulatorF2 ef2 = (EmulatorF2)instruction.F2;
switch (ef2)
{
case EmulatorF2.ACDEST:
return "ACDEST ";
case EmulatorF2.ACSOURCE:
return "ACSOURCE ";
case EmulatorF2.MAGIC:
return "MAGIC ";
case EmulatorF2.LoadDNS:
return "DNS<- ";
case EmulatorF2.BUSODD:
return "BUSODD ";
case EmulatorF2.LoadIR:
return "IR<- ";
case EmulatorF2.IDISP:
return "IDISP ";
default:
return String.Format("F2{0}", OctalHelpers.ToOctal((int)ef2));
}
}
}
}

View File

@ -66,6 +66,39 @@ namespace Contralto.CPU
Undefined2 = 15,
}
//
// Task-specific enumerations follow
//
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
{
ReadSLocation = 3, // <-SLOCATION: read from S reg into M
LoadSLocation = 4, // SLOCATION<- store to S reg from M
}
public class MicroInstruction
{
public MicroInstruction(UInt32 code)

View File

@ -13,7 +13,7 @@ namespace Contralto.CPU
ShiftLeft,
ShiftRight,
RotateLeft,
RotateRight
RotateRight,
}
public static class Shifter
@ -21,14 +21,28 @@ namespace Contralto.CPU
static Shifter()
{
_op = ShifterOp.Invalid;
_count = 0;
_magic = false;
}
public static void SetOperation(ShifterOp op, int count)
{
_op = op;
_count = count;
}
public static ushort DoOperation(ushort input)
public static void SetMagic(bool magic)
{
_magic = magic;
}
/// <summary>
/// Does the last specified operation to the specified inputs
/// </summary>
/// <param name="input">Normal input to be shifted</param>
/// <param name="t">CPU t register, for MAGIC shifts only</param>
/// <returns></returns>
public static ushort DoOperation(ushort input, ushort t)
{
ushort output = 0;
switch(_op)
@ -77,5 +91,6 @@ namespace Contralto.CPU
private static ShifterOp _op;
private static int _count;
private static bool _magic;
}
}

View File

@ -44,17 +44,21 @@
<Compile Include="CPU\ALU.cs" />
<Compile Include="CPU\ConstantMemory.cs" />
<Compile Include="CPU\CPU.cs" />
<Compile Include="CPU\Disassembler.cs" />
<Compile Include="CPU\MicroInstruction.cs" />
<Compile Include="CPU\Shifter.cs" />
<Compile Include="CPU\UCodeMemory.cs" />
<Compile Include="Memory\IMemoryMappedDevice.cs" />
<Compile Include="Memory\Memory.cs" />
<Compile Include="Memory\MemoryBus.cs" />
<Compile Include="OctalHelpers.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Disassembly\altocode24.mu" />
<None Include="App.config" />
<None Include="Disassembly\altoIIcode3.mu" />
<None Include="ROM\C0">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>

File diff suppressed because it is too large Load Diff

Binary file not shown.

16
Contralto/OctalHelpers.cs Normal file
View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Contralto
{
public static class OctalHelpers
{
public static string ToOctal(int i)
{
return Convert.ToString(i, 8);
}
}
}

View File

@ -15,6 +15,13 @@ namespace Contralto
{
AltoCPU cpu = new AltoCPU();
for(int i=0;i<2048;i++)
{
MicroInstruction inst = new MicroInstruction(UCodeMemory.UCodeROM[i]);
Console.WriteLine("{0}: {1}", OctalHelpers.ToOctal(i), Disassembler.DisassembleInstruction(inst, TaskType.Emulator));
}
while(true)
{
MemoryBus.Clock();