04.第一个程序
第一个程序
前言
终于可以编写第一个程序,有了前面的这些基础,我们可以轻易的编写一个汇编程序。但为了透彻理解一个完整的程序,我们将经历一个漫长的过程
一个源程序从写出到执行的过程
- 编写汇编源程序 这一步的工作结果会生成一个存储汇编源程序的文本文件
- 对源程序进行编译连接 使用汇编语言编译程序对源程序中的文件进行编译,产生目标文件;再用连接程序对目标文件进行连接,生成可在操作系统中直接运行的可执行文件
可执行文件包括两部分内容:- 程序(源程序翻译的机器码)和数据(源程序定义的数据)
- 相关的描述信息(比如:程序有多大,要占用多大的内存空间)
这一步的工作结果,产生了一个可以在操作系统上运行的可执行程序
- 执行可执行程序
操作系统根据可执行程序中描述信息,将可执行程序中机器码和数据加载进入内存,并进行相应的初始化(将CS:IP指针指向第一条要执行的指令),然后由CPU执行程序
源程序
下面是一个简单的汇编程序,我们分析一下
assume cs:codeseg
codeseg segment
mov ax, 0123H
mov bx, 0456H
add ax, bx
add ax, ax
mov ax, 4c00H
int 21H
codeseg ends
end
伪指令
在汇编源程序中,包含两种指令,一种是伪指令,一种是汇编指令。汇编指令是有对应机器码的指令,可以被编译成机器指令,最终被 CPU 执行。而伪指令没有对应的机器指令,最终不被 CPU 执行。那么伪指令由谁来执行呢?伪指令是由编译器来执行的指令,编译器根据伪指令来进行相应的编译工作
那么,我们来分析一下上述程序有哪些指令是伪指令?
- XXX segment
XXX ends
segment 和 ends 是一对成对使用的伪指令。功能是定义一个段,segment 表示段开始,ends 表示段结束。一个段必须有一个名称来标识。一个汇编程序由多个段组成,这些段被用来存放代码,数据或当作栈空间来使用。我们前面提到的 数据段 代码段 栈段 都由这个伪指令来指定。一个汇编程序至少有一个段 代码段 存放代码。 - end
end 是一个汇编程序的结束标志,编译器在编译汇编程序的过程中,如果碰到了伪指令 end ,就结束对源程序的编译。因此,我们在书写汇编程序时,必须加上结束标志 end,不然我们的编译器在编译程序时,无法知道程序在何时结束 - assume
这段伪指令的含义是 “假设”。它假设某一个段寄存器与程序中某一个 segment ... ends 定义的段相关联。通过 assume 说明这种关联,在需要的情况下,编译程序将段寄存器与某一个具体的段相联系。例如:CS 和 代码段相关联,DS 和数据段相关联,SS 和栈段相关联
源程序中的“程序”
我们将源程序中的所有内容称之为源程序,将源程序中最终由计算机执行,处理的指令或数据,称为程序
标号
汇编源程序中,除了一些汇编指令和伪指令以外,还有一些标号,比如 “codeseg” 。一个标号指代了一个地址,比如codeseg 在 segment 的前面,作为一个段的名称,这个段最终被编译,连接程序处理成一个段的段地址
程序的结构
我们需要书写出能让编译器进行编译的源程序,这样的源程序应该具备起码的结构
- 我们首先定义一个代码段
- 在代码段中书写我们的汇编指令
- 添加结束标志 end
- 将 代码段 和 CS 寄存器关联起来
- 程序返回
我们的程序最先以汇编指令的形式存在源程序中,经编译,连接转成机器码,存储在可执行文件中,那么,它怎么得到运行的呢
下面,我们在 DOS(一个单任务操作系统)的基础上,简单的讨论一下这个问题- 一个程序 P2 在可执行文件中,则必须要要有一个正在运行的程序 P1,将 P2 从可执行文件中加载进入内存后,将 CPU 的控制权交给 P2,P2 才能正常运行 。P2 正常运行,P1 暂停运行
- 而当 P2 运行完毕后,应该将 CPU 的控制权交还给使它得以运行的程序 P1,此后,P1 才能继续运行
- 一个程序结束运行后,将 CPU 的控制权交还给使他运行的程序,我们称这个过程叫做 程序返回
我们在汇编程序中是如何实现程序返回的?
mov ax, 4c00H
int 21H
通过以上几条指令实现的,目前我们不必理解 int 21H 的含义,只需知道这几条指令合在一起是程序返回
语法错误和逻辑错误
熟悉C语言的应该很清楚这两个的概念
编译源程序
- 下载 MASM 编译工具
- 书写一个汇编程序 04-1.asm
- 使用 DosBox 运行 masm.exe

上图通过 masm + 04-1.asm 会生成 04-1.obj 目标文件

上图通过 link 进行连接,首先选择目标文件名称,然后定义生成的 可执行文件名称,.lib 代表是否要链接库(相当于C语言里面的动静态库),其他的都是一些中间文件,我们在汇编这们课程中不予讨论
以简化的方式进行编译和连接
输入以下两条命令:
- masm 04-1;
- link 04-1;
04-1.exe 程序的执行
直接在dos窗口输入 04-1 即可
谁将可执行文件中的程序装在进入内存并让它执行
操作系统的外壳
操作系统是由多个功能模块组成的庞大,复杂的软件系统。任何一个通用的操作系统,都要提供一个称为 shell(外壳)的程序,用户使用这个程序来操作计算机进行工作
DOS 中有一个程序 command.com,这个程序在 DOS 中称之为命令解释器,也就是DOS中的shell
DOS启动后,先完成其他重要的初始化工作,然后运行 command.com,command.com 运行后,执行完其他的相关任务后,在屏幕上显示出由当前盘符和当前路径组成的提示符,比如 "C:" 等,然后等待用户的输入
用户可以输入“dir”等命令运行,用户执行玩这些命令后,再次显示由当前盘符和当前路径组成的提示符,如果用户要执行一个可执行程序,则输入该可执行程序的名称,command首先根据文件名找到可执行文件,然后将这个可执行文件中载入内存,设置 CS:IP 为程序的入口点。此后,command 暂停运行,CPU 运行程序。程序结束后,返回到command 中,在屏幕上显示出由当前盘符和当前路径组成的提示符,比如 "C:" 等,然后等待用户的输入
汇编程序从写出到执行的过程
编程 -> 1.asm -> 编译(masm) -> 1.obj -> 连接(link) -> 1.exe -> 加载(command) -> 内存中的程序 -> 运行
程序执行过程跟踪
DOS中.exe文件中程序的加载过程

- 找到一块起始地址为 SA:0(即骑士地址的偏移地址为0的容量足够大的空闲内存区)
- 在这段内存区的前256个字节处,创建一个称为程序前缀(PSP)的数据区,DOS 要利用 PSP 和被加载程序进行通信
- 从PSP后面,将程序装入,程序的起始地址 SA+10H:0
- 将该内存区的段地址放入ds中,初始化其他寄存器后,设置CS:IP指向程序的入口
调试程序 使用 debug + 可执行程序名称,以及前面提到的指令进行调试
一定要自己手动调试一下程序,然后查看寄存器的值
参考
<<汇编语言第四版>>-王爽
浙公网安备 33010602011771号