DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/dos/dos_ioctl.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 
00020 #include <string.h>
00021 #include "dosbox.h"
00022 #include "callback.h"
00023 #include "mem.h"
00024 #include "regs.h"
00025 #include "bios_disk.h"
00026 #include "dos_inc.h"
00027 #include "drives.h"
00028 
00029 bool DOS_IOCTL_AX440D_CH08(Bit8u drive,bool query) {
00030     PhysPt ptr  = SegPhys(ds)+reg_dx;
00031     switch (reg_cl) {
00032         case 0x40:              /* Set device parameters */
00033                         {
00034                                 if (strncmp(Drives[drive]->GetInfo(),"fatDrive ",9)) {
00035                                         DOS_SetError(DOSERR_ACCESS_DENIED);
00036                                         return false;
00037                                 }                               
00038                                 fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
00039                                 if (fdp == NULL || fdp->readonly) {
00040                                         DOS_SetError(DOSERR_ACCESS_DENIED);
00041                                         return false;
00042                                 }
00043 
00044                                 if (query) break;
00045                                 
00046                                 FAT_BootSector::bpb_union_t bpb=fdp->GetBPB();
00047                                 if (fdp->loadedDisk != NULL)
00048                                         fdp->loadedDisk->cylinders = mem_readw(ptr+4);                           // number of cylinders
00049 
00050                                 if (mem_readw(ptr+0xd) == 0 && mem_readw(ptr+0xf) == 0 && mem_readw(ptr+0x12) == 0) { // FAT32 BPB?
00051                                         bpb.v32.BPB_BytsPerSec = mem_readw(ptr+7);                   // bytes per sector (Win3 File Mgr. uses it)
00052                                         bpb.v32.BPB_SecPerClus = mem_readb(ptr+9);                   // sectors per cluster
00053                                         bpb.v32.BPB_RsvdSecCnt = mem_readw(ptr+0xa);                 // number of reserved sectors
00054                                         bpb.v32.BPB_NumFATs = mem_readb(ptr+0xc);                    // number of FATs
00055                                         bpb.v32.BPB_RootEntCnt = mem_readw(ptr+0xd);                 // number of root entries (Fake, the real BPB value is zero)
00056                                         bpb.v32.BPB_TotSec16 = mem_readw(ptr+0xf);                   // number of small sectors (always zero on BPB and returned by Win98)
00057                                         bpb.v32.BPB_Media = mem_readb(ptr+0x11);                     // media type
00058                                         bpb.v32.BPB_FATSz32 = (uint16_t)mem_readw(ptr+0x12);         // sectors per FAT (FIXME: What does Win98 do if this value > 0xFFFF?)
00059                                         bpb.v32.BPB_SecPerTrk = (uint16_t)mem_readw(ptr+0x14);       // sectors per track
00060                                         bpb.v32.BPB_NumHeads = (uint16_t)mem_readw(ptr+0x16);        // number of heads
00061                                         bpb.v32.BPB_HiddSec = (uint32_t)mem_readd(ptr+0x18);         // number of hidden sectors
00062                                         bpb.v32.BPB_TotSec32 = (uint32_t)mem_readd(ptr+0x1c);        // number of big sectors
00063                                 } else {
00064                                         bpb.v.BPB_BytsPerSec = mem_readw(ptr+7);                     // bytes per sector (Win3 File Mgr. uses it)
00065                                         bpb.v.BPB_SecPerClus = mem_readb(ptr+9);                     // sectors per cluster
00066                                         bpb.v.BPB_RsvdSecCnt = mem_readw(ptr+0xa);                   // number of reserved sectors
00067                                         bpb.v.BPB_NumFATs = mem_readb(ptr+0xc);                      // number of FATs
00068                                         bpb.v.BPB_RootEntCnt = mem_readw(ptr+0xd);                               // number of root entries
00069                                         bpb.v.BPB_TotSec16 = mem_readw(ptr+0xf);                     // number of small sectors
00070                                         bpb.v.BPB_Media = mem_readb(ptr+0x11);                       // media type
00071                                         bpb.v.BPB_FATSz16 = (uint16_t)mem_readw(ptr+0x12);           // sectors per FAT
00072                                         bpb.v.BPB_SecPerTrk = (uint16_t)mem_readw(ptr+0x14);         // sectors per track
00073                                         bpb.v.BPB_NumHeads = (uint16_t)mem_readw(ptr+0x16);          // number of heads
00074                                         bpb.v.BPB_HiddSec = (uint32_t)mem_readd(ptr+0x18);           // number of hidden sectors
00075                                         bpb.v.BPB_TotSec32 = (uint32_t)mem_readd(ptr+0x1c);          // number of big sectors
00076                                 }
00077                                 fdp->SetBPB(bpb);
00078                                 break;
00079                         }
00080         case 0x60:              /* Get device parameters */
00081                         if (query) break;
00082                         {
00083                 //mem_writeb(ptr+0,0);                                  // special functions (call value)
00084                 mem_writeb(ptr+1,(drive>=2)?0x05:0x07); // type: hard disk(5), 1.44 floppy(7)
00085                 mem_writew(ptr+2,(drive>=2)?0x01:0x00); // attributes: bit 0 set for nonremovable
00086                 mem_writew(ptr+4,(drive>=2)?0x3FF:0x50);// number of cylinders
00087                 mem_writeb(ptr+6,0x00);                                 // media type (00=other type)
00088                 // bios parameter block following
00089                 fatDrive *fdp;
00090                 FAT_BootSector::bpb_union_t bpb;
00091                 bool usereal=false;
00092                 if (!strncmp(Drives[drive]->GetInfo(),"fatDrive ",9)) {
00093                     fdp = dynamic_cast<fatDrive*>(Drives[drive]);
00094                     if (fdp != NULL) {
00095                         bpb=fdp->GetBPB();
00096                         if (bpb.v.BPB_BytsPerSec && bpb.v.BPB_Media)
00097                             usereal=true;
00098                     }
00099                 }
00100                 if (usereal) {
00101                     if (fdp->loadedDisk != NULL)
00102                         mem_writew(ptr+4,fdp->loadedDisk->cylinders);                           // number of cylinders
00103 
00104                     if (bpb.is_fat32()) {
00105                         /* Windows 98 behavior: Some of the FAT32 BPB fields are translated into FAT16 BPB fields even though those fields are zero in the actual BPB */
00106                         mem_writew(ptr+7,bpb.v32.BPB_BytsPerSec);                   // bytes per sector (Win3 File Mgr. uses it)
00107                         mem_writeb(ptr+9,bpb.v32.BPB_SecPerClus);                   // sectors per cluster
00108                         mem_writew(ptr+0xa,bpb.v32.BPB_RsvdSecCnt);                 // number of reserved sectors
00109                         mem_writeb(ptr+0xc,bpb.v32.BPB_NumFATs);                    // number of FATs
00110                         mem_writew(ptr+0xd,0x200);                                  // number of root entries (Fake, the real BPB value is zero)
00111                         mem_writew(ptr+0xf,0);                                      // number of small sectors (always zero on BPB and returned by Win98)
00112                         mem_writeb(ptr+0x11,bpb.v32.BPB_Media);                     // media type
00113                         mem_writew(ptr+0x12,(uint16_t)bpb.v32.BPB_FATSz32);         // sectors per FAT (FIXME: What does Win98 do if this value > 0xFFFF?)
00114                         mem_writew(ptr+0x14,(uint16_t)bpb.v32.BPB_SecPerTrk);       // sectors per track
00115                         mem_writew(ptr+0x16,(uint16_t)bpb.v32.BPB_NumHeads);        // number of heads
00116                         mem_writed(ptr+0x18,(uint32_t)bpb.v32.BPB_HiddSec);         // number of hidden sectors
00117                         mem_writed(ptr+0x1c,(uint32_t)bpb.v32.BPB_TotSec32);        // number of big sectors
00118                     }
00119                     else {
00120                         mem_writew(ptr+7,bpb.v.BPB_BytsPerSec);                     // bytes per sector (Win3 File Mgr. uses it)
00121                         mem_writeb(ptr+9,bpb.v.BPB_SecPerClus);                     // sectors per cluster
00122                         mem_writew(ptr+0xa,bpb.v.BPB_RsvdSecCnt);                   // number of reserved sectors
00123                         mem_writeb(ptr+0xc,bpb.v.BPB_NumFATs);                      // number of FATs
00124                         mem_writew(ptr+0xd,bpb.v.BPB_RootEntCnt);                               // number of root entries
00125                         mem_writew(ptr+0xf,bpb.v.BPB_TotSec16);                     // number of small sectors
00126                         mem_writeb(ptr+0x11,bpb.v.BPB_Media);                       // media type
00127                         mem_writew(ptr+0x12,(uint16_t)bpb.v.BPB_FATSz16);           // sectors per FAT
00128                         mem_writew(ptr+0x14,(uint16_t)bpb.v.BPB_SecPerTrk);         // sectors per track
00129                         mem_writew(ptr+0x16,(uint16_t)bpb.v.BPB_NumHeads);          // number of heads
00130                         mem_writed(ptr+0x18,(uint32_t)bpb.v.BPB_HiddSec);           // number of hidden sectors
00131                         mem_writed(ptr+0x1c,(uint32_t)bpb.v.BPB_TotSec32);          // number of big sectors
00132                     }
00133                 } else {
00134                     mem_writew(ptr+7,0x0200);                                                                   // bytes per sector (Win3 File Mgr. uses it)
00135                     mem_writew(ptr+9,(drive>=2)?0x20:0x01);                                             // sectors per cluster
00136                     mem_writew(ptr+0xa,0x0001);                                                                 // number of reserved sectors
00137                     mem_writew(ptr+0xc,0x02);                                                                   // number of FATs
00138                     mem_writew(ptr+0xd,(drive>=2)?0x0200:0x00E0);                               // number of root entries
00139                     mem_writew(ptr+0xf,(drive>=2)?0x0000:0x0B40);                               // number of small sectors
00140                     mem_writew(ptr+0x11,(drive>=2)?0xF8:0xF0);                                  // media type
00141                     mem_writew(ptr+0x12,(drive>=2)?0x00C9:0x0009);                              // sectors per FAT
00142                     mem_writew(ptr+0x14,(drive>=2)?0x003F:0x0012);                              // sectors per track
00143                     mem_writew(ptr+0x16,(drive>=2)?0x10:0x02);                                  // number of heads
00144                     mem_writed(ptr+0x18,0);                                                                     // number of hidden sectors
00145                     mem_writed(ptr+0x1c,(drive>=2)?0x31F11:0x00);                               // number of big sectors
00146                 }
00147                 for (int i=0x20; i<0x22; i++)
00148                     mem_writeb(ptr+i,0);
00149                 break;
00150             }
00151         case 0x42:  /* Format and verify logical device track (FORMAT.COM) */
00152             {
00153                 /* 01h    WORD    number of disk head
00154                  * 03h    WORD    number of disk cylinder
00155                  * ---BYTE 00h bit 1 set---
00156                  * 05h    WORD    number of tracks to format */
00157                 Bit8u flags = mem_readb(ptr+0);
00158                 Bit16u head = mem_readw(ptr+1);
00159                 Bit16u cyl = mem_readw(ptr+3);
00160                 Bit16u ntracks = (flags & 0x1) ? mem_readw(ptr+5) : 1;
00161                 Bit16u sect = 0;
00162 
00163                 fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
00164                 if (fdp == NULL || fdp->readonly) {
00165                     DOS_SetError(DOSERR_ACCESS_DENIED);
00166                     return false;
00167                 }
00168 
00169                 if (query) break;
00170 
00171                 /* BUT: These are C/H/S values relative to the partition!
00172                  * FIXME: MS-DOS may not adjust sector value, or maybe it does...
00173                  * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */
00174                 {
00175                     Bit32u adj = fdp->GetPartitionOffset();
00176                     sect += adj % fdp->loadedDisk->sectors;
00177                     adj /= fdp->loadedDisk->sectors;
00178                     head += adj % fdp->loadedDisk->heads;
00179                     adj /= fdp->loadedDisk->heads;
00180                     cyl += adj;
00181 
00182                     while (sect >= fdp->loadedDisk->sectors) {
00183                         sect -= fdp->loadedDisk->sectors;
00184                         head++;
00185                     }
00186                     while (head >= fdp->loadedDisk->heads) {
00187                         head -= fdp->loadedDisk->heads;
00188                         cyl++;
00189                     }
00190 
00191                     /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */
00192                     sect++;
00193                 }
00194 
00195                 // STUB!
00196                 LOG(LOG_IOCTL,LOG_DEBUG)("DOS:IOCTL Call 0D:42 Drive %2X pretending to format device track C/H/S=%u/%u/%u ntracks=%u",drive,cyl,head,sect,ntracks);
00197             }
00198             break;
00199         case 0x62:      /* Verify logical device track (FORMAT.COM) */
00200             {
00201                 /* 01h    WORD    number of disk head
00202                  * 03h    WORD    number of disk cylinder
00203                  * 05h    WORD    number of tracks to verify */
00204                 Bit16u head = mem_readw(ptr+1);
00205                 Bit16u cyl = mem_readw(ptr+3);
00206                 Bit16u ntracks = mem_readw(ptr+5);
00207                 Bit16u sect = 0;
00208 
00209                 fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
00210                 if (fdp == NULL) {
00211                     DOS_SetError(DOSERR_ACCESS_DENIED);
00212                     return false;
00213                 }
00214 
00215                 if (query) break;
00216 
00217                 /* BUT: These are C/H/S values relative to the partition!
00218                  * FIXME: MS-DOS may not adjust sector value, or maybe it does...
00219                  * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */
00220                 {
00221                     Bit32u adj = fdp->GetPartitionOffset();
00222                     sect += adj % fdp->loadedDisk->sectors;
00223                     adj /= fdp->loadedDisk->sectors;
00224                     head += adj % fdp->loadedDisk->heads;
00225                     adj /= fdp->loadedDisk->heads;
00226                     cyl += adj;
00227 
00228                     while (sect >= fdp->loadedDisk->sectors) {
00229                         sect -= fdp->loadedDisk->sectors;
00230                         head++;
00231                     }
00232                     while (head >= fdp->loadedDisk->heads) {
00233                         head -= fdp->loadedDisk->heads;
00234                         cyl++;
00235                     }
00236 
00237                     /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */
00238                     sect++;
00239                 }
00240 
00241                 // STUB!
00242                 LOG(LOG_IOCTL,LOG_DEBUG)("DOS:IOCTL Call 0D:62 Drive %2X pretending to verify device track C/H/S=%u/%u/%u ntracks=%u",drive,cyl,head,sect,ntracks);
00243             }
00244             break;
00245         case 0x46:      /* Set volume serial number */
00246                         if (query) break;
00247                         {
00248                                 fatDrive* fdp = dynamic_cast<fatDrive*>(Drives[drive]);
00249                                 if (fdp == NULL || fdp->readonly) {
00250                                         DOS_SetError(DOSERR_ACCESS_DENIED);
00251                                         return false;
00252                                 }
00253 
00254                                 FAT_BootSector::bpb_union_t bpb=fdp->GetBPB();
00255                                 unsigned long serial_number=mem_readd(ptr+2)?mem_readd(ptr+2):0x1234;
00256                                 if (bpb.is_fat32())
00257                                         bpb.v32.BS_VolID=serial_number;
00258                                 else
00259                                         bpb.v.BPB_VolID=serial_number;
00260                                 fdp->SetBPB(bpb);
00261                         }
00262             break;
00263         case 0x66:      /* Get volume serial number */
00264                         if (query) break;
00265                         {
00266                 char const* bufin=Drives[drive]->GetLabel();
00267                 char buffer[11];memset(buffer,' ',11);
00268 
00269                 char const* find_ext=strchr(bufin,'.');
00270                 if (find_ext) {
00271                     Bitu size=(Bitu)(find_ext-bufin);
00272                     if (size>8) size=8;
00273                     memcpy(buffer,bufin,size);
00274                     find_ext++;
00275                     memcpy(buffer+8,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext)); 
00276                 } else {
00277                     memcpy(buffer,bufin,(strlen(bufin) > 8) ? 8 : strlen(bufin));
00278                 }
00279 
00280                 char buf2[8]={ 'F','A','T','1','6',' ',' ',' '};
00281                 if(drive<2) buf2[4] = '2'; //FAT12 for floppies
00282 
00283                 //mem_writew(ptr+0,0);                  //Info level (call value)
00284                                 unsigned long serial_number=0x1234;
00285                                 if (!strncmp(Drives[drive]->GetInfo(),"fatDrive ",9)) {
00286                                         fatDrive* fdp = dynamic_cast<fatDrive*>(Drives[drive]);
00287                                         if (fdp != NULL) serial_number=fdp->GetSerial();
00288                                 }
00289 #if defined (WIN32)
00290                                 if (!strncmp(Drives[drive]->GetInfo(),"local ",6) || !strncmp(Drives[drive]->GetInfo(),"CDRom ",6)) {
00291                                         localDrive* ldp = !strncmp(Drives[drive]->GetInfo(),"local ",6)?dynamic_cast<localDrive*>(Drives[drive]):dynamic_cast<cdromDrive*>(Drives[drive]);
00292                                         if (ldp != NULL) serial_number=ldp->GetSerial();
00293                                 }
00294 #endif
00295                 mem_writed(ptr+2,serial_number);//Serial number
00296                 MEM_BlockWrite(ptr+6,buffer,11);//volumename
00297                 MEM_BlockWrite(ptr+0x11,buf2,8);//filesystem
00298             }
00299             break;
00300         case 0x41:  /* Write logical device track */
00301                         {
00302                 fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
00303                 if (fdp == NULL || fdp->readonly) {
00304                     DOS_SetError(DOSERR_ACCESS_DENIED);
00305                     return false;
00306                 }
00307 
00308                 Bit8u sectbuf[SECTOR_SIZE_MAX];
00309 
00310                 if (fdp->loadedDisk == NULL) {
00311                     DOS_SetError(DOSERR_ACCESS_DENIED);
00312                     return false;
00313                 }
00314 
00315                 if (query) break;
00316 
00317                 /* (RBIL) [http://www.ctyme.com/intr/rb-2896.htm]
00318                  * Offset  Size    Description     (Table 01562)
00319                  * 00h    BYTE    special functions (reserved, must be zero)
00320                  * 01h    WORD    number of disk head
00321                  * 03h    WORD    number of disk cylinder
00322                  * 05h    WORD    number of first sector to read/write
00323                  * 07h    WORD    number of sectors
00324                  * 09h    DWORD   transfer address */
00325                 Bit16u head = mem_readw(ptr+1);
00326                 Bit16u cyl = mem_readw(ptr+3);
00327                 Bit16u sect = mem_readw(ptr+5);
00328                 Bit16u nsect = mem_readw(ptr+7);
00329                 Bit32u xfer_addr = mem_readd(ptr+9);
00330                 PhysPt xfer_ptr = ((xfer_addr>>16u)<<4u)+(xfer_addr&0xFFFFu);
00331                 Bit16u sectsize = fdp->loadedDisk->getSectSize();
00332 
00333                 /* BUT: These are C/H/S values relative to the partition!
00334                  * FIXME: MS-DOS may not adjust sector value, or maybe it does...
00335                  * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */
00336                 {
00337                     Bit32u adj = fdp->GetPartitionOffset();
00338                     sect += adj % fdp->loadedDisk->sectors;
00339                     adj /= fdp->loadedDisk->sectors;
00340                     head += adj % fdp->loadedDisk->heads;
00341                     adj /= fdp->loadedDisk->heads;
00342                     cyl += adj;
00343 
00344                     while (sect >= fdp->loadedDisk->sectors) {
00345                         sect -= fdp->loadedDisk->sectors;
00346                         head++;
00347                     }
00348                     while (head >= fdp->loadedDisk->heads) {
00349                         head -= fdp->loadedDisk->heads;
00350                         cyl++;
00351                     }
00352 
00353                     /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */
00354                     sect++;
00355                 }
00356 
00357                 LOG(LOG_IOCTL,LOG_DEBUG)("DOS:IOCTL Call 0D:41 Write Logical Device Track from Drive %2X C/H/S=%u/%u/%u num=%u from %04x:%04x sz=%u",
00358                         drive,cyl,head,sect,nsect,xfer_addr >> 16,xfer_addr & 0xFFFF,sectsize);
00359 
00360                 while (nsect > 0) {
00361                     MEM_BlockRead(xfer_ptr,sectbuf,sectsize);
00362 
00363                     Bit8u status = fdp->loadedDisk->Write_Sector(head,cyl,sect,sectbuf);
00364                     if (status != 0) {
00365                         LOG(LOG_IOCTL,LOG_DEBUG)("IOCTL 0D:61 write error at C/H/S %u/%u/%u",cyl,head,sect);
00366                         DOS_SetError(DOSERR_ACCESS_DENIED);//FIXME
00367                         return false;
00368                     }
00369 
00370                     xfer_ptr += sectsize;
00371                     nsect--;
00372                     sect++;
00373                 }
00374             }
00375             break;
00376         case 0x61:  /* Read logical device track */
00377             {
00378                 fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
00379                 if (fdp == NULL) {
00380                     DOS_SetError(DOSERR_ACCESS_DENIED);
00381                     return false;
00382                 }
00383 
00384                 Bit8u sectbuf[SECTOR_SIZE_MAX];
00385 
00386                 if (fdp->loadedDisk == NULL) {
00387                     DOS_SetError(DOSERR_ACCESS_DENIED);
00388                     return false;
00389                 }
00390 
00391                 if (query) break;
00392 
00393                 /* (RBIL) [http://www.ctyme.com/intr/rb-2896.htm]
00394                  * Offset  Size    Description     (Table 01562)
00395                  * 00h    BYTE    special functions (reserved, must be zero)
00396                  * 01h    WORD    number of disk head
00397                  * 03h    WORD    number of disk cylinder
00398                  * 05h    WORD    number of first sector to read/write
00399                  * 07h    WORD    number of sectors
00400                  * 09h    DWORD   transfer address */
00401                 Bit16u head = mem_readw(ptr+1);
00402                 Bit16u cyl = mem_readw(ptr+3);
00403                 Bit16u sect = mem_readw(ptr+5);
00404                 Bit16u nsect = mem_readw(ptr+7);
00405                 Bit32u xfer_addr = mem_readd(ptr+9);
00406                 PhysPt xfer_ptr = ((xfer_addr>>16u)<<4u)+(xfer_addr&0xFFFFu);
00407                 Bit16u sectsize = fdp->loadedDisk->getSectSize();
00408 
00409                 /* BUT: These are C/H/S values relative to the partition!
00410                  * FIXME: MS-DOS may not adjust sector value, or maybe it does...
00411                  * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */
00412                 {
00413                     Bit32u adj = fdp->GetPartitionOffset();;
00414                     sect += adj % fdp->loadedDisk->sectors;
00415                     adj /= fdp->loadedDisk->sectors;
00416                     head += adj % fdp->loadedDisk->heads;
00417                     adj /= fdp->loadedDisk->heads;
00418                     cyl += adj;
00419 
00420                     while (sect >= fdp->loadedDisk->sectors) {
00421                         sect -= fdp->loadedDisk->sectors;
00422                         head++;
00423                     }
00424                     while (head >= fdp->loadedDisk->heads) {
00425                         head -= fdp->loadedDisk->heads;
00426                         cyl++;
00427                     }
00428 
00429                     /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */
00430                     sect++;
00431                 }
00432 
00433                 LOG(LOG_IOCTL,LOG_DEBUG)("DOS:IOCTL Call 0D:61 Read Logical Device Track from Drive %2X C/H/S=%u/%u/%u num=%u to %04x:%04x sz=%u",
00434                         drive,cyl,head,sect,nsect,xfer_addr >> 16,xfer_addr & 0xFFFF,sectsize);
00435 
00436                 while (nsect > 0) {
00437                     Bit8u status = fdp->loadedDisk->Read_Sector(head,cyl,sect,sectbuf);
00438                     if (status != 0) {
00439                         LOG(LOG_IOCTL,LOG_DEBUG)("IOCTL 0D:61 read error at C/H/S %u/%u/%u",cyl,head,sect);
00440                         DOS_SetError(DOSERR_ACCESS_DENIED);//FIXME
00441                         return false;
00442                     }
00443 
00444                     MEM_BlockWrite(xfer_ptr,sectbuf,sectsize);
00445                     xfer_ptr += sectsize;
00446                     nsect--;
00447                     sect++;
00448                 }
00449             }
00450             break;
00451         case 0x4A:
00452         case 0x4B:
00453         case 0x6A:
00454         case 0x6B:
00455                         if (query) break;
00456                         LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call 0D:%2X Drive %2X volume/drive locking IOCTL, faking it",reg_cl,drive);
00457             break;
00458                 case 0x67: /* Get access flag (whether allowed by driver) */
00459                         if (query) break;
00460                         /* In DOSBox-X, disk access is always allowed.
00461                          * Real MS-DOS might be more restrictive, especially Windows 95 which requires volume locking before disk access is allowed */
00462                         /* FDISK.EXE needs this IOCTL to determine whether it can read the partition and therefore whether the "system type" is "FAT16", "FAT12", "unknown" etc. */
00463                         /* ptr+0 = special function (always zero)
00464                          * ptr+1 = return whether access allowed */
00465                         mem_writeb(ptr+1,0x01);
00466                         break;
00467         default:
00468             LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL %s %2X:%2X Drive %2X unhandled (CH=08h)",query?"Query":"Call",reg_al,reg_cl,drive);
00469             DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00470             return false;
00471     }
00472     reg_ax=0;
00473     return true;
00474 }
00475 
00476 bool DOS_IOCTL_AX440D_CH48(Bit8u drive,bool query) {
00477     PhysPt ptr  = SegPhys(ds)+reg_dx;
00478     switch (reg_cl) {
00479         case 0x40:              /* Set device parameters */
00480                         {
00481                                 if (strncmp(Drives[drive]->GetInfo(),"fatDrive ",9)) {
00482                                         DOS_SetError(DOSERR_ACCESS_DENIED);
00483                                         return false;
00484                                 }
00485                                 fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]);
00486                                 if (fdp == NULL || fdp->readonly) {
00487                                         DOS_SetError(DOSERR_ACCESS_DENIED);
00488                                         return false;
00489                                 }
00490 
00491                                 if (query) break;
00492                                 
00493                                 FAT_BootSector::bpb_union_t bpb=fdp->GetBPB();                          
00494                                 if (fdp->loadedDisk != NULL)
00495                                         fdp->loadedDisk->cylinders = mem_readw(ptr+4);               // number of cylinders
00496 
00497                                 if (mem_readw(ptr+0xd) == 0 && mem_readw(ptr+0xf) == 0 && mem_readw(ptr+0x12) == 0) { // FAT32 BPB?
00498                                         bpb.v.BPB_BytsPerSec = mem_readw(ptr+7);                     // bytes per sector (Win3 File Mgr. uses it)
00499                                         bpb.v.BPB_SecPerClus = mem_readb(ptr+9);                     // sectors per cluster
00500                                         bpb.v.BPB_RsvdSecCnt = mem_readw(ptr+0xa);                   // number of reserved sectors
00501                                         bpb.v.BPB_NumFATs = mem_readb(ptr+0xc);                      // number of FATs
00502                                         bpb.v.BPB_RootEntCnt = mem_readw(ptr+0xd);                               // number of root entries
00503                                         bpb.v.BPB_TotSec16 = mem_readw(ptr+0xf);                     // number of small sectors
00504                                         bpb.v.BPB_Media = mem_readb(ptr+0x11);                       // media type
00505                                         bpb.v.BPB_FATSz16 = (uint16_t)mem_readw(ptr+0x12);           // sectors per FAT
00506                                         bpb.v.BPB_SecPerTrk = (uint16_t)mem_readw(ptr+0x14);         // sectors per track
00507                                         bpb.v.BPB_NumHeads = (uint16_t)mem_readw(ptr+0x16);          // number of heads
00508                                         bpb.v.BPB_HiddSec = (uint32_t)mem_readd(ptr+0x18);           // number of hidden sectors
00509                                         bpb.v.BPB_TotSec32 = (uint32_t)mem_readd(ptr+0x1c);          // number of big sectors
00510                                         bpb.v32.BPB_FATSz32 = (uint32_t)mem_readd(ptr+0x20);         // sectors per FAT
00511                                         bpb.v32.BPB_ExtFlags = (uint16_t)mem_readw(ptr+0x24);
00512                                         bpb.v32.BPB_FSVer = (uint16_t)mem_readw(ptr+0x26);
00513                                         bpb.v32.BPB_RootClus = (uint32_t)mem_readd(ptr+0x28);
00514                                         bpb.v32.BPB_FSInfo = (uint16_t)mem_readw(ptr+0x2C);
00515                                         bpb.v32.BPB_BkBootSec = (uint16_t)mem_readw(ptr+0x2E);
00516                                         fdp->SetBPB(bpb);
00517                                 } else {
00518                                         DOS_SetError(DOSERR_ACCESS_DENIED);
00519                                         return false;
00520                                 }
00521                                 break;
00522                         }
00523         case 0x60:              /* Get device parameters */
00524                         if (query) break;
00525                         {
00526                 //mem_writeb(ptr+0,0);                                  // special functions (call value)
00527                 mem_writeb(ptr+1,(drive>=2)?0x05:0x07); // type: hard disk(5), 1.44 floppy(7)
00528                 mem_writew(ptr+2,(drive>=2)?0x01:0x00); // attributes: bit 0 set for nonremovable
00529                 mem_writew(ptr+4,(drive>=2)?0x3FF:0x50);// num of cylinders
00530                 mem_writeb(ptr+6,0x00);                                 // media type (00=other type)
00531                 // bios parameter block following
00532                 fatDrive *fdp;
00533                 FAT_BootSector::bpb_union_t bpb;
00534                 bool usereal=false;
00535                 if (!strncmp(Drives[drive]->GetInfo(),"fatDrive ",9)) {
00536                     fdp = dynamic_cast<fatDrive*>(Drives[drive]);
00537                     if (fdp != NULL) {
00538                         bpb=fdp->GetBPB();
00539                         if (bpb.v.BPB_BytsPerSec && bpb.v.BPB_Media)
00540                             usereal=true;
00541                     }
00542                 }
00543                 if (usereal) {
00544                     if (fdp->loadedDisk != NULL)
00545                         mem_writew(ptr+4,fdp->loadedDisk->cylinders);           // num of cylinders
00546 
00547                     if (bpb.is_fat32()) {
00548                         mem_writew(ptr+7,bpb.v.BPB_BytsPerSec);                     // bytes per sector (Win3 File Mgr. uses it)
00549                         mem_writeb(ptr+9,bpb.v.BPB_SecPerClus);                     // sectors per cluster
00550                         mem_writew(ptr+0xa,bpb.v.BPB_RsvdSecCnt);                   // number of reserved sectors
00551                         mem_writeb(ptr+0xc,bpb.v.BPB_NumFATs);                      // number of FATs
00552                         mem_writew(ptr+0xd,bpb.v.BPB_RootEntCnt);                               // number of root entries
00553                         mem_writew(ptr+0xf,bpb.v.BPB_TotSec16);                     // number of small sectors
00554                         mem_writeb(ptr+0x11,bpb.v.BPB_Media);                       // media type
00555                         mem_writew(ptr+0x12,(uint16_t)bpb.v.BPB_FATSz16);           // sectors per FAT
00556                         mem_writew(ptr+0x14,(uint16_t)bpb.v.BPB_SecPerTrk);         // sectors per track
00557                         mem_writew(ptr+0x16,(uint16_t)bpb.v.BPB_NumHeads);          // number of heads
00558                         mem_writed(ptr+0x18,(uint32_t)bpb.v.BPB_HiddSec);           // number of hidden sectors
00559                         mem_writed(ptr+0x1c,(uint32_t)bpb.v.BPB_TotSec32);          // number of big sectors
00560                         mem_writed(ptr+0x20,(uint32_t)bpb.v32.BPB_FATSz32);         // sectors per FAT
00561                         mem_writew(ptr+0x24,(uint16_t)bpb.v32.BPB_ExtFlags);
00562                         mem_writew(ptr+0x26,(uint16_t)bpb.v32.BPB_FSVer);
00563                         mem_writed(ptr+0x28,(uint32_t)bpb.v32.BPB_RootClus);
00564                         mem_writew(ptr+0x2C,(uint16_t)bpb.v32.BPB_FSInfo);
00565                         mem_writew(ptr+0x2E,(uint16_t)bpb.v32.BPB_BkBootSec);
00566                     }
00567                     else {
00568                         DOS_SetError(DOSERR_ACCESS_DENIED);
00569                         return false;
00570                     }
00571                 } else {
00572                     DOS_SetError(DOSERR_ACCESS_DENIED);
00573                     return false;
00574                 }
00575                 break;
00576             }
00577         case 0x42:
00578         case 0x46:
00579         case 0x4A:
00580         case 0x4B:
00581         case 0x61:
00582         case 0x62:
00583         case 0x66:
00584         case 0x6A:
00585         case 0x6B:
00586             return DOS_IOCTL_AX440D_CH08(drive,query);
00587         default:
00588             LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call %02X:%2X Drive %2X unhandled (CH=48h)",reg_al,reg_cl,drive);
00589             DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00590             return false;
00591     }
00592     reg_ax=0;
00593     return true;
00594 }
00595 
00596 bool DOS_IOCTL(void) {
00597         Bitu handle=0;Bit8u drive=0;
00598         /* calls 0-4,6,7,10,12,16 use a file handle */
00599         if ((reg_al<4) || (reg_al==0x06) || (reg_al==0x07) || (reg_al==0x0a) || (reg_al==0x0c) || (reg_al==0x10)) {
00600                 handle=RealHandle(reg_bx);
00601                 if (handle>=DOS_FILES) {
00602                         DOS_SetError(DOSERR_INVALID_HANDLE);
00603                         return false;
00604                 }
00605                 if (!Files[handle]) {
00606                         DOS_SetError(DOSERR_INVALID_HANDLE);
00607                         return false;
00608                 }
00609         } else if (reg_al<0x12) {                               /* those use a diskdrive except 0x0b */
00610                 if (reg_al!=0x0b) {
00611                         drive=reg_bl;
00612                         if ((reg_al==0x0D||reg_al==0x11) && (reg_cl==0x4B||reg_cl==0x6B)) drive=reg_bh;
00613                         if (!drive) drive = DOS_GetDefaultDrive();else drive--;
00614                         if( (drive >= 2) && !(( drive < DOS_DRIVES ) && Drives[drive]) ) {
00615                                 DOS_SetError(DOSERR_INVALID_DRIVE);
00616                                 return false;
00617                         }
00618                 }
00619         } else {
00620                 LOG(LOG_DOSMISC,LOG_ERROR)("DOS:IOCTL Call %2X unhandled",reg_al);
00621                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00622                 return false;
00623         }
00624         switch(reg_al) {
00625         case 0x00:              /* Get Device Information */
00626                 if (Files[handle]->GetInformation() & 0x8000) { //Check for device
00627                         reg_dx=Files[handle]->GetInformation();
00628                 } else {
00629                         Bit8u hdrive=Files[handle]->GetDrive();
00630                         if (hdrive==0xff) {
00631                                 LOG(LOG_IOCTL,LOG_NORMAL)("00:No drive set");
00632                                 hdrive=2;       // defaulting to C:
00633                         }
00634                         /* return drive number in lower 5 bits for block devices */
00635                         reg_dx=(Files[handle]->GetInformation()&0xffe0)|hdrive;
00636                 }
00637                 reg_ax=reg_dx; //Destroyed officially
00638                 return true;
00639         case 0x01:              /* Set Device Information */
00640                 if (reg_dh != 0) {
00641                         DOS_SetError(DOSERR_DATA_INVALID);
00642                         return false;
00643                 } else {
00644                         if (Files[handle]->GetInformation() & 0x8000) { //Check for device
00645                                 reg_al=(Bit8u)(Files[handle]->GetInformation() & 0xff);
00646                         } else {
00647                                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00648                                 return false;
00649                         }
00650                 }
00651                 return true;
00652         case 0x02:              /* Read from Device Control Channel */
00653                 if (Files[handle]->GetInformation() & 0xc000) {
00654                         /* is character device with IOCTL support */
00655                         PhysPt bufptr=PhysMake(SegValue(ds),reg_dx);
00656                         Bit16u retcode=0;
00657                         if (((DOS_Device*)(Files[handle]))->ReadFromControlChannel(bufptr,reg_cx,&retcode)) {
00658                                 reg_ax=retcode;
00659                                 return true;
00660                         }
00661                 }
00662                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00663                 return false;
00664         case 0x03:              /* Write to Device Control Channel */
00665                 if (Files[handle]->GetInformation() & 0xc000) {
00666                         /* is character device with IOCTL support */
00667                         PhysPt bufptr=PhysMake(SegValue(ds),reg_dx);
00668                         Bit16u retcode=0;
00669                         if (((DOS_Device*)(Files[handle]))->WriteToControlChannel(bufptr,reg_cx,&retcode)) {
00670                                 reg_ax=retcode;
00671                                 return true;
00672                         }
00673                 }
00674                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00675                 return false;
00676         case 0x06:      /* Get Input Status */
00677                 if (Files[handle]->GetInformation() & 0x8000) {         //Check for device
00678                         reg_al=(Files[handle]->GetInformation() & 0x40) ? 0x0 : 0xff;
00679                 } else { // FILE
00680                         Bit32u oldlocation=0;
00681                         Files[handle]->Seek(&oldlocation, DOS_SEEK_CUR);
00682                         Bit32u endlocation=0;
00683                         Files[handle]->Seek(&endlocation, DOS_SEEK_END);
00684                         if(oldlocation < endlocation){//Still data available
00685                                 reg_al=0xff;
00686                         } else {
00687                                 reg_al=0x0; //EOF or beyond
00688                         }
00689                         Files[handle]->Seek(&oldlocation, DOS_SEEK_SET); //restore filelocation
00690                         LOG(LOG_IOCTL,LOG_NORMAL)("06:Used Get Input Status on regular file with handle %d",(int)handle);
00691                 }
00692                 return true;
00693         case 0x07:              /* Get Output Status */
00694                 LOG(LOG_IOCTL,LOG_NORMAL)("07:Fakes output status is ready for handle %d",(int)handle);
00695                 reg_al=0xff;
00696                 return true;
00697         case 0x08:              /* Check if block device removable */
00698                 /* cdrom drives and drive a&b are removable */
00699                 if (drive < 2) {
00700                         if (Drives[drive])
00701                                 reg_ax=0;
00702                         else {
00703                                 DOS_SetError(DOSERR_INVALID_DRIVE);
00704                                 return false;
00705                         }
00706                 } else if (!Drives[drive]->isRemovable()) reg_ax=1;
00707                 else {
00708                         DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00709                         return false;
00710                 }
00711                 return true;
00712         case 0x09:              /* Check if block device remote */
00713                 if ((drive >= 2) && Drives[drive]->isRemote()) {
00714                         reg_dx=0x1000;  // device is remote
00715                         // undocumented bits always clear
00716                 } else {
00717                         reg_dx=0x0802;  // Open/Close supported; 32bit access supported (any use? fixes Fable installer)
00718                         // undocumented bits from device attribute word
00719                         // TODO Set bit 9 on drives that don't support direct I/O
00720                 }
00721                 reg_ax=0x300;
00722                 return true;
00723         case 0x0A:              /* Is Device of Handle Remote? */
00724                 reg_dx=0x8000;
00725                 LOG(LOG_IOCTL,LOG_NORMAL)("0A:Faked output: device of handle %d is remote",(int)handle);
00726                 return true;
00727         case 0x0B:              /* Set sharing retry count */
00728                 if (reg_dx==0) {
00729                         DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00730                         return false;
00731                 }
00732                 return true;
00733         case 0x0D:              /* Generic block device request */
00734         case 0x11:              /* query generic ioctl capability */
00735                 {
00736                         if (drive < 2 && !Drives[drive]) {
00737                                 DOS_SetError(DOSERR_ACCESS_DENIED);
00738                                 return false;
00739                         }
00740                         if (Drives[drive]->isRemovable()) {
00741                                 LOG(LOG_IOCTL,LOG_DEBUG)("Attempt IOCTL AX=%04x CX=%04x on removable drive",reg_ax,reg_cx);
00742                                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00743                                 return false;
00744                         }
00745                         if (reg_ch == 0x08) {
00746                                 return DOS_IOCTL_AX440D_CH08(drive,reg_al==0x11);
00747                         }
00748                         else if (reg_ch == 0x48) {
00749                                 return DOS_IOCTL_AX440D_CH48(drive,reg_al==0x11); // Same functions as CH=08h but for FAT32 drives
00750                         }
00751                         else {
00752                                 LOG(LOG_IOCTL,LOG_DEBUG)("Attempt IOCTL AX=%04x CX=%04x",reg_ax,reg_cx);
00753                                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00754                                 return false;
00755                         }
00756                 }
00757                 break;
00758         case 0x0E:                      /* Get Logical Drive Map */
00759                 if (drive < 2) {
00760                         if (Drives[drive]) reg_al=drive+1;
00761                         else reg_al=1;
00762                 } else if (Drives[drive]->isRemovable()) {
00763                         DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00764                         return false;
00765                 } else reg_al=0;        /* Only 1 logical drive assigned */
00766                 reg_ah=0x07;
00767                 return true;
00768         default:
00769                 LOG(LOG_DOSMISC,LOG_ERROR)("DOS:IOCTL Call %2X unhandled",reg_al);
00770                 DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID);
00771                 break;
00772         }
00773         return false;
00774 }
00775 
00776 
00777 bool DOS_GetSTDINStatus(void) {
00778         Bit32u handle=RealHandle(STDIN);
00779         if (handle==0xFF) return false;
00780         if (Files[handle] && (Files[handle]->GetInformation() & 64)) return false;
00781         return true;
00782 }