计算机开机后,BIOS能自动装载MBR,但MBR大小限制在512B(MB)内,这根本装不下我们做的操作系统。所以我们只能自己装载程序,让CPU继续执行我们的程序,为后面开发庞大的操作系统做准备。
一、核心功能
使用BIOS13号磁盘中断功能读盘
寄存器参数 | 含义 |
---|---|
AH | 不同磁盘功能(0x02写磁盘) |
AL | 读取扇区数 |
BX | 读取磁盘内容后,存放在RAM的地址 |
DL | 驱动器号(0表示第一个软盘,硬盘C:80H,硬盘D:81H) |
返回值寄存器 | 含义 |
---|---|
EFLAGS:CF | 出错误为1 |
二、完整代码实现
mbr.asm
%define ENDSTR 0
%define UNKNOWN 1
%macro PUTS 1
MOV SI, %1
CALL putloop
%endmacro
MBRSeg EQU 07C0h
LoaderSeg EQU 0800h
KernelSeg EQU 0800h
RetryTimes EQU 5 ; 重读5次
SectorNum EQU 5 ; 18个扇区
HeaderNum EQU 0
CylindNum EQU 0 ; 10个柱面
; 以下的记述用于标准FAT12格式的软盘
jumper:
JMP short jumper_between_data
floppy_info:
DB 0x90
DB "PILLARLD" ; 启动区的名称可以是任意字符串(8字节)
DW 512 ; 每个扇区(sector)的大小必须为512字节
DB 1 ; 簇(cluster)的大小必须为1个扇区
DW 1 ; FAT的起始位置(一般从第一个扇区开始)
DB 2 ; FAT的个数(必须为2)
DW 224 ; 根目录的大小(一般设为244项)
DW 2880 ; 该磁盘的的大小(必须为2880扇区)
DB 0xf0 ; 磁盘的种类(必须为0xfd)
DW 9 ; FAT的长度(必须为9扇区)
DW 18 ; 一个磁道(track)有几个扇区(必须为18)
DW 2 ; 磁头数(必须为2)
DD 0 ; 不使用分区(必须为0)
DD 2880 ; 重写一次磁盘大小
DB 0,0,0x29 ; 意义不明,固定
DD 0xffffffff ; (可能是)卷标号码
DB "PillarFrame" ; 磁盘名称(11字节)
DB "FAT12 " ; 磁盘格式名称(8字节)
TIMES 18 DB 0 ; 先腾出18字
;===========================二次跳转=================================
jumper_between_data:
JMP entry
;===========================数据区域=================================
line:
DB 0X0A, 0X0D
DB ENDSTR
cylindmsg:
DB "Cylind:"
DB UNKNOWN, UNKNOWN
DB " "
DB ENDSTR
headermsg:
DB "Header:"
DB UNKNOWN, UNKNOWN
DB " "
DB ENDSTR
sectormsg:
DB "Sector:"
DB UNKNOWN, UNKNOWN
DB " "
DB ENDSTR
normalfymsg:
DB "--Read Normally!"
DB ENDSTR
errorfymsg:
DB "--Read Exceptionally!"
DB ENDSTR
sucessfullymsg:
DB "CELEBRATE!"
DB 0X0A, 0X0D
DB "MBR Loaded Sucessfully!"
DB ENDSTR
cylindct:
DB 0
headerct:
DB 0
sectorct:
DB 2
;===========================主要代码=================================
entry:
CALL ready
PUTS line
CALL fyloader
PUTS line
PUTS line
PUTS sucessfullymsg
JMP 800h:0
ready:
MOV AX, MBRSeg ;为显示各种提示信息做准备
MOV DS, AX
MOV AX, LoaderSeg ;为读软盘数据到内存做准备,因为读软盘需地址控制---ES:BX
MOV ES, AX
RET
;-------------------MOUDLE:FLOPPY LOAD-----------------------
fyloader:
call sectorreading
MOV AX, ES
ADD AX, 0X0020
MOV ES, AX ;ES+=0X0020
;一个扇区占512B=200H,刚好能被整除成完整的段,因此只需改变ES值,无需改变BP即可。
INC BYTE[sectorct] ;sectorct++
CMP BYTE[sectorct], SectorNum ;if(sectorct<=sectornum)
JBE fyloader ;jmp fyloader
MOV BYTE[sectorct],1 ;reset
INC BYTE[headerct]
CMP BYTE[headerct], HeaderNum
JBE fyloader
MOV BYTE[headerct],0
INC BYTE[cylindct]
CMP BYTE[cylindct], CylindNum
JBE fyloader
RET
sectorreading:
MOV CL, [sectorct]
CALL num2char
MOV [sectormsg+7], AL
MOV [sectormsg+8], AH
MOV CL, [headerct]
CALL num2char
MOV [headermsg+7], AL
MOV [headermsg+8], AH
MOV CL, [cylindct]
CALL num2char
MOV [cylindmsg+7], AL
MOV [cylindmsg+8], AH
MOV CH, [cylindct]
MOV DH, [headerct]
MOV CL, [sectorct]
CALL readinginfo
MOV DI, 0
readsector:
MOV AH, 02H ; AH=0x02 : AH设置为0x02表示读取磁盘
MOV AL, 1 ; 要读取的扇区数
MOV BX, 0 ; ES:BX表示读到内存的地址 0x0800*16 + 0 = 0x8000
MOV DL, 00H ; 驱动器号,0表示第一个软盘,是的,软盘。。硬盘C:80H C 硬盘D:81H
INT 13H ; 调用BIOS 13号中断,磁盘相关功能
JNC normallyread; 未出错则跳转,出错的话则会使EFLAGS寄存器的CF位置1
INC DI
MOV AH, 0x00
MOV DL, 0x00 ; A驱动器
INT 0x13 ; 重置驱动器
CMP DI, 5 ; 软盘很脆弱,同一扇区如果重读5次都失败就放弃
JNE readsector
PUTS errorfymsg
PUTS line
JMP exitread
exceptionallyread:
PUTS errorfymsg
PUTS line
normallyread:
PUTS normalfymsg
PUTS line
exitread:
RET
readinginfo:
PUTS cylindmsg
PUTS headermsg
PUTS sectormsg
RET
;----------------MOUDLE:DATA-TOOLS------------------
num2char: ;将2位数的10进制数分解成ASII码才能正常显示。如柱面56 分解成出口ascii: al:35,ah:36
MOV AX, 0
MOV AL, CL
MOV BL, 10
DIV BL
ADD AX, 3030H
RET
;----------------MOUDLE:PUTSTRING-------------------
putloop:
MOV AL, [SI] ;显示字符
CMP AL, ENDSTR
JE endprocedure
MOV AH, 0eh ;显示字符的颜色
INT 10H
INC SI
JMP putloop
endprocedure:
RET
;===========================尾部填充=================================
tail:
TIMES 510-($-$$) DB 0
DB 0X55, 0XAA
kernel.asm
%define ENDSTR 0
start:
MOV AX, 800H ;跳转后,CS为新的地址
MOV DS, AX
MOV ES, AX ;将DS=CS,ES=CS,直接执行后续程序
JMP entry
kernelmsg:
DB "Message from Kernel Program."
DB 0
entry:
MOV SI, kernelmsg
CALL putloop
JMP end
putloop:
MOV AL, [SI] ;显示字符
CMP AL, ENDSTR
JE endprocedure
MOV AH, 0eh ;显示字符的颜色
INT 10H
INC SI
JMP putloop
endprocedure:
RET
end:
JMP $
make.bat
cls
mkdir dist
mkdir log
nasm -o dist/mbr.plr -l log/mbr.lst mbr.asm
nasm -o dist/kernel.plr -l log/kernel.lst kernel.asm
cd dist
copy /b mbr.plr+kernel.plr pillar.plr
copy /b pillar.plr pillar.img
python ../../tools/bf.py pillar.img 1474560
cd ../
三、制作效果
运行make.bat,将dist/pillar.img装入虚拟机中运行