IV 8086指令系统

1. 数据传送指令

1.1.1. 赋值指令 mov

mov dest, src 可以将 src 的值赋值给 dest,需要注意的是:

  • 两个操作数不能同时为内存变量
  • 两个操作数必须等宽
  • 不能把常数或段寄存器赋值给段寄存器
  • 不允许对 CS 进行赋值
  • 不能用 mov 指令引用寄存器 IP 及 FL。

1.1.2. 堆栈指令

pushpop 指令,参见 V 汇编语言进阶

1.1.3. 交换指令 xchg

xchg op1, op2 可以实现:1. 两个寄存器;2. 一个寄存器和一个变量之间的值的交换操作。

1.2. 输入输出指令

inout 指令,参见 III CPU、内存和端口

1.3. 地址传送指令

1.3.1. lea

lea dest:reg src:mem:load effective address,即取变量 src 的偏移地址存储到寄存器 dest 中。

lea dx, ds:[1000h]    ; 等价于 mov dx, 1000h
lea dx, [abc]         ; 等价于 mov dx, offset abc
lea dx, ds:[bx+si+3]  ; 令dx=bx+si+3,无法直接用mov实现

1.3.2. ldsles

lds dest:reg, src:mem32les dest:reg, src:mem32 可以取出保存在变量 src 中的远地址,且将其中的段地址部分赋值给 DS(lds 的行为)或 ES(les 的行为),偏移地址部分赋值给 dest 寄存器。等效于如下汇编:

mov dest, word ptr [src]     ; 这里dest是一个寄存器名
mov ds, word ptr[src+2]      ; 如果是les指令这里就是es

1.4. 标志寄存器传送指令

pushfpopf 指令,参见 III CPU、内存和端口

2. 转换指令

2.1. 扩充指令

详见 II 数据的表示与运算

2.2. 换码指令 xlat

xlat没有参数)会把 byte ptr DS:[BX:AL] 的值赋值给 AL,必须要在操作前让 DS:BX 指向表,AL 指向需要的下标。

例:利用 xlat 指令将 32 位整数化为 16 进制输出

.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
  sub cx, 1
  jnz next
  mov ah, 4Ch
  int 21h
code ends
end main

3. 算术运算指令

详见 II 数据的表示与运算

4. 十进制调整指令

咕咕咕。

5. 逻辑运算指令和移位指令

详见 II 数据的表示与运算

6. 字符串操作指令

详见 II 数据的表示与运算

7. 控制转移指令

7.1. 无条件跳转指令 jmp

根据跳转距离的远近,可以将 jmp 分成三类:

  • 短跳(short jump):跳转距离用一个字节表示
  • 近跳(near jump):跳转距离或目标地址用一个字表示
  • 远跳(far jump):目标地址用一个原指针表示

7.1.1. jmp short dest

短跳的机器码由 2 个字节构成 0EBh, idata8。其中 idata8 是一个 8 位整数,表示短跳的跳转距离。在编写汇编程序的过程中,我们可以使用标号代替,编译器会自动计算出标号与下条指令之间的距离,公式如下:

idata8=标号($+2)\text{idata8} = \text{标号} - (\$ + 2)

这里 $ 表示的是短跳指令自身的偏移地址,故 $+2 实际上就是下条指令的偏移地址。

7.1.2. jmp near ptr dest

近跳的机器码由 3 个字节构成 0E9h, idata16_ls, idata16_hs。其中 idata16_lsidata16_hs 分别是 idata16 的低 8 位和高 8 位,同样的 idata16 就是近跳的目标地址和下条指令的偏移地址之差,计算公式如下:

idata16=标号($+3)\text{idata16} = \text{标号} - (\$ + 3)

由于近跳和短跳相比多了一个字节,故下条指令的地址变成了 $+3。在使用近跳指令时,near ptr 可以省略不写。

近跳指令还支持直接给定目标地址的偏移地址,格式为 jmp reg16|mem16,相当于直接给 IP 寄存器赋值。注意这里如果是 mem16 的话别忘了用 word ptr 修饰宽度。

