*      
* This is the filled polygon routine for the
* 3D graphics library.  It is based on the polygonamy
* routine, improved in some places and modified in others
* to save memory, but it ought to be WELL within a factor
* of two speed-wise.
*
* To use:
*   - Set up a list of points blah blah
*   - yak yak yak
* Points may lie in range yada yada yada
*
* Written over the 4th of July weekend.
*
* (Plus a few evenings :)
*
* SLJ 7/97
*
* v2 -- New variables, later will add multicolor mode
*       DY > 255 now allowed
*       Routines to plot points and lines
* SLJ 3/99
*

         REL
         DSK '@0:polyfill'

MULTLO1  = $B1            ;Pointers to multiplication
MULTLO2  = $B3            ;tables
MULTHI1  = $B5
MULTHI2  = $B7

BITMAP   = $B9            ;Pointer to base of bitmap (hi)
FILLPAT  = $BB            ;Pointer to fill pattern
PLISTXLO = $BD            ;POINTER to list of x coords (lo)
PLISTXHI = $BF
PLISTYLO = $C1            ;Pointer to list of y coords
PLISTYHI = $C3

TEMP1    = $55            ;Temporary var
TEMP2    = $56
LBITS    = $57            ;Fill pattern

NUMPTS   = $58
REMPTS   = $59            ;Remaining number of points
LINDEX   = $5A            ;Left point index
RINDEX   = $5B            ;Right point index
* LDY and RDY aren't used anywhere
* LDY = $5C ;Left dy
* RDY = $5D ;Right dy
YLEFT    = $5C            ;Remaining points left
YRIGHT   = $5E            ;remaining points right
LDXINT   = $60            ;Left dx, integer part
LDXREM   = $61            ;remainder
RDXINT   = $62
RDXREM   = $63
LEFTXLO  = $64            ;Left x coordinate
LEFTXHI  = $65
LEFTREM  = $66            ;remainder
RIGHTXLO = $67
RIGHTXHI = $68
RIGHTREM = $69
YCOUNT   = $6A            ;Counter = 0..7
YROW     = $6B            ;Current row
LCOL     = $6C            ;Column, left coordinate

BPOINT   = $6D            ;Pointer to bitmap
ROWHIGH  = $6F            ;Actual high byte of bitmap pointer

DIVXLO   = $70            ;Division: DIVX/DIVY
DIVXHI   = $71
DIVY     = $72
DIVTEMP  = MULTLO1        ;High byte of DY

LOG      = $CC00          ;Logarithm table
EXP      = $CD00          ;Exponential table
NEGEXP   = $CE00          ;e^-x
PQ       = $0200          ;Point index queue

*
* Short test routine
*

         DO 0
INIT     
         STY NUMPTS
         LDY #$C0
         STY MULTLO2+1
         INY
         STY MULTLO1+1
         LDY #$C3
         STY MULTHI2+1
         INY
         STY MULTHI1+1
         LDA #<TESTPAT
         STA FILLPAT
         LDA #>TESTPAT
         STA FILLPAT+1

         LDA BITMAP
         STA BPOINT+1
         LDA #00
         STA BPOINT
         LDX #32
         LDY #00
         TYA
:LOOP    STA (BPOINT),Y
         INY
         BNE :LOOP
         INC BPOINT+1
         DEX
         BNE :LOOP

         LDX NUMPTS
         JMP POLYFILL     ;Y=# of points

TESTPAT  HEX FFFFFFFFFFFFFFFF
         FIN

*
* Some tables
*

XCOLUMN                   ;xcolumn(x)=x/8
]BLAH    = 0
         LUP 32
         DFB ]BLAH,]BLAH,]BLAH,]BLAH
         DFB ]BLAH,]BLAH,]BLAH,]BLAH
]BLAH    = ]BLAH+1
         --^

* Below are EOR #$FF for merging into the bitmap, instead
* of just ORAing into the bitmap.

LBITP                     ;Left bit patterns
         LUP 32
* DFB $FF,$7F,$3F,$1F,$0F,$07,$03,$01
         DFB 00,$80,$C0,$E0,$F0,$F8,$FC,$FE
         --^
RBITP                     ;right bit patterns
         LUP 32
* DFB $80,$C0,$E0,$F0,$F8,$FC,$FE,$FF
         DFB $7F,$3F,$1F,$0F,$07,$03,$01,$00
         --^

*
* The fill routine.  It just blasts into the bitmap,
* with self-modifying code determining the entry and
* exit points.
*
FILLMAIN 
]BLAH    = 0              ;This assembles to
         LUP 32           ;LDY #00  STA (BPOINT),Y
         LDY #]BLAH       ;LDY #08  STA (BPOINT),Y
         STA (BPOINT),Y   ;...
]BLAH    = ]BLAH+8        ;LDY #248 STA (BPOINT),Y
         --^

         INC BPOINT+1     ;Advance pointer to column 32
COL32    LDY #00
         STA (BPOINT),Y
         LDY #08          ;33
         STA (BPOINT),Y
         LDY #16
         STA (BPOINT),Y
         LDY #24
         STA (BPOINT),Y
         LDY #32
         STA (BPOINT),Y
         LDY #40          ;37
         STA (BPOINT),Y
         LDY #48
         STA (BPOINT),Y
         LDY #56          ;Column 39
         STA (BPOINT),Y
FILLEND  RTS              ;64+256=320
FILL     
         JMP FILLMAIN     ;166 bytes

*
* Tables of high and low bitmap row offsets
*

LOBROW   DFB 00,64,128,192,00,64,128,192
         DFB 00,64,128,192,00,64,128,192
         DFB 00,64,128,192,00,64,128,192
         DFB 00
HIBROW   
         DFB 00,01,02,03,05,06,07,08
         DFB 10,11,12,13,15,16,17,18
         DFB 20,21,22,23,25,26,27,28
         DFB 30

COLTAB                    ;coltab(x)=x*8
         DFB 0,8,16,24,32,40,48,56,64,72,80
         DFB 88,96,104,112,120,128,136,144
         DFB 152,160,168,176,184,192,200
         DFB 208,216,224,232,240,248
         DFB 0,8,16,24,32,40,48,56

* Now on a page boundary!

*
* The jump table for the 3d library routines
*
* We should be at $8A00
*
CALCMAT  EXT
LOCROT   EXT
GLOBROT  EXT
ACCROTX  EXT
ACCROTY  EXT
ACCROTZ  EXT

         JMP CALCMAT
         JMP ACCROTX
         JMP ACCROTY
         JMP ACCROTZ
         JMP GLOBROT
         JMP LOCROT
         JMP POLYFILL
         JMP PLOT
         JMP DRAWLINE
VERSION  LDA #2
         RTS


*
* Table of entry point offsets into fill routine.
* The idea is to enter on an LDY
*
FILLENT  
         DFB 0,4,8,12,16,20,24,28,32,36,40 ;0-10
         DFB 44,48,52,56,60,64,68,72,76,80 ;11-20
         DFB 84,88,92,96,100,104,108,112   ;21-28
         DFB 116,120,124,128               ;29-32
         DFB 134          ;Skip over INC BPOINT+1
         DFB 138,142,146,150,154,158 ;34-39
         DFB 162          ;Remember that we use FILLENT+1
*
* Table of RTS points in fill routine.
* The idea is to rts after the NEXT LDY #xx so as to
* get the correct pointer to the rightmost column.
*
* Thus, these entries are just the above +2
*
FILLRTS  
         DFB 2,6,10,14,18,22,26,30,34,38,42 ;0-10
         DFB 46,50,54,58,62,66,70,74,78,82  ;11-20
         DFB 86,90,94,98,102,106,110,114,118,122,126
         DFB 132          ;Skip over INC
         DFB 136,140,144,148,152,156,160    ;33-39

         TXT 'Are you the king of the jews?'

*
* There are two types of lines: ones that move right and
* ones that move left.  They may have either positive
* or negative slope, for a total of four routines.
*
* Each line requires two parts: the initial computation
* of dx/dy, and the actual updating of the line.  When
* dy=0 the next set of points must be moved to.  The
* initial step is always dx/2, to make nice looking lines,
* but the initial column/bit pattern must be handled
* carefully, depending on the slope.
*
* The complementary routine jump address must be passed
* in, for negative changes in slope.
*

*
* Part 1: Compute dy
*
* Since the very last point must be plotted in a special
* way (else not be plotted at all), ]1=address of routine
* to handle last point.
*

DYLINEL  MAC              ;Line with left points
BEGIN    LDX LINDEX
L1       DEC REMPTS       ;Remaining lines to plot
         BPL L2           ;Zero is still plotted
         INC YLEFT
         LDA REMPTS
         CMP #$FF         ;Last point
         BCS ]1
EXIT     RTS
* JMP ALLDONE ;If we hit zero

L2       JSR CALCDYL

         BMI EXIT         ;(sometimes points get screwed up)
         ORA YLEFT
         BEQ L1           ;If DY=0 then skip to next point

         STX LINDEX
         <<<

DYLINER  MAC
         LDX RINDEX
L1       DEC REMPTS
         BPL L2
         LDA REMPTS       ;Might have to handle endpoint
         CMP #$FE
         BCS ]1
RRTS     RTS              ;We really should never get here

L3       LDX #00          ;Wrap around
L2       CPX NUMPTS
         BCS L3
         LDY PQ,X
         STY TEMP1
         LDA (PLISTYHI),Y
         PHA
         LDA (PLISTYLO),Y ;Start point
         INX
         SEC
         LDY PQ,X
         STY TEMP2
         SBC (PLISTYLO),Y ;Next point
         STA YRIGHT
         STA DIVY
         PLA
         SBC (PLISTYHI),Y
         STA YRIGHT+1
         STA DIVTEMP
         BMI RRTS         ;(sometimes points get screwed up)
         ORA DIVY
         BEQ L1           ;Advance if dy=0

         STX RINDEX
         <<<

*
* Part 2: Compute dx.  If dx has negative the expected
* sign then jump to the complementary routine.
*
* dx>0 means forwards point is to the right of the
* current point, and vice-versa.
*

LINELM   MAC              ;Left line, dx<0
         LDY TEMP1
         LDA (PLISTXHI),Y
         STA LEFTXHI
         LDA (PLISTXLO),Y ;Current point
         STA LEFTXLO
         LDY TEMP2
         SEC              ;Carry can be clear if jumped to
         SBC (PLISTXLO),Y ;Next point
         STA DIVXLO
         LDA LEFTXHI
         SBC (PLISTXHI),Y
         BPL CONT1        ;If dx>0 then jump out
         JMP ]1           ;Entry address

CONT1    STA DIVXHI
         JSR LINELM2
DONE     <<<              ;Now .X=low byte, .A=high byte
                          ;(of advanced point)
                          ;and carry=clear

LINELP   MAC              ;Left line, dx>0
         LDY TEMP2
         LDA (PLISTXHI),Y
         TAX
         LDA (PLISTXLO),Y ;Next point
         LDY TEMP1
         SEC              ;Carry can be clear if jumped to
         SBC (PLISTXLO),Y ;Current point
         STA DIVXLO
         TXA
         SBC (PLISTXHI),Y
         BPL CONT1        ;If dx<0 then jump out
         JMP ]1           ;Entry address

CONT1    STA DIVXHI
         LDA (PLISTXLO),Y ;Current point
         STA LEFTXLO
         LDA (PLISTXHI),Y
         STA LEFTXHI

         JSR LINELP2
DONE2    <<<              ;Now .X=low byte, .A=high byte
                          ;of current point
*
* Now do the same for the right lines
*

LINERP   MAC              ;Right line, dx>0
         LDY TEMP2
         LDA (PLISTXHI),Y
         TAX
         LDA (PLISTXLO),Y ;Next point
         LDY TEMP1
         SEC
         SBC (PLISTXLO),Y ;Current point
         STA DIVXLO
         TXA
         SBC (PLISTXHI),Y
         BPL CONT1        ;If dx<0 then jump out
         JMP ]1           ;Entry address

CONT1    STA DIVXHI
         LDA (PLISTXLO),Y ;Current point (DEY above)
         STA RIGHTXLO
         LDA (PLISTXHI),Y
         STA RIGHTXHI
         JSR LINERP2
