Sometimes it is necessary to write an interrupt service routine in assembly language. This is the case as well for the ARM Cortex-M0+ which is found in the KL25Z on my Freedom board. But there is something important about the ARM Cortex architecture: Thumb Mode.
Thumb mode the ‘ARM way’ to reduce the code size with a reduced (16bit wide) instruction set. The ARM architecture can implement a ‘mixed’ mode, on a function level. To distinguish between ‘normal’ ARM functions and ‘thumb’ functions, the processor is checking if the LSB (Least Significant Bit) of a function pointer (or function call destination) is set. So a jump address of 0x410 is for a ‘normal’ function, while a function jump to the address 0x411 (even if the function is located at the address 0x410) denotes a ‘thumb’ function.
Even with the ARM Cortex-M0+ implementing ‘thumb only’, it is using the same convention. So building an application for Cortex-M0+, it is important that this bit is set.
For the GNU ARM gcc compiler (and assembler) the tool chain is directed to use thumb mode with that option:
With that option set, the compiler tells the linker that this bit must be set for function pointer calls.
However, for assembly programs, a bit more is needed. Beside of passing the -mthumb option to the assembler as well, I need to use other directives to tell that a label is a function, and that this function is in thumb mode. A good summary of the different ARM assembler directives can be found here: http://microcross.com/GNU-ARM-Assy-Quick-Ref.pdf
But things are best shown with a simple example:
/* * asm_isr.s * * Created on: Nov 13, 2012 * Author: Erich Styger */ .global MyAsmISR /* external linkage for my ISR name */ .text /* put into linker code section */ .thumb_func /* we are a thumb function */ .type MyAsmISR, %function /* optional: mark it as a function */ MyAsmISR: push {r7,lr} /* push registers */ add r7,sp,#0 /* do something usefule here */ mov sp, r7 /* restore registers */ pop {r7, pc} .end /* end directive */
The magic directive is ‘.thumb_func’. Linking my interrupt routine shows that the LSB is set, and properly pointing to the interrupt routines:
Happy Arming 🙂