The relocatable module provides the programmer with a powerful means of implementing all manner of machine code applications and utilities in such a way that they appear to the user as if they were an integral part of the Archimedes' operating system. Indeed a great deal of the machine's firmware is constructed around this principle: except of course that, being in ROM, the resident modules are fixed rather than relocatable.
The aim of this article is to look at the way in which a simple relocatable module may be written. By way of illustration, the accompany- ing program creates a simple module which responds to the two commands *Test and *Dummy, and to various *Help calls. If you type in the program, and run it, you will see the code being assembled, and then a list of resident modules will be displayed. This list should include the test module as the last item. If you type *HELP Modules, you should again see the new module listed. This time there will be an accompanying version number, and the date of assembly. To confirm that the module functions correctly, you could also try typing *Test. This should produce the following display accompanied by a beep:
The response to *Test
The most important part of the code presented here is the module header, and as you can see, this is extremely short. I have split it into two parts. The first 4 lines (lines 170 to 200) give the offset to four optional pieces of code, though since our simple module uses none of them, each offset is given as a zero value 32-bit word. For further details of the use of these options, the reader is referred to the Reference Manual page 366.
The last three lines of the header (lines 220 to 240) are concerned with the title of the module, the response to *Help Modules, and most important of all, the command table itself. All three are made use of in our example, although again, any of them could be replaced by a zero value 32-bit word if the user wished the operating system to ignore them.
Line 220 must contain either a zero 32-bit word or a 32-bit address pointing to a title string within the module. The address, like all those within the module must be given as an offset from the start of the module. But this is automatically handled by starting the module at address zero. We have inserted the variable "title" at this point. This refers to the label on line 290. The protocol for the title is just a string (containing no spaces) followed by a zero byte, and then the pseudo operator ALIGN to align the next instruction to a word boundary.
The Help offset on line 230 works in a similar way. It refers to the "help" label on line 350. The protocol is identical (except that with this and all other strings in the module, spaces are permitted). The code consists of a string followed by a zero byte and an ALIGN directive. In this case I have used the machine's TIME$ function to automatically incorporate the date on which the module was assembled.
Line 240 points to the module's command table. In our example this is located from line 400. It contains a set of five entries for each command name recognised by the module, the whole table being terminated with a zero 32-bit word (on line 600). The implementation of a command table of this kind will be most welcome to anyone who has written sideways ROM software for the model B or BBC Master, since it completely handles the parsing of the command. The programmer has simply to place his command name in the command table followed by a zero byte and an ALIGN directive. As you can see, the name "Test" appears at the head of the command table (on line 440). The second command "Dummy" appears on line 530, also followed by a zero byte.
After the zero-byte and ALIGN directive following each command name, there are four 32-bit words. The first points to the code to be executed when the command is recognised, the second contains a set of flags (of which more in a moment), the third optionally points to a message that the operating system will issue if the syntax of the command is given wrongly by the user, while the fourth points to a string of text which will be used as a response to the command *Help word, where "word" is one of the command keywords ("Test" or "Dummy" in this case).
Lines 470 and 560 point to the two commands themselves. The code for these begins at lines 830 and 980 respectively. They are similar, and we will restrict ourselves to a discussion of the code for *Test. There are two essential elements to this. The first line of code preserves the program counter on the stack with the instruction:
STMFD R13!,{R14}
The reason why the program counter is in register 14, and not 15 is that when the operating system passes the star command to the module, it performs a branch with link, preserving the program counter in register 14. To exit the module, and return to the language from which the star command was issued, we simply need to reinstate register 15 with the contents of the stack. This is achieved on line 930 with the instruction:
LDMFD R13!,{PC}
In the code which makes up the body of the command you should take care not to use R13 for any purpose other than as a stack pointer, otherwise you will lose your return address. R14 can be used as normal in Branch with link operations to perform subroutines within the module. But code in the module should not make reference to any absolute address, otherwise the routine will not be relocatable, and will crash. The code used to implement the command *Test is trivial, using SWIs 1 and 3 to write the string of text "The response to *Test", followed by character 7 to generate a beep.
The second 32-bit word in the command table comprises a set of flags which tell the operating system about the star command which you have implemented. Their function is indicated in the accompanying table. Bytes 0 and 2 respectively give the minimum and maximum number of parameters which may be used with the star command. If the user supplies a number of parameters outside the acceptable range, he is prompted with the corresponding syntax message (set up on lines 490 and 580). In our example we have set the flags so that *Test must be given without parameters (max and min number =0), and *Dummy must have either one or two. The operating system, I should add, expects parameters to be separated by spaces, not commas.
It is hoped that, given this information about our skeleton module, users will be able to create customised modules of their own. For further details on writing modules (including the function of flag bytes 1 and 3, and the use of initialisation and finalisation code) the reader is referred to the Rererence Manual part 2.
Byte | Contents |
0 | Min no of parameters (0-255) |
1 | OS_GTrans map (1st 8 params) |
2 | Max no of parameters (0-255) |
3 | 3 bit flags |
10 REM >TestMod8
20 REM Program Test Module
30 REM Version A 0.8
40 REM Author Lee Calcraft
50 REM RISC User March 1988
60 REM Program Subject to copyright
70 :
80 REM Generates a relocatable
90 REM module called RMtest
100 DIM R% &1000
110 FOR I=4 TO 7 STEP 3
120 P%=0:O%=R% :REM start code at &0
130 [OPT I
140 ;---------------------------------
150 ; Module Header
160 ;---------------------------------
170 EQUD 0 ;startcode
180 EQUD 0 ;initialise
190 EQUD 0 ;finalise
200 EQUD 0 ;service
210 ;
220 EQUD title ;Title string offset
230 EQUD help ;Help string offset
240 EQUD command ;Command table offset
250 ;---------------------------------
260 ; Title & Help Strings
270 ;---------------------------------
280 ;Title of the module for *MODULES
290 .title
300 EQUS "TestModule"
310 EQUB 0
320 ALIGN
330 ;
340 ;Help message for *HELP MODULES
350 .help
360 EQUS "Test Module"+CHR$9+"1.00 ("+MID$(TIME$,5,11)+")"
370 EQUB 0
380 ALIGN
390 ;---------------------------------
400 ; Table of commands
410 ;---------------------------------
420 .command
430 ;Response to *TEST
440 EQUS "Test" ;The command name
450 EQUB 0 ;Zero terminator
460 ALIGN
470 EQUD testcommand ;Address of code
480 EQUD &00000100 ;Response flags
490 EQUD testsyntax ;Invalid syntax
500 EQUD testhelp ;Help text
510 ;
520 ;Response to *DUMMY
530 EQUS "Dummy" ;The command name
540 EQUB 0 ;Zero terminator
550 ALIGN
560 EQUD dummycommand;Address of code
570 EQUD &00020001 ;Response flags
580 EQUD dummysyntax ;Invalid syntax
590 EQUD dummyhelp ;Help text
600 EQUD 0 ;table terminator
610 ;---------------------------------
620 ; Help and Syntax messages
630 ;---------------------------------
640 .testsyntax
650 EQUS "Syntax: *Test (no params)"
660 EQUB 0
670 ;
680 .testhelp
690 EQUS "*TEST does very little"
700 EQUB 0
710 ;
720 .dummysyntax
730 EQUS "Syntax: *Dummy <n> [<m>]"
740 EQUB 0
750 ;
760 .dummyhelp
770 EQUS "*DUMMY <n> does very little"
780 EQUB 0
790 ALIGN
800 ;---------------------------------
810 ; Code for *TEST
820 ;---------------------------------
830 .testcommand
840 STMFD R13!,{R14} ;Preserve program
850 ;counter on stack
860 SWI 3
870 SWI 1
880 EQUS "The response to *Test"
890 EQUB 7
900 EQUB 0
910 ALIGN
920 SWI 3
930 LDMFD R13!,{PC} ;Restore program
940 ;counter
950 ;---------------------------------
960 ; Code for *DUMMY
970 ;---------------------------------
980 .dummycommand
990 STMFD R13!,{R14}
1000 SWI 3
1010 SWI 1
1020 EQUS "The response to *DUMMY"
1030 EQUB 0
1040 ALIGN
1050 SWI 3
1060 LDMFD R13!,{PC}
1070 ;---------------------------------
1080 ]:NEXT
1090 OSCLI"SAVE RMtest "+STR$~R%+"+"+STR$~P%
1100 OSCLI"SETTYPE RMtest FFA"
1110 *RMLOAD RMtest
1120 *MODULES