Skip to content
FreeDOSDeep Dive Published Updated 9 min readViews unavailable

BIOS INT 13h AH=48h: Read EDD Drive Parameters Without Guessing Geometry

Read BIOS EDD function 48h safely: validate its result buffer, distinguish physical from translated geometry, and calculate reported capacity carefully.

BIOS INT 13h, function AH=48h, returns an extended parameter record for a BIOS disk. It is a query, not a sector transfer: the caller supplies a result buffer and the firmware reports flags, geometry fields, a sector count, and a sector size. That makes it useful to a real-mode FreeDOS utility that needs to inventory the BIOS-visible device, but the result is not DOS free space, a partition length, or proof that a particular disk operation will work.

This article follows the 1995 Phoenix Enhanced Disk Drive (EDD) Specification 1.1 for the AH=48h buffer. It is a firmware interface available to DOS software, not a FreeDOS kernel API or a promise that every BIOS, emulator, or virtual disk implements the same extensions. Preserve the BIOS drive number provided by the boot path; a drive letter such as C: is not the value to put in DL.

Probe the BIOS interface before interpreting a response

The EDD 1.1 extension check calls INT 13h with function AH=41h, BX=55AAh, and the BIOS drive number in DL. A successful check clears carry and returns BX=AA55h; the returned value in AH is 21h, identifying compliance with the Phoenix EDD 1.1 specification. That returned version value is not the function number: AH=41h is still the service being called. CX reports a bitmask of supported extension subsets: bit 0 advertises fixed-disk access using the packet structure, bit 1 drive locking/ejecting, and bit 2 the EDD configuration-parameter subset. These are separate capabilities, not interchangeable spellings for “LBA works.” AH=48h is the parameter-query service; do not require the optional configuration-parameter bit merely to query it. Check that bit before relying on the optional pointer at the end of a 30-byte result.

The exact AH=21h comparison below intentionally limits this walkthrough to the EDD 1.1 contract; it is not a forward-compatible “at least this version” test and will reject BIOSes that report a later extension level. A general-purpose utility should implement the structure and capability rules it actually supports rather than replacing this comparison with an unverified numeric threshold. Function AH=48h belongs to multiple EDD 1.1 interface subsets, so the example requires at least one defined subset bit, but it does not require bit 2 unless it will use the optional configuration pointer.

Record the returned mask, then call only the service required and check that service’s own result. A good signature is not a blanket guarantee that every optional service, device, media type, or later EDD extension is supported. AH=48h returns carry set and an error in AH on failure; do not parse a partially initialized result buffer after an error.

An abbreviated assembly sequence is:

; DS addresses this data block. bios_drive came from the boot path.
edd_params:
        dw      30              ; capacity requested: 30 bytes
        times   28 db 0

        mov     ah, 41h
        mov     bx, 55AAh
        mov     dl, [bios_drive]
        int     13h
        jc      .edd_unavailable
        cmp     bx, 0AA55h
        jne     .edd_unavailable
        cmp     ah, 21h         ; Phoenix EDD 1.1 compatibility value
        jne     .unsupported_version
        test    cx, 0007h       ; At least one defined EDD 1.1 interface subset
        jz      .edd_unavailable
        ; Save CX if the program needs to report the advertised subsets.

        mov     word [edd_params], 30
        mov     ax, 4800h
        mov     dl, [bios_drive]
        mov     si, edd_params
        int     13h
        jc      .parameter_error
        cmp     word [edd_params], 26
        jb      .malformed_result
        cmp     word [edd_params], 30
        ja      .malformed_result
        cmp     word [edd_params], 26
        je      .record_26_bytes
        cmp     word [edd_params], 30
        jne     .malformed_result
        ; A 30-byte result may include the optional pointer at offset 1Ah.
        jmp     .record_ready
.record_26_bytes:
        ; Consume only the common fields through offset 19h.
        ; Never inspect the optional pointer at offset 1Ah on this path.
        jmp     .record_ready
.record_ready:
        ; Parse only fields covered by the validated returned length.

This is a 16-bit NASM-style outline, not a complete boot program. It assumes the buffer is addressable through DS:SI, remains within the segment’s valid range, and does not cross a real-mode address boundary the target BIOS cannot handle. Production code should preserve registers required by its caller, log the BIOS error immediately, and check the returned length before reading any field beyond it.

The result is length-prefixed

EDD 1.1 begins with a word containing the caller’s buffer length. The caller sets it to its maximum available size. The documented minimum is 26 bytes. If the caller offers 26 through 29 bytes, the function returns a 26-byte record. With 30 bytes or more, the version-1.1 function returns 30 bytes. A buffer shorter than 26 bytes is an error.

The first 26 bytes contain the common record:

