上节介绍了使用栈调用函数,call指令调用的时候,将当前的IP值压入栈。函数返回的时候,将栈顶的元素pop到IP中,由于默认使用_cdecl调用,因此需要调用者平衡栈。
这节介绍基本的运算:加减乘除。nasm使用Intel汇编指令,可以通过查阅Intel官方文档,这里是Intel指令官方文档:
代码test8.asm如下:
; test8.asm
; nasm -f elf64 -o test8.o test8.asm
; gcc -o test8 test8.o
; ./test8
extern printf
section .data
fmt: db '%ld',0xa,0
section .text
global main
main:
push rbp
; add
mov rax, 8
mov rbx, 123
add rax, rbx
; print result
mov rdi, fmt
mov rsi, rax
mov rax, 0
call printf
; sub
mov rax, 12
sub rax, 3
; print result
mov rdi, fmt
mov rsi, rax
mov rax, 0
call printf
; mul
mov rax, 3
mov rbx, 10
mul rbx
; print result
mov rdi, fmt
mov rsi, rax
mov rax, 0
call printf
; div
mov rdx, 0
mov rax, 10
mov rbx, 3
div rbx
mov rdx, rcx
; print rax
mov rdi, fmt
mov rsi, rax
mov rax, 0
call printf
; print rdx
mov rdi, fmt
mov rsi, rcx
mov rax, 0
call printf
pop rbp
; exit
mov rax, 60
syscall
ret
这段代码虽然比较长,但还算比较简单。add,sub,mul,div都是无符号运算。
add和sub比较好理解,主要介绍mul和div指令。
mov rax, 3
mov rbx, 10
mul rbx
rax存放一个乘数,mul 后面跟一个乘数,结果存放到rdx:rax中。这样的规则可以应用到32位乘法,16位乘法,以及8位的乘法,除法运算也如此。
mov rdx, 0
mov rax, 10
mov rbx, 3
div rbx
在div指令中,被除数存放到rdx:rax中,除数跟在div指令后面,除法的结果是,rax存放商,rdx存放余数。
乘法指令mul占用的CPU周期较多,编译器在优化代码时,会使用左移和加法来执行乘法操作,使用右移和减法来执行除法运算。
执行div和mul后,需要及时处理结果,把结果存放到内存中,否则这些寄存器中的值可能会随着其他代码或函数的执行被修改,而丢失运算结果。