DONE     <<<              ;Now .X=low byte, .A=high byte
                          ;carry=clear

LINERM   MAC              ;Right line, dx<0
         LDY TEMP1
         LDA (PLISTXHI),Y
         STA RIGHTXHI
         LDA (PLISTXLO),Y ;Current point
         STA RIGHTXLO
         LDY TEMP2
         SEC
         SBC (PLISTXLO),Y ;Next point
         STA DIVXLO
         LDA RIGHTXHI
         SBC (PLISTXHI),Y
         BPL CONT1        ;If dx<0 then jump out
         JMP ]1           ;Entry address

CONT1    STA DIVXHI
         JSR LINERM2
DONE     <<<              ;Now .X=low byte, .A=high byte

*
* Next, the parts which update the X coordinates
* for positive and negative dx (in real space, of
* course -- the stored value of dx is always positive)
*
* Parameters passed in are:
*   ]1 = lo byte x coord
*   ]2 = high byte x coord
*   ]3 = remainder
*   ]4 = dy (unused!)
*   ]5 = dx/dy, integer
*   ]6 = dx/dy, remainder
*

PUPDATE  MAC              ;dx>0
         LDA ]3
         CLC
         ADC ]6
         STA ]3
         LDA ]1           ;x=x+dx/dy
         ADC ]5
         TAX
         STA ]1
         BCC CONT
         INC ]2           ;High byte, x
         CLC
CONT     LDA ]2
         <<<              ;Carry is CLEAR on exit
                          ;.X = lo byte xcoord
                          ;.A = hi byte

MUPDATE  MAC              ;dx<0
         LDA ]3           ;remainder
         SEC
         SBC ]6
         STA ]3
         LDA ]1           ;x=x-dx/dy
         SBC ]5
         STA ]1
         TAX
         BCS CONT
         DEC ]2           ;high byte

CONT     LDA ]2
         CLC
         <<<              ;carry is clear
                          ;.X=x-coord low byte
                          ;.A=high byte

*
* Compute the columns and plot the endpoints
*
* .X contains the low byte of the x-coord
* .A was JUST loaded with the high byte
*
* Carry is assumed to be CLEAR
*

LCOLUMN  MAC
* LDA LEFTXHI
         BEQ CONT
         CMP #2
         BCC CONT1
         ASL
         BCS NEG

POS      LDA #$FF         ;Column flag
         STA LCOL
         BNE DONE

NEG      LDA #00          ;If negative, column=0
         STA LCOL
         STA LBITS        ;(will be EOR #$FF'ed)
         LDA #4           ;Start filling at column 1
         STA FILL+1
         BNE DONE

CONT1    CPX #64          ;Check X<320
         BCS POS
         LDA #32          ;Add 32 columns!
         INC BPOINT+1     ;and advance pointer
CONT     ADC XCOLUMN,X
         STA LCOL
         TAY

         LDA LBITP,X
         STA LBITS        ;For later use in RCOLUMN

         LDA FILLENT+1,Y  ;Get fill entry address
         STA FILL+1
DONE     <<<
                          ;By pushing off the plot until
                          ;later, things like shared
                          ;columns won't hose other things
                          ;on the screen.

*
* Note that RCOLUMN does the actual filling
*
* On entry, .X = lo byte x-coord, and .A was JUST loaded
*   with the high byte.
*
* Carry must be CLEAR.
*

RCOLUMN  MAC
* LDA RIGHTXHI
         BEQ CONT
         CMP #2
         BCC CONT1
         ASL
         BCS JDONE        ;Skip plot+fill if x<0

POS      LDX LCOL
         BMI JDONE        ;Skip if lcol>40!
         LDY YCOUNT       ;Plot the left edge
         LDA (FILLPAT),Y
         STA TEMP1
         LDY COLTAB,X     ;Get column index
         EOR (BPOINT),Y   ;Merge into the bitmap
         AND LBITS        ;bits EOR #$FF
         EOR TEMP1        ;voila!
         STA (BPOINT),Y
         LDA TEMP1
         JSR FILL         ;Just fill to last column
JDONE    JMP DONE

UNDER    LDY LCOL         ;It is possible that, due to
         BMI DONE         ;rounding, etc. the left column
         LDX #00          ;got ahead of the right column
                          ;x=0 will force load of $FF
SAME     LDA RBITP,X      ;If zero, they share column
         EOR LBITS
         STA LBITS
         LDY YCOUNT
         LDA (FILLPAT),Y
         STA TEMP1
         LDX LCOL
         LDY COLTAB,X
         EOR (BPOINT),Y   ;Merge
         AND LBITS
         EOR TEMP1
         STA (BPOINT),Y   ;and plot
         JMP DONE

CONT1    CPX #64          ;Check for X>319
         BCS POS
         LDA #32          ;Add 32 columns!
CONT     ADC XCOLUMN,X
         CMP LCOL         ;Make sure we want to fill
         BCC UNDER
         BEQ SAME

         LDY RBITP,X
         STY TEMP1
         LDX LCOL
         STA LCOL         ;Right column

         LDY YCOUNT       ;Plot the left edge
         LDA (FILLPAT),Y
         STA TEMP2
         LDY COLTAB,X     ;Get column index
         EOR (BPOINT),Y   ;Merge into bitmap
         AND LBITS
         EOR TEMP2
         STA (BPOINT),Y

         LDY LCOL         ;right column
         LDX FILLRTS,Y    ;fill terminator
         LDA #$60         ;RTS
         STA FILLMAIN,X
         LDA TEMP2        ;Fill pattern
         JSR FILL
         EOR (BPOINT),Y
         AND TEMP1        ;Right bits
         EOR TEMP2        ;Merge
         STA (BPOINT),Y
         LDA #$91         ;STA (),Y
         STA FILLMAIN,X   ;Fill leaves .X unchanged
                          ;And leaves .Y with correct offset
DONE     <<<

*
* Finally, we need to decrease the Y coordinate and
* update the pointer if necessary.
*
* Also, since BPOINT may have been altered, we need
* to restore the high byte.
*
* ]1 = loop address
*
UPYRS    MAC
         LDA ROWHIGH
         STA BPOINT+1
         DEC BPOINT
         DEC YCOUNT
         BMI FIXIT
         JMP ]1

