; 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.VduGrafG
;
; ARTHUR OPERATING SYSTEM - Vdu Drivers
; =======================
;
; Vdu driver code - Sprite stuff
;
; Author R C Manby
; Date   10.11.86
;

RangeB * 256
RangeC * 512

; Macros for various sprite operations

        MACRO
        KillSpChoosePtr
        MOV     R0, #0
        STR     R0, [WsPtr, #SpChoosePtr]
        MEND

        MACRO
        CopyDown $to,$from,$bytes, $tmp, $tmp2
        LDR     $tmp2, [WsPtr, #VduSprite]      ; sprite being output to by Vdu
        ADD     $tmp, $from, $bytes
        SUB     $tmp, $tmp, #1                  ; from + bytes -1
        CMP     $tmp2, $to                      ; if VOTS >= to
        CMPCS   $tmp, $tmp2                     ; and from+bytes-1 >= VOTS
        BCC     %FT00
        Push    "R0, R14"
        SUB     R0, $to, $from                  ; then adjust address vars
        BL      AdjustSpriteAddress             ; by offset = to-from
        Pull    "R0, R14"
00
        CMP     $bytes, #0                      ; bytes must be a multiple of 4
01
        LDRNE   $tmp, [$from], #4
        STRNE   $tmp, [$to], #4
        SUBNES  $bytes, $bytes, #4
        BNE     %BT01
        MEND

        MACRO
        CopyUp $to,$from,$bytes, $tmp, $tmp2
        LDR     $tmp2, [WsPtr, #VduSprite]      ; sprite being output to by Vdu
        ADD     $tmp, $to, $bytes
        SUB     $tmp, $tmp, #1                  ; to + bytes -1
        CMP     $tmp2, $from                    ; if VOTS >= from
        CMPCS   $tmp, $tmp2                     ; and to+bytes-1 >= VOTS
        BCC     %FT00
        Push    "R0, R14"
        SUB     R0, $to, $from                  ; then adjust address vars
        BL      AdjustSpriteAddress             ; by offset = to-from
        Pull    "R0, R14"
00
01
        SUBS    $bytes, $bytes, #4
        LDRCS   $tmp, [$from, $bytes]
        STRCS   $tmp, [$to, $bytes]
        BHI     %BT01
        MEND

; copy R0 bytes from R3 to R2 (R2+:=R0; R3+:=R0)
; corrupts R0, R4-R11, R14
; NB not used at present

        MACRO
        CopyDownFast
        SUBS    R0, R0, #9*4
10
        LDMCSIA R3!, {R4-R11,R14}
        STMCSIA R2!, {R4-R11,R14}
        SUBCSS  R0, R0, #9*4
        BCS     %BT10
        ADDS    R0, R0, #9*4
20
        LDRNE   R4, [R3], #4
        STRNE   R4, [R2], #4
        SUBNES  R0, R0, #4
        BNE     %BT20
        MEND

        MACRO
        ClearWords $from,$words, $tmp
        MOV     $tmp,#0
01
        SUBS    $words, $words, #1
        STRCS   $tmp, [$from], #4
        BHI     %BT01
        MEND

; *****************************************************************************
;
;       AdjustSpriteAddress - Move VduSprite, ScreenStart, CursorAddr and
;        InputCursorAddr by R0
;
;       Internal routine, called by routines that use macros
;        CopyDown, CopyUp
;
; in:   R0 = no. of bytes to add on (can be -ve)
;

AdjustSpriteAddress ROUT
        Push    R14
        LDR     R14, [WsPtr, #VduSprite]
        ADD     R14, R14, R0
        STR     R14, [WsPtr, #VduSprite]
        LDR     R14, [WsPtr, #ScreenStart]
        ADD     R14, R14, R0
        STR     R14, [WsPtr, #ScreenStart]
        B       AdjustCursorVars        ; update CursorAddr, InputCursorAddr

; *****************************************************************************
;
;       SpriteInit - Setup sprite workspace on reset (called whenever break
;                    is pressed)
;

SpriteInit ROUT
        Push    R14

        BL      ClearSpritePtrName      ; clear SpChoosePtr,SpChooseName

        Pull    PC

        LTORG

; *****************************************************************************
;
;       Vdu23_27 - SCHOOSE/SGET a numbered sprite
;
; in:   QQ?0 = 0 => Select sprite STR$(QQ?1) for plotting
;       QQ?0 = 1 => Sget an area of screen and put it in sprite STR$(QQ?1)
;

Vdu23_27 ROUT
        LDRB    R0, [WsPtr, #QQ+1]
        CMP     R0, #1
        MOVCC   R3, #SpriteReason_SelectSprite          ; 0-Select sprite
        MOVEQ   R3, #SpriteReason_GetSprite             ; 1-Pickup sprite
        MOVHI   PC, R14

        Push    R14

        LDRB    R0, [WsPtr, #QQ+2]      ; sprite number
        ADD     R1, WsPtr, #NameBuf
        MOV     R2, #4
        SWI     XOS_BinaryToDecimal
        MOV     R0, #13
        STRB    R0, [R1, R2]            ; $NameBuf := STR$(n)

        MOV     R0, R3                  ; R0 = sprite-op reason code
        MOV     R2, R1                  ; R2 -> sprite name
        MOV     R3, #0                  ; extension gap size
        SWI     XOS_SpriteOp            ; perform operation, ignore errors

        Pull    PC

; *****************************************************************************
;
;       SpritePlot - PLOT &E8-&EF,X,Y
;
; in:   R2 = plot code
;
; The 2 LSBits of the plot code specify fg/bg colour and action as :-
;  0 => No effect eqv. of Gcol(5,c)
;  1 => Plot sprite using foreground Gcol action
;  2 => Invert eqv. of Gcol(4,c)
;  3 => Plot mask in background colour and Gcol action
;

SpritePlot ROUT
        Push    R14

        AND     R3, R2, #3              ; 2 LSBits of plot code
        CMP     R3, #1
        MOVCC   R5, #5                  ; gcol action - no effect
        LDREQ   R5, [WsPtr, #GPLFMD]    ;              foreground
        MOVHI   R5, #4                  ;              invert or background

        AND     R5, R5, #&0F            ; knock out any ecf bits

        LDR     R2, [WsPtr, #SpChoosePtr]      ; If ChoosePtr <> 0
        CMP     R2, #0
        BNE     %FT10                   ; then use as pointer to sprite

        MOV     R0, #SpriteReason_SelectSprite ; else select it first
        LDR     R2, =SpChooseName
        ADD     R2, R2, WsPtr           ; sprite name ptr
        SWI     XOS_SpriteOp
        LDR     R2, [WsPtr, #SpChoosePtr]

10                                      ; R2 points to the sprite
        CMP     R3, #3
        MOVNE   R0, #RangeC+SpriteReason_PutSprite
        LDREQ   R0, =RangeC+SpriteReason_PlotMask
        LDR     R1, [WsPtr, #SpAreaStart]
        SWI     XOS_SpriteOp            ; perform operation, ignoring errors

        Pull    PC

; *****************************************************************************
;
;       SwiSpriteOp - Entry point for SWI OS_SpriteOp
;
; in:   R0 = sprite op reason code
;       R1 -> sprite area (usually)
;       R2 -> sprite (usually)
;       R3..? parameters
;

SwiSpriteOp ROUT
        Push    R14
        BranchNotJustUs %F10, SpriteV, R11, R14

; we are sole owners of SpriteV, so call our internal routine

        Push    PC                      ; push address of SwiSpriteReturn
        B       SpriteVecHandler
        &       0

SwiSpriteReturn
        Pull    R14
        ORRVS   R14, R14, #V_bit        ; if error, set V_bit in link
        ExitSWIHandler

; we are not the sole owners of SpriteV, so call the vector

10
        MOV     R10, #SpriteV
        BL      %FT20
        Pull    R14
        ORRVS   R14, R14, #V_bit        ; if error, set V_bit in link
        ExitSWIHandler

20
        CallAVector

; *****************************************************************************
;
;       SpriteVecHandler - Default owner of SpriteV
;
; in:   R0-R7 contain our entry parameters
;
; out:  R0-R7 contain exit parameters
;       R8-R12 are preserved
;

        MACRO
        SpriteOpDispatch $cond
        ADD$cond PC, R10, R11, ASR #8
        MEND

        MACRO
        SpriteOpEntry   $addr, $flags
        &       (($addr - SwiSpriteOpCallTb) :SHL: 8) + $flags
        MEND

; The end of this code is put before the beginning, so that
; SpriteDispatchReturn + SVC_mode is within ADR reach of the dispatcher

BadReasonCode ROUT
        ADRL    R0, SpriteErr_BadReasonCode
      [ International
        BL      TranslateError
      ]
        B       %FT20
SpriteIsCurrentDest
        ADRL    R0, SpriteErr_SpriteIsCurrentDest
      [ International
        BL      TranslateError
      ]
        B       %FT20

15
        ADRL    R0, SpriteErr_DoesntExist
      [ International
        BL      TranslateError
      ]

20                                      ; Exit SWI with error, error code in R0
        STR     R0, [WsPtr, #RetnReg0]
30
        SETV                            ; indicate an error

; and drop thru to...

SpriteDispatchReturn
        ADD     R11, WsPtr, #RetnReg0
        LDMIA   R11, {R0-R9}            ; Restore R0-R9
        MOV     R10, R13                ; Point at old returned registers
        Push    "R0-R9"                 ; Save new returned registers on stack
        LDMIA   R10, {R0-R9}            ; Load old returned registers
        STMIA   R11, {R0-R9}            ; and put them in the dump area
        Pull    "R0-R9"                 ; restore new returned registers
        ADD     R13, R13, #10*4         ; remove stack frame
        Pull    "R10-R12,PC"

SpriteVecHandler
        Push    "R10-R12"
        WritePSRc SVC_mode, WsPtr       ; Re-enable interupts

        VDWS    WsPtr                   ; Point R12 at Vdu driver workspace
        ADD     R11, WsPtr, #RetnReg0

        SUB     R13, R13, #10*4         ; Create stack frame for old RetnRegs
        MOV     R10, R13                ; Keep pointer to this frame
        Push    "R0-R9"                 ; Save new regs while we copy old ones
        LDMIA   R11, {R0-R9}            ; Load old regs
        STMIA   R10, {R0-R9}            ; and push them onto stack
        Pull    "R0-R9"                 ; Restore new regs
        STMIA   R11, {R0-R9}            ; Dump R0-R9

        CMP     R0, #RangeC + &100      ; if top bits out of range
        BCS     BadReasonCode           ; then error

        CMP     R0, #RangeB             ; if Range A type
        LDRCC   R1, [WsPtr, #SpAreaStart] ; then point at MOS sprite area

        AND     R0, R0, #&FF            ; Kill the range bits
        CMP     R0, #(SwiSpriteOpCallTb_End-SwiSpriteOpCallTb)/4
        BCS     BadReasonCode

        ADR     R10, SwiSpriteOpCallTb
        ADR     R14, SpriteDispatchReturn ; return address
        LDR     R11, [R10, R0, LSL #2]  ; load (offset<<8) + flags

        TST     R11, #SSO_ScreenNotAllowed ; if call can specify screen (R2=0)
        TEQEQ   R2, #0                  ; and it is specified
        MOVEQ   R1, #0                  ; then make sprite area ptr 0 as well
        TSTNE   R11, #SSO_NeedsSomething ; or nothing needed anyway
        SpriteOpDispatch EQ             ; then dispatch

        TEQ     R1, #0                  ; else needs sprite area ptr
        ADREQ   R0, SpriteErr_NoWorkSpace ; so if zero then invalid
      [ International
        BLEQ    TranslateError
      ]
        BEQ     %BT20

        TST     R11, #SSO_DangerAreaOp  ; if not a danger area op
        BEQ     %FT32                   ; then skip

        LDR     R9, [WsPtr, #VduSpriteArea] ; else check if sprite area is same
        TEQ     R9, R1
        BEQ     SpriteIsCurrentDest     ; and error if so

32
        TST     R11, #SSO_NeedsSprite   ; if doesn't need sprite
        BNE     %FT33

        TEQ     R0,#SpriteReason_CreateSprite
        BEQ     %FT21

        TEQ     R0,#SpriteReason_ScreenSave
        TEQNE   R0,#SpriteReason_GetSprite
        TEQNE   R0,#SpriteReason_GetSpriteUserCoords

        SpriteOpDispatch NE             ; let it go if we're not interested in it

        LDR     R9,[WsPtr, #Log2BPP]    ; fetch the current bpp

        CMP     R9,#4
        SpriteOpDispatch CC             ; let it go if below 16bpp
22
        CMP     R3,#0
        BNE     %FT34                   ; bang if it has a palette

        SpriteOpDispatch                ; then dispatch

21      ;createsprite. R6=mode number or sprite mode word, or => mode descriptor
        Push    "R0-R3,R14"
        MOV     R0,R6
        MOV     R1,#VduExt_Log2BPP
        SWI     XOS_ReadModeVariable
        MOVCS   R2,#0
        CMP     R2,#4
        Pull    "R0-R3,R14"
        SpriteOpDispatch CC
        B       %BT22

33
        TEQ     R2, #0                  ; if sprite ptr is zero it's a boob
        BEQ     %BT15                   ; so say SpriteDoesntExist

        LDR     R9, [WsPtr, #RetnReg0]
        CMP     R9, #RangeC             ; if not range C then must look up
        BCS     %FT35                   ; sprite name and convert to pt4
        Push    R14
        BL      SpriteCtrlBlk           ; in: R2 -> name; out: R2 -> sprite
        Pull    R14
        BVS     %BT15                   ; no such sprite

35

;medusa note.
;
;On Medusa masks and palettes for 16/32bpp sprites are in a transient state
;Medusa will fault 16/32bpp mask/palette operations, in readiness for the
;introduction of new (more compact) mask/palette formats.
;
;another medusa note.
;
;Mask operations using 1bpp masks on new format sprites are now being included.
;However palettes on 16/32bpp are still not allowed.
;

;amg 12/11/93, sort out the logic here so that palettes on new format sprites
;really do get faulted

;amg 25th May 1994. We now allow palettes on new format sprites of 8bpp
;and below

        ; find the sprite type
        Push   "R14"
        LDR    R9,[R2,#spMode]

        ANDS   R14,R9,#15<<27           ; get the sprite type
        BEQ    %FT37                    ; t=0 (ie old format)

        CMP    R14,#SpriteType_RISCOS5<<27 ; if it's a RISC OS 5 sprite mode word
        MOVEQ  R14,R9,LSR #20            ; then get the actual sprite type
        MOVNE  R14,R14,LSR #27
        ANDEQS R14,R14,#127
        MOVEQ  R14,#SpriteType_Substitute ; Panic and treat as 32bpp?

        CMP    R14,#SpriteType_New16bpp ; check sprite type number
        BCC    %FT38                    ; despatch if new format and under 16bpp


39
        ; so, does it have a palette
        ADD    R9,R2,#spImage
        LDMIA  R9,{R9,R14}              ; pick up offsets to mask & image
        CMP    R9,R14
        MOVGT  R9,R14                   ; R9->top of palette block
        SUBS   R9,R9,#spPalette         ; R9 = size of palette block
        BEQ    %FT38                    ; no palette, so no error

        Pull   "R14"
34
        ADRL    R0, SpriteErr_NoMaskOrPaletteAllowedInThisDepth
        [ International
        BL      TranslateError
        ]
        B       %BT20

37      ;however, until mode selectors work there are some 16/32bpp old modes
        Push    "R0-R3"                 ; save context
        MOV     R0,R9
        MOV     R1,#VduExt_Log2BPP
        SWI     XOS_ReadModeVariable    ; read log2bpp for the sprite's mode
        MOV     R9,R2                   ; and move it for posterity
        Pull    "R0-R3"
        CMP     R9,#4
        BCS     %BT39                   ; log2bpp of 4 = 16bpp, so see if we want to fault it
38
        TST     R11, #SSO_DangerOp      ; if a destructive op
        Pull    "R14"
        BEQ     %FT40

        LDR     R9, [WsPtr, #VduSprite]
        TEQ     R9, R2
        BEQ     SpriteIsCurrentDest     ; then stop him!

40
        TST     R11, #SSO_NeedsSpriteModeData
        SpriteOpDispatch EQ

        Push    "R5-R7, R10, R11, R14"
        BL      SetupSprModeData
        Pull    "R5-R7, R10, R11, R14"
        SpriteOpDispatch VC

        B       %BT30                   ; Invalid spMode field


SSO_ScreenNotAllowed    * 1 :SHL: 0
SSO_NeedsSomething      * 1 :SHL: 1
SSO_NeedsSprite         * 1 :SHL: 2
SSO_NeedsSpriteModeData * 1 :SHL: 3
SSO_DangerOp            * 1 :SHL: 4
SSO_DangerAreaOp        * 1 :SHL: 5

Group1  * SSO_ScreenNotAllowed
Group2  * Group1 + SSO_NeedsSomething
Group3  * Group2 + SSO_NeedsSprite
Group4  * Group3 + SSO_NeedsSpriteModeData
Group5  * SSO_NeedsSomething + SSO_NeedsSprite

SwiSpriteOpCallTb
        SpriteOpEntry BadReasonCode, Group1
        SpriteOpEntry BadReasonCode, Group1
        SpriteOpEntry ScreenSave, Group1
        SpriteOpEntry ScreenLoad, Group1
        SpriteOpEntry BadReasonCode, Group1 ; 4
        SpriteOpEntry BadReasonCode, Group1 ; 5
        SpriteOpEntry BadReasonCode, Group1 ; 6
        SpriteOpEntry BadReasonCode, Group1 ; 7

; The following need valid workspace

        SpriteOpEntry ReadAreaCB, Group2
        SpriteOpEntry ClearSprites, Group2 + SSO_DangerAreaOp   ; *SNew
        SpriteOpEntry LoadSpriteFile, Group2 + SSO_DangerAreaOp
                                                ; *SLoad <filename>
        SpriteOpEntry MergeSpriteFile, Group2 + SSO_DangerAreaOp
                                                ; *SMerge <filename>
        SpriteOpEntry SaveSpriteFile, Group2    ; *SSave <filename>
        SpriteOpEntry ReturnName, Group2
        SpriteOpEntry GetSprite, Group2         ; *SGet <name>
        SpriteOpEntry CreateSprite, Group2
        SpriteOpEntry GetSpriteUserCoords, Group2
        SpriteOpEntry BadReasonCode, Group2 ; 17 CheckSpriteArea
        SpriteOpEntry BadReasonCode, Group2 ; 18
        SpriteOpEntry BadReasonCode, Group2 ; 19
        SpriteOpEntry BadReasonCode, Group2 ; 20
        SpriteOpEntry BadReasonCode, Group2 ; 21
        SpriteOpEntry BadReasonCode, Group2 ; 22
        SpriteOpEntry BadReasonCode, Group2 ; 23

; The following need a sprite

        SpriteOpEntry SelectSprite, Group3      ; *SChoose <n> [<m>]
        SpriteOpEntry DeleteSprite, Group3 + SSO_DangerOp       ; *SDelete <n>
        SpriteOpEntry RenameSprite, Group3                      ; *SRename
        SpriteOpEntry CopySprite, Group3                        ; *SCopy
        SpriteOpEntry PutSprite, Group3
        SpriteOpEntry CreateMask, Group3
        SpriteOpEntry RemoveMask, Group3 + SSO_DangerOp
        SpriteOpEntry InsertRow, Group3 + SSO_DangerOp
        SpriteOpEntry DeleteRow, Group3 + SSO_DangerOp
        SpriteOpEntry FlipAboutXAxis, Group3
        SpriteOpEntry PutSpriteUserCoords, Group3
        SpriteOpEntry BadReasonCode, Group3 ; 35 AppendSprite
        SpriteOpEntry BadReasonCode, Group3 ; 36 SetPointerShape
        SpriteOpEntry BadReasonCode, Group3 ; 37 CreateRemovePalette
        SpriteOpEntry BadReasonCode, Group3 ; 38 CreateRemoveAlpha
        SpriteOpEntry BadReasonCode, Group3 ; 39

; The following need sprite mode data

        SpriteOpEntry ReadSpriteSize, Group4
        SpriteOpEntry ReadPixelColour, Group4
        SpriteOpEntry WritePixelColour, Group4
        SpriteOpEntry ReadPixelMask, Group4
        SpriteOpEntry WritePixelMask, Group4
        SpriteOpEntry InsertCol, Group4 + SSO_DangerOp
        SpriteOpEntry DeleteCol, Group4 + SSO_DangerOp
        SpriteOpEntry FlipAboutYAxis, Group4 + SSO_DangerOp
        SpriteOpEntry PlotMask, Group4
        SpriteOpEntry PlotMaskUserCoords, Group4
        SpriteOpEntry BadReasonCode, Group4 ; 50 PlotMaskScaled
        SpriteOpEntry BadReasonCode, Group4 ; 51 PaintCharScaled
        SpriteOpEntry BadReasonCode, Group4 ; 52 PutSpriteScaled
        SpriteOpEntry BadReasonCode, Group4 ; 53 PutSpriteGreyScaled
        SpriteOpEntry RemoveLeftHandWastage, Group4
        SpriteOpEntry BadReasonCode, Group4 ; 55 PlotMaskTransformed
        SpriteOpEntry BadReasonCode, Group4 ; 56 PutSpriteTransformed
        SpriteOpEntry BadReasonCode, Group4 ; 57 InsertDeleteRows
        SpriteOpEntry BadReasonCode, Group4 ; 58 InsertDeleteColumns
        SpriteOpEntry BadReasonCode, Group4 ; 59 pseudo reason used by Wimp

; The following need (sprite area + sprite) or (anything + 0), meaning screen

        SpriteOpEntry SwitchOutputToSprite, Group5 ; 60
        SpriteOpEntry SwitchOutputToMask, Group5   ; 61
        SpriteOpEntry ReadSaveAreaSize, Group5     ; 62

SwiSpriteOpCallTb_End


; *****************************************************************************
;
;       SetupSprModeData - Set up registers and variables from the sprite mode
;
;       Internal routine: called by sprite dispatch, CreateHeader
;
; in:   R2 -> sprite
;
; out:  R0-R5 preserved
;       R6 = ReadNColour (for a single screen pixel)
;       R7 = WriteNColour (=R6 except in double pixel modes)
;       R8 = BytesPerChar }
;       R9 = XShftFactor  } calculated from Log2BPC which is BitsPerPix
;       R10 = NPix        }  corrected for double pixel modes
;       R11 = Log2BPC     }
;
;       SprReadNColour..SprLog2BPC setup accordingly
;
;       If error, then RetnReg0 updated

SetupSprModeData ROUT
        Push    R14
        LDR     R11, [R2, #spMode]
        CMP     r11, #&100              ; check for mode selector/new format sprite word
        BCC     %FT05                   ; [it's a mode number so check for known ones]
        TST     r11, #1                 ; if it's a new format sprite word
        BNE     %FT10                   ; then call pushmodeinfo to get info on it
        B       %FT20                   ; else it's a mode selector, which is illegal as a sprite mode word
05
        BranchIfKnownMode R11, %FA10
        Push    "R2-R4"
        MOV     R2, R11
        BL      OfferModeExtensionAnyMonitor
        Pull    "R2-R4"
        BNE     %FT20

10
        MOV     R10, R11
        BL      PushModeInfoAnyMonitor
        BVS     %FT30                   ; if duff new format sprite word, return error

        LDR     R11, [R13, #wkModeFlags]
        TST     R11, #ModeFlag_NonGraphic
        BNE     %FT15                   ; non-graphic mode
        STR     R11, [WsPtr, #SprModeFlags]

        LDR     R11, [R13, #wkLog2BPC]                  ; Log2BPC
        LDR     R14, [R13, #wkLog2BPP]                  ; Log2BPP

        ADD     R13, R13, #PushedInfoSize

        MOV     R7, #1
        RSB     R9, R11, #5             ; XShftFactor
        RSB     R10, R7, R7, LSL R9     ; NPix
        MOV     R6, R7, LSL R14
        MOV     R8, R7, LSL R11         ; BytesPerChar
        RSB     R6, R7, R7, LSL R6      ; ReadNColour
        RSB     R7, R7, R7, LSL R8      ; WriteNColour

        Push    R5
        ADD     R5, WsPtr, #SprReadNColour
        STMIA   R5, {R6-R11,R14}        ; SprRead..SprLog2BPP
        CLRV
        Pull    "R5, PC"

15
        ADD     R13, R13, #PushedInfoSize
20
        ADRL    R0, SpriteErr_InvalidSpriteMode
      [ International
        BL      TranslateError
      ]
30
        STR     R0, [WsPtr, #RetnReg0]
        SETV
        Pull    pc


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

; Blocks for sprite errors

SpriteErr_NoWorkSpace                       MakeErrorBlock Sprite_NoWorkSpace
SpriteErr_NoRoom                            MakeErrorBlock Sprite_NoRoom
SpriteErr_DoesntExist                       MakeErrorBlock Sprite_DoesntExist
SpriteErr_NoSprites                         MakeErrorBlock Sprite_NoSprites
SpriteErr_NotGraphics                       MakeErrorBlock Sprite_NotGraphics
SpriteErr_NotEnoughRoom                     MakeErrorBlock Sprite_NotEnoughRoom
SpriteErr_BadSpriteFile                     MakeErrorBlock Sprite_BadSpriteFile
SpriteErr_NoRoomToMerge                     MakeErrorBlock Sprite_NoRoomToMerge
SpriteErr_Bad2ndPtr                         MakeErrorBlock Sprite_Bad2ndPtr
SpriteErr_InvalidRowOrCol                   MakeErrorBlock Sprite_InvalidRowOrCol
SpriteErr_InvalidHeight                     MakeErrorBlock Sprite_InvalidHeight
SpriteErr_InvalidWidth                      MakeErrorBlock Sprite_InvalidWidth
SpriteErr_NoRoomToInsert                    MakeErrorBlock Sprite_NoRoomToInsert
SpriteErr_SpriteAlreadyExists               MakeErrorBlock Sprite_SpriteAlreadyExists
SpriteErr_InvalidSpriteMode                 MakeErrorBlock Sprite_InvalidSpriteMode
SpriteErr_BadReasonCode                     MakeErrorBlock Sprite_BadReasonCode
SpriteErr_CantInTeletext                    MakeErrorBlock Sprite_CantInTeletext
SpriteErr_InvalidSaveArea                   MakeErrorBlock Sprite_InvalidSaveArea
SpriteErr_SpriteIsCurrentDest               MakeErrorBlock Sprite_SpriteIsCurrentDest
SpriteErr_NoMaskOrPaletteAllowedInThisDepth MakeErrorBlock Sprite_NoMaskOrPaletteAllowedInThisDepth

; *****************************************************************************
;
;       ClearSprites - Clear sprite area (*SNEW)
;
;       External routine + dropped thru to
;

ClearSprites ROUT
        LDR     R2, [R1, #saFirst]
        STR     R2, [R1, #saFree]
        MOV     R2, #0
        STR     R2, [R1,#saNumber]

        LDR     R0, [WsPtr, #RetnReg0]  ; if rangeb or rangec
        CMP     R0, #RangeB
        BHS     DoesNowt                ; exit immediately

; else its rangea, so drop thru to ...

ClearSpritePtrName ROUT
        MOV     R0, #0
        STR     R0, [WsPtr, #SpChoosePtr]
        STR     R0, [WsPtr, #SpChooseName]
        STR     R0, [WsPtr, #SpChooseName+4]
        STR     R0, [WsPtr, #SpChooseName+8]
        MOV     R0, #13
        STRB    R0, [WsPtr, #SpChooseName+12]   ; *SChoose <null name>
DoesNowt
        RETURNVC

; *****************************************************************************
;
;       ReadAreaCB - Read information from sprite area CB into registers
;
;       External routine
;
; in:   R1 -> sprite area
;
; out:  R2 = offset to end of sprite area (ie total size)
;       R3 = number of sprites in area
;       R4 = offset to first sprite
;       R5 = offset to first free word
;

ReadAreaCB ROUT
        LDMIA   R1, {R2,R3,R4,R5}               ; saEnd,saNumber,saFirst,saFree
        ADD     R11, WsPtr, #RetnReg2
        STMIA   R11, {R2,R3,R4,R5}
        RETURNVC

; *****************************************************************************
;
;       SelectSprite - Select a named sprite for use by PLOT &E8..&EF
;
;       External routine + called by GetSprite
;
; in:   R2 -> sprite CB
;
; out:  R0, R9..R11 corrupted
;       If not using system sprite area, then R2 -> address of sprite
;

SelectSprite ROUT
        Push    R14
        LDR     R0, [WsPtr, #RetnReg0]          ; if not in system sprite area
        CMP     R0, #RangeB
        STRCS   R2, [WsPtr, #RetnReg2]          ; return the sprite address
        Pull    PC, CS

        STR     R2, [WsPtr, #SpChoosePtr]       ; else store name & address
        ADD     R14, R2, #spName                ; for use by PLOT
        LDMIA   R14, {R9,R10,R11}               ; load 12 bytes of name
        LDR     R14, =SpChooseName
        ADD     R14, R14, WsPtr                 ; RetnReg2 NOT altered, so user
        STMIA   R14, {R9,R10,R11}               ; can't poke the workspace
        Pull    PC

; *****************************************************************************
;
;       ReturnName - Return name of nth sprite in sprite area as a string
;
;       External routine
;
; in:   R1 -> sprite area
;       R2 -> buffer
;       R3 = max buffer length
;       R4 = sprite number (n)
;
; out:  R3 actual string length
;       RetnReg3 updated
;

ReturnName ROUT
        LDR     R5, [R1, #saNumber]     ; check for 1 <= R4 <= saNumber
        CMP     R4, #1
        CMPGE   R5, R4
        BGE     %FT05

        ADRL    R0, SpriteErr_DoesntExist ; out of range, so generate error
      [ International
        Push    "lr"
        BL      TranslateError_VClear
        Pull    "lr"
      ]
        STR     R0, [WsPtr, #RetnReg0]
        RETURNVS

05      LDR     R5, [R1, #saFirst]
        ADD     R5, R5, R1              ; ptr to 1st sprite
10
        SUBS    R4, R4, #1
        LDRNE   R6, [R5, #spNext]       ; if not sprite we want
        ADDNE   R5, R5, R6              ; chain to next one
        BNE     %BT10

        ADD     R5, R5, #spName         ; found sprite, R5 -> name

        SUBS    R3, R3, #1
        MOVLS   R3, #0                  ; if was 0 or 1 then set R3 to 0
        STRLS   R3, [WsPtr, #RetnReg3]
        STREQB  R3, [R2]                ; if was 1 then store 0 terminator
        MOVLS   PC, R14                 ; if was 0 or 1 then exit (assume SUB cleared V)

        CMP     R3, #SpriteNameSize     ; if length > maximum sprite name len
        MOVHI   R3, #SpriteNameSize     ; then limit to maximum sprite name len

; R3 is now maximum number of characters to store, excluding terminator

        MOV     R6, R2                  ; remember start address
20
        LDRB    R4, [R5], #1            ; load a byte from sprite name
        CMP     R4, #" "                ; if char > " "
        STRHIB  R4, [R2], #1            ; then store character and inc ptr
        SUBHIS  R3, R3, #1              ; and decrement character count
        BHI     %BT20                   ; loop until char<=" " or count expired

        MOV     R4, #0                  ; store terminating 0
        STRB    R4, [R2]
        SUB     R3, R2, R6              ; R3 = no. of characters,
        STR     R3, [WsPtr, #RetnReg3]  ; excluding terminator
        RETURNVC                        ; indicate no error

; *****************************************************************************
;
;       RenameSprite - Rename a sprite
;
;       External routine (AlreadyExists is used by CopySprite)
;
; in:   R2 -> sprite
;       R3 -> new name
;

RenameSprite ROUT
        Push    "R2, R14"
        MOV     R2, R3
        BL      GetName                 ; returns name in R9-R11
        BL      SpriteCtrlBlk           ; try to find sprite of that name
        BVC     AlreadyExists
        Pull    "R2, R14"
        ADD     R8, R2, #spName
        STMIA   R8, {R9-R11}
        KillSpChoosePtr                 ; in case it points to renamed sprite
        RETURNVC

AlreadyExists                           ; sprite with new name already exists
        Pull    "R3, R14"
        TEQ     R2, R3                  ; if it's the same one, then exit VC
        RETURNVC                        ; (SRename/SCopy fred fred is allowed)

        ADRL    R0, SpriteErr_SpriteAlreadyExists
      [ International
        Push    "lr"
        BL      TranslateError
        Pull    "lr"
      ]
        STR     R0, [WsPtr,#RetnReg0]
        RETURNVS

; *****************************************************************************
;
;       CopySprite - Make a copy of a sprite
;
;       External routine
;
; in:   R1 -> sprite area
;       R2 -> sprite
;       R3 -> new name
;

CopySprite ROUT
        Push    "R2, R14"               ; save ptr to sprite to be copied
        MOV     R2, R3
        BL      GetName                 ; returns new name in R9-R11
        BL      SpriteCtrlBlk           ; try to find sprite of that name
        BVC     AlreadyExists           ; [we found one of that name]
        Pull    R2
        LDR     R8, [R1, #saFree]
        ADD     R8, R8, R1              ; address sprite will be copied to
        ADD     R8, R8, #spName         ; address of its name field
        BL      AppendSprite            ; copy it
        STMVCIA R8, {R9-R11}            ; if copy OK, rename it
        Pull    PC                      ; exit with V clear/set appropriately

; *****************************************************************************
;
;       ReadSpriteSize - Read sprite size and other info
;
;       External routine
;
; in:   R2 -> sprite
;
; out:  R1,R2 preserved
;       R3 = width in pixels
;       R4 = height in pixels
;       R5 = 0/1 for solid/transparent
;       R6 = mode sprite was defined in
;
;       RetnReg3..RetnReg6 updated
;

ReadSpriteSize ROUT
        ADD     R3, R2, #spWidth
        LDMIA   R3, {R3,R4,R5,R6}       ; spWidth,spHeight,spLBit,spRBit
        ADD     R4, R4, #1              ; R4 := height in pixels
        ADD     R3, R3, #1              ; R3 := width in words
        RSB     R3, R5, R3, LSL #5      ; convert to bits and sub LH wastage
        SUB     R3, R3, #31             ; subtract RH wastage
        ADD     R3, R3, R6
        LDR     R11, [WsPtr, #SprLog2BPC]
        MOV     R3, R3, LSR R11         ; number of pixels in row
        LDR     R5, [R2, #spImage]
        LDR     R6, [R2, #spTrans]
        SUBS    R5, R5, R6
        MOVNE   R5, #1                  ; if spImage=spTrans then no mask
        LDR     R6, [R2, #spMode]

        ADD     R11, WsPtr, #RetnReg3
        STMIA   R11, {R3-R6}

        RETURNVC

; *****************************************************************************
;
;       ReadSpriteWidth - Read width of a sprite
;
;       Internal routine, called by PlotMask, InsertCol, DeleteCol
;
; in:   R2 -> sprite
;
; out:  R0 = sprite width in pixels
;       All other registers preserved
;

ReadSpriteWidth ROUT
        Push    "R4-R6, R14"
        ADD     R0, R2, #spWidth
        LDMIA   R0, {R0,R4,R5,R6}       ; spWidth,spHeight,spLBit,spRBit
        ADD     R0, R0, #1              ; width in words
        RSB     R0, R5, R0, LSL #5
        SUB     R0, R0, #31
        ADD     R0, R0, R6              ; total number of bits in row
        LDR     R5, [WsPtr, #SprLog2BPC]
        MOV     R0, R0, LSR R5          ; number of pixels in row
        Pull    "R4-R6, PC"

; *****************************************************************************
;
;       DeleteSpriteByName
;
;       Internal routine, called by MergeSprite, GetSprite, CreateSprite
;
; in:   R1 -> sprite area
;       R2 -> name of sprite to be deleted
;
; out:  All registers preserved
;

DeleteSpriteByName ROUT
        Push    "R2-R4, R14"
        BL      SpriteCtrlBlk           ; R2: In , SpriteNamePtr
                                        ;     Out, SpriteCBptr
        BLVC    DeleteSprite            ; if found, then delete it
        Pull    "R2-R4, PC"

; *****************************************************************************
;
;       DeleteSprite
;
;       External routine + called by DeleteSpriteByName
;
; in:   R1 -> sprite area
;       R2 -> sprite
;
; out:  All registers preserved
;

DeleteSprite ROUT
        Push    "R0, R3,R4, R14"
        LDR     R3, [R2, #spNext]
        ADD     R3, R3, R2
        LDR     R0, [R1, #saFree]
        ADD     R0, R0, R1
        SUB     R0, R0, R3
        CopyDown R2, R3, R0, R14, R4
        LDR     R3, [R1, #saNumber]     ; decrement sprite count
        SUB     R3, R3, #1
        STR     R3, [R1, #saNumber]
        SUB     R3, R2, R1              ; (R2 points to first free location)
        STR     R3, [R1, #saFree]       ; update saFree

        KillSpChoosePtr                 ;Because the sprites have moved

        Pull    "R0, R3,R4, PC"

; *****************************************************************************
;
;       GetName - Read sprite name into buffer, lower cased, padded to 12 chars
;
;       Internal routine, called by RenameSprite, CopySprite,
;        SpriteCtrlBlk, CreateHeader
;
; in:   R2 -> sprite name (terminated by char <= 32)
;
; out:  Name returned in R9-R11 and NameBuf
;       All other registers preserved
;

GetName ROUT
        Push    "R2-R5, R14"
        ADD     R3, WsPtr, #NameBuf
        MOV     R4, #SpriteNameSize
10
        LDRB    R5, [R2], #1
        uk_LowerCase R5, R14
        CMP     R5, #" "
        STRHIB  R5, [R3], #1
        SUBHIS  R4, R4, #1              ; loop until char<=32 or done 12 chars
        BHI     %BT10

        MOV     R5, #0
        CMP     R4, #0                  ; pad with 0 or more nulls
20
        STRHIB  R5, [R3], #1
        SUBHIS  R4, R4, #1
        BHI     %BT20

        ADD     R9, WsPtr, #NameBuf
        LDMIA   R9, {R9-R11}            ; name returned in R9-R11 and NameBuf

        Pull    "R2-R5, PC"

; *****************************************************************************
;
;       SpriteCtrlBlk - Search for control block of named sprite
;
;       Internal routine, called by sprite dispatch, RenameSprite,
;        CopySprite, DeleteSpriteByName
;
; in:   R1 -> sprite area
;       R2 -> sprite name
;
; out:  V=0 => R2 -> sprite
;       V=1 => sprite not found
;              R2 -> first free byte (this fact not used by anyone yet)
;       All other registers preserved
;

SpriteCtrlBlk ROUT
        Entry   "R3-R11"
        ASSERT  saNumber = 4
        ASSERT  saFirst = 8
        LDMIB   R1, {R4,R5}             ; saNumber, saFirst
        ADD     R3, R5, R1              ; point to first sprite/free space
        CMP     R4, #0
        BEQ     %FT20                   ; no sprites, exit with R3 pointing
                                        ; at free space and R4=0
        BL      GetName                 ; search name in R9-R11 and NameBuf
10
        LDMIA   R3, {R5, R6,R7,R8}      ; spNext, spName(0..2)
        CMP     R6, R9
        CMPEQ   R7, R10
        CMPEQ   R8, R11                 ; (V:=0 if equal)
        BEQ     %FT30                   ; sprite found

        ADD     R3, R3, R5
        SUBS    R4, R4, #1              ; try next sprite
        BNE     %BT10
20
        SETV                            ; indicate not found
30
        MOV     R2, R3                  ; R2 -> sprite or to free space
        EXIT

; As above, but uses source name directly without passing it through GetName
; Name must be word aligned and in correct format!

SpriteCtrlBlkNoGetName
        ALTENTRY
        ASSERT  saNumber = 4
        ASSERT  saFirst = 8
        LDMIB   R1, {R4,R5}             ; saNumber, saFirst
        ADD     R3, R5, R1              ; point to first sprite/free space
        CMP     R4, #0
        BEQ     %BT20                   ; no sprites, exit with R3 pointing
        LDMIA   R2, {R9-R11}
        B       %BT10

        LTORG

; *****************************************************************************
;
;       InternaliseCoords - Convert from external to internal coords
;                           for sprite plotting
;
;       Internal routine called by PutSpriteUserCoords, PlotMaskUserCoords
;
; in:   R3 = external X coordinate
;       R4 = external Y coordinate
;
; out:  R3 = internal X coordinate
;       R4 = internal Y coordinate
;       R1, R2, R5 preserved
;

InternaliseCoords ROUT
        Push    "R1,R2,R5, R14"
        MOV     R0, R3                  ; put external coordinate in R0,R1
        MOV     R1, R4
        ADD     R7, WsPtr, #GCsX
        LDMIA   R7, {R8,R9}             ; preserve GCsX,GCsY around EIG
        MOV     R2, #4                  ; indicate absolute coords
        BL      EIG
        STMIA   R7, {R8,R9}             ; restore GcsX,GCsY
        MOV     R3, R0                  ; internal coord of plotting point
        MOV     R4, R1
        Pull    "R1,R2,R5, PC"

; *****************************************************************************
;
;       PutSpriteUserCoords - Draw sprite to screen using given ext. coords
;
;       External routine
;
; in:   R1 -> sprite area (not used)
;       R2 -> sprite
;       R3 = X coordinate to plot at
;       R4 = Y coordinate to plot at
;       R5 = GCOL action to be used
;

PutSpriteUserCoords ROUT
        Push    R14
        BL      InternaliseCoords
        Pull    R14
        B       PutSpri20

; *****************************************************************************
;
;       PutSprite - Draw sprite to screen at NewPt
;
;       External routine + PutSpri20 called by PutSpriteUserCoords
;        (also external)
;
; in:   R1 -> sprite area (not used)
;       R2 -> sprite
;       R5 = GCOL action to be used
;

; Values held in Ram
;
; N.B. The ordering of these variables is VERY important
;      rearrange at your peril
;
; Within the 'memory to screen' drawing loop, WsPtr (R12) points at
;  ScrAdr, variables either side being loaded/saved in groups using
;  LDMDB / STMIA / LDMIB
;
;           SPltWidth   }
;           SPltHeight  }
;           SPltScrOff  }
;           SPltMemOff  } LDMDB
; WsPtr ->  SPltScrAdr    } STMIA
;           SPltColCnt    } } LDMIB
;           SPltMemAdr      }
;           SPltShftR       }
;           SPltShftL       }
;
;           SPltMskAdr
;
;           SPltLMask
;           SPltRMask
;
;           SPltzgooPtr
;
;

PutSprite ROUT
        ASSERT  NewPtY = NewPtX +4
        ADD     R3, WsPtr, #NewPtX
        LDMIA   R3, {R3, R4}            ; plot sprite at NewPt(X,Y)
PutSpri20
        GraphicsMode R0
        BNE     PutSpriNotGraphics      ; quit with error if not graphics mode

        Push    "WsPtr, R14"            ; push both so R12,R14 are free for use

        Push    "R2"                    ; save the pointer to the sprite

        BL      GenSpritePlotParmBlk

        Pull    "R11"

        BVS     SpriteOffScreen

        SWI     XOS_RemoveCursors       ; assume no error can occur!

        LDR     R5, [WsPtr, #SPltMemAdr]
        LDR     R6, [WsPtr, #SPltMskAdr]
        TEQ     R6, #0
        BEQ     %FT20

        ADD     R6, R6, R5
        STR     R6, [WsPtr, #SPltMskAdr]

        LDR     R14, [R11, #spMode]
        MOVS    R8,R14,ASR #27          ; check for wide mask
        BMI     PutSpriWideMaskErr
        TEQ     R8,#SpriteType_RISCOS5
      [ NoARMT2
        ANDEQ   R8, R14, #127<<20
        MOVEQ   R8, R8, LSR #20
      |
        UBFXEQ  R8, R14, #20, #7
      ]

        ;note: new format 1bpp sprites are thrown at the old routine, since it
        ;will render them faster

        CMP     R8, #2
        BCC     TransPlot               ; it's got transparent bits in it! (
        BCS     NewTransPlot            ; it's got 1bpp transparent bits in it!!!
20

; Plot sprite ignoring transparency mask (if any)


        ADD     R11, WsPtr, #SPltzgooMasks
        LDMIA   R11, {R8-R11}           ; masks for screen access (zgoo..zgee)

        LDR     R2, [WsPtr, #SPltAction]  ; GCOL action
        ADD     WsPtr, WsPtr, #SPltScrAdr ; repoint WsPtr at SPltScrAdr
        LDMIA   WsPtr, {R0-R1,R5-R7}

        TST     R2, #7
        BNE     SolPl10                 ; not store, do it slowly

; SimpleCase

SolPlFast10

;       R0    ,R1    ,     R5    ,R6   ,R7   ,(R8  ,R9  ,R10 ,R11 )
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,(zgoo,zgeo,zgoe,zgee)

; Plot the first (leftmost) word

        LDMIA   R5, {R2,R3}
        ADD     R5, R5, #4

        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
                                        ; we only need result word R2
        LDR     R4, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
        AND     R3, R2, R4              ; mask down to just the required pixels

 [ AvoidScreenReads
        CMP     R4, #&FFFFFFFF
        LDRNE   R2, [R0]                ; plot 1 word
        BICNE   R2, R2, R4              ; knock out mask bits
        ORRNE   R3, R2, R3              ; and or sprite bits
 |
        LDR     R2, [R0]                ; plot 1 word
        BIC     R2, R2, R4              ; knock out mask bits
        ORR     R3, R2, R3              ; and or sprite bits
 ]
        STR     R3, [R0], #4

        SUBS    R1, R1, #1
        BLT     SolPlFast50             ; if all plotted, try next scanline
                                        ; else try for blocks of 7
        SUBS    R1, R1, #7
        BLT     SolPlFast30

        TEQ     R6, #0                  ; if shift needed, use loop 20
        BNE     SolPlFast20             ; else loop 15

SolPlFast15

;       R0    ,R1         ,R5
;       ScrAdr,ColCnt,    ,MemAdr

        LDMIA   R5!, {R2-R4,R8-R11}     ; read 7 words
        STMIA   R0!, {R2-R4,R8-R11}     ; write 7 words back to screen
        SUBS    R1, R1, #7
        BGE     SolPlFast15
        B       SolPlFast30

SolPlFast20

;       R0    ,R1         ,R5    ,R6   ,R7
;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL

        LDMIA   R5, {R2-R4,R8-R11,R14}  ; 8 words needed, gives 7 after shift
        ADD     R5, R5, #7*4            ; advance source ptr 7 words

        ShiftR  R2,R3, R6,R7            ; shift right R6 bits
        ShiftR  R3,R4, R6,R7            ; we only want result words
        ShiftR  R4,R8, R6,R7            ; R2-R4, R8-R11
        ShiftR  R8,R9, R6,R7
        ShiftR  R9,R10, R6,R7
        ShiftR  R10,R11, R6,R7
        ShiftR  R11,R14, R6,R7

        STMIA   R0!, {R2-R4,R8-R11}     ; write 7 words back to screen
        SUBS    R1, R1, #7
        BGE     SolPlFast20

SolPlFast30                             ; try 1 word at a time
        ADDS    R1, R1, #7

;       R0    ,R1    ,     R5    ,R6   ,R7
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL

; If EQ this is rightmost word

SolPlFast40
        LDMIA   R5, {R2,R3}
        ADD     R5, R5, #4

        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
                                        ; we only need result word R2
        BEQ     SolPlFast45             ; if rightmost word, jump out.
        STR     R2, [R0], #4

        SUBS    R1, R1, #1
        B       SolPlFast40

SolPlFast45
        LDR     R4, [WsPtr, #SPltRMask-SPltScrAdr]
        AND     R3, R2, R4              ; mask down to just the required pixels

 [ AvoidScreenReads
        CMP     R4, #&FFFFFFFF
        LDRNE   R2, [R0]                ; plot 1 word
        BICNE   R2, R2, R4              ; knock out mask bits
        ORRNE   R3, R2, R3              ; and or sprite bits
 |
        LDR     R2, [R0]                ; plot 1 word
        BIC     R2, R2, R4              ; knock out mask bits
        ORR     R3, R2, R3              ; and or sprite bits
 ]
        STR     R3, [R0], #4

SolPlFast50                             ; now try the next scanline
        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
                                        ; Width,Height,ScrOff,MemOff
        ADD     R0, R0, R3
        ADD     R5, R5, R4
        SUBS    R2, R2, #1
        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
        BGE     SolPlFast10             ; plot next scanline
ComplicatedExit
        SWI     XOS_RestoreCursors
SpriteOffScreen
        Pull    "WsPtr, R14"
        RETURNVC

PutSpriWideMaskErr
        ; Not supported!
        SWI     XOS_RestoreCursors
        Pull    "WsPtr, R14"
PutSpriWideMaskErr2
        ADRL    R0, SpriteErr_InvalidSpriteMode
        B       %FT90

PutSpriNotGraphics
        ADRL    R0, SpriteErr_NotGraphics
90
      [ International
        Push    "lr"
        BL      TranslateError
        Pull    "lr"
      ]
        STR     R0, [WsPtr, #RetnReg0]
        RETURNVS


; Complicated case

SolPl10 ROUT

;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee

; Plot the first (leftmost) word

        LDMIA   R5, {R2,R3}
        ADD     R5, R5, #4

        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
                                        ; we only need result word R2

        OrrEor  R3,R2, R10,R11          ; form EOR mask
        OrrEor  R2,R2, R8,R9            ; form OR mask

        LDR     R4, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
        AND     R2, R2, R4              ; mask down to just the required pixels
        AND     R3, R3, R4

        LDR     R4, [R0]                ; plot 1 word
        OrrEor  R4,R4, R2,R3
        STR     R4, [R0], #4

        SUBS    R1, R1, #1
        BLT     SolPl50                 ; if all plotted, try next scanline
                                        ; else try for blocks of 4
        SUBS    R1, R1, #4
        BLT     SolPl30

SolPl20
        STMIA   WsPtr, {R0,R1}          ; save ScrAdr,ColCnt

;       R0    ,R1         ,R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11
;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee

        LDMIA   R5, {R0-R4}             ; 5 words needed, gives 4 after shift
        ADD     R5, R5, #16             ; advance source ptr 4 words
        STR     R5, [WsPtr, #SPltMemAdr-SPltScrAdr]

        ShiftR  R0,R1, R6,R7            ; shift R4-R0 right R6 bits
        ShiftR  R1,R2, R6,R7            ; we only want result words R3-R0
        ShiftR  R2,R3, R6,R7
        ShiftR  R3,R4, R6,R7

        LDR     R4, [WsPtr]             ; get screen address
        LDMIA   R4, {R4-R7}             ; get 4 screen words

        ORoreorEORoreor R4,R0, R8,R9,R10,R11, R14
        ORoreorEORoreor R5,R1, R8,R9,R10,R11, R14
        ORoreorEORoreor R6,R2, R8,R9,R10,R11, R14
        ORoreorEORoreor R7,R3, R8,R9,R10,R11, R14

        LDR     R0, [WsPtr]             ; screen address
        STMIA   R0!, {R4-R7}            ; write 4 words back to screen
        LDMIB   WsPtr, {R1,R5-R7}       ; reload anything we shat on

        SUBS    R1, R1, #4
        BGE     SolPl20

SolPl30                                  ; try 1 word at a time
        ADDS    R1, R1, #4

;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee

; If EQ this is rightmost word

SolPl40
        LDMIA   R5, {R2,R3}
        ADD     R5, R5, #4

        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
                                        ; we only need result word R2
        OrrEor  R3,R2, R10,R11          ; form EOR mask
        OrrEor  R2,R2, R8,R9            ; form OR

        LDREQ   R4, [WsPtr, #SPltRMask-SPltScrAdr] ; if rightmost word,
        ANDEQ   R2, R2, R4              ; mask down to just the
        ANDEQ   R3, R3, R4              ; required pixels

        LDR     R4, [R0]
        OrrEor  R4,R4, R2,R3
        STR     R4, [R0], #4

        SUBS    R1, R1, #1
        BGE     SolPl40

SolPl50                                 ; now try the next scanline
        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
                                        ; Width,Height,ScrOff,MemOff
        ADD     R0, R0, R3
        ADD     R5, R5, R4
        SUBS    R2, R2, #1
        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
        BGE     SolPl10                 ; plot next scanline
        B       ComplicatedExit

; *****************************************************************************
;
;       NewTransPlot - Plot sprite using 1 bpp transparency mask
;       Called with R11 -> sprite, R8 = sprite type
;       TransPlot is used for 1bpp new sprites since it will be faster

NewTransPlot ROUT

        ; need to derive: bpp, and 1<<(5-log2bpp)

        ADRL    R0, NSM_bpptable-4
        LDR     R10, [R0, R8, LSL #2]   ; get the log2bpp to R10

        ;R10=log2bpp, R11=>sprite

        LDR     R8, [R11, #spWidth]      ; words-1

        ADD     R9, R8, #1
        MOV     R9, R9, LSL #2           ; number of bytes per row
        STR     R9, [WsPtr, #SPltMaskRowPtr]

        LDR     R9, [R11, #spRBit]       ; last bit used
        ADD     R9, R9, #1               ; change to number of bits
        MOV     R9, R9, LSR R10          ; number of pixels

        RSB     R0, R10, #5
        MOV     R8, R8, LSL R0           ; number of pixels for full words

        ADD     R8, R8, R9               ; total number of pixels

        ANDS    R9, R8, #&1F
        MOVNE   R9, #4
        BIC     R8, R8, #&1F
        ADD     R9, R9, R8, LSR #3       ; number of bytes per mask row

        STR     R9, [WsPtr, #SPltMaskRowLen]

        LDR     R8, [R11, #spImage]
        LDR     R9, [R11, #spTrans]
        ADD     R8, R8, R11
        ADD     R9, R9, R11

        ;RSB     R0, R10, #5            <- DONE ABOVE

        MOV     R1, #1
        MOV     R2, R1, LSL R0          ; r2 holds number of pixels per word

        MOV     R3, R1, LSL R10         ; r3 holds bits per pixel

        ADD     WsPtr, WsPtr, #SPltScrAdr       ; repoint WsPtr, at SPltScrAdr

        LDMIA   WsPtr, {R0-R1,R5-R7,R14}

        STR     R2, [WsPtr, #SPltPixPerWord-SPltScrAdr]
        STR     R3, [WsPtr, #SPltBPP-SPltScrAdr]

        ; sort out where to begin in 1bpp mask (rewritten Aug '93)

        MOV     LR, R9                  ; LR = mask pointer
        MOV     R3, #0                  ; R3 = mask bit
        CMP     R5, R8                  ; R5=>data to plot, R8=>start of sprite

        BEQ     %FT11                   ; nothing to do, go store R3 & LR
        BCC     %FT13                   ; start is before sprite data

        ;R3, R9, r10, R11 free
        ;R2 comes in holding pix per word

        ;if R6 is non zero the image will be a word early.
        MOV     R9, R5                  ; working copy of pointer within sprite
        CMP     R6, #0
        ADDNE   R9, R9, #4              ; take out the extra word for now

        LDR     R11,[WsPtr, #SPltMaskRowLen-SPltScrAdr] ; bytes per mask row

        SUBS    R10, R9, R8             ; difference in bytes

        LDR     R8,[WsPtr, #SPltMaskRowPtr-SPltScrAdr] ; loaded with bytes per image row
        BEQ     %FT13
14
        ;is it less than a row of data
        CMP     R10, R8
        BCC     %FT12                   ; yes it is

        ;deal with a row (or more of data)
        SUB     R10, R10, R8
        ADD     LR, LR, R11
        B       %BT14

12      ;start point is on this row. subtract 4 from difference and add
        ;pix per word to mask bit/ptr until diff=0
        CMP     R10, #0
        BEQ     %FT13

        SUB     R10, R10, #4

        ADD     R3, R3, R2
        CMP     R3, #32
        SUBCS   R3, R3, #32
        ADDCS   LR, LR, #4
        CMP     R10, #0
        BNE     %BT12

13
        ;deal with R6
        CMP     R6,#0
        BEQ     %FT11

        SUBS    R3, R3, R2                ;subtract pix per word
        ADDMI   R3, R3, #32
        SUBMI   LR, LR, #4                ;deal with going into previous word

11
        STR     R3, [WsPtr, #SPltMaskBit-SPltScrAdr]
        STR     LR, [WsPtr, #SPltMaskPtr-SPltScrAdr]

        ; and save it for the end of row increments
        STR     R3, [WsPtr, #SPltMaskRowBit-SPltScrAdr]
        STR     LR, [WsPtr, #SPltMaskRowPtr-SPltScrAdr]

        ADD     R11, WsPtr, #SPltzgooMasks-SPltScrAdr
        LDMIA   R11, {R8-R11}           ; masks for screen access (zgoo..zgee)

NTrnPl10

;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11,    R14
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee,
;
; As far as possible this is based on the original transplot.
;

; Plot the first (leftmost) word

        BL      getmaskword
        MOV     R2, R3

        BL      getmaskword_noinc
        ShiftR  R2,R3, R6,R7            ; we only need result word R2

        LDMIA   R5, {R3,R4}             ; fetch and shift image
        ADD     R5, R5, #4
        ShiftR  R3,R4, R6,R7            ; shift R4,R3 right R6 places

        OrrEor  R4,R3, R10,R11          ; form EOR mask
        OrrEor  R3,R3, R8,R9            ; form OR mask

        AND     R3, R3, R2              ; clear out any transparent pixels
        AND     R4, R4, R2

        LDR     R2, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
        AND     R3, R3, R2              ; mask down to just the required pixels
        AND     R4, R4, R2

        LDR     R2, [R0]                ; plot 1 word
        OrrEor  R2,R2, R3,R4
        STR     R2, [R0], #4

        SUBS    R1, R1, #1
        BLT     NTrnPl50                 ; if all plotted, try next scanline
                                        ; else try for blocks of 2
        SUBS    R1, R1, #2
        BLT     NTrnPl30

NTrnPl20
        STMIA   WsPtr, {R0,R1}          ; ScrAdr,ColCnt

;       R0    ,R1         ,R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11 ,   R14
;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee,   MskAdr


        BL     getmaskword
        MOV    R0, R3
        BL     getmaskword
        MOV    R1, R3

        BL     getmaskword_noinc
        MOV    R2, R3
        ShiftR R0,R1, R6,R7
        ShiftR R1,R2, R6,R7             ; aligned mask in R0,R1

        LDMIA  R5, {R2-R4}              ; 3 image words needed, gives 2 after
        ADD    R5, R5, #8               ; shifting
        STR    R5, [WsPtr, #SPltMemAdr-SPltScrAdr]
        ShiftR R2,R3, R6,R7
        ShiftR R3,R4, R6,R7             ; aligned image in R2,R3

        LDR    R4, [WsPtr]              ; screen address
        LDMIA  R4, {R4,R5}              ; 2 screen words

        ORoreorEORoreorMASK R4,R2,R0, R8,R9,R10,R11, R14
        ORoreorEORoreorMASK R5,R3,R1, R8,R9,R10,R11, R14

        LDR    R0, [WsPtr]              ; screen address
        STMIA  R0!, {R4,R5}             ; write 2 words back to screen
        LDMIB  WsPtr, {R1,R5,R6}        ; reload anything we shat on

        SUBS   R1, R1, #2
        BGE    NTrnPl20

NTrnPl30                                 ; try 1 word at a time
        ADDS   R1, R1, #2

;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee

; If EQ this is rightmost word

NTrnPl40

        BL      getmaskword
        MOV     R2, R3

        BL      getmaskword_noinc
        ShiftR  R2,R3, R6,R7    ; shift R3,R2 right R6 places, result in R2

        LDMIA   R5, {R3,R4}     ; fetch and align image
        ADD     R5, R5, #4
        ShiftR  R3,R4, R6,R7    ; shift R4,R3 right R6 places, result in R3

        OrrEor  R4,R3, R10,R11  ; form EOR mask
        OrrEor  R3,R3, R8,R9    ; form OR

        AND     R3, R3, R2      ; clear out transparant pixels
        AND     R4, R4, R2

        LDREQ   R2, [WsPtr, #SPltRMask-SPltScrAdr] ; if rightmost word,
        ANDEQ   R3, R3, R2              ; mask down to just the
        ANDEQ   R4, R4, R2              ; required pixels

        LDR     R2, [R0]
        OrrEor  R2,R2, R3,R4
        STR     R2, [R0], #4

        SUBS    R1, R1, #1
        BGE     NTrnPl40

NTrnPl50
        ; now try the next scanline

        ; BEWARE ... genspriteplotparmblock returns values for memoff and
        ; scroff which take account of alignment of screen and sprite data
        ;
        ; Do not alter the logic below unless you really know what
        ; to expect from this routine!!

        LDR     LR, [WsPtr, #SPltMaskRowPtr-SPltScrAdr]
        LDR     R3, [WsPtr, #SPltMaskRowBit-SPltScrAdr]

        LDR     R2, [WsPtr, #SPltMaskRowLen-SPltScrAdr]

        ADD     LR, LR, R2

        STR     LR, [WsPtr, #SPltMaskPtr-SPltScrAdr]
        STR     R3, [WsPtr, #SPltMaskBit-SPltScrAdr]
        STR     LR, [WsPtr, #SPltMaskRowPtr-SPltScrAdr]
        STR     R3, [WsPtr, #SPltMaskRowBit-SPltScrAdr]

        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
                                        ; Width,Height,ScrOff,MemOff

        ADD     R0, R0, R3
        ADD     R5, R5, R4

        SUBS    R2, R2, #1
        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
        BGE     NTrnPl10                 ; plot next scanline

        SWI     XOS_RestoreCursors

        Pull    "WsPtr, R14"
        RETURNVC

; get a mask word without incrementing the pointers

getmaskword_noinc ROUT
        Push    "R4, R5, LR"

        LDR     R4, [WsPtr, #SPltMaskBit-SPltScrAdr]
        LDR     R5, [WsPtr, #SPltMaskPtr-SPltScrAdr]
        BL      getmaskword                             ; preserves flags
        STR     R4, [WsPtr, #SPltMaskBit-SPltScrAdr]
        STR     R5, [WsPtr, #SPltMaskPtr-SPltScrAdr]

        LDMFD   R13!,{R4,R5,R15}                        ; must preserve flags

; get a mask word, and increment pointers

getmaskword ROUT
        Push    "R2,R4,R5,R6,R7,R8,R14"
        MRS     R8, CPSR

;R6 -> mask
;R7 = bit offset

;return in R3, update R6, R7, restore other registers

        MOV     R3, #0                   ; initial result
        LDR     LR, [WsPtr, #SPltPixPerWord-SPltScrAdr]  ; pixels per word
        LDR     R2, [WsPtr, #SPltBPP-SPltScrAdr]    ; bpp

        LDR     R7, [WsPtr, #SPltMaskBit-SPltScrAdr]
        LDR     R6, [WsPtr, #SPltMaskPtr-SPltScrAdr]

        LDR     R4, [R6]                 ; get the mask word
10
        CMP     R7, #0
        LDREQ   R4, [R6]                 ; fetch a new word if required
        MOV     R5, R4, LSR R7           ; shift desired bit down to bit 0

        MOV     R3, R3, LSR #1           ; shift down the result by one bit
        ORR     R3, R3, R5, LSL #31      ; and put bit 0 in at bit 31

        ; now use an ASR of bpp-1 to finish off
        SUB     R5, R2, #1
        MOV     R3, R3, ASR R5

        ADD     R7, R7, #1               ; next mask bit

        CMP     R7, #32                  ; on to next word ?
        ADDEQ   R6, R6, #4               ; increment mask word pointer
        MOVEQ   R7, #0                   ; bit pointer back to 0
                                         ; but don't fetch new word until it is needed!

        SUBS    LR, LR, #1               ; one pixel done
        BNE     %BT10

        STR     R7, [WsPtr, #SPltMaskBit-SPltScrAdr]
        STR     R6, [WsPtr, #SPltMaskPtr-SPltScrAdr]

        ;result in R3, MaskBit/MaskPtr adjusted

        MSR     CPSR_f, R8
        LDMFD   R13!,{R2,R4,R5,R6,R7,R8,R15} ; must save flags

        LTORG

; *****************************************************************************
;
;       TransPlot - Plot sprite using transparency mask
;

TransPlot
        ADD     R11, WsPtr, #SPltzgooMasks
        LDMIA   R11, {R8-R11}           ; masks for screen access (zgoo..zgee)

        ADD     WsPtr, WsPtr, #SPltScrAdr       ; repoint WsPtr, at SPltScrAdr

        LDMIA   WsPtr, {R0-R1,R5-R7,R14}

TrnPl10

;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11,    R14
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee,   MskAdr

; Plot the first (leftmost) word

        LDMIA   R14, {R2,R3}            ; fetch and shift mask right R6 places
        ADD     R14, R14, #4
        ShiftR  R2,R3, R6,R7            ; we only need result word R2

        LDMIA   R5, {R3,R4}             ; fetch and shift image
        ADD     R5, R5, #4
        ShiftR  R3,R4, R6,R7            ; shift R4,R3 right R6 places
                                        ; we only need result word R3
        OrrEor  R4,R3, R10,R11          ; form EOR mask
        OrrEor  R3,R3, R8,R9            ; form OR mask

        AND     R3, R3, R2              ; clear out any transparent pixels
        AND     R4, R4, R2

        LDR     R2, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
        AND     R3, R3, R2              ; mask down to just the required pixels
        AND     R4, R4, R2

        LDR     R2, [R0]                ; plot 1 word
        OrrEor  R2,R2, R3,R4
        STR     R2, [R0], #4

        SUBS    R1, R1, #1
        BLT     TrnPl50                 ; if all plotted, try next scanline
                                        ; else try for blocks of 2
        SUBS    R1, R1, #2
        BLT     TrnPl30

TrnPl20
        STMIA   WsPtr, {R0,R1}          ; ScrAdr,ColCnt

;       R0    ,R1         ,R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11 ,   R14
;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee,   MskAdr

        LDMIA  R14, {R0-R2}             ; 3 mask words, gives 2 after shifting
        ADD    R14, R14, #8             ; advance mask ptr 2 words
        ShiftR R0,R1, R6,R7
        ShiftR R1,R2, R6,R7             ; aligned mask in R0,R1

        LDMIA  R5, {R2-R4}              ; 3 image words needed, gives 2 after
        ADD    R5, R5, #8               ; shifting
        STR    R5, [WsPtr, #SPltMemAdr-SPltScrAdr]
        ShiftR R2,R3, R6,R7
        ShiftR R3,R4, R6,R7             ; aligned image in R2,R3

        LDR    R4, [WsPtr]              ; screen address
        LDMIA  R4, {R4,R5}              ; 2 screen words

        ORoreorEORoreorMASK R4,R2,R0, R8,R9,R10,R11, R6
        ORoreorEORoreorMASK R5,R3,R1, R8,R9,R10,R11, R6

        LDR    R0, [WsPtr]              ; screen address
        STMIA  R0!, {R4,R5}             ; write 2 words back to screen
        LDMIB  WsPtr, {R1,R5-R6}        ; reload anything we shat on

        SUBS   R1, R1, #2
        BGE    TrnPl20

TrnPl30                                 ; try 1 word at a time
        ADDS   R1, R1, #2

;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8  ,R9  ,R10 ,R11
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,zgoo,zgeo,zgoe,zgee

; If EQ this is rightmost word

TrnPl40
        LDMIA   R14, {R2,R3}    ; fetch and align trans mask
        ADD     R14, R14, #4
        ShiftR  R2,R3, R6,R7    ; shift R3,R2 right R6 places, result in R2

        LDMIA   R5, {R3,R4}     ; fetch and align image
        ADD     R5, R5, #4
        ShiftR  R3,R4, R6,R7    ; shift R4,R3 right R6 places, result in R3

        OrrEor  R4,R3, R10,R11  ; form EOR mask
        OrrEor  R3,R3, R8,R9    ; form OR

        AND     R3, R3, R2      ; clear out transparant pixels
        AND     R4, R4, R2

        LDREQ   R2, [WsPtr, #SPltRMask-SPltScrAdr] ; if rightmost word,
        ANDEQ   R3, R3, R2              ; mask down to just the
        ANDEQ   R4, R4, R2              ; required pixels

        LDR     R2, [R0]
        OrrEor  R2,R2, R3,R4
        STR     R2, [R0], #4

        SUBS    R1, R1, #1
        BGE     TrnPl40

TrnPl50                                 ; now try the next scanline
        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
                                        ; Width,Height,ScrOff,MemOff
        ADD     R0, R0, R3
        ADD     R5, R5, R4
        ADD     R14, R14, R4
        SUBS    R2, R2, #1
        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
        BGE     TrnPl10                 ; plot next scanline

        SWI     XOS_RestoreCursors

        Pull    "WsPtr, R14"
        RETURNVC

        LTORG

; *****************************************************************************
;
;       PlotMaskUserCoords - Draw a rectangle through a sprite mask
;                            using given external coordinates
;
;       External routine
;
; in:   R1 -> sprite area (not used)
;       R2 -> sprite
;       R3 = X coordinate to plot at
;       R4 = Y coordinate to plot at
;

PlotMaskUserCoords ROUT
        Push    R14
        BL      InternaliseCoords
        Pull    R14
        B       PlotMa20

; *****************************************************************************
;
; PlotNewMask
;
; Version of PlotMask for new 1bpp format sprites
;
; R0 = sprite mode word
; R2 -> sprite
; R10 = bits 27-31 of mode word

PlotNewMask ROUT

        TEQ     R10, #SpriteType_RISCOS5
      [ NoARMT2
        ANDEQ   R10, R0, #127<<20
        MOVEQ   R10, R10, LSR #20
      |
        UBFXEQ  R10, R0, #20, #7
      ]
        ADRL    R0, NSM_bpptable-4
        LDR     R10, [R0, R10, LSL #2]          ;get the log2bpp

        ;derive mask row length
        ;r10=log2bpp, r2=>sprite

        LDR     R8, [R2, #spWidth]       ; words-1

        ADD     R9, R8, #1               ; total words for a line of image
        MOV     R9, R9, LSL #2           ; total bytes for a line of image
        STR     R9, [WsPtr, #SPltMaskRowPtr]
                                         ; save it for determining mask start position

        LDR     R9, [R2, #spRBit]        ; last bit used
        ADD     R9, R9, #1               ; change to number of bits
        MOV     R9, R9, LSR R10          ; number of pixels

        RSB     R0, R10, #5
        MOV     R8, R8, LSL R0           ; number of pixels for full words

        ADD     R8, R8, R9               ; total number of pixels

        ANDS    R9, R8, #&1F
        MOVNE   R9, #4
        BIC     R8, R8, #&1F
        ADD     R9, R9, R8, LSR #3       ; number of bytes per mask row

        STR     R9, [WsPtr, #SPltMaskRowLen]

        MOV     R1, #1
        MOV     R0, R1, LSL R0                  ;number of pixels

        MOV     R6, R1, LSL R10                 ;bits per pixel

        STR     R0, [WsPtr, #SPltPixPerWord]
        STR     R6, [WsPtr, #SPltBPP]

        LDR     R0, [R2, #spHeight]
        ADD     R0, R0, R4
        LDR     R10, [WsPtr, #GWTRow]
        Least   R0, R0, R10             ; top scanline within window

        LDR     R10, [WsPtr, #YWindLimit]
        SUB     R0, R10, R0             ; flip Y
        AND     R0, R0, #7
        STR     R0, [WsPtr, #SPltEcfIndx] ; index into Ecf

        ADD     R0, WsPtr, R0, LSL #3
        ADD     R0, R0, #BgEcfOraEor
        STR     R0, [WsPtr, #SPltEcfPtr] ; ptr to ECF for highest row plotted

        ;STR     R2, [WsPtr, #SPltMaskRowPtr] ;temp, to save it

        LDR     R8, [R2, #spImage]
        LDR     R9, [R2, #spTrans]
        ADD     R8, R8, R2
        ADD     R9, R9, R2

        Push    "WsPtr, R14"            ; push both so R12,R14 are free for use
        Push    "R2"

        BL      GenSpritePlotParmBlk

        Pull    "R2"

        BVS     SpriteOffScreen
        SWI     XOS_RemoveCursors       ; assume no error can occur!

        ;LDR     R2, [WsPtr, #SPltMaskRowPtr]  ;recover pointer to sprite

        LDR     R8, [R2, #spImage]            ;offset to image
        LDR     R9, [R2, #spTrans]            ;offset to mask
        ADD     R8, R8, R2                    ;change to address of image
        ADD     R9, R9, R2                    ;change to address of mask

        LDR     R5, [WsPtr, #SPltMemAdr]      ;start memory address to plot
        LDR     LR, [WsPtr, #SPltMskAdr]      ;start mask address to plot
        TEQ     LR, #0                        ;off screen ?
        BEQ     SpriteOffScreen               ;so plot nothing

        ;amg 19/1/94 rip out the original algorithm and replace it with the correct one
        ;used with newtransplot which correctly handles genspriteplotparmblk's oddities

        ;now deal with sprites where we aren't starting
        ;at the beginning....
        LDR     R2, [WsPtr, #SPltPixPerWord]  ;pick up pixels per word

        MOV     LR, R9                        ;save mask address
        MOV     R3, #0                        ;set mask pixel counter to 0
        CMP     R5, R8                        ;memory address to plot from = image address ?
        BEQ     %FT11                         ;yes - no fudging needed
        BCC     %FT13

        MOV     R9, R5                        ;working copy of plot start within sprite
        CMP     R6, #0
        ADDNE   R9, R9, #4                    ;take out the extra word for now

        LDR     R11, [WsPtr, #SPltMaskRowLen] ;bytes per mask row
        SUBS    R10, R9, R8                   ;difference between plot start & sprite start
        LDR     R8, [WsPtr, #SPltMaskRowPtr]  ;bytes per image row
        BEQ     %FT13                         ;no difference

14
        ;is it less than a row of data different ?
        CMP     R10, R8
        BCC     %FT12                         ;yes, it is

        ;deal with whole rows
        SUB     R10, R10, R8                  ;decrease difference by size of image row
        ADD     LR, LR, R11                   ;increase mask pointer by size of mask row
        B       %BT14                         ;and loop until less than a row to do

12      ;start pointer is on this row. reduce the difference and increase the mask start
        ;point until they match

        CMP     R10, #0                       ;check for nothing to do
        BEQ     %FT13

        SUB     R10, R10, #4                  ;reduce image by a word

        ADD     R3, R3, R2                    ;increase mask start pixel by the number of
                                              ;pixels in that word

        CMP     R3, #32
        SUBCS   R3, R3, #32
        ADDCS   LR, LR, #4                    ;get the mask bit back into a word's worth

        CMP     R10, #0                       ;extra test down here to avoid taking two
        BNE     %BT12                         ;branches

13      ;remember R6 ? uncompensate now
        CMP     R6, #0
        BEQ     %FT11

        SUBS    R3, R3, R2                    ;go back by the number of pixels in a word
        ADDMI   R3, R3, #32
        SUBMI   LR, LR, #4                    ;deal with going back into previous mask word

11
        STR     R3, [WsPtr, #SPltMaskBit]     ;starting mask bit to plot
        STR     LR, [WsPtr, #SPltMaskPtr]     ;starting mask word to plot

        ; and save it for the end of row increments
        STR     R3, [WsPtr, #SPltMaskRowBit]
        STR     LR, [WsPtr, #SPltMaskRowPtr]

        STR     LR, [WsPtr, #SPltMskAdr]
        STR     LR, [WsPtr, #SPltMemAdr]

        ADD     WsPtr, WsPtr, #SPltScrAdr ; repoint WsPtr at SPltScrAdr

        LDMIA   WsPtr, {R0-R1,R5-R7}

        LDR     R5, [WsPtr, #SPltMaskPtr-SPltScrAdr]
        LDR     R8, [WsPtr, #SPltEcfPtr-SPltScrAdr]
10
        LDMIA   R8, {R8,R9}             ; ora,eor for this row

;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8 ,R9
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,ora,eor

;       Plot the first (Leftmost) word

        BL      getmaskword
        MOV     R2, R3

        BL      getmaskword_noinc

        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
                                        ; we only need result word R2
        LDR     R10, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
        AND     R2, R2, R10             ; mask down to just the required pixels

        LDR     R4, [R0]                ; plot 1 word
        OrrEorMASK R4,R2, R8,R9, R14
        STR     R4, [R0], #4

        SUBS    R1, R1, #1
        BLT     %FT50                   ; if all plotted, try next scanline
                                        ; else try for blocks of 4
        SUBS    R1, R1, #4
        BLT     %FT30

20
        STMIA   WsPtr, {R0,R1}          ; save ScrAdr,ColCnt

;       R0    ,R1         ,R5    ,R6   ,R7
;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL


        BL     getmaskword
        MOV    R0, R3
        BL     getmaskword
        MOV    R1, R3
        BL     getmaskword
        MOV    R2, R3
        BL     getmaskword
        MOV    R8, R3
        BL     getmaskword_noinc
        MOV    R4,R3
        MOV    R3,R8

        ShiftR  R0,R1, R6,R7            ; shift R4-R0 right R6 bits
        ShiftR  R1,R2, R6,R7            ; we only want result words R3-R0
        ShiftR  R2,R3, R6,R7
        ShiftR  R3,R4, R6,R7

        LDR     R4, [WsPtr]             ; get screen address
        LDMIA   R4, {R8-R11}            ; get 4 screen words

        LDR     R6, [WsPtr, #SPltEcfPtr-SPltScrAdr]
        LDMIA   R6, {R6,R7}             ; ora,eor for this row

        OrrEorMASK R8,R0,  R6,R7, R14   ; Scr:=ScrOR(oraANDmsk)EOR(eorANDmsk)
        OrrEorMASK R9,R1,  R6,R7, R14
        OrrEorMASK R10,R2, R6,R7, R14
        OrrEorMASK R11,R3, R6,R7, R14

        LDR     R0, [WsPtr]             ; screen address
        STMIA   R0!, {R8-R11}           ; write 4 words back to screen
        LDMIB   WsPtr, {R1,R5-R7}       ; reload anything we shat on

        SUBS    R1, R1, #4
        BGE     %BT20

30                                      ; try 1 word at a time
        ADDS    R1, R1, #4

;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8 ,R9
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,ora,eor

; If EQ this is rightmost word

        LDR     R8, [WsPtr, #SPltEcfPtr-SPltScrAdr]
        LDMIA   R8, {R8,R9}             ; ora,eor for this row
        LDR     R10, [WsPtr, #SPltRMask-SPltScrAdr]
40

        BL      getmaskword
        MOV     R2, R3

        BL      getmaskword_noinc
        ShiftR  R2,R3, R6,R7    ; shift R3,R2 right R6 places, result in R2
                                ; we only need result word R2

        ANDEQ   R2, R2, R10     ; if rightmost word, mask down to just the
                                ; required pixels
        LDR     R4, [R0]
        OrrEorMASK R4,R2, R8,R9, R14
        STR     R4, [R0], #4

        SUBS    R1, R1, #1
        BGE     %BT40

50                              ; now try the next scanline
        ; BEWARE ... genspriteplotparmblock returns values for memoff and
        ; scroff which take account of alignment of screen and sprite data
        ;
        ; Do not alter the logic below unless you really know what
        ; to expect from this routine!!

        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
                                        ; Width,Height,ScrOff,MemOff


        LDR     LR, [WsPtr, #SPltMaskRowPtr-SPltScrAdr]
        LDR     R3, [WsPtr, #SPltMaskRowBit-SPltScrAdr]

        LDR     R2, [WsPtr, #SPltMaskRowLen-SPltScrAdr]

        ADD     LR, LR, R2

        STR     LR, [WsPtr, #SPltMaskPtr-SPltScrAdr]
        STR     R3, [WsPtr, #SPltMaskBit-SPltScrAdr]
        STR     LR, [WsPtr, #SPltMaskRowPtr-SPltScrAdr]
        STR     R3, [WsPtr, #SPltMaskRowBit-SPltScrAdr]

        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
                                        ; Width,Height,ScrOff,MemOff
        ADD     R0, R0, R3

        ADD     R10, WsPtr, #(SPltEcfPtr-SPltScrAdr)

        LDMIA   R10, {R8,R9}    ; EcfPtr,EcfIndx
        ADD     R8, R8, #8      ; step both to next row in Ecf
        ADD     R9, R9, #1
        CMP     R9, #8
        MOVGE   R9, #0          ; it's a wrap!
        SUBGE   R8, R8, #(8*8)
        STMIA   R10, {R8,R9}

        SUBS    R2, R2, #1
        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
        BGE     %BT10                   ; plot next scanline

        SWI XOS_RestoreCursors

        Pull    "WsPtr, R14"
        RETURNVC

; *****************************************************************************
;
;       PlotMask - Draw a rectangle through a sprite mask
;
;       External routine + PlotMa20 called by PlotMaskUserCoords
;        (also external)
;
; in:   R1 -> sprite area (not used)
;       R2 -> sprite
;

PlotMask ROUT
        ASSERT  NewPtY = NewPtX +4
        ADD     R3, WsPtr, #NewPtX
        LDMIA   R3, {R3, R4}            ; plot sprite at NewPt(X,Y)
PlotMa20
        GraphicsMode R0
        BNE     PutSpriNotGraphics      ; quit with error if not graphics mode

        LDR     R5, [WsPtr, #GPLBMD]    ; background GCOL action
        ORR     R5, R5, #8              ; force 'use mask'
        LDR     R10, [R2, #spImage]
        LDR     R11, [R2, #spTrans]
        TEQ     R10, R11                ; spImage=spTrans if no mask
        BEQ     %FT90                   ; so plot a rectangle

        LDR     R0, [R2, #spMode]
        MOVS    R10, R0, ASR #27
        BMI     PutSpriWideMaskErr2
        BNE     PlotNewMask

        LDR     R0, [R2, #spHeight]
        ADD     R0, R0, R4
        LDR     R10, [WsPtr, #GWTRow]
        Least   R0, R0, R10             ; top scanline within window

        LDR     R10, [WsPtr, #YWindLimit]
        SUB     R0, R10, R0             ; flip Y
        AND     R0, R0, #7
        STR     R0, [WsPtr, #SPltEcfIndx] ; index into Ecf

        ADD     R0, WsPtr, R0, LSL #3
        ADD     R0, R0, #BgEcfOraEor
        STR     R0, [WsPtr, #SPltEcfPtr] ; ptr to ECF for highest row plotted

        Push    "WsPtr, R14"            ; push both so R12,R14 are free for use
        BL      GenSpritePlotParmBlk
        BVS     SpriteOffScreen
        SWI     XOS_RemoveCursors       ; assume no error can occur!

        LDR     R5, [WsPtr, #SPltMemAdr]
        LDR     R6, [WsPtr, #SPltMskAdr]
        TEQ     R6, #0
        BEQ     SpriteOffScreen

        ADD     R6, R6, R5
        STR     R6, [WsPtr, #SPltMskAdr]
        STR     R6, [WsPtr, #SPltMemAdr]

        ADD     WsPtr, WsPtr, #SPltScrAdr ; repoint WsPtr at SPltScrAdr

        LDMIA   WsPtr, {R0-R1,R5-R7}
        LDR     R8, [WsPtr, #SPltEcfPtr-SPltScrAdr]
10
        LDMIA   R8, {R8,R9}             ; ora,eor for this row

;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8 ,R9
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,ora,eor

;       Plot the first (Leftmost) word

        LDMIA   R5, {R2,R3}
        ADD     R5, R5, #4

        ShiftR  R2,R3, R6,R7            ; shift R3,R2 right R6 places
                                        ; we only need result word R2
        LDR     R10, [WsPtr, #SPltLMask-SPltScrAdr] ; on leftmost word
        AND     R2, R2, R10             ; mask down to just the required pixels

        LDR     R4, [R0]                ; plot 1 word
        OrrEorMASK R4,R2, R8,R9, R14
        STR     R4, [R0], #4

        SUBS    R1, R1, #1
        BLT     %FT50                   ; if all plotted, try next scanline
                                        ; else try for blocks of 4
        SUBS    R1, R1, #4
        BLT     %FT30

20
        STMIA   WsPtr, {R0,R1}          ; save ScrAdr,ColCnt

;       R0    ,R1         ,R5    ,R6   ,R7
;       ScrAdr,ColCnt,    ,MemAdr,ShftR,ShftL

        LDMIA   R5, {R0-R4}             ; 5 words needed, gives 4 after shift
        ADD     R5, R5, #16             ; advance source ptr 4 words
        STR     R5, [WsPtr, #SPltMemAdr-SPltScrAdr]

        ShiftR  R0,R1, R6,R7            ; shift R4-R0 right R6 bits
        ShiftR  R1,R2, R6,R7            ; we only want result words R3-R0
        ShiftR  R2,R3, R6,R7
        ShiftR  R3,R4, R6,R7

        LDR     R4, [WsPtr]             ; get screen address
        LDMIA   R4, {R8-R11}            ; get 4 screen words

        LDR     R6, [WsPtr, #SPltEcfPtr-SPltScrAdr]
        LDMIA   R6, {R6,R7}             ; ora,eor for this row

        OrrEorMASK R8,R0,  R6,R7, R14   ; Scr:=ScrOR(oraANDmsk)EOR(eorANDmsk)
        OrrEorMASK R9,R1,  R6,R7, R14
        OrrEorMASK R10,R2, R6,R7, R14
        OrrEorMASK R11,R3, R6,R7, R14

        LDR     R0, [WsPtr]             ; screen address
        STMIA   R0!, {R8-R11}           ; write 4 words back to screen
        LDMIB   WsPtr, {R1,R5-R7}       ; reload anything we shat on

        SUBS    R1, R1, #4
        BGE     %BT20

30                                      ; try 1 word at a time
        ADDS    R1, R1, #4

;       R0    ,R1    ,     R5    ,R6   ,R7   ,R8 ,R9
;       ScrAdr,ColCnt,     MemAdr,ShftR,ShftL,ora,eor

; If EQ this is rightmost word

        LDR     R8, [WsPtr, #SPltEcfPtr-SPltScrAdr]
        LDMIA   R8, {R8,R9}             ; ora,eor for this row
        LDR     R10, [WsPtr, #SPltRMask-SPltScrAdr]
40
        LDMIA   R5, {R2,R3}
        ADD     R5, R5, #4

        ShiftR  R2,R3, R6,R7    ; shift R3,R2 right R6 places
                                ; we only need result word R2

        ANDEQ   R2, R2, R10     ; if rightmost word, mask down to just the
                                ; required pixels
        LDR     R4, [R0]
        OrrEorMASK R4,R2, R8,R9, R14
        STR     R4, [R0], #4

        SUBS    R1, R1, #1
        BGE     %BT40

50                              ; now try the next scanline

        LDMDB   WsPtr, {R1,R2,R3,R4}    ; R1   ,R2    ,R3    ,R4
                                        ; Width,Height,ScrOff,MemOff
        ADD     R0, R0, R3
        ADD     R5, R5, R4

        ADD     R10, WsPtr, #(SPltEcfPtr-SPltScrAdr)

        LDMIA   R10, {R8,R9}    ; EcfPtr,EcfIndx
        ADD     R8, R8, #8      ; step both to next row in Ecf
        ADD     R9, R9, #1
        CMP     R9, #8
        MOVGE   R9, #0          ; it's a wrap!
        SUBGE   R8, R8, #(8*8)
        STMIA   R10, {R8,R9}

        SUBS    R2, R2, #1
        STRGE   R2, [WsPtr, #SPltHeight-SPltScrAdr]
        BGE     %BT10                   ; plot next scanline

        SWI XOS_RestoreCursors

        Pull    "WsPtr, R14"
        RETURNVC

; Sprite has no mask or gcol says dont use it, so draw a rectangle

90
        Push    R14
        BL      ReadSpriteWidth         ; on exit R0=width in pixels
        ADD     R5, R3, R0
        MOV     R0, R3                  ; x0
        MOV     R1, R4                  ; y0

        LDR     R3, [R2, #spHeight]
        ADD     R3, R3, R4              ; y1
        SUB     R2, R5, #1              ; x1

        LDR     R4, [WsPtr, #CursorFlags]
        TST     R4, #ClipBoxEnableBit
        BLNE    ClipPlotMask

        ADD     R4, WsPtr, #BgEcfOraEor ; select Bg colour & action
        STR     R4, [WsPtr, #GColAdr]
        B       RectFillB

ClipPlotMask ROUT
        Push    "R0-R7,R10,R11, R14"
        ADD     R10, R13, #2*4          ; R10 -> last point (R2,R3)
        MOV     R11, #2                 ; merge two points
        BL      MergeR11PointsFromR10
        Pull    "R0-R7,R10,R11, PC"

; *****************************************************************************
;
;       GenSpritePlotParmBlk - Generate lots of useful variables to help us
;                              to plot sprite
;
;       Internal routine, called by PutSprite, PlotMask, ScreenLoad
;
; in:   R2 -> sprite
;       R3 = X coordinate to plot at
;       R4 = Y coordinate to plot at
;       R5 = GCOL action
;

GenSpritePlotParmBlk ROUT
        Push    R14
        AND     R5, R5, #&F             ; lose any ECF colour bits
        LDR     R6, [R2, #spImage]
        LDR     R7, [R2,#spTrans]
        SUB     R6, R7, R6              ; offset from Image to Trans mask
                                        ; =0 if no mask
        STR     R5, [WsPtr, #SPltAction] ; save action for simple case spotting
        CMP     R5, #8                  ; if GCOL action < 8
        MOVLT   R6, #0                  ; plot as solid anyway
        STR     R6, [WsPtr, #SPltMskAdr]

        AND     R5, R5, #7
        MOV     R5, R5, LSL #2          ; 4 bits for each
        LDR     R6, =TBscrmasks
        MOV     R6, R6, ROR R5          ; put correct bits in top 4 bits
        MOV     R7, R6, ASR #31         ; set R7 to 0 or -1 on bit 31
        MOV     R6, R6, LSL #1
        MOV     R8, R6, ASR #31         ; set R8 to 0 or -1 on bit 30
        MOV     R6, R6, LSL #1
        MOV     R9, R6, ASR #31         ; set R9 to 0 or -1 on bit 29
        MOV     R6, R6, LSL #1
        MOV     R10, R6, ASR #31        ; set R10 to 0 or -1 on bit 28

        ADD     R6, WsPtr, #SPltzgooMasks
        STMIA   R6, {R7-R10}            ; store zgoo, zgeo, zgoe, zgee

        MOV     R14, R2                 ; leave sprite CB ptr in R14
        MOV     R0, R3                  ; leave X coord in R0
        MOV     R1, R4                  ; leave Y coord in R1

        ADD     R11, R14, #spWidth
        LDMIA   R11, {R2,R3}            ; Width-1, Height-1

        ADD     R4, WsPtr, #GWLCol      ; R4  ,R5  ,R6  ,R7
        LDMIA   R4, {R4,R5,R6,R7}       ; LCol,BRow,RCol,TRow

        SUBS    R5, R5, R1
        MOVLT   R5, #0                  ; no. of rows below window

        ADD     R1, R1, R3              ; Coord of topLH of sprite

        SUBS    R7, R1, R7
        MOVLT   R7, #0                  ; no. of rows above window
        SUB     R1, R1, R7              ; clipped topLH coord

        ADD     R5, R5, R7              ; reduction factor for height

        ADD     R2, R2, #1

        MOV     R8, R14
        LDR     R9, [WsPtr, #CursorFlags]
        TST     R9, #ClipBoxEnableBit
        BLNE    ClipSpritePlot
        MOV     R14, R8

        MUL     R3, R7, R2              ; word offset into sprite image

        Push    "R3,R5"                 ; word offset and height reduction

        Push    R14
        BL      ScreenAddr
        Pull    R14
        MOV     R4, R2                  ; address of top left corner

        LDR     R5, [R14, #spImage]
        ADD     R5, R5, R14             ; address of sprite image

        Pull    R6
        ADD     R5, R5, R6, LSL #2

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

        BitLOffset R7,R0, R9,R10,R11    ; R7 := bit position to align to
        WordOffset R8,R0, R9,R10,R11    ; R8 := word offset on line

        LDR     R0, [WsPtr, #GWRCol]
        BitROffset R1,R0, R9,R10,R11
        WordOffset R2,R0, R9,R10,R11
        Push    "R1,R2"

        LDR     R0, [WsPtr, #GWLCol]
        BitLOffset R1,R0, R9,R10,R11
        WordOffset R2,R0, R9,R10,R11

        Push    "R1,R2,R8"

        ADD     R11, R14, #spWidth
        LDMIA   R11, {R0,R1,R6,R8}      ; Width-1, Height-1, LBit, RBit

        MOV     R3, #0                  ; offset to next row in sprite

        SUBS    R6, R6, R7              ; no. of bits to shift sprite
                                        ; before plotting
                                        ; use R7 as LBit
        SUBS    R8, R8, R6              ; calculate new RBit
        ADDLT   R8, R8, #32
        ADDLT   R3, R3, #4

        CMP     R8, #32
        SUBGE   R8, R8, #32
        SUBGE   R3, R3, #4

; R9  Offset on line to plot point, R7 LBit
; R11 Offset on line to GWLCol      R10 bit position

        Pull    "R10,R11"
        Pull    R9

        SUBS    R9, R11, R9
        SUBLT   R11, R11, R9

        ADDGT   R4, R4, R9, LSL #2
        ADDGT   R5, R5, R9, LSL #2
        ADDGT   R3, R3, R9, LSL #2

        CMPEQ   R10, R7
        MOVGT   R7, R10

; R10 Offset to GWRCol, R9 bit position
; R11 Offset to RHedge of sprite, R8 RBit

        ADD     R11, R11, R0
        SUB     R11, R11, R3, ASR #2

        Pull    "R9,R10"

        SUBS    R10, R11, R10
        ADDGT   R3, R3, R10, LSL #2

        CMPEQ   R8, R9
        MOVGT   R8, R9

        Pull    R10

        SUBS    R1, R1, R10             ; correct height
        BLT     %FT20

        SUBS    R0, R0, R3, ASR #2      ; corrected width
        BLT     %FT20

        LDR     R2,[WsPtr,#LineLength]
        SUB     R2, R2, #4
        SUB     R2, R2, R0, LSL #2      ; offset to next screen line

        MOV     R9, #&FFFFFFFE          ; RHand partial word mask
        MVN     R9, R9, LSL R8
        MOV     R8, #&FFFFFFFF
        MOV     R8, R8, LSL R7          ; LHand partial word mask

        ANDEQ   R8, R8, R9              ; if width=0, combine LH & RH masks
        MOVEQ   R9, R8

        CMP     R6, #0
        ADDLT   R6, R6, #32             ; correct if neg
        SUBLT   R5, R5, #4
        RSB     R7, R6, #32             ; its complement

        ADD     R11, WsPtr, #SPltWidth
        STMIA   R11, {R0,R1,R2,R3,R4}   ; SPltWidth..SPltScrAdr

        ADD     R11, WsPtr, #SPltColCnt
        STMIA   R11, {R0,R5,R6,R7}      ; SPltColCnt..SPltShftL

        ADD     R11, WsPtr, #SPltMskAdr
        STMIB   R11, {R8,R9}            ; SPltLMask,SPltRMask

        CLRV
        Pull    PC

20
        SETV
        Pull    PC

        LTORG

        END