Building a Human68k utility to unpack LZSS-compressed game files

The Sharp X68000 remains one of the most elegant personal computers ever shipped in Japan, and a small but determined scene across Australia keeps the platform alive. When a collector in Melbourne picked up a boxed vertical shooter from a car-boot sale in Brunswick, the discs would not spin reliably, and a fresh belt became the only path to reading what was on them. That salvage mission is exactly what makes writing decompression tools worth the effort: every format that can be unlocked is another title pulled back from digital oblivion.

Many X68000 games shipped their graphics, music, and map data through custom LZSS routines that squeezed kilobytes onto single-density media. Emulators can run the original code, but preservation work needs the raw assets, and that means rebuilding the decompression path on the host operating system used by the machine itself. Human68k, the OS designed for the X68000 line, still hosts tools that target this environment, and a compact utility for unpacking LZSS streams fits naturally into that ecosystem.

This walkthrough follows the process of building such a tool from scratch, starting with the algorithm itself and ending with a binary that runs on real hardware or inside an emulator. Most of the work happens on a modern laptop, with only the final testing phase needing genuine vintage gear. Notes about local sourcing, climate-related storage concerns, and regional meetups are woven in for readers south of the equator.

The X68000 and Human68k in 2026

The X68000 walked a tightrope between arcade hardware and home computing, pairing a Motorola 68000 CPU with the same Yamaha sound chips that powered the YM2151 arcade scene. Human68k sat on top of that hardware as a compact command-driven environment with a structure that anyone familiar with MS-DOS or CP/M will recognise. Its interpreter accepted short verbs, its file system used 8.3 names, and its memory model treated the first megabyte as a flat playground for programs that knew what they were doing.

For a hobbyist tool, this matters in practical ways. Human68k expects programs to manage their own file buffers, return clean exit codes, and respect the small stack the OS hands out at startup. A decompression utility that wants to be polite will read in fixed-size chunks, write directly to the output handle, and refuse to crash when handed a truncated stream. Anyone writing one today should treat the original machine as the ultimate reference, even if development happens on a laptop in Adelaide or Perth.

The wider ecosystem around the platform is what keeps tools like this useful. Preservationists rely on dumps of original media, repair specialists keep donor boards alive, and coders port modern conveniences into a 1980s shell. Sites such as x68k.net act as a hub where stories of hardware revival and software archaeology sit side by side, and a fresh utility slots comfortably into that collection of community knowledge.

Why LZSS kept showing up on Japanese floppies

Lempel-Ziv-Storer-Szymanski compression is a sliding-window variant of the LZ family that excels at squeezing streams with lots of repeated patterns. Game data fits that profile neatly: tiles reuse bytes across a tilemap, sprite banks share colour tables, and dialogue text repeats short common phrases. A 4-kilobyte ring buffer with 4-bit length codes and 12-bit offsets shrinks a typical resource archive by forty or fifty percent without slowing the loader down on the original 68000.

Developers in the early 1990s did not just drop a stock library into their games. They tuned the window size, fiddled with literal-versus-match decisions, and sometimes pre-processed the data to bias it toward longer matches. Two games from the same publisher can use subtly different LZSS variants, and a generic decompressor needs to handle the common case gracefully while flagging the outliers. The code has to make space for parameters that the binary can read from the file or accept on the command line.

The algorithm also rewards being written close to the metal. Each match reference is a single 16-bit word that the CPU loads in one cycle, and the ring buffer fits comfortably in on-chip cache when sized appropriately. Code that branches predictably and avoids unnecessary memory loads outruns a naive port by a factor of three or four, which matters when an entire resource file has to be unpacked from a single-speed floppy.

Setting up a modern cross-compile environment

Building for the X68000 in 2026 looks surprisingly ordinary. A Debian or Fedora laptop with the GNU toolchain targeting m68k-elf, a copy of the Human68k system headers, and a small linker script is enough to produce a working command. The trickier piece is the runtime: Human68k expects programs to call _dos_init and to use the _dos trap layer for I/O, which means the tool should either link against the official IOCS stubs or call DOS function traps directly through inline assembly.

For day-to-day testing, the XM6 Type-G emulator handles real Human68k floppy images with high fidelity, and a developer in Brisbane can flip between editing source on a ThinkPad and verifying behaviour in an emulated session without leaving the desk. When the binary is mature enough, the same image can be copied onto a real machine through a Gotek floppy emulator or a SCSI2SD card, which makes the round-trip almost painless. Australian Post parcels have become a quiet lifeline for collectors who mail each other pre-flashed media.

Keeping the build reproducible is mostly about pinning toolchain versions and writing a small Makefile that exposes the obvious targets. A make all rule builds the binary, make test runs the decompressor against a known-good archive, and make clean removes intermediates. Adding a CI runner that cross-compiles on every push means a contributor in Hobart and one in Sydney can both send patches without worrying about diverging environments.

Reverse engineering a game's compression header

