自制操作系统学习1 引导程序

自制操作系统学习1引导程序

本系列学习有前面的汇编学习基础最好,如果没有影响也不大 本系列学习主要资源来自《[30天自制操作系统].(川合秀实)》,《自己动手写操作系统》两本书

一、准备工作

  • bochs
  • virtualbox,windows虚拟机
  • nasm

二、操作流程

回顾前文的程序,我们的引导程序已经可以加载软盘的10个柱面的数据到内存的 0x0820 处,引导区本身的512字节由BIOS加载到0x8200处。这一节让程序从这些数据中找到软盘上的程序,并运行其中的程序。

下面程序的重点是加载文件后,程序所在位置。通过前一节可以知道:

  • 文件名在磁盘的是 0x2600处
  • 文件内容在磁盘的0x4200处
  • 磁盘的内容加载到内存的0x8000处
  • 所以磁盘的0x4200处的内容在 内存的 0x8000+0x4200=0xc200处

1. 修改程序

;%define _BOOT_DEBUG_   		; 做Boot Sector时把这行注释掉
						        ; 启用这行就用nasm Boot.asm -o Boot.com生成.com文件用于调试
						
%ifdef _BOOT_DEBUG_
   org  0100h
%else
   org 07c00h
%endif

CYLS EQU 10                ; 一共读取10个柱面, 共 10柱面*2面*18扇区*512字节 = 184320 byte = 180K


; 把软盘按Fat12格式填充
start:
   JMP init                ; 跳转指令

   DB       0x90            ; 空 DB,DD用来写单字节
   DB       "NotOneOS"      ; 厂商名,8字节,DB用来写双字节
   DW       512             ; 每个扇区大小512字节,DW用来写4字节
   DB       1               ; 每个簇的扇区数
   DW       1               ; Boot占的扇区
   DB       2               ; 有2个FAT表
   DW       224             ; 根目录大小224
   DW       2880            ; 磁盘扇区总数 2880
   DB       0xf0            ; 介质描述符,磁盘种类必须为0xf0
   DW       9               ; 每个FAT扇区数
   DW       18              ; 每个磁道18个扇区
   DW       2               ; 2个磁头
   DD       0               ; 隐藏扇区数
   DD       2880            ; 同上,磁盘大小
   DB       0, 0, 0x29      ; 0x29 扩展引用标记
   DD       0xffffffff      ; 无意义,固定这么写
   DB       "NotOneOS   "   ; 磁盘名(卷标),11字节
   DB       "FAT12   "      ; 磁盘格式名,8字节
   RESB     18              ; 空18个字节,填充0x00

init:
   MOV      AX,0
   MOV      SS,AX
   MOV      SP,0x7c00      ; 堆栈空间,从0x7c00向前
   MOV      DS,AX

; 读取磁盘   
   MOV      AX,0x0820      ; 把磁盘数据加载到内存0x0820处。 0x8000~0x81ff的512字节给启动区用的,所以从0x8200开始
   MOV      ES,AX          ; 注意 ES:BX 是指向的地址,后面还需要对BX赋值0
; 初始化磁盘接口
   MOV      CH,0           ; 柱面 0
   MOV      DH,0           ; 磁头 0
   MOV      CL,2           ; 扇区 2

readloop:
   MOV      SI,0           ; 记录失败次数

retry:
   MOV      AH,0x02        ; 0x02 读磁盘
   MOV      AL,1           ; 读1个扇区
   MOV      BX,0
   MOV      DL,0x00        ; A驱动器

   INT      0x13           ; BIOS 读磁盘功能
   JNC      next           ; 成功跳转

   ADD      SI,1           ; 失败加一次
   CMP      SI,5           ; 到5次就跳到error
   JAE      error
   MOV      AH,0x00        ; 复位磁盘功能
   MOV      DL,0x00        ; A 驱动器
   INT      0x13           ; 重置磁盘驱动器
   JMP      retry          ; 重试
   
next:
   MOV      AX,ES          ; 内存地址向后移动0x0020
   ADD      AX,0x0020   
   MOV      ES,AX          ; 通过AX给ES加0x0020

   ADD      CL,1           ; 扇区+1
   CMP      CL,18          ; 有没有到18个扇区
   JBE      readloop       ; CL<=18,就跳到 readloop

   MOV      CL,1
   ADD      DH,1
   CMP      DH,2
   JB       readloop       ; 如果 DH < 2 ,则跳到readloop

   MOV      DH,0
   ADD      CH,1
   CMP      CH,CYLS
   JB       readloop       ; 如果CH<CYLS , 则跳到readloop

; 读取磁盘后,跳转到系统文件NotOneOs.sys执行接下来的程序   
   MOV      [0x0ff0],CH 
   MOV      DH, 1          ; 加载磁盘成功置个标记
   MOV      SI , boot_msg  ; 显示正在加载
   JMP      print

; 打印错误信息
error:                  
   MOV      DH, 0          ; 加载失败置个标记
   MOV      SI , error_msg

; 使用前给SI赋值字符串地址
print:
   MOV      AL, [SI]
   ADD      SI, 1
   CMP      AL, 0          ; 字符串有没有读完,到0结束
   JE       end            ; 跳到结束程序
   MOV      AH, 0x0e       ; 显示一个字符
   MOV      BX, 15         ; 指定颜色
   INT      0x10           ; 调用BIOS功能显示字符
   JMP      print

end:
   CMP      DH, 1          ; 比较前面记录的加载成功标识
   JE       0xc200         ; 跳转到磁盘上的系统程序
   HLT
   JMP end                 ; 无限循环

boot_msg:
   DB "Loading NotOneOS..."; 想要开机后在屏幕上显示的字符串
   DB 0
error_msg:    
   DB "Error"              ; 失败
   DB 0

times  510-($-$) db 0     ; 填充剩下的空间,使生成的二进制代码恰好为512字节 $表示一个section的开始处汇编后地址
DW  0xaa55                 ; 结束标志

; 启动扇区以外部分输出

		DB		0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
		RESB	4600
		DB		0xf0, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
		RESB	1469432

2. 编译,生成镜像

make img

3. 在Win7虚拟机中打开A盘,装载镜像

自制操作系统学习1引导程序

把NotOneOs.sys拷到A盘:

自制操作系统学习1引导程序

再移除虚拟盘:

自制操作系统学习1引导程序

三、启动镜像

这时候boot.img里包含了启动的NotOneOs.sys程序,使用bochs加载运行:

# 这里进入调试状态,以确定是否运行了NotOneOs.sys程序
make debug 
b 0x7c00
c
s

自制操作系统学习1引导程序

通过反汇编,可以找到我们的重点代码位置:

u 0x7c00 0x7cff

自制操作系统学习1引导程序

在0x7cca处设置断点: b 0x7cca

自制操作系统学习1引导程序

查看现在寄存器的值:

<bochs:21> r
rax: 00000000_00000e00
rbx: 00000000_0000000f
rcx: 00000000_00090a01
rdx: 00000000_00000100
rsp: 00000000_00007c00
rbp: 00000000_00000000
rsi: 00000000_000e7ce8
rdi: 00000000_0000ffac

rdx的值里看到 DH确实为1,再次单步运行,程序跳转到了0xc200:

自制操作系统学习1引导程序

而0xc200处正是NotOneOs.sys的程序:

自制操作系统学习1引导程序

自制操作系统学习1引导程序

后面的重心终于可以跳到C语言了。