Ubuntu x86-64汇编(5) 控制指令
控制指令 Control Instructions
汇编的控制指令主要包含标签, 无条件跳转和有条件跳转几种
标签 Label
标签用于标记跳转的目的, 必须以字母开头, 后面跟着字母, 数字和下划线, 最后以冒号:结束
yasm里的标签是区分大小写的
无条件跳转 Unconditional Control Instructions
格式
jmp <label> jmp startLoop jmp ifDone jmp last
条件跳转 Conditional Control Instructions
条件跳转一般发生在一个比较指令之后, 比较的结果决定跳转是否执行. 比较指令的结果会存储在rFlag寄存器. 条件跳转要紧接着比较指令进行, 以免结果被其他指令修改.
比较指令的格式
cmp <op1>, <op2>
<op1> 和 <op2> 必须是同一尺寸且不能修改. 不能同时为内存. <op1>不能为立即数. 跳转指令je和jne对无符号数和有符号数同样适用. 但是其他的跳转指令会有区别, 适用于有符号数的有 jl, jle, jg, jge; 适用于无符号数的有 jb, jbe, ja, jae. 列表如下
je <label> ; if <op1> == <op2> jne <label> ; if <op1> != <op2> jl <label> ; signed, if <op1> < <op2> jle <label> ; signed, if <op1> <= <op2> jg <label> ; signed, if <op1> > <op2> jge <label> ; signed; if <op1> >= <op2> jb <label> ; unsigned, if <op1> < <op2> jbe <label> ; unsigned, if <op1> <= <op2> ja <label> ; unsigned, if <op1> > <op2> jae <label> ; unsigned, if <op1> >= <op2>
举例对于下面的判断语句
if (currNum > myMax)
myMax = currNum;
;先定义变量 currNum dq 0 myMax dq 0 ; 代码为 mov rax, qword [currNum] cmp rax, qword [myMax] ;if currNum <= myMax jle notNewMax ; skip set new max mov qword [myMax], rax notNewMax:
另一个例子
if (x != 0) {
ans = x / y;
errFlg = FALSE;
} else {
ans = 0;
errFlg = TRUE;
}
代码
TRUE equ 1 FALSE equ 0 x dd 0 y dd 0 ans dd 0 errFlg db FALSE ; ; cmp dword [x], 0 ; if statement je doElse mov eax, dword [x] cdq idiv dword [y] mov dword [ans], eax mov byte [errFlg], FALSE jmp skpElse doElse: mov dword [ans], 0 mov byte [errFlg], TRUE skpElse:
这个例子中因为是带符号数, 所以使用了idiv和cdq.
超出跳转范围 Jump Out Of Range
一般来讲, 条件跳转的目标标签要在128byte以内, 如果超出的话就会出现jump out-of-range错误. 但是无条件跳转是没有这个限制的. 可以用以下的方法解决这个问题
cmp rcx, 0 je endOfLoop jmp startOfLoop endOfLoop:
用je + jmp 代替 jne, 就避免了条件跳转的限制
枚举 Iteration
这个控制指令用于枚举或循环. 一个基本的循环由一个计数器和一个顶上或底下的判断和跳转组成. 例如
maxN dq 30 sum dq 0 ; 下面的代码用于计算从1到maxN的奇数之和. mov rcx, qword [maxN] ; loop counter mov rax, 1 ; odd integer counter sumLoop: add qword [sum], rax ; sum current odd integer add rax, 2 ; set next odd integer dec rcx ; decrement loop counter cmp rcx, 0 jne sumLoop
在这个例子中, rcx 用于循环的计数, rax用于存储当前循环的奇数, 用1初始化并每次加2.
另外我们也可以用loop指令来实现, 其格式如下, 执行时会递减rcx寄存器的值, 与0比较, 当不等于0时跳转(到开始处再次循环)
loop <label>
之前的代码可以用loop改写为:
mov rcx, qword [maxN] ; loop counter mov rax, 1 ; odd integer counter sumLoop: add qword [sum], rax ; sum current odd integer add rax, 2 ; set next odd integer loop sumLoop
因为循环中会改写并检查rcx寄存器, 如果未初始化rcx寄存器, 将导致未知的循环次数. loop指令在编程中很有用, 但是受限于rcx寄存器. 如果需要多层的loop, 需要在循环内外进行rcx当前值的保存和恢复.
代码例子 平方数求和 Sum of Squares
; Simple example program to compute the ; sum of squares from 1 to N. ; ********************************************** ; Data declarations section .data ; ---- ; Define constants SUCCESS equ 0 ; Successful operation SYS_exit equ 60 ; call code for terminate ; Define Data. n dd 10 sumOfSquares dq 0 ; ******************************************************* section .text global _start _start: ; ---- ; Compute sum of squares from 1 to N. ; Approach: ; for (i=1; i<N; i++) ; sumOfSqaures += i^2; mov rbx, 1 ; i mov ecx, dword [n] sumLoop: mov rax, rbx ; get i mul rax ; i^2 add qword [sumOfSquares], rax inc rbx loop sumLoop ; ---- ; Done, terminate program. last: mov rax, SYS_exit ; call code for exit mov rdi, SUCCESS ; exit with success syscall
.