diff options
author | Wengier <wengierwu@yahoo.com> | 2022-09-30 10:35:09 +0300 |
---|---|---|
committer | kcgen <kcgen@users.noreply.github.com> | 2022-11-04 06:12:21 +0300 |
commit | 21420ec4f4a7729cd7d4bcfc69e5754a7111e460 (patch) | |
tree | d6d94766196d4d98ec631be4f49bdf63dd03f52d | |
parent | 7c14affc646665d1907ce32b334a15f8c384e818 (diff) |
Integrate FAT32 and large HDD patchesww/fat32-test-1
-rw-r--r-- | include/bios_disk.h | 2 | ||||
-rw-r--r-- | include/dos_inc.h | 24 | ||||
-rw-r--r-- | include/drives.h | 170 | ||||
-rw-r--r-- | src/dos/dos.cpp | 12 | ||||
-rw-r--r-- | src/dos/dos_devices.cpp | 2 | ||||
-rw-r--r-- | src/dos/dos_ioctl.cpp | 617 | ||||
-rw-r--r-- | src/dos/drive_fat.cpp | 369 | ||||
-rw-r--r-- | src/dos/program_imgmount.cpp | 110 | ||||
-rw-r--r-- | src/hardware/ide.cpp | 31 | ||||
-rw-r--r-- | src/ints/bios_disk.cpp | 170 |
10 files changed, 1347 insertions, 160 deletions
diff --git a/include/bios_disk.h b/include/bios_disk.h index 12c3753cb..a0ae32824 100644 --- a/include/bios_disk.h +++ b/include/bios_disk.h @@ -73,7 +73,7 @@ public: uint32_t sector_size; uint32_t heads,cylinders,sectors; private: - uint32_t current_fpos; + uint64_t current_fpos; enum { NONE,READ,WRITE } last_action; }; diff --git a/include/dos_inc.h b/include/dos_inc.h index caddb9b06..7a06ebfc7 100644 --- a/include/dos_inc.h +++ b/include/dos_inc.h @@ -267,6 +267,22 @@ static inline uint16_t long2para(uint32_t size) { else return (uint16_t)(size>>4); } +/* fopen64, ftello64, fseeko64 */ +#if defined(__APPLE__) + #define fopen64 fopen + #define ftello64 ftell + #define fseeko64 fseek +#elif defined (_MSC_VER) + #define fopen64 fopen + #if (_MSC_VER >= 1400) + #define ftello64 _ftelli64 + #define fseeko64 _fseeki64 + #else + #define ftello64 ftell + #define fseeko64 fseek + #endif +#endif + /* Dos Error Codes */ #define DOSERR_NONE 0 #define DOSERR_FUNCTION_NUMBER_INVALID 1 @@ -577,8 +593,8 @@ public: void SetDirID(uint16_t id) { SSET_WORD(sDTA, dirID, id); } uint16_t GetDirID() const { return SGET_WORD(sDTA, dirID); } - void SetDirIDCluster(uint16_t cl) { SSET_WORD(sDTA, dirCluster, cl); } - uint16_t GetDirIDCluster() const { return SGET_WORD(sDTA, dirCluster); } + void SetDirIDCluster(uint32_t cl) { SSET_DWORD(sDTA, dirCluster, cl); } + uint32_t GetDirIDCluster() const { return SGET_DWORD(sDTA, dirCluster); } private: #ifdef _MSC_VER @@ -590,8 +606,8 @@ private: uint8_t sext[3]; /* The Search pattern for the extension */ uint8_t sattr; /* The Attributes that need to be found */ uint16_t dirID; /* custom: dir-search ID for multiple searches at the same time */ - uint16_t dirCluster; /* custom (drive_fat only): cluster number for multiple searches at the same time */ - uint8_t fill[4]; + uint32_t dirCluster; /* custom (drive_fat only): cluster number for multiple searches at the same time */ + uint8_t fill[2]; uint8_t attr; uint16_t time; uint16_t date; diff --git a/include/drives.h b/include/drives.h index 51f5ff543..b13f06ed2 100644 --- a/include/drives.h +++ b/include/drives.h @@ -128,6 +128,157 @@ struct bootstrap { uint8_t magic2; /* 0xaa */ } GCC_ATTRIBUTE(packed); +#ifdef _MSC_VER +#pragma pack (1) +#endif +struct FAT_BPB_MSDOS20 { + uint16_t BPB_BytsPerSec; /* offset 0x00B size 0x002 Bytes per sector. Formerly bytespersector */ + uint8_t BPB_SecPerClus; /* offset 0x00D size 0x001 Sectors per cluster, must be a power of 2. Formerly sectorspercluster */ + uint16_t BPB_RsvdSecCnt; /* offset 0x00E size 0x002 Number of reserved sectors starting from partition, to FAT table. reservedsectors */ + uint8_t BPB_NumFATs; /* offset 0x010 size 0x001 Number of FAT tables. fatcopies */ + uint16_t BPB_RootEntCnt; /* offset 0x011 size 0x002 Number of 32-byte root directories (FAT12/FAT16), or 0 (FAT32). rootdirentries */ + uint16_t BPB_TotSec16; /* offset 0x013 size 0x002 Total sectors of volume if count < 0x10000, or 0 if not. 0 if FAT32. totalsectorcount */ + uint8_t BPB_Media; /* offset 0x015 size 0x001 Media type byte. mediadescriptor */ + uint16_t BPB_FATSz16; /* offset 0x016 size 0x002 Sectors per fat (FAT12/FAT16), or 0 (FAT32). sectorsperfat */ +} GCC_ATTRIBUTE(packed); /* ==> 0x018 size 0x00D total */ + +struct FAT_BPB_MSDOS30 { + uint16_t BPB_BytsPerSec; /* offset 0x00B size 0x002 Bytes per sector. Formerly bytespersector */ + uint8_t BPB_SecPerClus; /* offset 0x00D size 0x001 Sectors per cluster, must be a power of 2. Formerly sectorspercluster */ + uint16_t BPB_RsvdSecCnt; /* offset 0x00E size 0x002 Number of reserved sectors starting from partition, to FAT table. reservedsectors */ + uint8_t BPB_NumFATs; /* offset 0x010 size 0x001 Number of FAT tables. fatcopies */ + uint16_t BPB_RootEntCnt; /* offset 0x011 size 0x002 Number of 32-byte root directories (FAT12/FAT16), or 0 (FAT32). rootdirentries */ + uint16_t BPB_TotSec16; /* offset 0x013 size 0x002 Total sectors of volume if count < 0x10000, or 0 if not. 0 if FAT32. totalsectorcount */ + uint8_t BPB_Media; /* offset 0x015 size 0x001 Media type byte. mediadescriptor */ + uint16_t BPB_FATSz16; /* offset 0x016 size 0x002 Sectors per fat (FAT12/FAT16), or 0 (FAT32). sectorsperfat */ + uint16_t BPB_SecPerTrk; /* offset 0x018 size 0x002 Sectors per track. sectorspertrack */ + uint16_t BPB_NumHeads; /* offset 0x01A size 0x002 Number of heads. headcount */ + uint32_t BPB_HiddSec; /* offset 0x01C size 0x004 Number of hidden sectors (i.e. starting sector of partition). hiddensectorcount (MS-DOS 3.31) */ +} GCC_ATTRIBUTE(packed); /* ==> 0x020 size 0x015 total */ + /* ==== ADDITIONAL NOTES (Wikipedia) */ + /* offset 0x01C size 0x002 Number of hidden sectors (i.e. starting sector of partition). hiddensectorcount (MS-DOS 3.0) */ + /* offset 0x01E size 0x002 Total sectors including hidden (?) if BPB_TotSec16 != 0 (MS-DOS 3.20) */ + +struct FAT_BPB_MSDOS331 { + uint16_t BPB_BytsPerSec; /* offset 0x00B size 0x002 Bytes per sector. Formerly bytespersector */ + uint8_t BPB_SecPerClus; /* offset 0x00D size 0x001 Sectors per cluster, must be a power of 2. Formerly sectorspercluster */ + uint16_t BPB_RsvdSecCnt; /* offset 0x00E size 0x002 Number of reserved sectors starting from partition, to FAT table. reservedsectors */ + uint8_t BPB_NumFATs; /* offset 0x010 size 0x001 Number of FAT tables. fatcopies */ + uint16_t BPB_RootEntCnt; /* offset 0x011 size 0x002 Number of 32-byte root directories (FAT12/FAT16), or 0 (FAT32). rootdirentries */ + uint16_t BPB_TotSec16; /* offset 0x013 size 0x002 Total sectors of volume if count < 0x10000, or 0 if not. 0 if FAT32. totalsectorcount */ + uint8_t BPB_Media; /* offset 0x015 size 0x001 Media type byte. mediadescriptor */ + uint16_t BPB_FATSz16; /* offset 0x016 size 0x002 Sectors per fat (FAT12/FAT16), or 0 (FAT32). sectorsperfat */ + uint16_t BPB_SecPerTrk; /* offset 0x018 size 0x002 Sectors per track. sectorspertrack */ + uint16_t BPB_NumHeads; /* offset 0x01A size 0x002 Number of heads. headcount */ + uint32_t BPB_HiddSec; /* offset 0x01C size 0x004 Number of hidden sectors (i.e. starting sector of partition). hiddensectorcount (MS-DOS 3.31) */ + uint32_t BPB_TotSec32; /* offset 0x020 size 0x004 Total sectors of volume if count >= 0x10000 or FAT32, or 0 if not. totalsecdword */ +} GCC_ATTRIBUTE(packed); /* ==> 0x024 size 0x019 total */ + +struct FAT_BPB_MSDOS40 { /* FAT12/FAT16 only */ + uint16_t BPB_BytsPerSec; /* offset 0x00B size 0x002 Bytes per sector. Formerly bytespersector */ + uint8_t BPB_SecPerClus; /* offset 0x00D size 0x001 Sectors per cluster, must be a power of 2. Formerly sectorspercluster */ + uint16_t BPB_RsvdSecCnt; /* offset 0x00E size 0x002 Number of reserved sectors starting from partition, to FAT table. reservedsectors */ + uint8_t BPB_NumFATs; /* offset 0x010 size 0x001 Number of FAT tables. fatcopies */ + uint16_t BPB_RootEntCnt; /* offset 0x011 size 0x002 Number of 32-byte root directories (FAT12/FAT16), or 0 (FAT32). rootdirentries */ + uint16_t BPB_TotSec16; /* offset 0x013 size 0x002 Total sectors of volume if count < 0x10000, or 0 if not. 0 if FAT32. totalsectorcount */ + uint8_t BPB_Media; /* offset 0x015 size 0x001 Media type byte. mediadescriptor */ + uint16_t BPB_FATSz16; /* offset 0x016 size 0x002 Sectors per fat (FAT12/FAT16), or 0 (FAT32). sectorsperfat */ + uint16_t BPB_SecPerTrk; /* offset 0x018 size 0x002 Sectors per track. sectorspertrack */ + uint16_t BPB_NumHeads; /* offset 0x01A size 0x002 Number of heads. headcount */ + uint32_t BPB_HiddSec; /* offset 0x01C size 0x004 Number of hidden sectors (i.e. starting sector of partition). hiddensectorcount (MS-DOS 3.31) */ + uint32_t BPB_TotSec32; /* offset 0x020 size 0x004 Total sectors of volume if count >= 0x10000 or FAT32, or 0 if not. totalsecdword */ + uint8_t BPB_DrvNum; /* offset 0x024 size 0x001 Physical (INT 13h) drive number */ + uint8_t BPB_Reserved1; /* offset 0x025 size 0x001 Reserved? */ + uint8_t BPB_BootSig; /* offset 0x026 size 0x001 Extended boot signature. 0x29 or 0x28 to indicate following members exist. */ + uint32_t BPB_VolID; /* offset 0x027 size 0x004 Volume ID, if BPB_BootSig is 0x28 or 0x29. */ + uint8_t BPB_VolLab[11]; /* offset 0x02B size 0x00B Volume label, if BPB_BootSig is 0x28 or 0x29. */ + uint8_t BPB_FilSysType[8]; /* offset 0x036 size 0x008 File system type, for display purposes if BPB_BootSig is 0x29. */ +} GCC_ATTRIBUTE(packed); /* ==> 0x03E size 0x033 total */ + +struct FAT_BPB_MSDOS710_FAT32 { /* FAT32 only */ + uint16_t BPB_BytsPerSec; /* offset 0x00B size 0x002 Bytes per sector. Formerly bytespersector */ + uint8_t BPB_SecPerClus; /* offset 0x00D size 0x001 Sectors per cluster, must be a power of 2. Formerly sectorspercluster */ + uint16_t BPB_RsvdSecCnt; /* offset 0x00E size 0x002 Number of reserved sectors starting from partition, to FAT table. reservedsectors */ + uint8_t BPB_NumFATs; /* offset 0x010 size 0x001 Number of FAT tables. fatcopies */ + uint16_t BPB_RootEntCnt; /* offset 0x011 size 0x002 Number of 32-byte root directories (FAT12/FAT16), or 0 (FAT32). rootdirentries */ + uint16_t BPB_TotSec16; /* offset 0x013 size 0x002 Total sectors of volume if count < 0x10000, or 0 if not. 0 if FAT32. totalsectorcount */ + uint8_t BPB_Media; /* offset 0x015 size 0x001 Media type byte. mediadescriptor */ + uint16_t BPB_FATSz16; /* offset 0x016 size 0x002 Sectors per fat (FAT12/FAT16), or 0 (FAT32). sectorsperfat */ + uint16_t BPB_SecPerTrk; /* offset 0x018 size 0x002 Sectors per track. sectorspertrack */ + uint16_t BPB_NumHeads; /* offset 0x01A size 0x002 Number of heads. headcount */ + uint32_t BPB_HiddSec; /* offset 0x01C size 0x004 Number of hidden sectors (i.e. starting sector of partition). hiddensectorcount (MS-DOS 3.31) */ + uint32_t BPB_TotSec32; /* offset 0x020 size 0x004 Total sectors of volume if count >= 0x10000 or FAT32, or 0 if not. totalsecdword */ + uint32_t BPB_FATSz32; /* offset 0x024 size 0x004 Sectors per fat (FAT32). */ + uint16_t BPB_ExtFlags; /* offset 0x028 size 0x002 Bitfield: [7:7] 1=one fat active 0=mirrored [3:0]=active FAT if mirroring disabled */ + uint16_t BPB_FSVer; /* offset 0x02A size 0x002 Version number. Only 0.0 is defined now. Do not mount if newer version beyond what we support */ + uint32_t BPB_RootClus; /* offset 0x02C size 0x004 Starting cluster number of the root directory (FAT32) */ + uint16_t BPB_FSInfo; /* offset 0x030 size 0x002 Sector number in volume of FAT32 FSInfo structure in reserved area */ + uint16_t BPB_BkBootSec; /* offset 0x032 size 0x002 Sector number in volume of FAT32 backup boot sector */ + uint8_t BPB_Reserved[12]; /* offset 0x034 size 0x00C Reserved for future expansion */ + uint8_t BS_DrvNum; /* offset 0x040 size 0x001 BPB_DrvNum but moved for FAT32 */ + uint8_t BS_Reserved1; /* offset 0x041 size 0x001 BPB_Reserved1 but moved for FAT32 */ + uint8_t BS_BootSig; /* offset 0x042 size 0x001 Extended boot signature. 0x29 or 0x28 to indicate following members exist. */ + uint32_t BS_VolID; /* offset 0x043 size 0x004 Volume ID, if BPB_BootSig is 0x28 or 0x29. */ + uint8_t BS_VolLab[11]; /* offset 0x047 size 0x00B Volume label, if BPB_BootSig is 0x28 or 0x29. */ + uint8_t BS_FilSysType[8]; /* offset 0x052 size 0x008 File system type, for display purposes if BPB_BootSig is 0x29. */ +} GCC_ATTRIBUTE(packed); /* ==> 0x05A size 0x04F total */ + +typedef struct FAT_BPB_MSDOS40 FAT_BPB_MSDOS; /* what we use internally */ +typedef struct FAT_BPB_MSDOS710_FAT32 FAT32_BPB_MSDOS; /* what we use internally */ + +struct FAT_BootSector { + /* --------- Common fields: Amalgam of Wikipedia documentation with names from Microsoft's FAT32 whitepaper */ + uint8_t BS_jmpBoot[3]; /* offset 0x000 size 0x003 Jump instruction to boot code. Formerly nearjmp[3] */ + uint8_t BS_OEMName[8]; /* offset 0x003 size 0x008 OEM string. Formerly oemname[8] */ + /* --------- BIOS Parameter Block (converted in place from existing DOSBox-X code) */ + union bpb_union_t { + struct FAT_BPB_MSDOS20 v20; /* offset 0x00B size 0x00D MS-DOS 2.0 BPB */ + struct FAT_BPB_MSDOS30 v30; /* offset 0x00B size 0x015 MS-DOS 3.0 BPB */ + struct FAT_BPB_MSDOS331 v331; /* offset 0x00B size 0x019 MS-DOS 3.31 BPB */ + struct FAT_BPB_MSDOS40 v40; /* offset 0x00B size 0x039 MS-DOS 4.0 BPB (FAT12/FAT16) */ + struct FAT_BPB_MSDOS710_FAT32 v710_32;/* offset 0x00B size 0x04F MS-DOS 7.10 BPB (FAT32) */ + + FAT_BPB_MSDOS v; /* offset 0x00B ... */ + FAT32_BPB_MSDOS v32; /* offset 0x00B ... */ + + inline bool is_fat32(void) const { + return (v.BPB_RootEntCnt == 0 && v.BPB_TotSec16 == 0 && v.BPB_FATSz16 == 0); /* all fields are "must be set to 0" for FAT32 */ + } + } bpb; + /* --------- The rest of the sector ---------- */ + uint8_t bootcode[512 - 2/*magic*/ - sizeof(bpb_union_t) - 8/*OEM*/ - 3/*JMP*/]; + uint8_t magic1; /* 0x55 */ + uint8_t magic2; /* 0xaa */ +#define SECTOR_SIZE_MAX 2048 +#if SECTOR_SIZE_MAX > 512 + uint8_t extra[SECTOR_SIZE_MAX - 512]; +#endif +} GCC_ATTRIBUTE(packed); +static_assert(offsetof(FAT_BootSector,bpb.v20) == 0x00B,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v30) == 0x00B,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v331) == 0x00B,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v40) == 0x00B,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v) == 0x00B,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v20.BPB_TotSec16) == 0x013,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v30.BPB_TotSec16) == 0x013,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v331.BPB_TotSec16) == 0x013,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v40.BPB_TotSec16) == 0x013,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v.BPB_TotSec16) == 0x013,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v331.BPB_TotSec32) == 0x020,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v30.BPB_HiddSec) == 0x01C,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v40.BPB_TotSec32) == 0x020,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v40.BPB_VolLab) == 0x02B,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v710_32.BS_FilSysType) == 0x052,"Oops"); +static_assert(sizeof(FAT_BootSector) == SECTOR_SIZE_MAX,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v331.BPB_TotSec32) == 0x020,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v40.BPB_TotSec32) == 0x020,"Oops"); +static_assert(offsetof(FAT_BootSector,bpb.v710_32.BPB_TotSec32) == 0x020,"Oops"); +static_assert(sizeof(FAT_BootSector::bpb.v20) == 0x00D,"Oops"); +static_assert(sizeof(FAT_BootSector::bpb.v30) == 0x015,"Oops"); +static_assert(sizeof(FAT_BootSector::bpb.v331) == 0x019,"Oops"); +static_assert(sizeof(FAT_BootSector::bpb.v40) == 0x033,"Oops"); +static_assert(sizeof(FAT_BootSector::bpb.v710_32) == 0x04F,"Oops"); + struct direntry { uint8_t entryname[11]; uint8_t attrib; @@ -141,6 +292,13 @@ struct direntry { uint16_t modDate; uint16_t loFirstClust; uint32_t entrysize; + inline uint32_t Cluster32(void) const { + return ((uint32_t)hiFirstClust << (uint32_t)16) + loFirstClust; + } + inline void SetCluster32(const uint32_t v) { + loFirstClust = (uint16_t)v; + hiFirstClust = (uint16_t)(v >> (uint32_t)16); + } } GCC_ATTRIBUTE(packed); struct partTable { @@ -198,12 +356,18 @@ public: uint32_t appendCluster(uint32_t startCluster); void deleteClustChain(uint32_t startCluster, uint32_t bytePos); uint32_t getFirstFreeClust(void); + unsigned long GetSerial(); + uint32_t GetPartitionOffset(); bool directoryBrowse(uint32_t dirClustNumber, direntry *useEntry, int32_t entNum, int32_t start=0); bool directoryChange(uint32_t dirClustNumber, direntry *useEntry, int32_t entNum); std::shared_ptr<imageDisk> loadedDisk; bool created_successfully; uint32_t partSectOff; - + FAT_BootSector::bpb_union_t GetBPB() {return BPB;} + void SetBPB(const FAT_BootSector::bpb_union_t &bpb); + virtual uint32_t getSectSize(void); + uint32_t sector_size = 0; + bool readonly = false; private: uint32_t getClusterValue(uint32_t clustNum); void setClusterValue(uint32_t clustNum, uint32_t clustValue); @@ -215,9 +379,9 @@ private: void zeroOutCluster(uint32_t clustNumber); bool getEntryName(char *fullname, char *entname); - bootstrap bootbuffer; + FAT_BootSector::bpb_union_t BPB = {}; // BPB in effect (translated from on-disk BPB as needed) + FAT_BootSector bootbuffer = {}; bool absolute; - bool readonly; uint8_t fattype; uint32_t CountOfClusters; uint32_t firstDataSector; diff --git a/src/dos/dos.cpp b/src/dos/dos.cpp index 6081899ec..46cf79472 100644 --- a/src/dos/dos.cpp +++ b/src/dos/dos.cpp @@ -1267,6 +1267,18 @@ static Bitu DOS_21Handler(void) { } CALLBACK_SCF(false); break; + case 0x23: /* Determine if character represents yes/no response */ + { + unsigned int c = reg_dl; + if (tolower(c) == 'y') + reg_ax = 1;/*yes*/ + else if (tolower(c) == 'n') + reg_ax = 0;/*no*/ + else + reg_ax = 2;/*neither*/ + } + CALLBACK_SCF(false); + break; default: E_Exit("DOS:0x65:Unhandled country information call %2X",reg_al); }; diff --git a/src/dos/dos_devices.cpp b/src/dos/dos_devices.cpp index 050653ca4..885c83f88 100644 --- a/src/dos/dos_devices.cpp +++ b/src/dos/dos_devices.cpp @@ -205,7 +205,7 @@ uint32_t DOS_CheckExtDevice(const char *name, bool already_flag) no = real_readw(seg, off + 4); next_seg = real_readw(seg, off + 2); next_off = real_readw(seg, off); - if (next_seg == 0xffff && next_off == 0xffff) { + if ((next_seg == 0xffff && next_off == 0xffff) || (no == 0 && next_seg == 0 && next_off == 0)) { break; } if (no & 0x8000) { diff --git a/src/dos/dos_ioctl.cpp b/src/dos/dos_ioctl.cpp index 960465b89..9a7ad4be3 100644 --- a/src/dos/dos_ioctl.cpp +++ b/src/dos/dos_ioctl.cpp @@ -22,7 +22,570 @@ #include "callback.h" #include "mem.h" #include "regs.h" +#include "bios_disk.h" #include "dos_inc.h" +#include "drives.h" + +bool DOS_IOCTL_AX440D_CH08(uint8_t drive,bool query) { + PhysPt ptr = SegPhys(ds)+reg_dx; + switch (reg_cl) { + case 0x40: /* Set device parameters */ + { + if (Drives[drive]->GetType() != DosDriveType::Fat) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]); + if (fdp == NULL || fdp->readonly) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + + if (query) break; + + FAT_BootSector::bpb_union_t bpb=fdp->GetBPB(); + if (fdp->loadedDisk != NULL) + fdp->loadedDisk->cylinders = mem_readw(ptr+4); // number of cylinders + + if (mem_readw(ptr+0xd) == 0 && mem_readw(ptr+0xf) == 0 && mem_readw(ptr+0x12) == 0) { // FAT32 BPB? + bpb.v32.BPB_BytsPerSec = mem_readw(ptr+7); // bytes per sector (Win3 File Mgr. uses it) + bpb.v32.BPB_SecPerClus = mem_readb(ptr+9); // sectors per cluster + bpb.v32.BPB_RsvdSecCnt = mem_readw(ptr+0xa); // number of reserved sectors + bpb.v32.BPB_NumFATs = mem_readb(ptr+0xc); // number of FATs + bpb.v32.BPB_RootEntCnt = mem_readw(ptr+0xd); // number of root entries (Fake, the real BPB value is zero) + bpb.v32.BPB_TotSec16 = mem_readw(ptr+0xf); // number of small sectors (always zero on BPB and returned by Win98) + bpb.v32.BPB_Media = mem_readb(ptr+0x11); // media type + bpb.v32.BPB_FATSz32 = (uint16_t)mem_readw(ptr+0x12); // sectors per FAT (FIXME: What does Win98 do if this value > 0xFFFF?) + bpb.v32.BPB_SecPerTrk = (uint16_t)mem_readw(ptr+0x14); // sectors per track + bpb.v32.BPB_NumHeads = (uint16_t)mem_readw(ptr+0x16); // number of heads + bpb.v32.BPB_HiddSec = (uint32_t)mem_readd(ptr+0x18); // number of hidden sectors + bpb.v32.BPB_TotSec32 = (uint32_t)mem_readd(ptr+0x1c); // number of big sectors + } else { + bpb.v.BPB_BytsPerSec = mem_readw(ptr+7); // bytes per sector (Win3 File Mgr. uses it) + bpb.v.BPB_SecPerClus = mem_readb(ptr+9); // sectors per cluster + bpb.v.BPB_RsvdSecCnt = mem_readw(ptr+0xa); // number of reserved sectors + bpb.v.BPB_NumFATs = mem_readb(ptr+0xc); // number of FATs + bpb.v.BPB_RootEntCnt = mem_readw(ptr+0xd); // number of root entries + bpb.v.BPB_TotSec16 = mem_readw(ptr+0xf); // number of small sectors + bpb.v.BPB_Media = mem_readb(ptr+0x11); // media type + bpb.v.BPB_FATSz16 = (uint16_t)mem_readw(ptr+0x12); // sectors per FAT + bpb.v.BPB_SecPerTrk = (uint16_t)mem_readw(ptr+0x14); // sectors per track + bpb.v.BPB_NumHeads = (uint16_t)mem_readw(ptr+0x16); // number of heads + bpb.v.BPB_HiddSec = (uint32_t)mem_readd(ptr+0x18); // number of hidden sectors + bpb.v.BPB_TotSec32 = (uint32_t)mem_readd(ptr+0x1c); // number of big sectors + } + fdp->SetBPB(bpb); + break; + } + case 0x60: /* Get device parameters */ + if (query) break; + { + //mem_writeb(ptr+0,0); // special functions (call value) + mem_writeb(ptr+1,(drive>=2)?0x05:0x07); // type: hard disk(5), 1.44 floppy(7) + mem_writew(ptr+2,(drive>=2)?0x01:0x00); // attributes: bit 0 set for nonremovable + mem_writew(ptr+4,(drive>=2)?0x3FF:0x50);// number of cylinders + mem_writeb(ptr+6,0x00); // media type (00=other type) + // bios parameter block following + fatDrive *fdp; + FAT_BootSector::bpb_union_t bpb; + bool usereal=false; + if (Drives[drive]->GetType() == DosDriveType::Fat) { + fdp = dynamic_cast<fatDrive*>(Drives[drive]); + if (fdp != NULL) { + bpb=fdp->GetBPB(); + if (bpb.v.BPB_BytsPerSec && bpb.v.BPB_Media) + usereal=true; + } + } + if (usereal) { + if (fdp->loadedDisk != NULL) + mem_writew(ptr+4,fdp->loadedDisk->cylinders); // number of cylinders + + if (bpb.is_fat32()) { + /* 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 */ + mem_writew(ptr+7,bpb.v32.BPB_BytsPerSec); // bytes per sector (Win3 File Mgr. uses it) + mem_writeb(ptr+9,bpb.v32.BPB_SecPerClus); // sectors per cluster + mem_writew(ptr+0xa,bpb.v32.BPB_RsvdSecCnt); // number of reserved sectors + mem_writeb(ptr+0xc,bpb.v32.BPB_NumFATs); // number of FATs + mem_writew(ptr+0xd,0x200); // number of root entries (Fake, the real BPB value is zero) + mem_writew(ptr+0xf,0); // number of small sectors (always zero on BPB and returned by Win98) + mem_writeb(ptr+0x11,bpb.v32.BPB_Media); // media type + mem_writew(ptr+0x12,(uint16_t)bpb.v32.BPB_FATSz32); // sectors per FAT (FIXME: What does Win98 do if this value > 0xFFFF?) + mem_writew(ptr+0x14,(uint16_t)bpb.v32.BPB_SecPerTrk); // sectors per track + mem_writew(ptr+0x16,(uint16_t)bpb.v32.BPB_NumHeads); // number of heads + mem_writed(ptr+0x18,(uint32_t)bpb.v32.BPB_HiddSec); // number of hidden sectors + mem_writed(ptr+0x1c,(uint32_t)bpb.v32.BPB_TotSec32); // number of big sectors + } + else { + mem_writew(ptr+7,bpb.v.BPB_BytsPerSec); // bytes per sector (Win3 File Mgr. uses it) + mem_writeb(ptr+9,bpb.v.BPB_SecPerClus); // sectors per cluster + mem_writew(ptr+0xa,bpb.v.BPB_RsvdSecCnt); // number of reserved sectors + mem_writeb(ptr+0xc,bpb.v.BPB_NumFATs); // number of FATs + mem_writew(ptr+0xd,bpb.v.BPB_RootEntCnt); // number of root entries + mem_writew(ptr+0xf,bpb.v.BPB_TotSec16); // number of small sectors + mem_writeb(ptr+0x11,bpb.v.BPB_Media); // media type + mem_writew(ptr+0x12,(uint16_t)bpb.v.BPB_FATSz16); // sectors per FAT + mem_writew(ptr+0x14,(uint16_t)bpb.v.BPB_SecPerTrk); // sectors per track + mem_writew(ptr+0x16,(uint16_t)bpb.v.BPB_NumHeads); // number of heads + mem_writed(ptr+0x18,(uint32_t)bpb.v.BPB_HiddSec); // number of hidden sectors + mem_writed(ptr+0x1c,(uint32_t)bpb.v.BPB_TotSec32); // number of big sectors + } + } else { + mem_writew(ptr+7,0x0200); // bytes per sector (Win3 File Mgr. uses it) + mem_writew(ptr+9,(drive>=2)?0x20:0x01); // sectors per cluster + mem_writew(ptr+0xa,0x0001); // number of reserved sectors + mem_writew(ptr+0xc,0x02); // number of FATs + mem_writew(ptr+0xd,(drive>=2)?0x0200:0x00E0); // number of root entries + mem_writew(ptr+0xf,(drive>=2)?0x0000:0x0B40); // number of small sectors + mem_writew(ptr+0x11,(drive>=2)?0xF8:0xF0); // media type + mem_writew(ptr+0x12,(drive>=2)?0x00C9:0x0009); // sectors per FAT + mem_writew(ptr+0x14,(drive>=2)?0x003F:0x0012); // sectors per track + mem_writew(ptr+0x16,(drive>=2)?0x10:0x02); // number of heads + mem_writed(ptr+0x18,0); // number of hidden sectors + mem_writed(ptr+0x1c,(drive>=2)?0x31F11:0x00); // number of big sectors + } + for (int i=0x20; i<0x22; i++) + mem_writeb(ptr+i,0); + break; + } + case 0x42: /* Format and verify logical device track (FORMAT.COM) */ + { + /* 01h WORD number of disk head + * 03h WORD number of disk cylinder + * ---BYTE 00h bit 1 set--- + * 05h WORD number of tracks to format */ + uint8_t flags = mem_readb(ptr+0); + uint16_t head = mem_readw(ptr+1); + uint16_t cyl = mem_readw(ptr+3); + uint16_t ntracks = (flags & 0x1) ? mem_readw(ptr+5) : 1; + uint16_t sect = 0; + + fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]); + if (fdp == NULL || fdp->readonly) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + + if (query) break; + + /* BUT: These are C/H/S values relative to the partition! + * FIXME: MS-DOS may not adjust sector value, or maybe it does... + * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */ + { + uint32_t adj = fdp->GetPartitionOffset(); + sect += adj % fdp->loadedDisk->sectors; + adj /= fdp->loadedDisk->sectors; + head += adj % fdp->loadedDisk->heads; + adj /= fdp->loadedDisk->heads; + cyl += adj; + + while (sect >= fdp->loadedDisk->sectors) { + sect -= fdp->loadedDisk->sectors; + head++; + } + while (head >= fdp->loadedDisk->heads) { + head -= fdp->loadedDisk->heads; + cyl++; + } + + /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */ + sect++; + } + + // STUB! + LOG_INFO("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); + } + break; + case 0x62: /* Verify logical device track (FORMAT.COM) */ + { + /* 01h WORD number of disk head + * 03h WORD number of disk cylinder + * 05h WORD number of tracks to verify */ + uint16_t head = mem_readw(ptr+1); + uint16_t cyl = mem_readw(ptr+3); + uint16_t ntracks = mem_readw(ptr+5); + uint16_t sect = 0; + + fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]); + if (fdp == NULL) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + + if (query) break; + + /* BUT: These are C/H/S values relative to the partition! + * FIXME: MS-DOS may not adjust sector value, or maybe it does... + * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */ + { + uint32_t adj = fdp->GetPartitionOffset(); + sect += adj % fdp->loadedDisk->sectors; + adj /= fdp->loadedDisk->sectors; + head += adj % fdp->loadedDisk->heads; + adj /= fdp->loadedDisk->heads; + cyl += adj; + + while (sect >= fdp->loadedDisk->sectors) { + sect -= fdp->loadedDisk->sectors; + head++; + } + while (head >= fdp->loadedDisk->heads) { + head -= fdp->loadedDisk->heads; + cyl++; + } + + /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */ + sect++; + } + + // STUB! + LOG_INFO("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); + } + break; + case 0x46: /* Set volume serial number */ + if (query) break; + { + fatDrive* fdp = dynamic_cast<fatDrive*>(Drives[drive]); + if (fdp == NULL || fdp->readonly) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + + FAT_BootSector::bpb_union_t bpb=fdp->GetBPB(); + unsigned long serial_number=mem_readd(ptr+2)?mem_readd(ptr+2):0x1234; + if (bpb.is_fat32()) + bpb.v32.BS_VolID=serial_number; + else + bpb.v.BPB_VolID=serial_number; + fdp->SetBPB(bpb); + } + break; + case 0x66: /* Get volume serial number */ + if (query) break; + { + char const* bufin=Drives[drive]->GetLabel(); + char buffer[11];memset(buffer,' ',11); + + char const* find_ext=strchr(bufin,'.'); + if (find_ext) { + Bitu size=(Bitu)(find_ext-bufin); + if (size>8) size=8; + memcpy(buffer,bufin,size); + find_ext++; + memcpy(buffer+8,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext)); + } else { + memcpy(buffer,bufin,(strlen(bufin) > 8) ? 8 : strlen(bufin)); + } + + char buf2[8]={ 'F','A','T','1','6',' ',' ',' '}; + if(drive<2) buf2[4] = '2'; //FAT12 for floppies + + //mem_writew(ptr+0,0); //Info level (call value) + unsigned long serial_number=0x1234; + if (Drives[drive]->GetType() == DosDriveType::Fat) { + fatDrive* fdp = dynamic_cast<fatDrive*>(Drives[drive]); + if (fdp != NULL) serial_number=fdp->GetSerial(); + } + mem_writed(ptr+2,serial_number);//Serial number + MEM_BlockWrite(ptr+6,buffer,11);//volumename + MEM_BlockWrite(ptr+0x11,buf2,8);//filesystem + } + break; + case 0x41: /* Write logical device track */ + { + fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]); + if (fdp == NULL || fdp->readonly) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + + uint8_t sectbuf[SECTOR_SIZE_MAX]; + + if (fdp->loadedDisk == NULL) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + + if (query) break; + + /* (RBIL) [http://www.ctyme.com/intr/rb-2896.htm] + * Offset Size Description (Table 01562) + * 00h BYTE special functions (reserved, must be zero) + * 01h WORD number of disk head + * 03h WORD number of disk cylinder + * 05h WORD number of first sector to read/write + * 07h WORD number of sectors + * 09h DWORD transfer address */ + uint16_t head = mem_readw(ptr+1); + uint16_t cyl = mem_readw(ptr+3); + uint16_t sect = mem_readw(ptr+5); + uint16_t nsect = mem_readw(ptr+7); + uint32_t xfer_addr = mem_readd(ptr+9); + PhysPt xfer_ptr = ((xfer_addr>>16u)<<4u)+(xfer_addr&0xFFFFu); + uint16_t sectsize = fdp->loadedDisk->getSectSize(); + + /* BUT: These are C/H/S values relative to the partition! + * FIXME: MS-DOS may not adjust sector value, or maybe it does... + * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */ + { + uint32_t adj = fdp->GetPartitionOffset(); + sect += adj % fdp->loadedDisk->sectors; + adj /= fdp->loadedDisk->sectors; + head += adj % fdp->loadedDisk->heads; + adj /= fdp->loadedDisk->heads; + cyl += adj; + + while (sect >= fdp->loadedDisk->sectors) { + sect -= fdp->loadedDisk->sectors; + head++; + } + while (head >= fdp->loadedDisk->heads) { + head -= fdp->loadedDisk->heads; + cyl++; + } + + /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */ + sect++; + } + + LOG_INFO("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", + drive,cyl,head,sect,nsect,xfer_addr >> 16,xfer_addr & 0xFFFF,sectsize); + + while (nsect > 0) { + MEM_BlockRead(xfer_ptr,sectbuf,sectsize); + + uint8_t status = fdp->loadedDisk->Write_Sector(head,cyl,sect,sectbuf); + if (status != 0) { + LOG_INFO("IOCTL 0D:61 write error at C/H/S %u/%u/%u",cyl,head,sect); + DOS_SetError(DOSERR_ACCESS_DENIED);//FIXME + return false; + } + + xfer_ptr += sectsize; + nsect--; + sect++; + } + } + break; + case 0x61: /* Read logical device track */ + { + fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]); + if (fdp == NULL) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + + uint8_t sectbuf[SECTOR_SIZE_MAX]; + + if (fdp->loadedDisk == NULL) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + + if (query) break; + + /* (RBIL) [http://www.ctyme.com/intr/rb-2896.htm] + * Offset Size Description (Table 01562) + * 00h BYTE special functions (reserved, must be zero) + * 01h WORD number of disk head + * 03h WORD number of disk cylinder + * 05h WORD number of first sector to read/write + * 07h WORD number of sectors + * 09h DWORD transfer address */ + uint16_t head = mem_readw(ptr+1); + uint16_t cyl = mem_readw(ptr+3); + uint16_t sect = mem_readw(ptr+5); + uint16_t nsect = mem_readw(ptr+7); + uint32_t xfer_addr = mem_readd(ptr+9); + PhysPt xfer_ptr = ((xfer_addr>>16u)<<4u)+(xfer_addr&0xFFFFu); + uint16_t sectsize = fdp->loadedDisk->getSectSize(); + + /* BUT: These are C/H/S values relative to the partition! + * FIXME: MS-DOS may not adjust sector value, or maybe it does... + * perhaps there is a reason Linux fdisk warns about sector alignment to sect/track for MS-DOS partitions? */ + { + uint32_t adj = fdp->GetPartitionOffset();; + sect += adj % fdp->loadedDisk->sectors; + adj /= fdp->loadedDisk->sectors; + head += adj % fdp->loadedDisk->heads; + adj /= fdp->loadedDisk->heads; + cyl += adj; + + while (sect >= fdp->loadedDisk->sectors) { + sect -= fdp->loadedDisk->sectors; + head++; + } + while (head >= fdp->loadedDisk->heads) { + head -= fdp->loadedDisk->heads; + cyl++; + } + + /* finally, MS-DOS counts sectors from 0 and BIOS INT 13h counts from 1 */ + sect++; + } + + LOG_INFO("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", + drive,cyl,head,sect,nsect,xfer_addr >> 16,xfer_addr & 0xFFFF,sectsize); + + while (nsect > 0) { + uint8_t status = fdp->loadedDisk->Read_Sector(head,cyl,sect,sectbuf); + if (status != 0) { + LOG_INFO("IOCTL 0D:61 read error at C/H/S %u/%u/%u",cyl,head,sect); + DOS_SetError(DOSERR_ACCESS_DENIED);//FIXME + return false; + } + + MEM_BlockWrite(xfer_ptr,sectbuf,sectsize); + xfer_ptr += sectsize; + nsect--; + sect++; + } + } + break; + case 0x4A: + case 0x4B: + case 0x6A: + case 0x6B: + if (query) break; + LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call 0D:%2X Drive %2X volume/drive locking IOCTL, faking it",reg_cl,drive); + break; + case 0x67: /* Get access flag (whether allowed by driver) */ + if (query) break; + /* In DOSBox-X, disk access is always allowed. + * Real MS-DOS might be more restrictive, especially Windows 95 which requires volume locking before disk access is allowed */ + /* FDISK.EXE needs this IOCTL to determine whether it can read the partition and therefore whether the "system type" is "FAT16", "FAT12", "unknown" etc. */ + /* ptr+0 = special function (always zero) + * ptr+1 = return whether access allowed */ + mem_writeb(ptr+1,0x01); + break; + default: + LOG_WARNING("DOS:IOCTL %s %2X:%2X Drive %2X unhandled (CH=08h)",query?"Query":"Call",reg_al,reg_cl,drive); + DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); + return false; + } + reg_ax=0; + return true; +} + +bool DOS_IOCTL_AX440D_CH48(uint8_t drive,bool query) { + PhysPt ptr = SegPhys(ds)+reg_dx; + switch (reg_cl) { + case 0x40: /* Set device parameters */ + { + if (Drives[drive]->GetType() != DosDriveType::Fat) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + fatDrive *fdp = dynamic_cast<fatDrive*>(Drives[drive]); + if (fdp == NULL || fdp->readonly) { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + + if (query) break; + + FAT_BootSector::bpb_union_t bpb=fdp->GetBPB(); + if (fdp->loadedDisk != NULL) + fdp->loadedDisk->cylinders = mem_readw(ptr+4); // number of cylinders + + if (mem_readw(ptr+0xd) == 0 && mem_readw(ptr+0xf) == 0 && mem_readw(ptr+0x12) == 0) { // FAT32 BPB? + bpb.v.BPB_BytsPerSec = mem_readw(ptr+7); // bytes per sector (Win3 File Mgr. uses it) + bpb.v.BPB_SecPerClus = mem_readb(ptr+9); // sectors per cluster + bpb.v.BPB_RsvdSecCnt = mem_readw(ptr+0xa); // number of reserved sectors + bpb.v.BPB_NumFATs = mem_readb(ptr+0xc); // number of FATs + bpb.v.BPB_RootEntCnt = mem_readw(ptr+0xd); // number of root entries + bpb.v.BPB_TotSec16 = mem_readw(ptr+0xf); // number of small sectors + bpb.v.BPB_Media = mem_readb(ptr+0x11); // media type + bpb.v.BPB_FATSz16 = (uint16_t)mem_readw(ptr+0x12); // sectors per FAT + bpb.v.BPB_SecPerTrk = (uint16_t)mem_readw(ptr+0x14); // sectors per track + bpb.v.BPB_NumHeads = (uint16_t)mem_readw(ptr+0x16); // number of heads + bpb.v.BPB_HiddSec = (uint32_t)mem_readd(ptr+0x18); // number of hidden sectors + bpb.v.BPB_TotSec32 = (uint32_t)mem_readd(ptr+0x1c); // number of big sectors + bpb.v32.BPB_FATSz32 = (uint32_t)mem_readd(ptr+0x20); // sectors per FAT + bpb.v32.BPB_ExtFlags = (uint16_t)mem_readw(ptr+0x24); + bpb.v32.BPB_FSVer = (uint16_t)mem_readw(ptr+0x26); + bpb.v32.BPB_RootClus = (uint32_t)mem_readd(ptr+0x28); + bpb.v32.BPB_FSInfo = (uint16_t)mem_readw(ptr+0x2C); + bpb.v32.BPB_BkBootSec = (uint16_t)mem_readw(ptr+0x2E); + fdp->SetBPB(bpb); + } else { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + break; + } + case 0x60: /* Get device parameters */ + if (query) break; + { + //mem_writeb(ptr+0,0); // special functions (call value) + mem_writeb(ptr+1,(drive>=2)?0x05:0x07); // type: hard disk(5), 1.44 floppy(7) + mem_writew(ptr+2,(drive>=2)?0x01:0x00); // attributes: bit 0 set for nonremovable + mem_writew(ptr+4,(drive>=2)?0x3FF:0x50);// num of cylinders + mem_writeb(ptr+6,0x00); // media type (00=other type) + // bios parameter block following + fatDrive *fdp; + FAT_BootSector::bpb_union_t bpb; + bool usereal=false; + if (Drives[drive]->GetType() == DosDriveType::Fat) { + fdp = dynamic_cast<fatDrive*>(Drives[drive]); + if (fdp != NULL) { + bpb=fdp->GetBPB(); + if (bpb.v.BPB_BytsPerSec && bpb.v.BPB_Media) + usereal=true; + } + } + if (usereal) { + if (fdp->loadedDisk != NULL) + mem_writew(ptr+4,fdp->loadedDisk->cylinders); // num of cylinders + + if (bpb.is_fat32()) { + mem_writew(ptr+7,bpb.v.BPB_BytsPerSec); // bytes per sector (Win3 File Mgr. uses it) + mem_writeb(ptr+9,bpb.v.BPB_SecPerClus); // sectors per cluster + mem_writew(ptr+0xa,bpb.v.BPB_RsvdSecCnt); // number of reserved sectors + mem_writeb(ptr+0xc,bpb.v.BPB_NumFATs); // number of FATs + mem_writew(ptr+0xd,bpb.v.BPB_RootEntCnt); // number of root entries + mem_writew(ptr+0xf,bpb.v.BPB_TotSec16); // number of small sectors + mem_writeb(ptr+0x11,bpb.v.BPB_Media); // media type + mem_writew(ptr+0x12,(uint16_t)bpb.v.BPB_FATSz16); // sectors per FAT + mem_writew(ptr+0x14,(uint16_t)bpb.v.BPB_SecPerTrk); // sectors per track + mem_writew(ptr+0x16,(uint16_t)bpb.v.BPB_NumHeads); // number of heads + mem_writed(ptr+0x18,(uint32_t)bpb.v.BPB_HiddSec); // number of hidden sectors + mem_writed(ptr+0x1c,(uint32_t)bpb.v.BPB_TotSec32); // number of big sectors + mem_writed(ptr+0x20,(uint32_t)bpb.v32.BPB_FATSz32); // sectors per FAT + mem_writew(ptr+0x24,(uint16_t)bpb.v32.BPB_ExtFlags); + mem_writew(ptr+0x26,(uint16_t)bpb.v32.BPB_FSVer); + mem_writed(ptr+0x28,(uint32_t)bpb.v32.BPB_RootClus); + mem_writew(ptr+0x2C,(uint16_t)bpb.v32.BPB_FSInfo); + mem_writew(ptr+0x2E,(uint16_t)bpb.v32.BPB_BkBootSec); + } + else { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + } else { + DOS_SetError(DOSERR_ACCESS_DENIED); + return false; + } + break; + } + case 0x42: + case 0x46: + case 0x4A: + case 0x4B: + case 0x61: + case 0x62: + case 0x66: + case 0x6A: + case 0x6B: + return DOS_IOCTL_AX440D_CH08(drive,query); + default: + LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call %02X:%2X Drive %2X unhandled (CH=48h)",reg_al,reg_cl,drive); + DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); + return false; + } + reg_ax=0; + return true; +} bool DOS_IOCTL(void) { // LOG(LOG_IOCTL,LOG_WARN)("%X %X %X %X",reg_ax,reg_bx,reg_cx,reg_dx); @@ -156,60 +719,28 @@ bool DOS_IOCTL(void) { } return true; case 0x0D: /* Generic block device request */ + case 0x11: /* query generic ioctl capability */ { if (drive < 2 && !Drives[drive]) { DOS_SetError(DOSERR_ACCESS_DENIED); return false; } - if (reg_ch != 0x08 || Drives[drive]->isRemovable()) { + if (Drives[drive]->isRemovable()) { + LOG_INFO("Attempt IOCTL AX=%04x CX=%04x on removable drive",reg_ax,reg_cx); DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } - PhysPt ptr = SegPhys(ds)+reg_dx; - switch (reg_cl) { - case 0x60: /* Get Device parameters */ - //mem_writeb(ptr+0,0); // special functions (call value) - mem_writeb(ptr+1,(drive>=2)?0x05:0x07); // type: hard disk(5), 1.44 floppy(7) - mem_writew(ptr+2,(drive>=2)?0x01:0x00); // attributes: bit 0 set for nonremovable - mem_writew(ptr+4,0x0000); // num of cylinders - mem_writeb(ptr+6,0x00); // media type (00=other type) - // bios parameter block following - mem_writew(ptr+7,0x0200); // bytes per sector (Win3 File Mgr. uses it) - break; - case 0x46: /* Set volume serial number */ - break; - case 0x66: /* Get volume serial number */ - { - char const* bufin=Drives[drive]->GetLabel(); - char buffer[11];memset(buffer,' ',11); - - char const* find_ext=strchr(bufin,'.'); - if (find_ext) { - Bitu size=(Bitu)(find_ext-bufin); - if (size>8) size=8; - memcpy(buffer,bufin,size); - find_ext++; - memcpy(buffer+8,find_ext,(strlen(find_ext)>3) ? 3 : strlen(find_ext)); - } else { - memcpy(buffer,bufin,(strlen(bufin) > 8) ? 8 : strlen(bufin)); - } - - char buf2[8]={ 'F','A','T','1','6',' ',' ',' '}; - if(drive<2) buf2[4] = '2'; //FAT12 for floppies - - //mem_writew(ptr+0,0); //Info level (call value) - mem_writed(ptr+2,0x1234); //Serial number - MEM_BlockWrite(ptr+6,buffer,11);//volumename - MEM_BlockWrite(ptr+0x11,buf2,8);//filesystem - } - break; - default : - LOG(LOG_IOCTL,LOG_ERROR)("DOS:IOCTL Call 0D:%2X Drive %2X unhandled",reg_cl,drive); + if (reg_ch == 0x08) { + return DOS_IOCTL_AX440D_CH08(drive,reg_al==0x11); + } + else if (reg_ch == 0x48) { + return DOS_IOCTL_AX440D_CH48(drive,reg_al==0x11); // Same functions as CH=08h but for FAT32 drives + } + else { + LOG_INFO("Attempt IOCTL AX=%04x CX=%04x",reg_ax,reg_cx); DOS_SetError(DOSERR_FUNCTION_NUMBER_INVALID); return false; } - reg_ax=0; - return true; } case 0x0E: /* Get Logical Drive Map */ if (drive < 2) { diff --git a/src/dos/drive_fat.cpp b/src/dos/drive_fat.cpp index 796060261..b7d58abe8 100644 --- a/src/dos/drive_fat.cpp +++ b/src/dos/drive_fat.cpp @@ -246,7 +246,10 @@ bool fatFile::Write(uint8_t * data, uint16_t *size) { finalizeWrite: myDrive->directoryBrowse(dirCluster, &tmpentry, dirIndex); tmpentry.entrysize = filelength; - tmpentry.loFirstClust = (uint16_t)firstCluster; + if (myDrive->GetBPB().is_fat32()) + tmpentry.SetCluster32(firstCluster); + else + tmpentry.loFirstClust = (uint16_t)firstCluster; myDrive->directoryChange(dirCluster, &tmpentry, dirIndex); *size =sizecount; @@ -301,7 +304,7 @@ bool fatFile::UpdateDateTimeFromHost(void) { } uint32_t fatDrive::getClustFirstSect(uint32_t clustNum) { - return ((clustNum - 2) * bootbuffer.sectorspercluster) + firstDataSector; + return ((clustNum - 2) * BPB.v.BPB_SecPerClus) + firstDataSector; } uint32_t fatDrive::getClusterValue(uint32_t clustNum) { @@ -321,8 +324,8 @@ uint32_t fatDrive::getClusterValue(uint32_t clustNum) { fatoffset = clustNum * 4; break; } - fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff; - fatentoff = fatoffset % bootbuffer.bytespersector; + fatsectnum = BPB.v.BPB_RsvdSecCnt + (fatoffset / BPB.v.BPB_BytsPerSec) + partSectOff; + fatentoff = fatoffset % BPB.v.BPB_BytsPerSec; if(curFatSect != fatsectnum) { /* Load two sectors at once for FAT12 */ @@ -368,8 +371,8 @@ void fatDrive::setClusterValue(uint32_t clustNum, uint32_t clustValue) { fatoffset = clustNum * 4; break; } - fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff; - fatentoff = fatoffset % bootbuffer.bytespersector; + fatsectnum = BPB.v.BPB_RsvdSecCnt + (fatoffset / BPB.v.BPB_BytsPerSec) + partSectOff; + fatentoff = fatoffset % BPB.v.BPB_BytsPerSec; if(curFatSect != fatsectnum) { /* Load two sectors at once for FAT12 */ @@ -403,11 +406,11 @@ void fatDrive::setClusterValue(uint32_t clustNum, uint32_t clustValue) { var_write((uint32_t *)&fatSectBuffer[fatentoff], clustValue); break; } - for(int fc=0;fc<bootbuffer.fatcopies;fc++) { - writeSector(fatsectnum + (fc * bootbuffer.sectorsperfat), &fatSectBuffer[0]); + for(unsigned int fc=0;fc<BPB.v.BPB_NumFATs;fc++) { + writeSector(fatsectnum + (fc * (BPB.is_fat32() ? BPB.v32.BPB_FATSz32 : BPB.v.BPB_FATSz16)), &fatSectBuffer[0]); if (fattype==FAT12) { - if (fatentoff>=511) - writeSector(fatsectnum+1+(fc * bootbuffer.sectorsperfat), &fatSectBuffer[512]); + if (fatentoff >= (BPB.v.BPB_BytsPerSec-1U)) + writeSector(fatsectnum+1u+(fc * (BPB.is_fat32() ? BPB.v32.BPB_FATSz32 : BPB.v.BPB_FATSz16)), &fatSectBuffer[BPB.v.BPB_BytsPerSec]); } } } @@ -447,6 +450,10 @@ bool fatDrive::getFileDirEntry(char const * const filename, direntry * useEntry, safe_strcpy(dirtoken, filename); findFile=dirtoken; + if (BPB.is_fat32()) { + /* Set to FAT32 root directory */ + currentClust = BPB.v32.BPB_RootClus; + } /* Skip if testing in root directory */ if ((len>0) && (filename[len-1]!='\\')) { //LOG_MSG("Testing for filename %s", filename); @@ -470,7 +477,10 @@ bool fatDrive::getFileDirEntry(char const * const filename, direntry * useEntry, findDir = findNext; } - currentClust = foundEntry.loFirstClust; + if (BPB.is_fat32()) + currentClust = foundEntry.Cluster32(); + else + currentClust = foundEntry.loFirstClust; } } else { /* Set to root directory */ @@ -497,6 +507,10 @@ bool fatDrive::getDirClustNum(char *dir, uint32_t *clustNum, bool parDir) { /* Skip if testing for root directory */ if ((len>0) && (dir[len-1]!='\\')) { + if (BPB.is_fat32()) { + /* Set to FAT32 root directory */ + currentClust = BPB.v32.BPB_RootClus; + } //LOG_MSG("Testing for dir %s", dir); findDir = strtok(dirtoken,"\\"); while(findDir != NULL) { @@ -512,10 +526,15 @@ bool fatDrive::getDirClustNum(char *dir, uint32_t *clustNum, bool parDir) { imgDTA->GetResult(find_name,find_size,find_date,find_time,find_attr); if(!(find_attr &DOS_ATTR_DIRECTORY)) return false; } - currentClust = foundEntry.loFirstClust; - + if (BPB.is_fat32()) + currentClust = foundEntry.Cluster32(); + else + currentClust = foundEntry.loFirstClust; } *clustNum = currentClust; + } else if (BPB.is_fat32()) { + /* Set to FAT32 root directory */ + *clustNum = BPB.v32.BPB_RootClus; } else { /* Set to root directory */ *clustNum = 0; @@ -532,11 +551,11 @@ uint8_t fatDrive::readSector(uint32_t sectnum, void * data) { if (absolute) { return loadedDisk->Read_AbsoluteSector(sectnum, data); } - uint32_t cylindersize = bootbuffer.headcount * bootbuffer.sectorspertrack; + uint32_t cylindersize = (unsigned int)BPB.v.BPB_NumHeads * (unsigned int)BPB.v.BPB_SecPerTrk; uint32_t cylinder = sectnum / cylindersize; sectnum %= cylindersize; - uint32_t head = sectnum / bootbuffer.sectorspertrack; - uint32_t sector = sectnum % bootbuffer.sectorspertrack + 1L; + uint32_t head = sectnum / BPB.v.BPB_SecPerTrk; + uint32_t sector = sectnum % BPB.v.BPB_SecPerTrk + 1L; return loadedDisk->Read_Sector(head, cylinder, sector, data); } @@ -549,38 +568,42 @@ uint8_t fatDrive::writeSector(uint32_t sectnum, void * data) { if (absolute) { return loadedDisk->Write_AbsoluteSector(sectnum, data); } - uint32_t cylindersize = bootbuffer.headcount * bootbuffer.sectorspertrack; + uint32_t cylindersize = (unsigned int)BPB.v.BPB_NumHeads * (unsigned int)BPB.v.BPB_SecPerTrk; uint32_t cylinder = sectnum / cylindersize; sectnum %= cylindersize; - uint32_t head = sectnum / bootbuffer.sectorspertrack; - uint32_t sector = sectnum % bootbuffer.sectorspertrack + 1L; + uint32_t head = sectnum / BPB.v.BPB_SecPerTrk; + uint32_t sector = sectnum % BPB.v.BPB_SecPerTrk + 1L; return loadedDisk->Write_Sector(head, cylinder, sector, data); } +uint32_t fatDrive::getSectSize(void) { + return sector_size; +} + uint32_t fatDrive::getSectorCount() { - if (bootbuffer.totalsectorcount != 0) - return check_cast<uint32_t>(bootbuffer.totalsectorcount); + if (BPB.v.BPB_TotSec16 != 0) + return check_cast<uint32_t>(BPB.v.BPB_TotSec16); else - return bootbuffer.totalsecdword; + return BPB.v.BPB_TotSec32; } uint32_t fatDrive::getSectorSize(void) { - return bootbuffer.bytespersector; + return BPB.v.BPB_BytsPerSec; } uint32_t fatDrive::getClusterSize(void) { - return bootbuffer.sectorspercluster * bootbuffer.bytespersector; + return (unsigned int)BPB.v.BPB_SecPerClus * (unsigned int)BPB.v.BPB_BytsPerSec; } uint32_t fatDrive::getAbsoluteSectFromBytePos(uint32_t startClustNum, uint32_t bytePos) { - return getAbsoluteSectFromChain(startClustNum, bytePos / bootbuffer.bytespersector); + return getAbsoluteSectFromChain(startClustNum, bytePos / BPB.v.BPB_BytsPerSec); } uint32_t fatDrive::getAbsoluteSectFromChain(uint32_t startClustNum, uint32_t logicalSector) { - int32_t skipClust = logicalSector / bootbuffer.sectorspercluster; - uint32_t sectClust = logicalSector % bootbuffer.sectorspercluster; + int32_t skipClust = logicalSector / BPB.v.BPB_SecPerClus; + uint32_t sectClust = logicalSector % BPB.v.BPB_SecPerClus; uint32_t currentClust = startClustNum; uint32_t testvalue; @@ -733,9 +756,9 @@ fatDrive::fatDrive(const char *sysFilename, : loadedDisk(nullptr), created_successfully(true), partSectOff(0), - bootbuffer{{0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}, 0, 0}, - absolute(false), readonly(roflag), + bootbuffer{}, + absolute(false), fattype(0), CountOfClusters(0), firstDataSector(0), @@ -758,12 +781,17 @@ fatDrive::fatDrive(const char *sysFilename, created_successfully = (diskfile != nullptr); if (!created_successfully) return; - fseek(diskfile, 0L, SEEK_END); - filesize = (uint32_t)ftell(diskfile) / 1024L; + fseeko64(diskfile, 0L, SEEK_END); + filesize = (uint32_t)(ftello64(diskfile) / 1024L); is_hdd = (filesize > 2880); /* Load disk image */ loadedDisk.reset(new imageDisk(diskfile, sysFilename, filesize, is_hdd)); + if (loadedDisk->getSectSize() > sizeof(bootbuffer)) { + LOG_MSG("Disk sector/bytes (%u) is too large, not attempting FAT filesystem access",loadedDisk->getSectSize()); + created_successfully = false; + return; + } if(is_hdd) { /* Set user specified harddrive parameters */ @@ -804,27 +832,38 @@ fatDrive::fatDrive(const char *sysFilename, loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer); - bootbuffer.bytespersector = host_to_le(bootbuffer.bytespersector); - bootbuffer.reservedsectors = host_to_le(bootbuffer.reservedsectors); - bootbuffer.rootdirentries = host_to_le(bootbuffer.rootdirentries); - bootbuffer.totalsectorcount = host_to_le(bootbuffer.totalsectorcount); - bootbuffer.sectorsperfat = host_to_le(bootbuffer.sectorsperfat); - bootbuffer.sectorspertrack = host_to_le(bootbuffer.sectorspertrack); - bootbuffer.headcount = host_to_le(bootbuffer.headcount); - bootbuffer.hiddensectorcount = host_to_le(bootbuffer.hiddensectorcount); - bootbuffer.totalsecdword = host_to_le(bootbuffer.totalsecdword); + void* var = &bootbuffer.bpb.v.BPB_BytsPerSec; + bootbuffer.bpb.v.BPB_BytsPerSec = var_read((uint16_t*)var); + var = &bootbuffer.bpb.v.BPB_RsvdSecCnt; + bootbuffer.bpb.v.BPB_RsvdSecCnt = var_read((uint16_t*)var); + var = &bootbuffer.bpb.v.BPB_RootEntCnt; + bootbuffer.bpb.v.BPB_RootEntCnt = var_read((uint16_t*)var); + var = &bootbuffer.bpb.v.BPB_TotSec16; + bootbuffer.bpb.v.BPB_TotSec16 = var_read((uint16_t*)var); + var = &bootbuffer.bpb.v.BPB_FATSz16; + bootbuffer.bpb.v.BPB_FATSz16 = var_read((uint16_t*)var); + var = &bootbuffer.bpb.v.BPB_SecPerTrk; + bootbuffer.bpb.v.BPB_SecPerTrk = var_read((uint16_t*)var); + var = &bootbuffer.bpb.v.BPB_NumHeads; + bootbuffer.bpb.v.BPB_NumHeads = var_read((uint16_t*)var); + var = &bootbuffer.bpb.v.BPB_HiddSec; + bootbuffer.bpb.v.BPB_HiddSec = var_read((uint32_t*)var); + var = &bootbuffer.bpb.v.BPB_TotSec32; + bootbuffer.bpb.v.BPB_TotSec32 = var_read((uint32_t*)var); + var = &bootbuffer.bpb.v.BPB_VolID; + bootbuffer.bpb.v.BPB_VolID = var_read((uint32_t*)var); if (!is_hdd) { /* Identify floppy format */ - if ((bootbuffer.nearjmp[0] == 0x69 || bootbuffer.nearjmp[0] == 0xe9 || - (bootbuffer.nearjmp[0] == 0xeb && bootbuffer.nearjmp[2] == 0x90)) && - (bootbuffer.mediadescriptor & 0xf0) == 0xf0) { + if ((bootbuffer.BS_jmpBoot[0] == 0x69 || bootbuffer.BS_jmpBoot[0] == 0xe9 || + (bootbuffer.BS_jmpBoot[0] == 0xeb && bootbuffer.BS_jmpBoot[2] == 0x90)) && + (bootbuffer.bpb.v.BPB_Media & 0xf0) == 0xf0) { /* DOS 2.x or later format, BPB assumed valid */ - if ((bootbuffer.mediadescriptor != 0xf0 && !(bootbuffer.mediadescriptor & 0x1)) && - (bootbuffer.oemname[5] != '3' || bootbuffer.oemname[6] != '.' || bootbuffer.oemname[7] < '2')) { + if ((bootbuffer.bpb.v.BPB_Media != 0xf0 && !(bootbuffer.bpb.v.BPB_Media & 0x1)) && + (bootbuffer.BS_OEMName[5] != '3' || bootbuffer.BS_OEMName[6] != '.' || bootbuffer.BS_OEMName[7] < '2')) { /* Fix pre-DOS 3.2 single-sided floppy */ - bootbuffer.sectorspercluster = 1; + bootbuffer.bpb.v.BPB_SecPerClus = 1; } } else { /* Read media descriptor in FAT */ @@ -834,30 +873,30 @@ fatDrive::fatDrive(const char *sysFilename, if (mdesc >= 0xf8) { /* DOS 1.x format, create BPB for 160kB floppy */ - bootbuffer.bytespersector = 512; - bootbuffer.sectorspercluster = 1; - bootbuffer.reservedsectors = 1; - bootbuffer.fatcopies = 2; - bootbuffer.rootdirentries = 64; - bootbuffer.totalsectorcount = 320; - bootbuffer.mediadescriptor = mdesc; - bootbuffer.sectorsperfat = 1; - bootbuffer.sectorspertrack = 8; - bootbuffer.headcount = 1; + bootbuffer.bpb.v.BPB_BytsPerSec = 512; + bootbuffer.bpb.v.BPB_SecPerClus = 1; + bootbuffer.bpb.v.BPB_RsvdSecCnt = 1; + bootbuffer.bpb.v.BPB_NumFATs = 2; + bootbuffer.bpb.v.BPB_RootEntCnt = 64; + bootbuffer.bpb.v.BPB_TotSec16 = 320; + bootbuffer.bpb.v.BPB_Media = mdesc; + bootbuffer.bpb.v.BPB_FATSz16 = 1; + bootbuffer.bpb.v.BPB_SecPerTrk = 8; + bootbuffer.bpb.v.BPB_NumHeads = 1; bootbuffer.magic1 = 0x55; // to silence warning bootbuffer.magic2 = 0xaa; if (!(mdesc & 0x2)) { /* Adjust for 9 sectors per track */ - bootbuffer.totalsectorcount = 360; - bootbuffer.sectorsperfat = 2; - bootbuffer.sectorspertrack = 9; + bootbuffer.bpb.v.BPB_TotSec16 = 360; + bootbuffer.bpb.v.BPB_FATSz16 = 2; + bootbuffer.bpb.v.BPB_SecPerTrk = 9; } if (mdesc & 0x1) { /* Adjust for 2 sides */ - bootbuffer.sectorspercluster = 2; - bootbuffer.rootdirentries = 112; - bootbuffer.totalsectorcount *= 2; - bootbuffer.headcount = 2; + bootbuffer.bpb.v.BPB_SecPerClus = 2; + bootbuffer.bpb.v.BPB_RootEntCnt = 112; + bootbuffer.bpb.v.BPB_TotSec16 *= 2; + bootbuffer.bpb.v.BPB_NumHeads = 2; } } else { /* Unknown format */ @@ -867,43 +906,110 @@ fatDrive::fatDrive(const char *sysFilename, } } - if ((bootbuffer.magic1 != 0x55) || (bootbuffer.magic2 != 0xaa)) { + /* accept BPB.. so far */ + BPB = bootbuffer.bpb; + + /* DEBUG */ + LOG_MSG("FAT: BPB says %u sectors/track %u heads %u bytes/sector", + BPB.v.BPB_SecPerTrk, + BPB.v.BPB_NumHeads, + BPB.v.BPB_BytsPerSec); + + if (fatDrive::getSectSize() == 512 && (bootbuffer.magic1 != 0x55 || bootbuffer.magic2 != 0xaa)) { /* Not a FAT filesystem */ LOG_MSG("Loaded image has no valid magicnumbers at the end!"); } + if (BPB.v.BPB_SecPerTrk == 0 || (BPB.v.BPB_SecPerTrk > ((filesize <= 3000) ? 40 : 255)) || + (BPB.v.BPB_NumHeads > ((filesize <= 3000) ? 64 : 255))) { + LOG_MSG("Rejecting image, boot sector has weird values not consistent with FAT filesystem"); + created_successfully = false; + return; + } + sector_size = loadedDisk->getSectSize(); + /* Sanity checks */ - if ((bootbuffer.sectorsperfat == 0) || // FAT32 not implemented yet - (bootbuffer.bytespersector != 512) || // non-standard sector sizes not implemented - (bootbuffer.sectorspercluster == 0) || - (bootbuffer.rootdirentries == 0) || - (bootbuffer.fatcopies == 0) || - (bootbuffer.headcount == 0) || - (bootbuffer.headcount > headscyl) || - (bootbuffer.sectorspertrack == 0) || - (bootbuffer.sectorspertrack > cylsector)) { + if ((BPB.v.BPB_SecPerClus == 0) || + (BPB.v.BPB_NumFATs == 0) || + (BPB.v.BPB_NumHeads == 0) || + (BPB.v.BPB_SecPerTrk > cylsector)) { created_successfully = false; + LOG_MSG("here"); return; } + /* Sanity check: Root directory count is nonzero if FAT16/FAT12, or is zero if FAT32 */ + if (BPB.is_fat32()) { + if (BPB.v.BPB_RootEntCnt != 0) { + LOG_MSG("Sanity check fail: Root directory count != 0 and not FAT32"); + created_successfully = false; + return; + } + } + else { + if (BPB.v.BPB_RootEntCnt == 0) { + LOG_MSG("Sanity check fail: Root directory count == 0 and not FAT32"); + created_successfully = false; + return; + } + } + /* Filesystem must be contiguous to use absolute sectors, otherwise CHS will be used */ - absolute = ((bootbuffer.headcount == headscyl) && (bootbuffer.sectorspertrack == cylsector)); + absolute = ((BPB.v.BPB_NumHeads == headscyl) && (BPB.v.BPB_SecPerTrk == cylsector)); /* Determine FAT format, 12, 16 or 32 */ /* Get size of root dir in sectors */ - uint32_t RootDirSectors = ((bootbuffer.rootdirentries * 32) + (bootbuffer.bytespersector - 1)) / bootbuffer.bytespersector; + uint32_t RootDirSectors; uint32_t DataSectors; - if(bootbuffer.totalsectorcount != 0) { - DataSectors = bootbuffer.totalsectorcount - (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors); - } else { - DataSectors = bootbuffer.totalsecdword - (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors); - - } - CountOfClusters = DataSectors / bootbuffer.sectorspercluster; - firstDataSector = (bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + RootDirSectors) + partSectOff; - firstRootDirSect = bootbuffer.reservedsectors + (bootbuffer.fatcopies * bootbuffer.sectorsperfat) + partSectOff; + if (BPB.is_fat32()) { + /* FAT32 requires use of TotSec32, TotSec16 must be zero. */ + if (BPB.v.BPB_TotSec32 == 0) { + LOG_MSG("BPB_TotSec32 == 0 and FAT32 BPB, not valid"); + created_successfully = false; + return; + } + if (BPB.v32.BPB_RootClus < 2) { + LOG_MSG("BPB_RootClus == 0 and FAT32 BPB, not valid"); + created_successfully = false; + return; + } + if (BPB.v.BPB_FATSz16 != 0) { + LOG_MSG("BPB_FATSz16 != 0 and FAT32 BPB, not valid"); + created_successfully = false; + return; + } + if (BPB.v32.BPB_FATSz32 == 0) { + LOG_MSG("BPB_FATSz32 == 0 and FAT32 BPB, not valid"); + created_successfully = false; + return; + } + + RootDirSectors = 0; /* FAT32 root directory has it's own allocation chain, instead of a fixed location */ + DataSectors = (Bitu)BPB.v.BPB_TotSec32 - ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v32.BPB_FATSz32) + (Bitu)RootDirSectors); + CountOfClusters = DataSectors / BPB.v.BPB_SecPerClus; + firstDataSector = ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v32.BPB_FATSz32) + (Bitu)RootDirSectors) + (Bitu)partSectOff; + firstRootDirSect = 0; + } + else { + if (BPB.v.BPB_FATSz16 == 0) { + LOG_MSG("BPB_FATSz16 == 0 and not FAT32 BPB, not valid"); + created_successfully = false; + return; + } + + RootDirSectors = ((BPB.v.BPB_RootEntCnt * 32u) + (BPB.v.BPB_BytsPerSec - 1u)) / BPB.v.BPB_BytsPerSec; + + if (BPB.v.BPB_TotSec16 != 0) + DataSectors = (Bitu)BPB.v.BPB_TotSec16 - ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)RootDirSectors); + else + DataSectors = (Bitu)BPB.v.BPB_TotSec32 - ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)RootDirSectors); + + CountOfClusters = DataSectors / BPB.v.BPB_SecPerClus; + firstDataSector = ((Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)RootDirSectors) + (Bitu)partSectOff; + firstRootDirSect = (Bitu)BPB.v.BPB_RsvdSecCnt + ((Bitu)BPB.v.BPB_NumFATs * (Bitu)BPB.v.BPB_FATSz16) + (Bitu)partSectOff; + } if(CountOfClusters < 4085) { /* Volume is FAT12 */ @@ -939,7 +1045,7 @@ bool fatDrive::AllocationInfo(uint16_t *_bytes_sector, uint8_t *_sectors_cluster loadedDisk->Get_Geometry(&hs, &cy, §, §size); *_bytes_sector = (uint16_t)sectsize; - *_sectors_cluster = bootbuffer.sectorspercluster; + *_sectors_cluster = BPB.v.BPB_SecPerClus; if (CountOfClusters<65536) { *_total_clusters = (uint16_t)CountOfClusters; @@ -1025,7 +1131,7 @@ bool fatDrive::FileCreate(DOS_File **file, char *name, uint16_t attributes) { /* Empty file created, now lets open it */ /* TODO: check for read-only flag and requested write access */ - *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this); + *file = new fatFile(name, BPB.is_fat32() ? fileEntry.Cluster32() : fileEntry.loFirstClust, fileEntry.entrysize, this); (*file)->flags=OPEN_READWRITE; ((fatFile *)(*file))->dirCluster = dirClust; ((fatFile *)(*file))->dirIndex = subEntry; @@ -1051,7 +1157,7 @@ bool fatDrive::FileOpen(DOS_File **file, char *name, uint32_t flags) { uint32_t dirClust, subEntry; if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false; /* TODO: check for read-only flag and requested write access */ - *file = new fatFile(name, fileEntry.loFirstClust, fileEntry.entrysize, this); + *file = new fatFile(name, BPB.is_fat32() ? fileEntry.Cluster32() : fileEntry.loFirstClust, fileEntry.entrysize, this); (*file)->flags = flags; ((fatFile *)(*file))->dirCluster = dirClust; ((fatFile *)(*file))->dirIndex = subEntry; @@ -1093,7 +1199,10 @@ bool fatDrive::FileUnlink(char * name) { fileEntry.entryname[0] = 0xe5; directoryChange(dirClust, &fileEntry, subEntry); - if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust, 0); + { + const uint32_t chk = BPB.is_fat32() ? fileEntry.Cluster32() : fileEntry.loFirstClust; + if(chk != 0) deleteClustChain(chk, 0); + } return true; } @@ -1119,7 +1228,7 @@ bool fatDrive::FindFirst(char *_dir, DOS_DTA &dta,bool /*fcb_findfirst*/) { return false; } dta.SetDirID(0); - dta.SetDirIDCluster((uint16_t)(cwdDirCluster&0xffff)); + dta.SetDirIDCluster(cwdDirCluster); return FindNextInternal(cwdDirCluster, dta, &dummyClust); } @@ -1181,7 +1290,8 @@ nextfile: entryoffset = dirPos % 16; if(dirClustNumber==0) { - if(dirPos >= bootbuffer.rootdirentries) { + if (BPB.is_fat32()) return false; + if(dirPos >= BPB.v.BPB_RootEntCnt) { DOS_SetError(DOSERR_NO_MORE_FILES); return false; } @@ -1291,6 +1401,13 @@ bool fatDrive::SetFileAttr(const char *name, const uint16_t attr) return true; } +unsigned long fatDrive::GetSerial() { + if (BPB.is_fat32()) + return BPB.v32.BS_VolID?BPB.v32.BS_VolID:0x1234; + else + return BPB.v.BPB_VolID?BPB.v.BPB_VolID:0x1234; +} + bool fatDrive::directoryBrowse(uint32_t dirClustNumber, direntry *useEntry, int32_t entNum, int32_t start/*=0*/) { direntry sectbuf[16]; /* 16 directory entries per sector */ uint32_t logentsector; /* Logical entry sector */ @@ -1307,7 +1424,8 @@ bool fatDrive::directoryBrowse(uint32_t dirClustNumber, direntry *useEntry, int3 entryoffset = dirPos % 16; if(dirClustNumber==0) { - if(dirPos >= bootbuffer.rootdirentries) return false; + assert(!BPB.is_fat32()); + if(dirPos >= BPB.v.BPB_RootEntCnt) return false; tmpsector = firstRootDirSect+logentsector; readSector(tmpsector,sectbuf); } else { @@ -1341,7 +1459,8 @@ bool fatDrive::directoryChange(uint32_t dirClustNumber, direntry *useEntry, int3 entryoffset = dirPos % 16; if(dirClustNumber==0) { - if(dirPos >= bootbuffer.rootdirentries) return false; + assert(!BPB.is_fat32()); + if(dirPos >= BPB.v.BPB_RootEntCnt) return false; tmpsector = firstRootDirSect+logentsector; readSector(tmpsector,sectbuf); } else { @@ -1379,7 +1498,8 @@ bool fatDrive::addDirectoryEntry(uint32_t dirClustNumber, direntry useEntry) { entryoffset = dirPos % 16; if(dirClustNumber==0) { - if(dirPos >= bootbuffer.rootdirentries) return false; + assert(!BPB.is_fat32()); + if(dirPos >= BPB.v.BPB_RootEntCnt) return false; tmpsector = firstRootDirSect+logentsector; readSector(tmpsector,sectbuf); } else { @@ -1414,7 +1534,7 @@ void fatDrive::zeroOutCluster(uint32_t clustNumber) { memset(&secBuffer[0], 0, 512); int i; - for(i=0;i<bootbuffer.sectorspercluster;i++) { + for(i=0;i<BPB.v.BPB_SecPerClus;i++) { writeSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]); } } @@ -1467,8 +1587,14 @@ bool fatDrive::MakeDir(char *dir) { /* [..] entry */ memset(&tmpentry,0, sizeof(direntry)); memcpy(&tmpentry.entryname, ".. ", 11); - tmpentry.loFirstClust = (uint16_t)(dirClust & 0xffff); - tmpentry.hiFirstClust = (uint16_t)(dirClust >> 16); + if (BPB.is_fat32() && dirClust == BPB.v32.BPB_RootClus) { + tmpentry.loFirstClust = (uint16_t)0; + tmpentry.hiFirstClust = (uint16_t)0; + } + else { + tmpentry.loFirstClust = (uint16_t)(dirClust & 0xffff); + tmpentry.hiFirstClust = (uint16_t)(dirClust >> 16); + } tmpentry.attrib = DOS_ATTR_DIRECTORY; addDirectoryEntry(dummyClust, tmpentry); @@ -1494,6 +1620,7 @@ bool fatDrive::RemoveDir(char *dir) { /* Can't remove root directory */ if(dummyClust == 0) return false; + if(BPB.is_fat32() && dummyClust==BPB.v32.BPB_RootClus) return false; /* Get parent directory starting cluster */ if(!getDirClustNum(dir, &dirClust, true)) return false; @@ -1579,3 +1706,53 @@ bool fatDrive::TestDir(char *dir) { uint32_t dummyClust; return getDirClustNum(dir, &dummyClust, false); } + +uint32_t fatDrive::GetPartitionOffset(void) { + return partSectOff; +} + +void fatDrive::SetBPB(const FAT_BootSector::bpb_union_t &bpb) { + if (readonly) return; + BPB.v.BPB_BytsPerSec = bpb.v.BPB_BytsPerSec; + BPB.v.BPB_SecPerClus = bpb.v.BPB_SecPerClus; + BPB.v.BPB_RsvdSecCnt = bpb.v.BPB_RsvdSecCnt; + BPB.v.BPB_NumFATs = bpb.v.BPB_NumFATs; + BPB.v.BPB_RootEntCnt = bpb.v.BPB_RootEntCnt; + BPB.v.BPB_TotSec16 = bpb.v.BPB_TotSec16; + BPB.v.BPB_Media = bpb.v.BPB_Media; + BPB.v.BPB_FATSz16 = bpb.v.BPB_FATSz16; + BPB.v.BPB_SecPerTrk = bpb.v.BPB_SecPerTrk; + BPB.v.BPB_NumHeads = bpb.v.BPB_NumHeads; + BPB.v.BPB_HiddSec = bpb.v.BPB_HiddSec; + BPB.v.BPB_TotSec32 = bpb.v.BPB_TotSec32; + if (!bpb.is_fat32() && (bpb.v.BPB_BootSig == 0x28 || bpb.v.BPB_BootSig == 0x29)) + BPB.v.BPB_VolID = bpb.v.BPB_VolID; + if (bpb.is_fat32() && (bpb.v32.BS_BootSig == 0x28 || bpb.v32.BS_BootSig == 0x29)) + BPB.v32.BS_VolID = bpb.v32.BS_VolID; + if (bpb.is_fat32()) { + BPB.v32.BPB_BytsPerSec = bpb.v32.BPB_BytsPerSec; + BPB.v32.BPB_SecPerClus = bpb.v32.BPB_SecPerClus; + BPB.v32.BPB_RsvdSecCnt = bpb.v32.BPB_RsvdSecCnt; + BPB.v32.BPB_NumFATs = bpb.v32.BPB_NumFATs; + BPB.v32.BPB_RootEntCnt = bpb.v32.BPB_RootEntCnt; + BPB.v32.BPB_TotSec16 = bpb.v32.BPB_TotSec16; + BPB.v32.BPB_Media = bpb.v32.BPB_Media; + BPB.v32.BPB_FATSz32 = bpb.v32.BPB_FATSz32; + BPB.v32.BPB_SecPerTrk = bpb.v32.BPB_SecPerTrk; + BPB.v32.BPB_NumHeads = bpb.v32.BPB_NumHeads; + BPB.v32.BPB_HiddSec = bpb.v32.BPB_HiddSec; + BPB.v32.BPB_TotSec32 = bpb.v32.BPB_TotSec32; + BPB.v32.BPB_FATSz32 = bpb.v32.BPB_FATSz32; + BPB.v32.BPB_ExtFlags = bpb.v32.BPB_ExtFlags; + BPB.v32.BPB_FSVer = bpb.v32.BPB_FSVer; + BPB.v32.BPB_RootClus = bpb.v32.BPB_RootClus; + BPB.v32.BPB_FSInfo = bpb.v32.BPB_FSInfo; + BPB.v32.BPB_BkBootSec = bpb.v32.BPB_BkBootSec; + } + + FAT_BootSector bootbuffer = {}; + loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer); + if (BPB.is_fat32()) bootbuffer.bpb.v32=BPB.v32; + bootbuffer.bpb.v=BPB.v; + loadedDisk->Write_AbsoluteSector(0+partSectOff,&bootbuffer); +} diff --git a/src/dos/program_imgmount.cpp b/src/dos/program_imgmount.cpp index 77e9b58f2..af37be64a 100644 --- a/src/dos/program_imgmount.cpp +++ b/src/dos/program_imgmount.cpp @@ -37,6 +37,69 @@ #include "string_utils.h" #include "../ints/int10.h" +bool DetectMFMsectorPartition(uint8_t buf[], uint32_t fcsize, uint16_t sizes[]) { + // This is used for plain MFM sector format as created by IMGMAKE + // It tries to find the first partition. Addressing is in CHS format. + /* Offset | Length | Description + * +0 | 1 byte | 80 hex = active, 00 = inactive + * +1 | 3 bytes | CHS of first sector in partition + * +4 | 1 byte | partition type + * +5 | 3 bytes | CHS of last sector in partition + * +8 | 4 bytes | LBA of first sector in partition + * +C | 4 bytes | Number of sectors in partition. 0 may mean, use LBA + */ + uint8_t starthead = 0; // start head of partition + uint8_t startsect = 0; // start sector of partition + uint16_t startcyl = 0; // start cylinder of partition + //uint8_t ptype = 0; // Partition Type + uint16_t endcyl = 0; // end cylinder of partition + uint8_t heads = 0; // heads in partition + uint8_t sectors = 0; // sectors per track in partition + uint32_t pe1_size = host_readd(&buf[0x1ca]); + if ((uint32_t)host_readd(&buf[0x1fa]) != 0) { // DOS 2.0-3.21 partition table + pe1_size = host_readd(&buf[0x1fa]); + starthead = buf[0x1ef]; + startsect = (buf[0x1f0] & 0x3fu) - 1u; + startcyl = (unsigned char)buf[0x1f1] | (unsigned int)((buf[0x1f0] & 0xc0) << 2u); + endcyl = (unsigned char)buf[0x1f5] | (unsigned int)((buf[0x1f4] & 0xc0) << 2u); + //ptype = buf[0x1f2]; + heads = buf[0x1f3] + 1u; + sectors = buf[0x1f4] & 0x3fu; + } else if (pe1_size != 0) { // DOS 3.3+ partition table, starting at 0x1BE + starthead = buf[0x1bf]; + startsect = (buf[0x1c0] & 0x3fu) - 1u; + startcyl = (unsigned char)buf[0x1c1] | (unsigned int)((buf[0x1c0] & 0xc0) << 2u); + endcyl = (unsigned char)buf[0x1c5] | (unsigned int)((buf[0x1c4] & 0xc0) << 2u); + //ptype = buf[0x1c2]; + heads = buf[0x1c3] + 1u; + sectors = buf[0x1c4] & 0x3fu; + } + if (pe1_size != 0) { + uint32_t part_start = startsect + sectors * starthead + + startcyl * sectors * heads; + uint32_t part_end = heads * sectors * endcyl; + uint32_t part_len = part_end - part_start; + // partition start/end sanity check + // partition length should not exceed file length + // real partition size can be a few cylinders less than pe1_size + // if more than 1023 cylinders see if first partition fits + // into 1023, else bail. + if (/*(part_len<0) always false because unsigned || */(part_len > pe1_size) || (pe1_size > fcsize) || + ((pe1_size - part_len) / (sectors*heads)>2u) || + ((pe1_size / (heads*sectors))>1023u)) { + //LOG_MSG("start(c,h,s) %u,%u,%u",startcyl,starthead,startsect); + //LOG_MSG("endcyl %u heads %u sectors %u",endcyl,heads,sectors); + //LOG_MSG("psize %u start %u end %u",pe1_size,part_start,part_end); + } else { + sizes[0] = 512; sizes[1] = sectors; + sizes[2] = heads; sizes[3] = (uint16_t)(fcsize / (heads*sectors)); + if (sizes[3]>1023) sizes[3] = 1023; + return true; + } + } + return false; +} + void IMGMOUNT::ListImgMounts(void) { const std::string header_drive = MSG_Get("PROGRAM_MOUNT_STATUS_DRIVE"); @@ -162,7 +225,9 @@ void IMGMOUNT::Run(void) { if (wants_ide) { IDE_Get_Next_Cable_Slot(ide_index, is_second_cable_slot); } - } + } else if (type=="hdd" && wants_ide) { + IDE_Get_Next_Cable_Slot(ide_index, is_second_cable_slot); + } cmd->FindString("-size",str_size,true); if ((type=="hdd") && (str_size.size()==0)) { @@ -322,11 +387,40 @@ void IMGMOUNT::Run(void) { return; } Bitu sectors=(Bitu)(fcsize/(16*63)); - if (sectors*16*63!=fcsize) { + if (sectors*16*63>fcsize) { WriteOut(MSG_Get("PROGRAM_IMGMOUNT_INVALID_GEOMETRY")); return; } - sizes[0]=512; sizes[1]=63; sizes[2]=16; sizes[3]=sectors; + bool assume_lba = false; + if (!DetectMFMsectorPartition(buf, fcsize, sizes)) { + uint8_t ptype = buf[0x1c2]; // Location of DOS 3.3+ partition type + if (ptype == 0x0C/*FAT32+LBA*/ || ptype == 0x0E/*FAT16+LBA*/) { + assume_lba = true; + LOG_MSG("Failed to autodetect geometry, assuming LBA approximation based on first partition type (FAT with LBA)"); + } + if (!assume_lba && (ptype == 0x01 || ptype == 0x04 || ptype == 0x06 || ptype == 0x0B || ptype == 0x0C || ptype == 0x0E)) { + /* buf[] still contains MBR */ + unsigned int i=0; + while (i < 0x20 && buf[i] == 0) i++; + if (i == 0x20) { + assume_lba = true; + LOG_MSG("Failed to autodetect geometry, assuming LBA approximation based on first partition type (FAT-related) and lack of executable code in the MBR"); + } + } + if (!assume_lba && fcsize >= ((4ull*1024ull*1024ull*1024ull)/512ull)) { + assume_lba = true; + LOG_MSG("Failed to autodetect geometry, assuming LBA approximation based on size"); + } + if (assume_lba) { + sizes[0] = 512; + sizes[1] = 63; + sizes[2] = 255; + const Bitu d = sizes[1]*sizes[2]; + sizes[3] = (fcsize + d - 1) / d; /* round up */ + } else { + sizes[0]=512; sizes[1]=63; sizes[2]=16; sizes[3]=sectors; + } + } LOG_MSG("autosized image file: %d:%d:%d:%d",sizes[0],sizes[1],sizes[2],sizes[3]); } @@ -370,6 +464,16 @@ void IMGMOUNT::Run(void) { drive_index(drive) * 9, mediaid); + // If instructed, attach to IDE controller as IDE device + if (wants_ide) { + if (ide_index >= 0) { + if (imgDisks.size() && !imageDiskList[drive_index(drive)]) imageDiskList[drive_index(drive)] = static_cast<fatDrive*>(imgDisks[0])->loadedDisk; + IDE_Hard_Disk_Attach(ide_index, is_second_cable_slot, drive_index(drive)); + } else { + WriteOut(MSG_Get("PROGRAM_IMGMOUNT_IDE_CONTROLLERS_UNAVAILABLE")); + } + } + /* Command uses dta so set it to our internal dta */ RealPt save_dta = dos.dta(); dos.dta(dos.tables.tempdta); diff --git a/src/hardware/ide.cpp b/src/hardware/ide.cpp index ec7f6b3c8..9370a495e 100644 --- a/src/hardware/ide.cpp +++ b/src/hardware/ide.cpp @@ -2491,6 +2491,37 @@ static void IDE_SelfIO_Out(IDEController * /* ide */, io_port_t port, io_val_t v ide_baseio_w(port, val, width); } +/* Get physical (not logical INT 13h) geometry */ +bool IDE_GetPhysGeometry(unsigned char disk,uint32_t &heads,uint32_t &cyl,uint32_t §,uint32_t &size) { + IDEController *ide; + IDEDevice *dev; + Bitu idx,ms; + + for (idx=0;idx < MAX_IDE_CONTROLLERS;idx++) { + ide = GetIDEController(idx); + if (ide == NULL) continue; + + /* for master/slave device... */ + for (ms=0;ms < 2;ms++) { + dev = ide->device[ms]; + if (dev == NULL) continue; + + if (dev->type == IDE_TYPE_HDD) { + IDEATADevice *ata = (IDEATADevice*)dev; + if (ata->bios_disk_index == disk && ata->phys_heads != 0 && ata->phys_sects != 0 && ata->phys_cyls != 0) { + heads = ata->phys_heads; + sect = ata->phys_sects; + cyl = ata->phys_cyls; + size = 512; + return true; + } + } + } + } + + return false; +} + /* INT 13h extensions */ void IDE_EmuINT13DiskReadByBIOS_LBA(uint8_t disk, uint64_t lba) { diff --git a/src/ints/bios_disk.cpp b/src/ints/bios_disk.cpp index f1313f4f2..39775d285 100644 --- a/src/ints/bios_disk.cpp +++ b/src/ints/bios_disk.cpp @@ -164,10 +164,9 @@ uint8_t imageDisk::Read_Sector(uint32_t head,uint32_t cylinder,uint32_t sector,v uint8_t imageDisk::Read_AbsoluteSector(uint32_t sectnum, void *data) { - const uint32_t bytenum = sectnum * sector_size; - + const uint64_t bytenum = sectnum * sector_size; if (last_action == WRITE || bytenum != current_fpos) { - if (fseek(diskimg, bytenum, SEEK_SET) != 0) { + if (fseeko64(diskimg, bytenum, SEEK_SET) != 0) { LOG_ERR("BIOSDISK: Could not seek to sector %u in file '%s': %s", sectnum, diskname, strerror(errno)); return 0xff; @@ -190,16 +189,16 @@ uint8_t imageDisk::Write_Sector(uint32_t head,uint32_t cylinder,uint32_t sector, uint8_t imageDisk::Write_AbsoluteSector(uint32_t sectnum, void *data) { - uint32_t bytenum; + uint64_t bytenum; - bytenum = sectnum * sector_size; + bytenum = (uint64_t)sectnum * sector_size; //LOG_MSG("Writing sectors to %ld at bytenum %d", sectnum, bytenum); if (last_action == READ || bytenum != current_fpos) { - if (fseek(diskimg, bytenum, SEEK_SET) != 0) { - LOG_ERR("BIOSDISK: Could not seek to byte %u in file '%s': %s", - bytenum, diskname, strerror(errno)); + if (fseeko64(diskimg, bytenum, SEEK_SET) != 0) { + LOG_ERR("BIOSDISK: Could not seek to byte %llu in file '%s': %s", + (long long unsigned int)bytenum, diskname, strerror(errno)); return 0xff; } } @@ -318,19 +317,49 @@ static bool driveInactive(uint8_t driveNum) { return false; } +static struct { + uint8_t sz; + uint8_t res; + uint16_t num; + uint16_t off; + uint16_t seg; + uint32_t sector; +} dap; + +static void readDAP(uint16_t seg, uint16_t off) { + dap.sz = real_readb(seg,off++); + dap.res = real_readb(seg,off++); + dap.num = real_readw(seg,off); off += 2; + dap.off = real_readw(seg,off); off += 2; + dap.seg = real_readw(seg,off); off += 2; + + /* Although sector size is 64-bit, 32-bit 2TB limit should be more than enough */ + dap.sector = real_readd(seg,off); off +=4; + + if (real_readd(seg,off)) { + E_Exit("INT13: 64-bit sector addressing not supported"); + } +} + template<typename T, size_t N> static bool has_image(const std::array<T, N> &arr) { auto to_bool = [](const T &x) { return bool(x); }; return std::any_of(std::begin(arr), std::end(arr), to_bool); } +void IDE_ResetDiskByBIOS(unsigned char disk); +void IDE_EmuINT13DiskReadByBIOS_LBA(uint8_t disk, uint64_t lba); +void IDE_EmuINT13DiskReadByBIOS(unsigned char disk,unsigned int cyl,unsigned int head,unsigned sect); +bool IDE_GetPhysGeometry(unsigned char disk,uint32_t &heads,uint32_t &cyl,uint32_t §,uint32_t &size); static Bitu INT13_DiskHandler(void) { uint16_t segat, bufptr; uint8_t sectbuf[512]; uint8_t drivenum; - Bitu t; + Bitu i, t; last_drive = reg_dl; drivenum = GetDosDriveNumber(reg_dl); + if (!drivenum && imageDiskList[2] && (reg_ah == 0x41 || reg_ah == 0x42)) drivenum = 2; + std::fill_n(sectbuf, 512, 0); const bool any_images = has_image(imageDiskList); // unconditionally enable the interrupt flag @@ -360,6 +389,7 @@ static Bitu INT13_DiskHandler(void) { return CBRET_NONE; } if (machine!=MCH_PCJR && reg_dl<0x80) reg_ip++; + if (reg_dl >= 0x80) IDE_ResetDiskByBIOS(reg_dl); last_status = 0x00; CALLBACK_SCF(false); } @@ -412,6 +442,8 @@ static Bitu INT13_DiskHandler(void) { bufptr = reg_bx; for (Bitu i = 0; i < reg_al; i++) { last_status = imageDiskList[drivenum]->Read_Sector((uint32_t)reg_dh, (uint32_t)(reg_ch | ((reg_cl & 0xc0)<< 2)), (uint32_t)((reg_cl & 63)+i), sectbuf); + /* IDE emulation: simulate change of IDE state that would occur on a real machine after INT 13h */ + IDE_EmuINT13DiskReadByBIOS(reg_dl, (uint32_t)(reg_ch | ((reg_cl & 0xc0)<< 2)), (uint32_t)reg_dh, (uint32_t)((reg_cl & 63)+i)); if((last_status != 0x00) || (killRead)) { LOG_MSG("Error in disk read"); killRead = false; @@ -580,6 +612,126 @@ static Bitu INT13_DiskHandler(void) { reg_ah = 0x00; CALLBACK_SCF(false); break; + case 0x41: /* Check Extensions Present */ + if ((reg_bx == 0x55aa) && !(driveInactive(drivenum))) { + LOG_MSG("INT13: Check Extensions Present for drive: 0x%x", reg_dl); + reg_ah=0x1; /* 1.x extension supported */ + reg_bx=0xaa55; /* Extensions installed */ + reg_cx=0x1; /* Extended disk access functions (AH=42h-44h,47h,48h) supported */ + CALLBACK_SCF(false); + break; + } + LOG_MSG("INT13: AH=41h, Function not supported 0x%x for drive: 0x%x", reg_bx, reg_dl); + CALLBACK_SCF(true); + break; + case 0x42: /* Extended Read Sectors From Drive */ + /* Read Disk Address Packet */ + readDAP(SegValue(ds),reg_si); + + if (dap.num==0) { + reg_ah = 0x01; + CALLBACK_SCF(true); + return CBRET_NONE; + } + if (!any_images) { + // Inherit the Earth cdrom (uses it as disk test) + if (((reg_dl&0x80)==0x80) && (reg_dh==0) && ((reg_cl&0x3f)==1)) { + reg_ah = 0; + CALLBACK_SCF(false); + return CBRET_NONE; + } + } + if (driveInactive(drivenum)) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + + segat = dap.seg; + bufptr = dap.off; + for(i=0;i<dap.num;i++) { + last_status = imageDiskList[drivenum]->Read_AbsoluteSector(dap.sector+(uint32_t)i, sectbuf); + + IDE_EmuINT13DiskReadByBIOS_LBA(reg_dl,dap.sector+i); + + if((last_status != 0x00) || (killRead)) { + LOG_MSG("Error in disk read"); + killRead = false; + reg_ah = 0x04; + CALLBACK_SCF(true); + return CBRET_NONE; + } + for(t=0;t<512;t++) { + real_writeb(segat,bufptr,sectbuf[t]); + bufptr++; + } + } + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x43: /* Extended Write Sectors to Drive */ + if(driveInactive(drivenum)) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + + /* Read Disk Address Packet */ + readDAP(SegValue(ds),reg_si); + bufptr = dap.off; + for(i=0;i<dap.num;i++) { + for(t=0;t<imageDiskList[drivenum]->getSectSize();t++) { + sectbuf[t] = real_readb(dap.seg,bufptr); + bufptr++; + } + + last_status = imageDiskList[drivenum]->Write_AbsoluteSector(dap.sector+(uint32_t)i, §buf[0]); + if(last_status != 0x00) { + CALLBACK_SCF(true); + return CBRET_NONE; + } + } + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x48: { /* get drive parameters */ + uint16_t bufsz; + + if(driveInactive(drivenum)) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + + segat = SegValue(ds); + bufptr = reg_si; + bufsz = real_readw(segat,bufptr+0); + if (bufsz < 0x1A) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + if (bufsz > 0x1E) bufsz = 0x1E; + else bufsz = 0x1A; + + tmpheads = tmpcyl = tmpsect = tmpsize = 0; + if (!IDE_GetPhysGeometry(drivenum,tmpheads,tmpcyl,tmpsect,tmpsize)) + imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize); + + real_writew(segat,bufptr+0x00,bufsz); + real_writew(segat,bufptr+0x02,0x0003); /* C/H/S valid, DMA boundary errors handled */ + real_writed(segat,bufptr+0x04,tmpcyl); + real_writed(segat,bufptr+0x08,tmpheads); + real_writed(segat,bufptr+0x0C,tmpsect); + real_writed(segat,bufptr+0x10,tmpcyl*tmpheads*tmpsect); + real_writed(segat,bufptr+0x14,0); + real_writew(segat,bufptr+0x18,512); + if (bufsz >= 0x1E) + real_writed(segat,bufptr+0x1A,0xFFFFFFFF); /* no EDD information available */ + + reg_ah = 0x00; + CALLBACK_SCF(false); + } break; default: LOG(LOG_BIOS,LOG_ERROR)("INT13: Function %x called on drive %x (dos drive %d)", reg_ah, reg_dl, drivenum); reg_ah=0xff; |