Previous | Table of Contents | Next

“That’s an interesting application of write mode 2,” you may well say, “but is it really useful?” While the ability to convert chunky bitmaps into VGA bitmaps does have its uses, Listing 27.1 is primarily intended to illustrate the mechanics of write mode 2.

| For performance, it’s best to store 16-color bitmaps in pre-separated four-plane format in system memory, and copy one plane at a time to the screen. Ideally, such bitmaps should be copied one scan line at a time, with all four planes completed for one scan line before moving on to the next. I say this because when entire images are copied one plane at a time, nasty transient color effects can occur as one plane becomes visibly changed before other planes have been modified.

#### Drawing Color-Patterned Lines Using Write Mode 2

A more serviceable use of write mode 2 is shown in the program presented in Listing 27.2. The program draws multicolored horizontal, vertical, and diagonal lines, basing the color patterns on passed color tables. Write mode 2 is ideal because in this application color can vary from one pixel to the next, and in write mode 2 all that’s required to set pixel color is a change of the lower nibble of the byte written by the CPU. Set/reset could be used to achieve the same result, but an index/data pair of OUTs would be required to set the Set/Reset register to each new color. Similarly, the Map Mask register could be used in write mode 0 to set pixel color, but in this case not only would an index/data pair of OUTs be required but there would also be no guarantee that data already in display memory wouldn’t interfere with the color of the pixel being drawn, since the Map Mask register allows only selected planes to be drawn to.

Listing 27.2 is hardly a comprehensive line drawing program. It draws only a few special line cases, and although it is reasonably fast, it is far from the fastest possible code to handle those cases, because it goes through a dot-plot routine and because it draws horizontal lines a pixel rather than a byte at a time. Write mode 2 would, however, serve just as well in a full-blown line drawing routine. For any type of patterned line drawing on the VGA, the basic approach remains the same: Use the bit mask to select the pixel (or pixels) to be altered and use the CPU byte in write mode 2 to select the color in which to draw.

LISTING 27.2 L27-2.ASM

    ; Program to illustrate one use of write mode 2 of the VGA and EGA by
    ; drawing lines in color patterns.
    ;
    ; Assemble with MASM or TASM
    ;
    ; By Michael Abrash
    ;
    Stack   segment para stack ‘STACK’
            db      512 dup(0)
    Stack   ends

    SCREEN_WIDTH_IN_BYTES equ     80
    GRAPHICS_SEGMENT      equ    0a000h   ;mode 10 bit-map segment
    SC_INDEX              equ     3c4h    ;Sequence Controller Index register
    MAP_MASK              equ     2       ;index of Map Mask register
    GC_INDEX              equ     03ceh   ;Graphics Controller Index reg
    GRAPHICS_MODE         equ     5       ;index of Graphics Mode reg
    BIT_MASK              equ     8       ;index of Bit Mask reg

    Data    segment para common ‘DATA’
    Pattern0        db      16
                    db      0, 1, 2, 3, 4, 5, 6, 7, 8
                    db      9, 10, 11, 12, 13, 14, 15
    Pattern1        db      6
                    db      2, 2, 2, 10, 10, 10
    Pattern2        db      8
                    db      15, 15, 15, 0, 0, 15, 0, 0
    Pattern3        db      9
                    db      1, 1, 1, 2, 2, 2, 4, 4, 4
    Data    ends

    Code    segment para public ‘CODE’
            assume  cs:Code, ds:Data
    Start   proc    near
            mov     ax,Data
            mov     ds,ax
            mov     ax,10h
            int     10h             ;select video mode 10h (640×350)
    ;
    ; Draw 8 radial lines in upper-left quadrant in pattern 0.
    ;
            mov     bx,0
            mov     cx,0
            mov     si,offset Pattern0
            call    QuadrantUp
    ;
    ; Draw 8 radial lines in upper-right quadrant in pattern 1.
    ;
            mov     bx,320
            mov     cx,0
            mov     si,offset Pattern1
            call    QuadrantUp
    ;
    ; Draw 8 radial lines in lower-left quadrant in pattern 2.
    ;
            mov     bx,0
            mov     cx,175
            mov     si,offset Pattern2
            call    QuadrantUp
    ;
    ; Draw 8 radial lines in lower-right quadrant in pattern 3.
    ;
            mov     bx,320
            mov     cx,175
            mov     si,offset Pattern3
            call    QuadrantUp
    ;
    ; Wait for a key before returning to text mode and ending.
    ;
            mov     ah,01h
            int     21h
            mov     ax,03h
            int     10h
            mov     ah,4ch
            int     21h
    ;
    ; Draws 8 radial lines with specified pattern in specified mode 10h
    ; quadrant.
    ;
    ; Input:
    ;       BX = X coordinate of upper left corner of quadrant
    ;       CX = Y coordinate of upper left corner of quadrant
    ;       SI = pointer to pattern, in following form:
    ;               Byte 0: Length of pattern
    ;               Byte 1: Start of pattern, one color per byte
    ;
    ; AX, BX, CX, DX destroyed
    ;
    QuadrantUp      proc    near
            add     bx,160
            add     cx,87           ;point to the center of the quadrant
            mov     ax,0
            mov     dx,160
            call    LineUp          ;draw horizontal line to right edge
            mov     ax,1
            mov     dx,88
            call    LineUp          ;draw diagonal line to upper right
            mov     ax,2
            mov     dx,88
            call    LineUp          ;draw vertical line to top edge
            mov     ax,3
            mov     dx,88
            call    LineUp          ;draw diagonal line to upper left
            mov     ax,4
            mov     dx,161
            call    LineUp          ;draw horizontal line to left edge
            mov     ax,5
            mov     dx,88
            call    LineUp          ;draw diagonal line to lower left
            mov     ax,6
            mov     dx,88
            call    LineUp          ;draw vertical line to bottom edge
            mov     ax,7
            mov     dx,88
            call    LineUp          ;draw diagonal line to bottom right
            ret
    QuadrantUp      endp
    ;
    ; Draws a horizontal, vertical, or diagonal line (one of the eight
    ; possible radial lines) of the specified length from the specified
    ; starting point.
    ;
    ; Input:
    ;       AX = line direction, as follows:
    ;               3  2  1
    ;               4  *  0
    ;               5  6  7
    ;       BX = X coordinate of starting point
    ;       CX = Y coordinate of starting point
    ;       DX = length of line (number of pixels drawn)
    ;
    ; All registers preserved.
    ;
    ; Table of vectors to routines for each of the 8 possible lines.
    ;
    LineUpVectors   label   word
            dw      LineUp0, LineUp1, LineUp2, LineUp3
            dw      LineUp4, LineUp5, LineUp6, LineUp7

    ;
    ; Macro to draw horizontal, vertical, or diagonal line.
    ;
    ; Input:
    ;       XParm = 1 to draw right, -1 to draw left, 0 to not move horz.
    ;       YParm = 1 to draw up, -1 to draw down, 0 to not move vert.
    ;       BX = X start location
    ;       CX = Y start location
    ;       DX = number of pixels to draw
    ;       DS:SI = line pattern
    ;
    MLineUp macro   XParm, YParm
            local   LineUpLoop, CheckMoreLine
            mov     di,si           ;set aside start offset of pattern
            lodsb                   ;get length of pattern
            mov     ah,al

    LineUpLoop:
            lodsb                   ;get color of this pixel...
            call    DotUpInColor    ;...and draw it
    if XParm EQ 1
            inc     bx
    endif
    if XParm EQ -1
            dec     bx
    endif
    if YParm EQ 1
            inc     cx
    endif
    if YParm EQ -1
            dec     cx
    endif
            dec     ah              ;at end of pattern?
            jnz     CheckMoreLine
            mov     si,di           ;get back start of pattern
            lodsb
            mov     ah,al           ;reset pattern count

    CheckMoreLine:
            dec     dx
            jnz     LineUpLoop
            jmp     LineUpEnd
            endm

    LineUp  proc    near
            push    ax
            push    bx
            push    cx
            push    dx
            push    si
            push    di
            push    es

            mov     di,ax

            mov     ax,GRAPHICS_SEGMENT
            mov     es,ax

            push    dx              ;save line length
    ;
    ; Enable writes to all planes.
    ;
            mov     dx,SC_INDEX
            mov     al,MAP_MASK
            out     dx,al
            inc     dx
            mov     al,0fh
            out     dx,al
    ;
    ; Select write mode 2.
    ;
            mov     dx,GC_INDEX
            mov     al,GRAPHICS_MODE
            out     dx,al
            inc     dx
            mov     al,02h
            out     dx,al
    ;
    ; Vector to proper routine.
    ;
            pop     dx              ;get back line length

            shl     di,1
            jmp     cs:[LineUpVectors+di]
    ;
    ; Horizontal line to right.
    ;
    LineUp0:
            MLineUp 1, 0
    ;
    ; Diagonal line to upper right.
    ;
    LineUp1:
            MLineUp 1, -1
    ;
    ; Vertical line to top.
    ;
    LineUp2:
            MLineUp 0, -1
    ;
    ; Diagonal line to upper left.
    ;
    LineUp3:
            MLineUp -1, -1
    ;
    ; Horizontal line to left.
    ;
    LineUp4:
            MLineUp -1, 0
    ;
    ; Diagonal line to bottom left.
    ;
    LineUp5:
            MLineUp -1, 1
    ;
    ; Vertical line to bottom.
    ;
    LineUp6:
            MLineUp 0, 1
    ;
    ; Diagonal line to bottom right.
    ;
    LineUp7:
            MLineUp 1, 1

    LineUpEnd:
            pop     es
            pop     di
            pop     si
            pop     dx
            pop     cx
            pop     bx
            pop     ax
            ret
    LineUp  endp
    ;
    ; Draws a dot in the specified color at the specified location.
    ; Assumes that the VGA is in write mode 2 with writes to all planes
    ; enabled and that ES points to display memory.
    ;
    ; Input:
    ;       AL = dot color
    ;       BX = X coordinate of dot
    ;       CX = Y coordinate of dot
    ;       ES = display memory segment
    ;
    ; All registers preserved.
    ;
    DotUpInColor    proc    near
            push    bx
            push    cx
            push    dx
            push    di
    ;
    ; Point ES:DI to the display memory byte in which the pixel goes, with
    ; the bit mask set up to access that pixel within the addressed byte.
    ;
            push    ax              ;preserve dot color
            mov     ax,SCREEN_WIDTH_IN_BYTES
            mul     cx              ;offset of start of top scan line
            mov     di,ax
            mov     cl,bl
            and     cl,111b
            mov     dx,GC_INDEX
            mov     al,BIT_MASK
            out     dx,al
            inc     dx
            mov     al,80h
            shr     al,cl
            out     dx,al           ;set the bit mask for the pixel
            shr     bx,1
            shr     bx,1
            shr     bx,1            ;X in bytes
            add     di,bx           ;offset of byte pixel is in
            mov     al,es:[di]      ;load latches
            pop     ax              ;get back dot color
            stosb                   ;write dot in desired color

            pop     di
            pop     dx
            pop     cx
            pop     bx
            ret
    DotUpInColor    endp
    Start   endp
    Code    ends
            end     Start

Previous | Table of Contents | Next

Graphics Programming Black Book © 2001 Michael Abrash