首页 > 代码库 > 第一个汇编程序

第一个汇编程序

本文将开始编写完整的汇编语言程序,用编译器将它们编译成为可执行文件(如:*.exe文件),在操作系统中运行。

本文地址:http://www.cnblogs.com/archimedes/p/assembly-first-program.html,转载请注明源地址。

1、一个源程序从写出到执行的过程

一个汇编语言程序从写出到最终执行的简要过程:

   编写--〉编译--〉连接--〉执行

使用文本编辑器(如Edit、记事本等),用汇编语言编写汇编源程序。
使用汇编语言编译程序对源程序文件中的源程序进行编译,产生目标文件;再用连接程序对目标文件进行连接,生成可在操作系统中直接运行的可执行文件。
可执行文件中包含两部分内容:
程序(从原程序中的汇编指令翻译过来的机器码)和数据(源程序中定义的数据)
相关的描述信息(比如:程序有多大、要占多少内存空间等)
在操作系统中,执行可执行文件中的程序。
操作系统依照可执行文件中的描述信息,将可执行文件中的机器码和数据加载入内存,并进行相关的初始化(比如:设置CS:IP指向第一条要执行的指令),然后由CPU执行程序。

2、源程序

先看一段代码:

assum cs:codesgcodesg segmentstart: mov ax,0123H          mov bx,0456H          add ax,bx          add ax,ax          mov ax,4c00h          int 21hcodesg endsend
segment和ends是一对成对使用的伪指令,这是在写可被编译器编译的汇编程序时,必须要用到的一对伪指令。segment和ends的功能是定义一个段,segment说明一个段开始,ends 说明一个段结束。
一个段必须有一个名称来标识,使用格式为:

段名 segment

段名 ends

一个汇编程序是由多个段组成的,这些段被用来存放代码、数据或当作栈空间来使用。一个有意义的汇编程序中至少要有一个段,这个段用来存放代码。
end 是一个汇编程序的结束标记,编译器在编译汇编程序的过程中,如果碰到了伪指令 end,就结束对源程序的编译。如果程序写完了,要在结尾处加上伪指令end 。否则,编译器在编译程序时,无法知道程序在何处结束。
注意:不要搞混了end和ends。
nassume:含义为“假设”。它假设某一段寄存器和程序中的某一个用 segment … ends 定义的段相关联。通过assume说明这种关联,在需要的情况下 ,编译程序可以将段寄存器和某一个具体的段相联系。
程序经编译连接后变为机器码:
标号:
一个标号指代了一个地址。
codesg:放在segment的前面,作为一个段的名称,这个段的名称最终将被编译、连接程序处理为一个段的段地址。
任务:编程运算 2^3
assume cs:abcabc segmentmov ax,2add ax,axadd ax,axabc endsend 

 应该在程序的末尾添加返回的程序段。

mov ax,4c00Hint 21H

这两条指令所实现的功能就是程序返回。

3、编辑源程序

进入DOS方式,运行edit,在其中编辑程序,如下图所示:

 4、编译

进入DOS方式,进入 C:\masm 目录,运行masm.exe。如果源程序文件不是以 asm 为扩展名的话,就要输入它的全名。比如p1.txt。在输入源程序文件名的时候一定要指明它所在的路径。如果文件就在当前路径下,只输入文件名就可以。

输入要编译的源文件文件名后,按 Enter键。目标文件(*.obj)是我们对一个源程序进行编译要得到的最终结果。编译程序默认要输出的目标文件名为1.obj,所以可以不必再另行指定文件名。

编译程序提示输入交叉引用文件的名称。

这个文件同列表文件一样,是编译器将源程序编译为目标文件过程中产生的中间结果。
可以不生成这个文件,直接按 Enter 键即可。

一般来说,有两类错误使我们得不到所期望的目标文件:

(1)我们程序中有“Severe Errors”;

(2)找不到所给出的源程序文件。

5、连接

在对源程序进行编译得到目标文件后,我们需要对目标文件进行连接,从而得到可执行文件。继续上面的过程,我们再将C:\masm\1.obj连接为C:\masm\1.exe。

