Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories

Fire Graphics Programs

blipblip Member Posts: 756
I've done two versions of a fire graphics program. One uses double buffering while the other doesn't, resulting in a 10x-20x speed increase on a GeForce3 video card. Here's the NASM source code for the non-double-buffering one if anyone's interested (tury? ;-)):
[code]
;Compile into a .COM file with NASM.
[bits 16]
org 100h
[section .text]

ScreenWidth equ 320
ScreenHeight equ 200

Start:
mov ah,9 ;Ask which palette the user wants to use.
mov dx,WhichPalette
int 21h
GetKey:
xor ah,ah ;Get user input.
int 16h
cmp al,"1" ;If they pressed a key other than 1-4, try to get another.
jb GetKey
cmp al,"4"
ja GetKey
sub al,"1" ;Make the key into a zero-based number from ASCII-based "1".
movzx esi,al ;Save the soon-to-be index in ESI (also zero-extend it).

mov ah,9
mov dx,InvertColors
int 21h
call YorN
jc SetInvertFlag
xor al,al ;AL is a flag that remembers whether the user wanted inverted colors.
jmp SaveInvertFlag
SetInvertFlag:
mov al,1
SaveInvertFlag:
mov gs,ax ;Save it in a register that no one's gonna use.

mov dx,FlipPalette
mov ah,9
int 21h
call YorN
mov ax,gs ;Retrieve the flags (not the actual FLAGS register!).
jnc SaveFlipFlag ;Remember that GS and AX bit1 is zeroed already.
or al,1<<1
SaveFlipFlag:
mov gs,ax

SetMode13h:
mov ax,13h ;Switch to video mode 13h, 320x200x8bpp (also clears the screen and
int 10h ;sets up the default palette).
mov dx,3C8h ;Palette write index, getting ready to change the palette.
xor al,al ;Begin writing with index #0.
xor ch,ch ;This way, the procedures know that CH=0 and can better modify CX.
out dx,al ;Send the index.
xor bl,bl ;This will be used for variable parts of the palette entries.
inc dx ;Get to the palette data register, port 3C9h.

call [CallTable+esi*2] ;Set up the palette that the user chose.

mov dx,3C7h ;Palette read index.
xor al,al ;Set the index to zero.
mov cx,256 ;There are 256 entries to invert.
mov bx,gs
out dx,al ;Send the palette read index.
inc dx ;Get to the palette write index.
out dx,al ;Set it to zero.
inc dx ;Now get to the palette data register.
test bl,1<<0
jz TryFlip

PaletteInvertLoop:
in al,dx
mov bl,al
not bl
in al,dx
mov bp,ax
not bp
in al,dx
mov si,ax
not si
dec cx
mov al,bl
out dx,al
mov ax,bp
out dx,al
mov ax,si
out dx,al
jnz PaletteInvertLoop

TryFlip: ;All CPU and VGA registers are already set up!
test bl,1<<1
jz SkipFlip
PushPaletteLoop:
in al,dx
mov ah,al
in al,dx
shl eax,8
dec cx
in al,dx
push eax
jnz PushPaletteLoop
mov ch,1 ;Since CL=0, CX=100h=256d.
PopPaletteLoop:
pop eax
bswap eax
mov al,ah
out dx,al
shr eax,16
dec cx
out dx,al
mov al,ah
out dx,al
jnz PopPaletteLoop

SkipFlip:
mov bp,0A000h ;0A000h is the segment of the video graphics buffer.
cld
xor al,al ;Start with color #0.
xor di,di ;Start at the top-left pixel of the screen.
mov es,bp ;Set ES and DS to 0A000h, for implicit and explicit memory accesses.
mov ds,bp

DoLine: ;All this does is to show the colors in the palette in a line.
stosb ;Equivalent to: MOV ES:[DI],AL and INC DI when DF=0 (which it is now).
inc al ;Increment the color value.
jnz DoLine ;Has a wraparound occurred? Note: INC and DEC don't modify CF.

