RISCOS.com

www.riscos.com Technical Support:
Programmer's Reference Manual

 

An introduction to SWIs


Introduction

The main way you can access the routines provided by RISC OS is to use a SWI instruction. SWI stands for SoftWare Interrupt, and is one of the ARM's built-in instructions.

In brief, when you issue a SWI instruction, the ARM leaves your program. It jumps to a fixed location in memory, where there is normally a branch instruction into the RISC OS kernel code. This code examines the SWI instruction, and determines which particular OS routine you wanted. This is called, and when it is finished, control returns to your program.

The rest of the chapter will explain how to call SWIs from different languages, and will follow how a SWI works in rather more detail.

SWI numbers and names

RISC OS can work out what routine you require because the SWI instruction code contains a 24-bit information field which uniquely identifies a routine. This field is known as the SWI number. The chapter SWI numbers in detail describes how SWI numbers are allocated.

RISC OS provides several hundred different SWIs. You would find it difficult to remember what function each SWI number corresponds to, so each SWI also has a name. These names are held in the RISC OS ROMs, and in any system extension modules that have been loaded.

Parameters and results

Obviously, you need to be able to pass values to SWI routines (parameters), and must be able to read values back (results). The ARM registers are used to pass information between the user and RISC OS. In general, you will use R0 to pass the first parameter, and then enough registers after that to pass the rest.

  • SWIs may use registers R0 - R9 inclusive.
  • Note that the mechanism for calling SWIs from BASIC will only handle registers R0 - R7 inclusive. For this reason, parameters are normally restricted to these registers.

Fortunately it is rare that a routine needs to use more than 4 or 5 registers.

When the information passed is numeric, character or address, you generally store the data itself in the register. However, if the data is a string, or a large amount of numeric data, then you pass a pointer to the data instead. For example, filenames are passed as a pointer to the characters in memory, and the window manager uses pointers to large window descriptors.

An example

As an example of how to use a SWI. We will look at one called OS_WriteC; its SWI number is &00. It is used to output a character. It takes a single parameter - the character you want to output - which is passed in R0. Suppose you wanted to output the character 'A', the ASCII code of which is 65.

Calling from Assembler

In assembler you could write:

        MOV     R0,#65          ; Load R0 with 'A'
        SWI     0               ; and output it

It would be clearer if you set a constant named OS_WriteC to &00. We suggest you do so in a standard header file that contains all SWI names and numbers. Using such a file, you could then write:

        MOV     R0,#65          ; Load R0 with 'A'
        SWI     OS_WriteC       ; and output it

When this is assembled, the bottom 24 bits of the SWI instruction are set to zero - the SWI number for OS_WriteC.

Calling from BBC BASIC

From BBC BASIC you can call a SWI routine in two different ways:

  • use the built in assembler
  • call it directly from BASIC.
BBC BASIC Assembler

BASIC's built in assembler is very similar to the standard ARM assembler. However, the SWI names are available as strings; note that this means you must enclose them in double quotes. The case of the letters is significant:

        MOV     R0,#65         ; Load R0 with 'A'
        SWI     "OS_WriteC"    ; and output it

BBC BASIC

You can use the BASIC keyword SYS to call SWI routines directly from interpreted BASIC. BASIC just asks RISC OS what SWI number the given string corresponds to; you will find full details of the syntax in the BBC BASIC Reference Manual. Our example would be written:

SYS "OS_WriteC",65

Calling from C

The Acorn C library provides a similar procedure to call a SWI routine. Again, you should see the ANSI C manual for full details of the syntax, and how errors are handled. The example below assumes that relevant header files have been #include'd:

_kernel_swi_regs regs;                 /* declare register structure */
regs.r[0] = 65;                        /* set pseudo R0 to 'A' */
_kernel_swi(OS_WriteC, &regs, &regs);  /* call SWI */

More about SWI numbers and names

In general, you don't have to worry about the exact mechanism used by RISC OS to decode the SWI instructions. As long as you use the right SWI number, and pass the correct parameters, the correct result will be obtained.

We strongly advise you to use SWI names in your code, for added clarity. This is easy from BASIC, as the names are already set up; from other languages (such as assembler and C above) you will find this easiest if you set up header files. Examples in the rest of this manual will assume you have done so.

SWI name prefixes

The prefix of the SWI name (OS in the example above) determines which part of the system will deal with the SWI. OS obviously refers to the calls handled directly by RISC OS. Examples of other prefixes are Font, Wimp, and ADFS. The prefix is determined by the module which implements the SWI.