进入DOS方式,进入C:\masm目录,运行link.exe。如果目标文件不是以obj为扩展名的话,就要输入它的全名。比如:p1.bin。在输入目标文件名的时候,要注意指明它所在的路径。这里,我们要连接的文件是当前路径下1.obj,所以此处输入“1”。

输入要连接的目标文件名后,按Enter键。可执行文件是我们对一个程序进行连接要得到的最终结果。连接程序默认要输出的可执行文件名为 1.EXE ,所以可以不必再另行指定文件名。我们直接按 Enter 键,使用连接程序设定的可执行文件名。

映像文件是连接程序将目标文件连接为可执行文件过程中产生的中间结果。可以不生成这个文件,直接按 Enter 键即可。

连接程序提示输入库文件的名称。

库文件里包含了一些可以调用的子程序,如果我们的程序中调用了某一个库文件中的子程序,就需要在连接的时候,将这个库文件和我们的目标文件连接到一起,生成可执行文件。

如果没有调用任何子程序,直接按Enter键即可。

连接的作用:

当源程序很大时,可以将它分为多个源程序文件来编译,每个源程序编译成为目标文件后,再用连接程序将它们连接到一起,生成一个可执行文件;

程序中调用了某个库文件中的子程序,需要将这个库文件和该程序生成的目标文件连接到一起,生成一个可执行文件;

一个源程序编译后,得到了存有机器码的目标文件,目标文件中的有些内容还不能直接用来生成可执行文件,连接程序将这此内容处理为最终的可执行信息。

所以,在只有一个源程序文件,而又不需要调用某个库中的子程序的情况下,也必须用连接程序对目标文件进行处理,生成可执行文件。

6、以简化的方式进行编译和连接

我们编译、连接的最终目的是用源程序文件生成可执行文件。在这个过程中所产生的中间文件都可以忽略。我们可以用一种较为简捷的方式进行编译、连接。

编译:

连接:

7、1.exe的执行

现在,终于将我们的第一个汇编程序加工成了一个可在操作系统下执行的程序文件。1.exe的执行情况:

我们的程序没有像显示器输出任何信息。程序只是做了一些将数据送入寄存器和加法的操作,而这些事情,我们不可能从显示屏上看出来。程序执行完成后,返回,屏幕上再次出现操作系统的提示符。

8、可执行文件中的程序装入内存并运行的原理

DOS中有一个程序command.com ,这个程序在 DOS 中称为命令解释器,也就是DOS系统的shell。

(1)我们在DOS中直接执行 1.exe 时,是正在运行的command将1.exe中的程序加载入内存。

(2)command设置CPU的CS:IP指向程序的第一条指令(即程序的入口),从而使程序得以运行。

(3)程序运行结束后,返回到command中,CPU继续运行command。

汇编程序从写出到执行的过程:

9、程序执行过程的跟踪

为了观察程序的运行过程 ,我们可以使用Debug。

Debug 可以将程序加载入内存,设置CS:IP指向程序的入口,但Debug并不放弃对CPU 的控制,这样,我们就可以使用Debug 的相关命令来单步执行程序 ,查看每条指令指令的执行结果。

EXE文件中的程序的加载过程:

总结:

程序加载后,ds中存放着程序所在内存区的段地址,这个内存区的偏移地址为 0 ,则程序所在的内存区的地址为:ds:0;这个内存区的前256 个字节中存放的是PSP,dos用来和程序进行通信。

从 256字节处向后的空间存放的是程序。所以,我们从ds中可以得到PSP的段地址SA,PSP的偏移地址为 0,则物理地址为SA×16+0。

因为PSP占256(100H)字节,所以程序的物理地址是:

SA×16+0+256= SA×16+16×16=(SA+16)×16+0

可用段地址和偏移地址表示为:SA+10:0

用U命令查看一下其他指令:

用T命令但不执行程序中的每一条指令,并观察每条指令的执行结果,到了 int 21,我们要用P命令执行:

int 21 执行后,显示“Program terminated normally”,返回到Debug中。表示程序正常结束。

注意,要使用P命令执行int 21。

参考资料

《汇编语言》--王爽

 

第一个汇编程序