我正在编写程序集中的第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,但x/32b 0xa200
显示32 0,当我希望看到OS的数据时。 BIN。
修改
我在中断之前做了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
答案 0 :(得分:4)
您的代码存在的问题是您无法从磁盘上的某个位置读取您期望的内容。虽然您的磁盘读取成功,但它已将错误的扇区加载到内存中。
如果我们查看Ralph Brown的Int 13h/AH=2中断列表,我们会看到输入看起来像这样:
磁盘 - 将扇区读入存储器
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,看起来像:
您的计算是正确的。 2 * 9 + 1 = 19.前19个逻辑块从LBA 0运行到LBA 18. LBA 19是根目录的起始位置。我们需要将其转换为Cylinders / Heads / Sectors(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.如果我们使用LBA为19,我们计算C = 0,H = 1,S = 2的CHS。如果我们查看您传入寄存器的值( 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转换例程。由于在导航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 ÷ SPT)
在商(LBA mod SPT)
S = (LBA mod SPT) + 1
我们将等式缩减为几个 DIV 指令和增量/加法。我们可以更简化一些事情。如果我们假设我们使用众所周知的IBM兼容磁盘格式,那么我们也可以说每个磁道扇区(SPT),磁头(HPC),圆柱,磁头和扇区将始终小于256.当任何磁场上的最大LBA时已知的软盘格式除以SPT,结果总是小于256.知道这一点,我们可以避免位于圆柱顶部的两位,并将它们放在 CL 的前两位。我们也可以使用16位×8位无符号除法的 DIV 指令。
如果我们采用上面的伪代码,我们可以创建一个相当小的lba_to_chs
函数,它接受LBA并将其转换为CHS,并且仅适用于众所周知的IBM兼容软盘格式。
; Function: lba_to_chs
; Description: Translate Logical block address to CHS (Cylinder, Head, Sector).
; Works ONLY for well known IBM PC compatible floppy disk formats.
;
; 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
; CL = Sector
;
; Notes: Output registers match expectation of Int 13h/AH=2 inputs
;
lba_to_chs:
push ax ; Preserve AX
mov ax, si ; Copy 16-bit LBA to AX
div byte [SectorsPerTrack] ; 16-bit by 8-bit DIV : LBA / SPT
mov cl, ah ; CL = S = LBA mod SPT
inc cl ; CL = S = (LBA mod SPT) + 1
xor ah, ah ; Upper 8-bit of 16-bit value set to 0 for DIV
div byte [NumberOfHeads] ; 16-bit by 8-bit DIV : (LBA / SPT) / HEADS
mov ch, al ; CH = C = (LBA / SPT) / HEADS
mov dh, ah ; DH = H = (LBA / SPT) mod HEADS
mov dl, [boot_device] ; boot device, not necessary to set but convenient
pop ax ; Restore scratch register
ret
如果您需要的版本可以处理FAT12支持的所有有效磁盘几何,那么代码必须使用32位/ 16位 DIV 指令,并且必须处理正在处理的柱面10位代替8.示例代码可能如下所示:
; 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
xor dx, dx ; blank dx for division
mov ax, 32
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 $
答案 1 :(得分:3)
我在加载FAT后最终报废了加载根目录。最后,我修改了我的.load_fat例程,同时加载FATs 和根目录(基本上在引导扇区后读取32个扇区,但仍然允许我轻松修改磁盘几何)。
以下代码如下:
.load_fat:
mov ax, 0x07c0 ; address from start of programs
mov es, ax
mov al, [SectorsPerFAT] ; how many sectors to load
mul byte [FATcount] ; load both FATs
mov dx, ax
push dx
xor dx, dx ; blank dx for division
mov ax, 32
mul word [MaxDirEntries]
div word [BytesPerSector] ; number of sectors for root directory
pop dx
add ax, dx ; add root directory length and FATs length -- load all three at once
xor dh,dh
mov dl, [boot_device]
xor ch, ch ; cylinder 0
mov cl, [ReservedSectors] ; Load from after boot sector
add cl, byte 1
xor dh, dh ; head 0
mov bx, 0x0200 ; read data to 512B after start of code
mov ah, 0x02 ; set to read
int 13h
cmp ah, 0
je .load_root
mov si, error_text
call print
hlt
虽然不是我打算解决问题的方式,但它可以完成这项工作,我可以从此继续开发。
修改强>
无论如何,我认为我找到了旧代码出错的地方。我在第18区之后增加了圆柱体,当时我应该增加头部。这是CHS,而不是HCS,原因是什么!