ARM Technical Support Knowledge Articles

Using the Inline Assembler

Applies to: ARM Developer Suite (ADS), DS-5, RealView Developer Kit (RVDK) for OKI, RealView Developer Kit (RVDK) for ST, RealView Developer Kit for XScale (RVXDK), RealView Development Suite (RVDS)

Answer

The ARM compilers feature a built-in 'inline' assembler. The inline assembler built into the ARM compilers is a 'high-level' assembler, allowing you to use most ARM assembly language instructions in a C or C++ program. You can use the inline assembler to:

The inline assembler offers very flexible interworking with C and C++. Any register operand can be an arbitrary C or C++ expression. However, it is not intended to be a 'WYSIWYG' assembler (where the object code produced is identical to the written source) - that is the role of armasm.

For detailed information see the corresponding compiler guides. Note that the inline assembler appears as deprecated in RVCT versions 3.1, 4.0 and 4.1 and warnings will be shown when using the inline assembler with these tools. However, it is no longer deprecated in the ARM Compiler Tools version 5.0 and onward; therefore, there is no need to avoid use of the inline assembler at this time.

The following shows an example that enables/disables interrupts by reading from and writing to the CPSR. Three versions are shown. Version 1 is deliberately incorrect. The problems are corrected in Version 2. Finally, Version 3 achieves a more elegant and efficient solution.

Example - Version 1

The following example, which enables/disables interrupts, illustrates some pit-falls.

/* NewState=1 will enable IRQ, NewState=0 will disable IRQ 
   ARM core must be in a privileged mode, e.g. supervisor  */

void ChangeIRQ(unsigned char NewState)
{
  NewState=(~NewState)<<7; /* invert and shift to bit 7 */
  __asm                    /* invoke the inline assembler */
  {
    /* This code is deliberately incorrect - it is given for illustration only */
    STMDB SP!, {R1}        /* save work register */
    MRS R1, CPSR           /* get current program status */
    BIC R1, R1, #0x80      /* clear IRQ disable bit flag */
    ORR R1, R1, R0         /* OR with new value (variable NewState is in R0) */
    MSR CPSR, R1           /* store updated program status */
    LDMIA SP!,{R1}         /* restore work register */
  }
}

As a high-level assembler, the low-level capabilities of the inline assembler are restricted. For example:

You cannot modify the program counter or stack pointer. The example code above fails because it tries to stack and restore R1. This is not necessary, because the compiler will stack/restore any working registers as required automatically. It is not allowed to explicitly stack/restore work registers.

You cannot read a register without writing to it first, because this results in an uninitialized variable. The example code above reads R1 to put it onto the stack, but R1 has not been initialized (it is undefined), so an error will be reported.

This is all very similar to C, but is rather different to a normal assembler! For a more comprehensive list of restrictions, please refer to the documentation.

Example - Version 2

Fortunately, there is an easy solution to the above problems. Instead of trying to stack work registers on entry to an __asm block, create C variables to hold working data. The compiler's register allocator will then select the most appropriate registers to use, and the inline assembler will perform any stacking which is needed automatically. Remember that the inline assembler can only handle integer assignable types because ARM registers can only hold integers!

The code above has been re-written below to use C variables instead of registers.

/* NewState=1 will enable IRQ, NewState=0 will disable IRQ 
   ARM core must be in a privileged mode, e.g. supervisor  */

void ChangeIRQ(unsigned int NewState)
{
    int my_cpsr;                       /* to be used by inline assembler */
    NewState=(~NewState)<<7;           /* invert and shift to bit 7 */
    __asm                              /* invoke the inline assembler */
    {
      MRS my_cpsr, CPSR                /* get current program status */
      BIC my_cpsr, my_cpsr, #0x80      /* clear IRQ disable bit flag */
      ORR my_cpsr, my_cpsr, NewState   /* OR with new value */
      MSR CPSR_c, my_cpsr              /* store updated program status */
    }
}

This code compiles to:

ChangeIRQ
      MVN  r0,r0
      MOV  r0,r0,lsl #7
      MRS  r1,cpsr
      BIC  r1,r1,#0x80
      ORR  r0,r1,r0
      MSR  cpsr_c,r0
      MOV  pc,r14

Note that CPSR_c is used instead of CPSR in the MSR instruction, to avoid altering the condition code flags.

Example - Version 3

The most efficient implementation is:

/* NewState=1 will enable IRQ, NewState=0 will disable IRQ 
   ARM core must be in a privileged mode, e.g. supervisor  */

void ChangeIRQ(unsigned int NewState)
{
  int my_cpsr;
  __asm
  {
    MRS my_cpsr, CPSR                        /* get current program status */
    ORR my_cpsr, my_cpsr, #0x80              /* set IRQ disable bit flag */
    BIC my_cpsr, my_cpsr, NewState, LSL #7   /* reset IRQ bit with new value */
    MSR CPSR_c, my_cpsr                      /* store updated program status */
  }
}

This code compiles to:

ChangeIRQ
    MRS  r1,CPSR
    ORR  r1,r1,#0x80
    BIC  r0,r1,r0,LSL #7
    MSR  CPSR_c,r0
    MOV  pc,r14

In this version we save two move instructions over the previous implementation.

Other issues

Note that you must avoid using C variables with the same names as physical registers (e.g. r0r1, etc) or ARM Procedure Call Standard (APCS) named registers (e.g. ipsl, etc). If you try to access such a variable in an __asm block, the physical register will be accessed instead of the C variable, which may give unexpected results. For example, avoid the use of:

int fn(int v)
{
  int ip;                    /* avoid C variable names like this! */
  __asm { add ip, v, #1; }   /* the physical register ip is used here, */
                             /* not the C variable ip */
  return ip;
}

Article last edited on: 2011-09-27 22:10:14

Rate this article

[Bad]
|
|
[Good]
Disagree? Move your mouse over the bar and click

Did you find this article helpful? Yes No

How can we improve this article?

Link to this article
Copyright © 2011 ARM Limited. All rights reserved. External (Open), Non-Confidential