DOSBox-X
|
00001 /* 00002 * 00003 * Copyright (c) 2018 Shane Krueger 00004 * 00005 * This program is free software; you can redistribute it and/or modify 00006 * it under the terms of the GNU General Public License as published by 00007 * the Free Software Foundation; either version 2 of the License, or 00008 * (at your option) any later version. 00009 * 00010 * This program is distributed in the hope that it will be useful, 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 * GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License along 00016 * with this program; if not, write to the Free Software Foundation, Inc., 00017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "dosbox.h" 00021 #include "callback.h" 00022 #include "bios.h" 00023 #include "bios_disk.h" 00024 #include "regs.h" 00025 #include "mem.h" 00026 #include "dos_inc.h" /* for Drives[] */ 00027 #include "../dos/drives.h" 00028 #include "mapper.h" 00029 00030 /* imageDiskMemory simulates a hard drive image or floppy drive image in RAM 00031 * 00032 * It can be initialized as a floppy, using one of the predefined floppy 00033 * geometries, or can be initialized as a hard drive, accepting either 00034 * a size in kilobytes or a specific set of chs values to emulate 00035 * It will then split the image into 64k chunks that are allocated on-demand. 00036 * Initially the only RAM required is for the chunk map 00037 * The image is effectively intialized to all zeros, as each chunk is zeroed 00038 * upon allocation 00039 * Writes of all zeros do not allocate memory if none has yet been assigned 00040 * 00041 */ 00042 00043 // Create a hard drive image of a specified size; automatically select c/h/s 00044 imageDiskMemory::imageDiskMemory(Bit32u imgSizeK) : imageDisk(ID_MEMORY) { 00045 //notes: 00046 // this code always returns HARD DRIVES with 512 byte sectors 00047 // the code will round up in case it cannot make an exact match 00048 // it enforces a minimum drive size of 32kb, since a hard drive cannot be formatted as FAT12 with a smaller parition 00049 // the code works properly throughout the range of a 32-bit unsigned integer, however: 00050 // a) for drives requesting more than 8,225,280kb, the number of cylinders will exceed 1024 00051 // b) for drives requesting ULONG_MAX kb, the drive it creates will be slightly larger than ULONG_MAX kb, due to rounding 00052 00053 //BIOS and MBR c/h/s limits: 1024 / 255 / 63 8,225,280kb 00054 //ATA c/h/s limits: 65536 / 16 / 255 267,386,880kb 00055 //combined limits: 1024 / 16 / 63 516,096kb 00056 00057 //since this is a virtual drive, we are not restricted to ATA limits, so go with BIOS/MBR limits 00058 00059 //it targets c/h/s values as follows: 00060 // minimum: 4 / 1 / 16 32kb 00061 // through: 1024 / 1 / 16 8,192kb 00062 // through: 1024 / 16 / 16 131,072kb 00063 // through: 1024 / 16 / 63 516,096kb 00064 // max FAT16 drive size: 1024 / 130 / 63 4,193,280kb 00065 // through: 1024 / 255 / 63 8,225,280kb 00066 // maximum: 534699 / 255 / 63 4tb 00067 00068 //use 32kb as minimum drive size, since it is not possible to format a smaller drive that has 16 sectors 00069 if (imgSizeK < 32) imgSizeK = 32; 00070 //set the sector size to 512 bytes 00071 Bit32u sector_size = 512; 00072 //calculate the total number of sectors on the drive (imgSizeK * 2) 00073 Bit64u total_sectors = ((Bit64u)imgSizeK * 1024 + sector_size - 1) / sector_size; 00074 00075 //calculate cylinders, sectors, and heads according to the targets above 00076 Bit32u sectors, heads, cylinders; 00077 if (total_sectors <= 1024 * 16 * 16) { 00078 sectors = 16; 00079 heads = (Bit32u)((total_sectors + (1024 * sectors - 1)) / (1024 * sectors)); 00080 cylinders = (Bit32u)((total_sectors + (sectors * heads - 1)) / (sectors * heads)); 00081 } 00082 else if (total_sectors <= 1024 * 16 * 63) { 00083 heads = 16; 00084 sectors = (Bit32u)((total_sectors + (1024 * heads - 1)) / (1024 * heads)); 00085 cylinders = (Bit32u)((total_sectors + (sectors * heads - 1)) / (sectors * heads)); 00086 } 00087 else if (total_sectors <= 1024 * 255 * 63) { 00088 sectors = 63; 00089 heads = (Bit32u)((total_sectors + (1024 * sectors - 1)) / (1024 * sectors)); 00090 cylinders = (Bit32u)((total_sectors + (sectors * heads - 1)) / (sectors * heads)); 00091 } 00092 else { 00093 sectors = 63; 00094 heads = 255; 00095 cylinders = (Bit32u)((total_sectors + (sectors * heads - 1)) / (sectors * heads)); 00096 } 00097 00098 LOG_MSG("Creating ramdrive as C/H/S %u/%u/%u with %u bytes/sector\n", 00099 (unsigned int)cylinders, (unsigned int)heads, (unsigned int)sectors, (unsigned int)sector_size); 00100 00101 diskGeo diskParams; 00102 diskParams.secttrack = sectors; 00103 diskParams.cylcount = cylinders; 00104 diskParams.headscyl = heads; 00105 diskParams.bytespersect = sector_size; 00106 diskParams.biosval = 0; 00107 diskParams.ksize = imgSizeK; 00108 diskParams.mediaid = 0xF0; 00109 diskParams.rootentries = 512; 00110 diskParams.biosval = 0; 00111 diskParams.sectcluster = 1; 00112 init(diskParams, true, 0); 00113 } 00114 00115 // Create a floppy image of a specified geometry 00116 imageDiskMemory::imageDiskMemory(const diskGeo& floppyGeometry) : imageDisk(ID_MEMORY) { 00117 init(floppyGeometry, false, 0); 00118 } 00119 00120 // Create a hard drive image of a specified geometry 00121 imageDiskMemory::imageDiskMemory(Bit16u cylinders, Bit16u heads, Bit16u sectors, Bit16u sector_size) : imageDisk(ID_MEMORY) { 00122 diskGeo diskParams; 00123 diskParams.secttrack = sectors; 00124 diskParams.cylcount = cylinders; 00125 diskParams.headscyl = heads; 00126 diskParams.bytespersect = sector_size; 00127 diskParams.biosval = 0; 00128 diskParams.ksize = 0; 00129 diskParams.mediaid = 0xF0; 00130 diskParams.rootentries = 512; 00131 diskParams.biosval = 0; 00132 diskParams.sectcluster = 1; 00133 init(diskParams, true, 0); 00134 } 00135 00136 // Create a copy-on-write memory image of an existing image 00137 imageDiskMemory::imageDiskMemory(imageDisk* underlyingImage) : imageDisk(ID_MEMORY) { 00138 diskGeo diskParams; 00139 Bit32u heads, cylinders, sectors, bytesPerSector; 00140 underlyingImage->Get_Geometry(&heads, &cylinders, §ors, &bytesPerSector); 00141 diskParams.headscyl = (Bit16u)heads; 00142 diskParams.cylcount = (Bit16u)cylinders; 00143 diskParams.secttrack = (Bit16u)sectors; 00144 diskParams.bytespersect = (Bit16u)bytesPerSector; 00145 diskParams.biosval = 0; 00146 diskParams.ksize = 0; 00147 diskParams.mediaid = 0xF0; 00148 diskParams.rootentries = 0; 00149 diskParams.biosval = 0; 00150 diskParams.sectcluster = 0; 00151 init(diskParams, true, underlyingImage); 00152 } 00153 00154 // Internal initialization code to create a image of a specified geometry 00155 void imageDiskMemory::init(diskGeo diskParams, bool isHardDrive, imageDisk* underlyingImage) { 00156 //initialize internal variables in case we fail out 00157 this->total_sectors = 0; 00158 this->underlyingImage = underlyingImage; 00159 if (underlyingImage) underlyingImage->Addref(); 00160 00161 //calculate the total number of sectors on the drive, and check for overflows 00162 Bit64u absoluteSectors = (Bit64u)diskParams.cylcount * (Bit64u)diskParams.headscyl; 00163 if (absoluteSectors > 0x100000000u) { 00164 LOG_MSG("Image size too large in imageDiskMemory constructor.\n"); 00165 return; 00166 } 00167 absoluteSectors *= (Bit64u)diskParams.secttrack; 00168 if (absoluteSectors > 0x100000000u) { 00169 LOG_MSG("Image size too large in imageDiskMemory constructor.\n"); 00170 return; 00171 } 00172 //make sure that the number of total sectors on the drive is not zero 00173 if (absoluteSectors == 0) { 00174 LOG_MSG("Image size too small in imageDiskMemory constructor.\n"); 00175 return; 00176 } 00177 00178 //calculate total size of the drive in kilobytes, and check for overflow 00179 Bit64u diskSizeK = ((Bit64u)diskParams.headscyl * (Bit64u)diskParams.cylcount * (Bit64u)diskParams.secttrack * (Bit64u)diskParams.bytespersect + (Bit64u)1023) / (Bit64u)1024; 00180 if (diskSizeK >= 0x100000000u) 00181 { 00182 LOG_MSG("Image size too large in imageDiskMemory constructor.\n"); 00183 return; 00184 } 00185 00186 //split the sectors into chunks, which are allocated together on an as-needed basis 00187 //assuming a maximum of 63 heads and 255 sectors, this formula gives a nice chunk size that scales with the drive size 00188 //for any drives under 64mb, the chunks are 8kb each, and the map consumes 1/1000 of the total drive size 00189 //then the chunk size grows up to a 8gb drive, while the map consumes 30-60k of memory (on 64bit PCs) 00190 //with a 8gb drive, the chunks are about 1mb each, and the map consumes about 60k of memory 00191 //and for larger drives (with over 1023 cylinders), the chunks remain at 1mb each while the map grows 00192 this->sectors_per_chunk = (diskParams.headscyl + 7u) / 8u * diskParams.secttrack; 00193 this->total_chunks = (Bit32u)((absoluteSectors + sectors_per_chunk - 1u) / sectors_per_chunk); 00194 this->chunk_size = sectors_per_chunk * diskParams.bytespersect; 00195 //allocate a map of chunks that have been allocated and their memory addresses 00196 ChunkMap = (Bit8u**)malloc(total_chunks * sizeof(Bit8u*)); 00197 if (ChunkMap == NULL) { 00198 LOG_MSG("Error allocating memory map in imageDiskMemory constructor for %lu clusters.\n", (unsigned long)total_chunks); 00199 return; 00200 } 00201 //clear memory map 00202 memset((void*)ChunkMap, 0, total_chunks * sizeof(Bit8u*)); 00203 00204 //set internal variables 00205 this->diskname = "ram drive"; 00206 this->heads = diskParams.headscyl; 00207 this->cylinders = diskParams.cylcount; 00208 this->sectors = diskParams.secttrack; 00209 this->sector_size = diskParams.bytespersect; 00210 this->diskSizeK = diskSizeK; 00211 this->total_sectors = (Bit32u)absoluteSectors; 00212 this->reserved_cylinders = 0; 00213 this->hardDrive = isHardDrive; 00214 this->floppyInfo = diskParams; 00215 this->active = true; 00216 } 00217 00218 // imageDiskMemory destructor will release all allocated memory 00219 imageDiskMemory::~imageDiskMemory() { 00220 //quit if the map is already not allocated 00221 if (!active) return; 00222 //release the underlying image 00223 if (this->underlyingImage) this->underlyingImage->Release(); 00224 //loop through each chunk and release it if it has been allocated 00225 for (Bit32u i = 0; i < total_chunks; i++) { 00226 Bit8u* chunk = ChunkMap[i]; 00227 if (chunk) free(chunk); 00228 } 00229 //release the memory map 00230 free(ChunkMap); 00231 //reset internal variables 00232 ChunkMap = 0; 00233 total_sectors = 0; 00234 active = false; 00235 } 00236 00237 // Return the BIOS type of the floppy image, or 0 for hard drives 00238 Bit8u imageDiskMemory::GetBiosType(void) { 00239 return this->hardDrive ? 0 : this->floppyInfo.biosval; 00240 } 00241 00242 void imageDiskMemory::Set_Geometry(Bit32u setHeads, Bit32u setCyl, Bit32u setSect, Bit32u setSectSize) { 00243 if (setHeads != this->heads || setCyl != this->cylinders || setSect != this->sectors || setSectSize != this->sector_size) { 00244 LOG_MSG("imageDiskMemory::Set_Geometry not implemented"); 00245 //validate geometry and resize ramdrive 00246 } 00247 } 00248 00249 // Read a specific sector from the ramdrive 00250 Bit8u imageDiskMemory::Read_AbsoluteSector(Bit32u sectnum, void * data) { 00251 //sector number is a zero-based offset 00252 00253 //verify the sector number is valid 00254 if (sectnum >= total_sectors) { 00255 LOG_MSG("Invalid sector number in Read_AbsoluteSector for sector %lu.\n", (unsigned long)sectnum); 00256 return 0x05; 00257 } 00258 00259 //calculate which chunk the sector is located within, and which sector within the chunk 00260 Bit32u chunknum, chunksect; 00261 chunknum = sectnum / sectors_per_chunk; 00262 chunksect = sectnum % sectors_per_chunk; 00263 00264 //retrieve the memory address of the chunk 00265 Bit8u* datalocation; 00266 datalocation = ChunkMap[chunknum]; 00267 00268 //if the chunk has not yet been allocated, return underlying image if any, or else zeros 00269 if (datalocation == 0) { 00270 if (this->underlyingImage) { 00271 return this->underlyingImage->Read_AbsoluteSector(sectnum, data); 00272 } 00273 memset(data, 0, sector_size); 00274 return 0x00; 00275 } 00276 00277 //update the address to the specific sector within the chunk 00278 datalocation = &datalocation[chunksect * sector_size]; 00279 00280 //copy the data to the output and return success 00281 memcpy(data, datalocation, sector_size); 00282 return 0x00; 00283 } 00284 00285 // Write a specific sector from the ramdrive 00286 Bit8u imageDiskMemory::Write_AbsoluteSector(Bit32u sectnum, const void * data) { 00287 //sector number is a zero-based offset 00288 00289 //verify the sector number is valid 00290 if (sectnum >= total_sectors) { 00291 LOG_MSG("Invalid sector number in Write_AbsoluteSector for sector %lu.\n", (unsigned long)sectnum); 00292 return 0x05; 00293 } 00294 00295 //calculate which chunk the sector is located within, and which sector within the chunk 00296 Bit32u chunknum, chunksect; 00297 chunknum = sectnum / sectors_per_chunk; 00298 chunksect = sectnum % sectors_per_chunk; 00299 00300 //retrieve the memory address of the chunk 00301 Bit8u* datalocation; 00302 datalocation = ChunkMap[chunknum]; 00303 00304 //if the chunk has not yet been allocated, allocate the chunk 00305 if (datalocation == NULL) { 00306 //if no underlying image, first check if we are actually saving anything 00307 if (this->underlyingImage == NULL) { 00308 Bit8u anyData = 0; 00309 for (Bit32u i = 0; i < sector_size; i++) { 00310 anyData |= ((Bit8u*)data)[i]; 00311 } 00312 //if it's all zeros, return success 00313 if (anyData == 0) return 0x00; 00314 } 00315 00316 //allocate a new memory chunk 00317 datalocation = (Bit8u*)malloc(chunk_size); 00318 if (datalocation == NULL) { 00319 LOG_MSG("Could not allocate memory in Write_AbsoluteSector for sector %lu.\n", (unsigned long)sectnum); 00320 return 0x05; 00321 } 00322 //save the memory chunk address within the memory map 00323 ChunkMap[chunknum] = datalocation; 00324 //initialize the memory chunk to all zeros (since we are only writing to a single sector within this chunk) 00325 memset((void*)datalocation, 0, chunk_size); 00326 //if there is an underlying image, read from the underlying image to fill the chunk 00327 if (this->underlyingImage) { 00328 Bit32u chunkFirstSector = chunknum * this->sectors_per_chunk; 00329 Bit32u sectorsToCopy = this->sectors_per_chunk; 00330 //if this is the last chunk, don't read past the end of the original image 00331 if ((chunknum + 1) == this->total_chunks) sectorsToCopy = this->total_sectors - chunkFirstSector; 00332 //copy the sectors 00333 Bit8u* target = datalocation; 00334 for (Bit32u i = 0; i < sectorsToCopy; i++) { 00335 this->underlyingImage->Read_AbsoluteSector(i + chunkFirstSector, target); 00336 datalocation += this->sector_size; 00337 } 00338 } 00339 } 00340 00341 //update the address to the specific sector within the chunk 00342 datalocation = &datalocation[chunksect * sector_size]; 00343 00344 //write the sector to the chunk and return success 00345 memcpy(datalocation, data, sector_size); 00346 return 0x00; 00347 } 00348 00349 // Parition and format the ramdrive 00350 Bit8u imageDiskMemory::Format() { 00351 //verify that the geometry of the drive is valid 00352 if (this->sector_size != 512) { 00353 LOG_MSG("imageDiskMemory::Format only designed for disks with 512-byte sectors.\n"); 00354 return 0x01; 00355 } 00356 if (this->sectors > 63) { 00357 LOG_MSG("imageDiskMemory::Format only designed for disks with <= 63 sectors.\n"); 00358 return 0x02; 00359 } 00360 if (this->heads > 255) { 00361 LOG_MSG("imageDiskMemory::Format only designed for disks with <= 255 heads.\n"); 00362 return 0x03; 00363 } 00364 if (this->cylinders <= this->Get_Reserved_Cylinders()) { 00365 LOG_MSG("Invalid number of reserved cylinders in imageDiskMemory::Format\n"); 00366 return 0x06; 00367 } 00368 Bit32u reported_cylinders = this->cylinders - this->Get_Reserved_Cylinders(); 00369 if (reported_cylinders > 1024) { 00370 LOG_MSG("imageDiskMemory::Format only designed for disks with <= 1024 cylinders.\n"); 00371 return 0x04; 00372 } 00373 Bit32u reported_total_sectors = reported_cylinders * this->heads * this->sectors; 00374 00375 //plan the drive 00376 //write a MBR? 00377 bool writeMBR = this->hardDrive; 00378 Bit32u partitionStart = writeMBR ? this->sectors : 0; //must be aligned with the start of a head (multiple of this->sectors) 00379 Bit32u partitionLength = reported_total_sectors - partitionStart; //must be aligned with the start of a head (multiple of this->sectors) 00380 //figure out the media id 00381 Bit8u mediaID = this->hardDrive ? 0xF8 : this->floppyInfo.mediaid; 00382 //figure out the number of root entries and minimum number of sectors per cluster 00383 Bit32u root_ent = this->hardDrive ? 512 : this->floppyInfo.rootentries; 00384 Bit32u sectors_per_cluster = this->hardDrive ? 4 : this->floppyInfo.sectcluster; //fat requires 2k clusters minimum on hard drives 00385 00386 //calculate the number of: 00387 // root sectors 00388 // FAT sectors 00389 // reserved sectors 00390 // sectors per cluster 00391 // if this is a FAT16 or FAT12 drive 00392 Bit32u root_sectors; 00393 bool isFat16; 00394 Bit32u fatSectors; 00395 Bit32u reservedSectors; 00396 if (!this->CalculateFAT(partitionStart, partitionLength, this->hardDrive, root_ent, &root_sectors, §ors_per_cluster, &isFat16, &fatSectors, &reservedSectors)) { 00397 LOG_MSG("imageDiskMemory::Format could not calculate FAT sectors.\n"); 00398 return 0x05; 00399 } 00400 00401 LOG_MSG("Formatting FAT%u %s drive C/H/S %u/%u/%u with %u bytes/sector, %u root entries, %u-byte clusters, media id 0x%X\n", 00402 (unsigned int)(isFat16 ? 16 : 12), this->hardDrive ? "hard" : "floppy", 00403 (unsigned int)reported_cylinders, (unsigned int)this->heads, (unsigned int)this->sectors, (unsigned int)this->sector_size, 00404 (unsigned int)root_ent, (unsigned int)(sectors_per_cluster * this->sector_size), (unsigned int)mediaID); 00405 00406 //write MBR if applicable 00407 Bit8u sbuf[512]; 00408 if (writeMBR) { 00409 // initialize sbuf with the freedos MBR 00410 memcpy(sbuf, freedos_mbr, 512); 00411 // active partition 00412 sbuf[0x1be] = 0x80; 00413 //ASSUMING that partitionStart==this->sectors; 00414 // start head - head 0 has the partition table, head 1 first partition 00415 sbuf[0x1bf] = this->heads > 1 ? 1 : 0; 00416 // start sector with bits 8-9 of start cylinder in bits 6-7 00417 sbuf[0x1c0] = this->heads > 1 ? 1 : (this->sectors > 1 ? 2 : 1); 00418 // start cylinder bits 0-7 00419 sbuf[0x1c1] = this->heads > 1 || this->sectors > 1 ? 0 : 1; 00420 // OS indicator: DOS what else ;) 00421 sbuf[0x1c2] = 0x06; 00422 //ASSUMING that partitionLength==(reported_total_sectors-partitionStart) 00423 // end head (0-based) 00424 sbuf[0x1c3] = this->heads - 1; 00425 // end sector with bits 8-9 of end cylinder (0-based) in bits 6-7 00426 sbuf[0x1c4] = this->sectors | (((this->cylinders - 1 - this->Get_Reserved_Cylinders()) & 0x300) >> 2); 00427 // end cylinder (0-based) bits 0-7 00428 sbuf[0x1c5] = (this->cylinders - 1 - this->Get_Reserved_Cylinders()) & 0xFF; 00429 // sectors preceding partition1 (one head) 00430 host_writed(&sbuf[0x1c6], this->sectors); 00431 // length of partition1, align to chs value 00432 // ASSUMING that partitionLength aligns to chs value 00433 host_writed(&sbuf[0x1ca], partitionLength); 00434 00435 // write partition table 00436 this->Write_AbsoluteSector(0, sbuf); 00437 } 00438 00439 // initialize boot sector values 00440 memset(sbuf, 0, 512); 00441 // TODO boot code jump 00442 sbuf[0] = 0xEB; sbuf[1] = 0x3c; sbuf[2] = 0x90; 00443 // OEM 00444 sprintf((char*)&sbuf[0x03], "MSDOS5.0"); 00445 // bytes per sector: always 512 00446 host_writew(&sbuf[0x0b], 512); 00447 // sectors per cluster: 1,2,4,8,16,... 00448 sbuf[0x0d] = sectors_per_cluster; 00449 // reserved sectors: 1 for floppies (the boot sector), or align to 4k boundary for hard drives (not required to align to 4k boundary) 00450 host_writew(&sbuf[0x0e], reservedSectors); 00451 // Number of FATs - always 2 00452 sbuf[0x10] = 2; 00453 // Root directory entries 00454 host_writew(&sbuf[0x11], root_ent); 00455 // total sectors (<= 65535) 00456 if (partitionLength < 0x10000u) host_writew(&sbuf[0x13], (Bit16u)partitionLength); 00457 // media descriptor 00458 sbuf[0x15] = mediaID; 00459 // sectors per FAT 00460 host_writew(&sbuf[0x16], fatSectors); 00461 // sectors per track 00462 host_writew(&sbuf[0x18], this->sectors); 00463 // heads 00464 host_writew(&sbuf[0x1a], this->heads); 00465 // hidden sectors 00466 host_writed(&sbuf[0x1c], partitionStart); 00467 // sectors (>= 65536) 00468 if (partitionLength >= 0x10000u) host_writed(&sbuf[0x20], partitionLength); 00469 // BIOS drive 00470 sbuf[0x24] = this->hardDrive ? 0x80 : 0x00; 00471 // ext. boot signature 00472 sbuf[0x26] = 0x29; 00473 // volume serial number 00474 // let's use the BIOS time (cheap, huh?) 00475 host_writed(&sbuf[0x27], mem_readd(BIOS_TIMER)); 00476 // Volume label 00477 sprintf((char*)&sbuf[0x2b], "RAMDISK "); 00478 // file system type 00479 sprintf((char*)&sbuf[0x36], isFat16 ? "FAT16 " : "FAT12 "); 00480 // boot sector signature (indicates disk is bootable) 00481 host_writew(&sbuf[0x1fe], 0xAA55); 00482 00483 // write the boot sector 00484 this->Write_AbsoluteSector(partitionStart, sbuf); 00485 00486 // initialize the FATs and root sectors 00487 memset(sbuf, 0, sector_size); 00488 // erase both FATs and the root directory sectors 00489 for (Bit32u i = partitionStart + 1; i < partitionStart + reservedSectors + fatSectors + fatSectors + root_sectors; i++) { 00490 this->Write_AbsoluteSector(i, sbuf); 00491 } 00492 // set the special markers for cluster 0 and cluster 1 00493 if (isFat16) { 00494 host_writed(&sbuf[0], 0xFFFFFF00u | mediaID); 00495 } 00496 else { 00497 host_writed(&sbuf[0], 0xFFFF00u | mediaID); 00498 } 00499 this->Write_AbsoluteSector(partitionStart + reservedSectors, sbuf); 00500 this->Write_AbsoluteSector(partitionStart + reservedSectors + fatSectors, sbuf); 00501 00502 //success 00503 return 0x00; 00504 } 00505 00506 // Calculate the number of sectors per cluster, the number of sectors per fat, and if it is FAT12/FAT16 00507 // Note that sectorsPerCluster is required to be set to the minimum, which will be 2 for certain types of floppies 00508 bool imageDiskMemory::CalculateFAT(Bit32u partitionStartingSector, Bit32u partitionLength, bool isHardDrive, Bit32u rootEntries, Bit32u* rootSectors, Bit32u* sectorsPerCluster, bool* isFat16, Bit32u* fatSectors, Bit32u* reservedSectors) { 00509 //check for null references 00510 if (rootSectors == NULL || sectorsPerCluster == NULL || isFat16 == NULL || fatSectors == NULL || reservedSectors == NULL) return false; 00511 //make sure variables all make sense 00512 switch (*sectorsPerCluster) { 00513 case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: break; 00514 default: 00515 LOG_MSG("Invalid number of sectors per cluster\n"); 00516 return false; 00517 } 00518 if (((Bit64u)partitionStartingSector + partitionLength) > 0xfffffffful) { 00519 LOG_MSG("Invalid partition size\n"); 00520 return false; 00521 } 00522 if ((rootEntries > (isHardDrive ? 512u : 240u)) || 00523 (rootEntries / 16) * 16 != rootEntries || 00524 rootEntries == 0) { 00525 LOG_MSG("Invalid number of root entries\n"); 00526 return false; 00527 } 00528 //set the number of root sectors 00529 *rootSectors = rootEntries * 32 / 512; 00530 //make sure there is a minimum number of sectors available 00531 // minimum sectors = root sectors + 1 for boot sector + 1 for fat #1 + 1 for fat #2 + 1 cluster for data + add 7 for hard drives due to allow for 4k alignment 00532 if (partitionLength < (*rootSectors + 3 + *sectorsPerCluster + (isHardDrive ? 7 : 0))) { 00533 LOG_MSG("Partition too small to format\n"); 00534 return false; 00535 } 00536 00537 00538 //set the minimum number of reserved sectors 00539 //the first sector of a partition is always reserved, of course 00540 *reservedSectors = 1; 00541 //if this is a hard drive, we will align to a 4kb boundary, 00542 // so set (partitionStartingSector + reservedSectors) to an even number. 00543 //Additional alignment can be made by increasing the number of reserved sectors, 00544 // or by increasing the reservedSectors value -- both done after the calculation 00545 if (isHardDrive && ((partitionStartingSector + *reservedSectors) % 2 == 1)) (*reservedSectors)++; 00546 00547 00548 //compute the number of fat sectors and data sectors 00549 Bit32u dataSectors; 00550 Bit32u dataClusters; 00551 //first try FAT12 - compute the minimum number of fat sectors necessary using the minimum number of sectors per cluster 00552 *fatSectors = (((Bit64u)partitionLength - *reservedSectors - *rootSectors + 2) * 3 + *sectorsPerCluster * 1024 + 5) / (*sectorsPerCluster * 1024 + 6); 00553 dataSectors = partitionLength - *reservedSectors - *rootSectors - *fatSectors - *fatSectors; 00554 dataClusters = dataSectors / *sectorsPerCluster; 00555 //check if this calculation makes the drive too big to fit within a FAT12 drive 00556 if (dataClusters >= 4085) { 00557 //so instead let's calculate for FAT16, and increase the number of sectors per cluster if necessary 00558 //note: changing the drive to FAT16 will reducing the number of data sectors, which might change the drive 00559 // from a FAT16 drive to a FAT12 drive. however, the only difference is that there are unused fat sectors 00560 // allocated. we cannot allocate them, or else the drive will change back to a FAT16 drive, so, just leave it as-is 00561 do { 00562 //compute the minimum number of fat sectors necessary starting with the minimum number of sectors per cluster 00563 // the +2 is for the two reserved data sectors (sector #0 and sector #1) which are not stored on the drive, but are in the map 00564 // rounds up and assumes 512 byte sector size 00565 *fatSectors = ((Bit64u)partitionLength - *reservedSectors - *rootSectors + 2 + (*sectorsPerCluster * 256 + 1)) / (*sectorsPerCluster * 256 + 2); 00566 00567 //compute the number of data sectors and clusters with this arrangement 00568 dataSectors = partitionLength - *reservedSectors - *rootSectors - *fatSectors - *fatSectors; 00569 dataClusters = dataSectors / *sectorsPerCluster; 00570 00571 //check and see if this makes a valid FAT16 drive 00572 if (dataClusters < 65525) break; 00573 00574 //otherwise, double the number of sectors per cluster and try again 00575 *sectorsPerCluster <<= 1; 00576 } while (true); 00577 //determine if the drive is too large for FAT16 00578 if (*sectorsPerCluster >= 256) { 00579 LOG_MSG("Partition too large to format as FAT16\n"); 00580 return false; 00581 } 00582 } 00583 00584 //add padding to align hard drives to 4kb boundary 00585 if (isHardDrive) { 00586 //update the reserved sector count 00587 *reservedSectors = (partitionStartingSector + *reservedSectors + *rootSectors + *fatSectors + *fatSectors) % 8; 00588 if (*reservedSectors == 0) *reservedSectors = 8; 00589 //recompute the number of data sectors 00590 dataSectors = partitionLength - *reservedSectors - *rootSectors - *fatSectors - *fatSectors; 00591 dataClusters = dataSectors / *sectorsPerCluster; 00592 00593 //note: by reducing the number of data sectors, the drive might have changed from a FAT16 drive to a FAT12 drive. 00594 // however, the only difference is that there are unused fat sectors allocated. we cannot allocate them, or else 00595 // the drive alignment will change again, and most likely the drive would change back into a FAT16 drive. 00596 // so, just leave it as-is 00597 } 00598 00599 //determine whether the drive is FAT16 or not 00600 //note: this is AFTER adding padding for 4kb alignment 00601 *isFat16 = (dataClusters >= 4085); 00602 00603 //success 00604 return true; 00605 }