RISC OS Open
Safeguarding the past, present and future of RISC OS for everyone
ROOL
Home | News | Downloads | Bugs | Bounties | Forum | Documents | Photos | Contact us
Account

File formats: Relocatable Module

category: File formats

Programmer's Reference Manuals
» Filetypes
» Module

Relocatable Modules

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.

Legacy modules

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.

Module header

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 a 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 applicable should be zero.
Most fields are offsets, some of these must be aligned, and all offsets must point within the length of the module to be valid.

Header entries
Offset Hex Purpose
+0 &00 Offset to Start code (or Branch instruction)
+4 &04 Offset to Initialisation code (or compression offset)
+8 &08 Offset to Finalisation code (and b31 flag)
+12 &0C Offset to Service Call handler
+16 &10 Unaligned offset to Title string
+20 &14 Unaligned offset to Help string
+24 &18 Unaligned offset to Help and Command Keyword table
+28 &1C SWI Chunk Base Number
+32 &20 Offset to SWI Handler code
+36 &24 Unaligned offset to SWI Name Table
+40 &28 Offset to SWI Decoder 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.


Start code

Zero, aligned offset to code, or ARM machine code instruction if any of bits 31-25 or 1-0 are set

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 (excluding module name)
R12 Private word for the currently preferred instantiation
R14 Not a return address – the module must exit with OS_Exit

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 must exit by calling OS_Exit, OS_ExitAndDie, or implicitly by starting another application.

If treated as an ARM instruction, it can only sensibly be a B branch or possibly BL (in order to point R14 near the start of the module). It is not sensible to continue execution into the Initialisation offset, so some kind of branch must be achieved, and the other potential construction of LDR PC,[PC,#offset] is definitely not recommended. A simple offset is best.

BASIC is an example of a module with a Start offset, as are some common Wimp applications built as modules.


Initialisation code

Zero, or aligned offset to code (if b31 clear); aligned length of compressed module (if b31 set)

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.
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 – you must jump directly to your own code if necessary.

You must not generate errors so use the X form of SWIs. 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. The module will then be removed.

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 SVC stack
R14 return address

The processor is in SVC mode with interrupts enabled. This entry point is not re-entrant.

On exit:

<= R0-R6 can be corrupted
R12,14 can be corrupted
V set if R0 is an error block

Should preserve processor mode and interrupt state. Flags can be corrupted – RISC OS only examines the V flag to see if an error occurred.

If this entry point is zero, it means that the module does not require initialisation.

R11, the I/O base or instantiation value, 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 (RiscPC and Iyonix class hardware). Any other value means that the module is being reincarnated and there are R11 other instantiations of the module. Modules that cannot usefully be instantiated must check R11 and error if inappropriate.

If the private word (R12) contains a non-zero value, then the module is being reinitialised and the contents are as your module left them.
Typically, modules claim workspace and write the address 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]. However, the Kernel places no interpretation on the contents of the private word except after finalisation (see below).


Finalisation code

Zero, or aligned offset to code; b31 set if contents of private word must not be freed

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 release claimed workspace. It is not necessary to free the workspace pointed to by the private word – the Kernel will automatically attempt to free that after finalisation if it is non-zero, unless b31 of the Finalisation offset is set.

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. This should be a last resort, as it can inconvenience the user.

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 SVC stack
R14 return address

The processor is in SVC mode with interrupts enabled. This entry point is not re-entrant.

On exit:

<= R0-R6 can be corrupted
R12,R14 can be corrupted
V set if unable to quit, R0 is error block

Should preserve processor mode and interrupt state. Flags can be corrupted – RISC OS only examines the V flag to see if an error occurred.

If this entry point is zero, it means that the module does not require any special finalisation and the Kernel can 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 Finalisation offset’s b31 is clear and the private word (R12) contains a non-zero value after finalisation, 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) if it is likely to be confused for allocated memory.


Title string

Unaligned pointer; must not be zero

The title string is an offset to a null terminated module title. The title should be short, descriptive and unique, and should contain plain alphanumeric characters (A-Za-z0-9!_). It must 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).

It is also used as a de facto SWI prefix if the module has a SWI Base number, but no Name Table or Decoder. In this case it must not start with an X.


Help string

Unaligned pointer; must not be zero

The help string is an offset to a formatted 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 by the *RMEnsure command.

The string must not contain any control characters other than Tab (chr 9 aligns to the next eighth column) or character 31 (which behaves like a hard space). The help string may contain spaces, but must not contain digits after the first 16 characters.

In order for *RMEnsure to work correctly you must use the following format (one or two tabs may be required to pad to column 16 in *Help Modules depending on Title length):

module_name<1 or 2 Tab chrs>vv.vv (DD Mmm YYYY)

