; tinyfrac.asm
;
; teeny program displays the Mandelbrot set.
;
; written on Sun  03-26-1995  by Ed Beroset (Fidonet 1:3641/1.250)
;
; This program was based on a program by Frank Hommers, later optimized
; for size by Mikko Hyvarinen and posted in Fidonet's 80XXX echo.
;
; This new version has many new features and was based on my own
; optimization of Hyvarinen's version.  Some features:
;
; pan     using the arrow keys, one can navigate the fractal.
;
;               Home  Up  PgUp
;               Left      Right   correspond to 8 obvious directions
;               End   Dn  PgDn
;
; zoom    there are now ten levels of magnification available.  If the
;         program is assembled with FEATURES defined, the number
;         corresponding to the zoom level (0-9, zero is most zoomed in)
;         is displayed in the upper left hand corner of the screen just
;         before each new fractal is drawn.  The gray '+' key zooms out,
;         the gray '-' key zooms in.
;
; beep    the program will beep at the completion of each fractal
;         drawing or if the user attempts to zoom past either limit.
;
; mode    if the program is assembled with MODECHANGE defined, the
;         will change to the next video mode if the 'v' key is pressed.
;         This is handy because drawing fractals at high resolution can
;         be very timeconsuming.  The user can find an interesting spot
;         in a low res mode and then change to a high res mode to see it
;         more fully rendered.
;
; size    this whole project was started off as a size optimization
;         exercise, so there have been some rather ugly tradeoffs to
;         sacrifice speed for size.
;
; 8086    yes, it runs on an 8086 although only if you leave out either
;         the FEATURES option or the MODECHANGE option and it would be
;         slower and more painful than oral surgery.
;
; cost    there IS such a thing as a free lunch!  This code is hereby
;         released to the public domain by the author.
;
;
; to assemble & link:
;   TASM /m2 tinyfrac       (assemble using two pass mode if required)
;   TLINK /Tdc tinyfrac     (link Target platform is DOS, COM file)
;
;

PIXWIDTH    equ 511
PIXHEIGHT   equ 255

ZOOMLIMIT   equ  13       ; can change to up to 13 for extended zoom in

; feel free to experiment with the following constants:

DELTA       equ 200       ; the unit of pan movement in pixels
THRESHOLD   equ  7        ; must be in the range of (0,255)
STARTSCALE  equ  5        ; a number from 0 to ZOOMLIMIT, inclusive
CHAR_COLOR  equ 0fh       ; white on black background (for PRINTZOOM feature)



; ************************************************************
;
;   Menuet header


use32

                  org     0x0

                  db      'MENUET01'
                  dd      0x01
                  dd      START
                  dd      I_END
                  dd      0x62000
                  dd      0x1000
                  dd      0,0

include 'lang.inc'
include '..\..\..\macros.inc'

STARTX  dd  200
STARTY  dd  120

scaleaddy dd 120
scaleaddx dd 200

START:

red:
        call    draw_window
        call    draw_fractal

still:

        mov  eax,10
        mcall

        dec  eax
        jz   red
        dec  eax
        jz   key

      button:
        mov  al,17
        mcall

        cmp  ah,1
        jne  no_close
        or   eax,-1
        mcall
      no_close:

        cmp  ah,2
        jne  no_bgr


        mov  eax,15   ; bgr 512 x 256
        mov  ebx,1
        mov  ecx,512
        mov  edx,256
        mcall

        mov  eax,15
        mov  ebx,5
        mov  ecx,0x1000
        mov  edx,0
        mov  esi,512*3*256
        mcall

        mov  eax,15
        mov  ebx,3
        mcall

        jmp  still

      no_bgr:

        cmp  ah,3
        jb   no_color
        cmp  ah,5
        jg   no_color
        shr  eax,8
        sub  eax,3
        imul eax,8
        add  eax,8
        not  eax
        and  eax,11000b
        mov  [shlc],al
        call draw_fractal
        jmp  still

      no_color:

        jmp  still


      key:
        mov  al,2
        mcall

        cmp  ah,'e'
        je   cycle
        cmp  ah,'r'
        je   cycle
        jmp  no_cycle
      cycle:
        call color_cycle
        jmp  still
      no_cycle:

        cmp  ah,'q'
        jne  no_in
        inc  byte [scale]
        mov  ebx,[STARTX]
        imul ebx,2
        sub  ebx,[scaleaddx]
        mov  [STARTX],ebx
        mov  ebx,[STARTY]
        imul ebx,2
        sub  ebx,[scaleaddy]
        mov  [STARTY],ebx
      no_in:

        cmp  ah,'w'
        jne  no_out
        dec  byte [scale]
        mov  ebx,[STARTX]
        add  ebx,[scaleaddx]
        shr  ebx,1
        mov  [STARTX],ebx
        mov  ebx,[STARTY]
        add  ebx,[scaleaddy]
        shr  ebx,1
        mov  [STARTY],ebx
      no_out:

        cmp  ah,130+48
        jne  no_up
        sub  [STARTY],100
      no_up:

        cmp  ah,129+48
        jne  no_down
        add  [STARTY],100
      no_down:

        cmp  ah,128+48
        jne  no_left
        sub  [STARTX],100
      no_left:

        cmp  ah,131+48
        jne  no_right
        add  [STARTX],100
      no_right:

        call draw_fractal
        jmp  still

