ATA in x86 RealMode (BIOS)

From OSDev Wiki

Jump to: navigation, search

When the CPU is in Real Mode (before entering Protected Mode, or Long Mode), the BIOS is still functional. The BIOS provides a set of disk access routines using INT 0x13, with various values in the AX register. Until an OS has a disk driver loaded, these BIOS routines are the only way to access disks.

There are two basic INT0x13 calls to use for ATA disk access. One uses Cylinder, Head, Sector addressing, and the other uses LBA addressing. There is a third command to access ATAPI drives using the PACKET command set.

Note: These BIOS INT calls should completely preserve all the registers (except AH). However, some older versions of Bochs destroyed the upper words of some of the 32bit registers.

Contents

CHS

Reading or writing an ATA hard disk in Real Mode with CHS addressing is precisely the same as doing the same operation on a floppy drive.

To read some sectors from a CHS address:

  • Cylinder = 0 to 1023 (maybe 4095), Head = 0 to 15 (maybe 254, maybe 255), Sector = 1 to 63
  • Set AH = 2
  • AL = total sector count (0 is illegal) -- cannot cross ES page boundary
  • CH = cylinder & 0xff
  • CL = Sector | ((cylinder >> 2) & 0xC0);
  • DH = Head -- may include two more cylinder bits
  • ES:BX -> buffer
  • Set DL = "drive number" -- typically 0x80, for the "C" drive.
  • Issue an INT 0x13.

The carry flag will be set if there is any error during the read. AH should be set to 0 on success.

To write: set AH to 3, instead.

LBA in Extended Mode

To use LBA addressing with INT 0x13, you need to use a command in the "INT13h Extensions". Every BIOS since the mid-90's supports the extensions, but you may want to verify that they are supported anyway.

  • Set AH = 0x41
  • BX = 0x55AA
  • DL = 0x80
  • Issue an INT 0x13.

The carry flag will be set if Extensions are not supported.


To read or write, first you need to set up a "Disk Address Packet Structure" in memory, on a DWORD (4 byte) boundary.

  • Format of disk address packet:
Offset	Size	Description
 0	BYTE	size of packet (16 bytes)
 1	BYTE	always 0
 2	WORD	number of sectors to transfer (max 127 on some BIOSes)
 4	DWORD	-> transfer buffer (16 bit segment:16 bit offset)
 8	DWORD	starting LBA
12	DWORD	used for upper part of 48 bit LBAs

Notes: The 16 bit segment value ends up at an offset of 6 from the beginning of the structure. If the disk drive itself does not support LBA addressing, the BIOS will automatically convert the LBA to a CHS address for you -- so this function still works. The transfer buffer should be WORD (2 byte) aligned.

To read a disk:

  • Set the proper values in the disk address packet structure
  • Set DS:SI -> Disk Address Packet in memory
  • Set AH = 0x42
  • Set DL = "drive number" -- typically 0x80 for the "C" drive
  • Issue an INT 0x13.

The carry flag will be set if there is any error during the transfer. AH should be set to 0 on success.

To write to a disk, set AH = 0x43.

x86 Examples

  • Reading 16 sectors from LBA #1 to physical address 0x7C00
DAPACK:
	db	0x10
	db	0
blkcnt:	dw	16		; int 13 resets this to # of blocks actually read/written
db_add:	dw	0x7C00		; memory buffer destination address (0:7c00)
	dw	0		; in memory page zero
d_lba:	dd	1		; put the lba to read in this spot
	dd	0		; more storage bytes only for big lba's ( > 4 bytes )

	mov si, DAPACK		; address of "disk address packet"
	mov ah, 0x42		; AL is unused
	mov dl, 0x80		; drive number 0 (OR the drive # with 0x80)
	int 0x13
	jc short .error

Supported Systems

All systems support CHS addressing.

There exist some 486 systems that do not support LBA in any way. All known Pentium systems support Extended LBA in the BIOS.

Comments

One of the easiest ways to read or write a USB flash drive is to drop into Real or Unreal Mode, and use the INT 0x13 BIOS commands. However, the transfer must fit in the usable part of low memory (if in Real Mode), and you need to somehow know the proper drive number to use in DL.

Personal tools