仅 FAT16 引导加载程序加载文件的第一个簇
FAT16 Bootloader only Loading First Cluster of a File
我目前正在修复我编写的用于加载自定义实模式 x86 内核的引导加载程序 (SYS.BIN
)。我设法让它读取根目录和 FAT,并从文件系统加载一个小型内核,所有这些都在引导扇区内。然而,我开始用更大的内核对其进行测试,似乎引导加载程序 不会 加载多个集群。我对照另一个类似的引导加载程序检查了我的代码,它似乎在加载多集群文件时有效地做了同样的事情。主要区别在于我将第一个 FAT 加载到段 0x3000
中,将根目录加载到段 0x3800
中,以便内核可以访问它们。 (我完全搞砸了分割吗?)
我应该提一下,我正在通过使用 NASM 进行编译来测试它,将生成的 BOOT.BIN
文件写入原始 32M 图像的第一个扇区,将其安装在循环设备上,复制 SYS.BIN
,并创建该循环设备的新映像,然后我将其作为硬盘驱动器放入 QEMU。我确定它只是加载文件的第一个簇。
特别是,我认为导致问题的代码可能在此处:
.load_cluster:
mov si, msg_load_cluster
call print_str ; Print message
mov ax, word [cluster] ; Our cluster number
sub ax, 0x0002 ; Clusters begin at #2
mul byte [sectors_cluster] ; Multiply by number of sectors
mov dx, ax ; Save in DX
call calc_root_start ; Start of root directory
add ax, 0x20 ; Root directory is 32 sectors
add ax, dx ; Add to the number of sectors
call calc_chs_ls ; Convert this Logical sector to CHS
mov ax, 0x2000
mov es, ax ; Load the kernel into this segment
mov bx, word [buffer_pointer] ; At this offset
mov ah, 0x02 ; Read disk sectors
mov al, byte [sectors_cluster] ; 1 cluster
int 0x13 ; BIOS disk interrupt
jnc .next_cluster ; If no error, set up for the next cluster
call reset_disk ; Otherwise, reset the disk
mov ah, 0x02 ; Read disk sectors
mov al, byte [sectors_cluster] ; 1 cluster
int 0x13 ; Try again
jc reboot ; If failed again, reboot
.next_cluster:
mov ax, 0x3000
mov ds, ax ; Segment where the FAT is loaded
mov si, word [cluster] ; Our cluster number
shl si, 0x1 ; There are two bytes per entry in FAT16
mov ax, word [ds:si] ; DS:SI is pointing to the FAT entry
mov word [cluster], ax ; The entry contains our next cluster
cmp ax, 0xFFF8 ; Is this the end of the file?
mov ax, 0x0200
mul word [sectors_cluster]
add word [buffer_pointer], ax ; Advance pointer by one cluster
jb .load_cluster ; If not, load next cluster
这是我的完整代码,包括 BPB:
BITS 16
jmp strict short main
nop
; BIOS Parameter Block
; This was made to match up with the BPB of a blank 32M image formatted as FAT16.
OEM db "HDOSALPH" ; OEM ID
bytes_sector dw 0x0200 ; Number of bytes per sector (DO NOT CHANGE)
sectors_cluster db 0x04 ; Number of sectors per cluster
reserved dw 0x0001 ; Number of sectors reserved for bootsector
fats db 0x02 ; Number of FAT copies
root_entries dw 0x0200 ; Max number of root entries (DO NOT CHANGE)
sectors dw 0x0000 ; Number of sectors in volume (small)
media_type db 0xF8 ; Media descriptor
sectors_fat dw 0x0040 ; Number of sectors per FAT
sectors_track dw 0x0020 ; Number of sectors per Track (It's a LIE)
heads dw 0x0040 ; Number of heads (It's a LIE)
sectors_hidden dd 0x00000000 ; Number of hidden sectors
sectors_large dd 0x00010000 ; Number of sectors in volume (large)
drive_num db 0x80 ; Drive number
db 0x00 ; Reserved byte
extended_sig db 0x29 ; Next three fields are available
serial dd 0x688B221B ; Volume serial number
label db "NATE " ; Volume label
filesystem db "FAT16 " ; Volume filesystem type
; Main bootloader code
main:
mov ax, 0x07C0 ; Segment we're loaded at
mov ds, ax
add ax, 0x0020 ; 32-paragraph bootloader
mov ss, ax
mov sp, 0x1000 ; 4K stack
mov byte [boot_drive_num], dl ; Save boot drive number
mov ah, 0x08 ; Read disk geometry
int 0x13 ; BIOS disk interrupt
mov dl, dh
mov dh, 0x00
inc dl
mov word [heads], dx ; The true number of heads
mov ch, 0x00
and ch, 0x3F
mov word [sectors_track], cx ; The true number of sectors per track
.load_fat:
mov si, msg_load
call print_str ; Print message
mov ax, 0x3000
mov es, ax ; Load FAT into this segment
mov bx, 0x0000
mov ax, word [reserved] ; First sector of FAT 1
call calc_chs_ls ; Convert to CHS address
mov ax, word [sectors_fat] ; Read the entire FAT
mov ah, 0x02 ; Read disk sectors
int 0x13 ; BIOS disk interrupt
jnc .load_root ; If no error, load the root directory
jmp reboot ; Otherwise, reboot
.load_root:
mov si, msg_load
call print_str ; Print message
mov ax, 0x3800
mov es, ax ; Load root directory into this segment
call calc_root_start ; First sector of root directory
call calc_chs_ls ; Convert to CHS address
mov ah, 0x02 ; Read disk sectors
mov al, 0x20 ; Root directory is 32 sectors (512/512 = 1)
int 0x13 ; BIOS disk interrupt
jnc .search_init ; If no error, begin searching
call reset_disk ; Otherwise, reset the disk
mov ah, 0x02 ; Read disk sectors
mov al, 0x20 ; Root directory is 32 sectors (512/512 = 1)
int 0x13 ; BIOS disk interrupt
jc reboot ; If error, reboot
.search_init:
mov si, msg_search_root
call print_str ; Print message
mov ax, 0x07C0
mov ds, ax ; The segment we are loaded at
mov ax, 0x3800
mov es, ax ; The segment the root directory is loaded at
mov di, 0x0000 ; Offset 0
mov cx, word [root_entries] ; Number of entries to look through
.check_entry:
push cx ; Save this to stack
mov cx, 0x000B ; Compare the first 11 bytes
mov si, kern_filename ; This should be the filename
push di ; Save our location
repe cmpsb ; Compare!
pop di ; Restore our location
pop cx ; Restore the remaining entries
je .found_entry ; If the filenames are the same, we found the entry!
add di, 0x0020 ; Otherwise, move to next entry
loop .check_entry ; And repeat
jmp reboot_fatal ; If we've gone through everything, it's missing
.found_entry:
mov ax, word [es:di+0x1A]
mov word [cluster], ax ; The starting cluster number
.load_cluster:
mov si, msg_load_cluster
call print_str ; Print message
mov ax, word [cluster] ; Our cluster number
sub ax, 0x0002 ; Clusters begin at #2
mul byte [sectors_cluster] ; Multiply by number of sectors
mov dx, ax ; Save in DX
call calc_root_start ; Start of root directory
add ax, 0x20 ; Root directory is 32 sectors
add ax, dx ; Add to the number of sectors
call calc_chs_ls ; Convert this Logical sector to CHS
mov ax, 0x2000
mov es, ax ; Load the kernel into this segment
mov bx, word [buffer_pointer] ; At this offset
mov ah, 0x02 ; Read disk sectors
mov al, byte [sectors_cluster] ; 1 cluster
int 0x13 ; BIOS disk interrupt
jnc .next_cluster ; If no error, set up for the next cluster
call reset_disk ; Otherwise, reset the disk
mov ah, 0x02 ; Read disk sectors
mov al, byte [sectors_cluster] ; 1 cluster
int 0x13 ; Try again
jc reboot ; If failed again, reboot
.next_cluster:
mov ax, 0x3000
mov ds, ax ; Segment where the FAT is loaded
mov si, word [cluster] ; Our cluster number
shl si, 0x1 ; There are two bytes per entry in FAT16
mov ax, word [ds:si] ; DS:SI is pointing to the FAT entry
mov word [cluster], ax ; The entry contains our next cluster
cmp ax, 0xFFF8 ; Is this the end of the file?
mov ax, 0x0200
mul word [sectors_cluster]
add word [buffer_pointer], ax ; Advance pointer by one cluster
jb .load_cluster ; If not, load next cluster
.jump:
mov si, msg_ready
call print_str ; Otherwise, we are ready to jump!
mov ah, 0x00 ; Wait and read from keyboard
int 0x16 ; BIOS keyboard interrupt
mov dl, byte [boot_drive_num] ; Provide the drive number to the kernel
jmp 0x2000:0x0000 ; Jump!
; Calculation routines
calc_root_start: ; Calculate the first sector of the root directory
push dx ; Push register states to stack
mov ax, word [sectors_fat] ; Start with the number of sectors per FAT
mov dh, 0x00
mov dl, byte [fats]
mul dx ; Multiply by the number of FATs
add ax, word [reserved] ; Add the number of reserved sectors
pop dx ; Restore register states
ret ; Return to caller
calc_chs_ls: ; Setup Cylinder-Head-Sector from LBA (AX)
mov dx, 0x0000
div word [sectors_track]
mov cl, dl
inc cl ; Sector number
mov dx, 0x0000
div word [heads]
mov dh, dl ; The remainder is the head number
mov ch, al ; The quotient is the cylinder number
mov dl, byte [boot_drive_num] ; Drive number
ret ; Return to caller
; Other routines
print_str: ; Print string in SI
pusha ; Push register states to stack
mov ax, 0x07C0
mov ds, ax ; Segment in which we are loaded
mov ah, 0x0E ; Teletype output
mov bh, 0x00 ; Page 0
.char:
lodsb ; Load next character
cmp al, 0x00 ; Is it a NULL character?
je .end ; If so, we are done
int 0x10 ; Otherwise, BIOS VGA interrupt
jmp .char ; Repeat
.end:
mov ah, 0x03 ; Get cursor position
int 0x10 ; BIOS VGA interrupt
mov ah, 0x02 ; Set cursor position
inc dh ; One row down
mov dl, 0x00 ; Far left
int 0x10 ; BIOS VGA interrupt
popa ; Restore register states
ret ; Return to caller
reset_disk: ; Reset the disk
push ax ; Push register states to stack
mov si, msg_retrying
call print_str ; Print message
mov ah, 0x00 ; Reset disk
mov dl, byte [boot_drive_num]
int 0x13 ; BIOS disk interrupt
jc reboot_fatal ; If there was an error, reboot
pop ax ; Otherwise, restore register states
ret ; Return to caller
reboot_fatal: ; Display FATAL
mov si, msg_fatal
call print_str ; Print message
reboot: ; Prompt user to press a key and reboot
mov si, msg_reboot
call print_str ; Print message
mov si, msg_ready
call print_str ; Print message
mov ah, 0x00 ; Wait and read from keyboard
int 0x16 ; BIOS keyboard interrupt
int 0x19 ; Reboot
; Data
data:
cluster dw 0x0000
buffer_pointer dw 0x0000
boot_drive_num db 0x00
msg_retrying db "RE", 0x00
msg_fatal db "FATL", 0x00
msg_reboot db "X", 0x00
msg_search_root db "Srch", 0x00
msg_load_cluster db "Clstr", 0x00
msg_ready db "GO", 0x00
msg_load db "Press a key", 0x00
kern_filename db "SYS BIN"
times 510-($-$$) db 0x00 ; Pad remainder of bootsector with zeroes
boot_sig dw 0xAA55 ; Boot signature
在此先感谢您的帮助。
更新: 我在 BOCHS 调试器中 运行 这个,似乎程序正在将 cluster
中的单词加载为 0x0003
在 .load_cluster
下,但随后在 .next_cluster
下成为 0x0000
几条指令。
您的 mov ax, word [ds:si]
有一个不需要的 ds
段覆盖。
这也与你的变量问题有关,内存访问使用ds
作为默认段。所以在 mov ax, 0x3000
\ mov ds, ax
之后你不再访问你的原始变量。
您必须将 ds
重置为 7C0h,因为您的加载程序使用默认值 org 0
。您的 print_str
函数确实会像那样重置 ds
。但是 mov si, word [cluster]
以及 .next_cluster
和 .jump
中的 FAT 字访问之间的所有内容都使用了错误的 ds
。要更正此问题,请像这样更改您的代码,例如:
mov si, word [cluster]
shl si, 0x1
push ds
mov ax, 0x3000
mov ds, ax
mov ax, word [si]
pop ds
mov word [cluster], ax
另一个错误:.jump
之前的 jb
使用进位标志。但是,该标志不会如您所愿地从 cmp
中保留下来,因为 add
肯定(并且 mul
可能)会覆盖进位标志。
更多问题:
您假定根目录大小。
您假定扇区大小。 (公平地说,很多装载机都这样做。)
您假设 FAT 适合 64 KiB。它实际上可以增长到近 128 KiB。
在 mov ax, word [sectors_fat]
\ mov ah, 0x02
中,第二次写入会覆盖 ax
的上半部分,因此这仅适用于最多 255 个扇区的 FAT。
您假设一次 int 13h
调用最多可以读取 255 个扇区。这对于不支持磁道交叉读取请求的 ROM-BIOS 来说可能是错误的。这就是大多数加载程序一次加载一个扇区的原因。
您假设您的内核适合 64 KiB。
您假设您可以完全加载从 FAT 映射的内核。对于大型集群,这可能是不可能的,这是大多数加载协议只加载一定数量数据的原因。
在inc dl
\mov word [heads], dx
中你应该使用inc dx
.
在 and ch, 0x3F
\ mov word [sectors_track], cx
你打算使用 and cl, 3Fh
.
在 mul dx
\add ax, word [reserved]
中你应该添加adc dx, 0
.
您更普遍地使用 16 位扇区号。这对于一个玩具示例来说是可以的。现代加载程序使用 32 位扇区号。
您使用的是CHS磁盘读取接口(正常int 13h
)。还有 LBA 接口(int 13h
扩展)。仅当 CHS 几何形状未知或可访问扇区范围太小时才需要。
您正在使用 int 13h ah=08h
检索 CHS 几何图形。并非所有磁盘或 ROM-BIOS 都支持此调用。 (特别是软盘可能不支持,但那些也使用 FAT12 作为你不支持的文件系统。)
您没有使用隐藏扇区,尽管在您的 BPB 中这是零。如果你想从分区加载(在 MBR 分区方案中),你需要添加它们以从与文件系统相关的扇区号中获取单位扇区号。
我目前正在修复我编写的用于加载自定义实模式 x86 内核的引导加载程序 (SYS.BIN
)。我设法让它读取根目录和 FAT,并从文件系统加载一个小型内核,所有这些都在引导扇区内。然而,我开始用更大的内核对其进行测试,似乎引导加载程序 不会 加载多个集群。我对照另一个类似的引导加载程序检查了我的代码,它似乎在加载多集群文件时有效地做了同样的事情。主要区别在于我将第一个 FAT 加载到段 0x3000
中,将根目录加载到段 0x3800
中,以便内核可以访问它们。 (我完全搞砸了分割吗?)
我应该提一下,我正在通过使用 NASM 进行编译来测试它,将生成的 BOOT.BIN
文件写入原始 32M 图像的第一个扇区,将其安装在循环设备上,复制 SYS.BIN
,并创建该循环设备的新映像,然后我将其作为硬盘驱动器放入 QEMU。我确定它只是加载文件的第一个簇。
特别是,我认为导致问题的代码可能在此处:
.load_cluster:
mov si, msg_load_cluster
call print_str ; Print message
mov ax, word [cluster] ; Our cluster number
sub ax, 0x0002 ; Clusters begin at #2
mul byte [sectors_cluster] ; Multiply by number of sectors
mov dx, ax ; Save in DX
call calc_root_start ; Start of root directory
add ax, 0x20 ; Root directory is 32 sectors
add ax, dx ; Add to the number of sectors
call calc_chs_ls ; Convert this Logical sector to CHS
mov ax, 0x2000
mov es, ax ; Load the kernel into this segment
mov bx, word [buffer_pointer] ; At this offset
mov ah, 0x02 ; Read disk sectors
mov al, byte [sectors_cluster] ; 1 cluster
int 0x13 ; BIOS disk interrupt
jnc .next_cluster ; If no error, set up for the next cluster
call reset_disk ; Otherwise, reset the disk
mov ah, 0x02 ; Read disk sectors
mov al, byte [sectors_cluster] ; 1 cluster
int 0x13 ; Try again
jc reboot ; If failed again, reboot
.next_cluster:
mov ax, 0x3000
mov ds, ax ; Segment where the FAT is loaded
mov si, word [cluster] ; Our cluster number
shl si, 0x1 ; There are two bytes per entry in FAT16
mov ax, word [ds:si] ; DS:SI is pointing to the FAT entry
mov word [cluster], ax ; The entry contains our next cluster
cmp ax, 0xFFF8 ; Is this the end of the file?
mov ax, 0x0200
mul word [sectors_cluster]
add word [buffer_pointer], ax ; Advance pointer by one cluster
jb .load_cluster ; If not, load next cluster
这是我的完整代码,包括 BPB:
BITS 16
jmp strict short main
nop
; BIOS Parameter Block
; This was made to match up with the BPB of a blank 32M image formatted as FAT16.
OEM db "HDOSALPH" ; OEM ID
bytes_sector dw 0x0200 ; Number of bytes per sector (DO NOT CHANGE)
sectors_cluster db 0x04 ; Number of sectors per cluster
reserved dw 0x0001 ; Number of sectors reserved for bootsector
fats db 0x02 ; Number of FAT copies
root_entries dw 0x0200 ; Max number of root entries (DO NOT CHANGE)
sectors dw 0x0000 ; Number of sectors in volume (small)
media_type db 0xF8 ; Media descriptor
sectors_fat dw 0x0040 ; Number of sectors per FAT
sectors_track dw 0x0020 ; Number of sectors per Track (It's a LIE)
heads dw 0x0040 ; Number of heads (It's a LIE)
sectors_hidden dd 0x00000000 ; Number of hidden sectors
sectors_large dd 0x00010000 ; Number of sectors in volume (large)
drive_num db 0x80 ; Drive number
db 0x00 ; Reserved byte
extended_sig db 0x29 ; Next three fields are available
serial dd 0x688B221B ; Volume serial number
label db "NATE " ; Volume label
filesystem db "FAT16 " ; Volume filesystem type
; Main bootloader code
main:
mov ax, 0x07C0 ; Segment we're loaded at
mov ds, ax
add ax, 0x0020 ; 32-paragraph bootloader
mov ss, ax
mov sp, 0x1000 ; 4K stack
mov byte [boot_drive_num], dl ; Save boot drive number
mov ah, 0x08 ; Read disk geometry
int 0x13 ; BIOS disk interrupt
mov dl, dh
mov dh, 0x00
inc dl
mov word [heads], dx ; The true number of heads
mov ch, 0x00
and ch, 0x3F
mov word [sectors_track], cx ; The true number of sectors per track
.load_fat:
mov si, msg_load
call print_str ; Print message
mov ax, 0x3000
mov es, ax ; Load FAT into this segment
mov bx, 0x0000
mov ax, word [reserved] ; First sector of FAT 1
call calc_chs_ls ; Convert to CHS address
mov ax, word [sectors_fat] ; Read the entire FAT
mov ah, 0x02 ; Read disk sectors
int 0x13 ; BIOS disk interrupt
jnc .load_root ; If no error, load the root directory
jmp reboot ; Otherwise, reboot
.load_root:
mov si, msg_load
call print_str ; Print message
mov ax, 0x3800
mov es, ax ; Load root directory into this segment
call calc_root_start ; First sector of root directory
call calc_chs_ls ; Convert to CHS address
mov ah, 0x02 ; Read disk sectors
mov al, 0x20 ; Root directory is 32 sectors (512/512 = 1)
int 0x13 ; BIOS disk interrupt
jnc .search_init ; If no error, begin searching
call reset_disk ; Otherwise, reset the disk
mov ah, 0x02 ; Read disk sectors
mov al, 0x20 ; Root directory is 32 sectors (512/512 = 1)
int 0x13 ; BIOS disk interrupt
jc reboot ; If error, reboot
.search_init:
mov si, msg_search_root
call print_str ; Print message
mov ax, 0x07C0
mov ds, ax ; The segment we are loaded at
mov ax, 0x3800
mov es, ax ; The segment the root directory is loaded at
mov di, 0x0000 ; Offset 0
mov cx, word [root_entries] ; Number of entries to look through
.check_entry:
push cx ; Save this to stack
mov cx, 0x000B ; Compare the first 11 bytes
mov si, kern_filename ; This should be the filename
push di ; Save our location
repe cmpsb ; Compare!
pop di ; Restore our location
pop cx ; Restore the remaining entries
je .found_entry ; If the filenames are the same, we found the entry!
add di, 0x0020 ; Otherwise, move to next entry
loop .check_entry ; And repeat
jmp reboot_fatal ; If we've gone through everything, it's missing
.found_entry:
mov ax, word [es:di+0x1A]
mov word [cluster], ax ; The starting cluster number
.load_cluster:
mov si, msg_load_cluster
call print_str ; Print message
mov ax, word [cluster] ; Our cluster number
sub ax, 0x0002 ; Clusters begin at #2
mul byte [sectors_cluster] ; Multiply by number of sectors
mov dx, ax ; Save in DX
call calc_root_start ; Start of root directory
add ax, 0x20 ; Root directory is 32 sectors
add ax, dx ; Add to the number of sectors
call calc_chs_ls ; Convert this Logical sector to CHS
mov ax, 0x2000
mov es, ax ; Load the kernel into this segment
mov bx, word [buffer_pointer] ; At this offset
mov ah, 0x02 ; Read disk sectors
mov al, byte [sectors_cluster] ; 1 cluster
int 0x13 ; BIOS disk interrupt
jnc .next_cluster ; If no error, set up for the next cluster
call reset_disk ; Otherwise, reset the disk
mov ah, 0x02 ; Read disk sectors
mov al, byte [sectors_cluster] ; 1 cluster
int 0x13 ; Try again
jc reboot ; If failed again, reboot
.next_cluster:
mov ax, 0x3000
mov ds, ax ; Segment where the FAT is loaded
mov si, word [cluster] ; Our cluster number
shl si, 0x1 ; There are two bytes per entry in FAT16
mov ax, word [ds:si] ; DS:SI is pointing to the FAT entry
mov word [cluster], ax ; The entry contains our next cluster
cmp ax, 0xFFF8 ; Is this the end of the file?
mov ax, 0x0200
mul word [sectors_cluster]
add word [buffer_pointer], ax ; Advance pointer by one cluster
jb .load_cluster ; If not, load next cluster
.jump:
mov si, msg_ready
call print_str ; Otherwise, we are ready to jump!
mov ah, 0x00 ; Wait and read from keyboard
int 0x16 ; BIOS keyboard interrupt
mov dl, byte [boot_drive_num] ; Provide the drive number to the kernel
jmp 0x2000:0x0000 ; Jump!
; Calculation routines
calc_root_start: ; Calculate the first sector of the root directory
push dx ; Push register states to stack
mov ax, word [sectors_fat] ; Start with the number of sectors per FAT
mov dh, 0x00
mov dl, byte [fats]
mul dx ; Multiply by the number of FATs
add ax, word [reserved] ; Add the number of reserved sectors
pop dx ; Restore register states
ret ; Return to caller
calc_chs_ls: ; Setup Cylinder-Head-Sector from LBA (AX)
mov dx, 0x0000
div word [sectors_track]
mov cl, dl
inc cl ; Sector number
mov dx, 0x0000
div word [heads]
mov dh, dl ; The remainder is the head number
mov ch, al ; The quotient is the cylinder number
mov dl, byte [boot_drive_num] ; Drive number
ret ; Return to caller
; Other routines
print_str: ; Print string in SI
pusha ; Push register states to stack
mov ax, 0x07C0
mov ds, ax ; Segment in which we are loaded
mov ah, 0x0E ; Teletype output
mov bh, 0x00 ; Page 0
.char:
lodsb ; Load next character
cmp al, 0x00 ; Is it a NULL character?
je .end ; If so, we are done
int 0x10 ; Otherwise, BIOS VGA interrupt
jmp .char ; Repeat
.end:
mov ah, 0x03 ; Get cursor position
int 0x10 ; BIOS VGA interrupt
mov ah, 0x02 ; Set cursor position
inc dh ; One row down
mov dl, 0x00 ; Far left
int 0x10 ; BIOS VGA interrupt
popa ; Restore register states
ret ; Return to caller
reset_disk: ; Reset the disk
push ax ; Push register states to stack
mov si, msg_retrying
call print_str ; Print message
mov ah, 0x00 ; Reset disk
mov dl, byte [boot_drive_num]
int 0x13 ; BIOS disk interrupt
jc reboot_fatal ; If there was an error, reboot
pop ax ; Otherwise, restore register states
ret ; Return to caller
reboot_fatal: ; Display FATAL
mov si, msg_fatal
call print_str ; Print message
reboot: ; Prompt user to press a key and reboot
mov si, msg_reboot
call print_str ; Print message
mov si, msg_ready
call print_str ; Print message
mov ah, 0x00 ; Wait and read from keyboard
int 0x16 ; BIOS keyboard interrupt
int 0x19 ; Reboot
; Data
data:
cluster dw 0x0000
buffer_pointer dw 0x0000
boot_drive_num db 0x00
msg_retrying db "RE", 0x00
msg_fatal db "FATL", 0x00
msg_reboot db "X", 0x00
msg_search_root db "Srch", 0x00
msg_load_cluster db "Clstr", 0x00
msg_ready db "GO", 0x00
msg_load db "Press a key", 0x00
kern_filename db "SYS BIN"
times 510-($-$$) db 0x00 ; Pad remainder of bootsector with zeroes
boot_sig dw 0xAA55 ; Boot signature
在此先感谢您的帮助。
更新: 我在 BOCHS 调试器中 运行 这个,似乎程序正在将 cluster
中的单词加载为 0x0003
在 .load_cluster
下,但随后在 .next_cluster
下成为 0x0000
几条指令。
您的 mov ax, word [ds:si]
有一个不需要的 ds
段覆盖。
这也与你的变量问题有关,内存访问使用ds
作为默认段。所以在 mov ax, 0x3000
\ mov ds, ax
之后你不再访问你的原始变量。
您必须将 ds
重置为 7C0h,因为您的加载程序使用默认值 org 0
。您的 print_str
函数确实会像那样重置 ds
。但是 mov si, word [cluster]
以及 .next_cluster
和 .jump
中的 FAT 字访问之间的所有内容都使用了错误的 ds
。要更正此问题,请像这样更改您的代码,例如:
mov si, word [cluster]
shl si, 0x1
push ds
mov ax, 0x3000
mov ds, ax
mov ax, word [si]
pop ds
mov word [cluster], ax
另一个错误:.jump
之前的 jb
使用进位标志。但是,该标志不会如您所愿地从 cmp
中保留下来,因为 add
肯定(并且 mul
可能)会覆盖进位标志。
更多问题:
您假定根目录大小。
您假定扇区大小。 (公平地说,很多装载机都这样做。)
您假设 FAT 适合 64 KiB。它实际上可以增长到近 128 KiB。
在
mov ax, word [sectors_fat]
\mov ah, 0x02
中,第二次写入会覆盖ax
的上半部分,因此这仅适用于最多 255 个扇区的 FAT。您假设一次
int 13h
调用最多可以读取 255 个扇区。这对于不支持磁道交叉读取请求的 ROM-BIOS 来说可能是错误的。这就是大多数加载程序一次加载一个扇区的原因。您假设您的内核适合 64 KiB。
您假设您可以完全加载从 FAT 映射的内核。对于大型集群,这可能是不可能的,这是大多数加载协议只加载一定数量数据的原因。
在
inc dl
\mov word [heads], dx
中你应该使用inc dx
.在
and ch, 0x3F
\mov word [sectors_track], cx
你打算使用and cl, 3Fh
.在
mul dx
\add ax, word [reserved]
中你应该添加adc dx, 0
.您更普遍地使用 16 位扇区号。这对于一个玩具示例来说是可以的。现代加载程序使用 32 位扇区号。
您使用的是CHS磁盘读取接口(正常
int 13h
)。还有 LBA 接口(int 13h
扩展)。仅当 CHS 几何形状未知或可访问扇区范围太小时才需要。您正在使用
int 13h ah=08h
检索 CHS 几何图形。并非所有磁盘或 ROM-BIOS 都支持此调用。 (特别是软盘可能不支持,但那些也使用 FAT12 作为你不支持的文件系统。)您没有使用隐藏扇区,尽管在您的 BPB 中这是零。如果你想从分区加载(在 MBR 分区方案中),你需要添加它们以从与文件系统相关的扇区号中获取单位扇区号。