Skip to content

Amiga port design

Simon Dick edited this page Jun 27, 2026 · 17 revisions

MicroPython port to AmigaOS 3.x — Implementation Plan

This page is the index and architecture reference for the AmigaOS 3.x MicroPython port. The per-phase implementation log has been split into individual pages — each phase in the status table below links to its own page. For an upstream-style introduction to the port, see ports/amiga/README.md in the source tree. The full testing runbook lives on Amiga port testing.

Overview

Port of MicroPython to AmigaOS 3.x on Motorola 68k (68020+). CLI-driven REPL with file-system access, based on the ports/minimal template.

Phase status

Each phase links to its detail page. The small foundational phases are grouped onto two era pages (Phases 0–11 — Foundation, Phases 13–27 — Platform integration); the substantial phases each have their own page.

# Phase Status
0 Toolchain — bebbo GCC
1 Skeleton — builds and runs in Amiberry
2 File system access and import
3 Standard MicroPython library modules
4 Amiga-specific amiga C module
5 68k native code emitter
6 Package imports
7 Ctrl+C interrupt handling
8 Native AmigaOS API migration
9 Networking via bsdsocket.library
10 Command-line argument parsing
11 CI build workflow
12 68k native emitter rework (register NLR + big-endian byte order)
13 Interactive line editing at the REPL
14 Dynamic heap growth
15 withdrawn
16 Pythonic file I/O via VFS
17 Native AmigaOS library access (amiga.library) ✅ (step 7 deferred)
18 ARexx integration (inbound + outbound)
19 Workbench launch support
20 Env-var integration (os.getenv/putenv/unsetenv)
21 Volume / assign introspection
22 AmigaDOS pattern matching
23 timer.device-backed timing
24 Persistent REPL history
25 Extra break signals
26 PROGDIR: on sys.path
27 Additional build variants
28 TLS/SSL via AmiSSL v5
29 urequests frozen HTTP/HTTPS client
30 Intuition requester dialogs (amiga.intuition)
31 ASL file requester (amiga.asl)
32 ARexx polish (rexx_exists / rexx_list / persistent RexxClient)
33 platform.amiga_info() + frozen platform.py
34 Frozen os.py extensions + AmigaOS-aware os.path
35 amiga.icon.info file read / write / manipulation
36 amiga.cataloglocale.library catalog lookup
37 amiga.datatypesdatatypes.library file recognition planned (low priority)
38 ports/amiga/README.md for upstreaming
39 Additional extmod feature opt-ins in progress

Non-goals (initially)

  • Full Workbench GUI / Intuition windows — Phases 30/31 add modal requesters only; arbitrary window/widget surfaces stay out of scope
  • 68000 alignment-safe build — target 68020+ first

Surface-compatibility policy with other Amiga Python ports

Two other Python-on-Amiga efforts exist and surface in design conversations:

  • OoZe1911/micropython-amiga-port — a parallel MicroPython AmigaOS 3.x port. Different deltas (depth of amiga.* C surface, the Phase 5 native emitter, three-variant CPU/FP matrix, dynamic heap, the library proxy + .fd trampoline) but a converging top-level shape (amiga.intuition, amiga.asl, urequests, platform, ARexx surface).
  • OS4 Python — a port of CPython 2.x to AmigaOS 4 / PowerPC. Six Amiga-specific module names documented on wiki.amigaos.net: amiga, arexx, asl, catalog, icon, installer.

Policy: surface-compatible where it's cheap, not byte-compatible.

  • Match their module names (amiga.intuition, amiga.asl, amiga.icon, amiga.catalog, etc.) so users moving scripts between ports find familiar import paths.
  • Match their function signatures where the underlying AmigaOS API fits cleanly. Phases 30–36 mirror OoZe / OS4 shapes deliberately.
  • Where their choice doesn't fit OS3 / Python 3 / MicroPython (Posix-style chmod translation in Phase 34; OS4-interface- based library calls; CPython-2 stdlib expectations), pick the shape that fits our target and document the divergence in the per-phase out-of-scope list.

Don't promise true compatibility:

  • OS4 Python is CPython 2.x with the full CPython stdlib and C-extension ecosystem. We're MicroPython on OS3 / 68k. Any non-trivial OS4 Python script will hit Python-2 idioms or stdlib modules we can't ship; mirroring AmigaOS-specific module names doesn't change that.
  • OoZe1911 has feature deltas we've deliberately chosen not to match (smtplib + email module, OS-side path conventions baked deeper than ours). When their choice is reasonable, we copy. When ours is materially better (the library proxy, native emitter, timer-backed timing), we keep ours.

