How can ARM's MOV instruction work with a large number as the second operand?
Remember that the ARM can perform a certain set of manipulations on the immediate value as part of the barrel shifter that is incorporated into the ARM's opcodes.
This little article has one of the clearest explanations of some of the tricks that an ARM assembler can use to fit a large immediate number into the small available space of an ARM instruction:
- http://www.davespace.co.uk/arm/introduction-to-arm/immediates.html
The article discusses the trick likely used in your specific example of generating a MVN opcode to load the bitwise complement of the immediate value.
These kinds of manipulation can't be done with all immediate values, but the ARM assemblers are supposedly pretty smart about it (and C compilers certainly are). If no shift/complement tricks can be performed, the value will generally be loaded from a PC-relative location or maybe by 'building up' the value from several instructions.
A single ARM instruction can only encode an immediate constant that can be represented as an 8-bit immediate value, shifted by any even power of two.
However, there is also a MVN
instruction, which is like MOV
but inverts all the bits. So, while MOV R2, #0xFFFFFFFF
cannot be encoded as a MOV
instruction, it can be encoded as MVN R2, #0
. The assembler may well perform this conversion for you.
MOV instruction can either accept imm16 value or Operator2 value (due to instruction length opposed to memory alignment), which must conform any of the following rules (copied from CortexM instruction set manual, X and Y is any hex-value):
- Any constant that can be produced by shifting an 8-bit value left by any number of bits within a 32-bit word.
- Any constant of the form 0x00XY00XY .
- Any constant of the form 0xXY00XY00 .
- Any constant of the form 0xXYXYXYXY .
This is why 0xFFFFFFFF is accepted (conforms 4th rule).
If you wish to assemble your own 32 bit constant, you can use instruction MOVT, which writes into the upper half of a register.