How an Amiga Crack Loaded Intact: Boot Block, Cracktro and Game

A small Amiga archaeology exercise. Follow one disk from boot block to cracktro to game, combining an initial static reconstruction with a later emulator check. The bytes and the screenshots answer different questions.

There is something wonderfully revealing about old Amiga software: “loading a game” could mean taking over the whole machine. The disk might not contain a neat directory of files. It might contain a boot block, a custom floppy reader, a depacker, a music module, and a lot of carefully arranged bytes that only make sense when the loader puts everything back together.

That is exactly what turned up in Intact, the 1990 vertical shooter from Sphinx Software and CrossTechnics. I started with Intact (1990)(Sphinx)[cr Defjam - CCS - ACC].adf and followed the code statically. A later FS-UAE run checked the broad cracktro-to-game sequence. That run did not validate every packed region or the exact decompressor semantics.

First, what is Intact?

Intact is an OCS Amiga shoot-'em-up: a ship moves through vertically scrolling levels, fights enemy formations, and collects credits that can be spent on upgrades. It is the kind of game where the end result is fast action, but the startup path is a small systems-programming project in its own right.

For background, see Lemon Amiga's Intact entry. Its externally hosted preview is unavailable in the current browser check, so the images retained below are the labeled FS-UAE captures, with the no-disk reference kept separate from the ADF run.

The disk gives us a useful first clue

The ADF is 901,120 bytes, which is exactly the size expected for an 80-track, double-sided, 11-sector Amiga floppy image. The boot block begins with DOS\0 and has a valid Amiga boot checksum. That sounds like a normal disk, but the area where an AmigaDOS root block would normally be found contains high-entropy loader data rather than a clean filesystem header.

In other words: the disk keeps enough of the Amiga boot contract to start, then uses its own rules for the rest. A directory tool is looking in the wrong place.

PropertyValue
Boot signatureDOS\0
MD54293476ee4229d76803fbc749234136c
SHA-256153a3bef09fa40b6f95a8c0f33aa21ececa6f4de9cf1f1726b672b1239d604f5
FilesystemCustom bootable track layout, not a clean mountable AmigaDOS volume.

Step 1: the boot block loads two pieces

The first executable code is in the boot block itself. It builds an Amiga I/O request, uses read command 2, and performs two reads before jumping into the first one:

ReadADF offsetBytesRAMPurpose
1$000400$1200$20000Resident stage-1 loader
2$0ad400$1b800$62cf0Cracktro payload
; immediate values reconstructed from the boot block
move.l  #$1200,$24(a1)       ; read length
move.l  #$20000,$28(a1)      ; destination
move.l  #$400,$2c(a1)        ; disk offset
move.w  #2,$1c(a1)            ; device read
jsr     -$1c8(a6)

move.l  #$1b800,$24(a1)
move.l  #$62cf0,$28(a1)
move.l  #$ad400,$2c(a1)
move.w  #2,$1c(a1)
jsr     -$1c8(a6)

jmp     $20000

This is the key transition. The Amiga does not open a file called “loader”, and it does not launch a normal C/Intro program from a directory. The boot block copies raw bytes to known addresses and jumps to them.

Step 2: stage 1 becomes its own disk driver

Once running at $20000, the resident loader allocates nine destination buffers. It then talks directly to the floppy hardware through CIA and custom-chip registers. The track reader:

  • selects the drive, side, direction, and head-step controls;
  • starts disk DMA with DSKLEN=$9980 in read mode;
  • searches for the Amiga MFM sync word $4489;
  • separates odd and even MFM words with $55555555;
  • places sectors by their sector numbers; and
  • checks the decoded XOR checksum.

That explains how a disk can look “wrong” to AmigaDOS and still boot perfectly. The loader is treating the floppy as a physical track stream, not as a collection of ordinary files.

Step 3: the cracktro is a preloaded mini-demo

