; 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.VduGrafD

;
; ARTHUR OPERATING SYSTEM - Vdu Drivers
; =======================
;
; Vdu driver code - Block Copy and Move
;
; Author R C Manby
; Date   2.10.86
;

; *****************************************************************************
;
;       BlockCopyMove - Copy/Move a rectangular area
;
; in:   OldCs marks start of area to copy/move
;       ICursor marks end of area to copy/move
;       NewPt is lower left of destination area
;       R2 = plot number &B8..&BF
;
; out:  R0..R11 corrupt
;

BlockCopyMove ROUT
        TST     R2, #3                          ; Do nowt on 'MoveOnly' codes
        MOVEQ   PC, R14

        SaveRetAdr

        ADD     R0, WsPtr, #BgEcfStore          ; Select 'store' in background
        STR     R0, [WsPtr, #GColAdr]           ; to be used by HLine when
                                                ; clearing source & dest lines

; Build up source & destination data as follows
;
; R0   R1   R2   R3   R4    R5    R6    R7    R8
; SrcL,SrcB,SrcR,SrcT,DestL,DestB,DestR,DestT,CopyFlag

        AND     R8, R2, #2                      ; 0/2 means Move/Copy

        ADD     R11, WsPtr, #OldCsX
        LDMIA   R11, {R0,R1, R2,R3, R4,R5}      ; OldCs, ICursor, NewPt

        SortT   R0, R2, R6                      ; Order SrcL,SrcB,SrcR,SrcT
        SortT   R1, R3, R6                      ;       R0 , R1 , R2 , R3

        SUB     R6, R2, R0
        ADD     R6, R6, R4                      ; DestR := SrcR-SrcL+DestL

        SUB     R7, R3, R1
        ADD     R7, R7, R5                      ; DestT := SrcT-SrcB+DestB

        ADD     R11, WsPtr, #CMSrc
        STMIA   R11, {R0-R7,R8}                 ; Unclipped src & dest areas
                                                ; and CopyFlag
        LDR     R11, [WsPtr, #CursorFlags]
        TST     R11, #ClipBoxEnableBit
        BLNE    ClipBlockCopyMove

; now work out ACTUAL area to copy
; first, we window destination area

        LDR     R10, [WsPtr, #GWLCol]
        LDR     R11, [WsPtr, #GWRCol]

        SUBS    R8, R10, R4                     ; R8 = GWLCol - DestL
        ADDGT   R4, R4, R8                      ; if > 0 then DestL := GWLCol
        ADDGT   R0, R0, R8                      ; and adjust SrcL to match

        SUBS    R8, R6, R11                     ; R8 = DestR - GWRCol
        SUBGT   R6, R6, R8                      ; if > 0 then DestR := GWRCol
        SUBGT   R2, R2, R8                      ; and adjust SrcR to match

        CMP     R6, R4                          ; check DestR >= DestL
        BLT     EraseSource                     ; if not then just erase source
                                                ; if a block move

        STR     R4, [WsPtr, #CMDest2L]          ; save horizontal dest coords
        STR     R6, [WsPtr, #CMDest2R]          ; windowed at dest

        SUBS    R8, R10, R0                     ; R8 = GWLCol - SrcL
        ADDGT   R0, R0, R8                      ; if > 0 then SrcL := GWLCol
        ADDGT   R4, R4, R8                      ; and adjust DestL to match

        SUBS    R8, R2, R11                     ; R8 = SrcR - GWRCol
        SUBGT   R2, R2, R8                      ; if > 0 then SrcR := GWRCol
        SUBGT   R6, R6, R8                      ; and adjust DestR to match

        LDR     R10, [WsPtr, #GWBRow]
        LDR     R11, [WsPtr, #GWTRow]

        SUBS    R8, R10, R5                     ; R8 = GWBRow - DestB
        ADDGT   R5, R5, R8                      ; if > 0 then DestB := GWBRow
        ADDGT   R1, R1, R8                      ; and adjust SrcB to match

        SUBS    R8, R7, R11                     ; R8 = DestT - GWTRow
        SUBGT   R7, R7, R8                      ; if > 0 then DestT := GWTRow
        SUBGT   R3, R3, R8                      ; and adjust SrcT to match

        CMP     R7, R5                          ; check DestT >= DestB
        BLT     EraseSource                     ; if not then just erase source
                                                ; if a block move

        STR     R5, [WsPtr, #CMDest2B]          ; save vertical dest coords
        STR     R7, [WsPtr, #CMDest2T]          ; windowed at dest

        SUBS    R8, R10, R1                     ; R8 = GWBRow - SrcB
        ADDGT   R1, R1, R8                      ; if > 0 then SrcB := GWBRow
        ADDGT   R5, R5, R8                      ; and adjust DestB to match

        SUBS    R8, R3, R11                     ; R8 = SrcT - GWTRow
        SUBGT   R3, R3, R8                      ; if > 0 then SrcT := GWTRow
        SUBGT   R7, R7, R8                      ; and adjust DestT to match

; now R0-R3 is source windowed both ways
;     R4-R7 is dest   windowed both ways

        ADD     R8, WsPtr, #CMDest3L
        STMIA   R8, {R4-R7}                     ; save destination windowed
                                                ; both ways

        LDR     R9, [WsPtr, #VduSprite]

        CMP     R2, R0                          ; check SrcR >= SrcL
        SUBGES  R8, R3, R1                      ; and SrcT >= SrcB (R8=lines-1)
        BLT     EraseDest                       ; if not, then go to wiping out
                                                ; destination stage

        STR     R8, [WsPtr, #CMVertCount]       ; no. of vertical lines -1

        BL      CheckAcceleration               ; okay to accelerate?
        BNE     %FT05

; not redirected to sprite - try calling GraphicsV for some acceleration

        Push    "R2"
        Push    "R8"
        SUB     R14, R2, R0
        Push    "R0,R1,R4,R5,R14"               ; srcL,srcB,dstL,dstB,W-1,H-1
        MOV     R0, #GVRender_Sync
        MOV     R1, #GVRender_CopyRectangle
        MOV     R2, R13
        LDR     R4, [WsPtr, #CurrentGraphicsVDriver]
        MOV     R4, R4, LSL #24
        ORR     R4, R4, #GraphicsV_Render
        BL      CallGraphicsV                   ; (R0,R1,R2,R4->R4)
        TEQ     R4, #GraphicsV_Complete         ; did it do it?
        Pull    "R0,R1,R4"
        ADD     R13, R13, #3*4
        Pull    "R2"
        BEQ     EraseDest                       ; then go straight to erase

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

        MOV     R8, R6, LSR R10                 ; DestR word offset
        SUB     R8, R8, R4, LSR R10             ; -DestL word offset
        STR     R8, [WsPtr, #CMDestCount]       ; No. of dest words -1

        AND     R8, R6, R11                     ; R8 = DestR pixel offset
        ADD     R14, WsPtr,#RAMMaskTb
        LDR     R8, [R14, R8, LSL #2]           ; R8 = mask for right pixel
        SUB     R14, R8, #1             ; In rh mask, set all bits lower than
        ORR     R14, R14, R8            ; pixel, RHM := RHM OR (RHM-1)
        STR     R14, [WsPtr, #CMRMask]

        AND     R8, R4, R11                     ; R8 = DestL pixel offset
        ADD     R14, WsPtr, #RAMMaskTb
        LDR     R8, [R14, R8, LSL #2]           ; R8 = mask for left pixel
        RSB     R14, R8, #0             ; In lh mask, set all bits higher than
        ORR     R14, R14, R8            ; pixel, LHM := LHM OR (0-LHM)
        STR     R14, [WsPtr, #CMLMask]

        CMP     R0, R4                          ; test whether SrcL >= DestL
        BLT     SrcHLTDest

; source is to the right of dest, so start at left hand side

        AND     R8, R4, R11                     ; R8 = DestL pixel offset
        AND     R14, R0, R11                    ; R14 = SrcL pixel offset
        SUB     R11, R14, R8                    ; R11 = Src-Dest pixel offset
        MOV     R11, R11, LSL R9                ; R11 = Src-Dest bit offset

        CMP     R11, #0                         ; if rightshift < 0
        ADDLT   R11, R11, #32                   ; if < 0, correct result
        MOVGE   R10, #0
        MOVLT   R10, #-4                        ; and subtract 4 off src addr

        STR     R11, [WsPtr, #CMRShift]
        RSB     R11, R11, #32
        STR     R11, [WsPtr, #CMLShift]

        LDR     R6, [WsPtr, #LineLength]        ; (no longer need DestR)

        CMP     R1, R5                          ; if SrcB >= DestB
        RSBGE   R6, R6, #0                      ; then go upwards
        STR     R6, [WsPtr, #CMVertDir]

        MOVLT   R1, R3                          ; else go down
        MOVLT   R5, R7                          ; so use top coords

        BL      ScreenAddr                      ; R2 = address of src corner
        ADD     R2, R2, R10
        STR     R2, [WsPtr, #CMSourceAddr]

        MOV     R0, R4
        MOV     R1, R5
        BL      ScreenAddr                      ; R2 = address of dest corner
        STR     R2, [WsPtr, #CMDestAddr]

        ADD     R11, WsPtr, #CMStuff
        LDMIA   R11, {R0-R6}            ; src,dest,cnt,rtshf,lfshf,rtmsk,ltmsk
10
        TEQ     R2, #0                  ; only one word on a line ?
        ANDEQ   R5, R5, R6              ; then rightmask:=rightmask AND lftmask
        LDREQ   R14, [R0], #4           ; and load first word up
        BEQ     %FT45                   ; and do endword

; do first word

        LDMIA   R0!, {R11,R14}          ; load 2 words
        ShiftR  R11, R14, R3, R4        ; shift them
        AND     R11, R11, R6            ; AND with leftmask
        LDR     R10, [R1]               ; load word from dest
        BIC     R10, R10, R6            ; clear out bits in leftmask
        ORR     R10, R10, R11           ; OR in new bits
        STR     R10, [R1], #4
        SUBS    R2, R2, #1              ; decrement count
        BEQ     %FT40                   ; if zero, do finish word

        SUBS    R2, R2, #7              ; can we do 7 words ?
        BCC     %FT30                   ; no, then do 1 word at a time

; do 7 words at a time

        TEQ     R3, #0                  ; if rightshf = 0
        BEQ     %FT60                   ; then do non-shifting version
20
 [ {TRUE}                               ; TMD optimisation 12-May-93
        MOV     R5, R14, LSR R3
        LDMIA   R0!, {R6-R11,R14}
        ORR     R5, R5, R6, LSL R4
 |
        MOV     R5, R14
        LDMIA   R0!, {R6-R11,R14}       ; load next 7 words
        ShiftR  R5, R6, R3, R4
 ]
        ShiftR  R6, R7, R3, R4
        ShiftR  R7, R8, R3, R4
        ShiftR  R8, R9, R3, R4
        ShiftR  R9, R10, R3, R4
        ShiftR  R10, R11, R3, R4
        ShiftR  R11, R14, R3, R4
        STMIA   R1!, {R5-R11}
        SUBS    R2, R2, #7
        BCS     %BT20

30
        ADDS    R2, R2, #7
        BEQ     %FT40                   ; if count expired, do last word

; do 1 word at a time

35
        MOV     R5, R14
        LDR     R14, [R0], #4
        ShiftR  R5, R14, R3, R4
        STR     R5, [R1], #4
        SUBS    R2, R2, #1
        BNE     %BT35

; do last word

40
        LDR     R5, [WsPtr, #CMRMask]   ; load right mask
45

; now test if any bits would be used (so we don't go off end of memory)

        MOV     R11, #&FFFFFFFF
        TST     R5, R11, LSL R4         ; NE => safe to read from here
        LDRNE   R11, [R0], #4           ; R14 = left word; R11 = right word
        ShiftR  R14, R11, R3, R4        ; form single word
        AND     R14, R14, R5            ; mask source word
        LDR     R10, [R1]               ; load dest word
        BIC     R10, R10, R5            ; mask dest word
        ORR     R10, R10, R14           ; OR two words
        STR     R10, [R1], #4           ; store back

; now go on to next row

        LDR     R7, [WsPtr, #CMVertCount]
        SUBS    R7, R7, #1
        BCC     EraseDest               ; finished, so go to erase dest stage
        STR     R7, [WsPtr, #CMVertCount]
        ADD     R11, WsPtr, #CMStuff
        LDMIA   R11, {R0-R6}            ; load up info again
        LDR     R7, [WsPtr, #CMVertDir]
        ADD     R0, R0, R7              ; move source pointer
        ADD     R1, R1, R7              ; and dest pointer
        STMIA   R11, {R0,R1}            ; store these back
        B       %BT10                   ; and loop

; non-shifting version, for speed
; do 7 words at a time

60
        MOV     R5, R14
        LDMIA   R0!, {R6-R11,R14}       ; load next 7 words
        STMIA   R1!, {R5-R11}
        SUBS    R2, R2, #7
        BCS     %BT60
        ADDS    R2, R2, #7
        BNE     %BT35                   ; count not expired, do last few words
        B       %BT40                   ; count expired, do last word

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

; source is to the left of dest, so start at right hand side

SrcHLTDest ROUT
        MOV     R0, R2                          ; rt coords are relevant ones
        MOV     R4, R6

        AND     R8, R4, R11                     ; R8 = DestR pixel offset
        AND     R14, R0, R11                    ; R14 = SrcR pixel offset
        SUB     R11, R14, R8                    ; R11 = Src-Dest pixel offset
        MOV     R11, R11, LSL R9                ; R11 = Src-Dest bit offset

        RSB     R11, R11, #32                   ; R11 = leftshift
        CMP     R11, #32                        ; if >= 32
        SUBCS   R11, R11, #32                   ; then put in range
        MOVCC   R10, #4                         ; else add 4
        MOVCS   R10, #0                         ; to src addr

        STR     R11, [WsPtr, #CMLShift]
        RSB     R11, R11, #32
        STR     R11, [WsPtr, #CMRShift]

        LDR     R6, [WsPtr, #LineLength]        ; (no longer need R6)

        CMP     R1, R5                          ; if SrcB >= DestB
        RSBGE   R6, R6, #0                      ; then go upwards
        STR     R6, [WsPtr, #CMVertDir]

        MOVLT   R1, R3                          ; else go down
        MOVLT   R5, R7                          ; so use top coords

        BL      ScreenAddr                      ; R2 = address of src corner
        ADD     R2, R2, R10
        STR     R2, [WsPtr, #CMSourceAddr]

        MOV     R0, R4
        MOV     R1, R5
        BL      ScreenAddr                      ; R2 = address of dest corner
        STR     R2, [WsPtr, #CMDestAddr]

        ADD     R11, WsPtr, #CMStuff
        LDMIA   R11, {R0-R6}            ; src,dest,cnt,rtshf,lfshf,rtmsk,ltmsk
10
        TEQ     R2, #0                  ; only one word on a line ?
        ANDEQ   R6, R6, R5              ; then leftmask:=leftmask AND rightmask
        LDREQ   R5, [R0], #-4           ; and load first word up
        BEQ     %FT45                   ; and do endword

; do first word

        LDMDA   R0!, {R11,R14}          ; load 2 words
        ShiftL  R11, R14, R3, R4        ; shift them
        AND     R14, R14, R5            ; AND with rightmask
        LDR     R10, [R1]               ; load word from dest
        BIC     R10, R10, R5            ; clear out bits in rightmask
        ORR     R10, R10, R14           ; OR in new bits
        STR     R10, [R1], #-4
        MOV     R5, R11
        SUBS    R2, R2, #1              ; decrement count
        BEQ     %FT40                   ; if zero, do finish word

        SUBS    R2, R2, #7              ; can we do 7 words ?
        BCC     %FT30                   ; no, then do 1 word at a time

; do 7 words at a time

        TEQ     R4, #0                  ; if leftshf=0
        BEQ     %FT60                   ; then do non-shifting version
20
 [ {TRUE}                               ; TMD optimisation 12-May-93
        MOV     R14, R5, LSL R4
        LDMDA   R0!, {R5-R11}
        ORR     R14, R14, R11, LSR R3
 |
        MOV     R14, R5
        LDMDA   R0!, {R5-R11}           ; load next 7 words
        ShiftL  R11, R14, R3, R4
 ]
        ShiftL  R10, R11, R3, R4
        ShiftL  R9, R10, R3, R4
        ShiftL  R8, R9, R3, R4
        ShiftL  R7, R8, R3, R4
        ShiftL  R6, R7, R3, R4
        ShiftL  R5, R6, R3, R4
        STMDA   R1!, {R6-R11,R14}
        SUBS    R2, R2, #7
        BCS     %BT20

30
        ADDS    R2, R2, #7
        BEQ     %FT40                   ; if count expired, do last word

; do 1 word at a time

35
        MOV     R14, R5
        LDR     R5, [R0], #-4
        ShiftL  R5, R14, R3, R4
        STR     R14, [R1], #-4
        SUBS    R2, R2, #1
        BNE     %BT35

; do last word

40
        LDR     R6, [WsPtr, #CMLMask]   ; load left mask
45

; now test if any bits would be used (so we don't go off start of memory)

        MOV     R11, #&FFFFFFFF
        TST     R6, R11, LSR R3         ; NE => safe to read from here
        LDRNE   R11, [R0], #-4          ; R11 = left word; R5 = right word
        ShiftL  R11, R5, R3, R4         ; form single word
        AND     R5, R5, R6              ; mask source word
        LDR     R10, [R1]               ; load dest word
        BIC     R10, R10, R6            ; mask dest word
        ORR     R10, R10, R5            ; OR two words
        STR     R10, [R1], #-4          ; store back

; now go on to next row

        LDR     R7, [WsPtr, #CMVertCount]
        SUBS    R7, R7, #1
        BCC     EraseDest               ; finished, so go to erase dest stage
        STR     R7, [WsPtr, #CMVertCount]
        ADD     R11, WsPtr, #CMStuff
        LDMIA   R11, {R0-R6}            ; load up info again
        LDR     R7, [WsPtr, #CMVertDir]
        ADD     R0, R0, R7              ; move source pointer
        ADD     R1, R1, R7              ; and dest pointer
        STMIA   R11, {R0,R1}            ; store these back
        B       %BT10                   ; and loop

; non-shifting version, for speed
; do 7 words at a time

60
        MOV     R14, R5
        LDMDA   R0!, {R5-R11}           ; load next 8 words
      [ SupportARMX
        MOV     R11, R11                ; Dummy instruction for XScale weirdness
      ]
        STMDA   R1!, {R6-R11,R14}
        SUBS    R2, R2, #7
        BCS     %BT60
        ADDS    R2, R2, #7
        BNE     %BT35                   ; count not expired, do last few words
        B       %BT40                   ; count expired, do last word

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

; Erase the area Dest2\Dest3

EraseDest ROUT

; first do the flat rectangle

        ADD     R8, WsPtr, #CMDest2L
        LDMIA   R8, {R0-R7}             ; R0..R3 = Dest2; R4..R7 = Dest3
        BL      EraseDifference

; and drop thru to ...

EraseSource
        LDR     R8, [WsPtr, #CMCopyFlag] ; 0 => move, 2 => copy
        TEQ     R8, #0                  ; is it a move
        Return  NE                      ; no, then exit

        ADD     R8, WsPtr, #CMSrc       ; R0..R3 = unclipped src
        LDMIA   R8, {R0-R7}             ; R4..R7 = unclipped dest

; window both source and destination in source domain

        LDR     R10, [WsPtr, #GWLCol]
        LDR     R11, [WsPtr, #GWRCol]

        SUBS    R8, R10, R0             ; R8 = GWLCol - SrcL
        ADDGT   R0, R0, R8              ; if > 0 then SrcL := GWLCol
        ADDGT   R4, R4, R8              ; and adjust DestL to match

        SUBS    R8, R2, R11             ; R8 = SrcR - GWRCol
        SUBGT   R2, R2, R8              ; if > 0 then SrcR := GWRCol
        SUBGT   R6, R6, R8              ; and adjust DestR to match

        CMP     R2, R0                  ; check SrcR >= SrcL
        Return  LT                      ; if not then nothing to erase

        LDR     R10, [WsPtr, #GWBRow]
        LDR     R11, [WsPtr, #GWTRow]

        SUBS    R8, R10, R1             ; R8 = GWBRow - SrcB
        ADDGT   R1, R1, R8              ; if > 0 then SrcB := GWBRow
        ADDGT   R5, R5, R8              ; and adjust DestB to match

        SUBS    R8, R3, R11             ; R8 = SrcT - GWTRow
        SUBGT   R3, R3, R8              ; if > 0 then SrcT := GWTRow
        SUBGT   R7, R7, R8              ; and adjust DestT to match

        CMP     R3, R1                  ; check SrcT >= SrcB
        Return  LT                      ; if not then nothing to erase

; now window the dest coords to the source

        CMP     R7, R3                  ; if DestT >= SrcT
        MOVGE   R7, R3                  ; then DestT := SrcT
        MOVLT   R5, R1                  ; else DestB := SrcB
        CMP     R6, R2                  ; if DestR >= SrcR
        MOVGE   R6, R2                  ; then DestR := SrcR
        MOVLT   R4, R0                  ; else DestL := SrcL

        Pull    R14

; and drop thru to ...

; *****************************************************************************
;
;       EraseDifference - Erase the difference between two rectangles with at
;                         least one vertical and one horizontal shared boundary
;
; in:   R0-R3 = larger one
;       R4-R7 = smaller one
;

EraseDifference ROUT

; first do the flat rectangle

        CMP     R6, R4                  ; check for Dest3 being null
        CMPGE   R7, R5
        BLT     RectFillA               ; if is, just clear Dest2

        Push    "R0-R7,R14"

        TEQ     R3, R7                  ; if Dest2T = Dest3T
        SUBEQ   R3, R5, #1              ; then top = Dest3B -1
        ADDNE   R1, R7, #1              ; else bottom = Dest3T +1
        CMP     R3, R1                  ; if top >= bottom
        BLGE    RectFillA

; now do the tall rectangle

        Pull    "R0-R7"

        TEQ     R3, R7                  ; if Dest2T = Dest3T
        MOVEQ   R1, R5                  ; then bottom = Dest3B
        MOVNE   R3, R7                  ; else top = Dest3T

        TEQ     R0, R4                  ; if Dest2L = Dest3L
        ADDEQ   R0, R6, #1              ; then left = Dest3R +1
        SUBNE   R2, R4, #1              ; else right = Dest3L -1

        CMP     R3, R1                  ; if top >= bottom
        CMPGE   R2, R0                  ; and right >= left
        BLGE    RectFillA               ; then fill it

        Pull    PC


        END