汇编语言 程序设计
第 10章 模块化程序设计
◆ 模块化程序设计概述
◆ 段的定义
◆ 模块间的通讯
◆ 模块的连接
◆ 源程序综合举例
汇编语言 程序设计
10.1.1 模块化程序设计概念
在设计大型程序时, 常常要将整个问题分解为若干
个小问题, 必要时还要将小问题再次分解为更小的若干
问题, 每个小问题编写成独立的源文件, 最后将所有的
源文件连接起来组合成一个大程序 。 也就是说, 一个程
序往往由多个源文件组成, 那么构成一个程序的各个相
对独立的源文件通常称为模块 。 这样把一个程序分成多
个功能相对独立的程序模块分别编制, 调试后, 再用连
接程序把它们连接在一起生成一个完整的程序的设计的
方法称为模块化程序设计 。
10.1.2 模块化程序设计的优点
1, 开发速度快
2, 可维护性与可读性强
3, 可移埴性强
汇编语言 程序设计
10.1.3 模块划分的原则和方法
模块的划分应该是灵活的, 但不应是程序的等分,
应使各模块具有相对的独立性和完整性, 可以单独编程,
调试, 但也要考虑各个模块之间的联系 。 模块划分是一
个自上而下的过程 。 主模块是一个总控模块, 首先确定
主要的模块, 也就是说, 要把总任务划分成几个主要的
子任务 。 一般来说, 可以分成输入任务, 输出任务和一
个或多个进行处理或计算的子任务 。 在划分子模块的过
程中应该明确每个模块的功能, 数据结构及相互之间的
关系 。 第二步, 对这些主要的子模块根据需要再划分成
下一层的子模块 。 第三步, 重复上述过程, 一直到程序
分成易于理解和易于实现的小模块为止 。
汇编语言 程序设计
1.模块划分的原则
(1) 一个主模块完成对各子模块的调用, 实现总体任务,
而每个子模块完成相应的子任务, 各模块间除应在功能上分开,
逻辑上独立, 减少横向联系外, 不能使用转移指令在模块间转
来转去, 避免逻辑上的混乱;
(2) 子模块大小应适中, 模块过大就失去了模块化的意
义, 也会给编程和调试带来一定困难;模块过小, 会在的时间
和空间上造成浪费;
(3) 差别很大的两个程序段应作为两个模块;
(4) 当一些数据被多个程序段所公用, 那么这些数据所
在的程序段应作为一个模块;
(5) 当某些程序功能片段为多个模块所公用时, 应将它
们作为公用子程序模块;
(6) 各个模块的结构最好能设计为单入口, 单出口的形
式, 各模块间的接口应该简单, 要尽量减少公共标识符的个数 。
汇编语言 程序设计
2.模块划分的方法
(1) 层次图
层次图是表示模块与模块之间关系的方块图。层次
图的顶端是主模块,即一个总控制块,直接控制位于其
下一层的各个模块的执行,而各主要的子模块再去控制
其下一层的子模块。
(2) 模块说明
模块说明是对模块的功能、算法、模块输入和输出
以及它们的数据结构的简单说明。应该考虑程序中哪些
数据应该放在公共数据区,供所有模块访问,哪些数据
可在有直接从属关系的模块间传送。
返回
汇编语言 程序设计
10.2 段的定义SEGMENT伪指令的完整的格式为:
段名 SEGMENT [定位类型 ] [组合类型 ] [‘ 类别 ’ ]

段名 ENDS
10.2.1 定位类型
定位类型用于指定该段的段起始地址的特性, 也称为定位属性或对齐属性 。 连接程序连接目标文件时, 根据定位类型来确定段的开始地址 。
一共有 5种选择 。
1,PAGE(页 )
段的起始地址从页边界开始, 也就是说必须为 256的倍数, 即该地址的最后 8位二进制位应为 0。
2,PARA(节 )
段的起始地址必须从段边界开始, 也就是说必须为 16的倍数, 即该地址的最后 4位二进制位应为 0。
3,DWORD(双字 )
段的起始地址必须从双字边界开始, 也就是说必须为 4的倍数, 即该地址的最低两位二进制位应为 0。
4,WORD(字 )
段的起始地址必须从字边界开始, 也就是说必须为偶数地址, 即该地址的最低一位二进制位应为 0。
5,BYTE(字节 )
段的起始地址从字节边界开始, 也就是说可以从任意单元地址起,也就是说为下一个可用的字节地址开始 。
当段定义中没有指定段的定位类型时,定位类型的缺省方式为 PARA。
汇编语言 程序设计
10.2.2 组合类型
组合类型也称为组合属性, 组合类型标明本段与其他模块中
同名段的组合连接关系, 是用于控制本段与其他模块中的同名,
同类型段的组合连接方式, 有五种可选的组合类型 。
1,PUBLIC
连接程序将不同模块中的具有 PUBLlC属性的同名段连接在一
起,形成一个新的段,公用一个段基址。
2,STACK
STACK与 PLIBLIC的处理方式一样, 只是连接后的段为堆栈段,
连接程序在连接过程中自动将新段的段基址送到堆栈段寄存器 SS,
新段的长度送到堆栈指针寄存器 SP。 当堆栈段定义时没有说明为
STACK类型, 就要在程序中用指令给堆栈段寄存器 SS和堆栈指针
寄存器 SP赋值, 不然连接程序时就会产生警告信息 。
3,COMMON
COMMON类型会产生一个覆盖段, 连接程序把该类型的同名段
指定相同的段地址, 段的长度取决于最长的 COMMON段的长度 。
汇编语言 程序设计
4,MEMORY
连接程序不单独区分 MEMORY类型, 把 MEMORY与 PUBLIC类型同等
对待 。 MASM程序允许使用它主要是为了与其他支持 Intel MEMORY类
型的连接程序兼容 。
5,AT表达式
连接程序将具有 AT类型的段装在表达式值所指定的段地址边界
上 。 这个类型可以为标号或变量赋予绝对地址, 以便程序以标号或
变量的形式存取这些存储单元的内容 。 一般在 AT类型的段中不定义
指令或数据, 只说明一个地址结构 。
6,NONE
NONE为默认值, 表示该段是独立的, 与其他同名段无组合关系,
每段都有自己的段起始地址 。
10.2.3 类别
? 类别 ? 用于控制各段的存放顺序,类别名相同的所有段要相
邻存放。类别名可以是用单引号括起来任何合法的名称,若 ‘ 类别 ’
选择项省略,则表明该段类别为空。典型的类别名有:用于代码段 的 ‘ CODE’,用于数据段的 ‘ DATA’和用于堆栈段的 ‘ STACK’。
返回
汇编语言 程序设计
10.3 模块间的通讯
10.3.1 各模块之间的通信方式
当程序由几个模块组成时,势必存在一个模块使用另一个模块中定义的变量、标号以及子程序等问题。由于子程序与调用它的语
句,定义变量、标号及使用变量、标号的语句分别在不同的模块中,汇编是分开进行的,汇编程序无法知道子程序入口地址及变量、标
号的地址。因此,要由连接程序汇集各模块送来的地址信息,综合 决定各个调用指令的转移地址及变量、标号地址。因此,汇编语言
提供了几种伪指令来完成不同模块间的通讯。
1,TITLE
格式,TITLE [标题 ]
功能:给原程序指定一个标题, 而后,LST文件每页的头都会出现这个标题 。
2,NAME和 END
在模块化程序设计中, 常要用到模块定义伪指令 。 模块定义使用 NAME和 END两条伪指令 。
模块定义伪指令的一般格式为:
格式,[NAME 模块名 ]