The second boot read put $1b800 bytes at $62cf0. During stage-1 initialization, code at loader offset $1cc calls a small wrapper at $4b2; that wrapper calls the payload at $62cf0 and returns.

The payload is much more than a text string. Its entry code initializes pointers and custom-chip state, installs an interrupt vector at address $6c pointing into the payload at $62de4, calls several setup routines, and returns with RTS. The loader therefore gives the intro a chance to install its graphics, audio, and interrupt machinery while the rest of the startup continues.

EvidenceWhat it tells us
$62cf0 entry and RTSThe payload is called as an initialization stage, not simply copied and forgotten.
Interrupt vector $6c → $62de4The intro installs code that can continue running asynchronously.
graphics.library, custom-chip accessesGraphics/display setup is part of the payload's environment.
CRACKED IN 1990 and publisher/design creditsThis is the cracktro/credit screen payload.
introbeat plus six sample namesThe intro carries its own tracker music and samples.

The friendly way to think about it is: this is a tiny demo embedded in the loading process. It establishes a visual/audio presence, shows the group's credits, and can use interrupts while the loader does the less glamorous work of reading and expanding the game.

There is one important caveat. Static code proves the payload is loaded, called, and equipped to drive graphics/audio. It does not prove the exact order of the screens, whether the music starts before or after every track read, or precisely when the cracktro hands the display back. Those are emulator questions.

The soundtrack survived intact

At payload offset $dcac there is a complete standard 4-channel ProTracker module named introbeat. It has the M.K. signature, 22 song positions, 20 patterns, and six named 8-bit sample streams:

  • st-99:bdrum-finetime
  • st-99:snare-bigfun
  • st-99:hhop-alright
  • st-99:piano-oasis
  • st-99:snare-pink
  • st-99:bass-oasis

The module was extracted byte-for-byte from the payload. That is a nice example of the difference between “not everything is decoded yet” and “nothing can be recovered”: the cracktro's music is already a normal, recognizable Amiga artifact.

Step 4: the game is stored as nine packed regions

The loader contains a descriptor table at file offset $f88. Each 12-byte entry says where a region will go, which side-track it starts on, how many tracks to read, and how many packed bytes to pass to the depacker.

RegionStart side-trackTracksPackedOutput trailer
0129$27700$32f50
1307$08c24$0cc24
2374$04ff0$08790
3416$06e40$0a6c8
44710$0d014$11d80
5579$0c458$1d8ca
6669$0ba0c$1dc73
7757$098ec$1e979
88210$0ceb4$1ec5c

The “output trailer” column is the final output length stored at the end of each packed stream. It is not a decompressed file sitting on disk; it is a value the depacker reads while working backwards.

So what is the packing?

This is where the phrase custom backward-LZ-style depacker comes from. The label is descriptive, not a guess at a commercial packer name.

The routine is embedded in the loader at offset $6ca. It starts at the end of a packed region, consumes a least-significant-bit-first bitstream using a 68000-specific refill/anchor convention, and writes output toward the beginning of the destination. Its copy loop is unusually helpful:

move.b  -1(a2,d2.l),-(a2)

That means “take a byte from a backward distance and move the destination backwards by one byte.” The routine has literal runs, reference classes, and several distance forms. From the instructions we can describe:

  • a 3-bit literal count, including an extended-count branch;
  • a 2-bit reference class;
  • fixed and variable copy lengths;
  • 8-, 14-, 12-, and 16-bit backward-distance paths; and
  • a shared flag path selecting the 12- or 16-bit distance form.

Each stream ends with a 12-byte trailer. The final longword supplies the output length, the preceding longword is skipped by this routine, and the next longword seeds the initial bit reservoir.

That is enough to understand the shape of the codec and to avoid calling it PowerPacker or ByteKiller without evidence. It is not yet enough to reproduce all nine files: the current standalone translation still reaches impossible references on every exact stream. The likely missing piece is in the precise bitstream framing/refill behavior, not in the overall idea that the data is backward-output compressed.

The last move: handing control to the game

