; 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