        TTL     => ModHand - the Relocatable Module Handler


ExtraRMANeeded * 24*1024 ; Amount you get extra on top of what you configured


; Test version, incorporating multiple incarnation attempt

; The module handler needs to know the structure of the HPD, and nodes.
; See RMTidy in particular.

;**************************************************************
;
; Module chain structure: ModuleList points at a (singly-linked) list of
; nodes with following fields:

                        ^  0
Module_chain_Link       #  4   ; the link to the next module info block
Module_code_pointer     #  4   ; pointer to the module.
Module_Hardware         #  4   ; hardware base for podules; 0 for soft loaders
Module_incarnation_list #  4   ; pointer to list of incarnation specifiers

ModInfo                 *  @

; The incarnation list is a list of sub-nodes, one for each incarnation.

                        ^  0
Incarnation_Link        #  4   ; link to next incarnation
Incarnation_Workspace   #  4   ; 4 private bytes for this life
Incarnation_Postfix     #  0   ; postfix string starts here

; Incarnations are distinguished by their postfix, which is separated
; from the module name by a special character:

Postfix_Separator       *  "%"

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

; Handler initialisation.
; registers preserved

ModuleInit   ROUT                        ; call here on system startup
     Push   "R0-R3, R6-R8, R10, R12, lr"

     MOV     R0, #HeapReason_Init        ; First initialise the heap
     MOV     R1, #RMAAddress
     LDR     R3, [R1, #:INDEX: hpdend]   ; saved for us during init.
     SWI     XOS_Heap

     BL      GetUnplugCMOS               ; unplug bits -> R6
     TEQP    PC, #SVC_mode               ; clc
     ADRL    R1, SysModules_Info+4
01   LDR     R10, [R1, #-4]              ; loop adding modules in the ROM.
     TEQ     R10, #0
     BEQ     %FT02
     MOV     R0, #ModHandReason_AddArea
     SWICC   XOS_Module
     LDR     R10, [R1, #-4]
     ADD     R1, R1, R10
     MOVS    r7, r7, LSR #1
     MOVS    R6, R6, RRX                 ; next bit -> C
     B       %BT01

02   MOV     R3, #0
     MOV     R0, #0
     MOV     R2, #-1
03   BL      NextPoduleModule
     BEQ     %FT04

     Push   "R0-R2"
     CMP     R1, #0
     MOV     R0, #ReadCMOS
     SWINE   XOS_Byte
     MOVEQ   R2, #0
     LDR     R6, [stack, #4*2]
     CMP     R6, #8
     MOVLT   R8, #1
     MOVGE   R8, #0
     TST     R2, R8, LSL R6
     LDR     R2, [stack]
     SUB     R2, R2, #1
     ADRL    R1, crstring
     MOV     R0, #ModHandReason_AddPoduleModule
     SWIEQ   XOS_Module
     Pull   "R0-R2"
     B       %BT03

04   MOV     R1, #RMASizeCMOS
     MOV     R0, #ReadCMOS
     SWI     XOS_Byte
     MOV     R0, #0
     LDR     R0, [R0, #Page_Size]
     MULTIPLY R3, R0, R2                 ; size spare wanted
     ADD     r3, r3, #ExtraRMANeeded
     MOV     R0, #ModHandReason_Claim
     SWI     XOS_Module
     MOV     R0, #ModHandReason_Free
     SWI     XOS_Module
     Pull   "R0-R3, R6-R8, R10, R12, PC"

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

; start of module handler SWI

     GBLA  mhrc
mhrc SETA 0

     MACRO
$l   ModuleDispatchEntry $entry
$l   B      Module_$entry
     ASSERT ModHandReason_$entry = mhrc
mhrc SETA   mhrc + 1
     MEND

ModuleHandler ROUT

     CMP      R0, #(NaffSWI - (.+12))/4     ; Range check
     ADDLS    pc, pc, R0, LSL #2            ; dispatch
     B        NaffSWI

     ModuleDispatchEntry Run
     ModuleDispatchEntry Load
     ModuleDispatchEntry Enter
     ModuleDispatchEntry ReInit
     ModuleDispatchEntry Delete
     ModuleDispatchEntry RMADesc
     ModuleDispatchEntry Claim
     ModuleDispatchEntry Free
     ModuleDispatchEntry Tidy
     ModuleDispatchEntry Clear
     ModuleDispatchEntry AddArea
     ModuleDispatchEntry CopyArea
     ModuleDispatchEntry GetNames
     ModuleDispatchEntry ExtendBlock
     ModuleDispatchEntry NewIncarnation
     ModuleDispatchEntry RenameIncarnation
     ModuleDispatchEntry MakePreferred
     ModuleDispatchEntry AddPoduleModule
     ModuleDispatchEntry LookupName
     ModuleDispatchEntry EnumerateROM_Modules

NaffSWI                                     ; Set V and return
        ADR     R0, ErrorBlock_BadModuleReason
BumDealInModule
        B       SLVK_SetV

     MakeErrorBlock BadModuleReason

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

Module_Run      ROUT
       TEQP     PC, #SVC_mode               ; interrupts on
       Push    "R9, lr"
       BL       Load_Module
       BVS      LoadFailed
  ;     BL       EnvStringSkipName - done in load
EnterIt
 ; R9 now ptr to node, R10 ptr to command string to set up.
 ; Enters preferred incarnation.

       LDR      R12, [R9, #Module_incarnation_list]
       ADD      R12, R12, #Incarnation_Workspace

       LDR      R9, [R9, #Module_code_pointer]
       LDR      R11, [R9, #Module_Start]
       TEQ      R11, #0
       Pull    "R9, lr", EQ
       ExitSWIHandler EQ

       Push    "R1-R3"
       MOV      R1, R10
       MOV      R0, #FSControl_StartApplication
       MOV      R2, R9
       LDR      R3, [R9, #Module_Title]     ; prefix with module title
       ADD      R3, R3, R9

       SWI      XOS_FSControl
       BVS      CantGoIntoModule

       LDR      stack, =SVCSTK
       MOV      R0, R10
       TEQP     pc, #0
       TST      R11, #ARM_CC_Mask           ; check for B startit, etc.
       MOVNE    R11, #0
       ADD      PC, R9, R11

CantGoIntoModule
       Pull    "R1-R3"
LoadFailed
       Pull    "R9, lr"
       B        BumDealInModule

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

Module_Load     ROUT
        TEQP    PC, #SVC_mode               ; interrupts on
        Push    "R9, lr"
        BL      Load_Module
        Pull    "R9, lr"
        B       SLVK_TestV

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

Module_Enter    ROUT
       Push    "R9, lr"                     ; ready for EnterIt
       Push    "R0-R4"
       BL       lookup_commoned

       STRVS    R0, [stack]
       Pull    "R0-R4, R9, lr", VS
       BVS      BumDealInModule

       BLGT     PreferIncarnation
       Pull    "R0-R4"
       MOV      R10, R2                     ; envstring pointer
       B        EnterIt

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

Module_ReInit   ROUT
       Push    "R0-R4, R9, lr"

       BL       lookup_commoned
       BVS      %FT01

       ADDEQ    R3, R9, #Module_incarnation_list
       LDREQ    R12, [R9, #Module_incarnation_list]

 ;    R12 -> incarnation node, R3  -> previous incarnation

       MOV      R10, #1                     ; fatal die
       BL       CallDie
       BVS      %FT03

       SUB      R10, R1, #1
       BL       EnvStringSkipName

       BL       CallInit
       BLVS     LoseModuleSpace_if_its_the_only_incarnation
       STRVC    R12, [R3, #Incarnation_Link]
03     STRVS    R0, [stack]
       Pull    "R0-R4, R9, lr"
        B       SLVK_TestV


01     LDR      R11, [R0]
       LDR      R2, =ErrorNumber_RMNotFound
       CMP      R11, R2
       BEQ      %FT02
05
       SETV
       B        %BT03

02     MOV      R0, #0
       BL       AddModuleIfInROM
       B        %BT03

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

Module_Delete   ROUT
       Push    "R0-R4, R9, lr"
       BL       lookup_commoned
       BVS      %FT01

       ADDEQ    R3, R9, #Module_incarnation_list
       LDREQ    R12, [R9, #Module_incarnation_list]

 ;    R12 -> incarnation node, R3  -> previous incarnation

       BL       KillIncarnation
01     STRVS    R0, [stack]
       Pull    "R0-R4, R9, lr"
        B       SLVK_TestV

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

Module_Free      ROUT
Module_RMADesc
         Push   "R0, R1, lr"

         SUB     R0, R0, #(ModHandReason_RMADesc-HeapReason_Desc)
 ASSERT HeapReason_Desc-HeapReason_Free=ModHandReason_RMADesc-ModHandReason_Free
         MOV     R1, #RMAAddress
         SWI     XOS_Heap
         STRVS   R0, [stack]
         Pull   "R0, R1, lr"
        B       SLVK_TestV

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

Module_Claim  ROUT
         Push   "R0, R1, lr"
         BL      RMAClaim_Chunk
         STRVS   R0, [stack]
         Pull   "R0, R1, lr"
        B       SLVK_TestV

;*************************************************************
; Garbage collect the RMA. We know there's always one module,
; and some RMA space.

Module_Tidy      ROUT
         Push   "R0-R6, R9, lr"

         TEQP    PC, #SVC_mode

         MOV     r0, #0
         LDR     r0, [r0, #Curr_Active_Object]
         MOV     r1, #RMAAddress
         LDR     r2, [r1, #:INDEX:hpdend]
         SUBS    r0, r0, r1
         CMPGT   r2, r0
         ADRGTL  r0, ErrorBlock_CantKill
         SETV    GT

         BLVC    Genocide                   ; warn all of impending calamity
         BVS     ExitRMTidy

; now for the great adventure. R2 is old contents of ModuleList
; First build a list of block addresses, together with address of pointer to
; block, in ascending order.

         LDR     R2, =ScratchSpace
         STR     R9, [R2], #4               ; save original module list
         MOV     R3, R2

; in loop, R0 is block address, R1 is pointer to pointer to block
; R2 is ptr to list start
; R3 is list limit

01      ADD      R1, R9, #Module_code_pointer
        LDR      R0, [R1]
        BL       %FT10                      ; insert pair

        LDR      R1, [R9, #Module_incarnation_list]
02      ADD      R1, R1, #Incarnation_Workspace
        LDR      R0, [R1]
        CMP      R0, #0
        BLNE     %FT10                      ; insert workspace block if there
        LDR      R1, [R1, #-Incarnation_Workspace]
        CMP      R1, #0
        BNE      %BT02

        LDR      R9, [R9, #Module_chain_Link] ; next module
        CMP      R9, #0
        BNE      %BT01

; Now iterate over claimed blocks, to discard non-heap pointers.

        MOV      R5, R2                     ; currblock ptr

; if hpdfree <> hpdsize then
;    doblock (hpdsize, hpdfree=Nil -> hpdbase, hpdfree)

        MOV      R12, #RMAAddress
        LDR      R4, [R12, #:INDEX: hpdfree]
        CMP      R4, #Nil
        ADDNE    R4, R4, #:INDEX: hpdfree   ; convert to heapstart offset

        CMP      R4, #hpdsize
        BEQ      %FT04
        MOV      R0, #hpdsize
        CMP      R4, #Nil
        LDREQ    R1, [R12, #:INDEX: hpdbase]
        MOVNE    R1, R4
        BL       ScanAllocBlock
        CMP      R4, #Nil
        BEQ      BlocksScanned

;  while hpdfree <> Nil
;     doblock (hpdfree+fresize, step(hpdfree)=Nil -> hpdbase, hpdfree)

04      ADD      R4, R4, R12
        LDR      R1, [R4, #frelink]
        LDR      R0, [R4, #fresize]
        SUB      R4, R4, R12

        ADD      R0, R0, R4
        CMP      R1, #Nil
        ADDNE    R4, R4, R1
        MOVNE    R1, R4
        LDREQ    R1, [R12, #:INDEX: hpdbase]
        BL       ScanAllocBlock
        BNE      %BT04
       
BlocksScanned
        MOV      R3, R5                     ; new list end

; copy blocks, relocate ptrs
; R2, R3 list limits
; R12 heap start

        ADD      R0, R12, #hpdsize          ; get addr for first block
StripBlocks
        CMP      R2, R3
        BEQ      %FT09                      ; nowt to copy
        LDR      R1, [R2], #8
        CMP      R1, #0
        BEQ      StripBlocks
        LDR      R4, [R1]                   ; block size
        CMP      R1, R0
        ADDEQ    R0, R4, R0
        BEQ      StripBlocks

; R1 address of first block, R0 address to copy to - gopher it!
; R4 size
        LDR      R5, [R2, #-4]              ; pointer to
        ADD      R0, R0, #4
        STR      R0, [R5]                   ; relocate ptr
        SUB      R0, R0, #4

CopyBlock
        LDR      R5, [R1], #4
        STR      R5, [R0], #4
        SUBS     R4, R4, #4
        BGT      CopyBlock
        B        StripBlocks

09
; Update Hpd
        SUB      R0, R0, R12                ; convert to offset
        STR      R0, [R12, #:INDEX: hpdbase]
   
        MOV      R0, #Nil
        STR      R0, [R12, #:INDEX: hpdfree] ; no free list.

 ; for restarting, we need
 ; R1 -> prevmod to R9
 ; R9 -> the whinger, R12 -> incarnation list, R2 stop point
 ; R4 -> dead module list
 ; R3 -> previnc

        MOV      R9, #Module_List           ; "module" that's linked at end
        LDR      R4, =ScratchSpace
        LDR      R4, [R4]                   ; dead list
        MOV      R2, #0                     ; persuade it to step module
        MOV      R12, #0                    ; immediately.
        BL       RestartModuleStructure
        BVS      ExitRMTidy

        MOV      R0, #1
        MOV      R1, #-16*1024*1024
        SWI      XOS_ChangeDynamicArea
        CLRV

ExitRMTidy
        STRVS    R0, [stack] 
        Pull    "R0-R6, R9, lr"
        B       SLVK_TestV

;-------------------------------------------------------------------------
; RMTidy support routines

; Insertion routine. (R0, 1) is pair to insert, R2 list start, R3 list end.
; Uses R10, 11, 4

10      MOV      R11, R2           ; take curr posn ptr

        SUB      R0, R0, #4        ; genuine internal heap pointer value
11      CMP      R11, R3           ; list ended?
        BEQ      %FT13           
        LDR      R4, [R11], #8
        CMP      R4, R0            ; or right posn?
        BLO      %BT11

        SUB      R11, R11, #8      ; where we will store new entry to
        SUB      R4, R3, #4        ; R4 is OS_Word to move from.
12      LDR      R10, [R4], #-4    ; now copy between R11 and R3 up 8.
        STR      R10, [R4, #12]
        CMP      R4, R11
        BHS      %BT12

13      ADD      R3, R3, #8        ; update list end
        STMIA    R11, {R0, R1}     ; new entry in
        ADD      R0, R0, #4        ; and back to link.
        MOV      PC, lr

;-------------------------------------------------------------------
ScanAllocBlock    ROUT
 ; R0 block start
 ; R1 block end
 ; R12 heap start
 ; R3  list end
 ; R5  current list entry

 ; poke out list entries that aren't proper heap pointers

        Push     "R0, R1, R7, R8"
        MOV       R8, #0
        ADD       R0, R0, R12     ; convert to addressi
        ADD       R1, R1, R12

01      CMP       R5, R3
        BGE       %FT02
        LDR       R7, [R5], #8

        CMP       R7, R0          ; while entry at R5 LT R0 pokeout
        STRLT     R8, [R5, #-8]
        BLT       %BT01

        SUBGT     R5, R5, #8

        LDR       R7, [R0]
        ADD       R0, R0, R7      ; next block
        CMP       R0, R1          ; step block until end
        BLT       %BT01
02
        Pull     "R0, R1, R7, R8"
        MOVS      PC, lr

        LTORG

;-----------------------------------------------------------------------------

Genocide ROUT ; non-fatally kill de lot of em, stiff the module chain
              ; corrupts R1-R5, R9, R10, R12
              ; returns R9 = original module chain

         Push   "lr"
         MOV     R4, #0                     ; chain so far

FindChainEnd
         MOV     R1, #Module_List             ; prevnode
         LDR     R9, [R1, #Module_chain_Link] ; currnode
         CMP     R9, #0
         BNE     %FT01
         MOV     R9, R4
         Pull   "PC"

01       LDR     R11, [R9, #Module_chain_Link] ; lastnode?
         CMP     R11, #0
         MOVNE   R1, R9                     ; step chain
         MOVNE   R9, R11
         BNE     %BT01

         LDR     R2, [R9, #Module_incarnation_list] ; keep chain head
         ADD     R3, R9, #Module_incarnation_list
02       LDR     R12, [R3, #Incarnation_Link]       ; currinc
         CMP     R12, #0
         BNE     %FT03
         STR     R12, [R1, #Module_chain_Link]      ; remove from chain
         STR     R2, [R9, #Module_incarnation_list] ; replace incarnations
         STR     R4, [R9, #Module_chain_Link]       ; make into dead head
         MOV     R4, R9
         B       FindChainEnd

03       MOV     R10, #0                    ; not fatal indicator
         BL      CallDie
         BVC     %BT02

         MOV     R5, R12
         MOV     R12, R2
         MOV     R2, R5     ; r12 now incarnation to start, R2 stop point
         BL      RestartModuleStructure
                ; somebody winged, so try and restore consistency before error.
         Pull   "PC"

;------------------------------------------------------------------------------

RestartModuleStructure ROUT

 ; R1 -> prevmod to R9
 ; R9 -> the whinger, R12 -> incarnation list, R2 stop point
 ; R4 -> dead module list
 ; R3 -> previnc

         Push   "R0, lr"

11       CMP     R2, R12
         BNE     %FT12                      ; more incarnations to do

         CMP     R4, #0
         Pull   "R0, PC", EQ, ^
        
         MOV     R1, R9
14       MOV     R9, R4
         LDR     R4, [R4, #Module_chain_Link]
         MOV     R2,#0                      ; indicate reinit all incarnations
         LDR     R12, [R9, #Module_incarnation_list]
         STR     R2,  [R9, #Module_incarnation_list]
         ADD     R3, R9, #Module_incarnation_list ; previnc ptr

         STR     R2, [R9, #Module_chain_Link]     ; relink next
         STR     R9, [R1, #Module_chain_Link]
         B       %BT11                            ; start incarnations

12       LDR     R11, [R12, #Incarnation_Link]    ; get next in case problems
         ADRL    R10, crstring                    ; no environment
         BL      CallInit                         ; frees node if error
         BVC     %FT15

         STR     R0, [stack]
         LDR     R0, [stack, #4]
         ORR     R0, R0, #V_bit
         STR     R0, [stack, #4]
         Push   "R2"
         MOV     R2, R1                     ; prevnode
         BL      LoseModuleSpace_if_its_the_only_incarnation
         Pull   "R2"

         CMP     R9, #0                     ; did we just discard that module?
         BEQ     %BT14                      ; yup - next one

15       LDRVC   R0,  [R3, #Incarnation_Link]
         STRVC   R0,  [R12,#Incarnation_Link]
         STRVC   R12, [R3, #Incarnation_Link]
         MOVVC   R3, R12

         MOV     R12, R11
         B       %BT11                      ; next incarnation

;****************************************************************************
Module_Clear     ROUT
        Push    "R0-R3, lr"

        TEQP     PC, #SVC_mode               ; interrupts on
        MOV      R3, #0                      ; position in chain

; now find entry in chain to kill : one with successor = R3

MHC_GetEndOne
        MOV      R2, #Module_List            ; prevnode for killing
        LDR      R0, [R2, #Module_chain_Link]
        CMP      R0, R3
        Pull    "R0-R3, lr", EQ
        ExitSWIHandler EQ
MHC_StepOn
        LDR      R1, [R0, #Module_chain_Link]
        CMP      R1, R3
        MOVNE    R2, R0
        MOVNE    R0, R1
        BNE      MHC_StepOn

        LDR      R11, [R0, #Module_Hardware]
        CMP      R11, #IOC          ; skip podule modules.

        LDRLO    R11, [R0, #Module_code_pointer]
        CMPLO    R11, #RMAAddress+4*1024*1024
        MOVHS    R3, R0             ; step if not about to delete
                                    ; - don't assassinate ROM modules.
        BLLO     KillAndFree
        BVC      MHC_GetEndOne

        LDR      R3, [R2, #Module_chain_Link]
        STR      R0, [stack]
        LDR      R0, [stack, #4*4]
        ORR      R0, R0, #V_bit
        STR      R0, [stack, #4*4]
        B        MHC_GetEndOne

;*************************************************************
; AddArea:
; Entry;  R1 -> module in memory to add, leaving it in place.
; Return: registers preserved, V set if problem
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Module_AddArea   ROUT
         Push   "R9, lr"
         TEQP    PC, #SVC_mode               ; interrupts on
         ADRL    R10, crstring               ; null environment
         BL      ModuleIn_CheckForDuplicate  ; altentry to Load_Module
         Pull   "R9, lr"
        B       SLVK_TestV

;*************************************************************
; CopyArea
;    R1 -> area of memory to add to the module list,
;          copying into the RMA
;    R2 =  size of the area.

Module_CopyArea  ROUT
         Push   "R0-R5, R9, lr"
         TEQP    PC, #SVC_mode

  ; R1 address, R2 size
         BL      CheckHeader
         BVS     AreaFail

         MOV     R10, R1

         LDR     R1, [R10, #Module_Title]
         ADD     R1, R1, R10
         BL      LookUp_Module               ; check for duplicate
         BLNE    KillAndFree
         STRVS   R0, [stack]
         Pull   "R0-R5, R9, lr", VS
         BVS     SLVK_TestV

; R10 points at area
         LDR     R3, [stack, #4*2]           ; get size back
         BL      RMAClaim_Chunk
         ADRVSL  R0, ErrorBlock_MHNoRoom
         BVS     AreaFail

         MOV     R9, R2                      ; new module pointer

; copy R3 bytes from R10 to R2
01       LDR     R1, [R10], #4
         STR     R1, [R2], #4
         SUBS    R3, R3, #4
         BPL     %BT01

         ADRL    R10, crstring               ; no environment string
         MOV     R11, #0                     ; not podular
         BL      LinkAndInit

AreaFail
         STRVS   R0, [stack]
         Pull   "R0-R5, R9, lr"
        B       SLVK_TestV

;*************************************************************
; Enumerate modules
; Entry:  R0 Reason code
;         R1 module number
;         R2 incarnation number
; Exit:   R1, R2 updated to refer to next existing module
;         R3 -> module code
;         R4    private word contents
;         R5 -> postfix string

Module_GetNames  ROUT
         TEQP    PC, #SVC_mode               ; interrupts on
         MOV     R11, R1
         MOV     R12, R2
         MOV     R10, #Module_List
01       LDR     R10, [R10, #Module_chain_Link]
         CMP     R10, #0
         BEQ     %FT10                       ; no more modules
         SUBS    R11, R11, #1
         BPL     %BT01
         LDR     R3, [R10, #Module_code_pointer]
         ADD     R10, R10, #Module_incarnation_list
02       LDR     R10, [R10, #Incarnation_Link]
         CMP     R10, #0
         BEQ     %FT11                       ; no more incarnations
         SUBS    R12, R12, #1
         BPL     %BT02
         LDR     R4, [R10, #Incarnation_Workspace]
         ADD     R5, R10, #Incarnation_Postfix
         LDR     R10, [R10, #Incarnation_Link]
20       CMP     R10, #0
         ADDNE   R2, R2, #1
         MOVEQ   R2, #0
         ADDEQ   R1, R1, #1
         ExitSWIHandler

10       ADR     R0, ErrorBlock_NoMoreModules
         B       BumDealInModule
         MakeErrorBlock NoMoreModules

11       CMP     r2, #0
         LDREQ   r4, =&DEADDEAD
         MOVEQ   r10, #0
         BEQ     %BT20         ; fudge for modules that go bang in init/die
         ADR     R0, ErrorBlock_NoMoreIncarnations
         B       BumDealInModule
         MakeErrorBlock NoMoreIncarnations
         LTORG

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

Module_ExtendBlock ROUT
         Push   "R0, r1, R3, lr"

         ADD     R3, R3, #15
         BIC     R3, R3, #15

         MOV     R0, #HeapReason_ExtendBlock
         BL      DoRMAHeapOpWithExtension

         STRVS   R0, [stack]
         Pull   "R0, r1, R3, lr"
         B       SLVK_TestV

;*************************************************************
; New Incarnation
;    R1 -> module%newpostfix

Module_NewIncarnation ROUT
         Push   "R0-R4, R9, lr"
         TEQP    PC, #SVC_mode
         BL      LookUp_Module
         BEQ     CheckTheROM
         CMP     R12, #0
         BEQ     Incarnation_needed
         BGT     Incarnation_exists
         MOV     R9, R0                      ; node pointer
         MOV     R0, R1                      ; postfix
         MOV     R10, R1
         BL      EnvStringSkipName           ; envstring ptr in R10
         BL      Add_Incarnation
01       STRVS   R0, [stack]
         Pull   "R0-R4, R9, lr"
         B      SLVK_TestV

CheckTheROM
         MOV     R0, #Postfix_Separator      ; passed string must have postfix
         BL      AddModuleIfInROM
         B       %BT01

Incarnation_needed
         Pull   "R0-R4, R9, lr"
         ADR     R0, ErrorBlock_PostfixNeeded
         B       BumDealInModule
         MakeErrorBlock PostfixNeeded

Incarnation_exists
         Pull   "R0-R4, R9, lr"
         ADR     R0, ErrorBlock_IncarnationExists
         B       BumDealInModule
         MakeErrorBlock IncarnationExists

;*************************************************************
; Rename Incarnation
; R1 -> current module title
; R2 -> new postfix.

Module_RenameIncarnation ROUT
         Push   "R0-R4, R9, lr"
         BL      lookup_commoned
         BVS     %FT01

; R12 -> incarnation node    (0 for not specified)
; R3  -> previous incarnation

         MOV     R11, R12
         MOV     R0, R9                      ; check incarnation
         LDR     R1, [stack, #4*2]           ; not already there
         BL      FindIncarnation
         BNE     %FT03                       ; already exists
         MOV     R12, R11

         CMP     R12, #0
         ADDEQ   R3, R9, #Module_incarnation_list
         LDREQ   R12, [R9, #Module_incarnation_list]
         MOV     R11, R3

         ADD     R1, R12, #Incarnation_Postfix
         BL      %FT10                       ; old postfix length -> R0
         MOV     R10, R0
         LDR     R1, [stack, #4*2]           ; new postfix
         BL      %FT10                       ; new length - > R0
         SUB     R3, R0, R10

         MOV     R2, R12                     ; incarnation node
         MOV     R0, #HeapReason_ExtendBlock
         BL      DoSysHeapOpWithExtension
         BVS     %FT01

         STR     R2, [R11, #Incarnation_Link] ; relink
         ADD     R2, R2, #Incarnation_Postfix
         LDR     R1, [stack, #4*2]
02       LDRB    R0, [R1], #1
         CMP     R0, #" "
         MOVLE   R0, #0
         STRB    R0, [R2], #1
         BGT     %BT02
01       STRVS   R0, [stack]
         Pull   "R0-R4, R9, lr"
         B      SLVK_TestV

03       ADR     R0, ErrorBlock_IncarnationExists
         SETV
         B       %BT01

10       MOV     R0, #0
11       LDRB    R3, [R1, R0]
         CMP     R3, #" "
         ADDGT   R0, R0, #1
         BGT     %BT11
         MOV     PC, lr

;*************************************************************
; MakePreferred
;   R1 -> name

Module_MakePreferred ROUT
         Push   "R0-R4, R9, lr"
         BL      lookup_commoned
         BVS     %FT01
         BLGT    PreferIncarnation    ; only prefer it if found!
01
         Pull   "R0-R4, R9, lr"
         B      SLVK_TestV

;*************************************************************
; AddPoduleModule
;    R1 -> envstring
;    R2 chunk number
;    R3 podule number

Module_AddPoduleModule ROUT
         Push   "R0-R4, lr"
         TEQP    PC, #SVC_mode               ; interrupts on
         MOV     R0, R2
         SWI     XPodule_EnumerateChunks
         BVS     %FT99
         CMP     R2, #OSType_Module
         BNE     %FT98

         Push   "R1"                         ; size
         MOV     R1, R4
         BL      LookUp_Module               ; check for duplicate
         BLNE    KillAndFree
         Pull   "R3"                         ; get size back
         BLVC    RMAClaim_Chunk
         BVS     %FT99

         LDR     R0, [stack, #4*2]
         LDR     R3, [stack, #4*3]
         SWI     XPodule_ReadChunk
 ; size in R3, address in R2
         MOV     R1, R2
         LDR     R2, [R1, #-4]
         BLVC    CheckHeader
         BVS     %FT97                       ; free space too

         MOV     R9, R1
         LDR     R10, [stack, #4]            ; envptr
         MOV     R11, #&33C0000
         ADD     R11, R11, R3, LSL #14       ; correct h'ware base.
         BL      LinkAndInit

99       STRVS   R0, [stack]
         Pull   "R0-R4, lr"
         B      SLVK_TestV

98       ADR     R0, ErrorBlock_ChunkNotRM
96
         SETV
         B       %BT99
         MakeErrorBlock ChunkNotRM

97       MOV     R2, R1                 ; free claimed RMA space
         MOV     R1, #RMAAddress
         Push   "R0"
         MOV     R0, #HeapReason_Free
         SWI     XOS_Heap
         Pull   "R0"
         B       %BT96

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; LookupName
;    Take module name, return info on it suitable for use with Enumeration
;      (e.g. to get all incarnations of it)
;  In :   R1 -> name
;  Out:   R1 module number          \  of THIS module; first enumerate
;         R2 incarnation number     /  call will give back this module
;         R3 -> module code
;         R4    private word contents
;         R5 -> postfix string

Module_LookupName ROUT
         Push   "R0-R4, R9, lr"
         BL      lookup_commoned
         BVC     %FT01
         STR     R0, [stack]
         Pull   "R0-R4, R9, lr"
         B      SLVK_SetV

01       MOV     R1, #0               ; module number
         MOV     R0, #Module_List

; R9  -> module chain node
; R12 -> incarnation node    (0 for not specified, -1 for not found)

         LDREQ   R12, [R9, #Module_incarnation_list]  ; preferred inc.

02       LDR     R0, [R0]
         CMP     R0, R9
         ADDNE   R1, R1, #1
         BNE     %BT02
         ADD     R0, R0, #Module_incarnation_list
         MOV     R2, #0
03       LDR     R0, [R0]
         CMP     R0, R12
         ADDNE   R2, R2, #1
         BNE     %BT03
         LDR     R3, [R9, #Module_code_pointer]
         LDR     R4, [R12, #Incarnation_Workspace]
         ADD     R5, R12, #Incarnation_Postfix
         LDR     r0, [sp], #5*4            ; Load r0, skip r1-r4
         Pull   "R9, lr"
         ExitSWIHandler

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; EnumerateROM_Modules
;
;  In :   R1 = module number
;         R2 = -1    => ROM
;            = other => Podule R2
;
;  Out:   R1 = incremented: next call will return next module
;         R2 = preserved
;         R3 -> name
;         R4 = -1 => unplugged
;            =  0 => inserted but not currently in the module chain
;            =  1 => active
;            =  2 => running
;         R5 =  chunk number of podule RM

Module_EnumerateROM_Modules ROUT

         Push   "R6, r7, lr"
         CMP     R2, #-1
         BNE     %FT01
         ADRL    R10, SysModules_Info+4
         ADD     R11, R1, #1
10       LDR     R12, [R10, #-4]
         CMP     R12, #0
         MOVEQ   R2, #0                ; end of ROM: flip to podule space
         MOVEQ   R1, #0
         BEQ     %FT01
         SUBS    R11, R11, #1
         ADDNE   R10, R10, R12
         BNE     %BT10
         
; R10 is module base, R1 the CMOS bit number
         LDR     R3, [R10, #Module_Title]
         CMP     R3, #0
         ADREQL  R3, NoRIT
         ADDNE   R3, R3, R10

 ; now set up R4
         BL      GetUnplugCMOS
         MOV     R12, #1
         SUBS    R1, R1, #1
         BMI     %FT14
         CMP     r1, #32
         MOVGE   r6, r7
         SUBGE   r1, r1, #32
         MOV     r12, r12, LSL r1
         ADDGE   r1, r1, #32
         TST     R6, R12
         MOVNE   R4, #-1
         BNE     %FT12                   ; IF CMOS set THEN status := -1
14       MOV     R4, #Module_List
13       LDR     R4, [R4, #Module_chain_Link]
         CMP     R4, #0
         BEQ     %FT12                   ; module not active
         LDR     R12, [R4, #Module_code_pointer]
         CMP     R12, R10
         BNE     %BT13
12       ADD     R1, R1, #2
ERM_finish
         CMP     R4, #0
         BLE     %FT15
         MOV     R10, #0
         LDR     R10, [R10, #Curr_Active_Object]
         LDR     R4, [R4, #Module_code_pointer]
         LDR     R11, [R4, #-4]            ; node size of code
         ADD     R11, R11, R4
         CMP     R4, R10
         CMPLS   R10, R11
         MOVLT   R4, #2                    ; running
         MOVGE   R4, #1                    ; just active
15
         Pull   "R6, r7, lr"
         ExitSWIHandler
ERMNF
         Pull   "R0-R4, R7"
         ADRL    R0, ErrorBlock_NoMoreModules
         Pull   "R6, r7, lr"
         B      SLVK_SetV

01
         Push   "R0-R4, R7"
         MOV     R3, R2
         MOV     R6, R1
         MOV     R0, #0
02       BL      NextPoduleModule
         BEQ     ERMNF
         SUBS    R6, R6, #1
         BPL     %BT02

         SUB     R5, R0, #1                ; chunk number
         CMP     R1, #0
         MOV     R0, #ReadCMOS
         SWINE   XOS_Byte
         MOVEQ   R2, #0
         LDR     R1, [stack, #4]
         MOV     R0, #1
         TST     R2, R0, LSL R1
         MOVNE   R2, #-1
         BNE     %FT03

         MOV     R11, #&33C0000
         ADD     R11, R11, R3, LSL #14       ; correct h'ware base.
         MOV     R2, #Module_List
04       LDR     R2, [R2, #Module_chain_Link]
         CMP     R2, #0
         BEQ     %FT03                   ; module not active
         LDR     R12, [R2, #Module_Hardware]
         CMP     R12, R11
         BNE     %BT04
         MOV     R10, R4
         LDR     R12, [R2, #Module_code_pointer]
         LDR     R7, [R12, #Module_Title]
         ADD     R12, R12, R7
05       LDRB    R7, [R10], #1
         LDRB    R0,  [R12], #1
         CMP     R0, R7
         BNE     %BT04
         CMP     R0, #" "
         BGT     %BT05
03       STR     R2, [stack, #4*4]  ; return R4
         STR     R4, [stack, #4*3]  ; return R3
         LDR     R2, [stack, #4*2]  ; pick up incoming R2
         STR     R3, [stack, #4*2]  ; return R2
         CMP     R2, R3
         Pull   "R0-R4, R7"
         ADDEQ   R1, R1, #1         ; simple step if in same podule
         MOVNE   R1, #1             ; reset module no if podule stepped
         B       ERM_finish

;*************************************************************
; Support routines.
;
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Load_Module
;     takes filename pointer in R1, and loads and initialises the given file.
;     Returns R9 as a pointer to the node claimed
;     and  V/current error set if fails

Load_Module ROUT

        Push   "R0-R5, lr"

        MOV     r0, #OSFile_ReadInfo
        SWI     XOS_File
        BVS     modfailxit              ; return FileSwitch error
        CMP     r0, #object_file
        BNE     HeNotFile

        BIC     R2, R2, #&FF            ; low byte ignored by me.
        CMP     R2, #Module_LoadAddr
        BNE     NotAModule

; it's a module, so try and claim.
        MOV     R10, R1                 ; keep string pointer
        MOV     R3, R4                  ; size of vector needed
        BL      RMAClaim_Chunk
        BVS     modfailxit

02      MOV     R9, R2                  ; keep a copy of node ptr.
        MOV     R1, R10
        MOV     R0, #OSFile_Load
        MOV     R3, #0                  ; load to R2 posn
        SWI     XOS_File
        BVS     modfailxit              ; return FileSwitch error

50      MOV     R11, #0                 ; not loaded from hardware.

; R9 address, R9!-4 size
        MOV     R1, R9
        LDR     R2, [R9, #-4]
        BL      CheckHeader
        BVS     Duplicate_Immortal      ; actually means naff header field

; now we've got it, see if any other modules have the same name.

        LDR     R1, [R9, #Module_Title]
        ADD     R1, R1, R9
        BL      LookUp_Module
        BEQ     %FT01                   ; no module at all
        CMP     R12, #0
        BNE     nopostfixwanted         ; postfix given: bad name
        BL      KillAndFree
        BVS     Duplicate_Immortal

; now claim a link
; R9 module pointer, R10 environment

01      BL      EnvStringSkipName
        BL      LinkAndInit             ; takes R2 prevnode from lookup

        STRVS   R0, [stack]
        Pull   "R0-R5, pc"

Duplicate_Immortal                      ; free space claimed for loading
        STR     R0, [stack]
        MOV     R2, R9
        MOV     R0, #HeapReason_Free
        MOV     R1, #RMAAddress
        SWI     XOS_Heap
        Pull   "R0-R5, lr"
        ORRS    PC, lr, #V_bit

        MakeErrorBlock MHNoRoom

nopostfixwanted
        ADR     R0, ErrorBlock_ModulePostfix
        B       modfailxit

        MakeErrorBlock ModulePostfix

        MakeErrorBlock NotMod

NotAModule
        ADR     R0, ErrorBlock_NotMod

modfailxit
        STR     R0, [stack]
        Pull   "R0-R5, lr"
        ORRS    PC, lr, #V_bit

HeNotFile
        MOV     r2, r0
        MOV     r0, #OSFile_MakeError
        SWI     XOS_File
        B       modfailxit

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; ModuleIn_CheckForDuplicate
; Altentry to Load_Module for AddArea: module already in, initialise it.

ModuleIn_CheckForDuplicate
         Push   "R0-R5, lr"
         MOV     R9, R1           ; move module ptr to handy place
         B       %BT50

;*************************************************************
; AddModuleIfInROM
;     R1 -> name
;     R0 = Postfix_Separator => name must have postfix:
;                               add module from ROM (don't Insert), and
;                               rename Base to <given postfix>
;        = 0                 => no postfix:
;                               add module from ROM and Insert
;                               (BIC CMOS RAM)

AddModuleIfInROM ROUT
         Push   "R1-R6, lr"

         BL      FindHiddenModule
         BLVS    MakeNotFoundError
         Pull   "R1-R6, PC", VS

         BIC     R2, R2, R3                 ; in case CMOS needs clearing
         CMP     R0, #Postfix_Separator
         MOVNE   R0, #WriteCMOS
         SWINE   XOS_Byte
         BNE     %FT01
         LDRB    R0, [R5], #1
         CMP     R0, #Postfix_Separator
         ADRNEL  R0, ErrorBlock_PostfixNeeded
         BNE     %FT02
01       MOV     R6, PC
         MOVS    R1, R4                     ; base ptr
         MOVPL   R0, #ModHandReason_AddArea
         MOVMI   R0, #ModHandReason_AddPoduleModule
         ADRMIL  R1, crstring
         MOV     R2, R4, LSL #16
         MOV     R2, R2, LSR #16            ; chunkno
         BIC     R3, R4, #&80000000
         MOV     R3, R3, LSR #16            ; podule no
         SWI     XOS_Module
         BVS     %FT02
         TEQP    PC, R6
         Pull   "R1-R6, PC", NE
         MOV     R2, R5                     ; required postfix
         LDR     R0, [stack]                ; title ptr
         LDR     R1, =GeneralMOSBuffer
10       LDRB    R3, [R0], #1
         STRB    R3, [R1], #1
         CMP     R3, #Postfix_Separator
         BNE     %BT10
         ADR     R0, base_postfix
11       LDRB    R3, [R0], #1
         STRB    R3, [R1], #1
         CMP     R3, #0
         BNE     %BT11
         MOV     R0, #ModHandReason_RenameIncarnation
         LDR     R1, =GeneralMOSBuffer
         SWI     XOS_Module
         Pull   "R1-R6, PC"
02
         Pull   "R1-R6, lr"
         ORRS    PC, lr, #V_bit

;*************************************************************
; LinkAndInit :
;     module pointer in R9
;     module list position in R2 : added at end if posn not found
;     environment string pointer in R10
;    "hardware" in R11
;     returns module node pointer in R9

LinkAndInit      ROUT
         Push   "R2, R3, lr"

         MOV     R3, #ModInfo
         BL      ClaimSysHeapNode
         Pull   "R2, R3, PC", VS

         STR     R9, [R2, #Module_code_pointer]
         STR     R11, [R2, #Module_Hardware]
         MOV     R9, R2                      ; keep node pointer

         MOV     R0, #0
         STR     R0, [R2, #Module_incarnation_list] ; terminate list
         ADR     R0, base_postfix
         BL      Add_Incarnation             ; add Base incarnation
         BVS     %FT01

         Pull   "R2"
         ADR     R0, Module_List
05       LDR     R1, [R0]
         CMP     R1, #0
         CMPNE   R0, R2
         MOVNE   R0, R1
         BNE     %BT05

; add module to chain end - give ROM modules priority.

         STR     R1, [R9, #Module_chain_Link]
         STR     R9, [R0, #Module_chain_Link]
         Pull   "R3, PC"                   ; V clear from EQ compare with 0
01
         Push   "R0"
         LDR     R2, [R9, #Module_code_pointer]
         MOV     R1, #RMAAddress
         MOV     R0, #HeapReason_Free
         SWI     XOS_Heap
         MOV     R0, #HeapReason_Free
         LDR     R1, =SysHeapStart
         MOV     R2, R9                    ; node pointer
         SWI     XOS_Heap
         Pull   "R0, R2, R3, lr"
         ORRS    PC, lr, #V_bit

base_postfix
         =      "Base",0                   ; postfix used for 1st incarnation
         ALIGN

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Add_Incarnation
;       takes postfix pointer in R0 (terminated by <= space
;       module node pointer in R9
;       envstring in R10
;   Adds an incarnation node, reinitialises the module

Add_Incarnation  ROUT
         Push   "R0-R3, lr"

         MOV     R3, #Incarnation_Postfix      ; node size needed
01       LDRB    R1, [R0], #1
         ADD     R3, R3, #1
         CMP     R1, #" "
         BGT     %BT01
         BL      ClaimSysHeapNode
         STRVS   R0, [stack]
         Pull   "R0-R3, PC", VS

         LDR     R0, [stack]
         ADD     R3, R2, #Incarnation_Postfix
02       LDRB    R1, [R0], #1
         CMP     R1, #" "
         STRGTB  R1, [R3], #1
         BGT     %BT02
         MOV     R1, #0
         STRB    R1, [R3]

         MOV     R3, #0
         STR     R3, [R2, #Incarnation_Workspace] ; zero private word
         MOV     R12, R2
         BL      CallInit
;         BLVS    FreeIncarnation done by CallInit
         STRVS   R0, [stack]

         LDRVC   R3, [R9, #Module_incarnation_list]
         STRVC   R3, [R2, #Incarnation_Link]
         STRVC   R2, [R9, #Module_incarnation_list]
         Pull   "R0-R3, PC"

;*************************************************************
CallInit         ROUT
;    take R9  -> module node
;         R12 -> incarnation node
;         R10 -> envstring
;    set R11 appropriately
;    initialise module with R10 given

         Push   "R0-R6, R11, R12, lr"

  ; see if we need to set up a module swi node

         BL      CheckForSWIEntries
         LDR     R12, [stack, #4*(6+2)]
         BNE     %FT03

  ; the module really does have a SWI chunk. Add node to hashtable.

         MOV     R4, R0
         MOV     R11, R1
         MOV     R3, #ModSWINode_Size
         BL      ClaimSysHeapNode
         BVS     %FT02
         STR     R9,  [R2, #ModSWINode_MListNode]
         STR     R4,  [R2, #ModSWINode_CallAddress]
         STR     R11, [R2, #ModSWINode_Number]
         ModSWIHashval R11
         LDR     R6, [R11]       ;  link into table.
         STR     R6, [R2, #ModSWINode_Link]
         STR     R2, [R11]

03  ; now prepared to look at module

         LDR     R3, [R9, #Module_code_pointer]

         LDR     R4, [R3, #Module_Init]
         CMP     R4, #0
         Pull   "R0-R6, R11, R12, PC", EQ      ; V clear

         ADD     R12, R12, #Incarnation_Workspace
         MOV     R11, #0
         ADD     R5, R9, #Module_incarnation_list - Incarnation_Link
01       LDR     R5, [R5, #Incarnation_Link]
         CMP     R5, #0                        ; count incarnations
         ADDNE   R11, R11, #1
         BNE     %BT01
         CMP     R11, #0
         LDREQ   R11, [R9, #Module_Hardware]

  ; R11, R12 now set: initialise

         MOV     lr, PC                        ; pseudo BL
         ADD     PC, R3, R4                    ; call 'im

         Pull   "R0-R6, R11, R12, lr", VC
         BICVCS  PC, lr, #V_bit

02       LDR     R12, [stack, #4*(6+2)]
         BL      FreeIncarnation
         BL      FreeSWIEntry
         STR     R0, [stack]
         Pull   "R0-R6, R11, R12, PC"           ; V set return

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Enter with module pointer in R1
;              "    size    in R2

CheckHeader ROUT
         Push   "R3, lr"
         LDR     R3, [R1, #Module_HC_Table]
         BL      %FT11
         LDR     R3, [R1, #Module_HelpStr]
         BL      %FT11
         LDR     R3, [R1, #Module_Title]
         BL      %FT11
         LDR     R3, [R1, #Module_Service]
         BL      %FT10
         LDR     R3, [R1, #Module_Die]
         BL      %FT10
         LDR     R3, [R1, #Module_Init]
         BL      %FT10
         Pull   "R3, lr"
         BICS    PC, lr, #V_bit

10       TST     R3, #3
         BNE     %FT99
11       CMP     R3, R2
         MOVLO   PC, lr
99
         Pull   "R3, lr"
         ADR     R0, ErrorBlock_BadRMHeaderField
         ORRS    PC, lr, #V_bit
         MakeErrorBlock BadRMHeaderField  

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Enter with module node pointer in R9
; Sets R12 to module code pointer, R0 SWI code offset, R1 to SWI number
; Only returns SWIs extant if no incarnations of module yet

CheckForSWIEntries ROUT
         LDR     R12, [R9, #Module_incarnation_list]
         CMP     R12, #0
         LDREQ   R12, [R9, #Module_code_pointer]
         LDREQ   R1, [R12, #Module_SWIChunk]
         BICEQ   R1, R1, #Auto_Error_SWI_bit
         TSTEQ   R1, #Module_SWIChunkSize-1
         TSTEQ   R1, #&FF000000
         MOVNE   PC, lr                        ; naff chunk number.
         CMP     R1, #0
         LDRNE   R0, [R12, #Module_SWIEntry]
         CMPNE   R0, #0
         BEQ     %FT01
         TST     R0, #3
         MOVNE   PC, lr
         Push   "R5"
         LDR     R5, [R12, #-4]
         CMP     R5, R0
         Pull   "R5"
01       BICLSS  PC, lr, #Z_bit           ; NE return
         ADD     R0, R0, R12
         ORRS    PC, lr, #Z_bit           ; EQ for success

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Takes R9 pointer to module node; frees any module SWI hashtab node

FreeSWIEntry ROUT
         Push   "R0-R4, R12, lr"
         BL      CheckForSWIEntries
         Pull   "R0-R4, R12, PC", NE, ^
         MOV     R3, R1                ; copy of SWIno
         ModSWIHashval R1
         LDR     R2, [R1], #-ModSWINode_Link

  ; R1 predecessor, R2 currnode, R0 call address, R3 SWIno
  ; look down chain until find right call address and number
01       CMP     R2, #0
         Pull   "R0-R4, R12, PC", EQ, ^
         LDR     R4, [R2, #ModSWINode_CallAddress]
         CMP     R4, R0
         LDREQ   R4, [R2, #ModSWINode_Number]
         CMPEQ   R4, R3
         MOVNE   R1, R2
         LDRNE   R2, [R2, #ModSWINode_Link]
         BNE     %BT01
         LDR     R4, [R2, #ModSWINode_Link]
         STR     R4, [R1,#ModSWINode_Link]
         MOV     R0, #HeapReason_Free
         LDR     R1, =SysHeapStart
         SWI     XOS_Heap
         Pull   "R0-R4, R12, PC",,^

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

FreeIncarnation  ROUT
  ; copy error, free any workspace, incarnation link.
  ;   R12 incarnation pointer
         Push   "R0-R2, lr"
         BL      Module_CopyError
         STR     R0, [stack]
         LDR     R2, [R12, #Incarnation_Workspace]
         CMP     R2, #0
         MOV     R0, #HeapReason_Free
         MOV     R1, #RMAAddress
         SWINE   XOS_Heap
         MOV     R2, R12
         LDR     R1, =SysHeapStart
         MOV     R0, #HeapReason_Free
         SWI     XOS_Heap
         Pull   "R0-R2, PC",,^

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; KillAndFree:
;      R0 -> module
;      R2 -> prevmodule
;  Kills all incarnations, frees all space.

KillAndFree      ROUT
         Push   "R2, R3, R9, R12, lr"
         MOV     R9, R0
         ADDS    R3, R9, #Module_incarnation_list  ; ensure V clear
01       LDR     R12, [R9, #Module_incarnation_list]
         BL      KillIncarnation
         Pull   "R2, R3, R9, R12, PC", VS
         CMP     R9, #0
         BNE     %BT01                      ; more incarnations yet
         Pull   "R2, R3, R9, R12, PC"

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; KillIncarnation
;     R9  module ptr
;     R12 incarnation ptr
;     R3  previous incarnation
;     R2  previous module
; Exit: R9 zeroed if module completely gone
;
KillIncarnation  ROUT
         Push   "R0-R2, R10, lr"
         MOV     R10, #1                     ; fatal die
         CMP     r12, #0                     ; fudge for 0 incarnations:
         BLNE    CallDie                     ; tidy up anyway
         STRVS   R0, [stack]
         Pull   "R0-R2, R10, PC", VS

         MOV     R2, R12
         LDR     R1, =SysHeapStart
         MOV     R0, #HeapReason_Free
         SWI     XOS_Heap                    ; free incarnation node
01       LDR     R0, [R9, #Module_incarnation_list]
         CMP     R0, #0                      ; last incarnation?
         LDREQ   R2, [stack, #2*4]
         BLEQ    DelinkAndFreeModule
         MOVEQ   R9, #0
         Pull   "R0-R2, R10, PC",,^

LoseModuleSpace_if_its_the_only_incarnation
         Push   "R0-R2, R10, lr"
         B       %BT01

;*************************************************************
; CallDie
;    take R9  -> module node
;         R12 -> incarnation node
;         R3  -> previous incarnation
;         R10 =  fatality
; check against CAO
; delink incarnation, issue die, deal with workspace.

CallDie ROUT
         Push   "R0-R6, R11, R12, lr"

         MOV     R11, #0
         LDR     R11, [R11, #Curr_Active_Object]

 ; check killability

         LDR     R0,  [R9, #Module_code_pointer]
         BL      %FT10                        ; is_CAO?
         BLT     %FT04                        ; code block is CAO

 ; check if workspace block could be CAO: may have handler in there

         LDR     R0, [R12, #Incarnation_Workspace]
         BL      %FT20                        ; check block before getting size
         BVS     ModuleIsntCAO                ; not heap block - we don't
         BL      %FT10                        ; know what's going on.
         BGE     ModuleIsntCAO                ; not CAO

04       CMP     R10, #0                      ; fatal?
         BNE     CantKill
         LDR     R0, [R12, #Incarnation_Workspace]
         BL      %FT20
         BVS     ModuleIsntCAO                ; soft die of non-heap module OK
CantKill
         ADR     R0, ErrorBlock_CantKill
01       STR     R0, [stack]
         LDR     R3, [stack, #4*3]
         LDR     R12, [stack, #4*(6+2)]
         STR     R12, [R3, #Incarnation_Link] ; relink
         Pull   "R0-R6, R11, R12, lr"
         ORRS    PC, lr, #V_bit
         MakeErrorBlock CantKill

ModuleIsntCAO
         MOV     R11, #0                     ; set R11 to incarnation number.
         LDR     R0, [R9, #Module_incarnation_list]
03       CMP     R0, R12
         ADDNE   R11, R11, #1
         LDRNE   R0, [R0, #Incarnation_Link]
         BNE     %BT03

         LDR     R0, [R12, #Incarnation_Link]
         STR     R0, [R3, #Incarnation_Link] ; delink
         ADD     R12, R12, #Incarnation_Workspace
         LDR     R1, [R9, #Module_code_pointer]
         LDR     R0, [R1, #Module_Die]
         CMP     R0, #0
         MOV     lr, PC
         ADDNE   PC, R1, R0                  ; call.
         BVS     %BT01
         BL      FreeSWIEntry

         CMP     R10, #0                     ; soft die?
         BEQ     %FT02

         LDR     R12, [stack, #4*(6+2)]
         LDR     R2, [R12, #Incarnation_Workspace]
         CMP     R2, #0
         MOVNE   R1, #RMAAddress
         MOVNE   R0, #HeapReason_Free
         SWINE   XOS_Heap
         MOV     R0, #0
         STR     R0, [R12, #Incarnation_Workspace]   ; orgone
02  
         Pull   "R0-R6, R11, R12, lr"
         BICS    PC, lr, #V_bit

; check if block @ R0 contains address R11
10       LDR     R1, [R0, #-4]
         ADD     R1, R1, R0
         CMP     R0, R11
         CMPLS   R11, R1
         MOV     PC, lr                      ; return LT for Yes

; check block @ R0 is a valid RMA heap block
20
         Push   "R0-R3, lr"
         MOV     R2, R0
         MOV     R0, #HeapReason_ExtendBlock
         MOV     R1, #RMAAddress
         MOV     R3, #0
         SWI     XOS_Heap
         Pull   "R0-R3, PC"                 ; V set if not.

;*************************************************************
; DelinkAndFreeModule
;       R9 -> Module
;       R2 -> prevmodule

DelinkAndFreeModule ROUT
         Push   "R0-R2, lr"

;   loop here to find predecessor; make death re-entrant
         MOV     R0, #Module_List
01       LDR     R1, [R0, #Module_chain_Link]
         CMP     R1, R9
         MOVNE   R0, R1
         BNE     %BT01

         LDR     R1, [R9, #Module_chain_Link]
         STR     R1, [R0, #Module_chain_Link] ; delinked

         LDR     R2, [R9, #Module_code_pointer]
         MOV     R1, #RMAAddress
         MOV     R0, #HeapReason_Free
         SWI     XOS_Heap

         MOV     R2, R9
         LDR     R1, =SysHeapStart
         MOV     R0, #HeapReason_Free
         SWI     XOS_Heap

         Pull   "R0-R2, PC",,^ 

;*************************************************************************
;  common lookup for reinit, enter, die

lookup_commoned ROUT
       BIC      lr, lr, #I_bit
       TEQP     PC, #SVC_mode
       Push    "lr"
       BL       LookUp_Module               ; node ptr in R0
       BEQ      %FT01                       ; not found
       CMP      R12, #0
       BLT      %FT02                       ; incarnation not found
       MOV      R9, R0
       Pull    "PC"
01
       ADR      R0, ErrorBlock_RMNotFound
       Push    "r1-r6"
       MOV      r3, #0
       LDR      r3, [r3, #IRQsema]
       CMP      r3, #0
       BNE      %FT03
       BL       GetOscliBuffer
       Push     r5
       LDR      r2, [r0], #4
       STR      r2, [r5], #4
       BL       rmecopystr
copynfrmname
       LDRB     r2, [r1], #1
       CMP      r2, #32
       STRGTB   r2, [r5], #1
       BGT      copynfrmname

       BL       rmecopystr
       STRB     r2, [r5]                   ; terminate
       Pull    "r0-r6"

03
       Pull    "lr"
       ORRS     PC, lr, #V_bit
       MakeErrorBlock  RMNotFound
02
       ADR      R0, ErrorBlock_IncarnationNotFound
       B        %BT03
       MakeErrorBlock  IncarnationNotFound

MakeNotFoundError                         ; r1 -> module name
       Push     lr
       B        %BT01

;*************************************************************
; Lookup_Module
; Entry:  R1  -> module name
; Exit:   R0  -> module chain node   (0 for not found)
;         R1  -> postfix of name
;         R12 -> incarnation node    (0 for not specified, -1 for not found)
;         R2  -> previous module      for potential delinking
;         R3  -> previous incarnation  "      "         "
;         NE for found/EQ for not

LookUp_Module ROUT
         Push   "R4, R5, lr"
         TEQP    PC, #SVC_mode               ; interrupts on
         LDR     R2, =Module_List
01       LDR     R0, [R2, #Module_chain_Link]
         CMP     R0, #0
         Pull   "R4, R5, PC", EQ             ; return if not found
         LDR     R4, [R0, #Module_code_pointer]
         LDR     R3, [R4, #Module_Title]
         ADD     R3, R3, R4                  ; got ptr to title
         MOV     R4, #Postfix_Separator      ; allowed terminator for StrCmp
         BL      Module_StrCmp               ; compare with abbreviation.
         MOVNE   R2, R0
         BNE     %BT01                       ; loop if not found
         LDRB    R4, [R1], #1                ; get terminator
         CMP     R4, #Postfix_Separator
         BEQ     %FT02

   ; now a quick fudge to spot recursive ModHand calls during module death.
         LDR     R12, [R0, #Module_incarnation_list]
         CMP     R12, #0
         MOVEQ   R12, #-1                    ; no incarnations!
         MOVNE   R12, #0                     ; no postfix/incarnation specified
         CMP     PC, #0
         Pull   "R4, R5, PC"                 ; back with NE

02       LDRB    R4, [R1]
         CMP     R4, #" "
         Pull   "R4, R5, lr", LE
         ORRLES  PC, lr, #Z_bit              ; not found: naff postfix
         Push   "R1"                         ; updated value to return
         BL      FindIncarnation
         MOVEQ   R12, #-1                    ; failed to find postfix.
         CMP     PC, #0                      ; force NE
         Pull   "R1, R4, R5, PC"             ; back with NE

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; FindIncarnation
;    R0 is node pointer, need to loop to find incarnation ($R1)
;    Return EQ if not found,
;           NE => R12 -> incarnation, R3 -> previnc

FindIncarnation  ROUT
         Push   "lr"
         ADD     R3, R0, #Module_incarnation_list  ; previnc
03       LDR     R12, [R3, #Incarnation_Link]
         CMP     R12, #0
         Pull   "PC", EQ                     ; failed to find postfix.
         Push   "R3"
         ADD     R3, R12, #Incarnation_Postfix
         BL      Module_StrCmp
         Pull   "R3"
         MOVNE   R3, R12
         BNE     %BT03
         CMP     PC, #0                      ; force NE
         Pull   "PC"

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Module_StrCmp    ROUT
; Do a string comparison, given pointers in R1, R3.
; Ignore case, allow $R1 to be an abbreviation of $R3
; Strings are terminated by ctrl-char, or ASC(R4) for $R1
; Return EQ/NE

         Push   "R1, R2, R5-R7, lr"
         MOV     R2, #0
01       LDRB    R7, [R1], #1
         LDRB    R5, [R3], #1
         CMP     R7, R4
         CMPNE   R7, #32
         CMPLE   R5, #32
         BLE     %FT02
         UpperCase R7, R6
         UpperCase R5, R6
         CMP     R7, R5
         ADDEQ   R2, R2, #1
         BEQ     %BT01
         CMP     R2, #0
         MOV     R2, #Z_bit
         TEQP    R2, pc                      ; invert EQ/NE
         CMPEQ   R7, #"."                    ; success if abbreviation
         Pull   "R1, R2, R5-R7, PC", NE
         CMP     r5, #" "                    ; reject abbreviation
         Pull   "R1, R2, R5-R7, PC", LT      ; after full match
         ADD     R1, R1, #1

02       SUB     R1, R1, #1
         CMP     R2, #0                      ; reject 0-length match
         MOV     R2, #Z_bit
         TEQP    R2, pc                      ; invert EQ/NE
         STREQ   R1, [stack]                 ; R1 -> terminator
         Pull   "R1, R2, R5-R7, PC"          ; return with success

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; FindHiddenModule
;
; takes a name pointer in R1, potential terminator in R0
;  returns VS for not found
;   VC :
;      R1 offset in CMOS of byte
;      R2 old value of byte
;      R3 single bit mask for byte
;      R4: TBC: pointer to module base
;          TBS: lobyte chunkno, byte 1 poduleno
;      R5 -> name terminator
;      R6 -> actual name
;
;  Frigs 1st ROM module : utilitymod can't be zapped
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

FindHiddenModule ROUT
         Push   "R0, R10, lr"

         MOV     R4, R0                 ; terminator
         ADRL    R10, SysModules_Info+4
         MOV     R2, #0
         B       %FT02

01       LDR     R5, [R10, #-4]
         TEQ     R5, #0
         BEQ     CheckPodules            ; end of chain
         LDR     R3, [R10, #Module_Title]
         CMP     R3, #0
         BEQ     %FT04
         ADD     R3, R3, R10
         BL      Module_StrCmp
         BEQ     GotHidden
04       ADD     R2, R2, #1
02       LDR     R5, [R10, #-4]
         ADD     R10, R10, R5
         B       %BT01

CheckPodules
         Push   "R0, R1"
         MOV     R0, #0                   ; chunk number
         MOV     R3, #0                   ; podule number
         MOV     R2, #-1                  ; module index
10       BL      NextPoduleModule
         Pull   "R0, R1", EQ
         BEQ     NothingHidden
         Push   "R1, R3, R4"
         MOV     R3, R4
         LDR     R4, [stack, #4*3]        ; passed terminator
         LDR     R1, [stack, #4*4]        ; passed name
         BL      Module_StrCmp
         MOVEQ   R5, R1                   ; terminator ptr
         Pull   "R1, R3, R4"
         BNE     %BT10

    ; got hidden podule job.
         MOV     R6, R4                   ; full name of module
         ADD     stack, stack, #2*4       ; discard pushed junk

         SUB     R10, R0, #1              ; chunkno
         ORR     R10, R10, R3, LSL #16    ; OR poduleno
         ORR     R10, R10, #&80000000     ; with TBS

         CMP     R3, #3
         MOVGT   R1, #0                   ; no valid CMOS
         ADDLE   R1, R3, #PoduleFrugalCMOS

         CMP     R2, #8
         MOVGE   R3, #0
         MOVLT   R3, #1
         MOV     R3, R3, LSL R2           ; got bitmask
         B       %FT11

GotHidden
         MOV     R5, R1                   ; terminator ptr
         MOV     R1, #MosROMFrugalCMOS
         ADD     r1, r1, r2, LSR #3       ; byte offset
         CMP     r1, #MosROMFrugalCMOS+6
         MOVGE   r1, #0
         CMP     r1, #MosROMFrugalCMOS+4
         ADDGE   r1, r1, #FrugalCMOS-MosROMFrugalCMOS-4
         AND     r2, r2, #7
         MOV     R3, #1
         MOV     r3, r3, LSL r2           ; bit mask

         LDR     R6, [R10, #Module_Title]
         ADD     R6, R6, R10              ; point at full name

11       MOV     R0, #ReadCMOS
         CMP     R1, #0
         SWINE   XOS_Byte
         MOV     R4, R10
         Pull   "R0, R10, lr"
         BICS    PC, lr, #V_bit

NothingHidden
         ADR     R0, ErrorBlock_RMNotFoundInROM
         STR     R0, [stack]
         Pull   "R0, R10, lr"
         ORRS    PC, lr, #V_bit
         MakeErrorBlock RMNotFoundInROM

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; NextPoduleModule: enumerate podule chunks until end or next RM
;   In: R0 = chunkno
;       R2 = module no
;       R3 = podule no
;  Out: EQ for end
;    if NE
;       R0, R2, R3 updated
;       R1 = offset in CMOS of frugal byte (0 if podule number too big)
;       R4 -> name

NextPoduleModule ROUT
         Push   "R2, lr"

01       SWI     XPodule_EnumerateChunks
         BVS     %FT04                  ; bad podule or some such
         CMP     R2, #OSType_Module
         BEQ     %FT02
         CMP     R0, #0
         ADDEQ   R3, R3, #1             ; no more chunks: step podule
         B       %BT01

02       CMP     R3, #3
         MOVGT   R1, #0
         ADDLE   R1, R3, #PoduleFrugalCMOS
         Pull   "R2, lr"
         ADD     R2, R2, #1             ; increment module number
         BICS    PC, lr, #Z_bit         ; force NE return

04       ADD     R3, R3, #1
         MOV     R0, #0
         CMP     R3, #16                ; more podules than you could ever fit
         BNE     %BT01

         Pull   "R2, PC"                ; EQ return: no more modules

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

EnvStringSkipName ROUT
         Push   "R0"
01       LDRB    R0, [R10], #1
         CMP     R0, #" "
         BGT     %BT01
02       LDREQB  R0, [R10], #1
         CMP     R0, #" "
         BEQ     %BT02
         SUB     R10, R10, #1
         Pull   "R0"
         MOV     PC, lr

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; PreferIncarnation
;    R9  -> module node
;    R12 -> incarnation node
;    R3  -> previous incarnation
PreferIncarnation
         Push   "R0"
         LDR     R0,  [R12, #Incarnation_Link]
         STR     R0,  [R3,  #Incarnation_Link]
         LDR     R0,  [R9,  #Module_incarnation_list]
         STR     R0,  [R12, #Incarnation_Link]
         STR     R12, [R9,  #Module_incarnation_list]
         Pull   "R0"
         MOV     PC, R14

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Module_CopyError ROUT
; grab an oscli buffer for the error,
; rather than having a permanent module buffer
       Push     "R0, R2, R5, R6, lr"
       BL        GetOscliBuffer

       STR       R5, [stack]
       LDR       R2, [R0], #4
       STR       R2, [R5], #4
01     LDRB      R2, [R0], #1
       STRB      R2, [R5], #1
       CMP       R2, #0
       BNE       %BT01
       Pull     "R0, R2, R5, R6, PC"

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;  Claim chunk from RMA : increase RMA if can,
;  force size to multiple of 16 -4 to keep alignment OK

RMAClaim_Chunk   ROUT
         MOV     R0, #HeapReason_Get
         Push   "R0, R3, lr"

         ADD     R3, R3, #15+4               ; now force size to 16*n-4
         BIC     R3, R3, #15                 ; so heap manager always has
         SUB     R3, R3, #4                  ;  4-word aligned blocks

         B       IntoRMAHeapOp

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

DoRMAHeapOpWithExtension
         Push   "R0, R3, lr"

IntoRMAHeapOp
         MOV     R1, #RMAAddress
         SWI     XOS_Heap
         Pull   "R0, R3, PC", VC

         LDR     r14, [r0]                   ; look at error number
         TEQ     r14, #ErrorNumber_HeapFail_Alloc
         STRNE   r0, [stack]
         Pull   "r0, r3, PC", NE            ; can only retry if ran out of room

         Push    r3                         ; in case extension
         LDR     r1, [stack, #4]
         CMP     r1, #HeapReason_ExtendBlock
         BNE     notRMAextendblock
         Push   "r5, r6"
         LDR     r1, [r2, #-4]               ; pick up block size
         ADD     r5, r1, r2                  ; block end
         MOV     r6, #RMAAddress
         LDR     r6, [r6, #:INDEX:hpdbase]
         ADD     r6, r6, #RMAAddress         ; free space
         CMP     r5, r6                      ; does block butt against end?
         ADDNE   r3, r3, r1                  ; max poss size needed
         Pull   "r5, r6"

  ; note that this doesn't cope well with a block at the end preceded by a
  ; free block, but tough.

notRMAextendblock
         MOV     r1, #RMAAddress
         LDR     R0, [R1, #:INDEX: hpdbase]
         LDR     R1, [R1, #:INDEX: hpdend]
         SUB     R1, R1, R0                  ; bytes free
         SUB     R1, R3, R1                  ; bytes needed
         Pull    r3
         ADD     R1, R1, #8                  ; safety factor

         MOV     R0, #1                      ; try and expand RMA.
         SWI     XOS_ChangeDynamicArea
         Pull   "R0"                         ; heap reason code back
         MOV     R1, #RMAAddress
         SWIVC   XOS_Heap
01
         ADRVSL  R0, ErrorBlock_MHNoRoom
         Pull   "r3, PC"

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

GetUnplugCMOS    ROUT           ; module cmos unplug bytes -> R6, r7
         Push   "R0-R2, lr"
         MOV     R6, #0
         MOV     R0, #ReadCMOS
         MOV     R1, #MosROMFrugalCMOS
01       SWI     XOS_Byte
         ORR     R6, R2, R6
         MOV     R6, R6, ROR #8
         ADD     R1, R1, #1
         CMP     R1, #MosROMFrugalCMOS+4
         BNE     %BT01
         MOV     r1, #FrugalCMOS
         SWI     XOS_Byte
         MOV     r7, r2
         MOV     r1, #FrugalCMOS+1
         SWI     XOS_Byte
         ORR     r7, r7, r2, LSL #8
         Pull   "R0-R2, PC"

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Data

crstring =       13
         ALIGN

;*****************************************************************************
; *Unplug code.

Unplug_Code      ROUT
         Push   "r7, r8, lr"

         CMP     R1, #0
         BNE     ZapTheBastards

; If name not given, list stiff bastids

         MOV     r8, #0
         BL      GetUnplugCMOS
         TEQP    PC, #SVC_mode            ; CLC (1st module never unplugged)
         ADRL    R1, SysModules_Info+4
01       LDR     R2, [R1, #-4]
         TEQ     R2, #0
         BEQ     %FT20                    ; end of chain
         BCC     %FT02                    ; not unplugged
         BL      %FT30
         LDR     R3, [R1, #Module_Title]
         CMP     R3, #0
         ADDNE   R0, R1, R3
         ADREQL  R0, NoRIT
         SWI     XOS_Write0
         SWIVC   XOS_NewLine
         Pull   "r7, r8, PC", VS
02       ADD     R1, R1, R2
         MOVS    r7, r7, LSR #1
         MOVS    R6, R6, RRX           ; next bit -> C
         B       %BT01

20       MOV     R0, #0
         MOV     R2, #-1
         MOV     R3, #0
21       BL      NextPoduleModule
         BEQ     %FT10
         CMP     R1, #0
         BEQ     %FT10
         Push   "R0-R2"
         MOV     R0, #ReadCMOS
         SWI     XOS_Byte
         LDR     R6, [stack, #4*2]
         CMP     R6, #8
         MOVLT   R5, #1
         MOVGE   R5, #0
         TST     R2, R5, LSL R6
         Pull   "R0-R2"
         BLNE    %FT30
         Push   "R0"
         MOV     R0, R4
         SWINE   XOS_Write0
         Pull   "R1, r7, r8, PC", VS
         Pull   "R0"
         SWINE   XOS_NewLine
         Pull   "r7, r8, PC", VS
         B       %BT21

10       CMP     r8, #0
         Pull   "r7, r8, PC", NE
         SWI     XOS_WriteS
         =      "No modules are unplugged",10,13,0
         ALIGN
         Pull   "r7, r8, PC"

30       CMP     r8, #0
         MOVNES  PC, lr
         Push   "lr"
         MOV     r8, #-1
         SWI     XOS_WriteS
         =      "Unplugged modules are:",10,13,0
         ALIGN
         Pull   "lr", VC
         MOVVCS  PC, lr
         Pull   "R1, r7, r8, PC"

ZapTheBastards
         MOV     R1, R0                     ; name pointer
         BL      FindHiddenModule           ; first stiff him
         BVS     finished_Unplug
         ORR     R2, R2, R3                 ; set bit in byte
         MOV     R0, #WriteCMOS
         SWI     XOS_Byte
         MOV     R0, #ModHandReason_Delete  ; now tell him he's dead
         MOV     R1, R6
         SWI     XOS_Module
finished_Unplug
         Pull   "r7, r8, PC"

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

         LNK     ArthurSWIs