After the intro call and the track/decrunch sequence, stage 1 explicitly decrunches the $08c24-byte region to $25b48. It copies a 28-byte stub into low memory at $400 and jumps there. The stub copies 52,264 bytes from $25b48 to $1f0e0, then jumps to $50000.

; low-memory handoff stub
lea     $25b48,a1
lea     $1f0e0,a2
move.w  #$3309,d0
.copy:
move.l  (a1)+,(a2)+
dbra    d0,.copy       ; $330a longwords = 52,264 bytes
jmp     $50000

The retained loader bytes at file offset $51e are 30 3c 33 09: move.w #$3309,d0, not moveq. With dbra, the loop copies ($3309 + 1) × 4 = 52,264 bytes. This corrects the mnemonic without changing the stated copy length.

This is the final statically visible transfer into the loaded game/main-program address space. It is a very compact ending to a fairly elaborate startup: read tracks, expand regions, then jump into the reconstructed program.

What is confirmed, and what is still open?

Confirmed from the imageStill to explore
Two boot-block reads and their RAM destinations.The exact bit alignment/refill rule needed to decode all nine regions.
A raw MFM reader with sync, deinterleave, sector placement, and checksum logic.Which expanded region contains code, sprites, fonts, level data, or other assets.
The cracktro entry, interrupt-vector installation, credits, and tracker module.The exact order and timing of the intro screens and music relative to loading.
Nine packed regions, their track ranges, lengths, and final handoff addresses.A byte-level comparison with an untouched original disk to identify the crack delta.
The final copy-and-jump sequence.An instruction-level trace to confirm the exact live jump at $50000.

There are also some tempting leads that deserve restraint. The loader contains strings such as Intact:C/Intro, but the disk is not a normal AmigaDOS filesystem, so that string should not be turned into a claim that a real C/Intro file exists. Likewise, a write to the 68000 bus-error vector is an observation in the sample, not proof of malicious behavior.

Watching the three stages run

The retained capture notes date the emulator follow-up to 7 September 2026: FS-UAE 3.2.35, an Amiga 500/512 KB profile, 512 KB chip memory, and no slow or fast memory. The cracktro and game frames came from the supplied ADF run. The ROM/bootstrap image is a separate no-disk reference.

FS-UAE Amiga boot stage

Boot stage: the emulator is at the machine's ROM/bootstrap display before the disk's custom loader has drawn its own screen. This frame is a separate no-disk boot reference because the ADF's transition from boot reads to custom graphics is too quick to pause cleanly.

Intact cracktro credits

Crack intro: the preloaded stage-2 payload displays the INTACT title and credits Defjam, CCS, ACC, and Crosstechnics.

Intact game screen after cracktro

Post-intro game screen: the run has moved beyond the cracktro into the game's own display, consistent with the broad handoff reconstructed from the loader. A screenshot alone does not establish the exact program counter.

The runtime check confirms the broad chain: boot code, crack-intro payload, then game-side code. It does not by itself identify every packed region or prove the exact decompressor semantics; those remain byte-level questions for the disassembly.

Why this is a fun kind of reverse engineering

The crack did not merely remove a copy-protection check. It changed the computer's relationship with the disk. The Amiga boots a small program, that program becomes a disk driver, the disk driver reconstructs several memory regions, and a separate mini-demo announces the people who made the altered version. By the time the game starts, the “file” has been transformed several times.

What we can already recover is surprisingly tangible: the boot code, the raw-track loader, the descriptor table, the cracktro strings and setup logic, the complete introbeat module, and all nine packed regions. What remains is the satisfying hard part: making the depacker agree with the original 68000 on every bit, then identifying the assets hidden behind those streams.

Method note, corrected 8 October 2026: the analysis began as static, read-only inspection and was followed by the isolated emulator run described above. The retained private notes contain disassembly, hashes, packed-region extracts, soundtrack, decoder experiment, and capture settings. They are not linked here as a public download. The standalone decoder still fails validation on all nine exact streams.

Comments