cassemblyarmthumbrelative-addressing

Understanding Cortex-M assembly LDR with pc offset


I'm looking at the disassembly code for this piece of C code:

#define GPIO_PORTF_DATA_R       (*((volatile unsigned long *)0x400253FC))
int main(void){    
    // Initialization code
    while(1) {
        SW1 = GPIO_PORTF_DATA_R&0x10;  // Read PF4 into SW1
        // Other code
        SW2 = GPIO_PORTF_DATA_R&0x01;
    }
}

The assembly for that SW1= line is (sorry can't copy code):

https://i.sstatic.net/knsUg.jpg

Here are my questions:

I did a bit of research on SO and found out that PC actually points to the Next Next instruction to be executed.

When pc is used for reading there is an 8-byte offset in ARM mode and 4-byte offset in Thumb mode.

However 0x00000AB4 - 0x00000A56 = 0x5E = 94, neither does it match 92+8 or 92+4. Where did I get wrong?

Reference:

Strange behaviour of ldr [pc, #value]

Why does the ARM PC register point to the instruction after the next one to be executed?

LDR Rd,-Label vs LDR Rd,[PC+Offset]


Solution

  • You missed a key part of the rules for Thumb mode, quoted in one of the question you linked (Why does the ARM PC register point to the instruction after the next one to be executed?):

    For all other instructions that use labels, the value of the PC is the address of the current instruction plus 4 bytes, with bit[1] of the result cleared to 0 to make it word-aligned.

    (Thanks to Raymond Chen for spotting this; I had also missed it initially!)

    Also note that your debugger shows you a PC value when stopped at a breakpoint, but that's the address of the instruction you're stopped at. (Because that's how ARM exceptions work, I assume, saving the actual instruction to return to, not some offset.) During execution of the instruction, PC-relative stuff follows different rules. And the debugger doesn't "cook" this value to show what PC will be during its execution.

    The rule is not "relative to the end of this / start of next instruction". Answers and comments stating that rule happen to get the right answer in this case, but would get the wrong answer in other Thumb cases like in LDR Rd,-Label vs LDR Rd,[PC+Offset] where the PC-relative load instruction happens to start at a 4-byte aligned address so bit #1 of PC is already cleared.

    Your LDR is at address 0xA56 where bit #1 is set, so the rounding down has an effect. And your ldr instruction used a 2-byte encoding, not a Thumb2 32-bit instruction like you might need for a larger offset. Both of these things means round-down + 4 happens to be the address of the next instruction, rather than 2 instruction later or the middle of this instruction.