;**********************************************************;
;* FRAXZ.ASM -- Extremely fast fractal program for the *;
;* Mandelbrot and Julia Sets, and only 1311 bytes (1.3K)! *;
;* (This is the same as FRAX except it allows zooming) *;
;* Uses the diagonal interpolation algorithm. *;
;* Requires 386, VGA, color monitor. *;
;**********************************************************;
Ideal
TIMES = 5 ;Interpolation level, can be 1-6
;5 or 6 is fastest (7 won't work)
MAXZOOM = 3
LEFT = 4Bh
RIGHT = 4Dh
UP = 48h
DOWN = 50h
HOME = 47h
SPACE = 39h
ESCAPE = 01h
PGUP = 49h
PGDN = 51h
RETURN = 1Ch
Model Tiny
CodeSeg
P386
Org 100h
Proc Program
mov ax,cs ;Point FS to offscreen buffer
add ax,1000h
mov fs,ax
push 0A000h ;Point ES to video memory
pop es
mov ax,13h ;Change video mode
int 10h ;to 320x200
jmp GoCenter ;Center screen and redraw
KeyLoop: xor ah,ah ;Wait for a key
int 16h
cmp ah,LEFT ;Left?
je GoLeft
cmp ah,RIGHT ;Right?
je GoRight
cmp ah,UP ;Up?
je GoUp
cmp ah,DOWN ;Down?
je GoDown
cmp ah,SPACE ;Space?
je Toggle
cmp ah,ESCAPE ;Escape?
je Quit
cmp ah,PGUP ;PgUp?
je ZoomIn
cmp ah,PGDN ;PgDn?
je ZoomOut
cmp ah,HOME ;Home?
jne KeyLoop
GoCenter: xor eax,eax ;Re-center screen
mov [dword RowOffset],eax
jmp ReDraw ;Redraw fractal
GoLeft: sub [ColOffset],160 ;Move left 1/2 screen
jmp ReDraw ;Redraw fractal
GoRight: add [ColOffset],160 ;Move right 1/2 screen
jmp ReDraw ;Redraw fractal
GoUp: sub [RowOffset],100 ;Move up 1/2 screen
jmp ReDraw ;Redraw fractal
GoDown: add [RowOffset],100 ;Move down 1/2 screen
jmp ReDraw ;Redraw fractal
ZoomIn: cmp [Zoom],MAXZOOM ;Already in?
je KeyLoop ;If so, ignore
inc [Zoom] ;Zoom in
cmp [Zoom],1
je ZIskip
sal [PLimit],1
sal [NLimit],1
ZIskip: sub [RowOffset],50
sub [ColOffset],80
sal [RowOffset],1
sal [ColOffset],1
jmp ReDraw ;Redraw fractal
ZoomOut: cmp [Zoom],0 ;Already out?
je KeyLoop ;If so, ignore
dec [Zoom] ;Zoom out
cmp [Zoom],0
je ZOskip
sar [PLimit],1
sar [NLimit],1
ZOskip: sar [RowOffset],1
sar [ColOffset],1
add [RowOffset],50
add [ColOffset],80
jmp ReDraw ;Redraw fractal
Toggle: cmp [Mode],0 ;Julia mode?
jne ToMandel
cmp [Zoom],1 ;Only works with zoom 1
jne KeyLoop
call GetPos ;Switch to julia mode
test al,al ;If AL = 1, cancel
jne KeyLoop
mov [Mode],1
mov eax,[dword RowOffset]
mov [dword SaveRow],eax
jmp GoCenter ;Center picture
ToMandel: mov [Mode],0 ;Switch to mandel mode
mov [Zoom],1
mov [PLimit],512
mov [NLimit],-512
mov eax,[dword SaveRow]
mov [dword RowOffset],eax
jmp ReDraw ;Redraw
Quit: cmp [Mode],1 ;If in julia mode,
je ToMandel ;change back to mandel mode.
mov ax,3 ;Switch to text mode
int 10h
ret
ReDraw: mov [Dist],(1 shl (TIMES - 1)) ;Initialize vars
mov [Incr],(1 shl TIMES)
mov [IncrM],((1 shl TIMES) - 1)
mov [LDist],((1 shl (TIMES - 1)) * 320)
mov [LDist2],((1 shl (TIMES - 1)) * 640)
mov cl,[Zoom]
xor si,si ;Zero SI
xor di,di ;Zero DI
cmp [Mode],1 ;If julia, call DrawJ,
je IsJulia ;otherwise call DrawM.
call DrawM ;Then jump to keyloop.
jmp KeyLoop
IsJulia: call DrawJ
jmp KeyLoop
EndP Program
;**************************** GetPos -- Get julia coordinates
Proc GetPos
mov [dword CurX],6400A0h;Start at middle of screen
gKeyLoop: call ShowCursor ;Show cursor
xor ah,ah ;Wait for a key
int 16h
call HideCursor ;Hide cursor
cmp ah,LEFT ;Left?
je gLeft
cmp ah,RIGHT ;Right?
je gRight
cmp ah,UP ;Up?
je gUp
cmp ah,DOWN ;Down?
je gDown
cmp ah,ESCAPE ;Escape?
je gCancel
cmp ah,RETURN ;Enter?
jne gKeyLoop
mov ax,320 ;X = 320 - ColOffset - CurY
sub ax,[ColOffset]
sub ax,[CurX]
cwde
sal eax,8
mov [JuliaX],eax
mov ax,200 ;Y = 200 - RowOffset - CurX
sub ax,[RowOffset]
sub ax,[CurY]
cwde
sal eax,8
mov [JuliaY],eax
xor al,al ;Return 0
ret
gCancel: mov al,1 ;Return 1 (Cancel)
ret
gUp: cmp [CurY],0 ;If not at the top,
je $+6 ;decrement cursor Y
dec [CurY]
jmp gKeyLoop
gDown: cmp [CurY],199 ;If not at the bottom,
je $+6 ;increment cursor Y
inc [CurY]
jmp gKeyLoop
gLeft: cmp [CurX],0 ;If not at the left,
je $+6 ;decrement cursor X
dec [CurX]
jmp gKeyLoop
gRight: cmp [CurX],319 ;If not at the right,
je $+6 ;increment cursor X
inc [CurX]
jmp gKeyLoop
EndP GetPos
;**************************** ShowCursor -- Show the cursor
Proc ShowCursor
pusha ;Save registers
mov bx,[CurX] ;Calculate offset
mov cx,[CurY]
add bh,cl
shl cx,6
add bx,cx
mov ax,[fs:bx-2] ;Save screen data
mov [word SaveBuf],ax
mov ax,[fs:bx+1]
mov [word SaveBuf+2],ax
mov al,[fs:bx-640]
mov [SaveBuf+4],al
mov al,[fs:bx-320]
mov [SaveBuf+5],al
mov al,[fs:bx+320]
mov [SaveBuf+6],al
mov al,[fs:bx+640]
mov [SaveBuf+7],al
mov ax,0F0Fh ;Plot cursor
mov [es:bx-2],ax
mov [fs:bx-2],ax
mov [es:bx+1],ax
mov [fs:bx+1],ax
mov [es:bx-640],al
mov [fs:bx-640],al
mov [es:bx-320],al
mov [fs:bx-320],al
mov [es:bx+320],al
mov [fs:bx+320],al
mov [es:bx+640],al
mov [fs:bx+640],al
popa ;Restore registers
ret ;Return
EndP ShowCursor
;**************************** HideCursor -- Hide the cursor
Proc HideCursor
pusha ;Save registers
mov bx,[CurX] ;Calculate offset
mov cx,[CurY]
add bh,cl
shl cx,6
add bx,cx
mov ax,[word SaveBuf] ;Restore screen data
mov [es:bx-2],ax
mov [fs:bx-2],ax
mov ax,[word SaveBuf+2]
mov [es:bx+1],ax
mov [fs:bx+1],ax
mov al,[SaveBuf+4]
mov [es:bx-640],al
mov [fs:bx-640],al
mov al,[SaveBuf+5]
mov [es:bx-320],al
mov [fs:bx-320],al
mov al,[SaveBuf+6]
mov [es:bx+320],al
mov [fs:bx+320],al
mov al,[SaveBuf+7]
mov [es:bx+640],al
mov [fs:bx+640],al
popa ;Restore registers
ret ;Return
EndP HideCursor
;**************************** DrawM -- Draw Mandelbrot Set
Proc DrawM
mov [RowCtr],200 ;Init row counter
CalcRow: add si,320 ;Init column counter
CalcPixel: call DoPixel ;Calculate pixel
mov ax,bp ;Get pixel in AL
mov [es:di],al ;Write pixel
mov [fs:di],al
inc di
add di,(1 shl TIMES)-1 ;Advance DI
sub si,(1 shl TIMES) ;Dec column counter
jg CalcPixel ;Loop back
test di,(1 shl TIMES)-1 ;Slant off the edge?
jnz $+5 ;Jump if not
add di,(1 shl TIMES) ;Next diag. line
dec di ;Slant back
mov si,di ;SI = -(DI AND 7)
and si,(1 shl TIMES)-1
neg si
dec [RowCtr] ;Row loop
jnz CalcRow
FlashLoop: mov di,[Dist] ;DI = Distance
mov si,[Dist] ;SI = -Distance
neg si
mov [RowCtr],200 ;Init row counter
a_CalcRow: add si,320 ;Init column counter
a_PixLoop: mov bp,di ;If the points directly
add di,[Dist] ;above, below, and to the
mov al,[fs:di] ;left and right are all
sub di,[Incr] ;the same color, plot the
cmp al,[fs:di] ;pixel in that color.
jne a_CalcPixel ;Otherwise, calculate
add di,[Dist] ;the pixel separately.
sub di,[LDist]
cmp al,[fs:di]
jne a_CalcPixel
add di,[LDist2]
cmp al,[fs:di]
mov di,bp
je a_WrtPixel
a_CalcPixel: mov di,bp ;Restore DI
call DoPixel ;Calculate pixel
mov ax,bp ;Get pixel in AL
a_WrtPixel: mov [es:di],al ;Write pixel
mov [fs:di],al
inc di
add di,[IncrM] ;Advance DI
sub si,[Incr] ;Dec column counter
jg a_PixLoop ;Loop back
test di,[IncrM] ;Slant off the edge?
jnz a_DecDI ;Jump if not
add di,[Incr] ;Next diag. line
a_DecDI: dec di ;Slant back
mov si,di ;SI = -(DI AND 7)
and si,[IncrM]
neg si
dec [RowCtr] ;Row loop
jnz a_CalcRow
shr [Incr],1 ;Divide all lengths by 2
shr [IncrM],1 ;for next interpolation
shr [LDist],1
shr [LDist2],1 ;If it was the last one,
shr [Dist],1 ;this will be zero, so
jnz FlashLoop ;JNZ to loop
ret ;Return
DoPixel: mov bp,91 ;Init color counter
xor bx,bx ;Init i coefficient
xor dx,dx ;Init j coefficient
CycleColors: mov [Scratch],dx ;Save j
mov ax,bx ;AX = i
sub ax,dx ;AX = i - j
add dx,bx ;DX = i + j
imul dx ;DX:AX = (i+j)*(i-j) = i*i - j*j
mov al,ah ;Save middle bits
mov ah,dl ;(i*i - j*j)
mov dx,[Scratch] ;Restore j
xchg bx,ax ;Now swap new i with old i
sal bx,1 ;Adjust for zoom factor
sar bx,cl
sub bx,si ;Subtract column counter
add bx,[ColOffset] ;Add in offset
cmp bx,[PLimit] ;Is i >= 2 ?
jg CPret ;If so, draw this pixel
cmp bx,[NLimit] ;Is i <= -2 ?
jl CPret ;If so, draw this pixel
imul dx ;Now DX:AX = old i * j
mov dh,dl ;Get middle bits in DX
mov dl,ah
sal dx,2 ;Adjust for zoom factor
sar dx,cl
sub dx,[RowCtr] ;Add row counter
add dx,[RowOffset] ;Add in offset
cmp dx,[PLimit] ;Is j >= 2 ?
jg CPret ;If so, draw this pixel
cmp dx,[NLimit] ;Is j <= -2 ?
jl CPret ;If so, draw this pixel
dec bp ;Dec color counter
jnz CycleColors ;Loop back
CPret: ret
EndP DrawM
;**************************** DrawJ -- Draw Julia Set
Proc DrawJ
mov [RowCtr],200 ;Init row counter
jCalcRow: add si,320 ;Init column counter
jCalcPixel: call jDoPixel ;Calculate pixel
jDraw: mov ax,bp ;Get pixel in AL
mov [es:di],al ;Write pixel
mov [fs:di],al
inc di
add di,(1 shl TIMES)-1 ;Advance DI
sub si,(1 shl TIMES) ;Dec column counter
jg jCalcPixel ;Loop back
test di,(1 shl TIMES)-1 ;Slant off the edge?
jnz $+5 ;Jump if not
add di,(1 shl TIMES) ;Next diag. line
dec di ;Slant back
mov si,di ;SI = -(DI AND 7)
and si,(1 shl TIMES)-1
neg si
dec [RowCtr] ;Row loop
jnz jCalcRow
jFlashLoop: mov di,[Dist] ;DI = Distance
mov si,[Dist] ;SI = -Distance
neg si
mov [RowCtr],200 ;Init row counter
ja_CalcRow: add si,320 ;Init column counter
ja_PixLoop: mov bp,di ;If the points directly
add di,[Dist] ;above, below, and to the
mov al,[fs:di] ;left and right are all
sub di,[Incr] ;the same color, plot the
cmp al,[fs:di] ;pixel in that color.
jne ja_CalcPixel ;Otherwise, calculate
add di,[Dist] ;the pixel separately.
sub di,[LDist]
cmp al,[fs:di]
jne ja_CalcPixel
add di,[LDist2]
cmp al,[fs:di]
mov di,bp
je ja_WrtPixel
ja_CalcPixel: mov di,bp
call jDoPixel ;Calculate pixel
mov ax,bp ;Get pixel in AL
ja_WrtPixel: mov [es:di],al ;Write pixel
mov [fs:di],al
inc di
add di,[IncrM] ;Advance DI
sub si,[Incr] ;Dec column counter
jg ja_PixLoop ;Loop back
test di,[IncrM] ;Slant off the edge?
jnz ja_DecDI ;Jump if not
add di,[Incr] ;Next diag. line
ja_DecDI: dec di ;Slant back
mov si,di ;SI = -(DI AND 7)
and si,[IncrM]
neg si
dec [RowCtr] ;Row loop
jnz ja_CalcRow
shr [Incr],1 ;Divide all lengths by 2
shr [IncrM],1 ;for next interpolation
shr [LDist],1
shr [LDist2],1 ;If it was the last one,
shr [Dist],1 ;this will be zero, so
jnz jFlashLoop ;JNZ to loop
ret ;Return
jDoPixel: mov bp,135 ;Init color counter
mov bx,si ;Init i coefficient
sub bx,[ColOffset]
mov dx,[RowCtr] ;Init j coefficient
sub dx,[RowOffset]
movsx ebx,bx ;Shift them to 32-bit
movsx edx,dx ;fixed point values
sal ebx,9
sal edx,9
sar ebx,cl ;Adjust for zoom factor
sar edx,cl
jCycleColors: mov [dword Scratch],edx ;Save j
mov eax,ebx ;AX = i
sub eax,edx ;AX = i - j
add edx,ebx ;DX = i + j
imul edx ;DX:AX = (i+j)*(i-j) = i*i - j*j
shrd eax,edx,16 ;Save middle bits (i*i - j*j)
mov edx,[dword Scratch] ;Restore j
xchg ebx,eax ;Now swap new i with old i
sub ebx,[JuliaX] ;Subtract julia X parameter
cmp ebx,[dword PLimit-1];Is i >= 2 ?
jg jCPret ;If so, draw this pixel
cmp ebx,[dword NLimit-1];Is i <= -2 ?
jl jCPret ;If so, draw this pixel
imul edx ;Now EDX:EAX = old i * j
shld edx,eax,17 ;Get middle bits in DX
sub edx,[JuliaY] ;Subtract julia Y parameter
cmp edx,[dword PLimit-1];Is j >= 2 ?
jg jCPret ;If so, draw this pixel
cmp edx,[dword NLimit-1];Is j <= -2 ?
jl jCPret ;If so, draw this pixel
dec bp ;Dec color counter
jnz jCycleColors ;Loop back
jCPret: ret
EndP DrawJ
Mode db 0 ;Memory variables
Zoom db 1
db 0
PLimit dw 512
dw 0
NLimit dw -512
db -1
Scratch dw ?,?
Dist dw ?
Incr dw ?
IncrM dw ?
LDist dw ?
LDist2 dw ?
RowOffset dw ?
ColOffset dw ?
CurX dw ?
CurY dw ?
JuliaX dd ?
JuliaY dd ?
SaveRow dw ?
SaveCol dw ?
RowCtr dw ?
SaveBuf db 8 dup(?)
End Program