Example 4
A resetter module

 

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:

  1. How to write a basic module.
  2. Claiming and using events
  3. Supporting OSs with different procedures.
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.
Secondly, you will see that there is no distinct starting point. Instead, the OS uses offsets stored in a header. The eleven EQUD instructions comprise the header.
    .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.
The help and syntax labels provide the appropriate text for the module.
    .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.
RISC OS 3.5 and later require a SWI to be called to perform a reset.
Earlier versions reset by branching to the start of the ROMs.
So what we do is try SWI "XOS_Reset". If that faults, we continue to the next instruction, which will perform an old style reset.
The XOS_Reset call is given by number so that the code is compilable on older machines. We use the X-form of the SWI so that errors (like "SWI not known") are ignored. The V flag will be set...
The "OS_Enter" changes the processor to SVC mode as we will be poking stuff that is inaccessible in USR mode.
    .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.
Then we look for mouse keypresses, which are basically:
  1. ADJUST (right button)
  2. MENU (middle button)
  3. ADJUST + MENU
  4. SELECT (left button)
  5. SELECT + ADJUST
  6. SELECT + MENU
  7. SELECT + MENU + ADJUST
If all buttons are NOT pressed, we go to the exit function (below), which...
    .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 (it's a habit, there's no actual reason for the ID string), loop, then save the module.

Download example: resetsrc.basic


Return to assembler index
Copyright © 2004 Richard Murray