本文通过对.COM程序和.EXE程序加载时不同内存映象的研究进而得出:.COM程序只有一个物理段,段的最大长度为64KB:.COM程序只能从偏移地址为100H处开始执行。DOS对.EXE文件长度没有约束,便于组织大型应用程序;以及.EXE文件中用END启动标号来说明启动点,用PUSH DS来保存程序段前缀的段地址,用SUB AX,AX 和 PUSH AX指令来保存PSP中INT 20H指令首地址的必要性。从中还可看出:由于.COM程序比.EXE程序规模小而简单,所以.COM程序装入速度比.EXE程序装入速度要快。为进一步发挥汇编语言程序执行速度快的优势提供参考。
在MS—DOS中,有两种类型的可执行程序,即.COM程序和.EXE程序。8086/8088汇编语言也有相应两种程序结构的文件,可分别编译最终形成.COM程序和.EXE程序。而汇编语言又是计算机能提供给用户的最快而又最有效的语言,因而在对于程序的空间和时间要求很高的场合,汇编语言是必不可少的,所以从根本上阐明.EXE文件和.COM文件的程序结构特点,以及它们之间的区别,两种程序的不同加载情形和程序段前缀结构特征,是真正领会汇编语言编程的必要条件。
1 码段的基本组成
对于1个汇编语言小程序,可以不定义数据段、附加段和堆栈段,但不能不定义代码段,没有码段则不成为程序,所以首先来认识一下码段的基本组成。
1.1 8086/8088汇编语言程序的.EXE文件(“标准序”)结构形式如图1
对于1个汇编语言小程序,可以不定义数据段、附加段和堆栈段,但不能不定义代码段,没有码段则不成为程序,所以首先来认识一下码段的基本组成。
1.1 8086/8088汇编语言程序的.EXE文件(“标准序”)结构形式如图1
图1 .EXE文件的结构
1.1.1 为了正确汇编,汇编程序在汇编时必须清楚程序中段的结构以及各种指令在执行时应该访问哪一个段?即程序中段和段寄存器的关系,这可由ASSUME伪操作来实现。
格式为:ASSUME 段寄存器1:段名1,段寄存器2:段名2,…
如:ASSUME CS:CODE,DS:DATA,SS:STACK
这实际上就是说,码段CODE的段地址值放在CS段寄存器中,数据段DATA的段地址值放在DS段寄存器中,堆栈段STACK的段地址值放在SS段寄存器中。
1.1.2 ASSUME仅仅是指明段寄存器与段的关系,并未保存段地址,所以如有数据段、附加段、堆栈段,则必须保存段地址。码段不需保存段地址,因为码段的这一操作是在程序初始化时完成。
格式为: MOV AX,段名
MOV段寄存器,AX
MOVAX,DATA
如: MOVDS,AX
1.1.3 汇编语言程序是DOS的一个子程序,所以要把汇编语言程序的主体部分定义成一个远过程,以便由DOS调用该过程,在程序结束时用RET指令返回DOS。
格式为: 过程名 PROC FAR
.
.
.
RET
过程名 ENDP
1.1.4 为了在程序结束时用RET指令能正确返回DOS,在码段的开始处,必须保存DOS现场即保存返回地址,保存返回地址的指令为:
PUSH DS
SUBAX,AX
PUSHAX
1.2 8086/8088汇编语言程序的.COM文件结构形式
如图2
格式为:ASSUME 段寄存器1:段名1,段寄存器2:段名2,…
如:ASSUME CS:CODE,DS:DATA,SS:STACK
这实际上就是说,码段CODE的段地址值放在CS段寄存器中,数据段DATA的段地址值放在DS段寄存器中,堆栈段STACK的段地址值放在SS段寄存器中。
1.1.2 ASSUME仅仅是指明段寄存器与段的关系,并未保存段地址,所以如有数据段、附加段、堆栈段,则必须保存段地址。码段不需保存段地址,因为码段的这一操作是在程序初始化时完成。
格式为: MOV AX,段名
MOV段寄存器,AX
MOVAX,DATA
如: MOVDS,AX
1.1.3 汇编语言程序是DOS的一个子程序,所以要把汇编语言程序的主体部分定义成一个远过程,以便由DOS调用该过程,在程序结束时用RET指令返回DOS。
格式为: 过程名 PROC FAR
.
.
.
RET
过程名 ENDP
1.1.4 为了在程序结束时用RET指令能正确返回DOS,在码段的开始处,必须保存DOS现场即保存返回地址,保存返回地址的指令为:
PUSH DS
SUBAX,AX
PUSHAX
1.2 8086/8088汇编语言程序的.COM文件结构形式
如图2
使用.COM文件时,程序不分段,其入口点必须是0100H,也不必设置堆栈段,且对所有的过程均应定义为NEAR属性。
图2 .COM文件的结构
2 程序的加载
程序的加载是指将.EXE文件或.COM文件装入内存并执行之,加载1个程序是由DOS的4BH号系统功能来完成的。程序装入内存时,DOS还在它的前面安装1个256(100H)字节长的程序段前缀(Program Segment Prefix),作为DOS与运行程序的软件接口。
2.1 程序段前缀(PSP)
程序段前缀(PSP)简言之就是1个数据结构,它和用户程序本身位于同一内存分配块中,构成一个不可分割的整体。它是DOS(作为加载程序的父程)和被加载程序的软件接口。它主要是用来存放与用户程序有关的一些控制信息,并提供程序正常或异常结束时返回DOS的途径。它总共有256个字节长,位于程序所在段的起始位置。PSP的DOS结构如图3所示:
程序的加载是指将.EXE文件或.COM文件装入内存并执行之,加载1个程序是由DOS的4BH号系统功能来完成的。程序装入内存时,DOS还在它的前面安装1个256(100H)字节长的程序段前缀(Program Segment Prefix),作为DOS与运行程序的软件接口。
2.1 程序段前缀(PSP)
程序段前缀(PSP)简言之就是1个数据结构,它和用户程序本身位于同一内存分配块中,构成一个不可分割的整体。它是DOS(作为加载程序的父程)和被加载程序的软件接口。它主要是用来存放与用户程序有关的一些控制信息,并提供程序正常或异常结束时返回DOS的途径。它总共有256个字节长,位于程序所在段的起始位置。PSP的DOS结构如图3所示:
图3 程序段前缀结构
2.2 .COM文件的内存映象
一个.COM程序存储在磁盘中的内容同执行时装进内存的内容完全一致。执行时只是简单地将.COM程序拷贝到程序段前缀的紧上端,起始地址为100H,.COM文件装入内存后的映象如图4所示:
一个.COM程序存储在磁盘中的内容同执行时装进内存的内容完全一致。执行时只是简单地将.COM程序拷贝到程序段前缀的紧上端,起始地址为100H,.COM文件装入内存后的映象如图4所示:
图4 .COM文件的内存映象
由此内存映象图可看出:
(1)当1个.COM程序开始执行时,所有的段寄存器(CS,DS,SS和ES)都含有相同的值,都是程序段前缀(PSP)所在段的段地址。即4个段实际上是同一物理段,段的最大长度为64KB,这也是.COM文件长度的最大限度。
(2).COM程序是从偏移地址值为100H处开始执行的。DOS强行设置IP=0100H,即初始的CS:IP规定指向PSP之后的第1个字节位置。于是在编写.COM文件源程序时,在用ORG 100H伪指令预留PSP空间后,其后的第1条指令应该是1条可执行的指令。
(3)整个.COM程序必须在1个物理段(64KB)内,段尾处的1个字作为堆栈使用。在将控制权移交给.COM程序前,MS-DOS总要将1个字0压进堆栈。减去这个字和程序段前缀占用的256个字节空间,余下的空间便是.COM程序可以使用的空间。也就是说,.COM程序的所有代码和数据至多使用65,536—2—256=65,278字节的存储空间。
(4).COM文件源程序中不用设置堆栈段。堆栈指针寄存器SP指向该.COM程序可使用的存储器的最高端下面的1个字,即如果至少有64KB存储空间可供使用,则SP寄存器的值为OFFFEH。DOS强行设置SS=PSP段地址,SP=OFFFEH。综上所述,所以.COM程序文件的结构形式必须如图2所示。
2.3 .EXE文件的内存映象
2.3.1 .EXE文件结构 1个.EXE文件的源程序经汇编程序汇编后产生目标文件,目标文件经连接程序的组合—分类—定位后,产生的.EXE文件是1个可执行的浮动代码文件。该文件在磁盘空间内是由两部分组成的:一部分为头部信息块,另一部分为浮动的装入模块。头部信息又分为前部的格式化区(30字节)和后部的重定位项表,主要是用来说明如何装入代码和数据段。在文件装入时虽曾被读入内存高端缓冲区,但在分析使用后即被丢弃,真正留于内存空间的只是装入模块。.EXE文件的磁盘空间结构如图5所示:
(1)当1个.COM程序开始执行时,所有的段寄存器(CS,DS,SS和ES)都含有相同的值,都是程序段前缀(PSP)所在段的段地址。即4个段实际上是同一物理段,段的最大长度为64KB,这也是.COM文件长度的最大限度。
(2).COM程序是从偏移地址值为100H处开始执行的。DOS强行设置IP=0100H,即初始的CS:IP规定指向PSP之后的第1个字节位置。于是在编写.COM文件源程序时,在用ORG 100H伪指令预留PSP空间后,其后的第1条指令应该是1条可执行的指令。
(3)整个.COM程序必须在1个物理段(64KB)内,段尾处的1个字作为堆栈使用。在将控制权移交给.COM程序前,MS-DOS总要将1个字0压进堆栈。减去这个字和程序段前缀占用的256个字节空间,余下的空间便是.COM程序可以使用的空间。也就是说,.COM程序的所有代码和数据至多使用65,536—2—256=65,278字节的存储空间。
(4).COM文件源程序中不用设置堆栈段。堆栈指针寄存器SP指向该.COM程序可使用的存储器的最高端下面的1个字,即如果至少有64KB存储空间可供使用,则SP寄存器的值为OFFFEH。DOS强行设置SS=PSP段地址,SP=OFFFEH。综上所述,所以.COM程序文件的结构形式必须如图2所示。
2.3 .EXE文件的内存映象
2.3.1 .EXE文件结构 1个.EXE文件的源程序经汇编程序汇编后产生目标文件,目标文件经连接程序的组合—分类—定位后,产生的.EXE文件是1个可执行的浮动代码文件。该文件在磁盘空间内是由两部分组成的:一部分为头部信息块,另一部分为浮动的装入模块。头部信息又分为前部的格式化区(30字节)和后部的重定位项表,主要是用来说明如何装入代码和数据段。在文件装入时虽曾被读入内存高端缓冲区,但在分析使用后即被丢弃,真正留于内存空间的只是装入模块。.EXE文件的磁盘空间结构如图5所示:
图5 .EXE文件的磁盘空间结构
2.3.2 .EXE文件的内存映象 .EXE文件装入内存的只是装入模块本身,DOS同时还为用户程序安装1个256字节长的程序段前缀。.EXE程序执行时也是装入在程序段前缀的紧上端,但代码段,数据段和堆栈段具体装到内存何处,要由MS-DOS提供的4BH号系统功能调用根据.EXE文件头中的信息来确定。EXE文件的内存映象如图6所示:
图6 .EXE文件的内存映象
由此内存映象图可看出:
(1).EXE文件的程序中可有代码段、数据段、附加段和堆栈段4个分段,最能体现Intel 80x86体系存储器地址分段结构的特点,便于组织大型应用程序。
(2)DOS对.EXE文件的长度没有约束,可以占用全部用户程序空间。
(3).EXE文件的源程序中应该定义1个堆栈段,此段或者具有STACK组合类型,或者用ASSUME语句把段名和SS段寄存器联系起来,或者两者兼用之。在用户程序装入内存时,装入程序自动令SS:SP指向这个堆栈区的底部,源程序不用设置SS和SP。
(4).EXE文件的源程序中不应设置初始的CS和IP值,而应该使用END启动标号这样的伪指令来说明启动点,加载时CS:IP寄存器会自动指向程序中第1条可执行指令处。
(5).EXE程序装入内存后,DS和ES寄存器中的值均为程序段前缀PSP的段地址,同时从图3(程序段前缀结构示意图)中可看出,程序段前缀PSP中的头两个字节的内容是十六进制“CD20”与8086/8088指令“INT 20H”相对应,该指令的作用是程序非驻留退出时的结束指令,但发出此软中断指令时,要求CS段寄存器一定要为程序段前缀PSP的段地址。故在.EXE(“标准序”)结构中利用DS在堆栈中保存程序段前缀的段地址,利用SUB AX,AX和PUSH AX指令来保存程序段前缀中INT 20H指令的首地址。
PUSH DS ———→保存PSP段地址
SUB AX,AX
PUSH AX———→INT 20H指令所在第1个字节的偏移地址为0
其次,主过程又定义为远过程,因此RET是1个远程(段间)返回指令。于是,程序最后执行RET指令时将使CS=PSP的段地址,IP=0000H。这样一来系统就会自动转移到PSP+00H,从而正确执行“INT 20H”指令,完成程序的结束退出。
标准序还有另外1个优点:程序的主体呈现为1个远过程的结构,也便于程序模块化。
综上所述,所以.EXE程序文件的结构形式必须如图1所示。
(1).EXE文件的程序中可有代码段、数据段、附加段和堆栈段4个分段,最能体现Intel 80x86体系存储器地址分段结构的特点,便于组织大型应用程序。
(2)DOS对.EXE文件的长度没有约束,可以占用全部用户程序空间。
(3).EXE文件的源程序中应该定义1个堆栈段,此段或者具有STACK组合类型,或者用ASSUME语句把段名和SS段寄存器联系起来,或者两者兼用之。在用户程序装入内存时,装入程序自动令SS:SP指向这个堆栈区的底部,源程序不用设置SS和SP。
(4).EXE文件的源程序中不应设置初始的CS和IP值,而应该使用END启动标号这样的伪指令来说明启动点,加载时CS:IP寄存器会自动指向程序中第1条可执行指令处。
(5).EXE程序装入内存后,DS和ES寄存器中的值均为程序段前缀PSP的段地址,同时从图3(程序段前缀结构示意图)中可看出,程序段前缀PSP中的头两个字节的内容是十六进制“CD20”与8086/8088指令“INT 20H”相对应,该指令的作用是程序非驻留退出时的结束指令,但发出此软中断指令时,要求CS段寄存器一定要为程序段前缀PSP的段地址。故在.EXE(“标准序”)结构中利用DS在堆栈中保存程序段前缀的段地址,利用SUB AX,AX和PUSH AX指令来保存程序段前缀中INT 20H指令的首地址。
PUSH DS ———→保存PSP段地址
SUB AX,AX
PUSH AX———→INT 20H指令所在第1个字节的偏移地址为0
其次,主过程又定义为远过程,因此RET是1个远程(段间)返回指令。于是,程序最后执行RET指令时将使CS=PSP的段地址,IP=0000H。这样一来系统就会自动转移到PSP+00H,从而正确执行“INT 20H”指令,完成程序的结束退出。
标准序还有另外1个优点:程序的主体呈现为1个远过程的结构,也便于程序模块化。
综上所述,所以.EXE程序文件的结构形式必须如图1所示。
3 .COM程序与.EXE程序的比较
通过对.COM程序和.EXE程序结构的研究,从中可看出:.COM程序由程序本身的二进制代码组成,它没有.EXE程序所具有的格式化区和重定位项表,也就免去了最后修正装入模块中某些位置的机器码,所以它占有的存储空间比.EXE程序要小;.COM程序又不允许分段,它所占有的存储空间不允许超过64KB,因而只能用来编制较小的程序,由于其规模小而简单,所以.COM程序的装入速度比.EXE程序的装入速度要快得多。DOS对.EXE程序的长度没有约束,可以占用全部用户程序空间;.EXE程序中可有代码段、数据段、附加段和堆栈段4个分段,便于组织大型应用程序;.EXE程序主体呈现为1个远过程的结构,也便于实现程序模块化。
通过对.COM程序和.EXE程序结构的研究,从中可看出:.COM程序由程序本身的二进制代码组成,它没有.EXE程序所具有的格式化区和重定位项表,也就免去了最后修正装入模块中某些位置的机器码,所以它占有的存储空间比.EXE程序要小;.COM程序又不允许分段,它所占有的存储空间不允许超过64KB,因而只能用来编制较小的程序,由于其规模小而简单,所以.COM程序的装入速度比.EXE程序的装入速度要快得多。DOS对.EXE程序的长度没有约束,可以占用全部用户程序空间;.EXE程序中可有代码段、数据段、附加段和堆栈段4个分段,便于组织大型应用程序;.EXE程序主体呈现为1个远过程的结构,也便于实现程序模块化。