Previous [TOC] Next

| 8 Block Transfers

The block transfer instruction move multiword structures from a source address to a destination address, or they com pare two multiword structures for equality. They include word block transfers, word block comparisons, byte block transfer, bit block transfer and text block transfer. The last two operations are designed specifically for manipulate rectangles and test on a bitmap display.

Because of potentially long execution times, all of the block transfer instructions check for pending interrupts (§4.6.2). When a wakeup is detected, they save their indeterminate state on the stack and back up the PC so that, when the instruction is restarted, it will continue transferring from the point of interruption. The check for interrupts is made once per interaction of the main loop (the InterruptPending routing is defined in §10.4.4). An implementation of the processor may make this check less often, if the frequency is consistent with the interrupt latency requirements in §10.4.4.1.

### 8.1 Word Boundary Block Transfers

The word block transfer instruction pop a count along with (short or long) source and destination pointers from the stack. They move words from the source to the destination. If the source and destination address are the same, there will still be a transfer.

Block Transfer and Block Transfer Long move words from the source to the destination in the forward direction (from low to high addresses). If the source and destination blocks overlap,and the destination address is greater than the source address, words must be transferred on at a time from the source into the overlap area. This method causes words in the non-overlap area to be duplicated throughout the destination block. If the destination address is less than the source address or there is no overlap, then the words do not have to be transferred on at a time, allowing possible speed improvements.

#### BLT Block Transfer

    BLT: PROCEDURE =
        BEGIN
        DO
            dest: POINTER = Pop[];
            count: CARDINAL = Pop[];
            source: POINTER = Pop[];
            if count = 0 THEN EXIT;
            StoreMDS[dest]  FetchMds[source];
            Push[source + 1];
            Push[Count - 1];
            Push[dest + 1];
            IF InterruptPending[] THEN GOTO Suspend;
            REPEAT
                Suspend => PC  savedPC;
            ENDLOOP;
        END;

#### BLTL Block Transfer Long

In Block Transfer Long, the source and destination address are long pointers.

    BLTL: PROCEDURE =
        BEGIN
        DO
            dest: LONG POINTER = PopLong[];
            count: CARDINAL = Pop[];
            source: LONG POINTER = PopLong[];
            if count = 0 THEN EXIT;
            StoreMDS[dest]  FetchMds[source];
            PushLong[source + 1];
            Push[Count - 1];
            PushLong[dest + 1];
            IF InterruptPending[] THEN GOTO Suspend;
            REPEAT
                Suspend => PC  savedPC;
            ENDLOOP;
        END;

Block Transfer Long Reversed moves words from the source to the destination in the backward direction (from high to low addresses). If the source and destination blocks overlap and the destination address is less than the source address, words must be transferred on at a time from the source into the overlap area, causing the words in the non-overlap area to be duplicated throughout the destination block. As with `BLT` and `BLTL`, if the destination address is less than the source address or there is no overlap, then words do not have to be transferred on at a time.

#### BLTLR Block Transfer Long Reversed

    BLTLR: PROCEDURE =
        BEGIN
        DO
            dest: LONG POINTER = PopLong[];
            count: CARDINAL = Pop[];
            source: LONG POINTER = PopLong[];
            if count = 0 THEN EXIT;
            StoreMDS[dest + count]  FetchMds[source + count];
            PushLong[source];
            Push[Count - 1];
            PushLong[dest];
            IF InterruptPending[] THEN GOTO Suspend;
            REPEAT
                Suspend => PC  savedPC;
            ENDLOOP;
        END;

The Block Transfer Code and the Block Transfer Code Long instructions move words from a source block in the code segment addressed by an offset from the current code base `CB`. In Block Transfer Code the destination address is a short pointer. In the Block Transfer Code Long the destination address is a long pointer. The ReadCode routine is defined in §3.1.4.2.

#### BLTC Block Transfer Code

    BLTC: PROCEDURE =
        BEGIN
        DO
            dest: POINTER = Pop[];
            count: CARDINAL = Pop[];
            source: POINTER = Pop[];
            if count = 0 THEN EXIT;
            StoreMDS[dest]  ReadCode[source];
            Push[source + 1];
            Push[Count - 1];
            Push[dest + 1];
            IF InterruptPending[] THEN GOTO Suspend;
            REPEAT
                Suspend => PC  savedPC;
            ENDLOOP;
        END;

#### BLTCL Block Transfer Code Long

    BLTCL: PROCEDURE =
        BEGIN
        DO
            dest: LONG POINTER = Pop[];
            count: CARDINAL = Pop[];
            source: POINTER = Pop[];
            if count = 0 THEN EXIT;
            StoreMDS[dest]  ReadCode[source];
            Push[source + 1];
            Push[Count - 1];
            PushLong[dest + 1];
            IF InterruptPending[] THEN GOTO Suspend;
            REPEAT
                Suspend => PC  savedPC;
            ENDLOOP;
        END;

Implementation Note: If an interrupt occurs, the value of the (virtual) code base may be different when the instruction is resumed; therefore, it should not be part of the intermediate state saved on the stack.

Implementation Note: Since code segments are read-only, there can be no overlap in the block transfer code instructions. Therefore words do not have to be transferred on at a time, nor do they have to be transferred in the forward direction.

#### CKSUM Checksum

