Logo Search packages:      
Sourcecode: dosbox version File versions  Download package

bios_disk.cpp

/*
 *  Copyright (C) 2002-2004  The DOSBox Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "dosbox.h"
#include "callback.h"
#include "bios.h"
#include "regs.h"
#include "mem.h"
#include "dos_inc.h" /* for Drives[] */
#include "mapper.h"

#define MAX_SWAPPABLE_DISKS 20

diskGeo DiskGeometryList[] = {
      {160,  8, 1, 40, 0},
      {180,  9, 1, 40, 0},
      {320,  8, 2, 40, 1},
      {360,  9, 2, 40, 1},
      {720,  9, 2, 80, 3},
      {1200, 15, 2, 80, 2},
      {1440, 18, 2, 80, 4},
      {2880, 36, 2, 80, 6},
      {0, 0, 0 , 0},
};

Bitu call_int13;
Bitu diskparm0, diskparm1;
static Bit8u last_status;
static Bit8u last_drive;
Bit16u imgDTASeg;
RealPt imgDTAPtr;
DOS_DTA *imgDTA;
bool killRead;

/* 2 floppys and 2 harddrives, max */
imageDisk *imageDiskList[4];
imageDisk *diskSwap[MAX_SWAPPABLE_DISKS];
Bits swapPosition;

void updateDPT(void) {
      Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
      if(imageDiskList[2] != NULL) {
            imageDiskList[2]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
            real_writew(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0)),tmpcyl);
            real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+2,tmpheads);
            real_writew(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0x3,0);
            real_writew(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0x5,(Bit16u)-1);
            real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0x7,0);
            real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0x8,(0xc0 | (((imageDiskList[2]->heads) > 8) << 3)));
            real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0x9,0);
            real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0xa,0);
            real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0xb,0);
            real_writew(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0xc,tmpcyl);
            real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0xe,tmpsect);
      }
      if(imageDiskList[3] != NULL) {
            imageDiskList[3]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
            real_writew(RealSeg(CALLBACK_RealPointer(diskparm1)),RealOff(CALLBACK_RealPointer(diskparm1)),tmpcyl);
            real_writeb(RealSeg(CALLBACK_RealPointer(diskparm1)),RealOff(CALLBACK_RealPointer(diskparm1))+2,tmpheads);
            real_writeb(RealSeg(CALLBACK_RealPointer(diskparm1)),RealOff(CALLBACK_RealPointer(diskparm1))+0xe,tmpsect);
      }
}

void swapInDisks(void) {
      bool allNull = true;
      Bits diskcount = 0;
      Bits swapPos = swapPosition;
      int i;

      /* Check to make sure there's atleast one setup image */
      for(i=0;i<MAX_SWAPPABLE_DISKS;i++) {
            if(diskSwap[i]!=NULL) {
                  allNull = false;
                  break;
            }
      }

      /* No disks setup... fail */
      if (allNull) return;

      /* If only one disk is loaded, this loop will load the same disk in dive A and drive B */
      while(diskcount<2) {
            if(diskSwap[swapPos] != NULL) {
                  LOG_MSG("Loaded disk %d from swaplist position %d - \"%s\"", diskcount, swapPos, diskSwap[swapPos]->diskname);
                  imageDiskList[diskcount] = diskSwap[swapPos];
                  diskcount++;
            }
            swapPos++;
            if(swapPos>=MAX_SWAPPABLE_DISKS) swapPos=0;
      }
}

void swapInNextDisk(void) {
      /* Hack/feature: rescan all disks as well */
      for(Bitu i=0;i<DOS_DRIVES;i++) {
            if (Drives[i]) Drives[i]->EmptyCache();
      }
      swapPosition++;
      if(diskSwap[swapPosition] == NULL) swapPosition = 0;
      swapInDisks();
}


Bit8u imageDisk::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data) {
      Bit32u sectnum;

      sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;

      return Read_AbsoluteSector(sectnum, data);
}

Bit8u imageDisk::Read_AbsoluteSector(Bit32u sectnum, void * data) {
      Bit32u bytenum;

      bytenum = sectnum * sector_size;

      fseek(diskimg,bytenum,SEEK_SET);
      fread(data, 1, sector_size, diskimg);

      return 0x00;
}

Bit8u imageDisk::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data) {
      Bit32u sectnum;

      sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;

      return Write_AbsoluteSector(sectnum, data);

}


Bit8u imageDisk::Write_AbsoluteSector(Bit32u sectnum, void *data) {
      Bit32u bytenum;

      bytenum = sectnum * sector_size;

      //LOG_MSG("Writing sectors to %ld at bytenum %d", sectnum, bytenum);

      fseek(diskimg,bytenum,SEEK_SET);
      fwrite(data, sector_size, 1, diskimg);

      return 0x00;

}

