• 沒有找到結果。

微机原理与接口技术 - 万水书苑-出版资源网

N/A
N/A
Protected

Academic year: 2021

Share "微机原理与接口技术 - 万水书苑-出版资源网"

Copied!
63
0
0

加載中.... (立即查看全文)

全文

(1)第 5 章 汇编语言程序设计. 汇编语言是一种面向机器的低级语言,利用汇编语言设计的程序效率高、实时 性强,还能直接控制硬件,能充分发挥硬件的潜力。但现在主流的开发工具是高级 语言,所以本章不仅介绍 DOS 环境下的汇编语言程序设计,而且介绍了 Windows 下的汇编开发方法。汇编语言源程序的组成部分有:模块、段、子程序和宏等。第 1 部分介绍汇编语言的语法规定,着重叙述汇编语言程序的格式、各种伪指令的格式 及用法、常用标识符的定义及应用、宏汇编的应用;第 2 部分介绍汇编语言程序设 计的方法、步骤,着重叙述汇编语言程序的三大结构,并通过编程举例介绍常见问 题的解决方法;第 3 部分介绍子程序结构及设计方法,着重叙述子程序调用/返回的 原理、子程序入/出口参数传递方法、子程序的嵌套/递归调用,并结合实例说明子程 序设计方法;最后两部分介绍 Windows 汇编语言程序设计以及与高级语言的混合编 程方法。. z z z z z z. 汇编语言源程序书写格式及常用伪指令语句 结构化程序设计方法:顺序结构程序设计、分支结构程序设计、循环结构程 序设计 宏汇编 子程序设计方法 Windows 汇编语言程序设计 汇编语言与高级语言的混合编程. 5.1 汇编语言的特点 汇编语言(Assembly Language)是一种以处理器指令系统为基础的低级程序设计语言, 它采用助记符来表示操作码,用标识符来表示操作数地址码。 用汇编语言编写的程序具有以下特性: (1)与机器相关。汇编语言是一种面向机器的程序设计语言,是机器指令的符号表示。 不同类型 CPU 的机器指令系统不同,对应的汇编语言也就不同,所以汇编语言程序与机器有 着密切的关系。因此,汇编语言程序的通用性和可移植性较低。 (2)执行效率高。汇编语言程序能直接管理和控制硬件设备,直接与存储器、接口电路 等打交道,还能申请中断。程序员在编写程序时,可以对机器内部的各种资源进行合理的安排,.

(2) 第5章. 汇编语言程序设计 129. 编写出最优化的程序,因而用汇编语言编写的程序执行效率高、占用存储空间小、运行速度快。 (3)编程复杂。利用汇编语言编写程序完成某项工作时,必须了解 CPU 完成该项工作的 每一个细节,用一系列汇编指令一步一步来实现;另外,在编写汇编语言程序时,还要考虑机 器资源的限制、汇编指令的细节和限制等,增加了编写程序的复杂性,需要程序员对计算机硬 件和操作系统有相当深入的了解。. 5.2 汇编语言程序结构和基本语法 在 MS DOS 环境下,用 8086/8088 汇编语言开发设计出的既节省空间、又快速高效的程序 代码,可以直接控制计算机的硬件。到了 Windows 环境下,虽然仍可使用 8086/8088 汇编语 言,但这个环境已不是原有的 MS DOS 环境,不能像以前那样,随意对系统编程去控制计算 机了。下面首先介绍 DOS 环境下的汇编程序例子。 5.2.1. 示例程序. 通过下面一个完整的汇编语言源程序来讨论汇编语言程序的格式,该程序的功能是实现 C=A+B,其中 A、B、C 均为字节数据。 例 5-1 DATA A B C DATA CODE ASSUME START:. SEGMENT DB 12H DB 34H DB ? ENDS SEGMENT CS:CODE, DS:DATA MOV AX,DATA MOV DS,AX MOV AL,A ADD AL,B MOV C,AL MOV AH,4CH INT 21H CODE ENDS END START. ;定义段 DATA ;定义变量 A,其值为 12H ;定义变量 B,其值为 34H ;定义变量 C,但没有赋值 ;DATA 段定义结束 ;定义段 CODE ;规定 DATA、CODE 分别为数据段和代码段 ;用标号 START 指明程序执行的起始点 ;给数据段寄存器 DS 赋值 ;将变量 A 的值送入寄存器 AL ;将 AL 的值与变量 B 的值相加,并将其和存入 AL ;将 AL 的值送给变量 C ;调用 DOS 中断,退出程序并返回 DOS 状态 ;CODE 段定义结束 ;整个源程序结束. 从该例中可以看出汇编语言源程序具有以下特点: (1)汇编语言源程序由若干个段组成(完整的汇编语言源程序由数据段、代码段、附加 段、堆栈段组成,其中代码段是不可缺少的),在代码段中用 ASSUME 伪指令将段地址与段 寄存器的对应关系告诉汇编程序,每个段以 SEGMENT 语句开始,以 ENDS 语句结束,整个 源程序以 END 结束。 (2)段由若干语句组成,一条语句一般写在一行上,书写时语句的各部分应尽量对齐。 (3)汇编语言程序中至少要有一个启动标号,作为程序开始执行时目标代码的入口地址。 启动标号常用 START、BEGIN 等命名。 (4)为增加程序的可读性,可在汇编语言语句“;”后加上注释。 (5)为保证在执行过程中数据段地址的正确性,在源程序中需要对 DS 寄存器进行初始化。.

(3) 130. 微机原理与接口技术. (6)为了在程序结束时返回 DOS,一般通过调用 DOS 中断的 4CH 子功能来实现。 5.2.2. 基本概念. 1.汇编语言中的语句 汇编语言源程序由语句序列构成,其语句序列可分为指令语句、伪指令语句、宏指令语 句 3 种类型。 (1)指令语句。指令语句是可执行语句(即第 3 章中介绍的处理器指令系统),在汇编 后要产生对应的目标代码,CPU 根据这些代码执行相应的操作。 格式:[标号:] <指令助记符> [操作数] [;注释] 例如: START: MOV AX,DATA ;用标号START指明程序执行的起始点 (2)伪指令语句。伪指令是不可执行语句,在汇编中不产生目标代码,用于指示汇编程 序如何汇编源程序,利用它定义和说明常量与变量的属性及存储器单元的分配等。 格式:[名字] <伪指令助记符> [操作数] [;注释] 例如: A DB 12H ;定义变量A,其值为12H (3)宏指令语句。宏指令是以一个宏名定义的一段指令序列,在汇编中凡是出现宏指令 语句的地方,都会有相应的指令语句序列的目标代码插入。 格式:[标号:] <宏名> [实参表] [;注释] 汇编中的大部分指令语句与 8086 指令相对应,这里不再赘述。本节将着重介绍伪指令语 句和宏指令语句。 2.汇编语句使用说明 (1)标号和名字称为标识符,汇编语言中标识符的组成规则如下: z 标识符由字母、数字及规定的特殊符号(如 _、$、?、@)组成。 z 标识符必须以字母打头。 z 标识符字符长度不得超过 31。 z 默认情况下,汇编程序不区别标识符中字母的大小写。 z 用户定义标识符必须是唯一的,且不能与汇编语言专用的保留字重名。 (2)标号用来指向一条指令或宏指令,表示后面的指令第一个字节存放的内存地址,标 号常作为转移指令的操作数,确定程序转移的目标地址;名字用来指向一条伪指令,用作变量 名时,表示变量存放在内存中首字节的地址。 (3)名字和标号都具有以下 3 种属性: z 段属性:表示标号或变量所在段基址,标号的段基址在 CS 段寄存器中,变量的段基 址在 DS 或 ES 中。 z 偏移属性:表示标号或变量所在的段内偏移地址,它代表从段的起始地址到定义标号 或变量的位置之间的字节数,段基址和偏移地址组成标号或变量的逻辑地址。 z 类型属性:当标号作为转移类指令的操作数时,可在段内或段间转移,其属性有 NEAR (段内转移)和 FAR(段间转移)两种,若没有对标号进行类型说明,就默认为 NEAR 属性;对于变量,类型属性说明变量在内存中占多少个字节,其属性有 BYTE(字节)、 WORD(字)、DOUBLE WORD(双字)3 种。.

(4) 第5章. 汇编语言程序设计 131. (4)指令的操作数可以是立即数、寄存器和存储单元;伪指令的操作数可以是常数、变 量名、表达式等;若有多个操作数时,操作数之间用逗号间隔。 (5)分号“;”后的部分为注释内容,用以增加源程序的可读性,汇编程序在翻译源程序 时将跳过该部分,对它们不做任何处理。 3.汇编语言中的常量与变量 (1)常量:汇编中允许的常量有整数常量和字符串常量两种。 1)整数常量:整数常量可以采用 4 种表示方法: z 二进制常量:由数字 0、1 组成的序列,且以字母 B 结尾,如 10101010B。 z 十进制常量:由数字 0~9 组成的序列,结尾可以加上字母 D,如 9876D 或 6575。 z 八进制常量:由数字 0~7 组成的序列,且以字母 Q(或字母 O)结尾,如 255Q、377O。 z 十六进制常量:由数字 0~9、字母 A~F(或 A~F)组成的序列,且以字母 H 结尾, 如 3456H、0AB19H(为了避免与标识符相混淆,十六进制数在语句中必须以数字打 头,凡是以字母 A~F 开始的十六进制数,必须在前面加上数字 0)。 2)字符串常量:字符串常量是由单引号或双引号括起来的单个字符或多个字符构成的, 汇编程序把引号中的字符翻译成它的 ASCII 码值,如'A'(等于 41H)、'BC'(等于 4243H)、 "HELLO"等。 (2)变量:汇编语言中的变量用来表示存放在内存中的操作数,它的值是可以改变的, 变量的值就是操作数在内存中首字节的地址,变量要事先定义才能使用(详见 5.2.3 节)。 4.汇编语言中的运算符与表达式 (1)运算符:汇编语言中的运算符分为六大类:算术运算符、移位运算符、逻辑运算符、 关系运算符、分析运算符、合成运算符,如表 5-1 所示。 表 5-1 汇编语言中的运算符 运算符 类型. 算术 运算符. 移位 运算符. 逻辑 运算符. 关系 运算符. 符号. 实例. 表达式的值/功能说明. 名称. +. 加. 1+2. 3. -. 减. 4-3. 1. *. 乘. 5*6. 30. /. 除. 64/8. 8. MOD. 取余. 9MOD7. 2. SHL. 逻辑左移. 0011B SHL 2. 1100B. SHR. 逻辑右移. 1100B SHR 2. 0011B. NOT. 非. NOT 0001B. 1110B. AND. 与. 1000B AND 0001B. 0000B. OR. 或. 1000B OR 0001B. 1001B. XOR. 异或. 1000B XOR 0001B. 1001B. EQ. 相等. 10H EQ 10. 假(用全 0 表示). NE. 不等. 10H NE 10. 真(用全 1 表示). LT. 小于. 10H LT 10. 假(用全 0 表示).

