{
file: <CoPilot>DLion>BlockB0.mc
Created by E. Fiala 9 July 1986
Edited:
  Trow	18-Dec-87 16:03:45 Fix interrupts.
  Trow	22-Oct-87 16:15:09 Change XwdDisp to XdwDisp.
  Trow	12-Oct-87 19:17:06 Reverse targets 1 and 2 of XwdDisp.
  Trow   8-Oct-87 18:28:57 Added bank cross targets for Daybreak.
  Fiala 19-Nov-86 17:57:33 Removed MBus and PCBus opcodes to MBusB0.mc 

Copyright (C) 1984, 1985, 1986, 1987 by Xerox Corporation.  All rights reserved.

Formerly, all of this code was in CedarB0.mc.  Opcodes defined here are as follows:
	LocalBlkZ (not debugged and not used at present)
	LongBlkZ
	Checksum
	WriteMBus
	ReadMBus
	WritePCBus
	ReadPCBus

LongBlkZ and Checksum are performance critical.  Checksum replaces a slower implementation
formerly in Block.mc.

Notes:
1) Check for occurrences of rhB ← rC LRot0, rB ← rC which does not cause an assembler error
but doesn't work (??).
}

SetTask[0];


{***************************************************************************
aLocalBlkZ
  At entry to opcode:
  	TOS	Cardinal count of words to zero.
    Preceding Bank 1 code has been:
  	Bank ← MSBank0,						c1;
{db}	rhTT ← UvMDS, GOTOABS[BxLocalBlkZ],			c2;
{db}	TT ← UvL, GOTOABS[B0LocalBlkZ],				c3, at[BxLocalBlkZ];
  At return:
  	The count word is removed from the stack.
  
  Point <rhTT, TT> at the last word remaining to be zeroed; this word is
  at LOCAL + 4 + TOS - 1 since the data part of the local frame begins at
  LOCAL + 4.  Zero the block in reverse order so that the only state variable
  is TOS, decremented after each word is zeroed.  There is no 64k boundary
  crossing to worry about since the local frame is confined to the MDS.
  The block-zeroing subroutine is called and returns to the same exit as LongBlkZ.
  Timing = 1 click/word.
  ****************************************************************************}
{db	TT ← UvL, GOTOABS[B0LocalBlkZ],				c3, at[BxLocalBlkZ];}

{db
@LocalBlkZa:
	TT ← TT + 3,						c1, at[B0LocalBlkZ];
	TT ← TT + TOS, L1 ← L1.LBZ2,				c2;
	fXpop, push, CALL[BZero1],				c3;
db}


{***************************************************************************
aLongBlkZ
  At entry to opcode:
  	TOS	Cardinal count of words to zero.
	STK	High half of long pointer to block.
	STK-1	Low half of long pointer to block.
    Preceding Bank 1 code has been:
  	Bank ← MSBank0, L1 ← L1.LBZ2,				c1;
{db}	Q ← rhTT ← STK, GOTOABS[BxLongBlkZ],			c2;
{db}	Rx ← TOS - 1, pop, GOTOABS[B0LongBlkZ],			c3, at[BxLongBlkZ];
  At return:
  	The count word is removed from the stack, but the long pointer remains.
  
  The block is zeroed in reverse order to avoid state save and restore.  After each
  iteration the count in TOS is decremented.  Timing = 1 click/word.
  ****************************************************************************}
{db}	Rx ← TOS - 1, {CarryBr,} pop, GOTOABS[B0LongBlkZ],	c3, at[BxLongBlkZ];

@LongBlkZa:
	TT ← STK, fXpop, fZpop, push,				c1, at[B0LongBlkZ];
	TT ← TT + Rx, CarryBr, push,				c2;
LBZ0:	Q ← Q + 1, BRANCH[LBZ1, $],				c3;
	  rhTT ← Q LRot0,					c1;
	  GOTO[LBZ0],						c2;
{<rhTT, TT> points at the last word remaining to be zeroed.
TOS holds the cardinal word count.  Call the block-zeroing subroutine.}
LBZ1:	Map ← Q ← [rhTT, TT], push, CALL[BZero],		c1;

LBZ2:	Bank ← MSBank1, push, GOTO[LBZ7] {In AssignRef},	c1, at[L1.LBZ2, 10, BZRets];



