|
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 (it's a habit, there's no actual reason for the ID string), loop,
then save the module.