<> <> <> <> DIRECTORY Basics USING [BITAND, CARD], DragOpsCross USING [Byte, bytesPerWord, EUStackSize, FieldDescriptor, FourBytes, IFUOverflow, IFUStackSize, IFUStatusRec, Inst, KernalLimit, OnesWord, ProcessorRegister, 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], LizardHeart USING [ALUHelper, ALUOps, CacheBase, ChangeLogger, Control, InstBuffer, InstBufferRep, LizardIFUStackIndex, LizardIFUStackSize, NoFault, Processor, ProcessorRep, RegChangeProc, RegToWord, StackPlus, TrapPC, WordToReg], LizardLiver USING [Execute], PrincOpsUtils USING [LongCopy], Rope USING [ROPE]; LizardHeartImpl: CEDAR PROGRAM IMPORTS Basics, DragOpsCrossUtils, LizardHeart, LizardLiver, PrincOpsUtils EXPORTS LizardHeart = BEGIN OPEN DragOpsCross, DragOpsCrossUtils, LizardHeart; CARD: TYPE = Basics.CARD; 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] = { initStatus: IFUStatusRec _ [version: CurrentIFUVersion, rescheduleKeep: TRUE]; p _ NEW[ProcessorRep]; p.ifuCache _ ifuCache; p.euCache _ euCache; p.logger _ logger; p.eldest _ 1; p.youngest _ 1; p.stackEntries _ 0; p.regs[ifuPC] _ TrapPC[ResetTrap]; p.regsGood[euConstant] _ TRUE; -- constant register 0 = 0 (it's a ROM) p.regsGood[ifuStatus] _ TRUE; p.regs[ifuStatus] _ LOOPHOLE[initStatus]; p.instBuffer _ NEW[InstBufferRep[WordsInBuffer]]; }; 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; oldStatus: IFUStatusRec =LOOPHOLE[processor.regs[ifuStatus]]; status: IFUStatusRec _ oldStatus; 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[LOOPHOLE[regS, CARDINAL] -LOOPHOLE[WordToReg[processor.regs[ifuSLimit]], CARDINAL]+EUStackSize, CARDINAL] MOD EUStackSize IN [0..16) ]; }; CauseTrap: PROC [code: TrapIndex] = { <> <> regS: ProcessorRegister _ StackPlus[WordToReg[p.regs[ifuS]], 1]; oldStatus: Word = LOOPHOLE[status]; rtnPC _ thisPC; IF WillPushOverflow[p] THEN <> code _ IFUStackOverflowTrap; newPC _ TrapPC[code]; control _ doAbort; p.regs[ifuS] _ RegToWord[regS]; IF p.logger # NIL THEN { regChange: RegChangeProc _ p.logger.regChange; IF regChange # NIL THEN regChange[p.logger.data, p, regS, p.regs[regS], oldStatus]; }; p.regs[regS] _ oldStatus; status.trapsEnabled _ FALSE; status.userMode _ FALSE; p.regs[ifuStatus] _ LOOPHOLE[status]; 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 { <> status _ [version: CurrentIFUVersion, rescheduleKeep: TRUE]; p.regs[ifuStatus] _ LOOPHOLE[status]; thisPC _ newPC _ TrapPC[ResetTrap]; p.youngest _ youngest _ 0; p.eldest _ 1; p.stackEntries _ 0; p.resetRequested _ FALSE; p.regs[ifuL] _ CardToWord[1]; p.regs[ifuS] _ ZerosWord; p.euCarry _ FALSE; ForceBufferEmpty[]; p.instBuffer _ NEW[InstBufferRep[WordsInBuffer]]; p.instBuffer.forcedEmpty _ instBuffer.forcedEmpty; instBuffer _ p.instBuffer; p.stats _ []; p.ifuCache.stats _ []; p.euCache.stats _ []; initCycles _ cycles _ 0; }; TRUSTED { instBufferPtr _ @instBuffer[0]; }; IF newPC # instBuffer.nextPC THEN ForceBufferEmpty[] <> ELSE { <> 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 status.trapsEnabled THEN SELECT TRUE FROM IsEUStackOverflow[WordToReg[p.regs[ifuS]]] => { <> tx _ EUStackOverflowTrap; GO TO trapEarly; }; status.reschedule => { <> 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]; 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]; 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]; 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]; status _ LOOPHOLE[p.regs[ifuStatus]]; <> EXITS trapEarly => CauseTrap[tx]; }; thisPC _ newPC; SELECT control FROM nextInst => {}; <> doSwitch => <> ForceBufferEmpty[]; doCall, doAbort, doReturn => { ForceBufferEmpty[]; IF control = doReturn THEN { currentSize: NAT _ p.stackEntries; 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 status.trapsEnabled THEN { SIGNAL OutsideEnvelope["IFU control stack is too full during return!"]; GO TO badGnus; }; ENDCASE; p.stackEntries _ currentSize - 1; thisPC _ p.ifuStack[youngest].pc; p.regs[ifuL] _ regL _ RegToWord[p.ifuStack[youngest].regL]; p.youngest _ youngest _ (youngest + (LizardIFUStackSize - 1)) MOD LizardIFUStackSize; EXITS badGnus => {}; } ELSE { <> newSize: NAT _ p.stackEntries+1; 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, WordToReg[regL]]; ForceBufferEmpty[]; 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]; }; IF p.lastCallRtn + 3 >= p.stats.instructions THEN { <> cycles _ cycles + 2; p.stats.returnInterlockCycles _ p.stats.returnInterlockCycles + 2; }; 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, AddressCheckFault => WordToCard[res] 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: Word = DoubleWordShiftLeft[Left, Right, fd.shift]; <> mask: Word _ IF fd.mask >= 32 THEN OnesWord ELSE 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 { status: IFUStatusRec _ LOOPHOLE[processor.regs[ifuStatus]]; SELECT processor.stackEntries FROM = IFUOverflow-1 => {}; < IFUStackSize => IF status.trapsEnabled THEN SIGNAL OutsideEnvelope["IFU control stack too full with traps enabled"]; ENDCASE => SIGNAL OutsideEnvelope["IFU control stack has more than IFUStackSize entries"]; RETURN [status.trapsEnabled]; }; }; OutsideEnvelope: PUBLIC SIGNAL [explanation: Rope.ROPE] = CODE; END.