SWI
Instruction | SWI |
---|---|
Function | SoftWare Interrupt |
Category | Interrupt |
ARM family | All |
Notes | - |
Contents |
SWI : Software Interrupt
SWI calls a software interrupt. This is akin to INT
on the x86. The 6502 has no equivalent.
The SWI value field is an immediate 24 bit value, permitting SWIs from &0 to &FFFFFF.
The SWI value is ignored by the processor. The SWI interrupt is taken, at which point the SWI handler code of the operating system should sort out the SWI and then take the appropriate action.
Upon calling a SWI, the return address is placed in R14. This won't be of much concern to you unless you are writing kernel level code, but be aware that calling SWIs will corrupt R14.
Syntax
SWI <number>
Function
Call operating system software interrupt handler
Example
SWI &12345 ; call SWI &12345
RISC OS specific
Calling SWIs by name in BASIC, such as:
SYS "Wimp_PollIdle", %1, hdl%, (time% + 100)
is very inefficient as the BASIC interpreter is required to translate the SWI name ("Wimp_PollIdle") into a number (&400E1) every time the SWI is called. It is best to use SWI names in your source code, for clarity, and 'compress' the code prior to release. There are numerous compressors available, most of them will convert SWIs to numbers. The following is the same:
SYS 262369, 1, hdl%, (time%+100)
Convention provides for SWIs to have an "X" form, which instructs the operating system to suppress any error that might have been generated, and instead return with the V bit set to indicate an error. This permits you to handle the error in your own way, which is usually going to be nicer than the usual OS method (namely "bang!").
To use this, simply prefix any SWI name with an X (XOS_Write0, XFileCore_DiscOp, XWimp_Poll, etc), or if calling by number, set bit 17 (OR with &20000).
Linux specific
For reasons that are explained below, later (EABI) versions of Linux have deprecated the use of SWI numbers, and instead recommend calling SWI &0 with the desired SWI in R7.
Technical
The instruction bit pattern is as follows:
31 - 28 | 27 - 24 | 23 - 0 |
---|---|---|
condition | 1 1 1 1 | 24 bit immediate |
Example SWI dispatch
Here is how the SWI dispatch works on RISC OS 5:
- Registers R10-R12 are preserved
- The SWI instruction is loaded into R11 using
LDR r11, [r14 #-4]
, using the fact that R14 is set to point to the instruction after the SWI call. - The upper byte is cleared, leaving the OS with a number to dispatch as necessary.
By passing the number in a register, none of the above is required. We can skip straight to the SWI dispatch. This is the method used by the Linux EABI kernel which specifies the SWI number in R7 (although, for compatibility, there is an option to support the older method as well).
Why the SWI number matters
The behaviour of the SWI call was logical on the ARM2 - with no cache it would just backtrack and load the SWI instruction. The behaviour was even more logical on the ARM3 - with a von Neuman architecture, the SWI instruction would already be present in the combined instruction/data cache, so the LDR would just pluck it from the cache.
However modern ARM processors use Harvard architecture with separate instruction and data caches. While the instruction will be in the instruction cache, there is no mechanism for loading it from there. Instead, it is read anew into the data cache, thus polluting the data cache with code.
For this reason, it is a more preferable action to provide the SWI number in a register, rather than specify them on the command line. This does, however, carry its own baggage - registers may need to be preserved, the desired register will need to be set up prior to calling the SWI.
There are about 178 defined Linux system calls [1]. Linux has switched to the "armel" release to call SWIs with the number passed in a register.
The RISC OS kernel offers several hundred system calls, most system modules offer between five and thirty SWIs [2] (<- the list linked is woefully incomplete and misses many things - printer support, various hardware, internationalisation, most filing systems...). There are about 80 such modules in a standard ROM release, not to mention a plethora of third-party modules. As such, RISC OS depends heavily on SWI numbers being included in the SWI instruction. To do it any other way would be unthinkable, and would likely break everything ever written for RISC OS.
What happens when a SWI is issued
When the processor encounters a SWI instruction, it takes the SWI vector as follows:
- Switch to SVC mode
- R14_svc is set to SWI instruction address + 4
- SPSR_svc is set to CPSR (not on legacy processors)
- IRQs are disabled
- Processor branches to address &8
Very simple SWI dispatch
The operating system may have a simple SWI handler for a core set of system services, or it may be like RISC OS and provide a complex and overwhelmingly powerful use of SWIs.
Here is an example of a very simple SWI dispatch routine to give you a feel for how it works. At address &8 is a branch to this routine:
.swi_handler STMFD sp!, {r12} LDR r12, [r14, #-4] ; load the SWI instruction BIC r12, r12, #&FF000000 ; keep only the SWI number CMP r12, #SWIcount ; SWI in range? LDRLE pc, [pc, r12, LSL #2] ; if so, load address from jumptable B swi_unknown ; else handle unknown SWIs DCD SwiHandler0 ; pointer to SWI &0 handler code DCD SwiHandler1 ; pointer to SWI &1 handler code DCD SwiHandler2 ; pointer to SWI &2 handler code DCD SwiHandler3 ; pointer to SWI &3 handler code ...etc... .swihandler0 ; code to handle SWI &0 ...do stuff... LDMFD sp!, {r12} ; restore previously saved R12 MOVS pc, r14 ; restore CPSR and PC to return from SWI