This recipe shows how the ARM C Compiler can be used to generate in-line SWIs directly from C.
The ARM instruction set provides the Software Interrupt (SWI) instruction to call Operating System routines. It is useful to be able to generate such operating system calls from C without having to call hand crafted ARM Assembly Language to provide an interface between C and the SWI.
The ARM C Compiler provides a mechanism which allows many SWIs to be called efficiently from C. SWIs which conform to the following rules can be compiled in-line, without additional calling overhead:
The following sections demonstrate how to use the in-line SWI facility of armcc for a variety of different SWIs which conform to these rules. These SWIs are taken from the ARM Debug Monitor interface, .
In the examples below, the following options are used with armcc:
-------------------------------------------------------- Option |Use -------------------------------------------------------- -li |Specifies that the the target is a little |endian ARM. -------------------------------------------------------- -apcs 3/32bit |Specifies that the 32 bit variant of APCS |3 should be used. --------------------------------------------------------
For example: SWI_WriteC, which we want to be SWI number 0.
This SWI is intended to write a byte to the debugging channel. The byte to be written is passed in r0.
The following C code, intended to write a Carriage Return / Line Feed sequence to the debugging channel, can be found in the examples directory as newline.c:
void __swi(0) SWI_WriteC(int ch); void output_newline(void) { SWI_WriteC(13); SWI_WriteC(10); }
Look carefully at the declaration of SWI_WriteC. __swi(0) is the way in which the SWI_WriteC 'function' is declared to be in-line SWI number 0.
This code can be compiled to produce ARM Assembly Language source using:
armcc -S -li -apcs 3/32bit newline.c -o newline.s
The code produced for the output_newline function is:
output_newline MOV a1,#&d SWI &0 MOV a1,#&a SWI &0 MOV pc,lr
Consider SWI_ReadC, which we want to be SWI number 4.
This SWI is intended to read a byte from the debug channel, returning it in r0.
The following C code, a naive read a line routine, can be found in the examples directory as readline.c:
char __swi(4) SWI_ReadC(void); void readline(char *buffer) { char ch; do { *buffer++=ch=SWI_ReadC(); } while (ch!=13); *buffer=0; }
Again, the way in which SWI_ReadC is declared should be noted: it is a function which takes no arguments and returns a char, and is implemented as in-line SWI number 4.
This code can be compiled to produce ARM Assembler source using:
armcc -S -li -apcs 3/32bit readline.c -o readline.s
The code produced for the readline function is:
readline STMDB sp!,{lr} MOV lr,a1 |L000008.J4.readline| SWI &4 STRB a1,[lr],#1 CMP a1,#&d BNE |L000008.J4.readline| MOV a1,#0 STRB a1,[lr,#0] LDMIA sp!,{pc}
If a SWI returns two, three or four results then its declaration must specify that it is a struct-valued SWI, and the special keyword __value_in_regs must also be used. This is because a struct valued function is usually treated much as if it were a void function with a pointer to where to return the struct as the first argument. See Passing and returning structs for more details.
As an example consider SWI_InstallHandler, which we want to be SWI number 0x70.
On entry r0 contains the exception number, r1 contains the workspace pointer, r2 contains the address of the handler.
On exit r0 is undefined, r2 contains the address of the previous handler and r1 the previous handler's workspace pointer.
The following C code fragment demonstrates how this SWI could be declared and used in C:
typedef struct SWI_InstallHandler_struct { unsigned exception; unsigned workspace; unsigned handler; } SWI_InstallHandler_block; SWI_InstallHandler_block __value_in_regs __swi(0x70) SWI_InstallHandler (unsigned r0, unsigned r1, unsigned r2); void InstallHandler(SWI_InstallHandler_block *regs_in, SWI_InstallHandler_block *regs_out) { *regs_out=SWI_InstallHandler(regs_in->exception, regs_in->workspace, regs_in->handler); }
This code is provided in the examples directory as installh.c, and can be compiled to produce ARM Assembler source using:
armcc -S -li -apcs 3/32bit installh.c -o installh.s
The code which armcc produces is:
InstallHandler STMDB sp!,{lr} MOV lr,a2 LDMIA a1,{a1-a3} SWI &70 STMIA lr,{a1-a3} LDMIA sp!,{pc}
If a SWI is to be called, but the number of the SWI is not known until run time, then the mechanisms discussed above are not appropriate.
This situation might occur when there are a number of related operations which can be performed on a object, and these various operations are implemented by SWIs with different numbers.
There are several ways to deal with this, including:
A mechanism has been added to armcc to support the second method outlined here. The operation is specified by a value which is passed in r12 (ip). The arguments to the 'generic' SWI are as usual passed in registers r0-r3, and values may optionally be returned in r0-r3 using the mechanisms described above. The operation number passed in r12 may well be the number of the SWI to be called by the 'generic' SWI, but it need not be.
Here is an C fragment which uses a 'generic', or 'indirect' SWI:
unsigned __swi_indirect(0x80) SWI_ManipulateObject(unsigned operationNumber, unsigned object, unsigned parameter); unsigned DoSelectedManipulation(unsigned object, unsigned parameter, unsigned operation) { return SWI_ManipulateObject(operation, object, parameter); }
This code is provided in the examples directory as swimanip.c, and can be compiled to produce ARM Assembler source using:
armcc -S -li -apcs 3/32bit swimanip.c -o swimanip.s
The code which armcc produces is:
DoSelectedManipulation MOV ip,a3 SWI &80 MOV pc,lr