--mstone May 2, 1981  12:57 PM
--Low level screen functions

DIRECTORY
	ScreenDefs: FROM "ScreenDefs",
	PointDefs: FROM "PointDefs" USING [ScrPt,X,Y],
	InlineDefs: FROM "InlineDefs",
	ImageDefs: FROM "ImageDefs",
	MiscDefs: FROM "MiscDefs",
	BitBltDefs: FROM "BitBltDefs",
	GriffinMemoryDefs: FROM "GriffinMemoryDefs",
	DisplayDefs: FROM "DisplayDefs",
	EncodingDefs: FROM "EncodingDefs",
	SegmentDefs: FROM "SegmentDefs",
	HBltDefs: FROM "HBltDefs",
	AltoDefs: FROM "AltoDefs",
	AltoDisplay: FROM "AltoDisplay",
	GriffinDefs: FROM "GriffinDefs";

ScreenFns: PROGRAM IMPORTS InlineDefs,GriffinMemoryDefs, DisplayDefs,MiscDefs,SegmentDefs,
ImageDefs,GriffinDefs, BitBltDefs, HBltDefs EXPORTS ScreenDefs =
BEGIN OPEN ScreenDefs,PointDefs;
memConfig: SegmentDefs.MemoryConfig ← SegmentDefs.GetMemoryConfig[];
alto: BOOLEAN ← (memConfig.AltoType=AltoIIXM);

CUItem: ImageDefs.CleanupItem  ←	[NIL,ImageDefs.CleanupMask[Finish]+ImageDefs.CleanupMask[Abort],Cleanup];

currentScreen: Screen ← [[0,0,0,0,NIL], 0,0,0,0];
TY,BY,LX,RX: INTEGER;
initTY,initBY,initLX,initRX: INTEGER;
CLIP: BOOLEAN ← TRUE;
CULL: BOOLEAN ← FALSE;
FNC: BitBltDefs.BBoperation;

HBltBBT: HBltDefs.HBptr;
BBT,FillBBT,hgridBBT,vgridBBT,ScanBBT: BitBltDefs.BBptr;
BLTBank: SegmentDefs.BankIndex ← 0; 

DCBBank: POINTER TO CARDINAL = LOOPHOLE[177751B];
myDCB: POINTER TO AltoDisplay.LongDCB;
color: DisplayDefs.Background ← white;
FIRSTSCREEN: BOOLEAN ← TRUE;
allOnes: ARRAY[0..3] OF CARDINAL ← ALL[177777B];
hgrid: ARRAY[0..3] OF CARDINAL ← [100200B,77577B ,100200B,0];

--returns the current screen
GetCurrentScreen: PUBLIC PROCEDURE RETURNS[ScreenPtr] =	
BEGIN
RETURN[@currentScreen];
END;

--clears the current screen
ClearScreen: PUBLIC PROCEDURE =	
BEGIN
tl: ScrPt ← [LX,TY];
br: ScrPt ← [RX,BY];
EraseBox[tl,br];
END;

--defines the current screen
SetScreen: PUBLIC PROCEDURE [screen: ScreenPtr] =	
BEGIN OPEN AltoDisplay;
currentScreen ← screen↑;
BLTBank ← currentScreen.bitmap.bank;
hgridBBT.dbmr ← vgridBBT.dbmr ← BBT.dbmr ← ScanBBT.dbmr ← FillBBT.dbmr ← HBltBBT.dbmr
	← screen.bitmap.nWords;
IF alto THEN BEGIN
	hgridBBT.dbca ← vgridBBT.dbca ← BBT.dbca ← ScanBBT.dbca ←
		FillBBT.dbca ← HBltBBT.dbca ← screen.bitmap.bits;
	hgridBBT.destalt ← vgridBBT.destalt ← BBT.destalt ← ScanBBT.destalt ←
		FillBBT.destalt ← HBltBBT.destalt ← screen.bitmap.bank#0;
	hgridBBT.unused ← vgridBBT.unused ← BBT.unused ← ScanBBT.unused ←
		FillBBT.unused ← screen.bitmap.bank;
	END
ELSE BEGIN
	hgridBBT.sbca ← BBT.sbca ← ScanBBT.sbca ← FillBBT.sbca ← NIL;
	hgridBBT.dbca ← vgridBBT.dbca ← BBT.dbca ← ScanBBT.dbca ←
		FillBBT.dbca ← NIL;
	hgridBBT.dlbca ← vgridBBT.dlbca ← BBT.dlbca ← ScanBBT.dlbca ←
		FillBBT.dlbca ← MakeLongPointer[screen.bitmap.bits,BLTBank];
	vgridBBT.slbca ← BASE[vgrid];
	END;