END [标号 ]
模块名为本模块的名称, 是 NAME的操作数; END表示源程序到此结束, 若程序包含多个模块, 则每个模块的最后必须有 END,如果是
主模块, 其 END语句中可以指定一个标号, 这个标号表示程序的启动地址, 只有主模块的 END语句后有标号 。
汇编语言 程序设计
3,PUBLIC伪指令
格式,PUBLIC 标识符 [,标识符,...]
功能:表明本模块中所定义的标识符能够提供给其他模块引用 。
在一模块中, PUBLIC伪指令语句一般放在程序的开头, 只能说明一次 。 PUBLIC伪指令其后的标识符是本模块定义的可供其他
模块调用的标识符 。 这些标识符是在本模块中定义的符号常量,标号, 过程名或变量, 各名字之间用逗号隔开 。 注意:寄存器名
或其值为实数及其值超过两个字节的整数的符号常量均不能作公 共标识符使用 。
一旦经过 PUBLIC伪指令定义, EXAM子程序就成为公共子程序,即可被多个不同模块调用 。
4,EXTRN伪指令
格式,EXTRN标识符:类型 [,标识符:类型, … ]
功能:用来说明本模块中用到的标识符是由其它模块定义的,即本模块要引用其他模块定义的标识符 。
EXTRN是伪指令,其后的标识符就是本模块中要引用的外部标识符。而且标识符必须指出其类型。变量的类型可以是 BYTE,
WORD或 DWORD,标号和过程名的类型可以是 NEAR或 FAR。 所有的标识符类型必须与原定义时的类型一致;被 EXTRN伪指令说明的标
识符必须是在它所定义的模块中被 PUBLIC伪指令说明过的标识符。
返回
汇编语言 程序设计
10.4 模块的连接
在模块化程序设计中,一个大的程序分成了若干子
模块,每个模块具有完整的结构和独立的功能,并且能
单独汇编和调试。在实际使用时如何将这些具有独立功
能的模块连接成一个完整的整体呢?一般分两种情况:
源程序级间的装配连接和目标文件级间的装配连接。
10.4.1 源程序级间的装配连接
将若干个源文件( *.ASM) 连成一个完整的文件,
汇编后形成一个目标模块 (,OBJ)方式,称为源程序装
配连接。源程序级间的装配连接常常是被装配的若干模
块分别以源文件的形式存在,当需要在源主程序中插入
一个已存在的子程序文件,或是多个源文件要合成一块
的时候使用。
汇编语言 程序设计
10.4.2 目标文件级间的装配连接
目标模块连接就是通常所说的模块连接, 是多模块程序设
计的主要形式 。 目标模块连接是把被装配的各个模块分别汇编
后生成各自的目标文件 (.obj)经连接成一个可执行文件,它的
实现比源文件连接复杂 。
1,目标模块连接
目标模块的连接时要使用连接程序 LINK,通过 LINK将各模
块连接成一个可执行程序 。 命令格式如下:
LINK 模块 l文件名十模块 2文件名十模块 3文件名十 …
在 LINK执行时, 向使用者询问可执行文件名时, 回答一个
指定的文件名, 则连接后的可执行文件名就是回答中所指定的
文件名;如果没有回答指定文件名, 则连接后可执行文件名将
取所提供的第一个目标文件名 。 假设 MASM程序在 D盘 MASM目录
下, 目标模块为 file_1.obj,file_2.obj和 file_3.obj,则
D:\MASM>LINK file_1+file_2+file_3
ObjectModeles[.obj],↙
RunFile[file_1.exe],mainfile↙
ListFile[nul.map],mainfile↙
Libraries[.lib],↙
汇编语言 程序设计
2.段的组合与定位连接程序在执行时要对目标模块做两遍扫描, 第一遍扫描是
对所有段分配段地址, 并建立一张外部符号表;第二遍扫描是确定与这些外部符号有关的指令机器码值 。 连接完成后建立了装入
模块, 再由装入程序把该模块装入内存等待执行 。
对所有段分配段地址,涉及到段定义中的属性以及 PC机的可重定位技术。汇编程序汇编成的目标模块均是以浮动的零作为段起
始地址,理论上讲该模块可以定位在主存中的任何地方。而连接 程序可实现多个目标模块的连接,并按各段的新定位地址修改有
关的目标代码,使之成为一个整体来运行。在用连接程序将多个 目标模块连接在一起时,被连接模块要向连接程序提供两个方面
的信息:各段之间的组合方式和各模块之间的通信方式。各段之 间的组合方式由段定义中的组合类型和类别确定。
LINK程序连接时,先处理组合类型,后处理定位类型,再处理 ‘ 类别 ’,因此,各模块中具有同一种组合形式的段,其定位
类型不得相互矛盾,‘ 类别 ’ 名或者省略,或者相同。 L1NK程序连接时把 ‘ 类别 ’ 名相同的所有段 (段名未必相同 )放在连续的存
储区内,但仍然是不同的段。 ‘ 类别 ’ 名相同的各个段在连接时,先出现的在前,后出现的在后,每段都有自己的超始地址。没有
类别名的逻辑段,与其它没有类别名的逻辑段一起连续装入内存。
若多个模块的段都为 COMMON组合类型,连接后组合成一个互相覆盖的段,段的长度取二者的长者,这种连接方法只有当各模
块需要公共数据区时才使用,否则各数据段内容相互覆盖便会出 错。若多个模块的段都为 PUBLIC组合类型,连接后组合成一个相
邻连接的段,互不覆盖,连接顺序由连接程序确定,组合后形成 的段的长度约等于各个段长度之和。
返回
汇编语言 程序设计
10.5 源程序综合举例
[例 10.4] 从键盘输入的一个长度不超过 50个字符的字符串,
将其中的小写字母转变为大写字母输出,非字母字符不变。
解:可按题意和前面讲述的原则,把整个程序分成 3个模块:
模块 1是主程序,通过调用模块 2和模块 3定义的子程序实现接
收字符串以及小写字母转换大写字母的任务;模块 2定义子程序
IN_SUB接收输入的字符串及子程序 OUT_SUB输出字符串;模块 3定
义子程序 TRANS将串中的大写字母转换成小写字母。
应用例子层次图
汇编语言 程序设计;主模块 BLOCK_1:
NAME BLOCK_1 ; 模块 1
PUBLIC BUF ; 本模块缓冲区, 其它模块要使用
EXTRN IN_SUB:FAR,OUTSTR:FAR,TRANS:FAR; 使用其它模块的子程序
STACK SEGMENT STACK ‘ STACK’
DW 32 DUP(0)
STACK ENDS
DATA SEGMENT
BUF DB 50,?,50 DUP(?)
DATA ENDS
CODESG SEGMENT
MAIN PROC FAR
ASSUME CS:CODE,DS:DATA,SS:STACK
MOV AX,DATA
MOV DS,AX
CALL FAR PTR IN_SUB ; 调输入字符子程序 IN_SUB
CALL FAR PTR TRANS ; 调转换字符子程序 TRANS
CALL FAR PTR OUT_SUB ; 调输入字符子程序 OUT _SUB
MOV AX,4C00H ; 返回
INT 21H
MAIN ENDP
CODESG ENDS
END MAIN ;主模块结束
汇编语言 程序设计;子模块 2 BLOCK_2
NAME BLOCK_2 ; 命名模块 2
EXTRN BUF:BYTE ; 使用其它模块的缓冲区
PUBLIC IN_SUB,OUT_SUB ; 其它模块要使用
CODE SEGMENT
ASSUME CS:CODE
IN_SUB PROC FAR ; 输入子程序
PUSH DX
PUSH AX
LEA DX,BUF
MOV AH,10
INT 21H ; 输入字符
POP AX
POP DX
RET
IN_SUB ENDP
OUT_SUB PROC FAR
PUSH DX
PUSH AX
LEA DX,BUF+2
MOV AH,9
INT 21H ; 输出字符
POP AX
POP DX
RET
OUT_SUN ENDP
CODE ENDS
END ; 模块 2结束
汇编语言 程序设计;子模块 3 BLOCK_3
NAME BLOCK_3 ; 命名模块 3
EXTRN BUF:BYTE ; 使用其它模块的缓冲区
PUBLIC TRANS ; 其它模块使用
CODE SEGMENT
ASSUME CS:CODE
TRANS PROC FAR
XOR CX,CX
MOV CL,BUF+1 ;实际输入的字符个数
LEA SI,BUF
ADD SI,2 ;字符串首地址
CON,MOV AL,[SI] ;取串中某个字符
CMP AL,'a' ; 从 a开始到 z
JB OVER
CMP AL,'z'
JA OVER ;不为小写字母继续下一个字符
SUB AL,20H ;是小写字母则改为大写字母
MOV [S1],AL
OVER,INC SI
LOOP CON
MOV BYTE PTR [SI],'$' ;在串尾添加结束符,'$',以便输出
RET
TRANS ENDP
CODE ENDS
END ; 模块 3结束 返回