Last active
July 21, 2018 09:38
-
-
Save Todd-Davies/458b6ea463af2f182773 to your computer and use it in GitHub Desktop.
ARM Addressing modes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; Addressing modes | |
; ================ | |
; Here we write a program to add up the numbers in a table. | |
; We do it in four different ways to illustrate different methods of addressing | |
; in ARM Assembly. | |
; To compile/run, open Komodo with the command: | |
; start_komodo 15111 | |
; Then open, compile and run this file as normal. | |
; Expected output: | |
; Base + offset: 10 | |
; Register indirect: 10 | |
; Pre-indexed: 10 | |
; Base + offset with offset in register: 10 | |
; Post indexed: 10 | |
; Branch to the start so we don't try and execute the data we define below | |
B start | |
; Define all the data we need | |
msgPre DEFB "Base + offset: ", 0 | |
ALIGN | |
msgRegIn DEFB "Register indirect: ", 0 | |
ALIGN | |
msgAuto DEFB "Pre-indexed: ", 0 | |
ALIGN | |
msgReg DEFB "Base + offset with offset in register: ", 0 | |
ALIGN | |
msgPost DEFB "Post indexed: ", 0 | |
ALIGN | |
newline DEFB "\n", 0 | |
ALIGN | |
; This is the data we're working on. N.b. 1 + 2 + 3 + 4 = 10 | |
table DEFB 1, 2, 3, 4; | |
tableEnd ; | |
; N.b. we can compute how many bytes there | |
; are in the table like so: | |
; #(tableEnd - table) | |
start | |
; ---------------------------------------- | |
; Naive way - Base + offset addressing | |
; The first load (LDRB R2, [R1]) uses register indirect addressing, but the | |
; other three use pre-indexed register indirect addressing (LDR R2, [R1, #X]), | |
; where the value of R1 is not updated. | |
; ---------------------------------------- | |
; Print the message | |
ADR R0, msgPre; | |
SVC 3; | |
; Clear R0 since we'll keep the sum in there | |
MOV R0, #0 | |
ADR R1, table; | |
LDRB R2, [R1]; | |
ADD R0, R0, R2; | |
LDRB R2, [R1, #1]; | |
ADD R0, R0, R2; | |
LDRB R2, [R1, #2]; | |
ADD R0, R0, R2; | |
LDRB R2, [R1, #3]; | |
ADD R0, R0, R2; | |
; Print the result | |
SVC 4; | |
ADR R0, newline; | |
SVC 3; | |
; ---------------------------------------- | |
; Naive way 2 - Base + register indirect addressing | |
; Use register indirect addressing and increment the register after every load | |
; ---------------------------------------- | |
; Print the message | |
ADR R0, msgRegIn; | |
SVC 3; | |
; Clear R0 since we'll keep the sum in there | |
MOV R0, #0 | |
ADR R1, table; | |
LDRB R2, [R1]; | |
ADD R1, R1, #1; | |
ADD R0, R0, R2; | |
LDRB R2, [R1]; | |
ADD R1, R1, #1; | |
ADD R0, R0, R2; | |
LDRB R2, [R1]; | |
ADD R1, R1, #1; | |
ADD R0, R0, R2; | |
LDRB R2, [R1]; | |
ADD R0, R0, R2; | |
; Print the result | |
SVC 4; | |
ADR R0, newline; | |
SVC 3; | |
; ---------------------------------------- | |
; Naive way 3 - preindexed addressing | |
; The value of R1 is incremented by each (LDRB R2, [R1, #1]!) instruction | |
; before it executes. This is basically the same as the last one, but requires | |
; fewer instructions. | |
; ---------------------------------------- | |
; Print the result | |
ADR R0, msgAuto; | |
SVC 3; | |
; Clear R0 since we'll keep the sum in there | |
MOV R0, #0 | |
ADR R1, table; | |
LDRB R2, [R1]; | |
ADD R0, R0, R2; | |
LDRB R2, [R1, #1]!; | |
ADD R0, R0, R2; | |
LDRB R2, [R1, #1]!; | |
ADD R0, R0, R2; | |
LDRB R2, [R1, #1]!; | |
ADD R0, R0, R2; | |
; Print the result | |
SVC 4; | |
ADR R0, newline; | |
SVC 3; | |
; ---------------------------------------- | |
; External count register (Base + offset addressing, with offset in a register) | |
; Here, we keep a pointer to the start of the table (R1), then we increment an | |
; offset (R3) by one every time. | |
; ---------------------------------------- | |
; Print the result | |
ADR R0, msgReg; | |
SVC 3; | |
; Clear R0 since we'll keep the sum in there | |
MOV R0, #0 | |
MOV R3, #0 ; Keep a count | |
ADR R1, table ; Load a pointer to the table into R1 | |
loop LDRB R2, [R1, R3]; Load the value of memory at address R1 + R3 | |
ADD R3, R3, #1 ; Increment R3 | |
ADD R0, R0, R2 ; Add R2 to the sum | |
CMP R3, #(tableEnd - table); Have we reached the end of the table? | |
BLT loop ; If we have not, then loop around. | |
; Print the result | |
SVC 4; | |
ADR R0, newline; | |
SVC 3; | |
; ---------------------------------------- | |
; Increment pointer (post indexed addressing) | |
; We use one less register here (and one less ADD), since we increment R1 every | |
; time. Since we're using post indexed addressing, R1 is updated *after* each | |
; 'LDRB R2, [R1], #1' instruction | |
; ---------------------------------------- | |
; Print the result | |
ADR R0, msgPost; | |
SVC 3; | |
; Clear R0 since we'll keep the sum in there | |
MOV R0, #0 | |
ADR R1, table ; Load the address of the table | |
loop2 LDRB R2, [R1], #1 ; Load the byte at the address pointed to by R1. | |
; Then increment R1 | |
ADD R0, R0, R2 ; Add R2 to the sum | |
CMP R1, #tableEnd; See if we've reached the end of the table | |
BLT loop2 ; Loop if we've not | |
; Print the result | |
SVC 4; | |
ADR R0, newline; | |
SVC 3; | |
; Stop | |
SVC 2; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Minimal runnable address modes example on QEMU user mode with assertions: https://github.com/cirosantilli/arm-assembly-cheat/blob/e4acee1d9ce86319142e50c0a407ca4db815536d/v7/address_modes.S