{File name: <Workstation>mc>Refill.mc Description: Instruction buffer refill microcode for Dandelion, Author: R. Garner, Created: May 23, 1979, Last Edited: Amy Fasnacht, 31-Aug-83 18:00:50 Move IOPage for larger VM Last Edited: Daniels, 14-Jun-83 16:23:22 traps for new instruction set Last Edited: Daniels, 2-Jun-83 11:47:24 FixRefillTrap to handle Jump across page correctly Last Edited: Amy Fasnacht, 12-Jan-83 14:19:20 Delay ClrIntErr by one click Last Edited: Amy Fasnacht, April 23, 1982 1:49 PM Add conditional assembly for MagTape Sandman, November 10, 1981 8:13 AM New Instruction Set, Last Edited: R. Garner, April 11, 1980 4:20 PM, Last Edited: Sandman, January 9, 1981 3:19 PM, Last Edited: Johnsson, October 12, 1980 3:01 PM} {Assembly Configs: Config# File 0 Mesa.db 1 RavenMesa.db 2 TridentMesa.db 3 TridentRavenMesa.db 4 MagTapeMesa.db 5 TridentMagTapeMesa.db} {Overview: An instruction buffer refill is necessary whenever there are not 3 bytes in the IB. This is so all mesa opcodes can execute without finding the buffer empty. When an IBDisp is executed control goes to location 400'x if the buffer was if empty and 500'x if not empty (1 or 2 bytes). Two words (4 bytes) or one word (2 bytes), respectively, are fetched. IBPtr←0 is assumed since we are not jumping to the odd byte. RefillE is entered when the instruction buffer is empty. In this case, two words must be fetched. The PC already points to first of the two words to be fetched. A page cross test is made of PC-1 to see if the PC has already been incremented across a boundary. If we have crossed (PgCross=1), then the PC is remapped and UvPCsplit is updated. uPCCross remains false since we are irrevocably into the next page. Next we go to the RefillNE code to fetch the second word. If this code detects a page crossing, then uPCCross is set to true. Note that control can come to RefillE (or through MInt first) and the PC can be pointing to the second word of a page. "|||" indicates a page boundary and "↑" the location of the PC in the following pictures: | | | | ||| | | | | to 1st byte of page ↑ | | | | ||| | | | | to next to last byte ↑ RefillNE is entered when the instruction buffer contains 1 or 2 bytes. In this case one additional word must be fetched. The PC still points at the word which contains the 1 or 2 bytes in the IB, so PC+1 must be used to fetch the next word. A page cross test is made of this PC+1 to see if we will cross a boundary. If we will cross (PgCross=1), then a mapped version of UvPChigh+0FF is used to fetch the first word of the next page, but PC itself is not updated. This approach saves a click in the jump instructions: If the PC were actually updated and the last word of the page contained a jump instruction, the (page part of) the PC would be wrong if the jump were taken. Thus, jumps would have to always test a flag indicating the PC were wrong. The approach taken here is to set a flag (uPCCross) and the next time we do a refill we update the PC first. The flag must still be checked if the PC will be used before the refill code is reached. (This can occur in Traps (i.e. PageFaults), Xfer, Process, and Interrupts). Due to the approach outlined below, the flag checking in the Interrupt case occurs naturally. AlwaysIBDisp is necessary so that we won't trap again. If PgCross in c1 is true, then the AlwaysIBDisp of c2 is cancelled. Note that control can come to RefillNE (or MInt first) and the PC can be pointing to the second word of a page. | | | | ||| 2 bytes remaining ↑ | | | | ||| 1 byte remaining ↑ Register Useage: uPCCross Indicates that the buffer contains bytes from two different pages. When a hardware Refill Interrupt occurs and uPCCross is true (and the PC is still not on the last word of a page), the PC is updated to point to the next page before the buffer is refilled. This approach guarantees that all instructions will see the correct PC relative to the first byte of the instruction. Thus, the PC (in particular the real page bits of the PC) will be correct for Jumps, Traps (such as PageFault), and Interrupts (naked notify process switches). However, the Process and Xfer instructions must check uPCCross (see below). Before the Xfer & Process instructions save PC away, they must check uPCCross. If uPCCross is true & the PC is not pointing to the last word of a page (i.e., the Xfer or Process opcode was not single byte), then the virtual page must be incremented (by 0FF) to point to the next page. Note that the MInt bit is not reset The buffer refill code of Xfer, Jump, & Process instructions should clear uPCCross. MInt, however, should not be cleared (too much work to check whether its OK to be cleared). Not clearing of MInt can result in the state of MInt being set but there are not enabled pending wakeups. L0 Holds map update return point for Empty, Non-Empty, or Jump refill. L1 At RTrapFix, if L1=L1.Refill and L0=L0.JRemap, then L2 should be dispacthed on to update the stack. If L0=L0.ERefill or L0=L0.NERefill there is no stack pointer fixup. If L1=L1.Refill & L0=L0.NERefill & uPCCross is true, then save state, do not take the Page Fault, and continue executing with a non-filled instruction bufffer. Report the page fault only if an IBEmptyTrap occurs.} {***************************************************************************** Instruction buffer refill *****************************************************************************} {Timing: 1 click - buffer not empty, no page cross, 2 clicks - buffer empty, no page cross, 4 clicks - buffer not empty, page cross 6 clicks - buffer empty, page cross bewteen 2 fetched words 5 clicks - buffer empty, page cross not between 2 fetched words map update adds 2 clicks to the page cross cases} {Both types of refill do a push-pop on the stackP. This saves 2 instructions in the Jump code.} {Buffer Empty Refill. Control goes from NoRCross to RefillNE since RefillE+1 does not contain an IBDisp.} RefillE: MAR ← [rhPC, PC], PC ← PC-1, L0 ← L0.ERefill, c1, at[400]; PC ← PC+1 {restore}, push {dummy}, DISP2[NoRCross], c2; {Buffer Not Empty Refill.} OpTable: {"Noop" location of Instruction Dispatch table} RefillNE: MAR ← [rhPC, PC + 1], push {dummy}, c1, at[OpcodeBase]; AlwaysIBDisp, L0 ← L0.NERefill.Set, DISP2[NoRCross], c2; {The "pop"'s are used by the Jump exit code.} NoRCross: IB ← MD, uPCCross ← 0, pop, DISPNI[OpTable], c3, at[0,4,NoRCross]; RCross: Q ← 0FF + 1, {pop done at UpdatePC} GOTO[UpdatePC], c3, at[2,4,NoRCross]; {***************************************************************************** Refill Remap Routines *****************************************************************************} { Entry: T old real PC Q[0-7] page displacement to be added to virtual PC Q[8-15] ignored PC[0-7] ignored PC[8-15] valid location within new page pc16 must be valid Exit: UvPCpage new virtual PC page TT new virtual PC page rhTT UvChigh PC new real PC value Q new real PC value } UpdatePC: UrPC ← T ← PC, pop, c1, at[0F,10]; TT ← UvPCpage, L1 ← L1.Refill, c2; rhTT ← UvChigh, c3; RReMap: Map ← TT ← [rhTT, TT + Q], L0Disp, c1; TT ← TT and ~0FF, DISP4[ECross], c2; {Buffer Empty Refill page cross OR PCCross flag true. Remap the PC (which points to 1st or 2nd word of page). If we are doing an empty refill, return to the Empty Refill code at NoRCross. If we are updating the PC because PCCross is true, return to Refill-interrupt code.} ECross: PC ← MD, rhPC ← MD, XRefBr, c3, at[L0.ERefill,10,ECross]; ERedo: MAR ← Q ← [rhPC, T+0], Xbus ← uPCCross, XRefBr, BRANCH[EMUD, $], c1, RMFRet[L0.ERefill]; PC ← Q, UvPCpage ← TT, push, BRANCH[NoRCross, PageCrossed], c2; EMUD: Rx ← PC, CANCELBR[RCMapFix] c2; {Buffer Not Empty Refill page cross. Fetch the first word of the next page and do NOT Remap PC. Set PCCross true and the Refill-Interrupt hardware bit. Dispatch on the IB (as in the RefillNE code).} NECross: Rx ← MD, rhRx ← MD, XRefBr, c3, at[L0.NERefill,10,ECross]; NERedo: MAR ← [rhRx, 0+0], MesaIntRq, BRANCH[NEMUD, $], c1, RMFRet[L0.NERefill]; AlwaysIBDisp, c2; IB ← MD, uPCCross ← (~T xor T), DISPNI[OpTable], c3; NEMUD: uPCCross ← (~T xor T), CALL[RCMapFix] c2; {Jump Cross Remap. Remap the PC (which can point to any word of a page). uPCCross should remain unaltered in case the page cross test at Jgo is true.} JCross: PC ← MD, rhPC ← MD, XRefBr, c3, at[L0.JRemap,10,ECross]; JRedo: MAR ← Q ← [rhPC, T+0], BRANCH[JMUD, $], c1, RMFRet[L0.JRemap]; PC ← Q, Xbus←0, L2Disp, XC2npcDisp, c2; IB ← MD, UvPCpage ← TT, DISP4[JPtr1Pop0, 2], c3; JMUD: Rx ← PC, CALL[RCMapFix] c2; {***************************************************************************** Refill-Mesa Interrupt *****************************************************************************} {Timing: 3 clicks - uPCCross=0 & no enabled interrupts (uPCCross was reset by Xfer or Jump) 3 clicks - uPCCross=1 & haven't crossed boundary & no enabled interrupts 6 clicks - uPCCross=1 & across boundary & no enabled interrupts (PC updated} {The MInt hardware bit is only a hint that there may be a pending interrupt. uWDC & uWP should always be checked for the truth. MInt can be true in the following cases: a. An IO task has set it true while setting a bit in uWP (but it didn't look at uWDC) b. The Page Cross Refill code sets it true when the PC is in the last word of a page. (uPCCross=1) MInt remains on even after uPCCross has been reset by someone. c. On an IBEmtpy trap, MInt will be true. MInt should be cleared by the emulator in the same click that uWP is sampled (never in a click following the sampling of uWP, since an io task may set uWP bits after we've cleared MInt & decided there are no wakeups pending--The new uWP bits will not be seen until another io task sets MInt again!!) IO devices should set MInt in the same click in which they set uWP bits. (never in a click preceeding the seting of uWP since the emulator could clear MInt, decide there are no pending interrupts, and then the io task sets uWP bits--These bits will not be seen until another io task sets MInt again!). IO microcode does not have to check whether uWDC is zero when it sets MInt, since the code below will check.} {MInt is set. If uPCCross is true, then remap the PC only if the PC is not on last word of page. After remapping the PC, reset uPCCross and continue. If uPCCross is true and the PC is still on the last word of a page, then resave the state and continue. If uPCCross is false, check if WDC=0 and WP#0: if there are not interrupts, then continue on as if nothing had happened. If there are interrupts, go off and process them. Note that while uPCCross is true (for 1 or 2 instructions), interrupts are not checked for. However, if a Jump or Xfer occurs, interrupts will be checked at the end of them.} MInt0: T ← 0{buffer empty}, Xbus ← uPCCross, XRefBr, GOTO[MInt], c1, at[600]; MInt1: T ← T xor ~T{not empty}, Xbus ← uPCCross, XRefBr, GOTO[MInt], c1, at[700]; MInt: BRANCH[$, Crossing], c2; [] ← uWDC, NZeroBr, c3; [] ← uWP, ZeroBr, BRANCH[$, NoWakeupsc2], c1; ClrIntErr, BRANCH[Wakeups, NoWakeups], c2; Wakeups: Rx ← pInt, push, GOTO[Wakeupsx], c3; NoWakeupsc2: ClrIntErr, CANCELBR[$, 1], c2; NoWakeups: GOTO[IgnoreInt], c3; Wakeupsx: STK ← TOS, pop, GOTO[SaveRegs] {in Process.mc}, c1; {uPCCross is true. Check if we are still on last word of page. If not, go update the PC to point to the next page. T contains 0 if buffer is empty, -1 otherwise. This is put back into uPCCross. If buffer is not empty control goes from ECross to PageCross which ignores the first word fetched.} Crossing: [] ← PC + 1, PgCarryBr, c3; uCrSaveTOS ← TOS, L0 ← L0.ERefill, BRANCH[$, NotAcross], c1; uPCCross ← T, push{popped by RCross}, GOTO[RCross], c2; {We haven't crossed the boundary yet. Save state again, keep uPCCross true.} NotAcross: T ← LShift1 PC, Cin←pc16,, c2; uCrSavePC ← T, Cin←pc16 {restore}, GOTO[SaveStateR], c3; SaveStateR: T ← ~ErrnIBnStkp, c1; AlwaysIBDisp, c2; uCrSaveStkp ← T, DISPNI[OpTable], c3; {We've crossed the page boundary & updated the PC to point to the next page. Zero uPCCross. We know the buffer is not empty, but we also know it is not full so we exit through RefillNE.} PageCrossed: uPCCross ← 0, pop, GOTO[RefillNE], c3, at[1,4,NoRCross]; IgnoreInt: NxtInstc1: Noop, c1; IBDispOnly: IBDisp, c2; DISPNIonly: DISPNI[OpTable], c3; {***************************************************************************** Microcode Traps *****************************************************************************} {Control comes to location 0 with EKErr values of: 0 CSParErr 1 Emulator virtual address out of range OR Emulator double bit memory error 2 Stack overflow or underflow 3 IBEmpty Error. Smaller values of EKErr have priority over the larger. At least one additional emulator click can execute after the one which error'd (except IBEmpty error--see below). If EKErr=1 and MStatus[8]=1, then an emulator double bit memory error occured. If EKErr=0, we will be executing at the Kernel task level. Since ClrIntErr zeroes the MInt bit in addition to the errors register, it is set again in case there was a pending interrupt. uPCCross is zero'd.} ErrTrap: T ← RRot1 ErrnIBnStkp, ClrIntErr, CANCELBR[$,0F] {must be c1}, c1, at[0]; [] ← T LRot0, XwdDisp, L1←L1.None, c2; uPCCross ← 0, G ← 0, MesaIntRq, DISP2[ErrType], c3; ErrType: IfGreater[Config, 1, , SkipTo[NotTrident]]; IfEqual[Config, 4, SkipTo[NotTrident],]; RCnt ← 0F {stop disk on CSParErr}, GOTO[CSParErr], c1, at[0,4,ErrType]; SkipTo[AnyDisk]; NotTrident! KCtl ← 0 {stop disk on CSParErr}, GOTO[CSParErr], c1, at[0,4,ErrType]; AnyDisk! Xbus ← MStatus, XLDisp, GOTO[EmuMVErr], c1, at[1,4,ErrType]; StackErr: T ← sStackError, GOTO[Trapc2], c1, at[2,4,ErrType]; TT ← uCrSavePC + 0, Cin←pc16, LOOPHOLE[niblTiming], GOTO[IBEmptyErr], c1, at[3,4,ErrType]; EmuMVErr: uFaultParm0 ← TT xor ~TT, BRANCH[EmuVirtErr, EmuMemErr, 1], c2; EmuVirtErr: T ← qPageFault, c3; Q ← L xor ~L, push, c1; GOTO[Faultc3], c2; EmuMemErr: T ← sHardwareError, GOTO[Trapc1], c3; CSParErr: IfGreater[Config, 1, , SkipTo[NotTrident2]]; IfEqual[Config, 4, SkipTo[NotTrident2], ]; KCtl ← RCnt LRot12, c2; {Trident} KCmd ← U0400, c3; {Trident} Noop, c1; {Trident} NotTrident2! T ← LShift1 0B6, c2; CSErrSpin: rhTT ← IOPageHigh, c3; TT ← uIOPage, c1; Noop, c2; Noop, c3; MAR ← [rhTT, TT+40], c1; MDR ← T + T, {0B6*4 = 1330B = 728D} CANCELBR[CSErrSpin,0], c2; {IBEmpty Trap: Control comes to IBEmpty Trap if an "←ib, ←ibNA, ←ibLow, or ←ibHigh" were executed on an empty buffer (left empty by the NERefill code when the PC is on the last word of a page). It is (correctly) assumed that these ib accesses do not occur in c2 or c3 of the last click of a Mesa instuction, implying that the trap always occurs in the click after the one which error'd. Note that the IBDisp refill and MInt traps are overridden by the IBEmpty Trap. The PC, pc16, TOS, and stackP are restored to the values saved at RefillTrap or NotAcross, then a Page fault is caused to the new PC page} IBEmptyErr: T ← UvPCpage, Ybus ← TT, YDisp, c2; stackP ← uCrSaveStkp, BRANCH[IBEEa, IBEEb, 0E], c3; IBEEa: TOS ← uCrSaveTOS, Cin←pc16, GOTO[IBEEc], c1; IBEEb: TOS ← uCrSaveTOS, GOTO[IBEEc], c1; IBEEc: PC ← RShift1 uCrSavePC, c2; rhTT ← UvChigh, c3; TT ← T + 0FF + 1, L3 ← L3.rhTT.TT, GOTO[IBEmptyTrap], c1; {***************************************************************************** Refill Page Fault Fixup *****************************************************************************} {Fix For Refill Trap: If L1=L1.Refill and L0=L0.JRemap, restore PC from Q. If L0=L0.ERefill or L0=L0.NERefill there is no stack pointer fixup. If L1=L1.Refill & L0=L0.NERefill & uPCCross is true, then save state, do not take the Page Fault, and continue executing with a non-filled instruction bufffer. Report the page fault only if an IBEmptyTrap occurs.} FixRefillTrap: L0Disp, c1, at[L1.Refill,10,RTrapFix]; Xbus ← uPCCross, XRefBr, BRANCH[RFix, JFix, WhichFixType], c2; JFix: PC ← Q, L2Disp, CANCELBR[$], c3; UvPCpage ← TT, pop, DISP4[JFPop0,3], c1; uPCCross ← 0, push, GOTO[JFa], c2, at[3,10,JFPop0]; uPCCross ← 0, GOTO[JFa], c2, at[7,10,JFPop0]; uPCCross ← 0, GOTO[JFa], c2, at[0B,10,JFPop0]; uPCCross ← 0, pop, GOTO[JFa], c2, at[0F,10,JFPop0]; JFa: TOS ← STK, L3Disp, GOTO[LowTT], c3; {Save TOS, PC, pc16, and stackP. Continue executing with a non-filled buffer. Control will either go to the IBEmpty Trap location (if the instruction crosses the page boundary), or an Xfer, Jump, or Process instruction will be executed (if control goes elsewhere). (If control goes to IBEmpty trap, uPCCross will be reset. If control goes to Xfer or Process, they will still store the correct PC away because the PC still points to the last word of the page. If control goes to Jump, uPCCross will be reset as usual)} RFix: uCrSaveTOS ← TOS, BRANCH[RefillFault, SaveState], c3; RefillFault: PC ← UrPC, c1; UvPCpage ← TT, L3Disp, GOTO[TrapFixDone], c2; SaveState: pop{pushed at RTrap}, GOTO[NotAcross], c1;