Sine Qua Nons of the ARM Controller - The Load-Store Model

Sine Qua Nons of the ARM Controller - The Load-Store Model

The blog is in continuation to Processor Funamentals. Programming in Assembly language helps developers understand everything that happens under the hood. This article is everything you need to get started with assembly language and builds on to explain in detail, the load and store instructions.

Data types in Assembly Language

Right off the bat, learning any new programming language starts with the different data types. Assembly language, being a low-level programming language, deals with three main data types,

  • Byte: 8 bits
  • Halfword: 16 bits
  • Word: 32 bits

These grant us access to every finicky detail of low-level data and memory handling.

ARM Thumb and Thumb-2 Instruction sets

Instructions are cogs used to instruct a Central Processing Unit(CPU). A group of these cogs is called the instruction set of a CPU. If you want to understand how they work internally, bump into the blog attached below. ARM instructions are 32 bits wide and operate on 32-bit data. Thumb instructions are a subset of ARM instructions and are 16-bit wide, yet they work on 32-bit data.

ARM instructions are representations of binary codes. For instance, the instructions, ADD internally could represent a 4-bit code like 1001. Analogous to this, ARM instructions are 32 bits wide and operate on 32-bit data.

For example,

  • ADD R0, R0, R2 - ARM Instruction Set: Add R0 and R2, and store the result in R0.
  • ADD R0, R2 - Thumb Instruction Set: Register R0 acts as the source and destination.

Bear in mind that both the instructions operate on 32-bit registers, however, "ADD" is 32 and 16 bits in ARM and Thumb IS, respectively. The Thumb instruction trades better code density for performance. Thumb-2 is a further subset that entails both 16-bit and 32-bit instruction and strikes the right balance between both code density and performance. Processor cores like Cortex-M3 and Cortex-M4 can execute only Thumb-2 Instructions.

Load-Store Instructions

RISC architectures are LOAD STORE architectures because the operations can be performed only on the registers and not explicitly on the memory.

  • Load(LDR Instruction): Write a value from the memory to a register.
  • Store(STR Instruction): Write a value from a register to the memory.

Syntaxes:

ldr{size}{condition} <Rd>, <AddressingMode>
str{size}{condition} <Rd>, <AddressingMode>

{size} and {condition} are optional and add to the versatility of the instructions. Rd is the destination and source register, respectively. Addressing modes can take various forms to form different nuances of the instruction.

The simplest form of LDR and STR instructions could be,

ldr    r0, =1     ;Load register with value 1
str    r0,[r1]    ;Store register r0s value in the memory address stored in the register, r1

All registers can be used for load and store instructions. Altering the R15 register (Program counter) causes the program flow to change.

Load and Store - Sizes

ldr and str, used without the "size parameter" simply transfer a word.

image.png

For half-word loads, the data gets placed in the Least Significant Half-word of a register; the upper half-word bits get asserted with 0s.

Consider the memory and register R0 as

image.png

R0, post LDR operation

image.png

LDRSH and LDRSB are used to transfer signed data(2s complement form). These instructions, when plied, the loaded byte/half-word is passed into the sign-extend block where the data is sign-extended.

image.png

Load and Store - Addressing

The addresses for the LDR and STR instructions appear within square brackets. The tedious process of updating the addresses after load and store operations can be mitigated by using the various addressing modes.

  • Offset addressing mode

An offset is applied to the address pointed by the register. For instance, if address 0x20000000 is stored in register R0, [R0,#4] will refer to 0x20000004.

;this program demonstrates offset addressing mode        
        area    prgm, code, readonly
        entry
        export    __main

__main 
        ldr     r0, =0x20000000       ;load register with address 0x20000000
        mov     r1, #1                ;store value 0x01 in register r1
        str     r1,[r0,#4]            ;store r1s value(0x01) in memory location 0x20000000+4 = 0x200000004

stop    b       stop
        align
        end

image.png

  • Pre-indexed addressing mode

This mode updates the address register with the increment/decrement before the load/store. The syntax includes an exclamation mark following the square bracket. Assuming the register R0 has 0x2000000 and a load/store operation performed in this mode([R0,#4]!) will pre-update the register R0 with 0x20000004.

;this program demonstrates pre-index addressing mode        
        area    prgm, code, readonly
        entry
        export    __main

__main 
        ldr     r0, =0x20000000       ;load register with address 0x20000000
        mov     r1, #1                ;store value 0x01 in register r1
        str     r1,[r0,#4]!           ;store r1s value(0x01) in memory location 0x20000000+4 = 0x200000004
                                      ;r0 = r0+4

stop     b       stop
        align
        end

image.png

  • Post-indexed addressing mode

The address is loaded from/stored into the address contained in the base register, followed by its update. The address register(R0) having 0x20000000 will be used for the load/store operation and will be post-updated().

;this program demonstrates post-index addressing mode        
        area    prgm, code, readonly
        entry
        export    __main

__main 
        ldr     r0, =0x20000000       ;load register with address 0x20000000
        mov     r1, #1                ;store value 0x01 in register r1
        str     r1,[r0],#4            ;store R1s value(0x01) in memory location 0x20000000
                                      ;r0 = r0+4

stop     b         stop
        align
        end

image.png

Another nuance that can save us big time is using bit shift-based multiplication.

image.png

;this program demonstrates post-index addressing mode        
        area    prgm, code, readonly
        entry
        export    __main

__main 
        ldr     r0, =0x20000000       ;load register with address 0x20000000
        mov     r1, #1                ;store value 0x01 in register r1
        mov     r2, #2
        str     r1,[r0,r2,lsl #1]     ;store R1s value(0x01) in memory location, 0x2000000+(2*2)

stop     b         stop
        align
        end

image.png

Endianess

Endianness is how data bytes are stored in the computer memory. It can be of two types,

  • Big Endian as in Big-End stores the byte with the highest place value first. image.png
  • Little Endian as in Little-End stores the byte with the lowest place value first. This is the default memory format for ARM processors. image.png

With everything we've learned, let's try writing a program to swap register content.

EOR is used to perform the Logical Exclusive OR operation on registers.

;this program swaps register content 

        area    swapReg, code, readonly
        entry 
        export    __main

__main
        ldr        r0, =0xABCAABCA
        ldr         r1, =0xDEFDDEFD

        eor        r0,r0,r1            ;r0 = r0^r1
        eor        r1,r0,r1            ;r1 = r0^r1
        eor        r0,r0,r1            ;r0 = r0^r1

stop    b         stop
        align
        end

image.png

References