这次实验的主要内容是了解汇编程序的从编程到运行的整个流程。整个流程分为:编程->编译->链接->运行(测试)。当中我们会用到:文本编辑器,masm(编译用的),link(链接用的),debug(跟踪调试用)。
首先我们先编写一个汇编程序。打开记事本写入如下指令,保存为t1.txt。如图。
保存如下图。(Macos 下默认的文本文件格式为 RTF,Windows 下为TXT。)
由于 macOS的文本编辑器编辑完会在文件前自动加上一些格式设置(可能是我之前设置过文本编辑器的原因),会导致编译失败。但是,在文本编辑器中又看不到,所以我们需要通过控制台中将这些设置删除。(win 10 下没有这个问题。早知道这样我就直接用命令行写了。给上一张写完的 t1 在控制台中的图。)
将除了之前写入的代码的所有字符全部去掉。使用 cd 命令转到源文件所在的文件夹,然后用 nano 或者 vim 打开进行编辑。如图。
之后如图。nano 的操作比较简单,使用上下左右和退格键即可,必要时可以按 F1查看帮助。
清除完之后,按 Ctrl+X,然后输入 y,然后回车保存退出。(给出清理后的源文件截图)
PS:有人可能会说,为什么不改后缀?其实改不改没有影响,大家可以自己试一下。
接下来的操作我觉得是比较重要的吧。我周围的同学对于工作目录不是特别了解。这个原因造成了他们编译时找不到源文件从而报错。在此和大家说一下工作目录的问题。
在 dosbox 运行时,我们会有一个 mount 操作(可能有人说没有,因为在开始使用的时候,你配置过了环境,让 dosbox 启动时自动帮你完成了。),如下图。
这个语句是什么意思呢?这个语句实际上完成的是 dosbox 中文件(文件夹)到实际操作系统中文件(文件夹)的一个映射。dosbox,它是在我们的电脑上模拟了 dos 环境,那么,我们要在其中使用一些文件时,如何让它知道文件在哪呢,那这个映射就发挥作用了。(可以理解成给它起了个别名)
那么它和编译失败有什么关系呢?你在实际操作系统中写的源文件在哪(上面的源文件我保存在了桌面,你想必也知道自己保存在了哪),而 dos它不知道。在上图的 dos 环境下,系统只知道,Z 盘(dos 所在的盘,可以说是系统盘)和我 mount (挂载)上去的C 盘。然而这两个目录下并没有源文件,必然编译失败。所以我们要做的就是让他知道这个文件在哪。
怎么让它知道?有两个办法:
第一个,把这个文件直接丢到之前 mount 的那个文件夹里(图中为 masm ,路径为 /users/willeasun/masm/,每个人都不同。)
第二个,把源文件或者源文件所在的文件夹也 mount 上去,然后把路径给 masm 和 link 以及 debug 即可。
下面演示一下两个方法。如图。
第一种比较简单,丢进mount 好的文件夹,然后编译就完事了。
输入 masm t1.rtf ,然后回车。(想偷懒的可以打完 mount t1 然后按 tab 键,由系统自动补全。只要后缀系统能够识别,就可以用)
第二种,首先用 mount 命令挂载源文件或者源文件所在的文件夹。
然后用 masm 进行编译。 输入 masm o:\t1.rtf ,回车。
当然你也可以把当前的目录切换到刚刚 mount 的那个盘(图中的 O 盘),然后输入 c:\masm t1.rtf , 然后回车。
注:这里要注意,编译生成的目标文件保存在你的当前工作目录。我当前在 O 盘就保存在 O 盘,在 C 盘就保存在 C 盘。
不管你使用哪种方法,你需要知道你要编译或者链接又或者调试的文件在哪,然后给出对应的路径即可。
接下来是链接,由此开始,我只演示一种方法。
输入 link t1.obj,然后回车。
没有报错,只有警告。此时当前目录下会生成一个exe文件,名字如果你没改,那就是t1,如果你在编译或者链接的时候更改过,那就是你更改的名字。
我们来运行一下这个程序。
输入 t1.exe 。
屏幕上出现了 36 。
我们代码中有3和6 ,我们更改了再看看。这次改为4 和 8 。
再次编译,链接,运行,这次的结果如图。
接下来,我们用 debug 调试它。输入 debug t1.exe ,然后回车。下面会出现一个熟悉的小短线,说明我们成功进入了 debug。
然后我们来单步调试它。看看ds 和cs 之间有没有什么关系。
他们两之间满足:(cs)=(ds)+10H 。
我们看看去看看075a 有些什么东西。
开头有个 int 20 ,百度了一下,是程序中断的意思。应该是
接下来,我们反汇编代码,然后单步调试它。反汇编出来的就是我们卸载 code segment 里的代码。由此看出,源文件里起作用的只有 segment 里的语句。
我们来单步调试它。
从图中可以看到,每次执行到 int 21 就会输出。这应该就是8086 的输出指令吧。每当你要输出,就把 ah 改成02,把你要输出的数放在 dl 上并加上30 H,然后执行 int 21,就会把你要输出的数值输出到屏幕。
第二个实验
也是将给定代码编译运行,只是这次用寄存器代替了数据段的偏移地址。
第一步肯定是敲代码,这次我直接在命令行里敲了,避免之前的问题。
开始我以为代码都是对的,经人提醒(可以看评论),我才知道老师有说是错的,然后知道哪里有问题。错误在于:第7和第10行只给出了数据段偏移地址并没有指定数据长度。现在贴出正确代码:
保存然后一样的了链接,运行。
图上出现了一个红色的36。
我们把代码当中的0433h改成0432h,0436h 改成0439h 再试试。这次数字变成了红色的29。
我们再把0433h 改成 0333h ,0436h 改成 0336h 。再看有什么变化。得到的是一个淡绿色的36。
在实验一中我们也有个类似的实验,只是那个更改的是 b8100h,这次我们改的 b8000h ,并且,那次,我们是一个字节一个字节的写入的数据,而这次我们是用 mov 语句进行的赋值。我大胆的猜测:b8000h 开始的地址空间就是我们屏幕所在的地址空间。
而对比这两个实验,我们可以发现:输出数值到屏幕可以调用输出指令去实现,也可以直接修改屏幕所在的内存空间。
总结与体会:
通过这次实验,我熟悉了汇编源文件到可执行文件的整个过程,了解了输出指令的使用。