LizardLiverImpl.mesa
Copyright Ó 1984, 1985, 1986, 1987 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) April 21, 1987 1:54:29 pm PDT
McCreight, January 22, 1986 12:16:44 pm PST
DIRECTORY
Basics USING [CompareInt, Comparison],
DragOpsCross USING [EUConstIndex, EUStackSize, FieldDescriptor, FourBytes, IFUOverflow, IFUStackSize, Inst, IOOperand, ioRescheduleRequest, ioResetRequest, LRBformat, LRRBformat, PCmdFormat, ProcessorRegister, QRformat, RJBformat, RRformat, TrapIndex, TwoBytes, TwoHalves, Word, ZerosByte, ZerosWord],
DragOpsCrossUtils USING [AddDelta, ByteToCard, CardToFieldDescriptor, CardToWord, HalfToCard, TrapIndexToBytePC, VanillaAdd, VanillaSub, WordToBytes, WordToCard, WordToHalves, WordToInt, XopToBytePC],
HandCodingUtil USING [GetInstArray, GetRegArray],
IO USING [PutFR, rope],
LizardHeart USING [ALUOps, ChangeLogger, Control, DoALUOp, FieldUnit, InstDoneProc, InstStartProc, IOChangeProc, LizardIFUStackIndex, LizardIFUStackSize, MemChangeProc, NoFault, OutsideEnvelope, Processor, RegChangeProc, RegPlus, RegToWord, StackPlus, TrapPC, WillPushOverflow, WordToReg],
LizardLiver USING [],
LizardTweaker USING [OpcodeHandler, OpcodeRegistry, OpcodeRegistryEntry, OpcodeRegistryRep];
LizardLiverImpl: CEDAR PROGRAM
IMPORTS Basics, DragOpsCrossUtils, HandCodingUtil, IO, LizardHeart
EXPORTS LizardLiver, LizardTweaker
= BEGIN OPEN DragOpsCross, DragOpsCrossUtils, LizardHeart;
careful: BOOLTRUE;
If careful, then this module will make extra consistency checks to determine if we are executing instructions that are "dangerous" in normal code. OutsideEnvelope will be raised when one of these checks fails. Diagnostics programs should interpret "← LizardLiverImpl.careful ← FALSE" to turn this checking off. Some checking is always present because Lizard cannot reasonably do exactly what the real IFU does, and no one should be testing for these cases.
checkReadonlyConst: BOOLFALSE;
If checkReadonlyConst then stores into readonly constant registers will be checked. This is defaulted as FALSE since we are likely to allow this in the machine.
Register: PUBLIC PROC [inst: Inst, handler: LizardTweaker.OpcodeHandler, data: REFNIL] = {
Register an interceptor for the given opcode.
IF opcodeRegistry = NIL THEN
opcodeRegistry ← NEW[LizardTweaker.OpcodeRegistryRep ← ALL[LizardTweaker.OpcodeRegistryEntry[NIL, NIL]]];
opcodeRegistry[inst] ← [handler, data];
flagsArray[inst].callMesa ← TRUE;
};
opcodeRegistry: LizardTweaker.OpcodeRegistry ← NIL;
Execute: PUBLIC PROC [processor: Processor, thisPC: Word, inst: Inst, rest: Word] RETURNS [newPC, rtnPC: Word, control: Control ← nextInst] = {
trapCode: TrapIndex ← NoFault;
logger: ChangeLogger = processor.logger;
Capture commonly used pieces of the processor state
regS: ProcessorRegister = WordToReg[processor.regs[ifuS]];
regS is the current euRegister for the top of stack
regL: ProcessorRegister ← WordToReg[processor.regs[ifuL]];
regL is the current euRegister for the frame base
sM1: ProcessorRegister ← StackPlus[regS, -1];
sM1 is the current euRegister for just below the top of stack
euBusyReg: ProcessorRegister ← processor.euBusyReg;
euBusyReg is the current euRegister busy because of a previous fetch
Compute some common things based on the flags
flags: InstFlags ← flagsArray[inst];
cycles: NAT ← flags.cycles;
alphaBetaGammaDeltaZ: CARD ← WordToCard[rest];
stackEffect: INTEGER ← flags.stackEffect;
resReg: ProcessorRegister ← StackPlus[regS, stackEffect];
The destination is often given by the stack effect (especially when stackEffect = 1)
topOfStack: Word ← IF flags.usesTop THEN RegFetchInline[regS] ELSE ZerosWord;
Note that we don't fetch unless we have to. This is mostly to not get a lockout cycle on the topOfStack unless we really use it.
resWord: Word ← topOfStack;
It is useful to have this be initialized to the top of stack
resAddr: Word ← topOfStack;
It is useful to have this be initialized to the top of stack
bytes: CARDINAL ← flags.bytes;
dispatch: Inst ← SELECT TRUE FROM
flags.mod16 => VAL[ORD[inst] - (ORD[inst] MOD 16)],
ENDCASE => inst;
Capture some common quantities based on the operand bytes
alphaBetaZ: CARDINAL ← 0;
alphaZ: CARDINAL ← 0;
betaZ: CARDINAL ← 0;
formBytes2: TwoBytes;
for 2-byte instructions
formBytes3: FourBytes;
for 3-byte instructions
Routines for fetching and storing processor registers
RegFetch: PROC [reg: ProcessorRegister] RETURNS [Word] = {
Any funny side effects are handled in LIP.
IF reg = euBusyReg THEN {
processor.stats.cycles ← processor.stats.cycles + 1;
processor.stats.regBusyCycles ← processor.stats.regBusyCycles + 1;
euBusyReg ← euJunk;
};
IF NOT processor.regsGood[reg] THEN InitFault[reg];
RETURN [processor.regs[reg]];
};
RegFetchInline: PROC [reg: ProcessorRegister] RETURNS [Word] = INLINE {
Any funny side effects are handled in LIP.
IF reg = euBusyReg THEN {
processor.stats.cycles ← processor.stats.cycles + 1;
processor.stats.regBusyCycles ← processor.stats.regBusyCycles + 1;
euBusyReg ← euJunk;
};
IF NOT processor.regsGood[reg] THEN InitFault[reg];
RETURN [processor.regs[reg]];
};
InitFault: PROC [reg: ProcessorRegister] = {
SIGNAL OutsideEnvelope[IO.PutFR["Register %g was read before it was initialized.",
IO.rope[HandCodingUtil.GetRegArray[][reg]]]];
};
RegStore: PROC [reg: ProcessorRegister, word: Word] = {
Any funny side effects are handled in SIP.
IF reg # euJunk THEN {
IF logger # NIL THEN {
regChange: RegChangeProc ← logger.regChange;
IF regChange # NIL THEN {
old: Word ← processor.regs[reg];
SELECT reg FROM
ifuEldestPC =>
old ← processor.ifuStack[processor.eldest].pc;
ifuEldestL => {
old ← LOOPHOLE[processor.ifuStack[processor.eldest].status];
};
ifuYoungestPC =>
old ← processor.ifuStack[processor.youngest].pc;
ifuYoungestL =>
old ← LOOPHOLE[processor.ifuStack[processor.youngest].status];
ENDCASE;
regChange[logger.data, processor, reg, old, word];
};
};
processor.regsGood[reg] ← TRUE;
processor.regs[reg] ← word;
};
};
FastRegStore: PROC [reg: ProcessorRegister, word: Word] = INLINE {
A faster version of RegStore. Not to be used for the following IFU regs: ifuEldestPC, ifuEldestL, ifuYoungestPC, ifuYoungestL.
IF reg # euJunk THEN {
IF logger # NIL THEN {
regChange: RegChangeProc ← logger.regChange;
IF regChange # NIL THEN {
old: Word ← processor.regs[reg];
regChange[logger.data, processor, reg, old, word];
};
};
processor.regsGood[reg] ← TRUE;
processor.regs[reg] ← word;
};
};
Routines for fetching and storing memory words (incomplete)
MemFetch: PROC [addr: Word] RETURNS [word: Word, code: TrapIndex] = INLINE {
rejectCycles: INT;
[word, code, rejectCycles]
← processor.euCache.fetch[
processor.euCache, addr, processor.stats.cycles, processor.userMode];
IF rejectCycles # 0 THEN processor.stats.cycles ← processor.stats.cycles + rejectCycles;
IF code = NoFault
THEN processor.stats.euFetches ← processor.stats.euFetches + 1
ELSE FastRegStore[euMAR, addr];
};
MemStore: PROC [addr: Word, word: Word] RETURNS [tx: TrapIndex] = {
rejectCycles: INT;
old: Word;
[old, tx, rejectCycles]
← processor.euCache.store[
processor.euCache, addr, word, processor.stats.cycles, processor.userMode];
IF rejectCycles # 0 THEN processor.stats.cycles ← processor.stats.cycles + rejectCycles;
IF tx = NoFault
THEN {
processor.stats.euStores ← processor.stats.euStores + 1;
IF logger # NIL THEN {
memChange: MemChangeProc ← logger.memChange;
IF memChange # NIL THEN memChange[logger.data, processor, addr, old, word];
};
}
ELSE FastRegStore[euMAR, addr];
};
IOFetch: PROC [cmd: PCmdFormat, addr: Word] RETURNS [word: Word, code: TrapIndex] = {
Not yet implemented
RETURN [ZerosWord, NoFault];
};
IOStore: PROC [cmd: PCmdFormat, addr: Word, word: Word] RETURNS [TrapIndex] = {
Not yet implemented (except for ioRescheduleRequest & ioResetRequest)
IF logger # NIL THEN {
ioChange: IOChangeProc ← logger.ioChange;
IF ioChange # NIL THEN
ioChange[logger.data, processor, addr, IOFetch[cmd, addr].word, word];
};
SELECT WordToCard[word] FROM
ioRescheduleRequest => {
processor.rescheduleRequested ← TRUE;
};
ioResetRequest => {
processor.resetRequested ← TRUE;
};
ENDCASE;
RETURN [NoFault];
};
A routine to calculate regs for RR, QR, and RJB format
CalcReg: PROC [aux,opt: BOOL, regX: [0..15], dest: BOOLFALSE]
RETURNS [reg: ProcessorRegister] = INLINE {
First, handle aux regs and local regs
IF NOT opt THEN
RETURN [IF aux THEN RegPlus[euAux, regX] ELSE StackPlus[regL, regX]];
Handle constant regs, [S], and [S-1].
SELECT regX FROM
IN EUConstIndex => RETURN [RegPlus[euConstant, regX]];
12 => RETURN [regS];
13 => RETURN [sM1];
ENDCASE;
For destination regs, the register is [S+1]+.
IF dest THEN {stackEffect ← stackEffect+1; RETURN [StackPlus[regS, 1]]};
For source regs, the register is either [S]-, or [S-1]-
stackEffect ← stackEffect - 1;
RETURN [IF regX = 14 THEN regS ELSE sM1];
};
A routine to setup state when a trap occurs. This variety of trap aborts the instruction so that no effects (other than possibly setting carry) have taken place.
CauseTrap: PROC [code: TrapIndex] = {
All traps do trapsEnabled ← userMode ← FALSE, but do so by returning a control of doAbort. If an IFU stack overflow would result, then that has precedence over anything else. This routine does not actually do the push to the IFU stack, however.
rtnPC ← thisPC;
stackEffect ← 0;
IF processor.trapsEnabled THEN
SELECT TRUE FROM
LizardHeart.WillPushOverflow[processor] =>
IFUStackOverflow has precedence over other traps
code ← IFUStackOverflowTrap;
ENDCASE;
newPC ← TrapPC[code];
control ← doAbort;
IF code = IFUStackOverflowTrap THEN
processor.stats.stackOver ← processor.stats.stackOver + 1;
};
AdjustL: PROC [from: ProcessorRegister] RETURNS [BOOL] = {
newValue: ProcessorRegister = StackPlus[from, alphaZ];
SELECT alphaZ FROM
IN [EUStackSize/4..256-EUStackSize/4) =>
IF careful THEN SIGNAL OutsideEnvelope["VERY suspicious L adjustment!"];
ENDCASE;
FastRegStore[ifuL, RegToWord[newValue]];
RETURN [TRUE];
};
Checking for EU stack overflow
AdjustS: PROC [from: ProcessorRegister] RETURNS [BOOL] = {
newValue: ProcessorRegister = StackPlus[from, alphaZ];
SELECT alphaZ FROM
IN [EUStackSize/4..256-EUStackSize/4) =>
IF careful THEN SIGNAL OutsideEnvelope["VERY suspicious S adjustment!"];
ENDCASE;
IF processor.trapsEnabled THEN {
sLimit: ProcessorRegister = WordToReg[processor.regs[ifuSLimit]];
IF (newValue.ORD-sLimit.ORD+EUStackSize) MOD EUStackSize IN [0..16) THEN {
CauseTrap[EUStackOverflowTrap];
RETURN [FALSE];
};
};
FastRegStore[ifuS, RegToWord[newValue]];
RETURN [TRUE];
};
WillEUStackOverflow: PROC RETURNS [overflow: BOOLFALSE] = INLINE {
IF stackEffect # 0 AND processor.trapsEnabled THEN {
newS: ProcessorRegister = StackPlus[regS, stackEffect];
sLimit: ProcessorRegister = WordToReg[processor.regs[ifuSLimit]];
delta: CARDINAL = LOOPHOLE[newS.ORD-sLimit.ORD];
IF delta MOD EUStackSize IN [0..16) THEN {
overflow ← TRUE;
IF stackEffect < 0 AND careful THEN
SIGNAL OutsideEnvelope["Backing up S into EU overflow!"];
};
};
};
newPC ← rtnPC ← AddDelta[bytes, thisPC];
Init the next PC and the return PC to be just after this instruction
IF flags.callMesa AND opcodeRegistry # NIL THEN {
entry: LizardTweaker.OpcodeRegistryEntry ← opcodeRegistry[inst];
IF entry.handler # NIL THEN
IF NOT entry.handler[entry.data, processor, inst, rest] THEN RETURN;
};
**** This is where we log the start of the instruction, providing that someone is interested.
IF logger # NIL THEN {
instStart: InstStartProc ← logger.instStart;
IF instStart # NIL THEN instStart[logger.data, processor, thisPC, inst, rest];
};
SELECT bytes FROM
2 => {
alphaZ ← alphaBetaGammaDeltaZ;
formBytes2 ← [LOOPHOLE[inst], LOOPHOLE[alphaZ]];
};
3 => {
alphaBetaZ ← alphaBetaGammaDeltaZ;
alphaZ ← alphaBetaZ / 256;
betaZ ← alphaBetaZ MOD 256;
formBytes3 ← [LOOPHOLE[inst], LOOPHOLE[alphaZ], LOOPHOLE[betaZ], ZerosByte];
};
ENDCASE => NULL;
processor.euBusyReg ← euJunk;
In most cases the euBusyReg is set to not busy.
{
start scope for EXITS, all instruction dispatching is handled within this block.
SELECT dispatch FROM
dDFC => {
Effect: call proc at AlphaBetaGammaDelta
newPC ← rest;
GO TO call;
};
dLIQB => {
Effect: [S]𡤊lphaBetaGammaDelta; S←S+1
resWord ← rest;
GO TO storeReg;
};
dADDQB, dSUBQB => {
Effect: [S] ← [S] ± AlphaBetaGammaDelta ± carry; trap on overflow, clear carry
[resWord, trapCode] ← DoALUOp[
processor,
topOfStack,
rest,
IF inst = dADDQB THEN SAdd ELSE SSub,
ALUCondOver];
GO TO aluDone;
};
dJ5 => {
Effect: noop
GO TO noCheck;
};
dJQB => {
Effect: noop
newPC ← rest;
GO TO jump;
};
dOR, dAND, dRX, dBC, dADD, dSUB, dLADD, dLSUB => {
Effect: [S-1]←[S-1] op [S]; S←S-1;
op: ALUOps;
cond: TrapIndex ← ALUCondFalse;
wordA: Word = RegFetchInline[sM1];
SELECT inst FROM
dOR => {
Effect: [S-1]←[S-1] OR [S]; S←S-1
op ← Or;
};
dAND => {
Effect: [S-1]←[S-1] AND [S]; S←S-1
op ← And;
};
dRX => {
Effect: [S-1]←([S-1]+[S])^; S←S-1
resAddr ← VanillaAdd[wordA, topOfStack];
GO TO memFetch;
};
dBC => {
Effect: trap if [S-1] < 0 OR [S-1]-[S] >= 0; S←S-1
op ← BndChk;
cond ← ALUCondBC;
};
dADD => {
Effect: [S-1]←[S-1]+[S]+carry; carry𡤀 trap on overflow; S←S-1
op ← SAdd;
cond ← ALUCondOver;
};
dSUB => {
Effect: [S-1]←[S-1]-[S]-carry; carry𡤀 trap on overflow; S←S-1
op ← SSub;
cond ← ALUCondOver;
};
dLADD => {
Effect: [S-1]←[S-1]+[S]; carry𡤀 trap on overflow or Lisp NaN; S←S-1
op ← LAdd;
cond ← ALUCondIL;
};
dLSUB => {
Effect: [S-1]←[S-1]-[S]; carry𡤀 trap on overflow or Lisp NaN; S←S-1
op ← LSub;
cond ← ALUCondIL;
};
ENDCASE => GO TO xop;
[resWord, trapCode] ← DoALUOp[processor, wordA, topOfStack, op, cond];
GO TO aluDone;
};
dDUP => GO TO storeReg;
Effect: [S+1]←[S]; S←S+1
dDIS => GO TO done;
Effect: S←S-1
dEXDIS => GO TO storeReg;
Effect: [S-1]←[S]; S←S-1
dSFC => {
Effect: call proc at [S]; S←S-1
newPC ← topOfStack;
GO TO call;
};
dSFCI => {
Effect: call proc at ([S])^
[newPC, trapCode] ← MemFetch[topOfStack];
IF trapCode # NoFault THEN GO TO memFault;
GO TO call;
};
dRETN => GO TO return;
Effect: return from proc (no stack change)
dKFC => {
Effect: PC←InstTrap[KFC]; set kernel mode; disable traps; S←S+1
IF LizardHeart.WillPushOverflow[processor]
THEN CauseTrap[IFUStackOverflowTrap]
ELSE newPC ← CardToWord[DragOpsCrossUtils.XopToBytePC[inst]];
control ← doAbort;
GO TO done;
};
dJ1 => GO TO noCheck;
Effect: noop
dJSD => {
Effect: PC←[S]; S←S-1
newPC ← topOfStack;
IF WillEUStackOverflow[] THEN GO TO euOverflow;
GO TO jump;
};
dJSR => {
Effect: PC←PC+[S]; S←S-1
newPC ← VanillaAdd[thisPC, topOfStack];
IF WillEUStackOverflow[] THEN GO TO euOverflow;
GO TO jump;
};
dLC0, dLC1, dLC2, dLC3, dLC4, dLC5, dLC6, dLC7, dLC8, dLC9, dLC10, dLC11 => {
Effect: [S+1]𡤌onstants[n]; S←S+1
resReg ← RegPlus[euConstant, ORD[inst] MOD 16];
GO TO pushReg;
};
dLR0 => {
Effect: [S+1]←[L+n]; S←S+1
resReg ← StackPlus[regL, ORD[inst] MOD 16];
GO TO pushReg;
};
dSR0 => {
Effect: [L+n]←[S]; S←S-1
resReg ← StackPlus[regL, ORD[inst] MOD 16];
GO TO storeReg;
};
dQOR, dQAND, dQRX, dQBC, dQADD, dQSUB, dQLADD, dQLSUB => {
OPEN form: LOOPHOLE[formBytes2, QRformat];
wordB: Word ← RegFetchInline[CalcReg[form.aux, form.opt, form.reg]];
op: ALUOps;
cond: TrapIndex ← ALUCondFalse;
sd: [0..1] ← 1;
srcReg: ProcessorRegister ← regS;
SELECT form.aOp FROM
topAtop => sd ← 0;
C: [S], A: [S], B: general
pushAtop => {};
C: [S+1]+, A: [S], B: general
pushA0 => srcReg ← euConstant;
C: [S+1]+, A: c0, B: general
pushA1 => srcReg ← RegPlus[euConstant, 1];
C: [S+1]+, A: c1, B: general
ENDCASE;
topOfStack ← RegFetchInline[srcReg]; -- the A operand word
resReg ← StackPlus[regS, sd]; -- the C register
stackEffect ← stackEffect + sd; -- the change to stack effect
SELECT inst FROM
dQOR => {
Effect: [S] ← Rs OR Rb
op ← Or;
};
dQAND => {
Effect: [S] ← Rs AND Rb
op ← And;
};
dQRX => {
Effect: [S] ← (Rs+[Rb])^
resAddr ← VanillaAdd[topOfStack, wordB];
GO TO memFetch;
};
dQBC => {
Effect: [S] ← Rs BC Rb; trap if [S] < 0 OR [S]-Rb >= 0
op ← BndChk;
cond ← ALUCondBC;
};
dQADD => {
Effect: [S] ← Rs+Rb+carry; carry𡤀 trap on overflow
op ← SAdd;
cond ← ALUCondOver;
};
dQSUB => {
Effect: [S] ← Rs-Rb-carry; carry𡤀 trap on overflow
op ← SSub;
cond ← ALUCondOver;
};
dQLADD => {
Effect: [S] ← Rs+Rb; carry𡤀 trap on overflow or Lisp NaN
op ← LAdd;
cond ← ALUCondIL;
};
dQLSUB => {
Effect: [S] ← Rs-Rb; carry𡤀 trap on overflow or Lisp NaN
op ← LSub;
cond ← ALUCondIL;
};
ENDCASE => ERROR;
[resWord, trapCode] ← DoALUOp[processor, topOfStack, wordB, op, cond];
GO TO aluDone;
};
dALS => {
Effect: L ← S + Alpha
[] ← AdjustL[regS];
GO TO noCheck;
};
dAL => {
Effect: L ← L + Alpha
[] ← AdjustL[regL];
GO TO noCheck;
};
dASL => {
Effect: S ← L + Alpha (no stack overflow check)
[] ← AdjustS[regL];
GO TO noCheck;
};
dAS => {
Effect: S ← S + Alpha (no stack overflow check)
[] ← AdjustS[regS];
GO TO noCheck;
};
dCST => {
Effect: atomic
[S+1] ← ([S-2]+AlphaZ)^;
IF [S+1] = [S] THEN [S-2]+AlphaZ)^ ← [S-1] ELSE [] ← ([S-2]+AlphaZ)^;
S←S+1
resAddr ← AddDelta[alphaZ, RegFetch[StackPlus[regS, -2]]];
[resWord, trapCode] ← MemFetch[resAddr];
SELECT TRUE FROM
trapCode # NoFault => GO TO memFault;
WillEUStackOverflow[] => GO TO euOverflow;
ENDCASE;
RegStore[resReg, resWord];
IF resWord = topOfStack THEN {
The conditional store was successful, so store the new word
resWord ← RegFetch[sM1];
GO TO memStore;
};
The conditional store was not successful, so refetch the word (which simulates releasing the bus)
[] ← MemFetch[resAddr];
GO TO noCheck;
};
dRET => {
Effect: S←L+Alpha; return from proc
IF NOT AdjustS[regL] THEN GO TO noCheck;
GO TO return;
};
dLIP => {
Effect: [S+1]←PReg[Alpha]; S←S+1
reg: ProcessorRegister ← LOOPHOLE[alphaZ];
IF reg > euLast THEN cycles ← cycles + 4;
IFU registers take longer to fetch than EU registers
SELECT reg FROM
ifuEldestPC => {
Reading from the ifuEldestPC register has the side effect of popping the entry from the IFU stack. We do a check for empty stacks here, but the processor really doesn't.
eldest: NAT ← processor.eldest;
entries: NAT ← processor.stackEntries;
IF processor.userMode THEN GO TO modeFault;
resWord ← processor.ifuStack[eldest].pc;
IF processor.stackEntries = 0
THEN SIGNAL OutsideEnvelope["Empty IFU stack in LIP ifuEldestPC."]
ELSE {
IF WillEUStackOverflow[] THEN GO TO euOverflow;
Check for overflow before altering the state!
processor.eldest ← (eldest + 1) MOD LizardIFUStackSize;
processor.stackEntries ← entries - 1;
};
};
ifuEldestL => {
IF processor.stackEntries = 0 THEN
SIGNAL OutsideEnvelope["Empty IFU stack in LIP ifuEldestL."];
resWord ← LOOPHOLE[processor.ifuStack[processor.eldest].status];
};
ifuYoungestPC => {
IF processor.stackEntries = 0 THEN
SIGNAL OutsideEnvelope["Empty IFU stack in LIP ifuYoungestPC."];
resWord ← processor.ifuStack[processor.youngest].pc;
};
ifuYoungestL => {
IF processor.stackEntries = 0
THEN SIGNAL OutsideEnvelope["Empty IFU stack in LIP ifuYoungestL."];
resWord ← LOOPHOLE[processor.ifuStack[processor.youngest].status];
};
ENDCASE =>
resWord ← RegFetch[reg];
GO TO storeReg
};
dSIP => {
Effect: PReg[Alpha]←[S]; S←S-1
Note: we try to do the EU stack overflow checking after the IFU stack overflow checking in here, even though there is only one register (ifuEldestPC) that really does the overflow checking
reg: ProcessorRegister ← LOOPHOLE[alphaZ];
IF processor.userMode THEN GO TO modeFault;
[] ← WillEUStackOverflow[];
This just checks for bogus oveflows
IF reg > euLast THEN cycles ← cycles + 4;
IFU registers take longer to store than EU registers
resReg ← reg;
SELECT reg FROM
ifuEldestPC => {
Setting this register causes a new eldest entry to be created before writing. This is roughly the same as a push, of course, so we use the same IFU stack overflow check.
entries: NAT ← processor.stackEntries+1;
eldest: NAT ← processor.eldest;
IF LizardHeart.WillPushOverflow[processor] THEN {
CauseTrap[IFUStackOverflowTrap];
GO TO done;
};
processor.stackEntries ← entries;
eldest ← (eldest + (LizardIFUStackSize - 1)) MOD LizardIFUStackSize;
processor.eldest ← eldest;
processor.ifuStack[eldest].pc ← resWord;
GO TO storeRegIFU;
};
ifuEldestL => {
IF processor.stackEntries = 0 THEN
SIGNAL OutsideEnvelope["Empty IFU stack in SIP ifuEldestL."];
processor.ifuStack[processor.eldest].status ← LOOPHOLE[resWord];
GO TO storeRegIFU;
};
ifuYoungestPC => {
IF processor.stackEntries = 0 THEN
SIGNAL OutsideEnvelope["Empty IFU stack in SIP ifuYoungestPC."];
processor.ifuStack[processor.youngest].pc ← resWord;
GO TO storeRegIFU;
};
ifuYoungestL => {
IF processor.stackEntries = 0 THEN
SIGNAL OutsideEnvelope["Empty IFU stack in SIP ifuYoungestL."];
processor.ifuStack[processor.youngest].status ← LOOPHOLE[resWord];
GO TO storeRegIFU;
};
ENDCASE;
GO TO storeReg;
};
dLIB => {
Effect: [S+1]𡤊lphaZ; S←S+1
resWord ← CardToWord[alphaZ];
GO TO storeReg;
};
dADDB, dSUBB => {
Effect: [S] ← [S] ± AlphaZ ± carry; trap on overflow, clear carry
[resWord, trapCode] ← DoALUOp[
processor,
topOfStack,
CardToWord[alphaZ],
IF inst = dADDB THEN SAdd ELSE SSub,
ALUCondOver];
GO TO aluDone;
};
dJ2 => GO TO noCheck;
Effect: noop
dJB => {
Effect: PC←PC+Alpha
alphaS: INT ← alphaZ;
IF alphaZ > 127 THEN alphaS ← alphaS - 256;
newPC ← AddDelta[alphaS, thisPC];
GO TO jump;
};
dRB => GO TO memFetchAlpha;
Effect: [S]←([S]+AlphaZ)^
dWB => {
Effect: ([S]+AlphaZ)^←[S-1]; S←S-2
resWord ← RegFetch[sM1];
GO TO memStoreAlpha;
};
dRSB => GO TO memFetchAlpha;
Effect: [S+1]←([S]+AlphaZ)^; S←S+1
dWSB => {
Effect: ([S-1]+AlphaZ)^←[S]; S←S-2
resAddr ← RegFetch[sM1];
GO TO memStoreAlpha;
};
dIODA, dIOD, dION => {
An incomplete simulation of the IOx instructions. IOFetch & IOStore effects are mostly ignored. However, the stack effect and the other goodies are correct as far as I can tell.
ioOp: IOOperand = [LOOPHOLE[alphaZ], LOOPHOLE[betaZ]];
pDataA: Word ← AddDelta[ByteToCard[ioOp.pDataA], (IF inst = dIODA THEN topOfStack ELSE CardToWord[0])];
WITH ioOp.pCmd SELECT FROM
otherCmd: PCmdFormat.other => {};
cacheCmd: PCmdFormat.cache => {
SELECT cacheCmd.direction FROM
read => {
IF inst = dIOD THEN resReg ← StackPlus[regS, stackEffect ← 1];
[resWord, trapCode] ← IOFetch[ioOp.pCmd, pDataA];
IF trapCode # NoFault THEN GO TO memFault;
IF inst = dION THEN GO TO done;
processor.euBusyReg ← resReg;
GO TO storeReg;
};
write => {
SELECT inst FROM
dIOD => stackEffect ← -1;
dIODA => {resWord ← RegFetch[sM1]; stackEffect ← -2};
ENDCASE;
trapCode ← IOStore[ioOp.pCmd, pDataA, resWord];
IF trapCode # NoFault THEN GO TO memFault;
GO TO done;
};
ENDCASE => ERROR;
};
ENDCASE => ERROR;
};
dPSB => {
Effect: ([S-1]+AlphaZ)^←[S]; S←S-1
resAddr ← RegFetch[sM1];
GO TO memStoreAlpha;
};
dLRI0 => {
Effect: [S+1]←([L+n]+AlphaZ)^; S←S+1
OPEN form: LOOPHOLE[formBytes2, LRBformat];
resAddr ← RegFetchInline[StackPlus[regL, form.reg]];
GO TO memFetchAlpha;
};
dSRI0 => {
Effect: ([L+n]+AlphaZ)^←[S]; S←S-1
OPEN form: LOOPHOLE[formBytes2, LRBformat];
resAddr ← RegFetchInline[StackPlus[regL, form.reg]];
GO TO memStoreAlpha;
};
dROR, dRAND, dRRX, dRBC, dRADD, dRSUB, dRLADD, dRLSUB, dRXOR, dRFU, dRVADD, dRVSUB, dRUADD, dRUSUB => {
OPEN form: LOOPHOLE[formBytes3, RRformat];
aux: BOOL = form.aux;
regA: ProcessorRegister = CalcReg[aux, form.aOpt, form.a];
wordA: Word = RegFetchInline[regA];
regB: ProcessorRegister = CalcReg[aux, form.bOpt, form.b];
wordB: Word = RegFetchInline[regB];
op: ALUOps;
cond: TrapIndex ← ALUCondFalse;
resReg ← CalcReg[aux, form.cOpt, form.c, TRUE];
{
This code checks for assignments to the constant or auxilliary registers. All constant and the first eight auxiliary registers are protected against being written while in user mode.
firstProtectedAux: ProcessorRegister = euAux;
lastProtectedAux: ProcessorRegister = VAL[ORD[firstProtectedAux]+7];
firstProtectedConst: ProcessorRegister = euConstant;
lastProtectedConst: ProcessorRegister = VAL[ORD[firstProtectedConst]+11];
firstReadonlyConst: ProcessorRegister = euConstant;
lastReadonlyConst: ProcessorRegister = VAL[ORD[firstProtectedConst]+3];
SELECT resReg FROM
IN [firstProtectedAux..lastProtectedAux] =>
IF processor.userMode THEN GO TO modeFault;
IN [firstProtectedConst..lastProtectedConst] => {
IF processor.userMode THEN GO TO modeFault;
IF resReg IN [firstReadonlyConst..lastReadonlyConst]
AND checkReadonlyConst THEN {
IF checkReadonlyConst THEN
SIGNAL OutsideEnvelope["Storing into readonly register not permited!"];
resReg ← euJunk;
Don't affect the specified register, it's readonly
}
};
ENDCASE;
};
SELECT inst FROM
dROR => {
Effect: Rc ← Ra OR Rb
op ← Or;
};
dRAND => {
Effect: Rc ← Ra AND Rb
op ← And;
};
dRRX => {
Effect: [Rc]←([Ra]+[Rb])^
resAddr ← VanillaAdd[wordA, wordB];
GO TO memFetch;
};
dRBC => {
Effect: Rc ← Ra; trap if Ra < 0 OR Ra-Rb >= 0
op ← BndChk;
cond ← ALUCondBC;
};
dRADD => {
Effect: Rc ← Ra+Rb+carry; carry𡤀 trap on overflow
op ← SAdd;
cond ← ALUCondOver;
};
dRSUB => {
Effect: Rc ← Ra-Rb-carry; carry𡤀 trap on overflow
op ← SSub;
cond ← ALUCondOver;
};
dRLADD => {
Effect: Rc ← Ra+Rb; carry𡤀 trap on overflow or Lisp NaN
op ← LAdd;
cond ← ALUCondIL;
};
dRLSUB => {
Effect: Rc ← Ra-Rb; carry𡤀 trap on overflow or Lisp NaN
op ← LSub;
cond ← ALUCondIL;
};
dRXOR => {
Effect: Rc ← Ra XOR Rb
op ← Xor;
};
dRFU => {
Effect: [Rc]𡤏ieldUnit[[Ra],[Rb],MDF]
fd: FieldDescriptor = CardToFieldDescriptor[
HalfToCard[WordToHalves[RegFetch[euField]][1]]
];
resWord ← FieldUnit[wordA, wordB, fd];
GO TO storeReg;
};
dRVADD => {
Effect: Rc ← Ra+Rb
resWord ← VanillaAdd[wordA, wordB];
GO TO storeReg;
};
dRVSUB => {
Effect: Rc ← Ra-Rb
resWord ← VanillaSub[wordA, wordB];
GO TO storeReg;
};
dRUADD => {
Effect: Rc ← Ra+Rb+carry; set carry
op ← UAdd;
};
dRUSUB => {
Effect: Rc ← Ra-Rb-carry; set carry
op ← USub;
};
ENDCASE => ERROR;
[resWord, trapCode] ← DoALUOp[processor, wordA, wordB, op, cond];
GO TO aluDone;
};
dLGF => {
Effect: [S+1]←([GB]+AlphaBetaZ)^; S←S+1
resAddr ← AddDelta[alphaBetaZ, RegFetch[euAux]];
GO TO memFetch;
};
dLIDB => {
Effect: [S+1]𡤊lphaBetaZ; S←S+1
resWord ← CardToWord[alphaBetaZ];
GO TO storeReg;
};
dADDDB, dSUBDB => {
Effect: [S] ← [S] ± AlphaBetaZ; trap on overflow
[resWord, trapCode] ← DoALUOp[
processor,
topOfStack,
CardToWord[alphaBetaZ],
IF inst = dADDDB THEN SAdd ELSE SSub,
ALUCondOver];
GO TO aluDone;
};
dJ3 => GO TO noCheck;
Effect: noop
dJDB => {
Effect (dJDB): PC←PC+AlphaBetaS
newPC ← AddDelta[LOOPHOLE[alphaBetaZ, INTEGER], thisPC];
GO TO jump;
};
dLFC => {
Effect (dLFC): call proc at PC+AlphaBetaS
newPC ← AddDelta[LOOPHOLE[alphaBetaZ, INTEGER], thisPC];
GO TO call;
};
dRAI, dRRI, dWAI, dWRI => {
OPEN form: LOOPHOLE[formBytes3, LRRBformat];
baseReg: ProcessorRegister ← SELECT inst FROM
dRAI, dWAI => RegPlus[euAux, form.reg2],
ENDCASE => StackPlus[regL, form.reg2];
resReg ← StackPlus[regL, form.reg1];
resAddr ← RegFetchInline[baseReg];
SELECT inst FROM
dRAI, dRRI => {
Effect (dRAI): [L+BetaL]←(AuxRegs[BetaR]+AlphaZ)^
Effect (dRRI): [L+BetaL]←([L+BetaR]+AlphaZ)^
GO TO memFetchAlpha;
};
dWAI, dWRI => {
Effect (dWAI): (AuxRegs[BetaR]+AlphaZ)^←[L+BetaL]
Effect (dWRI): ([L+BetaR]+AlphaZ)^←[L+BetaL]
resWord ← RegFetchInline[resReg];
GO TO memStoreAlpha;
};
ENDCASE => ERROR;
};
dRJEB, dRJEBJ, dRJGB, dRJGBJ, dRJGEB, dRJGEBJ, dRJLB, dRJLBJ, dRJLEB, dRJLEBJ, dRJNEB, dRJNEBJ => {
Effect: IF Rs cond Rb THEN PCPC+BetaS
OPEN form: LOOPHOLE[formBytes3, RJBformat];
comparison: Basics.Comparison;
regA: ProcessorRegister ← regS;
SELECT form.aOp FROM
c0 => regA ← euConstant;
c1 => regA ← SUCC[euConstant];
top => regA ← regS;
popTop => {regA ← regS; stackEffect ← stackEffect - 1};
ENDCASE;
resWord ← RegFetchInline[regA];
comparison ← Basics.CompareInt[
WordToInt[resWord],
WordToInt[RegFetchInline[CalcReg[form.aux, form.opt, form.reg]]]];
IF WillEUStackOverflow[] THEN GO TO euOverflow;
SELECT inst FROM
dRJEB, dRJEBJ => IF comparison = equal THEN GO TO condJump;
dRJGB, dRJGBJ => IF comparison = greater THEN GO TO condJump;
dRJGEB, dRJGEBJ => IF comparison # less THEN GO TO condJump;
dRJLB, dRJLBJ => IF comparison = less THEN GO TO condJump;
dRJLEB, dRJLEBJ => IF comparison # greater THEN GO TO condJump;
dRJNEB, dRJNEBJ => IF comparison # equal THEN GO TO condJump;
ENDCASE => ERROR;
At this point we should NOT branch.
GO TO condFall;
};
dJEBB, dJEBBJ =>
Effect: AlphaZ = [S] => PC←PC+BetaS; S←S-1
IF topOfStack = CardToWord[alphaZ] THEN GO TO condJump ELSE GO TO condFall;
dJNEBB, dJNEBBJ =>
Effect: AlphaZ # [S] => PC←PC+BetaS; S←S-1
IF topOfStack # CardToWord[alphaZ] THEN GO TO condJump ELSE GO TO condFall;
dSHL => {
Effect: [S]𡤏ieldUnit[[S],0,AlphaBeta]
topOfStack ← ZerosWord;
GO TO storeField;
};
dSHR => GO TO storeField;
Effect: [S]𡤏ieldUnit[[S],[S],AlphaBeta]
dSHDL => {
Effect: [S-1]𡤏ieldUnit[[S-1],[S],AlphaBeta]; S←S-1
resWord ← RegFetch[sM1];
GO TO storeField;
};
dSHDR => {
Effect: [S-1]𡤏ieldUnit[[S],[S-1],AlphaBeta]; S←S-1
resWord ← topOfStack;
topOfStack ← RegFetch[sM1];
GO TO storeField;
};
dFSDB => {
Effect: euField𡤊lphaBeta+[S]; S←S-1
resWord ← VanillaAdd[CardToWord[alphaBetaZ], topOfStack];
resReg ← euField;
GO TO storeReg;
};
x311b, x313b, x337b, x340b, x344b, x350b, x354b => {
SIGNAL OutsideEnvelope[IO.PutFR["Opcode %g has undefined effects", IO.rope[HandCodingUtil.GetInstArray[][inst]]]];
GO TO xop;
};
ENDCASE => NULL;
GO TO xop;
EXITS
done => {
There is really nothing to do here, we're done.
IF WillEUStackOverflow[] THEN CauseTrap[EUStackOverflowTrap];
};
noCheck => {
There is really nothing to do here, not even an overflow check.
};
xop => {
Trap. If length # 1 then push AlphaBetaGammaDelta before calling to the trap address. Further traps are permitted - in particular, IFU stack and EU stack overflow even within this instruction.
stackEffect ← 0;
control ← doCall;
newPC ← CardToWord[DragOpsCrossUtils.XopToBytePC[inst]];
SELECT TRUE FROM
LizardHeart.WillPushOverflow[processor: processor] =>
CauseTrap[IFUStackOverflowTrap];
bytes > 1 => {
must push the following bytes (can have high-order junk)
cycles ← cycles+1;
stackEffect ← 1;
resWord ← rest;
IF WillEUStackOverflow[]
THEN CauseTrap[EUStackOverflowTrap]
ELSE RegStore[StackPlus[regS, stackEffect], resWord];
};
ENDCASE;
};
condJump => {
A conditional jump branched
betaS: INT ← betaZ;
IF betaZ > 127 THEN betaS ← betaS - 256;
newPC ← AddDelta[betaS, thisPC];
IF flags.predictJump
THEN processor.stats.jumpGood ← processor.stats.jumpGood + 1
ELSE {
control ← doSwitch;
processor.stats.fallThruBad ← processor.stats.fallThruBad + 1;
cycles ← cycles+3;
};
IF WillEUStackOverflow[] THEN CauseTrap[EUStackOverflowTrap];
};
condFall => {
A conditional jump did NOT branch
IF flags.predictJump
THEN {
control ← doSwitch;
processor.stats.jumpBad ← processor.stats.jumpBad + 1;
cycles ← cycles+3;
}
ELSE processor.stats.fallThruGood ← processor.stats.fallThruGood + 1;
IF WillEUStackOverflow[] THEN CauseTrap[EUStackOverflowTrap];
};
jump => {
There is no need to check for EU overflow here.
processor.stats.jumps ← processor.stats.jumps + 1;
control ← doSwitch;
};
call => {
SELECT TRUE FROM
LizardHeart.WillPushOverflow[processor] =>
CauseTrap[IFUStackOverflowTrap];
WillEUStackOverflow[] =>
CauseTrap[EUStackOverflowTrap];
ENDCASE => {
processor.stats.calls ← processor.stats.calls + 1;
control ← doCall;
};
};
return => {
To perform a return. The EU stack checking has already been done.
SELECT processor.stackEntries FROM
= 0 =>
SIGNAL OutsideEnvelope["IFU control stack is empty during return!"];
> IFUStackSize =>
SIGNAL OutsideEnvelope["IFU control stack is too full during return!"];
> IFUOverflow =>
IF processor.trapsEnabled THEN
SIGNAL OutsideEnvelope["IFU control stack is too full during return!"];
ENDCASE;
control ← doReturn;
rtnPC ← processor.ifuStack[processor.youngest].pc;
For debugging purposes
};
modeFault => {
This exit is taken if we attempt to execute a protected instruction while in user mode.
CauseTrap[ModeFault];
};
euOverflow => {
This exit is taken if we find that the resulting value of S is bogus.
CauseTrap[EUStackOverflowTrap];
};
pushReg => {
Note: no need to check for stack overflow since we did that earlier
IF WillEUStackOverflow[]
THEN CauseTrap[EUStackOverflowTrap]
ELSE FastRegStore[StackPlus[regS, 1], RegFetchInline[resReg]];
};
storeField => {
resWord holds the "left" word, topOfStack holds the "right" word, regardless of the actual top of stack. This exit is used by SHL, SHR, SHDL, SHDR
IF WillEUStackOverflow[]
THEN CauseTrap[EUStackOverflowTrap]
ELSE FastRegStore[resReg, FieldUnit[resWord, topOfStack, CardToFieldDescriptor[alphaBetaZ]]];
};
storeRegIFU => {
resReg can be an IFU register, so don't use FastRegStore here.
IF WillEUStackOverflow[]
THEN CauseTrap[EUStackOverflowTrap]
ELSE RegStore[resReg, resWord];
};
storeReg => {
resReg must not be a special IFU register, so we use FastRegStore here.
IF WillEUStackOverflow[]
THEN CauseTrap[EUStackOverflowTrap]
ELSE FastRegStore[resReg, resWord];
};
aluDone => {
SELECT TRUE FROM
trapCode # NoFault => CauseTrap[trapCode];
WillEUStackOverflow[] => CauseTrap[EUStackOverflowTrap];
ENDCASE => FastRegStore[resReg, resWord];
};
memFault => {
CauseTrap[trapCode];
};
memFetch => {
IF WillEUStackOverflow[]
THEN CauseTrap[EUStackOverflowTrap]
ELSE {
[resWord, trapCode] ← MemFetch[resAddr];
SELECT TRUE FROM
trapCode # NoFault => CauseTrap[trapCode];
ENDCASE => {FastRegStore[resReg, resWord]; processor.euBusyReg ← resReg};
};
};
memFetchAlpha => {
IF WillEUStackOverflow[]
THEN CauseTrap[EUStackOverflowTrap]
ELSE {
[resWord, trapCode] ← MemFetch[AddDelta[alphaZ, resAddr]];
SELECT TRUE FROM
trapCode # NoFault => CauseTrap[trapCode];
ENDCASE => {FastRegStore[resReg, resWord]; processor.euBusyReg ← resReg};
};
};
memStore => {
SELECT TRUE FROM
WillEUStackOverflow[] =>
CauseTrap[EUStackOverflowTrap];
ENDCASE => {
trapCode ← MemStore[resAddr, resWord];
IF trapCode # NoFault THEN CauseTrap[trapCode];
};
};
memStoreAlpha => {
resAddr ← AddDelta[alphaZ, resAddr];
SELECT TRUE FROM
WillEUStackOverflow[] =>
CauseTrap[EUStackOverflowTrap];
ENDCASE => {
trapCode ← MemStore[resAddr, resWord];
IF trapCode # NoFault THEN CauseTrap[trapCode];
};
};
};
IF stackEffect # 0 THEN
processor.regs[ifuS] ← RegToWord[StackPlus[regS, stackEffect]];
processor.stats.cycles ← processor.stats.cycles + cycles;
IF control # doAbort THEN processor.stats.instructions ← processor.stats.instructions+1;
IF logger # NIL THEN {
instDone: InstDoneProc ← logger.instDone;
IF instDone # NIL THEN instDone[logger.data, processor, newPC, rtnPC, control, cycles];
};
};
flagsArray: REF ARRAY Inst OF InstFlags ← NIL;
InstFlags: TYPE = RECORD [
usesTop: BOOLFALSE,
predictJump: BOOLFALSE,
mod16: BOOLFALSE,
callMesa: BOOLFALSE,
cycles: [0..15] ← 1,
bytes: [0..7] ← 1,
stackEffect: [-2..1] ← 0];
InitFlagsArray: PROC = {
defaultFlags: InstFlags ← [];
flagsArray ← NEW[ARRAY Inst OF InstFlags];
FOR inst: Inst IN Inst DO
bytes: NATORD[inst] / 64;
flags: InstFlags ← defaultFlags;
IF bytes = 0 THEN IF ORD[inst] < 40B THEN bytes ← 1 ELSE bytes ← 5;
flags.bytes ← bytes;
SELECT inst FROM
dWB, dWSB => flags.stackEffect ← -2;
dDIS, dEXDIS, dSFC, dJSD, dJSR, dPSB, dSIP, dSHDL, dSHDR, dFSDB, dJEBB, dJEBBJ, dJNEBB, dJNEBBJ, IN[dOR..dLSUB], IN [dSR0..dSR15], IN [dSRI0..dSRI15] =>
flags.stackEffect ← -1;
dLIQB, dDUP, dCST, dLIP, dLIB, dRSB, dLGF, dLIDB,
IN [dLC0..dLC11], IN [dLR0..dLR15], IN [dLRI0..dLRI15],
x060b, x063b, x215b, x223b, IN [x234b..x236b],
IN [x364b..x367b], IN [x374b..x377b] =>
flags.stackEffect ← 1;
ENDCASE;
SELECT inst FROM
dCST => flags.cycles ← 8;
dSFC, dSFCI, dJSD, dJSR => flags.cycles ← 4;
dRET, dRETN, dKFC => flags.cycles ← 2;
ENDCASE;
SELECT inst FROM
IN [dSR0..dSR15], IN [dSRI0..dSRI15], IN[dOR..dLSUB], dDUP, dEXDIS, dSFC, dSFCI, dJSD, dJSR, dCST, dSIP, dADDQB, dSUBQB, dADDB, dSUBB, dRB, dWB, dRSB, dWSB, dPSB, dADDDB, dSUBDB, dJEBB, dJEBBJ, dJNEBB, dJNEBBJ, dSHL, dSHR, dSHDL, dSHDR, dFSDB, dIODA, dIOD, dION =>
flags.usesTop ← TRUE;
ENDCASE;
SELECT inst FROM
dRJEBJ, dRJGBJ, dRJGEBJ, dRJLBJ, dRJLEBJ, dRJNEBJ, dJEBBJ, dJNEBBJ =>
flags.predictJump ← TRUE;
ENDCASE;
SELECT inst FROM
IN [dSR0..dSR15], IN [dSRI0..dSRI15], IN [dLR0..dLR15], IN [dLRI0..dLRI15] =>
flags.mod16 ← TRUE;
ENDCASE;
flagsArray[inst] ← flags;
ENDLOOP;
};
InitFlagsArray[];
END.