{File name:  GrayBltLoops.mc
Description: Intra-item loops for GrayBlt opcodes.
Author: JPM
Created: June 30, 1986
Last Revised:
October 14, 1986 -- JPM -- Add comments
August 7, 1986 -- JPM -- Changed register and subroutine names, used symbolic constants
July 24, 1986 -- JPM -- Fixed bug in ThreshDestPCb (need to increment count, not decrement it)
July 23, 1986 -- JPM -- Set L5 back to gr.set after one-byte operation (in GrSetFix2 dispatches); fix last-byte cases (count must be 0 for proper CarryBr value)
July 22, 1986 -- JPM -- Fixed bug in add function (single-carry cases reversed); added extra click for dstFunc = average if PgCarryBr (just in case there was also a CarryBr); fixed bugs in GrRSd3 (was "and", should be "or") and GrRD2TRC (cases reversed)
July 18, 1986 -- JPM -- Revised in accordance with register & subroutine name changes
July 10, 1986 -- JPM -- Reversed direction of CarryBr branch when decrementing count (in Sum and Threshold loops); inverted rTemp at each return from BumpByteVA}

{	Copyright (C) 1986 by Xerox Corporation.  All rights reserved.}

{Item loop for GRAYBLT
	L2 through L5 control sequencing (see GrayBltNotes.doc)
	uGrFlags preserves values for above link regs
	rhGrDispCond preserves value for L3 (TRC) or L4 (non-TRC)
	[rhSrcReal, rSrcReal] contain the real word address for the next source byte
	[rhDstReal, rDstReal] contain the real word address for the next destination byte
	[rhGrTRC, rGrTRC] contain the real word address for the TRC table (if applicable)
	rCount contains the number of pixels yet to process for this item
	r(h)Temp, r(h)GrSrcTemp, r(h)DstWord, and Q are used for temporary storage
	rGrFF00 = 0FF00
	rGr00FF = 0FF
	pending L2Disp and NZeroBr of source address byte index}

{roadmap definitions}
{NOTE: naming conventions are as follows:
	at-clause macros use "Gr", an abbreviation for the click label, and the cycle number
	dispatch macros put "Disp" before the name as described above
	dispatch setup macros (e.g. link reg dispatch) put "Disp" after the name
	in one case where different clicks may be the targets of one dispatch,
	  the special name "GrXRoad1" is used for dispatch and dispatch setup macros}

{loop cases}
MacroDef[GrRS1, at[Or[#1,7],10,GrRSc1]],
MacroDef[GrRS1Disp,L2Disp],
MacroDef[DispGrRS1,DISP4[GrRSc1,7]];

MacroDef[GrRS2, at[Or[#1,#2,8],10,GrRSc2]],
MacroDef[GrRS2Disp,L2Disp],
MacroDef[DispGrRS2,DISP4[GrRSc2,8]];

MacroDef[GrRS3, at[Or[#1,#2,0C],10,GrRSc3]],
MacroDef[GrRS3Disp,L3Disp],
MacroDef[DispGrRS3,DISP4[GrRSc3,0C]];

MacroDef[GrXRoad1Disp,L4Disp],
MacroDef[DispGrXRoad1,DISP4[GrXRoadc1]];

MacroDef[GrTRCa1, at[Or[#1,gr.trc],10,GrXRoadc1]],

MacroDef[GrTRCa2, at[Or[#1,gr.trc],10,GrTRCac2]],
MacroDef[DispGrTRCa2,DISP4[GrTRCac2,gr.trc]];

MacroDef[GrTRCa3, at[Or[#1,2],4,GrTRCac3]],
MacroDef[DispGrTRCa3,DISP2[GrTRCac3,2]];

MacroDef[GrTRCb1, at[Or[#1,2],4,GrTRCbc1]],
MacroDef[DispGrTRCb1,DISP2[GrTRCbc1,Or[#1,2]]];

MacroDef[GrTRCb2, at[Or[#1,8],10,GrTRCbc2]],
MacroDef[GrTRCb2Disp,L4Disp],
MacroDef[DispGrTRCb2,DISP4[GrTRCbc2,8]];

MacroDef[GrTRCb3, at[#1,4,GrTRCbc3]],
MacroDef[GrTRCb3Disp,(Xbus ← #1, XDisp)],
MacroDef[DispGrTRCb3,DISP2[GrTRCbc3]];

MacroDef[GrTRCc1, at[Or[#1,2],4,GrTRCcc1]],
MacroDef[DispGrTRCc1,DISP2[GrTRCcc1,2]];

MacroDef[GrTRCc2, at[Or[Lshift[#1,3],3],10,GrTRCcc2]],
MacroDef[GrTRCc2Disp,L4Disp],
MacroDef[DispGrTRCc2,DISP4[GrTRCcc2,3]];

MacroDef[GrTRCc3, at[Or[#1,#2,8],10,GrTRCcc3]],
MacroDef[GrTRCc3Disp,(L3Disp, Xbus ← #1, XDisp)],
MacroDef[DispGrTRCc3,DISP4[GrTRCcc3,8]];

MacroDef[GrRSd1, at[#1,10,GrXRoadc1]],
MacroDef[DispGrRSd1TRC,CANCELBR[GrRSd1TRC,0C]];

MacroDef[GrRSd2, at[Or[#1,8],10,GrRSdc2]],
MacroDef[DispGrRSd2TRC,GOTO[GrRSd2TRC]],
MacroDef[DispGrRSd2,DISP4[GrRSdc2,8]];

MacroDef[DispGrRSd3,GOTO[GrRSd3]],

MacroDef[GrRD1, at[gr.readDest,10,GrXRoadc1]],
MacroDef[DispGrRD1TRC,CANCELBR[GrRD1TRC,0E]];

MacroDef[GrRD2, at[Or[#1,8],10,GrRDc2]],
MacroDef[GrRD2TRC, at[Xor[Lshift[#1,3],0B],10,GrRDc2TRC]],
MacroDef[GrRD2Disp,L2Disp],
MacroDef[GrRD2TRCDisp,L4Disp],
MacroDef[DispGrRD2,DISP4[GrRDc2,8]];
MacroDef[DispGrRD2TRC,DISP4[GrRDc2TRC,3]];

MacroDef[GrRD3, at[Rshift[#1,1],4,GrRDc3]],
MacroDef[DispGrRD3,DISP2[GrRDc3,Rshift[#1,1]]],
MacroDef[DispGrRD3TRC,GOTO[GrRD3TRC]];

MacroDef[GrCSD1, at[Rshift[#1,1],4,GrCSDc1]],
MacroDef[DispGrCSD1,DISP2[GrCSDc1,Rshift[#1,1]]],
MacroDef[DispGrCSD1TRC,GOTO[GrCSD1TRC]];

MacroDef[GrCSD2, at[#1,10,GrCSDc2]],
MacroDef[GrCSD2Disp,L5Disp],
MacroDef[DispGrCSD2,DISP4[GrCSDc2]];

MacroDef[GrCSD3, at[#1,8,GrCSDc3]],
MacroDef[DispGrCSD3,DISP3[GrCSDc3,#1]];

MacroDef[GrAvgX1, at[Or[#1,#2,6],10,GrAvgXc1]],
MacroDef[DispGrAvgX1,DISP4[GrAvgXc1,6]];

MacroDef[DispGrAvgX2,CANCELBR[GrAvgX2,3]];

MacroDef[DispGrAvgX3,GOTO[GrAvgX3]];

MacroDef[GrWD1, at[Or[#1,#2,6],10,GrXRoadc1]],
MacroDef[GrWD1Disp,L2Disp],
MacroDef[GrWD1DispTRC,L3Disp],
MacroDef[DispGrWD1,DISP4[GrXRoadc1,Or[Rshift[#1,3],6]]];

MacroDef[GrWD2, at[Or[#1,#2,#3],10,GrWDc2]],
MacroDef[GrWD2Set, at[#1,10,GrWDc2]],
MacroDef[GrWD2Disp,L5Disp],
MacroDef[DispGrWD2,DISP4[GrWDc2,#1]],
MacroDef[DispGrWD2Avg,DISP4[GrWDc2,gr.average]];

MacroDef[GrWD3, at[Or[#1,0D],10,GrWDc3]],
MacroDef[DispGrWD3,DISP4[GrWDc3,0D]];

{start cases}
Set[gr.startHighByte,0],
Set[gr.startLowByte,1];

MacroDef[GrSt2, at[Or[#1,#2,#3],10,GrStc2]],
MacroDef[DispGrSt2,DISP4[GrStc2]];

MacroDef[GrSt3, at[Or[#1,0C],10,GrStc3]],
MacroDef[DispGrSt3,DISP4[GrStc3,0C]];

MacroDef[GrStX1, at[Or[#1,6],10,GrStXc1]],
MacroDef[GrStX1Disp,L2Disp],
MacroDef[DispGrStX1,DISP4[GrStXc1,6]];

{single-byte cases}
MacroDef[GrCSDS2, at[Or[#1,#2],10,GrCSDc2]],

MacroDef[DispGrCSDS3Set,GOTO[GrCSDS3Set]],
MacroDef[DispGrCSDS3,GOTO[GrCSDS3]];

MacroDef[DispGrCSDS1,GOTO[GrCSDS1]];

MacroDef[GrCSD2DispAndReset,(L5 ← gr.null, DISP4[GrCSDc2])];

MacroDef[GrSetFix1, at[Or[#1,0B],10,GrSetFixc1]],
MacroDef[GrSetFix1Disp,L4Disp],
MacroDef[DispGrSetFix1,DISP4[GrSetFixc1,0B]];

MacroDef[GrSetFix2, at[#1,10,GrSetFixc2]],
MacroDef[GrSetFix2TRC, at[Or[#1,gr.trc.set],10,GrSetFixc2]],
MacroDef[GrXRoad1Reset,(L4 ← gr.null, DISP4[GrSetFixc2])],
MacroDef[GrTRCc3Reset,(L3 ← gr.null, DISP4[GrSetFixc2])];

MacroDef[GrSetFixWD1Disp,(Xbus ← #1, XDisp, L5 ← gr.set)];

MacroDef[DispGrSetFix3,GOTO[GrSetFix3]];

{page-cross cases}
MacroDef[GrDstPgCr1, at[Or[#1,6],10,GrDstPgCrc1]],
MacroDef[DispGrDstPgCr1,DISP4[GrDstPgCrc1,6]];

MacroDef[DispGrDstPgCr2,GOTO[GrDstPgCr2]];

{loop entry: start cases handled here}
GrayBltStart:
	MAR ← [rhSrcReal, rSrcReal + 0], GrRS3Disp, DispGrSt2,		c1;

	CANCELBR[GrayBltStDecrCount,0D],				c2, GrSt2[gr.fwd,gr.aligned,gr.startHighByte];
	rCount ← rCount - 1, GrCSD2Disp, CANCELBR[GrayBltLowByte,0D],	c2, GrSt2[gr.fwd,gr.aligned,gr.startLowByte];
	rCount ← rCount - 1, GrCSD2Disp, CANCELBR[GrayBltLowByte,0D],	c2, GrSt2[gr.fwd,gr.unaligned.fwd,gr.startHighByte];
	GrStX1Disp, DispGrSt3,						c2, GrSt2[gr.fwd,gr.unaligned.fwd,gr.startLowByte];
	rCount ← rCount - 1, GrCSD2Disp, CANCELBR[GrayBltLowByte,0D],	c2, GrSt2[gr.fwd,gr.trc.unaligned.fwd,gr.startHighByte];
	GrStX1Disp, DispGrSt3,						c2, GrSt2[gr.fwd,gr.trc.unaligned.fwd,gr.startLowByte];
	rCount ← rCount - 1, GrCSD2Disp, CANCELBR[GrayBltHighByte,0D],	c2, GrSt2[gr.rev,gr.aligned,gr.startHighByte];
	CANCELBR[GrayBltStDecrCount,0D],				c2, GrSt2[gr.rev,gr.aligned,gr.startLowByte];
	GrStX1Disp, DispGrSt3,						c2, GrSt2[gr.rev,gr.unaligned.rev,gr.startHighByte];
	rCount ← rCount - 1, GrCSD2Disp, CANCELBR[GrayBltHighByte,0D],	c2, GrSt2[gr.rev,gr.unaligned.rev,gr.startLowByte];

GrayBltStDecrCount:
	rCount ← rCount - 2, CarryBr, GrRS2Disp, GOTO[GrayBltReadSrc],	c3;
	rDstWord ← MD, DispGrStX1,					c3, GrSt3[gr.null];
	rDstWord ← ~MD, DispGrStX1,					c3, GrSt3[gr.inv];

	rGrSrcTemp ← rDstWord LRot8, GOTO[GrayBltStX2],			c1, GrStX1[gr.fwd];
	rhTemp ← rDstWord LRot8, GOTO[GrayBltStX2],			c1, GrStX1[gr.rev];

GrayBltStX2:
	GrRS1Disp, GOTO[GrayBltDecrCount],				c2;

{additional code for single-byte operation (at start or end of loop)}
GrayBltHighByte:
	L5 ← gr.highByteOnly, Xbus ← L1.GrBLTAdjL5, XDisp, DISP4[LinkSet,gr.highByteOnly], c3;
GrayBltLowByte:
	L5 ← gr.lowByteOnly, Xbus ← L1.GrBLTAdjL5, XDisp, DISP4[LinkSet,gr.lowByteOnly], c3;

{LinkSet used to set register; starts and ends in c1}
	Q ← rhGrDispCond, L4Disp,					c2, LinkSetRet[L1.GrBLTAdjL5];
	Ybus ← Q and ~gr.trc.set, YDisp, BRANCH[GrayBltOneByteTRC,$,0B], c3;

	CANCELBR[$,0F],							c1;
	L4 ← gr.readDest, GOTO[GrayBltOneByteRestart],			c2;

GrayBltOneByteTRC:
	L3 ← 0, Xbus ← L1.GrBLTAdjL3, XDisp, DISP4[LinkSet],		c1;
{LinkSet used to set register; starts and ends in c2. Note: L1.GrBLTAdjL3 must equal gr.readDest}
GrayBltOneByteRestart:
	Xbus ← gr.carry, XDisp, GrRS2Disp, GOTO[GrayBltReadSrc],	c3, LinkSetRet[L1.GrBLTAdjL3];

	rTemp ← rDstWord and ~0FF, GrSetFix1Disp, DispGrCSDS3Set,	c2, GrCSDS2[gr.set,gr.lowByteOnly];
	rTemp ← 0, DispGrCSDS3,						c2, GrCSDS2[gr.add,gr.lowByteOnly];
	rTemp ← 0, DispGrCSDS3,						c2, GrCSDS2[gr.subtract,gr.lowByteOnly];
	rTemp ← rDstWord and ~0FF, DispGrCSDS3,				c2, GrCSDS2[gr.average,gr.lowByteOnly];
	Q ← rDstWord and 0FF, GrSetFix1Disp, DispGrCSDS3Set,		c2, GrCSDS2[gr.set,gr.highByteOnly];
	Q ← 0, DispGrCSDS3,						c2, GrCSDS2[gr.add,gr.highByteOnly];
	Q ← 0, DispGrCSDS3,						c2, GrCSDS2[gr.subtract,gr.highByteOnly];
	Q ← rDstWord and 0FF, DispGrCSDS3,				c2, GrCSDS2[gr.average,gr.highByteOnly];

GrCSDS3:
	Xbus ← uGrFlags, XDisp, DispGrCSDS1,				c3;
GrCSDS3Set:
	Xbus ← rhGrDispCond, XDisp, DispGrSetFix1,			c3;

GrCSDS1:
	GrCSD2DispAndReset,						c1;
	GrXRoad1Reset,							c1, GrSetFix1[gr.readDest];
	GrTRCc3Reset,							c1, GrSetFix1[gr.trc];

	GrSetFixWD1Disp[gr.fwd], DispGrSetFix3,				c2, GrSetFix2[gr.writeDest.fwd];
	GrSetFixWD1Disp[gr.rev], DispGrSetFix3,				c2, GrSetFix2[gr.writeDest.rev];
	GrSetFixWD1Disp[gr.fwd], DispGrSetFix3,				c2, GrSetFix2[gr.rotateSource.fwd];
	GrSetFixWD1Disp[gr.rev], DispGrSetFix3,				c2, GrSetFix2[gr.rotateSource.rev];
	GrSetFixWD1Disp[gr.fwd], DispGrSetFix3,				c2, GrSetFix2TRC[gr.fwd];
	GrSetFixWD1Disp[gr.rev], DispGrSetFix3,				c2, GrSetFix2TRC[gr.rev];

GrSetFix3:
	rDstWord ← rTemp or Q, DispGrWD1[gr.setOK],			c3;

{loop starts here}

{Click 1: read source (with case-specific operations in cycle 2)}
GrayBltReadSrc:
	MAR ← rSrcReal ← [rhSrcReal, rSrcReal + 0], GrRS3Disp, DispGrRS2, c1;
	MAR ← rSrcReal ← [rhSrcReal, rSrcReal + 1], GrRS3Disp, DispGrRS2, c1, GrRS1[gr.fwd];
	MAR ← rSrcReal ← [rhSrcReal, rSrcReal - 1], GrRS3Disp, DispGrRS2, c1, GrRS1[gr.rev];

	Q ← ~0FF, GrXRoad1Disp, DispGrRS3,				c2, GrRS2[gr.carry,gr.null];
	Q ← 0FF, GrXRoad1Disp, DispGrRS3,				c2, GrRS2[gr.carry,gr.trc.unaligned.fwd];
	rTemp ← rGrSrcTemp and ~0FF, GrXRoad1Disp, DispGrRS3,		c2, GrRS2[gr.carry,gr.unaligned.fwd];
	Q ← rhTemp, GrXRoad1Disp, DispGrRS3,				c2, GrRS2[gr.carry,gr.unaligned.rev];

	rDstWord ← MD, GrRD2Disp, DispGrXRoad1,				c3, GrRS3[gr.noPgCross,gr.null];
	rDstWord ← ~MD, GrRD2Disp, DispGrXRoad1,			c3, GrRS3[gr.noPgCross,gr.inv];

{Click 1a (TRC only): set up TRC indexes}
GrayBltSetUpTRC:
	rhTemp ← rDstWord LRot0, rTemp ← rDstWord and Q, DispGrTRCa2,	c1, GrTRCa1[gr.trc.aligned];
	rTemp ← rGrSrcTemp LRot8 and Q, XLDisp, DispGrTRCa2,		c1, GrTRCa1[gr.trc.unaligned.fwd];
	rhTemp ← rDstWord LRot8, rTemp ← rDstWord, DispGrTRCa2,		c1, GrTRCa1[gr.trc.unaligned.rev];

	rTemp ← rTemp LRot8, XLDisp, DispGrTRCa3,			c2, GrTRCa2[gr.aligned];
	rGrSrcTemp ← rDstWord LRot8, DispGrTRCa3,			c2, GrTRCa2[gr.trc.unaligned.fwd];
	rTemp ← rTemp and rGr00FF, Xbus ← rTemp and rGr00FF LRot0, XLDisp, DispGrTRCa3, c2, GrTRCa2[gr.unaligned.rev];

	rTemp ← RRot1 rTemp, GrTRCb2Disp, DispGrTRCb1[gr.null],		c3, GrTRCa3[gr.null];
	rTemp ← RRot1 rTemp, GrTRCb2Disp, DispGrTRCb1[gr.trc.inLowByte], c3, GrTRCa3[gr.trc.inLowByte];

{Click 1b (TRC only): read high TRC byte}
GrayBltReadTRCH:
	MAR ← [rhGrTRC, rGrTRC + rTemp], GrTRCb3Disp[gr.trc.inHighByte], DispGrTRCb2, c1, GrTRCb1[gr.trc.inHighByte];
	MAR ← [rhGrTRC, rGrTRC + rTemp], GrTRCb3Disp[gr.trc.inLowByte], DispGrTRCb2, c1, GrTRCb1[gr.trc.inLowByte];

	rTemp ← RShift1 rhTemp, XLDisp, DispGrTRCb3,			c2, GrTRCb2[gr.trc.aligned];
	rTemp ← RShift1 rGrSrcTemp and Q, Xbus ← rGrSrcTemp and Q LRot0, XLDisp, DispGrTRCb3, c2, GrTRCb2[gr.trc.unaligned.fwd];
	rTemp ← RShift1 Q, Xbus ← Q LRot0, XLDisp, DispGrTRCb3,		c2, GrTRCb2[gr.trc.unaligned.rev];

	rDstWord ← MD and rGrFF00, DispGrTRCc1,				c3, GrTRCb3[gr.trc.inHighByte];
	rDstWord ← MD and rGr00FF, GrTRCc2Disp, DispGrTRCc1,		c3, GrTRCb3[gr.trc.inLowByte];

{Click 1c (TRC only): read low TRC byte}
GrayBltReadTRCL:
	MAR ← [rhGrTRC, rGrTRC + rTemp], GrTRCc3Disp[gr.trc.inHighByte], DispGrTRCc2, c1, GrTRCc1[gr.trc.inHighByte];
	MAR ← [rhGrTRC, rGrTRC + rTemp], GrTRCc3Disp[gr.trc.inLowByte], DispGrTRCc2, c1, GrTRCc1[gr.trc.inLowByte];

	rTemp ← rDstWord, GrWD1DispTRC, DispGrTRCc3,			c2, GrTRCc2[gr.trc.inHighByte];
	rTemp ← rDstWord LRot8, GrWD1DispTRC, DispGrTRCc3,		c2, GrTRCc2[gr.trc.inLowByte];

	rhDstWord ← MD, DispGrRD1TRC,					c3, GrTRCc3[gr.null,gr.trc.inLowByte];
	rDstWord ← MD and rGrFF00, GrRD2TRCDisp, DispGrRD1TRC,		c3, GrTRCc3[gr.null,gr.trc.inHighByte];
	rhDstWord ← MD, DispGrWD1[gr.trc.setOK],			c3, GrTRCc3[gr.trc.set,gr.trc.inLowByte];
	rDstWord ← MD and rGrFF00, DispGrRSd1TRC,			c3, GrTRCc3[gr.trc.set,gr.trc.inHighByte];

{Click 1d: combine and rotate source (for dstFunc = set, words not aligned)}
GrayBltRotateSrc:
	rhGrSrcTemp ← rGrSrcTemp ← rDstWord LRot8, DispGrRSd2,		c1, GrRSd1[gr.rotateSource.fwd];
	rhTemp ← rTemp ← rDstWord LRot8, DispGrRSd2,			c1, GrRSd1[gr.rotateSource.rev];
GrRSd1TRC:
	rhDstWord ← rDstWord LRot8, DispGrRSd2TRC,			c1;

	Q ← rhGrSrcTemp, GrWD1Disp, DispGrRSd3,				c2, GrRSd2[gr.unaligned.fwd];
	rTemp ← rTemp and ~0FF, GrWD1Disp, DispGrRSd3,			c2, GrRSd2[gr.unaligned.rev];
GrRSd2TRC:
	Q ← rhDstWord, GrWD1Disp, DispGrRSd3,				c2;

GrRSd3:	rDstWord ← rTemp or Q, DispGrWD1[gr.setOK],			c3;

{Click 2: read destination (rotating source in cycle 2 if necessary) -- skipped if dstFunc = set}
GrayBltReadDst:
	MAR ← [rhDstReal, rDstReal + 0], DispGrRD2,			c1, GrRD1;
GrRD1TRC:
	MAR ← [rhDstReal, rDstReal + 0], DispGrRD2TRC,			c1;

	rhTemp ← rDstWord LRot0, rTemp ← rDstWord and Q, DispGrRD3[gr.aligned], c2, GrRD2[gr.aligned];
	rhGrSrcTemp ← rGrSrcTemp ← rDstWord LRot8, DispGrRD3[gr.unaligned.fwd], c2, GrRD2[gr.unaligned.fwd];
	rhTemp ← rTemp ← rDstWord LRot8, DispGrRD3[gr.unaligned.rev],	c2, GrRD2[gr.unaligned.rev];
	DispGrRD3TRC,							c2, GrRD2TRC[gr.trc.inLowByte];
	rhDstWord ← rDstWord LRot8, DispGrRD3TRC,			c2, GrRD2TRC[gr.trc.inHighByte];

	rDstWord ← MD, GrCSD2Disp, DispGrCSD1[gr.aligned],		c3, GrRD3[gr.aligned];
	rDstWord ← MD, GrCSD2Disp, DispGrCSD1[gr.unaligned.fwd],	c3, GrRD3[gr.unaligned.fwd];
	rDstWord ← MD, GrCSD2Disp, DispGrCSD1[gr.unaligned.rev],	c3, GrRD3[gr.unaligned.rev];
GrRD3TRC:
	rDstWord ← MD, GrCSD2Disp, DispGrCSD1TRC,			c3;

{Click 3: combine destination and source -- skipped if dstFunc = set}
GrayBltCombineSrcAndDst:
	Q ← rhTemp, DispGrCSD2,						c1, GrCSD1[gr.aligned];
	Q ← rhGrSrcTemp, DispGrCSD2,					c1, GrCSD1[gr.unaligned.fwd];
	rTemp ← rTemp and ~0FF, DispGrCSD2,				c1, GrCSD1[gr.unaligned.rev];
GrCSD1TRC:
	Q ← rhDstWord, DispGrCSD2,					c1;

	rTemp ← rDstWord + rTemp, CarryBr, GrWD1Disp, DispGrCSD3[gr.add], c2, GrCSD2[gr.add];
	rTemp ← rDstWord - rTemp, CarryBr, GrWD1Disp, DispGrCSD3[gr.subtract], c2, GrCSD2[gr.subtract];
	Q ← rDstWord + Q, PgCarryBr, GrWD1Disp, DispGrCSD3[gr.average], c2, GrCSD2[gr.average];

	rDstWord ← rTemp + Q, PgCarryBr, GrWD2Disp, DispGrWD1[gr.null], c3, GrCSD3[gr.add];
	rDstWord ← rTemp - Q, PgCarryBr, GrWD2Disp, DispGrWD1[gr.null], c3, GrCSD3[gr.subtract];
	rDstWord ← DARShift1 Q + rTemp, Xbus ← Q + rTemp LRot8, XLDisp, DispGrAvgX1, c3, GrCSD3[gr.average];

{Click 3a: test result (for dstFunc = average, PgCarryBr)}
	rDstWord ← DLShift1 rDstWord, rhDstWord ← rDstWord LRot0, DispGrAvgX2, c1, GrAvgX1[gr.fwd,gr.carry];
	rDstWord ← DLShift1 rDstWord, rhDstWord ← rDstWord LRot0, DispGrAvgX2, c1, GrAvgX1[gr.rev,gr.carry];

GrAvgX2:
	Q ← Q - 0FF - 1, GrWD1Disp, DispGrAvgX3,			c2;

GrAvgX3:
	rDstWord ← DARShift1 Q + rTemp, Xbus ← rhDstWord, XRefBr, DispGrAvgX1, c3;

{Click 4: write dest (with case-specific operations in cycle 2)}
GrayBltWriteDst:
	MAR ← [rhDstReal, rDstReal], rDstReal ← rDstReal + 1, DispGrWD2[gr.noCarryover], c1, GrWD1[gr.fwd,gr.noCarry];
	MAR ← [rhDstReal, rDstReal], rDstReal ← rDstReal + 1, DispGrWD2[gr.carryover], c1, GrWD1[gr.fwd,gr.carry];
	MAR ← [rhDstReal, rDstReal], rDstReal ← rDstReal - 1, DispGrWD2[gr.noCarryover], c1, GrWD1[gr.rev,gr.noCarry];
	MAR ← [rhDstReal, rDstReal], rDstReal ← rDstReal - 1, DispGrWD2[gr.carryover], c1, GrWD1[gr.rev,gr.carry];
	MAR ← [rhDstReal, rDstReal], rDstReal ← rDstReal + 1, DispGrWD2Avg, c1, GrAvgX1[gr.fwd,gr.noCarry];
	MAR ← [rhDstReal, rDstReal], rDstReal ← rDstReal - 1, DispGrWD2Avg, c1, GrAvgX1[gr.rev,gr.noCarry];

	MDR ← rDstWord, GrRS1Disp, DispGrWD3,				c2, GrWD2Set[gr.setOK];
	MDR ← rDstWord ← rTemp or rhDstWord, GrRS1Disp, DispGrWD3,	c2, GrWD2Set[gr.trc.setOK];
	MDR ← rDstWord, GrRS1Disp, DispGrWD3,				c2, GrWD2[gr.add,gr.noPgCarry,gr.noCarryover];
	MDR ← rDstWord ← rDstWord or rGrFF00, GrRS1Disp, DispGrWD3,	c2, GrWD2[gr.add,gr.noPgCarry,gr.carryover];
	MDR ← rDstWord ← rTemp or 0FF, GrRS1Disp, DispGrWD3,		c2, GrWD2[gr.add,gr.pgCarry,gr.noCarryover];
	MDR ← rDstWord ← ~rDstWord xor rDstWord, GrRS1Disp, DispGrWD3,	c2, GrWD2[gr.add,gr.pgCarry,gr.carryover];
	MDR ← rDstWord ← 0, GrRS1Disp, DispGrWD3,			c2, GrWD2[gr.subtract,gr.noPgCarry,gr.noCarryover];
	MDR ← rDstWord ← rTemp and ~0FF, GrRS1Disp, DispGrWD3,		c2, GrWD2[gr.subtract,gr.noPgCarry,gr.carryover];
	MDR ← rDstWord ← rDstWord and 0FF, GrRS1Disp, DispGrWD3,	c2, GrWD2[gr.subtract,gr.pgCarry,gr.noCarryover];
	MDR ← rDstWord, GrRS1Disp, DispGrWD3,				c2, GrWD2[gr.subtract,gr.pgCarry,gr.carryover];
	MDR ← rDstWord, GrRS1Disp, DispGrWD3,				c2, GrWD2[gr.average,gr.noCarryover,gr.bit0];
	MDR ← rDstWord ← rDstWord xor 80, GrRS1Disp, DispGrWD3,		c2, GrWD2[gr.average,gr.noCarryover,gr.bit1];

GrayBltDecrCount:
	rCount ← rCount - 2, CarryBr, GrRS2Disp, DispGrRS1,		c3, GrWD3[gr.noPgCross];

{loop end conditions}

GrayBltSrcPgCr:
	rDstWord ← rGrSrcTemp, L2Disp, CANCELBR[GrayBltSrcPageCross,0F], c3, GrRS3[gr.pgCross,gr.null];
	rDstWord ← rGrSrcTemp, L2Disp, CANCELBR[GrayBltSrcPageCross,0F], c3, GrRS3[gr.pgCross,gr.inv];

GrayBltSrcPageCross:
	uGrSaveCarry ← ~rDstWord xor rDstWord, BRANCH[$,GrayBltSrcPCRev,7], c1;
GrayBltSrcPCFwd:
	rTemp ← LRot1 rGr00FF + 1, L0 ← L0.GrBLTLoop, CALL[GrSrcPageCross], c2;
GrayBltSrcPCRev:
	rTemp ← ~rTemp xor rTemp, L0 ← L0.GrBLTLoop, CALL[GrSrcPageCross], c2;

{GrSrcPageCross calls MapSrc, which returns here}
	rGrSrcTemp ← rDstWord, Xbus ← uGrSaveCarry, XRefBr, GrRS2Disp, GOTO[GrayBltReadSrc], c3, MapSrcRet[L0.GrBLTLoop];

GrayBltDstPgCr:
	rTemp ← 0FF + 1, DispGrDstPgCr1,				c3, GrWD3[gr.pgCross];

	MAR ← [rhDstReal, 0FF + 0], DispGrDstPgCr2,			c1, GrDstPgCr1[gr.fwd];
	MAR ← [rhDstReal, 0 + 0], DispGrDstPgCr2,			c1, GrDstPgCr1[gr.rev];

GrDstPgCr2:
	MDR ← rDstWord, L0 ← L0.GrBLTLoop,				c2;
	L2Disp,								c3;

	rDstWord ← rGrSrcTemp, BRANCH[$,GrayBltDstPCRev,7],		c1;
GrayBltDstPCFwd:
	rTemp ← LShift1 rTemp, SE ← 0, CALL[GrDstPageCross],		c2;
GrayBltDstPCRev:
	rTemp ← ~rTemp xor rTemp, CALL[GrDstPageCross],			c2;

{GrDstPageCross calls MapDst, which returns here}
	rGrSrcTemp ← rDstWord,						c3, MapDstRet[L0.GrBLTLoop];

	Noop,								c1;
	GrRS1Disp, GOTO[GrayBltDecrCount],				c2;

GrayBltCountNegative:
	[] ← rCount + 1, ZeroBr, BRANCH[GrayBltFinishItem,GrayBltFinishItemSrcPageCross,0D], c2, GrRS2[gr.noCarry,gr.null];
	[] ← rCount + 1, ZeroBr, BRANCH[GrayBltFinishItem,GrayBltFinishItemSrcPageCross,0D], c2, GrRS2[gr.noCarry,gr.trc.unaligned.fwd];
	[] ← rCount + 1, ZeroBr, BRANCH[GrayBltFinishItem,GrayBltFinishItemSrcPageCross,0D], c2, GrRS2[gr.noCarry,gr.unaligned.fwd];
	[] ← rCount + 1, ZeroBr, BRANCH[GrayBltFinishItem,GrayBltFinishItemSrcPageCross,0D], c2, GrRS2[gr.noCarry,gr.unaligned.rev];

GrayBltFinishItem:
	L2Disp, BRANCH[GrayBltEndItem,$],				c3;

	BRANCH[$,GrayBltFinishItemRev,7],				c1;
GrayBltFinishItemFwd:
	rCount ← 0, L5Disp, GOTO[GrayBltHighByte],			c2;
GrayBltFinishItemRev:
	rCount ← 0, L5Disp, GOTO[GrayBltLowByte],			c2, at[0F,10,GrBLTFPC];

GrayBltFinishItemSrcPageCross:
	L2Disp, BRANCH[GrayBltEndItemSrcPageCross,$],			c3;

	uGrSaveCarry ← 0, DISP4[GrBLTFPC,1],				c1;
	rTemp ← LRot1 rGr00FF + 1, L0 ← L0.GrBLTLoop, CALL[GrSrcPageCross], c2, at[1,10,GrBLTFPC];
	rTemp ← ~rTemp xor rTemp, L0 ← L0.GrBLTLoop, CALL[GrSrcPageCross], c2, at[9,10,GrBLTFPC];
	rCount ← 0, L5Disp, GOTO[GrayBltHighByte],			c2, at[3,10,GrBLTFPC];
	rCount ← 0, L5Disp, GOTO[GrayBltHighByte],			c2, at[5,10,GrBLTFPC];

{Item loop for GRAYSUM
	[rhSrcReal, rSrcReal] contain the real word address for the next source byte
	rCount contains the number of bytes yet to process for this item
	[rSumHigh, rSumLow] contain the accumulated sum as a long unsigned number
	r(h)Temp and Q are used for temporary storage
	rGrFF00 = 0FF00}

SumItemStart:
	MAR ← [rhSrcReal, rSrcReal + 0], BRANCH[$,SumItemStartOddByte,2], c1;
	rCount ← rCount - 2, CarryBr, GOTO[SumIa],			c2;
SumItemStartOddByte:
	rCount ← rCount - 1,						c2;
	rTemp ← MD,							c3;

	rTemp ← rTemp and 0FF,						c1;
	GOTO[SumIc],							c2;

{loop starts here}
SumItemLoop:
	MAR ← rSrcReal ← [rhSrcReal, rSrcReal + 1], BRANCH[$,SumCarry],	c1;
	rCount ← rCount - 2, CarryBr, BRANCH[$,SumPC,1],		c2;
SumIa:	rhTemp ← MD, rTemp ← MD and rGrFF00, BRANCH[SumFinishItem,$],	c3;

SumIb:	Q ← rhTemp,							c1;
	rTemp ← rTemp LRot8 + Q,					c2;
SumIc:	rSumLow ← rSumLow + rTemp, CarryBr, GOTO[SumItemLoop],		c3;

SumCarry:
	rSumHigh ← rSumHigh + 1, BRANCH[$,SumPCCarry,1],		c2;
	rhTemp ← MD, rTemp ← MD and rGrFF00,				c3;

	[] ← rCount, ZeroBr,						c1;
	rCount ← rCount - 2, CarryBr, BRANCH[$,SumIEnd],		c2;
	BRANCH[SumFinishItem,SumIb],					c3;

{loop end conditions}

SumPC:
	rCount ← rCount + 2, L0 ← L0.GrSumLoop, CANCELBR[SumPCa,1],	c3;
SumPCCarry:
	L0 ← L0.GrSumLoop,						c3;

SumPCa:
	uGrSumLow ← rSumLow, GOTO[ThreshSrcPageCross],			c1;
{	rTemp ← LShift1 r0100, SE ← 0, CALL[GrSrcPageCross],		c2;}

{GrSrcPageCross calls MapSrc, which returns here}
	rSumLow ← uGrSumLow, GOTO[SumItemStart],			c3, MapSrcRet[L0.GrSumLoop];

SumIEnd:
	CANCELBR[SumEndItem,1],						c3;

SumFinishItem:
	[] ← rCount + 1, ZeroBr,					c1;
	rTemp ← rTemp LRot8, BRANCH[$,SumFIa],				c2;
	GOTO[SumEndItem],						c3;
SumFIa:
	rSumLow ← rSumLow + rTemp, CarryBr, GOTO[SumEndItem],		c3;

{Item loop for GRAYTHRSHLD
	[rhSrcReal, rSrcReal] contain the real word address for the next source byte
	[rhDstReal, rDstReal] contain the real word address for the next destination bit
	rCount contains the number of pixels yet to process for this item
	rThreshold contains the threshold value
	rDstWord contains the accumulated destination bits for the current word
	rTemp, rhTemp, and Q are used for temporary storage
	rGrFF00 = 0FF00}

ThreshItemStart:
	MAR ← [rhSrcReal, rSrcReal + 0], BRANCH[$,ThreshItemStartOddByte,2], c1;
	GOTO[ThreshIb],							c2;
ThreshItemStartOddByte:
	rTemp ← 0FF,							c2;
	rTemp ← MD and rTemp,						c3;

	rCount ← rCount - 1,						c1;
	GOTO[ThreshId],							c2;

{loop starts here}
ThreshItemLoop:
	MAR ← rSrcReal ← [rhSrcReal, rSrcReal + 1],			c1;
ThreshIa:
	rDstWord ← DLShift1 rDstWord, NegBr, BRANCH[$,ThreshSrcPC,1],	c2;
ThreshIb:
	rhTemp ← MD, rTemp ← MD and rGrFF00, BRANCH[$,ThreshWDa],	c3;

	rTemp ← rTemp LRot8,						c1;
ThreshIc:
	rCount ← rCount - 2, CarryBr,					c2;
	Q ← rThreshold - rTemp, BRANCH[ThreshFinishItem,$],		c3;

	rTemp ← rhTemp,							c1;
	rDstWord ← DLShift1 rDstWord, NegBr,				c2;
ThreshId:
	Q ← rThreshold - rTemp, BRANCH[ThreshItemLoop,ThreshWDb],	c3;

ThreshWDa:
	MAR ← [rhDstReal, rDstReal + 0],				c1;
	MDR ← rDstWord, rDstReal ← rDstReal + 1, PgCarryBr,		c2;
	rDstWord ← 1, BRANCH[$,ThreshDstPCa],				c3;

	rTemp ← rTemp LRot8, GOTO[ThreshIc],				c1;

ThreshWDb:
	MAR ← [rhDstReal, rDstReal + 0],				c1;
	MDR ← rDstWord, rDstReal ← rDstReal + 1, PgCarryBr,		c2;
	rDstWord ← 1, BRANCH[$,ThreshDstPCb],				c3;

	MAR ← rSrcReal ← [rhSrcReal, rSrcReal + 1], GOTO[ThreshIa],	c1;

{loop end conditions}

ThreshSrcPC:
	L0 ← L0.GrThresholdLoop, BRANCH[$,ThreshSrcPCWriteDst],		c3;

	uGrDstWord ← rDstWord, GOTO[ThreshSrcPageCross],		c1;
ThreshSrcPCWriteDst:
	MAR ← [rhDstReal, rDstReal + 0],				c1;
	MDR ← rDstWord, rDstReal ← rDstReal + 1, PgCarryBr,		c2;
	rDstWord ← 1, L0 ← L0.GrThresholdLoop, BRANCH[$,ThreshBothPageCross], c3;

	uGrDstWord ← rDstWord, GOTO[ThreshSrcPageCross],		c1;
ThreshBothPageCross:
	uGrDstWord ← rDstWord,						c1;
ThreshSrcPageCross:
	rTemp ← LShift1 r0100, SE ← 0, CALL[GrSrcPageCross],		c2;

{GrSrcPageCross calls MapSrc, which returns here}
	GOTO[ThreshItemStart],						c3, MapSrcRet[L0.GrThresholdLoop];
	GOTO[ThreshDstPCa],						c3, MapSrcRet[L0.GrThresholdExtra];

ThreshDstPCa:
	uGrDstWord ← rDstWord, L0 ← L0.GrThresholdLoop,			c1;
	rTemp ← LShift1 r0100, SE ← 0, CALL[GrDstPageCross],		c2;
{GrDstPageCross calls MapDst, which returns here}
	GOTO[ThreshItemStart],						c3, MapDstRet[L0.GrThresholdLoop];

ThreshDstPCb:
	uGrDstWord ← rDstWord, L0 ← L0.GrThresholdExtra,		c1;
	rTemp ← LShift1 r0100, SE ← 0, CALL[GrDstPageCross],		c2;
{GrDstPageCross calls MapDst, which returns here}
	rCount ← rCount + 1, Xbus ← 1, XLDisp, GOTO[ThreshItemStart],	c3, MapDstRet[L0.GrThresholdExtra];

ThreshFinishItem:
	[] ← rCount + 1, ZeroBr,					c1;
	BRANCH[$,ThreshFIa],						c2;
	GOTO[ThreshEndItem],						c3;
ThreshFIa:
	rDstWord ← DLShift1 rDstWord, NegBr, GOTO[ThreshEndItem],	c3;

{Common routines for handling page cross -- return through L0}

GrSrcPageCross:
	rVirtualL ← uGrCurrSrcLow, L1 ← L1.GrSrcPageCross,		c3;

 	rhVirtualH ← uGrCurrSrcHigh,					c1;
GrPageCross:
	[] ← rTemp, NegBr,						c2;
 	rVirtualL ← rVirtualL and ~0FF, BRANCH[BumpByteVA,BumpByteVANeg], c3;

{BumpByteVA starts in c1, ends in c2}
	uGrCurrSrcLow ← rVirtualL, MesaIntBr,				c3, BumpByteVARet[L1.GrSrcPageCross];

	uGrCurrSrcHigh ← Q, L0Disp, BRANCH[GrPCMapSrc,$],		c1;
	DISP4[MapGrSrcInt],						c2;

GrPCMapSrc:
	CANCELBR[$,0F],							c2;
	CALL[MapSrc],							c3;
{MapSrc starts in c1, ends in c2, returns to GrSrcPageCross caller}

GrDstPageCross:
	rVirtualL ← uGrCurrDstLow, L1 ← L1.GrDstPageCross,		c3;

 	rhVirtualH ← uGrCurrDstHigh, GOTO[GrPageCross],			c1;
{	[] ← rTemp, NegBr,						c2;
 	rVirtualL ← rVirtualL and ~0FF, BRANCH[BumpByteVA,BumpByteVANeg],c3;}

{BumpByteVA starts in c1, ends in c2}
	uGrCurrDstLow ← rVirtualL,					c3, BumpByteVARet[L1.GrDstPageCross];

	uGrCurrDstHigh ← Q,						c1;
	Noop,								c2;
	CALL[MapDst],							c3;
{MapDst starts in c1, ends in c2, returns to GrDstPageCross caller}