hgridBBT.ptrs ← vgridBBT.ptrs ← BBT.ptrs ← ScanBBT.ptrs ←
	FillBBT.ptrs ← (IF alto THEN short ELSE long);

initLX ←LX ← currentScreen.lx;
initRX ←RX ← currentScreen.rx;
initTY ←TY ← currentScreen.ty;
initBY ←BY ← currentScreen.by;
ClearScreen[];
myDCB ← AllocateEven[SIZE[LongDCB]];
myDCB↑←[next: DCBnil, resolution: high, background: white, indenting: 0,width: screen.bitmap.nWords, bitmap: (IF alto THEN screen.bitmap.bits ELSE LOOPHOLE[177423B]), tag: (IF alto THEN short ELSE long), height: screen.bitmap.nLines/2, longBitmap: (IF alto THEN NIL ELSE MakeLongPointer[screen.bitmap.bits,BLTBank])];
DisplayDefs.DisplayOff[color];
DCBchainHead↑←LOOPHOLE[myDCB];
IF alto THEN BEGIN
	DCBBank↑ ← 4*screen.bitmap.bank;
	ImageDefs.AddCleanupProcedure[@CUItem];
	END;
END;


--do this before exiting, or you won't see display in executive
Cleanup: ImageDefs.CleanupProcedure =
BEGIN
AltoDisplay.DCBchainHead↑←NIL;
DCBBank↑ ← 0;
END;

--changes clipping edges of current screen.  Must stay inside initial clip window
SetClipEdges: PUBLIC PROCEDURE [tl,br: PointDefs.ScrPt] = {
--clip clipping window
	[tl,br] ← ClipToScreen[tl,br];
	IF br[X]<tl[X] OR br[Y]<tl[Y] THEN CULL ← TRUE
	ELSE {CULL ← FALSE;
		currentScreen.lx ← LX ← tl[X];
		currentScreen.rx ← RX ← br[X];
		currentScreen.ty ← TY ← tl[Y];
		currentScreen.by ← BY ← br[Y];
		};
	};

ClipToScreen: PUBLIC PROC [tl,br: ScrPt] RETURNS [ScrPt, ScrPt] =
BEGIN
IF tl[X]>RX OR br[X]<LX OR tl[Y]>BY OR br[Y]<TY
	THEN {RETURN[[0,0],[-1,-1]]};
IF tl[X] < initLX THEN tl[X]  ← initLX;
IF br[X] >initRX THEN br[X] ← initRX;
IF tl[Y] < initTY THEN tl[Y]  ← initTY;
IF br[Y] >initBY THEN br[Y] ← initBY;
RETURN[tl,br];
END;

ClipPointToScreen: PUBLIC PROC [pt: ScrPt] RETURNS [ScrPt] =
BEGIN
IF pt[X] < initLX THEN pt[X]  ← initLX;
IF pt[X] >initRX THEN pt[X] ← initRX;
IF pt[Y] < initTY THEN pt[Y]  ← initTY;
IF pt[Y] >initBY THEN pt[Y] ← initBY;
RETURN[pt];
END;

ResetClipEdges: PUBLIC PROCEDURE =
BEGIN OPEN PointDefs;
tl: PointDefs.ScrPt ← [initLX,initTY];
br: PointDefs.ScrPt ← [initRX,initBY];
SetClipEdges[tl,br];
END;

ClipOn: PUBLIC PROCEDURE = 
BEGIN
CLIP ← TRUE;
END;

PrimeClipper: PUBLIC PROC [tl,br: PointDefs.ScrPt] RETURNS [ClipperState] = 
BEGIN
IF tl[X] > RX OR tl[Y] > BY OR br[X] < LX OR br[Y] < TY THEN RETURN[cull];
IF tl[X] IN [LX..RX] AND tl[Y] IN [TY..BY]
	AND br[X] IN [LX..RX] AND br[Y] IN [TY..BY]
		THEN {CLIP ← FALSE; RETURN[inside]};
CLIP ← TRUE;
RETURN[clip];
END;

SetFunction: PUBLIC PROCEDURE [fnc: BitBltDefs.BBoperation] =
BEGIN
FNC ← fnc;
ScanBBT.function ← HBltBBT.function ←
	(IF FNC=erase THEN erase ELSE replace);
END;

-- This routine "scan converts" a chain encoding and draws 

