14 KiB
14 KiB
第 4 章 汇编语言及其程序设计(6-8 节)总结
本章 6-8 节内容重点:伪指令(4.6)、汇编语言源程序的结构(4.7)、汇编语言程序设计(4.8,含顺序/分支/循环/子程序)。与第 4 章前 5 节(指令系统)衔接,但侧重点转向汇编源程序组织与结构化程序设计。
一、详细内容分析
4.6 伪指令
伪指令是给汇编程序(编译器)的指示,CPU 不执行,不产生目标代码(宏指令除外)。用于选择处理器、定义数据、分配存储区等。
1. END — 源程序结束伪指令
- 格式:
END [标号] - 功能:表示源程序结束,不可缺,是源程序编译的最后一条语句。
- 说明:
- 标号指示程序开始执行的起始地址;
- 起始执行地址缺省值为代码段第一条指令地址;
- 多个模块链接时,主程序用标号,其他程序不用。
2. SEGMENT 和 ENDS — 段定义伪指令
- 格式:
段名 SEGMENT [定位类型][组合类型][字长类型]['类别'] … 段名 ENDS - 功能:定义段名、段属性,并表示段的开始位置、结束位置。
- 说明:
SEGMENT和ENDS必须成对出现;- 段名是段的标识符(基址),由程序员指定;
- 四个参数选项可省略,使用默认值:
- 定位类型:
BYTE、WORD、DWORD、PARA(默认)、PAGE; - 组合类型:
PUBLIC、STACK、COMMON、MEMORY、PRIVATE(默认)、AT 表达式; - 字长类型:
USE16(默认)、USE32; - 类别:
'DATA'、'CODE'、'STACK'、'EXTRA'等,连接时同类别段放在连续存储空间。
- 定位类型:
3. ASSUME — 段分配伪指令
- 格式:
ASSUME 段寄存器名:段名, 段寄存器名:段名, …… - 功能:说明某个段关联哪一个段寄存器。
- 说明:
- 程序段必须用 CS,堆栈段必须用 SS;
- 该语句一般放在代码段最前面;
- 说明性语句,除 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 汇编语言源程序结构特点
- 一个程序由若干逻辑段组成,各逻辑段由伪指令语句定义和说明;
- 整个源程序以
END伪指令结束; - 每个逻辑段由语句序列组成,语句可以是:
- 指令语句:CPU 指令,可执行,汇编时翻译成目标码;
- 伪指令语句:CPU 不执行,提供汇编信息,不产生目标代码;
- 宏指令语句:一个指令序列,汇编时产生对应的目标代码序列;
- 注释语句:以分号
;开始,说明性语句,汇编程序不处理; - 空行语句:仅包含回车换行符,便于阅读。
- 每个源程序应当有返回操作系统的指令语句(典型:
MOV AH,4CH+INT 21H),使程序执行完后能自动返回系统。
DOS 环境下两种程序结构
| 类型 | 长度限制 | 加载特点 | 段起始 | 适用场景 |
|---|---|---|---|---|
| .COM 文件 | 一个段长(64KB) | 无段重定位,结构紧凑、装入快 | 偏移量从 100H 开始,且 100H 处必须是可执行指令 | 小型程序 |
| .EXE 文件 | 仅受内存空间限制 | 需段重定位,占用盘空间大、装入慢 | 由程序员指定 | 中、大型程序 |
4.8 汇编语言程序设计
基本步骤
- 描述问题 → 2. 确定算法 → 3. 绘制流程图 → 4. 分配存储空间和工作单元 → 5. 编写程序 → 6. 上机调试。
程序基本结构形式:顺序、分支、循环、子程序。
4.8.1 分支程序设计
- 分支程序:根据不同情况或条件执行不同功能,具有判断和转移功能,利用条件转移指令对状态标志进行判断实现转移。
- 特点:按条件判断、转移、分支。
- 两种结构:二路分支(if-then-else)、多路分支(case)。
- 共同特点:在某一特定条件下,只执行多个分支中的一个。
4.8.2 循环程序设计
- 循环程序五部分:
- 初始化部分(设置初值);
- 循环工作部分(核心);
- 循环修改部分(修改工作变量、指针);
- 循环控制部分(修改控制变量,确定程序走向);
- 循环结束部分(结果处理)。
- 两种常用结构:
- 先执行后判断(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 + 本次输入数位。
- 10 进制 → 2 进制转换算法(霍纳/秦九韶法):
- 子程序 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