Chasing the Wrong Culprit: What a 4GB CompactFlash Card Taught Me About the Oktagon 2008

The first article covers the build itself: one CompactFlash card, three self-contained Workbench installs, a Kickstart-detecting boot dispatcher, and an A500 that boots 1.3, 2.05 or 3.1 depending on where the switch is set. It works, and the write-up tells you how to do it.

This is the other half — the part where I spent the better part of a fortnight convinced I was one small fix away from using the whole of a 4GB card, and was wrong every single time. The conclusion is disappointingly simple, the route to it was not, and the reasons I kept going wrong are the genuinely useful part.

The answer, up front: the Oktagon 2008 cannot address past the 2GB boundary. It is a hard limit of the controller. No filesystem fixes it. Everything below is how I failed to work that out for about a week, and what I broke along the way.

Act one: the RDB, wiped twice

Before any of the interesting failures, two boring ones that nearly ended the project.

The Rigid Disk Block — the structure at the very start of the drive describing partitions and embedded filesystems — is tiny, and it is separate from your partition contents. Your installed Workbenches live in blocks far past it. So the tools can corrupt the RDB while leaving every byte of your installs intact underneath.

I wiped it twice, both times doing the same thing: embedding a filesystem into the RDB of a drive that was mounted and in use. The tool could not get exclusive access, threw an “object in use” error, and left the RDB half-written. Both times the machine came up with no partitions and no boot, which looks like catastrophe.

It isn’t. Rebuild the RDB describing the same partitions at their exact original cylinder boundaries — re-describe, do not reformat — and everything reappears, boot included, with nothing reinstalled. Which is entirely dependent on having written down your partition map. Start cylinder, end cylinder, size and DosType for every partition. With those numbers it is a twenty-minute inconvenience; without them it is a rebuild from scratch. I got away with it twice because I had them.

Two rules came out of this, and they held for the rest of the project. Do RDB work offline, on an image — nothing is mounting it, so nothing can lose the race. And use HDToolBox, not HDInstTools. HDInstTools nulled custom DosTypes I had entered, refused to scan drive units, mangled the RDB header, and threw the in-use errors that caused both wipes.

Act two: two CF cards on one bus

The original design had an internal system card plus an externally accessible second slot for swappable game cards. It fought me continuously — a second CF on the same IDE bus corrupted reads on the first, even correctly jumpered as slave. CF cards implement master/slave inconsistently and plenty will not act as a slave at all, whatever the adapter claims.

I stopped working around it and removed it: internal adapter out, external slot jumpered as master, one removable card. That killed an entire class of failure by construction — and, more usefully than it sounds, made the card easy to pull.

That turned out to matter enormously, because it enabled the workflow that made everything afterwards survivable: image the card to the fileserver, mount the image in WinUAE, break things freely, write back only what is proven. The image is simultaneously backup, restore point and development environment. On a retro machine the scarce resource was never storage. It was recoverability.

It also, unfortunately, set up the trap that cost me the most time.

Act three: chasing the wrong culprit, repeatedly

With the card sorted, the remaining prize was the upper half. FFS cannot address past the 2GB byte offset, so the standard answer is PFS3 — a third-party filesystem, embedded in the RDB, without that limit. I made the upper 2.4GB a PFS3 partition and it refused to read, with this:

TD32 and Direct SCSI access modes failed!
Can't read block 7813007 (<4G)
oktagon.device:0

What followed was a sequence of confident conclusions, each of which felt like the answer and none of which was.

False start: “it must be the controller’s addressing”

The failing block was high on the disk, so the obvious thought was that the Oktagon could not reach up there. I then disproved this to my own satisfaction: an older PFS3 build formatted the entire 2.4GB partition on real hardware without complaint. Formatting every block of a partition above the 2GB line seemed like conclusive proof the controller could reach it.

It was not proof of anything, and I will come back to why.

False start: “it’s the DosType”

PFS3 answers to more than one identifier — PFS\3 (0x50465303) and PDS\3 (0x50445303). I switched to PDS\3 and everything sprang into life: the partition mounted, 1.3 booted, and in WinUAE all three Workbenches read and wrote to it happily. I was ready to publish it as a one-byte miracle.

On real hardware it fell over immediately. The source explains why. In disk.c, where PFS3 decides how to talk to the drive:

/* if dostype = PDSx, test Direct SCSI first and always use it if test succeeded */
if ((g->dosenvec->de_DosType & 0xffffff00) == 0x50445300 || forceDS) {
    g->tdmode = ACCESS_DS;
    ...
}

0x50445300 is PDS\0 and matches any DosType beginning PDS. It is not an alternative identifier — it is an instruction to use Direct SCSI mode, and to skip access-mode detection entirely. It “worked” in emulation because uaehf.device supports Direct SCSI, and because skipping detection meant skipping the code that would have surfaced the real problem. This is worth knowing in its own right and is documented nowhere, so: use PFS\3, never PDS\3, unless you specifically want Direct SCSI.