The module name, which is generally the same as the title string (but can contain spaces), is followed by one or two tab characters to pad to column 16. This is then followed by the version number which is a 4.4 decimal number, eg 1.23 or 1234.5678. Modules typically only use two decimal digits, but up to four are supported. It is possible to follow the version number with letters such as “1.23a”, but these are ignored by *RMEnsure. There follows a space and the version date in parentheses: 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 help string is followed by a short copyright indication, extended version number, or other content, eg " © Author yyyy" or " [beta test version]"


Help and Command Keyword table

Zero, or unaligned pointer

The command table contains *Commands, help available through *Help, and configuration options accessed by *Configure and *Status. It comprises one or more structures of the following form, terminated by a single zero byte (the first structure need not be aligned, but the rest implicitly are):

bytes null terminated string, padded to word boundary
aligned offset offset from module start to Code, or 0
byte the minimum number of parameters, or 0
byte one bit per parameter if it is to be GSTransed
byte the maximum number of parameters, or 255
byte b7 Filing System Command (low priority)
b6 Configuration Option
b5 Help offset is code pointer
b4 Help and Syntax strings are tokens for the Messages file (see below)
unaligned offset   offset from module start to Syntax string
offset unaligned offset from module start to Help string,
or aligned offset to Help code, if b5 is set

Entries with a zero Code offset provide help only (but that help can be dynamically generated, see later). Otherwise, the Code is entered as follows:

On entry:

=> R0 ptr to command parameters or terminator, or if flags b6 was set:
0 = *Configure YourOption with no parameters
1 = *Status YourOption – display current configuration
R1 number of parameters
R12 ptr to private word
R13 SVC stack
R14 return address

On exit:

<= R0 error ptr if V set, or for configuration options:
0 = “Bad *Configure option”
1 = “Numeric parameter required”
2 = “Parameter too large”
3 = “Too many parameters”
R1-R6 can be corrupted

If flags b5 is set, the Help Code is entered on *Help like this:

On entry:

=> R0 ptr to buffer for your convenience
R1 length of that buffer
R12 private word
R13 SVC stack
R14 return address

Your code can display help text itself, or return a pointer to the provided buffer or your own:
On exit:

<= R0 passed to OS_PrettyPrint if not zero
R1-R6 can be corrupted

NOTE As this table is terminated by a single zero byte, be careful to align before an adjacent code label. Failing to do so is a very common error.


SWI chunk base number

Zero, or a multiple of 64 less than 1<<24; b17 completely ignored, but should be zero

This is the base number of the SWI chunk for 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 range has been issued.

If you request a SWI number, you will be allocated a “chunk” of 64 SWIs, starting at a Base Number such as &4E440. This is the number that goes into the SWI chunk base number field and together with the SWI Handler offset indicates that the module provides SWIs. The SWI Name Table (below) provides a simple way of translating between SWI names and numbers.

Because many modules provide SWIs and must all be unique, you must never release a module without a properly allocated SWI chunk.


SWI Handler code

Zero, or aligned offset to code

Called when one of your 64 SWIs has been issued. On entry:

=> R0-R9 the caller’s registers – your parameters
R11 sub-chunk number (SWI index) 0-63, no other values possible
R12 private word
R13 SVC stack
R14 return address

On exit:

<= R0-R9 as appropriate; R0 can be error block if V set
R10-R12 can be corrupted
CPSR flags returned to caller; if V set then R0 is error block

You should return error &1E6,“No such SWI” or suitably worded equivalent for any unimplemented SWIs. A typical Handler is:

Handler   LDR   R12, [R12]
          CMP   R11, #lastindex+1
          ADDCC PC, PC, R11, LSL#2
          B     BadSWI

          B     SWIzero
          B     SWIone
          ...

BadSWI    STMFD R13!, {R1-R7,R14}
          ADR   R0, BadSWIerr
          MOV   R1, #0
          MOV   R2, #0
          ADR   R4, Title
          SWI   XMessageTrans_ErrorLookup
          LDMFD R13!, {R1-R7,PC}

BadSWIerr &     &1E6
          =     "BadSWI",0

SWI Name Table

Zero, or unaligned offset to name table

This field is ignored unless the SWI Base Number and SWI Handler offsets are valid. SWI names comprise a prefix; an underscore; and then a name or numeric index such as “OS_WriteC” or “FooBar_63”. The prefix is often the same as the module Title (it does not have to be), but it cannot begin with an “X”. If your module Title is long, please take pity on your programmers and consider adopting a shorter SWI prefix. Module Titles and full SWI names must be unique, but SWI prefixes do not necessarily have to be – various modules share “Sound_” for example. This is difficult to coordinate and is discouraged.

