gpt4 book ai didi

assembly - 为什么我的根目录没有被加载? (FAT12)

转载 作者:行者123 更新时间:2023-12-02 19:19:00 33 4
gpt4 key购买 nike

我正在编写汇编中的阶段1引导加载程序,试图将FAT12文件系统加载到内存中,以便可以加载阶段2引导加载程序。我已经设法将FAT加载到内存中,但是我正在努力将根目录加载到内存中。

我目前正在使用this供引用,并产生了以下内容:

.load_root:
;es is 0x7c0
xor dx, dx ; blank dx for division
mov si, fat_loaded ; inform user that FAT is loaded
call print
mov al, [FATcount] ; calculate how many sectors into the disk must be loaded
mul word [SectorsPerFAT]
add al, [ReservedSectors]
div byte [SectorsPerTrack]
mov ch, ah ; Store quotient in ch for cylinder number
mov cl, al ; Store remainder in cl for sector number

xor dx, dx
xor ax, ax
mov al, ch ; get back to "absolute" sector number
mul byte [SectorsPerTrack]
add al, cl
mul word [BytesPerSector]
mov bx,ax ; Memory offset to load to data into memory after BOTH FATs (should be 0x2600, physical address should be 0xA200)

xor dx, dx ; blank dx for division
mov ax, 32
mul word [MaxDirEntries]
div word [BytesPerSector] ; number of sectors root directory takes up (should be 14)

xor dh, dh ; head 0
mov dl, [boot_device] ; boot device

mov ah, 0x02 ; select read mode

int 13h
cmp ah, 0
je .load_OS
mov si, error_text
call print
jmp $

但是,如果我用gdb检查 0xA200处的内存,我只会看到0。我的根目录确实包含一个文件-我已经在根目录中放置了一个名为OS.BIN的文件进行测试。

在读取操作之后在gdb中使用 info registers会给出以下输出:
eax            0xe      14
ecx 0x101 257
edx 0x0 0
ebx 0x2600 9728
esp 0x76d0 0x76d0
ebp 0x0 0x0
esi 0x16d 365
edi 0x0 0
eip 0x7cdd 0x7cdd
eflags 0x246 [ PF ZF IF ]
cs 0x0 0
ss 0x53 83
ds 0x7c0 1984
es 0x7c0 1984
fs 0x0 0
gs 0x0 0

该操作的状态为0,读取的扇区数为14,并且 es:bx指向0xA200,但是当我希望看到OS.BIN的数据时, x/32b 0xa200显示32个0。

编辑
我在中断之前做了 info registers,输出如下:
eax            0x20e    526
ecx 0x101 257
edx 0x0 0
ebx 0x2600 9728
esp 0x76d0 0x76d0
ebp 0x0 0x0
esi 0x161 353
edi 0x0 0
eip 0x7cc8 0x7cc8
eflags 0x246 [ PF ZF IF ]
cs 0x0 0
ss 0x53 83
ds 0x7c0 1984
es 0x7c0 1984
fs 0x0 0
gs 0x0 0

除功能请求号已替换为状态码外,其余与之后相同。

我要去哪里错了?我是从错误的CHS地址读取吗?还是其他一些简单的错误?我该如何纠正呢?

我正在使用 fat_imgen制作磁盘镜像。用于创建磁盘镜像的命令是 fat_imgen -c -f floppy.flp -F -s bootloader.bin,用于将 OS.BIN添加到镜像的命令是 fat_imgen -m -f floppy.flp -i OS.BIN
我有一个 BIOS Parameter Block(BPB),代表使用FAT12的1.44MB软盘:
jmp short loader
times 9 db 0

BytesPerSector: dw 512
SectorsPerCluster: db 1
ReservedSectors: dw 1
FATcount: db 2
MaxDirEntries: dw 224
TotalSectors: dw 2880
db 0
SectorsPerFAT: dw 9
SectorsPerTrack: dw 18
NumberOfHeads: dw 2
dd 0
dd 0
dw 0
BootSignature: db 0x29
VolumeID: dd 77
VolumeLabel: db "Bum'dOS ",0
FSType: db "FAT12 "

我还有另一个似乎可以正常工作的功能,可以将FAT12表加载到内存地址0x7c0:0x0200(物理地址0x07e00):
;;;Start loading File Allocation Table (FAT)
.load_fat:
mov ax, 0x07c0 ; address from start of programs
mov es, ax
mov ah, 0x02 ; set to read
mov al, [SectorsPerFAT] ; how many sectors to load
xor ch, ch ; cylinder 0
mov cl, [ReservedSectors] ; Load FAT1
add cl, byte 1
xor dh, dh ; head 0
mov bx, 0x0200 ; read data to 512B after start of code
int 13h
cmp ah, 0
je .load_root
mov si, error_text
call print
hlt