(5) 132. 微机原理与接口技术 续表 运算符 类型 关系 运算符. 分析 运算符. 合成 运算符. 实例. 符号. 表达式的值/功能说明. 名称. LE. 小于等于. 10 LE 0AH. 真(用全 1 表示). GT. 大于. 10H GT 10. 真(用全 1 表示). GE. 大于等于. 10H GE 100. 假(用全 0 表示). SEG. 求段基址. SEG X. X 所在段的段基址. OFFSET. 求偏移地址. OFFSET X. X 在段内的偏移地址. LENGTH. 求变量包含的单元 数. LENGTH X. X 包含的单元数(详见例 5-4). TYPE. 求变量的字节数. TYPE X. X 的字节数(详见例 5-4). SIZE. 求变量的总字节数. SIZE X. X 的总字节数(详见例 5-4). PTR. 修改类型. WORD PTR X. 访问 X 对应的字数据(详见例 5-4). THIS. 指定类型. X EQU THIS BYTE. 指定变量 X 为字节属性(详见例 5-6). :. 段超越. ES:[1000H]. 指定访问附加段中偏移地址为 1000H 的单元. HIGH. 求高字节. HIGH 1234H. 12H. LOW. 求低字节. LOW 1234H. 34H. SHORT. 短转移说明. JMP SHORT NEXT. 说明转移地址在下一条指令地址的 -128~127 个字节范围. (2)表达式:表达式是常数、寄存器、标号、变量与一些运算符和操作码相组合的序列。 表达式的运算不由 CPU 完成,而是在程序汇编过程中进行计算确定,并将表达式的结果作为 操作数参与指令所规定的操作。 当各种运算符同时出现在同一表达式中时,按照运算符的优先级进行计算,对于优先级 相同的运算符,按照从左到右的顺序进行计算(运算符优先级顺序如表 5-2 所示) 。 表 5-2 汇编语言中运算符的优先级 优先级. 运算符. 高 低. 1. LENGTH、SIZE、WIDTH、MASK、(. 2. PTR、OFFSET、SEG、TYPE、THIS. 3. HIGH、LOW. 4. +、–(单目运算,表示取正、取负). 5. *、/、MOD、SHL、SHR. 6. +、–(双目运算,表示加、减). 7. EQ、NE、LT、LE、GT、GE. 8. NOT. 9. AND. 10. OR、XOR. 11. SHORT. )、[. ]、<. >.

(6) 第5章. 汇编语言程序设计 133. 例 5-2 MOV AX, 1*2+3. ;等价于 MOV AX,5. MOV AX, X+4. ;等价于 MOV AX,X[4],注意这里是 X 地址加 4 ;不是 X 的值加 4. MOV AH, 0001B. SHL 3. ;等价于 MOV AH,00001000B. MOV BH, 1000B. SHL(1+2). ;等价于 MOV BH,00000001B. MOV DH, NOT 10000000B. ;等价于 MOV DH,01111111B. MOV DL, 00011010B AND 00101011B. ;等价于 MOV DL,00001010B. MOV AX, 10 EQ 1010B. ;等价于 MOV AX, 0FFFFH. MOV BX, 10H GT 10. ;等价于 MOV BH, 0FFFFH. ADD. CX, 99H LE 99. ;等价于 ADD CX, 0000H. MOV DX, SEG X. ;假设 X 为数据段内定义的变量,则该语句等价于. MOV AX, OFFSET X. ;等价于 LEA AX,X. MOV DL, 00011010B AND 00101011B. ;等价于 MOV DL,00001010B. MOV AX, 10 EQ 1010B. ;等价于 MOV AX, 0FFFFH. ;MOV DX, DS. MOV BX, 10H GT 10. ;等价于 MOV BH, 0FFFFH. ADD. ;等价于 ADD CX, 0000H. CX, 99H LE 99. MOV DX, SEG X. ;假设 X 为数据段内定义的变量,则该语句等价于. MOV AX, OFFSET X. ;MOV DX, DS ;等价于 LEA AX,X. MOV AX, [1000H]. ;将数据段中偏移地址为 1000H 的字数据送给 AX. MOV AH,HIGH 0ABCDH. ;等价于 MOV AH, 0ABH. MOV AL,LOW 0ABCDH. ;等价于 MOV AL, 0CDH. MOV AX, ES:[1000H]. ;将附加段中偏移地址为 1000H 的字数据送给 AX ;段超越用来指定地址是在附加段中. 5.2.3. 伪指令. 汇编语言中有丰富的伪指令。依其功能可将其分为数据定义伪指令、符号定义伪指令、段 定义伪指令、段分配伪指令、过程定义伪指令、模块定义伪指令、结构定义伪指令和记录定义 伪指令等。 1.数据定义伪指令 数据定义伪指令用来为变量申请固定长度的存储空间,并可同时将相应的存储单元初 始化。 格式:[变量名] 伪指令助记符 初值表 (1)变量名为用户自定义标识符,表示初值表中首个元素的逻辑地址,可以通过变量名 来访问它所指示的存储单元,有时也可以省略变量名。 (2)变量定义伪指令有 5 种形式: z DB 定义字节变量,即其后的每个操作数均占 1 个字节。 z DW 定义字变量,即其后的每个操作数均占 2 个字节。 z DD 定义双字变量,即其后的每个操作数均占 4 个字节。 z DQ 定义 4 字变量,即其后的每个操作数均占 8 个字节。.

(7) 134. 微机原理与接口技术. DT 定义 10 字节变量,即其后的每个操作数均占 10 个字节。 注意:存放多字节数据时,数据高字节存放在高地址单元,低字节存放在低地址单元。 (3)初值表给出变量的初始化值,有多个值时用逗号分隔,初始化值可以是数值常数, 也可以是表达式、?,还可以由$、重复操作符 DUP 组成。其中: z ?:表示未赋初值。 z $:表示将要分配的内存单元的偏移地址。 z DUP:表示重复初值,其格式为: 重复次数 DUP(重复参数) ;重复参数可以是多个,之间用逗号间隔 例如: z. 2. DUP(1,2). ;等价于 1,2,1,2. 2.起始位置定位伪指令 ORG 在数据段内一般从偏移地址为 0 的存储单元开始,依次按顺序分配内存单元;使用 ORG 可以指定从某一个偏移地址开始分配内存单元。 ORG 指令格式: ORG 表达式 ;从表达式的值指定的偏移地址开始分配的内存单元 例 5-3 DATA SEGMENT X DB 64*2-100, 'D' DB 'CHN' DW ? ORG 0100H Y DW 2 DUP(100) M DB 2 DUP(1,2 DUP(1,2)) Z. DW $-10. ;两初始化值的十六进制表示分别为 1CH、44H ;省略了变量名,初始化值的十六进制表示为 43H、48H、4EH ;只分配两个字节空间,未赋初值 ;指定从偏移地址为 0100H 单元开始分配内存单元 ;定义了两个字数据 0064H ;定义了十个字节数据 01H、01H、02H、01H、02H、01H、 ;01H、02H、01H、02H ;此时要分配的单元偏移地址为 010EH,故初始化值 ;为 010EH-10=0104H. DATA ENDS. 本例中数据段中的数据在内存中的存放如图5-1所示。 例 5-4 分析运算符的使用(数据定义同例 5-3) 。 ;说明:LENGTH 用来求其后的变量包含的单元数,即变量用 DUP 初始化时,返回 DUP 重复次数 ;对于不是用 DUP 初始化的变量,则返回 1 MOV AX, LENGTH Y ;等价于 MOV AX, 2 MOV AX, LENGTH M ;等价于 MOV AX, 2 MOV AX, LENGTH X ;等价于 MOV AX, 1 ;说明:TYPE 用来求其后的变量或标号的属性值(变量或标号的属性值如表 5-3 所示) MOV AX, TYPE Y ;等价于 MOV AX,2 MOV AX, TYPE X ;等价于 MOV AX,1 ;说明:SIZE 用来求其后的变量包含多少个字节,即 SIZE=LENGTH×TYPE MOV AX, SIZE Y ;等价于 MOV AX,4 MOV AX, SIZE X ;等价于 MOV AX,1 ;说明:PTR 用来求其后的变量或标号的类型,格式为:类型 PTR 表达式(其中,类型为 BYTE、 ;WORD、DWORD、NEAR 或 FAR) MOV AX, X ;(×)本语句源操作数 X 为字节类型,目的操作数 AX 为 ;字类型,两者类型不匹配 MOV AX, WORD PTR X ;(√)利用 PTR 运算符修改 X 类型为字类型,即源操作数 ;为从 X 开始的字数据,等价于 MOV AX, 441CH.

(8) 第5章. X. 存储单元. 偏移地址. 1CH. 0000H. 44H. 0001H. 43H. 0002H. 48H. 0003H. 4EH. 0004H. ——. 0005H. ——. 0006H. 汇编语言程序设计 135. 数据段 DATA. …… Y. M. Z 图 5-1. 64H. 0100H. 00H. 0101H. 64H. 0102H. 00H. 0103H. 01H. 0104H. 01H. 0105H. 02H. 0106H. 01H. 0107H. 02H. 0108H. 01H. 0109H. 01H. 010AH. 02H. 010BH. 01H. 010CH. 02H. 010DH. 04H. 010EH. 01H. 010FH. 例 5-3 中内存数据的存放. 表 5-3 变量或标号的属性值 属性名. 变量. 标号. 属性值. DB. 1. DW. 2. DD. 4. DQ. 8. DT. 10. NEAR. -1. FAR. -2. 3.符号定义伪指令 符号定义伪指令用来定义符号常量,系统不会给符号常量分配内存空间。其指令有 EQU、=。.

(9) 136. 微机原理与接口技术. 指令格式:符号 EQU 表达式 符号 = 表达式 ;左边符号的值为右边表达式的值 两者的区别是,用“=”定义的符号常量可以被重新定义,而用 EQU 定义的符号常量不 能被重新定义。 例 5-5 VAR1 MOV. EQU 10H AL,VAR1. ;等价于 MOV AL,10H. VAR2 EQU Z MOV AX,VAR2 ;等价于 MOV AX,Z VAR3 EQU VAR1*3+10 MOV AL,VAR3 ;等价于 MOV AL,3AH VAR4 EQU [BX+SI+100] MOV AL,VAR4 ;等价于 MOV AL,[BX+SI+100] VAR5 EQU ADD VAR5 AX,BX ;等价于 ADD AX,BX VAR6 EQU 01H VAR6 EQU 02H ;(×)前面已经定义了符号常量 VAR6,不能再重复定义 VAR6 MAX = 100 MAX = MAX + 100 ;(√)前面符号常量 MAX 的值为 100,现在其值被修改为 200 ;说明:可以用 PURGE 指令解除对符号常量的定义,之后就可以对该符号重新定义了 ;其格式为:PURGE 符号 1,符号 2,…,符号 N MIN EQU 01H PURGE MIN MIN EQU 02H ;(√)前面已经定义了符号常量 MIN,PURGE 解除了对 MIN ;的定义,所以可以重新定义 MIN. 4.LABEL 伪指令 LABEL 伪指令为其后定义的变量或标号定义一个不同类型的别名。其格式为: 变量或标号. LABEL 类型. 其中,类型为 BYTE、WORD、DWORD、NEAR 或 FAR。 例 5-6 VAR X. LABEL WORD DB 'AB'. MOV MOV. AX,VAR AL,X. ;变量 VAR、X 指向内存中的同一单元,但两者类型分别为 ;字类型、字节类型 ;等价于 MOV AX,4241H ;等价于 MOV AL,41H. 例 5-6 还可以改成: VAR X. EQU THIS WORD DB 'AB'. ;变量 VAR、X 指向内存中的同一单元,但两者类型分别 ;为字类型、字节类型 MOV AX, VAR ;等价于 MOV AX,4241H MOV AL,X ;等价于 MOV AL,41H ;说明:THIS 为其后定义的变量或标号定义一个不同类型的别名。其格式为:变量名 EQU THIS 类型 ;其中,类型为 BYTE、WORD、DWORD、NEAR 或 FAR. 5.段定义伪指令 汇编语言源程序由若干个段组成,段定义伪指令(SEGMENT/ENDS)用来定义一个段,.

