目录
一、数据传输指令
数据传输指令是用于在计算机系统中移动数据的一类指令。它们负责将数据从一个位置复制到另一个位置,这些位置可以是寄存器、内存地址或者是堆栈。以下是一些常见的数据传输指令及其功能:
- **MOV**:移动数据指令,用于将数据从一个源位置复制到目标位置。
- **PUSH**:入栈指令,将数据压入堆栈中,通常用于保存寄存器内容或传递函数参数。
- **POP**:出栈指令,从堆栈中弹出数据,通常用于恢复寄存器内容或获取函数返回值。
- **XCHG**:交换指令,用于交换两个位置(通常是寄存器或内存)中的数据。
- **LEA**:有效地址传送指令,用于计算并加载一个内存地址的有效地址到寄存器中,常用于地址计算。
- **PUSHF**:标志进栈指令,将标志寄存器的内容压入堆栈,用于保存当前的处理器状态。
这些指令是计算机程序设计中的基础,它们使得数据能够在不同的存储单元之间进行有效的传输和管理。
1.1 MOV 指令
MOV 指令是数据传输指令中的一种,用于在不同的存储单元之间复制数据。其基本形式包括将数据从寄存器、内存或立即数(常量)移动到另一个寄存器或内存位置。以下是 MOV 指令的详细描述:
- **指令原型**:
- `mov reg, reg/mem/imm`:将数据从寄存器、内存或立即数移动到寄存器。
- `mov mem, reg/imm`:将数据从寄存器或立即数移动到内存。
- `mov seg, reg16/mem16`:将数据从16位寄存器或内存移动到段寄存器。
- **操作数个数**:MOV 指令包含两个操作数。
- **指令结构**:`MOV 目标操作数, 源操作数`,其中目标操作数是数据将被复制到的位置,源操作数是数据来源的位置。
- **执行操作**:MOV 指令执行的操作是将源操作数的值复制到目标操作数,即目标操作数的值变为源操作数的值。
- **例子**:
- `mov al, byte ptr [100]`:将内存地址100处的字节数据移动到寄存器 AL。
- `mov word ptr [120], ax`:将寄存器 AX 中的字数据移动到内存地址120处。
- `mov ds, ax`:将寄存器 AX 中的数据移动到数据段寄存器 DS。
MOV 指令是汇编语言编程中最基本和最常用的指令之一,它使得程序能够在不同的数据存储位置之间进行数据传输。
1.2 PUSH 入栈指令
PUSH 入栈指令用于将数据存储到堆栈中,通常用于保存寄存器内容、传递函数参数或在函数调用前保存状态。以下是 PUSH 指令的详细描述:
- **指令原型**:`PUSH reg/mem/seg`,表示可以将寄存器、内存位置或段寄存器的内容压入堆栈。
- **操作数个数**:PUSH 指令包含一个操作数,即源操作数,它是将要被压入堆栈的数据来源。
- **指令结构**:`PUSH 源操作数`,其中源操作数是将要被压入堆栈的数据。
- **执行操作**:PUSH 指令执行的操作是将源操作数的内容压入堆栈。具体来说,堆栈指针(SP 或 ESP)会先减去操作数的大小(通常是2字节或4字节,取决于操作数是16位还是32位),然后将源操作数的内容复制到堆栈指针所指向的新位置。
- **例子**:
- `push ax`:将寄存器 AX 的内容压入堆栈。
- `push word ptr [120]`:将内存地址120处的字数据压入堆栈。
- `push cs`:将代码段寄存器 CS 的内容压入堆栈。
PUSH 指令是管理堆栈和保存程序状态的关键指令,它在函数调用和异常处理等场景中扮演着重要角色。
1.3 POP 出栈指令
POP 出栈指令用于从堆栈中取出数据,并将其存储到指定的目标位置。通常用于恢复之前保存的寄存器内容、获取函数返回值或在函数返回前恢复状态。以下是 POP 指令的详细描述:
- **指令原型**:`POP reg/mem/seg`,表示可以将堆栈顶部的数据弹出并存储到寄存器、内存位置或段寄存器中。
- **操作数个数**:POP 指令包含一个操作数,即目标操作数,它是数据弹出后将要被存储的位置。
- **指令结构**:`POP 目标操作数`,其中目标操作数是数据弹出后将要被存储的位置。
- **执行操作**:POP 指令执行的操作是将堆栈顶部的数据弹出,并将其存储到目标操作数中。具体来说,堆栈指针(SP 或 ESP)会先指向栈顶的数据,然后将该数据复制到目标操作数中,最后堆栈指针增加操作数的大小(通常是2字节或4字节,取决于操作数是16位还是32位)。
- **例子**:
- `pop ax`:将堆栈顶部的数据弹出并存储到寄存器 AX 中。
- `pop word ptr [120]`:将堆栈顶部的数据弹出并存储到内存地址120处。
- `pop cs`:将堆栈顶部的数据弹出并存储到代码段寄存器 CS 中。
POP 指令与 PUSH 指令相对应,它们共同构成了堆栈操作的基础,确保了程序在调用函数和返回时能够正确地管理数据和状态。
1.4 XCHG 指令
XCHG 指令用于交换两个操作数的内容,这些操作数可以是寄存器与寄存器、寄存器与内存位置之间的数据交换。以下是 XCHG 指令的详细描述:
- **指令原型**:
- `XCHG reg, reg/mem`:表示可以将寄存器的内容与另一个寄存器或内存位置的内容进行交换。
- `XCHG mem, reg`:表示可以将内存位置的内容与寄存器的内容进行交换。
- **操作数个数**:XCHG 指令包含两个操作数,即目标操作数和源操作数。
- **指令结构**:`XCHG 目标操作数, 源操作数`,其中目标操作数和源操作数的内容将会互换。
- **执行操作**:XCHG 指令执行的操作是将目标操作数的内容与源操作数的内容进行交换。具体来说,指令会将目标操作数的内容临时保存,然后将源操作数的内容复制到目标操作数,最后将临时保存的目标操作数内容复制到源操作数。
- **例子**:
- `xchg ax, word ptr [120]`:将寄存器 AX 的内容与内存地址120处的字数据进行交换。
XCHG 指令在需要临时交换两个位置的数据时非常有用,例如在排序算法或数据结构操作中。
1.5 LEA 有效地址传送指令
LEA(Load Effective Address)有效地址传送指令用于将内存操作数的地址加载到寄存器中,而不是将内存中的数据加载到寄存器中。以下是 LEA 指令的详细描述:
- **指令原型**:`LEA reg, mem`,表示可以将内存操作数的有效地址(而不是其内容)加载到寄存器中。
- **操作数个数**:LEA 指令包含两个操作数,即目标寄存器和源内存操作数。
- **指令结构**:`LEA 寄存器, 源操作数`,其中寄存器是目标操作数,它将接收源操作数的地址。
- **执行操作**:LEA 指令执行的操作是将源操作数的地址计算出来,并将其传送到目标寄存器中。具体来说,指令会计算源操作数的有效地址(可能包含基址、变址和偏移量的组合),然后将这个地址值加载到目标寄存器中。
- **例子**:
- `lea ax, [1234]`:将内存地址1234的有效地址加载到寄存器 AX 中,结果是 AX 的值变为1234。
- `lea ax, [1000+234]`:将内存地址1000加上234的有效地址加载到寄存器 AX 中,结果同样是 AX 的值变为1234。
LEA 指令常用于需要计算复杂地址的场景,例如在数组索引、指针操作和复杂数据结构访问中。它提供了一种快速计算和存储地址的方法,而不涉及实际的内存读取操作。
1.6 标志寄存器进栈/出栈指令
标志寄存器进栈/出栈指令用于在堆栈和标志寄存器(Flag Register)之间传输数据,通常用于保存和恢复程序执行过程中的状态信息。以下是这些指令的详细描述:
- **指令原型**:
- `PUSHF`:将标志寄存器的内容压入堆栈。
- `POPF`:从堆栈中弹出数据并将其存储到标志寄存器中。
- **操作数个数**:PUSHF 和 POPF 指令都不包含操作数,它们直接对标志寄存器和堆栈进行操作。
- **指令结构**:
- `PUSHF`:将标志寄存器的内容压入堆栈。
- `POPF`:从堆栈中弹出数据并将其存储到标志寄存器中。
- **执行操作**:
- `PUSHF` 指令执行的操作是将标志寄存器的内容压入堆栈,具体来说,堆栈指针(SP 或 ESP)会先减去标志寄存器的大小(通常是2字节或4字节,取决于操作数是16位还是32位),然后将标志寄存器的内容复制到堆栈指针所指向的新位置。
- `POPF` 指令执行的操作是从堆栈中弹出数据,并将其存储到标志寄存器中,具体来说,堆栈指针会先指向栈顶的数据,然后将该数据复制到标志寄存器中,最后堆栈指针增加标志寄存器的大小。
- **例子**:
- `pushf`:将标志寄存器的内容压入堆栈。
- `popf`:从堆栈中弹出数据并将其存储到标志寄存器中。
这些指令在函数调用、中断处理和异常处理等场景中非常重要,因为它们允许程序在执行关键操作之前保存当前的处理器状态,并在操作完成后恢复该状态。
1.7 寄存器入栈/出栈指令
寄存器入栈/出栈指令用于在堆栈和一组通用寄存器之间传输数据,通常用于保存和恢复程序执行过程中的寄存器状态。以下是这些指令的详细描述:
- **指令原型**:
- `PUSHA`:将所有16位通用寄存器的内容压入堆栈。
- `POPA`:从堆栈中弹出数据并将其存储到所有16位通用寄存器中。
- **操作数个数**:PUSHA 和 POPA 指令都不包含操作数,它们直接对一组16位通用寄存器和堆栈进行操作。
- **指令结构**:
- `PUSHA`:将所有16位通用寄存器的内容压入堆栈。
- `POPA`:从堆栈中弹出数据并将其存储到所有16位通用寄存器中。
- **执行操作**:
- `PUSHA` 指令执行的操作是将所有16位通用寄存器的内容(包括 AX, CX, DX, BX, SP, BP, SI, DI)压入堆栈,具体来说,堆栈指针(SP)会依次减去每个寄存器的大小(2字节),然后将每个寄存器的内容复制到堆栈指针所指向的新位置。
- `POPA` 指令执行的操作是从堆栈中弹出数据,并将其存储到所有16位通用寄存器中,具体来说,堆栈指针会依次指向栈顶的数据,然后将该数据复制到相应的寄存器中,最后堆栈指针增加每个寄存器的大小。
- **例子**:
- `pusha`:将所有16位通用寄存器的内容压入堆栈。
- `popa`:从堆栈中弹出数据并将其存储到所有16位通用寄存器中。
对于32位寄存器,相应的指令是 `PUSHAD` 和 `POPAD`,它们分别用于将所有32位通用寄存器的内容压入堆栈和从堆栈中弹出数据。
这些指令在函数调用、中断处理和异常处理等场景中非常重要,因为它们允许程序在执行关键操作之前保存当前的寄存器状态,并在操作完成后恢复该状态。
二、算数与逻辑运算指令
算术运算与逻辑指令是计算机指令集中的核心部分,它们负责执行各种数学运算和逻辑操作。这些指令不仅计算出结果,而且某些指令还会影响标志寄存器中的状态位,这些状态位随后可以被控制转移类指令所利用,从而影响程序的执行流程和逻辑。
以下是一些常见的算术运算与逻辑指令及其功能:
- **ADD**:加法指令,用于将两个操作数相加,并将结果存储在目标操作数中。
- **ADC**:带进位的加法指令,除了执行加法操作外,还会将进位标志(CF)考虑在内,适用于多字节或多字的加法运算。
- **INC**:加1指令,用于将目标操作数的值增加1。
- **SUB**:减法指令,用于将目标操作数减去源操作数,并将结果存储在目标操作数中。
- **SBB**:带借位的减法指令,除了执行减法操作外,还会将借位标志(CF)考虑在内,适用于多字节或多字的减法运算。
- **DEC**:减1指令,用于将目标操作数的值减少1。
这些指令在编程中非常重要,它们是实现复杂计算和逻辑判断的基础。通过这些指令,程序员可以控制数据处理的方式,从而实现各种功能和算法。
2.1 ADD 加法指令
ADD 加法指令用于将两个操作数相加,并将结果存储在目标操作数中。以下是 ADD 指令的详细描述:
- **指令原型**:
- `ADD reg, reg/mem/imm`:表示可以将寄存器、内存位置或立即数(常量)与寄存器相加。
- `ADD mem, reg/imm`:表示可以将寄存器或立即数与内存位置相加。
- **操作数个数**:ADD 指令包含两个操作数,即目标操作数和源操作数。
- **指令结构**:`ADD 目标操作数, 源操作数`,其中目标操作数是结果将被存储的位置,源操作数是将被加到目标操作数上的值。
- **执行操作**:ADD 指令执行的操作是将源操作数的值与目标操作数的值相加,并将结果存储在目标操作数中。具体来说,指令会将源操作数的值加到目标操作数上,然后将和存储回目标操作数。
- **例子**:
- `add ax, word ptr [123]`:将内存地址123处的字数据与寄存器 AX 的值相加,并将结果存储在 AX 中。
- `add word ptr [123], 1234`:将立即数1234与内存地址123处的字数据相加,并将结果存储回内存地址123处。
ADD 指令是汇编语言编程中最基本的算术指令之一,它使得程序能够在不同的数据存储位置之间进行加法运算。
2.2 ADC 带进位的加法指令
ADC 带进位的加法指令用于执行包含进位标志(CF)的加法运算。以下是 ADC 指令的详细描述:
- **指令原型**:
- `ADC reg, reg/mem/imm`:表示可以将寄存器、内存位置或立即数(常量)与寄存器以及进位标志相加。
- `ADC mem, reg/imm`:表示可以将寄存器或立即数与内存位置以及进位标志相加。
- **操作数个数**:ADC 指令包含两个操作数,即目标操作数和源操作数。
- **指令结构**:`ADC 目标操作数, 源操作数`,其中目标操作数是结果将被存储的位置,源操作数是将被加到目标操作数上的值。
- **执行操作**:ADC 指令执行的操作是将源操作数的值、目标操作数的值以及进位标志(CF)相加,并将结果存储在目标操作数中。具体来说,指令会将源操作数的值和进位标志的值加到目标操作数上,然后将和存储回目标操作数。
- **例子**:
- `mov ah, ff`:将寄存器 AH 的值设置为 FF(十六进制),此时进位标志 CF 为 0。
- `adc ah, 1`:将立即数 1 与寄存器 AH 的值以及进位标志 CF 相加。由于 AH 的值为 FF,加上 1 后结果为 0,并且进位标志 CF 被设置为 1。
ADC 指令在处理多字节或多字的加法运算时特别有用,因为它能够正确地处理进位,确保结果的准确性。
2.3 INC 加1指令
INC 加1指令用于将目标操作数的值增加1。以下是 INC 指令的详细描述:
- **指令原型**:`INC reg/mem`,表示可以将寄存器或内存位置的值增加1。
- **操作数个数**:INC 指令包含一个操作数,即目标操作数。
- **指令结构**:`INC 目标操作数`,其中目标操作数是将被增加1的位置。
- **执行操作**:INC 指令执行的操作是将目标操作数的值增加1,同时保持进位标志(CF)的状态不变。这意味着 INC 指令不会影响 CF 标志,即使发生了溢出。
- **例子**:
- `inc ax`:将寄存器 AX 的值增加1。
- `inc word ptr [123]`:将内存地址123处的字数据增加1。
INC 指令在编程中非常常见,特别是在循环计数器或数组索引的递增操作中。它提供了一种简洁的方式来增加数据存储位置的值,而不影响进位标志。
2.4 SUB 减法指令
SUB 减法指令用于将目标操作数的值减去源操作数的值,并将结果存储在目标操作数中。以下是 SUB 指令的详细描述:
- **指令原型**:
- `SUB reg, reg/mem/imm`:表示可以将寄存器、内存位置或立即数(常量)从寄存器中减去。
- `SUB mem, reg/imm`:表示可以将寄存器或立即数从内存位置中减去。
- **操作数个数**:SUB 指令包含两个操作数,即目标操作数和源操作数。
- **指令结构**:`SUB 目标操作数, 源操作数`,其中目标操作数是结果将被存储的位置,源操作数是将被从目标操作数中减去的值。
- **执行操作**:SUB 指令执行的操作是将目标操作数的值减去源操作数的值,并将结果存储在目标操作数中。具体来说,指令会将源操作数的值从目标操作数中减去,然后将差存储回目标操作数。
- **例子**:
- `sub ax, word ptr [123]`:将内存地址123处的字数据从寄存器 AX 的值中减去,并将结果存储在 AX 中。
- `sub word ptr [123], 1234`:将立即数1234从内存地址123处的字数据中减去,并将结果存储回内存地址123处。
SUB 指令是汇编语言编程中最基本的算术指令之一,它使得程序能够在不同的数据存储位置之间进行减法运算。
2.5 SBB 带借位的减法指令
SBB 带借位的减法指令用于执行包含借位标志(CF)的减法运算。以下是 SBB 指令的详细描述:
- **指令原型**:
- `SBB reg, reg/mem/imm`:表示可以将寄存器、内存位置或立即数(常量)与进位标志相加,然后从寄存器中减去。
- `SBB mem, reg/imm`:表示可以将寄存器或立即数与进位标志相加,然后从内存位置中减去。
- **操作数个数**:SBB 指令包含两个操作数,即目标操作数和源操作数。
- **指令结构**:`SBB 目标操作数, 源操作数`,其中目标操作数是结果将被存储的位置,源操作数是将被从目标操作数中减去的值。
- **执行操作**:SBB 指令执行的操作是将源操作数的值与进位标志(CF)相加,然后从目标操作数中减去这个和,并将最终结果存储在目标操作数中。具体来说,指令会将源操作数的值和进位标志的值相加,然后从这个和中减去目标操作数的值,最后将差存储回目标操作数。
- **例子**:
- `mov ah, ff`:将寄存器 AH 的值设置为 FF(十六进制),此时进位标志 CF 为 0。
- `adc ah, 5`:将立即数 5 与寄存器 AH 的值以及进位标志 CF 相加,结果为 0(因为 FF + 5 溢出),并且进位标志 CF 被设置为 1。
- `sbb ah, 1`:将立即数 1 与进位标志 CF 相加(结果为 2),然后从寄存器 AH 的值(现在是 0)中减去这个和,结果为 -2,但由于 AH 是 8 位寄存器,结果被截断为 2,并且进位标志 CF 被清除为 0。
SBB 指令在处理多字节或多字的减法运算时特别有用,因为它能够正确地处理借位,确保结果的准确性。
2.6 DEC 减1指令
DEC 减1指令用于将目标操作数的值减少1。以下是 DEC 指令的详细描述:
- **指令原型**:`DEC reg/mem`,表示可以将寄存器或内存位置的值减少1。
- **操作数个数**:DEC 指令包含一个操作数,即目标操作数。
- **指令结构**:`DEC 目标操作数`,其中目标操作数是将被减少1的位置。
- **执行操作**:DEC 指令执行的操作是将目标操作数的值减少1,同时保持进位标志(CF)的状态不变。这意味着 DEC 指令不会影响 CF 标志,即使发生了溢出。
- **例子**:
- `dec ax`:将寄存器 AX 的值减少1。
- `dec word ptr [123]`:将内存地址123处的字数据减少1。
DEC 指令在编程中非常常见,特别是在循环计数器或数组索引的递减操作中。它提供了一种简洁的方式来减少数据存储位置的值,而不影响进位标志。
2.7 MUL 无符号乘法指令
MUL 无符号乘法指令用于执行两个操作数的无符号乘法运算。以下是 MUL 指令的详细描述:
- **指令原型**:`MUL reg/mem`,表示可以将寄存器或内存位置的值与隐式操作数进行无符号乘法运算。
- **操作数个数**:MUL 指令实际上涉及两个操作数,一个是隐式的目标操作数,另一个是显式的源操作数。隐式目标操作数是位于 AL、AX 或 EAX 寄存器中的操作数,具体取决于源操作数的大小。
- **指令结构**:`MUL 源操作数`,其中源操作数是将被乘的值,隐式目标操作数是 AL、AX 或 EAX 寄存器中的值。
- **执行操作**:MUL 指令执行的操作是将隐式目标操作数与源操作数执行无符号乘法,并将结果存储到相应的寄存器中。具体来说,如果源操作数是 8 位的,那么 AL 寄存器中的值将与源操作数相乘,结果存储在 AX 寄存器中;如果源操作数是 16 位的,那么 AX 寄存器中的值将与源操作数相乘,结果存储在 DX:AX 寄存器对中;如果源操作数是 32 位的,那么 EAX 寄存器中的值将与源操作数相乘,结果存储在 EDX:EAX 寄存器对中。
- **例子**:
- `mul bx`:如果 AX 寄存器的值为 2,BX 寄存器的值为 3,执行完毕后 AX 寄存器的值将为 6。
- `mul word ptr [120]`:将 AX 寄存器的值与内存地址 120 处的字数据进行无符号乘法运算,结果存储在 DX:AX 寄存器对中。
MUL 指令在处理无符号乘法运算时非常有用,它能够自动处理不同大小的操作数,并将结果存储在适当的寄存器中。
2.8 IMUL 有符号的乘法指令
IMUL 有符号的乘法指令用于执行两个有符号操作数的乘法运算。以下是 IMUL 指令的详细描述:
- **指令原型**:
- `IMUL reg/mem`:单操作数形式,表示可以将寄存器或内存位置的值与隐式操作数进行有符号乘法运算。
- `IMUL reg, reg/mem/imm`:双操作数形式,表示可以将目标操作数与源操作数进行有符号乘法运算。
- `IMUL reg, reg/mem, imm`:三操作数形式,表示可以将目标操作数与两个源操作数进行有符号乘法运算。
- **操作数个数**:IMUL 指令的操作数个数可以是 1 到 3 个,具体取决于指令的形式。
- **指令结构**:
- 单操作数形式:`IMUL 源操作数`,其中源操作数是将被乘的值,隐式目标操作数是 AL、AX 或 EAX 寄存器中的值。
- 双操作数形式:`IMUL 目标操作数, 源操作数`,其中目标操作数是结果将被存储的位置,源操作数是将被乘的值。
- 三操作数形式:`IMUL 目标操作数, 源操作数, 立即数`,其中目标操作数是结果将被存储的位置,第一个源操作数是将被乘的值,第二个源操作数是立即数。
- **执行操作**:IMUL 指令执行的操作是对两个有符号操作数执行乘法。根据操作数的数量,此指令有三种形式:
- 单操作数形式:此形式与 MUL 指令使用的形式完全相同,源操作数(位于通用寄存器或内存位置)乘以 AL、AX 或 EAX 寄存器(取决于操作数大小)中的值,乘积分别存储到 AX、DX:AX 或 EDX:EAX 寄存器。
- 双操作数形式:此种形式是目标操作数乘以源操作数,目标操作数是通用寄存器,源操作数可以是立即数、通用寄存器或内存位置,乘积随后存储到目标操作数位置。
- 三操作数形式:此种形式需要一个目标操作数与两个源操作数,第一个源操作数(可以是通用寄存器或内存位置)乘以第二个源操作数(立即数),乘积随后存储到目标操作数(通用寄存器)。
- **例子**:
- `imul bx`:将 AX 寄存器的值与 BX 寄存器的值进行有符号乘法运算,结果存储在 DX:AX 寄存器对中。
- `imul ax, bx`:如果 AX 寄存器的值为 2,BX 寄存器的值为 2,结果为 AX 寄存器的值为 4。
- `imul ax, bx, 2`:如果 AX 寄存器的值为 0,BX 寄存器的值为 2,结果为 AX 寄存器的值为 4。
IMUL 指令在处理有符号乘法运算时非常有用,它能够自动处理不同大小的操作数,并将结果存储在适当的寄存器中。
2.9 DIV 无符号除法指令
DIV 无符号除法指令用于执行两个操作数的无符号除法运算。以下是 DIV 指令的详细描述:
- **指令原型**:`DIV reg/mem`,表示可以将寄存器或内存位置的值作为除数,与隐式操作数进行无符号除法运算。
- **操作数个数**:DIV 指令实际上涉及两个操作数,一个是隐式的被除数,另一个是显式的除数。隐式被除数是位于 AX、DX:AX 或 EDX:EAX 寄存器中的操作数,具体取决于除数的大小。
- **指令结构**:`DIV 源操作数`,其中源操作数是除数,隐式被除数是 AX、DX:AX 或 EDX:EAX 寄存器中的值。
- **执行操作**:DIV 指令执行的操作是将隐式被除数与源操作数执行无符号除法,并将结果存储到相应的寄存器中。具体来说,如果源操作数是 8 位的,那么 AX 寄存器中的值将被除以源操作数,商存储在 AL 寄存器中,余数存储在 AH 寄存器中;如果源操作数是 16 位的,那么 DX:AX 寄存器对中的值将被除以源操作数,商存储在 AX 寄存器中,余数存储在 DX 寄存器中;如果源操作数是 32 位的,那么 EDX:EAX 寄存器对中的值将被除以源操作数,商存储在 EAX 寄存器中,余数存储在 EDX 寄存器中。
- **例子**:
- `div bx`:如果 DX:AX 寄存器对的值为 6,BX 寄存器的值为 3,执行完毕后 AX 寄存器的值将为 2,DX 寄存器的值将为 0。
- `div word ptr [120]`:将 DX:AX 寄存器对的值与内存地址 120 处的字数据进行无符号除法运算,商存储在 AX 寄存器中,余数存储在 DX 寄存器中。
DIV 指令在处理无符号除法运算时非常有用,它能够自动处理不同大小的操作数,并将商和余数存储在适当的寄存器中。
2.10 IDIV 有符号除法指令
IDIV 有符号除法指令用于执行两个有符号操作数的除法运算。以下是 IDIV 指令的详细描述:
- **指令原型**:`IDIV reg/mem`,表示可以将寄存器或内存位置的值作为除数,与隐式操作数进行有符号除法运算。
- **操作数个数**:IDIV 指令实际上涉及两个操作数,一个是隐式的被除数,另一个是显式的除数。隐式被除数是位于 AX、DX:AX 或 EDX:EAX 寄存器中的操作数,具体取决于除数的大小。
- **指令结构**:`IDIV 源操作数`,其中源操作数是除数,隐式被除数是 AX、DX:AX 或 EDX:EAX 寄存器中的值。
- **执行操作**:IDIV 指令执行的操作是将隐式被除数与源操作数执行有符号除法,并将结果存储到相应的寄存器中。具体来说,如果源操作数是 8 位的,那么 AX 寄存器中的值将被除以源操作数,商存储在 AL 寄存器中,余数存储在 AH 寄存器中;如果源操作数是 16 位的,那么 DX:AX 寄存器对中的值将被除以源操作数,商存储在 AX 寄存器中,余数存储在 DX 寄存器中;如果源操作数是 32 位的,那么 EDX:EAX 寄存器对中的值将被除以源操作数,商存储在 EAX 寄存器中,余数存储在 EDX 寄存器中。
- **例子**:
- `idiv bx`:如果 AX 寄存器的值为 6,BX 寄存器的值为 -3(十六进制表示为 FFFD),执行完毕后 AX 寄存器的值将为 -2(十六进制表示为 FFFE)。
- `idiv word ptr [120]`:将 DX:AX 寄存器对的值与内存地址 120 处的字数据进行有符号除法运算,商存储在 AX 寄存器中,余数存储在 DX 寄存器中。
IDIV 指令在处理有符号除法运算时非常有用,它能够自动处理不同大小的操作数,并将商和余数存储在适当的寄存器中。
2.11 除法错中断
除法错中断是指在进行除法运算时,如果被除数远大于除数,导致所得的商超出了寄存器所能表示的范围,从而引发的中断。在 8086 CPU 中,这种中断被称为除法错中断,其编号为 0。
对于 DIV 指令,以下情况会触发除法溢出:
- 除数为 0。
- 在字节除法时,商超过了 8 位的表示范围(0 到 255)。
- 在字除法时,商超过了 16 位的表示范围(0 到 65535)。
对于 IDIV 指令,以下情况会触发除法溢出:
- 除数为 0。
- 在字节除法时,商不在 -128 到 127 的范围内。
- 在字除法时,商不在 -32768 到 32767 的范围内。
当发生除法溢出时,CPU 会自动触发中断,程序将跳转到预定义的中断处理程序来处理这个异常。这通常涉及到报告错误、清理状态或终止程序等操作。为了避免除法溢出,程序员在编写代码时需要确保除数不为零,并且被除数和除数的大小关系不会导致商超出寄存器的表示范围。
2.12 CMP 比较指令
CMP 比较指令用于比较两个操作数的值,通过执行一个隐式的减法操作来实现,但不保存实际的差值,而是根据减法的结果设置 EFLAGS 寄存器中的状态标志。以下是 CMP 指令的详细描述:
- **指令原型**:
- `CMP reg, reg/mem/imm`:表示可以将寄存器与寄存器、内存位置或立即数进行比较。
- `CMP mem, reg/imm`:表示可以将内存位置与寄存器或立即数进行比较。
- **操作数个数**:CMP 指令包含两个操作数,即两个源操作数。
- **指令结构**:`CMP 源操作数1, 源操作数2`,其中源操作数1是被减数,源操作数2是减数。
- **执行操作**:CMP 指令执行的操作是用第一个源操作数减去第二个源操作数,但不保存结果,而是根据减法的结果设置 EFLAGS 寄存器中的状态标志,如进位标志(CF)、零标志(ZF)、符号标志(SF)、溢出标志(OF)等。
- **例子**:
- `cmp ax, 1`:如果 AX 寄存器的值为 0,执行比较之后,由于 0 减去 1 的结果是 -1,最高位为 1,因此符号标志 SF 为 1(在调试器中通常表示为 NG,即 Negative)。
CMP 指令在条件跳转和条件指令中非常有用,因为它允许程序根据比较的结果来决定程序的流程。例如,程序可以根据 ZF 标志来判断两个操作数是否相等,或者根据 SF 标志来判断比较的结果是否为负。
2.13 AND 逻辑与指令
AND 逻辑与指令用于对两个操作数进行逐位逻辑与运算,并将结果存储在目标操作数中。以下是 AND 指令的详细描述:
- **指令原型**:
- `AND reg, reg/mem/imm`:表示可以将寄存器与寄存器、内存位置或立即数进行逐位逻辑与运算。
- `AND mem, reg/imm`:表示可以将内存位置与寄存器或立即数进行逐位逻辑与运算。
- **操作数个数**:AND 指令包含两个操作数,即目标操作数和源操作数。
- **指令结构**:`AND 目标操作数, 源操作数`,其中目标操作数是结果将被存储的位置,源操作数是将被与目标操作数进行逐位逻辑与运算的值。
- **执行操作**:AND 指令执行的操作是将目标操作数与源操作数进行逐位逻辑与运算,并将结果存储到目标操作数位置。在逻辑与运算中,只有当两个对应的位都为 1 时,结果的对应位才为 1;否则,结果的对应位为 0。
- **例子**:
- `and ax, 1`:如果 AX 寄存器的值为 FFFF(十六进制,即二进制的 1111111111111111),则结果 AX 寄存器的值为 0001(二进制的 0000000000000001),因为只有最低位在两个操作数中都为 1。
- `and word ptr [123], 1234`:将内存地址 123 处的字数据与立即数 1234 进行逐位逻辑与运算,并将结果存储回内存地址 123 处。
AND 指令常用于清除特定的位或提取某些位,例如,通过与一个掩码进行逻辑与运算,可以清除不需要的位,保留需要的位。
2.14 OR 逻辑或指令
OR 逻辑或指令用于对两个操作数进行逐位逻辑或运算,并将结果存储在目标操作数中。以下是 OR 指令的详细描述:
- **指令原型**:
- `OR reg, reg/mem/imm`:表示可以将寄存器与寄存器、内存位置或立即数进行逐位逻辑或运算。
- `OR mem, reg/imm`:表示可以将内存位置与寄存器或立即数进行逐位逻辑或运算。
- **操作数个数**:OR 指令包含两个操作数,即目标操作数和源操作数。
- **指令结构**:`OR 目标操作数, 源操作数`,其中目标操作数是结果将被存储的位置,源操作数是将被与目标操作数进行逐位逻辑或运算的值。
- **执行操作**:OR 指令执行的操作是将目标操作数与源操作数进行逐位逻辑或运算,并将结果存储到目标操作数位置。在逻辑或运算中,只要两个对应的位中有一个为 1,结果的对应位就为 1;只有当两个对应的位都为 0 时,结果的对应位才为 0。
- **例子**:
- `or ax, 1`:如果 AX 寄存器的值为 FFF0(十六进制,即二进制的 1111111111110000),则结果 AX 寄存器的值为 FFF1(二进制的 1111111111110001),因为最低位在源操作数中为 1,所以结果的最低位也为 1。
- `or word ptr [123], 1234`:将内存地址 123 处的字数据与立即数 1234 进行逐位逻辑或运算,并将结果存储回内存地址 123 处。
OR 指令常用于设置特定的位或合并两个操作数的某些位,例如,通过与一个掩码进行逻辑或运算,可以确保某些位被设置为 1。
2.15 XOR 逻辑异或指令
XOR 逻辑异或指令用于对两个操作数进行逐位逻辑异或运算,并将结果存储在目标操作数中。以下是 XOR 指令的详细描述:
- **指令原型**:
- `XOR reg, reg/mem/imm`:表示可以将寄存器与寄存器、内存位置或立即数进行逐位逻辑异或运算。
- `XOR mem, reg/imm`:表示可以将内存位置与寄存器或立即数进行逐位逻辑异或运算。
- **操作数个数**:XOR 指令包含两个操作数,即目标操作数和源操作数。
- **指令结构**:`XOR 目标操作数, 源操作数`,其中目标操作数是结果将被存储的位置,源操作数是将被与目标操作数进行逐位逻辑异或运算的值。
- **执行操作**:XOR 指令执行的操作是将目标操作数与源操作数进行逐位逻辑异或运算,并将结果存储到目标操作数位置。在逻辑异或运算中,如果两个对应的位相同(都为 0 或都为 1),结果的对应位为 0;如果两个对应的位不同,结果的对应位为 1。
- **例子**:
- `xor ax, 1F`:如果 AX 寄存器的值为 FFF0(十六进制,即二进制的 1111111111110000),则结果 AX 寄存器的值为 FFEF(二进制的 1111111111101111),因为立即数 1F 的二进制表示为 00011111,与 AX 寄存器的值逐位异或后得到 FFEF。
- `xor word ptr [123], 1234`:将内存地址 123 处的字数据与立即数 1234 进行逐位逻辑异或运算,并将结果存储回内存地址 123 处。
XOR 指令常用于翻转特定的位或清除寄存器的值,例如,通过与自身进行异或运算,可以快速将寄存器的值设置为 0。
2.16 NOT 逻辑非指令
NOT 逻辑非指令用于对单个操作数进行逐位逻辑非运算,即将每个位取反(1 变为 0,0 变为 1),并将结果存储在目标操作数中。以下是 NOT 指令的详细描述:
- **指令原型**:`NOT reg/mem`,表示可以将寄存器或内存位置的值进行逐位逻辑非运算。
- **操作数个数**:NOT 指令包含一个操作数,即目标操作数。
- **指令结构**:`NOT 目标操作数`,其中目标操作数是将被逐位取反的位置。
- **执行操作**:NOT 指令执行的操作是对目标操作数进行逐位逻辑非运算,并将结果存储到目标操作数位置。具体来说,指令会将目标操作数中的每个位取反,即每个 1 变为 0,每个 0 变为 1。
- **例子**:
- `not ax`:如果 AX 寄存器的值为 FF00(十六进制,即二进制的 1111111100000000),则结果 AX 寄存器的值为 00FF(二进制的 0000000011111111),因为每个位都被取反。
NOT 指令常用于生成掩码或在数据处理中翻转位模式。例如,在某些加密算法中,NOT 指令可以用于生成数据的补码。
2.17 TEST 逻辑比较指令
TEST 逻辑比较指令用于对两个操作数进行逐位逻辑与运算,但不保存结果,而是根据运算的结果设置状态标志寄存器(EFLAGS)中的状态标志。以下是 TEST 指令的详细描述:
- **指令原型**:
- `TEST reg, reg/mem/imm`:表示可以将寄存器与寄存器、内存位置或立即数进行逐位逻辑与运算。
- `TEST mem, reg/imm`:表示可以将内存位置与寄存器或立即数进行逐位逻辑与运算。
- **操作数个数**:TEST 指令包含两个操作数,即两个源操作数。
- **指令结构**:`TEST 源操作数1, 源操作数2`,其中源操作数1是被与运算的数,源操作数2是与运算的数。
- **执行操作**:TEST 指令执行的操作是计算第一个源操作数与第二个源操作数进行逐位逻辑与运算,并根据结果设置符号标志(SF)、零标志(ZF)及奇偶标志(PF)等状态标志,然后丢弃运算的结果。具体来说,指令会对两个操作数的对应位进行与运算,如果结果为 0,则 ZF 标志被设置为 1;如果结果的最高位为 1,则 SF 标志被设置为 1;如果结果中 1 的个数为偶数,则 PF 标志被设置为 1。
- **例子**:
- `test ax, 1`:如果 AX 寄存器的值为 0,执行 TEST 指令后,由于 0 与 1 的与运算结果为 0,ZF 标志将被设置为 1,表示结果为零。
TEST 指令常用于检查某些位的状态而不改变操作数的内容,例如,程序可以使用 TEST 指令来检查一个寄存器是否为零,或者检查某些位是否被设置。
三、串操作指令
串操作指令是一组特殊的指令,用于处理内存中连续存放的数据序列,例如字符串或其他类型的数组。这些指令通常与重复操作前缀(如 REP)结合使用,以便对整个数据序列进行操作。以下是一些常见的串操作指令及其功能:
- **MOVS**:串传送指令,用于将数据从内存的一个区域复制到另一个区域。它可以处理字节、字或双字的数据。
- **STOS**:存入串指令,用于将一个值重复存储到内存中的一个区域。通常用于初始化内存区域。
- **LODS**:取出串指令,用于从内存中读取数据到寄存器。它可以处理字节、字或双字的数据。
- **CMPS**:串比较指令,用于比较两个内存区域中的数据。它可以处理字节、字或双字的数据。
- **REP**:重复操作前缀,用于重复执行紧随其后的串操作指令,直到计数寄存器(通常是 CX 或 ECX)中的值减为零。
串操作指令在处理大量连续数据时非常高效,因为它们允许CPU在一次操作中处理多个数据单元,从而减少了指令的数量和内存访问的次数。这些指令通常与方向标志(DF)结合使用,以控制数据处理的方向(向前或向后)。
3.1 STOS 存入串指令
STOS 存入串指令用于将 AL、AX 或 EAX 寄存器中的内容存储到内存中的一个区域。这些指令可以处理字节、字或双字的数据,并且通常与重复操作前缀(REP)结合使用,以便对整个数据序列进行操作。以下是 STOS 指令的详细描述:
- **指令原型**:
- `STOS mem`:表示将 AL、AX 或 EAX 寄存器中的内容存储到由目标操作数指定的内存位置。
- `STOSB` / `STOSW` / `STOSD`:分别表示将 AL、AX 或 EAX 寄存器中的字节、字或双字存储到由目标操作数指定的内存位置。
- **操作数个数**:STOS 指令包含一个操作数,即目标操作数。
- **指令结构**:`STOS 目标操作数`,其中目标操作数是数据将被存储到的内存位置。
- **执行操作**:STOS 指令执行的操作是将 AL、AX 或 EAX 寄存器中的内容存储到目标操作数指定的内存位置。具体来说,`STOSB` 指令将 AL 寄存器中的字节存储到由 DI 寄存器指向的内存位置,`STOSW` 指令将 AX 寄存器中的字存储到由 DI 寄存器指向的内存位置,`STOSD` 指令将 EAX 寄存器中的双字存储到由 DI 寄存器指向的内存位置。
- **例子(填充字符串)**:
- `lea di, [120]`:将 DI 寄存器设置为串填充的目的地址,即内存地址 120。
- `mov al, 31`:将 AL 寄存器设置为串填充的内容,31 是字符 '1' 的 ASCII 码。
- `mov cx, 10`:设置串填充的执行次数为 10。
- `cld`:清除方向标志,控制串操作方向为递增。
- `rep stosb`:使用重复前缀 REP 重复执行 STOSB 指令,直到 CX 寄存器中的值减为零。
在这个例子中,程序将字符 '1' 重复存储到从内存地址 120 开始的 10 个字节中。STOS 指令在初始化内存区域或填充字符串时非常有用。
3.2 LODS 取出串指令
LODS 取出串指令用于从内存中读取数据到 AL、AX 或 EAX 寄存器。这些指令可以处理字节、字或双字的数据,并且通常与重复操作前缀(REP)结合使用,以便对整个数据序列进行操作。以下是 LODS 指令的详细描述:
- **指令原型**:
- `LODS mem`:表示从由源操作数指定的内存位置读取数据到 AL、AX 或 EAX 寄存器。
- `LODSB` / `LODSW` / `LODSD`:分别表示从由源操作数指定的内存位置读取字节、字或双字到 AL、AX 或 EAX 寄存器。
- **操作数个数**:LODS 指令包含一个操作数,即源操作数。
- **指令结构**:`LODS 源操作数`,其中源操作数是数据将被读取的内存位置。
- **执行操作**:LODS 指令执行的操作是将源操作数中的字节、字或双字分别加载到 AL、AX 或 EAX 寄存器。具体来说,`LODSB` 指令将由 SI 寄存器指向的内存位置的字节加载到 AL 寄存器,`LODSW` 指令将由 SI 寄存器指向的内存位置的字加载到 AX 寄存器,`LODSD` 指令将由 SI 寄存器指向的内存位置的双字加载到 EAX 寄存器。
- **例子(计算数组元素之和)**:
- `lea si, [120]`:将 SI 寄存器设置为数组的起始地址,即内存地址 120。
- `mov cx, 10`:设置循环的执行次数为 10。
- `xor ax, ax`:将 AX 寄存器清空,方便存储读取到的内容。
- `xor dx, dx`:将 DX 寄存器清空,保存累加结果。
- `lodsb`:将指定位置的字符读取到 AL 寄存器中。
- `add dx, ax`:将 AL 寄存器中的值(即读取到的字符)加到 DX 寄存器中,进行加法计算。
- `loop 10B`:跳转到标签 `10B` 处的 `lodsb` 指令,并将 CX 寄存器减 1,直到 CX 为 0。
在这个例子中,程序从内存地址 120 开始的数组中读取字节,并将这些字节的值累加到 DX 寄存器中。LODS 指令在处理数组或其他连续数据结构时非常有用。
3.3 CMPS 串比较指令
CMPS 串比较指令用于比较两个内存区域中的数据。这些指令可以处理字节、字或双字的数据,并且通常与重复操作前缀(REP 或 REPE)结合使用,以便对整个数据序列进行操作。以下是 CMPS 指令的详细描述:
- **指令原型**:
- `CMPS mem, mem`:表示比较两个内存区域中的数据。
- `CMPSB` / `CMPSW` / `CMPSD`:分别表示比较两个内存区域中的字节、字或双字。
- **操作数个数**:CMPS 指令包含两个操作数,即两个源操作数。
- **指令结构**:`CMPS 源操作数1, 源操作数2`,其中源操作数1和源操作数2是将被比较的内存位置。
- **执行操作**:CMPS 指令执行的操作是比较第一个源操作数指定的字节、字或双字与第二个源操作数指定的字节、字或双字,并根据比较结果设置 EFLAGS 寄存器中的状态标志,如零标志(ZF)、进位标志(CF)等。
- **例子(比较字符串)**:
- `lea si, [100]`:将 SI 寄存器设置为串比较的源地址,即内存地址 100。
- `lea di, [120]`:将 DI 寄存器设置为串比较的目的地址,即内存地址 120。
- `mov cx, 10`:设置比较的字节数为 10。
- `cld`:清除方向标志 DF,控制串操作方向为递增。
- `repe cmpsb`:循环比较,直到 CX 为 0 或 ZF 为 0 时结束。
- `je 114`:如果相等,则跳转到标签 `114` 处。
- `mov bx, 1`:否则将 BX 寄存器置为 1。
在这个例子中,程序从内存地址 100 和 120 开始的两个字符串进行比较,直到比较完 10 个字节或发现不相等的字节。如果所有字节都相等,程序将跳转到标签 `114` 处;否则,BX 寄存器将被设置为 1。CMPS 指令在字符串比较和数据验证等场景中非常有用。
3.4 REP 重复操作前缀
REP 重复操作前缀用于重复执行紧随其后的字符串指令,直到计数寄存器 (E)CX 中的值减为零,或者直到零标志 (ZF) 不再满足指定的条件为止。以下是 REP 前缀的详细描述:
- **指令原型**:
- `REP`:无条件重复执行字符串指令,直到 (E)CX 为零。
- `REPE` 或 `REPZ`:重复执行字符串指令,直到 (E)CX 为零或 ZF 为零(即比较结果不相等)。
- `REPNE` 或 `REPNZ`:重复执行字符串指令,直到 (E)CX 为零或 ZF 为 1(即比较结果相等)。
- **操作数个数**:REP 前缀不包含操作数。
- **指令结构**:`REP/REPE/REPNE 字符串指令`,其中字符串指令是将被重复执行的指令。
- **执行操作**:REP 前缀执行的操作是按计数寄存器 (E)CX 中指定的次数重复执行字符串指令,或是重复到 ZF 标志不再满足指定的条件为止。具体来说,REP 前缀会在每次执行字符串指令后将 (E)CX 寄存器减 1,并检查 (E)CX 是否为零或 ZF 是否满足条件,如果不满足,则停止重复。
- **例子(比较字符串)**:
- `lea si, [100]`:将 SI 寄存器设置为串比较的源地址,即内存地址 100。
- `lea di, [120]`:将 DI 寄存器设置为串比较的目的地址,即内存地址 120。
- `mov cx, 10`:设置比较的字节数为 10。
- `cld`:清除方向标志 DF,控制串操作方向为递增。
- `repe cmpsb`:循环比较,直到 CX 为 0 或 ZF 为 0 时结束。
- `je 114`:如果相等,则跳转到标签 `114` 处。
- `mov bx, 1`:否则将 BX 寄存器置为 1。
在这个例子中,程序从内存地址 100 和 120 开始的两个字符串进行比较,直到比较完 10 个字节或发现不相等的字节。如果所有字节都相等,程序将跳转到标签 `114` 处;否则,BX 寄存器将被设置为 1。REP 前缀在处理大量连续数据时非常有用,特别是在字符串操作和数据验证等场景中。
四、控制转移指令
控制转移指令是用于改变程序执行流程的一组指令,它们可以导致当前指令的执行轨迹发生改变,或者执行调用子程序的功能。以下是一些常见的控制转移指令及其功能:
- **JMP**:无条件转移指令,用于无条件地跳转到指定的内存地址或标签处继续执行程序。
- **Jcc**:有条件转移指令,其中 "cc" 代表条件码,表示只有在特定的条件满足时,程序才会跳转到指定的内存地址或标签处。
- **LOOP**:循环指令,用于根据计数寄存器 (E)CX 的值重复执行一段代码,每次执行后 (E)CX 的值会减 1,直到 (E)CX 为零时结束循环。
- **CALL**:函数调用指令,用于调用一个子程序或函数,并将当前的执行位置(返回地址)压入堆栈,以便在子程序执行完毕后返回到调用点。
- **RET**:返回指令,用于从子程序或函数返回到调用点,通常是从堆栈中弹出返回地址,并跳转到该地址继续执行。
控制转移指令在编程中非常重要,它们允许程序根据不同的条件执行不同的代码路径,从而实现复杂的逻辑和功能。这些指令通常与条件码(如零标志 ZF、进位标志 CF 等)结合使用,以实现条件判断和循环控制。
4.1 JMP 无条件转移指令
JMP 无条件转移指令用于无条件地改变程序的执行流程,使程序跳转到指定的目标地址处继续执行。以下是 JMP 指令的详细描述:
- **指令原型**:`JMP reg/mem/imm`,表示可以跳转到由寄存器、内存位置或立即数指定的目标地址。
- **操作数个数**:JMP 指令包含一个操作数,即源操作数。
- **指令结构**:`JMP 源操作数`,其中源操作数是程序将要跳转到的目标地址。
- **执行操作**:JMP 指令执行的操作是使程序无条件地转移到源操作数指定的目标地址处,并从该地址开始执行指令。
- **例子**:
- `Jmp ax`:程序将跳转到 AX 寄存器中存储的地址处继续执行。
- `jmp 120`:程序将跳转到内存地址 120 处继续执行。
JMP 指令在编程中用于实现无条件的跳转,例如在循环、分支或错误处理等场景中。它允许程序员在不满足任何条件的情况下改变程序的执行路径。
4.2 寻址方式
寻址方式是指定操作数位置的方法,它决定了如何计算操作数的有效地址。在转移指令中,寻址方式决定了程序如何找到跳转的目标地址。以下是一些常见的寻址方式及其描述:
- **直接寻址方式**:转移地址像立即数一样,直接包含在指令的机器代码中。这种寻址方式称为直接寻址。
- **间接寻址方式**:转移地址存储在寄存器或主存单元中,通过寄存器或存储器来间接获取转移地址。这种寻址方式称为间接寻址。
- **段内转移**:
- **近转移 (near)**:在当前代码段的 64KB 范围内转移(实际转移范围通常是 -32KB 到 +32KB)。这种转移不需要更改代码段寄存器 (CS),只需要改变指令指针 (IP) 的偏移地址。
- **短转移 (short)**:转移范围可以用一个字节表达,即在段内 -128 到 +127 的范围内的转移。这种转移是一种特殊的近转移。
- **段间转移**:
- **远转移 (far)**:从当前代码段跳转到另一个代码段,可以在 1MB 范围内转移。这种转移需要更改 CS 段地址和 IP 偏移地址。目标地址必须用一个 32 位数表达,称为 32 位远指针,它是逻辑地址。
在实际编程时,汇编程序会根据目标地址的距离,自动处理成短转移、近转移或远转移。程序员可以使用操作符 `short`、`near ptr` 或 `far ptr` 来强制指定转移类型。这些操作符允许程序员明确指定转移的范围,以优化代码的执行效率和空间使用。
4.3 Jxx条件转移指令
4.4 CALL 子程序指令
CALL 子程序指令用于调用一个子程序或函数,它会将当前的执行位置(返回地址)压入堆栈,以便在子程序执行完毕后返回到调用点。以下是 CALL 指令的详细描述:
- **指令原型**:`CALL reg/mem/imm`,表示可以调用由寄存器、内存位置或立即数指定的子程序。
- **操作数个数**:CALL 指令包含一个操作数,即源操作数。
- **指令结构**:`CALL 源操作数`,其中源操作数是子程序的起始地址。
- **执行操作**:当主程序(调用程序)需要执行某个功能时,采用 CALL 指令转移到该子程序的起始处执行。当子程序运行完毕后,采用 RET 返回指令回到主程序继续执行。
- **例子**:
- `call ax`:程序将调用 AX 寄存器中存储的地址处的子程序。
- `call 120`:程序将调用内存地址 120 处的子程序。
CALL 指令在编程中用于实现函数或子程序的调用,它允许程序员将复杂的任务分解成更小的、可重用的模块。通过使用 CALL 和 RET 指令,程序可以在不同的执行上下文中切换,从而实现模块化和结构化的编程。
4.5 RET 子程序返回指令
RET 子程序返回指令用于从子程序或函数返回到调用点,它会将堆栈中保存的返回地址弹出,并跳转到该地址继续执行。以下是 RET 指令的详细描述:
- **指令原型**:
- `RET imm`:段内返回指令,可以带有一个立即数(i16),表示返回时堆栈指针 SP 将增加的值。
- `RETF imm`:段间返回指令,可以带有一个立即数(i16),表示返回时堆栈指针 SP 将增加的值。
- `RETN imm`:段内返回指令的另一种形式,通常等同于 `RET imm`。
- **操作数个数**:RET 指令包含一个操作数,即立即数(imm)。
- **指令结构**:`RET 源操作数`,其中源操作数是立即数,表示返回时堆栈指针 SP 将增加的值。
- **执行操作**:RET 指令执行的操作是从堆栈中弹出返回地址,并跳转到该地址继续执行。如果 RET 指令带有一个立即数(i16),则在弹出返回地址后,堆栈指针 SP 将增加该立即数的值,这个特点使得程序可以方便地废除若干在执行 CALL 指令以前入栈的参数。
- **例子**:
- `ret 8`:从子程序返回,并且堆栈指针 SP 将增加 8,以废除在调用子程序前入栈的 8 字节参数。
RET 指令在编程中用于结束子程序或函数的执行,并返回到调用点。通过使用 RET 指令,程序可以在不同的执行上下文中切换,从而实现模块化和结构化的编程。带立即数的 RET 指令特别适用于那些在调用时需要传递参数的子程序,它允许子程序在返回时自动清理堆栈中的参数。