; CoolSwitch.Source  1.06  03-05-2011

; The source code for the ARMv7 compatible version of the CoolSwitch module, derived from Source-105.
; The module can be (re)created with the following commands (using the Norcroft compiler's assembler and linker):
;   objasm Source object
;   link -rmf -output CoolSwitch object
; Most alterations were done without comments. Any comments added by me are marked with [fdb].

; Frank de Bruijn
; The Hague, May 2011.


; Constants

              GBLS    VERSION
VERSION       SETS    "1.06 (03 May 2011)"

; General purpose stuff

V_bit         *       1<<28
EventV        *       16
Service_Reset *       &27
byte          *       1
word          *       4
V_set_value   *       1<<31            ; subtracting from positive number (CMP) or adding to negative number (CMN) will always set V [fdb]

; Caret history buffer depth

CaretDepth    *       20

; Display list

              ^       0, r11
DL_Link       #       4
DL_BBox       #       16               ; Bounding box in work area coordinates
DL_SprName    #       16               ; Name of sprite to display
DL_Caption    #       4                ; Pointer to caption text
DL_Handle     #       4                ; Client handle
DL_Flags      #       4                ; Various flags
DL_Size       #       0

DL_Hot        *       1 :SHL: 0        ; Selected
DL_WasHot     *       1 :SHL: 1
DL_Lost       *       1 :SHL: 2

              ^       0
Caret_Window  #       4                ; -1 if not used
Caret_Icon    #       4
Caret_X       #       4
Caret_Y       #       4
Caret_Height  #       4
Caret_Index   #       4
Caret_Time    #       4                ; monotonic time when used
Caret_Size    #       0

; Workspace definitions

StackSize     *       1024

ws            RN      12

              ^       0, ws

; Internal key numbers

KeyStart      #       0
KMod1         #       byte             ; ALT
KMod2         #       byte             ; CTRL
KMod3         #       byte             ; Shift
KAction       #       byte             ; Tab
KeyEnd        #       0

KeyBits       #       word
Ignore        #       word

Font          #       word

; Task stuff

MyTask        #       word
MyWindow      #       word             ; Only ever one
WorkArea      #       word * 4         ; Extent of work area (top left is 0, 0)
BackWindow    #       word
DList         #       word             ; Display list if open
Flags         #       word
FrontWindow   #       word             ; New front window
CaretInfo     #       Caret_Size
PollWord      #       word             ; For poll word not zero
Exclude       #       word             ; Exclude file base
ExcludeSize   #       word             ; Size of exclude file
PollBlock     #       256
Stack         #       StackSize

; Caret block

CaretBuffer   #       Caret_Size * CaretDepth

ListIsWindows *       1 :SHL: 0


XColourTrans_SetFontColours     * &6074F
XColourTrans_SetGCOL            * &60743
XFilter_DeRegisterPostFilter    * &62643
XFilter_RegisterPostFilter      * &62641
XFont_FindFont                  * &60081
XFont_LoseFont                  * &60082
XFont_Paint                     * &60086
XFont_ScanString                * &600A1
XOS_Byte                        * &20006
XOS_Claim                       * &2001F
XOS_Exit                        * &20011
XOS_ExitAndDie                  * &20050
XOS_File                        * &20008
XOS_Module                      * &2001E
XOS_Plot                        * &20045
XOS_ReadMonotonicTime           * &20042
XOS_ReadVduVariables            * &20031
XOS_Release                     * &20020
XOS_Write0                      * &20002
XOS_WriteI                      * &20100
XTaskManager_TaskNameFromHandle * &62680
XWimp_CloseWindow               * &600C6
XWimp_CreateMenu                * &600D4
XWimp_CreateWindow              * &600C1
XWimp_ForceRedraw               * &600D1
XWimp_GetCaretPosition          * &600D3
XWimp_GetRectangle              * &600CA
XWimp_GetWindowInfo             * &600CC
XWimp_GetWindowState            * &600CB
XWimp_Initialise                * &600C0
XWimp_OpenWindow                * &600C5
XWimp_PollIdle                  * &600E1
XWimp_ProcessKey                * &600DC
XWimp_ReadPalette               * &600E5
XWimp_ReadPixTrans              * &600ED
XWimp_RedrawWindow              * &600C8
XWimp_ReportError               * &600DF
XWimp_SendMessage               * &600E7
XWimp_SetCaretPosition          * &600D2
XWimp_SetColour                 * &600E6
XWimp_SpriteOp                  * &600E9
XWimp_TextOp                    * &600F9
XWimp_TransferBlock             * &600F1
XWimp_UpdateWindow              * &600C9


              AREA    codeblock,CODE,READONLY

              ORG     0

              ENTRY

Module_Header &       Main
              &       Initialise
              &       Finalise
              &       Service
              &       Title
              &       Help
              &       Command_Table
              &       0                          ; SWI chunk base number
              &       0                          ; SWI handler code
              &       0                          ; SWI decoding table
              &       0                          ; SWI decoding code
              &       0                          ; MessageTrans file
              &       ModuleFlags
ModuleFlags   &       1

GetBase       ADR     r0,Module_Header
              MOV     pc,lr

Title         =       "CoolSwitch",0
Help          =       "CoolSwitch",9,"$VERSION",0
              ALIGN


Initialise                             ROUT

              STR     lr,[sp,#-word]!
              LDR     r2,[ws]
              TEQ     r2,#0
              BNE     %f0000
              MOV     r3,#&790                   ; First time initialisation, so try to get enough RAM
              BL      Alloc
              LDRVS   pc,[sp],#word
              STR     r2,[ws]
0000          MOV     ws,r2
              LDR     r0,KeyDefault
              STR     r0,KeyStart
              MOV     r0,#-1
              STR     r0,FrontWindow
              STR     r0,Font
              BL      CaretInit
              BL      ClaimVectors
              BLVC    CaretFilter
              LDR     pc,[sp],#word

KeyDefault    =       &5e, &3b, &4c, &26         ; Default data for keymap


Finalise                               ROUT
              STR     lr,[sp,#-word]!            ; only R7-R11 and R13 must be preserved (PRM 1-209) [fdb]
              MOV     r3,ws
              LDR     ws,[r3]
              LDR     r0,MyTask
              CMP     r0,#0
              CMPNE   r0,#-1
              BNE     %f0010
              LDR     r0,Font
              CMP     r0,#-1
              SWINE   XFont_LoseFont
              BLVC    CaretUnFilter
              BLVC    ReleaseVectors
              BLVC    DL_DisposeAll
              BLVC    ExcludeDispose
              BVS     %f0000
              MOV     r2,ws
              BL      Free
              MOVVC   r2,#0
              STRVC   r2,[r3]
0000          LDR     pc,[sp],#word
0010          ADR     r0,CantDieMsg
              CMP     r0,#V_set_value            ; set V - according to Castle's 32-bit release notes, the RMA should stay below
              CMNVC   r0,#V_set_value            ; &80000000 so the CMN shouldn't be necessary, but why take the risk... [fdb]
              LDR     pc,[sp],#word

CantDieMsg    &       0
              =       "CoolSwitch in use",0
              ALIGN


CaretInit                              ROUT
              STMFD   sp!,{r0-r2,lr}
              ADRL    r0, CaretBuffer + Caret_Window
              MOV     r1,#-1
              MOV     r2,#CaretDepth
0000          STR     r1,[r0],#Caret_Size
              SUBS    r2,r2,#1
              BNE     %b0000
              LDMFD   sp!,{r0-r2,pc}


FilterName    =       "CoolSwitch caret tracker",0
              ALIGN

FilterCommon  ADR     r0,FilterName
              ADR     r1,Filter
              MOV     r2,ws
              MOV     r3,#0
              LDR     r4,EventMask
              MOV     pc,lr


CaretFilter   STMFD   sp!,{r0-r4,lr}
              BL      FilterCommon
              SWI     XFilter_RegisterPostFilter
              STRVS   r0,[sp]
              LDMFD   sp!,{r0-r4,pc}


CaretUnFilter STMFD   sp!,{r0-r4,lr}
              BL      FilterCommon
              SWI     XFilter_DeRegisterPostFilter
              STRVS   r0,[sp]
              LDMFD   sp!,{r0-r4,pc}


; Filter which tracks caret movements

Filter                                 ROUT
              STMFD   sp!,{r0-r7,lr}
              TEQ     r0,#12
              BEQ     %f0000
              TEQ     r0,#11
              BNE     %f0020
              LDMIA   r1,{r0,r2-r5,r7}           ; Window is losing caret, so remember where it was
              BL      FindSlot
              STMIA   r6,{r0,r2-r5,r7}
              LDMFD   sp!,{r0-r7,pc}
0000          ADRL    r0, CaretBuffer + Caret_Window  ; Window is gaining caret, so we don't need to worry
              LDR     r2,[r1]                         ; about it until it loses the caret again.
              MOV     r3,#20
0010          LDR     r4,[r0],#Caret_Size
              CMP     r4,r2
              MOVEQ   r4,#-1
              STREQ   r4,[r0,#-Caret_Size]
              SUBS    r3,r3,#1
              BNE     %b0010
0020          LDMFD   sp!,{r0-r7,pc}


; Find a free slot, or failing that the oldest non-free slot. If R1 is <> -1 and
; a slot exits with a window handle which matches R1 then return that
;
; R0  ==> Window handle or -1
;
; On exit
;
; R6  ==> Slot address (always guaranteed to return something)

FindSlot                             ROUT
              STMFD   sp!,{r0-r4,lr}
              MOV     r2,#CaretDepth
              MOV     r3,#&FFFFFFFF              ; Best time
              MOV     r4,#0                      ; Best slot address
              ADRL    r6, CaretBuffer
0000          LDR     r1,[r6]
              CMP     r0,#-1
              BEQ     %f0010
              CMP     r1,r0
              BEQ     %f0020
0010          CMP     r1,#-1
              BEQ     %f0020
              LDR     r1,[r6,#Caret_Time]
              CMP     r1,r3
              MOVLO   r3,r1
              MOVLO   r4,r6
              ADD     r6,r6,#Caret_Size
              SUBS    r2,r2,#1
              BNE     %b0000
              MOV     r6,r4
0020          SWI     XOS_ReadMonotonicTime
              STR     r0,[r6,#Caret_Time]
              LDMFD   sp!,{r0-r4,pc}


; Return a pointer to the cached caret information for the given window
;
; R0  ==> Window
;
; On exit
;
; R6  ==> Pointer to caret block or 0 if not found


CaretHistory                           ROUT
              STMFD   sp!,{r1,r2,lr}
              ADRL    r6, CaretBuffer
              MOV     r1,#20
0000          LDR     r2,[r6,#Caret_Window]
              CMP     r0,r2
              BEQ     %f0010
              ADD     r6,r6,#Caret_Size
              SUBS    r1,r1,#1
              BNE     %b0000
              MOV     r6,#0
0010          LDMFD   sp!,{r1,r2,pc}


EventMask     &       2_00000000000011100010000101110011


; Find the previous item in the list
;
; R11  ==> Current item

DL_FindPrev                            ROUT
              STMFD   sp!,{r9,r10,lr}
              MOV     r9,#0
              MOV     r10,r11
              LDR     r11,DList
0000          TEQ     r11,#0
              TEQNE   r10,r11
              MOVNE   r9,r11
              LDRNE   r11,DL_Link
              BNE     %b0000
              MOVS    r11,r9                     ; why does this have an S? [fdb]
              LDMFD   sp!,{r9,r10,pc}


DL_FindTail                          ROUT
              STMFD   sp!,{r9,lr}
              MOV     r9,#0
              LDR     r11,DList
0000          TEQ     r11,#0
              MOVNE   r9,r11
              LDRNE   r11,DL_Link
              BNE     %b0000
              MOV     r11,r9
              LDMFD   sp!,{r9,pc}


; Find the current hot item
;
; On exit
;
; R11  ==> Hot item

DL_FindHot                             ROUT
              STR     lr,[sp,#-word]!
              LDR     r11,DList
0000          TEQ     r11,#0
              BEQ     %f0010
              LDR     lr,DL_Flags
              TST     lr,#DL_Hot
              LDREQ   r11,DL_Link
              BEQ     %b0000
0010          LDR     pc,[sp],#word


; Bump the hot item
;
; R0  ==> Window handle
; R1  ==> +ve or -ve
;
; On exit
;
; R11 ==> New hot item

DL_BumpHot                             ROUT
              STR     lr,[sp,#-word]!
              BL      DL_FindHot
              TEQ     r11,#0
              LDREQ   r11,DList
              BEQ     %f0010
              CMP     r1,#0
              BLT     %f0000
              LDR     r11,DL_Link
              TEQ     r11,#0
              LDREQ   r11,DList
              B       %f0010
0000          BL      DL_FindPrev
              TEQ     r11,#0
              BLEQ    DL_FindTail                ; this was a BEQ - was that right? it looked like it could unbalance the stack [fdb]
0010          BL      DL_SetHot
              LDR     pc,[sp],#word


; Set the hot item
;
; R0  ==> Window handle
; R11 ==> New hot item, or NULL to unset

DL_SetHot                              ROUT
              STMFD   sp!,{r2,r10,r11,lr}
              MOV     r10,r11
              BL      DL_FindHot
              TEQ     r10,r11
              BEQ     %f0010
              TEQ     r11,#0
              BEQ     %f0000
              LDR     lr,DL_Flags
              BIC     lr,lr,#DL_Hot
              ORR     lr,lr,#DL_WasHot
              STR     lr,DL_Flags
              BL      DL_Update
              BVS     %f0010                     ; curious - originally, V could never be set after DL_Update (or DL_FindHot)... [fdb]
0000          MOVS    r11,r10
              BEQ     %f0010
              LDR     lr,DL_Flags
              ORR     lr,lr,#DL_Hot
              STR     lr,DL_Flags
              ADR     r2,DL_BBox
              BL      DL_Visible
              BLVC    DL_Update
0010          LDMFD   sp!,{r2,r10,r11,pc}


; R8  ==> List to take link out of
; R11 ==> Link

DL_Unlink     TEQ     r8,r11
              LDREQ   r8,DL_Link
              MOVEQ   pc,lr
              STMFD   sp!,{r7,r8,lr}
              MOV     r7,r8
              LDR     r8, [r7, #:INDEX:DL_Link]
              BL      DL_Unlink
              STR     r8, [r7, #:INDEX:DL_Link]
              LDMFD   sp!,{r7,r8,pc}


; R11 ==> Link to get rid off

DL_Dispose    STMFD   sp!,{r2,lr}
              LDR     r8,DList
              BL      DL_Unlink
              STR     r8,DList
              LDR     r2,DL_Caption
              BL      Free
              MOVVC   r2,r11
              BLVC    Free
              LDMFD   sp!,{r2,pc}


; Free entire display list

DL_DisposeAll                          ROUT
              STMFD   sp!,{r11,lr}
0000          LDR     r11,DList
              TEQ     r11,#0
              LDMEQFD sp!,{r11,pc}
              BL      DL_Dispose
              BVC     %b0000
              LDMFD   sp!,{r11,pc}


; Move the specified item to the head of the list
;
; R11  ==> Item to move

DL_ToHead     STMFD   sp!,{r8,lr}
              LDR     r8,DList
              BL      DL_Unlink
              STR     r8,DL_Link
              STR     r11,DList
              LDMFD   sp!,{r8,pc}


; R0  ==> Handle to look up
;
; On exit
;
; R11 ==> Matching item or 0

DL_Find                                ROUT
              STR     lr,[sp,#-word]!
              LDR     r11,DList
0000          TEQ     r11,#0
              LDRNE   lr,DL_Handle
              TEQNE   r0,lr
              LDRNE   r11,DL_Link
              BNE     %b0000
              LDR     pc,[sp],#word


; Add a new item to the display list, checking for a matching handle value,
; and moving the matched item if any found
;
; R0  ==> Handle
; R1  ==> Pointer to sprite name
; R2  ==> Pointer to text caption
;
; On exit
;
; R11 ==> Item added

DL_AddItem                             ROUT
              STMFD   sp!,{r1,r2,lr}
              BL      DL_Find
              TEQ     r11,#0
              BNE     %f0020
              MOV     r3,#:INDEX:DL_Size         ; No match, so new item is unique
              BL      Alloc
              BVS     %f0000
              MOV     r11,r2                     ; Pointer to item
              STR     r0,DL_Handle
              LDR     lr,Flags
              TST     lr,#ListIsWindows
              ADREQ   r2,DL_SprName
              MOVEQ   r0,r1
              MOVEQ   r1,#0
              BLEQ    StrCpy
              LDR     r0,[sp,#word * 1]          ; Now try to malloc space for caption
              MOV     r1,#&1F
              BL      StrLen
              ADD     r3,r2,#1
              BL      Alloc
              BVS     %f0010
              STR     r2,DL_Caption
              BL      StrCpy
              LDR     r0,DList                   ; All OK, so link it in
              STR     r0,DL_Link
              STR     r11,DList
0000          LDMFD   sp!,{r1,r2,pc}
0010          MOV     r2,r11
              BL      Free
              CMP     r0,#V_set_value
              CMNVC   r0,#V_set_value
              LDMFD   sp!,{r1,r2,pc}
0020          BL      DL_ToHead
              LDMFD   sp!,{r1,r2,pc}


; Format the display list, working out how big the largest item will be

PlinthGap     *       16
VGap          *       44
SprWidth      *       52
SprMargin     *       4
TextMargin    *       4
VMargin       *       8 + PlinthGap + 4
HMargin       *       8 + PlinthGap + 4
TextBase      *       12
SprBase       *       4

; On exit
;
; R4  ==> Window width
; R5  ==> Window height

DL_Format                              ROUT
              STMFD   sp!,{r0-r3,lr}
              MOV     r4,#0                      ; Maximum width
              MOV     r5,#VMargin * 2            ; Height
              LDR     r11,DList
0000          TEQ     r11,#0
              BEQ     %f0010
              LDR     r0,DL_Caption
              MOV     r1,#0
              BL      StrLen
              MOV     r1,r0
              MOV     r0,#1
              BL      TextOp
              BVS     %f0030
              CMP     r4,r0
              MOVLT   r4,r0
              ADD     r5,r5,#VGap
              LDR     r11,DL_Link
              B       %b0000
0010          ADD     r4,r4,#HMargin * 2 + TextMargin * 2  ; Got width in r4, height in r5
              LDR     lr,Flags
              TST     lr,#ListIsWindows
              ADDEQ   r4,r4,#SprWidth
              LDR     r11,DList
              MOV     r0,#HMargin                ; Left X
              SUB     r2,r4,#HMargin             ; Right X
              MOV     r3,#-VMargin               ; Top Y
              SUB     r1,r3,#VGap                ; Bottom Y
0020          TEQ     r11,#0
              BEQ     %f0030
              ADR     lr,DL_BBox
              STMIA   lr,{r0-r3}
              MOV     r3,r1
              SUB     r1,r3,#VGap
              LDR     r11,DL_Link
              B       %b0020
0030          MOV     r0,#0                      ; Left X      Now store the window's work area
              RSB     r1,r5,#0                   ; Bottom Y
              MOV     lr,#0                      ; Top Y
              ADR     r2,WorkArea
              STMIA   r2,{r0,r1,r4,lr}
              STRVS   r0,[sp]
              LDMFD   sp!,{r0-r3,pc}


; Paint the display list
;
; R0  ==> Window handle

DL_Redraw                              ROUT
              STMFD   sp!,{r0,r1,lr}
              SUB     sp,sp,#44
              STR     r0,[sp]
              MOV     r1,sp
              SWI     XWimp_RedrawWindow
              BVS     %f0010
0000          TEQ     r0,#0
              BEQ     %f0010
              BL      DL_PaintBit
              SWI     XWimp_GetRectangle
              BVC     %b0000
0010          ADD     sp,sp,#44
              LDMFD   sp!,{r0,r1,pc}


; Update an item in the list
;
; R0  ==> Window
; R11 ==> Item to update

DL_Update                              ROUT
              STMFD   sp!,{r0-r4,lr}
              SUB     sp,sp,#44
              STR     r0,[sp]                    ; Window handle
              ADR     r0,DL_BBox
              LDMIA   r0,{r1-r4}
              STMIB   sp,{r1-r4}
              MOV     r1,sp
              SWI     XWimp_UpdateWindow
              BVS     %f0010
0000          TEQ     r0,#0
              BEQ     %f0010
              BL      DL_PaintBit
              SWI     XWimp_GetRectangle
              BVC     %b0000
0010          ADD     sp,sp,#44
              LDMFD   sp!,{r0-r4,pc}


; R1  ==> Pointer to redraw structure (on stack)

DL_PaintBit                            ROUT
              STMFD   sp!,{r0-r11,lr}
              SUB     sp,sp,#16
              LDR     r6,[r1,#4]                 ; R4, R5 is the screen coordinate of the top left of the work area
              LDR     lr,[r1,#20]
              SUB     r6,r6,lr
              LDR     r7,[r1,#16]
              LDR     lr,[r1,#24]
              SUB     r7,r7,lr
              ADR     r3,WorkArea                ; Draw plinth
              MOV     r4,#PlinthGap
              MOV     r5,#1
              BL      DL_Plinth
              ADD     lr,r1,#28                  ; Shift clip region
              LDMIA   lr,{r2-r5}
              SUB     r2,r2,r6
              SUB     r3,r3,r7
              SUB     r4,r4,r6
              SUB     r5,r5,r7
              STMEA   sp,{r2-r5}
              LDR     r11,DList
0000          TEQ     r11,#0
              BEQ     %f0030
              MOV     r2,sp
              ADR     r3,DL_BBox
              BL      DL_Overlap                 ; BGE was BCC because C was set on LT [fdb]
              BGE     %f0020
              LDR     r4,DL_Flags                ; Got to draw this one
              TST     r4,#DL_Hot :OR: DL_WasHot
              BEQ     %f0010
              TST     r4,#DL_Hot
              BICEQ   r4,r4,#DL_WasHot
              STREQ   r4,DL_Flags
              MOVNE   r0,#7
              MOVEQ   r0,#1
              SWI     XWimp_SetColour
              BL      DL_FillRect
0010          TST     r4,#DL_Hot
              MOV     r0,#3
              MOVNE   r1,#1
              MOVNE   r2,#7
              MOVEQ   r1,#7
              MOVEQ   r2,#1
              BL      TextOp
              BVS     %f0030
              LDR     lr,Flags
              TST     lr,#ListIsWindows
              ADREQ   r2,DL_SprName
              LDREQ   r3,DL_BBox + word * 0
              ADDEQ   r3,r3,r6
              ADDEQ   r3,r3,#SprMargin
              LDREQ   r4,DL_BBox + word * 1
              ADDEQ   r4,r4,r7
              ADDEQ   r4,r4,#SprBase
              BLEQ    DL_PutHalfSpr
              MOV     r0,#2
              LDR     r1,DL_Caption
              LDR     r4,DL_BBox + word * 0      ; Left X
              LDR     lr,Flags
              TST     lr,#ListIsWindows
              ADDEQ   r4,r4,#SprWidth
              ADD     r4,r4,#TextMargin
              ADD     r4,r4,r6
              LDR     r5,DL_BBox + word * 1      ; Bottom Y
              ADD     r5,r5,#TextBase
              ADD     r5,r5,r7
              BL      TextOp
              BVS     %f0030
0020          LDR     r11,[r11]
              B       %b0000
0030          STRVS   r0,[sp,#16]
              ADD     sp,sp,#16
              LDMFD   sp!,{r0-r11,pc}


; Work out whether two rectangles overlap
;
; R2 ==> Pointer to first rectangle
; R3 ==> Pointer to second rectangle

DL_Overlap    STMFD   sp!,{r4-r11,lr}
              LDMIA   r2,{r4-r7}
              LDMIA   r3,{r8-r11}
              CMP     r4,r10
              CMPLT   r8,r6
              CMPLT   r5,r11
              CMPLT   r9,r7
              LDMFD   sp!,{r4-r11,pc}            ; had ORRLTS pc,lr,#1<<29 to set C on LT - check (BCC) changed to BGE [fdb]


; Try to put a half size sprite
;
; R2 ==> Sprite name (without the sm)
; R3 ==> Screen X
; R4 ==> Screen Y

DefaultName   =       "small_app",0
              ALIGN

DL_PutHalfSpr                          ROUT
              STMFD   sp!,{r0-r7,lr}
              SUB     sp,sp,#48
              MOV     r0,sp                      ; Generate sm!xxx on stack
              MOV     lr,#'s'
              STRB    lr,[r0],#1
              MOV     lr,#'m'
              STRB    lr,[r0],#1
0000          LDRB    lr,[r2],#1
              STRB    lr,[r0],#1
              TEQ     lr,#0
              BNE     %b0000
              MOV     r0,#24                     ; Check for sm!xxx
              MOV     r2,sp
              SWI     XWimp_SpriteOp
              MOVVS   r0,#24
              ADDVS   r2,sp,#2
              SWIVS   XWimp_SpriteOp
              BVC     %f0010
              ADR     r0,DefaultName
              LDMIA   r0,{r0-r2}
              STMIA   sp,{r0-r2}
              MOV     r2,sp
0010          MOV     r0,#&100                   ; Got the sprite name at R2 now
              MOV     r1,#1                      ; Wimp sprite area
              ADD     r6,sp,#16                  ; scale factors
              ADD     r7,sp,#32                  ; translation
              SWI     XWimp_ReadPixTrans
              BVS     %f0020
              TEQ     r2,sp                      ; sm?
              LDRNE   lr,[sp,#16 + 8]
              MOVNE   lr,lr,LSL #1
              STRNE   lr,[sp,#16 + 8]
              LDRNE   lr,[sp,#16 + 12]
              MOVNE   lr,lr,LSL #1
              STRNE   lr,[sp,#16 + 12]
              MOV     r0,#52
              MOV     r5,#16 + 8                 ; added bit 4 (use palette) assuming this will not cause problems on older Wimps [fdb]
              SWI     XWimp_SpriteOp
0020          ADD     sp,sp,#48
              LDMFD   sp!,{r0-r7,pc}


; R3  ==> Box to paint
; R6,
; R7  ==> Offset

DL_FillRect   STMFD   sp!,{r0-r2,r4,r5,lr}
              SUB     sp,sp,#8
              ADR     r0,VduVars
              MOV     r1,sp
              SWI     XOS_ReadVduVariables
              MOV     lr,#1
              LDR     r4,[sp,#word * 0]
              MOV     r4,lr,LSL r4
              LDR     r5,[sp,#word * 1]
              MOV     r5,lr,LSL r5
              MOV     r0,#4
              LDR     r1,[r3,#word * 0]
              LDR     r2,[r3,#word * 1]
              BL      DL_Plot
              MOVVC   r0,#101
              LDRVC   r1,[r3,#word * 2]
              LDRVC   r2,[r3,#word * 3]
              SUBVC   r1,r1,r4
              SUBVC   r2,r2,r5
              BLVC    DL_Plot
              STRVS   r0,[sp,#8]
              ADD     sp,sp,#8
              LDMFD   sp!,{r0-r2,r4,r5,pc}


DL_Plot       STMFD   sp!,{r1,r2,lr}
              ADD     r1,r1,r6
              ADD     r2,r2,r7
              SWI     XOS_Plot
              LDMFD   sp!,{r1,r2,pc}


VduVars       &       4, 5, -1

; R3  ==> Box to paint
; R4  ==> Gap between box and plinth (0 means box fills plinth)
; R5  ==> Pushed flag (NZ means pushed)
; R6,
; R7  ==> Offset

DL_Plinth                              ROUT
              STMFD   sp!,{r0-r2,r8-r10,lr}
              SUB     sp,sp,#8
              ADR     r0,VduVars
              MOV     r1,sp
              SWI     XOS_ReadVduVariables
              MOV     lr,#1
              LDR     r8,[sp,#word * 0]
              MOV     r8,lr,LSL r8
              LDR     r9,[sp,#word * 1]
              MOV     r9,lr,LSL r9
              MOV     r10,r8                     ; Find the smaller eigen value
              CMP     r10,r9
              MOVGT   r10,r9
              MOV     r11,#0
0000          TEQ     r5,#0                      ; Pushed
              MOVEQ   r0,#0
              MOVNE   r0,#4
              SWI     XWimp_SetColour
              MOV     r0,#4                      ; MOVE
              LDR     r1,[r3,#word * 0]
              LDR     r2,[r3,#word * 1]
              ADD     r1,r1,r4
              ADD     r2,r2,r4
              BL      DL_Plot
              MOV     r0,#5
              LDR     r2,[r3,#word * 3]
              SUB     r2,r2,r4
              SUB     r2,r2,r9
              BL      DL_Plot
              LDR     r1,[r3,#word * 2]
              SUB     r1,r1,r4
              SUB     r1,r1,r8
              BL      DL_Plot
              TEQ     r5,#0
              MOVEQ   r0,#4
              MOVNE   r0,#0
              SWI     XWimp_SetColour
              MOV     r0,#5
              LDR     r2,[r3,#word * 1]
              ADD     r2,r2,r4
              BL      DL_Plot
              LDR     r1,[r3,#word * 0]
              ADD     r1,r1,r4
              BL      DL_Plot
              ADD     r4,r4,r10
              ADD     r11,r11,r10
              CMP     r11,#4
              BCC     %b0000
              STRVS   r0,[sp,#8]
              ADD     sp,sp,#8
              LDMFD   sp!,{r0-r2,r8-r10,pc}


; Ensure that the specified rectangle is visible
;
; R0  ==> Window handle
; R2  ==> Pointer to box (work area coordinates)

DL_Visible                             ROUT
              STMFD   sp!,{r0-r5,lr}
              ADRL    r1, PollBlock
              STR     r0,[r1]
              SWI     XWimp_GetWindowState
              BVS     %f0000
              LDR     r3,[r1,#16]                ; Make R3 into the Y conversion
              LDR     lr,[r1,#24]
              SUB     r3,r3,lr
              MOV     r4,#0                      ; R4 is the amount to shift the window up or down.
              LDR     r5,[r2,#4]                 ; Bottom Y of box
              SUB     r5,r5,#16
              ADD     r5,r5,r3                   ; <== screen coordinates
              LDR     lr,[r1,#8]                 ; Bottom Y of window
              SUBS    lr,r5,lr
              MOVLT   r4,lr
              LDR     r5,[r2,#12]
              ADD     r5,r5,#16
              ADD     r5,r5,r3
              LDR     lr,[r1,#16]
              SUBS    lr,r5,lr
              MOVGT   r4,lr
              CMP     r4,#0
              LDRNE   r5,[r1,#24]
              ADDNE   r5,r5,r4
              STRNE   r5,[r1,#24]
              SWINE   XWimp_OpenWindow
0000          STRVS   r0,[sp]
              LDMFD   sp!,{r0-r5,pc}


; Load a file containing a list of names and provide a service to
; match a name against the list
;
; An exclude file is a sequence of LF separated lines. Lines with '#' as their
; first non space character and lines which are empty apart from spaces are
; skipped. Other lines are matched against the given string. Within a line
; * is a wildcard.

; R1  ==> Name of exclusions file to open

ExcludeOpen                            ROUT
              STR     lr,[sp,#-word]!
              MOV     r0,#17
              SWI     XOS_File
              BVS     %f0000
              TEQ     r0,#1                      ; R0 = Type, R2 = Load, R3 = Exec, R4 = Length, R5 = Attributes
              BEQ     %f0005
              MOV     r2,r0
              MOV     r0,#19
              SWI     XOS_File
              B       %f0000
0005          BL      ExcludeDispose             ; Got the file, so free any existing one
              MOV     r3,r4
              BL      Alloc
              BVS     %f0000
              STR     r2,Exclude
              STR     r3,ExcludeSize
              MOV     r0,#&FF                    ; Now load it
              MOV     r3,#0
              SWI     XOS_File
              BLVS    ExcludeDispose
0000          LDR     pc,[sp],#word


ExcludeDispose
              STMFD   sp!,{r2,lr}
              LDR     r2,Exclude
              BL      Free
              MOV     r2,#0
              STR     r2,Exclude
              LDMFD   sp!,{r2,pc}


; R0  ==> String to scan for
;
; On exit
;
; C   ==> 1 if the string was found in the exclude file

ExcludeScan                            ROUT
              STMFD   sp!,{r1,r2,lr}
              LDR     r1,Exclude
              TEQ     r1,#0
              BEQ     %f0020
              LDR     r2,ExcludeSize
              ADD     r2,r1,r2                   ; Limit of file
0000          CMP     r1,r2                      ; Now do the scan
              BGE     %f0020
              LDRB    lr,[r1],#1
              TEQ     lr,#' '
              TEQNE   lr,#9
              TEQNE   lr,#10
              BEQ     %b0000
              TEQ     lr,#'#'
              BEQ     %f0010
              SUB     r1,r1,#1
              BL      ExcludeMatch
              LDMCSFD sp!,{r1,r2,pc}             ; C=1 (obviously) [fdb]
0010          CMP     r1,r2
              BGE     %f0020
              LDRB    lr,[r1],#1
              TEQ     lr,#10
              BNE     %b0010
              B       %b0000
0020          CMN     r0,#0                      ; C=0 [fdb]
              LDMFD   sp!,{r1,r2,pc}


ExcludeMatch                           ROUT
              STMFD   sp!,{r0,r1,r3,r4,lr}
0000          CMP     r1,r2
              BGE     %f0030
              LDRB    r3,[r1],#1
              TEQ     r3,#10
              BEQ     %f0030
              TEQ     r3,#'*'
              BEQ     %f0010
              CMP     r3,#'a'
              RSBCSS  lr,r3,#'z'
              SUBCS   r3,r3,#'a' - 'A'
              LDRB    r4,[r0],#1
              TEQ     r4,#0
              BEQ     %f0020
              CMP     r4,#'a'
              RSBCSS  lr,r4,#'z'
              SUBCS   r4,r4,#'a' - 'A'
              TEQ     r3,r4
              BNE     %f0020
              B       %b0000
0010          BL      ExcludeMatch
              BCS     %f0040
              LDRB    r4,[r0],#1
              TEQ     r4,#0
              BNE     %b0010
0020          CMN     r0,#0                      ; C=0 [fdb]
              LDMFD   sp!,{r0,r1,r3,r4,pc}
0030          LDRB    r4,[r0],#1
              CMP     r4,#0                      ; C=1 [fdb]
              BNE     %b0020
0040          LDMFD   sp!,{r0,r1,r3,r4,pc}


ClaimVectors
              STMFD   sp!,{r0-r2,lr}
              MOV     r0,#EventV
              ADR     r1,EventHandler
              MOV     r2,ws
              SWI     XOS_Claim
              MOV     r0,#14
              MOV     r1,#11
              SWI     XOS_Byte
              LDMFD   sp!,{r0-r2,pc}


ReleaseVectors
              STMFD   sp!,{r0-r2,lr}
              MOV     r0,#13
              MOV     r1,#11
              SWI     XOS_Byte
              MOV     r0,#EventV
              ADR     r1,EventHandler
              MOV     r2,ws
              SWI     XOS_Release
              LDMFD   sp!,{r0-r2,pc}


EventHandler                           ROUT
              TEQ     r0,#11
              MOVNE   pc,lr
              STMFD   sp!,{r0-r5,lr}
              ADR     r0,KeyStart
              MOV     r3,#KeyEnd - KeyStart
              LDR     r4,KeyBits
              MOV     r5,#1
0000          LDRB    lr,[r0],#1
              TEQ     lr,r2
              BNE     %f0010
              TEQ     r1,#0
              BICEQ   r4,r4,r5
              ORRNE   r4,r4,r5
0010          MOV     r5,r5,LSL #1
              SUBS    r3,r3,#1
              BNE     %b0000
              STR     r4,KeyBits                 ; Now check combinations
              TST     r4,#1 :SHL: 3              ; Action key
              STRNE   r4,PollWord
              LDMFD   sp!,{r0-r5,pc}


Tag           =       "CLSW"

; Allocate R3 bytes and return the address in R2

Alloc                                  ROUT
              STMFD   sp!,{r0,r3,lr}
              ADD     r3,r3,#7
              BIC     r3,r3,#3
              MOV     r0,#6
              SWI     XOS_Module
              BVS     %f0010
              LDR     lr,Tag
              STR     lr,[r2],#word
              MOV     lr,#0
              MOV     r0,r2
              SUBS    r3,r3,#word
0000          STRNE   lr,[r0],#word
              SUBNES  r3,r3,#word
              BNE     %b0000
0010          STRVS   r0,[sp]
              LDMFD   sp!,{r0,r3,pc}


; Free the block at R2

Free                                   ROUT
              TEQ     r2,#0
              MOVEQ   pc,lr
              STMFD   sp!,{r0,r2,lr}
              LDR     lr,Tag
              LDR     r0,[r2,#-word]!
              TEQ     lr,r0
              BNE     %f0000
              MOV     r0,#0
              STR     r0,[r2]
              MOV     r0,#7
              SWI     XOS_Module
              STRVS   r0,[sp]
              LDMFD   sp!,{r0,r2,pc}
0000          ADR     r0,BadFreeMsg
              CMP     r0,#V_set_value
              CMNVC   r0,#V_set_value
              ADD     sp,sp,#word
              LDMFD   sp!,{r2,pc}

BadFreeMsg    &       0
              =       "Bad call to Free",0
              ALIGN


              &       svcTable - Module_Header
Service       NOP                                ; signals service call enhancements [fdb]
              TEQ     r1,#Service_Reset
              MOVNE   pc,lr
Enhanced      STMFD   sp!,{r0-r2,lr}
              LDR     ws,[ws]
              MOV     r0,#253
              MOV     r1,#0
              MOV     r2,#255
              SWI     XOS_Byte
              TEQ     r1,#0
              BLEQ    ClaimVectors
              LDMFD   sp!,{r0-r2,pc}

svcTable      &       0
              &       Enhanced - Module_Header
              &       Service_Reset
              &       0


Command_Table
              =       "Desktop_CoolSwitch",0
              ALIGN
              &       CoolSwitch
              &       0
              &       CoolSwitchSyntax
              &       CoolSwitchHelp
              =       0
CoolSwitchHelp   = "Start the CoolSwitch task",13
CoolSwitchSyntax = 27, 1, 0
              ALIGN

TASK          =        "TASK"
TaskName      =        "CoolSwitch",0
              ALIGN

MsgList       &     11,12,&400C3,0


; *Command to start a task

CoolSwitch
              STMFD   sp!,{r0,lr}
              LDR     ws,[ws]
              LDR     r0,MyTask
              CMP     r0,#0
              CMPNE   r0,#-1
              MOVEQ   r0,#-1
              STREQ   r0,MyTask
              BLEQ    GetBase                    ; doesn't change any flags [fdb]
              LDREQ   lr,[r0,#word * 4]
              ADDEQ   r1,r0,lr
              MOVEQ   r0,#2
              SWIEQ   XOS_Module
              STRVS   r0,[sp]
              LDMFD   sp!,{r0,pc}

Fatal         MOV     r1,#1<<1
              ADR     r2,TaskName
              SWI     XWimp_ReportError
              B       Exit


ExcludeName   =       "<CoolSwitch$Dir>.Exclude",0
              ALIGN

; Only start if the task handle is set to -1

Main          LDR     ws,[ws]
              LDR     r0,MyTask
              CMP     r0,#-1
              SWINE   XOS_Exit
              LDR     r0,ThreeTen
              LDR     r1,TASK
              ADR     r2,TaskName
              ADR     r3,MsgList
              SWI     XWimp_Initialise
              BVS     Exit
              STR     r1,MyTask
              ADRL    sp, Stack + StackSize      ; Stack so we can intialise
              BL      CreateWindow
              ADRVC   r1,ExcludeName
              BLVC    ExcludeOpen
              BVS     Exit
MainLoop      ADRL    sp, Stack + StackSize
              SWI     XOS_ReadMonotonicTime
              ADD     r2,r0,#5                   ; Twentieth
              LDR     r0,PollMask
              ADRL    r1,PollBlock
              ADRL    r3,PollWord
              SWI     XWimp_PollIdle
              BVS     Fatal
              BL      Despatch
              B       MainLoop
Exit          BL      GetBase
              LDR     r3,[r0,#word * 4]
              ADD     r3,r0,r3
              MOV     r0,#0
              STR     r0,MyTask
              SWI     XOS_ExitAndDie


; Despatch according to latest Wimp event

Despatch
              CMP     r0,#(PollJmpEnd - PollJmp) / 4
              ADDCC   pc,pc,r0,LSL #2            ; Dynamic branch
              MOV     pc,lr
PollJmp       B       Idle
              B       Redraw
              MOV     pc,lr
              MOV     pc,lr
              MOV     pc,lr
              MOV     pc,lr
              MOV     pc,lr
              MOV     pc,lr
              B       KeyPressed
              MOV     pc,lr
              MOV     pc,lr
              MOV     pc,lr
              MOV     pc,lr
              B       PollWordNZ
              MOV     pc,lr
              MOV     pc,lr
              MOV     pc,lr
              B       UserMessage
              B       UserMessage
              MOV     pc,lr
PollJmpEnd


Redraw        STMFD   sp!,{r0,lr}
              LDR     r0,PollBlock
              BL      DL_Redraw
              BVS     Fatal
              LDMFD   sp!,{r0,pc}


KeyPressed                             ROUT
              STMFD   sp!,{r0-r3,lr}
              LDR     r0,PollBlock + 24
              LDR     r1,Ignore
              TEQ     r1,#0
              SUBNE   r1,r1,#1
              STRNE   r1,Ignore
              BNE     %f0010
              EOR     r3,r0,#1<<8                ; Key pressed, so think about bumping selection
              TEQ     r3,#&8A
              TEQNE   r3,#&9A
              TEQNE   r3,#&AA
              TEQNE   r3,#&BA
              BNE     %f0000
              LDR     r0,[ws,#20]
              TEQ     r3,#&8A
              TEQNE   r3,#&AA
              MOVEQ   r1,#1
              MOVNE   r1,#-1
              BL      DL_BumpHot                 ; something in here can set V... [fdb]
              BVS     Fatal
              LDMFD   sp!,{r0-r3,pc}
0000          TEQ     r2,#&1B
              BNE     %f0010
              BL      HideWindow
              BVS     Fatal
              LDMFD   sp!,{r0-r3,pc}
0010          SWI     XWimp_ProcessKey
              BVS     Fatal
              LDMFD   sp!,{r0-r3,pc}


Idle                                   ROUT
              STMFD   sp!,{r0,sl,lr}
              LDR     r0,FrontWindow
              CMP     r0,#-1
              BLNE    PlaceCaret
              LDR     r0,DList
              TEQ     r0,#0
              BEQ     %f0010
              LDR     sl,Flags                   ; Check for windows closing
              TST     sl,#ListIsWindows
              BLEQ    CheckTasks
              TST     sl,#ListIsWindows
              BLNE    CheckWindows
              LDR     r0,KeyBits
              TST     r0,#DL_Hot :OR: DL_WasHot
              BNE     %f0010
              BL      DL_FindHot
              TEQ     r11,#0
              BEQ     %f0000
              LDR     r0,DL_Handle
              TST     sl,#ListIsWindows
              BLEQ    SelectTask
              TST     sl,#ListIsWindows
              BLNE    SelectWindow
0000          BLVC    HideWindow
              BVS     Fatal
0010          LDMFD   sp!,{r0,sl,pc}


PollWordNZ                             ROUT
              STMFD   sp!,{r0,r4,lr}
              MOV     lr,#0
              STR     lr,PollWord
              LDR     lr,DList
              TEQ     lr,#0
              BNE     %f0000
              LDR     r4,KeyBits
              TST     r4,#2_0011                 ; Ctrl or Alt
              BEQ     %f0000
              MOV     r1,#-1
              SWI     XWimp_CreateMenu
              TST     r4,#2_0010
              BLNE    ListWindows
              TST     r4,#2_0010
              BLEQ    ListTasks
              BVS     Fatal
              LDR     lr,DList
              TEQ     lr,#0
              BEQ     %f0010
              BL      ShowWindow
              ADRVC   r1,CaretInfo
              SWIVC   XWimp_GetCaretPosition
              LDRVC   r0,MyWindow
              MOVVC   r1,#-1
              MOVVC   r2,#0
              MOVVC   r3,#0
              MOVVC   r4,#1<<25
              MOVVC   r5,#0
              SWIVC   XWimp_SetCaretPosition
              BVS     Fatal
              MOV     r0,#1
              STR     r0,Ignore
0000          LDMFD   sp!,{r0,r4,pc}
0010          SWI     XOS_WriteI+7
              LDMFD   sp!,{r0,r4,pc}


UserMessage   LDR     r0,[r1,#16]
              TEQ     r0,#0
              BEQ     Exit
              LDR     r2,TaskCloseDown
              TEQ     r0,r2
              BEQ     TaskStopped
              MOV     pc,lr

ThreeTen      &       310
PollMask      &       2_00000000110000000001110000110000
TaskCloseDown &       &400C3

TaskStopped   LDR     r2,Flags                   ; A task has ended
              TST     r2,#ListIsWindows
              MOVNE   pc,lr
              LDR     r2,DList
              TEQ     r2,#0
              MOVEQ   pc,lr
              LDR     r0,[r1,#4]                 ; Displaying tasks all right
              B       RemoveHandle


; Work out how long a string is
;
; R0  ==> String
; R1  ==> Terminating character (stops at C <= R1)
;
; On exit
;
; R2  ==> Length

StrLen                                 ROUT
              STMFD   sp!,{r0,lr}
              MOV     r2,#0
              CMP     r0,#0
0000          LDRHIB  lr,[r0],#1
              CMPHI   lr,r1
              ADDHI   r2,r2,#1
              BHI     %b0000
              LDMFD   sp!,{r0,pc}


; Copy a string to a buffer
;
; R0  ==> String
; R1  ==> Terminating character
; R2  ==> Buffer

StrCpy                                 ROUT
              STMFD   sp!,{r0,r2,lr}
              CMP     r0,#0
0000          LDRHIB  lr,[r0],#1
              CMPHI   lr,r1
              STRHIB  lr,[r2],#1
              BHI     %b0000
              MOV     lr,#0
              STRB    lr,[r2],#1
              LDMFD   sp!,{r0,r2,pc}


; Create our window

Width         *       320
Height        *       256

WindowBlock
              &       800 - Width / 2, 600 - Height / 2
              &       800 + Width / 2, 600 + Height / 2
              &       0, 0
              &       -1                         ; Behind
              &       2_10000000000000000000000010000010
              =       7, 2, 7, 1, 1, 3, 12, 0
              &       0, -16000, 3000, 0         ; big extent so we don't need to Wimp_SetExtent
              &       0                          ; no title bar ==> no flags
              &       0 :SHL: 12
              &       1                          ; Sprite area
              &       0                          ; min. width, height
              &       0, 0, 0                    ; title data
              &       0                          ; # icons
BackBlock     &       -10, -10, -5, -5
              &       0, 0
              &       -1                         ; Behind
              &       2_10000000000000000000000001000010
              =       7, 2, 7, 1, 1, 3, 12, 0
              &       0, -2000, 3000, 0
              &       0                          ; no title bar ==> no flags
              &       0 :SHL: 12
              &       1                          ; Sprite area
              &       0                          ; min. width, height
              &       0, 0, 0                    ; title data
              &       0                          ; # icons

CreateWindow
              STMFD   sp!,{r0,r1,lr}
              ADR     r1,WindowBlock
              SWI     XWimp_CreateWindow
              STRVC   r0,MyWindow
              ADRVC   r1,BackBlock
              SWIVC   XWimp_CreateWindow
              STRVC   r0,BackWindow
              STRVS   r0,[sp]
              LDMFD   sp!,{r0,r1,pc}


; Open our window off screen to give a base for the list iteration

OpenBack      STMFD   sp!,{r0-r5,lr}
              ADRL    r1, PollBlock
              LDR     r0,BackWindow
              STR     r0,[r1]
              SWI     XWimp_GetWindowState
              MOVVC   r2,#-2
              STRVC   r2,[r1,#28]
              SWIVC   XWimp_OpenWindow
              SWIVC   XWimp_GetWindowState
              STRVS   r0,[sp]
              LDMFD   sp!,{r0-r5,pc}


; Find the owner of a window
;
; R2  ==> Window handle
;
; On exit
;
; R0  ==> Task handle, or -1 if unknown

FindOwner     STMFD   sp!,{r1-r5,lr}
              SUB     sp,sp,#20
              MOV     r0,#20
              MOV     r3,#0
              MOV     r4,#0
              MOV     r5,#0
              STMIA   sp,{r0-r5}
              MOV     r0,#19
              MOV     r1,sp
              MOV     r3,#-1
              SWI     XWimp_SendMessage
              MOVVC   r0,r2
              MOVVS   r0,#-1
              ADD     sp,sp,#20
              LDMFD   sp!,{r1-r5,pc}


; Iterate

NextWindow                             ROUT
              STMFD   sp!,{r3,lr}
0000          ADRL    r1, PollBlock
              LDR     r2,[r1,#28]
              CMP     r2,#-1
              BEQ     %f0010
              STR     r2,[r1]
              SWI     XWimp_GetWindowState
              BVS     %f0010
              LDR     r3,[r1,#32]                ; Ignore pane windows
              TST     r3,#1 :SHL: 5
              BNE     %b0000
              LDR     r3,[r1,#12]                ; Check for Y1 < 0 or X1 < 0. This seems to stop multidesk working
              CMP     r3,#0
              LDRGT   r3,[r1,#16]
              CMPGT   r3,#0
              BLE     %b0000
0010          LDMFD   sp!,{r3,pc}


; Must be called before the window is open

ListTasks                              ROUT
              STMFD   sp!,{r0-r6,r11,lr}
              LDR     r0,Flags
              BIC     r0,r0,#ListIsWindows
              STR     r0,Flags
              BL      OpenBack
              BVS     %f0030
0000          BL      NextWindow
              BVS     %f0030
              CMP     r2,#-1
              BEQ     %f0020
              BL      FindOwner
              CMP     r0,#-1
              BEQ     %b0000
              MOV     r3,r0                      ; Copy
              SWI     XTaskManager_TaskNameFromHandle
              BVS     %b0000
              BL      ExcludeScan
              BCS     %b0000
              MOV     r2,r0                      ; Name
              MOV     r1,sp
              MOV     r4,#11
              MOV     lr,#'!'
              STRB    lr,[r1],#1
0010          LDRB    lr,[r0],#1
              CMP     lr,#' '
              CMPHI   r4,#0
              STRHIB  lr,[r1],#1
              SUBHI   r4,r4,#1
              BHI     %b0010
              MOV     lr,#0
              STRB    lr,[r1],#1
              MOV     r0,r3
              MOV     r1,sp
              BL      DL_AddItem
              BVS     %f0030
              B       %b0000
0020          LDR     r11,DList
              TEQ     r11,#0
              LDRNE   r11,DL_Link
              TEQNE   r11,#0
              MOVNE   r0,#DL_Hot
              STRNE   r0,DL_Flags
0030          STRVS   r0,[sp]
              LDMFD   sp!,{r0-r6,r11,pc}


; Open the window centred on the screen

ShowWindow                             ROUT
              STMFD   sp!,{r0-r5,lr}
              LDR     lr,DList
              TEQ     lr,#0
              BEQ     %f0010
              ADRL    r1, PollBlock
              LDR     r0,MyWindow
              STR     r0,[r1]
              SWI     XWimp_GetWindowState
              BLVC    DL_Format
              BVS     %f0000
              ADR     r0,vduVars                 ; R4 = width, R5 = height
              SUB     sp,sp,#word * 4
              MOV     r1,sp
              SWI     XOS_ReadVduVariables
              LDMFD   sp!,{r0-r3}
              MOV     r2,r2,LSL r0
              MOV     r3,r3,LSL r1
              SUB     lr,r3,#64
              CMP     r5,lr
              MOVGE   r5,lr
              MOV     r2,r2,LSR #1
              MOV     r3,r3,LSR #1
              SUB     r0,r2,r4,LSR #1
              SUB     r1,r3,r5,LSR #1
              ADD     r2,r0,r4
              ADD     r3,r1,r5
              ADRL    lr, PollBlock
              STMIB   lr,{r0-r3}
              MOV     r1,lr
              MOV     lr,#0
              STR     lr,[r1,#20]
              STR     lr,[r1,#24]
              MOV     lr,#-1
              STR     lr,[r1,#28]
              SWI     XWimp_OpenWindow
0000          STRVS   r0,[sp]
              LDMFD   sp!,{r0-r5,pc}
0010          BL      HideWindow
              B       %b0000


HideWindow                             ROUT
              STMFD   sp!,{r0-r5,lr}
              BL      DL_DisposeAll              ; something in here can set V... [fdb]
              ADRVC   r1,MyWindow
              SWIVC   XWimp_CloseWindow
              BVS     %f0000
              ADR     r0,CaretInfo
              LDMIA   r0,{r0-r5}
              CMP     r0,#-1
              SWINE   XWimp_SetCaretPosition
0000          STRVS   r0,[sp]
              LDMFD   sp!,{r0-r5,pc}


vduVars       &       4,5,11,12,-1


; Handle selection of a task by iterating all the windows, and opening
; all of this task's windows
;
; R0  ==> Task handle to select, or -2 to select the icon bar

SelectTask                             ROUT
              CMP     r0,#-2
              BEQ     ShowIconBar
              STMFD   sp!,{r1-r6,r9,r10,lr}
              MOV     r4,r0                      ; Task handle to match
              MOV     r5,#0                      ; How many windows?
              BL      OpenBack
              BVS     %f0060
0000          BL      NextWindow
              BVS     %f0060
              CMP     r2,#-1
              BEQ     %f0010
              BL      FindOwner
              TEQ     r0,r4
              ADDEQ   r5,r5,#1
              B       %b0000
0010          MOVS    r3,r5,LSL #2               ; Allocate enough memory
              BEQ     %f0060
              BL      Alloc
              BVS     %f0060
              MOV     r10,r2                     ; Address
              BL      OpenBack                   ; Back to the start of the pile
              BVS     %f0070
              MOV     r9,r10
0020          BL      NextWindow
              BVS     %f0070
              CMP     r2,#-1
              BEQ     %f0030
              BL      FindOwner
              TEQ     r0,r4
              STREQ   r2,[r9],#word              ; Save window handle
              B       %b0020
0030          MOV     r9,#-1                     ; open behind here
0040          SUBS    r5,r5,#1
              BMI     %f0050
              LDR     r0,[r10,r5,LSL #2]         ; get window handle
              BL      OpenWindow
              BVS     %f0070
              B       %b0040
0050          MOV     r2,r10
              BL      Free
0060          LDMFD   sp!,{r1-r6,r9,r10,pc}
0070          MOV     r2,r10
              BL      Free
              CMP     r0,#V_set_value
              CMNVC   r0,#V_set_value
              LDMFD   sp!,{r1-r6,r9,r10,pc}


; R0  ==> Window to open
; R9  ==> Window to open it behind
;
; On exit
;
; R9  ==> R0 on entry

OpenWindow    STMFD   sp!,{r1-r3,r6,lr}
              ADRL    r1, PollBlock
              STR     r0,[r1]
              CMP     r9,#-1                     ; If opening at front try to place the caret
              STREQ   r0,FrontWindow
              SWI     XWimp_GetWindowState
              STRVC   r9,[r1,#28]
              MOVVC   r0,#2
              LDRVC   r2,[r1]
              MOVVC   r9,r2
              MOVVC   r3,#-1
              SWIVC   XWimp_SendMessage
              LDMFD   sp!,{r1-r3,r6,pc}


; R1  ==> Buffer to fill
; R2  ==> Window handle
;
; On exit
;
; C   ==> 1 if not a document type window

GetTitle
              STMFD   sp!,{r0-r8,lr}             ; why only save r0-r5? [fdb]
              ADRL    r1, PollBlock
              STR     r2,[r1]
              ORR     r1,r1,#1
              SWI     XWimp_GetWindowInfo
              BVS     %f0020
              BIC     r1,r1,#1
              LDR     lr,[r1,#28 + 4]            ; Check window flags: should have a title
              TST     lr,#1<<31
              MOVNE   r0,#1<<26
              MOVEQ   r0,#1
              TST     lr,r0
              BEQ     %f0030
              LDR     lr,[r1,#56 + 4]            ; window has a title, get flags for it
              AND     r3,lr,#2_11
              TEQ     r3,#2_01
              BNE     %f0030
              ADD     r3,r1,#72 + 4
              LDMIA   r3,{r3-r5}
              TST     lr,#1<<8                   ; Test indirect bit
              LDREQ   r0,[sp,#word * 1]
              MOVEQ   lr,#0
              STMEQIA r0,{r3-r5,lr}
              BEQ     %f0020
              BL      FindOwner                  ; Get task handle
              CMP     r0,#-1                     ; R3 => Text buffer, R4 => Validation string, R5 = Buffer length (includes terminator)
              BEQ     %f0030
              MOV     r8,r0                      ; save our window handle [fdb]
              MOV     r1,r3
              LDR     r2,MyTask
              LDR     r3,[sp,#word * 1]          ; buffer address
              CMP     r5,#256                    ; don't transfer more than 256 bytes or we may scribble all over the stack (StrongHelp
              MOVHI   r5,#256                    ; windows will do that, as their title buffers seem to be almost 1000 bytes long) [fdb]
              MOV     r4,r5
0000          MOV     r0,r8
              SWI     XWimp_TransferBlock
              MOV     r6,r3
              MOV     r7,r4
0010          LDRB    lr,[r6],#1
              CMP     lr,#' '
              BLT     %f0020
              SUBS    r7,r7,#1
              BNE     %b0010
              CMP     r5,#256                    ; no terminator found and already 256 bytes transferred? [fdb]
              ADDLO   r1,r1,r4
              ADDLO   r3,r3,r4
              ADDLO   r5,r5,#16
              MOVLO   r4,#16
              BLO     %b0000
              MOV     lr,#10                     ; too bad - just add terminator and be done with it [fdb]
              STRB    lr,[r6,#-1]
0020          STRVS   r0,[sp]
              LDMVSFD sp!,{r0-r8,pc}
              CMN     r0,#0                      ; C=0 [fdb]
              LDMFD   sp!,{r0-r8,pc}
0030          CMP     r0,#0                      ; C=1 [fdb]
              LDMFD   sp!,{r0-r8,pc}


; List the windows belonging to the task which owns the topmost window

ListWindows
              STMFD   sp!,{r0-r4,r11,lr}
              LDR     r0,Flags
              ORR     r0,r0,#ListIsWindows
              STR     r0,Flags
              BL      OpenBack
              BVS     %f0040
              MOV     r4,#-1
0000          LDR     r2,PollBlock
              BL      FindOwner
              CMP     r0,#-1
              BEQ     %f0010
              SWI     XTaskManager_TaskNameFromHandle
              BVS     %f0010
              BL      ExcludeScan
              MOVCC   r4,r2
0010          BL      NextWindow
              BVS     %f0040
              CMP     r2,#-1
              BNE     %b0000
              MOV     r2,r4
              LDR     lr,[ws,#40]
              CMP     r2,lr
              CMPNE   r2,#-1
              BEQ     %f0040
              BL      FindOwner
              MOV     r4,r0
              BL      OpenBack                   ; Now list all the windows
              BVS     %f0040
0020          BL      NextWindow
              BVS     %f0040
              CMP     r2,#-1
              BEQ     %f0030
              BL      FindOwner
              CMP     r0,#-1
              BEQ     %b0020
              TEQ     r0,r4
              BNE     %b0020
              ADRL    r1, Stack                  ; Add the window to the list
              BL      GetTitle                   ; on exit V=1 on error, C=1 if not a document type window [fdb]
              BVS     %f0040
              BCS     %b0020
              MOV     r0,r2
              MOV     r2,r1
              BL      DL_AddItem
              BVS     %f0040
              B       %b0020
0030          LDR     r11,DList
              TEQ     r11,#0
              LDRNE   r11,DL_Link
              TEQNE   r11,#0
              MOVNE   r0,#DL_Hot
              STRNE   r0,DL_Flags
0040          STRVS   r0,[sp]
              LDMFD   sp!,{r0-r4,r11,pc}


; R0 ==> Window handle

SelectWindow  STMFD   sp!,{r0,r9,lr}
              MOV     r9,#-1
              BL      OpenWindow
              STRVS   r0,[sp]
              LDMFD   sp!,{r0,r9,pc}


; R0  ==> Handle for item to remove

RemoveHandle                           ROUT
              STMFD   sp!,{r11,lr}
              BL      DL_Find
              TEQ     r11,#0
              BEQ     %f0000
              BL      DL_Dispose
              BLVC    ShowWindow
              BLVC    Redisplay
0000          LDMFD   sp!,{r11,pc}


Redisplay
              STMFD   sp!,{r0-r6,lr}
              LDR     lr,DList
              TEQ     lr,#0
              BEQ     %f0000
              ADRL    r1, PollBlock
              LDR     r0,[ws,#&014]
              SWI     XWimp_GetWindowState
              LDMIA   r1,{r0-r6}                 ; Convert bounding box to workarea coordinates
              SUB     r5,r1,r5                   ; r1 is left x, r4 is top y, r5 is scroll x, r6 is scroll y
              SUB     r6,r4,r6
              SUB     r1,r1,r5
              SUB     r2,r2,r6
              SUB     r3,r3,r5
              SUB     r4,r4,r6
              SWI     XWimp_ForceRedraw
0000          STRVS   r0,[sp]
              LDMFD   sp!,{r0-r6,pc}


PreScan                                ROUT
              STMFD   sp!,{r0,r11,lr}
              LDR     r11,DList
0000          TEQ     r11,#0
              LDRNE   r0,DL_Flags
              ORRNE   r0,r0,#DL_Lost
              STRNE   r0,DL_Flags
              LDRNE   r11,DL_Link
              BNE     %b0000
              BL      OpenBack
              STRVS   r0,[sp]
              LDMFD   sp!,{r0,r11,pc}


PostScan                               ROUT
              STR     lr,[sp,#-word]!
              CMP     r1,#0
              BEQ     %f0000
              BL      ShowWindow
              BLVC    Redisplay
0000          LDR     pc,[sp],#word


; Check that none of the windows have been closed

CheckWindows                           ROUT
              STMFD   sp!,{r0-r2,r11,lr}
              BL      PreScan
              BVS     %f0040
0000          BL      NextWindow
              BVS     %f0040
              CMP     r2,#-1
              BEQ     %f0010
              MOV     r0,r2
              BL      DL_Find
              TEQ     r11,#0
              LDRNE   r0,DL_Flags
              BICNE   r0,r0,#DL_Lost
              STRNE   r0,DL_Flags
              B       %b0000
0010          LDR     r11,DList
              MOV     r1,#0                      ; Number of list windows
0020          TEQ     r11,#0
              BEQ     %f0030
              LDR     r2,DL_Flags
              TST     r2,#DL_Lost
              LDREQ   r11,DL_Link
              BEQ     %b0020
              LDR     r0,DL_Handle               ; Window is lost
              LDR     r2,CaretInfo
              TEQ     r0,r2
              MOVEQ   r2,#-1
              STREQ   r2,CaretInfo
              LDR     r0,DL_Link
              BL      DL_Dispose
              BVS     %f0040
              MOV     r11,r0
              ADD     r1,r1,#1
              B       %b0020
0030          BL      PostScan
0040          STRVS   r0,[sp]
              LDMFD   sp!,{r0-r2,r11,pc}


CheckTasks                             ROUT
              STMFD   sp!,{r0-r4,r11,lr}
              BL      PreScan
              BVS     %f0040
              LDR     r3,CaretInfo
              MOV     r4,#0
0000          BL      NextWindow
              BVS     %f0040
              CMP     r2,#-1
              BEQ     %f0010
              TEQ     r2,r3
              MOVEQ   r4,#1
              BL      FindOwner
              CMP     r0,#-1
              BEQ     %b0000
              BL      DL_Find
              TEQ     r11,#0
              LDRNE   r0,DL_Flags
              BICNE   r0,r0,#DL_Lost
              STRNE   r0,DL_Flags
              B       %b0000
0010          CMP     r4,#0
              MOVEQ   r4,#-1
              STREQ   r4,CaretInfo
              LDR     r11,DList
              MOV     r1,#0                      ; Number of lost tasks
0020          TEQ     r11,#0
              BEQ     %f0030
              LDR     r2,DL_Flags
              TST     r2,#DL_Lost
              LDREQ   r11,DL_Link
              BEQ     %b0020
              LDR     r0,DL_Link                 ; Task is lost
              BL      DL_Dispose
              BVS     %f0040
              MOV     r11,r0
              ADD     r1,r1,#1
              B       %b0020
0030          BL      PostScan
0040          STRVS   r0,[sp]
              LDMFD   sp!,{r0-r4,r11,pc}


; Try to place the caret in a window

PlaceCaret                             ROUT
              STMFD   sp!,{r0-r6,lr}
              LDR     r0,FrontWindow
              CMP     r0,#-1
              BEQ     %f0020
              MOV     lr,#-1
              STR     lr,FrontWindow
              BL      CaretHistory
              TEQ     r6,#0
              BNE     %f0010
              ADRL    r1, PollBlock
              STR     r0,[r1]
0000          SWI     XWimp_GetWindowState
              BVS     %f0020
              LDR     r0,[r1,#28]
              CMP     r0,#-1
              BEQ     %f0020
              STR     r0,[r1]
              BL      CaretHistory
              TEQ     r6,#0
              BEQ     %b0000
0010          LDMIA   r6,{r0-r5}                 ; R6 points to caret info
              SWI     XWimp_SetCaretPosition
              MOV     r0,#-1
              STR     r0,CaretInfo
0020          STRVS   r0,[sp]
              LDMFD   sp!,{r0-r6,pc}


; Display the icon bar

ShowIconBar                            ROUT
              STMFD   sp!,{r0-r2,lr}
              ADRL    r1, PollBlock
              MOV     r0,#-2
              STR     r0,[r1]
              SWI     XWimp_GetWindowState
              BVS     %f0000
              LDR     r2,[r1,#28]
              CMP     r2,#-1
              MOVNE   r0,#&1DC                   ; Not at the front, so fake the keystroke
              SWINE   XWimp_ProcessKey
0000          STRVS   r0,[sp]
              LDMFD   sp!,{r0-r2,pc}


; Ensure the font handle is valid and return it in R0

EnsureFont
              LDR     r0,Font
              CMP     r0,#-1                     ; V=0 [fdb]
              MOVNE   pc,lr
              STMFD   sp!,{r1-r5,lr}
              ADR     r1,FontName
              MOV     r2,#12 * 16
              MOV     r3,#12 * 16
              MOV     r4,#0
              MOV     r5,#0
              SWI     XFont_FindFont
              STRVC   r0,Font
              LDMFD   sp!,{r1-r5,pc}

FontName      =       "Homerton.Medium",0
              ALIGN


; R0  ==> TextOp reason code
;         0 -- set font colours
;         1 -- get string width
;         2 -- paint string
;         3 -- set font colours (desktop colours)
;
; when R0 is 0
;
; R1  ==> background colour
; R2  ==> foreground colour
;
; when R0 is 1
;
; R1  ==> string
; R2  ==> string length
;
; on exit
;
; R0  ==> width in OS units
;
; when R0 is 2
;
; R0  ==> 2 or flags
; R1  ==> string
; R4  ==> x0
; R5  ==> y0
;
; extra code!!!
;
; when R0 is 3
;
; R1  ==> background desktop colour
; R2  ==> foreground desktop colour

TextOp                                 ROUT
              STMFD   sp!,{r0-r8,lr}
              AND     lr,r0,#&FF
              TEQ     lr,#3
              BNE     %f0005
              BL      GetColours
              BIC     r0,r0,#&FF
              STR     r0,[sp,#word * 0]
0005          BL      WimpVersion
              LDR     lr,ThreeFifty
              CMP     r6,lr
              BGE     %f0110
              B       %f0040
0000          AND     r6,r0,#&FF
              CMP     r6,#1
              BEQ     %f0010
              BHI     %f0030

; Function 0. Set text colours

              MOV     r0,r1
              MOV     r3,#0
              MOV     r4,#0
              SWI     XColourTrans_SetGCOL
              STRVS   r0,[sp]
              LDMFD   sp!,{r0-r8,pc}

ThreeFifty    &       350

; Function 1. Measure string

0010          MOV     r0,#0
0020          TEQ     r2,#0
              LDRNEB  lr,[r1],#1
              SUBNE   r2,r2,#1
              TEQNE   lr,#0
              ADDNE   r0,r0,#16
              BNE     %b0020
              STR     r0,[sp]
              LDMFD   sp!,{r0-r8,pc}

; Function 2. Paint string

0030          MOV     r0,#4
              MOV     r1,r4
              ADD     r2,r5,#24
              SWI     XOS_Plot
              LDR     r0,[sp,#word * 1]
              SWI     XOS_Write0
              LDMFD   sp!,{r0-r8,pc}

; Wimp 3.22 - 3.49

0040          BL      EnsureFont
              BVS     %f0050
              CMP     r0,#0
              BGT     %f0060

; Revert to system font

0050          LDR     r0,[sp,#word * 0]
              B       %b0000

; Font handle in R0

0060          LDRB    r6,[sp,#word * 0]
              CMP     r6,#1
              BEQ     %f0070
              BHI     %f0100

; Function 0. Set text colours

              MOV     r3,#14
              MOV     lr,r1
              MOV     r1,r2
              MOV     r2,lr
              SWI     XColourTrans_SetFontColours
              STRVS   r0,[sp]
              LDMFD   sp!,{r0-r8,pc}

; Find string width

0070          MOV     r7,r2                      ; String length
              MOV     r2,#&380
              MOV     r3,#&7FFFFFFF
              MOV     r4,#&7FFFFFFF
              MOV     r5,#0
              MOV     r6,#0
              SWI     XFont_ScanString
              BVS     %f0120
              ADD     r3,r3,#400
              SUB     r3,r3,#1
              MOV     r2,#400
            ; DIV     r0, r3, r2, lr
              MOV     lr,r2
              CMP     lr,r3,LSR #1
0080          MOVLS   lr,lr,LSL #1
              CMP     lr,r3,LSR #1
              BLS     %b0080
              MOV     r0,#0
0090          CMP     r3,lr
              SUBCS   r3,r3,lr
              ADC     r0,r0,r0
              MOV     lr,lr,LSR #1
              CMP     lr,r2
              BCS     %b0090
            ;
              STR     r0,[sp]
              LDMFD   sp!,{r0-r8,pc}

; Paint the string
;
; R1  ==> String
; R4  ==> X coordinate
; R5  ==> Y coordinate

0100          MOV     r2,#&310
              MOV     r3,r4
              MOV     r4,r5
              MOV     r5,#0
              MOV     r6,#0
              MOV     r7,#0
              SWI     XFont_Paint
              STRVS   r0,[sp]
              LDMFD   sp!,{r0-r8,pc}
0110          SWI     XWimp_TextOp
0120          STR     r0,[sp]
              LDMFD   sp!,{r0-r8,pc}


; R1  ==> Foreground
; R2  ==> Background
;
; On exit
;
; R1  ==> Foreground as RGB
; R2  ==> Background as RGB

GetColours    STMFD   sp!,{r3,lr}
              SUB     sp,sp,#word * 20
              MOV     r3,r1
              MOV     r1,sp
              SWI     XWimp_ReadPalette
              LDRVC   r1,[sp,r3,LSL #2]
              LDRVC   r2,[sp,r2,LSL #2]
              ADD     sp,sp,#word * 20
              LDMFD   sp!,{r3,pc}


; Return current wimp version
;
; On exit
;
; R6  ==> Wimp version

WimpVersion                            ROUT
              STMFD   sp!,{r0-r5,lr}
              MOV     r0,#18
              ADR     r1,WimpName
              SWI     XOS_Module
              BVS     %f0020
              LDR     r0,[r3,#word * 5]
              ADD     r0,r0,r3
              LDRB    r1,[r0],#1                 ; Now look for a string which is like #.## where # is a digit
              LDRB    r2,[r0],#1
              LDRB    r3,[r0],#1
              LDRB    r4,[r0],#1
              TEQ     r1,#0
              TEQNE   r2,#0
              TEQNE   r3,#0
              TEQNE   r4,#0
              BEQ     %f0020
0000          CMP     r1,#'0'
              RSBCSS  lr,r1,#'9'
              CMPCS   r3,#'0'
              RSBCSS  lr,r3,#'9'
              CMPCS   r4,#'0'
              RSBCSS  lr,r4,#'9'
              BCC     %f0010
              TEQ     r2,#'.'
              BNE     %f0010
              SUB     r1,r1,#'0'
              SUB     r3,r3,#'0'
              SUB     r4,r4,#'0'
              MOV     r1,r1,LSL #2               ; r1 * 4                 ; 64 + 32 + 4
              ADD     r6,r1,r1,LSL #3            ; + r1 * (4 * 8)
              ADD     r6,r6,r1,LSL #4            ; + r1 * (4 * 16)
              ADD     r6,r6,r3,LSL #1            ; + r3 * 2
              ADD     r6,r6,r3,LSL #3            ; + r3 * 8
              ADD     r6,r6,r4                   ; + r4
              LDMFD   sp!,{r0-r5,pc}
0010          MOV     r1,r2
              MOV     r2,r3
              MOV     r3,r4
              LDRB    r4,[r0],#1
              TEQ     r4,#0
              BNE     %b0000
0020          MOV     r6,#300
              LDMFD   sp!,{r0-r5,pc}

WimpName      =       "WindowManager",0
              ALIGN


              =       "by Andy Armstrong for Armstrong Walker",0
              ALIGN

              END
