1B InByte¶
Validated MON 1B (1 decimal) · Mnemonic INBT · Group: File Operations (manual section 2.4)
Available from: All programs (manual compatibility box)
Emulation source: src/handlers/mon_1B_InByte.c
Description¶
Reads one byte from a character device, e.g. a terminal or an opened file. If the device is a word-oriented device, one word is read. This monitor call can be used on most input devices.
- Bit 7 is a parity bit if terminal or file input. IOMultiFunction may change this.
- The program waits if there is no bytes in the input buffer of the device. You can change this with NoWaitSwitch or TerminalNoWait.
- The pointer to the next byte is incremented when you read from a mass-storage file.
- Input from card readers are converted to ASCII characters. Use DeviceControl to read the 12-bit card columns.
- Background programs may read from logical device number 0. This is the SINTRAN III command buffer. You may read parameters following the program name this way. Break and echo are both set to 1. Normal SINTRAN III command editing is available. All letters are converted to uppercase. You may control this with IOMultiFunction.
- Appendix F contains an ASCII table.
Parameters¶
| Name | Type | Direction | Description |
|---|---|---|---|
DeviceNumber |
INTEGER | In | Logical device number. Use 1 for your own terminal. |
ReturnValue |
INTEGER | Out | The read byte. |
Direction: In = the program supplies the value, Out = the call returns it, In/Out = both.
See also¶
In8AndFlag, InUpTo8Bytes, In8Bytes, InString, InputString, In4x2Bytes, OutByte
Compatibility¶
| Machines | Users | Programs |
|---|---|---|
| ND-100 and ND-500 | All users | All programs |
The manual's compatibility box for this call, word for word.
Yes/no fields from the YAML extraction (not in the manual's words):
| ND-100 | ND-500 | User programs | RT programs | System programs |
|---|---|---|---|---|
| Yes | Yes | Yes | Yes | No |
Examples¶
From the manual (OCR text, not corrected).
INTEGER: DeviceNumber, ReturnValue
..
ON ROUTINEERROR DO
IF ErrCode > 0 THEN ..
ENDON
Monitor_Call('InByte', DeviceNumber, ReturnValue)
INTEGER DeviceNumber, ReturnValue
...
Monitor Call('InByte', DeviceNumber, ReturnValue)
IF (ErrCode .NE. 0) THEN ...
DeviceNumber, ReturnValue : INTEGER2;
...
InByte(DeviceNumber, ReturnValue);
IF ErrCode <> 0 THEN ...
01 DeviceNumber COMP.
01 ReturnValue COMP.
01 ErrCode COMP.
...
MONITOR-CALL "InByte" USING DeviceNumber, ReturnValue.
CALL "CbError" USING ErrCode.
IF ErrCode NOT = 0 GO ...
DeviceNumber : W BLOCK 1
ReturnValue : W BLOCK 1
ErrCode : W BLOCK 1
InByte : EQU 37B9 + 1B
CALLG InByte, 2, DeviceNumber, ReturnValue
IF K GO ERROR
ERROR: W1 =: ErrCode %ErrorCode in W1 register.
LDT DEVNO %Logical device number.
MON 1 %Monitor call InByte.
JMP ERROR %Error return from monitor call.
STA BYTE %Normal return, store byte read.
ERROR, ... %Error number in register A.
DEVNO, ...
BYTE, 0
ndmonlib implementation¶
Validated Registered MON_STATUS_VALIDATED in src/core/mon_registry.c: implemented, tested and working.
| Handler | mon_1B_InByte |
| Code lines | 98 (non-blank, non-comment lines in the file) |
Notes from the handler source¶
src/handlers/mon_1B_InByte.c line 47
Device 0 is the SINTRAN command buffer. The ND LINKER's resident
reader polls it and busy-spins on EOF (it does NOT fall through to
the terminal on EOF), so an empty command buffer must SUSPEND like
a terminal read: 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 the next line and resume. (The command line itself is also
read from the terminal via 503B DVINST; both channels are fed.)
src/handlers/mon_1B_InByte.c line 54
RE-TESTED 2026-07-16 and CONFIRMED, after 143B RSIO moved command
input to the terminal: returning EOF here still makes the LINKER
busy-spin - 20,088 consecutive 1B INBT calls at PC=0xB004E759 in
600k instructions, with no other MON activity. So the suspend
below is required by the linker itself, not by NC (NC now reads
device 1 and never touches device 0). An earlier note blaming NC's
reader for the EOF spin was wrong. Do not "restore EOF" here.
src/handlers/mon_1B_InByte.c line 81
Check if input is available. On a real terminal SINTRAN SUSPENDS the
process when the input buffer is empty ("the program waits if there is
no bytes in the input buffer of the device"). We model that suspend by
requesting a blocking-read wait: the MON call is not committed, the CPU
rewinds to the CALLG, and the run loop stops with STOP_WAIT_INPUT. When
the host feeds input and resumes, this MON call re-executes and reads
the byte. This replaces the old busy-spin-on-EOF behaviour that hung
the linker's resident char reader when interactive input ran out.
src/handlers/mon_1B_InByte.c line 104
ESCAPE (user-break) handling: if the char is this terminal's escape
character AND escape is ENABLED (DFLAG.5IESC clear), it is a user
break, not input data. If escape is DISABLED (e.g. the linker called
71B DESCF), the character passes through as ordinary data.
src/handlers/mon_1B_InByte.c line 113
SINTRAN user-break: the ESCAPE key aborts the running program and
returns control to the command processor (the '@' shell). Request
the standard halt so the run loop stops back at the prompt, exactly
as a real terminal's ESCAPE user-break does. (Programs that own the
key call 71B DESCF first, so mon_is_escape_break is false and we
never reach here - they are unaffected.)
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_1B_InByte.c |
| Last updated | 2026-07-17 |
Device model¶
- Note: THE DEVICE MODEL, recorded here because 1B is where it is provable and where getting it wrong costs days. Device 0 = the SINTRAN command buffer = the INVOCATION COMMAND LINE (the manual's "Background programs may read from logical device number 0"). Device 1 = the program's OWN TERMINAL. They are DIFFERENT CHANNELS with different backing stores, not fallbacks for each other: an empty device 0 does NOT fall through to the terminal.
-
Devices
Device Meaning Backing 0 SINTRAN command buffer (the invocation command line) nd500x: g_command_buffer / mon_read_command_buffer_char() (nd500x/src/libmon/mon_file_table.c:1045) 1 the program's own terminal nd500x: the ConsoleIO handler (mon_file_table_get_console()) -
Routing in nd500x: device 0 -> command buffer; is_character_device()/is_terminal() -> console; is_mass_storage_file() -> open-file table (byte pointer incremented, matching the manual's "The pointer to the next byte is incremented"); anything else -> error 24 (030B No such device name).
Parameter notes¶
1. DeviceNumber
| Field | Value |
|---|---|
| Note | Read as a 32-bit word. On ND-500 all INTEGER MON parameters are 32-bit words (W BLOCK, not H BLOCK). |
| Verified | Yes |
2. ReturnValue
| Field | Value |
|---|---|
| Note | nd500x returns the byte BOTH in W1/I1 (ND-100 compatibility) and by writing the second CALLG argument as a 32-bit word. Callers observed to date read W1; the OUT-parameter write is defensive. |
| Verified | No |
Observed calls¶
1. ND linker (linker-b01.dom) @PC=0xB004E759
| Field | Value |
|---|---|
| Params | Devicenumber: 0 |
| Expectation | The linker's resident character reader polls device 0 for its command line. Its FIRST device-0 read must return 0x0D (CR); it then prints its banner and startup dialogue and NEVER reads device 0 again. |
| Note | This one call site is the whole evidence base for both the CR termination and the blocking-read model below. |
Return contract¶
- Success: K flag cleared; byte in W1 (and written to the OUT parameter).
-
Errors
Code Octal Meaning 3 003B End of file (nd500x also uses this to signal an ESCAPE user break) 24 030B No such device name (unsupported device class) 90 132B No file opened with this number 111 157B Missing parameter (fewer than 2 CALLG args)
Verified¶
1. Device 0 is ALWAYS CR-terminated (015B / 0x0D). 47B is an INTERNAL SOURCE MARKER that never reaches a device-0 reader.
| Field | Value |
|---|---|
| Evidence | WRITE SIDE, byte-proven in the L07 command processor: at the 47B source marker it substitutes CR and resets the command byte pointer CPNT - 050773: SAA 15 -> SBYT. (Command-buffer machinery is CPNT byte pointer + CSTRIN = 144035B, NOT CBUF = 170207B, which is a boot-time scratch pointer.) Carve: NDInsight/tools/sintran-segment-carver/versions/L-VSX-500/re/mon-analysis/1B-InByte/DEVICE0-CR-TERMINATION.md |
2. A program launched with NO arguments still reads a lone CR.
| Field | Value |
|---|---|
| Evidence | Follows from the same 050773 CR substitution (the terminator is written unconditionally). Asserted in nd500x test_mon_calls.c ("empty no-args -> lone CR, not wait/EOF"), commit 19f7354. |
3. READ SIDE: a device-0 read yields args + CR, and the reader consumes that and stops.
| Field | Value |
|---|---|
| Evidence | ND linker under nd500x with device 0 delivering CR: its first 1B INBT at PC=0xB004E759 returns 0x0D, it proceeds to its banner and startup dialogue, and never reads device 0 again. This is the runtime confirmation of the carver's write-side CR proof. Commit 19f7354. |
4. Returning raw EOF at end-of-line instead of the CR is WRONG and observably catastrophic.
| Field | Value |
|---|---|
| Evidence | MEASURED: 20,088 consecutive 1B INBT retries at PC=0xB004E759 within 600k instructions, with no other MON activity - the ND linker busy-spins. Fixed in mon_read_command_buffer_char() (nd500x/src/libmon/mon_file_table.c:1052-1071): return the stored bytes, then exactly ONE synthetic CR if the line did not already end in one, then end-of-line. A line that already ends in CR is not given a second one. Commits 19f7354, 2d9b00f (re-tested and re-confirmed). |
5. BLOCKING-READ SEMANTICS: an empty read SUSPENDS (STOP_WAIT_INPUT); it does not return EOF. This is the manual's own "The program waits if there is no bytes in the input buffer of the device" made real.
| Field | Value |
|---|---|
| Evidence | Modelled in nd500x as suspend + retry: the MON call is NOT committed, ctx->wait_requested is set, nd500_check_indirect_call rewinds the resolved address to the CALLG and stops the run loop with STOP_WAIT_INPUT, so the MON re-executes on resume. Applies to BOTH device 0 and the terminal. Verified: the linker's EXIT no longer spins (clean "STOP=waiting for input" at the command reader); NC unchanged (clean MON 0B LEAVE at instr 1902382). Commit 26f665d. |
6. ESCAPE handling is real per-device state, not a constant: if the byte is the terminal's escape character AND escape is ENABLED (DFLAG.5IESC clear) it is a user break; if 71B DESCF disabled escape, the character passes through as ordinary input data.
| Field | Value |
|---|---|
| Evidence | mon_is_escape_break(device, byte) against TerminalState.escape_inhibited / escape_char (VESCAPE, default 033B/0x1B). Commit 68e290d. |
7. MON 1B dispatch is a level-14 FAST PATH, not an MFELL call.
| Field | Value |
|---|---|
| Evidence | Byte-proven: GOTAB[1B] = 071633B = M1, a resident level-14 handler (one of the 32 GOTAB slots that are NOT MFELL) - 026-S3IMPIT :071234B, byte offset 32056 -> 73 9b. MCTAB[1B] = 026576B = YFGET, the filesystem byte-input worker - 044-S3IDPIT :005621B, byte offset 1826 -> 2d 7e; body carved in 006-S3FS :026576B-026640B. Carve: .../mon-analysis/1B-InByte/README.md (CORRECTED 2026-07-13; the earlier "stub-routed / INBT=032471B" model was an artefact of the wrong dispatch model and is debunked). |
Unverified¶
- WHAT A REAL DEVICE-0 READ RETURNS PAST THE CR. No program we have exercises it - the linker reads the CR once and stops. nd500x's command buffer returns -1 (end-of-line) there and the 1B handler treats that as SUSPEND. That is a CHOICE, not a proven behaviour. Carried forward from the carve's own "Still UNPROVEN (do not guess)" section and mon_file_table.c:1064-1066.
- The runtime hand-off between the M1 fast path and the YFGET file-byte worker is INFERRED (a dashed hop in the carve), not byte-proven.
- The manual's device-0 notes that nd500x does NOT model: "Break and echo are both set to 1", "Normal SINTRAN III command editing is available", "All letters are converted to uppercase" (controllable via IOMultiFunction). No program under test has depended on any of these, so none is implemented and none is disproven.
- Bit 7 as a parity bit on terminal/file input (manual). nd500x masks the byte to 0xFF and does no parity handling.
- NoWaitSwitch (36B) / TerminalNoWait (307B) can change the wait behaviour per the manual. nd500x's 1B always suspends on empty; it does not consult a no-wait flag.
Notes¶
- THE DUAL-INPUT-SOURCE TRAP - this cost days. The ND linker uses TWO input channels at once: it reads its 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. A driver harness must feed BOTH, or route each feed by the device that is actually waiting (STOP_WAIT_INPUT's stop_data). See nd500x/test/diag_linkdrive.c and commit 0b64731.
- An earlier note in this tree blamed NC's reader for the EOF spin. That was WRONG and is corrected: NC reads device 1 and never touches device 0. The suspend is required by the LINKER. Do not "restore EOF" here. (Commit 2d9b00f re-tested and confirmed this after 143B RSIO moved command input to the terminal.)
Discrepancies¶
1. The emulator previously returned SINTRAN error 57 on an empty 1B read ("non-blocking EOF semantics", asserted in an older test).
| Field | Value |
|---|---|
| Is | 57 is not an EOF code at all - it is "Space not available to expand file". The invented codes 52/53/55/57 were swept out and replaced with the real Appendix A values; and the EOF return itself was then replaced entirely by the STOP_WAIT_INPUT suspend model. |
| Evidence | Commits 6b57202 (real error codes; nd500x/src/libmon/mon_errors.h) and 26f665d (suspend replaces EOF). |
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/1B-InByte/DEVICE0-CR-TERMINATION.md | The write-side CR proof and the three-sentinel table (47B vs 015B vs -1). Do not conflate them. |
| NDInsight/tools/sintran-segment-carver/versions/L-VSX-500/re/mon-analysis/1B-InByte/README.md | Dispatch carve (GOTAB[1B]=M1, MCTAB[1B]=YFGET). |
| nd500x/docs/HANDOFF_CSHARP_FILE_TABLE_SINTRAN_SEMANTICS.md | ITEM 2 - device-0 CR termination, with the exact behaviours the C# side must match. |
| nd500x/docs/CSHARP_HANDOFF_BLOCKING_READ_SESSION.md | The blocking-read model (STOP_WAIT_INPUT) and device-0 vs terminal. |
Source¶
SINTRAN III Monitor Calls (ND-860228.2 EN), page 319.