(10) 第5章. 汇编语言程序设计 137. 要求给出段名,由 SEGMENT 指定段的开始,ENDS 指定段的结束。其格式为: 段名 SEGMENT [定位类型] [组合类型] ['类别'] … ;语句序列 段名 ENDS. 说明: (1)SEGMENT 和 ENDS 必须成对出现。 (2)段名由用户自己命名,必须符合标识符命名规则,前后段名必须保持一致。每个段 的段名即为该段的段基址。 (3)定位类型用来说明对段起始地址的要求,可以省略。定位类型有以下 4 种: z BYTE:段的起始地址可在任意字节边界上,即段起始地址是任意的。 z WORD:要求段的起始地址在任意字边界上,即段起始地址最低位为 0,亦即段起始 地址必须为偶地址。 z PARA:要求段的起始地址在节(16 字节)的边界上,即段起始地址低 4 位全部为 0, 如 XXXX0H。缺省定位类型时,默认为 PARA 类型。 z PAGE:要求段的起始地址在页(256 字节)边界上,即段起始地址低 8 位全部为 0, 如 XXX00H。 (4)组合类型用来说明同类别名的段的连接方式,可以省略。定位类型有以下 6 种: z NONE:不与其他段连接。缺省组合类型时,默认为 NONE 类型。 z PUBLIC:将不同程序模块中同名同类型的段按顺序连接成一个共同的段装入内存。 z STACK:指定该段为堆栈段,并将不同程序模块中的堆栈段按顺序连接成一个堆栈 段,即所有程序模块共用一个堆栈段。 z COMMON:将不同程序模块中同名同类型的段都从同一个地址开始装入,即以覆盖 方式连接,各个逻辑段将发生重叠,段长度为最大段的长度。 z AT 表达式:按照表达式的值指定的段基址将段装入内存。 z MEMORY:多个逻辑段连接时,连接程序将把本段连接在其他所有段之上。若多个 段均为 MEMORY 类型时,则将第一个 MEMORY 段置于所有段之上,其他 MEMORY 段当成 COMMON 类型来处理。 (5)类别名必须用‘’引起来,用来说明该段类别名,在连接时将同类别名的段按照组合 类型进行组合。类别名由用户自定义,长度不超过 40 个字符。 例 5-7 CODE SEGMENT 'CODE' … CODE ENDS ;定义一个段,段名为 CODE,类别名为 CODE STACKSEG SEGMENT STACK … STACKSEG ENDS ;定义一个堆栈段,段名为 STACKSEG,组合类型为 STACK DATA1 SEGMENT WORD PUBLIC 'CONST' … DATA1 ENDS ;定义一个段,段名为 DATA1,定位类型为 WORD,组合类型为 PUBLIC ;类别名为 CONST CODESEG SEGMENT PARA PUBLIC 'CODE' … CODESEG ENDS ;定义一个段,段名为 CODESEG,定位类型为 PARA,组合类型为 PUBLIC ;类别名为 CODE.

(11) 138. 微机原理与接口技术. 6.段分配伪指令 段分配伪指令用来说明当前哪些逻辑段为代码段、哪些为数据段、哪些为堆栈段、哪些 为附加段。其格式为: ASSUME 段寄存器: 段名[, 段寄存器: 段名, …]. 说明: (1)ASSUME 伪指令只能设置在代码段内,放在段定义语句之后。 (2)ASSUME 伪指令只是建立了逻辑段与段寄存器之间的关系,并没有为段寄存器赋值。 对于代码段和堆栈段,由连接程序来设置 CS、IP、SS、SP 的值;而数据段和附加段则需要由 用户在程序中对 DS、ES 赋值。 (3)每个段的段名即为该段的段基址,它是一个16位的立即数,因此不能直接将它送给 段寄存器,通常先将段名送给一个通用寄存器,然后将该通用寄存器的值再送给段寄存器,来 对DS、ES赋值。 例 5-8 DATA1. SEGMENT ;定义一个段,段名为 DATA1 X DB 100 DATA1 ENDS EXTRA SEGMENT ;定义一个段,段名为 EXTRA STR DW 10 DUP(?) EXTRA ENDS STACKSEG SEGMENT STACK ;定义一个堆栈段,段名为 STACKSEG BUF DW 50 DUP(?) STACKSEG ENDS CODE SEGMENT ;定义一个段,段名为 CODE ASSUME CS: CODE,DS: DATA1,ES: EXTRA,SS: STACKSEG ;指定 CODE 为代码段,DATA1 为数据段,EXTRA 为附加段,STACKSEG 为堆栈段 START: MOV AX, DATA1 MOV DS, AX ;将数据段段基址送入 DS MOV AX, EXTRA MOV ES, AX ;将附加段段基址送入 ES … CODE ENDS END START. 7.过程定义伪指令 对于程序中经常用到的具有独立功能的语句组,可将它定义成一个子过程,通过 CALL 来调用执行,可以简化主程序,实现模块化程序设计,提高编程效率。 (1)过程定义的格式: 过程名 PROC [属性] … ;语句序列 RET 过程名 ENDP. 说明: ① 过程名由用户自己命名,但必须符合标识符命名规则,前后过程名必须保持一致。过 程名代表过程的入口地址。 ② PROC 指定过程的开始,ENDP 指定过程的结束,PROC 和 ENDP 必须成对出现。 ③ 属性:过程属性有 NEAR(段内近调用)、FAR(段间远调用)两种,若缺省则为 NEAR。.

(12) 第5章. 汇编语言程序设计 139. NEAR 属性的过程只能被本代码段内的其他程序调用;FAR 属性的过程既可以被本代码段内 的程序调用,又可以被其他代码段内的程序调用。 ④ 过程必须以 RET 结尾,以便返回调用它的程序。 ⑤ 子过程应安排在代码段的主程序之外,最好放在主程序执行终止后的位置(返回 DOS 后、汇编结束 END 伪指令前),也可以放在主程序开始执行之前的位置。 (2)过程调用格式:CALL 过程名 例 5-9 CODE SEGMENT ASSUME CS:CODE BEGIN: … CALL SUB … SUB PROC NEAR … RET SUB ENDP CODE ENDS END BEGIN. ;定义代码段 CODE. ;调用过程 SUB ;定义过程 SUB,其属性为 NEAR ;返回主程序 ;过程 SUB 定义结束 ;代码段 CODE 定义结束. 主程序与子过程位于同一个代码段时称为段内近调用,主程序执行到 CALL 指令时,只 需将下一条指令的偏移地址 IP 压入堆栈,然后转到以 SUB 为偏移地址(只需修改 IP 的值) 的过程去执行,过程执行到 RET 指令时,从堆栈弹出一个字送入 IP,这样就返回到主程序中, 去执行主程序 CALL 后的指令。 例 5-10 CODE1. SEGMENT … CALL SUB … SUB PROC FAR … RET SUB ENDP CODE1 ENDS … CODE2 SEGMENT … CALL SUB … CODE2 ENDS …. 主程序与子过程不在同一个代码段时称为段间远调用,主程序执行到 CALL 指令时,将 下一条指令的段基址 CS 和偏移地址 IP 都压入堆栈(CS 先入栈),然后转到以 SUB 为入口地 址(既要修改 CS 的值,又要修改 IP 的值)的过程去执行,过程执行到 RET 指令时,从堆栈 弹出两个字分别送入 IP、CS,这样就返回到主程序中,去执行主程序 CALL 后的指令。 例 5-10 中,在代码段 CODE2 中,调用代码段 CODE1 里定义的子过程 SUB 就属于这种 情况;但是,在代码段 CODE1 中,调用子过程 SUB 时,虽然属于段内近调用,但 SUB 属性.

(13) 140. 微机原理与接口技术. 为 FAR,仍然要当作段间远调用处理,即调用时,CS、IP 都要入栈,返回时,也要分别弹出 IP、CS 的值,才能正确地返回主程序。 8.全局标识符伪指令 开发较复杂的大型应用程序时,通常把程序分解成多个功能独立的模块,分别编写子程 序来实现各个模块的功能,对各个子程序单独进行汇编产生相应的目标模块(OBJ 文件),最 后再用连接程序把它们连接成一个完整的可执行程序,称之为模块化程序设计方法。 采用模块化程序设计,各模块之间会存在着数据的交流,即在一个模块中需要引用在另 一个模块中定义的变量、标号或过程。 模块中的标识符有两种:①仅供本模块使用的标识符,称为局部标识符;②既可供本模 块使用,又可供另外的模块使用的标识符,称为全局标识符。 (1)全局标识符定义伪指令。要想让其他模块能调用本模块中的标识符,就需要在本模 块中将该标识符定义为全局标识符,其格式为: PUBLIC 标识符 1,标识符 2,… (2)全局标识符声明伪指令。要想在本模块中调用其他模块里的全局标识符,需要用 EXTRN 进行声明,其格式为: EXTRN. 标识符 1:类型,标识符 2:类型,…. 其中,类型可为 BYTE、WORD、DWORD、NEAR 或 FAR。 例 5-11 ;模块 1 , 文件 1.ASM EXTRN SUB2:FAR PUBLIC DATA1,RESULT DSEG SEGMENT DATA1 DB 3 DUP(1) RESULT DB ? DSEG ENDS CODE SEGMENT ASSUME CS:CODE,DS:DSEG START: MOV AX, DSEG MOV DS, AX CALL SUB2 ADD RESULT, '0' MOV DL,RESULT MOV AH,2 INT 21H MOV AH,4CH INT 21H CODE ENDS END START ;模块 2,文件 2.ASM EXTRN DATA1:BYTE, RESULT:BYTE PUBLIC SUB2 DSEG2 SEGMENT DATA2 DB 3 DUP(2) DSEG2 ENDS CODE2 SEGMENT ASSUME CS:CODE2,ES:DSEG2 SUB2 PROC FAR. ;声明全局远过程 SUB2 ;定义全局变量 DATA1、RESULT. ;初始化 DS ;调用远过程 SUB2 ;将结果转换成字符显示. ;程序结束,返回 DOS. ;声明全局变量字节变量 DATA1、RESULT ;定义全局远过程 SUB2. ;定义过程 SUB2.