The win from surface-compat is low friction — someone with an import amiga.icon; icon.read(...) script from OS4 has a chance of running it without edits. The win from not over-committing is avoiding broken promises about strict portability.

CPU and ABI

  • 68k big-endian, 32-bit. Minimum 68020 (unaligned access).
  • AmigaOS register-based calling convention for OS calls; standard C ABI otherwise.
  • MP_ENDIANNESS_LITTLE = 0 (auto-detected from GCC's __BYTE_ORDER__).

Toolchain

bebbo's GCC (m68k-amigaos-gcc), GCC 6.5.0b. Produces native AmigaOS HUNK executables.

Recommended: container-based build. tools/amiga-build.sh runs the build inside stefanreinauer/amiga-gcc:latest — the same image CI uses, so local and CI binaries are bit-identical. Output lands in the standard ports/amiga/build-<variant>/ paths so tools/amiga-vamos-run.sh and friends find the binaries unchanged. Files are written as the host user (via --user), not root.

tools/amiga-build.sh                   # all four variants
tools/amiga-build.sh standard          # one
tools/amiga-build.sh standard 68040    # several
tools/amiga-build.sh clean             # clean all build dirs

Native install (alternative). Install from https://franke.ms/git/bebbo/amiga-gcc:

git clone https://franke.ms/git/bebbo/amiga-gcc
cd amiga-gcc
make PREFIX=/opt/amiga all     # installs gcc, binutils, clib2, NDK
export PATH=/opt/amiga/bin:$PATH

make min does not install C library headers and cannot compile MicroPython. Always use all or at minimum clib2 ndk. Don't interleave native and container builds in the same mpy-cross/build/ without running tools/amiga-build.sh clean first — the host and Linux mpy-cross binaries share that path and aren't compatible.

Exception handling (NLR)

Register-based NLR: py/nlr.h auto-detects __m68k__MICROPY_NLR_M68K, implemented in ports/amiga/nlr68k.S + nlr68k_jump.c (Phase 12a). This replaced the original setjmp fallback, which was incompatible with the native emitter's try/except/with handling — see Phase 12.

GC and heap

Phase 14 manages a dynamic, growable heap of AllocVec'd chunks. See Phase 14. GC stack scan runs from current SP up to gc_stack_top (captured in main() at startup) — tc_SPUpper would be principled but vamos leaves it zero on the initial process and the resulting ~4 GB scan walks off into unmapped memory.


Port file structure

ports/amiga/
├── Makefile              # Build rules and toolchain config
├── mpconfigport.h        # Feature flags and type definitions
├── mphalport.h/.c        # HAL stubs + console I/O
├── main.c                # Entry point, gc_collect(), heap pre-scan
├── vfs_amiga.c           # VfsAmiga + FileIO/TextIOWrapper
├── sysstdio.c            # mp_sys_stdin/stdout/stderr stream objects
├── floatconv.c           # bebbo soft-float library bug fixes
├── modjson.c             # Port-local json.loads bypass
├── modamiga.c            # The `amiga` C module
├── modos.c               # os.getenv/putenv/unsetenv (Phase 20)
├── modsocket.c           # socket module (Phase 9)
├── amiga_timer.c         # timer.device + EClock timing (Phase 23/25)
├── amiga_history.c       # Persistent REPL history (Phase 24)
├── amiga_lib_call.S      # Library trampoline (Phase 17)
├── qstrdefsport.h        # Port-specific interned strings
├── manifest.py           # Frozen modules (amiga.py + _amiga_fd etc.)
├── modules/              # Frozen Python modules
└── variants/{standard,68020fpu,68040}/

Investigation items

Speculative work that isn't yet a phase. Each entry is a sized cost/benefit sketch so the call can be made later in context.

ClassAct / ReAction GUI binding

ClassAct is the BOOPSI-based GUI toolkit that shipped on Aminet in the mid-90s and was later acquired and folded into the OS as ReAction (AmigaOS 3.5 / 3.9 / 4.x). Same class hierarchy, same tag-driven object model, just rebranded and bundled in SYS:Classes/. On a stock OS 3.5+ install — and on every modern AmigaOS 4.x and MorphOS system — it is the default toolkit for native windowed UIs (the post-OS-3.0 counterpart to raw intuition.library gadget lists and the MUI alternative on systems without MUI installed).

The port currently exposes only modal Intuition requesters (Phase 30) and the ASL file requester (Phase 31). A real windowed UI surface — list browsers, button panels, layout groups, draggable windows that survive a Workbench depth-arrange — would need either ClassAct/ReAction or MUI. ClassAct is the more attractive of the two for "ships with the OS" reasons.

What it would expose (sketch):

from amiga import reaction as ra  # or amiga.classact — same lib

win = ra.Window(
    title="Hello",
    inner=ra.Layout(orientation="vertical", children=[
        ra.Button(label="OK",  id=1),
        ra.Button(label="Quit", id=2),
    ]),
)
win.open()
for msg in win.events():           # IDCMP pump
    if msg.id == 2:
        break
win.close()

Underlying ABI: NewObject(class, NULL, tag, value, ..., TAG_END) returning a BOOPSI Object *, then SetAttrs / GetAttr / DoMethod against it. Window objects expose an IDCMP signal mask that integrates with the existing Wait() loop the REPL already uses for Ctrl+C and timer.device.

Cost estimate:

  • ~30 BOOPSI classes worth wrapping for a useful subset (window.class, layout.gadget, button.gadget, string.gadget, checkbox.gadget, chooser.gadget, listbrowser.gadget, label.image, bitmap.image, getfile.gadget / getfont.gadget / getscreenmode.gadget, plus a handful of menu / scroller / palette classes).
  • Each class is a NewObject(class_lib->cl, ...) open plus a tag-table translator (Python kwargs → struct TagItem[]). The Phase 17 tag.py infrastructure already does the hard half of this — adding a class is roughly "open the library, add its tag IDs to the catalogue, write a thin Python wrapper". Call it ~200 lines per class for a polished surface, ~50 if we go barebones tag-passthrough.
  • Event loop: DoMethod(win, WM_HANDLEINPUT, &result) returns a 32-bit code whose meaning is class-defined. Workable but needs a Python-side dispatcher table.
  • Layout engine: layout.gadget does the sizing for us, so we don't need to reimplement Intuition's brutal hand-positioning story. Big win.
  • Binary cost: the classes themselves are external .class files in SYS:Classes/Gadgets/ etc., so the port-side binary is just glue — probably 8–15 KB text for a useful subset.

Open questions:

  1. OS version floor. ClassAct shipped standalone for OS 3.0+; ReAction is OS 3.5+. The port currently targets V37 (OS 2.04) as the floor for most features. A ClassAct binding would either bump the floor for the GUI surface specifically (lazy open, OSError(MP_ENOSYS) on older Kickstarts) or require shipping the ClassAct .class files alongside the port. The former is almost certainly the right call.
  2. AmigaOS 4 / MorphOS divergence. OS4 ships ReAction with subtle tag-name differences from OS3 ClassAct (WINDOW_Position vs WA_Left / WA_Top etc.). Need to confirm tag IDs are identical or paper over per-OS via the tag catalogue.
  3. Event loop integration. Right now the REPL's Wait() loop only knows about the console signal mask, Ctrl+C, and timer.device. A ClassAct window adds its own signal bit (win.sigmask) and would benefit from being multiplexed into the same Wait() so the REPL doesn't busy-poll. Plumbing this cleanly is what the deferred scheduler-lite story is partly about (see Phase 39 step 6 webrepl); the two share a need.
  4. MUI overlap. A subset of the Amiga community runs MUI as their preferred toolkit, with ClassAct/ReAction as fallback. If we ship a ClassAct binding, MUI users get a working GUI surface either way (ClassAct/ReAction installs cleanly on MUI systems). The reverse isn't true. So ClassAct first is the right ordering even for MUI shops.
  5. Required vs. headline classes. A Hello world window needs window.class + layout.gadget + button.gadget at minimum; that's the "is this even tractable" milestone. Everything else is incremental.

Verdict: likely a high-value phase if a real caller shows up (Amiga-side tooling that wants a tidier UI than ASL + EasyRequest), but it's a multi-step effort — probably split as "Phase 4x — amiga.reaction BOOPSI plumbing + 3-class proof-of-life" then "Phase 4y — polished class coverage". Worth a follow-up investigation pass to confirm tag-ID parity between OS3 ClassAct and OS4 ReAction (item 2 above) before committing — that's the single biggest risk to the binding working unchanged across the supported OS range.

Third-party header → drop-in Python constants tool

The port already mechanically harvests constants from the NDK for the built-in libraries via three host-side tools: amiga-fdgen.py (LVO + register tables from .fd files), amiga-taggen.py (resolves TAG_USER + N tag macros), and amiga-structgen.py (struct layouts via sizeof() / offsetof() against a whitelist). The output is baked into the frozen modules at build time — _amiga_libs.py, _amiga_tags.py, _amiga_structs.py.

That covers NDK-shipped libraries (intuition, dos, asl, ...) but leaves a real gap: anyone calling a third-party library (thirdparty.library, MUI, AHI, cybergraphics.library, identify.library, the various Aminet .library releases, the user's own .library) has to either:

  1. Hand-translate the C header to Python literals — brittle, error-prone, no machine-checkable link back to the source.
  2. Drop magic numbers inline in their script — the same problem one fork further down.
  3. Ship a hand-written shim alongside the library — works, but every distributor reinvents the wheel.

Proposed tool: amiga-headergen (working title) — a host-side script that takes a path to a SDK:Include/ (or equivalent) tree plus a list of .h files to ingest, and emits a single .py file the caller drops into PROGDIR: (or anywhere on sys.path). The emitted module exposes:

# AmiSSL example — pretend this isn't already wired into the port.
from openssl_consts import (
    SSL_VERIFY_PEER,
    SSL_OP_NO_SSLv3,
    OpenSSLBase_LVO_SSL_new,    # LVO offset, ready for amiga.lib_call
    SSL_CTX_struct,             # offsetof / sizeof dict, structgen shape
    OPENSSL_TAG_BASE,           # tag-base value if the library uses TAG_USER+N
)

How the tool would work:

  • Run the bebbo cross-cpp over a generated probe .c to resolve #define chains to literal values, the same trick amiga-taggen.py already uses for TAG_USER + N.
  • For #define FOO 0x1234 constants: emit FOO = 0x1234 directly. Skip #define macros with arguments (those are function-shaped and not portable to Python without a wrapper — surface them as # FOO: function-like macro, skipped so the user knows).
  • For struct definitions found in the header: reuse the amiga-structgen.py offsetof() / sizeof() probe to produce (name, type_code, offset) tuples consumable by amiga.uctypes_lite / Phase 17 struct-ctypes. Inline fixed-byte char arrays harvest their length via sizeof(((X*)0)->field) just like structgen does today.
  • For .fd files paired with the header: pull LVO + register table via the same engine as amiga-fdgen.py and emit LIBNAME_LVO_Func plus a (reg_map, signature) tuple per function. Caller passes those straight to amiga.lib_call.
  • For tag bases (#define LIB_TagBase (TAG_USER + 0x80000)), resolve and emit as plain ints.
  • Emit a header block at the top of the .py listing the source headers, their last-modified mtimes, and the tool version — so a stale generated file is detectable at a glance.
  • Optional --manifest mode that writes a frozen-module manifest entry instead of a PROGDIR: drop-in, for users baking the constants into a custom MicroPython build.

Why this is tractable:

  • All three engines already exist (-fdgen, -taggen, -structgen) and use the same bebbo-cross-gcc plumbing. The new tool is a thin orchestrator that picks which engine(s) to apply per input header and emits a single combined .py.
  • The trickiest bit — turning C preprocessor output into Python literals — is already solved by amiga-taggen.py.
  • Output is a plain Python file; no MicroPython-specific runtime support needed. It "just works" with the existing amiga.library / amiga.lib_call surface.

Open questions:

  1. Anonymous unions / bitfields. C structs with anonymous unions or bitfields can't be cleanly represented as (name, offset) tuples. Need a strategy: drop them with a comment, or invent a Python-side BIT_FIELD marker. The NDK structgen whitelist sidesteps this by hand-picking safe structs; a third-party tool can't.
  2. #include resolution. Real-world headers #include each other across SDK subdirs. The tool needs an -I sdk/Include/ flag and a way to not drag the whole transitive include set into the output — otherwise a mylib.h that includes <exec/types.h> ends up emitting the entire NDK as constants. Probably "only emit #defines from headers explicitly listed on the command line, but resolve their values using everything reachable via -I".
  3. Naming collisions across libraries. If a user generates modules for two libraries that both define FOO, the second wins. Tool should warn loudly. Optional --namespace mylib flag wraps everything in a class / dict so collisions are scoped.
  4. Distribution shape. Should the tool ship in tools/ alongside the existing trio, in a separate tools/contrib/ directory, or as a standalone Python package the user pip installs? Probably tools/ for parity with the existing tools — they all assume the bebbo cross-gcc is on PATH anyway, so the docker wrapper (tools/amiga-build.sh) gives a clean way to run it.
  5. Cross-host portability. Does the tool need to work on AmigaOS itself (so a user with sas/c or vbcc on-box can regenerate their own module from a stock C header), or is "run on Linux/macOS via the Docker wrapper" sufficient? Almost certainly the latter for v1 — the on-Amiga story would mean compiling the probe .c with sas/c or vbcc, which is a much bigger compatibility ask.

Cost estimate: ~300 lines of Python on top of the existing three engines, plus a docs page and a couple of worked examples (identify.library, cybergraphics.library). Probably 2–3 days of focused work plus a follow-up week of shaking out real third-party headers.

Verdict: medium-effort, high-leverage. The existing trio already proved the engine works; this is a packaging / ergonomics pass that converts "you can call any Amiga library from MicroPython if you're willing to hand-roll the constants" into "you can call any Amiga library from MicroPython, full stop". Pairs naturally with the ClassAct / ReAction GUI binding sketch above — if and when ClassAct lands, the same tool would let users generate constants for whatever third-party BOOPSI classes they want to use alongside the bundled ones.


Other known limitations

Issue Status Fix
try/except in @micropython.native crashes Fixed (Phase 12a) Register-based NLR in nlr68k.S + nlr68k_jump.c saving D2–D7/A2–A6 in nlr_buf_t
Nested-function / closure / generator crashes in native Fixed (Phase 12b) Big-endian byte order: byte-swap the prelude_ptr_index / generator start_offset words on N_68K in py/emitnative.c
@micropython.viper holds only 1 local in a register (D7) MAX_REGS_FOR_LOCAL_VARS = 1 Performance only — results are correct (extra locals spill to the stack). Optional future work: spill across D2–D7. Partly intrinsic to the 68k register layout.
@micropython.viper ptr16/ptr32 accesses are big-endian m68k is big-endian Not a bug — native machine memory order. The little-endian upstream .exp files differ; see the testing wiki's platform-differences table.
A few asyncio ordering/fairness tests fail on the (slow) 68k Execution-speed artifact, not a bug asyncio is enabled (MICROPY_PY_ASYNCIO + scheduler, event loop driven by select.poll() over bsdsocket). asyncio_gather_notimpl / asyncio_wait_for / asyncio_fair pass on the unix reference but fail on Amiberry. Root cause: modasyncio.c stamps a new task's ph_key = ticks_ms() at create time, and these tests assume the bookkeeping around their short (10–100 ms) sleeps is instantaneous — true on a fast host, false on a 68k. The emulated 68020's per-asyncio-iteration overhead is ~12–50 ms (measurable: asyncio_fair expects t2 to fire 5× in 0.45 s and gets 4×), so e.g. creating three tasks outlasts a 10 ms sleep and a later task's key lands after an earlier reschedule, reordering output. Functionality is correct (gather raises, wait_for cancels, scheduling is fair); only interleaving/count shifts. Not timing-precision — a throwaway µs-precise busy-wait WFE left all three unchanged. Eases on faster hardware. (asyncio_fair also has no .exp and is diffed against CPython's different scheduler.)
amiga.assigns() resolved-target buffer is 256 bytes (modamiga.c line 265), tight for deeply-nested assigns on long-name filesystems Surfaced during Phase 31 buffer audit Bump to 1024 if a real-world case hits it; assign targets are usually short.
REPL history file path is 256 bytes (amiga_history.c AMIGA_HISTORY_PATH_MAX), tight for unusual install locations Surfaced during Phase 31 buffer audit Bump to 1024; history file lives in ENVARC: by convention so unlikely to hit.

For the full enumerated list of currently-failing tests (35 entries grouped by cause — Phase-12 NLR/viper gaps, AmiSSL subset, vamos socket/select/clock gaps, port-config one-offs), see the Amiga port test status scoreboard's Remaining real-hardware failures table. Captured 2026-06-13 against commit 98a3a6e4c.

Fixed during the smoke test sweep

  • @micropython.native binary ops with a small-int literal mostly failed. asm_68k_moveq in py/asm68k.h placed the destination data register at bits 10-8 instead of 11-9, so MOVEQ #1, D2 (wanted opcode 0x7401) emitted 0x7201 = MOVEQ #1, D1 — clobbering the wrong register and leaving the intended one stale. Visible as TypeError: unsupported types for __X__: 'int', '' for any < / > / + / - / * against a literal in roughly -64..63, or as a silent wrong-answer for == / != (the runtime's small-int fast path turns the corrupted-rhs comparison into a default-false). Fixed in commit fb1aeab92; bug had been present since the original 68k native emitter commit (42beaaeca, Phase 12). Regression test in tests/ports/amiga/test_native_emitter_smoke.py.

Tooling and upstream-conformance pass (2026-06-13)

A non-feature housekeeping pass that brought the port's surface in line with the canonical project conventions in CODECONVENTIONS.md. Nothing user-visible changed; the diff was structural / cosmetic.

  • tools/amiga-structgen.py (new). Completes the trio with amiga-fdgen.py (parses .fd files into LVO + register tables) and amiga-taggen.py (compiles tag macros to resolve their TAG_USER + N IDs). The new tool emits offsetof() / sizeof() expressions against an NDK-struct whitelist, compiles them with the bebbo cross-gcc, and parses the resolved .long literals back to produce ports/amiga/modules/_amiga_structs.py. For inline fixed-byte strings (e.g. fib_FileName[108]) the array size is harvested via sizeof(((X*)0)->field) and the emitted type code is suffixed accordingly (s108), so the previously hand-typed magic numbers are gone. The output exactly matches the prior hand-curated table; the file is now mechanically tied to whatever NDK the build is targeting.
  • MIT license header retrofit. Sister ports carry the standard MicroPython /* This file is part of the MicroPython project ... */ block at the head of effectively every .c / .h; before this pass, 18 of 19 ports/amiga/*.c plus 4 .h plus 3 variants/*/mpconfigvariant.h files lacked it. All 25 retrofitted with Copyright (c) 2026 Simon Dick; modjson.c already carried the upstream Damien P. George block (inherited from the extmod/modjson.c it replaces) and was left alone.
  • mphalport.h guard. Switched from #pragma once to the canonical MICROPY_INCLUDED_AMIGA_MPHALPORT_H form used by every other port's mphalport.h.
  • Uncrustify clean. main.c had two #if / #endif lines inside an else if chain that the formatter wanted indented one level deeper; everything else round-trips clean against tools/uncrustify.cfg.
  • Commit policy realigned. Going forward, port commits use git commit -s (Signed-off-by, matching upstream's DCO requirement). Earlier commits on this branch deliberately don't carry a sign-off — the branch will be squashed before any upstream merge attempt, so the historical lack of sign-off doesn't matter.

Verification: full tests/ports/amiga/ smoke suite passes (28/28), and the upstream basics / float / import / micropython / extmod directories produce an identical failure set on the post-conformance HEAD as on its parent (diff of the two failure lists is empty).


Testing

Four paths, each for a different stage:

  • vamos (host, headless) — day-to-day iteration; widest automated coverage via tests/run-tests.py driven through tools/amiga-vamos-run.sh. Current snapshot under the standard variant: ~722 pass / ~256 self-skip / ~75 fail out of 1053 files, of which only 4 are real platform differences (m68k struct alignment, long-double precision); the rest are Phase-12 native/viper gaps, Unix-port-specific cmdline tests, or vamos emulation gaps.
  • Amiberry (full emulation) — anything needing Kickstart: Workbench launch, icon.library, 68881 FPU (the 68020fpu variant), persistent REPL history, real AmiSSL. On-device runner tools/amiga-runtests.py walks a test dir and diffs against .exp files pre-generated on the host via tools/amiga-gen-exp.py. Caveat: its emulated 68881 mangles NaN, so NaN edge cases (math.isnan, pow(-1, NaN), …) read wrong on the FPU build — an emulator defect, not a port bug; real silicon is fine.
  • Copperline (full emulation, headless) — independent Rust emulator with an IEEE-correct FPU (used as the oracle that pinned the Amiberry NaN bug) and serial→stdout capture via the port's -X serialdebug option (mirrors stdout through exec RawPutChar). FPU instruction set is currently incomplete, so the -m68881 binary doesn't run yet (Copperline#45); the soft-float standard binary runs the full suite.
  • CI.github/workflows/ports_amiga.yml (Phase 11). Cross-compile gate; produces release-style artefacts. No test execution.

Full runbook — install paths, variant selection, exclude lists, suite snapshot, known-good failures, soft-float library bugs, on-device runner setup — lives on the Amiga port testing wiki page.

Clone this wiki locally