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,228 @@
1
+ // uc386-dos MicroPython file I/O — port-supplied `open()` /
2
+ // `mp_import_stat()` / `mp_lexer_new_from_file()` backed by uc386's
3
+ // libc INT 21h file syscalls. Mirrors the shape of
4
+ // `extmod/vfs_posix_file.c` but without the VFS plumbing — we
5
+ // implement the file-object type directly and define
6
+ // `mp_builtin_open_obj` as the user-visible entry point.
7
+ //
8
+ // `open()` modes:
9
+ // "r" read-only (default)
10
+ // "w" write, truncate, create
11
+ // "a" write, append, create
12
+ // "+" read-write
13
+ // "b" binary (FileIO; default if no "t")
14
+ // "t" text (TextIOWrapper, but we do bytes ↔ str)
15
+
16
+ #include <fcntl.h>
17
+ #include <unistd.h>
18
+ #include <sys/stat.h>
19
+ #include <string.h>
20
+ #include <errno.h>
21
+
22
+ #include "py/builtin.h"
23
+ #include "py/lexer.h"
24
+ #include "py/mperrno.h"
25
+ #include "py/mphal.h"
26
+ #include "py/runtime.h"
27
+ #include "py/stream.h"
28
+ #include "py/objstr.h"
29
+
30
+ typedef struct _mp_obj_uc386dos_file_t {
31
+ mp_obj_base_t base;
32
+ int fd; // -1 = closed
33
+ } mp_obj_uc386dos_file_t;
34
+
35
+ extern const mp_obj_type_t mp_type_uc386dos_textio;
36
+ extern const mp_obj_type_t mp_type_uc386dos_fileio;
37
+
38
+ // `mp_lexer_new_from_file` — read entire file into a buffer and feed
39
+ // to the str-len lexer. Used by `import xxx` to load `xxx.py`.
40
+ mp_lexer_t *mp_lexer_new_from_file(qstr filename) {
41
+ const char *fname = qstr_str(filename);
42
+ int fd = open(fname, O_RDONLY);
43
+ if (fd < 0) {
44
+ mp_raise_OSError(MP_ENOENT);
45
+ }
46
+ // Stat to get the file size. Avoids a grow-the-buffer loop.
47
+ struct stat st;
48
+ if (fstat(fd, &st) < 0) {
49
+ close(fd);
50
+ mp_raise_OSError(MP_EIO);
51
+ }
52
+ size_t size = (size_t)st.st_size;
53
+ char *buf = m_new(char, size + 1);
54
+ size_t got = 0;
55
+ while (got < size) {
56
+ int n = read(fd, buf + got, size - got);
57
+ if (n <= 0) {
58
+ break;
59
+ }
60
+ got += (size_t)n;
61
+ }
62
+ close(fd);
63
+ buf[got] = '\0';
64
+ return mp_lexer_new_from_str_len(filename, buf, got, 0);
65
+ }
66
+
67
+ // `mp_import_stat` — does `path` resolve to a file, dir, or
68
+ // nothing? `import xxx` walks `sys.path` calling this for each
69
+ // candidate path.
70
+ mp_import_stat_t mp_import_stat(const char *path) {
71
+ struct stat st;
72
+ if (stat(path, &st) != 0) {
73
+ return MP_IMPORT_STAT_NO_EXIST;
74
+ }
75
+ if (S_ISDIR(st.st_mode)) {
76
+ return MP_IMPORT_STAT_DIR;
77
+ }
78
+ return MP_IMPORT_STAT_FILE;
79
+ }
80
+
81
+ // File-object methods.
82
+ static void uc386dos_file_print(const mp_print_t *print, mp_obj_t self_in,
83
+ mp_print_kind_t kind) {
84
+ (void)kind;
85
+ mp_obj_uc386dos_file_t *self = MP_OBJ_TO_PTR(self_in);
86
+ mp_printf(print, "<io.%s %d>", mp_obj_get_type_str(self_in), self->fd);
87
+ }
88
+
89
+ static mp_uint_t uc386dos_file_read(mp_obj_t o_in, void *buf, mp_uint_t size,
90
+ int *errcode) {
91
+ mp_obj_uc386dos_file_t *o = MP_OBJ_TO_PTR(o_in);
92
+ if (o->fd < 0) {
93
+ *errcode = MP_EBADF;
94
+ return MP_STREAM_ERROR;
95
+ }
96
+ int r = read(o->fd, buf, size);
97
+ if (r < 0) {
98
+ *errcode = MP_EIO;
99
+ return MP_STREAM_ERROR;
100
+ }
101
+ return (mp_uint_t)r;
102
+ }
103
+
104
+ static mp_uint_t uc386dos_file_write(mp_obj_t o_in, const void *buf,
105
+ mp_uint_t size, int *errcode) {
106
+ mp_obj_uc386dos_file_t *o = MP_OBJ_TO_PTR(o_in);
107
+ if (o->fd < 0) {
108
+ *errcode = MP_EBADF;
109
+ return MP_STREAM_ERROR;
110
+ }
111
+ int r = write(o->fd, buf, size);
112
+ if (r < 0) {
113
+ *errcode = MP_EIO;
114
+ return MP_STREAM_ERROR;
115
+ }
116
+ return (mp_uint_t)r;
117
+ }
118
+
119
+ static mp_uint_t uc386dos_file_ioctl(mp_obj_t o_in, mp_uint_t request,
120
+ uintptr_t arg, int *errcode) {
121
+ mp_obj_uc386dos_file_t *o = MP_OBJ_TO_PTR(o_in);
122
+ if (request == MP_STREAM_SEEK) {
123
+ struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)(uintptr_t)arg;
124
+ if (o->fd < 0) {
125
+ *errcode = MP_EBADF;
126
+ return MP_STREAM_ERROR;
127
+ }
128
+ int whence = (s->whence == 0) ? SEEK_SET
129
+ : (s->whence == 1) ? SEEK_CUR
130
+ : SEEK_END;
131
+ long pos = lseek(o->fd, (long)s->offset, whence);
132
+ if (pos < 0) {
133
+ *errcode = MP_EIO;
134
+ return MP_STREAM_ERROR;
135
+ }
136
+ s->offset = (mp_off_t)pos;
137
+ return 0;
138
+ }
139
+ if (request == MP_STREAM_FLUSH) {
140
+ // No-op: DOS write() is unbuffered at the libc layer.
141
+ return 0;
142
+ }
143
+ if (request == MP_STREAM_CLOSE) {
144
+ if (o->fd >= 0) {
145
+ close(o->fd);
146
+ o->fd = -1;
147
+ }
148
+ return 0;
149
+ }
150
+ *errcode = MP_EINVAL;
151
+ return MP_STREAM_ERROR;
152
+ }
153
+
154
+ static const mp_rom_map_elem_t uc386dos_file_locals_dict_table[] = {
155
+ { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) },
156
+ { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) },
157
+ { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) },
158
+ { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
159
+ { MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mp_stream_close_obj) },
160
+ { MP_ROM_QSTR(MP_QSTR_seek), MP_ROM_PTR(&mp_stream_seek_obj) },
161
+ { MP_ROM_QSTR(MP_QSTR_tell), MP_ROM_PTR(&mp_stream_tell_obj) },
162
+ { MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&mp_stream_flush_obj) },
163
+ { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) },
164
+ { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&mp_stream___exit___obj) },
165
+ };
166
+ static MP_DEFINE_CONST_DICT(uc386dos_file_locals_dict, uc386dos_file_locals_dict_table);
167
+
168
+ static const mp_stream_p_t uc386dos_fileio_stream_p = {
169
+ .read = uc386dos_file_read,
170
+ .write = uc386dos_file_write,
171
+ .ioctl = uc386dos_file_ioctl,
172
+ };
173
+
174
+ MP_DEFINE_CONST_OBJ_TYPE(
175
+ mp_type_uc386dos_fileio, MP_QSTR_FileIO,
176
+ MP_TYPE_FLAG_NONE,
177
+ print, uc386dos_file_print,
178
+ protocol, &uc386dos_fileio_stream_p,
179
+ locals_dict, &uc386dos_file_locals_dict
180
+ );
181
+
182
+ static const mp_stream_p_t uc386dos_textio_stream_p = {
183
+ .read = uc386dos_file_read,
184
+ .write = uc386dos_file_write,
185
+ .ioctl = uc386dos_file_ioctl,
186
+ .is_text = true,
187
+ };
188
+
189
+ MP_DEFINE_CONST_OBJ_TYPE(
190
+ mp_type_uc386dos_textio, MP_QSTR_TextIOWrapper,
191
+ MP_TYPE_FLAG_NONE,
192
+ print, uc386dos_file_print,
193
+ protocol, &uc386dos_textio_stream_p,
194
+ locals_dict, &uc386dos_file_locals_dict
195
+ );
196
+
197
+ // `open(filename, mode="r")` — port-supplied entry point bound to
198
+ // the `MP_QSTR_open` builtin via `mp_builtin_open_obj` below.
199
+ static mp_obj_t uc386dos_builtin_open(size_t n_args, const mp_obj_t *args,
200
+ mp_map_t *kwargs) {
201
+ (void)kwargs;
202
+ const char *fname = mp_obj_str_get_str(args[0]);
203
+ const char *mode_s = "r";
204
+ if (n_args >= 2) {
205
+ mode_s = mp_obj_str_get_str(args[1]);
206
+ }
207
+ int mode_rw = O_RDONLY;
208
+ int mode_x = 0;
209
+ const mp_obj_type_t *type = &mp_type_uc386dos_textio;
210
+ while (*mode_s) {
211
+ switch (*mode_s++) {
212
+ case 'r': mode_rw = O_RDONLY; break;
213
+ case 'w': mode_rw = O_WRONLY; mode_x = O_CREAT | O_TRUNC; break;
214
+ case 'a': mode_rw = O_WRONLY; mode_x = O_CREAT | O_APPEND; break;
215
+ case '+': mode_rw = O_RDWR; break;
216
+ case 'b': type = &mp_type_uc386dos_fileio; break;
217
+ case 't': type = &mp_type_uc386dos_textio; break;
218
+ }
219
+ }
220
+ int fd = open(fname, mode_rw | mode_x, 0644);
221
+ if (fd < 0) {
222
+ mp_raise_OSError(MP_ENOENT);
223
+ }
224
+ mp_obj_uc386dos_file_t *f = mp_obj_malloc(mp_obj_uc386dos_file_t, type);
225
+ f->fd = fd;
226
+ return MP_OBJ_FROM_PTR(f);
227
+ }
228
+ MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, uc386dos_builtin_open);
@@ -0,0 +1,45 @@
1
+ // uc386-dos shim header that mimics the subset of axtls's crypto.h
2
+ // used by upstream/extmod/modhashlib.c (when MICROPY_SSL_AXTLS=1).
3
+ //
4
+ // We don't actually link axtls — instead we route the AXTLS-shaped
5
+ // API to Brad Conte's public-domain reference implementations
6
+ // (`upstream/lib/crypto-algorithms/{md5,sha1}.[ch]`, fetched
7
+ // alongside the existing sha256 sources). The B-Con API is nearly
8
+ // identical: same MD5_CTX / SHA1_CTX typedef names, same Update
9
+ // argument order. Only `*_Final` differs — axtls takes
10
+ // `(digest, ctx)` while B-Con takes `(ctx, digest)` — so we wrap.
11
+ //
12
+ // Setting MICROPY_SSL_AXTLS=1 in mpconfigport.h tells modhashlib.c
13
+ // to take the AXTLS branch of its #if cascade. We don't enable the
14
+ // rest of axtls (TLS, RSA, AES, ...); the single include below is
15
+ // sufficient for hashlib.md5 and hashlib.sha1.
16
+ #ifndef UCDOS_AXTLS_CRYPTO_SHIM_H
17
+ #define UCDOS_AXTLS_CRYPTO_SHIM_H
18
+
19
+ #include "lib/crypto-algorithms/md5.h"
20
+ #include "lib/crypto-algorithms/sha1.h"
21
+
22
+ #define MD5_SIZE 16
23
+ #define SHA1_SIZE 20
24
+
25
+ static inline void MD5_Init(MD5_CTX *ctx) {
26
+ md5_init(ctx);
27
+ }
28
+ static inline void MD5_Update(MD5_CTX *ctx, const unsigned char *msg, int len) {
29
+ md5_update(ctx, msg, (size_t)len);
30
+ }
31
+ static inline void MD5_Final(unsigned char *digest, MD5_CTX *ctx) {
32
+ md5_final(ctx, digest);
33
+ }
34
+
35
+ static inline void SHA1_Init(SHA1_CTX *ctx) {
36
+ sha1_init(ctx);
37
+ }
38
+ static inline void SHA1_Update(SHA1_CTX *ctx, const unsigned char *msg, int len) {
39
+ sha1_update(ctx, msg, (size_t)len);
40
+ }
41
+ static inline void SHA1_Final(unsigned char *digest, SHA1_CTX *ctx) {
42
+ sha1_final(ctx, digest);
43
+ }
44
+
45
+ #endif // UCDOS_AXTLS_CRYPTO_SHIM_H
@@ -0,0 +1,46 @@
1
+ // uc386-dos lwIP `arch/cc.h` shim. Included via `-I` rather than
2
+ // the conventional `arch/cc.h` path because uc386's preprocessor
3
+ // doesn't have a per-port arch include directory convention.
4
+ // build_port.sh adds `uc386-dos/` to the include path AND aliases
5
+ // the conventional name via a stub at uc386-dos/arch/cc.h that
6
+ // just `#include`s this file.
7
+
8
+ #ifndef LWIP_ARCH_CC_H
9
+ #define LWIP_ARCH_CC_H
10
+
11
+ #include <stdint.h>
12
+ #include <stddef.h>
13
+ #include <stdlib.h>
14
+
15
+ // Endianness — i386 is little-endian.
16
+ #define BYTE_ORDER LITTLE_ENDIAN
17
+ #ifndef LITTLE_ENDIAN
18
+ #define LITTLE_ENDIAN 1234
19
+ #define BIG_ENDIAN 4321
20
+ #endif
21
+
22
+ // Packed structs — uc386 honors `__attribute__((packed))` (member
23
+ // byte-alignment, struct alignment 1). lwIP's init.c self-tests the
24
+ // layout via `sizeof(struct { u8; u32; } __packed) == 5` at boot;
25
+ // without real packing this fails because u32 lands at offset 4.
26
+ // The other PACK_STRUCT_* slots (BEGIN/END/FIELD) remain empty —
27
+ // upstream's lwip/arch.h defines them that way for GCC/clang.
28
+ #define PACK_STRUCT_FIELD(x) x
29
+ #define PACK_STRUCT_STRUCT __attribute__((packed))
30
+ #define PACK_STRUCT_BEGIN
31
+ #define PACK_STRUCT_END
32
+
33
+ // Diagnostics — route lwIP's debug printf through the regular
34
+ // printf chain. LWIP_DEBUG=0 in lwipopts.h makes these no-ops in
35
+ // the common case.
36
+ #include <stdio.h>
37
+ #define LWIP_PLATFORM_DIAG(x) do { printf x; } while (0)
38
+
39
+ // sys_prot_t — a critical-section level. We're NO_SYS=1 with no
40
+ // real interrupts to mask, so use a stub type and let lwipopts.h's
41
+ // SYS_ARCH_* macros expand to nothing.
42
+ typedef uint32_t sys_prot_t;
43
+
44
+ // strerror — uc386's libc has it.
45
+
46
+ #endif // LWIP_ARCH_CC_H
@@ -0,0 +1,248 @@
1
+ // uc386-dos lwIP port glue. Provides:
2
+ // - sys_now() — millisecond tick from BIOS (INT 1Ah AH=0).
3
+ // - lwip_uc386dos_init() — calls lwip_init() once + adds the
4
+ // loopback netif. Called from main.c at boot.
5
+ // - mp_module_socket — re-export of lwIP's mp_module_lwip under
6
+ // `MP_QSTR_socket` so `import socket` lights up the BSD-style
7
+ // surface modlwip.c provides.
8
+ //
9
+ // We don't ship modnetwork.c — that's the higher-level network
10
+ // configuration interface used by ports with WLAN/Ethernet drivers.
11
+ // For loopback-only Phase 1 testing, the raw socket surface
12
+ // (modlwip.c → MP_QSTR_socket) is sufficient.
13
+
14
+ #include <string.h>
15
+
16
+ #include "py/runtime.h"
17
+ #include "lwip/init.h"
18
+ #include "lwip/timeouts.h"
19
+ #include "lwip/netif.h"
20
+ #include "lwip/dhcp.h"
21
+ #include "lwip/etharp.h"
22
+ #include "lwip/pbuf.h"
23
+ #include "netif/ethernet.h"
24
+ #if LWIP_HAVE_LOOPIF
25
+ #include "lwip/sys.h"
26
+ #endif
27
+
28
+ // INT 0x83 packet-driver shim — see lib/i386_dos_libc.asm. dos_emu
29
+ // intercepts and routes to a NetworkSimulator (sim mode).
30
+ extern int ethdrv_init(unsigned char mac[6]);
31
+ extern int ethdrv_send(const unsigned char *buf, unsigned int len);
32
+ extern unsigned int ethdrv_recv(unsigned char *buf, unsigned int maxlen);
33
+
34
+ // Crynwr packet-driver bindings (real-DOS path) — pktdrv_uc386dos.c.
35
+ // Returns 0 on success when a "PKT DRVR" signature is found in the
36
+ // IVT 0x60–0x7F range; otherwise we fall back to the INT 0x83 sim.
37
+ extern int pktdrv_init(unsigned char mac[6]);
38
+ extern int pktdrv_send(const unsigned char *buf, unsigned int len);
39
+ extern unsigned int pktdrv_recv(unsigned char *buf, unsigned int maxlen);
40
+ extern int pktdrv_is_active(void);
41
+
42
+ // `bios_ticks()` from lib/i386_dos_libc.asm — INT 1Ah AH=0 read of
43
+ // the BIOS tick counter (~18.2 Hz). Multiply by 55 for an
44
+ // approximate millisecond clock; lwIP's timeouts only need
45
+ // monotonic ms, not absolute wall time, so the small drift is fine.
46
+ extern unsigned bios_ticks(void);
47
+
48
+ uint32_t sys_now(void) {
49
+ return bios_ticks() * 55u;
50
+ }
51
+
52
+ #if LWIP_HAVE_LOOPIF
53
+ // One-shot lwIP init. Call from `main()` after mp_init.
54
+ // Loopback netif is auto-added by lwip_init() when LWIP_HAVE_LOOPIF
55
+ // is on, so we just call lwip_init() and we're done.
56
+ void lwip_uc386dos_init(void) {
57
+ lwip_init();
58
+ }
59
+ #endif
60
+
61
+ // Drive periodic timer checks. Call from the REPL idle loop or a
62
+ // dedicated tick. modlwip.c expects sys_check_timeouts() to be
63
+ // pumped regularly so TCP retransmits / DNS timeouts fire.
64
+ void lwip_uc386dos_poll(void) {
65
+ sys_check_timeouts();
66
+ }
67
+
68
+ // Stub for `mp_mod_network_prefer_dns_use_ip_version` — modlwip.c
69
+ // reads it inside getaddrinfo() to pick AF_INET vs AF_INET6 ordering.
70
+ // modnetwork.c (which we don't compile) defines it as a real global;
71
+ // we ship the stub here defaulting to IPv4 preferred. If we ever
72
+ // turn LWIP_IPV6 on, switch this to 6 or wire modnetwork properly.
73
+ int mp_mod_network_prefer_dns_use_ip_version = 4;
74
+
75
+ // modlwip.c also references the hostname buffer that modnetwork.c
76
+ // otherwise owns. Provide a stub matching the symbol shape.
77
+ char mod_network_hostname_data[16 + 1] = "uc386-dos";
78
+
79
+ // ---- Virtual ethernet netif (uc386dos_eth) ---------------------
80
+ //
81
+ // A second netif on top of the INT 0x83 packet-driver shim. Used
82
+ // for DHCP discovery and any non-loopback IPv4 traffic the program
83
+ // wants. Stays separate from the loopback netif so 127.0.0.1 traffic
84
+ // doesn't bounce off the wire.
85
+ //
86
+ // Frame path:
87
+ // TX: lwIP → etharp_output → ethdrv_send → INT 0x83 AH=1
88
+ // → dos_emu hook
89
+ // → NetworkSimulator
90
+ // RX: ethdrv_recv (INT 0x83 AH=2) → pbuf_alloc + take
91
+ // → ethernet_input → ip_input
92
+ // Pumped from `uc386dos_loopback_poll` (which is wired in as
93
+ // modlwip's poll-list hook), so `lwip.callback()` drives both
94
+ // loopback delivery and eth RX in one shot.
95
+
96
+ static struct netif uc386dos_eth_netif;
97
+ static int uc386dos_eth_active = 0;
98
+
99
+ // Static TX scratch — one outgoing frame at a time, no concurrency
100
+ // since NO_SYS=1 + single-threaded REPL. Sized to lwIP MTU + eth hdr.
101
+ static unsigned char uc386dos_eth_tx_buf[1500 + 14];
102
+ static unsigned char uc386dos_eth_rx_buf[1500 + 14];
103
+
104
+ static err_t uc386dos_eth_linkoutput(struct netif *netif, struct pbuf *p) {
105
+ (void)netif;
106
+ if (p->tot_len > sizeof(uc386dos_eth_tx_buf)) {
107
+ return ERR_IF;
108
+ }
109
+ pbuf_copy_partial(p, uc386dos_eth_tx_buf, p->tot_len, 0);
110
+ int rc;
111
+ if (pktdrv_is_active()) {
112
+ rc = pktdrv_send(uc386dos_eth_tx_buf, p->tot_len);
113
+ } else {
114
+ rc = ethdrv_send(uc386dos_eth_tx_buf, p->tot_len);
115
+ }
116
+ return (rc != 0) ? ERR_IF : ERR_OK;
117
+ }
118
+
119
+ static err_t uc386dos_eth_netif_init_cb(struct netif *netif) {
120
+ netif->name[0] = 'e';
121
+ netif->name[1] = 'n';
122
+ netif->output = etharp_output;
123
+ netif->linkoutput = uc386dos_eth_linkoutput;
124
+ netif->mtu = 1500;
125
+ netif->hwaddr_len = 6;
126
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP
127
+ | NETIF_FLAG_ETHERNET;
128
+ // hwaddr is filled in by uc386dos_eth_start() before netif_add.
129
+ return ERR_OK;
130
+ }
131
+
132
+ // Pull queued frames from the RX path and feed them into lwIP.
133
+ // When Crynwr is the active driver, drain `pktdrv_recv` — under
134
+ // dos_emu the harness fills the buffer through AH=0x99 polling
135
+ // registration; on real DOS this requires a DPMI INT 31h fn 0x0303
136
+ // trampoline (see pktdrv_uc386dos.c) that's still TODO. With INT
137
+ // 0x83 sim active, drain `ethdrv_recv` instead. The two never
138
+ // coexist in a single boot.
139
+ static void uc386dos_eth_pump_rx(void) {
140
+ if (!uc386dos_eth_active) {
141
+ return;
142
+ }
143
+ for (;;) {
144
+ unsigned int len = pktdrv_is_active()
145
+ ? pktdrv_recv(uc386dos_eth_rx_buf,
146
+ sizeof(uc386dos_eth_rx_buf))
147
+ : ethdrv_recv(uc386dos_eth_rx_buf,
148
+ sizeof(uc386dos_eth_rx_buf));
149
+ if (len == 0) {
150
+ break;
151
+ }
152
+ struct pbuf *p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
153
+ if (p == NULL) {
154
+ // Drop the frame; lwIP will time out and retransmit.
155
+ continue;
156
+ }
157
+ pbuf_take(p, uc386dos_eth_rx_buf, (u16_t)len);
158
+ if (uc386dos_eth_netif.input(p, &uc386dos_eth_netif) != ERR_OK) {
159
+ pbuf_free(p);
160
+ }
161
+ }
162
+ }
163
+
164
+ // Bring the eth netif up. Calls INT 0x83 to fetch the host-assigned
165
+ // MAC, registers the netif, and (if dhcp_start_now) kicks off DHCP
166
+ // discovery. Idempotent — second call is a no-op.
167
+ int uc386dos_eth_start(int dhcp_start_now) {
168
+ if (uc386dos_eth_active) {
169
+ return 0;
170
+ }
171
+ unsigned char mac[6];
172
+ // Try a real Crynwr packet driver first (so the same binary
173
+ // lights up on hardware when one's loaded). Fall back to the
174
+ // INT 0x83 sim — what dos_emu provides without a fake Crynwr.
175
+ if (pktdrv_init(mac) != 0 && ethdrv_init(mac) != 0) {
176
+ return -1;
177
+ }
178
+ ip4_addr_t ip0, nm0, gw0;
179
+ ip0.addr = 0;
180
+ nm0.addr = 0;
181
+ gw0.addr = 0;
182
+ if (netif_add(&uc386dos_eth_netif, &ip0, &nm0, &gw0, NULL,
183
+ uc386dos_eth_netif_init_cb, ethernet_input) == NULL) {
184
+ return -2;
185
+ }
186
+ memcpy(uc386dos_eth_netif.hwaddr, mac, 6);
187
+ netif_set_default(&uc386dos_eth_netif);
188
+ netif_set_up(&uc386dos_eth_netif);
189
+ netif_set_link_up(&uc386dos_eth_netif);
190
+ uc386dos_eth_active = 1;
191
+ if (dhcp_start_now) {
192
+ if (dhcp_start(&uc386dos_eth_netif) != ERR_OK) {
193
+ return -3;
194
+ }
195
+ }
196
+ return 0;
197
+ }
198
+
199
+ // Static-IP override path for tests / environments where DHCP isn't
200
+ // available. Caller has already run uc386dos_eth_start(False) so the
201
+ // netif exists; this just clears any DHCP state and writes the
202
+ // addresses directly.
203
+ void uc386dos_eth_set_addr(unsigned int ip, unsigned int mask, unsigned int gw) {
204
+ if (!uc386dos_eth_active) return;
205
+ dhcp_release_and_stop(&uc386dos_eth_netif);
206
+ ip4_addr_t a, n, g;
207
+ a.addr = ip; n.addr = mask; g.addr = gw;
208
+ netif_set_addr(&uc386dos_eth_netif, &a, &n, &g);
209
+ netif_set_up(&uc386dos_eth_netif);
210
+ }
211
+
212
+ // Read-back helpers for the Python-facing module to inspect state.
213
+ unsigned int uc386dos_eth_ip(void) { return uc386dos_eth_netif.ip_addr.addr; }
214
+ unsigned int uc386dos_eth_netmask(void) { return uc386dos_eth_netif.netmask.addr; }
215
+ unsigned int uc386dos_eth_gateway(void) { return uc386dos_eth_netif.gw.addr; }
216
+ int uc386dos_eth_is_up(void) { return uc386dos_eth_active && netif_is_up(&uc386dos_eth_netif); }
217
+
218
+ // 0 = no eth init yet, 1 = INT 0x83 sim, 2 = Crynwr packet driver.
219
+ int uc386dos_eth_driver(void) {
220
+ if (!uc386dos_eth_active) return 0;
221
+ return pktdrv_is_active() ? 2 : 1;
222
+ }
223
+
224
+ // Loopback packet pump. With LWIP_NETIF_LOOPBACK=1 + NO_SYS=1, lwIP
225
+ // queues outgoing-to-127.0.0.1 packets in netif->loop_first/last and
226
+ // only delivers them when netif_poll(netif) is called. The loopback
227
+ // netif (`loop_netif`) is created by netif_init() but isn't promoted
228
+ // to `netif_default`, so use netif_poll_all() — it walks every netif
229
+ // on `netif_list` and is provided when LWIP_NETIF_LOOPBACK_MULTITHREADING
230
+ // is 0 (our case). Registered from mod_lwip_reset (patched in
231
+ // fetch.sh) so each `lwip.reset()` re-arms the loopback pump
232
+ // alongside the usual sys_check_timeouts() cadence. Also drains the
233
+ // virtual-eth RX queue so DHCP responses + ARP replies land.
234
+ void uc386dos_loopback_poll(void *arg) {
235
+ (void)arg;
236
+ extern int write(int fd, const void *buf, unsigned int n);
237
+ write(1, "[lp:np]", 7);
238
+ netif_poll_all();
239
+ write(1, "[lp:rx]", 7);
240
+ uc386dos_eth_pump_rx();
241
+ write(1, "[lp:done]", 9);
242
+ }
243
+
244
+ // Expose `mp_module_lwip` as `socket` too. modlwip.c registers
245
+ // itself under MP_QSTR_lwip and MP_QSTR_socket via `MP_REGISTER_*`
246
+ // markers — but our hand-rolled moduledefs.h doesn't process those.
247
+ // The `socket` registration goes through UCDOS_MOD_ENTRY_SOCKET in
248
+ // build.sh's heredoc; we don't need any C-side glue for that.
@@ -0,0 +1,117 @@
1
+ // uc386-dos lwipopts.h — minimum-viable config for IPv4-only TCP/UDP
2
+ // with loopback testing. No threading (NO_SYS=1), no DHCP for now,
3
+ // no IPv6, no stats. Just enough surface so MICROPY_PY_LWIP=1 can
4
+ // expose `socket` and a Python program can `socket.socket()`,
5
+ // `bind`, `listen`, `connect`, `send`, `recv` over 127.0.0.1.
6
+ //
7
+ // Tuning will follow once we have real packet I/O wired up — DHCP
8
+ // + DNS + a real netif driver are deferred until the dos-emu fake
9
+ // packet driver (or real-DOS testing) lands.
10
+
11
+ #ifndef MICROPY_INCLUDED_LWIPOPTS_H
12
+ #define MICROPY_INCLUDED_LWIPOPTS_H
13
+
14
+ // MicroPython's modlwip.c uses the raw API (tcp_new etc.) on a
15
+ // single-threaded foreground loop. NO_SYS=1 disables the
16
+ // thread-aware netconn/socket API and the rtos abstractions —
17
+ // matches the rp2 / esp32-port shape for raw-mode lwip.
18
+ #define NO_SYS 1
19
+ #define LWIP_RAW 1
20
+ #define LWIP_NETCONN 0
21
+ #define LWIP_SOCKET 0
22
+ #define LWIP_NETIF_API 0
23
+
24
+ // Core protocols. ARP+IP for the link layer, ICMP+IGMP for control,
25
+ // TCP+UDP for transport, DNS for name resolution, AUTOIP for fallback
26
+ // addressing when DHCP isn't running.
27
+ #define LWIP_ARP 1
28
+ #define LWIP_ETHERNET 1
29
+ #define LWIP_IPV4 1
30
+ #define LWIP_IPV6 0
31
+ #define LWIP_ICMP 1
32
+ #define LWIP_IGMP 1
33
+ #define LWIP_TCP 1
34
+ #define LWIP_UDP 1
35
+ #define LWIP_DNS 1
36
+ #define LWIP_DHCP 1
37
+ #define LWIP_AUTOIP 0
38
+ #define LWIP_ACD 0
39
+ #define LWIP_DHCP_DOES_ACD_CHECK 0 // ACD off → don't probe
40
+ #define LWIP_DHCP_BOOTP_FILE 0
41
+ #define LWIP_DHCP_GET_NTP_SRV 0
42
+ #define LWIP_DHCP_PROVIDE_DNS_SERVERS 1 // accept DNS from option 6
43
+ // dhcp.c includes <stddef.h> via lwIP's def.h, then time.h via
44
+ // timeouts.h's chain — both typedef size_t under _SIZE_T_DEFINED.
45
+ // stddef.h's separate _PTRDIFF_T_DEFINED guard (fixed in lib/include)
46
+ // keeps ptrdiff_t reachable.
47
+
48
+ // Loopback — lets unit tests bind/connect 127.0.0.1 to itself
49
+ // without any real netif driver. Phase 1 testing relies entirely
50
+ // on this; phase 2 will add a packet-driver netif alongside.
51
+ #define LWIP_HAVE_LOOPIF 1
52
+ #define LWIP_NETIF_LOOPBACK 1
53
+ #define LWIP_LOOPBACK_MAX_PBUFS 8
54
+
55
+ // Memory. uc386's GC heap is 256 KB total — keep lwIP's footprint
56
+ // modest. These knobs roughly mirror the minimal-port config from
57
+ // upstream lwIP examples.
58
+ #define MEM_ALIGNMENT 4
59
+ #define MEM_SIZE 4096
60
+ #define MEMP_NUM_PBUF 16
61
+ #define MEMP_NUM_RAW_PCB 4
62
+ #define MEMP_NUM_UDP_PCB 4
63
+ #define MEMP_NUM_TCP_PCB 4
64
+ #define MEMP_NUM_TCP_PCB_LISTEN 4
65
+ #define MEMP_NUM_TCP_SEG 16
66
+ #define MEMP_NUM_REASSDATA 4
67
+ #define MEMP_NUM_FRAG_PBUF 4
68
+ #define MEMP_NUM_ARP_QUEUE 2
69
+ #define MEMP_NUM_IGMP_GROUP 2
70
+ #define MEMP_NUM_SYS_TIMEOUT 8
71
+ #define PBUF_POOL_SIZE 16
72
+ #define PBUF_POOL_BUFSIZE 256
73
+ #define TCP_MSS 536
74
+ #define TCP_WND (4 * TCP_MSS)
75
+ #define TCP_SND_BUF (4 * TCP_MSS)
76
+ #define TCP_SND_QUEUELEN (4 * (TCP_SND_BUF / TCP_MSS))
77
+
78
+ // Statistics off — saves a few KB.
79
+ #define LWIP_STATS 0
80
+
81
+ // LWIP_DEBUG is INTENTIONALLY left undefined here. err.h gates
82
+ // `extern const char *lwip_strerr(err_t)` on `#ifdef LWIP_DEBUG`,
83
+ // so even `#define LWIP_DEBUG 0` is enough to pull in the extern
84
+ // reference (which isn't satisfied because lwip_strerr's
85
+ // definition lives behind LWIP_DEBUG too). Leaving it undefined
86
+ // makes err.h fall through to `#define lwip_strerr(x) ""`.
87
+
88
+ // Critical-section primitives. NO_SYS=1 with no real interrupts
89
+ // to mask, so make these all no-ops. modlwip.c's tcpip thread
90
+ // abstraction uses both macro and function forms — provide both.
91
+ #define SYS_ARCH_DECL_PROTECT(lev) do { } while (0)
92
+ #define SYS_ARCH_PROTECT(lev) do { } while (0)
93
+ #define SYS_ARCH_UNPROTECT(lev) do { } while (0)
94
+ #define sys_arch_protect() ((sys_prot_t)0)
95
+ #define sys_arch_unprotect(lev) do { (void)(lev); } while (0)
96
+
97
+ // We don't have an OS to call. Print the assert message + abort()
98
+ // so the dos_emu test path surfaces which assertion tripped (the
99
+ // message + filename + line make it down to stdout before exit).
100
+ #include <stdio.h>
101
+ #define LWIP_PLATFORM_ASSERT(x) \
102
+ do { printf("lwIP assert: %s at %s:%d\n", (x), __FILE__, __LINE__); \
103
+ abort(); } while (0)
104
+
105
+ // Use the standard lwip_htons/lwip_htonl from def.c — uc386 doesn't
106
+ // ship a system byteorder.h.
107
+ // (Don't define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS — that path
108
+ // expects htons/htonl etc. to come from elsewhere.)
109
+
110
+ // Random — lwIP's DNS uses LWIP_RAND() for transaction IDs / port
111
+ // allocation. Route through libc's `random()` (LCG, seeded by
112
+ // MICROPY_PY_RANDOM_SEED_INIT_FUNC's bios_ticks() at boot). Not
113
+ // crypto-strength, but transaction-ID strength is fine.
114
+ extern long random(void);
115
+ #define LWIP_RAND() ((unsigned long)random())
116
+
117
+ #endif // MICROPY_INCLUDED_LWIPOPTS_H