<> <> <> <> DIRECTORY Basics USING [BITAND], DragOpsCross USING [Byte, bytesPerWord, EUStackSize, FieldDescriptor, FourBytes, IFUOverflow, IFUStackSize, Inst, OnesWord, ProcessorRegister, StackedStatusWord, TrapIndex, Word, ZerosWord], DragOpsCrossUtils USING [BytePCToWordAddress, ByteToCard, BytesToHalf, BytesToWord, CardToWord, DoubleWordShiftLeft, DragAnd, DragNot, DragOr, DragXor, HalfToCard, IntToWord, SingleWordShiftLeft, SingleWordShiftRight, TrapIndexToBytePC, VanillaAdd, VanillaSub, WordAddressToBytePC, WordToBytes, WordToCard, WordToInt], LizardCache USING [ResetCache], LizardHeart USING [ALUHelper, ALUOps, CacheBase, ChangeLogger, Control, InstBuffer, InstBufferRep, LizardIFUStackIndex, LizardIFUStackSize, NoFault, Processor, ProcessorRep, RegToWord, TrapPC, WordToReg], LizardLiver USING [Execute], PrincOpsUtils USING [LongCopy], Rope USING [ROPE]; LizardHeartImpl: CEDAR PROGRAM IMPORTS Basics, DragOpsCrossUtils, LizardCache, LizardHeart, LizardLiver, PrincOpsUtils EXPORTS LizardHeart = BEGIN OPEN DragOpsCross, DragOpsCrossUtils, LizardHeart; CurrentIFUVersion: [0..255] _ 1; <> InstBufferIndex: TYPE = [0..64); <> WordsInBuffer: NAT _ 8; <<# of Dragon words in the instruction buffer (power of two)>> MaxMask: CARDINAL _ WordsInBuffer*bytesPerWord - 1; <> MaskInstBufferIndex: PROC [index: CARDINAL] RETURNS [InstBufferIndex] = INLINE { RETURN [LOOPHOLE[Basics.BITAND[index, MaxMask]]]; }; NewProcessor: PUBLIC PROC [ifuCache, euCache: CacheBase, logger: ChangeLogger] RETURNS [p: Processor] = { p _ NEW[ProcessorRep]; p.ifuCache _ ifuCache; p.euCache _ euCache; p.logger _ logger; InitProcessor[p]; }; InitProcessor: PROC [p: Processor] = { <> p.eldest _ 1; p.youngest _ 0; p.stackEntries _ 0; p.regs[ifuL] _ CardToWord[1]; p.regs[ifuS] _ ZerosWord; p.userMode _ FALSE; p.trapsEnabled _ FALSE; p.resetRequested _ FALSE; p.rescheduleRequested _ FALSE; p.euCarry _ FALSE; p.regs[ifuPC] _ TrapPC[ResetTrap]; p.regs[euConstant] _ IntToWord[0]; p.regs[euConstant.SUCC] _ IntToWord[1]; p.regs[euConstant.SUCC.SUCC] _ IntToWord[2]; p.regs[euConstant.SUCC.SUCC.SUCC] _ IntToWord[3]; p.regsGood _ ALL[FALSE]; p.regsGood[euConstant] _ TRUE; -- constant register 0 = 0 (it's a ROM) p.regsGood[euConstant.SUCC] _ TRUE; -- constant register 1 = 1 (it's a ROM) p.regsGood[euConstant.SUCC.SUCC] _ TRUE; -- constant register 2 = 2 (it's a ROM) p.regsGood[euConstant.SUCC.SUCC.SUCC] _ TRUE; -- constant register 3 = 3 (it's a ROM) p.instBuffer _ NEW[InstBufferRep[WordsInBuffer]]; p.version _ CurrentIFUVersion; p.stats _ []; LizardCache.ResetCache[p.ifuCache]; LizardCache.ResetCache[p.euCache]; }; FlushInstBuffer: PUBLIC PROC [processor: Processor] = { <> instBuffer: InstBuffer = processor.instBuffer; delta: INT = WordToInt[instBuffer.nextPC] - WordToInt[instBuffer.basePC]; IF delta > 0 THEN { validBytes: INT = instBuffer.validWords*bytesPerWord; IF validBytes > delta THEN instBuffer.bytesDiscarded _ instBuffer.bytesDiscarded + validBytes-delta; }; instBuffer.validWords _ 0; instBuffer.forcedEmpty _ instBuffer.forcedEmpty + 1; }; InstructionExecute: PUBLIC PROC [processor: Processor] = { thisPC: Word _ processor.regs[ifuPC]; newPC: Word _ thisPC; rtnPC: Word; control: Control _ nextInst; inst: Inst; rest: Word; regL: Word _ processor.regs[ifuL]; cycles: CARDINAL _ 0; rCycles: CARDINAL _ 0; initCycles: INT _ processor.stats.cycles; youngest: CARDINAL _ processor.youngest; nBytes: CARDINAL _ 0; word: Word; wordAddr: Word; rbi: [0..bytesPerWord); tx: TrapIndex _ NoFault; valid: InstBufferIndex _ 0; -- valid bytes in inst buffer AFTER newPC p: Processor = processor; -- for a short name instBuffer: InstBuffer _ p.instBuffer; instBufferPtr: LONG POINTER; max: CARDINAL = instBuffer.max * bytesPerWord; -- max bytes in buffer ForceBufferEmpty: PROC = INLINE { <> instBuffer.validWords _ 0; instBuffer.forcedEmpty _ instBuffer.forcedEmpty + 1; IF valid # 0 THEN { instBuffer.bytesDiscarded _ instBuffer.bytesDiscarded + valid; valid _ 0; }; }; IsEUStackOverflow: PROC [regS: ProcessorRegister] RETURNS [BOOL] = INLINE { RETURN [ LOOPHOLE[ORD[regS] - ORD[WordToReg[processor.regs[ifuSLimit]]], CARDINAL] MOD EUStackSize IN [0..16) ]; }; CauseTrap: PROC [code: TrapIndex] = { <> <> rtnPC _ thisPC; IF WillPushOverflow[p] THEN <> code _ IFUStackOverflowTrap; newPC _ TrapPC[code]; control _ doAbort; cycles _ cycles + 4; -- a rough guess tx _ code; IF code = IFUStackOverflowTrap THEN p.stats.stackOver _ p.stats.stackOver + 1; }; FlushInstWord: PROC = INLINE { <> vw: CARDINAL _ instBuffer.validWords - 1; instBuffer.basePC _ IntToWord[WordToInt[instBuffer.basePC] + bytesPerWord]; instBuffer.validWords _ instBuffer.validWords - 1; IF vw # 0 THEN TRUSTED { PrincOpsUtils.LongCopy[from: @instBuffer[1], nwords: instBuffer.validWords*SIZE[Word], to: @instBuffer[0]]; }; instBuffer.validWords _ vw; }; IF p.resetRequested THEN { <> thisPC _ newPC _ TrapPC[ResetTrap]; InitProcessor[p]; ForceBufferEmpty[]; p.instBuffer.forcedEmpty _ instBuffer.forcedEmpty; instBuffer _ p.instBuffer; initCycles _ cycles _ 0; youngest _ p.youngest; }; TRUSTED { instBufferPtr _ @instBuffer[0]; }; IF newPC = instBuffer.nextPC THEN { <> used: INT _ WordToInt[newPC] - WordToInt[instBuffer.basePC]; valid _ instBuffer.validWords*bytesPerWord; IF used > 0 AND used < valid THEN valid _ valid - used ELSE valid _ 0; }; { <> start: InstBufferIndex _ 0; -- byte index in buffer of newPC <> < RescheduleTrap > IFUPageFaultTrap>> IF p.trapsEnabled THEN SELECT TRUE FROM IsEUStackOverflow[WordToReg[p.regs[ifuS]]] => { <> tx _ EUStackOverflowTrap; GO TO trapEarly; }; p.rescheduleRequested => { <> p.rescheduleRequested _ FALSE; tx _ RescheduleTrap; GO TO trapEarly; }; ENDCASE; IF valid = 0 THEN { <> [wordAddr, rbi] _ BytePCToWordAddress[[newPC]]; [word, tx, rCycles] _ p.ifuCache.fetch[p.ifuCache, wordAddr, initCycles+cycles, p.userMode]; cycles _ cycles + 1; <> IF tx # NoFault THEN {tx _ IFUPageFaultTrap; GO TO trapEarly}; <> IF rCycles # 0 THEN { <> cycles _ cycles + rCycles; instBuffer.busyUntil _ p.ifuCache.sharedBase.busyUntil; }; valid _ bytesPerWord - rbi; instBuffer.validWords _ 1; instBuffer.basePC _ WordAddressToBytePC[wordAddr]; instBuffer[0] _ word; }; <> start _ WordToInt[newPC] - WordToInt[instBuffer.basePC]; IF start >= max THEN ERROR; word _ instBuffer[start/bytesPerWord]; inst _ LOOPHOLE[WordToBytes[word][start MOD bytesPerWord]]; nBytes _ LOOPHOLE[inst, CARDINAL] / 64; IF nBytes = 0 THEN nBytes _ IF LOOPHOLE[inst, CARDINAL] < 40B THEN 1 ELSE 5; IF valid < nBytes THEN { <> IF instBuffer.validWords = instBuffer.max THEN FlushInstWord[]; [wordAddr, rbi] _ BytePCToWordAddress[[IntToWord[WordToInt[newPC] + valid]]]; [word, tx, rCycles] _ p.ifuCache.fetch[p.ifuCache, wordAddr, initCycles+cycles, p.userMode]; cycles _ cycles + 1; <> IF rCycles # 0 THEN { <> cycles _ cycles + rCycles; rCycles _ 0; instBuffer.busyUntil _ p.ifuCache.sharedBase.busyUntil; }; IF tx # NoFault THEN {tx _ IFUPageFaultTrap; GO TO trapEarly}; <> <> start _ WordToInt[newPC] - WordToInt[instBuffer.basePC]; instBuffer[start/bytesPerWord + 1] _ word; instBuffer.validWords _ instBuffer.validWords + 1; valid _ valid + bytesPerWord; }; <> IF nBytes > 1 THEN TRUSTED { <> instBufferPtr: LONG POINTER TO PACKED ARRAY InstBufferIndex OF Byte _ LOOPHOLE[@instBuffer[0]]; SELECT nBytes FROM 5 => { rest _ BytesToWord[[ instBufferPtr[MaskInstBufferIndex[start+1]], instBufferPtr[MaskInstBufferIndex[start+2]], instBufferPtr[MaskInstBufferIndex[start+3]], instBufferPtr[MaskInstBufferIndex[start+4]] ]]; }; 3 => { rest _ CardToWord[HalfToCard[BytesToHalf[[ instBufferPtr[MaskInstBufferIndex[start+1]], instBufferPtr[MaskInstBufferIndex[start+2]] ]]]]; }; 2 => { rest _ CardToWord[ByteToCard[ instBufferPtr[MaskInstBufferIndex[start+1]] ]]; }; ENDCASE => ERROR; }; newPC _ IntToWord[WordToInt[newPC] + nBytes]; valid _ valid - nBytes; <> IF instBuffer.busyUntil <= initCycles+cycles THEN { <> IF valid <= max-bytesPerWord THEN { <> fetchPC: Word _ IntToWord[WordToInt[newPC] + valid]; IF instBuffer.validWords = instBuffer.max THEN FlushInstWord[]; <> [wordAddr, rbi] _ BytePCToWordAddress[[fetchPC]]; IF rbi # 0 THEN ERROR; -- rats! we blew it! [word, tx, rCycles] _ p.ifuCache.fetch[p.ifuCache, wordAddr, initCycles+cycles, p.userMode]; p.stats.lookaheadProbes _ p.stats.lookaheadProbes + 1; IF rCycles # 0 THEN { <> instBuffer.busyUntil _ p.ifuCache.sharedBase.busyUntil; p.stats.lookaheadRejects _ p.stats.lookaheadRejects + rCycles; }; IF tx = NoFault THEN { <> instBuffer[instBuffer.validWords] _ word; instBuffer.validWords _ instBuffer.validWords + 1; }; tx _ NoFault; <> }; }; instBuffer.nextPC _ newPC; <> p.stats.cycles _ p.stats.cycles+cycles; p.stats.instBufferCycles _ p.stats.instBufferCycles+cycles; cycles _ 0; <> [newPC, rtnPC, control] _ LizardLiver.Execute[p, thisPC, inst, rest]; EXITS trapEarly => CauseTrap[tx]; }; thisPC _ newPC; SELECT control FROM nextInst => {}; <> doSwitch => <> ForceBufferEmpty[]; doCall, doAbort, doReturn => { lastCallDelta: INT _ p.stats.instructions - p.lastCallRtn; ForceBufferEmpty[]; IF control = doReturn THEN { currentSize: NAT _ p.stackEntries; stackedStatus: DragOpsCross.StackedStatusWord _ p.ifuStack[youngest].status; SELECT currentSize FROM = 0 => { SIGNAL OutsideEnvelope["IFU control stack empty on return!"]; thisPC _ TrapPC[StackUnderflowTrap]; GO TO badGnus; }; > IFUStackSize => { SIGNAL OutsideEnvelope["IFU control stack is too full during return!"]; GO TO badGnus; }; > IFUOverflow => IF stackedStatus.trapsEnabled THEN { SIGNAL OutsideEnvelope["IFU control stack is too full during return!"]; GO TO badGnus; }; ENDCASE; p.trapsEnabled _ stackedStatus.trapsEnabled; p.userMode _ stackedStatus.userMode; p.stackEntries _ currentSize - 1; thisPC _ p.ifuStack[youngest].pc; p.regs[ifuL] _ regL _ RegToWord[VAL[stackedStatus.lBase]]; p.youngest _ youngest _ (youngest + (LizardIFUStackSize - 1)) MOD LizardIFUStackSize; EXITS badGnus => {}; } ELSE { <> newSize: NAT _ p.stackEntries+1; stackedStatus: DragOpsCross.StackedStatusWord _ [ version: CurrentIFUVersion, userMode: p.userMode, trapsEnabled: p.trapsEnabled, lBase: ORD[WordToReg[regL]] ]; IF newSize > IFUStackSize THEN SIGNAL OutsideEnvelope["IFU control stack too full!"] ELSE { p.stackEntries _ newSize; p.youngest _ youngest _ (youngest + 1) MOD LizardIFUStackSize; }; p.ifuStack[youngest] _ [rtnPC, stackedStatus]; IF control = doAbort THEN <> p.userMode _ p.trapsEnabled _ FALSE; IF tx # NoFault THEN <> IF p.logger # NIL AND p.logger.instDone # NIL THEN p.logger.instDone[p.logger.data, p, newPC, rtnPC, doAbort, cycles]; }; SELECT lastCallDelta FROM >= 3, < 0 => {}; ENDCASE => { <> cycles _ cycles + (3-lastCallDelta); p.stats.returnInterlockCycles _ p.stats.returnInterlockCycles + lastCallDelta; }; p.lastCallRtn _ p.stats.instructions; }; ENDCASE => ERROR; p.regs[ifuPC] _ thisPC; p.stats.cycles _ p.stats.cycles+cycles; p.stats.instBytes _ p.stats.instBytes+nBytes; }; <> DoALUOp: PUBLIC PROC [processor: Processor, wordA,wordB: Word, op: ALUOps, trap: TrapIndex] RETURNS [res: Word, resCode: TrapIndex _ NoFault] = { carryOut, overflow, boundsCheck, illegalLisp: BOOL _ FALSE; carryIn: BOOL _ processor.euCarry; <> SELECT op FROM SAdd => { [res, ] _ WordCarryAdd[wordA, wordB, carryIn]; SELECT ALUHelper[wordA[0], wordB[0], res[0]] FROM a0b0c1, a1b1c0 => {overflow _ TRUE}; ENDCASE; }; UAdd => [res, carryOut] _ WordCarryAdd[wordA, wordB, carryIn]; SSub => { [res, ] _ WordCarryAdd[wordA, DragNot[wordB], NOT carryIn]; SELECT ALUHelper[wordA[0], wordB[0], res[0]] FROM a0b1c1, a1b0c0 => overflow _ TRUE; ENDCASE; }; USub => { [res, carryOut] _ WordCarryAdd[wordA, DragNot[wordB], NOT carryIn]; carryOut _ NOT carryOut; }; LAdd, LSub => { res _ IF op = LAdd THEN VanillaAdd[wordA, wordB] ELSE VanillaSub[wordA, wordB]; IF res[0] # res[1] OR res[1] # res[2] OR wordA[0] # wordA[1] OR wordA[1] # wordA[2] OR wordB[0] # wordB[1] OR wordB[1] # wordB[2] THEN illegalLisp _ TRUE }; VAdd => {res _ VanillaAdd[wordA, wordB]; carryOut _ carryIn}; VSub => {res _ VanillaSub[wordA, wordB]; carryOut _ carryIn}; And => {res _ DragAnd[wordA, wordB]; RETURN}; Or => {res _ DragOr[wordA, wordB]; RETURN}; Xor => {res _ DragXor[wordA, wordB]; RETURN}; BndChk => { boundsCheck _ WordCarryAdd[wordA, DragNot[wordB], TRUE].carryOut; res _ wordA; carryOut _ carryIn; }; ENDCASE => ERROR; <> resCode _ (IF (SELECT trap FROM ALUCondFalse => FALSE, ALUCondEZ => res = ZerosWord, ALUCondLZ => WordToInt[res] < 0, ALUCondLE => WordToInt[res] <= 0, ModeFault => TRUE, ALUCondNE => res = ZerosWord, ALUCondGE => WordToInt[res] >= 0, ALUCondGZ => WordToInt[res] > 0, ALUCondOver => overflow, ALUCondBC => boundsCheck, ALUCondIL => illegalLisp, ALUCondDO => FALSE, -- not yet implemented ALUCondNotOver => NOT overflow, ALUCondNB => NOT boundsCheck, ALUCondNI => NOT illegalLisp, ENDCASE => ERROR) THEN trap ELSE NoFault); IF resCode = NoFault THEN processor.euCarry _ carryOut; <> }; WordCarryAdd: PROC [wordA, wordB: Word, carryIn: BOOL] RETURNS [wordC: Word, carryOut: BOOL _ FALSE] = { cardA: CARD _ WordToCard[wordA]; cardB: CARD _ WordToCard[wordB]; cardC: CARD _ cardA+cardB; SELECT cardC FROM < cardA, < cardB => carryOut _ TRUE; ENDCASE; IF carryIn THEN {cardC _ cardC+1; IF cardC = 0 THEN carryOut _ TRUE}; wordC _ CardToWord[cardC]; }; FieldUnit: PUBLIC PROC [Left,Right: Word, fd: FieldDescriptor] RETURNS [out: Word] = { shifter, mask: Word; IF fd.shift > 32 THEN SIGNAL OutsideEnvelope["Field unit shift > 32"]; shifter _ DoubleWordShiftLeft[Left, Right, fd.shift]; <> IF fd.mask > 32 THEN { mask _ OnesWord; SIGNAL OutsideEnvelope["Field unit mask > 32"]; } ELSE mask _ SingleWordShiftRight[OnesWord, 32-fd.mask]; <> IF fd.insert THEN { mask _ DragAnd[mask, SingleWordShiftLeft[OnesWord, fd.shift]]; < clear rightmost fd.shift bits of the mask >> out _ DragOr[DragAnd[mask, shifter], DragAnd[DragNot[mask], Right]]; <<1 bits in the mask select the shifter output>> <<0 bits in the mask select bits from Right>> } ELSE out _ DragAnd[mask, shifter]; <<1 bits in the mask select the shifter output>> }; WillPushOverflow: PUBLIC PROC [processor: Processor] RETURNS [BOOL] = { IF processor.stackEntries < IFUOverflow-1 THEN RETURN [FALSE] ELSE { SELECT processor.stackEntries FROM = IFUOverflow-1 => {}; < IFUStackSize => IF processor.trapsEnabled THEN SIGNAL OutsideEnvelope["IFU control stack too full with traps enabled"]; ENDCASE => SIGNAL OutsideEnvelope["IFU control stack has more than IFUStackSize entries"]; RETURN [processor.trapsEnabled]; }; }; OutsideEnvelope: PUBLIC SIGNAL [explanation: Rope.ROPE] = CODE; END.