Modules for Beginners
Modules, getting started
The First Module
In the last part I explained how to set up the BASIC Assembler to produce relocatable code and used it to produce a module header with title and help strings. In this part I will extend the ideas to produce some simple modules showing how to set up help strings and star commands with parameter values.
The first example called Module1 on the CD produces a module which does nothing! Well it initialises, and finalises correctly and has a title and help strings, so it will respond to *modules and *help Module1 commands. Here is the code:
REM Basic Assembling template for Modules
REM J.B.Pickard for RiscWorld
ON ERROR PRINTREPORT$+” at •+STR$(ERL):END
DIM mcode% 1024: REM reserve memory for assembling code
FOR pass% = 4 TO 7 STEP 3:REM two pass offset assembling
P% = 0: REM offset zero all offsets are from start of module.
O% = mcode%
REM Now setup the Module Header
[OPT pass%
EQUD 0 ;application startcode
EQUD init% ;initialisation
EQUD final% ;finalisation
EQUD 0 ;service start
EQUD title% ;Module title string
EQUD help% ;Module help string
EQUD 0 ;command table offset
.title% ;start of title string
EQUS ”Module1•
EQUB 0 ;terminating zero byte
ALIGN ;align for start of help string on a word boundary
.help% ;start of help string
EQUS ”Module1•+CHR$(9)+”1.00 (•+MID$(TIME$,5,11)+”)•+CHR$(13)
EQUS ”All it does is respond to *module and *help Module1 commands•
EQUB 0 ;terminating zero byte
ALIGN ;align for rest of module code
 
.init% ;start of module initialisation code
STMFD R13!,{R7-R11 ,R14}
 
LDMFD R13!,{R7-R11 ,PC}
 
.final% ;start of module finalisation code
STMFD R13!,{R7-R11 ,R14}
 
LDMFD R13!,{R7-R11 ,PC}
 
]
NEXT
REM Alter the pathname for the file so that it
REM will save the code in a convenient place
SYS ”OS_File•,10,”full pathname for module file•,&FFA,,mcode%,O%
A Few Notes
Do use the ON ERROR trap line, it makes debugging much simpler. Be careful with the SYS"OS_File",10 line. It must have a legal full path for the file and there are two commas between &FFA and mcode% parameters. You should find the module file appear in your chosen disc destination.
Double click on this module file, nothing should happen, your computer should work as before. Now open the Task Window (ctrl + F12). Type Modules [ret] at the star prompt. A list of the all the modules should appear with Module1 at the bottom. This shows your module has been successfully loaded and is residing in the RMA.
Now type Help Module1 [ret] The modules help message should appear with the date you assembled it. Now type *RMkill Module1 [ret] The prompt should return. Typing Modules [ret] will now show a new list which no longer contains Module1. N.B. [ret] stands for one press of the return key.
Module2 - a more useful example
This module demonstrates how easy it is to set up a star command.
The following lines are added to module1 (the full code is in the file Mod2SRC):
.command%
EQUS "Mod2on" ;Name of star command (not case sensitive)
EQUB 0 ;zero byte terminator
ALIGN ;align to start of next word
EQUD oncom% ;pointer to this commands routine
EQUD &00000000 ;star command information word
EQUD onsyntax% ;pointer to help syntax message
EQUD onhelp% ;pointer to help message for command
 
EQUD "Mod2off" ;start of next command
EQUB 0
ALIGN
EQUD offcom%
EQUD &00000000
EQUD offsyntax%
EQUD offhelp%
EQUD 0 ;zero word denotes the end of the command table
The above is called the command table. It has a strict structure.
First comes the name of the command terminated by a zero byte. RISC OS uses this to produce a lookup table. Next come 4 words:
- A pointer to the start of the routine for the command.
- Then an information word. This sets the parameters for the command. How many, what type, how are they to be read etc. In our example nothing is set. I will explain this word in greater detail later.
- The next word points to a string which is used if the user calls the command with the incorrect number of parameters.
- Finally we have a word pointing to a more general help string which is used when * help commandname is typed.
The whole structure is then repeated for the next command. A zero word is then used to mark the end of the command table.
The next set of lines setup the syntax and help strings for the commands.
.onsyntax% ;syntax message for command
EQUS ”Syntax: *Mod2on (no params)•
EQUB 0
.onhelp% ;help message for command
EQUS ”*Mod2on (no params) test help message•+CHR$(13)
+CHR$(10)+”first line of text.•+CHR$(13)+CHR$(10)
+”second line of text.•+CHR$(13)+
CHR$(10)+”Third line of text.•
EQUB 0
 
