freedos-micro-python 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. freedos_micro_python/__init__.py +8 -0
  2. freedos_micro_python/cli.py +106 -0
  3. freedos_micro_python/gen_qstrdefs.py +275 -0
  4. freedos_micro_python/port/arch/bpstruct.h +2 -0
  5. freedos_micro_python/port/arch/cc.h +4 -0
  6. freedos_micro_python/port/arch/epstruct.h +1 -0
  7. freedos_micro_python/port/base64_uc386dos.c +164 -0
  8. freedos_micro_python/port/file_uc386dos.c +228 -0
  9. freedos_micro_python/port/lib/axtls/crypto/crypto.h +45 -0
  10. freedos_micro_python/port/lwip-arch-cc.h +46 -0
  11. freedos_micro_python/port/lwip_uc386dos.c +248 -0
  12. freedos_micro_python/port/lwipopts.h +117 -0
  13. freedos_micro_python/port/math_gamma.c +63 -0
  14. freedos_micro_python/port/modtime_uc386dos.c +60 -0
  15. freedos_micro_python/port/modtls_axtls_uc386dos.c +461 -0
  16. freedos_micro_python/port/mpconfigport.h +358 -0
  17. freedos_micro_python/port/mphal_uc386dos.c +103 -0
  18. freedos_micro_python/port/mphalport.h +11 -0
  19. freedos_micro_python/port/os_uc386dos.c +264 -0
  20. freedos_micro_python/port/path_uc386dos.c +307 -0
  21. freedos_micro_python/port/pktdrv_uc386dos.c +650 -0
  22. freedos_micro_python/port/qstrdefsport.h +2 -0
  23. freedos_micro_python/port/shutil_uc386dos.c +111 -0
  24. freedos_micro_python/port/tempfile_uc386dos.c +129 -0
  25. freedos_micro_python/port/time_real_uc386dos.c +77 -0
  26. freedos_micro_python/port/uc386_net_uc386dos.c +126 -0
  27. freedos_micro_python/port/urllib_parse_uc386dos.c +360 -0
  28. freedos_micro_python/port/urllib_uc386dos.c +29 -0
  29. freedos_micro_python/scripts/build.sh +641 -0
  30. freedos_micro_python/scripts/build_port.sh +241 -0
  31. freedos_micro_python/scripts/fetch.sh +238 -0
  32. freedos_micro_python-0.1.0.dist-info/METADATA +131 -0
  33. freedos_micro_python-0.1.0.dist-info/RECORD +37 -0
  34. freedos_micro_python-0.1.0.dist-info/WHEEL +5 -0
  35. freedos_micro_python-0.1.0.dist-info/entry_points.txt +2 -0
  36. freedos_micro_python-0.1.0.dist-info/licenses/LICENSE +25 -0
  37. freedos_micro_python-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,641 @@