(14) 第5章 MOV MOV MOV MOV MOV LOOP1: MOV MOV ADD ADD INC LOOP MOV RET SUB2 ENDP CODE2 ENDS END. AX, DSEG2 ES, AX SI, 0 CX, 3 BL, 0 AH, DATA1[SI] AL, DATA2[SI] AH, AL BL, AH SI LOOP1 RESULT,BL. 汇编语言程序设计 141. ;初始化 ES ;循环次数 ;存放累加和,初始化值为 0 ;循环累加,结果存入 RESULT. 程序功能为:ESULT=(DATA1[0]+DATA2[0])+(DATA1[1]+DATA2[1])+(DATA1[2]+DATA2[2])。 编译,连接过程如下: E:\MASM5>MASM 1.ASM. ;编译模块 1. MICROSOFT (R) MACRO ASSEMBLER VERSION 5.00 COPYRIGHT (C) MICROSOFT CORP 1981-1985, 1987. ALL RIGHTS RESERVED. OBJECT FILENAME [1.OBJ]: SOURCE LISTING [NUL.LST]: CROSS-REFERENCE [NUL.CRF]: 50908 + 415700 BYTES SYMBOL SPACE FREE 0 WARNING ERRORS 0 SEVERE ERRORS E:\MASM5>MASM 2.ASM. ;编译模块 2. MICROSOFT (R) MACRO ASSEMBLER VERSION 5.00 COPYRIGHT (C) MICROSOFT CORP 1981-1985, 1987. ALL RIGHTS RESERVED. OBJECT FILENAME [2.OBJ]: SOURCE LISTING [NUL.LST]: CROSS-REFERENCE [NUL.CRF]: 50794 + 415814 BYTES SYMBOL SPACE FREE 0 WARNING ERRORS 0 SEVERE ERRORS E:\MASM5>LINK 1 2. ;链接模块 1、模块 2,模块 1 为主模块. MICROSOFT (R) OVERLAY LINKER VERSION 3.60 COPYRIGHT (C) MICROSOFT CORP 1983-1987. ALL RIGHTS RESERVED. RUN FILE [1.EXE]: LIST FILE [NUL.MAP]: LIBRARIES [.LIB]: LINK : WARNING L4021: NO STACK SEGMENT E:\MASM5>1. ;运行. 9. ;输出结果. 9.程序结束伪指令 格式:END [标号] 功能:表示程序的结束,汇编程序遇到 END 时结束汇编,其后的标号为程序执行的起始.

(15) 142. 微机原理与接口技术. 地址。 5.2.4. 结构与记录. 1.结构 当程序中的数据是由多个数据成员组成时,如学生信息表中的学生数据,包含学号、姓 名、性别、年龄等多个成员数据,若用前面的方式来定义多个学生数据就比较麻烦。8086 宏 汇编提供了结构(STRUCTURES)来实现对这种数据的处理,结构就是相互关联的一组数据 的某种组合形式。使用结构数据前,需要先定义结构类型,再用定义好的结构类型去定义结构 变量,并完成结构变量的初始化。 (1)结构类型的定义。 格式:结构类型名 STRUC 成员数据变量序列 结构类型名 ENDS 对于上述学生信息数据,可定义一个结构类型 STUDENT。 例 5-12 STUDENT STRUC NO DB ? NAMEX DB 'JACK' SEX DB 'M' AGE DB ? STUDENT ENDS. 其结构类型名为 STUDENT,它包含有 4 个成员变量(又叫结构字段名) :NO、NAMEX、 SEX、AGE。定义结构类型时,结构字段变量可以指定其初始值,也可以用“?”代替。 注意:这里只是定义了一个结构类型,系统并不为它分配存储单元。 (2)结构类型变量的定义与初始化。 格式:结构变量名 结构类型名 <字段值表> 说明: ①字段值表用来给结构变量中各结构字段赋初值,其类型、顺序应与结构类型定义中的 字段保持一致,各个字段初始化值之间用逗号间隔。 ②给结构变量中各结构字段赋初值时,有一定的限制:在结构类型定义中只具有一项数 据的结构字段,可以通过字段值表来修改代替初始定义时的值;用 DUP 定义的字段或一个字 段后有多个数据的字段, 则不能修改其定义时的值,即不能通过字段值表来修改这些字段的值。 例 5-13 DATA STRUC X DB Y DB Z DW M DB N DW DATA ENDS. 10H 1,2 ? ' ZXC' 20 DUP (?). ;简单元素,可以修改 ;多重元素,不能修改 ;简单元素,可以修改 ;可用同长度的字符串修改 ;多重元素,不能修改. ③若不需要修改某些字段的值(即仍采用其定义时的值),则在字段值表中的对应位置仅 写一个逗号即可。.

(16) 第5章. 汇编语言程序设计 143. ④若所有字段的值均采用其定义时的值,不需要修改,则仅写一对尖括号即可。 例 5-14 DATA SEGMENT STUDENT STRUC NO DB ? NAMEX DB 'JACK' SEX DB 'M' AGE DB ? STUDENT ENDS X1 STUDENT <1,,,21> X2 STUDENT <2,'ANDY',,22> X3 STUDENT <3, 'ROSE', 'F',20> X4 STUDENT <,,,> DATA ENDS. ;定义结构类型. ;定义结构类型变量 X1、X2、X3、X4. (3)结构类型变量的引用。 在程序中可以直接引用结构类型变量,也可以引用结构类型变量中的某一字段,其格式 为:结构变量名.字段名。 例如,将前面定义的 STUDENT 结构类型变量 X1 中的 AGE 字段值送到 AL 中。 MOV AL,X1.AGE. 也可以写成: MOV BX,OFFSET X1 MOV AL,[BX].AGE. 例 5-15 将四个学生的学号、姓名、性别、年龄用结构的形式存入内存,并编程求所有 男生年龄之和。 DATA SEGMENT STUDENT STRUC NO DB ? NAMEX DB 'JACK' SEX DB 'M' AGE DB ? STUDENT ENDS X1 STUDENT <1, , ,21> X2 STUDENT <2,'ANDY', 'F',22> X3 STUDENT <3,'ROSE', ,20> X4 STUDENT <4,'JOHN', ,23> DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START: MOV AX, DATA MOV DS, AX MOV AX, 0 MOV CX, 4 MOV BX, OFFSET X LP1: CMP [BX].SEX, 'F' JZ LP2 MOV DH, [BX].AGE ADD AL, DH LP2: ADD BX, 7 LOOP LP1. ;定义结构类型. ;定义结构类型变量 X1、X2、X3、X4. ;AX 清零 ;设置循环次数 ;BX 指向 X1 的第一字节 ;取出结构变量的 SEX 字段,判断是否为女生 ;若为女生,则转到 LP2 ;若不为女生,则取出结构变量的 AGE 字段到 DH ;将 DH 的值累加到 AL 中 ;让 BX 指向下一个结构变量的第一字节 ;循环.

(17) 144. 微机原理与接口技术 MOV CL, 10 DIV CL. MOV ADD MOV MOV INT ADD MOV MOV INT MOV INT CODE ENDS END START. CL, AH AL, '0' DL, AL AH, 2 21H CL, '0' DL, CL AH, 2 21H AH, 4CH 21H. ;所有男生年龄之和已存放在 AX 中,AX/10 得到的商(即 ;其十位上的数)在 AL 中,余数(即其个位上的数) ;在 AH 中 ;将 AH 中的余数保存到 CL 中 ;将 AL 中的商转换成其对应的数字字符,并显示. ;将保存到 CL 中的余数转换成其对应的数字字符,并显示. ;程序结束,返回 DOS. 2.记录 一般来说,访问存储器的最小单位是字节,但在实际应用中,某些数据只需要用一个二 进制位来表示,如何按位访问这些数据呢?8086 宏汇编提供了记录(RECORD)来实现对这 类数据的处理。使用记录前,也需要先定义记录类型,再用定义好的记录类型去定义记录变量, 并完成记录变量的初始化。 (1)记录类型的定义。 格式:记录类型名 RECORD <字段名>:宽度[=表达式][,<字段名>:宽度[=表达式]…] 说明: ①记录类型可由多个字段(至少要有一个字段)组成,每个字段之间要用逗号分开。 ②字段的属性包括字段名、宽度和初值。宽度表示该字段所占的二进制位数,它必须是 一个常数,且其取值范围为 1~16,并且各字段的宽度之和应在 1~16 之间。用“表达式”来 给相应字段赋初值,且表达式的值不能超过该字段的表示范围能容纳下的正整数,若缺省初值, 则默认该字段的初值为 0。 ③如果记录的总宽度小于等于 8 位,系统只为该记录分配一个字节空间;如果记录的总 宽度大于 8 位且小于等于 16 位,则系统为该记录分配两个字节空间。 例如,定义一个表示学生某门功课成绩的记录。 SCORE RECORD NO:3,SEX:1,COURSE:2,GRADE:2. 记录类型名为 SCORE,它包含有 4 个字段:NO(占 3 位)、SEX(占 1 位) 、COURSE(占 2 位)、GRADE(占 2 位) 。 (2)记录类型变量的定义与初始化。 格式:记录变量名 记录类型名<字段值表> (3)记录变量中字段值的存放。 记录中各字段靠右对齐到字节或字的最低有效位置,即记录的最后一个字段排在所分配 空间的最低位,然后对记录中的字段依次“从右向左”分配二进制位,左边没有分完的二进制 位补 0。 例 5-16 DATA SEGMENT.

(18) 第5章 SCORE RECORD NO:3,SEX:1,COURSE:2,GRADE:2 Y1 SCORE<111B,1B,11B,11B> DATA ENDS. 汇编语言程序设计 145. ;定义记录类型 ;定义记录类型变量 Y1. 本例中数据段中的数据在内存中的存放如图 5-2 所示。 NO Y1. 1. 1. SEX 1 图 5-2. 1. COURSE 1. 1. GRADE 1. 1. 例 5-16 中内存数据的存放. (4)记录运算符。 1)WIDTH:求记录或记录字段所占的位数。 格式:WIDTH 记录类型名或记录字段名 例如,对前面已定义的记录类型SCORE: WIDTH SCORE WIDTH NO. ;表达式的值为 8 ;表达式的值为 3. 2)MASK:返回一个记录值,将指定字段各位置为 1,其他字段各位全部置为 0。 格式:MASK 记录字段名 例如,对前面已定义的记录类型SCORE: MASK NO MASK SEX MASK COURSE MASK GRADE. ;表达式的值为 11100000B ;表达式的值为 00010000B ;表达式的值为 00001100B ;表达式的值为 00000011B. 3)记录字段名。记录字段名可以作为一个操作数在程序中单独出现,它表示该字段最低 位距该记录的最低位有多少位。 例如,对前面已定义的记录类型 SCORE: MOV AL,NO MOV AH,COURSE MOV BH,GRADE. ;等价于 MOV AL,5 ;等价于 MOV AH,2 ;等价于 MOV BH,0. 例 5-17 将四个学生的学号、性别、成绩用记录的形式存入内存,并编程求所有女生成 绩之和(以十六进制形式显示)。 DATA SEGMENT SCORE RECORD NO:3,SEX:1,GRADE:4 ;定义记录类型,SEX 字段为 1 时表示男生 Y1 SCORE<00B,0B,1000B> ;定义四个记录类型变量 Y1、Y2、Y3、Y4 Y2 SCORE<010B,0B,1011B> Y3 SCORE<101B,0B,1101B> Y4 SCORE<111B,0B,0011B> DISP DB 30H,31H,32H,33H,34H,35H,36H,37H,38H,39H,41H,42H,43H,44H,45H,46H DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START: MOV AX, DATA MOV DS,AX MOV CX,4 ;设置循环次数 MOV DH,0 ;求和初值 DH 置零 MOV BX,OFFSET Y1 ;BX 指向第一个记录变量 L1: MOV AL,[BX] ;取出记录变量的值到 AL 中 TEST AL,MASK SEX ;测试记录变量 SEX 字段的值.

