Skip to content

117B ReadFromFile

Validated   MON 117B (79 decimal) · Mnemonic RFILE · Group: File Operations (manual section 2.4)

Available from: All programs (manual compatibility box)

Emulation source: src/handlers/mon_117B_ReadFromFile.c

Description

Reads any number of bytes from a file. The read operation must start at the beginning of a block. The file must be opened for random read access.

  • The standard block size is 512 bytes. You can change this with SetBlockSize. The first block is number 0.
  • You may use access code D for direct transfer. Then the block size must be a multiple of the page size. The number of bytes to transfer must be a multiple of the block size.
  • Peripheral files are always read sequentially.
  • Data transfer across segment or RT common limits is illegal as this would be likely to result in inconsistent data.

Parameters

Name Type Direction Description
FileNo INTEGER2 In File number. See OpenFile.
WaitFlag INTEGER2 In Wait flag. 0=suspend until complete, non-zero=continue (use AwaitFileTransfer to check).
Buff BYTES Out Buffer to receive transferred data (output). Must start on even byte address.
BlockNo INTEGER2 In Block number to start reading from. Use -1 to read the next block.
NoOfBytes LONGINT In Number of bytes to read.

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

See also

SetStartBlock, SetBlockSize, ReadDiskPage, ReadBlock, WriteBlock, and WriteToFile

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 : FileNo, RetFlag, BlockNo
BYTES : Buff(0:511)
INTEGER4 : NoOfBytes
...
ON ROUTINEERROR DO
&nbsp;&nbsp;&nbsp;&nbsp;IF ErrCode >< 0 THEN ...
ENDON
Monitor_Call('ReadfromFile', FileNo, RetFlag, Buff(0), BlockNo, NoOfBytes)
INTEGER FileNo, WaitFlag, BlockNo
INTEGER Buff(256)
INTEGER*4 NoOfBytes
...
Monitor_Call('ReadFromFile', FileNo, WaitFlag, Buff(1),
                               BlockNo, NoOfBytes)
C  IF (ErrCode .NE. 0) THEN ...
FileNo, WaitFlag, BlockNo : INTEGER2;
Buff : ARRAY [0..15] OF RECORD...END;
NoOfBytes : LONGINT;
...
ReadFromFile(FileNo, WaitFlag, Buff, BlockNo, NoOfBytes);
IF ErrCode <> 0 THEN ...
01 FileNo COMP.
01 WaitFlag COMP.
01 Buff.
   02 array COMP OCCURS 256 TIMES.
01 BlockNo COMP.
01 NoOfBytes COMP PIC S9(10).
01 ErrCode COMP.
...
MONITOR-CALL "ReadFromFile" USING FileNo, WaitFlag, Buff,
                                BlockNo, NoOfBytes.

CALL "CbError" USING ErrCode.
IF ErrCode NOT = 0 GO ...
FileNo : W BLOCK 1
WaitFlag : W BLOCK 1
Buff : W BLOCK 256 %Must start on an even byte address.
BlockNo : W BLOCK 1
NoOfBytes : W BLOCK 1
ErrCode : W BLOCK 1
ReadFromFile : EQU 37B9 + 117B

...
CALLG ReadfromFile, 5, FileNo, WaitFlag, Buff, BlockNo, NoOfBytes
IF K GO ERROR
...
ERROR : W1 =: ErrCode %ErrorCode in W1 register.
LDA (PAR %Load register A with address of parameter list.
MON 117 %Monitor call ReadFromFile.
JAF ERROR %Handle error if register A is non-zero.
...
ERROR, ... %Error number in register A.
...
PAR, FILNO %File number returned from earlier call to OpenFile.
RETUR %Return flag.
BUFF %Buffer to receive data.
BLKNO %Block number in file where data starts.
COUNT %Number of words to be read.
...
FILNO, ...
RETUR, 0
BUFF, 0
*+400/ %Make a buffer of 256 words.
BLKNO, ...
COUNT, ...

ndmonlib implementation

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

Handler mon_117B_ReadFromFile
Code lines 132 (non-blank, non-comment lines in the file)

Notes from the handler source

src/handlers/mon_117B_ReadFromFile.c line 79

Check file is not mapped as segment (per SINTRAN docs:
"You may not use ReadFromFile on a file which is connected as a segment")

src/handlers/mon_117B_ReadFromFile.c line 88

Check access mode allows random reading
Valid modes: ACCESS_RAND_RDWR (2), ACCESS_RAND_READ (3),
             ACCESS_RAND_RDWR_CTG (6), ACCESS_RAND_READ_CTG (7),
             ACCESS_RAND_RDWR_RT (8), ACCESS_RAND_EXTEND (9)

src/handlers/mon_117B_ReadFromFile.c line 124

Position the file.

BlockNo is a SIGNED halfword: -1 means "read the next block", i.e. carry
on from wherever the previous transfer left off, without seeking. This is
how sequential streaming through RFILE works. Callers sign-extend it to a
word (PLANC emits H WCONV), so -1 arrives here as 0xFFFFFFFF; treating it
as an unsigned block index seeks past the end and fails every read.

src/handlers/mon_117B_ReadFromFile.c line 190

Return contract per the carved L07 RFILE (006-S3FS worker 102130B):
 - Error 3 (end-of-file) is a RANGE check raised only when the requested
   block/position is ENTIRELY beyond the file's data (carve: bounds test
   102364-102402, SAA 3 @ 102403). A block that exists but holds fewer
   bytes than requested PASSES and does the read.
 - A within-bounds read - even a short final block at EOF - returns
   SUCCESS (K clear) and writes the ACTUAL transferred byte count back to
   the caller (carve success path 102433-102470: recompute count -> B+16 ->
   stored via rec[26]). That count is how the reader learns the length; it
   is NOT signalled by an error.
The previous handler returned error 3 on every short read, so NC reading
4096 bytes of a 23-byte source saw K-set/EXIT-ERROR, treated the source as
unreadable, and rejected valid declarations ("IDENTIFIER deleted").

src/handlers/mon_117B_ReadFromFile.c line 204

Nothing at the requested position. SINTRAN distinguishes two cases, and
the distinction is load-bearing:

 - A RANDOM read (explicit BlockNo >= 0) of a page that is not allocated
   returns "No such page" (022B / 18), NOT "End of file". The ND linker's
   OPEN-DOMAIN reads block 0 of a freshly-created domain and RELIES on
   this: its domain-read routine (linker-b01.dom @B002215C) treats error
   022B as "page absent -> new domain, use a fresh header" and proceeds,
   but treats ANY OTHER error (including 003B) as fatal and aborts the
   command with error 41B. Returning 003B here left A-TEST:DOM empty.
   Confirmed against the SINTRAN III Reference Manual error table:
   022B(018) = "No such page", 003B(003) = "End of file".

 - A SEQUENTIAL resume (BlockNo == -1) that has run off the end is a
   genuine end-of-file: "End of file" (003B). This is the path NC uses
   when streaming a source file, so it must keep 003B.

src/handlers/mon_117B_ReadFromFile.c line 229

Success (full or short read): report the actual bytes transferred back to
the caller in the NoOfBytes parameter (arg 4), matching the carve's
count-write-back, so the reader knows exactly how many bytes it got.

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_117B_ReadFromFile.c
Last updated 2026-07-17

Parameter notes

1. BlockNo

Field Value
Note The manual field already says "Use -1 to read the next block". The trap is the SIGN: BlockNo is a SIGNED halfword that callers sign-extend to a word (PLANC emits H WCONV), so -1 arrives at the handler as 0xFFFFFFFF. Read as an unsigned block index it seeks far past the end and EVERY read fails. -1 must mean "resume at the recorded position, do not seek by block".
Verified Yes

2. NoOfBytes

Field Value
Note This slot is BIDIRECTIONAL in practice even though the manual marks it [I]. On a successful transfer the handler writes the ACTUAL number of bytes transferred back into arg 4. That count is how the reader learns the length of a short final block - it is NOT signalled by an error.
Verified Yes

3. all INTEGER parameters

Field Value
Note On ND-500 every INTEGER parameter is a 32-bit WORD. The manual's own assembly_500 example for this call declares FileNo/WaitFlag/BlockNo/ NoOfBytes as 'W BLOCK 1' (not 'H BLOCK'), and nd500x reads all of them with word accessors.
Verified Yes

Return contract

  • Success: K flag cleared, data in Buff, and the ACTUAL transferred byte count written back to NoOfBytes (arg 4). A short read - including a short FINAL block at end of data - is a SUCCESS, not an error.
  • Errors

    Code Octal Meaning
    3 003B End of file. A RANGE check: raised only when the requested block/position is ENTIRELY beyond the file's data (i.e. zero bytes available there).
    86 126B Not open for random read
    87 127B File number out of range
    90 132B No file opened with this number
    97 141B Transfer error (genuine host I/O error - kept distinct from EOF)
    89 131B No more buffer space
    124 174B Illegal parameter (negative BlockNo other than -1, or size over the cap)

Verified

