Previous | Table of Contents | Next

#### Copying Chunky Bitmaps to VGA Memory Using Write Mode 2

Let’s take a look at two examples of write mode 2 in action. Listing 27.1 presents a program that uses write mode 2 to copy a graphics image in chunky format to the VGA. In chunky format adjacent bits in a single byte make up each pixel: mode 4 of the CGA, EGA, and VGA is a 2-bit-per-pixel chunky mode, and mode 13H of the VGA is an 8-bit-per-pixel chunky mode. Chunky format is convenient, since all the information about each pixel is contained in a single byte; consequently chunky format is often used to store bitmaps in system memory.

Unfortunately, VGA memory is organized as a planar rather than chunky bitmap in modes 0DH through 12H, with the bits that make up each pixel spread across four planes. The conversion from chunky to planar format in write mode 0 is quite a nuisance, requiring a good deal of bit manipulation. In write mode 2, however, the conversion becomes a snap, as shown in Listing 27.1. Once the VGA is placed in write mode 2, the lower four bits (the lower nibble) of the CPU byte (a single 4-bit chunky pixel) become eight planar pixels, all the same color. As discussed in Chapter 25, the bit mask makes it possible to narrow the effect of the CPU write down to a single pixel.

Given the above, conversion of a chunky 4-bit-per-pixel bitmap to the VGA’s planar format in write mode 2 is trivial. First, the Bit Mask register is set to allow only the VGA display memory bits corresponding to the leftmost chunky pixel of the two stored in the first chunky bitmap byte to be modified. Next, the destination byte in display memory is read in order to load the latches. Then a byte containing two chunky pixels is read from the chunky bitmap in system memory, and the byte is rotated four bits to the right to get the leftmost chunky pixel in position. This rotated byte is written to the destination byte; since write mode 2 is active, each bit of the chunky pixel goes to its respective plane, and since the Bit Mask register is set up to allow only one bit in each plane to be modified, a single pixel in the color of the chunky pixel is written to VGA memory.

This process is then repeated for the rightmost chunky pixel, if necessary, and repeated again for as many pixels as there are in the image.