(19) 146. 微机原理与接口技术 JNZ L2 AND AL,MASK GRADE ADD DH,AL L2: INC BX LOOP L1 LEA SI,DISP MOV BH,0 MOV BL,DH AND BL,0F0H MOV CL,4 SHR BL,CL MOV DL,[BX][SI] MOV AH,02H INT 21H MOV BH,0 MOV BL,DH AND BL,0FH MOV DL,[BX][SI] MOV AH,02H INT 21H MOV DL,'H' MOV AH,02H INT 21H MOV AH,4CH INT 21H CODE ENDS END START. 5.2.5. ;若为男生,则不累加,转去处理下一条记录 ;若为女生,则取出记录变量 GRADE 字段的值 ;将女生成绩累加到 DH 中 ;BX 指向下一个记录 ;循环 ;将和的高四位以十六进制形式显示. ;将和的低四位以十六进制形式显示. ;显示字符 H. ;程序结束,返回 DOS. 宏指令. 前面讲到,对于程序中需要重复多次用到的具有独立功能的语句组,可将它定义成一个 子过程,通过 CALL 来调用执行。实际上,也可以把它们定义成一个宏指令,在程序中反复 调用,以达到简化主程序、提高编程效率的目的。 1.宏与子程序的区别 (1)宏与子程序的相同点:用一条指令来代替一段程序,子程序和宏指令定义好之后都 可以被多次调用,可以起到简化源程序的作用。 (2)宏与子程序的不同点: ①从代码开销的角度来讲,子程序优于宏指令。编译宏指令时,需要将每一个宏调用指 令展开,有多少次调用,就要在目标程序中插入多少次宏体程序段,因而调用次数越多,占用 内存空间就越大;编译子程序时只占用一个程序段(即使是调用多次) ,因而汇编后产生的目 标程序占用内存空间少。 ②从时间开销的角度来讲,宏指令优于子程序。每次调用子程序时都要保护/恢复现场和 断点,额外增加了时间开销;而宏指令在执行时不存在保护/恢复现场和断点的问题,执行的 时间短,速度快。 一般来说,当要重复执行的程序不长,重复次数又多时,速度是主要问题,通常用宏指 令;而要重复执行的程序较长,重复次数又不是太多时,额外操作所附加的时间就不明显了,.

(20) 第5章. 汇编语言程序设计 147. 节省内存空间应视为主要问题,通常采用子程序结构。 2.宏定义 格式:宏指令名 MACRO [形式参数1][,形式参数2……] 宏体 ;语句序列 ENDM 说明: (1)宏指令名由用户自己命名,但必须符合标识符命名规则。 (2)MACRO 指定宏定义的开始,ENDM 指定宏定义的结束,它们必须成对出现。 (3)宏体为实现宏指令功能的语句序列。 (4)形式参数列表用来给出宏定义中所用到的参数,形式参数可有一个或多个,也可以 没有,有多个形式参数时,参数之间以逗号隔开。 (5)宏定义不必在任何逻辑段中,通常写在源程序的开头。 (6)宏定义中的注释语句以“;;”开头。 例 5-18 ADDCAB MACRO ADD AX,BX MOV CX,AX ENDM PUTCHAR MACRO CHAR. ;定义宏指令 ADDCAB(没有参数),功能:CX=AX+BX. ;定义宏指令 PUTCHAR,参数为 CHAR,功能:输出参数 ;CHAR 对应的字符 PUSH AX PUSH DX MOV DL,CHAR MOV AH,2 INT 21H POP DX POP AX. ;保护寄存器 AX 和 DX 的值. ;恢复寄存器 AX 和 DX 的值. ENDM. 3.宏调用 格式:宏指令名 [实际参数1][,实际参数2…] 在程序中使用已经定义过的宏指令,称为宏调用。如果宏指令有形式参数,在宏调用时, 必须在宏指令名后面写上实际参数,并与形式参数一一对应,有多个实际参数时,参数之间以 逗号隔开。 具有宏调用的源程序被汇编时,汇编程序将用宏定义时设计的宏体去代替宏指令名,并 且用实际参数一一代替形式参数,称为宏展开。汇编程序在所展开的指令前加上“1”号以示 区别。 例 5-19 用宏指令定义两个字操作数相除,第一个操作数为被除数,第二个操作数为除 数,并将商存入第三个操作数,余数存入第四个操作数。 M_DIVIDE MACRO OPR1,OPR2,OPR3,OPR4 ;定义宏 M_DIVIDE,其功能为:OPR3=OPR1/OPR2 ;余数存放在 OPR4 中 PUSH DX PUSH AX MOV AX, OPR1.

(21) 148. 微机原理与接口技术 CWD DIV MOV MOV POP POP. OPR2 OPR3, AX OPR4,DX AX DX. ENDM DATA SEGMENT A1 DW 2424H A2 DW 1212H A3 DW ? A4 DW ? DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START: MOV AX,DATA MOV DS,AX M_DIVIDE A1,A2,A3,A4 MOV AH,4CH INT 21H CODE ENDS END START. ;调用宏 M_DIVIDE. 经宏展开后: START: 1 1 1 1 1 1 1 1 1. MOV MOV PUSH PUSH MOV CWD DIV MOV MOV POP POP MOV INT. AX,DATA DS,AX DX AX AX, A1 A2 A3, AX A4,DX AX DX AH,4CH 21H. 补充说明: (1)宏定义中的参数还可以是操作码。 宏定义: OP MACRO OPR1,OPR2,OPR3 MOV AX, OPR1 OPR2 AX,OPR3 ENDM. 宏调用: OP X,ADD, Y. ;假设 X、Y 为已经在数据段定义好的两个字变量. 宏展开: 1 1. MOV AX,X ADD AX,Y. (2)在宏定义中还可以使用分隔符&,展开时把&前后的两个符号连接起来,形成操作.

(22) 第5章. 汇编语言程序设计 149. 码、操作数或字符串。 宏定义: SHIFT MARCO OPR1,OPR2,OPR3 ;定义宏指令 SHIFT,用来将 OPR1 逻辑移位 OPR2 次,OPR3 指定是左移还是右移 MOV CL,OPR2 SH&OPR3 OPR1,CL ENDM. 宏调用: SHIFT AL,4,L. 宏展开: 1 MOV CL,4 1 SHL AL,CL. (3)在宏定义中可以调用之前已经定义过的宏。 例 5-20 定义一个宏指令求两个数相除的商。 PUSHDA MACRO PUSH DX PUSH AX ENDM POPDA MACRO POP AX POP DX ENDM M_DIVIDE MACRO OPR1,OPR2,OPR3,OPR4 PUSHDA MOV AX, OPR1 CWD DIV OPR2 MOV OPR3,AX MOV OPR4,DX POPDA ENDM. ;定义宏指令 PUSHDA. ;定义宏指令 POPDA. ;定义宏指令 DIVIDE ;调用宏指令 PUSHDA. ;调用宏指令 POPDA. 宏调用: M_DIVIDE. A1,A2,A3,A4. ;假设 A1、A2、A3、A4 为已经在数据段定义好的字变量. 宏展开: 1 1 1 1 1 1 1 1 1. PUSH PUSH MOV CWD DIV MOV MOV POP POP. DX AX AX, A1 A2 A3, AX A4,DX AX DX. 4.局部标号伪指令 LOCAL 对于使用了标号的宏,若多次调用,展开时将多次出现相同的标号,这在汇编语言程序 中是不允许的,汇编时将报错。8086 宏汇编提供了局部标号伪指令 LOCAL 来解决这一问题。.

(23) 150. 微机原理与接口技术. 格式:LOCAL 标号 1[,标号 2…] 说明: (1)标号 1、标号 2……为宏定义中的标号。 (2)LOCAL 伪指令只能用在宏定义体内,还必须是 MACRO 伪操作后的第一个语句, 且在 MACRO 与 LOCAL 之间不能出现注释和分号标志。 (3)对 LOCAL 后的标号,汇编程序将用“??0000”……“??FFFF”来依次取代宏展开 时的标号。这样,在宏展开后,程序中标号都是唯一的。 例 5-21 宏定义: ABS MACRO OPRW LOCAL L CMP OPRW,0 JGE L NEG OPRW L: MOV AX,OPRW ENDM. ;定义宏指令 ABS 求字数据 OPRW 的绝对值. 宏调用: ABS X ABS Y. ;假设 X、Y 为已经在数据段定义好的两个字变量. 宏展开: 1 CMP 1 JGE 1 NEG 1 ??0000: MOV 1 CMP 1 JGE 1 NEG 1 ??0001: MOV. X,0 ??0000 X AX,X Y,0 ??0001 Y AX,Y. 5.文件包含伪指令 INCLUDE 当多个程序要调用同一个宏时,可以把这些宏组合起来,建立一个独立的文件,称为宏 库,其扩展名是 MAC 或 INC。当需要调用宏库中的宏时,只需要在该程序的开始用 INCLUDE 伪指令把该宏库文件包含进来即可。 格式:INCLUDE 宏库文件名 汇编时,将用 INCLUDE 伪指令指定的文件的内容插入到该伪指令所在的位置,与源程序 一起进行汇编,所以要注意宏库文件中的标识符不能与源程序中的标识符重名。 例如: INCLUDE OUTPUT.MAC INCLUDE D:\MASM5\INPUT.MAC. 6.重复汇编伪指令 (1)REPT。 格式:REPEAT 重复次数 重复体 ENDM 说明:使汇编程序按照指定次数对重复体进行重复汇编。 例 5-22 CHAR = 0.

(24) 第5章. 汇编语言程序设计 151. REPEAT 10 DB CHAR CHAR = CHAR +1 ENDM. 展开: 1 1 1 1 1 1. DB CHAR CHAR = CHAR +1 DB CHAR CHAR = CHAR +1 … DB CHAR CHAR = CHAR +1. ;等价于 DB 0 ;等价于 DB 1. ;等价于 DB 9. (2)IRP。 格式:IRP 形式参数,<实际参数表> 重复体 ENDM 说明:重复汇编时,每作一次汇编就依次将实参表中的一个实参取代重复体中的形参。 例 5-23 IRR REG,<AX,BX,CX,DX> POP REG ENDM. 展开: 1 1 1 1. POP POP POP POP. AX BX CX DX. (3)IRPC。 格式:IRPC 形参,字符串 重复体 ENDM 说明:重复汇编时,每作一次汇编就依次用字符串中的一个字符取代重复体中的形参。 例如: IRPC. CHAR,ABCD PUSH CHAR&X. ENDM. 展开: 1 1 1 1. PUSH AX PUSH BX PUSH CX PUSH DX. 7.条件汇编伪指令 利用条件汇编伪指令可以有选择地汇编某段源程序。 格式: IFXX 表达式 ; 定义条件 语句组 1 ; 满足条件时编译语句组 1 [ ELSE ; ELSE 部分也可以省略 语句组 2 ] ; 不满足条件时编译语句组 2.

