-
Notifications
You must be signed in to change notification settings - Fork 0
Phases 0 11 foundation
Part of the Amiga port design log.
The foundational phases: toolchain, a bootable skeleton, file system and
import, the standard library, the amiga C module, the first cut of the
68k native emitter, package imports, Ctrl+C handling, the migration off
newlib onto native AmigaOS APIs, networking, command-line parsing, and the
CI build. The native-emitter rework that completes Phase 5's work lives on
its own page, Phase 12 — 68k native emitter rework.
bebbo GCC 6.5.0b at /opt/amiga. m68k-amigaos-gcc --version confirms.
Builds an AmigaOS loadseg()ble HUNK executable; confirmed running in Amiberry.
Notable mpconfigport.h settings worth remembering:
- Register-based NLR via
__m68k__auto-detect (Phase 12a);mpconfigport.hdeliberately does not setMICROPY_NLR_SETJMP -
MICROPY_LONGINT_IMPL_LONGLONG—'q'/'Q'struct codes and >30-bit literals need this; adds ~5 KB. -
MICROPY_ENABLE_PYSTACK (1)— not optional. bebbo gcc 6.5'sallocareturns 2-byte-aligned addresses on 68k, which breakmp_obj_is_obj's(o & 3) == 0check on iterator slots; routing throughmp_pystack_alloc(4 KBaligned(8)buffer) gives deterministic alignment.
mp_builtin_open_obj must be supplied by the port (py/modio.c references
but doesn't define it).
open(), read, write, seek, tell, readline, with, and import
from any mounted volume all work. Originally newlib stdio in amigaio.c /
amigafile.c; Phase 8 migrated to direct dos.library; Phase 16 then
rewrote behind a VFS layer (vfs_amiga.c) — those files are gone.
MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES. Working: math, struct, json,
re, hashlib, float. sys.platform == "amiga".
-msoft-float is in CFLAGS — no hardware FPU on 68020. FPU variants (Phase 27)
drop this.
json.loads workaround. Upstream extmod/modjson.c fails on 68k with
OSError: stream operation not supported — it builds a stack-allocated
mp_obj_stringio_t and routes through the stream protocol, but
mp_obj_is_obj() requires 4-byte alignment which the 68k SysV ABI doesn't
guarantee for every stack frame. ports/amiga/modjson.c is a port-local
replacement that bypasses the stream protocol for loads; Makefile filters
out extmod/modjson.c.
ports/amiga/modamiga.c exposes exec/dos primitives. Must be in SRC_QSTR
(not just SRC_C) so MP_REGISTER_MODULE and qstrs are picked up.
import amiga
amiga.os_version() # (version, revision)
amiga.find_task(name=None) # int task pointer (current if None)
amiga.alloc_vec(size, flags=amiga.MEMF_ANY) # int address; MemoryError on fail
amiga.free_vec(addr)
amiga.execute(cmd) # int rc (0/5/10/20, -1=start fail)
amiga.exists(path) # bool, suppresses volume requesters
# MEMF_ANY/PUBLIC/CHIP/FAST/CLEAR constantsamiga.execute() uses SystemTagList() (not Execute()) so the real CLI
return code surfaces. amiga.exists() brackets Lock with
pr_WindowPtr = (APTR)-1 so an unmounted volume doesn't pop an
"Insert volume" requester.
Later phases add many more bindings to this module — see their sections.
@micropython.native / --emit native via GENERIC_ASM_API.
| File | Role |
|---|---|
py/asm68k.h/.c |
68k instruction encoder + helpers |
py/emit68k.c |
#define N_68K 1 + #include "py/emitnative.c"
|
Register allocation: D0 = RET/ARG_1, D1/D2/D3 = ARG_2/3/4 (D2/D3 callee-saved,
reloaded in prologue), D4–D6 = temps, D7 = REG_LOCAL_1, A2/A3 = LOCAL_2/3 (used
for REG_GENERATOR_STATE / REG_QSTR_TABLE), A4 = REG_FUN_TABLE, A5 = frame ptr.
MAX_REGS_FOR_LOCAL_VARS = 1 — only D7 is a data register safe for arithmetic.
Calling convention: AmigaOS/cdecl, args pushed right-to-left.
LINK A5,#-N; MOVEM.L D2-D7/A2-A4,-(SP) for entry, branches always .W form,
CMP.L; Scc; ANDI.L #1.
Known limitations (tracked as Phase 12):
-
try/except/within native mode — fixed in Phase 12a via the register-based NLR (ports/amiga/nlr68k.S). - Nested functions / closures / generators inside
@micropython.native— fixed in Phase 12b (big-endian prelude-index byte order). - Viper shifts past bit 15 — fixed in Phase 12c (long-size shift opcodes).
-
MAX_REGS_FOR_LOCAL_VARS = 1caps viper at one register-held local (the rest spill to the stack). This is a performance limit only — results are correct — and is partly intrinsic to the 68k register layout (only D7 is a data register free for cached locals).
mp_import_stat() uses dos.library Lock/Examine; fib_DirEntryType > 0
is a directory. import mypackage works.
Two paths:
- During computation:
MICROPY_VM_HOOK_LOOPpollsCheckSignal(SIGBREAKF_CTRL_C)every 1024 bytecodes (amiga_check_ctrl_cinmphalport.c). - During input:
mp_hal_stdin_rx_chr()checksmp_interrupt_char.
shared/runtime/interrupt_char.c provides mp_interrupt_char and
mp_hal_set_interrupt_char().
All newlib stdio replaced with dos.library:
| Component | Now uses |
|---|---|
| Console input | FGetC(Input()) |
| Console output | Write(Output(), buf, len) |
| File I/O |
BPTR + Open/Read/Write/Close/Seek/Flush
|
| Heap | AllocVec(MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR) |
| GC stack bounds | FindTask(NULL)->tc_SPUpper |
mp_hal_delay_ms |
Delay() (later replaced by Phase 23 timer.device) |
ports/amiga/modsocket.c via bsdsocket.library. SocketBase opened in
main() (silently absent if library missing — socket creation raises
OSError). Errno() (per-library) instead of global errno;
IoctlSocket(FIONBIO); SO_RCVTIMEO/SO_SNDTIMEO; Inet_NtoA/inet_addr/
gethostbyname; getaddrinfo/freeaddrinfo/gethostname. Stream protocol
implemented so readline(), with, etc. work. CloseSocket() (not close)
on shutdown. __NO_NETINCLUDE_TIMEVAL guard avoids devices/timer.h /
sys/time.h conflict.
main.c parses argc/argv before the REPL:
micropython # interactive REPL
micropython script.py [args] # sys.argv = ["script.py", ...]
micropython -c "code" [args] # sys.argv = ["-c", ...]
micropython -m module [args] # sys.argv = ["module", ...]
micropython -h / --help / --version
MICROPY_PY_SYS_ARGV (1), mp_sys_path = [""]. Script directory is prepended
to sys.path[0] using AmigaOS path parsing. -c via pyexec_vstr; -m via
mp_builtin___import__. Raw console mode (SetMode(stdin, 1)) is REPL-only.
genhdr/mpversion.h included for MICROPY_BANNER_NAME_AND_VERSION.
The bebbo argv parser is broken under vamos with multi-arg invocations;
amiga_parse_args parses pr_Arguments itself.
.github/workflows/ports_amiga.yml, manually triggered via
workflow_dispatch with a ref input (branch / tag / commit, default
amiga-port). One job runs inside stefanreinauer/amiga-gcc:latest,
builds mpy-cross once, then builds all four variants sequentially with
make -j$(nproc) inside each. Each variant's binary is uploaded as a
separate artifact named
micropython-amiga-<variant>-<ref>-<sha>.
tools/amiga-build.sh mirrors the workflow exactly for local container
builds — same image, same commands, same output paths — so local and CI
binaries are bit-identical (see the design log's Toolchain section).
Required submodule. make -C ports/amiga submodules runs before the
build; the frozen-content rule unconditionally checks for
lib/micropython-lib/README.md whenever FROZEN_MANIFEST is set, even
though manifest.py only freezes ports/amiga/modules.
Timing. Image pull dominates at ~87 s; the rest (checkout, submodule, mpy-cross, four variant builds, four uploads) is ~45 s — total wall time ~130 s, billed compute matches.
Possible follow-ups (deferred):
-
Image tarball cache.
docker save | gzip+actions/cachewould drop repeat-run wall time from ~130 s to ~40 s. Not worth the complexity for a manual-trigger workflow. -
Release flow. When the input
refis a tag, also publish a draft GitHub release with the four binaries attached. - AmiSSL SDK. Phase 28 needed the SDK layered into the build environment — see that page's "SDK provisioning" subsection.