imageDisk::imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) {
      heads = 0;
      cylinders = 0;
      sectors = 0;
      sector_size = 512;
      diskimg = imgFile;
      
      memset(diskname,0,512);
      if(strlen((const char *)imgName) > 511) {
            memcpy(diskname, imgName, 511);
      } else {
            strcpy((char *)diskname, (const char *)imgName);
      }

      active = false;
      hardDrive = isHardDisk;
      if(!isHardDisk) {
            Bitu i=0;
            bool founddisk = false;
            while (DiskGeometryList[i].ksize!=0x0) {
                  if (DiskGeometryList[i].ksize==imgSizeK) {
                        founddisk = true;
                        active = true;
                        floppytype = i;
                        heads = DiskGeometryList[i].headscyl;
                        cylinders = DiskGeometryList[i].cylcount;
                        sectors = DiskGeometryList[i].secttrack;
                        break;
                  }
                  i++;
            }
            if(!founddisk) {
                  active = false;
            }
      }
}

void imageDisk::Set_Geometry(Bit32u setHeads, Bit32u setCyl, Bit32u setSect, Bit32u setSectSize) {
      heads = setHeads;
      cylinders = setCyl;
      sectors = setSect;
      sector_size = setSectSize;
      active = true;
}

void imageDisk::Get_Geometry(Bit32u * getHeads, Bit32u *getCyl, Bit32u *getSect, Bit32u *getSectSize) {
      *getHeads = heads;
      *getCyl = cylinders;
      *getSect = sectors;
      *getSectSize = sector_size;
}

Bit8u imageDisk::GetBiosType(void) {
      if(!hardDrive) {
            return DiskGeometryList[floppytype].biosval;
      } else return 0;
}

Bit32u imageDisk::getSectSize(void) {
      return sector_size;
}

static Bitu GetDosDriveNumber(Bitu biosNum) {
      switch(biosNum) {
    case 0x0:
                  return 0x0;
            case 0x1:
                  return 0x1;
            case 0x80:
                  return 0x2;
            case 0x81:
                  return 0x3;
            case 0x82:
                  return 0x4;
            case 0x83:
                  return 0x5;
            default:
                  return 0x7f;
      }
}

static bool driveInactive(Bitu driveNum) {
      if(driveNum>=(2 + MAX_HDD_IMAGES)) {
            LOG(LOG_BIOS,LOG_ERROR)("Disk %d non-existant", driveNum);
            last_status = 0x01;
            CALLBACK_SCF(true);
            return true;
      }
      if(imageDiskList[driveNum] == NULL) {
            LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", driveNum);
            last_status = 0x01;
            CALLBACK_SCF(true);
            return true;
      }
      if(!imageDiskList[driveNum]->active) {
            LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", driveNum);
            last_status = 0x01;
            CALLBACK_SCF(true);
            return true;
      }
      return false;
}