left: INTEGER = 0;
right: INTEGER = 1;
CodedBrush: TYPE = ARRAY [left..right] OF BrushSide;
BrushSide: TYPE =DESCRIPTOR FOR ARRAY OF INTEGER;
CodedBrushes: ARRAY [1..8] OF CodedBrush;
CurrentBrush: CodedBrush;

LineInProgress: BOOLEAN ← FALSE;
xleft, xright: ARRAY [0..7] OF INTEGER;
leftBrush: BrushSide;
rightBrush: BrushSide;
brushSize: INTEGER;
topIndex: INTEGER;
topY: INTEGER; -- top of brush
centerX: INTEGER;
LastLinePointX: INTEGER;
LastLinePointY: INTEGER;

DrawNextDot: PUBLIC PROCEDURE[pt: PointDefs.ScrPt]=
BEGIN OPEN PointDefs;
dx, dy, brushIndex, rangeIndex, temp: INTEGER;

-- initialize the xranges arrays when starting a line
IF NOT LineInProgress THEN
  BEGIN
	leftBrush ← CurrentBrush[left];
	rightBrush ← CurrentBrush[right];
	brushSize ← LENGTH[leftBrush];
	topIndex ← 0;
	topY ← pt[Y] - brushSize/2; -- top of brush
	centerX ← pt[X];
	LineInProgress ← TRUE;
	FOR brushIndex IN [0..brushSize) DO 
	    xleft[brushIndex] ← centerX + leftBrush[brushIndex];
	    xright[brushIndex] ← centerX + rightBrush[brushIndex];
	    ENDLOOP;
	LastLinePointX ← pt[X];
	LastLinePointY ← pt[Y];
	RETURN;
	END;

-- main loop for processing a point

dx ← pt[X] - LastLinePointX;
dy ← pt[Y] - LastLinePointY;
IF dx NOT IN [-1..1] THEN BEGIN
		EndLine[];
		DrawNextDot[pt];
		RETURN;
		END;
centerX ← centerX + dx;
SELECT dy FROM
    -1 => BEGIN -- move brush up
	    IF (topIndex ← topIndex -1) < 0
		THEN topIndex ← topIndex + brushSize;
		-- topIndex now points to bottom of xRanges
	    -- now write out lowest scan line.
	    DrawHLine[(topY + brushSize -1), xleft[topIndex],
		xright[topIndex]];
	    -- and reset for new entries
	    xleft[topIndex] ← 606;	
	    xright[topIndex] ← 0;	
	    topY ← topY - 1;
	    END;

    1 => BEGIN -- move brush down
	    -- first write out top scan line
	    DrawHLine[topY, xleft[topIndex], xright[topIndex]];
	    -- and reset for new entries
	    xleft[topIndex] ← 606;	
	    xright[topIndex] ← 0;	
	    IF (topIndex ← topIndex +1) >= brushSize
		THEN topIndex ← 0;
	    topY ← topY + 1;
	    END;
    0 => NULL;
    ENDCASE => BEGIN
		EndLine[];
		DrawNextDot[pt];
		RETURN;
		END;
rangeIndex ← topIndex;
FOR  brushIndex IN [0..brushSize)
    DO   IF xleft[rangeIndex] >
		(temp ← centerX + leftBrush[brushIndex])
	    THEN xleft[rangeIndex] ← temp;
	IF xright[rangeIndex] <
		(temp ← centerX + rightBrush[brushIndex])
	    THEN xright[rangeIndex] ← temp;
	IF (rangeIndex ← rangeIndex + 1) >= brushSize
		THEN rangeIndex ← 0;
	ENDLOOP;
LastLinePointX ← pt[X];
LastLinePointY ← pt[Y];
END;

EndLine: PUBLIC PROCEDURE =
BEGIN
-- cleanup remaining brush lines when finished
brushIndex: INTEGER;

IF NOT LineInProgress THEN RETURN;
FOR brushIndex IN [0..brushSize)
	DO
	DrawHLine[topY, xleft[topIndex], xright[topIndex]];
	topY ← topY + 1;
	IF (topIndex ← topIndex +1) >= brushSize
		THEN topIndex ← 0;
	ENDLOOP;
LineInProgress ← FALSE;
END;



--line drawing stuff.  Keeps info about last point, so can have MoveTo, DrawTo
--type operation.  Does brute force clipping, including culling.
--all lines have width, so some of this looks more like polygons
JNC: JunctionType;
Width: INTEGER;
Grey: GreyType;
fEnd,lEnd: EndType;
XY: PointDefs.ScrPt;

