-
Notifications
You must be signed in to change notification settings - Fork 0
Phase 39 extmod opt ins
Part of the Amiga port design log.
Status: in progress. Steps 1–4 shipped; Steps 5–6 remain.
Audit of extmod/ against the current Amiga port config turned up a
handful of cross-port modules and feature flags that aren't currently
enabled but would plausibly pay off on AmigaOS. This phase is a menu;
which items actually land is to be decided when the phase is picked
up. Each candidate below carries a rough cost/benefit so the choice
can be made in context.
The current baseline (via MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES
plus the port-local opt-ins) already includes binascii, hashlib
(SHA-256 only), heapq, random, re, deflate decompress,
uctypes, cmath, framebuf, errno, select, plus port-local
json, socket, ssl, and VfsAmiga. The candidates here are the
gaps left by that baseline that look genuinely useful on Amiga (i.e.
not GPIO / radio / micro-flash territory).
-
Wall-clock
timesurface —time.time(),time.time_ns(),time.gmtime(),time.localtime(),time.mktime(). Currently thetimemodule only exposesticks_*andsleep*(defaults forMICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIMEandMICROPY_PY_TIME_TIME_TIME_NSare both 0 and the port hasn't overridden them). Backing:dos.library/DateStamp()(day + minute- tick since 1978-01-01) for the wallclock, plus optionally
battclock.resourcefor sub-minute granularity on machines that have a battery-backed clock. Implementation lives in a port-localMICROPY_PY_TIME_INCLUDEFILE. Probably the highest-impact item on the list — a lot of stdlib-flavoured Python assumestime.time()exists. ~150 lines of glue. The Amiga epoch offset is2922 * 86400seconds (8 years from 1970-01-01 to 1978-01-01); locale handling stays minimal (UTC;locale.libraryintegration is out of scope).
- tick since 1978-01-01) for the wallclock, plus optionally
-
hashlib.md5+hashlib.sha1— currently gated on upstreamMICROPY_PY_SSL(which this port doesn't set; it usesMICROPY_PY_AMIGA_SSLinstead), so both digests are off despite the C sources being compiled in for free withhashlib. One-line opt-in via#define MICROPY_PY_HASHLIB_MD5 (1)andMICROPY_PY_HASHLIB_SHA1 (1). Cheap and routinely needed for file checksums, HTTP digest auth, and content-addressed lookups. -
btree(extmod/modbtree.c+ the bundledlib/berkeley-db-1.xxsubmodule, already a git submodule of the repo). Pure C, no networking, no GPIO — just a persistent K/V store over the file API. Gives Amiga scripts a real on-disk dict without writing flat-file plumbing. ~30 KB binary cost. Theberkeley-dbsubmodule must be initialised (it isn't pulled by default on a fresh clone). Pairs naturally withVfsAmiga. -
MICROPY_PY_DEFLATE_COMPRESS— extends the existingdeflatemodule from decompress-only to a full read/write surface (gzip / zlib output). Single-flag opt-in. Useful for log compression, network payload packing, and.tar.gz/.zipgeneration paired withbinascii.crc32(already on). -
marshal— bytecode (de)serialisation. Disabled belowROM_LEVEL_EVERYTHING. Cheap to enable; only useful if a use case shows up for serialising code objects (tooling,.mpyintrospection beyond whatmpy-toolalready covers). Optional / low priority. -
websocket+webrepl(extmod/modwebsocket.c,extmod/modwebrepl.c). Layers on the existing port-local socket module to give a REPL over the network — a meaningful quality-of-life win on AmigaOS where the local console is the classic Shell window. Caveats: webrepl wants aduptermhook and theMICROPY_ENABLE_SCHEDULER, which this port has deferred along withasyncio(seeMICROPY_PY_ASYNCIO (0)andMICROPY_ENABLE_SCHEDULER (0)inmpconfigport.h). So this item is the most work of the lot — likely needs a small cooperative poll-from-VM-hook shim rather than a real scheduler. Defer until after theasynciostory is resolved.
-
asyncio— already deferred port-wide pending threads / scheduler. -
bluetooth,lwip,cyw43,wiznet5k,ninaw10,esp_hosted,nimble,btstack,network_ppp_lwip— hardware-specific (radio / Ethernet MAC drivers) with no Amiga fit. Networking on this port isbsdsocket.library-only and that's already wired. -
machine_*(adc,pwm,i2c,spi,uart,timer,wdt,usb_device,i2s,can,pulse,bitstream,signal,pinbase) — no Amiga peripheral equivalent. -
onewire— GPIO-based. -
vfs_fat,vfs_lfs,vfs_posix,vfs_rom,vfs_blockdev—VfsAmigaalready covers the filesystem layer viados.library. Mounting FAT/LFS images would be a separate, large project. -
modopenamp— multi-core (M4/M7) RPMsg. N/A. -
modplatform— platform-identification strings. Already covered byplatform.amiga_info()(Phase 33).
Which subset to actually implement is decided when the phase is picked up. Likely landing order if all are taken:
- Wall-clock
time(biggest API-surface gain). -
hashlibMD5 + SHA-1 (one-line, lots of use cases). -
MICROPY_PY_DEFLATE_COMPRESS(one-line). -
btree(medium effort, niche-but-real use case). -
marshal(optional). -
websocket+webrepl(waits on a scheduler-lite story; arguably its own phase rather than part of this one).
Each item lands as a discrete commit so a partial Phase 39 can stop cleanly between any two.
ports/amiga/mpconfigport.h — feature-flag flips
ports/amiga/modtime.c (new) — for item 1 (time wallclock)
ports/amiga/Makefile — SRC_C / SRC_QSTR additions
tests/ports/amiga/test_phase39_*.py — per-item smoke tests
Variants: all three. Cumulative size impact depends on which items
land — rough upper bound if everything except webrepl is taken is
~40 KB text (btree dominates).
Shipped:
-
time.time()andtime.time_ns(), backed bytimer.deviceGetSysTime()(already opened at startup byamiga_timer.cfor ticks_/delay_; no additional resource open). -
time.gmtime()/time.localtime()/time.mktime()viaextmod/modtime.c's timeutils path (MICROPY_EPOCH_IS_1970selected to match CPython; the 1978-1970 offset of2922 * 86400= 252,460,800 seconds is applied inports/amiga/modtime.c).
battclock.resource was considered and rejected: the AmigaOS
system clock is synchronised from battclock at boot and tracks any
user Date edits thereafter, so GetSysTime() is the authoritative
runtime source. Reading battclock directly during runtime would
race the boot synchronisation and skip the user's edits.
There is no timezone database on AmigaOS, so time.gmtime(t) and
time.localtime(t) return the same broken-down tuple for the same
t. Whatever the user has set their AmigaDOS clock to (local time
or UTC) is reflected directly; the port doesn't try to add a
timezone-offset layer.
vamos doesn't emulate GetSysTime (the call returns d0=0 with
the destination buffer untouched), so the smoke test
test_time_wallclock_smoke.py gates the "is this a real recent
wall-clock?" assertions behind a runtime detection and only
verifies static surface coverage (gmtime(0), gmtime(amiga_epoch),
mktime round-trip, time.time() / time_ns() return-type) on
that host. Full wall-clock validation lands on Amiberry / real
hardware.
Variants: all three (no FPU sensitivity). Build sizes:
| Variant | text |
|---|---|
standard |
586,660 bytes |
68020fpu |
557,244 bytes |
68040 |
567,828 bytes |
(Size delta from Phase 38: ~6-8 KB across variants, dominated by
shared/timeutils/timeutils.c which now compiles into the port for
the first time, plus the conditional gmtime/mktime/time/time_ns
helpers in extmod's modtime.c.)
Shipped:
-
hashlib.md5andhashlib.sha1are now in the module alongsidehashlib.sha256. All three follow the standard upstream API: one-shothashlib.foo(b"..."), streamingupdate(...)/digest(), and a final"hash is final"ValueErrorafter the firstdigest().
The upstream extmod/modhashlib.c only implements MD5 / SHA-1 via
MICROPY_SSL_MBEDTLS or MICROPY_SSL_AXTLS. We don't ship mbedtls
and AmiSSL is an OpenSSL-shaped runtime library (wrong API for both
backends), so the port routes through the axtls code path:
-
mpconfigport.hflipsMICROPY_SSL_AXTLS=1,MICROPY_PY_HASHLIB_MD5=1,MICROPY_PY_HASHLIB_SHA1=1. -
MICROPY_PY_SSLstays0, which keepsmodtls_axtls.candmodcryptolib.cdormant — only the algorithm sources are linked, not the rest of axTLS (notls1, nox509, nobigint...). -
ports/amiga/Makefileaddslib/axtls/crypto/md5.candlib/axtls/crypto/sha1.cdirectly toSRC_C, along with-Iflags forlib/axtls/ssl,lib/axtls/crypto, andextmod/axtls-include. A per-fileCFLAGSoverride silences axtls's older-Wall-noisy style (mirrorsextmod.mk's rule). - A minimal
ports/amiga/axtls-include/axtls_os_port.hshadows the upstream shim. The upstream copy pullsarpa/inet.h(for tls1'sSOCKET_READmacro), which on AmigaOS drags inexec/types.hvianetinet/in.h → sys/socket.h → devices/timer.h. That collides withlib/crypto-algorithms/sha256.h'sBYTE/WORDtypedefs (AmigaOS defines them as signedint8_t/int16_t; crypto- algorithms defines them asunsigned char/unsigned int). The port-local shim provides just#include <stdint.h>— enough for md5.c / sha1.c, and they don't use any of the upstream shim's socket / SHA-256-alias macros anyway.
tests/ports/amiga/test_hashlib_smoke.py pins each algorithm
against canonical vectors:
- MD5: RFC 1321 (
"","abc","message digest"). - SHA-1: FIPS 180-1 (
"","abc", the 56-byte"abcdbcdecdefdefg..."vector). - SHA-256: FIPS 180-2 (
"","abc").
Also covers streaming (split update(...) chains must equal the
one-shot digest) and the final-state contract (update / digest
after digest() must raise ValueError).
Side note from the test-vector verification: an early version of the test had a transcribed-wrong SHA-256(b"abc") expected value, which made the Amiga output look 4-bit-shifted vs the (typo'd) expected. SHA-256 was always producing the correct FIPS digest — confirmed by running the same input through host CPython. No SHA-256 bug exists.
Variants: all three. Build sizes:
| Variant | text | Δ vs Step 1 |
|---|---|---|
standard |
514,324 bytes | ~+3 KB |
68020fpu |
488,204 bytes | ~+3 KB |
68040 |
497,952 bytes | +2,764 B |
(Δ is the cost of md5.c + sha1.c plus the additional dispatch
in modhashlib.c. The 68040 baseline was re-measured against
HEAD~1 for the delta column; the other two are within a few
hundred bytes of the same figure.)
Shipped:
-
deflate.DeflateIO(stream, format)now writes as well as reads for all three formats (deflate.RAW,deflate.ZLIB,deflate.GZIP), so scripts can both decompress and compress. - The decompress half has been on since the port joined
EXTRA_FEATURES; Step 3 just flipsMICROPY_PY_DEFLATE_COMPRESSto1inmpconfigport.h. Single-line change.
No Makefile work was needed: extmod/moddeflate.c #includes
lib/uzlib/lz77.c (which in turn #includes defl_static.c)
under #if MICROPY_PY_DEFLATE_COMPRESS, so the compressor source
is already pulled into the existing moddeflate.o translation
unit.
Test coverage in tests/ports/amiga/test_deflate_compress_smoke.py:
- Compress→decompress round-trip for RAW, ZLIB, and GZIP against a repetitive payload, asserting both bytes-out < bytes-in and exact match after decode.
- Streaming write split across multiple
write(...)calls (including a zero-length write) yields the same compressed bytes as a single concatenated write. -
write(...)on a closedDeflateIOraisesOSError. - Documents the uzlib quirk that compressing an empty input
produces an empty byte stream rather than a header-only frame.
(CPython's
zlibemits a 2-byte zlib header + 4-byte Adler32 trailer; uzlib only emits anything if data has actually been written. The test pins the current behaviour rather than treating it as a bug — anyone needing CPython-compatible framing can prepend the header by hand.)
Variants: all three. Build sizes:
| Variant | text | Δ vs Step 2 |
|---|---|---|
standard |
515,976 bytes | +1,652 B |
68020fpu |
489,876 bytes | +1,672 B |
68040 |
499,648 bytes | +1,696 B |
(Δ is the incremental cost of lz77.c + defl_static.c once they
compile into moddeflate.o.)
Shipped:
-
import btreeopens a B-tree-on-stream key/value store viabtree.open(stream, ...). Keys and values are arbitrary bytes (anything implementing the buffer protocol works for both); the store layers on top of whatever stream the caller provides, so it works equally well against anio.BytesIO, aVfsAmigafile handle (open(path, "w+b")), or any other MicroPython stream. - Supports the standard dict-ish surface:
db[key]/db[key] = val/del db[key]/key in db,db.get(k[, default]),db.put(k, v), plusdb.keys()/db.values()/db.items()(withstart,end, andbtree.INCL/btree.DESCflags), anddb.flush()/db.close().
Wiring in this port:
-
ports/amiga/MakefilesetsMICROPY_PY_BTREE = 1as a make variable before includingextmod.mk. That make-side flag is what triggersextmod.mkto pull inlib/berkeley-db-1.xx/btree/*.c+mpool/mpool.cand add the matching-Ipath. The corresponding C macro is set automatically byextmod.mkviaCFLAGS_EXTMOD += -DMICROPY_PY_BTREE=1. -
ports/amiga/mpconfigport.hflipsMICROPY_STREAMS_POSIX_API = 1so themp_stream_posix_{read,write,lseek,fsync}shims (declared inpy/stream.h, defined inpy/stream.c) are visible toextmod/modbtree.c. Those shims bridge berkeley-db'sread(fd, buf, n)/write(fd, buf, n)/lseek(fd, ...)calls onto a Python stream object'sMP_STREAM_OP_*methods. - No port-local Berkeley DB config file is needed -- the upstream
extmod/berkeley-db/berkeley_db_config_port.hbuilds cleanly against AmigaOS clib2, which providessys/file.h,sys/param.h,sys/stat.h,fcntl.h, etc. with the expected signatures.
(Going through the stream protocol rather than direct file I/O is
also what lets the test exercise the whole module against an
in-memory io.BytesIO without touching the filesystem.)
Test coverage in tests/ports/amiga/test_btree_smoke.py:
-
open()/put/__getitem__/__contains__/__delitem__, including theKeyErrorpaths for missing keys. - Buffer-protocol arguments (memoryview slices used for both key and value).
-
keys(),items(),items(start, end),items(None, None, btree.DESC), plainfor k in db-- all asserted against the sorted key list. (Note: btree shares a single iteration cursor across all calls on the same DB object, so you can't runkeys()andvalues()concurrently viazip(). The test cross-checks values viadb[k]lookup instead.) -
flush()+close(), plus the "use after close" guard which raises eitherOSErrororValueError("database closed")depending on the call site (currentlyValueError, but the test accepts both for forward compatibility).
Variants: all three. Build sizes:
| Variant | text | Δ vs Step 3 |
|---|---|---|
standard |
535,292 bytes | +19,316 B |
68020fpu |
509,192 bytes | +19,316 B |
68040 |
519,152 bytes | +19,504 B |
(Δ is dominated by the 13 berkeley-db source files
[bt_*.c + mpool.c] plus modbtree.c itself, against
the MICROPY_STREAMS_POSIX_API shim weight.)
- Step 5:
marshal(optional). - Step 6:
websocket+webrepl(defer until scheduler-lite story resolved — likely promoted to its own phase).