; Copyright 1996 Acorn Computers Ltd
;
; Licensed under the Apache License, Version 2.0 (the "License");
; you may not use this file except in compliance with the License.
; You may obtain a copy of the License at
;
;     http://www.apache.org/licenses/LICENSE-2.0
;
; Unless required by applicable law or agreed to in writing, software
; distributed under the License is distributed on an "AS IS" BASIS,
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
; See the License for the specific language governing permissions and
; limitations under the License.
;
; > $.Source.VduGrafB
;
; ARTHUR OPERATING SYSTEM - Vdu Drivers
; =======================
;
; Vdu driver code - Graphics circle outline, fill, arc, sector & segment
;
; Author R C Manby
; Date   5.9.86
;

; *****************************************************************************
;
;       CircleOutline - Circle outline
;
;       External routine
;
; in:   ICursor is the centre of the circle
;       NewPt is a point on the circumference
;
; out:  R0-R11 corrupt
;

CircleOutline ROUT
        Push    R14
        ADD     R11, WsPtr, #GCsIX              ; R0 := CentreX; R1 := CentreY
        LDMIA   R11, {R0-R3}                    ; R2 := NewPtX; R3 := NewPtY
        BL      GenCircleParm                   ; set up parm. block in R0-R7
        ADD     R9, WsPtr, #CircleBlk           ; R9 -> saved R0
        ADD     R11, R9, #5*4                   ; R11 -> saved R5
        STMIA   R11, {R5-R7}                    ; save R5-R7 once and for all
10
        STMIA   R9, {R0-R4}                     ; save R0-R4 before plotting pt

        SUB     R10, R5, R0                     ; upper left point
        ADD     R0, R5, R0                      ; upper right point
        ADD     R1, R6, R1
        BL      PlotPoint                       ; do upper right point

        CMP     R10, R0
        MOVNE   R0, R10                         ; do upper left point
        BLNE    PlotPoint                       ; unless same as upper right

        LDMIA   R9, {R0,R1}                     ; reload X, Y
        CMP     R1, #0                          ; if yPnt=0, skip lower
        BEQ     %FT20                           ; pixel pair

        LDMIA   R11, {R5,R6}                    ; reload CentreX, CentreY

        SUB     R10, R5, R0                     ; lower left point
        ADD     R0, R5, R0                      ; lower right point
        SUB     R1, R6, R1
        BL      PlotPoint

        CMP     R10, R0
        MOVNE   R0, R10                         ; do lower left point
        BLNE    PlotPoint                       ; unless same as lower right

20
        LDMIA   R9, {R0-R7}                     ; reload the parameter block
        TEQ     R0, #0                          ; if xPnt=0
        Pull    PC,EQ                           ; then finish
        BL      AdvCircleParm                   ; else step to next point
        B       %BT10                           ; and go round again

; *****************************************************************************
;
;       CircleFill - Circular area fill
;
;       External routine
;
; in:   ICursor is the centre of the circle
;       NewPt is a point on the circumference
;
; out:  R0-R11 corrupt
;

CircleFill ROUT
        Push    R14
        ADD     R11, WsPtr, #GCsIX              ; centre is ICursor
        LDMIA   R11, {R0-R3}                    ; point is NewPt
        BL      GenCircleParm                   ; set up parameter block
        ADD     R11, WsPtr, #CircleBlk          ; in R0-R6
10
        STMIA   R11, {R0-R7}                    ; save prior to line drawing
        ADD     R2, R5, R0                      ; RightX = CentreX+xPnt
        SUB     R11, R6, R1                     ; LowerY = CentreY-yPnt

        SUB     R0, R5, R0                      ; LeftX  = CentreX-xPnt
        ADD     R1, R6, R1                      ; UpperY = CentreY+yPnt
        BL      NewHLine                        ; draw upper slice

        CMP     R11, R1                         ; unless UpperY=LowerY
        MOV     R1, R11
        BLNE    NewHLine                        ; do draw lower slice

        ADDS    R11, WsPtr, #CircleBlk          ; (C := 0)
        LDMIA   R11, {R0-R7}                    ; reload the parameter block
20
        TEQ     R0, #0
        Pull    PC, EQ                          ; finish if xPnt=0
        BL      AdvCircleParm                   ; else step to next point
        BCC     %BT20                           ; step until yPnt changes
        B       %BT10                           ; do next slice

; *****************************************************************************
;
;       CircleArc - Circular arc outline
;
;       External routine
;
; in:   OldCs is the centre of the circle
;       ICursor is the start of the arc
;       NewPt is the finishing point of the arc
;
; out:  R0-R11 corrupt
;

CircleArc ROUT
        Push    R14
        BL      GenArcParmBlk
        ADD     R11, WsPtr, #CircleBlk
        LDMIA   R11, {R0-R7}                    ; reload the parameter block
