From d0e2bfa15fe9f7ca7f52ad137f127220eeeaa36f Mon Sep 17 00:00:00 2001 From: Dean Beeler Date: Sat, 3 Apr 2004 19:24:59 +0000 Subject: [PATCH] Added FAT image access and DOS booter functionality Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@1757 --- include/bios.h | 63 +- include/dos_inc.h | 21 +- src/dos/dos_programs.cpp | 152 ++++- src/dos/drive_fat.cpp | 1271 ++++++++++++++++++++++++++++++++++++++ src/dos/drive_local.cpp | 13 +- src/dos/drives.h | 125 +++- src/ints/bios_disk.cpp | 427 ++++++++++++- 7 files changed, 2024 insertions(+), 48 deletions(-) create mode 100644 src/dos/drive_fat.cpp diff --git a/include/bios.h b/include/bios.h index 18e2fb1d..af1ae858 100644 --- a/include/bios.h +++ b/include/bios.h @@ -16,6 +16,9 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#ifndef _BIOS_H_ +#define _BIOS_H_ + #define BIOS_BASE_ADDRESS_COM1 0x400 #define BIOS_BASE_ADDRESS_COM2 0x402 #define BIOS_BASE_ADDRESS_COM3 0x404 @@ -96,23 +99,57 @@ /* The Section handling Bios Disk Access */ #define BIOS_MAX_DISK 10 -class BIOS_Disk { -public: - virtual Bit8u Read_Sector(Bit8u * count,Bit8u head,Bit16u cylinder,Bit16u sector,Bit8u * data)=0; - virtual Bit8u Write_Sector(Bit8u * count,Bit8u head,Bit16u cylinder,Bit16u sector,Bit8u * data)=0; +struct diskGeo { + Bit32u ksize; /* Size in kilobytes */ + Bit16u secttrack; /* Sectors per track */ + Bit16u headscyl; /* Heads per cylinder */ + Bit16u cylcount; /* Cylinders per side */ + Bit16u biosval; /* Type to return from BIOS */ }; -class imageDisk : public BIOS_Disk { +extern diskGeo DiskGeometryList[]; + +#include +#include "mem.h" +#include "dos_inc.h" + +class imageDisk { public: - Bit8u Read_Sector(Bit8u * count,Bit8u head,Bit16u cylinder,Bit16u sector,Bit8u * data); - Bit8u Write_Sector(Bit8u * count,Bit8u head,Bit16u cylinder,Bit16u sector,Bit8u * data); - imageDisk(char * file); -private: - Bit16u sector_size; - Bit16u heads,cylinders,sectors; - Bit8u * image; + Bit8u Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data); + Bit8u Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data); + Bit8u Read_AbsoluteSector(Bit32u sectnum, void * data); + Bit8u Write_AbsoluteSector(Bit32u sectnum, void * data); + + void Set_Geometry(Bit32u setHeads, Bit32u setCyl, Bit32u setSect, Bit32u setSectSize); + void Get_Geometry(Bit32u * getHeads, Bit32u *getCyl, Bit32u *getSect, Bit32u *getSectSize); + Bit8u GetBiosType(void); + Bit32u getSectSize(void); + imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk); + ~imageDisk() { if(diskimg != NULL) { fclose(diskimg); } }; + + bool hardDrive; + bool active; + FILE *diskimg; + Bit8u diskname[512]; + Bit8u floppytype; + + Bit32u sector_size; + Bit32u heads,cylinders,sectors; }; +void updateDPT(void); + +#define MAX_HDD_IMAGES 2 + +extern imageDisk *imageDiskList[2 + MAX_HDD_IMAGES]; +extern imageDisk *diskSwap[20]; +extern Bits swapPosition; +extern Bit16u imgDTASeg; /* Real memory location of temporary DTA pointer for fat image disk access */ +extern RealPt imgDTAPtr; /* Real memory location of temporary DTA pointer for fat image disk access */ +extern DOS_DTA *imgDTA; + +void swapInDisks(void); +void swapInNextDisk(void); void BIOS_ZeroExtendedSize(void); void char_out(Bit8u chr,Bit32u att,Bit8u page); @@ -123,4 +160,4 @@ void INT2F_StartUp(void); void INT33_StartUp(void); void INT13_StartUp(void); - +#endif diff --git a/include/dos_inc.h b/include/dos_inc.h index d35e28d8..d5805e3b 100644 --- a/include/dos_inc.h +++ b/include/dos_inc.h @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: dos_inc.h,v 1.38 2004-02-28 16:35:42 qbix79 Exp $ */ +/* $Id: dos_inc.h,v 1.39 2004-04-03 19:19:29 canadacow Exp $ */ #ifndef DOS_H_ #define DOS_H_ @@ -67,6 +67,25 @@ struct DOS_Block { } tables; }; +#ifdef _MSC_VER +#pragma pack (1) +#endif +union bootSector { + struct entries { + Bit8u jump[3]; + Bit8u oem_name[8]; + Bit16u bytesect; + Bit8u sectclust; + Bit16u reserve_sect; + Bit8u misc[496]; + } bootdata; + Bit8u rawdata[512]; +} GCC_ATTRIBUTE(packed); +#ifdef _MSC_VER +#pragma pack () +#endif + + enum { MCB_FREE=0x0000,MCB_DOS=0x0008 }; enum { RETURN_EXIT=0,RETURN_CTRLC=1,RETURN_ABORT=2,RETURN_TSR=3}; diff --git a/src/dos/dos_programs.cpp b/src/dos/dos_programs.cpp index 79425a68..6fe1e4b8 100644 --- a/src/dos/dos_programs.cpp +++ b/src/dos/dos_programs.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: dos_programs.cpp,v 1.22 2004-01-10 14:03:34 qbix79 Exp $ */ +/* $Id: dos_programs.cpp,v 1.23 2004-04-03 19:21:28 canadacow Exp $ */ #include #include @@ -28,9 +28,13 @@ #include "regs.h" #include "callback.h" #include "cdrom.h" +#include "dos_system.h" +#include "dos_inc.h" +#include "bios.h" void MSCDEX_SetCDInterface(int intNr, int forceCD); +void IMGMOUNT_ProgramStart(Program * * make); class MOUNT : public Program { public: @@ -196,6 +200,150 @@ static void MEM_ProgramStart(Program * * make) { *make=new MEM; } +extern Bit32u floppytype; + + + +class BOOT : public Program { +private: + FILE *getFSFile(Bit8u * filename, Bit32u *ksize, Bit32u *bsize) { + Bit8u drive; + FILE *tmpfile; + char fullname[DOS_PATHLENGTH]; + + localDrive* ldp=0; + if (!DOS_MakeName((char *)filename,fullname,&drive)) return NULL; + ldp=(localDrive*)Drives[drive]; + tmpfile = ldp->GetSystemFilePtr(fullname, "r"); + if(tmpfile == NULL) { + WriteOut("Bootdisk file does not exist. Failing.\n"); + return NULL; + } + fclose(tmpfile); + tmpfile = ldp->GetSystemFilePtr(fullname, "rb+"); + if(tmpfile == NULL) { + WriteOut("Cannot open bootdisk file. Failing.\n"); + return NULL; + } + + fseek(tmpfile,0L, SEEK_END); + *ksize = (ftell(tmpfile) / 1024); + *bsize = ftell(tmpfile); + return tmpfile; + } + + void printError(void) { + WriteOut("This command boots DosBox from either a floppy or hard disk image.\n\n"); + WriteOut("For this command, one can specify a succession of floppy disks swappable\n"); + WriteOut("by pressing Ctrl-F4, and -l specifies the mounted drive to boot from. If\n"); + WriteOut("no drive letter is specified, this defaults to booting from the A drive.\n"); + WriteOut("The only bootable drive letters are A, C, and D. For booting from a hard\n"); + WriteOut("drive (C or D), the image should have already been mounted using the\n"); + WriteOut("IMGMOUNT command.\n\n"); + WriteOut("The syntax of this command is:\n\n"); + WriteOut("BOOT [diskimg1.img diskimg2.img] [-l driveletter]\n"); + } + + +public: + + void Run(void) { + FILE *usefile; + Bitu i; + Bit32u floppysize, rombytesize; + Bit8u drive; + + if(!cmd->GetCount()) { + printError(); + return; + } + i=0; + drive = 'A'; + while(iGetCount()) { + if(cmd->FindCommand(i+1, temp_line)) { + if(temp_line == "-l") { + /* Specifying drive... next argument then is the drive */ + i++; + if(cmd->FindCommand(i+1, temp_line)) { + drive=toupper(temp_line[0]); + if ((drive != 'A') && (drive != 'C') && (drive != 'D')) { + printError(); + return; + } + + } else { + printError(); + return; + } + i++; + continue; + } + + WriteOut("Opening image file: %s\n", temp_line.c_str()); + usefile = getFSFile((Bit8u *)temp_line.c_str(), &floppysize, &rombytesize); + if(usefile != NULL) { + if(diskSwap[i] != NULL) delete diskSwap[i]; + diskSwap[i] = new imageDisk(usefile, (Bit8u *)temp_line.c_str(), floppysize, false); + + } else { + WriteOut("Cannot open %s", temp_line.c_str()); + return; + } + + } + i++; + } + + swapPosition = 0; + + swapInDisks(); + + if(imageDiskList[drive-65]==NULL) { + WriteOut("Unable to boot off of drive %c", drive); + return; + } + + WriteOut("Booting from drive %c...\n", drive); + + bootSector bootarea; + imageDiskList[drive-65]->Read_Sector(0,0,1,(Bit8u *)&bootarea); + for(i=0;i<512;i++) real_writeb(0, 0x7c00 + i, bootarea.rawdata[i]); + + + + SegSet16(cs, 0); + reg_ip = 0x7c00; + + + + /* Most likely a PCJr ROM */ + /* Write it to E000:0000 */ + /* Code inoperable at the moment */ + + /* + Bit8u rombuff[65536]; + fseek(tmpfile,512L, SEEK_SET); + rombytesize-=512; + fread(rombuff, 1, rombytesize, tmpfile); + fclose(tmpfile); + for(i=0;i +#include +#include +#include +#include "dosbox.h" +#include "dos_inc.h" +#include "drives.h" +#include "support.h" +#include "cross.h" +#include "bios.h" + +#define IMGTYPE_FLOPPY 0 +#define IMGTYPE_ISO 1 +#define IMGTYPE_HDD 2 + +#define FAT12 0 +#define FAT16 1 +#define FAT32 2 + +class fatFile : public DOS_File { +public: + fatFile(const char* name, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive); + bool Read(Bit8u * data,Bit16u * size); + bool Write(Bit8u * data,Bit16u * size); + bool Seek(Bit32u * pos,Bit32u type); + bool Close(); + Bit16u GetInformation(void); + bool UpdateDateTimeFromHost(void); +public: + Bit32u firstCluster; + Bit32u seekpos; + Bit32u filelength; + Bit32u currentSector; + Bit32u curSectOff; + Bit8u sectorBuffer[512]; + /* Record of where in the directory structure this file is located */ + Bit32u dirCluster; + Bit32u dirIndex; + + bool loadedSector; + fatDrive *myDrive; +private: + enum { NONE,READ,WRITE } last_action; + Bit16u info; +}; + + +/* IN - char * filename: Name in regular filename format, e.g. bob.txt */ +/* OUT - char * filearray: Name in DOS directory format, eleven char, e.g. bob txt */ +static void convToDirFile(char *filename, char *filearray) { + Bit32u charidx = 0; + Bit32u flen; + int i; + flen = strlen(filename); + memset(filearray, 32, 11); + for(i=0;i= 11) break; + if(filename[i] != '.') { + filearray[charidx] = filename[i]; + charidx++; + } else { + charidx = 8; + } + } + + + +} + +fatFile::fatFile(const char* name, Bit32u startCluster, Bit32u fileLen, fatDrive *useDrive) { + Bit32u seekto = 0; + firstCluster = startCluster; + myDrive = useDrive; + filelength = fileLen; + loadedSector = false; + curSectOff = 0; + seekpos = 0; + memset(§orBuffer[0], 0, sizeof(sectorBuffer)); + + if(filelength > 0) { + Seek(&seekto, DOS_SEEK_SET); + myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer); + loadedSector = true; + } +} + +bool fatFile::Read(Bit8u * data, Bit16u *size) { + Bit16u sizedec, sizecount; + if(seekpos >= filelength) { + *size = 0; + return true; + } + + sizedec = *size; + sizecount = 0; + while(sizedec != 0) { + if(seekpos >= filelength) { + *size = sizecount; + return true; + } + data[sizecount] = sectorBuffer[curSectOff]; + curSectOff++; + seekpos++; + if(curSectOff >= myDrive->getSectorSize()) { + currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos); + if(currentSector == 0) { + /* EOC reached before EOF */ + LOG_MSG("EOC reached before EOF, seekpos %d, filelen %d", seekpos, filelength); + *size = sizecount; + return true; + } + curSectOff = 0; + myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer); + LOG_MSG("Reading absolute sector at %d for seekpos %d", currentSector, seekpos); + } + --sizedec; + sizecount++; + } + *size =sizecount; + return true; +} + +bool fatFile::Write(Bit8u * data, Bit16u *size) { + /* TODO: Check for read-only bit */ + + direntry tmpentry; + Bit16u sizedec, sizecount; + sizedec = *size; + sizecount = 0; + while(sizedec != 0) { + /* Increase filesize if necessary */ + if(seekpos >= filelength) { + if(filelength == 0) { + firstCluster = myDrive->getFirstFreeClust(); + myDrive->allocateCluster(firstCluster, 0); + } + filelength = seekpos+1; + } + sectorBuffer[curSectOff] = data[sizecount]; + curSectOff++; + seekpos++; + if(curSectOff >= myDrive->getSectorSize()) { + if(loadedSector) myDrive->loadedDisk->Write_AbsoluteSector(currentSector, sectorBuffer); + + currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos); + if(currentSector == 0) { + /* EOC reached before EOF - try to increase file allocation */ + myDrive->appendCluster(firstCluster); + /* Try getting sector again */ + currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos); + if(currentSector == 0) { + /* No can do. lets give up and go home. We must be out of room */ + goto finalizeWrite; + + } + } + curSectOff = 0; + myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer); + + loadedSector = true; + } + --sizedec; + sizecount++; + } + +finalizeWrite: + myDrive->directoryBrowse(dirCluster, &tmpentry, dirIndex); + tmpentry.entrysize = filelength; + tmpentry.loFirstClust = firstCluster; + myDrive->directoryChange(dirCluster, &tmpentry, dirIndex); + + *size =sizecount; + return true; + +} + +bool fatFile::Seek(Bit32u *pos, Bit32u type) { + Bit32s seekto; + + switch(type) { + case DOS_SEEK_SET: + seekto = (Bit32s)*pos; + break; + case DOS_SEEK_CUR: + /* Is this relative seek signed? */ + seekto = (Bit32s)*pos + (Bit32s)seekpos; + break; + case DOS_SEEK_END: + seekto = (Bit32s)filelength + (Bit32s)*pos; + break; + } + LOG_MSG("Seek to %d with type %d (absolute value %d)", *pos, type, seekto); + + if((Bit32u)seekto > filelength) seekto = (Bit32s)filelength; + if(seekto<0) seekto = 0; + seekpos = (Bit32u)seekto; + currentSector = myDrive->getAbsoluteSectFromBytePos(firstCluster, seekpos); + curSectOff = seekpos % myDrive->getSectorSize(); + myDrive->loadedDisk->Read_AbsoluteSector(currentSector, sectorBuffer); + *pos = seekpos; + return true; +} + +bool fatFile::Close() { + /* Flush buffer */ + if (loadedSector) myDrive->loadedDisk->Write_AbsoluteSector(currentSector, sectorBuffer); + + return false; +} + +Bit16u fatFile::GetInformation(void) { + return 0x202; +} + +bool fatFile::UpdateDateTimeFromHost(void) { + return true; +} + +Bit32u fatDrive::getClustFirstSect(Bit32u clustNum) { + return ((clustNum - 2) * bootbuffer.sectorspercluster) + firstDataSector; +} + +Bit32u fatDrive::getClusterValue(Bit32u clustNum) { + Bit32u fatoffset; + Bit32u fatsectnum; + Bit32u fatentoff; + + Bit32u clustValue; + + + /* Load two sectors at once for FAT12 */ + Bit8u sectbuffer[1024]; + + switch(fattype) { + case FAT12: + fatoffset = clustNum + (clustNum / 2); + break; + case FAT16: + fatoffset = clustNum * 2; + break; + case FAT32: + fatoffset = clustNum * 4; + break; + } + fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff; + fatentoff = fatoffset % bootbuffer.bytespersector; + + loadedDisk->Read_AbsoluteSector(fatsectnum, §buffer[0]); + loadedDisk->Read_AbsoluteSector(fatsectnum+1, §buffer[512]); + + switch(fattype) { + case FAT12: + clustValue = *((Bit16u *)§buffer[fatentoff]); + if(clustNum & 0x1) { + clustValue >>= 4; + } else { + clustValue &= 0xfff; + } + break; + case FAT16: + clustValue = *((Bit16u *)§buffer[fatentoff]); + break; + case FAT32: + clustValue = *((Bit32u *)§buffer[fatentoff]); + break; + } + + return clustValue; +} + +void fatDrive::setClusterValue(Bit32u clustNum, Bit32u clustValue) { + Bit32u fatoffset; + Bit32u fatsectnum; + Bit32u fatentoff; + Bit32u tmpValue; + + + /* Load two sectors at once for FAT12 */ + Bit8u sectbuffer[1024]; + + switch(fattype) { + case FAT12: + fatoffset = clustNum + (clustNum / 2); + break; + case FAT16: + fatoffset = clustNum * 2; + break; + case FAT32: + fatoffset = clustNum * 4; + break; + } + fatsectnum = bootbuffer.reservedsectors + (fatoffset / bootbuffer.bytespersector) + partSectOff; + fatentoff = fatoffset % bootbuffer.bytespersector; + + loadedDisk->Read_AbsoluteSector(fatsectnum, §buffer[0]); + loadedDisk->Read_AbsoluteSector(fatsectnum+1, §buffer[512]); + + switch(fattype) { + case FAT12: + tmpValue = *((Bit16u *)§buffer[fatentoff]); + if(clustNum & 0x1) { + clustValue &= 0xfff; + clustValue <<= 4; + tmpValue &= 0xf; + tmpValue |= clustValue; + + } else { + clustValue &= 0xfff; + tmpValue &= 0xf000; + tmpValue |= clustValue; + } + *((Bit16u *)§buffer[fatentoff]) = tmpValue; + break; + case FAT16: + *((Bit16u *)§buffer[fatentoff]) = clustValue; + break; + case FAT32: + *((Bit32u *)§buffer[fatentoff]) = clustValue; + break; + } + int fc; + for(fc=0;fcWrite_AbsoluteSector(fatsectnum + (fc * bootbuffer.sectorsperfat), §buffer[0]); + loadedDisk->Write_AbsoluteSector(fatsectnum+1+(fc * bootbuffer.sectorsperfat), §buffer[512]); + } +} + +bool fatDrive::getEntryName(char *fullname, char *entname) { + Bit16u len = strlen(fullname); + char dirtoken[DOS_PATHLENGTH]; + Bit32u currentClust = 0; + + direntry foundEntry; + char * findDir; + char * findFile; + strcpy(dirtoken,fullname); + + LOG_MSG("Testing for filename %s", fullname); + findDir = strtok(dirtoken,"\\"); + findFile = findDir; + while(findDir != NULL) { + findFile = findDir; + findDir = strtok(NULL,"\\"); + } + strcpy(entname, findFile); + return true; +} + +bool fatDrive::getFileDirEntry(char * filename, direntry * useEntry, Bit32u * dirClust, Bit32u * subEntry) { + Bit16u len = strlen(filename); + char dirtoken[DOS_PATHLENGTH]; + Bit32u currentClust = 0; + + direntry foundEntry; + char * findDir; + char * findFile; + strcpy(dirtoken,filename); + + /* Skip if testing in root directory */ + if ((len>0) && (filename[len-1]!='\\')) { + LOG_MSG("Testing for filename %s", filename); + findDir = strtok(dirtoken,"\\"); + findFile = findDir; + while(findDir != NULL) { + imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir); + imgDTA->SetDirID(0); + + findFile = findDir; + if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) break; + currentClust = foundEntry.loFirstClust; + findDir = strtok(NULL,"\\"); + } + } else { + /* Set to root directory */ + } + + /* Search found directory for our file */ + imgDTA->SetupSearch(0,0x5,findFile); + imgDTA->SetDirID(0); + if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) return false; + + memcpy(useEntry, &foundEntry, sizeof(direntry)); + *dirClust = (Bit32u)currentClust; + *subEntry = ((Bit32u)imgDTA->GetDirID()-1); + + + + return true; +} + +bool fatDrive::getDirClustNum(char *dir, Bit32u *clustNum, bool parDir) { + Bit16u len = strlen(dir); + char dirtoken[DOS_PATHLENGTH]; + Bit32u currentClust = 0; + direntry foundEntry; + char * findDir; + strcpy(dirtoken,dir); + + /* Skip if testing for root directory */ + if ((len>0) && (dir[len-1]!='\\')) { + LOG_MSG("Testing for dir %s", dir); + findDir = strtok(dirtoken,"\\"); + while(findDir != NULL) { + imgDTA->SetupSearch(0,DOS_ATTR_DIRECTORY,findDir); + imgDTA->SetDirID(0); + findDir = strtok(NULL,"\\"); + if(!parDir) { + if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) return false; + } else { + if(findDir == NULL) break; + if(!FindNextInternal(currentClust, *imgDTA, &foundEntry)) return false; + } + currentClust = foundEntry.loFirstClust; + + } + *clustNum = currentClust; + return true; + } else { + /* Set to root directory */ + *clustNum = 0; + return true; + } + return false; + +} + +Bit32u fatDrive::getSectorSize(void) { + return bootbuffer.bytespersector; +} + +Bit32u fatDrive::getAbsoluteSectFromBytePos(Bit32u startClustNum, Bit32u bytePos) { + return getAbsoluteSectFromChain(startClustNum, bytePos / bootbuffer.bytespersector); +} + +Bit32u fatDrive::getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSector) { + Bit32s skipClust = logicalSector / bootbuffer.sectorspercluster; + Bit32u sectClust = logicalSector % bootbuffer.sectorspercluster; + + Bit32u currentClust = startClustNum; + Bit32u testvalue; + + while(skipClust!=0) { + bool isEOF = false; + testvalue = getClusterValue(currentClust); + switch(fattype) { + case FAT12: + if(testvalue >= 0xff8) isEOF = true; + break; + case FAT16: + if(testvalue >= 0xfff8) isEOF = true; + break; + case FAT32: + if(testvalue >= 0xfffffff8) isEOF = true; + break; + } + if((isEOF) && (skipClust>1)) { + //LOG_MSG("End of cluster chain reached before end of logical sector seek!"); + return 0; + } + currentClust = testvalue; + --skipClust; + } + return (getClustFirstSect(currentClust) + sectClust); +} + +void fatDrive::deleteClustChain(Bit32u startCluster) { + Bit32u testvalue; + Bit32u currentClust = startCluster; + bool isEOF = false; + while(!isEOF) { + testvalue = getClusterValue(currentClust); + if(testvalue == 0) { + /* What the crap? Cluster is already empty - BAIL! */ + break; + } + /* Mark cluster as empty */ + setClusterValue(currentClust, 0); + switch(fattype) { + case FAT12: + if(testvalue >= 0xff8) isEOF = true; + break; + case FAT16: + if(testvalue >= 0xfff8) isEOF = true; + break; + case FAT32: + if(testvalue >= 0xfffffff8) isEOF = true; + break; + } + if(isEOF) break; + currentClust = testvalue; + } +} + +Bit32u fatDrive::appendCluster(Bit32u startCluster) { + Bit32u testvalue; + Bit32u currentClust = startCluster; + bool isEOF = false; + + while(!isEOF) { + testvalue = getClusterValue(currentClust); + switch(fattype) { + case FAT12: + if(testvalue >= 0xff8) isEOF = true; + break; + case FAT16: + if(testvalue >= 0xfff8) isEOF = true; + break; + case FAT32: + if(testvalue >= 0xfffffff8) isEOF = true; + break; + } + if(isEOF) break; + currentClust = testvalue; + } + + Bit32u newClust = getFirstFreeClust(); + /* Drive is full */ + if(newClust == 0) return 0; + + if(!allocateCluster(newClust, currentClust)) return 0; + + zeroOutCluster(newClust); + + return newClust; + + + + +} + +bool fatDrive::allocateCluster(Bit32u useCluster, Bit32u prevCluster) { + + /* Can't allocate cluster #0 */ + if(useCluster == 0) return false; + + if(prevCluster != 0) { + /* Refuse to allocate cluster if previous cluster value is zero (unallocated) */ + if(!getClusterValue(prevCluster)) return false; + + /* Point cluster to new cluster in chain */ + setClusterValue(prevCluster, useCluster); + } + + switch(fattype) { + case FAT12: + setClusterValue(useCluster, 0xfff); + break; + case FAT16: + setClusterValue(useCluster, 0xffff); + break; + case FAT32: + setClusterValue(useCluster, 0xffffffff); + break; + } + return true; + +} + +fatDrive::fatDrive(const char *sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, Bit32u startSector) { + FILE *diskfile; + Bit32u filesize; + struct partTable mbrData; + + if(imgDTASeg == 0) { + imgDTASeg = DOS_GetMemory(2); + imgDTAPtr = RealMake(imgDTASeg, 0); + imgDTA = new DOS_DTA(imgDTAPtr); + } + + diskfile = fopen(sysFilename, "rb+"); + if(!diskfile) return; + fseek(diskfile, 0L, SEEK_END); + filesize = (Bit32u)ftell(diskfile) / 1024L; + + /* Load disk image */ + loadedDisk = new imageDisk(diskfile, (Bit8u *)sysFilename, filesize, (filesize > 2880)); + if(!loadedDisk) { + delete this; + return; + } + + if(filesize > 2880) { + /* Set user specified harddrive parameters */ + loadedDisk->Set_Geometry(headscyl, cylinders,cylsector, bytesector); + + loadedDisk->Read_Sector(0,0,1,&mbrData); + startSector = 63; + int m; + for(m=0;m<4;m++) { + /* Pick the first available partition */ + if(mbrData.pentry[m].partSize != 0x00) { + LOG_MSG("Using partition %d on drive; skipping %d sectors", m, mbrData.pentry[m].absSectStart); + startSector = mbrData.pentry[m].absSectStart; + break; + } + } + + partSectOff = startSector; + } else { + /* Floppy disks don't have partitions */ + partSectOff = 0; + } + + loadedDisk->Read_AbsoluteSector(0+partSectOff,&bootbuffer); + if ((bootbuffer.magic1 != 0x55) || (bootbuffer.magic2 != 0xaa)) { + /* Not a FAT filesystem */ + delete this; + return; + } + + if(!bootbuffer.sectorsperfat) { + /* FAT32 not implemented yet */ + delete this; + return; + } + + + /* Determine FAT format, 12, 16 or 32 */ + + /* Get size of root dir in sectors */ + /* TODO: Get 32-bit total sector count if needed */ + Bit32u RootDirSectors = ((bootbuffer.rootdirentries * 32) + (bootbuffer.bytespersector - 1)) / bootbuffer.bytespersector; + Bit32u 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(CountOfClusters < 4085) { + /* Volume is FAT12 */ + LOG_MSG("Mounted FAT volume is FAT12 with %d clusters", CountOfClusters); + fattype = FAT12; + } else if (CountOfClusters < 65525) { + LOG_MSG("Mounted FAT volume is FAT16 with %d clusters", CountOfClusters); + fattype = FAT16; + } else { + LOG_MSG("Mounted FAT volume is FAT32 with %d clusters", CountOfClusters); + fattype = FAT32; + } + + /* There is no cluster 0, this means we are in the root directory */ + cwdDirCluster = 0; + + + + +} + +bool fatDrive::AllocationInfo(Bit16u *_bytes_sector, Bit8u *_sectors_cluster, Bit16u *_total_clusters, Bit16u *_free_clusters) { + Bit32u hs, cy, sect,sectsize; + Bit32u countFree = 0; + int i; + + loadedDisk->Get_Geometry(&hs, &cy, §, §size); + *_bytes_sector = (Bit16u)sectsize; + *_sectors_cluster = bootbuffer.sectorspercluster; + *_total_clusters = CountOfClusters; + for(i=0;iGetBiosType(); } + +bool fatDrive::FileCreate(DOS_File **file, char *name, Bit16u attributes) { + direntry fileEntry; + Bit32u dirClust, subEntry; + char dirName[DOS_NAMELENGTH_ASCII]; + char pathName[11]; + + /* Check if file already exists */ + if(getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false; + + /* Can we even get the name of the file itself? */ + if(!getEntryName(name, &dirName[0])) return false; + convToDirFile(&dirName[0], &pathName[0]); + + /* Can we find the base directory? */ + if(!getDirClustNum(name, &dirClust, true)) return false; + memset(&fileEntry, 0, sizeof(direntry)); + memcpy(&fileEntry.entryname, &pathName[0], 11); + fileEntry.attrib = attributes; + addDirectoryEntry(dirClust, fileEntry); + + /* Check if file exists now */ + if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false; + + /* 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); + ((fatFile *)(*file))->dirCluster = dirClust; + ((fatFile *)(*file))->dirIndex = subEntry; + + return true; +} + +bool fatDrive::FileExists(const char *name) { + direntry fileEntry; + Bit32u dummy1, dummy2; + if(!getFileDirEntry((char *)name, &fileEntry, &dummy1, &dummy2)) return false; + return true; +} + +bool fatDrive::FileOpen(DOS_File **file, char *name, Bit32u flags) { + direntry fileEntry; + Bit32u 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); + ((fatFile *)(*file))->dirCluster = dirClust; + ((fatFile *)(*file))->dirIndex = subEntry; + + return true; +} + +bool fatDrive::FileStat(const char *name, FileStat_Block *const stat_block) { + /* TODO: Stub */ + return false; +} + +bool fatDrive::FileUnlink(char * name) { + direntry fileEntry; + Bit32u dirClust, subEntry; + + if(!getFileDirEntry(name, &fileEntry, &dirClust, &subEntry)) return false; + + fileEntry.entryname[0] = 0xe5; + directoryChange(dirClust, &fileEntry, subEntry); + + if(fileEntry.loFirstClust != 0) deleteClustChain(fileEntry.loFirstClust); + + return true; +} + +bool fatDrive::FindFirst(char *_dir, DOS_DTA &dta) { + direntry dummyClust; + + if(!getDirClustNum(_dir, &cwdDirCluster, false)) return false; + dta.SetDirID(0); + return FindNextInternal(cwdDirCluster, dta, &dummyClust); +} + +char* removeTrailingSpaces(char* str) +{ + char* end = str + strlen(str); + while(*--end == ' ' && end > str); + *++end = '\0'; + return str; +} + +char* removeLeadingSpaces(char* str) +{ + size_t len = strlen(str); + size_t pos = strspn(str," "); + memmove(str,str + pos,len - pos + 1); + return str; +} + +char* trimString(char* str) +{ + return removeTrailingSpaces(removeLeadingSpaces(str)); +} + +bool fatDrive::FindNextInternal(Bit32u dirClustNumber, DOS_DTA &dta, direntry *foundEntry) { + direntry sectbuf[16]; /* 16 directory entries per sector */ + Bit32u logentsector; /* Logical entry sector */ + Bit32u entryoffset; /* Index offset within sector */ + Bit32u tmpsector; + Bit8u attrs; + Bit16u dirPos; + char srch_pattern[DOS_NAMELENGTH_ASCII]; + char find_name[DOS_NAMELENGTH_ASCII]; + char extension[4]; + + dta.GetSearchParams(attrs, srch_pattern); + dirPos = dta.GetDirID(); + +nextfile: + logentsector = dirPos / 16; + entryoffset = dirPos % 16; + + if(dirClustNumber==0) { + loadedDisk->Read_AbsoluteSector(firstRootDirSect+logentsector,sectbuf); + } else { + tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector); + /* A zero sector number can't happen */ + if(tmpsector == 0) return false; + loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + } + dirPos++; + dta.SetDirID(dirPos); + + /* Deleted file entry */ + if (sectbuf[entryoffset].entryname[0] == 0xe5) goto nextfile; + + /* End of directory list */ + if (sectbuf[entryoffset].entryname[0] == 0x00) return false; + + memset(find_name,0,DOS_NAMELENGTH_ASCII); + memset(extension,0,4); + memcpy(find_name,§buf[entryoffset].entryname[0],8); + memcpy(extension,§buf[entryoffset].entryname[8],3); + trimString(&find_name[0]); + trimString(&extension[0]); + if(!(sectbuf[entryoffset].attrib & DOS_ATTR_DIRECTORY)) { + strcat(find_name, "."); + strcat(find_name, extension); + } + + if((attrs & (sectbuf[entryoffset].attrib | 0x21)) == 0) goto nextfile; + if(!WildFileCmp(find_name,srch_pattern)) goto nextfile; + + dta.SetResult(find_name, sectbuf[entryoffset].entrysize, sectbuf[entryoffset].crtDate, sectbuf[entryoffset].crtTime, sectbuf[entryoffset].attrib); + memcpy(foundEntry, §buf[entryoffset], sizeof(direntry)); + + return true; + +} + +bool fatDrive::FindNext(DOS_DTA &dta) { + direntry dummyClust; + + return FindNextInternal(cwdDirCluster, dta, &dummyClust); +} + +bool fatDrive::GetFileAttr(char *name, Bit16u *attr) { + /* TODO: Stub */ + return false; +} + +bool fatDrive::directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum) { + direntry sectbuf[16]; /* 16 directory entries per sector */ + Bit32u logentsector; /* Logical entry sector */ + Bit32u entryoffset; /* Index offset within sector */ + Bit32u tmpsector; + Bit8u attrs; + Bit16u dirPos = 0; + char srch_pattern[DOS_NAMELENGTH_ASCII]; + char find_name[DOS_NAMELENGTH_ASCII]; + char extension[4]; + + while(entNum>=0) { + + logentsector = dirPos / 16; + entryoffset = dirPos % 16; + + if(dirClustNumber==0) { + if(dirPos >= bootbuffer.rootdirentries) return false; + tmpsector = firstRootDirSect+logentsector; + loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + } else { + tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector); + /* A zero sector number can't happen */ + if(tmpsector == 0) return false; + loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + } + dirPos++; + + + /* End of directory list */ + if (sectbuf[entryoffset].entryname[0] == 0x00) return false; + --entNum; + } + + memcpy(useEntry, §buf[entryoffset],sizeof(direntry)); + return true; +} + +bool fatDrive::directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum) { + direntry sectbuf[16]; /* 16 directory entries per sector */ + Bit32u logentsector; /* Logical entry sector */ + Bit32u entryoffset; /* Index offset within sector */ + Bit32u tmpsector = 0; + Bit8u attrs; + Bit16u dirPos = 0; + char srch_pattern[DOS_NAMELENGTH_ASCII]; + char find_name[DOS_NAMELENGTH_ASCII]; + char extension[4]; + + while(entNum>=0) { + + logentsector = dirPos / 16; + entryoffset = dirPos % 16; + + if(dirClustNumber==0) { + if(dirPos >= bootbuffer.rootdirentries) return false; + tmpsector = firstRootDirSect+logentsector; + loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + } else { + tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector); + /* A zero sector number can't happen */ + if(tmpsector == 0) return false; + loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + } + dirPos++; + + + /* End of directory list */ + if (sectbuf[entryoffset].entryname[0] == 0x00) return false; + --entNum; + } + if(tmpsector != 0) { + memcpy(§buf[entryoffset], useEntry, sizeof(direntry)); + loadedDisk->Write_AbsoluteSector(tmpsector, sectbuf); + return true; + } else { + return false; + } +} + +bool fatDrive::addDirectoryEntry(Bit32u dirClustNumber, direntry useEntry) { + direntry sectbuf[16]; /* 16 directory entries per sector */ + Bit32u logentsector; /* Logical entry sector */ + Bit32u entryoffset; /* Index offset within sector */ + Bit32u tmpsector; + Bit8u attrs; + Bit16u dirPos = 0; + char srch_pattern[DOS_NAMELENGTH_ASCII]; + char find_name[DOS_NAMELENGTH_ASCII]; + char extension[4]; + + while(true) { + + logentsector = dirPos / 16; + entryoffset = dirPos % 16; + + if(dirClustNumber==0) { + if(dirPos >= bootbuffer.rootdirentries) return false; + tmpsector = firstRootDirSect+logentsector; + loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + } else { + tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector); + /* A zero sector number can't happen - we need to allocate more room for this directory*/ + if(tmpsector == 0) { + Bit32u newClust; + newClust = appendCluster(dirClustNumber); + if(newClust == 0) return false; + /* Try again to get tmpsector */ + tmpsector = getAbsoluteSectFromChain(dirClustNumber, logentsector); + if(tmpsector == 0) return false; /* Give up if still can't get more room for directory */ + } + loadedDisk->Read_AbsoluteSector(tmpsector,sectbuf); + } + dirPos++; + + /* Deleted file entry or end of directory list */ + if ((sectbuf[entryoffset].entryname[0] == 0xe5) || (sectbuf[entryoffset].entryname[0] == 0x00)) { + sectbuf[entryoffset] = useEntry; + loadedDisk->Write_AbsoluteSector(tmpsector,sectbuf); + return true; + } + } + + + return false; +} + +void fatDrive::zeroOutCluster(Bit32u clustNumber) { + Bit8u secBuffer[512]; + + memset(&secBuffer[0], 0, 512); + + int i; + for(i=0;iWrite_AbsoluteSector(getAbsoluteSectFromChain(clustNumber,i), &secBuffer[0]); + } + +} + +bool fatDrive::MakeDir(char *dir) { + Bit32u dummyClust, dirClust; + direntry tmpentry; + char dirName[DOS_NAMELENGTH_ASCII]; + char pathName[11]; + + /* Can we even get the name of the directory itself? */ + if(!getEntryName(dir, &dirName[0])) return false; + convToDirFile(&dirName[0], &pathName[0]); + + /* Fail to make directory if already exists */ + if(getDirClustNum(dir, &dummyClust, false)) return false; + + dummyClust = getFirstFreeClust(); + /* No more space */ + if(dummyClust == 0) return false; + + if(!allocateCluster(dummyClust, 0)) return false; + + zeroOutCluster(dummyClust); + + /* Can we find the base directory? */ + if(!getDirClustNum(dir, &dirClust, true)) return false; + + /* Add the new directory to the base directory */ + memset(&tmpentry,0, sizeof(direntry)); + memcpy(&tmpentry.entryname, &pathName[0], 11); + tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff); + tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16); + tmpentry.attrib = DOS_ATTR_DIRECTORY; + addDirectoryEntry(dirClust, tmpentry); + + /* Add the [.] and [..] entries to our new directory*/ + /* [.] entry */ + memset(&tmpentry,0, sizeof(direntry)); + memcpy(&tmpentry.entryname, ". ", 11); + tmpentry.loFirstClust = (Bit16u)(dummyClust & 0xffff); + tmpentry.hiFirstClust = (Bit16u)(dummyClust >> 16); + tmpentry.attrib = DOS_ATTR_DIRECTORY; + addDirectoryEntry(dummyClust, tmpentry); + + /* [..] entry */ + memset(&tmpentry,0, sizeof(direntry)); + memcpy(&tmpentry.entryname, ".. ", 11); + tmpentry.loFirstClust = (Bit16u)(dirClust & 0xffff); + tmpentry.hiFirstClust = (Bit16u)(dirClust >> 16); + tmpentry.attrib = DOS_ATTR_DIRECTORY; + addDirectoryEntry(dummyClust, tmpentry); + + return true; + +} + +bool fatDrive::RemoveDir(char *dir) { + Bit32u dummyClust, dirClust; + direntry tmpentry; + char dirName[DOS_NAMELENGTH_ASCII]; + char pathName[11]; + + /* Can we even get the name of the directory itself? */ + if(!getEntryName(dir, &dirName[0])) return false; + convToDirFile(&dirName[0], &pathName[0]); + + /* Get directory starting cluster */ + if(!getDirClustNum(dir, &dummyClust, false)) return false; + + /* Can't remove root directory */ + if(dummyClust == 0) return false; + + /* Get parent directory starting cluster */ + if(!getDirClustNum(dir, &dirClust, true)) return false; + + /* Check to make sure directory is empty */ + Bit32u filecount = 0; + /* Set to 2 to skip first 2 entries, [.] and [..] */ + Bit32s fileidx = 2; + while(directoryBrowse(dummyClust, &tmpentry, fileidx)) { + /* Check for non-deleted files */ + if(tmpentry.entryname[0] != 0xe5) filecount++; + fileidx++; + } + + /* Return if directory is not empty */ + if(filecount > 0) return false; + + /* Find directory entry in parent directory */ + fileidx = 2; + bool found = false; + while(directoryBrowse(dirClust, &tmpentry, fileidx)) { + if(memcmp(&tmpentry.entryname, &pathName[0], 11) == 0) { + found = true; + tmpentry.entryname[0] = 0xe5; + directoryChange(dirClust, &tmpentry, fileidx); + deleteClustChain(dummyClust); + + break; + } + fileidx++; + } + + if(!found) return false; + + return true; +} + +bool fatDrive::Rename(char *oldname, char*newname) { + + return false; +} + +bool fatDrive::TestDir(char *dir) { + Bit32u dummyClust; + return getDirClustNum(dir, &dummyClust, false); +} + + +class IMGMOUNT : public Program { +public: + void Run(void) + { + DOS_Drive * newdrive; + imageDisk * newImage; + Bit32u imagesize; + char drive; + std::string label; + + std::string type="hdd"; + std::string fstype="fat"; + cmd->FindString("-t",type,true); + cmd->FindString("-fs",fstype,true); + Bit8u mediaid; + if (type=="floppy" || type=="hdd" || type=="iso") { + Bit16u sizes[4]; + + std::string str_size; + mediaid=0xF8; + + if (type=="floppy") { + mediaid=0xF0; + } else if (type=="cdrom") { + str_size="650,127,16513,1700"; + mediaid=0xF8; + } + cmd->FindString("-size",str_size,true); + if ((type=="hdd") && (str_size.size()==0)) { + WriteOut("Must specify drive geometry for hard drives:\n"); + WriteOut("bytes_per_sector, sectors_per_cylinder, heads_per_cylinder, cylinder_count\n"); + return; + } + char number[20]; + const char * scan=str_size.c_str(); + Bitu index=0;Bitu count=0; + + while (*scan) { + if (*scan==',') { + number[index]=0;sizes[count++]=atoi(number); + index=0; + } else number[index++]=*scan; + scan++; + } + number[index]=0;sizes[count++]=atoi(number); + + if(fstype=="fat") { + // get the drive letter + cmd->FindCommand(1,temp_line); + if ((temp_line.size() > 2) || ((temp_line.size()>1) && (temp_line[1]!=':'))) { + WriteOut("Must specify drive letter to mount image at.\n"); + return; + } + drive=toupper(temp_line[0]); + if (!isalpha(drive)) { + WriteOut("Must specify drive letter to mount image at.\n"); + return; + } + } else if (fstype=="none") { + cmd->FindCommand(1,temp_line); + if ((temp_line.size() > 1) || (!isdigit(temp_line[0]))) { + WriteOut("Must specify drive number (0 or 3) to mount image at (0,1=fda,fdb;2,3=hda,hdb)\n"); + return; + } + drive=temp_line[0]-'0'; + if(drive>3) { + WriteOut("Must specify drive number (0 or 3) to mount image at (0,1=fda,fdb;2,3=hda,hdb)\n"); + return; + } + } else { + WriteOut("Format \"%s\" is unsupported. Specify \"fat\" or \"none\".\n"); + return; + } + + if (!cmd->FindCommand(2,temp_line)) { + WriteOut("Must specify file image to mount\n"); + return; + } + if (!temp_line.size()) { + WriteOut("Must specify file image to mount\n"); + return; + } + struct stat test; + if (stat(temp_line.c_str(),&test)) { + WriteOut("Image file not found\n"); + return; + } + + if ((test.st_mode & S_IFDIR)) { + WriteOut("To mount directories, use the MOUNT command, not the IMGMOUNT command\n"); + return; + } + + if(fstype=="fat") { + newdrive=new fatDrive(temp_line.c_str(),sizes[0],sizes[1],sizes[2],sizes[3],0); + } else { + FILE *newDisk = fopen(temp_line.c_str(), "rb+"); + fseek(newDisk,0L, SEEK_END); + imagesize = (ftell(newDisk) / 1024); + + newImage = new imageDisk(newDisk, (Bit8u *)temp_line.c_str(), imagesize, (imagesize > 2880)); + if(imagesize>2880) newImage->Set_Geometry(sizes[2],sizes[3],sizes[1],sizes[0]); + } + } + if(fstype=="fat") { + if (Drives[drive-'A']) { + WriteOut("Drive already mounted at that letter\n"); + if (newdrive) delete newdrive; + return; + } + if (!newdrive) WriteOut("Can't create drive from file\n"); + Drives[drive-'A']=newdrive; + // Set the correct media byte in the table + mem_writeb(Real2Phys(dos.tables.mediaid)+drive-'A',mediaid); + WriteOut("Drive %c mounted as %s\n",drive,temp_line.c_str()); + if(((fatDrive *)newdrive)->loadedDisk->hardDrive) { + if(imageDiskList[2] == NULL) { + imageDiskList[2] = ((fatDrive *)newdrive)->loadedDisk; + updateDPT(); + return; + } + if(imageDiskList[3] == NULL) { + imageDiskList[3] = ((fatDrive *)newdrive)->loadedDisk; + updateDPT(); + return; + } + } + if(!((fatDrive *)newdrive)->loadedDisk->hardDrive) { + imageDiskList[0] = ((fatDrive *)newdrive)->loadedDisk; + } + } else if (fstype=="none") { + if(imageDiskList[drive] != NULL) delete imageDiskList[drive]; + imageDiskList[drive] = newImage; + updateDPT(); + WriteOut("Drive number %d mounted as %s\n",drive,temp_line.c_str()); + } + + // check if volume label is given + //if (cmd->FindString("-label",label,true)) newdrive->dirCache.SetLabel(label.c_str()); + return; + } +}; + +void IMGMOUNT_ProgramStart(Program * * make) { + *make=new IMGMOUNT; +} + + diff --git a/src/dos/drive_local.cpp b/src/dos/drive_local.cpp index a3509725..c395c7ae 100644 --- a/src/dos/drive_local.cpp +++ b/src/dos/drive_local.cpp @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: drive_local.cpp,v 1.45 2004-03-04 19:49:21 qbix79 Exp $ */ +/* $Id: drive_local.cpp,v 1.46 2004-04-03 19:23:06 canadacow Exp $ */ #include #include @@ -103,6 +103,17 @@ bool localDrive::FileOpen(DOS_File * * file,char * name,Bit32u flags) { return true; }; +FILE * localDrive::GetSystemFilePtr(char * name, char * type) { + + char newname[CROSS_LEN]; + strcpy(newname,basedir); + strcat(newname,name); + CROSS_FILENAME(newname); + dirCache.ExpandName(newname); + + return fopen(newname,type); +} + bool localDrive::FileUnlink(char * name) { char newname[CROSS_LEN]; strcpy(newname,basedir); diff --git a/src/dos/drives.h b/src/dos/drives.h index 1e5f7395..526386e8 100644 --- a/src/dos/drives.h +++ b/src/dos/drives.h @@ -16,7 +16,7 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* $Id: drives.h,v 1.19 2004-01-10 14:03:34 qbix79 Exp $ */ +/* $Id: drives.h,v 1.20 2004-04-03 19:22:33 canadacow Exp $ */ #ifndef _DRIVES_H__ #define _DRIVES_H__ @@ -24,6 +24,7 @@ #include #include "dos_system.h" #include "shell.h" /* for DOS_Shell */ +#include "bios.h" /* for fatDrive */ bool WildFileCmp(const char * file, const char * wild); @@ -31,6 +32,7 @@ class localDrive : public DOS_Drive { public: localDrive(const char * startdir,Bit16u _bytes_sector,Bit8u _sectors_cluster,Bit16u _total_clusters,Bit16u _free_clusters,Bit8u _mediaid); virtual bool FileOpen(DOS_File * * file,char * name,Bit32u flags); + virtual FILE *GetSystemFilePtr(char * name, char * type); virtual bool FileCreate(DOS_File * * file,char * name,Bit16u attributes); virtual bool FileUnlink(char * name); virtual bool RemoveDir(char * dir); @@ -61,6 +63,127 @@ private: } allocation; }; +#ifdef _MSC_VER +#pragma pack (1) +#endif +struct bootstrap { + Bit8u nearjmp[3]; + Bit8u oemname[8]; + Bit16u bytespersector; + Bit8u sectorspercluster; + Bit16u reservedsectors; + Bit8u fatcopies; + Bit16u rootdirentries; + Bit16u totalsectorcount; + Bit8u mediadescriptor; + Bit16u sectorsperfat; + Bit16u sectorspertrack; + Bit16u headcount; + /* 32-bit FAT extensions */ + Bit32u hiddensectorcount; + Bit32u totalsecdword; + Bit8u bootcode[474]; + Bit8u magic1; /* 0x55 */ + Bit8u magic2; /* 0xaa */ +} GCC_ATTRIBUTE(packed); + +struct direntry { + Bit8u entryname[11]; + Bit8u attrib; + Bit8u NTRes; + Bit8u milliSecondStamp; + Bit16u crtTime; + Bit16u crtDate; + Bit16u accessDate; + Bit16u hiFirstClust; + Bit16u modTime; + Bit16u modDate; + Bit16u loFirstClust; + Bit32u entrysize; +} GCC_ATTRIBUTE(packed); + +struct partTable { + Bit8u booter[446]; + struct { + Bit8u bootflag; + Bit8u beginchs[3]; + Bit8u parttype; + Bit8u endchs[3]; + Bit32u absSectStart; + Bit32u partSize; + } pentry[4]; + Bit8u magic1; /* 0x55 */ + Bit8u magic2; /* 0xaa */ +} GCC_ATTRIBUTE(packed); + +#ifdef _MSC_VER +#pragma pack () +#endif + +class fatDrive : public DOS_Drive { +public: + fatDrive(const char * sysFilename, Bit32u bytesector, Bit32u cylsector, Bit32u headscyl, Bit32u cylinders, Bit32u startSector); + virtual bool FileOpen(DOS_File * * file,char * name,Bit32u flags); + virtual bool FileCreate(DOS_File * * file,char * name,Bit16u attributes); + virtual bool FileUnlink(char * name); + virtual bool RemoveDir(char * dir); + virtual bool MakeDir(char * dir); + virtual bool TestDir(char * dir); + virtual bool FindFirst(char * _dir,DOS_DTA & dta); + virtual bool FindNext(DOS_DTA & dta); + virtual bool GetFileAttr(char * name,Bit16u * attr); + virtual bool Rename(char * oldname,char * newname); + virtual bool AllocationInfo(Bit16u * _bytes_sector,Bit8u * _sectors_cluster,Bit16u * _total_clusters,Bit16u * _free_clusters); + virtual bool FileExists(const char* name); + virtual bool FileStat(const char* name, FileStat_Block * const stat_block); + virtual Bit8u GetMediaByte(void); + virtual bool isRemote(void); +public: + Bit32u getAbsoluteSectFromBytePos(Bit32u startClustNum, Bit32u bytePos); + Bit32u getSectorSize(void); + Bit32u getAbsoluteSectFromChain(Bit32u startClustNum, Bit32u logicalSector); + bool allocateCluster(Bit32u useCluster, Bit32u prevCluster); + Bit32u appendCluster(Bit32u startCluster); + void deleteClustChain(Bit32u startCluster); + Bit32u getFirstFreeClust(void); + bool directoryBrowse(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum); + bool directoryChange(Bit32u dirClustNumber, direntry *useEntry, Bit32s entNum); + imageDisk *loadedDisk; +private: + Bit32u getClusterValue(Bit32u clustNum); + void setClusterValue(Bit32u clustNum, Bit32u clustValue); + Bit32u getClustFirstSect(Bit32u clustNum); + bool FindNextInternal(Bit32u dirClustNumber, DOS_DTA & dta, direntry *foundEntry); + bool getDirClustNum(char * dir, Bit32u * clustNum, bool parDir); + bool getFileDirEntry(char * filename, direntry * useEntry, Bit32u * dirClust, Bit32u * subEntry); + bool addDirectoryEntry(Bit32u dirClustNumber, direntry useEntry); + void zeroOutCluster(Bit32u clustNumber); + bool getEntryName(char *fullname, char *entname); + friend void DOS_Shell::CMD_SUBST(char* args); + struct { + char srch_dir[CROSS_LEN]; + } srchInfo[MAX_OPENDIRS]; + + struct { + Bit16u bytes_sector; + Bit8u sectors_cluster; + Bit16u total_clusters; + Bit16u free_clusters; + Bit8u mediaid; + } allocation; + + bootstrap bootbuffer; + Bit8u fattype; + Bit32u CountOfClusters; + Bit32u partSectOff; + Bit32u firstDataSector; + Bit32u firstRootDirSect; + + Bit32u cwdDirCluster; + Bit32u dirPosition; /* Position in directory search */ +}; + + class cdromDrive : public localDrive { public: diff --git a/src/ints/bios_disk.cpp b/src/ints/bios_disk.cpp index 3e7a38f4..9bbc85f7 100644 --- a/src/ints/bios_disk.cpp +++ b/src/ints/bios_disk.cpp @@ -21,60 +21,425 @@ #include "bios.h" #include "regs.h" #include "mem.h" +#include "keyboard.h" #include "dos_inc.h" /* for Drives[] */ -static Bitu call_int13; -static BIOS_Disk * Floppys[2]; -static BIOS_Disk * Harddisks[BIOS_MAX_DISK]; +#define MAX_SWAPPABLE_DISKS 20 + +diskGeo DiskGeometryList[] = { + {160, 8, 1, 40, 0}, + {180, 9, 1, 40, 0}, + {320, 8, 2, 40, 1}, + {360, 9, 2, 40, 1}, + {720, 9, 2, 80, 3}, + {1200, 15, 2, 80, 2}, + {1440, 18, 2, 80, 4}, + {2880, 36, 2, 80, 6}, + {0, 0, 0 , 0}, +}; + +Bitu call_int13; +Bitu diskparm0, diskparm1; static Bit8u last_status; +static Bit8u last_drive; +Bit16u imgDTASeg; +RealPt imgDTAPtr; +DOS_DTA *imgDTA; +bool killRead; + +/* 2 floppys and 2 harddrives, max */ +imageDisk *imageDiskList[4]; +imageDisk *diskSwap[MAX_SWAPPABLE_DISKS]; +Bits swapPosition; + +void updateDPT(void) { + Bit32u tmpheads, tmpcyl, tmpsect, tmpsize; + if(imageDiskList[2] != NULL) { + imageDiskList[2]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize); + real_writew(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0)),tmpcyl); + real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+2,tmpheads); + real_writew(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0x3,0); + real_writew(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0x5,-1); + real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0x7,0); + real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0x8,(0xc0 | (((imageDiskList[2]->heads) > 8) << 3))); + real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0x9,0); + real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0xa,0); + real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0xb,0); + real_writew(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0xc,tmpcyl); + real_writeb(RealSeg(CALLBACK_RealPointer(diskparm0)),RealOff(CALLBACK_RealPointer(diskparm0))+0xe,tmpsect); + + + + } + if(imageDiskList[3] != NULL) { + imageDiskList[3]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize); + real_writew(RealSeg(CALLBACK_RealPointer(diskparm1)),RealOff(CALLBACK_RealPointer(diskparm1)),tmpcyl); + real_writeb(RealSeg(CALLBACK_RealPointer(diskparm1)),RealOff(CALLBACK_RealPointer(diskparm1))+2,tmpheads); + real_writeb(RealSeg(CALLBACK_RealPointer(diskparm1)),RealOff(CALLBACK_RealPointer(diskparm1))+0xe,tmpsect); + } -static Bitu INT13_SmallHandler(void) { - switch (reg_ah) { +} + +void swapInDisks(void) { + bool allNull = true; + Bits diskcount = 0; + Bits swapPos = swapPosition; + int i; + + /* Check to make sure there's atleast one setup image */ + for(i=0;idiskname); + imageDiskList[diskcount] = diskSwap[swapPos]; + diskcount++; + } + swapPos++; + if(swapPos>=MAX_SWAPPABLE_DISKS) swapPos=0; + } +} + +static void swapInNextDisk(void) { + swapPosition++; + if(diskSwap[swapPosition] == NULL) swapPosition = 0; + swapInDisks(); +} + + +Bit8u imageDisk::Read_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data) { + Bit32u sectnum; + + sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L; + + return Read_AbsoluteSector(sectnum, data); +} + +Bit8u imageDisk::Read_AbsoluteSector(Bit32u sectnum, void * data) { + Bit32u bytenum; + + bytenum = sectnum * sector_size; + + fseek(diskimg,bytenum,SEEK_SET); + fread(data, 1, sector_size, diskimg); + + return 0x00; +} + +Bit8u imageDisk::Write_Sector(Bit32u head,Bit32u cylinder,Bit32u sector,void * data) { + Bit32u sectnum; + + sectnum = ( (cylinder * heads + head) * sectors ) + sector - 1L; + + return Write_AbsoluteSector(sectnum, data); + +} + + +Bit8u imageDisk::Write_AbsoluteSector(Bit32u sectnum, void *data) { + Bit32u bytenum; + + bytenum = sectnum * sector_size; + + //LOG_MSG("Writing sectors to %ld at bytenum %d", sectnum, bytenum); + + fseek(diskimg,bytenum,SEEK_SET); + fwrite(data, sector_size, 1, diskimg); + + return 0x00; + +} + +imageDisk::imageDisk(FILE *imgFile, Bit8u *imgName, Bit32u imgSizeK, bool isHardDisk) { + heads = 0; + cylinders = 0; + sectors = 0; + sector_size = 512; + diskimg = imgFile; + + memset(diskname,0,512); + if(strlen((const char *)imgName) > 511) { + memcpy(diskname, imgName, 511); + } else { + strcpy((char *)diskname, (const char *)imgName); + } + + active = false; + hardDrive = isHardDisk; + if(!isHardDisk) { + Bitu i=0; + bool founddisk = false; + while (DiskGeometryList[i].ksize!=0x0) { + if (DiskGeometryList[i].ksize==imgSizeK) { + founddisk = true; + active = true; + floppytype = i; + heads = DiskGeometryList[i].headscyl; + cylinders = DiskGeometryList[i].cylcount; + sectors = DiskGeometryList[i].secttrack; + break; + } + i++; + } + if(!founddisk) { + active = false; + } + } +} + +void imageDisk::Set_Geometry(Bit32u setHeads, Bit32u setCyl, Bit32u setSect, Bit32u setSectSize) { + heads = setHeads; + cylinders = setCyl; + sectors = setSect; + sector_size = setSectSize; + active = true; +} + +void imageDisk::Get_Geometry(Bit32u * getHeads, Bit32u *getCyl, Bit32u *getSect, Bit32u *getSectSize) { + *getHeads = heads; + *getCyl = cylinders; + *getSect = sectors; + *getSectSize = sector_size; +} + +Bit8u imageDisk::GetBiosType(void) { + if(!hardDrive) { + return DiskGeometryList[floppytype].biosval; + } else return 0; +} + +Bit32u imageDisk::getSectSize(void) { + return sector_size; +} + +static Bitu GetDosDriveNumber(Bitu biosNum) { + switch(biosNum) { case 0x0: - reg_ah=0x00; + return 0x0; + case 0x1: + return 0x1; + case 0x80: + return 0x2; + case 0x81: + return 0x3; + case 0x82: + return 0x4; + case 0x83: + return 0x5; + default: + return 0x7f; + } +} + +static bool driveInactive(Bitu driveNum) { + if(driveNum>=(2 + MAX_HDD_IMAGES)) { + LOG_MSG("Disk %d non-existant", driveNum); + last_status = 0x01; + CALLBACK_SCF(true); + return true; + } + if(imageDiskList[driveNum] == NULL) { + LOG_MSG("Disk %d not active", driveNum); + last_status = 0x01; + CALLBACK_SCF(true); + return true; + } + if(!imageDiskList[driveNum]->active) { + LOG_MSG("Disk %d not active", driveNum); + last_status = 0x01; + CALLBACK_SCF(true); + return true; + } + return false; +} + + +static Bitu INT13_DiskHandler(void) { + Bit16u segat, bufptr; + Bit8u sectbuf[512]; + Bitu drivenum; + Bits readcnt; + int i,t; + last_drive = reg_dl; + drivenum = GetDosDriveNumber(reg_dl); + //drivenum = 0; + //LOG_MSG("INT13: Function %x called on drive %x (dos drive %d)", reg_ah, reg_dl, drivenum); + switch(reg_ah) { + case 0x0: /* Reset disk */ + if(driveInactive(drivenum)) return CBRET_NONE; + last_status = 0x00; CALLBACK_SCF(false); - LOG(LOG_BIOS,LOG_NORMAL)("reset disk return succesfull"); break; - case 0x02: /* Read Disk Sectors */ - LOG(LOG_BIOS,LOG_NORMAL)("INT13:02:Read Disk Sectors not supported failing"); - reg_ah=0x80; + case 0x1: /* Get status of last operation */ + + if(last_status != 0x00) { + reg_ah = last_status; CALLBACK_SCF(true); + } else { + reg_ah = 0x00; + CALLBACK_SCF(false); + } break; - case 0x04: - if(Drives[reg_dl]!=NULL) { - reg_ah=0; + case 0x2: /* Read sectors */ + if(driveInactive(drivenum)) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + + segat = SegValue(es); + bufptr = reg_bx; + for(i=0;iRead_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf); + 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); - } - else{ - reg_ah=0x80; - CALLBACK_SCF(true); - } - LOG(LOG_BIOS,LOG_NORMAL)("INT 13:04 Verify sector used on %d, with result %d",reg_dl,reg_ah); - break; - - case 0x08: /* Get Drive Parameters */ - LOG(LOG_BIOS,LOG_NORMAL)("INT13:08:Get Drive parameters not supported failing"); - reg_ah=0xff; - CALLBACK_SCF(true); break; - case 0xff: + case 0x3: /* Write sectors */ + + if(driveInactive(drivenum)) { + reg_ah = 0xff; + CALLBACK_SCF(true); + return CBRET_NONE; + } + + + bufptr = reg_bx; + for(i=0;igetSectSize();t++) { + sectbuf[t] = real_readb(SegValue(es),bufptr); + bufptr++; + } + + last_status = imageDiskList[drivenum]->Write_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0) << 2)), (Bit32u)((reg_cl & 63) + i), §buf[0]); + if(last_status != 0x00) { + CALLBACK_SCF(true); + return CBRET_NONE; + } + } + CALLBACK_SCF(false); + break; + case 0x04: /* Verify sectors */ + if(driveInactive(drivenum)) return CBRET_NONE; + + /* TODO: Finish coding this section */ + /* + segat = SegValue(es); + bufptr = reg_bx; + for(i=0;iRead_Sector((Bit32u)reg_dh, (Bit32u)(reg_ch | ((reg_cl & 0xc0)<< 2)), (Bit32u)((reg_cl & 63)+i), sectbuf); + if(last_status != 0x00) { + LOG_MSG("Error in disk read"); + CALLBACK_SCF(true); + return CBRET_NONE; + } + for(t=0;t<512;t++) { + real_writeb(segat,bufptr,sectbuf[t]); + bufptr++; + } + }*/ + reg_ah = 0x00; + //reg_al = 0x10; /* CRC verify failed */ + reg_al = 0x00; /* CRC verify succeeded */ + CALLBACK_SCF(false); + + break; + case 0x08: /* Get drive parameters */ + if(driveInactive(drivenum)) { + last_status = 0x07; + reg_ah = last_status; + CALLBACK_SCF(true); + return CBRET_NONE; + } + reg_ax = 0x00; + reg_bl = imageDiskList[drivenum]->GetBiosType(); + Bit32u tmpheads, tmpcyl, tmpsect, tmpsize; + imageDiskList[drivenum]->Get_Geometry(&tmpheads, &tmpcyl, &tmpsect, &tmpsize); + reg_ch = tmpcyl; + reg_cl = tmpsect; + reg_dh = tmpheads-1; + last_status = 0x00; + reg_dl = 0; + if(imageDiskList[2] != NULL) reg_dl++; + if(imageDiskList[3] != NULL) reg_dl++; + CALLBACK_SCF(false); + break; + case 0x11: /* Recalibrate drive */ + reg_ah = 0x00; + CALLBACK_SCF(false); + break; + case 0x17: /* Set disk type for format */ + /* Pirates! needs this to load */ + killRead = true; + reg_ah = 0x00; + CALLBACK_SCF(false); + break; default: - LOG(LOG_BIOS,LOG_ERROR)("Illegal int 13h call %2X Fail it",reg_ah); + LOG_MSG("INT13: Function %x called on drive %x (dos drive %d)", reg_ah, reg_dl, drivenum); reg_ah=0xff; CALLBACK_SCF(true); } return CBRET_NONE; } + void BIOS_SetupDisks(void) { /* TODO Start the time correctly */ call_int13=CALLBACK_Allocate(); - CALLBACK_Setup(call_int13,&INT13_SmallHandler,CB_IRET); + //CALLBACK_Setup(call_int13,&INT13_SmallHandler,CB_IRET); + CALLBACK_Setup(call_int13,&INT13_DiskHandler,CB_IRET); RealSetVec(0x13,CALLBACK_RealPointer(call_int13)); -/* Init the Disk Tables */ - last_status=0; + int i; + for(i=0;i<4;i++) { + imageDiskList[i] = NULL; + } + + for(i=0;i