SWAPPER-K01 - Deep Analysis: ND-100 <-> ND-500 Interaction, MON Traps End to End, and System Role¶
A dedicated deep-dive that builds on (does not replace) the two companion documents:
SINTRAN/ND500/swapper/swapper-k01.pseg.md(entry path, the MON 377B gate mechanism, routine inventory)SINTRAN/ND500/swapper/swapper-k01.dseg.md(DSEG hex dump, PSEG->DSEG cross-reference, descriptor data)
Primary evidence read directly for THIS document:
- Disassembly (ND-500, OCTAL, base 0x08000000):
SINTRAN/ND500/swapper/swapper-k01-pseg.asm - Raw segments:
SINTRAN/ND500/swapper/SWAPPER-K01.PSEG,SINTRAN/ND500/swapper/SWAPPER-K01.DSEG - ND-100 front door (MON 60 worker source):
SINTRAN/NPL-SOURCE/NPL/5P-P2-MON60.NPL - MON 60 subfunction reference (SINTRAN-source-derived):
SINTRAN/ND500/nd-500-mon/mon60-callers/SUBFUNCTION-TABLE.md - Per-command carved folders under
SINTRAN/ND500/nd-500-mon/mon60-callers/ - Monitor-call mechanism (prior art, corroborating, NOT from the swapper bytes):
SINTRAN/ND500/ND500-MONITOR-CALL-PARAMETER-PASSING.md,SINTRAN/ND500/ND500-SWAPPER-ANALYSIS.md,SINTRAN/ND500/swapper/N500-SYMBOLS.SYMB
Numbers in the disassembly are OCTAL; hex is prefixed 0x. Every octal<->hex conversion in
this document was computed programmatically (Python oct()/hex()), not by hand; the ones
that matter are spelled out in section 2. Every factual claim is either cited to a byte
offset / listing line and marked PROVEN, or marked INFERRED / UNKNOWN with the
experiment that would settle it. Where prior art disagrees with the bytes, the bytes win and
the disagreement is stated.
0. TL;DR - the role, in one paragraph¶
SWAPPER-K01 is an ND-500-resident paging/swap worker DOMAIN and a CLIENT of SINTRAN - it
is NOT "the low-level handler that does all ND-500 work for the ND-100". It is the ND-500
side of ND-500 process #0. It receives work as messages posted into ND-100 private memory,
pulls those messages across the interface by DMA (RIOM, 3 sites), dispatches them on a
private 29-entry function-code table (jumpg $0x08026198, DSEG function code at 0x080240B8),
does the actual page moves itself with ND-500 supervisor instructions (RPHS read-physical-
segment x2, PCTSB/DCTSB cache/table primitives x7), and when it needs a service it cannot
perform in-domain (swap-disk transfer, fatal-error reporting) it asks the ND-100 by trapping
through the ND-500 monitor-call gate (MON 377B = segment-31 offset 0o377 = monitor call 255
= N5SWAP, 15 sites). It never fields the ND-100's low-level requests and contains no
MON-number receive dispatcher; the bus/interrupt/DMA hardware is ND-100 I/O space it cannot
touch, and the swap-disk I/O is done by the separate ND-100 RT-program 5SWAP. Every one of
these statements is proven from the bytes below.
Correction to the task brief's premise (important). The brief treats every $1000225xxx
operand as a "MON 377B descriptor" and names 225110 (49x) and 225070 (19x) as "the dominant
services". The bytes refute this. Only six of the seventeen $1000225xxx operands are ever
the first argument of a MON 377B call; 225110 and 225070 are never MON 377B arguments at
all - they are the integer constants 6 and 1, loaded by value (w2 :=, w move). See
section 3, which is the corrected descriptor decode.
1. What "225110" and "225070" actually are (answering the brief head-on)¶
The brief asked to "identify 225110 (49x) and 225070 (19x) first and carefully". Done, from the bytes:
$1000225110 = DSEG 0x12A48 = the constant integer 6. Its 49 references are all value
loads. Grep of SINTRAN/ND500/swapper/swapper-k01-pseg.asm:
8800:1000065572: 015 304 010 001 052 110 w2 := $1000225110
8953:1000066632: 032 304 010 001 052 110 205 w move $1000225110,r.24
... (49 sites, every one a w2:= / w move / w1:= / w4:=, i.e. read-by-value)
$1000225070 = DSEG 0x12A38 = the constant integer 1. Its 19 references are all value
loads (w3 :=, w move, w2 :=).
Neither ever appears as call $1777777777777000000377,<argc>,$1000225070,... or
...,$1000225110,.... They are entries in the compiler-generated constant pool at
0x12A20-0x12A4C documented in SINTRAN/ND500/swapper/swapper-k01.dseg.md section 5.6, in the "read by value"
sub-pool (Group B). They are the small integers the swapper's arithmetic uses; they are not
service selectors.
Why the brief's usage counts still line up. The brief's list
(225014(8) 225020(7) ... 225110(49) 225070(19) ...) is a raw scan of every absolute operand
in the octal range 0o225014..0o225130 = DSEG 0x12A0C..0x12A58. That range happens to contain
BOTH the six selector constants that MON 377B passes by address AND the run-time scalars and
by-value constants that surround them. The scan conflated them. The corrected split is in
section 3.
2. The gate mechanism, proven from bytes¶
2.1 Two trap targets, verified¶
Grep of every call $1777777777777... (sign-extended segment-31 target) in the 12046-line
listing returns exactly two distinct targets:
| Octal target | = hex | Segment / offset | MON | Count | Site(s) |
|---|---|---|---|---|---|
1777777777777000000000 |
0xF8000000 | seg 31, offset 0 | MON 0B (LEAVE) | 1 | line 40 = 0o1000000131 |
1777777777777000000377 |
0xF80000FF | seg 31, offset 0o377 = 255 | MON 377B | 15 | see 2.3 |
0xF8000000 is ND-500 logical segment 31 (the escape/trap segment); the low byte is the
monitor-call offset. 0x00 = 0 -> LEAVE/terminate; 0xFF = 0o377 = 255 -> the SINTRAN
service gate. PROVEN from the operand bytes 303 370 000 000 000/303 370 000 000 377
at those lines.
2.2 What monitor call 0o377 (=255) is¶
Offset 0o377 into segment 31 is monitor-call number 255 decimal. Per the monitor-call
reference SINTRAN/ND500/ND500-MONITOR-CALL-PARAMETER-PASSING.md
(section 3.4, derived from the SINTRAN kernel source MP-P2-N500.NPL, NOT from the swapper
bytes):
| Mon Call | Octal | Symbol | Handler | Purpose |
| 255 | 377 | N5SWAP | SWPDECODER | Swapper internal |
So the swapper's 15 service traps are all N5SWAP (MON 255 = 0o377), decoded on the far
side by SWPDECODER. This identity is corroborated inference, not proven from the two
swapper files: what the swapper bytes prove is the numeric target (offset 0o377); the name
N5SWAP/SWPDECODER comes from the kernel-source-derived reference. Experiment to make it
PROVEN from first principles: single-step the ND-500 at line 103 and observe the STOP-REASON /
MCNO the ND-100 sees (should read MCNO = 0o377).
2.3 The 15 MON 377B call sites, with arguments (PROVEN, byte-exact)¶
Each site is call $0xF80000FF, <argc>, <selector-ptr>, <params...>. The selector pointer is
always the ADDRESS of a small constant (ND-500 high-level languages pass arguments by
reference), so the VALUE at that address is the request sub-function code. Reading the operand
bytes directly:
| # | Listing line | I-addr (oct) | argc | selector ptr (oct) | selector DSEG off | value | remaining params |
|---|---|---|---|---|---|---|---|
| 1 | 103 | 1000000507 | 2 | 1000225064 | 0x12A34 | 6 | 1000436574 = 0x23D7C |
| 2 | 167 | 1000001131 | 2 | 1000225064 | 0x12A34 | 6 | 1000436574 = 0x23D7C |
| 3 | 520 | 1000003167 | 2 | 1000225040 | 0x12A20 | 0o2047 SWPFA | 1000437234 = 0x23E9C |
| 4 | 1359 | 1000010131 | 7 | 1000225044 | 0x12A24 | 2 | b.70,@b.134,b.64,1000246370=0x14CF8,b.44,b.50 |
| 5 | 1987 | 1000013600 | 7 | 1000225044 | 0x12A24 | 2 | @b.174,@b.-200,b.120,0x14CF8,b.100,b.74 |
| 6 | 2109 | 1000014466 | 7 | 1000225044 | 0x12A24 | 2 | @b.-650,@b.-654,b.120,0x14CF8,b.100,b.74 |
| 7 | 2433 | 1000016502 | 7 | 1000225044 | 0x12A24 | 2 | b.70,@b.124,b.64,b.40,b.50,b.44 |
| 8 | 2732 | 1000020237 | 7 | 1000225044 | 0x12A24 | 2 | b.70,@b.140,b.64,b.40,b.44,b.34 |
| 9 | 3089 | 1000022231 | 6 | 1000225054 | 0x12A2C | 4 | @b.-670,1000440264=0x240B4,b.30,b.50,b.54 |
| 10 | 3325 | 1000023534 | 7 | 1000225044 | 0x12A24 | 2 | b.70,@b.124,@b.130,b.64,@b.134,@b.140 |
| 11 | 5252 | 1000037154 | 7 | 1000225044 | 0x12A24 | 2 | b.1020,@b.1100,@b.1104,b.364,@b.1110,b.370 |
| 12 | 5577 | 1000041466 | 7 | 1000225044 | 0x12A24 | 2 | (disassembler lost column sync; arg1 bytes 052 044 = 0x12A24 PROVEN) |
| 13 | 5959 | 1000044151 | 2 | 1000225064 | 0x12A34 | 6 | 1000436600 = 0x23D80 |
| 14 | 8372 | 1000062726 | 3 | 1000225060 | 0x12A30 | 5 | b.44,1000440264=0x240B4 |
| 15 | 10535 | 1000101077 | 4 | 1000225050 | 0x12A28 | 1 | 1000440260=0x240B0,1000440264=0x240B4,b.24 |
Octal->hex spot checks (Python-verified):
0o1000225064 = 0x08012A34; 0o1000225044 = 0x08012A24; 0o1000225040 = 0x08012A20;
0o1000246370 = 0x08014CF8; 0o1000440264 = 0x080240B4; 0o1000440260 = 0x080240B0;
0o1000436574 = 0x08023D7C; 0o1000437234 = 0x08023E9C.
3. Corrected descriptor decode table (the real Task 1 answer)¶
There is no per-service descriptor block in this domain. The "descriptor" of a MON 377B
is simply the address of a one-word sub-function code. Six such constants are used as
selectors; the remaining $1000225xxx operands the brief listed are ordinary data. Full
decode of all seventeen operands in the 0x12A0C..0x12A58 window:
3.1 Actual MON 377B selectors (6 of them)¶
| Selector ptr (oct) | DSEG off | value at addr | # MON 377B sites | argc | site lines | inferred purpose |
|---|---|---|---|---|---|---|
| 1000225040 | 0x12A20 | 0o2047 = SWPFA (SWPFATAL) | 1 | 2 | 520 | fatal-error report; arg2 = fatal slot 0x23E9C |
| 1000225044 | 0x12A24 | 2 | 8 | 7 | 1359,1987,2109,2433,2732,3325,5252,5577 | dominant request (7 params, fixed buffer 0x14CF8 on some) |
| 1000225050 | 0x12A28 | 1 | 1 | 4 | 10535 | passes msg-control 0x240B0 + 0x240B4 |
| 1000225054 | 0x12A2C | 4 | 1 | 6 | 3089 | passes msg pointer 0x240B4 |
| 1000225060 | 0x12A30 | 5 | 1 | 3 | 8372 | passes msg pointer 0x240B4 |
| 1000225064 | 0x12A34 | 6 | 3 | 2 | 103,167,5959 | passes flag word 0x23D7C / 0x23D80 |
Totals: 1+8+1+1+1+3 = 15 sites, matching the 15 MON 377B traps exactly.
SWPFA = 0o2047 is PROVEN as the SWPFATAL error code: in
SINTRAN/ND500/swapper/N500-SYMBOLS.SYMB the value 002047 resolves
uniquely to SWPFA (5-char truncation of SWPFATAL), inside a coherent swapper error band
(SWADE=002040 ... NOMAS=002046 SWPFA=002047 MEMNA=002050 MICFA=002051). Its single use is the
fatal path at line 520 with arg2 = 0x23E9C (the error slot written by w1 =: $1000437234 at
line 497).
The sub-function VALUES 1, 2, 4, 5, 6 are UNKNOWN by name. They are the parameters
SWPDECODER decodes on the ND-100 side; nothing in the two swapper files names them, and no
symbol of magnitude 1..6 can be trusted (2628 distinct values over 7157 symbols). What is
established from the bytes is only their shape: function 2 is the heavy one (8 sites, 7
parameters, a fixed 0x14CF8 parameter buffer), functions 1/4/5 carry the message-control block
words (0x240B0/0x240B4), and function 6 carries a small flag word. Experiment to name them:
run the ND-100 side (5P-P2-MON60.NPL / the SWMC and N5SWAP paths) while the swapper
issues each and record which SINTRAN entry each value reaches.
3.2 The $1000225xxx operands that are NOT selectors (the brief's red herrings)¶
| Operand (oct) | DSEG off | value / role | how used | NOT a selector because |
|---|---|---|---|---|
| 1000225014 | 0x12A0C | 65536 seed, run-time -> 4096 divisor | w1 /, w move, w4:= |
value load; divisor (dseg.md 5.5) |
| 1000225020 | 0x12A10 | halfword counter variable | h incr/h decr |
run-time variable |
| 1000225022 | 0x12A12 | halfword variable | h decr |
run-time variable |
| 1000225030 | 0x12A18 | word variable | w move, w1 =: |
run-time variable |
| 1000225034 | 0x12A1C | word variable | w move, w1 =: |
run-time variable |
| 1000225070 | 0x12A38 | constant 1 | w3:=,w move,w2:= (19x) |
read by value; never a MON 377B arg |
| 1000225074 | 0x12A3C | constant 4 | w2:=,w move (6x) |
read by value |
| 1000225104 | 0x12A44 | constant 5 | w2:=,w move (10x) |
read by value |
| 1000225110 | 0x12A48 | constant 6 | w2:=,w move (49x) |
read by value; never a MON 377B arg |
| 1000225120 | 0x12A50 | record base | r:= (record base, 8x) |
base register, not an arg |
| 1000225130 | 0x12A58 | word variable | value/store | run-time variable |
So the answer to "identify 225110 and 225070": 225110 is the integer constant 6 and 225070 is the integer constant 1, both read by value; neither selects a SINTRAN service. The dominant actual service is MON 377B sub-function 2 (8 sites, 7 parameters).
4. How the swapper RECEIVES work: the interface, proven from bytes¶
The swapper is message-driven. Three mechanisms are visible in the listing.
4.1 It DMA-reads ND-100 private memory (RIOM x3) - PROVEN¶
RIOM = Read I/O-Processor Memory: a supervisor DMA that copies from ND-100 memory into
ND-500 memory across the interface, in halfword units (H RIOM <nd100-addr>,<buffer>,<count>).
Three sites:
8008 : 1000060340 : h riom b.34,b.30,$1000437240 ; count = [0x23EA0] = 2
8459 : 1000063341 : h riom b.50,b.26,$1000437774 ; count = [0x23FFC] = 10
10577: 1000101356 : h riom $1000440264,$1000440274,$1000440074+ ; the message pull
The third RIOM is the message-intake path and ties the message-control block together:
| Address | oct operand | Role | Evidence |
|---|---|---|---|
| 0x080240B4 | 1000440264 | ND-100 source address of the message | h riom operand 1 |
| 0x080240BC | 1000440274 | ND-500 message buffer base | h riom operand 2; r:= $1000440274 x15 (record base) |
| 0x0802403C+ | 1000440074+ | halfword count, indexed per command (29-entry table) | h riom operand 3 |
| 0x080240B8 | 1000440270 | message function code | w1 := $1000440270; jumpg $1000460630+ (lines 10599-10600) |
CORRECTION 2026-07-25 - this row previously read
0x0802408C+. That was an arithmetic slip:0o1000440074=0x0802403C, not0x0802408C. Verify:0x2403C= 147516 decimal =0o440074. The wrong address was inherited by a diagnostic run, which read a count from the wrong table and reported "8 halfwords". Measured against the CORRECT base, the table (32-bit entries) reads[0]=13 [1]=10 [2]=15 [3]=138 [4]=9 [5]=70 ..., and message function code 5 selects 15 halfwords. Observed live inRealSwapper_ServiceMon377BAnnounce_HowFarThen(RetroCoreCPU.ND5000tests).
So the swapper reads its request out of ND-100 private memory by DMA, into its own D-space
buffer at 0x240BC, and reads the function code at 0x240B8. This corrects the older prior-art
claim ("no direct interface access on the ND-500 side") - the swapper does reach ND-100 memory,
but only via the sanctioned RIOM supervisor DMA, not by touching interface registers (which
remain ND-100 I/O space).
4.2 It dispatches on a private 29-entry function-code table - PROVEN¶
10570: w comp2 $1000440270,$34 ; [0x240B8] fn code <= 0o34 (=28) - the REAL dispatch bound
10599: w1 := $1000440270 ; index = message function code [0x240B8]
10600: jumpg $1000460630+ ; jump via table @ 0x08026198
0o1000460630 = 0x08026198 (PROVEN). The table at 0x26198 is exactly 29 words of PSEG code
pointers, all in the compact block 0x0800836C..0x080086E2 (full list in SINTRAN/ND500/swapper/swapper-k01.dseg.md
section 5.12). Three independent structures agree the command count is 29: the handler table
(29 entries), the array descriptor at 0x28F54 (max_index = 0x1C = 28 -> 29 entries), and the
per-command count table at 0x2403C (29 entries). The index range is 0..28 - a small
swap-command namespace, not the MON-call number space. This is the swapper's own protocol.
Routing + handler carves (2026-07-16) - where the function code comes from, and what the 29 handlers do. Two follow-on carves settled the open ends of this dispatch: - The function code carrier is
SWPST, notSWPFU(the earlier guess is REFUTED). On the ND-100 side,5ACTSWAPPERstores the swap activation reason (anMSW*code) into the swapper message fieldSWPST(SWMSG offset0o103); the swapper receives it as the OUT parameter ofMON 377Bsub-function 1, lands it at DSEG0x240B0, and copies it to0x240B8(the jumpg index).SWPFUis the reverse-direction swapper->ND-100 request code. Full field layout + evidence chain:SINTRAN/ND500/ND500-SWAPPER-ANALYSIS.mdsection 12. - The dispatch index range is0..0o34(0..28), not 0..20 - the real bound is[0x240B8] <= 0o34(line 10570); the$24=20 compare cited earlier is line 10598 on a different variable. All 29 handlers are reachable. TheMSW*codes spanMSWFI=0 .. MSWDO=0o34= 29 codes, matching the table. - The 29 handler bodies are carved (27 of 29 characterized PROVEN-shape): the full index->target->behaviour table is inSINTRAN/ND500/swapper/swapper-k01-handlers.md.
4.3 It does the page moves itself - PROVEN¶
The physical page work is done in-domain with ND-500 supervisor instructions, not delegated:
| Instruction | Opcode bytes | Count | Sites | Meaning (disassembler mnemonic) |
|---|---|---|---|---|
RPHS |
377 365 | 2 | 1389, 1436 | Read from PHysical Segment (swap-in page read); operand = local -60 |
PCTSB |
377 034 | 3 | 95, 115, 157 | page/cache-table supervisor primitive |
DCTSB |
377 035 | 4 | 88, 108, 150, 411 | data/cache-table supervisor primitive |
WPHS |
- | 0 | - | absent |
Note WPHS (write physical segment) does NOT occur - a correction to the prior-art claim
"RPHS/WPHS for page moves" (SWAPPER-MON-DISPATCH.md section 3). Only RPHS is present.
CORRECTION (2026-07-20): "via RIOM's companion" is DEAD - there is no companion. An earlier
revision of this paragraph offered three possibilities for the write-back path; the second was a
supposed write-side partner to RIOM ("WIOM"). No such instruction exists - it appears in no
ND-500 manual, no manual index, and none of the three opcode tables; the "RIOM/WIOM pair" was
invented prose that had also propagated into the emulator's source comments (since deleted).
Of the three options only "handled on the ND-100 side" survives, and the mechanism is now
understood: the ND-500 does not need a write instruction at all, because
1. ordinary stores into the SHARED segment are the normal path (ND500_MMU_SPECIFICATION.md
defines DC_SHSEG 6 "Shared segment (ND-100 <-> ND-500)", mapped SG_RW | DC_SHS - read/write,
caching disabled because a second master touches the same cells);
2. the CPU microprogram writes mailbox answers directly ("The CPU microprogram initiates and
controls the DMA access channel to the I/O-processor memory"), which is why
ND500-WHO-ANSWERS-THE-MAILBOX.md concludes the answerer is THE MICROCODE; and
3. the ND-100 can reach ND-500 memory itself as the master I/O processor - which is what the
swapper relies on when it traps outward via MON 377B (15 sites), so this route collapses into 2.
RIOM is therefore not half of a pair: it is the escape hatch for the one direction nothing else
covers - reading ND-100 memory the ND-500 cannot address ("usually private ND-100 memory, not
directly addressable by the ND-500"). Writing never needed an instruction.
The
PCTSB/DCTSB mnemonic names are as the disassembler emits them; their precise semantics
are not established from these files (INFERRED to be page/data control-table maintenance).
5. The request/response loop (Task 2)¶
5.1 Startup / self-check (PROVEN)¶
line 4 : 1000000004 : init $1000441124,$44,$17504 ; frame/stack: bottom=0x24254, total=8004
line 19 : 1000000016 : w comp2 $1000224030,$12221253056 ; [0x12818] vs ASCII 'REV.'
line 23 : 1000000027 : w comp2 $1000224034,$5522630061 ; [0x1281C] vs ASCII '-K01'
line 40 : 1000000131 : call $0xF8000000,$0 ; MON 0B LEAVE on mismatch
The domain first verifies its own build tag REV.-K01 against two literal immediates and
LEAVEs if wrong (build-consistency guard). INIT establishes the run-time stack at DSEG
0x24254..0x26197 (8004 bytes; proven by 0x26198 - 0x24254 = 0x1F44 = 8004 = total_demand).
5.2 The MON 377B site is trace-gated, not "try-local-else-forward" (PROVEN, correction)¶
Every MON 377B may be preceded by a call to one of five near-identical trace routines
(PSEG 0x9187/0x9208/0x9272/0x92E8/0x9381). That call is conditional on the trace-enable flag
0x28F64 and the MON 377B runs either way:
line 99 : 1000000456 : w test $1000507544 ; [0x08028F64] trace enabled?
line 100 : 1000000464 : if = go $23 ; 0o464 + 0o23 = 0o507 -> jump PAST the trace call
line 101 : 1000000466 : call $1000111601,$2,$1000225064,$1000436574 ; trace routine (same args)
line 102 : 1000000506 : ifkret
line 103 : 1000000507 : call $0xF80000FF,$2,$1000225064,$1000436574 ; MON 377B (runs regardless)
Branch arithmetic verified: 0o1000000464 + 0o23 = 0o1000000507 = the MON 377B. So the "local
wrapper with identical args" seen in the pseg.md is the trace/logging path (it writes the
1024x32-byte ring at 0x28F8C), not a local fast-path substitute for the trap. This corrects the
"try internally, else trap" reading.
5.3 Core loop, pseudo-C (reconstructed; structure PROVEN, semantics of codes INFERRED)¶
/* SWAPPER-K01 - ND-500 process #0 server. All addresses are DSEG offsets. */
void swapper_main(void)
{
if (memcmp(&dseg[0x12818], "REV.-K01", 8) != 0) /* line 19/23 */
MON_LEAVE(); /* MON 0B, line 40 */
for (;;) {
/* 1. INTAKE: pull the request message out of ND-100 private memory by DMA */
h_riom(/*nd100*/ dseg_w[0x240B4], /* line 10577 */
/*buf*/ dseg_w[0x240BC],
/*count*/ count_table[0x2408C + idx]);
/* 2. DISPATCH on the swapper's own function code (0..28) */
uint32_t fn = dseg_w[0x240B8]; /* line 10599 */
if (dseg_w[0x240B8] > 034) error(); /* bound check, line 10570 (034 = 28) */
goto *handler_table_0x26198[fn]; /* jumpg, line 10600 */
/* 3. Each handler does page work IN-DOMAIN ... */
// rphs(local_-60); /* swap-in page read, lines 1389/1436 */
// pctsb(); dctsb(); /* cache/table primitives */
// ... and updates the DSEG tables (2560/255/29-entry arrays) ...
/* 4. ... and, when it needs SINTRAN, traps the ND-100 (client): */
if (need_swap_disk_or_status) {
if (dseg_w[0x28F64]) trace_log(fn_code, params); /* trace-gated */
mon_255_n5swap(/*argc*/ 7, /*subfn*/ &TWO, /* MON 377B, sub-fn 2 */
p2, p3, p4, &dseg[0x14CF8], p6, p7);
}
if (fatal)
mon_255_n5swap(2, &SWPFA /*0o2047*/, &dseg[0x23E9C]); /* line 520 */
/* 5. RESPONSE: results left in swapper D-space; the ND-100 reads them back
via MON 60 subfunction 121B (RDSWP). (See section 7.) */
}
}
The five sub-functions and their argument shapes are byte-proven (section 3.1); the loop's outer structure (intake by RIOM -> function-code jumpg -> in-domain paging -> MON 377B on demand) is proven by the cited lines. The names of sub-functions 1/2/4/5/6 and the internal logic of the 29 handlers are INFERRED/UNKNOWN.
6. Role determination, with byte evidence (Task 3 - the headline)¶
Determination: SWAPPER-K01 is an ND-500-side paging/swap worker DOMAIN that is a CLIENT of SINTRAN. It is NOT "the low-level handler that does all ND-500 work for the ND-100". The three specific sub-claims resolve as follows:
(a) It is process #0's server side - CONFIRMED (consistent with prior art; not contradicted
by any byte here). The ND-500 swap side is process #0 (S500S/5SWPROC), served by the
ND-100 RT-program 5SWAP (SINTRAN/ND500/ND500-SWAPPER-ANALYSIS.md
header). The swapper domain's message-driven structure (RIOM intake + 29-entry function-code
dispatch, section 4) is exactly the shape of a per-process server; it carries a 254 processes
build string (0x23E86) and per-process/segment arrays (2560, 255, 29 entries). This part is
corroborated, not independently proven from I/O bytes.
(b) It requests services FROM the ND-100 (client), it does not field the ND-100's low-level
requests - PROVEN. Direction of every trap is outward: 15x MON 377B are call instructions
the swapper executes (section 2.3), targeting SINTRAN. There is no receive-side MON
dispatcher: grep finds no MON-number entry vector, no trap prologue keyed to a MON number,
and the only inbound dispatch is the swapper's own 29-entry function-code table (0..28), a
different and smaller namespace than the MON-call space. The swapper's inbound work arrives as
messages (RIOM), not as trapped MON calls. Client, not server-of-MON-calls.
(c) The low-level bus/interrupt/DMA work lives in SINTRAN's level-12 driver + the ND-500
microcode, and swap-disk I/O in the ND-100 RT-program 5SWAP - CONFIRMED by absence + prior
art. The swapper contains zero interface-register access: it cannot, as an ND-500 domain,
touch the 3022/5015 interface registers (ND-100 I/O space). Its only cross-boundary primitives
are the sanctioned supervisor ops RIOM (DMA read of ND-100 memory) and the MON 377B trap.
It has no disk driver, no ABSTR/ABSLI, no channel code. The disk transfer is what it asks
for via MON 377B sub-function 2 (the dominant, 7-parameter request) - the ND-100/5SWAP side
turns that into an actual disk ABSTR. This matches the kernel-source picture in
MP-P2-N500.NPL (level-12 GOSW/SWMC) and RP-P2-N500.NPL (5SWAP), cited from prior art.
Net: the swapper is the brain of ND-500 paging (it decides which pages move, maintains
the page/segment tables in-domain, and moves pages with RPHS/PCTSB/DCTSB), but it is the
client for everything that crosses the machine boundary - it hands disk-transfer and
error-report requests to the ND-100 and lets SINTRAN + 5SWAP + the microcode do the physical
I/O. It is emphatically not a universal ND-500 MON-call first-responder (that is the resident
ND-500 System Monitor, per SWAPPER-MON-DISPATCH.md, unchanged by this pass).
7. Mapping to the ND-100 front door and the 5MPM handshake (Task 4)¶
Two directions cross the ND-100 <-> ND-500 boundary. They are distinct and must not be conflated.
7.1 Direction A: ND-100 -> swapper (the front door, MON 60 / N500M)¶
An ND-100 program controls the ND-500 through MON 60 (client name N500M). The subfunction
codes relevant to the swapper (from
SINTRAN/ND500/nd-500-mon/mon60-callers/SUBFUNCTION-TABLE.md,
authoritative from 5P-P2-MON60.NPL):
| MON 60 subfn (oct) | 5IFUNC handler | Purpose | Carved folder |
|---|---|---|---|
| 007B | IPLSWAPPER | PLACE SWAPPER (load this domain) | (no dedicated folder; see SINTRAN/ND500/nd-500-mon/mon60-callers/LOAD-SWAPPER/) |
| 054B | 5NOPAR | start swapper | SINTRAN/ND500/nd-500-mon/mon60-callers/START-SWAPPER/ |
| 076B | ITOSWP | MESSAGE TO SWAPPER | SINTRAN/ND500/nd-500-mon/mon60-callers/076B-TOSWP/ |
| 121B | 5NOPAR | READ FROM SWAPPERS DATA MEMORY (LOGICAL ADDRS) | SINTRAN/ND500/nd-500-mon/mon60-callers/121B-RDSWP/ |
| 046B / 047B | IDEFSWAP / IDELSWAP | define / delete swap file | SINTRAN/ND500/nd-500-mon/mon60-callers/046B-DEFSWAP/, SINTRAN/ND500/nd-500-mon/mon60-callers/047B-DELSW/ |
Note: the brief wrote "7B PLACE SWAPPER" and "54B start swapper" - confirmed as octal 007B and
054B. It also wrote "76B TOSWP" and "121B RDSWP" - confirmed. There is no 007B-named folder,
but the SINTRAN/ND500/nd-500-mon/mon60-callers/LOAD-SWAPPER/ operator-command folder exercises PLACE SWAPPER; SINTRAN/ND500/nd-500-mon/mon60-callers/START-SWAPPER/
exercises the start path.
076B-TOSWP (MESSAGE TO SWAPPER) is the intake counterpart to the swapper's RIOM in section
4.1: the ND-100 caller builds a message block in its frame, stores a pointer to it in MON 60
gateway param slot 6, and issues MON 60 TOSWP (thunk 146610, 5 call sites - all PROVEN in
SINTRAN/ND500/nd-500-mon/mon60-callers/076B-TOSWP/README.md). That message is what the swapper later pulls across by DMA and
dispatches on its function code.
121B-RDSWP (READ FROM SWAPPER'S DATA MEMORY) is the response counterpart: the ND-100 fills
four MON 60 param slots (6,7,10,11) and issues MON 60 RDSWP (thunk 146665, 3 call sites,
PROVEN in SINTRAN/ND500/nd-500-mon/mon60-callers/121B-RDSWP/README.md) to read results back out of the swapper's D-space by logical
address. This is how "answers come back" at the operator/utility level.
7.2 Direction B: swapper -> ND-100 (MON 377B = N5SWAP)¶
This is the direction analysed in sections 2-3: the swapper traps outward with monitor call
255 (0o377 = N5SWAP), sub-function in the first parameter, decoded by SWPDECODER on the ND-100
side. This is the swapper acting as a SINTRAN client (disk transfer, fatal-error reporting).
7.3 Where 5MPM sits (INFERRED from prior art; not in the swapper bytes)¶
Per the brief's verified context and the NPL front-door source, the ND-100 MON 60/N500M common
path 5NOPAR ends in FPT2ENTRY ("ENTER ND-500 SYSTEM MONITOR"), which builds a message in
the 5MPM shared-memory region and drives the ND-500. The swapper sits on the ND-500 side of
that handshake: 5MPM (and the swapper's own mailbox in ND-100 memory) is what 076B-TOSWP
writes and what the swapper's third RIOM reads. The 5MPM buffer itself is not present in
these two swapper files (its bytes live in ND-100 memory / the kernel image), so its layout is
UNKNOWN from here; the linkage is asserted from 5P-P2-MON60.NPL and the prior-art bus
reference, and is INFERRED, not proven by the swapper bytes.
8. Diagrams¶
8.1 System role - who does what across the boundary¶
flowchart LR
subgraph ND100 ["ND-100 side (SINTRAN III)"]
APP["ND-100 program / utility"]:::input
MON60["MON 60 / N500M front door<br/>5IFUNC dispatch -> 5NOPAR -> FPT2ENTRY"]:::teal
MPM["5MPM shared-memory message<br/>+ swapper mailbox in ND-100 memory"]:::green
SWAP5["RT-program 5SWAP (5SWRT)<br/>swap-disk I/O via ABSTR"]:::orange
DRV["level-12 ND-500 driver<br/>GOSW / SWMC / SWPDECODER"]:::purple
end
subgraph ND500 ["ND-500 side"]
MICRO["ND-500 microcode<br/>MON trap + interface DMA"]:::darkteal
SWK["SWAPPER-K01 domain (process #0)<br/>RIOM intake, 29-way dispatch,<br/>RPHS/PCTSB/DCTSB paging"]:::blue
end
APP -->|"MON 60: 007B PLACE, 054B START,<br/>076B TOSWP, 121B RDSWP"| MON60
MON60 --> MPM
MPM -->|message| SWK
SWK -->|"RIOM DMA read of ND-100 memory"| MPM
SWK -->|"MON 377B = MON 255 N5SWAP<br/>(client request: disk / fatal)"| MICRO
MICRO --> DRV
DRV -->|swap-disk request| SWAP5
SWAP5 -->|completion| DRV
MON60 -->|"121B RDSWP: read results"| SWK
classDef input fill:#E3F2FD,stroke:#0D47A1,color:#0D47A1;
classDef blue fill:#E3F2FD,stroke:#0D47A1,color:#0D47A1;
classDef teal fill:#E0F7FA,stroke:#00838F,color:#00838F;
classDef green fill:#E8F5E9,stroke:#2E7D32,color:#2E7D32;
classDef purple fill:#F3E5F5,stroke:#7B1FA2,color:#7B1FA2;
classDef orange fill:#FFF3E0,stroke:#E65100,color:#E65100;
classDef darkteal fill:#E0F2F1,stroke:#00695C,color:#00695C;
8.2 MON 377B end-to-end (the client trap)¶
sequenceDiagram
participant H as Swapper handler (PSEG)
participant T as Trace routine (0x9187..0x9381)
participant G as Seg-31 gate 0xF80000FF (offset 0o377 = MON 255)
participant D as ND-100 SWPDECODER (N5SWAP)
participant K as SINTRAN / 5SWAP
Note over H: needs swap-disk I/O or must report a fatal error
opt trace enabled: [0x28F64] != 0
H->>T: call trace(subfn, params) (same arg list)
T->>T: write 32-byte entry into ring @ 0x28F8C (index mod 1024)
end
H->>G: call 0xF80000FF, argc, &subfn{1,2,4,5,6 | SWPFA}, params...
G->>D: microcode trap, MCNO = 0o377, params in 5AP1..5APn
D->>K: decode subfn, perform swap-disk transfer / log fatal
K-->>D: result
D-->>H: skip return, write-back per NUMPA mask
8.3 Intake and dispatch (how work arrives)¶
flowchart TD
M["ND-100 posts message<br/>(MON 60 076B TOSWP -> mailbox)"]:::green
R["h riom [0x240B4] -> buffer [0x240BC]<br/>count from 29-entry table @ 0x2408C"]:::darkteal
F["read function code [0x240B8]"]:::teal
B{"[0x240B8] > 0o34 ?"}:::orange
J["jumpg table @ 0x26198<br/>(29 handlers, PSEG 0x836C..0x86E2)"]:::purple
W["handler: RPHS / PCTSB / DCTSB<br/>update 2560/255/29-entry DSEG arrays"]:::blue
C["need SINTRAN? -> MON 377B (sect 8.2)"]:::blue
M --> R --> F --> B
B -->|yes| E["error"]:::orange
B -->|no| J --> W --> C
classDef green fill:#E8F5E9,stroke:#2E7D32,color:#2E7D32;
classDef darkteal fill:#E0F2F1,stroke:#00695C,color:#00695C;
classDef teal fill:#E0F7FA,stroke:#00838F,color:#00838F;
classDef orange fill:#FFF3E0,stroke:#E65100,color:#E65100;
classDef purple fill:#F3E5F5,stroke:#7B1FA2,color:#7B1FA2;
classDef blue fill:#E3F2FD,stroke:#0D47A1,color:#0D47A1;
9. Named routines (Task 5)¶
SINTRAN/ND500/swapper/N500-SYMBOLS.SYMB stores symbols as 5-char names = octal values, not I-space addresses, so
it does not directly index this domain's PSEG addresses. The only clean value matches useful
here are the error/status constants the swapper loads:
| Constant used by swapper | Value (oct) | Symbol (PROVEN unique) |
|---|---|---|
| MON 377B fatal selector @ 0x12A20 | 002047 | SWPFA (SWPFATAL) |
The five trace routines and the 29 command handlers are named only by address (no symbol resolves to their I-space addresses):
| Address (hex) | Address (oct) | Role (PROVEN) |
|---|---|---|
| 0x08009187 | 1000110607 | trace routine paired with sub-fn 2 |
| 0x08009208 | 1000111010 | trace routine paired with sub-fn 1 |
| 0x08009272 | 1000111162 | trace routine paired with sub-fn 4 |
| 0x080092E8 | 1000111350 | trace routine paired with sub-fn 5 |
| 0x08009381 | 1000111601 | trace routine paired with sub-fn 6 |
| 0x0800836C..0x080086E2 | 1000206... | 29 command handlers (dispatch table @ 0x26198) |
To name the handlers would require the ND-500 System Monitor symbol map cross-referenced by
I-space address (not available in SINTRAN/ND500/swapper/N500-SYMBOLS.SYMB's value-keyed form) or a live trace.
10. Corrections this pass makes to prior art¶
| # | Prior claim | Source | Byte evidence | Correction |
|---|---|---|---|---|
| 1 | "225110 (49x) and 225070 (19x) are the dominant SINTRAN services" | task brief | grep: both are only w2:=/w move value loads; never MON 377B args |
They are the constants 6 and 1, read by value - not selectors. Dominant service is sub-fn 2 (8 sites). |
| 2 | Each $1000225xxx is a MON 377B descriptor |
task brief / pseg.md 5.2 | only 6 of 17 appear as MON 377B arg1 | Only 225040/044/050/054/060/064 are selectors; the rest are data. |
| 3 | "RPHS/WPHS for page moves" | SINTRAN/ND500/old/SWAPPER-MON-DISPATCH.md sect 3 |
grep: rphs x2, wphs x0 |
WPHS is absent. Only RPHS present. Write-back path UNKNOWN from these bytes. |
| 4 | "No IOX/interface access on the ND-500 side" | SINTRAN/ND500/old/SWAPPER-K01-ANALYSIS.md |
h riom x3 (lines 8008/8459/10577) |
Swapper DMA-reads ND-100 memory via RIOM (sanctioned supervisor DMA, not interface registers). |
| 5 | "try internally, else trap to ND-100" | pseg.md 5.1 | trace-enable flag [0x28F64] gates the internal call; MON 377B runs regardless |
The internal call is a trace/log routine, not a local fast-path. |
Confirmed unchanged: SWAPPER-K01 is a client/worker domain, not the universal ND-500 MON
dispatcher (SINTRAN/ND500/old/SWAPPER-MON-DISPATCH.md conclusion stands); SWPFA=0o2047; the 29-entry
function-code dispatch; the trace ring at 0x28F8C.
11. Open items / UNKNOWN (with the experiment that settles each)¶
- Names of MON 377B sub-functions 1/2/4/5/6. Byte shape known; names not. Experiment:
run the ND-100
SWPDECODER/SWMCpath while the swapper issues each value; record the SINTRAN entry reached. - N5SWAP identity of offset 0o377. Proven numerically (offset 255); the name N5SWAP comes from kernel-source-derived prior art. Experiment: single-step line 103 and read the MCNO the ND-100 receives.
- Physical write-back (no WPHS). How swapped-out pages are written to physical segment is not visible. Experiment: trace a swap-out and watch for the write primitive / RIOM companion / ND-100-side write.
- The 29 command handlers' semantics. Only addresses and the dispatch mechanism are proven. Experiment: trace each function code 0..28 through its handler.
- 5MPM layout and the exact mailbox address. Lives in ND-100 memory; not in these files.
Experiment: dump 5MPM from a running SINTRAN and correlate with
[0x240B4]at intake. - Function-code 6's flag words (0x23D7C, 0x23D80=32). Passed to MON 377B sub-fn 6; meaning UNKNOWN.
12. File written¶
SINTRAN/ND500/swapper/swapper-k01-deep-analysis.md
(this document). It extends, and does not overwrite, SINTRAN/ND500/swapper/swapper-k01.pseg.md and
SINTRAN/ND500/swapper/swapper-k01.dseg.md.