; 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.
;
; > s.Wimp04

;;---------------------------------------------------------------------------
;; Wimp_PlotIcon
;; Entry:  R1 --> block containing icon definition (userblk=R1 here)
;;         [redrawhandle] --> current redraw window
;;         [clipx0,y0,x1,y1] = current graphics window
;;---------------------------------------------------------------------------

SWIWimp_PlotIcon
        MyEntry "PlotIcon"
;
      [ SwitchingToSprite
        LDR     R14, switchtospr_current        ; outputting to a sprite?
        TEQ     R14, #0
        BLNE    switchingtosprite_recache       ; check graphics parameters are up-to-date if so
      ]

        LDR     handle,redrawhandle

        CMP     handle,#nullptr                 ; Not in a redraw loop.
        MOVEQ   R14,#1                          ; Use wimp sprite area
        STREQ   R14,areaCBptr                   ; for sprite plotting
        BLEQ    defaultwindow                   ; Set default graphics window.
        SWI     XOS_WriteI+5                    ; VDU 5 mode.
        BVS     ExitWimp
        BEQ     %FT01
        BL      checkhandle_owner               ; if not in redraw, this fails
        BVS     ExitWimp
;
        BL      setwindowptrs
01
        LDR     R14,careticonaddr
        EOR     R14,R14,userblk                 ; caret can't be here really!
        STR     R14,hascaret
