*-----------------------------------------------------------
Title[Memory.mc...November 7, 1982  4:13 PM...Taft];
* 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

Main data spaces
	LP		Lengthen Pointer
	LLOB		Load Local Overhead Byte
	SLOB		Store Local Overhead Byte
	ROB		Read Overhead Byte
	WOB		Write Overhead Byte

Processor memories
	RRIT		Read Register IT
	RRMDS		Read Register MDS
	RRPSB		Read Register PSB
	RRPTC		Read Register PTC
	RRWDC		Read Register WDC
	RRWP		Read Register WP
	RRXTS		Read Register XTS
	WRIT		Write Register IT
	WRMDS		Write Register MDS
	WRMP		Write Register MP
	WRPSB		Write Register PSB
	WRPTC		Write Register PTC
	WRWDC		Write Register WDC
	WRWP		Write Register WP
	WRXTS		Write Register XTS
%

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.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 take over one of the two high-order
* bits of the real page number, which do not participate in storage
* addressing unless 256K storage chips are installed (unlikely
* during the lifetime of the Dorado).

* 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.

*-----------------------------------------------------------
NewOp; ESCEntry[SM],			* Set Map
* mf: MapFlags ← Pop[];
* rp: RealPageNumber ← PopLong[]; vp: VirtualPageNumber ← PopLong[];
* WriteMap[virtual: vp, flags: mf, real: rp];
*-----------------------------------------------------------

* First, set up VA and flush virtual page from cache.
	T← Stack&-3;			* Save flags
	Q← T, Call[SetBRAndFlushPage];	* Returns with TaskingOff

* Store new flags and real page into map.
	T← (Add[MF.allFlags!]S) AND Q, StkP+2;
	StkP+2, Call[WriteMapPage];	* Returns with TaskingOn

* Restart IFU, since we reset it above.
	T← ID, IFUReset, StkP-2, Branch[JumpRelativeT]; * ID = instruction length


*-----------------------------------------------------------
NewOp; ESCEntry[GMF];			* 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.
	Call[SetBRAndFlushPage];	* Returns with TaskingOff

* Read current real page number and flags from map
	StkP+1, Call[ReadMapPage];	* Leaves RealPageNumber above top-of-stack
	Stack← T, TaskingOn, Branch[MapOpExit],
		DispTable[1, 1, 1];	* Return here whether vacant or not


*-----------------------------------------------------------
NewOp; ESCEntry[SMF],			* 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];
*-----------------------------------------------------------

* First, set up VA and flush virtual page from cache.
	T← Stack&-1;			* Save flags
	RTemp1← T AND (MF.allFlags),
		Call[SetBRAndFlushPage]; * Returns with TaskingOff

* Read current real page number and flags from map
	StkP+1, SCall[ReadMapPage];	* 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← RTemp1, Call[WriteMapPage];	* Pops RealPageNumber off stack; returns TaskingOn
MapOpExit:
	T← ID, IFUReset, StkP+2, Branch[JumpRelativeT]; * ID = instruction length

*-----------------------------------------------------------
SetBRAndFlushPage:	* 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, IFUReset;
	T← ShiftRMask[Stack],		* LSH[Stack, 10]
		MemBase← LPtr;
	T← Stack&+1, BRLo← T;
	T← ShiftNoMask[Stack],		* LCY[T, Stack, 10]
		Cnt← 17S;		* Assume 20B munches per page
	RTemp0← A0, T← MD, BRLo← T;

* 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;
	RTemp0← Flush← RTemp0, Carry20, Branch[., Cnt#0&-1];
	Flush← RTemp0, RTemp0← A0, Return;

*-----------------------------------------------------------
WriteMapPage:
* 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 T, RTemp0
*-----------------------------------------------------------
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).
	RTemp0← LSH[T, 15];		* B0← protected, B1← dirty, B2← referenced
	PD← T-(Add[MF.protected!, MF.dirty!, MF.referenced!]C), StkP-1;
	T← Stack&-1, Branch[.+3, 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 (100000C);

* 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

*-----------------------------------------------------------
ReadMapPage:
* 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
*-----------------------------------------------------------
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];
	T← RSH[T, 14],			* Right-justify protected & dirty
		Branch[.+3, ALU>=0];	* Branch if sign of previous real page is zero

* Transfer the duplicate dirty bit from the real page to the flags
	Stack← (Stack) AND NOT (100000C);
	T← T AND NOT (MF.dirty);	* Note that flags are still complemented

	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;

*-----------------------------------------------------------
IFUR[LP, 1];				* Lengthen Pointer
* ptr: POINTER ← Pop[];
* PushLong[IF ptr=NIL THEN LONG[NIL] ELSE MDS+LONG[CARDINAL[ptr]]];
*-----------------------------------------------------------

	PD← Stack&+1;
	T← MDSHi, Branch[.+2, ALU=0];
	Stack← T, NextOpcode;
	Stack← A0, NextOpcode;


*-----------------------------------------------------------
NewOp; ESCEntry[LLOB],			* Load Local Overhead Byte
* Push[FetchMds[LF-GetCodeByte[]]↑];
*-----------------------------------------------------------

	T← (LFShadow)-T, TisID, Branch[ExitReadStackT];