最佳答案

问题分析

代码的问题在于,您没有从所需的磁盘上读取数据。尽管您的磁盘读取成功,但已将错误的扇区加载到内存中。

如果我们查看Ralph Brown的Int 13h/AH=2中断列表,我们将看到输入看起来像这样:

DISK - READ SECTOR(S) INTO MEMORY

AH = 02h
AL = number of sectors to read (must be nonzero)
CH = low eight bits of cylinder number
CL = sector number 1-63 (bits 0-5)
high two bits of cylinder (bits 6-7, hard disk only)
DH = head number
DL = drive number (bit 7 set for hard disk)
ES:BX -> data buffer


如果我们在执行 int 13h中的 .load_root之前先检查您的寄存器,则会看到这些寄存器具有以下内容:

eax            0x20e   
ecx 0x101
edx 0x0
ebx 0x2600
es 0x7c0


因此ES:BX是0x7c0:0x2600,它是物理地址0xA200。那是正确的。 AH(0x02)是磁盘读取,AL中要读取的扇区数是14(0x0e)。这似乎是合理的。问题出现在ECX和EDX中。如果我们查看您的代码,则您似乎正在尝试在根目录开始的磁盘上找到扇区(逻辑块地址):
mov al, [FATcount]      ; calculate how many sectors into the disk must be loaded
mul word [SectorsPerFAT]
add al, [ReservedSectors]

在您的BIOS参数块中,您的 SectorsPerFat = 9, ReservedSectors = 1和 FATCount =2。如果我们查看显示此配置的 FAT12 design document,它将看起来像:

enter image description here

您的计算是正确的。 2 * 9 + 1 =19。前19个逻辑块从LBA 0到LBA 18运行。LBA19是您的根目录开始的位置。我们需要将其转换为汽缸/缸盖/扇形(CHS)。 Logical Block Address to CHS calculation:

CHS tuples can be mapped to LBA address with the following formula:

LBA = (C × HPC + H) × SPT + (S - 1)

where C, H and S are the cylinder number, the head number, and the sector number

LBA is the logical block address
HPC is the maximum number of heads per cylinder (reported by
disk drive, typically 16 for 28-bit LBA)
SPT is the maximum number of sectors per track (reported by
disk drive, typically 63 for 28-bit LBA)
LBA addresses can be mapped to CHS tuples with the following formula
("mod" is the modulo operation, i.e. the remainder, and "÷" is
integer division, i.e. the quotient of the division where any
fractional part is discarded):

C = LBA ÷ (HPC × SPT)
H = (LBA ÷ SPT) mod HPC
S = (LBA mod SPT) + 1


在您的代码中SPT = 18,HPC =2。如果我们使用19的LBA,则我们计算出的CHS为C = 0,H = 1,S = 2。如果我们看一下您传递到上面的寄存器(CL,CH,DH)中的值,就会发现您使用的CHS为C = 1,H = 0,S = 1。这恰好是LBA 36,而不是19。问题是您的计算错误。特别是 .load_root:
div byte [SectorsPerTrack]
mov ch, ah ; Store quotient in ch for cylinder number
mov cl, al ; Store remainder in cl for sector number
[snip]
xor dh, dh ; head 0
mov dl, [boot_device] ; boot device
mov ah, 0x02 ; select read mode
int 13h

不幸的是,这不是从LBA计算CHS的正确方法。 .load_fat也有类似的问题,但是很幸运您计算出正确的值。您正在从磁盘上的错误扇区读取数据,这导致意外的数据加载到0xA200。

LBA到CHS的翻译

您需要一个适当的LBA到CHS转换例程。由于在导航FAT12文件结构的不同方面将需要这样的功能,因此最好创建一个功能。我们将其称为 lba_to_chs

在编写这样的代码之前,我们应该更早地回顾一下等式:

C = LBA ÷ (HPC × SPT)
H = (LBA ÷ SPT) mod HPC
S = (LBA mod SPT) + 1


我们可以按原样实现,但是如果重新构造气缸的方程式,我们可以减少要做的工作量。 C = LBA ÷ (HPC × SPT)可以重写为:
C = LBA ÷ (HPC × SPT)
C = LBA ÷ (SPT × HPC)
C = (LBA ÷ SPT) × (1 ÷ HPC)
C = (LBA ÷ SPT) ÷ HPC

现在,如果我们看一下修改后的公式,则可以得出:
C = (LBA ÷ SPT) ÷ HPC
H = (LBA ÷ SPT) mod HPC
S = (LBA mod SPT) + 1

