; CGA "fbc" fractal, original by Keith P. Graham, circa 1988
; documented and heavily optimized by trixter@oldskool.org 2006

org 100h

start:          jmp     short FracBegin

xsize           =       640
ysize           =       200
const1          =       0A00h
const2          =       0100h
const3          =       0200h
iterations      =       1000  ; Default is 1000.  Less is faster, but produces
; a less detailed picture.  You can go down to 4 and still have some texture,
; but at 2 starts to break up completely. >1000 doesn't seem to have an effect.
xpos            dw      0
ypos            dw      0
var_A           dw      ((const1-const2) / xsize)
var_B           dw      ((const1-const3) / ysize)
var_N           dw      0
var_M           dw      0

FracBegin:      mov     ax,0006
                int     10h                     ; switch to 640x200
                xor     ax,ax
                mov     xpos,ax                 ; init xpos:=0
                mov     cx,xsize
  
xloop:          push    cx
                mov     ypos,0                  ; init ypos:=0, happens every loop
                mov     cx,ysize

yloop:          push    cx
                call    SmallMath
                mov     cx,iterations

locloop_4:      call    HeavyMath
		jc	loc_5			; Jump if carry Set
		loop	locloop_4		; Loop if cx > 0

loc_5:          test    cx,1                    ; Do we have a pixel to put?
		jnz	loc_6			; Jump if not zero
                call    PutPixel
loc_6:          call    CheckForKey
                inc     ypos
                pop     cx
                loop    yloop
  
                inc     xpos
		pop	cx
                loop    xloop

; We're done drawing, so wait until user presses a key
kbdwait:        xor     ax,ax
		int	16h			; Keyboard i/o  ah=function 00h
                cmp     al,1Bh                  ; Is it ESC?
                jne     kbdwait                 ; Keep waiting if not
ExitProgram:    mov     ax,0003h
                int     10h                     ; Video display   ah=functn 00h
                mov     ax,4c00
                int     21h                     ; Program Terminate
  
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
;			       SUBROUTINE
;ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ
  
HeavyMath  proc    near
           push    cx       ; going to use cx as var_N
           push    bp       ; and bp as var_M
           mov     cx,var_N
           mov     bp,var_M

           mov     ax,cx    ; ax:=var_N
           mul     ax       ; ax:=var_N^2
           add     ax,200h  ; ax:=(var_N^2)+512
           adc     dx,0     ; dx:ax:=(var_N^2)+512
           mov     al,ah
           mov     ah,dl
           mov     dl,dh
           xor     dh,dh
           shr     dx,1
           rcr     ax,1
           shr     dx,1
           rcr     ax,1
           mov     bx,ax    ; bx:=((var_N^2)+512) div 1024

           mov     ax,bp
           mul     ax       ; ax:=var_M^2
           add     ax,200h
           adc     dx,0     ; dx:ax:=(var_M^2)+512
           mov     al,ah
           mov     ah,dl
           mov     dl,dh
           xor     dh,dh
           shr     dx,1
           rcr     ax,1
           shr     dx,1
           rcr     ax,1     ; dx:ax:=((var_M^2)+512) div 1024
           sub     bx,ax    ; bx:=(((var_N^2)+512)/1024)-(((var_M^2)+512)/1024)
           add     bx,si    ; si from @SmallMath; bx:=bx+(const2+(var_A*xpos))

           mov     ax,cx    ; my brain hurts; figure out the rest for yourself
           mul     bp
           add     ax,200h
           adc     dx,0
           mov     al,ah
           mov     ah,dl
           mov     dl,dh
           xor     dh,dh
           shr     dx,1
           rcr     ax,1
           shr     dx,1
           rcr     ax,1
           shl     ax,1
           add     ax,di
           mov     cx,bx
           mov     bp,ax

           mov     ax,bp
           mul     ax
           add     ax,200h
           adc     dx,0
           mov     al,ah
           mov     ah,dl
           mov     dl,dh
           xor     dh,dh
           shr     dx,1
           rcr     ax,1
           shr     dx,1
           rcr     ax,1
           mov     bx,ax

           mov     ax,cx
           mul     ax
           add     ax,200h
           adc     dx,0
           mov     al,ah
           mov     ah,dl
           mov     dl,dh
           xor     dh,dh
           shr     dx,1
           rcr     ax,1
           shr     dx,1
           rcr     ax,1
           add     ax,bx

           mov     var_N,cx
           mov     var_M,bp
           pop     bp
           pop     cx
           retn
HeavyMath  endp

;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
;			       SUBROUTINE
;ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ
  
PutPixel        proc    near
                mov     di,xpos   ; need simulate DIV by 8, keeping remainder;
                mov     bx,di     ; we can do this via bit operations
                and     bx,0007   ; bx=remainder
                shr     di,3      ; di=quotient
                mov     dx,0B800h
                mov     ax,ypos
                test    ax,1      ; are we on an odd scanline?
                jz      storit    ; if not, keep going
                mov     dx,0ba00h ; if so, adjust to point to right vidram bank
storit:         mov     es,dx
                shr     ax,1      ; optimize out? pretty sure we need that bit shifted out
                ;original was multiply ax by 80 and add to di, but we'll shift and add
                shl     ax,4      ;*16
                add     di,ax
                shl     ax,2      ;*64
                add     di,ax
                mov     al,[pixBitMask+bx] ; Get bit position of pixel...
                or      es:[di],al         ; and set it
		retn
PutPixel        endp
pixBitMask      db      80h, 40h, 20h, 10h, 08h, 04h, 02h, 01h

;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
;			       SUBROUTINE
;ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ
  
SmallMath       proc    near

                mov     si,const2
                mov     ax,xpos
                mul     var_A
                add     si,ax         ; si:=const2 + (var_A * xpos)
                mov     di,const3
                mov     ax,ypos
                mul     var_B
                add     di,ax         ; di:=const3 + (var_B * ypos)

                xor     ax,ax         ; this shaves 1 cycle and a few bytes
                mov     var_N,ax      ; over that of "mov var_N,0"
                mov     var_M,ax      ; because these are DW, not DB

                retn
SmallMath       endp
  
  
;ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
;			       SUBROUTINE
;ÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜÜ
  
CheckForKey     proc    near
		mov	ax,100h
                int     16h                     ; get status, if zf=0  al=char
                jnc     loc_ret_16
                xor     ah,ah
                int     16h                     ; get keybd char in al, ah=scan
		cmp	al,1Bh
		jne	loc_ret_16		; Jump if not equal
                jmp     ExitProgram
loc_ret_16:     retn

CheckForKey     endp
  
