+;
+; Ullrich von Bassewitz, 2009-10-25
+;
+; Clips line coordinates to the screen coordinates and calls tgi_line
+;
+
+
+ .import umul16x16r32, udiv32by16r16
+ .import negax
+
+ .include "tgi-kernel.inc"
+ .include "zeropage.inc"
+
+ .macpack longbranch
+
+;----------------------------------------------------------------------------
+; Data
+
+.bss
+
+; Line coordinates. Must be set before calling tgi_clippedline
+tgi_clip_x1: .res 2
+tgi_clip_y1: .res 2
+tgi_clip_x2: .res 2
+tgi_clip_y2: .res 2
+
+tgi_clip_o1: .res 1
+tgi_clip_o2: .res 1
+
+tgi_clip_d: .res 1
+tgi_clip_dx: .res 2
+tgi_clip_dy: .res 2
+
+tgi_clip_sign: .res 1
+
+
+;----------------------------------------------------------------------------
+; outcode constants.
+
+CLIP_NONE = $00
+CLIP_LEFT = $01
+CLIP_RIGHT = $02
+CLIP_BOTTOM = $04
+CLIP_TOP = $08
+
+
+
+;----------------------------------------------------------------------------
+; Generate a Cohen Sutherland outcode
+;
+; void outcode ()
+; {
+; unsigned char o = 0;
+; if (Y < 0) {
+; o = CLIP_BOTTOM;
+; } else if (Y >= yres) {
+; o = CLIP_TOP;
+; }
+; if (X < 0) {
+; o |= CLIP_LEFT;
+; } else if (X >= xres) {
+; o |= CLIP_RIGHT;
+; }
+; return o;
+; }
+
+.code
+.proc outcode
+
+ lda #CLIP_NONE
+ sta tmp1
+
+; Check Y coordinate
+
+ lda tgi_clip_y1+1,y ; High byte of Y1
+ bmi L2 ; Jump if bottom clip
+
+ ldx tgi_clip_y1,y ; Low byte of Y1
+ cpx _tgi_yres
+ sbc _tgi_yres+1
+ bvs L1
+ eor #$80
+L1: bpl L4
+ lda #CLIP_TOP ; Top clipping necessary
+ bne L3
+L2: lda #CLIP_BOTTOM
+L3: sta tmp1 ; Save temp outcode
+
+
+; Check X coordinate
+
+L4: lda tgi_clip_x1+1,y ; High byte of X1
+ bmi L7 ; Jump if left clip
+
+ ldx tgi_clip_x1,y ; Low byte of X1
+ cpx _tgi_xres
+ sbc _tgi_xres+1
+ bvs L5
+ eor #$80
+L5: bmi L6
+
+; No right or left clipping necessary
+
+ lda tmp1
+ rts
+
+; Need right clipping
+
+L6: lda #CLIP_RIGHT
+ ora tmp1
+ rts
+
+; Need left clipping
+
+L7: lda #CLIP_LEFT
+ ora tmp1
+ rts
+
+.endproc
+
+
+;----------------------------------------------------------------------------
+; Calculate outcodes for both ends of the line
+;
+
+.code
+.proc outcode1
+
+ ldy #0
+ jsr outcode
+ sta tgi_clip_o1
+ rts
+
+.endproc
+
+.code
+.proc outcode2
+
+ ldy #(tgi_clip_y2 - tgi_clip_y1)
+ jsr outcode
+ sta tgi_clip_o2
+ rts
+
+.endproc
+
+
+;----------------------------------------------------------------------------
+; Negate tgi_clip_dxy
+;
+
+.code
+.proc negate
+
+ lda tgi_clip_dx,y
+ eor #$FF
+ clc
+ adc #1
+ sta tgi_clip_dx,y
+ lda tgi_clip_dx+1,y
+ eor #$FF
+ adc #$00
+ sta tgi_clip_dx+1,y
+ rts
+
+.endproc
+
+
+;----------------------------------------------------------------------------
+; Calculate the absolute values of dx and dy and store the combined sign in
+; tgi_clip_sign
+;
+
+.code
+.proc calcdeltas
+
+ lda tgi_clip_x2
+ sec
+ sbc tgi_clip_x1
+ sta tgi_clip_dx
+ lda tgi_clip_x2+1
+ sbc tgi_clip_x1+1
+ sta tgi_clip_dx+1
+ sta tgi_clip_sign
+ bpl @L1
+ ldy #0
+ jsr negate
+
+@L1: lda tgi_clip_y2
+ sec
+ sbc tgi_clip_y1
+ sta tgi_clip_dy
+ lda tgi_clip_y2+1
+ sbc tgi_clip_y1+1
+ sta tgi_clip_dy+1
+
+ eor tgi_clip_sign
+ sta tgi_clip_sign
+
+ bit tgi_clip_dy+1
+ bpl @L9
+
+ ldy #(tgi_clip_dy - tgi_clip_dx)
+ jmp negate
+
+@L9: rts
+
+.endproc
+
+
+;----------------------------------------------------------------------------
+; Helper routine. Generate the absolute value of y/a and calculate the sign
+; of the final result
+;
+
+.code
+.proc prepare_coord
+
+ tax ; Remember high byte
+ eor tgi_clip_sign
+ sta tmp1 ; Sign of result
+ tya
+ cpx #0 ; Check sign
+ bpl @L1
+ jsr negax
+@L1: sta ptr1
+ stx ptr1+1
+ rts
+
+.endproc
+
+
+;----------------------------------------------------------------------------
+; Helper routine. Move the value in eax to ptr1:ptr2
+;
+
+.code
+.proc move_intermediate_result
+
+ sta ptr1
+ stx ptr1+1
+ ldy sreg
+ sty ptr2
+ ldy sreg+1
+ sty ptr2+1
+ rts
+
+.endproc
+
+
+;----------------------------------------------------------------------------
+; Multiplicate value in y/a by dy, then divide by dx.
+;
+
+.code
+.proc muldiv_dydx
+
+; Generate the absolute value of y/a and calculate the sign of the final
+; result
+
+ jsr prepare_coord
+
+; All values are positive now (dx/dy have been made positive in calcdeltas)
+; and the sign of the final result is on tmp1, so we can use unsigned
+; operations and apply the final result later, after rounding.
+
+ lda tgi_clip_dy
+ ldx tgi_clip_dy+1 ; rhs
+ jsr umul16x16r32 ; Multiplicate
+
+; Move the result of the multiplication into ptr1:ptr2
+
+ jsr move_intermediate_result
+
+; Load divisor and divide
+
+ lda tgi_clip_dx
+ ldx tgi_clip_dx+1
+ jsr udiv32by16r16
+
+; Check the sign of the final result and negate it if nessary
+
+done: bit tmp1
+ jmi negax
+ rts
+
+.endproc
+
+
+
+;----------------------------------------------------------------------------
+; Multiplicate value in y/a by dx, then divide by dy.
+;
+
+.code
+.proc muldiv_dxdy
+
+; Generate the absolute value of y/a and calculate the sign of the final
+; result
+
+ jsr prepare_coord
+
+; All values are positive now (dx/dy have been made positive in calcdeltas)
+; and the sign of the final result is on tmp1, so we can use unsigned
+; operations and apply the final result later, after rounding.
+
+ lda tgi_clip_dx
+ ldx tgi_clip_dx+1 ; rhs
+ jsr umul16x16r32 ; Multiplicate
+
+; Move the result of the multiplication into ptr1:ptr2
+
+ jsr move_intermediate_result
+
+; Load divisor and divide
+
+ lda tgi_clip_dy
+ ldx tgi_clip_dy+1
+ jsr udiv32by16r16
+
+; Check the sign of the final result and negate it if nessary
+
+ jmp muldiv_dydx::done
+
+.endproc
+
+
+
+;----------------------------------------------------------------------------
+; Clip a line using Cohen Sutherland
+;
+
+.code
+.proc tgi_clippedline
+
+; Set a flag that we have no deltas calculated
+
+ lda #0
+ sta tgi_clip_d
+
+; Generate outcodes
+
+ jsr outcode1
+ jsr outcode2
+
+; if ((tgi_clip_o1 | tgi_clip_o2) == 0) {
+; tgi_line (x1, y1, x2, y2);
+; }
+
+Loop: lda tgi_clip_o1
+ ora tgi_clip_o2
+ bne L1
+
+; Copy the coordinates into ptr1-4 and draw the line
+
+ ldx #7
+L0: lda tgi_clip_x1,x
+ sta ptr1,x
+ dex
+ bpl L0
+ jmp tgi_line
+
+; if ((tgi_clip_o1 & tgi_clip_o2) != 0) reject;
+
+L1: lda tgi_clip_o1
+ and tgi_clip_o2
+ beq L2
+ rts ; Nothing to draw
+
+; We must clip. If we haven't already done so, calculate dx/dy.
+
+L2: lda tgi_clip_d ; Deltas alreay calculated?
+ bne HaveDeltas ; Jump if yes
+ inc tgi_clip_d
+ jsr calcdeltas
+
+; Check if X1/Y1 needs clipping
+
+HaveDeltas:
+ lda tgi_clip_o1
+ jeq L10
+
+; Need to clip X1/Y1
+
+ lsr a ; Check for CLIP_LEFT
+ bcc L3
+
+; tgi_clip_y1 += (0 - tgi_clip_x1) * tgi_clip_dy / tgi_clip_dx;
+; tgi_clip_x1 = 0;
+
+ lda #$00
+ tax
+ beq L4
+
+L3: lsr a ; Check for CLIP_RIGHT
+ bcc L5
+
+; tgi_clip_y1 += (tgi_xmax - tgi_clip_x1) * tgi_clip_dy / tgi_clip_dx;
+; tgi_clip_x1 = tgi_xmax;
+
+ lda _tgi_xmax
+ ldx _tgi_xmax+1
+
+L4: tay
+ sec
+ sbc tgi_clip_x1
+ sty tgi_clip_x1
+ tay
+ txa
+ sbc tgi_clip_x1+1
+ stx tgi_clip_x1+1
+
+ jsr muldiv_dydx
+
+ clc
+ adc tgi_clip_y1
+ sta tgi_clip_y1
+ txa
+ adc tgi_clip_y1+1
+ sta tgi_clip_y1+1
+
+;
+
+ lda tgi_clip_o1
+ lsr a
+ lsr a
+L5: lsr a ; Check for CLIP_BOTTOM
+ bcc L6
+
+; tgi_clip_x1 = (0 - tgi_clip_y1) * tgi_clip_dx / tgi_clip_dy;
+; tgi_clip_y1 = 0;
+
+ lda #$00
+ tax
+ beq L7
+
+L6: lsr a ; Check for CLIP_TOP
+ bcc L8
+
+; tgi_clip_x1 += (tgi_ymax - tgi_clip_y1) * tgi_clip_dx / tgi_clip_dy;
+; tgi_clip_y1 = ymax;
+
+ lda _tgi_ymax
+ ldx _tgi_ymax+1
+
+L7: tay
+ sec
+ sbc tgi_clip_y1
+ sty tgi_clip_y1
+ tay
+ txa
+ sbc tgi_clip_y1+1
+ stx tgi_clip_y1+1
+
+ jsr muldiv_dxdy
+
+ clc
+ adc tgi_clip_x1
+ sta tgi_clip_x1
+ txa
+ adc tgi_clip_x1+1
+ sta tgi_clip_x1+1
+
+; We need to recalculate outcode1 in this case
+
+L8: jsr outcode1
+
+; Check if X2/Y2 needs clipping
+
+L10: lda tgi_clip_o2
+ jeq Loop
+
+; Need to clip X2/Y2
+
+ lsr a ; Check for CLIP_LEFT
+ bcc L11
+
+; tgi_clip_y2 += (0 - tgi_clip_x2) * tgi_clip_dy / tgi_clip_dx;
+; tgi_clip_x2 = 0;
+
+ lda #$00
+ tax
+ beq L12
+
+L11: lsr a ; Check for CLIP_RIGHT
+ bcc L13
+
+; tgi_clip_y2 += (tgi_xmax - tgi_clip_x2) * tgi_clip_dy / tgi_clip_dx;
+; tgi_clip_x2 = tgi_xmax;
+
+ lda _tgi_xmax
+ ldx _tgi_xmax+1
+
+L12: tay
+ sec
+ sbc tgi_clip_x2
+ sty tgi_clip_x2
+ tay
+ txa
+ sbc tgi_clip_x2+1
+ stx tgi_clip_x2+1
+
+ jsr muldiv_dydx
+
+ clc
+ adc tgi_clip_y2
+ sta tgi_clip_y2
+ txa
+ adc tgi_clip_y2+1
+ sta tgi_clip_y2+1
+
+;
+
+ lda tgi_clip_o2
+ lsr a
+ lsr a
+L13: lsr a ; Check for CLIP_BOTTOM
+ bcc L14
+
+; tgi_clip_x2 += (0 - tgi_clip_y2) * tgi_clip_dx / tgi_clip_dy;
+; tgi_clip_y2 = 0;
+
+ lda #$00
+ tax
+ beq L15
+
+L14: lsr a ; Check for CLIP_TOP
+ bcc L16
+
+; tgi_clip_x2 += (tgi_ymax - tgi_clip_y2) * tgi_clip_dx / tgi_clip_dy;
+; tgi_clip_y2 = tgi_ymax;
+
+ lda _tgi_ymax
+ ldx _tgi_ymax+1
+
+L15: tay
+ sec
+ sbc tgi_clip_y2
+ sty tgi_clip_y2
+ tay
+ txa
+ sbc tgi_clip_y2+1
+ stx tgi_clip_y2+1
+
+ jsr muldiv_dxdy
+
+ clc
+ adc tgi_clip_x2
+ sta tgi_clip_x2
+ txa
+ adc tgi_clip_x2+1
+ sta tgi_clip_x2+1
+
+; We need to recalculate outcode2 in this case
+
+L16: jsr outcode2
+
+; Try again
+
+ jmp Loop
+
+.endproc
+
+
+
+