前言
本篇主要讨论boot与loader,依照书中内容与自己的理解,写一个boot.asm与loader.asm并成功启动
目录博客传送:
bootloader介绍
从开机到系统启动这段时间,需要完成配置硬件工作环境、引导加载内核等任务。这部分交给bootloader。但是这又分为boot和loader,或者说二者本来就是分开的,Boot负责开机启动,loader负责剩下的任务。
简单来说,如果比喻成火箭的话,Boot就是一级助推器,将引导程序Loader加载到内存中。
Boot
Boot主要完成的工作如下所示:
FAT12文件系统,可以专门提取出来作为一个头文件,方便之后的其他文件引用作为文件系统,命名为fat12.inc
:
RootDirSectors equ 14
SectorNumOfRootDirStart equ 19
SectorNumOfFAT1Start equ 1
SectorBalance equ 17
BS_OEMName db 'MINEboot'
BPB_BytesPerSec dw 512
BPB_SecPerClus db 1
BPB_RsvdSecCnt dw 1
BPB_NumFATs db 2
BPB_RootEntCnt dw 224
BPB_TotSec16 dw 2880
BPB_Media db 0xf0
BPB_FATSz16 dw 9
BPB_SecPerTrk dw 18
BPB_NumHeads dw 2
BPB_hiddSec dd 0
BPB_TotSec32 dd 0
BS_DrvNum db 0
BS_Reserved1 db 0
BS_BootSig db 29h
BS_VolID dd 0
BS_VolLab db 'boot loader'
BS_FileSysType db 'FAT12 '
在设置的结果中,我们设定每扇区字节数为512,每簇扇区数为1。
其次我们在boot中还需要将LBA(Logical Block Address,逻辑块寻址)格式的磁盘扇区号转化为能被INT 13h,AH = 02h中断服务程序只能受理CHS(Cylinder/Head/Sector,柱面/磁头/扇区)格式的磁盘扇区号。
实现方式如下:
Func_ReadOneSector:
push bp
mov bp, sp ; 保存栈指针寄存器
sub esp, 2 ; esp为扩展栈指针寄存器,在栈上分配了2个字节(用的sub减2,因为栈是向下增长的)
mov byte [bp - 2], cl ; cl寄存器的值保存在新开辟的栈空间中,cl是要读取的扇号
push bx
mov bl, [BPB_SecPerTrk]
div bl
inc ah ; 从LBA格式转为CHS格式
mov cl, ah
mov dh, al
shr al, 1 ; 获取柱面号
mov ch, al
and dh, 1 ; 获取磁头号
pop bx
mov dl, [BS_DrvNum]
FAT12采用12bit存储每个表项,因此FAT具有奇偶性。比如有两个簇A和B,这两个簇的存储结构如下:
| 1st byte | 2nd byte | 3rd byte |
| AAAAAAA | AAAABBBB | BBBBBBBB |
中间的8bit中,前4bit是簇A的低4位,后4bit是簇B的低4位。
因此我们解析FAT表项的时候,需要对奇数项和偶数项区别对待。并且,还可能存在FAT表项横跨两个扇区的问题,因此我们需要一次读取两个扇区进来,这样便于解决FAT项横跨两个扇区的问题。
检测奇偶性的方法就是把FAT表项号乘3然后除以2,也就是扩大1.5倍。这样能够获取余数的奇偶性,把它保存在Odd
变量中。
使用Func_GetFATEntry函数可以获取下一个FAT表项,ax寄存器保存输入与输出。
Func_GetFATEntry:
push es
push bx
push ax
mov ax, 00
mov es, ax
pop ax
mov byte [Odd], 0
mov bx, 3
mul bx
mov bx, 2
div bx ; 将FAT表项扩大1.5倍,因为每个FAT表项占1.5B
cmp dx, 0
jz Label_Even ; 判读余数的奇偶性
mov byte [Odd], 1
Label_Even:
xor dx, dx
mov bx, [BPB_BytesPerSec]
div bx ; 结果除以扇区量,得到扇区号与偏移量
push dx
mov bx, 8000h
add ax, SectorNumOfFAT1Start
mov cl, 2
call Func_ReadOneSector ; 连续读入两个扇区的数据,防止FAT表项横跨两个扇区
pop dx
add bx, dx
mov ax, [es:bx]
cmp byte [Odd], 1 ; 解决奇偶错位(奇数的话就右移4位)
jnz Label_Even_2
shr ax, 4
Label_Even_2:
and ax, 0fffh
pop bx
pop es
ret
以下是完整的boot程序以及我按照理解注释的结果:
org 0x7c00 ; 起始地址
BaseOfStack equ 0x7c00 ; 将标识符BaseOfStack等价于0x7c00
BaseOfLoader equ 0x1000
OffsetOfLoader equ 0x00 ; 这两项组合成了Loader的起始物理地址 公式:BaseOfLoader << 4 + OffsetOfLoader = 0x10000
RootDirSectors equ 14 ; 定义了根目录占用的扇区数(BPB_RootEntCnt * 32 + BPB_BytesPerSec - 1) / BPB_BytesPerSec (224 * 32 + 512 - 1) / 512
SectorNumOfRootDirStart equ 19 ; 根目录的起始扇区号
SectorNumOfFAT1Start equ 1 ; FAT1表的起始扇区号
SectorBalance equ 17 ; 平衡文件(目录)的其实簇号与数据区起始簇号的差值
jmp short Label_Start
nop
BS_OEMName db 'MINEboot'
BPB_BytesPerSec dw 512
BPB_SecPerClus db 1
BPB_RsvdSecCnt dw 1
BPB_NumFATs db 2
BPB_RootEntCnt dw 224
BPB_TotSec16 dw 2880
BPB_Media db 0xf0
BPB_FATSz16 dw 9
BPB_SecPerTrk dw 18
BPB_NumHeads dw 2
BPB_HiddSec dd 0
BPB_TotSec32 dd 0
BS_DrvNum db 0
BS_Reserved1 db 0
BS_BootSig db 0x29
BS_VolID dd 0
BS_VolLab db 'boot loader'
BS_FileSysType db 'FAT12 '
Label_Start:
mov ax, cs ; 因为cs不能直接用mov到其他通用寄存器中
mov ds, ax
mov es, ax
mov ss, ax ; 将cs寄存器的段基地址写到ds、es、ss中
mov sp, BaseOfStack ; 设置栈指针寄存器
;======= clear screen ; 清屏
mov ax, 0600h
mov bx, 0700h
mov cx, 0
mov dx, 0184fh
int 10h
;======= set focus ; 设置光标
mov ax, 0200h
mov bx, 0000h
mov dx, 0000h
int 10h
;======= display on screen : Start Booting...... ; 显示提示信息,start Booting...
mov ax, 1301h
mov bx, 000fh
mov dx, 0000h
mov cx, 10
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, StartBootMessage
int 10h
;======= reset floppy 重新初始化软盘
xor ah, ah ; 异或操作,相当于清零
xor dl, dl
int 13h
;======= search loader.bin ; 查找loader.bin文件
mov word [SectorNo], SectorNumOfRootDirStart ; word指明传送的是一个字
Lable_Search_In_Root_Dir_Begin:
cmp word [RootDirSizeForLoop], 0 ; 比较目录占用的磁盘数
jz Label_No_LoaderBin ; 0的时候进行跳转Label_No_LoaderBin
dec word [RootDirSizeForLoop] ; 对目录磁盘数-1
mov ax, 00h
mov es, ax
mov bx, 8000h
mov ax, [SectorNo]
mov cl, 1
call Func_ReadOneSector
mov si, LoaderFileName ; 比较目录项文件名
mov di, 8000h
cld ; 清除方向标志,因为使用了LODSB指令比对目录项文件名,与DF标志位有关,因此需要清除
mov dx, 10h
Label_Search_For_LoaderBin:
cmp dx, 0
jz Label_Goto_Next_Sector_In_Root_Dir ; 如果没有找到,就去下一个目录项找
dec dx
mov cx, 11
Label_Cmp_FileName:
cmp cx, 0
jz Label_FileName_Found
dec cx
lodsb
cmp al, byte [es:di]
jz Label_Go_On
jmp Label_Different
Label_Go_On:
inc di
jmp Label_Cmp_FileName
Label_Different:
and di, 0ffe0h
add di, 20h
mov si, LoaderFileName
jmp Label_Search_For_LoaderBin
Label_Goto_Next_Sector_In_Root_Dir:
add word [SectorNo], 1
jmp Lable_Search_In_Root_Dir_Begin
;======= display on screen : ERROR:No LOADER Found
Label_No_LoaderBin:
mov ax, 1301h
mov bx, 008ch ; 字符颜色
mov dx, 0100h ; 行
mov cx, 21 ; 字符串长度
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, NoLoaderMessage
int 10h
jmp $
;======= found loader.bin name in root director struct
Label_FileName_Found:
mov ax, RootDirSectors
and di, 0ffe0h
add di, 01ah
mov cx, word [es:di] ; 将目录项中的文件大小加载到cx中
push cx
add cx, ax
add cx, SectorBalance ; 可能的扇区平衡调整
mov ax, BaseOfLoader
mov es, ax ; es指向加载基地址
mov bx, OffsetOfLoader ; 偏移加载基地址
mov ax, cx
Label_Go_On_Loading_File:
push ax
push bx
mov ah, 0eh
mov al, '.'
mov bl, 0fh
int 10h ; BIOS中断,显示.
pop bx
pop ax
mov cl, 1 ; 表示要读取的扇区号
call Func_ReadOneSector
pop ax
call Func_GetFATEntry ; 取得下一个FAT表项
cmp ax, 0fffh ; 0fffh 表示停止
jz Label_File_Loaded
push ax
mov dx, RootDirSectors
add ax, dx
add ax, SectorBalance
add bx, [BPB_BytesPerSec]
jmp Label_Go_On_Loading_File
Label_File_Loaded: ; 加载loader
jmp BaseOfLoader:OffsetOfLoader ; 段间地址跳转,从一个段跳转至另一个段
;======= read one sector from floppy
Func_ReadOneSector:
push bp
mov bp, sp ; 保存栈指针寄存器
sub esp, 2 ; esp为扩展栈指针寄存器,在栈上分配了2个字节(用的sub减2,因为栈是向下增长的)
mov byte [bp - 2], cl ; cl寄存器的值保存在新开辟的栈空间中,cl是要读取的扇号
push bx
mov bl, [BPB_SecPerTrk]
div bl
inc ah ; 从LBA格式转为CHS格式
mov cl, ah
mov dh, al
shr al, 1 ; 获取柱面号
mov ch, al
and dh, 1 ; 获取磁头号
pop bx
mov dl, [BS_DrvNum]
Label_Go_On_Reading:
mov ah, 2 ; 执行磁盘读取功能
mov al, byte [bp - 2] ; 要读取的扇号
int 13h ; 调用中断读取文件
jc Label_Go_On_Reading ; CF为1就会继续,为1是因为出现了错误,未成功读取
add esp, 2 ; 释放分配给局部的内存
pop bp
ret
;======= get FAT Entry
Func_GetFATEntry:
push es
push bx
push ax
mov ax, 00
mov es, ax
pop ax
mov byte [Odd], 0
mov bx, 3
mul bx
mov bx, 2
div bx ; 将FAT表项扩大1.5倍,因为每个FAT表项占1.5B
cmp dx, 0
jz Label_Even ; 判读余数的奇偶性
mov byte [Odd], 1
Label_Even:
xor dx, dx
mov bx, [BPB_BytesPerSec]
div bx ; 结果除以扇区量,得到扇区号与偏移量
push dx
mov bx, 8000h
add ax, SectorNumOfFAT1Start
mov cl, 2
call Func_ReadOneSector ; 连续读入两个扇区的数据,防止FAT表项横跨两个扇区
pop dx
add bx, dx
mov ax, [es:bx]
cmp byte [Odd], 1 ; 结果奇偶错位(奇数的话就右移4位)
jnz Label_Even_2
shr ax, 4
Label_Even_2:
and ax, 0fffh
pop bx
pop es
ret
;======= tmp variable
RootDirSizeForLoop dw RootDirSectors
SectorNo dw 0
Odd db 0
;======= display messages
StartBootMessage: db "Start Boot"
NoLoaderMessage: db "ERROR:No LOADER Found"
LoaderFileName: db "LOADER BIN",0
StartLoaderMessage: db "find file"
;======= fill zero until whole sector
times 510 - ($ - $$) db 0
dw 0xaa55
Loader
loader部分主要做的事情:
以下是完整的boot程序:
org 10000h
jmp Label_Start
%include "/home/wp/Desktop/my-kernel/code/Chapter4/bootloader/fat12.inc" ; fat12的文件系统
BaseOfKernelFile equ 0x00
OffsetOfKernelFile equ 0x100000 ; 内核程序起始地址在0x100000处
BaseTmpOfKernelAddr equ 0x00
OffsetTmpOfKernelFile equ 0x7E00 ; n内核程序的临时转存地址
MemoryStructBufferAddr equ 0x7E00
; ===========================进入保护模式的GDT
[SECTION gdt]
LABEL_GDT: dd 0,0
LABEL_DESC_CODE32: dd 0x0000FFFF,0x00CF9A00
LABEL_DESC_DATA32: dd 0x0000FFFF,0x00CF9200
GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen - 1
dd LABEL_GDT
SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT
SelectorData32 equ LABEL_DESC_DATA32 - LABEL_GDT
; ============================进入IA-32e的GDT
[SECTION gdt64]
LABEL_GDT64: dq 0x0000000000000000
LABEL_DESC_CODE64: dq 0x0020980000000000
LABEL_DESC_DATA64: dq 0x0000920000000000
GdtLen64 equ $ - LABEL_GDT64
GdtPtr64 dw GdtLen64 - 1
dd LABEL_GDT64
SelectorCode64 equ LABEL_DESC_CODE64 - LABEL_GDT64
SelectorData64 equ LABEL_DESC_DATA64 - LABEL_GDT64
[SECTION .s16]
[BITS 16]
Label_Start:
mov ax, cs
mov ds, ax
mov es, ax
mov ax, 0x00
mov ss, ax
mov sp, 0x7c00
;======= display on screen : Start Loader......
mov ax, 1301h
mov bx, 000fh
mov dx, 0200h ; 第二行显示Start Loader......
mov cx, 12
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, StartLoaderMessage
int 10h
;======= open address A20 采用A20快速门,让处理器能寻址范围超过1MB
push ax
in al, 92h
or al, 00000010b
out 92h, al
pop ax
cli ; 关闭外部中断
db 0x66
lgdt [GdtPtr] ; 加载保护模式结构数据信息
mov eax, cr0
or eax, 1
mov cr0, eax ; 开启保护模式
mov ax, SelectorData32
mov fs, ax ; fs段寄存器加载新的数据段值
mov eax, cr0
and al, 11111110b
mov cr0, eax ; 关闭保护模式
sti
;======= reset floppy 重新初始化软盘,软盘复位
xor ah, ah
xor dl, dl
int 13h
;======= search kernel.bin
mov word [SectorNo], SectorNumOfRootDirStart
Lable_Search_In_Root_Dir_Begin:
cmp word [RootDirSizeForLoop], 0
jz Label_No_LoaderBin
dec word [RootDirSizeForLoop]
mov ax, 00h
mov es, ax
mov bx, 8000h
mov ax, [SectorNo]
mov cl, 1
call Func_ReadOneSector
mov si, KernelFileName
mov di, 8000h
cld
mov dx, 10h
Label_Search_For_LoaderBin:
cmp dx, 0
jz Label_Goto_Next_Sector_In_Root_Dir
dec dx
mov cx, 11
Label_Cmp_FileName:
cmp cx, 0
jz Label_FileName_Found
dec cx
lodsb
cmp al, byte [es:di]
jz Label_Go_On
jmp Label_Different
Label_Go_On:
inc di
jmp Label_Cmp_FileName
Label_Different:
and di, 0FFE0h
add di, 20h
mov si, KernelFileName
jmp Label_Search_For_LoaderBin
Label_Goto_Next_Sector_In_Root_Dir:
add word [SectorNo], 1
jmp Lable_Search_In_Root_Dir_Begin
;======= display on screen : ERROR:No KERNEL Found 没找到文件的话
Label_No_LoaderBin:
mov ax, 1301h
mov bx, 008Ch
mov dx, 0300h ;row 3
mov cx, 21
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, NoLoaderMessage
int 10h
jmp $
;======= found loader.bin name in root director struct
Label_FileName_Found:
mov ax, RootDirSectors
and di, 0FFE0h
add di, 01Ah
mov cx, word [es:di]
push cx
add cx, ax
add cx, SectorBalance
mov eax, BaseTmpOfKernelAddr ;BaseOfKernelFile
mov es, eax
mov bx, OffsetTmpOfKernelFile ;OffsetOfKernelFile
mov ax, cx
Label_Go_On_Loading_File:
push ax
push bx
mov ah, 0Eh
mov al, '.'
mov bl, 0Fh
int 10h
pop bx
pop ax
mov cl, 1
call Func_ReadOneSector
pop ax
;;;;;;;;;;;;;;;;;;;;;;; 此处是和boot中不一样的地方
push cx
push eax
push fs
push edi
push ds
push esi
mov cx, 200h ; 将内核程序读到临时转存空间中
mov ax, BaseOfKernelFile
mov fs, ax
mov edi, dword [OffsetOfKernelFileCount]
mov ax, BaseTmpOfKernelAddr
mov ds, ax
mov esi, OffsetTmpOfKernelFile
Label_Mov_Kernel: ;------------------ 一个字节一个字节的复制进物理地址
mov al, byte [ds:esi]
mov byte [fs:edi], al
inc esi
inc edi
loop Label_Mov_Kernel
mov eax, 0x1000
mov ds, eax
mov dword [OffsetOfKernelFileCount], edi
pop esi
pop ds
pop edi
pop fs
pop eax
pop cx
;;;;;;;;;;;;;;;;;;;;;;;
call Func_GetFATEntry
cmp ax, 0FFFh
jz Label_File_Loaded
push ax
mov dx, RootDirSectors
add ax, dx
add ax, SectorBalance
jmp Label_Go_On_Loading_File
Label_File_Loaded: ; 加载成功内核代码后显示一个字符‘G’
mov ax, 0B800h
mov gs, ax
mov ah, 0Fh ; 0000: 黑底 1111: 白字
mov al, 'G'
mov [gs:((80 * 0 + 39) * 2)], ax ; 屏幕第 0 行, 第 39 列。
KillMotor: ; 关闭软盘驱动因为加载完内核程序后,就需在使用软盘驱动器了
push dx
mov dx, 03F2h
mov al, 0
out dx, al
pop dx
;======= get memory address size type
mov ax, 1301h
mov bx, 000Fh
mov dx, 0400h ;row 4
mov cx, 24
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, StartGetMemStructMessage
int 10h
mov ebx, 0
mov ax, 0x00
mov es, ax
mov di, MemoryStructBufferAddr ; 保存物理地址空间信息的位置 0x7e00
Label_Get_Mem_Struct:
mov eax, 0x0E820
mov ecx, 20
mov edx, 0x534D4150
int 15h ; 使用int 15中断获取物理地址空间信息
jc Label_Get_Mem_Fail
add di, 20
cmp ebx, 0
jne Label_Get_Mem_Struct
jmp Label_Get_Mem_OK
Label_Get_Mem_Fail: ; 获取失败的话显示信息并进入死循环
mov ax, 1301h
mov bx, 008Ch
mov dx, 0500h ;row 5
mov cx, 23
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, GetMemStructErrMessage
int 10h
jmp $
Label_Get_Mem_OK: ; 获取成功也显示提示信息
mov ax, 1301h
mov bx, 000Fh
mov dx, 0600h ;row 6
mov cx, 29
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, GetMemStructOKMessage
int 10h
;======= get SVGA information
mov ax, 1301h
mov bx, 000Fh
mov dx, 0800h ;row 8
mov cx, 23
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, StartGetSVGAVBEInfoMessage
int 10h
mov ax, 0x00
mov es, ax
mov di, 0x8000
mov ax, 4F00h
int 10h ; 获取SVGA信息
cmp ax, 004Fh
jz .KO
;======= Fail 失败的话显示信息,并陷入死循环
mov ax, 1301h
mov bx, 008Ch
mov dx, 0900h ;row 9
mov cx, 23
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, GetSVGAVBEInfoErrMessage
int 10h
jmp $
.KO: ; 成功的话显示成功的提示信息
mov ax, 1301h
mov bx, 000Fh
mov dx, 0A00h ;row 10
mov cx, 29
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, GetSVGAVBEInfoOKMessage
int 10h
;======= Get SVGA Mode Info 获取SVGA模式信息
mov ax, 1301h
mov bx, 000Fh
mov dx, 0C00h ;row 12
mov cx, 24
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, StartGetSVGAModeInfoMessage
int 10h
mov ax, 0x00
mov es, ax
mov si, 0x800e
mov esi, dword [es:si]
mov edi, 0x8200
Label_SVGA_Mode_Info_Get:
mov cx, word [es:esi]
;======= display SVGA mode information
push ax
mov ax, 00h
mov al, ch
call Label_DispAL
mov ax, 00h
mov al, cl
call Label_DispAL
pop ax
;=======
cmp cx, 0FFFFh
jz Label_SVGA_Mode_Info_Finish
mov ax, 4F01h
int 10h ; 获取VESA
cmp ax, 004Fh
jnz Label_SVGA_Mode_Info_FAIL ; 获取失败
add esi, 2
add edi, 0x100
jmp Label_SVGA_Mode_Info_Get ; 继续获取下一个SVGA模式的信息
Label_SVGA_Mode_Info_FAIL:
mov ax, 1301h
mov bx, 008Ch
mov dx, 0D00h ;row 13
mov cx, 24
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, GetSVGAModeInfoErrMessage
int 10h
Label_SET_SVGA_Mode_VESA_VBE_FAIL:
jmp $
Label_SVGA_Mode_Info_Finish: ; 完成配置,显示提示信息
mov ax, 1301h
mov bx, 000Fh
mov dx, 0E00h ;row 14
mov cx, 30
push ax
mov ax, ds
mov es, ax
pop ax
mov bp, GetSVGAModeInfoOKMessage
int 10h
;======= set the SVGA mode(VESA VBE)
mov ax, 4F02h
mov bx, 4180h ;========================mode : 0x180
int 10h
cmp ax, 004Fh
jnz Label_SET_SVGA_Mode_VESA_VBE_FAIL
;======= init IDT GDT goto protect mode 进入保护模式
cli ;======close interrupt
db 0x66
lgdt [GdtPtr]
; db 0x66
; lidt [IDT_POINTER]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp dword SelectorCode32:GO_TO_TMP_Protect
[SECTION .s32]
[BITS 32]
GO_TO_TMP_Protect:
;======= go to tmp long mode
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov ss, ax
mov esp, 7E00h
call support_long_mode
test eax, eax
jz no_support
;======= init temporary page table 0x90000
mov dword [0x90000], 0x91007
mov dword [0x90800], 0x91007
mov dword [0x91000], 0x92007
mov dword [0x92000], 0x000083
mov dword [0x92008], 0x200083
mov dword [0x92010], 0x400083
mov dword [0x92018], 0x600083
mov dword [0x92020], 0x800083
mov dword [0x92028], 0xa00083
;======= load GDTR
db 0x66
lgdt [GdtPtr64]
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 7E00h
;======= open PAE
mov eax, cr4
bts eax, 5
mov cr4, eax
;======= load cr3
mov eax, 0x90000
mov cr3, eax
;======= enable long-mode
mov ecx, 0C0000080h ;IA32_EFER
rdmsr
bts eax, 8
wrmsr
;======= open PE and paging
mov eax, cr0
bts eax, 0
bts eax, 31
mov cr0, eax
jmp SelectorCode64:OffsetOfKernelFile
;======= test support long mode or not
support_long_mode:
mov eax, 0x80000000
cpuid
cmp eax, 0x80000001
setnb al
jb support_long_mode_done
mov eax, 0x80000001
cpuid
bt edx, 29
setc al
support_long_mode_done:
movzx eax, al
ret
;======= no support
no_support:
jmp $
;======= read one sector from floppy
[SECTION .s16lib]
[BITS 16]
Func_ReadOneSector:
push bp
mov bp, sp
sub esp, 2
mov byte [bp - 2], cl
push bx
mov bl, [BPB_SecPerTrk]
div bl
inc ah
mov cl, ah
mov dh, al
shr al, 1
mov ch, al
and dh, 1
pop bx
mov dl, [BS_DrvNum]
Label_Go_On_Reading:
mov ah, 2
mov al, byte [bp - 2]
int 13h
jc Label_Go_On_Reading
add esp, 2
pop bp
ret
;======= get FAT Entry
Func_GetFATEntry:
push es
push bx
push ax
mov ax, 00
mov es, ax
pop ax
mov byte [Odd], 0
mov bx, 3
mul bx
mov bx, 2
div bx
cmp dx, 0
jz Label_Even
mov byte [Odd], 1
Label_Even:
xor dx, dx
mov bx, [BPB_BytesPerSec]
div bx
push dx
mov bx, 8000h
add ax, SectorNumOfFAT1Start
mov cl, 2
call Func_ReadOneSector
pop dx
add bx, dx
mov ax, [es:bx]
cmp byte [Odd], 1
jnz Label_Even_2
shr ax, 4
Label_Even_2:
and ax, 0FFFh
pop bx
pop es
ret
;======= display num in al 将AL的值转换为两个十六进制字符显示到屏幕上
Label_DispAL:
push ecx
push edx
push edi
mov edi, [DisplayPosition]
mov ah, 0Fh
mov dl, al
shr al, 4
mov ecx, 2
.begin:
and al, 0Fh
cmp al, 9
ja .1
add al, '0'
jmp .2
.1:
sub al, 0Ah
add al, 'A'
.2:
mov [gs:edi], ax
add edi, 2
mov al, dl
loop .begin
mov [DisplayPosition], edi
pop edi
pop edx
pop ecx
ret
;======= tmp IDT
IDT:
times 0x50 dq 0
IDT_END:
IDT_POINTER:
dw IDT_END - IDT - 1
dd IDT
;======= tmp variable
RootDirSizeForLoop dw RootDirSectors
SectorNo dw 0
Odd db 0
OffsetOfKernelFileCount dd OffsetOfKernelFile
DisplayPosition dd 0
;======= display messages
StartLoaderMessage: db "Start Loader"
NoLoaderMessage: db "ERROR:No KERNEL Found"
KernelFileName: db "KERNEL BIN",0
StartGetMemStructMessage: db "Start Get Memory Struct."
GetMemStructErrMessage: db "Get Memory Struct ERROR"
GetMemStructOKMessage: db "Get Memory Struct SUCCESSFUL!"
StartGetSVGAVBEInfoMessage: db "Start Get SVGA VBE Info"
GetSVGAVBEInfoErrMessage: db "Get SVGA VBE Info ERROR"
GetSVGAVBEInfoOKMessage: db "Get SVGA VBE Info SUCCESSFUL!"
StartGetSVGAModeInfoMessage: db "Start Get SVGA Mode Info"
GetSVGAModeInfoErrMessage: db "Get SVGA Mode Info ERROR"
GetSVGAModeInfoOKMessage: db "Get SVGA Mode Info SUCCESSFUL!"