The Checksum instruction incrementally updates a single words checksum based on the contents of the source block. The updated checksum is returned on the stack.

    CKSUM: PROCEDURE =
        BEGIN
        cksum: CARDINAL;
        DO
            source: LONG POINTER = PopLong[];
            count: CARDINAL = Pop[];
            cksum  Pop[];
            IF count = 0 THEN EXIT;
            Push[Checksum[cksum, Fetch[Source]]];
            Push[count - 1];
            PushLong[source + 1];
            IF InterruptPending[] THEN GOTO Suspend;
            REPEAT
                Suspend => PC  savedPC;
                RETURN;
            ENDLOOP;
        IF cksum = 177777B THEN cksum  0;
        Push[cksum];
        END;

The checksum is ones' complement add-and-left-cycle as computed by the following routine.

    Checksum: PROCEDURE[chksum: CARDINAL, data: CARDINAL] RETURNS[CARDINAL] =
        BEGIN
        temp: CARDINAL;
        temp  chksum + data;
        IF chksum > temp THEN temp  temp + 1;
        IF temp >= 100000B THEN temp  temp * 2 + 1 ELSE temp  temp * 2;
        RETURN[temp];
        END;

### 8.2 Block Comparisons

The block comparison instruction pop a count and pointers from the stack. They compare two blocks of memory, returning `TRUE` if they are equal and `FALSE` otherwise. If Block Equal Long, the blocks are addressed by long pointers.

#### BLEL Block Equal Long

    BLEL: PROCEDURE =
        BEGIN
        DO
            ptr1: LONG POINTER  PopLong[];
            count: CARDINAL  Pop[];
            ptr2: LONG POINTER  PopLong[];
            offset: CARDINAL  Long[];
            IF count = 0 THEN
                BEGIN Push[TRUE]; EXIT; END;
            IF Fetch[ptr1] # Fetch[ptr2] THEN
                BEGIN Push[FALSE]; EXIT; END;
            PushLong[ptr2 + 1];
            Push[count -1];
            PushLong[ptr1 + 1];
            IF InterrruptPending[] THEN GOTO Suspend;
            REPEAT
                Suspend => PC  savedPC;
            ENDLOOP;
        END;

#### BLECL Block Equal Code Long

In Block Equal Code Long, one block is addressed by a long pointer and the other is addressed by the offset from the current code base `CB`. (The ReadCode routine used here is defined find §3.1.4.3.)

    BLECL: PROCEDURE =
        BEGIN
        DO
            ptr: LONG POINTER  PopLong[];
            count: CARDINAL  Pop[];
            offset: CARDINAL  Long[];
            IF count = 0 THEN
                BEGIN Push[TRUE]; EXIT; END;
            IF Fetch[ptr] # ReadCode[offset] THEN
                BEGIN Push[FALSE]; EXIT; END;
            Push[offset + 1];
            Push[count -1];
            PushLong[ptr + 1];
            IF InterrruptPending[] THEN GOTO Suspend;
            REPEAT
                Suspend => PC  savedPC;
            ENDLOOP;
        END;

Implementation Note: If an interrupt occurs, the value of the (virtual) code base may be different when the instruction is resumed; therefore, it should not be part of the intermediate state saved on the stack.

Implementation Note: In the block comparison instructions, it does not matter whether words are fetched in the forward or backward order.

### 8.3 Byte Boundary Block Transfers

The byte block transfer instruction pop a count, short source and destination offsets long source and destination pointers from the stack. They move bytes from the source to the destination. If the source and destination addresses are the same, there will still be a transfer.

Byte Block Transfer moves bytes from the source to the destination in the forward direction (from low to high addresses). If the source and destination blocks overlap and the destination address is greater than the source address, bytes must be transferred one at a time from the source into the overlap area, so bytes in the non-overlap area a duplicated throughout the destination block. If the destination address is less than the source address or there is no overlap, then the bytes do not have to be transferred on at a time. Some speed improvement become possible as a result.

#### BYTBLT Byte Block Transfer

    BYTBLT: PROCEDURE =
        BEGIN
        DO
            sourceOffset: CARDINAL  Pop[];
            sourceBase: LONG POINTER  PopLong[];
            count: CARDINAL  Pop[];
            destOffset: CARDINAL  Pop[];
            destBase: LONG POINTER  PopLong[];
            IF count = 0 THEN EXIT;
            sourceBase  sourceBase + LONG[sourceOffset / 2];
            sourceOffset  sourceOffset MOD 2;
            destBast  destBase + LONG[destBase / 2];
            destOffset  destOffset MOD 2;
            StoreByte[
                destBase, LONG[destOffset], FetchByte[sourceBase, LONG[sourceOffset]]];
            IF sourceOffset = 1 THEN
                BEGIN sourceBase  sourceBase + 1; sourceOffset  0; END;
            ELSE sourceOffset  1;
            IF destOffset = 1 THEN
                BEGIN destBase  destBase + 1; destOffset  0; END;
            ELSE destOffset  1;
            PushLong[destBase];
            Push[destOffset];
            Push[count - 1];
            PushLong[sourceBase];
            Push[sourceOffset];
            IF InterruptPending[] THEN GOTO Suspend;
            REPEAT
                Suspend => PC  savedPC;
            ENDLOOP;
        END;

Byte Block Transfer Reversed moves byes from the source to the destination in the backward direction (from high to low addresses). As with the other Block Transfer instructions, if the source and destination blocks overlap, and the destination address is less than the source address, bytes must be transferred on at a time from the source into the overlap area. Bytes in the non-overlap area are duplicated throughout the destination block. However, if the destination address is less than the source address or there is no overlap, bytes do not have to be transferred one at a time.

