在上篇博文中,我们详细学习了汇编源程序的组成,并且下载好两个必须的工具:编译源程序的工具 MASM.exe和处理中间文件的工具 LINK.exe。
我们在上篇博文中只是大致了解了一下MASM.exe和LINK.exe,那么它们两个究竟是如何使用的呢?这将会是我们在本篇博文中需要学习的部分。那么本篇博文学习目标:
1、熟悉并掌握MASM.exe和LINK.exe 的使用
2、完成源程序的编译、连接、运行整个过程
3、了解程序运行背后的逻辑
目标已定,那么就让我们赶紧开始学习吧!
我们使用MASM.exe工具来编译源代码文件,所谓源程序,在上篇博文中我们已经进行了详细讲述,有遗忘的同学可回看进行复习。
我们已经知道,MASM工具和Debug工具一样,都是运行在16位操作系统下,所以我们需要在DOSBox环境下才能运行MASM工具。所以首先我们打开DOSBox:
这里请注意,和运行Debug工具一样,挂载的路径下一定要是MASM工具和LINK工具所在的路径,这样才能使用并运行工具。
为了保证可以访问到源程序文件,我们还需要保证源程序文件也需要在挂载目录下,即源程序文件和MASM工具在同一目录下。博主这里的文件夹:
为了确保目标文件都已经挂载进DOSBox虚拟C盘下,我们可以使用命令 dir 来查看当前路径下的文件:
我们可以发现目标文件都在挂载目录下,这样就可以开始编译了。
下面,我们就将上篇本博文中已经写好的源程序文件 s1.asm编译一下。
使用MASM编译源程序文件其实很简单,只需要一个命令:
masm xxx.asm
解释:将指定的任意 .asm文件编译成 .obj文件。
下面我们就直接输入命令:masm s1.asm
我们发现执行后,光标在这里进行了停留等待输入,我们可以看到这是是等待你输入编译完成后生成的 .obj文件名,如果你不输入名字,那么生成的 .obj文件名将默认是 .asm文件名。这里我们选择默认,按Enter键跳过:
跳过后,光标又一次停留,这次提示你输入列表文件名,这个文件是编译器将源程序编译为目标文件的过程中产生的中间结果。这个中间结果相当于编译日志输出,我们这里可以让编译器不生成这个文件,直接按Enter键再次跳过:
这次光标又再次停留等待输入,而本次提示输入交叉引用文件名,这个交叉引用文件和上面的列表文件性质相同,也是编译器将源程序编译为目标文件的过程中产生的中间结果,这里我们还是不生成,直接按Enter键跳过:
终于,这次不是等待输入了,而是最终的编译结束。我们可以看到界面上显示:0 Warning Errors,0 Severe Errors,证明我们编译成功,并且生成了中间文件:s1.obj。这里我们使用 dir看下路径下是否存在生成的 .obj文件:
发现确实存在,说明我们编译成功了。我们当然可以去虚拟C盘挂载的路径下看下:
也是能够看到编译生成的 .obj文件,不过此时的 .obj还不能运行,还需要LINK工具连接后生成可执行文件才可。
上述就是整个完整的源程序文件编译过程,命令很简单,你可能会觉得有那么一点别扭,那里别扭呢?就是这个编译过程有点麻烦,要停下来让你输入三次文件名!有没有简化的流程呢?不想去挨着挨输入,干脆全部默认就行~
其实也是存在简化流程的,也很简单,只需要在命令最后面加上“;”即可:
masm xxx.asm;
解释:以简化的方式快速编译指定的任意源程序文件。; 的功能就是告诉编译器所有的待输入全部使用默认。
那么,我们就来验证一下,只是加上一个“;”,是否会如此的神奇呢?我们先把刚才编译生成的中间文件 s1.obj 文件删除掉,以方便观察和验证:
dir查看文件夹下内容,显示确实已删除,那么我们就输入:masm s1.asm;
这次我们发现,没有提示让你输入任何的字符,而是直接一步到位一气呵成完成了编译,而且给出了编译成功的提示。那么我们打印下文件夹内容,看看是否生成了中间文件:
OK,完全没有问题,准确输出了编译后的中间文件。看来“;”功能还是蛮使用的吼!
何为连接?要想回答这个问题,我们需要亲身去实际使用LINK工具,或许从中有一些感悟。
我们使用LINK工具来将中间文件通过一系列连接生成最终的可执行文件,为了保证连接过程成功,我们需要保证中间文件需要和LINK工具在同一文件目录下。
这里我们直接使用刚才编译好生成的中间文件为操作对象,LINK工具的使用同样十分简单,和MASM一样,只有一条连接命令:
link xxx.obj
解释:将指定的任意中间文件连接成可执行文件。
我们发现,此时光标停了下来等待输入连接完成后生成的可执行文件名,如果不输入的话那么将会默认中间文件的文件名,这里我们选择默认,直接按Enter键跳过继续执行:
此时光标再次停了下来等待输入,本次提示输入映像文件名,这个映像文件是连接程序LINK讲目标文件连接为可执行文件的过程中生成的中间结果,我们可以不做输入选择跳过,直接按Enter键继续执行:
光标又一次停下来等待输入,本次提示输入库文件名。什么是库文件?就是相当于C语言中引用的头文件,Java中import引用的各种包。在库文件中包含了一些可以调用的子程序,如果我们的程序中调用了某一个库文件中的子程序,那么在连接的时候,就要在这一步输入中输入库文件,这样就可以将这个库文件和目标文件连接在一起,生成可执行文件。所谓的连接,就是在这里体现。
事实上我们绝大数的汇编开发中不需要使用到库文件中的子程序,当然,我们本次的程序中也没有使用到某个库文件,所以在本次的输入中选择跳过,直接按Enter键跳过继续执行:
这次没有提示输入,而是执行完毕,我们发现有一条警告提示:no stack segment
这条警告告诉我们没有定义一个栈段,我们的源程序中确实只定义了一个段:CS代码段,并没有定义栈段,所以这里连接给出了警告提示。不过不用担心,这只是一个警告,并不意味着连接失败,我们可以使用dir查看下当前路径下是否生成了那个可执行文件:
OK,没有任何问题,生成了最终的可行性文件 .exe,这就说明警告的出现并不妨碍连接的结果。
我们现在可以思考,为什么没有定义栈段,连接会给出警告呢?很显然,这是LINK工具在告诉我们栈在程序中的重要性!
关于栈的讲解我们已经讲的比较充分了,相信大家在前面访问内存系列的博文中已经深深的感受到栈的重要性。如果我们没有在程序中定义一个栈段,那么将会由操作系统给我们分配一段空间当作栈段,通常这段空间是紧跟着代码后面,这就是默认的栈段。
虽然我们没有定义栈段,会有操作系统给我们擦屁股给出默认的栈段,但是操作系统并不知道我们程序中需要向栈中放多少个数据,默认的栈大小可能会放不下我们塞进来的数据,所以默认的栈段可能会存在栈顶越界的问题。为了避免出现可能存在的栈顶越界问题,所以LINK会给出没有栈段警告提示,就是为了给开发者提醒,避免程序出现栈顶越界。
在上述的连接过程中,我们也会感觉比较繁琐, 和MASM编译过程一样每次都要停下来让我们输入信息,那它也能不能像MASM简化流程那样,一切都自动默认跳过一口气执行到底呢?
答案是当然可以,和MASM简化流程一样,只需要在命令的后面加上“;”即可:
link s1.obj;
解释:以简化的方式快速连接指定的任意中间文件。; 的功能就是告诉连接器所有的待输入全部使用默认。
既然如此,那么我们就要验证一下,首先要将我们上面连接生成的可执行文件删掉,方便观察执行效果:
然后直接输入命令:link s1.obj;
我们发现果然和预期的一样,加上“;”之后,连接的过程一步到底没有提示需要输入任何信息。连接输入结果也是给出了一个没有定义栈段的警告提示,我们使用 dir查看下当前路径,看看是否生成了可执行文件:
准确输出了可执行文件,证明连接执行没有问题。
我们现在已经亲自动手将 .asm源程序文件编译连接生成了可执行文件 .exe,那么接下来的一步就是运行我们生成的可执行文件了,怎么运行呢?你可能会想到,直接双击 .exe文件不就运行了么!我们可以双击看下会发生什么:
出现了警告窗告诉我们无法运行该程序。废话,博主现在的电脑操作系统是64位,要是能运行一个16位的应用程序那就奇了怪了!
要知道,我们编译连接生成的这个可执行程序,它是16位的,所以还是只能在DOSBox给出的DOS环境下才能运行!在DOSBox中直接输入可执行程序文件名即可执行:
输入s1.exe后,我们按Enter键进行执行,会发现光标瞬间变成C:\>,而且s1.exe执行后没有任何结果,界面也没有任何的提示或者变化,给我们的感觉就好像是这个程序就没有执行一般!
也难怪,我们熟悉了高级语言的开发,编译运行后都会有各种输出来告诉我们程序的执行结果,思维定式就认为有输出有变化才证明程序得到了执行。但是,现在这里是汇编开发,是不会像高级语言那样人性化,我们在后面的汇编开发中,也要逐渐适应这种“睁眼瞎”的运行方式。
那么问题也就来了,s1.exe到底是否得到了运行?首先回答这个问题,s1.exe是肯定运行的,之所以没有任何变化,是因为我们的s1.exe程序中只是几条MOV指令和ADD指令,只是改变了寄存器的值,并未对界面的显示进行操作,所以我们看不到任何的变化和结果。
我们回想在学习高级语言的时候,例如C语言C++,相信第一个程序都是向界面输出“hello world”,我们看到界面上出现“hello world”,就表示我们的程序执行成功了。
汇编语言当然可以向界面输出字符显示,但是在编写上难度较为复杂,不适合作为汇编第一个程序来学习入门,所以我们现在学习的第一个汇编程序并未在界面输出显示,而是单纯的修改寄存器值。
我们说了这么多的MASM工具和LINK工具,那自然可不能少了Debug工具!在这里,Debug工具会有什么样的使用呢?
我们已经知道,s1.exe的运行是背后静默,并且没有任何的界面输出等其他可以证明运行发生的信息,那么,对于这种“睁眼瞎”的程序运行,我们该怎么了解并观察程序的运行呢?
没错,那就是Debug工具!我们可以通过Debug工具来具体观察到程序运行的整个过程,毕竟Debug工具的功能就是程序调试。
使用Debug调试程序,需要保证Debug工具和待调试的可执行文件在同一目录下,命令为:
debug xxx.exe
解释:调试执行任意指定的可执行程序
那么,我们直接输入命令:debug s1.exe:
我们发现执行完毕后,此时进入了Debug程序中,光标停留等待输入。我们使用R命令查看当前的各个寄存器值:
我们发现此时等待执行的汇编指令正是我们程序中的第一条汇编指令:mov ax,0123H,我们使用U命令来查看一下当前代码段中的内容:
我们会发现,s1.asm源程序中的程序已经被加载进了代码段中,这显然是Debug工具进行的处理。那么我们就可以使用T命令进行单步执行,来观察我们的程序s1.exe运行的过程:
这里我们可以很清楚的看到每条汇编指令执行完成后的寄存器变化,对程序的运行过程也明晰了很多。我们执行完四条汇编指令后,下面就是程序结束指令:mov ax,4c00H,int 21H。
执行到这里,我们先别莽撞执行,先使用T命令执行一条:
到这里,我们就不能使用T命令来执行了,而是转而使用另外一个命令。
不使用T命令,我们选择P命令,跳过命令。P,就是Pass。P命令的作用就是直接跳过或通过一段程序循环、中断,直接来到循环后或者中断后的指令。
我们先不解释为什么转而使用P命令,我们先输入P进行执行:
界面显示:Program terminated normally,表示程序正常结束。这时我们便可以使用Q命令,退出Debug程序,回到DOSBox的程序界面。Q命令,就是Quit,退出。
为什么非要使用P命令呢?答案是我们想要快速通过程序的结束处理过程。
int 21H就是结束一个程序的处理过程,当然也是一个程序。如果你使用T命令去执行,因为T命令是单步执行,而程序结束处理的程序中是有很多条汇编指令组成(起码有上百条之多),所以你就要连续输入T命令输入几百次甚至上千次,,,估计执行完,你的手也就废了~
所以我们需要使用P命令,直接通过程序结束过程,一步直达岂不快哉!
我们最后研究一下各个程序之间的运行问题。
首先,我们是在DOSBox的程序中,执行了MASM.exe程序编译源程序文件,执行了LINK.exe程序连接中间文件,它们两个执行结束后,CPU的权柄全部都自动回到了DOSBox程序中,包括我们在DOSBox中执行生成的可执行程序,最后无一例外都最终回到了DOSBox。
我们先搞清楚什么叫做“权柄”?
权柄是指程序占有CPU,得到CPU执行的时刻。我们都知道CPU是无法同时执行两条指令的,同一时刻下它只能执行一条汇编语句。当有多个程序的时候亦是如此,就会出现程序挂起或者执行的状态。当某个程序得到CPU权柄处于执行的时候,那么其他的程序就是挂起的状态。
1、以执行MASM.exe为例:
CPU的权柄首先是DOSBox.exe,然后跳到了MASM.exe,MASM.exe执行结束后权柄再次回落到DOSBox.exe。
顺序为:DOSBox.exe ----> MASM.exe ----> DOSBox.exe
2、以Debug调试执行s1.exe程序为例:
CPU的权柄首先是DOSBox.exe,然后跳到Debug.exe,因为T命令的存在,所以权柄会在Debug.exe和s1.exe之间反复来回跳转,最后s1.exe执行完毕,权柄最终落在Debug.exe,此时需要使用Q命令退出(结束)Debug.exe,最终权柄回落到DOSBox.exe
顺序:如下图所示:
下面我们思考一个问题:如果我们的程序最后没有写上程序结束指令:MOV AX,4C00H,INT 21H,那么如果在DOSBox中执行,会发生什么样的情况呢?
感兴趣的小伙伴可以尝试一下,这里我们直接说答案。答案就是,由于没有正确的程序结束返回,所以CPU权柄将无法重新回落到DOSBox中,界面上的效果就是DOSBox程序无法进行任何的输入和操作。
这也就告诉我们,程序的最后一定要加上结束指令,做到准确返回,避免出现程序崩溃异常的问题。
在本篇博文中,我们学习并掌握了MASM工具和LINK工具的使用,并且亲自运行了第一个汇编程序,熟悉了通过Debug对程序进行调试的过程,在此过程中学习了P命令和Q命令的运用。
那么在接下来的博文中,我们将学习如何在代码中配合DS进行内存的访问操作。
感谢围观,转发分享请标明出处,谢谢!