/***************************************************************************** Sector-level disk I/O code for DOS. This code is public domain (no copyright). You can do whatever you want with it. EXPORTS: int read_sector(disk_t *disk, unsigned long lba, unsigned char *buf); int write_sector(disk_t *disk, unsigned long lba, unsigned char *buf); int is_fat_bootsector(unsigned char *buf); int open_disk(disk_t *disk, unsigned drive_num); *****************************************************************************/ #include <stdio.h> /* printf() */ /*#include <bios.h>*/ /* _DISK_..., diskinfo_t, _bios_disk() */ #include "diskio.h" #include "dos.h" /* peekw() */ /* IMPORTS from _16BIT.C, DJGPP, or ??? */ int lba_biosdisk(int cmd, int drive, unsigned long lba, int nsects, void *buf); int get_hd_geometry(disk_t *disk); /* xxx - I'm not sure of the Turbo C version where these were introduced. They are present in Turbo C++ 3.0 (__TURBOC__ == 0x401) but not in Turbo C++ 1.0 (__TURBOC__ == 0x296) */ #if defined(__TURBOC__) #if __TURBOC__<0x300 struct diskinfo_t { unsigned drive, head, track, sector, nsectors; void far *buffer; }; unsigned _bios_disk(unsigned cmd, struct diskinfo_t *info) { struct SREGS sregs; union REGS regs; /* biosdisk() returns the 8-bit error code left in register AH by the call to INT 13h. It does NOT return a combined, 16-bit error code + number of sectors transferred, as described in the online help. return biosdisk(cmd, info->drive, info->head, info->track, info->sector, info->nsectors, info->buffer); */ regs.h.ah = cmd; regs.h.al = info->nsectors; regs.x.bx = FP_OFF(info->buffer); regs.h.ch = info->track; regs.h.cl = (info->track / 256) * 64 + (info->sector & 0x3F); regs.h.dh = info->head; regs.h.dl = info->drive; sregs.es = FP_SEG(info->buffer); int86x(0x13, ®s, ®s, &sregs); return regs.x.ax; } #endif #endif /***************************************************************************** *****************************************************************************/ int read_sector(disk_t *disk, unsigned long lba, unsigned char *buf) { struct diskinfo_t cmd; unsigned tries, err; lba += disk->partition_start; cmd.drive = disk->drive_num; /* use LBA if available */ if(disk->use_lba) { return lba_biosdisk(_DISK_READ, disk->drive_num, lba, 1, buf); } /* use CHS _bios_disk() */ cmd.sector = (unsigned)(lba % disk->sectors + 1); cmd.head = (unsigned)((lba / disk->sectors) % disk->heads); cmd.track = (unsigned)((lba / disk->sectors) / disk->heads); cmd.nsectors = 1; cmd.buffer = buf; /* make 3 attempts */ for(tries = 3; tries != 0; tries--) { err = _bios_disk(_DISK_READ, &cmd); err >>= 8; /* 0x11=="CRC/ECC corrected data error" */ if(err == 0 || err == 0x11) return 0; /* reset disk */ _bios_disk(_DISK_RESET, &cmd); } DEBUG(printf("read_sector(): error 0x%02X\n", err);) return err; } /***************************************************************************** *****************************************************************************/ int write_sector(disk_t *disk, unsigned long lba, unsigned char *buf) { struct diskinfo_t cmd; unsigned tries, err; lba += disk->partition_start; cmd.drive = disk->drive_num; /* use LBA if available */ if(disk->use_lba) { return lba_biosdisk(_DISK_WRITE, disk->drive_num, lba, 1, buf); } /* use CHS _bios_disk() */ cmd.sector = (unsigned)(lba % disk->sectors + 1); cmd.head = (unsigned)((lba / disk->sectors) % disk->heads); cmd.track = (unsigned)((lba / disk->sectors) / disk->heads); cmd.nsectors = 1; cmd.buffer = buf; /* make 3 attempts */ for(tries = 3; tries != 0; tries--) { err = _bios_disk(_DISK_WRITE, &cmd); err >>= 8; /* 0x11=="CRC/ECC corrected data error" */ if(err == 0 || err == 0x11) return 0; /* reset disk */ _bios_disk(_DISK_RESET, &cmd); } DEBUG(printf("write_sector(): error 0x%02X\n", err);) return err; } /***************************************************************************** *****************************************************************************/ int is_fat_bootsector(unsigned char *buf) { int temp, ok = 1; DEBUG(printf("check_if_fat_bootsector:\n");) /* must start with 16-bit JMP or 8-bit JMP plus NOP */ if(buf[0] == 0xE9) /* OK */; else if(buf[0] == 0xEB && buf[2] == 0x90) /* OK */; else { DEBUG(printf("\tMissing JMP/NOP\n");) ok = 0; } temp = buf[13]; if(temp == 0 || ((temp - 1) & temp) != 0) { DEBUG(printf("\tSectors per cluster (%u) " "is not a power of 2\n", temp);) ok = 0; } temp = buf[16]; temp = LE16(buf + 24); if(temp == 0 || temp > 63) { DEBUG(printf("\tInvalid number of sectors (%u)\n", temp);) ok = 0; } temp = LE16(buf + 26); if(temp == 0 || temp > 255) { DEBUG(printf("\tInvalid number of heads (%u)\n", temp);) ok = 0; } return ok; } /***************************************************************************** *****************************************************************************/ static void probe_floppy_geometry(disk_t *disk) { unsigned sectors, heads; unsigned char buf[BPS]; /* scan upwards for sector number where read fails */ disk->sectors = 64 + 1; for(sectors = 1; sectors <= 64; sectors++) { if(read_sector(disk, sectors - 1, buf) != 0) break; } disk->sectors = sectors - 1; #if 1 disk->heads = 2; #else /* scan upwards for head number where read fails xxx - this probing for number of heads doesn't work */ disk->heads = 16 + 1; for(heads = 1; heads < 16; heads++) { if(read_sector(disk, heads * disk->sectors, buf) != 0) break; } disk->heads = heads; #endif /* reset drive by reading sector 0 */ (void)read_sector(disk, 0, buf); DEBUG(printf("probe_floppy_geometry() for fd 0x%02X: " "CHS=?:%u:%u\n", disk->drive_num, disk->heads, disk->sectors);) } /***************************************************************************** *****************************************************************************/ int open_disk(disk_t *disk, unsigned drive_num) { unsigned char buf[BPS]; unsigned num_fds; int err; disk->drive_num = drive_num; disk->partition_start = 0; /* assume floppy */ disk->use_lba = 0; /* assume CHS */ /* hard disk */ if(disk->drive_num >= 0x80) return get_hd_geometry(disk); /* make sure floppy drive exists */ num_fds = peekw(0x40, 0x10); if(num_fds & 0x0001) num_fds = ((num_fds / 64) & 3) + 1; else num_fds = 0; if(disk->drive_num >= num_fds) { printf("open_disk(): fd 0x%02X does not exist\n", disk->drive_num); return -1; } /* temporary values to make read_sector(0) work */ disk->heads = disk->sectors = 1; err = read_sector(disk, 0, buf); if(err != 0) return err; /* if it's a FAT (DOS) disk, we get can reliable geometry info from the BIOS parameter block (BPB) in the bootsector */ if(is_fat_bootsector(buf)) { disk->heads = LE16(buf + 26); disk->sectors = LE16(buf + 24); DEBUG(printf("open_disk() for fd 0x%02X: " "CHS=?:%u:%u (from BPB)\n", disk->drive_num, disk->heads, disk->sectors);) return 0; } #if 0 /* ...otherwise, do slow probe */ probe_floppy_geometry(disk); #else /* ...or just assume some values */ disk->heads = 2; disk->sectors = 18; DEBUG(printf("open_disk() for fd 0x%02X: " "assuming CHS=?:2:18\n", disk->drive_num);) #endif return 0; }