DIRECTORY Basics USING [CompareINT, Comparison], DragOpsCross USING [EUConstIndex, EUStackSize, FieldDescriptor, FourBytes, IFUOverflow, IFUStackSize, IFUStatusRec, Inst, IOOperand, ioRescheduleRequest, ioResetRequest, KernalLimit, 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; CARD: TYPE = LONG CARDINAL; careful: BOOL _ TRUE; Register: PUBLIC PROC [inst: Inst, handler: LizardTweaker.OpcodeHandler, data: REF _ NIL] = { 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; regS: ProcessorRegister = WordToReg[processor.regs[ifuS]]; regL: ProcessorRegister _ WordToReg[processor.regs[ifuL]]; sM1: ProcessorRegister _ StackPlus[regS, -1]; euBusyReg: ProcessorRegister _ processor.euBusyReg; status: IFUStatusRec _ LOOPHOLE[processor.regs[ifuStatus]]; flags: InstFlags _ flagsArray[inst]; cycles: NAT _ flags.cycles; alphaBetaGammaDeltaZ: CARD _ WordToCard[rest]; stackEffect: INTEGER _ flags.stackEffect.delta; resReg: ProcessorRegister _ StackPlus[regS, stackEffect]; topOfStack: Word _ IF flags.usesTop THEN RegFetchInline[regS] ELSE ZerosWord; resWord: Word _ topOfStack; resAddr: Word _ topOfStack; bytes: CARDINAL _ flags.bytes; dispatch: Inst _ SELECT TRUE FROM flags.mod16 => VAL[ORD[inst] - (ORD[inst] MOD 16)], ENDCASE => inst; alphaBetaZ: CARDINAL _ 0; alphaZ: CARDINAL _ 0; betaZ: CARDINAL _ 0; formBytes2: TwoBytes; formBytes3: FourBytes; RegFetch: PROC [reg: ProcessorRegister] RETURNS [Word] = { IF reg = euBusyReg THEN { processor.stats.cycles _ processor.stats.cycles + 1; euBusyReg _ euJunk}; IF NOT processor.regsGood[reg] THEN InitFault[reg]; RETURN [processor.regs[reg]]; }; RegFetchInline: PROC [reg: ProcessorRegister] RETURNS [Word] = INLINE { IF reg = euBusyReg THEN { processor.stats.cycles _ processor.stats.cycles + 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] = { 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 _ RegToWord[processor.ifuStack[processor.eldest].regL]; ifuYoungestPC => old _ processor.ifuStack[processor.youngest].pc; ifuYoungestL => old _ RegToWord[processor.ifuStack[processor.youngest].regL]; ENDCASE; regChange[logger.data, processor, reg, old, word]; }; }; processor.regsGood[reg] _ TRUE; processor.regs[reg] _ word; }; FastRegStore: PROC [reg: ProcessorRegister, word: Word] = INLINE { 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; }; MemFetch: PROC [addr: Word] RETURNS [word: Word, code: TrapIndex] = { rejectCycles: INT; [word, code, rejectCycles] _ processor.euCache.fetch[processor.euCache, addr, processor.stats.cycles]; 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]; 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] = { RETURN [ZerosWord, NoFault]; }; IOStore: PROC [cmd: PCmdFormat, addr: Word, word: Word] RETURNS [TrapIndex] = { 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 => { status.reschedule _ TRUE; RegStore[ifuStatus, LOOPHOLE[status]]; }; ioResetRequest => { processor.resetRequested _ TRUE; }; ENDCASE; RETURN [NoFault]; }; CalcReg: PROC [aux,opt: BOOL, regX: [0..15], dest: BOOL _ FALSE] RETURNS [reg: ProcessorRegister] = { IF NOT opt THEN RETURN [IF aux THEN RegPlus[euAux, regX] ELSE StackPlus[regL, regX]]; SELECT regX FROM IN EUConstIndex => RETURN [RegPlus[euConstant, regX]]; 12 => RETURN [regS]; 13 => RETURN [sM1]; ENDCASE; IF dest THEN {stackEffect _ stackEffect+1; RETURN [StackPlus[regS, 1]]}; stackEffect _ stackEffect - 1; RETURN [IF regX = 14 THEN regS ELSE sM1]; }; CauseTrap: PROC [code: TrapIndex] = { oldStatus: IFUStatusRec _ status; rtnPC _ thisPC; stackEffect _ 1; IF status.trapsEnabled THEN SELECT TRUE FROM LizardHeart.WillPushOverflow[processor] => code _ IFUStackOverflowTrap; ENDCASE; newPC _ TrapPC[code]; control _ doAbort; status.trapsEnabled _ FALSE; status.userMode _ FALSE; processor.regs[ifuStatus] _ LOOPHOLE[status]; FastRegStore[StackPlus[regS, stackEffect], LOOPHOLE[oldStatus]]; 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; RegStore[ifuL, RegToWord[newValue]]; RETURN [TRUE]; }; MergeStatus: PROC RETURNS [Word] = { req: IFUStatusRec _ LOOPHOLE[topOfStack]; IF NOT req.userModeKeep THEN status.userMode _ req.userMode; IF NOT req.trapsEnabledKeep THEN status.trapsEnabled _ req.trapsEnabled; IF NOT req.rescheduleKeep THEN status.reschedule _ req.reschedule; IF status.userModeKeep THEN ERROR; IF status.trapsEnabledKeep THEN ERROR; IF NOT status.rescheduleKeep THEN ERROR; IF status.trapsEnabled AND NOT processor.regsGood[ifuSLimit] THEN SIGNAL OutsideEnvelope["Traps enabled before SLimit set"]; RETURN [LOOPHOLE[status]]; }; 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 status.trapsEnabled THEN { sLimit: ProcessorRegister = WordToReg[processor.regs[ifuSLimit]]; IF (newValue.ORD-sLimit.ORD+EUStackSize) MOD EUStackSize IN [0..16) THEN { CauseTrap[EUStackOverflowTrap]; RETURN [FALSE]; }; }; RegStore[ifuS, RegToWord[newValue]]; RETURN [TRUE]; }; WillEUStackOverflow: PROC RETURNS [overflow: BOOL _ FALSE] = INLINE { IF stackEffect # 0 AND status.trapsEnabled THEN { newS: ProcessorRegister = StackPlus[regS, stackEffect]; sLimit: ProcessorRegister = WordToReg[processor.regs[ifuSLimit]]; IF (newS.ORD-sLimit.ORD+EUStackSize) 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]; 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; }; 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; { SELECT dispatch FROM dDFC => { newPC _ rest; GO TO call; }; dLIQB => { resWord _ rest; GO TO storeReg; }; dJ5 => { GO TO noCheck; }; dJQB => { newPC _ rest; GO TO jump; }; dOR, dAND, dRX, dBC, dADD, dSUB, dLADD, dLSUB => { op: ALUOps; cond: TrapIndex _ ALUCondFalse; wordA: Word = RegFetchInline[sM1]; SELECT inst FROM dOR => { op _ Or; }; dAND => { op _ And; }; dRX => { resAddr _ VanillaAdd[wordA, topOfStack]; GO TO memFetch; }; dBC => { op _ BndChk; cond _ ALUCondBC; }; dADD => { op _ SAdd; cond _ ALUCondOver; }; dSUB => { op _ SSub; cond _ ALUCondOver; }; dLADD => { op _ LAdd; cond _ ALUCondIL; }; dLSUB => { op _ LSub; cond _ ALUCondIL; }; ENDCASE => GO TO xop; [resWord, trapCode] _ DoALUOp[processor, wordA, topOfStack, op, cond]; GO TO aluDone; }; dDUP => GO TO storeReg; dDIS => GO TO done; dEXDIS => GO TO storeReg; dSFC => { newPC _ topOfStack; GO TO call; }; dSFCI => { [newPC, trapCode] _ MemFetch[topOfStack]; IF trapCode # NoFault THEN GO TO memFault; GO TO call; }; dRETN => GO TO return; dKFC => { IF NOT LizardHeart.WillPushOverflow[processor] THEN { status.userMode _ status.trapsEnabled _ FALSE; processor.regs[ifuStatus] _ LOOPHOLE[status]; newPC _ CardToWord[DragOpsCrossUtils.XopToBytePC[inst]]; RegStore[resReg, resWord]; }; GO TO call; }; dJ1 => GO TO noCheck; dJS => { newPC _ VanillaAdd[thisPC, topOfStack]; IF WillEUStackOverflow[] THEN GO TO euOverflow; GO TO jump; }; dLC0, dLC1, dLC2, dLC3, dLC4, dLC5, dLC6, dLC7 => { resReg _ RegPlus[euConstant, ORD[inst] MOD 8]; GO TO pushReg; }; dLR0 => { resReg _ StackPlus[regL, ORD[inst] MOD 16]; GO TO pushReg; }; dSR0 => { 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; pushAtop => {}; pushA0 => srcReg _ euConstant; pushA1 => srcReg _ RegPlus[euConstant, 1]; 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 => { op _ Or; }; dQAND => { op _ And; }; dQRX => { resAddr _ VanillaAdd[topOfStack, wordB]; GO TO memFetch; }; dQBC => { op _ BndChk; cond _ ALUCondBC; }; dQADD => { op _ SAdd; cond _ ALUCondOver; }; dQSUB => { op _ SSub; cond _ ALUCondOver; }; dQLADD => { op _ LAdd; cond _ ALUCondIL; }; dQLSUB => { op _ LSub; cond _ ALUCondIL; }; ENDCASE => ERROR; [resWord, trapCode] _ DoALUOp[processor, topOfStack, wordB, op, cond]; GO TO aluDone; }; dALS => { [] _ AdjustL[regS]; GO TO noCheck; }; dAL => { [] _ AdjustL[regL]; GO TO noCheck; }; dASL => { [] _ AdjustS[regL]; GO TO noCheck; }; dAS => { [] _ AdjustS[regS]; GO TO noCheck; }; dCST => { 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 { resWord _ RegFetch[sM1]; GO TO memStore; }; [] _ MemFetch[resAddr]; GO TO noCheck; }; dRET => { IF NOT AdjustS[regL] THEN GO TO noCheck; GO TO return; }; dRETK => { delta: CARDINAL _ (ORD[regS]+(EUStackSize+1)-ORD[regL]) MOD EUStackSize; newS: ProcessorRegister = StackPlus[regL, alphaZ]; IF delta > EUStackSize/2 OR (alphaZ+1) MOD 256 > delta THEN IF careful THEN SIGNAL OutsideEnvelope["VERY suspicious RETK!"]; IF status.userMode THEN GO TO modeFault; resWord _ MergeStatus[]; RegStore[ifuS, RegToWord[newS]]; RegStore[ifuStatus, resWord]; GO TO return; }; dLIP => { reg: ProcessorRegister _ LOOPHOLE[alphaZ]; IF reg > euLast THEN cycles _ cycles + 4; SELECT reg FROM ifuEldestPC => { eldest: NAT _ processor.eldest; entries: NAT _ processor.stackEntries; IF status.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; 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 _ RegToWord[processor.ifuStack[processor.eldest].regL]; }; 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 _ RegToWord[processor.ifuStack[processor.youngest].regL]; }; ENDCASE => resWord _ RegFetch[reg]; GO TO storeReg }; dSIP => { reg: ProcessorRegister _ LOOPHOLE[alphaZ]; IF status.userMode THEN GO TO modeFault; stackEffect _ -1; [] _ WillEUStackOverflow[]; IF reg > euLast THEN cycles _ cycles + 4; SELECT reg FROM ifuStatus => { resWord _ MergeStatus[]; IF status.trapsEnabled AND processor.stackEntries >= IFUOverflow THEN SIGNAL OutsideEnvelope["Too full IFU stack in SIP ifuStatus."]; }; ifuEldestPC => { 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; }; ifuEldestL => { IF processor.stackEntries = 0 THEN SIGNAL OutsideEnvelope["Empty IFU stack in SIP ifuEldestL."]; processor.ifuStack[processor.eldest].regL _ WordToReg[resWord]; }; ifuYoungestPC => { IF processor.stackEntries = 0 THEN SIGNAL OutsideEnvelope["Empty IFU stack in SIP ifuYoungestPC."]; processor.ifuStack[processor.youngest].pc _ resWord; }; ifuYoungestL => { IF processor.stackEntries = 0 THEN SIGNAL OutsideEnvelope["Empty IFU stack in SIP ifuYoungestL."]; processor.ifuStack[processor.youngest].regL _ WordToReg[resWord]; }; ENDCASE => IF WillEUStackOverflow[] THEN GO TO euOverflow; resReg _ reg; GO TO storeReg; }; dLIB => { resWord _ CardToWord[alphaZ]; GO TO storeReg; }; dADDB, dSUBB => { [resWord, trapCode] _ DoALUOp[ processor, topOfStack, CardToWord[alphaZ], IF inst = dADDB THEN SAdd ELSE SSub, ALUCondOver]; GO TO aluDone; }; dJ2 => GO TO noCheck; dJB => { alphaS: INT _ alphaZ; IF alphaZ > 127 THEN alphaS _ alphaS - 256; newPC _ AddDelta[alphaS, thisPC]; GO TO jump; }; dRB => GO TO memFetchAlpha; dWB => { resWord _ RegFetch[sM1]; GO TO memStoreAlpha; }; dRSB => GO TO memFetchAlpha; dWSB => { resAddr _ RegFetch[sM1]; GO TO memStoreAlpha; }; dIOS, dIOL, dION => { ioOp: IOOperand = [LOOPHOLE[alphaZ], LOOPHOLE[betaZ]]; pDataA: Word _ AddDelta[ByteToCard[ioOp.pDataA], (IF inst = dIOS THEN topOfStack ELSE CardToWord[0])]; IF status.userMode THEN SELECT TRUE FROM ioOp.pCmd.mode = kernelOnly => GO TO modeFault; ioOp.pCmd.addressChecks = kernelIfFirst16M AND WordToCard[pDataA] < KernalLimit => { CauseTrap[AddressCheckFault]; GO TO noCheck; }; ENDCASE; SELECT ioOp.pCmd.direction FROM read => { IF inst = dIOL 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 dIOL => stackEffect _ -1; dIOS => {resWord _ RegFetch[sM1]; stackEffect _ -2}; ENDCASE; trapCode _ IOStore[ioOp.pCmd, pDataA, resWord]; IF trapCode # NoFault THEN GO TO memFault; GO TO done; }; ENDCASE => ERROR; }; dPSB => { resAddr _ RegFetch[sM1]; GO TO memStoreAlpha; }; dLRI0 => { OPEN form: LOOPHOLE[formBytes2, LRBformat]; resAddr _ RegFetchInline[StackPlus[regL, form.reg]]; GO TO memFetchAlpha; }; dSRI0 => { 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]; { firstProtectedAux: ProcessorRegister = euAux; lastProtectedAux: ProcessorRegister = VAL[ORD[firstProtectedAux]+7]; firstProtectedConst: ProcessorRegister = euConstant; lastProtectedConst: ProcessorRegister = VAL[ORD[firstProtectedConst]+11]; SELECT resReg FROM IN [firstProtectedAux..lastProtectedAux] => IF status.userMode THEN GO TO modeFault; IN [firstProtectedConst..lastProtectedConst] => { IF status.userMode THEN GO TO modeFault; IF resReg = euConstant THEN { SIGNAL OutsideEnvelope["Storing into constant register 0 not permited!"]; resWord _ CardToWord[0]; trapCode _ NoFault; GO TO aluDone; } }; ENDCASE; }; SELECT inst FROM dROR => { op _ Or; }; dRAND => { op _ And; }; dRRX => { resAddr _ VanillaAdd[wordA, wordB]; GO TO memFetch; }; dRBC => { op _ BndChk; cond _ ALUCondBC; }; dRADD => { op _ SAdd; cond _ ALUCondOver; }; dRSUB => { op _ SSub; cond _ ALUCondOver; }; dRLADD => { op _ LAdd; cond _ ALUCondIL; }; dRLSUB => { op _ LSub; cond _ ALUCondIL; }; dRXOR => { op _ Xor; }; dRFU => { fd: FieldDescriptor = CardToFieldDescriptor[ HalfToCard[WordToHalves[RegFetch[euField]][1]] ]; resWord _ FieldUnit[wordA, wordB, fd]; GO TO storeReg; }; dRVADD => { resWord _ VanillaAdd[wordA, wordB]; GO TO storeReg; }; dRVSUB => { resWord _ VanillaSub[wordA, wordB]; GO TO storeReg; }; dRUADD => { op _ UAdd; }; dRUSUB => { op _ USub; }; ENDCASE => ERROR; [resWord, trapCode] _ DoALUOp[processor, wordA, wordB, op, cond]; GO TO aluDone; }; dLGF => { resAddr _ AddDelta[alphaBetaZ, RegFetch[euAux]]; GO TO memFetch; }; dLIDB => { resWord _ CardToWord[alphaBetaZ]; GO TO storeReg; }; dADDDB, dSUBDB => { [resWord, trapCode] _ DoALUOp[ processor, topOfStack, CardToWord[alphaBetaZ], IF inst = dADDDB THEN SAdd ELSE SSub, ALUCondOver]; GO TO aluDone; }; dJ3 => GO TO noCheck; dJDB => { newPC _ AddDelta[LOOPHOLE[alphaBetaZ, INTEGER], thisPC]; GO TO jump; }; dLFC => { 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 => { GO TO memFetchAlpha; }; dWAI, dWRI => { resWord _ RegFetchInline[resReg]; GO TO memStoreAlpha; }; ENDCASE => ERROR; }; dRJEB, dRJEBJ, dRJGB, dRJGBJ, dRJGEB, dRJGEBJ, dRJLB, dRJLBJ, dRJLEB, dRJLEBJ, dRJNEB, dRJNEBJ => { 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; GO TO condFall; }; dJEBB, dJEBBJ => IF topOfStack = CardToWord[alphaZ] THEN GO TO condJump ELSE GO TO condFall; dJNEBB, dJNEBBJ => IF topOfStack # CardToWord[alphaZ] THEN GO TO condJump ELSE GO TO condFall; dSHL => { topOfStack _ ZerosWord; GO TO storeField; }; dSHR => GO TO storeField; dSHD => { resWord _ RegFetch[sM1]; GO TO storeField; }; dFSDB => { 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 => { IF WillEUStackOverflow[] THEN CauseTrap[EUStackOverflowTrap]; }; noCheck => { }; xop => { stackEffect _ 0; control _ doCall; newPC _ CardToWord[DragOpsCrossUtils.XopToBytePC[inst]]; SELECT TRUE FROM LizardHeart.WillPushOverflow[processor: processor] => CauseTrap[IFUStackOverflowTrap]; bytes > 1 => { cycles _ cycles+1; stackEffect _ 1; resWord _ rest; IF WillEUStackOverflow[] THEN CauseTrap[EUStackOverflowTrap] ELSE RegStore[StackPlus[regS, stackEffect], resWord]; }; ENDCASE; }; condJump => { 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 => { 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 => { 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 => { 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 status.trapsEnabled THEN SIGNAL OutsideEnvelope["IFU control stack is too full during return!"]; ENDCASE; control _ doReturn; rtnPC _ processor.ifuStack[processor.youngest].pc; }; modeFault => { CauseTrap[ModeFault]; }; euOverflow => { CauseTrap[EUStackOverflowTrap]; }; pushReg => { IF WillEUStackOverflow[] THEN CauseTrap[EUStackOverflowTrap] ELSE FastRegStore[StackPlus[regS, 1], RegFetchInline[resReg]]; }; storeField => { IF WillEUStackOverflow[] THEN CauseTrap[EUStackOverflowTrap] ELSE FastRegStore[resReg, FieldUnit[resWord, topOfStack, CardToFieldDescriptor[alphaBetaZ]]]; }; storeReg => { IF WillEUStackOverflow[] THEN CauseTrap[EUStackOverflowTrap] ELSE RegStore[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 status.userMode AND WordToCard[resAddr] < KernalLimit => CauseTrap[AddressCheckFault]; WillEUStackOverflow[] => CauseTrap[EUStackOverflowTrap]; ENDCASE => { trapCode _ MemStore[resAddr, resWord]; IF trapCode # NoFault THEN CauseTrap[trapCode]; }; }; memStoreAlpha => { resAddr _ AddDelta[alphaZ, resAddr]; SELECT TRUE FROM status.userMode AND WordToCard[resAddr] < KernalLimit => CauseTrap[AddressCheckFault]; 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: BOOL _ FALSE, predictJump: BOOL _ FALSE, mod16: BOOL _ FALSE, callMesa: BOOL _ FALSE, cycles: [0..15] _ 1, bytes: [0..7] _ 1, stackEffect: RECORD [sure: BOOL _ FALSE, delta: [-2..1] _ 0] ]; InitFlagsArray: PROC = { defaultFlags: InstFlags _ []; flagsArray _ NEW[ARRAY Inst OF InstFlags]; FOR inst: Inst IN Inst DO bytes: NAT _ ORD[inst] / 64; flags: InstFlags _ defaultFlags; IF bytes = 0 THEN IF ORD[inst] < 40B THEN bytes _ 1 ELSE bytes _ 5; flags.bytes _ bytes; flags.stackEffect _ SELECT inst FROM dWB, dWSB => [sure: TRUE, delta: -2], dDIS, dEXDIS, dSFC, dJS, dPSB, dSHD, dFSDB, dJEBB, dJEBBJ, dJNEBB, dJNEBBJ, IN[dOR..dLSUB], IN [dSR0..dSR15], IN [dSRI0..dSRI15] => [sure: TRUE, delta: -1], dLIQB, dDUP, dCST, dLIP, dLIB, dRSB, dLGF, dLIDB, IN [dLC0..dLC7], IN [dLR0..dLR15], IN [dLRI0..dLRI15], x060B, IN [x063B..x065B], x215B, x223B, IN [x234B..x236B], x323B, IN [x364B..x367B], IN [x374B..x377B] => [sure: TRUE, delta: 1], IN [dTrap..dLC0), dDFC, dJ5, dJQB, x112B, dRETN, IN [x117B..x123B], x125B, dJ1, dADDB, dSUBB, dJ2, dJB, dRB, dLFC, dADDDB, dSUBDB, dJ3, dJDB, IN [dRAI..dWRI], dSHL, dSHR, dSHD => [sure: TRUE, delta: 0], ENDCASE => [sure: FALSE, delta: 0]; flags.cycles _ SELECT inst FROM dCST => 8, dRETK, dSFC, dSFCI, dJS => 4, dRET, dRETN, dKFC => 2, ENDCASE => 1; SELECT inst FROM IN [dSR0..dSR15], IN [dSRI0..dSRI15], IN[dOR..dLSUB], dDUP, dEXDIS, dSFC, dSFCI, dJS, dCST, dRETK, dSIP, dADDB, dSUBB, dRB, dWB, dRSB, dWSB, dPSB, dADDDB, dSUBDB, dJEBB, dJEBBJ, dJNEBB, dJNEBBJ, dSHL, dSHR, dSHD, dFSDB, dIOS, dIOL, 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. !*LizardLiverImpl.mesa Copyright c 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) March 18, 1986 10:36:30 am PST McCreight, January 22, 1986 12:16:44 pm PST 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. Register an interceptor for the given opcode. Capture commonly used pieces of the processor state regS is the current euRegister for the top of stack regL is the current euRegister for the frame base sM1 is the current euRegister for just below the top of stack euBusyReg is the current euRegister busy because of a previous fetch Compute some common things based on the flags The destination is often given by the stack effect (especially when stackEffect = 1) 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. It is useful to have this be initialized to the top of stack It is useful to have this be initialized to the top of stack Capture some common quantities based on the operand bytes for 2-byte instructions for 3-byte instructions Routines for fetching and storing processor registers Any funny side effects are handled in LIP. Any funny side effects are handled in LIP. Any funny side effects are handled in SIP. A faster version of RegStore. Not to be used for IFU regs. Routines for fetching and storing memory words (incomplete) Not yet implemented Not yet implemented (except for ioRescheduleRequest & ioResetRequest) A routine to calculate regs for RR, QR, and RJB format First, handle aux regs and local regs Handle constant regs, [S], and [S-1]. For destination regs, the register is [S+1]+. For source regs, the register is either [S]-, or [S-1]- 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. RRA: All traps push the status onto the EU stack, and set the status to have traps disabled and be in kernel mode. If such a trap would cause an IFU stack overflow, then the trap is converted into an IFUStackOverflowTrap. IFUStackOverflow has precedence over other traps Status merging: used in SIP and RETK. Checking for EU stack overflow Init the next PC and the return PC to be just after this instruction **** This is where we log the start of the instruction, providing that someone is interested. In most cases the euBusyReg is set to not busy. start scope for EXITS, all instruction dispatching is handled within this block. Effect: call proc at AlphaBetaGammaDelta Effect: [S]_AlphaBetaGammaDelta; S_S+1 Effect: noop Effect: noop Effect: [S-1]_[S-1] op [S]; S_S-1; Effect: [S-1]_[S-1] OR [S]; S_S-1 Effect: [S-1]_[S-1] AND [S]; S_S-1 Effect: [S-1]_([S-1]+[S])^; S_S-1 Effect: trap if [S-1] < 0 OR [S-1]-[S] >= 0; S_S-1 Effect: [S-1]_[S-1]+[S]+carry; carry_0; trap on overflow; S_S-1 Effect: [S-1]_[S-1]-[S]-carry; carry_0; trap on overflow; S_S-1 Effect: [S-1]_[S-1]+[S]; carry_0; trap on overflow or Lisp NaN; S_S-1 Effect: [S-1]_[S-1]-[S]; carry_0; trap on overflow or Lisp NaN; S_S-1 Effect: [S+1]_[S]; S_S+1 Effect: S_S-1 Effect: [S-1]_[S]; S_S-1 Effect: call proc at [S]; S_S-1 Effect: call proc at ([S])^ Effect: return from proc (no stack change) Effect: [S+1]_Status; PC_InstTrap[KFC]; set kernel mode; disable traps; S_S+1 Effect: noop Effect: PC_PC+[S]; S_S-1 Effect: [S+1]_Constants[n]; S_S+1 Effect: [S+1]_[L+n]; S_S+1 Effect: [L+n]_[S]; S_S-1 C: [S], A: [S], B: general C: [S+1]+, A: [S], B: general C: [S+1]+, A: c0, B: general C: [S+1]+, A: c1, B: general Effect: [S] _ Rs OR Rb Effect: [S] _ Rs AND Rb Effect: [S] _ (Rs+[Rb])^ Effect: [S] _ Rs BC Rb; trap if [S] < 0 OR [S]-Rb >= 0 Effect: [S] _ Rs+Rb+carry; carry_0; trap on overflow Effect: [S] _ Rs-Rb-carry; carry_0; trap on overflow Effect: [S] _ Rs+Rb; carry_0; trap on overflow or Lisp NaN Effect: [S] _ Rs-Rb; carry_0; trap on overflow or Lisp NaN Effect: L _ S + Alpha Effect: L _ L + Alpha Effect: S _ L + Alpha (no stack overflow check) Effect: S _ S + Alpha (no stack overflow check) Effect: atomic [S+1] _ ([S-2]+AlphaZ)^; IF [S+1] = [S] THEN [S-2]+AlphaZ)^ _ [S-1] ELSE [] _ ([S-2]+AlphaZ)^; S_S+1 The conditional store was successful, so store the new word The conditional store was not successful, so refetch the word (which simulates releasing the bus) Effect: S_L+Alpha; return from proc Effect: RETurn from Kernel. Status_[S]; S_L+AlphaZ Effect: [S+1]_PReg[Alpha]; S_S+1 IFU registers take longer to fetch than EU registers 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. Check for overflow before altering the state! 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 This just checks for bogus oveflows IFU registers take longer to store than EU registers Setting this register may enable traps. 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. Effect: [S+1]_AlphaZ; S_S+1 Effect: [S] _ [S] + AlphaZ; trap on overflow Effect: noop Effect: PC_PC+Alpha Effect: [S]_([S]+AlphaZ)^ Effect: ([S]+AlphaZ)^_[S-1]; S_S-2 Effect: [S+1]_([S]+AlphaZ)^; S_S+1 Effect: ([S-1]+AlphaZ)^_[S]; S_S-2 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. kernel mode required for this cmd if address IN [0..1000000H) Effect: ([S-1]+AlphaZ)^_[S]; S_S-1 Effect: [S+1]_([L+n]+AlphaZ)^; S_S+1 Effect: ([L+n]+AlphaZ)^_[S]; S_S-1 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. Effect: Rc _ Ra OR Rb Effect: Rc _ Ra AND Rb Effect: [Rc]_([Ra]+[Rb])^ Effect: Rc _ Ra; trap if Ra < 0 OR Ra-Rb >= 0 Effect: Rc _ Ra+Rb+carry; carry_0; trap on overflow Effect: Rc _ Ra-Rb-carry; carry_0; trap on overflow Effect: Rc _ Ra+Rb; carry_0; trap on overflow or Lisp NaN Effect: Rc _ Ra-Rb; carry_0; trap on overflow or Lisp NaN Effect: Rc _ Ra XOR Rb Effect: [Rc]_FieldUnit[[Ra],[Rb],MDF] Effect: Rc _ Ra+Rb Effect: Rc _ Ra-Rb Effect: Rc _ Ra+Rb+carry; set carry Effect: Rc _ Ra-Rb-carry; set carry Effect: [S+1]_([GB]+AlphaBetaZ)^; S_S+1 Effect: [S+1]_AlphaBetaZ; S_S+1 Effect: [S] _ [S] + AlphaBetaZ; trap on overflow Effect: noop Effect (dJDB): PC_PC+AlphaBetaS Effect (dLFC): call proc at PC+AlphaBetaS Effect (dRAI): [L+BetaL]_(AuxRegs[BetaR]+AlphaZ)^ Effect (dRRI): [L+BetaL]_([L+BetaR]+AlphaZ)^ Effect (dWAI): (AuxRegs[BetaR]+AlphaZ)^_[L+BetaL] Effect (dWRI): ([L+BetaR]+AlphaZ)^_[L+BetaL] Effect: IF Rs cond Rb THEN PC_PC+BetaS At this point we should NOT branch. Effect: AlphaZ = [S] => PC_PC+BetaS; S_S-1 Effect: AlphaZ # [S] => PC_PC+BetaS; S_S-1 Effect: [S]_FieldUnit[[S],0,AlphaBeta] Effect: [S]_FieldUnit[[S],[S],AlphaBeta] Effect: [S-1]_FieldUnit[[S-1],[S],AlphaBeta]; S_S-1 Effect: euField_AlphaBeta+[S]; S_S-1 There is really nothing to do here, we're done. There is really nothing to do here, not even an overflow check. 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. must push the following bytes (can have high-order junk) A conditional jump branched A conditional jump did NOT branch There is no need to check for EU overflow here. To perform a return. The EU stack checking has already been done. For debugging purposes This exit is taken if we attempt to execute a protected instruction while in user mode. This exit is taken if we find that the resulting value of S is bogus. Note: no need to check for stack overflow since we did that earlier resReg can be an IFU register, so don't use FastRegStore here. ccodeK m=HK2K+Kk K&K KK1KK K KI\headK, BK"K :KKK KK n:]K--K ##iK''K!KK/3K>6KKK((K33::K33::K11--K==33KDDK;K--K$$KK20 sp tabStops.K /99KTT20 sp tabStops MK20 sp tabStopsK<<K<KKKCKK KQQK@X K88 K,,K 4KKKKKK"UKKKK+OKEE K)) KFFKK20 sp tabStopsK20 sp tabStops &KK KKK KK66K  eKK%%KEKK%%K6KKKKK--HKK77KK )KKK %KK20 sp tabStops!!K20 sp tabStopsK20 sp tabStops20 sp tabStops20 sp tabStops20 sp tabStops**K20 sp tabStops00K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStops -K20 sp tabStops+ @#K::KK:K20 sp tabStops6620 sp tabStops20 sp tabStops&(K20 sp tabStops 2HK20 sp tabStopsK20 sp tabStops$$K20 sp tabStopsKK%%K  $K )K K20 sp tabStops20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops K20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops((K20 sp tabStops K20 sp tabStops20 sp tabStops K20 sp tabStops66K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops44K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops44K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops::K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops::K20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStopsFFK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops00K20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops00K20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops 20 sp tabStopsbK20 sp tabStopsK20 sp tabStops EK20 sp tabStopsK20 sp tabStops::K20 sp tabStops((20 sp tabStopsK20 sp tabStops %K20 sp tabStops *K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops;;K20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStopsaaK20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops##K20 sp tabStops (K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops33K  HK22  ;K *@K (K20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops K20 sp tabStops *20 sp tabStops)K20 sp tabStops4420 sp tabStops20 sp tabStopsK20 sp tabStopsXOK20 sp tabStopsK20 sp tabStops &K (K20 sp tabStops((20 sp tabStopsK20 sp tabStops7B20 sp tabStops /K--K20 sp tabStops 7K20 sp tabStops%%K20 sp tabStopsK20 sp tabStops20 sp tabStops20 sp tabStops"K20 sp tabStops7=K20 sp tabStops??K20 sp tabStops20 sp tabStops20 sp tabStops"K20 sp tabStops:@K20 sp tabStops44K20 sp tabStops20 sp tabStops20 sp tabStopsK20 sp tabStops 9DK20 sp tabStopsAAK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops 20 sp tabStopsK20 sp tabStopsK20 sp tabStops *K (KK##20 sp tabStops)K20 sp tabStops44K''K'EK9?KKK (K)1K K KK!!K-DKK((K20 sp tabStops20 sp tabStops"K20 sp tabStops7=K20 sp tabStops??K20 sp tabStops20 sp tabStops20 sp tabStops"K20 sp tabStops:@K20 sp tabStops44K20 sp tabStops20 sp tabStops20 sp tabStops"K20 sp tabStops9?K20 sp tabStopsAAK20 sp tabStopsK  :K20 sp tabStops K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops,20 sp tabStopsK20 sp tabStops K20 sp tabStops K20 sp tabStopsK20 sp tabStops$K20 sp tabStops K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops K20 sp tabStops20 sp tabStopsK20 sp tabStopsK20 sp tabStops K20 sp tabStops+K20 sp tabStops!!K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops""K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops""K20 sp tabStops20 sp tabStops K20 sp tabStops""K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStopsK20 sp tabStops  6K20 sp tabStops2  fK20 sp tabStops20 sp tabStops (20 sp tabStopsK20 sp tabStops 20 sp tabStops+&TK20 sp tabStops==K20 sp tabStopsK20 sp tabStops K20 sp tabStops20 sp tabStopsK20 sp tabStops20 sp tabStops20 sp tabStops K20 sp tabStops +>K20 sp tabStops11K20 sp tabStops *K20 sp tabStops K20 sp tabStopsK20 sp tabStops K20 sp tabStops20 sp tabStops 20 sp tabStopsK20 sp tabStopsK20 sp tabStops44K20 sp tabStopsK20 sp tabStops//K20 sp tabStops *K20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops""K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops$$K20 sp tabStops+K20 sp tabStops44K20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops""K20 sp tabStops+K20 sp tabStops44K20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStopsggK20 sp tabStops*K20 sp tabStops K20 sp tabStops::K20 sp tabStops##K20 sp tabStops::K20 sp tabStops##K20 sp tabStops K20 sp tabStopsK20 sp tabStops)/K20 sp tabStops20 sp tabStopsK20 sp tabStopsK20 sp tabStops--K20 sp tabStops&DK20 sp tabStops44K20 sp tabStops(I20 sp tabStops20 sp tabStops)+K20 sp tabStops (20 sp tabStops/1K20 sp tabStops (20 sp tabStopsK20 sp tabStopsCIK20 sp tabStopsK20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops K20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops##K20 sp tabStops K20 sp tabStops20 sp tabStops K20 sp tabStops--K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops33K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops33K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops99K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops99K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops K20 sp tabStops20 sp tabStops K20 sp tabStops!%20 sp tabStops,,K20 sp tabStops..K20 sp tabStopsK20 sp tabStops&&K20 sp tabStops K20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops##K20 sp tabStops K20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops##K20 sp tabStops K20 sp tabStops20 sp tabStops K20 sp tabStops##K20 sp tabStops K20 sp tabStops20 sp tabStops K20 sp tabStops##K20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsAAK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops''K20 sp tabStops00K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops!!K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops020 sp tabStopsK20 sp tabStops K20 sp tabStops K20 sp tabStopsK20 sp tabStops%K20 sp tabStops K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops K20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops  8K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops))K20 sp tabStops  8K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops,20 sp tabStops-K20 sp tabStops((K20 sp tabStops&K20 sp tabStops$$K20 sp tabStops""20 sp tabStops20 sp tabStopsK20 sp tabStops11K20 sp tabStops,,K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops11K20 sp tabStops,,K20 sp tabStops!!K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStopsccK20 sp tabStops  &K20 sp tabStops+K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops  K20 sp tabStopsK20 sp tabStops77K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStopsK20 sp tabStopsBBK20 sp tabStopsK20 sp tabStops /K20 sp tabStops28 sp tabStopsK28 sp tabStops  ;K28 sp tabStops  =K28 sp tabStops  >K20 sp tabStopsK20 sp tabStopsK20 sp tabStops =K20 sp tabStops20 sp tabStops K20 sp tabStops!!28 sp tabStops28 sp tabStopsK28 sp tabStopsK28 sp tabStops66K20 sp tabStopsK28 sp tabStopsK28 sp tabStopsAEK20 sp tabStops =K20 sp tabStops20 sp tabStops K20 sp tabStops//K28 sp tabStops22K20 sp tabStopsK20 sp tabStops20 sp tabStops 20 sp tabStops20 sp tabStops**K20 sp tabStops 20 sp tabStopsK20 sp tabStops20 sp tabStops K28 sp tabStops22K20 sp tabStopsK20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsBB"K>DKAGKAGKK20 sp tabStops20 sp tabStops22K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStopsWWK20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStopsEEK20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsCC20 sp tabStopsK20 sp tabStops#K20 sp tabStops:>K20 sp tabStops20 sp tabStops20 sp tabStopsK20 sp tabStops#K20 sp tabStopsY]K20 sp tabStops20 sp tabStops K20 sp tabStops>>20 sp tabStopsK20 sp tabStops#K20 sp tabStopsK20 sp tabStops20 sp tabStops 20 sp tabStopsK20 sp tabStops**K20 sp tabStops88K20 sp tabStops")K20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops 20 sp tabStopsK20 sp tabStops#20 sp tabStopsK20 sp tabStops((20 sp tabStopsK20 sp tabStops**K20 sp tabStopsBIK20 sp tabStopsK20 sp tabStops20 sp tabStops20 sp tabStopsK20 sp tabStops#20 sp tabStopsK20 sp tabStops::20 sp tabStopsK20 sp tabStops**K20 sp tabStopsBIK20 sp tabStopsK20 sp tabStops20 sp tabStops 20 sp tabStops20 sp tabStops%8K20 sp tabStops20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops&&K20 sp tabStops/K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops$$20 sp tabStops20 sp tabStops%8K20 sp tabStops20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops&&K20 sp tabStops/K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK??KK9K?XK K))K AWKKKK  .K K K KK KKK