SetLineParms: PUBLIC PROCEDURE
	[junction: JunctionType,
	width: INTEGER,
	grey: GreyType,
	firstEnd,lastEnd: EndType]=
BEGIN
JNC ← junction;
Width ← width;
CurrentBrush ← CodedBrushes[Width];
Grey ← grey;
fEnd ← firstEnd;
lEnd ← lastEnd;
END;

StartLine: PUBLIC PROCEDURE[pt: PointDefs.ScrPt] =
BEGIN OPEN PointDefs;
XY ← pt;
DrawNextDot[XY];
END;

--uses junction type
DrawTo: PUBLIC PROCEDURE[pt: PointDefs.ScrPt] =
BEGIN OPEN PointDefs;
DrawToShort[pt];
EndLine[];
END;

--splines use this one alot, uses round jnc
DrawToShort: PUBLIC PROCEDURE[pt: PointDefs.ScrPt] =
BEGIN OPEN PointDefs;
--makes jump to DrawDot (a common case) as fast as possible
IF (ABS[pt[X]-XY[X]]<=1 AND ABS[pt[Y]-XY[Y]] <=1) THEN DrawNextDot[pt]
ELSE BEGIN
E: INTEGER ←0;
S: INTEGER ← Y;
L: INTEGER ← X;
IncS: INTEGER ← 1;	--point increments
IncL: INTEGER ← 1;
EIncS: INTEGER ← pt[X]-XY[X];	--error increments. S,L are direction, not size
EIncL: INTEGER ← pt[Y]-XY[Y];

--IncL goes with EIncS, and IncS goes with EIncL in this section
IF ABS[EIncS] < ABS[EIncL] THEN BEGIN	--45-90 octant
	T: INTEGER ← L;
	EInc: INTEGER ← EIncL;
	L ← S; EIncL ← EIncS;
	S ← T; EIncS ← EInc;
	END;
--following assures that S error increment is positive, the other is negative, and the point increments 
--are the correct signs
IF EIncL < 0 THEN IncS ← -1 ELSE EIncL ← -EIncL;
IF EIncS < 0 THEN BEGIN IncL ← -1; EIncS ← - EIncS; END;
--now IncL goes with EIncL and IncS with EIncS
UNTIL pt[L] = XY[L]  DO
	E ← E + EIncL;
	XY[L] ← XY[L]+IncL;
	IF ABS[E + EIncS] < ABS[E] THEN BEGIN
		XY[S] ← XY[S]+IncS;
		E ← E+EIncS;
		END;
	DrawNextDot[XY];
	ENDLOOP;	
XY ← pt;	--should be true anyway
END;	--to make the jump to DrawDot as fast as possible
END;

--returns true if point is inside window.  
--assumes point will have a 8 by 8 bitmap blitted around it
DrawSingleDot: PUBLIC PROCEDURE[pt: PointDefs.ScrPt] =
BEGIN
DrawNextDot[pt];
EndLine[];
END;


BLTBlockInScreen: PUBLIC PROCEDURE[src: BlockPtr, tl: PointDefs.ScrPt, fnc: BitBltDefs.BBoperation]=
BEGIN OPEN PointDefs;
dlx,dty,drx,dby: INTEGER;
--I want edges
	dlx ← tl[X]; dty ← tl[Y];
	drx ← dlx + src.w -1;
	dby ← dty + src.h -1;
IF CLIP THEN BEGIN
	IF CULL THEN RETURN;
	IF dlx>RX OR drx<LX OR dty>BY OR dby<TY THEN RETURN;	--cull
	IF dlx < LX THEN dlx  ← LX;
	IF drx >RX THEN drx ← RX;
	IF dty < TY THEN dty  ← TY;
	IF dby >BY THEN dby ← BY;
	END;

IF  currentScreen.bitmap.bank#0 AND BLTBank#currentScreen.bitmap.bank
	THEN MiscDefs.CallDebugger["Impossible XBITBLT"];

BBT.sourcealt ← src.bitmap.bank#0;
BBT.destalt ←currentScreen.bitmap.bank#0 ;

BBT.sourcetype ← block;
BBT.function ← fnc;

BBT.dlx ← dlx;
BBT.dty ← dty;
BBT.dw ← drx-dlx+1;
BBT.dh ← dby - dty +1;

IF alto THEN BBT.sbca ← src.bitmap.bits
ELSE BBT.slbca ← MakeLongPointer[src.bitmap.bits,src.bitmap.bank];
BBT.sbmr ← src.bitmap.nWords;
BBT.slx ← src.lx+(dlx-tl[X]);
BBT.sty ← src.ty+(dty-tl[Y]);


