Building the project for Amiga systems
Guide how to build, deploy and run Jazz² Resurrection on AmigaOS 3.x, AmigaOS 4.1 and MorphOS.
Three Amiga systems are supported, and one thing they share decides the shape of all of them: none of them has graphics hardware the game's shader pipeline can use. The classic machines have no texturing or blending at all (RTG cards accelerate blits and fills, nothing that helps here), so there the CPU software rasterizer (Software RHI, which the desktop and Vita builds already ship) is not a fallback but the renderer.
The two PowerPC systems do have 3D hardware, and what each of them offers the game is a fixed-function OpenGL 1.x - TinyGL on MorphOS, MiniGL over Warp3D on AmigaOS 4. That is exactly the shape the game's console backends target, so both are driven by the LegacyGL rendering backend (see Hardware rendering through TinyGL), which is the default on both – Software stays selectable for a machine whose card has no 3D driver. Everything below that differs:
| System | Toolchain (CMake toolchain file) | Renderer | Window/input | Audio | Details |
|---|---|---|---|---|---|
| AmigaOS 3.x (m68k) | AmigaPorts' m68k-amigaos-gcc (amiga.cmake) | Software | own backend (Intuition + RTG) | AHI | AmigaOS 3.x (m68k) |
| AmigaOS 4.1 (PowerPC) | adtools (os4.cmake) | LegacyGL (MiniGL) | SDL2 | SDL | AmigaOS 4.1 (PowerPC) |
| MorphOS (PowerPC) | MorphOS SDK (morphos.cmake) | LegacyGL (TinyGL) | SDL2 | SDL | MorphOS (PowerPC) |
The design document behind all three, including the hardware research the classic port is based on, is Docs/AmigaPortDesign.md in the repository.
AmigaOS 3.x (m68k)
The classic Amiga build is a native AmigaOS 3.x/m68k port that covers the whole fast-Amiga spectrum from a single executable: PiStorm/Emu68 accelerators, Apollo Vampire boards and accelerated 68060 machines with an RTG graphics card. The rasterizer presents into a Picasso96/CyberGraphX chunky framebuffer, and what differs between machines is not the code path but the size of the work, which is chosen at startup by measuring the machine — see Performance presets and autodetection.
| Property | Value |
|---|---|
| Toolchain | AmigaPorts' m68k-amigaos-gcc (GCC 16.2, prebuilt release; cmake/toolchains/amiga.cmake) |
| C runtime | libnix (-mcrt=nix20) |
| Rendering backend | Software (pinned – any other value is a configure error), forced to 16-bit surfaces |
| Window/input backend | Amiga (Sources/nCine/Backends/Amiga/) — Intuition screen, IDCMP input, lowlevel.library gamepads |
| Audio backend | AHI (Sources/nCine/Audio/Backends/Amiga/) — software mixer into ahi.device |
| Music | libxmp (NCINE_WITH_XMP, downloaded and built by the CMake configure), reads the original .j2b modules directly |
| Build artifact | build/amiga/dist/ — the Jazz2 executable next to its Content/ tree |
What it runs on
The port targets machines that are, in CPU terms, at or above the consoles the game already runs on. The table below is the honest picture, not a compatibility list.
| Machine | Verdict |
|---|---|
| PiStorm / Emu68 (Pi3A+, CM4, Pi4) | The primary target. Dreamcast-class or better, RTG framebuffer only — exactly what the software renderer wants. |
| Apollo Vampire V4 / V1200 (68080 + SAGA) | Supported, with the AMMX scanline kernels enabled automatically. Expect reductions rather than a full-resolution game. |
| 68060 + RTG card (Picasso IV, ZZ9000, …) | The floor. This is the Low preset — heavily reduced and experimental. |
| 68030/68040, or any machine without an FPU | No. The build refuses to start without an FPU, and the update loop is floating point throughout. |
| Stock AGA machines (no RTG) | Not supported. A chunky-to-planar demake tier is discussed in the design document as a stretch goal, and is not part of this build. |
An RTG screen mode is required: the port opens a CyberGraphX/Picasso96 screen and presents into it. At least 8 MB of fast RAM is realistic – the game itself asks for far more than a stock machine has.
Toolchain
The cross-compiler is AmigaPorts/
# The archive's own directory IS the prefix - the toolchain is relocatable, so unpack it anywhere curl -fsSLO https://github.com/AmigaPorts/m68k-amigaos-gcc/releases/download/v16.2-rc6/m68k-amigaos-gcc-16.2-rc6-Linux-x86_64.tar.xz tar xJf m68k-amigaos-gcc-16.2-rc6-Linux-x86_64.tar.xz -C /opt export AMIGA_INST=/opt/m68k-amigaos-gcc-16.2
The release already carries everything this port compiles against except one library. NDK 3.2, the CyberGraphX and AHI headers** (which the NDK itself does not include) and vasm (vasmm68k_mot, needed only for NCINE_WITH_AMMX – the AMMX kernels are hand-written assembly that no GCC can emit) are all in the archive. What is left is zlib, the one third-party library the game cannot run without:
export PATH="$AMIGA_INST/bin:$PATH" curl -LO https://zlib.net/zlib-1.3.1.tar.gz && tar xf zlib-1.3.1.tar.gz && cd zlib-1.3.1 CC=m68k-amigaos-gcc AR=m68k-amigaos-ar RANLIB=m68k-amigaos-ranlib \ CFLAGS='-mcpu=68060 -O2 -mcrt=nix20' ./configure --prefix=$AMIGA_INST/m68k-amigaos --static make -j$(nproc) libz.a && make install
What the toolchain file sets, and why
cmake/toolchains/amiga.cmake is small, but three of its flags are load-bearing and must not be "cleaned up":
-mcpu=68060with an explicit-m68881. The CPU flag alone does not imply the FPU —m68k-amigaos-gcc -mcpu=68060 -Q --help=targetreports-msoft-float [enabled]— so without-m68881every floating-point operation in the update loop compiles into a libgcc call. Measured under an emulated 68040: 1550 ns per multiply-add soft, 300 ns with the FPU, the integer side of the same benchmark unchanged. It is also an ABI trap, because the libnixlibmthe link resolves to (libm020/libm881/libm.a) is the hard-float one either way: it returns floating point infp0, and soft-float code reads the return value fromd0. That mismatch is what madefminf(),fmaxf(),rint()andnearbyint()return garbage (round()andtrunc()appeared to work only because their result bits are left ind0as well).-print-multi-directoryreports.for this flag combination, which looks alarming, but the link resolves thelibm020/libm881variant of every library — which is what actually matters.-fno-optimize-sibling-calls— this is correctness, not tuning. The m68k-amigaos binutils resolves a sibling call (bra.l, andld's own60FF/61FF→4EB9conversion) to a weak symbol against the wrong address, so any tail call into an inline or template function jumps into garbage. Ordinaryjsrcalls to the same symbols are fine, which is why the mitigation is this narrow.-fno-weakis not an alternative: it also removes weak data*, and template statics such asVector2f::Zerothen fail to link.-std=gnu++17, pinned rather than left to CMake.target_compile_features(cxx_std_17)is a minimum*, so on a compiler that already defaults higher – GCC 16 defaults tognu++20– CMake adds no-stdflag at all and this target alone would build as C++20. Beyond the inconsistency, the toolchain's<math.h>pullsusing std::lerp;into the global namespace under C++20, which is ambiguous against the engine's ownnCine::lerpat every call site.
The toolchain also force-includes cmake/toolchains/amiga-libstdc++-c99.h into every C++ translation unit, the same arrangement the PlayStation 3 build uses: the m68k-amigaos libstdc++ is configured without _GLIBCXX_USE_C99_MATH_TR1, so <cmath> declares none of the C99 maths set inside std:: even though the library provides it globally.
Preparing the content tree
Unlike the consoles that play from read-only media, an Amiga has somewhere to write: Source and Cache are resolved next to the executable, so an original Jazz Jackrabbit 2 installation copied into Source is converted on the machine on the first start, exactly as on the desktop. It is only slow* — the conversion decodes every animation, tileset and level and writes a compressed package out of them, which is minutes of work on a desktop CPU and a great deal more on a 68060 — so the build is normally pointed at a prebaked tree produced once on the host with AssetPacker instead:
cmake -B ./build/amiga/ -D CMAKE_TOOLCHAIN_FILE=./cmake/toolchains/amiga.cmake \ -D CMAKE_BUILD_TYPE=Release -D NCINE_CONTENT_DIR=./build/ConsoleContent
Point NCINE_CONTENT_DIR at the repository's Content directory instead and the build carries only the base assets — the game then starts and shows the "content missing" message until an original installation is placed in Source next to it. Neither route has been timed on real hardware yet.
Building
export AMIGA_INST=/opt/amiga export PATH="$AMIGA_INST/bin:$PATH" cmake -B ./build/amiga/ -D CMAKE_TOOLCHAIN_FILE=./cmake/toolchains/amiga.cmake \ -D CMAKE_BUILD_TYPE=Release -D NCINE_CONTENT_DIR=./build/ConsoleContent cmake --build ./build/amiga/ --parallel $(nproc)
AMIGA_INST is the toolchain prefix, and it is checked before anything is configured: the toolchain file wants $AMIGA_INST/bin/m68k-amigaos-gcc and the NDK headers to be there, with zlib installed beside them (see Toolchain). The AmigaOS 3.x job in .github/workflows/amiga.yml is the same recipe end to end – download the release, cross-compile zlib, configure, build – if an unattended one is what is wanted.
The build stages build/amiga/dist/, which is the directory to copy to the Amiga: the Jazz2 executable with its Content/ tree next to it. Jazz2.config is written into that same directory, which is the conventional home of a program's settings on AmigaOS.
Two notes specific to this target. Link-time optimization is off (NCINE_LINKTIME_OPTIMIZATION) and Release compiles at -O2 rather than -O3, for the same reason as the consoles: this is the least-tested compiler in the project and code size matters. And threads are off — there is no std::thread on m68k-amigaos — so everything, including audio decoding, runs on the main thread.
Deploying and running
On real hardware, copy dist/ anywhere on a hard drive and run the executable from a Shell or its icon. It opens its own screen and closes it on exit. The stack is set from the executable's own __stack cookie (1 MB), so no Stack command is needed, but a Stack 1048576 before it does no harm if you start it from a script.
For development the port is exercised under FS-UAE with AROS 68k, whose open-source Kickstart replacement ships CyberGraphX and AHI:
# jazz2.fs-uae - AROS boots to Workbench in about ten seconds like this amiga_model = A4000 cpu = 68040 jit_compiler = 0 kickstart_file = /path/to/aros/boot/amiga/aros-rom.bin hard_drive_0 = /path/to/aros/dh0 hard_drive_1 = /path/to/jazz2/build/amiga/dist
The game writes its log to standard output unbuffered, so redirecting it to a file on the emulated disk gives a complete trace even after a crash:
Jazz2:Jazz2 >Jazz2:jazz2-log.txt
A build configured with -DDEATH_DEBUG=ON also accepts /level <episode>/<level>, which boots straight into a level and is by far the fastest way to exercise the renderer:
Jazz2:Jazz2 /level prince/01_castle1 >Jazz2:jazz2-log.txt
Performance presets and autodetection
There is no configuration to get right before the game starts. AmigaPlatform::RunPerformanceProbe() times a 16-bit blend loop and an FPU dependency chain at startup and picks one of four presets, which then drive the screen mode and the audio mixing rate:
| Preset | Pixel budget (screen mode is chosen to fit) | Mixing rate |
|---|---|---|
Ultra | 640×512 | 44100 Hz |
High | 640×360 | 44100 Hz |
Medium | 384×300 | 22050 Hz |
Low | 320×220 | 22050 Hz |
The screen mode is not requested by number: the display database is searched for the RTG mode whose logical* resolution (the 720×405-clamped aspect fit the game renders at) is largest within the preset's pixel budget. That is why a 640×400 mode is chosen over a 640×480 one where both exist — it yields more logical pixels for less work.
Two environment variables override what the machine reports, which is what you want when bringing the port up on a new accelerator:
| Variable | Effect |
|---|---|
NCINE_PRESET | low, medium, high or ultra — forces the preset instead of measuring it. |
NCINE_NO_AMMX | Set to anything to keep the scalar scanline kernels on an Apollo 68080. The AMMX kernels are written to be bit-identical to the scalar ones (SwBackendHarness verifies that on hardware), so this is a diagnostic, not a workaround anyone should need. |
Limits and known issues
- An FPU is required, and on a 68040/68060 so is its support library. The binary is compiled for hardware floating point (
-mcpu=68060 -m68881) and the game's update loop is floating point throughout, so it exits with a message rather than trapping on a machine without an FPU at all. Neither the 68040 nor the 68060 implements the whole 68881 set in silicon, though —FMOVECRand the extended-precision forms GCC emits are left to Motorola's FPSP — soLibs:68040.library(resp.68060.library) has to be installed forSetPatchto load. Every accelerator ships it and Workbench 3.5/3.9 include it, but a bare Workbench 3.1 does not, and the symptom there is an immediateRecoverable Alert 0006 8881rather than anything the game gets to report. The Apollo 68080 and Emu68 implement the instructions natively and need nothing. - RTG only. There is no chunky-to-planar path, so an AGA-only machine cannot run this build.
- No threads.
WITH_THREADSis off, so audio streaming decodes synchronously on the main thread and the multiplayer client/server are not built. - Music is libxmp, not libopenmpt. libopenmpt's mixer is written for machines two orders of magnitude faster than a 68060. libxmp reads the original
.j2bGalaxy modules directly, so the soundtrack needs no conversion - what it cannot read is MO3, so the four.mo3tracks are silent here (the PSP shares both facts, see Limits and known issues). The library is downloaded and built by the CMake configure (cmake/Findlibxmp.cmake) rather than vendored. ahi.deviceimplementations disagree about queueing. The AHI autodocs describe a queue in which a request carryingahir_Linkstarts the moment the one before it finishes – AROS 68k's device accepts such a request and then never plays or replies to it, and it also stops servicing a queue that has run dry — which one long level-loading frame is enough to cause. The backend therefore measures linking at startup with two throwaway requests of its own and restarts a queue that has not retired anything for a second. Both mechanisms are inert on a device that behaves as documented – the log lineAudio device initialized: …states which mode is in use.- libnix does not ship every C99 function.
exp2,exp2f,log2f,strnlenandwcstoulare missing entirely and are defined inSources/nCine/Backends/Amiga/AmigaLibcCompat.c, along with the POSIX entry points AmigaOS has no equivalent for (flock,fchmod,getpwuid,realpath,gethostname) and__xpg_strerror_r, which libstdc++ references by name. What is not in that file any more is a replacement forround,trunc,fmin,fmax,rintornearbyint: those returned garbage under the old soft-float flags and are correct as libnix ships them once the build is hard-float (see What the toolchain file sets, and why). - Emulator timings are worth exactly one thing: A/B comparisons. FS-UAE interpreting a 68040 runs gameplay at well under one frame per second, and its absolute numbers say nothing about a PiStorm — but they are stable and they scale with the code, which is enough to compare two builds of the same thing. The startup probe is the instrument: it prints
… ns/composited pixel, … ns/FPU multiply-addinto the log on every boot, and the soft-float regression above was found by reading the second of those two numbers. Note that the integer figure it reports under emulation (around 330 ns/pixel here) happens to land in the same band as a real 68060/50, while the emulated FPU does not — so the two halves of that line are not comparable with each other. Anything that is not a comparison between two builds belongs on hardware.
AmigaOS 4.1 (PowerPC)
AmigaOS 4 needs no backend of its own: it has a maintained SDL2, so the window and input side is the SDL2 backend the desktop builds use, and audio is the SDL backend (Sources/nCine/Audio/Backends/SDL/) - the same software mixer as the classic Amiga's, handing its blocks to SDL_QueueAudio() instead of to ahi.device, because the AmigaOS 4.1 SDK has no OpenAL.
Hardware rendering through MiniGL
The renderer is the LegacyGL backend on MiniGL, the SDK's fixed-function OpenGL over Warp3D (libGL.a plus minigl.library – SDL2's own SDL_opengl.h includes its headers on this platform). It is the same backend MorphOS uses for TinyGL, so Hardware rendering through TinyGL describes what it does – two things differ here:
- MiniGL has the texture combiners it needs - it advertises
GL_EXT_texture_env_combine, whose enumerants are the ARB ones under_EXTnames (GL_COMBINE_EXTisGL_COMBINEis0x8570) - but it has no framebuffer objects at all, not even entry points to call. So the render-target path is not probed here the way it is on TinyGL: the copy-back form is compiled in and the framebuffer-object one is compiled out (RHI_LEGACYGL_HAS_FBOinLegacyGlApi.h). - Mirrored repeat is not offered, so that wrap mode degrades to a plain repeat rather than being sent to a driver that would answer
GL_INVALID_ENUMand leave the sampler as it was.
The other road to this hardware is Warp3D Nova's ogles2.library, which the engine's ES2 profile (NCINE_RHI_GL_PROFILE=ES2 - the one the PS Vita's vitaGL build uses) would match - that would be the shader pipeline rather than this fixed-function one. What is missing is not engine support but the SDK: it carries no GLES2 headers or link library for it. Software remains selectable (-D NCINE_PREFERRED_RHI=Software) for a machine with no 3D driver installed – the CPU on these machines is PC-class, so it is a real option rather than a compromise.
Toolchain and building
The cross-compiler is adtools (ppc-amigaos-gcc 11.5) together with the AmigaOS 4.1 SDK. Both come in one maintained container image, so nothing has to be installed - run the build inside it with the repository mounted at its own path:
docker run --rm -v "$PWD:$PWD" -w "$PWD" docker.io/walkero/amigagccondocker:os4-gcc11 bash -lc ' cmake -B ./build/os4/ -D CMAKE_TOOLCHAIN_FILE=./cmake/toolchains/os4.cmake \ -D CMAKE_BUILD_TYPE=Release -D NCINE_CONTENT_DIR=./build/ConsoleContent cmake --build ./build/os4/ --parallel $(nproc)'
(Podman takes the same arguments – add --security-opt label=disable where SELinux is enforcing.) The toolchain file expects the adtools prefix at the container's /opt/ppc-amigaos – set OS4_INST to build against a host installation instead. Either way the result is staged into build/os4/dist/ - the Jazz2 executable next to its Content/ tree, copied to the machine as it is. The executable is stripped on the way: this toolchain leaves DWARF in even in a Release build, which is two thirds of its size.
The same two commands run in CI, in the container job of .github/workflows/amiga.yml.
What differs from a desktop build
- No online multiplayer. The bsdsocket stack here is IPv4-only - there is no
<netinet/in6.h>and nostruct in6_addrin the SDK - while the bundled ENet is built around IPv6 addresses with IPv4-mapped ones inside them. The WebSocket transport is out for a smaller reason: IXWebSocket includes<poll.h>, and the SDK has none (the OS offersselect()andWaitSelect()). Local splitscreen is unaffected. - Threads are on, unlike the classic Amiga and MorphOS, but the engine's synchronisation layer calls the pthread API directly and this toolchain's
-athread=pthreadflavour cannot be used - itsgthr-amigaos-pthread.ois not shipped. The build therefore uses-athread=nativefor the compiler's gthreads and links the SDK's ownlibpthread. Thread affinity, thread names, barriers andpthread_kill()are all missing from that library, so those paths are compiled out. libatomicis linked explicitly: this is a 32-bit PowerPC, so a 64-bit atomic load is a library call rather than an instruction.-Uamigais part of the machine flags. The compiler predefines bothAMIGAand a lowercaseamiga, the way ancient compilers predefinedunix, and the lowercase one breaks any C++ with an identifier of that name - libopenmpt hascommon_encoding::amiga.
MorphOS (PowerPC)
MorphOS is built the same way as AmigaOS 4 - SDL2 for window and input, the SDL audio backend, and the same LegacyGL renderer on the system's own fixed-function OpenGL (Hardware rendering through TinyGL) - with a different toolchain and a different set of system quirks. What TinyGL does not have is shaders, which is why that backend is a fixed-function one rather than the engine's OpenGL backend.
Toolchain and building
The MorphOS SDK (ppc-morphos-gcc 11.3, at /gg) comes as a container image as well. One thing has to be built before the game itself: the SDK ships no zlib, and the game cannot run without it (PakFile).
docker run --rm -v "$PWD:$PWD" -w "$PWD" -e MORPHOS_DEPS=/tmp/morphos-deps \ docker.io/walkero/amigagccondocker:mos-gcc-amd64 bash -lc ' curl -fsSL https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz | tar xz -C /tmp cd /tmp/zlib-1.3.1 CC=ppc-morphos-gcc CFLAGS="-O2 -noixemul" ./configure --prefix=$MORPHOS_DEPS --static make -j $(nproc) libz.a && make install cd '"$PWD"' cmake -B ./build/morphos/ -D CMAKE_TOOLCHAIN_FILE=./cmake/toolchains/morphos.cmake \ -D CMAKE_BUILD_TYPE=Release -D NCINE_CONTENT_DIR=./build/ConsoleContent cmake --build ./build/morphos/ --parallel $(nproc)'
MORPHOS_DEPS is where the toolchain file looks for cross-compiled dependencies – MORPHOS_SDK points at an SDK somewhere other than /gg. The result is staged into build/morphos/dist/, and .github/workflows/amiga.yml runs these same steps in CI.
Running it, and what has to be installed first
MorphOS ships no SDL2 - the SDK's libSDL2.a is a stub that opens sdl2.library at load time, and that library is a separate download ("SDL 2.32.11 Libraries" on MorphOS Storage, which also carries sdl2_image/mixer/ttf). Without it in LIBS: the game does not start at all: the loader puts up a requester before main() runs. Nothing else has to be installed - the port deliberately does not depend on a TCP/IP stack (see the note about bsdsocket.library below).
The port can be tested without MorphOS hardware, because QEMU emulates the Pegasos II that MorphOS runs on, and its built-in open firmware boots the MorphOS ISO directly:
# boot.img is extracted from the top level of the MorphOS ISO qemu-system-ppc -machine pegasos2 -m 1024 -rtc base=localtime -device ati-vga,romfile="" -cdrom morphos-3.20.iso -kernel boot.img -serial stdio
Hardware rendering through TinyGL
MorphOS' 3D interface is TinyGL (tinygl.library, part of the system on machines whose graphics card it supports - Radeon, Voodoo, Permedia). It is an OpenGL 1.x with the 1.3 texture combiners, framebuffer objects and vertex arrays, and no programmable stage at all. That is exactly the shape the game's console backends target, so it is served by a backend of the same family rather than by the engine's OpenGL one:
It is the default on both PowerPC Amigas, so an ordinary build already uses it – the CPU rasterizer is one option away for a machine whose graphics card has no 3D driver:
cmake -B ./build/morphos/ -D CMAKE_TOOLCHAIN_FILE=./cmake/toolchains/morphos.cmake \ -D CMAKE_BUILD_TYPE=Release -D NCINE_PREFERRED_RHI=Software
LegacyGL (Sources/nCine/Graphics/RHI/LegacyGL/) is a fixed-function backend in the same sense as the Dreamcast's PowerVR or the PSP's GE one: it consumes the same transpiled fixed_function effect tables (Shaders/Generated/LegacyGlGeneratedEffects.h) and expresses each effect's texture stage as a GL_COMBINE program, so the game runs the direct tier - the scene goes straight to the display at the logical resolution and the CPU lightmap is composited by the device instead of a shader pass. Three things about it are worth knowing:
- Indexed textures are baked, not palettised. The consoles resolve an index through hardware (CLUT, TLUT, palette bank) – legacy GL has no such thing left (
GL_EXT_paletted_textureis gone from every driver and TinyGL never had it), so every indexed image is baked through its palette row into RGBA8 before it is uploaded. - Textures are padded to powers of two where the GL insists on them. Sampling a non-power-of-two texture is a GL 2.0 feature, so both that and
GL_MAX_TEXTURE_SIZEare probed at startup (the reported limit is capped at 2048 - a desktop driver answers 16384, which is not a size anything here should build). Tileset atlases are laid out 15 tiles to a row and chunked 512 texels tall, exactly as on the fixed-function consoles, so a chunk is one texture whether or not the padding applies – anything larger that is prebaked is split into pages internally rather than rejected.NCINE_NO_NPOTin the environment forces the padded path. - Render targets prefer a framebuffer object and fall back to copying. Whether FBOs work is probed at startup rather than assumed - TinyGL declares the entry points unconditionally - and where they do not, an off-screen pass is drawn into the back buffer and lifted into its texture with
glCopyTexSubImage2D(), which is a GL 1.1 operation. SettingNCINE_NO_FBOin the environment forces that path, which is how it can be exercised on a desktop.
What differs from a desktop build
The MorphOS SDK's headers are the C-era Amiga ones, and three of their habits collide with a modern C++ codebase. All three are handled in cmake/toolchains/morphos.cmake and cmake/toolchains/morphos-compat.h, and they are worth knowing before touching that file:
- The
ppcinlineheaders turn OS calls into function-like macros, and some of those names are ordinary C++ identifiers.bindis the fatal one:<unistd.h>pulls inproto/socket.h(zlib.h includes<unistd.h>, so this reaches almost every file) and the macro then rewritesstd::bindinside libstdc++'s own<functional>. The build defines_NO_PPCINLINE, which keeps the prototypes and drops the macros, and force-includesmorphos-compat.hfor what that leaves undeclared (FD_ZEROexpands tobzero). proto/exec.handproto/usergroup.hputstruct Taskin the global namespace, which is also the name of the engine's coroutine type - every unqualifiedTask<...>in the game then becomes ambiguous. Both are skipped by defining their include guards –FindTask(), whichgetpid()is a macro over, is declared by hand inmorphos-compat.hwith an opaque return type so thatTaskstays out of the global namespace.- Threads are off for the same reason, and this one cannot be worked around: MorphOS's
<pthread.h>includes<exec/semaphores.h>, so merely using pthreads bringsstruct Taskback. The port runs single-threaded, exactly like the classic Amiga - SDL2's own headers are clean, so nothing else is affected. INT32_MINand friends are still behind__STDC_LIMIT_MACROShere, the pre-C++11 rule, so the toolchain asks for them explicitly.realpath(),fsync(),flock()andwcstoul()are declared by the C library and implemented by none of it – the first three are compiled out andwcstoulis provided inSources/nCine/Backends/MorphOS/MorphOSLibcCompat.cpp, along with a definition of libstdc++'s__throw_bad_array_new_length(GCC 11 emits calls to it, the SDK's libstdc++ predates it).- TinyGL is reached through two globals in the program,
TinyGLBaseand__tglContext, because the SDK'sgl*names are macros that read them (GLFoo(__tglContext, …), the library base supplying the call vector). sdl2.library cannot fill them in - it is a shared library and they live in the program - so theLegacyGLbuild does it: the link asks for the SDK's own TinyGL initializer by name (-u _CSTP_init_TinyGLBase, which nothing would otherwise pull out oflibGL.abecause SDL2's glue already declares the same global), andSources/nCine/Backends/MorphOS/MorphOSTinyGl.cpppoints__tglContextat the contextSDL_GL_CreateContext()returned. gethostname()andgetpwuid()are not called here, and neither is libcurl linked. All three reachbsdsocket.libraryorusergroup.library, and a reference to one of those is enough for the loader to open it before the program starts - so a machine with no TCP/IP stack running would refuse to start the game, over a host name that only decorates a log line. The update check goes with them – online multiplayer is off here anyway.