← Back to all posts
News

72,758 Lines of Amiga Assembly. Byte-Identical in 15 Minutes.

September 4, 2026 · 00:11 UTC · News
72,758 Lines of Amiga Assembly. Byte-Identical in 15 Minutes.

TL;DR

In 1993, in Baghdad, Rabah Shihab wrote Babylonian Twins in pure 68000 assembly on an Amiga 500 with 512 KB of RAM, no hard drive, and electricity a few hours a day. Commodore collapsed and the finished game never shipped. On September 3 he published the port log for moving it into Godot with an LLM, and the technique buried in it is better than the nostalgia. Before porting anything, the model made the 1993 sources assemble again and kept going until the output was byte-identical to the binaries on the shipped disks. That took fifteen minutes, and it is the part you should steal.


Build the oracle before you build the port

Most LLM reverse-engineering sessions are a slow accumulation of plausible guesses. What does this byte mean, is this a width or a stride, is that table 16 or 18 bytes per record. You get an answer, it looks reasonable, and you have no way to check it that is cheaper than shipping and waiting for complaints.

Shihab inverted the order. The first task was not "port the game." It was "make the 1993 sources build again," using the vasm assembler on an Apple Silicon Mac, with success defined as bytes matching the original binaries. The sources were written for ASM-One, which disagrees with vasm in small ways, so the model wrote a preprocessing pass bridging five dialect differences.

Fifteen minutes from a folder of files to the first rebuild that matched the shipped bytes.

That is the whole trick, and the payoff line is the next one: "From then on, every claim about this game could be settled by comparing bytes."

Think of a locksmith. You can eyeball a blank and argue about the cuts, or you can file a key and turn it in the actual lock. A byte-exact rebuild is the lock. Once you have it, every subsequent question about the format stops being a debate and becomes a diff.

step zero: earn a ground truth, then port 1993 sources26 files vasm rebuild5 dialect fixes diff vs diskshipped binary byte-exact15 min
The port never started until the original could be reproduced exactly. Everything after that was checkable.

Read the reader, not the data

There is no documentation for a hobby project built under sanctions with one copy of the Amiga Hardware Reference Manual. So the model did not infer formats by staring at bytes. It went to the code that consumes them.

The level loader is 1,652 lines of uncommented 68000. Rather than guess the map layout, the model read the drawing routine, followed how the grid was walked, and recovered width and height from constants elsewhere in the file. Tile properties fell out of two routines contradicting each other: the draw loop masks off the low byte, and the collision check does the opposite, which pins the bit packing without a single guess.

Objects turned out to be an 18-byte record, and the field names came from finding the routine that walks the table every frame. Sprite sheets came from the draw routines and the org arithmetic. Verification stayed mechanical throughout: five full-level renders compared against the original, zero differing pixels. Level one alone is 600 tiles wide, or 9,600 pixels.

352 bytes of headroom

One number in the log explains why this codebase resists casual modernization. The level-one map consumes 74,400 of the 74,752 bytes budgeted for it. The margin is 352 bytes, on a machine with half a megabyte total.

level one map vs its 74,752-byte budget 352 bytes free 74,400 bytes used, 99.5% of the budget
The copper sliver on the right is the entire safety margin the 1993 build had left.

You do not refactor code like that. You reproduce it.

Two ports, three evenings

There were actually two rebuilds running in parallel, both landing in Godot 4 as GDScript. The first moved the 34,000-line C++ engine that Shihab wrote from scratch for the 2010 iOS release. That one was the control: he says twenty-one minutes from empty project to a playable character, on code he had personally written over months of nights and weekends. The second rebuilt the 1993 assembly game directly. The third session, which he calls the greedy ask, embedded the retro game inside the modern one as a guest with its own namespace and scene host, with the engine switching to 50 Hz on the way in and back to 60 Hz on the way out.

minutes per session, from the logged timestamps 2010 C++ port232 1993 asm port68 embed retro105 22:23 to 02:15 / 14:34 to 15:42 / 21:58 to 23:43
The assembly rebuild, the hardest of the three on paper, was the shortest session.

Both tick rates were preserved deliberately. Multiply a velocity by 0.85 sixty times a second and you get one friction curve. Do it fifty times a second and you get a different game. Shihab rejected Godot's standard physics layer for exactly this reason, and left the original constants alone: "Nothing tidied up the stray 0.49. There are no tests and no docs; those comments are the spec."

Where it got things wrong

The log is unusually honest about failures, which is most of what makes it useful. The model shipped real bugs into playtests:

  • A guard's shove attack reached through solid rock into the floor below, because an upper bound condition got dropped.
  • A hidden tile layer rendered from the start, exposing secrets that were supposed to stay hidden.
  • Enemy processing order double-counted trampoline jumps.
  • A door key made of four separate palm references got collapsed into one strange name.
  • Audio loops cut off early because mono sample lengths were computed as if they were stereo.

Note the shape of these. None of them are format errors, which is where the byte-exact baseline did its work. They are behavioral errors in the translated logic, the category no diff can catch for you. If you copy this workflow, that is where your playtesting budget goes.

There is also a bug that predates the AI entirely. Level one has an exit with no prompt telling you it exists. Shihab's line on it: "I never found that in fifteen years, and neither did my testers or two rebuilds. It took a stranger's one-star review." Thirty-three years of assembly archaeology, undone by a guy on an app store.

You can play the original today

Alongside the write-up, the team put the complete 1993 game on itch.io for free: both original disks plus the demo, as 880 KB ADF images that boot in FS-UAE, WinUAE, or on real hardware. The modern Definitive Edition is already on Google Play and iOS, with Windows, Mac, and Linux builds slated for Steam this fall. The game is catalogued in the Amiga Hall of Light database, and the site claims more than two million players across the 2010 re-release and after.

The Hacker News thread drew 171 points and 56 comments, mostly from people who have tried the same thing on their own dead codebases and lost.

Why this generalizes past retro games

Swap "1993 Amiga game" for any of the things sitting in your organization right now: a COBOL batch job, a vendor binary with no source, an undocumented wire protocol, a legacy build nobody can reproduce. The pattern is identical.

Find the cheapest mechanical check that can distinguish a correct answer from a confident one. Make the model earn that check before it writes a line of new code. Then let it work backward from the code that reads the bytes rather than from the bytes themselves. The model in this case was Claude Code driving Claude Fable 5, but the model is the least transferable part. The oracle is the transferable part.

Key Takeaways

  • Ground truth first, port second. Getting 72,758 lines of 68000 assembly to rebuild byte-identical took fifteen minutes and made every later claim checkable with a diff.
  • Work backward from the consumer of the data. The map format came from reading the 1,652-line draw routine, not from staring at the map bytes.
  • Mechanical verification scales, opinion does not. Five full-level renders, zero differing pixels, no argument required.
  • Byte-exactness catches format bugs, not behavior bugs. Every failure that reached playtest was translated logic: bounds, ordering, sample length. Budget human play time for those.
  • Do not tidy timing-dependent constants. The retro build runs at 50 Hz and the modern one at 60 Hz, with Godot's physics layer deliberately unused, because the friction math is the spec.
  • The technique is the artifact. Any codebase with a reproducible output, a legacy binary, a golden file, a recorded trace, can get the same oracle treatment.

Sources: Porting my 1993 Amiga game to Godot, with an LLM reading the 68000 assembly, babyloniantwins.com, itch.io devlog: the real disks, finally free, Hacker News discussion, Hall of Light: Babylonian Twins, Google Play listing

AIClaude CodeGame DevReverse EngineeringGodotRetro ComputingAmigaLLM
CONSOLE
$