BitBltDefs.BITBLT[BBT];
END;

BLTGreyBlockInScreen: PUBLIC PROCEDURE[src: BlockPtr, tl: PointDefs.ScrPt, fnc: BitBltDefs.BBoperation,grey: GreyType]=
BEGIN OPEN PointDefs;
dlx,dty,drx,dby: INTEGER;
--I want edges
	dlx ← tl[X]; dty ← tl[Y];
	drx ← dlx + src.w -1;
	dby ← dty + src.h -1;
IF CLIP THEN BEGIN
	IF CULL THEN RETURN;
	IF dlx>RX OR drx<LX OR dty>BY OR dby<TY THEN RETURN;	--cull
	IF dlx < LX THEN dlx  ← LX;
	IF drx >RX THEN drx ← RX;
	IF dty < TY THEN dty  ← TY;
	IF dby >BY THEN dby ← BY;
	END;
IF src.bitmap.bank#0 AND BLTBank#0
	THEN MiscDefs.CallDebugger["Impossible BitBlt"];

BBT.sourcealt ← src.bitmap.bank#0;
BBT.destalt ← BLTBank#0;
BBT.sourcetype ← andgray;
BBT.function ← fnc;

IF alto THEN BBT.sbca ← src.bitmap.bits
ELSE BBT.slbca ← MakeLongPointer[src.bitmap.bits,src.bitmap.bank];
BBT.sbmr ← src.bitmap.nWords;

BBT.dlx ← dlx;
BBT.dty ← dty;
BBT.dw ← drx-dlx+1;
BBT.dh ← dby - dty +1;

BBT.slx ← src.lx+(dlx-tl[X]);
BBT.sty ← src.ty+(dty-tl[Y]);

BBT.gray0 ← grey[(dty+1) MOD 4];
BBT.gray1 ← grey[(dty+2) MOD 4];
BBT.gray2 ← grey[(dty+3) MOD 4];
BBT.gray3 ← grey[dty MOD 4];

BitBltDefs.BITBLT[BBT];
END;

fillGrey: GreyType;
SetFillParms: PUBLIC PROCEDURE [grey: GreyType] =
BEGIN
fillGrey ← grey;
END;

HFill: PUBLIC PROCEDURE[y,lx,dx: INTEGER] =
BEGIN
HLine[y,lx,lx+dx-1, fillGrey];
END;

DrawHLine: PROCEDURE[y,lx,rx: INTEGER] =
BEGIN
HLine[y,lx,rx, Grey];
END;

HLine: PROCEDURE[y,lx,rx: INTEGER, grey: GreyType] =
BEGIN
IF CLIP THEN BEGIN
	IF CULL THEN RETURN;
	IF lx>RX OR rx<LX OR y>BY OR y<TY THEN RETURN;	--cull
	IF lx < LX THEN lx  ← LX;
	IF rx >RX THEN rx ← RX;
	END;
IF alto THEN {
	HBltBBT.gray ← grey[(y+1) MOD 4]; 
	HBltBBT.x1 ← lx;
	HBltBBT.y ← y;
	HBltBBT.x2 ← rx;
	HBltDefs.XHBlt[HBltBBT,BLTBank];
	}
ELSE {
	ScanBBT.gray0 ← grey[(y+1) MOD 4]; 
	ScanBBT.dlx ← lx;
	ScanBBT.dty ← y;
	ScanBBT.dw ← rx-lx+1;
	BitBltDefs.BITBLT[ScanBBT];
	};
END;

VFill: PUBLIC PROCEDURE[x,ty,by: INTEGER] =
BEGIN
IF CLIP THEN BEGIN
	IF CULL THEN RETURN;
	IF x>RX OR x<LX OR ty>BY OR by<TY THEN RETURN;	--cull
	IF ty < TY THEN ty  ← TY;
	IF by >BY THEN by ← BY;
	END;
FillBBT.dlx ← x;
FillBBT.dty ← ty;
FillBBT.dw ← 1;
FillBBT.dh ← by-ty+1;
FillBBT.function ← IF FNC=erase THEN erase ELSE replace;
FillBBT.gray0 ← fillGrey[(ty+1) MOD 4];
FillBBT.gray1 ← fillGrey[(ty+2) MOD 4];
FillBBT.gray2 ← fillGrey[(ty+3) MOD 4];
FillBBT.gray3 ← fillGrey[ty MOD 4];
BitBltDefs.BITBLT[FillBBT];
END;

