CARVE: S3SM5 stamps MICFU:=3SWMESS + SWFUN and fills the swapper-message body¶
Date: 2026-07-21
Track: TRACK A (static ND-100/ND-500 disassembly of SINTRAN III L, L-VSX-500)
Scope: get a RELIABLE instruction-level decode of segment 030-S3SM5 and name the
routine that stamps MICFU := 3SWMESS (=5) / SWFUN := MSWIN (=5) and fills the
15-word swapper-message body that SWAPPER-K01 reads.
Grades on every claim: - [V] byte/line-verified by direct reading of a cited disassembly line or file - [I] inference from that reading (reasoned, not literally stated by the bytes) - [OPEN] not established; needs a further carve or a live trace
Files read / produced (full absolute paths):
- IN E:\Dev\Ronny\NDInsight\tools\sintran-segment-carver\versions\L-VSX-500\segments\030-S3SM5.bin
- IN E:\Dev\Ronny\NDInsight\tools\sintran-segment-carver\versions\L-VSX-500\segments\030-S3SM5.meta.json
- IN E:\Dev\Ronny\NDInsight\tools\sintran-segment-carver\versions\L-VSX-500\re\030-S3SM5-routine-map.md
- IN E:\Dev\Ronny\NDInsight\tools\sintran-segment-carver\versions\L-VSX-500\re\030-S3SM5.ghidra-symbols.txt
- IN E:\Dev\Ronny\NDInsight\tools\sintran-segment-carver\versions\L-VSX-500\re\006-S3FS.dis (sibling recipe reference)
- IN E:\Dev\Ronny\NDInsight\SINTRAN\NPL-SOURCE\SYMBOLS\L07\N500-SYMBOLS.SYMB.TXT
- IN E:\Dev\Ronny\NDInsight\SINTRAN\NPL-SOURCE\s3vs-4.symb
- IN E:\Dev\Ronny\NDInsight\SINTRAN\ND500\swapper\swapper-k01-handlers.md
- IN E:\Dev\Ronny\NDInsight\SINTRAN\ND500\CARVE-MSWIN-MESSAGE-SENDER-2026-07-21.md (prior art - PARTLY CORRECTED here)
- OUT E:\Dev\Ronny\NDInsight\tools\sintran-segment-carver\versions\L-VSX-500\re\030-S3SM5.dis (full reliable decode, 49152 words)
0. HEADLINE CORRECTION OF THE TASK PREMISE (read first)¶
The task states that 030-S3SM5 is "32-bit BYTE-ADDRESSED ND-500 code" and that I
"MUST use nd500-dis, NOT nd100-dis (nd100-dis will produce garbage)". That is
empirically FALSE for this segment. [V]
030-S3SM5is ND-100 code (16-bit, word-addressed).nd100-dis(on the byte-swapped image) produces clean, coherent ND-100 code;nd500-disdrifts into garbage/ASCII within a few instructions.- Corroboration: the sibling ND-500-subsystem segments already carved in the same
re\directory -006-S3FS.dis,003-S3CP.dis,045-S3ISYS.dis- were ALL produced withnd100-dison a byte-swapped.le.bin, and they decode cleanly (e.g.006-S3FS.disshowsMON 122 ReserveResource,MON 164 SaveSegment). - Corroboration: the project MEMORY note already records "030-S3SM5 is ND-100 code (compiled 5STDRIV)".
The "S3" prefix = SINTRAN-III. S3SM5 is the ND-100 System Monitor for the ND-500
(the code that loads/manages the ND-500 swapper and services non-500B ND-500 MON
calls). It runs on the ND-100, not the ND-500. The ND-500 swapper it feeds
(SWAPPER-K01) is the separate ND-500 domain analysed in swapper-k01-handlers.md.
The prior 030-S3SM5-routine-map.md header line ("Disassembler: nd500-dis ... big-endian")
is the source of the wrong premise; that same doc's section 6 admits the nd500-dis
decode was untrustworthy. It was untrustworthy because it was the wrong ISA.
1. THE RELIABLE-DECODE RECIPE (reproducible)¶
# WSL (Git Bash is NOT WSL). nd100-dis + python3 are WSL-only.
# 1. Copy the .bin to a LOCAL WSL path. (nd100-dis/nd500-dis fail to open files
# under /mnt/... with "Cannot open ''"; a local copy in /tmp works.)
cp .../segments/030-S3SM5.bin /tmp/s3sm5.bin
# 2. Byte-swap BIG-ENDIAN -> LITTLE-ENDIAN (swap each 16-bit word).
python3 -c 'd=open("/tmp/s3sm5.bin","rb").read(); o=bytearray(len(d))
for i in range(0,len(d)-1,2): o[i]=d[i+1]; o[i+1]=d[i]
open("/tmp/s3sm5.le.bin","wb").write(o)'
# 3. Disassemble with nd100-dis, octal, base = segment load word address 040000.
nd100-dis -a -o -b 40000 /tmp/s3sm5.le.bin > /tmp/s3sm5.full.dis
What makes it reliable (all [V]):
- Base word 040000 octal = 0x4000 is the segment load address from
030-S3SM5.meta.json (load_address.oct=40000). With that base the displayed
octal address IS the runtime ND-100 word address, and the ghidra data symbols land
exactly: VERSI 0x4005 maps to the ASCII version string 88. 8.17, REVIS 0x400a
to L00.
- Byte order: the raw .bin is BIG-ENDIAN (per meta.json and the readable ASCII
in the raw file); nd100-dis wants LITTLE-ENDIAN, so byte-swap first. This matches
the sibling .dis files (they were built from a .le.bin).
- Sanity signals: the classic ND-100 PLANC call idiom JPL I <disp> immediately
followed by a pointer word that disassembles as RAND 0 0 (144400) appears
throughout; MON calls decode to sensible SINTRAN numbers; the swapper-message
builders (section 3) are internally consistent (message base, field offsets, and
the literal 5 all agree with the N500-SYMBOLS field layout).
Address arithmetic:
runtime_word (octal, as displayed) = file_word + 0x4000
file_byte = 2 * file_word = 2 * (runtime_word - 0x4000)
Caveat [V]: this is a linear disassembly. 030-S3SM5 is PLANC with inline
'-terminated ASCII strings and pointer data interspersed in the code, so stretches
of the listing that fall inside a string/data pool decode as nonsense instructions.
The decode is reliable as ND-100 code when read from a true instruction boundary;
do not trust a run that starts mid-string. The builders in section 3 were read from
verified instruction boundaries.
2. BOTTOM LINE (graded verdict)¶
S3SM5 itself - ND-100 code - stamps MICFU := 3SWMESS (=5), writes SWFUN, and
fills the swapper-message body. The stamp/fill is NOT in the ND-5800 microcode and
NOT (only) on the ND-500 side. [V for the field writes; [I] for the MICFU/SWFUN/body
symbolic identity, which rests on the N500-SYMBOLS offsets, not on an S3SM5 symbol.]
Two swapper-message builders were located and byte-verified in the reliable decode.
Both take the message base into X with LDX ,B -67 (octal 054711) and write the
control field MICFU at offset 6 with the literal 5 (SAA 5):
-
Generic builder - the MSWIN=5 path - stamp at runtime octal 140771..141001 (hex word 0xC1F9..0xC201; file byte 0x103F2..0x10402).
SWFUN(offset 7) := a caller-supplied variable[B-77], gated at 140765 so that if[B-61] != 0the code instead uses 24B (=MSWSWAIT). For a swap-in the caller passes[B-77] = MSWIN = 5. This is the routine that produces aMICFU=3SWMESS, SWFUN=MSWIN=5message.MICFU(offset 6) := 5 = 3SWMESS (SAA 5; STA ,X 6).- Body words: offset 2 := 1, offset 4 :=
[B+72].
-
Full-body builder - the MSWSWAIT path - stamp at runtime octal 162155..162207 (hex word 0xE46D..0xE487; file byte 0x1093A..0x1096E).
MICFU(offset 6) := 5 = 3SWMESS;SWFUN(offset 7) := 24B = MSWSWAIT (=20).- Fills a ~15-word body: offsets 2,3,4,10,11,12,13,14,15,16,17 plus the double words at 110 and 112 - i.e. the multi-word message body the task describes.
SWFUN is never hard-coded to 5; it is always the per-message swap-function code
(variable in builder 1, literal 24B in builder 2). MICFU is always the literal 5
(3SWMESS). That is exactly the two-different-5s structure the field table predicts:
MICFU=3SWMESS=5 (the interface control field) and SWFUN=MSWIN=5 (the swap
sub-function, supplied by the requester). [V]
This corrects the prior CARVE-MSWIN-MESSAGE-SENDER-2026-07-21.md. That doc
concluded (its bottom-line item 3, and evidence F) that "no ND-100 code fills the
body / SWFUN is only ever LOADED never STORED / the sender is on the ND-500 side."
That conclusion was an artefact of its grep scope: it searched the NPL source tree
and s3vs-4.symb (the resident SINTRAN nucleus). S3SM5 is a separate loadable
segment whose source is NOT in the repo (verified in section 4), so its stores were
invisible to that grep. The binary carve shows S3SM5 (still ND-100 code) DOES store
SWFUN and DOES write MICFU:=3SWMESS and DOES fill the body. [V]
Implication for the D4 blocker (INFERENCE, not proven here): if S3SM5 is the ND-100
routine that builds and fills the MSWIN body, then an empty body at HSWPI on the
ND-5800 image means either (a) S3SM5's builder for MSWIN did not run / took the
[B-61]!=0 MSWSWAIT branch, or (b) the buffer read at HSWPI is not the one S3SM5
filled. That is the next thing to pin (section 6). [I]/[OPEN]
3. ADDRESS-LEVEL EVIDENCE CHAIN¶
A. Field/const values (N500-SYMBOLS, L07) [V]¶
MICFU = 000006 (message word offset 6 - the interface control field)
SWFUN = 000007 (message word offset 7 - the swap sub-function)
3SWME = 000005 (3SWMESS - the MICFU value that selects the SWMESS handler)
MSWIN = 000005 (swap-function value MSWIN)
SWPST = 000103 HSWPI = 000104 (the ND-100-side SWMSG fields)
B. S3SM5 is the swapper manager (strings in the raw .bin) [V]¶
Byte offsets in 030-S3SM5.bin (big-endian, ASCII stored in natural order):
0x00C92 "$Swapper in use on another CPU'"
0x071BF "\"$> Loading Swapper'" <- the exact D4-stall string
0x0605C "...(SYSTEM)SWAPPER.PROCESS-SEGM..."
0x160C2 "(SYSTEM)SWAPPER.EXTRA-DATA-SEGMENT-2'"
0x16E29 "$Swapper debug mode set: use \"go\" to restart Swapper'"
C. Builder 1 - generic MSWIN/MSWSWAIT stamp (reliable decode) [V]¶
Runtime octal addresses (from 030-S3SM5.dis):
140765 044717 LDA ,B -61 ; gate variable
140766 131003 JAZ -> 140771 ; if [B-61]==0 use the passed SWFUN
140767 044024 LDA 24 ; else A := 24B (MSWSWAIT=20)
140770 124020 JMP -> 141010
140771 044701 LDA ,B -77 ; A := caller SWFUN (=MSWIN=5 for swap-in)
140772 054711 LDX ,B -67 ; X := MESSAGE-BUFFER BASE
140773 006007 STA ,X 7 ; msg.SWFUN (offset 7) := A
140774 170401 SAA 1
140775 006002 STA ,X 2 ; msg body offset 2 := 1
140776 044472 LDA ,B 72
140777 006004 STA ,X 4 ; msg body offset 4 := [B+72]
141000 170405 SAA 5 ; A := 5 = 3SWMESS
141001 006006 STA ,X 6 ; msg.MICFU (offset 6) := 3SWMESS(5)
D. Builder 2 - full ~15-word body + stamp (reliable decode) [V]¶
Runtime octal addresses:
162155 026352 LDD ,X -26
162156 022110 STD ,X 110 ; body double @110
162157 170401 SAA 1
162160 006016 STA ,X 16 ; body @16
162161 046354 LDA ,X -24
162163 046351 LDA ,X -27
162164 022112 STD ,X 112 ; body double @112
162166 006017 STA ,X 17 ; body @17
162167 002013 STZ ,X 13 ; body @13 := 0
162170 002014 STZ ,X 14
162171 002015 STZ ,X 15
162172 002010 STZ ,X 10
162173 002011 STZ ,X 11
162174 170423 SAA 23
162175 006012 STA ,X 12 ; body @12 := 23B
162176 170405 SAA 5
162177 006006 STA ,X 6 ; msg.MICFU (offset 6) := 3SWMESS(5)
162200 170424 SAA 24
162201 006007 STA ,X 7 ; msg.SWFUN (offset 7) := 24B (MSWSWAIT=20)
162202 170401 SAA 1
162203 006002 STA ,X 2 ; body @2 := 1
162204 044720 LDA ,B -60
162205 006003 STA ,X 3 ; body @3 := [B-60]
162206 044400 LDA ,B 0
162207 006004 STA ,X 4 ; body @4 := [B+0]
LDX ,B -67 at the head of this block, same message base as builder 1.)
E. S3SM5 also drives the ND-100 SWMSG + MON 377 [V]¶
Independent corroboration that S3SM5 speaks the swapper protocol on the ND-100 side:
164103 054711 LDX ,B -67
164104 046103 LDA ,X 103 ; A := SWMSG.SWPST (offset 0o103)
164106 153377 MON 377 ; issue MON 377 (swapper/interface)
F. Why the prior negative proof missed this (scope, not contradiction) [V]¶
s3vs-4.symb(the resident-nucleus build the prior doc grepped) does NOT contain S3SM5's data symbols:grep -E '\b(FSCAP|MRSEG|ADRZO|MBDYN|SGCOM)\b' s3vs-4.symbreturns nothing. Those symbols ARE S3SM5's (from030-S3SM5.ghidra-symbols.txt).- S3SM5's distinctive strings (
SET-TEMPORARY-FILE,WAIT-FOR-OPERATOR,Open File Table is full,power fail has occured) do NOT appear anywhere inSINTRAN\NPL-SOURCE\NPL\*.NPL. S3SM5's source is not in the repo. - Therefore the prior full-tree grep (
SWFUNonly ever LOADED,MSWINabsent,3SWMESSonly tested) is TRUE for the resident nucleus but says nothing about S3SM5. The stamp/fill lives precisely in the segment the grep could not see.
4. EVIDENCE TABLE¶
| Claim | Grade | Evidence |
|---|---|---|
030-S3SM5 is ND-100 code, not ND-500 |
[V] | nd100-dis(LE,base 040000) clean; nd500-dis garbage; sibling 006-S3FS.dis uses nd100-dis; MEMORY note |
Reliable recipe = byte-swap BE->LE + nd100-dis -a -o -b 40000 |
[V] | header symbols land (VERSI=88. 8.17), clean PLANC JPL-I/RAND pairs |
| S3SM5 is the ND-100 swapper loader/manager | [V] | strings "> Loading Swapper", "Swapper in use on another CPU", debug-mode strings |
Message base for the builders = X:=[B-67] (054711) |
[V] | 030-S3SM5.dis @140772, @162150-ish, @164103 |
| MICFU (offset 6) := 3SWMESS = literal 5 | [V] | SAA 5; STA ,X 6 @141000-141001 and @162176-162177 |
| SWFUN (offset 7) := per-message swap fn (var / 24B), never hardcoded 5 | [V] | @140771-140773 (var [B-77]); @162200-162201 (24B=MSWSWAIT) |
| Builder 1 (oct 140771..141001) is the MSWIN=5 path | [I] | SWFUN taken from caller var [B-77]; =5 for a swap-in; gate at 140765 selects MSWSWAIT otherwise |
| Builder 2 (oct 162155..162207) fills the ~15-word body | [V] | stores to offsets 2,3,4,10-17,110,112 |
| The written offsets 6/7 ARE MICFU/SWFUN of a swapper message | [I] | offsets match N500-SYMBOLS exactly; base holds a message; but no S3SM5 symbol proves the struct identity |
| S3SM5 reads SWMSG.SWPST(0o103)/HSWPI(0o104) and issues MON 377 | [V] | 030-S3SM5.dis @164103-164106 |
| Prior "no ND-100 code fills the body" was a grep-scope artefact | [V] | S3SM5 symbols/strings absent from s3vs-4.symb + NPL tree |
| ND-5800 microcode is NOT required to stamp/fill the message | [I] | the stamp/fill is present in S3SM5 ND-100 code |
5. WHAT THE STAMP ROUTINE IS (best statement)¶
- The stamp/fill is an ND-100 subroutine inside S3SM5 with two entry variants
(a compact one and a full-body one). Both write
MICFU:=3SWMESS(5)into offset 6 of the message buffer addressed by[B-67], setSWFUNat offset 7 to the swap function requested, and write the body words. [V] - The MSWIN=5 message specifically is produced by the compact builder at runtime
octal 140771..141001 (hex word 0xC1F9..0xC201, file byte 0x103F2..0x10402) when
its caller supplies
[B-77]=MSWIN=5and[B-61]==0. [V for the code; [I] for "=5 is the MSWIN case".] - The routine has no symbol-table name (S3SM5's internal routine labels are not in
N500-SYMBOLS, and its source is not in the repo), so it can only be named by address, not by a source identifier. [V]
6. [OPEN] - what still needs a carve or a live trace¶
-
[OPEN] The enclosing subroutine ENTRY of each builder. I read the builders from verified instruction boundaries but did not resolve the JPL/pointer that calls them, so the routine's entry address and its caller (the PLACE-DOMAIN path) are not pinned. Trace backward from oct 140771 / 162155 to the nearest subroutine head, or set a breakpoint there under the boot harness.
-
[OPEN] Confirm
[B-67]is the buffer whose address becomes HSWPI. The offset match (6=MICFU, 7=SWFUN) is strong [I] but not proven to be the same buffer that5ACTSWAPPERpublishes asSWMSG.HSWPI(byte 0x420E30 in the live D4 capture). A live single-step of PLACE-DOMAIN should showX:=[B-67]equal to the HSWPI value. -
[OPEN] Which builder runs in the D4 flow, and does it run at all. The D4 body is empty. Determine at runtime whether (a) the MSWIN builder at 140771 executes and the buffer it fills is later overwritten/recycled, (b) the
[B-61]!=0gate diverts to the MSWSWAIT builder, or (c) neither builder runs because the swapper bring-up stalls upstream ("> Loading Swapper", emitted by S3SM5 at raw byte 0x071BF). -
[OPEN] Is MICFU=3SWMESS set here by S3SM5 or ALSO by microcode elsewhere? S3SM5 demonstrably writes it here; whether the interface also stamps MICFU in the trap/microcode path for other message classes is a separate question, not needed to answer this one.
-
[OPEN] Symbolic naming. If an S3SM5
.SYMBwith internal labels ever surfaces, re-resolve oct 140771 / 162155 to their source routine names.
7. NOTE ON THE TWO 5s (kept from prior art, still true)¶
MICFU=3SWMESS=5 and SWFUN=MSWIN=5 are two different message fields that both hold 5.
This carve confirms the split at the byte level: MICFU (offset 6) is ALWAYS the
literal 5 (the interface control field selecting the SWMESS handler), while SWFUN
(offset 7) is the requester-chosen swap sub-function (=5 for MSWIN, =24B for MSWSWAIT,
etc.). Do not collapse them.