Skip to content

503B InputString

Validated   MON 503B (323 decimal) · Mnemonic DVINST · Group: Not grouped in manual

Available from: not known - no compatibility box captured from the manual

Emulation source: src/handlers/mon_503B_InputString.c

Description

Reads a string from a device, e.g. a terminal or an opened file. This monitor call provide a fast input to ND-500 programs.

Notes

  • Only the first four parameters are used for file input.
  • An ASCII table is given in Appendix G.

Parameters

Name Type Direction Description
DevNo INTEGER In Logical device number. See appendix B. Use 1 for your own terminal.
MaxNo INTEGER In Maximum number of bytes to read before break.
NoOfBytesRet INTEGER Out Number of bytes read.
Buff STRING Out Buffer to receive input.
BreakStrat INTEGER In Break setting. See SetBreak. Use 8 for user-defined break table.
EchoStrat INTEGER In Echo setting. See SetEcho. Use 8 for user-defined echo table.
BreakT1 INTEGER In Break table bits 0:31. Bits set to 1 cause break.
BreakT2 INTEGER In Break table bits 32:63.
BreakT3 INTEGER In Break table bits 64:95.
BreakT4 INTEGER In Break table bits 96:127.
EchoT1 INTEGER In Echo table bits 0:31. Bits set to 0 cause echo.
EchoT2 INTEGER In Echo table bits 32:63.
EchoT3 INTEGER In Echo table bits 64:95.
EchoT4 INTEGER In Echo table bits 96:127.

Direction: In = the program supplies the value, Out = the call returns it, In/Out = both.

See also

InByte, InString, OutputString

Compatibility

ND-100 ND-500 User programs RT programs System programs
Yes Yes Yes Yes No

From the YAML extraction; this call's page in the manual had no compatibility box that was captured.

Examples

From the manual (OCR text, not corrected).

INTEGER : DevNo, MaxNo, NoOfBytesRet, BreakStrat, EchoStrat, BreakT1
INTEGER : BreakT2, BreakT3, BreakT4, EchoT1, EchoT2, EchoT3, EchoT4
BYTES : Buff(0:199)
...
Monitor_Call('InputString', DevNo, MaxNo, NoOfBytesRet, Buff(1),
             BreakStrat, EchoStrat, BreakT1, BreakT2, BreakT3,
             BreakT4, EchoT1, EchoT2, EchoT3, EchoT4)
INTEGER DevNo, MaxNo, NoOfBytesRet, BreakStrat, EchoStrat, BreakT1
INTEGER BreakT2, BreakT3, BreakT4, EchoT1, EchoT2, EchoT3, EchoT4
INTEGER Buff(100)
...
Monitor_Call('InputString', DevNo, MaxNo, NoOfBytesRet, Buff(1),
             BreakStrat, EchoStrat, BreakT1, BreakT2, BreakT3,
             BreakT4, EchoT1, EchoT2, EchoT3, EchoT4)
DevNo, MaxNo, NoOfBytesRet, BreakStrat, EchoStrat, BreakT1: LONGINT;
BreakT2, BreakT3, BreakT4, EchoT1, EchoT2, EchoT3, EchoT4: LONGINT;
Buff : ARRAY [0..8] OF RECORD...END;
...
InputString(DevNo, MaxNo, NoOfBytesRet, Buff, BreakStrat, EchoStrat,
            BreakT1, BreakT2, BreakT3, BreakT4, EchoT1, EchoT2, EchoT3, EchoT4);
01 DevNo COMP.
01 MaxNo COMP.
01 NoOfBytesRet COMP.
01 BreakStrat COMP.
01 EchoStrat COMP.
01 BreakT1 COMP.
01 BreakT2 COMP.
01 BreakT3 COMP.
01 BreakT4 COMP.
01 EchoT1 COMP.
01 EchoT2 COMP.
01 EchoT3 COMP.
01 EchoT4 COMP.
01 Buff.
   02 array COMP OCCURS 100 TIMES.
...
MONITOR-CALL "InputString" USING DevNo, MaxNo, NoOfBytesRet, Buff,
             BreakStrat, EchoStrat, BreakT1, BreakT2, BreakT3,
             BreakT4, EchoT1, EchoT2, EchoT3, EchoT4.
