{File name:  FixedRefill.mc
bj    22-Jan-88  2:17:51 Add L1←L1r.NoFixes to ErrTrapC2.
Trow  21-Dec-87 18:18:01 Change  to -.  Fix Overview.
Trow  14-Dec-87 13:54:17 Fix ClrIntErr at IgnoreInt.
Trow   9-Dec-87 23:39:49 Change error entry from bank 0.
Trow  17-Nov-87 19:51:14 Remove ClrIntErr at IgnoreInt.
Trow  31-Oct-87  1:47:47 Change traps for Daybreak.
Trow  28-Oct-87 22:58:19 interrupts at MInt.
Trow  22-Oct-87 20:53:49 Remove reference to KCtl at ErrType.  Remove code for type 1 and 2 errors.
Fiala 23-Jul-86 17:04:16: Init rhTT from cedarIOPageHigh instead of from the
	right half of uIOPage.
Last edited by Sturgis:  2-Feb-84 17:06:18: IBEmptyErr should have a mask on its branch.
Last edited by Jim August 20, 1981  2:20 PM: Fix for new assembler. 
 Last Edited: Sandman, January 9, 1981  3:19 PM,
 Last Edited: Johnsson, October 12, 1980  3:01 PM
 Last Edited: R. Garner, April 11, 1980  4:20 PM,
 Description: Instruction buffer refill microcode for Dandelion,
 Author: R. Garner,
 Created: May 23, 1979,
}


{Overview:

	An instruction buffer refill is necessary whenever there are not 3 bytes in the IB.  This is so all Cedar opcodes can execute without finding the buffer empty.  When an IBDisp is executed control goes to location 400'x if the buffer is empty and 500'x if not empty (1 or 2 bytes left).  Two words (4 bytes) or one word (2 bytes), respectively, are fetched. 

	RefillE is entered when the instruction buffer is empty.  In this case, two words must be fetched.  The PC already contains the real address of the first of the two words to be fetched (unless there's been a page cross).  A page cross test is made of PC-1 to see if the PC has already been incremented across a boundary.  If there is a page cross (PgCross=1), then the PC is remapped.  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 causes a page cross, 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.
 
	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 whether the PC were wrong.  The approach taken here is to set a flag (uPCCross) and force an interrupt after the next bytecode so that the interrupt trap can update the PC.  The flag must still be checked if the PC will be used before the interrupt code is reached.
	 
	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.
	  
		     			
Register Usage:

	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 and Process instructions save PC away, they must check uPCCross.  If uPCCross is true and 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'x) to point to the next page.  Note that the MInt bit is not reset.

	The buffer refill code of Xfer, Jump, and Process instructions should clear uPCCross.  MInt, however, should not be cleared (too much work to check whether it's 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 RFixForTrap: 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 and L0=L0.NERefill and uPCCross is true, then save state, do not take the page fault, and continue executing with a non-filled instruction buffer.  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[500];
	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];
UpdatePCx:	TT ← UvPCpage, L1 ← L1r.Refill	,c2;
	rhTT ← UvChigh	,c3;

{Xfer enters at RReMapx with L0.JRemap disp pending}
RReMap:	Map ← TT ← [rhTT, TT + Q], L0Disp	,c1;
RReMapx:	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[EMapUD, $]	,c1, at[L0.ERefill,10,RxMapFixCaller];
	PC ← Q, UvPCpage ← TT, push, BRANCH[NoRCross, PageCrossed]	,c2;

EMapUD:	Rx ← PC, CANCELBR[LGxMapFix]  {returns at ERedo}	,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[NEMapUD, $]	,c1, at[L0.NERefill,10,RxMapFixCaller];
	AlwaysIBDisp	,c2;
	IB ← MD, uPCCross ← (~T xor T), DISPNI[OpTable]	,c3;

NEMapUD:	uPCCross ← (~T xor T), CALL[LGxMapFix]  {returns at NERedo}	,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[JMapUD, $]	,c1, at[L0.JRemap,10,RxMapFixCaller];
	PC ← Q, Xbus←0, L2Disp, XC2npcDisp	,c2;
	IB ← MD, UvPCpage ← TT, DISP4[JPtr1Pop0, 2]	,c3;

JMapUD:	Rx ← PC, CALL[LGxMapFix]  {returns at JRedo}	,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 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.}

{db *****
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:	[] ← uWDC, NZeroBr, BRANCH[$, Crossing]	,c2;
	TT ← uWP, ClrIntErr, BRANCH[$, IgnoreInt]	,c3;

	[] ← TT, ZeroBr	,c1;
	BRANCH[Wakeups, NoWakeups]	,c2;
Wakeups:	Rx ← pInt, push, GOTO[Wakeupsx]	,c3;
NoWakeups:	GOTO[IgnoreInt]	,c3;

Wakeupsx:	STK ← TOS, pop, GOTO[SaveRegs] {in Process.mc}	,c1;
***** db}


{db}
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:	[] ← uWDC, NZeroBr, BRANCH[$, Crossing],			c2;
	BRANCH[RefillInt, IgnoreInt],					c3;

NoWakeups:
	GOTO[IgnoreInt],						c3;

Wakeups:
	Rx ← pInt, push,						c3;

	STK ← TOS, pop, GOTO[SaveRegs] {in Process.mc},			c1;
{db}


{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, CANCELBR[$]	,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:
{db}	rInt ← rInt or IntStat, ClrIntErr,			c1;
IBDispOnly:
	IBDisp, GOTO[DISPNIonly],				c2;


{
	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.}

{db}
	GOTOABS[B1ErrInBank0],						c3, at[BxErrLoc];

	GOTO[ErrTrapC2],						c1, at[B1ErrInBank0];

ErrTrap:
	rInt ← ErrnIBInt, ClrIntErr, CANCELBR[$,0F]  {must be c1},	c1, at[0];
ErrTrapC2:
{bj}	Xbus ← rInt LRot12, XDisp, {L1←L1r.NoFixes,}			c2;
	uPCCross ← 0, MesaIntRq, DISP4[ErrTrap,3],			c3;

UnexpectedErr:
	T ← sHardwareError, GOTO[UnexpectedErr],					c*, at[3,10,ErrTrap];
BootTrap:
	GOTO[BootTrap],									c*, at[7,10,ErrTrap];
StackErr:
	T ← sStackError, L2 ← L2.TRAPStashr, GOTO[Trapc2],				c1, at[0B,10,ErrTrap];
	
IBTrap:
	[] ← uCrSavePC + 0, Cin←pc16, YDisp, SuppressTimingWarning, GOTO[IBEmptyErr],	c1, at[0F,10,ErrTrap];
{db}


{db
EmuMVErr:	uFaultParm0 ← L xor ~L, 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:
	T ← u200,					c2;
	{rhTT ← TT ← uIOPage,				c3;}
	TT ← uIOPage,					c3;

	rhTT ← cedarIOPageHigh,				c1;
	Noop,						c2;
	Noop,						c3;

	MAR ← [rhTT, TT+41{40}],	c1;
	MDR ← T + 0D8, {200X+D8X = 1330B = 728D} CANCELBR[$,0],	c2;
CSErrSpin:	GOTO[CSErrSpin],	c*;
db}

	

{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:	TOS ← uCrSaveTOS, BRANCH[$, pcNoFlip, 0E]	,c2;
	stackP ← uCrSaveStkp, Cin←pc16, GOTO[RestorePC]	,c3;
pcNoFlip:	stackP ← uCrSaveStkp	,c3;

RestorePC:	PC ← RShift1 uCrSavePC	,c1;
	rhTT ← UvChigh,	,c2;
	T ← UvPCpage, L3 ← L3.rhTT.TT	,c3;

	TT ← T + 0FF + 1, GOTO[IBEmptyTrap],	,c1; {L1 loaded at ErrTrap}

{
	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[L1r.Refill,10,RFixForTrap];
	Xbus ← uPCCross, XRefBr, BRANCH[RefillTrapFix, JumpTrapFix, WhichFixType]	,c2;


{Sturgis: 14-Feb-84 14:10:04: following code seems to set up Faultc3 to clobber STK from incorrect TOS, so add a cycle in which TOS is picked up from the stack.  {NoFixes} and {PopOnlyFix} were the original exits from this code.}

JumpTrapFix:
	PC ← Q, CANCELBR[$]	,c3;
	UvPCpage ← TT, L2Disp	,c1;
	uPCCross ← 0, DISP4[JumpTrapPop0,3]	,c2;
	GOTO[JTF0{NoFixes}]	,c3, at[3,10,JumpTrapPop0];
	pop, GOTO[JTF0{NoFixes}]	,c3, at[7,10,JumpTrapPop0];
	pop, GOTO[JTF1{PopOnlyFix}]	,c3, at[0B,10,JumpTrapPop0];
	pop, GOTO[JTF1{PopOnlyFix}]	,c3, at[0F,10,JumpTrapPop0];

JTF0:	GOTO[JTF2],			c1;	
JTF1:	pop, GOTO[JTF2],		c1;
JTF2:	TOS ← STK,			c2;
	GOTO[NoFixes],			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)}

RefillTrapFix:
	uCrSaveTOS ← TOS, BRANCH[RefillFault, SaveState]	,c3;

RefillFault:
	PC ← UrPC,	,c1;
	UvPCpage ← TT, L3Disp, GOTO[TrapFixDone]	,c2;

SaveState:	pop{pushed at RTrap}, GOTO[NotAcross]	,c1;



{
RTE:  2-Feb-84 17:07:03: Sturgis: IBEmptyErr should have a mask on its BRANCH
LiveRTE: 14-Feb-84 14:11:13: JumpTrapFix seems to set up STK to be clobbered from an incorrect TOS at Faultc3.   Added a click of fix up for TOS.
}