(25) 152. 微机原理与接口技术. ENDIF 常用条件汇编伪指令如表 5-4 所示。 表 5-4 常用条件汇编伪指令 条件汇编伪指令. 成立的条件. IF 表达式. 表达式的值不为 0. IFE 表达式. 表达式的值为 0. IFDEF 符号. 符号已定义. IFIDN <串 1>,<串 2>. <串 1>=<串 2>(区分大小写). IFIDNI <串 1>,<串 2>. <串 1>=<串 2>(不区分大小写). 例 5-24. 利用条件汇编定义宏指令,完成多种 DOS 系统功能调用。. DOSYS. MACRO IFE EXITM. ENDIF IFDEF. BUF LEA MOV INT. N,BUF N. DX,BUF AH,N 21H. ELSE MOV INT ENDIF ENDM DATA. DATA STACK STACK CODE BEGIN:. CODE END. SEGMENT MSG BUF ENDS SEGMENT DB ENDS SEGMENT ASSUME MOV MOV DOSYS DOSYS DOSYS ENDS BEGIN. AH,N 21H. DB DB. 'INPUT STRING:$' 81,0,80 DUP(0). STACK 200 DUP(0). DS:DATA,CS:CODE,SS:STACK AX,DATA DS,AX 9,MSG 10,BUF 4CH. 以上三条宏指令展开后的语句为: 1 1 1 1 1 1 1. LEA MOV INT LEA MOV INT MOV. DX,MSG AH,9 21H DX,BUF AH,10 21H AH,4CH.

(26) 第5章 1. INT. 5.2.6. 汇编语言程序设计 153. 21H. 简化段定义. 实际上,汇编语言源程序可以用两种格式书写:一种是前面介绍的完整段定义格式,另 一种是简化段定义格式。 例 5-25 .MODEL SMALL ;定义程序的存储模式 .STACK 20H .DATA ;定义数据段 STRING DB 'HELLO, ASSEMBLY',0DH,0AH, '$' .CODE ;定义代码段 .STARTUP ;程序起始点,建立 DS、SS MOV DX,OFFSET STRING ;指定字符串 MOV AH,9 INT 21H ;利用功能调用显示信息 .EXIT 0 ;程序结束点,返回 DOS .END ;汇编结束. 1.存储模式伪指令 MODEL 用来表示存储模式,说明在存储器中代码段、数据段等是如何存放的,段的大小有什么限制, 数据、代码寻址是近属性还是远属性。存储模式伪指令语句必须位于所有段定义语句之前。 格式:.MODEL 存储模型 常用的存储模型有: (1)TINY:编译时,所有段地址寄存器都被设置为同一值,即代码段、数据段、堆栈段 都在同一个段内。段的大小不超过 64KB,指令转移、程序调用、数据访问等都是近属性 (NEAR)。TINY 模式是 MASM 6.0 才引入的,用于创建 COM 类型程序(其他模式产生 EXE 程序),COM 程序必须从段内偏移地址为 0100H 的存储单元开始。一般用于小程序。 (2)SMALL:最多只有两个段:一个代码段,一个数据段,并且两者是独立的,即两个 段基址不同。两个段的大小都不超过 64KB,指令转移、程序调用、数据访问等都是近属性 (NEAR),此模式下程序的最大长度为 128KB。如果还有堆栈段和附加段,则数据段、堆栈 段、附加段共用同一个段基址,段长度仍不超过 64KB。是一般应用程序常用的模型。 (3)MEDIUM:可以有多个代码段,但只有一个数据段,并且段的大小都不超过 64KB。 所以数据访问是近属性(NEAR),而指令转移、程序调用可以是近属性(NEAR),也可以是 远属性(FAR),缺省时为远属性(FAR)。适合于数据量小但代码量大的程序。 (4)COMPACK:可以有多个数据段,但只有一个代码段,并且段的大小都不超过 64KB。 所以指令转移、程序调用是近属性(NEAR),而数据访问可以是近属性(NEAR),也可以是 远属性(FAR),缺省时为远属性(FAR)。适合于数据量大但代码量小的程序。 (5)LARGE:允许有多个代码段和多个数据段。数据段可以超过 64KB,但静态数据(不 能改变的数据)仍限制在 64KB 之内。指令转移、程序调用、数据访问可以是近属性(NEAR), 也可以是远属性(FAR),缺省时均为远属性(FAR)。适用于较大型程序。 (6)HUGE:与 LARGE 模型相同,只是静态数据不再被限制在 64KB 之内。 (7)FLAT:允许用 32 位偏移量,只能在 80386 及其以后的计算机系统中运行,只能用 于 32 位程序,只能在 OS/2 下或其他保护模式的操作系统下使用,在 DOS 下不允许使用这种.

(27) 154. 微机原理与接口技术. 模型。MASM 6.0 可以支持这种模型,但 MASM 5.0 版本不支持。 2.段定义伪指令 (1)定义堆栈段。 .STACK [SIZE] 参数 SIZE 指定堆栈段所占存储区的字节数,缺省时为 1KB。段名为 STACK。 (2)定义数据段。 简化段定义中把数据段分得很细。首先把数据段分为常量数据段和变量数据段,变量数 据段又可分为远数据段和近数据段,然后根据变量是否初始化,进一步将其分为初始化数据段 和未初始化数据段。所以简化段定义中的数据段有以下几种: 1)CONSTANTS(常数段)。 2)INITIALIZED DATA(初始化数据段)。 3)UN INITIALIZED DATA(未初始化数据段)。 4)FAR INITIALIZED DATA(远初始化数据段)。 5)FAR UN INITIALIZED DATA(远未初始化数据段)。 所以相对应数据段定义伪指令有: 1).CONST .... ;定义常量. 用来定义只读的常量数据段,段名为 CONST。 2).DATA .... ;定义数据. 用来定义初始化数据段,段中的变量具有初值,段名为_DATA。 3).DATA? .... ;定义数据. 用来定义未初始化数据段,段中的变量没有初值,段名为_BSS。 4).FARDATA [NAME] .... ;定义数据. 用来定义远初始化数据段,段中的变量有初值,默认段名为 FAR_DATA。 5).FARDATA? [NAME] .... ;定义数据. 用来定义远未初始化数据段,段中的变量没有初值,默认段名为 FAR_BSS。 (3)定义代码段。 .CODE [段名] 用于定义代码段,参数 NAME 用来指定代码段的段名。缺省时则采用默认段名。在 TINY、 SMALL、COMPACT 和 FLAT 模式下,默认的代码段名是_TEXT。在 MEDIUM、LARGE 和 HUGE 模式下,默认的代码段名是模块名_TEXT。 说明: ①采用简化段定义伪指令前,需要有.MODEL 语句。 ②段定义伪指令指明一个逻辑段的开始,同时自动结束前面的一个段,所以不必用 ENDS 作为段的结束符。.

(28) 第5章. 汇编语言程序设计 155. ③在简化段定义中,可以使用汇编程序提供的预定义符号,预定义符号如表 5-5 所示。 表 5-5 预定义符号表 符号. 说明. 符号. 说明. @CODE. 返回代码段段值. @FARDATA. 返回 FAR DATA 定义的段值. @CODESIZE. 返回代码段长度值. @FARDATA?. 返回 FAR DATA?定义的段值. @CURSEG. 返回当前段段值. @MODEL. 返回选择的内存模式. @DATA. 返回数据段段值. @STACK. 返回堆栈段段值. @DATASIZE. 返回数据段长度值. @WORDSIZE. 返回当前段类型属性. 例如: MOV AX,@DATA MOV DS,AX. 3..STARTUP 指定程序开始执行的起始点,(在 DOS 下)用于自动初始化寄存器 DS、SS 和 SP。 它等价于: MOV AX,@DATA MOV DS,AX. 4..EXIT [返回参数] 用于终止程序执行,返回操作系统。它的参数是一个返回的数码,用 0 表示没有错误。 它等价于: MOV AH,4CH INT 21H. 5..END [标号] 指示汇编程序到此结束汇编,标号用于指定程序开始执行点,连接程序将据此设置 CS:IP 值。若采用.STARTUP 指明了程序开始执行点,则可以省略标号。 注意:MASM 5.0/5.1 不支持.STARTUP、.EXIT 0 和.END。. 5.3 汇编语言程序设计基本方法 5.3.1. 程序设计的基本步骤. (1)分析问题。 对题目给出的已知条件和要完成的任务进行详细的了解和分析,将实际问题转化为计算 机可以处理的问题。 (2)确定算法。 算法,即利用计算机解决问题的方法和步骤。计算机一般只能进行最基本的算术运算和 逻辑运算,要完成较为复杂的运算和控制操作,就必须选择合适的算法。 (3)设计流程。 将算法以流程图的方式画出来。 画流程图是指用各种图形、符号、指向线等来说明程序设计的过程。国际通用的图形和 符号说明如下:.

(29) 156. 微机原理与接口技术. 1)椭圆框:起止框,在程序的开始和结束时使用,如图 5-3(a)所示。 2)矩形框:处理框,表示要进行的各种操作,如图 5-3(b)所示。 3)菱形框:判断框,表示条件判断,以决定程序的流向,如图 5-3(c)所示。 4)指向线:流程线,表示程序执行的流向,如图 5-3(d)所示。. (a)起止框. (b)处理框 图 5-3. (c)判断框. (d)流程线. 国际通用的流程图图形. (4)分配空间。 合理分配存储空间,即分段和数据定义,合理地使用寄存器。 (5)编写程序。 根据前面确定的算法流程图,采用汇编程序设计语言编写程序。 (6)调试运行。 程序编写好以后,检查语法错误,上机汇编、连接、调试运行,检验程序是否正确,能 否实现预期功能。 5.3.2. 顺序、分支与循环程序设计. 利用计算机解决实际问题时,其操作控制执行步骤有时是按顺序执行的,有时需要根据 实际情况选择某一个分支的操作执行,有时需要对某一些操作步骤反复执行,与之相对应,就 有 3 种程序结构:顺序结构、分支结构、循环结构。 1.顺序结构 顺序结构程序完全按指令书写的前后顺序,从头至尾逐条执行,是最常用、最基本的程 序结构。常用于处理查表程序、计算表达式程序。 a*b + c 例 5-26 编写程序计算表达式: f = 。 d−e DATA SEGMENT A DB 5 B DB 10 C DB 15 D DB 30 E DB 20 F DB ? DATA ENDS CODE SEGMENT ASSUME CS: CODE, DS: DATA START: MOV AX,DATA MOV DS,AX MOV AX,0 MOV AL,A MUL B MOV BL,C MOV BH,0 ADD AX,BX MOV CL, D SUB CL, E. ;初始化 DS. ;AX=A*B. ;AX=A*B+C ;CL=D-E.

