Previous | Table of Contents | Next

### Designing from a Mode X Perspective

Listing 47.4 shows Mode X rectangle fill code. The plane is selected for each pixel in turn, with drawing cycling from plane 0 to plane 3, then wrapping back to plane 0. This is the sort of code that stems from a write-pixel line of thinking; it reflects not a whit of the unique perspective that Mode X demands, and although it looks reasonably efficient, it is in fact some of the slowest graphics code you will ever see. I’ve provided Listing 47.4 partly for illustrative purposes, but mostly so we’ll have a point of reference for the substantial speed-up that’s possible with code that’s designed from a Mode X perspective.

LISTING 47.4 L47-4.ASM

    ; Mode X (320x240, 256 colors) rectangle fill routine. Works on all
    ; VGAs. Uses slow approach that selects the plane explicitly for each
    ; pixel. Fills up to but not including the column at EndX and the row
    ; at EndY. No clipping is performed.
    ; C near-callable as:
    ;
    ;    void FillRectangleX(int StartX, int StartY, int EndX, int EndY,
    ;       unsigned int PageBase, int Color);

    SC_INDEX      equ    03c4h              ;Sequence Controller Index
    MAP_MASK      equ    02h                ;index in SC of Map Mask register
    SCREEN_SEG    equ    0a000h             ;segment of display memory in mode X
    SCREEN_WIDTH  equ    80                 ;width of screen in bytes from one scan line
                                            ; to the next
    parms   struc
            dw      2 dup (?)               ;pushed BP and return address
    StartX  dw      ?                  ;X coordinate of upper left corner of rect
    StartY  dw      ?                  ;Y coordinate of upper left corner of rect
    EndX    dw      ?                  ;X coordinate of lower right corner of rect
                                       ; (the row at EndX is not filled)
    EndY    dw      ?                       ;Y coordinate of lower right corner of rect
                                            ; (the column at EndY is not filled)
    PageBase dw     ?                       ;base offset in display memory of page in
                                            ; which to fill rectangle
    Color   dw      ?                       ;color in which to draw pixel
    parms   ends

            .model  small
            .code
            public  _FillRectangleX
    _FillRectangleX proc    near
            push    bp                      ;preserve caller’s stack frame
            mov     bp,sp                   ;point to local stack frame
            push    si                      ;preserve caller’s register variables
            push    di

            mov     ax,SCREEN_WIDTH
            mul     [bp+StartY]             ;offset in page of top rectangle scan line
            mov     di,[bp+StartX]
            shr     di,1
            shr     di,1                    ;X/4 = offset of first rectangle pixel in scan
                                            ; line
            add     di,ax                   ;offset of first rectangle pixel in page
            add     di,[bp+PageBase]        ;offset of first rectangle pixel in
                                            ; display memory
            mov     ax,SCREEN_SEG
            mov     es,ax                   ;point ES:DI to the first rectangle pixel’s
                                            ; address
            mov     dx,SC_INDEX             ;set the Sequence Controller Index to
            mov     al,MAP_MASK             ; point to the Map Mask register
            out     dx,al
            inc     dx                      ;point DX to the SC Data register
            mov     cl,byte ptr [bp+StartX]
            and     cl,011b                 ;CL = first rectangle pixel’s plane
            mov     al,01h
            shl     al,cl                   ;set only the bit for the pixel’s plane to 1
            mov     ah,byte ptr [bp+Color]  ;color with which to fill
            mov     bx,[bp+EndY]
            sub     bx,[bp+StartY]          ;BX = height of rectangle
            jle     FillDone                ;skip if 0 or negative height
            mov     si,[bp+EndX]
            sub     si,[bp+StartX]          ;CX = width of rectangle
            jle     FillDone                ;skip if 0 or negative width
    FillRowsLoop:
            push    ax                      ;remember the plane mask for the left edge
            push    di                      ;remember the start offset of the scan line
            mov     cx,si                   ;set count of pixels in this scan line
    FillScanLineLoop:
            out     dx,al                   ;set the plane for this pixel
            mov     es:[di],ah              ;draw the pixel
            shl     al,1                    ;adjust the plane mask for the next pixel’s
            and     al,01111b               ; bit, modulo 4
            jnz     AddressSet              ;advance address if we turned over from
            inc     di                      ; plane 3 to plane 0
            mov     al,00001b               ;set plane mask bit for plane 0
    AddressSet:
            loop    FillScanLineLoop
            pop     di                      ;retrieve the start offset of the scan line
            add     di,SCREEN_WIDTH         ;point to the start of the next scan
                                            ; line of the rectangle
            pop     ax                      ;retrieve the plane mask for the left edge
            dec     bx                      ;count down scan lines
            jnz     FillRowsLoop
    FillDone:
            pop     di                      ;restore caller’s register variables
            pop     si
            pop     bp                      ;restore caller’s stack frame
            ret
    _FillRectangleX endp
            end

The two major weaknesses of Listing 47.4 both result from selecting the plane on a pixel by pixel basis. First, endless OUTs (which are particularly slow on 386s, 486s, and Pentiums, much slower than accesses to display memory) must be performed, and, second, REP STOS can’t be used. Listing 47.5 overcomes both these problems by tailoring the fill technique to the organization of display memory. Each plane is filled in its entirety in one burst before the next plane is processed, so only five OUTs are required in all, and REP STOS can indeed be used; I’ve used REP STOSB in Listings 47.5 and 47.6. REP STOSW could be used and would improve performance on most VGAs; however, REP STOSW requires extra overhead to set up, so it can be slower for small rectangles, especially on 8-bit VGAs. Note that doing an entire plane at a time can produce a “fading-in” effect for large images, because all columns for one plane are drawn before any columns for the next. If this is a problem, the four planes can be cycled through once for each scan line, rather than once for the entire rectangle.

Listing 47.5 is 2.5 times faster than Listing 47.4 at clearing the screen on a 20-MHz cached 386 with a Paradise VGA. Although Listing 47.5 is slightly slower than an equivalent mode 13H fill routine would be, it’s not grievously so.

Previous | Table of Contents | Next

Graphics Programming Black Book © 2001 Michael Abrash