Skip to content

โ† Home ยท Reverse Engineering ยท Static Analysis ยท Compilers

Manx Aztec C โ€” Reverse Engineering Field Manual

Overview

Manx Aztec C (versions 3.xโ€“5.x, 1985โ€“1992) was the first widely used C compiler for the Amiga, predating Lattice C's market dominance. It targets early AmigaOS (1.1โ€“1.3) and produces code with a distinctive LINK A5, #-N + MOVEM.L D3-D7, -(SP) prologue โ€” saving only 5 data registers (D3โ€“D7) instead of SAS/C's 9 registers. This narrower save set is the single most reliable Aztec C fingerprint.

Key constraints: - 5-register data save (D3โ€“D7 only) โ€” Aztec C preserves fewer registers than any other Amiga C compiler. D2 is considered scratch by Aztec, while SAS/C, GCC, and VBCC all preserve D2. - A5 frame pointer โ€” standard LINK A5, #-N convention, like SAS/C. - Absolute string addressing โ€” like SAS/C, strings are in DATA with HUNK_RELOC32 relocation. - Pre-MakeLibrary era โ€” Aztec C libraries use a different initialization pattern than later RTF_AUTOINIT libraries. - Early AmigaOS focus โ€” code may assume OS 1.1/1.2 behavior that changed in 2.0+.

; Aztec C function prologue (THE signature):
_func:
    LINK    A5, #-$14               ; allocate frame
    MOVEM.L D3-D7, -(SP)           ; save D3-D7 ONLY (5 regs!)
    ; Note: D2 is NOT saved (unlike SAS/C, GCC, VBCC)
    ; Note: A2-A4 are not saved (unlike SAS/C)

Binary Identification

Criterion Aztec C SAS/C
Register save D3-D7 (5 regs) D2-D7/A2-A4 (9 regs)
D2 preservation NOT preserved โ€” call-clobbered Preserved โ€” callee-saved
A2-A4 preservation NOT preserved by default Always preserved
Frame pointer A5 (LINK A5) A5 (LINK A5)
String addressing Absolute + relocation Absolute + relocation
Startup module aztec.o c.o
Hunk names CODE, DATA, BSS CODE, DATA, BSS
Era 1985โ€“1992 (OS 1.1โ€“1.3) 1988โ€“1996 (OS 1.2โ€“3.1)

Detecting D2 as Scratch Register

The most distinctive Aztec C behavior: D2 is call-clobbered. After a function call, Aztec C must reload D2 if it was using it. SAS/C, GCC, and VBCC all preserve D2 across calls.

; Aztec C: D2 is NOT preserved across calls
    MOVE.L  #value, D2             ; D2 = important value
    BSR     _some_func             ; D2 may be destroyed!
    MOVEQ   #0, D2                 ; reload D2 (Aztec C knows D2 is scratch)
    ; SAS/C would NOT need this reload โ€” D2 is callee-saved there

Historical Context

Manx Software Systems produced Aztec C for multiple platforms (CP/M, DOS, Macintosh, Amiga, Atari ST). The Amiga version was one of the earliest C compilers available โ€” released in 1985 alongside the Amiga 1000 launch. Its 5-register save convention (D3-D7 only) reflects the era's emphasis on minimizing prologue/epilogue overhead on the 7.14 MHz 68000.

Aztec C was superceded by Lattice C (which became SAS/C) in the late 1980s, though Manx continued to release versions into the early 1990s. Most Aztec C binaries date from 1985โ€“1989 โ€” the Amiga's formative years.

Software known to use Aztec C: - Early Amiga utilities (1985โ€“1987 era) - Some Commodore-developed tools - ABasiC (the Amiga BASIC compiler) - Early versions of certain games ported from other platforms


Same C Function โ€” Aztec C Output

; CountWords() โ€” Manx Aztec C 5.x:
; (Note: smaller register save set, but structurally similar to SAS/C)

_CountWords:
    LINK    A5, #-$08
    MOVEM.L D3-D4, -(SP)           ; ONLY D3-D4 (not D2-D3!)

    MOVEQ   #0, D3                  ; D3 = count
    MOVEQ   #0, D4                  ; D4 = in_word

    MOVEA.L $08(A5), A0             ; str

    BRA.S   .loop_test

.loop_body:
    MOVEQ   #' ', D0
    CMP.B   (A0), D0
    BEQ.S   .not_word
    MOVEQ   #'\t', D0
    CMP.B   (A0), D0
    BEQ.S   .not_word
    MOVEQ   #'\n', D0
    CMP.B   (A0), D0
    BEQ.S   .not_word

    TST.B   D4
    BNE.S   .next_char
    ADDQ.L  #1, D3
    MOVEQ   #1, D4
    BRA.S   .next_char

.not_word:
    MOVEQ   #0, D4

.next_char:
    ADDQ.L  #1, A0

.loop_test:
    TST.B   (A0)
    BNE.S   .loop_body

    MOVE.L  D3, D0
    MOVEM.L (SP)+, D3-D4
    UNLK    A5
    RTS

Aztec C observations: The function body is nearly identical to SAS/C, but notice D2 is not used โ€” Aztec C skips D2 and starts local register allocation at D3. If you see functions that never touch D2, it's likely Aztec C (or early Lattice C).


References