Tier-2 MON-call oracle for NC — 123B RELES, 54B MDLFI, 503B DVINST¶
Resolving three SINTRAN III VSX/500 L07 MON-call divergences between two emulators, using carved L07 bytes (and live-recovered L07 code) as the tie-breaker. All addresses and values are octal unless a decimal is named. Every claim below is tagged VERIFIED (byte-proven or live-captured on real SINTRAN L07) or INFERRED (structure/semantics imply it).
Source layers used:
- Carved segment listings: /mnt/d/ND/t/re/segments-ref/<seg>/<seg>.asm + .symbols.txt
- Resident image: /mnt/d/ND/t/resident/SINTRAN-DATA_commoncode.bin (base 0)
- Live-recovered reservation code: ../../OS/21-SEMAPHORES-RECOVERED-CODE.md
- NPL L07 source: ../../NPL-SOURCE/NPL/RP-P2-N500.NPL
- Existing ND-500 carve: /mnt/d/ND/t/re/mon-analysis/511B-DVIO/
- Error-code decode: ../../../Reference-Manuals/ND-60.145.7A EN ND FORTRAN Reference Manual.md
(decimal | octal | text) and the Monitor Calls manual appendix A.
Error-code decode (decimal = octal = text), the three that matter: | dec | oct | meaning | |-----|-----|---------| | 5 | 5 | Device not reserved | | 46 | 56 | No such file name | | 124 | 174 | Illegal parameter |
Q1 — 123B RELES (ReleaseResource): release a NEVER-reserved device¶
VERDICT: succeeds silently — no error 5.¶
Releasing a valid device/resource that was never reserved is a designed idempotent no-op; it returns with no error. Error 5 ("Device not reserved") is not raised by RELES on this path — error 5 is raised by the I/O operations that require a reservation, not by the release.
Proof¶
The RELES handler bytes are not present in the static carve: RELES=37156B in
SINTRAN-DATA_commoncode falls inside a 1024-word zeroed hole (37040B..41037B are 000000
in SINTRAN-DATA_commoncode.bin), i.e. the resident monitor image was paged out at carve time.
So RELES cannot be answered from the static segment bytes alone.
It is answered from the live-recovered L07 machine code in
../../OS/21-SEMAPHORES-RECOVERED-CODE.md, captured
with the CPU stopped inside the mapped monitor segment on a running SINTRAN III VSX/500 L system.
The release primitive BRELEASE (BRELE = 010610) decides the "not reserved" case explicitly:
010623 044400 LDA ,B 0 A := DF.RESLI (reserve-link of the datafield)
010624 131073 JAZ *+73 RESLI = 0 -> jump to 010717 (restore registers + EXIT)
...
010717 054270 LDX *-110 restore X
010720 034263 LDF *-115 restore T,A,D
010721 125265 JMP I *-113 return via saved L
- VERIFIED (live L07): when the datafield's
RESLI = 0(the resource is not reserved),010624 JAZbranches straight to the register-restore/return at010717. No error code is loaded; control returns normally. The two error calls in BRELEASE are reached only for inconsistent or third-party-owned datafields, not for the plain not-reserved case:010627 JPL I *+75— reached only ifRESLI != 0andRTRES = 0(chained but ownerless — an internal inconsistency).010633 JPL I *+71— reached only if the datafield is owned by a different RT program (X != owner,X != 0). A never-reserved datafield hasRESLI = 0and never reaches either — it exits at010624.
- The RELES monitor wrapper (
RELES = 037156B, captured but annotation preliminary in chapter 21 §4) loads only file-oriented error codes on its captured branches —037174 SAA 132(=err 90 "No file opened with this number"),037176 SAA 126(=err 86 "Not opened for random read"),037203 SAA 133(=err 91 "Not mass storage file") — i.e. parameter-validation errors for the file-number form of the call. None of them is error 5. The device-release tail (past037217, not in the capture) performs the actual release via the BRELEASE primitive shown above.
Emulator guidance: MON 123 (RELES) on a valid device number that the caller has not
reserved must return success (error code 0) and leave the reservation state untouched.
Emit an error only for a bad/undefined device or file parameter (the 86/90/91 family), never
error 5, and never for the idempotent "already free" case.
Honest caveat: BRELEASE's RESLI=0 -> silent exit is VERIFIED live; the RELES wrapper's
device tail is not in the capture, but the primitive's explicit handling of RESLI=0 as the
normal return path is the design proof that release-of-unreserved is a no-op, not error 5.
Q2 — 54B MDLFI (DeleteFile): empty vs nonexistent name¶
VERDICT: empty/blank name -> error 124 (Illegal parameter); nonexistent (well-formed) name -> error 46 (No such file name). The two cases differ.¶
Proof / structure¶
MDLFI = 106063B in 006-S3FS (load base 26000B) is carved (VERIFIED bytes). It is a
shared entry that selects the file operation with two status bits and then joins the common
file-function body:
106063 174070 BSET ZRO SSM function selector bit SSM := 0
106064 174020 BSET ZRO SSK function selector bit SSK := 0 (00 = delete)
106065 021111 STD I 111 stash the parameter pointer
...
106071 135106 JPL I 106 -> ptr @106177 call the name-parse routine (resident)
...
106114 135066 JPL I 66 -> ptr @106202 call the object/name lookup (resident)
106115 124057 JMP 57 -> 106174 on skip-return (lookup failed): error path
106116 044523 LDA ,B 123 success: continue with function code
...
106174 004402 STA ,B 2 ILDWD: store the returned error code into B+2 (the caller's error word)
- VERIFIED: the not-found error is propagated, not hard-coded, at
106174(STA ,B 2stores whatever error code the lookup routine returned in A into the caller error word). - VERIFIED: both candidate error constants are physically present in
006-S3FS:SAA 56(=170456, error 46 "No such file name") andSAA 174(=170574, error 124 "Illegal parameter") both occur in the segment. - The name-parse (
JPL I 106 -> 106177) and the directory/object lookup (JPL I 66 -> 106202) reach through the segment's indirect pointer pool (106177..106211) into resident routines (e.g. pool word106177 = 003752, a low resident address). That resident code is in the zeroed/uncarved region, so the exact branch that chooses 124 vs 46 is not statically in006-S3FS.
Which code for which case — INFERRED (strong)¶
Grounded in (a) the two error-code meanings, (b) both constants living in the file-system
segment, and (c) SINTRAN's fixed name-handling order:
1. Empty/blank filename is rejected by the name parser (the JPL I 106 step) before
any directory search — a zero-length name is a malformed parameter → error 124 (Illegal
parameter). It never reaches the lookup.
2. Well-formed name that matches no file passes the parser, reaches the object lookup
(JPL I 66), which fails and returns error 46 (No such file name), propagated at 106174.
Emulator guidance: MON 54 (MDLFI) — empty/blank name string → 124; syntactically valid
name with no matching file → 46. (Note "124" here is decimal = 174B; do not confuse
with 124B = decimal 84 "Not opened for sequential read" in the octal-numbered manual appendix.)
Honest caveat: the empty-vs-nonexistent split is INFERRED — the deciding branch lives in the
resident name-parse/lookup, which is not in the static carve. The MDLFI entry stub, the
error-propagation site (106174), and the presence of both error constants in 006-S3FS are
VERIFIED.
Q3 — 503B DVINST (InputString): break strategy + returned byte count¶
VERDICT: the terminating break character IS read, stored, and COUNTED; MaxNo is an inclusive ceiling. The returned count therefore includes the break byte. Of the two emulators, the one returning 14 (break byte counted) matches real SINTRAN; the 12 result under-counts by dropping the terminator (and stopping one read short of the inclusive max).¶
Proof (L07 NPL, corroborated by the 026-S3IMPIT carve)¶
The character-move + break loop for DVINST (SMCNO=503) and DVIO (SMCNO=511) is
IBTBREAK / IIBM in ../../NPL-SOURCE/NPL/RP-P2-N500.NPL:
IIBM: (130246)
130262 CALL IOTRANS; GO FAR TMWT read ONE char (break/echo tables applied by term driver); none -> wait
130264 X:=...5FYLLE; *SBYT store the byte in the ND-500 buffer at offset 5FYLLE
130270 A:=X+1 =: 5FYLLE increment fill count <-- count bumped BEFORE any break test
130273 GO IBTBREAK
IBTBREAK: (130325)
130325 A =: D D := new 5FYLLE
130326 IF X.SMCNO=511 THEN X.11MXBRK ELSE X.MAXBYT max := MAXBYT (DVINST) / 11MXBRK (DVIO)
130335 IF D>=A GO N5RST 5FYLLE >= MAX -> done (INCLUSIVE: the char that hit max is already stored+counted)
130337 IF T:=RSISTE>=0 THEN
130342 IF HENTE=T GO N5RST this char was the break char (matched break table) -> done
ELSE
130346 IF BRECHOFL BIT 5BREAK GO N5RST break flag set by input driver -> done
130351 GO IIBM otherwise read the next char
N5RST: (130353) ... restart user, return the count:
130406 A =: D D = number of chars = 5FYLLE
130407 IF X.SMCNO = 511 THEN A:=0; AD=:X.11NOCRET (DVIO)
130416 ELSE A:=0; AD=:X.NOCHRET (DVINST) <-- returned byte count = 5FYLLE
- VERIFIED (L07 NPL): order is store byte → increment
5FYLLE→ test break/max (130264→130270→130325). The break/terminating character is written into the buffer and the counter is incremented before the loop decides to stop. Hence the returned count (NOCHRET,130416) includes the break character. - VERIFIED (L07 NPL): the max test
130335 IF D>=Ais>=against the post-increment count, soMaxNois an inclusive ceiling (the char reachingMaxNois kept and counted). - Break-char detection happens inside
IOTRANS/the ND-100 terminal input driver using the break strategy + break tables (BreakStrat=8→ user tableBreakT1..BreakT4, 128 bits, bit set = break). The ND-500 side (IBTBREAK) only observes the result viaRSISTE/HENTE(a matched break char) or the5BREAKflag. Terminating chars are exactly those flagged by the break table; a break also occurs on reachingMaxNo. - Echo is independent of break: whether the (break) char is echoed is decided by the echo
strategy/echo table in the input driver, not by this counter — echo does not change
NOCHRET. - Corroboration (carved L07): the carved DVIO analysis at
/mnt/d/ND/t/re/mon-analysis/511B-DVIO/README.mddocuments the sibling DVIO handlerDVIO=141027Bin026-S3IMPIT(byte-proven), whose input phase is this sameXNINSTR/IBMOVEpath; its write-back mask andNOCHRET/11NOCRETsplit match the NPL above (DVINST writesNOCHRET, DVIO writes11NOCRET).
Breaking the 12-vs-14 tie¶
Real SINTRAN never stops before storing and counting the terminating byte, and MaxNo is
inclusive. So the correct return is the higher count that includes the break character:
14. The 12-result emulator is wrong in one (or both) of the exact ways this loop forbids:
(a) it excludes the break terminator from the count, and/or (b) it treats MaxNo as exclusive and
stops one read early.
Honest caveat: the rule (store+count-then-test; break byte counted; MaxNo inclusive) is
VERIFIED from L07 NPL and consistent with the 026-S3IMPIT carve. Pinning the literal "14" to NC's
run assumes NC's break table flags the terminator inside the data window (the normal case); the
loop guarantees that whichever byte NC's break table selects as the terminator is itself read,
stored, and counted.
Summary of verdicts¶
| Q | Call | Divergence | Verdict | Decisive evidence |
|---|---|---|---|---|
| 1 | 123B RELES | error 5 vs silent | Silent success (no error 5) | BRELEASE 010624 JAZ (RESLI=0 → exit 010717), live L07 |
| 2 | 54B MDLFI | 124 vs 46 | empty → 124; nonexistent → 46 | MDLFI 106063B carved; err propagated 106174; both SAA 56/SAA 174 in 006-S3FS |
| 3 | 503B DVINST | 12 vs 14 | 14 (break byte counted; MaxNo inclusive) |
IBTBREAK RP-P2-N500.NPL: store+incr (130264/130270) before break test (130335); count → NOCHRET (130416) |