指令速查¶
约 3851 个字 174 行代码 预计阅读时间 21 分钟
数据传送指令¶
MOV
- 指令格式: mov dest, src
- 指令效果: dest = src
- 注意:
mov
指令不影响任何标志位- 两个操作数必须等宽
- 两个操作数不能同时为
Mem
(内存变量) - 不能把常数或段寄存器赋值给段寄存器
push
- 指令格式: push reg16 | mem16 | reg32 | mem32
- 指令效果: 根据操作数的宽度,开辟栈空间并压入栈
- 注意:
push
指令不影响任何标志位- 不支持 8 位宽度的操作数
POP
- 指令格式: pop reg16 | mem16 | reg32 | mem32
- 指令效果: 根据操作数的宽度,从堆栈中弹出值
- 注意:
pop
指令不影响任何标志位- 不支持 8 位宽度的操作数
xchg
- 指令格式: xchg op1, op2
- 指令效果: 交换 op1 和 op2
- 注意:
xchg
不影响任何标志位;且不能对段寄存器操作
IN
- 指令格式: in al, port
- 指令效果: 从port端口号读取一个字节并保存在 AL 中
- 注意: 端口号的范围为
[0000h,0FFFFh]
,其中范围[00h,0FFh]
可以使用常数作为端口号;超出这个范围只能使用寄存器 DX 作为端口号
OUT
- 指令格式: out port, al
- 指令效果: 将 AL 的值写入port端口号
- 注意: 同上,当端口地址\(\gt 0FFh\)时,必须使用
out dx, al
此处省略不常用的地址传送指令、标志寄存器传送指令
LEA
- 指令格式: lea dest, src
- 指令效果: 取变量的偏移地址
- 注意: 等效于
mov dx, offset abc
但是可以用来作加法lea dx, ds:[bx+si+3]
正确mov dx, bx+si+3
错误lea eax, [eax+eax*4]
利用lea
做常数乘法- 对于16位,只有
bx,bp,si,di
可以在方括号内,且两两不能同时出现;32位,则可以包括四个通用寄存器
EAX = EAX * 10
由于里面的乘数只能是 2,4,6,8,并且同一个数不能重复出现,想要计算 EAX *= 10
可以:
LES
- 指令格式: les dest, src
- 指令效果: 将src指向地址的高十六位写入es,低十六位写入dest
- 注意: 由于采取小端存储法,所以是后面的存入es,例如:
- 从
ds:[bx]
连续存放四个字节:9A,78,56,34
les di, ds:[bx]
ES=3456h, DI=789Ah
- 从
LDS
- 指令格式: lds dest, src
- 指令效果: 将src指向地址的高十六位写入ds,低十六位写入dest
- 注意: 注意事项同上
习题
设有一内存字X,X的偏移地址与段地址按顺序存放在从地址1000:10F0起的内存单元中,请写出汇编指令把X的值赋值给寄存器AX。(要求使用LDS指令)
设有一内存字节Y,Y的偏移地址与段地址按顺序存放在从地址1000:10F0起的内存单元中,请写出汇编指令把Y的值加1。(要求使用LES指令)
扩充指令¶
CBW
- 指令格式: cbw
- 指令效果: 把 AL 中的值符号扩充至 AX 中
- 注意: Convert Byte to Word
- 例子:
cbw ; when al = 7Fh
=>AX = 007Fh
cbw ; when al = 0FCh
=>AX = 0FFFCh
CWD
- 指令格式: cwd
- 指令效果: 把 AX 中的值符号扩充至 DX:AX 中
- 注意: Convert word to dword
CDQ
- 指令格式: cdq
- 指令效果: 把 EAX 中的值符号扩充至 EDX:EAX 中
- 注意: Convert dword to qword
MOVSX
- 指令格式: movsx dest, src
- 指令效果: 把值 src 符号扩充至 dest 中
- 注意: dest 的位宽需大于 src
- 例子:
movsx bx, al ; al = 80h
=>bx = 0FF80h
MOVZX
- 指令格式: movzx dest, src
- 指令效果: 把值 src 零扩充至 dest 中
- 注意: dest 的位宽需大于 src
- 例子:
movzx bx, al ; al = 80h
=>bx = 0080h
XLAT
- 指令格式: xlat
- 指令效果: al = byte ptr ds:[bx+al]
利用 xlat 指令和转换表将十进制整数转换成十六进制并输出:
算数运算指令¶
加法指令¶
ADD
- 指令格式: add dest, src
- 指令效果: dest += src
ADC
- 指令格式: adc dest, src
- 指令效果: dest += src + CF
- 注意: 即带进位加法,add with carry
INC
- 指令格式: inc op
- 指令效果: op++
- 注意: inc 指令不影响 CF
习题
求 12345678h+4243BCBCh 的和,要求结果存放在寄存器DX:AX中:
减法指令¶
SUB
- 指令格式: sub dest, src
- 指令效果: dest -= src
SBB
- 指令格式: sbb dest, src
- 指令效果: dest = dest - src - CF
- 注意: 即带借位减法,subtract with borrow
DEC
- 指令格式: dec op
- 指令效果: op--
- 注意: dec 指令不影响 CF
NEG
- 指令格式: neg op
- 指令效果: op = -op
- 注意: 相当于取反加一(求补码, op = not op + 1),非零数求补后 cf=1,0求补后 cf=0
CMP
- 指令格式: cmp op1, op2
- 指令效果: compare op1 - op2
- 注意: cmp 指令并不计算差值,但会像 sub op1, op2 影响状态标志,后面通常接跳转指令
试写出对存放在DX:AX中的32位数进行求补(neg)的汇编指令序列
乘法指令¶
MUL
- 指令格式: mul src
- 指令效果: 非符号数乘法
- src 为 8 位时,ax = al * src
- src 为 16 位时,dx:ax = ax * src
- src 为 32 位时,edx:eax = eax * src
- Example: DX:AX=
123400h
➡ DX=0012h
, AX=3400h
IMUL
- 指令格式: imul src
- 指令效果: 符号数乘法
- src 为 8 位时,ax = al * src
- src 为 16 位时,dx:ax = ax * src
- src 为 32 位时,edx:eax = eax * src
IMUL 的第二类用法
Intel 第二代CPU为IMUL
新增了用法,支持两个或三个操作数。以下四组指令都实现对eax
乘10,不同的是,lea
和imul
指令并不能处理乘法的溢出:
除法指令¶
DIV
- 指令格式: div src
- 指令效果: 非符号数除法
- src 为 8 位时,al = ax / src, ah = ax % src
- src 为 16 位时,ax = dx:ax / src, dx = dx:ax % src
- src 为 32 位时,eax = edx:eax / src, edx = edx:eax % src
- 注意: 若除数为 0 或者保存商的寄存器无法容纳商时会发生除法溢出,此时 CPU 会在除法指令上方插入并执行一条 int 00h 指令(除法的异常处理)
IDIV
- 指令格式: idiv src
- 指令效果: 符号数除法
- src 为 8 位时,al = ax / src, ah = ax % src
- src 为 16 位时,ax = dx:ax / src, dx = dx:ax % src
- src 为 32 位时,eax = edx:eax / src, edx = edx:eax % src
- 注意: 若除数为 0 或者保存商的寄存器无法容纳商时会发生除法溢出,此时 CPU 会在除法指令上方插入并执行一条 int 00h 指令
逻辑运算和移位指令¶
逻辑运算指令¶
AND
- 指令格式: and dest, src
- 指令效果: dest = dest & src
OR
- 指令格式: or dest, src
- 指令效果: dest = dest | src
XOR
- 指令格式: xor dest, src
- 指令效果: dest = dest ^ src
NOT
- 指令格式: not op
- 指令效果: op = ^op
TEST
- 指令格式: test dest, op
- 指令效果: test dest & src
- 注意: test 指令并不保存结果值,但会像 and dest, src 一样影响状态标志
移位指令¶
SHL
- 指令格式: shl dest, count
- 指令效果: dest <<= count & 1Fh
- 注意: 右侧补0,左侧溢出位落入 CF;count 只能是 idata 或 cl
- 若为 .386,则 idata 可为任意大小的 8 位常数
- 若为 8086,则 idata 只能为 1
- 其它移位指令和 shl 指令规定相同,不再做阐述
SHR
- 指令格式: shr dest, count
- 指令效果: dest >>= count & 1Fh
- 注意: 左侧补0,右侧溢出位落入 CF;count 只能是 idata 或 cl
SAL
- 指令格式: sal dest, count
- 指令效果: dest <<= count & 1Fh
- 注意: 算术左移指令,与 SHL 完全相同
SAR
- 指令格式: sar dest, count
- 指令效果: dest >>= count & 1Fh
- 注意: 左侧不变,右侧溢出位落入 CF;count 只能是 idata 或 cl
ROL
- 指令格式: rol dest, count
- 指令效果: 对 dest 循环左移 count & 1Fh 位,最高位回到最低位同时移到 CF 中
- 注意: rotate left;count 只能是 idata 或 cl
ROR
- 指令格式: ror dest, count
- 指令效果: 对 dest 循环右移 count & 1Fh 位,最低位回到最高位同时移到 CF 中
- 注意: rotate right;count 只能是 idata 或 cl
RCL
- 指令格式: rcl dest, count
- 指令效果: 带进位循环左移,即 CF 加在 dest 左侧一起循环左移
- 注意: count 只能是 idata 或 cl
RCR
- 指令格式: rcr dest, count
- 指令效果: 带进位循环右移,即 CF 加在 dest 右侧一起循环右移
- 注意: count 只能是 idata 或 cl
试写出把32位数 3F7E59ACh 逻辑左移2位的汇编指令序列,要求结果存放在DX:AX中
控制转移指令¶
根据跳转距离的远近,jmp
分为三类:
- 短跳(short jump) :跳转距离用一个字节表示,机器码以
EB
开头- 短跳后面只能接目标偏移地址或标号;而近跳还可以接 16 位寄存器或 16 位变量
- 短跳机器码的 idata 为一个字节,对应大小为目标地址减去下条指令的偏移地址($+2)
- 近跳(near jump) :跳转距离或目标地址用一个字表示,机器码以
E9
开头- 机器码
E9FD1E
对应指令1D3E:0100: jmp 2000h
即跳转到地址1D3E:2000
- 其中
FD1E
含义为1EFD = 2000h - 0103h
,即目标地址减去下条指令的偏移地址 - 偏移地址在机器码中也是小端存储
- 机器码
- 远跳(far jump) :目标地址用一个远指针表示(段地址:偏移地址),机器码以
EA
开头- 机器码
EA0000FFFF
对应指令jmp 0FFFFh:0000h
。但是实际上远跳不能直接接常数地址,下面两种方式可以实现相同效果:
- 机器码
主要通过指令机器码长度区分这三个跳
不过我们编写代码的时候直接使用 jcc
指令即可,区分类型的事交给机器。
Jcc指令 | 含义 | 跳转条件 | 解释 |
---|---|---|---|
jc | 有进位则跳 | CF==1 | Jump if carry 有进位或借位 |
jnc | 无进位则跳 | CF==0 | Jump if no carry 无进位或借位 |
jz | 为零则跳 | ZF==1 | Jump if zero 运算结果为 0 |
jnz | 不为零则跳 | ZF==0 | Jump if not zero 运算结果不为 0 |
js | 有符号位则跳 | SF==1 | Jump if sign 符号数运算结果为负 |
jns | 无符号位则跳 | SF==0 | Jump if no sign 符号数运算结果为正 |
jo | 有溢出则跳 | OF==1 | Jump if overflow 符号数运算结果有错 |
jno | 无溢出则跳 | OF==0 | Jump if not overflow 符号数运算结果正确 |
jp | 有奇偶校验标志则跳 | PF==1 | Jump if parity 运算结果低八位1的个数为偶 |
jnp | 无奇偶校验标志则跳 | PF==0 | Jump if no parity 运算结果低八位1的个数为奇 |
jcxz | CX为零则跳 | CX==0 | Jump if CX is zero CX的值为 0 |
jecxz | ECX为零则跳 | ECX==0 | Jump if ECX is zero ECX的值为 0 |
ja | 无符号大于则跳 | CF==0 & ZF==0 | 与 jnbe 完全等价 |
jae | 无符号大于等于则跳 | ||
jb | 无符号小于则跳 | 与 jc , jnae 完全等价 | |
jbe | 无符号小于等于则跳 |
LOOP
- 指令格式: loop dest
- 指令效果: 循环跳转 CX 次
- if(--CX != 0) IP = dest;
- 注意: 先作减法,再判断是否为0,因此将CX赋值为0可循环的次数最多(1+FFFF);如果不希望CX为0时进入循环,应该在进入循环前使用
jcxz
指令跳转到循环出口
LOOPZ
- 指令格式: loopz dest
- 指令效果: 等于零则循环,最多循环 CX 次
- if(ZF == 1 && --CX != 0) IP = dest;
- 注意: 不影响标志位
LOOPNZ
- 指令格式: loopnz dest
- 指令效果: 不等于零则循环,最多循环 CX 次
- if(ZF == 0 && --CX != 0) IP = dest;
- 注意: 不影响标志位
十进制调整指令¶
似乎不在考试范围内
Binary Coded Decimal 是指用二进制编码的十进制数。
- 压缩BCD码 以4位二进制表示1位十进制
- \(37_{Decimal}\Rightarrow 37h\)
- 非压缩BCD码 以8位二进制表示1位十进制
- \(37_{Decimal}\Rightarrow 0307h\)
- 高位没有意义,可以为任意值,如
06h
,36h
都表示十进制数 6
# 压缩BCD码调整指令
DAA
- 指令格式: daa
- 指令效果: 压缩BCD码加法的十进制调整,将AL调整回BCD码
- if (af == 1 || (al&0Fh) > 9) al += 6, af = 1; else af = 0
- if (cf == 1 || al > 9Fh) al += 60h, cf = 1; else cf = 0
DAS
- 指令格式: das
- 指令效果: 压缩BCD码减法的十进制调整,将AL调整回BCD码
- if (af == 1 || (al&0Fh) > 9) al -= 6, af = 1; else af = 0
- if (cf == 1 || al > 9Fh) al -= 60h, cf = 1; else cf = 0
# 非压缩BCD码调整指令
AAA
- 指令格式: aaa
- 指令效果: 加法的 ASCII 调整,在 al 被做加法后调整 ax 为非压缩BCD码(ah也被修改)
- if (af == 1 || (al&0Fh) > 9) al = (al+6)&0Fh, ah += 1, af = 1, cf = 1
- else af = 0, cf = 0
AAS
- 指令格式: aas
- 指令效果: 减法的 ASCII 调整,在 al 被做减法后调整 ax 为非压缩BCD码(ah也被修改)
- if (af == 1 || (af&0Fh) > 9) al = (al-6)&0Fh, ah -= 1, af = 1, cf = 1
- else af = 0, cf = 0
AAM
- 指令格式: aam
- 指令效果: 乘法的 ASCII 调整,在 al 被做乘法后调整 ax 为非压缩BCD码(ah也被修改)
- ah = al / 10, al = al % 10
AAD
- 指令格式: aam
- 指令效果: 除法的 ASCII 调整,在 al 被做除法后调整 ax 为非压缩BCD码(ah也被修改)
- al = ah * 10 + al, ah = 0
字符串操作指令¶
字符串操作指令包括:
movs
字符串复制 move stringcmps
字符串比较 compare stringscas
字符串搜索 scan stringstos
写入字符串 store stringlods
读取字符串 load string
与字符串操作指令相关的指令前缀包括:
rep
重复 repeatrepe
若相等则重复 repeat if equalrepz
若结果为零则重复 repeat if zero ,与repe
等效repne
若不相等则重复 repeat if not equalrepnz
若不为零则重复 repeat if not zero , 与repne
等效
字符串操作指令可以与指令前缀结合使用,也可以单独使用。单独使用表示仅执行一次字符串操作,而加了指令前缀则可以最多重复执行 CX 次。
REP MOVSB
- 指令格式: rep movsb
- 指令效果: 以字节为单位从
DS:[SI]
复制字符串到ES:[DI]
- byte ptr ES:[DI] = byte ptr DS:[SI];
- if(DF == 0) {SI++; DI++} 即正向复制
- if(DF == 1) {SI--; DI--} 即反向复制
- CX--; if(CX == 0) goto DONE;
REPE CMPSB
- 指令格式: repe cmpsb
- 指令效果: 以字节为单位比较
DS:[SI]
与ES:[DI]
的字符串,并将 SI 和 DI 停留在第一次不相等的地方- cmp byte ptr ES:[DI], byte ptr DS:[SI];
- if(DF == 0) {SI++; DI++} 即正向比较
- if(DF == 1) {SI--; DI--} 即反向比较
- CX--; if(CX == 0 || ZF != 0) goto DONE;
- 注意:
CMPS
指令不影响标志位,当退出该指令时,标志寄存器 FL 恢复;但是仍然可以用je
等指令实现判断跳转
可以自己试试下面的程序的执行效果:
$
这里数据段中有一句 slen = $ - offset s
,作用是计算数组 s 的长度。但是 slen
本身不会出现在数据段内,而是类似于宏的存在。
REPE SCASB
- 指令格式: repe scasb
- 指令效果: 在
ES:[DI]
指向的目标字符串中搜索 AL|AX|EAX 的值 - 注意: 基本原理基本同上,一般只利用 CX 前后的变化求字符串长度
用 repne scasb
求字符串长度:
REP STOSB
- 指令格式: rep stosb
- 指令效果: 把 AL|AX|EAX 的值写入
ES:[DI]
指向的目标字符串 - 注意: 基本原理基本同上
REP LODSB
- 指令格式: rep lodsb
- 指令效果: 从
ES:[DI]
中读取 x 个字节保存在 AL|AX|EAX 中 - 注意: 基本原理基本同上,x 为对应的字节长度