现在我们应该注意到 (LBA ÷ SPT)在两个地方重复了。我们只需要做一次等式。同样,由于x86 DIV指令同时计算余数和商,我们在执行 LBA mod SPT时最终也免费计算了 (LBA ÷ SPT)。该代码将遵循以下结构:
  • 计算LBA DIV SPT。这将产生:

  • 中的
  • (LBA ÷ SPT) 其余部分
  • 中的
  • (LBA mod SPT)
  • 从步骤(1)取余数,并放入临时寄存器
  • 在步骤(2)中向临时目录添加1。该寄存器现在包含由S = (LBA mod SPT) + 1
  • 计算的扇区
  • 从步骤(1)中取商,然后除以HPC。
  • 缸数将是商
  • 剩下的就是头了。

  • 我们将等式简化为几个DIV指令和一个增量/加法。我们可以进一步简化事情。如果假设我们使用的是众所周知的IBM兼容磁盘格式,那么我们也可以说每磁道的扇区数(SPT),磁头(HPC),圆柱体,磁头和扇区始终小于256。已知的软盘格式除以SPT所得的结果始终小于256。知道这一点后,我们可以避免位旋转圆柱的顶部两位并将其放在CL的顶部两位中。我们还可以使用DIV指令执行16位乘8位无符号除法。

    翻译代码

    如果采用上面的伪代码,我们可以创建一个相当小的 lba_to_chs函数,该函数采用LBA并将其转换为CHS并适用于所有与IBM兼容的众所周知的软盘格式。
    ;    Function: lba_to_chs
    ; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
    ; Works for all valid FAT12 compatible disk geometries.
    ;
    ; Resources: http://www.ctyme.com/intr/rb-0607.htm
    ; https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
    ; https://stackoverflow.com/q/45434899/3857942
    ; Sector = (LBA mod SPT) + 1
    ; Head = (LBA / SPT) mod HEADS
    ; Cylinder = (LBA / SPT) / HEADS
    ;
    ; Inputs: SI = LBA
    ; Outputs: DL = Boot Drive Number
    ; DH = Head
    ; CH = Cylinder (lower 8 bits of 10-bit cylinder)
    ; CL = Sector/Cylinder
    ; Upper 2 bits of 10-bit Cylinders in upper 2 bits of CL
    ; Sector in lower 6 bits of CL
    ;
    ; Notes: Output registers match expectation of Int 13h/AH=2 inputs
    ;
    lba_to_chs:
    push ax ; Preserve AX
    mov ax, si ; Copy LBA to AX
    xor dx, dx ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [SectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT
    mov cl, dl ; CL = S = LBA mod SPT
    inc cl ; CL = S = (LBA mod SPT) + 1
    xor dx, dx ; Upper 16-bit of 32-bit value set to 0 for DIV
    div word [NumberOfHeads] ; 32-bit by 16-bit DIV : (LBA / SPT) / HEADS
    mov dh, dl ; DH = H = (LBA / SPT) mod HEADS
    mov dl, [boot_device] ; boot device, not necessary to set but convenient
    mov ch, al ; CH = C(lower 8 bits) = (LBA / SPT) / HEADS
    shl ah, 6 ; Store upper 2 bits of 10-bit Cylinder into
    or cl, ah ; upper 2 bits of Sector (CL)
    pop ax ; Restore scratch registers
    ret

    您可以使用此 lba_to_chs函数并将其集成到 .load_fat.load_root代码中。您的代码可能如下所示:
    ;;;Start loading File Allocation Table (FAT)
    .load_fat:
    mov ax, 0x07c0 ; address from start of programs
    mov es, ax
    mov ah, 0x02 ; set to read
    mov al, [SectorsPerFAT] ; how many sectors to load

    mov si, [ReservedSectors] ; Load FAT1 into SI for input to lba_to_chs
    call lba_to_chs ; Retrieve CHS parameters and boot drive for LBA

    mov bx, 0x0200 ; read data to 512B after start of code
    int 13h
    cmp ah, 0
    je .load_root
    mov si, error_text
    call print
    hlt

    ;;;Start loading root directory
    .load_root:
    mov si, fat_loaded
    call print
    xor ax, ax
    mov al, [FATcount]
    mul word [SectorsPerFAT]
    add ax, [ReservedSectors] ; Compute LBA of oot directory entries
    mov si, ax ; Copy LBA to SI for later call to lba_to_chs

    mul word [BytesPerSector]
    mov bx,ax ; Load to after BOTH FATs in memory

    mov ax, 32
    cwd ; Zero dx for division
    ; (works since AX(32) < 0x8000)
    mul word [MaxDirEntries]
    div word [BytesPerSector] ; number of sectors to read

    call lba_to_chs ; Retrieve CHS values and load boot drive
    mov ah, 0x02
    int 13h
    cmp ah, 0
    je .load_OS
    mov si, error_text
    call print
    jmp $

    关于assembly - 为什么我的根目录没有被加载? (FAT12),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45434899/

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