Direct memory manipulation is assembly's superpower. Today covers addressing modes, the stack vs. heap, and how to call C library functions from assembly for more complex I/O.
x86-64 supports rich memory addressing: [addr] (direct), [reg] (register indirect), [reg + disp] (base + displacement for struct fields), [base + index*scale + disp] (full form for array indexing). Example: 'mov rax, [rbp-8]' loads a local variable from the stack frame. 'mov rax, [rsi + rcx*8]' loads the RCX-th element of a 64-bit array at RSI.
At function entry, RSP points to the return address. After 'push rbp / mov rbp, rsp', the frame is set up. Local variables live at [rbp-8], [rbp-16], etc. Function arguments passed on the stack (7th arg and beyond) live at [rbp+16], [rbp+24], etc. The red zone: Linux x86-64 guarantees 128 bytes below RSP won't be clobbered by signal handlers, so leaf functions can use [rsp-8] through [rsp-128] without adjusting RSP.
Linking with libc lets you call printf, malloc, fopen, etc. from assembly. Declare them extern, use CALL, follow the ABI (args in registers), and ensure RSP is 16-byte aligned before the call (the ABI requires this). Link with 'ld hello.o -lc -dynamic-linker /lib64/ld-linux-x86-64.so.2' or compile with gcc to handle linking automatically.
; array_sum.asm: sum an integer array
extern printf
section .data
arr dq 10, 20, 30, 40, 50 ; 5 x 64-bit integers
arrlen equ 5
fmt db 'Sum: %ld', 0x0a, 0
section .text
global main
main:
push rbp
mov rbp, rsp
lea rsi, [rel arr] ; pointer to array
xor rax, rax ; sum = 0
xor rcx, rcx ; index = 0
.loop:
add rax, [rsi + rcx*8] ; sum += arr[i]
inc rcx
cmp rcx, arrlen
jl .loop
; printf(fmt, sum)
lea rdi, [rel fmt]
mov rsi, rax
xor eax, eax ; 0 FP args in XMM regs
call printf
xor eax, eax
pop rbp
ret
; gcc -no-pie array_sum.asm -o array_sum
Implement a complete string library in assembly: strlen, strcpy, strcmp, and strrev. Write a test harness that exercises all four functions and prints pass/fail for each test case.