www.riscos.com Technical Support: |
|
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.
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.
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.
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.
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.
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.
From BBC BASIC you can call a SWI routine in two different ways:
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
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
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, ®s, ®s); /* call SWI */
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.
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.
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:
For further details, see the chapter entitled Generating and handling errors.
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:
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.
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.
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.
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.
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.
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:
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.
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.
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:
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 name and number | |
| Summary | |
Entry and exit conditions for SWI routine | ||
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 | ||
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). | ||
Full description of the use of this SWI | ||
Closely related SWIs (if any) | ||
Closely related vectors (if any) | ||
Example SWI documentation |
There are some important points to note if you are writing your own SWI routines. These apply if:
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:
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.
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.