这份教程按照"为什么这样设计 → 底层逻辑是什么 → 具体怎么用"的思路编写,不仅罗列指令,更注重理解计算机工作的本质逻辑。


第一章:为什么要学汇编?——从机器视角看世界

1.1 高级语言 vs 汇编语言的本质差异

维度 C/Python 等高级语言 汇编语言
抽象层级 面向问题(if/elsefor 面向机器(寄存器、内存地址)
执行主体 程序员描述"做什么" 程序员指挥"怎么做"
硬件可见性 完全屏蔽 完全暴露
可移植性 高(换平台重新编译) 极低(绑定特定 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:1234
  • 2100:0234
  • 2123:0004
  • 2120: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

栈的应用逻辑

  1. 临时保存寄存器:子程序开头 PUSH,结尾 POP
  2. 传递参数:调用前 PUSH 参数
  3. 保存返回地址: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 的底层逻辑

  1. 自动递减 CX(这是硬编码的行为)
  2. 检查 CX 是否为 0
  3. 不为 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 答题技巧

  1. 读程序题:先标出每条指令修改了哪个寄存器/内存,逐步跟踪
  2. 写程序题:先画流程图,再写注释,最后填指令
  3. 寻址方式题:先找 [],再看里面成分,判断类型
  4. 标志位题:先算出实际结果,再逐位分析 CF/OF/ZF/SF

祝你期末顺利!如果有具体的题目或某条指令的疑惑,随时可以问我深入讲解。


相关问题

  1. 如何在汇编语言中实现函数递归调用?
  2. 8086 的中断处理流程是怎样的?
  3. 如何优化汇编程序的执行效率?
  4. 现代CPU架构与8086的主要区别是什么?
  5. 汇编语言在嵌入式开发中的实际应用案例有哪些?
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