*----------------------------------------------------------- Title[NewMemory.mc...April 17, 1984 4:33 PM...Willie-Sue]; * Map operations from Memory organization (PrincOps, chapter 3) *----------------------------------------------------------- % CONTENTS, by order of occurrence Virtual memory SM Set Map GMF Get Map Flags SMF Set Map Flags Virtual memory subroutines SetBRAndFlushPage Set up for map operation WriteMapPage Write real page and flags ReadMapPage Read real page and flags % TopLevel; *----------------------------------------------------------- * Map operations *----------------------------------------------------------- * MapFlags: TYPE = MACHINE DEPENDENT RECORD [ * reserved (0: 0..12): [0..17777B], MC[MF.protected, 4]; * protected (0: 13..13): BOOLEAN, MC[MF.dirty, 2]; * dirty (0: 14..14): BOOLEAN, MC[MF.specialDirty, 1]; * for ReadMap (before lsh 1) MC[MF.referenced, 1]; * referenced (0: 15..15): BOOLEAN]; MC[MF.vacant, MF.protected, MF.dirty]; MC[MF.allFlags, MF.protected, MF.dirty, MF.referenced]; * Mesa defines vacant as protected AND dirty AND NOT referenced. * Unfortunately, the Dorado defines vacant as protected AND dirty, * without regard to referenced. * To get around this problem, we set up a table in real memory * to hold the extra dirty bit; this memory is inaccessible to Cedar * The microcode takes over the highest "bank" of virtual memory, * and allocates enough real memory to hold one bit for each page of * real memory; the base register MapBitsBR references this bit array. * When Mesa asks us to set the combination protected AND dirty AND referenced * (which isn't vacant by Mesa's definition), we instead set * protected AND NOT dirty AND referenced, and we set the extra bit. * When Mesa asks to read the flags, we or together the hardware dirty * bit and the extra bit. * PrincOps deviation: * The map operations are supposed to detect out-of-bounds virtual addresses * (i.e., ones greater than the maximum supported on the processor) and treat * them as vacant. However, no existing software takes advantage of this; * rather, the size of VM is determined by processor-dependent means in * the Pilot ProcessorHead. Therefore, no check is made in this implementation * for out-of-bounds virtual addresses; and the hardware simply ignores * extraneous high-order bits. *----------------------------------------------------------- NewSetMap: MiscTable[150], * Set Map * mf: MapFlags _ Pop[]; * rp: RealPageNumber _ PopLong[]; vp: VirtualPageNumber _ PopLong[]; * WriteMap[virtual: vp, flags: mf, real: rp]; * * arrives with T_ MapFlags, StkP points above tos *----------------------------------------------------------- * First, set up VA and flush virtual page from cache. nop; Q_ T, StkP-4; Call[NewSetBRAndFlushPage]; * Returns with TaskingOff * Store new flags and real page into map. T_ (Add[MF.allFlags!]S) AND Q, StkP+2; StkP+2, Call[NewWriteMapPage]; * Returns with TaskingOn * Restart IFU, since we reset it above. T_ ID, IFUReset, StkP-2; * ID = instruction length NewMapExit: T_ T - (PCX') -1, branch[SetPCAndJump0]; *----------------------------------------------------------- NewGetMapFlags: MiscTable[151]; * Get Map Flags * mf: MapFlags; rp: RealPageNumber; * vp: VirtualPageNumber _ PopLong[]; * [flags: mf, real: rp] _ ReadMap[vp]; Push[mf]; PushLong[rp]; *----------------------------------------------------------- * First, set up VA and flush virtual page from cache. nop; StkP-1, Call[NewSetBRAndFlushPage]; * Returns with TaskingOff * Read current real page number and flags from map StkP+1, Call[NewReadMapPage]; * Leaves RealPageNumber above top-of-stack Stack_ T, TaskingOn, Branch[MapOpExit], DispTable[1, 1, 1]; * Return here whether vacant or not *----------------------------------------------------------- NewSetMapFlags: MiscTable[152], * Set Map Flags * mf: MapFlags; rp: RealPageNumber; * newMf: MapFlags _ Pop[]; vp: VirtualPageNumber _ PopLong[]; * [flags: mf, real: rp] _ ReadMap[vp]; Push[mf]; PushLong[rp]; * IF ~Vacant[mf] THEN WriteMap[virtual: vp, flags: newMf, real: rp]; * * arrives with T_ MapFlags, StkP points above tos *----------------------------------------------------------- * First, set up VA and flush virtual page from cache. nop; StkP-2; * T already has flags RTemp3_ T AND (MF.allFlags), Call[NewSetBRAndFlushPage]; * Returns with TaskingOff * Read current real page number and flags from map StkP+1, SCall[NewReadMapPage]; * Leaves RealPageNumber above top-of-stack * +1 return: entry is vacant, just return old flags and don't set new ones Stack_ T, TaskingOn, Branch[MapOpExit]; * +2 return: write the map with new flags and old real page number. Stack&+2_ T; * Put old flags on stack T_ RTemp3, Call[NewWriteMapPage]; * Pops RealPageNumber off stack; returns TaskingOn MapOpExit: T_ ID, IFUReset, StkP+2, Branch[NewMapExit]; * ID = instruction length *----------------------------------------------------------- NewSetBRAndFlushPage: * Set up for map operation * Enter * Stack[StkP],,Stack[StkP-1] = virtual page * Exit * MemBase = LPtr, containing virtual address * RTemp0 = 0 * TaskingOff * IFUReset (to prevent IFU from making references to the page being flushed) * Stack popped 2 * Clobbers T, RTemp0, Cnt *----------------------------------------------------------- Subroutine; StkP-1, Membase_ LPtr; IFUReset; T_ LSH[Stack, 10]; T_ Stack&+1, BRLo_ T; T_ LCY[T, Stack, 10]; RTemp0_ A0, T_ MD, BRHi_ T; Cnt_ 17S; * Assume 20B munches per page * Do the flush twice, once with TaskingOn and again with TaskingOff. * The idea is that all the actual flushing of dirty munches will occur * with TaskingOn; however, it is logically necessary to do it again with * TaskingOff because a higher-priority task might have touched the * page we are flushing, and subsequent Map operations depend on the page being * completely flushed. RTemp0_ Flush_ RTemp0, Carry20, Branch[., Cnt#0&-1]; RTemp0_ A0, Cnt_ 16S; TaskingOff, StkP-2; RTemp0_ Flush_ RTemp0, Carry20, Branch[., Cnt#0&-1]; Flush_ RTemp0, RTemp0_ A0, Return; *----------------------------------------------------------- NewWriteMapPage: * Enter: T = desired MapFlags (reserved bits must be zero) * Stack[StkP],,Stack[StkP-1] = desired RealPageNumber * MemBase = LPtr, LPtr contains virtual address * TaskingOff * Exit: TaskingOn * Stack popped 2 * Note: ignores the high half of RealPageNumber, since the Dorado hardware * implements only 16 bits of real page number. * Clobbers Q, T, RTemp0, RTemp1, RTemp6 *----------------------------------------------------------- Subroutine; * Shift wProtect and dirty bits into position for the hardware, and * test for the combination protected & dirty & referenced. * Mesa format is B13=protected, B14=dirty, B15=referenced. * Hardware format for writing is B0=protected, B1=dirty (referenced can't be set). RTemp1_ T, Q_ Link; * save flags for later TopLevel; RTemp0_ LSH[T, 15]; * B0_ protected, B1_ dirty, B2_ referenced StkP-1; * rpLo T_ Stack, Call[MapDirtyBit]; * returns word in Md, index in RTemp6, bit in T nop; PD_ (RTemp1)-(Add[MF.protected!, MF.dirty!, MF.referenced!]C); Branch[wrp, ALU#0]; * This is the state protected & dirty & referenced, which the Dorado hardware * can't handle (it would mistakenly interpret it as vacant). * Turn off the dirty bit, and set the sign bit in the real page number, * which we have taken over as a duplicate dirty bit. RTemp0_ (RTemp0) AND NOT (LShift[MF.dirty!, 15]C); T_ T OR Md, branch[.+2]; * set extra bit on wrp: T_ Md AND NOT T; * turn off extra bit store_ RTemp6, DBuf_ T; Link_ Q; Subroutine; T_ Stack&-1, Membase_ LPtr; * restore membase, get rpLo, pop * Now write the map entry. * Note: careful not to select TIOA values in [10..17], which can screw up * the disk controller! RTemp0_ (RTemp0)+(TIOA_ RTemp0); * Set up flags for Map_ RTemp0_ (RTemp0)+(RTemp0), * Shift referenced to B0 TaskingOn; * Will take after next instruction PD_ (Map_ 0S)-1, MapBuf_ T; * Write map entry (real page & flags) PD_ PRef, Branch[., ALU<0]; * Wait for map reference to finish * Writing the map zeroed the ref bit. If we desire to set ref, do so by * issuing a PreFetch to the page. T_ A0, RTemp0, Branch[.+2, R>=0]; PreFetch_ 0S; TIOA_ T, Return; * TIOA=0 required by Mesa emulator *----------------------------------------------------------- NewReadMapPage: * Enter: MemBase = LPtr, LPtr contains virtual address * RTemp0 = 0 * Call: SCall[ReadMapPage] * Exit: Returns to caller+1 if entry is vacant, caller+2 otherwise. * T = MapFlags * Pushes RealPageNumber onto the stack but does not change StkP * (i.e., leaves RealPageNumber as the 2 words above top-of-stack) * Clobbers T, Q, RTemp0, RTemp1, RTemp2, RTemp6 *----------------------------------------------------------- Subroutine; RMap_ RTemp0; * Read map entry (RTemp0=0) T_ 30000C, StkP+2; * Mask for hardware protected & dirty bits RTemp0_ MF.allFlags; * MapFlags mask used later Stack&-1_ A0; * Preset high RealPageNumber to zero PD_ NOT (PRef), Branch[., ALU>=0]; * Wait for map reference to finish * Read previous map flags and check if vacant. * Hardware flags are read as B0=referenced, B2=protected, B3=dirty. * Converting to Mesa format, which is B13=protected, B14=dirty, B15=referenced. T_ T AND (Q_ Errors'); * Note that flags are complemented Stack_ NOT (Map'), * Read and push previous real page Branch[ReadMapVacant, ALU=0]; RTemp2_ Link; Toplevel; RTemp1_ RSH[T, 14]; * Right-justify protected & dirty, save nmp: T_ Stack, Call[MapDirtyBit]; * stack has real page pd_ T AND Md; branch[.+2, alu=0], T_ RTemp1; T_ T AND NOT(MF.specialDirty); * flags up side down Link_ RTemp2; Subroutine; T_ A_ T, Divide, StkP-1; * T_ (T,,Q) LSH 1; Carry_ 0 T_ (RTemp0) XOR T, Return[Carry']; * Turn flags right side up and always skip ReadMapVacant: T_ MF.vacant, StkP-1, Return; TopLevel;