RandLineSetup:
mov di,ScreenHeight * ScreenWidth - 1 ;Last pixel on the screen.
mov cx,ScreenWidth * 2 ;Write two whole lines of data.
std ;Make string instructions work backwards.

RandomizeBottomTwoLines:
in al,40h
cmp al,64 ;Don't let the random values get too low!
jbe RandomizeBottomTwoLines
dec cx
stosb ;If AL > 64, then write it to the screen.
jnz RandomizeBottomTwoLines ;Decrement CX, and keep going if it's not zero.

dec di ;Now get to the second to last pixel on the second to last line.

;The blur algorithm used by fire routines works like this:
; X
;A B C
;Where A, B, C, and X are pixels at the positions represented by the diagram.
;The actual blur is done using: X = (A + B + C + X) / 4.

Blur:
movzx ax,byte [di] ;X
movzx bx,byte [di+ScreenWidth-1] ;A
movzx cx,byte [di+ScreenWidth] ;B
movzx dx,byte [di+ScreenWidth+1] ;C
add ax,cx ;Add together A and C.
add bx,dx ;Add X and B together.
add ax,bx ;Get the final sum.
shr ax,2 ;Fast divide by four.
stosb ;Now that DF=1, this is equivalent to: MOV ES:[DI],AL and DEC DI.
cmp di,1 ;The last X should be at the very second pixel (offset 1).
jnz Blur

mov ah,1 ;See if a key is in the keyboard buffer.
int 16h
jz RandLineSetup ;If not, keep blurring the screen image.
mov ax,3 ;If a key has been pressed, return to text mode and exit.
int 10h
int 20h

;-----------------------------------------------------------------------------
;Procedures and data:
FlipPalette db 10,10,13,"Would you like to flip the palette's arrangement?$"

InvertColors db 10,10,13,"Would you like to invert the palette's colors?$"

WhichPalette db "Palette to use with a fire effect:",10,13
db "1) Fire",10,13
db "2) Rainbow",10,13
db "3) Black and White",10,13
db "4) BIOS default$"

CallTable dw FirePalette, RainbowPalette, BlackAndWhitePalette, DefaultPalette

FirePalette:
mov cl,64 ;Write 64 new palette entries (this is done in each loop).
Black2Red: ;Only red of the entries will be variable, blue and green are zero.
mov al,bl
out dx,al
xor al,al
inc bx
dec cx
out dx,al
out dx,al
jnz Black2Red
xor bl,bl
mov cl,63
Red2Yellow: ;Here red is 63 (highest value), green is variable, and blue is 0..
mov al,63
out dx,al
mov al,bl
out dx,al
xor al,al
inc bx
dec cx
out dx,al
jnz Red2Yellow
xor bl,bl
mov cl,64
Yellow2White: ;In this last loop, red and green are 63 and blue is variable.
mov al,63
out dx,al
out dx,al
mov al,bl
inc bx
dec cx
out dx,al
jnz Yellow2White
mov bx,63 ;In this final loop, BX will be both the counter and variable
White2Blue: ;palette component (for both red and green.).
mov al,bl
out dx,al
out dx,al
dec bx
mov al,63
out dx,al
jnz White2Blue
ret