FIXIT    LDA #7
         STA YCOUNT
         LDY YROW
         BEQ EXIT         ;If y<0 then exit
         DEY
         STY YROW
         ORA LOBROW,Y
         STA BPOINT
         LDA HIBROW,Y
         CLC
         ADC BITMAP
         STA BPOINT+1
         STA ROWHIGH
         JMP ]1
EXIT     RTS
         <<<
*
* Like the above, but for the y>=200 routine
*
BIGFIX   MAC
* LDA ROWHIGH
* STA BPOINT+1
* DEC BPOINT
         DEC YCOUNT
         BMI FIXIT
         JMP ]1

FIXIT    LDA #7
         STA YCOUNT
         DEC YROW
         LDY YROW
         ORA LOBROW,Y
         STA BPOINT
         LDA HIBROW,Y
         CLC
         ADC BITMAP
         STA BPOINT+1
         STA ROWHIGH
         CPY #24
         BEQ EXIT         ;If y<0 then exit
         JMP ]1
EXIT     <<<              ;Don't leave this hanging :)

*
* Yay, time to code!
*
* Load X with the number of points in the point list
*
POLYFILL 
         STX NUMPTS
         STX REMPTS

         LDA #00          ;First find the largest Y
         STA TEMP1
         STA TEMP2
         STA LINDEX
:LOOP    LDY PQ,X
         LDA (PLISTYLO),Y
         CMP TEMP1
         LDA (PLISTYHI),Y
         BMI :SKIP
         SBC TEMP2
         BCC :SKIP
         STX LINDEX
         LDA (PLISTYLO),Y
         STA TEMP1
         LDA (PLISTYHI),Y
         STA TEMP2
:SKIP    DEX
         BNE :LOOP

         LDX LINDEX
         BNE GOGOGO       ;Possible that all Y<0
RTSMP    RTS

GOGOGO   STX RINDEX
         LDY PQ,X
         LDA (PLISTXLO),Y
         STA LEFTXLO
         STA RIGHTXLO
         LDA (PLISTXHI),Y
         STA LEFTXHI
         STA RIGHTXHI

         LDA TEMP1
         LSR TEMP2        ;Compute initial row
         ROR
         LSR TEMP2
         ROR
         LSR TEMP2
         ROR
         STA YROW
         TAX

         LDA TEMP1        ;Initial bit pattern
         AND #$07
         STA YCOUNT
         ORA LOBROW,X     ;Initial bitmap address
         STA BPOINT
         LDA HIBROW,X
         CLC
         ADC BITMAP
         STA BPOINT+1
         STA ROWHIGH

*
* Hidden face check.  Since point order is invariant
* under rotations but reversed by reflections, all that
* is required is to make sure the points are in order,
* i.e. check the line slopes.
*

         >>> DYLINEL,RTSMP
CHECKMP  >>> LINELM,CHECKPP
         JSR CHKLCOL      ;To save some memory
         >>> LINERP,CHECKMM

         STA TEMP2        ;Possible to fail if dy=0

         LDA LEFTXLO
         SEC
         SBC RIGHTXLO
         STA DIVXLO       ;temp
         LDA LEFTXHI
         SBC RIGHTXHI
         STA DIVXHI

         LDA DIVXLO
         SEC
         SBC LDXINT
         BCS :C1
         DEC DIVXHI
         SEC
:C1      SBC RDXINT
         STA DIVXLO
         LDA DIVXHI
         SBC #00
         BMI :OK          ;Skip unless leftx <= rightx
         ORA DIVXLO
         BEQ :OK
:RTS1    RTS

:OK      LDY YROW
         CPY #25
         BCS :OOPS

         LDA TEMP2        ;Restore A=high byte x
         JMP RCOLMP       ;Jump into right column update

:OOPS    TAY              ;Set flags
         CLC
         JMP BCMP         ;Run calculation until Y<200

         TXT 'Crucify him!'

*
* Sigh... need to save some memory, so this is now here.
*
CHKLCOL  
         >>> LCOLUMN      ;Update column info
         >>> DYLINER,CHEXIT
         RTS
CHEXIT   PLA              ;REALLY exit.
         PLA
         RTS

CHECKPP  >>> LINELP,CHECKMP
         JSR CHKLCOL
         >>> LINERP,CHECKPM

         STA TEMP2

         LDA LEFTXLO
         SEC
         SBC RIGHTXLO
         STA DIVXLO       ;temp
         LDA LEFTXHI
         SBC RIGHTXHI
         STA DIVXHI

         LDA DIVXLO
         CLC
         ADC LDXINT
         BCC :C1
         INC DIVXHI
:C1      SEC
         SBC RDXINT
         STA DIVXLO
         LDA DIVXHI
         SBC #00
         BMI :OK          ;If >= then punt
         ORA DIVXLO
         BNE :RTS
         LDA LDXREM       ;If 0, then compare slopes
         SEC
         SBC RDXREM
         LDA LDXINT
         SBC RDXINT
         BCC :OK
:RTS     RTS

:OK      LDY YROW
         CPY #25
         BCS :OOPS

         LDA TEMP2        ;Restore A=high byte x
         JMP RCOLPP       ;Jump into right column update

:OOPS    LDA TEMP2        ;Set flags
         CLC
         JMP BCPP         ;Run calculation until Y<200


CHECKMM  >>> LINERM,CHECKMP

         STA TEMP2

         LDA LEFTXLO
         SEC
         SBC RIGHTXLO
         STA DIVXLO       ;temp
         LDA LEFTXHI
         SBC RIGHTXHI
         STA DIVXHI

         LDA DIVXLO
         SEC
         SBC LDXINT
         BCS :C1
         DEC DIVXHI
:C1      CLC
         ADC RDXINT
         STA DIVXLO
         LDA DIVXHI
         ADC #00
         BMI :OK          ;If >= then punt
         ORA DIVXLO
         BNE :TOAST
         LDA RDXREM
         SEC
         SBC LDXREM
         LDA RDXINT
         SBC LDXINT
         BCC :OK
:TOAST   RTS

