| 
    
      Example 4
     | 
    
       
     | 
For this part of our assembler programming series, we shall turn our attention to Relocatable
Modules.
Modules are special programs which link themselves into the operating system to provide extra
functionality, such as SWIs or *commands, or simply background tasks. They are loaded into the
RMA (Relocatable Module Area) and are available all the time.
Examples of modules are new filing systems, or the nested WindowManager (which replaces the
WindowManager in the OS ROMs).
We are not going to aim quite so high as rewriting the WIMP. Instead you will discuss a small
module which resets the computer upon a *command, or all mouse keys pressed.
This module might seen a bit pointless. I was originally written for my network server, which is slung under the desk and has no monitor. Resetting via Econet DoImmediate was precarious. So plug in a mouse, press all three buttons, and Bob is your father's brother...
This example shows several things:
REM >SourceCode
REM
REM Resetter module
REM Version 1.02
REM
REM by Richard Murray
REM
REM Downloaded from: http://www.heyrick.co.uk/assembler/
:
ON ERROR PRINT REPORT$+" at "+STR$(ERL/10) : END
:
DIM code% 1024
FOR x%=0 TO 1023 : code%?x%=0 : NEXT
:
FOR pass% = 4 TO 7 STEP 3
P%=0
O%=code%
[ OPT pass%
    EQUD    0               ; Start-up code
    EQUD    initialise      ; Initialisation
    EQUD    finalise        ; Finalisation
    EQUD    0               ; Service call handler
    EQUD    module_title    ; Module title
    EQUD    module_help     ; Module help
    EQUD    help_table      ; Help and command decoding table
    EQUD    0               ; SWI chunk base number
    EQUD    0               ; SWI handling code
    EQUD    0               ; SWI decoding code
    EQUD    0               ; SWI decoding code
Two things should be immediately visible. Firstly, offset assembly is being used. This is because
relocatable modules can be loaded anywhere. In fact, relocatable modules should be truly
relocatable, and support being moved whilst active. Unfortunately many modules (including several
of the OS ones!) misbehave if moved after initialisation.
    .module_title
      EQUS    "Resetter"
      EQUB    0
      ALIGN
    .module_help
      EQUS    "Resetter"+CHR$(9)+"1.02 (03 Jan 1997)"
      EQUB    0
      ALIGN
Title and details for module...
    .help_table
      EQUS    "ResetNow"           ; Keyword string
      EQUB    0
      ALIGN
      EQUD    reset_code           ; Pointer to code
      EQUD    0                    ; Parameter information
                                   ; (no parameters)
      EQUD    reset_syntax         ; Pointer to syntax string
      EQUD    reset_help           ; Pointer to help string
      EQUD    0                    ; End of command table
    .reset_help
      EQUS    "*ResetNow will reset your computer."
      EQUB    13
      EQUB    13
      EQUS    "   ********************************************"
      EQUB    13
      EQUS    "   * WARNING : All unsaved work WILL be lost! *"
      EQUB    13
      EQUS    "   ********************************************"
      EQUB    13
      EQUB    0
      EQUB    0
      EQUB    0
    .reset_syntax
      EQUS    "Syntax: *ResetNow"
      EQUB    0
      ALIGN
The *command comprises of the command name, followed by data words for the command.
    .initialise
      STMFD   R13!, {R14}
      MOV     R0, #14              ; Set up event on mouse
                                   ; button change
      MOV     R1, #10
      SWI     "OS_Byte"
      MOV     R0, #&10             ; Claim event vector
      ADR     R1, check_mouse
      MOV     R2, #&100
      SWI     "OS_Claim"
      LDMFD   R13!, {PC}
This is our initialisation code. It enables mouse button
events, and then sets up a claim on the mouse button event.
    .finalise
      STMFD   R13!, {R14}
      MOV     R0, #13              ; Remove mouse event
      MOV     R1, #10
      SWI     "OS_Byte"
      MOV     R0, #&10             ; Release event vector
      ADR     R1, check_mouse
      MOV     R2, #&100
      SWI     "OS_Release"
      LDMFD   R13!, {PC}
This routine deals with unlinking the event and claim if the
module is shut down (ie, by *RMKill).
    .reset_code
      MOV     R0, #200
      MOV     R1, #2               ; Set to clear memory
      MOV     R2, #0
      SWI     "OS_Byte"
      SWI     131178               ; SWI XOS_Reset
      SWI     "OS_EnterOS"                           ; If not, fall back to
      MOV     R0, #&03800000       ; conventional reset code.
      LDR     R1, [R0, #0]
      STR     R1, [R0, -R0]
      MOV     PC, #0
This is the code which performs the reset.
    .check_mouse
      STMFD   R13!, {R0 - R12, R14}
      CMP     R0, #10              ; The right vector?
      BNE     exit
      CMP     R3, #7               ; All keys pressed?
      BNE     exit
      LDMFD   R13!, {R0 - R12, R14}
      B       reset_code
We handle mouse clicks with redundant checking. The first thing we do is ensure that we have been
passed the correct vector. With something that, say, filters ^A keypresses this is not necessary
as we can expect to get what we ask for and not something else. However with resetting
things are slightly more dangerous so we test this redundantly.
    .exit
      LDMFD   R13!, {R0 - R12, PC}
...restores the saved registers, only it places the setting of R14 into PC to save having to
do it manually.
    .stuff_at_the_end
      EQUB    10
      EQUB    10
      EQUS    "Resetter module © 1997 Richard Murray"
      EQUB    10
  ]
  NEXT pass%
  :
  OSCLI("Save <Obey$Dir>.ResetMod "+STR$~code%+" +"+STR$~P%)
  OSCLI("SetType <Obey$Dir>.ResetMod FFA")
  :
  END
Finally, write out an ID string, loop, then save the module.