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:
parent
9c3ee3afac
commit
59d98d1909
@ -13,6 +13,11 @@ namespace Contralto.CPU
|
||||
static class ALU
|
||||
{
|
||||
static ALU()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
_carry = 0;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -7,8 +7,6 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Contralto.CPU
|
||||
{
|
||||
|
||||
|
||||
static class ConstantMemory
|
||||
{
|
||||
static ConstantMemory()
|
||||
|
||||
410
Contralto/CPU/Disassembler.cs
Normal file
410
Contralto/CPU/Disassembler.cs
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
2232
Contralto/Disassembly/altoIIcode3.mu
Normal file
2232
Contralto/Disassembly/altoIIcode3.mu
Normal file
File diff suppressed because it is too large
Load Diff
BIN
Contralto/Disassembly/altocode24.mu
Normal file
BIN
Contralto/Disassembly/altocode24.mu
Normal file
Binary file not shown.
16
Contralto/OctalHelpers.cs
Normal file
16
Contralto/OctalHelpers.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user