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

时间:2017-11-05 06:26:22

标签: assembly dos x86-16

我知道你需要INT 13H扩展功能才能访问超过8GB的驱动器。这个问题涉及标准INT 13H,函数02H。

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

但这是由Int 13h本身引起的硬盘限制吗? 当头号的整个空格(dh)存在时,是否有任何特殊原因将头数限制为16?显然后来标准被改为允许头数最多255(导致8GB限制)

我问的原因是因为我在阅读某个部门时遇到了麻烦 是一个很难进入硬盘的方法。它占据了超过3千兆字节的磁盘。

其精确的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在执行后向AH返回错误代码。它返回的代码是00h,这意味着就其而言,负载是成功的。

INT 13H可以处理大于16的头数,还是根本无法访问超过第一个504MB或甚至2GB驱动器的扇区?

2 个答案:

答案 0 :(得分:2)

(这篇文章回答了问题的问题。我还没有检查过是否荒谬的长评论帖添加了任何应该包含在问题中的相关信息。)

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报告的模拟几何结构(并且如果驱动器不支持LBA寻址)使用驱动器报告的几何结构将通过INT 13h传递的CHS值转换为LBA地址。然而,已经使用了其他转换方案(例如,使用头部值的高两位来扩展柱面值。)即使使用现在标准的CHS / LBA / CHS转换,不同的BIOS实现也可以使用不同的模拟几何形状。同样的驱动力。

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

Hale Landis提供了一份题为How It Works -- CHS Translation的综合性文件,其中描述了较旧的BIOS如何更详细地执行CHS翻译,而不是上面给出的。特别是它描述了10种不同的BIOS类型,可以帮助识别您的计算机可能正在使用的转换方案。请注意,这个文档已经很老了,它所涉及的实际操作系统的大部分内容已经过时了。

答案 1 :(得分:2)

开发ATA / IDE接口时,内部最多支持16个磁头。虽然BIOS例程支持多达255个头,但底层接口最多只有16个。直到采用不同类型的转换方案(参见Ross Ridge的答案和他提供的链接)ATA / IDE驱动器(通过BIOS)限制为1024气缸 1 ,16个磁头,63个具有较旧BIOS的扇区。

回答这个问题:

  

INT 13H可以处理大于16的头数,还是根本无法访问超过第一个504MB或甚至2GB驱动器的扇区?

答案是肯定的,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个柱面)与物理介质通信。

1 原始ATA/IDE interface supported 16-bit cylinders(65536)内部,但BIOS接口支持最大10位的柱面编号。 BIOS界面阻止了超过1024个柱面。类似地,ATA / IDE支持256个扇区,但BIOS接口将其限制为63.内部ATA / IDE可以处理65536 * 16 * 256个扇区(137438953472字节或~137 GB)。不执行任何特殊转换的BIOS的最低公分母和ATA / IDE接口的组合,原始CHS限制为1024/16/63,用于ATA驱动器。

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

从磁盘读取错误的扇区

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

  

DISK - 获取驱动器参数(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 设置为驱动器号,并将 ES DI 清零。我们关心的2个组件是 CL 的低6位,它包含最大扇区号(基于1)和 DH ,它是最大头数。由于头数为0,你需要加1。

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

更棘手的部分是使用此信息计算32位LBA编号并将其转换为CHS元组。我之前写过关于需要执行的计算的Stackoverflow Answer

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

不幸的是,之前Stackoverflow Answer中的代码设计时考虑了软盘几何形状,其中划分更简单。部分等式需要重新设计,以便我们可以将32位数除以16位数,得到32位商和16位余数。如果您希望在8086上运行它,我们就不能使用32位指令和寄存器。我在Randall Hyde's tutorial中使用了扩展精度分部的变体,并将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 geomtry 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
;              https://stackoverflow.com/q/45434899/3857942
;              https://stackoverflow.com/q/47118827/3857942
;   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

英特尔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 geomtry 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检索它。这只是一个简单的练习。