EU.rose
Copyright © 1984 by Xerox Corporation. All rights reserved.
Last edited by: Monier, May 10, 1984 7:23:23 pm PDT
Last edited by: McCreight, June 12, 1984 5:22:14 pm PDT
Last edited by: Curry, February 1, 1985 5:06:00 pm PST
??? shows something that has to be discussed and fixed
Directory Rope, DragOpsCross;
Imports IO, BitOps, Cucumber, Dragon, RoseRun, RoseTypes, CacheOps, DragOpsCrossUtils;
Cedar
fieldAdr:  INTEGER = Dragon.PRtoByte[euField];
marAdr:  INTEGER = Dragon.PRtoByte[euMAR];
fpAluClear: INTEGER = Dragon.PRtoByte[fpAluClear];
fpAluSgl:  INTEGER = Dragon.PRtoByte[fpAluSgl];
fpAluLsw: INTEGER = Dragon.PRtoByte[fpAluLsw];
fpAluMsw: INTEGER = Dragon.PRtoByte[fpAluMsw];
fpMultClear: INTEGER = Dragon.PRtoByte[fpMultClear];
fpMultSgl: INTEGER = Dragon.PRtoByte[fpMultSgl];
fpMultLsw: INTEGER = Dragon.PRtoByte[fpMultLsw];
fpMultMsw: INTEGER = Dragon.PRtoByte[fpMultMsw];
Remark: PROC [ message: Rope.ROPE ] = {RoseRun.DisableableStop[Remark, message]};
Replace by straight logic functions; no case statements
FieldOp: PROC[aluLeft, aluRight, fieldDesc: Dragon.HexWord]
RETURNS [result:Dragon.HexWord] =
BEGIN
fd: DragOpsCross.FieldDescriptor ←
DragOpsCrossUtils.CardToFieldDescriptor[fieldDesc MOD 65536];
shiftout: BitOps.BitDWord ← [0,0]; -- to begin cleanly
maskhole:  BitOps.BitDWord ← BitOps.doubleMasks[fd.mask];
maskhole ← a cloud of zeros followed by fd.mask ones
Dragon.Assert[fd.shift < 33]; Dragon.Assert[fd.mask < 33];
SELECT fd.shift FROM
0   => shiftout ← Dragon.LTD[aluLeft]; -- no shift
32   => shiftout ← Dragon.LTD[aluRight]; -- full shift
ENDCASE => {
Insert 32-fd.shift bits of aluLeft on the left of shiftout
shiftout ← BitOps.MDTD
[Dragon.LTD[aluLeft], 32, fd.shift, 32-fd.shift, shiftout, 32, 0, 32-fd.shift];
Insert fd.shift bits of aluRight on the right of shiftout
shiftout ← BitOps.MDTD
[Dragon.LTD[aluRight], 32, 0, fd.shift, shiftout, 32, 32-fd.shift, fd.shift] };
result ← Dragon.LFD[BitOps.DAND[shiftout, maskhole]]; -- mask shiftout
IF fd.insert THEN {
mask2: BitOps.BitDWord ← BitOps.doubleMasks[fd.shift]; -- another intermediate mask
Maskhole ← zeros, then fd.mask-fd.shift ones, then fd.shift zeros.
maskhole ← BitOps.DNOT[BitOps.DXOR[maskhole, mask2],32];
Merge shiftout and aluRight using the mask
result ← WordOp[or, result, WordOp[and, aluRight, Dragon.LFD[maskhole] ] ] };
END;
Returns a+b+carry and c32, where a and b are considered to be signed numbers
DoubleADD: PROC[al, bl: Dragon.HexWord, carry: BOOL]
RETURNS [sl: Dragon.HexWord, c32: BOOL] = {
Xor: PROC[x, y: BOOL] RETURNS [z: BOOL] =
{RETURN[(x AND NOT y) OR (y AND NOT x)]};
ai, bi: BOOL;
s: BOOLFALSE;
c: BOOL ← carry;
i: INTEGER;
sum: BitOps.BitDWord ← [0,0];
FOR i IN [1..32]
DO
ai ← EBFL[al, 32-i];
bi ← EBFL[bl, 32-i];
s ← Xor[ai, Xor[bi, c]];
c ← (ai AND bi) OR (bi AND c) OR (ai AND c);
sum ← BitOps.IBID[s, sum, 32, 32-i];
ENDLOOP;
RETURN[Dragon.LFD[sum], c]};
Returns a-b-carry and c32, where a and b are considered to be signed numbers
Implemented as a+(~b)+1+(~carry)
DoubleSUB: PROC[a, b: Dragon.HexWord, carry: BOOL]
RETURNS [dif: Dragon.HexWord, c32: BOOL] ={
[dif, c32] ← DoubleADD[a, WordOp[not,b], NOT carry]};
WordOp: PROC[op:{not, or, and, xor}, left, right:Dragon.HexWord ← 0]
RETURNS[result:Dragon.HexWord] = {
RETURN[Dragon.LFD[SELECT op FROM
not   => BitOps.DNOT [Dragon.LTD[left], 32],
or    => BitOps.DOR [Dragon.LTD[left], Dragon.LTD[right]],
and   => BitOps.DAND [Dragon.LTD[left], Dragon.LTD[right]],
xor   => BitOps.DXOR [Dragon.LTD[left], Dragon.LTD[right]],
ENDCASE => ERROR]] };
EBFL: PROC[word: Dragon.HexWord, index: CARDINAL] RETURNS[BOOL] = {
RETURN[ BitOps.EBFD[Dragon.LTD[word], 32, index]] };
DblShiftRt: PROC[msb: BOOL, ltIn, rtIn: Dragon.HexWord]
RETURNS[ltOut, rtOut: Dragon.HexWord] =
{rtOut ← ShiftRt[EBFL[ltIn, 31], rtIn]; ltOut ← ShiftRt[msb, ltIn]};
ShiftRt: PROC[msb: BOOL, w: Dragon.HexWord] RETURNS[r: Dragon.HexWord] =
{r𡤍ragon.LFD[BitOps.IBID[msb, BitOps.MDTD[Dragon.LTD[w],32,0,31,[0,0],32,1,31],32,0]]};
DblShiftLt: PROC[ltIn, rtIn: Dragon.HexWord, lsb: BOOL]
RETURNS[cry: BOOL, ltOut, rtOut: Dragon.HexWord] =
{cry ← EBFL[ltIn,0]; ltOut ← ShiftLt[ltIn, EBFL[rtIn, 0]]; rtOut ← ShiftLt[rtIn, lsb]};
ShiftLt: PROC[w: Dragon.HexWord, lsb: BOOL] RETURNS[r: Dragon.HexWord] =
{r𡤍ragon.LFD[BitOps.IBID[lsb, BitOps.MDTD[Dragon.LTD[w],32,1,31,[0,0],32,0,31],32,31]]};
;
EU: LAMBDA [logRef: |REF IO.STREAM|] RETURN CELLTYPE AutoName
PORTS [
Signal names obey the following convention: If a signal x is computed during PhA and remains valid throughout the following PhB, it is denoted as xAB. If x is computed during PhA and can change during the following PhB (as, for example, in precharged logic), it is denoted as xA. In this latter case, a client wanting to use x during PhB must receive it in his own latch open during PhA. xBA and xB are defined symmetrically. Positive logic is assumed (dragon.Asserted = TRUE = 1 = more positive logic voltage); negative-logic signals have an extra "N" at or very near the beginning of the signal name (e.g., EPNPErrorB for PBus Negative-TRUE Parity Error).
P Interface
EPData=INT[32], -- address/data to cache/FP during PhA, data to/from cache/FP during PhB
EPRejectB<BOOL, -- received during PhB
EPFaultB<EnumType["Dragon.PBusFaults"], -- received during PhB, together with the last EPRejectB of a sequence. On the next PhA, the EU still freezes, but saves the Fault address in RAM. No instruction following a faulty Cache access should modify the carry!
EPParityB=BOOL, -- during PhB
EPNPErrorB=BOOL, -- parity error
Timing and housekeeping interface
PhA, PhB<BOOL,
ResetAB<BOOL,
Vdd, Gnd, PadVdd, PadGnd<BOOL,
I interface
KBus= INT[32],
PhiB: a, b, and c ram addresses are multiplexed on KBus. a=[0..7], b=[8..15], c=[16..23],
PhiA: data moves from EU -> IFU iff cAdr in [ifuXBus..ifuLast] during the previous PhiB, otherwise IFU may be using bus to pass data to EU or control to FP.
The following signals change during 1B and are stable during 2A.
EUAluLeftSrc1BA < EnumType["Dragon.ALULeftSources"],
EUAluRightSrc1BA < EnumType["Dragon.ALURightSources"],
EUStore2ASrc1BA < EnumType["Dragon.Store2ASources"],
EURes3AisCBus2BA < BOOL, -- multiplexor control to result3AB
EUSt3AisCBus2BA < BOOL, -- multiplexor control to store3AB
EURes3BisPBus3AB < BOOL, -- multiplexor control to cBusResult3BA
EUCheckParity3AB < BOOL, -- instructs EU to Check parity of cache reads.
EUWriteToPBus3AB < BOOL, -- Tells EU to drive PBus on 3B. Used for Store instructions, and issued every instruction following an EPrejectB. This is not necessary, but so far OK and simple.
EUAluOp2AB  < EnumType["Dragon.ALUOps"],
EUCondSel2AB  < EnumType["Dragon.CondSelects"],
EUCondition2BA > BOOL, -- latched to hide precharge in the carry propagator etc
InstrCountAB   < INT[32], -- just for debugging simulation
Serial debugging interface
All the following signals change during PhA and PhB, giving an entire clock cycle for them to change, are sampled during PhB, and are applied to the data path during the following PhA.
DShiftAB  < BOOL, -- shift the shift register by 1 bit if ~DNSelectAB
DExecuteAB < BOOL, -- interpret the content of the shift register if ~DNSelectAB
DNSelectAB < BOOL, -- if high, hold but don't Execute or Shift
DHoldAB  < BOOL, -- must be high before testing
DDataInAB < BOOL, -- sampled 1 full cycle after a PhB that DShiftAB is Asserted
DDataOutAB = BOOL -- changes in PhA after a PhB that DShiftAB is Asserted
]
State
euLogRef: REF REF,
Pipeline registers
aluLeft, aluRight, aluOut: Dragon.HexWord,
result2BA, result3AB, cBusResult3BA: Dragon.HexWord,
store2AB, store2BA, store3AB: Dragon.HexWord,
RAM, RAM addresses and various aliased registers
The RAM is organised as follows (Ref DragOpsCross):
registers 0 through 127 constitute the stack
128: euJunk i.e. no write
129: (currently spare)
130: euMAR
131: euField
132 through 147 are auxilliary registers
148 thru 151: (currently spare)
152 through 163 are constants
Any aAdr larger than 163 is illegal
aAdr, bAdr, cAdr: Dragon.HexByte,
ram: ARRAY Dragon.HexByte OF Dragon.HexWord,
field: Dragon.HexWord, -- a copy is kept as RAM[fieldAdr], another copy is in the field unit. Any write is performed to both physical locations, any read is from the most convenient location.
Control bits and register whose useful life does not extend beyond individual macro instructions and therefore need not be pampered
mqAB,   mqBA:    Dragon.HexWord,
mulSubAB,  mulSubBA:  BOOL,
divRmsbAB,  divRmsbBA:  BOOL,
divRCorrAB,  divRCorrBA:  BOOL,
mar: Dragon.HexWord, -- contains the faulty address after a cache access
Bits and pieces for the ALU and the Field Unit
kBusAB: Dragon.HexWord, -- a register to hold KBus for use in the field unit if required
carryAB, carryBA: BOOL, -- carryBA is the output of the adder modified by the opcode of the previous operation and latched if no trap; carryAB is a copy of carryBA if there is no reject or fault.
conditionB: BOOL,
overflow:  BOOL, -- temporary state, does not survive through a whole cycle
Other pieces from the Control pipeline
rejectBA: BOOL, -- a copy of EPRejectB stable during PhiA
faultBA: BOOL, -- a latched veriosn of EPFaultB # None
parityResult3, parityStore3: BOOL -- parity associated to the register
EvalSimple
log ← NARROW[euLogRef^];
ram[Dragon.PRtoByte[euConstant]] ← 0; -- done by IFU
PhiA phase. Note that rejectBA alone inhibits almost any state change during PhiA
IF PhA THEN {
Dragon.Assert[aAdr>=0 AND aAdr<164];
No simultaneous bypass control signals on
The last cycle of a faulty cache access: rejectBA and EPFaultB are both high
IF rejectBA AND faultBA THEN {ram[marAdr] ← cBusResult3BA}; -- save the faulty address
IF NOT (rejectBA OR EUCondition2BA) THEN {carryAB ← carryBA};
These state bits should not require the special protection given carry since they are not shared between macro instructions
mqAB      ← mqBA;
mulSubAB   ← mulSubBA;
divRmsbAB   ← divRmsbBA;
divRCorrAB   ← divRCorrBA;
rejected ← rejectBA;
IF NOT rejectBA THEN {
kBusAB ← Dragon.LFD[KBus];
aluLeft ← SELECT EUAluLeftSrc1BA FROM
aBus  => ram[aAdr], -- watch out for illegal addresses
rBus  => result2BA,
cBus  => cBusResult3BA,
ENDCASE => Dragon.Assert[FALSE];
aluRight ← SELECT EUAluRightSrc1BA FROM
bBus  => ram[bAdr]; -- watch out for illegal addresses
rBus  => result2BA,
cBus  => cBusResult3BA,
kBus  => Dragon.LFD[KBus],
ENDCASE => Dragon.Assert[FALSE];
store2AB ← SELECT EUStore2ASrc1BA FROM
bBus  => ram[bAdr]; -- watch out for illegal addresses
cBus  => cBusResult3BA,
rBus  => result2BA,
ENDCASE => Dragon.Assert[FALSE];
result3AB ← SELECT EURes3AisCBus2BA FROM
TRUE  => cBusResult3BA,
ENDCASE => result2BA;
Always send address to Cache during PhiA
EPData ← Dragon.LTD[result2BA];
store3AB ← SELECT EUSt3AisCBus2BA FROM
TRUE  => cBusResult3BA,
ENDCASE => store2BA;
parityStore3 ← IF EUSt3AisCBus2BA THEN parityResult3 ELSE CacheOps.Parity32[store2BA];
It is particularly important that no store in the RAM happens during rejectBA
RejectBA should run thru the cAdr decoder
SELECT TRUE FROM
cAdr IN 0 to 175
cAdr IN [Dragon.PRtoByte[euStack] .. Dragon.PRtoByte[euBogus])
=> ram[cAdr] ← cBusResult3BA;
cAdr IN 240 to 255
cAdr IN [Dragon.PRtoByte[ifuXBus] .. Dragon.PRtoByte[ifuLast]]
=> KBus ← Dragon.LTD[cBusResult3BA];
ENDCASE => Dragon.Assert[FALSE, "EU cAdr out of range"];
phBLastFLAG ← FALSE;
};
};
PhiB phase. Most of the computations take place during PhiB
IF PhB THEN { -- EPFaultB ???
PlusOrMinusAluRight: Dragon.HexWord; -- temporary used for subtraction
c32: BOOL;
IF NOT phBLastFLAG AND log # IO.noWhereStream THEN {
reset ← resetting AND NOT ResetAB;
EULog[InstrCountAB,aAdr,aluLeft,bAdr,aluRight,store2AB,cAdr,cBusResult3BA];
resetting ← ResetAB};
phBLastFLAG ← TRUE;
rejectBA ← EPRejectB;
EPRejectB is valid at the end of PhiB but bogus during PhiA, so it must be latched at the end of PhiB. A current problem is that the source for cBusResult3BA depends upon EPRejectB, and the choice is made during the same PhiB as it is received. So this statement has to be first.
faultBA ← EPFaultB#Dragon.None;
Updating the RAM addresses
aAdr ← BitOps.ECFD[KBus, 32, 0, 8];
bAdr ← BitOps.ECFD[KBus, 32, 8, 8];
cAdr ← BitOps.ECFD[KBus, 32, 16, 8];
PBus: notice that in case of reject during a store, we keep sending the data even though it is useless
Dragon.Assert[NOT (EUWriteToPBus3AB AND EURes3BisPBus3AB)];
IF EUWriteToPBus3AB
THEN { -- store in progress
EPData   ← Dragon.LTD[store3AB]; -- send data to Cache (Store)
EPParityB  ← CacheOps.Parity32[store3AB]; -- EMM, was parityStore3
cBusResult3BA ← result3AB}
save the address in cBusResult3BA, done normally since NOT EURes3BisPBus3AB
ELSE { -- either a fetch, an op, or a move in progress
IF rejectBA -- Fetch with reject => save address in cBusResult3BA and don't listen to IFU
THEN cBusResult3BA ← result3AB
ELSE -- Fetch without reject -- IF EURes3BisPBus3AB
THEN {
cBusResult3BA ← Dragon.LFD[EPData];
parityResult3 ← EPParityB;
IF EUCheckParity3AB AND CacheOps.Parity32[cBusResult3BA]#parityResult3
THEN EPNPErrorB ← FALSE}
ELSE cBusResult3BA ← result3AB}; -- op or move
Data pipe
store2BA ← store2AB;
Alu and Field Unit computation
Set Default values of state
carryBA  ← carryAB;
mqBA   ← mqAB;
divRmsbBA ← divRmsbAB;
divRCorrBA ← divRCorrAB;
mulSubBA ← mulSubAB;
SELECT EUAluOp2AB FROM
SAdd => {
[aluOut, c32] ← DoubleADD[aluLeft, aluRight, carryAB];
result2BA ← aluOut;
carryBA ← FALSE};
SSub => {
[aluOut, c32] ← DoubleSUB[aluLeft, aluRight, carryAB];
result2BA ← aluOut;
carryBA ← FALSE};
UAdd => {
[aluOut, c32] ← DoubleADD[aluLeft, aluRight, carryAB];
result2BA ← aluOut;
carryBA ← c32};
USub => {
[aluOut, c32] ← DoubleSUB[aluLeft, aluRight, carryAB];
result2BA ← aluOut;
carryBA ← NOT c32};
VAdd => {
[aluOut, c32] ← DoubleADD[aluLeft, aluRight, FALSE];
result2BA ← aluOut};
VSub => {
[aluOut, c32] ← DoubleSUB[aluLeft, aluRight, FALSE];
result2BA ← aluOut};
LAdd => {
[aluOut, c32] ← DoubleADD[aluLeft, aluRight, FALSE];
result2BA ← aluOut;
carryBA ← FALSE};
LSub => {
[aluOut, c32] ← DoubleSUB[aluLeft, aluRight, FALSE];
result2BA ← aluOut;
carryBA ← FALSE};
FOP => { -- field descriptor provided by Field
result2BA ← aluOut ← FieldOp[aluLeft, aluRight, field]};
FOPK => { -- field descriptor provided by kBusAB. Otherwise identical to FOP
result2BA ← aluOut ← FieldOp[aluLeft, aluRight, kBusAB]};
And => {
result2BA ← aluOut ← WordOp[and, aluLeft, aluRight]};
Or => {
result2BA ← aluOut ← WordOp[or, aluLeft, aluRight]};
Xor => {
result2BA ← aluOut ← WordOp[xor, aluLeft, aluRight]};
BndChk => {
[aluOut, c32] ← DoubleSUB[aluLeft, aluRight, FALSE];
result2BA ← aluLeft};
MulLd => {
aluOut  ← aluLeft; -- typically, aluOut will be tested for 0
result2BA ← 0;
mqBA   ← aluRight;
mulSubBA ← FALSE};
MulStep => {
tempR2, tempMQ: Dragon.HexWord;
zero: BOOL
EBFL[mqAB, 30] AND EBFL[mqAB, 31] AND mulSubAB OR
~EBFL[mqAB, 30] AND ~EBFL[mqAB, 31] AND ~mulSubAB;
two: BOOL
EBFL[mqAB, 30] AND ~EBFL[mqAB, 31] AND ~mulSubAB OR
~EBFL[mqAB, 30] AND EBFL[mqAB, 31] AND mulSubAB;
one: BOOL ← ~zero AND ~two;
mulSubBA ← EBFL[mqAB, 30];
log.PutF["\nEU Multiply operation: %g %g times the multiplicand",
IO.rope[ (IF EBFL[mqAB, 30] THEN "Subtract" ELSE "Add") ],
IO.card[ (IF two THEN 2 ELSE IF one THEN 1 ELSE 0 ] ];
SELECT TRUE FROM
zero => {
aluOut    ← aluLeft;
[tempR2, tempMQ] ← DblShiftRt[EBFL[aluOut, 0], aluOut, mqAB];
[result2BA, mqBA] ← DblShiftRt[EBFL[tempR2, 0], tempR2, tempMQ]};
one => {
IF mulSubBA
THEN [aluOut, ] ← DoubleSUB [aluLeft, aluRight, FALSE]
ELSE [aluOut, ] ← DoubleADD [aluLeft, aluRight, FALSE];
[tempR2, tempMQ] ← DblShiftRt[EBFL[aluOut, 0], aluOut, mqAB];
[result2BA, mqBA]  ← DblShiftRt[EBFL[tempR2, 0], tempR2, tempMQ]};
two => {
[tempR2, tempMQ] ← DblShiftRt[EBFL[aluLeft, 0], aluLeft, mqAB];
IF mulSubBA
THEN [aluOut, ] ← DoubleSUB [tempR2, aluRight, FALSE]
ELSE [aluOut, ] ← DoubleADD [tempR2, aluRight, FALSE];
[result2BA, mqBA]  ← DblShiftRt[EBFL[aluOut, 0], aluOut, tempMQ]};
ENDCASE => ERROR};
RdMQ => {
result2BA ← aluOut ← mqAB};
Divide Step 0
DivLdDbl => {
result2BA ← aluOut ← aluLeft;
mqBA     ← aluRight };
Divide Step 1
DivCk => { -- Check for carry on first subtract
[aluOut, carryBA] ← DoubleSUB[aluLeft, aluRight, FALSE]; -- Trap if carry
[divRmsbBA, result2BA, mqBA] ← DblShiftLt[aluOut, mqAB, FALSE] };
Divide Step 2..32
DivStep => {
bitIn: BOOL;
IF EBFL[mqAB,31]
THEN [aluOut, carryBA] ← DoubleSUB [aluLeft, aluRight, FALSE]
ELSE [aluOut, carryBA] ← DoubleADD [aluLeft, aluRight, FALSE];
bitIn ← (EBFL[mqAB,31] # (carryBA = divRmsbAB)); -- reverse op if Rmsb = carry
[divRmsbBA, result2BA, mqBA] ← DblShiftLt[ aluOut, mqAB, bitIn] };
DivAdjQ => {
bitIn: BOOL;
IF EBFL[mqAB,31]
THEN [aluOut, carryBA] ← DoubleSUB [aluLeft, aluRight, FALSE]
ELSE [aluOut, carryBA] ← DoubleADD [aluLeft, aluRight, FALSE];
bitIn ← (EBFL[mqAB,31] # (carryBA = divRmsbAB)); -- reverse op if Rmsb = carry
divRCorrBA ← NOT bitIn;
mqBA   ← ShiftLt[mqAB, bitIn];
result2BA  ← aluOut };
DivAdjR => {
result2BAAlt: Dragon.HexWord ← IF divRCorrAB THEN aluRight ELSE 0;
IF EBFL[mqAB,31]
THEN [aluOut, ] ← DoubleSUB [aluLeft, result2BAAlt, FALSE]
ELSE [aluOut, ] ← DoubleADD [aluLeft, result2BAAlt, FALSE];
carryBA  ← FALSE;
mqBA   ← mqAB;
result2BA ← aluOut};
ENDCASE => ERROR Stop["Invalid ALU Operation"];
Here we compute the overflow by checking the high-order bits of the operands (aluLeft and aluRight or - aluRight) and of the result
SELECT EUAluOp2AB FROM
SSub, USub, VSub, LSub =>
[PlusOrMinusAluRight, ] ← DoubleSUB[0, aluRight, FALSE];
ENDCASE => PlusOrMinusAluRight ← aluRight;
overflow ← (EBFL[aluLeft, 0] = EBFL[PlusOrMinusAluRight, 0]) AND (EBFL[aluLeft, 0] # EBFL[aluOut, 0]);
Condition and trap generation
conditionB ← SELECT EUCondSel2AB FROM
EZ   => aluOut=0, -- aluOut=0
LZ   => EBFL[aluOut, 0], -- aluOut<0 by checking the high-order bit
LE   => (aluOut=0) OR EBFL[aluOut, 0], -- aluOut<=0,
OvFl  => overflow,
BC   =>   EBFL[aluLeft, 0] OR-- arg   < 0
    NOTEBFL[aluOut, 0],  -- arg-limit >= 0
IL   => (EBFL[aluLeft, 0]#EBFL[aluLeft, 1]) OR
    (EBFL[aluRight, 0]#EBFL[aluRight, 1]) OR
    (EBFL[aluOut, 0]#EBFL[aluOut, 1]),
DivOvFl => carryBA,
False  => FALSE,
NE  => aluOut#0, -- aluOut#0
GE   => NOT EBFL[aluOut, 0], -- aluOut>=0 by checking the high-order bit
GZ   => NOT ((aluOut=0) OR EBFL[aluOut, 0]), -- aluOut>0,
NotOvFl => NOT overflow,
NotBC  => NOT EBFL[aluLeft, 0] -- 0<=arg -- AND EBFL[aluOut, 0] -- arg-limit<0 --,
NotIL  => NOT ((EBFL[aluLeft, 0]#EBFL[aluLeft, 1]) OR
    (EBFL[aluRight, 0]#EBFL[aluRight, 1]) OR
    (EBFL[aluOut, 0]#EBFL[aluOut, 1])),
True  => TRUE,
ENDCASE => ERROR Stop["Invalid EUCondition2BA Code"];
EUCondition2BA ← conditionB };
Initializer
IF initData#NIL THEN
WITH initData SELECT FROM
pl: Atom.PropList =>
BEGIN
r: REF;
IF (r ← pl.GetPropFromList[$LogRef]) # NIL THEN
euLogRef ← NARROW[r, REF REF];
END;
ENDCASE => NULL;
ENDCELLTYPE;
CEDAR -- Logging stuff
EULog:PROC[
instr:  BitOps.BitDWord,
aRam:  Dragon.HexByte,
left:  Dragon.HexWord,
bRam:  Dragon.HexByte,
right:  Dragon.HexWord,
store:  Dragon.HexWord,
cRam:  Dragon.HexByte,
result:  Dragon.HexWord] = {
SELECT TRUE FROM
reset => {
log.Flush[];
log.PutF["\n\n\nEULog.txt %g", IO.time[] ];
log.PutF["\n**Reset**" ]};
rejected => {
log.PutF["\n**Rejected**" ]};
ENDCASE => {
log.PutF["\n%5g",   IO.card[ Dragon.LFD[instr] ] ];
log.PutF[" %02x:%08x", IO.card[aRam], IO.card[ left  ] ];
log.PutF[" %02x:%08x", IO.card[bRam], IO.card[ right ] ];
log.PutF[":%08x",        IO.card[ store  ] ];
log.PutF[" %02x:%08x", IO.card[cRam], IO.card[ result ] ];
} };
reset, resetting: BOOLFALSE;
rejected: BOOLFALSE;
phBLastFLAG: BOOLFALSE;
log: IO.STREAMIO.noWhereStream;
EUStateHandler: Cucumber.Handler = NEW[Cucumber.HandlerRep ← [
PrepareWhole: EUStatePrepareProc,
PartTransfer: EUTransferProc
]];
EUStatePrepareProc: PROC [ whole: REF ANY, where: IO.STREAM, direction: Cucumber.Direction, data: REF ANY ] RETURNS [ leaveTheseToMe: Cucumber.SelectorList ] -- Cucumber.Bracket -- =
{leaveTheseToMe ← LIST[$euLogRef]};
EUTransferProc: PROC [ whole: REF ANY, part: Cucumber.Path, where: IO.STREAM, direction: Cucumber.Direction, data: REF ANY ] -- Cucumber.PartTransferProc -- = {NULL};
Cucumber.Register[EUStateHandler, CODE[EUStateRec]];