; 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.VduPlot
;
; ARTHUR OPERATING SYSTEM - Vdu Drivers
; =======================
;
; Vdu graphics code - Entry point for plotting & low level primitives
;                     & Ecf pattern setting
; Author R C Manby
; Date   5.9.86
;

; *****************************************************************************
;
; EM
; PLOT - Graphics operation, eg triangle, line etc.
; ====
;
; The plot operation, and the co-ordinate to plot at given by bytes
;  in the vdu queue, as follows
;  QQ+0 - plot number
;  QQ+1 - xLo
;  QQ+2 - xHi
;  QQ+3 - yLo
;  QQ+4 - yHi
;
; The co-ordinate is in external 16 bit form.
; If 2 or 3 are required, they are taken from ICursor & OldCs
;
; Also, both PLOT and EntryFromSWIPlot are entered with R6 = CursorFlags
;
EM
PLOT    ROUT
      [ {TRUE}
        GraphicsMode R0                 ; Quit if not a graphics mode
        MOVNE   PC, Link
      |
        LDR     R0, [WsPtr, #NPix]
        CMP     R0, #0                  ; Quit if not a graphics mode
        MOVEQ   PC, Link
      ]

        ASSERT  ((QQ+1):AND:3)=0
        LDR     R2, [WsPtr, #QQ+1]      ; R2 = xlo,xhi,ylo,yhi
        MOV     R0, R2, LSL #16         ; R0 = 0  ,0  ,xlo,xhi
        MOV     R0, R0, ASR #16         ; R0 = xlo,xhi,sgn,sgn
        MOV     R1, R2, ASR #16         ; R1 = ylo,yhi,sgn,sgn

        LDRB    R2, [WsPtr, #QQ+0]      ; plot mode
        MOV     R9, #0                  ; indicate coming from VDU Plot

EntryFromSWIPlot
        SaveRetAdr

; now convert to internal coords (modified EIG code)

        ASSERT  OrgY = OrgX +4
        ASSERT  GCsX = OrgX +8
        ASSERT  GCsY = OrgX +12
        ASSERT  YEigFactor = XEigFactor +4

        ADD     R3, WsPtr, #OrgX

        LDMIA   R3!, {R4-R5}            ; R4 = OrgX: R5 = OrgY
        TST     R2, #4                  ; If bit2 is clear, then relative
        LDMEQIA R3, {R7-R8}             ; so load old cursor position
        ADDEQ   R0, R0, R7              ; and add
        ADDEQ   R1, R1, R8
        STMIA   R3, {R0-R1}             ; store new coords in GCsX,GCsY

        ADD     R3, WsPtr, #XEigFactor
        LDMIA   R3, {R7-R8}             ; R7 = XEigFactor: R8 = YEigFactor
        ADD     R0, R0, R4              ; add on origin
        ADD     R1, R1, R5
        MOV     R0, R0, ASR R7          ; and shift down
        MOV     R1, R1, ASR R8

        ADD     R3, WsPtr, #NewPtX
        STMIA   R3, {R0-R1}

;
; The 2 LSBits of the plot code specify fg/bg colour and action as :-
;  0 No effect eqv. of Gcol(5,c)
;  1 Foreground colour using foreground Gcol action
;  2 Invert eqv. of Gcol(4,c)
;  3 Background colour using background Gcol action
;

; *****Change made by DJS
;   Original code was:
;        MOV     R3, R2, LSL #30         ; put bottom 2 bits into top 2 bits
;        CMP     R3, #&40000000          ; set lots of flags
;
;        ADDMI   R4, WsPtr, #BgEcfOraEor ; if 0 or 3
;        ADRCC   R4, NoEffect            ; if 0
;        ADDEQ   R4, WsPtr, #FgEcfOraEor ; if 1
;        ADRVS   R4, Invert              ; if 2

        MOVS    R3, R2, LSL #31         ;Put bit 1 in C, NOT(bit 0) in Z
        ADR     R4, NoEffect            ; if 0, 1, 2 or 3
        ADDNE   R4, WsPtr, #FgEcfOraEor ; if 1 or 3
        ADRCS   R4, Invert              ; if 2 or 3
        ADDHI   R4, WsPtr, #BgEcfOraEor ; if CS & NE - i.e. 3

; *****End of change made by DJS

        STR     R4, [WsPtr, #GColAdr]   ; save address of Ecf to plot with

        TST     R6, #ClipBoxEnableBit
        BLNE    DoPlotClipBox

        BIC     R11, R2, #2_111         ; ARMv4 says we have to keep bits 1:0 of PC =0
        ADR     R14, CTidy              ; set up return address
                                        ; R0=X, R1=Y, R2=plot code
        ADD     PC, PC, R11, LSR #1     ; jump to branch
        &       0                       ; dummy word

 B LineDrawSolid           ;   0 - Solid line
 B LineDrawSolid           ;   8 - Solid line, endpoint omitted
 B LineDrawDotted          ;  16 - Dot-dash line, restart pattern
 B LineDrawDotted          ;  24 - Dot-dash line, restart pattern,
                           ;        endpoint omitted
 B LineDrawSolid           ;  32 - Solid extension line
 B LineDrawSolid           ;  40 - Solid extension line, endpoint omitted
 B LineDrawDotted          ;  48 - Dot-dash extension line, continue pattern
 B LineDrawDotted          ;  56 - Dot-dash extension line, continue pattern,
                           ;        endpoint omitted
 B PlotPoint               ;  64 - Point plot
 B FillLRnonBg             ;  72 - Line fill L&R, upto non-background
 B TriangleFill            ;  80 - Triangle fill
 B FillLRtoBg              ;  88 - Fill right, upto background
 B RectangleFill           ;  96 - Rectangle fill
 B FillLRtoFg              ; 104 - Line fill L&R, upto foreground
 B ParallelogramFill       ; 112 - Parallelogram Fill
 B FillLRnonFg             ; 120 - Fill right, upto non-foreground
 B FloodNonBg              ; 128 - Flood to non-bg (ie over bg)
 B FloodToFg               ; 136 - Flood to fg (ie over non-fg)
 B CircleOutline           ; 144 - Circle outline
 B CircleFill              ; 152 - Circle fill
 B CircleArc               ; 160 - Circular arc outline
 B SegmentFill             ; 168 - Segment fill
 B SectorFill              ; 176 - Sector (pie) fill
 B BlockCopyMove           ; 184 - Block copy/move
 B EllipseOutline          ; 192 - Ellipse outline
 B EllipseFill             ; 200 - Ellipse fill
 B   NYA                   ; 208 - Unassigned
 B   NYA                   ; 216 - Unassigned
 B   NYA                   ; 224 - Unassigned
 B SpritePlot              ; 232 - Sprite plot
 B   NYA                   ; 240 - Assigned to applications
 B   NYA                   ; 248 - Assigned to applications

CTidy                           ; The "normal" return point after a plot
                                ; operation, any call that does not want
                                ; the cursors moving should pull return
                                ; address off the stack

                                ; Shuffle the cursors along
                                ;  NewPt -> ICursor -> OldCs -> OlderCs
                                ;
                                ; ECursor was set earlier by EIG

        ADD     R0, WsPtr, #OldCsX
        LDMIA   R0, {R1,R2, R3,R4, R5,R6} ; OldCs(X,Y) ICursor(X,Y) NewPt(X,Y)
        ADD     R0, WsPtr, #OlderCsX
        STMIA   R0, {R1,R2, R3,R4, R5,R6}
        Return

NYA
        MOV     R0, R2                  ; R0 := plot code
        MOV     R10, #UKPLOTV
        Push    "R9, WsPtr, R14"        ; save SWIPlot indicator, WsPtr + link
        BL      VduQQVec
        Pull    "R9, WsPtr, PC", VC     ; return to CTidy if no error

; error in UKPLOTV

        Pull    "R9, WsPtr"     ; restore SWIPlot indicator and WsPtr
        ADD     R13, R13, #8    ; throw away return to CTidy
                                ; and return address stacked by PLOT
        TEQ     R9, #0          ; called from Wrch ?
        BEQ     VduBadExit      ; yes, then go to error exit code
        B       SWIPlotBadExit  ; no, then go to SWIPlot error exit

VduQQVec
        ADD     R1, WsPtr, #QQ
VduGoVec
        CallAVector



;
; Words for forming ZGORA & ZGEOR for colour actions 0 to 7, where
;  0=Store, 1=OR, 2=AND, 3=EOR, 4=Inverse, 5=No change,
;  6=And Not(colour) ie BIC,   7=Or Not(colour)
;
;
; The values correspond to TBzgoo etc on the 6502 and are stored
; in the following order zgoo,zgeo,zgoe,zgee to allow LDM to be used
;
;                         Action
;                       0 1 2 3 4 5 6 7
; TBzgoo  OR the  OR :- F,0,0,F,F,F,0,0
; TBzgeo EOR the  OR :- 0,0,F,F,F,F,0,F
; TBzgoe  OR the EOR :- 0,F,0,0,F,F,0,F
; TBzgee EOR the EOR :- F,F,F,0,0,F,0,F
;

TBscrmasks      *       2_10010111000011111110110001010011

        [ {FALSE}
TBscrmask
 & &FFFFFFFF    ;Store colour to screen  ( OR the  OR)
 & &00000000    ;                        (EOR the  OR)
 & &00000000    ;                        ( OR the EOR)
 & &FFFFFFFF    ;                        (EOR the EOR)

 & &00000000    ;OR colour to screen
 & &00000000
 & &FFFFFFFF
 & &FFFFFFFF

 & &00000000    ;AND
 & &FFFFFFFF
 & &00000000
 & &FFFFFFFF

 & &FFFFFFFF    ;EOR
 & &FFFFFFFF
 & &00000000
 & &00000000

 & &FFFFFFFF    ;Invert
 & &FFFFFFFF
 & &FFFFFFFF
 & &00000000

 & &FFFFFFFF    ;No change
 & &FFFFFFFF
 & &FFFFFFFF
 & &FFFFFFFF

 & &00000000    ;BIC (ie AND NOTcol)
 & &00000000
 & &00000000
 & &00000000

 & &00000000    ;OR NOTcol
 & &FFFFFFFF
 & &FFFFFFFF
 & &FFFFFFFF
        ]

        MALIGN  64      ; Invert and NoEffect must be aligned to a multiple
                        ; of 64 for line drawing routines (TMD)

                        ;Interleaved zgora & zgeor values to give invert
                        ; and no effect plotting, used when plot code
Invert                  ; overrides Fg/Bg Gcol colour and action.
 & &00000000
 & &FFFFFFFF
 & &00000000
 & &FFFFFFFF
 & &00000000
 & &FFFFFFFF
 & &00000000
 & &FFFFFFFF
 & &00000000
 & &FFFFFFFF
 & &00000000
 & &FFFFFFFF
 & &00000000
 & &FFFFFFFF
 & &00000000
 & &FFFFFFFF
NoEffect
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000
 & &00000000

; *****************************************************************************
;
; EIG - External to internal graphic coordinate conversion
; ===
;
; Convert external coordinates (either relative or absolute) into
; internal ones. No windowing is done.
;
;
; On entry, R0 (X), R1 (Y) hold coordinate (external co-ords)
;           R2 holds plot mode, where bit2 = 0 for relative (add ECursor)
;                                          = 1 for absolute (add origin)
;
; On exit,  R0 (X), R1 (Y) hold internal representation
;           R2 preserved
;
;           R3 corrupt
;
;           The external cursor (GCsX,GCsY) is updated to the new point,
;           hence in triangle relative mode, the points are relative to
;           the last point specified, not the original.
;
EIG
                                ;Do Ycoord first
        TST R2,#4               ;If bit2 is clear
        LDREQ R3,[WsPtr,#GCsY]  ;then co-ord is relative
        ADDEQ R1,R1,R3          ;     so add previous cursor (ext. rep.)

        STR R1,[WsPtr,#GCsY]    ;Update previous cursor
        LDR R3,[WsPtr,#OrgY]    ;Add origin
        ADD R1,R1,R3

        LDR R3, [WsPtr, #YEigFactor]
        MOV R1,R1,ASR R3        ;Transform 0-1023 to 0-255 or 0-511

                                ;Do Xcoord
        LDREQ R3,[WsPtr,#GCsX]  ;If relative then
        ADDEQ R0,R0,R3          ;  add previous cursor (ext. rep.)

        STR R0,[WsPtr,#GCsX]    ;Update previous cursor
        LDR R3,[WsPtr,#OrgX]    ;Add origin
        ADD R0,R0,R3

        LDR R3,[WsPtr,#XEigFactor]
        MOV R0,R0,ASR R3        ;Transform 0-1279 to 0-639/0-319/0-159

        MOV PC,R14

; *****************************************************************************
;
; IEG - Inverse of EIG. Convert ICursor to ECursor
; ===
;
; On exit, R0 (X), R1 (Y) holds ECursor, ECursor updated
;       R2, R3 hold XEigFactor, YEigFactor
;       R4, R5 corrupt
;
        ASSERT  GCsIY = GCsIX +4
        ASSERT  YEigFactor = XEigFactor +4
        ASSERT  OrgY = OrgX +4
        ASSERT  GCsY = GCsX +4
IEG
        ADD     R0, WsPtr, #GCsIX
        LDMIA   R0, {R0,R1}             ; load graphics cursor (internal)
IEGB
        ADD     R2, WsPtr, #XEigFactor
        LDMIA   R2, {R2, R3}            ; R2 = XEigFactor; R3 = YEigFactor
        ADD     R4, WsPtr, #OrgX
        LDMIA   R4, {R4, R5}            ; R4 = OrgX; R5 = OrgY
        RSB     R0, R4, R0, LSL R2      ; R0 = (X << XEigFactor)-OrgX
        RSB     R1, R5, R1, LSL R3      ; R1 = (Y << YEigFactor)-OrgY
        ADD     R4, WsPtr, #GCsX
        STMIA   R4, {R0,R1}             ; write graphics cursor (external)

        MOV PC,R14

        [ {FALSE}
; *****************************************************************************
;
; Window - Check a coordinate against the graphics window
; ======
;
; On entry, R0 (X), R1 (Y) holds coordinate to window
; On exit,  R0,R1 preserved,
;           R3 corrupt
;           R2 holds result, as follows:
;
;                |      |
;           1001 | 1000 | 1010
;                |      |
;           -----+------+-----
;                |      |
;           0001 | 0000 | 0010
;                |      |
;           -----+------+-----
;                |      |
;           0101 | 0100 | 0110
;                |      |
;
;
;
Window
        MOV R2,#0
        LDR R3,[WsPtr,#GWBRow]          ;Test ycoord against window
        CMP R1,R3
        ORRLT R2,R2,#4                  ;Set bit 2 if Y < window

        LDR R3,[WsPtr,#GWTRow]
        CMP R3,R1
        ORRLT R2,R2,#8                  ;Set bit 3 if Y > window

        LDR R3,[WsPtr,#GWLCol]          ;Test xcoord against window
        CMP R0,R3
        ORRLT R2,R2,#1                  ;Set bit 0 if X < window

        LDR R3,[WsPtr,#GWRCol]
        CMP R3,R0
        ORRLT R2,R2,#2                  ;Set bit 1 if X > window

        MOV PC,R14

        ]
; *****************************************************************************
;
; ScreenAddr - Generate screen address of coordinate
; ==========
;
; On entry, R0 (X), R1 (Y) holds coordinate - must be within graphics window
; On exit,  R0,R1 preserved,
;           R2 holds word address
;           R3 holds pixel mask
;
;           R7,R8 corrupt
ScreenAddr
        ASSERT  LineLength = YWindLimit +4

        ADD     R7, WsPtr, #YWindLimit
        LDMIA   R7, {R7,R8}                     ; R7=YWindLimit,R8=LineLength
        SUB     R2, R7, R1                      ; flip ycoord into R2

        LDR     R7, [WsPtr, #ScreenStart]       ; add the screen start
        MLA     R2, R8, R2, R7                  ; to Ycoord * bytes per row

        LDR     R3, [WsPtr, #XShftFactor]       ; R7 := 2,3,4 or 5
        MOV     R8, R0, ASR R3                  ; R8 := word offset
        ADD     R2, R2, R8, LSL #2              ; add on to screen address

        EOR     R7, R0, R8, LSL R3              ; R7 := pixel offset
        ADD     R3, WsPtr, #RAMMaskTb
        LDR     R3, [R3, R7, LSL #2]            ; R3 := mask for this pixel

        MOV     PC, R14

; *****************************************************************************
;
; PlotPoint - Plot a point in the current colour
; =========
;
; On entry, R0 (X), R1 (Y) holds coordinate of point
; On exit,  R0,R1 preserved
;           R2-R8 corrupt
;           R9-R11 preserved
;

PlotPoint
        WINDow  R0,R1, R2,R3,R4,R5
        MOVLT   PC, R14                         ; If outside window, give up

        ASSERT  LineLength = YWindLimit +4

        ADD     R7, WsPtr, #YWindLimit
        LDMIA   R7, {R7,R8}                     ; R7=YWindLimit; R8=LineLength
        SUB     R2, R7, R1                      ; flip ycoord into R2

; *****Change made by DJS
; Original code was:
;        ASSERT  GColAdr = XShftFactor +4
;        ASSERT  ScreenStart = XShftFactor +8
;
;        ADD     R4, WsPtr, #XShftFactor         ; R4=XShftFactor; R5=GColAdr
;        LDMIA   R4, {R4, R5, R7}                ; R7=ScreenStart
;
;        MLA     R2, R8, R2, R7                  ; R2=ScreenStart+Y*LineLength
;        MOV     R8, R0, ASR R4                  ; R8 := XCoord DIV 4,8,16
;                                                ;   or 32
;        EOR     R7, R0, R8, LSL R4              ; R7 := pixel offset in word
;        ADD     R3, WsPtr, #RAMMaskTb
;        LDR     R3, [R3, R7, LSL #2]            ; R3 := mask for this pixel
;
;        EOR     R6, R1, #7                      ; flip Ycoord
;        AND     R6, R6, #7                      ; line within ecf
; This does not calculate the ecf line correctly, resulting in bugs if (e.g.)
; a destination sprite is not a multiple of 8 pixels high.

        AND     R6, R2, #7                      ; R6 := line with ecf

        ASSERT  GColAdr = XShftFactor +4
        ASSERT  ScreenStart = XShftFactor +8

        ADD     R4, WsPtr, #XShftFactor         ; R4=XShftFactor; R5=GColAdr
        LDMIA   R4, {R4, R5, R7}                ; R7=ScreenStart

        MLA     R2, R8, R2, R7                  ; R2=ScreenStart+Y*LineLength
        MOV     R8, R0, ASR R4                  ; R8 := XCoord DIV 4,8,16 or 32
        EOR     R7, R0, R8, LSL R4              ; R7 := pixel offset in word
        ADD     R3, WsPtr, #RAMMaskTb
        LDR     R3, [R3, R7, LSL #2]            ; R3 := mask for this pixel

; *****End of change made by DJS

        ADD     R5, R5, R6, LSL #3
        LDMIA   R5, {R5, R6}                    ; get zgora,zgeor

        AND     R5, R5, R3                      ; mask zgora
        AND     R6, R6, R3                      ; & zgeor for this pixel
 [ AvoidScreenReads
        CMP     R5, #&FFFFFFFF
        LDRNE   R7, [R2, R8, LSL #2]
 |
        LDR     R7, [R2, R8, LSL #2]
 ]
        ORR     R7, R7, R5                      ; and hit the screen
        EOR     R7, R7, R6
        STR     R7, [R2, R8, LSL #2]
        MOV     PC, R14

; *****************************************************************************
;
; ComplexEcfPattern
; =================
;
; On entry, R0 holds pattern number, where 2..6 means Ecf1..4
;
; Vdu queue holds
;
;          QQ+0  pattern number
;          QQ+1  pattern bytes 0
;           |                  |
;          QQ+8                7
;
; ComplexEcfPat10 is an entry point from simple Ecf setting for 8 bpp modes
;                 ie simple=complex in 8 bpp modes
;                 R1 points to Ecf(n) to be programmed.
;
; Corrupts R0..R3
;

ComplexEcfPattern ROUT                  ; R0 holds 2,3,4,5 for Ecfs 1,2,3,4
        ADD     R1, WsPtr, #(Ecf1-2*8)
        ADD     R1, R1, R0, LSL #3      ; point R1 at Ecf(n)
ComplexEcfPat10
        ADD     R0, WsPtr, #(QQ+1)
        LDR     R2, [WsPtr, #BBCcompatibleECFs] ; if in BBC mode
        CMP     R2, #0
        BEQ     ComplexEcfPat20                 ; then unmangle the interleaved
                                                ;      pixels
        LDMIA   R0, {R2,R3}                     ; else (native) use as given
        STMIA   R1, {R2,R3}
        B       SetColour       ; update FgEcf & BgEcf incase they use this Ecf

; R1 points at Ecf(n)
; R0 points at QQ+1
;
; Uses R2 - pointer into InterleaveTB(BitsPerPix)
;      R3 - byte to process 7..0
;      R4 - #1
;      R5 - bit within byte to process 7..0
;      R6 - result byte
;      R7 - byte from queue
;      R8 - bit mask from InterleaveTB(BitsPerPix,R5)

ComplexEcfPat20
        ADR     R2, InterleaveTB
        LDR     R3, [WsPtr, #Log2BPP]   ; 0,1,2,3 means 1,2,4,8 bits per pixel
        ADD     R2, R2, R3, LSL #3      ; point R2 at entry in interleave table
        MOV     R3, #7                  ; 7..0 bytes to process
        MOV     R4, #1
ComplexEcfPat30
        MOV     R5, #7                  ; 7..0 bits per byte
        MOV     R6, #0                  ; clear result byte
        LDRB    R7, [R0, R3]            ; byte from queue
ComplexEcfPat40
        LDRB    R8, [R2, R5]
        TST     R8, R7
        ORRNE   R6, R6, R4, LSL R5      ; set bit in result byte
        SUBS    R5, R5, #1
        BGE     ComplexEcfPat40         ; process next bit

        STRB    R6, [R1, R3]            ; write de-interleaved byte to Ecf(n)
        SUBS    R3, R3, #1
        BGE     ComplexEcfPat30         ; process next byte from queue

        B       SetColour       ; update FgEcf & BgEcf incase they use this Ecf

;
; InterleaveTB - Values used to unpack BBC style interleaved pixels
;
InterleaveTB
; 1 bit  per pixel eg Mode 0 & 4 - Log2BPP is 0
        =       &80, &40, &20, &10, &08, &04, &02, &01
; 2 bits per pixel eg Mode 1 & 5 - Log2BPP is 1
        =       &08, &80, &04, &40, &02, &20, &01, &10
; 4 bits per pixel eg Mode 2 - Log2BPP is 2
        =       &02, &08, &20, &80, &01, &04, &10, &40
; 8 bits per pixel - no effect - Log2BPP is 3
        =       &01, &02, &04, &08, &10, &20, &40, &80

        ALIGN

; *****************************************************************************
;
; LineStyle - Setup dotted line style (does not affect repeat length)
; =========
;
; Corrupts R0..R3
;

LineStyle ROUT
        MOV     R3, #8                          ; Copy 8 bytes in reverse order
        ADD     R1, WsPtr, #QQ+9
        ADD     R2, WsPtr, #DotLineStyle        ; from Queue into DotLineStyle
LineSty10
        LDRB    R0, [R1, #-1]!
        STRB    R0, [R2], #1
        SUBS    R3, R3, #1
        BNE     LineSty10
        MOV     PC, R14

        LTORG   ;limited offsets

; *****************************************************************************
;
;       DefaultLineStyle - Setup default line style and length
;
;       Internal routine, called by mode change, SwitchOutputToSprite and
;        FX163,242,n code
;
; out:  Can corrupt R0-R3,
;       PSR preserved
;

DefaultLineStyle ROUT
        MRS     R3, CPSR
DefaultLineStylePSRready
        MOV     R0, #8                  ; dot pattern repeats after 8 pixels
        STR     R0, [WsPtr, #DotLineLength]
        LDR     R0, =&AAAAAAAA          ; on-off pattern
        STR     R0, [WsPtr, #DotLineStyle+0] ; 64 bits of pattern
        STR     R0, [WsPtr, #DotLineStyle+4]
        MOV     R0, #0                  ; force a restart of the pattern
        STR     R0, [WsPtr, #LineDotCnt] ; if the user does a "continue"
        MSR     CPSR_f, R3
        MOV     PC, R14

; *****************************************************************************
;
;       SetPatLength - Set dotted line repeat length
;
;       Internal routine, called by DoOsbyte163_242
;
; in:   R2 = 1..64 => set pattern length to this
;            0     => set default length and pattern
;
; out:  Can corrupt R0-R3,
;       PSR preserved
;

SetPatLength ROUT
        MRS     R3, CPSR
        CMP     R2, #0
        BEQ     DefaultLineStylePSRready
        STR     R2, [WsPtr, #DotLineLength]     ; 1..64
        MOV     R2, #0                          ; force restart of the pattern
        STR     R2, [WsPtr, #LineDotCnt]        ; if the user does a "continue"
        MSR     CPSR_f, R3
        MOV     PC, R14


; *****************************************************************************
;
;       GetECFIndex - Return index of default ECF patterns for mode
;
;       Out:
;       R0 = table index
;       R2 corrupt
;

GetECFIndex ROUT
        ; Non-graphic modes always get index 0 (empty patterns)
        LDR     R0, [WsPtr, #ModeFlags]
        TST     R0, #ModeFlag_NonGraphic
        MOVNE   R0, #0
        MOVNE   PC, LR
        ; Other modes are dependent on BPP
        LDR     R0, [WsPtr, #Log2BPP]
        ; Extra hassle for 1bpp, we want two different patterns depending on pixel aspect ratio
        CMP     R0, #0
        LDREQ   R2, [WsPtr, #AspectRatio]
        CMPEQ   R2, #2
        MOVEQ   R0, #1
        LDRNEB  R0, [PC, R0] ; 1 or 4 for 1bpp
        MOV     PC, LR
        DCB     4, 2, 3, 5, 5, 5 ; 1bpp-32bpp
        ALIGN

; *****************************************************************************
;
;       DefaultEcfPattern - Setup all 4 ecf patterns for this mode
;       (does not affect line style)
;
;       Internal routine, called by mode change, SwitchOutputToSprite and
;        VDU 23_11
;

DefaultEcfPattern ROUT
        MOV     R0, #0
        STR     R0, [WsPtr, #BBCcompatibleECFs] ;Select BBC compatible ECF mode

        Push    "lr"
        BL      GetECFIndex
        Pull    "lr"
        ADR     R1, DefEcfTb
        ADD     R0, R1, R0, LSL #4      ; 16 bytes per entry
        ADD     R1, WsPtr, #Ecf1

        LDMIA   R0, {R2,R4,R6,R8}       ; Get all four patterns for each mode
        LDMIA   R0, {R3,R5,R7,R9}       ; duplicate top half into bottom half
        STMIA   R1, {R2-R9}             ; Write all 4 Ecfs
        B       SetColour       ; update FgEcf & BgEcf in case they are Ecfs


DefEcfTb        ; Table of default ECFs, indexed by ECFIndex

; Modes 3,6,7 (i.e. non-graphic modes)
 & 0
 & 0
 & 0
 & 0

; Mode 0 (i.e. 1:2 eigen 1bpp modes)
 & &00330033     ;Dark grey      (3 black, 1 white)
 & &CC33CC33     ;Grey           (2 black, 2 white)
 & &CCFFCCFF     ;Light grey     (1 black, 3 white)
 & &030C30C0     ;Hatching

; Modes 1,5,8,11,19 (i.e. 2bpp modes)
 & &55665566     ;Red orange     (3 red, 1 yellow)
 & &99669966     ;Orange         (2 red, 2 yellow)
 & &99AA99AA     ;Yellow orange  (1 red, 3 yellow)
 & &BBEEBBEE     ;Cream          (2 white, 2 yellow)

; Modes 2,9,12,14,16,17,20(,21) (i.e. 4bpp modes)
 & &31133113     ;Orange         (2 red, 2 yellow)
 & &51155115     ;Pink           (2 red, 2 magenta)
 & &32233223     ;Yellow green   (2 green, 2 yellow)
 & &37733773     ;Cream          (2 white, 2 yellow)

; Modes 4,18,22,23 (i.e. square pixel 1bpp modes)
 & &00550055     ;Dark grey      (3 black, 1 white)
 & &AA55AA55     ;Grey           (2 black, 2 white)
 & &AAFFAAFF     ;Light grey     (1 black, 3 white)
 & &11224488     ;Hatching

; Modes 10,13,15 (>= 8bpp modes)
 & &FFFEFDFC     ;Its runnier than yoy'll lik itt
 & &00010203
 & &20212223
 & &DFDEDDDC

; *****************************************************************************
;
;       SimpleEcfPattern - Setup simple ecf pattern
;
;       Internal routine, called by VDU23_12..15
;
; in:   R0 = 12..15 for ecfs 1..4
;       Vdu queue holds
;          QQ+0  pattern number
;          QQ+1  pattern bytes 0
;           |                  |
;          QQ+8                7
;

SimpleEcfPattern ROUT
        ADD     R1, WsPtr, #(Ecf1-12*8)
        ADD     R1, R1, R0, LSL #3      ; Point R1 at ECF(n)
        ADD     R2, WsPtr, #(QQ+1)      ; Point R2 at first colour in queue
        ADR     R3, SimpEcfTb
        Push    "lr"
        BL      GetECFIndex
        LDRB    R10, [R3, R0]           ; Mask for this mode
        Pull    "lr"
        CMP     R10, #&FF
        BEQ     SimplEc20               ; 256 colour modes are different
        LDR     R5, [WsPtr, #NColour]
        ADRL    R4, TBFullCol           ; Base of full colour table (of bytes)
        ADD     R4, R4, R5              ; Access to table is always
                                        ; TBFullCol(NColour+(byte AND NColour))
                                        ; so add NColour in now !
        MOV     R7, #4                  ; There are 4 pairs of bytes in queue
SimplEc10
        LDRB    R6, [R2], #1            ; Get first colour of pair from queue
        AND     R6, R6, R5              ; (byte AND NColour)
        LDRB    R6, [R4, R6]            ; The full byte for this colour
        AND     R6, R6, R10             ; Extract required pixels
        LDRB    R9, [R2], #1            ; Get next colour of pair from queue
        AND     R9, R9, R5              ; (byte AND Ncolour)
        LDRB    R9, [R4, R9]
        BIC     R9, R9, R10             ; Extract required pixels and
        ORR     R6, R6, R9              ; build into final pattern
        STRB    R6, [R1, #4]            ; Replicate pattern in both halves
        STRB    R6, [R1], #1            ; of ECF
        SUBS    R7, R7, #1
        BNE     SimplEc10
        B       SetColour       ; update FgEcf & BgEcf in case they use this

; R1 -> ECF(n)
; R2 -> first colour in queue

SimplEc20
        Push    R14
        MOV     R5, #8
SimplEc30
        LDRB    R0, [R2], #1            ; Get byte from queue
        AND     R3, R0, #&C0            ; Extract Tint
        AND     R0, R0, #&3F            ;         Colour

        BL      AddTintToColour         ; Recombine in our funny fashion
        STRB    R0, [R1], #1            ; And store in the Ecf
        SUBS    R5, R5, #1
        BNE     SimplEc30
        Pull    R14
        B       SetColour       ; Update FgEcf & BgEcf incase they use this Ecf

SimpEcfTb       ; Table of masks for simple ECFs, indexed by ECFIndex
                ; (bit set => use left pixel, bit clear => use right pixel)

        =       &00     ; Modes 3,6,7
        =       &33     ; Mode 0
        =       &33     ; Modes 1,5,8,11,19
        =       &0F     ; Modes 2,9,12,14,16,17,20
        =       &55     ; Modes 4,18,21,22
        =       &FF     ; Modes 10,13,15

        ALIGN

; *****************************************************************************
;
;       ExportedHLine - Routine exported via VDU variable HLineAddr
;
; in:   R0,R2 = X coords in some order (internal coords)
;       R1 = Y coord (internal coord)
;       R3 = 0 => no effect
;       R3 = 1 => foreground colour/action
;       R3 = 2 => invert
;       R3 = 3 => background colour/action
;       R3 >= 4 => R3 -> eight word pairs
;

ExportedHLine ROUT
        Push    "R0,R2,R4-R10,WsPtr,R14"
        VDWS    WsPtr
        CMP     R3, #4
        BCS     %FT10

; *****Change made by DJS
;   Original code was:
;        MOV     R3, R3, LSL #30         ; put bottom 2 bits into top 2 bits
;        CMP     R3, #&40000000          ; set lots of flags
;
;        ADDMI   R3, WsPtr, #BgEcfOraEor ; if 0 or 3
;        ADRCC   R3, NoEffect            ; if 0
;        ADDEQ   R3, WsPtr, #FgEcfOraEor ; if 1
;        ADRVS   R3, Invert              ; if 2

        MOVS    R3, R3, LSL #31         ;Put bit 1 in C, NOT(bit 0) in Z
        ADR     R3, NoEffect            ; if 0, 1, 2 or 3
        ADDCS   R3, R3,#Invert-NoEffect ; if 2 or 3
        ADDNE   R3, WsPtr, #FgEcfOraEor ; if 1 or 3
        ADDHI   R3, WsPtr, #BgEcfOraEor ; if CS & NE - i.e. 3

; *****End of change made by DJS

10
        STR     R3, [WsPtr, #GColAdr]   ; save address of Ecf to plot with
        BL      HLine
        CLRV
        Pull    "R0,R2,R4-R10,WsPtr,PC"

; *****************************************************************************
;
; NewHLine - Horizontal line draw   an even newer version what does 8 words
; ========                          at a time if it can
;
; On entry, R0 (X), R1 (Y) holds coordinate of point
;           R2             holds right hand XCoord
; On exit,  R3 preserved
;           R4..R10 corrupt
;           PSR preserved
;
; N.B. R11 WILL BE PRESERVED - This is assumed by rectangle fill and all circle
;                              operations
;

HLine
        SortT   R0, R2, R4              ; Sort the Xcoord into order R0<=R2

NewHLine ROUT                           ; Entry point for sorted Xcoords

        ADD     R4, WsPtr, #GWLCol
        LDMIA   R4, {R4-R7}             ; GWLCol,GWBRow,GWRCol,GWTRow

        CMP     R7, R1                  ; Test ycoord against window
        CMPGE   R1, R5

        CMPGE   R6, R0                  ; Test xcoords against window
        CMPGE   R2, R4
        MOVLT   PC, R14                 ; Quit if above,below,left or right
                                        ; of window

        Greatest R0, R0, R4             ; If start of line to left of window
                                        ; pull to window edge
        Least   R2, R2, R6              ; If end of line to right of window
                                        ; pull to window edge

        Push    "R0-R3,R11,Link"

;
; Now, R0 holds Start (leftX), R1 holds Y and R2 holds End (rightX)
;
; Now pick up ecf word and form the ZGORA & ZGEOR masks
;

        ASSERT  LineLength = YWindLimit +4
        ASSERT  GColAdr = XShftFactor +4
        ASSERT  ScreenStart = XShftFactor +8

        ADD     R11, WsPtr, #YWindLimit
        LDMIA   R11, {R10-R11}          ; R10 = YWindLimit; R11 = LineLength

        ADD     R9, WsPtr, #XShftFactor
        LDMIA   R9, {R7-R9}             ; R7 = XShftFactor; R8 = GColAdr
                                        ; R9 = ScreenStart

        SUB     R1, R10, R1             ; flip ycoord into R1
        AND     R6, R1, #7              ; R6 = line within ecf
        ADD     R8, R8, R6, LSL #3      ; R8 -> zgora, zgeor
        LDMIA   R8, {R8, R10}           ; R8 = zgora; R10 = zgeor

        MLA     R9, R11, R1, R9         ; R9 -> start of this scan line

        MOV     R6, R0, LSR R7          ; R6 = X1Coord word offset
        MOV     R11, R2, LSR R7         ; R11 = X2Coord word offset
        ADD     R9, R9, R6, LSL #2      ; R9 = address of lefthand word

; *****Change made by DJS
; Original code was:
;
;        EOR     R4, R0, R6, LSL R7      ; R4 = X1 pixel offset
;        EOR     R5, R2, R11, LSL R7     ; R5 = X2 pixel offset
;
;        SUBS    R11, R11, R6            ; R11 = number of words -1
;                                        ; and set Z on it
;
;        ADD     R7, WsPtr, #RAMMaskTb   ; R7 -> MaskTb for this mode
;        LDR     R6, [R7, R4, LSL #2]    ; R6 = left mask
;        LDR     R14, [R7, R5, LSL #2]   ; R14 = right mask
;
;        SUB     R7, R14, #1             ; in right mask set all bits lower
;        ORR     R14, R14, R7            ; by RM = RM OR (RM-1)
;
;        RSB     R7, R6, #0              ; in left mask, set all bits higher
;        ORR     R6, R6, R7              ; by LM = LM OR (-LM)
;
; The following code is shorter and faster.

        SUBS    R11, R11, R6            ; R11 = number of words -1
                                        ; and set Z on it

        RSB     R7, R7, #5              ; R7 = Log2BPC (quicker than loading!)
        MOV     R14, #31                ; constant to extract bit offsets
        AND     R4, R14, R0, LSL R7     ; R4 = start of X1 pixel offset
        AND     R5, R14, R2, LSL R7     ; R5 = start of X2 pixel offset
        MOV     R14, #-1                ; Useful both as -1 and as &FFFFFFFF
        SUB     R5, R5, R14, LSL R7     ; R5 = end of X2 pixel offset
        MOV     R6, R14, LSL R4         ; R6 = left mask
        MVN     R14, R14, LSL R5        ; R14 = right mask

; *****End of change made by DJS

        ANDEQ   R14, R14, R6            ; if start word = end word
        BEQ     %FT40                   ; then combine masks

 [ AvoidScreenReads
        AND     R1, R8, R6              ; zgora AND left mask
        CMP     R1, #&FFFFFFFF
        LDRNE   R0, [R9]                ; do left hand word of line
 |
        LDR     R0, [R9]                ; do left hand word of line
        AND     R1, R8, R6              ; zgora AND left mask
 ]
        AND     R2, R10, R6             ; zgeor AND left mask
        ORR     R0, R0, R1              ; screen OR or mask
        EOR     R0, R0, R2              ; EOR eor mask
        STR     R0, [R9], #4            ; store to screen

        SUBS    R11, R11, #1            ; decrement word count, if =0
        BEQ     %FT40                   ; then plot RH partial word

        CMP     R8, #-1                 ; if R8 = -1 then store action
        BNE     %FT05                   ; else do it the slow way

        MVN     R0, R10                 ; R0 = word of colour to plot
        MOV     R1, R0
        MOV     R2, R0
        MOV     R3, R0
        ADDS    R8, R8, R11, LSR #3     ; R8 = (no. of words DIV 8)-1
        BCC     %FT60                   ; must be fewer than 8
        MOV     R4, R0
        MOV     R5, R0
        MOV     R6, R0
        MOV     R7, R0

; *****Additional message inserted by DJS

        [       ((.-KernelBase) :AND: 15) = 12
        !       0, "HLine critical loop has bad alignment for running in RAM"
        ]

; *****End of message inserted by DJS

50
        STMCSIA R9!, {R0-R7}            ; write 8 words to screen if >=0
        STMHIIA R9!, {R0-R7}            ; write 8 words to screen if > 0
        SUBS    R8, R8, #2              ; try for another 16
        BCS     %BT50
60
        TST     R11, #4                 ; can we do 4 words ?
        STMNEIA R9!, {R0-R3}            ; write 4 words to screen
        TST     R11, #2                 ; can we do 2 words ?
        STMNEIA R9!, {R0-R1}            ; write 2 words to screen
        TST     R11, #1                 ; can we do 1 word ?
        STRNE   R0, [R9], #4            ; write 1 word to screen
        AND     R10, R10, R14           ; do partial word at end
 [ AvoidScreenReads
        CMP     R14, #&FFFFFFFF
        LDRNE   R0, [R9]
 |
        LDR     R0, [R9]
 ]
        OrrEor  R0,R0, R14,R10
        STR     R0, [R9],#4
70
        Pull    "R0-R3,R11,PC"

; code for when not store action

05
        SUBS    R11, R11, #8            ; else try for 8 words at a time
        BMI     %FT20                   ; failed, so try for 4 at a time
10
        LDMIA   R9, {R0-R7}
        OrrEor  R0,R0, R8,R10           ; Write 8 words to screen
        OrrEor  R1,R1, R8,R10
        OrrEor  R2,R2, R8,R10
        OrrEor  R3,R3, R8,R10
        OrrEor  R4,R4, R8,R10
        OrrEor  R5,R5, R8,R10
        OrrEor  R6,R6, R8,R10
        OrrEor  R7,R7, R8,R10
        STMIA   R9!, {R0-R7}
        SUBS    R11, R11, #8            ; try for another 8
        BPL     %BT10

20
        ADDS    R11, R11, #4            ; can we do 4 words ?
        BMI     %FT30                   ; no, then do 1 word at a time

        LDMIA   R9, {R0-R3}
        OrrEor  R0,R0, R8,R10           ; Write 4 words to screen
        OrrEor  R1,R1, R8,R10
        OrrEor  R2,R2, R8,R10
        OrrEor  R3,R3, R8,R10
        STMIA   R9!, {R0-R3}

        SUB     R11, R11, #4
30
        ADDS    R11, R11, #4            ; Correct for earlier SUB #4

40                                      ; Plot single words
        ANDEQ   R8, R8, R14             ; If EQ, this is RH word,
        ANDEQ   R10, R10, R14           ; so mask down to required pixels
        LDR     R0, [R9]
        OrrEor  R0,R0, R8,R10
        STR     R0, [R9],#4

        SUBS    R11,R11,#1
        BPL     %BT40

        Pull    "R0-R3,R11,PC"

; *****************************************************************************
;
;       NewVLine - Vertical line draw for non-dotted solid pattern lines
;
; in:   R0 = R2 = X coord
;       R1 = bottom Y coord
;       R3 = top Y coord
;
; out:  R4-R10 corrupt
;       PSR preserved
;

NewVLine ROUT                           ; Entry point for sorted Ycoords

        ADD     R4, WsPtr, #GWLCol
        LDMIA   R4, {R4-R7}             ; GWLCol,GWBRow,GWRCol,GWTRow

        CMP     R7, R1                  ; Test ycoord against window
        CMPGE   R3, R5

        CMPGE   R6, R0                  ; Test xcoords against window
        CMPGE   R2, R4
        MOVLT   PC, R14                 ; Quit if above,below,left or right
                                        ; of window

        Greatest R1, R1, R5             ; If bottom of line below window
                                        ; pull to window edge
        Least   R3, R3, R7              ; If top of line above window
                                        ; pull to window edge


        Push    "R0-R3,R11,Link"

; Now, R0 holds X, R1 holds bottom Y and R3 holds top Y
;
; Now pick up ecf word and form the ZGORA & ZGEOR masks
;

        ASSERT  LineLength = YWindLimit +4
        ASSERT  GColAdr = XShftFactor +4
        ASSERT  ScreenStart = XShftFactor +8

        ADD     R11, WsPtr, #YWindLimit
        LDMIA   R11, {R10-R11}          ; R10 = YWindLimit; R11 = LineLength

        ADD     R9, WsPtr, #XShftFactor
        LDMIA   R9, {R7-R9}             ; R7 = XShftFactor; R8 = GColAdr
                                        ; R9 = ScreenStart
        SUB     R1, R3, R1              ; R1 = number of dots to do -1

        SUB     R3, R10, R3             ; R3 = flipped top ycoord
        MLA     R9, R11, R3, R9         ; R9 -> start of this scan line

        LDMIA   R8, {R8, R10}           ; R8 = zgora; R10 = zgeor
                                        ; (no need to index with Y cos we
                                        ; know there's no ECF pattern)

        MOV     R6, R0, LSR R7          ; R6 = X coord word offset
        ADD     R9, R9, R6, LSL #2      ; R9 = address of top word

        EOR     R4, R0, R6, LSL R7      ; R4 = X pixel offset

        ADD     R7, WsPtr, #RAMMaskTb   ; R7 -> MaskTb for this mode
        LDR     R6, [R7, R4, LSL #2]    ; R6 = pixel mask
        AND     R8, R8, R6              ; zgora = zgora AND pixelmask
        AND     R10, R10, R6            ; zgeor = zgeor AND pixelmask

 [ AvoidScreenReads
        CMP     R8, #&FFFFFFFF
        BEQ     %FT20
 ]

; now do the plotting

10
        LDR     R6, [R9]
        ORR     R6, R6, R8
        EOR     R6, R6, R10
        STR     R6, [R9], R11
        SUBS    R1, R1, #1
        BPL     %BT10

        Pull    "R0-R3,R11,PC"

 [ AvoidScreenReads
20      MVN     R6, R10
25      STR     R6, [R9], R11
        SUBS    R1, R1, #1
        BPL     %BT25
        Pull    "R0-R3,R11,PC"
 ]


; *****************************************************************************
;
; DoubleHLine - Draw 2 horizontal lines
; ===========
;
; On entry, R0 (X) - Left most point
;           R1 (Y) - y ordinate of line
;           R2 (X) - Right most point
;           R3 (X) - end of left most line
;           R4 (X) - start of right most line
;
; On exit,  R0..R10 preserved  } subject
;           R11 corrupt        }  to change
;
DoubleHLine
        Push    "R0-R10, R14"
        MOV     R2, R3                  ; draw left line, R0->R3 inc.
        BL      HLine
        LDMIB   R13, {R1-R4}
        MOV     R0, R4                  ; draw right line, R4->R2 inc.
        BL      HLine
        Pull    "R0-R10, PC"

; *****************************************************************************
;
;       ExtractTintAndColour - Convert 256-colour mode byte value into
;        colour and tint
;
;       Internal routine, called by ReadPixelColour, SwiReadPoint
;
; in:   R0 = single screen pixel (ie 'half' user pixel in double modes)
;
; out:  R0 corrupt
;       R2 = colour value (GCOL a,colour)
;       R3 = tint Vdu 23,17 etc
;

ExtractTintAndColour ROUT
                                        ; R0 := B3 G3 G2 R3 B2 R2 T1 T0
        MOV     R3, R0, LSL #6          ; R3 := T1 T0  0  0  0  0  0  0
        AND     R3, R3, #&C0
        AND     R2, R0, #&84            ; R2 := B3  0  0  0  0 R2  0  0
        TST     R0, #8
        ORRNE   R2, R2, #&40            ; R2 := B3 B2  0  0  0 R2  0  0
        AND     R0, R0, #&70            ; R0 :=  0 G3 G2 R3  0  0  0  0
        ORR     R2, R2, R0, LSR #1      ; R2 := B3 B2 G3 G2 R3 R2  0  0
        MOV     R2, R2, LSR #2          ; R2 :=  0  0 B3 B2 G3 G2 R3 R2
        MOV     PC, R14

; *****************************************************************************
;
;       SwiReadPoint - Read colour of screen pixel
;
;       External routine - entry point for SWI OS_ReadPoint
;
; in:   R0, R1 = X, Y coordinate of point
;
; out:  R2 = colour (as in GCOL a,colour)       } -1 if off screen
;       R3 = tint value (as in VDU 23,17 etc)   }  0 if off screen
;       R4 = 0/-1 for On/Off screen
;       R0,R1, R5-R9 preserved (R10-R12 preserved by MOS)
;

SwiReadPoint ROUT
        WritePSRc SVC_mode, WsPtr       ; re-enable interrupts
        VDWS    WsPtr                   ; point R12 at vdu driver workspace

      [ {TRUE}
        GraphicsMode R10                ; if not a graphics mode then give up now!
        BNE     %FT20
      |
        LDR     R10, [WsPtr, #NPix]     ; if not a graphics mode then
        CMP     R10, #0                 ; indicate off screen
        BEQ     %FT20
      ]

        Push    "R0-R9, R14"            ; save registers

        ADD     R7, WsPtr, #GCsX
        LDMIA   R7, {R8,R9}             ; preserve GCsX,GCsY around EIG
        MOV     R2, #4                  ; absolute coord
        BL      EIG
        STMIA   R7, {R8,R9}             ; restore GcsX,GCsY

        WINDow  R0,R1, R2,R3,R4,R5
        BLT     %FT10                   ; outside window

        Push    "R0,R1"                 ; save internal coords
        BL      PreWrchCursor           ; remove any split cursors etc
        Pull    "R0,R1"
                                        ;     R0,R1,R2 ,R3 ,R4 ,R5 ,R6 ,R7 ,R8
        BL      ScreenAddr              ; in :X ,Y
                                        ; out:X ,Y ,Adr,Msk,   ,   ,   ,crp,crp
        LDR     R8, [WsPtr, #NColour]

;amg - changes here to cope with 16/32bpp which will also return an NCOL with b4-b7 set

;        TST     R8, #&F0                ; if NColour=63
;        MOVNE   R8, #&FF                ; then use 255

        CMP     R8,#63
        MOVEQ   R8,#255

        LDR     R9, [WsPtr, #XShftFactor]
        LDR     R10, [WsPtr, #NPix]
        LDR     R11, [WsPtr, #Log2BPC]

        BitLOffset R7,R0, R9,R10,R11    ; R7 := bit position to align to

        LDR     R0, [R2]
        AND     R0, R8, R0, LSR R7      ; extract one pixel

        TEQ     R8,#255
        MOVNE   R2,R0
        MOVNE   R3,#0
        BLEQ    ExtractTintAndColour

;        TST     R8, #&F0                ; if not a 256 colour mode
;        MOVEQ   R2, R0                  ; colour = pixel
;        MOVEQ   R3, #0                  ; tint   = 0
;        BLNE    ExtractTintAndColour    ; else extract colour & tint from pixel

        MOV     R4, #0                  ; indicate on screen

        ADD     R0, R13, #2*4           ; point to stacked R2
        STMIA   R0, {R2-R4}

        BL      PostWrchCursor

        Pull    "R0-R9, R14"            ; restore R0-R9 & return address
        BIC     R14, R14, #V_bit
        ExitSWIHandler

10
        Pull    "R0-R9, R14"            ; restore R0-R9 & return address
20
        MOV     R2, #-1
        MOV     R3, #0
        MOV     R4, #-1

        BIC     R14, R14, #V_bit
        ExitSWIHandler

; *****************************************************************************
;
;       GenCircleParm - Generate a control block for a circle
;
;       Internal routine, called by CircleOutline, CircleFill, GenArcParmBlk
;
; in:   R0 (X), R1(Y) centre of circle
;       R2 (X), R3(Y) point on circumference
;
; out:  R0-R7 hold the following 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)
;       R8-R11 undefined
;
GenCircleParm
        Push    R14
        LDR     R11, [WsPtr, #AspectRatio]
        SUB     R7, R2, R0
        TST     R11, #1                 ; if pixels are horz rects, adjust
        MOVNE   R7, R7, LSL #1          ; x distance
        MUL     R2, R7, R7              ; R2 = (x-cx)^2

        SUB     R7, R3, R1
        TST     R11, #2                 ; if pixels are vert rects, adjust
        MOVNE   R7, R7, LSL #1          ; y distance

        MLA     R2, R7, R7, R2          ; rawradsqr=(x-cx)^2 + (y-cy)^2
        MOV     R7, R2
        BL      SquareRoot

        ADD     R2, R2, R8              ; radsqr=rawradsqr+rawrad
        STR     R2, [WsPtr, #CircleRadSquare]   ; needed in seg. line calc

        MOV     R7, R2
        BL      SquareRoot
        MOV     R4, R8                  ; R4=rad, R2=radsqr

;
; Now build the parameter block proper
;
;  R0 = CentreX, R1 = CentreY, R2 = radsqr, R4 = rad
;
        MUL     R9, R4, R4              ; R9=rad*rad
        SUB     R2, R2, R9              ; Sum=radsqu-rad*rad

        MOV     R5, R0                  ; CentreX
        MOV     R6, R1                  ; CentreY

        MOV     R0, R4                  ; xPnt starts at rad

        ADD     R4, R4, R4
        SUB     R4, R4, #1              ; downcnt = 2*rad-1

        MOV     R1, #0                  ; yPnt starts at 0
        MOV     R3, #1                  ; upcnt = 1

        LDR     R7, [WsPtr,#AspectRatio] ; taking account of pixel shape
        LDR     R11, [WsPtr, #CursorFlags]
        TST     R11, #ClipBoxEnableBit
        BLNE    ClipCircle

        CMP     R7, #1

        MOVEQ   R0, R0, ASR #1          ; if horz pixel (ie like mode2)
        ADDEQ   R2, R2, R4
        SUBEQ   R4, R4, #2

        SUBGT   R2, R2, R3              ; if vert pixel (ie like mode0)
        ADDGT   R3, R3, #2

        Pull    PC

; *****************************************************************************
;
;       AdvCircleParm - Advance a set of circle parameters
;
;       Internal routine, called by CircleOutline, CircleFill, CircleArc,
;        SegmentFill, SectorFill
;
; in:   R0..R7 hold a circle parameter block
;
; out:  R0 (X), R1 (Y) updated
;       C=1 <=> R1 (Y) has changed
;       Z preserved
;
;       Format of a 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)
;

AdvCircleParm ROUT
        CMP     R2, R3                  ; if sum >= upcnt advance Y only
        BGE     %FT10

        SUB     R0, R0, #1              ; else step xPnt inward one point

        ADD     R2, R2, R4              ; Sum := Sum + DownCnt
        SUB     R4, R4, #2              ; DownCnt = next lower odd number

        TST     R7, #1                  ; if pixels are horizontal rectangles
        ADDNE   R2, R2, R4              ; modify sum again, so x steps at
        SUBNE   R4, R4, #2              ; half normal rate

        CMP     R2, R3
        BGE     %FT10
        CLC                             ; if not doing Y, indicate with C=0
        MOV     PC, R14
10
                                        ; if sum >= upcnt then advance Y
        ADD     R1, R1, #1              ; step yPnt up a line

        SUB     R2, R2, R3              ; Sum := Sum - UpCnt
        ADD     R3, R3, #2              ; UpCnt = next higher odd number

        TST     R7, #2                  ; if pixels are vertical rectangles
        SUBNE   R2, R2, R3              ; modify sum again, so y steps
        ADDNE   R3, R3, #2              ; at half normal rate

        SEC                             ; Y modified, so return C=1
        MOV     PC, R14

; *****************************************************************************
;
;       SquareRoot - Calculate the square root of a 32-bit number
;
;       Internal routine, called by GenSegParmBlk, GenCircleParm
;
;       SquareRootAlt is a alternative routine which specifies the precision
;        of the result in R11 (SquareRoot produces a 16-bit result)
;
; in:   R7 = number to squareroot
;
; out:  R8 = result
;       R9-R11 corrupted
;         R9 temp
;         R10 sqdiff
;         R11 counter
;

SquareRoot ROUT
        MOV     R11, #16                ; 16 bit result
SquareRootAlt
        MOV     R8, #0                  ; result=0
        MOV     R10, #0                 ; sqdiff=0
10

; *****Change made by DJS
; Original code was:
;        ADDS    R7, R7, R7              ; (sqdiff,number) = (sqdiff,number)*4
;        ADC     R10, R10, R10
;        ADDS    R7, R7, R7
;        ADCS    R10, R10, R10           ; (C:=0 we hope!)

        MOVS    R10, R10, LSL #2        ; C:=0 (we hope!) while doing (sqdiff,
        ORR     R10, R10, R7, LSR #30   ;   number) := (sqdiff, number) * 4
        MOV     R7, R7, LSL #2

; *****End of change made by DJS

        SBCS    R9, R10, R8, LSL #2     ; C=0 here, so try to subtract
                                        ; result*4 +1 from sqdiff
        MOVCS   R10, R9                 ; if successful then shift in a "1" bit
        ADC     R8, R8, R8              ; else shift in a "0" bit
        SUBS    R11, R11, #1            ; decrement loop counter
        BNE     %BT10

        MOV     PC, R14

; *****************************************************************************
;
;       DoOsWord13 - Read graphics cursors (in external coords)
;
; in:   R1 -> control block
;
; out:  [R1+0..1] = old cursor X
;       [R1+2..3] = old cursor Y
;       [R1+4..5] = current cursor X
;       [R1+6..7] = current cursor Y
;
        ASSERT  OldCsY = OldCsX +4
        ASSERT  YEigFactor = XEigFactor +4
        ASSERT  OrgY = OrgX +4
        ASSERT  GCsY = GCsX +4

DoOsWord13 ROUT
        Push    "R0-R6"
        MOV     R6, R1                  ; pointer to control block

        ADD     R0, WsPtr, #OldCsX
        LDMIA   R0, {R0, R1}
        ADD     R2, WsPtr, #XEigFactor
        LDMIA   R2, {R2, R3}            ; R2 = XEigFactor; R3 = YEigFactor
        ADD     R4, WsPtr, #OrgX
        LDMIA   R4, {R4, R5}            ; R4 = OrgX; R5 = OrgY
        RSB     R0, R4, R0, LSL R2      ; R0 = (X << XEigFactor)-OrgX
        RSB     R1, R5, R1, LSL R3      ; R1 = (Y << YEigFactor)-OrgY

      [ NoARMv6 :LOR: NoUnaligned
        STRB    R0, [R6], #1
        MOV     R0, R0, LSR #8
        STRB    R0, [R6], #1
        STRB    R1, [R6], #1
        MOV     R1, R1, LSR #8
        STRB    R1, [R6], #1
      |
        ; Use unaligned halfword stores available from ARMv6
        STRH    R0, [R6], #2
        STRH    R1, [R6], #2
      ]

        ADD     R0, WsPtr, #GCsX
        LDMIA   R0, {R0, R1}            ; get current cursor

      [ NoARMv6 :LOR: NoUnaligned
        STRB    R0, [R6], #1
        MOV     R0, R0, LSR #8
        STRB    R0, [R6], #1
        STRB    R1, [R6], #1
        MOV     R1, R1, LSR #8
        STRB    R1, [R6], #1
      |
        ; Use unaligned halfword stores available from ARMv6
        STRH    R0, [R6], #2
        STRH    R1, [R6]
      ]

        Pull    "R0-R6"
        MOV     PC, R14

        LTORG
        END