MoveScreenBox: PUBLIC PROCEDURE[tl,br: PointDefs.ScrPt,dx,dy: INTEGER] =
BEGIN
--clip the destination only
lx: INTEGER ← tl[X]+dx; ty: INTEGER ← tl[Y]+dy;	--simpler this way on a number of counts
rx: INTEGER ← lx+br[X]-tl[X]; by: INTEGER ← ty+br[Y]-tl[Y];
IF CLIP  THEN BEGIN
	IF CULL THEN RETURN;
	IF lx>RX OR rx<LX OR ty>BY OR by<TY THEN RETURN;	--cull
	IF lx < LX THEN lx  ← LX;
	IF rx >RX THEN rx ← RX;
	IF ty < TY THEN ty  ← TY;
	IF by >BY THEN by ← BY;
--first line should take care of this...
	IF rx < lx OR by < ty THEN RETURN;
	END;
BBT.dlx ← lx;
BBT.dty ← ty;
BBT.dw ← rx-lx+1;
BBT.dh ← by-ty+1;
BBT.slx ← tl[X];
BBT.sty ← tl[Y];
BBT.function ← replace;
BBT.sbmr ← BBT.dbmr;
IF alto THEN {BBT.sourcealt ← BBT.destalt ← BLTBank#0;
	BBT.sbca ← currentScreen.bitmap.bits;}
ELSE BBT.slbca ← MakeLongPointer[currentScreen.bitmap.bits,currentScreen.bitmap.bank];
BitBltDefs.BITBLT[BBT];
END;

InvertBox: PUBLIC PROCEDURE[tl,br: PointDefs.ScrPt] =
BEGIN OPEN PointDefs;
lx: INTEGER ← tl[X]; ty: INTEGER ← tl[Y];	--simpler this way on a number of counts
rx: INTEGER ← br[X]; by: INTEGER ← br[Y];
IF CLIP  THEN BEGIN
	IF CULL THEN RETURN;
	IF lx>RX OR rx<LX OR ty>BY OR by<TY THEN RETURN;	--cull
	IF lx < LX THEN lx  ← LX;
	IF rx >RX THEN rx ← RX;
	IF ty < TY THEN ty  ← TY;
	IF by >BY THEN by ← BY;
	END;
FillBBT.dlx ← lx;
FillBBT.dty ← ty;
FillBBT.dw ← rx-lx+1;
FillBBT.dh ← by-ty+1;
FillBBT.gray0 ← FillBBT.gray1 ← FillBBT.gray2 ← FillBBT.gray3 ← allOnes[0];
FillBBT.function ← invert;

BitBltDefs.BITBLT[FillBBT];
END;

EraseBox: PUBLIC PROCEDURE[tl,br: PointDefs.ScrPt] =
BEGIN OPEN PointDefs;
lx: INTEGER ← tl[X]; ty: INTEGER ← tl[Y];	--simpler this way on a number of counts
rx: INTEGER ← br[X]; by: INTEGER ← br[Y];
IF CLIP  THEN BEGIN
	IF CULL THEN RETURN;
	IF lx>RX OR rx<LX OR ty>BY OR by<TY THEN RETURN;	--cull
	IF lx < LX THEN lx  ← LX;
	IF rx >RX THEN rx ← RX;
	IF ty < TY THEN ty  ← TY;
	IF by >BY THEN by ← BY;
	END;
FillBBT.dlx ← lx;
FillBBT.dty ← ty;
FillBBT.dw ← rx-lx+1;
FillBBT.dh ← by-ty+1;
FillBBT.gray0 ← FillBBT.gray1 ← FillBBT.gray2 ← FillBBT.gray3 ← allOnes[0];
FillBBT.function ← erase;

BitBltDefs.BITBLT[FillBBT];
END;

BoxFill: PUBLIC PROCEDURE[tl,br: PointDefs.ScrPt] =
BEGIN OPEN PointDefs;
lx: INTEGER ← tl[X]; ty: INTEGER ← tl[Y];	--simpler this way on a number of counts
rx: INTEGER ← br[X]; by: INTEGER ← br[Y];
IF CLIP  THEN BEGIN
	IF CULL THEN RETURN;
	IF lx>RX OR rx<LX OR ty>BY OR by<TY THEN RETURN;	--cull
	IF lx < LX THEN lx  ← LX;
	IF rx >RX THEN rx ← RX;
	IF ty < TY THEN ty  ← TY;
	IF by >BY THEN by ← BY;
	END;