#### BYTBLTR Byte Block Transfer

    BYTBLTR: PROCEDURE =
        BEGIN
        DO
            sourceOffset: CARDINAL  Pop[];
            sourceBase: LONG POINTER  PopLong[];
            count: CARDINAL  Pop[];
            destOffset: CARDINAL  Pop[];
            destBase: LONG POINTER  PopLong[];
            IF count = 0 THEN EXIT;
            sourceBase  sourceBase + LONG[sourceOffset / 2];
            sourceOffset  sourceOffset MOD 2;
            destBast  destBase + LONG[destBase / 2];
            destOffset  destOffset MOD 2;
            StoreByte[
                destBase, LONG[destOffset], FetchByte[sourceBase, LONG[sourceOffset]]];
            IF sourceOffset = 0 THEN
                BEGIN sourceBase  sourceBase - 1; sourceOffset  1; END;
            ELSE sourceOffset  0;
            IF destOffset = 0 THEN
                BEGIN destBase  destBase - 1; destOffset  1; END;
            ELSE destOffset  0;
            PushLong[destBase];
            Push[destOffset];
            Push[count - 1];
            PushLong[sourceBase];
            Push[sourceOffset];
            IF InterruptPending[] THEN GOTO Suspend;
            REPEAT
                Suspend => PC  savedPC;
            ENDLOOP;
        END;

### 8.4 Bit Boundary Block Transfers

The bit boundary block transfer instructions include (`BITBLT`) for operation on rectangular arrays of bits in memory, and the Text Block Transfer (`TXTBLT`), for converting arrays of characters in their bitmap representations.

### 8.4.1 Bit Transfer Utilities

The transfer instruction describe below operate on arbitrary bit boundaries. The following structure is use to address bits: BitAddress: TYPE = MACHINE DEPENDENT RECORD [ word(0): LONG POINTER, reserved(2:0..11): [0..7777B] 0, bit(2:12..15): [0..WordSize)];

The Bump routine is used to increment (or decrement) a bit address by a bit offset.

    Bump: PROCEDURE addressL BitAddress, offset: LONG INTEGER] RETURSN[BitAddress] =
        BEGIN
        offset  offset + LONG[address.bit];
        RETURN[
            BitAddress[
                word: Address.word + LongArithShift[offset, -Log[WordSize]],
                bit: And[LowHalf[offset], WordSize - 1]]];
        END;

Implementation Note: Because the reserved field of the bit address is guaranteed to be zero, the extraction address.bit can be replace by a word access.

