home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff239.lzh / JGoodies / Mandelbrot / Mandelshift2.ASM < prev    next >
Assembly Source File  |  1989-08-21  |  8KB  |  245 lines

  1. \ Copyright 1989 NerveWare
  2. \ No portion of this code may used for commercial purposes,
  3. \ nor may any executable version of this code be disributed for 
  4. \ commercial purposes without the author's express written permission.
  5. \ This code is shareware, all rights reserved.
  6. \ Nick Didkovsky
  7. \ 12/21/88
  8. \                            MandelShift2.ASM 
  9.  
  10. \ MOD: Uses 16384 as max possible scaler!             12/20/88
  11. \ MOD: 350 as mandelmax seems optimal at this scale   2/11/89
  12. \ MOD: storing mandelmax iterations in register A1    2/11/89
  13. \ MOD: storing registers d2,d5 in scratch registers a0,a2  2/11/89
  14.  
  15. \ 4 := 65536
  16. \ 2 := 32768
  17.  
  18. \ This is the speedy assembler Mandelbrot routine.
  19. \ A little roundoff error is introduced by integer math,
  20. \ visible as a little roughness along the high-contrast boundaries of the 
  21. \ image generated. But it's insanely fast.
  22.  
  23. \ MandelMax iterations of the loop qualifies a given complex 
  24. \ number as belonging to the Mandelbrot Set.
  25.  
  26. \                          THE ALGORITHM
  27. \ The iterative function applied to a given complex number C is: 
  28. \ Z := Z^2+C
  29. \ In terms of the variables below, C = aconst + (bconst)i , where i
  30. \ is the square root of -1.
  31. \ The function is initialized with Z=0+0i 
  32. \ The function's result is fed into Z again, recalculated, fed in, 
  33. \ recalculated, over and over.
  34. \ Each intermediate result's magnitude is tested.  If the magnitude >= 2,
  35. \ break out of the loop, leaving the number of iterations it took to 
  36. \ "blow up" to this value on the stack.
  37. \ If, after 254 iterations of this function, the magnitude is still < 2,
  38. \ tested number C is considered a member of the Mandelbrot Set, and 254 is left
  39. \ on the stack.
  40.  
  41. \ SPEED SHORTCUTS:
  42.  
  43. \ 1) The magnitude of a complex number equals the square root of the sum
  44. \    of the squares of its real and imaginary parts (simple "distance formula"
  45. \    from high school).  
  46. \    Shortcut: Don't bother taking the square root and checking it
  47. \    against 2, rather test the sum of the squares against 4.
  48.  
  49. \ 2) As mentioned before, I use scaled integer math.  This is MUCH faster
  50. \    than floating point.  Additionally, instead of scaling by a nice looking 
  51. \    number like 10000, I use 16384, which is a power of 2.  So, instead of
  52. \    scaling results with a DIVISION by 10000 (slow), I scale it by
  53. \    SHIFTING register contents to the right 14 places (very very fast).
  54.  
  55. \ 3) The Z := Z^2+C function can be broken down into two calculations:
  56. \    one calculates what happens to the real part of Z (call this value az), 
  57. \    the other calculates what happens to the imaginary part (call this bz).
  58. \        NEWaz := az*az - bz*bz + aconst
  59. \        NEWbz := 2*az*bz + bconst
  60. \    Speedup: Notice that az*az and bz*bz are needed to calculate the new az,
  61. \    as well as to check the magnitude of the result.  So I only calculate these
  62. \    squares ONCE in each iteration, storing the results in registers d5 and d6,
  63. \    and use them for both purposes.
  64.  
  65. \ 4) The routine uses 68000 registers ONLY.  No subroutine calls at all.  This
  66. \    makes for very fast arithmetic!  Register's previous contents are stored 
  67. \    at the top of the routine, then restored at the bottom before it exits.
  68.  
  69. anew task_Mandelbrot-ASM
  70.  
  71. \ maximum iterations is hard coded in this version of the program = 350
  72.  
  73. 350 CONSTANT MandelMax
  74.  
  75. \ These variables determine the region generated.
  76. \ Their values represent scaled values, where the scaler = 16384.
  77. \ That means unity is represented by 16384.
  78. \ The smallest value in our scaled arithmetic is obviously "1",
  79. \ which represents the fraction (1 / 16384) = 0.000061
  80. \ So, smallest distance representable between two adjacent pixels is 0.000061
  81. \ ok so it's not double precision ... 
  82.  
  83. VARIABLE acorner  \ region's top left real part ( x value)
  84. VARIABLE bcorner  \ region's top left imaginary part ( y value)
  85. VARIABLE aconst   \ real part of number being tested
  86. VARIABLE bconst   \ imaginary part of number being tested
  87. VARIABLE xgap     \ horizontal distance between adjacent pixels
  88. VARIABLE ygap     \ vertical distance between adjacent pixels
  89.  
  90. \ the following variables are used to store register contents during
  91. \ the execution of the loop.  Registers are restored when this code returns.
  92.  
  93. VARIABLE temp.d3   
  94. VARIABLE temp.d4   
  95. VARIABLE temp.d6   
  96.  
  97. \ Speed notes:
  98. \ 1000 worst-case calls to this routine takes 2.72 sec. with MandelMax = 35
  99. \ (using 0+0i, which is a Mandelbrot member)
  100. \ Old version, using 10000 scaler and DIVS took 4.58 sec!!!
  101.  
  102.  
  103. \ This uses registers only for super speed. 
  104.  
  105. \ d0 = az
  106. \ d1 = bz
  107. \ d2 = loop counter
  108. \ d3 = aconst
  109. \ d4 = bconst
  110. \ d5 = azsqr
  111. \ d6 = bzsqr
  112. \ a1 = mandelmax
  113.  
  114. ASM quick.guts  ( -- iterations)
  115. \ save d3,d4,d6 in variables
  116.     callcfa    temp.d3
  117.     move.l    d3,$0(org,tos.l)
  118.     move.l    (dsp)+,tos
  119.     callcfa    temp.d4
  120.     move.l    d4,$0(org,tos.l)
  121.     move.l    (dsp)+,tos
  122.     callcfa    temp.d6
  123.     move.l    d6,$0(org,tos.l)
  124.     move.l    (dsp)+,tos
  125.  
  126.  
  127. \ ********************** initialize d3, d4 with aconst and bconst *********
  128.  
  129.     callcfa    aconst
  130.     move.l    $0(org,tos.l),d3
  131.     move.l    (dsp)+,tos
  132.     callcfa    bconst
  133.     move.l    $0(org,tos.l),d4
  134.     move.l    (dsp)+,tos
  135.     callcfa    MandelMax
  136.     move.l    tos,a1
  137.     move.l      (dsp)+,tos
  138. \ save d2,d5 in scratch registers a0,a2
  139.     move.l    d2,a0
  140.     move.l    d5,a2
  141.  
  142. \ ************************ more init's ************************************
  143.     
  144.     moveq    #0,d2            \ initialize loop counter
  145.     moveq    #0,d0            \ initialize az
  146.     moveq    #0,d1            \ initialize bz
  147.  
  148. \ ******************  loop starts, calculate azsqr, bzsqr *****************
  149.  
  150. \ Check if operand greater than 32768.  If so, don't bother squaring: exit loop
  151. \ because magnitude will automatically be greater than 4 = 65536.
  152.  
  153. \ AZSQR
  154. 1$: move.l    d0,d5               \ az copied to d5
  155.     tst.l    d5            \ make sure positive
  156.     bpl.s    22$
  157.     neg.l    d5
  158. 22$: cmpi.l    #32768,d5               \ operand >= 32768 ?
  159.     bge        2$            \ if so, exit loop
  160.     muls.l    d5,d5            \ else square it, d5 now contains azsqr
  161.     asr.l    #$8,d5            \ FAST SCALE BY 16384 !!!
  162.     asr.l    #$6,d5
  163.  
  164. \ BZSQR
  165.     move.l    d1,d6            \ bz copied to d6
  166.     tst.l    d6            \ make sure positive
  167.     bpl.s    32$
  168.     neg.l    d6
  169. 32$: cmpi.l    #32768,d6               \ operand >= 32768
  170.     bge        2$            \ if so, exit loop
  171.     muls.l    d6,d6            \ else square it, d6 now contains bzsqr
  172.     asr.l    #8,d6            \ FAST SCALE!!!
  173.     asr.l    #6,d6
  174.  
  175.  
  176. \ ************************** new BZ **************************
  177.  
  178.  
  179.    tst.l d0                \ test sign of d0
  180.    bpl.s 11$                 \ d0 positive, goto 11$
  181.    neg.l d0                \ else negate d0
  182.    tst.l d1                \ and test d1
  183.    bpl.l 31$                \ d1 pos, d0 was neg, goto 31$
  184.    neg.l d1                \ both neg, negate d1 
  185.    bra   21$                \ and goto 21$ 
  186.  
  187. 11$: tst.l d1                \ d0 pos, test d1
  188.      bpl.s 21$                \ both positive, goto 21$
  189.      neg.l d1                \ else negate d1
  190.      bra   31$                \ and goto 31$
  191.  
  192. 21$: muls.l    d1,d0            \ * bz
  193.      asr.l      #8,d0            \ FAST SCALE !!!
  194.      asr.l    #6,d0
  195.      bra     41$            \ leave answer positive
  196.  
  197. 31$: muls.l    d1,d0            \ * bz
  198.      asr.l      #8,d0            \ FAST SCALE !!!
  199.      asr.l    #6,d0
  200.      neg.l    d0            \ make final answer negative
  201.  
  202.  
  203. 41$: add.l    d0,d0            \ 2* 
  204.      add.l    d4,d0            \ + bconst
  205.      move.l    d0,d1            \ new bz now in d1
  206.  
  207. \ ************************** NEW AZ ***********************************
  208.  
  209.     move.l    d6,d0            \ copy bzsqr out to d0
  210.     neg.l    d0            \ negate its copy
  211.     add.l    d5,d0            \ azsqr-bzsqr
  212.     add.l    d3,d0            \ + aconst, new az now in d0 
  213.  
  214.     
  215. \ ******************* TEST FOR PREMATURE EXIT FROM LOOP ******************
  216.  
  217.     add.l    d5,d6            \ azsqr+bzsqr, magnitude is in d6 now
  218.     cmpi.l    #65536,d6        \ check if magnitude > 65536=4 
  219.     bgt        2$            \ if > , exit loop, else... 
  220.     addi.l    #1,d2             \ ... increment loop counter
  221.     cmp.l    a1,d2             \ MandelMax iterations stored in A1
  222.     blt        1$             \ back to loop start
  223. 2$: move.l     tos,-(dsp)
  224.     move.l    d2,tos          \ leave iteration count on tos
  225.  
  226. \ now restore register contents
  227.  
  228.     move.l     a0,d2
  229.     move.l    a2,d5
  230.  
  231.     callcfa    temp.d3
  232.     move.l    $0(org,tos.l),d3     \ restore d3 !!!
  233.     move.l    (dsp)+,tos
  234.     callcfa    temp.d4
  235.     move.l    $0(org,tos.l),d4     \ restore d4 !!!
  236.     move.l    (dsp)+,tos
  237.     callcfa    temp.d6
  238.     move.l    $0(org,tos.l),d6     \ restore d6 !!!
  239.     move.l    (dsp)+,tos
  240. END-CODE
  241.  
  242. \ ************************ end quick guts **********************************
  243.  
  244.