Welcome to Rooeye's blog

Orange’s 自制操作系统系列笔记(4)—一个引导扇区的具体实现

操作系统 rooeye 495℃ 0评论

引导扇区(boot sector)是设备的第一个扇区,大小为512个字节,并且以0xAA55结束。当机器加电启动后,如果选择从软盘启动,会检查软盘的0面0磁道1扇区,如果它以0xAA55结束,就认为它是一个引导扇区,之后把引导扇区内容加载到用户指定的内存地址处,并从此处开始执行,从此,计算机的控制权从处理器转移给了操作系统。

先来看引导扇区的代码:

  1. org 07c00h
  2. mov ax, cs
  3. mov ds, ax
  4. mov es, ax
  5. call DispStr
  6. jmp $
  7. DispStr:
  8. mov ax, BootMessage
  9. mov bp, ax
  10. mov cx, 38
  11. mov ax, 01301h
  12. mov bx, 000ch
  13. mov dl, 0
  14. int 10h
  15. ret
  16. BootMessage: db "Hey! I am Rooeye, welcome to my blog!"
  17. times 510-($-$$) db 0
  18. dw 0xaa55

这段代码实现的功能就是在开机的时候让屏幕显示  “Hey! I am Rooeye, welcome to my blog!”

先来看其运行结果,用虚拟机VMare加载其生成的软盘映像文件:



实际上我们实现的就是在屏幕上显示一个特定字符串,并且该字符串背景色是黑色,前景色是红色,并且高亮,显示位置是在第0行。这段代码最重要的莫过于第14行的 int 10h ,int 10h中断例程是BIOS提供的中断例程,其中包含了多个和屏幕输出相关的子程序,接下来就了解下 int 10h。
 
通常一个中断例程都会包含多个内部子程序,执行哪一个子程序根据传递进来的参数而定,而BIOS中的中断例程,都是用8位通用寄存器AH来传递使用的子程序的编号。在本程序的第11行:

  1. mov ax, 01301h

因为AX = 0x1301 ,  则 AH = 0x13,即最终会执行第10h号中断的编号为13h的子程序,而该子程序的作用就是用来显示一个字符串,其具体调用参数见下:

  1. ES:BP = 串地址
  2. CX = 串长度
  3. DH = 起始行
  4. DL = 起始列
  5. BH = 页号

AL和BL寄存器也会用来传递参数,但是比较复杂,下面详细解释。

AL寄存器一共有8位,但是只使用低两位,高6位并不使用:

  1. 如果AL0,表示目标字符串仅仅包含字符,属性在BL中包含,不移动光标
  2. 如果AL1,表示目标字符串仅仅包含字符,属性在BL中包含,移动光标
  3. 如果AL2,表示目标字符串包含字符和属性,不移动光标
  4. 如果AL3,表示目标字符串包含字符和属性,移动光标

在该引导扇区代码中,因为AX = 0x1301,则 AL = 0x01。

BL寄存器主要是用来定义一些颜色属性格式:



  1. BIT7 = 1 ,背景闪烁
  2. BIT3 = 1 ,前景色高亮显示
  3. BIT4~BIT6 表示背景色
  4. BIT0~BIT2 表示前景色

根据上面的知识可以对源代码得到如下分析:

  1. mov cx, 38 ;字符串长度为 38
  2. mov ax, 01301h ;AH = 13h,AL=01h,在屏幕上打印字符串
  3. mov bx, 000ch ;页号为0,不闪烁,背景色为黑色,前景色高亮显示,前景色为红色
  4. mov dl, 0 ;在第0列开始显示

从而达到显示黑底红字的效果。
 
上面提到 AH = 13H的时候,调用参数ES:BP 传递字符串的地址。ES寄存器存储段地址,BP寄存器存储段内偏移地址,实际物理地址 = 段地址*16 + 偏移地址。
 
下面代码表示boot sector的内容会被加载到内存偏移地址为0x7c00处开始执行。

  1. org 07c00h

因为段寄存器之间不能直接传递数据,所以先把段寄存器CS的内容传递给通用寄存器AX,然后再把其值送给段寄存器DS和ES。
  1. mov ax, cs
  2. mov ds, ax
  3. mov es, ax

bp存储的就是字符串的偏移地址

  1. mov ax, BootMessage
  2. mov bp, ax

因为boot sector的大小为512个字节,所以先填充了 510-($-$$) 个字节,然后在定义了一个字类型数据 0xaa55.

  1. times 510-($-$$) db 0 ;$表示当前行被汇编后的地址 
  2. ;$$表示section开始处被汇编后的地址
  3. dw 0xaa55 ;引导扇区的结束标志为 0xaa55

上面提到,cx寄存器是用来存储字符串的长度的,如果我们想显示任意字符串,那么cx的值就必须改变,有没有什么方法可以直接计算出字符串长度而不用我们人为指定呢?当然是有的啦,看如下代码:

  1. org 07c00h
  2. mov ax, cs
  3. mov ds, ax
  4. mov es, ax
  5. call DispStr
  6. jmp $
  7. DispStr:
  8. mov ax, BootMessage
  9. mov bp, ax
  10. mov cx, strlen
  11. mov ax, 01301h
  12. mov bx, 000ch
  13. mov dl, 0
  14. int 10h
  15. ret
  16. BootMessage: db "Nowcoder is so good!"
  17. strlen equ $-BootMessage
  18. times 510-($-$$) db 0
  19. dw 0xaa55


代码的第17行使用 $BootMessage 计算字符串长度,在第10行直接把strlen传递给通用寄存器CX即可。下面分别是使用bochs虚拟机和Vmare的运行上述代码的结果,运行后会输出 “Nowcoder is so good!”





下一篇笔记写保护模式(Protect Mode)。


来自为知笔记(Wiz)

转载请注明:寻梦人博客 » Orange’s 自制操作系统系列笔记(4)—一个引导扇区的具体实现

喜欢 (1)
发表我的评论
取消评论

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址