; REMARK: Due to self program update Draw/Erase, ABSDX, ABSDY, VY, etc., this program doesn't run on the ROM.
;         Needs to be on the RAM.

                        EXPORT  LINEDDAM_INPUTADDR
                        EXPORT  LINEDDAM

                        EXPORT  LINEDDA_VRAMADDR
                        EXPORT  LINEDDA_BYTE_PER_LINE
                        EXPORT  LINEDDA_OP
                        EXPORT  LINEDDA_X0
                        EXPORT  LINEDDA_Y0
                        EXPORT  LINEDDA_X1
                        EXPORT  LINEDDA_Y1
                        EXPORT  LINEDDA

                        ; Line drawing by DDA by Soji Yamakawa 10/26/2017

                        ; All input must come from an array.
                        ; LINEDDA_INPUT must have the address.

                        ; 2  PTR    VRAM ADDRESS
                        ; 1  UCHAR  BYTES PER LINE
                        ;           Should be 80 to use for FM-7 VRAM
                        ;           (640 pixels, 8 pixels per byte)
                        ; 1  UCHAR  Must be 1
                        ; 1  UCHAR  # LINES
                        ; 2  UINT   X0
                        ; 1  UINT   Y0
                        ; 2  UINT   X1
                        ; 1  UINT   Y1
                        ;     :
                        ;     :
                        ;     :

; Multiple lines
LINEDDAM_INPUTADDR      RZB     2

LINEDDAM                PSHS    A,B,X,Y,U

LINEDDAM_VRAMACCESSON   NOP     ; Will be made BITA $D409 for Sub-CPU
                        NOP
                        NOP

                        LDU     LINEDDAM_INPUTADDR,PCR
                        LDD     ,U++
                        STD     LINEDDA_VRAMADDR,PCR
                        LDD     ,U++
                        STD     LINEDDA_BYTE_PER_LINE,PCR

                        LDB     ,U+
                        BEQ     LINEDDAM_LOOP_END
LINEDDAM_LOOP
                        LDX     ,U++
                        STX     LINEDDA_X0,PCR
                        LDA     ,U+
                        STA     LINEDDA_Y0,PCR
                        LDX     ,U++
                        STX     LINEDDA_X1,PCR
                        LDA     ,U+
                        STA     LINEDDA_Y1,PCR
                        BSR     LINEDDA
                        DECB
                        BNE     LINEDDAM_LOOP
LINEDDAM_LOOP_END

LINEDDAM_VRAMACCESSOFF  NOP     ; Will be made STA $D409 for Sub-CPU
                        NOP     ; See INSTALL_LINEDDA_IN_SUBCPU function
                        NOP

                        PULS    A,B,X,Y,U,PC ; Save one RTS by pulling PC together
                        ; RTS



; Single line
; (X0,Y0), (X1,Y1) must be within the VRAM location.
; It doesn't clip.
; Input (X0,Y0),(X1,Y1) may be swapped after this function.
LINEDDA_VRAMADDR        RZB     2
LINEDDA_BYTE_PER_LINE   RZB     1
LINEDDA_OP              RZB     1   ; 1  UCHAR  1=Draw  
LINEDDA_X0              RZB     2
LINEDDA_Y0              RZB     1
LINEDDA_X1              RZB     2
LINEDDA_Y1              RZB     1

; EXEC &H578A
LINEDDA                 PSHS    A,B,X,Y,U

                        LDA     LINEDDA_OP,PCR
                        DECA
                        LBNE    LINEDDA_END ; Must be 1 for now.


                        ; Always X0<=X1
                        LDD     LINEDDA_X1,PCR
                        SUBD    LINEDDA_X0,PCR
                        STD     LINEDDA_ABSDX,PCR
                        BPL     LINEDDA_DX_MADE_POSI
                        COMA
                        COMB
                        ADDD    #1
                        STD     LINEDDA_ABSDX,PCR

                        ; Swap P0 and P1
                        LDD     LINEDDA_X0,PCR
                        LDX     LINEDDA_X1,PCR
                        STD     LINEDDA_X1,PCR
                        STX     LINEDDA_X0,PCR
                        LDA     LINEDDA_Y0,PCR
                        LDB     LINEDDA_Y1,PCR
                        STA     LINEDDA_Y1,PCR
                        STB     LINEDDA_Y0,PCR

LINEDDA_DX_MADE_POSI



                        ; X0,Y0 is fixed at this point.
                        ; Ready to calculate current address and bit.
                        LDA     LINEDDA_Y0,PCR
                        LDB     LINEDDA_BYTE_PER_LINE,PCR
                        MUL
                        ADDD    LINEDDA_VRAMADDR,PCR
                        STD     LINEDDA_CURADDR,PCR

                        LDB     LINEDDA_X0+1,PCR
                        LDA     #$80
                        ANDB    #7
                        BEQ     LINEDDA_X0Y0ADDR_SETBIT
LINEDDA_X0Y0ADDR_SHIFT  LSRA
                        DECB
                        BNE     LINEDDA_X0Y0ADDR_SHIFT
LINEDDA_X0Y0ADDR_SETBIT STA     LINEDDA_CURBIT,PCR

                        LDD     LINEDDA_X0,PCR
                        LSRA
                        RORB
                        LSRA
                        RORB
                        LSRA
                        RORB
                        ADDD    LINEDDA_CURADDR,PCR
                        STD     LINEDDA_CURADDR,PCR


                        ; Also ready to calculate VY
                        LDB     LINEDDA_BYTE_PER_LINE,PCR
                        LDA     LINEDDA_Y1,PCR
                        CMPA    LINEDDA_Y0,PCR
                        BHI     LINEDDA_SETVY ; Make sure to use unsigned comparison
                        NEGB
LINEDDA_SETVY           SEX
                        STD     LINEDDA_VY,PCR



                        LDB     LINEDDA_Y1,PCR
                        SUBB    LINEDDA_Y0,PCR
                        BHI     LINEDDA_DY_MADE_POSI
                        NEGB
LINEDDA_DY_MADE_POSI    CLRA
                        STD     LINEDDA_ABSDY,PCR

                        LDU     LINEDDA_CURADDR,PCR  ; U will be current VRAM address

                        ; D=ABSDY
                        CMPD    LINEDDA_ABSDX,PCR
                        ; Therefore, D=ABSDY at LINEDDA_DYGREATER
                        LBHI        LINEDDA_DYGREATER

                        ; At this line, ABSDX is either greater or equal than ABSDY
                        ; If ABSDX is zero, it means ABSDY is also zero.
                        LDD     LINEDDA_ABSDX,PCR
                        LBEQ    LINEDDA_ZERO



LINEDDA_DXGREATER       ; D is ABSDX
                        SUBD    LINEDDA_ABSDY,PCR
                        STD     LINEDDA_CURBALANCE,PCR
                        LDX     LINEDDA_ABSDX,PCR   ; Step count
                        LEAX    1,X

; Conservative method >>
;LINEDDA_DXG_LOOP
;                       LDA     LINEDDA_CURBIT,PCR
;                       ORA     ,U
;                       STA     ,U
;
;                       LDA     LINEDDA_CURBIT,PCR
;                       LSRA
;                       BCC     LINEDDA_DXG_XMOVED
;                       RORA
;                       LEAU    1,U
;LINEDDA_DXG_XMOVED     STA     LINEDDA_CURBIT,PCR
;
;
;                       LDD     LINEDDA_CURBALANCE,PCR
;                       SUBD    LINEDDA_ABSDY,PCR
;                       STD     LINEDDA_CURBALANCE,PCR
;                       BGE     LINEDDA_DXG_YMOVED
;                       ADDD    LINEDDA_ABSDX,PCR
;                       STD     LINEDDA_CURBALANCE,PCR
;
;                       LDD     LINEDDA_VY,PCR
;                       LEAU    D,U
;LINEDDA_DXG_YMOVED
;                       LEAX    -1,X ; Memo LEAU doesn't set zero flag.  LEAX does.
;                       BNE     LINEDDA_DXG_LOOP
; Conservative method <<


; Self Program Update >>
                        ; Updating the program >>
                        LDD     LINEDDA_ABSDX,PCR
                        STD     LINEDDA_DXG_ABSDX7+2,PCR
                        STD     LINEDDA_DXG_ABSDX6+2,PCR
                        STD     LINEDDA_DXG_ABSDX5+2,PCR
                        STD     LINEDDA_DXG_ABSDX4+2,PCR
                        STD     LINEDDA_DXG_ABSDX3+2,PCR
                        STD     LINEDDA_DXG_ABSDX2+2,PCR
                        STD     LINEDDA_DXG_ABSDX1+2,PCR
                        STD     LINEDDA_DXG_ABSDX0+2,PCR
                        STD     LINEDDA_DXG_ABSDX_+2,PCR
                        CLRA
                        CLRB
                        SUBD    LINEDDA_ABSDY,PCR
                        STD     LINEDDA_DXG_ABSDY7+2,PCR
                        STD     LINEDDA_DXG_ABSDY6+2,PCR
                        STD     LINEDDA_DXG_ABSDY5+2,PCR
                        STD     LINEDDA_DXG_ABSDY4+2,PCR
                        STD     LINEDDA_DXG_ABSDY3+2,PCR
                        STD     LINEDDA_DXG_ABSDY2+2,PCR
                        STD     LINEDDA_DXG_ABSDY1+2,PCR
                        STD     LINEDDA_DXG_ABSDY0+2,PCR
                        STD     LINEDDA_DXG_ABSDY_+2,PCR
                        LDD     LINEDDA_VY,PCR
                        ; In the expanded loop, B is free and used as VY
                        STD     LINEDDA_DXG_VY_+2,PCR

                        LDY     LINEDDA_CURBALANCE,PCR
                        LDB     LINEDDA_CURBIT,PCR
                        ; Updating the program <<

                        ; In self-programming loop,
                        ;   X is for balance, and
                        ;   Y is for the counter.
                        ;   It saves 1 cycle per pixel.
                        ;   5 bytes in total.
                        EXG     X,Y

                        CMPY    #8
                        LBLO    LINEDDA_DXG_LOOP_

                        CLRA    ; Needed for middle start
                        ASLB    ; CMPB  #$40 & BEQ
                        BMI     LINEDDA_DXG_ENTERLOOP6
                        ASLB    ; CMPB  #$20 & BEQ
                        BMI     LINEDDA_DXG_ENTERLOOP5
                        ASLB    ; CMPB  #$10 & BEQ
                        BMI     LINEDDA_DXG_ENTERLOOP4
                        ASLB    ; CMPB  #$08 & BEQ
                        BMI     LINEDDA_DXG_ENTERLOOP3
                        ASLB    ; CMPB  #$04 & BEQ
                        BMI     LINEDDA_DXG_ENTERLOOP2
                        ASLB    ; CMPB  #$02 & BEQ
                        BMI     LINEDDA_DXG_ENTERLOOP1
                        ASLB    ; CMPB  #$01 & BEQ
                        BMI     LINEDDA_DXG_ENTERLOOP0
                        BRA     LINEDDA_DXG_ENTERLOOP7

                        ; B is free in the expanded loop.  Use it as VY
LINEDDA_DXG_ENTERLOOP6  LDB     LINEDDA_VY+1,PCR
                        LEAY    1,Y ; Will skip one y-. To match up with y-=8.
                        BRA     LINEDDA_DXG_LOOP6
LINEDDA_DXG_ENTERLOOP5  LDB     LINEDDA_VY+1,PCR
                        LEAY    2,Y ; Will skip two y-. To match up with y-=8.
                        BRA     LINEDDA_DXG_LOOP5
LINEDDA_DXG_ENTERLOOP4  LDB     LINEDDA_VY+1,PCR
                        LEAY    3,Y ; Will skip three y-. To match up with y-=8.
                        BRA     LINEDDA_DXG_LOOP4
LINEDDA_DXG_ENTERLOOP3  LDB     LINEDDA_VY+1,PCR
                        LEAY    4,Y ; Will skip four y-. To match up with y-=8.
                        BRA     LINEDDA_DXG_LOOP3
LINEDDA_DXG_ENTERLOOP2  LDB     LINEDDA_VY+1,PCR
                        LEAY    5,Y ; Will skip five y-. To match up with y-=8.
                        LBRA    LINEDDA_DXG_LOOP2
LINEDDA_DXG_ENTERLOOP1  LDB     LINEDDA_VY+1,PCR
                        LEAY    6,Y ; Will skip six y-. To match up with y-=8.
                        LBRA    LINEDDA_DXG_LOOP1
LINEDDA_DXG_ENTERLOOP0  LDB     LINEDDA_VY+1,PCR
                        LEAY    7,Y ; Will skip seven y-. To match up with y-=8.
                        LBRA    LINEDDA_DXG_LOOP0

                        ; Let me say again, B is free in the expanded loop. Use it as VY
LINEDDA_DXG_ENTERLOOP7  LDB     LINEDDA_VY+1,PCR



LINEDDA_DXG_LOOP7       LDA     #$80
LINEDDA_DXG_ABSDY7      LEAX    $7FFF,X                 ; Using Self Program Update
                        CMPX    #0
                        BGE     LINEDDA_DXG_YMOVED7
LINEDDA_DXG_ABSDX7      LEAX    $7FFF,X                 ; Using Self Program Update

                        ORA     ,U
                        STA     ,U
                        CLRA
                        LEAU    B,U
LINEDDA_DXG_YMOVED7



LINEDDA_DXG_LOOP6       ORA     #$40
LINEDDA_DXG_ABSDY6      LEAX    $7FFF,X                 ; Using Self Program Update
                        CMPX    #0
                        BGE     LINEDDA_DXG_YMOVED6
LINEDDA_DXG_ABSDX6      LEAX    $7FFF,X                 ; Using Self Program Update

                        ORA     ,U
                        STA     ,U
                        CLRA
                        LEAU    B,U
LINEDDA_DXG_YMOVED6



LINEDDA_DXG_LOOP5       ORA     #$20
LINEDDA_DXG_ABSDY5      LEAX    $7FFF,X                 ; Using Self Program Update
                        CMPX    #0
                        BGE     LINEDDA_DXG_YMOVED5
LINEDDA_DXG_ABSDX5      LEAX    $7FFF,X                 ; Using Self Program Update

                        ORA     ,U
                        STA     ,U
                        CLRA
                        LEAU    B,U
LINEDDA_DXG_YMOVED5



LINEDDA_DXG_LOOP4       ORA     #$10
LINEDDA_DXG_ABSDY4      LEAX    $7FFF,X                 ; Using Self Program Update
                        CMPX    #0
                        BGE     LINEDDA_DXG_YMOVED4
LINEDDA_DXG_ABSDX4      LEAX    $7FFF,X                 ; Using Self Program Update

                        ORA     ,U
                        STA     ,U
                        CLRA
                        LEAU    B,U
LINEDDA_DXG_YMOVED4



LINEDDA_DXG_LOOP3       ORA     #$08
LINEDDA_DXG_ABSDY3      LEAX    $7FFF,X                 ; Using Self Program Update
                        CMPX    #0
                        BGE     LINEDDA_DXG_YMOVED3
LINEDDA_DXG_ABSDX3      LEAX    $7FFF,X                 ; Using Self Program Update

                        ORA     ,U
                        STA     ,U
                        CLRA
                        LEAU    B,U
LINEDDA_DXG_YMOVED3



LINEDDA_DXG_LOOP2       ORA     #$04
LINEDDA_DXG_ABSDY2      LEAX    $7FFF,X                 ; Using Self Program Update
                        CMPX    #0
                        BGE     LINEDDA_DXG_YMOVED2
LINEDDA_DXG_ABSDX2      LEAX    $7FFF,X                 ; Using Self Program Update

                        ORA     ,U
                        STA     ,U
                        CLRA
                        LEAU    B,U
LINEDDA_DXG_YMOVED2



LINEDDA_DXG_LOOP1       ORA     #$02
LINEDDA_DXG_ABSDY1      LEAX    $7FFF,X                 ; Using Self Program Update
                        CMPX    #0
                        BGE     LINEDDA_DXG_YMOVED1
LINEDDA_DXG_ABSDX1      LEAX    $7FFF,X                 ; Using Self Program Update

                        ORA     ,U
                        STA     ,U
                        CLRA
                        LEAU    B,U
LINEDDA_DXG_YMOVED1



LINEDDA_DXG_LOOP0       ORA     #$01
                        ORA     ,U
                        STA     ,U+

LINEDDA_DXG_ABSDY0      LEAX    $7FFF,X                 ; Using Self Program Update
                        CMPX    #0
                        BGE     LINEDDA_DXG_YMOVED0
LINEDDA_DXG_ABSDX0      LEAX    $7FFF,X                 ; Using Self Program Update

                        LEAU    B,U

LINEDDA_DXG_YMOVED0
                        LEAY    -8,Y ; Memo LEAU doesn't set zero flag.  LEAX does.
                        CMPY    #8
                        LBCC    LINEDDA_DXG_LOOP7

                        CMPY    #0
                        BEQ     LINEDDA_END

                        LDB     #$80

LINEDDA_DXG_LOOP_
                        TFR     B,A
                        ORA     ,U
                        STA     ,U

                        LSRB
                        BCC     LINEDDA_DXG_XMOVED_
                        RORB
                        LEAU    1,U
LINEDDA_DXG_XMOVED_

LINEDDA_DXG_ABSDY_      LEAX    $7FFF,X                 ; Using Self Program Update
                        CMPX    #0
                        BGE     LINEDDA_DXG_YMOVED_
LINEDDA_DXG_ABSDX_      LEAX    $7FFF,X                 ; Using Self Program Update

LINEDDA_DXG_VY_         LEAU    $7FFF,U                 ; Using Self Program Update
LINEDDA_DXG_YMOVED_
                        LEAY    -1,Y ; Memo LEAU doesn't set zero flag.  LEAX does.
                        BNE     LINEDDA_DXG_LOOP_

                        ; Remember X,Y usage is flipped in DY>DX case.
; Self Program Update <<


                        BRA     LINEDDA_END



LINEDDA_DYGREATER       ; See comments above.  D is already ABSDY
                        ; LDD       LINEDDA_ABSDY,PCR
                        SUBD    LINEDDA_ABSDX,PCR
                        STD     LINEDDA_CURBALANCE,PCR
                        LDX     LINEDDA_ABSDY,PCR   ; Step count
                        LEAX    1,X

; Conservative method >>
;LINEDDA_DYG_LOOP
;                       LDA     LINEDDA_CURBIT,PCR
;                       ORA     ,U
;                       STA     ,U
;                       LDD     LINEDDA_VY,PCR
;                       LEAU    D,U
;
;                       LDD     LINEDDA_CURBALANCE,PCR
;                       SUBD    LINEDDA_ABSDX,PCR
;                       STD     LINEDDA_CURBALANCE,PCR
;                       BGE     LINEDDA_DYG_NEXT
;                       ADDD    LINEDDA_ABSDY,PCR
;                       STD     LINEDDA_CURBALANCE,PCR
;
;                       LDA     LINEDDA_CURBIT,PCR
;                       LSRA
;                       BCC     LINEDDA_DYG_XMOVED
;                       RORA
;                       LEAU    1,U
;LINEDDA_DYG_XMOVED     STA     LINEDDA_CURBIT,PCR
;LINEDDA_DYG_NEXT
; Conservative method <<

; Using Self-Program Update >>
                        ; Updating the program >>
                        CLRA
                        CLRB
                        SUBD    LINEDDA_ABSDX,PCR
                        STD     LINEDDA_DYG_ABSDX+2,PCR
                        LDD     LINEDDA_ABSDY,PCR
                        STD     LINEDDA_DYG_ABSDY+2,PCR
                        LDD     LINEDDA_VY,PCR
                        STD     LINEDDA_DYG_VY+2,PCR

                        LDY     LINEDDA_CURBALANCE,PCR
                        LDB     LINEDDA_CURBIT,PCR
                        ; Updating the program <<

