2

I know that you need the INT 13H extended functions to access drives over 8GB in size. This question refers to standard INT 13H, function 02H.

I also know the old 504MB hard drive limit was a result of: 1024 cylinders x 16 heads x 63 sectors x 512 bytes = 528,482,304 bytes.

But was this hard drive limitation caused by Int 13h itself? Is there any particular reason the head number was limited to 16 when there is an entire byte of space (dh) for the head number? Obviously later on the standard was changed to allow head numbers up to 255 (which caused the 8GB limit)

The reason i'm asking is because i'm having trouble reading a sector that lies quite a ways into a hard disk. It lies over 3 gigabytes into the disk.

Its exact C/H/S offset is: Cylinder 485, Head 147, sector 47

And the code i'm using to attempt to read it is as follows:

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

I know for a fact that the boot sector of the second partition lies at this C/H/S, i've quadruple checked using a disk editing program, but instead of loading the boot sector into secBuff, it just gets filled with zeros.

INT 13H returns an error code into AH after execution. The code it returns is 00h, which means that as far as it's concerned, the load was successful.

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?

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
bad
  • 939
  • 6
  • 18
  • The 16 head limitation came about because the ATA/IDE interface had only 4 bits for the head (although the int 13h interface allowed up to 255 with 8 bits). 4 bits can only hold 0x0 to 0xf or 16 heads. On a side note: While the int 13h interface was limited to 1024 cylinders the ATA interface internally was capable of using 65536 cylinders since ATA/IDE allowed for a 16 bit cylinder number. So limitations in the bits that the ATA interface had and a limitation in the number of bits supported through int 13h put restrictions on the maximums supported. – Michael Petch Nov 05 '17 at 07:18
  • The thing is I can access the boot sector of the first partition just fine, I think it lies somewhere in like the first head of cylinder 0. I can also access the first sector of the root directory, the first sector of the FAT, and the first sector of the disk data area, all automatically using the FAT16 access procedures that I wrote. The problem comes into play when I try to access the second partition, which as I mentioned above starts over 3 gigabytes into the disk. I just can't get there with INT 13H. The MBR gives my procedure the correct location, I just can't get there. – bad Nov 05 '17 at 07:26
  • Have you tried using [int 13h/ah=8h](http://www.ctyme.com/intr/rb-0621.htm) to get the drive geometry the BIOS is using? I'd be curious to know what it returns for the cylinders heads and sectors. I'm wondering if it is reporting something different than 1024/255/63 . If it were that would explain why you might be loading from the wrong place on the drive. – Michael Petch Nov 05 '17 at 07:30
  • One other observation. You claim Cylinder 485, Head 147, sector 47 is what you want to read. Cylinders are 0 based so if you encode 485 that is actually the 486 cylinder. Heads are also 0 based so Head 147 is actually 148. Sectors don't have that issue because because they are 1 based. I just don't know how you are expressing C/H/S. That may not be an issue. If I knew the LBA of the sector I'd be able to tell you. – Michael Petch Nov 05 '17 at 07:39
  • Result of INT 13H, Function 08H: AH=0, DL=2, DH=7FH, CX=DFFF. – bad Nov 05 '17 at 07:40
  • The cylinder, head, and sector numbers are gathered from the partition entry in the MBR. And they are very much correct. – bad Nov 05 '17 at 07:44
  • According to the BIOS Cylinders=992 (0 to 0x3df) ,Heads=128 (0 to 0x7f), Sectors=63(0x1 to 0x3f) for a total of 7991424 . Is this by some chance a USB device? – Michael Petch Nov 05 '17 at 07:49
  • Nope it's a 4 gigabyte hard drive. However, the partition was written using Gparted over a USB to IDE adapter attached to a seperate computer. I resized the FAT32 Windows 98 partition. Then, I wrote the FAT16 partition to the very end of the drive. And that is not the actual disk geometry, not even close. The actual number of cylinders is like 500 and some change. The number of heads is 255 and the number of sectors per head is 63. Gparted actually reports all of this info. – bad Nov 05 '17 at 07:52
  • Its getting late here. Cylinders are 992, not 991, Heads=128 and sectors = 63. That is 992*128*63*512=4,095,737,856 (I forgot to multiply by 512 earlier as well). That would be about a 4gb drive. – Michael Petch Nov 05 '17 at 07:57
  • Your BIOS is using those translations when accessing that drive via int 13h/ah=2. They do suggest it is a ~4gb drive but not in the geometry you think it is using. – Michael Petch Nov 05 '17 at 08:00
  • Well when does the translation break? I can access everything on partition 1 via INT 13H, some of these things lying hundreds of sectors into the disk. Why can't I access anything on Partition 2 correctly? Does INT 13H need a delay maybe? Maybe the head doesn't have enough time to reach those last few sectors of the disk. I had problems like that when reading floppy disks with INT 13. – bad Nov 05 '17 at 08:06
  • Are you sure you can access ALL of partition 1? (have you tried every sector)? Or placed something in the very last sector of the partition 1 and tried to read it? – Michael Petch Nov 05 '17 at 08:09
  • I tried accessing things a few sectors beyond the 504MB mark, I wrote a couple bytes that couldn't be mistaken for anything else. And sure enough those same bytes appeared in the buffer. Anyway, i'm sure I'll figure this problem out on my own eventually. If not, i'll just get rid of Windows 98 and go back to a single 500MB partition. I'll do some extensive testing tomorrow and figure out exactly when INT 13 breaks. – bad Nov 05 '17 at 08:13
  • You were right, I downloaded some obsure DOS disk editing software and it pointed me to the correct sector containing partition 2's boot sector. It was at 967/46/47. Now I just have to figure out some way of translating the CHS reported by the MBR into the actual disk geometry returned by INT 13/08h. That's sure to be a nightmare. – bad Nov 05 '17 at 08:43
  • 967/46/47 would be correct. I happened to compute it by hand and can confirm those values (they correspond to LBA 7800832). One way to do the conversion is to convert the CHS in the partition table to an LBA based on the geometry where H=255 and S=63 and then convert that LBA to a CHS based on H=128 and S = 63. (that happens to be how I manually got 967/46/47. The calculations are here: https://en.wikipedia.org/wiki/Logical_block_addressing#CHS_conversion – Michael Petch Nov 05 '17 at 08:51
  • Any particular reason why you don't use the extended int 13h functions? At least there you can just use the LBA (and that's the easy part to compute from a CHS). I assume for whatever reason the drives you are using don't support it, or you have code that already relies on CHS that you don't wish to change. – Michael Petch Nov 05 '17 at 08:54
  • The LBA should also be computed and placed in the partition table after the CHS in each partition table entry starting at offset 0x8 (the LBA is 4 bytes). I'd expect it to probably read (in your case) 0x00770800. If it is your job is to convert it to the CHS you need based on the drive geometry returned by int 13h/ah=8. – Michael Petch Nov 05 '17 at 09:09
  • I could easily make the conversion, it's just that as far as I know there is no way of telling what the max heads and sectors are of the C/H/S reported in the MBR. Therefore there's no actual way to convert 485/147/47 into a flat number without assuming the max heads and sectors. I want my procedures to work on a wide variety of drives. For instance making such an assumption would break compatiblity with my 286 machine which has a 40MB hard drive. – bad Nov 05 '17 at 09:10
  • Sorry I deleted the last comment. See my comment above about using the LBA stored in the partition table rather than CHS. That LBA is already computed based on whatever drive geometry the partitioning tool assumed. All you need to to is take that LBA and convert it to the CHS using max heads and max sectors reported by int 13h/ah=8. – Michael Petch Nov 05 '17 at 09:15
  • Exactly, guess where that information is. In the boot sector. Guess how you get to the boot sector. By jumping to the C/H/S reported in the MBR. It's like a chicken and the egg situation. I need to reach the boot sector, to figure out how to navigate to the boot sector. – bad Nov 05 '17 at 09:16
  • The LBA for the partition should be in the partition table in the MBR (CHS=0/0/1). The parittion table should have the LBA already pre computed and stuffed in the 4 bytes after the CHS values). The partition table entries are described here: https://en.wikipedia.org/wiki/Master_boot_record#Partition_table_entries . 4 bytes should contain the LBA, and in your case the LBA should be decimal 7800832 or 0x00770800 hex. – Michael Petch Nov 05 '17 at 09:18
  • Oh look at that. I guess I could use that. But it's 32 bit! I'm in real mode. I've only got 16 bit registers. Guess i'll have to figure that one out on my own. – bad Nov 05 '17 at 09:23
  • Real mode supports division of 32 bit numbers by 16bit numbers (using DX:AX as the 32 dividend). Mul supports multiplication of 2 16 bit numbers yielding a 32-bit result in DX:AX – Michael Petch Nov 05 '17 at 09:24
  • I think I have some assembly code on Stackoverflow and a discussion of the calculation to convert a given LBA to CHS (given a particular Heads per cylinder and Sectors per track). Let me see if I can find it. – Michael Petch Nov 05 '17 at 09:25
  • Found it. This answer: https://stackoverflow.com/a/45495410/3857942 . I provide a couple variants *you'll want the second one that is slightly longer). You just define memory locations as `SectorsPerTrack: dw 0` and `NumberOfHeads: dw 0` that you fill in with the values you retrieve from Int 13/ah=8h. Then the translation functions should pop out a usable CHS that you can use for Int 13h/ah=2h – Michael Petch Nov 05 '17 at 09:31
  • Sorry I just realized that the LBA used in that function is limited to 16 bits which was okay in that answer because it was limited to FAT12. That doesn't solve your problem here. – Michael Petch Nov 05 '17 at 09:37
  • If you know you are on a 386+ you can actually use 32-bit registers and 32-bit instructions in real mode. If I have a chance later on (its 3am) I could amend that code to support 32-bit LBAs using just 16-bit instructions. If you were to use the extended disk reads then you don't need such conversions. You can just copy the LBA (in two move instructions) into the disk packet structure. – Michael Petch Nov 05 '17 at 09:38

