; 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
						; 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
