RISC World

Modules for Beginners

Part 3 of Brian Pickards series : Service Calls and SWI's

Last time I set a problem to add a star command to the third module. This command to be called 'deluser' would take the name parameter and would delete the record from the workspace, then move all the records after it up by 512 (length of record) bytes. Please find the solution in the BASIC listing called 'deluser' and the module 'Moddelusr' it assembles.

In this part I am going to show how modules deal with Service calls and SWI's.

Service Calls

Service calls are generated by RISC OS as a means of letting modules know about changes, that are about to happen. Some of these calls must never be claimed and therefore are can only be used for an application or a modules information.

Here is a list of Service calls, note they all have a number. All their names start with 'Service_' UKCommand (&04) an unknown star command. This issued before the file system tries to *run it.

  • Error (&06) issued after an error has occurred but before the error handling routine (must not be claimed).
  • UKByte (&07) an unknown OS_Byte number has been called.
  • UKWord (&08) an unknown OS_Word number has been called.
  • LookupFileType (&42) unknown file type (i.e. unknown name).
  • ModeChange (&46) issued after a mode change has taken place.
  • PreModeChange (&4D) issued before a mode is about to change.

This list is not complete, but these show where service calls can occur. Lets use the Service_LookupFileType to make up our own file type complete with name.

To make a module intercept ALL service calls offset &0C in the header code must point to the modules service call handler code. So in the BASIC assembler the following would assemble such a module.

   [OPT pass%
   EQUD 0
   EQUD 0
   EQUD 0
   EQUD service%    ;service call handler
   EQUD title%
   EQUD help%
   EQUD 0
   EQUS "Module4"
   EQUB 0
   EQUS "Module4"+CHR$(9)+"1.00 ("+MID$(TIME$,%,11)+CHR$(13)
   EQUS "This module intercepts service calls and recognises its own file type"
   EQUB 0
   TEQ R1,#&42      ;R1 =Service number. Is this Service_LookupFileType
   MOVNES PC,R14    ;if not then get out.
   CMP R2,#3        ;our file type number is 3
   BNE exit%        ;if not ours then exit (dont claim).
   ADR R1,filetype% ;point R1 to start of our filetype
   LDMIA R1,{R2,R3} ;load R2 with first 4 chrs and R2 with last 4 chrs
   MOV R1,#0        ;make R0 zero so we are claiming this service call
   MOVS PC,R14      ;return

   EQUS"Our Type"   ;our file type string

This is a simple module, note that R1 has to be made zero in the service call routine to tell RISC OS we have claimed this service call. If we did not wish to claim it but pass it on then R1 must be preserved.

R2 and R3 are required to store the eight byte name for the file type.

I have included a Dummy test file called OurType with its file type number set to 3 for testing out the module.

Before loading Module4 the file type is reported by its number showing the name is unknown. After loading Module4 the file type is shown as 'Our Type'. This proves the filer issues this service call before opening its filer info window.

Software Interrupts (SWIs)

One great advantage of RISC OS is its modular design which allows third parties to easily add to the operating system. The main way of adding extra features is by defining new SWI commands.

To implement SWI's in a module there must be extra words added to the header. Consider the following:.

   [ OPT pass%
   EQUD 0                      ;start code
   EQUD 0                      ;initialisation
   EQUD 0                      ;finalisation
   EQUD 0                      ;service
   EQUD title%                 ;title string pointer
   EQUD help%                  ;help string pointer
   EQUD 0                      ;star command
   EQUD SWI_chunk_base_number% ;starting base number for SWI's
   EQUD SWI_handler_code%      ;pointer to SWI handler code

In this header if the extra two words after the star command exist then RISC OS will register the module as having a set of SWI's. These SWI's must have a starting number, called the chunk base number. For example all WIMP SWI's have a base chunk number (in hexadecimal) of &400C0.

The maximum number of SWI's allowed within a module is 64 (more than ample!). These are assumed to start with the chunk base number and go up to a maximum equal to chunk base number + 63.

No two modules are allowed to use the same SWI chunk base number unless they are taking over the function of a previous modules routines. So an updated floating point emulator module uses the same SWI numbers as previous versions but must not change the overall function of any of the previous existing SWIs, but can add extra SWIs within the chunk number.

To make sure no clash in SWI numbers occurred third parties had to apply to Acorn for a chunk base number.

There are guide lines to the ranges and their specific function included in PRM volume 1 page 26.

It is safe to use a chunk base number which has bits 18 and 19 set (reserved for users). This means the minimum chunk base number of &C0000, so I will choose this.

How RISC OS enters a module using an SWI

When a SWI is called RISC OS looks up the SWI handler code in the module whose chunk base number lies within the SWI number. On entering the module R11 contains the offset from the chunk base number i.e. 0 to 63. Since it is unlikely that all 64 SWI numbers are used the programmer must trap for any number greater than that allowed for by the module. The neatest way of doing this is as follows:

.SWI_handler_code% . CMP R11,#(endjumptable%-startjumptable%)/4 ;R11 in our range? ADDLO PC,PC,R11,LSL#2 ;add offset*4 to PC B unknownSWIerror ;only executed if out of range .startjumptable% ;start of jump table B OurSWI_0 ;each jump for a SWI B OurSWI_1 ;in number order . . B OurSWI_n .endjumptable% ;end of jump table which in .unknownSWIerror ;this example is the error ADR R0,errortoken% ;R0 pointing to error number ORRS PC,R14,#(1<<28) ;set overflow flag for error .errortoken% EQUD &1E6 ;error number EQUD "Unknown Computer_XX SWI" ;error message EQUB 0 ;null byte ALIGN . .

We can now implement any of our SWI's by calling them by number. But for user friendliness SWIs should also be able to be called by name. To include this facility the module requires a name table. A pointer at position &24 (following the SWI handler code pointer) should point to a name table with the following structure:

   EQUS "Chunkname"
   EQUB 0
   EQUS "Subname one"
   EQUB 0
   EQUS "Subname two"
   EQUB 0
   EQUS "Subname n"
   EQUB 0
   EQUB 0

The chunkname is the first part of the name common to all the SWIs. The WIMP SWIs have a chunk name Wimp. The sub names are all different. The first subname used in the WIMP is Initialise. So the complete name for the first SWI in the WIMP is Wimp_Initialise. Note the underscore is stripped out by RISC OS when matching names in a module and so is not included in the name table.

MOD5SRC contains the source code for the complete implementation of SWIs.

As an example module5 has a set of SWI's whose chunk number starts at &C0000 and produce machine statistics.

SWI "Computer_OS"
Gives back in register 0 a pointer to the version of the operating system.

SWI "Computer_CPU"
Gives the type of CPU in the machine R0=0 not StrongArm

SWI "Computer_Memory"
Gives in register 0 the max main RAM and in register R1 the VRAM. Values in bytes.

SWI "Computer_Tasks"
Returns in register 0 a pointer to a list of all the tasks running on the machine together with their task handles etc. and in R1 the number of tasks found.

Some of the above are trivial but the last one might be useful. The source MOD5SRC assembles the module called Module5. For a more detailed account how to use the above look at and run the BASIC program SWIsUse. MAKE SURE MODULE5 IS LOADED FIRST!

That is about it for this part, next time I will tackle the use of the Wimp filtering system in modules and show how the action of applications can be modified.

Brian Pickard