2 Answers2

3

(This post answers the question as asked. I haven't checked to see if absurdly long comment thread added any pertinent information that should have been incorporated into the question.)

Whether the INT 13h CHS BIOS functions are capable of addressing sectors located on heads numbered 16 or higher depends on the BIOS. The oldest BIOS implementations don't provide any translation of CHS values, passing the cylinder, head and sector numbers directly through the drive interface unmodified. These BIOS implementations don't support drives with more than 16 heads because the standard IBM PC AT controller, the WD-1003, only supported 16 heads. Since the IDE CHS interface is backwards compatible with the WD-1003, this limit also applies to any IDE drive that (only) supports CHS addressing.

Newer BIOSes will do some sort of translation, but what translation used hasn't been consistent. Modern BIOSes will convert the CHS values passed through the INT 13h into an LBA address using the emulated geometry reported by the BIOS and (if the drive doesn't support LBA addressing) back into CHS values using the geometry reported by the drive. However other translation schemes have been used (eg. using the upper two bits of the head value to extend the cylinder value.) Also even when the now standard CHS/LBA/CHS translation used, different BIOS implementations can use different emulated geometries for the same drive.

If your BIOS doesn't use the modern CHS/LBA/CHS translation then you'll need to figure out what translation it does use. If your BIOS does use it and you've moved the drive between computers (or even potentially between controllers on the same PC) that use different emulated geometries then any CHS values stored in on the drive (eg. in the partition table, or FAT BPB) are no longer valid and you'll have to either ignore them or figure out how translate them. LBA values stored on the disk won't normally cause a problem as these remain the same regardless of emulated geometry.

