home *** CD-ROM | disk | FTP | other *** search
/ Computerworld 1996 March / Computerworld_1996-03_cd.bin / idg_cd3 / grafika / fraktaly / frasr192 / calcmand.asm < prev    next >
Assembly Source File  |  1995-03-18  |  33KB  |  985 lines

  1. ;    CALCMAND.ASM - Mandelbrot/Julia Set calculation Routines
  2.  
  3. ;    This module runs as part of an overlay with calcfrac.c.
  4. ;    It must not be called from anywhere other than calcfrac.
  5.  
  6. ;    The routines in this code perform Mandelbrot and Julia set
  7. ;    calculations using 32-bit integer math as opposed to the
  8. ;    "traditional" floating-point approach.
  9.  
  10. ;    This code relies on several tricks to run as quickly as it does.
  11.  
  12. ;    One can fake floating point arithmetic by using integer
  13. ;    arithmetic and keeping track of the implied decimal point
  14. ;    if things are reasonable -- and in this case, they are.
  15. ;    I replaced code that looked like: z = x*y with code that
  16. ;    looks like:
  17. ;            ix = x * ifudge         (outside the loops)
  18. ;            iy = y * ifudge
  19. ;            ....
  20. ;            iz = (ix * iy) / ifudge     (inside the loops)
  21. ;    (and keep remembering that all the integers are "ifudged" bigger)
  22.  
  23. ;    The 386 has native 32-bit integer arithmetic, and (briefly) keeps
  24. ;    64-bit values around after 32-bit multiplies.    If the result is
  25. ;    divided down right away, you've got 64-bit arithmetic.   You just
  26. ;    have to ensure that the result after the divide is <= 32 bits long.
  27. ;    CPUs predating the 386 have to emulate 32-bit arithmetic using
  28. ;    16-bit arithmetic, which is significantly slower.
  29.  
  30. ;    Dividing is slow -- but shifting is fast, and we can select our
  31. ;    "fudge factor" to be a power of two, permitting us to use that
  32. ;    method instead.   In addition, the 386 can perform 32-bit wide
  33. ;    shifting -- and even 64-bit shifts with the following logic:
  34. ;            shdr    eax,edx,cl
  35. ;            shr    edx,cl
  36. ;    so we make sure that our "fudge factor" is a power of 2 and shift
  37. ;    it down that way.
  38. ;    Calcmand is hardcoded for a fudge factor of 2**29.
  39.  
  40.  
  41. ;                    Bert Tyler
  42. ; History since Fractint 16.0
  43. ;  (See comments with CJLT in them)
  44. ;  CJLT=Chris Lusby Taylor who has...
  45. ;
  46. ;   1. Speeded up 16 bit on 16 bit CPU
  47. ;    Minor changes, notably prescaling to fg14 before multiplying
  48. ;    instead of scaling the answer.
  49. ;    Also, I added overflow detection after adding linit, since it
  50. ;    seems this could overflow.  
  51. ;    Overall effect is about 10% faster on 386 with debugflag=8088
  52. ;   2. Speeded up 32 bit on 16 bit CPU
  53. ;    The macro `square' is totally rewritten, as is the logic for 2xy,
  54. ;    by prescaling x and y to fg31, not fg29. This allows us to do a
  55. ;    32 bit multiply in 3, not 4, 16 bit chunks while retaining full
  56. ;    fg29 accuracy.
  57. ;       Also, I removed lots of well-meaning but ineffective code handling
  58. ;    special cases of zeros and tidied up the handling of negative numbers,
  59. ;    so the routine is quite a bit shorter now and overall throughput of
  60. ;    Mandel is now over 40% faster on a 386 with debugflag=8088.
  61. ;       By the way, I was tempted to go the whole hog and replace x*x-y*y
  62. ;    by (x+y)*(x-y) to reduce 4 16-bit multiplys to 3, but it makes
  63. ;    escape detection a bit trickier. Another time, maybe.
  64. ;
  65. ;   3. Made maxit a dword variable. 1/18/94
  66.  
  67. ;             required for compatibility if Turbo ASM
  68. IFDEF ??version
  69. MASM51
  70. QUIRKS
  71. ENDIF
  72.  
  73. .MODEL    medium,c
  74. DGROUP          group   _DATA,_DATA2
  75.  
  76. .8086
  77.  
  78.     ; these must NOT be in any segment!!
  79.     ; this get's rid of TURBO-C fixup errors
  80.  
  81.     extrn    keypressed:far        ; this routine is in 'general.asm'
  82.     extrn    getakey:far        ; this routine is in 'general.asm'
  83.     extrn    iplot_orbit:far     ; this routine is in 'calcfrac.c'
  84.     extrn    scrub_orbit:far     ; this routine is in 'calcfrac.c'
  85.  
  86. _DATA2        segment DWORD PUBLIC 'DATA'
  87.  
  88. FUDGEFACTOR    equ    29        ; default (non-potential) fudgefactor
  89. KEYPRESSDELAY    equ    32767    ; 7FFFh
  90.  
  91. ; ************************ External variables *****************************
  92.  
  93.     extrn    fractype:word        ; == 0 if Mandelbrot set, else Julia
  94.     extrn    inside:word        ; "inside" color, normally 1 (blue)
  95.     extrn   outside:word            ; "outside" color, normally -1 (iter)
  96.     extrn    creal:dword, cimag:dword ; Julia Set Constant
  97.     extrn    delmin:dword        ; min increment - precision required
  98.     extrn    maxit:dword        ; maximum iterations
  99.     extrn    lm:dword        ; magnitude bailout limit
  100.     extrn    coloriter:dword        ; iterations calculated for the pixel
  101.     extrn    realcoloriter:dword
  102.  
  103.     extrn    row:word, col:word    ; current pixel to calc
  104.  
  105.     extrn    reset_periodicity:word    ; nonzero if to be reset
  106.     extrn    kbdcount:word        ; keyboard counter
  107.  
  108.     extrn    cpu:word        ; cpu type: 86, 186, 286, or 386
  109.     extrn    dotmode:word
  110.  
  111.     extrn    show_orbit:word     ; "show-orbit" flag
  112.     extrn    orbit_ptr:word        ; "orbit pointer" flag
  113.     extrn    periodicitycheck:word    ; no periodicity if zero
  114.     extrn    lclosenuff:dword
  115.  
  116.     public    linitx,linity        ; caller sets these
  117.  
  118.     extrn    nextsavedincr:word        ; for incrementing AND value
  119.     extrn    firstsavedand:dword        ; AND value
  120.  
  121. ; ************************ Internal variables *****************************
  122.  
  123.         align    4
  124. x        dd    0        ; temp value: x
  125. y        dd    0        ; temp value: y
  126. ;absx        dd    0        ; temp value: abs(x)
  127. linitx        dd    0        ; initial value, set by calcfrac
  128. linity        dd    0        ; initial value, set by calcfrac
  129. savedx        dd    0        ; saved values of X and Y iterations
  130. savedy        dd    0        ;  (for periodicity checks)
  131. k        dd    0        ; iteration countdown counter
  132. oldcoloriter    dd    0        ; prior pixel's escape time k value
  133. savedand    dd    0        ; AND value for periodicity checks
  134. savedincr    dw    0        ; flag for incrementing AND value
  135. period        db    0        ; periodicity, if in the lake
  136.  
  137. _DATA2        ends
  138.  
  139. .CODE
  140.  
  141. ; ***************** Function calcmandasm() **********************************
  142.  
  143.     public    calcmandasm
  144.  
  145. FRAME    MACRO regs
  146.     push    bp
  147.     mov    bp, sp
  148.     IRP    reg, <regs>
  149.       push    reg
  150.       ENDM
  151.     ENDM
  152.  
  153. UNFRAME MACRO regs
  154.     IRP    reg, <regs>
  155.       pop reg
  156.       ENDM
  157.     pop bp
  158.     ENDM
  159.  
  160. calcmandasm    proc
  161.     FRAME    <di,si>         ; std frame, for TC++ overlays
  162.     sub    ax,ax            ; clear ax
  163.     mov    dx,ax            ; clear dx
  164.     cmp    periodicitycheck,ax    ; periodicity checking disabled?
  165.     je    initoldcolor        ;  yup, set oldcolor 0 to disable it
  166.     cmp    reset_periodicity,ax    ; periodicity reset?
  167.     je    short initparms     ; inherit oldcolor from prior invocation
  168.     mov    ax,word ptr maxit        ; yup.    reset oldcolor to maxit-250
  169.     mov    dx,word ptr maxit+2
  170.     sub    ax,250            ; (avoids slowness at high maxits)
  171.     sbb    dx,0
  172.  
  173. initoldcolor:
  174.     mov    word ptr oldcoloriter,ax        ; reset oldcoloriter
  175.     mov    word ptr oldcoloriter+2,dx        ; reset oldcoloriter
  176.  
  177. initparms:
  178.     mov    ax,word ptr creal    ; initialize x == creal
  179.     mov    dx,word ptr creal+2    ;  ...
  180.     mov    word ptr x,ax        ;  ...
  181.     mov    word ptr x+2,dx     ;  ...
  182.  
  183.     mov    ax,word ptr cimag    ; initialize y == cimag
  184.     mov    dx,word ptr cimag+2    ;  ...
  185.     mov    word ptr y,ax        ;  ...
  186.     mov    word ptr y+2,dx     ;  ...
  187.  
  188.     mov    ax,word ptr maxit        ; setup k = maxit
  189.     mov    dx,word ptr maxit+2
  190.     add    ax,1            ; (+ 1)
  191.     adc    dx,0
  192.     mov    word ptr k,ax            ;  (decrementing to 0 is faster)
  193.     mov    word ptr k+2,dx
  194.  
  195.     cmp    fractype,1        ; julia or mandelbrot set?
  196.     je    short dojulia        ; julia set - go there
  197.  
  198. ;    (Tim wants this code changed so that, for the Mandelbrot,
  199. ;    Z(1) = (x + iy) + (a + ib).  Affects only "fudged" Mandelbrots.
  200. ;    (for the "normal" case, a = b = 0, and this works, too)
  201. ;    cmp    word ptr x,0        ; Mandelbrot shortcut:
  202. ;    jne    short doeither        ;  if creal = cimag = 0,
  203. ;    cmp    word ptr x+2,0        ; the first iteration can be emulated.
  204. ;    jne    short doeither        ;  ...
  205. ;    cmp    word ptr y,0        ;  ...
  206. ;    jne    short doeither        ;  ...
  207. ;    cmp    word ptr y+2,0        ;  ...
  208. ;    jne    short doeither        ;  ...
  209. ;    dec    k            ; we know the first iteration passed
  210. ;    mov    dx,word ptr linitx+2    ; copy x = linitx
  211. ;    mov    ax,word ptr linitx    ;  ...
  212. ;    mov    word ptr x+2,dx     ;  ...
  213. ;    mov    word ptr x,ax        ;  ...
  214. ;    mov    dx,word ptr linity+2    ; copy y = linity
  215. ;    mov    ax,word ptr linity    ;  ...
  216. ;    mov    word ptr y+2,dx     ;  ...
  217. ;    mov    word ptr y,ax        ;  ...
  218.  
  219.     sub    word ptr k,1        ; we know the first iteration passed
  220.     sbb    word ptr k+2,0        ; we know the first iteration passed
  221.     mov    dx,word ptr linitx+2    ; add x += linitx
  222.     mov    ax,word ptr linitx    ;  ...
  223.     add    word ptr x,ax        ;  ...
  224.     adc    word ptr x+2,dx     ;  ...
  225.     mov    dx,word ptr linity+2    ; add y += linity
  226.     mov    ax,word ptr linity    ;  ...
  227.     add    word ptr y,ax        ;  ...
  228.     adc    word ptr y+2,dx     ;  ...
  229.     jmp    short doeither        ; branch around the julia switch
  230.  
  231. dojulia:                ; Julia Set initialization
  232.                     ; "fudge" Mandelbrot start-up values
  233.     mov    ax,word ptr x        ; switch x with linitx
  234.     mov    dx,word ptr x+2     ;  ...
  235.     mov    bx,word ptr linitx    ;  ...
  236.     mov    cx,word ptr linitx+2    ;  ...
  237.     mov    word ptr x,bx        ;  ...
  238.     mov    word ptr x+2,cx     ;  ...
  239.     mov    word ptr linitx,ax    ;  ...
  240.     mov    word ptr linitx+2,dx    ;  ...
  241.  
  242.     mov    ax,word ptr y        ; switch y with linity
  243.     mov    dx,word ptr y+2     ;  ...
  244.     mov    bx,word ptr linity    ;  ...
  245.     mov    cx,word ptr linity+2    ;  ...
  246.     mov    word ptr y,bx        ;  ...
  247.     mov    word ptr y+2,cx     ;  ...
  248.     mov    word ptr linity,ax    ;  ...
  249.     mov    word ptr linity+2,dx    ;  ...
  250.  
  251. doeither:                ; common Mandelbrot, Julia set code
  252.     mov    period,0        ; claim periodicity of 1
  253.     mov    ax,word ptr firstsavedand    ; initial periodicity check
  254.     mov    word ptr savedand,ax    ; initial periodicity check
  255.     mov    ax,word ptr firstsavedand+2    ; initial periodicity check
  256.     mov    word ptr savedand+2,ax    ; initial periodicity check
  257.     mov    savedincr,1        ;  flag for incrementing periodicity
  258.     mov    word ptr savedx+2,0ffffh; impossible value of "old" x
  259.     mov    word ptr savedy+2,0ffffh; impossible value of "old" y
  260.     mov    orbit_ptr,0        ; clear orbits
  261.  
  262.     dec    kbdcount        ; decrement the keyboard counter
  263.     jns    short nokey        ;  skip keyboard test if still positive
  264.     mov    kbdcount,10        ; stuff in a low kbd count
  265.     cmp    show_orbit,0        ; are we showing orbits?
  266.     jne    quickkbd        ;  yup.  leave it that way.
  267.     mov    kbdcount,5000    ; else, stuff an appropriate count val
  268.     cmp    cpu,386         ; ("appropriate" to the CPU)
  269.     je    short kbddiskadj    ;  ...
  270. ;;    cmp    word ptr delmin+2,1    ; is 16-bit math good enough?
  271.     cmp    word ptr delmin+2,8    ; is 16-bit math good enough?
  272.     ja    kbddiskadj        ;  yes. test less often
  273.     mov    kbdcount,500    ;  no.    test more often
  274. kbddiskadj:
  275.     cmp    dotmode,11        ; disk video?
  276.     jne    quickkbd        ;  no, leave as is
  277.     shr    kbdcount,1    ; yes, reduce count
  278.     shr    kbdcount,1    ; yes, reduce count
  279. quickkbd:
  280.     call    far ptr keypressed    ; has a key been pressed?
  281.     cmp    ax,0            ;  ...
  282.     je    nokey            ; nope.  proceed
  283.     mov    kbdcount,0        ; make sure it goes negative again
  284.     cmp    ax,'o'                  ; orbit toggle hit?
  285.     je    orbitkey        ;  yup.  show orbits
  286.     cmp    ax,'O'                  ; orbit toggle hit?
  287.     jne    keyhit            ;  nope.  normal key.
  288. orbitkey:
  289.     call    far ptr getakey     ; read the key for real
  290.     mov    ax,1            ; reset orbittoggle = 1 - orbittoggle
  291.     sub    ax,show_orbit        ;  ...
  292.     mov    show_orbit,ax        ;  ...
  293.     jmp    short nokey        ; pretend no key was hit
  294. keyhit: mov    ax,-1            ; return with -1
  295.     mov    dx,ax
  296.     mov    word ptr coloriter,ax    ; set coloriter to -1
  297.     mov    word ptr coloriter+2,dx
  298.     UNFRAME <si,di>         ; pop stack frame
  299.     ret                ; bail out!
  300.  
  301. nokey:
  302.     cmp    show_orbit,0        ; is orbiting on?
  303.     jne    no16bitcode        ;  yup.  slow down.
  304.     cmp    cpu,386         ; are we on a 386?
  305.     je    short code386bit    ;  YAY!! 386-class speed!
  306. ;;    cmp    word ptr delmin+2,1    ; OK, we're desperate.  16 bits OK?
  307.     cmp    word ptr delmin+2,8    ; OK, we're desperate.  16 bits OK?
  308.     ja    yes16bitcode        ;  YAY!  16-bit speed!
  309. no16bitcode:
  310.     call    near ptr code32bit    ; BOO!! nap time.  Full 32 bit math
  311.     cmp    cx,-1
  312.     je    keyhit        ; key stroke, get us out of here
  313.     jmp    kloopend        ;  bypass the 386-specific code.
  314. yes16bitcode:
  315.     call    near ptr code16bit    ; invoke the 16-bit version
  316.     cmp    cx,-1
  317.     je    keyhit        ; key stroke, get us out of here
  318.     jmp    kloopend        ;  bypass the 386-specific code.
  319.  
  320. .386                    ; 386-specific code starts here
  321.  
  322. code386bit:
  323. ;;    cmp    word ptr delmin+2,3    ; is 16-bit math good enough?
  324.     cmp    word ptr delmin+2,8    ; is 16-bit math good enough?
  325.     jbe    code386_32        ; nope, go do 32 bit stuff
  326. IFDEF ??version
  327.     jmp    code386_32        ; TASM screws up IMUL EBX,EBX!!
  328. ENDIF
  329.  
  330.     ; 16 bit on 386, now we are really gonna move
  331.     movsx    esi,word ptr x+2    ; use SI for X
  332.     movsx    edi,word ptr y+2    ; use DI for Y
  333.     push    ebp
  334.     mov    ebp,-1
  335.     shl    ebp,FUDGEFACTOR-1
  336.     mov    cx,FUDGEFACTOR-16
  337.  
  338. kloop386_16:   ; cx=bitshift-16, ebp=overflow.mask
  339.  
  340.     mov    ebx,esi         ; compute (x * x)
  341.     imul    ebx,ebx         ;  ...
  342.     test    ebx,ebp         ;
  343.     jnz    short end386_16     ;  (oops.  We done.)
  344.     shr    ebx,cl            ; get result down to 16 bits
  345.  
  346.     mov    edx,edi         ; compute (y * y)
  347.     imul    edx,edx         ;  ...
  348.     test    edx,ebp         ; say, did we overflow? <V20-compat>
  349.     jnz    short end386_16     ;  (oops.  We done.)
  350.     shr    edx,cl            ; get result down to 16 bits
  351.  
  352.     mov    ax,bx            ; compute (x*x - y*y) / fudge
  353.     sub    bx,dx            ;  for the next iteration
  354.  
  355.     add    ax,dx            ; compute (x*x + y*y) / fudge
  356.  
  357.     cmp    ax,word ptr lm+2    ; while (xx+yy < lm)
  358.     jae    short end386_16     ;  ...
  359.  
  360.     imul    edi,esi         ; compute (y * x)
  361.     shl    edi,1            ; ( * 2 / fudge)
  362.     sar    edi,cl
  363.     add    di,word ptr linity+2    ; (2*y*x) / fudge + linity
  364.     movsx    edi,di            ; save as y
  365.  
  366.     add    bx,word ptr linitx+2    ; (from above) (x*x - y*y)/fudge + linitx
  367.     movsx    esi,bx            ; save as x
  368.  
  369. ;    mov    eax,oldcoloriter        ; recall the old color
  370. ;    cmp    eax,k            ; check it against this iter
  371. ;    jge    short chkpd386_16    ;  yup.  do periodicity check.
  372.     mov    eax,k            ; rearranged for speed
  373.     cmp    eax,oldcoloriter
  374.     jb    short chkpd386_16
  375. nonmax386_16:
  376. ; miraculously, k is always loaded into eax at this point
  377. ;    mov    eax,k    ; set up to test for key stroke
  378.     test    eax,KEYPRESSDELAY
  379.     jne    notakey1        ; don't test yet
  380.     push    cx
  381.     call    far ptr keypressed    ; has a key been pressed?
  382.     pop    cx
  383.     cmp    ax,0            ;  ...
  384.     je    notakey1            ; nope.  proceed
  385.     pop    ebp
  386.     jmp    keyhit
  387. notakey1:
  388.  
  389.     dec    k            ; while (k < maxit)
  390.     jnz    short kloop386_16    ; try, try again
  391. end386_16:
  392.     pop    ebp
  393.     jmp    kloopend32        ; we done
  394.  
  395. chkpd386_16:
  396. ;    mov    eax,k    ; set up to test for save-time    ;; already loaded
  397.     test    eax,savedand        ; save on 0, check on anything else
  398.     jz    short chksv386_16    ;  time to save a new "old" value
  399.     mov    bx,si            ; load up x
  400.     xor    bx,word ptr savedx+2    ; does X match?
  401.     cmp    bx,word ptr lclosenuff+2 ;  truncate to appropriate precision
  402.     ja    short nonmax386_16    ;  nope.  forget it.
  403.     mov    bx,di            ; now test y
  404.     xor    bx,word ptr savedy+2    ; does Y match?
  405.     cmp    bx,word ptr lclosenuff+2 ;  truncate to appropriate precision
  406.     ja    short nonmax386_16    ;  nope.  forget it.
  407.     mov    period,1        ; note that we have found periodicity
  408.     mov    k,0    ; pretend maxit reached
  409.     jmp    short end386_16
  410. chksv386_16:
  411.     mov    word ptr savedx+2,si    ; save x
  412.     mov    word ptr savedy+2,di    ; save y
  413.     dec    savedincr        ; time to change the periodicity?
  414.     jnz    short nonmax386_16    ;  nope.
  415.     shl    savedand,1        ; well then, let's try this one!
  416.     inc    savedand        ;  (2**n +1)
  417.     mov    ax,nextsavedincr    ; and reset the increment flag
  418.     mov    savedincr,ax    ; and reset the increment flag
  419.     jmp    short nonmax386_16
  420.  
  421.     ; 32bit on 386:
  422. code386_32:
  423.     mov    esi,x            ; use ESI for X
  424.     mov    edi,y            ; use EDI for Y
  425.  
  426. ;    This is the main processing loop.  Here, every T-state counts...
  427.  
  428. kloop:                    ; for (k = 0; k <= maxit; k++)
  429.  
  430.     mov    eax,esi         ; compute (x * x)
  431.     imul    esi            ;  ...
  432.     shrd    eax,edx,FUDGEFACTOR    ; ( / fudge)
  433.     shr    edx,FUDGEFACTOR-1    ; (complete 64-bit shift and check
  434.     jne    short kloopend1     ; bail out if too high
  435.     mov    ebx,eax         ; save this for below
  436.  
  437.     mov    eax,edi         ; compute (y * y)
  438.     imul    edi            ;  ...
  439.     shrd    eax,edx,FUDGEFACTOR    ; ( / fudge)
  440.     shr    edx,FUDGEFACTOR-1    ; (complete 64-bit shift and check
  441.     jne    short kloopend1     ; bail out if too high
  442.  
  443.     mov    ecx,ebx         ; compute (x*x - y*y) / fudge
  444.     sub    ebx,eax         ;  for the next iteration
  445.  
  446.     add    ecx,eax         ; compute (x*x + y*y) / fudge
  447.     cmp    ecx,lm            ; while (lr < lm)
  448.     jae    short kloopend1     ;  ...
  449.  
  450.     mov    eax,edi         ; compute (y * x)
  451.     imul    esi            ;  ...
  452.     shrd    eax,edx,FUDGEFACTOR-1    ;  ( * 2 / fudge)
  453.     add    eax,linity        ;  (above) + linity
  454.     mov    edi,eax         ;  save this as y
  455.  
  456. ;    (from the earlier code)     ; compute (x*x - y*y) / fudge
  457.     add    ebx,linitx        ;    + linitx
  458.     mov    esi,ebx         ; save this as x
  459.  
  460. ;    mov    eax,oldcoloriter        ; recall the old coloriter
  461. ;    cmp    eax,k            ; check it against this iter
  462. ;    jge    short chkperiod1
  463.     mov    eax,k            ; rearranged for speed
  464.     cmp    eax,oldcoloriter    
  465.     jb    short chkperiod1
  466. nonmax1:
  467.     mov    eax,k
  468.     test    eax,KEYPRESSDELAY
  469.     jne    notakey2        ; don't test yet
  470.     call    far ptr keypressed    ; has a key been pressed?
  471.     cmp    ax,0            ;  ...
  472.     je    notakey2            ; nope.  proceed
  473.     jmp    keyhit
  474. notakey2:
  475.  
  476.     dec    k            ; while (k < maxit) (dec to 0 is faster)
  477.     jnz    kloop        ; while (k < maxit) ...
  478. kloopend1:
  479.     jmp    short kloopend32    ; we done.
  480.  
  481. chkperiod1:
  482. ;    mov    eax,k        ; already done
  483.     test    eax,savedand
  484.     jz    short chksave1
  485.     mov    eax,esi
  486.     xor    eax,savedx
  487.     cmp    eax,lclosenuff
  488.     ja    short nonmax1
  489.     mov    eax,edi
  490.     xor    eax,savedy
  491.     cmp    eax,lclosenuff
  492.     ja    short nonmax1
  493.     mov    period,1        ; note that we have found periodicity
  494.     mov    k,0            ; pretend maxit reached
  495.     jmp    short kloopend32    ; we done.
  496. chksave1:
  497.     mov    eax,k
  498.     mov    savedx,esi
  499.     mov    savedy,edi
  500.     dec    savedincr        ; time to change the periodicity?
  501.     jnz    short nonmax1        ;  nope.
  502.     shl    savedand,1        ; well then, let's try this one!
  503.     inc    savedand        ;  (2**n +1)
  504.     mov    ax,nextsavedincr        ; and reset the increment flag
  505.     mov    savedincr,ax        ; and reset the increment flag
  506.     jmp    short nonmax1
  507.  
  508. kloopend32:
  509.  
  510.     cmp    orbit_ptr,0        ; any orbits to clear?
  511.     je    noorbit32        ;  nope.
  512.     call    far ptr scrub_orbit    ; clear out any old orbits
  513. noorbit32:
  514.  
  515.     mov    eax, k        ; set old color
  516.     sub    eax,10        ; minus 10, for safety
  517.     mov    oldcoloriter,eax    ; and save it as the "old" color
  518.     mov    eax,maxit        ; compute color
  519.     sub    eax,k            ;  (first, re-compute "k")
  520.     sub    kbdcount,ax        ; adjust the keyboard count (use ax only)
  521.     cmp    eax,0            ; convert any "outlier" region
  522.     jg    short coloradjust1_32    ;  (where abs(x) > 2 or abs(y) > 2)
  523.     mov    eax,1            ;   to look like we ran through
  524. coloradjust1_32:                ;    at least one loop.
  525.     mov     realcoloriter,eax     ; result before adjustments
  526.     cmp     eax,maxit             ; did we max out on iterations?
  527.     jne     short notmax32        ;  nope.
  528.     mov     oldcoloriter,eax      ; set "oldcolor" to maximum
  529.     cmp     inside,0                ; is "inside" >= 0?
  530.     jl      wedone32                ;  nope.  leave it at "maxit"
  531.     sub     eax,eax
  532.     mov     ax,inside               ; reset max-out color to default
  533.     cmp     periodicitycheck,0      ; show periodicity matches?
  534.     jge     wedone32                ;  nope.
  535. ;    mov     al,period               ;  reset color to periodicity flag
  536.     cmp     period,0
  537.     je      wedone32
  538.     mov     ax,7                    ; use color 7 (default white)
  539.     jmp     short wedone32
  540.  
  541. notmax32:
  542.     cmp     outside,0               ; is "outside" >= 0?
  543.     jl      wedone32                ;   nope. leave as realcolor
  544.     sub     eax,eax
  545.     mov     ax, outside             ; reset to "outside" color
  546.  
  547. wedone32:                             ;
  548.     mov     coloriter,eax           ; save the color result
  549.     shld    edx,eax,16              ; put result in ax,dx
  550.     shr     eax,16
  551.     UNFRAME <si,di>                 ; pop stack frame
  552.     ret                             ; and return with color
  553.  
  554.  
  555. .8086                    ; 386-specific code ends here
  556.  
  557. kloopend:
  558.     cmp    orbit_ptr,0        ; any orbits to clear?
  559.     je    noorbit2        ;  nope.
  560.     call    far ptr scrub_orbit    ; clear out any old orbits
  561. noorbit2:
  562.  
  563.     mov    ax,word ptr k    ; set old color
  564.     mov    dx,word ptr k+2    ; set old color
  565.     sub    ax,10            ; minus 10, for safety
  566.     sbb    dx,0
  567.     mov    word ptr oldcoloriter,ax    ; and save it as the "old" color
  568.     mov    word ptr oldcoloriter+2,dx    ; and save it as the "old" color
  569.     mov    ax,word ptr maxit        ; compute color
  570.     mov    dx,word ptr maxit+2    ; compute color
  571.     sub    ax,word ptr k        ;  (first, re-compute "k")
  572.     sbb    dx,word ptr k+2        ;  (first, re-compute "k")
  573.     sub    kbdcount,ax            ; adjust the keyboard count
  574.     cmp    dx,0            ; convert any "outlier" region
  575.     js    short kludge_for_julia    ;  k can be > maxit!!!
  576.     ja    short coloradjust1    ;  (where abs(x) > 2 or abs(y) > 2)
  577.     cmp    ax,0
  578.     ja    short coloradjust1    ;  (where abs(x) > 2 or abs(y) > 2)
  579. kludge_for_julia:
  580.     mov    ax,1            ;   to look like we ran through
  581.     sub    dx,dx
  582. coloradjust1:                ;    at least one loop.
  583.     mov     word ptr realcoloriter,ax     ; result before adjustments
  584.     mov     word ptr realcoloriter+2,dx   ; result before adjustments
  585.     cmp     dx,word ptr maxit+2           ; did we max out on iterations?
  586.     jne     short notmax            ;  nope.
  587.     cmp     ax,word ptr maxit             ; did we max out on iterations?
  588.     jne     short notmax            ;  nope.
  589.     mov     word ptr oldcoloriter,ax      ; set "oldcolor" to maximum
  590.     mov     word ptr oldcoloriter+2,dx    ; set "oldcolor" to maximum
  591.     cmp     inside,0                ; is "inside" >= 0?
  592.     jl      wedone                  ;  nope.  leave it at "maxit"
  593.     mov     ax,inside               ; reset max-out color to default
  594.     sub     dx,dx
  595.     cmp     periodicitycheck,0      ; show periodicity matches?
  596.     jge     wedone                  ;  nope.
  597. ;    sub     ax,ax                   ; clear top half for next
  598. ;    mov     al,period               ;  reset color to periodicity flag
  599.     cmp     period,0
  600.     jz      wedone
  601.     mov     ax,7                    ; use color 7 (default white)
  602.     jmp     short wedone
  603.  
  604. notmax:
  605.     cmp     outside,0               ; is "outside" >= 0?
  606.     jl      wedone                  ;   nope. leave as realcolor
  607.     mov     ax, outside             ; reset to "outside" color
  608.     sub     dx,dx
  609.  
  610. wedone:                                 ;
  611.     mov     word ptr coloriter,ax   ; save the color result
  612.     mov     word ptr coloriter+2,dx   ; save the color result
  613.     UNFRAME <si,di>                 ; pop stack frame
  614.     ret                             ; and return with color
  615.  
  616. calcmandasm endp
  617.  
  618.  
  619. ; ******************** Function code16bit() *****************************
  620. ;
  621. ;    Performs "short-cut" 16-bit math where we can get away with it.
  622. ; CJLT has modified it, mostly by preshifting x and y to fg30 from fg29
  623. ; or, since we ignore the lower 16 bits, fg14 from fg13.
  624. ; If this shift overflows we are outside x*x+y*y=2, so have escaped.
  625. ; Also, he commented out several conditional jumps which he calculated could
  626. ; never be taken (e.g. mov ax,si / imul si ;cannot overflow).
  627.  
  628. code16bit    proc    near
  629.  
  630.     mov    si,word ptr x+2     ; use SI for X fg13
  631.     mov    di,word ptr y+2     ; use DI for Y fg13
  632.  
  633. start16bit:
  634.     add    si,si            ;CJLT-Convert to fg14
  635.     jo    end16bit        ;overflows if <-2 or >2
  636.     mov    ax,si            ; compute (x * x)
  637.     imul    si            ; Answer is fg14+14-16=fg12
  638. ;    cmp    dx,0            ;CJLT commented out-
  639. ;    jl    end16bit        ;CJLT-  imul CANNOT overflow
  640. ;    mov    cx,32-FUDGEFACTOR    ;CJLT. FUDGEFACTOR=29 is hard coded
  641. loop16bit1:
  642.     shl    ax,1            ;  ...
  643.     rcl    dx,1            ;  ...
  644.     jo    end16bit        ;  (oops.  overflow)
  645. ;    loop    loop16bit1        ;CJLT...do it once only. dx now fg13.
  646.     mov    bx,dx            ; save this for a tad
  647.  
  648. ;ditto for y*y...
  649.  
  650.     add    di,di            ;CJLT-Convert to fg14
  651.     jo    end16bit        ;overflows if <-2 or >2
  652.     mov    ax,di            ; compute (y * y)
  653.     imul    di            ;  ...
  654. ;    cmp    dx,0            ; say, did we overflow? <V20-compat>
  655. ;    jl    end16bit        ;  (oops.  We done.)
  656. ;    mov    cx,32-FUDGEFACTOR    ; ( / fudge)
  657. ;loop16bit2:
  658.     shl    ax,1            ;  ...
  659.     rcl    dx,1            ;  ...
  660.     jo    end16bit        ;  (oops.  overflow)
  661. ;    loop    loop16bit2        ;  ...
  662.  
  663.     mov    cx,bx            ; compute (x*x - y*y) / fudge
  664.     sub    bx,dx            ;  for the next iteration
  665.  
  666.     add    cx,dx            ; compute (x*x + y*y) / fudge
  667.     jo    end16bit        ; bail out if too high
  668. ;    js    end16bit        ;  ...
  669.  
  670.     cmp    cx,word ptr lm+2    ; while (xx+yy < lm)
  671.     jae    end16bit        ;  ...
  672.     sub    word ptr k,1    ; while (k < maxit)
  673.     sbb    word ptr k+2,0
  674.     jnz    notdoneyet
  675.     cmp    word ptr k,0
  676.     jz    end16bit        ;  we done.
  677. notdoneyet:
  678.     mov    ax,di            ; compute (y * x) fg14+14=fg28
  679.     imul    si            ;  ...
  680. ;    mov    cx,33-FUDGEFACTOR-2    ; ( * 2 / fudge)
  681. ;loop16bit3:
  682.     shl    ax,1            ;  ...
  683.     rcl    dx,1            ;  ...
  684.     shl    ax,1            ;  shift two bits
  685.     rcl    dx,1            ;  cannot overflow as |x|<=2, |y|<=2
  686. ;    loop    loop16bit3        ;  ...
  687.     add    dx,word ptr linity+2    ; (2*y*x) / fudge + linity
  688.     jo    end16bit        ; bail out if too high
  689.     mov    di,dx            ; save as y
  690.  
  691.     add    bx,word ptr linitx+2    ; (from above) (x*x - y*y)/fudge + linitx
  692.     jo    end16bit        ; bail out if too high
  693.     mov    si,bx            ; save as x
  694.  
  695.     mov    dx,word ptr oldcoloriter+2    ; recall the old color
  696.     cmp    dx,word ptr k+2            ; check it against this iter
  697.     jb    short nonmax3        ;  nope.  bypass periodicity check.
  698.     mov    ax,word ptr oldcoloriter    ; recall the old color
  699.     cmp    ax,word ptr k            ; check it against this iter
  700.     jb    short nonmax3        ;  nope.  bypass periodicity check.
  701.     mov    word ptr x+2,si     ; save x for periodicity check
  702.     mov    word ptr y+2,di     ; save y for periodicity check
  703.     call    checkperiod        ; check for periodicity
  704. nonmax3:
  705.     mov    ax,word ptr k    ; set up to test for key stroke
  706.     test    ax,KEYPRESSDELAY
  707.     jne    notakey3        ; don't test yet
  708.     push    cx
  709.     push    bx
  710.     call    far ptr keypressed    ; has a key been pressed?
  711.     pop    bx
  712.     pop    cx
  713.     cmp    ax,0            ;  ...
  714.     je    notakey3            ; nope.  proceed
  715.     mov    cx,-1
  716.     jmp    short end16bitgotkey    ; cx set, jump to end
  717. notakey3:
  718.     jmp    start16bit        ; try, try again.
  719.  
  720. end16bit:                ; we done.
  721.     xor    cx,cx            ; no key so zero cx
  722. end16bitgotkey:                ; jump here if key
  723.     ret
  724. code16bit    endp
  725.  
  726.  
  727. ;    The following routine checks for periodic loops (a known
  728. ;    method of decay inside "Mandelbrot Lake", and an easy way to
  729. ;    bail out of "lake" points quickly).  For speed, only the
  730. ;    high-order sixteen bits of X and Y are checked for periodicity.
  731. ;    For accuracy, this routine is only fired up if the previous pixel
  732. ;    was in the lake (which means that the FIRST "wet" pixel was
  733. ;    detected by the dull-normal maximum iteration process).
  734.  
  735. checkperiod    proc near        ; periodicity check
  736.     mov    ax,word ptr k    ; set up to test for save-time
  737.     test    ax,word ptr savedand    ; save on 0, check on anything else
  738.     jnz    notimeyet        ;  NOT time to save a new "old" value
  739.     mov    dx,word ptr k+2    ; set up to test for save-time
  740.     test    dx,word ptr savedand+2    ; save on 0, check on anything else
  741.     jz    checksave        ;  time to save a new "old" value
  742. notimeyet:
  743.     mov    dx,word ptr x+2     ; load up x
  744.     xor    dx,word ptr savedx+2
  745.     cmp    dx,word ptr lclosenuff+2
  746.     ja    checkdone
  747.     mov    ax,word ptr x        ; load up x
  748.     xor    ax,word ptr savedx
  749.     cmp    ax,word ptr lclosenuff
  750.     ja    checkdone
  751.     mov    dx,word ptr y+2     ; load up y
  752.     xor    dx,word ptr savedy+2
  753.     cmp    dx,word ptr lclosenuff+2
  754.     ja    checkdone
  755.     mov    ax,word ptr y        ; load up y
  756.     xor    ax,word ptr savedy
  757.     cmp    ax,word ptr lclosenuff
  758.     ja    checkdone
  759.     mov    period,1        ; note that we have found periodicity
  760.     mov    word ptr k,1    ; pretend maxit reached
  761.     mov    word ptr k+2,0    ; pretend maxit reached
  762. checksave:
  763.     mov    dx,word ptr x+2     ; load up x
  764.     mov    word ptr savedx+2,dx    ;  and save it
  765.     mov    ax,word ptr x        ; load up x
  766.     mov    word ptr savedx,ax    ;  and save it
  767.     mov    dx,word ptr y+2     ; load up y
  768.     mov    word ptr savedy+2,dx    ;  and save it
  769.     mov    ax,word ptr y        ; load up y
  770.     mov    word ptr savedy,ax    ;  and save it
  771.     dec    savedincr        ; time to change the periodicity?
  772.     jnz    checkdone        ;  nope.
  773.     shl    word ptr savedand,1    ; well then, let's try this one!
  774.     rcl    word ptr savedand+2,1        ; well then, let's try this one!
  775.     add    word ptr savedand,1        ;  (2**n +1)
  776.     adc    word ptr savedand+2,0        ;  (2**n +1)
  777.     mov    ax,nextsavedincr        ; and reset the increment flag
  778.     mov    savedincr,ax        ; and reset the increment flag
  779. checkdone:
  780.     ret                ; we done.
  781. checkperiod    endp
  782.  
  783.  
  784. ; ******************** Function code32bit() *****************************
  785. ;
  786. ;    Perform the 32-bit logic required using 16-bit logic
  787. ;
  788. ;    New twice as fast logic,
  789. ;       Courtesy of Bill Townsend and Mike Gelvin (CIS:73337,520)
  790. ;    Even newer, faster still by Chris Lusby Taylor
  791. ;     who noted that we needn't square the low word if we first multiply
  792. ;     by 4, since we only need 29 places after the point and this will
  793. ;     give 30. (We divide answer by two to give 29 bit shift in answer)
  794. ;    Also, he removed all testing for special cases where a word of operand
  795. ;    happens to be 0, since testing 65536 times costs more than the saving
  796. ;    1 time out of 65536! (I benchmarked it. Just removing the tests speeds
  797. ;    us up by 3%.)
  798. ;
  799. ;Note that square returns DI,AX squared in DX,AX now.
  800. ; DI,AX is first converted to unsigned fg31 form.
  801. ; (For its square to be representable in fg29 (range -4..+3.999)
  802. ; DI:AX must be in the range 0..+1.999 which fits neatly into unsigned fg31.)
  803. ; This allows us to ignore the part of the answer corresponding to AX*AX as it
  804. ; is less than half a least significant bit of the final answer.
  805. ; I thought you'd like that.
  806. ;
  807. ; As we prescaled DI:AX, we need to shift the answer 1 bit to the right to
  808. ; end up in fg29 form since 29=(29+2)+(29+2)-32-1
  809. ; However, the mid term AX*DI is needed twice, so the shifts cancel.
  810. ;
  811. ; Since abs(x) and abs(y) in fg31 form will be needed in calculating 2*X*Y
  812. ; we push them onto the stack for later use.
  813.  
  814. ; Note that square does nor affect bl,si,bp
  815. ; and leaves highword of argument in di
  816. ; but destroys bh,cx
  817. square    MACRO    donepops
  818.     LOCAL    notneg
  819.     shl    ax,1        ;Multiply by 2 to convert to fg30
  820.     rcl    di,1        ;If this overflows DI:AX was negative
  821.     jnc    notneg
  822.     not    ax            ; so negate it
  823.     not    di            ; ...
  824.     add    ax,1            ; ...
  825.     adc    di,0            ; ...
  826.     not    bl            ; change negswt
  827. notneg:    shl    ax,1        ;Multiply by 2 again to give fg31
  828.     rcl    di,1        ;If this gives a carry then DI:AX was >=2.0
  829.                 ;If its high bit is set then DI:AX was >=1.0
  830.                 ;This is OK, but note that this means that
  831.                 ;DI:AX must now be treated as unsigned.
  832.     jc    donepops
  833.     push    di        ; save y or x (in fg31 form) on stack
  834.     push    ax        ; ...
  835.     mul    di        ;GET MIDDLE PART - 2*A*B
  836.     mov    bh,ah        ;Miraculously, it needs no shifting!
  837.     mov    cx,dx
  838.     mov    ax,di
  839.     mul    ax        ;SQUARE HIGH HWORD - A*A
  840.     shl    bh,1        ;See if we round up
  841.     adc    ax,1        ;Anyway, add 1 to round up/down accurately
  842.     adc    dx,0
  843.     shr    dx,1        ;This needs shifting one bit
  844.     rcr    ax,1
  845.     add    ax,cx        ;Add in the 2*A*B term
  846.     adc    dx,0
  847.     ENDM    ;#EM
  848.  
  849.  
  850. code32bit    proc near
  851. ;
  852. ; BL IS USED FOR THE "NEGSWT" FLAG
  853. ;   NEGSWT IS ONE IF EITHER "X" OR "Y" ARE NEGATIVE, BUT NOT BOTH NEGATIVE
  854. ;
  855.  
  856.     push    bp
  857.     xor    bl,bl            ; NEGSWT STARTS OFF ZERO
  858.  
  859. ;    iteration loop
  860.  
  861. nextit:    mov    ax,word ptr y        ; ax=low(y)
  862.     mov    di,word ptr y+2     ; di=high(y)
  863.     square done1    ;square y and quit via done1 if it overflows
  864.     mov    si,ax        ; square returns results in dx,ax
  865.     mov    bp,dx        ; save y*y in bp,si
  866.     mov    ax,word ptr x
  867.     mov    di,word ptr x+2
  868.     square    done2        ; square x and quit via done2 if it overflows
  869.     mov    cx,ax        ; Save low answer in cx.
  870.     ADD    ax,si        ; calc y*y + x*x
  871.     mov    ax,bp
  872.     ADC    ax,dx        ;  ...
  873.     jno    nextxy        ; overflow?
  874.                 ;NOTE: The original code tests against lm
  875.                 ;here, but as lm=4<<29 this is the same
  876.                 ;as testing for signed overflow
  877. done4:    add    sp,4        ; discard saved value of |x| fg 31
  878. done2:    add    sp,4        ; discard saved value of |y| fg 31
  879. done1:    xor    cx,cx          ; no key exit, zero cx  
  880. done0:                ; exit here if key hit
  881.     pop    bp        ; restore saved bp
  882.     ret
  883.  
  884. ;---------------------------------------------------------------------------
  885.  
  886. nextxy:    sub    word ptr k,1    ; while (k < maxit)
  887.     sbb    word ptr k+2,0
  888.     jnz    tryagain
  889.     cmp    word ptr k,0
  890.     jz    done4        ;  we done.
  891. tryagain:
  892.     sub    cx,si            ; subtract y*y from x*x
  893.     sbb    dx,bp            ;  ...
  894.     add    cx,word ptr linitx    ; add "A"
  895.     adc    dx,word ptr linitx+2    ;  ...
  896.     jo    done4            ;CJLT-Must detect overflow here
  897.                     ; but increment loop count first
  898.     mov    word ptr x,cx        ; store new x = x*x-y*y+a
  899.     mov    word ptr x+2,dx     ;  ...
  900.  
  901. ; now calculate x*y
  902. ;
  903. ;More CJLT tricks here. We use the pushed abs(x) and abs(y) in fg31 form
  904. ;which, when multiplied, give x*y in fg30, which, luckily, is the same as...
  905. ;2*x*y fg29.
  906. ;As with squaring, we can ignore the product of the low order words, and still
  907. ;be more accurate than the original algorithm.
  908. ;
  909.     pop    bp        ;Xlow
  910.     pop    di        ;Xhigh (already there, actually)
  911.     pop    ax        ;Ylow
  912.     mul    di        ;Xhigh * Ylow
  913.     mov    bh,ah        ;Discard lowest 8 bits of answer
  914.     mov    cx,dx
  915.     pop    ax        ;Yhigh
  916.     mov    si,ax        ; copy it
  917.     mul    bp        ;Xlow * Yhigh
  918.     xor    bp,bp        ;Clear answer
  919.     add    bh,ah
  920.     adc    cx,dx
  921.     adc    bp,0
  922.     mov    ax,si        ;Yhigh
  923.     mul    di        ;Xhigh * Yhigh
  924.     shl    bh,1        ;round up/down
  925.     adc    ax,cx        ;Answer-low
  926.     adc    dx,bp        ;Answer-high
  927.                 ;NOTE: The answer is 0..3.9999 in fg29
  928.     js    done1        ;Overflow if high bit set
  929.     or    bl,bl        ; ZERO IF NONE OR BOTH X , Y NEG
  930.     jz    signok        ; ONE IF ONLY ONE OF X OR Y IS NEG
  931.     not    ax        ; negate result
  932.     not    dx        ;  ...
  933.     add    ax,1        ;  ...
  934.     adc    dx,0        ;  ...
  935.     xor    bl,bl        ;Clear negswt
  936. signok:
  937.     add    ax,word ptr linity
  938.     adc    dx,word ptr linity+2    ; dx,ax = 2(X*Y)+B
  939.     jo    done1
  940.     mov    word ptr y,ax        ; save the new value of y
  941.     mov    word ptr y+2,dx     ;  ...
  942.     mov    dx,word ptr oldcoloriter+2    ; recall the old color
  943.     cmp    dx,word ptr k+2        ; check it against this iter
  944.     jb    short chkkey4        ;  nope.  bypass periodicity check.
  945.     mov    ax,word ptr oldcoloriter    ; recall the old color
  946.     cmp    ax,word ptr k        ; check it against this iter
  947.     jb    short chkkey4        ;  nope.  bypass periodicity check.
  948.     call    checkperiod        ; check for periodicity
  949.  
  950. chkkey4:
  951.     mov    ax,word ptr k    ; set up to test for key stroke
  952.     test    ax,KEYPRESSDELAY
  953.     jne    notakey4        ; don't test yet
  954.     push    cx
  955.     push    bx
  956.     call    far ptr keypressed    ; has a key been pressed?
  957.     pop    bx
  958.     pop    cx
  959.     cmp    ax,0            ;  ...
  960.     je    notakey4        ; nope.  proceed
  961.     mov    cx,-1
  962.     jmp    done0            ; cx set, jump to very end
  963. notakey4:
  964.  
  965. chkmaxit:
  966.     cmp    show_orbit,0        ; orbiting on?
  967.     jne    horbit            ;  yep.
  968.     jmp    nextit            ;go around again
  969.  
  970. horbit: push    bx            ; save my flags
  971.     mov    ax,-1            ; color for plot orbit
  972.     push    ax            ;  ...
  973.     push    word ptr y+2        ; co-ordinates for plot orbit
  974.     push    word ptr y        ;  ...
  975.     push    word ptr x+2        ;  ...
  976.     push    word ptr x        ;  ...
  977.     call    far ptr iplot_orbit    ; display the orbit
  978.     add    sp,5*2            ; clear out the parameters
  979.     pop    bx            ; restore flags
  980.     jmp    nextit            ; go around again
  981.  
  982. code32bit    endp
  983.  
  984.        end
  985.