- New: 11_libraries/ahi_programming.md — AHI retargetable audio API - New: 13_toolchain/cross_compilation_guide.md — cross-compiling for Amiga - New: 08_graphics/rtg_programming.md — RTG Picasso96/CyberGraphX programming - New: 17_demoscene/ — full demoscene techniques section: - copper_effects.md (6 techniques, 10 Pouet screenshots, antipatterns) - sprite_techniques.md (5 techniques, antipatterns) - pixel_tricks.md (5 techniques, antipatterns) - 3d_rendering.md (fixed-point math, 4 techniques, antipatterns) - timing_optimization.md (7 techniques, instruction timing tables) - README.md (section index with Mermaid diagrams) - images/ (10 authentic Amiga screenshots from Pouet.net) - New: 05_reversing/games/ (4 copper-analysis screenshots) - Updated: README index, TODO status (30/30 complete) - Added external references: Pouet/Demozoo links, Scoopex YouTube tutorial series, Amiga Graphics Archive, coppershade.org
17 KiB
Modern Cross-Compilation Guide — CMake, VS Code, CI/CD
Overview
Building Amiga software in 2026 means writing C/C++ on a modern host (Linux, macOS, or Windows) and producing 68000 hunk executables. This guide covers the build system integration layer: CMake project configuration, VS Code debugging setup, and CI/CD pipelines — connecting the compiler tools documented in gcc_amiga.md, vbcc.md, and vasm_vlink.md.
graph TB
subgraph "Host Environment"
CMAKE["CMake Build System"]
VS["VS Code / IDE"]
CI["CI/CD Pipeline<br/>(GitHub Actions)"]
end
subgraph "Toolchain"
GCC["m68k-amigaos-gcc<br/>(bebbo)"]
VBCC["vc + vlink<br/>(VBCC)"]
VASM["vasmm68k_mot<br/>(vasm)"]
end
subgraph "Output"
HUNK["Amiga HUNK<br/>Executable"]
ADF["ADF Disk Image"]
HDF["HDF Hard File"]
LHA["LHA Archive<br/>(for distribution)"]
end
CMAKE --> GCC
CMAKE --> VBCC
CMAKE --> VASM
VS --> CMAKE
CI --> CMAKE
GCC --> HUNK
VBCC --> HUNK
VASM --> HUNK
HUNK --> ADF
HUNK --> HDF
HUNK --> LHA
style CMAKE fill:#e8f4fd,stroke:#2196f3,color:#333
style HUNK fill:#fff3e0,stroke:#ff9800,color:#333
Toolchain Selection Decision Guide
graph TD
START["Starting a new Amiga project?"] --> Q1{"Language?"}
Q1 -->|"C / C++"| Q2{"Need C++ or<br/>modern optimization?"}
Q1 -->|"Assembly only"| VASM["Use vasm + vlink<br/>(vasm_vlink.md)"]
Q1 -->|"Mixed C + ASM"| Q3
Q2 -->|Yes| GCC["Use GCC (bebbo)<br/>(gcc_amiga.md)"]
Q2 -->|No| Q3{"Prefer smaller<br/>binaries?"}
Q3 -->|Yes| VBCC["Use VBCC<br/>(vbcc.md)"]
Q3 -->|No| GCC
GCC --> CMAKE["Configure with CMake"]
VBCC --> CMAKE
VASM --> MAKEFILE["Use Makefile or CMake"]
style GCC fill:#e8f5e9,stroke:#4caf50,color:#333
style VBCC fill:#e8f4fd,stroke:#2196f3,color:#333
style VASM fill:#fff3e0,stroke:#ff9800,color:#333
style CMAKE fill:#f3e5f5,stroke:#9c27b0,color:#333
| Toolchain | Best For | Binary Size | C++ Support | Optimization |
|---|---|---|---|---|
| GCC (bebbo) | C/C++, modern standards, libraries | Larger | C++14 | Strong (GCC 6.5 optimizer) |
| VBCC | C, size-critical code, demos | Smaller | No | Good (vbcc optimizer) |
| vasm only | Pure assembly, bootblocks, demos | Smallest | N/A | Manual |
CMake Configuration
Toolchain File for GCC (bebbo)
Create toolchain/m68k-amigaos.cmake:
# CMake toolchain file for m68k-amigaos-gcc (bebbo)
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR m68k)
# Toolchain prefix
set(TOOLCHAIN_PREFIX m68k-amigaos)
# Find tools
find_program(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc)
find_program(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++)
find_program(CMAKE_ASM_COMPILER ${TOOLCHAIN_PREFIX}-gcc)
find_program(CMAKE_AR ${TOOLCHAIN_PREFIX}-ar)
find_program(CMAKE_STRIP ${TOOLCHAIN_PREFIX}-strip)
find_program(CMAKE_RANLIB ${TOOLCHAIN_PREFIX}-ranlib)
# Amiga-specific flags
set(CMAKE_C_FLAGS_INIT "-noixemul -m68000 -Os -fomit-frame-pointer -Wall")
set(CMAKE_CXX_FLAGS_INIT "-noixemul -m68000 -Os -fomit-frame-pointer -Wall")
set(CMAKE_EXE_LINKER_FLAGS_INIT "-noixemul -Wl,--gc-sections")
# Don't build host executables for tests
set(CMAKE_CROSSCOMPILING TRUE)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# Output suffix for Amiga executables
set(CMAKE_EXECUTABLE_SUFFIX "")
Toolchain File for VBCC
Create toolchain/vbcc-m68k-amigaos.cmake:
# CMake toolchain file for VBCC m68k-amigaos
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR m68k)
# VBCC uses 'vc' as compiler driver
find_program(CMAKE_C_COMPILER vc)
# VBCC flags
set(CMAKE_C_FLAGS_INIT "-m68000 -O2 -c99 -DCPU_68000")
set(CMAKE_EXE_LINKER_FLAGS_INIT "")
# VBCC doesn't have ar/ranlib in standard form
set(CMAKE_AR ${TOOLCHAIN_PREFIX}-ar)
set(CMAKE_RANLIB ${TOOLCHAIN_PREFIX}-ranlib)
set(CMAKE_CROSSCOMPILING TRUE)
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
set(CMAKE_EXECUTABLE_SUFFIX "")
Project CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(MyAmigaApp C)
# Source files
set(SOURCES
src/main.c
src/gfx.c
src/input.c
src/audio.c
)
# Assembly files (vasm, Motorola syntax)
set(ASSEMBLY
asm/copper.s
asm/blitter.s
)
# NDK includes
include_directories(
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/ndk/include
)
# Build executable
add_executable(${PROJECT_NAME} ${SOURCES} ${ASSEMBLY})
# Link against Amiga libraries
target_link_libraries(${PROJECT_NAME} amiga)
# Custom command: generate ADF after build
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_SOURCE_DIR}/scripts/makeadf.sh
$<TARGET_FILE:${PROJECT_NAME}>
${CMAKE_BINARY_DIR}/${PROJECT_NAME}.adf
COMMENT "Creating ADF disk image"
)
Building
# Configure (GCC toolchain)
cmake -B build -DCMAKE_TOOLCHAIN_FILE=toolchain/m68k-amigaos.cmake .
# Configure (VBCC toolchain)
cmake -B build -DCMAKE_TOOLCHAIN_FILE=toolchain/vbcc-m68k-amigaos.cmake .
# Build
cmake --build build -j$(nproc)
# Output: build/MyAmigaApp (Amiga hunk executable)
VS Code Integration
launch.json — Debug with FS-UAE
{
"version": "0.2.0",
"configurations": [
{
"name": "Run in FS-UAE",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/MyAmigaApp",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"preLaunchTask": "build",
"postDebugTask": "launch-fsuae",
"miDebuggerPath": "/usr/bin/m68k-amigaos-gdb"
}
]
}
tasks.json — Build + Launch
{
"version": "2.0.0",
"tasks": [
{
"label": "configure",
"type": "shell",
"command": "cmake",
"args": ["-B", "build", "-DCMAKE_TOOLCHAIN_FILE=toolchain/m68k-amigaos.cmake", "."]
},
{
"label": "build",
"type": "shell",
"command": "cmake",
"args": ["--build", "build", "-j4"],
"group": {
"kind": "build",
"isDefault": true
},
"dependsOn": "configure",
"problemMatcher": "$gcc"
},
{
"label": "launch-fsuae",
"type": "shell",
"command": "fs-uae",
"args": [
"--hard_drive_0=${workspaceFolder}/build/disk.hdf",
"--amiga_model=A1200"
]
}
]
}
settings.json — IntelliSense
{
"C_Cpp.default.compilerPath": "m68k-amigaos-gcc",
"C_Cpp.default.includePath": [
"${workspaceFolder}/include",
"${workspaceFolder}/ndk/include"
],
"C_Cpp.default.defines": [
"AMIGA",
"CPU_68000",
"__AMIGADATE__=20260101"
],
"C_Cpp.default.compilerArgs": ["-noixemul"],
"C_Cpp.default.cStandard": "c99"
}
CI/CD — GitHub Actions
Complete Pipeline: Build, Test, Package
# .github/workflows/amiga-build.yml
name: Amiga Build
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
container:
image: bebbo/amiga-gcc:latest
steps:
- uses: actions/checkout@v4
- name: Configure CMake
run: |
cmake -B build \
-DCMAKE_TOOLCHAIN_FILE=toolchain/m68k-amigaos.cmake \
-DCMAKE_BUILD_TYPE=Release .
- name: Build
run: cmake --build build -j$(nproc)
- name: Strip executable
run: m68k-amigaos-strip build/MyAmigaApp
- name: Create ADF disk image
run: |
pip install amitools
xdftool build/disk.adf format "MyApp" ffs
xdftool build/disk.adf makedir MyApp
xdftool build/disk.adf write build/MyAmigaApp MyApp/MyAmigaApp
- name: Create LHA distribution
run: |
lha a build/MyApp-$(git rev-parse --short HEAD).lha \
build/MyAmigaApp README.md
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: amiga-build
path: |
build/MyAmigaApp
build/disk.adf
build/*.lha
Docker-based Build (Alternative)
# For toolchains not available as Docker images
jobs:
build-vbcc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install VBCC
run: |
wget http://www.sun.hasenbraten.de/~frank/vbcc/vbcc_0_9.tar.gz
tar xzf vbcc_0_9.tar.gz
cd vbcc
make TARGET=m68k
echo "$(pwd)/bin" >> $GITHUB_PATH
- name: Build with VBCC
run: |
vc -m68000 -O2 -c99 -o build/MyApp src/main.c
Mixed C + Assembly Projects
CMake Configuration for Mixed Sources
# Enable assembly language
enable_language(ASM_M68K)
# Tell CMake to use vasm for .s files
set(CMAKE_ASM_M68K_COMPILER vasmm68k_mot)
set(CMAKE_ASM_M68K_FLAGS "-m68000 -Fhunk -phxass")
# Assembly sources get compiled separately
add_executable(${PROJECT_NAME}
src/main.c
src/gfx.c
asm/copper.s # vasm Motorola syntax
asm/blitter.s
)
# Custom include for generated headers from assembly
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_BINARY_DIR}/generated
)
Calling Convention: C ↔ Assembly
/* ---- C side (main.c) ---- */
/* Function implemented in assembly */
extern void SetupCopper(void *copperlist);
extern void BlitCopy(const void *src, void *dst, WORD width, WORD height);
/* Function called FROM assembly */
void OnVBlank(void)
{
/* Called from copper interrupt — keep it short */
UpdateDisplay();
}
; ---- Assembly side (copper.s) ----
; Amiga C calling convention:
; D0/D1 = scratch (return value in D0)
; A0/A1 = scratch
; D2-D7/A2-A6 = saved by callee
; Parameters: D0, D1, A0, A1 (left to right, first 4)
; Stack: remaining parameters
section text,code
xdef _SetupCopper
xdef _BlitCopy
xref _OnVBlank ; defined in C
; void SetupCopper(void *copperlist);
; A0 = copperlist pointer
_SetupCopper:
movem.l d2-d7/a2-a6, -(sp)
move.l a0, a2 ; save copperlist pointer
; ... build copper list ...
move.l a2, $dff080 ; COP1LCH
movem.l (sp)+, d2-d7/a2-a6
rts
; void BlitCopy(const void *src, void *dst, WORD width, WORD height);
; A0 = src, A1 = dst, D0 = width, D1 = height
_BlitCopy:
movem.l d2-d7/a2-a6, -(sp)
; ... blitter setup ...
movem.l (sp)+, d2-d7/a2-a6
rts
Named Antipatterns
"The Host Header Leak" — Including Host Headers
/* BAD: Including standard POSIX headers that don't exist
on AmigaOS. This compiles on Linux (host headers found)
but the resulting binary is broken — Amiga doesn't have
stdio.h in the same way, or <sys/mman.h> at all. */
#include <stdio.h> /* exists in libnix but limited */
#include <sys/mman.h> /* does NOT exist on Amiga */
#include <pthread.h> /* does NOT exist on Amiga */
/* CORRECT: Use AmigaOS-native APIs */
#include <proto/dos.h> /* Open/Read/Write/Close */
#include <proto/exec.h> /* AllocMem/FreeMem */
#include <exec/memory.h> /* MEMF_CHIP, MEMF_CLEAR */
/* Amiga has no mmap — use AllocMem() */
APTR buf = AllocMem(size, MEMF_CHIP | MEMF_CLEAR);
FreeMem(buf, size);
"The ixemul Trap" — Mixing ixemul and noixemul
# BAD: Mixing ixemul and noixemul flags.
# ixemul provides a POSIX layer (fopen, malloc, etc.)
# noixemul means direct AmigaOS calls.
# Linking both causes symbol conflicts and mysterious crashes.
CFLAGS = -noixemul -m68000
LDFLAGS = -lixemul # CONFLICT: ixemul AND noixemul!
# CORRECT: Choose one and be consistent
# Option A: noixemul (most Amiga-native, smaller binary)
CFLAGS = -noixemul -m68000
LDFLAGS = -noixemul -lamiga
# Option B: ixemul (easier porting from POSIX code)
CFLAGS = -m68000
LDFLAGS = -lixemul
"The Missing NDK Include Path"
# BAD: CMake can't find AmigaOS headers.
# The NDK include path must be explicitly provided.
# Without it, #include <exec/types.h> fails.
target_compile_definitions(myapp PRIVATE AMIGA)
# No include path! Compiler errors on every Amiga header.
# CORRECT: Always specify NDK include path
target_include_directories(myapp PRIVATE
${CMAKE_SOURCE_DIR}/ndk/include # NDK 3.9 headers
${CMAKE_SOURCE_DIR}/include # project headers
)
"The Unstripped Binary" — Shipping Debug Symbols
# BAD: Amiga executables with debug symbols waste RAM.
# On an A500 with 512 KB Chip RAM, every byte counts.
# Unstripped binaries can be 2-5× larger.
cmake --build build
cp build/MyApp dist/MyApp # 250 KB — includes symbols!
# CORRECT: Always strip for release
cmake --build build
m68k-amigaos-strip build/MyApp -o dist/MyApp # 80 KB
# Or in CMakeLists.txt:
# set(CMAKE_C_FLAGS_RELEASE "-Os -DNDEBUG")
# add_custom_command(TARGET myapp POST_BUILD
# COMMAND m68k-amigaos-strip $<TARGET_FILE:myapp>)
Historical Context & Modern Analogies
Cross-Platform Amiga Development Timeline
timeline
title Amiga Cross-Compilation Evolution
1985 : Native development only\\nAmiga compiles on Amiga
1990 : SAS/C native IDE\\nStill Amiga-only
1995 : GCC 2.95 cross-compiler\\nLinux→Amiga, unreliable
2005 : Bebbo's GCC fork begins\\nModern cross-compilation
2010 : vasm/vlink mature\\nReliable Amiga assembly
2015 : VBCC amiga target\\nAlternative C compiler
2018 : BlitterStudio Docker\\nOne-command setup
2020 : GitHub Actions CI\\nAutomated Amiga builds
2023 : CMake toolchain files\\nModern build system
2026 : VS Code integration\\nFull IDE experience
Modern Analogies
| Amiga Concept | Modern Equivalent | Notes |
|---|---|---|
m68k-amigaos-gcc |
aarch64-linux-gnu-gcc (ARM cross-compiler) |
Same GCC cross-compilation model |
-noixemul flag |
-nostdlib (embedded ARM) |
Don't link standard library |
| vasm Motorola syntax | arm-none-eabi-as |
Cross-assembler for target |
| vlink hunk output | objcopy -O binary |
Produce target binary format |
| ADF disk image | .img (Raspberry Pi SD card) |
Bootable media for target |
| FS-UAE debugger | QEMU GDB stub | Emulator-based remote debugging |
| CMake toolchain file | arm-toolchain.cmake for embedded |
Identical CMake pattern |
| GitHub Actions + Docker | Same for any cross-compilation target | Containerized builds |
FAQ
Q: Should I use GCC or VBCC for a new project? A: Use GCC if you need C++, modern C features, or extensive library support. Use VBCC if binary size is critical (demos, bootblocks) or you want a simpler, faster compilation. Both produce valid Amiga executables.
Q: Can I use C++ for Amiga development?
A: Yes — bebbo's GCC supports C++14 with some limitations: no RTTI (run-time type info) by default, exceptions are limited, and the C++ standard library (libstdc++) has reduced functionality. Many Amiga projects use C++ for class-based organization but avoid heavy STL usage.
Q: How do I test my cross-compiled binary? A: Use an emulator (FS-UAE or WinUAE) or a real Amiga with a Gotek floppy emulator. For rapid iteration, FS-UAE with a hard-file (.hdf) is fastest — just copy the binary into the HDF and reboot.
Q: Can I debug cross-compiled code with GDB?
A: Limited. m68k-amigaos-gdb exists but requires an Amiga-side GDB stub, which is complex to set up. Most developers use WinUAE/FS-UAE's built-in debugger instead, or printf debugging via serial port.
Q: How do I package my app for distribution?
A: The standard Amiga distribution format is LHA archives. Include the executable, a .info icon file, and optionally an Install script. For floppy-disk distribution, create an ADF using xdftool (from amitools Python package).
Q: Can I use CMake with assembly-only projects?
A: Yes — CMake supports assembly via enable_language(ASM_M68K) and set(CMAKE_ASM_M68K_COMPILER vasmm68k_mot). Alternatively, use a simple Makefile for assembly projects (see makefiles.md).
References
Related Toolchain Articles
- gcc_amiga.md — bebbo GCC cross-compiler: installation, flags, CPU targets, Docker
- vbcc.md — VBCC portable compiler:
__reg(), cross-compilation, size optimization - vasm_vlink.md — vasm assembler & vlink linker: modular architecture, linker scripts
- makefiles.md — Makefile patterns for cross-compilation, mixed C+asm
- ndk.md — NDK versions and header organization
- debugging.md — debugging tools: Enforcer, SnoopDOS, FS-UAE GDB remote
External Resources
- bebbo/amiga-gcc: https://github.com/bebbo/amiga-gcc
- BlitterStudio/amiga-gcc: https://github.com/BlitterStudio/amiga-gcc (with platform build docs)
- amitools: https://github.com/cnvogelg/amitools (xdftool, vamkftools)
- FS-UAE: https://fs-uae.net