Category : Printer + Display Graphics
Archive   : VGAFX.ZIP
Filename : FLASH1.ASM
This module demonstrates special effect attributes in attribute
control mode 1 and provides several user-callable functions for
attribute manipulation. The callable functions are:
flash_init Initializes system; call before using others
flash_term Restores system; call before terminating
set_flash_rate Sets number of timer ticks between palettes
set_delta Sets number added to current palette per change
load_palettes Loads all 16 palettes into VGA
reset_palettes Resets to 16 base palettes
pulse_color Creates a pulsing color, 16 palettes
pulse_color_partial Creates a pulsing color, < 16 palettes
set_color Defines a color for one attibute, all 16 palettes
set_color_partial Defines a color for one attibute, < 16 palettes
grade_color Creates a graded color over 16 palettes
grade_color_partial Creates a grade color over < 16 palettes
blinker Copies BG colors into FG, for simulated blinking
Additionally, one data item is public to the user. FLASH_ENABLED
is a byte variable; a zero value here disables palette changes,
effectively "freezing" the colors currently displayed.
The module is designed for linking into a COM program. All functions
are NEAR calls and assume DS=ES, except for TIMER_INT, which is an
interrupt intercept and assumes that CS=DS=ES.
The module prologues provide greater detail on function requirements
and register use.
Tested under MASM 5.1 and OPTASM 1.5.
Prep:
masm flash0;
-or-
optasm flash0;
link hostprog+flash0;
exe2bin hostprog
Uncopyrighted material, use freely
By Chris Dunford/Cove Software (CompuServe 76703,2002; tel. 301/992-9371)
Version history:
1.00 10/09/89
|
public flash_init,flash_term
public pulse_color,pulse_color_partial
public grade_color,grade_color_partial
public set_color,set_color_partial
public blinker
public load_palettes,reset_palettes
public set_flash_rate,set_delta
public flash_enabled
; This equate determines whether TIMER_INT uses BIOS or register-level
; programming to accomplish palette changes. Set to 0 for register-level,
; any non-zero value for BIOS level.
USE_BIOS equ 0
; Macro accesses the palette control video BIOS function (fn 10H)
; Call: palctrl subfunction
palctrl macro fn
mov ax,10h shl 8 + fn
int 10h
endm
; Structure for storing graded color scaling data. See CALC_SCALE_FACTORS.
scale_factors struc
incr db ?
xs_count db ?
xs_incr_val db ?
scale_factors ends
; Subfunctions (AL values) for palette control function
_SET_PALREGS equ 2
_GET_PALREGS equ 9
_SET_COLOR equ 10h
_SET_DACS equ 12h
_SET_ATR_SELECT_STATE equ 13h
_GET_DACS equ 17h
_GET_ATR_SELECT_STATE equ 1AH
code segment word public 'code'
assume cs:code,ds:code,es:code
; ========================================================================
; DATA FOR VGA MANIPULATION
; ========================================================================
; Storage for the original 16 palettes
origpals db 16*16*3 dup (0) ; 16 palettes, 16 colors each, 3 RGB values per
; Additional saved state info
orig_mode db ? ; Original attr control mode
orig_color_select db ? ; Original color select reg value
; Storage for the augmented palettes
newpals db 16*16*3 dup (0)
; Storage for the 16 palatte registers + overscan reg
palregs db 17 dup (0)
; The 16 new palette register contents we will use, plus overscan
newpalregs db 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0
; Storage for factors used to scale one color to another over
; fifteen palettes. Don't separate or re-order; module assumes
; that the R/G/B records are contiguous and in that order.
red_scale scale_factors <>
green_scale scale_factors <>
blue_scale scale_factors <>
; Color definitions for our 16 base colors. 16 colors, 3 RGB values each
new_base_pal label byte
.radix 16
db 00,00,00 ; Color 0 (normally black)
db 00,00,2A ; Color 1 (blue)
db 00,2A,00 ; Color 2 (green)
db 00,2A,2A ; Color 3 (cyan)
db 2A,00,00 ; Color 4 (red)
db 2A,00,2A ; Color 5 (magenta)
db 2A,2A,00 ; Color 6 (brown)
db 2A,2A,2A ; Color 7 (white)
db 00,00,15 ; Color 8 (some very dark color)
db 00,00,33 ; Color 9 (brt blue)
db 00,33,00 ; Color 10 (brt green)
db 00,33,33 ; Color 11 (brt cyan)
db 33,00,00 ; Color 12 (brt red)
db 33,00,33 ; Color 13 (brt magenta)
db 33,33,00 ; Color 14 (yellow)
db 33,33,33 ; Color 15 (brt white)
.radix 10
; Flasher data
flash_enabled db 1 ; 0=disabled, 1=enabled
flash_reset_count dw 1 ; Flash rate, in timer ticks
count dw 9 ; Remaining countdown
pal_select db 0 ; Current palette #
delta db 1 ; # palettes to change per flash
; Storage for original timer vector
oldtimer label dword
tickvec_lo dw ?
tickvec_hi dw ?
; ========================================================================
; CALLABLE FUNCTIONS
; ========================================================================
; ----- flash_init ------------------------------------------------------
; This function must be called to initialize the system for controlled
; flashing. It accomplishes several tasks:
;
; - Saves the 16 palette registers in palregs
; - Gets the current 256 colors (4 palettes) to origpals
; - Duplicates palette 0 in palette 1 and loads it into the VGA
; - Installs the timer intercept
;
; On exit, flashing is set up, but nothing is actually flashing (because
; palettes 0 and 1 are identical).
;
; Returns CF=1 if no VGA detected. All regs except segregs may be destroyed.
;
flash_init:
; Make sure we've got a VGA. Use a VGA-only function, and one that
; we can use to save the current attribute control mode and color select reg.
palctrl _GET_ATR_SELECT_STATE ; A VGA-only function
mov orig_mode,bl
mov orig_color_select,bh
cmp al,_GET_ATR_SELECT_STATE
; jne got_VGA
; stc ; Oops
; jmp fi_exit
; Save the 16 current palette registers into palregs; reset the
; palette registers to contain 16 "standard" 4-bit colors.
got_VGA:
; Get current regs
mov dx,offset palregs
palctrl _GET_palregs
; Continue to use the current border color
mov al,palregs+16
and al,0FH
mov newpalregs+16,al
; Set new palregs
mov dx,offset newpalregs
palctrl _SET_PALREGS
; Save the original DAC color registers (256 colors) in origpals
xor bx,bx ; Start with register 0
mov cx,256 ; 256 registers
mov dx,offset origpals ; Where to put 'em
palctrl _GET_DACS
; Create 16 standard palettes in newpals and send them to the VGA
call dupe_palette0
mov dx,offset newpals
call set_colors
; Set attribute control mode 1
mov bx,100h
palctrl _SET_ATR_SELECT_STATE
; Save/set the timer intercept
push es
mov ax,3508h
int 21h
mov tickvec_lo,bx
mov tickvec_hi,es
pop es
mov dx,offset timer_int
mov ax,2508h
int 21h
clc
fi_exit:
ret
; ----- flash_term -----------------------------------------------------
; This function must be called for cleanup when program terminates:
; - Deactivates the timer intercept
; - Restores the original VGA state
; AX,BX,CX,DX destroyed
;
flash_term:
; Clear the timer interrupt
push ds
lds dx,oldtimer
mov ax,2508h
int 21h
pop ds
; Restore original palette registers and video DAC color registers
mov dx,offset palregs
palctrl _SET_PALREGS
mov dx,offset origpals
call set_colors
; Restore original attribute control mode
xor bl,bl ; Subfn to set control mode
mov bh,orig_mode
palctrl _SET_ATR_SELECT_STATE
; Go back to palette 0
mov bl,1 ; Subfn to set color select reg
mov bh,orig_color_select ; Value to set
palctrl _SET_ATR_SELECT_STATE
ret
; ----- set_flash_rate ------------------------------------------------
; Reset the flash rate to the number of ticks in AX (18/sec); i.e.,
; the palette will change every AX ticks. All regs preserved.
;
set_flash_rate:
cli
mov flash_reset_count,ax
mov count,ax
sti
ret
; ----- set_delta ------------------------------------------------------
; Set the increment value for palette changes. When the ticker
; ticks down and the palette is to be changed, the timer ISR will
; add/subtract this number to the current palette number. With a
; higher delta, you can flash more rapidly. E.g., with delta=1,
; the palettes change 0,1,2,3,...,15. With delta=3, the palette
; changes are 1,3,6,9,12,15. If delta=0, only palette 0 is used.
;
; AX destroyed.
;
set_delta:
push cx
and al,15
mov cl,al
cli
mov delta,al
; Ensure that the selected palettes will
; be multiples of the delta
or cl,cl
jnz SD20
xor al,al
jmp SD50
SD20:
mov al,pal_select
xor ah,ah
div cl
mul cl
SD50:
mov pal_select,al
sti
pop cx
ret
; ----- load_palettes -------------------------------------------------
; Load the set of palettes in NEWPALS into the VGA.
;
load_palettes:
mov dx,offset newpals
call set_colors
ret
; ----- reset_palettes ----------------------------------------------
; This function resets the VGA to 16 copies of the "standard" palette.
;
reset_palettes:
call dupe_palette0
call load_palettes
ret
; ----- pulse_color_partial ----------------------------------------
; Creates a "pulsing" attribute. This is one whose intensity increases
; and decreases cyclically. On entry:
; AL = attribute
; AH = intensity increase/palette (each palette's RGB values
; will be this much higher than the previous palette's)
; CH = base palette
; CL = terminal palette
; The color definition in palette CH is unaffected; palettes CH+1..CL
; will contain augmented color definitions. Function does nothing
; if CL >= CH.
;
; AX destroyed. New palettes not loaded into VGA.
;
pulse_color_partial:
push bx
push cx
push si
push di
; Verify the palette numbers
cmp ch,15 ; CH > 15?
ja P90 ; Yes
cmp cl,ch ; CL >= CH?
jae P90 ; Yes
; Address the base definition (palette CL) for this attribute
call get_DAC_ptr
mov si,bx ; SI -> first definition
sub ch,cl ; CH = # of palettes affected
mov cl,ch
xor ch,ch ; Now CX
; Loop through the required number of palettes
p_palette_loop:
push cx
mov cx,3
mov di,si ; SI/DI -> color def, crnt palette
add di,16*3 ; DI -> color def, next palette
p_RGB_loop:
lodsb ; Get R/G/B intensity, crnt pal
or al,al ; Don't increment missing primaries
jz P10
add al,ah ; Add per-palette increment
cmp al,63 ; Don't let it go past 63
jbe P10
mov al,63
P10: stosb ; Store increment value in next pal
loop p_RGB_loop ; Loop for 3 primaries
pop cx
add si,16*3-3 ; Next palette
loop p_palette_loop
P90:
pop di
pop si
pop cx
pop bx
ret
; ----- pulse_color -----------------------------------------------------
; Identical to pulse_color_partial except that a full range 0-15 is used;
; reg CX input not required.
;
; Entry: see pulse_color_partial; CH/CL not required.
;
; AX destroyed. New palettes not loaded into VGA.
;
pulse_color:
push cx
mov cx,0F00H
call pulse_color_partial
pop cx
ret
; ----- set_color_partial --------------------------------------------
; This function sets the color definitions for attribute AL in palettes
; CL to CH to the 3-byte RGB definition at DS:SI. Ensure that CH >= CL
; and that both are in the range 0..15. The new palette is not sent to
; the VGA.
;
; The function does nothing if CL > CH or either is not in the range
; 0-15.
;
; AX destroyed.
;
set_color_partial:
push bx
push cx
push si
push di
; Verify the palette numbers
cmp ch,15 ; CH > 15?
ja S10 ; Yes
cmp cl,ch ; CL >= CH?
ja S10 ; Yes
; Address the base definition (palette CL) for this attribute
call get_DAC_ptr
mov di,bx ; DI -> first definition
inc ch
sub ch,cl ; CH = # of palettes affected
mov cl,ch
xor ch,ch ; Now CX
; Loop through the required number of palettes
sc_palette_loop:
push si ; Copy def from SI to palette n
lodsb
stosb
lodsw
stosw
pop si
add di,16*3-3 ; DI -> color def in pal n+1
loop sc_palette_loop
S10:
pop di
pop si
pop cx
pop bx
ret
; ----- set_color --------------------------------------------------
; Identical to set_color_partial, except that the full range of
; palettes (0..15) is assumed. I.e., this function defines all palettes
; for attribute AL to contain the RGB color definition at DS:SI.
; Reg CX input not required.
;
set_color:
push cx
mov cx,0F00h
call set_color_partial
pop cx
ret
; ----- grade_color_partial ------------------------------------------
; This function creates a graded set of colors for attribute AL.
; CL contains a starting palette (0-14) and CH contains an ending
; palette (1-15, CH > CL).
;
; DS:SI points to the "terminal" color definition, which will be
; the definition in palette CH. On exit, palettes CL-CH will contain
; "graded" color definitions for the attribute, so that the displayed
; color will change slowly from the base color (in palette CL) to the
; terminal color (in palette CH). The color definition at DS:SI
; is three bytes long (one byte each for R, G, B intensity). RGB
; values are modulated into the range 0-63. The new palette is not
; sent to the VGA. AX destroyed.
;
; The function does nothing if CL >= CH or either is not in the range
; 0-15.
;
grade_color_partial:
push bx
push cx
push si
push di
; Verify the palette numbers
cmp ch,15 ; CH > 15?
ja G10 ; Yes
cmp cl,ch ; CL >= CH?
jae G10 ; Yes
; Address the base definition (palette CL) for this color
call get_DAC_ptr
push bx
sub ch,cl ; CH = # of palettes graded
mov cl,ch
xor ch,ch ; Now CX
mov di,offset red_scale
call calc_scale_factors ; Calc red scaling factors
call calc_scale_factors ; " grn " "
call calc_scale_factors ; " blue " "
pop si ; SI -> initial definition
; Loop through the required number of palettes
gc_palette_loop:
mov di,si ; SI/DI -> color def in palette n
add di,16*3 ; DI -> color def in pal n+1
; Augment RGB values for this video DAC color register
mov bx,offset red_scale ; Point to red scale factors
call increment ; Scale red
call increment ; Scale green
call increment ; Scale blue
add si,16*3-3 ; Next palette
loop gc_palette_loop
G10:
pop di
pop si
pop cx
pop bx
ret
; ----- grade_color --------------------------------------------------
; This is the same as GRADE_COLOR_PARTIAL, except that a full 15-palette
; grade is automatic. Reg CX input is not required.
;
grade_color:
push cx
mov cx,0F00h ; Grade palettes 0-15
call grade_color_partial
pop cx
ret
; ----- blinker --------------------------------------------------
; This function creates a simulated "blinking" color for attribute
; AL. Unlike most of the other functions, this one works with a
; full 8-bit attribute (bits 0-3=FG, 4-7=BG, as usual). "Blinking"
; is accomplished by putting the BG color definition into palettes
; 8-15 for the selected FG color.
;
; Note that palettes 0-7 are not altered, so you can do whatever
; you want with the "visible" half of the blink text (like scaling it,
; as is done in the "softened blinking" demo.
;
; AX destroyed. New palette not sent to VGA.
;
blinker:
push bx
push cx
; Get a pointer to the color definition for the BG attribute
push ax
mov cl,4 ; Mov high nibble (BG) to low
shr al,cl
xor cl,cl ; Get ptr to def in palette 0
call get_DAC_ptr
mov si,bx ; SI->BG def, palette 0
pop ax
; Now do a SET_COLOR for the FG attribute in palettes 8-15,
; using the color definition at DS:SI (which is the BG color)
and al,0FH ; Mask the BG attribute number
mov cx,0F08h ; Palettes 8-15
call set_color_partial
pop cx
pop bx
ret
; =======================================================================
; INTERNAL SUBROUTINES
; =======================================================================
; ----- dupe_palette0 -----------------------------------------------
; This function creates 16 "standard" palettes in NEWPALS.
; The palettes are not loaded into the VGA.
; Regs used: AX,CX,SI,DI
;
dupe_palette0:
; Copy the base palette into palette 0 of newpals. Each color
; register contains 3 colors (R, G, and B), so the full palette
; is 16*3 bytes long
mov si,offset new_base_pal
mov di,offset newpals
mov cx,16*3/2 ; 256 colors, 3 RGB values each
cld
rep movsw
; Now duplicate pallete 0 (colors 0-15) to pals 1-15 (colors 16-255)
; We simplify this by allowing the copies to overlap.
mov si,offset newpals ; SI -> palette 0
mov di,offset newpals+16*3 ; DI -> palette 1
mov cx,15*16*3/2 ; 15 pals, 16 colors each, @ 3 bytes
rep movsw
ret
; ----- calc_scale_factors ---------------------------------------------
; This function generates the parameters for scaling a color from
; an initial value to a terminal value. On entry, DS:BX points
; to an initial color value (0-63), DS:SI points to a terminal
; color value (0-63), and ES:DI points to a 3-byte interpolation
; factor storage area. The function calculates the numbers needed
; to scale the color from the initial definition to the terminal
; definition over a span of CL palettes (normally 15).
;
; The 3-byte factor storage area is filled as follows:
; byte signed integer: increment/palette
; byte unsigned integer: number of extra increments required
; byte signed integer: excess increment value (1 or -1)
;
; To scale a palette, start with palette 0 and add the increment/palette
; to each succeeding palette. Also add the excess increment value (1 or -1)
; to the first n palettes (1-n), where n is the number of extra increments.
; For example, if the initial color value is 21 and the terminal is 63, the
; factor storage area would contain 2,12,1. To scale from 21 to 63, start
; with the value in palette 0 and add 3 per palette (2+1) from 1-12 and two
; per palette from 13-15:
; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
; 21 24 27 30 33 36 39 42 45 48 51 54 57 59 61 63
; (Everything in the above assumes a 15-palette scale).
;
; On exit, BX and SI have been incremented by one, and DI by 3. This
; conveniently points to the next color values and factor storage area.
; Other regs are preserved.
;
calc_scale_factors:
; Make sure CL is OK
and cl,0FH
or cl,cl
jnz CSF10
mov cl,15
CSF10:
; Get the initial color to AH and terminal color to AL
mov al,[bx] ; Initial color value
inc bx
mov ah,al
lodsb ; Terminal color value
and al,3FH ; Force 0-63
; Compute increment/palette and number of excess increments needed
sub al,ah ; AL = difference (term-init)
cbw
idiv cl ; AL = inc/pal, AL = excess
mov [di.incr],al ; Store increment/palette
; Decide whether the excess increment value is +1 or -1. It will be
; -1 if the "excess" calculated above is negative; the excess count will
; also have to be made positive, if so.
mov al,1 ; Assume positive
or ah,ah ; Is it negative?
jns I1 ; No, continue
neg al ; Yes, make increment negative
neg ah ; And count positive
I1: mov [di.xs_count],ah ; Store the values
mov [di.xs_incr_val],al
add di,type scale_factors ; Next storage area
ret
; ----- increment -----------------------------------------------------
; This subfunction increments a color value from palette n to palette
; n+1 using the scale factors at DS:BX (see CALC_SCALE_FACTORS).
; Entry: DS:BX->scale factors, DS:SI->palette n color value,
; ES:DI -> palette n+1 color value. On exit, SI has been incremented
; (to point to the next color value), and BX is increased by 3 (to point
; to the next scale factor storage area). The xs_incr field of the
; scale factor record is decremented if not already zero.
;
increment:
lodsb ; Get original R/G/B value
add al,[bx.incr] ; Add per-palette increment
test [bx.xs_count],-1 ; Any excess increments left?
jz no_rem ; No
dec [bx.xs_count] ; Yes, dec remaining excess count
add al,[bx.xs_incr_val] ; And add the excess incrmt (1/-1)
no_rem:
stosb ; Store the graded value
add bx,type scale_factors
ret
; ----- set_colors --------------------------------------------------
; This function sets the 256 video DAC color registers from the table
; at ES:DX, i.e., it loads the 256 colors definitions into the VGA.
;
set_colors:
push ax
push bx
push cx
xor bx,bx ; Start with register 0
mov cx,256 ; 256 colors
palctrl _SET_DACS
pop cx
pop bx
pop ax
ret
; ----- get_DAC_ptr ----------------------------------------------
; Returns a pointer in BX to the color definition for attribute AL
; in palette CL of NEWPALS. Other regs preserved.
;
get_DAC_ptr:
push ax
and ax,0FH ; Ensure range 0-15
mov bx,ax
mov al,newpalregs[bx] ; Get palreg for this attrib
mov bx,ax ; Triple it for offset into color tab
shl bx,1
add bx,ax ; BX = 3 * color #
mov al,16*3 ; Bytes/palette
mul cl ; AX -> offset of palette CL
add bx,ax ; BX -> offset of color def in NEWPALS
add bx,offset newpals ; BX -> base color definition
pop ax
ret
; =======================================================================
; TIMER INTERCEPT
; =======================================================================
Comment |
This is the timer intercept. On each timer tick, we decrement the
countdown (if we are enabled). If the count goes to zero, we go to
the next palette. The next palette is determined by the current
palette (in pal_select) and the delta value; delta is added to
the current value and range checked. If the new palette is out of
range, it's brought in range and the sign of delta is changed.
|
timer_int:
assume cs:code,ds:nothing,es:nothing
; Is the flasher enabled?
test flash_enabled,-1
jz timer9 ; No
; Dec count, skip rest if nonzero
dec count
jnz timer9
; Count has zeroed, switch palettes by adding the delta. If the
; palette number goes out of range, reverse the sign of the delta
; and bring the palette number back into range. PAL_SELECT has
; the current palette number.
push ax
push bx
mov bh,pal_select ; Get current palette
add bh,delta ; Add the delta
js P2 ; Go if new palette not negative
P1: cmp bh,15 ; Check for positive out-of-range
jbe pal_OK ; It's OK
P2: neg delta ; Reverse the direction
add bh,delta
add bh,delta
pal_OK:
mov pal_select,bh ; Save new palette
if USE_BIOS
; Use BIOS to set color select register (palette)
mov bl,1 ; And send it to the VGA
palctrl _SET_ATR_SELECT_STATE
else
; Use register-level programming of the attribute control reg (ACR)
push dx
; Get port address of CRT status register
xor ax,ax
push ds
mov ds,ax
mov dx,ds:[463h] ; DX = 3x8 register
pop ds
add dx,6 ; DX = 3xA, CRT status reg
; Wait for a retrace
push cx
mov ah,5
xor cx,cx
t_wait: in al,dx
test al,8
jnz t_go
loop t_wait
dec ah
jnz t_wait
t_go: pop cx
; Do rest with ints off
pushf
cli
; Set color select
in al,dx ; Set addr/data flipflop in ACR
push dx ; Save CRT status reg port #
mov dx,3C0H ; Select ACR reg 14h (color select)
mov al,14h
out dx,al
jmp $+2
mov al,bh ; Send color select data
out dx,al
pop dx ; Recover CRT status reg
in al,dx ; Reset flipflop
mov dx,3C0h ; ACR again
mov al,20h ; Restore palette
out dx,al
popf ; Ints back on
pop dx
endif
mov ax,flash_reset_count ; Reset the count
mov count,ax
pop bx
pop ax
; Done, go do the real timer routine
timer9:
jmp oldtimer
code ends
end
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/