INT13H(非扩展)是否能够访问每个柱面超过 16 个磁头的驱动器?

Is INT13H (non extended) capable of accessing drives with more than 16 heads per cylinder?

我知道您需要 INT 13H 扩展函数来访问大小超过 8GB 的​​驱动器。本题参考标准INT 13H,函数02H。

我也知道旧的 504MB 硬盘限制是由于: 1024 个柱面 x 16 个磁头 x 63 个扇区 x 512 字节 = 528,482,304 字节。

但是这个硬盘限制是Int 13h本身造成的吗? 当头部编号为 space (dh) 的整个字节时,是否有任何特殊原因将头部编号限制为 16?显然后来标准被更改为允许头数最多为 255(这导致了 8GB 的​​限制)

我问的原因是因为我无法读取一个扇区 很多种方式进入硬盘。它位于磁盘中超过 3 GB。

它的确切C/H/S偏移量是:柱面 485,磁头 147,扇区 47

我用来尝试阅读它的代码如下:

mov bx, ds
mov es, bx        ;es takes ds
lea bx, secBuff   ;bx takes the offset of secBuff, a 512 byte buffer
mov ah, 2         ;function 2, read sectors
mov dl, 80h       ;source drive set to master drive
mov ch, 0e5h      ;lower 8 bits of cylinder number
mov dh, 93h       ;head number
mov cl, 6fh       ;upper 2 bits of cylinder number, 6 bit sector number
int 13h           ;read the sector into ds:secBuff

我知道第二个分区的引导扇区在这个C/H/S,我用磁盘编辑程序四次检查过,但不是将引导扇区加载到secBuff,它只是被零填满。

INT 13H returns一个错误代码执行后转入AH。 itreturns的代码是00h,表示就其而言,加载成功

INT 13H 能否处理大于 16 的磁头数,还是它无法访问驱动器前 504MB 或什至 2GB 以外的扇区?

(这个 post 回答了所问的问题。我还没有检查过长得离谱的评论线程是否添加了任何应该纳入问题的相关信息。)

INT 13h CHS BIOS 函数是否能够寻址位于编号为 16 或更高的磁头上的扇区取决于 BIOS。最古老的 BIOS 实现不提供任何 CHS 值的转换,直接通过驱动器接口传递柱面、磁头和扇区号而不加修改。这些 BIOS 实现不支持超过 16 个磁头的驱动器,因为标准的 IBM PC AT 控制器 WD-1003 只支持 16 个磁头。由于 IDE CHS 接口向后兼容 WD-1003,此限制也适用于任何(仅)支持 CHS 寻址的 IDE 驱动器。

较新的 BIOS 会进行某种翻译,但使用的翻译并不一致。现代 BIOS 将使用 BIOS 报告的模拟几何将通过 INT 13h 传递的 CHS 值转换为 LBA 地址,并且(如果驱动器不支持 LBA 寻址)使用驱动器报告的几何返回 CHS 值。然而,已经使用了其他转换方案(例如,使用 head 值的高两位来扩展柱面值。)即使现在使用标准 CHS/LBA/CHS 转换,不同的 BIOS 实现也可以使用不同的模拟几何结构相同的驱动器。

如果您的 BIOS 不使用现代 CHS/LBA/CHS 转换,那么您需要弄清楚它使用的是什么转换。如果您的 BIOS 确实使用它并且您已经在使用不同模拟几何的计算机之间(甚至可能在同一台 PC 上的控制器之间)移动了驱动器,那么存储在驱动器上的任何 CHS 值(例如在分区 table, 或 FAT BPB) 不再有效,您将不得不忽略它们或弄清楚如何翻译它们。存储在磁盘上的 LBA 值通常不会引起问题,因为无论模拟几何形状如何,这些值都保持不变。

Hale Landis 有一篇名为 How It Works -- CHS Translation 的综合文档,比我上面给出的更详细地描述了旧 BIOS 如何执行 CHS 转换。特别是它描述了 10 种不同的 BIOS 类型,可以帮助识别您的计算机可能使用的转换方案。请注意,这份文件已经很老了,它谈论的实际操作系统所做的很多事情都已经过时了。

开发ATA/IDE接口时,内部最多支持16个头。尽管 BIOS 例程最多支持 2552 个磁头,但底层接口最多有 16 个。直到采用不同类型的转换方案(参见 Ross Ridge 的回答和 link 他提供)ATA/IDE 驱动器(通过 BIOS)被限制为 1024 个柱面 1,16 个磁头,63 个扇区与旧的 BIOS。

回答这个问题:

Can INT 13H handle head numbers greater than 16, or is it simply unable to access sectors that lie beyond the first 504MB, or maybe even 2GB of the drive?

答案是肯定的,BIOS可以,但ATA/IDE等底层硬件是否支持又是另外一回事了。已经开发出不同类型的机制,通过在 BIOS 基本 Int 13h 磁盘操作中完成的 CHS 转换,允许更多的磁头具有 ATA/IDE 驱动器。一种机制是欺骗用户并告诉他们 ATA/IDE 磁盘的磁头数量超过了它支持的数量,然后由 BIOS 在幕后完成转换。 BIOS 可能会报告具有 16 个磁头的驱动器实际上最多有 128 个磁头。BIOS 知道这一点并在将其发送到驱动器控制器之前将磁头除以 8 (128/16=8),但同时将柱面数乘以8. 尽管 BIOS 最多支持 1024 个柱面,但这并不意味着 BIOS 不能使用更大的值与物理介质通信(ATA/IDE 最多支持 65536 个柱面)。


然而分区 table 是在原始发帖者的问题中生成的,用于计算分区 table 中的 Cylinder/Head/Sector (CHS) 值的驱动器几何形状可能不是BIOS 用于进行翻译。您最终使用 Int 13h/ah=2

从磁盘读取了错误的扇区

在这种情况下,可能的解决方案是调用 Int 13/ah=8 以获取用于 CHS 转换的驱动器几何结构。

DISK - GET DRIVE PARAMETERS (PC,XT286,CONV,PS,ESDI,SCSI)

AH = 08h
DL = drive (bit 7 set for hard disk)
ES:DI = 0000h:0000h to guard against BIOS bugs

Return:
CF set on error
AH = status (07h) (see #00234)
CF clear if successful
AH = 00h
AL = 00h on at least some BIOSes
BL = drive type (AT/PS2 floppies only) (see #00242)
CH = low eight bits of maximum cylinder number
CL = maximum sector number (bits 5-0)
high two bits of maximum cylinder number (bits 7-6)
DH = maximum head number
DL = number of drives
ES:DI -> drive parameter table (floppies only)

您需要将 DL 设置为驱动器编号并清零 ESDI.我们关心的 2 个组件是 CL 的低 6 位,它保存最大扇区号(基于 1)和 DH 这是最大磁头数字。由于头部编号是基于 0 的,因此您需要为其加 1。

例如,您可以将它们存储到字大小的内存位置,并为两个值返回字节。

更棘手的部分是使用此信息计算 32 位 LBA 数并将其转换为 CHS 元组。关于需要进行的计算,我之前写过一篇

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

不幸的是,之前的 Whosebug Answer 中的代码在设计时考虑了软盘几何形状,其中除法更简单。方程的一部分需要修改,以便我们可以将 32 位数字除以 16 位数字并得到 32 位商和 16 位余数。如果你想在 8086 上 运行 我们不能使用 32 位指令和寄存器。我在 The Art of Assembly 书中使用了 Extended Precision Division 的变体,并将 lba_to_chs 翻译代码修改为:

; Global variables that need to be set before lba_to_chs can be called
; One can use int 13h/ah=8 to retrieve this data

SectorsPerTrack: dw 0           ; Drive geometry and drive info used by lba_to_chs
NumberOfHeads:   dw 0
boot_device:     db 0x80        ; Should be filled in with real drive #

;    Function: lba_to_chs
; Description: Translate a 32-bit Logical block address (LBA) 
;              to CHS (Cylinder, Head, Sector).
;
;   Resources: http://www.ctyme.com/intr/rb-0607.htm
;              https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion
;              
;              
;   http://www.oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_9/CH09-4.html
;
;              Sector    = (LBA mod SPT) + 1
;              Head      = (LBA / SPT) mod HEADS
;              Cylinder  = (LBA / SPT) / HEADS
;
;      Inputs: SI = Lower 16-bits of LBA
;              DI = Upper 16-bits of LBA
;              DI:SI = 32 bit LBA number
;
;     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
;              This routine should work on an 8086 processor.

lba_to_chs:
    push bx                    ; Save temporary registers
    push ax
    xor dx, dx                 ; Set up 32-bit by 16-bit DIV to determine high order
    mov ax, di                 ;     of Quotient (HOQ), DX:AX = (0x0000:DI)
    div word [SectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT Part 1 (HOQ)
    mov bx, ax                 ; Save high order of Quotient (HOQ)
    mov ax, si                 ; Do division to compute low order of Quotient (LOQ)
    div word [SectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT Part 2 (LOQ)
    mov cl, dl                 ; CL = S = LBA mod SPT
    inc cl                     ; CL = S = (LBA mod SPT) + 1
    mov dx, bx                 ; Move saved HOQ to DX (Upper 16-bits of DX:AX)
    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
    ror ah, 1                  ; Rotate upper 2 bits of 10-bit Cylinder
    ror ah, 1                  ;     into top of AH
    and ah, 0xC0               ;     set lower 6 bits to 0
    or  cl, ah                 ; Place upper 2 bits of 10-bit Cylinder
                               ;    Into the upper 2 bits of the sector number
    pop ax                     ; Restore temporary registers
    pop bx
    ret

发帖者提到使用 8086。严格 符合我使用的 8086 指令集:

ror ah, 1                  ; Rotate upper 2 bits of 10-bit Cylinder
ror ah, 1                  ;     into top of AH
and ah, 0xC0               ;     set lower 6 bits to 0

在 80186 或更高版本上,此代码可以这样编写以获得相同的预期效果(除了我不关心的设置标志外):

shl ah, 6

在 Intel 8086 上,任何大于 1 的值都不能使用直接移位形式。或者,可以使用 6 条单独的 shl ah, 1 指令。

这是一个简单的示例,可以修改为在真正的引导加载程序中使用或转换为用作 MS-DOS 程序:

; Sample bootloader that demonstrates lba_to_chs
; Assemble with: nasm -f bin boot.asm -o boot.bin

org 0x7c00
start:
    xor ax, ax
    mov ds, ax
    mov es, ax                  ; ES zeroed for int 13h/ah=8 to avoid bug
    mov ss, ax
    mov sp, 0x7c00
    cld

    mov [boot_device], dl       ; Save the boot drive needed by lba_to_chs

    mov ah, 0x08
    xor di, di                  ; DI (and ES) Zeroed to avoid bug

    int 0x13                    ; Get drive Paramaters
    inc dh                      ; Number of head is 0 based, add 1
    mov [NumberOfHeads], dh     ; Save the Number of Heads for use by lba_to_chs
    and cl, 0x3f                ; Lower 6 bits of CL are the maximum sector number
    mov [SectorsPerTrack], cl   ; Save the sectors per track for use by lba_to_chs

    ; This is sample code. For the original posters question this wcould be read
    ; from the 32-bit LBA field from the Partition table
    mov si, [LBAtoRead]         ; Read lower 16-bits of 32-bit LBA
    mov di, [LBAtoRead+2]       ; Read higher 16-bits of 32-bit LBA
    call lba_to_chs             ; Convert 32 bit LBA in SI:DI to CHS values
    ; DX and CX now set with CHS parameters and boot drive number.
    ; Can be used by int 13h/ah=2 to do read etc

    ; Insert other useful code here that sues the CHS values.

    ; We are finished, so halt
    cli
    hlt

LBAtoRead: dd 0x00770800        ; 32 Bit LBA in memory as test. This should be the LBA
                                ; Number of Original Posters partition.

; Global variables that need to be set before lba_to_chs can be called
; One can use int 13h/ah=8 to retrieve this data    
SectorsPerTrack: dw 0           ; Drive geometry and drive info used by lba_to_chs
NumberOfHeads:   dw 0
boot_device:     db 0x00

lba_to_chs:
    push bx                    ; Save temporary registers
    push ax
    xor dx, dx                 ; Set up 32-bit by 16-bit DIV to determine high order
    mov ax, di                 ;     of Quotient (HOQ), DX:AX = (0x0000:DI)
    div word [SectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT Part 1 (HOQ)
    mov bx, ax                 ; Save high order of Quotient (HOQ)
    mov ax, si                 ; Do division to compute low order of Quotient (LOQ)
    div word [SectorsPerTrack] ; 32-bit by 16-bit DIV : LBA / SPT Part 2 (LOQ)
    mov cl, dl                 ; CL = S = LBA mod SPT
    inc cl                     ; CL = S = (LBA mod SPT) + 1
    mov dx, bx                 ; Move saved HOQ to DX (Upper 16-bits of DX:AX)
    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
    ror ah, 1                  ; Rotate upper 2 bits of 10-bit Cylinder
    ror ah, 1                  ;     into top of AH
    and ah, 0xC0               ;     set lower 6 bits to 0
    or  cl, ah                 ; Place upper 2 bits of 10-bit Cylinder
                               ;    Into the upper 2 bits of the sector number
    pop ax                     ; Restore temporary registers
    pop bx
    ret

TIMES 510-($-$$) db 0x00
dw 0xaa55

这只是演示 lba_to_chs 的示例代码。而不是使用变量 LBAtoRead 作为 32 位 LBA 编号,原始发布者应该从 partition table in the MBR 中检索它。这是一个简单的练习。


脚注

  • 1内部原始 ATA/IDE interface supported 16-bit cylinders (65536),但 BIOS 接口支持最多 10 位的柱面号。 BIOS 接口阻止了超过 1024 个柱面。同样,ATA/IDE 支持 256 个扇区,但 BIOS 接口将其限制为 63 个。内部 ATA/IDE 可以寻址 65536x16x256 个扇区(137438953472 字节或 ~137 GB)。不执行任何特殊转换的 BIOS 的最低公分母和 ATA/IDE 接口结合用于 ATA 驱动器的原始 CHS 限制 1024/16/63。
  • 28 位通常允许 256 个磁头 (0-255) 但是 255 head limit 由于 IBM-DOS 和 PC 中的错误而成为兼容性所必需的-DOS 到版本 7.10.