Assembly FAT16引导加载程序仅加载文件的第一个群集

Assembly FAT16引导加载程序仅加载文件的第一个群集,assembly,nasm,x86-16,bootloader,fat16,Assembly,Nasm,X86 16,Bootloader,Fat16,我目前正在修复一个引导加载程序,我编写这个引导加载程序是为了加载我的定制实模式x86内核(SYS.BIN)。我设法让它读取根目录和FAT,并从文件系统加载一个小的ish内核,所有这些都在引导扇区内。然而,我开始用更大的内核测试它,而且引导加载程序似乎不会加载多个集群。我对照另一个类似的引导加载程序检查了我的代码,在加载多集群文件时,它似乎也在做同样的事情。主要区别在于,我正在将第一个FAT加载到段0x3000中,并将根目录加载到段0x3800,以便内核可以访问它们。(我把分割搞砸了吗?) 我可能

我目前正在修复一个引导加载程序,我编写这个引导加载程序是为了加载我的定制实模式x86内核(
SYS.BIN
)。我设法让它读取根目录和FAT,并从文件系统加载一个小的ish内核,所有这些都在引导扇区内。然而,我开始用更大的内核测试它,而且引导加载程序似乎不会加载多个集群。我对照另一个类似的引导加载程序检查了我的代码,在加载多集群文件时,它似乎也在做同样的事情。主要区别在于,我正在将第一个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调试器中运行了这个程序,似乎该程序正在
集群中
将单词加载为
下的
0x0003
。加载集群
,然后在
下加载为
0x0000
。接下来的
集群
后面会有一些指令。

你的
mov ax,单词[ds:si]
具有不必要的
ds
段覆盖

这也与您的变量问题有关,内存访问使用
ds
作为默认段。因此,在
mov-ax,0x3000
\
mov-ds,ax
之后,您不再访问原始变量

您必须将
ds
重置为7C0h,因为加载程序使用默认的
org 0
。您的
print\u str
功能确实会像那样重置
ds
。但是
mov-si、word[cluster]
中FAT-word访问之间的所有内容。next\u cluster
和up to
。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
另一个错误:就在
.jump
之前的
jb
使用进位标志。但是,
cmp
不会根据需要保留该标志,因为
add
肯定(并且
mul
可能)会覆盖进位标志

更多问题:

  • 您假定根目录的大小

  • 假设扇区大小。(公平地说,很多装载机都是这样做的。)

  • 你假设脂肪可以容纳64千磅。它实际上可以增长到近128千磅

  • mov-ax中,单词[sections\u-fat]
    \
    mov-ah,0x02
    第二次写入会覆盖
    ax
    的上半部分,因此这只适用于最多255个扇区的fat

  • 假设一次
    int13h
    调用最多可以读取255个扇区。这对于ROM BIOS可能是错误的,例如,它不支持磁道交叉读取请求。这就是为什么大多数装载机一次装载一个扇区

  • 您假设您的内核将适合64kib

  • 您假设可以完全按照FAT映射的方式加载内核。对于较大的集群大小,这可能是不可能的,这也是大多数加载协议只加载一定数量的数据的原因

  • inc-dl
    \
    mov-word[heads]中,dx
    应该使用
    inc-dx

  • 和ch,0x3F
    \
    mov-word[sections\u-track],cx
    中,您打算使用
    和cl,3Fh

  • mul-dx
    \
    中添加ax,word[保留]
    您应该添加
    adc-dx,0

  • 您通常使用16位扇区号。这对于一个玩具的例子来说是可以的。现代装载机使用32位扇区号

  • 您正在使用CHS磁盘读取接口(正常
    int13h
    )。还有LBA接口(
    int13h
    extensions)。仅当CHS几何结构未知或可访问扇区范围太小时才需要

  • 您正在使用
    int 13h ah=08h
    检索CHS几何图形。并非所有磁盘或ROM都支持此调用。(特别是磁盘可能不支持FAT12,但它们也使用FAT12作为文件系统,而您不支持FAT12。)

  • 您没有使用隐藏扇区,尽管在BPB中,这无论如何都是零。如果要从分区(在MBR分区方案中)加载,则需要添加这些分区,以从与文件系统相关的分区中获取单元扇区号


这里是供参考的。谢谢您的帮助!一切似乎都在运转。我不会发现的。