There's a comprehensive document titled How It Works -- CHS Translation by Hale Landis that describes how older BIOSes perform CHS translation in more detail than I've given above. In particular it describes 10 different BIOS types that may help identify what translation scheme your computer might be using. Note that this document is pretty old, so much of what it talks about actual operating systems doing is out of date.

Ross Ridge
  • 38,414
  • 7
  • 81
  • 112
  • To sum up the comments. He's using the CHS values in the partition table to read the first sector of a partition near the end of the disk. He discovered when he sues those CHS value with int 13h/ah=2 that he doesn't read the data he expects. He had assumed drive geometry would be using 255 heads and 63 sectors. I expected as you did that the drive geometry the BIOS was using wasn't the same. At my behest he did a int 13h/ah=8 and found the drive geometry was CHS 992/128/63 . – Michael Petch Nov 05 '17 at 21:09
  • 1
    I pointed out to him (link to wikipedia) the partition table has the LBA which is heads and sector per track agnostic and can be converted into a CHS by a formula (I provided a link to the formulas on Wiki) using the heads and sectors per track reported by Int 13h/ah=8h . His issue is that the calculation is a bit more involve (not too much) if using 16-bit real mode code only (I pointed out he could use 32-bit instructions &registers). The calculation is only slightly more involved because you need a 32 bit by 16 divide that allows for the quotient to be larger than 16 bits to avoid overflow – Michael Petch Nov 05 '17 at 21:13
  • Also pointed out to him he could potentially avoid all this if he used the extended disk reads and use LBA, although he never has said why he needs to use basic int 13/ah=2 (and non extended functions). – Michael Petch Nov 05 '17 at 21:16
  • I'm working on translating the starting sector in the partition entry to C/H/S now. Making pretty good progress, I just had no idea that the boot sector C/H/S listed there in the partition entry could actually be based on a different disk geometry that is then translated by the BIOS into the true disk geometry. That was the whole problem. Also I noticed in my BIOS that i'm actually given the option to disable C/H/S translation. I left it enabled, though. It might cause problems to disable it now. – bad Nov 05 '17 at 21:36
  • 1
    So, in conclusion to all of this. My solution to the problem is to, from now on, use the disk geometry returned by Int 13H/08H to convert the first boot sector in the desired partition entry to that C/H/S scheme. – bad Nov 05 '17 at 21:48
