前言
由于上一章中的budOS.img都是用十六进制写的,里面很多数据意义不明,而且大量的0x00即使复制粘贴都很花时间,而且容易出错。这一章中,我们将使用汇编来重写这个系统,并解释系统中各个参数的含义。
budOS-0.0.2
这次版本中还是只有两个文件:
其中budOS.asm就是我们的操作系统啦。.asm是汇编文件的后缀,可以用文本编辑器直接打开。但是计算机无法直接理解汇编语言,我们需要一款汇编器来将汇编语言编译成机器语言,这里推荐使用开源软件NASM(原书中作者使用了自己改造的汇编器,改动了汇编语言的语法,为了学习真正通用的汇编,这里使用最为通用的NASM),下载地址NASM,请根据自己计算机的操作系统安装合适的版本。
接下来开一下budOS.asm里面的内容。这个文件也不是我自己写的,只是参照《30天自制操作系统》以及这篇文章链接。我也是因为看到这篇文章写的很好,才想自己也写一下博客,可惜作者只更新了前三章就没有再更新了。这里面作者描述的很详细,推荐看一下,这里我只简单地过一遍。
这里的所谓扇区,是指磁盘上的一段弧形区域。磁盘被划分成一个个同心圆(即磁道),每个圆又以512个字节为单位划分为一段段圆弧,这些圆弧就是扇区,扇区是最小的读写单元。磁盘的磁道从0开始计数,0磁道是同心圆里最大的,也就是最外面的圆。扇区从1开始累加计数,0磁道的第一个扇区为1扇区,第二个为2扇区。假如0磁道有32个扇区,则1磁道的第一个扇区即为33扇区。不难得到磁盘容量为扇区数*512字节。
下面直接贴上budOS.asm的代码:
; hello-os
; TAB=4
; 下面实现FAT12格式软盘代码
DB 0xeb, 0x4e, 0x90
DB "budOSIPL" ;启动区的名称可以是任意的字符串,但长度必须是8字节
DW 512; 每一个扇区的大小,必须是512字节
DB 1 ;簇的大小(必须为1个扇区)
DW 1 ;FAT的起始位置(一般从第一个扇区开始)
DB 2 ;FAT的个数 必须是2
DW 224;根目录的大小 一般是224项
DW 2880; 该磁盘的大小 必须是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 ;扩展引导标记 固定0x29
DD 0xffffffff ;卷列序号
DB "budOS " ;磁盘的名称(11个字节)
DB "FAT12 " ;磁盘的格式名称(8字节)
TIMES 18 DB 0; 先空出18字节 这里与原文写法不同
;程序主体
DB 0xb8, 0x00, 0x00, 0x8e, 0xd0, 0xbc, 0x00, 0x7c
DB 0x8e, 0xd8, 0x8e, 0xc0, 0xbe, 0x74, 0x7c, 0x8a
DB 0x04, 0x83, 0xc6, 0x01, 0x3c, 0x00, 0x74, 0x09
DB 0xb4, 0x0e, 0xbb, 0x0f, 0x00, 0xcd, 0x10, 0xeb
DB 0xee, 0xf4, 0xeb, 0xfd
;信息显示部分
DB 0x0a, 0x0a ;两次换行
DB "Hello, I'm buddingpop!"
DB 0x0a ; 换行
DB 0
TIMES 0x1fe-($-$$) DB 0 ;填写0x00,知道0x001fe
DB 0x55,0xaa
;启动区以外的输出
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 4600 DB 0
DB 0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
TIMES 1469432 DB 0
可见代码量很小,已经可以全部贴上来了。代码中一些常见指令:
;:注释
DB:define byte,定义一个字节(8bit)
DW:define word,定义一个字(16bit)
DD:define double word,定义两个字(32bit)
TIMES n DB m:重复DB m操作n次,DB也可以是其他操作,这是我们代码量大大减少的原因
$:当前位置
$$:当前扇区起始地址
对照budOS-0.0.1中的budOS.img文件,不难发现,我们只是用汇编的方法将他重写了一遍。至于为什么要这么写呢?看可以参照上面的windows读软盘第一个扇区的读法图,都是能够一一对应的。当然这只是第一个扇区,从0x0000到0x01ff这512个字节的内容,后面的我们暂时不要管。最后两个字节的内容如果是0x55与0xaa,则表示该扇区内包含启动代码。
我们按照一定规则(汇编语法)用budOS.asm重写了budOS.img,但是计算机是看不懂.asm这种文本文件的,因此需要使用上面提到的汇编器将其翻译成机器语言。打开build.bat文件,可以发现里面有三条命令:
nasm budOS.asm -o budOS.img
qemu-system-x86_64 budOS.img
pause
其中第一条的含义为使用nasm将源文件budOS.asm编译为名为budOS.img的目标文件,这条命令执行完成后会在当前文件夹下生成文件budOS.img,之后的流程就与上一章的一样啦。双击build.bat文件,我们的操作系统又可以整场地运行了。我们可以对比一下budOS-0.0.1和0.0.2两个文件的内容,用二进制编辑器打开两个.img文件,发现是完全一样的(除了我改了一点打印的字符之外)。有了汇编器以后,我们就可以不用疯狂的敲0啦,而.img文件也可以从我们的源代码里删掉了。
budOS-0.0.3
以上我们只是用汇编将.img重新写了一遍,但其中有很多地方为什么要这么写还不是很了解,比如程序主体部分。现在我们用更加清楚的汇编语法来描述这段代码究竟在干什么。从这里开始大家就可以自己手敲代码啦。
budOS-0.0.3相对于0.0.2只是修改了汇编程序,具体代码如下:
; hello-os
; TAB=4
ORG 0x7c00 ;程序的装载地址
; 下面实现FAT12格式软盘代码
JMP entry
DB 0x90
DB "budOSIPL" ;启动区的名称可以是任意的字符串,但长度必须是8字节
DW 512; 每一个扇区的大小,必须是512字节
DB 1 ;簇的大小(必须为1个扇区)
DW 1 ;FAT的起始位置(一般从第一个扇区开始)
DB 2 ;FAT的个数 必须是2
DW 224;根目录的大小 一般是224项
DW 2880; 该磁盘的大小 必须是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 ;扩展引导标记 固定0x29
DD 0xffffffff ;卷列序号
DB "budOS " ;磁盘的名称(11个字节)
DB "FAT12 " ;磁盘的格式名称(8字节)
TIMES 18 DB 0; 先空出18字节 这里与原文写法不同
;程序核心
entry:
MOV AX,0 ;初始化寄存器
MOV SS,AX
MOV SP,0x7c00
MOV DS,AX
MOV ES,AX
MOV SI,msg
putloop:
MOV AL,[SI]
ADD SI,1 ;SI加一
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 ;换行两次
DB "Hello, I am buddingpop!"
DB 0x0a ;换行
DB 0
TIMES 0x1fe-($-$$) DB 0 ;填写0x00,直到0x001fe
DB 0x55,0xaa
这一段里面又多了一些新指令:
ORG:origin,告诉编译器这段代码要被搬运至内存的起始地址;
JMP:jump,跳转指令,跳转至tag处;
MOV:move,赋值指令;
[]:取内存地址中的内容;
ADD:加法指令;
CMP:compare, 比较;
JE:jump equal,相等则跳转;
INT:interrupt,中断;
HLT:halt,停止指令。
这里还有一些寄存器的符号,寄存器属于cpu,主要包括:
其中AX,BX,CX,DX,SP,BP,SI,DI均是16位寄存器,AX,BX,CX,DX又可以分为低8位与高8位,例如AX就可以分为AL和AH。在32与64位cpu中,这些寄存器会被扩展到32位(EAX等)与64位(RAX等),但仍然可以只使用低位,因此向下兼容。CS,DS,SS,ES称为段寄存器,不管是32位与64位cpu下都是16位的。
这样上面的代码就很清楚啦,运行build.bat可以发现我们的操作系统能够正常运行。打开生成的budOS.img,会发现这次的有一些不一样,只包含了一个扇区,即512字节的内容,后面的都丢失了,但程序依然正常运行。这是为什么呢?我也不知道哈哈,不过继续看下去就知道了。
总结
我们使用汇编指令替代了十六进制。汇编器能够帮助我们将汇编翻译成机器语言,但机器语言具体是怎么调用显卡的呢?以及这些寄存器为什么要这样用呢?我们暂时不得而知,不过后面就会学习到啦。下一次我们就要开始引入c语言啦。
P.S. 感觉每次程序更新的比较少,现在开始我每个版本尽量多加一点内容进去。