Reverse-engineering Amiga games

; Tetracorp video game disassembly and analysis

All manner of secrets hide within classic video games: undiscovered cheat codes, cut content, undocumented game mechanics, potential speedrun exploits, and opportunities for modding or translation projects. The following guide offers and introduction to reverse-engineering and exploring Commodore Amiga games in particular.

  1. Avoid duplicating effort
  2. Disassembly
    1. IRA
    2. Ghidra
  3. Introductory analysis
  4. Finding the interesting features
  5. Modifying the game
  6. Emulator debugging
  7. NDOS disks
  8. Packed executables
  9. Sprite extraction
  10. Disk and file analysis
  11. Background research

Avoid duplicating effort

The first step in any reverse-engineering project should be research. It’s entirely possible that someone has already analyzed the game you’re interested in, or even better, that the original source code has been released. In some cases the original author or company may have the source, and may be convinced to release it.

Disassembly

For optimal performance, nearly all commercial Amiga games were written in 68000 assembly, the closest human-readable language to the CPU’s native machine code. Just as an assembler once turned the original programmer’s source code into an executable program, a tool called a disassembler can recreate the source code from the program (though with certain limitations).

Even games written in other languages like C or AMOS Basic will be reversed to assembly language. There’s no known way to reverse a compiled AMOS program back into AMOS (and its assembly output is really ugly).

IRA

One of the best Amiga-specific disassemblers is the freeware tool IRA. This runs natively on Amiga, but it can also be compiled for Windows, Mac and Linux, which is recommended since it runs a lot faster.

If for example the game executable you’re analyzing is named MatchPatch, run something like this:

ira -A -KEEPZH -NEWSTYLE -COMPAT=bi -PREPROC MatchPatch

“Preproc” attempts to work out which bytes are 68k instructions and which are data, although it’s not perfect. It creates an assembly language source file, MatchPatch.asm, and a config file, MatchPatch.cnf. If you want to get it more perfect, you can manually edit the .cnf to change the start and end points of the code sections, then re-run it with -CONFIG:

ira -A -KEEPZH -NEWSTYLE -COMPAT=bi -CONFIG MatchPatch

Identifying misidentified sections is something of an acquired skill. Signs of code misidentified as data include the code for common instructions appearing DC.L lines (e.g. 4e75) or sometimes data areas containing labels. Signs of data misidentified code include a lot of EXT_ declarations at the start of a program, or code sections with a lot of ORI #0 ($0000). Strings of numbers from hexadecimal $41 to $7A are often unidentified ASCII text. The -A option is good for helping you to familiarize ourself with the numeric equivalents of instructions.

See also this EAB thread on using IRA: Small IRA Tutorial.

Ghidra

Ghidra is a powerful reverse-engineering tool released in 2019. It can handle the the 68000 platform, and thanks to a plugin called ghidra_amiga_ldr, it can specifically handle Amiga executable files. It can also interpret disassembled code as C, which won’t give you an accurate representation of the game’s source code if it was originally written in Assembly (as most Amiga games were), but it can give you a representation of the code in a high-level language that may aids understanding of the program if you’re more familiar with C-inspired languages like Java.

As of May 2022, Ghidra is now my preferred tool for analyzing Amiga games. I’ve written an introductory tutorial to using Ghidra for this purpose: Intro to Amiga reverse-engineering with Ghidra.

Introductory analysis

At this point, it becomes clear that you need to know at least the basics of 68000 assembly language. Introductory tutorials include MarkeyJester’s 68k Tutorial and redhotsonic’s Work with Motorola 68000 assembly. Useful reference guides include NVG’s 68k docs, 68000 Instruction Set Summary, and the 68000 Programmer’s Reference Manual.

The limitations of disassembly are also obvious. The true original source code to a game contained meaningful variable names, label names, and comments, which would make it much easier to understand the program’s meaning: see the original MatchPatch source code for an example of this. The assembly process usually stripped names and comments, meaning that these will not appear in a disassembly.

However, on occasion you may get lucky and find a program with variable names intact. This usually occurs by mistake when the programmer creates a debug build with the symbol table enabled for testing, and forgets to disable it again when making the final release build. Some games with this include the CD32 versions of Nigel Mansell’s World Championship and Zool 2.

In most cases, you will have to rename labels yourself once you discover their meaning. Find-replacing one throughout the document and looking for other instances will let you identify related labels; e.g. once you’ve identified the the variable for Score, you can look for other things for events which increase Score.

The more elegant method is to only add variable names and comments to the .cnf file, then re-run ira. See ira_config.doc. Useful directives include SYMBOL (rename a label), LABEL (add a new label), and COMMENT and BANNER (place a comment). The method then is to continually discover new label definitions, add them to the configuration, re-disassemble the program with the option -CONFIG.

Finding the interesting features

Even a very simple Amiga game can have thousands of lines of code, but you’re mainly interested in identifying the code locations referring to relevant game mechanics, and these can be a good starting point to explore the function further. Here are some things to look for:

