; 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.VduGrafE
;
; ARTHUR OPERATING SYSTEM - Vdu Drivers
; =======================
;
; Vdu driver code - Line Fill L & R and Flood Fill
;
; Author R C Manby
; Date   17.10.86
;

; *****************************************************************************
;
; FillLRnonBg
; ===========
;
; On entry, R0 (X), R1 (Y) hold point to fill from
;
; On exit, R0..R11 corrupt
;

FillLRtoFg
        ADD     R2, WsPtr, #FgEcf
        MOV     R3, #0
        B       FillLRnonBg10           ; Fill L <-> R

FillLRnonBg
        ADD     R2, WsPtr, #BgEcf
        MOV     R3, #&80000000
FillLRnonBg10
        SaveRetAdr
        BL      FillAlong2
        SUBCS   R1, R1, #1              ; indicate nothing filled by Y-=1
        STR     R0, [WsPtr, #GCsIX]     ; left X
        STR     R1, [WsPtr, #GCsIY]     ; Y
        Return  CS                      ; external new pt hasn't changed
        STR     R2, [WsPtr, #NewPtX]    ; right X
        MOV     R0, R2
        BL      IEGB                    ; update external version of new NewPt
        Return                          ; then things get shuffled

; *****************************************************************************
;
; FillLRtoBg    (Really only fill right!)
; ==========
;
; On entry, R0 (X), R1 (Y) hold point to fill from
;
; On exit, R0..R11 corrupt
;

FillLRnonFg
        ADD     R2, WsPtr, #FgEcf
        MOV     R3, #&80000000
        B       FillLRtoBg10            ; Fill -> R

FillLRtoBg
        ADD     R2, WsPtr, #BgEcf
        MOV     R3, #&0
FillLRtoBg10
        SaveRetAdr
        STR     R0, [WsPtr, #GCsIX]     ; "left hand point" = given point
        STR     R1, [WsPtr, #GCsIY]
        BL      GenLineFillParm         ; if C=1 (nothing can be plotted)
        SUBCS   R0, R0, #1              ; then NewPtX-=1 ie NewPtX < GCsIX
        BLCC    FillLineRightRegs       ; else fill right
        STR     R0, [WsPtr, #NewPtX]    ; Convert NewPt(X,Y) (internal) into
        LDR     R1, [WsPtr, #NewPtY]    ; GCs(X,Y) (external)
        BL      IEGB
        Return                          ; On returning, cursors are shuffled
                                        ; which updates GCsI(X,Y)

; *****************************************************************************
;
; GenLineFillParm
; ===============
;
; On entry, R0 (X), R1 (Y) hold point to fill from
;           R2 points to delimiting colour (FgEcf/BgEcf)
;           R3 fill flag, 0/&80000000 for fill to/to-non
;
; On exit, Carry=0, valid parameter setup, R0-R10 contain copy of control block
;          Carry=1, point outside window, R0 preserved, R1-R10 undefined
;
;  Format of control block
;
;  R0  - StartX
;  R1  - DelimitColour
;  R2  - FillFlag
;  R3  - ScreenAdr
;  R4  - PixelMsk
;  R5  - zgora
;  R6  - zgeor
;  R7  - GWLCol
;  R8  - GWRCol
;  R9  - BytesPerChar
;  R10 - NPix
;
GenLineFillParm
        WINDow  R0,R1, R7,R8,R9,R10     ; Window(R0,R1) using R7-R10
                                        ;  gives GE if in window
        BGE     %FT01
        SEC                             ; If point outside window
        MOV     PC, Link                ;  then quit with carry set
01
        Push    "R2,R3,R7,R9,Link"      ; Save colourptr, fillflag
                                        ; GWLCol, GWRCol
        BL      ScreenAddr              ; Returns R2 = Addr, R3 = Mask

        LDR     R9, [WsPtr, #YWindLimit]
        SUB     R9, R9, R1              ; Flip Ycoord
        AND     R9, R9, #7              ; Line within ecf
        LDR     R5, [WsPtr, #GColAdr]   ; Base address of ecf pattern
        ADD     R5, R5, R9, LSL #3
        LDMIA   R5, {R5, R6}            ; Get zgora,zgeor

        MOV     R4, R3
        MOV     R3, R2

        Pull    "R1,R2,R7,R8,Link"      ; restore other registers
        LDR     R1, [R1, R9, LSL #2]    ; load delimiting colour
        LDR     R9, [WsPtr, #BytesPerChar]
        LDR     R10, [WsPtr, #NPix]

        ADD     R11, WsPtr, #LineFillBlk ; now save completed control block
        STMIA   R11, {R0-R10}

        LDR     R11, [R3]               ; load screen
        EOR     R11, R11, R1            ; screen EOR boundary
        TST     R11, R4                 ; just look at this pixel
        ; Blimey.
        MRS     R7, CPSR
        TEQ     R2, R7, LSL #1          ; N := (Z EOR R2) ie invert test if
                                        ; stopping on non-colour
        BICPL   R7, R7, #C_bit          ; point can be filled, exit CC
        ORRMI   R7, R7, #C_bit          ; point cannot be filled, exit CS
        MSR     CPSR_f, R7
        MOV     PC, Link

; *****************************************************************************
;
; FillLineRight - Fill right from point upto delimiting colour
; =============    or graphics window
;
;
;  R0  - RightLimitX
;  R1  - LimitColour
;  R2  - FillFlag
;  R3  - ScreenAdr
;  R4  - PixelMsk
;  R5  - zgora
;  R6  - zgeor
;  R7  - GWLCol /BPC/NPIX
;  R8  - GWRCol
;  R9  -          ScreenWord
;  R10 - NPix
;  R11 -          MultPixMsk     / temp
;
; out:  PSR must be preserved
;
FillLineRight ROUT
        ADD     R11, WsPtr, #LineFillBlk
        LDMIA   R11, {R0-R10}
FillLineRightRegs
        Push    R14
        MRS     R14, CPSR
        Push    "WsPtr, R14"            ; we also need R12 for a temp
        RSB     R7, R9, #32             ; shift factor for next pixel position
        SUB     R0, R8, R0
10
        MOV     R11, #0                 ; clear partial word mask
        LDR     R9, [R3]                ; screen word EOR delimit colour
        EOR     R14, R9, R1
30
        TST     R14, R4                 ; test pixel position for delimiting
        MRS     WsPtr, CPSR
        TEQ     R2, WsPtr, LSL #1       ; invert condition if necessary

        ORRPL   R11, R11, R4            ; add pixel to partial word mask
        SUBPLS  R0, R0, #1              ; "increment" X; if on edge of window
        BMI     %FT40                   ; then give up and plot partial word

        TEQ     R4, #0                  ; test for end of word
        MOV     R4, R4, ROR R7          ; shift pixel mask left
        BPL     %BT30

        AND     R14, R5, R11            ; fill partial word, using pixel mask
        AND     R11, R6, R11            ; in R11
        ORR     R9, R9, R14
        EOR     R9, R9, R11
        STR     R9, [R3], #4

        CMP     R2, #2                  ; CC => filling until pixel=target
        BCC     %BT10                   ; so do it slowly (goto next word)

        RSB     R0, R0, #0              ; make X in range GWLCol-GWRCol .. 0
        ADDS    R0, R0, R10             ; move R0 to end of word
        BGT     %FT36                   ; outside window, continue slowly

        ORR     R14, R1, R5             ; word to store if going fast
        EOR     R14, R14, R6

32
        LDR     R9, [R3]                ; screen word
        CMP     R9, R1                  ; EOR target colour (CS if EQ)
        BNE     %FT36
        STR     R14, [R3], #4
        ADCS    R0, R0, R10             ; C=1, so add NPix+1
        BLE     %BT32
36
        SUBS    R0, R10, R0             ; R0 back to start of word and negate
        BGE     %BT10                   ; if still inside, continue slowly
        B       %FT50

40
        AND     R14, R5, R11            ; fill partial word, using pixel mask
        AND     R11, R6, R11            ; in R11
        ORR     R9, R9, R14
        EOR     R9, R9, R11
        STR     R9, [R3]
50
        SUB     R0, R8, R0              ; make back into normal coord again
        SUB     R0, R0, #1              ; correct end point
        Pull    "WsPtr, R14"
        MSR     CPSR_f, R14
        Pull    PC

; *****************************************************************************
;
; FillLineLeft - Fill left from point upto delimiting colour
; ============    or graphics window
;
;                N.B. Starts one pixel left of point given, since
;                      FillLineRight will have already plotted it.
;
;  R0  - LeftLimitX
;  R1  - LimitColour
;  R2  - FillFlag
;  R3  - ScreenAdr
;  R4  - PixelMsk
;  R5  - zgora
;  R6  - zgeor
;  R7  - GWLCol                 } Slightly different from FillLineRight
;  R8  - GWRCol /BPC/NPIX       }
;  R9  -          ScreenWord
;  R10 -          LimitEorScreen / temp
;  R11 -          MultPixMsk     / temp
;
;
;
FillLineLeft ROUT
        ADD     R11, WsPtr, #LineFillBlk
        LDMIA   R11, {R0-R10}
FillLineLeftRegs
        Push    R14
        MRS     R14, CPSR
        Push    "WsPtr, R14"            ; also need WsPtr for a temp
        MOV     R8, R9                  ; shift factor for next pixel position

        SUB     R0, R0, R7
        LDR     R9, [R3]                ; screen word EOR delimit colour
        EOR     R14, R9, R1
        MOVS    R11, #0                 ; clear partial word mask (and set PL)
        B       %FT31

10
        MOV     R11, #0                 ; clear partial word mask
        LDR     R9, [R3]                ; screen word EOR delimit colour
        EOR     R14, R9, R1

30
        TST     R14, R4                 ; test pixel position for delimiting
        MRS     WsPtr, CPSR
        TEQ     R2, WsPtr, LSL #1       ; invert condition if appropriate
        ORRPL   R11, R11, R4            ; add pixel to partial word mask
31
        SUBPLS  R0, R0, #1              ; decrement X; if on edge of window
        BMI     %FT40                   ; then give up and plot partial word

        MOVS    R4, R4, ROR R8          ; test for end of word
        BPL     %BT30                   ; loop until mask exhausted

        AND     R14, R5, R11            ; fill partial word, using pixel mask
        AND     R11, R6, R11            ; in R11
        ORR     R9, R9, R14
        EOR     R9, R9, R11
        STR     R9, [R3], #-4

        CMP     R2, #2                  ; CC => filling until pixel=target
        BCC     %BT10                   ; so do it slowly (goto next word)

        SUBS    R0, R0, R10             ; move R0 to beginning of word
        BCC     %FT36                   ; if outside window continue slowly
        ADD     R10, R10, #1
        ORR     R14, R1, R5             ; compute target word
        EOR     R14, R14, R6
32
        LDR     R9, [R3]                ; screen word
        CMP     R9, R1
        BNE     %FT35                   ; NZ => terminate
        STR     R14, [R3], #-4
        SUBS    R0, R0, R10
        BCS     %BT32
35
        SUB     R10, R10, #1
36
        ADDS    R0, R0, R10             ; point R0 back to end of word
        BGE     %BT10                   ; if still inside, continue but slowly
        B       %FT50

40
        AND     R14, R5, R11            ; fill partial word, using pixel mask
        AND     R11, R6, R11            ; in R11
        ORR     R9, R9, R14
        EOR     R9, R9, R11
        STR     R9, [R3]
50
        ADD     R0, R0, R7              ; add GWLCol back on
        ADD     R0, R0, #1              ; Correct endpoint value
        Pull    "WsPtr, R14"
        MSR     CPSR_f, R14
        Pull    PC

; *****************************************************************************
;
; FillAlong - Fill left and right from point given
; =========
;
; On entry, R0 (X), R1(Y) hold point to fill from
;
; On exit,  R0 (X) End of scan left
;           R1 (Y) Preserved
;           R2 (X) End of scan right
;
;           Carry set = no fill performed ie outside window or already filled
;           Carry clear = fill occured
;
        ASSERT  FldBoundaryFlag = FldBoundaryCol +4

FillAlong
        ADD     R2, WsPtr, #FldBoundaryCol
        LDMIA   R2, {R2,R3}             ; R2 = colour, R3 = flag
FillAlong2                              ; entry point when R2, R3 loaded
        Push    "R1, R14"
        BL      GenLineFillParm         ; On exit C=1 if nothing can be plotted
        BLCC    FillLineRightRegs
        Push    R0                      ; New RightX
        BLCC    FillLineLeft
        Pull    R2                      ; Recover RightX
        Pull    "R1, PC"                ; Recover Y, C=1 <=> fill occured

; *****************************************************************************
;
; FloodFill
; =========
;
; On entry, R0 (X), R1 (Y) hold point to flood from
;           R2 Delimit colour, pointer to FgEcf/BgEcf
;           R3 0/&80000000 for flood until/over
;
        ASSERT  FldBoundaryFlag = FldBoundaryCol +4
        ASSERT  FldY = FldLeftXLimit +4
        ASSERT  FldRightXLimit = FldLeftXLimit +8

FloodToFg
        ADD     R2, WsPtr, #FgEcf       ; delimit colour
        MOV     R3, #0                  ; flood until
        B       FloodFill

FloodNonBg
        ADD     R2, WsPtr, #BgEcf       ; delimit colour
        MOV     R3, #&80000000          ; flood over
FloodFill ROUT
        Push    R14
        STR     R13, [WsPtr, #FldStackLevel]    ; save stack level !

        ADD     R14, WsPtr, #FldBoundaryCol
        LDR     R4, [WsPtr, #YWindLimit]
        STMIA   R14, {R2-R4}            ; store colour, flag, YWindLimit

        [ med_00001_userma

        ; the idea here is to try to use between 48k and 128k of rma instead of 16k of
        ; scratchspace. if there's less than 48k of rma in the first place we go back to
        ; scratchspace. if there's 128k or more we claim the 128k and proceed. For values
        ; between 48k and 128k we first grow the rma by 128k-largest_free_space. If this
        ; results in a lump of 128k now being free we claim it and proceed. If the largest
        ; free space hasn't grown to 128k we then claim the additional balance needed to
        ; have 128k available. Should either grow fail, we just use whatever (over 48k)
        ; was available

        ;R3-R7 spare at presenet
        Push    "R0-R2"
        MOV     R0, #0
        STR     R0, [WsPtr, #flood_cda_rma]                     ; set amount to change dyn. area by
        MOV     R0, #ModHandReason_RMADesc
        SWI     XOS_Module                                      ; get the largest free space in rma
        BVC     %FT91                                           ; if that failed, use scratchspace

use_scratchspace
        LDR     R3, =FldQueueStart                              ; head ptr (== scratchspace)
        MOV     R4, R3                                          ; tail ptr
        ADD     R5, R3, #FldQueueSize                           ; end ptr (== scratchspace size)
        MOV     R6, R3                                          ; start ptr
        B       %FT92
91
        CMP     R2, #smallest_rma_size                          ; compare against small threshold
        BLT     use_scratchspace                                ; not enough free - use scratchspace
        CMP     R2, #largest_rma_size                           ; compare against high threshold
        BHS     %FT96                                           ; lots free - use largest ceiling value

        RSB     R1, R2, #largest_rma_size                       ; work out how much more to claim
        MOV     R0, #1                                          ; rma
        SWI     XOS_ChangeDynamicArea
        STRVC   R1, [WsPtr, #flood_cda_rma]                     ; save the amount we grew the rma
        BVS     %FT94                                           ; if it failed, use the minimum<size<maximum lump

        MOV     R0, #ModHandReason_RMADesc                      ; reread the largest free space to see if
        SWI     XOS_Module                                      ; it has risen to maximum
        BVS     use_scratchspace
        CMP     R2, #largest_rma_size
        BHS     %FT96                                           ; it has, so claim the maximum

        LDR     R1, [WsPtr, #flood_cda_rma]                     ; the free space didn't grow to maximum, so the
        RSB     R1, R1, #largest_rma_size                       ; largest space wasn't at the end. Do a second
        MOV     R0, #1                                          ; grow of the rma to make the maximum
        SWI     XOS_ChangeDynamicArea
        LDRVC   R0, [WsPtr, #flood_cda_rma]                     ; if it succeeded, update the amount that we
        ADDVC   R0, R0, R1                                      ; need to shrink the rma by
        STRVC   R0, [WsPtr, #flood_cda_rma]
                                                                ; and fall into the claim largest space routine
94
        MOV     R0, #ModHandReason_RMADesc
        SWI     XOS_Module
        BVS     use_scratchspace                                ; find the largest lump now available
        CMP     R2, #largest_rma_size
96
        MOVGT   R3, #largest_rma_size
        MOVLE   R3, R2                                          ; cap it at the maximum size we want
        MOV     R0, #ModHandReason_Claim                        ; try to claim it
        SWI     XOS_Module
        BVS     use_scratchspace

        ADD     R5, R2, R3                                      ; do it in a slightly different order so
        MOV     R3, R2                                          ; we can use the R2 and R3 back from the claim
        MOV     R4, R3                                          ; (R2=where, R3=size)
        MOV     R6, R3

92
        ADD     R7, WsPtr, #QueuePtrs
        STMIA   R7, {R3, R4, R5, R6}    ; initialise queue
        Pull    "R0-R2"
        |
        LDR     R3, =FldQueueStart      ; head ptr
        MOV     R4, R3                  ; tail ptr
        ADD     R5, R3, #FldQueueSize   ; end ptr
        MOV     R6, R3                  ; start ptr
        ADD     R7, WsPtr, #QueuePtrs
        STMIA   R7, {R3, R4, R5, R6}    ; initialise queue
        ]

        BL      FillAlong               ; try filling L&R from given point
        Pull    PC, CS                  ; quit if can't be filled

        MOV     R3, #3                  ; bit0 => fill up, bit1 => fill down
10
        ADD     R14, WsPtr, #FldLeftXLimit
        STMIA   R14, {R0-R2}            ; store leftX, Y, rightX

        TST     R3, #1
        BEQ     %FT20

        LDR     R10, [WsPtr, #GWTRow]   ; if below Top of window
        CMP     R10, R1
        BLE     %FT20

        ADD     R4, WsPtr, #FldBoundaryCol      ; R4=target colour ptr,R5=flag,
        LDMIA   R4, {R4,R5,R6}                  ; R6=YWindLimit

        ORR     R5, R5, #FillingUpBit   ; fill line above
        Push    R3
        BL      CheckAlong              ; (will bomb out if queue explodes)
        Pull    R3

        ADD     R14, WsPtr, #FldLeftXLimit
        LDMIA   R14, {R0,R1}            ; reload leftX, Y
20
        TST     R3, #2
        BEQ     %FT30

        LDR     R10, [WsPtr, #GWBRow]   ; if above bottom of window
        CMP     R1, R10
        BLE     %FT30

        ADD     R4, WsPtr, #FldBoundaryCol      ; R4=target colour ptr,R5=flag,
        LDMIA   R4, {R4,R5,R6}                  ; R6=YWindLimit

;        BIC     R5, R5, #FillingUpBit  ; fill line below
        BL      CheckAlong              ; (will bomb out if queue explodes)
30
        ADD     R11, WsPtr, #QueuePtrs  ; unqueue one item
        LDMIA   R11, {R3,R4,R5}         ; head,tail,limit
        TEQ     R3, R4                  ; if queue empty
        [ med_00001_userma
        BLEQ    release_memory
        ]
        Pull    PC, EQ                  ; then exit

        [ med_00001_twowords
        LDR     R1, [R3], #4
        MOV     R0, R1, LSR #16         ; LeftX
        MOV     R2, R1
        EOR     R2, R2, R0, LSL #16     ; RightX (with LeftX EORed out)
        LDR     R1, [R3], #4            ; Recover Y
        |
        LDR     R1, [R3], #4            ; then load word
        MOV     R2, R1, LSR #21         ; Recover RightX
        EOR     R1, R1, R2, LSL #21     ; clear those bits out
        MOV     R0, R1, LSR #10         ; Recover LeftX
        EOR     R1, R1, R0, LSL #10     ; Recover Y
        ]
        TEQ     R3, R5                  ; if at limit
        LDREQ   R3, [R11, #12]          ; then reload with start
        STR     R3, [R11, #0]           ; store head ptr back

        MOV     R3, #2                  ; indicate fill down
        CMP     R2, R0                  ; if right < left then subtract 1 off
                                        ; larger and swap
        SBCCC   R0, R0, R2              ; R0 := large-small-1
        ADDCC   R2, R0, R2              ; R2 := (large-small-1)+small = large-1
        SUBCC   R0, R2, R0              ; R0 := (large-1)-(large-small-1)=small
        MOVCC   R3, #1                  ; and indicate fill up
        B       %BT10

; *****************************************************************************
;
; CheckAlong - Fill and skip background between LeftXLimit & RightXLimit
; ==========
;
; On entry, R0 LeftXLimit
;           R1 Y
;           R4 target colour
;           R5 fill flag
;           R6 YWindLimit
;
; Exits using R14 if normal termination
; Exits using FldStackLevel if queue full
;

FillingUpBit * 1

        ASSERT  FldBoundaryFlag = FldBoundaryCol +4
        ASSERT  FldYWindLimit = FldBoundaryCol +8

        ASSERT  FldY = FldLeftXLimit +4
        ASSERT  FldRightXLimit = FldLeftXLimit +8

CheckAlong ROUT
        Push    R14

        TST     R5, #FillingUpBit               ; if going up
        ADDNE   R1, R1, #1                      ; then increment Y
        SUBEQ   R1, R1, #1                      ; else decrement Y

        BL      ScreenAddr                      ; R2=screen addr, R3=mask

        SUB     R6, R6, R1                      ; flip Y
        AND     R6, R6, #7                      ; ecf offset
        LDR     R4, [R4, R6, LSL #2]            ; R4=target colour
        LDR     R7, [WsPtr, #GColAdr]           ; base address of plot ecf
        ADD     R7, R7, R6, LSL #3              ; R7 -> zgora, zgeor
        LDMIA   R7, {R10,R11}                   ; R10=zgora, R11=zgeor
        LDR     R6, [WsPtr, #BytesPerChar]      ; R6=pixel shift
        LDR     R9, [WsPtr, #NPix]              ; R9=no. of pixels per word-1

        ADD     R7, WsPtr, #FldSaveArea         ; save Y, target colour,
        STMIA   R7, {R1, R4, R9, R10, R11}      ; NPix, zgora, zgeor

; test first pixel, to see if we can go left

        LDR     R7, [R2]
        EOR     R1, R7, R4
        TST     R1, R3
        MRS     R8, CPSR
        TEQ     R5, R8, LSL #1          ; PL => it's fillable
        RSBMI   R6, R6, #32             ; not fillable, reverse pixel shift
        LDRMI   R8, [WsPtr, #GWRCol]    ; load right window limit
        BMI     %FT37                   ; and skip left+right filling bit

        Push    "R0, R2, R3"            ; save X, screen address, mask
        LDR     R8, [WsPtr, #GWLCol]
        SUB     R0, R0, R8              ; make X in range 0..GWRCol-GWLCol
        MOV     R14, #0                 ; clear partial word mask
        TEQ     R5, #0                  ; get into correct loop
        BMI     %FT84
        BPL     %FT64

10
        MOV     R14, #0                 ; clear partial word mask
        LDR     R7, [R2]                ; R7 = screen
        EOR     R1, R7, R4

 ; ***KJB - very cumbersome. Surely a temporary reg somewhere?
        TEQ     R5, #0                  ; check "not" flag
        BMI     %FT80

61      TST     R1, R3                  ; test pixel position for delimiter
        BEQ     %FT22                   ; NE => it's fillable
        ORR     R14, R14, R3            ; add pixel to partial word mask
64      SUBS    R0, R0, #1              ; decrement X; if on edge of window
        BMI     %FT22                   ; then give up and plot partial word

        MOVS    R3, R3, ROR R6          ; test for end of word
        BPL     %BT61                   ; loop until mask wraps
        B       %FT89

80      TST     R1, R3                  ; test pixel position for delimiter
        BNE     %FT22                   ; EQ => it's fillable
        ORR     R14, R14, R3            ; add pixel to partial word mask
84      SUBS    R0, R0, #1              ; decrement X; if on edge of window
        BMI     %FT22                   ; then give up and plot partial word

        MOVS    R3, R3, ROR R6          ; test for end of word
        BPL     %BT80                   ; loop until mask wraps
89

        AND     R1, R10, R14            ; R1 := zgora AND pixmask
        ORR     R7, R7, R1              ; screen := screen OR R1
        AND     R1, R11, R14            ; R1 := zgeor AND pixmask
        EOR     R7, R7, R1              ; screen := screen EOR R1
        STR     R7, [R2], #-4

        CMP     R5, #2                  ; CC => filling until target
        BCC     %BT10                   ; so do it slowly (do next word)

        SUBS    R0, R0, R9              ; move R0 to start of word
        BCC     %FT20                   ; if outside window continue slowly

        ADD     R9, R9, #1              ; R9 := pixels per word
        ORR     R1, R4, R10             ; compute word to plonk on screen
        EOR     R1, R1, R11
16
        LDR     R7, [R2]                ; screen word
        CMP     R7, R4
        BNE     %FT18                   ; NZ => terminate
        STR     R1, [R2], #-4
        SUBS    R0, R0, R9              ; back another word
        BCS     %BT16
18
        SUB     R9, R9, #1              ; R9 := pixels per word -1
20
        ADDS    R0, R0, R9              ; point R0 back to end of word
        BGE     %BT10                   ; if still inside, continue but slowly
        B       %FT24                   ; else exit
22
        AND     R1, R10, R14            ; R1 := zgora AND pixmask
        ORR     R7, R7, R1              ; screen := screen OR R1
        AND     R1, R11, R14            ; R1 := zgeor AND pixmask
        EOR     R7, R7, R1              ; screen := screen EOR R1
        STR     R7, [R2]
24
        ADD     R0, R0, R8              ; add GWLCol back on
        ADD     R0, R0, #1
        Pull    "R1, R2, R3"            ; restore X, screen addr, mask
        Push    R0                      ; save left-of-line coordinate
        MOV     R0, R1                  ; R0 = start X
        RSB     R6, R6, #32             ; reverse pixel shift
        LDR     R8, [WsPtr, #GWRCol]    ; load right window limit

; now the filling right part

26
        SUB     R0, R8, R0              ; make X in range GWRCol-GWLCol .. 0

; start of each word

27
        MOV     R14, #0
        LDR     R7, [R2]                ; screen word
        EOR     R1, R7, R4

; ***KJB - very cumbersome. Surely a temporary reg somewhere?
        TEQ     R5, #0
        BMI     %FT75
72
        TST     R1, R3                  ; test pixel position for delimiter
        BEQ     %FT34                   ; NE => it's fillable
        ORR     R14, R14, R3            ; add pixel to partial word mask
        SUBS    R0, R0, #1              ; "increment" X; if on edge of window
        BMI     %FT34                   ; then give up and plot partial word

        TEQ     R3, #0                  ; test for end of word
        MOV     R3, R3, ROR R6          ; shift pixel mask
        BPL     %BT72                   ; loop until mask wraps
        B       %FT79

75
        TST     R1, R3                  ; test pixel position for delimiter
        BNE     %FT34                   ; EQ => it's fillable
        ORR     R14, R14, R3            ; add pixel to partial word mask
        SUBS    R0, R0, #1              ; "increment" X; if on edge of window
        BMI     %FT34                   ; then give up and plot partial word

        TEQ     R3, #0                  ; test for end of word
        MOV     R3, R3, ROR R6          ; shift pixel mask
        BPL     %BT75                   ; loop until mask wraps
79

        AND     R1, R10, R14            ; R1 := zgora AND pixmask
        ORR     R7, R7, R1              ; screen := screen OR R1
        AND     R1, R11, R14            ; R1 := zgeor AND pixmask
        EOR     R7, R7, R1              ; screen := screen EOR R1
        STR     R7, [R2], #4

        CMP     R5, #2                  ; CC => filling until target
        BCC     %BT27                   ; so do it slowly (do next word)

        RSB     R0, R0, #0              ; make X in range GWLCol-GWRCol .. 0
        ADDS    R0, R0, R9              ; move R0 to end of word
        BGT     %FT32                   ; outside window, continue slowly

        ORR     R1, R4, R10             ; word to store if going fast
        EOR     R1, R1, R11
30
        LDR     R7, [R2]                ; screen word
        CMP     R7, R4                  ; EOR target colour (CS if EQ)
        BNE     %FT32                   ; NZ => terminate
        STR     R1, [R2], #4
        ADCS    R0, R0, R9              ; C=1, so add NPix +1
        BLE     %BT30
32
        SUBS    R0, R9, R0              ; R0 back to start of word and negate
        BGE     %BT27                   ; if still inside, continue slowly
        B       %FT36                   ; else exit

34
        AND     R1, R10, R14            ; R1 := zgora AND pixmask
        ORR     R7, R7, R1              ; screen := screen OR R1
        AND     R1, R11, R14            ; R1 := zgeor AND pixmask
        EOR     R7, R7, R1              ; screen := screen EOR R1
        STR     R7, [R2]
36
        SUB     R0, R8, R0              ; make back into normal coord again

; now queue this segment

        Pull    R4                      ; R4 := LH end point
        SUB     R7, R0, #1              ; R7 := corrected RH end point
        LDR     R1, [WsPtr, #FldSaveY]  ; reload Y coordinate of THIS segment
        BL      Queue

        ADD     R14, WsPtr, #FldLeftXLimit      ; load lxlim, (Y+?), rxlim
        LDMIA   R14, {R7,R10,R11}

        EOR     R5, R5, #FillingUpBit   ; invert direction

        SUB     R7, R7, #2              ; lxlim -2
        CMP     R4, R7                  ; if leftX <= lxlim-2
        BLLE    Queue                   ; then Q LH end bit in opposite dir

        ADD     R4, R11, #2             ; rxlim +2
        SUB     R7, R0, #1              ; R7 = RH end point
        CMP     R7, R4                  ; if rightX >= rxlim+2
        BLGE    Queue                   ; then Q RH end bit in opposite dir

        EOR     R5, R5, #FillingUpBit   ; invert direction back again

        SUB     R4, R4, #3              ; rxlim -1
        CMP     R7, R4                  ; if rightX >= rxlim-1
        Pull    PC, GE                  ; no point in skipping any more

; now do the skipping
; R0 points to first non-fillable pixel; R3=correct mask for this pixel

        ADD     R7, WsPtr, #FldSaveArea         ; reload target colour, NPix,
        LDMIB   R7, {R4, R9, R10, R11}          ; zgora, zgeor
37
        LDR     R1, [WsPtr, #FldRightXLimit]
38
        LDR     R7, [R2], #4
        EOR     R7, R7, R4                      ; screen EOR target
40
        TST     R7, R3                          ; if pixel is fillable
        MRS     R14, CPSR
        TEQ     R5, R14, LSL #1
        SUBPL   R2, R2, #4                      ; then correct address
        Push    R0, PL                          ; and push new X coord
        BPL     %BT26                           ; and go to fill right bit
50
        CMP     R0, R1                          ; on limit of segment ?
        Pull    PC, CS                          ; yes, so give up
        ADD     R0, R0, #1                      ; step coord
        TEQ     R3, #0                          ; test for mask wrap
        MOV     R3, R3, ROR R6                  ; rotate mask to next pixel
        BPL     %BT40                           ; continue within this word
        B       %BT38                           ; continue with next word

; *****************************************************************************
;
;       Queue - Queue a line segment
;
; in:   R1 = Y coordinate of segment
;       R4 = left X coordinate
;       R5 = fill flag (Bit0=0/1 => look downwards/upwards)
;       R7 = right X coordinate
;
; out:  R0-R8, R11, R12 preserved
;       R9,R10 corrupted
;       exits by pulling stacked R14 if queue was full
;

Queue   ROUT
        Push    "R0,R11,R14"
        TEQ     R5, R5, LSR #1          ; CS => mangle coords

        MOVCC   R9, R4
        MOVCC   R10, R7

        SUBCS   R9, R7, R4              ; R9 = R-L
        SUBCS   R10, R7, R9             ; R10 = R-(R-L) = L
        ADCCS   R9, R9, R10             ; R9 = (R-L)+L+1 = R+1

        [ med_00001_twowords
        ; revised packing, expanded to two words, LeftX in 31..16 of word 1,
        ; RightX in 15..0 of word 1, and Y in 15..0 of word 2

        MOV     R9, R9, LSL #16         ; shift LeftX up
        ORR     R9, R9, R10             ; combine in RightX

        ADD     R0, WsPtr, #QueuePtrs
        LDMIA   R0, {R10, R11, R14}     ; head,tail,limit

        STR     R9, [R11], #4           ; store word at tail and move on
        STR     R1, [R11], #4           ; store second word
        |
        ; pack Y into bits 0..9, L into bits 10..20, R into bits 21..31

        ORR     R9, R1, R9, LSL #10     ; R9 = Y + (L << 10)
        ORR     R9, R9, R10, LSL #21    ; R9 = Y + (L << 10) + (R <<21)

        ADD     R0, WsPtr, #QueuePtrs
        LDMIA   R0, {R10, R11, R14}     ; head,tail,limit

        STR     R9, [R11], #4           ; store word at tail and move on
        ]

        TEQ     R11, R14                ; if at limit
        LDREQ   R11, [R0, #12]          ; then reload with start ptr
        CMP     R11, R10                ; EQ and CS if queue full
        STRNE   R11, [R0, #4]           ; store tail ptr back if OK

        Pull    "R0,R11,PC", NE         ; queue not full, terminate normally

        [ med_00001_userma
        BL      release_memory
        ]
        LDR     R13, [WsPtr, #FldStackLevel]
        Pull    PC                      ; else bomb out

; *****************************************************************************

        [ med_00001_userma
release_memory
        ROUT
        Push   "R0-R5,lr"

        ADD    R0, WsPtr, #QueuePtrs
        LDMIA  R0, {R0-R3}

        LDR    R5, =FldQueueStart

        CMP    R3, R5                   ; did we use scratchspace ?
        BEQ    %FT20

        MOV    R2, R3
        MOV    R0, #ModHandReason_Free
        SWI    XOS_Module
20
        LDR    R0, [WsPtr, #flood_cda_rma]
        CMP    R0, #0
        BEQ    %FT30                    ; no need to shrink rma

        RSB    R1, R0, #0               ; get the signed negative value
        MOV    R0, #1                   ; rma
        SWI    XOS_ChangeDynamicArea
30
        CMP    r0, r0                   ; ensure we return EQ set
        Pull   "r0-r5,pc"
        ]

; *****************************************************************************

        END