数据传输指令
- 一般数据传送指令:MOV(数据传送)、MOVSX(有符号扩展传送)、MOVZX(无符号扩展传送)
- 堆栈操作指令:PUSH、POP、PUSHA、PUSHAD、POPA、POPAD
- 标志寄存器操作指令:PUSHF、POPF、PUSHFD、POPFD、LAHF、SAHF
- 地址传送指令:LEA
- 待条件的数据传送指令:CMOVE、CMOVNE、CMOVA ….
一般数据传送指令
- MOV OPD, OPS :数据传送
- MOVSX R16/R32, OPS :符号扩展传送
- MOVZX R16/R32, OPS :0(无符号)扩展传送
int x = 10;
short y = 20;
x = y;
movsx eax, word ptr [ebp-14h]
mov dword ptr [ebp-8], eax
unsigned short z = 20;
mov eax,14h
mov word ptr [ebp-20h],ax
x = z;
movzx eax, word ptr [ebp-20h]
mov dword ptr [ebp-8], eax
使用单个标志位判断转移条件是否成立
cmove/cmovz、cmovc、cmovs、cmovo、cmovp
条件:ZF=1 CF=1 SF=1 OF=1 PF=1
cmovne/cmovnz、cmovnc、cmovns、cmovno、cmovnp
条件:ZF=0 CF=0 SF=0 OF=0 PF=0
使用多个标志位组合判断转移条件是否成立
cmova、cmovb、cmovg、cmovl、cmovae、cmovbe、cmovge、cmovle
cmova:如果无符号溢出标志 CF 为 0,则将源操作数传送到目的操作数。即 "COnditional MOver if Above" 的缩写。
cmovb:如果进位标志 CF 为 1,则将源操作数传送到目的操作数。即 "COnditional MOver if Below" 的缩写。
cmovg:如果大于标志 ZF=0 and SF=OF,则将源操作数传送到目的操作数。即 "COnditional MOver if Greater" 的缩写。
cmovl:如果小于标志 SF ≠ OF,则将源操作数传送到目的操作数。即 "COnditional MOver if Less" 的缩写。
cmovae:如果无符号大于或等于标志 CF=0,则将源操作数传送到目的操作数。即 "COnditional MOver if Above or Equal" 的缩写。
cmovbe:如果小于或等于标志 CF=1 or ZF=1,则将源操作数传送到目的操作数。即 "COnditional MOver if Below or Equal" 的缩写。
cmovge:如果大于等于标志 SF=OF,则将源操作数传送到目的操作数。即 "COnditional MOver if Greater or Equal" 的缩写。
cmovle:如果小于或等于标志 ZF=1 or SF ≠ OF,则将源操作数传送到目的操作数。即 "COnditional MOver if Less or Equal" 的缩写。
cmovg
适用于有符号整数的比较,而cmova
适用于无符号整数的比较。
//设无符号双字类型变量 x、y、z,将x和y中间的大者存放到z中
mov eax, x
cmp eax, y ; //比较指令,
;//根据(eax)-(y) 设置标志位
jae L1
mov eax, y
L1: mov z, eax
cmovb eax, y ; CF=1 且 ZF=0时,传送
mov z, eax
int x,y,z;
z = x > y ? x : y;
mov ecx, DWORD PTR _y$[ebp]
add esp, 16 ; 00000010H
cmp DWORD PTR _x$[ebp], ecx
cmovg ecx, DWORD PTR _x$[ebp]
/*
下面使用 z 时直接使用了 ecx
Release 版,不同程序编译结果不同
_x$、_y$是编译时生成的符号常量:_x$ = … _y$ = …
红色语句与z的赋值无关,是下一条C语句翻译结果的部分。将其穿插到前面提高流水线的处理性能。
*/
堆栈操作指令
进栈指令: PUSH OPS
字数据入栈
- (ESP) – 2 → ESP
- 字数据 → [ESP]
双字数据入栈:
- (ESP) – 4 → ESP
- 双字数据 → [ESP]
出栈指令: POP OPD
字数据出栈
① ([ESP]) → OPD
② (ESP)+2 → ESP
记为:↑(ESP) → OPD
双字数据出栈类似, (ESP)+4->ESP
- 8个16位寄存器入栈 PUSHA
- 8个16位寄存器出栈 POPA
- 8个32位寄存器入栈 PUSHAD
- 8个32位寄存器出栈 POPAD
32位标志寄存器进栈指令:
- 格式:PUSHFD
- 功能:将标志寄存器的内容压入堆栈,
- 记为(eflags)→↓(esp)。
32位标志寄存器出栈指令:
- 格式:POPFD
- 功能:将栈顶内容弹出送入标志寄存器中,
- 记为↑(esp)→eflags。
16位标志寄存器进栈指令:
- 格式:PUSHF
- 功能:将标志寄存器的内容压入堆栈,
- 记为(eflags)15-0→↓(esp)。
16位标志寄存器出栈指令
- 格式:POPF
- 功能:将栈顶内容弹出送入标志寄存器中,
- 记为↑(esp)→eflags 15-0 。
地址传送指令
传送偏移地址指令
语句格式:LEA R32, M32
功 能:将M32 对应的地址送入R32中。
与MOV对比,如果是mov ebx, [eax]会取出eax的内容所指向的内存的值,而LEA会将EAX的内容视作地址传送。
LEA ESI, NUM
LEA EDI, [ESI+4] (ESI)+4 ➡ EDI
实现(EAX)+ (EBX) * 8 ➡ ECX :LEA ECX,[EAX + EBX * 8]
总结
一般传送 MOV OPD,OPS
有符号数传送 MOVSX R16/R32,OPS/非立即数
无符号数传送 MOVZX R16/R32, OPS/非立即数
传送偏移地址 LEA R32, M32
进栈 PUSH OPS
出栈 POP OPD
32位通用寄存进栈、出栈 PUSHAD、POPAD
标志寄存器进栈、出栈 PUSHFD、POPFD
LEA
(Load Effective Address)和MOV
(Move)是 x86 汇编语言中的两种指令,它们虽然都可以用于数据的传输,但在功能和用途上有明显的区别。
MOV 指令:
MOV
指令用于将数据从一个位置复制到另一个位置。- 示例:
MOV AX, [BX] ; 将内存地址 BX 处的数据复制到寄存器 AX 中 MOV [DI], CX ; 将寄存器 CX 中的数据复制到内存地址 DI 处
MOV
指令的作用是将数据从一个位置复制到另一个位置,可以是寄存器之间的传输,也可以是寄存器与内存之间的传输,或者是内存之间的传输。它是一种通用的数据传输指令。LEA 指令:
LEA
指令用于加载有效地址,即将内存地址计算结果加载到目标寄存器中,而不是将内存中的数据加载到寄存器中。- 示例:
LEA SI, [BX+DI] ; 将 BX 和 DI 寄存器中的值相加得到地址,加载到 SI 寄存器中 LEA BX, [SI+10] ; 将 SI 寄存器中的值加上 10 得到地址,加载到 BX 寄存器中
LEA
指令的作用是将有效地址加载到寄存器中,这个地址是根据指定的内存地址计算得到的,而不是实际存储的数据。LEA
指令通常用于进行地址计算,而不是数据传输。总的来说,
MOV
指令用于数据的传输,而LEA
指令用于计算地址。虽然它们都涉及到寄存器和内存之间的操作,但是MOV
是用于数据传输,而LEA
则是用于地址计算。
算术运算指令
- 加法指令: INC、ADD、ADC
- 减法指令: DEC、NEG、SUB、SBB、CMP
- 乘法指令: IMUL、MUL
- 除法指令: IDIV、DIV
- 符号扩展指令: CBW、CWD、CWDE、CDQ
一般对标志位都有影响,但INC和DEC对CF无影响
加法指令
加1指令:INC OPD ; (OPD) +1 → OPD
加指令:ADD OPD,OPS ; (OPD)+(OPS) → OPD
带进位加指令:
语句格式:ADC OPD,OPS
功能: (OPD) + (OPS) + CF → OPD
减法指令
减1指令:DEC OPD ; (OPD) –1 → OPD
求补指令:NEG OPD ; (OPD)求反加1 → OPD
减指令:SUB OPD,OPS ;(OPD)-(OPS) → OPD
带借位减指令:SBB OPD,OPS;(OPD) – (OPS) – CF → OPD
乘法指令
有符号乘法
双操作数的有符号乘指令:
- 语句格式:IMUL OPD, OPS
- 功 能:(OPD) * (OPS) → OPD
3个操作数的有符号乘指令:
- 语句格式:IMUL OPD, OPS,n
- 功 能:(OPS) * n → OPD
单操作数的有符号乘法
语句格式: IMUL OPS
- 字节乘法: (AL) * (OPS) → AX
- 字 乘 法: (AX) * (OPS) → DX , AX
- 双字乘法:(EAX) * (OPS) →EDX, EAX
无符号乘法
语句格式: MUL OPS
功 能:
- 字节乘法: (AL) * (OPS) → AX
- 字乘法: (AX) * (OPS) → DX , AX
- 双字乘法:(EAX) * (OPS) →EDX, EAX
除法指令
有符号除法
IDIV OPS
- 字节除法:(AX) / (OPS) → AL(商),AH(余)
- 字 除 法:(DX, AX) / (OPS) → AX (商), DX (余)
- 双字除法:(EDX, EAX) / (OPS) → EAX (商), EDX
无符号除法
DIV OPS
- 字节除法:(AX) / (OPS) → AL(商), AH(余)
- 字 除 法:(DX, AX) / (OPS) → AX (商), DX (余)
- 双字除法:(EDX, EAX) / (OPS) → EAX (商), EDX
使用除法指令应注意的问题
(1) 除数为 0(2) 除法溢出(AX) = 1234, (BL)=1, (AX) / (BL) → AL
为了解决这种情况,需要考虑极端,也就是用来存储商的寄存器大小要等于被除数的长度,即比如这里就需要用AX来存商、DX来存余数,所以要将被除数和除数扩展到(EAX)和(BX)中。
符号扩展指令
将字节转换成字
CBW:将AL中的符号扩展至AH中。
将字转换成双字
CWD:将AX中的符号扩展至DX中。
将AX中的有符号数扩展为32位送EAX:CWDE
将EAX中的有符号数扩展为64位数送 EDX,EAX:CDQ
unsigned short us1,us2;
unsigned int ui;
short s1,s2 ;
int i;
us1 = ui / us2;
00EF1975 movzx ecx,word ptr [ebp-18h]
00EF1979 mov eax,dword ptr [ebp-24h]
00EF197C xor edx,edx
00EF197E div eax,ecx
00EF1980 mov word ptr [ebp-0Ch],ax
s1 = i / s2;
00EF1984 movsx ecx,word ptr [ebp-3Ch]
00EF1988 mov eax,dword ptr [ebp-48h]
00EF198B cdq
00EF198C idiv eax,ecx
00EF198E mov word ptr [ebp-30h],ax ;这里只取ax是因为要赋给short型的s1
正确理解 div eax, ecx:本质是 (edx, eax) / (ecx)
位运算指令
- 求反:NOT OPD ; ~ (OPD) →OPD
- 逻辑乘:AND OPD, OPS ; (OPD) & (OPS) →OPD
- 测试指令:TEST OPD, OPS ; (OPD) & (OPS)
- 逻辑加:OR OPD, OPS ; (OPD) | (OPS) →OPD
- 按位加:XOR OPD, OPS ; (OPD) ^ (OPS) →OPD
移位指令
(1)算术左移 SAL Shift Arithmetic Left(2)逻辑左移 SHL SHift Logical Left( 两种左移实际上完全一样 )(3)逻辑右移 SHR SHift Logical Right(4)算术右移 SAR Shift Arithmetic Right
Debug版本与Release版本的对比
例子1
int x, y;
y = x * 9;
imul eax, dword ptr [x], 9
mov dword ptr [y], eax
//Debug version
mov ecx, dword ptr [x]
lea eax, [ecx+ecx*8]
mov dword ptr [y], ecx
//Release version
例子2
int x, y;
y = x * 17;
imul eax, dword ptr [x], 11h
mov dword ptr [y], eax
//Debug version
mov ecx, dword ptr [x]
mov eax, ecx
shl eax, 4
add eax, ecx
mov dword ptr [y], eax
//Release version
位运算应用
编写C程序,判断两个整数是否相等,相等为1,不相等为 0
int isEqual(int x, int y) { return !(x ^ y); // return x==y; }
编写C程序,实现用按位运算方法实现计算 -x
-x 等于 ~x+1
NOT x
INC x
用乘法指令实现 MOV EAX,x
IMUL EAX, -1
MOV x, EAX
用减法指令实现 MOV EAX,0
SUB EAX, x
MOV x, EAX
试用不同指令将(AX)置0。
MOV AX, 0
SUB AX, AX
AND AX, 0
XOR AX, AX
SHL AX, 16
试用不同的指令,将AX的高、低字节内容互换。
XCHG AH, AL(交换 AH 和 AL 寄存器中的内容。)
ROL AX, 8(将 AX 寄存器中的值向左循环移动 8 位)
ROR AX, 8(将 AX 寄存器中的值向右循环移动 8 位)
只使用位运算、算术运算等指令,不使用转移之类的语句,实现求一个数的绝对值
int abs_op(int x){
int y,mask;
mask = x >> 31; //算术右移,得到FFFFFFFF/00000000
y = (x ^ mask) + ~mask + 1 ; //method 1:
y = (x ^ mask) + (1-mask) - 1; //method 2
y = ((x & 0x80000000) >> 31) + (x ^ mask); //method 3
return y;
}
//注意运算的优先级
//单纯的 x >> 31 是算术右移
//(x & 0x80000000)>> 31 是逻辑右移
不使用转移之类的语句,判断两个有符号数相加 是否出现溢出。
int addOK(int x, int y)
int sum = x + y;
int x_neg = x >> 31;
int y_neg = y >> 31;
int s_neg = sum >> 31;
/* Overflow when x and y have same sign, but s is different */
return !(~(x_neg ^ y_neg) & (x_neg ^ s_neg));
}
复杂指令集和精简指令集
按指令格式的复杂度来分,有两种类型计算机:
- 复杂指令集计算机 CISC (Complex Instruction Set Computer)
早期CISC设计风格的主要特点
(1) 指令系统复杂变长操作码 / 变长指令字 / 指令多 / 寻址方式多 / 指令格式多(2) 指令周期长绝大多数指令需要多个时钟周期才能完成(3) 各种指令都能访问存储器除了专门的存储器读写指令外,运算指令也能访问存储器(4) 采用微程序控制(5) 难以进行编译优化来生成高效目标代码CISC的缺陷–日趋庞大的指令系统使计算机的研制周期变长,–难以保证设计的正确性,难以调试和维护,–因指令操作复杂而增加机器周期,从而降低了系统性能。
- 精简指令集计算机 RISC (Reduce Instruction Set Computer)
(1) 简化的指令系统指令少 / 寻址方式少 / 指令格式少 / 指令长度一致
(2) 以RR方式工作除Load/Store指令可访存外,其余指令都只访问寄存器
(3) 指令周期短以流水线方式工作, 因而除Load/Store指令外,其他简单指令都只需一个或一个不到的时钟周期就可完成
(4) 采用大量通用寄存器,以减少访存次数(5) 采用硬连线路控制器,不用或少用微程序控制(6) 采用优化的编译系统,力求有效地支持高级语言程序MIPS是典型的RISC处理器,82年以来新的指令集大多采用RISC体系结构x86 因为“兼容”的需要,保留了CISC的风格,同时也借鉴了RISC思想。
Microcomputer without interlocked pipeline stages