一、乘除运算指令
- 乘除运算指令区分有符号数与无符号数
- 对状态标志的影响,和加减指令相比不是很自然。
(1)无符号数乘法指令
| 名称 |
MUL(无符号乘法指令) |
| 格式 |
MUL OPRD |
| 动作 |
乘数是OPRD,被乘数位于AL、AX或EAX中(由OPRD的尺寸决定,乘数和被乘数的尺寸一致),相乘后乘积尺寸翻倍:16位乘积送到AX;32位乘积送DX:AX;64位乘积送EDX:EAX |
| 合法值 |
OPRD:通用寄存器、存储单元; |
| 注意 |
OPRD不能是立即数 |
|
乘数和被乘数的尺寸一致 |
MUL BL //8位乘 乘积在AX
MUL ECX //32位乘 乘积在DX:AX
MUL DX //16位乘 乘积在EDX:EAX
(1)有符号数乘法指令
- 单操作数形式:
| 名称 |
IMUL(单操作数乘法指令) |
| 格式 |
IMUL OPRD |
| 动作 |
乘数是OPRD,被乘数位于AL、AX或EAX中(由OPRD的尺寸决定,乘数和被乘数的尺寸一致),乘法运算时把二者看作有符号数,相乘后乘积尺寸翻倍:16位乘积送到AX;32位乘积送DX:AX;64位乘积送EDX:EAX |
| 合法值 |
OPRD:通用寄存器、存储单元; |
| 注意 |
OPRD不能是立即数 |
|
乘数和被乘数的尺寸一致 |
IMUL CL
IMUL DWORD PTR [EBP+12] //双字存储单元
- 双操作数形式:
| 名称 |
IMUL(双操作数乘法指令) |
| 格式 |
IMUL DEST,SRC |
| 动作 |
DEST和SRC相乘后送到DEST,乘法运算时把二者看作有符号数 |
| 合法值 |
DEST:16位或者32位通用寄存器 |
|
SRC:通用寄存器、存储单元、立即数 |
| 注意 |
DEST不能是8位寄存器 |
|
SRC是通用寄存器、存储单元时,尺寸需要和目的操作数一致 |
|
SRC是立即数时,尺寸不能超过目的操作数 |
- 三操作数形式:和
MUL相比
| 名称 |
IMUL(三操作数乘法指令) |
| 格式 |
IMUL DEST,SRC1,SRC2 |
| 动作 |
SRC1 和SRC2相乘后送到DEST,乘法运算时把二者看作有符号数 |
| 合法值 |
SRC1:寄存器、存储单元 |
|
SRC2:立即数 |
| 注意 |
SRC1和SRC2都看作有符号数 |
|
SRC1尺寸需要和目的操作数一致 |
|
SRC2尺寸不能超过目的操作数 |
IMUL BX
IMUL EBX, ECX
IMUL AX,CX,3
IMUL EDX,DWORD PTR [ESI],5
IMUL AX,7
IMUL AX,AX,7
(3)无符号数除法指令
| 名称 |
DIV(无符号数除法指令) |
| 格式 |
DIV OPRD |
| 动作 |
除数是OPRD |
|
被除数位于AX、DX:AX或EDX:EAX中(被除数的尺寸是OPRD两倍) |
|
商在AL、AX或者EAX中(尺寸与oprd相同) |
|
余数在AH、DX或者EDX中(尺寸与oprd相同) |
| 合法值 |
OPRD:寄存器、存储单元 |
| 注意 |
必须防止除溢出,除数不能为0,商不能太大超出存放位置尺寸 |
|
OPDR不能是立即数 |
DIV BL //除数8位
DIV ESI //除数32位
DIV CX //除数16位
(4)有符号数除法指令
| 名称 |
IDIV(有符号数除法指令) |
| 格式 |
IDIV OPRD |
| 动作 |
除数是OPRD |
|
被除数位于AX、DX:AX或EDX:EAX中(尺寸为OPRD两倍) |
|
商在AL、AX或者EAX中(尺寸同OPRD) |
|
余数在AH、DX或者EDX中(尺寸同OPRD) |
| 合法值 |
OPRD:寄存器、存储单元 |
| 注意 |
必须防止除溢出,除数不能为0,商绝对值不能太大超出存放位置尺寸 |
|
如果不能整除,余数的符号与被除数一致,而且余数的绝对值小于除数的绝对值。 |
|
OPDR不能是立即数 |
(5)符号扩展指令
- 字节转换为字
| 名称 |
CBW(字节转换为字指令) |
| 格式 |
CBW |
| 动作 |
指令把AL中的符号扩展到AH |
| 注意 |
若AL的最高有效位为0,则AH=0;若AL的最高有效位为1,则AH=0FFH,也即AH的8位全都为1 |
MOV AX, 3487H //AX=3487H
CBW //AX=FF87H
MOV AX, 8734H
CBW //AX=0034H
- 字转换为双字
| 名称 |
CWD(字转换为双字指令) |
| 格式 |
CWD |
| 动作 |
指令把AX中的符号扩展到DX |
| 注意 |
若AX的最高有效位为0,则DX=0;若AX最高有效位为1,则DX=0FFFFH,也即DX的16位全都为1 |
MOV AX, 3487H //AX=3487H
CWD //DX=0000H, AX=3487H
MOV AX, 8734H
CWD //DX=FFFFH, AX=8734H
| 名称 |
CWDE(字转换为双字指令) |
| 格式 |
CWDE |
| 动作 |
指令把AX中的符号扩展到EAX高16位 |
| 注意 |
AX的最高有效位为0,则EAX的高16位都为0;若AX的最高有效位为1,则EAX的高16位都为1。 |
MOV AX, 3487H //AX=3487H
CWDE //EAX=00003487H
MOV AX, 8734H
CWDE //EAX=FFFF8734H
- 双字转换为四字
| 名称 |
CDQ(双字转换为四字指令) |
| 格式 |
CDQ |
| 动作 |
指令把EAX中的符号扩展到EDX |
| 注意 |
若EAX的最高有效位为0,则DX=0;若AX最高有效位为1,则DX=0FFFFFFFFH,也即EDX的16位全都为1 |
MOV EAX, 12563487H ;EAX=12563487H
CDQ ;EDX=00000000H, EAX=12563487H
综合示例:演示除法指令和符号扩展指令的使用
#include <stdio.h>
int main()
{
int quotient, remainder; //为了输出结果,安排两个变量
_asm
{
MOV AX, -601
MOV BL, 10
IDIV BL //除数是BL,被除数是AX,余数在AH,商在AL
MOV BL, AH //先临时保存余数
;
CBW //商在AL,符号扩展到AX
CWDE //AX符号扩展到EAX
MOV quotient, EAX
;
MOV AL, BL //余数送到AL
CBW //AL符号扩展到AX
CWDE //AX符号扩展到EAX
MOV remainder, EAX
}
printf("quotient= %d\n", quotient); //显示为-200
printf("remainder= %d\n", remainder); //显示为-1
return 0;
}
- 符号扩展传送指令
| 名称 |
MOVSX(符号扩展传送指令指令) |
| 格式 |
MOVSX DEST,SRC |
| 动作 |
把源操作数符号扩展后送至目的操作数DEST |
| 合法值 |
SRC:通用寄存器、存储单元 |
|
DEST:通用寄存器 |
| 注意 |
目的操作数的尺寸必须大于源操作数的尺寸。源操作数的尺寸可以是8位或者16位;目的操作数的尺寸可以是16位或者32位 |
MOV AL, 85H //AL=85H
MOVSX EDX, AL //EDX=FFFFFF85H
MOVSX CX, AL //CX=FF85H
MOV AL, 75H //AL=75H
MOVSX EAX, AL //EAX=00000075H
- 零扩展传送指令
| 名称 |
MOVZX(零扩展传送指令指令) |
| 格式 |
MOVZX DEST,SRC |
| 动作 |
指令把源操作数SRC零扩展后送至目的操作数DEST |
| 合法值 |
SRC:通用寄存器、存储单元 |
|
DEST:通用寄存器 |
| 注意 |
目的操作数的尺寸必须大于源操作数的尺寸。源操作数的尺寸可以是8位或者16位;目的操作数的尺寸可以是16位或者32位 |
MOV DX, 8885H ;DX=8885H
MOVZX ECX, DL ;ECX=00000085H
MOVZX EAX, DX ;EAX=00008885H
综合示例
//参数是有符号字符型,返回值是int
int cf310(char x, char y)
{
return ( x + 22 ) / y ;
}
//转汇编如下
push ebp
mov ebp, esp
movsx eax, BYTE PTR [ebp+8] //把参数x符号扩展后送到eax
add eax, 22
movsx ecx, BYTE PTR [ebp+12] //把参数y符号扩展后送到ecx
cdq //符号扩展,形成64位的被除数
idiv ecx
pop ebp
ret
//参数是无符号字符型,返回值是unsigned int
unsigned int cf311(unsigned char x, unsigned char y)
{
return (unsigned)( x + 22 ) / y ;
}
//转汇编如下
push ebp
mov ebp, esp
movzx eax, BYTE PTR [ebp+8] //把参数x零扩展后送到eax
add eax, 22
movzx ecx, BYTE PTR [ebp+12] //把参数x零扩展后送到ecx
xor edx, edx //零扩展,形成64位的被除数
div ecx
pop ebp
ret
二、逻辑运算指令
- C语言中有一组按位逻辑运算符
- 按位取反运算符 ~
- 按位与运算符 &
- 按位或运算符 |
- 按位异或运算符 ^
- 处理器提供一组逻辑运算指令
- 否指令 NOT
- 与指令 AND
- 或指令 OR
- 异或指令 XOR
- 关于逻辑运算指令的通用说明
- 只有通用寄存器或存储单元可作为目的操作数,用于存放运算结果。
- 如只有一个操作数,则该操作数既是源又是目的。
- 如有两个操作数,那么最多只能有一个是存储单元,源操作数可以是立即数。
- 存储单元可采用各种存储器操作数寻址方式。
- 操作数可以是字节、字或者双字。如果有两个操作数,尺寸必须一致。
(1)否运算指令
| 名称 |
NOT(否运算指令) |
| 格式 |
NOT OPRD |
| 动作 |
把操作数OPRD按位取反,然后送回OPRD |
| 合法值 |
OPRD:通用寄存器、存储单元 |
NOT CL
NOT EAX
NOT BX
(2)与运算指令
| 名称 |
AND(与运算指令) |
| 格式 |
AND DEST,SRC |
| 动作 |
指令对两个操作数进行按位的逻辑与运算,结果送到目的操作数DEST |
| 合法值 |
SRC:通用寄存器、存储单元、立即数 |
|
OPRD:通用寄存器、存储单元 |
| 注意 |
两个操作数最多只能有一个是存储单元 |
AND ECX,ESI
MOV AX,3437H //AX=3437H
AND AX,0F0FH //AX=0407H
(3)或运算指令
| 名称 |
OR(或运算指令) |
| 格式 |
OR DEST,SRC |
| 动作 |
指令对两个操作数进行按位的逻辑或运算,结果送到目的操作数DEST |
| 合法值 |
SRC:通用寄存器、存储单元、立即数 |
|
OPRD:通用寄存器、存储单元 |
| 注意 |
两个操作数最多只能有一个是存储单元 |
OR CL,CH
OR EBX,EAX
MOV AL,41H ;AL=01000001B,后缀B表示二进制
OR AL,20H ;AL=01100001B
(4)异或运算指令
| 名称 |
XOR(异或运算指令) |
| 格式 |
XOR DEST,SRC |
| 动作 |
指令对两个操作数进行按位异或的逻辑“异或”运算,结果送到目的操作数DEST |
| 合法值 |
SRC:通用寄存器、存储单元、立即数 |
|
OPRD:通用寄存器、存储单元 |
| 注意 |
两个操作数最多只能有一个是存储单元 |
MOV AL,34H ;AL=00110100B,符号B表示二进制
MOV BL,0FH ;BL=00001111B
XOR AL,BL ;AL=00111011B
XOR ECX,ECX ;ECX=0,CF=0
(5)测试指令
| 名称 |
TEST(测试指令) |
| 格式 |
TEST DEST,SRC |
| 动作 |
类似指令AND,把两个操作数进行按位“与”,但结果不送到目的操作数DEST,仅仅影响状态标志 |
| 合法值 |
SRC:通用寄存器、存储单元、立即数 |
|
OPRD:通用寄存器、存储单元 |
| 注意 |
两个操作数最多只能有一个是存储单元 |
|
标志ZF、PF和SF反映运算结果,标志CF和OF被清0 |
TEST AL, BL
TEST EDX, ECX
//检查AL中的位6和位2是否有一位为1
TEST AL,01000100B //符号B表示二进制
//随后,判断标志位ZF,若ZF=0,则这两位上都是0
三、移位指令
关于移位的几个要素:
- 移动方式
- 移动方向
- 移动位数
(1)一般移位指令
| 名称 |
算术左移SAL SAL OPRD,count |
|
逻辑左移SHL SHL OPRD,count |
|
算术右移SAR SAR OPRD,count |
|
逻辑右移SHR SHR OPRD,count |
| 动作 |
算术左移SAL:动作一样,左移count位。每左移一位,移出的最高位进入CF,最低位补0 |
|
逻辑左移SHL:同算术左移SAL |
|
算术右移SAR:右移count位。每右移一位,移出的最低位进入CF,符号(最高)位不变 |
|
逻辑右移SAR:右移count位。每右移一位,移出的最低位进入CF,符号(最高)位补0 |
| 合法值 |
count:8位立即数、存储单元、寄存器CL |
|
OPRD:通用寄存器、存储单元(字节、字、双字) |
| 注意 |
通过截取count的低5位,实际的移位数被限于0到31之间 |

