汇编语言期末复习完全指南
这份教程按照"为什么这样设计 → 底层逻辑是什么 → 具体怎么用"的思路编写,不仅罗列指令,更注重理解计算机工作的本质逻辑。
第一章:为什么要学汇编?——从机器视角看世界
1.1 高级语言 vs 汇编语言的本质差异
| 维度 | C/Python 等高级语言 | 汇编语言 |
|---|---|---|
| 抽象层级 | 面向问题(if/else、for) |
面向机器(寄存器、内存地址) |
| 执行主体 | 程序员描述"做什么" | 程序员指挥"怎么做" |
| 硬件可见性 | 完全屏蔽 | 完全暴露 |
| 可移植性 | 高(换平台重新编译) | 极低(绑定特定 CPU 架构) |
核心逻辑:汇编不是另一种"语法",而是直接控制硬件的工作流程。每一条指令都对应 CPU 的一个具体动作。
1.2 计算机执行程序的本质模型
所有程序最终都归结为冯·诺依曼模型的循环:
取指令 (Fetch) → 译码 (Decode) → 执行 (Execute) → 回写 (Write-back)
逻辑要点:
- CPU 不认识变量名、函数名,它只认识二进制机器码
- 汇编是机器码的"人类可读形式"(助记符)
- 程序 = 指令序列 + 数据序列,都存放在同一片内存中
- CPU 通过 CS:IP(代码段:指令指针)知道下一条指令在哪
第二章:数的表示与运算——一切的基础
2.1 进制系统:为什么计算机用二进制?
物理逻辑:电子元件最容易表示两种稳定状态(高电平/低电平、有磁/无磁、有光/无光)。多态表示(如十进制)对硬件精度要求极高,容易出错。
关键换算关系(必须熟记):
| 二进制 | 十六进制 | 十进制 |
|---|---|---|
0000 |
0 |
0 |
0001 |
1 |
1 |
| … | … | … |
1000 |
8 |
8 |
1001 |
9 |
9 |
1010 |
A |
10 |
1011 |
B |
11 |
1100 |
C |
12 |
1101 |
D |
13 |
1110 |
E |
14 |
1111 |
F |
15 |
为什么汇编里十六进制最常用?
- 二进制写太长:
1111 1111= 8 位 - 十六进制刚好 1 位对应 4 位二进制:
FF=1111 1111 - 一个字节(8 位)= 两位十六进制,非常直观
2.2 有符号数的表示:补码机制(极其重要)
问题:8 位寄存器能表示 0~255,如何表示负数?
设计逻辑:
- 最高位作为符号位:
0正,1负 - 但简单符号位(原码)有问题:
0000 0001(+1) +1000 0001(-1) =1000 0010(-2),运算结果错误
补码解决方案:
- 正数:不变
- 负数:按位取反 + 1
为什么补码能正确运算?
数学原理:在模 256 的系统中,-x 的等价表示是 256 - x。
-1的补码 =256 - 1=255=1111 1111(FF)3 + (-1)=0000 0011+1111 1111=1 0000 0010(溢出位丢弃)=0000 0010= 2 ✅
溢出判断逻辑:
- 无符号溢出:结果超出 0~255,看 CF(进位标志)
- 有符号溢出:正+正=负 或 负+负=正,看 OF(溢出标志)
第三章:8086 CPU 架构与寄存器系统
3.1 寄存器设计的核心逻辑
为什么需要寄存器?
- 内存访问速度 << CPU 运算速度
- 寄存器位于 CPU 内部,速度极快
- 寄存器是零延迟的临时工作空间
3.2 通用寄存器(重点掌握)
8086 有 14 个 16 位寄存器,分为几类:
数据寄存器(AX, BX, CX, DX)
| 寄存器 | 全称 | 默认用途 | 可拆分 |
|---|---|---|---|
| AX | Accumulator | 累加器,乘除法默认操作数,I/O 操作 | AH(高8位)+ AL(低8位) |
| BX | Base | 基址寄存器,常用于内存寻址 | BH + BL |
| CX | Count | 计数器,LOOP 指令默认使用 |
CH + CL |
| DX | Data | 数据寄存器,乘除法辅助,I/O 端口地址 | DH + DL |
设计逻辑:为什么 AX 是默认累加器?
- 早期 CPU 指令编码长度有限,隐含使用 AX 可以缩短指令长度,提高执行效率
- 乘除法结果通常很长,AX(及 DX)成对使用存放结果
指针与变址寄存器
| 寄存器 | 名称 | 核心用途 |
|---|---|---|
| SP | Stack Pointer | 栈顶指针,始终指向栈顶 |
| BP | Base Pointer | 栈基址指针,访问栈中局部变量 |
| SI | Source Index | 源变址,串操作源地址 |
| DI | Destination Index | 目的变址,串操作目的地址 |
SP vs BP 的逻辑区别:
SP是"动态"的,随PUSH/POP/CALL/RET自动变化,指向当前栈顶BP是"静态"的,在子程序中通常固定指向栈帧底部,通过BP+偏移访问参数和局部变量
段寄存器(理解内存分段的关键)
| 寄存器 | 名称 | 配合的偏移寄存器 | 用途 |
|---|---|---|---|
| CS | Code Segment | IP | 代码段,存放指令 |
| DS | Data Segment | SI, DI, BX, 直接地址 | 数据段,存放变量 |
| SS | Stack Segment | SP, BP | 栈段,存放临时数据 |
| ES | Extra Segment | DI(串操作时) | 附加段,辅助数据操作 |
3.3 物理地址的形成逻辑(必考)
问题:8086 是 16 位 CPU(寄存器 16 位),如何访问 20 位地址空间(1MB = 2^20)?
解决方案:分段机制
物理地址 = 段地址 × 16 + 偏移地址
= 段地址左移 4 位 + 偏移地址
举例:
- 段地址
2000H,偏移1234H - 物理地址 =
20000H+1234H=21234H
为什么这样设计?
- 16 位寄存器只能存 0~FFFFH(64KB)
- 通过"段:偏移"组合,实现 1MB 寻址
- 程序可以重定位:只需改变段寄存器,同一段代码可在不同物理位置运行
注意:一个物理地址可由多组"段:偏移"表示。例如 21234H:
2000:12342100:02342123:00042120:0034
第四章:寻址方式——如何找到操作数
4.1 为什么需要多种寻址方式?
核心逻辑:操作数可能存放在不同位置,CPU 需要知道去哪里取数。不同寻址方式对应不同的编码长度和执行速度。
4.2 七种寻址方式详解
1. 立即寻址(Immediate)
MOV AX, 3064H
- 逻辑:操作数直接包含在指令中
- 特点:速度快(无需内存访问),但只能用于源操作数
- 限制:立即数不能作为目的操作数(不能向常数赋值)
2. 寄存器寻址(Register)
MOV AX, BX
- 逻辑:操作数在寄存器中
- 特点:速度最快(零等待周期)
- 注意:源和目的位数必须一致(不能
MOV AX, BL)
3. 直接寻址(Direct)
MOV AX, [2000H]
- 逻辑:指令中给出内存的偏移地址,默认段为 DS
- 实际物理地址 =
DS × 16 + 2000H - 方括号
[]的逻辑:表示取内存中的值,而非立即数
4. 寄存器间接寻址(Register Indirect)
MOV AX, [BX] ; 或 [SI], [DI]
- 逻辑:寄存器中存放的是地址,而非数据
- 适用寄存器:BX, SI, DI, BP
- 默认段规则:
- 使用 BX/SI/DI → 默认 DS 段
- 使用 BP → 默认 SS 段(因为 BP 通常操作栈)
5. 寄存器相对寻址(Register Relative)
MOV AX, [BX+10H] ; 也可写成 [10H+BX] 或 10H[BX]
- 逻辑:有效地址 = 寄存器值 + 位移量
- 用途:访问结构体成员、数组元素、局部变量
- 示例逻辑:结构体基地址在 BX,偏移 10H 处是某个字段
6. 基址变址寻址(Based Indexed)
MOV AX, [BX+SI] ; 或 [BX+DI]
- 逻辑:有效地址 = 基址寄存器 + 变址寄存器
- 典型用途:二维数组访问
- BX = 行基址,SI = 列偏移
7. 相对基址变址寻址(Relative Based Indexed)
MOV AX, [BX+SI+10H]
- 逻辑:有效地址 = 基址 + 变址 + 位移
- 用途:复杂数据结构,如结构体数组中的某个字段
4.3 寻址方式速查表
| 寻址方式 | 示例 | 有效地址 EA | 指令长度 | 速度 |
|---|---|---|---|---|
| 立即 | MOV AX, 5 |
无 | 短 | 最快 |
| 寄存器 | MOV AX, BX |
无 | 短 | 最快 |
| 直接 | MOV AX, [100H] |
100H | 中 | 慢(访存) |
| 寄存器间接 | MOV AX, [BX] |
(BX) | 短 | 慢 |
| 寄存器相对 | MOV AX, [BX+5] |
(BX)+5 | 中 | 慢 |
| 基址变址 | MOV AX, [BX+SI] |
(BX)+(SI) | 短 | 慢 |
| 相对基址变址 | MOV AX, [BX+SI+5] |
(BX)+(SI)+5 | 中 | 最慢 |
第五章:数据传送指令——程序的血液流动
5.1 MOV 指令的深层逻辑
MOV 目的操作数, 源操作数
逻辑:将源操作数的值复制到目的操作数,源不变。
限制规则(考试常考):
| 规则 | 原因 |
|---|---|
| 不能同时为段寄存器 | 硬件设计限制(如不能 MOV DS, ES) |
| 不能同时为内存操作数 | 指令编码无法表示两个地址 |
| 立即数不能直接送段寄存器 | 需通过通用寄存器中转 |
| 目的不能是立即数 | 逻辑上不能向常数赋值 |
| 源目长度必须一致 | 硬件按字节/字传输 |
正确示例:
MOV AX, DATA_SEG ; 立即数→通用寄存器
MOV DS, AX ; 通用寄存器→段寄存器(中转)
MOV AL, [BX] ; 内存→寄存器
MOV [DI], DX ; 寄存器→内存
5.2 堆栈操作指令(PUSH / POP)
堆栈的逻辑模型:
- 一段特殊的内存区域,后进先出(LIFO)
- SS:SP 始终指向栈顶
- 栈向低地址方向生长(压栈时 SP 减小)
PUSH AX ; SP = SP - 2, 然后 [SP] = AX
POP BX ; BX = [SP], 然后 SP = SP + 2
为什么是 SP-2 而不是 SP-1?
- 8086 按**字(16 位)**操作栈
- 压入一个字 = 2 个字节,所以 SP 减 2
栈的应用逻辑:
- 临时保存寄存器:子程序开头 PUSH,结尾 POP
- 传递参数:调用前 PUSH 参数
- 保存返回地址:CALL 指令自动 PUSH IP
5.3 交换指令 XCHG
XCHG AX, BX ; AX 和 BX 的值互换
XCHG AL, [SI] ; AL 和内存单元互换
逻辑:两操作数互换,不能使用立即数。
5.4 地址传送指令(LEA / LDS / LES)
LEA(Load Effective Address):
LEA BX, [SI+10H] ; BX = SI + 10H(不是取内容!)
与 MOV 的本质区别:
LEA BX, [SI+10H]→ BX 得到的是地址值MOV BX, [SI+10H]→ BX 得到的是该地址的内容
LDS / LES:
LDS SI, [BX] ; SI = [BX], DS = [BX+2]
- 逻辑:从内存取一个 32 位地址(16 位偏移 + 16 位段地址)
- 常用于加载远指针(段:偏移)
5.5 标志传送指令
LAHF ; AH = FLAGS 的低 8 位(SF,ZF,AF,PF,CF)
SAHF ; FLAGS 低 8 位 = AH
PUSHF ; 标志寄存器压栈
POPF ; 标志寄存器弹栈
用途:保存/恢复程序状态,或手动修改标志位。
第六章:算术运算指令——CPU 如何计算
6.1 加法指令
ADD(普通加法)
ADD AX, BX ; AX = AX + BX,影响所有标志位
标志位变化逻辑:
- CF:无符号溢出(最高位进位)
- OF:有符号溢出
- ZF:结果为零
- SF:结果为负(最高位为 1)
- AF:辅助进位(低 4 位向高 4 位进位,用于 BCD 运算)
- PF:低 8 位中 1 的个数为偶数
ADC(带进位加法)
ADC AX, BX ; AX = AX + BX + CF
设计逻辑:用于多精度运算。例如 32 位数相加:
; 假设 DX:AX = 12345678H, BX:CX = 9ABCDEF0H
ADD AX, CX ; 低 16 位相加,产生进位到 CF
ADC DX, BX ; 高 16 位相加,加上低位的进位
INC(自增)
INC AX ; AX = AX + 1
- 不影响 CF,影响其他标志位
- 为什么不影响 CF? 便于在循环中保持 CF 状态
6.2 减法指令
SUB(普通减法)
SUB AX, BX ; AX = AX - BX
SBB(带借位减法)
SBB AX, BX ; AX = AX - BX - CF
- 逻辑:多精度减法,处理低位借位
DEC(自减)
DEC CX ; CX = CX - 1,不影响 CF
CMP(比较指令)——极其重要
CMP AX, BX ; 做 AX - BX,但不保存结果,只影响标志位
逻辑本质:执行减法但不回写,专门用于设置标志位供后续条件跳转使用。
标志位解读:
| 关系 | 无符号解释 | 有符号解释 |
|---|---|---|
| AX = BX | ZF = 1 | ZF = 1 |
| AX > BX | CF = 0 且 ZF = 0 | ZF = 0 且 SF = OF |
| AX < BX | CF = 1 | SF ≠ OF |
6.3 乘法指令
MUL(无符号乘法)
MUL BL ; AL × BL → AX
MUL BX ; AX × BX → DX:AX(高 16 位在 DX,低 16 位在 AX)
IMUL(有符号乘法)
IMUL BX ; 同上,但操作数视为有符号数
为什么结果放 DX:AX?
- 16 位 × 16 位 最大需要 32 位存放结果
- DX 存高 16 位,AX 存低 16 位
标志位:CF=OF=1 表示结果高半部分非零(对于 MUL)或符号扩展(对于 IMUL)。
6.4 除法指令
DIV(无符号除法)
DIV BL ; AX ÷ BL,商 → AL,余数 → AH
DIV BX ; DX:AX ÷ BX,商 → AX,余数 → DX
IDIV(有符号除法)
- 余数符号与被除数相同
除法溢出问题:
- 如果商超出目标寄存器容量,产生除法溢出中断
- 例如
DIV BL时,若 AX > 255×BL,则溢出
6.5 BCD 调整指令(了解即可)
AAA ; 加法后 ASCII 调整
DAA ; 加法后十进制调整
AAS ; 减法后 ASCII 调整
DAS ; 减法后十进制调整
AAM ; 乘法后 ASCII 调整
AAD ; 除法前 ASCII 调整
存在逻辑:早期计算机需直接处理十进制数,现代编程极少使用。
第七章:逻辑运算与移位指令——位操作的艺术
7.1 逻辑运算指令
AND(按位与)
AND AL, 0FH ; 屏蔽高 4 位,保留低 4 位
逻辑应用:清零特定位(与 0),保留特定位(与 1)。
OR(按位或)
OR AL, 80H ; 将最高位置 1
逻辑应用:置位特定位(或 1),保留特定位(或 0)。
XOR(异或)
XOR AX, AX ; 清零 AX(比 MOV AX, 0 更快,更短)
XOR AL, 0FFH ; 按位取反
经典应用:寄存器清零(自身异或)、简单加密、交换两数:
XOR AX, BX
XOR BX, AX
XOR AX, BX ; 无需临时变量交换两数
NOT(取反)
NOT AX ; 所有位翻转
- 不影响任何标志位(唯一不修改标志的逻辑指令)
TEST(测试)
TEST AL, 80H ; 测试最高位是否为 1(AL & 80H,不保存结果)
逻辑:与 CMP 类似,专门用于位测试,不破坏操作数。
7.2 移位指令
逻辑移位(SHL / SHR)
SHL AX, 1 ; 左移 1 位,最低位补 0,最高位 → CF
SHR AX, CL ; 右移 CL 位
SHL 的数学意义:左移 1 位 = 乘以 2
SHR 的数学意义:右移 1 位 = 无符号除以 2
算术移位(SAL / SAR)
SAL AX, 1 ; 与 SHL 完全相同
SAR AX, 1 ; 右移,最高位保持不变(符号位)
为什么需要 SAR?
- 有符号数右移时,符号位必须保持,否则负数变正数
- 例如
-8=11111000,SAR 后 =11111100(-4) ✅ - 若用 SHR:
01111100(124) ❌
循环移位(ROL / ROR / RCL / RCR)
ROL AX, 1 ; 左循环,最高位 → 最低位 和 CF
ROR AX, 1 ; 右循环,最低位 → 最高位 和 CF
RCL AX, 1 ; 带进位左循环,CF → 最低位,最高位 → CF
RCR AX, 1 ; 带进位右循环
应用场景:
- ROL/ROR:位串循环处理,生成校验码
- RCL/RCR:多字移位(配合 CF 传递进位)
第八章:控制转移指令——改变执行流程
8.1 无条件转移 JMP
JMP SHORT LABEL ; 短转移,-128 ~ +127 字节
JMP NEAR PTR LABEL ; 近转移,同一段内
JMP FAR PTR LABEL ; 远转移,跨段
JMP AX ; 寄存器间接转移
JMP [BX] ; 内存间接转移
底层逻辑:
- 短转移:指令中包含 8 位位移,IP = IP + 位移(相对跳转)
- 近转移:指令中包含 16 位偏移,IP = 新偏移
- 远转移:指令中包含 16 位偏移 + 16 位段地址,CS 和 IP 都改变
为什么有短/近/远之分?
- 指令长度不同,短转移只需 2 字节,节省内存和取指时间
- 汇编器自动选择最优编码,程序员可用
SHORT/NEAR PTR/FAR PTR强制指定
8.2 条件转移指令(基于标志位)
所有条件转移都是短转移(-128~+127),这是 8086 设计限制。
| 指令 | 条件 | 含义 | 检查的标志 |
|---|---|---|---|
| JZ/JE | 结果为 0 / 相等 | Jump if Zero/Equal | ZF=1 |
| JNZ/JNE | 结果非 0 / 不等 | Jump if Not Zero/Not Equal | ZF=0 |
| JC | 有进位/借位 | Jump if Carry | CF=1 |
| JNC | 无进位 | Jump if No Carry | CF=0 |
| JO | 溢出 | Jump if Overflow | OF=1 |
| JNO | 无溢出 | Jump if Not Overflow | OF=0 |
| JS | 结果为负 | Jump if Sign | SF=1 |
| JNS | 结果为正 | Jump if Not Sign | SF=0 |
| JP/JPE | 低 8 位有偶数个 1 | Jump if Parity Even | PF=1 |
| JNP/JPO | 低 8 位有奇数个 1 | Jump if Parity Odd | PF=0 |
无符号数比较(使用 CF 和 ZF):
| 指令 | 含义 |
|---|---|
| JA/JNBE | 高于/不低于等于(Above/Not Below or Equal) |
| JAE/JNB | 高于等于/不低于 |
| JB/JNAE | 低于/不高于等于(Below/Not Above or Equal) |
| JBE/JNA | 低于等于/不高于 |
有符号数比较(使用 SF, OF 和 ZF):
| 指令 | 含义 |
|---|---|
| JG/JNLE | 大于/不小于等于(Greater/Not Less or Equal) |
| JGE/JNL | 大于等于/不小于 |
| JL/JNGE | 小于/不大于等于(Less/Not Greater or Equal) |
| JLE/JNG | 小于等于/不大于 |
记忆逻辑:
- A/B = Above/Below → 无符号(把数当纯二进制)
- G/L = Greater/Less → 有符号(考虑正负)
8.3 循环控制指令
LOOP LABEL ; CX = CX - 1,若 CX ≠ 0 则跳转
LOOPE/LOOPZ ; CX = CX - 1,若 CX ≠ 0 且 ZF=1 则跳转
LOOPNE/LOOPNZ ; CX = CX - 1,若 CX ≠ 0 且 ZF=0 则跳转
JCXZ LABEL ; 若 CX = 0 则跳转
LOOP 的底层逻辑:
- 自动递减 CX(这是硬编码的行为)
- 检查 CX 是否为 0
- 不为 0 则跳转回循环体
典型结构:
MOV CX, 10 ; 循环 10 次
LOOP_START:
; 循环体
LOOP LOOP_START ; CX--,若不为 0 继续
8.4 子程序调用与返回
CALL 指令
CALL NEAR PTR SUB1 ; 近调用:PUSH IP, IP = 偏移地址
CALL FAR PTR SUB2 ; 远调用:PUSH CS, PUSH IP, CS:IP = 新地址
CALL AX ; 寄存器间接调用
CALL [BX] ; 内存间接调用
逻辑:调用子程序前必须保存返回地址,以便子程序结束后能正确返回。
RET 指令
RET ; 近返回:POP IP
RET 4 ; 返回并 SP = SP + 4(清理栈上参数)
RET ; 远返回:POP IP, POP CS
RET n 的逻辑:用于调用者清理参数的约定(C 语言 pascal 调用约定)。
8.5 中断指令
INT 21H ; 调用 21H 号中断(DOS 功能调用)
INTO ; 若 OF=1 则触发 4 号中断(溢出处理)
IRET ; 中断返回:POP IP, POP CS, POP FLAGS
中断与 CALL 的区别:
- CALL 是程序主动调用已知子程序
- INT 是调用中断向量表中的服务程序(可硬件触发)
- INT 额外保存 FLAGS(因为中断可能随时发生,需完整保存状态)
第九章:串操作指令——批量数据处理
9.1 串操作的设计逻辑
问题:如何高效处理连续内存数据(字符串、数组)?
方案:用 SI/DI 自动指向数据,用 CX 计数,用 FLAGS 控制方向,一条指令完成一次操作并自动指针移动。
9.2 方向标志 DF 与 STD/CLD
CLD ; DF = 0,地址递增(SI/DI 增加)
STD ; DF = 1,地址递减(SI/DI 减少)
默认状态:通常使用 CLD(地址递增),处理字符串从左到右。
9.3 串操作指令族
| 指令 | 操作 | 源 | 目的 | 重复前缀 |
|---|---|---|---|---|
| MOVS | 传送字节/字 | DS:SI | ES:DI | REP |
| LODS | 取到 AL/AX | DS:SI | AL/AX | 通常不用 |
| STOS | 存入 AL/AX | AL/AX | ES:DI | REP |
| CMPS | 比较 | DS:SI | ES:DI | REPE/REPNE |
| SCAS | 扫描 | AL/AX | ES:DI | REPE/REPNE |
9.4 重复前缀
REP ; 重复直到 CX = 0
REPE/REPZ ; 重复直到 CX = 0 或 ZF = 0
REPNE/REPNZ ; 重复直到 CX = 0 或 ZF = 1
典型应用示例:
1. 内存拷贝(类似 memcpy):
CLD
MOV CX, 100 ; 拷贝 100 个字
REP MOVSW ; 重复执行 MOVSW 直到 CX = 0
执行逻辑:每次 MOVSW 做 [DI] = [SI],然后 SI+=2, DI+=2, CX–,直到 CX=0。
2. 字符串比较:
CLD
MOV CX, 20
REPE CMPSB ; 比较直到不等或 CX=0
JNZ NOT_EQUAL ; ZF=0 表示发现不同字符
3. 查找字符:
CLD
MOV CX, 100
MOV AL, 'A'
REPNE SCASB ; 在 ES:DI 指向的 100 字节中找 'A'
JZ FOUND ; ZF=1 表示找到了
第十章:伪指令与程序结构
10.1 段定义伪指令
DATA SEGMENT ; 定义数据段开始
BUF DB 100 DUP(0) ; 定义 100 字节缓冲区
DATA ENDS ; 数据段结束
CODE SEGMENT
ASSUME CS:CODE, DS:DATA ; 告诉汇编器段寄存器对应关系
START:
MOV AX, DATA
MOV DS, AX ; 初始化 DS 段寄存器
; 程序主体
MOV AH, 4CH
INT 21H ; 返回 DOS
CODE ENDS
END START ; 程序结束,入口点为 START
ASSUME 的逻辑:只是声明性的,告诉汇编器在编译时如何解释符号地址,不修改实际段寄存器值。所以程序开始时必须手动给 DS/ES 赋值。
10.2 数据定义伪指令
| 伪指令 | 含义 | 示例 | 分配空间 |
|---|---|---|---|
| DB | Define Byte | A DB 5 |
1 字节 |
| DW | Define Word | B DW 1234H |
2 字节 |
| DD | Define Doubleword | C DD 12345678H |
4 字节 |
| DQ | Define Quadword | 8 字节 | |
| DT | Define Ten Bytes | 10 字节 |
字符串定义:
STR DB 'HELLO' ; 分配 5 字节,依次存放 48H, 45H, 4CH, 4CH, 4FH
STR2 DB 'A', 'B', 0 ; 混合定义
DUP 重复操作符:
BUF DB 100 DUP(0) ; 100 个 0
TAB DW 10 DUP(?) ; 10 个未初始化字
ARR DB 3 DUP(1, 2) ; 1,2,1,2,1,2(重复整个序列)
10.3 符号定义
EQU ; 等价,不可重定义
PORT EQU 80H ; 编译时所有 PORT 替换为 80H
= ; 等号,可重定义
COUNT = 5
COUNT = 10 ; 合法
与变量的区别:EQU 不分配内存,只是文本替换。
10.4 过程定义
SUB1 PROC NEAR ; 近过程(同段调用)
PUSH AX ; 保护寄存器
; 子程序体
POP AX ; 恢复寄存器
RET
SUB1 ENDP
SUB2 PROC FAR ; 远过程(可跨段调用)
; 自动保存/恢复 CS:IP
RET
SUB2 ENDP
第十一章:DOS 功能调用——与操作系统交互
11.1 调用机制
MOV AH, 功能号 ; 选择具体功能
设置入口参数 ; 根据功能要求
INT 21H ; 触发中断
获取出口参数 ; 结果通常在 AX 或指定寄存器
11.2 常用功能号速查
| AH | 功能 | 入口参数 | 出口参数 |
|---|---|---|---|
| 01H | 键盘输入并回显 | 无 | AL = 字符 |
| 02H | 显示字符 | DL = 字符 | 无 |
| 07H | 键盘输入无回显 | 无 | AL = 字符 |
| 08H | 键盘输入无回显(检测 Ctrl+C) | 无 | AL = 字符 |
| 09H | 显示字符串 | DS:DX = 串地址($结尾) | 无 |
| 0AH | 输入字符串 | DS:DX = 缓冲区首址 | 缓冲区填充 |
| 4CH | 返回 DOS | AL = 返回码 | 无 |
显示字符串示例:
DATA SEGMENT
MSG DB 'Hello World$' ; 必须以 $ 结尾
DATA ENDS
CODE SEGMENT
MOV AX, DATA
MOV DS, AX
MOV DX, OFFSET MSG ; 或 LEA DX, MSG
MOV AH, 09H
INT 21H
CODE ENDS
输入字符串缓冲区格式:
[0] = 最大允许长度(需预先设置)
[1] = 实际输入长度(由 DOS 填写)
[2..n] = 输入的字符内容
第十二章:程序设计逻辑与典型算法
12.1 分支程序设计逻辑
单分支:
CMP AX, 0
JGE NON_NEG ; 如果 AX >= 0 跳过
NEG AX ; 否则取反(求绝对值)
NON_NEG:
双分支:
CMP AX, BX
JGE AX_BIGGER ; AX >= BX 走这边
MOV MAX, BX ; 否则 BX 更大
JMP DONE
AX_BIGGER:
MOV MAX, AX
DONE:
多分支(跳转表法):
MOV BX, 0
MOV BL, CHOICE ; 选择 0~3
SHL BX, 1 ; ×2,因为地址是字
JMP TABLE[BX] ; 跳转到对应处理程序
TABLE DW CASE0, CASE1, CASE2, CASE3
12.2 循环程序设计
计数循环:
MOV CX, N
LOOP1:
; 循环体
LOOP LOOP1
条件循环:
WHILE_START:
CMP AX, 0
JLE WHILE_END ; while (AX > 0)
; 循环体
JMP WHILE_START
WHILE_END:
12.3 子程序设计规范
标准模板:
PROC_NAME PROC
PUSH AX ; 保护所有要修改的寄存器(除了传结果的)
PUSH BX
PUSH CX
PUSH DX
; 子程序功能代码
POP DX ; 按相反顺序恢复
POP CX
POP BX
POP AX
RET
PROC_NAME ENDP
参数传递方式:
| 方式 | 实现 | 优点 | 缺点 |
|---|---|---|---|
| 寄存器 | 参数放 AX/BX/CX | 快速 | 参数数量受限 |
| 内存变量 | 全局变量 | 简单 | 不可重入,耦合高 |
| 栈 | PUSH 参数,CALL,[BP+4] 访问 | 通用,支持递归 | 稍复杂 |
栈参数访问示例:
; 调用前:PUSH ARG2 / PUSH ARG1 / CALL SUM
; 栈状态:[BP+0]=旧BP, [BP+2]=返回IP, [BP+4]=ARG1, [BP+6]=ARG2
SUM PROC
PUSH BP
MOV BP, SP ; BP 指向栈帧基址
MOV AX, [BP+4] ; 取 ARG1
ADD AX, [BP+6] ; 加 ARG2
POP BP
RET 4 ; 返回并清理 4 字节参数
SUM ENDP
第十三章:标志位与条件判断深度理解
13.1 FLAGS 寄存器结构
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
| 位 | 名称 | 含义 |
|---|---|---|
| 0 | CF | 进位/借位标志 |
| 2 | PF | 奇偶标志 |
| 4 | AF | 辅助进位 |
| 6 | ZF | 零标志 |
| 7 | SF | 符号标志 |
| 8 | TF | 陷阱标志(单步调试) |
| 9 | IF | 中断允许 |
| 10 | DF | 方向标志 |
| 11 | OF | 溢出标志 |
13.2 标志位变化规律总结
| 指令类型 | 影响的标志 | 说明 |
|---|---|---|
| 数据传送(MOV/PUSH/POP/LEA) | 无 | 纯搬运 |
| 加法/减法(ADD/SUB/CMP) | 全部 | 按结果设置 |
| 增量/减量(INC/DEC) | 除 CF 外 | 保持 CF 用于多精度 |
| 逻辑运算(AND/OR/XOR/TEST) | CF=0, OF=0 | 逻辑操作无进位溢出 |
| 移位(SHL/SHR/SAR) | CF=移出位 | 按最后移出位设置 |
| 循环移位(ROL/ROR) | CF=移出位 | OF 视情况而定 |
| 乘法(MUL/IMUL) | CF, OF | 表示结果是否需要高半部分 |
| 除法(DIV/IDIV) | 不确定 | 通常视为未定义 |
| 标志操作(CLC/STC/CMC) | CF | 专门操作 CF |
第十四章:高频考点与易错点总结
14.1 经典易错题
1. MOV 指令合法性判断
MOV [BX], [SI] ; ❌ 错误:不能同时为内存操作数
MOV DS, 1000H ; ❌ 错误:立即数不能直接送段寄存器
MOV AX, BL ; ❌ 错误:长度不匹配
XCHG AX, [BX+SI] ; ✅ 正确:寄存器和内存可以交换
2. 寻址方式识别
MOV AX, [100H] ; 直接寻址
MOV AX, [BX+10H] ; 寄存器相对寻址
MOV AX, [BX+SI+5] ; 相对基址变址寻址
3. 段超越前缀
MOV AX, CS:[BX] ; 强制使用 CS 段而非默认 DS
MOV AX, SS:[SI] ; 强制使用 SS 段
4. 栈操作细节
PUSH AL ; ❌ 错误:栈操作必须是字(16位)
PUSH AX ; ✅ 正确:SP = SP - 2
14.2 编程题常见模式
1. 统计字符串中某字符出现次数:
MOV CX, STR_LEN
MOV SI, OFFSET STRING
MOV BL, 0 ; 计数器
MOV AL, 'A' ; 目标字符
COUNT_LOOP:
CMP AL, [SI]
JNE NEXT
INC BL
NEXT:
INC SI
LOOP COUNT_LOOP
2. 数组求和:
MOV CX, 100
MOV SI, 0
MOV AX, 0 ; 累加和
SUM_LOOP:
ADD AX, ARRAY[SI]
ADD SI, 2 ; 字数组,每次 +2
LOOP SUM_LOOP
3. 冒泡排序核心:
MOV CX, N
DEC CX ; 外层循环 N-1 次
OUTER:
MOV DX, CX
MOV SI, 0
INNER:
MOV AX, ARRAY[SI]
CMP AX, ARRAY[SI+2]
JLE NO_SWAP ; 升序,若 <= 则不交换
XCHG AX, ARRAY[SI+2]
MOV ARRAY[SI], AX
NO_SWAP:
ADD SI, 2
DEC DX
JNZ INNER
LOOP OUTER
第十五章:复习策略与考试技巧
15.1 知识分层
必须熟记(肌肉记忆):
- 14 个寄存器名称、功能、可拆分性
- 7 种寻址方式的名称和格式
- 所有条件转移指令的助记符和判断条件
- 常用 DOS 功能号(01H, 02H, 09H, 0AH, 4CH)
必须理解(能推导):
- 物理地址计算(段×16+偏移)
- 补码运算和溢出判断
- 标志位变化规律
- 栈的生长方向和操作细节
必须掌握(能编写):
- 完整程序框架(段定义、初始化、返回 DOS)
- 分支、循环、子程序结构
- 串操作 + REP 前缀的使用
- 简单算法实现(求和、求最大最小、查找、排序)
15.2 答题技巧
- 读程序题:先标出每条指令修改了哪个寄存器/内存,逐步跟踪
- 写程序题:先画流程图,再写注释,最后填指令
- 寻址方式题:先找
[],再看里面成分,判断类型 - 标志位题:先算出实际结果,再逐位分析 CF/OF/ZF/SF
祝你期末顺利!如果有具体的题目或某条指令的疑惑,随时可以问我深入讲解。
相关问题:
- 如何在汇编语言中实现函数递归调用?
- 8086 的中断处理流程是怎样的?
- 如何优化汇编程序的执行效率?
- 现代CPU架构与8086的主要区别是什么?
- 汇编语言在嵌入式开发中的实际应用案例有哪些?
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)