:OK      LDY YROW
         CPY #25
         BCS :OOPS

         LDA TEMP2        ;Restore A=high byte x
         JMP RCOLMM       ;Jump into right column update

:OOPS    LDA TEMP2        ;Set flags
         CLC
         JMP BCMM         ;Run calculation until Y<200

CHECKPM  >>> LINERM,CHECKPM ;It's possible, if dy=0

         STA TEMP2        ;Remember that we've advanced

         LDA LEFTXLO
         SEC
         SBC RIGHTXLO
         STA DIVXLO       ;temp
         LDA LEFTXHI
         SBC RIGHTXHI
         STA DIVXHI

         LDA DIVXLO
         CLC
         ADC LDXINT
         BCC :C1
         INC DIVXHI
         CLC
:C1      ADC RDXINT
         LDA DIVXHI
         ADC #00
         BMI :OK          ;If >= then punt
         RTS

:OK      LDY YROW
         CPY #25
         BCS :OOPS

         LDA TEMP2        ;Restore A=high byte x
         JMP RCOLPM       ;Jump into right column update

:OOPS    LDA TEMP2        ;Set flags
         CLC
         JMP BCPM         ;Run calculation until Y<200

*
* And finally, the main routine!  (Yay :)
*

* Left lines minus, right lines positive

DYL1     DEC YLEFT+1
         BPL UL1
         >>> DYLINEL,ENDMP
LEFTMP   >>> LINELM,LEFTPP
         JMP LCOLMP
ENDMP    RTS              ;Actually, we don't want to get here!

LOOP1    DEC YLEFT
         BEQ DYL1
UL1      >>> MUPDATE,LEFTXLO;LEFTXHI;LEFTREM;TEMP1;LDXINT;LDXREM
LCOLMP   >>> LCOLUMN

         DEC YRIGHT
         BNE UR1
         DEC YRIGHT+1
         BPL UR1
         >>> DYLINER,ENDMP
RIGHTMP  >>> LINERP,RIGHTMM
         JMP RCOLMP
UR1      >>> PUPDATE,RIGHTXLO;RIGHTXHI;RIGHTREM;TEMP1;RDXINT;RDXREM
RCOLMP   >>> RCOLUMN
         >>> UPYRS,LOOP1

* Left positive, right positive
*
* Endpoint: left advances, right=current point

DYL2     DEC YLEFT+1
         BPL UL2
         >>> DYLINEL,UL2
LEFTPP   >>> LINELP,LEFTMP
         JMP LCOLPP

LOOP2    DEC YLEFT
         BEQ DYL2
UL2      >>> PUPDATE,LEFTXLO;LEFTXHI;LEFTREM;TEMP1;LDXINT;LDXREM
LCOLPP   >>> LCOLUMN

         DEC YRIGHT
         BNE UR2
         DEC YRIGHT+1
         BPL UR2
         >>> DYLINER,ENDPP
RIGHTPP  >>> LINERP,RIGHTPM
         JMP RCOLPP
ENDPP    LDY PQ,X
         LDA (PLISTXLO),Y ;Last point
         TAX
         LDA (PLISTXHI),Y
         CLC
         JMP RCOLPP

UR2      >>> PUPDATE,RIGHTXLO;RIGHTXHI;RIGHTREM;TEMP1;RDXINT;RDXREM
RCOLPP   >>> RCOLUMN
         >>> UPYRS,LOOP2

* Left positive, right negative
*
* End: left and right advance normally

DYL3     DEC YLEFT+1
         BPL UL3
         >>> DYLINEL,UL3
LEFTPM   >>> LINELP,LEFTMM
         JMP LCOLPM

LOOP3    DEC YLEFT
         BEQ DYL3
UL3      >>> PUPDATE,LEFTXLO;LEFTXHI;LEFTREM;TEMP1;LDXINT;LDXREM
LCOLPM   >>> LCOLUMN

         DEC YRIGHT
         BNE UR3
         DEC YRIGHT+1
         BPL UR3
         >>> DYLINER,UR3
RIGHTPM  >>> LINERM,RIGHTPP
         JMP RCOLPM
UR3      >>> MUPDATE,RIGHTXLO;RIGHTXHI;RIGHTREM;TEMP1;RDXINT;RDXREM
RCOLPM   >>> RCOLUMN
         >>> UPYRS,LOOP3

* Left negative, right negative
*
* End: left=current point, right advances

EXITMM                    ;X points to the last point
         INC YRIGHT       ;Sneaky!
         LDY PQ,X
         LDA (PLISTXLO),Y
         TAX
         LDA (PLISTXHI),Y
         CLC
         JMP LCOLMM

DYL4     DEC YLEFT+1
         BPL UL4
         >>> DYLINEL,EXITMM
LEFTMM   >>> LINELM,LEFTPM
         JMP LCOLMM

LOOP4    DEC YLEFT
         BEQ DYL4
UL4      >>> MUPDATE,LEFTXLO;LEFTXHI;LEFTREM;TEMP1;LDXINT;LDXREM
LCOLMM   >>> LCOLUMN
         DEC YRIGHT
         BNE UR4
         DEC YRIGHT+1
         BPL UR4
         >>> DYLINER,UR4
RIGHTMM  >>> LINERM,RIGHTMP
         JMP RCOLMM
UR4      >>> MUPDATE,RIGHTXLO;RIGHTXHI;RIGHTREM;TEMP1;RDXINT;RDXREM
RCOLMM   >>> RCOLUMN
         >>> UPYRS,LOOP4

         TXT 'He saved others; let him save himself.'


*
* Routines to advance but not plot
*

BIGLOOP1 DEC YLEFT
         BNE BL1
         DEC YLEFT+1
         BPL BL1
         >>> DYLINEL,BENDMP
BIGLMP   >>> LINELM,BIGLPP
         JMP BC1
BENDMP   RTS              ;Actually, we don't want to get here!
BL1      >>> MUPDATE,LEFTXLO;LEFTXHI;LEFTREM;TEMP1;LDXINT;LDXREM

BC1      DEC YRIGHT
         BNE BR1
         DEC YRIGHT+1
         BPL BR1
         >>> DYLINER,BENDMP
BIGRMP   >>> LINERP,BIGRMM
         JMP BC2
BR1      >>> PUPDATE,RIGHTXLO;RIGHTXHI;RIGHTREM;TEMP1;RDXINT;RDXREM
BCMP                      ;Entry point from hidden check
BC2      >>> BIGFIX,BIGLOOP1
         JMP LOOP1


BIGLOOP2 DEC YLEFT
         BNE BL2
         DEC YLEFT+1
         BPL BL2
         >>> DYLINEL,BL2
BIGLPP   >>> LINELP,BIGLMP
         JMP BC3
BL2      >>> PUPDATE,LEFTXLO;LEFTXHI;LEFTREM;TEMP1;LDXINT;LDXREM

BC3      DEC YRIGHT
         BNE BR2
         DEC YRIGHT+1
         BPL BR2
         >>> DYLINER,BENDPP
BIGRPP   >>> LINERP,BIGRPM
         JMP BC4
BENDPP   RTS

BR2      >>> PUPDATE,RIGHTXLO;RIGHTXHI;RIGHTREM;TEMP1;RDXINT;RDXREM
BCPP     
BC4      >>> BIGFIX,BIGLOOP2
         JMP LOOP2


BIGLOOP3 DEC YLEFT
         BNE BL3
         DEC YLEFT+1
         BPL BL3
         >>> DYLINEL,BENDPP
BIGLPM   >>> LINELP,BIGLMM
         JMP BC5
BL3      >>> PUPDATE,LEFTXLO;LEFTXHI;LEFTREM;TEMP1;LDXINT;LDXREM

BC5      DEC YRIGHT
         BNE BR3
         DEC YRIGHT+1
         BPL BR3
         >>> DYLINER,BR3
BIGRPM   >>> LINERM,BIGRPP
         JMP BC6
BR3      >>> MUPDATE,RIGHTXLO;RIGHTXHI;RIGHTREM;TEMP1;RDXINT;RDXREM
BCPM     
BC6      >>> BIGFIX,BIGLOOP3
         JMP LOOP3

BIGRTS   RTS

BIGLOOP4 DEC YLEFT
         BNE BL4
         DEC YLEFT+1
         BPL BL4
         >>> DYLINEL,BIGRTS
BIGLMM   >>> LINELM,BIGLPM
         JMP BC7
BL4      >>> MUPDATE,LEFTXLO;LEFTXHI;LEFTREM;TEMP1;LDXINT;LDXREM

BC7      DEC YRIGHT
         BNE BR4
         DEC YRIGHT+1
         BPL BR4
         >>> DYLINER,BR4
BIGRMM   >>> LINERM,BIGRMP
         JMP BC8
BR4      >>> MUPDATE,RIGHTXLO;RIGHTXHI;RIGHTREM;TEMP1;RDXINT;RDXREM
BCMM     
BC8      >>> BIGFIX,BIGLOOP4
         JMP LOOP4

         TXT 'Hail, king of the jews.'

*
* Called by DYLINEx macros.  It checks for negative dy.
* Yep, it's here to save on memory.
*

* v2 -- handled automatically now that dy>255 is allowed
         DO 0
FLOWCHK  
         LDY TEMP1
         LDA (PLISTYHI),Y
         LDY TEMP2
         SBC (PLISTYHI),Y
         RTS
         FIN

*
* Called by DYLINEx macros -- computes left DY etc.
*
* Right line will follow, if more mem is needed.
*

CDYL2    PLA
         LDX NUMPTS
CALCDYL  
         LDY PQ,X
         STY TEMP1
         LDA (PLISTYHI),Y
         PHA
         LDA (PLISTYLO),Y ;Remember, y -decreases-
         DEX
         BMI CDYL2        ;Loop around if needed
         LDY PQ,X
         STY TEMP2
         SEC
         SBC (PLISTYLO),Y
         STA YLEFT
         STA DIVY
         PLA
         SBC (PLISTYHI),Y
         STA YLEFT+1
         STA DIVTEMP
         RTS


*
* Called by LINELM macro
*
LINELM2  
         JSR DIVXY        ;Returns int,rem = .X,.A
         STX LDXINT

         CMP #$FF         ;if dy=1
         BNE :NOT1        ;then X=dx/2
         STX TEMP2        ;just two steps of dx/2
         LDA #00
         ROR              ;Get carry bit from dx/2
         STA LDXREM
         BCC :CONT2

:NOT1    STA LDXREM
         TXA              ;x=x-dxdy/2
         LSR
         STA TEMP2
:CONT2   LDA #$80
         STA LEFTREM      ;rem = 1/2
         LDA LEFTXLO
         SEC
         SBC TEMP2
         STA LEFTXLO
         TAX              ;X=advanced point
         LDA LEFTXHI
         SBC #00
         STA LEFTXHI
         CLC
         RTS
*
* Called by LINELP macro
*
LINELP2  
         JSR DIVXY        ;Returns int,rem = .X,.A
         STX LDXINT

         CMP #$FF         ;if dy=1
         BNE :NOT1        ;then A=dx/2
         LDA #00
         ROR              ;Get carry bit from dx/2
         STA LDXREM
         LDA #$80
         STA LEFTREM
         LDX LEFTXLO
         LDA LEFTXHI
         BCC :RTS         ;And start from current point

:NOT1    STA LDXREM
         LDA #$80
         STA LEFTREM      ;Initialize remainder to 1/2
         TXA              ;x=x-dxdy/2
         LSR
         STA TEMP2
         LDA LEFTXLO
         TAX              ;.X = current point
         SEC
         SBC TEMP2
         STA LEFTXLO
         LDA LEFTXHI
         BCS :DONE
         DEC LEFTXHI
         TAY              ;Set/clear Z flag
:DONE    CLC
:RTS     RTS

*
* Called by LINERP macro
*
LINERP2  
         JSR DIVXY        ;Returns int,rem = .X,.A
         STX RDXINT

         CMP #$FF
         BNE :NOT1
         LDA #00
         ROR
         STA RDXREM
         LDA #$80
         STA RIGHTREM
         TXA
         BCC :CONT2       ;Advance by dxdy/2

:NOT1    STA RDXREM
         LDA #$80         ;-1/2
         STA RIGHTREM
         TXA              ;x=x+dxdy/2
         LSR
         CLC