DevNo : W BLOCK 1
MaxNo : W BLOCK 1
NoOfBytesRet: W BLOCK 1
BreakStrat: W BLOCK 1
EchoStrat: W BLOCK 1
BreakT1 : W BLOCK 1
BreakT2: W BLOCK 1
BreakT3: W BLOCK 1
BreakT4 : W BLOCK 1
EchoT1: W BLOCK 1
EchoT2: W BLOCK 1
EchoT3: W BLOCK 1
EchoT4 : W BLOCK 1
Buff : BY BLOCK 400B
InputString: EQU 37B9 + 503B
...
Monitor_Call('InputString', DevNo, MaxNo, NoOfBytesRet, Buff(0), &
             BreakStrat, EchoStrat, BreakT1, BreakT2, BreakT3, &
             BreakT4, EchoT1, EchoT2, EchoT3, EchoT4)
...
CALLG InputString, 14, DevNo, MaxNo, NoOfBytesRet, Buff, &
      BreakStrat, EchoStrat, BreakT1, BreakT2, &
      BreakT3, BreakT4, EchoT1, EchoT2, EchoT3, EchoT4

ndmonlib implementation

Validated Registered MON_STATUS_VALIDATED in src/core/mon_registry.c: implemented, tested and working.

Handler mon_503B_InputString
Code lines 279 (non-blank, non-comment lines in the file)
Generator note registered VALIDATED but the source says 'PLACEHOLDER' - check whether the call is complete

Notes from the handler source

src/handlers/mon_503B_InputString.c line 87

Check if character should be echoed based on strategy and tables.
IMPORTANT: Echo table has INVERTED semantics from break table!
bit=0 means echo, bit=1 means don't echo.

src/handlers/mon_503B_InputString.c line 161

=========================================================================
DVINST core - shared with MON 511B DVIO (see mon_device_io.h).

Reads the break/echo strategies and user tables from ctx at the FIXED
argument indices 4..13, which DVINST and DVIO share. Only DevNo, MaxNo,
the returned-count index and the buffer differ between the two.
=========================================================================

src/handlers/mon_503B_InputString.c line 183

Break table handling:
Strategy 7 or 8 with inline T1-T4: read and store to device state
Strategy 7 or 8 without inline: use device's current user table
Note: DVINST docs say "Use 8 for user-defined break table"

src/handlers/mon_503B_InputString.c line 204

Guard against a mis-decoded user break table. The ND LINKER's 503B DVINST
uses a 14-arg layout that differs from NC's (arg[1] is a procedure pointer,
not MaxNo; args[6..9] are not an inline break table - see MON_TO_BINARY_PLAN
"503B DVINST arg-layout mismatch"). Read positionally, its "table" is garbage
that breaks on the letter 'T' but NOT on CR, so every command truncates at
its first 'T'. A valid terminal line-input break table ALWAYS breaks on CR
(0x0D). If a user-table strategy yields a table that does not break on CR,
treat it as invalid and fall back to MAC-style line breaks (CR/LF/ESC/EOF).
NC uses strategy 1 and is unaffected. (Remove once the linker's true 503B
parameter layout is mapped and the args are read correctly.)

src/handlers/mon_503B_InputString.c line 222

Echo table handling:
Strategy 7 or 8 with inline T1-T4: read and store to device state
Strategy 7 or 8 without inline: use device's current user table
Note: DVINST docs say "Use 8 for user-defined echo table"

src/handlers/mon_503B_InputString.c line 254

Validate byte count. PHASE-2 EXPERIMENT (session 557c0950): the ND LINKER
passes a huge MaxNo (0xF80000CB); clamp instead of erroring to test whether
its command read then proceeds (BuffAddr correct?) - revert if not.

src/handlers/mon_503B_InputString.c line 277

