PLANC XMSG (COSMOS XMP library) - rules a linter can check¶
This is the companion to xmp-api.json. The JSON says what every routine and
constant IS. This page says what a PROGRAM using them must DO, written as rules with a
detection recipe for each, so a linter author does not have to read four manuals first.
Every rule below states its source. Anything that could not be confirmed from a document or a measurement on real hardware is marked UNVERIFIED.
Sources
| Source | What it settles |
|---|---|
Installation/Software/ND-10609/files/XMP-B02-IMPT.readable.txt |
routine signatures, parameter types, WRITE / READ WRITE direction |
Installation/Software/ND-10609/files/XMP-B02-DEFS.readable.txt |
the library's own status vocabulary: XMOK, XMXENTM, all XMX... values |
SINTRAN/XMSG/SINTRAN-CHAT/XMSG-PL-VALUES-L.INCL |
the release-L raw MON 200B constants, as installed on D100 |
Reference-Manuals/ND-60.164.3 EN COSMOS Programmer Guide.md |
the per-routine reference: parameter names, purposes, options, rules |
Reference-Manuals/ND-60.134.2 EN SINTRAN III Communication Guide.md |
section 4.3.2.10, the raw XFRCV behaviour |
SINTRAN/XMSG/SINTRAN-CHAT/CHATSV.PLNC, CHAT.PLNC |
a server and a client that compiled and ran on D100 |
Counts in the JSON: 54 routines, 397 constants.
1. THE RECEIVE LOOP - the rules that cost the most when broken¶
Rule 1.1 - XFRCV receives on ONE port. There is no receive-on-any.¶
What the documents say. ND-60.134.2 section 4.3.2.10 gives the raw call as
T:=XFRCV; A:=PORTNO - one port number, one call. The COSMOS guide's XMPFRCV table names
parameter 2 localPort, "Number of the receiving port", and adds only that a zero means the
task's default port. No form of the call takes a list.
What a two-port program must do instead. Poll. Call xmpfrcv on each port in turn with
flags ZERO (which returns at once, message or not), and when every port came back empty,
sleep and go round. That is what the working server does:
0 =: waitFlags
DO
xmpfrcv(waitFlags, myPort, msgType, remotePortHash, msgIdent, nBytes) =: returnStatus
IF returnStatus = XMXENTM THEN
xmpfrcv(waitFlags, adminPort, msgType, remotePortHash, msgIdent, nBytes) =: returnStatus
IF returnStatus = XMXENTM THEN
50 =: sleepUnits % 50 basic time units = 1 second
1 =: sleepKind
MONITOR_CALL('TimeOut', sleepUnits, sleepKind, sleepWhy)
ENDIF
ENDIF
...
XMPFGST is the one call that looks at more than one port: "A task may have many open ports.
It does not always know on which one the next message is arriving. XMPFGST allows the task to
check all ports belonging to the task." But it only NAMES a port that has something waiting -
you still call xmpfrcv on that port afterwards. UNVERIFIED: no program in this repo has
used xmpfgst on real hardware; the polling loop above is the shape that has actually run.
Rule 1.2 - XFWTF on a two-port loop blocks on the wrong port.¶
2**XFWTF suspends the task until a message arrives on that one port. A server that
blocks on port A never sees anything on port B - not failing, not logging, just sitting there.
Rule 1.3 - XFWAK is a doorbell, not a delivery.¶
Both manuals say it in the same words. COSMOS guide, XMPFRCV options: "When the wake up is
done, the message is not received, and so the receiving must be repeated. This option can be
enabled on more than one port at a time." ND-60.134.2 4.3.2.10 repeats it.
Two traps follow:
- The wake-up delivers nothing. After a wake-up you must call
xmpfrcvagain. -
Arming it on two ports does not make one call watch two ports. MEASURED on D100 2026-08-21.
X-C LIST-PORTSduring the failure:No Address Owner-task Qhead Qlen Chain WAK 6 160057 152616 BAK06 161762 1 0 0 <- the room port, a message SITTING on it 14 160147 152616 BAK06 0 0 6 1 <- the admin port, task parked HEREThe server had a message waiting on one port and was parked on the other's wake-up. The client waited for ever.
The guide names a third trap in the same paragraph: if the task is already in an XMSG wait (for example a secure send with wait) when the wake-up should fire, the wake-up bit is cleared but the task is not - and cannot be - woken.
Rule 1.4 - a task that terminates loses its ports.¶
MEASURED on D100 2026-08-20: a server that drained both empty ports and RETURNed came back
PASSIVE, and LIST-NAMES showed neither of its port names. SINTRAN closes a task's ports
when the task terminates, so the wake-up had nothing left to arrive on and the server could
never be restarted. The wake-up mechanism assumes a task that STAYS ALIVE to be woken.
LINTER RECIPE for section 1¶
Build a per-routine list of xmpfrcv / xmpfrre / xmpfrrh calls, and for each, the
localPort argument (parameter 2) and the flags argument (parameter 1).
- ERROR - two or more receive calls in the same loop body naming different port
variables, where any of their flags arguments mentions
XFWTForXFWAK. Message: "XFRCV receives on one port. Poll both with flags 0 and sleep between passes." - ERROR - a receive whose flags argument is the bare identifier
XFWTF/XFWAK/XFSEC(see Rule 4.1 - bit position used as a value). - WARN - a receive with flags 0 inside a loop that has no
MONITOR_CALL('TimeOut', ...)and no other blocking call anywhere in the loop body. That is a spin; it burns the machine. MEASURED: when XMSG died under a running server,xmpfrcvstopped blocking, returnedXMXENRUinstantly for ever, and the loop burned the machine until somebody pressed ESC. - WARN -
XFWAKused anywhere in a program whose main loop does not terminate. If the task stays alive there is nothing for a wake-up to do. - WARN -
XFWAKarmed and no repeat receive after it.
2. THE STATUS TEST - both obvious tests are wrong¶
Rule 2.1 - the library's status model¶
From XMP-B02:DEFS, which is the only file that describes this layer:
| Value | Symbol | Means |
|---|---|---|
| 0 | XMOK |
success. (The raw MON 200B kernel's OK is 1; the library remaps it. The file says so: CONSTANT XMOK = 0 % NOTE change from XMSG OK=1.) |
| 16896 | XMXENTM |
not an error - "not terminated". For a receive with flags 0, it means THE PORT IS EMPTY. |
| 16897 .. 16959 | XMX E... |
real errors: XKXXX plus the size of the raw negative code |
| 16961 .. 17008 | XMX R... |
XROUT errors: XRXXX (16960) plus the small positive XR number |
| -1 | (no symbol) | the XMPB* buffer builders' only failure answer |
Rule 2.2 - a receive is the exception¶
The constants file says it in its own heading: "Message types: Returned as successfull status
from XFRCV", XMTNO=1 through XMTPS=6. The vendor's Appendix G server tests
IF RETURNSTATUS >< XMOK THEN ... bad status and only then reads MSGTYPE.
Both forms have been seen, so accept either:
IF returnStatus = XMOK OR (returnStatus >= XMTNO AND returnStatus <= XMTPS) THEN
% a real message; its type is in the msgType OUT-PARAMETER
D100 printed SV: rcv st= 0 t= 2 len= 25 - status XMOK, type in msgType.
The two wrong tests, and exactly what each does:
IF returnStatus > 0- accepts an empty port.XMXENTMis 16896, which is greater than zero, and so is every error. The loop then treats an empty port as a message and parses whatever was left in the buffer.IF returnStatus = XMOKalone, orIF returnStatus >< XMOK THEN discard- throws away every message on any kernel/library pair that hands the message type back as the status.
Rule 2.3 - the length parameter is not always a length¶
msgLengthOrStat (parameter 6 of xmpfrcv): "Message length in bytes. If msgType is XMTRE,
msgLengthOrStat contains the error status." Keep it before anything overwrites it -
xmpfmst writes over the same variable in the usual server sequence.
Rule 2.4 - stop, do not spin, on XMXENRU¶
XMXENRU (16933) is "XMSG not running". Nothing the program can do brings XMSG back; an
operator has to restart it. Retrying is a busy loop with no end. MEASURED on D100 2026-08-18.
LINTER RECIPE for section 2¶
- ERROR - the result variable of an
xmpfrcv/xmpfrre/xmpfrrhcall compared with> 0,>= 1,<> 0or>< XMOKas the gate on "did I get a message". Message: "'> 0' accepts an empty port (XMXENTM = 16896); '>< XMOK' can discard every message. Accept XMOK OR XMTNO..XMTPS." - WARN - a receive whose status is compared only against
XMOKand never againstXMXENTM. The program has no way to tell an empty port from a message. - WARN - a receive loop that never mentions
XMXENRU(16933) - no way out when XMSG dies. - WARN -
msgTypenever read after a successful receive. The type is where the answer is. - INFO -
msgLengthOrStatused as a length withoutmsgTypebeing tested forXMTREfirst. - ERROR - the result of an
xmpb*call compared againstXMOKor anXMX...symbol. Those routines answer -1, not a symbolic status.
3. SEATS - who spends one, and who has to give it back¶
Rule 3.1 - a seat only exists on a CONNECTION port¶
| Opened with | Has a free-connection counter? |
|---|---|
xmpfopn (unnamed port) |
no |
xmpopnm (plain named port) |
no |
xmpopcn (connection port) |
yes - set to maxConnections |
Rule 3.2 - XROUT spends the seat, before your program sees anything¶
COSMOS guide, XMPOPCN explanation: "When somebody contacts portName by sending a letter via
XROUT, XROUT looks at the free connection counter and if it is greater than zero, XROUT
decrements it and forwards the letter. If there are no free connections, XROUT tries to find
another port with the same name."
So the seat is gone by the time the letter arrives. The arrival looks like this:
msgType=XMROU(2) - "routed message, via XROUT". This is the only thing that says a seat was spent.- byte 1 of the arrived message =
XSLET(65) means a client sent a letter and a seat went with it. Any other service byte is XROUT answering one of your own calls, which spent nothing.
The caller's side of the same fact: xmprout is the call that spends a seat - a name
lookup is not free.
Rule 3.3 - only the port's owner puts a seat back, with xmpinfc¶
xmpinfc(0, portNumber, +1, serialNumber). Guide: "After opening a connection port using
XMPOPCN, a task can later increment ... the free connection counter associated with that
port." Nothing else ever does it.
MEASURED on D100 2026-08-18, before the working server had a reaper: CHAT-LOBBY went from
16 free seats to 15 when a client was killed with ESC, and stayed at 15 for good. Clearing
only the program's own member table leaves the room looking empty while it refuses everybody.
When a seat must go back: whenever an arrival spent one and the sender did NOT end up as a member - a refused join, a member who left, and a member found dead. Take the "was it a member before / is it a member now" decision from the values BEFORE the message was handled.
Rule 3.4 - xmpinfc does not wait, and its reply arrives as a message¶
"Note that this routine will not wait for a reply from XROUT, and so the caller will later
receive this reply from XROUT on the port specified by portNumber." A receive loop that does
not expect that reply will treat it as garbage. serialNumber goes into byte 0 of the request
so a caller with several outstanding can recognise which reply is which.
Rule 3.5 - a send does not tell you the port is dead¶
MEASURED on D100 2026-08-18: two clients in a room, one killed with ESC, the other spoke.
xmpsend to the dead port returned XMOK. It does not validate the destination, and the
magic number's generation does not save you.
The cure is 2**XFSEC on the send: "The message will be returned to the sending port if it
cannot be delivered, or if the receiving port is closed." It comes back later as a message of
type XMTRE, and msgLengthOrStat then holds the reason - measured as 16915, XMXEIMA.
Rule 3.6 - the routines that quietly change the current message¶
The guide flags these because they have to reserve and send a message to XROUT to do their
job: xmpopnm, xmpopcn, xmpinfc. Each changes the task's 'task current' message, and
xmpinfc also changes the 'port current' message for its port. Anything you were building in
the current message before one of these calls is no longer the current message afterwards.
LINTER RECIPE for section 3¶
- ERROR -
xmpinfccalled with a port variable that was assigned byxmpopnmorxmpfopnrather than byxmpopcn. There is no counter on those ports. - WARN - a program that calls
xmpopcnand never callsxmpinfcanywhere. It leaks a seat per refused or departed client and eventually refuses everybody while looking idle. - WARN - a receive loop that tests
msgTypeforXMROUand has noxmpinfcon the path where the sender does not become a member. - WARN -
xmpsend/xmpfsndwith a flags argument of0in a server that keeps a table of remote magic numbers. Without2**XFSECa dead peer is written to for ever, in silence. - WARN - a send with
2**XFSECin a program whose receive loop never handlesXMTRE. The bounce comes back as a message and will be thrown away. - INFO - a message buffer built with
xmpfgetand thenxmpopnm/xmpopcn/xmpinfccalled before it is sent. The current message has moved.
4. FLAGS¶
Rule 4.1 - every XF... option is a BIT POSITION, not a value¶
XFWTF = 15 means bit 15. Write 2**XFWTF (= 32768). Writing 15 sets bits 0, 1, 2 and
3 - four unrelated options - and the wait simply does not happen. The guide's own Appendix G
samples both write 2**XFWTF =: FLAGS.
| Symbol | Bit | Flags word |
|---|---|---|
XFWTF |
15 | 32768 |
XFWAK |
14 | 16384 (0x4000 - confirmed on the wire in this repo) |
XFSEC |
9 | 512 |
Rule 4.2 - flags is always parameter 1, and 0 is the right value when no option applies¶
Every routine except the XMPB* buffer builders takes flags first. Many routines document
"Options: not implemented, flags should be zero" - xmpfopn, xmpopnm, xmpopcn, xmpinfc,
xmpfrea, among others.
Rule 4.3 - the same bit means different things on different calls¶
Bit 13 is XFPON, XFUSG, XFHIP, XFRRO and XFEXC depending on which routine you pass
it to. Bit 12 is XFOPS, XFRES, XFRMR and XFBNC. Bit 10 is XFROU and XFRDI. A
linter that knows the routine can check that the named option belongs to it; one that does not
should not guess.
LINTER RECIPE for section 4¶
- ERROR - a bare
XF...identifier (no2**) passed as parameter 1 of any XMP routine, or assigned to a variable that is later passed as parameter 1. Message: "XFWTF is bit 15. Write 2**XFWTF; passing 15 sets bits 0-3." - WARN - an option named for a routine it does not belong to (for example
XFRESon a receive). Needs the per-routine option table; skip if unsure rather than guess.
5. BUFFERS AND LENGTHS - PLANC checks no array bounds¶
Rule 5.1 - clamp a received length BEFORE reading¶
xmpfrea's parameter 5 is userLength, "Number of bytes you want to read". The number a
program naturally has to hand is nBytes from the receive - and that is the SENDER'S
message size, not a size the program chose. PLANC checks no array bound, so passing it
straight in lets any peer that sends more than your array holds write past the end of it. The
overflow happens before a single field is parsed, so no amount of careful field checking
downstream can save it.
INTEGER : inBufSize := 256
BYTES : inBuf(0:255)
...
nBytes =: readAmount
IF readAmount > inBufSize THEN
inBufSize =: readAmount
ENDIF
xmpfrea(0, 0, ADDR(inBuf(0)) FORCE XMUSERADDRESS, 0, readAmount, readLength) =: returnStatus
xmpfrre avoids the whole question, because its userLength is naturally your own buffer's
size.
Rule 5.2 - clamp every length that came off the wire, not just the message length¶
A one-byte length field on the wire can ask for 255. A sixteen-byte name column plus an unchecked copy writes through everything after it. Every copy driven by a wire number needs a bound.
Rule 5.3 - the user address idiom¶
ADDR(buf(0)) FORCE XMUSERADDRESS - round brackets. The guide prints square ones and
PLANC F rejects them with EXPECTS ")" ILLEGAL SYNTAX "[". Plain ADDR(buf) is not enough.
XMUSERADDRESS IS INTEGER, NOT INTEGER4, BECAUSE AN ND-100 ADDRESS IS SIXTEEN BITS.
XMP-B02:IMPT declares TYPE XMUSERADDRESS = INTEGER. The line above it,
% modify XMUSERADDRESS = INTEGER4 %, is a COMMENT describing what the MC68000 compiler needs -
and taking that first matching line for the declaration is what produced two withdrawn measurements
on 2026-08-27.
Forcing to a 32-bit type does not fail the build. It is a size mismatch, which FORCE refuses -
but only as a WARNING:
668/XRADDROF *** WARNING - ILLEGAL DATA-ELEMENT TO BE CONVERTED
so the program links, runs, and hands XMSG a plausible wrong address: 3473663 where the caller's
own ADDR said 65791, reproducibly across two builds. A wrong address given to xmpfrea is a
write to memory nobody owns, and PLANC checks no bounds, so nothing else would have reported it.
Read the LISTING - see R110 and R46.
Rule 5.3a - a routine MAY take ADDR of a buffer it was HANDED¶
MEASURED 2026-08-27: ADDR param 1001 / ADDR here 1001. PLANC passes arrays by reference, so a
shared routine given a BYTES parameter can take its address and gets the CALLER'S OWN array.
This is what makes a shared transport layer possible at all - every ADDR in this product used to
be of a module-level array, in the same module as its XMSG call, because a routine handed a buffer
had never been shown to work. "Should" was not good enough for something whose failure mode is a
silent write into memory nobody owns.
Rule 5.4 - the buffer builders return -1 on a bad buffer¶
xmpblet: "headerBuffer must start on an even byte boundary and lengthBuffer must be big
enough ... If one of these checks fails, -1 will be returned as error code in returnStatus."
Same for xmpbini and friends. That is not a symbolic status and will not match any XMX...
comparison.
Rule 5.5 - offSet is READ WRITE in the real declarations¶
xmpblet, xmpbini, xmpbain, xmpbadb, xmpbast all take offSet as INTEGER READ
WRITE, not plain WRITE. It carries the running "bytes used so far" through the whole build
sequence. Initialise it (via xmpbini) before the first append.
Rule 5.6 - message displacements are rounded¶
"the displacement within the message is always rounded up to the next even byte and, on an ND-100, userDisp is always rounded down to the previous even byte before the data is read." A program that computes an odd displacement is not reading where it thinks it is.
LINTER RECIPE for section 5¶
- ERROR - the
msgLengthOrStatvariable of a receive passed directly asuserLengthtoxmpfrea/xmpread, with no comparison against a buffer-size constant between the two calls. Message: "Clamp the sender's length against your buffer size first - PLANC checks no array bounds." - ERROR -
ADDR(x)wherexis an array name rather thanx(0), orADDR(x[0])with square brackets, in auserAddressposition. - WARN - a
FORloop copying from a received buffer whose bound is a variable read out of that buffer, with no clamp on the path. - WARN -
xmpbain/xmpbadb/xmpbastcalled beforexmpbinion the same buffer. - WARN - a buffer-builder sequence with no
xmpbrdyat the end.
6. CURRENT MESSAGE - which call acts on what¶
This is the quietest source of wrong behaviour, because everything returns XMOK.
Rule 6.1 - the calls with NO message identifier act on the CURRENT message¶
xmpfrea, xmpfwri, xmpfwhd, xmpfsnd. Their xmp* twins take one explicitly:
xmpread, xmpwrte, xmpwrhd, xmpsend.
Rule 6.2 - xmpfsnd picks a message in two steps, and the first step surprises people¶
Guide: "the current (default) message buffer is assumed, namely the 'port current' message if one exists, or, if none, the 'task current' message."
xmpfget makes the new buffer task current only - it has no port parameter and cannot
make it port current. A port that has just received a secure message may still have a port
current message, and xmpfsnd will send that instead of your reply.
So: a program that builds its own reply with xmpfget should send it with xmpsend, which
"is set as 'task current' message and as 'port current' message for localPort" first. The
vendor's Appendix G server gets away with xmpfsnd only because it never calls xmpfget - it
replies in the buffer that arrived, which is already both.
Rule 6.3 - what clears the current message¶
- 'task current': releasing it, sending it, or receiving another message.
- 'port current': releasing it, sending it, or receiving another secure message.
xmpfscmsets it explicitly.xmpopnm/xmpopcn/xmpinfcchange it as a side effect (Rule 3.6).
Rule 6.4 - a received message must be released or sent¶
Every xmpfget and every successful receive puts a buffer in your hands. Read it out, then
xmpfrel it - or send it on. Buffers neither sent nor released are leaked message space, and
XMXEMFL (16916, "message space full") is what a leak eventually looks like.
LINTER RECIPE for section 6¶
- ERROR -
xmpfsndin a routine that also callsxmpfget. Message: "xmpfget makes the buffer task current only; xmpfsnd prefers the port current message. Use xmpsend." - WARN - a successful-receive branch with no
xmpfreland no send call on any path. - WARN -
xmpfgetwhose returned identifier is never passed toxmpfrel,xmpsend,xmproutorxmpfrtn. - INFO -
xmpfrea/xmpfwricalled afterxmpopnm/xmpopcn/xmpinfcwithout an interveningxmpfscmor receive. The current message moved underneath them.
7. THE ANSWER PATH - hashed port versus magic number¶
Rule 7.1 - what a receive hands back cannot be used to answer¶
xmpfrcv parameter 4 is remotePort, "Hashed magic number of remote port". You cannot
send to a hash.
Rule 7.2 - xmpfmst is how you get an address you can send to¶
xmpfmst(0, msgIdent, msgType, senderMagic, nBytes) - parameter 4 is the FULL
INTEGER4 magic number of the sending port. The guide's own walkthrough of the letter
exchange says the same: the client "can then use the XMPFMST routine to extract the magic
number of the remote (server) port, and direct communication ... can begin".
Rule 7.3 - a magic number is 32 bits¶
INTEGER4 everywhere: xmpfsnd parameter 3, xmpsend parameter 4, xmpfp2m parameter 3,
xmpfm2p parameter 2. A magic number is a full port address and does not fit one word.
Rule 7.4 - answering IS giving away your magic number¶
The guide is explicit: a server "can, and it normally will, check that the sending task is
allowed to use the server before it sends a (positive) reply to the requester, and thereby
gives away its own magic number. If the server task does not want to give away its own magic
number, it can do so by sending a (negative) reply with the XFFWD (forward message) option."
xmpfcpv is the call that does the checking.
LINTER RECIPE for section 7¶
- ERROR - the
remotePortout-variable of a receive passed asremoteMagicNumtoxmpfsnd/xmpsend. Message: "That is a hashed port. Call xmpfmst for the full magic number." - ERROR - a magic-number variable declared
INTEGERrather thanINTEGER4. - WARN -
xmpfmstoverwriting a length variable that the receive filled and that is read again afterwards.
8. THE LETTER - reaching a server BY NAME¶
The sequence, from the guide's XMPROUT example and from the working client:
xmpblet(letterBuf, 64, offSet, serial, systemName, portName)- format anXSLETheader into your OWN buffer. It sends nothing. A zero-lengthsystemNamemeans the local system.xmpfget(0, size, msgIdent)- reserve a message.xmpfwri(0, 0, ADDR(letterBuf(0)) FORCE XMUSERADDRESS, 0, offSet, wLength)- copy the header in.xmpfwri(0, wLength, ...your data..., 0, dataLen, offSet)- your data goes after the header. XROUT never looks at it.xmpfopn(0, myPort)- you need a port of your own for the answer.xmprout(0, msgIdent, myPort)- hand it to XROUT.
Rule 8.1 - a refusal comes back as a MESSAGE, not as a bad returnStatus¶
"if XMSG is unable to send the letter to XROUT in the specified system, or if the destination XROUT does not know the name of the destination port ... or if portName is a connection port with no free connections, then XROUT will return the letter (message) to the sending port with an error status." The client tells a server reply from an XROUT refusal by the message type.
Rule 8.2 - the reply arrives with the letter header still in front of it¶
The server sees the XSLET header first, then the user data. Byte 0 of that header is the serial number, not user data. Derive where the payload starts from the header's own length word - never from a fixed number.
LINTER RECIPE for section 8¶
- WARN -
xmpbletwith no followingxmproutin the same routine. - WARN -
xmproutin a program that never callsxmpfopn,xmpopnmorxmpopcn. There is no port for the answer. - WARN - a client that calls
xmproutand whose receive loop never testsmsgType. It cannot tell a server's answer from XROUT's refusal. - INFO - a fixed constant used as the payload offset in a routed (
XMROU) arrival.
9. DECLARATIONS AND THE BUILD¶
Rule 9.1 - include ONE constants file, never both¶
XMP-B02:DEFS and XMSG-PL-VALUES-L:INCL share 184 constant names. A redeclaration in
this PLANC compiler is not a warning: it drew IDENTIFIER ALREADY SPECIFIED/DECLARED, then
ASSERT VIOLATION AT 136747B, and took the whole batch job with it.
- Talking through the
XMPF*library:XMP-B02:DEFS+XMP-B02:IMPT. - Talking raw MON 200B from NPL or MAC:
XMSG-PL-VALUES-L:INCL.
The two files agree on 183 of the 184 shared names. The one real difference is X5FUN
(43 in the older DEFS, 48 in the L-era file) - a sentinel, not something a program passes.
XSMAX also differs, for the same reason: it is defined as "the highest service value", and
release L added services. Both differences are recorded in the JSON as conflict.
Rule 9.2 - do not redeclare a constant the include already provides¶
The same fatal ASSERT VIOLATION came from a program declaring its own XFWTF and its own
XSLET when the include already had them.
Rule 9.3 - the includes go INSIDE the MODULE¶
Above MODULE the declarations land in the outer scope, the body cannot see them, and every
call fails NOT PREVIOUSLY DECLARED while the include's own lines draw no diagnostic at all.
Rule 9.4 - every included file must end with $EOF¶
Without it the compiler runs off the end of the include, treats that as the end of the whole
compilation, and reports 0 DIAGNOSTICS - a clean-looking build that silently skipped the
program. The tell: LINES COMPILED stops at the $INCLUDE line, and the next command answers
COMMAND NOT PERMITTED WITHIN MODULES.
Rule 9.5 - names are unique in their first TEN characters¶
memberName and memberNameLen both start MEMBERNAME, so the second silently became a
redeclaration of the first, and every memberName(slot, i) then drew MORE SUBSCRIPTS THAN IN
THE ARRAY DECLARATION. Four errors, one cause, and none of them mentioned the length. The
compiler listing gives it away: it prints the truncated names.
Rule 9.6 - the link needs the right library¶
LOAD XMP-100-1-B02 (1-bank) or XMP-100-2-B02 (2-bank), from (SYSTEM) after the ND-10609
installer has run. It is not (UTILITY)XMSG-LIBRARY-L03 - that is the NPL-level XMSG
support library, eleven internal entries, not one XMPF symbol, and loading it leaves every
call UNDEFINED. Load the program first, then the library, then PLANC-1BANK-F00 for
5MON_P.
Rule 9.7 - a monitor-call failure is a ROUTINEERROR, not a return value¶
ON ROUTINEERROR DO ... ENDON. A failed MON50 leaves 46 (SINTRAN's "no such file") in the
device number, not zero, so testing the number is wrong. Clear the failure flag AFTER the
ON ... ENDON block, not before - cleared before, the handler body has already run and every
open reports failure while returning a good device.
LINTER RECIPE for section 9¶
- ERROR - both
XMP-B02:DEFSandXMSG-PL-VALUES-Lincluded in one compilation unit. - ERROR - a
CONSTANTdeclaration whose name matches a constant inxmp-api.json. - ERROR - an
$INCLUDEof the XMP files placed before theMODULEline. - ERROR - an included file whose last non-blank line is not
$EOF. - ERROR - two declared names in the same scope whose first ten characters match.
- WARN - a
MON50result compared against 0 rather than guarded byON ROUTINEERROR.
10. Small ones that still bite¶
- Terminal input carries even parity in bit 7. CR arrives as 141, not 13. Mask with 127.
- A written SINTRAN text file needs the right parity too, or
LIST-FILEshows nothing at all, silently. Note the control run that matters here:LIST-FILEandQEDanswer "PARITY ERROR" for every file pushed onto the machine by an FA transfer, good or bad, so that symptom is not evidence about your bytes. Pull the file off and look at it. - A string literal cannot be stored into a
BYTESelement.' ' =: outBuf(at)drawsILLEGAL DATA TYPE. Hold single characters in a one-elementBYTESand copy byte to byte. - A local declaration cannot carry an initialiser.
BYTES : b := ' 'inside a routine drawsINITIAL VALUE ILLEGAL HERE; the identical line at MODULE level compiles. - PLANC string literals subscript from ZERO.
name(0:len-1)is the whole of a length-lenname. - There is no
LENGTH. The routine isSIZE, spelledSIZE ident =: var. XMPFFREdoes not exist. The release call isXMPFREL.- The manual's
XMPF2PMis a typo. The entry point isxmpfp2m. XMMAXNameLengthis 32 inXMP-B02:DEFS, "actually installation-dependent". A longer name returns -1; a name over the generation-time limit is silently truncated by XMSG.
11. What is NOT settled¶
xmpxets/xmpxrts- declared inXMP-B02:IMPTand exported byXMP-100-1-B02:BRF, documented nowhere in ND-60.164.3. Their shape is unusual:ROUTINE INTEGER,VOID (INTEGER WRITE)- an in-value and no out-value, so they cannot be writtenxmpxets(x) =: status. What they DO is UNVERIFIED.xmpfgston real hardware - the documented multi-port scan, never exercised in this repo. UNVERIFIED.- J-on-L compatibility - the B02 library and DEFS were built against XMSG version J; the
running kernel on D100 is release L.
X5FUN43-vs-48 is the visible gap. Old-caller-on- newer-kernel is the compatible direction on every other ND product measured, but for XMSG it is UNVERIFIED beyond the calls the chat pair actually makes. - The ND-500 path (
XMP-500-B02:NRF) - how the ND-500 library reaches the ND-100-side XMSG kernel is undocumented and untried here. UNVERIFIED. - Which success form a given kernel returns from a receive (
XMOKvs the message type). D100 returnedXMOK; the constants file documents the message type. Accept both.
See also¶
xmp-api.json- the machine-readable table: 54 routines, 397 constants, the error-base rule computable fromerror_model- COSMOS-XMP-LIBRARY.md - what the library is, what ships, how to build
- PLANC-XMSG-COMMUNICATION.md - the D100 field notes
SINTRAN/XMSG/DOC/XMSG-API.md- the MON 200B function interface underneath the library