Error handling - an introduction

RISC OS provides full error handling facilities for SWIs. In general, if a SWI has no errors, the V flag in R15 is clear as the routine exits; if there is an error, the V flag is set and R0 points to an error block on exit.

As the routine exits, RISC OS checks the V flag. If it is set (meaning there was an error), then RISC OS looks at bit 17 (the X bit) of the SWI number:

  • If it is set then control returns to your program, and you should deal with the error yourself.
  • If it is clear control is passed to the system error handler, which reports the error to you. You can of course replace the system error handler with one of your own; indeed, most programs do.

For further details, see the chapter entitled Generating and handling errors.

SWI numbers in detail

The 24 bits used to encode the SWI number in the instruction allow SWIs in the range 0 - &FFFFFF (16777215) to be used. This SWI 'address range' is divided up into several parts under RISC OS. For example, SWIs in the range 0 - &3FFFF (262143) provide the basic operating system functions. (Only a small proportion of these are currently used, however.) Modules can provide their own SWIs, and these must be given unique numbers to avoid clashes.

You can also define your own SWI calls. When a program executes a SWI whose number is not recognised by the OS or any of the modules in the machine, the OS calls a special routine called the 'Unused SWI vector', or 'UKSWIV' for short. Usually, this will just return the error No such SWI. However, a user program can claim this and, if the SWI number is one that it recognises, perform the appropriate task.

This section explains in detail how SWI numbers are allocated. The bottom 24-bit section of the SWI op-code is divided up as follows:

Bits 20 - 23

These are used to identify the particular operating system that the SWI expects to be in the machine. All SWIs used under RISC OS have these bits set to zero. Under RISC iX, bit 23 is set to 1 and all other bits are set to zero.

Bits 18 - 19

These are used to identify which part of the system software implements the SWI, as follows:

Bit number Meaning
19 18
0 0 Operating system
0 1 Operating system extension modules
1 0 Third party resident applications
1 1 User applications

Thus OS SWIs, such as OS_WriteC, have both bits clear.

Modules such as filing systems, device drivers for expansion cards, and the Font manager have bit 18 of their SWIs set, so their SWI numbers start at &40000. Note that this can include system extension modules written by third parties.

Any SWIs provided by application software that is distributed by other software houses should have bit 19 set and bit 18 clear.

Bit 17

This is used to determine the action taken on errors. It is the 'X' bit. Error handling in SWIs is described in the Generating and handling errors.

Bits 6 - 16

These are the SWI Chunk Identification numbers. They identify a block of 64 consecutive SWIs, for use within a single application or system extension module. Anyone wishing to use one of these blocks of SWIs for distributed software should apply in writing to Acorn Customer Service, who will allocate a unique value.

Bits 0 - 5

These identify individual SWIs in a chunk. Hence a third party application may use SWIs in the following binary range:

000010nnnnnnnnnnnn000000 to
000010nnnnnnnnnnnn111111

where nnnnnnnnnnnn is the chunk number that the software house has been allocated for the application or module.

Technical details

Although in general you don't need to know how a SWI is decoded and executed, there are some more advanced cases where you will need to know more. This is what happens:

  1. The contents of R15 are saved in R14_svc (the SVC mode subroutine link register).
  2. The M0 and M1 bits of R15 are set (the processor is forced to SVC mode) and the I bit is also set (IRQ is disabled).
  3. The PC bits of R15 are forced to &08.
  4. The instruction at &08 is fetched and executed. It is normally a branch to the code that RISC OS uses to decode SWIs.
  5. RISC OS uses the PC bits of the return address held in R14_svc to pick up a copy of the SWI instruction.
  6. Interrupts are restored to the state they were in when the SWI was issued. This is done by setting the I bit in R15 to the value of the equivalent bit in R14_svc.
  7. The V bit of the return address held in R14_svc is cleared, unless the SWI was OS_BreakPt or OS_CallAVector. (This is done for the error handling system - see the chapter Generating and handling errors.)
  8. RISC OS looks at the 24 bit SWI number field held in the SWI instruction, and uses it to decide where to branch to.
  9. If the SWI does not use a vector, RISC OS will branch directly to the actual SWI routine.

    If the SWI does use a vector, RISC OS branches to the routine that calls the vector. Unless you have claimed the vector, this will execute the actual SWI routine.

  10. The SWI routine is executed.
  11. Any error handling is performed.
  12. Any call back handling is performed.
  13. Control is returned to your program by using the instruction:

            MOVS R15, R14_svc.

    This restores both the mode you were in when you called the SWI, and the interrupt status. Note however that a few SWIs (such as OS_IntOn, which enables interrupts) deliberately alter the mode and/or interrupt status so they are not restored on exit.

    If an error is being returned by setting the V bit, the instruction

            ORRS R15, R14_svc, #V_bit

    is used instead.

An example of documentation

Below is an example of how a SWI is documented. Comments are provided in grey boxes so you can understand exactly what each bit means.

Some things are assumed to be consistent for all SWIs, and only exceptions are documented:

  • SWIs are decoded and executed as outlined above.
  • The V flag is cleared if there is no error; it is set if there is an error, and R0 will then point to an error block. See the chapter entitled Generating and handling errors for further details.
  • Other registers and flags are preserved across the call, unless stated otherwise.

Note that the description of the SWI refers to the routine itself - in other words, what happens during step 10 above. Thus headings such as Processor mode and Interrupts refer to what happens in the SWI routine itself - not what happens when the SWI instruction is decoded, and so on.


OS_Write0
(SWI &02)

SWI name
and number

Writes an indirect string to all of the active output streams

Summary
On entry

R0 = pointer to null-terminated string to write

On exit

R0 = pointer to the byte after the null byte

Entry and exit conditions for SWI routine
Interrupts

Interrupts are enabled
Fast interrupts are enabled

Interrupt status (The Interrupts and Processor mode entries define how the SWI routine affects these, not how RISC OS does. See above for further details).
Processor Mode

Processor is in SVC mode

Processor mode
Re-entrancy

SWI is not re-entrant

Re-entrancy in interrupt routines (The concept of re-entrancy and its application to interupt handling routines is explained in the chapter entitled Interrupts and handling them).
Use

This call sends the string pointed to by R0 to all of the active output streams. It uses OS_WriteC directly a character at a time. The string is terminated by a null byte.

Full description of the use of this SWI
Related SWIs

OS_WriteC

Closely related SWIs (if any)
Related vectors

WrchV

Closely related vectors (if any)
Example SWI documentation

Important notes

There are some important points to note if you are writing your own SWI routines. These apply if:

  • you call a SWI from your own SWI routine
  • you claim a vector and replace a routine with one of your own.
Calling SWIs from SWI routines

Normally SWIs are executed in SVC mode. If you call another SWI from this mode without taking precautions, it will use R14_svc and crash the computer as follows:

  1. The first SWI is executed from a program that is running in User mode. R15 (the return address to the program) is copied to R14_svc, and the processor is put into SVC mode.
  2. The first SWI routine is entered.
  3. This routine calls the second SWI from SVC mode. R15 is copied to R14_svc, overwriting the return address to the program. The processor remains in SVC mode.
  4. The second SWI executes, and control is returned to the first SWI routine by loading R14_svc back into R15.
  5. The first SWI routine finishes executing, and tries to return control to the program by loading R14_svc back into R15.
  6. Because R14_svc was overwritten by the second SWI, control is not returned to the program. Instead, the computer just repeatedly loops through the end of the first SWI routine.

The cure is simple; you must save R14_svc before you call a SWI from within another SWI routine, and restore it after control returns to the SWI routine. This is typically done using a full descending stack pointed to by R13_svc, like this:

        STMFD   R13!, {R14}    ; Save return address
        SWI ...                ; Call the SWI (corrupts R14)
        LDMFD   R13!, {R14}    ; Restore return address

Of course if you call several SWIs in succession, you don't have to save and restore R14 around each call - instead you should save it before calling the first SWI, and restore it after the last one.

Error handling with vectored SWIs

Normally, you can assume that the V flag of the return address held in R14_svc has been cleared by RISC OS before a SWI routine is entered. This leaves the return address in the correct format to indicate that no errors occurred.

You cannot make this assumption for SWI routines that are vectored. This is because any of these routines might be called using the SWI OS_CallAVector, for which RISC OS does not clear the V bit.

Therefore, if you claim a vector and replace a SWI routine with one of your own, that routine must not assume the state of the V flag. Instead, you must explicitly clear the V flag if there was no error, or explicitly set it (and set up an error block) if there was an error.

This edition Copyright © 3QD Developments Ltd 2015
Last Edit: Tue,03 Nov 2015