;
        LDR     R1,[userblk,#i_flags]           ; R1 = flag word

        TST     R1,#is_deleted
        BNE     ExitWimp
;
        ADD     R2,userblk,#i_data              ; R2 --> data
        BL      seticonptrs                     ; NB: mustn't cause a task swap
;
        LDMIA   userblk,{x0,y0,x1,y1}           ; x0,y0,x1,y1 = bounding box
        ADD     x0,x0,cx1
        ADD     y0,y0,cy1
        ADD     x1,x1,cx1
        ADD     y1,y1,cy1
;
        BL      drawcolouredicon                ; depends on flag settings
;
      [ outlinefont
        LDR     R14,systemfont
        TST     R14,#&80000000

        BEQ     ExitWimp

        LDR     userblk,[sp]

        LDR     R1,[userblk,#i_flags]           ; R1 = flag word
        TST     R1,#if_fancyfont

        MOVEQ   R14,#0                          ; FM may fail on fancy font icons
                                                ; but not on real ones, eg. missing font
                                                ; handle.
        BICNE   R14,R14,#&80000000

        STR     R14,systemfont

        BNE     ExitWimp                        ; it'll probably fail again

        B       %BT01
      |
        B       ExitWimp
      ]


;;---------------------------------------------------------------------------
;; drawusericons - draw all icons in a window
;; Entry:  handle --> window definition
;;         [clipx0,y0,x1,y1] = current graphics clip window
;;---------------------------------------------------------------------------

drawusericons
        Push    "LR"
;
        BL      setwindowptrs                   ; sets up [careticonaddr] etc.
;
        Push    "handle,userblk"
;
        ASSERT  (handle<>R11)

        [ outlinefont
        LDR     R11,iconbarhandle
        Abs     R11,R11
        TEQ     handle,R11
        LDREQB  R11,iconbar_needs_rs
        TEQEQ   R11,#1
        BLEQ    resizeiconbaricons
        ]

        LDR     R11,[handle,#w_nicons]
        LDR     R10,[handle,#w_icons]           ; R10 --> start of icon list
        ADD     R11,R10,R11,LSL #i_shift        ; R11 --> end of list
druilp
        CMP     R10,R11
        BCS     doneusericons
;
        LDR     R14,careticonaddr
        EOR     R14,R14,R10                     ; 0 ==> this icon has the caret
        STR     R14,hascaret
;
        LDR     R1,[R10,#i_flags]               ; R1 = flag word
        TST     R1,#is_deleted
        BNE     %FT01

        ADD     R2,R10,#i_data                  ; R2 --> data
        BL      seticonptrs                     ; NB: mustn't cause a task swap
;
        ADD     R14,R10,#i_bbx0                 ; x0,y0,x1,y1 = bounding box
        LDMIA   R14,{x0,y0,x1,y1}
        ADD     x0,x0,cx1
        ADD     y0,y0,cy1
        ADD     x1,x1,cx1
        ADD     y1,y1,cy1
;
        BL      drawcolouredicon                ; depends on flag settings

; check to see if font manager has gone ga ga

      [ outlinefont
        LDR     R14,systemfont
        TST     R14,#&80000000

        BEQ     %FT00
        LDR     R1,[R10,#i_flags]               ; R1 = flag word
        TST     R1,#if_fancyfont                ; it'll probably fail again.

        MOVEQ   R14,#0
        BICNE   R14,R14,#&80000000
        STR     R14,systemfont

        BEQ     druilp
      ]

00
        Pull    "handle,userblk,PC",VS          ; error!
01
        LDR     R14,iconhandle
        ADD     R14,R14,#1
        STR     R14,iconhandle
        ADD     R10,R10,#i_size
        B       druilp

doneusericons
        CLRV
        Pull    "handle,userblk,PC"

;
; Entry:  handle-->window definition
; Exit:   cx1,cy1 = window origin
;         [careticonaddr], [areaCBptr] set up
;

setwindowptrs
        Push    "LR"
;
        LDR     R14,caretdata+0
        CMP     R14,#nullptr
        Abs     R14,R14,NE
        LDRNE   R14,[R14,#w_icons]              ; R14 --> start of icon list   (NK 339)
        LDRNE   R0,caretdata+4
        ADDNE   R14,R14,R0,LSL #i_shift
        STR     R14,careticonaddr               ; R14 --> addr of caret icon
;
        ADD     R14,handle,#w_wax0
        LDMIA   R14,{R0,R1,cx0,cy0,cx1,cy1}
        SUB     cx1,R0,cx1                      ; cx1 <- x0-scx
        SUB     cy1,cy0,cy1                     ; cy1 <- y1-scy
;
        LDR     R14,[handle,#w_areaCBptr]
        STR     R14,areaCBptr                   ; for sprite plotting
      [ :LNOT: TrueIcon3
        LDRB    R14,[handle,#w_wbcol]
        STRB    R14,work_back_colour            ; 320nk, setup default background colour
      ]
        MOV     R14,#0
        STR     R14,iconhandle                  ; for iconbar plotting
;
        Pull    "PC"

  [ TrueIcon3
;
; Entry: handle -> window definition
; Exit:  true window colour words set up
;
setwindowcolours ROUT
        EntryS  "R3"
        MOV     R14, #nullptr           ; initialise all colours to -1
        STR     R14, truetitlefg        ; ie not yet specified
        STR     R14, truetitlebg
        STR     R14, trueworkfg
        STR     R14, trueworkbg
        STR     R14, truescoutcolour
        STR     R14, truescincolour
        STR     R14, truetitlebg2
;
        LDRB    R14, [handle, #w_flags2]
        TST     R14, #wf2_truecolour    ; this bit set is a prerequisite
        BEQ     %FT01
;
        LDR     R14, [handle, #w_flags]
        TST     R14, #wf_icon3
        BEQ     %FT02                   ; no title bar => definitely use w_title+4 as validation string
;
        LDR     R14, [handle, #w_titleflags]
        TST     R14, #if_indirected
        ANDNE   R14, R14, #if_text :OR: if_sprite
        TEQNE   R14, #if_sprite
        BEQ     %FT01                   ; w_title+4 is already put to another use in these cases
;
02      ; read validation string
        Push    "R4"
        LDR     R3, [handle, #w_title+4]
        ADRL    R4, truetitlefg
        BL      readtruecolours
        Pull    "R4"
;
01      ; each word now holds the specified true colour, or -1 if not specified
        ; look up unspecified colours - must assume wf_realcolours is unset
        BL      getpalpointer           ; R14 -> Wimp palette
;
        LDR     R3, truetitlefg
        CMP     R3, #-1
        BNE     %FT10
        LDRB    R3, [handle, #w_tfcol]
        TEQ     R3, #&FF                ; special flag value?
        MOVEQ   R3, #sc_black           ; default to Wimp black
        ANDNE   R3, R3, #&F
        LDR     R3, [R14, R3, LSL #2]   ; look up palette entry
        STR     R3, truetitlefg
10
        LDR     R3, truetitlebg
        CMP     R3, #-1
        LDREQB  R3, [handle, #w_tbcol]
        ANDEQ   R3, R3, #&F
        LDREQ   R3, [R14, R3, LSL #2]   ; look up palette entry
        STREQ   R3, truetitlebg
;
        LDR     R3, trueworkfg
        CMP     R3, #-1
        LDREQB  R3, [handle, #w_wfcol]
        ANDEQ   R3, R3, #&F
        LDREQ   R3, [R14, R3, LSL #2]   ; look up palette entry
        STREQ   R3, trueworkfg
;
        LDR     R3, trueworkbg
        CMP     R3, #-1
        BNE     %FT11
        LDRB    R3, [handle, #w_wbcol]
        TEQ     R3, #&FF                ; special flag value?
        MOVEQ   R3, #sc_white           ; default to Wimp white (yes, that's what the old Wimp did too...)
        ANDNE   R3, R3, #&F
        LDR     R3, [R14, R3, LSL #2]   ; look up palette entry
        STR     R3, trueworkbg
11
        LDR     R3, truescoutcolour
        CMP     R3, #-1
        LDREQB  R3, [handle, #w_scouter]
        ANDEQ   R3, R3, #&F
        LDREQ   R3, [R14, R3, LSL #2]   ; look up palette entry
        STREQ   R3, truescoutcolour
;
        LDR     R3, truescincolour
        CMP     R3, #-1
        LDREQB  R3, [handle, #w_scinner]
        ANDEQ   R3, R3, #&F
        LDREQ   R3, [R14, R3, LSL #2]   ; look up palette entry
        STREQ   R3, truescincolour
;
        LDR     R3, truetitlebg2
        CMP     R3, #-1
        LDREQB  R3, [handle, #w_tbcol2]
        ANDEQ   R3, R3, #&F
        LDREQ   R3, [R14, R3, LSL #2]   ; look up palette entry
        STREQ   R3, truetitlebg2
;
        EXITS
  ]

;
; Entry:  R1 = icon flags
;         R2 --> icon data
; Exit:   various ptrs and flags set up
;         task swap can occur without userblk being set up !!!
;
        ASSERT  border_normal =0

; do main processing of flags + icon information

seticonptrs TraceL font
int_seticonptrs ROUT

        Trace   fcol, "seticonptrs: icon flags ", X, R1

        Push    "R3,R10,LR"
;
        LDR     R14,areaCBptr
        STR     R14,thisCBptr
        STR     R2,spritename
;
        MOV     R14,#0
        STR     R14,validationstring
        STR     R14,border_type

        MOV     R14,#1
        STR     R14,lengthflags                 ; not null!
        MOV     R14,#-1
        STR     R14,linespacing
      [ TrueIcon1
        STR     R14,truefgcolour
        STR     R14,truebgcolour
      ]
      [ TrueIcon2
        STR     R14, truebgcolour2
        STR     R14, truewellcolour
        STR     R14, truefacecolour
        STR     R14, trueoppcolour
      ]
      [ TrueSelectionColours
	STR	r14, trueselfgcolour
	STR	r14, trueselbgcolour
      ]

        TST     R1,#if_fancyfont
        ;If system font, use colours in flags
        MOVEQ   R14,R1
        ;If scalable font, use white & black. This might be changed later, if
        ;       there's an F in the validation string.
        MOVNE   R14,#(sc_white:SHL:ib_bcol):OR:(sc_black:SHL:ib_fcol)
        ASSERT  ib_bcol > ib_fcol
        MOV     R3,R14,LSR #ib_bcol
        STRB    R3,fontbackground

        Trace   fcol, "seticonptrs: (1) fontbackground ", X, R3

        MOV     R3,R14,LSR #ib_fcol
        AND     R3,R3,#if_fcol:SHR:ib_fcol
        STRB    R3,fontforeground

        Trace   fcol, "seticonptrs: (1) fontforeground ", X, R3
;
        TST     R1,#if_indirected
      [ TrueIcon2
        BLEQ    mungetruecolours
      ]
        Pull    "R3,R10,PC",EQ
;
        LDMIA   R2,{R2,R3,R14}
        STR     R2,spritename
;
        TST     R1,#if_text                     ; unless text as well,
        STREQ   R3,thisCBptr                    ; validation string = areaCBptr
        STRNE   R3,validationstring
        STR     R14,lengthflags                 ; if text, this must be <>0
      [ TrueIcon2
        BLEQ    mungetruecolours
      ]
        Pull    "R3,R10,PC",EQ
;
; check for line spacing
;
        Push    "R2,R3"
        AcceptLoosePointer_NegOrZero R3,-1
        CMP     R3,R3,ASR #31
        BEQ     %FT01
        BL      pageiniconbartask
        MOV     R2,#WimpValidation_Line
        BL      findcommand
        BNE     %FT01
;
        MOV     R2,#40
        LDRB    R14,[R3]                        ; shouldn't need to do this, but XOS_ReadUnsigned
        CMP     R14,#"0"                        ; returning an error can trash MessageTrans'
        BLO     %FT02                           ; error buffers
        CMP     R14,#"9"
        BHI     %FT02
;
        Push    "R0,R1"
        MOV     R0,#10
        MOV     R1,R3
        SWI     XOS_ReadUnsigned
        MOVVS   R2,#40
        Pull    "R0,R1"
02
        STR     R2,linespacing
01
        Pull    "R2,R3"
;
; check for anti-aliased font colour field
;
        TST     R1,#if_fancyfont
        BEQ     %FT01

        Push    "R2,R3"
        AcceptLoosePointer_NegOrZero R3,-1
        CMP     R3,R3,ASR #31
        BEQ     %FT11
        BL      pageiniconbartask       ; we need to access the task's data
        MOV     R2,#WimpValidation_Font
        BL      findcommand
        BNE     %FT11
;
        LDRB    R2, [R3], #1
      [ true ; slightly more robust (if less elegant) code: copes with incomplete/invalid specifier (eg "F0", "F9x" or even just "F")
        CMP     R2, #' '
        BLO     %FT11                   ; stop if terminated
        ORR     R2, R2, #&20
        CMP     R2, #'0'
        BLO     %FT11
        CMP     R2, #'9'
        SUBLS   R2, R2, #'0'
        BLS     %FT05
        CMP     R2, #'a'
        BLO     %FT11
        CMP     R2, #'f'
        SUBLS   R2, R2, #'a'-10
        BHI     %FT11
05
      |
        ASCII_UpperCase R2,R14
        Hex     R2,R2                   ; R2 = background colour
      ]
        STRB    R2,fontbackground
        Trace   fcol, "seticonptrs: (2) fontbackground ", X, R2

        LDRB    R2,[R3]
      [ true ; slightly more robust (if less elegant) code: copes with incomplete/invalid specifier (eg "F0", "F9x" or even just "F")
        CMP     R2, #' '
        BLO     %FT11                   ; stop if terminated
        ORR     R2, R2, #&20
        CMP     R2, #'0'
        BLO     %FT11
        CMP     R2, #'9'
        SUBLS   R2, R2, #'0'
        BLS     %FT05
        CMP     R2, #'a'
        BLO     %FT11
        CMP     R2, #'f'
        SUBLS   R2, R2, #'a'-10
        BHI     %FT11
05
      |
        ASCII_UpperCase R2,R14
        Hex     R2,R2                   ; R2 = foreground colour
      ]
        STRB    R2,fontforeground

        Trace   fcol, "seticonptrs: (2) fontforeground ", X, R2
11
        Pull    "R2,R3"
01
;
; check for the border field
;
        Push    "R0,R3"
;
        AcceptLoosePointer_NegOrZero R3,-1
        CMP     R3,R3,ASR #31
        BLNE    pageiniconbartask
        BLNE    getborder               ; attempt to define the border string
;
        Pull    "R0,R3"

    [ TrueIcon2
;
; check for a 24-bit colour specifier
;
        Push    "R4"
        ADR     R4, truefgcolour
        BL      readtruecolours
        Pull    "R4"

    |
      [ TrueIcon1
;
; check for a 24-bit colour specifier
;
        Push    "R2,R3"
        AcceptLoosePointer_NegOrZero R3,-1
        CMP     R3,R3,ASR #31
        BEQ     %FT14
        BL      pageiniconbartask
        MOV     R2,#WimpValidation_Colour
        BL      findcommand
        BNE     %FT14
        Push    "R0,R1"
;
        LDRB    R0,[R3]
        TEQ     R0,#'/'                ; if a '/' here then no foreground supplied
        MOV     R1,R3
        MOVEQ   R3,#-1
        BEQ     %FT12
;
        MOV     R0,#16
        MOV     R1,R3
        SWI     XOS_ReadUnsigned       ; read the foreground colour
        BVS     %FT13
	MOV	R2,R2,LSL #8
        STR     R2,truefgcolour
        LDRB    R0,[R1]
        TEQ     R0,#'/'                ; check for the /
        BNE     %FT13
12      MOV     R0,#16
        ADD     R1,R1,#1
        SWI     XOS_ReadUnsigned       ; read the background colour
	MOVVC	R2,R2,LSL #8
        STRVC   R2,truebgcolour
13
        Pull    "R0,R1"

        TST     R1,#is_shaded
        BEQ     %FT14
; deal with shading...
        LDR     R2,truefgcolour
        LDR     R3,truebgcolour
        Push    "R0,R12"
        BIC     R12,R1,#is_inverted
        CMP     R2,#-1
        MOVNE   R0,R2
        BLNE    inversefunc
        MOVNE   R2,R0
        CMP     R3,#-1
        MOVNE   R0,R3
        BLNE    inversefunc
        MOVNE   R3,R0
        Pull    "R0,R12"
        STR     R2,truefgcolour
        STR     R3,truebgcolour
14
        Pull    "R2,R3"

        TST     R1,#is_inverted         ; is the icon inverted
        BEQ     %FT04
      |
        TST     R1,#is_inverted         ; is the icon inverted
        BEQ     %FT02
      ]
;
; if the icon is inverted then swap around the various colours making it appear
; suitable.  Note that we should include the highlight colour if required.
;
        LDR     R0,border_type
        TEQ     R0,#border_action
        TEQNE   R0,#border_defaultaction
        BNE     %FT02                   ; if not sensible border then ignore
;
        LDR     R0,border_highlight
        STRB    R0,fontbackground       ; then freak the background colour to something sensible
;
        Push    "R3"                    ;320nk
        MOV     R3,R0
        TST     R1, #if_fancyfont
        BICEQ   R1,R1,#if_fcol :OR: if_bcol
        ORREQ   R1,R1,R0,LSL #ib_fcol
        LDRB    R0,fontforeground       ; was LDR!!
        ORREQ   R1,R1,R0,LSL #ib_bcol   ; munge flags to contain the new fg/bg colours
        STRB    R0,fontbackground
        STRB    R3,fontforeground
        Pull    "R3"
      [ TrueIcon1
        B       %FT04
02
        TST     R1, #if_text            ; text+sprite icons don't swap
        TSTNE   R1, #if_sprite
        BNE     %FT04
;
        LDR     R0, truefgcolour        ; swap the 24-bit colours here to save hassle later
        LDR     R14, truebgcolour
        STR     R0, truebgcolour
        STR     R14, truefgcolour
04
      |
02
      ]

    ]

        TST     R1,#if_sprite
      [ TrueIcon2
        BLEQ    mungetruecolours
      ]
        Pull    "R3,R10,PC",EQ          ; finished unless Sprite+Text
;
        AcceptLoosePointer_NegOrZero R3,-1
        CMP     R3,R3,ASR #31           ; no validation string?
      [ TrueIcon2
        BLEQ    mungetruecolours
      ]
        Pull    "R3,R10,PC",EQ

;
; process validation string for sprite+text icon
;
        BL      pageiniconbartask       ; we need to access the task's data
;
        Push    "R2"                    ; R2 --> text buffer
        MOV     R2,#WimpValidation_Sprite
        BL      findcommand             ; R3 --> command found
      [ TrueIcon2
        BLNE    mungetruecolours
      ]
        Pull    "R2,R3,R10,PC",NE       ; leave sprite as text if not found
;
        BL      scanname                ; copy sprite name into buffer
        [ slabinout
        STR     R1,two_sprite_save
        ]
        TST     R1,#is_inverted         ; if selected, choose the 2nd version
      [ TrueIcon2
        BLEQ    mungetruecolours
      ]
        Pull    "R2,R3,R10,PC",EQ
        LDRB    R14,[R3,#-1]            ; was that a ","?
        TEQ     R14,#","                ; if ended with ",", scan next name
        BICEQ   R1,R1,#is_inverted      ; prevent sprite being inverted too!
        BLEQ    scanname
      [ TrueIcon2
        BL      mungetruecolours
      ]
        Pull    "R2,R3,R10,PC"          ; R2 --> original (text) buffer


      [ TrueIcon2
readtruecolours ROUT
        ; In:  R3 -> validation string (or <= 0 to do nothing)
        ;      R4 -> first word to write to (should be truefgcolour or truetitlefg)
        ; Out: [truefgcolour], [truebgcolour], [truebgcolour2], [truewellcolour], [truefacecolour],  [trueoppcolour]   OR
        ;      [truetitlefg],  [truetitlebg],  [trueworkfg],    [trueworkbg],     [truescoutcolour], [truescincolour], [truetitlebg2]
        ;      may each be modified according to validation string
        Entry   "R0,R2,R3"
        AcceptLoosePointer_NegOrZero R3,-1
        CMP     R3,R3,ASR #31
        BEQ     %FT14
        BL      pageiniconbartask
        MOV     R2,#WimpValidation_Colour
        BL      findcommand
        BNE     %FT14
        Push    "R0,R1"
        MOV     R1, R3
readval_truefg
        LDRB    R0, [R1]
        TEQ     R0, #'/'               ; if a '/' here then no foreground supplied
        BEQ     readval_truebg
        MOV     R0, #16
        SWI     XOS_ReadUnsigned       ; read the foreground colour
        BVS     %FT13
        MOV	R2, R2, LSL #8
        STR     R2, [R4]
        LDRB    R0, [R1]
        TEQ     R0, #'/'               ; if not followed by a '/', then no other colours are supplied
        BNE     %FT13
readval_truebg
        ADD     R4, R4, #4
        LDRB    R0, [R1, #1] !         ; skip preceding '/'
        TEQ     R0, #'/'               ; if a '/' here then no background supplied
        BEQ     readval_truebg2
        MOV     R0, #16
        SWI     XOS_ReadUnsigned       ; read the background colour
        BVS     %FT13
        MOV     R2, R2, LSL #8
        STR     R2, [R4]
        LDRB    R0, [R1]
        TEQ     R0, #'/'               ; if not followed by a '/', then no other colours are supplied
        BNE     %FT13
readval_truebg2
        ADD     R4, R4, #4
        LDRB    R0, [R1, #1] !         ; skip preceding '/'
        TEQ     R0, #'/'               ; if a '/' here then no slabbed-in background supplied
        BEQ     readval_truewell
        MOV     R0, #16
        SWI     XOS_ReadUnsigned       ; read the slabbed-in background colour
        BVS     %FT13
        MOV     R2, R2, LSL #8
        STR     R2, [R4]
        LDRB    R0, [R1]
        TEQ     R0, #'/'               ; if not followed by a '/', then no other colours are supplied
        BNE     %FT13
readval_truewell
        ADD     R4, R4, #4
        LDRB    R0, [R1, #1] !         ; skip preceding '/'
        TEQ     R0, #'/'               ; if a '/' here then no well supplied
        BEQ     readval_trueface
        MOV     R0, #16
        SWI     XOS_ReadUnsigned       ; read the well colour
        BVS     %FT13
        MOV     R2, R2, LSL #8
        STR     R2, [R4]
        LDRB    R0, [R1]
        TEQ     R0, #'/'               ; if not followed by a '/', then no other colours are supplied
        BNE     %FT13
readval_trueface
        ADD     R4, R4, #4
        LDRB    R0, [R1, #1] !         ; skip preceding '/'
        TEQ     R0, #'/'               ; if a '/' here then no light 3D supplied
        BEQ     readval_trueopp
        MOV     R0, #16
        SWI     XOS_ReadUnsigned       ; read the light 3D colour
        BVS     %FT13
        MOV     R2, R2, LSL #8
        STR     R2, [R4]
        LDRB    R0, [R1]
        TEQ     R0, #'/'               ; if not followed by a '/', then no other colours are supplied
        BNE     %FT13
readval_trueopp
        ADD     R4, R4, #4
        LDRB    R0, [R1, #1] !         ; skip preceding '/'
        TEQ     R0, #'/'               ; if a '/' here then no dark 3D supplied
      [ :LNOT: TrueIcon3
        BEQ     %FT13
        MOV     R0, #16
        SWI     XOS_ReadUnsigned       ; read the dark 3D colour
        MOVVC   R2, R2, LSL #8
        STRVC   R2, [R4]
      |
        BEQ     readval_truetitlebg2
        MOV     R0, #16
        SWI     XOS_ReadUnsigned       ; read the dark 3D colour
        BVS     %FT13
        MOV     R2, R2, LSL #8
        STR     R2, [R4]
        LDRB    R0, [R1]
        TEQ     R0, #'/'               ; if not followed by a '/', then no other colours are supplied
        BNE     %FT13
readval_truetitlebg2
        ADD     R4, R4, #4
        LDRB    R0, [R1, #1] !         ; skip preceding '/'
        TEQ     R0, #'/'               ; if a '/' here then no input focus supplied
 [ TrueSelectionColours
	BEQ	readval_trueselbg
 |
        BEQ     %FT13
 ]
        MOV     R0, #16
        SWI     XOS_ReadUnsigned       ; read the input focus colour
 [ TrueSelectionColours
	BVS	%FT13
	MOV	r2,r2,LSL #8
	STR	r2,[r4]
 |
        MOVVC   R2, R2, LSL #8
        STRVC   R2, [R4]
 ]

 [ TrueSelectionColours

        LDRB    R0, [R1]
        TEQ     R0, #'/'               ; if not followed by a '/', then no other colours are supplied
        BNE     %FT13
readval_trueselbg
        ADD     R4, R4, #4
        LDRB    R0, [R1, #1] !         ; skip preceding '/'
        TEQ     R0, #'/'
	BEQ	%FT13
        MOV     R0, #16
        SWI     XOS_ReadUnsigned
	MOVVC	r2,r2,LSL #8
	STRVC	r2,[r4]
 ]
      ]

13
        Pull    "R0,R1"
14
        CLRV
        EXIT

mungetruecolours ROUT
        ; In:  R1 = icon flags
        ;      if fancy font, then [fontforeground] and [fontbackground] hold Wimp colours
        ;      [truefgcolour], [truebgcolour], [truebgcolour2], [truewellcolour], [truefacecolour] and [trueoppcolour]
        ;        hold specified true colours, or -1 if no specified true colour
        ;      [border_type] is border type
        ;      [border_highlight] is slabbed-in background Wimp colour
        ; Out: [truefgcolour], [truebgcolour] made valid, and adjusted for selection or shading
        ;      [truewellcolour], [truefacecolour] and [trueoppcolour] made valid, and shaded if necessary
        ;      R1 may be modified
        EntryS  "R0,R2"
        BL      getpalpointer           ; R14 -> Wimp palette

        LDR     R0, truefgcolour
        CMP     R0, #-1
        BNE     %FT01                   ; don't corrupt a 24-bit colour that was specified explicitly
        TST     R1, #if_fancyfont
        LDRNEB  R0, fontforeground
        MOVEQ   R0, R1, LSR #ib_fcol
        AND     R0, R0, #&F
        LDR     R0, [R14, R0, LSL #2]   ; look up palette entry
        STR     R0, truefgcolour
01
        LDR     R0, truebgcolour
        CMP     R0, #-1
        BNE     %FT01                   ; don't corrupt a 24-bit colour that was specified explicitly
        TST     R1, #if_fancyfont
        LDRNEB  R0, fontbackground
        MOVEQ   R0, R1, LSR #ib_bcol
        AND     R0, R0, #&F
        LDR     R0, [R14, R0, LSL #2]   ; look up palette entry
        STR     R0, truebgcolour
01
        LDR     R0, truebgcolour2
        CMP     R0, #-1
        LDREQ   R0, border_highlight
        ANDEQ   R0, R0, #&F
        LDREQ   R0, [R14, R0, LSL #2]   ; look up palette entry
        STREQ   R0, truebgcolour2

        LDR     R0, truewellcolour
        CMP     R0, #-1
        BNE     %FT01
        LDR     R0, border_type
        TEQ     R0, #border_defaultaction
        LDREQ   R0, [R14, #sc_cream :SHL: 2]         ; default = Wimp cream  (default action)
        LDRNE   R0, [R14, #sc_verylightgrey :SHL: 2] ; default = Wimp grey 1 (editable)
        STR     R0, truewellcolour
01
        LDR     R0, truefacecolour
        CMP     R0, #-1
        LDREQ   R0, [R14, #sc_white :SHL: 2]         ; default = Wimp white
        STREQ   R0, truefacecolour

        LDR     R0, trueoppcolour
        CMP     R0, #-1
        BNE     %FT01
        LDR     R0, border_type
        TEQ     R0, #border_ridge
        TEQNE   R0, #border_channel
        LDREQ   R0, [R14, #sc_lightgrey :SHL: 2]     ; default = Wimp grey 2 (shallow)
        LDRNE   R0, [R14, #sc_middarkgrey :SHL: 2]   ; default = Wimp grey 4 (normal)
        STR     R0, trueoppcolour
01
        TST     R1, #is_inverted
        BEQ     %FT01
        LDR     R0, border_type
        TEQ     R0, #border_action
        TEQNE   R0, #border_defaultaction
        BEQ     %FT02
        TST     R1, #is_shaded
        TSTEQ   R1, #if_sprite :OR: if_filled
        BEQ     %FT03
        ; normal inversion
 [ TrueSelectionColours
        LDR     R3, trueselfgcolour
	CMP	R3, #-1
	LDREQ	R3, truebgcolour
        LDR     R2, trueselbgcolour
	CMP	R2, #-1
	LDREQ	R2, truefgcolour
 |
        LDR     R2, truefgcolour
        LDR     R3, truebgcolour
 ]
        STR     R3, truefgcolour
        STR     R2, truebgcolour
        TST     R1, #if_sprite          ; deal with special cases
        ORREQ   R1, R1, #if_filled
        B       %FT01
03      ; "EOR" inversion
        LDR     R2, truebgcolour
        LDR     R3, =&FFFFFF00
        EOR     R0, R2, R3              ; necessary to ensure black inverts (compatibility)
        LDR     R2, truefgcolour
      [ TrueIcon3
        LDR     R3, trueworkbg          ; best guess at background colour
      |
        LDRB    R3, work_back_colour    ; best guess at background colour
        AND     R3, R3, #&F
        LDR     R3, [R14, R3, LSL #2]   ; look up palette entry
      ]
        EOR     R2, R2, R0
        EOR     R3, R3, R0
        STR     R2, truefgcolour
        STR     R3, truebgcolour
        B       %FT01
02      ; action button inversion
        LDR     R0, truebgcolour2
        STR     R0, truebgcolour
        TST     R1, #if_sprite          ; deal with special cases
        ORREQ   R1, R1, #if_filled
01
      [ true
        TST     R1, #if_sprite :OR: if_filled :OR: if_fancyfont  ; should only apply to system font, for compatibility
      |
        TST     R1, #if_sprite :OR: if_filled
      ]
        TSTEQ   R1, #is_inverted
      [ TrueIcon3
        LDR     R2, trueworkbg
      |
        LDRB    R2, work_back_colour
        AND     R2, R2, #&F
        LDR     R2, [R14, R2, LSL #2]   ; look up palette entry
      ]
        STREQ   R2, truebgcolour        ; best guess for what background to antialias to

        TST     R1, #is_shaded
        EXITS   EQ                      ; must preserve flags

      [ true
        ADR     R0, truefgcolour
        LDR     R14, [R0]
        Push    "R2, R14"
        MOV     R2, #-1
        BL      fadetruecolour
        ; further bodge: if the faded icon fg matches the unfaded icon bg AND the window bg, reinstate the unfaded icon fg
        ; this fixes the questionable way CC fade their gadgets
        LDR     R14, [R0]
        ADR     R2, truebgcolour
        LDR     R2, [R2]
        TEQ     R14, R2
        Pull    "R2"
        TEQEQ   R14, R2
        Pull    "R14"
        STREQ   R14, [R0]
      ]
        LDR     R0, border_type
        TEQ     R0, #border_normal
        MOVNE   R2, #-1                 ; only bodge the background if a non-3D icon
        ADR     R0, truebgcolour
        BL      fadetruecolour
        MOV     R2, #-1
      [ false
        ADR     R0, truefgcolour
        BL      fadetruecolour
      ]
        ADR     R0, truewellcolour
        BL      fadetruecolour
        ADR     R0, truefacecolour
        BL      fadetruecolour
        ADR     R0, trueoppcolour
        BL      fadetruecolour
        EXITS                           ; must preserve flags

        LTORG

fadetruecolour ROUT
        ; In:  R0 -> palette entry to fade
        ;      R2 =  window background palette entry, or -1 to turn off special case
        ; Out: [R0] updated: grey, and half the distance from white (cf sprites, which are 1/4 the distance from white)
        Push    "R1, R2, R4, R14"
        LDR     R4, [R0]

        TEQ     R4, R2
        Pull    "R1, R2, R4, PC", EQ    ; leave icon background alone if it matches the work area background

        MOV     R14, #&FF
        AND     R1, R14, R4, LSR #8     ; extract red
        AND     R2, R14, R4, LSR #16    ; extract green
        AND     R3, R14, R4, LSR #24    ; extract blue

        MOV     R14,#77
        MUL     R14,R1,R14              ; red *77
        MOV     R1,#150
        MLA     R14,R2,R1,R14           ; red *77 + green *150
        RSB     R3,R3,R3,LSL #3
        ADD     R14,R14,R3,LSL #2       ; red *77 + green *150 + blue *28

        ADD     R14,R14,#&7F            ; Rounding
        ADD     R14,R14,R14,LSL #8      ; Make 16 bit fractional
        ADD     R14,R14,#&100           ; Rounding
        MOV     R14,R14,LSR #16         ; Convert to 8bit luma

        RSBS    R14, R14, #&FF          ; get distance from white
        MOVMI   R14, #0                 ; just in case
        MOV     R14, R14, LSR #1        ; divide by 2
        RSB     R14, R14, #&FF          ; invert back

        MOV     R4, R14, LSL #8
        ORR     R4, R4, R14, LSL #16
        ORR     R4, R4, R14, LSL #24    ; recombine to make &BBGGRR00

        STR     R4, [R0]
        Pull    "R1, R2, R4, PC"
      ]


scanname
        Push    "LR"
;
        ADR     R2,spritenamebuf
        STR     R2,spritename           ; [spritename] --> sprite name
        MOV     R14,#12                 ; max chars
01
        LDRB    R10,[R3],#1             ; get next char
        TEQ     R10,#";"
        TEQNE   R10,#","
        MOVEQ   R10,#0                  ; terminate if ";" or "," or ctrl-char
        STRB    R10,[R2],#1
        SUBS    R14,R14,#1              ; no terminator if 12 chars long
        Pull    "PC",EQ
        CMP     R10,#32                 ; any ctrl-char also terminates
        BCS     %BT01
;
        Pull    "PC"


; in    R3 -> string to scan
; out   R0 = result =0
;       R3 -> terminating character
; Preserves flags

getnumber ROUT

        EntryS  "R1-R2"

        MOV     R0,#0                   ; result =0
        MOV     R1,#10                  ; Number base

10      LDRB    R2,[R3],#1
        SUB     R2,R2,#'0'              ; ASCII -> Numeric, ctrl's-> large +ve
        CMP     R2,#10                  ; is the character valid?
        MLALO   R0,R1,R0,R2             ; R0=R1*R0+R2
        BLO     %BT10                   ; loop back until all characters scanned
20
        Debug   bo,"getnumber result =>",R0

        SUB     R3,R3,#1                ; balance the stack and finished
        EXITS


;
; Entry:  [redrawhandle] = window handle
;         [iconhandle] = icon handle
;         If swapping R1=icon flags.
;
; Exit:   if this is the icon bar, the relevant task is paged in
;         userblk preserved - this is incorrect if a task swap occurs
;         If swapping, only if icon is indirected.

pageiniconbartask
        EntryS
;
        LDR     R14,redrawhandle
        CMP     R14,#nullptr
        BEQ     piibt_x1
        LDR     R0,iconbarhandle
        TEQ     R0,R14
        BNE     piibt_x1
        TST     R1,#if_indirected
        BEQ     piibt_x1
;
        Push    "R1-R4,R7,userblk"              ; userblk not in use here!
        LDR     R4,iconhandle
        BL      findicon
        LDREQ   R14,[R2,#icb_taskhandle]
        Task    R14,EQ,"Icon bar redraw"
;
        Pull    "R1-R4,R7,userblk"
piibt_x1
        EXITS                                   ; preserve flags

;
; Entry:  R0,R1 = window/icon handles (rel.)
; Exit:   handle -> window definition, relevant task paged in
;         userblk -> block for relevant task, if task swap occurred
;
; If swapping, page in the task only if icon is indirected.

pageinicontask_R3R4
        Push    "R0-R4,LR"
        MOV     R0,R3
        MOV     R1,R4
        BL      pageinicontask
        Pull    "R0-R4,PC"

pageinicontask
        EntryS
;
        MOV     handle,R0
        BL      checkhandle
        BVS     piit_x1
;
        CMP     R0,#nullptr             ; is it system window?
        BEQ     %FT01
;
        CMP     R1,#0
        BLT     %FT01                   ; or a system icon?
;
        LDR     R14,[handle,#w_icons]
        ADD     R14,R14,R1,LSL #i_shift
        LDR     R14,[R14,#i_flags]
        TST     R14,#if_indirected
        BEQ     piit_x1
01
        LDR     R14,iconbarhandle
        TEQ     R0,R14
        BEQ     %FT01
;
        LDR     R14,[handle,#w_taskhandle]
        Task    R14,,"Icon access"
piit_x1 EXITS
01
        Push    "R1-R4,R7"
        MOV     R4,R1                           ; R1 = icon handle
        BL      findicon
        LDREQ   R14,[R2,#icb_taskhandle]
        Task    R14,EQ,"Icon access (iconbar)"
        Pull    "R1-R4,R7"
        EXITS                                   ; preserve flags


;-----------------------------------------------------------------------------
; Draw icon, with regard to the inverting and shading flags
; Entry:  R1 = flag word (including colours)
;         R2 --> data
;         x0,y0,x1,y1 = bounding box
; Exit :  colours set up (if required) and icon drawn (if necessary)
;-----------------------------------------------------------------------------

drawcolouredicon
        Push    "cx0,cy0,cx1,cy1,LR"
;
        ADR     R14,clipx0                      ; check intersection FIRST!
        LDMIA   R14,{cx0,cy0,cx1,cy1}
        CMP     x0,cx1
        CMPLT   y0,cy1
        CMPLT   cx0,x1
        CMPLT   cy0,y1
;
        Pull    "cx0,cy0"
        Pull    "cx1,cy1,PC",GE
;
; now set the colours
;
    [ TrueIcon2
;
; this code covers every type of icon
;
        Push    "R0-R9"
        LDR     R0, border_type
        TEQ     R0, #border_action
        TEQNE   R0, #border_defaultaction
        BEQ     %FT01
        TST     R1, #if_sprite :OR: if_filled
        TSTEQ   R1, #is_shaded
        BNE     %FT01
        TST     R1, #is_inverted
        BEQ     %FT01
        ; apply EOR inversion *before* main icon draw
        BL      trueeorrectangle
01
        Pull    "R0-R9"
        BL      icon_fg
        BL      icon_bg

      [ outlinefont
        LDR     R0,systemfont
        TEQ     R0,#0                           ; is the outline font defined?
        TSTEQ   R1,#if_fancyfont
      |
        TST     R1,#if_fancyfont
      ]
        TSTNE   R1, #if_text
        BLNE    setfancyfontcolours

        BLVC    godrawicon

tryshaded
exitcicon
        Pull    "cx1,cy1,PC"

trueeorrectangle
        ; determine EOR colour from the window and icon background colours
        Push    "R14"
      [ TrueIcon3
        LDR     R0, trueworkbg
      |
        BL      getpalpointer
        LDRB    R0, work_back_colour
        AND     R0, R0, #&F
        LDR     R0, [R14, R0, LSL #2]
      ]
        SWI     XColourTrans_ReturnColourNumber
        MOV     R1, R0
        LDR     R0, truebgcolour
        SWI     XColourTrans_ReturnColourNumber

        EOR     R1, R0, R1              ; we want the window background to be mapped to the icon background
        MOV     R0, #&13                ; set background, EOR colours
        SWI     XOS_SetColour

        BLVC    solidrectangle
        CLRV
        Pull    "PC"

    |

        TST     R1,#if_sprite
        BNE     ciconsprite
;
        TST     R1,#if_fancyfont
        BNE     ciconfancy
        TST     R1,#if_filled
        BEQ     ciconhollow                     ; EOR with background colour
   ;    TST     R1,#if_text
   ;    BNE     cicontext
;
; ordinary text or default
;       - swap colours if inverted
;       - AND colours if shaded
;

cicontext
        TST     R1,#is_shaded
        ANDNE   R1,R1,#if_shadecols             ; shade foreground & background
        TST     R1,#is_inverted
      [ TrueIcon1
        MOV     R0,R1,LSR #ib_fcol              ; get foreground colour
        BLEQ    icon_fg
        BLNE    icon_bg
        MOV     R0,R1,LSR #ib_bcol              ; get background colour
        BLEQ    icon_bg
        BLNE    icon_fg
      |
        MOV     R0,R1,LSR #ib_fcol              ; get foreground colour
        BLEQ    foreground
        BLNE    background
        MOV     R0,R1,LSR #ib_bcol              ; get background colour
        BLEQ    background
        BLNE    foreground
      ]
;
      [ outlinefont
        LDR     R0,systemfont
        CMP     R0,#0                           ; is the outline font defined?
        TSTNE   R1,#if_text                     ; and is the icon a text jobbie?
        BLNE    ciconfancy2
      ]
;
        BLVC    godrawicon
        B       exitcicon                       ; shading done already

tryshaded
        [ false
        BVS     exitcicon
        TST     R1,#is_shaded                   ; NB do shading last!
        MOVNE   R0,#(2:SHL:4):OR:(&80+2)
        BLNE    int_setcolour                   ; GCOL 2,128+wimpcolour(2)
        BLNE    solidrectangle                  ; AND with colour 2
;

        |
        [ false                                 ; don't need to do anything
        TST     R1,#is_shaded
        LDRNE   R14,tempworkspace+20
        STRNEB  R14,work_back_colour
        ]
        ]
exitcicon
        Pull    "cx1,cy1,PC"

ciconsprite
        LDRB    R0,fontbackground               ; get background colour
      [ TrueIcon1
        BL      icon_bg                         ; in case background set
      |
        BL      background                      ; in case background set
      ]
        BVS     exitcicon
;
        TST     R1,#if_text
        TSTNE   R1,#is_inverted
        LDREQB  R0,fontforeground               ; R0 = foreground colour
        BEQ     %FT01

        TST     R1,#if_filled
        BICNE   R1,R1,#if_filled                ; CORRUPTS R1!!!
        BLNE    solidrectangle                  ; fill in normally
      [ TrueIcon1
        LDRB    R0,fontforeground
        BL      icon_bg                         ; but invert the text
        LDRB    R0,fontbackground
01
        BL      icon_fg                         ; (must be same as EOR colour)
      |
        LDRB    R0,fontforeground
        BL      background                      ; but invert the text
        LDRB    R0,fontbackground
01
        BL      foreground                      ; (must be same as EOR colour)
      ]
;    [ {FALSE}
      [ outlinefont
        LDR     R14,systemfont
        TEQ     R14,#0
        TSTEQ   R1,#if_fancyfont
      |
        TST     R1,#if_fancyfont
      ]
        BLNE    setfancyfontcolours
;    ]
        BL      godrawicon
;
        B       exitcicon

ciconfancy
        TST     R1,#if_border
        LDRNEB  R0,fontforeground               ; colour in validation string
      [ TrueIcon1
        BLNE    icon_fg
      |
        BLNE    foreground
      ]
;
        TST     R1,#is_inverted
        LDREQB  R0,fontbackground               ; colour in validation string
        LDRNEB  R0,fontforeground               ; colour in validation string

        TST     R1,#if_filled
        LDREQB  R0,fontbackground
      [ TrueIcon1
        BLNE    icon_bg
      |
        BLNE    background
      ]

ciconfancy2
;      [ {False}
        BL      setfancyfontcolours
;      ]
        BL      godrawicon
        B       exitcicon
    ]

setfancyfontcolours TraceL font
        Push    "R1-R2,LR"
;
    [ TrueIcon3
        LDR     R2, truefgcolour
        LDR     R1, truebgcolour
        BL      settruefontcolours              ; now true colours are guaranteed to be available
    |
        TST     R1,#is_inverted
        MOV     R14,R1
        LDREQB  R1,fontbackground
        LDRNEB  R1,fontforeground
        LDREQB  R2,fontforeground
        LDRNEB  R2,fontbackground
        TST     R14,#is_shaded
        BEQ     %FT05
        TST     R14,#if_fancyfont
        ANDNE   R1,R1,#2                        ; 3.10 compat, but looks naff
        ANDNE   R2,R2,#2
        ANDEQ   R2,R2,#4                        ; for system font icons with outline font
      [ TrueIcon1
05
        BL      getpalpointer
        LDR     R1, [R14, R1, LSL #2]           ; R1 = physical background colour
        LDR     R2, [R14, R2, LSL #2]           ; R2 = physical foreground colour

        LDR     R14,truefgcolour                ; check for true colour overrides
        CMP     R14,#-1
        MOVNE   R2,R14
        LDR     R14,truebgcolour
        CMP     R14,#-1
        MOVNE   R1,R14

        BL      settruefontcolours
      |
05
        BL      setfontcolours
      ]
    ]
;
        Pull    "R1-R2,PC"

    [ :LNOT: TrueIcon2

ciconhollow
        ; do shaded by munging its colours and pretending it's inverted
        ; this emulates the really naff way that 3.10 shades an icon

        TST     R1,#is_shaded
        BEQ     %FT05
        ORR     R1,R1,#is_inverted
        LDRB    R14,work_back_colour
        BIC     R14,R14,#3                      ; how will it 'shade'
; note 3.10 use 2, but this looks bad with an outline font
        AND     R14,R14,#15                     ; make sure we don't overwrite
        BIC     R1,R1,#15 :SHL: ib_bcol         ; EOR colour = 0
        ORR     R1,R1,R14 ,LSL #ib_bcol         ; EOR colour = back BIC 2
        LDR     R0,=&ffffffff
        BIC     R0,R0,#15 :SHL: ib_fcol
        ORR     R0,R0,#3  :SHL: ib_fcol         ; yep, can be done more easilly,
                                                ; but this is so screwed it needs to be obvious!
        AND     R1,R1,R0                        ; 'shade' foreground
        EOR     R1,R1,R14 ,LSL #ib_fcol         ; cancel out EOR effect
        BIC     R1,R1,#is_shaded                ; stop munge later on

05
        [ {TRUE}
         [ outlinefont
        LDR     R0,systemfont           ; 320nk background must be set up
                                        ; for antialiased fonts
         |
        MOV     R0,#0
        ]

        TEQ     R0,#0
        BEQ     hollow_next

        TST     R1,#is_inverted

        LDRB    R0,work_back_colour
        STREQB  R0,fontbackground

        BEQ     hollow_next

        MOV     R14,R1, LSR #ib_bcol

        EOR     R0,R0,R14
        STRB    R0,fontbackground

        MOV     R0,R1, LSR #ib_fcol
        EOR     R0,R0,R14
        AND     R0,R0,#15
        STRB    R0,fontforeground

        MOV     R0,R1, LSR #ib_bcol

        BL      eorrectangle
        BIC     R1,R1,#is_inverted
        TST     R1,#if_border
        LDRNEB  R0,fontforeground
      [ TrueIcon1
        BLNE    icon_fg                         ; needs to be set
      |
        BLNE    foreground                      ; needs to be set
      ]
        BL      godrawicon
        B       tryshaded
hollow_next
        ]


        TST     R1,#is_inverted
        BNE     %FT10
        MOV     R0,R1,LSR #ib_fcol
      [ TrueIcon1
        BL      icon_fg                        ; background colour not used
      |
        BL      foreground                      ; background colour not used
      ]
        BLVC    godrawicon
        B       tryshaded

;
10
        MOV     R0,R1,LSR #ib_bcol
        MOV     R14,R1, LSR #ib_fcol
        EOR     R0,R0,R14                       ; new foreground
      [ TrueIcon1
        BL      icon_fg
      |
        BL      foreground
      ]
        BIC     R1,R1,#is_inverted
        MOV     R0,R1, LSR #ib_bcol
        BLVC    eorrectangle                    ; EOR with background colour
        BLVC    godrawicon
        B       tryshaded

eorrectangle
        [ false
        Push    LR
;
        AND     R0,R0,#&0F
        ORR     R0,R0,#&80+&30
        BL      int_setcolour
        BLVC    solidrectangle
;
        Pull    PC
        |
; the old way (above) only worked sucessfully in 16 colour modes
        [ false
        Push    "R1-R3,lr"
        LDRB    R14,work_back_colour
        EOR     R0,R0,R14
        AND     R0,R0,#&f                               ; bg+ib
        BL      getpalpointer
        LDR     R0,[R14,R0, LSL #2]
        MOV     R2,R14
        SWI     XColourTrans_ReturnColourNumber
        MOV     R1,R0                                   ; C(bg+ib)
        LDRB    R0,work_back_colour                     ; work area background
        AND     R0,R0,#&f
        LDR     R0,[R2,R0, LSL #2]
        SWI     XColourTrans_ReturnColourNumber         ;C(bg)
        EOR     R1,R0,R1                                ;C(bg)+C(bg+ib)
        MOV     R0,#&13                                 ; EOR action on background
        SWI     XOS_SetColour
        BL      solidrectangle
        Pull    "R1-R3,PC"
        |
; the above demonstrates the mechanism, but we use a lookup table to improve performance
        Push    "R1,lr"
        ADRL    lr,inverselookup
        LDR     R1,[lr]
        CMP     R1,#-1
        BLEQ    findeorvalues
        ADRL    lr,inverselookup

        AND     R0,R0,#15
        ADD     lr,lr,R0, LSL #6
        LDRB    R0,work_back_colour
        AND     R0,R0,#15
        LDR     R1,[lr,R0, LSL #2]
        MOV     R0,#&13
        SWI     XOS_SetColour
        BL      solidrectangle
        Pull    "R1,PC"
         ]
        ]

findeorvalues
        Push    "R0-R5,lr"
        BL      getpalpointer
        MOV     R2,R14

        ADRL    R3,inverselookup+16*64
        MOV     R4,#15
05
        MOV     R5,#15
        SUB     R3,R3,#64                               ; one row
10
        EOR     R0,R4,R5
        LDR     R0,[R2,R0, LSL #2]                      ; palette entry for colour
        SWI     XColourTrans_ReturnColourNumber
        MOV     R1,R0                                   ; C(bg+ib)
        LDR     R0,[R2,R5, LSL #2]
        SWI     XColourTrans_ReturnColourNumber         ;C(bg)
        EOR     R1,R0,R1                                ;C(bg)+C(bg+ib)
        STR     R1,[R3,R5, LSL #2]
        SUBS    R5,R5,#1
        BPL     %BT10
        SUBS    R4,R4,#1
        BPL     %BT05

        Pull    "R0-R5,PC"

    ]

;-----------------------------------------------------------------------------
; Icon graphics routines - assumes colours already set up
; Entry :  R1 = flag word
;          R2 --> text/sprite name etc.
;          x0,y0,x1,y1 = bounding box of icon
;          [iconhandle] = handle of icon
;-----------------------------------------------------------------------------

drawicon_system_sysf
      [ outlinefont
        Push    "LR"
        LDR     R14,systemfont
        Push    "R14"
        MOV     R14,#0
        STR     R14,systemfont          ; Force this icon to use system font
        BL      drawicon_system
        Pull    "R14"
        STR     R14,systemfont
        Pull    "PC"
      |
        ; Fall through, it's already system font
      ]
drawicon_system

; R1 = icon flags
; R2 -> icon data
; (R6, R7, R8, R9) = icon bbox

        TraceK  font, "drawicon_system: flags "
        TraceX  font, R1
        TraceK  font, ", data "
        TraceX  font, R2
        TraceNL font

      [ No32bitCode
        Push    "LR"
      |
        ; Yucky yucky stacky stacky (omounts to Push "LR,CPSR")
        STR     LR, [SP, #-8]!
        MRS     LR, CPSR
        STR     LR, [SP, #4]
      ]
        BL      seticonptrs             ; clear out validation string etc.
      [ TrueIcon3
        LDR     LR, truetitlefg         ; forcibly reassert title truecolours
        STR     LR, truefgcolour
        LDR     LR, titlecolour
        STR     LR, truebgcolour
      ]
;        Pull    "LR"

;drawicon TraceL font

; R1 = icon flags
; R2 -> icon data
; (R6, R7, R8, R9) = icon bbox

;        TraceK  font, "drawicon: flags "
;        TraceX  font, R1
;        TraceK  font, ", data "
;        TraceX  font, R2
;        TraceNL font

        Push    "cx0,cy0,cx1,cy1"
;
        ADR     R14,clipx0
        LDMIA   R14,{cx0,cy0,cx1,cy1}
        CMP     x0,cx1
        CMPLT   y0,cy1
        CMPLT   cx0,x1
        CMPLT   cy0,y1
      [ No32bitCode
        Pull    "cx0,cy0,cx1,cy1,LR"
        MOVGES  PC,LR                   ; drop through
      |
        Pull    "cx0,cy0,cx1,cy1"
        LDR     LR,[SP,#4]
        BGE     drawicon_out

        MSR     CPSR_f,LR
        LDR     LR,[SP],#8              ; woo
      ]

godrawicon TraceL font

; R1 = icon flags
; R2 -> icon text

        TraceK  font, "godrawicon: flags "
        TraceX  font, R1
        TraceK  font, ", data "
        TraceX  font, R2
        TraceNL font

        EntryS  "cx1,cy1"
;
        BL      pageiniconbartask       ; also called by seticonptrs (maybe)
;
        LDR     R14,writeabledir
        Push    "r14"

 [ FullIconClipping
	LDR	r14,ThreeDFlags
	TST	r14,#ThreeDFlags_FullIconClipping
	BEQ	%FT01
        Push    "cx0-y1"
        ADR     R14,clipx0
        LDMIA   R14,{cx0,cy0,cx1,cy1}
        ADR     R14,oldclipx0
        STMIA   R14,{cx0,cy0,cx1,cy1}
        max     x0,cx0                  ; NB must result in non-null window
        max     y0,cy0                  ; since the rectangles intersect
        min     x1,cx1
        min     y1,cy1
        BL      graphicswindow
        Pull    "cx0-y1"
01
 ]
        TST     R1,#if_numeric
        MOVNE   R14,#0
        STRNE   R14,writeabledir
;
        TST     R1,#if_filled
 [ ThreeDPatch
	BLNE	iconfilledCheckMenu
 |
        BLNE    iconfilled              ; this checks for if_fancyfont
 ]
        TST     R1,#if_sprite
        BLNE    iconsprite              ; IS allowed if if_text set!!!
        BVC     %FT01
        TST     R1,#if_indirected
        BEQ     enddr
        CLRV
01
        TST     R1,#if_text
        BLNE    icontext                ; also checks for if_fancyfont
        TST     R1,#if_border
        BLNE    iconborder
enddr
 [ FullIconClipping
	LDR	r14,ThreeDFlags
	TST	r14,#ThreeDFlags_FullIconClipping
        ADRNE   R14,oldclipx0
        LDMNEIA R14,{x0,y0,x1,y1}
        BLNE    graphicswindow
 ]
        Pull    "r14"
        STR     R14,writeabledir
        EXITS                           ; preserves flags (errors lost)

      [ :LNOT:No32bitCode
drawicon_out
        MSR     CPSR_f,LR
        LDR     PC,[SP],#8              ; woo
      ]

icontext TraceL font

; R1 = icon flags
; R2 -> icon text

        TraceK  font, "icontext: flags "
        TraceX  font, R1
        TraceK  font, ", string "
        TraceS  font, R2
        TraceNL font

        Push    "R1-R4,LR"

        LDR     R14,linespacing         ; is this a formatted icon?
        CMP     R14,#-1
        BNE     iconformatted
;
        Push    "R3,x0-y1"
        LDR     R3,validationstring
        BL      getborder
        BL      adjustforborder         ; attempt to adjust for border specified
        BL      findtextorigin          ; [spaceinicon] ==> does it fit?, adjust cx1
        Pull    "R3,x0-y1"
;
        Pull    "R1-R4,PC",VS           ; depends on [hascaret], too
;
; if icon is too big, ensure that it is clipped
;
 [ RO4
; MB FIX assume icon is always too big so that top and bottom are clipped properly.
;        LDR     R14,spaceinicon
;        CMP     R14,#6                  ; needs at least 3*dx space left
;        BPL     %FT01
; end MB FIX

 [ FullIconClipping
	LDR	r14,ThreeDFlags
	TST	r14,#ThreeDFlags_FullIconClipping
	BNE	%FT01
 ]
 |
        LDR     R14,spaceinicon
        CMP     R14,#6                  ; needs at least 3*dx space left
        BPL     %FT01
 ]
;
        Push    "cx0-y1"
        ADR     R14,clipx0
        LDMIA   R14,{cx0,cy0,cx1,cy1}
        ADR     R14,oldclipx0
        STMIA   R14,{cx0,cy0,cx1,cy1}
        max     x0,cx0                  ; NB must result in non-null window
        max     y0,cy0                  ; since the rectangles intersect
        min     x1,cx1
        min     y1,cy1
        BL      graphicswindow
        Pull    "cx0-y1"
01
;
; if sprite+text icon, and (inverted or not filled), fill area just round text
;
        EOR     R14,R1,#if_filled
        TST     R14,#is_inverted
        TSTEQ   R14,#if_filled
        TSTNE   R14,#if_sprite
        BEQ     %FT02

 [ ThreeDPatch
	LDR	r14,ThreeDFlags
	TST	r14,#ThreeDFlags_NoIconBgInTransWindows
	BEQ	%FT90			; if clearing the background for icons in transparent windows is disabled then skip this bit
	LDR	r14,redrawhandle
	CMP	r14,#nullptr
	BEQ	%FT90
	Abs	r14,r14
	LDRB	r14,[R14,#w_wbcol]
	TEQ	r14,#&FF		; if window background is transparent
	TSTEQ	r1,#is_inverted		; and the icon is not inverted
	BEQ	%FT02			; then don't plot the rectangle
90
 ]
        LDRB    R14,[R2]                ; is the text null?
        CMP     R14,#32
        BICCC   R1,R1,#if_filled        ; don't fill if null rectangle
        BCC     %FT02
;
 [ ThreeDPatch
	ORR	R1,R1,#if_filled        ; now we must fill the background!
        Push    "r0,cx0-cy1"

        LDR     R14,text_y0
        ADD     cy1,cy1,R14
        CMP     cx1,x0                  ; don't draw outside icon!
        MOVLT   cx1,x0
        CMP     cy1,y0
        MOVLT   cy1,y0

        LDR     R14,writeabledir
        TEQ     R14,#0
        SUBNE   R14,cx1,x0
        SUBNE   cx1,x1,R14
        CMP     cx1,x1                  ; don't draw outside icon!
        MOVGT   cx1,x1

	MOV	cx0,cx1
	MOV	cy0,cy1

        LDR     cy1,[sp,#4*4]
        LDR     R14,temp_text_height
        ADD     cy1,cy1,R14
        LDR     R14,text_width

        LDR     R0,writeabledir
        TEQ     R0,#0
        ADDEQ   cx1,cx1,R14
        SUBNE   cx1,cx1,r14
        CMP     cx1,x0                  ; don't draw outside icon!
        MOVLT   cx1,x0

        CMP     cx1,x1                  ; don't draw outside icon!
        MOVGT   cx1,x1
        CMP     cy1,y1
        MOVGT   cy1,y1

;        LDR     R14,dx                  ; make inclusive
;        SUB     cx1,cx1,R14
;        LDR     R14,dy
;        SUB     cy1,cy1,R14

	; cx0-cy1 now contain the rectangle to plot

	Push	"handle"

	TST	r1,#is_inverted
	BNE	%FT92

	LDR	r14,ThreeDFlags
	TST	r14,#ThreeDFlags_RemoveIconBackgrounds
	BEQ     %FT92

	MOV	r0,r1,LSR #ib_bcol
	LDR	handle,redrawhandle
	Debug	threedpatch,"redraw handle = ",handle
	CMP	handle,#nullptr
	BEQ	%FT92
	Abs	handle,handle
93	LDRB	r14,[handle,#w_wbcol]
	Debug	threedpatch,"Window bg colour = ",r14
	Debug	threedpatch,"Icon bg colour = ",r0
	TST	r1,#if_fancyfont
	LDRNEB	r0,fontbackground
	TEQ	r0,r14
	BEQ	%FT90
92
	Push	"cx0-y1"		; cx0-cy1 and x0-y1
	Pull	"x0-y1"
	bl	solidrectangle
	Pull	"x0-y1"
	b	%FT91
90
	BIC	r1,r1,#if_filled	; the icon isn't actually filled now.
	Debug	threedpatch,"Calling ploticonbackgroundsprite",cx0,cy0,cx1,cy1,handle
	bl	ploticonbackgroundsprite
91
	Pull	"handle"
        Pull    "r0,cx0-cy1"
 | ; NOT ThreeDPatch
        ORR     R1,R1,#if_filled        ; now we must fill the background!
        Push    "cx1,cy1"
        LDR     R14,text_y0
        ADD     cy1,cy1,R14
        CMP     cx1,x0                  ; don't draw outside icon!
        MOVLT   cx1,x0
        CMP     cy1,y0
        MOVLT   cy1,y0
        MOV     R0,#4
;
        LDR     R14,writeabledir
        TEQ     R14,#0
        SUBNE   R14,cx1,x0
        SUBNE   cx1,x1,R14
        CMP     cx1,x1                  ; don't draw outside icon!
        MOVGT   cx1,x1
;
        BL      plotcxy1                ; PLOT R0,cx1,cy1 (lower-left)
        LDR     cy1,[sp,#1*4]
        [ {FALSE}
        TST     R1,#is_inverted
        LDREQ   R14,temp_text_height    ;use real height!
        LDRNE   R14,text_y1
        |
        LDR     R14,temp_text_height
        ]
        ADD     cy1,cy1,R14
        LDR     R14,text_width
;
        LDR     R0,writeabledir
        TEQ     R0,#0
        ADDEQ   cx1,cx1,R14
        SUBNE   cx1,cx1,r14
        CMP     cx1,x0                  ; don't draw outside icon!
        MOVLT   cx1,x0
;
        CMP     cx1,x1                  ; don't draw outside icon!
        MOVGT   cx1,x1
        CMP     cy1,y1
        MOVGT   cy1,y1

        LDR     R14,dx                  ; make inclusive
        [ false
        ADD     cx1,cx1,R14
        LDR     R14,dx_1
        BIC     cx1,cx1,R14
        |
        SUB     cx1,cx1,R14
        ]
        LDR     R14,dy
        SUB     cy1,cy1,R14
;
    [ {FALSE}
      [ outlinefont
        LDR     R0,systemfont
        TEQ     R0,#0                   ; is it a system font?
        TSTEQ   R1,#if_fancyfont
      |
        TST     R1,#if_fancyfont
      ]
        MOVNE   R0,#4
        MOVEQ   R0,#&67
    |
        MOV     R0,#&67
    ]
;
        BL      plotcxy1                ; PLOT R0,cx1,cy1 (upper-right)
;
        Pull    "cx1,cy1"
 ]
02
        LDR     LR,writeabledir
        CMP     LR,#0
        BEQ     %FT94
;
        SUB     r14,cx1,x0
        SUB     cx1,x1,r14
        SUB     cx1,cx1,#16

        Push    "R1"
      [ BlendedFonts
        ADRL    R0,writedirreverse
      |
        ADR     R0,writedirreverse
      ]
        MOV     R1,#writedirreverse_len
        SWI     XOS_WriteN              ; poke the write direction for the cursor
        Pull    "R1"
94
        MOV     R0,#4
        BL      plotcxy1                ; move to a sensible position
;
      [ outlinefont
        LDR     LR,systemfont
        TEQ     LR,#0
        TSTEQ   R1,#if_fancyfont
      |
        TST     R1,#if_fancyfont
      ]
        BEQ     %FT95                   ; if not fancy font then ignore

        [       outlinefont
        BL      remembercurrentfont
        ]

        Push    "R0-R7"

        BL      setfancyfontcolours

        ;get the font to use
        [       outlinefont
        TST     R1, #if_fancyfont
        LDREQ   R3, systemfont
        MOVNE   R3, R1, LSR #ib_fontno  ; get the correct font handle
        |
        MOV     R3, R1, LSR #ib_fontno  ; select appropriate font
        ]

; R1 = flags for icon
; R2 -> string to paint
; R3 = handle for icon font

        LDR     R0, redrawhandle
        Trace   menuw, "icontext: painting icon for handle ", X, R0
        BIC     R0, R0, #3              ; get the real pointer
        LDR     R0, [R0, #w_taskhandle]
; R0 = handle of the task that owns this window (-1 for menus)

        Trace   menuw, "icontext: task handle ", X, R0
        ;We have to fix up the font string if the window is a menu, and not
        ;       writable.
        CMP     R0, #-1
        BNE     icontext_pushfontstring

        AND     R0, R1, #15 :SHL: 12
        TEQ     R0, #15 :SHL: 12
        TEQNE   R0, #14 :SHL: 12 ; test for both types of writable
        ;Invert Z flag
        MOVNE   R0, #0
        MOVEQ   R0, #1
        TEQ     R0, #0
icontext_pushfontstring

        Trace   menuw, "icontext: icon flags ", X, R1
        MOV     R0, R1
      [ CnP
        MOV     R7, #nullptr
      ]
        BL      pushfontstring
        BNE     icontext_dont_justify

        ;Get max index value for menu entry
        TST     R0, #if_indirected
        MOVNE   R3, #bignum
        MOVEQ   R3, #11

        AND     R14,R0,#if_esg
        CMP     R14,#13 :SHL: ib_esg    ; check for esg 13 - if it isn't (could be 12
        BEQ     icontext_dont_justify   ; for a non-writable menu icon) don't munge it

        [ outlinefont
        BL      fixupfontstring         ; only call for non-writable icons
        ]
        MOV     R2, #2_10001
        B       icontext_paint
icontext_dont_justify
        MOV     R2, #2_10000
icontext_paint
 [ BlendedFonts
        TST     R0, #if_filled
      [ RO4
	LDREQ	LR,ThreeDFlags
	TSTEQ	LR,#ThreeDFlags_NoFontBlending
      ]
        ORREQ   R2, R2, #1:SHL:11
 ]

;        LDR     R0,systemfont
;        TEQ     R0,#0
;        ORRNE   R2,R2,#2                ; rub out box required

        LDR     R0,writeabledir
        TEQ     R0,#0
        ORRNE   R2,R2,#1 :SHL: 10       ; write right to left

        ADD     R0, SP, #cx1*4
        ADD     R0, R0, R7
        LDMIA   R0, {R3-R4}             ; pick up cx1 and cy1

        Trace   menuw, "icontext: painting string ", S, R1
        ADD     R3,R3,#2                ; KJB - attempt to nobble it a bit
	ADDNE   R3,R3,#12               ; BJGA - attempt to fix right-to-left printing
        SWI     XFont_Paint             ; then paint the string

 [ BlendedFonts
        BVC     %FT10
        TST     R2, #1:SHL:11
        BICNE   R2, R2, #1:SHL:11
        SWINE   XFont_Paint
10
 ]

      [ outlinefont
        BLVS    LoseFont
        LDRVS   R0,systemfont
        ORRVS   R0,R0,#&80000000
        STRVS   R0,systemfont
      ]

        LDR     R0,tool_list            ; 320nk
        TEQ     R0,#0
        BLEQ    restore_tool_list       ; make sure there's something there
                                        ; just in case we're in the middle of
                                        ; plotting a title bar, for instance.


        ADD     SP,SP,R7                ; balance the stack for an easy life
94
        Pull    "R0-R7"
        B       %FT90

writedirnormal
        = 23,16,0,&FD,0,0,0,0,0,0

writedirnormal_len * .-writedirnormal

writedirreverse
        = 23,16,2,&FD,0,0,0,0,0,0
writedirreverse_len * .-writedirreverse

        ALIGN

95

; set up R4 = character to display as (if 0, display real string)

        Push    "R2"
        MOV     R4,#0                   ; no char to print as
        LDR     R3,validationstring
        AcceptLoosePointer_NegOrZero R3,-1
        CMP     R3,R3,ASR #31           ; R3 = 0/-1 ==> no validation string
        BEQ     %FT96
        MOV     R2,#WimpValidation_Display
        BL      findcommand
        LDREQB  R4,[R3],#1
        TEQEQ   R4,#"\\"
        LDREQB  R4,[R3]
96
        Pull    "R2"

        TST     R1,#if_indirected       ; limit 12 chars unless indirected
        MOVEQ   R3,#12
        MOVNE   R3,#bignum
wrcrlp
        SUBS    R3,R3,#1
        LDRCSB  R0,[R2],#1
        CMPCS   R0,#32
        BCC     %FT90
        CMP     R4,#32
        MOVCS   R0,R4                   ; use R4 if it's ok
        SWI     XOS_WriteC
        BVC     wrcrlp
90
        LDR     LR,writeabledir
        CMP     LR,#0
        BEQ     %FT94
;
        Push    "R0,R1"
        ADR     R0,writedirnormal
        MOV     R1,#writedirnormal_len
        SWI     XOS_WriteN              ; poke the write direction for the cursor
        Pull    "R0,R1"
94
      [ No32bitCode
        Push    "R0,x0,y0,x1,y1,PC"
      |
        MRS     R14,CPSR
        Push    "R0,x0,y0,x1,y1,R14"
      ]
;
 [ RO4
; MB FIX
; as we removed the other side of this removing this as well might be a good idea
;        LDR     R14,spaceinicon
;        CMP     R14,#6                  ; needs at least 3*dx space left
;        BPL     %FT01
; end MB FIX
 |
        LDR     R14,spaceinicon
        CMP     R14,#6                  ; needs at least 3*dx space left
        BPL     %FT01
 ]
;
 [ FullIconClipping
	LDR	r14,ThreeDFlags
	TST	r14,#ThreeDFlags_FullIconClipping
        ADREQ   R14,oldclipx0
        LDMEQIA R14,{x0,y0,x1,y1}
        BLEQ    graphicswindow
 |
        ADR     R14,oldclipx0
        LDMIA   R14,{x0,y0,x1,y1}
        BL      graphicswindow
 ]
01
        Pull    "R0,x0,y0,x1,y1,R14"
      [ No32bitCode
        TEQP    R14,#0                  ; restore PSR
      |
        MSR     CPSR_f,R14              ; restore PSR
      ]

        Pull    "R1-R4,PC"

plotcxy1
        Push    "R1,R2,LR"
        MOV     R1,cx1
        MOV     R2,cy1
        SWI     XOS_Plot
        Pull    "R1,R2,PC"              ; yeehhaaa!

; draw formatted text in icon
; Entry:  R1 = icon flags
;         R2 --> text string
;         x0,y0,x1,y1 = coordinates of surrounding box
;         [linespacing] = amount to leave between lines
; Exit:   R1-R4, PC pulled from stack

iconformatted TraceL L40
        ASSERT  x0=R6                   ; can't use R6..R9

        TraceK  L40, "iconformatted: flags "
        TraceX  L40, R1
        TraceK  L40, ", string "
        TraceS  L40, R2
        TraceNL L40

        Push    "R5-R6,R10"

        [       outlinefont
        ;Should we use a scalable font? This new code allows L40 icons to be
        ;       antialiased for the first time: previously, if_fancyfont was
        ;       ignored.
        TST     R1, #if_fancyfont
        MOVNE   R3, R1, LSR #ib_fontno ;get the font handle
        LDREQ   R3, systemfont
        TEQEQ   R3, #0
        BNE     iconformatted_fancy
        ]

; draw, or return info on, formatted system font text
; Entry:  R1 = icon flags
;         R2 -> text string
;         x0-y1 = icon bounding box
;         [linespacing] = distance between baselines
;         [linecount] <> 0 => just return number of lines required
; Exit:   if returning number,  R0-R5, R10, R14 corrupted, [linecount] contains line count, PC pulled from stack
;         else:                 R5, R6, R10, R1-R4, PC pulled from stack

iconformatted_system TraceL L40
        MOV     R0,R2
; R0 -> string
        MOV     R10,#0
; R10 = lines so far
        LDR     R14,dy
; R14 = ???
        MOV     R5,#0

iconformatted_skip_spaces
        LDRB    R14,[R0],#1
        TEQ     R14,#" "                ; skip leading spaces
        BEQ     iconformatted_skip_spaces
        SUB     R0,R0,#1

        MOV     R2,R0                   ; scan from here
; R2 -> string (ignoring leading spaces)
        MOV     R3,R2                   ; last split position
;
        SUB     R4,x1,x0
; R4 = width of icon in OSU
        MOV     R4,R4,ASR #4            ; divide by 16 (char width)
; R4 = width of icon in characters
        SUB     R4,R4,#2                ; margin of 1 char, and R4=nchars-1
        CMP     R4,#5
        MOVLT   R4,#5                   ; R4 = max no of chars - 1
; R4 = width of icon, minimum 5

        ;Get the length of the string
iconformatted_length
        LDRB    R14,[R0],#1
        CMP     R14,#" "                ; look for space or ctrl-char
        SUBLS   R3,R0,#1                ; R3 --> separator
        SUBLO   R0,R0,#1                ; don't pass terminator
        SUBCSS  R4,R4,#1                ; decrement counter
        BCS     iconformatted_length    ; stop on terminator/19 chars
; R3 -> terminator
        SUBS    R3,R3,R2
; R3 = length of string
        SUBEQS  R3,R0,R2                ; if no split char, do all so far
        ADD     R0,R2,R3                ; go back to split char (skip later)
        Push    "R2,R3",HI
        ADDHI   R10,R10,#1              ; no of lines to do
        LDRHI   R14,linespacing
        ADDHI   R5,R5,R14
        BHI     iconformatted_skip_spaces

      [ StretchErrorText
        LDR     R3, linecount
        TEQ     R3, #0
        STRNE   R10, linecount          ; store the number of lines required
        ADDNE   sp, sp, R10, LSL#3      ; skip line markers on stack
        Pull    "PC", NE
      ]

; R5 = height of text in OSU
; R10 = no of lines
; on stack are pairs [addr,size] of strings (last one on top)

        Push    "R5,R10"
;
        ADD     R3,x0,x1
        ADD     R2,y0,y1
; R3,R2 = coordinates of centre of icon * 2
        Pull    "R5,R6"

        LDR     R14,linespacing         ; last line is only 32 high, not
        SUB     R5,R5,R14               ; [linespacing] high (if you see
        ADD     R5,R5,#32               ; what I mean)

; R5 = height of string in OSU
; R6-> string to paint
        TEQ     R6,#0
        BEQ     iconformatted_system_done

        SUB     R2,R2,R5                ; R2 = y-coord of bottom line
        MOV     R2,R2,ASR #1
        MOV     R3,R3,ASR #1
        LDR     R14,dy_1
        ADD     R2,R2,#31
        SUB     R2,R2,R14
        BIC     R2,R2,R14               ; round R2 down to pixels
iconformatted_system_paint_line
        LDR     R0,[sp,#4]              ; length of next item
        SUB     R1,R3,R0,LSL #3
        MOV     R0,#4
        SWI     XOS_Plot
        Pull    "R0,R1"                 ; get ptr/length
        SWI     XOS_WriteN
        LDR     R14,linespacing
        ADD     R2,R2,R14
        SUBS    R6,R6,#1
        BNE     iconformatted_system_paint_line
iconformatted_system_done
        Pull    "R5-R6,R10"
        Pull    "R1-R4,PC"

        [       outlinefont
; draw, or return info on, formatted outline font text
; Entry:  R1 = icon flags
;         R2 -> string to paint
;         R3 = font handle
;         ((R6, R7), (R8, R9)) = icon bbox
;         [linespacing] = distance between baselines
;         [linecount] <> 0 => just return number of lines required
; Exit:   if returning number,  R0-R6, R10, R14 corrupted, [linecount] contains line count, PC pulled from stack
;         else:                 R5, R6, R10, R1-R4, PC pulled from stack

iconformatted_fancy TraceL L40

        TraceK  L40, "iconformatted: icon bbox is (("
        TraceD  L40, R6
        TraceK  L40, ", "
        TraceD  L40, R7
        TraceK  L40, "), ("
        TraceD  L40, R8
        TraceK  L40, ", "
        TraceD  L40, R9
        TraceK  L40, "))"
        TraceNL L40

        Push    "R7-R9,R11"

        ADD     R4, R7, R9
        MOV     R4, R4, ASR #1
; R4 = y-coord of icon centre

        SUB     R5, R9, R7
; R5 = total height of icon (OSU)

        Trace   L40, "iconformatted: icon height ", D, R5

        BL      remembercurrentfont
        BL      setfancyfontcolours
      [ CnP
        MOV     R7, #nullptr
      ]
        BL      pushfontstring

        MOV     R0,R3
        SWI     XFont_SetFont
; unfortunately, because we plot a line at a time the fact that pushfontstring
; has prepended the font handle, for lines 2... this is somewhat useless if the
; app has been using the font manager!

        MOV     R0, R1
        MOV     R10, R7
; R0 -> string to paint
; R10 = difference in stack

        SUB     R1, R8, R6
; R1 = width available in icon (OSU)

        ADD     R8, R6, R8
        MOV     R8, R8, ASR #1
; R8 = x-coord of icon centre

        MOV     R6, R9
; R6 = depth so far

        Trace   L40, "iconformatted: initial icon top ", D, R6

        SUBS    R1, R1, #16
        MOVLE   R1, #0

        SWI     XFont_Converttopoints
        MOV     R9, R1
; R9 = width available in icon (mpt)

        MOV     R1, R0
; R1 -> string

        MOV     R11, #0
; R11 = indicator for pass number

iconformatted_fancy_next_pass
        ;On the first pass, save R1, R5 on the stack; on the second, get
        ;       them back
        TEQ     R11, #0
        Push    "R1, R5", EQ
        Pull    "R1, R5", NE

        BEQ     iconformatted_fancy_first_pass
; R11 = number of lines formatted

      [ StretchErrorText
        LDR     R0, linecount
        TEQ     R0, #0
        BEQ     %FT06
        STR     R11, linecount
        ADD     sp, sp, R10         ; balance stack after pushfontstring
        Pull    "R7-R9, R11, PC"    ; exit with number of lines required in [linecount]
06
      ]

        LDR     R0, linespacing
        MUL     R11, R0, R11

        SUB     R11, R11, R0            ; again, the last line has height 32,
        ADD     R11, R11, #32           ; not [linespacing]

; R11 = total height of lines required
        Trace   L40, "iconformatted: height of lines ", D, R11
        Trace   L40, "iconformatted: icon height ", D, R5

      [ false
        CMP     R11, R5                 ; KJB: This isn't done in the system
        MOVGT   R11, R5                 ; font case!
      ]
; R11 = total height available for lines
        Trace   L40, "iconformatted: height available ", D, R11

        SUB     R11, R5, R11
; R11 = space in icon after lines are printed (>= 0)
        Trace   L40, "iconformatted: => total gap ", D, R11

        Trace   L40, "iconformatted: old icon top ", D, R6

        ;Work out the position of the first line to paint
        SUB     R6, R6, R11, ASR #1

        SUB     R6, R6, #28
        LDR     R14, dy_1
        BIC     R6, R6, R14

        Trace   L40, "iconformatted: new icon top ", D, R6

        MOV     R11, #-1
iconformatted_fancy_first_pass

        B       iconformatted_fancy_reinit

iconformatted_fancy_skip_spaces
        ADD     R1, R1, #1
iconformatted_fancy_paint_lines
        LDRB    R0, [R1, #0]
        CMP     R0, #" "
        BEQ     iconformatted_fancy_skip_spaces

        CMP     R0,#0
        MOVEQ   R5,#0                           ; NK, added to cope with spaces at end of string
        BEQ     iconformatted_fancy_next

        MOV     R0, R1
; R0 -> string

        ;Scan string to the required length
        MOV     R2, R9
        MOV     R3, #bignum
        MOV     R4, #" "
        MOV     R5, #bignum
        TraceK  L40, "iconformatted: Font_StringWidth (first time)"
        TraceNL L40
        SWI     XFont_StringWidth
; R1 corrupted
; R2 = x-offset up to termination (mpt)
; R5 = length that will fit

        ;There is the ghastly possibility that R5 = 0 (probably because
        ;       there are no spaces in the string). If so, try again with no
        ;       split character.
        TEQ     R5, #0
        BNE     iconformatted_width_ok
        MOV     R1, R0
        MOV     R2, R9
        MOV     R3, #bignum
        MOV     R4, #-1
        MOV     R5, #bignum
        TraceK  L40, "iconformatted: Font_StringWidth (second time)"
        TraceNL L40
        SWI     XFont_StringWidth
; R0 corrupted
; R2 = x-offset up to termination (mpt)
; R5 = length that will fit

        ;In fact, there is STILL the ghastly possibility that R5 = 0 - now,
        ;       it's presumably because the characters are just TOO BIG!!
        ;       Deal with this by setting R5 to 1 and R2 to the width of the
        ;       icon.
        TEQ     R5, #0
        BNE     iconformatted_width_ok
        TraceK  L40, "iconformatted: Font_StringWidth given up"
        TraceNL L40
        MOV     R5, #1
        MOV     R2, R9
iconformatted_width_ok
; R1 corrupted
; R2 = x-offset up to termination (mpt)
; R5 = length that will fit

        SWI     XFont_ConverttoOS
        MOV     R1, R0 ;put R1 back

        TraceK  L40, "iconformatted: Font_StringWidth ("
        TraceS  L40, R1
        TraceK  L40, ") -> width "
        TraceD  L40, R2
        TraceK  L40, " OSU, length "
        TraceD  L40, R5
        TraceNL L40

        ;Work out where to paint the string
        ;Count on the first pass; paint on the second.
        CMP     R11, #-1
        ADDNE   R11, R11, #1
        BNE     iconformatted_fancy_next
        MOV     R7, R5
        LDR     R14, linespacing
        MOV     R4, R6
        SUB     R6, R6, R14             ; y -:= linespacing
        SUB     R3, R8, R2, ASR #1      ; x := centre - x/2
 [ BlendedFonts
      [ RO4
        MOV     R2, #1 :SHL: 4 :OR: 1 :SHL: 7
	LDR	LR,ThreeDFlags
	TST	LR,#ThreeDFlags_NoFontBlending
	ORREQ	R2,R2,#1:SHL:11
      |
        MOV     R2, #1 :SHL: 4 :OR: 1 :SHL: 7 :OR: 1 :SHL: 11 ; as below + blend bit
      ]
 |
        MOV     R2, #1 :SHL: 4 :OR: 1 :SHL: 7 ;Font_OS_Units | Font_GivenLength
 ]
        ;Push    "R6"
        ;ADD     R6,R6,#32                ; system text is top-left, font paint bottom-left
        SWI     XFont_Paint

 [ BlendedFonts
      [ RO4
	BVC	%FT01
	TST 	R2,#1:SHL:11
        BICEQ   R2, R2, #1:SHL:11
        SWIEQ   XFont_Paint
01
      |
        BICVS   R2, R2, #1:SHL:11
        SWIVS   XFont_Paint
      ]
 ]

        BLVS    LoseFont
        LDRVS   R0,systemfont
        ORRVS   R0,R0,#&80000000
        STRVS   R0,systemfont

        ;Pull    "R6"

        LDR     R0,tool_list            ; 320nk
        TEQ     R0,#0
        BLEQ    restore_tool_list       ; make sure there's something there
                                        ; just in case we're in the middle of
                                        ; plotting a title bar, for instance.
iconformatted_fancy_next

        ADD     R1, R1, R5 ;output from Font_StringWidth
iconformatted_fancy_reinit
        LDRB    R0, [R1, #0]
        TEQ     R0, #:CHR: 0
        BNE     iconformatted_fancy_paint_lines

        Trace   L40, "iconformatted: lines of text to format ", D, R11

        ;Loop to do the painting on the first pass; continue on the second
        CMP     R11, #-1
        BEQ     %FT99           ;320nk
        CMP     R11, #0
        BNE     iconformatted_fancy_next_pass
        MOV     R11, #-1
        B       iconformatted_fancy_next_pass

99
        ADD     SP, SP, R10 ;balance stack after pushfontstring

        Pull    "R7-R9,R11"
        Pull    "R5-R6,R10"
        Pull    "R1-R4,PC"
        ]


; Find origin of text (depends on flags)
; Entry:  R1,R2 = flag word/data ptr
;         x0,y0,x1,y1 = bounding box of icon
;         [hascaret] ==> does the caret have to be considered?
; Exit :  cx1,cy1 = coords of LHS of text
;
; Sprite+Text case:
; -----------------
; Vertical:
;             V ==> both vertically centred             <== as before
;          HR~V ==> text at top, sprite at bottom
;            ~V ==> text at bottom, sprite at top
; Horizontal:
;       ~V ~H~R ==> both at left                        <== as before
;       ~V ~H R ==> both at right                       <== as before
;       ~V  H   ==> both centred                        <== as before
;        V ~H~R ==> sprite at left, text +6 units to right (LJ)
;        V  H~R ==> both centred (text visible)         <== as before
;        V ~H R ==> text at left, sprite at right
;        V  H R ==> sprite at left, text at right (RJ)
;

L       *       1:SHL:0                 ; only 1 of L,R,H set
R       *       1:SHL:1
H       *       1:SHL:2
T       *       1:SHL:3                 ; only 1 of T,B,M set
B       *       1:SHL:4
V       *       1:SHL:5
X       *       1:SHL:6                 ; text is just to the right of sprite

text_lookup
        DCB     L :OR: V                ; ~V ~H ~R ~S
        DCB     L :OR: B  ;             ; ~V ~H ~R  S    <-- new
        DCB     R :OR: V                ; ~V ~H  R ~S
        DCB     R :OR: B  ;             ; ~V ~H  R  S    <--
        DCB     H :OR: V                ; ~V  H ~R ~S
        DCB     H :OR: B  ;             ; ~V  H ~R  S    <--
        DCB     H :OR: V                ; ~V  H  R ~S
        DCB     H :OR: T  ;             ; ~V  H  R  S    <--
        DCB     L :OR: V                ;  V ~H ~R ~S
        DCB     X :OR: V  ;             ;  V ~H ~R  S    <--
        DCB     R :OR: V                ;  V ~H  R ~S
        DCB     L :OR: V  ;             ;  V ~H  R  S    <--
        DCB     H :OR: V                ;  V  H ~R ~S
        DCB     H :OR: V  ;             ;  V  H ~R  S    <--
        DCB     H :OR: V                ;  V  H  R ~S
        DCB     R :OR: V  ;             ;  V  H  R  S    <--
        ASSERT  (.-text_lookup)=16

findtextorigin
        Push    "cx0,cy0,LR"
;
        BL      textwidth               ; cx1 <- width of text (& select font)
        Pull    "cx0,cy0,PC",VS
        STR     cx1,text_width
        SUB     R14,x1,x0
        SUB     R14,R14,cx1             ; is this too long?
        STR     R14,spaceinicon         ; used when displaying the icon
        CMP     r14,#0
        BGE     %FT01
        TST     R1,#if_sprite             ; sprite?
        BNE     %FT01
        TST     R1,#if_hcentred
        ORRNE   R1,R1,#if_rjustify
        BICNE   R1,R1,#if_hcentred
01
;
        ASSERT  if_sprite   = 2_00010
        ASSERT  if_border   = 2_00100
        ASSERT  if_hcentred = 2_01000
        ASSERT  if_vcentred = 2_10000
;
        BIC     R14,R1,#if_border       ; move rjustify bit into range
        TST     R1,#if_rjustify
        ORRNE   R14,R14,#if_border
        AND     R14,R14,#2_11110        ; mask out 4 bits
        ADR     R0,text_lookup
        LDRB    R0,[R0,R14,LSR #1]      ; R0 = flag byte
;
        TST     R0,#X
        BEQ     %FT01
        ASSERT  cy0 = R3                ; no need to stash this
        ASSERT  cx1 = R4
        BL      spritesize              ; R3,R4 = sprite size (R0 preserved)
        ADD     cx1,x0,R3
        ADD     cx1,cx1,#6              ; left-justified, just after sprite
        B       %FT02
01
        TST     R0,#L
        ADDNE   cx1,x0,#6               ; cx1 <- x0 + 3*dx
        TST     R0,#H
        SUBNE   cx1,x1,cx1
        SUBNE   cx1,cx1,x0              ; width of box - width of text
        ADDNE   cx1,x0,cx1,ASR #1
        TST     R0,#R
        SUBNE   cx1,x1,cx1
        SUBNE   cx1,cx1,#6              ; cx1 <- cx1 - 3*dx
02
        Push    cx1
        BL      textheight              ; cy0,cy1 = top/bottom of text (rel)
        Pull    cx1
        STR     cy0,text_y0
        STR     cy1,text_y1
;                                       ; R0 preserved
        TST     R0,#T
        SUBNE   cy1,y1,cy1              ; origin = y1 - cy1 (at top)
        TST     R0,#V
        ADDNE   cy1,cy1,cy0             ; cy1 = 2*midpoint of text
        ADDNE   R14,y0,y1               ; R14 = 2*midpoint of icon
        SUBNE   R14,R14,cy1
        MOVNE   cy1,R14,ASR #1          ; now cx1,cy1 = origin of text
        TST     R0,#B
        SUBNE   cy1,y0,cy0              ; origin = y0 - cy0 (at bottom)

; now for fancy system font get baseline to that of ordinary system font (4 OS)
        [ {FALSE}
        LDR     R14,systemfont
        TEQ     R14,#0
        ADDNE   cy1,cy1,#2
        ADDNE   cy1,cy1,cy0, ASR #1
        ]
01
        Pull    "cx0,cy0"
;
; now decide whether we must move the origin to incorporate the caret
;
        LDR     R14,hascaret            ; 0 ==> this icon has the caret
        TEQ     R14,#0
        Pull    "PC",NE                 ; not in this icon!
;
; bestx = (x0+x1)/2 - caretx
; x1 = x0 - 3*dx + [spaceinicon]
; x0 = x0 + 3*dx
; if bestx > cx1
; then if cx1 < x0 then cx1 = min(x0,bestx)
; else if cx1 > x1 then cx1 = max(x1,bestx)
;
        Push    "R1,cx1,x0,x1"
;
        LDR     R1,caretx               ; offset of caret from text origin
;
        Debug   sc,"caretx, x0, x1, cx1=",R1,x0,x1,cx1
;
        ADD     R14,x0,x1
        RSB     R1,R1,R14,ASR #1        ; R1 = (x0+x1)/2 - caretx
;
        SUB     x1,x0,#6
        ADD     x0,x0,#6                ; include margins
        LDR     R14,spaceinicon
        ADD     x1,x1,R14
;
        CMP     R1,cx1
        BLE     %FT01                   ; if bestx > cx1 then
;                                       ; {
        CMP     cx1,x0                  ;   if cx1 < x0 then
        MOVLT   cx1,x0                  ;   { cx1 = x0
        CMPLT   R1,cx1                  ;     if bestx<x0 then cx1 = bestx
        MOVLT   cx1,R1                  ;   }
        B       %FT02                   ; }
01                                      ; else
        CMP     cx1,x1                  ; { if cx1 > x1 then
        MOVGT   cx1,x1                  ;   { cx1 = x1
        CMPGT   R1,cx1                  ;     if bestx>x1 then cx1 = bestx
        MOVGT   cx1,R1                  ;   }
02                                      ; }
        Pull    "R1,R14"                ; R14 = old offset before caret bits

        SUB     R14,cx1,R14
        STR     R14,caretscrollx        ; offset due to presence of caret
;
        Debug   sc,"caretscrollx =",R14
;
        Pull    "x0,x1,PC"

;
; Scan text width
; Entry:  R1,R2 = flags, data ptr
; Exit :  cx1 = width of text (OS coords)
;

textwidth TraceL font
        Push    "LR"

        [       outlinefont
        LDR     R14,systemfont
        TEQ     R14,#0
        TSTEQ   R1,#if_fancyfont
        |
        TST     R1,#if_fancyfont
        ]
        BEQ     getordwidth

        [       outlinefont
        LDR     R0, currentfont
        TEQ     R0, #0
        BNE     textwidth_current_font_known
        Push    "R0-R3"
        SWI     XFont_CurrentFont
        ;save the current font handle in a safe place
        STRVC   R0, currentfont
        STRVC   R1, currentbg
        STRVC   R2, currentfg
        STRVC   R3, currentoffset
        ;if V is set, everything is going to slow down horribly (calling
        ;       Font_CurrentFont on every call to textwidth), but it will
        ;       still work

        Trace   font, "textwidth: current font is ", X, R0
; NK 349
        MOV     R0,#0
        MOV     R1,#0
        SWI     XFont_SwitchOutputToBuffer
        CLRV

; make sure we're on the screen!
        Pull    "R0-R3"
textwidth_current_font_known
        ]

        ASSERT  (cx1=R4)
        Push    "R1-R3,R5-R7"

        [       outlinefont
        TST     R1, #if_fancyfont
        LDREQ   R3, systemfont
        MOVNE   R3, R1, LSR #ib_fontno  ; get the correct font handle
        |
        MOV     R3, R1, LSR #ib_fontno  ; select appropriate font
        ]

; R3 = font handle to use for text
        Push    "R3"
        STR     R1,tempworkspace+28
      [ CnP
        MOV     R7, #nullptr
      ]
        BL      pushfontstring
        LDR     R0,[SP,R7]              ; stack has moved!!!
; R1 -> string

;320nk  redone this bit to use scanstring and set CORRECT height at same time
        [ {FALSE}
        MOV     R2, #0
        MOV     R3, #bignum
        MOV     R4, #bignum
        MOV     R5, #bignum
        SWI     XFont_ScanString
        ]
;        SWI     XFont_StringBBox       ; get width and height
        BL      my_StringBBox           ; correct screen BBox
                                        ; unlike Font_StringBBox!!
        ADD     SP, SP, R7              ; balance the stack for a happy life
	ADD	SP, SP, #4

        Pull    "R1-R3,R5-R7,PC",VS
;        SUB     R1,R3,R1
;        SUB     R2,R4,R2
;        SWI     XFont_ConverttoOS      ; already in OS
        LDRVC   R14,dx
        ADDVC   cx1,R1,R14 , LSL #1             ; cx1 = offset in OS coords
        STRVC   R2,temp_text_height
        TraceK  font, "textwidth done"
        TraceNL font
        Pull    "R1-R3,R5-R7,PC"

;;===================================================================
;; my_StringBBox        returns width and height of a string
;; Entry R1 points to string
;; Exit R1 width of string in OS units, R2 height above baseline
;;===================================================================

my_StringBBox
; Let's do the job properly this time:
;  * Font_ScanString *can* be used to return the width instead of the bounding box
;  * if_fancyfont icons might as well be measured the same way as the desktop font
;  * icons with text above the sprite don't need the height trimming down

        Entry   "R1,R7,R8"
        MOV     R2, #0
        MOV     R3, #bignum
        MOV     R4, #bignum
        SWI     XFont_ScanString        ; pushfontstring always puts font-selection control at start, so R0 not needed
        MOV     R1, R3                  ; x offset to end of string
        MOV     R2, #0                  ; dummy value
        SWI     XFont_ConverttoOS       ; results are already pixel-aligned

        LDR     R3, tempworkspace+28    ; retrieve icon flags
        EOR     R3, R3, #if_rjustify :OR: if_vcentred :OR: if_hcentred  ; flip bits to make comparisons easier
        TST     R3, #if_sprite
        TSTNE   R3, #if_vcentred
        TSTNE   R3, #if_rjustify :OR: if_hcentred
        LDREQ   R2, systemfonty1        ; unless we've got a text+sprite icon with text below sprite, use full system font height

        LDRNE   R5, [sp]                ; get original R1 (if we're dropping through)
        STR     R1, [sp]                ; store width where it will be reloaded into R1
        BNE     %FT00
        CLRV
        EXIT
00
        ; Now we've still got to find the height using a Font_CharBBox loop, due to rounding errors in the
        ; Font Manager's string bounding box code (PRM 3-463). However, we can make some simplifications, based upon
        ; knowledge of what pushfontstring will have given us (ie only printable characters and font/colour changes)

        MOV     R7, #0                  ; cumulative maximum (starts at 0, so we'll always go up to the baseline at least)
      [ UTF8
        LDRB    R8, alphabet            ; set up for us by pushfontstring
01      TEQ     R8, #ISOAlphabet_UTF8
        LDRNEB  R1, [R5], #1
        MOVEQ   R4, #6
        MOVEQ   R6, R5
        BLEQ    convert_UTF8_to_UCS4    ; may return -1 for malformed characters
        MOVEQ   R1, R6
        ADDEQ   R5, R5, R4
        CMP     R1, #-1                 ; malformed character?
        LDREQ   R1, =&FFFD              ; use special glyph for the purpose
      |
01      LDRB    R1, [R5], #1
      ]
        TEQ     R1, #0                  ; pushfontstring always null-terminates
        BEQ     %FT02
        TEQ     R1, #26                 ; font change?
        LDREQB  R0, [R5], #1            ; set R0 to hold font handle
        BEQ     %BT01
      [ CnP
        TEQ     R1, #19                 ; colour change?
        ADDEQ   R5, R5, #8-1            ; skip
        BEQ     %BT01
      ]

        MOV     R2, #1:SHL:4            ; return OS units
        SWI     XFont_CharBBox
        LDRVS   R4, systemfonty1
        CMP     R4, R7                  ; compare top of bbox with cumulative maximum
        MOVGT   R7, R4
        B       %BT01

02      MOV     R2, R7
        CLRV
        EXIT

        LTORG



getordwidth
        Push    "R2"
        MOV     cx1,#0
        LDR     R14,dx
getllp  LDRB    R0,[R2],#1
        CMP     R0,#32
        ADDCS   cx1,cx1,#16             ; constant character width
        BCS     getllp
;
        Pull    "R2,PC"

;
; Entry:  R1 = flag word for icon
; Exit:   cy0,cy1 = bottom/top of text (rel. to MOVE command)
;

        ASSERT  cy0=R3
        ASSERT  cy1=R5

textheight TraceL font
        Push    "R0-R2,R4,LR"
;
      [ outlinefont
        LDR     R14,systemfont
        TEQ     R14,#0
        TSTEQ   R1,#if_fancyfont
      |
        TST     R1,#if_fancyfont
      ]
        LDREQ   cy1,dy                  ; (upper origin is exclusive)
        SUBEQ   cy0,cy1,#32
        STREQ   cy1,temp_text_height
        Pull    "R0-R2,R4,PC",EQ
;
      [ outlinefont
        TST     R1,#if_fancyfont
        LDREQ   cy1,systemfonty1
        LDREQ   cy0,systemfonty0
        Pull    "R0-R2,R4,PC",EQ
      ]
        MOV     R0,R1,LSR #ib_fontno
        Trace   font, "textheight: reading info for font ", X, R0

        SWI     XFont_ReadInfo
        MOVVC   cy1,R4
        MOVVC   cy0,R2

        CLRV
        Pull    "R0-R2,R4,PC"           ; R0 preserved for icontext

;
; First calculate sprite origin
; (see findtextorigin for details of flag settings)
; Entry:  R1,R2 = sprite flags, data ptr
; Exit:   [spriteX], [spriteY] remembered
;

sprite_lookup
        DCB     L :OR: B                ; ~V ~H ~R ~T
        DCB     L :OR: T  ;             ; ~V ~H ~R  T    <-- new
        DCB     R :OR: B                ; ~V ~H  R ~T
        DCB     R :OR: T  ;             ; ~V ~H  R  T    <--
        DCB     H :OR: B                ; ~V  H ~R ~T
        DCB     H :OR: T  ;             ; ~V  H ~R  T    <--
        DCB     H :OR: B                ; ~V  H  R ~T
        DCB     H :OR: B  ;             ; ~V  H  R  T    <--
        DCB     L :OR: V                ;  V ~H ~R ~T
        DCB     L :OR: V  ;             ;  V ~H ~R  T    <--
        DCB     R :OR: V                ;  V ~H  R ~T
        DCB     R :OR: V  ;             ;  V ~H  R  T    <--
        DCB     H :OR: V                ;  V  H ~R ~T
        DCB     H :OR: V  ;             ;  V  H ~R  T    <--
        DCB     H :OR: V                ;  V  H  R ~T
        DCB     L :OR: V  ;             ;  V  H  R  T    <--
        ASSERT  (.-sprite_lookup)=16

iconsprite
        Push    "R0-R5,LR"
;
        BL      spritesize              ; R3,R4 = size of sprite (OS units)
;
        ASSERT  if_sprite   = 2_00010
        ASSERT  if_border   = 2_00100
        ASSERT  if_hcentred = 2_01000
        ASSERT  if_vcentred = 2_10000
;

        BIC     R14,R1,#if_border:OR:if_sprite
        TST     R1,#if_rjustify
        ORRNE   R14,R14,#if_border
        TST     R1,#if_text
        ORRNE   R14,R14,#if_sprite
        AND     R14,R14,#2_11110        ; mask out 4 bits
        ADR     R0,sprite_lookup
        LDRB    R0,[R0,R14,LSR #1]      ; R0 = flag byte
;
        LDR     R5,writeabledir
        TEQ     R5,#0
        BNE     %FT01
        TST     R0,#L
        B       %FT02
01
        TST     R0,#R
02
        MOVNE   R3,x0                   ; left-justified
        TST     R0,#H
        ADDNE   R14,x0,x1               ; x coord = (x0+x1-width)/2
        SUBNE   R14,R14,R3
        MOVNE   R3,R14,ASR #1           ; centred
        LDR     R5,writeabledir
        TEQ     R5,#0
        BNE     %FT01
        TST     R0,#R
        B       %FT02
01
        TST     R0,#L
02
        SUBNE   R3,x1,R3                ; right-justified
;
        TST     R0,#T
        SUBNE   R4,y1,R4                ; sprite at top
        TST     R0,#V
        ADDNE   R14,y0,y1               ; y coord = (y0+y1-height)/2
        SUBNE   R14,R14,R4
        MOVNE   R4,R14,ASR #1           ; R4 = origin of sprite
        TST     R0,#B
        MOVNE   R4,y0                   ; not centred ==> at bottom
;
        STR     R3,spriteX              ; may be needed for inverting/shading
        STR     R4,spriteY
;
      [ TrueIcon2
        LDR     R5, border_type
        TEQ     R5, #border_action
        TEQNE   R5, #border_defaultaction
        BICEQ   R1, R1, #is_inverted    ; never invert sprites in action buttons
      ]

  [ PlotSpritesFromPalette
        MOV     R5,#8+16                ; plot sprite with mask using palette
  |
        MOV     R5,#8                   ; plot sprite with mask
  ]
        BL      wimp_SpriteOp_putsprite ; uses [spritename] and R1 (flags)
exitspricon
        STRVS   R0,[sp]
        Pull    "R0-R5,PC"

;
; Entry:  R1 = icon flags
;         [spritename] --> sprite name (set up by seticonptrs)
; Exit:   R3,R4 = sprite size (OS units)
;

spritesize
        Push    "R0,LR"
        BL      cachespritedata

 [ RO4 :LOR: true
	MOVVS	r3,#0			; if the call failed then the sprite probably
	MOVVS	r4,#0			; doesn't exist so return 0 for width and height
	BVS     %FT01
 ]

        LDR     R14,sprite_log2px
        MOV     R3,R3,LSL R14
        LDR     R14,sprite_log2py
        MOV     R4,R4,LSL R14           ; exit: R3,R4 = sprite size (OS units)
        TST     R1,#if_halfsize
        MOVNE   R3,R3,ASR #1
        MOVNE   R4,R4,ASR #1
01
        CLRV
        Pull    "R0,PC"                 ; must preserve R0 for icontext code


; nb: foreground colour should be restored!

; in    x0-y1 contain the bounding box of the area to plot around
; out   -

iconborder
        LDR     R0,border_type
        Debug   borders,"border type to plot =>",R0
        CMP     R0,#border_max          ; is it still valid?
        ADDCC   PC,PC,R0,LSL #2         ; if so then despatch,
        B       hollowrectangle         ; otherwise use default single pixel jobbie

        B       hollowrectangle
        B       plot_slabout
        B       plot_slabin
        B       plot_ridge
        B       plot_channel
        B       plot_action
        B       plot_default
        B       plot_editable

iconfilled
        B       solidrectangle          ; fill the background of the icon

 [ ThreeDPatch
iconfilledCheckMenu
	TST	r1,#is_inverted		; if it's inverted
	BNE	solidrectangle

	LDR	r0,ThreeDFlags
	TST	r0,#ThreeDFlags_TexturedMenus
	BEQ	solidrectangle

	LDR	r0,redrawhandle
	CMP	r0,#nullptr
	BEQ	solidrectangle
	Abs	r0,r0
	LDR	r0,[r0,#w_taskhandle]
	CMP	r0,#-1			; if it's not a menu
	BNE	solidrectangle

	AND	r0,r1,#&F0000
	TEQ	r0,#&D0000		; writable icons have esg set to &D
	BEQ	%FT01
	TEQ	r0,#&C0000		; the main menu item has ESG 12
	BNE	solidrectangle

      [ true ; see argument in createsubmenu
        TST     r1,#if_sprite
      | ; 4.02 code
	TST	r1,#if_text
	TSTNE	r1,#if_sprite		; if it's not a text+sprite icon
      ]
	BEQ	solidrectangle
01
	Push	"cx0-cy1,handle,lr"
	Push	"x0-y1"
	Pull	"cx0-cy1"
	LDR	handle,redrawhandle
	Abs	handle,handle
	BL	ploticonbackgroundsprite
	Pull	"cx0-cy1,handle,pc"
 ]

;;----------------------------------------------------------------------------
;; Wimp_ReadPixTrans
;; call to allow user programs to find out what translation table to use
;; Entry:  R0 = 0 / &100 / &200 ==> system / name ptr / sprite ptr
;;         R1 --> sprite area (if R0 > 0)
;;         R2 --> sprite name (if R0 < &200), else sprite ptr
;;         R6 --> 4-word table to contain factors (0 ==> don't do it)
;;         R7 --> 4-word table to contain pixel translation table (0==>don't)
;; Exit:   [R6] contains the scale factors (4 words)
;;         [R7] contains the pixel translation table (16 bytes, word-aligned)
;;
;; This call should not be used in any mode > 8BPP, nor should it be called
;; for sprites which have a depth > 4BPP as it will not return a sensibly sized
;; table - you have been warned!
;;----------------------------------------------------------------------------

SWIWimp_ReadPixTrans

        MyEntry "ReadPixTrans"

        CMP     R0,#&100
        MOVLO   R1,#0                   ; use system area if R0 < &100
        MOV     R14,#0
        CMP     R0,#&200
        MOVLO   R14,#1                  ; lengthflags ==> is R2 a name/sprite?
        STR     R14,lengthflags
        STR     R1,thisCBptr            ; used by cachespritedata
        STR     R2,spritename           ; used by cachespritedata
        BL      cachespritedata
        BVS     ExitWimp
;
        CMP     R6,#0
        ADRNE   R14,sprite_factors
        LDMNEIA R14,{R1-R4}
        STMNEIA R6,{R1-R4}              ; copy factors out
;
        CMP     R7,#0
        LDRNE   R14,pixtable_at
        CMPNE   R14,#0
        LDMNEIA R14,{R1-R4}
        STMNEIA R7,{R1-R4}              ; copy pixtrans out (NB word-aligned)
;
        B       ExitWimp

;;----------------------------------------------------------------------------
;; Rationalise sprite scaling factors
;; Entry:  R0,R1  sprite's eigx/eigy
;;         R5,R6  mode's eigx/eigy
;;         R2 --> where to store the factors
;; Exit:   R0,R1,R5,R6 corrupted
;;         [R2]   factors stored (xmul,ymul,xdiv,ydiv)
;;----------------------------------------------------------------------------
rationalisefactors
        Entry
        MOV     R14,#1

        ; all factors are ?:1 or 1:? so make R0:R5, R1:R6
        ; into this form
        CMP     R0,R5
        MOVEQ   R0,#1
        MOVEQ   R5,#1

        MOVLT   R5,R14,LSL R5
        MOVLT   R5,R5, LSR R0
        MOVLT   R0,#1

        MOVGT   R0,R14,LSL R0
        MOVGT   R0,R0, LSR R5
        MOVGT   R5,#1

        CMP     R1,R6
        MOVEQ   R1,#1
        MOVEQ   R6,#1

        MOVLT   R6,R14,LSL R6
        MOVLT   R6,R6, LSR R1
        MOVLT   R1,#1

        MOVGT   R1,R14,LSL R1
        MOVGT   R1,R1, LSR R6
        MOVGT   R6,#1

        STMIA   R2,{R0-R1,R5-R6}       ; store the scaling block
        EXIT

;;----------------------------------------------------------------------------
;; Mode-independent sprite code
;; Entry:  [spritename] --> sprite name
;;         [thisCBptr] --> sprite area
;;         [lengthflags] ==> is R2 a name ptr or a sprite ptr?
;; Exit:   R3,R4 = sprite size (pixels)
;;         mode data set up if different input mode from last time
;;----------------------------------------------------------------------------

cachespritedata
        Entry   "R1-R2,R5-R7"

        MOV     R2,#1
        STRB    R2,selecttable_crit     ; entering critical period for pixtable

        LDR     R2,lengthflags
        CMP     R2,#0                   ; absolute pointer?
        LDR     R2,spritename
        BLNE    cachespriteaddress
        MOVVC   R0,#&200+SpriteReason_ReadSpriteSize
        SWIVC   XOS_SpriteOp            ; read information about the sprite
        BVS     %FT92
;
; R2 -> sprite in area
;
        LDR     R7,[R2,#spImage]
        LDR     R14,[R2,#spTrans]
        CMP     R7,R14                  ; min(image,trans)
        MOVHI   R7,R14

        LDR     R14,pixtable_at
        CMP     R14,#0                  ; is a pixtable defined?
        BEQ     %FT10

        TEQ     R7,#spPalette           ; does it have a palette?
        LDREQ   R14,sprite_lastmode
        TEQEQ   R6,R14                  ; no, so have we already cached this information?
        BEQ     %FT92

10      MOV     R0,R6                   ; R0 = mode of sprite

        TEQ     R7,#spPalette
        MOVNE   R6,#-1                  ; if it has a palette then corrupt last mode
        STR     R6,sprite_lastmode

        Push    "R2,R3-R4"              ; R2 -> sprite, R3,R4 = height / width

        MOV     R1,#VduExt_Log2BPP
        SWI     XOS_ReadModeVariable
        STRVC   R2,sprite_log2bpp       ; get the depth of the sprite

        MOVVC   R1,#VduExt_XEigFactor
        SWIVC   XOS_ReadModeVariable
        STRVC   R2,sprite_log2px        ; get the X scaling factor (pixels => OS units)

        MOVVC   R1,#VduExt_YEigFactor
        SWIVC   XOS_ReadModeVariable
        STRVC   R2,sprite_log2py        ; get the Y scaling factor (pixels => OS units)
        Pull    "R2,R3-R4",VS
        BVS     %FT92

        LDR     R1,sprite_log2bpp
        LDR     R6,log2bpp
        TEQ     R1,R6                   ; any special translating?
        LDR     R0,sprite_log2px
        LDR     R5,log2px
        TEQEQ   R0,R5
        LDR     R1,sprite_log2py
        LDR     R6,log2py
        TEQEQ   R1,R6                   ; any special scaling?
        MOVEQ   R14,#0
        MOVNE   R14,#-1
        STRB    R14,sprite_needsfactors ; may be modified later

        ADR     r2,sprite_factors
        BL      rationalisefactors

        Pull    "R1"                    ; R1 -> sprite
        MOV     R2,#-1
        MOV     R3,#-1                  ; convert to the current mode
      [ Medusa
        MOV     R5,#(1:SHL:0) :OR: 1:SHL:1 ; Use mode's palette if sprite has none
      |
        MOV     R5,#(1:SHL:0)
      ]
;
; we must now attempt to cope with the dilemma of sorting out the correct
; mapping table for the sprite.
;
; in earlier versions of the Window Manager it made no attempt to cope
; with different depth sprites other than 1,2 or 4BPP so we will break
; new ground and go boldly where no larma has been parping before.
;
        LDR     R14,sprite_log2bpp

        Debug   ic,"Sprite bpp, palette",R14,R7

        CMP     R14,#3                  ; is it 8,16 or 32BPP?
        CMPLT   R7,#(spPalette+4)       ; or does it have a palette?
        BLT     %FT02

        LDR     R0,baseofromsprites     ; might have 8+ bpp sprites in ROM now
        LDR     R4,[R0,#saEnd]
        ADD     R4,R0,R4                ; R4 -> end of ROM sprite area
        CMP     R1,R0
        CMPHS   R4,R1                   ; is sprite in ROM?
        LDRLO   R0,baseofsprites        ; no, use RAM sprite area      ; 320nk Medusa fix
        B       %FT10
;
; sprite is in a depth less than 8BPP so take the current palette and munge
; it based on the translation table defined in workspace
;

02
        LDR     R0,[R1,#spMode]         ; R0 = mode of sprite
        ADRL    R1,temppaltable         ; R1 -> temporary palette area
        Push    "R0-R3,R5"

        BL      getpalpointer           ; R14 -> palette to be used

        LDR     R4,sprite_log2bpp
        CMP     R4,#1
        ADRLTL  R4,transtable1          ; R4 => translation 1BPP
        ADREQL  R4,transtable2          ;       translation 2BPP
        ADRGTL  R4,transtable4          ;       translation 4BPP
        MOV     R5,#15

05      LDRB    R3,[R4,R5]              ; get index into real palette
        LDR     R3,[R14,R3,LSL #2]
        BIC     R3,R3,#&000000FF        ; R3 = &BBGGRR00
        STR     R3,[R1,R5,LSL #2]
        SUBS    R5,R5,#1
        BPL     %BT05                   ; loop back copying the data

        Pull    "R0-R3,R5"

10      MOV     R4,#0                   ; R4 =0 read table size
        SWI     XColourTrans_SelectTable
        BVS     %FT90                   ; exit

        LDR     R14,pixtable_at
        TEQ     R14,#0                  ; does the pixtable exist?
        LDRNE   R14,pixtable_size
        CMP     R14,R4                  ; is it big enough?
        BHS     %FT20

        Push    "R0-R3"

        LDR     R2,pixtable_at
        CMP     R2,#0                   ; is there a pixel table defined yet?
        MOVNE   R0,#ModHandReason_Free
        BLNE    XROS_Module              ; free it if there is - ignoring errors

        MOV     R2,#0
        STR     R2,pixtable_at          ; mark as the pix table has been released

        MOV     R0,#ModHandReason_Claim
        MOV     R3,R4
        BL      XROS_Module              ; attempt to claim a new buffer big enough
        STRVC   R2,pixtable_at
        STRVC   R3,pixtable_size        ; store pointer and define the size

        STRVS   R0,[SP]
        Pull    "R0-R3"
        BVS     %FT90                   ; return if it errors

20      MOV     R6,R4                   ; R6 = size of table generated (first time)
        LDR     R4,pixtable_at

        ASSERT  ?selecttable_args = 4*6
        ADR     R7,selecttable_args
        STMIA   R7,{R0-R1,R2-R3,R4,R5}  ; cache entry parameters for the translation

        Debug   ic,"Sprite src mode, src pptr, dest mode, dest pptr",R0,R1,R2,R3

        SWI     XColourTrans_SelectTable
        BVS     %FT90                   ; return if not important

        CMP     R6,#256
        BHI     %FT40                   ; if its greater than 8bpp then we can ignore it (was GT)

35      SUBS    R6,R6,#1                ; decrease the index into the table
        BMI     %FT90                   ; return if end of the world reached

        LDRB    R14,[R4,R6]
        TEQ     R14,R6                  ; colour number = index? (1:1 mapped)
        BEQ     %BT35
40
        MOV     R14,#-1
        STRB    R14,sprite_needsfactors ; mark as needing translation
90
        Pull    "R3-R4"
92
        MOV     R14, #0
        STRB    R14, selecttable_crit   ; passed the critical moment for pixtable

        EXIT

;..............................................................................

; in    [spritename] --> sprite name
;       [thisCBptr] --> sprite area
;       [lengthflags] ==> R2 -> sprite name or sprite?
; out   R2 -> sprite
;       R0 corrupt

area_System     * 0
area_Wimp       * 1

cachespriteaddress ROUT
        Entry   "R1,R3"

 [ debugsprite
        LDR     LR,spritename
 ]
        DebugS  sprite,"cachespriteaddress: ",LR,12

        LDR     R0,lengthflags
        CMP     R0,#0                   ; absolute pointer to the sprite?
        LDREQ   R2,spritename           ; R2 -> sprite
        EXIT    EQ

        LDR     R1,thisCBptr            ; R1 -> sprite pool
        CMP     R1,#nullptr             ; trap area pointer -1 (none)
        MOVEQ   R1,#area_Wimp
        CMP     R1,#area_Wimp           ; if its the Wimp or above
        BCS     %FT10

        MOV     R0,#3
        SWI     XOS_ReadDynamicArea
        MOV     R1,R0                   ; R1 -> system sprite pool

10      TEQ     R1,#area_Wimp           ; Wimp sprite pool or user specified?
        BNE     %FT20

        LDR     R0,list_at
        CMP     R0,#nullptr             ; is there a list allocated?
        BEQ     %FT15                   ; no, its flagged as invalid

        CMP     R0,#0
        BNE     %FT13
        BL      makespritelist
        LDR     R0,list_at              ; resolve pointer
13
        CMP     R0,#nullptr
        BEQ     %FT15                   ; if not valid still then return

        BL      getspriteaddr
        STRVC   R2,spritename           ; store as pointer to the sprite
        MOVVC   R0,#0
        STRVC   R0,lengthflags          ; speed up 'cos makes it go faster
        EXIT

15
      [ SpritePriority
        Debug   sprite,"Looking in high-priority sprite area"

        LDR     R1,baseofhisprites
      |
        Debug   sprite,"Looking in RAM"

        LDR     R1,baseofsprites        ; R1 -> RAM based pool
      ]
        BL      checkforsprite
        EXIT    VC                      ; return if it has been found

        LDR     R1,[R0]
        LDR     R2,=ErrorNumber_Sprite_DoesntExist
        TEQ     R1,R2
        BNE     %FT25                   ; if sprite not found then return R2 =0

      [ SpritePriority
        Debug   sprite,"Looking in low-priority sprite area"

        LDR     R1,baseoflosprites
      |
        Debug   sprite,"Looking in ROM"

        LDR     R1,baseofromsprites
      ]
20      BL      checkforsprite
        EXIT    VC

        LDR     LR,[R0]                 ; error number returned
        LDR     R2,=ErrorNumber_Sprite_DoesntExist
        TEQ     LR,R2
        BNE     %FT25                   ; if not then return with R2 =0

      [ SpritePriority
        LDR     LR,baseoflosprites
      |
        LDR     LR,baseofromsprites
      ]
        TEQ     LR,R1                   ; Searching wimp areas?
        BEQ     %FT25                   ; Yes then exit

; As a last resort if searching user area then now try wimp area

        Debug   sprite,"Looking in Wimp area"

        LDR     R3,thisCBptr            ; R3 -> pool
        MOV     R14,#area_Wimp
        STR     R14,thisCBptr           ; mark as being Wimp
        BL      cachespriteaddress
        STR     R3,thisCBptr            ; restore (original) pool pointer
25
        DebugE  sprite,"Sprite not found "

        MOVVS   R2,#0
        EXIT

;..............................................................................

; in    R1 -> sprite area to be checked
;       [spritename] --> sprite name to look for
; out   R2 -> sprite

checkforsprite ROUT
        Push    "LR"
        LDR     R0,=&100+SpriteReason_SelectSprite
        LDR     R2,spritename           ; R2 -> sprite name
        SWI     XOS_SpriteOp            ; VC => R2 -> sprite
        Pull    "PC"


;;----------------------------------------------------------------------------
;; Graphics routines - used for various graphic effects
;;----------------------------------------------------------------------------

;
; Entry:  R0 bits 0..3 = wimp colour that text is to be set to
;         R0 bit 7     = fg / bg (1 ==> bg colour)
; Exit:   text (vdu 4) colour set up appropriately
;

SWIWimp_TextColour

        MyEntry "TextColour"

        Push    "R0,R3"
        AND     R3,R0,#&80              ; fg/bg flag
        AND     R0,R0,#&0F
        BL      getpalpointer
        LDR     R0,[R14,R0,LSL #2]      ; physical colour to use
        SWI     XColourTrans_SetTextColour
        STRVS   R0,[SP]
        Pull    "R0,R3"
;
        B       ExitWimp

;
; Entry:  R0 = colour/action to be set up
;             bits 0..3 = colour (Wimp colour, in range 0..15)
;             bits 4..6 = GCOL action
;             bit 7     = fg / bg (1 ==> bg colour)
;

SWIWimp_SetColour

        MyEntry "SetColour"

        Push    R0
        BL      int_setcolour
        ADDVS   sp,sp,#4
        BVS     ExitWimp
        MOV     R1,R0
        Pull    R0
        ADRL    R4,last_fg_gcol
        TST     R0,#128
        STREQ   R14,[R4]                ; bg/fg flag affects last_??_gcol
        STRNE   R14,[R4,#4]
        MOV     R0,R1
        B       ExitWimp

; set colour using ColourTrans

; in    R0 colour value
;               bits 0-3 colour index
;               bits 4-6 gcol operation
;               bit 7 fg/bg

int_setcolour
        Push    "R1-R4,LR"

        AND     R3,R0,#2_10000000       ; get the fg/bg flag
        AND     R4,R0,#2_01110000       ; get the gcol mode
        MOV     R4,R4,LSR #4
        AND     R0,R0,#2_00001111       ; get the logical colour index
;
        LDR     R14,log2bpp
        CMP     R14,#1                  ; is this mode monochrome?
        CMPCC   R0,#8                   ; and is the colour in the range of 0-7?
        ORRCC   R3,R3,#1:SHL:8          ; if this is so then dither
;
        LDR     R14,ditheringflag
        ORR     R3,R3,R14               ; use dithering if enabled
;
        BL      getpalpointer
        LDR     R0,[R14,R0,LSL #2]      ; get the physical colour required
        ADRL    R14,last_fg_gcol
        TST     R3,#128
        STREQ   R0,[R14]                ; bg/fg flag affects last_??_gcol
        STRNE   R0,[R14,#4]
        Push    R0
        SWI     XColourTrans_SetGCOL
        Pull    R14                     ; return palette entry in R14 (New!!!)
;
        Pull    "R1-R4,PC"

;..............................................................................

; set a foreground colour (checking to see if translation required)

foreground
        EntryS  "R0-R4"

        MOV     R3,#&00
        MOV     R4,#&0                  ; fg + GCOL action 0
        B       settranslate

      [ TrueIcon1
icon_fg
        ALTENTRY

        MOV     R3,#&00
        MOV     R4,#&00
      [ TrueIcon2
        LDR     R0, truefgcolour
      |
        LDR     R14,truefgcolour        ; check for a 24-bit colour override
        CMP     R14,#-1
        BEQ     settranslate
        MOV     R0,R14
      ]
        B       settruecolour
      ]

window_fg
        ALTENTRY

        MOV     R3,#&00
        MOV     R4,#&00                 ; GCOL action 0
      [ TrueIcon3
        B       settruecolour
      |
        LDR     R14,[handle,#w_flags]
        TST     R14,#wf_realcolours     ; need translation?
        BNE     setnotranslate
        B       settranslate            ; call a suitable routine
      ]

;..............................................................................

; set a background colour (checking to see if translation required)

background
        ALTENTRY

        MOV     R3,#&80
        MOV     R4,#&00                 ; bg + GCOL action 0
        B       settranslate

      [ TrueIcon1
icon_bg
        ALTENTRY

        MOV     R3,#&80
        MOV     R4,#&00
      [ TrueIcon2
        LDR     R0, truebgcolour
      |
        LDR     R14,truebgcolour        ; check for a 24-bit colour override
        CMP     R14,#-1
        BEQ     settranslate
        MOV     R0,R14
      ]
        B       settruecolour
      ]

window_bg
        ALTENTRY

        MOV     R3,#&80
        MOV     R4,#&00                 ; GCOL action 0
      [ TrueIcon3
        B       settruecolour
      |
        LDR     R14,[handle,#w_flags]
        TST     R14,#wf_realcolours     ; need translation?
        BEQ     settranslate            ; call suitable routine
        ; fall through
      ]

;..............................................................................

; set the colour performing a suitable translation

; in    R0 logical colour in bits 0-3
;       R3 fg / bg bits
;       R4 logical operation

setnotranslate
        AND     R0,R0,#&F               ; assume R0 is a GCOL value
        SWI     XColourTrans_SetColour
        EXITS

settranslate
        AND     R0,R0,#&F               ; only 4 bits describe the colour to setup
;
        LDR     R14,log2bpp
        CMP     R14,#1                  ; is this mode monochrome?
        CMPCC   R0,#8                   ; and is the colour in the range of 0-7?
        ORRCC   R3,R3,#1:SHL:8          ; if this is so then dither
;
        LDR     R14,ditheringflag
        ORR     R3,R3,R14               ; use dithering if CMOS enabled it
;
        BL      getpalpointer
        LDR     R0,[R14,R0,LSL #2]      ; get the physical colour required
;
        SWI     XColourTrans_SetGCOL    ; and set the colour
        EXITS

      [ TrueIcon1
settruecolour
      [ TrueIcon2
        LDR     R14, log2bpp
        TEQ     R14, #0
        BNE     %FT01
        Push    "R4 - R6"               ; in 2-colour modes, dither greys
        MOV     R4, R0, LSR #8
        AND     R4, R4, #&FF
        MOV     R5, R0, LSR #16
        AND     R5, R5, #&FF
        MOV     R6, R0, LSR #24
        TEQ     R4, R5
        TEQEQ   R5, R6
        ORREQ   R3, R3, #&100
        Pull    "R4 - R6"
01
      ]
        LDR     R14,ditheringflag
        ORR     R3,R3,R14               ; use dithering if CMOS enabled it
;
        SWI     XColourTrans_SetGCOL    ; and set the colour
        EXITS
      ]

;
; Adjust the scaling block if required
;
; in    R6 -> scaling block
; out   [R6] may have been modified
;

scaled
        Push    "R8-R11"
        LDMIA   R6,{R8-R11}             ; bodge factors if half size
        TST     R1,#if_halfsize
        MOVNE   R10,R10,LSL #1
        MOVNE   R11,R11,LSL #1
        Push    "R8-R11"
        MOV     R6,sp
        BL      wimp_SpriteOp
        ADD     sp,sp,#4*4
        Pull    "R8-R11"
        Pull    "R1,R6-R7,PC"

;
; Plot sprite
; Entry:  R1 = icon flag word
;         R3,R4 = x,y coords to plot sprite at
;         R5 = GCOL action (8)
;         [thisCBptr], [lengthflags], [spritename] set up
;

wimp_SpriteOp_putsprite
        Push    "R1,R6-R7,LR"
;
        LDR     R7,pixtable_at          ; -> translation table
;
        TST     R1,#is_inverted:OR:is_shaded
      [ PlotSpritesFromPalette
        BICNE   R5,R5,#16               ; ensure we don't use the palette
      ]
        BLNE    calcinverse             ; updates R7
;
        LDRB    R0,sprite_needsfactors
        TEQ     R0,#0                   ; do I need to translate
        TSTEQ   R1,#if_halfsize         ; does need factors if half size
;
        MOVNE   R0,#SpriteReason_PutSpriteScaled
        ADRNE   R6,sprite_factors
        BNE     scaled
;
        MOV     R0,#SpriteReason_PutSpriteUserCoords
unscaled
        BL      wimp_SpriteOp
        Pull    "R1,R6-R7,PC"

;
; do sprite operation
;      [thisCBptr] = version in window defn
;                    or version in indirected sprite validation string
;      [lengthflags] <= 0 ==> [spritename] --> sprite itself, else name
;

wimp_SpriteOp
        Push    "R1,R2,R11,LR"
        LDR     R1,thisCBptr            ; version for this sprite
        LDR     R2,spritename           ; ditto
        CMP     R1,#1                   ; 0 ==> use system sprite pool
        ORRHS   R0,R0,#&100             ; 1 ==> use common sprite pool
        BNE     %FT01
11
      [ SpritePriority
        LDR     R1,baseofhisprites
      |
        LDR     R1,baseofsprites        ; common sprite pool is split in 2
      ]
        MOV     R11,R0
        BL      do_spriteop             ; first try the RAM sprites
        Pull    "R1,R2,R11,PC",VC
        LDR     R14,[R0]
        TEQ     R14,#ErrorNumber_Sprite_DoesntExist     ; preserves V
        MOVEQ   R0,R11
      [ SpritePriority
        LDREQ   R1,baseoflosprites
      |
        LDREQ   R1,baseofromsprites     ; in resourcefs:
      ]
        BLEQ    do_spriteop             ; then the ROM sprites
        Pull    "R1,R2,R11,PC"
01
        MOV     R11,R0
;
        BL      do_spriteop
        Pull    "R1,R2,R11,PC",VC
;
        LDR     R14,[R0]
        TEQ     R14,#ErrorNumber_Sprite_DoesntExist     ; preserves V
        MOVEQ   R0,R11
        ORREQ   R0,R0,#&100
        BEQ     %BT11
;
        Pull    "R1,R2,R11,PC"

do_spriteop
        Push    "LR"
      [ true
        AND     R14, R0, #&FF
        TEQ     R14, #SpriteReason_DeleteSprite ; is able to get here, provided it came via *WimpKillSprite
        LDREQ   R14, baseofromsprites
        TEQEQ   R1, R14
        Pull    "PC", EQ                        ; don't attempt to delete ROM sprites!
      ]
        LDR     R14,lengthflags
        CMP     R14,#0                  ; if length 0, then R2 --> sprite
        ANDEQ   R0,R0,#&0FF
        ORREQ   R0,R0,#&200
        SWI     XOS_SpriteOp
        Pull    "PC"

;----------------------------------------------------------
; calculate new ttr table to deal with inverting the sprite
; Entry:  R1 = icon flag word
;         R0,R6 = input/output log2bpp     <-- don't count on this!
;         R7 --> pixtable
; Exit:   pixtable updated to allow for inverting/shading
;---------------------------------------------------------

calcinverse ROUT

        Entry   "R0-R7"

        ADR     R7,inversefunc          ; pointer to the transfer function
        MOV     R6,R1
        LDR     R4,pixtable_at
        TEQ     R4,#0
        EXIT    EQ                      ; no pixtable!
      [ true ; BJGA bugfix: was sometimes trying to work from a paged-out sprite!
        LDR     R1, sprite_log2bpp      ; if <256-colour sprite without palette, we pass palette from Wimp workspace, so no problem
        CMP     R1, #3                  ; if <256-colour sprite with palette, cachespritedata has already updated selecttable_args
        BLGE    cachespriteaddress      ; R2 -> this sprite, ie *not* the first sprite in this run of matching-mode sprites!
        EXIT    VS                      ; bail out now if sprite not found!
        CMP     R1, #3
        Push    "R2", GE
        ADR     R0, selecttable_args
        LDMIA   R0, {R0-R5}
        LDRGE   R0, baseofsprites       ; any non-system sprite area will do
        Pull    "R1", GE
      |
        ADR     R0,selecttable_args
        LDMIA   R0,{R0-R5}              ; get the parameters for the call
      ]
        ORR     R5,R5,#1:SHL:2          ; mark as a transfer function supplied
        SWI     XColourTrans_GenerateTable

;        SWI     XColourTrans_InvalidateCache
        MOV     R0,#-1
        STR     R0,sprite_lastmode
        STRB    R0,sprite_needsfactors  ; mark as recache and needing translation table

        CLRV
        EXIT

;------------------------------------------------------------------------------
; Routine called to process the RGB and generate a suitable output RGB based
; on the shading or inversion required.
;
; in    R0  physical colour to manipulate
;       R12 icon flags to be modified
; out   R0  modified RGB value - all other registers preserved
;------------------------------------------------------------------------------

inversefunc EntryS "R1-R4"
        MOV     R4,#&FF                 ; mask used to extract the various components of the word
        BIC     R0,R0,R4                ; clear out the bottom bits

        MOV     R3,R0,LSR #24           ; extract the blue
        AND     R2,R4,R0,LSR #16        ; extract the green
        AND     R1,R4,R0,LSR #8         ; extract the red

        Debug   inverse,"InverseFunc: red,green,blue",R1,R2,R3

        TST     R12,#is_inverted        ; Inverting?
        BEQ     %FT10                   ; No then jump

      [ UrsulaHighlighting              ; New highlighting for Risc PC 2 release
      [ true
        MOV     R0, R1, LSL#8           ; Convert to 16 bit fixed point
        ADD     R0, R0, #128
        MOV     R1, R2, LSL#8           ; Convert to 16 bit fixed point
        ADD     R1, R1, #128
        MOV     R2, R3, LSL#8           ; Convert to 16 bit fixed point
        ADD     R2, R2, #128
        SWI     XColourTrans_ConvertRGBToHSV ; R0->H, R1->S, R2->V

        LDR     R3, =&FFFF
        CMP     R1, #1:SHL:16
        MOVHS   R1, R3
        CMP     R2, #1:SHL:16
        MOVHS   R2, R3

        MUL     R4, R1, R2              ; can't use MLA in case of overflow
        SUB     R2, R3, R2              ; V = 1 - V
        SUB     R2, R2, R2, LSR#2       ; V = .25 (3 - 3V)
        ADD     R2, R2, R1, LSR#2       ; V = .25 (3 - 3V + S)
        ADD     R2, R2, R4, LSR#18      ; V = .25 (3 - 3V + S + SV)

        SWI     XColourTrans_ConvertHSVToRGB ; R0->R, R1->G, R2->B
        CMP     R2, #1:SHL:16
        MOVLO   R3, R2, LSR#8           ; Convert to 8 bit
        MOVHS   R3, #255
        CMP     R1, #1:SHL:16
        MOVLO   R2, R1, LSR#8           ; Convert to 8 bit
        MOVHS   R2, #255
        CMP     R0, #1:SHL:16
        MOVLO   R1, R0, LSR#8           ; Convert to 8 bit
        MOVHS   R1, #255
      |                                 ; Alternative (original) Ursula highlighting scheme
        TEQ     R1,R2
        TEQEQ   R1,R3                   ; grey ?
        BEQ     %FT20

        MOV     R0,R1,LSL #8            ; Convert to 16 bit fixed point
        ADD     R0,R0,#128
        MOV     R1,R2,LSL #8            ; Convert to 16 bit fixed point
        ADD     R1,R1,#128
        MOV     R2,R3,LSL #8            ; Convert to 16 bit fixed point
        ADD     R2,R2,#128
        SWI     XColourTrans_ConvertRGBToHSV ; R0->H, R1->S, R2->V
        ADD     R0,R0,#180:SHL:16
        CMP     R0,#360:SHL:16
        SUBHI   R0,R0,#360:SHL:16
        SWI     XColourTrans_ConvertHSVToRGB ; Form RGB
        CMP     R2,#1:SHL:16
        MOVLO   R3,R2, LSR #8           ; Convert to 8 bit
        MOVHS   R3, #255
        CMP     R1,#1:SHL:16
        MOVLO   R2,R1, LSR #8           ; Convert to 8 bit
        MOVHS   R2, #255
        CMP     R0,#1:SHL:16
        MOVLO   R1,R0, LSR #8           ; Convert to 8 bit
        MOVHS   R1, #255
20
        RSB     R1,R1,#255
        RSB     R2,R2,#255              ; make a negative
        RSB     R3,R3,#255

        SUB     R1,R1,R1,LSR #2
        SUB     R2,R2,R2,LSR #2         ; darken a little
        SUB     R3,R3,R3,LSR #2
      ]

      |

        TEQ     R1,R2
        TEQEQ   R1,R3                   ; grey ?
        RSBEQ   R1,R1,#255
        RSBEQ   R2,R2,#255
        RSBEQ   R3,R3,#255
        BEQ     %FT10

        CMP     R1,#5
        CMPGT   R2,#5
        CMPGT   R3,#5                   ; if one is less than 5, then essentially un saturated

        MOVLE   R1,R1, LSR #1
        MOVLE   R2,R2, LSR #1
        MOVLE   R3,R3, LSR #1
        BLE     %FT10

; Invert colour

        MOV     R0,R1,LSL #8           ; Convert to 16 bit fixed point
        ADD     R0,R0,#128
        MOV     R1,R2,LSL #8           ; Convert to 16 bit fixed point
        ADD     R1,R1,#128
        MOV     R2,R3,LSL #8           ; Convert to 16 bit fixed point
        ADD     R2,R2,#128
        SWI     XColourTrans_ConvertRGBToHSV ; R0->H, R1->S, R2->V
        CMP     R1,#1:SHL:11            ; S < 1/32? - i.e. gray?
        RSBLO   R2,R2,#1:SHL:16         ; Yes then v = 1 - V
        MOVHS   R4,#10:SHL:12           ; k=10/16
        MULHS   R3,R2,R4                ; Else V = V*k
        MOVHS   R2,R3,LSR #16           ;   v = V*k, adjusting for multiply
        SWI     XColourTrans_ConvertHSVToRGB ; Form RGB
        CMP     R2,#1:SHL:16
        MOVLO   R3,R2, LSR #8           ; Convert to 8 bit
        MOVHS   R3, #255
        CMP     R1,#1:SHL:16
        MOVLO   R2,R1, LSR #8           ; Convert to 8 bit
        MOVHS   R2, #255
        CMP     R0,#1:SHL:16
        MOVLO   R1,R0, LSR #8           ; Convert to 8 bit
        MOVHS   R1, #255

      ]
        Debug   inverse,"Inverted red,green,blue",R1,R2,R3

10      TST     R12,#is_shaded          ; Shaded?
        BEQ     %FT20                   ; No then jump

; Grey the colour

        MOV     R4,#77
        MUL     R4,R1,R4                ; red *77
        MOV     R14,#150
        MLA     R4,R2,R14,R4            ; red *77 + green *150
        RSB     R3,R3,R3,LSL #3
        ADD     R4,R4,R3,LSL #2         ; red *77 + green *150 + green *28

        ADD     R4,R4,#&7F              ; Rounding
        ADD     R4,R4,R4,LSL #8         ; Make 16 bit fractional
        ADD     R4,R4,#&100             ; Rounding
        MOV     R4,R4,LSR #16           ; Convert to 8bit luma

        MOV     R0, #&b0                ; Low point (&b0 - &18), (&b0b0b0 is colour 2)
        MOV     R1, #&ff                ; High point (&b0 + &17)
        MUL     R2,R4,R1                ; V*h
        MUL     R3,R4,R0                ; V*l
        SUB     R2,R2,R3                ; V*h - V*l
        ADD     R4,R0,R2,LSR #8         ; V*h - V*l + l normalised => V = V*(h-l) +l
        MOV     R1,R4
        MOV     R2,R1
        MOV     R3,R1                   ; Map all colours to grey

        Debug   inverse,"Grey to",R1

20      MOV     R0,R1,LSL #8
        ORR     R0,R0,R2,LSL #16
        ORR     R0,R0,R3,LSL #24        ; recombine to make &BBGGRRxx
        EXITS

;; ---------------------------------------------------------------------------
;; Miscellaneous routines
;; ---------------------------------------------------------------------------
;
; set up graphics window
;

graphicswindow ROUT
        EntryS  "x0,y0,x1,y1,R10"
;
        CMP     x0,#0
        MOVLT   x0,#0
        CMP     y0,#0
        MOVLT   y0,#0
;
        ADR     R14,clipx0              ; used by drawicon
        STMIA   R14,{x0,y0,x1,y1}
;
        LDR     R10,commandflag
        ORR     R14,R10,#cf_wimpdoingvdu
        STR     R14,commandflag
;
        SWI     XOS_WriteI+24           ; set graphics window
        Coords  x0,y0
        SUB     x1,x1,#1
        SUB     y1,y1,#1
        Coords  x1,y1
;
        STR     R10,commandflag
;
        EXITS

;
; clear graphics window
;

defaultwindow
        EntryS  "x0,y0,x1,y1,R10"
;
        LDR     R10,commandflag
        ORR     R14,R10,#cf_wimpdoingvdu
        STR     R14,commandflag
;
        ADR     R14,scrx0
        LDMIA   R14,{x0,y0,x1,y1}       ; include icon bar
        ADR     R14,clipx0
        STMIA   R14,{x0,y0,x1,y1}
        SWI     XOS_WriteI+26
;
        STR     R10,commandflag
;
        EXITS

;
; plot a solid rectangle in the background colour
;

solidrectangle
        TEQ     x0,x1                   ; SMC: don't draw size 0 icons
        TEQNE   y0,y1
        MOVEQ   PC,LR

        Push    "R1,R2,x1,y1,LR"
;
        Plot    4,x0,y0    ; plot code &64 is WAAAY slower
        LDR     R14,dx
        SUB     x1,x1,R14
        LDR     R14,dy
        SUB     y1,y1,R14
        Plot    &67,x1,y1
;
        Pull    "R1,R2,x1,y1,PC"

;
; plot a rectangular outline in the foreground colour
;

hollowrectangle
        TEQ     x0,x1                   ; SMC: don't draw size 0 icons
        TEQNE   y0,y1
        MOVEQ   PC,LR

        Push    "R1,R2,x1,y1,LR"
;
        LDR     R14,dx
        SUB     x1,x1,R14
        LDR     R14,dy
        SUB     y1,y1,R14
;
        Plot    &04,x0,y0
        Plot    &05,x1,y0
        Plot    &05,x1,y1
        Plot    &05,x0,y1
        Plot    &05,x0,y0
;
        Pull    "R1,R2,x1,y1,PC"

;
; turn off drag rectangle
;

nodrag
        EntryS  "x0,y0,x1,y1"
        ADR     R14,dragx0
        LDMIA   R14,{x0,y0,x1,y1}
        LDRB    R14,dragflag
        TEQ     R14,#0
        MOVNE   R14,#0
        STRNEB  R14,dragflag
        ; Plot to remove box
    [ false
        MOVNE   R0,#drg_off
        BLNE    dottedbox1
    |
        BEQ     %FT06
      [ Autoscr
        LDR     R14, dragflags
        TST     R14, #dragf_clip        ; clipped dragboxes must only be redrawn within their own window's visible rectangles
        MOVNE   R0, #drg_off
        BLNE    dottedbox1_clipped      ; calls OS_ScreenMode 5 as part of redraw loop
        BNE     %FT06
      ]
        MOV     R0, #drg_off
        BL      dottedbox1
      [ ChocolateScreen
        MOV     R0, #5                  ; ChocolateUpdate reason code
        SWI     XOS_ScreenMode
      ]
06
    ]
        EXITS

;
; move the drag rectangle to a new position (x0,y0,x1,y1)
;

yesdrag
        EntryS  "cx0,cy0,cx1,cy1"
;
        ADR     R14,dragx0              ; Examine the old dragbox
        LDMIA   R14,{cx0,cy0,cx1,cy1}
        LDRB    R14,dragflag
        CMP     R14,#0
        BNE     %FT01
;
        LDR     R14,dragtype            ; Leading edge
        CMP     R14,#drag_subr_posn
        BLO     yesdr1
        B       yesdrplot               ; Do a plot the first time round (NOT A MOVE !)
01
        CMP     x0,cx0                  ; Dragbox continuing
        CMPEQ   y0,cy0
        CMPEQ   x1,cx1
        CMPEQ   y1,cy1
        BEQ     yesdr2                  ; just rotate the dashes if it's keeping the same position
;
yesdr1
        LDR     R14,dragtype            ; Dragbox moving
        CMP     R14,#drag_scrollboth
        CMPNE   R14,#drag_subr_posn-1
        BLS     %FT01
        LDR     R14,dragsubr_move       ; does it have a move routine?
        AcceptLoosePointer_NegOrZero R14,0
        CMP     R14,R14,ASR #31
        BEQ     %FT01
;
        ; Plot to translate/resize box - dashes don't rotate here
      [ Autoscr
        LDR     R14, dragflags
        TST     R14, #dragf_clip ; clipped dragboxes must only be redrawn within their own window's visible rectangles
        MOVNE   R0, #drg_move
        BLNE    dottedbox1_clipped
        BNE     %FT02
      ]
        MOV     R0,#drg_move
        BL      dottedbox1
      [ ChocolateScreen
        MOV     R0, #5                  ; ChocolateUpdate reason code
        SWI     XOS_ScreenMode
      ]
        B       %FT02

01                                      ; No move routine - remove and redraw
    [ false
        BL      nodrag
    |
        ; Plot to toggle box
      [ Autoscr
        LDR     R14, dragflags
        TST     R14, #dragf_clip ; clipped dragboxes must only be redrawn within their own window's visible rectangles
        BLNE    dottedbox_toggle_clipped
        BNE     %FT02
      ]
        BL      dottedbox_toggle
      [ ChocolateScreen
        MOV     R0, #5                  ; ChocolateUpdate reason code
        SWI     XOS_ScreenMode
      ]
        B       %FT02
    ]

yesdrplot
        ; Plot to draw box
      [ Autoscr
        LDR     R14, dragflags
        TST     R14, #dragf_clip ; clipped dragboxes must only be redrawn within their own window's visible rectangles
        MOVNE   R0, #drg_on
        BLNE    dottedbox1_clipped
        BNE     %FT02
      ]
        MOV     R0,#drg_on
        BL      dottedbox1
      [ ChocolateScreen
        MOV     R0, #5                  ; ChocolateUpdate reason code
        SWI     XOS_ScreenMode
      ]

02                                      ; Dragbox has now been moved
        ADR     R14,dragx0
        STMIA   R14,{x0,y0,x1,y1}       ; store new coords
        MOV     R14,#1
        STRB    R14,dragflag
        EXITS
;
yesdr2
        ; Plot to rotate dashes, if necessary
    [ true
        BL      rotdotdash
        BNE     %FT01                   ; don't do anything if timer hasn't counted down
        MOV     R0, #drg_move
        STRB    R0, dragaction
        LDRB    R0, dotdash2
      [ Autoscr
        LDR     R14, dragflags
        TST     R14, #dragf_clip ; clipped dragboxes must only be redrawn within their own window's visible rectangles
        BLNE    dottedbox_clipped
        BNE     %FT01
      ]
        BL      dottedbox
      [ ChocolateScreen
        MOV     R0, #5                  ; ChocolateUpdate reason code
        SWI     XOS_ScreenMode
      ]
01
    |
        BL      rotdotdash
        MOVEQ   R0,#drg_move
        STREQB  R0,dragaction
        LDREQB  R0,dotdash2
        BLEQ    dottedbox
    ]
        EXITS

      [ Autoscr
;
; force the drag rectangle off/on (if it is enabled)
;

forcedrag_off
        Entry   "R0-y1"
;
        LDRB    R14, dragflag
        CMP     R14, #0
        EXIT    EQ

        ADR     R14, dragx0
        LDMIA   R14, {x0, y0, x1, y1}
      [ false ; Some DragASprite callers have a bounding box smaller than the sprite (duh),
              ; so we can't optimise out dragboxes that don't intersect the graphics window  :-(
        Push    "x0, y0, x1, y1"
        ADR     R14, clipx0
        LDMIA   R14, {cx0, cy0, cx1, cy1}

        SUBS    R0, x1, x0
        ADDLT   x0, x0, R0              ; ensure right way round
        SUBLT   x1, x1, R0
        LDR     R0, dx
        SUB     x0, x0, R0              ; add a pixel all round for luck
        ADD     x1, x1, R0

        SUBS    R0, y1, y0
        ADDLT   y0, y0, R0              ; ensure right way round
        SUBLT   y1, y1, R0
        LDR     R0, dy
        SUB     y0, y0, R0              ; add a pixel all round for luck
        ADD     y1, y1, R0

        CMP     x0, cx0
        MOVGT   cx0, x0                 ; cx0 = max (x0, cx0)
        CMP     x1, cx1
        MOVLT   cx1, x1                 ; cx1 = min (x1, cx1)
        CMP     y0, cy0
        MOVGT   cy0, y0                 ; cy0 = max (y0, cy0)
        CMP     y1, cy1
        MOVLT   cy1, y1                 ; cy1 = min (y1, cy1)
        CMP     cx1, cx0
        CMPGT   cy1, cy0

        Pull    "x0, y0, x1, y1"
        MOVGT   R0, #drg_off
        BLGT    dottedbox1              ; don't write back to the drag flag
      |
        MOV     R0, #drg_off
        BL      dottedbox1              ; don't write back to the drag flag
      ]
;
        EXIT

forcedrag_on
        Entry   "R0-y1"
;
        LDRB    R14, dragflag
        CMP     R14, #0
        EXIT    EQ

        TEQ     R14, #2                 ; special code => get new dragbox position from tempworkspace
        ASSERT  :INDEX:tempworkspace = 0
        ADDEQ   R14, wsptr, #4*4
        ADRNE   R14, dragx0
        LDMIA   R14, {x0, y0, x1, y1}
      [ false ; Some DragASprite callers have a bounding box smaller than the sprite (duh),
              ; so we can't optimise out dragboxes that don't intersect the graphics window  :-(
        Push    "x0, y0, x1, y1"
        ADR     R14, clipx0
        LDMIA   R14, {cx0, cy0, cx1, cy1}

        SUBS    R0, x1, x0
        ADDLT   x0, x0, R0              ; ensure right way round
        SUBLT   x1, x1, R0
        LDR     R0, dx
        SUB     x0, x0, R0              ; add a pixel all round for luck
        ADD     x1, x1, R0

        SUBS    R0, y1, y0
        ADDLT   y0, y0, R0              ; ensure right way round
        SUBLT   y1, y1, R0
        LDR     R0, dy
        SUB     y0, y0, R0              ; add a pixel all round for luck
        ADD     y1, y1, R0

        CMP     x0, cx0
        MOVGT   cx0, x0                 ; cx0 = max (x0, cx0)
        CMP     x1, cx1
        MOVLT   cx1, x1                 ; cx1 = min (x1, cx1)
        CMP     y0, cy0
        MOVGT   cy0, y0                 ; cy0 = max (y0, cy0)
        CMP     y1, cy1
        MOVLT   cy1, y1                 ; cy1 = min (y1, cy1)
        CMP     cx1, cx0
        CMPGT   cy1, cy0

        Pull    "x0, y0, x1, y1"
        MOVGT   R0, #drg_on
        BLGT    dottedbox1              ; don't write back to the drag flag
      |
        MOV     R0, #drg_on
        BL      dottedbox1              ; don't write back to the drag flag
      ]
;
        EXIT
      |
;
; force the drag rectangle off/on (if it is enabled)
;

forcedrag_off
        Push    "R0,R1,x0,y0,x1,y1,LR"
;
        LDRB    R14,dragflag
        CMP     R14,#0
        ADRNE   R14,dragx0
        LDMNEIA R14,{x0,y0,x1,y1}
        MOVNE   R0,#drg_off
        BLNE    dottedbox1              ; don't write back to the drag flag
;
        Pull    "R0,R1,x0,y0,x1,y1,PC"

forcedrag_on
        Push    "R0,R1,x0,y0,x1,y1,LR"
;
        LDRB    R14,dragflag
        CMP     R14,#0
        ADRNE   R14,dragx0
        LDMNEIA R14,{x0,y0,x1,y1}
        MOVNE   R0,#drg_on
        BLNE    dottedbox1              ; don't write back to the drag flag
;
        Pull    "R0,R1,x0,y0,x1,y1,PC"
      ]

      [ Autoscr
; Routines to call dottedbox1 and dottedbox from within an update_window loop
;
dottedbox_clipped
        EntryS  "cx0, cy0, cx1, cy1, handle"
        Debug   autoscr, "Called dottedbox_clipped"
        ADR     R1, dottedbox
        B       %FT01
dottedbox_toggle_clipped
        ALTENTRY
        Debug   autoscr, "Called dottedbox_toggle_clipped"
        ADR     R1, dottedbox_toggle
        B       %FT01
dottedbox1_clipped
        ALTENTRY
        Debug   autoscr, "Called dottedbox1_clipped"
        ADR     R1, dottedbox1
01
; initialise window rects for the redraw loop
        LDR     handle, draghandle
        Abs     handle, handle
        Debug   autoscr, "dottedbox(1)_clipped: handle, draghandle =", handle, #draghandle
        Push    "R0, R1, x0, y0, x1, y1"

      [ true
        ADD     R1, handle, #w_wex0
        LDMIA   R1, {x0, y0, x1, y1}
      | ; the below doesn't work for toggle operations
        ADD     R1, handle, #w_wax0
        LDMIA   R1, {cx0, cy0, cx1, cy1, x0, y0}
        SUB     cx0, cx0, x0            ; get work area x origin
        SUB     cy0, cy1, y0            ; get work area y origin
        LDMIA   sp, {R0, R1, x0, y0}
        SUB     x0, x0, cx0             ; make window-relative
        SUB     y0, y0, cy0
        SUB     x1, x1, cx0
        SUB     y1, y1, cy0
        SUBS    R14, x1, x0             ; is x0 < x1 ?
        SUBLT   x1, x1, R14             ; swap them over if not
        ADDLT   x0, x0, R14
        SUBS    R14, y1, y0             ; is y0 < y1?
        SUBLT   y1, y1, R14             ; swap them over if not
        ADDLT   y0, y0, R14
        LDR     R14, dx
        SUB     x0, x0, R14             ; add 1-pixel borders
        ADD     x1, x1, R14
        LDR     R14, dy
        SUB     y0, y0, R14
        ADD     y1, y1, R14
      ]

        MOV     R0, #getrect_firstrect :OR: getrect_updating :OR: getrect_noicons :OR: getrect_keepdragbox
        BL      int_update_window2
        Debug   autoscr, "Clipped dragbox redraw: exited int_update_window2"
02
; redraw loop
        BL      int_get_rectangle
        Debug   autoscr, "Clipped dragbox redraw loop: R0 on exit from int_get_rectangle =", R0
        BVS     %FT10
        TEQ     R0, #0                  ; finished?
        BEQ     %FT10
        LDMIA   sp, {R0, R1, x0, y0, x1, y1}
        MOV     R14, PC                 ; return to PC+8
        MOV     PC, R1                  ; equivalent of BL dottedbox[1]
        BVC     %BT02
10
        Pull    "R0, R1, x0, y0, x1, y1"
        EXITS
      ]

;
; draw a dotted box (similar to hollowrectangle)
; invisible if dragtype=7 or system drag is continuous
;

drb_system      *       (1:SHL:(drag_user-1))-1
drb_user3       *        1:SHL:(drag_user3-1)

      [ true
dottedbox_toggle
        Push    "x0-y1,LR"
        LDRB    R14, dragflag
        TEQ     R14, #0
        BEQ     %FT01 ; just in case

        ADR     R14, dragx0
        LDMIA   R14, {x0, y0, x1, y1}
        MOV     R0, #drg_off
        BL      dottedbox1              ; remove old box
01
        Pull    "x0-y1"
        MOV     R0, #drg_on
        BL      dottedbox1              ; plot new box
        Pull    "PC"
      ]

dottedbox1
        STRB    R0,dragaction           ; R0 = drag action (if subrs used)
        LDRB    R0,dotdash1

dottedbox
        Push    "x1,y1,R10-R12,LR"
;
        LDR     R14,dragtype
        TEQ     R14,#drag_scrollboth
        MOVEQ   R14,#drag_vscroll       ; use vscroll bit of sysflags for this
        LDRB    R10,sysflags
        AND     R10,R10,#drb_system     ; system drags may be invisible
        ORR     R10,R10,#drb_user3      ; user3 drag is invisible
        MOVS    R10,R10,LSR R14
        Pull    "x1,y1,R10-R12,PC",CS
;
        BL      setdotdash              ; ensure we have the correct pattern
;
; check the drag type to see if we should call a user subroutine
;
        LDR     R14,dragtype

        Debug   ub,"Dragtype",R14

        CMP     R14,#drag_scrollboth
        CMPNE   R14,#drag_subr_posn-1
        BHI     callsubr
;
        Debug   ub,"Normal drag",R14

      [ true
        ; make sure OS coordinates don't overflow
        MOV     R14, #&7F00 ; max
        LDR     R0, =-&8000 ; min
        CMP     x0, R14
        MOVGT   x0, R14
        CMP     x0, R0
        MOVLT   x0, R0
        CMP     y0, R14
        MOVGT   y0, R14
        CMP     y0, R0
        MOVLT   y0, R0
        CMP     x1, R14
        MOVGT   x1, R14
        CMP     x1, R0
        MOVLT   x1, R0
        CMP     y1, R14
        MOVGT   y1, R14
        CMP     y1, R0
        MOVLT   y1, R0
      ]

        LDR     R14,dx
        SUB     x1,x1,R14
        LDR     R14,dy
        SUB     y1,y1,R14
;
        Push    "R1,R2"
      [ true
        LDR     R0, log2bpp
        TEQ     R0, #0
        BEQ     %FT01                   ; use inverse for 2-colour modes
        Push    "R3, R4"
        LDR     R0, =&80808000          ; mid-grey ensures maximum contrast against all colours
        MOV     R3, #0
        MOV     R4, #3
        SWI     XColourTrans_SetGCOL
        Pull    "R3, R4"
        BVS     %FT01                   ; use old scheme as fallback
        Plot    &04,x0,y0
        Plot    &15,x1,y0               ; restart, both endpoints
        Plot    &35,x1,y1               ; continue, omit first point
        Plot    &35,x0,y1               ; continue, omit first point
        Plot    &3D,x0,y0               ; continue, omit both endpoints
        B       %FT02
01
      ]
        Plot    &04,x0,y0
        Plot    &16,x1,y0               ; restart, both endpoints
        Plot    &36,x1,y1               ; continue, omit first point
        Plot    &36,x0,y1               ; continue, omit first point
        Plot    &3E,x0,y0               ; continue, omit both endpoints
      [ true
02
      ]
;
        Pull    "R1,R2,x1,y1,R10-R12,PC"

callsubr
        Debug   ub,"User Drag",R14

      [ Autoscr
        Push    "cx0, cy0, cx1, cy1, x0, y0" ; must preserve R2-R5 over call now!
      |
        Push    "x0,y0"                 ; written back later
      ]
        LDRB    R8,dragaction           ; dragaction = offset from dragwsptr
        ADR     R14,dragwsptr
        LDR     R8,[R14,R8]
        AcceptLoosePointer_NegOrZero R8,0
        CMP     R8,R8,ASR #31           ; if null routine, don't call it
      [ Autoscr
        ADD     R0, sp, #4*4    ; skip cx0 - cy1 on stack
        LDMIA   R0,{R0-R3}      ; ALWAYS LOAD THESE !!!
      |
        LDMIA   sp,{R0-R3}      ; ALWAYS LOAD THESE !!!
      ]
        ADRNE   R14,dragx0
        LDMNEIA R14,{R4-R7}
        LDRNE   R12,dragwsptr           ; get appropriate workspace pointer
        MOVNE   R14,PC
        MOVNE   PC,R8
      [ Autoscr
        ADD     R14, sp, #4*4   ; skip cx0 - cy1 on stack
        STMIA   R14,{R0-R3}             ; may have been modified
        Pull    "cx0,cy0,cx1,cy1,x0,y0,x1,y1,R10-R12,PC"
      |
        STMIA   sp,{R0-R3}              ; may have been modified
        Pull    "x0,y0,x1,y1,R10-R12,PC"
      ]

;
; ensure we have the right dot-dash pattern
;

setdotdash
        Push    "R0,R1,LR"
;
        LDRB    R14,dotdash
        CMP     R0,R14
        Pull    "R0,R1,PC",EQ
        STRB    R0,dotdash
;
        ORR     R1,R0,R0,LSL #8
        ORR     R1,R1,R1,LSL #16        ; duplicate byte across word
        LDR     R0,=&06170000           ; VDU 23,6
        STR     R1,[R13,#-4]!
        STMDB   R13!,{R0-R1}            ; push onto stack:
        ADD     R0,R13,#2               ; 0,0,23,6,n,n,n,n,n,n,n,n
        MOV     R1,#10
        SWI     XOS_WriteN
        ADD     R13,R13,#12
        Pull    "R0,R1,PC"

;
; cycle dot-dash pattern if appropriate
;

rotatedefault   * 2                     ; update on the quarter second

rotdotdash
        Push    "R1,LR"
        SWI     XOS_ReadMonotonicTime   ; get the current centi-second time
        LDR     R1,rotatecounter
        SUBS    R1,R1,R0                ; should I rotate yet?
        ADDLE   R1,R0,#rotatedefault    ; if so then reset the counter for next check
        STRLE   R1,rotatecounter
        Pull    "R1,PC",GT              ; balance and then exit; Z=0 (NE)
;
        LDRB    R0,dotdash1
        MOVS    R0,R0,LSR #1
        ORRCS   R0,R0,#&80
        STRB    R0,dotdash1
        LDRB    R0,dotdash2
        MOVS    R0,R0,LSR #1
        ORRCS   R0,R0,#&80
        STRB    R0,dotdash2
;
        TEQ     R0,R0                   ; set Z flag
        Pull    "R1,PC"
;
; set pointer bounding box (half-open as usual)
;

clearpointerwindow
        ADR     x0,scrx0
        LDMIA   x0,{x0,y0,x1,y1}

pointerwindow
        Push    "R0,R1,x1,y1,LR"

        Debug   ptr,"ptrwindow:",x0,y0,x1,y1

        SUB     x1,x1,#1
        SUB     y1,y1,#1
;
        CMP     x0,#-&8000              ; (x0,y1 are normally OK anyway)
        LDRLE   x0,=-&7FFF
        CMP     y0,#-&8000              ; (x0,y1 are normally OK anyway)
        LDRLE   y0,=-&7FFF
        CMP     x1,#&8000               ; keep this signed 16-bit!
        LDRGE   x1,=&7FFF
        CMP     y1,#&8000               ; keep this signed 16-bit!
        LDRGE   y1,=&7FFF
;
        ADR     R1,mouseblk
        MOV     R14,#1                  ; reason code 1
        STRB    R14,[R1,#0]
        strw    x0,R1,1
        strw    y0,R1,3
        strw    x1,R1,5
        strw    y1,R1,7
        MOV     R0,#&15
        SWI     XOS_Word                    ; OSWORD &15
      [ NCErrorBox
        ; If confining the mouse causes it to change position,
        ; we *don't* want to trigger the reappearance of the pointer
        ADRL    y1, ptrsuspendflag
        LDR     x1, [y1]
        TEQ     x1, #2                  ; waiting for a mouse move?
        Push    "R2", EQ                ; if so then flush the mouse buffer
        MOVEQ   R0, #21                 ;   (maybe a bit dodgy, but it's the only way
        MOVEQ   R1, #Buff_Mouse         ;   to ensure that the next read from the
        SWIEQ   OS_Byte                 ;   mouse buffer was due to the OS_Word call,
        Pull    "R2", EQ                ;   which doesn't in itself flush the buffer)
        MOVEQ   x1, #1                  ; and then flag to ignore the next mouse read
        STREQ   x1, [y1]
      ]
;
        Pull    "R0,R1,x1,y1,PC"
        LTORG

;;----------------------------------------------------------------------------
;; Get_Window_Info - return whole of window data in user's buffer
;;----------------------------------------------------------------------------

; In    R1 bits 2..31 -> window header
;       R1 bit 0 set => just copy the header, not the icons
;       R1 bit 1 reserved (must be 0)
;       userblk = R1

SWIWimp_GetWindowInfo
        MyEntry "GetWindowInfo"
;
        BIC     userblk,userblk,#3              ; bottom 2 bits are flags
        LDR     handle,[userblk,#u_handle]
        BL      checkhandle_iconbar
        TST     R1,#1                           ; bit 0 set => don't return icons
        BLVC    int_get_window_info             ; NE => don't return icons
        B       ExitWimp


SWIWimp_GetWindowState
        MyEntry "GetWindowState"

        LDR     handle,[userblk]
        BL      checkhandle_iconbar
        BLVC    int_get_window_state
      [ ChildWindows
        BVS     ExitWimp

        LDR     R14,openidentifier2
        LDR     R2,[SP,#1*4]
        TEQ     R2,R14
        BNE     ExitWimp

        MOV     R14,#0                          ; ensure <> "TASK"
        STR     R14,[sp,#1*4]                   ; R2 on exit = 0
        LDR     R14,[handle,#w_parent]
        CMP     R14,#nullptr
        Rel     R14,R14,NE
        STR     R14,[SP,#2*4]                   ; R3 on exit = parent handle
        LDR     R14,[handle,#w_alignflags]
        STR     R14,[SP,#3*4]                   ; R4 on exit = alignment flags
        B       ExitWimp

openidentifier2  DCB     "TASK"                 ; magic word for new form of Wimp_GetWindowState
      |
        B       ExitWimp
      ]



SWIWimp_GetWindowOutline
        MyEntry "GetWindowOutline"
;
        LDR     handle,[userblk]
        BL      checkhandle_iconbar
;
        ADDVC   R14,handle,#w_x0    ; get system coordinates
        LDMVCIA R14,{x0,y0,x1,y1}
        ADDVC   R14,userblk,#u_ow0
        STMVCIA R14,{x0,y0,x1,y1}
;
        B       ExitWimp


;-----------------------------------------------------------------------------
; get window info - copy data into the user's buffer
;-----------------------------------------------------------------------------

; In    handle -> window info
;       userblk -> buffer to receive data
;       NE => just return window header
;       EQ => return list of icons too
; Out   [userblk,#u_ow0..] = window status
;       R1,R2,R3 corrupted

int_get_window_info
        MOV     R0,#w_size
        LDREQ   R1,[handle,#w_nicons]   ; if we need icons, increase size
        ADDEQ   R0,R0,R1,LSL #i_shift
        MOV     R1,#0                   ; don't mind about panes
        B       getwindow1

;..............................................................................

; In    handle -> window info
;       userblk -> block to receive data
; Out   [userblk,#u_ow0..] = window status
;       R1,R2,R3 corrupted

int_get_window_state
        MOV     R1,#0                   ; don't mind about panes

go_get_window_state
        MOV     R0,#w_st1               ; end of status info

; In    R0 = number of bytes of data required
;       R1 = flags for calc_w_status2

getwindow1
        Push    LR
;
        BL      calc_w_status2          ; get flags and bhandle (using R1)
;
        Rel     R14,handle
        Pull    "PC",VS
        STR     R14,[userblk,#u_handle]
;
        ADD     R1,userblk,#u_ow0       ; on to window data
        ADD     R2,handle,#w_st0        ; start of status info
;
; Copy the window part
;
        CMP     R0, #w_size
        ADDLS   R3,handle,R0
        ADDHI   R3,handle,#w_size
cplp9   LDR     R14,[R2],#4
        STR     R14,[R1],#4
        CMP     R2,R3
        BCC     cplp9
;
; Copy the icon part if necessary
;
        SUBS    R0,R0,#w_size
        Pull    PC,LS
        LDR     R2,[handle,#w_icons]
        ADD     R3,R2,R0
cplp10  LDR     R14,[R2],#4
        STR     R14,[R1],#4
        CMP     R2,R3
        BCC     cplp10
;
        Pull    PC


;;----------------------------------------------------------------------------
;; Routines for reading / writing icon states
;;----------------------------------------------------------------------------

SWIWimp_SetIconState
        MyEntry "SetIconState"
;
        LDR     handle,[userblk]
        AcceptLoosePointer_Neg handle,-2
        CMP     handle,#nullptr
        CMPNE   handle,#-2
        LDREQ   handle,iconbarhandle    ; allow access to icon bar
        BL      checkhandle
        BVS     ExitWimp
        ADD     R14,userblk,#4
        LDMIA   R14,{R0,R1,R2}          ; input parameters
        BL      int_set_icon_state
        B       ExitWimp

;
; This routine also does some graphics
;

int_set_icon_state
        Push    "R2-R9,LR"
;
        Debug   ic,"Set_Icon_State: icon, EOR, BIC, handle:",R0,R1,R2,handle
;
        LDR     R14,[handle,#w_nicons]
        CMP     R0,R14
        BCS     exitseticon                     ; ignore illegal handles
;
        LDR     R14,[handle,#w_icons]
        ADD     R0,R14,R0,ASL #i_shift          ; R0 --> icon defn
;
        LDR     R14,[R0,#i_flags]
        BIC     R14,R14,R2                      ; clear bits to change
        EOR     R14,R14,R1                      ; EOR with new bits
        STR     R14,[R0,#i_flags]               ; store new data

        Debug   ic,"Set_Icon_State: new state:",R14
;
        LDMIA   R0,{x0,y0,x1,y1}

        Push    R14                     ; icon flags
;
; now just redraw the icon - if 'funny', get the user to do it
;
        BL      int_update_window               ; get rectangle list
        Pull    R14
        BVS     exitseticon
        TST     R14,#if_funnyicon
        BNE     mustredraw
;      [ outlinefont                    ;I don't think this is necessary (nk)
;        LDR     R0,systemfont
;        TEQ     R0,#0
;        TSTNE   R14,#if_filled:OR:if_sprite:OR:if_fancyfont
;        TSTEQ   R14,#if_filled:OR:if_sprite:OR:if_fancyfont
;     |
;        TST     R14,#0
        TST     R14,#if_filled:OR:if_sprite
;      ]
        BEQ     mustredraw                      ; be careful of inverting

; new for iconbar stuff

        TST     R14,#if_text
        BEQ     uiclp

 [ BlendedFonts :LAND: RO4
	; any selectable text+sprite icons must be fully redrawn
	LDR	r0,ThreeDFlags
	TST	r0,#ThreeDFlags_NoFontBlending
	BNE	%FT01

	AND	r0,r14,#if_buttontype
	TEQ	r0,#4 :SHL: ib_buttontype
	TEQNE	r0,#5 :SHL: ib_buttontype
	TEQNE	r0,#7 :SHL: ib_buttontype
	TEQNE	r0,#8 :SHL: ib_buttontype
	TEQNE	r0,#9 :SHL: ib_buttontype
	TEQNE	r0,#10 :SHL: ib_buttontype
	TEQNE	r0,#11 :SHL: ib_buttontype
	BEQ	mustredraw
01
 ]

 [ ThreeDPatch
	; if we get to here then it's a text+sprite unfilled icon
	LDR	r0,ThreeDFlags
	TST	r0,#ThreeDFlags_NoIconBgInTransWindows
	BEQ	%FT90

	LDRB	r0,[handle,#w_wbcol]
	TEQ	r0,#&FF
	BEQ	mustredraw
90
 ]
        LDR     R14,iconbarhandle
        Abs     R14,R14
        TEQ     handle,R14
        BEQ     mustredraw
uiclp
        BL      int_get_rectangle
        BVS     exitseticon
        TEQ     R0,#0
        BNE     uiclp
exitseticon
        Pull    "R2-R9,PC"

mustredraw
        BL      markinvalidrects
        BL      losewindowrects
        B       exitseticon


;;----------------------------------------------------------------------------
;; Get info relating to an icon (coords, flags and name)
;;----------------------------------------------------------------------------

SWIWimp_GetIconState
        MyEntry "GetIconState"
;
        LDR     handle,[userblk]
        AcceptLoosePointer_Neg handle,-2
        CMP     handle,#nullptr
        CMPNE   handle,#-2
        LDREQ   handle,iconbarhandle    ; allow access to icon bar
        BL      checkhandle
        BVS     ExitWimp
        LDR     R0,[userblk,#4]         ; icon handle
        BL      int_get_icon_state
;
        ADD     R14,userblk,#8          ; skip window and icon handles
        STMIA   R14!,{x0,y0,x1,y1}
        STMIA   R14,{R1,R2,R3,R4}
;
        B       ExitWimp                ; result in R0

;
; Exit:  x0,y0,x1,y1 = coords of icon
;        R1 = flags
;        R2,R3,R4 = data
;

int_get_icon_state
        Push    "LR"
;
        LDR     R2,[handle,#w_nicons]
        CMP     R0,R2
        MOVHS   R1,#is_deleted          ; does not exist!
        MOVHS   R2,#cr                  ; null name (just in case)
        Pull    "PC",HS
        LDR     R14,[handle,#w_icons]
        ADD     R14,R14,R0,ASL #i_shift
        LDMIA   R14!,{x0,y0,x1,y1}
        LDMIA   R14,{R1,R2,R3,R4}
;
        Pull    "PC"

;;----------------------------------------------------------------------------
;; Find list of icons satisfying conditions
;; Entry:  R0 = window handle
;;         userblk --> buffer to contain list (was R1)
;;         R2 = bits to consider
;;         R3 = desired values in those bits
;; Exit :  userblk --> list of matching handles (terminated by -1)
;;----------------------------------------------------------------------------

SWIWimp_WhichIcon

        MyEntry "WhichIcon"

        MOV     handle,R0               ; R0 < 0 => iconbar
        BL      checkhandle_iconbar
        BLVC    int_which_icon
        B       ExitWimp

int_which_icon
        Push    "R4-R7,LR"
;
        MOV     R4,#0
        LDR     R5,[handle,#w_icons]
        ADD     R5,R5,#i_flags
        LDR     R6,[handle,#w_nicons]
        MOV     R7,userblk
whiclp
        CMP     R4,R6
        BHS     endwhlp
        AcceptLoosePointer_Neg R0,nullptr2
        CMP     R0,#nullptr2            ; if iconbar,
        BNE     %FT01                   ; check that the task owns this icon
        Push    "R1,R2,R7"
        BL      findicon                ; R2 -> iconblock
        LDREQ   R14,[R2,#icb_taskhandle]
        LDREQ   R1,taskhandle
        TEQEQ   R1,R14
        Pull    "R1,R2,R7"
        ADDNE   R5,R5,#i_size
        BNE     %FT02

01      LDR     R14,[R5],#i_size
        AND     R14,R14,R2              ; which bits are interesting
        TEQ     R14,R3                  ; what to match with
        STREQ   R4,[R7],#4              ; put in list
02      ADD     R4,R4,#1
        B       whiclp
endwhlp
        MOV     R14,#nullptr
        STR     R14,[R7]                 ; terminator
;
        Pull    "R4-R7,PC"


;;----------------------------------------------------------------------------
;; Routine to allow the user (and the Wimp!) to drag boxes
;; SWI Wimp_DragBox
;; Entry:  R1 =0 or -1 ==> just cancel current drag operation
;;         R1 --> block
;;                +0  window handle (not used if dragtype >= 5)
;;                +4  drag type
;;                +8  initial box position (x0,y0,x1,y1) - screen coords
;;               +24  parent box (x0,y0,x1,y1) - screen coords
;;   if type>7:  +40  value to pass to subroutines in R12 (workspace ptr)
;;               +44  address of subroutine to call to draw box
;;               +48  address of subroutine to call to remove box
;;               +52  address of subroutine to call to move box (<=0 ==> can't)
;;                     - previous box coordinates are supplied
;;                     - the box may or may not have moved
;;
;; User-supplied subroutines:
;; Entry:  SVC mode,
;;         R0-R3 = new box coordinates (as derived by mouse movements)
;;         R4-R7 = previous box coordinates (undefined unless moving)
;; Exit:   R0-R3 = actual box coordinates (normally unchanged)
;;
;;----------------------------------------------------------------------------

                ^       0
dr_handle       #       4
dr_type         #       4
dr_initbox      #       16
dr_parentbox    #       16
dr_wsptr        #       4
dr_subr_on      #       4
dr_subr_off     #       4
dr_subr_move    #       4               ; if <=0, use undraw/draw sequence

SWIWimp_DragBox
        MyEntry "DragBox"

        AcceptLoosePointer_NegOrZero userblk,0
        CMP     userblk,userblk,ASR #31
        BLEQ    nodragging              ; preserves flags
        BEQ     ExitWimp                ; =0 or -1 ==> cancel current dragbox
;
      [ Autoscr
        LDR     R14, openidentifier2    ; watch out for extended API
        TEQ     R2, R14
        MOVEQ   R14, #0                 ; ensure <> "TASK"
        STREQ   R14, [sp, #1*4]         ; and store where it will be reloaded into R2 on exit
        MOVNE   R3, #0                  ; flags default to 0 if non-extended version of call
        Push    "R3"                    ; reload into R1 later
      ]
;
        LDR     handle,[userblk],#4     ; window handle (maybe)
        LDR     R0,[userblk],#4         ; drag type
;
        CMP     R0,#drag_scrollboth
        CMPNE   R0,#drag_subr_posn-1
        ADDHI   R14,userblk,#dr_wsptr-8
        LDMHIIA R14,{R1-R4}
        ADRHI   R14,dragwsptr
        STMHIIA R14,{R1-R4}
;
      [ Autoscr
        RSBS    R14, R0, #drag_user
        CMPLS   R0, #drag_scrollboth -1 ; LS => drag type is in range 5 through 11
                                        ; so we need only validate the window handle if dragf_inwind is set
        SETPSR  Z_bit, R14, LS          ; now NE => window drag, EQ => drag type 5-11

        LDREQ   R14, [sp]               ; get the new dragflags pushed above
        TSTEQ   R14, #dragf_anchor :OR: dragf_clip  ; window-relative bit(s) set => window handle needs validating

        BLNE    checkhandle_iconbar

        Pull    "R1"                    ; retrieve new dragflags
      |
        CMP     R0,#drag_scrollboth
        CMPNE   R0,#drag_user-1
        BLLE    checkhandle_iconbar     ; only do this if SYSTEM drag!
      ]
;
        LDMVCIA userblk,{cx0,cy0,cx1,cy1,x0,y0,x1,y1}
        BLVC    int_drag_box
        B       ExitWimp

;
; Entry:           R0 = drag type
;                  R1 = flags word (as passed in R0 to extended form of SWI)
;     cx0,cy0,cx1,cy1 = initial drag box position
;                       not used if drag type = 1,2,3,4 or 7
;         x0,y0,x1,y1 = parent box
;              handle --> window definition (only if R0<=4)
;         [mousexpos] = current mouse x-coordinate
;         [mouseypos] = current mouse y-coordinate
;
; Drag types:
;       1       drag window position
;       2       drag window size
;       3       drag horizontal scroll bar
;       4       drag vertical scroll bar
;       5       user drag - box within parent box
;       6       user drag - rubber box with pointer in parent box
;       7       user drag - invisible box with pointer in parent box
;       8       user-supplied subroutine, fixed box within parent
;       9       user-supplied subroutine, rubber box within parent
;      10       user-supplied subroutine, fixed box, disregard buttons
;      11       user-supplied subroutine, rubber box, disregard buttons
;

int_drag_box
        Push    "LR"

; if a dragbox starts, cancel any pending double-click state

        LDR     R14,mouseflags
        TST     R14,#mf_wait2clicks
        BICNE   R14,R14,#mf_wait2clicks
        STRNE   R14,mouseflags
        BLNE    doubleptr_off                   ; only changes ptr if shape = 1

        LDR     R14,mousexpos
        STR     R14,dragoldx
        LDR     R14,mouseypos
        STR     R14,dragoldy
        MOV     R14,#0
        STRB    R14,continueflag
        B       %FT01

int_drag_box_continue                   ; treat mouse as being at last posn
        Push    "R0,LR"

        LDR     R14,dragoldx            ; make these coordinates relative
        LDR     R0,dragx1
        SUB     R14,R14,R0
        STR     R14,dragoldx

        LDR     R14,dragoldy            ; these offsets must be maintained
        LDR     R0,dragy0
        SUB     R14,R14,R0
        STR     R14,dragoldy

        MOV     R14,#1
        STRB    R14,continueflag

        Pull    "R0"
01                                      ; come here from int_drag_box
        LDRB    R14,continueflag        ; don't cancel drag if continuing!
        CMP     R14,#0
        BLEQ    nodragging
;
      [ Autoscr
        LDRNE   R1, dragflags           ; keep old dragflags if continuing

        CMP     R0, #drag_scrollboth
        CMPNE   R0, #drag_user - 1
        BICLS   R1, R1, #dragf_anchor :OR: dragf_clip  ; window-relative dragging is not applicable to window drags!
        STR     R1, dragflags           ; store new dragflags

        RSBS    R14, R0, #drag_user
        CMPLS   R0, #drag_scrollboth -1
        SETPSR  Z_bit, R14, LS          ; NE => window drag, EQ => drag type 5-11
        LDREQ   R14, dragflags
        TSTEQ   R14, #dragf_anchor :OR: dragf_clip  ; now NE => window handle is needed later

        Rel     R14, handle, NE
        MOVEQ   R14, #0
        STR     R14, draghandle         ; ensure handle = 0 if we won't be using it
        Debug   autoscr, "New draghandle =", #draghandle

        CMP     R0, #drag_scrollboth
        CMPNE   R0, #drag_user - 1
      |
        CMP     R0,#drag_scrollboth
        CMPNE   R0,#drag_user-1
        Rel     R14,handle,LS
        MOVHI   R14,#0
        STR     R14,draghandle          ; ensure handle=0 if not window drag
      ]
        LDRLE   R14,[handle,#w_taskhandle]  ; remember who started it!
        LDRHI   R14,taskhandle              ; no window assoc. if user drag
        STR     R14,dragtask
;
        TEQ     R0,#drag_posn           ; if not moveable, can't drag posn/size
        TEQNE   R0,#drag_size
        LDREQ   R14,[handle,#w_flags]
        TSTEQ   R14,#wf_moveable
        Pull    "PC",EQ
;
        STR     R0,dragtype
;
        TEQ     R0,#drag_user3          ; if no visible box,
        LDREQ   cx0,dragoldx            ; box is based on mouse position
        LDREQ   cy0,dragoldy
        LDREQ   cx1,dragoldx            ; NB: not suitable for int_drag_box_continue
        LDREQ   cy1,dragoldy
;
; work out initial position of the outline and parent boxes
;
        TEQ     R0,#drag_posn
        TEQNE   R0,#drag_size
        ADDEQ   R14,handle,#w_x0
        LDMEQIA R14,{cx0,cy0,cx1,cy1}   ; if posn or size, start with outline
;
; calculate offset to theoretical (bodged) coords
;
        Push    "cx0,cy0,cx1,cy1"
;
        ADDEQ   R14,handle,#w_wax0
        LDMEQIA R14,{x0,y0,x1,y1}
        SUBEQ   cx0,x0,cx0              ; can corrupt these since set later
        SUBEQ   cy0,y0,cy0
        SUBEQ   cx1,x1,cx1
        SUBEQ   cy1,y1,cy1
        MOVNE   cx0,#0
        MOVNE   cy0,#0
        MOVNE   cx1,#0
        MOVNE   cy1,#0
        ADR     R14,dragoffx0
        STMIA   R14,{cx0,cy0,cx1,cy1}   ; set up offset to theoretical coords
;
        Pull    "cx0,cy0,cx1,cy1"
;
        TEQ     R0,#drag_hscroll
        BLEQ    draghscrollcoords        ; set up cx0,cy0,cx1,cy1,x0,y0,x1,y1
        TEQ     R0,#drag_vscroll
        BLEQ    dragvscrollcoords        ; (ditto)
        TEQ     R0,#drag_scrollboth
        BLEQ    pointeroff
        BLEQ    draghvscrollcoords
;
        ADR     R14,dragx0
        STMIA   R14,{cx0,cy0,cx1,cy1}   ; outline box
;
; if drag_posn, check whether window has wf_nochecks set
;
        TEQ     R0,#drag_posn
        BNE     %FT01
;
      [ ChildWindows
        Push    "cx0,cy0,cx1,cy1"
        LDR     cx0,[handle,#w_parent]
        BL      getparentbounds
        Push    "cx0,cy0,cx1,cy1"
        Pull    "x0,y0,x1,y1"
        Pull    "cx0,cy0,cx1,cy1"       ; now x0,y0,x1,y1 = bounding box
      ]

        LDR     R14,[handle,#w_flags]
        TST     R14,#wf_nochecks        ; if not bounded, or (parent=-1 and sysflags_nobounds set)
      [ ChildWindows
        BNE     setpointerbox

        LDR     R14,[handle,#w_parent]
        CMP     R14,#nullptr
        BNE     %FT01                   ; restrict if window is bounded and parent <> -1
      ]
        LDREQB  R14,sysflags
        TSTEQ   R14,#sysflags_nobounds
      [ :LNOT: ChildWindows
        ADR     R14,scrx0
        LDMIA   R14,{x0,y0,x1,y1}
      ]
        MOVEQ   y0,#sz_scrbot           ; exclude icon bar (unless wf_nochecks)
        BNE     setpointerbox           ; just ensure pointer is on-screen
                                ; NB: can't do this within int_drag_box_continue
01
;
; if drag type = drag_size, do more work on the bounding box
;
        TEQ     R0,#drag_size
        BNE     %FT01
;
        ADD     R14,handle,#w_wax0
        LDMIA   R14,{cx0,cy0,cx1,cy1}
        BL      minmaxwindow            ; x0,y0,x1,y1 = min/max size
        Pull    "PC",VS
        ADD     x0,cx0,x0               ; x0 = wax0 + msx0
        SUB     R14,cy1,y0              ; y1 = way1 - msy0
        ADD     x1,cx0,x1               ; x1 = wax0 + msx1
        SUB     y0,cy1,y1               ; y0 = way1 - msy1
        MOV     y1,R14                  ; avoid propagation
;
        LDR     R14,[handle,#w_x1]
        ADD     x1,x1,R14
        SUB     x1,x1,cx1               ; x1 = x1 + (w_x1-w_wax1)
        LDR     R14,[handle,#w_y0]
        ADD     y0,y0,R14
        SUB     y0,y0,cy0               ; y1 = y1 + (w_y0-w_way0)
;
        LDR     R14,[handle,#w_flags]   ; if extent is flexible, remove bounds
        TST     R14,#wf_rubbery_wex1
        MOVNE   x1,#bignum              ; later clipped to screen size
        TST     R14,#wf_rubbery_wey0
        MOVNE   y0,#-bignum
;
        LDR     cx0,[handle,#w_x0]      ; restrict window size to screen size
        LDR     cy0,[handle,#w_y1]
        LDR     cx1,scrx1
        LDR     cy1,scry1
        ADD     cx0,cx0,cx1
        SUB     cy0,cy0,cy1
        CMP     x1,cx0
        MOVGT   x1,cx0
        CMP     y0,cy0
        MOVLT   y0,cy0
;
        LDR     cx0,[handle,#w_wax1]
        LDR     cy1,[handle,#w_way0]
        LDR     cx1,[handle,#w_x1]      ; stop edge going off
        LDR     cy0,[handle,#w_y0]
01
;
; further clip the parent box to the screen size:
; if WimpFlags bit 5 clear, clip posn and size to screen unless wf_nochecks
; if WimpFlags bit 5 set, clip posn to screen unless wf_nochecks
;
      [ ChildWindows
        CMP     R0,#drag_size
        CMPNE   R0,#drag_posn
        LDREQ   R14,[handle,#w_parent]  ; NB: handle only valid for system drags
        CMPEQ   R14,#nullptr
        BNE     dontclipit
      ]
        LDRB    R14,sysflags
        TST     R14,#sysflags_offscreen
        CMPEQ   R0,#drag_size
        CMPNE   R0,#drag_posn           ; only clip posn drags to screen
        LDREQ   R14,[handle,#w_flags]   ; or windows with wf_nochecks set
        TSTEQ   R14,#wf_nochecks
        LDREQB  R14,sysflags
        TSTEQ   R14,#sysflags_nobounds
        BNE     dontclipit
;
        Push    "cx0,cy0,cx1,cy1"
        ADR     R14,scrx0
        LDMIA   R14,{cx0,cy0,cx1,cy1}
        max     x0,cx0
        max     y0,#sz_scrbot           ; above icon bar
        min     x1,cx1
        min     y1,cy1
        Pull    "cx0,cy0,cx1,cy1"
dontclipit

; now work out the *pointer* bounding box
; here     x0,y0,x1,y1 = parent box
;      cx0,cy0,cx1,cy1 = initial position of box itself
; if a drag is continuing, we want to maintain the offset from the mouse position
; to the box, ie. dragoldx-cx1 and dragoldy-cy0 must remain constant

        LDR     R0,dragoldx
        LDR     R1,dragoldy
        LDRB    R14,continueflag
        TEQ     R14,#0                  ; if continuing, these are relative to
        ADDNE   R0,R0,cx1               ; bottom-right of box
        ADDNE   R1,R1,cy0
;
        ADD     x0,x0,R0
        SUB     x0,x0,cx0
        ADD     y0,y0,R1
        SUB     y0,y0,cy0
        ADD     x1,x1,R0
        SUB     x1,x1,cx1
        ADD     y1,y1,R1
        SUB     y1,y1,cy1
;
        STR     R0,dragoldx             ; ensure initial coords are inside box
        STR     R1,dragoldy             ; (forced in by pointerwindow)

        LDR     R14,dx
        ADD     x1,x1,R14               ; we want these coords inclusive
        LDR     R14,dy
        ADD     y1,y1,R14               ; so bodge to avoid decreasing by 1

; if sysflags_offscreen = 1, allow movement drags to go off bottom-right

        LDR     R0,dragtype             ; if dragtype = drag_posn,
        TEQ     R0,#drag_posn
      [ ChildWindows
        BNE     setpointerbox
        LDR     R14,[handle,#w_parent]
        CMP     R14,#nullptr
      ]
        LDREQ   R14,[handle,#w_flags]   ; and wf_onscreen = 0,
        TSTEQ   R14,#wf_onscreen
        LDREQB  R14,sysflags            ; and sysflags_offscreen = 1
        ANDEQ   R14,R14,#sysflags_offscreen
        TEQEQ   R14,#sysflags_offscreen
        LDREQ   x1,scrx1                ; allow the drag to go off-screen
        LDREQ   y0,scry0

setpointerbox
        BL      pointerwindow           ; c.f. graphicswindow
;
; force the rectangle to appear on the screen
;
        MOV     R14,#0
        STRB    R14,dragflag
        STRB    R14,dotdash             ; invalidate current dot-dash
        ADR     R14,dragx0
        LDMIA   R14,{x0,y0,x1,y1}       ; these coords are relevant!
        BL      yesdrag
;
        Pull    "PC"


nodragging
        EntryS  "R0-R11"
;
        LDR     R2,dragtype
        TEQ     R2,#drag_scrollboth
        BLEQ    pointeron
        TEQ     R2,#0
        BLNE    defaultwindow
        BLNE    nodrag                  ; remove drag box from screen
        MOVNE   R14,#0                  ; flags must have been preserved
        STRNE   R14,dragtype
        STRNE   R14,draghandle          ; NB: reset drag handle too
        BLNE    clearpointerwindow
;
        EXITS


;;-----------------------------------------------------------------------------
;; Pointer enabling / disabling routines
;;-----------------------------------------------------------------------------

pointeroff
        EntryS  "R0"
        ADR     R0,ptr_off
        SWI     XOS_CLI
        EXITS                           ; can't handle errors here

pointeron
        EntryS  "R0"
        ADR     R0,ptr_on
        SWI     XOS_CLI
        EXITS                           ; can't handle errors here

ptr_off DCB     "Pointer 0", 0
ptr_on  DCB     "Pointer", 0
        ALIGN


;;----------------------------------------------------------------------------
;; Routines to check the status of windows etc.
;;----------------------------------------------------------------------------


;-----------------------------------------------------------------------------
; Get the handle pointer appropriate to the relative one in 'handle'
;-----------------------------------------------------------------------------
;
; checkhandle_owner watches out for illegal access to another task's windows
; this explicitly forbids background processes from accessing their windows
;

checkhandle_owner ROUT
        EntryS  "R1"
        BL      checkhandle
        EXIT    VS
        LDR     R14,[handle,#w_taskhandle]
        LDR     R1,polltaskhandle       ; NB no-one owns the 'system' windows
        CMP     R1,R14                  ; so this check always fails!
        EXITS   EQ
        MyXError  WimpOwnerWindow         ; "access to window denied"
        EXIT
        MakeErrorBlock WimpOwnerWindow

checkhandle_iconbar
        CMP     handle,#-2
        LDREQ   handle,iconbarhandle    ; drop through

checkhandle
        EntryS  "R0,R1"
;
; Check the window structure for being inside valid memory
;
        Abs     handle,handle
        TST     handle,#3
        BNE     errhandle ; If it's not word aligned, it's definitely invalid
        MOV     R0,handle
        ADD     R1,R0,#w_size
        Debug   valid,"WIMP validating: ",r0,r1
        SWI     XOS_ValidateAddress
        BVS     errhandle
        BCS     errhandle
;
; Check the guard word
;
        LDR     R0,[handle,#w_guardword]
        LDR     R1,=w_guardword_valid
        CMP     R0,R1
;
; It's clean
;
        EXITS   EQ

errhandle
        Debug   err,"**** Task, bad window handle",#taskhandle,handle

        ; It's dirty
        MyXError  WimpBadHandle
        STR     R0,[sp,#Proc_RegOffset]
        EXIT
        MakeErrorBlock WimpBadHandle
        LTORG


;-----------------------------------------------------------------------------
; calc_w_status - work out window flags (top window)
; Entry:  R1 = flag bits (if any set, then ignore window in stack)
;-----------------------------------------------------------------------------

calc_w_status   ROUT
        MOV     R1,#0                   ; default entry sets R1 = 0

calc_w_status2
;
; This code must be brought up-to-date to deal with child windows
; Note that it no longer affects the ws_open bit - that is set in open/close window
;
      [ ChildWindows

        Push    "R0,LR"
;
; quick check - if window is not open, clear 'top' bit and set bhandle to -1
;
        LDR     R0,[handle,#w_flags]
        MOV     R4,#nullptr                     ; initialise R4 to -1 in any case

        TST     R0,#ws_open
        BICEQ   R0,R0,#ws_top
        BEQ     calcw3
;
; scan siblings 'above' this one (possibly skipping sibling panes)
;
        ADD     R14,handle,#w_x0                ; bounding box of window
        LDMIA   R14,{x0,y0,x1,y1}

        ORR     R0,R0,#ws_top

        MOV     R3,R1                           ; hold 'ignore' flags in R3

      [ true
        ; Scan the windows above us in our own stack; we only want to ignore our *own* panes.
        ; bhandle needs to be either the window in front of us, or the first non-pane, depending upon R3.
        ; Backwards in the list sense is higher in the window-stacking sense.
        LDR     R1, [handle, #w_active_link + ll_backwards]
lookw1
        LDR     R2, [R1, #ll_backwards]
        CMP     R2, #nullptr                    ; have we reached the front of our stack?
        BEQ     lookw2

        LDR     R14, [R1, #w_flags - w_active_link]
        TST     R14, R3                         ; EQ => this window matches our criteria for bhandle
        CMPEQ   R4, #nullptr                    ; have we already determined bhandle?
        SUBEQ   R4, R1, #w_active_link          ; no, so remember bhandle
        Rel     R4, R4, EQ

        TST     R14, #wf_isapane                ; we don't want to consider our own panes as stopping us from being on top
        MOVNE   R1, R2
        BNE     lookw1
lookw2
        ; Now we have set up R4, and R1 links the first non-pane window in front of us.
        ; We're now ready to recurse up the window tree!
        Push    "R4, handle"
        MOV     handle, R0
        BL      calcw_checkwindow
        Pull    "R4, handle"

calcw3
        STR     R0, [handle, #w_flags]
        STR     R4, [handle, #w_bhandle]
        TST     R0, #ws_open

        Pull    "R0,PC"

calcw_checkwindow
; Entry: R0 = window flags of original window
;        R1 -> active link in higher window (may actually -> list header if we've reached the front of a stack)
;        x0-y1 = bounding box of original window
;        handle = window flags of original window, or those of its direct ancestor in this stack
; Exit:  R0 has ws_top cleared if R1 window (or any window above it) overlapped original window
        Entry
        LDR     R2, [R1, #ll_backwards]
        CMP     R2, #nullptr                    ; are we pointing at the list header?
        BEQ     %FT20
10
        Push    "handle"
        SUB     handle, R1, #w_active_link
        BL      checkclip                       ; LT => intersection
        Pull    "handle"

        BICLT   R0, R0, #ws_top                 ; if we've found an overlapping window, clear "uncovered" bit
        MOVGE   R1, R2                          ; otherwise try the next window up
        BLGE    calcw_checkwindow
        EXIT

20      ; R1 is pointing at the list header, we need to consider the next-highest window stack
        LDR     R1, [R1, #ll_forwards]          ; get first window in stack
        LDR     R1, [R1, #w_parent - w_active_link]
        CMP     R1, #nullptr
        EXIT    EQ                              ; if this was top-level stack, leave bit set and exit (we're uncovered!)

        LDR     R14, [R1, #w_flags]
        TST     R14, #ws_open
        BICEQ   R0, R0, #ws_top                 ; if parent is closed, children cannot be seen, so clear bit and exit
        EXIT    EQ

        TST     handle, #wf_inborder
        ADDNE   R2, R1, #w_x0
        ADDEQ   R2, R1, #w_wax0
        LDMIA   R2, {cx0, cy0, cx1, cy1}        ; cx0-cy1 now hold rectangle that original window is clippped to by new ancestor

        CMP     x0, cx0
        CMPGE   y0, cy0
        CMPGE   cx1, x1
        CMPGE   cy1, y1                         ; GE => original window isn't clipped by new ancestor
        BICLT   R0, R0, #ws_top
        EXIT    LT                              ; if clipped, clear bit and exit

        MOV     handle, R14                     ; remember the flags for this direct ancestor
        LDR     R1, [R1, #w_active_link + ll_backwards]
        BL      calcw_checkwindow               ; and try the new ancestor's higher siblings
        EXIT

      |
        ; Old code that doesn't concern itself with aunts, great-aunts etc., nor clipping by ancestors
        LDR     R2,[handle,#w_active_link+ll_backwards]
lookw1
        LDR     R1,[R2,#ll_backwards]
        CMP     R1,#nullptr
        BEQ     calcw3

        LDR     R14,[R2,#w_flags-w_active_link]
        TST     R14,R3
        CMPEQ   R4,#nullptr                     ; first non-masked window above this one is the bhandle
        SUBEQ   R4,R2,#w_active_link
        Rel     R4,R4,EQ

        LDR     R14,[R2,#w_flags-w_active_link]
        TST     R14,#wf_isapane                 ; panes never count towards the 'top' bit
        BNE     lookw2

        Push    "handle"
        SUB     handle,R2,#w_active_link
        BL      checkclip
        Pull    "handle"

        BICLT   R0,R0,#ws_top                   ; not top if it's covered
        BLT     calcw3

lookw2
        MOV     R2,R1
        B       lookw1

calcw3
        STR     R0,[handle,#w_flags]
        STR     R4,[handle,#w_bhandle]
        TST     R0,#ws_open

        Pull    "R0,PC"
      ]

      |

        Push    "R0,LR"
;
; look for this window in the stack
;
        MOV     R3,R1                   ; hold 'ignore' flags in R3
        LDR     R2,activewinds+lh_backwards
lookw1
        LDR     R1,[R2,#ll_backwards]
        CMP     R1,#nullptr
        MOVEQ   R0,#0
        BEQ     calcw1
        SUB     R2,R2,#w_active_link
        TEQ     R2,handle
        MOV     R2,R1
        BNE     lookw1
;
; store appropriate status in window data
;
        MOV     R0,#ws_open:OR:ws_top           ; assume top at this stage
calcw1
        LDR     R14,[handle,#w_flags]
        BIC     R14,R14,#ws_status
        ORR     R14,R14,R0
        STR     R14,[handle,#w_flags]           ; store value so far

; get handle of window above

calcw2

; Is there another window?
;
        LDR     R4,[R2,#ll_backwards]
        CMP     R4,#nullptr                     ; Passes on as bhandle=nullptr (top)
        BEQ     calcw3
;
; Is the next window masked out?, if so loop
;
        LDR     R14,[R2,#w_flags-w_active_link]
        TST     R14,R3
        MOVNE   R2,R4
        BNE     calcw2

        ; next window not masked, convert to bhandle
        SUB     R4,R2,#w_active_link
        Rel     R4,R4
calcw3
        STR     R4,[handle,#w_bhandle]
        TST     R0,#ws_open
        Pull    "R0,PC",EQ                      ; window is closed
;
; check whether window is top (ensure nothing (except panes) cover it)
;
        ADD     R0,handle,#w_x0                 ; bounding box of window
        LDMIA   R0,{x0,y0,x1,y1}
;
lookw2
        LDR     R1,[R2,#ll_backwards]           ; Another window to check?
        CMP     R1,#nullptr
        Pull    "R0,PC",EQ
;
        Push    "handle"                        ; Convert to handle and step to next
        SUB     handle,R2,#w_active_link
        MOV     R2,R1
        LDR     R14,[handle,#w_flags]
        AND     R14,R14,#wf_isapane
        CMP     R14,#wf_isapane                 ; panes don't count
        BLLT    checkclip
        Pull    "handle"
;
        BGE     lookw2
;
; window is not top - mark status accordingly
;
        LDR     R0,[handle,#w_flags]
        BIC     R0,R0,#ws_top                   ; not top
        STR     R0,[handle,#w_flags]
;
        Pull    "R0,PC"
      ]

;------------------------------------------------------------------------------
; checkclip - see if window indicated by handle intersects x0,y0,x1,y1
; Exit : LT --> window intersects
;------------------------------------------------------------------------------

checkclip       ROUT
        Push    "cx0,cy0,cx1,cy1,LR"
;
        ADD     R14,handle,#w_x0        ; point to outer box of window
        LDMIA   R14,{cx0,cy0,cx1,cy1}
;
        CMP     x0,cx1                  ; if x0 < cx1
        CMPLT   cx0,x1                  ; and x1 > cx0
        CMPLT   y0,cy1                  ; and y0 < cy1
        CMPLT   cy0,y1                  ; and y1 > cy0
;
        Pull    "cx0,cy0,cx1,cy1,PC"

;-----------------------------------------------------------------------------
; routine to work out outer coordinates from x0,y0,x1,y1 and flags
;-----------------------------------------------------------------------------

calc_w_x0y0x1y1 ROUT
        Push    "R1,R2,LR"
;
        LDR     R0,[handle,#w_flags]
        LDRB    R14,[handle,#w_tfcol]   ; if title fg = 255,
        TEQ     R14,#&FF                ; thin borders are not drawn
      [ ChildWindows
        LDR     R1,dx
        LDR     R2,dy
        BEQ     %FT02
      |
        Pull    "R1,R2,PC",EQ
;
        LDR     R1,dx
        LDR     R2,dy
      ]
;
        SUB     x0,x0,R1                ; line goes outside work area
        SUB     y0,y0,R2
        ADD     x1,x1,R1
        ADD     y1,y1,R2
;
;;<<        LDRB    R1,xborder
;;<<        LDRB    R2,yborder

      [ ChildWindows
01
      ]
        TST     R0,#wf_icon3
        LDRNE   R2,title_height
        ADDNE   y1,y1,R2                ; make room for title
        TST     R0,#wf_icon5
        LDRNE   R1,vscroll_width
        ADDNE   x1,x1,R1
        TST     R0,#wf_icon7
        LDRNE   R2,hscroll_height
        SUBNE   y0,y0,R2
;
        Pull    "R1,R2,PC"

      [ ChildWindows

02      TST     R0,#wf_icon3
        ADDNE   y1,y1,R2                ; must add one pixel as well
        TST     R0,#wf_icon5
        ADDNE   x1,x1,R1
        TST     R0,#wf_icon7
        SUBNE   y0,y0,R2
        B       %BT01
      ]

;
; Entry:  R0 = icon 'handle' (1-7)
;         handle --> window defn
; Exit:   x0,y0,x1,y1 = coords of icon
;

      [ ChildWindows

calc_w_iconposn  Entry  "cx0,cy0,cx1,cy1"

        BL      calc_w_iconposn2

        EXIT

calc_w_iconposn2 ROUT                   ; this version returns cx0..cy1 = 'normal' bbox, for scrollbars

      |

calc_w_iconposn ROUT

      ]

        EntryS  "R1,cx0,cy0,cx1,cy1"    ; ChildWindows

        Debuga  x1,"Calc icon posn ",r0
;
        ADD     R14,handle,#w_x0
        LDMIA   R14,{cx0,cy0,cx1,cy1}

      [ ChildWindows
        LDR     R1,[handle,#w_flags]
        BL      calc_w_iconposn_R1      ; calculate from R1 and cx0,cy0,cx1,cy1 (ie. don't look at handle data)

; If there are child window(s) with the wf_inborder bit set, move the scrollbar to make room
; The scrollbar only allows for child windows that are aligned with the left or right of the 'normal' scrollbar position
; NOTE: If you want two windows at the same end, do NOT make them both children of the main parent, as it WILL NOT WORK
;       The scrollbar will only budge over if both windows are aligned with the edge of the main parent
;       Alternatively make one window a child of the other, or both windows children of a third window (which is a child of the parent).

        CMP     R0,#7
        BEQ     %FT07

        CMP     R0,#5
        EXITS   NE

; move vertical scrollbar to make room for children with wf_inborder set

        ADD     R14,SP,#Proc_RegOffset+4
        STMIA   R14,{x0,y0,x1,y1}               ; cx0,cy0,cx1,cy1 on exit = 'normal' bbox

        LDR     R1,[handle,#w_children + lh_forwards]

01      LDR     R14,[R1,#ll_forwards]
        CMP     R14,#nullptr
        EXITS   EQ

        LDR     R14,[R1,#w_flags - w_active_link]
        TST     R14,#wf_inborder
        BEQ     %FT02

        ADD     R14,R1,#w_x0 - w_active_link
        LDMIA   R14,{cx0,cy0,cx1,cy1}
        LDR     R14,dx
        SUB     R14,x1,R14
        CMP     cx1,R14                         ; if window touches outer edge of v-scroll,
        BLT     %FT02

        CMP     cx0,x1
        CMPLT   cy0,y1
        CMPLT   y0,cy1                          ; ignore if completely outside scrollbar range
        BGE     %FT02

        LDR     R14,dy
        SUB     cy0,cy0,R14
        ADD     cy1,cy1,R14

        CMP     cy0,y0
        SUBLE   y0,cy1,R14,LSL #1               ; move scrollbar upwards if window touches the bottom

        CMP     cy1,y1
        ADDGE   y1,cy0,R14,LSL #1               ; move scrollbar downwards if window touches the top

02      LDR     R1,[R1,#ll_forwards]
        B       %BT01

; move horizontal scrollbar to make room for children with wf_inborder set

07      ADD     R14,SP,#Proc_RegOffset+4
        STMIA   R14,{x0,y0,x1,y1}               ; cx0,cy0,cx1,cy1 on exit = 'normal' bbox

        LDR     R1,[handle,#w_children + lh_forwards]

01      LDR     R14,[R1,#ll_forwards]
        CMP     R14,#nullptr
        EXITS   EQ

        LDR     R14,[R1,#w_flags - w_active_link]
        TST     R14,#wf_inborder
        BEQ     %FT02

        ADD     R14,R1,#w_x0 - w_active_link
        LDMIA   R14,{cx0,cy0,cx1,cy1}
        LDR     R14,dy
        ADD     R14,y0,R14
        CMP     cy0,R14                         ; if window touches bottom of h-scroll,
        BGT     %FT02

        CMP     y0,cy1
        CMPLT   cx0,x1
        CMPLT   x0,cx1                          ; ignore if completely outside scrollbar range
        BGE     %FT02

        LDR     R14,dx
        SUB     cx0,cx0,R14
        ADD     cx1,cx1,R14

        CMP     cx0,x0
        SUBLE   x0,cx1,R14,LSL #1               ; move scrollbar to the right if window touches the left

        CMP     cx1,x1
        ADDGE   x1,cx0,R14,LSL #1               ; move scrollbar to the left if window touches the right

02      LDR     R1,[R1,#ll_forwards]
        B       %BT01

;.............................................................................

; In    R0 = border icon number
;       R1 = window flags
;       cx0,cy0,cx1,cy1 = outer bounding box of window
; Out   x0,y0,x1,y1 = bounding box of border icon (without scrollbar adjustment for wf_inborder child windows)
;       R0 preserved, R1,cx0,cy0,cx1,cy1 corrupted

calc_w_iconposn_R1  EntryS  "R1"

      ] ; ChildWindows

        ADRL    R14,iconposndata-1
        LDRB    R14,[R14,R0]
;
;<<        LDRB    R1,xborder1             ; xborder+dx
        TST     R14,#2_1000
        MOVEQ   x0,cx0
        LDRNE   R1,vscroll_width1
        SUBNE   x0,cx1,R1
        TST     R14,#2_0010
        MOVEQ   x1,cx1
;<<        ADDNE   x1,cx0,R1
;<<        LDRB    R1,yborder1             ; yborder+dy
        TST     R14,#2_0100
        MOVEQ   y0,cy0
        LDRNE   R1,title_height1
        SUBNE   y0,cy1,R1
        TST     R14,#2_0001
        MOVEQ   y1,cy1
        LDRNE   R1,hscroll_height1
        ADDNE   y1,cy0,R1

      [ ChildWindows
        LDR     R1,[SP,#Proc_RegOffset] ; recover window flags from the stack
      |
        LDR     R1,[handle,#w_flags]
      ]
01
        ADD     PC,PC,R0,ASL #2         ; jump !
;
        DCD     0
        DCD     0                       ; extra bodge word cos 1 <= R0 <= 7
;
        ASSERT  (.-%BT01)=8+4
        B       check1                  ; back
        B       check2                  ; close
        B       check3                  ; title
        B       check4                  ; toggle
        B       check5                  ; vscroll
        B       check6                  ; resize
        B       check7                  ; hscroll
        [ IconiseButton
        B       check8                  ; iconise
        ]

check1
        LDR     R14,back_width
        ADD     x1,cx0,R14
        LDR     R14,dx
        ADD     x1,x1,R14
;
        EXITS           ; ChildWindows

check2
        LDR     R14,close_width
        ADD     x1,cx0,R14
        LDR     R14,dx
        ADD     x1,x1,R14
;
        TST     R1,#wf_icon1
        LDRNE   R14,back_width
        ADDNE   x0,x0,R14
        ADDNE   x1,x1,R14
;
        EXITS           ; ChildWindows

check3
        TST     R1,#wf_icon1
        LDRNE   R14,back_width
        ADDNE   x0,x0,R14
        TST     R1,#wf_icon2
        LDRNE   R14,close_width
        ADDNE   x0,x0,R14
      [ IconiseButton
        BEQ     %FT01
        LDR     R14, [handle, #w_parent]
        CMP     R14, #-1
        BNE     %FT01
        LDRB    R14, iconisebutton
        TEQ     R14, #0
        BEQ     %FT01
        LDR     R14,iconise_width
        SUB     x1,x1,R14
01
      ]
        TST     R1,#wf_icon4
        LDRNE   R14,vscroll_width
        SUBNE   x1,x1,R14
;
        EXITS           ; ChildWindows

check5
        TST     R1,#wf_icon4:OR:wf_icon3        ; give way to title
        LDRNE   R14,title_height
        SUBNE   y1,y1,R14
        TST     R1,#wf_icon6:OR:wf_icon7        ; give way to h_scroll
        LDRNE   R14,hscroll_height
        ADDNE   y0,y0,R14

        EXITS           ; ChildWindows

check7
        TST     R1,#wf_icon6:OR:wf_icon5
        LDRNE   R14,vscroll_width
        SUBNE   x1,x1,R14

        EXITS           ; ChildWindows

check4
check6
        Debug   x1," x0 y0 x1 y1 ",x0,y0,x1,y1

        EXITS           ; ChildWindows

      [ IconiseButton
check8
        LDR     R14,iconise_width
        SUB     x0,cx1,R14
        LDR     R14,dx
        SUB     x0,x0,R14

        TST     R1,#wf_icon4                    ; give way to toggle size icon
        LDRNE   R14,vscroll_width
        SUBNE   x0,x0,R14
        SUBNE   x1,x1,R14

        EXITS

      ]

;
; 2_1000 ==> x0 = cx1-vscroll_width , else cx0
; 2_0100 ==> y0 = cy1-title_height  , else cy0
; 2_0010 ==> x1 = undefined         , else cx1
; 2_0001 ==> y1 = cy0+hscroll_height, else cy0
;

iconposndata
        DCB     2_0110          ; back
        DCB     2_0110          ; quit
        DCB     2_0100          ; title
        DCB     2_1100          ; toggle
        DCB     2_1000          ; v_scroll
        DCB     2_1001          ; size
        DCB     2_0001          ; h_scroll
        [ IconiseButton
        DCB     2_1100          ; iconise
        ]
        ALIGN


;-----------------------------------------------------------------------------
; routine to work out minimum x/y size of a given window
; Entry:  handle --> window definition
; Exit :  R14 = minimum x/y size
;-----------------------------------------------------------------------------

      [ ChildWindows
title_minwidth  *       8
      ]


minwindowx
        Push    "R0,R1,x1,LR"
;
;<<<        LDRB    x1,xborder                      ; used throughout
;
        LDR     R1,[handle,#w_flags]
        TST     R1,#wf_icon3
        MOVEQ   R14,#0
        BEQ     minscrx                         ; check scroll bars
;
      [ ChildWindows
        TST     R1,#wf_icon1 :OR: wf_icon2
      [ true                                    ; BJGA bugfix
        MOVEQ   R14, #0                         ; allow title to shrink to zero if no back/quit and minX=1
        BEQ     %FT01
        LDRB    R0, [handle, #w_tfcol]
        TEQ     R0, #&FF                        ; check for borderless windows
        MOVNE   R14, #0
        LDREQ   R14, dx                         ; these need an extra pixel to compensate
        TSTEQ   R1, #wf_icon5
        MOVEQ   R14, R14, LSL #1                ; or two pixels if vscroll is absent too!
        ADD     R14, R14, #title_minwidth
01
      |
        MOVNE   R14,#title_minwidth             ; min title size if back or quit are present
        MOVEQ   R14,#0                          ; allow title to shrink to zero if no back/quit and minX=1
      ]
        LDR     R0,[handle,#w_minx]             ; word contains x- and y- min
        MOVS    R0,R0,LSL #16                   ; if x=0, use title width
      |
        LDR     R14,[handle,#w_minx]            ; word contains x- and y- min
        MOVS    R14,R14,LSL #16                 ; if x=0, use title
        MOVNE   R14,#8                          ; reasonable size
      ]
        BNE     ignoretitle                     ; ignore title if specific
;
        Push    "R1,R2,cx1"
        LDR     R14,[handle,#w_areaCBptr]       ; in case title is a sprite
        STR     R14,areaCBptr
        LDR     R1,[handle,#w_titleflags]
        ADD     R2,handle,#w_title
        BL      seticonptrs                     ; sets up [spritename] etc.
;
        MOV     cx1,#0                          ; width of title portion
;
        TST     R1,#if_sprite
        BEQ     %FT01
        ASSERT  cx1=R4
        Push    "R3"                            ; R1 = icon flags
        BL      spritesize                      ; R3,R4 = sprite size (pixels)
        MOV     cx1,R3
        Pull    "R3"
        B       %FT02
01                                      ; 320nk auto title bar width
        TST     R1,#if_text
        BLNE    textwidth
02
        LDR     R14,dx
        ADD     R14,cx1,R14,ASL #1              ; R14 <- cx1 + 2*dx
        Pull    "R1,R2,cx1"
        BVS     %FT99

ignoretitle
        TST     R1,#wf_icon1                    ; back
        LDRNE   x1,back_width
        ADDNE   R14,R14,x1
        TST     R1,#wf_icon2                    ; quit
        LDRNE   x1,close_width
        ADDNE   R14,R14,x1
      [ IconiseButton
        BEQ     %FT01
        LDR     x1, [handle, #w_parent]
        CMP     x1, #-1
        BNE     %FT01
        LDRB    x1, iconisebutton
        TEQ     x1, #0
        BEQ     %FT01
        LDR     x1,iconise_width                ; + iconise
        ADD     R14,R14,x1
01
      ]
        TST     R1,#wf_icon5                    ; v_scroll
        BNE     %FT01
        TST     R1,#wf_icon4                    ; toggle
        LDRNE   x1,vscroll_width
        ADDNE   R14,R14,x1
01
minscrx
        TST     R1,#wf_icon7                    ; h_scroll
        BEQ     gotminx

      [ ChildWindows
        LDR     R0,[handle,#w_minx]
        MOV     R0,R0,LSL #16
        MOVS    R0,R0,LSR #16                   ; bottom 16 bits are min x
        MOVNE   R0,#0
        BNE     skiphscroll                     ; scrollbars can now go to zero size (if minx<>0)
      ]
        LDRB    R0,scroll_minxbar
        LDR     x1,left_width
        ADD     R0,R0,x1
        LDR     x1,right_width
        ADD     R0,R0,x1                        ; allow for scroll arrows
;<<<       ADD     R0,R0,x1,LSL #1                 ; allow for 2 scroll arrows
;
      [ hvblip
        LDR     x1,hscroll_blipwidth
        ADD     R0,R0,x1
      ]

      [ ChildWindows
skiphscroll                                     ; scrollbar can go to zero width, but we must still consider the size box
      ]

        TST     R1,#wf_icon5                    ; v_scroll
        BNE     %FT02
        TST     R1,#wf_icon6                    ; size
        LDRNE   x1,vscroll_width
        ADDNE   R0,R0,x1
02
        CMP     R14,R0                          ; allow for bottom scroll bar
        MOVLT   R14,R0
gotminx
        ASSERT  (w_minx:AND:3)=0
        LDR     R1,[handle,#w_minx]             ; assume word-aligned
        MOV     R1,R1,LSL #16
        MOV     R1,R1,LSR #16                   ; bottom 16 bits are min x
      [ ChildWindows
        CMP     R1,#1
        MOVEQ   R1,#0                           ; 1 is a special value (treat as 0 here)
      ]
        CMP     R14,R1
        MOVLT   R14,R1                          ; take max of the two
99
        STRVS   R0,[sp]
        Pull    "R0,R1,x1,PC"


minwindowy
        Push    "R1,y1,LR"

      [ ChildWindows
        LDR     R14,[handle,#w_minx]
        MOVS    R14,R14,LSR #16         ; R14 = min y size specified
        MOVNE   R14,#0                  ; we can potentially go to zero size if miny=1
        LDREQ   R14,dy
        MOVEQ   R14,R14,ASL #1          ; back compatibility: R14 <- 2*dy  [ I don't know where this came from! ]
      |
;
;<<<        LDRB    y1,yborder
;
        LDR     R14,dy
        MOV     R14,R14,ASL #1          ; R14 <- 2*dy
      ]

        LDR     R1,[handle,#w_flags]
        TST     R1,#wf_icon5
        BEQ     %FT02

      [ ChildWindows
        LDR     y1,[handle,#w_minx]
        MOVS    y1,y1,LSR #16
        BNE     skipvscroll             ; scrollbar can go to zero height if minY > 0
      ]

        LDRB    R14,scroll_minybar
        LDR     y1,up_height
        ADD     R14,R14,y1
        LDR     y1,down_height
        ADD     R14,R14,y1              ; allow for scroll arrows
;<<        ADD     R14,R14,y1,LSL #1       ; allow for 2 scroll arrows
;
      [ hvblip
        LDR     y1,vscroll_blipheight
        ADD     R14,R14,y1
      ]

      [ ChildWindows
skipvscroll                             ; we must still consider the size and toggle, even if the scrollbar can go to zero
      ]

        TST     R1,#wf_icon7            ; if no h_scroll
        BNE     %FT01
        TST     R1,#wf_icon6            ; and size
        LDRNE   y1,hscroll_height
        ADDNE   R14,R14,y1
01
        TST     R1,#wf_icon3            ; if no title,
        BNE     %FT02
        TST     R1,#wf_icon4            ; and toggle
        LDRNE   y1,title_height
        ADDNE   R14,R14,y1

02
        ASSERT  (w_minx:AND:3)=0
        ASSERT  w_miny=w_minx+2
        LDR     R1,[handle,#w_minx]
        MOV     R1,R1,LSR #16           ; R1 = min y size specified
      [ ChildWindows
        CMP     R1,#1
        MOVEQ   R1,#0                   ; 1 is a special case (treat as 0 here)
      ]
        CMP     R14,R1
        MOVLT   R14,R1                  ; take max of the two

        Pull    "R1,y1,PC"

;.............................................................................

; In    handle -> window definition
; Out   x0,y0 = absolute minimum x,y size of window (determined purely by which borders are present)

      [ ChildWindows

minwindow_borders  Entry  "R1"

        MOV     x0,#0
        MOV     y0,#0

        LDR     R1,[handle,#w_flags]

        TST     R1,#wf_icon1                    ; back
        LDRNE   x0,back_width

        TST     R1,#wf_icon2                    ; quit
        LDRNE   R14,close_width
        ADDNE   x0,x0,R14

        TST     R1,#wf_icon1 :OR: wf_icon2
        TSTNE   R1,#wf_icon3
        ADDNE   x0,x0,#title_minwidth           ; title must be this big if back or quit present

        AND     R14,R1,#wf_icon4 :OR: wf_icon5
        TEQ     R14,#wf_icon4                   ; toggle with no v-scroll
        LDREQ   R14,vscroll_width
        ADDEQ   x0,x0,R14

        AND     R14,R1,#wf_icon5 :OR: wf_icon6
        TEQ     R14,#wf_icon6                   ; size with no v-scroll
        LDREQ   R14,vscroll_width
        MOVNE   R14,#0
        max     x0,R14                          ; see if this is greater than current min x

        AND     R14,R1,#wf_icon3 :OR: wf_icon4
        TEQ     R14,#wf_icon4                   ; toggle with no title
        LDREQ   y0,title_height

        AND     R14,R1,#wf_icon6 :OR: wf_icon7
        TEQ     R14,#wf_icon6                   ; size with no h-scroll
        LDREQ   R14,hscroll_height
        ADDEQ   y0,y0,R14

        EXIT
      ]

;.............................................................................

; In    handle -> window definition
; Out   x0,y0 = minimum window size
;       x1,y1 = maximum window size

minmaxwindow
        Push    "LR"
;
        ADD     R14,handle,#w_wex0
        LDMIA   R14,{x0,y0,x1,y1}
        SUB     x1,x1,x0
        SUB     y1,y1,y0
        BL      minwindowx
        MOVVC   x0,R14
        BLVC    minwindowy
        MOVVC   y0,R14
;
        Pull    "PC"                    ; pass back V


;;-----------------------------------------------------------------------------
;; Wimp_SetColourMapping
;; Entry:  R1 -> 16 words contining physical entries for Wimp colours / 0 = no translation  / -1 = default
;;         R2 -> 2  byte transfer table mapping 1BPP sprites to palette above / 0 = current / -1 = default
;;         R3 -> 4  byte transfer table mapping 2BPP sprites to palette above / 0 = current / -1 = default
;;         R4 -> 16 byte transfer table mapping 4BPP sprites to palette above / 0 = current / -1 = default
;;         R5-R7 reserved and must be zero
;;-----------------------------------------------------------------------------

SWIWimp_SetColourMapping

        MyEntry "SetColourMapping"
;
        Push    "R8-R11"

        CMP     R1,#-1                  ; = -1 => use default palette table
        ADREQL  R1,emergencypalette
        STR     R1,usephyspaltable      ;  = 0 => use current palette table

        CMP     R1,#0                   ; if table supplied then copy data
        MOVNE   R8,R1
        ADRNEL  R9,physpaltable
        LDRNE   R10,=?physpaltable      ; R10 = number of bytes to copy
        BLNE    set_copyblock
;
        CMP     R2,#0                   ; use the default 1BPP mapping table
        ADREQL  R2,transtable1
        CMP     R2,#-1
        ADREQ   R2,map1bpp
        CMP     R3,#0                   ; use the default 2BPP mapping table
        ADREQL  R3,transtable2
        CMP     R3,#-1
        ADREQ   R3,map2bpp
        CMP     R4,#0                   ; use the default 4BPP mapping table
        ADREQL  R4,transtable4
        CMP     R4,#-1
        ADREQ   R4,map4bpp
;
        ASSERT  ?transtable1= ?map1bpp
        ASSERT  ?transtable2= ?map2bpp
        ASSERT  ?transtable4= ?map4bpp
;
        ASSERT  :INDEX:transtable2 = (:INDEX:transtable1 + ?map1bpp)
        ASSERT  :INDEX:transtable4 = (:INDEX:transtable2 + ?map2bpp)
;
        Push    "R2-R4"
        ADRL    R9,transtable1          ; R9 -> start of transfer area
;
        Pull    "R8"
        LDR     R10,=?transtable1
        BL      set_copyblock           ; copy mapping table for 1BPP
        Pull    "R8"
        LDR     R10,=?transtable2
        BL      set_copyblock           ; copy mapping table for 2BPP
        Pull    "R8"
        LDR     R10,=?transtable4
        BL      set_copyblock           ; copy mapping table for 4BPP
;
        LDR     R2,pixtable_at
        CMP     R2,#0                   ; has a pixtable been setup?
        MOVNE   R0,#ModHandReason_Free
        BLNE   XROS_Module              ; attempt to release - ignore errors

        MOV     R2,#0
        STR     R2,pixtable_at          ; mark the pixtable as being zapped!
        MOV     R2,#-1                  ; and the PixTrans mode
        STR     R2,sprite_lastmode
;
        LDR     R2,tpixtable_at
        CMP     R2,#0                   ; has a tool pixtable been setup?
        MOVNE   R0,#ModHandReason_Free
        BLNE   XROS_Module              ; attempt to release - ignore errors

        MOV     R2,#0
        STR     R2,tpixtable_at         ; mark the tool pixtable as being zapped!
        MOV     R2,#-1                  ; and the tool PixTrans mode
        STR     R2,tsprite_lastmode
;
        STRB    R2,tsprite_needsregen   ; re-calcuate border based information
;
        LDR     R2,log2bpp              ; is it <8BPP?
        CMP     R2,#3
        MOVLT   cx0,#-bignum            ; force a redraw of the screen
        MOVLT   cy0,#-bignum
        MOVLT   cx1,#bignum
        MOVLT   cy1,#bignum
        BLLT    markinvalid_cx0cy0cx1cy1
;
        Pull    "R8-R11"                ; preserve 'em
        B       ExitWimp

; in    R8 -> source data
;       R9 -> destination block
;       R10 = number of bytes to be copied
; out   R8,R9 updated
;       R10 =-1
;       R11 corrupt

set_copyblock
        SUBS    R10,R10,#1              ; any more bytes left?
        LDRPLB  R11,[R8],#1
        STRPLB  R11,[R9],#1             ; yup ... so copy it away
        BPL     set_copyblock
        MOV     PC,LR

;..............................................................................

; mapping tables - real colour to wimp colour

map1bpp = 0,7
map2bpp = 0,2,5,7
map4bpp = 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
        ALIGN

        ASSERT  ?map1bpp = 2
        ASSERT  ?map2bpp = 4
        ASSERT  ?map4bpp = 16

;;-----------------------------------------------------------------------------
;; Initialise the handling of physical mapping tables etc...
;; NB: corrupts R0-R3
;;-----------------------------------------------------------------------------

initphyspalmap ROUT

        MOV     R0,#0
        STR     R0,usephyspaltable      ; don't indirect to seperate wimp palette

        ASSERT  ?transtable1= ?map1bpp
        ASSERT  ?transtable2= ?map2bpp
        ASSERT  ?transtable4= ?map4bpp

        ASSERT  :INDEX:transtable2 = (:INDEX:transtable1 + ?map1bpp)
        ASSERT  :INDEX:transtable4 = (:INDEX:transtable2 + ?map2bpp)

        ADRL    R0,transtable1          ; R0 -> transtable block
        ADR     R1,map1bpp              ; R1 -> table to copy
        MOV     R2,#(?transtable1 +?transtable2 +?transtable4)

00      LDRB    R3,[R1],#1
        STRB    R3,[R0],#1
        SUBS    R2,R2,#1
        BNE     %BT00                   ; loop back copying default tables for mapping Wimp colours

        MOV     PC,LR

;;-----------------------------------------------------------------------------
;; Return a pointer to the real physical palette
;; out  R14 -> palette table to be used
;;-----------------------------------------------------------------------------

getpalpointer ROUT
        Push    "R14"
        LDR     R14,usephyspaltable     ; indirected palette?
        TEQ     R14,#0
        ADREQL  R14,paltable            ; R14 -> standard palette
        ADRNEL  R14,physpaltable        ; R14 -> remapped palette
        Pull    "PC"

;---------------------------------------------------------------------------
; Push font string. Scans an icon string and determines if it must be copied
; in order to be painted successfully. This is so if (a) the string contains
; any symbol font characters; (b) if it is terminated by a control code
; other than 0, 10, 13 (font manager terminators); (c) if it needs a
; replacement character (validation 'D' command). The copy is made on the
; stack, if necessary.
;
; Now always copies, prepending Font_ComandFont, <start font>
;
; in    R1 = flags for icon
;       R2 -> string to paint
;       R3 = handle for icon font
;       R7 -> control sequence list (-1 indicates no list)
;       R9 = number of characters to include, iff is_deleted is set in R1
; out   R1 -> string to use
;       R7 = difference in stack (*word aligned*)
;       all others preserved
;
; R7 on entry points to a list of control sequences that need inserting into
; the pushed font string. The list has the following form:
;       +0 = byte index into original string where sequence is to be inserted
;            these must be kept in ascending order
;       +4 = number of bytes in sequence
;       +8 = bytes to insert (eg colour change), padded to next word boundary
; The sequence is terminated by the word -1. The list may need to be writeable.
;---------------------------------------------------------------------------

State_CurrentFont       *       0
State_IconFont          *       1
State_SymbolFont        *       2

pushfontstring TraceL font
      [ UTF8
        Push    "R0, R2-R6, R8-R11, LR"
      |
        Push    "R0, R2-R6, R8-R10, LR"
      ]
      [ :LNOT:No32bitCode
        MRS     LR, CPSR
        Push    LR
      ]

	Debug	err, "pushfontstring: flags=",R1
	DebugS  err, "                string=",R2
	Debug	err, "                handle=",R3

; R1 = flags for icon
; R2 -> string to paint
; R3 = handle for icon font

        TraceK  font, "pushfontstring: painting "
        TraceS  font, R2
        TraceK  font, ", flags "
        TraceX  font, R1
        TraceK  font, ", icon font "
        TraceX  font, R3
        TraceNL font

      [ CnP
        CMP     R7, #nullptr
        ADREQL  R7, null_control_sequence_list
        STR     R7, font_cs_list
      ]

      [ UTF8
; Initialise R11 (flag word, containing list of characters that need mapping to WimpSymbol)
        BL      read_current_alphabet
        LDR     R14, systemfont
        TEQ     R3, R14                         ; is it a desktop font icon?
        LDREQ   R11, systemfont_wimpsymbol_map  ; get the cached map if so (speedup)
        MOVNE   R11, #0                         ; else mark as not yet calculated (may be set during test_arrow)
      ]

        MOV     R8, R2

; 323nk allow variable length strings, uses is_deleted flag => r9 valid

        TST     R1,#is_deleted
        MOVNE   R10,#0                  ; no replacement character
        BNE     pushfontstring_scan_string

        MOV     R7, R3
; R8 -> string to paint
; R7 = handle for icon font

        ;find the replacement character by looking at the validation string
        MOV     R10, #:CHR: 0
        LDR     R3, validationstring
        AcceptLoosePointer_NegOrZero R3,-1
        CMP     R3, R3, ASR #31
        BEQ     %FT01
        ;R3 <= 0 => no validation string
        MOV     R2, #WimpValidation_Display
; R2 = WimpValidation_Display ('D')
; R3 -> validation string

        BL      findcommand
      [ UTF8
        BLEQ    checkvalid_getchar
        TEQ     R10, #"\\"
        ADDEQ   R3, R3, R9
        BLEQ    checkvalid_getchar
      |
        LDREQB  R10, [R3], #1
        TEQEQ   R10, #"\\"
        LDREQB  R10, [R3]
      ]
; R10 = replacement character, or 0
01
        MOV     R3, R7
; R3 = handle for icon font

        ;if the string is not indirected, we stop after 12 characters
        TST     R1, #if_indirected
        MOVEQ   R9, #12
        MOVNE   R9, #bignum
; R9 = maximum length of string

      [ UTF8
        TEQ     R10, #0
        BEQ     pushfontstring_scan_string
        LDRB    R14, alphabet
        TEQ     R14, #ISOAlphabet_UTF8
        BEQ     pushfontstring_scan_UTF8_string   ; special code for passworded UTF-8 sequences
      ]

pushfontstring_scan_string TraceL font
        MOV     R0, #0
        MOV     R1, #State_CurrentFont
      [ UTF8
        LDRB    R2, alphabet
      ]
        MOV     R6, #0
; R0 = index into destination
; R1 = state
; R2 = alphabet
; R3 = handle of icon font
; R6 = index into source
; R8 -> source
; R9 = "source buffer length" (bignum if indirected, else 12)
; R10 = replacement character
; R11 = WimpSymbol map

        B       pushfontstring_end_scan_loop

pushfontstring_start_scan_loop
; R4 = width of current character
; R5 = current character
        TEQ     R1, #State_SymbolFont
        BEQ     pushfontstring_scanning_symbol_font
        TEQ     R1, #State_CurrentFont
        BEQ     pushfontstring_scanning_current_font

pushfontstring_scanning_icon_font
      [ UTF8
        Push    "R9"
        MOV     R9, R5
        BL      test_arrow
        Pull    "R9"
      |
        TEST_ARROW R5
      ]
        ADDEQ   R0, R0, #2
        MOVEQ   R1, #State_SymbolFont
        B       pushfontstring_reinitialise_scan_loop

pushfontstring_scanning_current_font
      [ UTF8
        Push    "R9"
        MOV     R9, R5
        BL      test_arrow
        Pull    "R9"
      |
        TEST_ARROW R5
      ]
        ADD     R0, R0, #2
        MOVEQ   R1, #State_SymbolFont
        MOVNE   R1, #State_IconFont
        B       pushfontstring_reinitialise_scan_loop

pushfontstring_scanning_symbol_font
      [ UTF8
        Push    "R9"
        MOV     R9, R5
        BL      test_arrow
        Pull    "R9"
      |
        TEST_ARROW R5
      ]
        ADDNE   R0, R0, #2
        MOVNE   R1, #State_IconFont

pushfontstring_reinitialise_scan_loop
      [ UTF8
        ADD     R0, R0, R4
        ADD     R6, R6, R4
        TEQ     R5, #' '
        ADDEQ   R0, R0, #1  ; may need expanding to UTF-8 &C2 &A0 in fixupfontstring
      |
        ADD     R0, R0, #1
        ADD     R6, R6, #1
      ]

pushfontstring_end_scan_loop
        CMP     R6, R9
        BHS     pushfontstring_scan_loop_done
      [ UTF8
        LDRB    R5, [R8, R6]                    ; get byte
        MOV     R4, #1
        TEQ     R2, #ISOAlphabet_UTF8
        TEQEQ   R5, #&E2                        ; optimisation: unless first byte is &E2 (as with all WimpSymbol characters),
        Push    "R6", EQ                        ;               treat as a byte stream, even if alphabet is UTF-8
        ADDEQ   R6, R8, R6
        MOVEQ   R4, #6
        BLEQ    convert_UTF8_to_UCS4
        MOVEQ   R5, R6
        Pull    "R6", EQ                        ; keep R4 returned from convert routine
        CMP     R5, #" "
        BHS     pushfontstring_start_scan_loop  ; must use unsigned in order to handle malformed characters!
      |
        LDRB    R5, [R8, R6]
        CMP     R5, #" "
        BHS     pushfontstring_start_scan_loop
      ]
pushfontstring_scan_loop_done

; R6 = length of source

        ;if we did anything at all, switch back to the current font
        TEQ     R1, #State_CurrentFont
        ADDNE   R0, R0, #2
; R0 = length of destination

pushfontstring_switch
        ;do we need to copy the string?
        TEQ R10, #:CHR: 0
        BNE pushfontstring_use_replacement_string


pushfontstring_copy_string TraceL font
        ;R7 := ALIGN (R0 + 1)
        ADD     R7, R0, #4
      [ CnP
        BL      add_control_sequences_to_length
      ]
        BIC     R7, R7, #3
        SUB     SP, SP, R7

        MOV     R1, SP

        [ AllowMatrix
        Push    "R2-R3"
        LDR     R3, validationstring
        AcceptLoosePointer_NegOrZero R3,-1
        CMP     R3, R3, ASR #31
        BEQ     %FT05
        MOV     R2, #"M"
        BL      findcommand
        BNE     %FT05
        LDRB    R14,[R3]
        Pull    "R2-R3"
        SUB     SP,SP,#28
        Push    "R2-R3"
        ADD     R7,R7,#28
        Push    "R0-R7"

        SUB     R14,R14,#"0"
        CMP     R14,#9
        MOVGT   R14,#0
        CMP     R14,#0
        MOVLT   R14,#0

        ADRL    R7,font_matrix
        ADD     R7,R7, R14, LSL #5
        ADD     R14,SP,#40
        LDMIA   R7,{R0-R6}
        STMIA   R14,{R0-R6}
        Pull    "R0-R7"

05
        Pull    "R2-R3"
        ]

        MOV     R0, #0
        MOV     R4, #0
        MOV     R5, #State_CurrentFont
; R0 = index into destination
; R1 -> destination
; R2 = alphabet
; R3 = handle of icon font
; R4 = index into source
; R5 = state
; R6 = maximum index into source (either index of terminator, or max length of buffer)
; R7 = stack alignment
; R8 -> source
; R11 = WimpSymbol map

        B       pushfontstring_end_copy_loop

pushfontstring_start_copy_loop
      [ CnP
        LDR     R9, font_cs_list
        LDR     R10, [R9, #0]                   ; check to see if we need to insert a control sequence
        TEQ     R10, R4
        BNE     %FT02
        LDR     R10, [R9, #4]                   ; byte count
        ADD     R9, R9, #8
01      LDRB    R14, [R9], #1                   ; copy control bytes
        STRB    R14, [R1, R0]
        ADD     R0, R0, #1
        SUBS    R10, R10, #1
        BNE     %BT01
        ADD     R9, R9, #3                      ; align, and store back in font_cs_list
        BIC     R9, R9, #3
        STR     R9, font_cs_list
02
      ]
      [ UTF8
        LDRB    R9, [R8, R4]                    ; get byte
        MOV     R10, #1
        TEQ     R2, #ISOAlphabet_UTF8
        TEQEQ   R9, #&E2                        ; optimisation: unless first byte is &E2 (as with all WimpSymbol characters),
        Push    "R4,R6", EQ                     ;               treat as a byte stream, even if alphabet is UTF-8
        ADDEQ   R6, R8, R4
        MOVEQ   R4, #6
        BLEQ    convert_UTF8_to_UCS4
        MOVEQ   R9, R6
        MOVEQ   R10, R4
        Pull    "R4,R6", EQ
; R10 = width of current character
; R9 = current character
      ]
        TEQ     R5, #State_SymbolFont
        BEQ     pushfontstring_copying_symbol_font
        TEQ     R5, #State_CurrentFont
        BEQ     pushfontstring_copying_current_font

pushfontstring_copying_icon_font
      [ UTF8
        BL      test_arrow
      |
        ;R9 := current character
        LDRB    R9, [R8, R4]

        TEST_ARROW R9
      ]
        BNE     pushfontstring_reinitialise_copy_loop

        ;append Font_CommandFont and the handle for symbol font
        [ outlinefont

        MOV     R9, #26
        STRB    R9, [R1, R0]
        ADD     R0, R0, #1

        LDR     R9, symbolfont
        STRB    R9, [R1, R0]
        ADD     R0, R0, #1

        ;set new state
        MOV     R5, #State_SymbolFont
        ]
        B       pushfontstring_reinitialise_copy_loop

pushfontstring_copying_symbol_font
      [ UTF8
        BL      test_arrow
      |
        ;R9 := current character
        LDRB    R9, [R8, R4]

        TEST_ARROW R9
      ]
        BEQ     pushfontstring_reinitialise_copy_loop

        ;append Font_CommandFont and the handle for the icon font
        MOV     R9, #26
        STRB    R9, [R1, R0]
        ADD     R0, R0, #1

        STRB    R3, [R1, R0]
        ADD     R0, R0, #1

        ;set new state
        MOV     R5, #State_IconFont
        B       pushfontstring_reinitialise_copy_loop

pushfontstring_copying_current_font
        [ outlinefont
      [ UTF8
        BL      test_arrow
      |
        ;R9 := current character
        LDRB    R9, [R8, R4]

        TEST_ARROW R9
      ]

        ;append Font_CommandFont and the handle for the appropriate font
        MOV     R9, #26
        STRB    R9, [R1, R0]
        ADD     R0, R0, #1

        MOVNE   R9, R3
        LDREQ   R9, symbolfont
        STRB    R9, [R1, R0]
        ADD     R0, R0, #1

        ;set new state
        MOVNE   R5, #State_IconFont
        MOVEQ   R5, #State_SymbolFont
        ]

pushfontstring_reinitialise_copy_loop
        ;copy a byte
        LDRB    R9, [R8, R4]
        STRB    R9, [R1, R0]
        ;update both indexes
        ADD     R0, R0, #1
        ADD     R4, R4, #1
      [ UTF8
        SUBS    R10, R10, #1
        BNE     pushfontstring_reinitialise_copy_loop  ; copy the other bytes in the character!
      ]

pushfontstring_end_copy_loop
        CMP     R4, R6
        BLO     pushfontstring_start_copy_loop

pushfontstring_copy_loop_done
        ;terminate the destination
        MOV     R9, #:CHR: 0
        STRB    R9, [R1, R0]
; R0 = length of destination (same as before)

        B       pushfontstring_exit


pushfontstring_use_replacement_string TraceL font
        ;R7 := Align (R6 + 4 + 1)
        ADD     R7, R6, #8
      [ CnP
        BL      add_control_sequences_to_length
      ]
        BIC     R7, R7, #3
        SUB     SP, SP, R7

        MOV     R1, SP
; R1 -> destination

        MOV     R0, #0
; R0 = index into destination

        ;change to icon font
        MOV     R9, #26
        STRB    R9, [R1, R0]
        ADD     R0, R0, #1

        STRB    R3, [R1, R0]
        ADD     R0, R0, #1

        MOV     R2, #0
; R2 = counter for source

      [ CnP
        LDR     R9, font_cs_list
        LDR     R11, [R9, #0]
pushfontstring_replacement_loop
        TEQ     R11, R2                 ; check if we need to insert a control sequence
        BNE     %FT02
        LDR     R11, [R9, #4]
        ADD     R9, R9, #8
01      LDRB    R14, [R9], #1           ; copy control bytes
        STRB    R14, [R1, R0]
        ADD     R0, R0, #1
        SUBS    R11, R11, #1
        BNE     %BT01
        ADD     R9, R9, #3              ; align
        BIC     R9, R9, #3
        LDR     R11, [R9, #0]
02
      |
pushfontstring_replacement_loop
      ]
        TEQ     R2, R6
        ;store replacement character
        STRNEB  R10, [R1, R0]
        ADDNE   R0, R0, #1
        ADDNE   R2, R2, #1
        BNE     pushfontstring_replacement_loop

        ;terminate the string
        MOV     R9, #:CHR: 0
        STRB    R9, [R1, R0]
; R0 = length of destination
      [ UTF8
        B       pushfontstring_exit
      ]


      [ UTF8
pushfontstring_scan_UTF8_string
        ; R3 = handle for icon font
        ; R8 -> source string
        ; R9 = maximum byte-count of source string
        ; R10 = UCS-4 replacement character
        MOV     R0, #0                  ; character count
        MOV     R2, R8                  ; rover
        MOV     R6, R9
        ADD     R7, R8, R9              ; -> byte after end of source
      [ CnP
        LDR     R11, font_cs_list       ; -> control sequence list
01      LDR     R14, [R11, #0]          ; is there a control sequence at this byte offset?
        SUB     R4, R2, R8              ; convert back to offset
        CMP     R4, R14
        STRHS   R0, [R11, #0]           ; if so, then store *character* offset back into list
        LDRHS   R14, [R11, #4]          ; and advance to next item
        ADDHS   R11, R11, R14
        ADDHS   R11, R11, #8 + 3
        BICHS   R11, R11, #3
      |
01
      ]
        MOV     R4, R2
        BL      skipcharR
        CMP     R2, R4
        BEQ     %FT02                   ; break from loop if we've not advanced
        ADD     R0, R0, #1
        SUBS    R6, R7, R2              ; calculate number of bytes left
        BHI     %BT01
02      ; R0 = number of characters
        SUB     sp, sp, #8              ; we need some scratch space - tempworkspace may already be in use
        MOV     R5, sp
        MOV     R6, R10
        BL      convert_UCS4_to_UTF8
        ADD     sp, sp, #8
        ; R4 = number of bytes per replacement character
        MUL     R7, R4, R0
      [ CnP
        BL      add_control_sequences_to_length
      ]
        ADD     R7, R7, #3 + 3
        BIC     R7, R7, #3
        ; R7 = Align (R0 + 2 + 1)
        SUB     sp, sp, R7
        MOV     R1, sp
        ; R1 -> destination string
        SUB     sp, sp, #8              ; this time, we're using it to hold byte-sequence template
        MOV     R5, sp
        MOV     R6, R10
        BL      convert_UCS4_to_UTF8
        MOV     R2, #0                  ; character count into destination
      [ CnP
        LDR     R11, font_cs_list       ; -> control sequence list
      ]
        ; first, select font:
        MOV     R14, #26
        STRB    R14, [R1], #1
        STRB    R3, [R1], #1
        ; now put in characters:
01      CMP     R2, R0                  ; finished?
        BPL     %FT03
      [ CnP
        LDR     R14, [R11, #0]          ; is there a control sequence at this character offset?
        TEQ     R2, R14
        BNE     %FT12
        LDR     R5, [R11, #4]           ; byte count
        ADD     R11, R11, #8
11      LDRB    R14, [R11], #1          ; copy control bytes
        STRB    R14, [R1], #1
        SUBS    R5, R5, #1
        BNE     %BT11
        ADD     R11, R11, #3            ; align
        BIC     R11, R11, #3
12
      ]
        MOV     R5, #0                  ; initialise index into replacement character
02      LDRB    R14, [sp, R5]
        STRB    R14, [R1], #1           ; copy across byte
        ADD     R5, R5, #1
        CMP     R5, R4
        BLO     %BT02
        ADD     R2, R2, #1              ; increment character count
        B       %BT01
03      ADD     sp, sp, #8              ; realign stack
        ; finally, terminate the string
        MOV     R14, #0
        STRB    R14, [R1]
        ; Reset R1 to start of string
        MOV     R1, sp
        ; Drop through...
      ]


pushfontstring_exit
        ;get registers back from where we put them
        ; 320nk Neil Kelleher 22nd Feb 1993
        ; This was a thoroughly ghastly bit of code that meant interrupts could
        ; seriously screw up the painted string. DON'T alter my changes unless
        ; you understand why i did them, or ASK me!
;        ADD     SP, SP, R7
        MOV     LR,SP
        ADD     LR,LR,R7
        TEQ     R7, #0
    [ UTF8
      [ No32bitCode
        ADDNE   R7, R7, #4*11 ;11 = no of registers stacked
        LDMFD   LR, {R0, R2, R3, R4, R5, R6, R8, R9, R10, R11, LR}
      |
        ADDNE   R7, R7, #4*12 ;12 = no of registers stacked
        LDR     R1, [LR]
        LDMED   LR, {R0, R2, R3, R4, R5, R6, R8, R9, R10, R11, LR}
      ]
    |
      [ No32bitCode
        ADDNE   R7, R7, #4*10 ;10 = no of registers stacked
        LDMFD   LR, {R0, R2, R3, R4, R5, R6, R8, R9, R10, LR}
      |
        ADDNE   R7, R7, #4*11 ;11 = no of registers stacked
        LDR     R1, [LR]
        LDMED   LR, {R0, R2, R3, R4, R5, R6, R8, R9, R10, LR}
      ]
    ]
        ;SP now as at entry. Move it back over the string
;        SUB     SP, SP, R7
; R1 -> string to use
; R7 = difference in stack (*word aligned*)

      [ No32bitCode
        MOV     R1,SP
      |
        MSR     CPSR_f, R1
        MOV     R1,SP
      ]
        TraceK  font, "pushfontstring: using "
        TraceS  font, R1
        TraceK  font, " stack moved by "
        TraceD  font, R7
        TraceK  font, " bytes"
        TraceNL font
        ;return
      [ No32bitCode
        MOVS    PC, LR
      |
        MOV     PC, LR
      ]

        [       outlinefont
remembercurrentfont TraceL font
;Make sure we know the current font and colours
        Push    "R0-R3, LR"

        LDR     LR, currentfont
        TEQ     LR, #0
        Pull    "R0-R3, PC", NE

        SWI     XFont_CurrentFont
        BVS     %FT01
        CLRV
        Pull    "R0-R3, PC"
01
        ;save the current font handle in a safe place
        STR     R0, currentfont
        STR     R1, currentbg
        STR     R2, currentfg
        STR     R3, currentoffset

        Trace   font, "remembercurrentfont: currentfont ", X, R0
; NK 349
        MOV     R0,#0
        MOV     R1,#0
        SWI     XFont_SwitchOutputToBuffer
        CLRV

        Pull    "R0-R3, PC"
        ]

      [ CnP
null_control_sequence_list  DCD  -1

add_control_sequences_to_length
; On exit, R7 is incremented by the total of the control sequence lengths indicated in font_cs_list
        Entry   "R0"
        LDR     R0, font_cs_list
01      LDR     R14, [R0, #0]
        CMP     R14, #nullptr
        EXIT    EQ
        LDR     R14, [R0, #4]
        ADD     R7, R7, R14
        ADD     R0, R0, R14
        ADD     R0, R0, #8 + 3
        BIC     R0, R0, #3
        B       %BT01
      ]

        [ AllowMatrix
font_matrix
  ; italic
        DCD     28
        DCD     65536,0,13930,65536,0,0
        DCD     0
  ; larger
        DCD     28
        DCD     71000,0,0,71000,0,0
        DCD     0
  ; larger still
        DCD     28
        DCD     76500,0,0,76500,0,0
        DCD     0
  ; larger still and italic
        DCD     28
        DCD     76500,0,19000,76500,0,0
        DCD     0
  ; reverse italic
        DCD     28
        DCD     65536,0,-13930,65536,0,0
        DCD     0
  ; larger still
        DCD     28
        DCD     131072,0,0,131072,0,0
        DCD     0
  ; narrow
        DCD     28
        DCD     52000,0,0,65536,0,0
        DCD     0
  ; tiny
        DCD     28
        DCD     32768,0,0,32768,0,0
        DCD     0
        ]

      [ UTF8

; This table cunningly works both as character strings and character numbers

wimpsymbols
        DCD     &00000080       ; tick/euro
        DCD     &00000084       ; cross
        DCD     &00000088       ; left arrow
        DCD     &00000089       ; right arrow
        DCD     &0000008A       ; down arrow
        DCD     &0000008B       ; up arrow

; The next two tables *must* be kept in the same order!

wimpsymbols_UTF8
        DCD     &00949CE2       ; tick/euro
        DCD     &00989CE2       ; cross
        DCD     &009087E2       ; left arrow
        DCD     &009287E2       ; right arrow
        DCD     &009387E2       ; down arrow
        DCD     &009187E2       ; up arrow

wimpsymbols_UCS4
        DCD     &00002714       ; tick/euro
        DCD     &00002718       ; cross
        DCD     &000021D0       ; left arrow
        DCD     &000021D2       ; right arrow
        DCD     &000021D3       ; down arrow
        DCD     &000021D1       ; up arrow

; Work out which characters need mapping to WimpSymbol in this font
; Entry: R3 = font handle
; Exit:  R11 contains bits, bit n *clear* means index-n character in wimpsymbols table needs mapping
measure_symbols
        Entry   "R0-R8"
        MOV     R11, #-1                ; start off assuming no glyphs need mapping
        LDRB    R14, alphabet
        TEQ     R14, #ISOAlphabet_UTF8
        ADRNE   R7, wimpsymbols
        ADREQ   R7, wimpsymbols_UTF8
        MOV     R8, #0                  ; index into symbol list
        MOV     R0, R3
01      SUB     sp, sp, #16             ; space for returned bbox block
        MOV     R1, #-1
        Push    "R1"                    ; no split character
        MOV     R1, #0
        Push    "R1"                    ; no offsets
        Push    "R1"
        Push    "R1"
        Push    "R1"
        ADD     R1, R7, R8, LSL#2
        LDR     R2, = 1:SHL:18 :OR: 1:SHL:8 :OR: 1:SHL:5
        MOV     R3, #bignum
        MOV     R4, #bignum
        MOV     R5, sp
        SWI     XFont_ScanString
        ADD     sp, sp, #20
        Pull    "R1-R2,R5-R6"           ; load bounding box from stack
        SUBS    R5, R5, R1              ; calc bbox width/height
        SUBLES  R6, R6, R2
        SETPSR  Z_bit, R14, LE
        TEQEQ   R3, #0
        TEQEQ   R4, #0                  ; now EQ => offsets are both zero, and bbox has zero or negative width and height
        MOVEQ   R14, #1
        BICEQ   R11, R11, R14, LSL R8   ; if character is *not* defined, then clear its bit in R11 (=> test_arrow returns EQ)
        ADD     R8, R8, #1
        CMP     R8, #6                  ; six characters in the table
        BLO     %BT01
        EXIT

; Compare a UCS-4 character against the WimpSymbol mapping bitmap, to see if it needs to be mapped
; Entry: R2 = alphabet
;        R3 = icon's font handle
;        R9 = UCS-4 character
;        R11 = mapping bitmap (as set up in measure_symbols)
; Exit:  EQ => needs mapping
test_arrow
        ROUT
      [ RegisterWIMPSymbolFont
        Push    "LR"
        CMP     R9, #&80
        Pull    "PC", LO                ; optimisation for ASCII: return NE
        Push    "R0,R1"
        TEQ     R2, #ISOAlphabet_UTF8
        ADRNE   R0, wimpsymbols
        ADREQ   R0, wimpsymbols_UCS4
        MOV     R1, #0                  ; index into symbol list
01      LDR     R14, [R0, R1, LSL#2]
        CMP     R9, R14
        BEQ     %FT02
        ADD     R1, R1, #1
        CMP     R1, #6                  ; six characters in table
        BNE     %BT01
        CMP     PC, #0
        Pull    "R0,R1,PC"              ; return NE
02      ; we've found a character from the list - what does R11 say?
        TEQ     R11, #0                 ; not calculated yet?
        BLEQ    measure_symbols         ; work it out if so
        MOV     R14, #1
        TST     R11, R14, LSL R1        ; set Z according to R1-th bit of R11
        Pull    "R0,R1,PC"
      |
        TEQ     PC, #0                  ; clear Z - no WIMPSymbol font fitted
        MOV     PC, LR
      ]

        LTORG
      ]

        END