False start: “it’s a bug in pfs3aio”

With the DosType theory dead I read the source properly, and built a case I was rather pleased with. PFS3 must choose a method for reading blocks above the 2GB byte offset, since the classic CMD_READ takes a 32-bit byte offset that cannot express one. It probes the device for TD64, NSD and Direct SCSI support — and the TD64 probe works by sending a deliberately malformed request (zero length, null buffer) and inferring support from which error comes back, accepting only a narrow set of codes.

Meanwhile the older PFS3 build never probed at all. In struct.h:

#define ACCESS_DETECT (TD64 + NSD + SCSIDIRECT > 1)

Detection only compiles in when more than one access mode is enabled. Build with a single mode and PFS3 simply assumes TD64 for a large partition and gets on with it. That neatly explained why the old build worked and the new one did not, so I rebuilt pfs3aio with TD64 forced on — and it worked. All three Kickstarts, reading and writing the partition.

In WinUAE.

What was actually going on

The Oktagon 2008 cannot address past 2GB. That is the whole story, and every observation above is explained by it once you stop assuming otherwise.

pfs3aio was never buggy. It was the only thing being honest. Its detection routine tests the top of the partition before committing to an access mode — so on a partition extending above 2GB it immediately tries to read a block the controller cannot reach, and correctly reports that it cannot. The error message I spent days trying to eliminate was accurate the entire time.

The older build “worked” because it never checked. It assumed an access mode and wrote from the bottom upwards, so a format completes and looks successful right up until something actually needs the unreachable region. It was not succeeding where the new build failed; it was hiding the problem. My “conclusive proof” that the controller could reach high blocks was nothing of the kind — it was a format that had not yet had cause to touch them.

My forced-TD64 rebuild did the same thing. I had not fixed anything. I had removed the check that was telling me the truth.

And emulation validated every single wrong answer. WinUAE’s uaehf.device has none of the Oktagon’s restrictions and reads above 2GB perfectly happily. So every theory I formed was confirmed in emulation and refuted on hardware, over and over. The tooling that made the whole project safe — image it, mount it, tinker freely — was also the thing that kept telling me I had solved a problem I had not touched.

The last idea: a different device driver

There was one avenue left that would genuinely have addressed the root cause: if the limit is in the controller, replace what drives it. lide.device is a modern IDE driver with large-drive support, and flashing it to the controller was the obvious next move.

It did not go well. At one point the machine would not boot at all with the accelerator/RAM/IDE board enabled — just a flashing power LED and a boot loop, which looks exactly like a bricked controller and feels considerably worse. It took several attempts at reflashing the original firmware before it came back to its previous working state.

It was recoverable, and I had a known-good firmware image, which is the only reason this story has an ending rather than a postscript. But it is worth being clear-eyed about the trade: the prize was the upper 2GB of a card holding 161MB of games, and the stake was a working 68000 A500. Once a firmware experiment escalates from “the filesystem will not mount” to “the machine will not POST”, the sensible move is to stop, and that is what I did.

Where it ended up: give up on cards over 2GB

That is the actual conclusion, and it is a recommendation rather than a shrug. On an Oktagon 2008, do not bother with CompactFlash cards larger than 2GB. The controller cannot use the space, no filesystem will change that, and the only route past it is reflashing the controller — which is a genuine risk to the machine for a genuinely small reward.

A 2GB card is simply simpler. Partition it entirely in plain FFS, below the line where everything works, and every problem in this article evaporates: no second filesystem in the RDB, no DosType subtleties, no access-mode detection, no custom binaries, no firmware. It boots all three Kickstarts, every Workbench can read every partition, and nothing surprises you. For a machine that in practice holds 161MB of games, 2GB is not a compromise — it is roughly twelve times more room than the thing needs.

So that is where it sits: a 2GB card, externally accessible, jumpered as master, plain FFS throughout, stock everything. No patched filesystem, no reflashed controller, nothing I would hesitate to rebuild from scratch. And after a fortnight of increasingly baroque theories, a machine I am not nervous about switching on.

Two things are worth salvaging from the PFS3 detour, because they are true and useful even though the project abandoned it. 1.3 can read a PFS3 partition — the handler loads from the RDB exactly as the FastFileSystem does, which surprised me. And use PFS\3, never PDS\3, for the reason above.

The rest is a lesson about diagnosis. Every wrong turn came from the same mistake in different clothes: treating an absence of complaint as evidence of success. The old filesystem did not complain because it never checked. My rebuild did not complain because I had removed the check. The emulator did not complain because it does not share the hardware’s limits. The one component that did complain, loudly and accurately, from the very first attempt, was the one I spent a week trying to fix.