Showing changes from revision #7 to #8:
Added | Removed | Changed
Modules (filetype &FFA) lie at the heart of RISC OS. A module extends the operating system, or replaces part of it.
For example, while we are used to periodically updating our ROM image on current hardware, on older machines the ROM was a physical ROM so it was common to load a later version of the SharedCLibrary which would replace the one supplied with RISC OS. Likewise, the “nested Wimp” would also be loaded from disc and would entirely replace the older and less capable Wimp supplied in ROM.
Modules can also be used to extend system functionality. For instance, Fat32FS is an extended and more capable version of DOSFS. SparkFS provides a filing system interface to archive files, where they can be opened as if regular directories. A USB-MIDI module exists to permit simpler communication with MIDI keyboards using a USB interface. The options are nearly endless.
Modules written for earlier versions of RISC OS will not work on RISC OS 5. This is due to incompatibilities in the ARM programmer’s model, namely RISC OS 5 uses a 32 bit Program Counter with a separate Status Register, while older versions use a combined 26 bit PC plus PSR. The two are fundamentally incompatible.
It is, however, possible to create a 26/32 agnostic module if you set the flag word to indicate that your module is 32 bit safe, and then you include the expected function exit (preserves flags or does not) according to whether or not the system is using a combined or separate PSR.
Information on writing modules for the older 26 bit versions of RISC OS may be found here.
Modules consist of code and data in a single file that is prefixed by a header. The header provides offsets to various entry points and data.
A 26 bit module must have at least the first seven fields.
A 32 bit module must have all fields present as the last one is the offset to the flags word containing the 32 bit flag (Note: Other flags may be used in future at present only the bit for 32 bit status is used).
Fields that are not required should be zero to ensure they are seen as invalid.
Most fields are offsets, and the offset must point within the module itself or the operating system will reject the module.
Offset | Hex | Purpose |
---|---|---|
+0 | &00 | Offset to Start code |
+4 | &04 | Offset to Initialisation code |
+8 | &08 | Offset to Finalisation code |
+12 | &0C | Offset to Service Call handler |
+16 | &10 | Offset to Title string |
+20 | &14 | Offset to Help string |
+24 | &18 | Offset to Help and Command Keyword table |
+28 | &1C | Offset to SWI chunk base number |
+32 | &20 | Offset to SWI handler code |
+36 | &24 | Offset to SWI decoding table |
+40 | &28 | Offset to SWI decoding code |
+44 | &2C | Offset to Messages file name |
+48 | &30 | Offset to Module flags |
Legacy versions of RISC OS only required the first seven fields (those prior to the SWI chunk). The rest were optional, depending on necessity. RISC OS 5 requires all fields to be present.
Calling OS_Module with either Run or Enter will enter a module through this offset, as will the *RMRun
command.
On entry:
R0 | Pointer to command string (including module name) |
R12 | Private word for the currently preferred instantiation |
The processor is in User mode with interrupts enabled. This entry point is not re-entrant.
This call does not return.
On exit:
As this was started as the current application, it should exit by calling OS_Exit.
Note that the field need not actually be an offset. If it cannot be interpreted as an offset, it will instead be executed. This permits the first instruction to be a branch which may be useful to directly run the application code during development, as if it was a normal application and not a module.
This entry is important for having modules that can provide Wimp tasks.
When a module is initially loaded, reloaded, or after a tidy (in other words, following the Run, Enter, or ReInit calls) the Initialisation entry is called. This is expected to set up the module so that all of the other entry points are able to operate. This entry point is the first one to be called and no others will be called beforehand.
Typically you would use this entry point to claim some workspace, set up data tables, hook into vectors and events, register as a filing system, etc.
Note that at this point, the module is not yet on the list of active modules so you cannot call your own SWIs. If you need to do such a thing, jump directly to your own code as is appropriate.
You must not generate errors. If there is a problem that prevents the module from initialising, R0
should point to a standard error block and the module should return with the V flag set.
Registers R7
-R11
and R13
must be preserved.
On entry:
R0 | Pointer to the environment string (any initialisation parameters given when the module was loaded) |
R11 | I/O base or instantiation number (see below) |
R12 | Private word for this instantiation (see below) |
R13 | Pointer to SVC stack |
The processor is in SVC mode with interrupts enabled. This entry point is not re-entrant.
On exit:
Should preserve processor mode and interrupt state. Flags can be corrupted – RISC OS only examines the V flag to see if an error occurred.
Registers R0
-R6
, R12
and R14
can also be corrupted.
Return using MOV PC, R14
with, optionally, V set if flagging an error.
If this entry point is zero, it means that the module does not require any special initialisation.
If the I/O base or instantiation value (R11
) has the following meanings: If it is zero, the module was loaded from ROM, from a filing system, or was already present in memory. If it is > &03000000 then the module was loaded from an expansion card and R11
is the synchronous base address of the card [this only applies to RiscPC and Iyonix class hardware]. Finally, any other value means that the module is being reincarnated and there are R11
other instantiations of the module.
If the private word (R12
) contains a non-zero value, then the module is being reinitialised. R12
will point to your claimed workspace.
It is normal to claim workspace and then write the address of this workspace to R12 with STR Rxx,[R12]
, so that when RISC OS later calls the module via the other entry points and provides the private word in R12, you can extract your workspace address from this by using LDR R12, [R12]
.
This is the opposite of the initialisation code, it is called prior to a module being removed from memory. The module should remove itself from vectors, events, etc and to release claimed workspace.
The finalisation entry is called by the ReInit, Delete, and Clear calls as well as the associated commands *RMReInit
, *RMKill
, and *RMClear
; and also when a newer version of a module replaces an older version, the older one would be finalised prior to the new one being loaded.
If the module does not want to quit, it should set R0 to point to an error block, and return with the V flag set. The finalisation will be aborted and the module will still be active.
Registers R7
-R11
and R13
must be preserved.
On entry:
R10 | Fatality indication – is ‘1’ as finalisation is always fatal. |
R11 | Instantiation number |
R12 | Private word for this instantiation |
R13 | Pointer to SVC stack |
The processor is in SVC mode with interrupts enabled. This entry point is not re-entrant.
On exit:
Should preserve processor mode and interrupt state. Flags can be corrupted – RISC OS only examines the V flag to see if an error occurred.
Registers R0
-R6
, R12
and R14
can also be corrupted.
Return using MOV PC, R14
with, optionally, V set if flagging an error.
If this entry point is zero, it means that the module does not require any special finalisation and that RISC OS can just go ahead and terminate the module.
The fatality indication in R10
had meaning a long time ago when it was possible to use *RMTidy
to tidy up gaps in the module area. It would do this by temporarily ‘soft’ finalising all modules, relocating everything, then ‘soft’ reinitialising the modules afterwards. However *RMTidy
has not been feasible for a very long time, so it should be assumed that all finalisation attempts are terminal.
R11
provides the dynamic instantiation number, which may not be the same as the value given in R11
on initialisation.
If the private word (R12
) contains a non-zero value once the finalisation has occurred, RISC OS will consider the value to be the module’s claimed workspace and will attempt to free it. Thus, it is good form to write zero to the module’s private word (the original R12 on entry) once the workspace has been released.
The title string is an offset to a null terminated module title. The title should be short and descriptive and unique and contain plain alphanumberic alphanumeric characters. It should not contain spaces or control characters.
It is recommended that it is formatted in a manner similar to the built-in modules; for example: “SpriteUtils”, “Desktop”, “Filer”, “FileSwitch”, and “TaskManager”.
The matching of module names is not case sensitive.
The module name is used with OS_Module calls that take module names (Enter, ReInit, or Delete) as well as the associated commands (*RMReInit
and *RMKill
), as well as being listed by *Modules
).
The help string is an offset to a null terminated string that is printed out by *Help
prior to any information from the module such as the list of commands available.
The help string also provides the module version number that is used with the *RMEnsure
command.
The string must not contain any control characters asides other from than Tab which (which will align to the next eighth column, column) and or character 31 which (which behaves like a hard space. space). The help string may contain spaces.
In order to make the output of *Help Modules
(which lists all available modules) look tidy, it is recommended that you use the following standard format:
module_name Tab[Tab] v.vv (DD Mmm YYYY)
The module name, which is generally the same as the title string (but can contain a space between words), is followed by one or two tab characters to align to make the title be 16 characters.
This is then followed by the version number which is simply a single digit major version, followed by a full stop, followed by a two digit minor version; for example “1.23
” or “0.50
”.
Following is the date of module creation, which consists of a two digit day of the month (with leading zero if necessary), a three character month name (in English), and a four digit year; for example “12 Apr 2015
”.
Sometimes the standard help string is followed by some sort of copyright indication, extended version number, or other content. This is unofficial but is tolerated by RISC OS.
…
This is the base number of the SWI chunk used by the module, if the module provides SWIs. This is used by RISC OS to enable the module to be called when a SWI within its SWI chunk has been issued.
When you register for a module allocation, if you request a SWI chunk you will be allocated a base number, such as &4E440
or &57180
. This is the number that goes into the SWI chunk base number field and it indicates to RISC OS that that SWI number allocated and the following 63 ‘belong’ to your module. The SWI decoding table (below) provides a way of translating SWI names to numbers.
Because many modules provide SWIs and they must all be unique, you should never release a module without a properly allocated SWI chunk.
…
The SWI decoding table is a simple table providing a list of names for the SWIs available. If this field is zero, RISC OS will try calling the SWI decoding code (below), but generally providing a list of SWI names is sufficient.
The list is a series of names each followed by a single null byte. They do not need to be word aligned. A second null byte ends the list.
If you recall, SWIs are laid out as a “group” name, followed by an underscore, followed by an individual name for the SWI. For example OS_WriteC
, ADFS_DiscOp
, and Sound_Control
.
Therefore, the first entry is the group name. This is usually the same as the module name, and it will be used as the prefix to all of the SWIs available in the module. Then follow a list of SWI names, each null terminated, followed by a null to end the list.
For example, the first few SWIs of my TTXHelper module are:
switable
= "TTXHelper", 0
= "AutoDetect", 0
= "ScanFlash", 0
= "ConvertToISO", 0
= "ConvertToANSI", 0
= "ExtractLine", 0
EQUB 0
ALIGN
(there are others, but this is sufficient for explanation)
The module’s SWI chunk is &57180
, so RISC OS can look this up and know that TTXHelper_AutoDetect is SWI &57180, TTXHelper_ScanFlash is SWI &57181, TTXHelper_ConvertToISO is SWI &57182, TTXHelper_ConvertToANSI is SWI &57183, and finally SWI &57184 is TTXHelper_ExtractLine.
You do not need to include entries for the “X” prefixed form of SWIs (such as XOS_Module
), this is handled automatically by RISC OS.
If there are gaps in the SWI numbering, it is typical to use the name “NOP” (for no-operation) for any gap entries. Be aware that this would make modulename_NOP
be a valid SWI, so your code should cope sensibly should anybody decide to call it. Since you would typically need to include these in your jump table (if writing in assembler) it is simplest if you make all of the NOPs jump to the “invalid SWI” error message.
…
In order to facilitate internationalising modules, it is possible to provide a pointer to a Messages file that RISC OS can use to look up the output from the help and command table.
If the Messages file name (which is the full null terminated pathname of a Messages file) is present and bit 28 of the help and command information word is set, the output is not literal text to be printed, it is a token to be looked up in the Messages file here referred.
This Messages file name only relates to the help and command table. All other use of Messages within the module needs to be dealt with by the module author.
This is an offset to the module flags word.
The flags are:
Bit 0 | Module is 32 bit compatible |
Bits 1-31 | Reserved (should be zero) |
Modules not flagged as 32 bit compatible will not be loaded by a RISC OS 5. If no flags word is present, the module is assumed to be 26 bit compatible only. Note that the 32 bit compatibility flag is only a marker, the actual module code is not guaranteed to be 32 bit safe by the presence of this flag. Incorrectly coded modules (with 26 bit only elements) will be loaded when this flag is present. It is the responsibility of the module author to ensure the code is compatible.
For more complete information on modules, please refer to the chapter on Modules in book 1 of the Programmer’s Reference Manuals (it’s a very big chapter!) but please note that the information provided there is nearly a quarter century and several major RISC OS versions out of date. The chapter is useful to provide a detailed overview of how modules work and interact, however the information presented here is what is correct nowadays.