Skip to content

指令速查

约 3851 个字 174 行代码 预计阅读时间 21 分钟

数据传送指令

MOV

  • 指令格式: mov dest, src
  • 指令效果: dest = src
  • 注意:
    • mov 指令不影响任何标志位
    • 两个操作数必须等宽
    • 两个操作数不能同时为 Mem (内存变量)
    • 不能把常数或段寄存器赋值给段寄存器

Warning

mov byte ptr [bp+6+si], '$' ; 必须使用 byte ptr 指定宽度
mov [bp+6+si], bl           ; 这个不需要,自动对齐 dl 宽度

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 可以:

add eax, eax
lea eax, [eax+eax*4]

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指令)

1
2
3
4
mov ax, 1000h
mov ds, ax
lds bx, dword ptr ds:[10F0h]
mov ax, [bx]

设有一内存字节Y,Y的偏移地址与段地址按顺序存放在从地址1000:10F0起的内存单元中,请写出汇编指令把Y的值加1。(要求使用LES指令)

1
2
3
4
mov ax, 1000h
mov ds, ax
les di, dword ptr ds:[10F0h]
inc byte ptr es:[di]

扩充指令

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 指令和转换表将十进制整数转换成十六进制并输出:

.386
data segment use16
t db "0123456789ABCDEF"
x dd 2147483647
data ends

code segment use16
assume cs:code, ds:data
main:
  mov ax, data      ;\
  mov ds, ax        ; | ds:bx->t[0]
  mov bx, offset t  ;/
  mov cx, 8
  mov eax, [x]
next:
  rol eax, 4
  push eax
  and eax, 0Fh
  xlat
  mov ah, 2
  mov dl, al
  int 21h
  pop eax
  loop next
  mov ah, 4Ch
  int 21h
code ends
end main

算数运算指令

加法指令

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中:

1
2
3
4
mov ax, 5678h
mov dx, 1234h
add ax, 0BCBCh
adc dx, 4243h

减法指令

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)的汇编指令序列

1
2
3
4
5
neg ax    ; 若 ax 非零,则产生进位 CF=1
pushf     ; 保存 CF 状态
neg dx
popf      ; 恢复 CF 状态
sbb dx, 0 ; 减去 neg ax 产生的进位

乘法指令

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,不同的是,leaimul指令并不能处理乘法的溢出:

mov ebx, 10
mul ebx               ; edx:eax = eax * ebx

mov ebx, 10
imul eax, ebx         ; eax = eax * ebx

imul eax, eax, 10     ; eax = eax * Imm

add eax, eax
lea eax, [eax+eax*4]  ; eax = eax*2 + eax*8

除法指令

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中

1
2
3
4
5
6
7
    mov ax, 59ACh
    mov dx, 3F7Eh
    mov cx, 2
again:
    shl ax, 1
    rcl dx, 1
    loop again

控制转移指令

根据跳转距离的远近,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 。但是实际上远跳不能直接接常数地址,下面两种方式可以实现相同效果:
data segment
addr dw 0000h, 0FFFFh
data ends

code segment
assume cs:code, ds:data
main:
    mov ax, data
    mov ds, ax

    ; jmp 0FFFFh:0000h

    ; jmp dword ptr 32位变量
    jmp dword ptr [addr]

    ; 机器码方式
    db 0EAh
    dw 0
    dw 0FFFFh

    ; 假定 extra 段已正确定义
    mov word ptr es:[di], 0000h
    mov word ptr es:[di+4], 0FFFFh
    jmp dword ptr es:[di]
code ends
end main

主要通过指令机器码长度区分这三个跳

不过我们编写代码的时候直接使用 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 string
  • cmps 字符串比较 compare string
  • scas 字符串搜索 scan string
  • stos 写入字符串 store string
  • lods 读取字符串 load string

与字符串操作指令相关的指令前缀包括:

  • rep 重复 repeat
  • repe 若相等则重复 repeat if equal
  • repz 若结果为零则重复 repeat if zero ,与 repe 等效
  • repne 若不相等则重复 repeat if not equal
  • repnz 若不为零则重复 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 等指令实现判断跳转

可以自己试试下面的程序的执行效果:

comment @
将字符串s复制到到t中,并比较s和t是否相等
@

data segment
s db "I hate Monsterhunter:wild", 0Dh, 0Ah    ; , '$'
slen =  $ - offset s
equal_msg db "Equal", 0Dh, 0Ah, '$'
unequal_msg db "Not Equal", 0Dh, 0Ah, '$'
data ends

extra segment
t db slen dup(0)
extra ends

code segment
assume cs:code, ds:data, es:extra
compare:
    mov si, offset s           ; 为什么这里需要重新对 si 和 di 赋值? movsb 指令已经对其进行了++操作
    mov di, offset t
    mov cx, slen
    repe cmpsb                 ; 比较字符串
    je is_equal
not_equal:
    mov dx, offset unequal_msg
    jmp output
is_equal:
    mov dx, offset equal_msg
output:
    mov ah, 9                  ; 输出 ds:[dx] 的字符串,一直到遇到 '$'
    int 21h
    ret

main:
    mov ax, data
    mov ds, ax
    mov ax, extra
    mov es, ax
    mov si, offset s
    mov di, offset t
    mov cx, slen
    cld                        ; DF = 0
    rep movsb                  ; 字符串复制
    mov cx, slen
    mov bx, 0
L1:
    mov dl, byte ptr es:[bx]   ; 取数据,不能是 t:[bx];byte ptr 不加也行
    mov ah, 2                  ; 如果字符串以 '$' 结尾,则可以直接使用ah = 9输出dx中字符串
    int 21h
    inc bx
    loopnz L1                  ; CX--,若cx不等于零,则循环
    call compare
exit:
    mov ah, 1h
    int 21h
    mov ah, 4ch
    int 21h
code ends
end main

$

这里数据段中有一句 slen = $ - offset s,作用是计算数组 s 的长度。但是 slen 本身不会出现在数据段内,而是类似于宏的存在。

REPE SCASB

  • 指令格式: repe scasb
  • 指令效果:ES:[DI] 指向的目标字符串中搜索 AL|AX|EAX 的值
  • 注意: 基本原理基本同上,一般只利用 CX 前后的变化求字符串长度

repne scasb 求字符串长度:

data segment
s db "Sakura no toki", 0
slen dw 0
data ends

code segment
assume cs:code, ds:data
main:
    mov ax, data
    mov ds, ax
    mov es, ax
    mov di, offset s
    mov cx, 0FFFFh     ; 最大搜索次数
    xor al, al         ; AL = 0h
    cld                ; DF = 0
    repne scasb
    inc cx             ; 00h 不算在字符串内
    not cx             ; CX = 0FFFFh - CX = 搜索过的字符个数
    mov [slen], cx
    mov ah, 4Ch
    int 21h
code ends
end main

REP STOSB

  • 指令格式: rep stosb
  • 指令效果: 把 AL|AX|EAX 的值写入 ES:[DI] 指向的目标字符串
  • 注意: 基本原理基本同上

REP LODSB

  • 指令格式: rep lodsb
  • 指令效果:ES:[DI] 中读取 x 个字节保存在 AL|AX|EAX 中
  • 注意: 基本原理基本同上,x 为对应的字节长度
Comments: