What are offsets in Assembly and how do I use them?

2

In the code below, the person points a register at an address, I get that. Why is it that later on he didn't just load R3 into R1, why did it have to be offset by 0x14?

How did R1 move in a few lines of code and what made it do it? It's really confusing me and hours of searching has shown no clear answer.

The code is stolen from here.

http://mbed.org/forum/mbed/topic/3205/

my_asm

    ; setup a pointer to the base address of port 1
    LDR     R1,=0x2009c020      ; pointer to base of port1 

    LDR     R3,=0x00040000      ; set LED 1 port pin to output
    STR     R3,[R1,#0x00]       ; set direction bits (base+0x00)


loop
    LDR     R3,=0x00000000      ; set LED 1 off (all bits 'zero')
    STR     R3,[R1,#0x14]       ; set outputs directly (base+0x14)

    LDR     R3,=0x00040000      ; set LED 1 on (bit 18 aka P1.18 is 'one')
    STR     R3,[R1,#0x14]       ; set outputs directly (base+0x14)

    B       loop                ; branch to loop (endless loop!)

    ALIGN
    END
assembly
arm
asked on Stack Overflow Mar 26, 2014 by user124757

3 Answers

4

Offsets are used in assembler to access data structures.

In the code you are using, you can see the base address being loaded. This is the starting address of a data structure.

The data structure will be defined some place, but not in the code. The definition is often as a table, showing, for example

  • 4 bytes for this
  • 2 bytes for that
  • 4 bytes for something else
  • etc.

Modern processors use memory maps like this for input and output. Each device is "mapped" to a particular address. That's the base address in your code.

Without knowing what hardware is attached to your processor, we can't tell you what the data structure has in it.

answered on Stack Overflow Mar 26, 2014 by andy256
0

The reason is that it's the shortest (fewest instructions) way to accomplish the desired effect. Two things are being done: (1) configuring the port for output, and (2) toggling an output. Since the memory locations that need to be written to in order to perform these operations are nearby, it makes to use the processor's base/offset facilities to address both.

The same effect could be achieved with the following code, but for no benefit. It's larger (less likely to fit in fixed storage) and the loop is no faster (the processor still has to add zero to the new value in R1 when executing the STR instructions).

my_asm
    ; setup a pointer to the base address of port 1
    LDR     R1,=0x2009c020      ; pointer to base of port1 

    LDR     R3,=0x00040000      ; set LED 1 port pin to output
    STR     R3,[R1,#0x00]       ; set direction bits (base+0x00)

    LDR     R1,=0x2009c034      ; pointer to port1 outputs         <-- change R1

loop
    LDR     R3,=0x00000000      ; set LED 1 off (all bits 'zero')
    STR     R3,[R1,#0x00]       ; set outputs                      <-- no more offset

    LDR     R3,=0x00040000      ; set LED 1 on (bit 18 aka P1.18 is 'one')
    STR     R3,[R1,#0x00]       ; set outputs                      <-- no more offset

    B       loop                ; branch to loop (endless loop!)

    ALIGN
    END
answered on Stack Overflow Mar 26, 2014 by nobody
0

Register 1 is being loaded with the base address for Port 1 and the third instruction...

STR     R3, [R1, #0x00]       ; set direction bits (base + 0x00)

...suggests that the second byte of the data addressed by the contents of R1 are the direction bits for the LED on port 1.

You probably could, theoretically, load R1 to address the data at offset 0x014 after this instruction has completed but this would mean you include an extra - and unnecessary - instruction. Typically, if you're writing something in Assembly, you're trying to do it as efficiently as possible.

STR     R3, [R1, #0x14]       ; set outputs directly (base + 0x14)

What you can learn from the code above is the direction indicators are at offset 1 and the outputs are at byte 15 (I think) in the data relating to Port 1. Other than that, we don't have any information.

However, if you needed to access any of the other data relating to Port 1 later in the process, and had updated the base register to point at offset 0x14 past the start of the record for port 1, you'd have to load the address for that, or reload the base and get the offset particularly if it was somewhere between the base of the data for port 1 and offset 14.

It's more efficient, if you're doing with a specific record/piece of data, to use a base register and offset it rather than loading a register with the direct address in storage that you wish to update every time you need to do so.

answered on Stack Overflow Mar 26, 2014 by temptar • edited Aug 30, 2018 by Edwin Pratt

User contributions licensed under CC BY-SA 3.0