The following routines are used to read and write individual bits within a word. The source (destination) is specified by a base bit address and a bit offset. The ReadField and WriteField routines are defined in §7.5.

    ReadBit: PROCEDURE [address: BitAddress, offset: INTEGER] RETURNS [BIT] =
        BEGIN
        spec: FieldSpec;
        addrss  Bump[Address, LONG[offset]];
        spec  FieldSpec[pos: address.bit, size: 0];
        RETURN[ReadField[Fetch[Address.word], spec];
        END;

    WriteBit: PROCEDURE [address: BitAddress, offset: INTEGER, bit: BIT] =
        BEGIN
        spec: FieldSpec;
        word: UNSPECIFIED;
        address  Bump[address, LONG[offset]];
        word  Fetch[address.word];
        spec  FieldSpec[pos: address.bit, size: 0];
        Store[Address.word]  WriteField[word, spec, bit];
        END;

### 8.4.2 Bit Block Transfer

The `BITBLT` instruction manipulates rectangular arrays of bits. It accesses source bits and destination bits, performs a function on them, and stores the result in the destination bits.

Successive bit pairs are obtained by scanning a source bit stream and a destination bit stream. The instruction operates successively on lines of bits called items; it processes width bits from a pair of lines, and then moves down to the next item by adding srcBpl (bits per line) to the source address and dstBpl to the destination address. It continues until it has processed height lines.

Figure 8.1 illustrates a possible configuration of source and destination rectangles, which are always of the same size and dimensions, embedded in separate bitmaps. Approximately half of the items have been moved to the destination, and the location of the next item is highlighted in the source bitmap and shown as a dotted line in the destination bitmap.

Figure 8.1

### 8.4.2.1 BitBlt Arguments

The arguments to Bit Block Transfer is a short pointer to a record containing the source and the destination bit addresses and bits per line, the width and height (in bits) of the rectangle to be operated on, and a word of flags that indicate the operation to be performed. The width and height of the rectangle are restricted to a maximum of 32,767. The argument record must be aligned on a sixteen-word boundary.

Note: Review the section on Gray Flag for the relationship between SrcBpl and the `gray flag` in BitBltFlags.

    BitBltArg: TYPE = MACHINE DEPENDENT RECORD [
        dst(0): BitAddress,
        dstBpl(3): INTEGER,
        src(4): BitAddress,
        srcBpl(7): INTEGER,
        width(8): CARDINAL,
        height(9): CARDINAL,
        flags(10: BitBltFlags,
        reserved(11): UNSPECIFIED  0];

The flag bits specify the direction of the operation, the overlap of the operands, whether the source is interpreted as gray brick, and the function to be performed on the source and destination bits.

    BitBltFlags: TYPE = MACHINE DEPENDENT RECORD [
        direction:(0: 0..0): Direction,
        disjoint(0: 1..1): BOOLEAN,
        disjointItems(0: 2..2): BOOLEAN,
        gray(0: 3..3): BOOLEAN,
        srcFunc(0: 4..4): SrcFunc,
        dstFunc(0: 5..6): DstFunc,
        reserved(0: 7..15): [0..777B]  0];

Source and Destination Functions

The following routine describes the function available for combining the source and destination rectangles (arg is the argument record). These function are also shown in Figure 8.2.

    SrcFunc: TYPE = MACHINE DEPENDENT {null, complement};
    DstFunc: TYPE = MACHINE DEPENDENT {null, and, or, xor};

    Function: PROCEDURE [dst, src: BIT] RETURNS [BIT] =
        BEGIN
        src  SELECT arg.flags.srcFunc FROM
            null => src,
            complement => Not[src],
            ENDCASE => ERROR;
        dst  SELECT arg.flags.dstFunc FROM
            null => src,
            and => And[src, dst],
            or => Or[src, dst],
            xor => Xor[src, dst],
            ENDCASE => ERROR;
        RETURN[dst];
        END;

Figure 8.2

The src field has two options; the null selection indicates using the source rectangle "as is" for the destination function. The complement selection will invert the source bit in the destination function.

The dst field determines the function to be used for changing bits in the destination rectangle. The null selection causes the destination to be "replaced" with the source bits. There is no boolean operation in this case. Anding the destination bits with the source bits leaves only those bits in common in the destination. "Painting" the destination requires oring. This operation will leave the union of the two sets of bits in the destination. The last function is xor. It essentially masks out the matching bits leaving the union but not the intersection of the bits in the destination rectangle.

Direction Flag

The direction flag indicates whether the operation should place forward (left to right, from low to high memory addresses) or backward (right to left, from high to low memory addresses). This allows unambiguous specification of overlapping BitBlts, as in scrolling.

    Direction: TYPE = MACHINE DEPENDENT {forward, backward};

If the direction is backward, the source and destination addressees point to the beginning of the last item of the blocks to be processed, and the source and destination bits per line must be negative. This restricts the width of the bitmaps involved to a maximum of 32,767 bits.

Adjustments of the arguments required by a change in direction are performed by the ComputeDirection routine which appears after the `BITBLT` opcode.

Disjoint Flag

If the operation's source and destination are completely disjoint, the implementation performs the operation in the most efficient horizontal and vertical directions, given by the following processor dependent variables:

    xPreference, yPreference: Direction;

Both the direction and the disjointItems flags in the argument record are ignored when disjoint is set.

DisjointItems Flag

If the individual items of the source and destination are disjoint, but the rectangle otherwise overlap, the disjointItems flag should be set (and the disjoint flag should be clear). The implementation can then perform the operation so that, within each item, the bits are processed in the most efficient horizontal direction. The items are processed in the order indicated by direction.

If neither disjoint nor disjointItems is set, the implementation processes the items and the bits within items in the direction indicated by the direction flag.

Programming note: Correct specification of disjoint and disjointItems is the responsibility of the programmer. The implementation makes no attempt to verify claims about overlapping source and destination arguments. If, in the course of instruction execution, a bit is used as a destination bit and then subsequently used as a source bit, the results are undefined.

Gray Flag

The gray flag allows repetitively bit patters to be specified in a condensed format. The usual application for the generation of various shades of gray on the display, but any repetitive pattern within the limits stated below may be supplied.

    GrayParm: TYPE = MACHINE DEPENDENT RECORD [
        reserve(0: 0..3): NIBBLE , 0,
        yOffset(0: 4..7): NIBBLE,
        widthMinusOne(0: 8..11): NIBBLE,
        heightMinusOne(0: 12..15}: NIBBLE];

The fields grayParm.widthMinusOne and grayParm.heightMinusOne define the width (less one) in words and height (less one) in bits, respectively, of the gray brick located at arg.src. Note, the term "brick" reefers to a rectangular area containing the gray pattern to be copied. Conceptually, this brick is replicated horizontally and vertically to tile a plane of dimensions arg.width by arg.height. This plane becomes the source rectangle of the operation. The brick is maximum of sixteen words wide and sixteen lines high. Patterns, therefore, are also limited to a repetition rate of sixteen in each direction. To guarantee correct repeatability of the pattern in the horizontal direction, the width of the gray brick (in bits) is usually a multiple of the repetition rate. The height of the gray brick is usually equal to the vertical repletion rate.

Proper alignment of the gray pattern with the destination bitmap requires the initial x- and y-offsets into the brick along with its width and height. The initial x-offset is derived from arg.src as follows: arg.src.word always points to the beginning of the first line to be transferred (not to the origin of the gray brick). The x-offset of the first bit to be transferred is supplied by arg.src.bit. This bit is always the fist word of the line. The initial y-offset is the number of lines down from the origin of the brick. It is specified by grayParm.yOffset. Subtracting the y-offset times the brick width from arg.src.word give the origin of the gray brick.

Design Note: Since the brick is word-aligned and the repetition rate is sixteen or less, the initial x-offset can never exceed fifteen.

Design Note: The gray case is always forward and completely disjoint (disjointItems is ignored).

Design Note: Allowing grayParm.widthMinusOne to be greater than zero allows gray patterns having repetition rates of other than 1, 2, 4, 8, or 16 in the horizontal direction. Patterns with other repetition rates may be desirable, but are not mandatory. While the BitBlt code allows values greater than zero for grayParm.widthMinusOne, the initial implementation is restricted to a value of zero.

Figure 8.3 Gray Brick

### 8.4.2.2 Interruptibility

The Bit Block Transfer instruction checks for interrupts after it completes each item. If a pending interrupt is detected, the current state of the `BITBLT` is saved on the stack. When the instruction is restarted, the stack count is used to distinguish the restart case. The actual format of the stack is processor-dependent. The following routines are assumed to save and restore the intermediate state:

    PuhsState, PopState: PROCEDURE;

Design Note: If any of the values of the arguments (in memory) change between the time of an interrupt and the subsequent restart of the instruction, the effects of the instruction are undefined. This allows the original values in the argument record to be saved as part of the intermediate state.

### 8.4.2.3 BitBlt Routines

#### BITBLT Bit Block Transfer

    BITBLT: PROCEDURE =
        BEGIN
        line: CARDINAL;
        arg: BIBLTARG;
        grayParm: GrayParm;
        lastGray: [0..15);
        grayWidth: INTEGER;
        GrayBump: LONG INTEGER
        xDirection, yDirection: Direction;
        if StackCount[] = 1 THEN Setup[] ELSE PopState[];
        MinimalStack[];
        WHILE line IN [0..arg.height) DO
            BitBitItem[];
            arg.src  Bump[arg.src,
                IF (line MOD grayParm.heigthMinusOne + 1) = lastGray
                    THEN grayBump
                    ELSE LONG[grayWidth]
                ELSE LONG[arg.srcBpl]];
            arg.dst  Bump[arg.dst, LONG[arg.dstBpl]];
            line  line + (IF yDirection = forward THEN 1 ELSE -1);
            IF InterruptPendint[] THEN GOTO Suspend;
            REPEAT
                Suspend => {PushState[]; PC  safecPC};
            ENDLOOP;
        END;

    BitBltItem: PROCEDURE =
        BEGIN
        offset, pos: INTEGER;
        offset  IF xDirection = forward THEN 0 ELSE arg.width - 1;
        THROUGH [0...arg.width) DO
            pos  IF arg.flags.gray THEN
                ((ofset + arg.src.bit) MOD ABS[grayWidth]) - arg.src.bit ELSE offset;
            WriteBit[
                arg.dst, offset, Function[
                    ReadBit[arg.dst, offset], ReadBit[arg.src, pos]]];
            offset  offset + (IF xDirection = forward THEN 1 ELSE -1);
            ENDLOOP;
        END;

The routines given below are used to set up the BitBlt operation on the fist entry. They fetch the argument record, perform the error checks on its fields, choose a direction, adjust the arguments accordingly, and compute the gray brick boundaries.

    Setup: PROCEDURE =
        BEGIN
        ptr :POOINTER TO BitBltArg = Pop[];
        arg  FetchBitBlltArg[ptr];
        IF arg.flags.reserve # 0 OR arg.reserved # 0
        OR arg.src.reserved # 0 OR arg.dst.reserved # 0
        of (~arg.flags.gray AND arg.srcBpl = 0) OR arg.dstBpl = 0
        OR arg.width > 32767 OR arg.heigth > 32767
            THEN ERROR;
        IF arg.flags.gray THEN
            BEGIN
            grayParm  LOOPHOLE[arg.src.Bpl];
            IF grayParm.widthMinusOne # 0
            OR grayParm.reserved # 0
            OR arg.flags.directon # forward
            OR ~arg.flags.disjoint or ARG.dstBpl  0
                THEN ERROR;
            grayWidth  INTEGER[(grayParm.widthMinusOne + 1) * WordSize];
            grayBump  -grayWidth * grayParm.heightMinusOne;
            END
        ELSE
            IF (arg.flags,direction = fowrard AND (arg.srcBpl  0 OR arg.dstBpl  0))
            OR (arg.flags.direction = backward AND (arg.srcBpl > 0 OR arg.dstBpl > 0))
                THEN ERROR;
        ComputeDirection[];
        IF arg.flags.gray THEN
            lastGray  IF yDirection = forward
                THEN grayParm.heightMinusOne - grayParm.yOffset
                ELSE grayParm.yOffset;
        line  IF yDirection = fowrard THEN 0  ELSE arg.height - 1;
        END;

    FetchBitBltArg: PROCEDURE[ptr: POINTER TO BitBltArg] RETURNS[BitBltArg] =
        BEGIN
        ArgIndex: TYPE = [0..SIZE[BitBltArg]);
        tmp: ARRAY ArgIndex OF UNSPECIFIED;
        IF And[ptr, 17B] # 0 THEN ERROR;
        FOR i: ArgIndex IN ArgIndex DO
            temp[i]  FetchMds[Prt + i];
            ENDLOOP;
        RETURN[LOOPHILE[temp]];
        END;

    ComputeDirection: PROCEDURE =
        BEGIN
        yDirection  xDirection  arg.flags.direction;
        IF arg.flags.disjoint AND yDirection # yPreference THEN
            BEGIN
            yDirection  yPreferende;
            IF arg.flags.gray THEN
                BEGIN
                arg.src  Bump[arg.src,
                    grayWidth * ((grayParm.yOffset + arg.height - 1)
                    MOD (grayParm.heightMinusOne + 1) - grayparm.yOffset)];
                grayWidth  - grayWidth;
                grayBump  -grayBump
                END
            ELSE
                BEGIN
                arg.src  Bump[arg.src, arg.srcBpl * (arg.height - 1)];
                arg.srcBpl  -arg.srcBpl;
                END;
            arg.dst  Bump[arg.src, arg.srcBpl * (arg.height - 1)];
            arg.dstBpl  -arg.dstBpl;
            END;
        IF arg.falgs.disjoin OR arg.flags.disjoinItems THEN xDirecton  xPreference;
        END;

Implementation Note: The products computed in Setup and ComputeDirection are thirty-two bit signed numbers (`LONG INTEGER`s) produced by multiplying an integer by a cardinal. Since the cardinal is known to be less than 32,768, a short-integer multiply can be used.

Implementation Note: Much of the complexity in ComuteDirection comes from reversing direction in the gray case. This can be avoided if yPreference is forward, since all legal gray BitBlts specify a forward direction.

### 8.4.3 Text Block Transfer

The Text Block Transfer instruction operates on an array of characters. It implements three function useful for generating the font representation of the text in a bitmap.

    Function: TYPE = MACHINE DEPENDENT {display, format, resolve, unused};

The format function is used to calculate the number of characters that will fit on a line and the number of spaces that may be added to the line, given its right margin (in micas). The display function convert characters to their font representation in the destination bitmap, optionally widening or narrowing pad characters to perform line justification. The resolve function is used to record the horizontal bit position of the origin of each character in the bitmap; it also handles justification. This function is used when determining which character in a text line has been selected with a pointing device.

Note: In the following section, the directional references used refer to the association with conventional Xerox bitmap displays. The top left of a CRT display is considered to be the point of origin for the x- and y-coordinates. The x-coordinate increases horizontally from left to right across the screen. The y-coordinate increases vertically from the top of the screen to the bottom. So for instance, referring to the bit-position in the left-to-right, top-to-bottom order is only for conceptual purposes.

### 8.4.3.1 Font Representation

The font determines the height and width (in bits) of the characters and the bit pattern to be transferred. The font also contains two flag bit for each character: the first specifies whether the character is a pad character (widenable for justification), and the second specifies whether the character is a stop character (terminating a TextBlt operation).

The precise format of the font is private to the implementation; the following types and routines are used in the `TXTBLT` code to access the font. FontRecord contains the font information `TXTBLT` needs. rasters indicates the font's raster specification. spacingWidths specifies the width of a character in bits. printerWidths gives the printing width of the character. The flags consist of a pad and stop. pad is set to `TRUE` if the character is a pad character; stop is set to `TRUE` if the character is a stop character. rasterInfo includes the number of bits to the left or right of the character's origin and specifies the offset of the character's raster. height is the height of the font measured in bits; it is constant for all characters. To allow for kerned fonts, DisplayWidth returns the width of the entire font representation of the character, which includes the left and right kerning not included in the spacingWidth. Bit returns the individual bits of the character's font representation.

FontHandle points to the font information TextBlt needs. FontRecord is the concrete type of a Font. FontRecord must be aligned on a sixteen-word boundary.

    Font: TYPE;
    FontHandle: TYPE = LONG POINTER TO Font;

    fontRecordAlignment: NATURAL = 16;

    FontRecord: TYPE = MACHINE DEPENDENT RECORD [
        rasters(0): FontRasters,
        spacingWidth(2): SpacingWidths,
        printerWidths(4): PrinterWidths,
        flags(6): FlagsArray,
        rasterInfo(8): RasterInfos,
        Height(10): CARDINAL];

The following types make of the FontRecord:

    FontBitsPtr: TYPE = LONG BASE POINTER TO ARRAY [0..0) OF UNSPECIFIED;

    FontRasters: TYPE = LONG BASE POINTER TO <> ARRAY [0..0) OF WORD;

The data at FontRasters is a base pointer for the character raster data. For a particular character, RasterInfo.offset (defined below) is added to this base to get the address of the character's raster. The raster format includes the scan lines within the dimensions given by spacingWidths and height. The scan lines are tightly packed together, so that the last bit of the scan line is immediately followed by the first bit of the next. The height of the raster is constant for all characters. Each raster begins on a word boundary.

The memory order of the bits in the raster correspond to the memory order in which TextBlt will paint them into the destination bitmap. Said another way, TextBlt paints the first scan line of the raster into the appropriate place in the first scan line of the destination bitmap, and so on. Similarly, the first bit of a raster's scan line is painted into the appropriate first bit of the scan line in the destination bitmap, and so on.

The first scan line in memory corresponds to the top line on the screen (of Xerox conventional bitmap displays). The first bit of the scan line corresponds to the left pixel of the line. For this case, the first scan line in the raster will be the topmost row of the character, and the first pixel (most significant bit) for a scan line will be the leftmost pixel of its row.

    Byte: TYPE = CARDINAL[0..255];

    SpacingWidths: TYPE = LONG POOINTER TO PACKED ARRAY Byte OF SpacingWidth;
    SpacingWidth: TYPE = Byte;

    PrinterWidths: TYPE = LONG POINTER TO ARRAY OF Byte OF PrinterWidth;
    PrinterWidth: TYPE = CARDINAL;

    FlagsArray: TYPE = LONG POINTER TO PACKED ARRAY Byte OF Flags;

    Flags: TYPE = MACHINE DEPENDENT RECORD [
        pad(0: 0..0): BOOLEAN,
        stop(0: 1..1): BOOLEAN];

The pad flag allows the character to have its width increased or decreased (in bits) for the line justification. The stop flag will specify a stop character to terminate a TextBlt operation.

    RasterInfos: TYPE = LONG POINTER TO Array Byte OF RaasterInfo;

    RasterInfo: TYPE = MACHINE DEPENDENT RECORD [
        leftKern: BOOLEAN,
        rightKern: BOOLEAN,
        offset: RasterOffset];

If RasterInfo.leftKern = `TRUE`, the character's raster has one column preceding the char's origin, and will be written into the destination bitmap with one column preceding the current position (bitPos). If RasterInfo.rightKern = `TRUE`, the raster extends one column past the spacing width into the space for the next character; that character's raster should begin coincident with the current character's last column (one column preceding where it would normally go). RasterInfo.offset is the offset for the address of the character's raster.

    RasterOffsetDomain: TYPE = CARDINAL[0..37777B];
    RasterOffset: TYPE = FontRasters RELATIVE POINTER[0..37777B] TO <> UNSPECIFIED;

    RasterOffsetFromDomain: PROC [domain: RassterOffsetDomain]
        RETURNS [RasterOffsetDomain] = INLINE {RETURN[LOOPHOLE[offset]]};

    maxLeftKern: CARDINAL = 1;
    maxRightKern: CARDINAL = 1;

maxLeftKern and maxRightKern support kerning up to one pixel in the respective direction.

Design Note: Although the architecture permits it, the specification is not intended to encourage the creation of a different fount format for each implementor of the processor. A new format may be specified only if significant performance improvement can be gained and is required.

### 8.4.3.2 TextBlt Arguments and Results

TextBlt's static arguments are passed via a short pointer to a record. The argument record must be aligned on a sixteen-word boundary. Arguments updated and returned by TextBlt are passed (and returned) on the stack (see the opcode description below).

    TextBltArg: TYPE = MACHINE DEPENDENT RECORD [
        reserved(0: 0..13): [0..37777B]  0,
        function(0: 14..15): Function,
        last(1): CARDINAL,
        text(2): LONG POINTER TO ARRAY CARDINAL OF BytePair,
        font(4): FontHandle,
        dst(6): LONG POINTER,
        dstBpl(8): CARDINAL,
        margin(9): CARDINAL,
        space(10): INTEGER,
        coord(11): LONG POINTER TO ARRAY CARDINAL OF CARDINAL];

TextBlt proceeds through the characters of arg.txt from index through arg.last unless a stop character is encountered (for example, not that index, shown in the TextBlt Routines, section, is a byte offset). It maintains the bitPos (position of the first bit of the character's raster) and the printPos (position of the first bit of the printed character) of the origin of each character, and increments the count of the number of pad characters processed. During the format function, the scan is also terminated before the right arg.margin (in micas) is passed. The display function ors the character's font bits into the destination bitmap specified by arg.dst and arg.dstBpl (bits per line). The resolve function saves the bitPos of the origin of each character in the array arg.coord.

Programming Note: Because of kerning, the display function may place bits into the destination bitmap to the left of the bitPos of the left most character and to the right of the right margin. It is the programmer's responsibility to initialize the bitPos to allow for the left kerning of the first character, and to supply a bitmap wide enough to allow for the maximum possible right kerning. (at present the maximum is one bit.)

Justification can be accomplished using the display and resolve function with appropriate setting of the arg.space and count values; arg.space is added to the width of every pad character (it may be negative), and count is incremented each time a pad character is encountered (it may also be initially negative). Since the amount of white space to be absorbed by (or squeezed out of) pad characters is rarely an even multiple of the number of pad characters, pad characters encountered have arg.space + 1 added to their width as long as count is negative. This if sixteen bits need to be added the width of the line in order to justify it, but it contains only thirteen pad characters, arg.space would be set to one, and count would be initialized to negative three. This operation will result in a widening of the first three pad characters by two bits each and the remaining ten pad characters by one bit each.

TextBlt returns, in place of the argument pointer on the stack, an indication of it completion condition: normal if the last character was processed, margin if the right margin was reached (format only), and stop if a terminating character was detected.

    Result: TYPE = MACHINE DEPENDENT {normal, margin, stop, unused};

The display font (arg.font), character array (arg.txt), and destination bitmap (arg.dst) must not cross 64K boundaries. (For bitmaps larger than 64K, the display lines can be created in a private buffer, then transferred to the display bitmap using the Bit Block Transfer instruction.) As in `BITBLT`, the maximum value of dstBpl is 32,677. This limitation also applies to horizontal bit positions. The effects of the instruction are undefined if there is any overlap in memory amount the arguments (arg), display font (arg.font), character array (arg.txt), widths array (arg.coord), or the destination bitmap (arg.dst).

### 8.4.3.3 Interruptibility

The Text Block Transfer instruction checks for interrupts after in processes each character. As with all block transfers, the intermediate state of the operation is saved on the stack when an interrupt is detected. The saving operation consists of pushing the updated values of the original arguments. The actual format of the stack can be processor-dependent.

Design Note: If any of the values of the arguments (in memory) change between the time of the interrupt and the subsequent restart of the instruction, the effects of the instruction are undefined. The original values in the argument record are thereby allowed to be save as part of the intermediate state.

### 8.4.3.4 TextBlt Routines

#### TXTBLT Text Block Transfer

    TXTBLT: PROCEDURE =
        BEGIN
        result: Result;
        arg: TxbBltArg;
        font: FontRecord;
        ptr: POINTER TO TxtBltArg = Pop[];
        count: INTEGER  Pop[];
        PrintPos: CARDINAL  Pop[];
        bitPos: CARDINAL  Pop[];
        index: CARDINAL  Pop[];
        MinimalStack[];
        arg  FetchTxtBltArg[ptr];
        IF arg.reserved # 0 OR arg.dstBpl > 32767 THEN ERROR;
        UNTIL index > arg.last DO
            char: BYTE;
            IF arg.function = resolve THEN Store[@arg.coord[index]]  bitPos;
            char  FetchChar[arg.text, index];
            IF font.flags[char].stop THEN GOTO Stop;
            IF printPos + font.printerWidths[char] > arg.margin
                THEN GOTO Margin;
            IF arg.function = display THEN DisplayChar[bitPos, char];
            bitPos  bitPos + font.spacingWidths[char];
            printPos  printPos + font.printerWidths[char];
            IF font.flags[char].pad THEN
                BEGIN
                IF arg.functon = (display OR format) THEN
                    BEGIN
                    bitPos  bitPos + arg.space;
                    IF count < 0 THEN bitPos  bitPos + 1;
                    END;
                count  count + 1;
                END;
            index  index + 1;
            IF InterruptPending[] THEN GOTO Suspend;
            REPEAT
                Suspend =>
                    BEGIN
                    PushState[prt];
                    PC  savedPC;
                    GOTO Done;
                    END;
                Stop => result  stop;
                Margin => result  margin;
                FINISHED => reslult  normal;
            ENDLOOP;
        PushState[result];
        EXITS Done => NULL;
        END;

The routines below are used by the `TXTBLT` code. They fetch the argument record, fetch a character from the text array, and move a character from the display font into the destination bitmap. The type BitAddress and the routines Bump, ReadBit, and WriteBit are defined in §8.4.1.

    FetchTxtBltArg: PROCEDURE [ptr: POITER TO TxtBltArg]
        RETURNS [TExtBltArg] =
        BEGIN
        ArgIndex: TYPE = [0..SIZE[TxtBltArg]);
        temp: ARRAY ArgIndex OF UNSPECIFIED;
        IF And[ptr, 17B] # 0 THEN ERROR;
        FOR I: ArgIndex IN ArgIndex DO
            temp[i]  FetchMds[ptr + i]];
            ENDLOOP;
        RETURN [LOOPHOLE[temp]];
        END;

    FetchChar: PROCEDURE [
        ptr: LONG POINTER TO ARRAY CARDINAL OF BytePair, index: CARDINAL]
        RETURNS [BYTE] =
        BEGIN
        data: BytePair = Fetch[@ptr[index / 2]];
        RETURN [IF (index MOD 2) = 0 THEN data.left ELSE data.right];
        END;

    DisplayChar: PROCEDURE [pos: CARDINAL, char: BTTE] =
        BEGIN
        count: CARDINAL  0;
        dst: BitAddress  [word: arg.dst, bit: 0];
        width: CARDINAL = DisplayWidth[arg.font, char];
        pos  pos - (IF font.rasterInfo[char].leftKern THEN 1 ELSE 0)];
        THROUGH [0..font.height) DO
            FOR inc: CARDINAL IN [0..width) DO
                bit: BIT  Bit[arg.font, char, count];
                offset: INTEGER  INTEGER[pos + inc];
                WriteBit[dst, offset, Or[bit, ReadBit[dst, offset]]];
                count  count + 1;
                ENDLOOP;
            dst  Bump[dst, LONG[INTEGER[arg.dstBpl]]];
            ENDLOOP;
        END;

    Bit: PROC [font: FontHandle, char: Byte, scanLine, Pixel: CARDINAL]
        RETURNS[Bit] = {
        raster: LONG POINTER TO PACKED ARRAY OF BIT =
            LOOPHOLE[font.raster + font.rasterInfos[char].offset];
        bit: CARDINAL = scanLine * displayWidth[font, char] + pixel;
        RETURN [raster[bit]]};

[This definition of Bit has been recast above in terms of explicit scanline and pixel.]

    DisplayWicth: PROC [font: FontHandle, char: Byte]
        RETURNS[CARDINAL] = {
        RETURN [font.spacingWidths[char]
            + (IF font.rasterInfo[char].leftKern THEN 1 ELSE 0)
            + (IF font.rasterInfo[char].rightKern THEN 1 ELSE 0)]};

Programming Note: The programmer should ensure that the calculation pos - (IF font.rasterInfo[char].leftKern THEN 1 ELSE 0) does not underflow, that is, the pos of the first character must allow for its left kerning. The programmer must also ensure that the maximum offset does not exceed 32,767.

Implementation Note: Because the destination bits per lie does not exceed 32,767, the conversion of arg.dstBpl to a long integer can be performed buy supplying high-order zeros. Likewise, the conversion of pos + inc to an integer need not be range-checked.

Design Note: For short (or narrow) characters, considerable optimization of the DisplayChar inner loops is possible by adding information to the font format (the starting vertical location and the height of each character are examples). Because the character is ored into the destination, the white space surrounding the character need not actually be stored in the bitmap. Not, however, that such optimizations may substantially increase the amount of storage required for the font.

    PushState: PROCEDURE [data: UNSPECIFIED] =
        BEGIN
        Push[index];
        Push[bitPos];
        Push[printPos];
        Push[count];
        Push[data];
        END;

PushState handles the stack both for the intermediate state (in the case of an interrupt) and for the final results of the instruction. In the former case the last item pushed in the pointer to the argument record, in the latter case the last item is the result of the TextBlt.

Previous [TOC] Next

[last edited 4/11/99 1:04PM]