home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 437a.lha / Mandel_v1.0 / source / brot.asm < prev    next >
Encoding:
Assembly Source File  |  1990-11-09  |  25.8 KB  |  987 lines

  1.  
  2. * This file: brot version 1.00
  3.  
  4. * (c) 1990 by H. Helminen
  5.  
  6. * This program will draw Mandelbrot set.
  7. * It uses two advanced features:
  8.  
  9. * 1) Contour Crawling
  10. *    When this feature is enabled, I will follow the edge
  11. *    of each area of same colour and assume the interior
  12. *    is of same color. This is true for ideal set, but because
  13. *    of digital sampling, some errors may occur.
  14. *    NOTE: This method is very sensitive to errors that may occur
  15. *          if you change the code, e.g.
  16. *    o   pixel colors must not be changed during draw
  17. *    o   draw must not go out of screen bounds
  18. *    o   if screen is not clear prior to drawing,
  19. *        uncleared areas may be left untouched
  20. *    o   draw must start at an edge of the picture
  21.  
  22. *    If disabled, every single pixel is calculated individually
  23. *    resulting a bit better accuracy, a lot slower computing time.
  24. *    When drawing disconnected Julia sets, crawling should be
  25. *    disabled since it would produce inaccurate pictures.
  26. *    Setting the AUTOCRAWL bit in mb_flags will do that for you.
  27.  
  28. * 2) 32-bit fixed point arithmetics
  29. *    To speed this program up (standard A500 with 68000 and no
  30. *    FP-processors) while keeping reasonable resolution, I used
  31. *    signed 32-bit fixed point arithmetics, shifted left by 29
  32. *    bits. Multiplication was somewhat cumbersome.
  33. *    This format allows a resolution of about 1.9 x 10 ¯9.
  34. *    When the resolution needed is much less accurate, one can use
  35. *    only 16 bits in multiplication. Typically one would use
  36. *    16 bits when delta > 0.0001 .. 0.0005.
  37.  
  38. *    Setting the AUTOPREC flag in mb_flags will do that for you.
  39. *    However, in order to produce accurate pictures of Julia sets
  40. *    that have complex structure near origo, you may need to
  41. *    set the HIGH flag by yourself.
  42.  
  43. * Typical computing times for whole set with 100 iterations
  44. * (5-bitplane 320 x 256 lores screen, no competing 68000 activity)
  45.  
  46. *        crawl  walk (=traditional pixel-by-pixel method)
  47. * 16-bit  0:49   2:12
  48. * 32-bit  1:29   5:33
  49.  
  50. * And, of course, the author:
  51.  
  52. * |_|_  _   _|    _  _  _  _  _   _ _  _  ,|_ _  _
  53. * (_| )(/_ (_|(_|| )(_|(/_(_)| ) | ) )(_|/)(_(/_|
  54. *                    _)
  55. * Hannu Helminen so-dm@stekt.oulu.fi
  56.  
  57. * This proggie is quite useless by itself.
  58. * You should (b)link it with pipe.o (by me, of course).
  59. * Or you could use stack.o, which may be visually more attractive
  60. * but uses more memory.
  61. * This proggie is full of references to "stack", please ignore.
  62.  
  63. * You could use brot with your own programs. Just pass
  64. * a valid MandelBrot structure in a0 with all fields initialized.
  65. * (see mbrot.i) The RastPort's *must* have valid TmpRas and AreaInfo
  66. * structures attached to it.
  67.  
  68. * Or, link it with gui.o (again by me)
  69.  
  70. * BUGS: MANY, including:
  71.  
  72. * - not very smart handling of AUTOCRAWL, since non-connected
  73. *   set _could_ be crawled provided that before areafilling an
  74. *   area, a few points in the interior were checked to make sure
  75. *   that they are of same color.
  76. * - AUTOPREC will fail on Julia sets with complex structre near origo.
  77.  
  78.    NOLIST
  79.    INCLUDE  "macros.i"
  80.    INCLUDE  "mbrot.i"
  81.  
  82.    XREF     _AbsExecBase
  83.  
  84. * If you insist... poof, you can remove the equates and use includes.
  85. * I however prefer shorter compiles.
  86.  
  87.  
  88. * These are used to maintain a stack-like dynamically allocated structure.
  89. * (I like these functions ... only entry points are visible to this proggie)
  90.  
  91.    XREF     f_push
  92.    XREF     f_pull
  93.    XREF     f_clear
  94.  
  95. * Now make myself visible to others.
  96.  
  97.    XDEF     MandelBrot
  98.  
  99.    LIST
  100.  
  101.  
  102.  
  103.    SECTION  main,CODE
  104.  
  105. MandelBrot:
  106.          push     d0-d7/a0-a6
  107.  
  108. * Save mbrot structure to a4. It can stay there all the time...
  109.          move.l   a0,a4
  110.  
  111.          move.l   _AbsExecBase,a6
  112.          lea      GfxName,a1
  113.          moveq    #0,d0
  114.  
  115.          Call     OpenLibrary
  116.          tst.l    d0
  117.          beq      exit_mb
  118.          move.l   d0,a6
  119.  
  120. * If precision is AUTOPREC, determine which precision we actually use.
  121.          btst.b   #MBB_AUTOPREC,mb_flags(a4)
  122.          beq.s    skip_low
  123.          bset.b   #MBB_HIGH,mb_flags(a4)
  124.  
  125.          cmp.l    #DELTALIMIT,mb_dx(a4)
  126.          blo.s    skip_low
  127.          cmp.l    #DELTALIMIT,mb_dy(a4)
  128.          blo.s    skip_low
  129.          bclr.b   #MBB_HIGH,mb_flags(a4)
  130.  
  131. skip_low:
  132.  
  133. * If AUTOCRAWL is set, determine whether we should crawl or no.
  134.          btst.b   #MBB_AUTOCRAWL,mb_flags(a4)
  135.          beq.s    skip_crawl
  136.          bset.b   #MBB_CRAWL,mb_flags(a4)
  137.  
  138. * Mandelbrot set is always connected, so it may be crawled
  139.          btst.b   #MBB_JULIA,mb_flags(a4)
  140.          beq.s    skip_crawl
  141.  
  142. * Julia set should be crawled if and only if corresponding
  143. * c = jx + jy i belongs to Mandelbrot set.
  144. * LS: like that "if and only if"?
  145.          move.l   mb_jx(a4),d6
  146.          move.l   mb_jy(a4),d7
  147.          move.w   mb_i(a4),d3    ; use same number of iterations
  148.  
  149.          bsr      iterate
  150.          tst.w    d3
  151.          bmi.s    skip_crawl     ; inside the set!
  152.  
  153.          bclr.b   #MBB_CRAWL,mb_flags(a4)
  154.  
  155. skip_crawl:
  156.  
  157. * clear area prior to drawing.
  158.          move.l   mb_RastPort(a4),a5
  159.          move.l   a5,a1
  160.          move.w   #CLEAR,d0
  161.          Call     SetAPen
  162.          move.l   a5,a1
  163.          move.w   mb_x1(a4),d0
  164.          move.w   mb_y1(a4),d1
  165.          move.w   mb_x2(a4),d2
  166.          move.w   mb_y2(a4),d3
  167.          Call     RectFill
  168.  
  169. * start drawing...
  170.          btst.b   #MBB_CRAWL,mb_flags(a4)
  171.          beq.s    walk_mb
  172.  
  173.          bsr      Crawling
  174.          bra.s    exit_mb
  175. walk_mb:
  176.          bsr      Walking
  177. exit_mb:
  178.          move.l   a6,a1
  179.          move.l   _AbsExecBase,a6
  180.          Call     CloseLibrary
  181.          pull     d0-d7/a0-a6
  182. no_gfx:
  183.          rts
  184.  
  185. Walking:
  186.          move.w   mb_x1(a4),d4
  187.          move.w   mb_y1(a4),d5
  188.          move.w   d4,d6
  189.          move.w   mb_x2(a4),d7
  190.          move.w   mb_y2(a4),d3
  191. 1$:
  192.  
  193.          bsr      Pixel
  194.          bsr      Break
  195.          bne.s    3$
  196.          addq.w   #1,d4
  197.          cmp.w    d7,d4
  198.          blo.s    1$
  199.          bsr      Pixel
  200.  
  201.          addq.w   #1,d5
  202.          cmp.w    d3,d5
  203.          bhi.s    3$
  204. 2$:
  205.          bsr      Pixel
  206.          bsr      Break
  207.          bne.s    3$
  208.          subq.w   #1,d4
  209.          cmp.w    d6,d4
  210.          bhi.s    2$
  211.          bsr      Pixel
  212.  
  213.          addq.w   #1,d5
  214.          cmp.w    d3,d5
  215.          bls.s    1$
  216. 3$:
  217.          rts
  218.  
  219.  
  220. Crawling:
  221.          move.w   mb_y1(a4),d0
  222.          or.w     #%1100000000000000,d0   ; initial direction down
  223.          swap     d0
  224.          move.w   mb_x1(a4),d0
  225. 1$:
  226.          bsr.s    Crawl    ; return with Z clear if user issued break
  227.          bne.s    2$
  228.          bsr      f_pull
  229.          bne.s    1$       ; stack non-empty
  230.  
  231. 2$:
  232. * Clear stack ... important in case someone used ^C
  233.          bsr      f_clear
  234.          rts
  235.  
  236.  
  237. * "Crawl" is supposed to take care of the actual crawling.
  238. * When handed with a pixel & direction in d0, it will find
  239. * its way through the edge of area with same colour.
  240.  
  241. * In addition, it will push all pixels _not_ in that area
  242. * to stack.
  243.  
  244. * d0 format: ddyyyyyy yyyyyyyy xxxxxxxx xxxxxxxx
  245.  
  246. * direction bits:
  247. *     00
  248. *   01  10
  249. *     11
  250.  
  251.  
  252. Crawl:
  253.          push     d0-d7
  254.          move.w   d0,d4       ; x
  255.          swap     d0
  256.          move.w   d0,d5
  257.          and.w    #%0011111111111111,d5   ; y
  258.          move.w   d0,d3
  259.          and.w    #%1100000000000000,d3   ; d (high 2 bits)
  260.  
  261. * If all pixels are drawn, it seems to me this area has already been
  262. * crawled (if not so, there are other pixels in the stack...)
  263.          bsr      Test4
  264.          tst.w    d0
  265.          bpl.s    1$          ; All pixels next to me were drawn
  266.  
  267.          move.w   d4,d6    ; Save place & dir until come to same place again
  268.          move.w   d5,d7
  269.          move.w   d3,d2
  270.  
  271.          move.l   a5,a1
  272.          move.l   d4,d0
  273.          move.l   d5,d1
  274.          bsr      AreaMove    ; note: This is a subroutine, not library call
  275. 2$:
  276.          bsr.s    proceed
  277.          move.l   a5,a1
  278.          move.l   d4,d0
  279.          move.l   d5,d1
  280.          bsr      AreaDraw    ; and so is this
  281.          bsr      Break
  282.          bne.s    3$
  283.          cmp.w    d4,d6       ; Compare with initial place & direction
  284.          bne.s    2$
  285.          cmp.w    d5,d7
  286.          bne.s    2$
  287.          cmp.w    d3,d2
  288.          bne.s    2$
  289.  
  290.          move.l   a5,a1
  291.          move.l   d4,d0
  292.          move.l   d5,d1
  293.          Call     ReadPixel
  294.          move.l   a5,a1
  295.          Call     SetAPen     ; Just in case no pixels were drawn above.
  296.          move.l   a5,a1
  297.          bsr      AreaEnd     ; this
  298. 1$:      moveq    #0,d0
  299.          bra.s    4$
  300. 3$:
  301.          move.l   a5,a1
  302.          bsr      AreaEnd     ; This finishes any draw (subroutine)
  303.          moveq    #-1,d0
  304. 4$:
  305.          pull     d0-d7
  306.          rts                  ; This routine returns same as Break
  307.  
  308.  
  309. * Proceed: This routine finds edge of an area of uniform color.
  310. * d4/d5 = pixel, d3 = direction. Returns next pixel & direction
  311. * along the edge.
  312. * Lets give an example: let's assume current pixel is A. (a colour)
  313. * X is known to be != A. b and c are yet unknown.
  314. * Last direction was up.
  315. *
  316. * b c
  317. * A X
  318. *
  319. * check b
  320. * if (A != b) then
  321. *    direction = left
  322. *    push b
  323. * else
  324. *    check c
  325. *    if (A != c) then
  326. *       current pixel = b
  327. *       push c
  328. *    else
  329. *       direction = right
  330. *       current pixel = c
  331. *
  332.  
  333. proceed:
  334.          push     d0/d2/d6-d7
  335.          bsr      Pixel
  336.          move.w   d0,d2    ; color of current pixel
  337.  
  338.          move.w   d4,d6
  339.          move.w   d5,d7    ; save normal pixel so we can resume
  340.  
  341.          bsr.s    front
  342.          bsr      Pixel    ; check "b"
  343.          cmp.w    d0,d2
  344.          beq.s    1$
  345.  
  346. * Turn left.
  347.          tst.w    d3
  348.          bpl.s    2$
  349.          eor.w    #%0100000000000000,d3
  350. 2$:
  351.          add.w    #%0100000000000000,d3
  352.          bpl.s    3$
  353.          eor.w    #%0100000000000000,d3
  354. 3$:
  355. * Luckily, d3/d6/d7 now contain just right info for pushing.
  356.          tst.w    d0
  357.          bmi.s    7$       ; do not push if pixel was outside
  358.          tst.l    d0
  359.          bmi.s    7$       ; if pixel drawn, dont push it.
  360.          move.w   d5,d0
  361.          or.w     d3,d0
  362.          eor.w    #%1100000000000000,d0   ; opposite direction
  363.          swap     d0
  364.          move.w   d4,d0
  365.          bsr      f_push
  366. 7$:
  367.          move.w   d6,d4    ; resume pixel
  368.          move.w   d7,d5
  369.          bra.s    4$       ; DONE...
  370.  
  371. 1$:
  372.          move.w   d4,d6    ; save "forwarded" pixel so we can resume
  373.          move.w   d5,d7
  374.  
  375.          bsr.s    right
  376.          bsr      Pixel    ; check "c"
  377.          cmp.w    d0,d2
  378.          beq.s    5$
  379.  
  380. * Go forward
  381. * This is what I call luck: again the right components for pushing
  382.          tst.w    d0
  383.          bmi.s    8$       ; again, if outside, dont push.
  384.          tst.l    d0
  385.          bmi.s    8$       ; dont push drawn pixels either
  386.          move.w   d5,d0
  387.          or.w     d3,d0
  388.          eor.w    #%1100000000000000,d0
  389.          swap     d0
  390.          move.w   d4,d0
  391.          bsr      f_push
  392. 8$:
  393.          move.w   d6,d4    ; resumes pixel to what it was before 'bsr right'
  394.          move.w   d7,d5
  395.          bra.s    4$       ; DONE
  396.  
  397. 5$:
  398. * Turn to right
  399.          tst.w    d3
  400.          bpl.s    6$
  401.          eor.w    #%0100000000000000,d3
  402. 6$:
  403.          add.w    #%1100000000000000,d3
  404.          bpl.s    4$
  405.          eor.w    #%0100000000000000,d3
  406. 4$:
  407.          pull     d0/d2/d6-d7
  408.          rts
  409.  
  410. * move one pixel d3-wards.
  411. front:
  412.          tst.w    d3
  413.          bmi.s    1$
  414.          btst.w   #14,d3
  415.          bne.s    2$
  416.  
  417.          subq.w   #1,d5    ; 00
  418.          rts
  419. 2$:      subq.w   #1,d4    ; 01
  420.          rts
  421. 1$:
  422.          btst.w   #14,d3
  423.          bne.s    3$
  424.          addq.w   #1,d4    ; 10
  425.          rts
  426. 3$:      addq.w   #1,d5    ; 11
  427.          rts
  428.  
  429. * move one pixel to the right of direction d3.
  430. right:
  431.          tst.w    d3
  432.          bmi.s    1$
  433.          btst.w   #14,d3
  434.          bne.s    2$
  435.  
  436.          addq.w   #1,d4    ; 00
  437.          rts
  438. 2$:      subq.w   #1,d5    ; 01
  439.          rts
  440. 1$:
  441.          btst.w   #14,d3
  442.          bne.s    3$
  443.          addq.w   #1,d5    ; 10
  444.          rts
  445. 3$:      subq.w   #1,d4    ; 11
  446.          rts
  447.  
  448. * Test4 will test if all 4 pixels next to d4,d5 are drawn.
  449. * If so, a negative number will be returned in d0.
  450.  
  451. Test4:
  452.          move.l   d3,-(a7)
  453.          moveq    #0,d3
  454.  
  455.          subq.w   #1,d4
  456.          bsr.s    GetPixel
  457.          or.w     d0,d3       ; if any of these is negative ...
  458.  
  459.          addq.w   #2,d4
  460.          bsr.s    GetPixel
  461.          or.w     d0,d3       ; (meaning pixel was not drawn) ...
  462.          subq.w   #1,d4
  463.  
  464.          subq.w   #1,d5
  465.          bsr.s    GetPixel
  466.          or.w     d0,d3       ; -1 will get written all over d3
  467.  
  468.          addq.w   #2,d5
  469.          bsr.s    GetPixel
  470.          or.w     d0,d3
  471.          subq.w   #1,d5
  472.  
  473.          move.w   d3,d0
  474.          move.l   (a7)+,d3
  475.          rts
  476.  
  477. * This routine is also called with screen pixel in d4,d5.
  478. * (GfxBase = a6, RastPort = a5)
  479. * It will return pixel color in d0 (but NOT draw it)
  480. * If pixel is outside region, 0 will be returned.
  481. * If it is not drawn, -1 will be returned
  482.  
  483. GetPixel:
  484.          push     d1/a0-a1
  485.          moveq    #0,d0      ; d0 will be ready just in case any
  486.          cmp.w    mb_x1(a4),d4       ; of the conditions below is met
  487.          blo      1$
  488.          cmp.w    mb_x2(a4),d4
  489.          bhi      1$
  490.          cmp.w    mb_y1(a4),d5
  491.          blo      1$
  492.          cmp.w    mb_y2(a4),d5
  493.          bhi      1$
  494.  
  495.          move.l   a5,a1
  496.          move.l   d4,d0
  497.          move.l   d5,d1
  498.          Call     ReadPixel
  499.  
  500.          cmp.w    #CLEAR,d0
  501.          bne.s    1$
  502.  
  503.          moveq    #-1,d0
  504. 1$:
  505.          pull     d1/a0-a1
  506.          rts
  507.  
  508. * This routine is called with screen pixel in d4,d5.
  509. * (GfxBase = a6, RastPort = a5)
  510. * It will return pixel color in d0 and draw it if required.
  511. * If pixel is outside drawing region, -1 will be returned.
  512.  
  513. * (An undocumented feature: if pixel was drawn before this
  514. * routine was called, bit 31 will be set.)
  515.  
  516. * Another feature: pixels right of y-axis are returned
  517. * with bit 14 set (unless color == interior).
  518. * This will make separate areas on both sides of y-axis,
  519. * thus making it impossible to accidentally crawl around
  520. * the set (without noticing the interior at all)...
  521.  
  522. Pixel:
  523.          push     d1-d3/d6-d7/a0-a1
  524.          moveq    #0,d2
  525.          moveq    #-1,d0      ; d0 will be ready just in case any
  526.          cmp.w    mb_x1(a4),d4       ; of the conditions below is met
  527.          blo      pixel_outofbounds
  528.          cmp.w    mb_x2(a4),d4
  529.          bhi      pixel_outofbounds
  530.          cmp.w    mb_y1(a4),d5
  531.          blo      pixel_outofbounds
  532.          cmp.w    mb_y2(a4),d5
  533.          bhi      pixel_outofbounds
  534.  
  535.          move.w   d4,d3
  536.          move.w   d3,d6
  537.          mulu     mb_dx(a4),d6   ; dx.hi * d2
  538.          swap     d6             ; calmly ignore any overflow
  539.          move.w   #0,d6
  540.          mulu     2+mb_dx(a4),d3 ; dx.lo * d2
  541.          add.l    d3,d6
  542.          add.l    mb_x0(a4),d6
  543.          bmi.s    pixel_left
  544.          move.w   #%0100000000000000,d2
  545. pixel_left:
  546.          move.l   a5,a1
  547.          move.l   d4,d0
  548.          move.l   d5,d1
  549.          Call     ReadPixel
  550.  
  551.          bset     #31,d0
  552.          cmp.w    #CLEAR,d0
  553.          bne.s    pixel_outofbounds       ; not clear -> dont draw (OBS! d0 still has color)
  554.  
  555.          move.w   d5,d3
  556.          move.w   d3,d7
  557.          mulu     mb_dy(a4),d7
  558.          swap     d7
  559.          move.w   #0,d7
  560.          mulu     2+mb_dy(a4),d3
  561.          add.l    d3,d7
  562.          neg.l    d7       ; upwards increasing y coordinate
  563.          add.l    mb_y0(a4),d7
  564.  
  565.          move.w   mb_i(a4),d3
  566.          sub.w    #2,d3
  567.          bpl.s    positive_i
  568.          moveq    #0,d3
  569. positive_i
  570.          bsr.s    iterate
  571.  
  572.          cmp.w    #-1,d3
  573.          beq.s    pixel_inside_set
  574.  
  575.          and.l    #$ffff,d3
  576.          divu     mb_colors(a4),d3
  577.          swap     d3          ; remainder
  578.          add.w    #FIRST,d3
  579.          bra.s    pixel_outside_set
  580. pixel_inside_set:
  581.          move.w   #INTERIOR,d3
  582. pixel_outside_set:
  583.          move.w   d3,d0
  584.          move.l   a5,a1
  585.          Call     SetAPen
  586.          move.w   d4,d0
  587.          move.w   d5,d1
  588.          move.l   a5,a1
  589.          Call     WritePixel
  590.          move.w   d3,d0
  591.          bclr     #31,d0
  592. pixel_outofbounds:
  593.          cmp.w    #INTERIOR,d0
  594.          beq.s    pixel_dontOR
  595.          or.w     d2,d0
  596. pixel_dontOR:
  597.          pull     d1-d3/d6-d7/a0-a1
  598.          rts
  599.  
  600.  
  601. * This is the soul of the MandelBrot set.
  602. * We iterate a point represented by x=d6 and y=d7 in our
  603. * numerical system. d3 is set to maximum number of iterations,
  604. * and will be decremented until -1 reached unless point is
  605. * found to be outside the set.
  606. * We find the point to be outside when |z| > 2
  607.  
  608. * There are two versions of this routine, the former for 32 bits,
  609. * the latter for 16 bits.
  610.  
  611. * For those who don't know the formula of Mandelbrot set:
  612. *
  613. * Point c = x + yi {x,y E R, i^2 = -1} is said to be in the M. set if
  614. *          2
  615. * z    = z   + c    , z  = c,
  616. *  n+1    n            0
  617. *
  618. * doesnt -> oo as n -> oo. (This routine is only approximation, n -> d3)
  619.  
  620. * NEW: If client wants the user to see Julia sets, we handle them here.
  621. * The formula is the same, except: z0 is point and c is constant
  622. * throughout the set (it corresponds to a point in the M. set).
  623. * Hope you can complex math!
  624.  
  625.  
  626. iterate:
  627.          btst.b   #MBB_HIGH,mb_flags(a4)
  628.          beq      iterate16
  629.  
  630.          push     d0-d2/d4-d7
  631.  
  632. * z is initialized with c
  633.          move.l   d6,d4    ; x0 -> x
  634.          move.l   d7,d5    ; y0 -> y
  635.  
  636. * Julia stuff!
  637.          btst.b   #MBB_JULIA,mb_flags(a4)
  638.          beq.s    1$
  639.          move.l   mb_jx(a4),d6
  640.          move.l   mb_jy(a4),d7
  641. 1$:
  642. * real part
  643. * (And as an intermission, find out if x² + y² > 2² or |z| > 2.
  644. *  Because our multiplication routine does not monitor overflow,
  645. *  we check the arguments first.)
  646.  
  647.          move.l   d4,d0
  648.          bsr      square
  649.          move.l   d1,d2    ; x²
  650.          add.l    d0,d0    ; |x| > 2 ?  d0 was reserved
  651.          bvs.s    3$
  652.  
  653.          move.l   d5,d0
  654.          bsr      square     ; y²
  655.          add.l    d0,d0    ; |y| > 2 ?
  656.          bvs.s    3$
  657.          move.l   d1,d0    ; |z| > 2 ?
  658.          add.l    d2,d0
  659.          bvs.s    3$
  660.  
  661.          sub.l    d1,d2    ; x² - y²
  662.          add.l    d6,d2    ;           + x0
  663.          bvs.s    2$
  664.  
  665. * imaginary part
  666.          move.l   d4,d0
  667.          move.l   d5,d1
  668.          bsr      multi
  669.          add.l    d1,d1    ; 2 * x * y
  670.          bvs.s    2$
  671.          add.l    d7,d1                + y0
  672.          bvs.s    2$
  673.  
  674. * get ready for next iteration:
  675.          move.l   d1,d5
  676.          move.l   d2,d4
  677.  
  678.          dbf      d3,1$
  679.  
  680. * This is more or less a kludge!
  681. * Testing if |z| < 2 should be last in our loop, before
  682. * dbf. Because it was first, low-level iterations
  683. * would show some sharp edges on the interior.
  684. * We need one more |z| < 2 here.
  685. * (The kludge is not removed because of speed reasons.)
  686.  
  687.          move.l   d4,d0
  688.          bsr      square
  689.          move.l   d1,d2    ; x²
  690.          add.l    d0,d0    ; |x| > 2 ?
  691.          bvs.s    3$
  692.  
  693.          move.l   d5,d0
  694.          bsr      square   ; y²
  695.          add.l    d0,d0    ; |y| > 2 ?
  696.          bvs.s    3$
  697.          add.l    d2,d1    ; |z| > 2 ?
  698.          bvc.s    2$
  699.  
  700. * Thanks to our kludge, d3 has been decremented too much if
  701. * branched here. This would result in sharp edges on areas
  702. * outside the set as well. This time we have an easy fix:
  703.  
  704. 3$:      addq.w   #1,d3
  705. 2$:
  706.          pull     d0-d2/d4-d7
  707.          rts
  708.  
  709. * Square and multi are primary operations of signed fixed-point math.
  710. * Addition can be done using normal add.l instruction.
  711.  
  712. * This routine does not monitor overflowing.
  713. * Its arguments are expected to be within the range -2 .. 2.
  714.  
  715. * NOTE: all registers except d1 are preserved (including d0!)
  716.  
  717. * square : d0^2 -> d1
  718. * multi: d0*d1 -> d1
  719.  
  720. square:  move.l   d0,d1
  721. multi:   push     d2-d5
  722.  
  723. * We can only multiply with unsigned numbers.
  724. * So we calculate the sign of the result ahead of time.
  725.  
  726.          move.l   d0,d2
  727.          bpl.s    1$
  728.          neg.l    d0       ; fix d0
  729. 1$:
  730.          tst.l    d1
  731.          bpl.s    2$
  732.          neg.l    d1       ; fix d1
  733.          not.l    d2       ; but d2 has the sign of final result
  734. 2$:
  735.          move.w   d1,d3
  736.          mulu     d0,d3    ; d3 = d0lo * d1lo
  737.          moveq    #29,d5   ; strip this much bits out of d3
  738.          lsr.l    d5,d3    ; (introduces rounding error of max. 1 bits)
  739.  
  740.          swap     d0
  741.          move.w   d1,d4
  742.          mulu     d0,d4    ; d4 = d0h1 * d1lo
  743.          moveq    #13,d5   ; again some strip-tease
  744.          lsr.l    d5,d4    ; (and rounding errors)
  745.          add.l    d4,d3
  746.  
  747.          swap     d1
  748.          move.w   d1,d4
  749.          mulu     d0,d4    ; d4 = d0hi * d1hi
  750.          lsl.l    #3,d4    ; now shift the other way up
  751.          add.l    d4,d3    ; if (-2)*(-2) this will overflow .. who cares?
  752.  
  753.          swap     d0
  754.          mulu     d0,d1    ; d1 = d0lo * d1hi
  755.          lsr.l    d5,d1    ; shift .. d5 still has #13
  756.          add.l    d1,d3    ; all summed in d3
  757.  
  758.          tst.l    d2
  759.          bpl.s    3$       ; test & fix sign of product
  760.          neg.l    d3
  761. 3$:
  762.          move.l   d3,d1    ; result will be carried back in d1 (!)
  763.          pull     d2-d5
  764.          rts
  765.  
  766. * The same routine with .w and with no bsr's to multi/square.
  767. * Comments have been stripped out, only changed parts are recommented.
  768.  
  769. MULTI    macro
  770.             muls     d0,d1
  771.             asl.l    #3,d1
  772.             swap     d1
  773.          endm
  774. SQUARE   macro
  775.             move.w   d0,d1
  776.             muls     d0,d1
  777.             asl.l    #3,d1
  778.             swap     d1
  779.          endm
  780.  
  781. iterate16:
  782.          push     d0-d2/d4-d7
  783.          swap     d6          ; long -> word
  784.          swap     d7
  785.  
  786.          move.w   d6,d4
  787.          move.w   d7,d5
  788.  
  789.          btst.b   #MBB_JULIA,mb_flags(a4)
  790.          beq.s    1$
  791.          move.w   mb_jx(a4),d6   ; always remember: we want HIGH order bits
  792.          move.w   mb_jy(a4),d7
  793. 1$:
  794.          move.w   d4,d0
  795.          SQUARE
  796.          move.w   d1,d2
  797.          add.w    d0,d0
  798.          bvs.s    3$
  799.          move.w   d5,d0
  800.          SQUARE
  801.          add.w    d0,d0
  802.          bvs.s    3$
  803.          move.w   d1,d0
  804.          add.w    d2,d0
  805.          bvs.s    3$
  806.          sub.w    d1,d2
  807.          add.w    d6,d2
  808.          bvs.s    2$
  809.  
  810.          move.w   d4,d0
  811.          move.w   d5,d1
  812.          MULTI
  813.          add.w    d1,d1
  814.          bvs.s    2$
  815.          add.w    d7,d1
  816.          bvs.s    2$
  817.          move.w   d1,d5
  818.          move.w   d2,d4
  819.          dbf      d3,1$
  820.  
  821.          muls     d5,d5    ; This part is slightly different
  822.          muls     d4,d4
  823.          add.l    d5,d4
  824.          bvs.s    3$
  825.          add.l    d4,d4
  826.          add.l    d4,d4
  827.          bvs.s    3$
  828.          add.l    d4,d4
  829.          bvc.s    2$
  830.  
  831. 3$:      addq.w   #1,d3
  832. 2$:
  833.          pull     d0-d2/d4-d7
  834.          rts
  835.  
  836. * General-purpose break-condition check.
  837. * Current version uses call-back procedures, because I could
  838. * not make it general enough.
  839. Break:
  840.          push     a0/d0
  841.          move.l   mb_break(a4),d0
  842.          beq.s    1$
  843.          move.l   d0,a0
  844.          jsr      (a0)
  845. 1$:
  846.          pull     a0/d0
  847.          rts
  848.  
  849. * This group of routines essentially remove unnecessary points
  850. * and then call the real AreaXXXX routines.
  851. * Just because 6553 vectors seems to be a maximum.
  852.  
  853. * One could code this by allocating one bitplane, setting bits accorging
  854. * to what is fed to AreaDraw, filling it with one raw blitter operation
  855. * and then BlitPattern()ing it to RastPort. This would save allocating
  856. * TmpRas and AreaInfo structures.
  857.  
  858. * I am a perfectionist, but I aint a fool. This is a kludge, but so what?
  859.  
  860. AreaMove:
  861.          move.w   d0,startx
  862.          move.w   d1,starty
  863.          move.w   d0,prevx
  864.          move.w   d1,prevy
  865.          clr.b    dir
  866.          Call     AreaMove
  867.          rts
  868. AreaDraw:
  869.          push     d2/d3
  870.          bsr.s    which_dir
  871.  
  872.          tst.b    dir
  873.          bpl.s    skip_it
  874.  
  875. * Now! All bits are not on same line.
  876. * More accurately, since we test this on every point, the current
  877. * point is not on same line as the previous ones. So:
  878.          move.w   d0,d2
  879.          move.w   d1,d3
  880.          move.w   prevx,d0    ; The previous was the last in line
  881.          move.w   prevy,d1
  882.          move.w   d0,startx   ; starting point of the new line
  883.          move.w   d1,starty
  884.          Call     AreaDraw
  885.          move.w   d2,d0       ; one-stage pipelining
  886.          move.w   d3,d1
  887.          clr.b    dir
  888.          bsr.s    which_dir   ; right direction for these first 2 bits.
  889. skip_it:
  890.          move.w   d0,prevx
  891.          move.w   d1,prevy
  892. draw_done:
  893.          pull     d2/d3
  894.          rts
  895.  
  896. * This routine keeps track of our heading.
  897. * dir carries the direction from previous stages, all 8 directions
  898. * are mapped to numbers 1..4 (opposite directions get same number).
  899. * If directions is undetermined so far, dir is 0.
  900. * -1 means pixels are no more on same line (requires AreaDraw.)
  901. which_dir:
  902.          move.w   startx,d2
  903.          move.w   starty,d3
  904.          sub.w    d0,d2
  905.          sub.w    d1,d3
  906.  
  907.          tst.w    d2       ; normalize (make opposite dirs equal)
  908.          bpl.s    pos_1
  909.          neg.w    d2
  910.          neg.w    d3
  911. pos_1
  912.          tst.w    d3
  913.          bpl.s    pos_2
  914.          neg.w    d2
  915.          neg.w    d3
  916. pos_2
  917.  
  918.          tst.w    d2       ; now determine direction.
  919.          beq.s    dir_03
  920.          bpl.s    dir_12
  921. ;dir4
  922.          add.w    d2,d3
  923.          bne.s    dir_illegal ; not diagonal!
  924.          moveq    #4,d2
  925.          bra.s    dir_proc
  926. dir_12
  927.          tst.w    d3
  928.          beq.s    dir_1
  929. ;dir_2
  930.          cmp.w    d2,d3
  931.          bne.s    dir_illegal ; not diagonal!
  932.          moveq    #2,d2
  933.          bra.s    dir_proc
  934. dir_1
  935.          moveq    #1,d2
  936.          bra.s    dir_proc
  937. dir_03
  938.          tst.w    d3
  939.          beq.s    dir_0
  940. ;dir_3
  941.          moveq    #3,d2
  942.          bra.s    dir_proc
  943. dir_0
  944.          moveq    #0,d2
  945.          bra.s    dir_proc
  946. dir_illegal
  947.          moveq    #-1,d2
  948. dir_proc
  949.          move.b   dir,d3
  950.          beq.s    dir_valid
  951.          tst.b    d2
  952.          beq.s    dir_OK
  953.          cmp.b    d2,d3
  954.          beq.s    dir_OK
  955. ;neither valid!
  956.          move.b   #-1,dir
  957.          rts
  958. dir_valid
  959.          move.b   d2,dir
  960. dir_OK
  961.          rts
  962.  
  963.  
  964. AreaEnd:
  965.          move.l   a1,-(a7)
  966.          move.w   prevx,d0
  967.          move.w   prevy,d1
  968.          Call     AreaDraw    ; force dirty buffers out
  969.          move.l   (a7)+,a1
  970.          Call     AreaEnd
  971.          rts
  972.  
  973. * Needed by my Area routines
  974.  
  975. prevx       ds.w  1
  976. prevy       ds.w  1
  977. startx      ds.w  1
  978. starty      ds.w  1
  979. dir         ds.b  1
  980.  
  981. * perhaps this should be a section of its own.
  982.  
  983. GfxName     dc.b  'graphics.library',0
  984.  
  985.          end
  986.  
  987.