10
        STMIA   R11, {R0-R7}                    ; save prior to line drawing
        BL      Reflect
        BL      UpdateQuadrants

        LDRB    R8, [WsPtr, #Quad0Draw]         ; if LSBit set, plot this point
        TST     R8, #1
        LDRNE   R0, [WsPtr, #ArcPoint0X]
        LDRNE   R1, [WsPtr, #ArcPoint0Y]
        BLNE    PlotPoint

        LDR     R0, [WsPtr, #CircleBlk]         ; if xPnt=0, ignore left pixel
        CMP     R0, #0
        BEQ     %FT20

        LDRB    R8, [WsPtr, #Quad1Draw]         ; if LSBit set, plot this point
        TST     R8, #1
        LDRNE   R0, [WsPtr, #ArcPoint1X]
        LDRNE   R1, [WsPtr, #ArcPoint1Y]
        BLNE    PlotPoint
20
        LDR     R0, [WsPtr, #(CircleBlk+4)]     ; if yPnt=0, skip lower
        CMP     R0, #0                          ; pixel pair
        BEQ     %FT30

        LDRB    R8, [WsPtr, #Quad3Draw]         ; if LSBit set, plot this point
        TST     R8, #1
        LDRNE   R0, [WsPtr, #ArcPoint3X]
        LDRNE   R1, [WsPtr, #ArcPoint3Y]
        BLNE    PlotPoint

        LDR     R0, [WsPtr, #CircleBlk]         ; if xPnt=0, ignore left pixel
        CMP     R0, #0
        BEQ     %FT30

        LDRB    R8, [WsPtr, #Quad2Draw]         ; if LSBit set, plot this point
        TST     R8,#1
        LDRNE   R0, [WsPtr, #ArcPoint2X]
        LDRNE   R1, [WsPtr, #ArcPoint2Y]
        BLNE    PlotPoint
30
        ADD     R11, WsPtr, #CircleBlk
        LDMIA   R11, {R0-R7}                    ; reload the parameter block
        TEQ     R0, #0
        Pull    PC, EQ                          ; finish if xPnt=0

        BL      AdvCircleParm                   ; else step to next point
        B       %BT10                           ; and go round again

; *****************************************************************************
;
;       SegmentFill - Circular segment fill
;
;       External routine
;
; in:   OldCs is the centre of the circle
;       ICursor is the start of the segment
;       NewPt is the finishing point of the segment
;
; out:  R0-R11 corrupt
;

SegmentFill ROUT
        Push    R14
        BL      GenArcParmBlk
        BL      GenSegParmBlk
        ADD     R11, WsPtr,#CircleBlk
        LDMIA   R11, {R0-R7}                    ; reload the parameter block
10
        STMIA   R11, {R0-R7}                    ; save prior to line drawing
        BL      Reflect
        BL      UpdateQuadrants
        LDR     R0, [WsPtr, #Quad0StateChange]  ; if any quadrant changes state
        CMP     R0, #0
        BLNE    SegmentLineOn                   ; try starting segment line

        LDR     R7, [WsPtr, #ArcPoint1X]        ; limits of segment line
        LDR     R8, [WsPtr, #ArcPoint0X]
        LDR     R9, [WsPtr, #ArcPoint1Y]        ; current scanline
        LDR     R11, [WsPtr, #UpperSegLinePtr]  ; holds 0 or points at CLine2
        CMP     R11, #0
        BLNE    SegLineStep                     ; and advance if active

        LDR     R0, [WsPtr, #ArcPoint1X]
        LDR     R1, [WsPtr, #ArcPoint1Y]
        LDR     R2, [WsPtr, #ArcPoint0X]
        LDRB    R3, [WsPtr, #Quad1Draw]
        LDRB    R4, [WsPtr, #Quad0Draw]
        BL      SegmentSlice

        LDR     R0, [WsPtr, #(CircleBlk+4)]     ; if yPnt=0, skip lower line
        CMP     R0, #0
        BEQ     %FT15

        LDR     R7, [WsPtr, #ArcPoint2X]        ; limits of segment line
        LDR     R8, [WsPtr, #ArcPoint3X]
        LDR     R9, [WsPtr, #ArcPoint3Y]        ; current scanline
        LDR     R11, [WsPtr, #LowerSegLinePtr]  ; holds 0 or points at CLine3
        CMP     R11, #0
        BLNE    SegLineStep                     ; and advance if active

        Swap    R7,R8

        LDR     R0, [WsPtr, #ArcPoint3X]
        LDR     R1, [WsPtr, #ArcPoint3Y]
        LDR     R2, [WsPtr, #ArcPoint2X]
        LDRB    R3, [WsPtr, #Quad3Draw]
        LDRB    R4, [WsPtr, #Quad2Draw]
        BL      SegmentSlice
15
        LDR     R0, [WsPtr, #Quad0StateChange]  ; if any quadrant state changes
        CMP     R0, #0                          ; left, kill segment line
        BLNE    SegmentLineOff

        ADD     R11, WsPtr, #CircleBlk
        LDMIA   R11, {R0-R7}                    ; reload the parameter block
20
        TEQ     R0, #0
        Pull    PC, EQ                          ; finish if xPnt=0

        BL      AdvCircleParm                   ; else step to next point
        BCS     %BT10                           ; do next slice
        BCC     %BT20                           ; step until yPnt changes

; *****************************************************************************
;
;       SegLineStep - Advance the segment line, limited to be within the circle
;
;       Internal routine, called by SegmentFill
;
; in:   R7 = left circleX
;       R8 = right circleX
;       R9 = current scanline
;       R11 = pointer to Line (CLine2 or CLine3) (can't hold zero any more)
;

SegLineStep ROUT
        Push    R14
        LDMIA   R11, {R0-R6,R10} ; N.B. EndX is in R10
        CMP     R4, #0          ; if line is horizontal
        MOVEQ   R9, R10         ; then line limits are in R0 & R10,
        BEQ     %FT50           ;      R9 := R10 and branch
                                ; else find limits

        CMP     R9, R1          ; advance line until currentY = circleY
20
        BLNE    AdvLineParm     ; (this usually takes one step)
        CMP     R9, R1
        BNE     %BT20

        MOV     R9, R0          ; assume CurrentX is the left most point

        CMP     R10, R0         ; if currentX=EndX
        BEQ     %FT40           ; then no need to advance line

        CMP     R2, #0          ; else While bres >= 0 do AdvLineParm
30
        AdvLineParm_GE          ; this leaves us furthest point on segment
        CMP     R10, R0
        BEQ     %FT40
        CMP     R2, #0          ; line for this scanline in R0
        BGE     %BT30
40
        STMIA   R11, {R0-R4}    ; update the line parameter block
50
        CMP     R9, R0          ; R9 := LeftX
        MOVGT   R10, R9
        MOVGT   R9, R0
        MOVLE   R10, R0         ; R10 := RightX

        CMP     R8, R10         ; force R10 into range R7..R8
        MOVLT   R10, R8
        CMP     R7, R10
        MOVGT   R10, R7

        CMP     R7, R9          ; force R9 into range R7..R8
        MOVGT   R9, R7
        CMP     R8, R9
        MOVLT   R9, R8

        MOV     R8, R10
        MOV     R7, R9
        Pull    PC

; *****************************************************************************
;
;       SegmentSlice - Draw a slice of a circular segment
;
;       Internal routine, called by SegmentFill
;
; in:   R0 = circleX left
;       R1 = circleY
;       R2 = circleX right
;       R3 = left quadrant control word  (TopL or BotR)
;       R4 = right quadrant control word (TopR or BotL)
;       R7 = left most point of segment line for this slice
;       R8 = right most point of segment line for this slice
;
; out:  R0-R11 corrupt
;

SegmentSlice ROUT
        TST     R3, #1          ; if both quadrants empty
        TSTEQ   R4, #1          ; or not at start of segment
        MOVEQ   PC, R14         ; then return

        TST     R3, #1          ; if both quadrants to be filled
        TSTNE   R4, #1          ; or filling below segment line
        BNE     HLine           ; then draw a slice of circle

;              0 0              ; ......
;              1 1              ; (----)

;              0 1              ;     \)    ;     /) ;  \---)    ;  /---)
;              1 0              ; (/        ; (\     ; (---/     ; (---\�

; From here on, all plotting decisions can be made from
; the LSBit of one quadrant field

        TST     R3, #1
        MOVEQ   R0, R7          ; Draw ---)
        BEQ     HLine

        MOV     R2, R8          ; Draw (---
        B       HLine

; *****************************************************************************
;
;       SegmentLineOn - Try to start segment line
;
;       Internal routine, called by SegmentFill
;

SegmentLineOn ROUT
        Push    R14
                                                ; quadrant 0
        LDRB    R10, [WsPtr, #Quad0StateChange] ; state change 0..2
        ADD     R9, WsPtr, #ArcPoint0X          ; address of point on circle
        ADD     R8, WsPtr, #CLine2              ; line block to use if starting
        LDR     R11, [WsPtr, #UpperSegLinePtr]  ; 0 or ptr to active line
        BL      SegmentLineO5
        STRB    R10, [WsPtr, #Quad0StateChange] ; new state change flag

        LDRB    R10, [WsPtr, #Quad1StateChange] ; quadrant 1
        ADD     R9, WsPtr, #ArcPoint1X
        ADD     R8, WsPtr, #CLine2
        BL      SegmentLineO5
        STRB    R10, [WsPtr, #Quad1StateChange]
        STR     R11, [WsPtr, #UpperSegLinePtr]  ; unchanged/updated lineptr

                                                ; lower hemisphere
        LDRB    R10, [WsPtr, #Quad2StateChange] ; quadrant 2
        ADD     R9, WsPtr, #ArcPoint2X
        ADD     R8, WsPtr, #CLine3
        LDR     R11, [WsPtr, #LowerSegLinePtr]
        BL      SegmentLineO5
        STRB    R10, [WsPtr, #Quad2StateChange]

        LDRB    R10, [WsPtr, #Quad3StateChange] ; quadrant 3
        ADD     R9, WsPtr, #ArcPoint3X
        ADD     R8, WsPtr, #CLine3
        BL      SegmentLineO5
        STRB    R10, [WsPtr, #Quad3StateChange]
        STR     R11, [WsPtr, #LowerSegLinePtr]

        Pull    PC

; *****************************************************************************
;
;       SegmentLineO5 - On state change, start segment line and update
;        statechange - if line already active, do nothing
;
;       Internal routine, called by SegmentLineOn
;
; in:   R8  -> CLine(2..3)
;       R9  -> ArcPoint(0..3)X
;       R10 = Quad(0..3)StateChange
;         0 means no change
;         1 means one line hit
;         2 means both lines hit
;       R11 = 0 or pointer to segment line
;
; out:  R10 = updated Quad(0..3)StateChange
;       R11 = 0 or points at newly created line
;

SegmentLineO5 ROUT
        CMP     R10, #0         ; if state unchanged
        MOVEQ   PC, R14         ; then go home

        CMP     R11, #0         ; else if segment line active
        MOVNE   PC, R14         ;      then go home

        Push    R14             ;      else start the segment line
        MOV     R11, R8
        LDMIA   R9, {R0,R1}     ; run from point on circle
        LDMIA   R11, {R2,R3}    ; to other end
        BL      GenLineParm
        STMIA   R11, {R0-R8}
        MOV     R10, R10, LSR #1 ; state1->state0, state2->state1
        Pull    PC

; *****************************************************************************
;
;       SegmentLineOff - Try to kill segment line
;
;       Internal routine, called by SegmentFill
;
; in:   R0 = stateflags for each quadrant
;
; out:  R0 preserved
;       R1 corrupted
;

SegmentLineOff ROUT
        MOV     R1, #0
        TST     R0, #&3                         ; if statechange occurred in
        TSTEQ   R0, #&300                       ; Quad0 or Quad1
        STRNE   R1, [WsPtr, #UpperSegLinePtr]   ; then kill upper segment line

        TST     R0, #&30000                     ; if statechange occured in
        TSTEQ   R0, #&3000000                   ; Quad2 or Quad3
        STRNE   R1, [WsPtr, #LowerSegLinePtr]   ; then kill lower segment line

        MOV     PC, R14

; *****************************************************************************
;
;       SectorFill - Circular sector (pie) fill
;
;       External routine
;
; in:   OldCs is the centre of the circle
;       ICursor is the start of the sector
;       NewPt is the finishing point of the sector
;
; out:  R0-R11 corrupt
;

SectorFill ROUT
        Push    R14
        BL      GenArcParmBlk
        ADD     R11, WsPtr, #CircleBlk
        LDMIA   R11, {R0-R7}                    ; reload the parameter block
SectorFi10
        STMIA   R11, {R0-R7}                    ; save prior to line drawing
        BL      Reflect
        BL      UpdateQuadrants

        LDR     R0, [WsPtr, #(CircleBlk+4)]     ; if yPnt=0, panic
        CMP     R0, #0
        BEQ     SectorFi40

        LDR     R0, [WsPtr, #ArcPoint1X]
        LDR     R1, [WsPtr, #ArcPoint1Y]
        LDR     R2, [WsPtr, #ArcPoint0X]
        LDRB    R3, [WsPtr, #Quad1Draw]
        LDRB    R4, [WsPtr, #Quad0Draw]
        BL      SectorSlice

        LDR     R0, [WsPtr, #ArcPoint3X]
        LDR     R1, [WsPtr, #ArcPoint3Y]
        LDR     R2, [WsPtr, #ArcPoint2X]
        LDRB    R3, [WsPtr, #Quad3Draw]
        LDRB    R4, [WsPtr, #Quad2Draw]
        BL      SectorSlice

SectorFi20
        ADD     R11, WsPtr, #CircleBlk
        LDMIA   R11, {R0-R7}                    ; reload the parameter block
SectorFi30
        TEQ     R0, #0
        Pull    PC, EQ                          ; finish if xPnt=0
        BL      AdvCircleParm                   ; else step to next point
        BCS     SectorFi10                      ; do next slice
        BCC     SectorFi30                      ; step until yPnt changes

SectorFi40
        LDR     R0, [WsPtr, #CLine0Near]        ; equal to CLine1NearX &
        LDR     R1, [WsPtr, #ArcPoint0Y]        ; centre of circle
        LDR     R3, [WsPtr, #CLine0Far]
        LDR     R4, [WsPtr, #CLine1Far]

        Greatest R2, R0,R3              ; draw from rightmost of CLine0, Cline1
        Greatest R2, R2,R4

        Least R0, R0,R3                 ; to left most of CLine0, Cline1
        Least R0, R0,R4

        LDR     R3, [WsPtr, #Quad0Draw] ; all 4 drawing control bytes

        TST     R3, #&00000001          ; if Quad0 or
        TSTEQ   R3, #&01000000          ;    Quad3 fills against circle
        LDRNE   R2, [WsPtr, #ArcPoint0X] ; then override R2

        TST     R3, #&00000100          ; If Quad1 or
        TSTEQ   R3, #&00010000          ;    Quad2 fills against circle
        LDRNE   R0, [WsPtr, #ArcPoint1X] ; then override R0

        BL      NewHLine                ; draw the line (sorted coords)
        B       SectorFi20


;
; Internal subroutine for sector (pie) fills
;
; On entry, R0 - circleX left
;           R1 - circleY
;           R2 - circleX right
;           R3 - left quadrant control word  (TopL or BotR)
;           R4 - right quadrant control word (TopR or BotL)
;
; On exit, R0-R11 corrupt
;
SectorSlice
        CMP   R4,#&57                           ; (--//)
        LDREQ R3,[WsPtr,#CLine0Far]
        LDREQ R4,[WsPtr,#CLine1Near]
        BEQ DoubleHLine

        CMP   R3,#&73                           ; (\\--)
        LDREQ R3,[WsPtr,#CLine0Near]
        LDREQ R4,[WsPtr,#CLine1Far]
        BEQ DoubleHLine

        CMP   R4,#&07                           ; (-\/-)
        CMPEQ R3,#&03
        LDREQ R3,[WsPtr,#CLine0Near]
        LDREQ R4,[WsPtr,#CLine1Near]
        BEQ DoubleHLine

        CMP   R4,#&07                           ;    /-)
        LDREQ R0,[WsPtr,#CLine1Near]
        BEQ HLine
        CMP   R3,#&03                           ; (-\�
        LDREQ R2,[WsPtr,#CLine0Near]
        BEQ HLine

        CMP   R4,#&3A                           ;    /-/
        LDREQ R0,[WsPtr,#CLine1Near]
        LDREQ R2,[WsPtr,#CLine0Far]
        BEQ HLine

        CMP   R3,#&1E                           ; \-\�
        LDREQ R0,[WsPtr,#CLine1Far]
        LDREQ R2,[WsPtr,#CLine0Near]
        BEQ HLine

        CMP   R4,#1                             ; ...--)
        MOVLT PC,Link                           ;Nothing in either quadrant
        LDRGT R2,[WsPtr,#CLine0Far]             ; ...--/
        CMP   R3,#1                             ; (--...
        LDRGT R0,[WsPtr,#CLine1Far]             ; \--...
        B HLine
;
;
;
;------------------------------------------------------------------------------
;
; Reflect - Generate four point on a circle by
; =======   reflection about its centre
;
; On entry, R0..R7 hold a circle parameter block
; On exit,  R0 (X), R1  (Y) point in Quadrant0
;           R2 (X), R3  (Y) point in Quadrant1
;           R7 (X), R8  (Y) point in Quadrant2
;           R9 (X), R10 (Y) point in Quadrant3
;           R11 points at ArcPoint0X
;
;           ArcPoint(0..3) updated
;
; Format of a circle control block
;       R0 - xPnt   (CurrentX - relative to centre)
;       R1 - yPnt   (CurrentY - relative to centre)
;       R2 - sum (Bres)
;       R3 - upcnt
;       R4 - downcnt
;       R5 - CentreX
;       R6 - CentreY
;       R7 - Aspect (pixel shape : 0 square, 1 horz rect, 2 vert rect)
;
Reflect
        ADD R9,R5,R0    ;Quad 3  CentreX+xPnt   ;Calculate all 4 points
        SUB R10,R6,R1   ;        CentreY-yPnt   ; by reflection about centre

        SUB R7,R5,R0    ;Quad 2  CentreX-xPnt
        SUB R8,R6,R1    ;        CentreY-yPnt

        SUB R2,R5,R0    ;Quad 1  CentreX-xPnt
        ADD R3,R6,R1    ;        CentreY+yPnt

        ADD R0,R5,R0    ;Quad 0  CentreX+xPnt
        ADD R1,R6,R1    ;        CentreY+yPnt

        ADD R11,WsPtr,#ArcPoint0X
        STMIA R11,{R0,R1, R2,R3, R7,R8, R9,R10} ;And store the lot for later on

        MOV PC,Link
;
;
;
;  update lines & quadrant data
;
;  use R9  as offset from WsPtr to ArcPoint(0..3)X
;  use R10 as offset from WsPtr to QuadControl(0..3)
;  use R11 as address of line parameter block(0..1)
;
;
UpdateQuadrants
        SaveRetAdr

        MOV R0,#0
        STR R0,[WsPtr,#Quad0StateChange]        ;Clear flags for each quadrant

        LDR R0,[WsPtr,#Quad0Control]            ;Update the 4 'drawing'
        STR R0,[WsPtr,#Quad0Draw]               ; control bytes

                                                ;Start by looking at quadrant 0
        ADD R10,WsPtr,#Quad0Control             ;Address of control byte
        ADD R9,WsPtr,#ArcPoint0X                ;Address of point on circle
        BL UpdateQuadr10

        ADD R10,R10,#(Quad1Control-Quad0Control)        ;Quadrant 1
        ADD R9,R9,#(ArcPoint1X-ArcPoint0X)
        BL UpdateQuadr10

        ADD R10,R10,#(Quad1Control-Quad0Control)        ;Quadrant 2
        ADD R9,R9,#(ArcPoint1X-ArcPoint0X)
        BL UpdateQuadr10

        ADD R10,R10,#(Quad1Control-Quad0Control)        ;Quadrant 3
        ADD R9,R9,#(ArcPoint1X-ArcPoint0X)
        BL UpdateQuadr10

        Return


UpdateQuadr10
        LDRB R0,[R10]           ;Get control block for quadrant
        TST R0,#&2              ;If 0 or 1
        MOVEQ PC,Link           ; then nothing to do

        SaveRetAdr              ; else update the line

        LDMIA R9,{R7,R8}                        ;Point on circle in this quad

        TST R0,#4
        ADDEQ R11,WsPtr,#CLine0                 ;Load parm blk for line(0/1)
        ADDNE R11,WsPtr,#CLine1

        LDMIA R11,{R0,R1,R2,R3,R4,R5,R6}        ;EndX,EndY (R7,R8) not needed
        BL ArcLineStep
        STMIA R11,{R0,R1,R2,R3,R4,R5,R6,R8}     ;Update the changes, EndX
                                                ; used for NearX

        CMP R7,#1                               ; 'change state' flag
        MOV R7,#1                               ;Convert to one line hit
        STRGEB R7,[R10,#(Quad0StateChange-Quad0Control)]

        LDRB R0,[R10]           ;Get control block for quadrant and look at
        MOV R0,R0,LSR #3        ; next control field
        STRGEB R0,[R10]         ;If 'change state' write back next field

                                                        ;If outside circle
        TSTEQ R0,#1                                     ; or changing into a
        STRGTB R0,[R10,#(Quad0Draw-Quad0Control)]       ; plotting state update
                                                        ; drawing control byte

        TST R0,#2                               ;If new field doesnt advance a
        Return EQ                               ; line then quit
                                                ; else update second line
        Push R0

        LDMIA R9,{R7,R8}                        ;Point on circle in this quad

        TST R0,#4
        ADDEQ R11,WsPtr,#CLine0                 ;Load parm blk for line(0/1)
        ADDNE R11,WsPtr,#CLine1

        LDMIA R11,{R0,R1,R2,R3,R4,R5,R6}        ;EndX,EndY (R7,R8) not needed
        BL ArcLineStep
        STMIA R11,{R0,R1,R2,R3,R4,R5,R6,R8}     ;Update the changes, EndX
                                                ; used for NearX

        CMP R7,#1                               ; 'change state' flag
        MOV R7,#2                               ;Convert to both lines hit
        STRGEB R7,[R10,#(Quad0StateChange-Quad0Control)]

        Pull R0         ;Use earlier value instead of reloading control block,
                        ; as whally lines (dy/dx = 0/0) blewup when 2nd line
                        ; terminated before 1st line. This case should not
                        ; now get through, but you never now.

        MOV R0,R0,LSR #3        ;Next field in control field
        STRGEB R0,[R10]         ;If 'changing state' write this back

        STRGTB R0,[R10,#(Quad0Draw-Quad0Control)]       ; and update drawing
                                                        ; control byte
        Return
;
;
;
; ArcLineStep - Step line parameter block checking for interception
; ===========     with circle.
;
;               Return first ('near') and last ('far') points of line
;                 that fall on this scanline, limiting 'far' to the point on
;                 the circle if interception occurs.
;
; On entry, R0..R6 hold a line parameter block (EndX,EndY are not loaded)
;
; Format of a line control block
;           R0 - StartX (CurrentX)
;           R1 - StartY (CurrentY)
;           R2 - Bres
;           R3 - DeltaX
;           R4 - DeltaY
;           R5 - StepX (+1/-1) (Equv bit6 of Sign in 6502 version)
;           R6 - StepY (+1/-1) (Equv bit7 of Sign in 6502 version)
;                (R7 - EndX            Not used in this routine,)
;                (R8 - EndY            so not passed in)
;
;           R7,R8  CircleX,CircleY
;
; On exit,  R0 (X), R1 (Y), R2 (bres) updated
;           R7 0/1/2 for within/on/outside circle
;           R8 nearX
;
;
; R9,R10,R11 unused
;
ArcLineStep
        SaveRetAdr

        CMP R8,R1               ;Advance line until CurrentY = CircleY
ArcLineSt10
        BLNE AdvLineParm
        CMP R8,R1               ; {this usually takes one step}
        BNE ArcLineSt10

        MOV R8,R0               ;This point is nearX
                                ; ie the first point on this scanline

        SUBS R14,R0,R7          ;If  (CurrentX=CircleX) then farX is on circle
        TEQNE R5,R14            ;If ((CurrentX-CircleX) EOR StepX is +ve)
                                ;then farX is outside circle
        MOVPL R0,R7             ;      limit farX to circleX
        MOVPL R7,#2             ;      set change flag =2 for outside
        MOVEQ R7,#1             ;                      =1 for on circle
        Return PL               ;      and return


        CMP R2,#0               ;While bres >= 0 and within circle AdvLineParm
ArcLineSt20                     ; this leaves us with farX,farY in R0,R1
        AdvLineParm_GE

        CMP R0,R7               ;If (CurrentX=CircleX) then farX is on circle
        MOVEQ R7,#1             ;      set change flag
        Return EQ               ;      and return
                                ; else within circle

        CMP R2,#0               ;If y about to change, return farX,farY
        BGE ArcLineSt20         ; else loop back to step the line
        MOV R7,#0
        Return
;
;
;
; Assumes  R9, R10 & R11 are not corrupted over calls to GenLineParm
;
;
GenArcParmBlk
        SaveRetAdr

        ADD R11,WsPtr,#OldCsX                   ;Build parm block for a circle
        LDMIA R11,{R0,R1,R2,R3}                 ; centre OldCs, point on
        BL GenCircleParm                        ; circumference in ICursor

        ADD R11,WsPtr,#CircleBlk
        STMIA R11,{R0,R1,R2,R3,R4,R5,R6,R7}

        ADD R10,WsPtr,#CLine0
        ADD R11,WsPtr,#OldCsX

        LDMIA R11!,{R0,R1,R2,R3}                ;CLine0 gives start of arc
        BL GenLineParm                          ; (OldCs->ICursor)
        STMIA R10!,{R0,R1,R2,R3,R4,R5,R6,R7,R8}
;
; Use StepX and StepY for 'start of arc' line to form the beginnings of
; an index into the arc control block table
; StepX & StepY each hold +1 or -1 and the value left in R9 is
;
;     R9  | Quad
;   ------+------
;    0000 |  0
;    0100 |  1
;    1100 |  2
;    1000 |  3
;
;
        AND R5,R5,#&4                           ;
        AND R6,R6,#&8                           ; See above for details
        ORR R9,R6,R5                            ;

        LDMIA R11,{R2,R3}                       ;CLine1 gives end line of arc
                                                ; (OldCs->NewPt)

        CMP R0,R2                               ;If OldCs=NewPt
        CMPEQ R1,R3                             ; use OldCs->(NewPtX+1,NewPtY)
        ADDEQ R2,R2,#1                          ; for compatability with Master

        BL GenLineParm
        STMIA R10,{R0,R1,R2,R3,R4,R5,R6,R7,R8}

        AND R5,R5,#&4
        AND R6,R6,#&8
        ORR R6,R6,R5                            ; 'end of arc'

        CMP R9,R6               ;If start and end quadrants different
        ORRNE R9,R9,R6,LSL #2   ;  then index = start OR (end <<2)
        BNE GenArcPar10         ;  and branch

                                ;else special case code for index, based
                                ; on gradients of line

        LDR R7,[WsPtr,#(CLine0+12)]     ; DeltaX0.DeltaY1
        LDR R8,[WsPtr,#(CLine1+16)]
        MUL R0, R7, R8

        LDR R7,[WsPtr,#(CLine0+16)]     ; DeltaX1.DeltaY0
        LDR R8,[WsPtr,#(CLine1+12)]
        MUL R9, R7, R8

        CMP R0,R9                       ;For gradients <,>,=
        ORRLT R9,R6,#&40                ; generate index into table
        ORRGT R9,R6,#&50
        ORREQ R9,R6,R6,LSL #2

GenArcPar10
        ADR R0,GenArcTb                 ; GenArcTb

        LDR R0,[R0,R9]
        STR R0,[WsPtr,#Quad0Control]    ;Setup all FOUR control BYTEs

        Return



;
;
; 00 000 000  &00     no points to be plotted
; 00 000 001  &01     all points to be plotted
; 00 001 x10  &0A,0E  plot nothing until line x hits circle then start plotting
; 00 000 x11  &03,07  plot points until line x hits circle then stop
; 00 y11 x10  &3A,1E  plot points only between line x and line y
; 01 y10 x11  &73,57  plot points from x axis to line x, then line y to y axis
;
;
GenArcTb
 & &0000003A    ; 0->0 (short arc)
 & &01010307    ; 1->0
 & &03000007    ; 3->0
 & &010A0007    ; 2->0

 & &00000E0A    ; 0->1
 & &00001E00    ; 1->1 (short arc)
 & &03000E01    ; 3->1
 & &010A0E01    ; 2->1

 & &0E01010A    ; 0->3
 & &0E010300    ; 1->3
 & &1E000000    ; 3->3 (short arc)
 & &0E0A0000    ; 2->3

 & &0007010A    ; 0->2
 & &00070300    ; 1->2
 & &03070101    ; 3->2
 & &003A0000    ; 2->2 (short arc)


 & &01010157    ; 0->0 (long arc)    grad line0 > grad line1
 & &00001E00    ; 1->1 (short arc)
 & &1E000000    ; 3->3 (short arc)
 & &01570101    ; 2->2 (long arc)

 & &0000003A    ; 0->0 (short arc)   grad line0 < grad line 1
 & &01017301    ; 1->1 (long arc)
 & &73010101    ; 3->3 (long arc)
 & &003A0000    ; 2->2 (short arc)
;
;
;


GenSegParmBlk
        SaveRetAdr

        ADD R11,WsPtr,#CLine1           ;Get all line data except EndX,EndY
        LDMIA R11,{R0,R1,R2,R3,R4,R5,R6}

        LDR R11,[WsPtr,#AspectRatio]    ;Frigging non square frigging pixels
                                        ; 0=Sq, 1=horz, 2=vert
        CMP R11,#1
        MOVEQ R3,R3,LSL #1              ;If horz, scale up deltaX
        MOVGT R4,R4,LSL #1              ;If vert, scale up deltaY

        [ {TRUE}
        LDR     R8, [WsPtr, #CircleRadSquare] ; R8 = r2
        MUL     R9, R3, R3              ; R9 = DX2
        MUL     R10, R4, R4             ; R10 = DY2
        ADD     R11, R9, R10            ; R11 = R2

        MOV     R3, R10
        BL      DoubleMulDivSquareRoot
        MOV     R4, R3                  ; R4 = dy

        MOV     R3, R9
        BL      DoubleMulDivSquareRoot  ; R3 = dx
        |
        MUL R9,R3,R3                    ;R9 := Square(deltaX)
        MOV R2,R9                       ;R2 := Square(deltaX)

        LDR R8,[WsPtr,#CircleRadSquare]
        MUL R3,R9,R8                    ;R3 := Square(radius) * Square(deltaX)

        MUL R9,R4,R4                    ;R9 := Square(deltaY)
        ADD R2,R2,R9                    ;R2 := Square(deltaX) + Square(deltaY)

        MUL R10,R9,R8                   ;R10 := Square(radius) * Square(deltaY)

        MOV R9,R2
; *****Change made by DJS
; Use new DivRem macro, not old DIVREM
; Original code was:
;        DIVREM R7,R10,R9, R8            ;R7 := (rad^2 * deltaY^2)/R2
        DivRem R7,R10,R9, R8            ;R7 := (rad^2 * deltaY^2)/R2
; *****End of change made by DJS
        BL SquareRoot                   ;Iy left in R8
        MOV R4,R8

; *****Change made by DJS
; Use new DivRem macro, not old DIVREM
; Original code was:
;        DIVREM R7,R3,R2, R8             ;R7 := (rad^2 * deltaX^2)/R2
        DivRem R7,R3,R2, R8             ;R7 := (rad^2 * deltaX^2)/R2
; *****End of change made by DJS
        BL SquareRoot                   ;Ix left in R8
        MOV R3,R8
        ]

        LDR R11,[WsPtr,#AspectRatio]    ; 0=Sq, 1=horz, 2=vert
        CMP R11,#1
        MOVEQ R3,R3,LSR #1              ;If horz, scale down deltaX
        MOVGT R4,R4,LSR #1              ;If vert, scale down deltaY

        CMP R5,#0                       ;If StepX >= 0
        ADDGE R0,R0,R3                  ; then R0 := StartX+R3
        SUBLT R0,R0,R3                  ; else R0 := StartX-R3

        CMP R6,#0                       ;If StepY >= 0
        ADDGE R1,R1,R4                  ; then R1 := StartY+R4
        SUBLT R1,R1,R4                  ; else R1 := StartY-R4

;
; R0,R1 is the intercept of CLine1 and the circle
;       so, segment line runs from here to endpoint of CLine0
;

        LDR R2,[WsPtr,#CLine0EndX]
        LDR R3,[WsPtr,#CLine0EndY]

        CompSwapT R0,R1, R2,R3, R4       ; Order coords

;
; If segment line crosses X axis
; then initialise both upper & lower segment lines
; else leave endpoints for later use
;

        LDR R4,[WsPtr,#Quad0Control]    ;All 4 control bytes
        ORR R4,R4,R4,LSR #8             ;If bit1 =0, not in upper hemisphere
                                        ;If bit9 =0, not in lower hemisphere
        AND R4,R4,R4,LSR #16
        TST R4,#2                       ;If bit1 =0
        BEQ GenSegParmB10               ; then line does not cross X axis

                                        ; else start both CLine2 & CLine3
                                        ;      running, so..

        BL GenLineParm                          ;Initialise CLine2 as the
        ADD R11,WsPtr,#CLine2                   ; upper hemisphere segment line
        STMIA R11,{R0,R1,R2,R3,R4,R5,R6,R7,R8}
        STR R11,[WsPtr,#UpperSegLinePtr]

        MOV R2,R0                               ;Run CLine3, in the opposite
        MOV R3,R1                               ; direction for use as the
        MOV R0,R7                               ; lower hemisphere segment line
        MOV R1,R8
        BL GenLineParm
        ADD R11,WsPtr,#CLine3
        STMIA R11,{R0,R1,R2,R3,R4,R5,R6,R7,R8}
        STR R11,[WsPtr,#LowerSegLinePtr]

        Return

GenSegParmB10                           ;Line does not cross X axis, so..
        ADD R11,WsPtr,#CLine2           ;Upper hemisphere segment line, runs
        STMIA R11,{R2,R3}               ; to R2,R3  (if it runs at all)

        ADD R11,WsPtr,#CLine3           ;Lower hemisphere segment line, runs
        STMIA R11,{R0,R1}               ; to R0,R1  (if it runs at all)

        MOV R11,#0                              ;Both lines inactive
        STR R11,[WsPtr,#UpperSegLinePtr]
        STR R11,[WsPtr,#LowerSegLinePtr]

        Return

; *****************************************************************************
;
;       DoubleMulDivSquareRoot - Compute SQR(a*b/c) in double precision
;
; in:   R3 = a
;       R8 = b
;       R11 = c
;
; out:  R3 = result
;       R2, R7 corrupted
;

DoubleMulDivSquareRoot ROUT
        Push    "R8-R11,R14"
    [ NoARMM
        MOV     R2, R3, LSR #16                 ; R2 = ah
        EOR     R7, R3, R2, LSL #16             ; R7 = al
        MOV     R9, R8, LSR #16                 ; R9 = bh
        EOR     R10, R8, R9, LSL #16            ; R10 = bl

        MUL     R3, R7, R10                     ; R3 = al.bl
        MUL     R14, R7, R9                     ; R14 = al.bh
        MLA     R14, R10, R2, R14               ; R14 = al.bh + ah.bl
        MUL     R8, R2, R9                      ; R8 = ah.bh

        ADDS    R3, R3, R14, LSL #16            ; R3 = lower 32 bits of a.b
        ADC     R8, R8, R14, LSR #16            ; R8 = upper 32 bits of a.b
    |
        ; Use UMULL
      [ NoARMv6
        ; Rn can't be RdLo/RdHi in ARMv5 and below
        MOV     R2, R3
        UMULL   R3, R8, R2, R8
      |
        UMULL   R3, R8, R3, R8
      ]
    ]
; now do divide of a.b by c
; we know that a.b < 2^61, so no problem with top bit of a.b
      [ NoARMv5
        MOV     R9, R11                         ; R9 = low 32 bits of shifted c
        MOV     R10, #0                         ; R10 = hi 32 bits of shifted c
10
        ADDS    R9, R9, R9                      ; shift R9,R10 left one place
        ADC     R10, R10, R10
        CMP     R9, R3                          ; compare R9,R10 with a.b
        SBCS    R14, R10, R8
        BCC     %BT10                           ; if lower then loop
      |
        CLZ     R9, R11
        TEQ     R8, #0
        CLZNE   R2, R8
        CLZEQ   R2, R3
        ADDNE   R9, R9, #32
        SUBS    R2, R9, R2                      ; R2 = how many bits to shift R11 left
        MOVLT   R3, #0
        Pull    "R8-R11,PC",LT

        MOV     R9, R11, LSL R2
        SUBS    R2, R2, #32
        MOVGE   R10, R11, LSL R2
        RSBLT   R2, R2, #0
        MOVLT   R10, R11, LSR R2
      ]

        MOV     R7, #0                          ; zero result
20
        CMP     R3, R9                          ; if a.b >= R9,R10
        SBCS    R14, R8, R10
        SUBCS   R3, R3, R9                      ; then a.b -:= R9,R10
        MOVCS   R8, R14
        ADC     R7, R7, R7                      ; shift result up with new bit
        MOVS    R10, R10, LSR #1                ; shift R9,R10 right one bit
        MOV     R9, R9, RRX
        BNE     %BT20                           ; for termination, R10 = 0
        CMP     R9, R11                         ; and R9 < R11
        BCS     %BT20

        BL      SquareRoot                      ; in: R7 = arg
                                                ; out: R8 = result, R9-R11 corrupt
        MOV     R3, R8
        Pull    "R8-R11,PC"



        END