Fork me on GitHub

8086汇编:指令系统(上)

8086汇编常用的指令,从功能上分,一般分为:

  • 数据传送指令
  • 算术指令
  • 逻辑指令
  • 控制转移指令
  • 标志处理指令
  • 串处理指令

这些指令书上网上都有很多介绍,我就不会全部详细介绍,只挑一些常用的、需要注意的重点说明。

数据传送指令

1. mov(传送)

1
mov dst, src

有以下需要注意的地方:

  1. dstsrc 类型必须匹配,无法确定类型时要显式指定类型。

    1
    2
    3
    4
    5
    6
    mov ax, 1 ; 正确!立即数会自动扩展类型
    mov ax, bl ; 错误!类型不匹配
    mov ax, [0] ; 可以运行,但不是预期结果,原因见 2
    mov ax, ds:[0] ; 正确!ax是字类型,ds:[0]也获取字类型
    mov ds:[0], 1 ; 错误!无法确定类型
    mov byte ptr ds:[0], 1 ; 正确!指定了访问的内存单元是一个字节单元
  2. 形如 mov ax, [0] 这类指令 Debug加载运行 和 masm编译器对它们的解释不同,Debug[idata]解释为一个内存单元,idata是内存单元的偏移地址;而编译器将[idata]解释为idata,一个立即数。
    Markdown
    所以,在汇编源程序中,要访问一个内存单元,并且 [...] 里面是一个立即数,必须要用以下两种方法

    • 将偏移地址放进 bx 寄存器(或si,di)中,然后用 [bx] 来访问内存单元。(用 bx, si, di 时,默认段寄存器在 ds 中, 用 bp 时,默认段寄存器在 ss 中)

      1
      2
      mov bx, 1
      mov ax, [bx]
    • 在 [] 前面显示指定段寄存器。

      1
      mov ax, ds:[0]
  3. dstsrc 不能同时为内存单元和段寄存器。

  4. 不能将立即数传给段寄存器,段名也是立即数,但可以将内存单元数据传给段寄存器。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    assume cs:code, ds:data
    data segment
    address dw 1234h
    data ends
    code segment
    main:
    mov ds, 1234h ; 错误!不能将立即数传给段寄存器
    mov ds, data ; 错误!段名也是一个立即数
    mov bx, 0
    mov ds, [bx] ; 可以
    mov ds, address ; 可以,address会被编译成一个地址,同上一条指令类似
    mov ax, 4c00h
    int 21h
    code ends
    end main
  5. 目的操作数 dst 不能为立即数或 cs(代码段寄存器)。

  6. mov 指令不影响标志位。

2. push(入栈) / pop(出栈)

1
2
push reg16/segreg/mem16
pop reg16/segreg/mem16

pushpop 的操作数可以是16位寄存器、段寄存器或者字内存单元。注意:不能 popcs(代码段寄存器)中

以下操作都是合法的:

1
2
3
4
5
6
push ax
push ds
push word ptr ds:[0]
pop word ptr ds:[0]
pop ds
pop ax

3. xchg(交换)

1
xchg object1, object2

交换8/16位寄存器/内存单元的值。

注意:

  1. 类型必须匹配。
  2. 交换数必须是通用寄存器(AX,BX,CX,DX,SI,DI)或内存单元。
  3. 两个交换数不能同时为内存单元。
    1
    2
    3
    4
    5
    6
    7
    8
    xchg al, dl ; 可以
    xchg ax, bx ; 可以
    xchg ax, word ptr ds:[0] ; 可以
    xchg [bx], ax ; 可以
    xchg al, bx ; 错误!类型不匹配
    xchg ax, ds ; 错误!交换数不能为段寄存器
    xchg ds, ax ; 错误!交换数不能为段寄存器
    xchg byte ptr ds:[0], byte ptr ds:[1] ; 错误!两个都是内存单元

4. lea(传入有效地址 Load Effective Address)

1
lea reg16 mem

注意:

  1. 目的操作数必须为16位通用寄存器,不能是段寄存器
  2. 传入寄存器的是有效地址,而不是内容
1
2
3
4
lea dx, ds:[0] ; 可以
lea dx, msg ; 可以,类似于上一条指令
lea dl, ds:[0] ; 错误!必须为16位通用寄存器
lea ds, ds:[0] ; 错误!不能为段寄存器
1
lea dx, ds:[0]

dx中保存了ds:[0]的偏移地址,若想得到ds:[0]的值,要用:

1
mov dx, ds:[0]

以下两条指令功能相同:

1
2
lea dx, msg
mov dx, offset msg

