50B OpenFile¶
Validated MON 50B (40 decimal) · Mnemonic OPEN · Group: File Operations (manual section 2.4)
Available from: All programs (manual compatibility box)
Emulation source: src/handlers/mon_50B_OpenFile.c
Description¶
Opens a file. You cannot access a file before you open it. Specify what kind of access you want, e.g. sequential write or random read.
Notes¶
- CloseFile closes the file.
- Opened files are closed when a program terminates.
- You may have a maximum of 18 files opened at a time.
- Files are protected from access by unauthorized users. Use @FILE-STATISTICS to list the file access.
- ND-500 has its own file numbers which may differ from the SINTRAN III file numbers.
Parameters¶
| Name | Type | Direction | Description |
|---|---|---|---|
FileNo |
INTEGER | In/Out | If 0 on input, returns the ND-500 open file number. Otherwise specifies the file number to use. |
AccessCode |
INTEGER | In | Access code specifying type of file access: 0 = Sequential write 1 = Sequential read 2 = Random read or write 3 = Random read only 4 = Sequential read or write 5 = Sequential write append 6 = Random read or write common on contiguous files 7 = Random read common on contiguous files 8 = Random read or write on contiguous files (direct transfer for RT programs) 9 = Random read, write append for WriteToFile |
FileName |
STRING | In | File name string (up to 64 characters). If empty, name is read from terminal. |
FileType |
STRING | In | Default file type string (up to 4 characters), e.g. 'SYMB'. |
Direction: In = the program supplies the value, Out = the call returns it, In/Out = both.
See also¶
ScratchOpen, SetPermanentOpen, DirectOpen, and @OPEN-FILE
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 | No | No | No |
Examples¶
From the manual (OCR text, not corrected).
INTEGER : FileNo, AccessCode
BYTES : FileName(0:63), FileType(0:3)
...
ON ROUTINEERROR DO
IF ErrCode > 0 THEN ...
ENDON
0 =: FileNo
Monitor_Call('OpenFile', FileNo, AccessCode, FileName, FileType)
INTEGER FileNo, AccessCode
CHARACTER FileName*64, FileType*4
FileNo, AccessCode : INTEGER;
FileName : PACKED ARRAY [0..63] OF CHAR;
FileType : PACKED ARRAY [0..3] OF CHAR;
...
OpenFile(FileNo, AccessCode, FileName, FileType);
IF ErrCode <> 0 THEN ...
01 FileNo COMP.
01 AccessCode COMP.
01 FileName PIC X(64).
01 FileType PIC X(4).
01 ErrCode COMP.
MOVE 0 TO FileNo.
MONITOR-CALL "OpenFile" USING FileNo, AccessCode, FileName, FileType.
CALL "CbError" USING ErrCode.
IF ErrCode NOT = 0 GO ...
| Variable | Type | Description |
|------------|-----------|------------------------------------------------------------|
| FileNo | W BLOCK 1 | %Returned ND-500 open file number if 0 on input. |
| S3No | W BLOCK 1 | %SINTRAN III open file number as optional parameter. |
| AccessCode | W BLOCK 1 | |
| FileName | STRINGDATA| 'EXAMPLE' |
| FileType | STRINGDATA| 'SYMB' |
| ErrCode | W BLOCK 1 | |
| OpenFile | EQU 37B9 + 50B | |
...
CALLG OpenFile, 4, FileNo, AccessCode, FileName, FileType
IF K GO ERROR
...
ERROR = W1 =: ErrCode %ErrorCode in W1 register.
LDX (FILE %If 0, the name is read from the terminal.
LDA (TYPE %Address of default file type string.
LDT ACCES %Access code.
MON 50 %Monitor call OpenFile.
JMP ERROR %Error return from monitor call.
STA FILNO %Normal return, store the file number returned.
...
ERROR, ... %Error number in register A.
FILNO, 0
ACCES, ...
FILE, 'EXAMPLE' %Open EXAMPLE:SYMB
TYPE, 'SYMB' %
ndmonlib implementation¶
Validated Registered MON_STATUS_VALIDATED in src/core/mon_registry.c: implemented, tested and working.
| Handler | mon_50B_OpenFile |
| Code lines | 65 (non-blank, non-comment lines in the file) |
Notes from the handler source¶
src/handlers/mon_50B_OpenFile.c line 49
Parameter order per MASTER reference (ND-860228.2 EN):
[0] FileNo (IO) - If 0, returns allocated file number; otherwise use specified
[1] AccessCode (I)
[2] FileName (STRING)
[3] FileType (STRING)
src/handlers/mon_50B_OpenFile.c line 62
Read filename string from memory (up to 64 chars per MASTER reference)
High-level languages (FORTRAN/Pascal) pass string descriptors [Length:4][Pointer:4]
src/handlers/mon_50B_OpenFile.c line 84
Open file via file table API
Per MASTER reference: If FileNo is 0, allocate new; otherwise use specified number
src/handlers/mon_50B_OpenFile.c line 98
Map internal error codes to SINTRAN error codes.
EVERY code mon_file_open_ex can return must appear here. Anything
that falls through to the default is reported to the guest as 056B
"No such file name", which is a lie about what happened and sends
whoever is debugging it looking for a missing file. That is exactly
what happened with -62: a quoted create over an existing file is
detected correctly in mon_file_table.c (returns -62 "file already
exists"), but with no case for it here the guest saw 056B, and
CONVERT-DOMAIN's failure to create its destination read as a
name-resolution problem instead of the destination simply being
there already. -47 had the same defect.
Note the return codes are NOT uniformly negated SINTRAN codes:
-46/-47/-62 happen to be, but -52/-54/-55 are internal numbers that
map to unrelated SINTRAN values. Do not "simplify" this to a
negation.
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_50B_OpenFile.c |
| Last updated | 2026-07-17 |
Supporting code¶
nd500x/src/libmon/mon_file_table.c
Parameter notes¶
1. FileNo
| 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 each INTEGER parameter as 'W BLOCK 1' (not 'H BLOCK'), so the handler reads/writes all four parameters with word accessors. |
| Verified | Yes |
2. FileNo
| Field | Value |
|---|---|
| Note | The handler treats FileNo as BIDIRECTIONAL exactly as the manual's io: IO says: 0 on input allocates, non-zero uses the given number, and the assigned number is written BACK into the parameter slot as well as left in W1. Callers observed reading either channel. |
| Verified | Yes |
3. FileName
| Field | Value |
|---|---|
| Note | Real callers (NC, the ND linker) pass the name as a [Length:4][Pointer:4] string DESCRIPTOR, not as inline text. Reading the parameter slot directly yields the descriptor struct (first byte 0x00) and so an empty string. nd500x uses mon_read_descriptor_string here. |
| Verified | Yes |
4. FileName
| Field | Value |
|---|---|
| Note | The guest string is terminated by 047 octal (0x27, apostrophe). The descriptor reader converts that to a C NUL before the name reaches the matcher, so NUL is the terminator inside the matcher. |
| Verified | Yes |
Abbreviated file name resolution¶
- Summary: SINTRAN resolves an ABBREVIATED file name on OPEN. This is not a convenience feature - the ND linker depends on it. Implemented in nd500x byte-for-byte from the carve; hooked ONLY after a literal open fails, and ONLY on a READ open (SINTRAN does not permit abbreviation when creating a file).
-
Source
- Carve: L-VSX-500 segment 006-S3FS
- Comparator: COMPS @041552
- Scanner decision: GOBJI @056326 (terminal codes 056576-056607)
- Carve doc: NDInsight/tools/sintran-segment-carver/versions/L-VSX-500/re/segments-ref/006-S3FS/CARVE-ANSWER-FLPAR-MDEAB-FOR-IMPLEMENTER.md
-
Comparator comps
-
Note: Compares a WHOLE supplied name against a WHOLE stored name in one pass and returns three states (NO_MATCH / PREFIX_MATCH / EXACT_MATCH). The '-' handling yields per-subpart behaviour implicitly - there is no separate subpart splitter.
-
Rules
Rule Address equal chars advance; both terminators together -> EXACT_MATCH 041603 / 041612 (advance at 041606) '*' in the supplied name is a PER-CHARACTER wildcard 041600 / 041661 supplied terminator reached first -> PREFIX_MATCH (supplied is a prefix of stored) 041616-041620 '-' in the supplied name (including the empty middle of '--') skips the STORED name forward to its next '-' boundary, so that slot matches ANY value 041621-041706 anything else -> NO_MATCH 041621 -> 041623 -> 041614
-
Scanner gobji
- Note: Scan the directory. An EXACT match wins outright and stops the scan immediately. Otherwise count PREFIX hits: 0 -> error 056 (no such file), more than 1 -> error 057 (ambiguous), exactly 1 -> unique, resolve to it.
- Address: 056326
-
Name type split
-
Note: Name and type are matched as SEPARATE strings, per the NAME:TYPE framing split (SEPOB @056645 / SEPFS @042622). Do NOT match the joined string.
- Verified: Yes
Observed calls¶
1. ND linker (linker-b01.dom), startup
| Field | Value |
|---|---|
| Params | Filename: 'UE-ERMSG--C:ERR' |
| Expectation | Must resolve to the real on-disk file UE-ERMSG-EN-C06:ERR - the empty slot in '--' swallows 'EN', and supplied-is-prefix takes '-C' to '-C06'. Observed: "MON OPEN: './GUEST/UE-ERMSG--C.ERR' resolved to './GUEST/UE-ERMSG-EN-C06.ERR'" -> opened as file 101. Without abbreviation the literal fopen fails and OPEN returns -46, and the linker never finds its error-message file. |
2. ND linker (linker-b01.dom), DDB table load
| Field | Value |
|---|---|
| Params | Filename: 'DDBTABLES-G':VTM |
| Expectation | Must resolve to DDBTABLES-G06:VTM (supplied-is-prefix-of-stored). This is how the linker loads its DDB tables at all; it then 74B SETBTs to 0 and reads the file via 144B MAGTP function 0. |
3. ND linker (linker-b01.dom), first startup OPEN
| Field | Value |
|---|---|
| Params | Filename: '' (empty) |
| Expectation | Observed: the first 50B OPEN the linker issues carries an EMPTY filename. nd500x mis-resolves it to './GUEST/.' and fails with -46, which cascades into 74B SETBT (invalid file 56) and 76B SETBS (block size 0). See discrepancies - the manual says an empty name means "read the name from the terminal", which nd500x does not implement. |
Return contract¶
- Success: K flag cleared; the assigned file number is returned BOTH in W1 and written back into the FileNo parameter slot (arg 0).
-
Errors
Code Octal Meaning 46 056B No such file name (also: abbreviation resolved to 0 candidates) 47 057B Ambiguous file name (abbreviation resolved to more than 1 candidate) 69 105B File already open 71 107B Attempt to open too many files 111 157B Missing parameter (fewer than 4 args) 124 174B Illegal parameter (AccessCode > 9)
Verified¶
1. The abbreviated-name matcher (COMPS/GOBJI) is implemented as carved and resolves the linker's real names.
| Field | Value |
|---|---|
| Evidence | Carve L-VSX-500 006-S3FS COMPS @041552 / GOBJI @056326, each branch cited to its octal address in nd500x/src/libmon/mon_file_table.c. Runtime: 'UE-ERMSG--C:ERR' -> 'UE-ERMSG-EN-C06:ERR' (opened as file 101) and 'DDBTABLES-G:VTM' -> 'DDBTABLES-G06:VTM'. Unit tests 9/9. Commit badb3c4. |
2. Abbreviation is attempted only after the literal open fails, and only on a READ open.
| Field | Value |
|---|---|
| Evidence | SINTRAN does not permit abbreviation when creating a file; hook site is mon_file_table.c mon_file_open_ex, guarded on !fp and a non-write mode. |
3. Mass-storage file numbers occupy 64-127 decimal (0100-0177 octal).
| Field | Value |
|---|---|
| Evidence | Used consistently by every file handler (is_mass_storage_file) and matches the manual note "ND-500 has its own file numbers which may differ from the SINTRAN III file numbers"; the linker's OPEN returned 65 and 101 in observed runs. |
Unverified¶
- COMPS edge: when the STORED name runs out of subparts before the SUPPLIED name (i.e. the supplied name has MORE subparts than the stored one), the carve did not trace the branch. nd500x treats it as NO_MATCH. The verified common path is unaffected. Carried from the handler comment.
- Because EXACT short-circuits the scan, 'A--C' can take 'A-XX-C' without ever noticing that 'A-YY-C' also matches (so no ambiguity is reported). That is what the carved code does; whether the real system reaches the same outcome for every directory ordering is not proven.
- The manual states "You may have a maximum of 18 files opened at a time". nd500x does not enforce 18; it errors 107B only when its own table is full. Whether the real limit is exactly 18 on ND-500 has not been carve-checked.
- The manual's "If empty, name is read from terminal" behaviour is NOT implemented (see discrepancies).
- Access codes 6/7/8 (contiguous / direct transfer) are accepted and recorded but nd500x has no contiguous-file or direct-transfer model behind them.
Discrepancies¶
1. The SINTRAN ambiguous-file-name code is 0111.
| Field | Value |
|---|---|
| Is | Ambiguous is 057 and no-such is 056. The 0111/0113 codes belong to a SEPARATE access/type classifier that runs AFTER a unique resolve. Any implementation with 0111 wired as "ambiguous" is wrong. |
| Evidence | Carve GOBJI @056326 with terminal codes at 056576-056607; corrected in commit badb3c4 and in nd500x/docs/HANDOFF_CSHARP_FILE_TABLE_SINTRAN_SEMANTICS.md. |
2. An earlier belief in this tree that the resolver returns "A=0 unique / A=-2".
| Field | Value |
|---|---|
| Is | That pair is really the SPUSH/SPOP skip-count, not a result code. |
| Evidence | Carve read recorded in commit badb3c4. |
3. Emulator defect (now fixed): the -52 illegal-parameter case fell through to the no-such-file-name error because of a missing break.
| Field | Value |
|---|---|
| Is | Illegal parameter now returns 174B distinctly. |
| Evidence | Commit 2618145. |
4. Manual parameter text: "FileName ... If empty, name is read from terminal."
| Field | Value |
|---|---|
| Is | NOT implemented in nd500x. An empty name is passed through to the path translator, which produces './GUEST/.' and fails with -46. The manual field is left untouched - the manual is right and the emulator does not implement this. |
| Evidence | Read from the nd500x path-translation code. NO program we run is known to exercise it, so the divergence is currently theoretical. |
| Correction | CORRECTED 2026-07-17. An earlier draft of this entry claimed "the ND linker DOES issue an empty-name OPEN at startup, so this behaviour is reachable in practice". That was read from a trace taken BEFORE the smove fix (nd500x commit e9111dd) and is now FALSE. The empty name was a SYMPTOM: the ND-500 STRING class was a silent no-op, so the linker's "by smove" never filled its file-name buffer and it opened garbage. With smove fixed, the linker opens 'DDBTABLES-G':VTM and issues ZERO empty-name OPENs (re-measured: 0 occurrences over a 400,000-instruction run). Do not chase this - it is not a live path. |
Sources¶
1. SINTRAN III Monitor Calls (ND-860228.2 EN)
| Field | Value |
|---|---|
| Page | 369 |
| Note | The manual page this YAML was extracted from. |
2. nd500x/docs/HANDOFF_CSHARP_FILE_TABLE_SINTRAN_SEMANTICS.md
| Field | Value |
|---|---|
| Note | Full write-up of the matcher and the device model. Item 1. |
3. NDInsight/tools/sintran-segment-carver/versions/L-VSX-500/re/segments-ref/006-S3FS/CARVE-ANSWER-FLPAR-MDEAB-FOR-IMPLEMENTER.md
| Field | Value |
|---|---|
| Note | COMPS/GOBJI carve answer. |
4. badb3c4 (nd500x) - SINTRAN abbreviated file-name resolution on OPEN
| Field | Value |
|---|---|
5. 2618145 (nd500x) - 50B missing break
| Field | Value |
|---|---|
Updates 2026 07 17¶
1. File-name QUOTES convention = create (byte-verified)
| Field | Value |
|---|---|
| Status | verified |
| Detail | SINTRAN III has NO write-open-creates. A file name in DOUBLE QUOTES means "create if absent"; without quotes the file must already exist. Carve 006-S3FS: the resolver GCFIL @064670B tests the first char for '"' (042B) at 064677B and dispatches CROBJ @063726B (create) for quoted, GFILI @057173B (lookup-only) for unquoted. The quotes are STRIPPED by the copy loop; there is no create flag - quoted and unquoted take different routines. Reached from MON 50B via MCTAB[50B]=103034B=OPFIL -> FCON@067002B -> FFILE@065144B. |
| Rules | QUOTED + absent -> create (indexed file), version defaults to 1. QUOTED + already exists -> error 076B "File already exists" (62 dec). NOT truncated, overwritten, or opened-as-is (SAA 76 at 064040B). UNQUOTED + missing -> error 056B "No such file name" (46 dec), for EVERY access code 0..9. GFILI has no create call at all. ":TYPE" scratch syntax -> created on open by definition. Quote decision is purely lexical and happens before access-code processing; access 0 (sequential write) does NOT imply create. A quoted (USER) prefix: SEPUS skips one leading quote before the user; the quote wraps the whole spec. |
| Linker note | The ND linker relies on this: OPEN-DOMAIN "NAME" (quoted) creates a new domain; OPEN-DOMAIN NAME (unquoted) opens an existing one and errors 41B (its own code) if absent. The quotes arrive at MON 50B as literal name characters (FileName='"A-TEST"'). |
| Discrepancy | Was: nd500x auto-created any write-access open of a missing file. Is: Non-standard - removed 2026-07-17 (commit c61b039). Create now gated on quoted-or-scratch; unquoted-missing returns -46. |
| Unverified | CROBJ also creates when GOBJI lookup returns T==0 ("object entry exists but no version"?) - exact meaning of GOBJI T not chased. |
| Source | NDInsight/SINTRAN/ND500/CARVE-ANSWER-OPEN-QUOTED-FILENAME.md; ND-60.050.06 Users Guide p1422; ND-60.128.5 Ref Manual ~2054 |
Source¶
SINTRAN III Monitor Calls (ND-860228.2 EN), page 369.