; 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