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 bool imageDiskChange[MAX_DISK_IMAGES]={false};
00068 imageDisk *imageDiskList[MAX_DISK_IMAGES]={NULL};
00069 imageDisk *diskSwap[MAX_SWAPPABLE_DISKS]={NULL};
00070 Bits swapPosition;
00071 
00072 imageDisk *GetINT13FloppyDrive(unsigned char drv) {
00073     if (drv >= 2)
00074         return NULL;
00075     return imageDiskList[drv];
00076 }
00077 
00078 imageDisk *GetINT13HardDrive(unsigned char drv) {
00079     if (drv < 0x80 || drv >= (0x80+MAX_DISK_IMAGES-2))
00080         return NULL;
00081 
00082     return imageDiskList[drv-0x80];
00083 }
00084 
00085 void FreeBIOSDiskList() {
00086     for (int i=0;i < MAX_DISK_IMAGES;i++) {
00087         if (imageDiskList[i] != NULL) {
00088             if (i >= 2) IDE_Hard_Disk_Detach(i);
00089             imageDiskList[i]->Release();
00090             imageDiskList[i] = NULL;
00091         }
00092     }
00093 
00094     for (int j=0;j < MAX_SWAPPABLE_DISKS;j++) {
00095         if (diskSwap[j] != NULL) {
00096             diskSwap[j]->Release();
00097             diskSwap[j] = NULL;
00098         }
00099     }
00100 }
00101 
00102 //update BIOS disk parameter tables for first two hard drives
00103 void updateDPT(void) {
00104     Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
00105     PhysPt dpphysaddr[2] = { CALLBACK_PhysPointer(diskparm0), CALLBACK_PhysPointer(diskparm1) };
00106     for (int i = 0; i < 2; i++) {
00107         tmpheads = 0; tmpcyl = 0; tmpsect = 0; tmpsize = 0;
00108         if (imageDiskList[i + 2] != NULL) {
00109             imageDiskList[i + 2]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
00110         }
00111         phys_writew(dpphysaddr[i], (Bit16u)tmpcyl);
00112         phys_writeb(dpphysaddr[i] + 0x2, (Bit8u)tmpheads);
00113         phys_writew(dpphysaddr[i] + 0x3, 0);
00114         phys_writew(dpphysaddr[i] + 0x5, tmpcyl == 0 ? 0 : (Bit16u)-1);
00115         phys_writeb(dpphysaddr[i] + 0x7, 0);
00116         phys_writeb(dpphysaddr[i] + 0x8, tmpcyl == 0 ? 0 : (0xc0 | (((tmpheads) > 8) << 3)));
00117         phys_writeb(dpphysaddr[i] + 0x9, 0);
00118         phys_writeb(dpphysaddr[i] + 0xa, 0);
00119         phys_writeb(dpphysaddr[i] + 0xb, 0);
00120         phys_writew(dpphysaddr[i] + 0xc, (Bit16u)tmpcyl);
00121         phys_writeb(dpphysaddr[i] + 0xe, (Bit8u)tmpsect);
00122     }
00123 }
00124 
00125 void incrementFDD(void) {
00126     Bit16u equipment=mem_readw(BIOS_CONFIGURATION);
00127     if(equipment&1) {
00128         Bitu numofdisks = (equipment>>6)&3;
00129         numofdisks++;
00130         if(numofdisks > 1) numofdisks=1;//max 2 floppies at the moment
00131         equipment&=~0x00C0;
00132         equipment|=(numofdisks<<6);
00133     } else equipment|=1;
00134     mem_writew(BIOS_CONFIGURATION,equipment);
00135     CMOS_SetRegister(0x14, (Bit8u)(equipment&0xff));
00136 }
00137 
00138 int swapInDisksSpecificDrive = -1;
00139 // -1 = swap across A: and B: (DOSBox / DOSBox-X default behavior)
00140 //  0 = swap across A: only
00141 //  1 = swap across B: only
00142 
00143 void swapInDisks(void) {
00144     bool allNull = true;
00145     Bits diskcount = 0;
00146     Bits diskswapcount = 2;
00147     Bits diskswapdrive = 0;
00148     Bits swapPos = swapPosition;
00149     int i;
00150 
00151     /* Check to make sure that  there is at least one setup image */
00152     for(i=0;i<MAX_SWAPPABLE_DISKS;i++) {
00153         if(diskSwap[i]!=NULL) {
00154             allNull = false;
00155             break;
00156         }
00157     }
00158 
00159     /* No disks setup... fail */
00160     if (allNull) return;
00161 
00162     /* if a specific drive is to be swapped, then adjust to focus on it */
00163     if (swapInDisksSpecificDrive >= 0 && swapInDisksSpecificDrive <= 1) {
00164         diskswapdrive = swapInDisksSpecificDrive;
00165         diskswapcount = 1;
00166     }
00167 
00168     /* If only one disk is loaded, this loop will load the same disk in dive A and drive B */
00169     while(diskcount < diskswapcount) {
00170         if(diskSwap[swapPos] != NULL) {
00171             LOG_MSG("Loaded drive %d disk %d from swaplist position %d - \"%s\"", (int)diskswapdrive, (int)diskcount, (int)swapPos, diskSwap[swapPos]->diskname.c_str());
00172 
00173             if (imageDiskList[diskswapdrive] != NULL)
00174                 imageDiskList[diskswapdrive]->Release();
00175 
00176             imageDiskList[diskswapdrive] = diskSwap[swapPos];
00177             imageDiskList[diskswapdrive]->Addref();
00178 
00179             imageDiskChange[diskswapdrive] = true;
00180 
00181             diskcount++;
00182             diskswapdrive++;
00183         }
00184 
00185         swapPos++;
00186         if(swapPos>=MAX_SWAPPABLE_DISKS) swapPos=0;
00187     }
00188 }
00189 
00190 bool getSwapRequest(void) {
00191     bool sreq=swapping_requested;
00192     swapping_requested = false;
00193     return sreq;
00194 }
00195 
00196 void swapInNextDisk(bool pressed) {
00197     if (!pressed)
00198         return;
00199     DriveManager::CycleAllDisks();
00200     /* Hack/feature: rescan all disks as well */
00201     LOG_MSG("Diskcaching reset for floppy drives.");
00202     for(Bitu i=0;i<2;i++) { /* Swap A: and B: where DOSBox mainline would run through ALL drive letters */
00203         if (Drives[i] != NULL) {
00204             Drives[i]->EmptyCache();
00205             Drives[i]->MediaChange();
00206         }
00207     }
00208     swapPosition++;
00209     if(diskSwap[swapPosition] == NULL) swapPosition = 0;
00210     swapInDisks();
00211     swapping_requested = true;
00212 }
00213 
00214 void swapInNextCD(bool pressed) {
00215     if (!pressed)
00216         return;
00217     DriveManager::CycleAllCDs();
00218     /* Hack/feature: rescan all disks as well */
00219     LOG_MSG("Diskcaching reset for normal mounted drives.");
00220     for(Bitu i=2;i<DOS_DRIVES;i++) { /* Swap C: D: .... Z: TODO: Need to swap ONLY if a CD-ROM drive! */
00221         if (Drives[i] != NULL) {
00222             Drives[i]->EmptyCache();
00223             Drives[i]->MediaChange();
00224         }
00225     }
00226 }
00227 
00228 
00229 Bit8u imageDisk::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) {
00230     Bit32u sectnum;
00231 
00232     if (req_sector_size == 0)
00233         req_sector_size = sector_size;
00234     if (req_sector_size != sector_size)
00235         return 0x05;
00236 
00237     sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;
00238 
00239     return Read_AbsoluteSector(sectnum, data);
00240 }
00241 
00242 Bit8u imageDisk::Read_AbsoluteSector(Bit32u sectnum, void * data) {
00243     Bit64u bytenum,res;
00244     int got;
00245 
00246     bytenum = (Bit64u)sectnum * (Bit64u)sector_size;
00247     if ((bytenum + sector_size) > this->image_length) {
00248         LOG_MSG("Attempt to read invalid sector in Read_AbsoluteSector for sector %lu.\n", (unsigned long)sectnum);
00249         return 0x05;
00250     }
00251     bytenum += image_base;
00252 
00253     //LOG_MSG("Reading sectors %ld at bytenum %I64d", sectnum, bytenum);
00254 
00255     fseeko64(diskimg,(off_t)bytenum,SEEK_SET);
00256     res = (Bit64u)ftello64(diskimg);
00257     if (res != bytenum) {
00258         LOG_MSG("fseek() failed in Read_AbsoluteSector for sector %lu. Want=%llu Got=%llu\n",
00259             (unsigned long)sectnum,(unsigned long long)bytenum,(unsigned long long)res);
00260         return 0x05;
00261     }
00262 
00263     got = (int)fread(data, 1, sector_size, diskimg);
00264     if ((unsigned int)got != sector_size) {
00265         LOG_MSG("fread() failed in Read_AbsoluteSector for sector %lu. Want=%u got=%d\n",
00266             (unsigned long)sectnum,(unsigned int)sector_size,(unsigned int)got);
00267         return 0x05;
00268     }
00269 
00270     return 0x00;
00271 }
00272 
00273 Bit8u imageDisk::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) {
00274     Bit32u sectnum;
00275 
00276     if (req_sector_size == 0)
00277         req_sector_size = sector_size;
00278     if (req_sector_size != sector_size)
00279         return 0x05;
00280 
00281     sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L;
00282 
00283     return Write_AbsoluteSector(sectnum, data);
00284 }
00285 
00286 
00287 Bit8u imageDisk::Write_AbsoluteSector(Bit32u sectnum, const void *data) {
00288     Bit64u bytenum;
00289 
00290     bytenum = (Bit64u)sectnum * sector_size;
00291     if ((bytenum + sector_size) > this->image_length) {
00292         LOG_MSG("Attempt to read invalid sector in Write_AbsoluteSector for sector %lu.\n", (unsigned long)sectnum);
00293         return 0x05;
00294     }
00295     bytenum += image_base;
00296 
00297     //LOG_MSG("Writing sectors to %ld at bytenum %d", sectnum, bytenum);
00298 
00299     fseeko64(diskimg,(off_t)bytenum,SEEK_SET);
00300     if ((Bit64u)ftello64(diskimg) != bytenum)
00301         LOG_MSG("WARNING: fseek() failed in Write_AbsoluteSector for sector %lu\n",(unsigned long)sectnum);
00302 
00303     size_t ret=fwrite(data, sector_size, 1, diskimg);
00304 
00305     return ((ret>0)?0x00:0x05);
00306 
00307 }
00308 
00309 void imageDisk::Set_Reserved_Cylinders(Bitu resCyl) {
00310     reserved_cylinders = resCyl;
00311 }
00312 
00313 Bit32u imageDisk::Get_Reserved_Cylinders() {
00314     return reserved_cylinders;
00315 }
00316 
00317 imageDisk::imageDisk(IMAGE_TYPE class_id) {
00318     heads = 0;
00319     cylinders = 0;
00320     image_base = 0;
00321     sectors = 0;
00322     refcount = 0;
00323     sector_size = 512;
00324     image_length = 0;
00325     reserved_cylinders = 0;
00326     diskimg = NULL;
00327     this->class_id = class_id;
00328     active = false;
00329     hardDrive = false;
00330 }
00331 
00332 imageDisk::imageDisk(FILE* diskimg, const char* diskName, Bit32u cylinders, Bit32u heads, Bit32u sectors, Bit32u sector_size, bool hardDrive) {
00333     if (diskName) this->diskname = diskName;
00334     this->cylinders = cylinders;
00335     this->heads = heads;
00336     this->sectors = sectors;
00337     image_base = 0;
00338     this->image_length = (Bit64u)cylinders * heads * sectors * sector_size;
00339     refcount = 0;
00340     this->sector_size = sector_size;
00341     this->diskSizeK = this->image_length / 1024;
00342     reserved_cylinders = 0;
00343     this->diskimg = diskimg;
00344     class_id = ID_BASE;
00345     active = true;
00346     this->hardDrive = hardDrive;
00347 }
00348 
00349 /* .HDI and .FDI header (NP2) */
00350 #pragma pack(push,1)
00351 typedef struct {
00352     uint8_t dummy[4];           // +0x00
00353     uint8_t hddtype[4];         // +0x04
00354     uint8_t headersize[4];      // +0x08
00355     uint8_t hddsize[4];         // +0x0C
00356     uint8_t sectorsize[4];      // +0x10
00357     uint8_t sectors[4];         // +0x14
00358     uint8_t surfaces[4];        // +0x18
00359     uint8_t cylinders[4];       // +0x1C
00360 } HDIHDR;                       // =0x20
00361 
00362 typedef struct {
00363         uint8_t dummy[4];           // +0x00
00364         uint8_t fddtype[4];         // +0x04
00365         uint8_t headersize[4];      // +0x08
00366         uint8_t fddsize[4];         // +0x0C
00367         uint8_t sectorsize[4];      // +0x10
00368         uint8_t sectors[4];         // +0x14
00369         uint8_t surfaces[4];        // +0x18
00370         uint8_t cylinders[4];       // +0x1C
00371 } FDIHDR;                       // =0x20
00372 
00373 typedef struct {
00374         char    sig[16];            // +0x000
00375         char    comment[0x100];     // +0x010
00376         UINT8   headersize[4];      // +0x110
00377     uint8_t prot;               // +0x114
00378     uint8_t nhead;              // +0x115
00379     uint8_t _unknown_[10];      // +0x116
00380 } NFDHDR;                       // =0x120
00381 
00382 typedef struct {
00383         char    sig[16];            // +0x000
00384         char    comment[0x100];     // +0x010
00385         UINT8   headersize[4];      // +0x110
00386     uint8_t prot;               // +0x114
00387     uint8_t nhead;              // +0x115
00388     uint8_t _unknown_[10];      // +0x116
00389     uint32_t trackheads[164];   // +0x120
00390     uint32_t addinfo;           // +0x3b0
00391     uint8_t _unknown2_[12];     // +0x3b4
00392 } NFDHDRR1;                     // =0x3c0
00393 
00394 typedef struct {
00395     uint8_t log_cyl;            // +0x0
00396     uint8_t log_head;           // +0x1
00397     uint8_t log_rec;            // +0x2
00398     uint8_t sec_len_pow2;       // +0x3         sz = 128 << len_pow2
00399     uint8_t flMFM;              // +0x4
00400     uint8_t flDDAM;             // +0x5
00401     uint8_t byStatus;           // +0x6
00402     uint8_t bySTS0;             // +0x7
00403     uint8_t bySTS1;             // +0x8
00404     uint8_t bySTS2;             // +0x9
00405     uint8_t byRetry;            // +0xA
00406     uint8_t byPDA;              // +0xB
00407     uint8_t _unknown_[4];       // +0xC
00408 } NFDHDR_ENTRY;                 // =0x10
00409 
00410 typedef struct {
00411     char        szFileID[15];                 // 識別ID "T98HDDIMAGE.R0"
00412     char        Reserve1[1];                  // 予約
00413     char        szComment[0x100];             // イメージコメント(ASCIIz)
00414     uint32_t    dwHeadSize;                   // ヘッダ部のサイズ
00415     uint32_t    dwCylinder;                   // シリンダ数
00416     uint16_t    wHead;                        // ヘッド数
00417     uint16_t    wSect;                        // 1トラックあたりのセクタ数
00418     uint16_t    wSectLen;                     // セクタ長
00419     char        Reserve2[2];                  // 予約
00420     char        Reserve3[0xe0];               // 予約
00421 }NHD_FILE_HEAD,*LP_NHD_FILE_HEAD;
00422 #pragma pack(pop)
00423 
00424 imageDisk::imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) {
00425     heads = 0;
00426     cylinders = 0;
00427     image_base = 0;
00428     image_length = imgSizeK * 1024;
00429     sectors = 0;
00430     refcount = 0;
00431     sector_size = 512;
00432     reserved_cylinders = 0;
00433     diskimg = imgFile;
00434     class_id = ID_BASE;
00435     diskSizeK = imgSizeK;
00436 
00437     if (imgName != NULL)
00438         diskname = (const char*)imgName;
00439 
00440     active = false;
00441     hardDrive = isHardDisk;
00442     if(!isHardDisk) {
00443         Bit8u i=0;
00444         bool founddisk = false;
00445 
00446         if (imgName != NULL) {
00447             char *ext = strrchr((char*)imgName,'.');
00448             if (ext != NULL) {
00449                 if (!strcasecmp(ext,".fdi")) {
00450                     if (imgSizeK >= 160) {
00451                         FDIHDR fdihdr;
00452 
00453                         // PC-98 .FDI images appear to be 4096 bytes of a short header and many zeros.
00454                         // followed by a straight sector dump of the disk. The header is NOT NECESSARILY
00455                         // 4KB in size, but usually is.
00456                         LOG_MSG("Image file has .FDI extension, assuming FDI image and will take on parameters in header.");
00457 
00458                         assert(sizeof(fdihdr) == 0x20);
00459                         if (fseek(imgFile,0,SEEK_SET) == 0 && ftell(imgFile) == 0 &&
00460                             fread(&fdihdr,sizeof(fdihdr),1,imgFile) == 1) {
00461                             uint32_t ofs = host_readd(fdihdr.headersize);
00462                             uint32_t fddsize = host_readd(fdihdr.fddsize); /* includes header */
00463                             uint32_t sectorsize = host_readd(fdihdr.sectorsize);
00464 
00465                             if (sectorsize != 0 && ((sectorsize & (sectorsize - 1)) == 0/*is power of 2*/) &&
00466                                 sectorsize >= 256 && sectorsize <= 1024 &&
00467                                 ofs != 0 && (ofs % sectorsize) == 0/*offset is nonzero and multiple of sector size*/ &&
00468                                 (ofs % 1024) == 0/*offset is a multiple of 1024 because of imgSizeK*/ &&
00469                                 fddsize >= sectorsize && (fddsize/1024) <= (imgSizeK+4)) {
00470 
00471                                 founddisk = true;
00472                                 sector_size = sectorsize;
00473                                 imgSizeK -= (ofs / 1024);
00474                                 image_base = ofs;
00475                                 image_length -= ofs;
00476                                 LOG_MSG("FDI header: sectorsize is %u bytes/sector, header is %u bytes, fdd size (plus header) is %u bytes",
00477                                     (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)fddsize);
00478 
00479                                 /* take on the geometry. */
00480                                 sectors = host_readd(fdihdr.sectors);
00481                                 heads = host_readd(fdihdr.surfaces);
00482                                 cylinders = host_readd(fdihdr.cylinders);
00483                                 LOG_MSG("FDI: Geometry is C/H/S %u/%u/%u",
00484                                     (unsigned int)cylinders,(unsigned int)heads,(unsigned int)sectors);
00485                             }
00486                             else {
00487                                 LOG_MSG("FDI header rejected. sectorsize=%u headersize=%u fddsize=%u",
00488                                     (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)fddsize);
00489                             }
00490                         }
00491                         else {
00492                             LOG_MSG("Unable to read .FDI header");
00493                         }
00494                     }
00495                 }
00496             }
00497         }
00498 
00499         if (sectors == 0 && heads == 0 && cylinders == 0) {
00500             while (DiskGeometryList[i].ksize!=0x0) {
00501                 if ((DiskGeometryList[i].ksize==imgSizeK) ||
00502                         (DiskGeometryList[i].ksize+1==imgSizeK)) {
00503                     if (DiskGeometryList[i].ksize!=imgSizeK)
00504                         LOG_MSG("ImageLoader: image file with additional data, might not load!");
00505                     founddisk = true;
00506                     active = true;
00507                     floppytype = i;
00508                     heads = DiskGeometryList[i].headscyl;
00509                     cylinders = DiskGeometryList[i].cylcount;
00510                     sectors = DiskGeometryList[i].secttrack;
00511                     sector_size = DiskGeometryList[i].bytespersect;
00512                     LOG_MSG("Identified '%s' as C/H/S %u/%u/%u %u bytes/sector",
00513                             imgName,cylinders,heads,sectors,sector_size);
00514                     break;
00515                 }
00516                 i++;
00517             }
00518         }
00519         if(!founddisk) {
00520             active = false;
00521         }
00522     }
00523     else { /* hard disk */
00524         if (imgName != NULL) {
00525             char *ext = strrchr((char*)imgName,'.');
00526             if (ext != NULL) {
00527                 if (!strcasecmp(ext,".nhd")) {
00528                     if (imgSizeK >= 160) {
00529                         NHD_FILE_HEAD nhdhdr;
00530 
00531                         LOG_MSG("Image file has .NHD extension, assuming NHD image and will take on parameters in header.");
00532 
00533                         assert(sizeof(nhdhdr) == 0x200);
00534                         if (fseek(imgFile,0,SEEK_SET) == 0 && ftell(imgFile) == 0 &&
00535                             fread(&nhdhdr,sizeof(nhdhdr),1,imgFile) == 1 &&
00536                             host_readd((ConstHostPt)(&nhdhdr.dwHeadSize)) >= 0x200 &&
00537                             !memcmp(nhdhdr.szFileID,"T98HDDIMAGE.R0\0",15)) {
00538                             uint32_t ofs = host_readd((ConstHostPt)(&nhdhdr.dwHeadSize));
00539                             uint32_t sectorsize = host_readw((ConstHostPt)(&nhdhdr.wSectLen));
00540 
00541                             if (sectorsize != 0 && ((sectorsize & (sectorsize - 1)) == 0/*is power of 2*/) &&
00542                                 sectorsize >= 256 && sectorsize <= 1024 &&
00543                                 ofs != 0 && (ofs % sectorsize) == 0/*offset is nonzero and multiple of sector size*/) {
00544 
00545                                 sector_size = sectorsize;
00546                                 imgSizeK -= (ofs / 1024);
00547                                 image_base = ofs;
00548                                 image_length -= ofs;
00549                                 LOG_MSG("NHD header: sectorsize is %u bytes/sector, header is %u bytes",
00550                                         (unsigned int)sectorsize,(unsigned int)ofs);
00551 
00552                                 /* take on the geometry.
00553                                  * PC-98 IPL1 support will need it to make sense of the partition table. */
00554                                 sectors = host_readw((ConstHostPt)(&nhdhdr.wSect));
00555                                 heads = host_readw((ConstHostPt)(&nhdhdr.wHead));
00556                                 cylinders = host_readd((ConstHostPt)(&nhdhdr.dwCylinder));
00557                                 LOG_MSG("NHD: Geometry is C/H/S %u/%u/%u",
00558                                         (unsigned int)cylinders,(unsigned int)heads,(unsigned int)sectors);
00559                             }
00560                             else {
00561                                 LOG_MSG("NHD header rejected. sectorsize=%u headersize=%u",
00562                                         (unsigned int)sectorsize,(unsigned int)ofs);
00563                             }
00564                         }
00565                         else {
00566                             LOG_MSG("Unable to read .NHD header");
00567                         }
00568                     }
00569                 }
00570                 if (!strcasecmp(ext,".hdi")) {
00571                     if (imgSizeK >= 160) {
00572                         HDIHDR hdihdr;
00573 
00574                         // PC-98 .HDI images appear to be 4096 bytes of a short header and many zeros.
00575                         // followed by a straight sector dump of the disk. The header is NOT NECESSARILY
00576                         // 4KB in size, but usually is.
00577                         LOG_MSG("Image file has .HDI extension, assuming HDI image and will take on parameters in header.");
00578 
00579                         assert(sizeof(hdihdr) == 0x20);
00580                         if (fseek(imgFile,0,SEEK_SET) == 0 && ftell(imgFile) == 0 &&
00581                             fread(&hdihdr,sizeof(hdihdr),1,imgFile) == 1) {
00582                             uint32_t ofs = host_readd(hdihdr.headersize);
00583                             uint32_t hddsize = host_readd(hdihdr.hddsize); /* includes header */
00584                             uint32_t sectorsize = host_readd(hdihdr.sectorsize);
00585 
00586                             if (sectorsize != 0 && ((sectorsize & (sectorsize - 1)) == 0/*is power of 2*/) &&
00587                                 sectorsize >= 256 && sectorsize <= 1024 &&
00588                                 ofs != 0 && (ofs % sectorsize) == 0/*offset is nonzero and multiple of sector size*/ &&
00589                                 (ofs % 1024) == 0/*offset is a multiple of 1024 because of imgSizeK*/ &&
00590                                 hddsize >= sectorsize && (hddsize/1024) <= (imgSizeK+4)) {
00591 
00592                                 sector_size = sectorsize;
00593                                 imgSizeK -= (ofs / 1024);
00594                                 image_base = ofs;
00595                                 image_length -= ofs;
00596                                 LOG_MSG("HDI header: sectorsize is %u bytes/sector, header is %u bytes, hdd size (plus header) is %u bytes",
00597                                     (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)hddsize);
00598 
00599                                 /* take on the geometry.
00600                                  * PC-98 IPL1 support will need it to make sense of the partition table. */
00601                                 sectors = host_readd(hdihdr.sectors);
00602                                 heads = host_readd(hdihdr.surfaces);
00603                                 cylinders = host_readd(hdihdr.cylinders);
00604                                 LOG_MSG("HDI: Geometry is C/H/S %u/%u/%u",
00605                                     (unsigned int)cylinders,(unsigned int)heads,(unsigned int)sectors);
00606                             }
00607                             else {
00608                                 LOG_MSG("HDI header rejected. sectorsize=%u headersize=%u hddsize=%u",
00609                                     (unsigned int)sectorsize,(unsigned int)ofs,(unsigned int)hddsize);
00610                             }
00611                         }
00612                         else {
00613                             LOG_MSG("Unable to read .HDI header");
00614                         }
00615                     }
00616                 }
00617             }
00618         }
00619 
00620         if (sectors == 0 || heads == 0 || cylinders == 0)
00621             active = false;
00622     }
00623 }
00624 
00625 void imageDisk::Set_Geometry(Bit32u setHeads, Bit32u setCyl, Bit32u setSect, Bit32u setSectSize) {
00626     Bitu bigdisk_shift = 0;
00627 
00628     if (IS_PC98_ARCH) {
00629         /* TODO: PC-98 has it's own 4096 cylinder limit */
00630     }
00631     else {
00632         if(setCyl > 16384) LOG_MSG("This disk image is too big.");
00633         else if(setCyl > 8192) bigdisk_shift = 4;
00634         else if(setCyl > 4096) bigdisk_shift = 3;
00635         else if(setCyl > 2048) bigdisk_shift = 2;
00636         else if(setCyl > 1024) bigdisk_shift = 1;
00637     }
00638 
00639     heads = setHeads << bigdisk_shift;
00640     cylinders = setCyl >> bigdisk_shift;
00641     sectors = setSect;
00642     sector_size = setSectSize;
00643     active = true;
00644 }
00645 
00646 void imageDisk::Get_Geometry(Bit32u * getHeads, Bit32u *getCyl, Bit32u *getSect, Bit32u *getSectSize) {
00647     *getHeads = heads;
00648     *getCyl = cylinders;
00649     *getSect = sectors;
00650     *getSectSize = sector_size;
00651 }
00652 
00653 Bit8u imageDisk::GetBiosType(void) {
00654     if(!hardDrive) {
00655         return (Bit8u)DiskGeometryList[floppytype].biosval;
00656     } else return 0;
00657 }
00658 
00659 Bit32u imageDisk::getSectSize(void) {
00660     return sector_size;
00661 }
00662 
00663 static Bitu GetDosDriveNumber(Bitu biosNum) {
00664     switch(biosNum) {
00665         case 0x0:
00666             return 0x0;
00667         case 0x1:
00668             return 0x1;
00669         case 0x80:
00670             return 0x2;
00671         case 0x81:
00672             return 0x3;
00673         case 0x82:
00674             return 0x4;
00675         case 0x83:
00676             return 0x5;
00677         default:
00678             return 0x7f;
00679     }
00680 }
00681 
00682 static bool driveInactive(Bitu driveNum) {
00683     if(driveNum>=(2 + MAX_HDD_IMAGES)) {
00684         LOG(LOG_BIOS,LOG_ERROR)("Disk %d non-existant", (int)driveNum);
00685         last_status = 0x01;
00686         CALLBACK_SCF(true);
00687         return true;
00688     }
00689     if(imageDiskList[driveNum] == NULL) {
00690         LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", (int)driveNum);
00691         last_status = 0x01;
00692         CALLBACK_SCF(true);
00693         return true;
00694     }
00695     if(!imageDiskList[driveNum]->active) {
00696         LOG(LOG_BIOS,LOG_ERROR)("Disk %d not active", (int)driveNum);
00697         last_status = 0x01;
00698         CALLBACK_SCF(true);
00699         return true;
00700     }
00701     return false;
00702 }
00703 
00704 static struct {
00705     Bit8u sz;
00706     Bit8u res;
00707     Bit16u num;
00708     Bit16u off;
00709     Bit16u seg;
00710     Bit32u sector;
00711 } dap;
00712 
00713 static void readDAP(Bit16u seg, Bit16u off) {
00714     dap.sz = real_readb(seg,off++);
00715     dap.res = real_readb(seg,off++);
00716     dap.num = real_readw(seg,off); off += 2;
00717     dap.off = real_readw(seg,off); off += 2;
00718     dap.seg = real_readw(seg,off); off += 2;
00719 
00720     /* Although sector size is 64-bit, 32-bit 2TB limit should be more than enough */
00721     dap.sector = real_readd(seg,off); off +=4;
00722 
00723     if (real_readd(seg,off)) {
00724         E_Exit("INT13: 64-bit sector addressing not supported");
00725     }
00726 }
00727 
00728 void IDE_ResetDiskByBIOS(unsigned char disk);
00729 void IDE_EmuINT13DiskReadByBIOS(unsigned char disk,unsigned int cyl,unsigned int head,unsigned sect);
00730 void IDE_EmuINT13DiskReadByBIOS_LBA(unsigned char disk,uint64_t lba);
00731 
00732 static Bitu INT13_DiskHandler(void) {
00733     Bit16u segat, bufptr;
00734     Bit8u sectbuf[512];
00735     Bitu  drivenum;
00736     Bitu  i,t;
00737     last_drive = reg_dl;
00738     drivenum = GetDosDriveNumber(reg_dl);
00739     bool any_images = false;
00740     for(i = 0;i < MAX_DISK_IMAGES;i++) {
00741         if(imageDiskList[i]) any_images=true;
00742     }
00743 
00744     // unconditionally enable the interrupt flag
00745     CALLBACK_SIF(true);
00746 
00747     /* map out functions 0x40-0x48 if not emulating INT 13h extensions */
00748     if (!int13_extensions_enable && reg_ah >= 0x40 && reg_ah <= 0x48) {
00749         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);
00750         reg_ah=0xff;
00751         CALLBACK_SCF(true);
00752         return CBRET_NONE;
00753     }
00754 
00755     //drivenum = 0;
00756     //LOG_MSG("INT13: Function %x called on drive %x (dos drive %d)", reg_ah,  reg_dl, drivenum);
00757     switch(reg_ah) {
00758     case 0x0: /* Reset disk */
00759         {
00760             /* if there aren't any diskimages (so only localdrives and virtual drives)
00761              * always succeed on reset disk. If there are diskimages then and only then
00762              * do real checks
00763              */
00764             if (any_images && driveInactive(drivenum)) {
00765                 /* driveInactive sets carry flag if the specified drive is not available */
00766                 if ((machine==MCH_CGA) || (machine==MCH_AMSTRAD) || (machine==MCH_PCJR)) {
00767                     /* those bioses call floppy drive reset for invalid drive values */
00768                     if (((imageDiskList[0]) && (imageDiskList[0]->active)) || ((imageDiskList[1]) && (imageDiskList[1]->active))) {
00769                         if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++;
00770                         last_status = 0x00;
00771                         CALLBACK_SCF(false);
00772                     }
00773                 }
00774                 return CBRET_NONE;
00775             }
00776             if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++;
00777             if (reg_dl >= 0x80) IDE_ResetDiskByBIOS(reg_dl);
00778             last_status = 0x00;
00779             CALLBACK_SCF(false);
00780         }
00781         break;
00782     case 0x1: /* Get status of last operation */
00783 
00784         if(last_status != 0x00) {
00785             reg_ah = last_status;
00786             CALLBACK_SCF(true);
00787         } else {
00788             reg_ah = 0x00;
00789             CALLBACK_SCF(false);
00790         }
00791         break;
00792     case 0x2: /* Read sectors */
00793         if (reg_al==0) {
00794             reg_ah = 0x01;
00795             CALLBACK_SCF(true);
00796             return CBRET_NONE;
00797         }
00798         if (!any_images) {
00799             // Inherit the Earth cdrom (uses it as disk test)
00800             if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) {
00801                 reg_ah = 0;
00802                 CALLBACK_SCF(false);
00803                 return CBRET_NONE;
00804             }
00805         }
00806         if (driveInactive(drivenum)) {
00807             reg_ah = 0xff;
00808             CALLBACK_SCF(true);
00809             return CBRET_NONE;
00810         }
00811 
00812         /* INT 13h is limited to 512 bytes/sector (as far as I know).
00813          * The sector buffer in this function is limited to 512 bytes/sector,
00814          * so this is also a protection against overruning the stack if you
00815          * mount a PC-98 disk image (1024 bytes/sector) and try to read it with INT 13h. */
00816         if (imageDiskList[drivenum]->sector_size > sizeof(sectbuf)) {
00817             LOG(LOG_MISC,LOG_DEBUG)("INT 13h: Read failed because disk bytes/sector on drive %c is too large",(char)drivenum+'A');
00818 
00819             imageDiskChange[drivenum] = false;
00820 
00821             reg_ah = 0x80; /* timeout */
00822             CALLBACK_SCF(true);
00823             return CBRET_NONE;
00824         }
00825 
00826         /* If the disk changed, the first INT 13h read will signal an error and set AH = 0x06 to indicate disk change */
00827         if (drivenum < 2 && imageDiskChange[drivenum]) {
00828             LOG(LOG_MISC,LOG_DEBUG)("INT 13h: Failing first read of drive %c to indicate disk change",(char)drivenum+'A');
00829 
00830             imageDiskChange[drivenum] = false;
00831 
00832             reg_ah = 0x06; /* diskette changed or removed */
00833             CALLBACK_SCF(true);
00834             return CBRET_NONE;
00835         }
00836 
00837         segat = SegValue(es);
00838         bufptr = reg_bx;
00839         for(i=0;i<reg_al;i++) {
00840             last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
00841 
00842             /* IDE emulation: simulate change of IDE state that would occur on a real machine after INT 13h */
00843             IDE_EmuINT13DiskReadByBIOS(reg_dl, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)reg_dh, (Bit32u)((reg_cl & 63)+i));
00844 
00845             if((last_status != 0x00) || (killRead)) {
00846                 LOG_MSG("Error in disk read");
00847                 killRead = false;
00848                 reg_ah = 0x04;
00849                 CALLBACK_SCF(true);
00850                 return CBRET_NONE;
00851             }
00852             for(t=0;t<512;t++) {
00853                 real_writeb(segat,bufptr,sectbuf[t]);
00854                 bufptr++;
00855             }
00856         }
00857         reg_ah = 0x00;
00858         CALLBACK_SCF(false);
00859         break;
00860     case 0x3: /* Write sectors */
00861         
00862         if(driveInactive(drivenum)) {
00863             reg_ah = 0xff;
00864             CALLBACK_SCF(true);
00865             return CBRET_NONE;
00866         }                     
00867 
00868         /* INT 13h is limited to 512 bytes/sector (as far as I know).
00869          * The sector buffer in this function is limited to 512 bytes/sector,
00870          * so this is also a protection against overruning the stack if you
00871          * mount a PC-98 disk image (1024 bytes/sector) and try to read it with INT 13h. */
00872         if (imageDiskList[drivenum]->sector_size > sizeof(sectbuf)) {
00873             LOG(LOG_MISC,LOG_DEBUG)("INT 13h: Write failed because disk bytes/sector on drive %c is too large",(char)drivenum+'A');
00874 
00875             imageDiskChange[drivenum] = false;
00876 
00877             reg_ah = 0x80; /* timeout */
00878             CALLBACK_SCF(true);
00879             return CBRET_NONE;
00880         }
00881 
00882         bufptr = reg_bx;
00883         for(i=0;i<reg_al;i++) {
00884             for(t=0;t<imageDiskList[drivenum]->getSectSize();t++) {
00885                 sectbuf[t] = real_readb(SegValue(es),bufptr);
00886                 bufptr++;
00887             }
00888 
00889             last_status = imageDiskList[drivenum]->Write_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0) << 2)), (Bit32u)((reg_cl & 63) + i), &sectbuf[0]);
00890             if(last_status != 0x00) {
00891             CALLBACK_SCF(true);
00892                 return CBRET_NONE;
00893             }
00894         }
00895         reg_ah = 0x00;
00896         CALLBACK_SCF(false);
00897         break;
00898     case 0x04: /* Verify sectors */
00899         if (reg_al==0) {
00900             reg_ah = 0x01;
00901             CALLBACK_SCF(true);
00902             return CBRET_NONE;
00903         }
00904         if(driveInactive(drivenum)) return CBRET_NONE;
00905 
00906         /* TODO: Finish coding this section */
00907         /*
00908         segat = SegValue(es);
00909         bufptr = reg_bx;
00910         for(i=0;i<reg_al;i++) {
00911             last_status = imageDiskList[drivenum]->Read_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf);
00912             if(last_status != 0x00) {
00913                 LOG_MSG("Error in disk read");
00914                 CALLBACK_SCF(true);
00915                 return CBRET_NONE;
00916             }
00917             for(t=0;t<512;t++) {
00918                 real_writeb(segat,bufptr,sectbuf[t]);
00919                 bufptr++;
00920             }
00921         }*/
00922         reg_ah = 0x00;
00923         //Qbix: The following codes don't match my specs. al should be number of sector verified
00924         //reg_al = 0x10; /* CRC verify failed */
00925         //reg_al = 0x00; /* CRC verify succeeded */
00926         CALLBACK_SCF(false);
00927           
00928         break;
00929     case 0x05: /* Format track */
00930         /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */
00931         LOG_MSG("WARNING: Format track ignored\n");
00932         CALLBACK_SCF(false);
00933         reg_ah = 0x00;
00934         break;
00935     case 0x06: /* Format track set bad sector flags */
00936         /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */
00937         LOG_MSG("WARNING: Format track set bad sector flags ignored (6)\n");
00938         CALLBACK_SCF(false);
00939         reg_ah = 0x00;
00940         break;
00941     case 0x07: /* Format track set bad sector flags */
00942         /* ignore it. I just fucking want FORMAT.COM to write the FAT structure for God's sake */
00943         LOG_MSG("WARNING: Format track set bad sector flags ignored (7)\n");
00944         CALLBACK_SCF(false);
00945         reg_ah = 0x00;
00946         break;
00947     case 0x08: /* Get drive parameters */
00948         if(driveInactive(drivenum)) {
00949             last_status = 0x07;
00950             reg_ah = last_status;
00951             CALLBACK_SCF(true);
00952             return CBRET_NONE;
00953         }
00954         reg_ax = 0x00;
00955         reg_bl = imageDiskList[drivenum]->GetBiosType();
00956         Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
00957         imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
00958         if (tmpcyl==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: cylinder count zero!");
00959         else tmpcyl--;      // cylinder count -> max cylinder
00960         if (tmpheads==0) LOG(LOG_BIOS,LOG_ERROR)("INT13 DrivParm: head count zero!");
00961         else tmpheads--;    // head count -> max head
00962 
00963         /* older BIOSes were known to subtract 1 or 2 additional "reserved" cylinders.
00964          * some code, such as Windows 3.1 WDCTRL, might assume that fact. emulate that here */
00965         {
00966             Bit32u reserv = imageDiskList[drivenum]->Get_Reserved_Cylinders();
00967             if (tmpcyl > reserv) tmpcyl -= reserv;
00968             else tmpcyl = 0;
00969         }
00970 
00971         reg_ch = (Bit8u)(tmpcyl & 0xff);
00972         reg_cl = (Bit8u)(((tmpcyl >> 2) & 0xc0) | (tmpsect & 0x3f)); 
00973         reg_dh = (Bit8u)tmpheads;
00974         last_status = 0x00;
00975         if (reg_dl&0x80) {  // harddisks
00976             reg_dl = 0;
00977             for (int index = 2; index < MAX_DISK_IMAGES; index++) {
00978                 if (imageDiskList[index] != NULL) reg_dl++;
00979             }
00980         } else {        // floppy disks
00981             reg_dl = 0;
00982             if(imageDiskList[0] != NULL) reg_dl++;
00983             if(imageDiskList[1] != NULL) reg_dl++;
00984         }
00985         CALLBACK_SCF(false);
00986         break;
00987     case 0x11: /* Recalibrate drive */
00988         reg_ah = 0x00;
00989         CALLBACK_SCF(false);
00990         break;
00991     case 0x17: /* Set disk type for format */
00992         /* Pirates! needs this to load */
00993         killRead = true;
00994         reg_ah = 0x00;
00995         CALLBACK_SCF(false);
00996         break;
00997     case 0x41: /* Check Extensions Present */
00998         if ((reg_bx == 0x55aa) && !(driveInactive(drivenum))) {
00999             LOG_MSG("INT13: Check Extensions Present for drive: 0x%x", reg_dl);
01000             reg_ah=0x1; /* 1.x extension supported */
01001             reg_bx=0xaa55;  /* Extensions installed */
01002             reg_cx=0x1; /* Extended disk access functions (AH=42h-44h,47h,48h) supported */
01003             CALLBACK_SCF(false);
01004             break;
01005         }
01006         LOG_MSG("INT13: AH=41h, Function not supported 0x%x for drive: 0x%x", reg_bx, reg_dl);
01007         CALLBACK_SCF(true);
01008         break;
01009     case 0x42: /* Extended Read Sectors From Drive */
01010         /* Read Disk Address Packet */
01011         readDAP(SegValue(ds),reg_si);
01012 
01013         if (dap.num==0) {
01014             reg_ah = 0x01;
01015             CALLBACK_SCF(true);
01016             return CBRET_NONE;
01017         }
01018         if (!any_images) {
01019             // Inherit the Earth cdrom (uses it as disk test)
01020             if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) {
01021                 reg_ah = 0;
01022                 CALLBACK_SCF(false);
01023                 return CBRET_NONE;
01024             }
01025         }
01026         if (driveInactive(drivenum)) {
01027             reg_ah = 0xff;
01028             CALLBACK_SCF(true);
01029             return CBRET_NONE;
01030         }
01031 
01032         segat = dap.seg;
01033         bufptr = dap.off;
01034         for(i=0;i<dap.num;i++) {
01035             last_status = imageDiskList[drivenum]->Read_AbsoluteSector(dap.sector+i, sectbuf);
01036 
01037             IDE_EmuINT13DiskReadByBIOS_LBA(reg_dl,dap.sector+i);
01038 
01039             if((last_status != 0x00) || (killRead)) {
01040                 LOG_MSG("Error in disk read");
01041                 killRead = false;
01042                 reg_ah = 0x04;
01043                 CALLBACK_SCF(true);
01044                 return CBRET_NONE;
01045             }
01046             for(t=0;t<512;t++) {
01047                 real_writeb(segat,bufptr,sectbuf[t]);
01048                 bufptr++;
01049             }
01050         }
01051         reg_ah = 0x00;
01052         CALLBACK_SCF(false);
01053         break;
01054     case 0x43: /* Extended Write Sectors to Drive */
01055         if(driveInactive(drivenum)) {
01056             reg_ah = 0xff;
01057             CALLBACK_SCF(true);
01058             return CBRET_NONE;
01059         }
01060 
01061         /* Read Disk Address Packet */
01062         readDAP(SegValue(ds),reg_si);
01063         bufptr = dap.off;
01064         for(i=0;i<dap.num;i++) {
01065             for(t=0;t<imageDiskList[drivenum]->getSectSize();t++) {
01066                 sectbuf[t] = real_readb(dap.seg,bufptr);
01067                 bufptr++;
01068             }
01069 
01070             last_status = imageDiskList[drivenum]->Write_AbsoluteSector(dap.sector+i, &sectbuf[0]);
01071             if(last_status != 0x00) {
01072                 CALLBACK_SCF(true);
01073                 return CBRET_NONE;
01074             }
01075         }
01076         reg_ah = 0x00;
01077         CALLBACK_SCF(false);
01078         break;
01079     case 0x48: { /* get drive parameters */
01080         uint16_t bufsz;
01081 
01082         if(driveInactive(drivenum)) {
01083             reg_ah = 0xff;
01084             CALLBACK_SCF(true);
01085             return CBRET_NONE;
01086         }
01087 
01088         segat = SegValue(ds);
01089         bufptr = reg_si;
01090         bufsz = real_readw(segat,bufptr+0);
01091         if (bufsz < 0x1A) {
01092             reg_ah = 0xff;
01093             CALLBACK_SCF(true);
01094             return CBRET_NONE;
01095         }
01096         if (bufsz > 0x1E) bufsz = 0x1E;
01097         else bufsz = 0x1A;
01098 
01099         Bit32u tmpheads, tmpcyl, tmpsect, tmpsize;
01100         imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize);
01101 
01102         real_writew(segat,bufptr+0x00,bufsz);
01103         real_writew(segat,bufptr+0x02,0x0003);  /* C/H/S valid, DMA boundary errors handled */
01104         real_writed(segat,bufptr+0x04,tmpcyl);
01105         real_writed(segat,bufptr+0x08,tmpheads);
01106         real_writed(segat,bufptr+0x0C,tmpsect);
01107         real_writed(segat,bufptr+0x10,tmpcyl*tmpheads*tmpsect);
01108         real_writed(segat,bufptr+0x14,0);
01109         real_writew(segat,bufptr+0x18,512);
01110         if (bufsz >= 0x1E)
01111             real_writed(segat,bufptr+0x1A,0xFFFFFFFF); /* no EDD information available */
01112 
01113         reg_ah = 0x00;
01114         CALLBACK_SCF(false);
01115         } break;
01116     default:
01117         LOG(LOG_BIOS,LOG_ERROR)("INT13: Function %x called on drive %x (dos drive %d)", (int)reg_ah, (int)reg_dl, (int)drivenum);
01118         reg_ah=0xff;
01119         CALLBACK_SCF(true);
01120     }
01121     return CBRET_NONE;
01122 }
01123 
01124 void CALLBACK_DeAllocate(Bitu in);
01125 
01126 void BIOS_UnsetupDisks(void) {
01127     if (call_int13 != 0) {
01128         CALLBACK_DeAllocate(call_int13);
01129         RealSetVec(0x13,0); /* zero INT 13h for now */
01130         call_int13 = 0;
01131     }
01132     if (diskparm0 != 0) {
01133         CALLBACK_DeAllocate(diskparm0);
01134         diskparm0 = 0;
01135     }
01136     if (diskparm1 != 0) {
01137         CALLBACK_DeAllocate(diskparm1);
01138         diskparm1 = 0;
01139     }
01140 }
01141 
01142 void BIOS_SetupDisks(void) {
01143     unsigned int i;
01144 
01145     if (IS_PC98_ARCH) {
01146         // TODO
01147         return;
01148     }
01149 
01150 /* TODO Start the time correctly */
01151     call_int13=CALLBACK_Allocate(); 
01152     CALLBACK_Setup(call_int13,&INT13_DiskHandler,CB_INT13,"Int 13 Bios disk");
01153     RealSetVec(0x13,CALLBACK_RealPointer(call_int13));
01154 
01155     //release the drives after a soft reset
01156     FreeBIOSDiskList();
01157 
01158     /* FIXME: Um... these aren't callbacks. Why are they allocated as callbacks? We have ROM general allocation now. */
01159     diskparm0 = CALLBACK_Allocate();
01160     CALLBACK_SetDescription(diskparm0,"BIOS Disk 0 parameter table");
01161     diskparm1 = CALLBACK_Allocate();
01162     CALLBACK_SetDescription(diskparm1,"BIOS Disk 1 parameter table");
01163     swapPosition = 0;
01164 
01165     RealSetVec(0x41,CALLBACK_RealPointer(diskparm0));
01166     RealSetVec(0x46,CALLBACK_RealPointer(diskparm1));
01167 
01168     PhysPt dp0physaddr=CALLBACK_PhysPointer(diskparm0);
01169     PhysPt dp1physaddr=CALLBACK_PhysPointer(diskparm1);
01170     for(i=0;i<16;i++) {
01171         phys_writeb(dp0physaddr+i,0);
01172         phys_writeb(dp1physaddr+i,0);
01173     }
01174 
01175     imgDTASeg = 0;
01176 
01177 /* Setup the Bios Area */
01178     mem_writeb(BIOS_HARDDISK_COUNT,2);
01179 
01180     killRead = false;
01181     swapping_requested = false;
01182 }
01183 
01184 // VFD *.FDD floppy disk format support
01185 
01186 Bit8u imageDiskVFD::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) {
01187     vfdentry *ent;
01188 
01189     if (req_sector_size == 0)
01190         req_sector_size = sector_size;
01191 
01192 //    LOG_MSG("VFD read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size);
01193 
01194     ent = findSector(head,cylinder,sector,req_sector_size);
01195     if (ent == NULL) return 0x05;
01196     if (ent->getSectorSize() != req_sector_size) return 0x05;
01197 
01198     if (ent->hasSectorData()) {
01199         fseek(diskimg,ent->data_offset,SEEK_SET);
01200         if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01201         if (fread(data,req_sector_size,1,diskimg) != 1) return 0x05;
01202         return 0;
01203     }
01204     else if (ent->hasFill()) {
01205         memset(data,ent->fillbyte,req_sector_size);
01206         return 0x00;
01207     }
01208 
01209     return 0x05;
01210 }
01211 
01212 Bit8u imageDiskVFD::Read_AbsoluteSector(Bit32u sectnum, void * data) {
01213     unsigned int c,h,s;
01214 
01215     if (sectors == 0 || heads == 0)
01216         return 0x05;
01217 
01218     s = (sectnum % sectors) + 1;
01219     h = (sectnum / sectors) % heads;
01220     c = (sectnum / sectors / heads);
01221     return Read_Sector(h,c,s,data);
01222 }
01223 
01224 imageDiskVFD::vfdentry *imageDiskVFD::findSector(Bit8u head,Bit8u track,Bit8u sector/*TODO: physical head?*/,unsigned int req_sector_size) {
01225     std::vector<imageDiskVFD::vfdentry>::iterator i = dents.begin();
01226     unsigned char szb=0xFF;
01227 
01228     if (req_sector_size == 0)
01229         req_sector_size = sector_size;
01230 
01231     if (req_sector_size != ~0U) {
01232         unsigned int c = req_sector_size;
01233         while (c >= 128U) {
01234             c >>= 1U;
01235             szb++;
01236         }
01237 
01238 //        LOG_MSG("req=%u c=%u szb=%u",req_sector_size,c,szb);
01239 
01240         if (szb > 8 || c != 64U)
01241             return NULL;
01242     }
01243 
01244     while (i != dents.end()) {
01245         imageDiskVFD::vfdentry &ent = *i;
01246 
01247         if (ent.head == head &&
01248             ent.track == track &&
01249             ent.sector == sector &&
01250             (ent.sizebyte == szb || req_sector_size == ~0U))
01251             return &(*i);
01252 
01253         i++;
01254     }
01255 
01256     return NULL;
01257 }
01258 
01259 Bit8u imageDiskVFD::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) {
01260     unsigned long new_offset;
01261     unsigned char tmp[12];
01262     vfdentry *ent;
01263 
01264 //    LOG_MSG("VFD write sector: CHS %u/%u/%u",cylinder,head,sector);
01265 
01266     if (req_sector_size == 0)
01267         req_sector_size = sector_size;
01268 
01269     ent = findSector(head,cylinder,sector,req_sector_size);
01270     if (ent == NULL) return 0x05;
01271     if (ent->getSectorSize() != req_sector_size) return 0x05;
01272 
01273     if (ent->hasSectorData()) {
01274         fseek(diskimg,ent->data_offset,SEEK_SET);
01275         if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01276         if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05;
01277         return 0;
01278     }
01279     else if (ent->hasFill()) {
01280         bool isfill = false;
01281 
01282         /* well, is the data provided one character repeated?
01283          * note the format cannot represent a fill byte of 0xFF */
01284         if (((unsigned char*)data)[0] != 0xFF) {
01285             unsigned int i=1;
01286 
01287             do {
01288                 if (((unsigned char*)data)[i] == ((unsigned char*)data)[0]) {
01289                     if ((++i) == req_sector_size) {
01290                         isfill = true;
01291                         break; // yes!
01292                     }
01293                 }
01294                 else {
01295                     break; // nope
01296                 }
01297             } while (1);
01298         }
01299 
01300         if (ent->entry_offset == 0) return 0x05;
01301 
01302         if (isfill) {
01303             fseek(diskimg,ent->entry_offset,SEEK_SET);
01304             if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05;
01305             if (fread(tmp,12,1,diskimg) != 1) return 0x05;
01306 
01307             tmp[0x04] = ((unsigned char*)data)[0]; // change the fill byte
01308 
01309             LOG_MSG("VFD write: 'fill' sector changing fill byte to 0x%x",tmp[0x04]);
01310 
01311             fseek(diskimg,ent->entry_offset,SEEK_SET);
01312             if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05;
01313             if (fwrite(tmp,12,1,diskimg) != 1) return 0x05;
01314         }
01315         else {
01316             fseek(diskimg,0,SEEK_END);
01317             new_offset = (unsigned long)ftell(diskimg);
01318 
01319             /* we have to change it from a fill sector to an actual sector */
01320             LOG_MSG("VFD write: changing 'fill' sector to one with data (data at %lu)",(unsigned long)new_offset);
01321 
01322             fseek(diskimg,ent->entry_offset,SEEK_SET);
01323             if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05;
01324             if (fread(tmp,12,1,diskimg) != 1) return 0x05;
01325 
01326             tmp[0x00] = ent->track;
01327             tmp[0x01] = ent->head;
01328             tmp[0x02] = ent->sector;
01329             tmp[0x03] = ent->sizebyte;
01330             tmp[0x04] = 0xFF; // no longer a fill byte
01331             tmp[0x05] = 0x00; // TODO ??
01332             tmp[0x06] = 0x00; // TODO ??
01333             tmp[0x07] = 0x00; // TODO ??
01334             *((uint32_t*)(tmp+8)) = new_offset;
01335             ent->fillbyte = 0xFF;
01336             ent->data_offset = (uint32_t)new_offset;
01337 
01338             fseek(diskimg,ent->entry_offset,SEEK_SET);
01339             if ((uint32_t)ftell(diskimg) != ent->entry_offset) return 0x05;
01340             if (fwrite(tmp,12,1,diskimg) != 1) return 0x05;
01341 
01342             fseek(diskimg,ent->data_offset,SEEK_SET);
01343             if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01344             if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05;
01345         }
01346     }
01347 
01348     return 0x05;
01349 }
01350 
01351 Bit8u imageDiskVFD::Write_AbsoluteSector(Bit32u sectnum,const void *data) {
01352     unsigned int c,h,s;
01353 
01354     if (sectors == 0 || heads == 0)
01355         return 0x05;
01356 
01357     s = (sectnum % sectors) + 1;
01358     h = (sectnum / sectors) % heads;
01359     c = (sectnum / sectors / heads);
01360     return Write_Sector(h,c,s,data);
01361 }
01362 
01363 imageDiskVFD::imageDiskVFD(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) : imageDisk(ID_VFD) {
01364     (void)isHardDisk;//UNUSED
01365     unsigned char tmp[16];
01366 
01367     heads = 1;
01368     cylinders = 0;
01369     image_base = 0;
01370     sectors = 0;
01371     active = false;
01372     sector_size = 0;
01373     reserved_cylinders = 0;
01374     diskSizeK = imgSizeK;
01375     diskimg = imgFile;
01376 
01377     if (imgName != NULL)
01378         diskname = (const char*)imgName;
01379 
01380     // NOTES:
01381     // 
01382     //  +0x000: "VFD1.00"
01383     //  +0x0DC: array of 12-byte entries each describing a sector
01384     //
01385     //  Each entry:
01386     //  +0x0: track
01387     //  +0x1: head
01388     //  +0x2: sector
01389     //  +0x3: sector size (128 << this byte)
01390     //  +0x4: fill byte, or 0xFF
01391     //  +0x5: unknown
01392     //  +0x6: unknown
01393     //  +0x7: unknown
01394     //  +0x8: absolute data offset (32-bit integer) or 0xFFFFFFFF if the entire sector is that fill byte
01395     fseek(diskimg,0,SEEK_SET);
01396     memset(tmp,0,8);
01397     fread(tmp,1,8,diskimg);
01398 
01399     if (!memcmp(tmp,"VFD1.",5)) {
01400         Bit8u i=0;
01401         bool founddisk = false;
01402         uint32_t stop_at = 0xC3FC;
01403         unsigned long entof;
01404 
01405         // load table.
01406         // we have to determine as we go where to stop reading.
01407         // the source of info I read assumes the whole header (and table)
01408         // is 0xC3FC bytes. I'm not inclined to assume that, so we go by
01409         // that OR the first sector offset whichever is smaller.
01410         // the table seems to trail off into a long series of 0xFF at the end.
01411         fseek(diskimg,0xDC,SEEK_SET);
01412         while ((entof=((unsigned long)ftell(diskimg)+12ul)) <= stop_at) {
01413             memset(tmp,0xFF,12);
01414             fread(tmp,12,1,diskimg);
01415 
01416             if (!memcmp(tmp,"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF",12))
01417                 continue;
01418             if (!memcmp(tmp,"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",12))
01419                 continue;
01420 
01421             struct vfdentry v;
01422 
01423             v.track = tmp[0];
01424             v.head = tmp[1];
01425             v.sector = tmp[2];
01426             v.sizebyte = tmp[3];
01427             v.fillbyte = tmp[4];
01428             v.data_offset = *((uint32_t*)(tmp+8));
01429             v.entry_offset = (uint32_t)entof;
01430 
01431             // maybe the table can end sooner than 0xC3FC?
01432             // if we see sectors appear at an offset lower than our stop_at point
01433             // then adjust the stop_at point. assume the table cannot mix with
01434             // sector data.
01435             if (v.hasSectorData()) {
01436                 if (stop_at > v.data_offset)
01437                     stop_at = v.data_offset;
01438             }
01439 
01440             dents.push_back(v);
01441 
01442             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",
01443                 v.track,
01444                 v.head,
01445                 v.sector,
01446                 v.getSectorSize(),
01447                 v.fillbyte,
01448                 v.hasSectorData(),
01449                 v.hasFill(),
01450                 (unsigned long)v.entry_offset,
01451                 (unsigned long)v.data_offset);
01452         }
01453 
01454         if (!dents.empty()) {
01455             /* okay, now to figure out what the geometry of the disk is.
01456              * we cannot just work from an "absolute" disk image model
01457              * because there's no VFD header to just say what the geometry is.
01458              * Like the IBM PC BIOS, we have to look at the disk and figure out
01459              * which geometry to apply to it, even if the FDD format allows
01460              * sectors on other tracks to have wild out of range sector, track,
01461              * and head numbers or odd sized sectors.
01462              *
01463              * First, determine sector size according to the boot sector. */
01464             vfdentry *ent;
01465 
01466             ent = findSector(/*head*/0,/*track*/0,/*sector*/1,~0U);
01467             if (ent != NULL) {
01468                 if (ent->sizebyte <= 3) /* x <= 1024 */
01469                     sector_size = ent->getSectorSize();
01470             }
01471 
01472             /* oh yeah right, sure.
01473              * I suppose you're one of those FDD images where the sector size is 128 bytes/sector
01474              * in the boot sector and the rest is 256 bytes/sector elsewhere. I have no idea why
01475              * but quite a few FDD images have this arrangement. */
01476             if (sector_size != 0 && sector_size < 512) {
01477                 ent = findSector(/*head*/0,/*track*/1,/*sector*/1,~0U);
01478                 if (ent != NULL) {
01479                     if (ent->sizebyte <= 3) { /* x <= 1024 */
01480                         unsigned int nsz = ent->getSectorSize();
01481                         if (sector_size != nsz)
01482                             LOG_MSG("VFD warning: sector size changes between track 0 and 1");
01483                         if (sector_size < nsz)
01484                             sector_size = nsz;
01485                     }
01486                 }
01487             }
01488 
01489             if (sector_size != 0) {
01490                 i=0;
01491                 while (DiskGeometryList[i].ksize != 0) {
01492                     diskGeo &diskent = DiskGeometryList[i];
01493 
01494                     if (diskent.bytespersect == sector_size) {
01495                         ent = findSector(0,0,diskent.secttrack);
01496                         if (ent != NULL) {
01497                             LOG_MSG("VFD disk probe: %u/%u/%u exists",0,0,diskent.secttrack);
01498                             if (sectors < diskent.secttrack)
01499                                 sectors = diskent.secttrack;
01500                         }
01501                     }
01502 
01503                     i++;
01504                 }
01505             }
01506 
01507             if (sector_size != 0 && sectors != 0) {
01508                 i=0;
01509                 while (DiskGeometryList[i].ksize != 0) {
01510                     diskGeo &diskent = DiskGeometryList[i];
01511 
01512                     if (diskent.bytespersect == sector_size && diskent.secttrack >= sectors) {
01513                         ent = findSector(0,diskent.cylcount-1,sectors);
01514                         if (ent != NULL) {
01515                             LOG_MSG("VFD disk probe: %u/%u/%u exists",0,diskent.cylcount-1,sectors);
01516                             if (cylinders < diskent.cylcount)
01517                                 cylinders = diskent.cylcount;
01518                         }
01519                     }
01520 
01521                     i++;
01522                 }
01523             }
01524 
01525             if (sector_size != 0 && sectors != 0 && cylinders != 0) {
01526                 ent = findSector(1,0,sectors);
01527                 if (ent != NULL) {
01528                     LOG_MSG("VFD disk probe: %u/%u/%u exists",1,0,sectors);
01529                     heads = 2;
01530                 }
01531             }
01532 
01533             // TODO: drive_fat.cpp should use an extension to this API to allow changing the sectors/track
01534             //       according to what it reads from the MS-DOS BIOS parameter block, just like real MS-DOS.
01535             //       This would allow better representation of strange disk formats such as the "extended"
01536             //       floppy format that Microsoft used to use for Word 95 and Windows 95 install floppies.
01537 
01538             LOG_MSG("VFD geometry detection: C/H/S %u/%u/%u %u bytes/sector",
01539                 cylinders, heads, sectors, sector_size);
01540 
01541             if (sector_size != 0 && sectors != 0 && cylinders != 0 && heads != 0)
01542                 founddisk = true;
01543 
01544             if(!founddisk) {
01545                 active = false;
01546             } else {
01547                 incrementFDD();
01548             }
01549         }
01550     }
01551 }
01552 
01553 imageDiskVFD::~imageDiskVFD() {
01554     if(diskimg != NULL) {
01555         fclose(diskimg);
01556         diskimg=NULL; 
01557     }
01558 }
01559 
01560 // D88 *.D88 floppy disk format support
01561 
01562 enum {
01563     D88_TRACKMAX        = 164,
01564     D88_HEADERSIZE      = 0x20 + (D88_TRACKMAX * 4)
01565 };
01566 
01567 #pragma pack(push,1)
01568 typedef struct D88HEAD {
01569     char            fd_name[17];                // +0x00 Disk Name
01570     unsigned char   reserved1[9];               // +0x11 Reserved
01571     unsigned char   protect;                    // +0x1A Write Protect bit:4
01572     unsigned char   fd_type;                    // +0x1B Disk Format
01573     uint32_t        fd_size;                    // +0x1C Disk Size
01574     uint32_t        trackp[D88_TRACKMAX];       // +0x20 <array of DWORDs>     164 x 4 = 656 = 0x290
01575 } D88HEAD;                                      // =0x2B0 total
01576 
01577 typedef struct D88SEC {
01578     unsigned char   c;                          // +0x00
01579     unsigned char   h;                          // +0x01
01580     unsigned char   r;                          // +0x02
01581     unsigned char   n;                          // +0x03
01582     uint16_t        sectors;                    // +0x04 Sector Count
01583     unsigned char   mfm_flg;                    // +0x06 sides
01584     unsigned char   del_flg;                    // +0x07 DELETED DATA
01585     unsigned char   stat;                       // +0x08 STATUS (FDC ret)
01586     unsigned char   seektime;                   // +0x09 Seek Time
01587     unsigned char   reserved[3];                // +0x0A Reserved
01588     unsigned char   rpm_flg;                    // +0x0D rpm          0:1.2  1:1.44
01589     uint16_t        size;                       // +0x0E Sector Size
01590                                                 // <sector contents follow>
01591 } D88SEC;                                       // =0x10 total
01592 #pragma pack(pop)
01593 
01594 Bit8u imageDiskD88::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) {
01595     vfdentry *ent;
01596 
01597     if (req_sector_size == 0)
01598         req_sector_size = sector_size;
01599 
01600 //    LOG_MSG("D88 read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size);
01601 
01602     ent = findSector(head,cylinder,sector,req_sector_size);
01603     if (ent == NULL) return 0x05;
01604     if (ent->getSectorSize() != req_sector_size) return 0x05;
01605 
01606     fseek(diskimg,ent->data_offset,SEEK_SET);
01607     if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01608     if (fread(data,req_sector_size,1,diskimg) != 1) return 0x05;
01609     return 0;
01610 }
01611 
01612 Bit8u imageDiskD88::Read_AbsoluteSector(Bit32u sectnum, void * data) {
01613     unsigned int c,h,s;
01614 
01615     if (sectors == 0 || heads == 0)
01616         return 0x05;
01617 
01618     s = (sectnum % sectors) + 1;
01619     h = (sectnum / sectors) % heads;
01620     c = (sectnum / sectors / heads);
01621     return Read_Sector(h,c,s,data);
01622 }
01623 
01624 imageDiskD88::vfdentry *imageDiskD88::findSector(Bit8u head,Bit8u track,Bit8u sector/*TODO: physical head?*/,unsigned int req_sector_size) {
01625     if ((size_t)track >= dents.size())
01626         return NULL;
01627 
01628     std::vector<imageDiskD88::vfdentry>::iterator i = dents.begin();
01629 
01630     if (req_sector_size == 0)
01631         req_sector_size = sector_size;
01632 
01633     while (i != dents.end()) {
01634         imageDiskD88::vfdentry &ent = *i;
01635 
01636         if (ent.head == head &&
01637             ent.track == track &&
01638             ent.sector == sector &&
01639             (ent.sector_size == req_sector_size || req_sector_size == ~0U))
01640             return &(*i);
01641 
01642         i++;
01643     }
01644 
01645     return NULL;
01646 }
01647 
01648 Bit8u imageDiskD88::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) {
01649     vfdentry *ent;
01650 
01651     if (req_sector_size == 0)
01652         req_sector_size = sector_size;
01653 
01654 //    LOG_MSG("D88 read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size);
01655 
01656     ent = findSector(head,cylinder,sector,req_sector_size);
01657     if (ent == NULL) return 0x05;
01658     if (ent->getSectorSize() != req_sector_size) return 0x05;
01659 
01660     fseek(diskimg,ent->data_offset,SEEK_SET);
01661     if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01662     if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05;
01663     return 0;
01664 }
01665 
01666 Bit8u imageDiskD88::Write_AbsoluteSector(Bit32u sectnum,const void *data) {
01667     unsigned int c,h,s;
01668 
01669     if (sectors == 0 || heads == 0)
01670         return 0x05;
01671 
01672     s = (sectnum % sectors) + 1;
01673     h = (sectnum / sectors) % heads;
01674     c = (sectnum / sectors / heads);
01675     return Write_Sector(h,c,s,data);
01676 }
01677 
01678 imageDiskD88::imageDiskD88(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) : imageDisk(ID_D88) {
01679     (void)isHardDisk;//UNUSED
01680     D88HEAD head;
01681 
01682     fd_type_major = DISKTYPE_2D;
01683     fd_type_minor = 0;
01684 
01685     assert(sizeof(D88HEAD) == 0x2B0);
01686     assert(sizeof(D88SEC) == 0x10);
01687 
01688     heads = 0;
01689     cylinders = 0;
01690     image_base = 0;
01691     sectors = 0;
01692     active = false;
01693     sector_size = 0;
01694     reserved_cylinders = 0;
01695     diskSizeK = imgSizeK;
01696     diskimg = imgFile;
01697     active = false;
01698 
01699     if (imgName != NULL)
01700         diskname = (const char*)imgName;
01701 
01702     // NOTES:
01703     // 
01704     //  +0x000: D88 header
01705     //  +0x020: Offset of D88 tracks, per track
01706     //  +0x2B0: <begin data>
01707     //
01708     // Track offsets are sequential, always
01709     //
01710     // Each track is an array of:
01711     //
01712     //  ENTRY:
01713     //   <D88 sector head>
01714     //   <sector contents>
01715     //
01716     // Array of ENTRY from offset until next track
01717     fseek(diskimg,0,SEEK_END);
01718     off_t fsz = ftell(diskimg);
01719 
01720     fseek(diskimg,0,SEEK_SET);
01721     if (fread(&head,sizeof(head),1,diskimg) != 1) return;
01722 
01723     // validate fd_size
01724     if ((uint32_t)host_readd((ConstHostPt)(&head.fd_size)) > (uint32_t)fsz) return;
01725 
01726     fd_type_major = head.fd_type >> 4U;
01727     fd_type_minor = head.fd_type & 0xFU;
01728 
01729     // validate that none of the track offsets extend past the file
01730     {
01731         for (unsigned int i=0;i < D88_TRACKMAX;i++) {
01732             uint32_t trackoff = host_readd((ConstHostPt)(&head.trackp[i]));
01733 
01734             if (trackoff == 0) continue;
01735 
01736             if ((trackoff + 16U) > (uint32_t)fsz) {
01737                 LOG_MSG("D88: track starts past end of file");
01738                 return;
01739             }
01740         }
01741     }
01742 
01743     // read each track
01744     for (unsigned int track=0;track < D88_TRACKMAX;track++) {
01745         uint32_t trackoff = host_readd((ConstHostPt)(&head.trackp[track]));
01746 
01747         if (trackoff != 0) {
01748             fseek(diskimg, trackoff, SEEK_SET);
01749             if (ftell(diskimg) != trackoff) continue;
01750 
01751             D88SEC s;
01752             unsigned int count = 0;
01753 
01754             do {
01755                 if (fread(&s,sizeof(s),1,diskimg) != 1) break;
01756 
01757                 uint16_t sector_count = host_readw((ConstHostPt)(&s.sectors));
01758                 uint16_t sector_size = host_readw((ConstHostPt)(&s.size));
01759 
01760                 if (sector_count == 0U || sector_size < 128U) break;
01761                 if (sector_count > 128U || sector_size > 16384U) break;
01762                 if (s.n > 8U) s.n = 8U;
01763 
01764                 vfdentry vent;
01765                 vent.sector_size = 128 << s.n;
01766                 vent.data_offset = (uint32_t)ftell(diskimg);
01767                 vent.entry_offset = vent.data_offset - (uint32_t)16;
01768                 vent.track = s.c;
01769                 vent.head = s.h;
01770                 vent.sector = s.r;
01771 
01772                 LOG_MSG("D88: trackindex=%u C/H/S/sz=%u/%u/%u/%u data-at=0x%lx",
01773                     track,vent.track,vent.head,vent.sector,vent.sector_size,(unsigned long)vent.data_offset);
01774 
01775                 dents.push_back(vent);
01776                 if ((++count) >= sector_count) break;
01777 
01778                 fseek(diskimg, sector_size, SEEK_CUR);
01779             } while (1);
01780         }
01781     }
01782 
01783     if (!dents.empty()) {
01784         /* okay, now to figure out what the geometry of the disk is.
01785          * we cannot just work from an "absolute" disk image model
01786          * because there's no D88 header to just say what the geometry is.
01787          * Like the IBM PC BIOS, we have to look at the disk and figure out
01788          * which geometry to apply to it, even if the FDD format allows
01789          * sectors on other tracks to have wild out of range sector, track,
01790          * and head numbers or odd sized sectors.
01791          *
01792          * First, determine sector size according to the boot sector. */
01793         bool founddisk = false;
01794         vfdentry *ent;
01795 
01796         ent = findSector(/*head*/0,/*track*/0,/*sector*/1,~0U);
01797         if (ent != NULL) {
01798             if (ent->getSectorSize() <= 1024) /* x <= 1024 */
01799                 sector_size = ent->getSectorSize();
01800         }
01801 
01802         /* oh yeah right, sure.
01803          * I suppose you're one of those FDD images where the sector size is 128 bytes/sector
01804          * in the boot sector and the rest is 256 bytes/sector elsewhere. I have no idea why
01805          * but quite a few FDD images have this arrangement. */
01806         if (sector_size != 0 && sector_size < 512) {
01807             ent = findSector(/*head*/0,/*track*/1,/*sector*/1,~0U);
01808             if (ent != NULL) {
01809                 if (ent->getSectorSize() <= 1024) { /* x <= 1024 */
01810                     unsigned int nsz = ent->getSectorSize();
01811                     if (sector_size != nsz)
01812                         LOG_MSG("D88 warning: sector size changes between track 0 and 1");
01813                     if (sector_size < nsz)
01814                         sector_size = nsz;
01815                 }
01816             }
01817         }
01818 
01819         if (sector_size != 0) {
01820             unsigned int i = 0;
01821             while (DiskGeometryList[i].ksize != 0) {
01822                 diskGeo &diskent = DiskGeometryList[i];
01823 
01824                 if (diskent.bytespersect == sector_size) {
01825                     ent = findSector(0,0,diskent.secttrack);
01826                     if (ent != NULL) {
01827                         LOG_MSG("D88 disk probe: %u/%u/%u exists",0,0,diskent.secttrack);
01828                         if (sectors < diskent.secttrack)
01829                             sectors = diskent.secttrack;
01830                     }
01831                 }
01832 
01833                 i++;
01834             }
01835         }
01836 
01837         if (sector_size != 0 && sectors != 0) {
01838             unsigned int i = 0;
01839             while (DiskGeometryList[i].ksize != 0) {
01840                 diskGeo &diskent = DiskGeometryList[i];
01841 
01842                 if (diskent.bytespersect == sector_size && diskent.secttrack >= sectors) {
01843                     ent = findSector(0,diskent.cylcount-1,sectors);
01844                     if (ent != NULL) {
01845                         LOG_MSG("D88 disk probe: %u/%u/%u exists",0,diskent.cylcount-1,sectors);
01846                         if (cylinders < diskent.cylcount)
01847                             cylinders = diskent.cylcount;
01848                     }
01849                 }
01850 
01851                 i++;
01852             }
01853         }
01854 
01855         if (sector_size != 0 && sectors != 0 && cylinders != 0) {
01856             ent = findSector(1,0,sectors);
01857             if (ent != NULL) {
01858                 LOG_MSG("D88 disk probe: %u/%u/%u exists",1,0,sectors);
01859                 heads = 2;
01860             }
01861         }
01862 
01863         // TODO: drive_fat.cpp should use an extension to this API to allow changing the sectors/track
01864         //       according to what it reads from the MS-DOS BIOS parameter block, just like real MS-DOS.
01865         //       This would allow better representation of strange disk formats such as the "extended"
01866         //       floppy format that Microsoft used to use for Word 95 and Windows 95 install floppies.
01867 
01868         LOG_MSG("D88 geometry detection: C/H/S %u/%u/%u %u bytes/sector",
01869                 cylinders, heads, sectors, sector_size);
01870 
01871         if (sector_size != 0 && sectors != 0 && cylinders != 0 && heads != 0)
01872             founddisk = true;
01873 
01874         if(!founddisk) {
01875             active = false;
01876         } else {
01877             incrementFDD();
01878         }
01879     }
01880 }
01881 
01882 imageDiskD88::~imageDiskD88() {
01883     if(diskimg != NULL) {
01884         fclose(diskimg);
01885         diskimg=NULL; 
01886     }
01887 }
01888 
01889 /*--------------------------------*/
01890 
01891 Bit8u imageDiskNFD::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data,unsigned int req_sector_size) {
01892     vfdentry *ent;
01893 
01894     if (req_sector_size == 0)
01895         req_sector_size = sector_size;
01896 
01897 //    LOG_MSG("NFD read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size);
01898 
01899     ent = findSector(head,cylinder,sector,req_sector_size);
01900     if (ent == NULL) return 0x05;
01901     if (ent->getSectorSize() != req_sector_size) return 0x05;
01902 
01903     fseek(diskimg,ent->data_offset,SEEK_SET);
01904     if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01905     if (fread(data,req_sector_size,1,diskimg) != 1) return 0x05;
01906     return 0;
01907 }
01908 
01909 Bit8u imageDiskNFD::Read_AbsoluteSector(Bit32u sectnum, void * data) {
01910     unsigned int c,h,s;
01911 
01912     if (sectors == 0 || heads == 0)
01913         return 0x05;
01914 
01915     s = (sectnum % sectors) + 1;
01916     h = (sectnum / sectors) % heads;
01917     c = (sectnum / sectors / heads);
01918     return Read_Sector(h,c,s,data);
01919 }
01920 
01921 imageDiskNFD::vfdentry *imageDiskNFD::findSector(Bit8u head,Bit8u track,Bit8u sector/*TODO: physical head?*/,unsigned int req_sector_size) {
01922     if ((size_t)track >= dents.size())
01923         return NULL;
01924 
01925     std::vector<imageDiskNFD::vfdentry>::iterator i = dents.begin();
01926 
01927     if (req_sector_size == 0)
01928         req_sector_size = sector_size;
01929 
01930     while (i != dents.end()) {
01931         imageDiskNFD::vfdentry &ent = *i;
01932 
01933         if (ent.head == head &&
01934             ent.track == track &&
01935             ent.sector == sector &&
01936             (ent.sector_size == req_sector_size || req_sector_size == ~0U))
01937             return &(*i);
01938 
01939         i++;
01940     }
01941 
01942     return NULL;
01943 }
01944 
01945 Bit8u imageDiskNFD::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,const void * data,unsigned int req_sector_size) {
01946     vfdentry *ent;
01947 
01948     if (req_sector_size == 0)
01949         req_sector_size = sector_size;
01950 
01951 //    LOG_MSG("NFD read sector: CHS %u/%u/%u sz=%u",cylinder,head,sector,req_sector_size);
01952 
01953     ent = findSector(head,cylinder,sector,req_sector_size);
01954     if (ent == NULL) return 0x05;
01955     if (ent->getSectorSize() != req_sector_size) return 0x05;
01956 
01957     fseek(diskimg,ent->data_offset,SEEK_SET);
01958     if ((uint32_t)ftell(diskimg) != ent->data_offset) return 0x05;
01959     if (fwrite(data,req_sector_size,1,diskimg) != 1) return 0x05;
01960     return 0;
01961 }
01962 
01963 Bit8u imageDiskNFD::Write_AbsoluteSector(Bit32u sectnum,const void *data) {
01964     unsigned int c,h,s;
01965 
01966     if (sectors == 0 || heads == 0)
01967         return 0x05;
01968 
01969     s = (sectnum % sectors) + 1;
01970     h = (sectnum / sectors) % heads;
01971     c = (sectnum / sectors / heads);
01972     return Write_Sector(h,c,s,data);
01973 }
01974 
01975 imageDiskNFD::imageDiskNFD(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk, unsigned int revision) : imageDisk(ID_NFD) {
01976     (void)isHardDisk;//UNUSED
01977     union {
01978         NFDHDR head;
01979         NFDHDRR1 headr1;
01980     }; // these occupy the same location of memory
01981 
01982     assert(sizeof(NFDHDR) == 0x120);
01983     assert(sizeof(NFDHDRR1) == 0x3C0);
01984     assert(sizeof(NFDHDR_ENTRY) == 0x10);
01985 
01986     heads = 0;
01987     cylinders = 0;
01988     image_base = 0;
01989     sectors = 0;
01990     active = false;
01991     sector_size = 0;
01992     reserved_cylinders = 0;
01993     diskSizeK = imgSizeK;
01994     diskimg = imgFile;
01995     active = false;
01996 
01997     if (imgName != NULL)
01998         diskname = (const char*)imgName;
01999 
02000     // NOTES:
02001     // 
02002     //  +0x000: NFD header
02003     //  +0x020: Offset of NFD tracks, per track
02004     //  +0x2B0: <begin data>
02005     //
02006     // Track offsets are sequential, always
02007     //
02008     // Each track is an array of:
02009     //
02010     //  ENTRY:
02011     //   <NFD sector head>
02012     //   <sector contents>
02013     //
02014     // Array of ENTRY from offset until next track
02015     fseek(diskimg,0,SEEK_END);
02016     off_t fsz = ftell(diskimg);
02017 
02018     fseek(diskimg,0,SEEK_SET);
02019     if (revision == 0) {
02020         if (fread(&head,sizeof(head),1,diskimg) != 1) return;
02021     }
02022     else if (revision == 1) {
02023         if (fread(&headr1,sizeof(headr1),1,diskimg) != 1) return;
02024     }
02025     else {
02026         abort();
02027     }
02028 
02029     // validate fd_size
02030     if ((uint32_t)host_readd((ConstHostPt)(&head.headersize)) < sizeof(head)) return;
02031     if ((uint32_t)host_readd((ConstHostPt)(&head.headersize)) > (uint32_t)fsz) return;
02032 
02033     unsigned int data_offset = host_readd((ConstHostPt)(&head.headersize));
02034 
02035     std::vector< std::pair<uint32_t,NFDHDR_ENTRY> > seclist;
02036 
02037     if (revision == 0) {
02038         unsigned int secents = (host_readd((ConstHostPt)(&head.headersize)) - sizeof(head)) / sizeof(NFDHDR_ENTRY);
02039         if (secents == 0) return;
02040         secents--;
02041         if (secents == 0) return;
02042 
02043         for (unsigned int i=0;i < secents;i++) {
02044             uint32_t ofs = (uint32_t)ftell(diskimg);
02045             NFDHDR_ENTRY e;
02046 
02047             if (fread(&e,sizeof(e),1,diskimg) != 1) return;
02048             seclist.push_back( std::pair<uint32_t,NFDHDR_ENTRY>(ofs,e) );
02049 
02050             if (e.log_cyl == 0xFF || e.log_head == 0xFF || e.log_rec == 0xFF || e.sec_len_pow2 > 7)
02051                 continue;
02052 
02053             LOG_MSG("NFD %u/%u: ofs=%lu data=%lu cyl=%u head=%u sec=%u len=%u",
02054                     (unsigned int)i,
02055                     (unsigned int)secents,
02056                     (unsigned long)ofs,
02057                     (unsigned long)data_offset,
02058                     e.log_cyl,
02059                     e.log_head,
02060                     e.log_rec,
02061                     128 << e.sec_len_pow2);
02062 
02063             vfdentry vent;
02064             vent.sector_size = 128 << e.sec_len_pow2;
02065             vent.data_offset = (uint32_t)data_offset;
02066             vent.entry_offset = (uint32_t)ofs;
02067             vent.track = e.log_cyl;
02068             vent.head = e.log_head;
02069             vent.sector = e.log_rec;
02070             dents.push_back(vent);
02071 
02072             data_offset += 128u << e.sec_len_pow2;
02073             if (data_offset > (unsigned int)fsz) return;
02074         }
02075     }
02076     else {
02077         /* R1 has an array of offsets to where each tracks begins.
02078          * The end of the track is an entry like 0x1A 0x00 0x00 0x00 0x00 0x00 0x00 .... */
02079         /* The R1 images I have as reference always have offsets in ascending order. */
02080         for (unsigned int ti=0;ti < 164;ti++) {
02081             uint32_t trkoff = host_readd((ConstHostPt)(&headr1.trackheads[ti]));
02082 
02083             if (trkoff == 0) break;
02084 
02085             fseek(diskimg,trkoff,SEEK_SET);
02086             if (ftell(diskimg) != trkoff) return;
02087 
02088             NFDHDR_ENTRY e;
02089 
02090             // track id
02091             if (fread(&e,sizeof(e),1,diskimg) != 1) return;
02092             unsigned int sectors = host_readw((ConstHostPt)(&e) + 0);
02093             unsigned int diagcount = host_readw((ConstHostPt)(&e) + 2);
02094 
02095             LOG_MSG("NFD R1 track ent %u offset %lu sectors %u diag %u",ti,(unsigned long)trkoff,sectors,diagcount);
02096 
02097             for (unsigned int s=0;s < sectors;s++) {
02098                 uint32_t ofs = (uint32_t)ftell(diskimg);
02099 
02100                 if (fread(&e,sizeof(e),1,diskimg) != 1) return;
02101 
02102                 LOG_MSG("NFD %u/%u: ofs=%lu data=%lu cyl=%u head=%u sec=%u len=%u rep=%u",
02103                         (unsigned int)s,
02104                         (unsigned int)sectors,
02105                         (unsigned long)ofs,
02106                         (unsigned long)data_offset,
02107                         e.log_cyl,
02108                         e.log_head,
02109                         e.log_rec,
02110                         128 << e.sec_len_pow2,
02111                         e.byRetry);
02112 
02113                 vfdentry vent;
02114                 vent.sector_size = 128 << e.sec_len_pow2;
02115                 vent.data_offset = (uint32_t)data_offset;
02116                 vent.entry_offset = (uint32_t)ofs;
02117                 vent.track = e.log_cyl;
02118                 vent.head = e.log_head;
02119                 vent.sector = e.log_rec;
02120                 dents.push_back(vent);
02121 
02122                 data_offset += 128u << e.sec_len_pow2;
02123                 if (data_offset > (unsigned int)fsz) return;
02124             }
02125 
02126             for (unsigned int d=0;d < diagcount;d++) {
02127                 if (fread(&e,sizeof(e),1,diskimg) != 1) return;
02128 
02129                 unsigned int retry = e.byRetry;
02130                 unsigned int len = host_readd((ConstHostPt)(&e) + 10);
02131 
02132                 LOG_MSG("NFD diag %u/%u: retry=%u len=%u data=%lu",d,diagcount,retry,len,(unsigned long)data_offset);
02133 
02134                 data_offset += (1+retry) * len;
02135             }
02136         }
02137     }
02138 
02139     if (!dents.empty()) {
02140         /* okay, now to figure out what the geometry of the disk is.
02141          * we cannot just work from an "absolute" disk image model
02142          * because there's no NFD header to just say what the geometry is.
02143          * Like the IBM PC BIOS, we have to look at the disk and figure out
02144          * which geometry to apply to it, even if the FDD format allows
02145          * sectors on other tracks to have wild out of range sector, track,
02146          * and head numbers or odd sized sectors.
02147          *
02148          * First, determine sector size according to the boot sector. */
02149         bool founddisk = false;
02150         vfdentry *ent;
02151 
02152         ent = findSector(/*head*/0,/*track*/0,/*sector*/1,~0U);
02153         if (ent != NULL) {
02154             if (ent->getSectorSize() <= 1024) /* x <= 1024 */
02155                 sector_size = ent->getSectorSize();
02156         }
02157 
02158         /* oh yeah right, sure.
02159          * I suppose you're one of those FDD images where the sector size is 128 bytes/sector
02160          * in the boot sector and the rest is 256 bytes/sector elsewhere. I have no idea why
02161          * but quite a few FDD images have this arrangement. */
02162         if (sector_size != 0 && sector_size < 512) {
02163             ent = findSector(/*head*/0,/*track*/1,/*sector*/1,~0U);
02164             if (ent != NULL) {
02165                 if (ent->getSectorSize() <= 1024) { /* x <= 1024 */
02166                     unsigned int nsz = ent->getSectorSize();
02167                     if (sector_size != nsz)
02168                         LOG_MSG("NFD warning: sector size changes between track 0 and 1");
02169                     if (sector_size < nsz)
02170                         sector_size = nsz;
02171                 }
02172             }
02173         }
02174 
02175         if (sector_size != 0) {
02176             unsigned int i = 0;
02177             while (DiskGeometryList[i].ksize != 0) {
02178                 diskGeo &diskent = DiskGeometryList[i];
02179 
02180                 if (diskent.bytespersect == sector_size) {
02181                     ent = findSector(0,0,diskent.secttrack);
02182                     if (ent != NULL) {
02183                         LOG_MSG("NFD disk probe: %u/%u/%u exists",0,0,diskent.secttrack);
02184                         if (sectors < diskent.secttrack)
02185                             sectors = diskent.secttrack;
02186                     }
02187                 }
02188 
02189                 i++;
02190             }
02191         }
02192 
02193         if (sector_size != 0 && sectors != 0) {
02194             unsigned int i = 0;
02195             while (DiskGeometryList[i].ksize != 0) {
02196                 diskGeo &diskent = DiskGeometryList[i];
02197 
02198                 if (diskent.bytespersect == sector_size && diskent.secttrack >= sectors) {
02199                     ent = findSector(0,diskent.cylcount-1,sectors);
02200                     if (ent != NULL) {
02201                         LOG_MSG("NFD disk probe: %u/%u/%u exists",0,diskent.cylcount-1,sectors);
02202                         if (cylinders < diskent.cylcount)
02203                             cylinders = diskent.cylcount;
02204                     }
02205                 }
02206 
02207                 i++;
02208             }
02209         }
02210 
02211         if (sector_size != 0 && sectors != 0 && cylinders != 0) {
02212             ent = findSector(1,0,sectors);
02213             if (ent != NULL) {
02214                 LOG_MSG("NFD disk probe: %u/%u/%u exists",1,0,sectors);
02215                 heads = 2;
02216             }
02217         }
02218 
02219         // TODO: drive_fat.cpp should use an extension to this API to allow changing the sectors/track
02220         //       according to what it reads from the MS-DOS BIOS parameter block, just like real MS-DOS.
02221         //       This would allow better representation of strange disk formats such as the "extended"
02222         //       floppy format that Microsoft used to use for Word 95 and Windows 95 install floppies.
02223 
02224         LOG_MSG("NFD geometry detection: C/H/S %u/%u/%u %u bytes/sector",
02225                 cylinders, heads, sectors, sector_size);
02226 
02227         if (sector_size != 0 && sectors != 0 && cylinders != 0 && heads != 0)
02228             founddisk = true;
02229 
02230         if(!founddisk) {
02231             active = false;
02232         } else {
02233             incrementFDD();
02234         }
02235     }
02236 }
02237 
02238 imageDiskNFD::~imageDiskNFD() {
02239     if(diskimg != NULL) {
02240         fclose(diskimg);
02241         diskimg=NULL; 
02242     }
02243 }
02244