第四章
4.1 一个源程序从写出到执行的过程
使用汇编语言编译程序对源程序进行编译,产生目标文件,再用连接程序对目标文件进行连接,生成可在操作系统中直接运行的可执行文件
可执行文件包括两部分内容
· 程序(从汇编指令翻译过来的机器码)和数据(源程序中定义的数据)
· 相关的描述信息(如程序占用的内存空间等)
操作系统根据可执行文件中的描述信息,将可执行文件中的机器码和数据加载入内存,并进行相关的初始化(如设置CS:IP等)并由CPU执行
4.2 源程序
assume cs:codesg
codesg segment
mov ax,0123H
mov bx.0456H
add ax,bx
add ax,ax
mov ax.4c00H
int 21H
codesg ends
end
在源程序中包含两个指令:汇编指令和伪指令,汇编指令最终被编译为机器指令,最终由CPU执行,伪指令无对应的机器指令,由编译器根据伪指令进行相关的编译工作
上述源程序有3种伪指令
1、XXX segment …… XXX ends,segment和ends的功能是定义一个段,前者说明段开始,后者说明段结束,XXX是一个名称用来标识一个段,一个汇编程序由多个段组成,这些段用来存放代码、数据甚至当作一个栈空间等,一个有意义的汇编程序至少有一个段用来存放代码,称为代码段
2、end是一个汇编程序的结束标记,编译器在编译时如果碰到了伪指令end,就结束对源程序的编译,若不加end,编译器无法知道程序在何处结束
3、assume的含义为假设,假设某一段寄存器和程序中的某一segment…ends定义的段相关联,以后编程时记得用assume将有特定用途的段和相关的段寄存器关联起来
我们将源程序文件中的所有内容称为源程序,而源程序中最终由计算机执行、处理的指令或数据称为程序
程序最先以汇编指令的形式存在,后转变为机器码(如下图)
源程序中还有一些标号,如“codesg”,指代了一个地址,比如codesg在segment前面,作为一个栈的名称,这个栈的名称最终将被编译、连接程序处理为一个段的段地址
个人理解,存疑(因为codesg段是代码段,所以这个段的段地址传给cs,这就是为什么这个段要和cs寄存器相关联)
在DOS(一个单任务操作系统)的基础上讨论程序返回,一个程序P2在可执行文件中,需要一个在运行的程序P1将其加载入内存,将CPU的控制权交给P2,使得P2开始运行,同时P1暂停运行,P2运行结束后将CPU控制权还给P1,P1继续运行,所以我们需要在程序的末尾添加返回的程序段来交还CPU 的控制权,即
mov ax,4c00H
int 21H
源程序在编译时发现的错误为语法错误,如assume写为aume,编译后运行时发生的错误为逻辑错误,如没有返回的程序段
4.3 编辑源程序
保存为.asm文件
4.4 编译
使用masm汇编编译器
[.ASM]提示我们默认文件扩展名为asm,若编写的源程序文件名为t1.asm,只需输入t1,若为t1.txt,则需要输入全名,输入t1后回车,要求输入编译后目标文件的名称,默认为t1.obj,所以回车,要求指定生成目标文件的目录,默认为当前地址,所以回车,之后继续两个回车使编译器不产生中间结果,最后两行显示没有警告错误和必须改正的错误
4.5 连接
使用link
默认可执行文件名称,回车
忽略中间结果,回车
Libraries提示输入库文件的名称,库文件里包含了一些可以调用的子程序,若程序中调用了某个库文件中的子程序,就需要将这个库文件和目标文件连接到一起,若没有直接回车
忽略最后一行”没有栈段“的警告,最后便生成t1.exe
连接的几个作用
1、当源程序很大时,可以将它分为多个源程序文件来编译,每个源程序编译成为目标文件后,再用连接程序连接到一起,形成可执行文件
2、程序中调用了某个库文件中的子程序,需要将这个库文件和该程序的目标文件连接到一起,生成一个可执行文件
3、一个源程序编译后得到目标文件,其中有些内容还不能直接用来生成可执行文件,需要连接程序将其处理为最终的可执行信息,所以连接这一步骤时必须的
4.6 以简化的方式进行编译和连接
masm t1;
link t1;
加分号后可忽略中间文件的生成
4.7 运行
在命令行中直接输入t1即可
4.8 谁将可执行文件中的程序装载进入内存并使它运行?
任何通用的操作系统,都提供一个为shell的程序,操作人员使用这个程序来操作计算机系统进行工作,DOS中的command称为命令解释器,是DOS的shell,在屏幕上显示当前盘符和当前路径,如”c:\“用户可以输入cd、dir等命令,由command执行
要执行一个程序,就输入该程序的可执行文件的名称,command找到后将这个可执行文件中的程序载入内存,并设置CS:IP,然后command暂停运行,CPU运行程序,结束后command继续运行,commond就是之前提到的P1
所以在DOS中直接运行t1.exe时,是正在运行的command将t1.exe中的程序加载入内存,再由command设置CPU的CS:IP指向程序的第一条指令(即程序的入口),运行结束后返回到command,CPU继续运行command
4.9 程序执行过程的跟踪
可以使用Debug将程序载入内存,设置CS:IP,并用相关命令单步执行程序并查看每次执行后的结果
debug t1.exe ;注意不能省略.exe
输入后CS:IP就会指向程序的入口,cx存放程序的长度,如15个字节即为000FH
关于DOS加载.exe文件
先找到一段起始地址为SA:0000的容量足够的空闲内存区,这段内存的前256个字节作为数据区,称为程序段前缀(PSP),DOS利用PSP来和被加载程序进行通信,而程序被装入PSP之后的内存区中,为了更好得区分PSP和程序,将它们分在不同的段中(物理地址是连续的,但有不同的段地址)
因为256 = 100H,所以SA * 16 + 0 + 256 = (SA + 16) * 16 + 0,因此程序的段地址可表示为SA+10H,偏移地址为0
最后将该内存区的段地址存入ds,初始化其他相关寄存器后,设置CS:IP
假设t1.exe加载完后DS=129E:0,即PSP的地址为123E:0,那么程序的地址为12AE:0
单步执行至int 21指令时用P命令
这里由Debug将程序载入内存,所以运行结束后返回Debug,而Q命令退出Debug返回到command,所以完整的顺序为:
command加载Debug,再由Debug加载t1.exe,返回时从t1.exe返回到Debug再返回到command