.offsyntax%
EQUS ”Syntax: *Mod2off (no params)•
EQUB 0
.offhelp%
EQUS ”Syntax: *Mod2off (no params) test help message•
EQUB 0
ALIGN
The following lines set up the routines for each command:
.oncom% ;start of actual code executed when *Mod2on typed
STMFD R13!,{R0-R11 ,R14}
ADR R0,onhelp% ;in this case just print out the help string
SWI ”XOS_Write0•
SWI ”XOS_NewLine•
LDMFD R13!,{R0-R11 ,PC}
 
.offcom% ;start of actual code executed when *Mod2off typed
STMFD R13!,{R0-R11 ,R14}
ADR R0,offhelp% ;in this case just print out the help string
SWI ”XOS_Write0•
SWI ”XOS_NewLine•
LDMFD R13!,{R0-R11 ,PC}
The pushing and pulling of registers R0 to R11 is probably an overkill but sometimes SWI's do corrupt certain registers! All thats left to do is alter the module header so the command table pointing word is changed from zero to command%.
When this module is run you should be able to do the following in a Task Window: Type either Mod2on[ret] or Mod2off[ret] and get back the help string for the command. Mod2on[ret] will give the syntax message. (RISC OS has used this message as an error since these commands do not have any parameters).
Typing *help commands[ret], this gives a list of all the modules commands loaded. Ours should be the last one in the list.
Typing *help Mod2on[ret], this should give the help message for this command.
Module3 Using parameters
The source code for this module is called Mod3SRC on the CD. This demo module is set up to accept 3 parameters for its *commands as shown below:
*newuser "name chrs" age system$variable
Rather strange but it is only a demo! The 1st parameter must be placed in quotes and can be one word or more. The 2nd parameter is typed as an integer. The third is the name of a system variable like File$Type_FFF etc.
Once this command is executed the module stores the data and then prints the message Data Accepted. Its other * commands are as follows
*modusers (no parameters)
*userdetails "name chrs"
The first one just prints out a list of the users. The second will find the data for matching the name.
The reason I chose such strange data is it shows the method of reading and transforming parameter data into a useable forms. The command block is set up as follows:
.command%
EQUS ”newuser• ;Name of star command (not case sensitive)
EQUB 0
ALIGN
EQUD nuser% ;pointer to this commands routine
EQUD 3+(3<<16)+(4<<8) ;star commands information word
EQUD nusyntax% ;pointer to help syntax message
EQUD nuhelp% ;pointer to help message for command
 
EQUS ”Modusers• ;next star command
EQUB 0
ALIGN
EQUD moduscom%
EQUD &00000000 ;do not need any parameters
EQUD modussyntax%
EQUD modushelp%
 
EQUS ”userdetails• ;final star command
EQUB 0
ALIGN
EQUD fnduser%
EQUD 1+(1<<16) ;only need first parameter
EQUD fndussyntax%
EQUD fndushelp%
 
EQUD 0 ;zero word denotes the end of command table
The Information word
This word holds the following:
Byte 0: The minimum number of parameters before an error will be reported. For the first command we need all 3. For the last command we only need one.
Byte 1: The way the first 8 parameters may be read by RiscOS. Each bit is used for each parameter. If set then RiscOS will try and expand the parameter as is if it is a system variable.In our example the third parameter is a system variable e.g. <File$Type_FFF> Note the use of < > to enclose the variable.
So to read this variable before it is passed to the module we need to set bit 2. Hence the (4<<8) value.
Byte 2:The maximum number of parameters before an error will occur. Hence for the first parameter (3<<16) and for the last parameter (1<<16).
Byte 3: This is rarely used, it contains flags which determine whether a string variable is a filing system command like *ADFS etc. or a star command related to *Status or *Configure.
Hence we do not use it.The full annotated BASIC source listing is on the CD called Mod3SRC.
On entering any star command routine RISCOS sets up the following registers:
- R0 points to the command tail. That is the complete string of characters that make up the parameters.
- R1 contains the number of spaces in the command tail which equals the number of parameters. NOTE: Any spaces contained in quotation marks are ignored.
- R12 contains the memory location of the modules private word. This value is usually the pointer to the workspace.
- R13 is the usual pointer to a full descending stack.
- R14 holds the return address.
I use !Zap for grabbing workspace and module from the RMA for checking. This excellent routine is very powerful, I have never used all its features. You can download it from the web.
Well that is it for this time. Just for fun(!) try and add another star command to module3 called 'deluser ' which will take the name parameter only and will delete the record from the workspace, then move all records after it up by 512 bytes.
My solution next time
Brian Pickard
|