为什么我的根目录没有被加载? (FAT12)
Why isn't my root directory being loaded? (FAT12)
我正在用程序集编写第 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 table 加载到内存地址 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
加载 FAT 后,我最终放弃了加载根目录。最后,我修改了 .load_fat 例程以同时加载 FAT 和 根目录(本质上是在引导扇区之后读取 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 之后递增 cylinder,而我本应递增磁头。这是 CHS,而不是 HCS,这是有原因的!
问题分析
您的代码存在的问题是您没有按照预期从磁盘上的位置读取数据。尽管您的磁盘读取成功,但它已将错误的扇区加载到内存中。
如果我们查看 Int 13h/AH=2 的 Ralph Brown 中断列表,我们会看到输入如下所示:
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
如果我们在 .load_root
中执行 int 13h
之前检查您的寄存器,我们会看到这些寄存器包含以下内容:
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。从 LBA 0 到 LBA 18 的前 19 个逻辑块 运行。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。如果我们使用 19 的 LBA,我们计算的 CHS 为 C=0,H=1,S=2。如果我们查看您传递到寄存器 (CL、CH、DH) 的值,我们会发现您使用了 C=1、H=0、S=1 的 CHS。这恰好是 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 ÷ SPT)
时也最终免费计算 LBA mod SPT
。代码将遵循以下结构:
- 计算 LBA DIV SPT。这产生:
(LBA ÷ SPT)
中商
(LBA mod SPT)
余数
- 取步骤(1)的余数存入暂存器
- 将步骤(2)中的临时值加1。该寄存器现在包含由
S = (LBA mod SPT) + 1
计算的扇区
- 从步骤 (1) 中取商并除以 HPC。
- 柱数取商
- 头将是余数。
我们已将等式简化为一对 DIV 指令和 increment/add。我们可以进一步简化事情。如果我们假设我们使用的是众所周知的 IBM 兼容磁盘格式,那么我们也可以说每个磁道的扇区数 (SPT)、磁头数 (HPC)、柱面数、磁头数和扇区数将始终小于 256。当任何孔上的最大 LBA已知的软盘格式除以 SPT 结果总是小于 256。知道这一点可以让我们避免位旋转柱面的前两位并将它们放在 CL[=113 的前两位=].我们还可以使用 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
;
; 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 $
我正在用程序集编写第 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 table 加载到内存地址 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
加载 FAT 后,我最终放弃了加载根目录。最后,我修改了 .load_fat 例程以同时加载 FAT 和 根目录(本质上是在引导扇区之后读取 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 之后递增 cylinder,而我本应递增磁头。这是 CHS,而不是 HCS,这是有原因的!
问题分析
您的代码存在的问题是您没有按照预期从磁盘上的位置读取数据。尽管您的磁盘读取成功,但它已将错误的扇区加载到内存中。
如果我们查看 Int 13h/AH=2 的 Ralph Brown 中断列表,我们会看到输入如下所示:
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
如果我们在 .load_root
中执行 int 13h
之前检查您的寄存器,我们会看到这些寄存器包含以下内容:
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。从 LBA 0 到 LBA 18 的前 19 个逻辑块 运行。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。如果我们使用 19 的 LBA,我们计算的 CHS 为 C=0,H=1,S=2。如果我们查看您传递到寄存器 (CL、CH、DH) 的值,我们会发现您使用了 C=1、H=0、S=1 的 CHS。这恰好是 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 ÷ SPT)
时也最终免费计算 LBA mod SPT
。代码将遵循以下结构:
- 计算 LBA DIV SPT。这产生:
(LBA ÷ SPT)
中商(LBA mod SPT)
余数
- 取步骤(1)的余数存入暂存器
- 将步骤(2)中的临时值加1。该寄存器现在包含由
S = (LBA mod SPT) + 1
计算的扇区
- 从步骤 (1) 中取商并除以 HPC。
- 柱数取商
- 头将是余数。
我们已将等式简化为一对 DIV 指令和 increment/add。我们可以进一步简化事情。如果我们假设我们使用的是众所周知的 IBM 兼容磁盘格式,那么我们也可以说每个磁道的扇区数 (SPT)、磁头数 (HPC)、柱面数、磁头数和扇区数将始终小于 256。当任何孔上的最大 LBA已知的软盘格式除以 SPT 结果总是小于 256。知道这一点可以让我们避免位旋转柱面的前两位并将它们放在 CL[=113 的前两位=].我们还可以使用 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
;
; 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 $