mirror of
https://github.com/livingcomputermuseum/Darkstar.git
synced 2026-02-26 00:53:37 +00:00
1268 lines
40 KiB
C#
1268 lines
40 KiB
C#
/*
|
|
BSD 2-Clause License
|
|
|
|
Copyright Vulcan Inc. 2017-2018 and Living Computer Museum + Labs 2018
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
* Redistributions of source code must retain the above copyright notice, this
|
|
list of conditions and the following disclaimer.
|
|
|
|
* Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
using System;
|
|
|
|
namespace D.CP
|
|
{
|
|
public enum AluSourcePair
|
|
{
|
|
AQ = 0,
|
|
AB = 1,
|
|
ZQ = 2,
|
|
ZB = 3,
|
|
ZA = 4,
|
|
DA = 5,
|
|
DQ = 6,
|
|
D0 = 7,
|
|
}
|
|
|
|
public enum AluFunction
|
|
{
|
|
RplusS = 0,
|
|
SminusR = 1,
|
|
RminusS = 2,
|
|
RorS = 3,
|
|
RandS = 4,
|
|
notRandS = 5,
|
|
RxorS = 6,
|
|
notRxorS = 7,
|
|
}
|
|
|
|
public enum FunctionSelectFY
|
|
{
|
|
DispBr = 0,
|
|
fyNorm = 1,
|
|
IOOut = 2,
|
|
Byte = 3,
|
|
}
|
|
|
|
public enum FunctionSelectFZ
|
|
{
|
|
fzNorm = 0,
|
|
Nibble = 1,
|
|
Uaddr = 2,
|
|
IOXIn = 3,
|
|
}
|
|
|
|
public enum XFunction
|
|
{
|
|
pCallRet0 = 0x0,
|
|
pCallRet1 = 0x1,
|
|
pCallRet2 = 0x2,
|
|
pCallRet3 = 0x3,
|
|
pCallRet4 = 0x4,
|
|
pCallRet5 = 0x5,
|
|
pCallRet6 = 0x6,
|
|
pCallRet7 = 0x7,
|
|
Noop = 0x8,
|
|
LoadRH = 0x9,
|
|
shift = 0xa,
|
|
cycle = 0xb,
|
|
LoadCinFrompc16 = 0xc,
|
|
LoadMap = 0xd,
|
|
pop = 0xe,
|
|
push = 0xf,
|
|
}
|
|
|
|
public enum YNormFunction
|
|
{
|
|
ExitKern = 0x0,
|
|
EnterKern = 0x1,
|
|
ClrIntErr = 0x2,
|
|
IBDisp = 0x3,
|
|
MesaIntRq = 0x4,
|
|
LoadstackP = 0x5,
|
|
LoadIB = 0x6,
|
|
cycle = 0x7,
|
|
Noop = 0x8,
|
|
LoadMap = 0x9,
|
|
Refresh = 0xa,
|
|
push = 0xb,
|
|
ClrDPRq = 0xc,
|
|
ClrIOPRq = 0xd,
|
|
ClrRefRq = 0xe,
|
|
ClrKFlags = 0xf,
|
|
}
|
|
|
|
public enum YDispBrFunction
|
|
{
|
|
NegBr = 0x0,
|
|
ZeroBr = 0x1,
|
|
NZeroBr = 0x2,
|
|
MesaIntBr = 0x3,
|
|
PgCarryBr = 0x4,
|
|
CarryBr = 0x5,
|
|
XRefBr = 0x6,
|
|
NibCarryBr = 0x7,
|
|
XDisp = 0x8,
|
|
YDisp = 0x9,
|
|
XC2npcDisp = 0xa,
|
|
YIODisp = 0xb,
|
|
XwdDisp = 0xc,
|
|
XHDisp = 0xd,
|
|
XLDisp = 0xe, // AKA XDirtyDisp
|
|
PgCrOvDisp = 0xf,
|
|
}
|
|
|
|
public enum YIOOutFunction
|
|
{
|
|
IOPOData = 0x0,
|
|
IOPCtl = 0x1,
|
|
KOData = 0x2,
|
|
KCtl = 0x3,
|
|
EOData = 0x4,
|
|
EICtl = 0x5,
|
|
DCtlFifo = 0x6,
|
|
DCtl = 0x7,
|
|
DBorder = 0x8,
|
|
PCtl = 0x9,
|
|
MCtl = 0xa,
|
|
Invalid0 = 0xb,
|
|
EOCtl = 0xc,
|
|
KCmd = 0xd,
|
|
Invalid1 = 0xe,
|
|
POData = 0xf,
|
|
}
|
|
|
|
public enum ZNormFunction
|
|
{
|
|
Refresh = 0x0,
|
|
LoadIBPtr1 = 0x1,
|
|
LoadIBPtr0 = 0x2,
|
|
LoadCinFrompc16 = 0x3,
|
|
LoadBank = 0x4,
|
|
pop = 0x5,
|
|
push = 0x6,
|
|
AltUaddr = 0x7,
|
|
Noop0 = 0x8,
|
|
Noop1 = 0x9,
|
|
Noop2 = 0xa,
|
|
Noop3 = 0xb,
|
|
LRot0 = 0xc,
|
|
LRot12 = 0xd,
|
|
LRot8 = 0xe,
|
|
LRot4 = 0xf
|
|
}
|
|
|
|
// For Zap Rowsdower
|
|
public enum ZIOXIn
|
|
{
|
|
ReadEIdata = 0x0,
|
|
ReadEStatus = 0x1,
|
|
ReadKIData = 0x2,
|
|
ReadKStatus = 0x3,
|
|
KStrobe = 0x4,
|
|
ReadMStatus = 0x5,
|
|
ReadKTest = 0x6,
|
|
EStrobe = 0x7,
|
|
ReadIOPIData = 0x8,
|
|
ReadIOPStatus = 0x9,
|
|
ReadErrnIBnStkp = 0xa,
|
|
ReadRH = 0xb,
|
|
ReadibNA = 0xc,
|
|
Readib = 0xd,
|
|
ReadibLow = 0xe,
|
|
ReadibHigh = 0xf,
|
|
}
|
|
|
|
public enum StackTestType
|
|
{
|
|
None,
|
|
Underflow,
|
|
Overflow,
|
|
Underflow2,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decodes a single microcode word.
|
|
/// </summary>
|
|
public class Microinstruction
|
|
{
|
|
public Microinstruction(ulong word)
|
|
{
|
|
rA = (int)((word & 0xf00000000000) >> 44);
|
|
rB = (int)((word & 0x0f0000000000) >> 40);
|
|
aS = (AluSourcePair)((word & 0x00e000000000) >> 37);
|
|
aF = (AluFunction)((word & 0x001c00000000) >> 34);
|
|
aD = (int)((word & 0x000300000000) >> 32);
|
|
ep = (word & 0x000080000000) != 0;
|
|
Cin = (word & 0x000040000000) != 0;
|
|
enSU = (word & 0x000020000000) != 0;
|
|
mem = (word & 0x000010000000) != 0;
|
|
fSfY = (FunctionSelectFY)((word & 0x00000c000000) >> 26);
|
|
fSfZ = (FunctionSelectFZ)((word & 0x000003000000) >> 24);
|
|
fX = (XFunction)((word & 0x000000f00000) >> 20);
|
|
fY = (int)((word & 0x0000000f0000) >> 16);
|
|
fZ = (int)((word & 0x00000000f000) >> 12);
|
|
INIA = (int)((word & 0x000000000fff));
|
|
|
|
|
|
//
|
|
// Instruction metadata that can be precomputed and cached
|
|
//
|
|
Cycle = (fX == XFunction.cycle) ||
|
|
(fSfY == FunctionSelectFY.fyNorm && ((YNormFunction)fY) == YNormFunction.cycle);
|
|
Shift =
|
|
((fX == XFunction.shift) ||
|
|
Cycle);
|
|
|
|
AluNeedsXBus = (aS == AluSourcePair.D0 || aS == AluSourcePair.DA || aS == AluSourcePair.DQ);
|
|
|
|
AluDestination = aD | (Shift ? 0x4 : 0x0);
|
|
|
|
SURead = enSU && !Cin;
|
|
|
|
SUWrite = enSU && Cin;
|
|
|
|
LoadMap = fX == XFunction.LoadMap ||
|
|
(fSfY == FunctionSelectFY.fyNorm &&
|
|
(YNormFunction)fY == YNormFunction.LoadMap);
|
|
|
|
ABypass = AluDestination == 0x2;
|
|
|
|
LoadStackP = (fSfY == FunctionSelectFY.fyNorm &&
|
|
(YNormFunction)fY == YNormFunction.LoadstackP);
|
|
|
|
LoadIBPtr1 = (fSfZ == FunctionSelectFZ.fzNorm && ((ZNormFunction)fZ) == ZNormFunction.LoadIBPtr1);
|
|
|
|
AlwaysIBDisp = (fSfY == FunctionSelectFY.fyNorm &&
|
|
(YNormFunction)fY == YNormFunction.IBDisp) &&
|
|
LoadIBPtr1;
|
|
|
|
LoadIB = fSfY == FunctionSelectFY.fyNorm &&
|
|
(YNormFunction)fY == YNormFunction.LoadIB;
|
|
|
|
UAddress = (rA << 4) | fZ;
|
|
|
|
switch (fX)
|
|
{
|
|
case XFunction.pCallRet0:
|
|
case XFunction.pCallRet1:
|
|
case XFunction.pCallRet2:
|
|
case XFunction.pCallRet3:
|
|
case XFunction.pCallRet4:
|
|
case XFunction.pCallRet5:
|
|
case XFunction.pCallRet6:
|
|
case XFunction.pCallRet7:
|
|
LinkAddress = (int)fX;
|
|
break;
|
|
|
|
default:
|
|
LinkAddress = -1;
|
|
break;
|
|
}
|
|
|
|
MarMapMDR = mem || LoadMap;
|
|
|
|
LateLRotN = !ABypass && fSfZ == FunctionSelectFZ.fzNorm;
|
|
|
|
if (fSfY == FunctionSelectFY.Byte)
|
|
{
|
|
// Byte constant
|
|
Byte = (byte)((fY << 4) | fZ);
|
|
}
|
|
else if (fSfZ == FunctionSelectFZ.Nibble)
|
|
{
|
|
// Nibble constant
|
|
Byte = (byte)fZ;
|
|
}
|
|
else
|
|
{
|
|
// No constant value.
|
|
Byte = 0;
|
|
}
|
|
|
|
bool fxPop = (fX == XFunction.pop);
|
|
bool fzPop = (fSfZ == FunctionSelectFZ.fzNorm && ((ZNormFunction)fZ) == ZNormFunction.pop);
|
|
|
|
Pop = fxPop || fzPop;
|
|
|
|
//
|
|
// There is a special case if both fxPop and fzPop are specified: stackP is still decremented by 1,
|
|
// but a trap is invoked if stackP is 1 or 0 (rather than just 0).
|
|
//
|
|
DoublePop = fxPop && fzPop;
|
|
|
|
Push = (fX == XFunction.push) ||
|
|
(fSfY == FunctionSelectFY.fyNorm && ((YNormFunction)fY) == YNormFunction.push) ||
|
|
(fSfZ == FunctionSelectFZ.fzNorm && ((ZNormFunction)fZ) == ZNormFunction.push);
|
|
|
|
StackOperation = Pop || Push;
|
|
|
|
//
|
|
// From the HWref (p. 33):
|
|
// "Multiple pop's and push's can be specified per microinstruction in order to ameliorate the detection
|
|
// of Stack overflow or underflow. For instance, fXpop (i.e. the pop in the fX field), fZpop, and
|
|
// push executed together leave the stackPointer unmodified, yet simulate two pop's with respect to
|
|
// stack underflow detection..."
|
|
// The actual overflow detection logic is controlled by a PROM, there's nothing too weird going on
|
|
// here (other than overloading to provide only semi-related semantics, which is annoying.) At
|
|
// any rate, we precompute the check that's being requested (if any) so we don't have to do it
|
|
// at execution time.
|
|
// TODO: might make sense to dump the PROM and use that.
|
|
//
|
|
if (fxPop && fzPop && Push)
|
|
{
|
|
StackTest = StackTestType.Underflow2;
|
|
}
|
|
else if (Push && fzPop)
|
|
{
|
|
StackTest = StackTestType.Overflow;
|
|
}
|
|
else if (fxPop && Push)
|
|
{
|
|
StackTest = StackTestType.Underflow;
|
|
}
|
|
else
|
|
{
|
|
// No non-modify test, just normal stack behavior.
|
|
StackTest = StackTestType.None;
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// 2901 A reg addr, U addr [0-3]
|
|
/// </summary>
|
|
public readonly int rA;
|
|
|
|
/// <summary>
|
|
/// 2901 B reg addr, RH addr
|
|
/// </summary>
|
|
public readonly int rB;
|
|
|
|
/// <summary>
|
|
/// 2901 alu Source operand pair
|
|
/// </summary>
|
|
public readonly AluSourcePair aS;
|
|
|
|
/// <summary>
|
|
/// 2901 alu Function
|
|
/// </summary>
|
|
public readonly AluFunction aF;
|
|
|
|
/// <summary>
|
|
/// 2901 alu Destination/shift control
|
|
/// </summary>
|
|
public readonly int aD;
|
|
|
|
/// <summary>
|
|
/// Even Parity
|
|
/// </summary>
|
|
public readonly bool ep;
|
|
|
|
/// <summary>
|
|
/// 2901 Carry In, Shift Ends, writeSU (if enSU = 1)
|
|
/// </summary>
|
|
public readonly bool Cin;
|
|
|
|
/// <summary>
|
|
/// enable SU reg file
|
|
/// </summary>
|
|
public readonly bool enSU;
|
|
|
|
/// <summary>
|
|
/// MAR<- (if c1), MDR<- (if c2), <-MD (if c3)
|
|
/// </summary>
|
|
public readonly bool mem;
|
|
|
|
/// <summary>
|
|
/// Function field selector for Y
|
|
/// </summary>
|
|
public readonly FunctionSelectFY fSfY;
|
|
|
|
/// <summary>
|
|
/// Function field selector for Z
|
|
/// </summary>
|
|
public readonly FunctionSelectFZ fSfZ;
|
|
|
|
/// <summary>
|
|
/// X Function
|
|
/// </summary>
|
|
public readonly XFunction fX;
|
|
|
|
/// <summary>
|
|
/// Y Function
|
|
/// </summary>
|
|
public readonly int fY;
|
|
|
|
/// <summary>
|
|
/// Z Function
|
|
/// </summary>
|
|
public readonly int fZ;
|
|
|
|
/// <summary>
|
|
/// Next Instruction Address
|
|
/// </summary>
|
|
public readonly int INIA;
|
|
|
|
//
|
|
// The following are metadata for this instruction, used to speed execution.
|
|
//
|
|
|
|
/// <summary>
|
|
/// Instruction specifies a Cycle of ALU output when writing back to R/Q
|
|
/// </summary>
|
|
public readonly bool Cycle;
|
|
|
|
/// <summary>
|
|
/// Instruction specifies a Shift of ALU as above.
|
|
/// </summary>
|
|
public readonly bool Shift;
|
|
|
|
/// <summary>
|
|
/// Instruction requires XBus input to ALU.
|
|
/// </summary>
|
|
public readonly bool AluNeedsXBus;
|
|
|
|
/// <summary>
|
|
/// Destination control for the ALU
|
|
/// </summary>
|
|
public readonly int AluDestination;
|
|
|
|
/// <summary>
|
|
/// Instruction specifies an SU register read
|
|
/// </summary>
|
|
public readonly bool SURead;
|
|
|
|
/// <summary>
|
|
/// Instruction specifies an SU register write
|
|
/// </summary>
|
|
public readonly bool SUWrite;
|
|
|
|
/// <summary>
|
|
/// Instruction specifies a Map<- operation.
|
|
/// </summary>
|
|
public readonly bool LoadMap;
|
|
|
|
/// <summary>
|
|
/// Instruction uses the A-bypass mode for the ALU.
|
|
/// </summary>
|
|
public readonly bool ABypass;
|
|
|
|
/// <summary>
|
|
/// Instruction specifies a stackP<- operation.
|
|
/// </summary>
|
|
public readonly bool LoadStackP;
|
|
|
|
/// <summary>
|
|
/// Instruction specifies a push operation
|
|
/// </summary>
|
|
public readonly bool Push;
|
|
|
|
/// <summary>
|
|
/// Instruction specifies a pop operation
|
|
/// </summary>
|
|
public readonly bool Pop;
|
|
|
|
/// <summary>
|
|
/// Instruction specifies a double-pop operation.
|
|
/// </summary>
|
|
public readonly bool DoublePop;
|
|
|
|
/// <summary>
|
|
/// Whether any stack operations (pushes or pops) occur in this instruction.
|
|
/// </summary>
|
|
public readonly bool StackOperation;
|
|
|
|
/// <summary>
|
|
/// Specifies the kind of test specified by the various
|
|
/// push/pop instruction fields.
|
|
/// </summary>
|
|
public readonly StackTestType StackTest;
|
|
|
|
/// <summary>
|
|
/// Causes an IBDisp branch even if IB is not full;
|
|
/// specified by IBDisp + IBPtr<-1
|
|
/// </summary>
|
|
public readonly bool AlwaysIBDisp;
|
|
|
|
/// <summary>
|
|
/// Whether an IB<- is specified this instruction.
|
|
/// </summary>
|
|
public readonly bool LoadIB;
|
|
|
|
/// <summary>
|
|
/// Whether the instruction specifies an ibPtr<-1 operation,
|
|
/// which can be used to modify other operations.
|
|
/// </summary>
|
|
public readonly bool LoadIBPtr1;
|
|
|
|
/// <summary>
|
|
/// Constant address used to address U register when loading/storing
|
|
/// </summary>
|
|
public readonly int UAddress;
|
|
|
|
/// <summary>
|
|
/// Constant byte value
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public readonly byte Byte;
|
|
|
|
/// <summary>
|
|
/// Link address specified by instruction (or -1 if not specified)
|
|
/// </summary>
|
|
public readonly int LinkAddress;
|
|
|
|
/// <summary>
|
|
/// Whether the instruction specifies an MAR<-, Map<-, or MDR<- operation.
|
|
/// </summary>
|
|
public readonly bool MarMapMDR;
|
|
|
|
/// <summary>
|
|
/// Whether to do an LrotN operation after the ALU runs.
|
|
/// </summary>
|
|
public readonly bool LateLRotN;
|
|
|
|
public override string ToString()
|
|
{
|
|
return String.Format("rA={0:x} rB={1:x} aS={2} aF={3} aD={4} ep={5} Cin={6} enSU={7} mem={8} fSY={9} fSZ={10} fX={11} fY={12:x} fZ={13:x} INIA={14:x3}",
|
|
rA, rB, aS, aF, aD, ep, Cin, enSU, mem, fSfY, fSfZ, fX, fY, fZ, INIA);
|
|
}
|
|
|
|
public string Disassemble(int cycle)
|
|
{
|
|
//
|
|
// Build ALU op, start with the sources:
|
|
//
|
|
|
|
string aluR;
|
|
string aluS;
|
|
bool Rzero = false;
|
|
bool Szero = false;
|
|
|
|
string xBusValue = DisassembleXBusSource(cycle);
|
|
|
|
switch (aS)
|
|
{
|
|
case AluSourcePair.AB:
|
|
aluR = String.Format("R{0:x}", rA);
|
|
aluS = String.Format("R{0:x}", rB);
|
|
break;
|
|
|
|
case AluSourcePair.AQ:
|
|
aluR = String.Format("R{0:x}", rA);
|
|
aluS = "Q";
|
|
break;
|
|
|
|
case AluSourcePair.ZA:
|
|
aluR = "0";
|
|
Rzero = true;
|
|
aluS = String.Format("R{0:x}", rA);
|
|
break;
|
|
|
|
case AluSourcePair.ZB:
|
|
aluR = "0";
|
|
Rzero = true;
|
|
aluS = String.Format("R{0:x}", rB);
|
|
break;
|
|
|
|
case AluSourcePair.ZQ:
|
|
aluR = "0";
|
|
Rzero = true;
|
|
aluS = "Q";
|
|
break;
|
|
|
|
case AluSourcePair.D0:
|
|
aluR = xBusValue;
|
|
aluS = "0";
|
|
Szero = true;
|
|
break;
|
|
|
|
case AluSourcePair.DA:
|
|
aluR = xBusValue;
|
|
aluS = String.Format("R{0:x}", rA);
|
|
break;
|
|
|
|
case AluSourcePair.DQ:
|
|
aluR = xBusValue;
|
|
aluS = "Q";
|
|
break;
|
|
|
|
default:
|
|
throw new InvalidOperationException("Unexpected ALU source pair.");
|
|
}
|
|
|
|
//
|
|
// Select operation
|
|
//
|
|
string aluOp;
|
|
switch (aF)
|
|
{
|
|
case AluFunction.RplusS:
|
|
if (Rzero)
|
|
{
|
|
aluOp = aluS;
|
|
}
|
|
else if (Szero)
|
|
{
|
|
aluOp = aluR;
|
|
}
|
|
else
|
|
{
|
|
aluOp = String.Format("{0}+{1}", aluR, aluS);
|
|
}
|
|
break;
|
|
|
|
case AluFunction.SminusR:
|
|
if (Rzero)
|
|
{
|
|
aluOp = aluS;
|
|
}
|
|
else if (Szero)
|
|
{
|
|
aluOp = "-" + aluR;
|
|
}
|
|
else
|
|
{
|
|
aluOp = String.Format("{0}-{1}", aluS, aluR);
|
|
}
|
|
break;
|
|
|
|
case AluFunction.RminusS:
|
|
if (Rzero)
|
|
{
|
|
aluOp = "-" + aluS;
|
|
}
|
|
else if (Szero)
|
|
{
|
|
aluOp = aluR;
|
|
}
|
|
else
|
|
{
|
|
aluOp = String.Format("{0}-{1}", aluR, aluS);
|
|
}
|
|
break;
|
|
|
|
case AluFunction.RorS:
|
|
if (Rzero)
|
|
{
|
|
aluOp = aluS;
|
|
}
|
|
else if (Szero)
|
|
{
|
|
aluOp = aluR;
|
|
}
|
|
else
|
|
{
|
|
aluOp = String.Format("{0} or {1}", aluR, aluS);
|
|
}
|
|
break;
|
|
|
|
case AluFunction.RandS:
|
|
if (Rzero)
|
|
{
|
|
aluOp = "0";
|
|
}
|
|
else if (Szero)
|
|
{
|
|
aluOp = "0";
|
|
}
|
|
else
|
|
{
|
|
aluOp = String.Format("{0} and {1}", aluR, aluS);
|
|
}
|
|
break;
|
|
|
|
case AluFunction.notRandS:
|
|
if (Rzero)
|
|
{
|
|
aluOp = aluS;
|
|
}
|
|
else if (Szero)
|
|
{
|
|
aluOp = "0";
|
|
}
|
|
else
|
|
{
|
|
aluOp = String.Format("~{0} and {1}", aluR, aluS);
|
|
}
|
|
break;
|
|
|
|
case AluFunction.RxorS:
|
|
if (Rzero)
|
|
{
|
|
aluOp = aluS;
|
|
}
|
|
else if (Szero)
|
|
{
|
|
aluOp = aluR;
|
|
}
|
|
else
|
|
{
|
|
aluOp = String.Format("{0} xor {1}", aluR, aluS);
|
|
}
|
|
break;
|
|
|
|
case AluFunction.notRxorS:
|
|
if (Szero)
|
|
{
|
|
aluOp = "~" + aluR;
|
|
}
|
|
else
|
|
{
|
|
aluOp = String.Format("~{0} xor {1}", aluR, aluS);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new InvalidOperationException("Unexpected ALU operation");
|
|
}
|
|
|
|
//
|
|
// Select register writeback (to rB)
|
|
// Q writeback, and Y source (F, or A bypass)
|
|
//
|
|
int writeFn = aD | (Shift ? 0x4 : 0x0);
|
|
string regAssignment;
|
|
bool aBypass = false;
|
|
bool yBusIsSourceForDestination = false;
|
|
bool aluNoWriteBack = false;
|
|
switch(writeFn)
|
|
{
|
|
case 0:
|
|
// no write, Q<-F
|
|
regAssignment = String.Format("Q<- {0}{1}", aluOp, GetCarryMod());
|
|
yBusIsSourceForDestination = true;
|
|
break;
|
|
|
|
case 1:
|
|
// no write.
|
|
regAssignment = String.Format("{0}{1}", aluOp, GetCarryMod());
|
|
yBusIsSourceForDestination = false;
|
|
aluNoWriteBack = true;
|
|
break;
|
|
|
|
case 2:
|
|
// R[rB] <- F, no write to Q, A Bypass for YBus<-
|
|
regAssignment = String.Format("R{0:x}<- {1}{2}", rB, aluOp, GetCarryMod());
|
|
aBypass = true;
|
|
yBusIsSourceForDestination = true;
|
|
break;
|
|
|
|
case 3:
|
|
// R[rB] <- F, no write to Q
|
|
regAssignment = String.Format("R{0:x}<- {1}{2}", rB, aluOp, GetCarryMod());
|
|
yBusIsSourceForDestination = true;
|
|
break;
|
|
|
|
case 4:
|
|
if (Cycle)
|
|
{
|
|
// double-word right shift
|
|
regAssignment = String.Format("R{0:x}<- DRShift1 {1}{2}{3}", rB, aluOp, GetCarryMod(), Cin ? " SE<-1" : String.Empty);
|
|
}
|
|
else
|
|
{
|
|
// double-word arithmetic right shift.
|
|
regAssignment = String.Format("R{0:x}<- DARShift1 {1}{2}{3}", rB, aluOp, GetCarryMod(), Cin ? " SE<-1" : String.Empty);
|
|
}
|
|
yBusIsSourceForDestination = true;
|
|
break;
|
|
|
|
case 5:
|
|
if (Cycle)
|
|
{
|
|
// F: single-word right rotate:
|
|
regAssignment = String.Format("R{0:x}<- RRot1 {1}{2}", rB, aluOp, GetCarryMod());
|
|
}
|
|
else
|
|
{
|
|
// F: single-word right shift w/carryIn to MSB:
|
|
regAssignment = String.Format("R{0:x}<- RShift1 {1}{2}{3}", rB, aluOp, GetCarryMod(), Cin ? " SE<-1" : String.Empty);
|
|
}
|
|
yBusIsSourceForDestination = true;
|
|
break;
|
|
|
|
case 6:
|
|
if (Cycle)
|
|
{
|
|
// double-word left shift
|
|
regAssignment = String.Format("R{0:x}<- DLShift1 {1}{2}{3}", rB, aluOp, GetCarryMod(), Cin ? " SE<-1" : String.Empty);
|
|
}
|
|
else
|
|
{
|
|
// double-word arithmetic left shift
|
|
regAssignment = String.Format("R{0:x}<- DALShift1 {1}{2}{3}", rB, aluOp, GetCarryMod(), Cin ? " SE<-1" : String.Empty);
|
|
}
|
|
yBusIsSourceForDestination = true;
|
|
break;
|
|
|
|
case 7:
|
|
if (Cycle)
|
|
{
|
|
// single-word left rotate:
|
|
regAssignment = String.Format("R{0:x}<- LRot1 {1}{2}", rB, aluOp, GetCarryMod());
|
|
}
|
|
else
|
|
{
|
|
// single-word left shift w/carryIn to MSB:
|
|
regAssignment = String.Format("R{0:x}<- LShift1 {1}{2}{3}", rB, aluOp, GetCarryMod(), Cin ? " SE<-1" : String.Empty);
|
|
}
|
|
yBusIsSourceForDestination = true;
|
|
break;
|
|
|
|
default:
|
|
throw new InvalidOperationException("Unexpected sh,,aD value.");
|
|
}
|
|
|
|
string yBusValue = aBypass ? String.Format("R{0:x}, {1}", rA, regAssignment) : String.Format("{0}", regAssignment);
|
|
|
|
string fxFunc = String.Empty;
|
|
bool xBusIsSourceForDestination = false;
|
|
|
|
bool yBusBranch = false;
|
|
bool xBusBranch = false;
|
|
|
|
// Handle fX functions that aren't implicitly handled elsewhere (shift, cycle)
|
|
switch (fX)
|
|
{
|
|
case XFunction.pCallRet0:
|
|
case XFunction.pCallRet1:
|
|
case XFunction.pCallRet2:
|
|
case XFunction.pCallRet3:
|
|
case XFunction.pCallRet4:
|
|
case XFunction.pCallRet5:
|
|
case XFunction.pCallRet6:
|
|
case XFunction.pCallRet7:
|
|
fxFunc = String.Format("pCall/Ret{0} ", (int)fX);
|
|
break;
|
|
|
|
case XFunction.LoadRH:
|
|
fxFunc = String.Format("RH{0:x}<-", rB);
|
|
xBusIsSourceForDestination = true;
|
|
break;
|
|
|
|
case XFunction.LoadCinFrompc16:
|
|
fxFunc = "SE<-pc16 ";
|
|
break;
|
|
|
|
case XFunction.LoadMap:
|
|
fxFunc = String.Format("Map<- RH{0:x},,", rB);
|
|
yBusIsSourceForDestination = true;
|
|
break;
|
|
|
|
case XFunction.pop:
|
|
fxFunc = "pop ";
|
|
break;
|
|
|
|
case XFunction.push:
|
|
fxFunc = "push ";
|
|
break;
|
|
}
|
|
|
|
string fyFunc = String.Empty;
|
|
|
|
// Handle fY functions that aren't implicitly handled elsewhere (cycle, Byte, etc.)
|
|
switch (fSfY)
|
|
{
|
|
case FunctionSelectFY.fyNorm:
|
|
switch ((YNormFunction)fY)
|
|
{
|
|
case YNormFunction.ExitKern:
|
|
fyFunc = "ExitKern ";
|
|
break;
|
|
|
|
case YNormFunction.EnterKern:
|
|
fyFunc = "EnterKern ";
|
|
break;
|
|
|
|
case YNormFunction.ClrIntErr:
|
|
fyFunc = "ClrIntErr ";
|
|
break;
|
|
|
|
case YNormFunction.IBDisp:
|
|
fyFunc = "IBDisp ";
|
|
break;
|
|
|
|
case YNormFunction.MesaIntRq:
|
|
fyFunc = "MesaIntRq ";
|
|
break;
|
|
|
|
case YNormFunction.LoadstackP:
|
|
fyFunc = "stackP<-";
|
|
yBusIsSourceForDestination = true;
|
|
break;
|
|
|
|
case YNormFunction.LoadIB:
|
|
fyFunc = "IB<-";
|
|
xBusIsSourceForDestination = true;
|
|
break;
|
|
|
|
case YNormFunction.LoadMap:
|
|
fyFunc = String.Format("Map<- RH{0:x},,", rB);
|
|
break;
|
|
|
|
case YNormFunction.Refresh:
|
|
fyFunc = "Refresh ";
|
|
break;
|
|
|
|
case YNormFunction.push:
|
|
fyFunc = "push ";
|
|
break;
|
|
|
|
case YNormFunction.ClrDPRq:
|
|
fyFunc = "ClrDPRq ";
|
|
break;
|
|
|
|
case YNormFunction.ClrIOPRq:
|
|
fyFunc = "ClrIOPRq ";
|
|
break;
|
|
|
|
case YNormFunction.ClrRefRq:
|
|
fyFunc = "ClrRefRq ";
|
|
break;
|
|
|
|
case YNormFunction.ClrKFlags:
|
|
fyFunc = "ClrKFlags ";
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case FunctionSelectFY.DispBr:
|
|
fyFunc = ((YDispBrFunction)fY).ToString() + " ";
|
|
|
|
switch ((YDispBrFunction)fY)
|
|
{
|
|
case YDispBrFunction.NegBr:
|
|
case YDispBrFunction.ZeroBr:
|
|
case YDispBrFunction.NibCarryBr:
|
|
case YDispBrFunction.PgCarryBr:
|
|
case YDispBrFunction.CarryBr:
|
|
case YDispBrFunction.PgCrOvDisp:
|
|
case YDispBrFunction.YDisp:
|
|
case YDispBrFunction.YIODisp:
|
|
yBusBranch = true;
|
|
break;
|
|
|
|
case YDispBrFunction.XRefBr:
|
|
case YDispBrFunction.XwdDisp:
|
|
case YDispBrFunction.XHDisp:
|
|
case YDispBrFunction.XLDisp:
|
|
case YDispBrFunction.XDisp:
|
|
case YDispBrFunction.XC2npcDisp:
|
|
xBusBranch = true;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case FunctionSelectFY.IOOut:
|
|
if (fY != 0xb && fY != 0xe)
|
|
{
|
|
YIOOutFunction yIOOut = ((YIOOutFunction)fY);
|
|
fyFunc = yIOOut.ToString() + "<-";
|
|
|
|
// IOOut functions are roughly split between taking data from the XBus or the YBus.
|
|
xBusIsSourceForDestination =
|
|
(yIOOut == YIOOutFunction.IOPOData ||
|
|
yIOOut == YIOOutFunction.IOPCtl ||
|
|
yIOOut == YIOOutFunction.KOData ||
|
|
yIOOut == YIOOutFunction.KCtl ||
|
|
yIOOut == YIOOutFunction.EOData ||
|
|
yIOOut == YIOOutFunction.EICtl ||
|
|
yIOOut == YIOOutFunction.DCtl ||
|
|
yIOOut == YIOOutFunction.PCtl ||
|
|
yIOOut == YIOOutFunction.EOCtl ||
|
|
yIOOut == YIOOutFunction.KCmd ||
|
|
yIOOut == YIOOutFunction.POData
|
|
);
|
|
|
|
yBusIsSourceForDestination = (!xBusIsSourceForDestination && (fY != 0xb && fY != 0xe));
|
|
|
|
}
|
|
break;
|
|
}
|
|
|
|
string fzFunc = String.Empty;
|
|
|
|
// Handle fZ functions that aren't implicitly handled elsewhere (IOXIn)
|
|
switch (fSfZ)
|
|
{
|
|
case FunctionSelectFZ.fzNorm:
|
|
switch((ZNormFunction)fZ)
|
|
{
|
|
case ZNormFunction.Refresh:
|
|
fzFunc = "Refresh ";
|
|
break;
|
|
|
|
case ZNormFunction.LoadIBPtr1:
|
|
fzFunc = "IBPtr<-1 ";
|
|
break;
|
|
|
|
case ZNormFunction.LoadIBPtr0:
|
|
fzFunc = "IBPtr<-0 ";
|
|
break;
|
|
|
|
case ZNormFunction.LoadCinFrompc16:
|
|
fzFunc = "SE<-pc16 ";
|
|
break;
|
|
|
|
case ZNormFunction.pop:
|
|
fzFunc = "pop ";
|
|
break;
|
|
|
|
case ZNormFunction.push:
|
|
fzFunc = "push ";
|
|
break;
|
|
|
|
case ZNormFunction.AltUaddr:
|
|
fzFunc = "AltUaddr ";
|
|
break;
|
|
|
|
case ZNormFunction.LRot0:
|
|
fzFunc = "LRot0 ";
|
|
break;
|
|
|
|
case ZNormFunction.LRot12:
|
|
fzFunc = "LRot12 ";
|
|
break;
|
|
|
|
case ZNormFunction.LRot8:
|
|
fzFunc = "LRot8 ";
|
|
break;
|
|
|
|
case ZNormFunction.LRot4:
|
|
fzFunc = "LRot4 ";
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// SU reg write
|
|
string suWriteStr = String.Empty;
|
|
bool suWrite = enSU && Cin;
|
|
bool suRead = enSU && !Cin;
|
|
if (suWrite)
|
|
{
|
|
switch((int)fSfZ)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
suWriteStr = "STK<-";
|
|
break;
|
|
|
|
case 2:
|
|
case 3:
|
|
suWriteStr = String.Format("U{0:x2}<-", (rA << 4) | fZ);
|
|
break;
|
|
}
|
|
|
|
yBusIsSourceForDestination = true;
|
|
}
|
|
|
|
if(!yBusIsSourceForDestination && !xBusIsSourceForDestination)
|
|
{
|
|
suWriteStr = "Xbus<- ";
|
|
|
|
// Y bus is implicitly used to provide an X bus value if nothing else is selected.
|
|
yBusIsSourceForDestination = string.IsNullOrEmpty(xBusValue);
|
|
}
|
|
|
|
// MAR or MDR writes:
|
|
string memWrite = String.Empty;
|
|
|
|
if (mem)
|
|
{
|
|
if (cycle == 1)
|
|
{
|
|
memWrite = "MAR<- ";
|
|
}
|
|
else if (cycle == 2)
|
|
{
|
|
memWrite = "MDR<- ";
|
|
}
|
|
else if (cycle == -1)
|
|
{
|
|
memWrite = "{MAR/MDR/MD} ";
|
|
}
|
|
}
|
|
|
|
//
|
|
// The below is kind of messy because of conflation of the ALU with the Y-Bus way up above, etc.
|
|
// Bear with me.
|
|
//
|
|
|
|
//
|
|
// The Y Bus value is important and needs to be included in the disassembly if one or more of the
|
|
// below are true:
|
|
// - A register assignment is taking place
|
|
// - The Y Bus is being used as a data source
|
|
// - A dispatch or branch involving the Y Bus or ALU is being invoked during this instruction.
|
|
//
|
|
bool showyBusValue = (yBusBranch || yBusIsSourceForDestination || !aluNoWriteBack);
|
|
|
|
//
|
|
// The X Bus value is important and needs to be included in the disassembly if one or more of the
|
|
// below are true:
|
|
// - The ALU isn't already using the X Bus as an input
|
|
// - The X Bus is being used as a data source
|
|
// - A dispatch or branch involving the X Bus is being invoked during this instruction.
|
|
//
|
|
bool showxBusValue = (xBusBranch || !AluNeedsXBus || xBusIsSourceForDestination);
|
|
|
|
string disassembly = String.Format("{0}{1}{2}{3}{4}{5}{6} [{7:x3}]",
|
|
fxFunc,
|
|
fyFunc,
|
|
fzFunc,
|
|
memWrite,
|
|
suWriteStr,
|
|
showxBusValue ? xBusValue : String.Empty,
|
|
showyBusValue ? yBusValue : String.Empty,
|
|
INIA);
|
|
|
|
|
|
return disassembly;
|
|
}
|
|
|
|
private string GetCarryMod()
|
|
{
|
|
string mod = String.Empty;
|
|
bool add = (aF == AluFunction.RplusS);
|
|
bool sub = (aF == AluFunction.RminusS || aF == AluFunction.SminusR);
|
|
|
|
if (Cin & add)
|
|
{
|
|
mod = "+1";
|
|
}
|
|
else if (!Cin & sub)
|
|
{
|
|
mod = "-1";
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
private string DisassembleXBusSource(int cycle)
|
|
{
|
|
string xBus = String.Empty;
|
|
|
|
// Byte and/or Nibble. In theory these are mutually exclusive,
|
|
// but there's nothing that prevents them both from being coded at the same time.
|
|
// If this happens, Byte takes precedence.
|
|
if (fSfY == FunctionSelectFY.Byte)
|
|
{
|
|
xBus = String.Format("byte({0:x2})", ((fY << 4) | fZ));
|
|
}
|
|
|
|
if(fSfZ == FunctionSelectFZ.Nibble && fSfY != FunctionSelectFY.Byte)
|
|
{
|
|
xBus = String.Format("nibble({0:x1})", fZ);
|
|
}
|
|
else if (fSfZ == FunctionSelectFZ.IOXIn)
|
|
{
|
|
// IOXIn sources
|
|
switch((ZIOXIn)fZ)
|
|
{
|
|
case ZIOXIn.ReadEIdata:
|
|
xBus += "EIData";
|
|
break;
|
|
|
|
case ZIOXIn.ReadEStatus:
|
|
xBus += "EStatus";
|
|
break;
|
|
|
|
case ZIOXIn.ReadKIData:
|
|
xBus += "KIData";
|
|
break;
|
|
|
|
case ZIOXIn.ReadKStatus:
|
|
xBus += "KStatus";
|
|
break;
|
|
|
|
case ZIOXIn.ReadMStatus:
|
|
xBus += "MStatus";
|
|
break;
|
|
|
|
case ZIOXIn.ReadKTest:
|
|
xBus += "KTest";
|
|
break;
|
|
|
|
case ZIOXIn.ReadIOPIData:
|
|
xBus += "IOPIData";
|
|
break;
|
|
|
|
case ZIOXIn.ReadIOPStatus:
|
|
xBus += "IOPStatus";
|
|
break;
|
|
|
|
case ZIOXIn.ReadErrnIBnStkp:
|
|
xBus += "ErrnIBnStkP";
|
|
break;
|
|
|
|
case ZIOXIn.ReadRH:
|
|
xBus += String.Format("RH{0:x}", rB);
|
|
break;
|
|
|
|
case ZIOXIn.ReadibNA:
|
|
xBus += "ibNA";
|
|
break;
|
|
|
|
case ZIOXIn.ReadibLow:
|
|
xBus += "ibLow";
|
|
break;
|
|
|
|
case ZIOXIn.ReadibHigh:
|
|
xBus += "ibHigh";
|
|
break;
|
|
|
|
default:
|
|
xBus += ((ZIOXIn)fZ).ToString();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (enSU && !Cin) // Cin is 0 for reads
|
|
{
|
|
// SU read operations
|
|
switch((int)fSfZ)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
xBus += "STK";
|
|
break;
|
|
|
|
case 2:
|
|
case 3:
|
|
xBus += String.Format("U{0:x2}", (rA << 4) | fZ);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (mem && cycle == 3)
|
|
{
|
|
xBus += "<-MD";
|
|
}
|
|
|
|
return xBus;
|
|
}
|
|
}
|
|
}
|