8086汇编语言学习(四) 8086汇编程序的编译与链接
1、8086汇编源程序从编写到执行的过程
前面介绍过通过debug模式来进行汇编程序的编写和调试。但是随着深入学习,所编写的汇编程序会越来越复杂,而通过debug的A命令去逐条编写汇编指令是非常低效的。
因此,这里将要介绍8086汇编源程序这一概念,使得我们可以通过文本的方式编写汇编程序,并通过一系列的措施将其转化为最终的二进制可执行程序。
一个汇编源程序从编写到执行大概可以分为几个阶段:
1. 开发者编写文本形式的汇编源程序
2. 对汇编源程序进行编译并生成目标文件、对目标文件进行链接并生成可执行文件
3. 运行可执行文件
下面通过一个简单例子来详细说明(以windows操作系统下的masm5环境举例):
1. 编写汇编源程序
通过系统自带的记事本或者更高级的文本编辑器编写文本形式的汇编源程序,将其命名为demo.asm,程序内容如下:
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
2. 对汇编源程序进行编译、链接
编译:
通过命令行运行masm.exe,先让用户输入需要编译的汇编源文件的本地文件地址(Source filename :)。如果文件在当前工作路径下可以仅输入文件名(例如 demo.asm),否则就需要输入绝对地址路径(例如 c:\MASM\ASM\demo.asm)。另外,如果源程序文件的默认后缀拓展名为[.ASM],如果是.txt之类的文件,则需要输入全名(例如 c:\MASM\ASM\demo.txt)。
接着会让用户输入所生成的.obj目标文件的存放路径以及名称(Object filename :),例如 c:\MASM\ASM\demo.obj。后面的Source listing和Cross-reference能够让masm编译器生成编译过程中的中间结果文件,这里可以直接回车跳过,不去让编译器生成。
如果没有出现错误日志(0 Errors),此时可以在Object filename指定的路径下找到所生成的目标文件。
链接:
通过命令行执行link.exe,先让用户输入需要进行链接的目标文件(Object Modules:) 例如 c:\MASM\ASM\demo.obj。绝对路径/相对路径以及文件全名的使用和masm.exe进行编译时规则保持一致。
接着需要用户输入所生成的.exe可执行文件的存放路径以及名称(Run File :),例如 c:\MASM\ASM\demo.exe。后面的List File以及Libraries用于指定和obj文件进行链接的子程序以及库程序,由于这里程序很简单,并不需要用到,选择回车直接跳过。
如无意外,将会在指定位置出现demo.exe可执行文件。(no stack segment警告(而不是错误)程序中没有栈段,这是个不好的习惯,但这里先暂时忽略掉)
3. 运行可执行文件
通过命令行窗口运行demo.exe,会发现没有什么效果。这是因为我们的程序中只有对CPU寄存器的简单操作,在执行完之后程序便立即结束了,并没有常驻。
对于这种执行完成会立即退出的汇编程序,可以通过debug.exe对其进行调试,以了解中间的执行过程。
使用debug命令将可执行文件路径作为参数执行,可以对该可执行文件进行调试(例如:debug c:\masm\asm\demo.exe)。从截图中可以看到,通过U指令反编译,文本中的源程序被准确的转换为了二进制可执行程序。
2、8086汇编源程序语法介绍
汇编源程序的内容大致可以分为两种,一种是与机器指令对应的可执行指令(例如:mov ax,0123H add ax,bx等),另一种是伪指令(例如: codesg segment)。
伪指令和可执行指令的区别在于,可执行指令是和CPU的硬件挂钩的,会被汇编器转换为对应的机器指令;而伪指令的作用是为了控制编译过程而存在的,其仅由汇编器处理,没有对应的机器指令,不会被CPU执行。
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的几种类型的段寄存器对应。
因为程序是结构化的,所以要理解上面这段程序不能完全从上到下的进行理解,而是要进行有机的拆解:
首先,我们定义了一个段,名为codesg,段的程序界限由segment和ends这对关键字控制,也就是 codesg segment 。。。 codesg ends,在这两行语句中间的所有内容都属于这个段。codesg段是作为代码段来使用的,所以内容都是程序指令。
接着明确的告诉汇编器,将codesg作为程序段(cs)来使用,也就是第一行assume cs:codesg的意义,关键字为assume cs:。
最后指明程序的结束,也就是最后一行end关键字的作用。
这里segment 、ends、end等关键字构成的指令都是伪指令。
其它需要注意的点:
1. 8086汇编的注释是通过分号;来识别的,汇编器会将每一行分号后面的内容当作注释,自动忽略后面一整行的内容。
2. 在codesg的段内容中出现了一个前面没有出现过的指令int。int指令代表着中断(interrupt),由于当前程序是由DOS调用执行的,而mov ax,4c00H int 21H这两句指令就是通过中断结束当前程序并返回到DOS中。关于中断的内容会在后续的博客里做详细介绍。
3. 和高级语言一样,汇编语言中的错误也分为语法错误和逻辑错误两种,语法错误包括未识别的符号、变量等;而逻辑错误包括栈越界溢出、错误的跳转等。一般来说,能被编译器在编译时直接发现的错误都是语法错误,而更深层次的逻辑错误就只能靠开发人员自己想办法避免了。