"Assembly Language Programming with ARM – Full Tutorial for Beginners"
siiky
2023/05/04
2023/05/04
2023/05/12
video,course,programming
=> https://www.freecodecamp.org/news/learn-assembly-language-programming-with-arm/ | https://yewtu.be/watch?v=gfmRrPjnEw4
Short-ish freecodecamp video about ARM assembly.
Notes
Registers R0 through R12, SP (Stack Pointer), LR (Link Register?), PC (Program Counter). R7 is used to specify syscalls. CPSR contains arithmetic flags (Negative, Carry, Zero, &c).
The different types of addressing:
- Immediate -- values are prefixed with # -- e.g. #42 for the decimal number 42, or #0xAB for the hexadecimal number AB.
- Register direct addressing -- the name of the register by itself.
- Direct -- labels are prefixed with = -- e.g. =somedata for the address of the label somedata.
- Indirect register addressing -- the register name is surrounded by square brackets -- e.g. [R3]; optionally an offset may be specified.
- Pre-increment -- like "register indirect addressing", but additionally everything is suffixed with ! -- e.g. [R2,#4]!
- Post-increment -- like "register indirect addressing", but the offset is specified outside -- e.g. [R2],#4
Common instructions:
- MOV DST, SRC -- move, duh
- SWI X -- Software Interrupt -- what's X?
- LDR REG, =ADDR -- load an address into a register
- ADD DST, SRC1, SRC2 -- DST = SRC1 + SRC2
- SUB DST, SRC1, SRC2 -- DST = SRC1 - SRC2
- MUL DST, SRC1, SRC2 -- DST = SRC1 * SRC2
- ADDS DST, SRC1, SRC2 -- like the operations above, but also set the right flags in the CPSR register
- SUBS DST, SRC1, SRC2 -- like the operations above, but also set the right flags in the CPSR register
- MULS DST, SRC1, SRC2 -- like the operations above, but also set the right flags in the CPSR register
- ADC DSR, SRC1, SRC2 -- like ADD, but also adds the carry from CPSR
- AND DST, SRC1, SRC2 -- binary AND, e.g. 0xFF & 0x16 = 0x16
- ORR DST, SRC1, SRC2 -- binary OR, e.g. 0xFF | 0x16 = 0xFF
- EOR DST, SRC1, SRC2 -- binary XOR, e.g. 0xFF ^ 0x16 = 0xE9
- MVN DST, SRC -- binary NOT, e.g. ~0xFF = 0xFFFFFF00 (negates the whole register, of course)
- LSL REG, #IM -- logical left shift
- LSR REG, #IM -- logical right shift
- ROR REG, #IM -- right rotation
- MOV DST, SRC, LSL #IM -- save in DST the result of shifting SRC left #IM
- MOV DST, SRC, LSR #IM -- save in DST the result of shifting SRC right #IM
- CMP REG1, REG2 -- compare the values of two registers by computing REG1-REG2
- BGT LBL -- jump if greater-than (Branch Greater-Than)
- BGE LBL -- jump if greater-or-equal (Branch Greater-or-Equal)
- BLT LBL -- jump if lesser-than (Branch Lesser-Than)
- BLE LBL -- jump if greater-or-equal (Branch Greater-or-Equal)
- BEQ LBL -- jump if equal (Branch EQual)
- BNE LBL -- jump if not equal (Branch Not Equal)
- BAL LBL -- jump (Branch ALways? Branch And Link?)
- B LBL -- Branch?
- ADDLT DST, SRC -- compute DST += SRC if the last comparison was lesser-than
- MOVEQ DST, SRC -- DST = SRC if the last comparison was equal
- BL LBL -- Branch Linked ("call"?)
- BX REG -- Branch eXit? Usually called on LR
- PUSH {REG, ...} -- push values onto the stack; e.g. PUSH {R0, R2, R3}
- POP {REG, ...} -- pop values off the stack; e.g. POP {R0, R2, R3}
- STR SRC, DST -- store the value of SRC in DST
.global _start
_start:
MOV R0,=somedata // Save in R0 the address of somedata
LDR R1,[R0] // Save in R1 the value stored at the address that's stored in R0 -- equivalent to R1 = *R0
LDR R2,[R0,#4] // Save in R2 the value stored at the address that's stored in R0, plus 4 bytes -- equivalent to R2 = *(R0+4)
LDR R3,[R0,#4]! // Save in R2 the value stored at the address that's stored in R0, plus 4 bytes -- equivalent to R3 = *(++R0) (assume ++ increments 4 bytes)
LDR R4,[R0],#4 // Save in R2 the value stored at the address that's stored in R0, plus 4 bytes -- equivalent to R4 = *(R0++) (assume ++ increments 4 bytes)
MOV R7,#1 // 1 is the exit syscall code
SWI 0
.data
somedata:
.word 4,2
Procedure calls
.global _start
_start:
MOV R0, #1
MOV R1, #3
PUSH {R0, R1}
BL get_value
POP {R0, R1}
B end
get_value:
MOV R0, #5
MOV R1, #7
ADD R2, R0, R1
BX LR
end:
First program
Actually wrote it before the video went into loops, with this exact example. The code is very different though.
.global _start
_start:
LDR R0, =list // array ptr
MOV R1, #1 // current index
LDR R2, [R0] // sum
loop:
LDR R3, [R0, #4]! // get value at current index and move array ptr forward
ADD R2, R2, R3 // add current value to sum
ADD R1, R1, #1 // increment index
CMP R1, #10
BLT loop // index < 10 ?
.data
list:
.word 1,2,3,4,5,6,7,8,9,10
Should be roughly equivalent to the following:
int main (void)
{
int list[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int idx = 1;
int sum = *list;
int r3;
do {
r3 = *(++list);
sum += r3;
idx += 1;
} while (idx < 10);
return 0;
}
First (runnable) program
.global _start
_start:
MOV R0, #1 // stdout
LDR R1, =message
LDR R2, =len
MOV R7, #4 // 4 is the write syscall code
SWI 0
MOV R7, #1 // 1 is the exit syscall code
SWI 0
.data
message:
.asciz "hello world"
len = .-message
Roughly equivalent to the following:
int main (void)
{
const char msg[] = "hello world\n";
write(1, msg, sizeof(msg));
return 0;
}
Constants
.global _start
.equ endlist, 0xaaaaaaaa
_start:
// ...
QEMU
At around 1h58min they show how to run an ARM VM in QEMU -- neat! Could be useful for other archs in the future.
Compiling and linking
$ as foo.s -o foo.o
$ ld foo.o -o foo
GDB
Starting at around 2h22min they show some useful GDB functionality I didn't know about.
# Show in a TUI the CPU instructions around the current PC
> layout asm
# Present in a TUI the register contents
> layout regs
will jump between the GDB REPL and the other "windows".
Proxy Information
- Original URL
- gemini://siiky.srht.site/wiki/v.freecodecamp.assembly_language_programming_arm.gmi
- Status Code
- Success (20)
- Meta
text/gemini
- Capsule Response Time
- 180.134967 milliseconds
- Gemini-to-HTML Time
- 1.016068 milliseconds
This content has been proxied by September (ba2dc).