BitBltImpl.mesa
Copyright (C) 1986 by Xerox Corporation. All rights reserved.
Willie-Sue, September 11, 1986 3:32:54 pm PDT
Cedar code to implement the BitBlt operation as defined (almost) by PrincOps
DIRECTORY
Basics,
BitBlt,
PrincOps;
BitBltImpl: MONITOR
IMPORTS Basics
EXPORTS BitBlt
= BEGIN OPEN Basics;
BitAddress: TYPE = PrincOps.BitAddress;
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: PrincOps.SrcFunc ← null;
dstFunc: PrincOps.DstFunc ← null;
srcInc, dstInc: BitBltInc;
goingForward: BOOLTRUE;
isThin: BOOLFALSE;   -- true if dest is all within one word
lMask: WORD;
rMask: WORD;
skew: NAT;
dWidth, sWidth: INTEGER;  -- 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: PrincOps.GrayParm;
grayWidth: BitBltInc;
grayBump: BitBltInc;
lastGray: [0..15);
InvalidBitBltCall: ERROR = CODE;
BITBLT: PUBLIC ENTRY PROC[bbt: PrincOps.BitBltTablePtr] = {
ENABLE UNWIND, ABORTED => NULL;
grayHeightMod: CARDINAL;
lineNum: CARDINAL ← 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 ← BitBltAddrInc[srcAddr, sInc];
dstAddr ← BitBltAddrInc[dstAddr, dstInc];
}
ELSE Recompute[bbt, sInc];
ENDLOOP;
};
DoSetup: PROC[bbt: PrincOps.BitBltTablePtr] RETURNS[lineNum: CARDINAL] = {
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: INTEGER = 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: INTEGER = srcAddr.bit - dstAddr.bit;
finalDstInFirstLine: BitAddress ←
BitBltAddrInc[bbt.dst, [wVal: -1, bitVal: bbt.width, easy: FALSE] ];
lMask ← LMask[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;
};
BitBltAddrInc: PROC[base: BitAddress, offset: BitBltInc] RETURNS[ba: BitAddress] = {
IF offset.easy THEN {
ba.word ← LOOPHOLE[base.word + offset.wVal];
ba.bit ← base.bit;
}
ELSE {
add: LongNumber = LOOPHOLE[base.bit + offset.bitVal];
wordPart: LongNumber ← LOOPHOLE[DoubleShift[add, -logBitsPerWord]];
what we really want here is arithemtic right shift, but alas, we'll have to fake it
we know that base.bit + offset.bitVal is no more than 16 bits
IF add.hi < 0 THEN wordPart.hi ← 177777B;
wordPart.hi ← add.hi;  -- makes negative if add was negative
ba.word ← LOOPHOLE[base.word + LOOPHOLE[wordPart, INT]];
ba.bit ← BITAND[add.lo, bitsPerWord-1];
};
};
Recompute: PROC[bbt: PrincOps.BitBltTablePtr, sInc: BitBltInc] = {
either srcBpl or dstBpl was not a multiple of bitsPerWord; recompute masks for this line
bitSkew: INTEGER;
finalDstAddrInLine: BitAddress;
srcAddr ← BitBltAddrInc[srcAddr, [wVal: sInc.wVal, bitVal: sInc.bitVal, easy: sInc.easy] ];
dstAddr ← BitBltAddrInc[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 ← BitBltAddrInc[dstAddr, [wVal: -1, bitVal: bbt.width, easy: FALSE] ];
lMask ← LMask[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: LONG POINTER TO WORDLOOPHOLE[srcAddr.word];
dst: LONG POINTER TO WORDLOOPHOLE[dstAddr.word];
longSrc, shiftedSrc: Basics.LongNumber;
dstValue, srcWord, curVal: WORD;
width: INTEGER ← dWidth;
longSrc.hi ← longSrc.lo ← src^;
src ← LOOPHOLE[src+1];
IF extraWordAtLeft THEN { 
longSrc.lo ← src^;
src ← LOOPHOLE[src+1];
};
[shiftedSrc, srcWord, dstValue] ← ComputeDstValue[longSrc, dst];
IF isThin THEN {
doubleMask: WORD = BITAND[lMask, rMask];
curVal ← BITAND[dst^, BITNOT[doubleMask]];
dstValue ← BITAND[dstValue, doubleMask];
dst^ ← BITOR[curVal, dstValue];
RETURN;
};
dstValue ← BITAND[dstValue, lMask];  -- at left side, so do left mask
curVal ← BITAND[dst^, BITNOT[lMask]];
dstValue ← BITOR[curVal, dstValue];
DO  -- store full words in this loop
dst^ ← dstValue;
dst ← LOOPHOLE[dst + 1];
longSrc ← SwapHalves[longSrc];  -- cheaper than shifting
IF width <=2 THEN EXIT;
longSrc.lo ← src^;
[shiftedSrc, srcWord, dstValue] ← ComputeDstValue[longSrc, dst];
src ← LOOPHOLE[src+1];
width ← width - 1;
ENDLOOP;
IF extraWordAtRight THEN longSrc.lo ← src^;
[shiftedSrc, srcWord, dstValue] ← ComputeDstValue[longSrc, dst];
curVal ← BITAND[dst^, BITNOT[rMask] ];
dstValue ← BITAND[dstValue, rMask];  -- at right side, so do right mask
dst^ ← BITOR[curVal, dstValue];
};
BltOneLineForwardGray: PROC = TRUSTED {
src: LONG POINTER TO WORDLOOPHOLE[srcAddr.word];
dst: LONG POINTER TO WORDLOOPHOLE[dstAddr.word];
longSrc, shiftedSrc: Basics.LongNumber;
dstValue, srcWord, curVal: WORD;
width: INTEGER ← dWidth;
count: CARDINAL ← 0;
longSrc.hi ← longSrc.lo ← src^;
src ← GrayInc[src, count ← count + 1];
IF extraWordAtLeft THEN { 
longSrc.lo ← src^;
src ← GrayInc[src, count ← count + 1];
};
[shiftedSrc, srcWord, dstValue] ← ComputeDstValue[longSrc, dst];
IF isThin THEN {
doubleMask: WORD = BITAND[lMask, rMask];
curVal ← BITAND[dst^, BITNOT[doubleMask]];
dstValue ← BITAND[dstValue, doubleMask];
dst^ ← BITOR[curVal, dstValue];
RETURN;
};
dstValue ← BITAND[dstValue, lMask];  -- at left side, so do left mask
curVal ← BITAND[dst^, BITNOT[lMask]];
dstValue ← BITOR[curVal, dstValue];
DO  -- store full words in this loop
dst^ ← dstValue;
dst ← LOOPHOLE[dst + 1];
longSrc ← SwapHalves[longSrc];  -- cheaper than shifting
IF width <=2 THEN EXIT;
longSrc.lo ← src^;
[shiftedSrc, srcWord, dstValue] ← ComputeDstValue[longSrc, dst];
src ← GrayInc[src, count ← count + 1];
width ← width - 1;
ENDLOOP;
IF extraWordAtRight THEN longSrc.lo ← src^;
[shiftedSrc, srcWord, dstValue] ← ComputeDstValue[longSrc, dst];
curVal ← BITAND[dst^, BITNOT[rMask] ];
dstValue ← BITAND[dstValue, rMask];  -- at right side, so do right mask
dst^ ← BITOR[curVal, dstValue];
};
BltOneLineBackward: PROC = TRUSTED {
src: LONG POINTER TO WORDLOOPHOLE[srcAddr.word + sWidth - 1];
dst: LONG POINTER TO WORDLOOPHOLE[dstAddr.word + dWidth - 1];
longSrc, shiftedSrc: Basics.LongNumber;
dstValue, srcWord, curVal: WORD;
width: INTEGER ← dWidth;
longSrc.hi ← longSrc.lo ← src^;
src ← LOOPHOLE[src-1];
IF extraWordAtRight THEN {
longSrc.hi ← src^;
src ← LOOPHOLE[src-1];
};
[shiftedSrc, srcWord, dstValue] ← ComputeDstValue[longSrc, dst];
IF isThin THEN {
doubleMask: WORD = BITAND[lMask, rMask];
curVal ← BITAND[dst^, BITNOT[doubleMask]];
dstValue ← BITAND[dstValue, doubleMask];
dst^ ← BITOR[curVal, dstValue];
RETURN;
};
dstValue ← BITAND[dstValue, rMask];  -- at right side, so do right mask
curVal ← BITAND[dst^, BITNOT[rMask] ];
dstValue ← BITOR[curVal, dstValue];
DO  -- store full words in this loop
dst^ ← dstValue;
dst ← LOOPHOLE[dst-1];
longSrc ← SwapHalves[longSrc];  -- cheaper than shifting
IF width <=2 THEN EXIT;
longSrc.hi ← src^;
src ← LOOPHOLE[src-1];
[shiftedSrc, srcWord, dstValue] ← ComputeDstValue[longSrc, dst];
width ← width - 1;
ENDLOOP;
IF extraWordAtLeft THEN longSrc.hi ← src^;
[shiftedSrc, srcWord, dstValue] ← ComputeDstValue[longSrc, dst];
dstValue ← BITAND[dstValue, lMask];  -- at left side, so do left mask
curVal ← BITAND[dst^, BITNOT[lMask]];
dst^ ← BITOR[curVal, dstValue];
};
ComputeDstValue: PROC[longSrc: Basics.LongNumber, dst: LONG POINTER TO WORD]
RETURNS[shiftedSrc: Basics.LongNumber, srcWord, dstValue: WORD] = {
shiftedSrc ← Basics.DoubleShiftLeft[longSrc, skew];
srcWord ← shiftedSrc.hi;
IF srcFunc = complement THEN srcWord ← BITNOT[srcWord];
dstValue ← SELECT dstFunc FROM
null => srcWord,
and => BITAND[srcWord, dst^],
or => BITOR[srcWord, dst^],
xor =>BITXOR[srcWord, dst^],
ENDCASE => ERROR InvalidBitBltCall;
};
GrayInc: PROC[src: LONG POINTER TO WORD, count: INT]
RETURNS[LONG POINTER TO WORD] = {
inc: INT;
IF grayWidth.wVal = 1 THEN RETURN[src]  -- special case
ELSE inc ← count MOD grayWidth.wVal;
RETURN[LOOPHOLE[src+inc]];
};
LMask: PROC[bitNum: [0..bitsPerWord)] RETURNS[WORD] = {
SELECT bitNum FROM
0 => RETURN[177777B];
1 => RETURN[ 77777B];
2 => RETURN[ 37777B];
3 => RETURN[ 17777B];
4 => RETURN[ 7777B];
5 => RETURN[ 3777B];
6 => RETURN[ 1777B];
7 => RETURN[ 777B];
8 => RETURN[ 377B];
9 => RETURN[ 177B];
10 => RETURN[ 77B];
11 => RETURN[ 37B];
12 => RETURN[ 17B];
13 => RETURN[ 7B];
14 => RETURN[ 3B];
15 => RETURN[ 1B];
ENDCASE => ERROR InvalidBitBltCall;
};
RMask: PROC[bitNum: [0..bitsPerWord)] RETURNS[WORD] = {
SELECT bitNum FROM
0 => RETURN[177777B];  -- really bit 16
1 => RETURN[100000B];
2 => RETURN[140000B];
3 => RETURN[160000B];
4 => RETURN[170000B];
5 => RETURN[174000B];
6 => RETURN[176000B];
7 => RETURN[177000B];
8 => RETURN[177400B];
9 => RETURN[177600B];
10 => RETURN[177700B];
11 => RETURN[177740B];
12 => RETURN[177760B];
13 => RETURN[177770B];
14 => RETURN[177774B];
15 => RETURN[177776B];
ENDCASE => ERROR InvalidBitBltCall;
};
END.