# 第 4 章 汇编语言及其程序设计(6-8 节)总结 > 本章 6-8 节内容重点:**伪指令**(4.6)、**汇编语言源程序的结构**(4.7)、**汇编语言程序设计**(4.8,含顺序/分支/循环/子程序)。与第 4 章前 5 节(指令系统)衔接,但侧重点转向**汇编源程序组织与结构化程序设计**。 --- ## 一、详细内容分析 ### 4.6 伪指令 伪指令是给汇编程序(编译器)的指示,CPU 不执行,**不产生目标代码**(宏指令除外)。用于选择处理器、定义数据、分配存储区等。 #### 1. END — 源程序结束伪指令 - **格式**:`END [标号]` - **功能**:表示源程序结束,不可缺,是源程序编译的最后一条语句。 - **说明**: 1) 标号指示程序开始执行的起始地址; 2) 起始执行地址缺省值为代码段第一条指令地址; 3) 多个模块链接时,主程序用标号,其他程序不用。 #### 2. SEGMENT 和 ENDS — 段定义伪指令 - **格式**: ``` 段名 SEGMENT [定位类型][组合类型][字长类型]['类别'] … 段名 ENDS ``` - **功能**:定义段名、段属性,并表示段的开始位置、结束位置。 - **说明**: 1) `SEGMENT` 和 `ENDS` 必须成对出现; 2) 段名是段的标识符(基址),由程序员指定; 3) 四个参数选项可省略,使用默认值: - **定位类型**:`BYTE`、`WORD`、`DWORD`、`PARA`(默认)、`PAGE`; - **组合类型**:`PUBLIC`、`STACK`、`COMMON`、`MEMORY`、`PRIVATE`(默认)、`AT 表达式`; - **字长类型**:`USE16`(默认)、`USE32`; - **类别**:`'DATA'`、`'CODE'`、`'STACK'`、`'EXTRA'` 等,连接时同类别段放在连续存储空间。 #### 3. ASSUME — 段分配伪指令 - **格式**:`ASSUME 段寄存器名:段名, 段寄存器名:段名, ……` - **功能**:说明某个段关联哪一个段寄存器。 - **说明**: 1) 程序段必须用 CS,堆栈段必须用 SS; 2) 该语句一般放在代码段最前面; 3) **说明性语句**,除 CS 由初始化赋值外,其他段寄存器在程序中赋值。 #### 4. ORG — 起始地址定义 - **格式**:`ORG 数值表达式` - **功能**:定义指令或数据的起始地址,`$ ← 表达式的值`。数值表达式范围 0~65535。 - **地址计数器 `$`**:指示当前语句使用的段内偏移值,每段开始时为 0,每分配一个单元加 1。 #### 5. EVEN — 偶数地址定义 - **格式**:`EVEN` - **功能**:使下一个变量或指令从偶地址开始(用于字访问对齐)。 #### 6. 数据定义伪指令 - **格式**:`[变量名] 操作符, 操作数[, …] [: 注释]` - **功能**:为操作数分配存储单元,用变量与存储单元相联系。 - **操作符**(定义数据类型): - `DB`:1 字节(8 位),字节变量; - `DW`:1 字(16 位),字变量; - `DD`:1 双字(32 位),双字变量。 - **操作数**:常数、表达式、字符串、`?`、`n DUP(操作数)`(重复 n 次)。 - **典型示例**: ``` ORG 200H DATA1 DB 12H, ?, 2+6 ; 12H、保留、08H EVEN DATA2 DW 789AH, 5 ; 字变量 DATA3 DD 12345678H ; 双字 DATA4 DW $, 6699H ; $ 为当前地址 DATA5 DB 4 DUP(?) ; 预留 4 个字节 ``` #### 7. PROC 和 ENDP — 过程定义伪指令 - **格式**: ``` 过程名 PROC [属性] … 过程名 ENDP ``` - **功能**:定义子程序结构,过程名是入口,是 `CALL` 的操作数。 - **属性**:`FAR`、`NEAR`(默认)。 #### 8. EQU — 赋值伪指令 - **格式**:`符号常数名 EQU 表达式` - **功能**:将表达式的值赋给符号常数;**只能定义一次**。 - 例:`DATA1 EQU 88`、`AAA1 EQU CX`、`DATA2 EQU DATA1+12`。 #### 9. 返回值运算符 | 运算符 | 含义 | |--------|------| | `SEG 变量/标号` | 段地址值 | | `OFFSET 变量/标号` | 偏移量值 | | `TYPE 变量` | 数据类型字节数:`DB=1`、`DW=2`、`DD=4` | | `TYPE 标号` | 标号属性:`NEAR=-1`、`FAR=-2` | | `LENGTH 变量` | 第一个数占用的单元数 | | `SIZE 变量` | 第一个数占用的字节数 | #### 10. PTR — 临时改变类型属性运算符 - **格式**:`类型 PTR 变量/标号` - **变量类型**:BYTE / WORD / DWORD / FWORD / QWORD / TBYTE; - **标号类型**:NEAR(近)/ FAR(远)。 - 当指令中操作数类型不明确(如 `MOV BYTE PTR [BX], 8` 或 `MOV WORD PTR [BX], 8`)时**必须使用 PTR**。 --- ### 4.7 汇编语言源程序的结构 #### 80x86 汇编语言源程序结构特点 1. 一个程序由若干**逻辑段**组成,各逻辑段由伪指令语句定义和说明; 2. 整个源程序以 `END` 伪指令结束; 3. 每个逻辑段由语句序列组成,语句可以是: - **指令语句**:CPU 指令,可执行,汇编时翻译成目标码; - **伪指令语句**:CPU 不执行,提供汇编信息,不产生目标代码; - **宏指令语句**:一个指令序列,汇编时产生对应的目标代码序列; - **注释语句**:以分号 `;` 开始,说明性语句,汇编程序不处理; - **空行语句**:仅包含回车换行符,便于阅读。 4. 每个源程序应当有**返回操作系统的指令语句**(典型:`MOV AH,4CH` + `INT 21H`),使程序执行完后能自动返回系统。 #### DOS 环境下两种程序结构 | 类型 | 长度限制 | 加载特点 | 段起始 | 适用场景 | |------|----------|----------|--------|----------| | **.COM 文件** | 一个段长(64KB) | 无段重定位,结构紧凑、装入快 | 偏移量从 100H 开始,且 100H 处必须是可执行指令 | 小型程序 | | **.EXE 文件** | 仅受内存空间限制 | 需段重定位,占用盘空间大、装入慢 | 由程序员指定 | 中、大型程序 | --- ### 4.8 汇编语言程序设计 #### 基本步骤 1. 描述问题 → 2. 确定算法 → 3. 绘制流程图 → 4. 分配存储空间和工作单元 → 5. 编写程序 → 6. 上机调试。 #### 程序基本结构形式:顺序、分支、循环、子程序。 #### 4.8.1 分支程序设计 - **分支程序**:根据不同情况或条件执行不同功能,具有判断和转移功能,利用条件转移指令对状态标志进行判断实现转移。 - **特点**:按条件判断、转移、分支。 - **两种结构**:**二路分支**(if-then-else)、**多路分支**(case)。 - **共同特点**:在某一特定条件下,只执行多个分支中的一个。 #### 4.8.2 循环程序设计 - **循环程序五部分**: 1. 初始化部分(设置初值); 2. 循环工作部分(核心); 3. 循环修改部分(修改工作变量、指针); 4. 循环控制部分(修改控制变量,确定程序走向); 5. 循环结束部分(结果处理)。 - **两种常用结构**: - **先执行后判断**(DO-WHILE):至少执行一次循环体; - **先判断后执行**(DO-UNTIL):允许零次循环。 #### 典型例题:例 4.8.4 多重循环(冒泡排序) - **问题**:从首地址 `AA1` 开始存放 N 个无符号字节数据,用**冒泡法**将数据按由大到小排序。 - **冒泡法核心**:相邻两个单元比较,位置交换,N-1 次后排出一个最大(或最小)数据;经 N-1 次循环完成排序。 - **关键步骤**:比较 → 判断 → 交换。 **版本 (1):N-1 遍,每遍比较 N-1 次** — 内外循环计数都用 N-1。 **版本 (2):N-1 遍,每遍比较次数少 1** — 用 DX 作为外循环计数器,每次进入内循环前 `MOV CX, DX`,使每遍比较次数递减。 **版本 (3):有交换标志** — 增加 AH 作为交换标志,每次交换时 `MOV AH,1`;每遍结束 `OR AH,AH` + `JZ` 提前退出;无交换则提前结束排序(效率优化)。 #### 4.8.3 子程序设计 ##### 1. 子程序的定义 - **子程序组成 7 部分**:子程序定义(即入口)、保护现场、取入口参数、子程序体、存输出参数、恢复现场、返回。 ##### 2. 现场的保存与恢复 - **保护现场**:在子程序中,保护那些主程序要用、而子程序也要用的寄存器内容。 - **恢复现场**:恢复现场保护的内容。 - **方法**:(1) 使用堆栈指令(PUSH/POP);(2) 使用数据传送指令。 ##### 3. 子程序的参数传送 - **参数传送**:主程序和子程序之间的数据传送。 - **方法**:(1) 通过**寄存器/存储器**传送参数;(2) 通过**堆栈**传送参数或参数表地址。 **(1) 通过寄存器传送参数**(最常用) - 特点:参数放在寄存器中,简便、实用,适用于参数较少的情况。 **(2) 典型例题:例 4.8.5 — 用寄存器传送,编制键入 5 位十进制数加法程序** - **子程序 S1 功能**:键盘输入 5 位以内十进制数 → 二进制数 → 存入 CX,**用非数字键结束本次数字输入**。 - 10 进制 → 2 进制转换算法(霍纳/秦九韶法): `((D5×10+D4)×10+D3)×10+D2)×10+D1)×10+D0` - 实现方式:每按一个数字键,`CX×10 + 本次输入数位`。 - **子程序 S2 功能**:CX(二进制数) → 十进制数,在显示器上显示。 - 基本方法:从万位开始依次比较 `CX ≥ 10000`、`≥ 1000`、`≥ 100`、`≥ 10`,不够则跳过(避免最高位显示 0);用 `SUB` + `INC DL`(不影响 CF)+ `JNC` 累加,再用 `OR DL,30H` 转为 ASCII 码,最后用 `INT 21H` 的 02H 号功能逐位显示。 - **主程序 MAIN**:调用 S1 取被加数和加数 → 求和 → 调用 S2 显示结果。 - 约束条件:结果 ≤ 65535;MAIN、S1、S2 同段,子程序使用 NEAR 属性,主程序 MAIN 使用 FAR 属性。 - **程序返回操作系统**:`MOV AH, 4CH` + `INT 21H`。 --- ## 二、考点总结 ### 1. 伪指令(4.6) - 【高频】**END**:源程序结束伪指令,必须有,可带启动标号。 - 【高频】**SEGMENT/ENDS**:成对出现,四个参数(定位、组合、字长、类别)及默认值(PARA / PRIVATE / USE16)。 - 【高频】**ASSUME**:说明段与段寄存器关联(CS、SS 必用,其他由程序赋值),属说明性语句。 - 【高频】**数据定义 DB/DW/DD**:字节/字/双字,`DUP(?)` 预留空间;`?` 表示不赋值。 - 【高频】**ORG** 与 **`$` 地址计数器**:控制起始地址。 - **EQU**:赋值伪指令,**只能定义一次**,可指向寄存器名。 - **PROC/ENDP**:过程定义,属性 NEAR(默认)/ FAR;过程名是 CALL 的操作数。 - 【高频】**PTR**:类型不确定时必须使用(如 `MOV BYTE PTR [BX], 8`)。 - **返回值运算符**:SEG / OFFSET / TYPE / LENGTH / SIZE 的返回值含义(TYPE: NEAR=-1、FAR=-2、DB=1、DW=2、DD=4)。 ### 2. 源程序结构(4.7) - 【高频】**5 种语句类型**:指令语句、伪指令语句、宏指令语句、注释语句、空行语句。 - 【高频】**.COM 与 .EXE 比较**:段长限制(64KB / 内存空间)、段重定位、入口偏移(100H / 段首)。 - 程序结尾必须有返回 DOS 指令:`MOV AH,4CH` + `INT 21H`。 ### 3. 程序设计(4.8) - **程序设计 6 步**:描述问题 → 算法 → 流程图 → 存储分配 → 编写 → 调试。 - **基本结构 4 类**:顺序、分支、循环、子程序。 - 【高频】**分支**:二路分支、多路分支;常用条件转移指令(JZ/JNZ、JC/JNC、JO/JNO 等)。 - 【高频】**循环 5 部分**:初始化、工作、修改、控制、结束。 - 【高频】**两种循环结构**:先执行后判断(至少一次)、先判断后执行(可零次)。 - 【高频】**冒泡排序**:N 个数需 N-1 遍,每遍两两比较交换;可优化为带"交换标志"提前退出。 - 【高频】**子程序 7 部分**及**参数传送两种方式**:寄存器传送(最常用)、堆栈传送。 - **现场保护**:PUSH/POP 配对使用,后进先出。 ### 4. 常见题型 | 题型 | 示例 | |------|------| | **简答题** | 简述汇编语言源程序的 5 种语句;简述子程序的组成结构;简述现场保护与恢复的方法 | | **填空题** | `DB`/`DW`/`DD` 数据类型字节数;SEGMENT 默认定位类型 PARA;TYPE NEAR = -1 | | **选择题** | 下列伪指令属于说明性语句的是(A. END B. ASSUME C. DB D. PROC);COM 文件入口偏移为 100H | | **判断题** | SEGMENT 与 ENDS 必须成对出现(√);EQU 可重复定义同一符号(×);PROC 默认属性 FAR(×,应为 NEAR) | | **程序阅读/改错** | 阅读冒泡排序程序指出错误(如 LOOP 用错、循环计数初值错) | | **程序设计题** | 用寄存器传送参数设计"键入十进制数求和并显示"程序;用多重循环实现数据排序 | | **指令正误** | `MOV BYTE PTR [BX], 8` 是否合法(合法);`MOV AH, 4CH` 与 `INT 21H` 是否配套(是) | ### 5. 易混淆点 - **PROC 属性**:默认 **NEAR**,不是 FAR;只有跨段调用才用 FAR。 - **ASSUME**:仅说明关联关系,**不是赋值**;段寄存器 DS、ES 等需程序中 `MOV`。 - **PTR 与变量定义**:DB 定义字节变量,若要按字读需 `MOV AX, WORD PTR DATA2`。 - **冒泡排序方向**:从大到小排序时用 `JNC`(前大后小不交换);从小到大排序时用 `JC`(前小后大不交换)。 - **EQU vs `=`**:EQU 不允许重复定义同名符号。 - **堆栈段**:必须有 `SEGMENT … STACK`,并通过 `ASSUME SS:段名` 关联。 ### 6. 综合程序设计模板(务必熟记) ``` DATA SEGMENT ; 数据定义 DATA ENDS CODE SEGMENT ASSUME CS:CODE, DS:DATA MAIN PROC FAR MOV AX, DATA MOV DS, AX ; … 程序主体 … MOV AH, 4CH INT 21H MAIN ENDP ; … 子程序 … CODE ENDS END MAIN ```