static Bitu INT13_DiskHandler(void) {
      Bit16u segat, bufptr;
      Bit8u sectbuf[512];
      Bitu  drivenum;
      Bits  readcnt;
      int i,t;
      last_drive = reg_dl;
      drivenum = GetDosDriveNumber(reg_dl);
      //drivenum = 0;
      //LOG_MSG("INT13: Function %x called on drive %x (dos drive %d)", reg_ah,  reg_dl, drivenum);
      switch(reg_ah) {
      case 0x0: /* Reset disk */
            if(driveInactive(drivenum)) return CBRET_NONE;
            last_status = 0x00;
        CALLBACK_SCF(false);
        break;
      case 0x1: /* Get status of last operation */

            if(last_status != 0x00) {
                  reg_ah = last_status;
            CALLBACK_SCF(true);
            } else {
                  reg_ah = 0x00;
                  CALLBACK_SCF(false);
            }
            break;
      case 0x2: /* Read sectors */
            if(driveInactive(drivenum)) {
                  reg_ah = 0xff;
                  CALLBACK_SCF(true);
                  return CBRET_NONE;
            }

            segat = SegValue(es);
            bufptr = reg_bx;
            for(i=0;i<reg_al;i++) {
                  last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
                  if((last_status != 0x00) || (killRead)) {
                        LOG_MSG("Error in disk read");
                        killRead = false;
                        reg_ah = 0x04;
                        CALLBACK_SCF(true);
                        return CBRET_NONE;
                  }
                  for(t=0;t<512;t++) {
                        real_writeb(segat,bufptr,sectbuf[t]);
                        bufptr++;
                  }
            }
            reg_ah = 0x00;
            CALLBACK_SCF(false);
            break;
      case 0x3: /* Write sectors */
            
            if(driveInactive(drivenum)) {
                  reg_ah = 0xff;
                  CALLBACK_SCF(true);
                  return CBRET_NONE;
        }                     


            bufptr = reg_bx;
            for(i=0;i<reg_al;i++) {
                  for(t=0;t<imageDiskList[drivenum]->getSectSize();t++) {
                        sectbuf[t] = real_readb(SegValue(es),bufptr);
                        bufptr++;
                  }

                  last_status = imageDiskList[drivenum]->Write_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0) << 2)), (Bit32u)((reg_cl & 63) + i), &sectbuf[0]);
                  if(last_status != 0x00) {
            CALLBACK_SCF(true);
                        return CBRET_NONE;
                  }
        }
            CALLBACK_SCF(false);
        break;
      case 0x04: /* Verify sectors */
            if(driveInactive(drivenum)) return CBRET_NONE;

            /* TODO: Finish coding this section */
            /*
            segat = SegValue(es);
            bufptr = reg_bx;
            for(i=0;i<reg_al;i++) {
                  last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
                  if(last_status != 0x00) {
                        LOG_MSG("Error in disk read");
                        CALLBACK_SCF(true);
                        return CBRET_NONE;
                  }
                  for(t=0;t<512;t++) {
                        real_writeb(segat,bufptr,sectbuf[t]);
                        bufptr++;
                  }
            }*/
            reg_ah = 0x00;
            //reg_al = 0x10; /* CRC verify failed */
            reg_al = 0x00; /* CRC verify succeeded */
            CALLBACK_SCF(false);
          
            break;
      case 0x08: /* Get drive parameters */
            if(driveInactive(drivenum)) {
                  last_status = 0x07;
                  reg_ah = last_status;
            CALLBACK_SCF(true);
                  return CBRET_NONE;
            }
            reg_ax = 0x00;
            reg_bl = imageDiskList[drivenum]->GetBiosType();
            Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
            imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
            reg_ch = tmpcyl;
            reg_cl = tmpsect;
            reg_dh = tmpheads-1;
            last_status = 0x00;
            reg_dl = 0;
            if(imageDiskList[2] != NULL) reg_dl++;
            if(imageDiskList[3] != NULL) reg_dl++;
            CALLBACK_SCF(false);
            break;
      case 0x11: /* Recalibrate drive */
            reg_ah = 0x00;
            CALLBACK_SCF(false);
            break;
      case 0x17: /* Set disk type for format */
            /* Pirates! needs this to load */
            killRead = true;
            reg_ah = 0x00;
            CALLBACK_SCF(false);
            break;
      default:
            LOG(LOG_BIOS,LOG_ERROR)("INT13: Function %x called on drive %x (dos drive %d)", reg_ah,  reg_dl, drivenum);
            reg_ah=0xff;
            CALLBACK_SCF(true);
      }
      return CBRET_NONE;
}


void BIOS_SetupDisks(void) {
/* TODO Start the time correctly */
      call_int13=CALLBACK_Allocate();     
      //CALLBACK_Setup(call_int13,&INT13_SmallHandler,CB_IRET);
      CALLBACK_Setup(call_int13,&INT13_DiskHandler,CB_IRET,"Int 13 Bios disk");
      RealSetVec(0x13,CALLBACK_RealPointer(call_int13));
      int i;
      for(i=0;i<4;i++) {
            imageDiskList[i] = NULL;
      }

      for(i=0;i<MAX_SWAPPABLE_DISKS;i++) {
            diskSwap[i] = NULL;
      }

      diskparm0 = CALLBACK_Allocate();
      diskparm1 = CALLBACK_Allocate();
      swapPosition = 0;

      RealSetVec(0x41,CALLBACK_RealPointer(diskparm0));
      RealSetVec(0x46,CALLBACK_RealPointer(diskparm1));

      for(i=0;i<16;i++) {
            real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+i,0);
            real_writeb(RealSeg(CALLBACK_RealPointer(diskparm1)),RealOff(CALLBACK_RealPointer(diskparm1))+i,0);
      }

      imgDTASeg = 0;

/* Setup the Bios Area */
      mem_writeb(BIOS_HARDDISK_COUNT,2);

      MAPPER_AddHandler(swapInNextDisk,MK_f4,MMOD1,"swapimg","Swap Image");
      killRead = false;
}


Generated by  Doxygen 1.6.0   Back to index