目录
程序简述
之前聊寄存器和内存的一些事情,是在debug调试模式下写一些指令。下面我们简单看看编写完整的汇编语言程序是个怎样的过程。
源程序示例:
assume cs:codesgcodesg segment mov ax,0123h mov bx,0456h add ax,bx add ax,ax mov ax,4c00h int 21hcodesg endsend
源程序里有两种指令,一种是汇编指令/机器指令,由CPU执行;
另一种是伪指令,由编译器识别。
伪指令
在上面的源码中出现了3类:
①
XXX 段名 segmentXXX 段名 ends
segment 和 ends 是成对使用的伪指令,来定义一个段。前者表示段的开始,后者表示段的结束。当然定义一个段一定要有名字。
②end 汇编程序结束标志
与ends(end segment)区分开
③assume:将有特定用途的段和相关的段寄存器关联起来
上述程序中的 assume cs(寄存器名):codesg(段名) 就是把一个代码段和段寄存器联系起来了。
源程序和程序:
这里有必要区分,即:
源程序文件中的所有内容共同成为源程序,而最终由计算机执行处理的指令、数据则称为 程序。
所以两者的关键在于伪指令的包括与否。
(程序首先是源程序中的汇编指令,之后转变为机器码存在.exe可执行文件中)
关于标号:
上面的栈名 codesg实际上作为一个栈的名称,从内存角度看 指代了一个地址,我们称之为标号。
源程序是由一个个段组成的。
关于程序返回
对于DOS,这样一个单任务操作系统,一个程序的运行可以概括如下:
一个程序P2在可执行文件中要想运行,就必须有一个正在运行的程序P1,让P1将P2从可执行文件中加载进入内存,再由P1将CPU控制权交给P2,P2得以运行,P1暂停运行。
P2运行完毕,CPU控制权还给P1,P1继续运行。
上述程序结束,交还CPU控制权的过程,我们称为 程序返回。
这在上述源程序中体现为:
mov ax,4c00h int 21h
作为汇编指令,由CPU执行。这和end/ends 两个伪指令是完全不同的。
——————————————————————————————
BX和LOOP
BX
我们知道,要完整表示一个内存单元,需要两种信息:
①内存单元的地址 ②内存单元的长度(这一点通常由类型可知)
BX本身作为一个寄存器,存储着数据,而
[BX]就表示一个内存单元,它的偏移地址在BX中,配合段地址在段寄存器ds中。
所以操作的内存空间即起始地址为DS:BX,长度为数据类型
eg:
mov ax , [bx]
LOOP
就是一循环语句。
只是循环次数由寄存器CX里的值是否为0决定,所以我们循环前要给cx赋值。
我们直接来看它的格式:
mov cx , 次数 s: 循环语句loop s#这里的S只是一个循环标识符,代表地址。你可以另行命名
段前缀
汇编指令里,我们可以显式的指明内存单元的段地址,像DS: CS: SS: ES:
这些称之为段前缀。
段前缀可以配合不同寄存器,简化很多涉及到不同段地址的代码。
一段安全的空间
0:200~02ff 没系统/程序数据。
——————————————————————————————
dosbox
dos程序运行
注意,一段代码指令的运行,有debug和汇编编译器masm两种处理方式。
debug是我们通过-a逐步填写的。
masm则是提前编辑好汇编指令txt文件去编译处理。
1.像masm模式下,十六进制数据后要加H,而debug则直接用数字表示;
2.汇编源程序里,数据不能以字母开头。像FFFF这样的数据,前面要加上数字0。
debug则不然
3.对于同样的 mov bl,[idata] ,debug视 [idata] 为内存单元 DS:IDATA;masm则直接认为[idata]为一个常量idata了。
(当然,masm模式下指代内存单元,前面要加上ds,即ds:[idata])
关于操作系统的外壳 shell
操作系统通常提供一个shell程序,让用户能够利用它操作计算机系统 进行工作。
对于DOS,它的shell是command.com,称为命令解释器。
Dos启动,先初始化,然后运行command.com。运行完毕,由command.com在屏幕上显示盘符和当前路径组成的提示符,等待用户输入。用户输入的所有指令由command执行。
执行过程需要的文件也由command处理。
主
注意:
1.这里的shell — command.com内容和上面的DOS单任务操作系统的程序运行可以相互补充来看。
2.COMMAND往往充当程序P1的身份,同时COMMOND也负责程序运行过程中,设置CPU的CS:IP指向程序入口(程序的第一条指令),使该程序正常运行。
3.debug模式下,command会加载运行debug,由debug负责加载运行用户程序的一些工作。
一个源程序从写出到执行要经历 的过程:
编写源程序(txt→asm)——编译(asm→obj)——连接(obj→exe)——生成exe可执行文件(程序)
涉及到 编译指令 masm + 被编译asm文件 ,连接指令 link +被连接obj文件
拓展
中间会有很多中间文件:
source listing [NUL.LST] : 列表文件
cross-reference[NUL.CRF]:交叉引用文件
List File [NUL.MAP]:映像文件
Libraries[.LIB]:提示输入库文件名称
(如果程序调用了某一库文件子程序,需要在连接时将该库文件和目标文件连接在一起)
这些目标文件在非必要情况下可以直接ENTER 跳过。
中间过程的所有文件(包括目标文件)都可以指定名称甚至路径。
程序加载过程(exe)
1.找一段起始地址SA:0000的容量足够的空闲内存。
2.在该内存区前256字节中,创建一个PSP(程序段前缀)的数据区。DOS利用PSP和被加载的程序进行通信。
3.在该内存区PSP后(256字节处)装入程序。程序起始地址:SA+10H:0
(PSP区和程序区物理地址连续,但是段地址截然不同)
这里有一步重定位工作没有讲解,不讨论。
一个好习惯
把内存单元里的数据存放在某个寄存器DX中,要考虑以下基本问题:
1.运算后的结果是否超出寄存器dx 所能存储的范围?
2.我们能否将该内存单元中的数据直接累加到dx中?
3.内存单元和寄存器之间的数据类型是否匹配?
即,结果大小的越界和数据类型的匹配 问题