FillBBT.dlx ← lx;
FillBBT.dty ← ty;
FillBBT.dw ← rx-lx+1;
FillBBT.dh ← by-ty+1;
FillBBT.function ← FNC;
FillBBT.gray0 ← fillGrey[(ty+1) MOD 4];
FillBBT.gray1 ← fillGrey[(ty+2) MOD 4];
FillBBT.gray2 ← fillGrey[(ty+3) MOD 4];
FillBBT.gray3 ← fillGrey[ty MOD 4];
BitBltDefs.BITBLT[FillBBT];

END;

ngridpieces: INTEGER ← 0;
gridT: INTEGER ← 3;
GetGridParameters: PUBLIC PROCEDURE
	RETURNS[center: PointDefs.ScrPt,hgridlx,hgridrx,vgridty,vgridby,gridt: INTEGER]=
BEGIN
pt: PointDefs.ScrPt;
IF currentScreen.bitmap.bits=NIL THEN ERROR;
ngridpieces ← 1+(initBY-initTY)/8;
pt ← [(initRX-initLX)/2, (initBY-initTY)/2];
pt ← GriffinDefs.Grid[pt];
RETURN[pt,initLX,initRX,initTY,initBY,gridT];
END;

BltVGrid: PUBLIC PROCEDURE[lx: INTEGER ]=
BEGIN
ty: INTEGER ← GriffinDefs.Grid[[0,initTY]][Y];
rx: INTEGER ← lx+gridT-1;
THROUGH [0..ngridpieces] DO
	BltGridPiece[lx,ty,rx,ty+7];
	ty ← ty+8;
	ENDLOOP;
END;

BltGridPiece: PROCEDURE [lx,ty,rx,by: INTEGER] =
BEGIN
tl: ScrPt ← [lx,ty];	--save it
--always clip the vgrid since it can extend outside of the
--active region of the screen.  Right thing to do would be to
--construct the vgrid pieces as a function of the full screen size
IF CULL THEN RETURN;
IF lx>RX OR rx<LX OR ty>BY OR by<TY THEN RETURN;	--cull
IF lx < LX THEN lx  ← LX;
IF rx >RX THEN rx ← RX;
IF ty < TY THEN ty  ← TY;
IF by >BY THEN by ← BY;
vgridBBT.dlx ← lx;
vgridBBT.dty ← ty;
vgridBBT.dw ← rx-lx+1;
vgridBBT.dh ← by-ty+1;
vgridBBT.slx ← lx-tl[X];
vgridBBT.sty ← ty-tl[Y];
BitBltDefs.BITBLT[vgridBBT];
END;

BltHGrid: PUBLIC PROCEDURE[ty: INTEGER ]=
BEGIN
lx: INTEGER ← initLX;
rx: INTEGER ← initRX;
by: INTEGER ← ty+gridT-1;
IF CLIP  THEN BEGIN
	IF CULL THEN RETURN;
	IF lx>RX OR rx<LX OR ty>BY OR by<TY THEN RETURN;	--cull
	IF lx < LX THEN lx  ← LX;
	IF rx >RX THEN rx ← RX;
	IF ty < TY THEN ty  ← TY;
	IF by >BY THEN by ← BY;
	END;
hgridBBT.dlx ← lx;
hgridBBT.dty ← ty;
hgridBBT.dw ← rx-lx+1;
hgridBBT.dh ← by-ty+1;
hgridBBT.gray0 ← hgrid[(ty+1) MOD 4];
hgridBBT.gray1 ← hgrid[(ty+2) MOD 4];
hgridBBT.gray2 ← hgrid[(ty+3) MOD 4];
hgridBBT.gray3 ← hgrid[ty MOD 4];

BitBltDefs.BITBLT[hgridBBT];
END;

MakeLongPointer: PROCEDURE[ptr: POINTER, bank: CARDINAL] RETURNS [LONG POINTER] =
MACHINE CODE BEGIN
END;

AllocateEven: PROCEDURE[len: CARDINAL] RETURNS[POINTER]=
BEGIN
ptr: POINTER ← GriffinMemoryDefs.Allocate[len+1];
IF InlineDefs.BITAND[LOOPHOLE[ptr,CARDINAL],1]=1 THEN ptr←ptr+1;
RETURN[ptr];
END;

vgrid: DESCRIPTOR FOR ARRAY [0..7] OF CARDINAL ← DESCRIPTOR[AllocateEven[8],8];
vgrid[0] ← 120000B;
vgrid[1] ← vgrid[2] ← vgrid[3] ← vgrid[4] ← vgrid[5] ← vgrid[6] ← vgrid[7] ← 40000B;

