操作系统的第一个程序 - MBR程序
MBR程序必须是操作系统运行的第一个程序吗?
正常运行的操作系统,MBR程序确实是第一个运行的程序。但是在开发操作系统的时候,MBR程序却是可以不需要的,完全可以在另一个操作系统启动自己操作系统来调试。其实EFI启动就是在一个操作系统里启动另一个操作系统,难道你还想自己写EFI?
为了照顾那些认定开发操作系统就必须先开发MBR程序的人,也就只能先说MBR程序了。
MBR程序要干些什么事情
我认为,是的,就仅仅是我认为,因为我没找到明确的规范说MBR应该干什么。我认为MBR仅仅是提供了让你接管CPU的方法,至于接管CPU后,你能干什么,那就是你自己的事情了。
MBR程序要做的事情一般是:找到活动分区,然后读入活动分区的首扇区,就把这个扇区称为活动分区的启动扇区吧,然后执行活动分区的启动扇区的代码。
常规的流程是POST之后,BIOS就会把MBR读入内存,然后跳转到这个地方开始执行,就是俗称的启动程序。
MBR程序涉及的问题
MBR程序因为可用的空间很小,最多就446个字节,稍微不注意就超了,
内存分配的问题
要记得,这个时候是没有人帮你管理内存的,只能自己管理设置。比如,BIOS把MBR读到了0000:7C00处 ,但是,你知道这个时候栈在哪里吗?还有,活动分区的启动扇区你要读到什么位置?这些问题都要自己处理的,
这个就要求你知道POST之后的内存布局了,或者说PC典型的内存布局。一般的安排是这个样子
地址范围(32位表示) | 用途 |
---|---|
0x00000000 - 0x000003FF | 实模式中断向量表 |
0x00000400 - 0x000005FF | BIOS数据区 |
0x00000600 - 0x00007BFF | 可用区域 |
0x00007C00 - 0x00007DFF | MBR扇区,启动过后可用 |
0x00007E00 - 0x0009FFFF | 可用区域,总共640K,这也是DOS可用内存是640K的原因 |
0x000A0000 - 0x000B7FFF | 图形显示模式的显存 |
0x000B8000 - 0x000BFFFF | 字符显示模式的显存 |
0x000C0000 - 0x000EF7FF | 各种设备的ROM |
0x000EF800 - 未知 | 其实是我不知道 |
MBR为什么会装到7C00这个位置
真正的可用内存地址是从600开始,但为什么BIOS会把MBR读到7C00这个位置呢?不能直接读到600吗?后面就可以连续起来了。毕竟windows的MBR程序就把自己从7C1B复制到61B了,也就是说明其实可以到这个位置的。
其实吧,这是因为要向前兼容,因为早期的DOS用了这个位置作为MBR程序空间,以及紧接着的512个字节作为数据空间,然后就一直保留下来了。有兴趣的可以在网上搜搜这个这个历史,
活动分区的启动扇区又该放到哪里
如果只是测试的话,在640K以内随便找个地方就行,但是为了方便后面程序的载入,还是需要优化一下的。
我是根据我的需要啊。
我不用考虑什么向前兼容,所以MBR我就不换地方了,就让他在7C00那里呆着。然后活动分区的启动扇区就读到600处。另外我还在活动分区的保留扇区中用了8K的空间,这个地方的数据我安排在90000处,用表格来看是这样的
地址范围 | 用途 |
---|---|
00600 - 03FFF | 活动分区的启动扇区 |
07C00 - 07DFF | MBR扇区 |
08100 - 17FFF | 16位装载程序,64K的空间,完成基本设置和文件装载,足够了 |
20000 - 8FFFF | 32位装载程序,这个地方给的很大,足够干很多事情了 |
90000 - 97FFF | 活动分区的装载程序,分配了32K |
为什么需要一个额外的8K空间呢?
是因为我的编程水平不够,没办法在412个字节的程序里,完成在分区里查找文件,还要把文件读到内存里,只好另外找了一个空间来放这个功能的程序。方便定位的,也就保留扇区了,如果有其他程序用到保留扇区,那结果自然就是凉了。
检测INT 13H的版本
检测INT 13H的版本不能缺,我虽然只用INT 13H的扩展功能,但不能保证某个人拿个20、30年前的老电脑来试试,BIOS没有INT 13H扩展功能,也许还真的就出现问题了。
我的MBR程序
注释基本是我的中国式英语,如果看不懂,那就看不懂吧,等我能写出靠谱的英文在说了。代码的中文解释放在书里,这里就不写了。
这个程序编译出来,257个字节,如果发现有遗漏的地方,还有增加一些代码的空间
程序代码
我觉个肯定会有人纠结为什么一定要0000:7C00,弄成07B0:0100,不也是一样吗?这样还方便在DOS下调试。好吧,确实可以,不过我是放在书里解释这个问题了。
;
;==============================================================================
; ROBIX
; Robin's Operating System. Order Source Studio @ 2011 - 2019
; ALL RIGHTS RESERVED
; ***------------------------------***
;Name : mbr.asm
;Create : 2017-05-26. robin
;Purpose: this asm program is MBR booter. should be complied to COM format
; executable file with 16-BIT asm complier. this program would be written
; to MBR.
; it read active parition boot sector to 0000:0600, and jmp to that
; address
;==============================================================================
;
.386p
.model tiny
DAP STRUC
DAPSIZE DB 0;
Rsvd DB 0;
Count DW 0;
Buf DD 0;
SectL32 DD 0;
SectH32 DD 0;
DAP ENDS
; if found the active partition, booter will save the active partition start
; sector at 0000:03F8, 8 bytes, the active partition booter can use this
TEXT SEGMENT USE16 PUBLIC 'CODE'
assume cs:TEXT,ds:TEXT
org 07C00h
start:
;cli
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 07BF8h
; MBR need to use extend INT 13h, so check it
mov ah, 041h
mov bx, 055AAh
mov dl, 080h
int 013h
cmp bx, 0aa55h
jnz failed;
; search active partition
; dx: contain the active partition number
; di: contain the active item pointer
mov cx, 4
mov si, 07DBEh
xor di, di
xor dx, dx
serach_active_part:
cmp byte ptr [si],080h
jne next_item
inc dx
cmp di, 0
jnz next_item
mov di, si
next_item:
add si,16
loop serach_active_part
; the active partition must be 1, if not, boot failed
cmp dx, 1
je found_active_part
int 018h ; this is not the ROM BASIC, it is boot failed
found_active_part:
; if the partition is extend partition, boot failed
cmp byte ptr [di + 4], 05h
je failed
cmp byte ptr [di + 4], 0Fh
je failed
mov cx, 3
read_active_part:
push di
push cx
mov bx,03F8h
xor si,si
; read active partition boot sector to 0000:0600
; save the active partition start sector at 0000:03F8, 8 bytes
mov ax,[di + 8]
mov dx,[di + 10]
mov [bx ], ax ; save the active partition start sector to 3F8,
mov [bx + 2], dX ; 8 byte
mov [bx + 4], si ;
mov [bx + 6], si ;
push si ; construct DAP
push si ; dap.SectH32 = 0
push dx
push ax ; dap.SectL32 = start sector
push si
push 0600h ; dap.Buf = 0000:0600
push 1 ; dap.Count = 1
push 010h ; dap.DAPSIZE = 16
mov si, sp
mov ah, 042h
mov dl, 080h
int 013h
popa
jnc run_active_part
pop cx
test cx, cx
jz failed ; retry 3 times failed, terminated
dec cx
push cx
xor ah, ah ; if reading failed, reset disk, and try again
mov dl, 080h
int 013h
pop cx
pop di
jmp read_active_part
; run the active partition boot sector, JMP 0000:0600
run_active_part:
DB 0EAh
DW 00600h
DW 00000h
failed:
sti
push cs
pop es
mov ah,03h
xor bh,bh
int 010H
lea ax,errmsg
mov bp,ax
mov bx,0Fh
mov cx,65
mov ax,01301h
int 010h
xor ax,ax
int 016h
cmp al,'r'
je reboot
cmp al,'R'
je reboot
jmp failed
reboot:
; jmp FFFF:0000
DB 0EAh
DW 0
DW 0FFFFh
align qword
; err message
errmsg DB "PARTITION TABLE INVALID OR DISK IO ERROR, PRESS 'R' TO REBOOT",
0Dh,0Ah,0dh,0ah
TEXT ENDS
end start
编译
这是一个批处理代码,把文件编译成COM格式的文件。这里只不过把文件命名为MBR.BIN,实际还是COM格式
@ECHO off
echo ==========================================================================
echo this file is used to create MBR.BIN under windows
echo [NOTE] it need MASM32 v11 or early, because the later version do not
echo support TINY mode.
echo if failed, maybe the command path is wrong.
echo ==========================================================================
ml /AT /omf MBR.ASM
link16 /TINY MBR.OBJ, MBR.BIN, nul, nul, nul
@ECHO on
编译结果
放一个编译的结果,链接给出了一个警告,没办法,因为设定的地址是7C00,