19static constexpr unsigned DOS1_MAX_CLUSTER_COUNT = 0x3FE;
20static constexpr unsigned FAT12_MAX_CLUSTER_COUNT = 0xFF4;
21static constexpr unsigned FAT16_MAX_CLUSTER_COUNT = 0xFFF4;
22static constexpr unsigned SECTOR_SIZE =
sizeof(
SectorBuffer);
23static constexpr unsigned DIR_ENTRIES_PER_SECTOR = SECTOR_SIZE /
sizeof(
MSXDirEntry);
30static constexpr std::array<char, 11> SUNRISE_PARTITION_TABLE_HEADER = {
31 '\353',
'\376',
'\220',
'M',
'S',
'X',
'_',
'I',
'D',
'E',
' '
33static constexpr std::array<char, 11> NEXTOR_PARTITION_TABLE_HEADER = {
34 '\353',
'\376',
'\220',
'N',
'E',
'X',
'T',
'O',
'R',
'2',
'0'
36[[nodiscard]]
static std::optional<PartitionTableType> getPartitionTableType(
const SectorBuffer& buf)
38 if (buf.ptSunrise.header == SUNRISE_PARTITION_TABLE_HEADER) {
40 }
else if (buf.ptNextor.header == NEXTOR_PARTITION_TABLE_HEADER &&
41 buf.ptNextor.part[0].sys_ind != 0 &&
42 buf.ptNextor.part[0].start == 1) {
52 return getPartitionTableType(buf).has_value();
56static Partition& getPartitionNextorExtended(
58 unsigned remaining,
unsigned ebrOuterSector)
60 unsigned ebrSector = ebrOuterSector;
71 p.start = ebrSector + p.start;
77 if (link.start == 0) {
79 }
else if (link.sys_ind !=
one_of(0x05, 0x0F)) {
80 throw CommandException(
"Invalid extended boot record.");
82 ebrSector = ebrOuterSector + link.start;
85 throw CommandException(
"No partition number ",
partition);
89static Partition& getPartitionNextor(
90 SectorAccessibleDisk& disk,
unsigned partition, SectorBuffer& buf)
93 for (
auto& p : buf.ptNextor.part) {
96 }
else if (p.sys_ind ==
one_of(0x05, 0x0F)) {
97 return getPartitionNextorExtended(disk,
partition, buf, remaining, p.start);
98 }
else if (remaining == 0) {
103 throw CommandException(
"No partition number ",
partition);
107static Partition& getPartitionSunrise(
unsigned partition, SectorBuffer& buf)
110 if (partition < 1 || partition > buf.ptSunrise.part.size()) {
111 throw CommandException(
112 "Invalid partition number specified (must be 1-",
113 buf.ptSunrise.part.size(),
").");
116 auto& p = buf.ptSunrise.part[buf.ptSunrise.part.size() -
partition];
118 throw CommandException(
"No partition number ",
partition);
128 auto partitionTableType = getPartitionTableType(buf);
130 return getPartitionSunrise(
partition, buf);
132 return getPartitionNextor(disk,
partition, buf);
178 uint8_t nbReservedSectors = 1;
179 uint8_t nbHiddenSectors = 1;
183 uint16_t nbSides = 2;
185 uint16_t nbSectorsPerFat = 3;
186 uint8_t nbSectorsPerCluster = 2;
187 uint16_t nbDirEntry = 112;
188 uint8_t descriptor = 0xF9;
202 nbSectorsPerCluster = narrow<uint8_t>(
std::clamp(std::bit_ceil(nbSectors) >> 16,
size_t(4),
size_t(128)));
205 unsigned fatStart = nbReservedSectors + nbDirEntry / DIR_ENTRIES_PER_SECTOR;
206 unsigned estSectorCount = narrow<unsigned>(nbSectors - fatStart);
207 unsigned estClusterCount =
std::min(estSectorCount / nbSectorsPerCluster, FAT16_MAX_CLUSTER_COUNT);
208 unsigned fatSize = 2 * (estClusterCount + 2);
209 nbSectorsPerFat = narrow<uint16_t>((fatSize + SECTOR_SIZE - 1) / SECTOR_SIZE);
212 unsigned dataStart = fatStart + nbFats * nbSectorsPerFat;
213 unsigned dataSectorCount = narrow<unsigned>(nbSectors - dataStart);
214 unsigned clusterCount =
std::min(dataSectorCount / nbSectorsPerCluster, FAT16_MAX_CLUSTER_COUNT);
215 nbSectors = dataStart + clusterCount * nbSectorsPerCluster;
225 nbSectorsPerCluster = narrow<uint8_t>(
std::clamp(std::bit_ceil(nbSectors) >> 12,
size_t(1),
size_t(16)));
230 unsigned maxClusterCount = FAT12_MAX_CLUSTER_COUNT;
231 if (nbSectors <= 0x8000) {
232 maxClusterCount = DOS1_MAX_CLUSTER_COUNT - 1;
233 nbSectorsPerCluster *= 4;
237 unsigned fatStart = nbReservedSectors + nbDirEntry / DIR_ENTRIES_PER_SECTOR;
238 unsigned estSectorCount = narrow<unsigned>(nbSectors - fatStart);
239 unsigned estClusterCount =
std::min(estSectorCount / nbSectorsPerCluster, maxClusterCount);
240 unsigned fatSize = (3 * (estClusterCount + 2) + 1) / 2;
241 nbSectorsPerFat = narrow<uint16_t>((fatSize + SECTOR_SIZE - 1) / SECTOR_SIZE);
244 unsigned dataStart = fatStart + nbFats * nbSectorsPerFat;
245 unsigned dataSectorCount = narrow<unsigned>(nbSectors - dataStart);
246 unsigned clusterCount =
std::min(dataSectorCount / nbSectorsPerCluster, maxClusterCount);
247 nbSectors = dataStart + clusterCount * nbSectorsPerCluster;
257 nbSectorsPerCluster = narrow<uint8_t>(
std::clamp(std::bit_ceil(nbSectors) >> 10,
size_t(2),
size_t(64)));
260 unsigned fatStart = nbReservedSectors + nbDirEntry / DIR_ENTRIES_PER_SECTOR;
261 unsigned estSectorCount = narrow<unsigned>(nbSectors - fatStart);
262 unsigned estClusterCount =
std::min(estSectorCount / nbSectorsPerCluster, DOS1_MAX_CLUSTER_COUNT);
263 unsigned fatSize = (3 * (estClusterCount + 2) + 1) / 2;
264 nbSectorsPerFat = narrow<uint16_t>((fatSize + SECTOR_SIZE - 1) / SECTOR_SIZE);
267 unsigned dataStart = fatStart + nbFats * nbSectorsPerFat;
268 unsigned dataSectorCount = narrow<unsigned>(nbSectors - dataStart);
269 unsigned clusterCount =
std::min(dataSectorCount / nbSectorsPerCluster, DOS1_MAX_CLUSTER_COUNT);
270 nbSectors = dataStart + clusterCount * nbSectorsPerCluster;
271 }
else if (nbSectors > 32732) {
277 nbSectorsPerFat = 12;
278 nbSectorsPerCluster = 16;
281 nbHiddenSectors = 16;
285 if (nbSectors > 65535) nbSectors = 65535;
286 }
else if (nbSectors > 16388) {
291 nbSectorsPerFat = 12;
292 nbSectorsPerCluster = 8;
295 }
else if (nbSectors > 8212) {
300 nbSectorsPerFat = 12;
301 nbSectorsPerCluster = 4;
304 }
else if (nbSectors > 4126) {
309 nbSectorsPerFat = 12;
310 nbSectorsPerCluster = 2;
313 }
else if (nbSectors > 2880) {
319 nbSectorsPerCluster = 2;
322 }
else if (nbSectors > 1440) {
328 nbSectorsPerCluster = 2;
331 }
else if (nbSectors > 720) {
337 nbSectorsPerCluster = 2;
347 nbSectorsPerCluster = 2;
353 assert(nbDirEntry % 16 == 0);
355 if (nbSectors < 0x10000) {
356 boot.
nrSectors = narrow<uint16_t>(nbSectors);
360 throw CommandException(
"Too many sectors for FAT12 ", nbSectors);
377 params.vol_id = vol_id;
380 if (nbSectors <= 0x800000) {
381 params.
nrSectors = narrow<unsigned>(nbSectors);
383 throw CommandException(
"Too many sectors for FAT16 ", nbSectors);
385 params.hiddenSectors = nbHiddenSectors;
386 params.vol_id = vol_id;
391 SetBootSectorResult result;
392 result.sectorsPerFat = nbSectorsPerFat;
393 result.fatCount = nbFats;
394 result.fatStart = nbReservedSectors;
395 result.rootDirStart = result.fatStart + nbFats * nbSectorsPerFat;
396 result.dataStart = result.rootDirStart + nbDirEntry / 16;
397 result.descriptor = descriptor;
398 result.fat16 = fat16;
453[[nodiscard]]
static constexpr CHS logicalToCHS(
unsigned logical)
457 unsigned tmp = logical + 1;
458 uint8_t sector = tmp % 32;
459 if (sector == 0) sector = 32;
460 tmp = (tmp - sector) / 32;
461 uint8_t head = tmp % 16;
462 unsigned cylinder = tmp / 16;
463 return {cylinder, head, sector};
466static std::vector<unsigned> clampPartitionSizes(std::span<const unsigned> sizes,
467 size_t diskSize,
unsigned initialOffset,
unsigned perPartitionOffset)
469 std::vector<unsigned> clampedSizes;
472 if (sizeRemaining >= initialOffset) {
473 sizeRemaining -= initialOffset;
478 for (
auto size : sizes) {
479 if (sizeRemaining <= perPartitionOffset) {
482 sizeRemaining -= perPartitionOffset;
483 if (
size <= perPartitionOffset) {
484 throw CommandException(
"Partition size too small: ",
size);
486 size -= perPartitionOffset;
487 if (sizeRemaining >
size) {
488 clampedSizes.push_back(
size);
489 sizeRemaining -=
size;
491 clampedSizes.push_back(narrow<unsigned>(sizeRemaining));
499static std::vector<unsigned> partitionNextor(SectorAccessibleDisk& disk, std::span<const unsigned> sizes)
502 auto& pt = buf.ptNextor;
504 std::vector<unsigned> clampedSizes = clampPartitionSizes(sizes, disk.getNbSectors(), 0, 1);
506 if (clampedSizes.empty()) {
508 pt.header = NEXTOR_PARTITION_TABLE_HEADER;
510 disk.writeSector(0, buf);
514 unsigned ptSector = 0;
518 pt.header = NEXTOR_PARTITION_TABLE_HEADER;
523 auto& p = pt.part[0];
524 p.boot_ind = (i == 0) ? 0x80 : 0x00;
525 p.sys_ind =
size > 0x10000 ? 0x0E : 0x01;
530 if (i != clampedSizes.size() - 1) {
531 auto& link = pt.part[1];
534 link.start = ptSector + 1 +
size;
535 link.size =
sum(
view::drop(sizes, 1), [](
unsigned s) {
return 1 + s; });
537 link.start = ptSector + 1 +
size - (1 + clampedSizes[0]);
538 link.size = 1 + clampedSizes[i + 1];
542 disk.writeSector(ptSector, buf);
544 ptSector += 1 +
size;
550static std::vector<unsigned> partitionSunrise(SectorAccessibleDisk& disk, std::span<const unsigned> sizes)
553 auto& pt = buf.ptSunrise;
555 std::vector<unsigned> clampedSizes = clampPartitionSizes(sizes, disk.getNbSectors(), 1, 0);
556 if (clampedSizes.size() > pt.part.size()) {
557 clampedSizes.resize(pt.part.size());
561 pt.header = SUNRISE_PARTITION_TABLE_HEADER;
564 unsigned partitionOffset = 1;
566 unsigned partitionNbSectors =
size;
567 auto& p = pt.part[30 - i];
568 auto [startCylinder, startHead, startSector] =
569 logicalToCHS(partitionOffset);
570 auto [endCylinder, endHead, endSector] =
571 logicalToCHS(partitionOffset + partitionNbSectors - 1);
572 p.boot_ind = (i == 0) ? 0x80 : 0x00;
574 p.sector = startSector;
575 p.cyl = narrow_cast<uint8_t>(startCylinder);
577 p.end_head = endHead;
578 p.end_sector = endSector;
579 p.end_cyl = narrow_cast<uint8_t>(endCylinder);
580 p.start = partitionOffset;
581 p.size = partitionNbSectors;
582 partitionOffset += partitionNbSectors;
584 disk.writeSector(0, buf);
589static std::vector<unsigned> partitionBeer(SectorAccessibleDisk& disk, std::span<const unsigned> sizes)
592 auto& pt = buf.ptNextor;
594 std::vector<unsigned> clampedSizes = clampPartitionSizes(sizes, disk.getNbSectors(), 1, 0);
595 if (clampedSizes.size() > pt.part.size()) {
596 clampedSizes.resize(pt.part.size());
600 pt.header = NEXTOR_PARTITION_TABLE_HEADER;
603 unsigned partitionOffset = 1;
605 auto& p = pt.part[i];
606 p.boot_ind = (i == 0) ? 0x80 : 0x00;
608 p.start = partitionOffset;
610 partitionOffset +=
size;
613 disk.writeSector(0, buf);
619 std::vector<unsigned> clampedSizes = [&] {
621 return partitionNextor(disk, sizes);
623 return partitionSunrise(disk, sizes);
625 return partitionBeer(disk, sizes);
636 return narrow<unsigned>(clampedSizes.size());
static const SectorBuffer nextorBootBlockFAT12
static const SectorBuffer dos2BootBlock
static const SectorBuffer dos1BootBlock
static const SectorBuffer nextorBootBlockFAT16
void readSector(size_t sector, SectorBuffer &buf)
size_t getNbSectors() const
void writeSector(size_t sector, const SectorBuffer &buf)
constexpr auto enumerate(Iterable &&iterable)
Heavily inspired by Nathan Reed's blog post: Python-Like enumerate() In C++17 http://reedbeta....
constexpr vecN< N, T > min(const vecN< N, T > &x, const vecN< N, T > &y)
constexpr vecN< N, T > max(const vecN< N, T > &x, const vecN< N, T > &y)
constexpr vecN< N, T > clamp(const vecN< N, T > &x, const vecN< N, T > &minVal, const vecN< N, T > &maxVal)
bool hasPartitionTable(SectorAccessibleDisk &disk)
Check whether the given disk is partitioned.
void checkSupportedPartition(SectorAccessibleDisk &disk, unsigned partition)
Check whether partition is of type FAT12 or FAT16.
Partition & getPartition(SectorAccessibleDisk &disk, unsigned partition, SectorBuffer &buf)
Gets the requested partition.
void format(SectorAccessibleDisk &disk, MSXBootSectorType bootType)
Format the given disk (= a single partition).
unsigned partition(SectorAccessibleDisk &disk, std::span< const unsigned > sizes, MSXBootSectorType bootType)
Write a partition table to the given disk and format each partition.
constexpr void fill(ForwardRange &&range, const T &value)
size_t size(std::string_view utf8)
constexpr auto drop(Range &&range, size_t n)
uint32_t random_32bit()
Return a random 32-bit value.
auto sum(InputRange &&range, Proj proj={})
struct openmsx::MSXBootSector::@1::@2 dos1
struct openmsx::MSXBootSector::@1::@4 extended
struct openmsx::MSXBootSector::@1::@3 dos2
Endian::L16 hiddenSectors
Endian::UA_L16 dirEntries
union openmsx::MSXBootSector::@1 params
std::array< Partition, 4 > part
std::array< uint8_t, 512 > raw
PartitionTableNextor ptNextor
constexpr auto xrange(T e)