BitBltImpl.mesa
Copyright (C) 1986 by Xerox Corporation. All rights reserved.
Willie-Sue, September 12, 1986 2:50:40 pm PDT
Carl Hauser, November 12, 1986 2:31:12 pm PST
DIRECTORY
Basics,
DragOps,
DragOpsUtils,
BitBlt;
DragonBitBltImpl: MONITOR
IMPORTS
Basics
EXPORTS BitBlt
= BEGIN
BitAddress: TYPE = BitBlt.DragonBitAddress;
DragonPointer: TYPE = BitBlt.DragonPointer;
bitsPerWord: CARDINAL = Basics.bitsPerWord;
BitBltInc: TYPE = RECORD[wVal, bitVal: INT, easy: BOOL]; -- easy if value MOD bitsPerWord = 0
srcAddr: BitAddress;  -- address of source word at beginning of line
dstAddr: BitAddress;  -- address of destination word at beginning of line
srcFunc: BitBlt.SrcFunc ← null;
dstFunc: BitBlt.DstFunc ← null;
srcInc, dstInc: BitBltInc;
goingForward: BOOLTRUE;
MaskArray: ARRAY [0..bitsPerWord] OF DragOps.Word;
isThin: BOOLFALSE;   -- true if dest is all within one word
lMask: DragOps.Word;
rMask: DragOps.Word;
skew: DragOps.SixBitIndex;
dWidth, sWidth: INT;  -- width in words, including left & right partial words
dWidth & sWidth differ by ar most +/-1
dWidth > 2 => left partial, dWidth-2 full words, and right partial
dWidth = 2 => left partial, right partial only
dWidth = 1 => entire destination within one word - special case as both masks need to be applied at the same time
extraWordAtLeft, extraWordAtRight: BOOL;
If sWidth = dWidth+1, an extra source word must be fetched at both ends.
If sWidth = dWidth-1 or sWidth = 1, no extra source words need be fetched.
If sWidth = dWidth # 1, an extra source word must be fetched at one end:
if src.bit mod bitsPerWord > dst.bit mod bitsPerWord then left else right
isGray: BOOLFALSE;
grayParam: BitBlt.GrayParm;
grayWidth: BitBltInc;
grayBump: BitBltInc;
lastGray: [0..15);
InvalidBitBltCall: ERROR = CODE;
BITBLT: PUBLIC ENTRY PROC[bbt: DragonBitBlt.BitBltTablePtr] = {
BITBLT: PUBLIC PROC[bbt: BitBlt.BitBltTablePtr] = {
ENABLE UNWIND, ABORTED => NULL;
grayHeightMod: INT;
lineNum: INT ← DoSetup[bbt];  -- which line do we start with
IF isGray THEN grayHeightMod ← grayParam.heightMinusOne+1;
WHILE lineNum IN [0..bbt.height) DO
sInc: BitBltInc;
IF isGray THEN BltOneLineForwardGray[]
ELSE IF goingForward THEN BltOneLineForward[] ELSE BltOneLineBackward[];
lineNum ← lineNum + (IF goingForward THEN 1 ELSE -1);
sInc ← IF bbt.flags.gray THEN
IF (lineNum MOD grayHeightMod) = lastGray THEN grayBump ELSE grayWidth
ELSE srcInc;
IF sInc.easy AND dstInc.easy THEN {
srcAddr ← BitAddrInc[srcAddr, sInc];
dstAddr ← BitAddrInc[dstAddr, dstInc];
}
ELSE Recompute[bbt, sInc];
ENDLOOP;
};
DoSetup: PROC[bbt: BitBlt.BitBltTablePtr] RETURNS[lineNum: INT] = {
check for legality
IF bbt.flags.reserved # 0 OR bbt.reserved # 0 OR
bbt.src.reserved # 0 OR bbt.dst.reserved # 0 OR
(~bbt.flags.gray AND LOOPHOLE[bbt.srcDesc, INTEGER] = 0) OR -- bbt.dstBpl = 0 OR
bbt.width > LAST[NAT] OR bbt.height > LAST[NAT] THEN ERROR InvalidBitBltCall;
goingForward ← bbt.flags.direction = forward;
srcFunc ← bbt.flags.srcFunc;
dstFunc ← bbt.flags.dstFunc;
srcAddr ← bbt.src;  -- true for either real source or gray
IF (isGray ← bbt.flags.gray) THEN {
grayParam ← LOOPHOLE[bbt.srcDesc];
more checking
IF grayParam.widthMinusOne # 0 OR grayParam.reserved # 0 OR
~goingForward OR ~bbt.flags.disjoint OR bbt.dstBpl < 0 THEN ERROR InvalidBitBltCall;
grayWidth.easy ← TRUE;
grayWidth.wVal ← grayParam.widthMinusOne+1;
grayWidth.bitVal ← grayWidth.wVal * bitsPerWord;
grayBump.easy ← TRUE;
grayBump.wVal ← -grayWidth.wVal*grayParam.heightMinusOne;
grayBump.bitVal ← grayBump.wVal * bitsPerWord;
}
ELSE {
srcI: INT = LOOPHOLE[bbt.srcDesc];
IF srcI < 0 THEN srcInc.easy ← (-srcI MOD bitsPerWord) = 0
ELSE srcInc.easy ← (srcI MOD bitsPerWord) = 0;
srcInc.wVal ← IF srcInc.easy THEN srcI/bitsPerWord ELSE -1;
srcInc.bitVal ← srcI;
IF (goingForward AND (srcInc.bitVal < 0 OR bbt.dstBpl < 0)) OR
(~goingForward AND (srcInc.bitVal > 0 OR bbt.dstBpl > 0)) THEN
ERROR InvalidBitBltCall;
};
IF bbt.dstBpl < 0 THEN dstInc.easy ← (-bbt.dstBpl MOD bitsPerWord) = 0
ELSE dstInc.easy ← (bbt.dstBpl MOD bitsPerWord) = 0;
dstInc.bitVal ← bbt.dstBpl;
dstInc.wVal ← IF dstInc.easy THEN bbt.dstBpl/bitsPerWord ELSE -1;
dstAddr ← bbt.dst;
IF isGray THEN lastGray ← IF goingForward THEN
grayParam.heightMinusOne - grayParam.yOffset
ELSE grayParam.yOffset;
lineNum ← IF goingForward THEN 0 ELSE bbt.height-1;
checking for thin case, setting up masks, widths for line
BEGIN
bitSkew: INT = srcAddr.bit - dstAddr.bit;
finalDstInFirstLine: BitAddress ←
BitAddrInc[bbt.dst, [wVal: -1, bitVal: bbt.width, easy: FALSE] ];
lMask ← MaskArray[dstAddr.bit];
rMask ← RMask[finalDstInFirstLine.bit];
skew ← IF bitSkew < 0 THEN bitsPerWord + bitSkew ELSE bitSkew;
dWidth ← (dstAddr.bit + bbt.width + bitsPerWord - 1)/bitsPerWord;
sWidth ← (srcAddr.bit + bbt.width + bitsPerWord - 1)/bitsPerWord;
isThin ← (dWidth = 1);
extraWordAtLeft ← extraWordAtRight ← FALSE;
SELECT TRUE FROM
sWidth > dWidth => extraWordAtLeft ← extraWordAtRight ← TRUE;
sWidth < dWidth, sWidth = 1 => NULL;
ENDCASE =>
IF bitSkew >= 0 THEN extraWordAtLeft ← TRUE
ELSE extraWordAtRight ← TRUE;
END;
};
BitAddrInc: PROC[base: BitAddress, offset: BitBltInc] RETURNS[ba: BitAddress] = TRUSTED {
IF offset.easy THEN {
ba.word ← LOOPHOLE[offset.wVal + LOOPHOLE[base.word, INT], DragonPointer];
ba.bit ← base.bit;
}
ELSE {
addedBits: INT = offset.bitVal + base.bit;
absBits: INT = IF addedBits < 0 THEN -addedBits ELSE addedBits;
wordPart: INT ← IntShiftRight[absBits, DragOps.logBitsPerWord];
wordPart: INT ← IntShiftRight[absBits, 5]; -- DragOps.logBitsPerWord is wrong
IF addedBits < 0 THEN wordPart ← - wordPart;
ba.word ← LOOPHOLE[LOOPHOLE[base.word, INT] + wordPart, DragonPointer];
ba.bit ← IntAnd[addedBits, bitsPerWord-1];
};
};
IntShiftRight: PROC[x: INT, dist: DragOps.SixBitIndex] RETURNS [INT] = TRUSTED INLINE {
OPEN Basics;
RETURN [LOOPHOLE[ Basics.ShiftRight[LOOPHOLE[x, LongNumber], dist], INT] ];
};
IntAnd: PROC[x, y: INT] RETURNS [INT] = TRUSTED INLINE {
OPEN Basics;
RETURN [
LOOPHOLE[ Basics.BITAND[LOOPHOLE[x], LOOPHOLE[y]], INT] ];
};
Recompute: PROC[bbt: BitBlt.BitBltTablePtr, sInc: BitBltInc] = {
either srcBpl or dstBpl was not a multiple of bitsPerWord; recompute masks for this line
bitSkew: INT;
finalDstAddrInLine: BitAddress;
srcAddr ← BitAddrInc[srcAddr, [wVal: sInc.wVal, bitVal: sInc.bitVal, easy: sInc.easy] ];
dstAddr ← BitAddrInc[dstAddr, [wVal: dstInc.wVal, bitVal: dstInc.bitVal, easy: dstInc.easy] ];
bitSkew ← srcAddr.bit - dstAddr.bit;
skew ← IF bitSkew < 0 THEN bitsPerWord + bitSkew ELSE bitSkew;
finalDstAddrInLine ← BitAddrInc[dstAddr, [wVal: -1, bitVal: bbt.width, easy: FALSE] ];
lMask ← MaskArray[dstAddr.bit];
rMask ← RMask[finalDstAddrInLine.bit];
dWidth ← (dstAddr.bit + bbt.width + bitsPerWord - 1)/bitsPerWord;
sWidth ← (srcAddr.bit + bbt.width + bitsPerWord - 1)/bitsPerWord;
isThin ← dWidth = 1;
extraWordAtLeft ← extraWordAtRight ← FALSE;
SELECT TRUE FROM
sWidth > dWidth => extraWordAtLeft ← extraWordAtRight ← TRUE;
sWidth < dWidth, sWidth = 1 => NULL;
ENDCASE =>
IF bitSkew >= 0 THEN extraWordAtLeft ← TRUE
ELSE extraWordAtRight ← TRUE;
};
BltOneLineForward: PROC = TRUSTED {
src: DragonPointer ← srcAddr.word;
dst: DragonPointer ← dstAddr.word;
longSrcHi, longSrcLo: DragOps.Word;
dstValue, srcWord, curVal: DragOps.Word;
width: INT ← dWidth;
longSrcHi ← longSrcLo ← FetchWord[src];
src ← LOOPHOLE[src+1];
IF extraWordAtLeft THEN { 
longSrcLo ← FetchWord[src];
src ← LOOPHOLE[src+1];
};
[srcWord, dstValue] ← ComputeDstValue[longSrcHi, longSrcLo, dst];
IF isThin THEN {
doubleMask: DragOps.Word = Basics.BITAND[lMask, rMask];
curVal ← Basics.BITAND[FetchWord[dst], Basics.BITNOT[doubleMask]];
dstValue ← Basics.BITAND[dstValue, doubleMask];
dst^ ← Basics.BITOR[curVal, dstValue];
StoreWord[dst, Basics.BITOR[curVal, dstValue] ];
RETURN;
};
dstValue ← Basics.BITAND[dstValue, lMask];  -- at left side, so do left mask
curVal ← Basics.BITAND[FetchWord[dst], Basics.BITNOT[lMask]];
dstValue ← Basics.BITOR[curVal, dstValue];
DO  -- store full words in this loop
StoreWord[dst, dstValue];
dst ← LOOPHOLE[dst + 1];
longSrcHi ← longSrcLo;  -- (lshift bitsPerWord)
IF width <=2 THEN EXIT;
longSrcLo ← FetchWord[src];
[srcWord, dstValue] ← ComputeDstValue[longSrcHi, longSrcLo, dst];
src ← LOOPHOLE[src+1];
width ← width - 1;
ENDLOOP;
IF extraWordAtRight THEN longSrcLo ← FetchWord[src];
[srcWord, dstValue] ← ComputeDstValue[longSrcHi, longSrcLo, dst];
curVal ← Basics.BITAND[FetchWord[dst], Basics.BITNOT[rMask] ];
dstValue ← Basics.BITAND[dstValue, rMask];  -- at right side, so do right mask
StoreWord[dst, Basics.BITOR[curVal, dstValue] ];
};
BltOneLineForwardGray: PROC = TRUSTED {
src: DragonPointer ← srcAddr.word;
dst: DragonPointer ← dstAddr.word;
longSrcHi, longSrcLo: DragOps.Word;
dstValue, srcWord, curVal: DragOps.Word;
width: INT ← dWidth;
count: INT ← 0;
longSrcHi ← longSrcLo ← FetchWord[src];
src ← GrayInc[src, count ← count + 1];
IF extraWordAtLeft THEN { 
longSrcLo ← FetchWord[src];
src ← GrayInc[src, count ← count + 1];
};
[srcWord, dstValue] ← ComputeDstValue[longSrcHi, longSrcLo, dst];
IF isThin THEN {
doubleMask: DragOps.Word = Basics.BITAND[lMask, rMask];
curVal ← Basics.BITAND[FetchWord[dst], Basics.BITNOT[doubleMask]];
dstValue ← Basics.BITAND[dstValue, doubleMask];
StoreWord[dst, Basics.BITOR[curVal, dstValue]];
RETURN;
};
dstValue ← Basics.BITAND[dstValue, lMask];  -- at left side, do left mask
curVal ← Basics.BITAND[FetchWord[dst], Basics.BITNOT[lMask]];
dstValue ← Basics.BITOR[curVal, dstValue];
DO  -- store full words in this loop
StoreWord[dst, dstValue];
dst ← LOOPHOLE[dst + 1];
longSrcHi ← longSrcLo;
IF width <=2 THEN EXIT;
longSrcLo ← FetchWord[src];
[srcWord, dstValue] ← ComputeDstValue[longSrcHi, longSrcLo, dst];
src ← GrayInc[src, count ← count + 1];
width ← width - 1;
ENDLOOP;
IF extraWordAtRight THEN longSrcLo ← FetchWord[src];
[srcWord, dstValue] ← ComputeDstValue[longSrcHi, longSrcLo, dst];
curVal ← Basics.BITAND[FetchWord[dst], Basics.BITNOT[rMask] ];
dstValue ← Basics.BITAND[dstValue, rMask];  -- at right side, so do right mask
StoreWord[dst, Basics.BITOR[curVal, dstValue]];
};
BltOneLineBackward: PROC = TRUSTED {
src: DragonPointer ← LOOPHOLE[srcAddr.word + sWidth - 1];
dst: DragonPointer ← LOOPHOLE[dstAddr.word + dWidth - 1];
longSrcHi, longSrcLo: DragOps.Word;
dstValue, srcWord, curVal: DragOps.Word;
width: INTEGER ← dWidth;
longSrcHi ← longSrcLo ← FetchWord[src];
src ← LOOPHOLE[src-1];
IF extraWordAtRight THEN {
longSrcHi ← FetchWord[src];
src ← LOOPHOLE[src-1];
};
[srcWord, dstValue] ← ComputeDstValue[longSrcHi, longSrcLo, dst];
IF isThin THEN {
doubleMask: DragOps.Word = Basics.BITAND[lMask, rMask];
curVal ← Basics.BITAND[FetchWord[dst], Basics.BITNOT[doubleMask]];
dstValue ← Basics.BITAND[dstValue, doubleMask];
StoreWord[dst, Basics.BITOR[curVal, dstValue]];
RETURN;
};
dstValue ← Basics.BITAND[dstValue, rMask];  -- at right side, do right mask
curVal ← Basics.BITAND[FetchWord[dst], Basics.BITNOT[rMask] ];
dstValue ← Basics.BITOR[curVal, dstValue];
DO  -- store full words in this loop
StoreWord[dst, dstValue];
dst ← LOOPHOLE[dst-1];
longSrcLo ← longSrcHi;  -- cheaper than shifting
IF width <=2 THEN EXIT;
longSrcHi ← FetchWord[src];
src ← LOOPHOLE[src-1];
[srcWord, dstValue] ← ComputeDstValue[longSrcHi, longSrcLo, dst];
width ← width - 1;
ENDLOOP;
IF extraWordAtLeft THEN longSrcHi ← FetchWord[src];
[srcWord, dstValue] ← ComputeDstValue[longSrcHi, longSrcLo, dst];
dstValue ← Basics.BITAND[dstValue, lMask];  -- at left side, so do left mask
curVal ← Basics.BITAND[FetchWord[dst], Basics.BITNOT[lMask]];
StoreWord[dst, Basics.BITOR[curVal, dstValue]];
};
ComputeDstValue: PROC[longSrcHi, longSrcLo: DragOps.Word, dst: DragonPointer]
RETURNS[srcWord, dstValue: DragOps.Word] = {
srcWord ← LOOPHOLE[Basics.DoubleWordShiftLeft[LOOPHOLE[longSrcHi], LOOPHOLE[longSrcLo], skew]];
IF srcFunc = complement THEN srcWord ← Basics.BITNOT[srcWord];
dstValue ← SELECT dstFunc FROM
null => srcWord,
and => Basics.BITAND[srcWord, FetchWord[dst]],
or => Basics.BITOR[srcWord, FetchWord[dst]],
xor => Basics.BITXOR[srcWord, FetchWord[dst]],
ENDCASE => ERROR InvalidBitBltCall;
};
FetchWord: PROC[ptr: DragonPointer] RETURNS[DragOps.Word] = TRUSTED {
x: DragonPointer = LOOPHOLE[2*LOOPHOLE[ptr, INT]];
RETURN[x^];
};
StoreWord: PROC[ptr: DragonPointer, value: DragOps.Word] = TRUSTED {
x: DragonPointer = LOOPHOLE[2*LOOPHOLE[ptr, INT]];
x^ ← value;
};
GrayInc: PROC[src: DragonPointer, count: INT] RETURNS[DragonPointer] = {
inc: INT;
IF grayWidth.wVal = 1 THEN RETURN[src]  -- special case
ELSE inc ← count MOD grayWidth.wVal;
RETURN[LOOPHOLE[LOOPHOLE[src, INT]+inc]];
};
RMask: PROC[bitNum: [0..bitsPerWord)] RETURNS[DragOps.Word] = {
SELECT bitNum FROM
0 => RETURN[DragOps.OnesWord];  -- really bit 32
ENDCASE => RETURN[Basics.BITNOT[MaskArray[bitNum]] ];
};
MaskArray[ 0] ← DragOps.OnesWord;
MaskArray[ 1] ← LOOPHOLE[17777777777B];
MaskArray[ 2] ← LOOPHOLE[ 7777777777B];
MaskArray[ 3] ← LOOPHOLE[ 3777777777B];
MaskArray[ 4] ← LOOPHOLE[ 1777777777B];
MaskArray[ 5] ← LOOPHOLE[ 777777777B];
MaskArray[ 6] ← LOOPHOLE[ 377777777B];
MaskArray[ 7] ← LOOPHOLE[ 177777777B];
MaskArray[ 8] ← LOOPHOLE[ 77777777B];
MaskArray[ 9] ← LOOPHOLE[ 37777777B];
MaskArray[10] ← LOOPHOLE[ 17777777B];
MaskArray[11] ← LOOPHOLE[ 7777777B];
MaskArray[12] ← LOOPHOLE[ 3777777B];
MaskArray[13] ← LOOPHOLE[ 1777777B];
MaskArray[14] ← LOOPHOLE[ 777777B];
MaskArray[15] ← LOOPHOLE[ 377777B];
MaskArray[16] ← LOOPHOLE[ 177777B];
MaskArray[17] ← LOOPHOLE[ 77777B];
MaskArray[18] ← LOOPHOLE[ 37777B];
MaskArray[19] ← LOOPHOLE[ 17777B];
MaskArray[20] ← LOOPHOLE[ 7777B];
MaskArray[21] ← LOOPHOLE[ 3777B];
MaskArray[22] ← LOOPHOLE[ 1777B];
MaskArray[23] ← LOOPHOLE[ 777B];
MaskArray[24] ← LOOPHOLE[ 377B];
MaskArray[25] ← LOOPHOLE[ 177B];
MaskArray[26] ← LOOPHOLE[ 77B];
MaskArray[27] ← LOOPHOLE[ 37B];
MaskArray[28] ← LOOPHOLE[ 17B];
MaskArray[29] ← LOOPHOLE[ 7B];
MaskArray[30] ← LOOPHOLE[ 3B];
MaskArray[31] ← LOOPHOLE[ 1B];
END.