The SWI Name Table is a list of consecutive null-terminated strings, starting with the SWI prefix (the part before the underscore) followed by the names (without prefix), and ending with a single null byte. If the Name Table field is zero, RISC OS will call the SWI Decoder code (below) if present, and otherwise default to a numeric index attached to the module Title. If there are insufficient names in the table for the requested SWI number, the Kernel will append a numeric index to your SWI prefix.

An example SWI Name Table:

switable =   "TTXHelper",0
         =   "AutoDetect",0
         =   "ScanFlash",0
         =   "ConvertToISO",0
         =   "ConvertToANSI",0
         =   "ExtractLine",0
         =   0

Note the final empty string to end the table. Also remember to align before using any code labels.

Unnamed SWIs at the end of the range can be omitted (and would be given numeric names automatically), but you cannot have empty strings within the table, so if the SWI corresponding to “ConvertToISO” in the above example were not defined the name should instead be “3” as that is its zero-based index.

The error-returning form of a SWI is indicated by b17 of the SWI number, and by prefixing the full name by “X”. However, this is never included in the Name Table, it is automatic.


SWI Decoder code

Zero, or aligned offset to code

If the module has a valid SWI Base Number and SWI Handler offset but no SWI Name Table, then code can be provided to assist OS_SWINumberToString and OS_SWINumberFromString. R0 selects between the two functions, being 0-63 for the former, and negative for the latter. Other values will not occur.

NumberToString
On entry:

=> R0 0-63 sub-chunk SWI number for this module (Base Number matched)
R1 ptr to buffer for the name, combined with R2
R2 offset into the buffer to start (“X” may already have been written)
R3 length of the buffer (i.e. offset from R1 to stop at)
R12 private word
R13 SVC stack
R14 return address

On exit:

<= R2 updated to point past the characters inserted into buffer, Kernel will terminate for you
R4-R6 can be corrupted
R9,R10 can be corrupted
R12 can be corrupted
V set if buffer overflow – no error block required

This is called by OS_SWINumberToString to provide a name for a given SWI number. Your code will only be called if the requested SWI number is one of the 64 starting from your SWI Base number, which cannot be < &200.

If the full SWI name fits into the buffer between R2 and R3, then write the unterminated name and return the updated R2. If the buffer is too small, return with V set but no error block in R0. There is no way to refuse to name a SWI, so build unused SWI names in the usual fashion – your SWI prefix; underscore; decimal index (R0 on entry).

NumberFromString
On entry:

=> R0 negative (-1) to distinguish from the above case
R1 ptr to ctrl-terminated string to recognise, no initial X
R12 private word
R13 SVC stack
R14 return address

On exit:

<= R0 0-63 sub-chunk number if SWI name recognised, else negative
R1 can be corrupted if recognised
R2-R6 can be corrupted
R9,R12 can be corrupted

Unlike NumberToString, your Decoder will be called for all unrecognised SWIs. However, the value you return in R0 is ORRed with the module’s SWI Base Number before being returned. Although this is intended to allow the module to only recognise its 64 SWI names, it can be seen that a module could in extremis return names for half of all SWI numbers.


Messages filename

Zero, or aligned offset to null-terminated filename

In order to facilitate internationalising modules, it is possible to provide a null-terminated filename for the Messages file for RISC OS to look up tokens from the Help and Command Table.
If the Messages filename is present and bit4 of a flag byte in the above Command Table is set, the help and syntax text are tokens to be looked up.

This Messages file only relates to the Help and Command table. All other internationalisation within the module needs to be dealt with by the module author.


Module flags

Zero (26bit only) or aligned offset to flags word

The flags word comprises:

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.


See also

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, but is superseded by this document.

Revised on February 7, 2021 15:45:09 by nemo (145) (82.10.217.226)
Edit | Back in time (10 revisions) | See changes | History | Views: Print | Source | Linked from: Modules, File Formats, The Acorn Terminal Interface Protocol, Addressing the end-of-life of AArch32

Search the Wiki

Social

Follow us on and

ROOL Store

Buy RISC OS Open merchandise here, including SD cards for Raspberry Pi and more.

Donate! Why?

Help ROOL make things happen – please consider donating!

RISC OS IPR

RISC OS is an Open Source operating system owned by RISC OS Developments Ltd and licensed primarily under the Apache 2.0 license.

Navigation

  • Home Page
  • All Pages
  • Recently Revised
  • Authors
  • Feeds
Site design © RISC OS Open Limited 2018 except where indicated
The RISC OS Open Instiki theme is based on Insitki's default layout

Valid XHTML 1.0  |  Valid CSS

Instiki 0.19.1(MML+)
This site runs on Rails

Hosted by Arachsys