"Assembly Language Programming with ARM – Full Tutorial for Beginners"

siiky

2023/05/04

2023/05/04

2023/05/12

video,course,programming

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:

Common instructions:

.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

<C-X><C-O> will jump between the GDB REPL and the other "windows".