Previous | Table of Contents | Next

### A Test Program for Color Cycling

Anyway, the choice of how to load the DAC is yours. Given that I’m not providing you with any hard-and-fast rules (mainly because there don’t seem to be any), what you need is a tool so that you can experiment with various DAC-loading approaches for yourself, and that’s exactly what you’ll find in Listing 34.1.

Listing 34.1 draws a band of vertical lines, each one pixel wide, across the screen. The attribute of each vertical line is one greater than that of the preceding line, so there’s a smooth gradient of attributes from left to right. Once everything is set up, the program starts cycling the colors stored in however many DAC locations are specified by the CYCLE_SIZE equate; as many as all 256 DAC locations can be cycled. (Actually, CYCLE_SIZE-1 locations are cycled, because location 0 is kept constant in order to keep the background and border colors from changing, but CYCLE_SIZE locations are loaded, and it’s the number of locations we can load without problems that we’re interested in.)

LISTING 34.1 L34-1.ASM

    ; Fills a band across the screen with vertical bars in all 256
    ; attributes, then cycles a portion of the palette until a key is
    ; pressed.
    ; Assemble with MASM or TASM

    USE_BIOS           equ 1                    ;set to 1 to use BIOS functions to access the
                                                ; DAC, 0 to read and write the DAC directly
    GUARD_AGAINST_INTS equ 1                    ;1 to turn off interrupts and set write index
                                                ; before loading each DAC location, 0 to rely
                                                ; on the DAC auto-incrementing
    WAIT_VSYNC         equ 1                    ;set to 1 to wait for the leading edge of
                                                ; vertical sync before accessing the DAC, 0
                                                ; not to wait
    NOT_8088           equ 0                    ;set to 1 to use REP INSB and REP OUTSB when
                                                ; accessing the  DAC directly, 0 to use
                                                ; IN/STOSB and LODSB/OUT
    CYCLE_SIZE         equ 256                  ;# of DAC locations to cycle, 256 max
    SCREEN_SEGMENT     equ 0a000h               ;mode 13h display memory segment
    SCREEN_WIDTH_IN_BYTES equ 320               ;# of bytes across the screen in mode 13h
    INPUT_STATUS_1     equ 03dah                ;input status 1 register port
    DAC_READ_INDEX     equ 03c7h                ;DAC Read Index register
    DAC_WRITE_INDEX    equ 03c8h                ;DAC Write Index register
    DAC_DATA           equ 03c9h                ;DAC Data register

    if NOT_8088
            .286
    endif   ;NOT_8088

            .model  small
            .stack  100h
            .data
    ;Storage for all 256 DAC locations, organized as one three-byte
    ; (actually three 6-bit values; upper two bits of each byte aren't
    ; significant) RGB triplet per color.
    PaletteTempdb   256*3 dup(?)
            .code
    start:
            mov     ax,@data
            mov     ds,ax

    ;Select VGA's standard 256-color graphics mode, mode 13h.
            mov     ax,0013h                    ;AH = 0: set mode function,
            int     10h                         ; AL = 13h: mode # to set

    ;Read all 256 DAC locations into PaletteTemp (3 6-bit values, one
    ; each for red, green, and blue, per DAC location).

    if WAIT_VSYNC
    ;Wait for the leading edge of the vertical sync pulse; this ensures
    ; that we read the DAC starting during the vertical non-display
    ; period.
            mov     dx,INPUT_STATUS_1
    WaitNotVSync:                               ;wait to be out of vertical sync
            in      al,dx
            and     al,08h
            jnz     WaitNotVSync
    WaitVSync:                                  ;wait until vertical sync begins
            in      al,dx
            and     al,08h
            jz      WaitVSync
    endif                                       ;WAIT_VSYNC

    if USE_BIOS
            mov     ax,1017h                    ;AH = 10h: set DAC function,
                                                ; AL = 17h: read DAC block subfunction
            sub     bx,bx                       ;start with DAC location 0
            mov     cx,256                      ;read out all 256 locations
            mov     dx,seg PaletteTemp
            mov     es,dx
            mov     dx,offset PaletteTemp       ;point ES:DX to array in which
                                                ; the DAC values are to be stored
            int     10h                         ;read the DAC
    else                                        ;!USE_BIOS
     if GUARD_AGAINST_INTS
            mov     cx,CYCLE_SIZE               ;# of DAC locations to load
            mov     di,seg PaletteTemp
            mov     es,di
            mov     di,offset PaletteTemp       ;dump the DAC into this array
            sub     ah,ah                       ;start with DAC location 0
    DACStoreLoop:
            mov     dx,DAC_READ_INDEX
            mov     al,ah
            cli
            out     dx,al                       ;set the DAC location #
            mov     dx,DAC_DATA
            in      al,dx                       ;get the red component
            stosb
            in      al,dx                       ;get the green component
            stosb
            in      al,dx                       ;get the blue component
            stosb
            sti
            inc     ah
            loopDACStoreLoop
     else;!GUARD_AGAINST_INTS
            mov     dx,DAC_READ_INDEX
            sub     al,al
            out     dx,al                       ;set the initial DAC location to 0
            mov     di,seg PaletteTemp
            mov     es,di
            mov     di,offset PaletteTemp       ;dump the DAC into this array
            mov     dx,DAC_DATA
      if NOT_8088
            mov     cx,CYCLE_SIZE*3
            rep     insb                        ;read CYCLE_SIZE DAC locations at once
      else;!NOT_8088
            mov     cx,CYCLE_SIZE               ;# of DAC locations to load
    DACStoreLoop:
            in      al,dx                       ;get the red component
            stosb
            in      al,dx                       ;get the green component
            stosb
            in      al,dx                       ;get the blue component
            stosb
            loop    DACStoreLoop
      endif                                     ;NOT_8088
     endif                                      ;GUARD_AGAINST_INTS
    endif                                       ;USE_BIOS

    ;Draw a series of 1-pixel-wide vertical bars across the screen in
    ; attributes 1 through 255.
            mov     ax,SCREEN_SEGMENT
            mov     es,ax
            mov     di,50*SCREEN_WIDTH_IN_BYTES ;point ES:DI to the start
                                                ; of line 50 on the screen
            cld
            mov     dx,100                      ;draw 100 lines high
    RowLoop:
            mov     al,1                        ;start each line with attr 1
            mov     cx,SCREEN_WIDTH_IN_BYTES    ;do a full line across
    ColumnLoop:
            stosb                               ;draw a pixel
            add     al,1                        ;increment the attribute
            ad      cal,0                       ;if the attribute just turned
                                                ; over to 0, increment it to 1
                                                ; because we're not going to
                                                ; cycle DAC location 0, so
                                                ; attribute 0 won't change
            loop    ColumnLoop
            dec     dx
            jnz     RowLoop

    ;Cycle the specified range of DAC locations until a key is pressed.
    CycleLoop:
    ;Rotate colors 1-255 one position in the PaletteTemp array;
    ; location 0 is always left unchanged so that the background
    ; and border don't change.
            push    word ptr PaletteTemp+(1*3)  ;set aside PaletteTemp
            push    word ptr PaletteTemp+(1*3)+2; setting for attr 1
            mov     cx,254
            mov     si,offset PaletteTemp+(2*3)
            mov     di,offset PaletteTemp+(1*3)
            mov     ax,ds
            mov     es,ax
            mov     cx,254*3/2
            rep     movsw                       ;rotate PaletteTemp settings
                                                ; for attrs 2 through 255 to
                                                ; attrs 1 through 254
            pop     bx                          ;get back original settings
            pop     ax                          ; for attribute 1 and move
            stosw                               ; them to the PaletteTemp
            mov     es:[di],bl                  ; location for attribute 255

    if WAIT_VSYNC
    ;Wait for the leading edge of the vertical sync pulse; this ensures
    ; that we reload the DAC starting during the vertical non-display
    ; period.
            mov     dx,INPUT_STATUS_1
    WaitNotVSync2:                              ;wait to be out of vertical sync
            in      al,dx
            and     al,08h
            jnz     WaitNotVSync2
    WaitVSync2:                                 ;wait until vertical sync begins
            in      al,dx
            and     al,08h
            jz      WaitVSync2
    endif   ;WAIT_VSYNC

    if USE_BIOS
    ;Set the new, rotated palette.
            mov     ax,1012h                    ;AH = 10h: set DAC function,
                                                ; AL = 12h: set DAC block subfunction
            sub     bx,bx                       ;start with DAC location 0
            mov     cx,CYCLE_SIZE               ;# of DAC locations to set
            mov     dx,seg PaletteTemp
            mov     es,dx
            mov     dx,offset PaletteTemp       ;point ES:DX to array from which
                                                ; to load the DAC
            int     10h                         ;load the DAC
    else    ;!USE_BIOS
     if GUARD_AGAINST_INTS
            mov     cx,CYCLE_SIZE               ;# of DAC locations to load
            mov     si,offset PaletteTemp       ;load the DAC from this array
            sub     ah,ah                       ;start with DAC location 0
    DACLoadLoop:
            mov     dx,DAC_WRITE_INDEX
            mov     al,ah
            cli
            out     dx,al                       ;set the DAC location #
            mov     dx,DAC_DATA
            lodsb
            out     dx,al                       ;set the red component
            lodsb
            out     dx,al                       ;set the green component
            lodsb
            out     dx,al                       ;set the blue component
            sti
            inc     ah
            loop    DACLoadLoop
     else;!GUARD_AGAINST_INTS
            mov     dx,DAC_WRITE_INDEX
            sub     al,al
            out     dx,al                       ;set the initial DAC location to 0
            mov     si,offset PaletteTemp       ;load the DAC from this array
            mov     dx,DAC_DATA
      if NOT_8088
            mov     cx,CYCLE_SIZE*3
            rep     outsb                       ;load CYCLE_SIZE DAC locations at once
      else;!NOT_8088
            mov     cx,CYCLE_SIZE               ;# of DAC locations to load
    DACLoadLoop:
            lodsb
            out     dx,al                       ;set the red component
            lodsb
            out     dx,al                       ;set the green component
            lodsb
            out     dx,al                       ;set the blue component
            loop    DACLoadLoop
      endif;NOT_8088
     endif;GUARD_AGAINST_INTS
    endif;USE_BIOS

    ;See if a key has been pressed.
            mov     ah,0bh                      ;DOS check standard input status fn
            int     21h
            and     al,al                       ;is a key pending?
            jz      CycleLoop                   ;no, cycle some more

    ;Clear the keypress.
            mov     ah,1                        ;DOS keyboard input fn
            int     21h

    ;Restore text mode and done.
            mov     ax,0003h                    ;AH = 0: set mode function,
            int     10h                         ; AL = 03h: mode # to set
            mov     ah,4ch                      ;DOS terminate process fn
            int     21h

            endstart

Previous | Table of Contents | Next

Graphics Programming Black Book © 2001 Michael Abrash