Offset Size Field Interpretation
00h 2 Buffer size Set to capacity on entry; inspect the returned size.
02h 2 Information flags Feature and media-state bits; reserved bits must not be treated as private flags.
04h 4 Cylinders BIOS-reported physical cylinder count, not the maximum zero-based cylinder index.
08h 4 Heads BIOS-reported physical head count, not the maximum zero-based head index.
0Ch 4 Sectors per track The maximum sector number; sector numbering is one-based, so this value is also the count.
10h 8 Number of sectors A count, not the final sector’s LBA.
18h 2 Bytes per sector Use the reported unit rather than assuming 512.

When the returned size is 30, bytes 1Ah through 1Dh hold a pointer to EDD configuration parameters if the AH=41h result advertises the EDD subset. Treat that pointer as optional and short-lived: the Phoenix specification warns that it may refer to temporary BIOS storage that later INT 13h calls can reuse. The specification also defines FFFFh:FFFFh as an invalid-pointer sentinel; never dereference that value. A program that does not need the configuration record should not dereference it. If it does, it must validate the capability, returned length, pointer, and the separate record’s own contract before access.

Keep physical geometry separate from translated geometry

The word “physical” in the EDD parameter table can mislead software that expects a partitioning geometry. EDD 1.1 says to use INT 13h/AH=08h for the logical cylinder, head, and sectors-per-track values used by the conventional BIOS interface. The AH=48h geometry fields and AH=08h translated values can differ. A partition table, a volume BPB, and a device’s full BIOS capacity are also different layers of information.

Use the information flags before treating optional fields as valid. In EDD 1.1, bit 1 says the geometry fields are valid; bit 2 says the device is removable; bit 3 reports write-with-verify support; and bits 4 and 5 describe change-line and lock support for removable media. Bit 6 describes a removable device with maximum geometry and no media. Reserved bits are not application storage. If the device is removable and empty, geometry can be a placeholder rather than a description of inserted media.

The Number of sectors field is a 64-bit count. It is one greater than the maximum sector number because the maximum address is zero-based. For a checked capacity estimate, multiply that count by Bytes per sector with overflow detection and reject a zero or unsupported sector size. The mathematical product can require up to 80 bits for the full field ranges, so a 64-bit result is not automatically sufficient. Do not multiply in a 16-bit intermediate and widen afterward. On vintage compilers without a sufficiently wide checked type, report the raw sector count and sector size separately or use an explicitly checked multiword routine.

Even a correct product is only a BIOS-reported device-capacity estimate. It is not the free-cluster count returned by DOS INT 21h/AH=36h, nor does it tell an application how much space is available on a selected partition. A VM can report the size of a virtual disk; firmware translation and controller policy can affect the view. Compare the BIOS result with the partition table and mounted-volume information only after identifying the exact boot drive and storage stack.

Validate output without writing to the disk

An inventory utility can test this interface safely with read-only calls:

  1. Save the BIOS drive number supplied by the boot chain. Do not guess it from C: or probe by writing to candidate drives.
  2. Run AH=41h; record carry, returned BX, version in AH, and capability bits in CX. For this Phoenix EDD 1.1 walkthrough, require the specified AH=21h value and at least one defined subset bit. If the signature, version, or subset check fails, use only a fallback whose own BIOS contract is supported.
  3. Prepare a result buffer of at least 30 bytes addressable at DS:SI, set its length word, and call AH=48h with the same drive number.
  4. On carry set, save AH as the BIOS error code and stop parsing. On success, accept only a returned length the allocated buffer can contain (26 or 30 bytes for this EDD 1.1 request); validate relevant flag bits before consuming optional values.
  5. Compare the reported logical geometry with AH=08h only as a consistency diagnostic. A difference is not automatically an error because one interface returns BIOS-translated logical geometry and the other EDD characteristics.
  6. Display sector count, bytes per sector, flags, and calculated units with their source labels. Do not label the result “partition size,” “FreeDOS free space,” or “usable application space.”

If the returned size is 26 despite a larger buffer, the optional pointer at offset 1Ah is absent; do not read it. If the geometry-valid flag is clear, do not present the cylinder/head fields as reliable geometry. If AH=48h fails while ordinary DOS file access succeeds, the discrepancy may simply mean the BIOS interface is unavailable for that device or environment; it does not prove the filesystem is damaged. Record the machine or VM firmware, boot path, BIOS drive ID, and exact carry/error values before comparing results across systems.

The practical boundary is straightforward: AH=48h describes what the BIOS reports about a drive, while DOS file APIs describe mounted files and volumes. Use the former for firmware-level inventory, and use DOS filesystem APIs for application capacity checks. The broader INT 13h guide covers capability probing and extended sector transfers; this article isolates the parameter query so a geometry or capacity report does not accidentally become a raw-I/O operation.

Related:

Sources:

Comments