一个boot程序

这是“从零开始写OS”系列(一个大坑)的第一篇。

环境

BIOS 引导原理

BIOS自检结束后会根据启动选项的设置选择启动设备,我们这里设置的启动介质是软盘。BIOS会检查第0磁道第1扇区是否以0xaa55(小端)结尾,如果是,BIOS就认为这是一个引导扇区,然后将这个扇区的数据复制到内存的0x7c00(小端)处,随后从此处执行。

不要问为什么是0xaa550x7c00,这是设定!

要注意的是,1.44M软盘的一个扇区只有512B。

实现

第一个boot程序很简单,并不会加载loader,只是简单的打印一串文字。这个程序分为4个部分,初始化、清屏、设置焦点、显示, 其中并非所有步骤都是必要的。

初始化

设置起始地址为0x7c00。由于BIOS的设定是到0x7c00执行引导程序,我们也没办法,只能这么做。随后设置栈指针,顺便赋值需要打印的变量,代码如下:

org 0x7c00
base equ 0x7c00
msg db "too young too simple"
mov sp, base

清屏

通过BIOS中断服务int 10h实现屏幕显示相关操作,和Linux系统调用类似,需要对a寄存器设置不同的值实现不同的操作,接着按照需要设置b, c, d寄存器的值相当于这个操作所需的参数,代码如下:

clear:
  mov ax, 0x0600
  mov bx, 0x0700
  mov cx, 0
  mov dx, 0xffff
  int 0x10
  jmp focus

ah = 0x6表示滚动窗口(即ax的高位), al 表示滚动的列数,0则实现清屏功能

设置焦点

清屏,不过ah = 0x2, bh表示页码,dh为光标列,dl为行,代码如下:

focus:
  mov ax, 0x0200
  mov bx, 0
  mov dx, 0x0505
  int 0x10
  jmp elder

显示

这时需要设置 ah = 0x13, cx表示显示的字符串长度,字符串的地址存在bp寄存器,代码如下:

elder:
  mov ax, 0x1301
  mov bx, 0x000f
  mov dx, 0x1010
  mov bx, 0x0002
  mov cx, 20
  mov bp, msg
  int 0x10
  jmp start

最后,必要的工作

因为必须以 0xaa55 结尾才能被BIOS认为是一个引导扇区,又由于软盘是块设备,这时我们需要将空闲的空间填充起来。一个扇区的大小是512B,除去0xaa55两个字节后还剩下510B,需要填充的大小就是510B减去我们程序已经使用的空间。这里可以利用nasm的$$$计算,其中$表示当前行编译后的地址,$$表示分节编译后的地址。$ - $$则是编译后占用的大小,因此,通过填充 510 - ($ - $$)个0即可。

start:
  jmp clear

call start
times 510 - ($ - $$) db 0
dw 0xaa55

编译运行

配置bochsrc

floppya: type=1_44, 1_44="boot.img", status=inserted, write_protected=1
boot: floppy
floppy_bootsig_check: disabled=0

编译和运行

nasm boot.asm -o boot.bin
dd if=boot.bin of=boot.img bs=512 count=1 conv=notrunc
bochs -f bochsrc

结果如图:

Record_20191215_192429.gif

完整代码在这里