LISTING 27.1 L27-1.ASM

    ; Program to illustrate one use of write mode 2 of the VGA and EGA by
    ; animating the image of an “A” drawn by copying it from a chunky
    ; bit-map in system memory to a planar bit-map in VGA or EGA memory.
    ;
    ; 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
    DISPLAY_MEMORY_SEGMENT  equ     0a000h
    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_MASKequ     8       ;index of Bit Mask reg

    Data    segment para common ‘DATA’
    ;
    ; Current location of “A” as it is animated across the screen.
    ;
    CurrentX        dw      ?
    CurrentY        dw      ?
    RemainingLength dw      ?
    ;
    ; Chunky bit-map image of a yellow “A” on a bright blue background
    ;
    AImage          label   byte
                    dw      13, 13          ;width, height in pixels
                    db      000h, 000h, 000h, 000h, 000h, 000h, 000h
                    db      009h, 099h, 099h, 099h, 099h, 099h, 000h
                    db      009h, 099h, 099h, 099h, 099h, 099h, 000h
                    db      009h, 099h, 099h, 0e9h, 099h, 099h, 000h
                    db      009h, 099h, 09eh, 0eeh, 099h, 099h, 000h
                    db      009h, 099h, 0eeh, 09eh, 0e9h, 099h, 000h
                    db      009h, 09eh, 0e9h, 099h, 0eeh, 099h, 000h
                    db      009h, 09eh, 0eeh, 0eeh, 0eeh, 099h, 000h
                    db      009h, 09eh, 0e9h, 099h, 0eeh, 099h, 000h
                    db      009h, 09eh, 0e9h, 099h, 0eeh, 099h, 000h
                    db      009h, 099h, 099h, 099h, 099h, 099h, 000h
                    db      009h, 099h, 099h, 099h, 099h, 099h, 000h
                    db      000h, 000h, 000h, 000h, 000h, 000h, 000h
    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)
    ;
    ; Prepare for animation.
    ;
            mov     [CurrentX],0
            mov     [CurrentY],200
            mov     [RemainingLength],600   ;move 600 times
    ;
    ; Animate, repeating RemainingLength times. It’s unnecessary to erase
    ; the old image, since the one pixel of blank fringe around the image
    ; erases the part of the old image not overlapped by the new image.
    ;
    AnimationLoop:
            mov     bx,[CurrentX]
            mov     cx,[CurrentY]
            mov     si,offset AImage
            call    DrawFromChunkyBitmap    ;draw the “A” image
            inc     [CurrentX]              ;move one pixel to the right

            mov     cx,0                    ;delay so we don’t move the
    DelayLoop:                              ; image too fast; adjust as
                                            ; needed
            loop    DelayLoop

            dec     [RemainingLength]
            jnz     AnimationLoop
    ;
    ; 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
    Start   endp
    ;
    ; Draw an image stored in a chunky-bit map into planar VGA/EGA memory
    ; at the specified location.
    ;
    ; Input:
    ;       BX = X screen location at which to draw the upper-left corner
    ;               of the image
    ;       CX = Y screen location at which to draw the upper-left corner
    ;               of the image
    ;       DS:SI = pointer to chunky image to draw, as follows:
    ;               word at 0: width of image, in pixels
    ;               word at 2: height of image, in pixels
    ;               byte at 4: msb/lsb = first & second chunky pixels,
    ;                       repeating for the remainder of the scan line
    ;                       of the image, then for all scan lines. Images
    ;                       with odd widths have an unused null nibble
    ;                       padding each scan line out to a byte width
    ;
    ; AX, BX, CX, DX, SI, DI, ES destroyed.
    ;
    DrawFromChunkyBitmap    proc    near
            cld
    ;
    ; Select write mode 2.
    ;
            mov     dx,GC_INDEX
            mov     al,GRAPHICS_MODE
            out     dx,al
            inc     dx
            mov     al,02h
            out     dx,al
    ;
    ; Enable writes to all 4 planes.
    ;
            mov     dx,SC_INDEX
            mov     al,MAP_MASK
            out     dx,al
            inc     dx
            mov     al,0fh
            out     dx,al
    ;
    ; Point ES:DI to the display memory byte in which the first pixel
    ; of the image goes, with AH set up as the bit mask to access that
    ; pixel within the addressed byte.
    ;
            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     ah,80h          ;set AH to the bit mask for the
            shr     ah,cl           ; initial pixel
            shr     bx,1
            shr     bx,1
            shr     bx,1            ;X in bytes
            add     di,bx           ;offset of upper-left byte of image
            mov     bx,DISPLAY_MEMORY_SEGMENT
            mov     es,bx           ;ES:DI points to the byte at which the
                                    ; upper left of the image goes
    ;
    ; Get the width and height of the image.
    ;
            mov     cx,[si]         ;get the width
            inc     si
            inc     si
            mov     bx,[si]      ;get the height
            inc     si
            inc     si
            mov     dx,GC_INDEX
            mov     al,BIT_MASK
            out     dx,al           ;leave the GC Index register pointing
            inc     dx              ; to the Bit Mask register
    RowLoop:

            push    ax              ;preserve the left column’s bit mask
            push    cx              ;preserve the width
            push    di              ;preserve the destination offset

    ColumnLoop:
            mov     al,ah
            out     dx,al           ;set the bit mask to draw this pixel
            mov     al,es:[di]      ;load the latches
            mov     al,[si]         ;get the next two chunky pixels
            shr     al,1
            shr     al,1
            shr     al,1
            shr     al,1            ;move the first pixel into the lsb
            stosb                   ;draw the first pixel
            ror     ah,1           ;move mask to next pixel position
            jc      CheckMorePixels ;is next pixel in the adjacent byte?
            dec     di              ;no

    CheckMorePixels:
            dec     cx              ;see if there are any more pixels
            jz      AdvanceToNextScanLine ; across in image
            mov     al,ah
            out     dx,al           ;set the bit mask to draw this pixel
            mov     al,es:[di]      ;load the latches
            lodsb                   ;get the same two chunky pixels again
                              ; and advance pointer to the next
                                   ; two pixels
            stosb                   ;draw the second of the two pixels
            ror     ah,1            ;move mask to next pixel position
            jc      CheckMorePixels2 ;is next pixel in the adjacent byte?
            dec     di              ;no

    CheckMorePixels2:
            loop    ColumnLoop      ;see if there are any more pixels
                                    ; across in the image
            jmp     short CheckMoreScanLines

    AdvanceToNextScanLine:
            inc     si              ;advance to the start of the next
                                    ; scan line in the image

    CheckMoreScanLines:
            pop     di              ;get back the destination offset
            pop     cx              ;get back the width
            pop     ax              ;get back the left column’s bit mask
            add     di,SCREEN_WIDTH_IN_BYTES
                                    ;point to the start of the next scan
                                    ; line of the image
            dec     bx              ;see if there are any more scan lines
            jnz     RowLoop         ; in the image
            ret
    DrawFromChunkyBitmap    endp
    Code    ends
            end     Start

Previous | Table of Contents | Next

Graphics Programming Black Book © 2001 Michael Abrash