(30) 第5章 DIV CL MOV F,AL MOV AH,4CH INT 21H CODE ENDS END START. 汇编语言程序设计 157. ;AL=AX/CL=(A*B+C)/(D-E) ;F=AL=(A*B+C)/(D-E) ;程序结束,返回 DOS. 2.分支结构 根据指定的条件选择程序执行的方向,这种程序结构称为分支程序结构。常根据 CMP、 TEST 等指令执行后形成的状态标志,通过转移指令 JXX 判断标志位的变化,来实现条件判断 控制程序转向某个分支执行;或通过 JMP 实现无条件转移。 根据分支转向的不同结构,可将分支结构分为 3 种:单分支结构、双分支结构和多分支 结构。 (1)单分支程序。满足条件时转向分支执行,否则顺序执行。流程图如图 5-4(a)所示。 例 5-27 求数的绝对值。 DATA SEGMENT X DB -25 X_ABS DB ? DATA ENDS CODE SEGMENT ASSUME DS:DATA,CS:CODE START: MOV AX,DATA MOV DS,AX MOV AL,X CMP JGE NEG NEXT: MOV MOV INT CODE ENDS END START. AL,0 NEXT AL X_ABS,AL AH,4CH 21H. ;初始化 ;X 取到 AL 中 ;测试 AL 正负 ;X≥0,转 NEXT ;否则 X<0,AL 求补 ;送结果 ;返回 DOS. (2)双分支程序。条件成立转向分支语句体 2 执行,否则顺序执行分支语句体 1,并且 执行完分支语句体 1 后要跳过分支语句体 2,用 JMP 无条件跳转到分支语句体 2 后执行。流 程图如图 5-4(b)所示。 例 5-28 奇偶数判断,是奇数时输出 N,是偶数时输出 Y。 DATA. SEGMENT X DB -30 DATA ENDS CODE SEGMENT ASSUME DS:DATA,CS:CODE START: MOV AX,DATA MOV DS,AX MOV AL,X SHR AL,1 JC NEXT MOV DL,'Y' JMP DISP NEXT: MOV DL,'N' DISP: MOV AH,02H INT 21H. ;初始化 ;将 X 最低位右移至 CF 中 ;CF 为 1,则为奇数,转 NEXT ;否则 CF 为 0,则为偶数,输出字符'Y' ;为奇数时输出字符'N' ;显示 DL 中的字符.

(31) 158. 微机原理与接口技术 MOV AH,4CH INT 21H CODE ENDS END START. ;返回 DOS. JXX 条件满足吗? Y N. JXX 条件满足吗?. Y. 分支语句体 1. N. JMP. 语句体. 分支语句体 2. 分支语句体. (a)单分支结构. (b)双分支结构 图 5-4. 单、双分支结构流程图. (3)多分支程序。需要对多个条件进行判断,每个条件都对应一个分支,满足某个条件 时就进入相对应的分支执行。流程图如图 5-5 所示。. 条件判断. 满足条件 1 分支语句体 1. 满足条件 2 分支语句体 2. 图 5-5. 满足条件 N ……. 分支语句体 N. 多分支结构流程图. 对于多分支程序结构,可以采用以下两种解决方法: 1)逻辑分解方法。将多分支结构以逻辑等效的方法分解为一串双分支结构。 (X>0) 1 (X=0) 0 例 5-29 求符号函数 Y 的值: Y= DATA SEGMENT X DB -10 Y DB ? DATA ENDS CODE SEGMENT ASSUME DS:DATA,CS:CODE. -1. (X<0).

(32) 第5章 START:. L1: L0: EXIT: CODE END. MOV MOV CMP JG JE MOV JMP MOV JMP MOV MOV INT ENDS START. AX,DATA DS,AX X,0 L1 L0 Y,-1 EXIT Y,1 EXIT Y,0 AH,4CH 21H. 汇编语言程序设计 159. ;初始化 ;比较 X 与 0 的大小 ;X>0,转 L1 ;X=0,转 L0 ;否则 X<0,Y=-1 ;Y=1 ;Y=0 ;返回 DOS. 2)地址表方法。在数据段定义一个地址表,依次存放各分支语句体的入口地址,用寄存 器间接寻址或寄存器相对寻址方式产生转移目标地址,实现转移。 分支入口地址=地址表首地址+偏移地址 例 5-30 假设一个程序有 5 个分支,根据用户输入的数字(0~4)转入相应的分支去执 行,试编写程序。 DATA. SEGMENT FUN DW FUN0,FUN1,FUN2,FUN3,FUN4 ;将标号 FUN0、FUN1、FUN2、FUN3、FUN4 偏移地址存入变量 FUN 中 DATA ENDS CODE SEGMENT ASSUME DS:DATA,CS:CODE START: MOV AX,DATA MOV DS,AX MOV AH,01H ;选择输入数字字符 0、1、2、3、4 中的一个 INT 21H SUB AL,'0' ;将输入的数字字符转化成对应的数,存入 AX 中 MOV AH,0 SHL AX,1 ;AX=AX*2,各标号偏移地址占两个字节 LEA BX,FUN ADD BX,AX ;BX=地址表 FUN 首地址+2*N(输入的数字) JMP [BX] ;根据输入的数字,形成分支语句体入口地址,转移到相应的 ;分支语句体去执行,采用的是寄存器间接寻址方式 FUN0: … ;分支语句体 0 JMP EXIT ;结束 FUN1: … ;分支语句体 1 JMP EXIT ;结束 FUN2: … JMP EXIT. ;分支语句体 2 ;结束. … JMP EXIT. ;分支语句体 3 ;结束. … MOV AH,4CH. ;分支语句体 4 ;返回 DOS. FUN3:. FUN4: EXIT:.

(33) 160. 微机原理与接口技术. CODE END. INT 21H ENDS START. (4)说明: ①对于既能用双分支结构,又能用单分支结构实现的程序,宜采用单分支结构,以减少 转移次数,程序结构简单。 ②对于多分支结构程序,宜采用地址表法,以减少转移次数,程序结构简单。 ③对分支结构程序进行测试时,应对每一个分支都进行检测,才能保证整个程序的正 确性。 3.循环结构 根据某一条件是否成立判断是否需要重复执行某个语句组,这种程序结构称为循环结构。 (1)说明: ①一个循环结构一般由循环条件控制、循环体两部分组成。 循环条件控制:对循环条件进行判断,决定是否继续循环。 循环体:重复执行的语句组。 注意:循环体中应对循环条件的值进行修改,否则将会成为死循环(循环无限次) 。 ②根据循环条件控制所在的位置,可将循环结构分为两种: z “先判断、后循环”:先判断循环条件,再决定是否执行循环体。 z “先循环、后判断”:先执行循环体(至少一次) ,再判断循环条件。 ③用的循环指令有: z 循环指令:LOOP、LOOPE/LOOPZ、LOOPNE/LOOPNZ。 z 转移指令:JCXZ、JXX。 其中 LOOP 、 JCXZ 常用于循环次数固定的循环结构,称这种循环结构为计数循环; LOOPE/LOOPZ、LOOPNE/LOOPNZ、JXX 常用于循环次数不定的循环结构,称这种循环结构 为条件控制循环。 (2)应用举例。 1)计数循环:循环次数已知,用计数器 CX 计数来控制循环次数,要求在循环之前设置 CX 的值,即将循环次数送入 CX 中,然后每循环一次计数器值减 1,直至其值减为 0 则不再循环。 例 5-31 设计一个程序,求 1+2+ " +99+100 的和,结果保存在变量 RESULT 中。 DATA SEGMENT RESULT DW 0 DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START: MOV AX,DATA MOV DS,AX MOV AX,1 MOV CX,100 L0: ADD RESULT,AX INC AX LOOP L0 MOV AH,4CH INT 21H CODE ENDS.

(34) 第5章 END. 汇编语言程序设计 161. START. 2)条件控制循环:循环次数未知,需要设置一个循环条件,每次对条件进行判断来确定 是否继续转去执行循环体。 例5-32 计算数组ARRAY中元素的平均值、最大值、最小值,数组以-1为结束标志。 分析:数组元素个数不确定,因而循环次数也不确定,需要通过判断数组元素是否为-1 来确定是否结束循环。这里需要取出数组的每个元素累加求和,并统计数组元素个数,以计算 出平均值;求最大值(或最小值)时,可以先默认数组第一个元素即为最大值(或最小值), 送入 MAX(或 MIN)中,然后依次取出数组剩下的元素与 MAX(或 MIN)比较,若该元素 的值大于 MAX(或小于 MIN),则将它送入 MAX(或 MIN)中,这样循环结束后,MAX(或 MIN)里面存放的就是数组的最大值(或最小值)。 DATA SEGMENT ARRAY DB 10,58,63,94,85,32,-1 SUM DW 0 COUNT DB 0 AVERAGE DB ? MAX DB ? MIN DB ? DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START: MOV AX,DATA MOV DS,AX LEA SI,ARRAY MOV BL,[SI] MOV MAX,BL MOV MIN,BL L0: MOV AL,[SI] CMP AL,-1 JZ L3 CBW ADD SUM,AX INC COUNT CMP MAX,AL JGE L1 MOV MAX,AL L1: CMP MIN,AL JLE L2 MOV MIN,AL L2: INC SI JMP L0 L3: MOV AX,SUM DIV COUNT MOV AVERAGE,AL. CODE END. MOV AH,4CH INT 21H ENDS START. ;取出第一个元素送入 MAX、MIN 中. ;判断数组元素是否为-1,若是则结束循环 ;若数组元素不为-1,则加入 SUM 中 ;统计元素个数,个数值加 1. ;若数组元素值大于 MAX,则将它送入 MAX 中. ;若数组元素值小于 MIN,则将它送入 MIN 中 ;SI 指向数组的下一个元素 ;循环处理数组的下一个元素. ;将数组元素的和 SUM 除以数组元素个数 COUNT,得到商 ;即为平均值,送入 AVERAGE ;返回 DOS. 3)循环嵌套。 实际应用中,经常出现在一个循环中又包含另一个循环,称为循环嵌套,也称多重循环。.

(35) 162. 微机原理与接口技术. 例 5-33 数组 ARRAY 的长度为 N(即数组 ARRAY 中共有 N 个数,假设均为无符号字 节数),请将数组中的数按升序(从小到大)排序。 分析:排序算法有很多种,这里采用冒泡排序算法,其算法思想为: ①第 1 趟:从数组的最左边开始,依次将相邻两个数作比较,若前者大于后者,则交换 两者的值,经 N-1 次两两相邻比较后,最大的数已交换到最后一个位置。 ②第 2 趟:对前 N-1 个数,按上法两两相邻比较,经 N-2 次比较后得到次大的数,安置 在第 N-1 个元素的位置。 ③重复上述过程,经过 N-1 趟冒泡排序后,数据呈升序排列。 DATA SEGMENT ARRAY DB 10,58,23,94,85,32,70,5,42,62 N EQU $-ARRAY ;N 为数组长度 DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START: MOV AX,DATA MOV DS,AX MOV CX,N-1 ;设置外层循环次数为 N-1,即冒泡排序趟数 L0: PUSH CX ;保存外层循环次数计数器 LEA SI,ARRAY L1: MOV AL,[SI] MOV AH,[SI+1] CMP AL,AH ;相邻两个数作比较 JLE L2 ;若前者小于等于后者,则不交换,转去比较下一对相邻 ;的两个数 MOV [SI+1],AL ;否则(即前者大于后者),交换两者的值 MOV [SI],AH L2: INC SI LOOP L1 ;内存循环两两比较,循环次数刚好等于外层循环次数计数器 ;的值,所以不需要另外设置内层循环计数器 CX 的值 POP CX ;恢复外层循环次数计数器 LOOP L0 ;继续外层循环 MOV AH,4CH ;返回 DOS INT 21H CODE ENDS END START. 5.3.3. 子程序设计. 通常将一个大的程序按照功能划分为几个子程序(子程序就是一个功能上相对独立的程 序段,可以被多次重复调用。在一个完整的程序中,可以有多个子程序,子程序能被别的程序 所调用,也可以调用其他子程序,也称过程),通过调用各个子程序来实现程序的功能。 在定义子程序时,一般需要包含以下几个部分: z 保护现场 z 子程序体 z 恢复现场 z 子程序返回 调用子程序时,子程序与主程序之间往往存在着数据的交流,称主程序传递给子程序的.