Modifying the game

You can experiment by changing the code. Use a hex editor to search for a certain series of bytes (if you used the -A option with ira each line in the disassembly shows you the raw bytes, and certain sequences will be unique within the file and can be used as landmarks).

For example, suppose you see a number which is set to 3 at game start, and you suspect this is the starting number of lives. Find it in the program binary and hex edit it 5, then play the game again. If you start with 5 lives, you’ve found the value. You can now use this to, say, give yourself a large number of lives to cheat, or find other code which references it (e.g. life pickups, and from there the general item pickup code, and from there the list of pickups, etc).

You can do a similar thing with emulator save states, which can be quicker than reloading the entire game.

A more elaborate method of program modification involves reassembling the source code. The documentation for ira recommends using vasm like so:

vasmm68k_mot -no-opt -Fhunkexe -nosym -O MatchPatch MatchPatch.asm

In theory, this will create a functionally identical binary to the original. You can omit -nosym to create a version of the binary with a symbol table, which may aid debugging.

A particularly useful setup here is to configure your emulator to mount a folder on the host PC as a hard disk, which the Amiga side can then launch your modified games from directly.

Emulator debugging

The Amiga emulator WinUAE, and derivatives like FS-UAE, include a useful debugger which lets you freeze the emulation and read/write memory. There are various WinUAE debugging tutorials, such as this thread Basics of debugging ASM in WinUAE?

While the emulator is running, trigger the debugger by hitting shift-F12 (WinUAE) or F12-D (FS-UAE). You may have to first enable debugger in the settings. In the debugger, type ? or h to show a list of commands.

Search for values in memory with s. For example, to find a string called “Loading” anywhere in memory, type s "Loading" 0, or to find the number 10,000, type s !100000 0 or s $186a0 0.

Dump a section of memory as data with m, e.g. m 400867EB. Dump it as disassembled code with d, e.g. d 400867EB. Save a chunk to disk with S, e.g. S dump.txt 400867EB $10000.

Write into memory with W, e.g. W 400867EB 10. Write to a register with r, e.g. r D0 $00000000.

Creat a watchpoint for when a certain memory location is accessed, with lowercase w. It will automatically trigger the debugger when this occurs. For example, w 1 400867EB 1.

Return to the game with g.

NDOS disks

Finding the executable file can be tricky for NDOS disks (i.e. disks using a non-standard format, generally used by commercial games to prevent disk copying).

One solution is to just try to disassemble the entire ADF. Another is to start the game in an emulator, make a save state, and disassemble that. You need to configure the emulator to make uncompressed save states first, and it’s recommended to set the emulator to have the minimum amount of RAM necessary (usually 1MB chip with no fast RAM) to limit the size of the memory. You also want to boot from the game disk to avoid having Workbench in your disassembly, which will only confuse analysis.

Some games received a re-release for CDTV or CD32. These use a standard CD-ROM format and can be read freely. Antipiracy was not a priority for CD32 games since consumer-level CD copying was impractical in 1993.

Packed executables

Some executables are compressed. Disassembling such a program will give the impression of a program with a very short code section followed by a very long data section. This might be done either to fit a larger program on a single disk, or to obfuscate the program to make it harder for pirate groups to strip copy-protection code.

xfdMaster can unpack nearly any format.

If this fails, load the program normally and disassemble from a save state or memory dump.

Sprite extraction

Extracting graphics from Amiga games is an entire topic in itself.

Maptapper is the standard tool for finding sprites and graphics in Amiga games. You can pull data directly from the disk or from a memory dump. It can be tricky to use, but something it will do is let you see the location of graphical data within an executable, which can help you to make labels.

amostools may let you extract data from ABK files.

Disk and file analysis

Other files on the game disks often contain data such as graphics, sound, maps, levels, and saved games. Decompress them if necessary with xfdDecrunch.

DiskSalv can, occasionally, find leftover data which does not appear in the file listing. This can occasionally find things like source code, unused game data, or fragments of older versions of the game executable.

Background research

A lot of Commodore Amiga games have little to no online presence today, and it is possible that your reverse-engineering project will be the most in-depth resource ever created on the subject. If you really want to perform the ultimate analysis, look into other related information which you might collect for the interest of readers.

Multiple versions of the same game may be available, including free demo versions distributed on Amiga magazine coverdisks, bugfix releases, alternate language versions, sequels, or ports to other platforms. Games written for Amiga 500 may have received an AGA port (A1200) or a CD release for CDTV or CD32. You can find and compare them for differences.

Search for contemporary magazine articles at Amiga Magazine Rack (AMR) and Archive.org. Discover what people were saying about the game at the time, and help your readers to understand the context that may not be obvious today. For example, magazine articles tell us that K240 was widely understood as a “god game” like Populous (1989), because the current genre of “real-time strategy” was not yet defined by Dune II (1993) and Command & Conquer (1995).


« Back to index page