home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Collection of Hack-Phreak Scene Programs
/
cleanhpvac.zip
/
cleanhpvac
/
4GE_FRAC.ZIP
/
4GE-FRAC.ASM
next >
Wrap
Assembly Source File
|
1995-06-13
|
22KB
|
1,126 lines
; 4ge's Xmas 94 Intro fractalzoom source
; ======================================
; Copyright (C) 1994/5 Samuel Marshall. All rights reserved.
; Text and program code by Samuel Marshall, a.k.a. CuteElf / 4ge.
; Contact me at the following email address:
; Samuel.Marshall@durham.ac.uk
; and if you don't get a response, probably it's not during termtime, and
; I am at home - in which case, I check email here only about once a month
; (if that). You will get a reply eventually.
; My WWW homepage is
; http://www.dur.ac.uk/~d405ua/
; Enjoy the program... hope it helps you. Don't expect a wonderfully-
; optimised piece of code, because 1) the zoom routine wasn't really time-
; critical, and 2) this is the first time I ever wrote a fractal program,
; in my entire life, and 3) I didn't know how to do fixed-point numbers
; properly when I wrote this ;)
; General Principles for a real-time fractalzoom using this method
; ================================================================
;
; First, we calculate a fractal at twice the size, each way (i.e. 4 times
; the area) as the screen display area.
;
; We then display that fractal, zoomed-out to half size each way, so that
; it will exactly fill the screen display area.
;
; Then, we calculate another fractal twice the size of the display area,
; but this one is calculated "zoomed in" so that this fractal is a more
; detailed view of one-quarter of the area of the fractal just calculated.
;
; While we are calculating this, which takes a few seconds, we gradually
; zoom in the fractal we already have - using standard bitmap-zoom
; techniques - until eventually by the time the new fractal is finished,
; the old fractal will be showing at 1:1 size, and will can then be
; seamlessly replaced on the display by the new fractal at 1:2. Then
; repeat.
;
; Note: the fractal being DISPLAYED, i.e. the one that's already been
; calculated, can be zoomed to any point at all within the region, but
; this decision must be known in advance so that the next fractal is
; calculated from the right point. That's why you can't change the direction
; "realtime", only every so often.
; I would include references here but I worked the method out myself with
; no help from anything or anyone, so there aren't any... (oh, by the
; way, I assume this is the standard method everyone else uses too, it's
; nothing special or anything, just that I reinvented the wheel one more
; time ;)
; More details are included in the individual function descriptions.
.MODEL tiny
.386
.CODE
.STARTUP
jmp start
Getch:
mov ax,0
int 16h
ret
; You can probably change height without messing things up, but
; changing the width will I think need some work.
FRACWIDTH equ 256
FRACHEIGHT equ 128
; Fractal parameter
frac dd 3
; Memory
enlargebufferseg dw 0 ; segment address of the 64*32 enlarge buf.
textureseg dw 0 ; the segment address of texture to display.
fractalseg dw 0 ; the segment address of texture to create
;====================================================================
;ZOOMTEXTURE - display picture at given zoom fraction
;--------------------------------------------------------------------
; This is pretty obvious; just a standard, fixed-point bitmap
; display routine. The only extra bit you might notice is that
; it doubles each pixel, so as to get a larger screen display without
; taking too long to calculate the fractals.
; Data_______________________________________________________________
lfrac dw 0
hfrac dw 0 ; texture pixels per real pixel
hfracb db 0 ; same but a byte
ystart db 0
xstart db 0 ; where to start, in texturemap
oldbx dw 0
oldss dw 0
yfracpos dw 0 ; fractional y-position
stopat dw 0 ; where to stop in screen ram
; Source_____________________________________________________________
ZoomTexture:
push ax
push bx
push cx
push dx
push si
push di
mov di,screenstart ; start position on display memory
mov stopat,di
add stopat,FRACHEIGHT*320 ; stop position on display memory
mov bh,ystart
mov bl,xstart ; start position on texture memory
mov ax,hfrac
mov hfracb,al
mov dx,lfrac
mov ax,ss
mov oldss,ax
mov ax,textureseg
mov ss,ax
ZoomYLoop:
mov si,FRACWIDTH/2
mov oldbx,bx
mov cx,0
ZoomXLoop:
mov al,ss:[bx]
mov ah,al
mov es:[di],ax
mov es:[di+320],ax
add di,2
add cx,lfrac
adc bx,hfrac
dec si
jnz ZoomXLoop
mov bx,oldbx
add yfracpos,dx
adc bh,hfracb
add di,640-FRACWIDTH
cmp di,stopat
jb ZoomYLoop
mov ax,oldss
mov ss,ax
; Check for keypress
mov ah,1
int 16h
jz zt_afterkeyhit
jmp dfs_keyhit
zt_afterkeyhit:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
;====================================================================
;CALCULATE - work out one pixel of Mandelbrot fractal
;--------------------------------------------------------------------
; The fractal formula used, with coordinates x,y, is:
; A = B = 0. Colour=starting colour.
;
; new A = a squared - b squared - x
; new B = 2 * a * b - y
;
; If A squared + B squared > some number frac, then stop.
; If been round loop more than colour limit (64) times, then stop too.
;
; Otherwise, work out next A and B, and increment the colour we're going
; to use for the pixel.
;
; When we get out of the loop, that colour value is the one to draw at
; this pixel.
; Note:
; The fixed point sections of this are pretty crap; there are proper ways
; to do fixed point (I think...) so for god's sake don't copy those
; bits for your own fixed point code.
; Data_______________________________________________________________
colour db 0 ; colour to plot the pixel
esign db 0 ; esign of multiplication result
la dd 0
ha dd 0
lb dd 0
hb dd 0 ; a,b from a+bi. Low and high words thereof.
lasq dd 0
hasq dd 0
lbsq dd 0
hbsq dd 0 ; a squared, b squared, low and high words.
lnewa dd 0
hnewa dd 0 ; temporary storage for `new' veresion of `a'.
lx dd 0
hx dd 0
ly dd 0
hy dd 0 ; co-oredinates in complex plane of this point
; Source_____________________________________________________________
Calculate:
push eax
push ebx
push ecx
push edx
push esi
push edi
; 0) Fix up the x,y data to specific point
; mov lx,2621
; mov hx,1
; mov ly,11107
; mov hy,1
; 1) setup colour
mov colour,32
; 2) clear the a,b and squared variables
mov la,0
mov ha,0
mov lb,0
mov hb,0
mov lasq,0
mov hasq,0
mov lbsq,0
mov hbsq,0
CalcLoop:
; 3) increment colour
inc colour
; 4) set up a-squared
asquared:
mov eax,lasq
mov edx,hasq
minusbsquared:
; 5) subtract b-squared
sub eax,lbsq
sbb edx,hbsq
minusx:
; 6) subtract x and store result in new-a
sub eax,lx
sbb edx,hx
mov lnewa,eax
mov hnewa,edx
; 7) multiply a and b
atimesb:
; a. setup variables
mov ebx,la
mov ecx,ha
mov esi,lb
mov edi,hb
fixesigns:
; b. sort out esigns to be poesitive
mov esign,0
cmp ecx,0
jge ecxok_1
xor ecx,0ffffffffh
xor ebx,0ffffffffh
add ebx,1
adc ecx,0
inc esign
ecxok_1:
cmp edi,0
jge ediok_1
xor edi,0ffffffffh
xor esi,0ffffffffh
add esi,1
adc edi,0
inc esign
ediok_1:
multiply:
; c. multiply the two numbers
mov hb,0
mov eax,ebx
mov edx,esi
mul edx
mov lb,edx
mov eax,ebx
mov edx,edi
mul edx
add lb,eax
adc hb,edx
mov eax,ecx
mov edx,esi
mul edx
add lb,eax
adc hb,edx
mov eax,ecx
mov edx,edi
mul edx
add hb,eax
fixresultesign:
; d. fix the esign of the result
test esign,1
jz esignok_1
xor hb,0ffffffffh
xor lb,0ffffffffh
add lx,1
adc hb,0
esignok_1:
doublenumber:
; 8) Add this to itself, (with carry)
mov eax,lb
mov edx,hb
add eax,eax
adc edx,edx
subtracty:
; 9) Subtract y and store in b
sub eax,ly
sbb edx,hy
mov lb,eax
mov hb,edx
; 10) Update a from new-a : combined with
; 11) Square a and store in a-squared
; a. setup variable
starttosquare:
mov ebx,lnewa
mov ecx,hnewa
mov la,ebx
mov ha,ecx
fixesign:
; b. sort out esign to be poesitive
cmp ecx,0
jge ecxok_2
xor ecx,0ffffffffh
xor ebx,0ffffffffh
add ebx,1
adc ecx,0
ecxok_2:
squareit:
; c. square data
mov hasq,0
mov eax,ebx
mul eax
mov lasq,edx
mov eax,ebx
mul ecx
add lasq,eax
adc hasq,edx
add lasq,eax
adc hasq,edx
mov eax,ecx
mul eax
add hasq,eax
sametosquareb:
; 12) Square b and store in b-squared
; a. setup variable
mov ebx,lb
mov ecx,hb
; b. sort out esign to be poesitive
cmp ecx,0
jge ecxok_3
xor ecx,0ffffffffh
xor ebx,0ffffffffh
add ebx,1
adc ecx,0
ecxok_3:
; c. square data
mov hbsq,0
mov eax,ebx
mul eax
mov lbsq,edx
mov eax,ebx
mul ecx
add lbsq,eax
adc hbsq,edx
add lbsq,eax
adc hbsq,edx
mov eax,ecx
mul eax
add hbsq,eax
asquaredaddbsquared:
; 13) Setup a-squared
mov eax,lasq
mov edx,hasq
; 14) Add b-squared
add eax,lbsq
adc edx,hbsq
; 15) Compare with *n*, stop if >
cmp edx,frac
jg CalcFinish
; 16) If colour > *c*, stop
cmp colour,63
jg CalcFinish
jmp CalcLoop
CalcFinish:
; 17) Return pixel colour
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
mov al,colour
ret
;====================================================================
;FRACTAL - loop round to draw a 256x256 fractal using Calculate
;--------------------------------------------------------------------
; Basically, this just uses fixed-point to go through all the
; x and y coordinates corresponding to SCREEN x and y.
; If you're confused about the "normaltime", "othertime", etc crap
; in the zooming-in section, well, I *think* this is because the
; Y-position sometimes needs to start at a half-pixel (i.e. "othertime")
; but usually ("normaltime") starts on a whole pixel.
; Note, it would be possible to speed this up by 1/4, simply by re-using
; the relevant pixels from the fractal calculated last: see this diagram
; We're zooming in to top left corner.
; Original fractal Fractal needs calculating next
;
; abcd.... a?b?c?d?
; efgh.... ????????
; ijkl.... e?f?g?h?
; mnop.... ????????
; ........ i?j?k?l?
; ........ ????????
; ........ m?n?o?p?
; ........ ????????
;
; where "." has been calculated on original fractal, but will not be used
; for the new one, and ? represents what actually needs to be calculated
; in the new one. (a,b,c,... could be copied from the old one).
;
; Actually, this routine doesn't copy over a,b,c,..., they are recalculated.
; Data_______________________________________________________________
lxcentre dd 0
hxcentre dd 0
lycentre dd 0
hycentre dd 0 ; co-ordinates of the window's centre, not used here
; (they are used in the main loop...)
lxs dd 0
hxs dd 0
lys dd 0
hys dd 0 ; co-oredinates of the window's top left corner
lxi dd 0
hxi dd 0
lyi dd 0
hyi dd 0 ; amount to increment fractal parameter per pixel
ycount dw 0
xcount dw 0 ; loop counters
nodraw db 1 ; whether or not to draw the last one
ydirection db 1
xdirection db 1 ; direction of the zoom (0 = left/up, 1=centre, 2=rt/down)
newxdirection db 1
newydirection db 1
screenstart dw 0 ; screen edisplay offset
; Source_____________________________________________________________
Fractal:
push eax
push ebx
push ecx
push edx
push esi
push edi
; 1) Initialise x and y parameters of fractal to xstart,ystart
mov eax,lxs
mov edx,hxs
mov lx,eax
mov hx,edx
mov eax,lys
mov edx,hys
mov ly,eax
mov hy,edx
; 1.5) Initialise zoom parameters
mov lfrac,1024*63
mov hfrac,1
mov ystart,0
mov xstart,0
; 2) Set up screen pointer
push es
mov ax,fractalseg
mov es,ax
mov edi,0
mov ycount,FRACHEIGHT
FracYLoop:
mov xcount,FRACWIDTH
FracXLoop:
; 3) Calculate pixel
call Calculate
; 4) Draw pixel
mov es:[edi],al
; 5) Add X increment to X
mov eax,lxi
mov edx,hxi
add lx,eax
adc hx,edx
; 6) Increment screen poesition
inc edi
; 7) If count >127, stop
dec xcount
jnz FracXLoop
; 8) Set X to xstart
mov eax,lxs
mov edx,hxs
mov lx,eax
mov hx,edx
; 10) Add Y increment to Y
mov eax,lxi
mov edx,hxi
add ly,eax
adc hy,edx
; 10.5) Draw zoomed last fractal if count%2==0
mov ax,ycount
and al,1
jnz notthistime
push es
mov ax,0a000h
mov es,ax
; Update zoom position
mov yfracpos,0
mov al,xdirection
add xstart,al
cmp ydirection,0
je normaltime
cmp ydirection,2
jne ydirection1
inc ystart
jmp normaltime
ydirection1:
test ycount,2
jz othertime
inc ystart
jmp normaltime
othertime:
mov yfracpos,8000h
normaltime:
cmp nodraw,0
jne dontdraw
call ZoomTexture
dontdraw:
pop es
sub lfrac,1024
notthistime:
; 11) If count>127, stop
dec ycount
jnz FracYLoop
pop es
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
ret
;====================================================================
; SWITCHTEXTURES - switch around the two buffers
;--------------------------------------------------------------------
; This just changes the two buffers when one has been finished; so
; that the new fractal becomes the one that gets drawn to screen, and
; the old fractal will get written over by the one newly being
; calculated.
; This is a separate function, because if you decide to implement
; the 25% saving described above, you'll need to copy over those
; re-cycled pixels at some point, and this is a good time to do that.
; Source_____________________________________________________________
SwitchTextures:
push ax
push bx
mov ax,fractalseg
mov bx,textureseg
mov fractalseg,bx
mov textureseg,ax
pop bx
pop ax
ret
;====================================================================
;DOBACKGROUND - the background for lo-res part of demo
;--------------------------------------------------------------------
; Just draws the swirly background things, very simple.
DoBackground:
push ax
push bx
push cx
push di
push es
mov ax,0a000h
mov es,ax
mov bx,0 ; bh=y, bl=x
mov di,0
mov cx,64000
mov al,0
rep stosb
mov di,0
db_loop:
mov al,bh
mul bl
and al,0fh
add al,16
mov es:[di],al
inc di
inc bx
cmp bl,0
jne notnextline
add di,320-256
notnextline:
cmp bx,256*193
jb db_loop
mov si,0
mov di,256
push ds
push es
pop ds
mov dx,192
copy_loop:
mov cx,32
rep movsw
add si,320-64
add di,320-64
dec dx
jnz copy_loop
pop ds
;mov di,256*193
;mov cx,8*320
;mov al,0
;rep stosb
pop es
pop di
pop cx
pop bx
pop ax
ret
;====================================================================
;DRAWSQUARE - fills a square, for showing which way things are going
;--------------------------------------------------------------------
; I won't bother explaining this, you can all manage to draw a square
; by now...
DrawSquare:
push ax
push dx
push si
push di
push es
mov si,ax
mov ax,0a000h
mov es,ax
; bh=starty, ax(now si)=startx
; dx=width and height
; cl=colour
push dx
mov di,bx
shr di,8
mov ax,320
mul di
add ax,si
mov di,ax
pop dx
mov al,cl
mov si,dx
ds_yloop:
mov cx,dx
rep stosb
add di,320
sub di,dx
dec si
jnz ds_yloop
pop es
pop di
pop si
pop dx
pop ax
ret
;====================================================================
;DRAWDIRECTIONSQUARES - draw the motion direction indicators
;--------------------------------------------------------------------
; this is pretty trivial too.
DrawDirectionSquares:
push ax
push bx
push cx
push dx
push si
push di
; Clear all squares
mov bh,0 ; was 16
mov ax,0 ; was 16
mov cl,0
mov dx,32
mov si,3
dds_yloop:
mov di,3
dds_xloop:
cmp di,2
jne dds_drawit
cmp si,2
je dds_skipit
dds_drawit:
call DrawSquare
dds_skipit:
add ax,9*16
dec di
jnz dds_xloop
mov ax,0
add bh,5*16
dec si
jnz dds_yloop
; Draw chosen square
cmp newydirection,1
jne drawchosen
cmp newxdirection,1
je afterchosen
drawchosen:
mov ah,0
mov al,newydirection
imul ax,5*16
mov bh,al
mov eax,0
mov al,newxdirection
mov si,9*16
mul si
mov dx,32 ; square side length
mov cl,1
call DrawSquare
add bh,4
add ax,4
sub dx,8
mov cl,0
call DrawSquare ; clear the inside
afterchosen:
; Draw actual (current) square
cmp ydirection,1
jne drawcurrent
cmp xdirection,1
je aftercurrent
drawcurrent:
mov ah,0
mov al,ydirection
imul ax,5*16
add ax,0
mov bh,al
mov ah,0
mov al,xdirection
mov si,9*16
mul si
add bh,4
add ax,4 ; current square start now in bx.
mov dx,24 ; square side length
mov cl,2
call DrawSquare
aftercurrent:
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
;====================================================================
;DOFRACTALSECTION - the controllable fractals part of the demo
;--------------------------------------------------------------------
; hopefully what with the background you have already read, this
; routine is self-explanatory.
DoFractalSection:
; Now setup fractal parameters
; Start at preplanned position
mov hxcentre,000000000h
mov lxcentre,04afadfffh
mov hycentre,0fffffffeh
mov lycentre,08f71bfffh
; and increment by 1/64 per pixel
mov hxi,0
mov lxi,1024*65536
mov hyi,0
mov lyi,1024*65536
mov screenstart,33*320+32
dfs_fracloop:
; Calculate new hxcentre etc depending on xdirection,ydirection
; (new position = FW/4*(xdirection+1),FH/4*(ydirection+1) on the display)
; xcentre=xcentre+fw/4*(xdirection-1)*xi
cmp xdirection,1
je xchangedone
mov edx,FRACWIDTH/2
mov eax,lxi
mul edx
cmp xdirection,2
jne xchangeminus
add lxcentre,eax
adc hxcentre,edx
jmp xchangedone
xchangeminus:
sub lxcentre,eax
sbb hxcentre,edx
xchangedone:
; Same for Y:
cmp ydirection,1
je ychangedone
mov edx,FRACHEIGHT/2
mov eax,lyi
mul edx
cmp ydirection,2
jne ychangeminus
add lycentre,eax
adc hycentre,edx
jmp ychangedone
ychangeminus:
sub lycentre,eax
sbb hycentre,edx
ychangedone:
; Calculate start hxs,lxs hys,lys to keep hxcentre in middle (at 128,128)
; xs=xcentre-128*xi ys=ycentre-128*yi
mov eax,lxi
mov edx,FRACWIDTH/2
mul edx
mov ebx,eax ; bx is l(xi*128)
mov ecx,edx ; cx is h(xi*128)
xor ebx,0ffffffffh
xor ecx,0ffffffffh
inc ecx ; cx:bx now negative'd
add ebx,lxcentre
adc ecx,hxcentre
mov lxs,ebx
mov hxs,ecx
; Same for Y:
mov eax,lyi
mov edx,FRACHEIGHT/2
mul edx
mov ebx,eax ; bx is l(xi*128)
mov ecx,edx ; cx is h(xi*128)
xor ebx,0ffffffffh
xor ecx,0ffffffffh
inc ecx ; cx:bx now negative'd
add ebx,lycentre
adc ecx,hycentre
mov lys,ebx
mov hys,ecx
; Calculate next fractal while we zoom the last one
call Fractal
; Switch the texture buffers, including copying 1/4 of the pixels
call SwitchTextures
cmp nodraw,1
jne alreadydrawing
mov nodraw,0
call DoBackground
mov cx,0
alreadydrawing:
; Double magnification
mov eax,lxi
mov edx,hxi
shr eax,1
shr edx,1
mov lxi,eax
mov hxi,edx
mov lyi,eax
mov hyi,edx
; Update direction
mov al,newxdirection
mov xdirection,al
mov al,newydirection
mov ydirection,al
call DrawDirectionSquares
jmp dfs_fracloop
dfs_keyhit:
call Getch
cmp al,'7'
jne not7
mov newxdirection,0
mov newydirection,0
not7:
cmp al,'4'
jne not4
mov newxdirection,0
mov newydirection,1
not4:
cmp al,'1'
jne not1
mov newxdirection,0
mov newydirection,2
not1:
cmp al,'8'
jne not8
mov newxdirection,1
mov newydirection,0
not8:
cmp al,'5'
jne not5
mov newxdirection,1
mov newydirection,1
not5:
cmp al,'2'
jne not2
mov newxdirection,1
mov newydirection,2
not2:
cmp al,'9'
jne not9
mov newxdirection,2
mov newydirection,0
not9:
cmp al,'6'
jne not6
mov newxdirection,2
mov newydirection,1
not6:
cmp al,'3'
jne not3
mov newxdirection,2
mov newydirection,2
not3:
cmp al,27
je breakout
call DrawDirectionSquares
donethecentrechange:
jmp zt_afterkeyhit
;====================================================================
;MAIN SECTION & MISC
;--------------------------------------------------------------------
Init:
push ax
push bx
; 1) Setup ES segment
mov ax,0a000h
mov es,ax
; 2) Allocate RAM
; This is a COM program, so we just set the segments to spare
; space in memory. (well, hopefully spare space :)
push cs
pop ax
add ax,4096 ; textureseg is 64k above our segment
mov textureseg,ax
add ax,4096 ; and fractalseg is 128k above
mov fractalseg,ax
sub ax,4096
sub ax,2048 ; enlargebuffer is 1/2 way through our segment.
mov enlargebufferseg,ax
; 3) Do graphics mode
mov ax,0013h
int 10h
pop bx
pop ax
ret
Shutdown:
push ax
mov ax,0003h
int 10h
pop ax
ret
message db 'Thanks for watching the modified version of 4ge',39,'S XMaS 94 iNTRo.'
db 13,10,13,10
db 'Get 4ge-xmas.zip from ftp.cdrom.com for the full version, Tseng gfx only.',13,10,13,10,'$'
Start:
call Init
call DoFractalSection
breakout:
call Shutdown
mov ah,09h
mov dx,offset message
int 21h
int 20h
END