SAL、SHL示例
MOV EBX, 7400EF9CH //EBX=7400EF9CH
ADD EBX, 0 //EBX=7400EF9CH,CF=0,SF=0,ZF=0,PF=1
SHL EBX, 1 //EBX=E801DF38H,CF=0,SF=1,ZF=0,PF=0
MOV CL, 3 //CL=3
SHL EBX, CL //EBX=400EF9C0H,CF=1,SF=0,ZF=0,PF=1
SHL EBX, 16 //EBX=F9C00000H,CF=0,SF=1,ZF=0,PF=1
SHL EBX, 12 //EBX=00000000H,CF=0,SF=0,ZF=1,PF=1
//实现把寄存器AL中的内容(设为无符号数)乘以10
//算术(逻辑)左移1位,相当于乘以2
XOR AH, AH //AH=0
SHL AX, 1 //2*X
MOV BX, AX //暂存2*X
SHL AX, 2 //8*X
ADD AX, BX //8*X+2*X
SAR示例
//算术右移1位,相当于有符号数除以2
MOV DX, 82C3H //DX=82C3H
SAR DX, 1 //DX=C161H,CF=1,SF=1,ZF=0,PF=0
MOV CL, 3 //CL=3
SAR DX, CL //DX=F82CH,CF=0,SF=1,ZF=0,PF=0
SAR DX, 4 //DX=FF82H,CF=1,SF=1,ZF=0,PF=1
SHR示例
MOV DX, 82C3H //DX=82C3H
SHR DX, 1 //DX=4161H,CF=1,SF=0,ZF=0,PF=0
MOV CL, 3 //CL=3
SHR DX, CL //DX=082CH,CF=0,SF=0,ZF=0,PF=0
SHR DX, 12 //DX=0000H,CF=1,SF=0,ZF=1,PF=1
(2)循环移位指令
| 名称 |
左循环移位指令 ROL ROL OPRD,count |
|
右循环移位指令 ROR ROR OPRD,count |
|
带进位左循环移位指令 RCL RCL OPRD,count |
|
带进位右循环移位指令 RCR RCR OPRD,count |
| 动作 |
见下图 |
| 合法值 |
count:8位立即数、存储单元、寄存器CL |
|
OPRD:通用寄存器、存储单元(字节、字、双字) |
| 注意 |
通过截取count的低5位,实际的移位数被限于0到31之间 |

