*
*Home|Chinese|Japanese*About ARM|Forums|Events|News|Employment|Contact Us|Investors*
dotted rule
*ARM - the architecture for the digital worldARM - the architecture for the digital world
search
*
*
***
*MARKETS:PRODUCTS & SOLUTIONS:CONNECTED COMMUNITY:TECHNICAL SUPPORT:DOCUMENTATION*
*
technical support
*
*
****
*.Technical Support
*
*
*>>Home Page*
*
*.Obtaining Support*
*
*.FAQs*
*
**Development Tool FAQs*
**IP FAQs*
**Embedded Software FAQs*
**Artisan Physical IP FAQs (Login Required)*
*
*.Downloads*
*
*.Documentation*
*
*.Training*
*
*.Where To Buy*
*
*.Keil MCU Tools*
*
*.What's New*
*
*.ARM Newsgroups*
*
*
*
technical support FAQsask ARM*
*

Technical Support Search
*     (Advanced Search)
  FAQs   Documentation   Downloads   Forums

*

 
downarrowUsing the Inline Assembler
Applies to: ARM Developer Suite (ADS), Compilers, RealView Developer Kit (RVDK) for OKI , RealView Developer Kit (RVDK) for ST, RealView Developer Kit for XScale (RVXDK), RealView Developer Suite (RVDS) 2.0, RealView Developer Suite (RVDS) 2.1, RealView Developer Suite (RVDS) 2.2

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:

  • Use features of the target processor that cannot be accessed from C, for example the Program Status Register (PSR)
  • Achieve more efficient code.

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 ADS 1.1/1.2 Developer Guide, Section 4.1, "Using the inline assemblers", or the RVCT 2.x Compiler and Libraries Guide, Section 4.

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 */< /FONT>
        __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;
}





back to top

*
**
*4 dots*Other ARM Websites | Help with Accessibility
*
shadow *LEGAL STATEMENTshadow