1. 数据传送指令
1.1.1. 赋值指令 mov
mov dest, src
可以将 src 的值赋值给 dest,需要注意的是:
- 两个操作数不能同时为内存变量
- 两个操作数必须等宽
- 不能把常数或段寄存器赋值给段寄存器
- 不允许对 CS 进行赋值
- 不能用
mov
指令引用寄存器 IP 及 FL。
1.1.2. 堆栈指令
push
、pop
指令,参见 V 汇编语言进阶。
1.1.3. 交换指令 xchg
xchg op1, op2
可以实现:1. 两个寄存器;2. 一个寄存器和一个变量之间的值的交换操作。
1.2. 输入输出指令
in
、out
指令,参见 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. lds
、les
等
lds dest:reg, src:mem32
、les 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. 标志寄存器传送指令
pushf
、popf
指令,参见 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 位整数,表示短跳的跳转距离。在编写汇编程序的过程中,我们可以使用标号代替,编译器会自动计算出标号与下条指令之间的距离,公式如下:
这里 $
表示的是短跳指令自身的偏移地址,故 $+2
实际上就是下条指令的偏移地址。
7.1.2. jmp near ptr dest
近跳的机器码由 3 个字节构成 0E9h, idata16_ls, idata16_hs
。其中 idata16_ls
和 idata16_hs
分别是 idata16
的低 8 位和高 8 位,同样的 idata16
就是近跳的目标地址和下条指令的偏移地址之差,计算公式如下:
由于近跳和短跳相比多了一个字节,故下条指令的地址变成了 $+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 指令有jz
和jnz
。通过对两操作数执行test
指令,如果他们的逻辑与的值为 0,ZF 就会被置 1,这时可用jz
指令跳转。 - 关于
cmp
指令(相当于只影响寄存器的sub
指令)的 JCC 指令有je
、ja
、jb
、jl
、jg
、jae
、jbe
、jle
、jge
等。 - 进一步地、关于标志寄存器的 JCC 指令有
jc
、jnc
、jz
、jnz
、js
、jns
、jo
、jno
、jp
、jnp
十个,分别对应相应的标志寄存器。 - 特别地,还有
jcxz
和jexcz
指令,检查 CX 或 ECX 寄存器是否为 0,是的话则跳转,往往和loop
指令配合使用。
注意 JCC 指令的跳转距离为 1 字节,也就是说跳转的目标地址和下条指令的偏移地址的之间的距离必须在 的范围内。
7.3. 循环指令 loop
loop dest
指令会先将 CX 寄存器的值自减,再检查 CX 是否不等于零,如果是的话则跳转到 dest
标号处。在不能确定 CX 的初值非零的情况下,最好先用 jcxz
指令判断,避免程序陷入“死循环”中。
loop
指令与 JCC 指令一样,跳转距离为 1 字节。
这里的 例:求 的和
mov ax, 0
mov cx, 3
next:
add ax, cx
loop next
loop next
等价于 dec cx
和 jnz next
这两条指令。
7.4. 子程序调用与返回指令 call
、ret
、retf
详见 V 汇编语言进阶 的函数部分。
7.5. 中断和中断返回指令 int
、int3
、into
、iret
详见 V 汇编语言进阶 的中断程序设计部分。