--set up BBTables
BBT ← AllocateEven[SIZE[BitBltDefs.BBTable]];
BBT↑ ← [ptrs: short, pad: 0,sourcealt:  FALSE,destalt:TRUE ,sourcetype: block, function: paint,unused: ,dbca: ,dbmr: , dlx: ,dty: ,dw: ,dh: ,sbca: NIL,sbmr: 0,slx: 0,sty: 0, gray0: ,gray1: ,gray2: ,gray3: ,slbca: NIL ,dlbca:  NIL];

HBltBBT ← AllocateEven[SIZE[HBltDefs.HBTable]];
HBltBBT↑ ← [pad: 0,function: replace,destalt: TRUE, dbca: ,dbmr: ,y: ,x1: ,x2: ,gray: ];

ScanBBT ← AllocateEven[SIZE[BitBltDefs.BBTable]];
ScanBBT↑ ← [ptrs: short, pad: 0,sourcealt: FALSE,destalt:TRUE ,sourcetype:
gray, function: replace,unused: ,dbca: ,dbmr: , dlx: ,dty: ,dw: ,dh:
1,sbca: NIL,sbmr: 0,slx: 0,sty: 0, gray0: ,gray1: ,gray2: ,gray3:  ,slbca: NIL ,dlbca: NIL];

FillBBT ← AllocateEven[SIZE[BitBltDefs.BBTable]];
FillBBT↑ ← [ptrs: short, pad: 0,sourcealt: FALSE ,destalt:TRUE ,sourcetype: gray, function: replace,unused: ,dbca: ,dbmr: , dlx: ,dty: ,dw: ,dh: ,sbca: NIL,sbmr: 0,slx: 0,sty: 0, gray0: ,gray1: ,gray2: ,gray3:  ,slbca: NIL ,dlbca: NIL];

vgridBBT ← AllocateEven[SIZE[BitBltDefs.BBTable]];
vgridBBT↑ ← [ptrs: short, pad: 0,sourcealt: FALSE ,destalt:TRUE ,sourcetype: block, function: replace,unused: ,dbca: ,dbmr: , dlx: ,dty: ,dw: ,dh: ,sbca: @vgrid[0],sbmr: 1,slx: 0,sty: 0, gray0: 0,gray1: 0,gray2: 0,gray3:0  ,slbca: NIL ,dlbca: NIL];

hgridBBT ← AllocateEven[SIZE[BitBltDefs.BBTable]];
hgridBBT↑ ← [ptrs: short, pad: 0,sourcealt:  FALSE,destalt:TRUE ,sourcetype: gray, function: replace,unused: ,dbca: ,dbmr: , dlx: ,dty: ,dw: ,dh: ,sbca: NIL,sbmr: 0,slx: 0,sty: 0, gray0: 100200B,gray1:77577B ,gray2: 100200B,gray3:0  ,slbca: NIL ,dlbca: NIL];

-- Initialize coded brushes
BEGIN OPEN GriffinMemoryDefs;
   CodedBrushes[1] ←
	[DESCRIPTOR[Allocate[1],1],DESCRIPTOR[Allocate[1],1]];
   CodedBrushes[1][left][0]← 0;
   CodedBrushes[1][right][0]← 0;
   CodedBrushes[2] ←
	[DESCRIPTOR[Allocate[2],2],DESCRIPTOR[Allocate[2],2]];
   CodedBrushes[2][left][0]← -1;
   CodedBrushes[2][left][1]← -1;
   CodedBrushes[2][right][0]← 0;
   CodedBrushes[2][right][1]← 0;
   CodedBrushes[3] ←
	[DESCRIPTOR[Allocate[3],3],DESCRIPTOR[Allocate[3],3]];
   CodedBrushes[3][left][0]← 0;
   CodedBrushes[3][left][1]← -1;
   CodedBrushes[3][left][2]← 0;
   CodedBrushes[3][right][0]← 0;
   CodedBrushes[3][right][1]← 1;
   CodedBrushes[3][right][2]← 0;
   CodedBrushes[4] ←
	[DESCRIPTOR[Allocate[4],4],DESCRIPTOR[Allocate[4],4]];
   CodedBrushes[4][left][0]← -1;
   CodedBrushes[4][left][1]← -2;
   CodedBrushes[4][left][2]← -2;
   CodedBrushes[4][left][3]← -1;
   CodedBrushes[4][right][0]← 0;
   CodedBrushes[4][right][1]← 1;
   CodedBrushes[4][right][2]← 1;
   CodedBrushes[4][right][3]← 0;

   END;
END.