-
-
[分享]8086汇编语言基础知识
-
发表于: 4天前 1024
-
8086汇编语言基础知识
参考书目:王爽《汇编语言》(第4版)
处理器架构:Intel 8086
一、基础原理
1.1 计算机基本结构
计算机由 CPU(中央处理器)、内存(存储器)和 I/O 设备(外部设备)三大部分组成。CPU 是计算机的核心,负责执行指令;内存用于存储程序和数据;I/O 设备实现人机交互和数据输入输出。
CPU 通过总线(Bus) 与内存和 I/O 设备连接。8086 CPU 的外部总线分为三类:
| 总线类型 | 宽度 | 作用 | 说明 |
|---|---|---|---|
| 地址总线(Address Bus) | 20 根 | 传送地址信息 | 可寻址 2^20 = 1MB 内存空间 |
| 数据总线(Data Bus) | 16 根 | 传送数据信息 | 一次可传送 16 位(2 字节)数据 |
| 控制总线(Control Bus) | 若干根 | 传送控制信号 | 包括读/写信号、中断请求、中断响应等 |
1.2 内存与地址
内存是计算机中用于存储程序和数据的部件。内存被划分为若干个存储单元,每个存储单元可存储 1 字节(8 bit)的数据。每个存储单元都有一个唯一的编号,称为地址(Address)。
8086 CPU 有 20 根地址线,因此可寻址的内存空间为 2^20 = 1,048,576 字节 = 1MB,地址范围为 00000H ~ FFFFFH。
关键概念:
| 概念 | 说明 |
|---|---|
| 字节(Byte) | 8 个二进制位(bit),内存的基本存储单位 |
| 字(Word) | 16 个二进制位,即 2 字节,8086 的基本字长 |
| 双字(Double Word) | 32 个二进制位,即 4 字节 |
| 物理地址 | CPU 实际输出到地址总线上的 20 位地址,唯一标识一个内存单元 |
| 段地址 | 16 位的段基址,用于在段式内存管理中定位段的起始位置 |
| 偏移地址 | 16 位的段内偏移量,表示数据在段内相对于段起始位置的偏移 |
1.3 段地址与物理地址的计算
8086 CPU 内部所有寄存器都是 16 位的,只能直接处理 16 位数据,但地址总线是 20 位的,需要 20 位地址。为了解决这个矛盾,8086 采用段地址 + 偏移地址的方式来形成 20 位物理地址。
物理地址计算公式:
物理地址 = 段地址 × 10H + 偏移地址
即段地址左移 4 位(乘以 16),再加上偏移地址,得到 20 位物理地址。
示例:
| 段地址 | 偏移地址 | 计算过程 | 物理地址 |
|---|---|---|---|
| 2000H | 0030H | 2000H × 10H + 0030H = 20000H + 0030H | 20030H |
| FFFFH | 0000H | FFFFH × 10H + 0000H = FFFF0H + 0000H | FFFF0H |
| 1230H | 0089H | 1230H × 10H + 0089H = 12300H + 0089H | 12389H |
| 0000H | FFFFH | 0000H × 10H + FFFFH = 00000H + FFFFH | 0FFFFH |
段的特性:
- 段的起始地址必须是 16 的倍数(即段地址 × 10H)
- 段的最大长度为 64KB(偏移地址范围 0000H ~ FFFFH)
- 一个物理地址可以由多个不同的"段地址:偏移地址"组合表示(例如 12389H 可以是 1230H:0089H,也可以是 1238H:0009H)
1.4 内存分段
8086 将 1MB 内存划分为若干个逻辑段,每个段用于存放特定用途的信息。常见的段类型如下:
| 段类型 | 用途 | 对应段寄存器 | 说明 |
|---|---|---|---|
| 代码段(Code Segment) | 存放程序的指令代码 | CS | CPU 从 CS:IP 指向的地址读取指令执行 |
| 数据段(Data Segment) | 存放程序使用的数据 | DS | 程序中访问数据时默认使用 DS 作为段地址 |
| 栈段(Stack Segment) | 用于栈操作,存放临时数据 | SS | 栈顶地址由 SS:SP 指向 |
| 附加段(Extra Segment) | 附加数据段,常用于串操作 | ES | 串操作指令中目的串的段地址 |
1.5 指令执行过程
CPU 执行指令的基本过程如下:
- CPU 从 CS:IP 指向的内存单元读取指令(取指)
- IP 自动增加,指向下一条指令(IP = IP + 指令长度)
- CPU 对取出的指令进行译码(译码)
- CPU 执行该指令所规定的操作(执行)
- 回到第 1 步,继续下一条指令的执行
关键要点:
- CS(代码段寄存器)和 IP(指令指针寄存器)共同决定 CPU 当前要执行的指令地址
- CPU 加电或复位时,CS = FFFFH,IP = 0000H,物理地址 = FFFF0H,这是 BIOS 程序的入口
- 8086 的指令长度不固定,从 1 字节到 6 字节不等
1.6 栈(Stack)
栈是一种后进先出(LIFO) 的数据结构,8086 在硬件层面支持栈操作。栈段由 SS:SP 指定:SS 为栈段段地址,SP 为栈顶偏移地址。
8086 的栈是向下生长的,即每入栈一个数据,SP 减小。
| 操作 | 指令 | SP 变化 | 说明 |
|---|---|---|---|
| 入栈(Push) | PUSH AX | SP = SP - 2 | 先将 SP 减 2,再将 AX 的内容存入 SS:SP 指向的内存单元 |
| 出栈(Pop) | POP AX | SP = SP + 2 | 先将 SS:SP 指向的内存单元的内容取出送入 AX,再将 SP 加 2 |
| 入栈(Push) | PUSH BX | SP = SP - 2 | 将 BX 的值压入栈中 |
| 出栈(Pop) | POP DS | SP = SP + 2 | 从栈中弹出一个字送入 DS |
注意: 8086 的栈操作以**字(16 位)**为单位,每次入栈或出栈都是 2 字节。
1.7 BIOS 与 DOS
| 概念 | 说明 |
|---|---|
| BIOS(基本输入输出系统) | 固化在 ROM 中的程序,提供最基本的硬件控制服务,开机后首先运行 |
| DOS(磁盘操作系统) | 运行在 BIOS 之上的操作系统,提供文件管理、程序加载等服务 |
| 中断(Interrupt) | CPU 暂停当前程序,转去执行中断处理程序,执行完后返回原程序继续执行 |
| 中断向量 | 中断处理程序的入口地址,存放在内存 0000:0000 开始的中断向量表中 |
| 中断向量表 | 位于内存 0000H:0000H ~ 0000H:03FFH,共 256 个中断向量,每个占 4 字节 |
1.8 寻址方式
8086 汇编语言支持多种寻址方式,用于确定操作数的来源:
| 寻址方式 | 说明 | 示例 | 操作数位置 |
|---|---|---|---|
| 立即寻址 | 操作数直接包含在指令中 | MOV AX, 1234H | 指令本身 |
| 寄存器寻址 | 操作数在寄存器中 | MOV AX, BX | 寄存器 |
| 直接寻址 | 操作数的偏移地址直接给出 | MOV AX, [1234H] | 内存,DS:1234H |
| 寄存器间接寻址 | 操作数的偏移地址在寄存器中 | MOV AX, [BX] | 内存,DS:[BX] |
| 寄存器相对寻址 | 寄存器内容加位移量 | MOV AX, [BX+08H] | 内存,DS:[BX+08H] |
| 基址变址寻址 | 基址寄存器加变址寄存器 | MOV AX, [BX+SI] | 内存,DS:[BX+SI] |
| 相对基址变址寻址 | 基址+变址+位移量 | MOV AX, [BX+SI+08H] | 内存,DS:[BX+SI+08H] |
可用于间接寻址的寄存器:
| 寄存器 | 默认段寄存器 | 说明 |
|---|---|---|
| BX | DS | 基址寄存器 |
| BP | SS | 基址指针,默认使用栈段 |
| SI | DS | 源变址寄存器 |
| DI | DS | 目的变址寄存器 |
1.9 数据类型与存储顺序
8086 采用小端模式(Little-Endian):低字节存放在低地址,高字节存放在高地址。
| 数据 | 占用字节 | 内存存储示例(地址从低到高) |
|---|---|---|
| 字节(Byte) 45H | 1 字节 | 45 |
| 字(Word) 1234H | 2 字节 | 34 12 |
| 双字(DWord) 12345678H | 4 字节 | 78 56 34 12 |
二、寄存器
2.1 通用寄存器
8086 CPU 有 4 个 16 位通用寄存器,每个都可以拆分为两个独立的 8 位寄存器使用。
| 16位寄存器 | 高8位 | 低8位 | 主要用途 | 特殊用途 |
|---|---|---|---|---|
| AX(累加器) | AH | AL | 算术运算、逻辑运算的默认操作数 | 乘除法中隐含使用;I/O 指令中用于传送数据 |
| BX(基址寄存器) | BH | BL | 存放内存操作数的基址偏移量 | 可用于寄存器间接寻址 [BX] |
| CX(计数寄存器) | CH | CL | 通用数据存储 | LOOP 指令和串操作 REP 前缀中用作循环计数器;移位指令中指定移位次数 |
| DX(数据寄存器) | DH | DL | 通用数据存储 | 乘法中存放高 16 位结果;除法中存放被除数的高 16 位;间接 I/O 中存放端口地址 |
通用寄存器的拆分:
AX = AH : AL (AH 为高 8 位,AL 为低 8 位)
BX = BH : BL
CX = CH : CL
DX = DH : DL
各寄存器在乘除法中的隐含使用:
| 运算 | 指令 | 隐含使用的寄存器 | 说明 |
|---|---|---|---|
| 字节乘法 | MUL BL | AX = AL × BL | 被乘数在 AL,结果在 AX |
| 字乘法 | MUL BX | DX:AX = AX × BX | 被乘数在 AX,结果高位在 DX,低位在 AX |
| 字节除法 | DIV BL | AL = AX / BL,AH = AX % BL | 被除数在 AX,商在 AL,余数在 AH |
| 字除法 | DIV BX | AX = DX:AX / BX,DX = DX:AX % BX | 被除数在 DX:AX,商在 AX,余数在 DX |
2.2 段寄存器
| 寄存器 | 全称 | 作用 | 默认使用场景 |
|---|---|---|---|
| CS | 代码段寄存器(Code Segment) | 存放当前执行代码所在段的段地址 | CPU 取指令时使用 CS:IP |
| DS | 数据段寄存器(Data Segment) | 存放程序数据所在段的段地址 | 访问数据时默认使用 DS 作为段地址 |
| SS | 栈段寄存器(Stack Segment) | 存放栈所在段的段地址 | PUSH/POP 操作使用 SS:SP |
| ES | 附加段寄存器(Extra Segment) | 存放附加数据段的段地址 | 串操作中目的串使用 ES:DI |
注意: 段寄存器不能直接参与算术运算,必须通过通用寄存器中转。例如:
MOV AX, 1000H ; 先将段地址送入 AX
MOV DS, AX ; 再通过 AX 转送到 DS
2.3 控制寄存器
| 寄存器 | 全称 | 位数 | 作用 |
|---|---|---|---|
| IP | 指令指针寄存器(Instruction Pointer) | 16 位 | 存放下一条要执行指令的偏移地址,与 CS 配合形成指令的物理地址 |
| FLAGS | 标志寄存器 | 16 位(使用 9 位) | 存放运算结果的状态标志和控制标志 |
IP 的特殊性: IP 不能被直接读写(没有 MOV IP, AX 这样的指令),只能通过跳转指令(JMP、CALL、RET、LOOP 等)来改变其值。
2.4 标志寄存器(FLAGS)详细定义
FLAGS 寄存器中各标志位的位置(位编号从 0 开始):
位编号: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
标志位: - - - - OF DF IF TF SF ZF - AF - PF - CF
2.4.1 状态标志位
| 标志位 | 名称 | 位置 | 为 1 时含义 | 为 0 时含义 | 设置条件 |
|---|---|---|---|---|---|
| CF | 进位标志(Carry Flag) | 第 0 位 | 无符号运算产生进位/借位 | 无符号运算无进位/借位 | 最高位有进位或借位时置 1 |
| PF | 奇偶标志(Parity Flag) | 第 2 位 | 结果低 8 位中 1 的个数为偶数 | 结果低 8 位中 1 的个数为奇数 | 根据结果低 8 位中 1 的个数设置 |
| AF | 辅助进位标志(Auxiliary Carry Flag) | 第 4 位 | 低 4 位向高 4 位有进位/借位 | 低 4 位向高 4 位无进位/借位 | 第 3 位向第 4 位有进位或借位时置 1 |
| ZF | 零标志(Zero Flag) | 第 6 位 | 运算结果为零 | 运算结果不为零 | 运算结果为全 0 时置 1 |
| SF | 符号标志(Sign Flag) | 第 7 位 | 运算结果为负(最高位为 1) | 运算结果为非负(最高位为 0) | 等于结果的最高位 |
| OF | 溢出标志(Overflow Flag) | 第 11 位 | 有符号运算产生溢出 | 有符号运算无溢出 | 有符号运算结果超出表示范围时置 1 |
2.4.2 控制标志位
| 标志位 | 名称 | 位置 | 为 1 时含义 | 为 0 时含义 | 用途 |
|---|---|---|---|---|---|
| TF | 陷阱标志(Trap Flag) | 第 8 位 | 单步执行模式 | 正常执行模式 | 用于调试,每执行一条指令产生单步中断 |
| IF | 中断允许标志(Interrupt Flag) | 第 9 位 | 允许可屏蔽中断 | 禁止可屏蔽中断 | 控制 CPU 是否响应外部可屏蔽中断请求 |
| DF | 方向标志(Direction Flag) | 第 10 位 | 串操作地址递减(从高到低) | 串操作地址递增(从低到高) | 控制串操作指令中 SI/DI 的增减方向 |
2.4.3 OF 与 CF 的区别
| 对比项 | CF(进位标志) | OF(溢出标志) |
|---|---|---|
| 适用对象 | 无符号数运算 | 有符号数运算 |
| 含义 | 最高位是否产生进位/借位 | 有符号数运算结果是否超出表示范围 |
| 字节运算溢出范围 | 无符号 0 ~ 255 | 有符号 -128 ~ +127 |
| 字运算溢出范围 | 无符号 0 ~ 65535 | 有符号 -32768 ~ +32767 |
| 典型使用场景 | 无符号数比较(JA/JB) | 有符号数比较(JG/JL) |
2.5 变址寄存器
| 寄存器 | 全称 | 默认段 | 用途 |
|---|---|---|---|
| SI | 源变址寄存器(Source Index) | DS | 串操作中作为源操作数的偏移地址;通用寻址中的变址寄存器 |
| DI | 目的变址寄存器(Destination Index) | ES | 串操作中作为目的操作数的偏移地址;通用寻址中的变址寄存器 |
| BP | 基址指针寄存器(Base Pointer) | SS | 通常用于访问栈段中的数据;访问函数参数和局部变量 |
| SP | 栈指针寄存器(Stack Pointer) | SS | 始终指向栈顶,与 SS 配合确定栈顶物理地址 |
2.6 寄存器总览
| 类别 | 寄存器 | 位数 | 可否直接赋值 |
|---|---|---|---|
| 通用寄存器 | AX, BX, CX, DX | 16 位 | 可以(MOV AX, 1234H) |
| 通用寄存器(8位) | AH, AL, BH, BL, CH, CL, DH, DL | 8 位 | 可以(MOV AL, 45H) |
| 变址/指针寄存器 | SI, DI, BP, SP | 16 位 | 可以(MOV SI, 1000H) |
| 段寄存器 | CS, DS, SS, ES | 16 位 | 可以,但须通过通用寄存器中转 |
| 控制寄存器 | IP | 16 位 | 不可以,只能通过跳转指令修改 |
| 标志寄存器 | FLAGS | 16 位 | 不可以,由运算结果自动设置或通过专用指令修改 |
三、汇编指令
3.1 数据传送指令
| 指令 | 含义 | 作用 | 格式/用法示例 | 对标志位影响 |
|---|---|---|---|---|
| MOV | 传送(Move) | 将源操作数的值复制到目的操作数 | MOV AX, BX(寄存器间传送) MOV AX, 1234H(立即数送寄存器) MOV [BX], AX(寄存器送内存) MOV AX, [BX](内存送寄存器) |
不影响任何标志位 |
| PUSH | 入栈(Push) | 将操作数压入栈中 | PUSH AX(将 AX 压栈) PUSH [BX](将内存字数据压栈) |
不影响标志位 |
| POP | 出栈(Pop) | 从栈中弹出数据到操作数 | POP AX(弹出栈顶数据到 AX) POP DS(弹出栈顶数据到 DS) |
不影响标志位 |
| XCHG | 交换(Exchange) | 交换两个操作数的值 | XCHG AX, BX(交换 AX 和 BX) XCHG AL, [BX](交换 AL 和内存字节) |
不影响标志位 |
| LEA | 取有效地址(Load Effective Address) | 将操作数的偏移地址送入寄存器 | LEA AX, [BX+SI+08H](将有效地址送入 AX) | 不影响标志位 |
| LDS | 加载 DS 和寄存器 | 从内存中取双字,低字送指定寄存器,高字送 DS | LDS SI, [BX]([BX]低字→SI,[BX+2]高字→DS) | 不影响标志位 |
| LES | 加载 ES 和寄存器 | 从内存中取双字,低字送指定寄存器,高字送 ES | LES DI, [BX]([BX]低字→DI,[BX+2]高字→ES) | 不影响标志位 |
MOV 指令的限制规则:
- 不能在两个内存单元之间直接传送(MOV [SI], [DI] 是非法的)
- 不能将立即数直接送入段寄存器(MOV DS, 1000H 是非法的)
- 不能在两个段寄存器之间直接传送(MOV ES, DS 是非法的)
- 源操作数和目的操作数的类型(字节/字)必须一致
3.2 算术运算指令
3.2.1 加法与减法
| 指令 | 含义 | 作用 | 格式/用法示例 | 对标志位影响 |
|---|---|---|---|---|
| ADD | 加法(Add) | 将两个操作数相加,结果送目的操作数 | ADD AX, BX(AX = AX + BX) ADD AX, 1234H |
影响 CF、ZF、SF、OF、PF、AF |
| ADC | 带进位加法(Add with Carry) | 两个操作数相加再加上 CF | ADC AX, BX(AX = AX + BX + CF) | 影响 CF、ZF、SF、OF、PF、AF |
| SUB | 减法(Subtract) | 目的操作数减去源操作数 | SUB AX, BX(AX = AX - BX) | 影响 CF、ZF、SF、OF、PF、AF |
| SBB | 带借位减法(Subtract with Borrow) | 目的操作数减去源操作数再减去 CF | SBB AX, BX(AX = AX - BX - CF) | 影响 CF、ZF、SF、OF、PF、AF |
| INC | 加 1(Increment) | 操作数加 1 | INC AX(AX = AX + 1) INC CX |
影响 ZF、SF、OF、PF、AF,不影响 CF |
| DEC | 减 1(Decrement) | 操作数减 1 | DEC AX(AX = AX - 1) DEC BX |
影响 ZF、SF、OF、PF、AF,不影响 CF |
| NEG | 取补(Negate) | 对操作数取补码(即取相反数) | NEG AX(AX = -AX,等价于 0 - AX) | 影响所有状态标志位 |
| CMP | 比较(Compare) | 目的操作数减源操作数,但不保存结果,仅设置标志位 | CMP AX, BX(计算 AX - BX,只设置标志位) | 影响 CF、ZF、SF、OF、PF、AF |
3.2.2 乘法
| 指令 | 含义 | 作用 | 格式/用法 | 隐含操作数 | 结果存放 |
|---|---|---|---|---|---|
| MUL | 无符号乘法 | 无符号数相乘 | MUL BL | 被乘数:AL | AX = AL × BL |
| MUL | 无符号乘法 | 无符号数相乘 | MUL BX | 被乘数:AX | DX:AX = AX × BX |
| IMUL | 有符号乘法 | 有符号数相乘 | IMUL BL | 被乘数:AL | AX = AL × BL(有符号) |
| IMUL | 有符号乘法 | 有符号数相乘 | IMUL BX | 被乘数:AX | DX:AX = AX × BX(有符号) |
乘法说明:
- 字节乘:AL × 源操作数 → AX(结果 16 位)
- 字乘:AX × 源操作数 → DX:AX(结果 32 位,DX 存高 16 位,AX 存低 16 位)
- MUL 用于无符号数,IMUL 用于有符号数
- 如果结果的高半部分(AH 或 DX)不为 0,则 CF = OF = 1
3.2.3 除法
| 指令 | 含义 | 作用 | 格式/用法 | 被除数 | 商 | 余数 |
|---|---|---|---|---|---|---|
| DIV | 无符号除法 | 无符号数相除 | DIV BL | AX(16 位被除数) | AL(8 位商) | AH(8 位余数) |
| DIV | 无符号除法 | 无符号数相除 | DIV BX | DX:AX(32 位被除数) | AX(16 位商) | DX(16 位余数) |
| IDIV | 有符号除法 | 有符号数相除 | IDIV BL | AX(有符号) | AL | AH |
| IDIV | 有符号除法 | 有符号数相除 | IDIV BX | DX:AX(有符号) | AX | DX |
除法说明:
- 字节除:AX / 源操作数 → 商在 AL,余数在 AH
- 字除:DX:AX / 源操作数 → 商在 AX,余数在 DX
- 如果商超出寄存器能表示的范围,将触发除法溢出中断(中断 0)
- DIV 用于无符号数,IDIV 用于有符号数
3.2.4 符号扩展指令
| 指令 | 含义 | 作用 | 说明 |
|---|---|---|---|
| CBW | 字节扩展为字(Convert Byte to Word) | 将 AL 的符号位扩展到 AH | AL 为正数时 AH = 00H;AL 为负数时 AH = FFH |
| CWD | 字扩展为双字(Convert Word to Double Word) | 将 AX 的符号位扩展到 DX | AX 为正数时 DX = 0000H;AX 为负数时 DX = FFFFH |
3.3 逻辑运算指令
| 指令 | 含义 | 作用 | 格式/用法示例 | 对标志位影响 |
|---|---|---|---|---|
| AND | 逻辑与 | 按位进行与运算 | AND AX, 0FF0H(屏蔽 AX 低 4 位) AND AL, 0DFH(将第 5 位清 0) |
CF=0,OF=0,影响 ZF、SF、PF |
| OR | 逻辑或 | 按位进行或运算 | OR AX, 000FH(将 AX 低 4 位置 1) OR AL, 20H(将第 5 位置 1) |
CF=0,OF=0,影响 ZF、SF、PF |
| NOT | 逻辑非 | 按位取反 | NOT AX(AX 各位取反) | 不影响任何标志位 |
| XOR | 逻辑异或 | 按位进行异或运算 | XOR AX, AX(将 AX 清零) XOR AL, 20H(翻转第 5 位) |
CF=0,OF=0,影响 ZF、SF、PF |
| TEST | 测试 | 按位进行与运算,但不保存结果,仅设置标志位 | TEST AL, 01H(测试最低位是否为 1) | CF=0,OF=0,影响 ZF、SF、PF |
常用技巧:
| 操作 | 指令 | 说明 |
|---|---|---|
| 寄存器清零 | XOR AX, AX | 比 MOV AX, 0 更高效(指令更短) |
| 屏蔽特定位 | AND AL, 0F0H | 保留高 4 位,低 4 位清零 |
| 设置特定位 | OR AL, 01H | 将最低位置 1 |
| 翻转特定位 | XOR AL, 0FFH | 将 AL 所有位翻转 |
| 测试特定位 | TEST AL, 80H | 测试最高位是否为 1(不改变 AL 的值) |
3.4 移位与循环移位指令
3.4.1 移位指令
| 指令 | 含义 | 作用 | 格式/用法 | 对标志位影响 |
|---|---|---|---|---|
| SHL | 逻辑左移(Shift Left) | 各位左移,低位补 0 | SHL AX, 1(左移 1 位) SHL AX, CL(左移 CL 位) |
CF = 最后移出的位,影响 ZF、SF、PF、OF |
| SHR | 逻辑右移(Shift Right) | 各位右移,高位补 0 | SHR AX, 1(右移 1 位) | CF = 最后移出的位,影响 ZF、SF、PF、OF |
| SAL | 算术左移(Shift Arithmetic Left) | 与 SHL 相同,各位左移,低位补 0 | SAL AX, 1 | 同 SHL |
| SAR | 算术右移(Shift Arithmetic Right) | 各位右移,高位补符号位(保持符号不变) | SAR AX, 1(右移 1 位,符号位不变) | CF = 最后移出的位,影响 ZF、SF、PF、OF |
移位指令说明:
- 移位 1 位时直接写 1(如 SHL AX, 1)
- 移位多位时必须将移位次数放入 CL 寄存器(如 MOV CL, 4 然后 SHL AX, CL)
- SHL 左移 1 位相当于无符号数乘以 2
- SHR 右移 1 位相当于无符号数除以 2
- SAR 右移 1 位相当于有符号数除以 2(保持符号)
3.4.2 循环移位指令
| 指令 | 含义 | 作用 | 格式/用法 | 对标志位影响 |
|---|---|---|---|---|
| ROL | 循环左移(Rotate Left) | 各位左移,最高位移入最低位,同时移入 CF | ROL AX, 1 | CF = 最后移出的位(最高位),OF 在移 1 位时受影响 |
| ROR | 循环右移(Rotate Right) | 各位右移,最低位移入最高位,同时移入 CF | ROR AX, 1 | CF = 最后移出的位(最低位),OF 在移 1 位时受影响 |
| RCL | 带进位循环左移(Rotate through Carry Left) | 各位左移,CF 移入最低位,最高位移入 CF | RCL AX, 1 | CF = 最后移出的位(最高位) |
| RCR | 带进位循环右移(Rotate through Carry Right) | 各位右移,CF 移入最高位,最低位移入 CF | RCR AX, 1 | CF = 最后移出的位(最低位) |
3.5 控制转移指令
3.5.1 无条件转移
| 指令 | 含义 | 作用 | 格式/用法 | 说明 |
|---|---|---|---|---|
| JMP | 无条件跳转(Jump) | 无条件转移到目标地址执行 | JMP label(段内直接跳转) JMP AX(段内间接跳转) JMP FAR PTR label(段间直接跳转) |
修改 IP(段内)或 CS:IP(段间) |
JMP 的跳转类型:
| 类型 | 格式 | 修改的寄存器 | 说明 |
|---|---|---|---|
| 段内直接跳转 | JMP label | IP | 目标地址在编译时确定 |
| 段内间接跳转 | JMP AX / JMP WORD PTR [BX] | IP | 目标地址在寄存器或内存中 |
| 段间直接跳转 | JMP FAR PTR label | CS 和 IP | 跳转到另一个代码段 |
| 段间间接跳转 | JMP DWORD PTR [BX] | CS 和 IP | 从内存取 32 位地址送入 CS:IP |
3.5.2 条件转移指令
条件转移指令根据标志位的状态决定是否跳转,只能进行段内短转移(偏移范围 -128 ~ +127)。
基于单个标志位的条件转移:
| 指令 | 含义 | 转移条件 | 等价指令 |
|---|---|---|---|
| JZ / JE | 为零/相等则跳转(Jump if Zero/Equal) | ZF = 1 | JZ 和 JE 等价 |
| JNZ / JNE | 不为零/不相等则跳转(Jump if Not Zero/Not Equal) | ZF = 0 | JNZ 和 JNE 等价 |
| JS | 为负则跳转(Jump if Sign) | SF = 1 | — |
| JNS | 为正则跳转(Jump if Not Sign) | SF = 0 | — |
| JO | 溢出则跳转(Jump if Overflow) | OF = 1 | — |
| JNO | 不溢出则跳转(Jump if Not Overflow) | OF = 0 | — |
| JP / JPE | 偶校验则跳转(Jump if Parity/Parity Even) | PF = 1 | JP 和 JPE 等价 |
| JNP / JPO | 奇校验则跳转(Jump if Not Parity/Parity Odd) | PF = 0 | JNP 和 JPO 等价 |
| JC | 有进位则跳转(Jump if Carry) | CF = 1 | JB、JNAE |
| JNC | 无进位则跳转(Jump if Not Carry) | CF = 0 | JNB、JAE |
无符号数比较的条件转移(CMP 后使用):
| 指令 | 含义 | 转移条件 | 等价指令 |
|---|---|---|---|
| JA | 高于则跳转(Jump if Above) | CF = 0 且 ZF = 0 | JNBE |
| JAE | 高于或等于则跳转(Jump if Above or Equal) | CF = 0 | JNB、JNC |
| JB | 低于则跳转(Jump if Below) | CF = 1 | JNAE、JC |
| JBE | 低于或等于则跳转(Jump if Below or Equal) | CF = 1 或 ZF = 1 | JNA |
有符号数比较的条件转移(CMP 后使用):
| 指令 | 含义 | 转移条件 | 等价指令 |
|---|---|---|---|
| JG | 大于则跳转(Jump if Greater) | ZF = 0 且 SF = OF | JNLE |
| JGE | 大于或等于则跳转(Jump if Greater or Equal) | SF = OF | JNL |
| JL | 小于则跳转(Jump if Less) | SF ≠ OF | JNGE |
| JLE | 小于或等于则跳转(Jump if Less or Equal) | ZF = 1 或 SF ≠ OF | JNG |
CX 为零条件转移:
| 指令 | 含义 | 转移条件 |
|---|---|---|
| JCXZ | CX 为零则跳转(Jump if CX is Zero) | CX = 0 |
3.5.3 循环指令
| 指令 | 含义 | 作用 | 格式/用法 | 说明 |
|---|---|---|---|---|
| LOOP | 循环(Loop) | CX 减 1,若 CX ≠ 0 则跳转到目标标号 | LOOP label | 隐含使用 CX 作为计数器 |
| LOOPE / LOOPZ | 相等/为零时循环 | CX 减 1,若 CX ≠ 0 且 ZF = 1 则跳转 | LOOPE label | 用于在循环中查找不匹配项 |
| LOOPNE / LOOPNZ | 不相等/不为零时循环 | CX 减 1,若 CX ≠ 0 且 ZF = 0 则跳转 | LOOPNE label | 用于在循环中查找匹配项 |
LOOP 指令的执行过程:
- CX = CX - 1
- 若 CX ≠ 0,则 IP = IP + 位移量(跳转到标号处)
- 若 CX = 0,则顺序执行下一条指令
3.6 调用与返回指令
| 指令 | 含义 | 作用 | 格式/用法 | 说明 |
|---|---|---|---|---|
| CALL | 调用子程序(Call) | 将返回地址(IP 或 CS:IP)压栈,然后跳转到子程序 | CALL label(段内调用) CALL FAR PTR label(段间调用) |
段内调用压入 IP,段间调用压入 CS 和 IP |
| RET | 返回(Return) | 从栈中弹出返回地址,回到调用点 | RET(段内返回) RET n(段内返回并丢弃 n 字节参数) |
段内返回弹出 IP,段间返回弹出 IP 和 CS |
| RETF | 远返回(Return Far) | 段间返回 | RETF | 从栈中弹出 IP 和 CS |
CALL 与 RET 的配对使用:
| 调用方式 | 调用动作 | 返回指令 | 返回动作 |
|---|---|---|---|
| 段内调用 CALL label | 压入 IP,跳转到目标地址 | RET | 弹出 IP,返回到调用点 |
| 段间调用 CALL FAR PTR label | 压入 CS 和 IP,跳转到目标地址 | RETF | 弹出 IP 和 CS,返回到调用点 |
3.7 串操作指令
串操作指令用于对内存中的一批数据进行操作。SI 指向源串(默认 DS 段),DI 指向目的串(默认 ES 段)。DF 标志决定地址变化方向:DF=0 时地址递增,DF=1 时地址递减。
| 指令 | 含义 | 作用 | 格式 | 隐含操作数 | 对标志位影响 |
|---|---|---|---|---|---|
| MOVSB | 传送字节串(Move String Byte) | 将 DS:[SI] 的字节传送到 ES:[DI],SI 和 DI 加/减 1 | MOVSB | DS:SI → ES:DI | 不影响 |
| MOVSW | 传送字串(Move String Word) | 将 DS:[SI] 的字传送到 ES:[DI],SI 和 DI 加/减 2 | MOVSW | DS:SI → ES:DI | 不影响 |
| STOSB | 存字节串(Store String Byte) | 将 AL 的内容存入 ES:[DI],DI 加/减 1 | STOSB | AL → ES:DI | 不影响 |
| STOSW | 存字串(Store String Word) | 将 AX 的内容存入 ES:[DI],DI 加/减 2 | STOSW | AX → ES:DI | 不影响 |
| LODSB | 取字节串(Load String Byte) | 将 DS:[SI] 的字节送入 AL,SI 加/减 1 | LODSB | DS:SI → AL | 不影响 |
| LODSW | 取字串(Load String Word) | 将 DS:[SI] 的字送入 AX,SI 加/减 2 | LODSW | DS:SI → AX | 不影响 |
| CMPSB | 比较字节串(Compare String Byte) | 比较 DS:[SI] 和 ES:[DI] 的字节,SI 和 DI 加/减 1 | CMPSB | DS:[SI] - ES:[DI] | 影响 CF、ZF、SF、OF、PF、AF |
| CMPSW | 比较字串(Compare String Word) | 比较 DS:[SI] 和 ES:[DI] 的字,SI 和 DI 加/减 2 | CMPSW | DS:[SI] - ES:[DI] | 影响 CF、ZF、SF、OF、PF、AF |
| SCASB | 扫描字节串(Scan String Byte) | 比较 AL 和 ES:[DI] 的字节,DI 加/减 1 | SCASB | AL - ES:[DI] | 影响 CF、ZF、SF、OF、PF、AF |
| SCASW | 扫描字串(Scan String Word) | 比较 AX 和 ES:[DI] 的字,DI 加/减 2 | SCASW | AX - ES:[DI] | 影响 CF、ZF、SF、OF、PF、AF |
REP 前缀:
| 前缀 | 含义 | 作用 | 搭配指令 | 说明 |
|---|---|---|---|---|
| REP | 重复(Repeat) | CX ≠ 0 时重复执行串操作,每次 CX 减 1 | MOVSB/MOVSW、STOSB/STOSW | 用于批量传送或填充 |
| REPE / REPZ | 相等时重复 | CX ≠ 0 且 ZF = 1 时重复执行 | CMPSB/CMPSW、SCASB/SCASW | 查找第一个不匹配/不等的元素 |
| REPNE / REPNZ | 不相等时重复 | CX ≠ 0 且 ZF = 0 时重复执行 | CMPSB/CMPSW、SCASB/SCASW | 查找第一个匹配/相等的元素 |
方向控制指令:
| 指令 | 含义 | 作用 | 说明 |
|---|---|---|---|
| CLD | 清方向标志(Clear Direction) | DF = 0,串操作地址递增 | SI/DI 每次加 1 或加 2 |
| STD | 置方向标志(Set Direction) | DF = 1,串操作地址递减 | SI/DI 每次减 1 或减 2 |
3.8 中断指令
| 指令 | 含义 | 作用 | 格式/用法 | 说明 |
|---|---|---|---|---|
| INT | 软中断(Interrupt) | 引发指定类型的中断,调用中断处理程序 | INT 10H(调用 BIOS 视频服务) INT 21H(调用 DOS 系统服务) |
压入 FLAGS、CS、IP,然后跳转到中断处理程序 |
| IRET | 中断返回(Interrupt Return) | 从中断处理程序返回 | IRET | 弹出 IP、CS、FLAGS,返回到被中断的程序 |
| INTO | 溢出中断(Interrupt on Overflow) | 若 OF = 1 则触发中断 4 | INTO | 常用于除法溢出检测 |
常用 BIOS 中断(INT 10H):
| AH 值 | 功能 | 主要参数 |
|---|---|---|
| 02H | 设置光标位置 | BH = 页号,DH = 行号,DL = 列号 |
| 06H | 屏幕窗口上滚 | AL = 滚动行数,BH = 属性,CH/CL = 窗口左上角,DH/DL = 窗口右下角 |
| 09H | 在光标处显示字符 | AL = 字符,BH = 页号,BL = 属性,CX = 重复次数 |
| 0EH | 以电传方式显示字符 | AL = 字符,BL = 前景色 |
常用 DOS 中断(INT 21H):
| AH 值 | 功能 | 主要参数 | 返回值 |
|---|---|---|---|
| 01H | 从键盘输入一个字符并回显 | 无 | AL = 输入字符的 ASCII 码 |
| 02H | 输出一个字符到屏幕 | DL = 要输出的字符 | 无 |
| 09H | 输出字符串 | DS:DX = 字符串首地址(以 $ 结尾) | 无 |
| 4CH | 程序终止返回 DOS | AL = 返回码 | 无(程序结束) |
3.9 输入/输出指令
| 指令 | 含义 | 作用 | 格式/用法 | 说明 |
|---|---|---|---|---|
| IN | 输入(Input) | 从端口读取数据到 AL/AX | IN AL, 60H(从端口 60H 读入一个字节到 AL) IN AX, DX(从 DX 指定的端口读入一个字到 AX) |
端口号 0255 可直接指定,25665535 须通过 DX |
| OUT | 输出(Output) | 将 AL/AX 的数据写入端口 | OUT 60H, AL(将 AL 的内容写入端口 60H) OUT DX, AX(将 AX 的内容写入 DX 指定的端口) |
端口号 0255 可直接指定,25665535 须通过 DX |
3.10 标志位操作指令
| 指令 | 含义 | 作用 | 说明 |
|---|---|---|---|
| CLC | 清进位标志(Clear Carry) | CF = 0 | 在进行多精度运算前清除 CF |
| STC | 置进位标志(Set Carry) | CF = 1 | — |
| CMC | 进位标志取反(Complement Carry) | CF = NOT CF | — |
| CLD | 清方向标志(Clear Direction) | DF = 0 | 串操作前通常执行,确保地址递增 |
| STD | 置方向标志(Set Direction) | DF = 1 | 使串操作地址递减 |
| CLI | 清中断标志(Clear Interrupt) | IF = 0 | 禁止可屏蔽中断 |
| STI | 置中断标志(Set Interrupt) | IF = 1 | 允许可屏蔽中断 |
| LAHF | 标志送 AH(Load AH from Flags) | AH = FLAGS 的低 8 位 | 将 SF、ZF、AF、PF、CF 送入 AH |
| SAHF | AH 送标志(Store AH into Flags) | FLAGS 的低 8 位 = AH | 将 AH 的内容送入标志寄存器低 8 位 |
| PUSHF | 标志入栈(Push Flags) | 将 FLAGS 压入栈中 | — |
| POPF | 标志出栈(Pop Flags) | 从栈中弹出 FLAGS | — |
3.11 其他常用指令
| 指令 | 含义 | 作用 | 格式/用法 | 说明 |
|---|---|---|---|---|
| NOP | 空操作(No Operation) | 不执行任何操作,占用一个时钟周期 | NOP | 用于延时或指令对齐 |
| HLT | 停机(Halt) | CPU 停止执行指令,等待中断 | HLT | 使 CPU 进入暂停状态 |
| XLAT | 查表转换(Translate) | 用 AL 中的值作为偏移量,从表中取出对应字节送入 AL | XLAT | AL = DS:[BX + AL],常用于字符转换 |
| AAA | ASCII 加法调整 | 调整 AL 中的 ASCII 加法结果 | AAA | 用于 BCD/ASCII 加法运算后的调整 |
| AAS | ASCII 减法调整 | 调整 AL 中的 ASCII 减法结果 | AAS | 用于 BCD/ASCII 减法运算后的调整 |
| DAA | 压缩 BCD 加法调整 | 调整 AL 中的压缩 BCD 加法结果 | DAA | — |
| DAS | 压缩 BCD 减法调整 | 调整 AL 中的压缩 BCD 减法结果 | DAS | — |
3.12 伪指令
伪指令不是 CPU 指令,而是在汇编时指导汇编器如何处理源代码的指令。
| 伪指令 | 含义 | 作用 | 用法示例 |
|---|---|---|---|
| DB | 定义字节(Define Byte) | 在内存中分配并初始化字节数据 | DB 1, 2, 3, 'ABC' |
| DW | 定义字(Define Word) | 在内存中分配并初始化字数据 | DW 1234H, 5678H |
| DD | 定义双字(Define Double Word) | 在内存中分配并初始化双字数据 | DD 12345678H |
| DUP | 重复(Duplicate) | 与 DB/DW/DD 配合,批量定义数据 | DB 100 DUP(0)(定义 100 个字节全为 0) |
| SEGMENT / ENDS | 段定义 | 定义一个逻辑段的开始和结束 | DATA SEGMENT ... DATA ENDS |
| ASSUME | 段假定 | 告诉汇编器段寄存器与段的对应关系 | ASSUME CS:CODE, DS:DATA |
| EQU | 等值定义 | 定义一个常量符号 | COUNT EQU 100 |
| OFFSET | 取偏移地址 | 获取变量或标号的偏移地址 | MOV BX, OFFSET BUFFER |
| SEG | 取段地址 | 获取变量或标号所在段的段地址 | MOV AX, SEG BUFFER |
| PTR | 类型指定(Pointer) | 指定操作数的类型 | MOV BYTE PTR [BX], 0FFH |
| PROC / ENDP | 过程定义 | 定义一个子过程 | SUB1 PROC ... SUB1 ENDP |
| END | 源程序结束 | 标记源程序的结束,可指定程序入口 | END START |
附录:8086 指令编码参考
操作数大小指定
| 后缀 | 含义 | 对应大小 | 使用场景 |
|---|---|---|---|
| BYTE PTR | 字节操作 | 8 位(1 字节) | MOV BYTE PTR [BX], 0FFH |
| WORD PTR | 字操作 | 16 位(2 字节) | MOV WORD PTR [BX], 1234H |
| DWORD PTR | 双字操作 | 32 位(4 字节) | MOV DWORD PTR [BX], 12345678H |
| FAR PTR | 远转移/调用 | 段间转移 | JMP FAR PTR label |
| NEAR PTR | 近转移/调用 | 段内转移 | JMP NEAR PTR label |
| SHORT | 短转移 | 段内短转移(-128~+127) | JMP SHORT label |
[招生]科锐逆向工程师培训(2026年7月3日实地,远程教学同时开班, 第56期)!