算术指令

加法指令

1. add(加法)

1
add reg/mem, reg/mem/idata

作用:dst = dst + src
注意:1) 两个操作数类型要匹配。 2)操作数不能同为内存单元。

2. adc(带进位加法)

1
adc reg/mem, reg/mem/idata

作用:dst = dst + src + cf
注意:同 add

3. inc(自增一)

1
inc reg/mem

减法指令

1. sub(减法)

1
sub reg/mem, reg/mem/idata

作用:dst = dst - src
注意:同 add

2. sbb(带借位减法)

1
sbb reg/mem, reg/mem/idata

作用:dst = dst - src - cf
注意:同 add

3. dec(自减一)

1
dec reg/mem

4. neg(求补)

1
neg reg/mem

作用:dst = 0 - dst (即求相反数)

5. cmp(比较)

1
cmp reg/mem, reg/mem/idata

作用:根据 dst - src 的结果去影响标志位。
注意:同 add

乘法指令

1. mul(无符号乘法)

1
mul reg/mem

作用:1)当操作数为8位时,乘数默认保存在 al 中,结果保存在 ax 中,ax = al × src。2)当操作数为16位时,乘数默认保存在 ax 中,结果的高16位保存在 dx 中,低16位保存在 dx 中,dx : ax = ax × src

2. imul(带符号乘法)

mul

除法指令

1. div(无符号除法)

1
div reg/mem

作用:1)当操作数为8位时,被除数默认保存在 ax 中,结果商保存在 al 中,余数保存在 ah 中。2)当操作数为16位时,被除数默认高16位保存在 dx 中,低16位保存在 ax 中,结果商保存在 ax 中,余数保存在 dx 中。

2. idiv(带符号除法)

div
注意:除法指令可能会发生溢出,产生0号中断。可参考 http://www.doc88.com/p-5836860600548.html

逻辑指令

逻辑运行指令

1. and(与)/ or(或)/ not(非)/ xor(异或)

1
2
3
4
and reg/mem, reg/mem/idata
or reg/mem, reg/mem/idata
not reg/mem
xor reg/mem, reg/mem/idata

注意:1) 两个操作数类型要匹配。 2)操作数不能同为内存单元。

2. test(测试)

1
test reg/mem, reg/mem/idata

作用:将两个操作数相与(and)后的结果去影响标志位,若结果为0,则zf=1,否则zf=0。
注意:1) 两个操作数类型要匹配。 2)操作数不能同为内存单元。
常用用法:检查某位是否为1,e.g.:

  • 检验ax是否为负数

    1
    2
    test ax, 8000h
    jnz l ; 是负数则跳转
  • 检验ax是否为奇数

    1
    2
    test ax, 1
    jnz l ; 是奇数则跳转
  • 判断大小写

    1
    2
    3
    test ax, 00100000b
    jz label1 ; 大写则跳转
    jmp lable2 ; 小写则跳转

移位指令

shl(逻辑左移)/ shr(逻辑右移)/ sal(算术左移)/ sar(算术右移)

1
2
3
4
shl reg/mem, 1/cl
shr reg/mem, 1/cl
sal reg/mem, 1/cl
sar reg/mem, 1/cl

作用:将操作数各个二进制位向左(右)移动1(cl)位,最高(低)位移出到 cf,最低(高)位补0(sf)。
注意:1)若移位数为1,可以直接写1,否则必须将移位数放入cl中并调用。2)无论移动多少次,cf等于最后移出位的值。
逻辑移位跟算术移位的区别:算术移位是将数据看成是有正有负的补码数的运算,逻辑移位是将数据看成是无符号数。左移时,算术移位和逻辑移位最右端都是用0补充,操作相同,所以指令等价。右移时,算术右移最右端补sf(符号标志位),逻辑右移补0,所以指令不同。

循环移位指令

rol(循环左移)/ ror(循环右移)/ rcl(带进位循环左移)/ rcr(带进位循环右移)

1
2
3
4
rol reg/mem, 1/cl
ror reg/mem, 1/cl
rcl reg/mem, 1/cl
rcr reg/mem, 1/cl

作用:循环移位时,将操作数各个二进制位向左(右)移动1(cl)位,最高(低)位同时移入cf和最低(高)位。带进位循环移位时,将操作数各个二进制位向左(右)移动1(cl)位,最高(低)位移入cf,原cf移入最低(高)位。
注意:同移位指令。

-------------------------------- 全文完 感谢您的阅读 --------------------------------
「写的那么辛苦,连一块钱都不打赏吗/(ㄒoㄒ)/~~」