3

When the ATA/IDE interface was developed, internally they supported a maximum of 16 heads. Although the BIOS routines support up to 2552 heads the underlying interface had a maximum of 16. Until different types of translation schemes were adopted (See Ross Ridge's answer and the link he provided) ATA/IDE drives (via the BIOS) were limited to 1024 Cylinders1, 16 heads, 63 sectors with older BIOSes.

In response to this question:

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?

The answer is yes, the BIOS can but whether the underlying hardware like ATA/IDE etc supports it is another story. Different types of mechanisms have developed to allow a greater number of heads with ATA/IDE drives via CHS translations done in the BIOSes basic Int 13h disk operations. One mechanism is to lie to the user and tell them an ATA/IDE disk has more heads than it supports and then translation is done behind the scenes by the BIOS. BIOS might report a drive with 16 heads actually has a maximum of 128. The BIOS knows this and divides the heads by 8 (128/16=8) before sending it to the drive controller but at the same time multiplies the number of cylinders by 8. Although the BIOS supports a maximum of 1024 cylinders it doesn't mean the BIOS can't talk to the physical medium using larger values (up to 65536 cylinders supported by ATA/IDE).


However the partition table was generated in the Original Poster's question, the drive geometry used to compute the Cylinder/Head/Sector (CHS) values in the partition table likely are not what the BIOS is using to do translations. You end up reading the wrong sector from the disk with Int 13h/ah=2

In this case the likely solution is to make a call to Int 13/ah=8 to get the drive geometry used for the CHS translation.

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)

You'll need to set DL to the drive number and zero out ES and DI. The 2 components we care about are the lower 6 bits of CL which hold the maximum sector number (1 based) and DH which is the maximum head number. Since the head number is 0 based you'll need to add 1 to it.

As an example you could store them to memory locations that are word size with the bytes returned for both values.

The trickier part is to use this information to compute a 32-bit LBA number and convert it to a CHS tuple. I previously wrote a Stackoverflow Answer regarding the calculation that needs to be performed:

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

Unfortunately the code in the previous Stackoverflow Answer was designed with floppy disk geometries in mind where the division is simpler. Part of the equation needs to be reworked so that we can divide a 32-bit number by a 16-bit number and get a 32-bit quotient and 16-bit remainder. If you want this to run on an 8086 we can't use the 32-bit instructions and registers. I used a variation of the Extended Precision Division in the book The Art of Assembly and amended the lba_to_chs translation code to be:

; 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
;              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

The Original Poster mentioned using 8086. To be strictly compliant with the 8086 instruction set I used:

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

On an 80186 or later this code can be written as this to get the same desired effect (with the exception of the flags being set which I don't care about):

shl ah, 6

The immediate form of shifting was not available for any value above 1 on the Intel 8086. Alternatively one could have used 6 separate shl ah, 1 instructions.

This is a simple example that could be modified to be used inside a real bootloader or converted to be used as an MS-DOS program:

; 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

This is just sample code that demonstrates lba_to_chs. Rather than use the variable LBAtoRead for the 32-bit LBA Number, the original poster should retrieve it from the partition table in the MBR. That is left as a simple exercise.


Footnotes

  • 1Internally the original ATA/IDE interface supported 16-bit cylinders (65536), but the BIOS interface had support for a maximum of 10 bits for the cylinder number. The BIOS interface prevented any more than 1024 cylinders. Similarly ATA/IDE supported 256 sectors but the BIOS interface limited it to 63. Internally ATA/IDE could address 65536x16x256 sectors (137438953472 bytes or ~137 GB). The lowest common denominator for BIOSes that don't perform any special translation and the ATA/IDE interface combined for the original CHS limits of 1024/16/63 for ATA drives.
  • 28 bits would normally allow 256 heads (0-255) however the 255 head limit became one necessitated by compatibility because of a bug in IBM-DOS and PC-DOS through version 7.10.
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • All right thanks man, I think i'm gonna use this method to actually divide the 32 bit sector number into C/H/S. It chains div instructions together. [32 bit division on 8086](http://tbruylan.blogspot.com/2013/01/32-bit-division-on-old-school-8086.html) – bad Nov 06 '17 at 00:41
  • 1
    @Proughgrammor That code takes a similar approach to my division (32 by 16 with 32 bit quotient and 16-bit remainder) by chaining them together, although I have tailored mine specifically for the `lba_to_chs` routine (I have a link to a tutorial regarding the math involved in my answer for anyone who doesn't know how the chaining works) . The fellow who wrote the one you link to is more generic, but does the same sort of thing. Good luck! – Michael Petch Nov 06 '17 at 00:45