/*****************************************************************************
Sector-level disk I/O code for DOS, using DJGPP.
This code is public domain (no copyright).
You can do whatever you want with it.
EXPORTS:
int lba_biosdisk(int cmd, int drive, unsigned long lba,
int nsects, void *buf);
int get_hd_geometry(disk_t *disk);
*****************************************************************************/
#include <sys/movedata.h> /* dosmemget(), dosmemput() */
#include <string.h> /* memset() */
#include <stdio.h> /* printf() */
#include <bios.h> /* _DISK_... */
#include <dpmi.h> /* __dpmi_regs, __dpmi_int() */
#include <go32.h> /* _go32_info_block, __tb */
#include "diskio.h"
#include "dos.h" /* peekb() */
/*****************************************************************************
*****************************************************************************/
int lba_biosdisk(int cmd, int drive, unsigned long lba, int nsects, void *buf)
{
struct
{
unsigned char pkt_len __attribute__((packed));
unsigned char res0 __attribute__((packed));
unsigned char nsects __attribute__((packed));
unsigned char res1 __attribute__((packed));
unsigned short buf_off __attribute__((packed));
unsigned short buf_seg __attribute__((packed));
unsigned long lba31_0 __attribute__((packed));
unsigned long lba63_32 __attribute__((packed));
} lba_cmd_pkt;
unsigned tries, err = 0;
__dpmi_regs regs;
if(cmd != _DISK_READ && cmd != _DISK_WRITE)
return 0x100;
/* make sure the DJGPP transfer buffer (in conventional memory)
is big enough */
if(BPS * nsects + sizeof(lba_cmd_pkt) >
_go32_info_block.size_of_transfer_buffer)
return 0x100;
/* make sure drive and BIOS support LBA */
regs.x.bx = 0x55AA;
regs.h.dl = drive;
regs.h.ah = 0x41;
__dpmi_int(0x13, ®s);
if(regs.x.flags & 0x0001) /* carry bit (CY) is set */
return 0x100;
/* fill out the INT 13h AH=4xh command packet */
memset(&lba_cmd_pkt, 0, sizeof(lba_cmd_pkt));
lba_cmd_pkt.pkt_len = sizeof(lba_cmd_pkt);
lba_cmd_pkt.nsects = nsects;
/* use start of transfer buffer for data transferred by BIOS disk I/O... */
lba_cmd_pkt.buf_off = 0;
lba_cmd_pkt.buf_seg = __tb >> 4;
lba_cmd_pkt.lba31_0 = lba;
/* ...use end of transfer buffer for the command packet itself */
dosmemput(&lba_cmd_pkt, sizeof(lba_cmd_pkt),
__tb + BPS * nsects);
/* fill out registers for INT 13h AH=4xh */
regs.x.ds = (__tb + BPS * nsects) >> 4;
regs.x.si = (__tb + BPS * nsects) & 0x0F;
regs.h.dl = drive;
/* if writing, store the data */
if(cmd == _DISK_WRITE)
dosmemput(buf, BPS * nsects, __tb);
/* make 3 attempts */
for(tries = 3; tries != 0; tries--)
{
regs.h.ah = (cmd == _DISK_READ) ? 0x42 : 0x43;
__dpmi_int(0x13, ®s);
err = regs.h.ah;
if((regs.x.flags & 0x0001) == 0)
{
/* if reading, load the data */
if(cmd == _DISK_READ)
dosmemget(__tb, BPS * nsects, buf);
return 0;
}
/* reset disk */
regs.h.ah = _DISK_RESET;
__dpmi_int(0x13, ®s);
}
DEBUG(printf("lba_biosdisk(): error 0x%02X\n", err);)
return err;
}
/*****************************************************************************
*****************************************************************************/
int get_hd_geometry(disk_t *disk)
{
__dpmi_regs regs;
/* make sure hard drive exists */
if(disk->drive_num - 0x80 >= peekb(0x40, 0x75))
{
printf("get_hd_geometry(): hd 0x%02X does not exist\n",
disk->drive_num);
return -1;
}
/* use LBA if drive and BIOS support it */
regs.h.ah = 0x41;
regs.x.bx = 0x55AA;
regs.h.dl = disk->drive_num;
__dpmi_int(0x13, ®s);
if((regs.x.flags & 0x0001) == 0 && regs.x.bx == 0xAA55)
{
disk->use_lba = 1;
DEBUG(printf("get_hd_geometry(): using LBA for hd 0x%02X\n",
disk->drive_num);)
return 0;
}
/* get geometry from BIOS */
regs.h.ah = 0x08;
regs.h.dl = disk->drive_num;
__dpmi_int(0x13, ®s);
if(regs.x.flags & 0x0001)
{
printf("get_hd_geometry(): error getting geometry "
"for hard drive 0x%02X\n", disk->drive_num);
return -1;
}
disk->heads = regs.h.dh + 1;
disk->sectors = regs.h.cl & 0x3F;
DEBUG(printf("get_hd_geometry() for hd 0x%02X: "
"CHS=?:%u:%u (from INT 13h AH=08h)\n",
disk->drive_num,
disk->heads, disk->sectors);)
return 0;
}