:CONT2   ADC RIGHTXLO
         STA RIGHTXLO
         TAX              ;Move right and use new point
         LDA RIGHTXHI
         ADC #00
         STA RIGHTXHI
         CLC              ;What if rightx equaled -1?
         RTS

*
* Called by LINERM
*
LINERM2  
         JSR DIVXY        ;Returns int,rem = .X,.A
         STX RDXINT

         CMP #$FF
         BNE :NOT1
         LDA #00
         ROR
         STA RDXREM
         LDA #$80
         STA RIGHTREM
         LDX RIGHTXLO     ;Advance from current point
         LDA RIGHTXHI
         BCC :DONE

:NOT1    STA RDXREM
         LDA #$80
         STA RIGHTREM     ;1/2
         TXA              ;x=x+dxdy/2
         LDX RIGHTXLO     ;.X = current point
         LSR
         CLC
         ADC RIGHTXLO
         STA RIGHTXLO
         LDA RIGHTXHI
         BCC :DONE
         INC RIGHTXHI
         TAY              ;Reset Z
         CLC
:DONE    RTS

*
* DIVXY
*
* This guy is the old predictor-corrector method,
* modified slightly to be more general.
*
* It computes 2X/2Y as the predictor, then corrects
* in both directions.  Both X and Y must be positive.
* X is assumed to be 9-bits and Y is 8-bits.
*
* Inputs: .X=dx/2 .A=dy/2, dx and dy are in xdivlo etc.
*
* Result is returned in X=integer part, A=remainder
*
* Remainder is now a fraction of 256.
*
* DX and DY can now be huge -- they are simply shifted
* right until DX < 512 and DY < 256.
*
DIVSHIFT 
         LSR DIVTEMP
         ROR DIVY
         LSR DIVXHI
         ROR DIVXLO
DIVXY    
         LDA DIVTEMP      ;Div by 2 if dy>255
         BNE DIVSHIFT

         LDA DIVXHI
         CMP #2
         BCS DIVSHIFT     ;Or if dx>511

         LSR              ;Compute dx/2
         LDA DIVXLO
         ROR
         TAX
         LDA DIVY
         LSR              ;dy/2
         BEQ :TWOSTEP     ;If Y=1 then handle special

         TAY
         LDA LOG,X        ;This is the division part
         SEC
         SBC LOG,Y
         BCC :NEG
         TAX
         LDA EXP,X
         TAX              ;Now we have int estimate

         STA MULTLO1
         STA MULTHI1
         EOR #$FF
         ADC #00          ;Carry is guaranteed set
         STA MULTLO2
         STA MULTHI2
         LDY DIVY
         LDA (MULTLO1),Y
         SEC
         SBC (MULTLO2),Y  ;a=N*dy
         STA TEMP1
         LDA (MULTHI1),Y
         SBC (MULTHI2),Y
         STA TEMP2

         LDA DIVXLO       ;R=dx-a
         SBC TEMP1        ;C set
         STA TEMP1
         LDA DIVXHI
         SBC TEMP2
         LDA TEMP1        ;A=remainder
         BCC :RNEG
                          ;If R>0 then assume R<255
                          ;(true unless dx>500 or so)

:RPOS    CMP DIVY         ;If R>=dy then
         BCC :DONE
:L1      INX              ;a=a+1
         SBC DIVY         ;R=R-dy
         CMP DIVY
         BCS :L1
:DONE                     ;Now X contains integer, A rem
                          ;y=dy
*
* Compute remainder as a fraction of 256, i.e.
* 256*r/dy
*
* Currently, a small error may occur for large r
* (cumulative error of 1-2 pixels, up to 4 in rare cases)
*
:FRACREM 
         STX TEMP1
         TAX
         BEQ :ZERO
         LDA LOG,X
         SEC
         SBC LOG,Y
         TAX
         LDA NEGEXP,X
:ZERO    LDX TEMP1
         RTS
                          ;And, if R<0 then assume
                          ;R>-255
:RNEG    DEX
         ADC DIVY
         BCC :RNEG
         JMP :FRACREM

:NEG     LDX #00          ;Since log is monotonic, and
         LDA DIVXLO       ;we /2, there is no chance
         LDY DIVY
         JMP :FRACREM     ;of undershooting.

:TWOSTEP LDA DIVXHI       ;If Y=1
         LSR
         LDA DIVXLO       ;then just two steps of size
         ROR              ;dx/2
         TAX
         LDA #255
         RTS

         TXT 'Jesus, remember me when you come '
         TXT 'into your kingdom.'

*-------------------------------
*
* Plot and line commands
*

X1       = $60
Y1       = $62
X2       = $64
Y2       = $66
DX       = $68
DY       = $6A
NPOINTS  = $6C
POINT    = $6E
XC       = $70
YC       = $71
BITP     = $72
TEMP     = $5F


BITS     DFB $80,$40,$20,$10,$08,$04,$02,$01

*
* PLOT -- plot a point!
*
* On entry: point in (x1,y1), bitmap in BITMAP
*
* On exit: plotted point!
*

PLOT     
         LDA X1+1
         BMI :EXIT
         LDX X1           ;Check range
         CPX #64
         SBC #01
         BCS :EXIT
         LDY Y1+1
         BNE :EXIT
         LDA Y1
         CMP #200
         BCS :EXIT
         LSR
         LSR
         LSR
         TAY
         JSR SETPOINT     ;Compute address

         LDA X1
         AND #$07
         TAX
         LDA BITS,X       ;Change in lib3d
         ORA (POINT),Y
         STA (POINT),Y
:EXIT    RTS

*
* Drawin' a line.  A fahn lahn.
*
* The same routines are used for on-screen and off-screen
* coordinates, but when off-screen, STA (BITMAP),Y is
* changed to BIT xx.  Slower, but much more compact.
*

LINESWAP 
         LDX #3
:L1      LDA X1,X         ;If not, swap P1 and P2
         LDY X2,X
         STA X2,X
         TYA
         STA X1,X
         DEX
         BPL :L1

DRAWLINE 
         LDA X2           ;Make sure x1<x2
         SEC
         SBC X1
         STA DX
         LDA X2+1
         SBC X1+1
         BMI LINESWAP

