In-Line SWIs


About this recipe

This recipe shows how the ARM C Compiler can be used to generate in-line SWIs directly from C.

Introduction

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.                        
--------------------------------------------------------

Using a SWI which returns no result

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

Using a SWI which returns one result

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}

Using a SWI which returns 2-4 results

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}

The SWI number is not known until run time

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

Related topics