DOS中调用INT13中断操作磁盘 tc3.0

本文介绍了一个用于磁盘操作的类CDisk的实现细节,包括如何通过BIOS中断进行扇区级别的读写操作,支持CHS与LBA模式,并提供了多种实用的方法如保存与填充扇区。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通过调用中断操作磁盘,进行扇区操作。
/* 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";
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值