Search code examples
assemblyarmmachine-codethumbaddressing-mode

How is "!" part of "LDM R1! {R2}" instruction in Armv6-M Architecture Encoded?


When I look at the official Arm specifications,

! 
Causes the instruction to write a modified value back to <Rn> . If ! is omitted, the instruction does not change <Rn> in this way.

This is how the function is explained, yet the ASL Code is as follows,

if ConditionPassed() then
  n = UInt(Rn); registers = '00000000':register_list;
  if BitCount(registers) < 1 then UNPREDICTABLE;
  wback = (registers<n> == '0');
  address = R[n];
  for i = 0 to 7
    if registers<i> == '1' then
      R[i] = MemA[address,4];
      address = address + 4;
if wback && registers<n> == '0' then R[n] = R[n] + 4*BitCount(registers);

So the only logical thing would be to use wback for the purpose, yet it doesn't seem to add up when I examine the general structure.


Solution

  • It is documented in the arm documentation.

    Initially it looks strange if you are used to the w bit and so that might lead you to check the armv7-m, armv7-ar, so of course go back to the original arm arm

    ! Causes base register writeback, and is not optional.
    

    You can also try it and see what an assembler thinks of it

    .cpu cortex-m0
    .thumb
    ldm r1,{r2}
    ldm r1!,{r2}
    
    arm-none-eabi-as so.s -o so.o
    so.s: Assembler messages:
    so.s:4: Warning: this instruction will write back the base register
    

    line 4 being the one without the !

    Disassembly of section .text:
    
    00000000 <.text>:
       0:   c904        ldmia   r1!, {r2}
       2:   c904        ldmia   r1!, {r2}
    

    Upon further inspection it is not a bug in the documentation though

    LDM <Rn>,<registers> <Rn> included in <registers>
    

    so test that

    .cpu cortex-m0
    .thumb
    ldm r1,{r1}
    ldm r1!,{r1}
    so.s: Assembler messages:
    so.s:5: Warning: this instruction will not write back the base register
    

    line 5 being the one with the !

    Disassembly of section .text:
    
    00000000 <.text>:
       0:   c902        ldmia   r1, {r1}
       2:   c902        ldmia   r1, {r1}
    

    Which is also stated in the psuedocode from the arm documentation:

    if wback && registers<n> == ‘0’ then R[n] = R[n] + 4*BitCount(registers);
    

    so this is not encoded as a bit as in the larger instructions, this is implied based on the presence of rn in the register list.

    Now go back to the original arm arm

    Operand restrictions
    If the base register <Rn> is specified in <registers>, the final value of <Rn> is the loaded value (not the written-back value).
    

    so even in the original it is not written back. and if you test it gnu assembler knows this as well with similar messages

    .cpu arm7tdmi
    .thumb
    ldm r1,{r1}
    ldm r1!,{r1}
    
    
    arm-none-eabi-as so.s -o so.o
    so.s: Assembler messages:
    so.s:5: Warning: this instruction will not write back the base register
    
    Disassembly of section .text:
    
    00000000 <.text>:
       0:   c902        ldmia   r1, {r1}
       2:   c902        ldmia   r1, {r1}
    

    This is perhaps an intentional thing in the armv4 days as they were trying to operate as an IP company and a number of the UNPREDICTABLE RESULTS, and other such things were actually predictable and were how they tested to see if you had stolen a core. It would not surprise me if the way this was written in the original arm arm docs was also something to catch folks making clones.