源程序
第一步:编写汇编源程序
使用文本编辑器(记事本等),用汇编语言来编写汇编语言
第二步:编译源程序
使用汇编语言的编译程序对源程序进行编译,产生目标文件
再用连接程序对目标程序进行连接,生成可执行文件
可执行分界线包含两部分内容:
程序和数据
相关的描述信息
第三步:执行可执行文件中的程序
1 assume cs:codesg 2 codesg segment 3 mov ax,0123H 4 mov bx,0456H 5 add ax,bx 6 add ax, ax 7 mov ax,4c0OH 8 int 21H 9 codesg ends 10 end
在汇编源程序中包含两种指令,一种是汇编指令,一种是伪指令
汇编指令有对应的机器码,可以被编译为机器指令被cpu执行
伪指令没有对应的机器指令,不能被cpu执行
伪指令由编辑器执行
伪指令
segment和ends
segment和ends是一对 成对使用的伪指令,这是在写可被编译器编译的汇编程序时,必须要用到的一对伪指令。
segment和ends的功能是定义一个段
segment说明一个段开始
ends说明一个段结束
一个段必须有一个名称来标识,使用格式为:
1 段名 segment 2 : 3 段名 ends
一个汇编程序是由多个段组成的,一个有意义的汇编程序至少有一个存放代码的段
end
end是一个汇编程序的结束标记,编译器遇到end就会结束对源程序的编译
注意end后面没有s,ends是段结束
assume
含义为"假设"
它假设某一段寄存器 和 程序中的某一个用segment...ends定义的段相关联。
通过assume 说明这种关联,在需要的情况下,编译程序可以将段寄存器和某一个具体的段相联系。
不需要深入理解,只要记着用assume将有特定用途的段和相关的段寄存器关联起来就行
源程序由计算机编译成程序
程序以汇编指令的形式粗在于源程序中
编译连接后变成机器码,存储在可执行文件里
标号
一个标号代表了一个地址
比如
codesg:放在segment前面
作为一个段的名称
这个段的名称最终将被编译、连接程序处理为一个段地址
程序的结构
例题:运算2^3
先定义一个段,名为abc
1 abc segment 2 : 3 abc ends
之后在段中写入汇编指令
1 abc segment 2 3 mov ax,2 4 add ax,ax 5 add ax,ax 6 7 abc ends
段写完后要指出程序在何时结束
1 abc segment 2 3 mov ax,2 4 add ax,ax 5 add ax,ax 6 7 abc ends 8 end 9
abc作为代码段所以要让cs指向abc
1 abc segment 2 mov ax,2 3 add ax,ax 4 add ax,ax 5 abc ends 6 7 end
程序返回
DOS是一个单任务操作系统
一个程序P2在可执行文件中则必须有一个正在运行的程序P1
将P2从可执行文件中加载入内存后
将CPU控制权交结P2,P2才能得运行·P2开始运行后,P1暂停运行
而当P2运行完毕后,应该将CPU的控制权交还给使它得以运行的程序Pl
此后P1继续运行
程序返回 就是将CPU的控制权交还给使它得以运行的程序,
程序的末尾添加返回的程序段
mov ax, 4c00H int 21H
这两条指令所实现的功能就是程序返回
暂时不需要理解原因,只需要知道这两条指令可以实现程序返回
语法错误和逻辑错误
语法错误:
编译时被编译器发现的错误
逻辑错误:
编译后,运行时发生的错误是逻辑错误
编译
使用dosbox编译
在编辑器里写号源代码,后缀名是asm
存放在c:/code(dosbox默认目录)
打开dosbox,输入dir,就能显示出文件(文件夹变动,dosbox必须重启才能在里面看到)
开始编译
输入masm + 名字.asm
或者这样,输入masm,在第一行后面写文件名(如果文件后缀是asm,则可省略不写)
后面都可按enter跳过,或者输入路径,指定生成位置
最终生成目标文件.obj 其中.list(列表文件) .crf(交叉引用文件)是中间结果,可忽略
连接
使用dosbox
输入link
类似编译
只要在第一行输入文件名即可,后面可跳过
最后一行报错是 没有栈段,可忽略
最后生成 .EXE文件
还有种简洁的方法
masm 1;
link 1;
自动跳过中间文件
直接生成目标文件
执行
在dosbox里执行文件,win10里不支持这种格式了
执行没结果,很正常,因为没有写向显示器输出信息的命令
后面再说
如果在想用debug跟踪执行
只要 debug xxx.exe 即可
然后输入t,输入一次执行一行
谁将可执行文件中的程序装载进入内存并使它运行?
前文提过
在DOS中,可执行文件中的程序 a 若要运行
必须有一个正在运行的程序 b,
将 a 从可执行文件中加载入内存,将CPU的控制权交给它,a 才能得以运行;
当 a 运行完毕后,应该将CPU的控制权交还给使它得以运行的程序 b。
按照上面的原理,再来看一下 上面的 执行 中的 1.exe的执行过程
(1)在提示符 “c:>” 后面输入可执行文件的名字“1”,按Enter键。
这时,请思考问题1。
(2)1.exe中的程序运行
(3)运行结束,返回,再次显示提示符 “c:>” 。请思考问题2。.
问题1
此时,有一个正在运行的程序将1.exe中的程序加载入内存,这个正在运行的程序是
什么?它将程序加载入内存后,如何使程序得以运行?
问题2
程序运行结束后,返回到哪里?
如果你对DOS有比较深入的了解,那么,很容易回答问题4.1、问题4.2中所提出的
问题。如果没有这种了解,可以先阅读下面的内容。
操作系统的外壳
操作系统是由多个功能模块组成的庞大、复杂的软件系统。
任何通用的操作系统,都要提供一个称为shell(外壳)的程序
用户(操作人员)使用这个程序来操作计算机系统进行工作。
DOS中有一个程序command.com,这个程序在DOS中称为命令解释器,也就是DOS系统的shell。
DOS启动时,先完成其他重要的初始化工作,然后运行command.com
command.com运行后,执行完其他的相关任务后,在屏幕上显示出由当前盘符和当前路径组成的提示符
比如:“c:” 或 “c:windows”等,然后等待用户的输入。
用户可以输入所要执行的命令,比如,cd、dir、type等,这些命令由command执行
command执行完这些命令后,再次显示由当前盘符和当前路径组成的提示符,等待用户的输入。
如果用户要执行一个程序,则输入该程序的可执行文件的名称
command首先根据文件名找到可执行文件
然后将这个可执行文件中的程序加载入内存
设置CS:IP 指向程序的入口
此后,command 暂停运行,CPU 运行程序
程序运行结束后,返回到command 中,command 再次显示由当前盘符和当前路径组成的提示符,等待用户的输入。
在 DOS中,command处理各种输入命令或要执行的程序的文件名。
我们就是通过command来进行工作的
现在我们可以回答 问题1和2
问题1:
是正在运行的command,将1.exe中的程序加载入内存;
command 设置CPU的 CS:IP指向程序的第一条指令(即程序的入口),从而使程序得以运行;
问题2
程序运行结束后,返回到command 中,CPU继续运行command。
程序执行过程的跟踪
可以用Debug来跟踪一个程序的运行过程
对于隐藏较深的错误,就必须对程序的执行过程进行跟踪分析才容易发现。
以1.exe为例,讲解如何用Debug 对程序的执行过程进行跟踪。
现在我们知道,在DOS中运行一个程序的时候,是由command 将程序从可执行文件中加载入内存,并使其得以执行。
而Debug可以将程序加载入内存,设置CS:IP指向程序的入口
但Debug 并不放弃对CPU的控制
这样,我们就可以使用Debug的相关命令来单步执行程序,查看每一条指令的执行结果。
输入
DOS中.exe文件中的程序的加载过程
注意,有一步称为重定位的工作没有讲解,因为这个问题和操作系统的关系较大,不作讨论
从上图中我们知道以下的信息。
(1)程序加载后,ds 中存放着程序所在内存区的段地址,这个内存区的偏移地址为0,则程序所在的内存区的地址为ds:0;
(2)这个内存区的前256个字节中存放的是PSP,DOS用来和程序进行通信。
从256字节处向后的空间存放的是程序。
所以,从ds中可以得到PSP的段地址SA,PSP的偏移地址为0,则物理地址为SA×16+0。
因为PSP占256(100H)字节,所以程序的物理地址是:
SA×16+0+256 = SA×16+16×16+0= (SA+16) × 16 + 0
可用段地址和偏移地址表示为:SA+10H:0。
上图中,DS = 075C,IP = 0,CS:IP 指向程序的第一条指令
所以程序的地址为075C + 10 :0 即 076C : 0
cs里就是076C
注意,debug里默认数据用16进制表示
使用u查看指令
输入t开始单步执行,观察每一步指令的执行结果,直到int 21
用p命令执行int 21
上图中 int 21 执行后,显示出“Program terminated normally” 返回到 Debug中。
表示程序正常结束。
注意,要使用Р命令执行int 21。
这里不必考虑是为什么,只要记住这一点就可以了。
需要注意的是,在这里是Debug 将程序加载入内存
所以程序运行结束后要返回到Debug 中
使用 q 命令退出 Debug,将返回到command 中
因为Debug 是由command加载运行的。
在DOS中用“debug 1.exe”运行Debug对1.exe进行跟踪时
程序加载的顺序是:
command加载Debug
Debug加载1.exe
返回的顺序是:
从 1.exe中的程序返回到Debug
从Debug 返回到command
参考: 王爽 - 汇编语言 和 小甲鱼零基础汇编