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