Blocking line-input: a terminal DVINST with NO input available must
SUSPEND the process (SINTRAN "the program waits ...") rather than
return an empty line, which would make the caller act on a blank
answer and busy-loop. Mirror the MON 1B blocking-read model: since
nothing has been consumed yet, the MON call is not committed - the
CPU rewinds to the CALLG and the run loop stops with STOP_WAIT_INPUT,
so the host can feed a line and resume, re-reading the whole line.
Only applies before the first byte; a line already in the buffer is
read to its break character as usual.

src/handlers/mon_503B_InputString.c line 286

A console that EXISTS but is empty must SUSPEND, so the host can feed a
line and the CPU retries the whole call.

NO console configured is deliberately NOT a suspend: it means this run
has no terminal at all (headless/batch), so nobody will ever type and
suspending would deadlock. Batch programs rely on this - NC reads the
terminal and proceeds on the empty result; making no-console suspend
makes the whole NC compile stop with "waiting for input" (measured:
dom_nc_compiler fails exactly that way).

CONSEQUENCE FOR HARNESSES, worth knowing: with no console installed, a
terminal read returns 0 bytes + SUCCESS forever, and an interactive
program will spin rather than suspend - the ND linker did exactly this,
123,308 zero-byte reads in one run. A driver that intends to feed input
MUST install a console BEFORE the run (mon_queue_console_input), not
lazily on the first STOP_WAIT_INPUT that then never comes.

src/handlers/mon_503B_InputString.c line 323

SINTRAN user-break: if ESCAPE is enabled on this terminal and this
is its escape char, it aborts the running program back to the '@'
command processor - it is NOT input data. Programs that own the key
call 71B DESCF first, so mon_is_escape_break is false for them and
ESCAPE is read as an ordinary line terminator/byte as before.

src/handlers/mon_503B_InputString.c line 351

Genuine EOF from an INSTALLED console (read_char returned EOF with
nothing buffered) = batch stdin exhausted. Do NOT return "0 bytes +
success": the caller then re-reads forever (measured: NC 515k+ /
linker 123k zero-byte reads - a silent spin). Instead SUSPEND, exactly
like the empty-console case: the shell run loop calls
mon_console_wait_for_input(), which returns false at EOF and BREAKS the
run, so the program (and any nested UECOM sub-program) terminates
cleanly. An interactive terminal never hits this - its read_char blocks
and only yields EOF when the terminal is actually closed (session end),
where suspend-then-EOF-break is likewise correct.

src/handlers/mon_503B_InputString.c line 410