{***************************************************************************
BZero block-zeroing subroutine
  At entry to BZero1 and on any interrupt or page fault:
  	TOS		Cardinal count of words to zero.
	<rhTT, TT>	Long pointer to last word of the block.
	L1		Return link.
  At exit:
  	TOS, STK + 1	0.
	TT, rhTT, Q, Rx, rhRx, L0, L2 smashed
  
  The block is zeroed in reverse order to avoid state save and restore.  After each
  iteration the count in TOS is decremented.  On interrupts and page faults, the
  shadow cell for TOS on the stack is also written, and it is important that TOS and
  its shadow value in STK+1 = 0 on return for FreeObject.
Timing = 1 click/word + 2 clicks/page.
****************************************************************************}
{Entry from LocalBlkZ and FreeObject}
BZero1:	Map ← Q ← [rhTT, TT], push,				c1;
{Entry from LongBlkZ}
BZero:	[] ← STK ← TOS, ZeroBr, L2 ← L2.BZ1, pop,		c2;
{db}	Rx ← rhRx ← MD, XdwDisp, BRANCH[$, BZ9],		c3;

BZ1:	MAR ← [rhRx, Q + 0], DISP2[BZ4],			c1, at[L2.BZ1, 10, NcWMapFixRets];
BZ4:	CALL[NcWMapFix] {Will return via L2 to BZ1},		c2, at[0, 4, BZ4]; {WP=0, D=0 => set D=1 and proceed}
{db}	CALL[NcWMapFix] {WP fault},				c2, at[1, 4, BZ4]; {WP=1, D=0 => WP fault}
	CALL[NcWMapFix] {Page fault},				c2, at[3, 4, BZ4]; {WP=1, D=1 => Page fault}
{db}	MDR ← 0, MesaIntBr,					c2, at[2, 4, BZ4]; {WP=0, D=1 => ok to write}
BZ1a:	TOS ← TOS - 1, ZeroBr, BRANCH[$, BZInt],		c3;

	MAR ← Q ← [rhRx, Q - 1], BRANCH[$, BZ8],		c1;
BZ7:	MDR ← 0, MesaIntBr, BRANCH[BZ1a, BZ5, 1],		c2;

{page cross; no word has been written}
BZ5:	Q ← 377'b, CANCELBR[$],					c3;
	TT ← TT - Q - 1, CarryBr, L2 ← L2.BZ6,			c1;
	Q ← rhTT, BRANCH[$, BZ6],				c2;
	  Q ← Q - 1, CALL[BankFix],				c3;
BZ6:	TT ← TT or 377'b, GOTO[BZero1],				c3, at[L2.BZ6, 10, BankFixRets];

BZInt:	[] ← uWP, ZeroBr {Wakeups pending?}, BRANCH[$, BZ8a],		c1;
	[] ← uWDC, NZeroBr {Wakeups disabled?}, BRANCH[$, BZ2a],	c2;
{db}	rInt ← rInt or IntStat, ClrIntErr, BRANCH[$, BZ2b],		c3;
	
	Bank ← MSBank1,						c1;
{db}	push, GOTOABS[BxIntContinue1],				c2;
{db}	STK ← TOS, pop, GOTOABS[B1IntContinue],			c3, at[BxIntContinue1];

{no wakeups}
BZ2a:
{db}	rInt ← rInt or IntStat, ClrIntErr, CANCELBR[$],		c3;
{wakeups disabled}
BZ2b:	MAR ← Q ← [rhRx, Q - 1], GOTO[BZ7],			c1;

BZ8a:	{Finished} CANCELBR[BZ8b, 3],				c2;
BZ8:	{Finished} CANCELBR[$, 3],				c2;
BZ8b:	{noop}							c3;

{This code is copied from Block.mc.  It sets MesaIntRq true if the next
opcode crosses a page boundary.  Discussion of this is in Refill.mc.}
BZ9:	Xbus ← uPCCross, XRefBr, push, CANCELBR[$, 3],		c1;
	STK ← TOS, pop, L1Disp, BRANCH[$, BZSetInt],		c2;
{This instruction sets up TT for the FreeObject opcode.}
	TT ← LShift1(uBSISave), SE ← 0, RET[BZRets],		c3;
BZSetInt:
	MesaIntRq, RET[BZRets],					c3;


{******************************************************************************
Checksum
	   TOS = uStack5 = srcHi
	   STK = uStack4 = srcLo
	   STK-1 = uStack3 = count
	   STK-2 = uStack2 = csum
The 16-bit Pup checksum is initialized to zero; each 16-bit word in the block is ones-complement
added to the checksum; following each addition, the checksum is left-cycled 1.  A final result
of "minus one" (177777B) is converted to zero. 177777B is specifically defined to mean that
the Pup carries no checksum.

During the inner loop, TOS holds the word count, <rhTT, Q> the current VA, T the checksum,
Rx and rhRx the high part of the real address.

Timing: 2 clicks/word + 5 clicks/page.

Previous instructions in bank 1 have been:
	Bank ← MSBank0, L0 ← L0.CSRem2,				c1, at[7, 10, Misc0n];
{db}	rhTT ← TOS LRot0 {srcHi}, GOTOABS[BxCSum],		c2;
{db}	TT ← STK {srcLo}, GOTOABS[B0CSum],			c3, at[BxCSum];
**********************************************************************************}
{db}	TT ← STK {srcLo}, GOTOABS[B0CSum],			c3, at[BxCSum];

