{File name: Refill.mc Description: Instruction buffer refill microcode for Dandelion, Author: RXG , Created: May 23, 1979, Last Edited: JPM, 29-Jul-86 14:14:23 Add code for linkage from initialization routine. Last Edited: JPM, 11-Jul-86 9:17:43 Hard-wire address of DISPNIonly. Last Edited: JPM (per JGS), 8-Feb-85 12:42:00 Reset uPCCross at JRedo+2. Last Edited: JPM, 12-Dec-84 9:35:38 Fix dispatch at ErrTrap+1 to check proper bits. Last Edited: JPM, 6-Nov-84 11:34:45 Fix trap code at 0 (need to ClrIntErr in c1). Last Edited: JPM, 10-Oct-84 8:55:56 Fix page cross code. Last Edited: JPM, 28-Sep-84 8:39:02 Save IntStat at ErrTrap. Last Edited: DEG, 1-Sep-84 19:47:50 Add copyright notice. HGM, 2-Nov-83 19:29:01, Dicentra version Last Edited: AXD, 14-Jun-83 16:23:22 traps for new instruction set Last Edited: AXD, 2-Jun-83 11:47:24 FixRefillTrap to handle Jump across page correctly Last Edited: AEF, 12-Jan-83 14:19:20 Delay ClrIntErr by one click Last Edited: AEF, April 23, 1982 1:49 PM Add conditional assembly for MagTape JGS, November 10, 1981 8:13 AM New Instruction Set, Last Edited: RXG, April 11, 1980 4:20 PM, Last Edited: JGS, January 9, 1981 3:19 PM, Last Edited: RXJ, October 12, 1980 3:01 PM} { Copyright (C) 1980, 1981, 1982, 1983, 1984, 1985, 1986 by Xerox Corporation. All rights reserved.} {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, and PC is updated so that PC+1 will be correct. Note that this approach differs from Dandelion, where the PC is not updated. The Dandelion version sometimes saves a click in the jump instructions. On Daybreak, if the last word of the page contains a backwards jump instruction, the (page part of) PC would be wrong if the jump were taken. Thus, some jumps have to test a flag (uPCCross) indicating whether the PC is correct. The flag must also 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). This approach was chosen because the Dandelion code won't work with interrupts disabled by hardware. 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. 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 100) to point to the next page. The buffer refill code of Xfer, Jump, & Process instructions should clear uPCCross. 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 between 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 ← r0100, Xbus ← uPCCross, XRefBr, {pop done at UpdatePC} GOTO[UpdatePC], c3, at[2,4,NoRCross]; {***************************************************************************** Refill Remap Routines *****************************************************************************} { Entry: 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 pending branch on uPCCross (if Q = 0100) Exit: T old real PC 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, BRANCH[$,Crossing], c1, at[0F,10]; rhTT ← UvChigh, c2; TT ← UvPCpage, L1 ← L1.Refill, GOTO[RReMap], c3; {PC was already updated for next page. Adjust Q.} Crossing: rhTT ← UvChigh, Q ← 0, c2; TT ← UvPCpage, L1 ← L1.Refill, c3; RReMap: Map ← TT ← [rhTT, TT + Q], L0Disp, c1; TT ← TT and ~0FF, DISP4[ECross], c2; {Buffer Empty Refill page cross. Remap the PC (which points to 1st word of page). Return to the Empty Refill code at NoRCross.} ECross: PC ← MD, rhPC ← MD, XRefBr, UvPCpage ← TT, c3, at[L0.ERefill,10,ECross]; ERedo: MAR ← Q ← [rhPC, T+0], BRANCH[EMUD, $], c1, RMFRet[L0.ERefill]; PC ← Q, push, CANCELBR[NoRCross,0], c2; EMUD: Rx ← PC, CANCELBR[RCMapFix] c2; {Buffer Not Empty Refill page cross. Fetch the first word of the next page and Remap PC. Set PCCross true. Dispatch on the IB (as in the RefillNE code).} NECross: PC ← MD, rhPC ← MD, XRefBr, UvPCpage ← TT, c3, at[L0.NERefill,10,ECross]; NERedo: MAR ← Q ← [rhPC, 0+0], BRANCH[NEMUD,$], c1, RMFRet[L0.NERefill]; PC ← Q - 1, AlwaysIBDisp, c2; IB ← MD, uPCCross ← (TT xor ~TT), DISPNI[OpTable], c3; NEMUD: Rx ← PC, CALL[RCMapFix] c2; {Jump Cross Remap. Remap the PC (which can point to any word of a page). Set uPCCross false, since we remap to the current page.} JCross: PC ← MD, rhPC ← MD, XRefBr, UvPCpage ← TT, 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, uPCCross ← 0, DISP4[JPtr1Pop0, 2], c3; JMUD: Rx ← PC, CALL[RCMapFix] c2; {***************************************************************************** Mesa Interrupt *****************************************************************************} {The MInt hardware bit indicates that there is a pending interrupt. MInt can be true in the following cases: a. An IOP task has interrupted while setting a bit destined for uWP b. The process timer has interrupted c. A subroutine in Process.mc has called MesaIntRq to "carry over" an interrupt to Refill.} MInt0: T ← 0{buffer empty}, GOTO[MInt], c1, at[600]; MInt1: T ← T xor ~T{not empty}, GOTO[MInt], c1, at[700]; MInt: GOTO[RefillInt], c2; NxtInstc1: Noop, c1; IBDispOnly: IBDisp, c2; DISPNIonly: DISPNI[OpTable], c3, at[addrDISPNIonly]; {***************************************************************************** Microcode Traps *****************************************************************************} {Control comes to location 0 with EKErr values of: 1 Boot 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). 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: rInt ← ErrnIBInt, ClrIntErr, CANCELBR[$,0F] {must be c1}, c1, at[0]; Xbus ← rInt LRot12, XDisp, L1←L1.None, c2; G ← 0, uPCCross ← 0, MesaIntRq, DISP4[ErrTrap,3], c3; UnexpectedErr: T ← sHardwareError, GOTO[UnexpectedErr], c*, at[3,10,ErrTrap]; BootTrap: Map ← Q ← [rhMDS,T], CANCELBR[XferIndirect,0E], c1, at[7,10,ErrTrap]; StackErr: T ← sStackError, GOTO[Trapc2], c1, at[0B,10,ErrTrap]; IBEmptyErr: GOTO[IBEmpty], c1, at[0F,10,ErrTrap]; {InitDaybreak is now an initialization routine which ends by looping at addrLinkage. The emulator resumes at this address in any cycle; the following code synchronizes the cycle and transfers control to BootTrap. Note that InitDaybreak has set up the XferIndirect parameters for a germ boot.} Linkage: [] ← L{=0} + PC16, NZeroBr, BRANCH[Linkage,LinkageEntry], c*, at[addrLinkage]; LinkageEntry: Xbus ← 0, XC2npcDisp, BRANCH[LinkageEntry,BootTrap,5], c*; {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 SaveState, then a Page fault is caused to the new PC page} IBEmpty: T ← UvPCpage, XC2npcDisp, c2; PC ← RShift1 uCrSavePC, XLDisp, BRANCH[IBEEa,IBEEb,0E], c3; IBEEa: TOS ← uCrSaveTOS, Cin←pc16, BRANCH[IBEEc,IBEEd,2], c1; IBEEb: TOS ← uCrSaveTOS, BRANCH[IBEEc,IBEEd,2], c1; IBEEc: stackP ← uCrSaveStkp, GOTO[IBEEe], c2; IBEEd: stackP ← uCrSaveStkp, Cin←pc16, c2; IBEEe: 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, 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. TT has already been saved.} FixRefillTrap: L0Disp, c1, at[L1.Refill,10,RTrapFix]; DISP4[TrapFixType], c2; JFix: PC ← Q, L2Disp, CANCELBR[$], c3, at[L0.JRemap,10,TrapFixType]; 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; {Buffer is empty. Save state and cause a page fault.} REFix: uCrSaveTOS ← TOS, c3, at[L0.ERefill,10,TrapFixType]; PC ← UrPC, c1; L3Disp, GOTO[TrapFixDone], c2; {Buffer is not empty. Save TOS, PC, pc16, and stackP. Set UvPCpage back to previous page (it was updated in anticipation of a page cross). 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 Xfer or Process, they will store the correct PC away because it points to the last word of the page. If control goes to Jump, uPCCross will be reset as usual.) } RNEFix: uCrSaveTOS ← TOS, c3, at[L0.NERefill,10,TrapFixType]; SaveState: Map ← TT ← [rhTT, TT - r0100], c1; UvPCpage ← TT, pop{pushed at RTrap}, c2; PC ← MD, rhPC ← MD, c3; PC ← PC or 0FF, c1; T ← LShift1 PC, Cin←pc16, c2; uCrSavePC ← T, Cin←pc16 {restore}, c3; T ← ~ErrnIBnStkp, c1; AlwaysIBDisp, c2; uCrSaveStkp ← T, DISPNI[OpTable], c3;