计算机系统基础(一)笔记——Week6 IA-32指令类型
本文最后更新于:2024年5月25日 晚上
Week6 IA-32指令类型
无需记忆,能看懂即可
前置知识:条件码
除了整数寄存器,CPU还维护了一组单个位的条件码寄存器,描述了最近的算术或逻辑操作的属性。
CF
:进位标志。最近的操作使最高位产生了进位,用来检测无符号操作的溢出
ZF
:零标志。最近的操作得出的结果为0
SF
:符号标志。最近的操作得到的结果为负数
OF
:溢出标志。最近的操作导致一个补码溢出——正溢出或者负溢出
6.1 传送指令
常用传送指令
-
通用数据传送指令
MOV:一般传送,包括movb、movw、movl等
MOVS:符号拓展传送(高位补符号),如movsbw、movswl等
MOVZ:零拓展传送(高位补0),movzwl、movzbl
XCHG:数据交换
PUSH/POP:入栈、出栈,如pushl、pushw、popl、popw等
-
地址传送指令
LEA:加载有效地址,如
leal(%edx,%eax),%eax
的功能为R[eax]=R[edx]+R[eax]
-
输入输出指令
IN/OUT:I/O端口和寄存器之间的交换
-
标志传送指令
PUSHF、POPF:将EFLAG压栈,或将栈顶内容送到EFLAG
入栈(pushw %ax)
此条语句是将寄存器ax的内容压栈
- 栈不等于“堆栈”
执行过程:
首先R[sp]=R[sp]-2
然后M[R[sp]]=R[ax]
出栈(popw %ax)
执行过程:
首先R[ax]=M[R[sp]]
然后R[sp]=R[sp]+2
对一段代码的简单分析
1 |
|
执行反汇编objdump -d test
结果
1 |
|
add
函数从80483d4
开始
6.2 定点算术运算指令
-
定点算术运算指令
-
加/减运算(影响标志,不区分无/带符号)
ADD:addb、addw、addl
SUB:subb、subw、subl
-
加1/减1运算(影响除CF以外的标志,不区分带/无符号)
INC/DEC:incb incw incl(decb decw decl)
-
取负运算(影响标志,对0取负,则结果为0且CF清0,否则CF置1)
NEG:取负(negb negw negl)
-
比较运算(做减法得到标志、不区分无/带符号)
CMP:比较,包括cmpb、cmpw、cmpl
-
乘/除运算(==不影响==标志、区分带/无符号)
MUL/IMUL(无符号乘/带符号乘)
DIV/IDIV(无符号除/带符号除)
-
整数乘除指令
-
乘法指令:可给出一个、两个或三个操作数
一个:SRC,另一个在AL/AX/EAX中,将SRC和累加器内容相乘,结果存放在AX(16)/DX-AX(32)/EDX-EAX(64)中。
DX-AX表示32位的乘积高16位和低16位分别在DX、AX。==n位×n位=2n位==
两个:DST和SRC,将DST和SRC相乘,结果在DST中。==n位×n位=n位==
三个:REG、SRC和IMM,将SRC和立即数IMM相乘结果在REG中。==n位×n位=n位==
-
除法指令:只给出除数。EDX-EAX中内容除以指定除数
8位:16位被除数在AX寄存器中,商给AL,余数在AH
16位:32位被除数在DX-AX寄存器中,商给AX,余数在DX
32位:被除数在EDX-EAX寄存器中,商送EAX,余数在EDX
同样的例子
1 |
|


