-
Notifications
You must be signed in to change notification settings - Fork 0
Phases 13 27 platform integration
Part of the Amiga port design log.
Runtime and platform-integration polish: REPL line editing, a dynamic heap,
Pythonic VFS file I/O, Workbench launch, env-var integration, volume / assign
introspection, AmigaDOS pattern matching, timer.device-backed timing,
persistent REPL history, extra break signals, PROGDIR: on sys.path, and
the build-variant matrix.
Two of the larger phases in this range have their own pages: Phase 17 — Native AmigaOS library access and Phase 18 — ARexx integration.
shared/readline/readline.c drives the REPL. Cursor keys, history, kill/yank,
Home/End all work.
CSI translation. AmigaOS's console.device emits cursor reports as
single-byte CSI (0x9B) followed by parameters; shared/readline/ expects
the two-byte ESC [ form. mp_hal_stdin_rx_chr() keeps a one-byte pending
buffer: when FGetC returns 0x9B, return ESC and hand [ to the next
call. Hosts that already emit ESC [ (vamos's xterm pass-through) see no
change.
Running the REPL under vamos: tools/amiga-vamos-repl.sh puts the host
TTY into -icanon -echo -isig for the duration of the run (vamos's
SetMode(stdin,1) doesn't translate to tcsetattr on the host TTY).
AMIGA_VARIANT=68040 selects the build.
Heap is a chain of AllocVec chunks managed via MICROPY_GC_SPLIT_HEAP_AUTO.
Initial size from (priority order) -X heap=<N>[K|M], MICROPYHEAP env-var
(dos.library GetVar), compile-time MICROPY_HEAP_SIZE. Cap via
-X maxheap=<N> / MICROPYHEAPMAX.
main.c owns amiga_heap_chunks[16] for tracking (AllocVec is not
reclaimed by AmigaOS on task exit, so all chunks must be FreeVec'd
explicitly at shutdown). gc_get_max_new_split() reports
AvailMem(MEMF_ANY|MEMF_PUBLIC|MEMF_LARGEST) minus a small headroom, clamped
to -X maxheap. The GC's own sweep auto-releases empty grown chunks.
>>> amiga.heap_info() # → (total_bytes, free_bytes, num_arenas)
(256000, 248000, 1)Originally exec.library memory pools. Phase 14's dynamic GC heap covers the
common case; explicit native-buffer lifetimes can use alloc_vec. Number kept
to avoid renumbering later phases.
MICROPY_VFS=1 + MICROPY_READER_VFS=1 with a port-local VfsAmiga in
vfs_amiga.c. Stateless wrapper around Lock / Examine / CurrentDir /
Open / Read / Write / CreateDir / DeleteFile / Rename. AmigaDOS
keeps cwd in pr_CurrentDir. main() mounts a single VfsAmiga at /;
AmigaOS-style paths (volume:dir/file or relative) route directly.
amigafile.c and amigaio.c are deleted — mp_lexer_new_from_file comes
from extmod/vfs_reader.c; mp_import_stat is the inline that delegates to
mp_vfs_import_stat; mp_builtin_open_obj is aliased to mp_vfs_open_obj.
vfs_amiga.c reuses the pr_WindowPtr = -1 requester-suppression pattern
around every Lock. os.chdir keeps the first inherited cwd lock (it's the
shell's, not ours) and UnLocks subsequent ones. ilistdir uses a finaliser
so an abandoned for f in os.listdir(...) doesn't leak the directory lock.
open(..., "r+b") correctly maps to MODE_OLDFILE (fail-if-missing), not
MODE_READWRITE (create-on-missing).
WB-launched processes get a WBStartup message instead of argc/argv, and
their config comes from .info tooltypes.
Detecting. Bebbo's crt0.o already does the heavy lifting — when
pr_CLI == 0 it WaitPorts, GetMsgs the WBStartup, stashes the pointer
in the global _WBenchMsg, and on exit Forbid()s and ReplyMsgs back to
Workbench. We just extern struct WBStartup *_WBenchMsg; and test for null.
Console. WB-launched processes have pr_CIS/pr_COS both NULL, so
main.c opens CON:0/30/640/200/MicroPython/AUTO/CLOSE/WAIT and points
stdin/stdout/console-task at it. /AUTO defers window appearance until
write; /WAIT keeps it open after main() returns.
Tooltypes. icon.library GetDiskObject(sm_ArgList[0].wa_Name); handle
cached in IconBase + amiga_wb_diskobject. Two consumed at startup:
-
SCRIPT=<path>— script to run instead of REPL -
HEAP=<N>/MAXHEAP=<N>— same parser as-X heap=(env vars still win, since they're more explicitly user-set)
if amiga.launched_from_workbench():
extra = amiga.tooltype("EXTRA_PATH", "")
for path in amiga.wb_selected_files(): # shift-clicked icons
process(path)wb_selected_files() renders each WBArg with NameFromLock + AddPart.
Cannot be exercised under vamos (no Workbench, no icon.library); full
validation requires Amiberry/FS-UAE or real hardware.
os.getenv / os.putenv / os.unsetenv via dos.library GetVar/SetVar
with flags=0 (local CLI vars, falling through to global ENV:). Matches
Unix os.putenv semantics — visible to child processes spawned via
amiga.execute(), not to unrelated shells. For system-wide / persistent,
write to ENV:/ENVARC: directly with open().
mpconfigport.h: MICROPY_PY_OS_GETENV_PUTENV_UNSETENV (1) +
MICROPY_PY_OS_INCLUDEFILE "ports/amiga/modos.c". modos.c is #included
by extmod/modos.c; no Makefile changes.
Vamos workarounds (both correct on real AmigaOS):
-
GetVarreturns 0 (not -1) for a missing variable; we treatlen <= 0as missing (misreports a genuine empty-string variable on real AmigaOS, correct on vamos). -
DeleteVarreadsflagsfromD4, but the NDK fd specifiesD2. We use the V36-documented "SetVarwithNULLbuffer deletes" form instead.
amiga.volumes() # ['Python:', 'Ram Disk:', 'Workbench:']
amiga.assigns() # {'C:': 'Workbench:C', 'LIBS:': 'Workbench:Libs', ...}
amiga.disk_info(path) # (free_bytes, total_bytes, block_size)volumes()/assigns() walk LockDosList(LDF_VOLUMES/LDF_ASSIGNS | LDF_READ)
via NextDosEntry. Assign targets from NameFromLock(dol_Lock) for
DLT_DIRECTORY, or dol_misc.dol_assign.dol_AssignName for late/non-binding
assigns. Multi-directory assigns report first dir only.
disk_info(path) locks (with pr_WindowPtr=-1), calls Info(), computes
byte counts as uint64_t for >4 GB volumes. IoErr() mapped to MP_E*
constants (e.g. ERROR_DEVICE_NOT_MOUNTED → MP_ENODEV).
for path in amiga.match("S:#?"): # eager list
for path in amiga.imatch("Work:#?.py"): # lazy iteratorOne AnchorPath with trailing 512-byte buffer; ap_Strlen preset.
MatchFirst parses the pattern (no separate ParsePattern). Empty list for
ERROR_NO_MORE_ENTRIES / ERROR_OBJECT_NOT_FOUND; other DOS errors raise
OSError.
imatch uses mp_type_polymorph_iter_with_finaliser; the iterator owns the
AnchorPath, its finaliser calls MatchEnd + FreeVec so an abandoned
loop (for p in amiga.imatch(...): break) doesn't leak. MatchFirst runs
eagerly inside imatch() so the first result is ready on the first
next(). Both suppress auto-requesters.
Replaced clock()-based path (busy-wait, ms-ish resolution) with:
-
mp_hal_delay_us(n)→timer.device TR_ADDREQUESTviaDoIO()for ≥200 µs, tightReadEClock()busy-loop below. -
mp_hal_delay_ms(n)→timer.devicefor arbitrary-millisecond accuracy (the previousDelay()had 20 ms granularity). -
mp_hal_ticks_us/ms()→ReadEClock()(hardware counter, monotonic, cheap). EClock frequency cached at init.
Setup in amiga_timer.c: CreateMsgPort + CreateIORequest +
OpenDevice("timer.device", UNIT_MICROHZ, ...) once at startup, before
mp_init(). TimerBase (referenced by bebbo proto/timer.h inlines) is
set from the request's io_Device. IORequest stored port-local-static;
not thread-safe but the port is single-threaded.
amiga_history.c loads / saves S:MicroPython.history (override via
MICROPYHISTORY env-var). One entry per line, oldest first; CRLF tolerated
on read. MICROPY_READLINE_HISTORY_SIZE bumped to 32 (was 8). Both paths
suppress auto-requesters; failure (no S: volume, read-only, corrupt) is
silent — history just doesn't persist that session.
main.c calls amiga_history_load() after mp_init() and
amiga_history_save() before mp_deinit(). amiga.readline_history() and
amiga.readline_push_history(line) exposed for scripting / testing.
Not in ENVARC: — that's for preferences; a frequently-written log there
would make every reboot's copy ENVARC: ENV: all slower. S: is the
conventional AmigaOS dotfile spot.
amiga.signal(other_task_addr, amiga.SIGBREAKF_CTRL_E)
mask = amiga.wait_signal(amiga.SIGBREAKF_CTRL_D | amiga.SIGBREAKF_CTRL_E,
timeout_ms=5000)-
signal(task_addr, sigmask)→exec.library Signal(). NULL task raisesValueError. -
wait_signal(mask, timeout_ms=None)→Wait().SIGBREAKF_CTRL_Cis always ORed into the internal mask (so the user can break out) but always stripped from the return (so Ctrl+C never spuriously satisfies a user signal); if CTRL_C fires,KeyboardInterrupt.
timeout_ms uses a second, async timer.device IORequest in
amiga_timer.c (kept separate from Phase 23's synchronous request so a
pending Wait can't collide with an in-flight mp_hal_delay_us). SendIO
arms the timer; the MsgPort's signal bit ORs into the Wait mask; the
request is aborted/drained on return. If the second port fails at startup,
wait_signal falls back to an untimed Wait — documented best-effort.
PROGDIR: is the auto-assign AmigaDOS creates per process for the
executable's directory. main.c appends it to sys.path after
mp_init() (prepending would lose it on every script run, since
positional-script handling replaces sys.path[0] with the script's
directory). After a script launch, sys.path is
['<script_dir>', '.frozen', 'PROGDIR:'] — script wins, PROGDIR: as final
fallback.
make VARIANT=<name>; build dir is build-<variant>.
| Variant | CPU | Heap | Notes |
|---|---|---|---|
standard (default) |
-m68020 -msoft-float |
256 KB | Any 68020+, no FPU. Default for stock A1200 / unaccelerated 68030 |
68020fpu |
-m68020 -m68881 |
512 KB | 68020/30 + 68881/2 (A2630, A3000). No libgcc soft-float wraps |
68040 |
-m68040 |
1 MB | 68040 built-in FPU (A3640, A4000/040) |
Text-segment sizes: standard 356 KB, 68020fpu 330 KB, 68040 339 KB.
A500 isn't a target — its 68000 lacks unaligned access. Why 68040
is larger than 68020fpu: Motorola dropped transcendentals (FSIN/COS/TAN/
ATAN/etc.) from the 68040; gcc emits libm calls instead of inlines, pulling
in ~9 KB. Alternative -m68040 -m68881 would shrink it back and rely on
AmigaOS's FPSP (68040.library) to trap-and-emulate, but a binary that
uses FSIN on a 68040 without FPSP loaded (custom Workbench, stripped
startup, certain demos) gurus. The 9 KB is the price of self-containment.
sys.implementation._machine reports "Amiga with 68EC020" /
"68020/68881" / "68040". floatconv.c pow/tgamma wraps apply on
all variants (math-library bugs, not FPU codegen).
FPU variants require bebbo to have FPU multilibs (make all provides them);
if m68k-amigaos-gcc -m68881 -print-multi-lib shows only a soft-float entry,
the link fails with cannot find -lgcc.