DIRECTORY Basics USING [CompareINT, Comparison], TamarinOps USING [FieldDescriptor, FourBytes, Inst, LRBformat, LRRBformat, ProcessorRegister, QRformat, RJBformat, RRformat, TrapIndex, TwoBytes, TwoHalves, Word, ZerosByte, ZerosWord], TamarinOpsUtils USING [AddDelta, ByteToCard, CardToFieldDescriptor, CardToWord, HalfToCard, TrapIndexToBytePC, VanillaAdd, VanillaSub, WordToBytes, WordToCard, WordToHalves, WordToInt, XopToBytePC], IO USING [PutFR, rope], MonkeyHeart USING [ALUOps, ChangeLogger, Control, DoALUOp, FieldUnit, InstDoneProc, InstStartProc, IOChangeProc, MemChangeProc, NoFault, OutsideEnvelope, Processor, RegChangeProc, RegPlus, RegToWord, StackPlus, TrapPC, WillPushOverflow, WordToReg], MonkeyLiver USING [], MonkeyTweaker USING [OpcodeHandler, OpcodeRegistry, OpcodeRegistryEntry, OpcodeRegistryRep]; MonkeyLiverImpl: CEDAR PROGRAM IMPORTS Basics, TamarinOpsUtils, HandCodingUtil, IO, MonkeyHeart EXPORTS MonkeyLiver, MonkeyTweaker = BEGIN OPEN TamarinOps, TamarinOpsUtils, MonkeyHeart; CARD: TYPE = LONG CARDINAL; careful: BOOL _ TRUE; Register: PUBLIC PROC [inst: Inst, handler: MonkeyTweaker.OpcodeHandler, data: REF _ NIL] = { IF opcodeRegistry = NIL THEN opcodeRegistry _ NEW[MonkeyTweaker.OpcodeRegistryRep _ ALL[MonkeyTweaker.OpcodeRegistryEntry[NIL, NIL]]]; opcodeRegistry[inst] _ [handler, data]; flagsArray[inst].callMesa _ TRUE; }; opcodeRegistry: MonkeyTweaker.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; flags: InstFlags _ flagsArray[inst]; cycles: NAT _ flags.cycles; alphaBetaGammaDeltaZ: CARD _ WordToCard[rest]; stackEffect: INTEGER _ flags.stackEffect; 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; processor.stats.regBusyCycles _ processor.stats.regBusyCycles + 1; }; 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; 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] = { 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 { 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] = 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] = INLINE { 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] = { 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 => { processor.rescheduleRequested _ TRUE; }; ioResetRequest => { processor.resetRequested _ TRUE; }; ENDCASE; RETURN [NoFault]; }; CalcReg: PROC [aux,opt: BOOL, regX: [0..15], dest: BOOL _ FALSE] RETURNS [reg: ProcessorRegister] = INLINE { IF NOT opt THEN RETURN [IF aux THEN RegPlus[euAux, regX] ELSE StackPlus[regL, regX]]; 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] = { rtnPC _ thisPC; stackEffect _ 0; IF processor.trapsEnabled THEN SELECT TRUE FROM MonkeyHeart.WillPushOverflow[processor] => 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]; }; 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: BOOL _ FALSE] = 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 StackSize 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: MonkeyTweaker.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; }; dADDQB, dSUBQB => { [resWord, trapCode] _ DoALUOp[ processor, topOfStack, rest, IF inst = dADDQB THEN SAdd ELSE SSub, ALUCondOver]; GO TO aluDone; }; 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 MonkeyHeart.WillPushOverflow[processor] THEN CauseTrap[IFUStackOverflowTrap] ELSE newPC _ CardToWord[TamarinOpsUtils.XopToBytePC[inst]]; control _ doAbort; GO TO done; }; dJ1 => GO TO noCheck; dJSD => { newPC _ topOfStack; IF WillEUStackOverflow[] THEN GO TO euOverflow; GO TO jump; }; dJSR => { 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 => { resReg _ RegPlus[euConstant, ORD[inst] MOD 16]; 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; }; dLIP => { GO TO storeReg }; dSIP => { reg: ProcessorRegister _ LOOPHOLE[alphaZ]; IF processor.userMode THEN GO TO modeFault; [] _ WillEUStackOverflow[]; IF reg > euLast THEN cycles _ cycles + 4; resReg _ reg; SELECT reg FROM ifuEldestPC => { entries: NAT _ processor.stackEntries+1; eldest: NAT _ processor.eldest; IF MonkeyHeart.WillPushOverflow[processor] THEN { CauseTrap[IFUStackOverflowTrap]; GO TO done; }; processor.stackEntries _ entries; eldest _ (eldest + (MonkeyIFUStackSize - 1)) MOD MonkeyIFUStackSize; 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 => { 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; }; dIODA, dIOD, dION => { 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 => { 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]; 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] THEN { SIGNAL OutsideEnvelope["Storing into readonly register not permited!"]; resWord _ CardToWord[ORD[resReg]]; 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; dSHDL => { resWord _ RegFetch[sM1]; GO TO storeField; }; dSHDR => { resWord _ topOfStack; topOfStack _ 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[TamarinOpsUtils.XopToBytePC[inst]]; SELECT TRUE FROM MonkeyHeart.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 MonkeyHeart.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 processor.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]]]; }; storeRegIFU => { IF WillEUStackOverflow[] THEN CauseTrap[EUStackOverflowTrap] ELSE RegStore[resReg, resWord]; }; storeReg => { 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: BOOL _ FALSE, predictJump: BOOL _ FALSE, mod16: BOOL _ FALSE, callMesa: BOOL _ FALSE, 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: NAT _ ORD[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. 'MonkeyLiverImpl.mesa Copyright c 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) November 5, 1986 4:52:46 pm PST McCreight, January 22, 1986 12:16:44 pm PST Last Edited by: Ross February 3, 1987 11:36:02 am 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 "_ MonkeyLiverImpl.careful _ FALSE" to turn this checking off. Some checking is always present because Monkey 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 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 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 the following IFU regs: ifuEldestPC, ifuEldestL, ifuYoungestPC, ifuYoungestL. 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]. SELECT regX FROM IN EUConstIndex => RETURN [RegPlus[euConstant, regX]]; 12 => RETURN [regS]; 13 => RETURN [sM1]; ENDCASE; 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. 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. IFUStackOverflow has precedence over other traps 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: [S] _ [S] + AlphaBetaGammaDelta + carry; trap on overflow, clear carry 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: PC_InstTrap[KFC]; set kernel mode; disable traps; S_S+1 Effect: noop Effect: PC_[S]; S_S-1 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: [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 MonkeyIFUStackSize; 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]; 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 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 + carry; trap on overflow, clear carry 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. 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: [S-1]_FieldUnit[[S],[S-1],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 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 resReg can be an IFU register, so don't use FastRegStore here. resReg must not be a special IFU register, so we use FastRegStore here. ccodeK m=HK3K+K5Kk K&K KKK K KI\KheadK* @K"K 6KKK KK n:]K--K ##iK''K!KK/3K>6KKK((K33::K11--K==33KDDK--K$$KK20 sp tabStops.K )99KTT20 sp tabStops MK20 sp tabStopsK<<K<KK22KKKKKK (BK K,, K K22KKKKKK;;K!LKK``K@XK:>KKKJKK KffK@X K88 K,,K 4KKKKKK"UKKKK+OKEE K)) KFFKK20 sp tabStops %KK KKK KK66K lKK%%KEKK%%K6KKKKK--HKK77KK )KKK %KK20 sp tabStopsK20 sp tabStops20 sp tabStops20 sp tabStops20 sp tabStops**K20 sp tabStops00K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStops#K::KK:K20 sp tabStops6620 sp tabStops20 sp tabStops&(K20 sp tabStops 2HK20 sp tabStopsK20 sp tabStops((K20 sp tabStopsKKK:K20 sp tabStops6620 sp tabStops20 sp tabStops&(K20 sp tabStops 2HK20 sp tabStops20 sp tabStops KAA    JK20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStops((K20 sp tabStopsKK  E4K77KAAK 0  (K  #K39KKKK((KDDK1K@@K2DKKK]] K,,K 7NKKKK 0KK20 sp tabStops""KKK LKKKKK//KKPPKK20 sp tabStops 20 sp tabStops20 sp tabStops K20 sp tabStops((K20 sp tabStops K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops&&K20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops%N20 sp tabStopsK20 sp tabStops K20 sp tabStops K20 sp tabStopsK20 sp tabStops%K20 sp tabStops K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops K20 sp tabStops K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops22K20 sp tabStops""K20 sp tabStops K20 sp tabStopsK20 sp tabStops""K20 sp tabStops20 sp tabStops20 sp tabStopsK20 sp tabStops!!K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops""K20 sp tabStops K20 sp tabStops20 sp tabStopsK20 sp tabStops!!K20 sp tabStops((K20 sp tabStops K20 sp tabStops20 sp tabStopsK20 sp tabStops22K20 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 tabStops20 sp tabStops K20 sp tabStopsEEK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsEEK20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStopsFFK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops K20 sp tabStops20 sp tabStops  K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 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 K??20 sp tabStops(*K20 sp tabStops $K20 sp tabStops7;K20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops K20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStops /K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops''K20 sp tabStops /K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStopsMMK20 sp tabStops!!K20 sp tabStops/K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops+K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops+K20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops::K20 sp tabStops*K20 sp tabStopsDDK20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 sp tabStops!!K20 sp tabStops20 sp tabStops 20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops20 sp tabStops**K20 sp tabStopsK20 sp tabStopsK20 sp tabStopsK20 sp tabStops&c;K20 sp tabStops0K20 sp tabStops!>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 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 tabStops 0BK20 sp tabStops20 sp tabStops K20 sp tabStopsK20 sp tabStops K20 sp tabStopsK20 sp tabStops20 sp tabStops 20 sp tabStopsK20 sp tabStopsK20 sp tabStops *K +K##20 sp tabStops)K20 sp tabStops44K20 sp tabStops KK (K)1K K KK!!K-DKK((K20 sp tabStops K20 sp tabStops20 sp tabStops"K20 sp tabStops7=K20 sp tabStops. @K20 sp tabStops K20 sp tabStops20 sp tabStops20 sp tabStops"K20 sp tabStops:@K20 sp tabStops44K20 sp tabStops K20 sp tabStops20 sp tabStops20 sp tabStops"K20 sp tabStops9?K20 sp tabStops0 BK20 sp tabStops K20 sp tabStopsKK20 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%A20 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 gK20 sp tabStops20 sp tabStops K20 sp tabStops!!20 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 tabStops55K20 sp tabStopsK20 sp tabStops//K20 sp tabStops *K20 sp tabStops K20 sp tabStopsK20 sp tabStopsK20 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(IK20 sp tabStops33K20 sp tabStops'G20 sp tabStops20 sp tabStops)+K20 sp tabStops +20 sp tabStops/1K20 sp tabStops +20 sp tabStops);K20 sp tabStopsAGK20 sp tabStops "K20 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 tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops#K20 sp tabStopsY]K20 sp tabStops20 sp tabStopsK20 sp tabStops>>20 sp tabStopsK20 sp tabStops#K20 sp tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStopsGG20 sp tabStopsK20 sp tabStops#K20 sp tabStops#K20 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 tabStopsK20 sp tabStops20 sp tabStops K20 sp tabStops&&K20 sp tabStops/K20 sp tabStopsK20 sp tabStops20 sp tabStopsK20 sp tabStops$$20 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 KKKKKK  *K K K KK     CKKKK$$KaKK 2.KKKKKK,,K&&KKKKKEEKKKMKKKKKKKKKKbX