7.1.3. jmp far ptr dest

远跳的字节码有 5 个字节构成,0EAh, idata32_l16, idata32_h16。其中 idata32 是一个 32 位的远指针,表示跳转的目标地址,其端地址就是 idata32_h16,偏移地址就是 idata32_l16。注意这里的地址仍然是小端存储。例如:机器码 0EAh, 78h, 56h, 34h, 12h 对应的目标地址是 1234:5678。

当远跳指令向后引用(引用的变量或标号在当前语句的上方)一个不在同一段内的远标号时,far ptr 可以省略不写;否则必须写出 far ptr 不然 CPU 会报错:

  • 当引用了一个不在同一段的近标号而不写明 far ptr 时会报“Near JMP/CALL to different CS”错误
  • 向前引用(引用的变量或标号在当前语句下方)了一个不在同一段内的原标号而不写明 far ptr 时会报“Forward needs override or FAR”错误。(不过这一问题可以通过用 tasm /m2 代替 masm 编译来解决)

远跳指令可以跳转到远标号(定义方法:标号名 label far),也可以直接给定目标地址,相当于同时修改了 CS 和 IP 寄存器的值。示例代码如下:

mov word ptr es:[di], 5678h
mov word ptr es:[di+4], 1234h
jmp dword ptr es:[di]  ; 将会跳转到1234:5678

7.2. 条件跳转指令 jcc

  • 关于 test 指令(相当于只影响寄存器的 and 指令)的 JCC 指令有 jzjnz。通过对两操作数执行 test 指令,如果他们的逻辑与的值为 0,ZF 就会被置 1,这时可用 jz 指令跳转。
  • 关于 cmp 指令(相当于只影响寄存器的 sub 指令)的 JCC 指令有 jejajbjljgjaejbejlejge 等。
  • 进一步地、关于标志寄存器的 JCC 指令有 jcjncjzjnzjsjnsjojnojpjnp 十个,分别对应相应的标志寄存器。
  • 特别地,还有 jcxzjexcz 指令,检查 CX 或 ECX 寄存器是否为 0,是的话则跳转,往往和 loop 指令配合使用。

注意 JCC 指令的跳转距离为 1 字节,也就是说跳转的目标地址和下条指令的偏移地址的之间的距离必须在 [128,127][-128,127] 的范围内。

7.3. 循环指令 loop

loop dest 指令会将 CX 寄存器的值自减,再检查 CX 是否不等于零,如果是的话则跳转到 dest 标号处。在不能确定 CX 的初值非零的情况下,最好先用 jcxz 指令判断,避免程序陷入“死循环”中。

loop 指令与 JCC 指令一样,跳转距离为 1 字节。

例:求 1+2+31+2+3 的和

  mov ax, 0
  mov cx, 3
next:
  add ax, cx
  loop next

这里的 loop next 等价于 dec cxjnz next 这两条指令。

7.4. 子程序调用与返回指令 callretretf

详见 V 汇编语言进阶 的函数部分。

7.5. 中断和中断返回指令 intint3intoiret

详见 V 汇编语言进阶 的中断程序设计部分。

评论

TABLE OF CONTENTS

1. 数据传送指令
1.2. 输入输出指令
1.3. 地址传送指令
1.4. 标志寄存器传送指令
2. 转换指令
2.1. 扩充指令
2.2. 换码指令 xlat
3. 算术运算指令
4. 十进制调整指令
5. 逻辑运算指令和移位指令
6. 字符串操作指令
7. 控制转移指令
7.1. 无条件跳转指令 jmp
7.2. 条件跳转指令 jcc
7.3. 循环指令 loop
7.4. 子程序调用与返回指令 callretretf
7.5. 中断和中断返回指令 intint3intoiret

© 2018-2025 memset0.

All rights reserved.

Source Code

Built with Gatsby.js


Made with ❤️ in China