第2天 汇编语言学习与Makefile入门
温馨提示:建议大家使用VS Code编写程序,VS Code的代码提示功能非常好用,能够容错提示,另外还要安装ASM Code Lens扩展,此扩展可以实现汇编代码的高亮。
1.继续开发
再次对helloos.nas中的内容进行修改扩充。
ORG 0x7c00 ; 指明程序的装载地址
首先是程序的装载地址,先介绍ORG指令吧。在开始执行的时候,它会指定把机器语言指令装载到内存中的哪个地址。没有它,有几个指令就不能被正确地翻译和执行。美元符($)的含义也随之变化,它不再是指输出文件的第几个字符,而是代表将要读入的内存地址。
ORG 0x7c00这段代码表示将程序装载在0x7c00地址中,你是否疑惑0x7c00是怎么来的?这就要说说内存的分布了,下面是一张内存分布图,这张图来自于https://www.cnblogs.com/flashsun/p/11626691.html这篇博客中。
可以看到0x7c00~0x7dff这512KB的空间启动区内容的装载地址,这是规定好的,所以就别问为什么这么规定了。
; 以下的技术用于标准FAT12格式的软盘
JMP entry
DB 0x90
DB "HELLOIPL" ; 启动区的名称可以是任意的字符串(8字符)
DW 512 ; 每个扇区(sector)的大小
DB 1 ; 簇(cluster)的大小(必须为1个扇区)
DW 1 ; FAT的起始位置(一般从第一个扇区开始)
DB 2 ; FAT的个数(必须为2)
DW 224 ; 根目录的大小(一般设成224项)
DW 2880 ; 该磁盘的大小(必须是2880扇区)(软盘大小1440KB除以扇区大小512B得2880)
DB 0xf0 ; 磁盘的种类(必须是0xf0)
DW 9 ; FAT的长度(必须是9扇区)
DW 18 ; 1个磁道(track)有几个扇区(必须是18)
DW 2 ; 磁头数(必须是2)
DD 0 ; 不使用分区,必须是0
DD 2880 ; 重写一次磁盘大小
DB 0, 0, 0x29 ; 意义不明,固定
DD 0xffffffff ; (可能是)卷标号码
DB "HELLO-OS " ; 磁盘的名称(11字节)
DB "FAT12 " ; 磁盘格式名称(8字节)
RESB 18 ; 先空出18字节
这段代码首先跳转到entry标签所在的位置,其他部分并不需要执行,这些只是一些描述信息,在以后对操作系统的扩展中也基本不会修改(当然可以改改启动区和磁盘的名称,毕竟这取得太随意了)。
; 程序主体
entry:
MOV AX, 0 ; 初始化寄存器
MOV SS, AX
MOV SP, 0x7c00
MOV DS, AX
MOV ES, AX
现在到了程序的主体部分了,如注释所示,这段代码就是用来初始化寄存器的。栈指针寄存器设置为0x7c00是准备把0x7c00以前的地址当栈使用,至于为什么把栈段寄存器、数据段寄存器和附加段寄存器设置成0,我就不知道为什么了。
这里简单介绍一下CPU的部分寄存器:
AX | 累计寄存器 | AL | 累计寄存器低位 |
CX | 计数寄存器 | CL | 计数寄存器低位 |
DX | 数据寄存器 | DL | 数据寄存器低位 |
BX | 基址寄存器 | BL | 基址寄存器低位 |
SP | 栈指针寄存器 | AH | 累计寄存器高位 |
BP | 基址指针寄存器 | CH | 计数寄存器高位 |
SI | 源变址寄存器 | DH | 数据寄存器高位 |
DI | 目的变址寄存器 | BH | 基址寄存器高位 |
ES | 附加段寄存器 | DS | 数据段寄存器 |
CS | 代码段寄存器 | FS | 没有名称 |
SS | 栈段寄存器 | GS | 没有名称 |
对于这些寄存器没有过多的要求,当看代码时看到这些寄存器名称不会懵就行。另外,除了AL、CL、DL、BL、AH、CH、DH、BH是8位寄存器以外,上面的寄存器都是16位的。
MOV SI, msg
putloop:
MOV AL, [SI]
ADD SI, 1 ; 给SI加1
CMP AL, 0
JE fin
MOV AH, 0x0e ; 显示一个文字
MOV BX, 15 ; 指定字符颜色
INT 0x10 ; 调用显卡BIOS
JMP putloop
fin:
HLT ; 让CPU停止,等待指令
JMP fin ; 无限循环
msg:
DB 0x0a, 0x0a ; 2个换行
DB "hello, world"
DB 0x0a ; 换行
DB 0
这段代码是用来在屏幕上显示字符的。显示字符的核心代码在putloop中,是INT指令和其他寄存器合作的结果,INT指令是软件中断指令,可以使用BIOS功能。INT 0x10控制的是显示服务(Video Service),如果要显示一个字符,需要将AH赋值为0x0e,AL=要显示的字符,BH=页码(通常为0)、BL=字符颜色,该操作没有返回值。
CMP指令用来进行对比,JE指令表示等于则跳转(jump if equal),如此就可以理解上面的代码,如果AL等于0就跳转到fin(以后有机会会将条件转移指令罗列出来,方便记忆),否则就继续执行。
MOV SI, msg是将msg标签的地址送入SI寄存器中,MOV AL, [SI]是将SI寄存器的地址的内容送入AL中,最后将0送入AL中,通过CMP和JE指令跳转到fin标签,然后不断循环。
RESB 0x7dfe-$ ; 填写0x00,直到0x7dfe(不包括0x7dfe)
DB 0x55, 0xaa
这两句代码的意思是,将0x7dfe以前的空白地址都填写0x00,0x7dfe地址的内容为0x55,0x7dff地址的内容为0xaa。这样启动区从0x7c00开始,结束于0x7dff,大小刚好为512KB。
; 以下是启动区以外部分的输出
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB 4600
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
RESB 1469432
这段没有什么好介绍的,就是填充软盘的剩余空间,不填充满的话就等着报错吧。
最后双击运行asm.bat和run.bat,得到如下界面。
2.先制作启动区
首先把helloos..nas的后半部分删掉,就是把启动区以外部分给去掉,并把它重命名为ipl.nas。
然后是asm.bat,修改为:
..\z_tools\nask.exe ipl.nas ipl.bin ipl.lst
ipl.lst中的内容是ipl.nas对应的机器语言。
另外还新增了一个makeimg.bat。它以ipl.bin为基础,制作磁盘镜像文件。其内容如下所示:
..\z_tools\edimg.exe imgin:../z_tools/fdimg0at.tek wbinimg src:ipl.bin len:512 from:0 to:0 imgout:helloos.img
最后只要双击!cons_nt.bat,然后在命令行窗口中按顺序输入asm→makeimg→run这3个命令就完成了。
我寻思为什么不把这三个bat文件弄成一个文件,然后双击运行一个文件就完事了,就算出错了命令行里面也会有提示。
得到的界面如下:
界面还是没有什么变化,这学习真的是朴实无华且枯燥。
3.Makefile入门
如下是Makefile中的内容:
default :
../z_tools/make.exe img
# 文件生成规则
ipl.bin : ipl.nas Makefile
../z_tools/nask.exe ipl.nas ipl.bin ipl.lst
helloos.img : ipl.bin Makefile
../z_tools/edimg.exe imgin:../z_tools/fdimg0at.tek \
wbinimg src:ipl.bin len:512 from:0 to:0 imgout:helloos.img
asm :
../z_tools/make.exe -r ipl.bin
img :
../z_tools/make.exe -r helloos.img
run :
../z_tools/make.exe img
copy helloos.img ..\z_tools\qemu\fdimage0.bin
../z_tools/make.exe -C ../z_tools/qemu
install :
../z_tools/make.exe img
../z_tools/imgtol.com w a: helloos.img
clean :
-del ipl.bin
-del ipl.lst
src_only :
../z_tools/make.exe clean
-del helloos.img
#号表示注释。下一行“ipl.bin: ipl.nas Makefile”的意思是,如果想要只做文件ipl.bin,就先检查一下ipl.nas和Makefile这两个文件是否都准备好了。如果这两个文件都有了,make工具就会自动执行Makefile的下一行。
至于helloos.img,Makefile的写法也是完全一样的。其中的“\”是续行符号,表示这一行太长写不下,跳转到下一行继续写。
另外,要想使用Makefile,要从tolset/z_new_w文件夹中获取一个make.bat文件。
这样一来asm.bat、run.bat、makeimg.bat都用不着了。我还是觉得弄个run.bat,再弄个clean.bat,run.bat双击编译文件然后显示界面,clean.bat双击删除冗余文件,岂不是美滋滋。何必每次都要make一下,享受简单的快乐不好吗?
我也不知道为什么我运行到copy和del的时候总是报错,看别人写的博客好像没这种问题,难道是我电脑出问题了?不过,辛亏我电脑安装了Cygwin,并把它的bin目录添加到了系统变量里,用cp和rm代替了copy和del。但是在bat文件中使用copy和del却不报错。
双击!cons_nt.bat,在命令行中输入make run,按下回车键运行,最后得到如下界面:
第2天的内容就这么结束了。第2天的内容相比第1天要难上不少,需要了解启动盘的运行逻辑,也需要掌握不少额外的知识,但这些知识在以后的学习中必将成为重要的助力。期待你继续看第3天的博客!