ALU有什么?
-
补码加/减器
带符号整数加、减,无符号整数加、减
-
各种与或非、前置0/1运算
没有乘除法器,因为可以通过加减法+移位实现
举个例子:定点加法指令
假设R[ax]=FFFAH,R[bx]=FFF0H,则执行指令“addw %bx, %ax”后,AX、BX中的内容各是什么?标志CF、OF、ZF、SF各是什么?要求分别将操作数作为无符号数和带符号整数解释并验证指令执行结果
功能R[ax]=R[ax]+R[bx],即R[ax]=FFFAH+FFF0H=FFEAH,BX中内容不变
CF=1(有进位),OF=0(无溢出),ZF=0(最终结果不为0),SF=1(最高位为1)
-
无符号整数运算,则CF=1说明结果溢出
验证:FFFA=65535-5=65530,FFEA=65535-21=65514
-
带符号运算,OF=0说明没有溢出
验证:FFFA=-6,FFF0=-16,FFEA=-22=-6+(-16),结果吻合
举个例子:定点乘法指令
假设R[eax]=000000B4H,R[ebx]=00000011H,M[000000F8H]=000000A0H,请问:
执行指令“mulb %bl”后,哪些寄存器的内容会发生变化?是否与"imulb %bl"指令所发生的变化一样?请用该例子给出的数据验证结论。
- "mulb %bl"指的是R[ax]=R[al]×R[bl],
mulb
中的b指出乘数是8位(byte),%bl
指出寄存器类型是用的低八位。也就是R[ax]=R[eax]的低八位×R[ebx]的低八位=B4H×11H=1011 0100×0001 0001=0000 1011 1111 0100=0BF4H。R[ax]=0BH,R[bx]=F4H - "imulb %bl"功能是带符号相乘,R[ax]=R[al]×R[bl]=B4H×11H=0BF4H,结果显然不对
==对于带符号乘,若乘积取低n位,和无符号一致;取2n位,要采用布斯乘法==
假设R[eax]=000000B4H,R[ebx]=00000011H,M[000000F8H]=000000A0H,请问:
执行指令"imull $-16, (%eax,%ebx,4), %eax"后,哪些寄存器和存储单元发生了变化?乘积的机器数和真值是多少?
该指令有三个参数,功能为R[eax]=(-16)×M[R[eax]+R[ebx]×4]
(带符号整数乘法)
6.3 按位运算指令
逻辑运算
NOT:notb、notw、notl
AND:andb、andw、andl
OR:orb、orw、orl
XOR:xorb、xorw、xorl
TEST:做”与“操作测试,仅影响标志,不改变寄存器和存储单元的内容
- 仅NOT不影响标志,其他指令OF=CF=0,而ZF和SF根据结果设置:若全为0,则ZF=1;若最高位为1,则SF=1
逻辑运算举例
M[0x1000]=00000F89H
M[0x1004]=00001270H
R[eax]=FF000001H
R[ecx]=00001000H
执行下列指令后如何变化?
notw %ax:R[ax]=not(0001H)=FFFEH
andl %eax, (%ecx):M[R[ecx]]=M[1000H]=M[0x1000]=00000F89H^FF000001H=0000001H
orb 4(%ecx), %al:R[al]=R[al] or M[4+R[ecx]]=01H or 70H=71H
xorw %ax, 4(%ecx):M[R[ecx]+4]=M[0x1004]=1270H$\oplus$0001H=1271H
testl %eax,%ecx:00001000H ^ FF000001H=0,ZF=1,SF=0,CF=OF=0
移位运算
SHL/SHR:逻辑左/右移动,包括shlb、shrw、shrl等
SAL/SAR:算术左/右移动,左移判溢出,右移高位补符。包括salb、sarw、sarl等
ROL/ROR:循环左/右移,包括rolb、rorw、roll等
RCL/RCR:带进位循环左/右移,即将CF作为操作数一部分循环移位,包括rclb、rcrw、rcll等
移位运算举例
假设short型变量x分配在AX中,R[ax]=FF80H,则以下汇编代码段执行后变量x的机器数和真值是什么?
movw %ax,%dx
salw $2,%ax
addl %dx,%ax
sarw $1,%ax
- R[dx]=R[ax]
- 1111 1111 1000 0000 << 2,算术左移,OF=0(移出的符号和剩下的符号相等,无溢出)
- R[ax]=1111 1110 0000 0000+1111 1111 1000 0000=1111 1101 1000 0000
- R[ax]>>1,1111 1110 1100 0000
程序执行的操作是((x<<2)+x)>>1
6.4 控制转移指令
指令执行可以==按顺序==或==跳转到转移目标指令处==执行
-
无条件转移指令
JMP DST:无条件转移到目标指令DST处执行
-
条件转移
Jcc DST:cc为条件码,根据条件码判断是否满足条件从而转移到目标指令DST
-
条件设置
SETcc DST:按条件码cc判断的结果保存到DST中(8位寄存器)
-
调用和返回指令(用于过程调用)
CALL DST:返回地址RA入栈,转移到DST处执行
RET:从栈中取出返回地址RA,转到RA处执行
-
中断指令(见Week7/8)
IA-32的标志寄存器
-
6个条件标志
OF/SF/ZF/CF
AF:辅助进位标志(BCD码运算下才有意义)
PF:奇偶标志
-
3个控制标志
DF(Direction Flag):方向标志(自动变址方向是增还是减)
IF(Interrupt Flag):中断允许标志(仅对外部可屏蔽中断有用)
TF(Trap Flag):陷阱标志(是否是单步跟踪状态)
根据标志进行跳转
序号 | 指令 | 转移条件 | 说明 |
---|---|---|---|
jc label | CF=1 | 有进位/借位 | |
jnc label | CF=0 | 无进位/借位 | |
je/jz label | ZF=1 | 相等/等于零 | |
jne/jnz label | ZF=0 | 不相等/不等于零 | |
js label | SF=1 | 是负数 | |
jns label | SF=0 | 是非负数 | |
jo label | OF=1 | 有溢出 | |
jno label | OF=0 | 无溢出 | |
ja/jnbe label | CF=0 AND ZF=0 | 无符号整数A>B | |
jae/jnb label | CF=0 OR ZF=1 | 无符号整数A>=B | |
jb/jnae label | CF=1 AND ZF=0 | 无符号整数A<B | |
jbe/jna label | CF=1 OR ZF=1 | 无符号整数A<=B | |
jg/jnle label | SF=OF AND ZF=0 | 带符号整数A>B | |
jge/jnl label | SF=OF OR ZF=1 | 带符号整数A>=B | |
jl/jnge label | SF≠OF AND ZF≠0 | 带符号整数A<B | |
jle/jng label | SF≠OF OR ZF=1 | 带符号整数A<=B |
:根据单个标志的值进行转移
:按无符号整数比较转移
:按带符号整数比较转移
举个例子
1 |
|
len=0
时应该返回0,为什么是存储器访问异常
?
1 |
|
解答:i在%eax中,len在%edx中,第一次循环eax=edx=0000 0000H
-
subl $1,%edx
执行结果:在运算器中,A=0000 0000H,B=0000 0001H,sub=1即,即 -
cmpl %edx,%eax
执行结果: -
jbe .L3
执行结果:CF=1,条件满足,因此转到.L3执行
进入下次循环后,i即使增加也会一直小于len-1=FFFF FFFF,因此是死循环。当i增加到非常大时,a[i]超出了访问界限,发生存储器访问异常
len如果声明为int类型,指令
jbe .L3
改为了jle .L3
,按带符号整数比较此时ZF=0且SF=OF,因此不满足条件,不会进入循环
C表达式类型转换顺序
两个数比较时,(unsigned)short/char——>int——>unsigned——>long long ——>unsigned long long
1 |
|
6.5 x87浮点处理指令
IA-32的浮点处理架构
- x87FPU指令集(gcc默认)
- SSE指令集(x86-64架构所用)
IA-32中处理浮点数有三种:
-
float:32位IEEE 754单精度
-
double:64位IEEE 754双精度
-
long double:80位双精度拓展格式
1符号位s,15位阶码e,偏置常数16383,1位显式首位有效位j和63位尾数f
x87FPU指令概述
最初早期的浮点处理器是作为CPU的外置协处理器出现的
x87 FPU 特指与x86处理器配套的浮点协处理器架构
-
浮点寄存器采用栈结构
深度为8,宽度为80位,8个80位寄存器
名称为ST(0)-ST(7),栈顶ST(0)
-
所有浮点运算都按80位扩展精度进行
-
浮点数在浮点寄存器和内存之间传送
float、double、long double型变量在内存分别用IEEE 754单精度、双精度和扩展精度表示,分别占32位(4B)、64位(8B)和96位(12B,其中高16位无意义)
float、double、long double类型变量在浮点寄存器中都用80位扩展精度表示
*从浮点寄存器到内存:*80位拓展精度转为32位或64位
*从内存到浮点寄存器:*32位或64位转为80位拓展精度
x87FPU指令
-
数据传送类
-
装入
FLD:将数据从存储单元装入浮点寄存器栈顶 ST(0)
FILD:将数据从int型转换为浮点格式后,装入浮点寄存器栈顶
-
存储
FSTx:x为 s/l时,将栈顶ST(0)转换为单/双精度格式,然后存入存储单元、
FSTPx:弹出栈顶元素,并完成与FSTx相同的功能
FISTx:将栈顶数据从int型转换为浮点格式后,存入存储单元
FISTP:弹出栈顶元素,并完成与FISTx相同的功能
出栈:ST(1)变为ST(0)
-
交换
FXCH:交换ST(0)和ST(1)
-
常数装载到栈顶
FLD1 :装入常数1.0
FLDZ :装入常数0.0
FLDPI :装入常数pi (=3.1415926…)
FLDL2E :装入常数log(2)e
FLDL2T :装入常数log(2)10
FLDLG2 :装入常数log(10)2
FLDLN2 :装入常数Log(e)2
-
-
算术运算类
-
加法
FADD/FADDP: 相加/相加后弹出栈
FIADD:按int型转换后相加
-
减法
FSUB/FSUBP : 相减/相减后弹出栈
FSUBR/FSUBRP:调换次序相减/相减后弹出栈
FISUB:按int型转换后相减
FISUBR:按int型转换并调换次序相减
指令未带操作数则默认ST(0)和ST(1)
带r指令就是相减顺序反转
-
乘法
FMUL/FMULP: 相乘/相乘后弹出栈
FIMUL:按int型转换后相乘
-
除法
FDIV/FDIVP : 相除/相除后弹出栈
FIDIV:按int型转换后相除
FDIVR/FDIVRP:调换次序相除/相减后弹出栈
FIDIVR:按int型转换并调换次序相除
-
IA-32浮点运算举例
例1
1 |
|
1 |
|
Question:老版本gcc -O2
编译第一个输出0,第二个输出1为什么?
Answer:
1 |
|
f(10)=1.0(80位扩展精度)/10(转换为double)=0.1
0.1=0.00011[0011]B= 0.00011 0011 0011 0011 0011 0011 0011…B,无法精确表示0.1
1 |
|
a舍入过而b没有舍入,因此a≠b
1 |
|
a、b都舍入过,因此a==b
例2
1 |
|
**Question:**为什么?
**Answer:**区别在于指令fldl
和fildl