home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Unsorted BBS Collection
/
thegreatunsorted.tar
/
thegreatunsorted
/
programming
/
misc_programming
/
tplas.asm
< prev
next >
Wrap
Assembly Source File
|
1994-03-20
|
29KB
|
995 lines
;************************************************************************
;* *
;* TPLAS.ASM - Generate a Plasma fractal of 320x200 dimensions *
;* and display on screen. *
;* *
;* This is a working prototype of what will become TomsPlas. At *
;* that point, the fractal will be twice the size of the screen *
;* (640x480), allowing greater movement in the window, and the *
;* window will be expanded to much more of the screen (perhaps *
;* all?). Controls will be added to vary the speed (1-9 the *
;* number of frames to skip to speed it up, +/- a delay factor.) *
;* The code itself will be optimized to eliminate repetitive *
;* code, such as a push right before a pop, from the macros. *
;* Also, the movement and palette generation routines should *
;* be moved into the function itself rather than in the data *
;* file. This would shrink the data file to exactly 65,536 *
;* bytes, all of which would be numbers for the random series. *
;* *
;* All code and original algorithms expressed herein, *
;* Copyright (C) 1994 by Tom Dibble *
;************************************************************************
.MODEL TINY ; always must be TINY model for .COM ...
LOCALS
;<<Macros>>
Screen_MaxX equ 320
Screen_MaxY equ 240
; PrintNum(n)
; Print a decimal number to the screen using DBuf and INT 21h:09h
; Prints number up to 1000
PrintNum MACRO n
LOCAL @@PosNum
push ax
push bx
push cx
push dx
push di
xor bx,bx
mov di,offset DBuf
mov ax,n
cmp ax,0
jg @@PosNum
neg ax
mov [di+0], BYTE PTR '-'
inc bx
@@PosNum:
xor dx,dx
mov cx,1000
div cx
add ax,48 ; convert whole # to ASCII
mov [di+bx],al
inc bx
mov ax,dx ; put remainder in ax
xor dx,dx
mov cx,100
div cx
add ax,48 ; convert whole # to ASCII
mov [di+bx],al
inc bx
mov ax,dx ; put remainder in ax
xor dx,dx
mov cx,10
div cx
add ax,48 ; convert whole # to ASCII
mov [di+bx],al
inc bx
mov ax,dx ; put remainder in ax
add ax,48 ; convert whole # to ASCII
mov [di+bx],al
inc bx
mov ax,dx ; put remainder in ax
inc bx
mov [di+bx], BYTE PTR 10 ; CR
inc bx
mov [di+bx], BYTE PTR 13 ; LF
inc bx
mov [di+bx], BYTE PTR '$' ; terminate string
mov ah,09h
mov dx,offset DBuf
int 21h
pop di
pop dx
pop bx
pop cx
pop ax
endm
; GetPoint(x,y)
; Get the point in DS:[plasma] and place it in AL
; x and y can not be any of the following: AX, BX, CX
GetPoint MACRO x,y
push bx ; So we have some regs to play with
push cx ;
push ds
mov bx, y
mov cx, bx ; multiply by (320) [256+64 == <<8 + <<6]
xchg bh, bl ;
shl cx, 6 ;
add bx, cx ;
add bx, x
mov ds, [Plasma] ; DS --> (0,0) in Plasma mem block
mov al, [bx] ; Get the damned byte
pop ds
pop cx
pop bx
endm
; SetPoint(x,y)
; Set the point in DS:[plasma] to value in AL
SetPoint MACRO x,y
push bx ; So we have some regs to play with
push cx ;
push ds
mov bx, y
mov cx, bx ; multiply by (320) [256+64 == <<8 + <<6]
xchg bh, bl ; (mov low into high, same as shl bx, 8)
shl cx, 6 ;
add bx, cx ;
add bx, x
mov ds, [Plasma] ; DS --> (0,0) in Plasma mem block
mov [bx], al ; Set the damned byte
pop ds ; DS --> Data
push ds
mov ds, [Video] ; DS --> Video mem block
mov [bx], al ; Put this bitch on screen!
pop ds
pop cx
pop bx
endm
; SubDivide x1, y1, x2, y2
; Subdivide a Plasma rectangle defined by given coordinates and recurse.
SubDivide MACRO x1,y1,x2,y2
mov ax,y2
push ax
mov ax,x2
push ax
mov ax,y1
push ax
mov ax,x1
push ax
call SubDiv
endm
; Adjust x1, y1, x2, y2, xm, ym
; Adjust point (x,ym) to average of (x,y1) and (x,y2) plus a random val.
;
Adjust MACRO x1, y1, x2, y2, xm, ym
mov ax, ym
push ax
mov ax, xm
push ax
mov ax, y2
push ax
mov ax, x2
push ax
mov ax, y1
push ax
mov ax, x1
push ax
call Adj
endm
; Rand8
; Get the next 8-bit random value from Rand_Series. Put it into AL.
Rand8 MACRO
push ds
mov bx, Rand_Ptr
mov ds, [Rand_Series]
mov al, [bx]
pop ds
inc Rand_Ptr
endm
; Rand16
; Get the next 16-bit random value from Rand_Series. Put it into AX.
Rand16 MACRO
push ds
mov bx, Rand_Ptr
mov ds, [Rand_Series]
mov ax, [bx]
pop ds
inc Rand_Ptr
inc Rand_Ptr
endm
; GetTRand
; Get System-Time 'seconds' and '1/100 seconds' into DH and DL, respectively
; 'hour' and 'minute' into CH and CL, respectively
GetTRand MACRO
push ax
mov ah, 2Ch ; DOS Interrupt Fn: 'Get System Time'
int 21h
pop ax
endm
; malloc: allocate memory to program in 16-byte chunks
; 'amount' is the number of 'paragraphs' to allocate
; (a paragraph being 16 bytes).
; CF set on error, AX is 09h, BX is size of largest block
; The segment address is left in AX if successful
malloc MACRO amount
mov ah,48h
mov bx,amount
int 21h
endm
; mfree: frees malloc'd memory.
mfree MACRO where
mov ah,49h
mov es,where
int 21h
endm
; keyp: check for key press
; CF set if key is present.
keyp MACRO
mov ah,0bh
int 21h
or al,al
endm
; kwait: wait for key-press
kwait MACRO
push ax
mov ah, 08h
int 21h
pop ax
endm
; exit: exit with error code
exit MACRO return_code
mov al,return_code
mov ah,4ch
int 21h
endm
; prints: print a string to text screen
prints MACRO str
mov ah,09h
mov dx,offset str
int 21h
endm
; fopen: open a file for read (0) or write (1) or both (2)
; CF set on error, AX holds error code.
; file handle left in AX if successful.
fopen macro file,attrib
mov ah,3dh
mov al,attrib
mov dx,offset file
int 21h
endm
; fread: read information from file opened by fopen.
; Data read into (AX:00h).
; CF set on error.
; AX return (number of bytes read) is nuked.
fread macro handle,bytes
push ax
push ds
push ax
mov ah,3fh
mov bx,[handle]
mov cx,bytes
xor dx,dx ; Segment is (00h)
pop ds ; this used to be AX!
int 21h
pop ds
pop ax
endm
;**************************** << CODE SEGMENT >> ****************************;
.CODE
org 100h
start: jmp begin
; Variables
; Strings
CopyRight db "Tom's Plasma (C) 1994 by Tom Dibble",10,13,"$"
TPVersion db "Revision 1.1, March 19, 1994",10,13,"$"
NoMemory db "Out of Base Memory!",10,13,"$"
NoFile db 10,13,\
"ERROR: Couldn't open TomsPlas.DAT!",10,13,\
" Change to the directory containing",10,13,\
" TomsPlas.DAT and restart the program!",10,13,"$"
DebugPoint db "Debug Point!",10,13,"$"
DataFile db "TOMSPLASM.DAT",0
DBuf db 80 dup (?)
; General Variables
OrigVMode db 03h ; Mode the video card was in when we got here!
recadj db 11111111B ; last 8 bits set (255)
count dw 0 ; loop counter while swimmin' and cyclin'
DATHandle dw ? ; Handle to Data File
Rand_Ptr dw ? ; Pointer to next random value in [Rand_Series]
WinHide db 0 ; Is the swim-window hidden?
; Segment Addresses
Plasma dw 0 ; Segment beginning for Plasma Bitmap
Video dw 0 ; Segment beginning for Video Memory
Color dw 0 ; Segment for Color-Palette Data
Movement dw 0 ; Movement data
Rand_Series dw 0 ; 64k of random values
ASSUME CS:@code,DS:@code,ES:@code
.386 ; select the processor
begin: ; from here on in is executable stuffs ...
mov sp,offset stacktop ; set new stack
mov bx,last_inst-start+100h ; (size of prog. + 100h header)
shr bx,4 ; shrink memory usage to program size
inc bx ; in pages (16 bytes)
mov ah,4ah
int 21h ; ES segment is resized. ES is set to @code
prints CopyRight ; display header message
prints TPVersion ; display header message, part 2
; InitProgram sets video mode, allocates memory, seeds 'Random'
CALL InitProgram
; GenPlasma does all the work
mov [recadj], 11111111B ; last eight bits default ('Rough')
CALL ClearPlasma
NewPlasma:
CALL ShowPlasma
CALL ClearPlasma
CALL GenPlasma
; Cycle the colors and swim the image
ContinueCycle:
cmp [WinHide],0
jne NoWindow
CALL CycleSwim
jmp KeyDecipher
NoWindow:
CALL ShowPlasma
CALL CycleNoSwim
KeyDecipher:
; Wait for a key to be pressed (if none has already)
mov ah, 08h
int 21h
; Process that key!
cmp al, 13 ; Return --> New Plasma!
je NewPlasma
cmp al, ' ' ; Space --> Hide Window!
je ToggleWindow
cmp al, '1' ; New Plasma, recadj of 255
je RoughPlasma
cmp al, '2' ; New Plasma, recadj of 127
je MedPlasma
cmp al, '3' ; New Plasma, recadj of 63
je SmoothPlasma
cmp al,27 ; <Esc> --> Get outta here!
je LeaveProg
jmp ContinueCycle
ToggleWindow:
not [WinHide]
jmp ContinueCycle ; any key puts the window back!
RoughPlasma:
mov recadj, 11111111B
jmp NewPlasma
MedPlasma:
mov recadj, 01111111B
jmp NewPlasma
SmoothPlasma:
mov recadj, 00111111B
jmp NewPlasma
LeaveProg:
; Clean out memory allocated, return to right video mode
CALL CleanUp
;Now exit to DOS
mov ah,4Ch
int 21h
ret
;************************************************************************
;* *
;* InitProgram *
;* *
;* Allocate memory to Plasma, clear it out *
;* Open Data File *
;* Allocate memory to Movement, read *
;* Allocate memory to Color, read *
;* Allocate memory to Rand_Series, read *
;* Get Rand_Ptr from system time function *
;* Set Video Mode to 13 *
;* *
;************************************************************************
InitProgram PROC NEAR
malloc 4000+1 ; allocate memory for plasma buffer + 1 para.
jc nomem ; break if not enough memory
mov [Plasma],ax ; store address
malloc 2500+1 ; allocate memory for movement buffer + 1
jc nomem ; as above...
mov [Movement],ax
malloc 192+1 ; allocate memory for color buffer + 1
jc nomem
mov [Color],ax
malloc 1000h ; allocate memory for random buffer + 1
jc nomem
mov [Rand_Series],ax
jmp allocok ; skip the following
allocok: fopen DataFile,0 ; open TomsPlas.DAT read only
jnc loadok ;
prints NoFile ; You schmuck! It's not here!
mfree [Plasma]
mfree [Movement]
mfree [Color]
mfree [Rand_Series]
exit 254 ; so quit
loadok: mov [DATHandle],ax ; store handle
mov ax,[Movement] ; read movement data
fread DATHandle,40000
mov ax,[Color] ; color data
fread DATHandle,3072
mov ax,[Rand_Series] ; and random series
fread DATHandle,0ffffh
GetTRand ; get 'time' values into CX and DX
add cx, dx ; accumulate values
mov Rand_Ptr, cx ; put Rand_Ptr as a random place in series
mov ax,0F00h ; get the video mode now
int 10h ;
mov [OrigVMode], al
mov ax,0013h ; set video mode 13
int 10h
mov [Video],0a000h ; Start segment for Video Mem
mov si, 0
mov cx, 3*255 ; Set Color Palette!
push ds ;
mov ds, [Color]
cld
mov dx, 03C8h ;
mov al, 1 ; start with color '1'
out dx, al ;
inc dx
rep outsb
pop ds
ret
nomem:
prints NoMemory
exit -1
InitProgram ENDP
ClearPlasma PROC NEAR
push eax ;
push cx ;
push es ; Clear out the memory devoted to
push di ; the Plasma buffer so that
; we can create a new Plasma!
mov ax, 0 ;
shl eax, 16 ;
mov ax,0 ;
mov cx, [Plasma] ;
mov es, cx ;
mov di, 0 ;
mov cx, 64000/4 ;
rep stosd ; put eax into es:di
;
pop di ;
pop es ;
pop cx ;
pop eax ;
ret
ClearPlasma ENDP
ShowPlasma PROC NEAR
push ax
push cx
push es
push ds
push si
push di
mov si,0
mov di,0
mov ax,[Video]
mov es,ax
mov ds,[Plasma]
mov cx, 320*200/4
rep movsd
pop di
pop si
pop ds
pop es
pop cx
pop ax
ret
ShowPlasma ENDP
CleanUp PROC NEAR
push ax
mfree [Plasma]
mfree [Movement]
mfree [Color]
mfree [Rand_Series]
mov ah,00h
mov al,[OrigVMode] ; set video mode back to what it was
int 10h
prints CopyRight ; Tell 'em this again!
pop ax
ret
CleanUp ENDP
GenPlasma PROC NEAR
Rand8
SetPoint 0,0
Rand8
SetPoint 319, 199
Rand8
SetPoint 0, 199
Rand8
SetPoint 319, 0
SubDivide 0,0,319,199
ret
GenPlasma ENDP
;************************************************************************
;* *
;* SubDiv x1, y1, x2, y2 *
;* *
;* Corners have already been set to proper values. *
;* Check each side if it has been set yet, and if not, *
;* set it to the average of associated corners plus *
;* a random value based on Code:recadj. *
;* Set middle to average of four sides. *
;* Recurse for each of four sectors in lt,rt,rb,lb order. *
;* Free up our memory and get the hell outta here! *
;* *
;* 18 bytes on stack per recursion, times 7 recursions. Stack *
;* size must be large enough to handle this load! *
;* *
;************************************************************************
SubDiv PROC
ARG x1:word, y1:word, x2:word, y2:word
LOCAL x:word, y:word=LocalStk
push bp
mov bp,sp
sub sp,LocalStk ; Set-up local stack
push cx
push dx
; First, check for recursion halts.
; < if(xhalt && yhalt) return >
mov ax,[x1]
add ax,[x2]
shr ax,1 ; divide by two for average of x1,x2
mov [x],ax ; now 'x' is on stack!
; Find the 'y' value now!
mov cx,[y1]
add cx,[y2]
shr cx,1 ; divide by two for average of y1,y2
mov [y],cx ; now 'y' is on stack!
cmp ax,[x1]
jg xchecks ; if x1 != x, keep on going
; x does not check.
; Check for 'y' value recursion halt.
cmp cx,[y1]
jg ychecks ; y checks. Jump out.
pop dx ; Cleanup: return DX, to it's place
pop cx ; and same with CX, SP and BP ...
mov sp,bp ;
pop bp ;
ret 8
xchecks:
ychecks:
; Okay! We have checked for recursion halt
; and have calculated x and y.
; Next, check each of the four sides for empty
; and call 'Adjust' if so. Meanwhile, keep
; a running total of all the four sides so we
; can do a quick average of them in the next
; step.
GetPoint [x],[y1]
cmp al,0
jne topgood
Adjust [x1],[y1],[x2],[y1],[x],[y1]
topgood:
mov ah,0
push ax
GetPoint [x2],[y]
cmp al,0
jne rightgood
Adjust [x2],[y1],[x2],[y2],[x2],[y]
rightgood:
mov ah,0
pop bx
add ax,bx
push ax
GetPoint [x],[y2]
cmp al,0
jne bottomgood
Adjust [x1],[y2],[x2],[y2],[x],[y2]
bottomgood:
mov ah,0
pop bx
add ax,bx
push ax
GetPoint [x1],[y]
cmp al,0
jne leftgood
Adjust [x1],[y1],[x1],[y2],[x1],[y]
leftgood:
mov ah,0
pop cx
add cx,ax
; No push of AX here!
; Okay. All four side pixels have been computed
; and plotted on the screen and in memory. AX
; holds the sum of all four pixels. Divide by four
; and set x,y to the resulting value.
shr cx,2
mov al,cl
SetPoint [x],[y]
; Finally. All the real work is done. Now, decrease
; the recurse-level adjustment and recurse down
; to the next level for each of the four quads
mov al, recadj
push ax
shr al, 1
mov recadj, al
SubDivide [x1], [y1], [x], [y]
SubDivide [x], [y1], [x2], [y]
SubDivide [x], [y], [x2], [y2]
SubDivide [x1], [y], [x], [y2]
pop ax
mov recadj, al ; return it to what it was before we got here.
pop dx ; Cleanup: return DX, to it's place
pop cx ; and same with CX, SP and BP ...
mov sp,bp ;
pop bp ;
ret 8
SubDiv ENDP
;************************************************************************
;* *
;* Adj x1, y1, x2, y2, x, y *
;* *
;* Sets the point at (x,y) to be the average of points (x1,y1) *
;* and (x2,y2), plus a random factor based on recval. *
;* *
;* Get points (1) and (2) into AH, AL. *
;* Average the values into AL. *
;* CBW AL, move into BX. *
;* Get a random 16-bit number into AX *
;* Multiply AX and recval. *
;* Shift DX to the right 2 bits (same as shifting 32-bit 18) *
;* Move DX into AX *
;* Add BX into AX *
;* if AX < 1, AX = 1. *
;* if AH != 0, AL = 255 *
;* Set point (x,y) to value in AL. *
;* *
;************************************************************************
Adj PROC NEAR
ARG x1:word, y1:word, x2:word, y2:word, xm:word, ym:word
push bp
mov bp, sp
push bx
push cx
push dx
mov ah,0
GetPoint [x1], [y1] ; Sets 'AL'
mov cl, al ; Put it into 'CL'
GetPoint [x2], [y2] ; Sets 'AL'
add cx, ax ; Add this guy to the first.
shr cx, 1 ; Divide by two (to make average)
Rand8 ; random byte in al
mov bl, recadj
and al, bl ; restrict the rand #
shr bl, 1 ; div by 2
sub al, bl ; subtract 1/2 (so low # neg, high pos)
cbw
add ax, cx ; cx is pos. ax may be + or -
; If the sum exceeded 255, ah will
; have a positive number in it.
; If the sum was less than zero, ax
; will be negative.
cmp ax,0
jg AdjLowCheck
mov ax,1 ; ax < 0, force it to lower bound!
jmp AdjSetPoint
AdjLowCheck:
cmp ax,256
jl AdjSetPoint
mov ax,255 ; ax > 255, force it to upper bound!
AdjSetPoint:
SetPoint [xm], [ym]; Set (x,y) to 'AL'
pop dx
pop cx
pop bx
pop bp
ret 12 ; 6*2 parameters
Adj ENDP
;************************************************************************
;* *
;* CycleSwim *
;* *
;* Cycle the color palette and swim a middle window of plasma. *
;* *
;* 'Swimming' is the process of adding two sections of the *
;* plasma fractal together, then moving the pointers *
;* to each of those sections in a loop so as to create *
;* a composite, changing fractal. *
;* *
;************************************************************************
CycleSwim PROC NEAR
mov ax, [Video] ; Put the video segment address in es
mov es, ax ; for stosd loop below.
mainloop:
; We'll do all this setup stuff *BEFORE* we wait for VRT.
; This sets things up for :setpl: loop below ...
; Rotate the palette!
push ds
push es
cld
mov es, [Color]
mov ds, [Color]
mov bx, 0
mov ax, [bx] ; r,g of color[0]
mov dl, [bx+2] ; b of color[0]
mov cx, 256*3 ; loop 256*4*3/4 times
mov si, 3
mov di, 0
rep movsd ; move bytes from ds:si to es:di
mov bx, 256*4*3-3 ; copy saved rgb to end of current pal.
mov [bx],ax
mov [bx+2],dl
pop es
pop ds
mov si,0
mov cx,255*3 ; loop 255 times (for 255 DAC's)
push ds
mov ds,[Color] ; address segment
cld ; ensure SI is incremented
VSync:
mov dx,03dah ; VGA input status register 1
in al,dx ; load value
test al,08 ; vertical retrace??
je VSync ; if not, try again...
; Load all the info from the colour segment
; into the DAC's ...
; NOW do the final prep for color loading
mov dx,3c8h ; DAC index register
mov al,1 ; start with color 1
out dx,al ; and load
inc dx ; DAC read/write register
rep outsb
pop ds ; and restore DS back to code segment
mov di,[count] ; source = count * 4
shl di,2 ;
push ds
mov ds,[Movement] ; get segment address of movement data
mov si,[di] ; load point 1, start of reading x-data
mov bx,[di+2] ; load point 2, start of added x-data
pop ds ; and restore DS back to code segment
push ds
mov ds,[Plasma] ; get segment of start of plasma
mov di,320*50+90 ; put window in the center of the screen.
mov ch,100 ; y loop = 100 pixels
pl1: mov cl,40 ; x loop = 40 * 4 = 160 pixels
pl2: lodsd ; get 4 source pixels from ds:si, inc si
add eax,[si+bx] ; add 4 source pixels from ds:(si+bx)
stosd ; and store them on screen to es:di, inc di
dec cl ; dec x-counter
jnz pl2 ; and loop..
sub si,160 ; reset source to beg. of :pl1:
mov dx,ds ; add 20 to DS :
add dx,20 ; move 20*16 = 320 bytes down in source mem
mov ds,dx
add di,160 ; add 160 to vid ptr (to beg of next line)
dec ch ; dec y-counter
jnz pl1 ; and loop..
pop ds ; and restore DS back to code segment
inc word ptr [count] ; increase counter
cmp word ptr [count],10000 ; reset it at end of cycle
jne noreset
mov word ptr [count],0
noreset:
keyp ; keypressed??
jnz closedown ; if yes then quit..
jmp mainloop
closedown:
ret
CycleSwim ENDP
;************************************************************************
;* *
;* CycleNoSwim *
;* *
;* Just cycle the color palette. *
;* *
;************************************************************************
CycleNoSwim PROC NEAR
mov ax, [Video] ; Put the video segment address in es
mov es, ax ; for stosd loop below.
CNSmainloop:
; We'll do all this setup stuff *BEFORE* we wait for VRT.
; This sets things up for :setpl: loop below ...
; Rotate the palette!
push ds
push es
cld
mov es, [Color]
mov ds, [Color]
mov bx, 0
mov ax, [bx] ; r,g of color[0]
mov dl, [bx+2] ; b of color[0]
mov cx, 256*3 ; loop 256*4*3/4 times
mov si, 3
mov di, 0
rep movsd ; move bytes from ds:si to es:di
mov bx, 256*4*3-3 ; copy saved rgb to end of current pal.
mov [bx],ax
mov [bx+2],dl
pop es
pop ds
push ds
mov si,0
mov cx,255*3 ; loop 255 times (for 255 DAC's)
mov ds,[Color] ; address segment
cld ; ensure SI is incremented
CNSVSync:
mov dx,03dah ; VGA input status register 1
in al,dx ; load value
test al,08 ; vertical retrace??
je CNSVSync ; if not, try again...
; Load all the info from the colour segment
; into the DAC's ...
; NOW do the final prep for color loading
mov dx,3c8h ; DAC index register
mov al,1 ; start with color 1
out dx,al ; and load
inc dx ; DAC read/write register
rep outsb
pop ds ; and restore DS back to code segment
keyp ; keypressed??
jnz leaveCNS ; if yes then quit..
jmp CNSmainloop
leaveCNS:
ret
CycleNoSwim ENDP
mystack db 8192 dup (?) ; my stack: room for lots o' recursion
stacktop equ $ ; top of my stack.
last_inst: ; the end of the program!
END start