
* This file: brot version 1.00

* (c) 1990 by H. Helminen

* This program will draw Mandelbrot set.
* It uses two advanced features:

* 1) Contour Crawling
*    When this feature is enabled, I will follow the edge
*    of each area of same colour and assume the interior
*    is of same color. This is true for ideal set, but because
*    of digital sampling, some errors may occur.
*    NOTE: This method is very sensitive to errors that may occur
*          if you change the code, e.g.
*    o   pixel colors must not be changed during draw
*    o   draw must not go out of screen bounds
*    o   if screen is not clear prior to drawing,
*        uncleared areas may be left untouched
*    o   draw must start at an edge of the picture

*    If disabled, every single pixel is calculated individually
*    resulting a bit better accuracy, a lot slower computing time.
*    When drawing disconnected Julia sets, crawling should be
*    disabled since it would produce inaccurate pictures.
*    Setting the AUTOCRAWL bit in mb_flags will do that for you.

* 2) 32-bit fixed point arithmetics
*    To speed this program up (standard A500 with 68000 and no
*    FP-processors) while keeping reasonable resolution, I used
*    signed 32-bit fixed point arithmetics, shifted left by 29
*    bits. Multiplication was somewhat cumbersome.
*    This format allows a resolution of about 1.9 x 10 ¯9.
*    When the resolution needed is much less accurate, one can use
*    only 16 bits in multiplication. Typically one would use
*    16 bits when delta > 0.0001 .. 0.0005.

*    Setting the AUTOPREC flag in mb_flags will do that for you.
*    However, in order to produce accurate pictures of Julia sets
*    that have complex structure near origo, you may need to
*    set the HIGH flag by yourself.

* Typical computing times for whole set with 100 iterations
* (5-bitplane 320 x 256 lores screen, no competing 68000 activity)

*        crawl  walk (=traditional pixel-by-pixel method)
* 16-bit  0:49   2:12
* 32-bit  1:29   5:33

* And, of course, the author:

* |_|_  _   _|    _  _  _  _  _   _ _  _  ,|_ _  _
* (_| )(/_ (_|(_|| )(_|(/_(_)| ) | ) )(_|/)(_(/_|
*                    _)
* Hannu Helminen so-dm@stekt.oulu.fi

* This proggie is quite useless by itself.
* You should (b)link it with pipe.o (by me, of course).
* Or you could use stack.o, which may be visually more attractive
* but uses more memory.
* This proggie is full of references to "stack", please ignore.

* You could use brot with your own programs. Just pass
* a valid MandelBrot structure in a0 with all fields initialized.
* (see mbrot.i) The RastPort's *must* have valid TmpRas and AreaInfo
* structures attached to it.

* Or, link it with gui.o (again by me)

* BUGS: MANY, including:

* - not very smart handling of AUTOCRAWL, since non-connected
*   set _could_ be crawled provided that before areafilling an
*   area, a few points in the interior were checked to make sure
*   that they are of same color.
* - AUTOPREC will fail on Julia sets with complex structre near origo.

   NOLIST
   INCLUDE  "macros.i"
   INCLUDE  "mbrot.i"

   XREF     _AbsExecBase

* If you insist... poof, you can remove the equates and use includes.
* I however prefer shorter compiles.


* These are used to maintain a stack-like dynamically allocated structure.
* (I like these functions ... only entry points are visible to this proggie)

   XREF     f_push
   XREF     f_pull
   XREF     f_clear

* Now make myself visible to others.

   XDEF     MandelBrot

   LIST



   SECTION  main,CODE

MandelBrot:
         push     d0-d7/a0-a6

* Save mbrot structure to a4. It can stay there all the time...
         move.l   a0,a4

         move.l   _AbsExecBase,a6
         lea      GfxName,a1
         moveq    #0,d0

         Call     OpenLibrary
         tst.l    d0
         beq      exit_mb
         move.l   d0,a6

* If precision is AUTOPREC, determine which precision we actually use.
         btst.b   #MBB_AUTOPREC,mb_flags(a4)
         beq.s    skip_low
         bset.b   #MBB_HIGH,mb_flags(a4)

         cmp.l    #DELTALIMIT,mb_dx(a4)
         blo.s    skip_low
         cmp.l    #DELTALIMIT,mb_dy(a4)
         blo.s    skip_low
         bclr.b   #MBB_HIGH,mb_flags(a4)

skip_low:

* If AUTOCRAWL is set, determine whether we should crawl or no.
         btst.b   #MBB_AUTOCRAWL,mb_flags(a4)
         beq.s    skip_crawl
         bset.b   #MBB_CRAWL,mb_flags(a4)

* Mandelbrot set is always connected, so it may be crawled
         btst.b   #MBB_JULIA,mb_flags(a4)
         beq.s    skip_crawl

* Julia set should be crawled if and only if corresponding
* c = jx + jy i belongs to Mandelbrot set.
* LS: like that "if and only if"?
         move.l   mb_jx(a4),d6
         move.l   mb_jy(a4),d7
         move.w   mb_i(a4),d3    ; use same number of iterations

         bsr      iterate
         tst.w    d3
         bmi.s    skip_crawl     ; inside the set!

         bclr.b   #MBB_CRAWL,mb_flags(a4)

skip_crawl:

* clear area prior to drawing.
         move.l   mb_RastPort(a4),a5
         move.l   a5,a1
         move.w   #CLEAR,d0
         Call     SetAPen
         move.l   a5,a1
         move.w   mb_x1(a4),d0
         move.w   mb_y1(a4),d1
         move.w   mb_x2(a4),d2
         move.w   mb_y2(a4),d3
         Call     RectFill

* start drawing...
         btst.b   #MBB_CRAWL,mb_flags(a4)
         beq.s    walk_mb

         bsr      Crawling
         bra.s    exit_mb
walk_mb:
         bsr      Walking
exit_mb:
         move.l   a6,a1
         move.l   _AbsExecBase,a6
         Call     CloseLibrary
         pull     d0-d7/a0-a6
no_gfx:
         rts

Walking:
         move.w   mb_x1(a4),d4
         move.w   mb_y1(a4),d5
         move.w   d4,d6
         move.w   mb_x2(a4),d7
         move.w   mb_y2(a4),d3
1$:

         bsr      Pixel
         bsr      Break
         bne.s    3$
         addq.w   #1,d4
         cmp.w    d7,d4
         blo.s    1$
         bsr      Pixel

         addq.w   #1,d5
         cmp.w    d3,d5
         bhi.s    3$
2$:
         bsr      Pixel
         bsr      Break
         bne.s    3$
         subq.w   #1,d4
         cmp.w    d6,d4
         bhi.s    2$
         bsr      Pixel

         addq.w   #1,d5
         cmp.w    d3,d5
         bls.s    1$
3$:
         rts


Crawling:
         move.w   mb_y1(a4),d0
         or.w     #%1100000000000000,d0   ; initial direction down
         swap     d0
         move.w   mb_x1(a4),d0
1$:
         bsr.s    Crawl    ; return with Z clear if user issued break
         bne.s    2$
         bsr      f_pull
         bne.s    1$       ; stack non-empty

2$:
* Clear stack ... important in case someone used ^C
         bsr      f_clear
         rts


* "Crawl" is supposed to take care of the actual crawling.
* When handed with a pixel & direction in d0, it will find
* its way through the edge of area with same colour.

* In addition, it will push all pixels _not_ in that area
* to stack.

* d0 format: ddyyyyyy yyyyyyyy xxxxxxxx xxxxxxxx

* direction bits:
*     00
*   01  10
*     11


Crawl:
         push     d0-d7
         move.w   d0,d4       ; x
         swap     d0
         move.w   d0,d5
         and.w    #%0011111111111111,d5   ; y
         move.w   d0,d3
         and.w    #%1100000000000000,d3   ; d (high 2 bits)

* If all pixels are drawn, it seems to me this area has already been
* crawled (if not so, there are other pixels in the stack...)
         bsr      Test4
         tst.w    d0
         bpl.s    1$          ; All pixels next to me were drawn

         move.w   d4,d6    ; Save place & dir until come to same place again
         move.w   d5,d7
         move.w   d3,d2

         move.l   a5,a1
         move.l   d4,d0
         move.l   d5,d1
         bsr      AreaMove    ; note: This is a subroutine, not library call
2$:
         bsr.s    proceed
         move.l   a5,a1
         move.l   d4,d0
         move.l   d5,d1
         bsr      AreaDraw    ; and so is this
         bsr      Break
         bne.s    3$
         cmp.w    d4,d6       ; Compare with initial place & direction
         bne.s    2$
         cmp.w    d5,d7
         bne.s    2$
         cmp.w    d3,d2
         bne.s    2$

         move.l   a5,a1
         move.l   d4,d0
         move.l   d5,d1
         Call     ReadPixel
         move.l   a5,a1
         Call     SetAPen     ; Just in case no pixels were drawn above.
         move.l   a5,a1
         bsr      AreaEnd     ; this
1$:      moveq    #0,d0
         bra.s    4$
3$:
         move.l   a5,a1
         bsr      AreaEnd     ; This finishes any draw (subroutine)
         moveq    #-1,d0
4$:
         pull     d0-d7
         rts                  ; This routine returns same as Break


* Proceed: This routine finds edge of an area of uniform color.
* d4/d5 = pixel, d3 = direction. Returns next pixel & direction
* along the edge.
* Lets give an example: let's assume current pixel is A. (a colour)
* X is known to be != A. b and c are yet unknown.
* Last direction was up.
*
* b c
* A X
*
* check b
* if (A != b) then
*    direction = left
*    push b
* else
*    check c
*    if (A != c) then
*       current pixel = b
*       push c
*    else
*       direction = right
*       current pixel = c
*

proceed:
         push     d0/d2/d6-d7
         bsr      Pixel
         move.w   d0,d2    ; color of current pixel

         move.w   d4,d6
         move.w   d5,d7    ; save normal pixel so we can resume

         bsr.s    front
         bsr      Pixel    ; check "b"
         cmp.w    d0,d2
         beq.s    1$

* Turn left.
         tst.w    d3
         bpl.s    2$
         eor.w    #%0100000000000000,d3
2$:
         add.w    #%0100000000000000,d3
         bpl.s    3$
         eor.w    #%0100000000000000,d3
3$:
* Luckily, d3/d6/d7 now contain just right info for pushing.
         tst.w    d0
         bmi.s    7$       ; do not push if pixel was outside
         tst.l    d0
         bmi.s    7$       ; if pixel drawn, dont push it.
         move.w   d5,d0
         or.w     d3,d0
         eor.w    #%1100000000000000,d0   ; opposite direction
         swap     d0
         move.w   d4,d0
         bsr      f_push
7$:
         move.w   d6,d4    ; resume pixel
         move.w   d7,d5
         bra.s    4$       ; DONE...

1$:
         move.w   d4,d6    ; save "forwarded" pixel so we can resume
         move.w   d5,d7

         bsr.s    right
         bsr      Pixel    ; check "c"
         cmp.w    d0,d2
         beq.s    5$

* Go forward
* This is what I call luck: again the right components for pushing
         tst.w    d0
         bmi.s    8$       ; again, if outside, dont push.
         tst.l    d0
         bmi.s    8$       ; dont push drawn pixels either
         move.w   d5,d0
         or.w     d3,d0
         eor.w    #%1100000000000000,d0
         swap     d0
         move.w   d4,d0
         bsr      f_push
8$:
         move.w   d6,d4    ; resumes pixel to what it was before 'bsr right'
         move.w   d7,d5
         bra.s    4$       ; DONE

5$:
* Turn to right
         tst.w    d3
         bpl.s    6$
         eor.w    #%0100000000000000,d3
6$:
         add.w    #%1100000000000000,d3
         bpl.s    4$
         eor.w    #%0100000000000000,d3
4$:
         pull     d0/d2/d6-d7
         rts

* move one pixel d3-wards.
front:
         tst.w    d3
         bmi.s    1$
         btst.w   #14,d3
         bne.s    2$

         subq.w   #1,d5    ; 00
         rts
2$:      subq.w   #1,d4    ; 01
         rts
1$:
         btst.w   #14,d3
         bne.s    3$
         addq.w   #1,d4    ; 10
         rts
3$:      addq.w   #1,d5    ; 11
         rts

* move one pixel to the right of direction d3.
right:
         tst.w    d3
         bmi.s    1$
         btst.w   #14,d3
         bne.s    2$

         addq.w   #1,d4    ; 00
         rts
2$:      subq.w   #1,d5    ; 01
         rts
1$:
         btst.w   #14,d3
         bne.s    3$
         addq.w   #1,d5    ; 10
         rts
3$:      subq.w   #1,d4    ; 11
         rts

* Test4 will test if all 4 pixels next to d4,d5 are drawn.
* If so, a negative number will be returned in d0.

Test4:
         move.l   d3,-(a7)
         moveq    #0,d3

         subq.w   #1,d4
         bsr.s    GetPixel
         or.w     d0,d3       ; if any of these is negative ...

         addq.w   #2,d4
         bsr.s    GetPixel
         or.w     d0,d3       ; (meaning pixel was not drawn) ...
         subq.w   #1,d4

         subq.w   #1,d5
         bsr.s    GetPixel
         or.w     d0,d3       ; -1 will get written all over d3

         addq.w   #2,d5
         bsr.s    GetPixel
         or.w     d0,d3
         subq.w   #1,d5

         move.w   d3,d0
         move.l   (a7)+,d3
         rts

* This routine is also called with screen pixel in d4,d5.
* (GfxBase = a6, RastPort = a5)
* It will return pixel color in d0 (but NOT draw it)
* If pixel is outside region, 0 will be returned.
* If it is not drawn, -1 will be returned

GetPixel:
         push     d1/a0-a1
         moveq    #0,d0      ; d0 will be ready just in case any
         cmp.w    mb_x1(a4),d4       ; of the conditions below is met
         blo      1$
         cmp.w    mb_x2(a4),d4
         bhi      1$
         cmp.w    mb_y1(a4),d5
         blo      1$
         cmp.w    mb_y2(a4),d5
         bhi      1$

         move.l   a5,a1
         move.l   d4,d0
         move.l   d5,d1
         Call     ReadPixel

         cmp.w    #CLEAR,d0
         bne.s    1$

         moveq    #-1,d0
1$:
         pull     d1/a0-a1
         rts

* This routine is called with screen pixel in d4,d5.
* (GfxBase = a6, RastPort = a5)
* It will return pixel color in d0 and draw it if required.
* If pixel is outside drawing region, -1 will be returned.

* (An undocumented feature: if pixel was drawn before this
* routine was called, bit 31 will be set.)

* Another feature: pixels right of y-axis are returned
* with bit 14 set (unless color == interior).
* This will make separate areas on both sides of y-axis,
* thus making it impossible to accidentally crawl around
* the set (without noticing the interior at all)...

Pixel:
         push     d1-d3/d6-d7/a0-a1
         moveq    #0,d2
         moveq    #-1,d0      ; d0 will be ready just in case any
         cmp.w    mb_x1(a4),d4       ; of the conditions below is met
         blo      pixel_outofbounds
         cmp.w    mb_x2(a4),d4
         bhi      pixel_outofbounds
         cmp.w    mb_y1(a4),d5
         blo      pixel_outofbounds
         cmp.w    mb_y2(a4),d5
         bhi      pixel_outofbounds

         move.w   d4,d3
         move.w   d3,d6
         mulu     mb_dx(a4),d6   ; dx.hi * d2
         swap     d6             ; calmly ignore any overflow
         move.w   #0,d6
         mulu     2+mb_dx(a4),d3 ; dx.lo * d2
         add.l    d3,d6
         add.l    mb_x0(a4),d6
         bmi.s    pixel_left
         move.w   #%0100000000000000,d2
pixel_left:
         move.l   a5,a1
         move.l   d4,d0
         move.l   d5,d1
         Call     ReadPixel

         bset     #31,d0
         cmp.w    #CLEAR,d0
         bne.s    pixel_outofbounds       ; not clear -> dont draw (OBS! d0 still has color)

         move.w   d5,d3
         move.w   d3,d7
         mulu     mb_dy(a4),d7
         swap     d7
         move.w   #0,d7
         mulu     2+mb_dy(a4),d3
         add.l    d3,d7
         neg.l    d7       ; upwards increasing y coordinate
         add.l    mb_y0(a4),d7

         move.w   mb_i(a4),d3
         sub.w    #2,d3
         bpl.s    positive_i
         moveq    #0,d3
positive_i
         bsr.s    iterate

         cmp.w    #-1,d3
         beq.s    pixel_inside_set

         and.l    #$ffff,d3
         divu     mb_colors(a4),d3
         swap     d3          ; remainder
         add.w    #FIRST,d3
         bra.s    pixel_outside_set
pixel_inside_set:
         move.w   #INTERIOR,d3
pixel_outside_set:
         move.w   d3,d0
         move.l   a5,a1
         Call     SetAPen
         move.w   d4,d0
         move.w   d5,d1
         move.l   a5,a1
         Call     WritePixel
         move.w   d3,d0
         bclr     #31,d0
pixel_outofbounds:
         cmp.w    #INTERIOR,d0
         beq.s    pixel_dontOR
         or.w     d2,d0
pixel_dontOR:
         pull     d1-d3/d6-d7/a0-a1
         rts


* This is the soul of the MandelBrot set.
* We iterate a point represented by x=d6 and y=d7 in our
* numerical system. d3 is set to maximum number of iterations,
* and will be decremented until -1 reached unless point is
* found to be outside the set.
* We find the point to be outside when |z| > 2

* There are two versions of this routine, the former for 32 bits,
* the latter for 16 bits.

* For those who don't know the formula of Mandelbrot set:
*
* Point c = x + yi {x,y E R, i^2 = -1} is said to be in the M. set if
*          2
* z    = z   + c    , z  = c,
*  n+1    n            0
*
* doesnt -> oo as n -> oo. (This routine is only approximation, n -> d3)

* NEW: If client wants the user to see Julia sets, we handle them here.
* The formula is the same, except: z0 is point and c is constant
* throughout the set (it corresponds to a point in the M. set).
* Hope you can complex math!


iterate:
         btst.b   #MBB_HIGH,mb_flags(a4)
         beq      iterate16

         push     d0-d2/d4-d7

* z is initialized with c
         move.l   d6,d4    ; x0 -> x
         move.l   d7,d5    ; y0 -> y

* Julia stuff!
         btst.b   #MBB_JULIA,mb_flags(a4)
         beq.s    1$
         move.l   mb_jx(a4),d6
         move.l   mb_jy(a4),d7
1$:
* real part
* (And as an intermission, find out if x² + y² > 2² or |z| > 2.
*  Because our multiplication routine does not monitor overflow,
*  we check the arguments first.)

         move.l   d4,d0
         bsr      square
         move.l   d1,d2    ; x²
         add.l    d0,d0    ; |x| > 2 ?  d0 was reserved
         bvs.s    3$

         move.l   d5,d0
         bsr      square     ; y²
         add.l    d0,d0    ; |y| > 2 ?
         bvs.s    3$
         move.l   d1,d0    ; |z| > 2 ?
         add.l    d2,d0
         bvs.s    3$

         sub.l    d1,d2    ; x² - y²
         add.l    d6,d2    ;           + x0
         bvs.s    2$

* imaginary part
         move.l   d4,d0
         move.l   d5,d1
         bsr      multi
         add.l    d1,d1    ; 2 * x * y
         bvs.s    2$
         add.l    d7,d1                + y0
         bvs.s    2$

* get ready for next iteration:
         move.l   d1,d5
         move.l   d2,d4

         dbf      d3,1$

* This is more or less a kludge!
* Testing if |z| < 2 should be last in our loop, before
* dbf. Because it was first, low-level iterations
* would show some sharp edges on the interior.
* We need one more |z| < 2 here.
* (The kludge is not removed because of speed reasons.)

         move.l   d4,d0
         bsr      square
         move.l   d1,d2    ; x²
         add.l    d0,d0    ; |x| > 2 ?
         bvs.s    3$

         move.l   d5,d0
         bsr      square   ; y²
         add.l    d0,d0    ; |y| > 2 ?
         bvs.s    3$
         add.l    d2,d1    ; |z| > 2 ?
         bvc.s    2$

* Thanks to our kludge, d3 has been decremented too much if
* branched here. This would result in sharp edges on areas
* outside the set as well. This time we have an easy fix:

3$:      addq.w   #1,d3
2$:
         pull     d0-d2/d4-d7
         rts

* Square and multi are primary operations of signed fixed-point math.
* Addition can be done using normal add.l instruction.

* This routine does not monitor overflowing.
* Its arguments are expected to be within the range -2 .. 2.

* NOTE: all registers except d1 are preserved (including d0!)

* square : d0^2 -> d1
* multi: d0*d1 -> d1

square:  move.l   d0,d1
multi:   push     d2-d5

* We can only multiply with unsigned numbers.
* So we calculate the sign of the result ahead of time.

         move.l   d0,d2
         bpl.s    1$
         neg.l    d0       ; fix d0
1$:
         tst.l    d1
         bpl.s    2$
         neg.l    d1       ; fix d1
         not.l    d2       ; but d2 has the sign of final result
2$:
         move.w   d1,d3
         mulu     d0,d3    ; d3 = d0lo * d1lo
         moveq    #29,d5   ; strip this much bits out of d3
         lsr.l    d5,d3    ; (introduces rounding error of max. 1 bits)

         swap     d0
         move.w   d1,d4
         mulu     d0,d4    ; d4 = d0h1 * d1lo
         moveq    #13,d5   ; again some strip-tease
         lsr.l    d5,d4    ; (and rounding errors)
         add.l    d4,d3

         swap     d1
         move.w   d1,d4
         mulu     d0,d4    ; d4 = d0hi * d1hi
         lsl.l    #3,d4    ; now shift the other way up
         add.l    d4,d3    ; if (-2)*(-2) this will overflow .. who cares?

         swap     d0
         mulu     d0,d1    ; d1 = d0lo * d1hi
         lsr.l    d5,d1    ; shift .. d5 still has #13
         add.l    d1,d3    ; all summed in d3

         tst.l    d2
         bpl.s    3$       ; test & fix sign of product
         neg.l    d3
3$:
         move.l   d3,d1    ; result will be carried back in d1 (!)
         pull     d2-d5
         rts

* The same routine with .w and with no bsr's to multi/square.
* Comments have been stripped out, only changed parts are recommented.

MULTI    macro
            muls     d0,d1
            asl.l    #3,d1
            swap     d1
         endm
SQUARE   macro
            move.w   d0,d1
            muls     d0,d1
            asl.l    #3,d1
            swap     d1
         endm

iterate16:
         push     d0-d2/d4-d7
         swap     d6          ; long -> word
         swap     d7

         move.w   d6,d4
         move.w   d7,d5

         btst.b   #MBB_JULIA,mb_flags(a4)
         beq.s    1$
         move.w   mb_jx(a4),d6   ; always remember: we want HIGH order bits
         move.w   mb_jy(a4),d7
1$:
         move.w   d4,d0
         SQUARE
         move.w   d1,d2
         add.w    d0,d0
         bvs.s    3$
         move.w   d5,d0
         SQUARE
         add.w    d0,d0
         bvs.s    3$
         move.w   d1,d0
         add.w    d2,d0
         bvs.s    3$
         sub.w    d1,d2
         add.w    d6,d2
         bvs.s    2$

         move.w   d4,d0
         move.w   d5,d1
         MULTI
         add.w    d1,d1
         bvs.s    2$
         add.w    d7,d1
         bvs.s    2$
         move.w   d1,d5
         move.w   d2,d4
         dbf      d3,1$

         muls     d5,d5    ; This part is slightly different
         muls     d4,d4
         add.l    d5,d4
         bvs.s    3$
         add.l    d4,d4
         add.l    d4,d4
         bvs.s    3$
         add.l    d4,d4
         bvc.s    2$

3$:      addq.w   #1,d3
2$:
         pull     d0-d2/d4-d7
         rts

* General-purpose break-condition check.
* Current version uses call-back procedures, because I could
* not make it general enough.
Break:
         push     a0/d0
         move.l   mb_break(a4),d0
         beq.s    1$
         move.l   d0,a0
         jsr      (a0)
1$:
         pull     a0/d0
         rts

* This group of routines essentially remove unnecessary points
* and then call the real AreaXXXX routines.
* Just because 6553 vectors seems to be a maximum.

* One could code this by allocating one bitplane, setting bits accorging
* to what is fed to AreaDraw, filling it with one raw blitter operation
* and then BlitPattern()ing it to RastPort. This would save allocating
* TmpRas and AreaInfo structures.

* I am a perfectionist, but I aint a fool. This is a kludge, but so what?

AreaMove:
         move.w   d0,startx
         move.w   d1,starty
         move.w   d0,prevx
         move.w   d1,prevy
         clr.b    dir
         Call     AreaMove
         rts
AreaDraw:
         push     d2/d3
         bsr.s    which_dir

         tst.b    dir
         bpl.s    skip_it

* Now! All bits are not on same line.
* More accurately, since we test this on every point, the current
* point is not on same line as the previous ones. So:
         move.w   d0,d2
         move.w   d1,d3
         move.w   prevx,d0    ; The previous was the last in line
         move.w   prevy,d1
         move.w   d0,startx   ; starting point of the new line
         move.w   d1,starty
         Call     AreaDraw
         move.w   d2,d0       ; one-stage pipelining
         move.w   d3,d1
         clr.b    dir
         bsr.s    which_dir   ; right direction for these first 2 bits.
skip_it:
         move.w   d0,prevx
         move.w   d1,prevy
draw_done:
         pull     d2/d3
         rts

* This routine keeps track of our heading.
* dir carries the direction from previous stages, all 8 directions
* are mapped to numbers 1..4 (opposite directions get same number).
* If directions is undetermined so far, dir is 0.
* -1 means pixels are no more on same line (requires AreaDraw.)
which_dir:
         move.w   startx,d2
         move.w   starty,d3
         sub.w    d0,d2
         sub.w    d1,d3

         tst.w    d2       ; normalize (make opposite dirs equal)
         bpl.s    pos_1
         neg.w    d2
         neg.w    d3
pos_1
         tst.w    d3
         bpl.s    pos_2
         neg.w    d2
         neg.w    d3
pos_2

         tst.w    d2       ; now determine direction.
         beq.s    dir_03
         bpl.s    dir_12
;dir4
         add.w    d2,d3
         bne.s    dir_illegal ; not diagonal!
         moveq    #4,d2
         bra.s    dir_proc
dir_12
         tst.w    d3
         beq.s    dir_1
;dir_2
         cmp.w    d2,d3
         bne.s    dir_illegal ; not diagonal!
         moveq    #2,d2
         bra.s    dir_proc
dir_1
         moveq    #1,d2
         bra.s    dir_proc
dir_03
         tst.w    d3
         beq.s    dir_0
;dir_3
         moveq    #3,d2
         bra.s    dir_proc
dir_0
         moveq    #0,d2
         bra.s    dir_proc
dir_illegal
         moveq    #-1,d2
dir_proc
         move.b   dir,d3
         beq.s    dir_valid
         tst.b    d2
         beq.s    dir_OK
         cmp.b    d2,d3
         beq.s    dir_OK
;neither valid!
         move.b   #-1,dir
         rts
dir_valid
         move.b   d2,dir
dir_OK
         rts


AreaEnd:
         move.l   a1,-(a7)
         move.w   prevx,d0
         move.w   prevy,d1
         Call     AreaDraw    ; force dirty buffers out
         move.l   (a7)+,a1
         Call     AreaEnd
         rts

* Needed by my Area routines

prevx       ds.w  1
prevy       ds.w  1
startx      ds.w  1
starty      ds.w  1
dir         ds.b  1

* perhaps this should be a section of its own.

GfxName     dc.b  'graphics.library',0

         end

