4

I'm trying to set a register to 12000. Since MOV can't put a value larger than 255 in I figure 12000/2^8=375. But 375 is still too big and not divisible by 2. Is the only thing left to store 12000 in memory? Is it that much less efficient?

Out of curiosity why is it 255 and not 256 since it's unsigned?

  • Related: [immediate value encoding in ARM assembly](https://stackoverflow.com/q/69572040) explains the encoding used in ARM mode (8-bit encoding with 4-bit rotate count), which imposes this limitation. (Thumb mode is somewhat different). – Peter Cordes Jul 05 '22 at 07:45

4 Answers4

6

What ARM arch are you targeting? On ARMv7, there is a very nice solution -- the movw instruction, which takes a 16-bit immediate:

movw r0, #12000

Prior to ARMv7, you need to use two steps:

mov  r0,     #0x00002e00
orr  r0, r0, #0x000000e0

Note that expressible immediates aren't only eight bits wide; they are eight bits rotated by any even offset. Alternatively, you can simply load the value from memory instead of using an immediate.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
  • 1)Why does 2e00 fit? 2)How did you come up with 2e00? –  Nov 07 '11 at 02:22
  • @quest4knoledge: `0x2e00` fits because it's *eight bits* (`0x2e`) *rotated by an even offset* (8). I came up with it by printing 12000 in hex (0x2ee0), and just splitting it into two field that were both expressible. The exact split is unimportant; it could also be `0x2000` and `0x0ee0`, with no difference. – Stephen Canon Nov 07 '11 at 12:15
5

traditionally arm allows for 8 or 9 bits and a shift, so 12000 = 0x2EE0, as Stephen pointed out you can then do this:

mov r0, #0x2E00
orr r0, #0x00E0

Another way, with a quick shortcut is:

ldr r0,=0x2EE0

which means the assembler will find a place to put that value then do a pc relative load or you can do that yourself:

ldr r0,mynumber
...
mynumber: .word 12000

The newer extensions to the instruction set allow for more bits in the immediate. I normally use the

ldr r0,=0x2EE0

solution, and make sure I have unconditional branches, basically pools for the assembler to place variables. The compilers normally do the same thing, if they cant fit the immediate in a single instruction they tend to use a load pc relative rather than multiple immediate instructions.

old_timer
  • 69,149
  • 8
  • 89
  • 168
  • For the first solution how did you know to choose 0x2E00? Is it because it is the largest number that would fit into r0? It seems like a lot of guess and check. –  Nov 07 '11 at 06:05
  • It makes it easier to read if you break it along byte boundaries, if I had 0x1234 I could do 0x1000 and 0x234 but 0x12xx and 0xxx34 is easier to read and implement, 0x12345678 -> 0x12000000, 0x00340000, 0x00005600, 0x00000078. basically just break it into bytes. – old_timer Nov 07 '11 at 15:37
2

12 bits from 32 are assigned for the immediate value.

8bits making out the pattern, and the remaining 4 bits the 16 positions of the pattern :

0x000000ff
0x000003fc
0x00000ff0
0x00003fc0
0x0000ff00
0x0003fc00
0x000ff000
0x003fc000
0x00ff0000
0x03fc0000
0x0ff00000
0x3fc00000
0xff000000
0xfc000003
0xf000000f
0xc000003f

where the 8bit pattern is put onto the 8bits set.

Please note that the order above doesn't match the actual value of the 4 bits. You really don't have to know that kind of stuff either, unless you intend to write some self altering codes in runtime for whatever reason.

If you want to load ANY 32bit value into r0, use the pseudo instruction ldr with '='

ldr r0, =0x12345678

The assembler will then convert this into most efficient one(s) depending on the architecture at compile time.

martin
  • 1,185
  • 17
  • 22
Jake 'Alquimista' LEE
  • 6,197
  • 2
  • 17
  • 25
1

It is 255 because an 8 bit number in binary goes from 00000000-11111111. 11111111 is 255 in decimal. You can use XOR to swap values between registers instead of MOV. It tends to be faster.

Trevor Arjeski
  • 2,108
  • 1
  • 24
  • 40