Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dosbox-staging/dosbox-staging.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWengier <wengierwu@yahoo.com>2022-09-30 10:35:09 +0300
committerkcgen <kcgen@users.noreply.github.com>2022-11-04 06:12:21 +0300
commit21420ec4f4a7729cd7d4bcfc69e5754a7111e460 (patch)
treed6d94766196d4d98ec631be4f49bdf63dd03f52d
parent7c14affc646665d1907ce32b334a15f8c384e818 (diff)
Integrate FAT32 and large HDD patchesww/fat32-test-1
-rw-r--r--include/bios_disk.h2
-rw-r--r--include/dos_inc.h24
-rw-r--r--include/drives.h170
-rw-r--r--src/dos/dos.cpp12
-rw-r--r--src/dos/dos_devices.cpp2
-rw-r--r--src/dos/dos_ioctl.cpp617
-rw-r--r--src/dos/drive_fat.cpp369
-rw-r--r--src/dos/program_imgmount.cpp110
-rw-r--r--src/hardware/ide.cpp31
-rw-r--r--src/ints/bios_disk.cpp170
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, &sect, &sectsize);
*_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 &sect,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 &sect,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, &sectbuf[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;