(36) 第5章. 汇编语言程序设计 163. 数据为入口参数,称子程序返回给主程序的结果数据为出口参数。 常采用的参数传递方法有:通过寄存器传递、通过共享变量传递、通过堆栈传递。 (1)通过寄存器传递参数。 把入口参数、出口参数存放于约定的寄存器中,这是最常用的参数传递方式。通过寄存 器传递参数时,需要视具体情况来选择是否需要对入口参数、出口参数进行保护和恢复。由于 通用寄存器个数有限,通过寄存器传递参数的方法只适合参数个数较少的场合。 例 5-34 编写一个子程序,在数据块中查找某个指定数据,若找到则把该数据在数据块 中的序号返回,若找不到则返回-1。 DATA SEGMENT ARRAY DB 10,58,23,94,85,32,70,5,42,62 N EQU $-ARRAY ;N 为数组长度 DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START: MOV AX,DATA MOV DS,AX LEA SI,ARRAY MOV CX,N MOV DL,94 CALL LOOKUP ;调用子程序 MOV AH,4CH ;返回 DOS INT 21H ;子程序名:LOOKUP ;功能:片内 RAM 中的数据检索 ;入口参数:SI 存放数据块首地址,CX 存放数据块长度,DL 存放要查找的数据 ;出口参数:若找到,则将数据的序号存入 DI,否则存-1 到 DI LOOKUP PROC PUSH CX PUSH SI MOV DI ,-1 L0: CMP DL,[SI] JZ L1 INC SI LOOP L0 JMP L2 L1: MOV DI,SI SUB DI,OFFSET ARRAY L2: POP SI POP CX RET LOOKUP ENDP CODE ENDS END START. (2)通过共享变量传递参数。把入口参数、出口参数存放于约定的内存共享变量中。若 子程序和调用程序在同一程序模块中,则子程序可直接访问模块中的变量,进行参数传递;若 子程序和调用程序在两个不同的程序模块中,需要利用 PUBLIC、EXTREN 对共享变量进行声 明才能访问共享变量。.

(37) 164. 微机原理与接口技术. 若调用程序还要引用共享变量原来的值,则需要对共享变量进行保护和恢复。 通过共享变量传递参数的方法适合于传递参数较多的情况,以及在多个程序段间传递参 数的情况。但是采用这种参数传递方式的子程序的通用性比较差。 例 5-35 编写一个子程序,从键盘输入若干字符,以“$”结束,并将输入的字符存入数 组 STRING 中。要求:若输入的字符为大写,则需要将其改为小写后存入数组。 DATA SEGMENT STRING DB 100 DUP(?) DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START: MOV AX,DATA MOV DS,AX MOV SI,OFFSET STRING CALL STOSTR ;调用子程序 MOV AH,4CH ;返回 DOS INT 21H ;子程序名:STOSTR ;功能:输入字符串,并将其中的大写字母改为小写字母,然后存入数组 STRING ;入口参数:SI 存放数组 STRING 的首址 ;出口参数:数组 STRING STOSTR PROC PUSH AX AGAIN: MOV AH,1 INT 21H CMP AL,'$' JZ OVER ;结束 CMP AL,'A' JL NEXT ;不是大写 CMP AL,'Z' JG NEXT ;不是大写 ADD AL,32 ;是大写 NEXT: MOV [SI],AL ;存入 INC SI JMP AGAIN OVER: POP AX RET STOSTR ENDP CODE ENDS END START. (3)通过堆栈传递参数。把入口参数、出口参数存放于堆栈当中。在调用子程序前,主 程序将入口参数压入堆栈,子程序从堆栈中取出入口参数;在子程序返回前,子程序将出口参 数压入堆栈,主程序从堆栈中取到出口参数。 采用堆栈传递参数方法是编译程序处理参数传递,以及汇编语言与高级语言混合编程时 的常规方法。通过堆栈传递参数的方法适合于传递参数较多的情况,采用堆栈传递参数时要: 保证子程序中堆栈操作的正确性,对堆栈的压入和弹出操作要成对使用,保持堆栈的平衡,避 免因堆栈操作而造成子程序不能正确返回的错误。 例 5-36 编写程序,求数据块 BUF 中存放的若干个无符号字节数据的平均值。 DATA SEGMENT.

(38) 第5章 BUF DW 10,58,23,94,85,32,70,5,32,62 N EQU ($-BUF)/2 DATA ENDS CODE SEGMENT ASSUME CS:CODE,DS:DATA START: MOV AX,DATA MOV DS,AX MOV AX,OFFSET BUF PUSH AX CALL AVERAGE MOV AH,4CH INT 21H ;子程序名:AVERAGE ;功能:求数据块中若干数据的平均值 ;入口参数:数据块的首地址,压入堆栈 ;出口参数:平均值,压入堆栈 AVERAGE PROC POP BX POP SI MOV AX,0 MOV CX,N L0: ADD AX,[SI] ADD SI,2 LOOP L0 MOV CL,N DIV CL CBW PUSH AX PUSH BX RET AVERAGE ENDP CODE ENDS END START. 5.3.4. 汇编语言程序设计 165. ;N 为数据块长度. ;调用子程序 ;返回 DOS. 子程序的嵌套与递归. 1.子程序的嵌套 在一个子程序中调用其他的子程序,称为子程序的嵌套,如图 5-6 所示。嵌套的层数不限, 只要堆栈空间足够即可。 主程序. 子程序 SUB2. SUB1 PROC. SUB2 PROC. CALL SUB1. ……. ……. ……. 子程序 SUB1. ……. ……. 图 5-6. ……. ……. CALL SUB2. RET. RET. 子程序嵌套.

(39) 166. 微机原理与接口技术. 例 5-37 ALDISP PROC PUSH AX PUSH CX PUSH AX MOV CL,4 SHR AL,CL CALL HTOASC POP AX CALL HTOASC POP CX POP AX RET ALDISP ENDP. ;实现 AL 内容的显示 ;暂存 AX ;转换 AL 的高 4 位 ;子程序调用(嵌套) ;转换 AL 的低 4 位 ;子程序调用(嵌套). ;将 AL 低 4 位表达的一位十六进制数转换为 ASCII 码 HTOASC PROC PUSH AX PUSH BX PUSH DX MOV BX,OFFSET ASCII ;BX 指向 ASCII 码表 AND AL,0FH ;取得一位十六进制数 XLAT ASCII ;换码:AL←CS:[BX+AL],注意数据在代码段 CS MOV DL,AL ;显示 MOV AH,2 INT 21H POP DX POP BX POP AX RET ;子程序返回 ;子程序的数据区 ASCII DB 30H,31H,32H,33H,34H,35H,36H,37H DB 38H,39H,41H,42H,43H,44H,45H,46H HTOASC ENDP. 注意:子程序可以与主程序共用一个数据段,也可以使用不同的数据段(注意修改 DS), 还可以在子程序最后设置数据区(利用 CS 寻址)。 2.子程序的递归 在一个子程序中又直接或间接调用子程序本身,称为子程序的递归,如图 5-7 所示。 主程序. 子程序 SUB1. SUB1 PROC. SUB1 PROC. CALL SUB1. ……. ……. ……. 子程序 SUB1. ……. ……. 图 5-7. ……. ……. CALL SUB1. RET. RET. 子程序递归.

(40) 第5章. 例5-38. 汇编语言程序设计 167. 求自然数N(N≥1)的阶乘。. DATA SEGMENT N DB 3 F_MUL DW ? DATA ENDS STACK SEGMENT PARA STACK 'STACK' DB 100 DUP(?) STACK ENDS CODE SEGMENT ASSUME CS:CODE, DS:DATA, SS:STACK BEGIN: MOV AX, DATA MOV DS, AX MOV AH, 0 MOV AL, N CALL FACTOR MOV F_MUL,AX MOV AH,4CH INT 21H FACTOR PROC PUSH AX SUB AX, 1 JNE RECUR POP AX JMP OVER RECUR: CALL FACTOR POP CX MUL CL OVER: RET FACTOR ENDP CODE ENDS END BEGIN. 5.4. Windows 汇编语言程序设计. 前面介绍了基于 DOS 的 8086 汇编语言程序设计,了解了 8086 汇编语言程序设计的思路 和方法。本节对 Windows 下的汇编程序设计进行简要介绍。 在 Windows 环境下,不需要掌握更多的硬件知识就能够应用 Win32 汇编语言编制应用程 序。在 DOS 系统中用汇编语言编制程序是借助中断来调用操作系统内核提供的功能,而 Win32 汇编是借助应用编程接口 API(Application Program Interface)去调用操作系统内核。Windows 环境下的很多高级语言对功能调用与实现的细节进行了不同程度的封装,如多线程处理和消息 循环等都被隐藏封装起来。虽然能够使用它们进行可视化编程,却无法全面了解 Win32 操作 系统程序的具体运行方式。由于封装使操作出现了某种缺陷和不足。如 VB 不支持指针,而程 序员编程需要有指针的 API,结果操作起来十分不方便,使多线程一类特征在 VB 中无法实现。 但用 Win32 汇编语言能洞察到操作系统的真实工作情况,充分发挥 Windows 系统的各种功能, 如对 Windows 环境的文件进行加密保护,编制出在 Windows 操作系统管理下各种文件的防病 毒程序。.

參考文獻

相關文件

mWrite MACRO text LOCAL string. .data ;;

push cx ; save character count mov si,dx ; point to input buffer dec cx ; save room for null byte L1: mov ah,1 ; function: keyboard input. int 21h ; returns character in AL cmp

 Extend the syntax analyzer into a full-blown compiler that, instead of passive XML code, generates executable VM code.  Two challenges: (a) handling data, and (b)

• tiny (a single segment, used by .com programs), small (one code segment and one data segment), medium (multiple code segments and a single data segment), compact (one code

In this section we define a general model that will encompass both register and variable automata and study its query evaluation problem over graphs. The model is essentially a

mov ax,var1 ;將其中一個記憶體內容先複製到暫存器 xchg ax,var2 ;分別執行記憶體與暫存器內容的交換動作 xchg ax,var1 ;完成交換。 Swap var1

MOV reg,data reg ← data 轉移立即資料(data)到暫存器 reg 內 MOV dreg,sreg dreg ← sreg 轉移暫存器 sreg 的內容到暫存器 dreg MOV segreg,reg segreg ← reg

ƒ 提供 Connection Oriented (連結導向) 並達成End-to- End (兩端通訊端點對端點) Process-to-Process (程序對 程序)、Reliable Data Delivery