diff --git a/Makefile b/Makefile index b522c14..ac731b8 100644 --- a/Makefile +++ b/Makefile @@ -6,26 +6,26 @@ # Release version: YYYYMMDD package date + optional in-progress suffix # (-dev, -rc1, ...). Empty suffix for a final release. -RELEASE_DATE = 20260827 -VERSION_SUFFIX = -dev +RELEASE_DATE = 20260911 +VERSION_SUFFIX = # compactflash.device version CFD_MAJOR = 2 CFD_MINOR = 0 -CFD_VERSION_SUFFIX = -dev -CFD_DATE = 27.08.2026 +CFD_VERSION_SUFFIX = +CFD_DATE = 11.09.2026 # compactflash.automount version (optional boot/automount module) AUTOMOUNT_MAJOR = 2 AUTOMOUNT_MINOR = 0 -AUTOMOUNT_VERSION_SUFFIX = -dev +AUTOMOUNT_VERSION_SUFFIX = AUTOMOUNT_DATE = 27.08.2026 # Tool-specific versions CFINFO_MAJOR = 1 -CFINFO_MINOR = 37 +CFINFO_MINOR = 38 CFINFO_VERSION_SUFFIX = -CFINFO_DATE = 11.01.2026 +CFINFO_DATE = 11.09.2026 PCMCIASPEED_MAJOR = 1 PCMCIASPEED_MINOR = 36 @@ -439,19 +439,19 @@ $(GUIDE_LSPTRES): $(PTABLE_VERSION_FILE) $(Q)cp $(PTABLE)/dist/docs/lsptres.guide $@ $(Q)echo " PTABLE $@" -$(TARGET_CFINFO): $(SOURCE_CFINFO) +$(TARGET_CFINFO): $(SOURCE_CFINFO) Makefile $(Q)mkdir -p $(OUTDIR_C) $(Q)echo " VBCC $(TARGET_CFINFO)" $(Q)VBCC=$(VBCC_HOME) PATH=$(VBCC_HOME)/bin:$$PATH $(VBCC) +aos68k -O2 -c99 -INDK/Include_H -DVERSION='"$(CFINFO_VERSION)"' -DDATE='"$(CFINFO_DATE)"' -o $(TARGET_CFINFO) $< $(Q)echo " $$(stat -c%s $(TARGET_CFINFO)) bytes, md5:$$(md5sum $(TARGET_CFINFO) | cut -c1-8)" -$(TARGET_PCMCIASPEED): $(SOURCE_PCMCIASPEED) +$(TARGET_PCMCIASPEED): $(SOURCE_PCMCIASPEED) Makefile $(Q)mkdir -p $(OUTDIR_C) $(Q)echo " VBCC $(TARGET_PCMCIASPEED)" $(Q)VBCC=$(VBCC_HOME) PATH=$(VBCC_HOME)/bin:$$PATH $(VBCC) +aos68k -O2 -c99 -INDK/Include_H -DVERSION='"$(PCMCIASPEED_VERSION)"' -DDATE='"$(PCMCIASPEED_DATE)"' -o $(TARGET_PCMCIASPEED) $< $(Q)echo " $$(stat -c%s $(TARGET_PCMCIASPEED)) bytes, md5:$$(md5sum $(TARGET_PCMCIASPEED) | cut -c1-8)" -$(TARGET_PCMCIACHECK): $(SOURCE_PCMCIACHECK) +$(TARGET_PCMCIACHECK): $(SOURCE_PCMCIACHECK) Makefile $(Q)mkdir -p $(OUTDIR_C) $(Q)echo " VBCC $(TARGET_PCMCIACHECK)" $(Q)VBCC=$(VBCC_HOME) PATH=$(VBCC_HOME)/bin:$$PATH $(VBCC) +aos68k -O2 -c99 -INDK/Include_H -DVERSION='"$(PCMCIACHECK_VERSION)"' -DDATE='"$(PCMCIACHECK_DATE)"' -o $(TARGET_PCMCIACHECK) $< diff --git a/README.md b/README.md index 7a27c93..9321224 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ See [docs/changes.md](docs/changes.md) for release news and history. * Amiga 1200 or 600 (A1200 tested) * AmigaOS 2.0+ (tested with 3.2.3) * CF-to-PCMCIA adapter or SD-to-CF adapter (see [Hardware Notes](#hardware-notes)) +* `ptable.library` (bundled) in `LIBS:` or in ROM: does all partition scanning and mounting * Works with fat95 filesystem for FAT32 support (disk/misc/fat95.lha) or native (FFS, SFS, PFS) filesystems if RDB partition table is used ## Installation diff --git a/dist.readme.in b/dist.readme.in index fc2819c..220c925 100644 --- a/dist.readme.in +++ b/dist.readme.in @@ -14,26 +14,38 @@ COMPONENTS IN THIS RELEASE ========================== @COMPONENT_VERSIONS@ -CHANGES IN @DATE@ -===================== +WHAT'S NEW (@DATE@) +======================= -compactflash.device: +New major version of compactflash.device 2.0 driver: -* Automount: hotplugged cards mount automatically at DOS time (on by - default; disable with AUTOMOUNT 0 in the new ENV:cfd.prefs config - file). Removing a card unmounts all supported filesystems by default. - Boot and automount bringup is split into a separate - compactflash.automount module. See automount.guide. +* Automount: hotplugged cards mount automatically at DOS time, on by + default. Can be disabled with AUTOMOUNT 0 in the new ENV:cfd.prefs + config file. Removing a card fully unmounts all supported filesystems + by default (UNMOUNT restricts it, UNMOUNT NONE keeps the + handlers). Boot and automount bringup is split into a separate + compactflash.automount module. Cold boot registers the RDB partitions + (the autoboot chain); MBR, GPT and flat partitions are mounted by the + automount agent once DOS is up, so cfd.prefs applies to them on a + ROM-resident system too. See automount.guide for the full reference; + automount-review.guide walks the feature scenario by scenario with + the expected result of each. -ptable.library: +New major version of ptable.library 2.0 (bundled): -* Unified partition scanning: one scanner parses RDB, MBR, GPT, and - whole-disk FAT and publishes every partition into a shared - partition.resource, now used by both compactflash.device and fat95 - instead of each carrying its own. The new lsptres tool lists the - resource. Library: https://github.com/pulchart/amigaos-ptable +* Unified partition scanning: a shared partition-table library the + driver now uses instead of its own parser. It scans RDB, MBR, GPT and + flat (whole-disk) partition tables and publishes every partition into + a shared partition.resource. The new lsptres tool lists the resource + (https://github.com/pulchart/amigaos-ptable). -pcmciacheck: +Tools 'CFInfo 1.38': + +* The report prints one screenful at a time; any key continues, Q + stops. +* Output lines end in LF alone. + +Tools 'pcmciacheck 2.0': * New "-identify" read-stability test: reads the same sector several times per mode and reports STABLE, UNSTABLE or STUCK. A card that @@ -156,8 +168,9 @@ recognise exactly five filesystem names and no others: DOS, FFS, SFS, PFS, FAT. Any other name is silently ignored. FAT partitions still need fat95 loaded so its filesystem code is -registered. On cold boot the driver tolerates slow cards by polling for -up to ~1.8s; with a stable card the delay is zero. +registered. At cold boot an empty slot is detected by a single Gayle +read and costs nothing; with a card present the boot stub polls up to +5 s for a slow one. More details are in automount.guide. @@ -341,7 +354,7 @@ cfd/small/68020/devs/compactflash.device - 68020+, no debug code cfd/full/68000/devs/compactflash.device - 68000+, debug-capable (serial output with Flags=8) cfd/small/68000/devs/compactflash.device - 68000+, no debug code -ptable.library (optional) - partition scanner/automounter (RDB/MBR/GPT/FAT): +ptable.library (required) - partition scanner/automounter (RDB/MBR/GPT/FAT): cfd/full/68020/libs/ptable.library - 68020+, debug-capable (serial output always on at boot) cfd/small/68020/libs/ptable.library - 68020+, no debug code cfd/full/68000/libs/ptable.library - 68000+, debug-capable (serial output always on at boot) @@ -357,15 +370,18 @@ cfd/small/68000/libs/compactflash.automount - no debug code cfd/c/CFInfo - Card information utility (displays model, size, capabilities) cfd/c/pcmciacheck - PCMCIA check utility cfd/c/pcmciaspeed - Speed test utility +cfd/c/lsptres - partition.resource entry lister (from ptable.library) cfd/Storage/DOSDrivers/CF0 - Mountlist entry (with flags documentation) cfd/Storage/DOSDrivers/CF0.info - Mountlist Workbench icon cfd/ENVARC/cfd.prefs - Example config (copy to ENVARC:cfd.prefs to automount) cfd/docs/cfd.guide - Main documentation +cfd/docs/automount.guide - Autoboot, automount, and ENV:cfd.prefs configuration cfd/docs/automount-review.guide - Automount feature review (expected behaviour) cfd/docs/changes.guide - Release notes and original CFD history cfd/docs/CFInfo.guide - CFInfo utility documentation +cfd/docs/lsptres.guide - lsptres utility documentation cfd/docs/pcmciacheck.guide - pcmciacheck utility documentation cfd/docs/pcmciaspeed.guide - pcmciaspeed utility documentation diff --git a/dist/ENVARC/cfd.prefs b/dist/ENVARC/cfd.prefs index 796987b..e58be60 100644 --- a/dist/ENVARC/cfd.prefs +++ b/dist/ENVARC/cfd.prefs @@ -1,13 +1,16 @@ -; compactflash.device config. copy to ENVARC:cfd.prefs (see automount.guide). +; compactflash.device config. copy to ENVARC:cfd.prefs AUTOMOUNT 1 FLAGS 0 CONTROL_FAT -d-D+q -; Mount FAT cards with CrossDOS instead of fat95 (both lines needed): -; DOSTYPE_FAT 0x4D534400 -; HANDLER_FAT L:CrossDOSFileSystem -; Hand-mounted (DEVS:DOSDrivers) partitions are unmounted on card removal -; like everything else. Set 0 to keep their handler mounted instead: -; UNMOUNT_STATIC 0 -; A hand mount over an already-served partition is refused (object in use). -; Set 1 to allow it anyway; writes then corrupt the volume, your own risk: -; MOUNT_USED 1 + +; Mount FAT cards with CrossDOS instead of fat95: +;DOSTYPE_FAT 0x4D534400 +;HANDLER_FAT L:CrossDOSFileSystem + +; Static mounts (DEVS:DOSDrivers) are unmounted on card removal. +; Set 0 to keep their handler mounted instead: +;UNMOUNT_STATIC 0 + +; A static mount over an already-automounted partition is refused +; (object in use). Set 1 to allow it anyway. +;MOUNT_USED 1 diff --git a/dist/c/CFInfo b/dist/c/CFInfo index 0251788..25f509f 100755 --- a/dist/c/CFInfo +++ b/dist/c/CFInfo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:036f51b0b4310ba43a60db62decb035afabd917aed954500677bb0f45ec5ac49 -size 15564 +oid sha256:27d007dee6f275d3d45d7d4c691a3a3adfc78d8e99ba66ca086c8966ad2dcc3e +size 15992 diff --git a/dist/c/lsptres b/dist/c/lsptres index a71c53e..bc3843b 100755 --- a/dist/c/lsptres +++ b/dist/c/lsptres @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc14f0206f65cdf99b54968fb245c9e5acc00386b957a5b040c45cd36850ada4 -size 11184 +oid sha256:6d3269ebc28e1ea5648aa8c65fdcaf347c0b1d05d14c93d546a3c50a6e1a4f00 +size 11200 diff --git a/dist/docs/CFInfo.guide b/dist/docs/CFInfo.guide index 68cbdd1..456a5bf 100644 --- a/dist/docs/CFInfo.guide +++ b/dist/docs/CFInfo.guide @@ -1,5 +1,5 @@ @database "CFInfo - CompactFlash Card Information Tool" -@$VER: CFInfo guide 1.37 (11.01.2026) +@$VER: CFInfo guide 1.38 (11.09.2026) @author "Generated by md2guide.py v1.6 (14.06.2026)" @(c) "See LICENSE" @font topaz.font 8 @@ -103,9 +103,13 @@ using the @{fg shine}compactflash.device@{fg text} driver. CFInfo 1 ; Show info for unit 1 @{fg text} +The report prints one screenful at a time; any key continues, @{fg shine}Q@{fg text} stops. +Output +redirected to a file or another stream scrolls without pausing. + Example Output: @{fg highlight} - CFInfo 1.37 - CompactFlash Card Information + CFInfo 1.38 - CompactFlash Card Information Device: compactflash.device unit 0 === CompactFlash Card Information === diff --git a/dist/docs/automount.guide b/dist/docs/automount.guide index e690073..39f7620 100644 --- a/dist/docs/automount.guide +++ b/dist/docs/automount.guide @@ -50,13 +50,13 @@ they go away again. With the driver in a Kickstart ROM you can also boot straight off the card. This guide covers both, and the @{fg shine}ENV:cfd.prefs@{fg text} file that configures them. -The scanning and mounting itself is done by @{fg shine}ptable.library@{fg text} , which is -required for all of it. Without that library @{fg shine}compactflash.device@{fg text} still -works, but as a plain mount-only device: you mount partitions yourself -through @{fg shine}DEVS:DOSDrivers/@{fg text} . See @{fg shine}ptable.guide@{fg text} (shipped with the -ptable.library archive) for the library internals. The expected -behaviour of the whole feature is catalogued scenario by scenario in -@{fg shine}automount-review.guide@{fg text} ( @{" automount-review.guide " link "automount-review.guide/Main"} ). +@{fg shine}ptable.library@{fg text} does the scanning and mounting, and is required for all +of it. Without that library @{fg shine}compactflash.device@{fg text} still works, but as a +plain mount-only device: you mount partitions yourself through +@{fg shine}DEVS:DOSDrivers/@{fg text} . See @{fg shine}ptable.guide@{fg text} (shipped with the ptable.library +archive) for the library internals. The expected behaviour of the whole +feature is catalogued scenario by scenario in @{fg shine}automount-review.guide@{fg text} ( @{" automount-review.guide " link "automount-review.guide/Main"} +). ---------------------------------------- @{"<< Prev" link Main} @{"Contents" link Main} @{"Next >>" link Quick_start} @@ -66,25 +66,29 @@ behaviour of the whole feature is catalogued scenario by scenario in @node Quick_start "Quick start" @{b}Quick start@{ub} -Three modules are involved, and automount is opt-in by whether you -include the third: +Three modules are involved: * @{fg shine}compactflash.device@{fg text}: the I/O driver. * @{fg shine}ptable.library@{fg text} : the partition scanner and automounter (RDB, MBR, GPT, flat FAT). - * @{fg shine}compactflash.automount@{fg text} : boot and automount bringup. Include it only when you - want autoboot or automount. - - @{b}Goal@{ub} @{b}Modules@{ub} @{b}Cold-boot autoboot@{ub} - -------------------- ------------------------ ------------------ - ROM autoboot @{fg shine}compactflash.device@{fg text} + available - @{fg shine}compactflash.automount@{fg text} + - @{fg shine}ptable.library@{fg text} in - Kickstart - Resident/on-disk, no @{fg shine}compactflash.device@{fg text} + no - automount @{fg shine}ptable.library@{fg text} (no - automount module) - On-disk automount @{fg shine}LoadModule@{fg text} line below no + * @{fg shine}compactflash.automount@{fg text} : bringup. It opens the device after DOS, and in a ROM + it also carries the cold-boot autoboot stub. + +Automount needs the device open, because the agent lives in the unit +task. Anything that opens it will do, a @{fg shine}DEVS:DOSDrivers/CF0@{fg text} mount +included; @{fg shine}compactflash.automount@{fg text} is how you get it open without a +mountlist entry, and the only way to get cold-boot autoboot. + + @{b}Goal@{ub} @{b}Modules@{ub} @{b}Cold-boot autoboot@{ub} + ------------------------- ------------------------ ------------------ + ROM autoboot @{fg shine}compactflash.device@{fg text} + available + @{fg shine}compactflash.automount@{fg text} + + @{fg shine}ptable.library@{fg text} in + Kickstart + On-disk, device opened by @{fg shine}compactflash.device@{fg text} + no + a @{fg shine}DOSDrivers@{fg text} mount @{fg shine}ptable.library@{fg text} + On-disk, no mountlist @{fg shine}LoadModule@{fg text} line below no + entry For a disk install, put @{fg shine}compactflash.automount@{fg text} and @{fg shine}ptable.library@{fg text} in @{fg shine}LIBS:@{fg text} and add this to your @{fg shine}user-startup@{fg text} : diff --git a/dist/docs/cfd.guide b/dist/docs/cfd.guide index f80c46b..d4c310c 100644 --- a/dist/docs/cfd.guide +++ b/dist/docs/cfd.guide @@ -1,5 +1,5 @@ @database "compactflash.device" -@$VER: compactflash.device guide 2.0-dev (25.08.2026) +@$VER: compactflash.device guide 2.0-dev (27.08.2026) @author "Generated by md2guide.py v1.6 (14.06.2026)" @(c) "See LICENSE" @font topaz.font 8 @@ -185,6 +185,8 @@ See @{" docs/changes.guide " link "changes.guide/Main"} for release news and h * Amiga 1200 or 600 (A1200 tested) * AmigaOS 2.0+ (tested with 3.2.3) * CF-to-PCMCIA adapter or SD-to-CF adapter (see @{"Hardware Notes" link hardware_notes}) + * @{fg shine}ptable.library@{fg text} (bundled) in @{fg shine}LIBS:@{fg text} or in ROM: does all partition scanning and + mounting * Works with fat95 filesystem for FAT32 support (disk/misc/fat95.lha) or native (FFS, SFS, PFS) filesystems if RDB partition table is used diff --git a/dist/docs/changes.guide b/dist/docs/changes.guide index 7c01a5e..f9126cc 100644 --- a/dist/docs/changes.guide +++ b/dist/docs/changes.guide @@ -1,5 +1,5 @@ @database "compactflash.device release notes" -@$VER: compactflash.device release notes guide 20260827-dev (27.08.2026) +@$VER: compactflash.device release notes guide 20260911 (11.09.2026) @author "Generated by md2guide.py v1.6 (14.06.2026)" @(c) "See LICENSE" @font topaz.font 8 @@ -10,7 +10,7 @@ @{b}compactflash.device release notes@{ub} - @{"20260827-dev" link 20260827_dev} + @{"20260911" link 20260911} @{"20260614" link 20260614} @{"20260609" link 20260609} @{"v1.43 (19.05.2026)" link v1_43__19_05_2026_} @@ -32,7 +32,7 @@ @{"20260609" link 20260609} @{"20260614" link 20260614} - @{"20260827-dev" link 20260827_dev} + @{"20260911" link 20260911} @{b}O@{ub} @{"Original CFD History Through v1.33" link Original_CFD_History_Through_v1_33} @{b}V@{ub} @@ -49,47 +49,47 @@ @endnode -@node 20260827_dev "20260827-dev" -@{b}20260827-dev@{ub} +@node 20260911 "20260911" +@{b}20260911@{ub} @{i}Components in this release@{ui}: - * @{fg shine}compactflash.device 2.0-dev (27.08.2026)@{fg text} @{i}(new)@{ui} - * @{fg shine}compactflash.automount 2.0-dev (27.08.2026)@{fg text} @{i}(new)@{ui} - * @{fg shine}ptable.library 2.0-dev (27.08.2026)@{fg text} @{i}(new)@{ui} - * @{fg shine}CFInfo 1.37 (11.01.2026)@{fg text} + * @{fg shine}compactflash.device 2.0 (11.09.2026)@{fg text} @{i}(new)@{ui} + * @{fg shine}compactflash.automount 2.0 (27.08.2026)@{fg text} @{i}(new)@{ui} + * @{fg shine}ptable.library 2.0 (11.09.2026)@{fg text} @{i}(new)@{ui} + * @{fg shine}CFInfo 1.38 (11.09.2026)@{fg text} @{i}(new)@{ui} * @{fg shine}pcmciaspeed 1.36 (02.01.2026)@{fg text} * @{fg shine}pcmciacheck 2.0 (06.08.2026)@{fg text} @{i}(new)@{ui} - * @{fg shine}lsptres 1.0-dev (27.08.2026)@{fg text} @{i}(new)@{ui} + * @{fg shine}lsptres 1.0 (11.09.2026)@{fg text} @{i}(new)@{ui} -@{b}CompactFlash Driver@{ub} +@{b}New major version of compactflash.device 2.0 driver@{ub} - * @{b}Automount.@{ub} Hotplugged cards mount automatically at DOS time, on by default; - disable with @{fg shine}AUTOMOUNT 0@{fg text} in the new @{fg shine}ENV:cfd.prefs@{fg text} config file. Removing a card - fully unmounts all supported filesystems by default ( @{fg shine}UNMOUNT @{fg text} - restricts it, @{fg shine}UNMOUNT NONE@{fg text} keeps the handlers). Boot and automount bringup is - split into a separate @{fg shine}compactflash.automount@{fg text} module. Cold boot registers the - RDB partitions (the autoboot chain); MBR, GPT and flat partitions are mounted - by the automount agent once DOS is up, so @{fg shine}cfd.prefs@{fg text} applies to them on a - ROM-resident system too. See @{" automount.guide " link "automount.guide/Main"} . - * @{b}The FAT filesystem is selectable.@{ub} @{fg shine}DOSTYPE_FAT 0x4D534400@{fg text} with - @{fg shine}HANDLER_FAT L:CrossDOSFileSystem@{fg text} in @{fg shine}cfd.prefs@{fg text} mounts FAT cards with CrossDOS - instead of fat95; @{fg shine}HANDLER_FAT L:fat95@{fg text} alone loads fat95 from disk when it is - not resident. See @{" automount.guide " link "automount.guide/Main"} . + * @{b}Automount.@{ub} Hotplugged cards mount automatically at DOS time, on by default. + Can be disabled with @{fg shine}AUTOMOUNT 0@{fg text} in the new @{fg shine}ENV:cfd.prefs@{fg text} config file. + Removing a card fully unmounts all supported filesystems by default ( + @{fg shine}UNMOUNT @{fg text} restricts it, @{fg shine}UNMOUNT NONE@{fg text} keeps the handlers). Boot and + automount bringup is split into a separate @{fg shine}compactflash.automount@{fg text} module. Cold + boot registers the RDB partitions (the autoboot chain); MBR, GPT and flat + partitions are mounted by the automount agent once DOS is up, so @{fg shine}cfd.prefs@{fg text} + applies to them on a ROM-resident system too. See @{" automount.guide " link "automount.guide/Main"} for the full reference; @{" automount-review.guide " link "automount-review.guide/Main"} + walks the feature scenario by scenario with the expected result of each. -@{b}Partition Table library@{ub} +@{b}New major version of ptable.library 2.0 (bundled)@{ub} - * @{b}Unified partition scanning.@{ub} One scanner parses RDB, MBR, GPT, and flat - (whole-disk) partition tables and publishes every partition into a shared - @{fg shine}partition.resource@{fg text} , now used by both @{fg shine}compactflash.device@{fg text} and @{fg shine}fat95@{fg text} instead of - each carrying its own. The new @{fg shine}lsptres@{fg text} tool lists the resource. See @{" ptable.guide " link "ptable.guide/Main"} and @{" lsptres.guide " link "lsptres.guide/Main"} . - * @{b}Selectable filesystem for MBR/GPT FAT partitions@{ub} , driven by the @{fg shine}DOSTYPE_FAT@{fg text} - and @{fg shine}HANDLER_FAT@{fg text} keys above. @{fg shine}lsptres@{fg text} shows the DosType a partition is mounted - with. + * @{b}Unified partition scanning.@{ub} A shared partition-table library the driver now + uses instead of its own parser. It scans RDB, MBR, GPT and flat (whole-disk) + partition tables and publishes every partition into a shared + @{fg shine}partition.resource@{fg text} . The new @{fg shine}lsptres@{fg text} tool lists the resource. See @{" ptable.guide " link "ptable.guide/Main"} and @{" lsptres.guide " link "lsptres.guide/Main"} . @{b}Tools@{ub} -@{i}pcmciacheck 2.0@{ui} +@{b}'CFInfo 1.38'@{ub} + + * The report prints one screenful at a time; any key continues, @{fg shine}Q@{fg text} stops. + * Output lines end in @{fg shine}LF@{fg text} alone. + +@{b}'pcmciacheck 2.0'@{ub} + * @{b}New @{fg shine}-identify@{fg text} read-stability test.@{ub} (#67 (https://github.com/pulchart/cfd/issues/67)) Reads the same sector several times per mode and reports @{fg shine}STABLE@{fg text} , @{fg shine}UNSTABLE@{fg text} or @{fg shine}STUCK@{fg text} . A card that returns @@ -107,9 +107,6 @@ attached to a bug report. Restored on exit, and a diagnostic only: the driver programs that register itself. - * @{b}The read and write mode tests follow the driver more accurately@{ub} , and @{fg shine}-cis@{fg text} now - reports the real access speed of cards that use the extended encoding. See @{" pcmciacheck.guide " link "pcmciacheck.guide/Main"} . - ---------------------------------------- @{"<< Prev" link Main} @{"Contents" link Main} @{"Next >>" link 20260614} @@ -132,7 +129,7 @@ Documentation-only update (no binaries changed). Release notes and original CFD history are included in @{" docs/changes.guide " link "changes.guide/Main"} document. ---------------------------------------- -@{"<< Prev" link 20260827_dev} @{"Contents" link Main} @{"Next >>" link 20260609} +@{"<< Prev" link 20260911} @{"Contents" link Main} @{"Next >>" link 20260609} @endnode diff --git a/dist/docs/lsptres.guide b/dist/docs/lsptres.guide index ef78d04..7d377a0 100644 --- a/dist/docs/lsptres.guide +++ b/dist/docs/lsptres.guide @@ -1,5 +1,5 @@ @database "lsptres" -@$VER: lsptres guide 1.0-dev (27.08.2026) +@$VER: lsptres guide 1.0 (11.09.2026) @author "Generated by md2guide.py v1.6 (14.06.2026)" @(c) "See LICENSE" @font topaz.font 8 @@ -152,13 +152,14 @@ companion to fat95's @{fg shine}lsfsres@{fg text} , which lists @{fg shine}FileS Verbose (@{fg shine}VERBOSE@{fg text} / @{fg shine}V@{fg text}) appends: - @{b}Column@{ub} @{b}Meaning@{ub} - ------ --------------------------------- - CMD Read command used for the device: - @{fg shine}NSCMD@{fg text} , @{fg shine}TD64@{fg text} , @{fg shine}SCSI@{fg text} or @{fg shine}CMD@{fg text} . - Start Start block (LBA). - Blocks Block count. - Size Size in MB. + @{b}Column@{ub} @{b}Meaning@{ub} + ------ ------------------------------------- + CMD Read command used for the device: + @{fg shine}NSCMD@{fg text} , @{fg shine}TD64@{fg text} , @{fg shine}SCSI@{fg text} or @{fg shine}CMD@{fg text} . + Start Start block (LBA). + Blocks Block count. + Size Size in MiB, or kiB below 1 MiB. Both + are binary: a block is 512 bytes. ---------------------------------------- @{"<< Prev" link Usage} @{"Contents" link Main} @{"Next >>" link Examples} diff --git a/dist/full/68000/devs/compactflash.device b/dist/full/68000/devs/compactflash.device index 108a869..7b1c79d 100644 Binary files a/dist/full/68000/devs/compactflash.device and b/dist/full/68000/devs/compactflash.device differ diff --git a/dist/full/68000/libs/compactflash.automount b/dist/full/68000/libs/compactflash.automount index 4d51dd0..6fd6a1c 100644 Binary files a/dist/full/68000/libs/compactflash.automount and b/dist/full/68000/libs/compactflash.automount differ diff --git a/dist/full/68000/libs/ptable.library b/dist/full/68000/libs/ptable.library index f47ff79..2138145 100644 Binary files a/dist/full/68000/libs/ptable.library and b/dist/full/68000/libs/ptable.library differ diff --git a/dist/full/68020/devs/compactflash.device b/dist/full/68020/devs/compactflash.device index 1b18721..44996ec 100644 Binary files a/dist/full/68020/devs/compactflash.device and b/dist/full/68020/devs/compactflash.device differ diff --git a/dist/full/68020/libs/compactflash.automount b/dist/full/68020/libs/compactflash.automount index 4d51dd0..6fd6a1c 100644 Binary files a/dist/full/68020/libs/compactflash.automount and b/dist/full/68020/libs/compactflash.automount differ diff --git a/dist/full/68020/libs/ptable.library b/dist/full/68020/libs/ptable.library index 48d702a..b4aaa81 100644 Binary files a/dist/full/68020/libs/ptable.library and b/dist/full/68020/libs/ptable.library differ diff --git a/dist/small/68000/devs/compactflash.device b/dist/small/68000/devs/compactflash.device index b2c7f32..f4f9896 100644 Binary files a/dist/small/68000/devs/compactflash.device and b/dist/small/68000/devs/compactflash.device differ diff --git a/dist/small/68000/libs/compactflash.automount b/dist/small/68000/libs/compactflash.automount index 5681a74..8538e3c 100644 Binary files a/dist/small/68000/libs/compactflash.automount and b/dist/small/68000/libs/compactflash.automount differ diff --git a/dist/small/68000/libs/ptable.library b/dist/small/68000/libs/ptable.library index 8bc0d03..ffb1e4b 100644 Binary files a/dist/small/68000/libs/ptable.library and b/dist/small/68000/libs/ptable.library differ diff --git a/dist/small/68020/devs/compactflash.device b/dist/small/68020/devs/compactflash.device index 6d6f5cf..23bcff5 100644 Binary files a/dist/small/68020/devs/compactflash.device and b/dist/small/68020/devs/compactflash.device differ diff --git a/dist/small/68020/libs/compactflash.automount b/dist/small/68020/libs/compactflash.automount index 5681a74..8538e3c 100644 Binary files a/dist/small/68020/libs/compactflash.automount and b/dist/small/68020/libs/compactflash.automount differ diff --git a/dist/small/68020/libs/ptable.library b/dist/small/68020/libs/ptable.library index 51ce14e..fe59e86 100644 Binary files a/dist/small/68020/libs/ptable.library and b/dist/small/68020/libs/ptable.library differ diff --git a/docs/CFInfo.md b/docs/CFInfo.md index 4ad77b1..c4abbd9 100644 --- a/docs/CFInfo.md +++ b/docs/CFInfo.md @@ -16,9 +16,12 @@ CFInfo ; Show info for unit 0 CFInfo 1 ; Show info for unit 1 ``` +The report prints one screenful at a time; any key continues, `Q` stops. Output +redirected to a file or another stream scrolls without pausing. + Example Output: ``` -CFInfo 1.37 - CompactFlash Card Information +CFInfo 1.38 - CompactFlash Card Information Device: compactflash.device unit 0 === CompactFlash Card Information === diff --git a/docs/automount.md b/docs/automount.md index 3f8d8af..a1dfc5d 100644 --- a/docs/automount.md +++ b/docs/automount.md @@ -2,21 +2,23 @@ Insert a CF card and its partitions mount by themselves; pull it and they go away again. With the driver in a Kickstart ROM you can also boot straight off the card. This guide covers both, and the `ENV:cfd.prefs` file that configures them. -The scanning and mounting itself is done by `ptable.library`, which is required for all of it. Without that library `compactflash.device` still works, but as a plain mount-only device: you mount partitions yourself through `DEVS:DOSDrivers/`. See `ptable.guide` (shipped with the ptable.library archive) for the library internals. The expected behaviour of the whole feature is catalogued scenario by scenario in `automount-review.guide` ([automount-review.md](automount-review.md)). +`ptable.library` does the scanning and mounting, and is required for all of it. Without that library `compactflash.device` still works, but as a plain mount-only device: you mount partitions yourself through `DEVS:DOSDrivers/`. See `ptable.guide` (shipped with the ptable.library archive) for the library internals. The expected behaviour of the whole feature is catalogued scenario by scenario in `automount-review.guide` ([automount-review.md](automount-review.md)). ## Quick start -Three modules are involved, and automount is opt-in by whether you include the third: +Three modules are involved: - `compactflash.device`: the I/O driver. - `ptable.library`: the partition scanner and automounter (RDB, MBR, GPT, flat FAT). -- `compactflash.automount`: boot and automount bringup. Include it only when you want autoboot or automount. +- `compactflash.automount`: bringup. It opens the device after DOS, and in a ROM it also carries the cold-boot autoboot stub. + +Automount needs the device open, because the agent lives in the unit task. Anything that opens it will do, a `DEVS:DOSDrivers/CF0` mount included; `compactflash.automount` is how you get it open without a mountlist entry, and the only way to get cold-boot autoboot. | Goal | Modules | Cold-boot autoboot | |---|---|---| | ROM autoboot | `compactflash.device` + `compactflash.automount` + `ptable.library` in Kickstart | available | -| Resident/on-disk, no automount | `compactflash.device` + `ptable.library` (no automount module) | no | -| On-disk automount | `LoadModule` line below | no | +| On-disk, device opened by a `DOSDrivers` mount | `compactflash.device` + `ptable.library` | no | +| On-disk, no mountlist entry | `LoadModule` line below | no | For a disk install, put `compactflash.automount` and `ptable.library` in `LIBS:` and add this to your `user-startup`: diff --git a/docs/changes.md b/docs/changes.md index 789ad6f..a3ee437 100644 --- a/docs/changes.md +++ b/docs/changes.md @@ -1,36 +1,38 @@ -## 20260827-dev +## 20260911 _Components in this release_: -- `compactflash.device 2.0-dev (27.08.2026)` _(new)_ -- `compactflash.automount 2.0-dev (27.08.2026)` _(new)_ -- `ptable.library 2.0-dev (27.08.2026)` _(new)_ -- `CFInfo 1.37 (11.01.2026)` +- `compactflash.device 2.0 (11.09.2026)` _(new)_ +- `compactflash.automount 2.0 (27.08.2026)` _(new)_ +- `ptable.library 2.0 (11.09.2026)` _(new)_ +- `CFInfo 1.38 (11.09.2026)` _(new)_ - `pcmciaspeed 1.36 (02.01.2026)` - `pcmciacheck 2.0 (06.08.2026)` _(new)_ -- `lsptres 1.0-dev (27.08.2026)` _(new)_ +- `lsptres 1.0 (11.09.2026)` _(new)_ -##### CompactFlash Driver +#### New major version of compactflash.device 2.0 driver -- **Automount.** Hotplugged cards mount automatically at DOS time, on by default; disable with `AUTOMOUNT 0` in the new `ENV:cfd.prefs` config file. Removing a card fully unmounts all supported filesystems by default (`UNMOUNT ` restricts it, `UNMOUNT NONE` keeps the handlers). Boot and automount bringup is split into a separate `compactflash.automount` module. Cold boot registers the RDB partitions (the autoboot chain); MBR, GPT and flat partitions are mounted by the automount agent once DOS is up, so `cfd.prefs` applies to them on a ROM-resident system too. See [automount.md](automount.md). -- **The FAT filesystem is selectable.** `DOSTYPE_FAT 0x4D534400` with `HANDLER_FAT L:CrossDOSFileSystem` in `cfd.prefs` mounts FAT cards with CrossDOS instead of fat95; `HANDLER_FAT L:fat95` alone loads fat95 from disk when it is not resident. See [automount.md](automount.md). +- **Automount.** Hotplugged cards mount automatically at DOS time, on by default. Can be disabled with `AUTOMOUNT 0` in the new `ENV:cfd.prefs` config file. Removing a card fully unmounts all supported filesystems by default (`UNMOUNT ` restricts it, `UNMOUNT NONE` keeps the handlers). Boot and automount bringup is split into a separate `compactflash.automount` module. Cold boot registers the RDB partitions (the autoboot chain); MBR, GPT and flat partitions are mounted by the automount agent once DOS is up, so `cfd.prefs` applies to them on a ROM-resident system too. See [automount.md](automount.md) for the full reference; [automount-review.md](automount-review.md) walks the feature scenario by scenario with the expected result of each. -##### Partition Table library +#### New major version of ptable.library 2.0 (bundled) -- **Unified partition scanning.** One scanner parses RDB, MBR, GPT, and flat (whole-disk) partition tables and publishes every partition into a shared `partition.resource`, now used by both `compactflash.device` and `fat95` instead of each carrying its own. The new `lsptres` tool lists the resource. See [ptable.md](https://github.com/pulchart/amigaos-ptable/blob/HEAD/docs/ptable.md) and [lsptres.md](https://github.com/pulchart/amigaos-ptable/blob/HEAD/docs/lsptres.md). -- **Selectable filesystem for MBR/GPT FAT partitions**, driven by the `DOSTYPE_FAT` and `HANDLER_FAT` keys above. `lsptres` shows the DosType a partition is mounted with. +- **Unified partition scanning.** A shared partition-table library the driver now uses instead of its own parser. It scans RDB, MBR, GPT and flat (whole-disk) partition tables and publishes every partition into a shared `partition.resource`. The new `lsptres` tool lists the resource. See [ptable.md](https://github.com/pulchart/amigaos-ptable/blob/HEAD/docs/ptable.md) and [lsptres.md](https://github.com/pulchart/amigaos-ptable/blob/HEAD/docs/lsptres.md). -##### Tools +#### Tools + +##### 'CFInfo 1.38' + +- The report prints one screenful at a time; any key continues, `Q` stops. +- Output lines end in `LF` alone. + +##### 'pcmciacheck 2.0' -*pcmciacheck 2.0* - **New `-identify` read-stability test.** ([#67](https://github.com/pulchart/cfd/issues/67)) Reads the same sector several times per mode and reports `STABLE`, `UNSTABLE` or `STUCK`. A card that returns different bytes on each read, or the same word over and over, corrupts files, and that is why the driver refuses it with `FAILED (data mismatch)` or `FAILED (repeated pattern)`. Only the mode the driver actually uses decides the verdict. `-r ` sets the number of runs (default 5). Console only, no log file, like `-cis`. - **A Gayle PCMCIA timing can be given** (`100`, `150`, `250`, `720`): `-s ` for the normal run, `pcmciacheck -identify ` for the stability test. Both accept `all`: `-identify all` prints a verdict matrix so you can see whether a card only misbehaves at the faster timings, and `-s all` repeats the capture at every timing and logs all of them, so the raw data behind such a fault can be attached to a bug report. Restored on exit, and a diagnostic only: the driver programs that register itself. -- **The read and write mode tests follow the driver more accurately**, and `-cis` now reports the real access speed of cards that use the extended encoding. See [pcmciacheck.md](pcmciacheck.md). - ## 20260614 _Components in this release_: diff --git a/extern/ptable b/extern/ptable index fee9475..778c57f 160000 --- a/extern/ptable +++ b/extern/ptable @@ -1 +1 @@ -Subproject commit fee947551d70f70106baffe52556f842f0fddc37 +Subproject commit 778c57f5c21fe81c89cc9ccdee8c3d7ac7dd7a4c diff --git a/src/cfd.s b/src/cfd.s index 3315942..1d5c859 100644 --- a/src/cfd.s +++ b/src/cfd.s @@ -721,7 +721,7 @@ CFU_KillTask = 956 ;task to signal on kill CFU_CacheFlags = 960 ;saved cache flags ;--- Multi-sector R/W --- -CFU_MultiSizeRW = 964 ;bytes per transfer (sectors * 512) +CFU_MultiSizeRW = 964 ;sectors per transfer, 256 or the firmware count ;set from card or override (Flags=16) ;--- Runtime-bound I/O handlers (see _BindIOHandlers header for dispatch flow) --- @@ -1689,7 +1689,30 @@ _r_odd: ;_Clear: ;--- SCSI direct ------------------------------------------- - +; HD_SCSICMD: run one SCSI command line against this unit. Public, and +; the only way in for the vendor command CFD_GETCONFIG. +; +; Input: +; a2 = &IORequest, IO_Data = &SCSICmd (required, else IOERR_BADLENGTH) +; a3 = CFU pointer +; SCSI_CmdLength must be >= 6 and must equal the length _sc_tab +; records for that opcode; SCSI_Command must be non-NULL +; +; Output: +; d0 = IO_Error: 0, IOERR_BADLENGTH for a malformed request, or a code +; derived from the drive's status +; SCSI_Status = 0, or $02 (CHECK CONDITION) for an opcode this driver +; does not emulate and for a command-length mismatch +; SCSI_CmdActual = the CDB length that was accepted +; SCSI_SenseActual, SCSI_SenseData filled only when SCSIF_AUTOSENSE is +; set in SCSI_Flags +; IO_Actual is cleared on every path; the per-command byte count is +; reported in SCSI_Actual by the handler itself +; +; With the ATAPI handler compiled in and the unit in ATAPI mode +; (CFU_PLength > 0) the whole SCSICmd goes to the drive via _Packet and +; none of the emulation below runs. +; _ScsiCmd: move.l a2,-(sp) moveq.l #IOERR_BADLENGTH,d0 @@ -1766,6 +1789,10 @@ _sc_atapi: bra.s _sc_end endc +; Commands this driver emulates, one entry per opcode: +; dc.w (CDB length)<<8 + opcode, handler - _sc_tab +; terminated by a zero word. The length is checked, not assumed, so an +; opcode sent with the wrong CDB length is refused like an unknown one. _sc_tab: dc.w 10<<8+READ10, _Read10-_sc_tab dc.w 10<<8+WRITE10, _Write10-_sc_tab @@ -1981,6 +2008,13 @@ _aid_end: ; --- v1.37 structure ends here (12 bytes) --- ; Future versions may add more fields after offset 12 ; +; A caller may declare less than struct_size: it then receives that many +; bytes, SCSI_Actual reports what was delivered, and nothing is written +; past the buffer. Two bytes are enough to read struct_size and size a +; second request. A zero length or a null pointer is a no-op that still +; reports success: nothing is written, and SCSI_Actual keeps whatever the +; caller left there. +; ; Clients should: ; 1. Request a large buffer (e.g., 64 bytes) ; 2. Check struct_size to know what fields are available @@ -2003,15 +2037,25 @@ _cgc_1: move.l d1,SCSI_Actual(a2) move.l d0,a1 ;destination - ;Build config data in buffer - move.w #CFD_CONFIG_SIZE,(a1)+ ;offset 0-1: structure size (for versioning) - move.b #FILE_VERSION,(a1)+ ;offset 2: major version - move.b #FILE_REVISION,(a1)+ ;offset 3: minor version - move.w CFU_OpenFlags(a3),(a1)+ ;offset 4-5: flags - move.w CFU_MultiSize(a3),(a1)+ ;offset 6-7: firmware multi - move.w CFU_MultiSizeRW(a3),(a1)+ ;offset 8-9: actual multi - move.b CFU_ReceiveMode(a3),(a1)+ ;offset 10: read mode - move.b CFU_WriteMode(a3),(a1)+ ;offset 11: write mode + ;Build the struct on the stack, then hand over only the bytes the + ;caller asked for: a client sized to an older struct_size must not + ;be written past, and probing with a short buffer has to stay safe. + lea -CFD_CONFIG_SIZE(sp),sp + move.l sp,a0 + move.w #CFD_CONFIG_SIZE,(a0)+ ;offset 0-1: structure size (for versioning) + move.b #FILE_VERSION,(a0)+ ;offset 2: major version + move.b #FILE_REVISION,(a0)+ ;offset 3: minor version + move.w CFU_OpenFlags(a3),(a0)+ ;offset 4-5: flags + move.w CFU_MultiSize(a3),(a0)+ ;offset 6-7: firmware multi + move.w CFU_MultiSizeRW(a3),(a0)+ ;offset 8-9: actual multi + move.b CFU_ReceiveMode(a3),(a0)+ ;offset 10: read mode + move.b CFU_WriteMode(a3),(a0)+ ;offset 11: write mode + move.l sp,a0 +_cgc_copy: + move.b (a0)+,(a1)+ + subq.l #1,d1 + bgt.s _cgc_copy + lea CFD_CONFIG_SIZE(sp),sp _cgc_end: moveq.l #0,d0 ;success @@ -5361,24 +5405,51 @@ _gid_end: movem.l (sp)+,d2-d6/a2 rts + ifd ATAPI +; In: a2 = buffer, d2 = LBA, d4 = block count, a3 = unit. +; Out: a0 = SCSI structure for _Packet. Clobbers d0/d1/a1. +INIT_SCSI_RW macro + lea CFU_SCSIStruct(a3),a0 + move.l a2,(a0)+ ;SCSI_Data + move.l d4,d0 + move.l CFU_BlockSize(a3),d1 + UMUL32 ;sectors * block size, not a fixed 512 + move.l d0,(a0)+ ;SCSI_Length + clr.l (a0)+ ;SCSI_Actual + lea CFU_Packet(a3),a1 + move.l a1,(a0)+ ;SCSI_Command + moveq.l #10,d0 + swap d0 + move.l d0,(a0)+ ;SCSI_CmdLength, _CmdActual + move.w #\1<<8,(a0) ;SCSI_Flags, _Status + move.w #\2<<8,(a1)+ + move.l d2,(a1)+ + move.l d4,d0 + lsl.l #8,d0 + move.l d0,(a1) + lea CFU_SCSIStruct(a3),a0 + endm + endc + ;--- read Blocks ------------------------------------------- ; Read sectors from CF card using PIO ; ; Input: ; d0 = starting block number (LBA) -; d1 = byte count to read +; d1 = sector count to read ; a1 = destination buffer pointer ; a3 = CFU pointer ; ; Output: -; d0 = bytes actually read (0 on error) +; d0 = sectors actually read, which after an error is what completed +; before it; the code is in CFU_IOErr ; ; Register usage: ; d2 = current block number -; d3 = bytes remaining -; d4 = bytes per transfer chunk -; d5 = total bytes (for return value) -; d6 = sectors per interrupt +; d3 = sectors remaining +; d4 = sectors per transfer chunk +; d5 = total sectors (for return value) +; d6 = sectors per interrupt (IDE) / sector cap per chunk (ATAPI) ; d7 = MOVEM loop counter ; a2 = buffer pointer ; @@ -5398,7 +5469,7 @@ _ReadBlocks: ifd ATAPI tst.w CFU_PLength(a3) - bgt.w _rb_scsi + bgt.w _rb_satapi endc bsr _IDEStart @@ -5533,43 +5604,44 @@ _rb_c1: bra.w _rb_0 ifd ATAPI +_rb_satapi: + move.l CFU_BlockSize(a3),d1 + beq.w _rb_serror ;no block size: nothing can be sized + move.l #$7fffffff,d0 + UDIVMOD32 ;most sectors whose byte count stays positive + tst.l d0 + beq.w _rb_serror ;one sector already overflows a request + move.l d0,d6 ;the block size cannot change under the loop _rb_scsi: moveq.l #0,d4 - not.w d4 + not.w d4 ;CDB count field is 16 bits + cmp.l d6,d4 + bls.s _rb_cap + move.l d6,d4 ;overflow limit is the tighter one +_rb_cap: cmp.l d4,d3 bcc.s _rb_s1 move.l d3,d4 _rb_s1: - lea CFU_SCSIStruct(a3),a0 - move.l a2,(a0)+ ;SCSI_Data - move.l d4,d0 - lsl.l #8,d0 - add.l d0,d0 ;*2 via add.l (faster than lsl.l #1) - move.l d0,(a0)+ ;SCSI_Length - clr.l (a0)+ ;SCSI_Actual - lea CFU_Packet(a3),a1 - move.l a1,(a0)+ ;SCSI_Command - moveq.l #10,d0 - swap d0 - move.l d0,(a0)+ ;SCSI_CmdLength, _CmdActual - move.w #SCSIF_READ<<8,(a0) ;SCSI_Flags, _Status - move.w #READ10<<8,(a1)+ - move.l d2,(a1)+ - move.l d4,d0 - lsl.l #8,d0 - move.l d0,(a1) - lea CFU_SCSIStruct(a3),a0 + INIT_SCSI_RW SCSIF_READ,READ10 bsr _Packet tst.b d0 bne.s _rb_serror move.l CFU_SCSIStruct+SCSI_Actual(a3),d0 - add.l d0,a2 - lsr.l #8,d0 - lsr.l #1,d0 + move.l d0,d4 ;bytes moved, for the buffer + move.l CFU_BlockSize(a3),d1 + beq.s _rb_serror ;no block size: cannot account, and divu would trap + UDIVMOD32 ;d0 = sectors, d1 = bytes left over + tst.l d1 + bne.s _rb_serror ;partial sector: accounting would drift + tst.l d0 + beq.s _rb_serror ;no progress would loop for ever + add.l d4,a2 + add.l d0,d2 ;next chunk starts after this one sub.l d0,d3 - bgt.s _rb_scsi + bgt.w _rb_scsi bra.w _rb_ready _rb_serror: @@ -6008,19 +6080,20 @@ wb_handler_tab: ; ; Input: ; d0 = starting block number (LBA) -; d1 = byte count to write +; d1 = sector count to write ; a0 = source buffer pointer, or -1 for erase (zero-fill) ; a3 = CFU pointer ; ; Output: -; d0 = bytes actually written (0 on error) +; d0 = sectors actually written, which after an error is what completed +; before it; the code is in CFU_IOErr ; ; Register usage: ; d2 = current block number -; d3 = bytes remaining -; d4 = bytes per transfer chunk -; d5 = total bytes (for return value) -; d6 = sectors per interrupt +; d3 = sectors remaining +; d4 = sectors per transfer chunk +; d5 = total sectors (for return value) +; d6 = sectors per interrupt (IDE) / sector cap per chunk (ATAPI) ; d7 = MOVEM loop counter ; a2 = buffer pointer ; @@ -6043,7 +6116,7 @@ _WriteBlocks: ifd ATAPI tst.w CFU_PLength(a3) - bgt.w _wb_scsi + bgt.w _wb_satapi endc bsr _IDEStart @@ -6229,47 +6302,48 @@ _wb_break: bra.w _wb_try ifd ATAPI -_wb_scsi: +_wb_satapi: move.l a2,d1 add.l #1,d1 beq.w _wb_eerror ;erase is IDE only + move.l CFU_BlockSize(a3),d1 + beq.w _wb_serror ;no block size: nothing can be sized + move.l #$7fffffff,d0 + UDIVMOD32 ;most sectors whose byte count stays positive + tst.l d0 + beq.w _wb_serror ;one sector already overflows a request + move.l d0,d6 ;the block size cannot change under the loop +_wb_scsi: moveq.l #0,d4 - not.w d4 + not.w d4 ;CDB count field is 16 bits + cmp.l d6,d4 + bls.s _wb_cap + move.l d6,d4 ;overflow limit is the tighter one +_wb_cap: cmp.l d4,d3 bcc.s _wb_s1 move.l d3,d4 _wb_s1: - lea CFU_SCSIStruct(a3),a0 - move.l a2,(a0)+ ;SCSI_Data - move.l d4,d0 - lsl.l #8,d0 - add.l d0,d0 ;*2 via add.l (faster than lsl.l #1) - move.l d0,(a0)+ ;SCSI_Length - clr.l (a0)+ ;SCSI_Actual - lea CFU_Packet(a3),a1 - move.l a1,(a0)+ ;SCSI_Command - moveq.l #10,d0 - swap d0 - move.l d0,(a0)+ ;SCSI_CmdLength, _CmdActual - move.w #SCSIF_WRITE<<8,(a0) ;SCSI_Flags, _Status - move.w #WRITE10<<8,(a1)+ - move.l d2,(a1)+ - move.l d4,d0 - lsl.l #8,d0 - move.l d0,(a1) - lea CFU_SCSIStruct(a3),a0 + INIT_SCSI_RW SCSIF_WRITE,WRITE10 bsr _Packet tst.b d0 bne.s _wb_serror move.l CFU_SCSIStruct+SCSI_Actual(a3),d0 - add.l d0,a2 - lsr.l #8,d0 - lsr.l #1,d0 + move.l d0,d4 ;bytes moved, for the buffer + move.l CFU_BlockSize(a3),d1 + beq.s _wb_serror ;no block size: cannot account, and divu would trap + UDIVMOD32 ;d0 = sectors, d1 = bytes left over + tst.l d1 + bne.s _wb_serror ;partial sector: accounting would drift + tst.l d0 + beq.s _wb_serror ;no progress would loop for ever + add.l d4,a2 + add.l d0,d2 ;next chunk starts after this one sub.l d0,d3 - bgt.s _wb_scsi + bgt.w _wb_scsi bra.w _wb_ready _wb_serror: diff --git a/src/cfd_automount_version.i b/src/cfd_automount_version.i index 5ddbfe6..d96b8eb 100644 --- a/src/cfd_automount_version.i +++ b/src/cfd_automount_version.i @@ -2,5 +2,5 @@ FILE_VERSION = 2 FILE_REVISION = 0 VERSION_STRING macro - dc.b "compactflash.automount 2.0-dev (27.08.2026)" + dc.b "compactflash.automount 2.0 (27.08.2026)" endm diff --git a/src/cfd_version.i b/src/cfd_version.i index ce06cde..f3d877a 100644 --- a/src/cfd_version.i +++ b/src/cfd_version.i @@ -3,8 +3,8 @@ FILE_VERSION = 2 FILE_REVISION = 0 VERSION_STRING macro ifd __68020__ - dc.b "compactflash.device 2.0-dev (27.08.2026) [68020]" + dc.b "compactflash.device 2.0 (11.09.2026) [68020]" else - dc.b "compactflash.device 2.0-dev (27.08.2026) [68000]" + dc.b "compactflash.device 2.0 (11.09.2026) [68000]" endc endm diff --git a/src/cfinfo.c b/src/cfinfo.c index 79a9d5b..9fc811e 100644 --- a/src/cfinfo.c +++ b/src/cfinfo.c @@ -22,6 +22,9 @@ const char version[] = MAKE_VERSION_STRING("CFInfo"); #include #include +#include + +#include #include #include #include @@ -87,6 +90,121 @@ UBYTE *scsi_sense = NULL; struct SCSICmd *scsi_cmd = NULL; UBYTE scsi_cdb[12]; +/* --- console pager ------------------------------------------------- + * + * One screenful at a time: any key continues, Q stops the rest of the + * report. Paging is on only when input and output are the same console, + * and only when that console reports a plausible height; a redirected + * or silent stream scrolls as before. + */ +#define PAGE_PROMPT "-- more -- (any key, Q quits)" + +static BPTR page_in, page_out; +static int page_rows; /* usable lines per page, 0 = no paging */ +static int page_left; +static int page_stop; /* the reader asked to stop */ +static char page_erase[sizeof(PAGE_PROMPT) + 1]; /* CR, blanks, CR */ + +/* Ask the console how tall its window is. RAW mode must already be set. + * WINDOW STATUS REQUEST is answered by a WINDOW BOUNDS REPORT, + * CSI ;;; SP r, whose third field is the height. + */ +static int page_query_rows(void) +{ + int fields = 0, num = 0, rows = 0, guard = 40; + UBYTE c; + + Write(page_out, "\033[ q", 4); + while (guard--) { + if (!WaitForChar(page_in, 200000)) return 0; /* console stayed silent */ + if (Read(page_in, &c, 1) != 1) return 0; + if (c == 'r') return (fields >= 3) ? rows : 0; /* too short to trust */ + if (c == ';') { + if (++fields == 3) rows = num; + num = 0; + continue; + } + if (c < '0' || c > '9') continue; + if (num < 1000) num = num * 10 + (c - '0'); /* absurd: stop adding */ + } + return 0; +} + +static void page_begin(void) +{ + struct FileHandle *in, *out; + int rows; + + page_rows = page_left = page_stop = 0; + page_in = Input(); + page_out = Output(); + if (!page_in || !page_out) return; + if (!IsInteractive(page_in) || !IsInteractive(page_out)) return; + + /* The query is written to the output and answered on the input, so both + * must be the same handler. Equal fh_Type means one console; a redirected + * ">SER:" is a different port and gets neither the query nor a pause. + */ + in = (struct FileHandle *)BADDR(page_in); + out = (struct FileHandle *)BADDR(page_out); + if (in->fh_Type != out->fh_Type) return; + + SetMode(page_in, 1); /* RAW for the query and every keypress */ + rows = page_query_rows() - 1; /* the prompt needs a line of its own */ + if (rows < 4 || rows > 200) { /* implausible height, do not guess */ + SetMode(page_in, 0); + return; + } + page_rows = page_left = rows; + + page_erase[0] = '\r'; + memset(page_erase + 1, ' ', sizeof(PAGE_PROMPT) - 1); + page_erase[sizeof(PAGE_PROMPT)] = '\r'; +} + +static void page_end(void) +{ + if (!page_rows) return; + page_rows = 0; + SetMode(page_in, 0); /* always hand the shell back cooked */ +} + +/* Call after writing one line. Returns 0 when the reader asked to stop. */ +static int page_line(void) +{ + UBYTE key = '\n'; + + if (!page_rows) return 1; + if (--page_left > 0) return 1; + + fflush(stdout); /* the prompt bypasses stdio */ + Write(page_out, PAGE_PROMPT, sizeof(PAGE_PROMPT) - 1); + if (Read(page_in, &key, 1) != 1) key = '\n'; + Write(page_out, page_erase, sizeof(page_erase)); + page_left = page_rows; /* a fresh page */ + if ((key & 0xdf) == 'Q') { /* fold case */ + page_stop = 1; + return 0; + } + return 1; +} + +/* printf that keeps the page accounting. No format here spans two lines, + * so one newline in the format is one line on screen. + */ +static void pout(const char *fmt, ...) +{ + va_list ap; + const char *c; + + if (page_stop) return; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + for (c = fmt; *c; c++) + if (*c == '\n' && !page_line()) return; +} + /* Extract and clean a string from IDENTIFY data */ /* Note: On 68k (big-endian), ATA strings are already in correct byte order */ void GetIDString(UWORD *id_data, int start_word, int num_words, char *dest) @@ -173,13 +291,13 @@ void PrintSize(ULONG sectors) ULONG gb = mb / 1024; if (gb > 0) { - printf("%lu.%lu GB", gb, (mb % 1024) * 10 / 1024); + pout("%lu.%lu GB", gb, (mb % 1024) * 10 / 1024); } else if (mb > 0) { - printf("%lu.%lu MB", mb, (kb % 1024) * 10 / 1024); + pout("%lu.%lu MB", mb, (kb % 1024) * 10 / 1024); } else { - printf("%lu KB", kb); + pout("%lu KB", kb); } - printf(" (%lu sectors)\r\n", sectors); + pout(" (%lu sectors)\n", sectors); } /* Print card information from IDENTIFY data */ @@ -199,109 +317,109 @@ void PrintCardInfo(UWORD *id) multi = id[ID_MAXMULTI] & 0xFF; sectors = GetIDLong(id, ID_LBA_SECTORS); - printf("\r\n"); - printf("=== CompactFlash Card Information ===\r\n"); - printf("\r\n"); - printf("Model: %s\r\n", model); - printf("Serial: %s\r\n", serial); - printf("Firmware: %s\r\n", firmware); - printf("\r\n"); + pout("\n"); + pout("=== CompactFlash Card Information ===\n"); + pout("\n"); + pout("Model: %s\n", model); + pout("Serial: %s\n", serial); + pout("Firmware: %s\n", firmware); + pout("\n"); - printf("=== Capacity ===\r\n"); - printf("Size: "); + pout("=== Capacity ===\n"); + pout("Size: "); PrintSize(sectors); - printf("Geometry: %u cyl, %u heads, %u sectors/track\r\n", + pout("Geometry: %u cyl, %u heads, %u sectors/track\n", id[ID_CYLS], id[ID_HEADS], id[ID_SECTORS]); - printf("\r\n"); + pout("\n"); - printf("=== Capabilities ===\r\n"); - printf("LBA: %s\r\n", (caps & 0x0200) ? "Yes" : "No"); - printf("DMA: %s\r\n", (caps & 0x0100) ? "Yes" : "No"); + pout("=== Capabilities ===\n"); + pout("LBA: %s\n", (caps & 0x0200) ? "Yes" : "No"); + pout("DMA: %s\n", (caps & 0x0100) ? "Yes" : "No"); /* PIO modes */ - printf("PIO Modes: 0"); - if (id[ID_PIO_OLD] >= 1) printf(", 1"); - if (id[ID_PIO_OLD] >= 2) printf(", 2"); - if (pio_modes & 0x01) printf(", 3"); - if (pio_modes & 0x02) printf(", 4"); - printf("\r\n"); + pout("PIO Modes: 0"); + if (id[ID_PIO_OLD] >= 1) pout(", 1"); + if (id[ID_PIO_OLD] >= 2) pout(", 2"); + if (pio_modes & 0x01) pout(", 3"); + if (pio_modes & 0x02) pout(", 4"); + pout("\n"); /* Multi-sector */ if (multi > 0) { - printf("Multi-sect: Max %u sectors/interrupt\r\n", multi); + pout("Multi-sect: Max %u sectors/interrupt\n", multi); } else { - printf("Multi-sect: Not supported\r\n"); + pout("Multi-sect: Not supported\n"); } /* UDMA modes */ if (id[ID_UDMA_MODES] != 0) { - printf("UDMA Modes: "); - if (id[ID_UDMA_MODES] & 0x01) printf("0 "); - if (id[ID_UDMA_MODES] & 0x02) printf("1 "); - if (id[ID_UDMA_MODES] & 0x04) printf("2 "); - if (id[ID_UDMA_MODES] & 0x08) printf("3 "); - if (id[ID_UDMA_MODES] & 0x10) printf("4 "); - if (id[ID_UDMA_MODES] & 0x20) printf("5 "); - if (id[ID_UDMA_MODES] & 0x40) printf("6 "); - printf("\r\n"); + pout("UDMA Modes: "); + if (id[ID_UDMA_MODES] & 0x01) pout("0 "); + if (id[ID_UDMA_MODES] & 0x02) pout("1 "); + if (id[ID_UDMA_MODES] & 0x04) pout("2 "); + if (id[ID_UDMA_MODES] & 0x08) pout("3 "); + if (id[ID_UDMA_MODES] & 0x10) pout("4 "); + if (id[ID_UDMA_MODES] & 0x20) pout("5 "); + if (id[ID_UDMA_MODES] & 0x40) pout("6 "); + pout("\n"); } - printf("\r\n"); - printf("=== Card Type ===\r\n"); - printf("Removable: %s\r\n", (config & 0x0080) ? "Yes" : "No"); - printf("Type: "); + pout("\n"); + pout("=== Card Type ===\n"); + pout("Removable: %s\n", (config & 0x0080) ? "Yes" : "No"); + pout("Type: "); /* Check for CompactFlash signature (0x848x) */ if ((config & 0xFFF0) == 0x8480) { - printf("CompactFlash\r\n"); + pout("CompactFlash\n"); } else if ((config & 0x8000) == 0) { - printf("ATA\r\n"); + pout("ATA\n"); } else { - printf("ATAPI\r\n"); + pout("ATAPI\n"); } /* Command Sets / Features */ - printf("\r\n"); - printf("=== Features (SET FEATURES capable) ===\r\n"); + pout("\n"); + pout("=== Features (SET FEATURES capable) ===\n"); if (id[ID_CMD_SET1] || id[ID_CMD_SET2]) { UWORD cmd1 = id[ID_CMD_SET1]; UWORD cmd2 = id[ID_CMD_SET2]; UWORD en1 = id[ID_CMD_EN1]; UWORD en2 = id[ID_CMD_EN2]; - printf(" Supported Enabled\r\n"); + pout(" Supported Enabled\n"); /* Word 82 bits */ if (cmd1 & 0x0020) - printf("Write Cache: Yes %s\r\n", (en1 & 0x0020) ? "Yes" : "No"); + pout("Write Cache: Yes %s\n", (en1 & 0x0020) ? "Yes" : "No"); if (cmd1 & 0x0040) - printf("Read Look-ahead: Yes %s\r\n", (en1 & 0x0040) ? "Yes" : "No"); + pout("Read Look-ahead: Yes %s\n", (en1 & 0x0040) ? "Yes" : "No"); if (cmd1 & 0x0008) - printf("Power Management: Yes %s\r\n", (en1 & 0x0008) ? "Yes" : "No"); + pout("Power Management: Yes %s\n", (en1 & 0x0008) ? "Yes" : "No"); if (cmd1 & 0x0004) - printf("Security Mode: Yes %s\r\n", (en1 & 0x0004) ? "Yes" : "No"); + pout("Security Mode: Yes %s\n", (en1 & 0x0004) ? "Yes" : "No"); if (cmd1 & 0x0001) - printf("SMART: Yes %s\r\n", (en1 & 0x0001) ? "Yes" : "No"); + pout("SMART: Yes %s\n", (en1 & 0x0001) ? "Yes" : "No"); /* Word 83 bits */ if (cmd2 & 0x0400) - printf("48-bit LBA: Yes %s\r\n", (en2 & 0x0400) ? "Yes" : "No"); + pout("48-bit LBA: Yes %s\n", (en2 & 0x0400) ? "Yes" : "No"); if (cmd2 & 0x1000) - printf("Write FUA: Yes %s\r\n", (en2 & 0x1000) ? "Yes" : "No"); + pout("Write FUA: Yes %s\n", (en2 & 0x1000) ? "Yes" : "No"); if (cmd2 & 0x0020) - printf("PUIS: Yes %s\r\n", (en2 & 0x0020) ? "Yes" : "No"); + pout("PUIS: Yes %s\n", (en2 & 0x0020) ? "Yes" : "No"); if (cmd2 & 0x0008) - printf("APM: Yes %s\r\n", (en2 & 0x0008) ? "Yes" : "No"); + pout("APM: Yes %s\n", (en2 & 0x0008) ? "Yes" : "No"); /* CFA specific */ if (cmd2 & 0x4000) - printf("CFA Features: Yes\r\n"); + pout("CFA Features: Yes\n"); } else { - printf("(not reported by card)\r\n"); + pout("(not reported by card)\n"); } /* CF Advanced True IDE Timing (Word 163) */ - printf("\r\n"); - printf("=== CF True IDE Timing (Word 163) ===\r\n"); + pout("\n"); + pout("=== CF True IDE Timing (Word 163) ===\n"); if (id[ID_CFA_IDE] & 0x8000) { UWORD ide = id[ID_CFA_IDE]; UWORD pio_no_iordy = ide & 0x07; @@ -311,19 +429,19 @@ void PrintCardInfo(UWORD *id) /* PIO mode to cycle time lookup */ static const char *pio_ns[] = {"600", "383", "240", "180", "120", "100", "80", "?"}; - printf("PIO (no IORDY): max=%u (%sns cycle)\r\n", + pout("PIO (no IORDY): max=%u (%sns cycle)\n", (unsigned)pio_no_iordy, pio_ns[pio_no_iordy]); - printf("PIO (IORDY): max=%u (%sns cycle)\r\n", + pout("PIO (IORDY): max=%u (%sns cycle)\n", (unsigned)pio_iordy, pio_ns[pio_iordy]); if (mdma_max > 0) - printf("Multiword DMA: max=%u\r\n", (unsigned)mdma_max); + pout("Multiword DMA: max=%u\n", (unsigned)mdma_max); } else { - printf("(not reported by card)\r\n"); + pout("(not reported by card)\n"); } /* CF Advanced PCMCIA Timing (Word 164) */ - printf("\r\n"); - printf("=== CF PCMCIA Timing (Word 164) ===\r\n"); + pout("\n"); + pout("=== CF PCMCIA Timing (Word 164) ===\n"); if (id[ID_CFA_TIMING] & 0x8000) { UWORD timing = id[ID_CFA_TIMING]; UWORD mem_max = timing & 0x07; @@ -334,19 +452,19 @@ void PrintCardInfo(UWORD *id) /* Timing mode to nanoseconds lookup (modes 6-7 are vendor-specific) */ static const char *mode_ns[] = {"600", "250", "150", "100", "80", "50", "?", "?"}; - printf("Memory Mode: max=%u (%sns), current=%u (%sns)\r\n", + pout("Memory Mode: max=%u (%sns), current=%u (%sns)\n", (unsigned)mem_max, mode_ns[mem_max], (unsigned)mem_cur, mode_ns[mem_cur]); - printf("I/O Mode: max=%u (%sns), current=%u (%sns)\r\n", + pout("I/O Mode: max=%u (%sns), current=%u (%sns)\n", (unsigned)io_max, mode_ns[io_max], (unsigned)io_cur, mode_ns[io_cur]); } else { - printf("(not reported by card)\r\n"); + pout("(not reported by card)\n"); } /* Gayle timing register - actual hardware setting */ - printf("\r\n"); - printf("=== Gayle Timing (hardware) ===\r\n"); + pout("\n"); + pout("=== Gayle Timing (hardware) ===\n"); { volatile UBYTE *gayle = (volatile UBYTE *)0x00DAB000; UBYTE reg_val = *gayle; @@ -361,30 +479,30 @@ void PrintCardInfo(UWORD *id) case 3: gayle_ns = "720"; break; default: gayle_ns = "?"; break; } - printf("Memory Speed: %sns\r\n", gayle_ns); + pout("Memory Speed: %sns\n", gayle_ns); } } /* Print driver configuration */ void PrintDriverConfig(struct CFDConfig *cfg) { - printf("\r\n"); - printf("=== Driver Configuration ===\r\n"); - printf("Driver Ver: %u.%u\r\n", cfg->version_major, cfg->version_minor); - printf("Mount Flags: %u (", cfg->open_flags); + pout("\n"); + pout("=== Driver Configuration ===\n"); + pout("Driver Ver: %u.%u\n", cfg->version_major, cfg->version_minor); + pout("Mount Flags: %u (", cfg->open_flags); if (cfg->open_flags == 0) { - printf("none"); + pout("none"); } else { int first = 1; - if (cfg->open_flags & 1) { printf("%scfd_first", first ? "" : ", "); first = 0; } - if (cfg->open_flags & 2) { printf("%sskip_sig", first ? "" : ", "); first = 0; } - if (cfg->open_flags & 4) { printf("%scompat", first ? "" : ", "); first = 0; } - if (cfg->open_flags & 8) { printf("%sserial_debug", first ? "" : ", "); first = 0; } - if (cfg->open_flags & 16) { printf("%sforce_multi", first ? "" : ", "); first = 0; } - if (cfg->open_flags & 32) { printf("%sno_autodetect", first ? "" : ", "); first = 0; } + if (cfg->open_flags & 1) { pout("%scfd_first", first ? "" : ", "); first = 0; } + if (cfg->open_flags & 2) { pout("%sskip_sig", first ? "" : ", "); first = 0; } + if (cfg->open_flags & 4) { pout("%scompat", first ? "" : ", "); first = 0; } + if (cfg->open_flags & 8) { pout("%sserial_debug", first ? "" : ", "); first = 0; } + if (cfg->open_flags & 16) { pout("%sforce_multi", first ? "" : ", "); first = 0; } + if (cfg->open_flags & 32) { pout("%sno_autodetect", first ? "" : ", "); first = 0; } } - printf(")\r\n"); - printf("Multi-sect: FW=%u, Used=%u\r\n", cfg->multi_size, cfg->multi_size_rw); + pout(")\n"); + pout("Multi-sect: FW=%u, Used=%u\n", cfg->multi_size, cfg->multi_size_rw); /* Transfer modes - combined R/W display */ { @@ -409,12 +527,13 @@ void PrintDriverConfig(struct CFDConfig *cfg) default: sprintf(write_buf, "mode %u", cfg->write_mode); write_mode = write_buf; break; } - printf("R/W Mode: %s/%s\r\n", read_mode, write_mode); + pout("R/W Mode: %s/%s\n", read_mode, write_mode); } } void Cleanup(void) { + page_end(); if (io) { if (io->io_Device) { CloseDevice((struct IORequest *)io); @@ -431,7 +550,9 @@ int main(int argc, char **argv) { int unit = 0; - printf("CFInfo " STR(VERSION) " - CompactFlash Card Information\r\n"); + page_begin(); + + pout("CFInfo " STR(VERSION) " - CompactFlash Card Information\n"); /* Parse arguments */ if (argc > 1) { @@ -441,13 +562,14 @@ int main(int argc, char **argv) /* Allocate resources */ mp = CreateMsgPort(); if (!mp) { - printf("Error: Cannot create message port\r\n"); + pout("Error: Cannot create message port\n"); + Cleanup(); /* the console must not stay RAW */ return 10; } io = (struct IOStdReq *)CreateIORequest(mp, sizeof(struct IOStdReq)); if (!io) { - printf("Error: Cannot create IO request\r\n"); + pout("Error: Cannot create IO request\n"); Cleanup(); return 10; } @@ -457,32 +579,32 @@ int main(int argc, char **argv) scsi_cmd = AllocMem(sizeof(struct SCSICmd), MEMF_PUBLIC | MEMF_CLEAR); if (!data_buf || !scsi_sense || !scsi_cmd) { - printf("Error: Cannot allocate memory\r\n"); + pout("Error: Cannot allocate memory\n"); Cleanup(); return 10; } /* Open device */ if (OpenDevice(DEVICE_NAME, unit, (struct IORequest *)io, 0) != 0) { - printf("Error: Cannot open %s unit %d\r\n", DEVICE_NAME, unit); - printf(" (Is a CF card inserted?)\r\n"); + pout("Error: Cannot open %s unit %d\n", DEVICE_NAME, unit); + pout(" (Is a CF card inserted?)\n"); Cleanup(); return 5; } - printf("Device: %s unit %d\r\n", DEVICE_NAME, unit); + pout("Device: %s unit %d\n", DEVICE_NAME, unit); /* Test unit ready */ if (!DoTestUnitReady()) { - printf("Error: Card not ready\r\n"); + pout("Error: Card not ready\n"); Cleanup(); return 5; } /* Get IDENTIFY data via ATA passthrough (v1.36+) */ if (!DoATAIdentify()) { - printf("Error: IDENTIFY command failed\r\n"); - printf(" (Requires compactflash.device v1.36+)\r\n"); + pout("Error: IDENTIFY command failed\n"); + pout(" (Requires compactflash.device v1.36+)\n"); Cleanup(); return 5; }