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,358 @@
1
+ // uc386-dos MicroPython port config — ROM_LEVEL = EXTRA_FEATURES.
2
+ //
3
+ // Started life as a clone of `upstream/ports/minimal/mpconfigport.h`
4
+ // (MINIMUM ROM level + REPL + GC + external import). Walked up the
5
+ // ROM ladder slice by slice until we hit EXTRA_FEATURES wholesale,
6
+ // fixing each new failure as it surfaced (qstr-hash field width,
7
+ // X-macro qstrs for errno, IEEE-754 inf/nan via __builtin_*,
8
+ // static-pool placement for special-method dunders, ...). See
9
+ // `addons/gnu/micropython/NOTES.md` for the per-slice diary.
10
+ //
11
+ // Port-incompatible defaults explicitly turned off below.
12
+
13
+ #include <stdint.h>
14
+
15
+ // Use the setjmp/longjmp-backed NLR (non-local return) machinery.
16
+ // The default x86 NLR (`upstream/py/nlrx86.c`) is GCC inline asm,
17
+ // which uc_core / uc386 doesn't compile — `nlr_push`/`nlr_jump`
18
+ // would silently expand to no-ops, leaving uninitialized garbage in
19
+ // the nlr_buf and crashing the parser at the first exception. The
20
+ // setjmp path goes through uc386's libc `_setjmp`/`_longjmp`
21
+ // (lib/i386_dos_libc.asm), which is real i386 asm (saves 6 dwords:
22
+ // ebx/esi/edi/ebp/esp/eip). Pair this with the fix in
23
+ // `lib/include/setjmp.h` that widens jmp_buf to 24 bytes — the old
24
+ // 6-byte declaration would have caused setjmp to buffer-overflow.
25
+ #define MICROPY_NLR_SETJMP (1)
26
+
27
+ // EXTRA_FEATURES baseline. Lights up `compile()` / `eval()` /
28
+ // `exec()`, `input()`, `memoryview`, `MICROPY_PY_ALL_SPECIAL_METHODS`
29
+ // (`__add__` / `__radd__` / `__iadd__` / `__and__` / etc on class
30
+ // instances), `collections.deque`, math constants (pi/e/tau/inf/
31
+ // nan), `math.factorial`, `math.isclose`, `bytes.hex` /
32
+ // `bytes.fromhex`, `str.center` / `partition` / `splitlines`,
33
+ // `frozenset`, **f-strings**, function attribute access
34
+ // (`f.__name__`), `delattr`/`setattr`, REPL Emacs keys + auto-
35
+ // indent + Ctrl-C → KeyboardInterrupt, deque iter+subscr,
36
+ // bytearray slice-assign, plus dozens of smaller surface knobs.
37
+ #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES)
38
+
39
+ // All previously-disabled EXTRA-default flags now enabled below
40
+ // (STACK_CHECK / PY_UCTYPES / TIME_TIME_TIME_NS / BUILTINS_HELP /
41
+ // MODULE___FILE__).
42
+
43
+ // Stack-overflow guard. With a 1 MB stack (dos_emu maps STACK_BASE
44
+ // 0x01000000..0x01100000), an unbounded recursion or huge frame
45
+ // would otherwise hit unmapped memory and surface as
46
+ // UC_ERR_WRITE_UNMAPPED instead of a clean RuntimeError. main.c
47
+ // (sed-patched in build.sh) calls `mp_stack_ctrl_init()` after
48
+ // mp_init to capture the real stack top, then
49
+ // `mp_stack_set_limit(0xC0000)` to give a 256 KB safety margin
50
+ // (i.e., raise RecursionError when usage hits 768 KB, well below
51
+ // the 1 MB hard limit).
52
+ #define MICROPY_STACK_CHECK (1)
53
+
54
+ // `help()` builtin — uses the upstream default help text from
55
+ // `py/builtinhelp.c:mp_help_default_text`. With
56
+ // MICROPY_PY_BUILTINS_HELP_TEXT undefined, the default
57
+ // (`mp_help_default_text`) is used.
58
+ #define MICROPY_PY_BUILTINS_HELP (1)
59
+
60
+ // `__file__` attribute on imported modules. Auto-set by
61
+ // upstream's `do_load_from_lexer` (builtinimport.c:158) using
62
+ // the lexer's `source_name` qstr — which our
63
+ // `uc386-dos/file_uc386dos.c:mp_lexer_new_from_file` initializes
64
+ // from the import filename. No port-supplied helper needed.
65
+ #define MICROPY_MODULE___FILE__ (1)
66
+ // `uctypes` — binary struct access for memoryview/buffer-like
67
+ // objects (define a layout dict, pin it onto a buffer, read/write
68
+ // fields by name). build_port.sh adds extmod/moductypes.c to the
69
+ // source list. The module gets registered under MP_QSTR_uctypes
70
+ // via MP_REGISTER_EXTENSIBLE_MODULE.
71
+ #define MICROPY_PY_UCTYPES (1)
72
+
73
+ // `time.time()` / `time.localtime()` / `time.gmtime()` /
74
+ // `time.mktime()` — wired to the DOS RTC via INT 21h AH=0x2A
75
+ // (date) + AH=0x2C (time-of-day) in
76
+ // lib/i386_dos_libc.asm:_dos_get_datetime, then converted to
77
+ // seconds-since-epoch by upstream's shared/timeutils. The port
78
+ // shim lives in `uc386-dos/modtime_uc386dos.c` and is
79
+ // `#include`'d into extmod/modtime.c via the
80
+ // MICROPY_PY_TIME_INCLUDEFILE hook (provides
81
+ // `mp_time_time_get` + `mp_time_localtime_get`).
82
+ //
83
+ // MICROPY_TIMESTAMP_IMPL = 1 (UINT) forces `mp_timestamp_t` to
84
+ // be `mp_uint_t` (32-bit on i386). The default for
85
+ // MICROPY_EPOCH_IS_2000 is LONG_LONG, which we can't safely
86
+ // return from `time.time()` without longlong int support
87
+ // (`mp_obj_new_int_from_ll` is a stub in LONGINT_IMPL_NONE that
88
+ // always raises OverflowError). 32-bit unsigned seconds-since-
89
+ // 2000 covers through year 2136 — adequate for a DOS port.
90
+ //
91
+ // Caveat: TIME_TIME_NS gates BOTH `time.time()` AND
92
+ // `time.time_ns()` in upstream modtime.c. Calling
93
+ // `time.time_ns()` will raise `OverflowError("small int
94
+ // overflow")` because it routes through
95
+ // `mp_obj_new_int_from_ull(mp_hal_time_ns())` and the ull stub
96
+ // rejects everything. Lighting it up cleanly requires
97
+ // MICROPY_LONGINT_IMPL_LONGLONG.
98
+ #define MICROPY_TIMESTAMP_IMPL (1)
99
+ #define MICROPY_PY_TIME_TIME_TIME_NS (1)
100
+ #define MICROPY_PY_TIME_GMTIME_LOCALTIME_MKTIME (1)
101
+ #define MICROPY_PY_TIME_INCLUDEFILE "uc386-dos/modtime_uc386dos.c"
102
+
103
+ // `open()` + `import xxx` (loading `xxx.py` from disk) wired through
104
+ // uc386's libc INT 21h file syscalls. We provide port-supplied
105
+ // `mp_builtin_open_obj` / `mp_import_stat` / `mp_lexer_new_from_file`
106
+ // in `uc386-dos/file_uc386dos.c` (no full VFS — just enough for
107
+ // flat .py imports and read/write file objects). MICROPY_PY_IO=1
108
+ // pulls in modio.c (io.IOBase, BytesIO, StringIO) and exposes
109
+ // `open` in the builtins table.
110
+ #define MICROPY_PY_IO (1)
111
+
112
+ // `sys`-module sub-features.
113
+ // - sys.exit: raises `SystemExit`; pyexec_friendly_repl catches
114
+ // it and bails out of the REPL loop, then main() returns to
115
+ // the libc cleanup → INT 21h AH=4Ch with the exit code.
116
+ // - sys.modules: dict of imported modules. `mp_init` already
117
+ // initializes `mp_loaded_modules_dict` via
118
+ // `mp_obj_dict_init(...)`; just exposing it as `sys.modules`.
119
+ // - sys.argv: empty list. `mp_init`'s
120
+ // `MICROPY_PY_SYS_PATH_ARGV_DEFAULTS` block (runtime.c:142)
121
+ // auto-initializes both sys.path and sys.argv when the flags
122
+ // are on, so no port shim needed.
123
+ // - sys.path: list of directories to search on import. With
124
+ // MICROPY_PY_SYS_PATH=1, builtinimport.c's `stat_top_level`
125
+ // iterates the list and our `mp_import_stat` (file_uc386dos.c)
126
+ // stat-checks each candidate. Initialized in mp_init with
127
+ // a single empty-string entry meaning "current directory".
128
+ // Enabling MICROPY_PY_SYS_PATH automatically enables
129
+ // MICROPY_PY_SYS_ATTR_DELEGATION (per modsys.c:267), which
130
+ // in turn requires MICROPY_MODULE_ATTR_DELEGATION (already
131
+ // on at our EXTRA_FEATURES ROM level per mpconfig.h:1127).
132
+ #define MICROPY_PY_SYS_MODULES (1)
133
+ #define MICROPY_PY_SYS_EXIT (1)
134
+ #define MICROPY_PY_SYS_PATH (1)
135
+ #define MICROPY_PY_SYS_ARGV (1)
136
+
137
+ // Polish: introspection knobs default to off (or EVERYTHING-only) at
138
+ // EXTRA_FEATURES — opt in explicitly. All live in modules we already
139
+ // compile (modsys.c / objrange.c) so no new sources needed.
140
+ //
141
+ // `sys.exc_info()` returns the (type, value, tb) tuple for the
142
+ // currently-handled exception inside an `except:` block. Useful for
143
+ // generic logging / propagation.
144
+ #define MICROPY_PY_SYS_EXC_INFO (1)
145
+ // `sys.tracebacklimit` lets user code cap traceback depth. Useful
146
+ // in DOS where stdout is precious screen real-estate.
147
+ #define MICROPY_PY_SYS_TRACEBACKLIMIT (1)
148
+ // `range(3) == range(3)` returns True (without this, ranges only
149
+ // compare equal by identity). Tiny, but a frequent CPython surprise.
150
+ #define MICROPY_PY_BUILTINS_RANGE_BINOP (1)
151
+
152
+ // errno requires:
153
+ // - build.sh to pre-emit the EPERM/ENOENT/... qstrs (the module's
154
+ // globals table uses `MP_QSTR_##e` token paste over its X-macro
155
+ // list, which our grep-based gen_qstrdefs.py can't see otherwise).
156
+ // - MICROPY_USE_INTERNAL_ERRNO=1 so MP_EPERM resolves to the
157
+ // upstream's hardcoded MP_##e values rather than to the system's
158
+ // EPERM macros from <errno.h>. uc386's libc errno.h ships only
159
+ // a Linux subset (no EOPNOTSUPP / EADDRINUSE / ECONN* / EHOST* /
160
+ // EALREADY / EINPROGRESS), so the system path leaves several
161
+ // MP_##e references as bare Identifiers that fail const-eval.
162
+ #define MICROPY_USE_INTERNAL_ERRNO (1)
163
+
164
+ // Float — uc386 lowers `double` through the x87 FPU and uc386's
165
+ // libc (lib/i386_dos_libc.asm) ships sin/cos/tan/asin/acos/atan/
166
+ // atan2/exp/log/log10/log2/expm1/pow/sqrt/floor/ceil/trunc/fmod/
167
+ // modf/fabs/copysign/signbit/isnan/isinf/isfinite/nan/nearbyint/
168
+ // ldexp/frexp/sinh/cosh/tanh/asinh/acosh/atanh/erf/erfc plus NaN
169
+ // stubs for tgamma/lgamma in raw 387 asm. DOUBLE rather than
170
+ // FLOAT so the libc's `sin`/`cos`/... unsuffixed names match what
171
+ // micropython calls (FLOAT_IMPL_FLOAT calls `sinf` / `cosf` /
172
+ // ... which we don't have).
173
+ #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_DOUBLE)
174
+ #define MICROPY_PY_MATH_SPECIAL_FUNCTIONS (1)
175
+
176
+ // Math edge-case fixes — defaults are 0 across all ROM levels
177
+ // because they cost a few extra branches. We're not size-constrained
178
+ // (the EXACT float formatter alone costs more), and matching CPython
179
+ // behavior at infinity / NaN boundaries makes test code that relies
180
+ // on `math.atan2(0, 0) == 0`-style assumptions actually portable.
181
+ // See py/modmath.c for the wrappers each flag enables.
182
+ #define MICROPY_PY_MATH_ATAN2_FIX_INFNAN (1)
183
+ #define MICROPY_PY_MATH_FMOD_FIX_INFNAN (1)
184
+ #define MICROPY_PY_MATH_MODF_FIX_NEGZERO (1)
185
+ #define MICROPY_PY_MATH_POW_FIX_NAN (1)
186
+ #define MICROPY_PY_MATH_GAMMA_FIX_NEGINF (1)
187
+
188
+ // Float formatter — opt into the EXACT formatter (vs the default
189
+ // APPROX picked when long double has the same width as double).
190
+ // EXACT's headline value is a parse-and-retry loop in
191
+ // `py/formatfloat.c:472`: format with N digits, parse the result
192
+ // back, compare against the original; if not equal, retry with
193
+ // N+1. With APPROX the loop is gated out and the formatter walks
194
+ // to MAX_MANTISSA_DIGITS digits unconditionally — accumulating
195
+ // floating-point noise in each multiplication step and producing
196
+ // `print(4.0)` → `3.999999999999996` and `print(1e10)` →
197
+ // `09999999999.99998`. EXACT picks the SHORTEST decimal that
198
+ // round-trips, so simple values like 4.0 / 0.1 / 1e10 get the
199
+ // expected literal form back. Caveat: `mp_decimal_exp` (parsenum.c)
200
+ // has an `assert(sizeof(mp_large_float_t) > sizeof(mp_float_t))`
201
+ // guarding against picking EXACT on a platform where long double
202
+ // isn't wider than double — which IS our case (uc386 lowers long
203
+ // double through the x87 FPU but stores it as 8 bytes). build.sh
204
+ // sed-patches that assertion out: the algorithm still works,
205
+ // just at the same precision as APPROX would have used; the
206
+ // verify-retry loop is what actually delivers the correctness.
207
+ #define MICROPY_FLOAT_FORMAT_IMPL (MICROPY_FLOAT_FORMAT_IMPL_EXACT)
208
+
209
+ // Long-long int support — heap-allocated `mp_obj_int_t` for
210
+ // values that don't fit a small int. Default is
211
+ // `LONGINT_IMPL_NONE`, where `mp_obj_new_int_from_ll` /
212
+ // `mp_obj_new_int_from_ull` are stubs that always raise
213
+ // `OverflowError("small int overflow")`. That makes
214
+ // `time.time_ns()`, large `struct.unpack` results, and other
215
+ // 64-bit-int paths unusable. LONGLONG enables the real
216
+ // implementations in `upstream/py/objint_longlong.c` (already
217
+ // in our source list) and pulls heap-allocated `mp_obj_int_t`
218
+ // instances onto the GC heap. Same surface as the upstream
219
+ // stm32 / esp32 ports.
220
+ #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG)
221
+
222
+ // `time` module — `time.ticks_ms`, `time.sleep_ms`, etc. wired
223
+ // through INT 1Ah AH=0 BIOS tick counter (~18.2 Hz, ~55 ms/tick)
224
+ // in lib/i386_dos_libc.asm and uc386-dos/mphal_uc386dos.c.
225
+ // Default-off at our ROM_LEVEL because upstream's ROM-level
226
+ // numbering puts BASIC_FEATURES (20) ABOVE CORE_FEATURES (10),
227
+ // so `MICROPY_PY_TIME = AT_LEAST_BASIC_FEATURES` evaluates to 0.
228
+ #define MICROPY_PY_TIME (1)
229
+
230
+ // extmod modules: `random`, `binascii`, `hashlib` (SHA-256 only),
231
+ // `re`. Each is a single source file added to build_port.sh's
232
+ // list and registered in build.sh's moduledefs.h. random's
233
+ // seed-init defaults to the BIOS tick counter so `random.seed()`
234
+ // with no arg picks up some entropy. hashlib pulls in the SHA-256
235
+ // implementation from `lib/crypto-algorithms/sha256.c` via inline
236
+ // `#include` (no separate source-list entry needed). re uses
237
+ // `lib/re1.5/*.c` similarly. binascii's CRC32 path is gated on
238
+ // `MICROPY_PY_DEFLATE` (off) so the unzip dep doesn't pull in.
239
+ #define MICROPY_PY_RANDOM (1)
240
+ #define MICROPY_PY_RANDOM_EXTRA_FUNCS (1)
241
+ #define MICROPY_PY_RANDOM_SEED_INIT_FUNC (bios_ticks())
242
+ extern unsigned long bios_ticks(void);
243
+ #define MICROPY_PY_BINASCII (1)
244
+ // CRC32 + DEFLATE — enabled now that build_port.sh includes the
245
+ // uzlib sources (crc32.c, tinflate.c, lz77.c, defl_static.c,
246
+ // adler32.c, header.c). DEFLATE_COMPRESS adds the encoder side
247
+ // (defl_static.c + lz77.c) for `deflate.DeflateIO(buf, mode, ...)`
248
+ // in write mode.
249
+ #define MICROPY_PY_BINASCII_CRC32 (1)
250
+ #define MICROPY_PY_DEFLATE (1)
251
+ #define MICROPY_PY_DEFLATE_COMPRESS (1)
252
+ #define MICROPY_PY_HASHLIB (1)
253
+ #define MICROPY_PY_HASHLIB_SHA256 (1)
254
+
255
+ // MD5 + SHA1 are gated on MICROPY_SSL_AXTLS in upstream modhashlib.c.
256
+ // We compile the real axtls library (upstream/lib/axtls/), so the
257
+ // AXTLS branch of modhashlib.c picks up its native MD5/SHA1 from
258
+ // crypto/md5.c and crypto/sha1.c. (Earlier the port shimmed the
259
+ // AXTLS API onto B-Con's public-domain hashes via
260
+ // uc386-dos/lib/axtls/crypto/crypto.h to get hashlib without TLS;
261
+ // that shim is now superseded by the real axtls build, but we keep
262
+ // the search path priority so it would still work if disabled.)
263
+ #define MICROPY_PY_HASHLIB_SHA1 (1)
264
+ #define MICROPY_PY_HASHLIB_MD5 (1)
265
+ #define MICROPY_SSL_AXTLS (1)
266
+
267
+ // Real TLS via axtls. modtls_axtls.c is the MicroPython glue,
268
+ // upstream/lib/axtls/{ssl,crypto}/ is the library itself. axtls's
269
+ // I/O is wired to mp_stream_posix_read/write (extmod/axtls-include/
270
+ // axtls_os_port.h) so reads/writes go through the lwIP socket's
271
+ // stream protocol — no separate BIO wrapper needed. Cert verification
272
+ // is OFF (CONFIG_SSL_CERT_VERIFICATION undef in the same header), so
273
+ // `import ssl` accepts whatever the server presents; this matches
274
+ // upstream MP's posture on resource-constrained ports. Use a CA
275
+ // chain via load_verify_locations once the bigger ROM budget
276
+ // allows — for now, server-auth is the user's responsibility.
277
+ #define MICROPY_PY_SSL (1)
278
+ // MICROPY_STREAMS_POSIX_API exposes mp_stream_posix_{read,write,lseek}
279
+ // in py/stream.c — axtls's SOCKET_READ/SOCKET_WRITE macros (in
280
+ // extmod/axtls-include/axtls_os_port.h) call those to bridge axtls
281
+ // I/O to the underlying lwIP socket. Without this gate the symbols
282
+ // are #ifdef'd out and the link fails.
283
+ #define MICROPY_STREAMS_POSIX_API (1)
284
+
285
+ // lwIP — IPv4-only TCP/UDP/DNS stack. Phase 1 is loopback-only
286
+ // (no packet driver yet), so socket(AF_INET, SOCK_STREAM) can
287
+ // bind/listen/connect against 127.0.0.1 within a single REPL.
288
+ // MICROPY_PY_SOCKET turns on the BSD-style surface modlwip.c
289
+ // exposes; MICROPY_PY_LWIP itself just exposes the raw `lwip`
290
+ // module which we don't bother with separately. The hostname
291
+ // default is required by extmod/modnetwork.c — we don't actually
292
+ // build modnetwork.c (no MICROPY_PY_NETWORK), but modlwip.c
293
+ // references the hostname for DNS, so set it here.
294
+ #define MICROPY_PY_LWIP (1)
295
+ #define MICROPY_PY_SOCKET (1)
296
+ #define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "uc386-dos"
297
+ #define MICROPY_PY_NETWORK_HOSTNAME_MAX_LEN 16
298
+
299
+ // modlwip.c's poll_sockets() drives this hook between events. With
300
+ // no real OS scheduler it's how blocking calls (DNS resolution,
301
+ // non-blocking-with-timeout connect, the udp_recv wait loop) drive
302
+ // netif RX / sys_check_timeouts forward. Empty default would deadlock
303
+ // any blocking lwIP call — `getaddrinfo` would never see its DNS
304
+ // response because the netif input loop never runs.
305
+ extern void uc386dos_loopback_poll(void *arg);
306
+ extern void sys_check_timeouts(void);
307
+ // Plain-statements form (no `do{}while(0)`): modlwip.c's call site
308
+ // is `MICROPY_PY_LWIP_POLL_HOOK\n next_stmt;` with no trailing
309
+ // semicolon after the macro, so a `do{}while(0)` body would fall
310
+ // directly into the next statement and parse-error.
311
+ #define MICROPY_PY_LWIP_POLL_HOOK \
312
+ uc386dos_loopback_poll(0); \
313
+ sys_check_timeouts();
314
+
315
+ #define MICROPY_PY_RE (1)
316
+ #define MICROPY_PY_RE_SUB (1)
317
+ #define MICROPY_PY_RE_MATCH_GROUPS (1)
318
+ #define MICROPY_PY_RE_MATCH_SPAN_START_END (1)
319
+ #define MICROPY_PY_HEAPQ (1)
320
+
321
+ #define MICROPY_ENABLE_COMPILER (1)
322
+ #define MICROPY_ENABLE_GC (1)
323
+ #define MICROPY_HELPER_REPL (1)
324
+
325
+ // We don't run mpy-tool.py, so the frozen-module symbols
326
+ // (mp_frozen_mpy_content, mp_frozen_names,
327
+ // mp_qstr_frozen_const_pool) don't exist; the minimal port's =1
328
+ // setting requires them as externs.
329
+ #define MICROPY_MODULE_FROZEN_MPY (0)
330
+ #define MICROPY_ENABLE_EXTERNAL_IMPORT (1)
331
+
332
+ #define MICROPY_ALLOC_PATH_MAX (256)
333
+ #define MICROPY_ALLOC_PARSE_CHUNK_INIT (16)
334
+
335
+ typedef long mp_off_t;
336
+
337
+ #define MICROPY_HW_BOARD_NAME "uc386-dos"
338
+ #define MICROPY_HW_MCU_NAME "i386"
339
+
340
+ // `platform` module identity. modplatform.h's MICROPY_PLATFORM_*
341
+ // detections fall through to the generic "MicroPython" / "" case
342
+ // because our build doesn't define __linux / __GLIBC__ / etc.
343
+ // MICROPY_PLATFORM_VERSION IS `#ifndef`-guarded though, so we can
344
+ // stamp the runtime identity through that one.
345
+ #define MICROPY_PLATFORM_VERSION "uc386-dos i386 (PMODE/W)"
346
+
347
+ // Use the same STDOUT path the minimal port uses on linux/darwin —
348
+ // uc386's libc turns read(STDIN)/write(STDOUT) into INT 21h DOS calls.
349
+ #define MICROPY_MIN_USE_STDOUT (1)
350
+
351
+ // 256 KB GC heap. Bumped from 64 KB after the first runnable port:
352
+ // arbitrary input echoes back but the parser/compile path hits an
353
+ // UC_ERR_READ_UNMAPPED — heap exhaustion during parse-tree
354
+ // allocation is one likely cause. dos_emu maps a much wider data
355
+ // region than 64 KB so the additional fixed-region heap is fine.
356
+ #define MICROPY_HEAP_SIZE (262144)
357
+
358
+ #define MP_STATE_PORT MP_STATE_VM
@@ -0,0 +1,103 @@
1
+ // uc386-dos MicroPython HAL — out-of-line implementations.
2
+ //
3
+ // The minimal port's mphalport.h normally inlines mp_hal_ticks_ms()
4
+ // to a constant 0 because the minimal port has no realtime source.
5
+ // We override that with a real BIOS-tick-backed version so the
6
+ // `time` module can return non-trivial ticks_ms / sleep behavior.
7
+ //
8
+ // All timing is derived from INT 1Ah AH=00h (BIOS tick counter,
9
+ // ~18.2 Hz, ~55 ms per tick). That's the only realtime source DOS
10
+ // programs can portably read without programming the PIT directly,
11
+ // and it matches what most DOS C runtimes use under the hood.
12
+
13
+ #include "py/mpconfig.h"
14
+ #include "py/mphal.h"
15
+
16
+ extern unsigned long bios_ticks(void);
17
+
18
+ // Each BIOS tick is 1000 / 18.20649 ≈ 54.9254 ms. Use 55 — the ~0.13%
19
+ // drift is well below what user code that reaches for ticks_ms can
20
+ // distinguish, and matches the rounding most DJGPP/Watcom DOS time
21
+ // helpers do.
22
+ #define BIOS_TICK_MS 55U
23
+
24
+ mp_uint_t mp_hal_ticks_ms(void) {
25
+ return bios_ticks() * BIOS_TICK_MS;
26
+ }
27
+
28
+ mp_uint_t mp_hal_ticks_us(void) {
29
+ // Single-tick resolution. Multiply by 1000 (giving us 55000 µs
30
+ // step) so callers comparing two ticks_us readings see a
31
+ // monotonically-non-decreasing sequence with predictable steps.
32
+ return bios_ticks() * (BIOS_TICK_MS * 1000U);
33
+ }
34
+
35
+ mp_uint_t mp_hal_ticks_cpu(void) {
36
+ // No RDTSC or PIT-direct reads here; reuse the µs scale so the
37
+ // value is at least monotonically non-decreasing.
38
+ return mp_hal_ticks_us();
39
+ }
40
+
41
+ void mp_hal_delay_ms(mp_uint_t ms) {
42
+ // Convert ms → ticks (round up), then busy-wait until the BIOS
43
+ // counter has advanced that many ticks. Polls in a tight loop;
44
+ // there's no HLT/yield equivalent we can portably issue under
45
+ // PMODE/W since the extender owns interrupt dispatch.
46
+ if (ms == 0) {
47
+ return;
48
+ }
49
+ unsigned long ticks_to_wait = (ms + BIOS_TICK_MS - 1) / BIOS_TICK_MS;
50
+ if (ticks_to_wait == 0) {
51
+ ticks_to_wait = 1;
52
+ }
53
+ unsigned long start = bios_ticks();
54
+ while ((bios_ticks() - start) < ticks_to_wait) {
55
+ // poll
56
+ }
57
+ }
58
+
59
+ void mp_hal_delay_us(mp_uint_t us) {
60
+ // Sub-tick precision isn't available, so we round up to the
61
+ // nearest ms and reuse the ms path. Anything < 55000 µs ends up
62
+ // waiting one BIOS tick, anything >= 55000 µs waits the
63
+ // appropriate number.
64
+ if (us == 0) {
65
+ return;
66
+ }
67
+ unsigned long ms = (us + 999U) / 1000U;
68
+ mp_hal_delay_ms(ms);
69
+ }
70
+
71
+ // `sys.stdio.poll` and `input()`-readiness checks call this. We
72
+ // don't have a non-blocking stdin path under PMODE/W (INT 21h
73
+ // AH=0Bh exists but isn't reliable inside the dos_emu harness),
74
+ // so return 0 (no events ready). REPL still gets stdin via the
75
+ // normal `mp_hal_stdin_rx_chr` path in uart_core.c.
76
+ uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
77
+ (void)poll_flags;
78
+ return 0;
79
+ }
80
+
81
+ // `time.time_ns()` — nanoseconds since the configured epoch.
82
+ // We don't have sub-second precision from the DOS RTC (INT 21h
83
+ // AH=0x2C reports hundredths but we drop them for simplicity),
84
+ // so just multiply seconds-since-epoch by 1e9. Returned as
85
+ // uint64 so it can carry the full epoch timestamp without
86
+ // overflow on 32-bit builds.
87
+ #include "shared/timeutils/timeutils.h"
88
+
89
+ extern void dos_get_datetime(unsigned char out[7]);
90
+
91
+ uint64_t mp_hal_time_ns(void) {
92
+ unsigned char raw[7];
93
+ dos_get_datetime(raw);
94
+ unsigned int year = (unsigned int)(raw[0] | (raw[1] << 8));
95
+ unsigned int month = raw[2];
96
+ unsigned int day = raw[3];
97
+ unsigned int hour = raw[4];
98
+ unsigned int minute = raw[5];
99
+ unsigned int second = raw[6];
100
+ mp_timestamp_t secs = timeutils_seconds_since_epoch(
101
+ year, month, day, hour, minute, second);
102
+ return (uint64_t)secs * 1000000000ULL;
103
+ }
@@ -0,0 +1,11 @@
1
+ // uc386-dos MicroPython HAL header.
2
+ //
3
+ // `mp_hal_stdin_rx_chr` / `mp_hal_stdout_tx_strn` are out-of-line in
4
+ // `upstream/ports/minimal/uart_core.c`. The timing-related HAL
5
+ // functions (mp_hal_ticks_ms, mp_hal_delay_ms, etc.) are out-of-line
6
+ // in `uc386-dos/mphal_uc386dos.c`, so this header just defers to the
7
+ // `extern` declarations py/mphal.h ships when no override is present.
8
+
9
+ static inline void mp_hal_set_interrupt_char(char c) {
10
+ (void)c;
11
+ }