;**********************************************************;
;*  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
