-
Notifications
You must be signed in to change notification settings - Fork 0
Amiga port testing
Three paths to test the port, each suited to a different stage of work:
| Method | When |
|---|---|
| vamos (host-side, headless) | Day-to-day iteration; widest automated test coverage |
| Amiberry (full emulation) | Real-AmigaOS behaviour — Workbench launch, icon.library, 68881 FPU, persistent history, anything serial.device-shaped |
| CI (cross-compile only) | Confirming a branch builds in the canonical container; produces release-style artefacts |
For day-to-day work, vamos. For anything that needs Kickstart, an emulator. CI gives you the same binary you'd ship.
vamos is the userspace 68k/AmigaOS emulator from the amitools package.
It boots in milliseconds, has no GUI, and integrates with the standard
tests/run-tests.py harness. Setup here assumes vamos is installed at
~/vamos/ and activated via pipenv.
cd ~/vamos
pipenv run vamos --cpu 68020 \
-V "mp:/path/to/micropython/tests" \
--cwd mp:basics \
-- /path/to/build/micropython string1.py-
--cpu 68020is required — vamos defaults to 68000, which faults onm68020instructions. -
-V name:/host/pathmounts a host directory as an AmigaOS volume. -
--cwdsets cwd; with basename invocation,sys.argv[0]/__file__match host CPython exactly. -
--separates vamos options from the binary's args.
tools/amiga-vamos-run.sh is a MICROPY_MICROPYTHON wrapper. It mounts
tests/ as the internal mp: volume, points vamos's cwd at the test's
directory, replaces the script argument with its basename, uses a private
--vols-base-dir so parallel workers don't collide on the auto RAM:
volume, and -q so vamos logs don't pollute captured stdout.
export MICROPY_MICROPYTHON="$(pwd)/tools/amiga-vamos-run.sh"
cd tests
./run-tests.py -d basics float io micropython miscExclude directories that can't run on Amiga (no socket-server harness under vamos, no asyncio in the port, no inline asm for non-68k targets, etc.):
./run-tests.py -d basics float io micropython misc \
-e "inlineasm|machine_|thread|extmod/ussl|extmod/uasync"AMIGA_VARIANT=standard|68040 picks build + matching --cpu
flag. Default is standard. (68020fpu builds 68881 instructions
that vamos can't emulate, so it's Amiberry-only.)
AMIGA_VARIANT=68040 ./run-tests.py basics/string1.py68020fpu cannot run under vamos — it builds 68881 instructions and
vamos has no 68881/68882 emulation. The wrapper detects this and exits
with an explanation; test that variant under Amiberry or real hardware.
For run-tests.py to work against the Amiga binary, the port must:
- Accept
-X <option>flags as no-ops (run-tests.pyalways emits-X emit=bytecode; macOS adds-X realtime). - Return POSIX-style exit codes
(
MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING (1)). - Free
AllocVec'd argv buffers before returning — vamos's orphan-memory check makes the process exit non-zero otherwise.
All three are already in place.
-
Bebbo argv parser is broken under vamos for multi-arg invocations;
amiga_parse_argsparsespr_Argumentsitself. -
WaitForCharreturns 0 immediately under vamos — the port uses plainFGetCfor console reads. -
SetMode(fh, 1)is a no-op under vamos (it already delivers one char at a time). - No 68881 emulation — see variant selection above.
Vamos doesn't translate SetMode(stdin, 1) to tcsetattr on the host
TTY, so an interactive REPL launched naked sees the host shell's cooked
mode (cursor keys echoed as ^[[D, bytes only delivered on newline).
tools/amiga-vamos-repl.sh flips the host TTY into raw / no-echo /
no-isig before launching vamos and restores the original mode on exit.
tools/amiga-vamos-repl.sh # standard variant
AMIGA_VARIANT=68040 tools/amiga-vamos-repl.shPipe input is unaffected; only interactive sessions need the wrapper.
Run via tools/amiga-vamos-run.sh against the standard variant:
| Directory | Files | Pass | Self-skip | Fail | Notes |
|---|---|---|---|---|---|
basics/ |
574 | 490 | 83 | 1 |
struct1.py (bebbo ABI alignment) |
float/ |
68 | 54 | 11 | 3 | EXACT-mode precision at double-range edges |
io/ |
16 | 15 | 1 | 0 |
argv.py now passes |
import/ |
30 | 29 | 0 | 1 |
import_file.py (vamos host-path rewriting) |
micropython/ |
108 | 85 | 18 | 5 | Emitter complete: NLR (12a) + big-endian byte order (12b) + viper long-shift fix flipped 15 tests; the 5 remaining failures are all big-endian/little-endian byte-order differences in the viper ptr16/ptr32 boundary tests (platform differences, not bugs) |
extmod/ |
192 | 76 | 102 | 13 | vamos socket / select / time-quantum / vfs_userfs gaps; one known-flaky (time_time_ns.py) ignored |
misc/ |
14 | 6 | 8 | 0 | Skips: settrace, sys_exc_info, cexample |
cmdline/ |
27 | 9 | 2 | 14 | Unix-port-specific (REPL banner, -v, terminal editing); not re-run this snapshot -- vamos's stdin/stdout fan-out across run-tests.py worker fork hangs on the REPL tests, so the row carries the previous (2026-05-12) numbers verbatim |
stress/ |
13 | 12 | 0 | 1 |
bytecode_limit.py parser memory pressure |
Aggregate: 776 pass / 225 self-skip / 38 fail out of 1042 files
(the micropython/ +15/-15 below applied to the 2026-06-13 base of
761/225/53; the full micropython/ suite was re-run under vamos on
2026-06-20). Excluding the viper ptr16/ptr32 byte-order differences,
Unix-port-specific cmdline tests, vamos emulation gaps, and the
bytecode_limit.py parser edge case, 4 individual tests fail --
the same real platform differences as in the 2026-05-12 snapshot
(m68k struct alignment, long-double EXACT-mode precision, vamos
host-path rewriting).
Notable movement vs the 2026-05-12 snapshot:
-
micropython/(Phase 12, 2026-06-20): +15 pass / -15 fail total, measured by fullmicropython/re-runs under vamos with zero regressions. Phase 12a (register NLR,ports/amiga/nlr68k.S+nlr68k_jump.c) flippednative_try,native_try_deep,native_with,viper_try,viper_with,viper_globals(the viper try/with/globals cases needed only the NLR). Phase 12b (big-endianprelude_ptr_indexbyte-order fix inpy/emitnative.c) flippednative_const,native_closure,native_gen. The viper long-shift fix (asm68k.hLSL/LSR/ASR emitted word-size, not long) then flippedviper_binop_arith,viper_binop_bitwise_uint,viper_misc2,viper_subscr_multi,viper_ptr32_store,viper_ptr32_load(all depended on a shift past bit 15). The emitter is now functionally complete; the 5 remaining failures are big-endian/little-endian byte-order differences in the viper ptr16/ptr32 boundary tests (see the platform-differences table). All fixes confirmed on Amiberry. -
micropython/(2026-06-13): +27 pass / -27 fail. Theasm_68k_moveqdestination-register fix (commitfb1aeab92) unblocked everynative_*test that wasn't dependent on the then-missing 68k NLR or the big-endian byte-order bug. -
io/argv.py: now passes (was a vamos host-path rewriting failure); the path rewriting intools/amiga-vamos-run.shcaught up. -
extmod/: net file count dropped 205 → 192 (13 tests removed upstream); on the larger active set, pass count rose 67 → 76 as more surfaces became reachable after Phase 39's hashlib / deflate / btree additions.
vamos re-run (standard, dirs basics float io import micropython extmod misc stress, same exclusions): 752 pass / 40 fail / 223 self-skip,
no regressions — every failure is a previously-documented category.
For the first time the failing extmod/ surfaces were also run on real
Amiberry via the new run-tests.py -t serial path (above). This splits
the vamos "re-run under Amiberry" TODO bucket into real passes vs genuine
gaps:
| Test | vamos | Amiberry | Verdict |
|---|---|---|---|
extmod/socket_tcp_basic.py |
FAIL | PASS | vamos bsdsocket stub only |
extmod/socket_badconstructor.py |
FAIL | PASS | vamos bsdsocket stub only |
extmod/socket_fileno.py |
FAIL | PASS | vamos bsdsocket stub only |
extmod/time_ms_us.py |
FAIL | PASS | vamos ~10 ms clock quantum |
extmod/time_time_ns.py |
FAIL | PASS | vamos clock quantum (also flaky-tagged) |
extmod/ssl_sslcontext.py |
FAIL | PASS | needed real AmiSSL |
extmod/ssl_sslcontext_verify_mode.py |
FAIL | PASS | needed real AmiSSL |
extmod/select_poll_basic.py |
FAIL | FAIL | genuine: no select()/poll() on the port |
extmod/time_res.py |
FAIL | FAIL→fixed |
time.time() froze: a port bug left the 68881 FPCR rounding-precision at single, so mathieeedoubbas's FPU path truncated every double. Fixed by resetting FPCR at startup; passes with the FPU enabled. See note below. |
extmod/ssl_basic.py |
FAIL | FAIL | genuine: AmiSSL API gap |
extmod/ssl_ioctl.py |
FAIL | FAIL | genuine: AmiSSL API gap |
extmod/ssl_keycert.py |
FAIL | FAIL | genuine: AmiSSL API gap |
import/import_file.py |
FAIL | FAIL→fixed | absolute __file__; fixed by the sys.path[0] change below (vamos + on-device now pass; -t still ImportError, a harness limit) |
So 7 of the 13 vamos failures are emulator artifacts, not port bugs;
import_file.py and time_res.py were both then fixed outright (see
below). The real-hardware failure set is now: 4 platform-diffs + 5 viper
ptr16/ptr32 byte-order differences (was 20 native/viper; Phase 12 and the
viper long-shift fix cleared the entire emitter, leaving only big-endian
vs little-endian-.exp differences — themselves platform diffs) +
select_poll_basic + 3 AmiSSL
(ssl_basic/ssl_ioctl/ssl_keycert) + vfs_userfs + platform_basic
-
stress/bytecode_limit.
time_res.py was a port bug, fixed 2026-06-20 (FPCR rounding
precision). It failed because time.time() was frozen: with an FPU
present, every double (epoch seconds ~1.78e9, 0.1+0.2, …) was truncated to
single (~24-bit) precision, so consecutive seconds collapsed to the same
value. The loss tracked FPU presence (byte-identical across host-64-bit,
host-80-bit and softfloat-80-bit — the FPU-mode selector made no difference)
and vanished with the FPU disabled, which initially looked like an Amiberry
emulation artifact. It was not.
Root cause (found by bisect): the bebbo C startup leaves the 68881/68882
FPCR rounding-precision field set to single (0x40) on entry to the binary
— a bare C program built with the same toolchain gets double (0x90).
MicroPython emits no FPU control writes (all double math is soft-float
library calls), but LIBS:mathieeedoubbas.library is a single on-disk
binary that auto-selects software vs direct-FPU math by FPU presence, and on
its direct-FPU path it honours the inherited FPCR. With rounding stuck at
single, every result rounded to ~24 bits. The bisect ruled out -lm and
every library MicroPython opens: FPCR read 0x40 before main and before
the earliest constructor, so it was the startup, not any OpenLibrary.
(Diagnostic trick: inline fmove.l %fpcr,%0 reads FPCR even in a
-msoft-float build — the assembler accepts FPU opcodes regardless of
codegen flags.)
Fix: amiga_fpu_fix_rounding() in ports/amiga/main.c, called first thing
in main(), sets the rounding-precision field to double
((fpcr & ~0xC0) | 0x80) via inline fmove.l. Guarded by
SysBase->AttnFlags & (AFF_68881|AFF_68882|AFF_FPU40) because the soft-float
standard build also targets FPU-less 68020s, where fmove would trap.
Verified on an FPU-enabled Amiberry: 0.1+0.2 == 0.30000000000000004,
consecutive large integers stay distinct, time.time() advances, and
time_res passes — no more need for a no-FPU config. (The residual
~1-2 ulp imprecision on transcendentals / repr(pi) digit count is the
separate, pre-existing bebbo soft-float / dtoa tax that also causes
float_format_accuracy / float_parse; present under vamos too.)
import_file.py fix (2026-06-20). Triage showed two layers: (1) over
run-tests.py -t, ImportError because the board harness ships no sibling
modules — inherent, not a port bug; (2) when the sibling is reachable, the
import worked but import1b.__file__ came back absolute
(Python:tests/import/import1b.py) vs the expected relative import1b.py.
Layer (2) was a real port deviation: for a basename script main.c set
sys.path[0] to the absolute cwd (NameFromLock) instead of "". Setting
it to "" (the MicroPython convention, resolved relative to cwd by
VfsAmiga) matches CPython/unix; import_file.py now passes under vamos and
the on-device runner, all 30 import/ tests pass, sibling imports still
work. Committed ports/amiga: Keep sys.path[0] relative for a basename script.
Genuine platform differences — present whether tested under vamos, Amiberry, or real hardware. Both sides are effectively correct; the test's expectations match CPython on x86 (or full-precision IEEE doubles) rather than the AmigaOS m68k ABI and bebbo's soft-float library:
| Test | Cause |
|---|---|
basics/struct1.py |
struct.calcsize("97sI") == 102, test expects 104. bebbo gcc on m68k uses 2-byte int alignment per the AmigaOS m68k ABI; CPython on x86 uses 4. Both platform-correct. |
float/float_parse*.py |
Very long mantissa with very negative exponent; 1e+300 vs 9.999…e+299 differing by 2 ULP; 1e4294967301 not detected as overflow — 1–2 ULP off. Bebbo's 80-bit long-double soft-float loses just enough precision that EXACT-mode parsing can't always nail the closest double. |
float/float_format_accuracy.py |
repr round-trip rate ~72 % vs ≥ 99.7 % expected. Same long-double precision tax. |
float/inf_nan_arith.py |
min/max over mixed nan/±inf operands picks the wrong-signed inf in one row (nan -inf -inf vs expected nan inf -inf). bebbo soft-float comparison quirk when a nan is involved; the ordered arithmetic itself is correct. |
micropython/viper_ptr16_load.py, viper_ptr16_load_boundary.py, viper_ptr16_store_boundary.py, viper_ptr32_load_boundary.py, viper_ptr32_store_boundary.py
|
@micropython.viper ptr16/ptr32 load/store are raw machine memory accesses, so a multi-byte value is read/written in the CPU's native byte order. The m68k is big-endian (0x3231 for bytes 31 32); the .exp files were generated on little-endian x86 (0x3132). Both are correct for their platform — the byte-uniform viper_ptr* tests (e.g. viper_ptr32_store storing 0x42424242) pass. No fix possible without diverging from native pointer semantics. |
Tests below fail on the current amiga-port head. None are regressions;
each is a documented gap waiting on a specific piece of port work.
Originally captured 2026-06-13 against commit 98a3a6e4c (standard
under vamos); updated 2026-06-20 with real-Amiberry verification of
the socket / select / time / ssl bucket. After verification the genuine
real-hardware gaps are: 3 ssl, 1 select, 1 vfs, 1 platform = 6
entries (Phase 12 plus the viper long-shift fix cleared the entire
native + viper emitter — see the movement notes above; the 5 remaining
viper ptr16/ptr32 boundary failures are big-endian byte-order
differences, moved to the platform-differences table below. The 3 socket
tests and 2 sslcontext tests turned out to be emulator artifacts that
pass on a correctly-configured target, 2 of the time tests were vamos
clock-quantum artifacts, and import_file.py and time_res.py were both
fixed — see the snapshot section above).
| Test(s) | Cause | TODO |
|---|---|---|
micropython/native_try.py, native_try_deep.py, native_with.py |
FIXED 2026-06-20 (Phase 12a). @micropython.native try/except/with needed a real 68k NLR. |
Done: ports/amiga/nlr68k.S + nlr68k_jump.c save D2–D7/A2–A6 in nlr_buf_t with D7 at regs[0]; py/nlr.h auto-detects __m68k__. Verified under vamos and on Amiberry; regression test tests/ports/amiga/test_native_nlr_smoke.py. |
micropython/native_closure.py, native_const.py, native_gen.py |
FIXED 2026-06-20 (Phase 12b). Big-endian byte order: the emitter wrote prelude_ptr_index (and the generator start_offset) little-endian, but the 68k reads it back as a native uintptr_t, so index 1 became 0x01000000 and child_table[] indexed out of bounds. Any nested def / closure / generator inside @micropython.native crashed. |
Done: byte-swap both words on N_68K in py/emitnative.c. Verified under vamos and on Amiberry; regression test tests/ports/amiga/test_native_nested_smoke.py. |
micropython/viper_binop_arith.py, viper_binop_bitwise_uint.py, viper_misc2.py, viper_subscr_multi.py, viper_ptr32_store.py, viper_ptr32_load.py |
FIXED 2026-06-20. asm68k.h emitted word-size shift opcodes (LSL.W/LSR.W; ASR was even encoded as ASL), so any viper shift past bit 15 was wrong (1<<30→0, byte-assembly losing high bytes, >>-based length/index maths corrupting state). The "register allocator caps at 1 local" / "16-32-bit pointer opcodes missing" theory was wrong — those work; this single shift bug was the cause. |
Done: emit LSL.L/LSR.L/ASR.L in py/asm68k.h. Verified under vamos and on Amiberry; regression test tests/ports/amiga/test_viper_shift_smoke.py. |
extmod/ssl_basic.py, ssl_ioctl.py, ssl_keycert.py
|
AmiSSL ships a subset of the OpenSSL API surface. (ssl_sslcontext.py and ssl_sslcontext_verify_mode.py were in this bucket but pass on Amiberry as of 2026-06-20 — they only failed because vamos has no AmiSSL.) Remaining suspects: certificate-format combinations and ioctl flag mappings. |
Triage each .py.out vs .py.exp in tests/results/ to attribute the divergence to either a real AmiSSL API gap (file an issue upstream / wrap with stubs) or a port-side wrapper bug in ports/amiga/modssl.c. |
extmod/socket_tcp_basic.py, socket_badconstructor.py, socket_fileno.py |
vamos's bsdsocket.library stub is incomplete. |
Resolved 2026-06-20: all three PASS on Amiberry against a real bsdsocket.library. vamos-only failures; no port work needed. |
extmod/select_poll_basic.py |
The port doesn't expose select() / poll() over bsdsocket. |
Confirmed 2026-06-20: still FAILS on Amiberry → genuine gap (not just a vamos limitation). Real TODO if select/poll support is wanted. |
extmod/time_res.py |
time.time() was frozen. |
Fixed 2026-06-20 — port bug (FPCR rounding precision). The bebbo C startup left the 68881 FPCR rounding-precision field at single, so mathieeedoubbas's direct-FPU path truncated every double, collapsing time.time(). Fixed by resetting FPCR to double precision at startup (amiga_fpu_fix_rounding() in main.c, guarded by AttnFlags); passes with the FPU enabled. time_ms_us.py/time_time_ns.py also pass on Amiberry (they only failed on vamos's ~10 ms clock quantum). |
import/import_file.py |
FIXED 2026-06-20 (vamos + on-device). The absolute-__file__ half is resolved: main.c now leaves sys.path[0] as "" for a basename script instead of the absolute NameFromLock cwd, so import1b.__file__ is the relative import1b.py matching CPython/unix. All 30 import/ tests pass under vamos. |
Over run-tests.py -t the test still raises ImportError — the board harness ships no sibling modules (test source sent plain in emit=bytecode), so import import1b isn't on the target's sys.path. Generic board limitation, not a port bug; nothing further to do unless -t gains sibling-shipping. |
extmod/vfs_userfs.py |
The test mounts a Python-defined VFS at /, which collides with the port's built-in VfsAmiga already mounted at / by main.c. |
Either skip this test for the Amiga port, or rework vfs_amiga.c to permit a runtime unmount of the default root so userfs can take over. The first is the pragmatic answer. |
extmod/platform_basic.py |
Port reports sys.platform = "amiga"; the test's expected-platforms list doesn't include it. |
Decide whether to upstream "amiga" into extmod/modplatform.c (preferred), or carry a downstream .py.exp override (tests/extmod/platform_basic.py.exp.amiga or similar). |
bebbo gcc 6.5b on -msoft-float ships incorrect floating-point helpers
in libgcc / clib2 / libnix. ports/amiga/floatconv.c overrides each one
(some directly, some via --wrap because clib2 fat-packs them with
__muldf3 and friends). Keep this list in mind when triaging arithmetic
oddities:
| Routine | Bug | Trigger |
|---|---|---|
__floatunsidf, __floatundidf, __floatdidf
|
High-bit-set values convert to garbage |
float("9"*51 + "e-39"), array.array('Q', [...]), any mp_obj_new_int_from_uint(>2³¹) → double |
__eqdf2, __nedf2, __ledf2, __gedf2, __ltdf2, __gtdf2
|
NaN treated as ordered/equal |
== / != / <= / >= with NaN; math.isclose, set/dict NaN keys, x != x
|
pow(-1, NaN) (libnix) |
Returns 1.0, CPython expects NaN |
(-1) ** float('nan') |
tgamma(-inf) (libnix) |
Returns +inf, CPython raises |
math.gamma(-inf) |
__fixdfsi (clib2) |
Calls IEEEDPFix which under vamos aborts the whole emulator on NaN |
hash(float('nan')) (gcc 6.5 emits __fixdfsi for mp_float_hash's bit-level body after opt) |
Use Amiberry when behaviour needs Kickstart — Workbench launch, the icon
/ datatypes / intuition libraries, 68881 FPU, persistent REPL history in
S:, anything where AmigaDOS shell quirks matter, anything where vamos
"close enough" isn't.
One-shot scripted runs use a startup hook: S:user-startup runs
py0:boot if present (where py0: is the repo root mounted as an
Amiberry hard drive), and boot redirects its own output to
py0:boot.log that the host can read directly. Custom tooling, but
handy for a single canned script.
tests/run-tests.py -t does now drive the emulated Amiga like a
bare-metal board, over Amiberry's TCP serial port. (Earlier the RX path
looked broken — SER: delivered host→Amiga zero bytes — but that was
the raw serial handler: it's single-open and doesn't assert the CTS/RTS
handshake the bridge needs. The AUX console works fine.)
Setup, Amiga side — attach a shell to the serial console on boot, with
an explicit baud, in S:user-startup:
newshell AUX:57600/8n1
Use 57600 (or 38400). 115200 is flaky on the Amiga. At 57600/8n1 the console is byte-clean both directions (no spurious CR doubling).
Setup, host side — tools/pyboard.py can't speak TCP, so a tiny bridge
relays the socket to/from stdin/stdout as a raw serial line. It lives at
tests/ports/amiga/amiga_serial.py and runs via pyboard's exec:
transport:
cd tests
./run-tests.py -t "exec:python3 $PWD/ports/amiga/amiga_serial.py --bridge --wait" \
extmod/socket_tcp_basic.py extmod/time_ms_us.pyPlatform auto-detects as amiga. --wait polls for the console so
run-tests is the sole connection.
Port changes that made this work (pyboard soft-resets with Ctrl-D before every test, which the port previously treated as "exit to shell"):
-
main.cfactors the runtime intoamiga_runtime_init/deinitand wraps the REPL in a soft-reset loop: Ctrl-D / SystemExit in the raw REPL rebuilds the VM, printsMPY: soft reboot, and re-enters (like a real board); friendly-REPL Ctrl-D still drops to the AmigaShell. -
mphalport.caddsamiga_stdin_hit_eof()so a client disconnect (FGetC < 0) exits cleanly instead of spinning in the soft-reset loop. -
main.copensbsdsocket.library/ AmiSSL once at startup, not per soft reset — Amiberry'sbsdsocklib_Closepumps SDL/Cocoa events off the main thread and SIGABRTs the emulator on macOS, so a long sweep of per-reset closes used to crash it.
Reliability hardening (2026-06-20) — these turned the serial path from
"flaky, per-test only" into a dependable bulk runner. Before them a serial
sweep dropped or interrupted random tests every run; after them the
previously-flaky validation set went 5/15 → 14/15 and bulk basics//
extmod/ runs are stable (see the suite sections below):
-
mphalport.cbulk-drains stdin (amiga_rx_refill, 1 KiB buffer) instead of byte-at-a-timeFGetC.FGetCwas a DOS round-trip per char, too slow to drain the serial RX during a burst, so the flow-control-less link overran the emulated UART and silently dropped bytes — garbling the test source pyboard streams in (a different test failed each run). The reliable pacing floor dropped from ~5 ms/byte to ~1 ms. -
main.cclearsSIGBREAKF_CTRL_Cat each soft reset. pyboard interrupts with a Ctrl-C before every test; the AUX console latched it as the task break signal even in raw mode, and the VM hook fired it partway into the next test — a spuriousKeyboardInterruptthat hit long / high-output tests. Clearing it per reset fixed all of those. -
main.creclaims the auto-grown GC heap on soft reset and reserves a RAM floor for AmigaOS (256 KiB,MICROPYHEAPRESERVE). The split heap used to leak its grown chunks across resets (freed only at quit), so a heap-heavy test starved later tests; and it grew until ~64 bytes of system RAM remained, starving OS serial I/O. Now RAM returns to baseline each reset and a floor stays free.
Practical limits:
-
Pacing: 2 ms/byte default is reliable (
AMIGA_SERIAL_DELAY/--delay). Verified 20/20 source-integrity at 1–3 ms; don't go below ~1 ms (overrun cliff ~0.5 ms). The old 5 ms default and the "don't pass --delay 0.002" warning predate the bulk-drain fix and no longer apply. -
Bulk runs are now usable. A full
basics/orextmod/sweep runs cleanly over serial (it does still ship every test body over the link, so it's slower than vamos — minutes, and run-tests' block-buffered stdout means no live progress; redirect to a file andtail). vamos / the on-device runner remain the faster authoritative bulk path. -
Exclude heap-exhaustion tests (
basics/memoryerror.py,extmod/deflate_compress_memory_error.py). They deliberately fill all RAM, which is too slow over the remote link — pyboard reportstimeout waiting for first EOF— and they used to cascade. Run those on-device / under vamos. -
One run-tests session per boot. The AUX console doesn't survive a
host disconnect, so reset the emulator (
reset_emulation, hard) before each session;--waitthen handles boot timing. -
git status tests/<dir>before trusting a failure. A stray untracked.py.expmakes run-tests diff against an old on-device capture, not CPython. (The ~800 stray files from earlier on-device runs were removed.)
The bridge is also a standalone REPL driver: amiga_serial.py -c "code",
-f file.py, --shell "cmd", or no args for the banner. The REPL launch
command is overridable via AMIGA_SERIAL_MP (e.g.
AMIGA_SERIAL_MP="micropython -X maxheap=8M").
After the FPCR rounding-precision fix (the time_res.py note above), the
whole float/ suite (68 tests) was run over serial with the FPU enabled
to confirm double precision is restored end-to-end:
cd tests
./run-tests.py -t "exec:python3 $PWD/ports/amiga/amiga_serial.py --bridge --wait" float/*.pyResult: 48 passed, 11 skipped, 9 reported failures. All the
precision-sensitive tests pass (float1, float_format,
float_format_ints, math_fun, math_constants, math_isclose,
int_64_float, string_format, …) — the fix holds. None of the failures
are regressions: the pre-fix binary truncated every double to single
(strictly worse), so the fix can only have improved float results.
-
11 skipped — all
*_intbig/*_endian; features the port doesn't build, expected. -
5 of the 9 "failures" were not real — now fixed. They showed
KeyboardInterrupt: CRASHin the captured.out; this looked like a timeout but was the lingering-Ctrl-C bug (a stale break from pyboard's per-test interrupt firing partway into the next test), since fixed by the Ctrl-C clear in the reliability hardening above. They passed when re-run directly even then, and pass over serial now:math_domain,float_divmod,cmath_fun,float_struct_e,string_format_modulo2. -
4 genuine failures — the pre-existing bebbo soft-float / dtoa tax
(present under vamos too, not caused by the fix):
float_format_accuracy(~72 % exact-repr round-trip,max_err≈4.1e-16≈ 2 ulp),float_parse+float_parse_doubleprec(huge-value overflow yields8.99e+307instead ofinf, plus subnormal edges), andinf_nan_arith(a nan/inf sign quirk inmin/max). See Known failures that are not port bugs.
Resolved. The three failure modes below drove the reliability hardening above (bulk-drained stdin, Ctrl-C clear, heap reclaim) and the removal of the stray
.expfiles. With those in place abasics/serial sweep is now reliable — every test passes or correctly skips except the one knownstruct1ABI diff. The forensic account is kept because it's how that conclusion was reached.
Running the full basics/ suite (~440 tests) over serial originally showed
why bulk-over-serial was unreliable — three independent failure modes
stacked up, and the raw run-tests.py summary was almost entirely
misleading:
-
Cascade.
basics/memoryerror.pydeliberately exhausts the heap; on the Amiga that desyncs the raw-REPL shell, so the next several tests came backraw REPL failed(b'') and run-tests gave up with "Too many raw REPL failures, aborting" — the run aborted ~⅔ of the way through (326 of ~440 "performed"), leaving the wholememoryview1…with1tail un-run. -
Flakiness. Re-running the tail (with
memoryerrorexcluded) the raw-paste protocol failed different tests each time —bytearray_slice_assign/generator_throwfailed once then passed, whileset_remove/string_format2newly "failed". Timing, not bugs. -
Stray local
.exp. Manybasics/*.py.expfiles left over from the on-device runner (amiga-gen-exp.py, below) are untracked — so run-tests diffed Amiga output against old Amiga captures instead of CPython, turning any nondeterminism into a spurious FAIL.
The honest result only emerged by verifying every test that ever failed
directly against the authoritative reference (host CPython for
CPython-diff tests, the shipped .exp for the two MicroPython-specific
ones). Of the 12 distinct "failures" seen across the serial runs, 10 pass
when run directly — bytearray_slice_assign, class_setname_hazard_rand,
dict_del, generator_throw, memoryerror, memoryview_gc,
memoryview_slice_assign, memoryview_slice_size, set_remove,
string_format2. The remaining two are not real failures either:
-
struct1.py— the documented m68kstruct.calcsizeABI diff (102vs104); see Known failures that are not port bugs. -
memoryview_itemsize.py— legitimately printsSKIP(feature probe), which run-tests only flagged because the stale local.expheld different output.
Bottom line: basics/ is clean on the Amiga — every test passes (or
correctly skips) except the one known struct1 ABI difference. Since the
reliability hardening this now holds over serial too (exclude the
heap-exhaustion memoryerror.py, which is too slow remotely). vamos and the
on-device runner remain the faster authoritative path for routine bulk
coverage. The 797 stray untracked .py.exp files that caused mode 3 were
removed; only shipped, git-tracked .exp remain, so a serial run diffs
against CPython again (they regenerate if you re-run the on-device runner).
The full extmod/ suite (206 tests) ran over serial in one clean session
— no cascade (nothing in extmod/ exhausts the heap the way
basics/memoryerror.py does). Raw summary: 91 performed, 74 passed, 115
skipped, 17 failed. The 115 skips are absent subsystems (asyncio,
machine_*, vfs_fat/vfs_lfs, tls_*, cryptolib_*, little-endian
uctypes_*, websocket) — expected for this port.
Resolved. This run predates the reliability hardening; the "transport artifacts" below now pass over serial too. The lasting result is the real failure set: 7 known gaps + 1 environmental. Verifying the 17 failures split them three ways —
-
9 were transport artifacts — now fixed (they passed under vamos at the
time and pass over serial since the bulk-stdin / Ctrl-C / heap-reclaim
fixes):
deflate_compress,deflate_compress_memory_error,framebuf_ellipse,framebuf_polygon,framebuf8,random_extra,random_extra_float,time_mktime,vfs_basic. (Caveat:deflate_compress_memory_erroris a heap-exhaustion test — still too slow to run over serial; run it on-device / under vamos.) -
1 is environmental, not a port bug:
socket_tcp_basicneeds a routable TCP path from the Amiga to the host test server, which neither the serial bridge nor vamos provides. -
7 are genuine, already-documented gaps:
select_poll_basic&select_ipoll(noselect()/poll()),ssl_basic/ssl_ioctl/ssl_keycert(AmiSSL API gaps),vfs_userfs, andplatform_basic(platform-identity diff).
Over half the reported serial failures (9 of 17) were transport noise that
the hardening has since removed. The real extmod/ picture is ~83
effective passes with only the seven known gaps (plus the environmental
socket test) — unchanged from the documented baseline. (Historical lesson,
now mostly moot: a pre-hardening serial summary was a list of suspects, not
failures. Post-hardening it's trustworthy, bar the heap-exhaustion tests.)
The on-device runner walks a single test directory, runs each .py test
via amiga.execute("micropython … >T:…"), and compares the captured
output against the matching .py.exp file. No CPython to diff against
on the Amiga side, so it relies entirely on the .exp files.
Upstream only ships .exp files where MicroPython output is expected
to differ from CPython. Generate the rest first:
tools/amiga-gen-exp.py tests/basics tests/float tests/io \
tests/import tests/micropython tests/misc tests/cmdline tests/stressExisting .exp files are never overwritten — many of them use the
######## wildcard or regex matching that the test framework depends on.
Delete a specific .exp and re-run if you need to regenerate one.
.exp files are not checked in. Add tests/**/*.py.exp to
.git/info/exclude if the chatter in git status bothers you.
The default 4–8 KB stack is too small for the deep compile-time
recursion a handful of tests trigger (e.g. try_except_break.py); a
stack overflow there manifests as Software Failure 8000000B (Line F
trap) rather than a clean Python RuntimeError.
1> Stack 32768
1> cd py0:
1> micropython tools/amiga-runtests.py tests/basics
1> micropython tools/amiga-runtests.py tests/float T:my-results/
The second argument is the result directory (default T:mp-test-results/).
On FAIL, <dir>/<test>.py.out (captured stdout) and <dir>/<test>.py.exp
(expected reference) land there. On pass / skip, stale artefacts from a
previous run are deleted.
T: lives in RAM under AmigaOS — a clean reboot wipes the artefacts.
The runner skips test classes the port can't pass for structural
reasons, matching the equivalent feature-check skips in run-tests.py:
-
int_big*/*_intbig.py— no arbitrary-precision ints (MICROPY_LONGINT_IMPL_LONGLONG). -
*_endian.py— big-endian platform, most tests assume little-endian. -
native_*/viper_*— the emitter is complete (Phase 12 + the viper long-shift fix); these run, not skipped. The only remainingviper_ptr16/ptr32boundary failures are big-endian byte-order differences, listed in the platform-differences table above. -
cmd_*/repl_*— need# cmdline:directives, regex.expmatching, and PTY interaction; onlytests/run-tests.pycan drive those. - A small frozenset of one-off tests that hit disabled extmod features
(
weakref_*,fun_code,attrtuple2.py, etc.).
The Amiga port emits \r\n on stdout even when redirected to a file —
this is mp_hal_stdout_tx_strn_cooked doing the AmigaShell-compatible
thing. The on-device runner strips trailing \r line by line before
comparing against the LF .exp reference, so the .exp files generated
by amiga-gen-exp.py stay platform-neutral. Any host-side diff against
captured output needs the same normalisation:
diff <(tr -d '\r' < captured.out) reference.exp-
68020fpu— needs 68881; vamos has no 68881/68882 emulation. -
Workbench launch —
WBStartup,icon.library, tooltype handling. Vamos has noicon.library. -
AmiSSL (Phase 28) — needs Kickstart and a real
amisslmaster.libraryinstall (see SSL section below).
TLS support is on in all three shipped variants. Exercising ssl on
target needs:
-
AmiSSL v5 installed.
amisslmaster.librarymust be reachable viaLIBS:. The conventional install puts it atSYS:AmiSSL/Libs/amisslmaster.librarywith the CA bundle in a c_rehash dir atSYS:AmiSSL/certs/. AmiSSL's own installer addsAssign AmiSSL: SYS:AmiSSLandAssign LIBS: AmiSSL:Libs ADDtoS:user-startup. -
AmiSSL assigns in scope at micropython launch. Watch the
ordering in
user-startup: if the boot hook runspy0:bootbefore the AmiSSL assigns, the boot script must add them itself orimport sslwill raise onSSLContext().
Recommended verify path:
import ssl
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.set_default_verify_paths() # preferred over load_verify_locationsset_default_verify_paths() sidesteps two AmigaOS-specific
gotchas with manual cert loading:
-
Trailing-slash capath fails.
load_verify_locations(capath= "AmiSSL:certs/")concatenates internally to a double-slash that AmigaDOS interprets as parent-dir reference, and lookups silently miss. Pass"AmiSSL:certs"(no trailing slash) if you want to use it explicitly. - AmiSSL c_rehash uses the old (pre-OpenSSL-1.0.0) subject hash algorithm. New-hash filenames silently miss in lookups even though byte-identical files exist under their old-hash names in the same directory.
CERT_REQUIRED HTTPS against TLS-1.3-eager fronts (Cloudflare,
GitHub) completes the handshake (cert chain verifies fine) but
the server closes the connection before any application data can
be written: ws.write returns EPIPE / broken pipe with zero
bytes sent. Hosts that don't strict-front like this (e.g.
www.python.org) work cleanly.
Reproducible with AmiSSL's own openssl s_client — same
CONNECTION CLOSED BY SERVER + tls_retry_write_records:Broken pipe trace, with all four certs in the chain reporting
verify return:1. So it is below the MicroPython layer. Workaround
attempts inside s_client (forcing classic X25519 to skip the
post-quantum MLKEM hybrid, disabling session tickets, forcing
TLS 1.2) either reproduce the same close or get rejected outright
at ClientHello. See Phase 28 — TLS/SSL via AmiSSL v5
and its AmiSSL issue notes for the full
diagnostic log.
tools/amiga-runtests.py does not gate these out, so they run
when the on-device runner walks extmod/. A handful require
features we don't yet expose (ssl.wrap_socket module-level
legacy form, cadata= parameter, server-mode handshake against a
canned cert pair) and will fail; the SSLContext-shape tests
(ssl_sslcontext.py, ssl_sslcontext_verify_mode.py etc.) should
pass.
tests/ports/amiga/test_intuition_smoke.py covers the module-registration
and argument-validation surface; it runs under vamos and prints OK
on success. The actual modal dialog can't be tested headlessly —
vamos's intuition.library stub is a no-op print, and the requester
needs a real public screen.
For end-to-end visual confirmation under Amiberry, from the REPL:
>>> from amiga import intuition
>>> intuition.easy_request("Title", "Pick one.\nMulti-line works.",
... ["Apple", "Banana", "Cancel"])
# requester pops; left → 0, middle → 1, right → 2
>>> intuition.auto_request("Replace existing file?")
# returns True for Yes, False for No
>>> intuition.message("Done.")
# single-button requester; clicking OK returns Noneintuition.library opens its own screen if no public screen is
already up, so the requester appears regardless of whether
Workbench is loaded. OSError(EIO) is reserved for the rare case
where EasyRequestArgs returns -1 (genuine intuition-side
failure, not a missing screen).
tests/ports/amiga/test_asl_smoke.py covers the module-registration,
arg-shape validation, and the multi=True + save=True
ValueError guard; it runs under vamos and prints OK on success.
The actual file-pick dialog needs Amiberry or real hardware.
For end-to-end visual confirmation:
>>> from amiga import asl
>>> asl.file_request(title="Pick a script", initial_drawer="Sys:")
'Sys:Prefs/Workbench' # picked, or
None # cancelled
>>> asl.file_request(save=True, initial_file="out.txt")
'Ram Disk:out.txt'
>>> asl.file_request(multi=True, pattern="#?.py")
['Work:scripts/foo.py', 'Work:scripts/bar.py']
>>> asl.file_request(drawers_only=True)
'Work:scripts'file_request runs the underlying AllocAslRequest +
AslRequest calls on a 32 KB scratch stack via StackSwap, so
the dialog works even from a default-stack shell prompt — no
Stack 32768 prelude required.
tests/ports/amiga/test_rexx_polish.py covers the C-primitive +
Python-facade surface area; it runs under vamos and prints OK.
Real driving of an ARexx host needs rexxsyslib.library and a
running peer, so it lives under Amiberry / real hardware.
For end-to-end visual confirmation:
>>> import amiga
>>> amiga.rexx_open() # so we have a port to be queried
'MICROPYTHON.1'
>>> amiga.rexx_exists("MICROPYTHON.1")
True
>>> "MICROPYTHON.1" in amiga.rexx_list()
True
>>> from amiga import RexxClient
>>> with RexxClient("MICROPYTHON.1") as c:
... # ... a `rexx_serve()` loop on another task would handle this
... pass
>>> # Forgetting close() is OK -- amiga_rexx_shutdown picks it up
>>> # on process exit so the MsgPort doesn't leak.Driving a real host (DOpus, IBrowse, YAM) in a tight loop is what
RexxClient was built for: the persistent reply MsgPort saves
the CreateMsgPort / DeleteMsgPort cost per send. The one-shot
amiga.rexx_send / amiga.rexx helpers are still the right
shape for single sends.
tests/ports/amiga/test_platform_smoke.py covers the CPython-shaped
platform surface plus the underlying amiga.* accessors; it
runs under vamos. platform.amiga_info() and amiga.chipset()
need graphics.library (not stubbed by vamos) and are skipped
when that fails.
For end-to-end confirmation under Amiberry:
>>> import platform
>>> platform.amiga_info()
'CPU: 68020 | FPU: 68881 | Chipset: AGA | Kickstart: 45.57 | Chip: 1856KB | Fast: 14336KB'
>>> platform.machine(), platform.release()
('68020', '45.57')The CPU and FPU strings reflect runtime AmigaOS state -- a
standard binary built -m68020 -msoft-float running on a
68040 reports '68040' / '68040', not the compile-time target.
Memory values are currently free bytes (AvailMem(MEMF_*)),
not total installed.
tests/ports/amiga/test_os_smoke.py covers the surface of the
frozen os.py / _ospath.py extensions: module assembly via
from uos import *, the _osamiga re-exports (chmod,
getprotect, FIBF_*), makedirs, walk, and every os.path
helper. Under vamos the test also round-trips makedirs / walk
against RAM: (vamos's RAM disk maps to a host temp directory).
chmod round-tripping only fully exercises on Amiberry because
vamos's SetProtection is a partial stub.
For interactive REPL confirmation under Amiberry:
>>> import os
>>> os.makedirs("RAM:t/a/b", exist_ok=True)
>>> os.path.isdir("RAM:t/a/b")
True
>>> os.chmod("RAM:t/a/b", os.FIBF_DELETE)
>>> hex(os.getprotect("RAM:t/a/b"))
'0x1'
>>> os.chmod("RAM:t/a/b", 0)
>>> hex(os.getprotect("RAM:t/a/b"))
'0x0'
>>> list(os.walk("RAM:t"))
[('RAM:t', ['a'], []), ('RAM:t/a', ['b'], []), ('RAM:t/a/b', [], [])]getprotect returns the raw fib_Protection ULONG;
a set bit denies the operation for RWED, so mask == 0
means "everything allowed". The APSH bits (FIBF_ARCHIVE,
FIBF_PURE, FIBF_SCRIPT, FIBF_HOLD) follow the conventional
"set means yes". This matches the AmigaShell Protect
command's encoding.
tests/ports/amiga/test_icon_smoke.py covers the _icon module
registration, all WB* constants, the error paths (missing path,
non-DiskObject to write, unknown type code to new, non-dict
tooltypes kwarg), and -- when the host can satisfy
GetDefDiskObject (i.e. ENV:sys/def_project.info is reachable) --
a full new() → mutate → write() → read() round trip against
RAM:. Vamos soft-passes the round trip when ENV:sys defaults
aren't available; Amiberry exercises it end-to-end.
For interactive REPL confirmation under Amiberry:
>>> from amiga import icon
>>> d = icon.read("SYS:Prefs")
>>> d.type, d.current_x, d.current_y
('drawer', 80, 24)
>>> list(d.tooltypes)
['SHOW=ICONS']
>>> d.close()
>>>
>>> new = icon.new(
... icon.WBPROJECT,
... default_tool="C:Ed",
... stack_size=8192,
... tooltypes={"WINDOW": "CON:0/0/640/256/Test", "FLAG": None},
... )
>>> icon.write("RAM:test", new)
>>> new.close()
>>> back = icon.read("RAM:test")
>>> back.default_tool, back.stack_size
('C:Ed', 8192)
>>> back.tooltypes["FLAG"]
b''
>>> back.close()Tooltype values come back as bytes (the AmigaOS on-disk
representation), assignments accept str / bytes / None
(None ⇒ flag-style entry, no = separator).
After the Phase 39 extmod opt-ins shipped, the port had targeted
smoke tests for the phase-numbered surfaces (intuition / asl / icon /
catalog / rexx / platform / os-extensions / hashlib / deflate /
btree / wallclock time) but most of the older Amiga-specific surface
had no port-local test. A sweep filled the gap, adding 17 smoke
tests in tests/ports/amiga/ that cover the foundational ports of
the port:
| Test file | Surface |
|---|---|
test_library_proxy_smoke.py |
amiga.Library + .fd-driven trampoline (_amiga.lib_open / lib_call / lib_close) |
test_mem_smoke.py |
_amiga.alloc_vec / free_vec, peek_b/w/l/bytes, poke_b/w/l/bytes, big-endian byte order, MEMF_* constants |
test_amiga_introspection_smoke.py |
_amiga.os_version / heap_info / exists / volumes / assigns / disk_info / match / imatch
|
test_signal_smoke.py |
_amiga.signal / wait_signal + SIGBREAKF_CTRL_* constants |
test_amiga_execute_smoke.py |
_amiga.execute (dos.library SystemTagList() wrap) |
test_workbench_smoke.py |
_amiga.launched_from_workbench / wb_selected_files / tooltype
|
test_history_smoke.py |
REPL history ring buffer (_amiga.readline_history / readline_push_history) |
test_time_monotonic_smoke.py |
time.ticks_ms / ticks_us / ticks_cpu / ticks_add / ticks_diff / sleep_ms / sleep_us
|
test_os_envvar_smoke.py |
os.getenv / putenv / unsetenv ↔ AmigaDOS ENV: + os.chmod round-trip via getprotect
|
test_vfs_smoke.py |
VfsAmiga file I/O: open r/w/a / b+text, write / read / readline / readlines, seek / tell, ilistdir, stat, rename, mkdir / rmdir / remove, chdir / getcwd, error paths |
test_stdio_smoke.py |
sys.stdin / stdout / stderr -- POSIX fd numbers, write direction guards, TextIOWrapper method set |
test_json_smoke.py |
Port-local modjson -- primitives, composites, whitespace tolerance, string escapes, error paths, dumps/loads round-trip |
test_socket_smoke.py |
socket module surface (constants pinned to AmigaOS bsdsocket numbering, OSError when bsdsocket not present) |
test_ssl_smoke.py |
ssl.SSLContext surface (constants pinned to CPython values, OSError when AmiSSL not present) |
test_urequests_smoke.py |
Frozen urequests -- _parse_url, _quote, _urlencode, verb surface, Response class shape |
test_floatconv_smoke.py |
ports/amiga/floatconv.c libgcc / libm wraps (__floatunsidf / __floatdidf / __wrap_pow / __wrap_tgamma / __fixdfsi / NaN comparison) |
test_native_emitter_smoke.py |
Regression test for the MOVEQ encoding fix (see below) |
All 17 pass under vamos on the standard variant. The
test_workbench_smoke.py / test_socket_smoke.py /
test_ssl_smoke.py / test_amiga_execute_smoke.py files document
which assertions only fully exercise on Amiberry / real hardware
(Workbench launch state, live bsdsocket I/O, live AmiSSL handshakes,
live SystemTagList command execution) and the test structure accepts
either the vamos-stub or live-success path so the same file is
portable to the Amiberry rig.
Real bug found during the sweep. While drafting
test_native_emitter_smoke.py, every @micropython.native binary
op against a small-int literal in the range -64..63 was raising
TypeError: unsupported types for __X__: 'int', '' or wandering
into a vamos invalid-memory crash. Root cause was a one-character
typo in py/asm68k.h asm_68k_moveq: the destination data register
was being placed at bit position 8 instead of 9, so
MOVEQ #1, D2 (wanted opcode 0x7401) was generating 0x7201 =
MOVEQ #1, D1. That clobbered the operand register the lhs was
about to be loaded into, and the rhs register the binary op
expected stayed stale. Fixed in commit fb1aeab92; the regression
test pins both sides of the MOVEQ-range boundary (literal 63 vs 64)
plus <, >, ==, !=, +, -, *, branch-on-literal,
while-loop with literal-bounded condition, and a viper compare for
completeness. Bug had been present since the original 68k native
emitter commit (42beaaeca, Phase 12).
One Amiga-specific surface remains without a targeted smoke
test: Ctrl+C interrupt polling (amiga_check_ctrl_c invoked by
the VM hook every 1024 bytecodes). vamos's exec.library
Signal / Wait emulation doesn't deliver signals to the same
task, so the round trip can only be verified on Amiberry / real
hardware. The hook itself is exercised implicitly by every test
that loops more than 1024 bytecodes without crashing.
tests/ports/amiga/test_catalog_smoke.py covers the _catalog
module registration, the language() accessor, error paths
(missing catalog, missing name, non-str language kwarg), the
context-manager surface, and -- when the host can satisfy a
catalog open -- a lookup round trip. Vamos has no real
locale.library, so it soft-passes the round-trip block;
Amiberry covers it end-to-end against Sys/monitors.catalog.
For interactive REPL confirmation under Amiberry:
>>> from amiga import catalog
>>> catalog.language()
'english'
>>> # Stock Workbench is English -- declaring the binary's built-in
>>> # language as "german" forces a translation lookup that actually
>>> # loads the file rather than treating it as already in place.
>>> with catalog.open("Sys/monitors.catalog",
... language="english",
... built_in_language="german") as cat:
... print(cat.lookup(99999, "FALLBACK"))
...
FALLBACKClosed catalogs are safe to call lookup on — GetCatalogStr
treats NULL as "no catalog" and returns the caller's default. The
context manager closes on block exit so the underlying
locale.library allocation is released promptly.
.github/workflows/ports_amiga.yml runs on workflow_dispatch with a
ref input (branch / tag / commit, default amiga-port). One job
inside stefanreinauer/amiga-gcc:latest builds mpy-cross once, then
all four variants sequentially. Each binary uploads as a separate
artifact named micropython-amiga-<variant>-<ref>-<sha>.
CI does not run the test suite — it's a cross-compile gate plus an artefact producer. Functional testing happens locally against vamos / Amiberry.
Runs the same image with the same commands, so local and CI binaries are bit-identical:
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 dirsFiles are written as the host user (via --user), not root.
# Full vamos sweep against the standard variant
cd ports/amiga && make
export MICROPY_MICROPYTHON="$(pwd)/../../tools/amiga-vamos-run.sh"
cd ../../tests
./run-tests.py -d basics float io import micropython misc \
-e "inlineasm|machine_|thread|extmod/ussl|extmod/uasync"
# Interactive REPL under vamos
tools/amiga-vamos-repl.sh
# Generate .exp files for on-device runner
tools/amiga-gen-exp.py tests/basics tests/float tests/io tests/import \
tests/micropython tests/misc tests/cmdline tests/stress
# Then boot Amiberry, `Stack 32768`, `cd py0:`, and
# `micropython tools/amiga-runtests.py tests/basics`
# CI-identical build all variants
tools/amiga-build.sh