From 7e283dc2678ba7c09485c1e728eb000827731415 Mon Sep 17 00:00:00 2001 From: Jonathan Campbell Date: Tue, 9 Jul 2024 01:57:27 -0700 Subject: [PATCH] FAT driver, IMGMOUNT: Add code to allow directing all FAT driver access through INT 13h so that if any program depends on hooking INT 13h to intercept I/O, it will work --- include/bios_disk.h | 2 + src/cpu/callback.cpp | 5 ++ src/dos/dos_programs.cpp | 13 ++++- src/dos/drive_fat.cpp | 73 ++++++++++++++++++++++++ src/dos/drives.h | 2 + src/ints/bios.cpp | 3 + src/ints/bios_disk.cpp | 120 ++++++++++++++++++++++++++++++++++++++- 7 files changed, 213 insertions(+), 5 deletions(-) diff --git a/include/bios_disk.h b/include/bios_disk.h index f1d50c2f20..1d769eccff 100644 --- a/include/bios_disk.h +++ b/include/bios_disk.h @@ -594,7 +594,9 @@ class imageDiskINT13Drive : public imageDisk { virtual ~imageDiskINT13Drive(); uint8_t bios_disk = 0; + bool enable_int13 = false; imageDisk* subdisk = NULL; + bool busy = false; }; #endif diff --git a/src/cpu/callback.cpp b/src/cpu/callback.cpp index fbe7cbd44c..f3ccff0d6f 100644 --- a/src/cpu/callback.cpp +++ b/src/cpu/callback.cpp @@ -691,6 +691,11 @@ Bitu CALLBACK_SetupExtra(Bitu callback, Bitu type, PhysPt physAddress, bool use_ phys_writeb(physAddress+0x01,(uint8_t)0xCF); //An IRET Instruction phys_writew(physAddress+0x02,(uint16_t)0x0ECD); // int 0e phys_writeb(physAddress+0x04,(uint8_t)0xCF); //An IRET Instruction + // for the image disk support to call + phys_writew(physAddress+0x05,(uint16_t)0x13CD); // int 13 + phys_writeb(physAddress+0x07,0xFE); + phys_writeb(physAddress+0x08,0x38); + phys_writew(physAddress+0x09,(uint16_t)call_idle); return (use_cb?9:5); case CB_VESA_WAIT: if (use_cb) E_Exit("VESA wait must not implement a callback handler!"); diff --git a/src/dos/dos_programs.cpp b/src/dos/dos_programs.cpp index 72f7abb5cc..74c957ed97 100644 --- a/src/dos/dos_programs.cpp +++ b/src/dos/dos_programs.cpp @@ -6114,10 +6114,10 @@ class IMGMOUNT : public Program { if ((*i) == "int13") { char buf[32]; - if (drive >= 2) - sprintf(buf,"=%u",drive+0x80-2); + if (drive >= 'C') + sprintf(buf,"=%u",drive+0x80-'C'); else - sprintf(buf,"=%u",drive); + sprintf(buf,"=%u",drive-'A'); (*i) += buf; } @@ -6344,6 +6344,13 @@ class IMGMOUNT : public Program { } } } + + /* now that the image is attached to INT 13h the INT 13 image can use it now */ + if (image->class_id == imageDisk::ID_INT13) { + imageDiskINT13Drive *x = (imageDiskINT13Drive*)image; + x->enable_int13 = true; + LOG_MSG("INT 13 image enabling calling"); + } } return true; } diff --git a/src/dos/drive_fat.cpp b/src/dos/drive_fat.cpp index 1555fa3405..d9e7821ad3 100644 --- a/src/dos/drive_fat.cpp +++ b/src/dos/drive_fat.cpp @@ -2495,6 +2495,8 @@ bool fatDrive::FileCreate(DOS_File **file, const char *name, uint16_t attributes if (unformatted) return false; + checkDiskChange(); + if (readonly) { DOS_SetError(DOSERR_WRITE_PROTECTED); return false; @@ -2598,6 +2600,8 @@ bool fatDrive::FileCreate(DOS_File **file, const char *name, uint16_t attributes bool fatDrive::FileExists(const char *name) { if (unformatted) return false; + checkDiskChange(); + direntry fileEntry = {}; uint32_t dummy1, dummy2; uint16_t save_errorcode = dos.errorcode; @@ -2609,6 +2613,8 @@ bool fatDrive::FileExists(const char *name) { bool fatDrive::FileOpen(DOS_File **file, const char *name, uint32_t flags) { if (unformatted) return false; + checkDiskChange(); + direntry fileEntry = {}; uint32_t dirClust, subEntry; @@ -2639,6 +2645,8 @@ bool fatDrive::FileStat(const char * /*name*/, FileStat_Block *const /*stat_bloc bool fatDrive::FileUnlink(const char * name) { if (unformatted) return false; + checkDiskChange(); + if (readonly) { DOS_SetError(DOSERR_WRITE_PROTECTED); return false; @@ -2688,6 +2696,8 @@ bool fatDrive::FileUnlink(const char * name) { bool fatDrive::FindFirst(const char *_dir, DOS_DTA &dta,bool fcb_findfirst) { if (unformatted) return false; + checkDiskChange(); + direntry dummyClust = {}; // volume label searches always affect root directory, no matter the current directory, at least with FCBs @@ -3050,6 +3060,8 @@ bool fatDrive::FindNext(DOS_DTA &dta) { bool fatDrive::SetFileAttr(const char *name, uint16_t attr) { if (unformatted) return false; + checkDiskChange(); + if (readonly) { DOS_SetError(DOSERR_WRITE_PROTECTED); return false; @@ -3354,6 +3366,8 @@ void fatDrive::zeroOutCluster(uint32_t clustNumber) { bool fatDrive::MakeDir(const char *dir) { if (unformatted) return false; + checkDiskChange(); + const char *lfn = NULL; if (readonly) { @@ -3459,6 +3473,8 @@ bool fatDrive::MakeDir(const char *dir) { bool fatDrive::RemoveDir(const char *dir) { if (unformatted) return false; + checkDiskChange(); + if (readonly) { DOS_SetError(DOSERR_WRITE_PROTECTED); return false; @@ -3530,6 +3546,8 @@ bool fatDrive::RemoveDir(const char *dir) { bool fatDrive::Rename(const char * oldname, const char * newname) { if (unformatted) return false; + checkDiskChange(); + const char *lfn = NULL; if (readonly) { @@ -3646,3 +3664,58 @@ void fatDrive::clusterChainMemory::clear(void) { current_cluster_index = 0; } +void fatDrive::checkDiskChange(void) { + bool chg = false; + + if (loadedDisk->detectDiskChange() && !BPB.is_fat32()) { + LOG_MSG("FAT: disk change\n"); + + FAT_BootSector bootbuffer = {}; + loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer); + + 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 (BPB.v.BPB_FATSz16 == 0) { + LOG_MSG("BPB_FATSz16 == 0 and not FAT32 BPB, not valid"); + return; + } + + uint32_t RootDirSectors; + uint32_t DataSectors; + + 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; + + LOG_MSG("NEW FAT: data=%llu root=%llu rootdirsect=%lu datasect=%lu", + (unsigned long long)firstDataSector,(unsigned long long)firstRootDirSect, + (unsigned long)RootDirSectors,(unsigned long)DataSectors); + } +} + diff --git a/src/dos/drives.h b/src/dos/drives.h index 36f8ee66fc..e157465bb9 100644 --- a/src/dos/drives.h +++ b/src/dos/drives.h @@ -521,6 +521,8 @@ class fatDrive : public DOS_Drive { virtual uint32_t GetFirstClusterOffset(void); virtual uint32_t GetHighestClusterNumber(void); + void checkDiskChange(void); + unsigned char bios_disk = 0; bool unformatted = false; }; diff --git a/src/ints/bios.cpp b/src/ints/bios.cpp index f7736842a1..b56a1073c6 100644 --- a/src/ints/bios.cpp +++ b/src/ints/bios.cpp @@ -9352,6 +9352,8 @@ void BuildACPITable(void) { rsdt_tw.finish(); } +extern unsigned int INT13Xfer; + class BIOS:public Module_base{ private: static Bitu cb_bios_post__func(void) { @@ -9370,6 +9372,7 @@ class BIOS:public Module_base{ INT13_ElTorito_NoEmuDriveNumber = 0; INT13_ElTorito_NoEmuCDROMDrive = 0; INT13_ElTorito_IDEInterface = -1; + INT13Xfer = 0; ACPI_mem_enable(false); ACPI_REGION_SIZE = 0; diff --git a/src/ints/bios_disk.cpp b/src/ints/bios_disk.cpp index aeca730d3b..1ee3e591ef 100644 --- a/src/ints/bios_disk.cpp +++ b/src/ints/bios_disk.cpp @@ -30,6 +30,7 @@ #include "../dos/drives.h" #include "mapper.h" #include "ide.h" +#include "cpu.h" #if defined(_MSC_VER) # pragma warning(disable:4244) /* const fmath::local::uint64_t to double possible loss of data */ @@ -3739,11 +3740,95 @@ imageDiskEmptyDrive::~imageDiskEmptyDrive() { ///// +unsigned int INT13Xfer = 0; +size_t INT13XferSize = 4096; + +static void imageDiskCallINT13(void) { + unsigned int rv = CALLBACK_RealPointer(call_int13); + Bitu oldIF=GETFLAG(IF); + SETFLAGBIT(IF,true); + uint16_t oldcs=SegValue(cs); + uint32_t oldeip=reg_eip; + SegSet16(cs,rv>>16); + reg_eip=(rv&0xFFFF)+4+5; + DOSBOX_RunMachine(); + reg_eip=oldeip; + SegSet16(cs,oldcs); + SETFLAGBIT(IF,oldIF); +} + uint8_t imageDiskINT13Drive::Read_Sector(uint32_t head,uint32_t cylinder,uint32_t sector,void * data,unsigned int req_sector_size) { - return subdisk->Read_Sector(head,cylinder,sector,data,req_sector_size); + if (!enable_int13 || busy) return subdisk->Read_Sector(head,cylinder,sector,data,req_sector_size); + + uint8_t ret = 0x05; + unsigned int retry = 3; + + if (req_sector_size == 0) req_sector_size = sector_size; + +// LOG_MSG("INT13 read C/H/S %u/%u/%u busy=%u",cylinder,head,sector,busy); + + if (!busy && sector_size == req_sector_size && sector_size <= INT13XferSize) { + busy = true; + + if (INT13Xfer == 0) INT13Xfer = DOS_GetMemory(INT13XferSize/16u,"INT 13 transfer buffer"); + + unsigned int s_eax = reg_eax; + unsigned int s_ebx = reg_ebx; + unsigned int s_ecx = reg_ecx; + unsigned int s_edx = reg_edx; + unsigned int s_es = SegValue(es); + unsigned int s_fl = reg_flags; + +again: + reg_eax = 0x200/*read command*/ | 1/*count*/; + reg_ebx = 0; + reg_ch = cylinder; + reg_cl = sector; + reg_dh = head; + reg_dl = bios_disk; + CPU_SetSegGeneral(es,INT13Xfer); + + imageDiskCallINT13(); + + if (reg_flags & FLAG_CF) { + ret = reg_ah; + if (ret == 0) ret = 0x05; + + if (ret == 6/*disk change*/) { + diskChangeFlag = true; + if (--retry > 0) goto again; + } + } + else { + MEM_BlockRead32(INT13Xfer<<4,data,sector_size); + data = (void*)((char*)data + sector_size); + if ((++sector) >= (sectors + 1)) { + assert(sector == (sectors + 1)); + sector = 1; + if ((++head) >= heads) { + assert(head == heads); + head = 0; + cylinder++; + } + } + } + + reg_eax = s_eax; + reg_ebx = s_ebx; + reg_ecx = s_ecx; + reg_edx = s_edx; + reg_flags = s_fl; + CPU_SetSegGeneral(es,s_es); + + busy = false; + } + + return ret; } uint8_t imageDiskINT13Drive::Write_Sector(uint32_t head,uint32_t cylinder,uint32_t sector,const void * data,unsigned int req_sector_size) { + if (INT13Xfer == 0) INT13Xfer = DOS_GetMemory(INT13XferSize/16u,"INT 13 transfer buffer"); + return subdisk->Write_Sector(head,cylinder,sector,data,req_sector_size); } @@ -3804,7 +3889,38 @@ uint32_t imageDiskINT13Drive::getSectSize(void) { } bool imageDiskINT13Drive::detectDiskChange(void) { - return subdisk->detectDiskChange(); + if (enable_int13 && !busy) { + busy = true; + + unsigned int s_eax = reg_eax; + unsigned int s_ebx = reg_ebx; + unsigned int s_ecx = reg_ecx; + unsigned int s_edx = reg_edx; + unsigned int s_fl = reg_flags; + + reg_eax = 0x1600/*disk change detect*/; + reg_dl = bios_disk; + CPU_SetSegGeneral(es,INT13Xfer); + + imageDiskCallINT13(); + + if (reg_flags & FLAG_CF) { + if (reg_ah == 0x06) { + LOG_MSG("INT13 image disk change flag\n"); + diskChangeFlag = true; + } + } + + reg_eax = s_eax; + reg_ebx = s_ebx; + reg_ecx = s_ecx; + reg_edx = s_edx; + reg_flags = s_fl; + + busy = false; + } + + return imageDisk::detectDiskChange(); } imageDiskINT13Drive::imageDiskINT13Drive(imageDisk *sdisk) : imageDisk(ID_INT13) {