End of file must be SIGNALLED, not reported as a successful read of
zero bytes. SINTRAN's file-system EOF is error 3 (003B) - the same
contract 117B RFILE follows (see commit 01c286e; the authority is
73B SMAX's own manual text).

This matters: the ND linker reads its LINKER:INIT command file
through DVINST, one line per call. Returning "success, 0 bytes" at
the end of that file left it with no way to know the file was
finished, so it re-read forever instead of moving on to the
terminal - a silent infinite loop with no error anywhere.

Only when NOTHING was read: a short final line with no trailing
break character is a successful read, and its EOF is reported on
the NEXT call.

Emulation research

Source

This section comes from the emulation: block of the call's YAML file in the NDInsight repo. It records what was learned while implementing the call in nd500x; its status is nd500x's, not ndmonlib's.

Status (nd500x) verified
nd500x handler nd500x/src/libmon/handlers/mon_503B_InputString.c
Last updated 2026-07-17

Health note

503B DVINST CORRECTLY SUSPENDS ON AN EMPTY QUEUE. Recorded explicitly because this call was suspected during the linker investigation and was cleared: the blocking-read model is right, and the 503B-line hypothesis for the linker's config dialogue was DISPROVEN, not left open.

Architecture note

THE DVINST BODY IS SHARED, NOT DUPLICATED. mon_dvinst_read() in nd500x/src/libmon/handlers/mon_503B_InputString.c is the core, exported via mon_device_io.h, and MON 511B DVIO calls the SAME function for its input phase. The carve independently confirms this is how SINTRAN itself is built: 511B's input phase is named XNINSTR, which is literally 503B DVINST's body. Fix a bug in the core and both calls get it.

Parameter notes

1. DevNo

Field Value
Note 32-bit word. arg 0 for DVINST; also arg 0 for 511B DVIO.
Verified Yes

2. MaxNo

Field Value
Note arg 1 for DVINST. NOTE it moves to arg 14 in 511B DVIO - the arg lists are NOT a shared prefix. Capped at 2048 (DVINST_MAX_BYTES).
Verified Yes

3. NoOfBytesRet

Field Value
Note arg 2 for DVINST (arg 15 in 511B DVIO). An OUT parameter - it MUST be written or the caller keeps a stale count. This is the MON 412B FSCNT bug shape.
Verified Yes

4. Buff

Field Value
Note arg 3. Taken as a raw byte address (arg_addresses[3]); bytes are written directly. Same index in 511B DVIO.
Verified Yes

5. BreakStrat / EchoStrat / BreakT1-T4 / EchoT1-T4

Field Value
Note FIXED indices 4..13, and 511B DVIO uses THE SAME INDICES for the same fields - which is why the shared core can read them straight from ctx for both calls. Absent BreakStrat/EchoStrat args fall back to the device defaults set by 4B/3B.
Verified Yes

Observed calls

1. ND linker (linker-b01.dom), thunk @0xB004AC8A, call @0xB004AC90

Field Value
Params Arg count: 14
Expectation The linker reads its command line ONCE from the TERMINAL via DVINST. Its call site is a bare pass-through thunk (ents $0x4C; call $0xF8000143, $0xE, b.0x14, b.0x18, b.0x1C, @b.0x20, b.0x24..b.0x48) with exactly ONE caller (@0xB004754F..0xB00475C0) that builds the argument block at a fixed +0x150 and calls it with ZERO CALLG args.
Note Laying this call site beside the 511B one is what established the 511B argument mapping. See 511B_DVIO.yaml.

2. NC compiler (nc-a06.dom)

Field Value
Params Breakstrat: 1
Expectation NC uses break strategy 1 (control characters break) and is unaffected by the linker-specific table fallback below.
Note NC reads device 1 and never touches device 0.

Return contract

  • Success: K flag cleared; NoOfBytesRet written with the byte count; bytes in Buff.
  • Errors

    Code Octal Meaning
    24 030B No such device name (unsupported device class)
    90 132B No file opened with this number
    124 174B Illegal parameter (file not open for reading)
    111 157B Missing parameter (fewer than 4 CALLG args)

Verified

1. BLOCKING-READ SEMANTICS: a terminal DVINST with NO input available SUSPENDS (STOP_WAIT_INPUT) rather than returning an empty line. Nothing is consumed, the MON is left uncommitted, the CPU rewinds to the CALLG and the whole line is re-read on resume. Only applies before the first byte - a line already in the buffer is read to its break character as usual.

Field Value
Evidence Commit 7819a36, mirroring the MON 1B model (commit 26f665d). Returning an empty line instead makes the caller act on a blank answer and busy-loop.

2. 503B is an ND-500 LEVEL-12 monitor call. It is NOT dispatched through the ND-100 monitor GOTAB - a GOTAB[503] probe is MEANINGLESS. It arrives via the resident level-12 GOSW, slot 3 (NINSTR).

Field Value
Evidence Carve .../mon-analysis/503B-InputString/README.md; prove-mon.py 503 reports "500-523B -> level-12 5CMNO GOSW; GOTAB[503] is meaningless".

3. The handler body is real, byte-identical SINTRAN L code.

Field Value
Evidence NINST = 141272B with extended entry XNINS = 141277B, carved from 026-S3IMPIT (load 32000B). Two independent anchors agree: the L07 symbol table, and the fact that every indirect worker pointer resolves to exactly this call's worker set (EMONICO, XACTRDY, NXTMSG, 5GTDF, NORMMC, SET12, GCPUDF, WN5STATUS). The illegal-parameter error path (SAA 174 -> EMONICO) is byte-verified - note 174B is exactly the "Illegal parameter" code the handler returns.

4. Maximum string length is 2048 bytes.

Field Value
Evidence The manual, corroborated by the NPL/carve side of 511B (DNOBY max 4000B octal = 2048 bytes).

Unverified

  • THE LINKER'S 503B ARGUMENT LAYOUT IS NOT FULLY MAPPED. Read positionally, the linker's 14-arg call yields args[6..9] that are NOT an inline break table - the resulting "table" breaks on the letter 'T' but NOT on CR, truncating every command at its first 'T'. nd500x GUARDS against this rather than fixing it: if a user-table strategy yields a table that does not break on CR, the table is treated as invalid and the read falls back to MAC-style line breaks. A valid terminal line-input break table ALWAYS breaks on CR. This guard should be removed once the linker's true parameter layout is mapped.
  • MaxNo CLAMPING IS AN EXPERIMENT, NOT A CONTRACT. The linker passes MaxNo = 0xF80000CB (a leftover CALLG target address, not a length). nd500x CLAMPS to 2048 instead of erroring, explicitly to test whether its command read then proceeds. Real SINTRAN's response to an out-of-range MaxNo is UNKNOWN - the carve's only proven error path is 174B illegal parameter.
  • THE PARAMETER FIELDS ARE NOT BYTE-ISOLATED IN THE CARVE, and the carve says so: the max-byte-count, the destination-array pointer and the returned-byte-count write-back are all read/written as numeric displacements into the ND-500 message buffer, and the carve captured the handler CODE but not the data-structure DECLARATIONS that give those displacements meaning. See the folder's ADDENDUM.md.
  • Break/echo strategies 3-6 (system-defined tables) are NOT implemented; the core falls through to a PLACEHOLDER using the MAC set.
  • A DEBUG PROBE REMAINS IN THE HANDLER, gated behind ND500X_DVINST_DUMP.

Discrepancies

1. The working hypothesis that the ND linker's startup config answers ("Batch abortion", "Advanced mode") are read via 503B DVINST, and that feeding a 503B line would unblock it.

Field Value
Is DISPROVEN by driving it: those answers are read via an in-memory datafield poll, NOT via 503B or INBT. The blocking-read change was kept because it is correct in general, but it did NOT unblock the linker.
Evidence Commit 7819a36 - "driving it showed its config-question answers are read via an in-memory datafield poll, NOT via 503B/INBT, so the 503B-line hypothesis is disproven for the linker".

Notes

  • THE DUAL-INPUT-SOURCE TRAP - this cost days. The ND linker uses TWO input channels: the command line ONCE from the TERMINAL via 503B DVINST, and characters from DEVICE 0 via 1B INBT. Feeding only one channel sends it down an unreachable path and produces a CONVINCING FALSE BLOCKER. Feed both, or route each feed by the device that is actually waiting. See 1B_InByte.yaml and nd500x/test/diag_linkdrive.c.

Sources

Doc Note
SINTRAN III Monitor Calls (ND-860228.2 EN) The manual body of this YAML.
NDInsight/tools/sintran-segment-carver/versions/L-VSX-500/re/mon-analysis/503B-InputString/README.md Level-12 GOSW dispatch, the NINST/XNINS body, and its honest caveats.
NDInsight/tools/sintran-segment-carver/versions/L-VSX-500/re/mon-analysis/503B-InputString/ADDENDUM.md Why the carve alone cannot name the parameter fields (message-buffer displacements).
nd500x/docs/CSHARP_HANDOFF_BLOCKING_READ_SESSION.md The blocking-read model for the C# side.

Updates 2026 07 17

1. File EOF = error 3; console suspend policy

Field Value
Status verified
Detail When DVINST reads from an OPEN FILE and hits end of file, it returns error 3 (003B End of file), NOT success-with-0-bytes. The ND linker reads its LINKER:INIT command file through DVINST one line per call; returning "success, 0 bytes" made it re-read the file forever.
Console policy Terminal read: a console that EXISTS but is empty must SUSPEND (STOP_WAIT_INPUT) so the host can feed a line and the CPU retries. NO console configured deliberately does NOT suspend (headless/batch runs, e.g. NC, proceed on the empty result); suspending there would deadlock. A driver that intends to feed input must install a console BEFORE the run.
Nd500x commits a628b70, 3b8660f

Source

SINTRAN III Monitor Calls (ND-860228.2 EN).