LINEDDA_DYG_LOOP
                        TFR     B,A
                        ORA     ,U
                        STA     ,U

LINEDDA_DYG_VY          LEAU    $7FFF,U                     ; Using Self-Program Update

LINEDDA_DYG_ABSDX       LEAY    $7FFF,Y                     ; Using Self-Program Update
                        CMPY    #0
                        BGE     LINEDDA_DYG_NEXT
LINEDDA_DYG_ABSDY       LEAY    $7FFF,Y                     ; Using Self-Program Update

                        LSRB
                        BCC     LINEDDA_DYG_NEXT
                        RORB
                        LEAU    1,U
LINEDDA_DYG_NEXT
; Using Self-Program Update >>


                        LEAX    -1,X ; Memo LEAU doesn't set zero flag.  LEAX does.
                        BNE     LINEDDA_DYG_LOOP
                        BRA     LINEDDA_END



LINEDDA_ZERO            LDA     LINEDDA_CURBIT,PCR
LINEDDA_ZERO_SETPIXEL   ORA     ,U
                        STA     ,U



LINEDDA_END
                        PULS    A,B,X,Y,U,PC ; Save 1 RTS by pulling PC together.
                        ; RTS

LINEDDA_ABSDX           RZB     2
LINEDDA_ABSDY           RZB     2
LINEDDA_CURADDR         RZB     2   ; Maybe just use X register?
LINEDDA_CURBALANCE      RZB     2
LINEDDA_VY              RZB     2
LINEDDA_CURBIT          RZB     1

LINEDDA_LASTADDR

; Better be after LINEDDA_LASTADDR because these bytes don't need to be copied to Sub-CPU RAM.
LINEDDA_BITA_D409       BITA    $D409   ; Sub-CPU VRAM Access Flag On
LINEDDA_STA_D409        STA     $D409   ; Sub-CPU VRAM Access Flag Clear



LINEDDA_LINEBUF_PTR     FDB     0   ; Input buffer address
                                    ; VRAM Address taken from LINEDDA_VRAMADDR 
                                    ; Bytes-Per-Line taken from LINEDDA_BYTE_PER_LINE
LINEDDA_LINECOUNT_PTR   FDB     0   ; Output Byte_ptr line-count address
LINEDDA_NEXTLINE_PTR    FDB     0   ; Output next line address

LINEDDA_INIT_LINEBUF    PSHS    A,B,U

                        LDU     LINEDDA_LINEBUF_PTR,PCR

                        LDD     LINEDDA_VRAMADDR,PCR
                        STD     ,U++

                        LDA     LINEDDA_BYTE_PER_LINE,PCR
                        STA     ,U+     ; 80 bytes per line

                        LDA     #1
                        STA     ,U+     ; Needs to be one.

                        STU     LINEDDA_LINECOUNT_PTR,PCR
                        CLR     ,U+     ; Number of lines

                        STU     LINEDDA_NEXTLINE_PTR,PCR

                        PULS    A,B,U,PC


                        ; LINEDDA_LINEBUF_PTR   Input 
                        ; LINEDDA_LINECOUNT_PTR Output Byte_ptr line-count address
                        ; LINEDDA_NEXTLINE_PTR  Output next line address
LINEDDA_GET_LINECOUNT_AND_NEXTLINE_PTR
                        PSHS    A,B,U

                        LDU     LINEDDA_LINEBUF_PTR,PCR
                        LEAU    4,U
                        STU     LINEDDA_LINECOUNT_PTR,PCR

                        CLRA
                        LDB     ,U+

                        LSLB
                        ROLA
                        LEAU    D,U ; +2N
                        LSLB
                        ROLA
                        LEAU    D,U ; +4N : Total +6N
                        STU     LINEDDA_NEXTLINE_PTR,PCR

                        PULS    A,B,U,PC