Every compressed asset begins with a header that tells the decompressor how to interpret the rest of the stream. Some games start with the four ASCII bytes "LZSS", followed by little-endian 16-bit words describing the window size, the minimum match length, and the offset to the first compressed block. Others bury the parameters at a fixed offset from a known tag string, and a few store the parameters inside the executable itself so the on-disk assets are essentially opaque without the loader.

The first step is always to open the suspected archive in a hex editor and look for a region of low entropy. A header tends to contain recognisable text, version numbers, and pointers that stand out from the dense pattern of a compressed payload. In a typical LZSS stream, a literal byte is encoded as a single bit followed by eight data bits, while a match is encoded as a reference plus a length. A quick script that decodes the bitstream and prints the first hundred literals usually confirms whether the format is the expected variant.

When the format turns out to be non-standard, the tool needs to expose parameters through a configuration file. A small JSON file that maps file extensions to known compression settings lets the utility handle several publishers without rebuilding from source. The same configuration file can store notes about which games the parameters were measured against, which makes future debugging much easier when someone reports that the tool produces the wrong output.

Writing the decompressor in C with a touch of assembly

A clean reference implementation of LZSS fits comfortably in about 150 lines of C. The core loop reads a flag byte, walks through eight bits, and for each one either emits a literal or follows a back-reference into the ring buffer. The buffer is a fixed-size array indexed by a write pointer that wraps around when it hits the end, and matches are looked up by reading a 16-bit word that packs the high offset bits with the low length bits.

Performance work starts where the obvious code stops being fast. The 68000 lacks a barrel shifter and struggles with bit-level arithmetic in high-level languages, so the inner loop benefits from being unrolled and from having its constants precomputed. A small assembly stub that consumes the flag byte, masks the bit, and dispatches to the literal or match path can cut the runtime roughly in half, which pays off when the tool processes dozens of files in a batch.

Memory layout matters on Human68k because the OS allocates a relatively small default stack. The decompressor should not recurse, should not allocate from the heap unless absolutely necessary, and should keep its ring buffer in the BSS section where it costs nothing at load time. Tools that misbehave on this front tend to work fine on the emulator and then crash on real hardware, which is a frustrating way to discover the difference.

Putting the tool through its paces on real archives

The first serious test case for any X68000 decompression tool is a known-good archive from a popular game. Titles like Akumajou Dracula, Choaniki, and several System Sacom shooters ship with documented resource formats, and a small archive from one of these games is enough to confirm that the basic pipeline works. The output should match byte-for-byte against a reference dump, and the tool should report the input and output sizes plus the achieved compression ratio.

The second class of test is the awkward case. Truncated files, archives with a corrupt header, and streams that use a non-standard window size all need to be handled without crashing. A well-behaved utility prints a clear error message, sets a non-zero exit code, and leaves no half-written output file behind. This is where Australian humidity becomes a quiet variable: floppies stored in a garden shed in Darwin or Cairns often suffer from bit rot that produces exactly the kind of corrupted header the tool needs to survive.

Once the binary passes those tests, it should be run on a real X68000 rather than only inside an emulator. Loading the tool from a Gotek-equipped machine, feeding it a compressed file copied onto the same virtual drive, and watching the output file appear on a second image is a satisfying moment. Anything that survives this loop is ready for distribution.

Packaging, documenting, and sharing with the community

A useful tool is not just a binary; it is also a manual page, a small set of example invocations, and a notes file that explains the supported variants. Human68k tools traditionally ship with a JIS-encoded text file that describes the command-line flags, and even English-speaking users appreciate that level of documentation. A README on the source repository can mirror this content for developers who want to rebuild the binary.

Distribution happens through a handful of well-trodden channels. Files shared on the X68K archive mirrors, posted to mailing lists, and uploaded to community sites reach collectors quickly. A short story-style write-up, similar in tone to the belt replacement guide, makes the project feel approachable. Hosting the project alongside other resources at x68k.net helps new users find it, especially when the tool is mentioned in passing on forums that hobbyists in Sydney and Melbourne already follow.

Once the tool is out in the world, feedback shapes the next iteration. A user in Perth might report a variant used by a lesser-known shooter, a maintainer in Adelaide could submit a patch that handles a different header layout, and a new contributor might add scripting bindings that integrate the decompressor with other preservation tools. That loop is what keeps the platform genuinely supported rather than merely archived.

Grab a copy of the source, point it at an old X68000 floppy image, and watch the bytes fall out where the loader used to put them. Every archive the tool unpacks is one more piece of the platform rescued from the slow decay of magnetic media, and every patch that lands in the repository tightens the net around data that would otherwise have stayed locked inside a custom format.

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.

A two-ink risograph print in muted slate-blue and charcoal on off-white paper, showing a stylized desktop computer monitor beside a circuit board with soft geometric trace lines, conveying a calm retro-computing workshop atmosphere. A two-ink risograph print in deep purple and dark grey on cream stock, depicting a compact expansion card with connector ports and subtle Japanese technical annotations, evoking a hobbyist electronics bench. A two-ink risograph print in teal and charcoal on warm white paper, showing a server rack silhouette with soft network-line motifs and a small weather icon, suggesting a personal server room corner.

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.