Checksum:	
	TOS ← uStack3 {count}, ZeroBr,				c1, at[B0CSum];
	BRANCH[$, CSDon0],					c2;
	CALL[NcRdOne],						c3;

CSLp:	MAR ← Q ← [rhRx, Q + 1],				c1;
CSLp1:
{db}	{MesaIntBr,} BRANCH[$, CSPgO, 1],			c2;
	TT ← MD, BRANCH[$, CSInt],				c3;

CSRmLp:	T ← T + TT, CarryBr,					c1;
CSInt5:	TOS ← TOS - 1, ZeroBr, BRANCH[$, CSWrap],		c2;
	T ← T LRot1, BRANCH[CSLp, CSDone],			c3;
CSWrap:	T ← (T + 1) LRot1, BRANCH[CSLp, CSDone],		c3;

{Finished with final word.  If csum is all 1's, change it to 0.}
CSDon0:	T ← uStack2 {csum},					c3;
CSDone:	[] ← T xor ~0, NZeroBr, pop,				c1;
	Xbus ← uPCCross, XRefBr, pop, BRANCH[$, CSDon3],	c2;
	STK ← TOS ← 0, pop, BRANCH[CSDon5, CSDon4],		c3;
CSDon3:	STK ← TOS ← T, pop, BRANCH[CSDon5, CSDon4],		c3;

CSDon4:	MesaIntRq,						c1;
	Noop,							c2;
	Noop,							c3;

CSDon5:	Bank ← MSBank1, GOTO[CSDon6] {In CRefType},		c1;

{Interrupt request: Ignore current word and check for done first.
ClrIntErr must occur before or in the same click as uWP testing.}
CSInt:	[] ← uWP, ZeroBr {Wakeups pending?},			c1;
	[] ← uWDC, NZeroBr {Disabled?}, BRANCH[$, CSInt2],	c2;
	uStack2 ← T, ClrIntErr, BRANCH[CSInt4, CSInt3],		c3;
CSInt2:	ClrIntErr, CANCELBR[$], {no wakeups}			c3;

CSInt3:	T ← T + TT, CarryBr, GOTO[CSInt5], {Wakeups disabled}	c1;

{The interrupt will take.  Save the state; tricky part is reconstructing the
virtual address from <low 8 bits in Q, high 8 bits in uStack4>; srcHi is
already correct since it is updated on every page overflow.}
CSInt4:	TT ← uStack4 {old srcLo},				c1;
	TT ← TT and ~0FF,					c2;
	Q ← Q and 0FF,						c3;

	TT ← TT or Q, push,					c1;
	uStack4 ← TT {current srcLo},				c2;
	uStack3 ← TOS {count},					c3;

	Bank ← MSBank1,						c1;
{db}	Q ← rhTT, GOTOABS[BxIntContinue2],			c2;
{db}	TOS ← STK ← Q, pop, GOTOABS[B1IntContinue],		c3, at[BxIntContinue2];

{Page crossing: First, update the stack values from the working registers
in preparation for a possible page fault.  Then copy the updated source
address into <rhTT, TT> for mapping.
}
CSPgO:	TT ← uStack4 {old srcLo}, CANCELBR[$],			c3;

{Set VA to the last word of the old page and then add 1.}
	TT ← TT or 0FF, push {point stkp at srcHi},		c1;
	TT ← TT + 1, CarryBr,					c2;
CSPgO1:	uStack3 ← TOS {count}, BRANCH[CSRem, $],		c3;
	  Q ← rhTT + 1, LOOPHOLE[byteTiming],			c1;
	  rhTT ← Q LRot0, STK ← Q, GOTO[CSPgO1],		c2;

CSRem:	Noop,							c1;
CSRem1:	uStack4 ← TT {srcLo}, L0 ← L0.CSRem2,			c2;
	uStack2 ← T {csum}, pop, CALL[NcRdOne],			c3;

CSRem2:	TT ← uStack2 {csum},					c1, at[L0.CSRem2, 10, NcRdOneRets];
	Noop,							c2;
	GOTO[CSRmLp],						c3;