DESCRIPTION-FILE:DESC Format (Old ND-500(0) Domain Format Index)¶
DESCRIPTION-FILE:DESC is a per-SINTRAN-user index file used by the old ND-500
domain format. In that format a domain's content is split across NAME:PSEG (program
code), NAME:DSEG (data), and NAME:LINK (symbol/link info), none of which know about
each other. DESCRIPTION-FILE:DESC is what lets the old Linkage-Loader (NLL), the
ND-500 Monitor, and the CONVERT-DOMAIN migration program resolve a bare domain
name to its actual files and bookkeeping (sizes, load addresses, segment numbers). See
DOM-FILE-FORMAT.md for the newer, self-contained format this was
replaced by (and NRF-FILE-FORMAT.md for the object format the
Linker/NLL builds domains from, which is a separate concern from :DESC).
Primary sources, in order of authority:
- The ND-500 Monitor's own code -
MON-DEBUG:PROG(ND-500/5000 Loader/Debug Monitor J04), carved 2026-08-11. It reads this file and prints every segment-entry field with the field's own name beside it, which pins each offset. Full carve:../ND500/nd-500-mon/CARVE-ANSWER-DESC-FIELD-OFFSETS-2026-08-11.md. - Real files on disk - six segment entries from three independently produced product installs (NLL H02, LED B03, COBOL-85 K01) worked through in detail in section 5, then the whole 13-floppy corpus in samples/ (section 5a).
ND-60.136.04A ND-500 Loader Monitor.md(Reference-Manuals/), chapter 11 "DESCRIPTION FILE LAYOUT" - which states plainly that it "does not pretend to give a complete description": it gives field names, sizes and order, never byte offsets.
Status 2026-08-11: the layout below is resolved. The fields this document previously
marked UNABLE TO DETERMINE (PLB, PSIZE, DLB, DSIZE, DEBUGINFO) are confirmed from
the monitor's code AND match the real files exactly.
Status 2026-08-17: the domain entry (section 3) is code-proven as well, the bytes 74-84
conflict is settled in the manual's favour (section 4), and the one anomaly that used to be
open was a transcription error (section 6). What is still open: the MINPAGES/MAXPAGES
offsets and the inner layout of an ADDSGELEM element (both manual only), the meaning of the
five fields that read zero in every sample (section 4 note), a chain of more than one segment
(section 5a), and a write-side proof of the size-minus-one rule (section 5a).
1. Overall structure¶
Four areas: process entries, domain entries, segment entries, symbol entries.
The file is organised in 2048-byte pages. Each domain-entry page is a 256-byte header/bitmap followed by 32 domain entries of 56 bytes (256 + 32x56 = 2048 exactly). The ND-500 Monitor computes the position of domain entry index as
position = 56*index + 256*(index div 32 + 1)
so the first domain entry starts at byte 256. This is the monitor's own arithmetic
(013454B READ-DOMAIN-ENTRY), and it is confirmed against both real files: entries land on
0x100 and 0x138, exactly where the domain names are. It also explains the old puzzle
that the manual's domain-entry field list sums to 54 while entries are spaced 56 apart -
the entry is 56 bytes because 32 of them plus a 256-byte header fill a 2048-byte page.
Segment entries are a singly linked list, not an array. Word 0 of a domain entry holds
the file byte position of that domain's first segment entry; word 0 of a segment entry
holds the position of the next one, and 0 ends the chain. Verified in both real files:
domain SCRATCH-DOMAIN -> 0x4000, domain LINKAGE-LOAD-H02 (resp. LED-B03) ->
0x40C0, each segment entry's link then 0.
The monitor walks domain indexes 0..253 (limit 000376B at 016504B).
2. Process Entry - 1 byte¶
| Field | Size | Meaning |
|---|---|---|
| (unnamed) | 1 | Domain number of the first domain belonging to this process. |
3. Domain Entry - 56 bytes¶
All offsets are now code-proven from the monitor's own print routine (014035B-014520B in
nd-500-mon-j04.prog), each label print paired with the single field load that follows it,
same method as the segment entry. "Word" gives the ND-100 word offset off the domain-entry
buffer 037651B. Full evidence:
../ND500/nd-500-mon/CARVE-ANSWER-FOUR-OPEN-QUESTIONS-2026-08-17.md.
| Byte | Word | Field | Size | Meaning | Verified |
|---|---|---|---|---|---|
| 0 | 0 |
SEGLINK | 4 | File byte position of the first segment entry of this domain. | YES - code (014325B LDD ,X 0, label $Segptr:) + files |
| 4 | 2 |
DNAME | 16 | Domain name, 0x27 (apostrophe) terminator then zero padding. |
YES - byte-exact; print loop 014061B-014121B runs exactly 16 iterations off word 2 |
| 20 | 12B |
CHILDDOMAINS | 6 | Byte array of child-domain indexes; element count is byte 27. Max 6 fit before byte 26. | YES - 014407B AAT 12 + LBYT loop, label $Child domains : |
| 26 | 15B hi |
MOTHER | 1 | Domain index of mother domain; 0xFF = none (all root domains in all 13 samples). |
YES - 014157B LDA ,X 15 + SHR 8, label $Owner: |
| 27 | 15B lo |
CHILDINDEX | 1 | Number of child domains - the monitor prints it as Childindex: AND uses it as the CHILDDOMAINS element count. |
YES - 014176B LDA ,X 15 + SHL 8 + SHR 8 |
| 28 | 16B |
FLAG + PRIOR | 2 | One 16-bit word, not two bytes: bit 15 alton, bit 14 dinuse (set on every in-use entry in all samples), bit 13 occup; bits 5-12 = priority (Prior:). Bits 0-4 unprinted, zero in all samples. |
YES - bit tests 014441B/014472B/014505B, prior extract 014216B (w<<3)>>8 |
| 30 | 17B |
STADR | 4 | Start address (0 for SCRATCH-DOMAIN, segment-1 addresses like 0x8000004 for real programs). |
YES - 014132B LDD ,X 17, label Start address: |
| 34 | 21B |
ENABLEINT | 4 | Bitmask of enabled traps. | YES - 014261B LDD ,X 21, label $Enableint: |
| 38 | 23B |
THA | 4 | Trap handler address. | YES - 014277B LDD ,X 23, label THA: |
| 42 | 25B |
SYSENABL | 4 | Bitmask of system-enabled traps. | YES - 014312B LDD ,X 25, label Sysenable: |
| 46 | 27B |
(unprinted) | 2 | Not printed by the monitor; zero in all 13 samples. The manual's field order has no room for it, which is where its 54-vs-56-byte sum went. | code (absence) |
| 48 | 30B |
PBITMAP | 4 | Monitor label PSEG use:. Value 2 in all samples = bitmap with bit 1 set, matching STADR in segment 1 - so the bitmap reading fits. NOTE: manual order put this at byte 46; real offset is 48. |
YES - 014340B LDD ,X 30 |
| 52 | 32B |
DBITMAP | 4 | Monitor label DSEG use:. Same bitmap shape. Manual order put this at 50; real offset is 52. |
YES - 014353B LDD ,X 32 |
The 0x27 terminator is not in the manual. It was missed on the first pass because the only
name checked then, LINKAGE-LOAD-H02, is exactly 16 characters and leaves no room for it;
SCRATCH-DOMAIN (14 chars) stores ...44 4f 4d 41 49 4e 27 00.
Checked against all 13 real DESC files in samples/ (2026-08-17): every used entry parses
cleanly under this layout; MOTHER is 0xFF and CHILDINDEX 0 everywhere (no sample has a
domain tree), dinuse is set on every used entry and no unused one. The child machinery is
code-proven but sample-unexercised.
4. Segment Entry - 192 bytes¶
All multi-byte values are big-endian. "Word" columns give the ND-100 word offset used by the monitor's own code (byte offset / 2).
| Byte | Word | Field | Size | Meaning | Verified |
|---|---|---|---|---|---|
| 0 | 0 |
SEGLINK | 4 | File byte position of the next segment entry; 0 = end of chain. | YES - code + both files |
| 4 | 2 |
SNAME | 54 | (directory:user)filename, 0x27 terminator then zero padding. |
YES - byte-exact |
| 58 | 35B |
SEGTYPE | 4 | Flags indicating segment type. | manual only |
| 60 | 36B |
(flags) | 2 | A flags word the monitor prints bit by bit (015743B-016052B). |
code |
| 62 | 37B |
COMSEGNO | 2 | Number of shared (common) SINTRAN-III segments; bounds the four arrays below, max 5. Monitor's own label $Comsegno: at 015150B. |
YES - adjudicated 2026-08-17, see note |
| 64 | 40B |
COMSEGADDR | 10 | Array of 5 x uint16 (one word per common segment, not 10 loose bytes): logical address of each shared segment. Label $Comsegaddr:. |
YES - 015233B AAX 40, word loop 015220B-015245B |
| 74 | 45B |
COMSEGSIZE | 5 | Byte array [5]: size of each shared segment. Label $Comsegsize:. |
YES - 015270B AAT 45 + LBYT loop |
| 79 | - | (pad) | 1 | Between the two byte arrays; the monitor never touches it. | code (absence) |
| 80 | 50B |
N100SEGNO | 5 | Byte array [5]: actual SINTRAN-III segment numbers. Label $N100segno:. Manual order suggested 79; real offset is 80 (word-aligned). |
YES - 015324B AAT 50 + LBYT loop |
| 85 | - | (pad) | 1 | Low byte of word 52B, unprinted. |
code (absence) |
| 130 | 101B |
INDPLOG/INDDLOG | 2 | Bitfield word: Indplog: = bits 10-15 (015164B w>>10), Inddlog: = bits 5-9 (015200B (w<<6)>>11). |
YES - code |
| 132 | 102B |
ADDSGELEM | 60 | 5 elements x 12 bytes (multiply constant 6 words at 015553B), count = COMSEGNO; the monitor prints the first double of each element, label $Addsgelem:. Fills the entry to exactly 192 bytes. Manual's per-element split (ADDSEGLINK/INDDOMAIN/ADDTYPE.../LINKDATE) not yet paired. |
offsets YES, inner layout manual only |
| 88 | 54B |
PLB | 4 | Logical low bound for program segment. | YES - 014575B LDD ,X 54, label $PLB: |
| 92 | 56B |
PSIZE | 4 | Program segment size, stored as size - 1. | YES - 014610B LDD ,X 56, label Psize: |
| 96 | 60B |
DLB | 4 | Logical low bound for data segment. | YES - 014636B LDD ,X 60, label $DLB: |
| 100 | 62B |
DSIZE | 4 | Data segment size, stored as size - 1. | YES - 014651B LDD ,X 62, label Dsize: |
| 104 | 64B |
DEBUGINFO | 4 | Size of debug info. | YES - 014677B LDD ,X 64, label $Debuginfo: |
| 108 | 66B |
DLINKDATE | 4 | Labelled Dlinkdate: by the monitor. The offset is proven; the meaning is not - see the note below. |
YES - 014712B LDD ,X 66 |
| 112 | 70B |
ABSFIXAD | 2 | Absolute fixed address, if fixed. | YES - 014750B LDA ,X 70 |
| 114 | 71B |
LOWLOGFIX | 2 | Lower page number in fixed area. | YES - 014763B LDA ,X 71 |
| 126 | 77B |
PLOLOGFIX | 2 | (monitor's own label Plologfix:) |
YES - 014623B LDA ,X 77 |
| 128 | 100B |
PUPLOGFIX | 2 | (monitor's own label Puplogfix:) |
YES - 014664B LDA ,X 100 |
| ? | - | MINPAGES, MAXPAGES | - | Manual's remaining fields (probably in bytes 116-125 between LOWLOGFIX and PLOLOGFIX); the monitor does not print them, so their offsets are still manual-order only. INDIPLOG/INDDLOG and the ADDSEG block are now pinned at 130 and 132 above. | manual only |
The bytes 74-84 conflict is ADJUDICATED (2026-08-17): the manual was right. The earlier
"two counted byte strings" reading was a misreading of the loop idiom. Word 37B is not a
character count - the monitor prints it under its own label $Comsegno:, and the same count
bounds a WORD array at byte 64 ($Comsegaddr:) and a DOUBLE array at byte 132
($Addsgelem:), which no string reading can explain. The two LBYT loops print array
ELEMENTS as numbers through 013301B, a wrapper around the resident library number converter
at 172340B - the same routine that prints the domain child-domain list - not through the
string printer 013177B. All 13 sample files have COMSEGNO = 0 (floppy installs carry no
common segments), consistent either way; the code is what decides it.
Field values in the six real entries (see section 5) confirm the size fields independently of the code, so this table is supported from two directions.
DLINKDATE at +108 is zero in every real entry, including linked ones (2026-08-17). All
26 segment entries across the 13-floppy corpus in section 5a read 00 00 00 00 there,
spanning 1982 to 1989 and twelve unrelated products. DEBUGINFO at +104 does vary over the same
entries (0, 4, 157323, 201075, 311129), so this is not a case of the whole region being
unused. If +108 held a link date it should be non-zero for shipped, linked products such as
LINKAGE-LOAD-H02, LED-B03 and COBOL-85-K01. So one of these is true and the evidence does not
separate them: the field is a date but NLL never writes it in a shipped domain; or the field
is something else that the monitor merely labels Dlinkdate:. What is proven is the offset
the monitor loads, not what the value means. Treat the name as the monitor's label, not as a
decoded meaning. The same caution applies to ABSFIXAD, LOWLOGFIX, PLOLOGFIX and PUPLOGFIX,
which are zero in all 26 entries - their offsets are proven, their behaviour is untested
because no available sample exercises them.
5. The real segment entries¶
Extended 2026-08-17. A sweep of the ND archive turned up four DESC files rather than two,
covering three independently produced product installs - the NLL H02 floppy, the LED B03
floppy, and a COBOL-85 K01 floppy that had not been looked at before. In every case the
matching .pseg and .dseg files sit in the same directory, so the derived size can be
checked against the real file size rather than against another document.
| Segment | Install | .pseg |
PSIZE stored | .dseg |
DSIZE stored | DLB | DEBUGINFO |
|---|---|---|---|---|---|---|---|
| SCRATCH-SEG-01 | H02 floppy | 5 | 4 | 1029 | 1028 | 0 | 0 |
| LINKAGE-LOAD-H02 | H02 floppy | 123989 | 123988 | 2184977 | 2109142 | 75834 | 157323 |
| SCRATCH-SEG-01 | LED floppy | 5 | 4 | 1029 | 1028 | 0 | 0 |
| LED-B03 | LED floppy | 223695 | 223694 | 394525 | 394524 | 0 | 201075 |
| SCRATCH-SEG-01 | COBOL K01 floppy | 5 | 4 | 1029 | 1028 | 0 | 0 |
| COBOL-85-K01 | COBOL K01 floppy | 265213 | 265212 | 129253 | 129252 | 0 | 4 |
Eight exact matches of PLB + PSIZE + 1 = .pseg and DLB + DSIZE + 1 = .dseg, with no
exceptions. The COBOL entry matters most: those offsets were derived from the monitor's code
and checked against the H02 and LED floppies, so COBOL is a file the conclusion was not fitted
to. It agrees anyway.
Produced by nd500-dump (pcc-nd500) reading each DESC file through the domain-entry position
formula and the segment chain, with the .pseg/.dseg sizes taken from the filesystem.
Correction 2026-08-11: the LINKAGE-LOAD-H02 DSIZE-stored value above was previously
transcribed as 2,109,654 (an error, off by exactly 512). The real stored value, re-read
byte-for-byte from description-file.desc offset 0x4120 (LINKAGE-LOAD-H02 segment
entry at 0x40C0 + byte 96 = DLB, +100 = DSIZE): raw bytes 00 01 28 3a 00 20 2e d6 00 02
66 8b = DLB 0x0001283a (75834), DSIZE-stored 0x00202ed6 (2,109,142), DEBUGINFO
0x0002668b (157323). This also resolves section 6 below - it was not a format anomaly,
it was this transcription error.
5a. Corpus check across 13 vendor floppies (2026-08-17)¶
The three installs above were extended to a proper corpus: thirteen SINTRAN floppy images, each
holding its own DESCRIPTION-FILE:DESC together with the :PSEG and :DSEG files that DESC
describes. The derived sizes were compared against the sizes in each image's own directory
entry - not against extracted copies, for the reason given below.
Result: 48 size checks, 48 matches, 0 mismatches, across 24 distinct segment entries.
| Image | Product | Directory |
|---|---|---|
| ND-disk-00458 | FORTRAN-500 (1982, oldest in the set) | ND-10190D-PART1 |
| 8_nd_f0b_10177h00-1s_fe | COBOL-500-H00 | 10177H00-1S |
| ND-disk-00096 | LED-FORTRAN-A01 | 211159A01-XX-01D |
| ND-disk-00093 | FORTRAN-500-K02 | 210190K02-XX-01D |
| ND-disk-00042 | LINKAGE-LOAD-H02 | 210319H02 |
| ND-disk-00172 | HYPHEN-TEST-L03 | 210814L05-XX-01D |
| ND-disk-00177 | SL202-FO-L27 | 210874L05-XX-02D |
| ND-disk-00022 | OEM-STATU-A01 | 211078A01-EN-02D |
| ND-disk-00092 | COBOL-85-K01 | 210177K01-XX-01D |
| ND-disk-00095 | LED-B03 | 211160B03-XX-01D |
| ND-disk-00215 | RG-SERVICE-D10 | 211066D10-SW-02D |
| ND-disk-00216 | NOTIS-RG-SW-D10 | 210528D10-SW-01D |
| ND-disk-00217 | RG-START-SW-D10 | 210528D10-SW-02D |
Twelve distinct products over seven years, from unrelated release lines - compilers, editors,
a linker, NOTIS applications - all obeying the same rule. The layout is identical in every one:
DESC is 22528 bytes, domain entries land at bytes 256 and 312, and the chains run to 0x4000
and 0x40C0.
A trap worth recording. The first run of this check reported one mismatch, on the 1982
floppy: DESC said SCRATCH-SEG-01's .dseg was 5 bytes, and the extracted file was 0 bytes.
The DESC was right. That file is 5 bytes with 0 pages allocated in the image's own
directory entry, and ndtool -x writes such a file out as empty. Checking against extracted
copies would have manufactured a format anomaly out of an extraction artifact. Read sizes from
the directory entry.
What this corpus does not test. Every floppy holds exactly two domains and every segment chain is one entry long, so the linked-list walk is confirmed to start and terminate correctly but has never been exercised on a chain of length two or more. A multi-segment domain is still wanted.
Eight exact size - 1 matches in the detailed table above, 48 across the corpus.
This is why every earlier byte-value search failed: the
file never contains the size, it contains the last byte index.
The monitor itself does not adjust these on the way out - it prints the stored value raw, so
it is not a write-side proof of the -1. But its reader uses the same inclusive-last-index
convention twice (277B = 191 for the 192-byte segment record, 67B = 55 for the 56-byte
domain entry), so stored = last index is this code's house style. The writer is NLL, which
is where a write-side proof would have to come from.
CONVERT-DOMAIN is not a witness for these fields. Patching PSIZE from 123988 to 16384 in
a real DESC and re-running CONVERT-DOM-A03 under nd500x produced a byte-identical
2,316,049-byte .DOM. It queries the filesystem (MON 62B GetBytesInFile) instead of reading
the entry. Any future investigation should not spend time there.
6. Resolved: LINKAGE-LOAD-H02's data segment (was marked an open anomaly)¶
Status 2026-08-11: closed. This was reported as an anomaly - DSIZE not matching the
.dseg file size - based on a DSIZE-stored value (2,109,654) that turned out to be a
transcription error in section 5's table, not a byte re-read from the file. The real
stored value, verified directly against description-file.desc offset 0x4124 (DSIZE; DLB
is the four bytes before it at 0x4120), is 2,109,142 (raw bytes 00 20 2e d6).
With the correct value, DLB + (DSIZE_stored + 1) = .dseg file size holds exactly:
75834 + 2,109,143 = 2,184,977, matching linkage-load-h02.dseg byte for byte. The same
formula holds trivially for the three zero-DLB entries (0 + real_size = file size). DLB
is simply the logical low bound the data segment is placed at; DSIZE is its length from
there - there is no unexplained gap and nothing left for NLL or the segment placer to
account for.
7. How the file is read (ND-500 Monitor, carved)¶
Not RFILE. The monitor opens the file by building DESCRIPTION-FILE + .DESC as PLANC
descriptors (016200B-016240B), then:
013454BREAD-DOMAIN-ENTRY(index) - computes the position with the formula in section 1 and reads 56 bytes into the buffer at037651B.013527BREAD-SEGMENT-ENTRY(byte position) - reads 192 bytes into the buffer at037705B.- Both call
013406B, which issues MON 74 SETBT (seek to the byte position) and then a MON 1 INBT byte loop, storing withSBYT.
The buffer therefore holds the raw file record byte for byte, which is what makes the word-offset to file-byte-offset mapping exact.
8. Symbol Entry - variable size¶
| Field | Size | Meaning |
|---|---|---|
| ELINK | 4 | Link to next symbol. |
| SL | 1 | Length of symbol name. |
| NLE | 3 bits | Numeric length. |
| OPER | (packed with NLE?) | Operation type (+, -, *, /) upon this symbol. |
| IDENT | 1 | Language code. |
| CW | 1 | Type-of-symbol bitmask, see below. |
| VAL | 4 | Value of symbol. |
| SIZE | 4 | Size of common block. |
| SS | up to 255 | Symbol name. |
CW bits¶
| Bit | Name | Meaning |
|---|---|---|
| 0 | UDEF | false = undefined element |
| 1 | DREF | false = program memory reference, true = data memory reference |
| 2 | DSYM | false = program label, true = data label |
| 3 | CLAB | true = common label |
| 4 | DMPF | true = symbol is written (used in list handling) |
| 5 | GLOB | true = symbol survives a loader-table save |
| 6 | SELECT | true = module must be loaded |
| 7 | OMIT | true = module must not be loaded |
(Same bit layout as the NRF loader's in-memory symbol table - see NRF-FILE-FORMAT.md section 4 - these two are almost certainly the same table, one in memory and one persisted.)
9. The real examples used throughout¶
NLL H02 installer floppy (210319H02-XX-01D), domains LINKAGE-LOAD-H02 and
SCRATCH-DOMAIN:
| File | Size | Role |
|---|---|---|
description-file.desc |
22528 | worked example |
linkage-load-h02.pseg |
123989 | program segment |
linkage-load-h02.dseg |
2184977 | data segment |
linkage-load-h02.link |
0 | empty. Why the linker ships without one is an open question - see LINK-FILE-FORMAT.md section 6, question 4. (This row used to say "no unresolved externals"; that was a guess, and :LINK holds the label table, not unresolved externals.) |
LED floppy (211160B03-XX-01D), domains LED-B03 and SCRATCH-DOMAIN: same
22528-byte DESC geometry, led-b03.pseg 223695, led-b03.dseg 394525, led-b03.link 0,
scratch-seg-01.pseg 5, scratch-seg-01.dseg 1029. Cross-referenced in
../../Developer/Workflow/CONVERT-DOMAIN-PSEG-DSEG-TO-DOM.md section E.
A live data point for PLOG once its offset is pinned: CONVERT-DOM-A03 reported
LINKAGE-LOAD-H02's logical segment as 22 during a real conversion run.