循环移位指令的示例
MOV DX, 82C3H ;DX=82C3H
ROL DX, 1 ;DX=0587H, CF=1
MOV CL, 3 ;CL=3
ROL DX, CL ;DX=2C38H, CF=0
MOV EBX, 8A2035F7H ;EBX=8A2035F7H
ROR EBX, 4 ;EBX=78A2035FH, CF=0
STC ;CF=1(设置进位标志)
RCL EBX, 1 ;EBX=F14406BFH, CF=0
RCR EBX, CL ;EBX=DE2880D7H, CF=1
(3)双精度移位指令
| 名称 |
双精度左移SHLD SHLD OPRD1,OPRD2,count |
|
双精度右移SHRD SHRD OPRD1,OPRD2,count |
| 动作 |
双精度左移SHLD :OPDR1左移count位,低端空出的位用OPDR2高端的count位填补,但OPDR2不变。OPDR1中最后移出的放在CF |
|
双精度右移SHRD :OPDR1右移count位,高端空出的位用OPDR2低端的count位填补,但OPDR2不变。OPDR1中最后移出的放在CF |
| 合法值 |
OPDR1:通用寄存器、存储单元(字、双字) |
|
OPRD:通用寄存器(字、双字) |
| 注意 |
两个操作数尺寸必须一致 |
|
count表示移位的位数,可以是一个8位立即数,也可以是寄存器CL。寄存器CL表示移位数由CL的值决定。通过截取count的低5位,移位数被限于0到31之间 |
示例
MOV AX, 8321H
MOV DX, 5678H
SHLD AX, DX, 1 //AX=0642H,DX=5678H,CF=1,OF=1
SHLD AX, DX, 2 //AX=1909H,DX=5678H,CF=0,OF=0
MOV EAX, 01234867H
MOV EDX, 5ABCDEF9H
SHRD EAX, EDX, 4 //EAX=90123486H,CF=0,OF=1
MOV CL, 8
SHRD EAX, EDX, CL //EAX=F9901234H,CF=1,OF=0