HelloSun commited on
Commit
99910e5
·
verified ·
1 Parent(s): c722e6d

Add developer package: sources, build/test tools, HANDOFF

Browse files
Files changed (3) hide show
  1. README.md +4 -0
  2. dev/HANDOFF.md +128 -0
  3. dev/dosmqtt-dev.zip +3 -0
README.md CHANGED
@@ -41,3 +41,7 @@ So the DOS side is genuine TCP/IP; only the last hop (browser → broker) is a W
41
  ## Sources and licenses
42
 
43
  See `LICENSE`, `NOTICE.md` and `src/` (DOS program sources, build scripts, boot-disk scripts). GPL-3.0-or-later.
 
 
 
 
 
41
  ## Sources and licenses
42
 
43
  See `LICENSE`, `NOTICE.md` and `src/` (DOS program sources, build scripts, boot-disk scripts). GPL-3.0-or-later.
44
+
45
+ ## Developer package
46
+
47
+ `dev/dosmqtt-dev.zip` holds the sources, build scripts, tests and the developer handover (`dev/HANDOFF.md`). It also contains the mTCP source archive that A.EXE / B.EXE are linked against. Rebuild everything with `tools/build_all.sh`.
dev/HANDOFF.md ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HANDOFF — DOS MQTT LAB
2
+
3
+ Read this first. Everything below was checked against the real running page, not assumed.
4
+ Project owner language: Traditional Chinese. Replies: short, factual, no emojis, say limitations honestly.
5
+
6
+ ## 1. What it is (and what it is not)
7
+
8
+ * Space: `HelloSun/dosmqtt` (static, public). Live: https://hellosun-dosmqtt.static.hf.space/
9
+ * Two **FreeDOS 1.4** machines ("A" listener, "B" sender) run side by side in the page on the **v86** x86 emulator.
10
+ * Inside each DOS: Crynwr **NE2000 packet driver** -> mTCP **DHCP.EXE** (gets 192.168.86.100) ->
11
+ **A.EXE / B.EXE**, real DOS programs (Open Watcom + mTCP library) doing DNS, TCP and **MQTT 3.1.1** (QoS 0) themselves.
12
+ * `A.EXE`: connect, subscribe, print `[hh:mm:ss] A rx: <msg>`, 1 s later print `[hh:mm:ss] A echo: <msg>`.
13
+ * `B.EXE`: run once: connect, publish `Hello from DOS B at hh:mm:ss`, disconnect, exit. The page types `B` every 5 s
14
+ (DOS has no scheduler). The page types `A` once.
15
+ * **Honest limits**: only the last hop is not TCP: the browser cannot open raw TCP, and a static Space has no server,
16
+ so `site/bridge.js` relays the guest's TCP streams on **port 1883** to a public broker's **MQTT-over-WSS** endpoint.
17
+ MQTT is identical on both transports, so the bytes DOS writes are exactly what the broker receives (verified with an
18
+ independent raw-TCP subscriber, see section 5). "Background" A is a second VM because DOS is single-tasking.
19
+ js-dos 6.22 (an earlier version of this Space) has no TCP/IP at all, which is why v86 is used now.
20
+
21
+ ## 2. Data path
22
+
23
+ ```
24
+ A.EXE/B.EXE (mTCP TCP/IP stack, DOS)
25
+ -> packet driver NE2000.COM (INT 0x60, I/O 0xC000, IRQ 10)
26
+ -> v86 emulated NE2000 (PCI) DHCP + DNS answered by v86 itself
27
+ -> v86 "wisp" network adapter (terminates guest TCP) DNS = DoH to cloudflare-dns.com (needs internet)
28
+ -> WebSocket('wisp://mqttbridge.invalid/?m=A|B') <- monkeypatched in bridge.js (Proxy on window.WebSocket)
29
+ -> FakeWispServer (bridge.js): per TCP stream opens
30
+ new WebSocket('wss://<broker>', 'mqtt') (port must be 1883, else stream refused)
31
+ -> public broker
32
+ ```
33
+ * WISP frames used: 1 CONNECT, 2 DATA, 3 CONTINUE (flow-control credit, we hand out 128 on stream 0 and per DATA), 4 CLOSE.
34
+ * Data the guest sends before the WSS is open is queued; broker->guest data is cut into 1 KB frames.
35
+ * Both VMs use the same IP; they are isolated (wisp backend isolates guests).
36
+
37
+ ## 3. Layout and how to build
38
+
39
+ See `README.md` for the tree. Build chain (`tools/build_all.sh`, a few seconds once deps are cached):
40
+
41
+ 1. `tools/fetch_deps.sh` downloads v86 0.5.472 (npm), SeaBIOS/VGABIOS, FreeDOS 1.4 floppy, Crynwr NE2000.COM,
42
+ mTCP 2026-10-10 (bin + src), Open Watcom **1.9** into `deps/`, and checks pinned SHA-256s (edit the hashes on purpose when upgrading).
43
+ 2. `src/build.sh` -> `build/A.EXE`, `build/B.EXE` (16-bit DOS compact model, `-0 -mc -oh -os -s -zp2 -zpw -we`).
44
+ mTCP sources are mirrored in lowercase first (Linux is case sensitive, mTCP is not).
45
+ 3. `src/make_image.sh` -> `build/dosmqtt.img` (FreeDOS boot floppy, strips installer files, adds boot files + NE2000.COM + DHCP.EXE + A/B.EXE; ~1 MB stays free).
46
+ 4. `src/build_assets.py` -> `dist/assets.b64.js` (v86.wasm, BIOSes, floppy as **gzip+base64 text**) and `dist/libv86.js`.
47
+ Reason: HF static Spaces answer binary files with a CORS-less redirect; text files are served directly.
48
+ 5. `build_all.sh` also copies `site/*` and `src/` into `dist/`. **`dist/` is exactly what gets uploaded to the Space.**
49
+
50
+ Rebuilt EXEs equal the deployed ones except 1 byte (mTCP embeds the build date `Oct 24 2026` vs `Oct 25 2026`).
51
+
52
+ `site/app.js` flow: unpack assets (`DecompressionStream`) -> start two `V86` instances -> poll the DOS text screens until
53
+ `TCP/IP is up` appears (printed by `FDAUTO.BAT`) -> enable the start button -> on start: type `SET MQTT_HOST/PORT/TOPIC`
54
+ in both machines, type `A` in machine A, wait for `subscribed to` on A's screen, then every 5 s type `B` in machine B
55
+ (skipped and logged if B's prompt `X:\>` is not the last screen line). Results are read **from the DOS screens**
56
+ (`emu.screen_adapter.get_text_screen()`), regex on `A rx:` / `A echo:` / `B sent:`; the log also shows bridge events.
57
+ A and B read host/port/topic from the DOS environment (`MQTT_HOST`, `MQTT_PORT` default 1883, `MQTT_TOPIC`) or from the command line `A <broker> <port> <topic>`.
58
+
59
+ ## 4. Hard-won facts (do not rediscover these)
60
+
61
+ * **NE2000 is NOT at 0x300.** SeaBIOS re-maps the PCI card: I/O **0xC000, IRQ 10**. Load with `NE2000 0x60 10 0xC000`.
62
+ (Read from `emu.v86.cpu.devices.pci.device_spaces`: BAR0 `c001`, dword 0x3c `10a`.) Packet interrupt 0x60 must match `PACKETINT 0x60` in `MTCP.CFG`.
63
+ * DHCP.EXE rewrites `A:\MTCP.CFG` on the floppy (writable). `MTCPCFG=A:\MTCP.CFG` is set in `FDAUTO.BAT`.
64
+ * **Use Open Watcom 1.9, not 2.0 beta.** EXEs from 2.0 beta end with `End: heap is corrupted!` (mTCP's `_heapchk`) even for an empty program
65
+ (`_heapchk` returned 3). With 1.9 it returns 0. `-we` (warnings are errors) bites on struct padding (W880): order fields so no padding is needed.
66
+ * mTCP needs `#include "global.cfg"` lowercase in `a.cfg/b.cfg` on Linux; the configs are copies of mTCP's `SAMPLE.CFG` with `TCP_MAX_SOCKETS 2`.
67
+ * **Typing into DOS:** `emu.keyboard_send_text(text, 25)` (25 ms per char). Without the delay FreeCOM drops characters
68
+ (`SET PATH=...` came out as `FRESET MTCPCFG=...`). Give each command >= 250 ms before the next.
69
+ * **Screen scaling:** in text mode v86 draws a DOM text layer (80x25, 14 px monospace), not the canvas. `screen_set_scale()` and
70
+ `use_graphical_text` turned the screen black/cropped. `fitScreen()` in `app.js` scales whichever layer is visible with a CSS `transform`.
71
+ * B uses a pseudo-random local TCP port per run; DNS answers rotate between several A records (that is normal).
72
+ * DOS clock is **UTC**; mTCP timer granularity is the 18.2 Hz BIOS tick (55 ms), so "1 s" echo reads 0.8-1.2 s from the host.
73
+ * **Broker quality differs a lot** (CONNECT -> CONNACK over WSS from headless Chromium in this sandbox; 12 attempts per broker, HiveMQ also 30):
74
+ `broker.emqx.io:8084` 12/12 fast (~560 ms); `test.mosquitto.org:8081` 12/12 fast (~390 ms);
75
+ `broker.hivemq.com:8884` only 7/12 (and 19/30 in another run) fast, the rest took ~10 s or never answered (an idle WS without CONNECT is closed at ~10 s);
76
+ `mqtt.eclipseprojects.io/mqtt` 0/12. Default is EMQX. B's 10 s CONNACK timeout then shows `MQTT: CONNECT refused or timed out`.
77
+ * A sends PINGREQ every 30 s (keepalive 60 s) and keeps <= 8 pending echoes. A has no reconnect: restart the VMs ("停止並重新開機").
78
+ * The "stop" button can't send Ctrl-Break; it reboots both VMs (`vm.restart()`), then waits for `TCP/IP is up` again.
79
+
80
+ ## 5. Testing (Playwright + independent MQTT peer)
81
+
82
+ `pip install playwright && playwright install chromium`; Chromium flags `--no-sandbox --disable-gpu --disable-dev-shm-usage`.
83
+ * `tests/serve_harness.sh`: bare v86 page on :8801 (verbose bridge logging) for manual DOS poking via `drive2.py`
84
+ (`session([(cmd, wait_s), ...])` prints the DOS screen + bridge log).
85
+ * `python3 -m http.server 8802 --directory dist`, then
86
+ `python3 tests/full.py http://127.0.0.1:8802/ <topic> <seconds> <shot.png>` runs the whole page (also works on the live URL).
87
+ * **Always cross-check with something that is not the page**: `python3 tests/sub.py <topic> <secs> broker.emqx.io` (raw TCP 1883 subscriber)
88
+ must print every `Hello from DOS B at ...`; `python3 tests/pub.py <topic> <delay> <n>` publishes to test A on its own.
89
+ Use the same topic in the page (`full.py` fills it) and the same broker host (default EMQX).
90
+ * Results when this was written: local 8/8 matched, 8 messages seen by the independent subscriber; **live URL** 9 B runs, 9 sent, A received 9 and echoed 8
91
+ (the 9th echo was still within its 1 s delay when sampled); the independent subscriber saw all 9.
92
+ * Use `kill <pid>`; `pkill -f <name>` kills your own shell.
93
+
94
+ ## 6. Deploy rules (the owner was angry about breakage before)
95
+
96
+ 1. **Only write to `HelloSun/dosmqtt`.** Never touch `HelloSun/c_dos`, `flare-gamet2` or any other Space (reading `c_dos` for reference is fine).
97
+ 2. **Never delete remote files.** Upload with `HfApi.create_commit` + `CommitOperationAdd` only; compare `list_repo_files` before/after and report `deleted: []`.
98
+ 3. Account name is `HelloSun` (case matters: `hellosun/...` gave 403 "no rights to create a space"). Auth: read the token from
99
+ `/root/.cache/huggingface/token`. Env `HF_TOKEN` is invalid. A token was once pasted into chat; it is exposed and
100
+ lacks create rights: advise revoking it. **Never write a token into any file.** `hf` CLI is broken here (typer/click mismatch): use the Python API.
101
+ 4. After upload HF needs ~1 min to publish; verify with `curl` for a new symbol and then run `tests/full.py` on the live URL.
102
+
103
+ ```python
104
+ from huggingface_hub import HfApi, CommitOperationAdd
105
+ from pathlib import Path
106
+ api = HfApi(token=Path("/root/.cache/huggingface/token").read_text().strip()); repo = "HelloSun/dosmqtt"
107
+ before = set(api.list_repo_files(repo, repo_type="space"))
108
+ ops = [CommitOperationAdd(path_in_repo=str(p.relative_to(root)), path_or_fileobj=str(p)) for p in files]
109
+ api.create_commit(repo_id=repo, repo_type="space", operations=ops, commit_message="...")
110
+ assert not (before - set(api.list_repo_files(repo, repo_type="space")))
111
+ ```
112
+ Space file set: web files + `assets.b64.js` + `libv86.js` + `src/` + licenses, and `dev/` (this package, `dev/dosmqtt-dev.zip`, `dev/HANDOFF.md`).
113
+ Unused leftovers from the old js-dos version, **deliberately not deleted**: `js-dos.js`, `emu.b64.js`, `dos.b64.js`, `LICENSE-js-dos.txt`.
114
+
115
+ ## 7. Licensing
116
+
117
+ A.EXE/B.EXE link mTCP (GPL-3.0-or-later), so our DOS sources, scripts and web code are GPL-3.0-or-later and the mTCP source archive
118
+ is shipped in `third_party/`. FreeDOS (GPL-2.0), v86 (BSD-2-Clause), SeaBIOS (LGPL-3.0) and the Crynwr packet driver
119
+ (source must stay available: Crynwr 11.x sources are at https://archive.org/details/PKTD11A_ZIP) are listed in `site/NOTICE.md`.
120
+ Open Watcom is only a build tool and is not redistributed.
121
+
122
+ ## 8. Known limits / ideas
123
+
124
+ * QoS 0 only, no TLS and no auth in the DOS clients (TLS is done by the browser's WSS). Topic/payload are small (payload <= 95 chars in A).
125
+ * B is started by the page typing `B`; if the previous run is still going the tick is skipped (log shows it). A real in-DOS loop would need a `B.EXE -loop` mode.
126
+ * Only port 1883 is bridged. Other ports are refused on purpose.
127
+ * The page needs `DecompressionStream`, WebAssembly and internet (DoH + broker). No offline mode.
128
+ * Ideas: reconnect in A, in-DOS 5 s loop for B, MQTT QoS 1, mobile on-screen keyboard, a selectable second bridge (own WISP server) for brokers without WSS.
dev/dosmqtt-dev.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:16f49d5ad27b3cf0304a1375ae38ec7d0cb4def1f1cd1b9d22c7dc95c3ca02ab
3
+ size 893923