:CONT    STA DX+1

         LDX #$C8         ;INY
         LDA Y2           ;Calculate dy
         SEC
         SBC Y1
         STA DY
         LDA Y2+1
         SBC Y1+1
         BPL :DYPOS       ;Is y2>=y1?
         LDA Y1           ;Otherwise dy=y1-y2
         SEC
         SBC Y2
         STA DY
         LDA Y1+1
         SBC Y2+1
         LDX #$88         ;DEY

:DYPOS   STA DY+1
         STX YINCDEC
         STX XINCDEC

         LDA X1
         STA XC           ;Next compute column
         AND #$07         ;Bit pattern
         TAX
         LDA BITS,X
         STA BITP

         LDA X1+1
         LSR
         ROR XC
         LSR
         ROR XC
         LSR
         ROR XC           ;X column

:CONT1   LDA Y1           ;Now do the same for Y
         STA YC
         LDA Y1+1
         LSR
         ROR YC
         LSR
         ROR YC
         LSR
         ROR YC

         LDX DY
         CPX DX           ;Who's bigger: dy or dx?
         LDA DY+1
         SBC DX+1
         BCC STEPINX      ;If dx, then...

*
* Y-direction stepping:
*   y=y+1
*   a=a-dx
*   if a<0 then a=a+dy:x=x+1
*

STEPINY  
* LDX DY
         STX NPOINTS
         LDA DY+1
         JSR LINSETUP

         LDA DY           ;Init counter to dy/2
         LSR

*
* Main loop
*
YLOOP    STA TEMP

         LDA (POINT),Y    ;Otherwise plot
         ORA BITP
YSTA     STA (POINT),Y

YINCDEC  INY              ;Advance Y coordinate
         CPY #8
         BCC :CONT        ;No prob if Y=0..7
         JSR FIXY
:CONT    LDA TEMP         ;Restore A
         SEC
         SBC DX
         BCS YCONT

         ADC DY
         LSR BITP
         BCC YCONT
         JSR XCPLUS1

YCONT    DEX              ;X is counter
         BNE YLOOP
         DEC NPOINTS+1
         BEQ YLOOP
         RTS

*
* Big steps in X direction
*

STEPINX  
         LDX DX
         LDA DX+1
         JSR LINSETUP
         LDA DX           ;Init counter to dx/2
         LSR

XLOOP    STA TEMP

         LDA (POINT),Y    ;Otherwise plot
         ORA BITP
XSTA     STA (POINT),Y

         LDA TEMP
         SEC
         SBC DY
         BCS XLSR

         ADC DX
XINCDEC  INY
         CPY #8
         BCC XLSR
         STA TEMP
         JSR FIXY
         LDA TEMP

XLSR     LSR BITP
         BCC :C1
         JSR XCPLUS1
:C1      DEX              ;counter
         BNE XLOOP
         DEC NPOINTS+1
         BEQ XLOOP
         RTS

*
* Set up lines for drawing
*
LINSETUP 
         STX NPOINTS
         STA NPOINTS+1
                          ; JSR FIXDXDY ;If DX or DY > 256 then /2

                          ;If DX or DY > 256 then /2
:SHIFT   LDA DY+1
         ORA DX+1
         BEQ :C1
         LSR DY+1
         ROR DY
         LSR DX+1
         ROR DX
         JMP :SHIFT
:C1      

         JSR SETSTA
         LDX NPOINTS      ;Number of points
         INX              ;+1 for endpoint
         LDY YC
                          ; JMP SETPOINT
*
* SETPOINT -- Compute bitmap address
*
* On entry: x-cooord in X1
*           y-COLUMN in .Y
*
* On exit: .Y -> Y1 AND #$07 (ready for plotting)
*
* Both x and y are assumed to be in range!
*

SETPOINT 
                          ; LDY YCOL ;Compute column
         LDA X1
         AND #$F8
         CLC
         ADC LOBROW,Y     ;Base row
         STA POINT
         LDA X1+1
         ADC HIBROW,Y
         ADC BITMAP
         STA POINT+1
         LDA Y1
         AND #$07
         TAY
         RTS

*
* Update columns
*
XCPLUS1                   ;Increase x-column
         ROR BITP
         STA TEMP
         LDA POINT
         ADC #8
         STA POINT
         BCC :C1
         INC POINT+1
         CLC
:C1      
         LDA X1           ;X1 needed to compute
         ADC #8           ;bitmap address
         STA X1
         BCC :C2
         INC X1+1
:C2      
         INC XC
         BMI :LDA
         BNE :C3          ;Entered left side of screen,
         STY Y1
         LDY YC
         JSR SETSTA       ;so calculate pointers
         JSR SETPOINT
:C3      
         LDA XC
         CMP #40
         BCS PLAEXIT
:LDA     LDA TEMP
         RTS

*
* Subroutine to fix up pointer when Y decreases through
* zero or increases through 7.
*
FIXY     INY              ;Y=255 or Y=8
         BEQ :DECPTR
:INCPTR  
         INC YC
         BMI :LDY0
         BNE :C1
         JSR SETSTA       ;Just entered screen
:C1      LDY YC
         CPY #25
         BCS PLAEXIT      ;See-ya!
         JSR SETPOINT
:LDY0    LDY #00
         RTS
:DECPTR  
         DEC YC
         BMI PLAEXIT
         LDY YC
         CPY #24          ;just entered screen
         BNE :BCS
         JSR SETSTA
         CLC
:BCS     BCS :LDY
         JSR SETPOINT
:LDY     LDY #7
         RTS

PLAEXIT  PLA              ;Remove old return address
         PLA
         RTS

*
* If point is on-screen, then STA to bitmap.
* Otherwise, BIT
*
SETSTA   
         LDA XC
         CMP #40          ;pos and neg
         BCS :BIT
         LDA YC
         CMP #25
         BCS :BIT
         LDA #$91         ;STA (),Y
         DFB $2C
:BIT     LDA #$24         ;BIT zp
         STA YSTA
         STA XSTA
         RTS

         TXT 'Father, into thy hands I commit my spirit'
 |n
@à†¬„Ìøÿ¿)×@X£