RainbowPalette: ;32 different shades of eight different transition colors.
;No regular comments can describe how this works, so here's some RGB diagrams:
; Black2Brown Brown2Red Red2Orange
;+--------+--------+ +--------+--------+ +--------+--------+
;|00 00 00|31 31 00| |31 31 00|63 00 00| |63 00 00|63 31 00|
;
; Orange2Yellow Yellow2Green Green2Blue
;+--------+--------+ +--------+--------+ +--------+--------+
;|63 31 00|63 63 00| |63 63 00|00 63 00| |00 63 00|00 00 63|
;
; Blue2Violet Violet2White
;+--------+--------+ +--------+--------+
;|00 00 63|63 00 63| |63 00 63|63 63 63|
xor bp,bp ;BL already equals zero, so no need to clear it.
mov cl,32 ;Do 32 transitions/shades.
Black2Brown:
mov al,bl
out dx,al
mov ax,bp
inc bx
out dx,al
inc bp
xor al,al
dec cx
out dx,al
jnz Black2Brown
mov cl,32
Brown2Red:
mov al,bl
out dx,al
mov ax,bp
inc bx
out dx,al
dec bp
xor al,al
dec cx
out dx,al
jnz Brown2Red
mov cl,64
Red2Yellow2: ;I'm taking a shortcut here....
mov al,63
out dx,al
mov ax,bp
out dx,al
inc bp
xor al,al
dec cx
out dx,al
jnz Red2Yellow2
mov cl,32
Yellow2Green:
mov al,bl
out dx,al
mov al,63
add bl,2
out dx,al
xor al,al
dec cx
out dx,al
jnz Yellow2Green
mov cl,32
Green2Blue:
xor al,al
out dx,al
mov ax,bp
out dx,al
sub bp,2
mov al,bl
add bl,2
dec cx
out dx,al
jnz Green2Blue
mov cl,32
Blue2Violet:
mov ax,bp
out dx,al
xor al,al
add bp,2
out dx,al
mov al,63
dec cx
out dx,al
jnz Blue2Violet
xor bl,bl
mov cl,32
Violet2White:
mov al,63
out dx,al
mov al,bl
out dx,al
add bl,2
mov al,63
dec cx
out dx,al
jnz Violet2White
ret

BlackAndWhitePalette: ;There are 64 different shades of black and white.
xor al,al ;This won't change unless it is really needed, start off with black.
mov bp,64 ;Send 64 colors.
ReloadCL:
mov cl,4 ;Each shade must be copied four times to fill up the palette.
SendFourEntries:
dec cx
out dx,al
out dx,al
out dx,al
jnz SendFourEntries
inc ax
dec bp
jnz ReloadCL
DefaultPalette: ;Nothing has to be done to keep the default palette! ;-)
ret ;Make both use this RET just to save a bit of code space.

YorN:
xor ah,ah
int 16h
and al,0DFh
cmp al,"Y"
jz SetCF
cmp al,"N"
jnz YorN
clc
ret
SetCF:
stc
ret
[/code]