*-----------------------------------------------------------
NewOp; ESCEntry[SLOB],			* Store Local Overhead Byte
* StoreMds[LF-GetCodeByte[]]↑ ← Pop[];
*-----------------------------------------------------------

	T← (LFShadow)-T, TisID, Branch[ExitWriteStackT];


*-----------------------------------------------------------
NewOp; ESCEntry[ROB],			* Read Overhead Byte
* ptr: POINTER ← Pop[]; Push[FetchMds[ptr-GetCodeByte[]]↑];
*-----------------------------------------------------------

	T← (Stack&-1)-T, TisID, Branch[ExitReadStackT];


*-----------------------------------------------------------
NewOp; ESCEntry[WOB],			* Write Overhead Byte
* ptr: POINTER ← Pop[]; StoreMds[ptr-GetCodeByte[]]↑ ← Pop[];
*-----------------------------------------------------------

	T← (Stack&-1)-T, TisID, Branch[ExitWriteStackT];

*-----------------------------------------------------------
NewOp; ESCEntry[RRIT];			* Read Register IT
* PushLong[IT];
*-----------------------------------------------------------

	RBase← RBase[ITLo], StkP+1;
	T← ITLo, TaskingOff;		* Read double word atomically!!
	Stack← T;
	T← ITHi, TaskingOn, Branch[ExitPushT];


*-----------------------------------------------------------
NewOp; ESCEntry[RRMDS],			* Read Register MDS
* Push[HighHalf[MDS]];
*-----------------------------------------------------------

	T← MDSHi, Branch[ExitPushT];


*-----------------------------------------------------------
NewOp; ESCEntry[RRPSB],			* Read Register PSB
* Push[Handle[PSB]];
*-----------------------------------------------------------

	T← PSB, Branch[ExitPushT];


*-----------------------------------------------------------
NewOp; ESCEntry[RRPTC];			* Read Register PTC
* Push[PTC];
*-----------------------------------------------------------

	RBase← RBase[PTC];
	T← PTC, Branch[ExitPushT];


*-----------------------------------------------------------
NewOp; ESCEntry[RRWDC];			* Read Register WDC
* Push[WDC];
*-----------------------------------------------------------

	RBase← RBase[WDC];
	T← WDC, Branch[ExitPushT];


*-----------------------------------------------------------
NewOp; ESCEntry[RRWP];			* Read Register WP
* Push[WP];
*-----------------------------------------------------------

	RBase← RBase[WP];
	T← WP, Branch[ExitPushT];


*-----------------------------------------------------------
NewOp; ESCEntry[RRXTS],			* Read Register XTS
* Push[XTS];
*-----------------------------------------------------------

	T← XTS, Branch[ExitPushT];

*-----------------------------------------------------------
NewOp; ESCEntry[WRIT];			* Write Register IT
* IT ← PopLong[];
*-----------------------------------------------------------

	T← Stack&-1, RBase← RBase[ITHi];
	ITHi← T, TaskingOff;		* Write double word atomically!!
	ITLo← Stack, Branch[WRPopExit];


*-----------------------------------------------------------
NewOp; ESCEntry[WRMDS];			* Write Register MDS
* MDS ← LongShift[LONG[Pop[]], WordSize];
*-----------------------------------------------------------

	MDSHi← T, Call[BRHiGetsT];	* T = Stack[StkP]; set high part of MDS
	MemBase← LF, Call[BRHiGetsT];
	MemBase← GF, Call[BRHiGetsT];
WRPopExit:
	StkP-1, TaskingOn, NextOpcode;

BRHiGetsT:
Subroutine;
	BRHi← T, Return, Global;
TopLevel;


*-----------------------------------------------------------
NewOp; ESCEntry[WRMP];			* Write Register MP
* MP ← Pop[];
* Dorado-only feature: also generate an Opcode trap iff there is a trap handler
* set up, so the software has a chance to look at the MP code.
*-----------------------------------------------------------

	T← ETT;
	T← T OR (Add[EOpWRMP]C);
	T← Stack&-1, Fetch← T;
	PD← MD;
	MaintPanel← T, Branch[.+2, ALU#0];
	NextOpcode;
	Branch[ESCOpcodeTrap];

*-----------------------------------------------------------
NewOp; ESCEntry[WRPSB],			* Write Register PSB
* PSB ← Pop[];
*-----------------------------------------------------------

	PSB← Stack&-1, NextOpcode;


*-----------------------------------------------------------
NewOp; ESCEntry[WRPTC];			* Write Register PTC
* PTC ← Pop[];
*-----------------------------------------------------------

	PTC← T, Branch[WRPopExit];	* ESCEntry: T = Stack[StkP]


*-----------------------------------------------------------
NewOp; ESCEntry[WRWDC];			* Write Register WDC
* WDC ← Pop[];
*-----------------------------------------------------------

	WDC← T, Branch[WRPopExit];	* ESCEntry: T = Stack[StkP]


*-----------------------------------------------------------
NewOp; ESCEntry[WRWP];			* Write Register WP
* WP ← Pop[];
*-----------------------------------------------------------

	WP← T, Branch[WRPopExit];	* ESCEntry: T = Stack[StkP]


*-----------------------------------------------------------
NewOp; ESCEntry[WRXTS],			* Write Register XTS
* XTS ← Pop[];
*-----------------------------------------------------------

	XTS← Stack&-1, NextOpcode;