Object entry bytes 32-35, and the OWN access tier - settled from ND manuals¶
Date: 2026-07-30
Scope: read-only research. No source file was modified.
Status of the answer: all four questions are answered, three of them VERIFIED
against two independent official ND manuals plus a real annotated @DUMP-OBJECT-ENTRY
listing printed in one of them.
This document does not edit
object-entry.md. It contradicts that file in two places and
those contradictions are called out in section 6 for a human to reconcile.
1. The question¶
object-entry.md records the 64-byte object entry with these four bytes unsettled:
| byte | current claim there | verdict there |
|---|---|---|
| 32 | file-type code (0 DATA, 1 PROG, 2 SYMB, 3 TEXT) | VERIFIED |
| 33 | unexplained | OPEN |
| 34 | owning user index | VERIFIED |
| 35 | file slot low byte, word 34-35 = [user | slot] |
INFERRED |
Asked: (1) what does SINTRAN write into 34-35, (2) what is byte 33, (3) is there a per-file reserving user, (4) is the OWN access tier 3 bits or 5 bits.
2. What was searched¶
E:\Dev\Ronny\NDInsight\SINTRAN\NPL-SOURCE\- the 45 NPL files and the 3 symbol sets. Searched forOBJENT,OBJE,OBJIN,OBIND,USIND,FILNO, file index. Result: no object-entry field layout. The FILSYS symbol tables list the routines (CROBJ=063726,DLOBJ=064146,WOBJE=055750,COBJE=061502,ROBJE=055566) but carry no field-offset symbols. The NPL tree is monitor part 2 and does not contain the file-system module source. SeeE:\Dev\Ronny\NDInsight\SINTRAN\NPL-SOURCE\SYMBOLS\L07\FILSYS-SYMBOLS.SYMB.TXTlines 391, 413-416, 957, 1175, 1876, 2461, 2614.E:\Dev\Ronny\NDInsight\tools\sintran-segment-carver\versions\L-VSX-500\re\segments-ref\006-S3FS\006-S3FS.asm- read the
CROBJbody at line 15653 (address063726B) and theDLOBJbody at line 15774 (address064146B). Both are PLANC output using B-relative local frames and indirect calls through a tail pointer table, so the field stores are not directly legible without resolving the pool words. I did not extract field offsets from the disassembly and I am not going to claim any. See section 5.
- read the
E:\Dev\Ronny\NDInsight\SINTRAN\Filesystem\code-logic\s3fs-code-map.mdandE:\Dev\Ronny\NDInsight\tools\sintran-segment-carver\versions\L-VSX-500\re\mon-analysis\41B-ReadObjectEntry\README.md- routine map and the MON 41 carve. Neither documents the field layout.
E:\Dev\Ronny\NDInsight\Developer\MON\calls\215B_GetObjectEntry.yamland216B_SetObjectEntry.yaml- both say "See appendix C". That pointer is what broke the problem open.E:\Dev\Ronny\NDInsight\Reference-Manuals\ND-860228-2-EN SINTRAN III Monitor Calls.mdAPPENDIX C: FILE SYSTEM ENTRIES.E:\Dev\Ronny\NDInsight\Operations\SINTRAN\ND-30.003.007 EN SINTRAN III System Supervisor.mdappendix F.6 Object Entry, and the worked@DUMP-OBJECT-ENTRYexample in the Preventive Maintenance chapter.
3. What was found¶
3.1 Appendix C of the Monitor Calls manual - a byte-indexed layout table¶
E:\Dev\Ronny\NDInsight\Reference-Manuals\ND-860228-2-EN SINTRAN III Monitor Calls.md
line 28839 opens # APPENDIX C: FILE SYSTEM ENTRIES. The object-entry table starts at
line 28896. This table is indexed in BYTES, not words - it says so in its column
header (| BYTE | OBJECT INFORMATION |) and its rows run 0:1 through 60:63, i.e. 64
bytes. Verbatim, the rows that matter:
| 0:1 | Various, bit 15: set if object entry in use.
bit 14: set if currently opened for write.
bit 13: set if file is reserved.
bit 12: set if the file is modified.
bit 10-0: terminal number of last user opening the file. 0 if opened by RT program. |
| 26:27 | File access, bit 14-9: public access
bit 9-4: friend access.
bit 4-0: own access. |
| 28:29 | Attributes, bit 15-12: object block number
bit 8-0: logical file type
bit 8: set if temporary file. bit 7: set if library file.
bit 8: set if magnetic-tape file. [OCR error, see 3.4]
bit 5: set if allocated file. bit 4: set if contiguous file.
bit 3: set if indexed file. bit 2: set if spooling file.
bit 1: set if peripheral file. bit 0: set if terminal file. |
| 30:31 | Device number. |
| 32:33 | User index in main directory of reserving user. |
| 34:35 | Object index of this object entry. |
| 36:37 | Current open count. |
| 38:39 | Total open count. |
There is no file-type code byte anywhere in the entry.
3.2 The System Supervisor manual, appendix F.6 - the same entry, word-indexed¶
E:\Dev\Ronny\NDInsight\Operations\SINTRAN\ND-30.003.007 EN SINTRAN III System Supervisor.md
line 16149 # F.6 Object Entry. Its word table (line 16157 onward) is OCR-damaged:
the word-number column mixes octal and decimal, and the values 22B and 24B each
appear twice. Do not use that column. Two things in F.6 are clean and decisive:
Line 16155:
Each user area can have up to 4096 files, divided into object blocks of 256 files each. That means each directory can have 256 user areas * 4096 files = 1,048,576 files.
Line 16247, the note on the version-pointer words:
### THE 3 FILE VERSION POINTERS:
| Bit No. | |
| 15 8 7 0 | (Dec.) |
| User Index | Object Index |
- The object index is relative to the object block.
Line 16241, the note on the preceding word:
### LAST RESERVING USER:
| Bit No. | |
| 15 8 7 0 | |
and line 16169 names that word LAST RESERVING USER.
3.3 A real annotated @DUMP-OBJECT-ENTRY listing¶
Same file, lines 13697-13725. This is the strongest single piece of evidence: an
actual octal dump of USER 000 OBJECT ENTRY 025 with ND's own per-word labels.
Reproduced in full:
110001 U/W/R/M TERM. NO OF RESERVING USER
044105 047124 026515 047504 042447 000000 000000 000000
HENT-MODE
051531 046502 TYPE (SYMB)
000025 000025 POINTERS TO NEXT AND PREVIOUS VERSION
002377 ACCESS WORD
000040 OBJBL (BITS 017-014)/TEMP/L/M/A/C/I/S/P/T
000000 DEVICE NUMBER
000000 MAIN DIR INDEX / USER INDEX OF RESERVING USER
000025 OBJECT INDEX OF THIS ENTRY
000001 000004 CURRENT AND TOTAL OPEN COUNT
106561 001127 DATE CREATED (1985.5.24 16.09.23)
110714 162713 LAST DATE OPENED FOR READ (1986.1.30 14.23.11)
110714 162713 LAST DATE OPENED FOR WRITE (1986.1.30 14.23.11)
000000 000001 PAGES IN FILE
000000 003015 MAX. BYTE POINTER
040000 044765 FILE POINTER
Count the words: 1 header + 8 name + 2 type + 2 version pointers + 1 access + 1 attributes + 1 device + 1 reserving user + 1 object index + 2 open counts + 6 dates + 2 pages + 2 max byte pointer + 2 file pointer = 32 words = 64 bytes. The word order matches Appendix C's byte order exactly, position for position. The two manuals are independent and agree.
3.4 The answers¶
Q1 - bytes 34-35. The word is the object index of this object entry, split
[bit 15-8 = user index | bit 7-0 = object index relative to the object block].
VERIFIED - Appendix C line 28907 (34:35 Object index of this object entry) for
the field identity; System Supervisor line 16247 for the byte split; line 13707
for ND's own label on a real dump.
The existing object-entry.md reading [user | file-slot] is therefore correct in
shape, and the "INFERRED" tag on byte 35 can be upgraded, with one important
correction: byte 35 is the object index within the 256-entry object block, not a
global slot. The full file number that @DUMP-OBJECT-ENTRY prompts for
(FILE NO. (0-07777), line 13699, i.e. 0 to 4095) is
objectBlockNumber * 256 + byte35, where objectBlockNumber is bits 15-12 of the
attributes word at bytes 28-29 (Appendix C line 28903, and the dump label
OBJBL (BITS 017-014) at line 13705 - octal bits 17-14 are decimal bits 15-12).
Cross-check against the real PACK-ONE bytes already quoted in object-entry.md
section 1: SINTRAN has bytes 34-35 = 0000, MACM-AREA = 0001, SEGFIL0 = 0002.
They are objects 0, 1 and 2 of user SYSTEM (user index 0, object block 0). The
version-pointer words at 22-25 hold the same values (0000/0000, 0001/0001,
0002/0002), which is exactly the self-linked single-version chain the same
manual describes at line 13799 ("They should be set to the same value as this
version pointer"). Consistent. VERIFIED.
Q2 - byte 33. It is the user index of the reserving user, the low half of the
LAST RESERVING USER word at bytes 32-33. VERIFIED as a field
(Appendix C line 28906; System Supervisor lines 16169 and 13707).
Byte 32 is the main directory index of that reserving user. The high/low split of
the word is VERIFIED (F.6 line 16241 shows the word divided at 15 8 7 0; the
dump label at line 13707 is the two-part MAIN DIR INDEX / USER INDEX OF RESERVING
USER). Which half holds which is INFERRED but strongly supported: the same
Monitor Calls manual packs a directory/user index pair the same way for MON 215B and
216B - LDT INDEX %Left byte: Dir index. Right byte: User index.
(E:\Dev\Ronny\NDInsight\Developer\MON\calls\215B_GetObjectEntry.yaml, the mac
example). Left byte is the high byte. So byte 32 = directory index, byte 33 = user
index.
Byte 32 is NOT a file-type code. Neither manual has a file-type code byte anywhere in the entry, and the annotated dump accounts for all 32 words with none left over. See section 6.
Q3 - is a reserving user stored on disk? Yes. Refuted. The previous search
concluded there is no per-file reserving user in the entry. There is: the word at
bytes 32-33, plus a matching flag - Appendix C, byte 0 bit 13 "set if file is
reserved" - plus F.6's own key at line 16197 (R = Reserved peripheral file). The
earlier reasoning (RESERVE-FILE applies to peripheral files and is dropped at
logout) is not wrong about the semantics; it is wrong that it leaves no disk trace.
Note the field is the last reserving user (F.6's name for it), so it is a stale
record, not a live lock. VERIFIED as a field. Its exact lifecycle is UNKNOWN -
nothing found says when SINTRAN clears it.
Q4 - OWN access tier width: 5 bits. VERIFIED, arithmetically.
The dump at line 13703 gives a real access word for a :SYMB file:
002377 octal = 1279 decimal = 0 0100 1111 1111 binary.
| tier | bits | value | meaning |
|---|---|---|---|
| OWN | 4-0 | 11111 = 0x1F |
R W A C D, all five |
| FRIEND | 9-5 | 00111 = 0x07 |
R W A |
| PUBLIC | 14-10 | 00001 = 0x01 |
R |
That is a coherent permission set for a source file. Under a 3-bit OWN tier the
same word decodes to nothing sensible. The 5-bit tier width is independently
corroborated by the user entry friend table, which the same Appendix C
(line 28866) spells out bit by bit: bit 12 directory, bit 11 common, bit 10 append,
bit 9 write, bit 8 read - five bits, and relative to that field's base the order is
0=R, 1=W, 2=A, 3=C, 4=D, exactly the R/W/A/C/D bit assignment object-entry.md
section 4.2 currently marks INFERRED. That INFERRED tag can be upgraded to
VERIFIED.
Appendix C's own text for the access word ("bit 14-9 public, bit 9-4 friend, bit 4-0 own") is internally inconsistent - it overlaps at bits 9 and 4 and gives two 6-bit fields. The tier boundaries 14-10 / 9-5 / 4-0 are what the arithmetic above requires. Treat the manual's bit numbers here as OCR or typesetting damage.
So: AccessBits & 0x1F is right, and the adjacent "bits 2-0" comment is wrong.
3.5 Two bonus findings, both of which close OPEN items in object-entry.md¶
Byte 0 flag bits (section 4.1 of the old doc, marked OPEN). Appendix C line 28899
gives them: bit 15 in use, bit 14 open for write, bit 13 file reserved, bit 12 file
modified, bits 10-0 terminal number of the last user to open the file (0 if opened by
an RT program). This explains SEGFIL0's header 0x90 = bit 15 + bit 12 = in use +
modified. The dump listing confirms it live: header 110001 octal = 0x9001 -
identical high byte, plus terminal number 1 in the low bits. The other sample header
in the same manual, 150001 (line 13670) = 0xD001 = in use + open for write +
modified, terminal 1. VERIFIED.
Bytes 28-29 (attributes). object-entry.md section 4.3 models only bits 7-0.
Two fields are missing from every implementation: bits 15-12 = object block
number and bit 8 = temporary file. Both manuals agree
(Appendix C line 28903; dump label OBJBL (BITS 017-014)/TEMP/L/M/A/C/I/S/P/T at
line 13705). The dump's letter order maps bit 8 down to bit 0 as
TEMP, L(ibrary), M(agnetic tape), A(llocated), C(ontiguous), I(ndexed), S(pooling),
P(eripheral), T(erminal) - which matches the NDFS bit assignments for bits 7-0, and
shows NDFS's letter "B" for bit 2 should be S for spooling. Appendix C's line
"bit 8: set if magnetic-tape file" repeats bit 8 and is an OCR error for bit 6; the
dump's positional letter list is the reliable one. Sample value 000040 octal = bit 5
= Allocated, matching the three PACK-ONE entries. VERIFIED.
Bytes 56-59. The dump labels this word pair MAX. BYTE POINTER, not "bytes in
file". That is the same thing as NDFS's stored-value-plus-one convention and does not
change any decode, but it is the field's real name.
4. What remains unknown¶
- When SINTRAN clears the reserving-user word at bytes 32-33. Not found.
- Whether byte 32 or byte 33 holds the directory index. INFERRED from the MON
215B/216B
INDEXregister convention, not read from file-system code or a dump with a non-zero value. Every sample available (PACK-ONE entries, the manual's dump) has the whole word zero. - The actual store instructions in
CROBJ/COBJE/WOBJE. Not traced. The question "what does SINTRAN itself WRITE" is answered here from ND's own format documentation and a real dump, not from the code. That is a different class of evidence and I am flagging it rather than dressing it up. - Bytes 22-25 version-chain semantics remain as
object-entry.mdhas them, now with the added fact that those two words use the same[user index | object index]packing (F.6 line 16247 calls them, together with bytes 34-35, "THE 3 FILE VERSION POINTERS"), and that all versions of a file live in the same object block (F.6 line 16294).
5. A note on the disassembly¶
I opened 006-S3FS.asm at CROBJ (063726B, line 15653) and DLOBJ (064146B,
line 15774) and did not get field offsets out of them. Both are PLANC compiler output:
the entry sequence is STD I n / RADD CLD SL DA / RADD CLD SB DD / SAB k, after
which nearly every memory reference is B-relative into a local frame and every call is
JPL I through a pointer table sitting past the code body. DLOBJ's X-relative loads
(LDA ,X 26, LDD ,X 64, LDD ,X 60, LDA ,X 41, LDA ,X 42, LDA ,X 50) reach
displacements above 37 octal, so X there is not pointing at a 32-word object entry
and those offsets must not be read as object-entry fields. Resolving them needs the
pool words traced, which I did not do. Recorded here so nobody re-derives a wrong
field table from those loads.
6. Are any of the four implementations wrong?¶
Yes. All four share one wrong field, and the C# has a second one.
6.1 Byte 32 read as a file-type code - WRONG in all four¶
| file | line | code |
|---|---|---|
E:\Dev\Ronny\norskdata-ndfs\ndfs-c\src\object_entry.c |
65 | out->file_type = data[offset + 32]; |
E:\Dev\Ronny\norskdata-ndfs\ndfs-py\src\ndfs\object_entry.py |
180 | entry.file_type = data[offset + 32] |
E:\Dev\Ronny\norskdata-ndfs\ndfs-ts\src\object-entry.ts |
146 | entry.fileType = data[offset + 32]; |
E:\Dev\Ronny\RetroFS\src\RetroFS.NDFS\Elements\ObjectEntry.cs |
271 | entry.FileType = data[offset + 32]; |
Byte 32 is the main directory index of the reserving user. The 0=DATA / 1=PROG /
2=SYMB / 3=TEXT mapping (ObjectEntry.cs lines 167-176, and the equivalents) is not
a SINTRAN field. It reads correct on ordinary files only because an unreserved file
has zero there and zero maps to "DATA". The real file type is the 4-character text at
bytes 18-21, which all four already read correctly.
Practical impact: any file that has ever been reserved by a user on a non-zero
directory would be reported with a bogus type code. On the write path the C# is worse
than the readers: ObjectEntry.cs line 412 writes buffer[offset + 32] = (byte)FileType;
unconditionally, so a round trip through the C# destroys the reserving-user
directory index. The C, Python and TypeScript ports base their output on the
preserved raw bytes, which limits the damage to entries built from scratch.
6.2 UserIndexOfReservingUser reading byte 34 - WRONG in the C¶
E:\Dev\Ronny\RetroFS\src\RetroFS.NDFS\Elements\ObjectEntry.cs line 299:
entry.UserIndexOfReservingUser = data[offset + 34];
The property name is right and the offset is wrong; it should be byte 33. The
comment block above it (lines 286-298) states that no reserving-user field exists in
the entry, that byte 33 is the one remaining open byte, and that this property is an
alias for UserIndex. All three statements are refuted by Appendix C. Byte 33 is
the reserving user. The property was correctly named and then wired to the wrong byte.
6.3 What the four get right¶
disk_object_index/diskObjectIndexread as the big-endian word at 34-35 (object_entry.cline 67,object-entry.tsline 150) is correct.- Reading byte 34 as the owning user index is correct - it is the high half of that word.
- Reading byte 35 as the file slot is correct in shape, but it is the object index
within the object block, so the full file number needs bits 15-12 of the word at
bytes 28-29 added as
block * 256. No implementation reads that field. AccessBits & 0x1Ffor the OWN tier is correct (see 3.4 Q4).- No implementation models byte 0 bits 14/13/12, attributes bits 15-12 and bit 8, or the reserving-user word. These are omissions, not decode errors, and the raw-bytes preservation in the C/Python/TypeScript ports keeps them safe on a round trip.
7. Contradictions with the existing object-entry.md¶
Left unreconciled by design. Two rows of its section 2 table and the matching prose in section 4.4 and 4.5 disagree with this document:
- Byte 32 is marked VERIFIED as the file-type code. It is not a file-type code.
The "verification" in section 4.4 - byte 32 = 0 and type text
DATAagreeing on all three PACK-ONE samples - is a coincidence: all three samples are unreserved SYSTEM files on the main directory, so byte 32 is 0, and 0 happens to be the DATA code in the invented mapping. Three samples that are all zero cannot distinguish the two hypotheses. - Byte 33 is marked OPEN. It is the user index of the reserving user.
Section 4.5's reading of bytes 34-35 as [user | file-slot] is confirmed, and its
INFERRED tag on byte 35 and on the R/W/A/C/D letter-to-bit map in section 4.2 can both
be upgraded to VERIFIED, with the object-block qualification in 3.4 Q1 added.
8. Sources¶
| what | full path | locator |
|---|---|---|
| Appendix C, object entry byte table | E:\Dev\Ronny\NDInsight\Reference-Manuals\ND-860228-2-EN SINTRAN III Monitor Calls.md |
appendix at line 28839, object table at line 28896, user entry at line 28866 |
| Appendix F.6, object entry | E:\Dev\Ronny\NDInsight\Operations\SINTRAN\ND-30.003.007 EN SINTRAN III System Supervisor.md |
line 16149; version-pointer split line 16247; reserving-user line 16241 |
Real annotated @DUMP-OBJECT-ENTRY |
same file | lines 13697-13725 (second example), 13667-13671 (first example) |
| MON 215B/216B, "see appendix C", INDEX byte packing | E:\Dev\Ronny\NDInsight\Developer\MON\calls\215B_GetObjectEntry.yaml, 216B_SetObjectEntry.yaml |
mac example |
| FILSYS symbols | E:\Dev\Ronny\NDInsight\SINTRAN\NPL-SOURCE\SYMBOLS\L07\FILSYS-SYMBOLS.SYMB.TXT |
lines 391, 413-416, 957, 1175, 1876, 2461, 2614 |
| S3FS disassembly (opened, no field data extracted) | E:\Dev\Ronny\NDInsight\tools\sintran-segment-carver\versions\L-VSX-500\re\segments-ref\006-S3FS\006-S3FS.asm |
CROBJ line 15653, DLOBJ line 15774 |
| Existing format notes | E:\Dev\Ronny\NDInsight\SINTRAN\Filesystem\on-disk-format\object-entry.md |
sections 2, 4.1-4.5 |
| Implementations | E:\Dev\Ronny\norskdata-ndfs\ndfs-c\src\object_entry.c (65), E:\Dev\Ronny\norskdata-ndfs\ndfs-py\src\ndfs\object_entry.py (180), E:\Dev\Ronny\norskdata-ndfs\ndfs-ts\src\object-entry.ts (146), E:\Dev\Ronny\RetroFS\src\RetroFS.NDFS\Elements\ObjectEntry.cs (271, 299, 412) |
as noted |