Skip to content

Hard disk / SD images: FAT16 and MBR/extended partitions - #4

Merged
VC-Anvil merged 3 commits into
masterfrom
claude/fat16-partitions
Nov 4, 2026
Merged

VC-Anvil merged 3 commits into
masterfrom
claude/fat16-partitions

Conversation

@VC-Anvil

@VC-Anvil VC-Anvil commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Summary

Hard disk / SD card images as used by MSX-DOS 2 and Nextor: FAT16 volumes and MBR partition tables (primary + extended/logical drives). DiskFormat::HDD was declared but unused; only FAT12 floppies were readable.

Core

  • Fat12 mounts FAT16 too: type from the BPB's cluster count (4085+ = FAT16, decided before clamping to the container), 2-byte entries and FAT16 markers, 32-bit sector counts (trusted only with an extended BPB or when the image can hold them), up to 128 sectors/cluster. FAT32 is refused with a clear message.
  • Volumes can start anywhere: mount(disk, firstSector, sectorCount); all sector access is volume-relative and cannot escape the partition.
  • partition.{h,cpp}: findPartitions() (MBR + EBR chain; strict, loop-safe, 64-bit arithmetic for Windows, drops overlapping/out-of-extended entries, ignores floppy-sized images and superfloppies), formatFatVolume() (FAT12/FAT16, FAT always large enough), buildPartitionedImage().
  • FAT16 write performance: one-sector read cache tied to a DiskImage::generation() counter plus a next-free-cluster hint (16 MB write: 14.5 s → 0.12 s).

CLI

  • Global --part N for every disk command (default: first FAT partition, with a whole-image fallback); info lists the partition table and reports FAT12/FAT16.
  • format <out> <N>M [LABEL] builds a FAT16 superfloppy; mkhdd <out.img> 8M,4M,... builds a partitioned image (≤ 2047 MB, ≤ 255 partitions).

GUI

  • Partition selector for partitioned images; FAT16/partition-aware status, info strip and cluster-map offsets; HDD images show their size instead of CHS. Checked with a headless (Xvfb) screenshot.

Review

An independent review of the first version found 6 bugs (FAT type from the clamped size, floppies misread as partitioned, false fsck markers near 0xFFF0, FAT sized one sector short for some sizes, total32 read from MSX-DOS 1 boot code, a FAT32-guard regression) plus quadratic FAT16 writes; all are fixed with regression tests.

Testing

  • 89 core tests (16 new in tests/test_hdd.cpp, incl. a sweep of every volume size up to 9000 sectors) + cli_smoke extended with mkhdd, --part, FAT16 format; green locally, also under ASan/UBSan. MinGW (Windows) and GUI builds compile without warnings.
  • Not yet tested against SD cards partitioned by Nextor's own FDISK — only images built by this tool.

Generated by Claude Code

- Fat12 mounts FAT16 as well (type chosen by cluster count, 4085+),
  with 2-byte FAT entries, FAT16 end/bad/reserved markers, 32-bit
  sector counts and up to 128 sectors per cluster; FAT32 is refused
  with a clear message. All sector access goes through volume-relative
  helpers so a volume can start anywhere: mount(disk, first, count).
- partition.{h,cpp}: findPartitions() walks the MBR and the EBR chain
  of an extended partition (loop-safe, strict validation, superfloppy
  FAT images are not mistaken for an MBR); formatFatVolume() builds a
  FAT12/FAT16 volume; buildPartitionedImage() builds an MBR image with
  primaries or one primary + logical drives.
- Images larger than 2MB are classified as HDD.
- tests/test_hdd.cpp: FAT16 geometry and cluster scaling, chains
  through clusters above 0xFFF, partition isolation, extended chains,
  malformed tables, FAT32 refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CtBtjm9PRm6sDhDVJbtciC
…cker)

- CLI: global --part N selects a partition of a hard disk image for
  every disk command (default: first FAT partition); info lists the
  partition table and reports FAT12/FAT16; format accepts <N>M for a
  FAT16 superfloppy (optional label); mkhdd builds an MBR image with
  primary or primary + logical partitions. Sizes up to 2047 MB, since
  image offsets are 32-bit on Windows.
- GUI: Partition selector for partitioned images; status, info strip
  and cluster-map offsets are FAT16- and partition-aware; HDD images
  show their size instead of a meaningless CHS geometry. Checked with
  a headless (Xvfb) screenshot of a 2-partition image.
- cli_smoke: mkhdd, --part on add/ls/cat/fsck, bad partition numbers,
  FAT16 superfloppy format + label.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CtBtjm9PRm6sDhDVJbtciC
- The FAT type is decided from the cluster count the BPB describes,
  before clamping to the image/partition size: a truncated FAT16 image
  (or a partition entry smaller than its BPB) was mounted as FAT12, so
  reads returned wrong data and writes put 12-bit entries in a 16-bit
  FAT. DMK floppies are bounded by their geometry.
- Floppies whose boot code contains 0x55AA and table-like bytes at
  0x1BE were taken for partitioned disks and failed to mount: no MBR
  is looked for in floppy-sized images or when a FAT follows sector 0,
  and CLI/GUI fall back to the whole image if a partition does not mount.
- fsck no longer flags valid clusters 0xFFF0-0xFFF5 (0xFF0-0xFF5 on
  FAT12) as reserved markers.
- formatFatVolume could leave the FAT one sector short of the cluster
  count (2028 sizes, none MB-aligned); the FAT now always holds it.
- The 32-bit sector count is trusted only with an extended BPB or when
  the image can hold it (MSX-DOS 1 boot code lives there); the FAT32
  check runs only after the floppy fallback failed, and the media-byte
  fallback is limited to floppy-sized images.
- findPartitions: 64-bit arithmetic for the 32-bit fields (long is
  32-bit on Windows), overlapping partitions and logical drives outside
  the extended partition are dropped, up to 4096 EBRs; mkhdd caps at 255.
- Writes: a one-sector read cache (invalidated by a DiskImage
  generation counter, so rawMut() edits and reloads are seen) and a
  next-free-cluster hint. A 16MB write on FAT16: 14.5s -> 0.12s.
- Comments: entry/cluster offsets are volume-relative.

Each fix has a regression test (tests/test_hdd.cpp).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CtBtjm9PRm6sDhDVJbtciC
@VC-Anvil
VC-Anvil merged commit e307e18 into master Nov 4, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants