DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/ints/bios_disk.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 #include "dosbox.h"
00020 #include "callback.h"
00021 #include "bios.h"
00022 #include "bios_disk.h"
00023 #include "regs.h"
00024 #include "mem.h"
00025 #include "dos_inc.h" /* for Drives[] */
00026 #include "../dos/drives.h"
00027 #include "mapper.h"
00028 #include "ide.h"
00029 
00030 #if defined(_MSC_VER)
00031 # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */
00032 #endif
00033 
00034 extern bool int13_extensions_enable;
00035 
00036 diskGeo DiskGeometryList[] = {
00037     { 160,  8, 1, 40, 0, 512,  64, 1, 0xFE},      // IBM PC double density 5.25" single-sided 160KB
00038     { 180,  9, 1, 40, 0, 512,  64, 2, 0xFC},      // IBM PC double density 5.25" single-sided 180KB
00039     { 200, 10, 1, 40, 0, 512,   0, 0,    0},      // DEC Rainbow double density 5.25" single-sided 200KB (I think...)
00040     { 320,  8, 2, 40, 1, 512, 112, 2, 0xFF},      // IBM PC double density 5.25" double-sided 320KB
00041     { 360,  9, 2, 40, 1, 512, 112, 2, 0xFD},      // IBM PC double density 5.25" double-sided 360KB
00042     { 400, 10, 2, 40, 1, 512,   0, 0,    0},      // DEC Rainbow double density 5.25" double-sided 400KB (I think...)
00043     { 640,  8, 2, 80, 3, 512, 112, 2, 0xFB},      // IBM PC double density 3.5" double-sided 640KB
00044     { 720,  9, 2, 80, 3, 512, 112, 2, 0xF9},      // IBM PC double density 3.5" double-sided 720KB
00045     {1200, 15, 2, 80, 2, 512, 224, 1, 0xF9},      // IBM PC double density 5.25" double-sided 1.2MB
00046     {1440, 18, 2, 80, 4, 512, 224, 1, 0xF0},      // IBM PC high density 3.5" double-sided 1.44MB
00047     {2880, 36, 2, 80, 6, 512, 240, 2, 0xF0},      // IBM PC high density 3.5" double-sided 2.88MB
00048 
00049     {1232,  8, 2, 77, 7, 1024,192, 1, 0xFE},      // NEC PC-98 high density 3.5" double-sided 1.2MB "3-mode"
00050 
00051     {   0,  0, 0,  0, 0,    0,  0, 0,    0}
00052 };
00053 
00054 Bitu call_int13 = 0;
00055 Bitu diskparm0 = 0, diskparm1 = 0;
00056 static Bit8u last_status;
00057 static Bit8u last_drive;
00058 Bit16u imgDTASeg;
00059 RealPt imgDTAPtr;
00060 DOS_DTA *imgDTA;
00061 bool killRead;
00062 static bool swapping_requested;
00063 
00064 void CMOS_SetRegister(Bitu regNr, Bit8u val); //For setting equipment word
00065 
00066 /* 2 floppys and 2 harddrives, max */
00067 imageDisk *imageDiskList[MAX_DISK_IMAGES]={NULL};
00068 imageDisk *diskSwap[MAX_SWAPPABLE_DISKS]={NULL};
00069 Bits swapPosition;
00070 
00071 imageDisk *GetINT13FloppyDrive(unsigned char drv) {
00072     if (drv >= 2)
00073         return NULL;
00074     return imageDiskList[drv];
00075 }
00076 
00077 imageDisk *GetINT13HardDrive(unsigned char drv) {
00078     if (drv < 0x80 || drv >= (0x80+MAX_DISK_IMAGES-2))
00079         return NULL;
00080 
00081     return imageDiskList[drv-0x80];
00082 }
00083 
00084 void FreeBIOSDiskList() {
00085     for (int i=0;i < MAX_DISK_IMAGES;i++) {
00086         if (imageDiskList[i] != NULL) {
00087             if (i >= 2) IDE_Hard_Disk_Detach(i);
00088             imageDiskList[i]->Release();
00089             imageDiskList[i] = NULL;
00090         }
00091     }
00092 
00093     for (int j=0;j < MAX_SWAPPABLE_DISKS;j++) {
00094         if (diskSwap[j] != NULL) {
00095             diskSwap[j]->Release();
00096             diskSwap[j] = NULL;
00097         }
00098     }
00099 }
00100 
00101 //update BIOS disk parameter tables for first two hard drives
00102 void updateDPT(void) {
00103     Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
00104     PhysPt dpphysaddr[2] = { CALLBACK_PhysPointer(diskparm0), CALLBACK_PhysPointer(diskparm1) };
00105     for (int i = 0; i < 2; i++) {
00106         tmpheads = 0; tmpcyl = 0; tmpsect = 0; tmpsize = 0;
00107         if (imageDiskList[i + 2] != NULL) {
00108             imageDiskList[i + 2]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
00109         }
00110         phys_writew(dpphysaddr[i], (Bit16u)tmpcyl);
00111         phys_writeb(dpphysaddr[i] + 0x2, (Bit8u)tmpheads);
00112         phys_writew(dpphysaddr[i] + 0x3, 0);
00113         phys_writew(dpphysaddr[i] + 0x5, tmpcyl == 0 ? 0 : (Bit16u)-1);
00114         phys_writeb(dpphysaddr[i] + 0x7, 0);
00115         phys_writeb(dpphysaddr[i] + 0x8, tmpcyl == 0 ? 0 : (0xc0 | (((tmpheads) > 8) << 3)));
00116         phys_writeb(dpphysaddr[i] + 0x9, 0);
00117         phys_writeb(dpphysaddr[i] + 0xa, 0);
00118         phys_writeb(dpphysaddr[i] + 0xb, 0);
00119         phys_writew(dpphysaddr[i] + 0xc, (Bit16u)tmpcyl);
00120         phys_writeb(dpphysaddr[i] + 0xe, (Bit8u)tmpsect);
00121     }
00122 }
00123 
00124 void incrementFDD(void) {
00125     Bit16u equipment=mem_readw(BIOS_CONFIGURATION);
00126     if(equipment&1) {
00127         Bitu numofdisks = (equipment>>6)&3;
00128         numofdisks++;
00129         if(numofdisks > 1) numofdisks=1;//max 2 floppies at the moment
00130         equipment&=~0x00C0;
00131         equipment|=(numofdisks<<6);
00132     } else equipment|=1;
00133     mem_writew(BIOS_CONFIGURATION,equipment);
00134     CMOS_SetRegister(0x14, (Bit8u)(equipment&0xff));
00135 }
00136 
00137 int swapInDisksSpecificDrive = -1;
00138 // -1 = swap across A: and B: (DOSBox / DOSBox-X default behavior)
00139 //  0 = swap across A: only
00140 //  1 = swap across B: only
00141 
00142 void swapInDisks(void) {
00143     bool allNull = true;
00144     Bits diskcount = 0;
00145     Bits diskswapcount = 2;
00146     Bits diskswapdrive = 0;
00147     Bits swapPos = swapPosition;
00148     int i;
00149 
00150     /* Check to make sure that  there is at least one setup image */
00151     for(i=0;i<MAX_SWAPPABLE_DISKS;i++) {
00152         if(diskSwap[i]!=NULL) {
00153             allNull = false;
00154             break;
00155         }
00156     }
00157 
00158     /* No disks setup... fail */
00159     if (allNull) return;
00160 
00161     /* if a specific drive is to be swapped, then adjust to focus on it */
00162     if (swapInDisksSpecificDrive >= 0 && swapInDisksSpecificDrive <= 1) {
00163         diskswapdrive = swapInDisksSpecificDrive;
00164         diskswapcount = 1;
00165     }
00166 
00167     /* If only one disk is loaded, this loop will load the same disk in dive A and drive B */
00168     while(diskcount < diskswapcount) {
00169         if(diskSwap[swapPos] != NULL) {
00170             LOG_MSG("Loaded drive %d disk %d from swaplist position %d - \"%s\"", (int)diskswapdrive, (int)diskcount, (int)swapPos, diskSwap[swapPos]->diskname.c_str());
00171 
00172             if (imageDiskList[diskswapdrive] != NULL)
00173                 imageDiskList[diskswapdrive]->Release();
00174 
00175             imageDiskList[diskswapdrive] = diskSwap[swapPos];
00176             imageDiskList[diskswapdrive]->Addref();
00177 
00178             diskcount++;
00179             diskswapdrive++;
00180         }
00181 
00182         swapPos++;
00183         if(swapPos>=MAX_SWAPPABLE_DISKS) swapPos=0;
00184     }
00185 }
00186 
00187 bool getSwapRequest(void) {
00188     bool sreq=swapping_requested;
00189     swapping_requested = false;
00190     return sreq;
00191 }
00192 
00193 void swapInNextDisk(bool pressed) {
00194     if (!pressed)
00195         return;
00196     DriveManager::CycleAllDisks();
00197     /* Hack/feature: rescan all disks as well */
00198     LOG_MSG("Diskcaching reset for floppy drives.");
00199     for(Bitu i=0;i<2;i++) { /* Swap A: and B: where DOSBox mainline would run through ALL drive letters */
00200         if (Drives[i] != NULL) {
00201             Drives[i]->EmptyCache();
00202             Drives[i]->MediaChange();
00203         }
00204     }
00205     swapPosition++;
00206     if(diskSwap[swapPosition] == NULL) swapPosition = 0;
00207     swapInDisks();
00208     swapping_requested = true;
00209 }
00210 
00211 void swapInNextCD(bool pressed) {
00212     if (!pressed)
00213         return;
00214     DriveManager::CycleAllCDs();
00215     /* Hack/feature: rescan all disks as well */
00216     LOG_MSG("Diskcaching reset for normal mounted drives.");
00217     for(Bitu i=2;i<DOS_DRIVES;i++) { /* Swap C: D: .... Z: TODO: Need to swap ONLY if a CD-ROM drive! */
00218         if (Drives[i] != NULL) {
00219             Drives[i]->EmptyCache();
00220             Drives[i]->MediaChange();
00221         }
00222     }
00223 }
00224 
00225 
00226 Bit8u imageDisk::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) {
00227     Bit32u sectnum;
00228 
00229     if (req_sector_size == 0)
00230         req_sector_size = sector_size;
00231     if (req_sector_size != sector_size)
00232         return 0x05;
00233 
00234     sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;
00235 
00236     return Read_AbsoluteSector(sectnum, data);
00237 }
00238 
00239 Bit8u imageDisk::Read_AbsoluteSector(Bit32u sectnum, void * data) {
00240     Bit64u bytenum,res;
00241     int got;
00242 
00243     bytenum = (Bit64u)sectnum * (Bit64u)sector_size;
00244     if ((bytenum + sector_size) > this->image_length) {
00245         LOG_MSG("Attempt to read invalid sector in Read_AbsoluteSector for sector %lu.\n", (unsigned long)sectnum);
00246         return 0x05;
00247     }
00248     bytenum += image_base;
00249 
00250     //LOG_MSG("Reading sectors %ld at bytenum %I64d", sectnum, bytenum);
00251 
00252     fseeko64(diskimg,(off_t)bytenum,SEEK_SET);
00253     res = (Bit64u)ftello64(diskimg);
00254     if (res != bytenum) {
00255         LOG_MSG("fseek() failed in Read_AbsoluteSector for sector %lu. Want=%llu Got=%llu\n",
00256             (unsigned long)sectnum,(unsigned long long)bytenum,(unsigned long long)res);
00257         return 0x05;
00258     }
00259 
00260     got = (int)fread(data, 1, sector_size, diskimg);
00261     if ((unsigned int)got != sector_size) {
00262         LOG_MSG("fread() failed in Read_AbsoluteSector for sector %lu. Want=%u got=%d\n",
00263             (unsigned long)sectnum,(unsigned int)sector_size,(unsigned int)got);
00264         return 0x05;
00265     }
00266 
00267     return 0x00;
00268 }
00269 
00270 Bit8u imageDisk::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) {
00271     Bit32u sectnum;
00272 
00273     if (req_sector_size == 0)
00274         req_sector_size = sector_size;
00275     if (req_sector_size != sector_size)
00276         return 0x05;
00277 
00278     sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;
00279 
00280     return Write_AbsoluteSector(sectnum, data);
00281 }
00282 
00283 
00284 Bit8u imageDisk::Write_AbsoluteSector(Bit32u sectnum, const void *data) {
00285     Bit64u bytenum;
00286 
00287     bytenum = (Bit64u)sectnum * sector_size;
00288     if ((bytenum + sector_size) > this->image_length) {
00289         LOG_MSG("Attempt to read invalid sector in Write_AbsoluteSector for sector %lu.\n", (unsigned long)sectnum);
00290         return 0x05;
00291     }
00292     bytenum += image_base;
00293 
00294     //LOG_MSG("Writing sectors to %ld at bytenum %d", sectnum, bytenum);
00295 
00296     fseeko64(diskimg,(off_t)bytenum,SEEK_SET);
00297     if ((Bit64u)ftello64(diskimg) != bytenum)
00298         LOG_MSG("WARNING: fseek() failed in Write_AbsoluteSector for sector %lu\n",(unsigned long)sectnum);
00299 
00300     size_t ret=fwrite(data, sector_size, 1, diskimg);
00301 
00302     return ((ret>0)?0x00:0x05);
00303 
00304 }
00305 
00306 void imageDisk::Set_Reserved_Cylinders(Bitu resCyl) {
00307     reserved_cylinders = resCyl;
00308 }
00309 
00310 Bit32u imageDisk::Get_Reserved_Cylinders() {
00311     return reserved_cylinders;
00312 }
00313 
00314 imageDisk::imageDisk(IMAGE_TYPE class_id) {
00315     heads = 0;
00316     cylinders = 0;
00317     image_base = 0;
00318     sectors = 0;
00319     refcount = 0;
00320     sector_size = 512;
00321     image_length = 0;
00322     reserved_cylinders = 0;
00323     diskimg = NULL;
00324     this->class_id = class_id;
00325     active = false;
00326     hardDrive = false;
00327 }
00328 
00329 imageDisk::imageDisk(FILE* diskimg, const char* diskName, Bit32u cylinders, Bit32u heads, Bit32u sectors, Bit32u sector_size, bool hardDrive) {
00330     if (diskName) this->diskname = diskName;
00331     this->cylinders = cylinders;
00332     this->heads = heads;
00333     this->sectors = sectors;
00334     image_base = 0;
00335     this->image_length = (Bit64u)cylinders * heads * sectors * sector_size;
00336     refcount = 0;
00337     this->sector_size = sector_size;
00338     this->diskSizeK = this->image_length / 1024;
00339     reserved_cylinders = 0;
00340     this->diskimg = diskimg;
00341     class_id = ID_BASE;
00342     active = true;
00343     this->hardDrive = hardDrive;
00344 }
00345 
00346 /* .HDI header (NP2) */
00347 #pragma pack(push,1)
00348 typedef struct {
00349     uint8_t dummy[4];           // +0x00
00350     uint8_t hddtype[4];         // +0x04
00351     uint8_t headersize[4];      // +0x08
00352     uint8_t hddsize[4];         // +0x0C
00353     uint8_t sectorsize[4];      // +0x10
00354     uint8_t sectors[4];         // +0x14
00355     uint8_t surfaces[4];        // +0x18
00356     uint8_t cylinders[4];       // +0x1C
00357 } HDIHDR;                       // =0x20
00358 #pragma pack(pop)
00359 
00360 imageDisk::imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) {
00361     heads = 0;
00362     cylinders = 0;
00363     image_base = 0;
00364     image_length = imgSizeK * 1024;
00365     sectors = 0;
00366     refcount = 0;
00367     sector_size = 512;
00368     reserved_cylinders = 0;
00369     diskimg = imgFile;
00370     class_id = ID_BASE;
00371     diskSizeK = imgSizeK;
00372 
00373     if (imgName != NULL)
00374         diskname = (const char*)imgName;
00375 
00376     active = false;
00377     hardDrive = isHardDisk;
00378     if(!isHardDisk) {
00379         Bit8u i=0;
00380         bool founddisk = false;
00381 
00382         if (imgName != NULL) {
00383             char *ext = strrchr((char*)imgName,'.');
00384             if (ext != NULL) {
00385                 if (!strcasecmp(ext,".fdi")) {
00386                     if (imgSizeK >= 160) {
00387                         // PC-98 .FDI images appear to be 4096 bytes of unknown and mostly zeros,
00388                         // followed by a straight sector dump of the disk.
00389                         imgSizeK -= 4; // minus 4K
00390                         image_base += 4096; // +4K
00391                         image_length -= 4096; // -4K
00392                         LOG_MSG("Image file has .FDI extension, assuming 4K offset");
00393                     }
00394                 }
00395             }
00396         }
00397 
00398         while (DiskGeometryList[i].ksize!=0x0) {
00399             if ((DiskGeometryList[i].ksize==imgSizeK) ||
00400                 (DiskGeometryList[i].ksize+1==imgSizeK)) {
00401                 if (DiskGeometryList[i].ksize!=imgSizeK)
00402                     LOG_MSG("ImageLoader: image file with additional data, might not load!");
00403                 founddisk = true;
00404                 active = true;
00405                 floppytype = i;
00406                 heads = DiskGeometryList[i].headscyl;
00407                 cylinders = DiskGeometryList[i].cylcount;
00408                 sectors = DiskGeometryList[i].secttrack;
00409                 sector_size = DiskGeometryList[i].bytespersect;
00410                 LOG_MSG("Identified '%s' as C/H/S %u/%u/%u %u bytes/sector",
00411                     imgName,cylinders,heads,sectors,sector_size);
00412                 break;
00413             }
00414             i++;
00415         }
00416         if(!founddisk) {
00417             active = false;
00418         }
00419     }
00420     else { /* hard disk */
00421         if (imgName != NULL) {
00422             char *ext = strrchr((char*)imgName,'.');
00423             if (ext != NULL) {
00424                 if (!strcasecmp(ext,".hdi")) {
00425                     if (imgSizeK >= 160) {
00426                         HDIHDR hdihdr;
00427 
00428                         // PC-98 .HDI images appear to be 4096 bytes of a short header and many zeros.
00429                         // followed by a straight sector dump of the disk. The header is NOT NECESSARILY
00430                         // 4KB in size, but usually is.
00431                         LOG_MSG("Image file has .HDI extension, assuming HDI image and will take on parameters in header.");
00432 
00433                         assert(sizeof(hdihdr) == 0x20);
00434                         if (fseek(imgFile,0,SEEK_SET) == 0 && ftell(imgFile) == 0 &&
00435                             fread(&hdihdr,sizeof(hdihdr),1,imgFile) == 1) {
00436                             uint32_t ofs = host_readd(hdihdr.headersize);
00437                             uint32_t hddsize = host_readd(hdihdr.hddsize); /* includes header */
00438                             uint32_t sectorsize = host_readd(hdihdr.sectorsize);
00439 
00440                             if (sectorsize != 0 && ((sectorsize & (sectorsize - 1)) == 0/*is power of 2*/) &&
00441                                 sectorsize >= 256 && sectorsize <= 1024 &&
00442                                 ofs != 0 && (ofs % sectorsize) == 0/*offset is nonzero and multiple of sector size*/ &&
00443                                 (ofs % 1024) == 0/*offset is a multiple of 1024 because of imgSizeK*/ &&
00444                                 hddsize >= sectorsize && (hddsize/1024) <= (imgSizeK+4)) {
00445 
00446                                 sector_size = sectorsize;
00447                                 imgSizeK -= (ofs / 1024);
00448                                 image_base = ofs;
00449                                 image_length -= ofs;
00450                                 LOG_MSG("HDI header: sectorsize is %u bytes/sector, header is %u bytes, hdd size (plus header) is %u bytes",
00451                                     (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)hddsize);
00452 
00453                                 /* take on the geometry.
00454                                  * PC-98 IPL1 support will need it to make sense of the partition table. */
00455                                 sectors = host_readd(hdihdr.sectors);
00456                                 heads = host_readd(hdihdr.surfaces);
00457                                 cylinders = host_readd(hdihdr.cylinders);
00458                                 LOG_MSG("HDI: Geometry is C/H/S %u/%u/%u",
00459                                     (unsigned int)cylinders,(unsigned int)heads,(unsigned int)sectors);
00460                             }
00461                             else {
00462                                 LOG_MSG("HDI header rejected. sectorsize=%u headersize=%u hddsize=%u",
00463                                     (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)hddsize);
00464                             }
00465                         }
00466                         else {
00467                             LOG_MSG("Unable to read .HDI header");
00468                         }
00469                     }
00470                 }
00471             }
00472         }
00473 
00474         if (sectors == 0 || heads == 0 || cylinders == 0)
00475             active = false;
00476     }
00477 }
00478 
00479 void imageDisk::Set_Geometry(Bit32u setHeads, Bit32u setCyl, Bit32u setSect, Bit32u setSectSize) {
00480     Bitu bigdisk_shift = 0;
00481 
00482     if (IS_PC98_ARCH) {
00483         /* TODO: PC-98 has it's own 4096 cylinder limit */
00484     }
00485     else {
00486         if(setCyl > 16384) LOG_MSG("This disk image is too big.");
00487         else if(setCyl > 8192) bigdisk_shift = 4;
00488         else if(setCyl > 4096) bigdisk_shift = 3;
00489         else if(setCyl > 2048) bigdisk_shift = 2;
00490         else if(setCyl > 1024) bigdisk_shift = 1;
00491     }
00492 
00493     heads = setHeads << bigdisk_shift;
00494     cylinders = setCyl >> bigdisk_shift;
00495     sectors = setSect;
00496     sector_size = setSectSize;
00497     active = true;
00498 }
00499 
00500 void imageDisk::Get_Geometry(Bit32u * getHeads, Bit32u *getCyl, Bit32u *getSect, Bit32u *getSectSize) {
00501     *getHeads = heads;
00502     *getCyl = cylinders;
00503     *getSect = sectors;
00504     *getSectSize = sector_size;
00505 }
00506 
00507 Bit8u imageDisk::GetBiosType(void) {
00508     if(!hardDrive) {
00509         return (Bit8u)DiskGeometryList[floppytype].biosval;
00510     } else return 0;
00511 }
00512 
00513 Bit32u imageDisk::getSectSize(void) {
00514     return sector_size;
00515 }
00516 
00517 static Bitu GetDosDriveNumber(Bitu biosNum) {
00518     switch(biosNum) {
00519         case 0x0:
00520             return 0x0;
00521         case 0x1:
00522             return 0x1;
00523         case 0x80:
00524             return 0x2;
00525         case 0x81:
00526             return 0x3;
00527         case 0x82:
00528             return 0x4;
00529         case 0x83:
00530             return 0x5;
00531         default:
00532             return 0x7f;
00533     }
00534 }
00535 
00536 static bool driveInactive(Bitu driveNum) {
00537     if(driveNum>=(2 + MAX_HDD_IMAGES)) {
00538         LOG(LOG_BIOS,LOG_ERROR)("Disk %d non-existant", (int)driveNum);
00539         last_status = 0x01;
00540         CALLBACK_SCF(true);
00541         return true;
00542     }
00543     if(imageDiskList[driveNum] == NULL) {
00544         LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", (int)driveNum);
00545         last_status = 0x01;
00546         CALLBACK_SCF(true);
00547         return true;
00548     }
00549     if(!imageDiskList[driveNum]->active) {
00550         LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", (int)driveNum);
00551         last_status = 0x01;
00552         CALLBACK_SCF(true);
00553         return true;
00554     }
00555     return false;
00556 }
00557 
00558 static struct {
00559     Bit8u sz;
00560     Bit8u res;
00561     Bit16u num;
00562     Bit16u off;
00563     Bit16u seg;
00564     Bit32u sector;
00565 } dap;
00566 
00567 static void readDAP(Bit16u seg, Bit16u off) {
00568     dap.sz = real_readb(seg,off++);
00569     dap.res = real_readb(seg,off++);
00570     dap.num = real_readw(seg,off); off += 2;
00571     dap.off = real_readw(seg,off); off += 2;
00572     dap.seg = real_readw(seg,off); off += 2;
00573 
00574     /* Although sector size is 64-bit, 32-bit 2TB limit should be more than enough */
00575     dap.sector = real_readd(seg,off); off +=4;
00576 
00577     if (real_readd(seg,off)) {
00578         E_Exit("INT13: 64-bit sector addressing not supported");
00579     }
00580 }
00581 
00582 void IDE_ResetDiskByBIOS(unsigned char disk);
00583 void IDE_EmuINT13DiskReadByBIOS(unsigned char disk,unsigned int cyl,unsigned int head,unsigned sect);
00584 void IDE_EmuINT13DiskReadByBIOS_LBA(unsigned char disk,uint64_t lba);
00585 
00586 static Bitu INT13_DiskHandler(void) {
00587     Bit16u segat, bufptr;
00588     Bit8u sectbuf[512];
00589     Bitu  drivenum;
00590     Bitu  i,t;
00591     last_drive = reg_dl;
00592     drivenum = GetDosDriveNumber(reg_dl);
00593     bool any_images = false;
00594     for(i = 0;i < MAX_DISK_IMAGES;i++) {
00595         if(imageDiskList[i]) any_images=true;
00596     }
00597 
00598     // unconditionally enable the interrupt flag
00599     CALLBACK_SIF(true);
00600 
00601     /* map out functions 0x40-0x48 if not emulating INT 13h extensions */
00602     if (!int13_extensions_enable && reg_ah >= 0x40 && reg_ah <= 0x48) {
00603         LOG_MSG("Warning: Guest is attempting to use INT 13h extensions (AH=0x%02X). Set 'int 13 extensions=1' if you want to enable them.\n",reg_ah);
00604         reg_ah=0xff;
00605         CALLBACK_SCF(true);
00606         return CBRET_NONE;
00607     }
00608 
00609     //drivenum = 0;
00610     //LOG_MSG("INT13: Function %x called on drive %x (dos drive %d)", reg_ah,  reg_dl, drivenum);
00611     switch(reg_ah) {
00612     case 0x0: /* Reset disk */
00613         {
00614             /* if there aren't any diskimages (so only localdrives and virtual drives)
00615              * always succeed on reset disk. If there are diskimages then and only then
00616              * do real checks
00617              */
00618             if (any_images && driveInactive(drivenum)) {
00619                 /* driveInactive sets carry flag if the specified drive is not available */
00620                 if ((machine==MCH_CGA) || (machine==MCH_AMSTRAD) || (machine==MCH_PCJR)) {
00621                     /* those bioses call floppy drive reset for invalid drive values */
00622                     if (((imageDiskList[0]) && (imageDiskList[0]->active)) || ((imageDiskList[1]) && (imageDiskList[1]->active))) {
00623                         if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++;
00624                         last_status = 0x00;
00625                         CALLBACK_SCF(false);
00626                     }
00627                 }
00628                 return CBRET_NONE;
00629             }
00630             if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++;
00631             if (reg_dl >= 0x80) IDE_ResetDiskByBIOS(reg_dl);
00632             last_status = 0x00;
00633             CALLBACK_SCF(false);
00634         }
00635         break;
00636     case 0x1: /* Get status of last operation */
00637 
00638         if(last_status != 0x00) {
00639             reg_ah = last_status;
00640             CALLBACK_SCF(true);
00641         } else {
00642             reg_ah = 0x00;
00643             CALLBACK_SCF(false);
00644         }
00645         break;
00646     case 0x2: /* Read sectors */
00647         if (reg_al==0) {
00648             reg_ah = 0x01;
00649             CALLBACK_SCF(true);
00650             return CBRET_NONE;
00651         }
00652         if (!any_images) {
00653             // Inherit the Earth cdrom (uses it as disk test)
00654             if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) {
00655                 reg_ah = 0;
00656                 CALLBACK_SCF(false);
00657                 return CBRET_NONE;
00658             }
00659         }
00660         if (driveInactive(drivenum)) {
00661             reg_ah = 0xff;
00662             CALLBACK_SCF(true);
00663             return CBRET_NONE;
00664         }
00665 
00666         segat = SegValue(es);
00667         bufptr = reg_bx;
00668         for(i=0;i<reg_al;i++) {
00669             last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
00670 
00671             /* IDE emulation: simulate change of IDE state that would occur on a real machine after INT 13h */
00672             IDE_EmuINT13DiskReadByBIOS(reg_dl, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)reg_dh, (Bit32u)((reg_cl & 63)+i));
00673 
00674             if((last_status != 0x00) || (killRead)) {
00675                 LOG_MSG("Error in disk read");
00676                 killRead = false;
00677                 reg_ah = 0x04;
00678                 CALLBACK_SCF(true);
00679                 return CBRET_NONE;
00680             }
00681             for(t=0;t<512;t++) {
00682                 real_writeb(segat,bufptr,sectbuf[t]);
00683                 bufptr++;
00684             }
00685         }
00686         reg_ah = 0x00;
00687         CALLBACK_SCF(false);
00688         break;
00689     case 0x3: /* Write sectors */
00690         
00691         if(driveInactive(drivenum)) {
00692             reg_ah = 0xff;
00693             CALLBACK_SCF(true);
00694             return CBRET_NONE;
00695         }                     
00696 
00697 
00698         bufptr = reg_bx;
00699         for(i=0;i<reg_al;i++) {
00700             for(t=0;t<imageDiskList[drivenum]->getSectSize();t++) {
00701                 sectbuf[t] = real_readb(SegValue(es),bufptr);
00702                 bufptr++;
00703             }
00704 
00705             last_status = imageDiskList[drivenum]->Write_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0) << 2)), (Bit32u)((reg_cl & 63) + i), &sectbuf[0]);
00706             if(last_status != 0x00) {
00707             CALLBACK_SCF(true);
00708                 return CBRET_NONE;
00709             }
00710         }
00711         reg_ah = 0x00;
00712         CALLBACK_SCF(false);
00713         break;
00714     case 0x04: /* Verify sectors */
00715         if (reg_al==0) {
00716             reg_ah = 0x01;
00717             CALLBACK_SCF(true);
00718             return CBRET_NONE;
00719         }
00720         if(driveInactive(drivenum)) return CBRET_NONE;
00721 
00722         /* TODO: Finish coding this section */
00723         /*
00724         segat = SegValue(es);
00725         bufptr = reg_bx;
00726         for(i=0;i<reg_al;i++) {
00727             last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
00728             if(last_status != 0x00) {
00729                 LOG_MSG("Error in disk read");
00730                 CALLBACK_SCF(true);
00731                 return CBRET_NONE;
00732             }
00733             for(t=0;t<512;t++) {
00734                 real_writeb(segat,bufptr,sectbuf[t]);
00735                 bufptr++;
00736             }
00737         }*/
00738         reg_ah = 0x00;
00739         //Qbix: The following codes don't match my specs. al should be number of sector verified
00740         //reg_al = 0x10; /* CRC verify failed */
00741         //reg_al = 0x00; /* CRC verify succeeded */
00742         CALLBACK_SCF(false);
00743           
00744         break;
00745     case 0x05: /* Format track */
00746         /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */
00747         LOG_MSG("WARNING: Format track ignored\n");
00748         CALLBACK_SCF(false);
00749         reg_ah = 0x00;
00750         break;
00751     case 0x06: /* Format track set bad sector flags */
00752         /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */
00753         LOG_MSG("WARNING: Format track set bad sector flags ignored (6)\n");
00754         CALLBACK_SCF(false);
00755         reg_ah = 0x00;
00756         break;
00757     case 0x07: /* Format track set bad sector flags */
00758         /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */
00759         LOG_MSG("WARNING: Format track set bad sector flags ignored (7)\n");
00760         CALLBACK_SCF(false);
00761         reg_ah = 0x00;
00762         break;
00763     case 0x08: /* Get drive parameters */
00764         if(driveInactive(drivenum)) {
00765             last_status = 0x07;
00766             reg_ah = last_status;
00767             CALLBACK_SCF(true);
00768             return CBRET_NONE;
00769         }
00770         reg_ax = 0x00;
00771         reg_bl = imageDiskList[drivenum]->GetBiosType();
00772         Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
00773         imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
00774         if (tmpcyl==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: cylinder count zero!");
00775         else tmpcyl--;      // cylinder count -> max cylinder
00776         if (tmpheads==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: head count zero!");
00777         else tmpheads--;    // head count -> max head
00778 
00779         /* older BIOSes were known to subtract 1 or 2 additional "reserved" cylinders.
00780          * some code, such as Windows 3.1 WDCTRL, might assume that fact. emulate that here */
00781         {
00782             Bit32u reserv = imageDiskList[drivenum]->Get_Reserved_Cylinders();
00783             if (tmpcyl > reserv) tmpcyl -= reserv;
00784             else tmpcyl = 0;
00785         }
00786 
00787         reg_ch = (Bit8u)(tmpcyl & 0xff);
00788         reg_cl = (Bit8u)(((tmpcyl >> 2) & 0xc0) | (tmpsect & 0x3f)); 
00789         reg_dh = (Bit8u)tmpheads;
00790         last_status = 0x00;
00791         if (reg_dl&0x80) {  // harddisks
00792             reg_dl = 0;
00793             for (int index = 2; index < MAX_DISK_IMAGES; index++) {
00794                 if (imageDiskList[index] != NULL) reg_dl++;
00795             }
00796         } else {        // floppy disks
00797             reg_dl = 0;
00798             if(imageDiskList[0] != NULL) reg_dl++;
00799             if(imageDiskList[1] != NULL) reg_dl++;
00800         }
00801         CALLBACK_SCF(false);
00802         break;
00803     case 0x11: /* Recalibrate drive */
00804         reg_ah = 0x00;
00805         CALLBACK_SCF(false);
00806         break;
00807     case 0x17: /* Set disk type for format */
00808         /* Pirates! needs this to load */
00809         killRead = true;
00810         reg_ah = 0x00;
00811         CALLBACK_SCF(false);
00812         break;
00813     case 0x41: /* Check Extensions Present */
00814         if ((reg_bx == 0x55aa) && !(driveInactive(drivenum))) {
00815             LOG_MSG("INT13: Check Extensions Present for drive: 0x%x", reg_dl);
00816             reg_ah=0x1; /* 1.x extension supported */
00817             reg_bx=0xaa55;  /* Extensions installed */
00818             reg_cx=0x1; /* Extended disk access functions (AH=42h-44h,47h,48h) supported */
00819             CALLBACK_SCF(false);
00820             break;
00821         }
00822         LOG_MSG("INT13: AH=41h, Function not supported 0x%x for drive: 0x%x", reg_bx, reg_dl);
00823         CALLBACK_SCF(true);
00824         break;
00825     case 0x42: /* Extended Read Sectors From Drive */
00826         /* Read Disk Address Packet */
00827         readDAP(SegValue(ds),reg_si);
00828 
00829         if (dap.num==0) {
00830             reg_ah = 0x01;
00831             CALLBACK_SCF(true);
00832             return CBRET_NONE;
00833         }
00834         if (!any_images) {
00835             // Inherit the Earth cdrom (uses it as disk test)
00836             if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) {
00837                 reg_ah = 0;
00838                 CALLBACK_SCF(false);
00839                 return CBRET_NONE;
00840             }
00841         }
00842         if (driveInactive(drivenum)) {
00843             reg_ah = 0xff;
00844             CALLBACK_SCF(true);
00845             return CBRET_NONE;
00846         }
00847 
00848         segat = dap.seg;
00849         bufptr = dap.off;
00850         for(i=0;i<dap.num;i++) {
00851             last_status = imageDiskList[drivenum]->Read_AbsoluteSector(dap.sector+i, sectbuf);
00852 
00853             IDE_EmuINT13DiskReadByBIOS_LBA(reg_dl,dap.sector+i);
00854 
00855             if((last_status != 0x00) || (killRead)) {
00856                 LOG_MSG("Error in disk read");
00857                 killRead = false;
00858                 reg_ah = 0x04;
00859                 CALLBACK_SCF(true);
00860                 return CBRET_NONE;
00861             }
00862             for(t=0;t<512;t++) {
00863                 real_writeb(segat,bufptr,sectbuf[t]);
00864                 bufptr++;
00865             }
00866         }
00867         reg_ah = 0x00;
00868         CALLBACK_SCF(false);
00869         break;
00870     case 0x43: /* Extended Write Sectors to Drive */
00871         if(driveInactive(drivenum)) {
00872             reg_ah = 0xff;
00873             CALLBACK_SCF(true);
00874             return CBRET_NONE;
00875         }
00876 
00877         /* Read Disk Address Packet */
00878         readDAP(SegValue(ds),reg_si);
00879         bufptr = dap.off;
00880         for(i=0;i<dap.num;i++) {
00881             for(t=0;t<imageDiskList[drivenum]->getSectSize();t++) {
00882                 sectbuf[t] = real_readb(dap.seg,bufptr);
00883                 bufptr++;
00884             }
00885 
00886             last_status = imageDiskList[drivenum]->Write_AbsoluteSector(dap.sector+i, &sectbuf[0]);
00887             if(last_status != 0x00) {
00888                 CALLBACK_SCF(true);
00889                 return CBRET_NONE;
00890             }
00891         }
00892         reg_ah = 0x00;
00893         CALLBACK_SCF(false);
00894         break;
00895     case 0x48: { /* get drive parameters */
00896         uint16_t bufsz;
00897 
00898         if(driveInactive(drivenum)) {
00899             reg_ah = 0xff;
00900             CALLBACK_SCF(true);
00901             return CBRET_NONE;
00902         }
00903 
00904         segat = SegValue(ds);
00905         bufptr = reg_si;
00906         bufsz = real_readw(segat,bufptr+0);
00907         if (bufsz < 0x1A) {
00908             reg_ah = 0xff;
00909             CALLBACK_SCF(true);
00910             return CBRET_NONE;
00911         }
00912         if (bufsz > 0x1E) bufsz = 0x1E;
00913         else bufsz = 0x1A;
00914 
00915         Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
00916         imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
00917 
00918         real_writew(segat,bufptr+0x00,bufsz);
00919         real_writew(segat,bufptr+0x02,0x0003);  /* C/H/S valid, DMA boundary errors handled */
00920         real_writed(segat,bufptr+0x04,tmpcyl);
00921         real_writed(segat,bufptr+0x08,tmpheads);
00922         real_writed(segat,bufptr+0x0C,tmpsect);
00923         real_writed(segat,bufptr+0x10,tmpcyl*tmpheads*tmpsect);
00924         real_writed(segat,bufptr+0x14,0);
00925         real_writew(segat,bufptr+0x18,512);
00926         if (bufsz >= 0x1E)
00927             real_writed(segat,bufptr+0x1A,0xFFFFFFFF); /* no EDD information available */
00928 
00929         reg_ah = 0x00;
00930         CALLBACK_SCF(false);
00931         } break;
00932     default:
00933         LOG(LOG_BIOS,LOG_ERROR)("INT13: Function %x called on drive %x (dos drive %d)", (int)reg_ah, (int)reg_dl, (int)drivenum);
00934         reg_ah=0xff;
00935         CALLBACK_SCF(true);
00936     }
00937     return CBRET_NONE;
00938 }
00939 
00940 void CALLBACK_DeAllocate(Bitu in);
00941 
00942 void BIOS_UnsetupDisks(void) {
00943     if (call_int13 != 0) {
00944         CALLBACK_DeAllocate(call_int13);
00945         RealSetVec(0x13,0); /* zero INT 13h for now */
00946         call_int13 = 0;
00947     }
00948     if (diskparm0 != 0) {
00949         CALLBACK_DeAllocate(diskparm0);
00950         diskparm0 = 0;
00951     }
00952     if (diskparm1 != 0) {
00953         CALLBACK_DeAllocate(diskparm1);
00954         diskparm1 = 0;
00955     }
00956 }
00957 
00958 void BIOS_SetupDisks(void) {
00959     unsigned int i;
00960 
00961     if (IS_PC98_ARCH) {
00962         // TODO
00963         return;
00964     }
00965 
00966 /* TODO Start the time correctly */
00967     call_int13=CALLBACK_Allocate(); 
00968     CALLBACK_Setup(call_int13,&INT13_DiskHandler,CB_INT13,"Int 13 Bios disk");
00969     RealSetVec(0x13,CALLBACK_RealPointer(call_int13));
00970 
00971     //release the drives after a soft reset
00972     FreeBIOSDiskList();
00973 
00974     /* FIXME: Um... these aren't callbacks. Why are they allocated as callbacks? We have ROM general allocation now. */
00975     diskparm0 = CALLBACK_Allocate();
00976     CALLBACK_SetDescription(diskparm0,"BIOS Disk 0 parameter table");
00977     diskparm1 = CALLBACK_Allocate();
00978     CALLBACK_SetDescription(diskparm1,"BIOS Disk 1 parameter table");
00979     swapPosition = 0;
00980 
00981     RealSetVec(0x41,CALLBACK_RealPointer(diskparm0));
00982     RealSetVec(0x46,CALLBACK_RealPointer(diskparm1));
00983 
00984     PhysPt dp0physaddr=CALLBACK_PhysPointer(diskparm0);
00985     PhysPt dp1physaddr=CALLBACK_PhysPointer(diskparm1);
00986     for(i=0;i<16;i++) {
00987         phys_writeb(dp0physaddr+i,0);
00988         phys_writeb(dp1physaddr+i,0);
00989     }
00990 
00991     imgDTASeg = 0;
00992 
00993 /* Setup the Bios Area */
00994     mem_writeb(BIOS_HARDDISK_COUNT,2);
00995 
00996     killRead = false;
00997     swapping_requested = false;
00998 }
00999 
01000 // VFD *.FDD floppy disk format support
01001 
01002 Bit8u imageDiskVFD::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) {
01003     vfdentry *ent;
01004 
01005     if (req_sector_size == 0)
01006         req_sector_size = sector_size;
01007 
01008 //    LOG_MSG("VFD read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size);
01009 
01010     ent = findSector(head,cylinder,sector,req_sector_size);
01011     if (ent == NULL) return 0x05;
01012     if (ent->getSectorSize() != req_sector_size) return 0x05;
01013 
01014     if (ent->hasSectorData()) {
01015         fseek(diskimg,ent->data_offset,SEEK_SET);
01016         if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01017         if (fread(data,req_sector_size,1,diskimg) != 1) return 0x05;
01018         return 0;
01019     }
01020     else if (ent->hasFill()) {
01021         memset(data,ent->fillbyte,req_sector_size);
01022         return 0x00;
01023     }
01024 
01025     return 0x05;
01026 }
01027 
01028 Bit8u imageDiskVFD::Read_AbsoluteSector(Bit32u sectnum, void * data) {
01029     unsigned int c,h,s;
01030 
01031     if (sectors == 0 || heads == 0)
01032         return 0x05;
01033 
01034     s = (sectnum % sectors) + 1;
01035     h = (sectnum / sectors) % heads;
01036     c = (sectnum / sectors / heads);
01037     return Read_Sector(h,c,s,data);
01038 }
01039 
01040 imageDiskVFD::vfdentry *imageDiskVFD::findSector(Bit8u head,Bit8u track,Bit8u sector/*TODO: physical head?*/,unsigned int req_sector_size) {
01041     std::vector<imageDiskVFD::vfdentry>::iterator i = dents.begin();
01042     unsigned char szb=0xFF;
01043 
01044     if (req_sector_size == 0)
01045         req_sector_size = sector_size;
01046 
01047     if (req_sector_size != ~0U) {
01048         unsigned int c = req_sector_size;
01049         while (c >= 128U) {
01050             c >>= 1U;
01051             szb++;
01052         }
01053 
01054 //        LOG_MSG("req=%u c=%u szb=%u",req_sector_size,c,szb);
01055 
01056         if (szb > 8 || c != 64U)
01057             return NULL;
01058     }
01059 
01060     while (i != dents.end()) {
01061         imageDiskVFD::vfdentry &ent = *i;
01062 
01063         if (ent.head == head &&
01064             ent.track == track &&
01065             ent.sector == sector &&
01066             (ent.sizebyte == szb || req_sector_size == ~0U))
01067             return &(*i);
01068 
01069         i++;
01070     }
01071 
01072     return NULL;
01073 }
01074 
01075 Bit8u imageDiskVFD::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) {
01076     unsigned long new_offset;
01077     unsigned char tmp[12];
01078     vfdentry *ent;
01079 
01080 //    LOG_MSG("VFD write sector: CHS %u/%u/%u",cylinder,head,sector);
01081 
01082     if (req_sector_size == 0)
01083         req_sector_size = sector_size;
01084 
01085     ent = findSector(head,cylinder,sector,req_sector_size);
01086     if (ent == NULL) return 0x05;
01087     if (ent->getSectorSize() != req_sector_size) return 0x05;
01088 
01089     if (ent->hasSectorData()) {
01090         fseek(diskimg,ent->data_offset,SEEK_SET);
01091         if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01092         if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05;
01093         return 0;
01094     }
01095     else if (ent->hasFill()) {
01096         bool isfill = false;
01097 
01098         /* well, is the data provided one character repeated?
01099          * note the format cannot represent a fill byte of 0xFF */
01100         if (((unsigned char*)data)[0] != 0xFF) {
01101             unsigned int i=1;
01102 
01103             do {
01104                 if (((unsigned char*)data)[i] == ((unsigned char*)data)[0]) {
01105                     if ((++i) == req_sector_size) {
01106                         isfill = true;
01107                         break; // yes!
01108                     }
01109                 }
01110                 else {
01111                     break; // nope
01112                 }
01113             } while (1);
01114         }
01115 
01116         if (ent->entry_offset == 0) return 0x05;
01117 
01118         if (isfill) {
01119             fseek(diskimg,ent->entry_offset,SEEK_SET);
01120             if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05;
01121             if (fread(tmp,12,1,diskimg) != 1) return 0x05;
01122 
01123             tmp[0x04] = ((unsigned char*)data)[0]; // change the fill byte
01124 
01125             LOG_MSG("VFD write: 'fill' sector changing fill byte to 0x%x",tmp[0x04]);
01126 
01127             fseek(diskimg,ent->entry_offset,SEEK_SET);
01128             if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05;
01129             if (fwrite(tmp,12,1,diskimg) != 1) return 0x05;
01130         }
01131         else {
01132             fseek(diskimg,0,SEEK_END);
01133             new_offset = (unsigned long)ftell(diskimg);
01134 
01135             /* we have to change it from a fill sector to an actual sector */
01136             LOG_MSG("VFD write: changing 'fill' sector to one with data (data at %lu)",(unsigned long)new_offset);
01137 
01138             fseek(diskimg,ent->entry_offset,SEEK_SET);
01139             if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05;
01140             if (fread(tmp,12,1,diskimg) != 1) return 0x05;
01141 
01142             tmp[0x00] = ent->track;
01143             tmp[0x01] = ent->head;
01144             tmp[0x02] = ent->sector;
01145             tmp[0x03] = ent->sizebyte;
01146             tmp[0x04] = 0xFF; // no longer a fill byte
01147             tmp[0x05] = 0x00; // TODO ??
01148             tmp[0x06] = 0x00; // TODO ??
01149             tmp[0x07] = 0x00; // TODO ??
01150             *((uint32_t*)(tmp+8)) = new_offset;
01151             ent->fillbyte = 0xFF;
01152             ent->data_offset = (uint32_t)new_offset;
01153 
01154             fseek(diskimg,ent->entry_offset,SEEK_SET);
01155             if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05;
01156             if (fwrite(tmp,12,1,diskimg) != 1) return 0x05;
01157 
01158             fseek(diskimg,ent->data_offset,SEEK_SET);
01159             if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01160             if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05;
01161         }
01162     }
01163 
01164     return 0x05;
01165 }
01166 
01167 Bit8u imageDiskVFD::Write_AbsoluteSector(Bit32u sectnum,const void *data) {
01168     unsigned int c,h,s;
01169 
01170     if (sectors == 0 || heads == 0)
01171         return 0x05;
01172 
01173     s = (sectnum % sectors) + 1;
01174     h = (sectnum / sectors) % heads;
01175     c = (sectnum / sectors / heads);
01176     return Write_Sector(h,c,s,data);
01177 }
01178 
01179 imageDiskVFD::imageDiskVFD(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) : imageDisk(ID_VFD) {
01180     (void)isHardDisk;//UNUSED
01181     unsigned char tmp[16];
01182 
01183     heads = 1;
01184     cylinders = 0;
01185     image_base = 0;
01186     sectors = 0;
01187     active = false;
01188     sector_size = 0;
01189     reserved_cylinders = 0;
01190     diskSizeK = imgSizeK;
01191     diskimg = imgFile;
01192 
01193     if (imgName != NULL)
01194         diskname = (const char*)imgName;
01195 
01196     // NOTES:
01197     // 
01198     //  +0x000: "VFD1.00"
01199     //  +0x0DC: array of 12-byte entries each describing a sector
01200     //
01201     //  Each entry:
01202     //  +0x0: track
01203     //  +0x1: head
01204     //  +0x2: sector
01205     //  +0x3: sector size (128 << this byte)
01206     //  +0x4: fill byte, or 0xFF
01207     //  +0x5: unknown
01208     //  +0x6: unknown
01209     //  +0x7: unknown
01210     //  +0x8: absolute data offset (32-bit integer) or 0xFFFFFFFF if the entire sector is that fill byte
01211     fseek(diskimg,0,SEEK_SET);
01212     memset(tmp,0,8);
01213     fread(tmp,1,8,diskimg);
01214 
01215     if (!memcmp(tmp,"VFD1.",5)) {
01216         Bit8u i=0;
01217         bool founddisk = false;
01218         uint32_t stop_at = 0xC3FC;
01219         unsigned long entof;
01220 
01221         // load table.
01222         // we have to determine as we go where to stop reading.
01223         // the source of info I read assumes the whole header (and table)
01224         // is 0xC3FC bytes. I'm not inclined to assume that, so we go by
01225         // that OR the first sector offset whichever is smaller.
01226         // the table seems to trail off into a long series of 0xFF at the end.
01227         fseek(diskimg,0xDC,SEEK_SET);
01228         while ((entof=((unsigned long)ftell(diskimg)+12ul)) <= stop_at) {
01229             memset(tmp,0xFF,12);
01230             fread(tmp,12,1,diskimg);
01231 
01232             if (!memcmp(tmp,"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",12))
01233                 continue;
01234             if (!memcmp(tmp,"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",12))
01235                 continue;
01236 
01237             struct vfdentry v;
01238 
01239             v.track = tmp[0];
01240             v.head = tmp[1];
01241             v.sector = tmp[2];
01242             v.sizebyte = tmp[3];
01243             v.fillbyte = tmp[4];
01244             v.data_offset = *((uint32_t*)(tmp+8));
01245             v.entry_offset = (uint32_t)entof;
01246 
01247             // maybe the table can end sooner than 0xC3FC?
01248             // if we see sectors appear at an offset lower than our stop_at point
01249             // then adjust the stop_at point. assume the table cannot mix with
01250             // sector data.
01251             if (v.hasSectorData()) {
01252                 if (stop_at > v.data_offset)
01253                     stop_at = v.data_offset;
01254             }
01255 
01256             dents.push_back(v);
01257 
01258             LOG_MSG("VFD entry: track=%u head=%u sector=%u size=%u fill=0x%2X has_data=%u has_fill=%u entoff=%lu dataoff=%lu",
01259                 v.track,
01260                 v.head,
01261                 v.sector,
01262                 v.getSectorSize(),
01263                 v.fillbyte,
01264                 v.hasSectorData(),
01265                 v.hasFill(),
01266                 (unsigned long)v.entry_offset,
01267                 (unsigned long)v.data_offset);
01268         }
01269 
01270         if (!dents.empty()) {
01271             /* okay, now to figure out what the geometry of the disk is.
01272              * we cannot just work from an "absolute" disk image model
01273              * because there's no VFD header to just say what the geometry is.
01274              * Like the IBM PC BIOS, we have to look at the disk and figure out
01275              * which geometry to apply to it, even if the FDD format allows
01276              * sectors on other tracks to have wild out of range sector, track,
01277              * and head numbers or odd sized sectors.
01278              *
01279              * First, determine sector size according to the boot sector. */
01280             vfdentry *ent;
01281 
01282             ent = findSector(/*head*/0,/*track*/0,/*sector*/1,~0U);
01283             if (ent != NULL) {
01284                 if (ent->sizebyte <= 3) /* x <= 1024 */
01285                     sector_size = ent->getSectorSize();
01286             }
01287 
01288             /* oh yeah right, sure.
01289              * I suppose you're one of those FDD images where the sector size is 128 bytes/sector
01290              * in the boot sector and the rest is 256 bytes/sector elsewhere. I have no idea why
01291              * but quite a few FDD images have this arrangement. */
01292             if (sector_size != 0 && sector_size < 512) {
01293                 ent = findSector(/*head*/0,/*track*/1,/*sector*/1,~0U);
01294                 if (ent != NULL) {
01295                     if (ent->sizebyte <= 3) { /* x <= 1024 */
01296                         unsigned int nsz = ent->getSectorSize();
01297                         if (sector_size != nsz)
01298                             LOG_MSG("VFD warning: sector size changes between track 0 and 1");
01299                         if (sector_size < nsz)
01300                             sector_size = nsz;
01301                     }
01302                 }
01303             }
01304 
01305             if (sector_size != 0) {
01306                 i=0;
01307                 while (DiskGeometryList[i].ksize != 0) {
01308                     diskGeo &diskent = DiskGeometryList[i];
01309 
01310                     if (diskent.bytespersect == sector_size) {
01311                         ent = findSector(0,0,diskent.secttrack);
01312                         if (ent != NULL) {
01313                             LOG_MSG("VFD disk probe: %u/%u/%u exists",0,0,diskent.secttrack);
01314                             if (sectors < diskent.secttrack)
01315                                 sectors = diskent.secttrack;
01316                         }
01317                     }
01318 
01319                     i++;
01320                 }
01321             }
01322 
01323             if (sector_size != 0 && sectors != 0) {
01324                 i=0;
01325                 while (DiskGeometryList[i].ksize != 0) {
01326                     diskGeo &diskent = DiskGeometryList[i];
01327 
01328                     if (diskent.bytespersect == sector_size && diskent.secttrack >= sectors) {
01329                         ent = findSector(0,diskent.cylcount-1,sectors);
01330                         if (ent != NULL) {
01331                             LOG_MSG("VFD disk probe: %u/%u/%u exists",0,diskent.cylcount-1,sectors);
01332                             if (cylinders < diskent.cylcount)
01333                                 cylinders = diskent.cylcount;
01334                         }
01335                     }
01336 
01337                     i++;
01338                 }
01339             }
01340 
01341             if (sector_size != 0 && sectors != 0 && cylinders != 0) {
01342                 ent = findSector(1,0,sectors);
01343                 if (ent != NULL) {
01344                     LOG_MSG("VFD disk probe: %u/%u/%u exists",1,0,sectors);
01345                     heads = 2;
01346                 }
01347             }
01348 
01349             // TODO: drive_fat.cpp should use an extension to this API to allow changing the sectors/track
01350             //       according to what it reads from the MS-DOS BIOS parameter block, just like real MS-DOS.
01351             //       This would allow better representation of strange disk formats such as the "extended"
01352             //       floppy format that Microsoft used to use for Word 95 and Windows 95 install floppies.
01353 
01354             LOG_MSG("VFD geometry detection: C/H/S %u/%u/%u %u bytes/sector",
01355                 cylinders, heads, sectors, sector_size);
01356 
01357             if (sector_size != 0 && sectors != 0 && cylinders != 0 && heads != 0)
01358                 founddisk = true;
01359 
01360             if(!founddisk) {
01361                 active = false;
01362             } else {
01363                 incrementFDD();
01364             }
01365         }
01366     }
01367 }
01368 
01369 imageDiskVFD::~imageDiskVFD() {
01370     if(diskimg != NULL) {
01371         fclose(diskimg);
01372         diskimg=NULL; 
01373     }
01374 }
01375 
01376 // D88 *.D88 floppy disk format support
01377 
01378 enum {
01379     D88_TRACKMAX        = 164,
01380     D88_HEADERSIZE      = 0x20 + (D88_TRACKMAX * 4)
01381 };
01382 
01383 #pragma pack(push,1)
01384 typedef struct D88HEAD {
01385     char            fd_name[17];                // +0x00 Disk Name
01386     unsigned char   reserved1[9];               // +0x11 Reserved
01387     unsigned char   protect;                    // +0x1A Write Protect bit:4
01388     unsigned char   fd_type;                    // +0x1B Disk Format
01389     uint32_t        fd_size;                    // +0x1C Disk Size
01390     uint32_t        trackp[D88_TRACKMAX];       // +0x20 <array of DWORDs>     164 x 4 = 656 = 0x290
01391 } D88HEAD;                                      // =0x2B0 total
01392 
01393 typedef struct D88SEC {
01394     unsigned char   c;                          // +0x00
01395     unsigned char   h;                          // +0x01
01396     unsigned char   r;                          // +0x02
01397     unsigned char   n;                          // +0x03
01398     uint16_t        sectors;                    // +0x04 Sector Count
01399     unsigned char   mfm_flg;                    // +0x06 sides
01400     unsigned char   del_flg;                    // +0x07 DELETED DATA
01401     unsigned char   stat;                       // +0x08 STATUS (FDC ret)
01402     unsigned char   seektime;                   // +0x09 Seek Time
01403     unsigned char   reserved[3];                // +0x0A Reserved
01404     unsigned char   rpm_flg;                    // +0x0D rpm          0:1.2  1:1.44
01405     uint16_t        size;                       // +0x0E Sector Size
01406                                                 // <sector contents follow>
01407 } D88SEC;                                       // =0x10 total
01408 #pragma pack(pop)
01409 
01410 Bit8u imageDiskD88::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) {
01411     vfdentry *ent;
01412 
01413     if (req_sector_size == 0)
01414         req_sector_size = sector_size;
01415 
01416 //    LOG_MSG("D88 read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size);
01417 
01418     ent = findSector(head,cylinder,sector,req_sector_size);
01419     if (ent == NULL) return 0x05;
01420     if (ent->getSectorSize() != req_sector_size) return 0x05;
01421 
01422     fseek(diskimg,ent->data_offset,SEEK_SET);
01423     if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01424     if (fread(data,req_sector_size,1,diskimg) != 1) return 0x05;
01425     return 0;
01426 }
01427 
01428 Bit8u imageDiskD88::Read_AbsoluteSector(Bit32u sectnum, void * data) {
01429     unsigned int c,h,s;
01430 
01431     if (sectors == 0 || heads == 0)
01432         return 0x05;
01433 
01434     s = (sectnum % sectors) + 1;
01435     h = (sectnum / sectors) % heads;
01436     c = (sectnum / sectors / heads);
01437     return Read_Sector(h,c,s,data);
01438 }
01439 
01440 imageDiskD88::vfdentry *imageDiskD88::findSector(Bit8u head,Bit8u track,Bit8u sector/*TODO: physical head?*/,unsigned int req_sector_size) {
01441     if ((size_t)track >= dents.size())
01442         return NULL;
01443 
01444     std::vector<imageDiskD88::vfdentry>::iterator i = dents.begin();
01445 
01446     if (req_sector_size == 0)
01447         req_sector_size = sector_size;
01448 
01449     while (i != dents.end()) {
01450         imageDiskD88::vfdentry &ent = *i;
01451 
01452         if (ent.head == head &&
01453             ent.track == track &&
01454             ent.sector == sector &&
01455             (ent.sector_size == req_sector_size || req_sector_size == ~0U))
01456             return &(*i);
01457 
01458         i++;
01459     }
01460 
01461     return NULL;
01462 }
01463 
01464 Bit8u imageDiskD88::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) {
01465     vfdentry *ent;
01466 
01467     if (req_sector_size == 0)
01468         req_sector_size = sector_size;
01469 
01470 //    LOG_MSG("D88 read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size);
01471 
01472     ent = findSector(head,cylinder,sector,req_sector_size);
01473     if (ent == NULL) return 0x05;
01474     if (ent->getSectorSize() != req_sector_size) return 0x05;
01475 
01476     fseek(diskimg,ent->data_offset,SEEK_SET);
01477     if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01478     if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05;
01479     return 0;
01480 }
01481 
01482 Bit8u imageDiskD88::Write_AbsoluteSector(Bit32u sectnum,const void *data) {
01483     unsigned int c,h,s;
01484 
01485     if (sectors == 0 || heads == 0)
01486         return 0x05;
01487 
01488     s = (sectnum % sectors) + 1;
01489     h = (sectnum / sectors) % heads;
01490     c = (sectnum / sectors / heads);
01491     return Write_Sector(h,c,s,data);
01492 }
01493 
01494 imageDiskD88::imageDiskD88(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) : imageDisk(ID_D88) {
01495     (void)isHardDisk;//UNUSED
01496     D88HEAD head;
01497 
01498     fd_type_major = DISKTYPE_2D;
01499     fd_type_minor = 0;
01500 
01501     assert(sizeof(D88HEAD) == 0x2B0);
01502     assert(sizeof(D88SEC) == 0x10);
01503 
01504     heads = 0;
01505     cylinders = 0;
01506     image_base = 0;
01507     sectors = 0;
01508     active = false;
01509     sector_size = 0;
01510     reserved_cylinders = 0;
01511     diskSizeK = imgSizeK;
01512     diskimg = imgFile;
01513     active = false;
01514 
01515     if (imgName != NULL)
01516         diskname = (const char*)imgName;
01517 
01518     // NOTES:
01519     // 
01520     //  +0x000: D88 header
01521     //  +0x020: Offset of D88 tracks, per track
01522     //  +0x2B0: <begin data>
01523     //
01524     // Track offsets are sequential, always
01525     //
01526     // Each track is an array of:
01527     //
01528     //  ENTRY:
01529     //   <D88 sector head>
01530     //   <sector contents>
01531     //
01532     // Array of ENTRY from offset until next track
01533     fseek(diskimg,0,SEEK_END);
01534     off_t fsz = ftell(diskimg);
01535 
01536     fseek(diskimg,0,SEEK_SET);
01537     if (fread(&head,sizeof(head),1,diskimg) != 1) return;
01538 
01539     // validate fd_size
01540     if ((uint32_t)host_readd((ConstHostPt)(&head.fd_size)) > (uint32_t)fsz) return;
01541 
01542     fd_type_major = head.fd_type >> 4U;
01543     fd_type_minor = head.fd_type & 0xFU;
01544 
01545     // validate that none of the track offsets extend past the file
01546     {
01547         for (unsigned int i=0;i < D88_TRACKMAX;i++) {
01548             uint32_t trackoff = host_readd((ConstHostPt)(&head.trackp[i]));
01549 
01550             if (trackoff == 0) continue;
01551 
01552             if ((trackoff + 16U) > (uint32_t)fsz) {
01553                 LOG_MSG("D88: track starts past end of file");
01554                 return;
01555             }
01556         }
01557     }
01558 
01559     // read each track
01560     for (unsigned int track=0;track < D88_TRACKMAX;track++) {
01561         uint32_t trackoff = host_readd((ConstHostPt)(&head.trackp[track]));
01562 
01563         if (trackoff != 0) {
01564             fseek(diskimg, trackoff, SEEK_SET);
01565             if (ftell(diskimg) != trackoff) continue;
01566 
01567             D88SEC s;
01568             unsigned int count = 0;
01569 
01570             do {
01571                 if (fread(&s,sizeof(s),1,diskimg) != 1) break;
01572 
01573                 uint16_t sector_count = host_readw((ConstHostPt)(&s.sectors));
01574                 uint16_t sector_size = host_readw((ConstHostPt)(&s.size));
01575 
01576                 if (sector_count == 0U || sector_size < 128U) break;
01577                 if (sector_count > 128U || sector_size > 16384U) break;
01578                 if (s.n > 8U) s.n = 8U;
01579 
01580                 vfdentry vent;
01581                 vent.sector_size = 128 << s.n;
01582                 vent.data_offset = (uint32_t)ftell(diskimg);
01583                 vent.entry_offset = vent.data_offset - (uint32_t)16;
01584                 vent.track = s.c;
01585                 vent.head = s.h;
01586                 vent.sector = s.r;
01587 
01588                 LOG_MSG("D88: trackindex=%u C/H/S/sz=%u/%u/%u/%u",
01589                     track,vent.track,vent.head,vent.sector,vent.sector_size);
01590 
01591                 dents.push_back(vent);
01592                 if ((++count) >= sector_count) break;
01593 
01594                 fseek(diskimg, sector_size, SEEK_CUR);
01595             } while (1);
01596         }
01597     }
01598 
01599     if (!dents.empty()) {
01600         /* okay, now to figure out what the geometry of the disk is.
01601          * we cannot just work from an "absolute" disk image model
01602          * because there's no D88 header to just say what the geometry is.
01603          * Like the IBM PC BIOS, we have to look at the disk and figure out
01604          * which geometry to apply to it, even if the FDD format allows
01605          * sectors on other tracks to have wild out of range sector, track,
01606          * and head numbers or odd sized sectors.
01607          *
01608          * First, determine sector size according to the boot sector. */
01609         bool founddisk = false;
01610         vfdentry *ent;
01611 
01612         ent = findSector(/*head*/0,/*track*/0,/*sector*/1,~0U);
01613         if (ent != NULL) {
01614             if (ent->getSectorSize() <= 1024) /* x <= 1024 */
01615                 sector_size = ent->getSectorSize();
01616         }
01617 
01618         /* oh yeah right, sure.
01619          * I suppose you're one of those FDD images where the sector size is 128 bytes/sector
01620          * in the boot sector and the rest is 256 bytes/sector elsewhere. I have no idea why
01621          * but quite a few FDD images have this arrangement. */
01622         if (sector_size != 0 && sector_size < 512) {
01623             ent = findSector(/*head*/0,/*track*/1,/*sector*/1,~0U);
01624             if (ent != NULL) {
01625                 if (ent->getSectorSize() <= 1024) { /* x <= 1024 */
01626                     unsigned int nsz = ent->getSectorSize();
01627                     if (sector_size != nsz)
01628                         LOG_MSG("D88 warning: sector size changes between track 0 and 1");
01629                     if (sector_size < nsz)
01630                         sector_size = nsz;
01631                 }
01632             }
01633         }
01634 
01635         if (sector_size != 0) {
01636             unsigned int i = 0;
01637             while (DiskGeometryList[i].ksize != 0) {
01638                 diskGeo &diskent = DiskGeometryList[i];
01639 
01640                 if (diskent.bytespersect == sector_size) {
01641                     ent = findSector(0,0,diskent.secttrack);
01642                     if (ent != NULL) {
01643                         LOG_MSG("D88 disk probe: %u/%u/%u exists",0,0,diskent.secttrack);
01644                         if (sectors < diskent.secttrack)
01645                             sectors = diskent.secttrack;
01646                     }
01647                 }
01648 
01649                 i++;
01650             }
01651         }
01652 
01653         if (sector_size != 0 && sectors != 0) {
01654             unsigned int i = 0;
01655             while (DiskGeometryList[i].ksize != 0) {
01656                 diskGeo &diskent = DiskGeometryList[i];
01657 
01658                 if (diskent.bytespersect == sector_size && diskent.secttrack >= sectors) {
01659                     ent = findSector(0,diskent.cylcount-1,sectors);
01660                     if (ent != NULL) {
01661                         LOG_MSG("D88 disk probe: %u/%u/%u exists",0,diskent.cylcount-1,sectors);
01662                         if (cylinders < diskent.cylcount)
01663                             cylinders = diskent.cylcount;
01664                     }
01665                 }
01666 
01667                 i++;
01668             }
01669         }
01670 
01671         if (sector_size != 0 && sectors != 0 && cylinders != 0) {
01672             ent = findSector(1,0,sectors);
01673             if (ent != NULL) {
01674                 LOG_MSG("D88 disk probe: %u/%u/%u exists",1,0,sectors);
01675                 heads = 2;
01676             }
01677         }
01678 
01679         // TODO: drive_fat.cpp should use an extension to this API to allow changing the sectors/track
01680         //       according to what it reads from the MS-DOS BIOS parameter block, just like real MS-DOS.
01681         //       This would allow better representation of strange disk formats such as the "extended"
01682         //       floppy format that Microsoft used to use for Word 95 and Windows 95 install floppies.
01683 
01684         LOG_MSG("D88 geometry detection: C/H/S %u/%u/%u %u bytes/sector",
01685                 cylinders, heads, sectors, sector_size);
01686 
01687         if (sector_size != 0 && sectors != 0 && cylinders != 0 && heads != 0)
01688             founddisk = true;
01689 
01690         if(!founddisk) {
01691             active = false;
01692         } else {
01693             incrementFDD();
01694         }
01695     }
01696 }
01697 
01698 imageDiskD88::~imageDiskD88() {
01699     if(diskimg != NULL) {
01700         fclose(diskimg);
01701         diskimg=NULL; 
01702     }
01703 }
01704