NDIX's XMSG use vs what SINTRAN does to our Ethernet II card — cross-check findings (2026-08-08)¶
Sources read: E:\Dev\Ronny\NDIX-C\kernel\MASTER\if\if_et.c, if_access.h, xmsg.h (Release C,
1988), against our carve corpus — chiefly
E:\Dev\Ronny\NDInsight\Installation\Communication\Ethernet\RE\PIOCOS\LOC-XMSG-CLIENT.md,
MP-P2-PIOC-DRIV.NPL, XMSG-VALUES-M.SYMB, and the RetroCore decoder
Emulated.HW\ND\CPU\ND100\Sintran\MON_200_XMSG.cs. Tags: [V] verified, [I] inferred, [U] unknown.
1. The big match: NDIX and the Ethernet II card are the SAME KIND of thing [V]¶
Both are remote XMSG clients of the SINTRAN kernel. Neither can issue MON 200 itself, so both
hand the kernel a function + the four ND-100 registers and get the registers written back:
| NDIX (ND-500, 1988) | Ethernet II card (68K LOC-XMSG) | |
|---|---|---|
| Transport | fecall generic dev 7, two rings @0x30000000/0x800 | MBOXH activation queue @ card DRAM 0x4C2 |
| Request unit | xmsg_cmd {seq, subdev, func, T/A/D/X, magno} (20 B) |
6-word param block: func(T), A, D, X, uaddr |
| Func encoding | low byte = function, options OR-ed high (XFMASK 0xff) |
same (0x800D = XFRCV+wake, 0x040C = XFSND+route) |
| Doorbell in | KICK, only on empty→non-empty | SCIP INT12 (0xEF0080/0xEF0180) |
| Doorbell out | interrupt per response | PWCR.BNDC → MFP GPIP-I6 → 0x250E |
| Reply | response packet echoing seq/subdev/func | registers written back IN PLACE, NXFNC bit1 |
| Outstanding | 1 receive + 1 non-receive per subdevice | one element in flight (classic bring-up) |
| Recovery on loss | NONE — hangs forever | NONE — same |
The function numbers agree exactly where both sides define them (octal in NDIX, decimal in our
symbol file): XFGET 2, XFREL 3, XFRHD 4, XFWHD 5, XFREA 6, XFWRI 7, XFMST 9, XFOPN 10, XFSND 12,
XFRCV 13, XFRTN 31, XFRRH 32, XFRRE 41. The card's own XMPF* wrapper table (embedded vendor
symbols) covers nearly the same list. Same dialect, three independent 1980s sources. [V]
2. The mismatch that matters: functions 45 and 48 [V both sides; resolution open]¶
| Code | NDIX xmsg.h (1988) |
Our XMSG-VALUES-M.SYMB / decoder |
|---|---|---|
| 45 (055) | XETHER "special Ethernet call" — every packet NDIX transmits |
XFSFM "send message via specified link/netserver (privileged, COSROUT only)" |
| 48 (060) | XFRREN "receive and read, don't wait" — NDIX's standing receive |
X5FUN END MARKER — the M-era function table ends at 47 |
Reading of 45 [I]: these are probably the SAME function under two names — "send via a specified netserver" is exactly what NDIX's XETHER does (A = buffer word addr, X = length, D = own port, plus the interface's magno). Not proven; needs the kernel dispatch carve for T=45.
Reading of 48 [V for the M symbol file]: an M-era XMSG kernel has no function 48. NDIX re-arms
its receive with XFRREN|XFWAK|XFRMR (48) after every packet. Against an M-vintage XMSG that call
would return XEILF (invalid function) — and by NDIX's own no-recovery design, receive would then
be dead. Any attempt to serve NDIX from a SINTRAN emulation must use a kernel generation whose
function table includes 48 (the K/L-era ND-500-serving kernels need checking; the version that
shipped with NDIX obviously had it). This is a compatibility gate, found only because the two
sources were put side by side.
Also new to us from NDIX: option bit XFRMR 0x1000 on XFRRE/XFRREN = "release after read" —
that is the buffer-recycling bit; our option-bit table has 0x1000 as XFBNC (bounce) for XFSND
context only. Same bit, function-dependent meaning, matches our "role byte" experience. [V]
3. magno confirmed by running 1988 code [V]¶
if_et.c etinit: after attach it calls XFMST(A=-1) (current message), stores
es_magno = A<<16 | D, and later reaches the interface DIRECTLY with
XFSND(A = magno>>16, D = magno & 0xffff). That is a live consumer treating A as SYSTEM and D as
the WIRE PORT WORD — our carved MAGNO = system<<16 | port<<7 | random layout exactly, and the
strongest possible confirmation of the LOC-XMSG doc's handle mapping (XFRRE.D = MESAD,
XFMST takes it as input; section 8b there). [V]
4. The media-access server: where NDIX's letters GO — still not located [U]¶
NDIX's attach letter (XSLET to *ENUM<unit>) is "forwarded to the Ethernet interface" — the
server lives card-side, per the 1985 manual ("a separate process running in the Ethernet
Controller"). But a raw byte scan of BOTH our 512 KB firmware images
(encos-ser-all-banks-68k.bin, tcp-ser-all-banks-b05-68k.bin) finds zero occurrences of the
ASCII string ENUM [V]. So neither image registers *ENUM<i> by literal name. Consistent with
the *XM-ENNS0 resolution: the card registers names only LOCALLY (XMSGIOCGAT) and the global
XROUT name is created HOST-side. Either the media-access server builds/receives the name without
storing it as a literal, or it lives in firmware we do not have (Ethernet-I-era "Ethernet Master").
The EXMTY message-type family (128..132 = data/attach/status/detach/defineMulti, from
if_access.h) has NOT been searched for in the images (16-bit constants, too noisy for a blind
scan) — a targeted Ghidra pass over tcp-ser's message dispatcher is the next step if we ever need
to serve NDIX. [U]
4a. UPDATE 2026-08-08 — tcp-ser's media-access dispatcher enumerated, and it SHARPENS #4 [V]¶
Done, while carving tcp-ser's *TCP seam. tcp-ser DOES contain a media-access server, but it is
the WRONG layer for NDIX and that is the point:
MACMDPORTH@0x6D2E (vendor name from the embedded symbol table) is the MEDIA-ACCESS command port handler. Its client is AIP — the card's OWN on-card IP stack, not a remote XMSG peer.- Its dispatch table
tbl_maCommandDispatch@0x24A86 (maxindex 0x1A = 26) uses a 6-bit opcode(RB[0] >> 2) & 0x3F. Only EVEN opcodes are populated (0,2,4,6,8,10,12,14,22,24,26); every odd index and every unassigned even index goes to the default error handler 0x727C. This is the live confirmation of the RB rule the NDIX side stated: request types are even, response = request+1, reply written in place. - Because that opcode space is 0..26, the EXMTY message types 128..132 can never appear here — they belong to the XMSG media-access USER↔SERVER layer, which is a different (XMSG-facing) server.
So the negative result in #4 is not just "no ENUM string" — it now has a mechanism: tcp-ser is
the TCP/IP product; it wires AIP→MA on-card and does not export the media-access service over
XMSG. The *ENUM<unit> XMSG server (Ethernet Basic Software, product 210582A) is genuinely a
different product that is not in either image we hold. Serving NDIX still requires that product;
carving tcp-ser further will not produce it.
Handlers of interest for anyone continuing: opcodes 22/24/26 (@0x71EA / 0x6FF2 / 0x7050) are the attach/start/stop family. [V for the table; those three opcode meanings partly I — verify per handler.]
4b. UPDATE 2026-08-08 — opcode 12 VERIFIED, and the old "set DIX mode" label is WRONG [V]¶
Fully decoded (0x7096-0x715E, plus workers 0x5522 search and 0x5486 allocate). Opcode 12 = DEFINE / ADD MULTICAST ADDRESS, not "set DIX mode". Argument = a 6-byte MAC address at RB+0x08:
arg[0] & 1must be 1 — this is the Ethernet I/G (group) bit; a non-multicast address is rejected with status −18. This is exactly the "bit 0 of byte 0" the earlier note saw, but its meaning is the multicast bit of a real MAC, and the other 5 bytes are the rest of the address, not padding.- Search the active list at 0x24A08 (worker 0x5522, a 6-byte compare down a linked list). Already present → −18.
- Allocate a node from the free pool 0x24A04 and append it to 0x24A08 (worker 0x5486,
#REMVfrom free list +#APPDto active list). Pool empty → −20. - Require an active user —
ACTIVEMAUS(0x2493E) orACTIVEDIXU(0x24942) nonzero — else −16. - Copy the 6 MAC bytes into node+4.
- If
LNMAIOACTI(0x24860) is set, STOPMA (0x5C6E) + STARTMA (0x5C46) to reprogram the LANCE multicast filter. (This STOPMA/STARTMA pair is what made the earlier reader guess "set DIX mode".) - Reply OK; response TYPE stamped request+1 = 13 (
andi #0x3FF / ori #0x3400at 0x7152) — a byte-exact confirmation of the even-request / odd-response rule.
This maps to NDIX if_access.h EXMTY "defineMulti". Sibling opcode 14 @0x7162 = REMOVE MULTICAST
ADDRESS (same 6-byte key, search via 0x5522 then delete). The real DIX-vs-802.3 mode gate is the
0x1888A mode word (a separate mechanism, already documented) — it is NOT an MA command opcode. The
handoff's set DIX mode = AIP request-block type 12 line should be struck.
5. Receive discipline — what it says about our starvation hunt [V facts, I application]¶
NDIX keeps exactly ONE outstanding XFRREN and re-arms it inside the completion interrupt; if the
command ring is full it retries on a 40 ms timeout; buffers recycle via the XFRMR release bit.
There is no watchdog: one lost completion = receive dead forever, by design. Combined with the
card's SCIP one-shot RFT latch and the host-fed rx pool, the family invariant is:
Completions and doorbells are assumed exactly-once. Consumers drain to empty. Producers are re-armed by the consumer's own completion path. NOTHING times out.
For the emulator this is a hard obligation: our card/bus emulation must never drop a SCIP, a
mailbox completion, or a kick under any interleaving — there is no protocol-level recovery to save
us. It also sharpens the starvation question: SINTRAN's driver-side analog of NDIX's
"re-arm in the completion handler" is the POST-BUFFER (opcode 0x12) refill; the new
[68K-RXPOOL] watches (0x67DA/0x6CEE, landed 2026-08-08) will show whether that re-arm chain
breaks, and this comparison says WHERE to look if it does: an error path that returns without
re-arming (NDIX carefully re-arms even on T<0 — does SINTRAN's ENNS0?). [I]
6. The hang contract — a concrete lead for the HLE burst-2 ordering bug [V contract, I application]¶
NDIX xg.c/etintr route completions by the response's func field, with two independent
slots per subdevice: the receive slot (XFRRE/XFRREN) and the other slot (XETHER, XFMST, ...). A
response whose func does not match unbalances the flags and hangs the subdevice permanently —
their words. Our HLE conn-to blocker is exactly an XFRRE-vs-XFRCV ordering race
(project_nd_ethernetii_hle_burst2_ordering). The 1988 contract says the fix shape: completions
must be matched to waiters by FUNCTION (receive-class vs other-class), never by arrival order.
Worth auditing XmsgClient/MboxhTransport completion routing against this rule. [I]
7. Action list¶
- (Done 2026-08-08) Reply to NDIX updated with the 45/48 version gate + the no-ENUM-string scan.
- When next in
MON_200_XMSG.cs: annotate 45/48 with both names (M-eraXFSFM/end-marker vs NDIXXETHER/XFRREN) so the decoder does not mislabel a future NDIX-side trace. Add option nameXFRMR(0x1000 on XFRRE/XFRREN). - If we ever serve NDIX from RetroCore SINTRAN: check the kernel generation's function-table length (>= 49) FIRST; then the targeted EXMTY dispatch search in tcp-ser.
- HLE burst-2: audit completion routing for func-matched (receive-class vs other-class) delivery.