怎样写一个简单的操作系统(9)
时间:2026-01-15
时间:2026-01-15
怎样写一个简单的操作系统
分支的代码使用BIOS输出文本到屏幕上面。首先把0Eh传送到ah寄存器里面(ax寄存器的高8位),然后lodsb指令从si寄存器指向的地址处取出一个字节存放到al寄存器(ax寄存器的低8位)里面,接下来将使用cmp指令判断取出来的这个指令是否为0,如果等于0,说明已经是字符串的末尾,将退出打印(跳转到.done标签)。
如果取出来的值不是0,那么将通过int 10h指令触发中断进入BIOS,在BIOS里面,将会读取ah寄存器的值,之前设置的是0Eh,这个值对于BIOS来说就是打印al寄存器中的字符到屏幕上的意思。因此BIOS首先打印我们的字符串里面的第一个字符,然后从中断返回,代码跳转到.repeat标签,然后重复上面的过程,lodsb指令继续从si寄存器(每次该指令会把si寄存器增加1)指向的地址处取得一个字节放入al寄存器,判断是否为0并决定做什么事情。print_sting分支最后的ret指令的意思是:这里的工作已经做完啦,可以回去了,o(∩_∩)o ,然后程序会返回到我们调用该分支代码的地方,并弹出堆栈上的值到IP寄存器中(译注:之前不是说过把call指令的下一条指令存放到了堆栈中么)。
在这个单独的分支里面有一个循环语句。在代码里面,text_string标识符的旁边是一个字符流,是通过上面提到过的db指令插入到我们的操作系统中的。这段文本包含在一对单引号(’)里面,就是告诉NASM这里面不是代码,并且以一个0结尾,标识文本的结束,在print_string分支里面作为字符串的结束的判断标志。
重新叙述一下基本要点:首先是设置段寄存器,因此操作系统就知道了堆栈的位置和可执行代码的起始位置。然后我们把si寄存器指向我们系统中的一个二进制串,然后调用一个字符串打印子程。这个子程通过si寄存器指向的字符指针扫描整个二进制串,直到遇到0,然后程序回到调用这个子程的下一句代码继续执行。jmp $ 这句的意思跳转到同一行(在NSAM里面“$”的意思是表示当前代码行)。这样就设置了一个无限循环,因此在信息被打印到屏幕上之后我们的系统将不再执行下面的代码(译注:因为一直在这里死循环)。
最后的两行代码比较的有意思,就我们的计算机来说,一个合法有效的磁盘引导扇区,必须是精确的512字节,并且以AAh和55h结尾(引导扇区的签名)。因此times 510-($-$$) db 0这句的意思是说,填充我们的二进制目标文件直到大小达到510个字节。然后第二句dw 0xAA55的意思是使用dw(定义一个word,两个字节)指令填充之前说的引导扇区的签名。510+2不是刚好512字节了么,一个以引导扇区的签名结尾的,正确大小的启动文件就此诞生,呵呵。
下面我们来编译我们的新操作系统,在命令行窗口中,进入你的home目录,输入如下的命令:
nasm -f bin -o myfirst.bin myfirst.asm
用这个命令,我们把文本代码汇编成了原始的机器码二进制文件。命令参数 –f bin是告诉NASM,我们需要的是一个纯的二进制文件(而不是一个复杂的linux下的可执行文件——总之我们希望这个文件越简单纯粹越好)。-o myfirst.bin部分是告诉汇编器生成一个名为myfirst.bin的二进制文件。
现在我们需要一个虚拟软盘映像来存放我们的引导程序,把mikeos.flp文件从Mike OS 包目下的disk_images/文件夹中拷贝到你的home目录下,并且改名为myfirst.flp,然后输入下面的命令: