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 |
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, ReInit, or Tidy 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.
…
…
…
…
…
…
…
…
…
…
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.