通过调用中断操作磁盘,进行扇区操作。
/* disk.cpp
*
* Source file for implementation of CDisk,
*
* Authors: perry
* Date : February 21, 2011
* class : CDisk
*
* History:
* 2011/02/21 perry created.
*/
CDisk::CDisk(uint16_t driver) :
m_int13ext_ver(0),
m_valid(false),
m_drive(0)
{
union REGS
regs;
struct SREGS
sregs;
// try multiple times, if error.
for ( int i = 0; i < CALL_INT_TRIES; i++ )
{
regs.h.ah = 0x41;
regs.x.bx = 0x55aa;
regs.h.dl = driver | 0x80;
// confirm ext int 13 can be support.
int86x(0x13, ®s, ®s, &sregs);
if ( regs.x.cflag || regs.x.bx != 0xaa55 )
continue;
// 01h = 1.x
// 20h = 2.0 / EDD-1.0
// 21h = 2.1 / EDD-1.1
// 30h = EDD-3.0
m_int13ext_ver = regs.h.ah;
break;
}
regs.h.ah = 0x08;
regs.h.dl = driver | 0x80;
int86x(0x13, ®s, ®s, &sregs);
if ( regs.h.dl <= driver )
{
fprintf(stderr, "%d: drive not found\n", driver);
return;
}
if ( regs.x.cflag )
{
#ifdef _DEBUG
CException
* exp = NULL;
exp = new CExceptInt13(regs.h.ah);
exp->show();
exp->destroy();
#endif
return;
}
m_drive = driver;
m_valid = true;
m_cylinder_count = ((((int)regs.h.cl << 2) & 0x300) | regs.h.ch) + 1;
m_head_count = regs.h.dh + 1;
m_sector_count = regs.h.cl & 0x3f;
}
bool CDisk::callBios_CHS(
uint16_t cmd,
uint16_t cylinder,
uint16_t head,
uint16_t sector,
uint16_t count,
CBufferT<uint8_t> _FAR & buff)
{
union REGS
regs;
struct SREGS
sregs;
if ( sector < 1 )
return false;
sector &= 0x3f;
if ( cylinder > 0xff )
sector |= (cylinder >> 0x02) & 0xc0;
segread(&sregs);
// set up packet.
regs.x.bx = FP_OFF((void _FAR *)buff);
sregs.es = FP_SEG((void _FAR *)buff);
// set up registers for INT 13h AH = 02/03h.
regs.h.ch = cylinder; // low eight bits of cylinder number
regs.h.cl = sector; // start sector and high two bits of cylinder
regs.h.dh = head; // head
regs.h.dl = m_drive | 0x80; // drive number
// try multiple times, if error.
for ( int i = 0; i < CALL_INT_TRIES; i++ )
{
switch ( cmd )
{
case DISK_RESET:
regs.h.ah = 0x00; // reset.
break;
case DISK_STATUS:
regs.h.ah = 0x01; // status.
break;
case DISK_READ:
regs.h.ah = 0x02; // read.
break;
case DISK_WRITE:
regs.h.ah = 0x03; // write.
break;
case DISK_VERIFY:
regs.h.ah = 0x04; // verify.
break;
default:
#ifdef _DEBUG
fprintf(stderr, "%d, invalid command.\n", cmd);
#endif
return false;
}
// set sector count.
regs.h.al = count;
// call BIOS interrup
int86x(0x13, ®s, ®s, &sregs);
// call succeed.
if ( !regs.x.cflag && !regs.h.ah )
return true;
#ifdef _DEBUG
// CF set on error
CException
* exp = NULL;
exp = new CExceptInt13(regs.h.ah);
exp->show();
exp->destroy();
#endif
// reset disk
regs.h.ah = 0;
// call BIOS interrup
int86x(0x13, ®s, ®s, &sregs);
}
return false;
}
bool CDisk::callBios_LBA(
uint16_t cmd,
uint64_t lba,
uint16_t count,
CBufferT<uint8_t> _FAR & buff)
{
union REGS
regs;
struct SREGS
sregs;
struct lba_addr_pkt
packet;
segread(&sregs);
// set up packet.
sregs.ds = FP_SEG(&packet);
regs.x.si = FP_OFF(&packet);
packet.pkt_len = sizeof(lba_addr_pkt);
packet.nsects = count;
packet.buf_off = FP_OFF((void _FAR *)buff);
packet.buf_seg = FP_SEG((void _FAR *)buff);
packet.lba = lba;
// set up registers for INT 13h AH = 02/03h.
regs.h.ah = 0x42; // extended read.
regs.h.dl = m_drive | 0x80; // drive number
// try multiple times, if error.
for ( int i = 0; i < CALL_INT_TRIES; i++ )
{
switch ( cmd )
{
case DISK_RESET:
regs.h.ah = 0x00; // reset.
break;
case DISK_STATUS:
regs.h.ah = 0x01; // status.
break;
case DISK_READ:
regs.h.ah = 0x42; // read.
regs.h.al = 0x00; /* AL = write flags
---v1.0,2.0---
bit 0: verify write
bits 7-1 reserved (0)
---v2.1+ ---
00h,01h write without verify
02h write with verify
*/
break;
case DISK_WRITE:
regs.h.ah = 0x43; // write.
break;
case DISK_VERIFY:
regs.h.ah = 0x04; // verify.
break;
default:
#ifdef _DEBUG
fprintf(stderr, "%d, invalid command.\n", cmd);
#endif
return false;
}
// call BIOS interrup
int86x(0x13, ®s, ®s, &sregs);
// call succeed.
if ( !regs.x.cflag && !regs.h.ah )
return true;
#ifdef _DEBUG
// CF set on error
CException
* exp = NULL;
exp = new CExceptInt13Ex(regs.h.ah);
exp->show();
exp->destroy();
#endif
// reset disk
regs.h.ah = 0;
// call BIOS interrup
int86x(0x13, ®s, ®s, &sregs);
}
return false;
}
bool CDisk::readSectors(
uint32_t lba31_0,
uint32_t lba63_32,
uint16_t count,
CBufferT<uint8_t> _FAR & buff)
{
uint64_t
lba;
// using 64 bit address.
lba.dwords.dw1 = lba31_0;
lba.dwords.dw2 = lba63_32;
return readSectors(lba, count, buff);
}
bool CDisk::writeSectors(
uint32_t lba31_0,
uint32_t lba63_32,
uint16_t count,
CBufferT<uint8_t> _FAR & buff)
{
uint64_t
lba;
// using 64 bit address.
lba.dwords.dw1 = lba31_0;
lba.dwords.dw2 = lba63_32;
return writeSectors(lba, count, buff);
}
bool CDisk::readSectors(
uint64_t lba,
uint16_t count,
CBufferT<uint8_t> _FAR & buff)
{
if ( !isValid() )
return false;
// the number of sector must be greater than zero.
if ( count < 1 )
{
#ifdef _DEBUG
fprintf(stderr, "number of sectors must be greater than 0; (%d).\n", count);
#endif
return false;
}
// check size of buffer.
if ( buff.length() < (count * SECTOR_PER_BYTES) )
{
#ifdef _DEBUG
fprintf(stderr, "size of buffer too small; %d < (%d * %d).\n", buff.length(), count, SECTOR_PER_BYTES);
#endif
return false;
}
// clear buffer
buff.clear();
// if support ext int13. using LBA mode.
if ( m_int13ext_ver > 0 )
{
#ifdef _DEBUG
fprintf(stdout, "DISK_READ LBA %04x.\n", m_int13ext_ver);
#endif
if ( callBios_LBA(DISK_READ, lba, count, buff) )
return true;
}
// CHS params.
uint16_t
cylinder,
head,
sector;
// translate a LBA value to an CHS value.
if ( !LBA2CHS(lba, &cylinder, &head, §or) )
return false;
#ifdef _DEBUG
fprintf(stdout, "DISK_READ CHS.\n");
#endif
// using CHS mode to read sectors.
return callBios_CHS(DISK_READ, cylinder, head, sector, count, buff);
}
bool CDisk::writeSectors(
uint64_t lba,
uint16_t count,
CBufferT<uint8_t> _FAR & buff)
{
if ( !isValid() )
return false;
// the number of sector must be greater than zero.
if ( count < 1 )
{
#ifdef _DEBUG
fprintf(stderr, "number of sectors must be greater than 0; (%d).\n", count);
#endif
return false;
}
// check size of buffer.
if ( buff.length() < (count * SECTOR_PER_BYTES) )
{
#ifdef _DEBUG
fprintf(stderr, "size of buffer too small; %d < (%d * %d).\n", buff.length(), count, SECTOR_PER_BYTES);
#endif
return false;
}
// if support ext int13. using LBA mode.
if ( m_int13ext_ver > 0 )
{
#ifdef _DEBUG
fprintf(stdout, "DISK_WRITE LBA %04x.\n", m_int13ext_ver);
#endif
if ( callBios_LBA(DISK_WRITE, lba, count, buff) )
return true;
}
// CHS params.
uint16_t
cylinder,
head,
sector;
#ifdef _DEBUG
fprintf(stdout, "DISK_WRITE CHS.\n");
#endif
// translate a LBA value to an CHS value.
if ( !LBA2CHS(lba, &cylinder, &head, §or) )
return false;
// using CHS mode to read sectors.
return callBios_CHS(DISK_WRITE, cylinder, head, sector, count, buff);
}
bool CDisk::LBA2CHS(
uint64_t lba,
uint16_t _FAR * pCylinder,
uint16_t _FAR * pHead,
uint16_t _FAR * pSector)
{
uint32_t
temp1 = m_head_count * m_sector_count,
temp2;
if ( !temp1 )
return false;
temp2 = lba.dwords.dw1 % temp1;
*pCylinder = (uint16_t)((lba.dwords.dw1 - temp2) / temp1);
*pHead = (uint16_t)(temp2 / m_sector_count);
*pSector = (uint16_t)(temp2 % m_sector_count + 1);
return true;
}
bool CDisk::CHS2LBA(
uint16_t cylinder,
uint16_t head,
uint16_t sector,
uint64_t _FAR * plba)
{
if ( sector < 1 )
return false;
plba->dwords.dw1 = ((cylinder * m_head_count + head) * m_sector_count) + sector - 1;
plba->dwords.dw2 = 0;
return true;
}
bool CDisk::saveSectors(
uint32_t lba,
uint16_t num,
const CString _FAR & file)
{
if ( !isValid() )
return false;
CBufferT
buff(SECTOR_PER_BYTES);
bool
result = true;
CFile
ffout(file);
for ( int i = 0; i < num; i++ )
{
if ( !readSectors(lba + i, 0, 1, buff) )
{
#ifdef _DEBUG
fprintf(stderr, "FAIL ==> readSectors\n");
#endif
result = false;
break;
}
if ( ffout.write(buff) != SECTOR_PER_BYTES )
{
#ifdef _DEBUG
fprintf(stderr, "FAIL ==> ffout.Write\n");
#endif
result = false;
break;
}
}
#ifdef _DEBUG
if ( !result )
fprintf(stdout, "FAIL ==> saveSectors\n");
else
fprintf(stdout, "PASS ==> saveSectors\n");
#endif
return result;
}
bool CDisk::saveSectors(
uint32_t lba,
uint16_t num,
CBufferT _FAR & buff)
{
if ( !isValid() )
return false;
if ( buff.length() < (num * SECTOR_PER_BYTES) )
return false;
return readSectors(lba, 0, num, buff);
}
bool CDisk::fillSectors(
uint32_t lba,
uint16_t num,
const CString _FAR & file)
{
if ( !isValid() )
return false;
CBufferT
buff(SECTOR_PER_BYTES);
bool
result = true;
CFile
ffin(file, O_RDONLY | O_BINARY);
for ( int i = 0; i < num; i++ )
{
// zero buffer
buff.clear();
// read data from file.
if ( ffin.read(buff) != SECTOR_PER_BYTES )
{
#ifdef _DEBUG
fprintf(stderr, "FAIL ==> ffin.Read\n");
#endif
result = false;
break;
}
// write buffer to sector.
if ( !writeSectors(lba + i, 0, 1, buff) )
{
#ifdef _DEBUG
fprintf(stderr, "FAIL ==> writeSectors\n");
#endif
result = false;
break;
}
}
#ifdef _DEBUG
if ( !result )
fprintf(stdout, "FAIL ==> fillSectors\n");
else
fprintf(stdout, "PASS ==> fillSectors\n");
#endif
return result;
}
bool CDisk::fillSectors(
uint32_t lba,
uint16_t num,
uint8_t val)
{
if ( !isValid() )
return false;
CBufferT
buff(SECTOR_PER_BYTES);
bool
result = true;
// fill buffer
buff.clear();
for ( int i = 0; i < num; i++ )
{
if ( !writeSectors(lba + i, 0, 1, buff) )
{
#ifdef _DEBUG
fprintf(stderr, "FAIL ==> writeSectors\n");
#endif
result = false;
break;
}
}
#ifdef _DEBUG
if ( !result )
fprintf(stdout, "FAIL ==> fillSectors\n");
else
fprintf(stdout, "PASS ==> fillSectors\n");
#endif
return result;
}
const char* CDisk::toString(void)
{
return "CDisk";
}