Comments

  • blipblip Member Posts: 756
    [b][red]This message was edited by Moderator at 2002-10-4 13:44:21[/red][/b][hr]
    ...and here's the double-buffering version, just 30 bytes larger in compiled form:
    [code]
    ;Compile into a .COM file with NASM.
    [bits 16]
    org 100h
    [section .text]

    ScreenWidth equ 320
    ScreenHeight equ 200

    Start:
    mov ah,9 ;Ask which palette the user wants to use.
    mov dx,WhichPalette
    int 21h
    GetKey:
    xor ah,ah ;Get user input.
    int 16h
    cmp al,"1" ;If they pressed a key other than 1-4, try to get another.
    jb GetKey
    cmp al,"4"
    ja GetKey
    sub al,"1" ;Make the key into a zero-based number from ASCII-based "1".
    movzx esi,al ;Save the soon-to-be index in ESI (also zero-extend it).

    mov ah,9
    mov dx,InvertColors
    int 21h
    call YorN
    jc SetInvertFlag
    xor al,al ;AL is a flag that remembers whether the user wanted inverted colors.
    jmp SaveInvertFlag
    SetInvertFlag:
    mov al,1
    SaveInvertFlag:
    mov gs,ax ;Save it in a register that no one's gonna use.

    mov dx,FlipPalette
    mov ah,9
    int 21h
    call YorN
    mov ax,gs ;Retrieve the flags (not the actual FLAGS register!).
    jnc SaveFlipFlag ;Remember that GS and AX bit1 is zeroed already.
    or al,1<<1
    SaveFlipFlag:
    mov gs,ax

    SetMode13h:
    mov gs,di ;Save it in a register that no one's gonna use.
    mov ax,13h ;Switch to video mode 13h, 320x200x8bpp (also clears the screen and
    int 10h ;sets up the default palette).
    mov dx,3C8h ;Palette write index, getting ready to change the palette.
    xor al,al ;Begin writing with index #0.
    xor ch,ch ;This way, the procedures know that CH=0 and can better modify CX.
    out dx,al ;Send the index.
    xor bl,bl ;This will be used for variable parts of the palette entries.
    inc dx ;Get to the palette data register, port 3C9h.

    call [CallTable+esi*2] ;Set up the palette that the user chose.

    mov dx,3C7h ;Palette read index.
    xor al,al ;Set the index to zero.
    mov cx,256 ;There are 256 entries to invert.
    mov bx,gs
    out dx,al ;Send the palette read index.
    inc dx ;Get to the palette write index.
    out dx,al ;Set it to zero.
    inc dx ;Now get to the palette data register.
    test bl,1<<0
    jz TryFlip

    PaletteInvertLoop:
    in al,dx
    mov bl,al
    not bl
    in al,dx
    mov bp,ax
    not bp
    in al,dx
    mov si,ax
    not si
    dec cx
    mov al,bl
    out dx,al
    mov ax,bp
    out dx,al
    mov ax,si
    out dx,al
    jnz PaletteInvertLoop

    TryFlip: ;All CPU and VGA registers are already set up!
    test bl,1<<1
    jz SkipFlip
    PushPaletteLoop:
    in al,dx
    mov ah,al
    in al,dx
    shl eax,8
    dec cx
    in al,dx
    push eax
    jnz PushPaletteLoop
    mov ch,1 ;Since CL=0, CX=100h=256d.
    PopPaletteLoop:
    pop eax
    bswap eax
    mov al,ah
    out dx,al
    shr eax,16
    dec cx
    out dx,al
    mov al,ah
    out dx,al
    jnz PopPaletteLoop

    SkipFlip: ;As long as the program doesn't get > 1279 bytes, we're fine.
    cld ;Make string instructions work forwards.
    xor al,al ;Start with color #0.
    mov di,Buffer ;Start at the top-left pixel of the buffered screen.

    DoLine: ;All this does is to show the colors in the palette in a line.
    stosb ;Equivalent to: MOV ES:[DI],AL and INC DI when DF=0 (which it is now).
    inc al ;Increment the color value.
    jnz DoLine ;Has a wraparound occurred? Note: INC and DEC don't modify CF.

    RandLineSetup:
    push cs
    mov di,Buffer + (ScreenHeight * ScreenWidth - 1) ;Last pixel on the screen.
    push cs
    mov cx,ScreenWidth * 2 ;Write two whole lines of data.
    pop ds
    std ;Make string instructions work backwards.
    pop es

    RandomizeBottomTwoLines:
    in al,40h
    cmp al,64 ;Don't let the random values get too low!
    jbe RandomizeBottomTwoLines
    dec cx
    stosb ;If AL > 64, then write it to the screen.
    jnz RandomizeBottomTwoLines ;Decrement CX, and keep going if it's not zero.

    dec di ;Now get to the second to last pixel on the second to last line.

    ;The blur algorithm used by fire routines works like this:
    ; X
    ;A B C
    ;Where A, B, C, and X are pixels at the positions represented by the diagram.
    ;The actual blur is done using: X = (A + B + C + X) / 4.

    Blur:
    movzx ax,byte [di] ;X
    movzx bx,byte [di+ScreenWidth-1] ;A
    movzx cx,byte [di+ScreenWidth] ;B
    movzx dx,byte [di+ScreenWidth+1] ;C
    add ax,cx ;Add together A and C.
    add bx,dx ;Add X and B together.
    add ax,bx ;Get the final sum.
    shr ax,2 ;Fast divide by four.
    stosb ;Now that DF=1, this is equivalent to: MOV ES:[DI],AL and DEC DI.
    cmp di,Buffer + 1 ;The last X should be at the very second pixel.
    jnz Blur

    mov dx,3DAh ;VGA status register, get ready to sync with vertical retrace.
    VRT1:
    in al,dx
    test al,8
    jz VRT1
    VRT2:
    in al,dx
    test al,8
    jnz VRT2

    cld
    mov ax,0A000h ;Segment of VGA video graphics buffer.
    mov cx,64000 / 4 ;Copy the entire screen using DWORDs.
    mov si,Buffer ;Read the double buffer from the beginning/
    xor di,di ;Start writing at offset zero in the video graphics buffer.
    mov es,ax
    rep movsd ;Copy as fast as you can!

    mov ah,1 ;See if a key is in the keyboard buffer.
    int 16h
    jz RandLineSetup ;If not, keep blurring the screen image.
    mov ax,3 ;If a key has been pressed, return to text mode and exit.
    int 10h
    int 20h

    ;-----------------------------------------------------------------------------
    ;Procedures and data:
    FlipPalette db 10,10,13,"Would you like to flip the palette's arrangement?$"

    InvertColors db 10,10,13,"Would you like to invert the palette's colors?$"

    WhichPalette db "Palette to use with a fire effect:",10,13
    db "1) Fire",10,13
    db "2) Rainbow",10,13
    db "3) Black and White",10,13
    db "4) BIOS default$"

    CallTable dw FirePalette, RainbowPalette, BlackAndWhitePalette, DefaultPalette

    FirePalette:
    mov cl,64 ;Write 64 new palette entries (this is done in each loop).
    Black2Red: ;Only red of the entries will be variable, blue and green are zero.
    mov al,bl
    out dx,al
    xor al,al
    inc bx
    dec cx
    out dx,al
    out dx,al
    jnz Black2Red
    xor bl,bl
    mov cl,63
    Red2Yellow: ;Here red is 63 (highest value), green is variable, and blue is 0..
    mov al,63
    out dx,al
    mov al,bl
    out dx,al
    xor al,al
    inc bx
    dec cx
    out dx,al
    jnz Red2Yellow
    xor bl,bl
    mov cl,64
    Yellow2White: ;In this last loop, red and green are 63 and blue is variable.
    mov al,63
    out dx,al
    out dx,al
    mov al,bl
    inc bx
    dec cx
    out dx,al
    jnz Yellow2White
    mov bx,63 ;In this final loop, BX will be both the counter and variable
    White2Blue: ;palette component (for both red and green.).
    mov al,bl
    out dx,al
    out dx,al
    dec bx
    mov al,63
    out dx,al
    jnz White2Blue
    ret

    RainbowPalette: ;32 different shades of eight different transition colors.
    ;No regular comments can describe how this works, so here's some RGB diagrams:
    ; Black2Brown Brown2Red Red2Orange
    ;+--------+--------+ +--------+--------+ +--------+--------+
    ;|00 00 00|31 31 00| |31 31 00|63 00 00| |63 00 00|63 31 00|
    ;
    ; Orange2Yellow Yellow2Green Green2Blue
    ;+--------+--------+ +--------+--------+ +--------+--------+
    ;|63 31 00|63 63 00| |63 63 00|00 63 00| |00 63 00|00 00 63|
    ;
    ; Blue2Violet Violet2White
    ;+--------+--------+ +--------+--------+
    ;|00 00 63|63 00 63| |63 00 63|63 63 63|
    xor bp,bp ;BL already equals zero, so no need to clear it.
    mov cl,32 ;Do 32 transitions/shades.
    Black2Brown:
    mov al,bl
    out dx,al
    mov ax,bp
    inc bx
    out dx,al
    inc bp
    xor al,al
    dec cx
    out dx,al
    jnz Black2Brown
    mov cl,32
    Brown2Red:
    mov al,bl
    out dx,al
    mov ax,bp
    inc bx
    out dx,al
    dec bp
    xor al,al
    dec cx
    out dx,al
    jnz Brown2Red
    mov cl,64
    Red2Yellow2: ;I'm taking a shortcut here....
    mov al,63
    out dx,al
    mov ax,bp
    out dx,al
    inc bp
    xor al,al
    dec cx
    out dx,al
    jnz Red2Yellow2
    mov cl,32
    Yellow2Green:
    mov al,bl
    out dx,al
    mov al,63
    add bl,2
    out dx,al
    xor al,al
    dec cx
    out dx,al
    jnz Yellow2Green
    mov cl,32
    Green2Blue:
    xor al,al
    out dx,al
    mov ax,bp
    out dx,al
    sub bp,2
    mov al,bl
    add bl,2
    dec cx
    out dx,al
    jnz Green2Blue
    mov cl,32
    Blue2Violet:
    mov ax,bp
    out dx,al
    xor al,al
    add bp,2
    out dx,al
    mov al,63
    dec cx
    out dx,al
    jnz Blue2Violet
    xor bl,bl
    mov cl,32
    Violet2White:
    mov al,63
    out dx,al
    mov al,bl
    out dx,al
    add bl,2
    mov al,63
    dec cx
    out dx,al
    jnz Violet2White
    ret

    BlackAndWhitePalette: ;There are 64 different shades of black and white.
    xor al,al ;This won't change unless it is really needed, start off with black.
    mov bp,64 ;Send 64 colors.
    ReloadCL:
    mov cl,4 ;Each shade must be copied four times to fill up the palette.
    SendFourEntries:
    dec cx
    out dx,al
    out dx,al
    out dx,al
    jnz SendFourEntries
    inc ax
    dec bp
    jnz ReloadCL
    DefaultPalette: ;Nothing has to be done to keep the default palette! ;-)
    ret ;Make both use this RET just to save a bit of code space.

    YorN:
    xor ah,ah
    int 16h
    and al,0DFh
    cmp al,"Y"
    jz SetCF
    cmp al,"N"
    jnz YorN
    clc
    ret
    SetCF:
    stc
    ret

    Buffer:
    [/code]
  • CroWCroW Member Posts: 348
    the video-ram is shared ram between the cpu and the DAC of the vga-chip.only one of both could access it at one time.and the dac of the graphicscard must access it much more often than the cpu does to display a flicker-free picture under dos with 50hz screen-mode.so it oftens happens,that the cpu has written something and the memory-controller must wait until dac has finished all operations.and read-operations from vram are even more time-expensive.

    writing to true memory hasnt such bottlenecks and executes lots faster.and the refresh to vram could be done in only one big write-operation,which only one time have to wait for the dac.
  • blipblip Member Posts: 756
    I'm not so sure you've done too many graphics programs. Really what happens is if there's a change to the video data while the card is displaying it, tearing and flickering will occur because the current trace has 2+ different screens being displayed one after another. The video displays a pixel by: the card reads an item from the display memory. It then (in 256 color modes) uses that as an index into the palette and retrieves the binary RGB triplet that is then sent to the DAC and on to the electron gun which "paints" the picture onto the screen. According to my explaination, it wouldn't matter if the VRAM contained the palette and display screen, they wouldn't be used in parallel (except maybe on newer cards).

    Btw, I updated the double-buffering program (not posted yet, ask me for it if you want it).
  • Justin BibJustin Bib USAMember Posts: 0

    ___ // http://forcoder.org // free ebooks and video tutorials about \ Objective-C Go Python Scratch Assembly C# JavaScript PHP MATLAB C Visual Basic .NET R Ruby PL/SQL Java Swift C++ Visual Basic Perl Delphi Bash D Crystal Ada Scala COBOL Kotlin Dart Alice FoxPro Apex ML Hack SAS Lua ABAP VBScript Fortran Clojure Transact-SQL Lisp LabVIEW Erlang Julia Logo Rust Awk F# Scheme Prolog \ ______________

Sign In or Register to comment.