1. EOF is signalled with SINTRAN error code 3 (003B), not 55. RFILE has no residual-byte-count output in the manual's own parameter list, so the error code in W1 is the only EOF channel the manual documents.

Field Value
Evidence Authoritative manual cite: the 73B SMAX documentation states "Error code 3 means end of file". The handler previously returned 55, so a read-until-EOF loop testing ErrCode==3 could never terminate cleanly. Commit 01c286e.

2. Error 3 is a RANGE check, and a within-bounds short read returns SUCCESS with the real count written back.

Field Value
Evidence Carved L07 RFILE (segment 006-S3FS, worker 102130B): bounds test at 102364-102402 with SAA 3 @ 102403 raising the EOF code; the SUCCESS path at 102433-102470 recomputes the transferred count -> B+16 -> stored via rec[26]. Commit 2618145.

3. Returning error 3 on every short read breaks the NC compiler.

Field Value
Evidence NC reading 4096 bytes of a 23-byte source saw K-set/EXIT-ERROR, treated the source as unreadable, and rejected valid declarations ("IDENTIFIER deleted"). Fixed by the range-check semantics above. Commit 2618145.

4. ReadFromFile is illegal on a file connected as a segment.

Field Value
Evidence The manual states it; the handler enforces it (entry->mapped_as_segment -> 174B).

Unverified

  • WaitFlag is READ and LOGGED but has no effect - every transfer is synchronous. The manual's non-zero "continue, then use AwaitFileTransfer" path is not modelled.
  • The manual's rule "The read operation must start at the beginning of a block" is NOT enforced; the handler will honour a byte pointer set by 74B SETBT mid-block.
  • Access code D (direct transfer) constraints - block size a multiple of the page size, byte count a multiple of the block size - are not enforced.
  • "Peripheral files are always read sequentially" is not modelled; nd500x only serves host-backed mass-storage files here.
  • nd500x caps a single read at 65536 bytes (MAX_READ_SIZE) and returns 174B above it. That cap is an emulator invention, not a documented SINTRAN limit.
  • The exact error the real system returns for a negative BlockNo other than -1 is not established; nd500x returns 174B.

Discrepancies

1. Emulator defect (fixed): RFILE returned error 55 for EOF, returned 55 on seek-past-end, and silently returned SUCCESS on a short final block with no way to learn the length.

Field Value
Is EOF is code 3; a short final block is a SUCCESS whose length is reported through the NoOfBytes slot.
Evidence Commits 01c286e then 2618145 (carve 006-S3FS worker 102130B).

2. Emulator defect (fixed): BlockNo was read as an unsigned word, so the manual-documented -1 ("read the next block") became 0xFFFFFFFF and every sequential streaming read failed.

Field Value
Is BlockNo is sign-interpreted; -1 resumes at the recorded position.
Evidence Handler comment in mon_117B_ReadFromFile.c; the manual field itself documents -1.

Sources

1. SINTRAN III Monitor Calls (ND-860228.2 EN)

Field Value
Page 399
Note The manual page this YAML was extracted from.

2. SINTRAN III Monitor Calls (ND-860228.2 EN) - 73B SetMaxBytes page

Field Value
Note "Error code 3 means end of file" - the authoritative EOF-code cite.

3. L-VSX-500 segment 006-S3FS, RFILE worker 102130B (bounds 102364-102402, SAA 3 @102403, success path 102433-102470)

Field Value

4. nd500x/docs/MON_CSHARP_SYNC_HANDOFF.md

Field Value
Note Part B, "117B RFILE".

5. 01c286e (nd500x) - EOF is code 3, not 55

Field Value

6. 2618145 (nd500x) - range-check EOF + count write-back

Field Value

Updates 2026 07 17

1. Random read of an unallocated page = "No such page" (022B), not EOF

Field Value
Status verified
Detail SINTRAN distinguishes two zero-byte outcomes. A SEQUENTIAL resume (BlockNo == -1) that runs off the end is "End of file" (003B). A RANDOM / positioned read (explicit BlockNo >= 0) of a page that is not allocated is "No such page" (022B = 18 dec). Confirmed vs ND-60.128.5 SINTRAN III Ref Manual error table (022 018 = "No such page"; 003 003 = "End of file").
Why it matters The ND linker reads block 0 of a freshly created domain; its domain-read handler (linker-b01.dom @B002215C) treats 022B as "page absent -> new domain, use a fresh header" and any other error (incl. 003B) as fatal. Returning 003B left the domain uninitialised.
Nd500x commit 570dd85

Source

SINTRAN III Monitor Calls (ND-860228.2 EN), page 399.