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