Generating CRC32 checksums for every file under Human68k
The X68000 community is small but unusually persistent, and one of the recurring chores in maintaining a software collection is checking that the bytes on your hard disk are still the bytes you wrote a decade ago. Between flaky power supplies, ageing SCSI cables, and the occasional swap of an original CZ-601CE motherboard for a later revision, it is easy to end up with a hard disk that quietly drifts away from the community archives. I found myself wanting a small, native Human68k tool that would walk a directory tree, compute a CRC32 for every regular file, and print the result in a form I could compare against the .crc files that already circulate among collectors in Sydney, Brisbane, and elsewhere.
Writing that tool turned into a useful weekend project, both for the practical checksum workflow and for the deeper tour of Human68k's DOS layer that it forced on me. This article walks through the design choices, the parts of the Human68k interface that matter, the CRC32 algorithm itself, and the small set of habits that have made the tool part of my regular maintenance routine for the X68000 machines that live on my workbench.
Why CRC32 and not something stronger
A reasonable first question is why I picked CRC32 at all, when MD5, SHA-1, and even SHA-256 are well known and freely available. The honest answer is that CRC32 is what most of the existing X68000 disk images on community FTP mirrors already use, so the format matches checksum files that have circulated for years. If I redistribute a freshly imaged Human68k install disk and ship a matching .crc file alongside it, anyone with the same tool can verify their copy with no conversion step in between.
CRC32 is also extremely cheap to compute. On an X68000 running at 10 MHz, a tight C implementation can hash a floppy's worth of data in well under a minute, which matters when you are running the tool interactively rather than overnight on a workstation. Cryptographic hashes are intentionally slow, which is the opposite of what I want when I just want to know whether the contents of two .XDF images match. For genuine tamper detection or archival-grade integrity, I would reach for SHA-256 every time, but for catching bit rot and transfer errors in a hobbyist workflow, CRC32 sits at exactly the right point on the cost-versus-confidence curve.
There is also a cultural fit. The X68000 ecosystem grew up around small, single-purpose utilities that did one thing well, and a tool that prints CRC32 values fits that aesthetic. The same philosophy appears in projects such as the controller-based MIDI interface that turns the machine into a sequenced instrument, where a lean piece of code leans on the existing hardware to do the heavy lifting. Both lean on the same principles: keep dependencies thin, do one thing well, and let the user string simple tools together.
Assembling a modern-ish toolchain for Human68k
Human68k is a real operating system in the DOS family, with its own executable format (.X and .R files), its own system call interface, and its own small set of quirks. The most reliable way I have found to produce code for it from a contemporary desktop is to cross-compile with GCC targeting m68k and then ship the resulting .X binary over to the real machine via the X68000's own networking stack or a null-modem cable. On the development side I tend to run my build host from a home office in Melbourne, where the NBN connection is fast enough to pull toolchain updates without ceremony, and the actual testing happens on a CZ-601CE sitting on the workbench behind me.
The runtime side is intentionally minimal. Human68k exposes its DOS functions through a software interrupt (trap #15 in most configurations), and the relevant ones for this project are the file open, read, close, and directory enumeration calls. The header translations have been stable since the late 1980s, which means code written for Human68k 3.0 still works on Human68k 4.x. I keep a small header file with the structures and constants I need so that I am not redefining them every project. The same approach scaled nicely when I was experimenting with the USB keyboard adapter, where a shared header file made the device descriptor handling much easier to reason about.
I also lean on the X68000's IOCS calls for console I/O rather than building my own ANSI sequences, because the system already knows how to talk to the keyboard controller, the screen, and the printer port. Wrapping those calls in a couple of putchar-style helpers means the rest of the program looks like ordinary C, which is what you want when you are iterating on a checksum tool that will be edited many times over the years.
Walking the filesystem safely
The interesting part of the tool is the part that walks the filesystem. Human68k uses a fairly conventional FAT-style layout, and the DOS calls FILES, NFILES, and IOCTRL let you enumerate the entries in a directory one by one. My implementation takes a starting path on the command line, appends \*.* if no wildcard is given, and recurses through every subdirectory it finds, skipping . and .. to avoid loops.
One trap I did not anticipate until I ran the tool on a friend's hard drive is that Human68k allows file names that include lowercase letters, but the underlying FAT layer may store them as uppercase only. This means that if you build a search list and the comparison is case-sensitive, you can end up hashing the same file twice under two different names. The fix is a small normalisation step that folds names to uppercase before adding them to the work queue, which has the side benefit of matching the convention used by most existing .crc files in the community archives.
Another subtlety is the handling of read-only files, hidden files, and system files. By default, the tool processes everything it can open for reading, which means system files like COMMAND.X and STARTUP.X end up in the checksum list. That is the behaviour I want for archival verification, but it is worth a comment in the code so that the next person who reads it knows it was intentional. Errors are reported to the standard error stream in a compact path: error format, so a user can pipe the output through a tool like grep on the X68000 itself.
The CRC32 algorithm in detail
The core of the program is a table-driven CRC32 implementation that processes one byte at a time. I chose the polynomial 0xEDB88320, which is the reversed form of 0x04C11DB7, the polynomial used by Ethernet, ZIP, PNG, and most of the existing .crc files in the X68000 scene. Using the same polynomial means my output can be compared directly with checksums generated by cksum, 7-Zip, and similar tools on other platforms without any conversion.
Performance comes from precomputing a 256-entry table of CRC32 values for every possible byte. Once that table is built at startup, each input byte contributes exactly one table lookup, one XOR, and one shift of an accumulator. On a 10 MHz 68000 this is still noticeably slower than a hand-written assembly version, but it is fast enough to keep up with the floppy drive during real verification work. For users on faster accelerator boards, the bottleneck shifts to the disk controller long before the CRC computation becomes a problem.
Initialising the running CRC to 0xFFFFFFFF and finally XORing the result with 0xFFFFFFFF is the part that often trips people up, because half the tutorials on the web quietly leave one of those steps out and end up with values that look almost right but never match anything. I added a small assertion against a known input ("123456789" should yield 0xCBF43926) that runs in debug builds, so a future change to the algorithm cannot silently produce wrong output. That single test has caught more regressions than I would like to admit, particularly during refactors that touched the byte-ordering code.
Choosing an output format that compares well
For output, the tool prints one line per file in a format inspired by the GNU coreutils style, but trimmed for an 80-column screen: an eight-character hex CRC32, two spaces, and the relative path of the file. This is similar in spirit to other common hash formats, and it stays readable when you redirect the output to a file and then diff two captures taken at different times.
Below is a quick comparison of the formats I considered, showing their typical width per file and how well they match existing tooling in the X68000 ecosystem.
| Format | Example line | Matches existing X68k .crc files? |
Sortable on Human68k? |
|---|---|---|---|
GNU cksum style |
12345678 GAME.XDF |
Yes | Yes |
md5sum style |
e10adc3949ba59abbe56e057f20f883e GAME.XDF |
No (MD5, not CRC32) | Yes |
BSD md5 style |
MD5 (GAME.XDF) = e10a... |
No | No |
| Bare hex + path | 0xCBF43926 GAME.XDF |
No | Yes |
The first row is the format my tool actually emits, because it produces checksums that can be diffed against community-archived .crc files, fits within 80 columns even with long Human68k paths, and stays greppable when piped through the X68000's own command shell. The other rows show formats I considered and rejected, mostly because they either used a different algorithm or wrapped the filename in a way that broke sort and diff workflows.
Lessons that only show up on real hardware
After running the tool across my entire archive for several months, a few habits have become second nature. The first is that I always run a verification pass immediately after copying files onto a fresh SCSI disk. Power supplies on machines this old are unpredictable, and a partial write failure is far easier to catch with a checksum tool than by trying to load every program by hand. A handful of retro computing meetups in Sydney and a couple of smaller gatherings in Adelaide now keep a copy of the tool on a shared utility disk for exactly this reason.
The second lesson is that progress reporting matters more on a 10 MHz machine than I expected. Without a counter that prints "hashed N files so far", the tool feels broken when it is actually just chewing through a directory of a thousand small files. I added a simple counter that flushes to the console every fifty files, which keeps the user informed without flooding the screen.
The third lesson is that it pays to keep the source portable between Human68k and a modern Unix-like host. I have a small #ifdef block that lets the same C file compile on Linux for testing and then cross-compile cleanly for the X68000. This means I can regression-test the CRC32 logic on my laptop before every change, without needing to boot the actual machine. It also makes it easier to share the source with other hobbyists around Australia and overseas, since most of them have at least a basic Linux or BSD setup they can use for build verification before they try the binary on real hardware.
If you maintain a collection of X68000 software, a small CRC32 utility is one of the most useful weekend projects you can ship. It costs an afternoon to write, it teaches you about Human68k's DOS interface, and it gives you a tool you will reach for every time you image a disk, replace a hard drive, or recover from a power supply hiccup. Pull the source, build it for your own machine, run it across your archive, and share the resulting .crc file alongside your disk images so the rest of the community can verify their copies too.
Nereid-X Expansion Board
A personally-produced LAN+USB+Memory expansion board for Sharp X68000 series computers. Multiple production runs were offered, including a final batch and a later revival reproduction run.
Power Supply Repair
X68 power supply repair and modification services were offered by the site owner, with documentation shared through diary entries spanning 2001–2006.
Server & Networking
Notes on FreeBSD administration, ISP changes, server migration, and networking topics. The site itself ran on FreeBSD with the hns diary system and Namazu search integration.
Get in touch
X68K.NET connects Sharp X68000 enthusiasts through community links and shared projects. Reach out with questions about the Nereid project or X68 resources.