1
+ #!/bin/sh
2
+ # Per-file triage of the upstream/py/ core sources through uc386.
3
+ #
4
+ # This is NOT a full build (yet) — MicroPython needs a port shim
5
+ # (mp_hal_stdout_tx_*, mp_hal_delay_*, a fixed-region heap for GC,
6
+ # argv plumbing) before it can produce a runnable image. Today this
7
+ # script answers the prerequisite question: how many of the ~50
8
+ # platform-independent .c files in py/ compile cleanly through
9
+ # uc386 → NASM-ready .asm? An error histogram surfaces the top
10
+ # blockers as concrete tickets.
11
+ #
12
+ # Outputs:
13
+ # build/<name>.asm — for every PASS
14
+ # build/<name>.err — uc386 stderr for every FAIL
15
+ # build/triage.txt — per-source PASS/FAIL summary
16
+ # build/errors.txt — first-line error histogram (sorted by count)
17
+ set -eu
18
+
19
+
20
+ if [ ! -d upstream ]; then
21
+ echo "micropython: run ./fetch.sh first." >&2
22
+ exit 1
23
+ fi
24
+
25
+ # See build_port.sh header for the dual-invocation contract.
26
+ if [ -z "${UC386_LIB_INCLUDE:-}" ]; then
27
+ REPO="$(cd ../../.. && pwd)"
28
+ UC386_LIB_INCLUDE="$REPO/src/uc386/lib/include"
29
+ fi
30
+ if [ -n "${PYTHON:-}" ]; then
31
+ :
32
+ elif [ -n "${REPO:-}" ] && [ -x "$REPO/.venv/bin/python" ]; then
33
+ PYTHON="$REPO/.venv/bin/python"
34
+ else
35
+ PYTHON="$(command -v python3.12 || command -v python3 || command -v python)"
36
+ fi
37
+ INCLUDE="$UC386_LIB_INCLUDE"
38
+ SRC_DIR="upstream/py"
39
+ PORT_DIR="upstream/ports/minimal" # supplies mpconfigport.h + mphalport.h
40
+
41
+ mkdir -p build build/genhdr
42
+
43
+ # Stub the upstream-generated headers that py/*.c expects. A real
44
+ # build runs upstream/py/makeqstrdefs.py + makemoduledefs.py to
45
+ # emit these from the source tree; for triage we ship empty stubs
46
+ # so the preprocessor finds them and we see compile-class failures
47
+ # (uc386 limitations) instead of a wall of missing-header errors.
48
+ # Patch upstream/extmod/modtime.c so its `#include MICROPY_PY_TIME_INCLUDEFILE`
49
+ # becomes a literal include of our port shim. uc386's preprocessor
50
+ # doesn't support macro-name-in-#include (the GCC/Clang feature
51
+ # where the preprocessor expands the macro and then re-tokenizes).
52
+ # Idempotent: checks for the already-patched literal before
53
+ # re-applying.
54
+ if grep -q '^#include MICROPY_PY_TIME_INCLUDEFILE$' upstream/extmod/modtime.c; then
55
+ # Bare filename — found via build_port.sh's `-I uc386-dos` search path.
56
+ sed -i.bak \
57
+ 's|^#include MICROPY_PY_TIME_INCLUDEFILE$|#include "modtime_uc386dos.c"|' \
58
+ upstream/extmod/modtime.c
59
+ rm -f upstream/extmod/modtime.c.bak
60
+ fi
61
+
62
+ # Patch upstream/ports/minimal/main.c so its stub `mp_import_stat`
63
+ # and `mp_lexer_new_from_file` don't collide with the real
64
+ # implementations our port provides in `uc386-dos/file_uc386dos.c`.
65
+ # The minimal port hardcodes "no filesystem" responses; we overrride
66
+ # with INT 21h-backed file I/O via uc386's libc. Idempotent: checks
67
+ # for the already-patched marker before re-applying.
68
+ if grep -q "^mp_import_stat_t mp_import_stat" upstream/ports/minimal/main.c; then
69
+ sed -i.bak \
70
+ -e 's|^mp_lexer_t \*mp_lexer_new_from_file(qstr filename) {|static mp_lexer_t *_unused_mp_lexer_new_from_file(qstr filename) { (void)filename;|' \
71
+ -e 's|^mp_import_stat_t mp_import_stat(const char \*path) {|static mp_import_stat_t _unused_mp_import_stat(const char *path) { (void)path;|' \
72
+ upstream/ports/minimal/main.c
73
+ rm -f upstream/ports/minimal/main.c.bak
74
+ fi
75
+
76
+ # Patch upstream/ports/minimal/main.c to initialize sys.argv as an
77
+ # empty list right after mp_init(). MICROPY_PY_SYS_ARGV expects the
78
+ # `mp_sys_argv_obj` root pointer to hold a real list object — without
79
+ # this init, sys.argv reads back as a half-initialized struct and
80
+ # crashes on `len(sys.argv)`. Idempotent: only inserts if not already
81
+ # present.
82
+ if ! grep -q "mp_obj_list_init.*mp_sys_argv_obj" upstream/ports/minimal/main.c; then
83
+ sed -i.bak \
84
+ 's|^ mp_init();$| mp_init();\n mp_obj_list_init((mp_obj_list_t *)\&MP_STATE_VM(mp_sys_argv_obj), 0);|' \
85
+ upstream/ports/minimal/main.c
86
+ rm -f upstream/ports/minimal/main.c.bak
87
+ fi
88
+
89
+ # Patch upstream/ports/minimal/main.c to wire up `MICROPY_STACK_CHECK`.
90
+ # `mp_stack_ctrl_init()` captures the real stack top from a local
91
+ # stack variable; `mp_stack_set_limit(LIMIT)` sets the recursion-
92
+ # depth cap. Without these calls, every `mp_stack_check()` fires
93
+ # immediately (stack_top is NULL → mp_stack_usage returns a huge
94
+ # value → recursion-depth raise infinite-loops on its own setup).
95
+ # 0xC0000 = 768 KB, leaving 256 KB margin in dos_emu's 1 MB stack.
96
+ # Idempotent: only inserts if not already present.
97
+ if ! grep -q "mp_stack_ctrl_init" upstream/ports/minimal/main.c; then
98
+ sed -i.bak \
99
+ 's|^ mp_init();$| mp_stack_ctrl_init();\n mp_stack_set_limit(0xC0000);\n mp_init();|' \
100
+ upstream/ports/minimal/main.c
101
+ rm -f upstream/ports/minimal/main.c.bak
102
+ fi
103
+
104
+ # Patch upstream/py/formatfloat.c so the DOUBLE-mode `repr()` doesn't
105
+ # request more digits than uc386's double-precision FPU can deliver.
106
+ # Default is `MAX_MANTISSA_DIGITS=19` (designed for the EXACT formatter
107
+ # that runs on long double, ~80-bit). uc386 lowers double through the
108
+ # x87 FPU but stores `long double` as 8 bytes (= double), so the
109
+ # APPROX formatter at 19 digits surfaces the last 3 noise digits as
110
+ # wrong values: `print(4.0)` becomes `3.99999999999999382` instead of
111
+ # `4.0`. Lower the request to SAFE_MANTISSA_DIGITS=16 (already
112
+ # defined adjacent in the file). Idempotent: checks for the
113
+ # already-patched value before re-applying.
114
+ if grep -q "^#define MAX_MANTISSA_DIGITS (19)$" upstream/py/formatfloat.c; then
115
+ sed -i.bak 's/^#define MAX_MANTISSA_DIGITS (19)$/#define MAX_MANTISSA_DIGITS (16) \/\/ uc386: long double == double, cap at SAFE/' upstream/py/formatfloat.c
116
+ rm -f upstream/py/formatfloat.c.bak
117
+ fi
118
+
119
+ # Patch upstream/py/parsenum.c to neutralize the
120
+ # `assert(sizeof(mp_large_float_t) > sizeof(mp_float_t))` in
121
+ # `mp_decimal_exp`. uc386 stores long double as 8 bytes (= double),
122
+ # so the assert fires at runtime on the first compile-time-evaluated
123
+ # float. The algorithm still works — just at double precision —
124
+ # and the verify-retry loop in formatfloat.c delivers correctness
125
+ # regardless. Idempotent: leaves the assert commented after first
126
+ # patch.
127
+ if grep -q "^ assert(sizeof(mp_large_float_t) > sizeof(mp_float_t));$" upstream/py/parsenum.c; then
128
+ sed -i.bak \
129
+ 's|^ assert(sizeof(mp_large_float_t) > sizeof(mp_float_t));$| /* uc386: removed — long double == double, see mpconfigport.h */|' \
130
+ upstream/py/parsenum.c
131
+ rm -f upstream/py/parsenum.c.bak
132
+ fi
133
+
134
+ if [ ! -f build/genhdr/qstrdefs.generated.h ]; then
135
+ # Emit a triage qstr table by grep over upstream/py/ +
136
+ # upstream/shared/. Real builds use upstream's
137
+ # tools/makeqstrdefs.py which preprocesses each TU to find
138
+ # MP_QSTR_x macro uses; for triage we approximate with a grep
139
+ # over the source tree, which over-includes (any string that
140
+ # parses as an identifier becomes a qstr) but keeps the mapping
141
+ # complete enough that the enum in py/qstr.h covers every
142
+ # reference downstream code (py/, shared/runtime, etc.) makes.
143
+ #
144
+ # The first entry (MP_QSTRnull) goes in the static pool as
145
+ # QDEF0; everything else goes in the main pool as QDEF1.
146
+ # Reason: `mp_qstr_const_pool` (the main pool) has
147
+ # `is_sorted = true` and the binary search in
148
+ # `qstr_find_strn` does `pool->len - 1` as its upper bound —
149
+ # which underflows to 0xFFFFFFFF when `pool->len == 0`,
150
+ # then loops the binary search into out-of-bounds reads.
151
+ # A real upstream build always has at least one QDEF1 entry,
152
+ # so the empty-main-pool case never happens. We avoid it by
153
+ # routing every grep'd qstr to QDEF1.
154
+ {
155
+ # MP_QSTRnull (id 0) is the null/sentinel qstr. The rest of
156
+ # the static + unsorted qstrs (`__add__`, `print`, `__name__`,
157
+ # ...) are emitted by gen_qstrdefs.py from upstream's
158
+ # `static_qstr_list` + `unsorted_qstr_list` so they get
159
+ # ids < 256 — required for the byte-stored
160
+ # `mp_binary_op_method_name` table at py/objtype.c:483.
161
+ echo "QDEF0(MP_QSTRnull, 0, 0, \"\")"
162
+ # `MP_QSTR_<name>` — skip the 8-char `MP_QSTR_` prefix (M-P-_-Q-S-T-R-_)
163
+ # to recover the actual qstr string. Earlier the prefix-strip used 7
164
+ # chars and left a stray leading underscore (`__repl_print__` came
165
+ # out as `___repl_print__`), making LOAD_NAME for builtins fail and
166
+ # value-print compile crash inside mp_obj_equal_not_equal.
167
+ #
168
+ # Emit `length(name)` as the third QDEF1 field. qstr_find_strn's
169
+ # post-binary-search linear sweep checks lengths[at] == str_len
170
+ # before doing memcmp. Length 0 (the previous heuristic) made
171
+ # every static qstr fail the length check, causing
172
+ # `qstr_find_strn("__name__")` to return MP_QSTRnull even
173
+ # though the qstr was in the pool — so the bytecode compiler
174
+ # invented a fresh dynamic qstr for every identifier and
175
+ # LOAD_NAME against the static-init dict_main key (whose qstr
176
+ # id is the static 67) would never match.
177
+ # gen_qstrdefs.py reverse-mangles each `MP_QSTR_<sanitized>`
178
+ # macro back to its source string (e.g. `MP_QSTR__0x0a_` →
179
+ # `"\n"`, `MP_QSTR__lt_stdin_gt_` → `"<stdin>"`) using
180
+ # upstream's own codepoint2name table for fidelity, then emits
181
+ # QDEF1 lines sorted by the *original* string in ASCII order.
182
+ #
183
+ # Sort key matters: qstr_find_strn does
184
+ # `strncmp(probe, pool->qstrs[mid], n)` against the 4th QDEF1
185
+ # field (the un-escaped string). The pool's `is_sorted=true`
186
+ # invariant therefore requires sort-by-original. Earlier we
187
+ # used `LC_ALL=C sort -u` over macro names, which coincided
188
+ # for pure-identifier qstrs (`print`, `__name__`) but broke
189
+ # for escaped ones — `MP_QSTR__0x0a_` lex-orders near `_`,
190
+ # while its actual byte (0x0A) would sort first. With the
191
+ # macro-name-as-payload heuristic, escaped qstrs also rendered
192
+ # the *macro tail* as their string content, so `print()`'s
193
+ # trailing `\n` showed up as the literal text `_0x0a_`.
194
+ # `--bytes-hash 2` matches MICROPY_QSTR_BYTES_IN_HASH at
195
+ # ROM_LEVEL_EXTRA_FEATURES (uc386-dos/mpconfigport.h sets
196
+ # ROM_LEVEL=EXTRA, which makes BYTES_IN_HASH=2). Required:
197
+ # qstr_find_strn's post-binary-search filter does
198
+ # `pool->hashes[at] == str_hash` before memcmp at any
199
+ # non-zero MICROPY_QSTR_BYTES_IN_HASH — a stale `--bytes-hash 1`
200
+ # gen would emit 8-bit hashes and the runtime would
201
+ # truncate the lookup hash to 16 bits, mismatching every
202
+ # entry.
203
+ #
204
+ # The grep also pulls in X-macro NAMES from moderrno.c's
205
+ # MICROPY_PY_ERRNO_LIST so the `MP_QSTR_##e` token paste
206
+ # in moderrno.c's globals table resolves at compile time.
207
+ # Without this, enabling MICROPY_PY_ERRNO would fail with
208
+ # `__static_moderrno__errorcode_table.value: float init must
209
+ # be a constant expression (got Identifier)` because uc386
210
+ # can't resolve `MP_QSTR_EPERM` etc. as enum constants.
211
+ # Translate `X(NAME)` lines into `MP_QSTR_NAME` lines that
212
+ # gen_qstrdefs.py treats identically to a regular reference.
213
+ {
214
+ grep -rhoE "MP_QSTR_[A-Za-z_][A-Za-z0-9_]*" \
215
+ upstream/py/ upstream/shared/ upstream/extmod/ \
216
+ uc386-dos/
217
+ # POSIX `[[:space:]]` rather than `\s` — macOS's BSD
218
+ # `sed -E` doesn't honor PCRE shorthand in BRE/ERE.
219
+ grep -hoE "^[[:space:]]*X\([A-Z][A-Z0-9_]*\)" \
220
+ upstream/py/moderrno.c \
221
+ | sed -E 's/^[[:space:]]*X\(([A-Z][A-Z0-9_]*)\)/MP_QSTR_\1/'
222
+ } | "$PYTHON" gen_qstrdefs.py --bytes-hash 2
223
+ } > build/genhdr/qstrdefs.generated.h
224
+ fi
225
+ # Always regenerate moduledefs.h — it's small and the cache turned
226
+ # stealth-stale when we added new UCDOS_MOD_ENTRY_* entries below
227
+ # without touching the file's mtime, so the new module didn't
228
+ # register and its `import` raised at runtime.
229
+ cat > build/genhdr/moduledefs.h <<'EOF'
230
+ // Hand-rolled equivalent of `upstream/py/makemoduledefs.py`'s output,
231
+ // covering the modules our uc386-dos port supports at the
232
+ // CORE_FEATURES ROM level. A real upstream build runs
233
+ // `tools/makeqstrdefs.py cat module` to preprocess each TU and
234
+ // extract MP_REGISTER_MODULE invocations after `#if` filtering, then
235
+ // pipes the result through makemoduledefs.py. We approximate by
236
+ // emitting each entry under the same `#if` gate the module's source
237
+ // file uses, so flipping `MICROPY_PY_<X>` in mpconfigport.h adds or
238
+ // drops the entry consistently.
239
+ //
240
+ // Modules NOT registered here:
241
+ // - cmath — requires MICROPY_PY_CMATH; we have float math
242
+ // via the x87 FPU but no complex-number support
243
+ // today.
244
+ // - _thread — requires MICROPY_PY_THREAD (single-threaded
245
+ // DOS, no need today).
246
+ // - weakref — requires MICROPY_PY_WEAKREF (off at CORE).
247
+ // - io — requires MICROPY_PY_IO + a VFS implementation;
248
+ // the port has no VFS today so `open()` is a
249
+ // no-op. Pre-set MICROPY_PY_IO=0 in
250
+ // mpconfigport.h to skip.
251
+
252
+ // All modules registered as regular (non-extensible). Extensible
253
+ // only matters with a VFS so users can override built-ins with .py
254
+ // files; the port has no VFS so the distinction is moot.
255
+
256
+ extern const struct _mp_obj_module_t mp_module_builtins;
257
+ extern const struct _mp_obj_module_t mp_module_sys;
258
+ extern const struct _mp_obj_module_t mp_module___main__;
259
+
260
+ #if MICROPY_PY_GC
261
+ extern const struct _mp_obj_module_t mp_module_gc;
262
+ #define UCDOS_MOD_ENTRY_GC { MP_ROM_QSTR(MP_QSTR_gc), MP_ROM_PTR(&mp_module_gc) },
263
+ #else
264
+ #define UCDOS_MOD_ENTRY_GC
265
+ #endif
266
+
267
+ #if MICROPY_PY_MATH
268
+ extern const struct _mp_obj_module_t mp_module_math;
269
+ #define UCDOS_MOD_ENTRY_MATH { MP_ROM_QSTR(MP_QSTR_math), MP_ROM_PTR(&mp_module_math) },
270
+ #else
271
+ #define UCDOS_MOD_ENTRY_MATH
272
+ #endif
273
+
274
+ #if MICROPY_PY_MICROPYTHON
275
+ extern const struct _mp_obj_module_t mp_module_micropython;
276
+ #define UCDOS_MOD_ENTRY_MICROPYTHON { MP_ROM_QSTR(MP_QSTR_micropython), MP_ROM_PTR(&mp_module_micropython) },
277
+ #else
278
+ #define UCDOS_MOD_ENTRY_MICROPYTHON
279
+ #endif
280
+
281
+ #if MICROPY_PY_ARRAY
282
+ extern const struct _mp_obj_module_t mp_module_array;
283
+ #define UCDOS_MOD_ENTRY_ARRAY { MP_ROM_QSTR(MP_QSTR_array), MP_ROM_PTR(&mp_module_array) },
284
+ #else
285
+ #define UCDOS_MOD_ENTRY_ARRAY
286
+ #endif
287
+
288
+ #if MICROPY_PY_COLLECTIONS
289
+ extern const struct _mp_obj_module_t mp_module_collections;
290
+ #define UCDOS_MOD_ENTRY_COLLECTIONS { MP_ROM_QSTR(MP_QSTR_collections), MP_ROM_PTR(&mp_module_collections) },
291
+ #else
292
+ #define UCDOS_MOD_ENTRY_COLLECTIONS
293
+ #endif
294
+
295
+ #if MICROPY_PY_ERRNO
296
+ extern const struct _mp_obj_module_t mp_module_errno;
297
+ #define UCDOS_MOD_ENTRY_ERRNO { MP_ROM_QSTR(MP_QSTR_errno), MP_ROM_PTR(&mp_module_errno) },
298
+ #else
299
+ #define UCDOS_MOD_ENTRY_ERRNO
300
+ #endif
301
+
302
+ #if MICROPY_PY_STRUCT
303
+ extern const struct _mp_obj_module_t mp_module_struct;
304
+ #define UCDOS_MOD_ENTRY_STRUCT { MP_ROM_QSTR(MP_QSTR_struct), MP_ROM_PTR(&mp_module_struct) },
305
+ #else
306
+ #define UCDOS_MOD_ENTRY_STRUCT
307
+ #endif
308
+
309
+ #if MICROPY_PY_TIME
310
+ extern const struct _mp_obj_module_t mp_module_time;
311
+ #define UCDOS_MOD_ENTRY_TIME { MP_ROM_QSTR(MP_QSTR_time), MP_ROM_PTR(&mp_module_time) },
312
+ #else
313
+ #define UCDOS_MOD_ENTRY_TIME
314
+ #endif
315
+
316
+ #if MICROPY_PY_UCTYPES
317
+ extern const struct _mp_obj_module_t mp_module_uctypes;
318
+ #define UCDOS_MOD_ENTRY_UCTYPES { MP_ROM_QSTR(MP_QSTR_uctypes), MP_ROM_PTR(&mp_module_uctypes) },
319
+ #else
320
+ #define UCDOS_MOD_ENTRY_UCTYPES
321
+ #endif
322
+
323
+ #if MICROPY_PY_RANDOM
324
+ extern const struct _mp_obj_module_t mp_module_random;
325
+ #define UCDOS_MOD_ENTRY_RANDOM { MP_ROM_QSTR(MP_QSTR_random), MP_ROM_PTR(&mp_module_random) },
326
+ #else
327
+ #define UCDOS_MOD_ENTRY_RANDOM
328
+ #endif
329
+
330
+ #if MICROPY_PY_BINASCII
331
+ extern const struct _mp_obj_module_t mp_module_binascii;
332
+ #define UCDOS_MOD_ENTRY_BINASCII { MP_ROM_QSTR(MP_QSTR_binascii), MP_ROM_PTR(&mp_module_binascii) },
333
+ #else
334
+ #define UCDOS_MOD_ENTRY_BINASCII
335
+ #endif
336
+
337
+ #if MICROPY_PY_HASHLIB
338
+ extern const struct _mp_obj_module_t mp_module_hashlib;
339
+ #define UCDOS_MOD_ENTRY_HASHLIB { MP_ROM_QSTR(MP_QSTR_hashlib), MP_ROM_PTR(&mp_module_hashlib) },
340
+ #else
341
+ #define UCDOS_MOD_ENTRY_HASHLIB
342
+ #endif
343
+
344
+ #if MICROPY_PY_RE
345
+ extern const struct _mp_obj_module_t mp_module_re;
346
+ #define UCDOS_MOD_ENTRY_RE { MP_ROM_QSTR(MP_QSTR_re), MP_ROM_PTR(&mp_module_re) },
347
+ #else
348
+ #define UCDOS_MOD_ENTRY_RE
349
+ #endif
350
+
351
+ #if MICROPY_PY_CMATH
352
+ extern const struct _mp_obj_module_t mp_module_cmath;
353
+ #define UCDOS_MOD_ENTRY_CMATH { MP_ROM_QSTR(MP_QSTR_cmath), MP_ROM_PTR(&mp_module_cmath) },
354
+ #else
355
+ #define UCDOS_MOD_ENTRY_CMATH
356
+ #endif
357
+
358
+ // `os` is the custom uc386-dos shim from `uc386-dos/os_uc386dos.c`
359
+ // (mkdir/rmdir/unlink/rename/chdir/getcwd/listdir backed by INT 21h
360
+ // via uc386's libc). Always registered — there's no MICROPY_PY_OS
361
+ // gate on our shim.
362
+ extern const struct _mp_obj_module_t mp_module_os;
363
+ #define UCDOS_MOD_ENTRY_OS { MP_ROM_QSTR(MP_QSTR_os), MP_ROM_PTR(&mp_module_os) },
364
+
365
+ #if MICROPY_PY_HEAPQ
366
+ extern const struct _mp_obj_module_t mp_module_heapq;
367
+ #define UCDOS_MOD_ENTRY_HEAPQ { MP_ROM_QSTR(MP_QSTR_heapq), MP_ROM_PTR(&mp_module_heapq) },
368
+ #else
369
+ #define UCDOS_MOD_ENTRY_HEAPQ
370
+ #endif
371
+
372
+ #if MICROPY_PY_DEFLATE
373
+ extern const struct _mp_obj_module_t mp_module_deflate;
374
+ #define UCDOS_MOD_ENTRY_DEFLATE { MP_ROM_QSTR(MP_QSTR_deflate), MP_ROM_PTR(&mp_module_deflate) },
375
+ #else
376
+ #define UCDOS_MOD_ENTRY_DEFLATE
377
+ #endif
378
+
379
+ #if MICROPY_PY_IO
380
+ extern const struct _mp_obj_module_t mp_module_io;
381
+ #define UCDOS_MOD_ENTRY_IO { MP_ROM_QSTR(MP_QSTR_io), MP_ROM_PTR(&mp_module_io) },
382
+ #else
383
+ #define UCDOS_MOD_ENTRY_IO
384
+ #endif
385
+
386
+ #if MICROPY_PY_JSON
387
+ extern const struct _mp_obj_module_t mp_module_json;
388
+ #define UCDOS_MOD_ENTRY_JSON { MP_ROM_QSTR(MP_QSTR_json), MP_ROM_PTR(&mp_module_json) },
389
+ #else
390
+ #define UCDOS_MOD_ENTRY_JSON
391
+ #endif
392
+
393
+ #if MICROPY_PY_PLATFORM
394
+ extern const struct _mp_obj_module_t mp_module_platform;
395
+ #define UCDOS_MOD_ENTRY_PLATFORM { MP_ROM_QSTR(MP_QSTR_platform), MP_ROM_PTR(&mp_module_platform) },
396
+ #else
397
+ #define UCDOS_MOD_ENTRY_PLATFORM
398
+ #endif
399
+
400
+ // `base64`, `shutil`, `tempfile` — port-supplied modules in
401
+ // uc386-dos/{base64,shutil,tempfile}_uc386dos.c. No upstream
402
+ // gates; always registered.
403
+ extern const struct _mp_obj_module_t mp_module_base64;
404
+ extern const struct _mp_obj_module_t mp_module_shutil;
405
+ extern const struct _mp_obj_module_t mp_module_tempfile;
406
+ #define UCDOS_MOD_ENTRY_BASE64 { MP_ROM_QSTR(MP_QSTR_base64), MP_ROM_PTR(&mp_module_base64) },
407
+ #define UCDOS_MOD_ENTRY_SHUTIL { MP_ROM_QSTR(MP_QSTR_shutil), MP_ROM_PTR(&mp_module_shutil) },
408
+ #define UCDOS_MOD_ENTRY_TEMPFILE { MP_ROM_QSTR(MP_QSTR_tempfile), MP_ROM_PTR(&mp_module_tempfile) },
409
+
410
+ // `lwip` + `socket` — both back the same `mp_module_lwip` from
411
+ // upstream's extmod/modlwip.c. The Python-level `socket` API uses
412
+ // the lwIP raw API via that module.
413
+ #if MICROPY_PY_LWIP
414
+ extern const struct _mp_obj_module_t mp_module_lwip;
415
+ #define UCDOS_MOD_ENTRY_LWIP { MP_ROM_QSTR(MP_QSTR_lwip), MP_ROM_PTR(&mp_module_lwip) },
416
+ #define UCDOS_MOD_ENTRY_SOCKET { MP_ROM_QSTR(MP_QSTR_socket), MP_ROM_PTR(&mp_module_lwip) },
417
+ #else
418
+ #define UCDOS_MOD_ENTRY_LWIP
419
+ #define UCDOS_MOD_ENTRY_SOCKET
420
+ #endif
421
+
422
+ // `urllib` + `urllib_parse` — port-supplied. Registered as a
423
+ // `urllib` package shim (with `parse` as an attribute, so
424
+ // `from urllib import parse` works) and a top-level `urllib_parse`
425
+ // (so `import urllib_parse` works). `import urllib.parse` resolves
426
+ // via MP's dotted-import path: it imports `urllib`, then reads the
427
+ // `parse` attribute. We don't register a dotted name in the
428
+ // builtin-modules table — the qstr-grep doesn't see the dotted
429
+ // form anyway since `.` can't appear in a C identifier.
430
+ extern const struct _mp_obj_module_t mp_module_urllib;
431
+ extern const struct _mp_obj_module_t mp_module_urllib_parse;
432
+ #define UCDOS_MOD_ENTRY_URLLIB { MP_ROM_QSTR(MP_QSTR_urllib), MP_ROM_PTR(&mp_module_urllib) },
433
+ #define UCDOS_MOD_ENTRY_URLLIB_PARSE { MP_ROM_QSTR(MP_QSTR_urllib_parse), MP_ROM_PTR(&mp_module_urllib_parse) },
434
+
435
+ // `uc386_net` — port-supplied. Control surface for the lwIP eth
436
+ // netif sitting on the INT 0x83 packet-driver shim. Always
437
+ // registered when LWIP is on (no separate config gate yet).
438
+ #if MICROPY_PY_LWIP
439
+ extern const struct _mp_obj_module_t mp_module_uc386_net;
440
+ #define UCDOS_MOD_ENTRY_UC386_NET { MP_ROM_QSTR(MP_QSTR_uc386_net), MP_ROM_PTR(&mp_module_uc386_net) },
441
+ #else
442
+ #define UCDOS_MOD_ENTRY_UC386_NET
443
+ #endif
444
+
445
+ // `select` — extmod/modselect.c. Provides `select.select()` and
446
+ // `select.poll()` over the stream-protocol's poll hook (which
447
+ // modlwip.c's socket type implements). Useful for non-blocking
448
+ // I/O multiplexing.
449
+ #if MICROPY_PY_SELECT
450
+ extern const struct _mp_obj_module_t mp_module_select;
451
+ #define UCDOS_MOD_ENTRY_SELECT { MP_ROM_QSTR(MP_QSTR_select), MP_ROM_PTR(&mp_module_select) },
452
+ #else
453
+ #define UCDOS_MOD_ENTRY_SELECT
454
+ #endif
455
+
456
+ // `tls` (and the `ssl` alias) — extmod/modtls_axtls.c. Wraps a
457
+ // socket through axtls: SSLContext + wrap_socket -> SSLSocket
458
+ // implementing the standard stream protocol. The actual axtls
459
+ // library lives at upstream/lib/axtls/{ssl,crypto}/.
460
+ #if MICROPY_PY_SSL
461
+ extern const struct _mp_obj_module_t mp_module_tls;
462
+ #define UCDOS_MOD_ENTRY_TLS { MP_ROM_QSTR(MP_QSTR_tls), MP_ROM_PTR(&mp_module_tls) },
463
+ // Python's stdlib name is `ssl`; map it to the same object so user
464
+ // code written against CPython continues to work.
465
+ #define UCDOS_MOD_ENTRY_SSL { MP_ROM_QSTR(MP_QSTR_ssl), MP_ROM_PTR(&mp_module_tls) },
466
+ #else
467
+ #define UCDOS_MOD_ENTRY_TLS
468
+ #define UCDOS_MOD_ENTRY_SSL
469
+ #endif
470
+
471
+ #define MICROPY_REGISTERED_MODULES \
472
+ { MP_ROM_QSTR(MP_QSTR_builtins), MP_ROM_PTR(&mp_module_builtins) }, \
473
+ { MP_ROM_QSTR(MP_QSTR_sys), MP_ROM_PTR(&mp_module_sys) }, \
474
+ { MP_ROM_QSTR(MP_QSTR___main__), MP_ROM_PTR(&mp_module___main__) }, \
475
+ UCDOS_MOD_ENTRY_GC \
476
+ UCDOS_MOD_ENTRY_MATH \
477
+ UCDOS_MOD_ENTRY_MICROPYTHON \
478
+ UCDOS_MOD_ENTRY_ARRAY \
479
+ UCDOS_MOD_ENTRY_COLLECTIONS \
480
+ UCDOS_MOD_ENTRY_ERRNO \
481
+ UCDOS_MOD_ENTRY_STRUCT \
482
+ UCDOS_MOD_ENTRY_TIME \
483
+ UCDOS_MOD_ENTRY_UCTYPES \
484
+ UCDOS_MOD_ENTRY_RANDOM \
485
+ UCDOS_MOD_ENTRY_BINASCII \
486
+ UCDOS_MOD_ENTRY_HASHLIB \
487
+ UCDOS_MOD_ENTRY_RE \
488
+ UCDOS_MOD_ENTRY_CMATH \
489
+ UCDOS_MOD_ENTRY_OS \
490
+ UCDOS_MOD_ENTRY_HEAPQ \
491
+ UCDOS_MOD_ENTRY_DEFLATE \
492
+ UCDOS_MOD_ENTRY_IO \
493
+ UCDOS_MOD_ENTRY_JSON \
494
+ UCDOS_MOD_ENTRY_PLATFORM \
495
+ UCDOS_MOD_ENTRY_BASE64 \
496
+ UCDOS_MOD_ENTRY_SHUTIL \
497
+ UCDOS_MOD_ENTRY_TEMPFILE \
498
+ UCDOS_MOD_ENTRY_URLLIB \
499
+ UCDOS_MOD_ENTRY_URLLIB_PARSE \
500
+ UCDOS_MOD_ENTRY_LWIP \
501
+ UCDOS_MOD_ENTRY_SOCKET \
502
+ UCDOS_MOD_ENTRY_UC386_NET \
503
+ UCDOS_MOD_ENTRY_SELECT \
504
+ UCDOS_MOD_ENTRY_TLS \
505
+ UCDOS_MOD_ENTRY_SSL
506
+
507
+ // Module attribute-access delegation table — modules whose attr
508
+ // loads/stores need to dispatch through a port-supplied function.
509
+ // Picks up `MP_REGISTER_MODULE_DELEGATION(mod, fun)` calls.
510
+ // Currently only `sys` registers one (modsys.c:412): the
511
+ // `mp_module_sys_attr` function which dispatches `sys.path` /
512
+ // `sys.ps1` / `sys.ps2` / `sys.tracebacklimit` reads and writes
513
+ // against `MP_STATE_VM(sys_mutable[])`.
514
+ #if MICROPY_PY_SYS_ATTR_DELEGATION
515
+ extern void mp_module_sys_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest);
516
+ #define MICROPY_MODULE_DELEGATIONS \
517
+ { MP_ROM_PTR(&mp_module_sys), mp_module_sys_attr },
518
+ #endif
519
+
520
+ #define MICROPY_REGISTERED_EXTENSIBLE_MODULES
521
+ EOF
522
+ [ -f build/genhdr/mpversion.h ] || cat > build/genhdr/mpversion.h <<'EOF'
523
+ // Triage stub.
524
+ #define MICROPY_GIT_TAG "uc386-triage"
525
+ #define MICROPY_GIT_HASH "0000000"
526
+ #define MICROPY_BUILD_DATE "2026-05-01"
527
+ EOF
528
+ if [ ! -f build/genhdr/root_pointers.h ]; then
529
+ # Real builds run upstream/py/makeqstrdefs.py with mode=root_pointer
530
+ # to scan all C sources for `MP_REGISTER_ROOT_POINTER(<decl>);`
531
+ # declarations and emit them as struct fields of `_mp_state_vm_t`
532
+ # (via py/mpstate.h's `#include "genhdr/root_pointers.h"`). For
533
+ # triage we approximate with grep — the macro pattern is regular,
534
+ # we just take everything between the parens and emit it as a
535
+ # struct member terminated with a semicolon.
536
+ {
537
+ echo "// Triage stub. Real build emits MP_REGISTER_ROOT_POINTER entries here."
538
+ grep -rhE "^MP_REGISTER_ROOT_POINTER\(.*\);" \
539
+ upstream/py/ upstream/shared/ upstream/extmod/ \
540
+ | sed -E 's#^MP_REGISTER_ROOT_POINTER\((.*)\);# \1;#' \
541
+ | sort -u
542
+ } > build/genhdr/root_pointers.h
543
+ fi
544
+
545
+ # Triage stub: a one-line main() so uc386 (which requires a main
546
+ # function in every translation unit it compiles) accepts library
547
+ # .c files. We append it to each src on the fly. This answers
548
+ # "would this file compile if it were linked into a real port?"
549
+ # without the multi-file plumbing a real port needs.
550
+ TRIAGE_MAIN="build/_triage_main.c"
551
+ cat > "$TRIAGE_MAIN" <<'EOF'
552
+ // Synthetic main so uc386 has an entry-point during per-file triage.
553
+ // Real ports/<port>/main.c supplies its own main + mp_init/mp_deinit.
554
+ int main(int argc, char **argv) { (void)argc; (void)argv; return 0; }
555
+ EOF
556
+
557
+ TRIAGE="build/triage.txt"
558
+ ERR_HIST="build/errors.txt"
559
+ : > "$TRIAGE"
560
+ : > "$ERR_HIST"
561
+
562
+ PASS=0
563
+ FAIL=0
564
+ TOTAL=0
565
+
566
+ # Section accounting so the per-section pass/fail is visible.
567
+ PY_PASS=0; PY_FAIL=0; PY_TOTAL=0
568
+ SH_PASS=0; SH_FAIL=0; SH_TOTAL=0
569
+
570
+ triage_one() {
571
+ src="$1"
572
+ section="$2" # used only for the section-count update
573
+ name_prefix="$3" # disambiguates basenames between sections
574
+ [ -f "$src" ] || return 0
575
+ TOTAL=$((TOTAL + 1))
576
+ name="${name_prefix}$(basename "$src" .c)"
577
+ if "$PYTHON" -m uc386.main "$TRIAGE_MAIN" "$src" \
578
+ -o "build/${name}.asm" \
579
+ -I "$INCLUDE" \
580
+ -I "upstream" \
581
+ -I "$PORT_DIR" \
582
+ -I "build" \
583
+ > "build/${name}.out" 2> "build/${name}.err"; then
584
+ PASS=$((PASS + 1))
585
+ if [ "$section" = py ]; then PY_PASS=$((PY_PASS + 1)); PY_TOTAL=$((PY_TOTAL + 1)); fi
586
+ if [ "$section" = sh ]; then SH_PASS=$((SH_PASS + 1)); SH_TOTAL=$((SH_TOTAL + 1)); fi
587
+ echo "$name: OK" >> "$TRIAGE"
588
+ rm -f "build/${name}.err" "build/${name}.out"
589
+ else
590
+ FAIL=$((FAIL + 1))
591
+ if [ "$section" = py ]; then PY_FAIL=$((PY_FAIL + 1)); PY_TOTAL=$((PY_TOTAL + 1)); fi
592
+ if [ "$section" = sh ]; then SH_FAIL=$((SH_FAIL + 1)); SH_TOTAL=$((SH_TOTAL + 1)); fi
593
+ first_line="$(head -1 "build/${name}.err" 2>/dev/null || echo unknown)"
594
+ echo "$name: FAIL $first_line" >> "$TRIAGE"
595
+ # Strip filename + line numbers from the leading error so
596
+ # the histogram clusters by error class.
597
+ echo "$first_line" \
598
+ | sed -E 's#^[^:]+:[0-9]+:[0-9]+:?##; s#^[^:]+:[0-9]+:?##' \
599
+ | sed -E 's# +# #g; s#^ +##' \
600
+ >> "$ERR_HIST"
601
+ fi
602
+ }
603
+
604
+ # py/ — the platform-independent core (132 sources today).
605
+ for src in "$SRC_DIR"/*.c; do
606
+ triage_one "$src" py ""
607
+ done
608
+
609
+ # shared/{libc,readline,runtime,timeutils,netutils}/ — extra sources
610
+ # real ports pull in alongside py/. The minimal port uses the first
611
+ # three; richer ports (esp32, rp2, etc.) also pull in timeutils +
612
+ # netutils. Keeping them in the same triage answers "how close is a
613
+ # full port to compiling cleanly" not just "how clean is py/".
614
+ for shared_src in \
615
+ upstream/shared/libc/printf.c \
616
+ upstream/shared/libc/string0.c \
617
+ upstream/shared/libc/__errno.c \
618
+ upstream/shared/libc/abort_.c \
619
+ upstream/shared/readline/readline.c \
620
+ upstream/shared/runtime/pyexec.c \
621
+ upstream/shared/runtime/stdout_helpers.c \
622
+ upstream/shared/runtime/interrupt_char.c \
623
+ upstream/shared/runtime/sys_stdio_mphal.c \
624
+ upstream/shared/timeutils/timeutils.c \
625
+ upstream/shared/netutils/netutils.c \
626
+ upstream/shared/netutils/trace.c \
627
+ upstream/shared/netutils/dhcpserver.c; do
628
+ [ -f "$shared_src" ] || continue
629
+ # name_prefix=shared_ so e.g. shared/libc/printf.c doesn't collide
630
+ # with py/ — there is no collision today, but the prefix keeps
631
+ # the name space clean and makes the section visible in triage.txt.
632
+ triage_one "$shared_src" sh "shared_"
633
+ done
634
+
635
+ echo
636
+ echo "== triage: $PASS pass / $FAIL fail / $TOTAL total =="
637
+ echo " py/ $PY_PASS / $PY_TOTAL"
638
+ echo " shared/{libc,readline,runtime,timeutils,netutils}/ $SH_PASS / $SH_TOTAL"
639
+ echo
640
+ echo "Top error classes (count × class):"
641
+ sort "$ERR_HIST" | uniq -c | sort -rn | head -15