一个源程序从写出到执行的过程
编写汇编源程序
对源程序进行编译连接
可执行文件包含两部分内容:程序和数据、相关的描述信息
执行可执行文件中的程序
源程序
程序4.1
assume cs:codesg |
源程序中包含两种指令:汇编指令、伪指令。
汇编指令:有对应的机器码,最终为CPU所执行。
伪指令:由编译器来执行,编译器根据伪指令来进行相关的编译工作。
伪指令
程序4.1中出现了3种伪指令
XXX segment …… XXX ends
segment
和ends
是一对成对使用的伪指令。segment
和ends
的功能是定义一个段,segment
代表开始,ends
代表结束。使用格式:
段名 segment
...
...
段名 ends一个汇编程序是由多个段组成的,这些段被用来存放代码、数据或当作栈空间来使用。一个有意义的汇编程序中至少要有一个段,这个段用来存放代码。codesg就是一个代码段。
end
end
是一个汇编程序的结束标记。编译器碰到end
就会结束对源程序的编译。assume
含义为“假设”。它假设某一段寄存器和程序中的某一个用
segment...ends
定义的段相关联。在程序4.1的开头,用assume cs:codesg
将用作代码段的段codesg和CPU中的段寄存器cs联系起来。
源程序中的“程序”
源程序中的汇编指令组成了最终由计算机执行的程序,以后可以将源程序文件中的所有内容称为源程序,将源程序中最终由计算机执行、处理的指令或数据,称为程序。程序最先以汇编指令的形式存在源程序中,经编译、连接后转变为机器码,存储在可执行文件中。
标号
codesg
就是一个标号。一个标号指代了一个地址。codesg
这个段的名称最终将被编译、连接程序处理为一个段的地址。
程序的结构
例:编程运算2^3
- 定义一个段abc
- 在段中写入汇编指令
- 指出程序在哪里结束
- 将abc和cs联系起来
程序4.2
assume cs:abc |
程序返回
一个程序结束后,将CPU的控制权交还给使它得以运行的程序,我们称这个过程为:程序返回。所以,应该在程序的末尾添加返回的程序段。
4.1中的这两条指令所实现的功能就是程序返回。
mov ax,4c00H |
目前接触的几个和结束相关的内容:段结束、程序结束、程序返回。
目的 | 相关指令 | 指令性质 | 指令执行者 |
---|---|---|---|
通知编译器一个段结束 | 段名 ends | 伪指令 | 编译时,由编译器执行 |
通知编译器程序结束 | end | 伪指令 | 编译时,由编译器执行 |
程序返回 | mov ax,4c00H int 21H | 汇编指令 | 执行时,由CPU执行 |
语法错误和逻辑错误
程序4.2在运行时会引发一些问题,因为程序没有返回。但这个错误编译器是不会发现的。
一般来说,程序在编译时被编译器发现的错误是语法错误,比如写错assume,忘写ends等。在源程序编译后,在运行时发生的错误是逻辑错误。
改正后的程序4.2
assume cs:abc |
编辑源程序
可以用任意的文本编辑器来编辑源程序,只要最终将其存储为纯文本文件即可。将程序保存为.asm文件。
编译
可以对源程序文件xxx.asm进行编译,生成包含机器代码的目标文件。
这里采用微软的masm5.0汇编编译器,文件名为masm.exe。
一般来说,有两类错误使我们得不到所期望的目标文件:
- 程序中有“Severe Errors”;
- 找不到所给出的源程序文件
在编译过程中,我们提供了一个输入,即源程序文件。最多可以得到3个输出:目标文件(.obj)、列表文件(.lst)、交叉引用文件(.crf),这3个输出文件中,目标文件使我们最终要得到的结果,而另外两个只是中间结果,可以让编译器忽略对它们的生成。
简捷的编译方式
masm c:\1; |
在 masm 后面加上被编译的源程序文件的路径、文件名,在命令行的结尾再加上分号,按enter键,编译器就对 c:\1.asm 进行编译,在当前路径下生成目标文件 1.obj,并在编译的过程中自动忽略中间文件的生成。
连接
这里使用的是微软的 Overlay Linker3.60 连接器,文件名为link.exe。
连接的作用:
- 当源程序很大时,可以将它分为多个源程序文件来编译,每个源程序编译成为目标文件后,再用连接程序将它们连接到一起,生成一个可执行文件;
- 程序中调用了某个库文件中的子程序,需要将这个库文件和该程序生成的目标文件连接到一起,生成一个可执行文件。
- 一个源程序编译后,得到了存有机器码的目标文件,目标文件中的有些内容还不能直接用来生成可执行文件,连接程序将这些内容处理为最终的可执行信息。所以,在只有一个源程序文件,而又不需要调用某个库中的子程序的情况下,也必须用连接程序对目标文件进行处理,生成可执行文件。
简捷的连接方式,与简捷的编译方式类似。
link c:\1; |
编译 masm asm -> obj
连接 link obj -> exe
谁将可执行文件中的程序装载进入内存并使它运行?
如果用户要执行一个程序,则输入该程序的可执行文件的名称,command 首先根据文件名找到可执行文件,然后将这个可执行文件中的程序加载入内存,设置 CS:IP 指向程序的入口。此后,command 暂停运行,CPU 运行程序。程序运行结束后,返回到 command 中。
在DOS 中直接执行1.exe 时,是正在运行的command,将1.exe 中的程序加载入内存;
command 设置CPU 的 CS:IP 指向程序的第一条指令(即程序的入口),从而使程序得以运行;
程序运行结束后,返回到command 中,CPU 继续运行command。
汇编程序从写出到执行的过程
程序执行过程的跟踪
前置知识
DOS系统中.exe文件中的程序的加载过程
注:重定位没有在图中讲解。
我们从图中得到的信息:
- 程序加载后,ds中存放着程序所在内存区的段地址,这个内存区的偏移地址为0,则程序的内存区的地址为 ds:0;
- 这个内存区的前256个字节中存放的是PSP,DOS用来和程序进行通信。从256字节处向后的空间存放的是程序。
所以,程序的物理地址是:$SA*16 + 0 + 256 = (SA + 16) * 16 + 0$ ,可用段地址和偏移地址表示为:SA + 10H:0
。
Debug的使用
为了观察程序的运行过程,可以使用Debug。Debug可以将程序加载入内存,设置 CS:IP 指向程序的入口,但Debug并不放弃对CPU的控制,这样,我们就可以使用Debug的相关命令来单步执行程序,查看每一条指令的执行结果。
输入指令
debug 1.exe |
r
:查看各个寄存器的设置情况
Debug将程序从可执行文件加载入内存后,cx中存放的是程序的长度。
DS = 075A,则 PSP 的地址为 075A:0
,程序的地址为 076A:0
。
CS = 076A,IP = 0000,CS:IP 指向程序的第一条指令。
u
:查看指令
t
:单步执行程序中的每一条指令,并观察每条指令的的执行结果
p
:到了 int 21
,我们要用 p
命令执行。
int 21
执行后,显示出“Program terminated normally”,返回到debug中。表示程序正常结束。
使用 q
命令退出debug。
在 DOS 中用 “ debug 1.exe” 运行 Debug 对 1.exe 进行跟踪时,程序加载的顺序是:command 加载 Debug,Debug 加载 1.exe。返回的顺序是:从 1.exe 中的程序返回到 Debug,从Debug 返回到 command。
实验3
test3_t1.asm
assume cs:codesg |
执行过程:
PSP内容: