gpt4 book ai didi

assembly - FAT16 Bootloader 仅加载文件的第一个簇

转载 作者:行者123 更新时间:2023-12-03 23:05:15 26 4
gpt4 key购买 nike

我目前正在修复我为加载自定义实模式 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 下, 但随后为 0x0000.next_cluster 下几条指令后。

最佳答案

您的 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 中 FAT 字访问之间的所有内容直到 .jump使用错误 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
另一个错误: jb就在 .jump 之前使用进位标志。但是, 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 映射的方式加载内核。对于大型集群,这可能是不可能的,这是大多数加载协议(protocol)仅加载一定数量数据的原因。
  • 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 磁盘读取接口(interface)(正常 int 13h )。还有 LBA 接口(interface)(int 13h 扩展)。仅当 CHS 几何形状未知或适用于可访问扇区的范围太小时才需要。
  • 您正在使用 int 13h ah=08h检索 CHS 几何。并非所有磁盘或 ROM-BIOS 都支持此调用。 (尤其是软盘可能不支持它,但那些也使用 FAT12 作为您不支持的文件系统。)
  • 您没有使用隐藏扇区,尽管在您的 BPB 中这无论如何都是零。如果要从分区加载(在 MBR 分区方案中),则需要添加它们以从相对于文件系统的那些中获取单位扇区号。
  • 关于assembly - FAT16 Bootloader 仅加载文件的第一个簇,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63144980/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com