ARM and Thumb Instruction Set

Recently, I've been asked about the code in "TinyInjector". The code for calling fuctions in remote process made somebody confused. Let's take a look at the code.

  regs.ARM_pc = function_addr;
  if (regs.ARM_pc & 1) {
    // thumb
    regs.ARM_pc &= (~1u);
    regs.ARM_cpsr |= CPSR_T_MASK;
  } else {
    // arm
    regs.ARM_cpsr &= ~CPSR_T_MASK;

The code is taken from the fuction "CallRemoteFuction". The purpose of it is to mask the CPSR and PC register according to the instruction set. It's complicated to explain every detail of ARM and Thumb instruction set. I'll just cover the main points of it, but that's enough to understand the code above.

Thumb Instruction Set

RISC processors execute instruction in a single clock cycle, the execution is faster on RISC processors than on a CISC processor with the same clock speed. However, the same program requires more memory on RISC processor. Many operations which can be accomplished by a single instruction on CISC processor requires three or more instructions on RISC processor. Memory is more critical than the execution speed for most embedded devices. To reduce the memory cost, ARM introduced the Thumb instruction set.
One instruction in ARM instruction set is 32-bit, while in Thumb instruction set, it's 16-bit, which makes Thumb instruction set more memory-efficient. The Thumb instruction set can be regarded as a shorthand for 32-bit ARM instructions. Most operations accomplished in one ARM instruction can be substituted by one or several Thumb instructions. There're some exceptions like code related to low-level device drivers and exception handler.


There's a special register CPSR, Current Program Status Register, which the thumb status bit lies(T). The bits in this register is allocated like below:

31 30 29 28 27 24 19 … 16 9 8 7 6 5 4 … 0
N Z C V Q J GE[3:0] E A I F T M[4:0]

The bit indicating Thumb state is bit 5, value 0 for ARM state and value 1 for Thumb state. So we defined the CPSR_T_MASK 1u << 5. Detail information about CPSR can be found at "ARM architecture".

Switch States

The usual method to switch to Thumb state is via the BX/BLX instructions. The usage of these instructions is not part of this post. The CPU will examine the least significant bit(also known as LSB) of the target address to determine the instruction state when executing branch instructions. We have mentioned above, all instructions in ARM processor are 32-bit or 16-bit aligned, so the LSB can be used as an indicator of instruction set. If the LSB is 1, the processor switches to Thumb state, and the actual target address is (*(PC) - 1), as the processor plus 1 to indicate the Thumb instruction state. Now the code above is not hard to understand.