color_cycle:

     pusha
     mov  ecx,0x08080808
     mov  esi,(256/8)*5
     cmp  ah,'e'
     je   f_out
     mov  ecx,-0x08080808
     mov  esi,(256/8)*5-1
   f_out:

   newcycle:
     mov  edi,0x1000
   newpix:
     mov  eax,[edi]
     add  eax,ecx
     mov  [edi],eax
     add  edi,4
     cmp  edi,0x1000+512*256*3
     jb   newpix
     call put_image
     mov  eax,5
     mov  ebx,1
     mcall
     dec  esi
     jnz  newcycle

     mov  eax,0
     mov  edi,0x1000
     mov  ecx,512*256*3 / 4 +50
     cld
     rep  stosd

     popa

     call draw_fractal

     ret





; **********************************************************************
;
;    Tinyfrac
;


draw_fractal:

        pusha
        mov     eax,4
        mov     ebx,10*65536+30
        mov     ecx,0x80ffffff
        mov     edx,calc
        mcall
        popa
        pusha

        movzx   ebp,word [STARTX]
        movzx   edi,word [STARTY]


;       This routine is the fractal drawing engine.  It has been
;       optimized for size, sacrificing speed.

        mov     cx, PIXHEIGHT   ; height of screen in pixels

        sub     di,cx           ; adjust our Y offset
@@CalcRow:

        push    cx

        mov     cx, PIXWIDTH -1  ; width of screen in pixels

        sub     bp,cx           ;
@@CalcPixel:
        push    cx              ; save the column counter on stack
        xor     cx, cx          ; clear out color loop counter
        xor     bx, bx          ; zero i coefficient
        xor     dx, dx          ; zero j coefficient
@@CycleColors:
        push    dx              ; save j value for later
        mov     ax, bx          ; ax = i
        sub     ax, dx          ; ax = i - j
        add     dx, bx          ; dx = i + j
        stc                     ; one additional shift, please
        call    Shifty          ; ax = ((i+j)*(i-j)) shifted right
        pop     dx              ; retrieve our saved value for j
        add     ax,bp           ; account for base offset...
        cmp     ah,THRESHOLD    ; Q: is i > THRESHOLD * 256?
        xchg    bx,ax           ; now swap new i with old i
        jg      @@draw          ; Y: draw this pixel
        clc                     ; no additional shifts here, please
        call    Shifty          ; now dx:ax = old i * j
        xchg    dx,ax           ;
        add     dx,di           ; account for base offset...
        inc     cl              ; increment color
        jnz     @@CycleColors   ; keep going until we're done
@@draw:
        xchg    ax, cx          ; mov color into al
        pop     cx              ; retrieve our column counter
        pop     dx              ; fetch row (column already in cx)
        push    dx              ; must leave a copy on the stack
        xor     bx,bx           ; write to video page zero

        call    put_pixel

        inc     bp
        loop    @@CalcPixel
        inc     di
        pop     cx
        loop    @@CalcRow

        call    put_image

        popa

        ret


put_image:

        pusha

        mov  eax,7
        mov  ebx,0x1000
        mov  ecx,512*65536+256
        mov  edx,4*65536+21
        mcall

        popa

        ret


shlc db 0

put_pixel:

        pusha
        sub     edi,[STARTY]
        sub     ebp,[STARTX]
        and     edi,0xff
        and     ebp,0x1ff
        shl     edi,9
        mov     ebx,edi ; * 3 - Y
        add     edi,ebx
        add     edi,ebx
        mov     ebx,ebp
        add     ebp,ebx
        add     ebp,ebx
        add     edi,ebp
        mov     cl,[shlc]
        mov     ebx,0xff
        shl     ebx,cl
        add     cl,3
        shl     eax,cl
        and     eax,ebx
        mov     [0x1000+edi],eax
        popa

        ret


;****************************************************************************
 ;
 ;       This routine multiplies AX by DX and shifts the result (in
;       DX:AX) to the right by scale bits (or scale+1 bits if CY is
;       set).  The resulting value is left in AX.  DX is destroyed.
;
;****************************************************************************

Shifty:
        push    cx              ; save middle bits (i*i - j*j)
        db      0b1h            ; code for mov cl,immed8
scale   db      STARTSCALE
        adc     cl,0            ; adjust per CY flag
        imul    dx              ; do the multiply

        xchg    ax,dx           ;
        shl     eax,16          ; put hi part in hi 16 bits
        xchg    ax,dx
        shr     eax,cl          ;

        pop     cx              ;
        ret                     ;



; **********************************************************************
;
;                   WINDOW DEFINITIONS AND DRAW
;
; **********************************************************************



draw_window:

      pusha

      mov  eax,12
      mov  ebx,1
      mcall

      xor  eax,eax
      mov  ebx,50*65536+PIXWIDTH+8
      mov  ecx,100*65536+PIXHEIGHT+25
      mov  edx,0x14334455
      mov  edi,title
      mcall

      mov  eax,8
      mov  ebx,290*65536+112
      mov  ecx,5*65536+12
      mov  edx,2
      mov  esi,0x808080
      mcall

      mov  ebx,420*65536+12
      mov  ecx,5*65536+12
      mov  edx,3
      mov  esi,0xa00000
      mov  edi,3
      ;mov  eax,8
    newcolor:
      mcall
      add  ebx,13*65536
      shr  esi,8
      inc  edx
      dec  edi
      jnz  newcolor

      mov  eax,4
      mov  ebx,300*65536+8
      mov  ecx,0x80ffffff
      mov  edx,button_txt
      mcall

      mov  eax,12
      mov  ebx,2
      mcall

      popa
      ret


; ***************************************************************
;
;     DATA AREA
;


title      db 'Tinyfrac - MOVE: ARROWS, ZOOM Q/W, CYCLE: E/R',0
button_txt  db 'Set as wallpaper',0

calc        db 'CALCULATING',0

I_END: