DIRECTORY Basics USING [BITAND], TamarinOps USING [Byte, bytesPerWord, FieldDescriptor, FourBytes, Inst, OnesWord, ProcessorRegister, TrapIndex, Word, ZerosWord], TamarinOpsUtils USING [BytePCToWordAddress, ByteToCard, BytesToHalf, BytesToWord, CardToWord, CardToByte, DoubleWordShiftLeft, TamAnd, TamNot, TamOr, TamXor, HalfToCard, IntToWord, SingleWordShiftLeft, SingleWordShiftRight, TrapIndexToBytePC, VanillaAdd, VanillaSub, WordAddressToBytePC, WordToBytes, WordToCard, WordToInt], MonkeyHeart USING [ALUHelper, ALUOps, ChangeLogger, Control, InstBuffer, InstBufferRep, MicroInstruction, NoFault, NoTLBmiss, Processor, ProcessorRep, TrapPC], PrincOpsUtils USING [LongCopy], Rope USING [ROPE]; MonkeyHeartImpl: CEDAR PROGRAM IMPORTS Basics, TamarinOpsUtils, MonkeyHeart, PrincOpsUtils EXPORTS MonkeyHeart = BEGIN OPEN TamarinOps, TamarinOpsUtils, MonkeyHeart; CurrentProcVersion: [0..255] _ 0; InstBufferIndex: TYPE = [0..64); WordsInBuffer: NAT _ 4; MaxMask: CARDINAL _ WordsInBuffer*bytesPerWord - 1; MaskInstBufferIndex: PROC [index: CARDINAL] RETURNS [InstBufferIndex] = INLINE { RETURN [LOOPHOLE[Basics.BITAND[index, MaxMask]]]; }; NewProcessor: PUBLIC PROC [logger: ChangeLogger] RETURNS [p: Processor] = { p _ NEW[ProcessorRep]; p.logger _ logger; InitProcessor[p]; }; InitProcessor: PROC [p: Processor] = { p.trapsEnabled _ FALSE; p.resetRequested _ FALSE; p.ccResult _ FALSE; p.instBuffer _ NEW[InstBufferRep[WordsInBuffer]]; p.version _ CurrentProcVersion; p.stats _ []; }; FlushInstBuffer: PUBLIC PROC [processor: Processor] = { instBuffer: InstBuffer = processor.instBuffer; delta: INT = WordToInt[instBuffer.nextPC] - WordToInt[instBuffer.basePC]; IF delta > 0 THEN { validBytes: INT = instBuffer.validWords*bytesPerWord; IF validBytes > delta THEN instBuffer.bytesDiscarded _ instBuffer.bytesDiscarded + validBytes-delta; }; instBuffer.validWords _ 0; instBuffer.forcedEmpty _ instBuffer.forcedEmpty + 1; }; InstructionFetch: PUBLIC PROC [control: Control, processor: Processor] RETURNS [inst: Inst, rest: Word] = { thisPC: Word _ processor.regs[nextPC].data; newPC: Word _ thisPC; rtnPC: Word; p: Processor = processor; -- for a short name cycles: CARDINAL _ 0; rCycles: CARDINAL _ 0; initCycles: INT _ processor.stats.cycles; nBytes: CARDINAL _ 0; instBuffer: InstBuffer _ p.instBuffer; instBufferPtr: LONG POINTER; max: CARDINAL = instBuffer.max * bytesPerWord; -- max bytes in buffer word: Word; wordAddr: Word; valid: InstBufferIndex _ 0; -- valid bytes in inst buffer AFTER newPC rbi: [0..bytesPerWord); tx: TrapIndex _ NoFault; CauseTrap: PROC [code: TrapIndex] = { rtnPC _ thisPC; newPC _ TrapPC[code]; control _ doAbort; cycles _ cycles + 4; -- a rough guess tx _ code; IF code = StackOverflowTrap THEN p.stats.stackOver _ p.stats.stackOver + 1; }; ForceBufferEmpty: PROC = INLINE { instBuffer.validWords _ 0; instBuffer.forcedEmpty _ instBuffer.forcedEmpty + 1; IF valid # 0 THEN { instBuffer.bytesDiscarded _ instBuffer.bytesDiscarded + valid; valid _ 0; }; }; FlushInstWord: PROC = INLINE { vw: CARDINAL _ instBuffer.validWords - 1; instBuffer.basePC _ IntToWord[WordToInt[instBuffer.basePC] + bytesPerWord]; instBuffer.validWords _ instBuffer.validWords - 1; IF vw # 0 THEN TRUSTED { PrincOpsUtils.LongCopy[from: @instBuffer[1], nwords: instBuffer.validWords*SIZE[Word], to: @instBuffer[0]]; }; instBuffer.validWords _ vw; }; IF p.resetRequested THEN { thisPC _ newPC _ TrapPC[ResetTrap]; InitProcessor[p]; ForceBufferEmpty[]; p.instBuffer.forcedEmpty _ instBuffer.forcedEmpty; instBuffer _ p.instBuffer; initCycles _ cycles _ 0; control _ nextOpcode; }; IF control = nextuInst THEN RETURN [LOOPHOLE[0, Inst], ZerosWord]; -- am inside of a uInst sequence and already have uPCa & uPCb so bug out. TRUSTED { instBufferPtr _ @instBuffer[0]; }; IF newPC = instBuffer.nextPC THEN { used: INT _ WordToInt[newPC] - WordToInt[instBuffer.basePC]; valid _ instBuffer.validWords*bytesPerWord; IF used > 0 AND used < valid THEN valid _ valid - used ELSE valid _ 0; }; { start: InstBufferIndex _ 0; -- byte index in buffer of newPC IF valid = 0 THEN { [wordAddr, rbi] _ BytePCToWordAddress[[newPC]]; [word, tx, rCycles] _ p.memory.fetch[p, wordAddr, ZerosWord, ZerosWord]; cycles _ cycles + 2; IF tx # NoTLBmiss THEN {tx _ TLBmissTrap; GO TO trapEarly}; IF tx # NoFault THEN {tx _ PageFaultTrap; GO TO trapEarly}; IF rCycles # 0 THEN { cycles _ cycles + rCycles; }; valid _ bytesPerWord - rbi; instBuffer.validWords _ 1; instBuffer.basePC _ WordAddressToBytePC[wordAddr]; instBuffer[0] _ word; }; start _ WordToInt[newPC] - WordToInt[instBuffer.basePC]; IF start >= max THEN ERROR; word _ instBuffer[start/bytesPerWord]; inst _ LOOPHOLE[WordToBytes[word][start MOD bytesPerWord]]; nBytes _ p.mi[LOOPHOLE[inst, NAT] MOD 256].opLength; IF valid < nBytes THEN { IF instBuffer.validWords = instBuffer.max THEN FlushInstWord[]; [wordAddr, rbi] _ BytePCToWordAddress[[IntToWord[WordToInt[newPC] + valid]]]; [word, tx, rCycles] _ p.memory.fetch[p, wordAddr, ZerosWord, ZerosWord]; cycles _ cycles + 1; IF rCycles # 0 THEN { cycles _ cycles + rCycles; rCycles _ 0; }; IF tx # NoTLBmiss THEN {tx _ TLBmissTrap; GO TO trapEarly}; IF tx # NoFault THEN {tx _ PageFaultTrap; GO TO trapEarly}; start _ WordToInt[newPC] - WordToInt[instBuffer.basePC]; instBuffer[start/bytesPerWord + 1] _ word; instBuffer.validWords _ instBuffer.validWords + 1; valid _ valid + bytesPerWord; }; IF nBytes > 1 THEN TRUSTED { instBufferPtr: LONG POINTER TO PACKED ARRAY InstBufferIndex OF Byte _ LOOPHOLE[@instBuffer[0]]; SELECT nBytes FROM 5 => { rest _ BytesToWord[[ instBufferPtr[MaskInstBufferIndex[start+1]], instBufferPtr[MaskInstBufferIndex[start+2]], instBufferPtr[MaskInstBufferIndex[start+3]], instBufferPtr[MaskInstBufferIndex[start+4]] ]]; }; 4 => { rest _ BytesToWord[[ CardToByte[0], instBufferPtr[MaskInstBufferIndex[start+1]], instBufferPtr[MaskInstBufferIndex[start+2]], instBufferPtr[MaskInstBufferIndex[start+3]] ]]; }; 3 => { rest _ CardToWord[HalfToCard[BytesToHalf[[ instBufferPtr[MaskInstBufferIndex[start+1]], instBufferPtr[MaskInstBufferIndex[start+2]] ]]]]; }; 2 => { rest _ CardToWord[ByteToCard[ instBufferPtr[MaskInstBufferIndex[start+1]] ]]; }; ENDCASE => ERROR; }; newPC _ IntToWord[WordToInt[newPC] + nBytes]; valid _ valid - nBytes; instBuffer.nextPC _ newPC; p.stats.cycles _ p.stats.cycles+cycles; p.stats.instBufferCycles _ p.stats.instBufferCycles+cycles; cycles _ 0; EXITS trapEarly => CauseTrap[tx]; }; RETURN [inst, rest]; }; InstructionDecode: PUBLIC PROC [control: Control, processor: Processor, inst: Inst] RETURNS [mi: MicroInstruction] = { p: Processor = processor; -- for a short name upc: NAT; SELECT control FROM nextOpcode, doReturn => { RETURN [p.mi[LOOPHOLE[inst, NAT] MOD 256]]; }; nextuInst => { IF p.ccResult THEN upc _ p.nextuPCa ELSE upc _ p.nextuPCb; RETURN [p.mi[upc]]; }; doCall => { p.stats.calls _ p.stats.calls + 1; RETURN [p.mi[LOOPHOLE[inst, NAT] MOD 256]]; }; ENDCASE => ERROR; }; DoALUOp: PUBLIC PROC [processor: Processor, wordA,wordB: Word, op: ALUOps, trap: TrapIndex] RETURNS [res: Word, resCode: TrapIndex _ NoFault] = { carryOut, overflow, boundsCheck, illegalLisp: BOOL _ FALSE; carryIn: BOOL _ processor.euCarry; SELECT op FROM SAdd => { [res, ] _ WordCarryAdd[wordA, wordB, carryIn]; SELECT ALUHelper[wordA[0], wordB[0], res[0]] FROM a0b0c1, a1b1c0 => {overflow _ TRUE}; ENDCASE; }; UAdd => [res, carryOut] _ WordCarryAdd[wordA, wordB, carryIn]; SSub => { [res, ] _ WordCarryAdd[wordA, TamNot[wordB], NOT carryIn]; SELECT ALUHelper[wordA[0], wordB[0], res[0]] FROM a0b1c1, a1b0c0 => overflow _ TRUE; ENDCASE; }; USub => { [res, carryOut] _ WordCarryAdd[wordA, TamNot[wordB], NOT carryIn]; carryOut _ NOT carryOut; }; LAdd, LSub => { res _ IF op = LAdd THEN VanillaAdd[wordA, wordB] ELSE VanillaSub[wordA, wordB]; IF res[0] # res[1] OR res[1] # res[2] OR wordA[0] # wordA[1] OR wordA[1] # wordA[2] OR wordB[0] # wordB[1] OR wordB[1] # wordB[2] THEN illegalLisp _ TRUE }; VAdd => {res _ VanillaAdd[wordA, wordB]; carryOut _ carryIn}; VSub => {res _ VanillaSub[wordA, wordB]; carryOut _ carryIn}; And => {res _ TamAnd[wordA, wordB]; RETURN}; Or => {res _ TamOr[wordA, wordB]; RETURN}; Xor => {res _ TamXor[wordA, wordB]; RETURN}; BndChk => { boundsCheck _ WordCarryAdd[wordA, TamNot[wordB], TRUE].carryOut; res _ wordA; carryOut _ carryIn; }; ENDCASE => ERROR; resCode _ (IF (SELECT trap FROM ALUCondFalse => FALSE, ALUCondEZ => res = ZerosWord, ALUCondLZ => WordToInt[res] < 0, ALUCondLE => WordToInt[res] <= 0, ModeFault => TRUE, ALUCondNE => res = ZerosWord, ALUCondGE => WordToInt[res] >= 0, ALUCondGZ => WordToInt[res] > 0, ALUCondOver => overflow, ALUCondBC => boundsCheck, ALUCondIL => illegalLisp, ALUCondDO => FALSE, -- not yet implemented ALUCondNotOver => NOT overflow, ALUCondNB => NOT boundsCheck, ALUCondNI => NOT illegalLisp, ENDCASE => ERROR) THEN trap ELSE NoFault); IF resCode = NoFault THEN processor.euCarry _ carryOut; }; WordCarryAdd: PROC [wordA, wordB: Word, carryIn: BOOL] RETURNS [wordC: Word, carryOut: BOOL _ FALSE] = { cardA: CARD _ WordToCard[wordA]; cardB: CARD _ WordToCard[wordB]; cardC: CARD _ cardA+cardB; SELECT cardC FROM < cardA, < cardB => carryOut _ TRUE; ENDCASE; IF carryIn THEN {cardC _ cardC+1; IF cardC = 0 THEN carryOut _ TRUE}; wordC _ CardToWord[cardC]; }; FieldUnit: PUBLIC PROC [Left,Right: Word, fd: FieldDescriptor] RETURNS [out: Word] = { shifter: Word = DoubleWordShiftLeft[Left, Right, fd.shift]; mask: Word _ IF fd.mask >= 32 THEN OnesWord ELSE SingleWordShiftRight[OnesWord, 32-fd.mask]; IF fd.insert THEN { mask _ TamAnd[mask, SingleWordShiftLeft[OnesWord, fd.shift]]; out _ TamOr[TamAnd[mask, shifter], TamAnd[TamNot[mask], Right]]; } ELSE out _ TamAnd[mask, shifter]; }; OutsideEnvelope: PUBLIC SIGNAL [explanation: Rope.ROPE] = CODE; END. ุMonkeyHeartImpl.mesa Copyright ำ 1987 by Xerox Corporation. All rights reserved. Pilfered by Ross January 28, 1987 11:57:47 am PST Originally by Russ Atkinson (RRA) September 16, 1986 6:25:55 am PDT Last Edited by: Ross February 3, 1987 11:58:43 am PST Interface for Basic Tamarin machine. Borrowed heavily from LizardHeartImpl.mesa (notice even the name similarities). MonkeyLiver USING [InstructionExecute], The current version # of the processor, stored in StackedStatusWord.version max # of words allowed # of Tamarin words in the instruction buffer (power of two) Mask for valid bytes in the buffer Performs initialization common to NewProcessor and the reset handling. This routine flushes the instruction buffer. This occurs on a jump (forced or conditional), a trap, or call. All traps do trapsEnabled _ userMode _ FALSE. This routine was cribbed from LizardHeartImpl and simplified. -- **** Should add section for Stack UnderFlow (i.e., not present in machine) IF WillPushOverflow[p] THEN StackOverflow has precedence over other traps code _ StackOverflowTrap; ForceBufferEmpty forces the instruction buffer to be empty. It is a faster local version of FlushInstBuffer. Flushes one word from the inst buffer, shifting the words down so that instBuffer[0] corresponds to inst.basePC. We also update instBuffer.basePC forward one word, and instBuffer.validWords backwards one word. -- ** Start of Instruction Fetch (only trap which is a uInst trap is reset). All other traps occur at the opcode boundary. Force the machine to its reset state. The state we force it to here is highly speculative. Compute the number of valid bytes in the buffer. Fetch the next instruction byte in the instruction buffer, causing a refill if the instruction buffer is empty. Between instructions we have the following priority: EUStackOverflowTrap > RescheduleTrap > IFUPageFaultTrap -- **** Trap Handler should check OverFlow and UnderFlow IF p.trapsEnabled THEN SELECT TRUE FROM IsEUStackOverflow[WordToReg[p.regs[ifuS]]] => { There was an EU stack overflow caused by the previous instruction. This may be because a previous SIP or RETK instruction enabled traps. Should this ever really happen? tx _ EUStackOverflowTrap; GO TO trapEarly; }; p.rescheduleRequested => { Take a reschedule trap (which clears rescheduleRequested). p.rescheduleRequested _ FALSE; tx _ RescheduleTrap; GO TO trapEarly; }; ENDCASE; need to fill the inst buffer from scratch The memory takes two cycles to come back with the answer A TLB miss has occurred during an instruction fetch. Go freak out. A page fault has occurred during an instruction fetch. Go freak out. Account for the number of reject cycles. Also keep track of the next time that the ifu cache will be available. At this point we have at least one valid byte in the buffer. This is enough to determine the instruction and how many bytes must follow. nBytes _ p.opCodeMap[LOOPHOLE[inst, NAT]].size; We must make sure that we have enough bytes in the instruction buffer to execute the whole instruction. This simplifies things later, to be sure. Things are a little simpler because we know we are fetching a full word that will be appended onto the current valid stuff. We won't execute the next instruction unless we have a one line inst. buffer. Account for cache access time Account for the number of reject cycles. Also keep track of the next time that the ifu cache will be available. instBuffer.busyUntil _ p.ifuCache.sharedBase.busyUntil; A TLB miss has occurred during an instruction fetch. Go freak out. A page fault has occurred during an operand byte fetch. Go freak out. Move the four bytes from the fetched word into the instruction buffer. Move the bytes from the instruction buffer to rest. RRA: highest byte is first in stream, and so on Update the cycle count to allow EU cache to delay properly for busy bus Finally, we get to execute the instruction we fetched. [newPC, rtnPC, control] _ MonkeyLiver.Execute[p, thisPC, inst, rest]; Utility Routines for manipulating data (incomplete) Calculate the result word and carryOut Detarmine the returned condition code There is the possibility that the carry out should NOT be set if the returned code # ALUCondFalse. One of these days we should find out if this is true. The shifter output has the input double word shifted left by fd.shift bits The default mask has fd.mask 1s right-justified in the word fd.insert => clear rightmost fd.shift bits of the mask 1 bits in the mask select the shifter output 0 bits in the mask select bits from Right 1 bits in the mask select the shifter output WillPushOverflow: PUBLIC PROC [processor: Processor] RETURNS [BOOL] = { IF processor.stackEntries < IFUOverflow-1 THEN RETURN [FALSE] ELSE { SELECT processor.stackEntries FROM = IFUOverflow-1 => {}; < IFUStackSize => IF processor.trapsEnabled THEN SIGNAL OutsideEnvelope["IFU control stack too full with traps enabled"]; ENDCASE => SIGNAL OutsideEnvelope["IFU control stack has more than IFUStackSize entries"]; RETURN [processor.trapsEnabled]; }; }; สa˜codešœ™Kšœ<™˜>Kšœ ˜ K˜—K˜K˜—šŸ œžœžœ˜Kšœา™าKšœžœ˜)KšœK˜KKšœ2˜2šžœžœžœ˜KšœKžœ˜kK˜—Kšœ˜K˜—K˜šœ]™]K™K˜—šžœžœ˜Kšœ[™[Kšœ#˜#Kšœ˜Kšœ˜Kšœ2˜2Kšœ˜K˜K˜K˜K˜—šžœžœžœžœ˜DKš I˜I—šžœ˜ Kšœ˜K˜K˜—šžœžœ˜#K™0Kšœžœ3˜