USB Boot Loader(1)完成了把编译成的二进制文件拷贝到DBR区域,实现了Loader.bin突破512字节的限制,但是最大仅仅为4K,若果想要把操作系统代码也载入内存,显然4K也是太小了,那么就把Loader.bin当做一个跳板,模仿DBR内的代码实现加载任意大小的文件。
修改Loader.asm 的内容,主要完成了循环查找KERNEL.BIN文件在U盘中存储的位置,并且载入内存后,跳转到KERNEL.BIN加载内存的位置。这样就完全突破了执行代码大小的限制。
loader.asm
;
;nasm loader.asm -o LOADER.BIN
;
org 0100h;必须为0100h,因为boot.asm中跳转来时cs为09000h,保证地址偏移正确
SectorsOfMBR equ 63 ;MBR 大小
BaseOfFat equ 07900h
BaseOfKernel equ 08000h ; KERNEL.BIN 被加载到的位置 ---- 段地址
OffsetOfKernel equ 0100h ; KERNEL.BIN 被加载到的位置 ---- 偏移地址
BaseOfLoader equ 09000h ; LOADER.BIN 被加载到的位置 ---- 段地址
OffsetOfLoader equ 0100h ; LOADER.BIN 被加载到的位置 ---- 偏移地址
BaseOfStack equ 0100h ; 堆栈基址
jmp short START
nop ; 这个 nop 不可少
; 下面是 FAT32 磁盘的头 磁盘头的数据写入到一个inc文件中
%include "fat32head.inc"
;offset 90Bytes
;[section .begin]
START:
pop ax ;BS_DrvNum
mov byte[BS_DrvNum], al
mov ax, cs
mov es, ax
mov ds, ax
mov ss, ax
mov sp, BaseOfStack
; 清屏
mov ax, 0600h ; AH = 6, AL = 0h
mov bx, 0700h ; 黑底白字(BL = 07h)
mov cx, 0 ; 左上角: (0, 0)
mov dx, 0184fh ; 右下角: (80, 50)
int 10h ; int 10h
;;Loading
mov ax, KernelLoading
call DispStr
;;计算根目录扇区位置
xor eax, eax
mov al, [BPB_NumFATs]
mov ebx, [BPB_SecPerFat]
mul ebx
xor ebx, ebx
mov bx, word[BPB_RsvdSecCnt]
add eax, ebx
mov dword[SectorNoOfRootDirectory], eax
add eax, SectorsOfMBR ;;sectors of MBR
;;读取根目录两个扇区
push eax
push 2
push OffsetOfKernel
push BaseOfKernel
call ReadSector
add sp , 10
push es
mov ax, BaseOfKernel
mov es, ax
mov di, OffsetOfKernel
mov si, LoaderFileName ; ds:si -> "KERNEL BIN"
cld
mov dx, 20h ;;两个扇区有32个目录项
LABEL_SEARCH_FOR_KERNEL_BIN:
cmp dx, 0 ; `. 循环次数控制,
jz LABEL_NO_KERNEL_BIN ; / 如果已经读完了一个 Sector,
dec dx ; / 就跳到下一个 Sector
mov cx, 11
LABEL_CMP_FILENAME:
cmp cx, 0
jz LABEL_FILENAME_FOUND ; 如果比较了 11 个字符都相等, 表示找到
dec cx
lodsb ; ds:si -> al
cmp al, byte [es:di]
jz LABEL_GO_ON
jmp LABEL_DIFFERENT ; 只要发现不一样的字符就表明本 DirectoryEntry
; 不是我们要找的 LOADER.BIN
LABEL_GO_ON:
inc di
jmp LABEL_CMP_FILENAME ; 继续循环
LABEL_DIFFERENT:
and di, 0FFE0h ; di &= E0 为了让它指向本条目开头
add di, 20h ; |
mov si, LoaderFileName ; | di += 20h 下一个目录条目
jmp LABEL_SEARCH_FOR_KERNEL_BIN; /
LABEL_NO_KERNEL_BIN:
pop es
mov ax, KernelNoNoFound
;mov ax, OffsetOfLoader
Call DispStr ; 调用显示字符串例程
jmp $ ; 无限循环
LABEL_FILENAME_FOUND:
;;寻找KERNEL并加载
;;计算文件所在扇区
and di, 0FFE0h ; else `. di &= E0 为了让它指向本条目开头
mov ax, [es:di + 1ah]
mov word[ClusterNum], ax
mov ax, [es:di + 14h]
mov word[ClusterNum + 2], ax;得到存放文件的簇号
mov si , OffsetOfKernel
.1:
mov eax, [ClusterNum]
cmp eax, 00FFFFFFFh;判断是否为最后一个文件簇
je .2
xor eax, eax
mov al, [BPB_SecPerClus]
mov ebx, [ClusterNum]
sub ebx, 2
mul ebx
mov ebx, [SectorNoOfRootDirectory]
add eax, ebx ;;eax 记录文件扇区位置
add eax, SectorsOfMBR
push si
;;读取根目一个簇
push eax
push 8
push si
push BaseOfKernel
call ReadSector
add sp , 10
;打点
push ax
push bx
mov ah, 0Eh
mov al, '.'
mov bl, 0Fh
int 10h
pop bx
pop ax
;计算文件簇号对应的 FAT表 所在的簇号
xor edx, edx
xor eax, eax
mov dx, [ClusterNum + 2]
mov ax, [ClusterNum]
mov bx, 128 ;一个fat扇区(512B)有128个表 512/4 = 128
div bx ;ax 商 dx余数
xor ebx, ebx
mov bx, word[BPB_RsvdSecCnt]
add eax, ebx
add eax, SectorsOfMBR
push eax
push dx
;;读取一个fat扇区
push eax
push 1
push 0
push BaseOfFat
call ReadSector
add sp , 10
pop dx
pop eax
;计算FAT表项内的值
push es
shl dx, 2 ;2^2 = 4Byte = 一个FAT表大小
mov di, dx
mov ax, BaseOfFat
mov es, ax
mov eax, dword[es:di]
pop es
pop si
add si, 8*512
mov dword[ClusterNum], eax
jmp .1
.2:
pop es
jmp BaseOfKernel:OffsetOfKernel
;mov ax, KernelFound
;Call DispStr ; 调用显示字符串例程
;jmp $ ; 无限循环
ReadSector:;;(Base,Offset, sectors_count,sector_num)
;;读取根目一个簇
mov ax, word[esp + 2]
mov word[disk_addr_pkt + buffer_addr+2], ax
mov ax, [esp + 4]
mov word[disk_addr_pkt + buffer_addr], ax
mov ax, [esp + 6]
mov word[disk_addr_pkt + block_count], ax
mov ax, [esp + 8]
mov word[disk_addr_pkt + block_num], ax
mov ax, [esp + 10]
mov word[disk_addr_pkt + block_num + 2], ax
mov byte[disk_addr_pkt + packet_size], 10h
mov byte[disk_addr_pkt + reserved], 0
mov dword[disk_addr_pkt + block_num + 4], 0
mov ah, 42h
mov dl, [BS_DrvNum]
mov si, disk_addr_pkt
int 13h
ret
DispStr:
push bp
mov bp, ax; ES:BP = 串地址
mov cx, 16 ; CX = 串长度
mov ax, 01301h ; AH = 13, AL = 01h
mov bx, 000ch ; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮)
mov dx, 0
int 10h ; int 10h
pop bp
ret
LoaderFileName db "KERNEL BIN", 0 ; KERNEL.BIN 之文件名
KernelFound db "Kernel Found "
KernelNoNoFound db "Kernel No Found!"
KernelLoading db "On Loading"
;=================变量 =============
struc disk_addr_pkt
packet_size: resb 1 ; 磁盘参数包的尺寸,必须为10H
reserved: resb 1 ; 保留,必须为零
block_count: resw 1 ; 传输的扇区数
buffer_addr: resd 1 ; 内存缓冲区地址(段:偏移)
block_num: resq 1 ; 起始绝对扇区号(即起始扇区的LBA号码)
endstruc
ClusterNum DD 0
SectorNoOfRootDirectory DD 0
;==============================
Kernel.asm如下:
;/*
;nasm kernel.asm -o KERNEL.BIN
;*/
org 0100h
mov ax, cs
mov es, ax
mov ds, ax
mov ss, ax
call DispStr
jmp $
DispStr:
mov ax, BootMessage
mov bp, ax
mov cx, 20
mov ax, 0x1301
mov bx, 0x0c
mov dl, 0
int 10h
ret
BootMessage: db "Hello Kernel world!!"
思考:
1、编写过程中特别要注意溢出这一问题,注意像eax、ax、ah、al等寄存器的范围。
2、要注意在跳转过程中或者在调用routine时,某些寄存器的改变像es等,不然会出现打印不出字符串或者打印乱码等的问题
3、成功打印出“Hello Kernel world!!”后,就可以结合前面用bochs调试的程序的代码,在loader.asm 中跳转到保护模式下, 把操作系统的代码写入到Kernel.bin里面,通过Loader.bin 实现操作系统的加载。