本篇介绍
本篇介绍汇编的整数,浮点运算,还有栈。
整数运算
代码如下:
; icalc.asm
extern printf
section .data
number1 dq 128 ; the numbers to be used to
number2 dq 19 ; show the arithmetic
neg_num dq -12 ; to show sign extension
fmt db "The numbers are %ld and %ld",10,0
fmtint db "%s %ld",10,0
sumi db "The sum is",0
difi db "The difference is",0
inci db "Number 1 Incremented:",0
deci db "Number 1 Decremented:",0
sali db "Number 1 Shift left 2 (x4):",0
sari db "Number 1 Shift right 2 (/4):",0
sariex db "Number 1 Shift right 2 (/4) with "
db "sign extension:",0
multi db "The product is",0
divi db "The integer quotient is",0
remi db "The modulo is",0
section .bss
resulti resq 1
modulo resq 1
section .text
global main
main:
push rbp
mov rbp,rsp
; displaying the numbers
mov rdi, fmt
mov rsi, [number1]
mov rdx, [number2]
mov rax, 0
call printf
; adding---------------------------------------------------------------------
mov rax, [number1]
add rax, [number2] ; add number2 to rax
mov [resulti], rax ; move sum to result
; displaying the result
mov rdi, fmtint
mov rsi, sumi
mov rdx, [resulti]
mov rax, 0
call printf
; substracting---------------------------------------------------------------
mov rax, [number1]
sub rax, [number2] ; subtract number2 from rax
mov [resulti], rax
; displaying the result
mov rdi, fmtint
mov rsi, difi
mov rdx, [resulti]
mov rax, 0
call printf
; incrementing-----------------------------------------------------------------
mov rax, [number1]
inc rax ; increment rax with 1
mov [resulti], rax
; displaying the result
mov rdi, fmtint
mov rsi, inci
mov rdx, [resulti]
mov rax, 0
call printf
; decrementing-----------------------------------------------------------------
mov rax, [number1]
dec rax ; decrement rax with 1
mov [resulti], rax
; displaying the result
mov rdi, fmtint
mov rsi, deci
mov rdx, [resulti]
mov rax, 0
call printf
; shift arithmetic left------------------------------------------------------
mov rax, [number1]
sal rax, 2 ; multiply rax by 4
mov [resulti], rax
; displaying the result
mov rdi, fmtint
mov rsi, sali
mov rdx, [resulti]
mov rax, 0
call printf
; shift arithmetic right------------------------------------------------------
mov rax, [number1]
sar rax, 2 ; divide rax by 4
mov [resulti], rax
; displaying the result
mov rdi, fmtint
mov rsi, sari
mov rdx, [resulti]
mov rax, 0
call printf
; shift arithmetic right with sign extension ---------------------------------
mov rax, [neg_num]
sar rax, 2 ; divide rax by 4
mov [resulti], rax
; displaying the result
mov rdi, fmtint
mov rsi, sariex
mov rdx, [resulti]
mov rax, 0
call printf
; multiply-------------------------------------------------------------------
mov rax, [number1]
imul qword [number2] ; multiply rax with number2
mov [resulti], rax
; displaying the result
mov rdi, fmtint
mov rsi, multi
mov rdx, [resulti]
mov rax, 0
call printf
; divide---------------------------------------------------------------------
mov rax, [number1]
mov rdx, 0 ; rdx needs to be 0 before idiv
idiv qword [number2] ; divide rax by number2, modulo in rdx
mov [resulti], rax
mov [modulo], rdx ; rdx to modulo
; displaying the result
mov rdi, fmtint
mov rsi, divi
mov rdx, [resulti]
mov rax, 0
call printf
mov rdi, fmtint
mov rsi, remi
mov rdx, [modulo]
mov rax, 0
call printf
mov rsp,rbp
pop rbp
ret
结果如下:
The numbers are 128 and 19
The sum is 147
The difference is 109
Number 1 Incremented: 129
Number 1 Decremented: 127
Number 1 Shift left 2 (x4): 512
Number 1 Shift right 2 (/4): 32
Number 1 Shift right 2 (/4) with sign extension: -3
The product is 2432
The integer quotient is 6
The modulo is 14
add,sub
可以用于有符号,也可以用于无符号,第二个操作数会加到第一个操作数上。对于有符号数,如果第一个操作数放不下结果,那么CF 标记会置位,对于无符号数,OF标记会置位,如果结果是0,ZF标记会置位,如果结果是负数,那么SF标记会置位。
sub 和add类似,无需重复。
sal,sar
算术左移,算术右移,在移动的 时候会考虑符号位,比如对于负数,在做sar的时候,就会在最高位补1。这儿会见证一次数字的神奇:
举一个例子, 比如一个8位的寄存器,需要表示 -4,那么结果如下:
0000 0100
=>
1111 1011
+ 1
1111 1100 (-4 的补码,也就是计算机中真正的存储形式)
算术右移1位
1111 1110 (-2)
也就是高位补一个符号位,就可以正确表示除以2的结果!
imul, idiv
mul用于无符号数乘法,imul用于有符号数乘法,rax用来存放第一个参数,乘法会用到rdx,rax2个寄存器,rdx用来存放高64位,rax存放低64位。
对于除法,rax用来存放被除数,也用来存放结果的整数部分,rdx用来存放余数,因此使用idiv之前,需要将rdx清零。
栈
先看一个代码,反转字符串:
; stack.asm
extern printf
section .data
string db "ABCDE",0
stringLen equ $ - string-1 ; stringlength without 10 and 0
fmt1 db "The original string: %s",10,0
fmt2 db "The reversed string: %s",10,0
section .bss
section .text
global main
main:
push rbp
mov rbp,rsp
; Print the original string
mov rdi, fmt1
mov rsi, string
mov rax, 0
call printf
;push the string char per char on the stack
xor rax, rax
mov rbx, string ; address of string in rbx
mov rcx, stringLen ; length in rcx counter
mov r12, 0 ; use r12 as pointer
pushLoop:
; push char per char on the stack
mov al, byte [rbx+r12] ; move char into rax
push rax ;push rax on the stack
inc r12 ; increase char pointer with 1
loop pushLoop ; continue loop
;pop the string char per char from the stack
;this will reverse the original string
mov rbx, string ; address of string in rbx
mov rcx, stringLen ; length in rcx counter
mov r12, 0 ; use r12 as pointer
popLoop:
pop rax ; pop a char from the stack
mov byte [rbx+r12], al ;move the popped char into string
inc r12 ; increase char pointer with 1
loop popLoop ; continue loop
mov byte [rbx+r12],0 ; terminate string with 0
; Print the reversed string
mov rdi, fmt2
mov rsi, string
mov rax, 0
call printf
mov rsp,rbp
pop rbp
ret
结果:
The original string: ABCDE
The reversed string: EDCBA
上面程序本质上就是按照栈的FILO规则,第一个loop先将字符串push到栈上,第二个loop再读出来,这样就把字符串反过来了。可以在第一个循环结束后打一个断点,显示rsp指向地址的内容
> i registers rsp
rsp 0x7fffffffe468 0x7fffffffe468
> x /5cg 0x7fffffffe468
0x7fffffffe468: 69 'E' 68 'D'
0x7fffffffe478: 67 'C' 66 'B'
0x7fffffffe488: 65 'A'
浮点
首先看下浮点在计算机中的表示:
32位单精度
1 个符号位,8个指数位,23个分数位
64位双精度
1个符号位,11个指数位,52个分数位
对于指数位,还有一个偏移值,对于单精度,偏移值是127,双精度偏移值是1023。接下来看一个例子:
1101010.01011 (106 + 1/4 + 1/16 + 1/32 = 106 + 11/32)
=1.0101001011 x 2^6
由于是正数,符号位是0,指数位是 6 + 127 = 133,那么表示结果就是:
0 10000101 01010010110000000000000
接下来看一点浮点的代码:
; fcalc.asm
extern printf
section .data
number1 dq 9.0
number2 dq 73.0
fmt db "The numbers are %f and %f",10,0
fmtfloat db "%s %f",10,0
f_sum db "The float sum of %f and %f is %f",10,0
f_dif db "The float difference of %f and %f is %f",10,0
f_mul db "The float product of %f and %f is %f",10,0
f_div db "The float division of %f by %f is %f",10,0
f_sqrt db "The float squareroot of %f is %f",10,0
section .bss
section .text
global main
main:
push rbp ; necessary because of printf with float
mov rbp,rsp
; print the numbers
movsd xmm0, [number1]
movsd xmm1, [number2]
mov rdi,fmt
mov rax,2 ; two floats
call printf
; sum
movsd xmm2, [number1] ; double precision float into xmm
addsd xmm2, [number2] ; add into to xmm
; print the result
movsd xmm0, [number1]
movsd xmm1, [number2]
mov rdi,f_sum
mov rax,3 ; one float
call printf
; difference
movsd xmm2, [number1] ; double precision float into xmm
subsd xmm2, [number2] ; subtract from xmm
; print the result
movsd xmm0, [number1]
movsd xmm1, [number2]
mov rdi,f_dif
mov rax,3 ; three floats
call printf
; multiplication
movsd xmm2, [number1] ; double precision float into xmm
mulsd xmm2, [number2] ; multiply with xmm
; print the result
mov rdi,f_mul
movsd xmm0, [number1]
movsd xmm1, [number2]
mov rax,3 ; three floats
call printf
; division
movsd xmm2, [number1] ; double precision float into xmm
divsd xmm2, [number2] ; dividdatae xmm0
; print the result
mov rdi,f_div
movsd xmm0, [number1]
movsd xmm1, [number2]
mov rax,1 ; one float
call printf
; squareroot
sqrtsd xmm1, [number1] ; squareroot double precision float in xmm
; print the result
mov rdi,f_sqrt
movsd xmm0, [number1]
mov rax,2 ; one float
call printf
; exit
mov rsp,rbp
pop rbp ; undo the push at the beginning
ret
mov rdi,f_sum
mov rax,3 ; one float
call printf
; difference
movsd xmm2, [number1] ; double precision float into xmm
subsd xmm2, [number2] ; subtract from xmm
; print the result
movsd xmm0, [number1]
movsd xmm1, [number2]
mov rdi,f_dif
mov rax,3 ; three floats
call printf
; multiplication
movsd xmm2, [number1] ; double precision float into xmm
mulsd xmm2, [number2] ; multiply with xmm
; print the result
mov rdi,f_mul
movsd xmm0, [number1]
movsd xmm1, [number2]
mov rax,3 ; three floats
call printf
; division
movsd xmm2, [number1] ; double precision float into xmm
divsd xmm2, [number2] ; divide xmm0
; print the result
mov rdi,f_div
movsd xmm0, [number1]
movsd xmm1, [number2]
mov rax,1 ; one float
call printf
; squareroot
sqrtsd xmm1, [number1] ; squareroot double precision float in xmm
; print the result
mov rdi,f_sqrt
movsd xmm0, [number1]
mov rax,2 ; one float
call printf
; exit
mov rsp,rbp
pop rbp ; undo the push at the beginning
ret
结果如下:
The numbers are 9.000000 and 73.000000
The float sum of 9.000000 and 73.000000 is 82.000000
The float difference of 9.000000 and 73.000000 is -64.000000
The float product of 9.000000 and 73.000000 is 657.000000
The float division of 9.000000 by 73.000000 is 0.123288
The float squareroot of 9.000000 is 3.000000
对于浮点,参数传递用的就是xmm0,xmm1,等,这时候调用printf时指定的rax值就作用出来了,会告诉系统从哪里获取参数。
movsd 用于双精度,movss用于单精度。
网友评论