From:	CSBVAX::MRGATE!RELAY-INFO-VAX@CRVAX.SRI.COM@SMTP  9-AUG-1988 07:23
To:	ARISIA::EVERHART
Subj:	UNSDL Part 3 of 3 parts


Received: From KL.SRI.COM by CRVAX.SRI.COM with TCP; Tue,  9 AUG 88 03:31:08 PDT
Received: from uwavm.acs.washington.edu (oly.acs.washington.edu) by KL.SRI.COM with TCP; Tue, 9 Aug 88 03:08:44 PDT
Received: from UWAVM.ACS.WASHINGTON.EDU by uwavm.acs.washington.edu ; Tue, 09 Aug 88 03:02:54 PDT
Received: from UWAVM.ACS.WASHINGTON.EDU by UWAVM.ACS.WASHINGTON.EDU (Mailer
 X1.24) with BSMTP id 2812; Tue, 09 Aug 88 03:02:49 PDT
P1-Message-Id: US**EDU;UWAVM.ACS.WASHINGTON.EDU:LTbkCZBT
Date:         Mon, 8 Aug 88 22:01-0700
X400-Trace:   US**EDU; arrival Mon, 8 Aug 88 22:01-0700 action Relayed
X400-Trace:   US**EDU; arrival Tue, 9 Aug 88 03:02-0700 action Relayed
From:         Joe Meadows</ADMD=/PRMD=BITNET/O=FHCRCVAX/PN=JOE/@US>
Subject:      UNSDL Part 3 of 3 parts
To:           <info-vax@kl.sri.COM>
Message-Id:   <UWAVM.ACS.WASHINGTON.EDU:LTbkCZBT*>
X-VMS-To: IN%"info-vax@kl.sri.com"

This is part 3 (of 3 parts) of UNSDL, see the AAAREADME.TXT file in
part 1 for a description.

        This file contains:
        SDL.RNO

  Cheers,
  Joe Meadows Jr.
  VAX/VMS System Manager / guru in training
  Fred Hutchinson Cancer Research Center
  1124 Columbia St.
  Seattle Wa. 98104
     bitnet - JOE@FHCRCVAX
     arpa   - JOE%FHCRCVAX.BITNET@OLY.ACS.WASHINGTON.EDU
     voice  - (206) 467-4970

$ create/log SDL.RNO
$DECK
.ap
.lm 5
.rm 75
.hl Intro

        This is a rough draft document. If
anyone wishes to rewrite the material to be more readable, I will gladly
accept modifications and update this manual. If things are unclear,
please feel free to contact me. If you write language modules and
would like them included in the DECUS library please contact me, I plan
on submitting this and all related works at some point in the future
and updating it as needed.

        Please note that this is a "hackers" guide. I produced this
document while trying to figure out the formats used. I had no documentation
to go by whatsoever, not even in the fiche. I take no responsibility for
the information contained herein. I will point out that some of the
SDI modules provided by DEC are erroneous, esp. in regards to function
prototyping. For example in the LIB$ROUTINES module LIB$GETDVI, LIB$GETJPI
and LIB$GETSYI are all missing their first argument. In MTH$ROUTINES the
MTH$CVT_D_G and MTH$CVT_G_D routines erroneously claim the source and
result are of the same type, which they aren't. At least, in VMS V4.7
these modules are incorrect. A cursory glance at VMS 5.0 shows that many
of these have been fixed.

.hl What is SDL

        I don't know exactly what SDL stands for, but a good guess would be
Structure Definition Language, or Symbol Definition Language.

        SDL is a language used internally at DEC, for defining
data structures, constants, and function definitions. For instance
DEC seems to be using it to define all the structures and definitions
found in the STARLET macro library. SDL reads source code, and creates
intermediate code in files with a ".SDI" file name extension. SDL can then
take this intermediate file, along with a "language module" and
generate structure, constant, and function definitions in whatever language
the "language module" chooses to create. DEC distributes many of these .SDI
files with VMS in the text library SYS$SHARE:STARLETSD.TLB. If you program
in BASIC or FORTRAN, the installation process for these products makes use
of these files to create include files for many of the system structures,
constants and function definitions.

        It is easy to see why DEC would want to have SDL, it certainly
makes it a lot easier to create the include files for all the various
languages. However, DEC doesn't provide anyone with the means for using
this facility. So, if you are using a language that DEC didn't sell you,
chances are good that you would have to create include files using the
macro definitions as a guide. This is error prone at best. Even for the
languages DEC does support, the language developers don't necessarily use SDL
to create include files. For instance the C include files are all made
by hand, even though there is a language module for C in use by the
VAXstation group, the C developers aren't currently using it.

        My interest in SDL is to see to it that I never create another
copy of $FABDEF for <insert language here>. I don't think anyone should,
because there's already enough places for code to have problems.
So this document is going to attempt to explain how you can use
SDL. This will require writing a "language module" of your own, and
interfacing it with SDL. I will describe the hooks into SDL, and the
data structures used by SDL to describe all of the structures, constants,
and function definitions.

.hl Using SDL (Actually, SDL/NOPARSE)

        SDL actually isn't distributed to anyone, perhaps someday it
will be a commercial product, or rolled into VMS, but not at present.
However, a small part of SDL is included. That part is the program
SDLNPARSE, which is activated by the command SDL/NOPARSE. This part
is used to read an SDI file, create an internal tree (actually, a bunch
of linked circular lists), and then DUMP the internal tree if requested
(via SDL/NOPARSE/DUMP), and then call a language module if requested
(via SDL/NOPARSE/LANGUAGE=language-module).

        Just to see what we're working with, lets take a look at a
module. First we need to find out what modules exist, we do this by
listing the text library:
.literal
        $ LIBRARY/LIST/TEXT SYS$SHARE:STARLETSD
.end literal
One of the modules should be STARLET, this module defines the system
services, what the routine names are, what the return values are, and what
the parameters are. Lets extract this module:
.literal
        $ LIBRARY/EXTRACT=STARLET/OUTPUT=STARLET.SDI/TEXT SYS$SHARE:STARLETSD
.end literal
We could then get a large amount of output by executing the command:
.literal
        $ SDL/NOPARSE/DUMP STARLET
.end literal
        If you have the DUMP language module I wrote, you could
cause it to be run instead, but first, you need to define a logical name:
.literal
        $ DEFINE SDL$SHARE DISK:[DIR]
.end literal
to point to where the language modules
reside. Once you do this you can say:
.literal
        $ SDL/NOPARSE/LANGUAGE=DUMP STARLET
.end literal
This produces a somewhat smaller dump, in a style that is somewhat
easier to read. The DUMP module isn't really meant to do anything
usefull, except provide some working code to look at.

.hl UNSDL

        UNSDL is a command I wrote which is meant to simplify creating
include files via SDL. All it does is provide a slightly different syntax
and gives you some more outputting options. These options are passed
to the language module via logical names, since there is no way to
pass further information on the command line for SDL. The C language module
I wrote takes advantage of this, allowing you to specify wether you
want comments or not, and wether you want output to be split up by module
name rather than by the input file name. The C module may be used without
using UNSDL, in which case you will get comments, and only one output file.
This is usually quite acceptable. Again, it serves mostly to show how
further enhancements could be made, allowing you more flexibility than
the SDL command allows.

        Features of the CC module:
.literal
                [NO]COMMENTS
                [NO]SEPARATE
                [NO]PROTOCOLS
                CASE=[ORIGINAL | LOWER | UPPER | MIXED]
                MASK=[HEX | DECIMAL]
                MODULES=(module-list)
.end literal
        To use these, you must say something like UNSDL/LANGUAGE=CC="options".
Where "options" is a quoted list of comma separated items. For example,
to split up STARDEFAE into separate little files (one per module), and
force the case of variable declarations to lower case (to make happier C
programmers), and _#defines to upper case you would use:
.literal
        UNSDL/LAN=CC="SEPARATE,CASE=MIXED" STARDEFAE
.end literal

.hl Writing a language module
        Well, the first thing to remember, is "what language are
you writing for". I created the first draft C module without
really keeping that fact in mind. Rather than thinking it through,
I created a module that looked exactly like that provided by the VWS
software. The structure of the SDI files, or at least the internal tree
that SDL passes to us, is simple enough, and powerfull enough, to do
whatever we want. You may want to think, "what is stylistically correct",
rather than, "how would DEC have done it". Of course, style is subjective,
so if possible, you need to give the user a choice.

        Remember that people read the include files. (Well, I don't,
I read the documentation - it's always so clear (ha-ha)), so format them
nicely.

.hl The interface to SDL - SDL$OUTPUT
        Before describing the data structures, I should probably mention
how the language module gets loaded and activated. This is fairly simple,
SDLNPARSE uses a call similar to:
.literal
        LIB$FIND_IMAGE_SYMBOL("SDL$SHARE:language","SDL$OUTPUT")
.end literal
So the language module must be written with this in mind.
You must use a linker options file like the following:
.literal
        ! SDL.OPT
        sys$share:vaxcrtl.exe/share
        universal = sdl$output
.end literal
The link command should be something like:
.literal
        link/share SDLlanguage,sdl/option
        ! Your image name must start with SDL, and the rest should
        ! be the language name, i.e. SDLCC for a CC module
.end literal
        Note that SDLNPARSE does a little magic for you, in that it tries to
find the language module by prefixing "SDL", and suffixing "*" to the name
given in the /LANGUAGE qualifier, this allows you to abbreviate the
language name. For example, if you wrote SDLMODULA, you could specify
SDL/NOPARSE/LAN=MOD and SDLNPARSE would find the proper image, at least
if SDL$SHARE is properly defined, and nothing else started with SDLMOD!

        SDL$OUTPUT must be your main routine. You can dispatch to other
routines of course, but this routine must exist. SDL$OUTPUT will get
passed three arguments. The first two are PLI strings, passed by reference.
A PLI string (apparently) is a 2 byte count, followed by 'count' characters.
In the CC module I cheat and created a structure with a short int
count followed by 32 characters, like this:
.literal
        struct sptr { /* this is a string pointer, as used in PLI apparently */
          short int len;
          char str[32];
        };
.end literal
This (32 chars) just seemed to be the size most often
used in the internal structures, but it is certainly not the limit on
the string lengths. The first argument is the output file name, this is taken
from the /LANGUAGE=language=output qualifier. If not given it will be
blank. The second argument is the name of the module (or input file).
The third argument is the one we are really interested in. This
contains a pointer to a structure. This structure contains a descriptive
string, the version of SDL, and a pointer to the internal structure
that actually describes all the modules within the input file.
This pointer (struct Node *head) points to the ROOT node
of the tree (or circular list of circular lists if you prefer).
.literal
        struct Head {
            char fill[298];
            short int banner_len;
            char banner[68];
            char version[10];
            struct Node *head;
        };
.end literal


        All of the nodes have the same structure. I use the following
C structure, which seems to be correct so far, although it is somewhat
incomplete.

.literal
        struct Node {
          struct Node *flink, *blink;   /* forward and backward pointers */
          short int size;               /* ??? */
          short int type;               /* Node type */
          struct Node *parent, *child;  /* pointer to parent and child */
          char fill1[4];
          int hidim;                    /* if array this is the high dim. */
          int lodim;                    /* if array this is the low dim. */
          int default_value;            /* default value if DEFAULT flag set */
          struct sptr name;             /* name, i.e. FAB$L_FOP */
          char fill2[2];
          struct sptr prefix;           /* prefix, i.e. FAB$ */
          char fill3[34];
          struct sptr tag;              /* tag, i.e. L */
          struct sptr naked;            /* name w/o prefix and tag, i.e. FOP */
          char fill4[2];
          int offset;                   /* offset from base of structure */
          int fldsiz;                   /* field size, in bytes */
          unsigned int datatype;        /* data type */
          variant_union {
            int flags;                  /* flags - */
            struct {
              unsigned m_value : 1;     /* passed by value */
              unsigned m_mask : 1;      /* represents bit mask */
              unsigned m_unsigned : 1;  /* unsigned data type */
              unsigned m_common : 1;    /* ??? guessing at location */
              unsigned m_global : 1;    /* ??? guessing at location */
              unsigned m_varying : 1;   /* ??? guessing at location */
              unsigned m_variable : 1;  /* variable number of arguments */
              unsigned m_based : 1;     /* structure is based (??) */
              unsigned m_descriptor : 1;/* passed by descriptor */
              unsigned m_dimen : 1;     /* data type is array */
              unsigned m_in : 1;        /* data is input (not modified) */
              unsigned m_out : 1;       /* data is output (is modified) */
              unsigned m_bottom : 1;    /* end of structure (??) */
              unsigned m_bound : 1;     /* ??? guessing at location */
              unsigned m_ref : 1;       /* passed by reference */
              unsigned m_fill : 1;
              unsigned m_16 : 1;
              unsigned m_default : 1;   /* default value for parameter */
              unsigned m_vardim : 1;    /* array bounds can vary */
              unsigned m_link : 1;      /* I don't know */
              unsigned m_optional : 1;  /* argument is optional */
              unsigned m_21 : 1;
              unsigned m_fixed : 1;     /* I don't know! */
              unsigned m_generated : 1; /* I don't know! */
              unsigned m_24 : 1;
              unsigned m_list : 1;      /* new with 5.0 */
              unsigned m_rtl_str_desc : 1;      /* new with 5.0 */
              unsigned m_complex : 1;   /* new with 5.0 */
              unsigned m_28 : 1;
              unsigned m_29 : 1;
              unsigned m_30 : 1;
              unsigned m_31 : 1;
            } flag;
          } sdl_flag_bits ; /* end of variant_union */
          int typeinfo;                 /* depends on context */
          int typeinfo2;                /* depends on context */
          struct sptr *comment;         /* pointer to comment (if not 0) */
          struct sptr typename;         /* name of data type */
        };
.end literal

        Not all nodes use all of the fields.

        I use the following code to define the node types:

.literal
        #define SDL$C_NODE_ROOT 1       /* the root of the whole thing */
        #define SDL$C_NODE_COMMENT 2    /* defines a comment */
        #define SDL$C_NODE_CONSTANT 3   /* defines an integer constant */
        #define SDL$C_NODE_ENTRY 4      /* defines a routine */
        #define SDL$C_NODE_ITEM 5       /* defines variable/structure/union */
        #define SDL$C_NODE_MODULE 6     /* defines module name */
        #define SDL$C_NODE_PARAMETER 7  /* defines function parameters */
        #define SDL$C_NODE_UNKNOWN 8    /* I don't know if it exists */
        #define SDL$C_NODE_OBJECT 9     /* defines what an address points to */
        #define SDL$C_NODE_HEAD 10      /* one heads up almost every list */
.end literal

        And the following code to define the datatypes:

.literal
        #define SDL$C_TYPE_ADDRESS 1
        #define SDL$C_TYPE_BYTE 2
        #define SDL$C_TYPE_CHARACTER 3
        #define SDL$C_TYPE_BOOLEAN 4
        #define SDL$C_TYPE_DECIMAL 5 /* ??? strong guess */
        #define SDL$C_TYPE_DFLOAT 6
        #define SDL$C_TYPE_FFLOAT 7
        #define SDL$C_TYPE_GLOAT 8
        #define SDL$C_TYPE_HFLOAT 9
        #define SDL$C_TYPE_LONGWORD 10
        #define SDL$C_TYPE_OCTAWORD 11 /* ??? strong guess */
        #define SDL$C_TYPE_QUADWORD 12
        #define SDL$C_TYPE_BITFIELD 13
        #define SDL$C_TYPE_WORD 14
        #define SDL$C_TYPE_STRUCTURE 15
        #define SDL$C_TYPE_UNION 16
        #define SDL$C_TYPE_ANY 17
        #define SDL$C_TYPE_ENTRY 18
        #define SDL$C_TYPE_DFLOAT_COMPLEX 19    /* new with 5.0 */
        #define SDL$C_TYPE_FFLOAT_COMPLEX 20    /* new with 5.0 */
        #define SDL$C_TYPE_GFLOAT_COMPLEX 21    /* new with 5.0 */
        #define SDL$C_TYPE_HFLOAT_COMPLEX 22    /* new with 5.0 (guess) */
.end literal

.hl Node types
.hl2 ROOT

        The ROOT node is a dummy node, which just simplifies list
traversal. All it contains is a forward and backward link. This list
is simply a circularly linked list of MODULE nodes. The forward links points
to the first (and possibly only) MODULE node. The last MODULE node
points back to the ROOT node. Thus you can tell you've
traversed the entire list when you get back to the ROOT node,
you don't even need to keep a pointer around to this node, you can simply
traverse the nodes and see if the node type is ROOT.
        Here is an example dump of a root node:

.literal
Address: 000221D4 Type: root node
Flink: 000222EC, Blink: 000222EC
Parent: 00000000, Child: 00000000
.end literal

        Not very exciting is it?

.hl2 MODULE

        MODULE nodes define the name of the module, and have a child
pointer that points at the actual definition of that module, all the
constants, structures, etc. This child pointer always points to a HEAD
node. This HEAD node is part of another circularly linked list. This
list can contain COMMENT, CONSTANT, ENTRY, or ITEM nodes, and of course
the HEAD node itself.
        Here's an example:

.literal
        Address: 000222EC Type: module node
        Flink: 000221D4, Blink: 000221D4
        Parent: 00000000, Child: 00022434
        Name: $DTKDEF
        Comment:  Definitions for RTL DECtalk Management
.end literal

        As you can see, a module node can have a comment associated with it.
Almost every type of node can have a comment actually. Not all module
nodes have comments though (in fact, few of them do).

        I think it is a good idea to allow separate output files to be created
for each module. While many of the SDI files contain only one module, the
STARDEFAE STARDEFFL STARDEFMP STARDEFQZ files all contain multiple modules.
It makes sense to cut these modules up into seperate files. This should of
course be an option.

.hl2 HEAD
        HEAD nodes are basically placekeepers. They mark the start of
another circular list. These can be pointed to i a number of ways. The
important thing about them is that you can easily tell when you've
traversed the entire list by simply looking to see if you've bumped into
a node of type HEAD. There isn't much to a head node:

.literal
        Address: 00022434 Type: head node
        Flink: 0002254C, Blink: 0002A65C
        Parent: 000222EC, Child: 00000000
.end literal

.hl2 COMMENT

        COMMENT nodes are simple, they contain a comment, and of course
a forward pointer to the next node in the list. Almost all nodes can have
a comment associated with them, this node is just a special case in that
it contains nothing else. Note that the comment-pointer should be non-zero.
If it is zero, then there isn't any comment. It points to a PLI-string, as
described above. The comment length might actually be zero, indicating a
blank line.

        Since all comments can be handled in pretty much
the same way, I would just have a display__comment routine which
checks to see if there is a comment (comment-pointer isn't zero), and
possibly checks the comment, adding a leading blank, and possibly several
trailing blanks to create a nicely formatted string. It would be nice to
keep track of what column you're currently printing at so that you could
be sure to start the comment on a nice boundary (like, at a tab stop?),
and ends on a nice boundary (perhaps 80 chars?). Of course, you only need
to worry about the trailing part if you need to close up the comment with
something, like in C where you start with "/*" and end with "*/".
        Here are a few examples:

.literal
        Address: 0002254C Type: comment node
        Flink: 0002266C, Blink: 00022434
        Parent: 000222EC, Child: 00000000
        Comment:

        Address: 0002266C Type: comment node
        Flink: 000227A4, Blink: 0002254C
        Parent: 000222EC, Child: 00000000
        Comment:        Phone keypad keys.
.end literal

.hl2 CONSTANT
        CONSTANT nodes are pretty easy too, they simply define an integer
constant. The constants value is stored in the TYPEINFO field. If the
FLAGS field specifies that the variable is a mask, you might want to
format it in hex if possible, at least make it optional.
        Here are some examples:

.literal
        Address: 000228C4 Type: constant node
        Flink: 000229DC, Blink: 000227A4
        Parent: 000222EC, Child: 00000000
        Name: DTK$K_TRM_ZERO
        typeinfo: 00000030 (48)

        Address: 00027914 Type: constant node
        Flink: 00027A2C, Blink: 000277F4
        Parent: 000222EC, Child: 00000000
        Name: DTK$M_SQUARE
        flags: (00000002) mask
        typeinfo: 00000001 (1)
.end literal

.hl2 ENTRY

        ENTRY nodes are more complex. They define function/routine
entry points. These nodes have a child pointer that points to a circular
list of PARAMETER nodes (with a HEAD node at the head of the list).
Sometimes an ENTRY node can be empty, this only happens when there
is an ITEM or PARAMETER node with a datatype of ADDRESS (which then points
to a sublist with an OBJECT node, which may then point to the null ENTRY
node), see the second parameter in LIB$CALL__G (in LIB$ROUTINES) for
an example.
        Here are several examples:

.literal
        Address: 00024264 Type: entry node
        Flink: 000248F4, Blink: 000240F4
        Parent: 000222EC, Child: 0002437C
        Name: lib$addx
        Typename: cond_value
        datatype: longword (10)
        flags: (00000004) unsigned

        Address: 00025614 Type: entry node
        Flink: 0002572C, Blink: 00024E6C
        Parent: 000222EC, Child: 00000000
        Name: lib$ast_in_prog
        Typename: BOOLEAN
        datatype: boolean (4)
        typeinfo: 00000001 (1)

        Address: 00027E8C Type: entry node
        Flink: 000282EC, Blink: 00027914
        Parent: 000222EC, Child: 00027FA4
        Name: lib$crc_table

        Address: 0002A704 Type: entry node
        Flink: 0002A5EC, Blink: 0002A5EC
        Parent: 0002A4D4, Child: 00000000
.end literal

        Note that LIB$CRC__TABLE does not have a return value, i.e. it is a
subroutine rather than a function, or in C terms, a void function.

.hl2 ITEM

        ITEM nodes are used to declare an instance of a variable or
structure. Structure and Union declarations contain a child pointer that
points to a circular list of ITEM nodes (with a HEAD node).  How all the fields
get used depends a lot on the datatype of the item.
        Here are several examples, see the discussion of datatypes for more
info:

.literal
        Address: 0004D394 Type: item node
        Flink: 00038C4C, Blink: 0004D27C
        Parent: 00038B34, Child: 0004D4AC
        Name: NSAPKTDEF
        Prefix: NSA$
        Naked: NSAPKTDEF
        fldsiz: 448
        datatype: structure (15)
        flags: (00000080) based
        typeinfo: FFFFFFFC (-4)

        Address: 0002707C Type: item node
        Flink: 00022404, Blink: 00026F44
        Parent: 000222EC, Child: 00027194
        Name: MNTDEF
        Prefix: MNT$
        Naked: MNTDEF
        fldsiz: 4
        datatype: union (16)
        flags: (00000080) based

        Address: 00027614 Type: item node
        Flink: 0002774C, Blink: 000274DC
        Parent: 000272AC, Child: 00000000
        Name: MNT$V_GROUP
        Prefix: MNT$
        Tag: V
        Naked: GROUP
        Comment:  GROUP OPTION SELECTED
        fldsiz: 1
        datatype: bitfield (13)
        flags: (00000002) mask
        typeinfo: 00000001 (1)
        typeinfo2: 00000001 (1)

        Address: 0002EDBC Type: item node
        Flink: 0002EEE4, Blink: 0002ECA4
        Parent: 0002EB6C, Child: 00000000
        Name: ACE$W_FID
        Prefix: ACE$
        Tag: W
        Naked: FID
        Comment:  file id
        hidim: 3
        lodim: 1
        offset: 18
        fldsiz: 6
        datatype: word (14)
        flags: (00000204) unsigned dimen
.end literal

.hl2 PARAMETER

        PARAMETER nodes define parameters to argument, and are very similar
to ITEM nodes. Depending on the dayatype, there may be 'children'. If the
datatype is ADDRESS, then TYPEINFO2 field may contain a pointer to a child
list. This sublist would start with a HEAD node and contain an OBJECT node.
        Here are some examples:

.literal
        Address: 00024494 Type: parameter node
        Flink: 000245AC, Blink: 0002437C
        Parent: 00024264, Child: 00000000
        Name: addend_array
        Naked: PARAMETER_1
        Typename: vector_longword_signed
        datatype: any (17)
        flags: (00000400) in

        Address: 00027B44 Type: parameter node
        Flink: 00027C5C, Blink: 00027A2C
        Parent: 00027914, Child: 00000000
        Name: crc_table
        Naked: PARAMETER_1
        Typename: vector_longword_signed
        datatype: longword (10)
        flags: (00044600) dimen in ref vardim

        Address: 0002621C Type: parameter node
        Flink: 00025FEC, Blink: 00026104
        Parent: 00025ED4, Child: 00000000
        Name: bit_zero_address
        Naked: PARAMETER_2
        Typename: ADDRESS
        datatype: address (1)
        flags: (00004400) in ref

        Address: 0002667C Type: parameter node
        Flink: 0002644C, Blink: 00026564
        Parent: 00026334, Child: 00000000
        Name: user_procedure
        Naked: PARAMETER_2
        Typename: procedure
        datatype: address (1)
        flags: (00000401) value in
        typeinfo2: 00026794 (157588)
.end literal

.hl2 OBJECT

        OBJECT nodes define what an 'address' points to, and are very similar
to ITEM nodes. Currently it seems to only be used when a variable of type
ADDRESS is defined to be pointing to an entry point. They may be more general
than that, but I have found no cases so far.
        Here is an example:

.literal
        Address: 000560D4 Type: object node
        Flink: 00055FBC, Blink: 00055FBC
        Parent: 00055EA4, Child: 00000000
        Naked: ADDRESS_OBJECT
        datatype: entry (18)
        typeinfo2: 000561EC (352748)

        This is the sublist to which it points (via TYPEINFO2):
                Address: 000561EC Type: head node
                Flink: 00056304, Blink: 00056304
                Parent: 000560D4, Child: 00000000

                Address: 00056304 Type: entry node
                Flink: 000561EC, Blink: 000561EC
                Parent: 000560D4, Child: 00000000
.end literal

.hl Data types
        The different datatypes have somewhat different definitions,
for instance an ADDRESS datatype may use the TYPEINFO2 field to point
to a sublist which defines what the address is pointing to. A BITFIELD
needs to convey the length and the starting bit, via TYPEINFO and TYPEINFO2.

        They all have certain things in common of course. The Name, Prefix,
Tag, and Comment fields are used in the same way for each of the datatypes,
with similar meaning as used above, each being a string (or string pointer
in the case of the Comment field).
The OFFSET describes the offset into the structure at which this data occurs.
It could be used to verify that the definition you are creating matches what
is in the file by letting you keep track of what you think the offset should
be and comparing the two. Note that top level structure/union definitions
should have an offset value of 0, but sometimes they don't.
I don't know what this means, but the VWS CC module displays the
warning  "/* WARNING: aggregate has origin of nnn */" if it is non zero.
None of the definitions I have seen have any 'holes'. There is no missing
or skipped sections, so, if you declare the variables as they appear in the
lists you shouldn't have to generate any filler yourself. I don't know if
this is a function of SDL itself, or just the way they defined all the modules,
so if you want to be real carefull you could add logic to handle putting in
fills when the offsets seem to skip a chunk. I did not do this in my C module,
and I think I would recommend not doing it at this point.

        In general, the FLDSIZ field reflects the length of the data. If the
data is a single character for instance, it would be 1. If it were a
character string of length 3, then it would be 3. If it were a 5 element array
of character strings of length 3, then it would be 15. However, if the
datatype is BITFIELD, then this reflects the length in bits rather than
bytes.

        If the flags specify that this is a DIMENsioned variable, then
HIDIM and LODIM contain the high and low dimensions of an array, unless
the VARDIM flag is also set, meaning that it can have variable dimensions.

.hl2 ADDRESS
        length: 4 bytes

        As mentioned above, the TYPEINFO2 field may be used to point
to a sublist describing what the address contains. This isn't used
to any great extent, and can likely be ignored. Note that there is
a 'bug' with the LIB$ROUTINES module in that at one point there is
a TYPEINFO2 that points off to never-never land. I wrote a short macro
program to validate a pointer before following it to avoid this
sort of pitfall. SDL/DUMP will crash on it, however, many of the language
modules do work.

.literal
        Address: 00056614 Type: item node
        Flink: 00056754, Blink: 000564DC
        Parent: 000562AC, Child: 00000000
        Name: CLI$A_PROGXFER
        Prefix: CLI$
        Tag: A
        Naked: PROGXFER
        Comment:  PROGRAM TRANSFER VECTOR ADDRESS
        offset: 4
        fldsiz: 4
        datatype: address (1)


        Address: 0002667C Type: parameter node
        Flink: 0002644C, Blink: 00026564
        Parent: 00026334, Child: 00000000
        Name: user_procedure
        Naked: PARAMETER_2
        Typename: procedure
        datatype: address (1)
        flags: (00000401) value in
        typeinfo2: 00026794 (157588)
.end literal
.hl2 BYTE
        length: 1 byte

        This is just a standard one byte datatype.
.hl2 CHARACTER
        length: Varies

        This is just a standard character string. The TYPEINFO field
contains the length of the character string.

For example:
.literal
        Address: 0002DF5C Type: item node
        Flink: 0002DD0C, Blink: 0002DE24
        Parent: 0002DBF4, Child: 00000000
        Name: ACE$T_AUDITNAME
        Prefix: ACE$
        Tag: T
        Naked: AUDITNAME
        Comment:  Start of the security journal name
        offset: 8
        fldsiz: 16
        datatype: character (3)
        typeinfo: 00000010 (16)
.end literal
.hl2 BOOLEAN
        length: unknown

        This is a true/false datatype, the only place I've seen it used
are as function return values, and a field size isn't given. I think BASIC
declares them as BYTE. I do not know if they can be used in any other context,
only that they haven't been so far.

.hl2 DECIMAL
        length: unknown

        I haven't run into one of these ever, so I do not know
anything about it. In my CC module I think I will just define
it as a character array with FLDSIZ characters. I will ignore
the fact that it may be an array of DECIMAL, since I will just
be using the FLDSIZ field to declare the right amount of space, which
would already take that into account.
.hl2 DFLOAT
        length: 8 bytes
.hl2 FFLOAT
        length: 4 bytes
.hl2 GLOAT
        length: 8 bytes
.hl2 HFLOAT
        length: 16 bytes
.hl2 LONGWORD
        length: 4 bytes
.hl2 OCTAWORD
        length: 16 bytes

        I have not run into one of these ever. It is simply an
8 word (16 byte, 128 bit) quantity.
.hl2 QUADWORD
        length: 8 bytes
.hl2 BITFIELD
        length: varies

        The TYPEINFO field contains the length (number of bits) of the
bit field, and the TYPEINFO2 contains the starting bit of the bit field.
Note that the TYPEINFO field and the FLDSIZ field always match. I suppose
this is because you can't declare an array of bitfields in SDL, it may
be possible, I just haven't seen it yet.

        Here's an example:
.literal
        Address: 0002C014 Type: item node
        Flink: 0002BC7C, Blink: 0002BED4
        Parent: 0002BB64, Child: 00000000
        Name: ACE$V_fill_2
        Prefix: ACE$
        Tag: V
        Naked: fill_2
        offset: 2
        fldsiz: 6
        datatype: bitfield (13)
        flags: (00008000) fill
        typeinfo: 00000006 (6)
        typeinfo2: 00000002 (2)
.end literal
.hl2 WORD
        length: 2 bytes
.hl2 STRUCTURE
        length: varies

        A structure contains a pointer to a circularly linked list which
contains all the definitions contained within the structure. The CHILD field
is used to point to the list. If a structure definition occurs within
another structure definition, you will probably need to do something a
little different. For instance, in C you might create something like:
.literal
        struct example$whatever {
                struct { /* struct within a struct */
                        int whatever;
                } inner$struct;
        };
.end literal
        Or, you might want to create the following to save people extra
typing effort:
.literal
        struct example$whatever {
                variant_struct { /* variant_struct within a struct */
                        int whatever;
                } inner$struct;
        };
.end literal
        Note that the outer structure (example$whatever) would have a
flag of BASED set, so that could be one way of noting how to handle the
structure. In the C module, I just used a level counter, however, I
will probably switch to checking for the BASED flag. The flag BOTTOM seems
to also indicate wether you are at the end of the complete structure
definition or not. So you could check for the BOTTOM flag to determine
wether to put the name of the structure before the opening brace or after
the closing brace...

        Here are a few examples:
.literal
        Address: 00022B74 Type: item node
        Flink: 00024664, Blink: 00022A2C
        Parent: 000222EC, Child: 00022C8C
        Name: ACCDEF
        Prefix: ACC$
        Naked: ACCDEF
        fldsiz: 108
        datatype: structure (15)
        flags: (00000080) based
.end literal
        Note, the following is a main structure definition (I suppose
an easy way to tell is the fact that the FLAGS say it is based). If it
is based, and the OFFSET field is non-zero, you may want to display a warning
of some sort, the C module uses "/* WARNING: aggregate has origin of nnn */"
where 'nnn' gets replaced by the value in the OFFSET field.
.literal
        Address: 0004D394 Type: item node
        Flink: 00038C4C, Blink: 0004D27C
        Parent: 00038B34, Child: 0004D4AC
        Name: NSAPKTDEF
        Prefix: NSA$
        Naked: NSAPKTDEF
        fldsiz: 448
        datatype: structure (15)
        flags: (00000080) based
        typeinfo: FFFFFFFC (-4)
.end literal

        Note, the following is a structure contained within a structure. An
easy way to tell, besides the check for it being BASED (which I am not 100%
sure about at this point) is to maintain a level counter or some such, which
you increment everytime you start a structure or union, and decrement
when you are done with it. Note here that the OFFSET field merely points at
how far in the defining structure this structure starts.
.literal
        Address: 0002B224 Type: item node
        Flink: 0002B6BC, Blink: 0002B0DC
        Parent: 0002AEAC, Child: 0002B35C
        Name: ACE$R_FLAGS_INFO
        Prefix: ACE$
        Tag: R
        Naked: FLAGS_INFO
        Comment:  Flags for INFO type ACE
        offset: 2
        fldsiz: 1
        datatype: structure (15)
        flags: (00008000) fill
.end literal
.hl2 UNION
        length: varies

        Unions are very similar to structures. The circular list pointed
to by the CHILD pointer contains all the definitions contained within the
union.
.hl2 ANY
        length: unknown

        This is a unique datatype, the only place I've seen it used
are as function parameters, and a field size isn't given. Basically it
says ANY datatype is valid for passing into the function.

.hl2 ENTRY
        length: 4 bytes (?)

        The ENTRY datatype describes a function/subroutine entry point.
The only place I've seen it used are as function parameters and return
values, and a field size isn't given.
.hl Flags
.hl2 value
        Argument is passed by value, I would assume this only occurs in
PARAMETER nodes. Anyway, if this isn't set then I would assume the
argument is passed by reference. Perhaps more correctly if it isn't
set then the argument should be passed by the default mechanism for that
argument type, as described in Appendix C of the Architecture handbook.
.hl2 mask
        Variable (or constant) represents bit mask. You might want to display
it in hexadecimal to make it easier to comprehend.
.hl2 unsigned
        Variable is unsigned.
.hl2 common
        Unknown. I found no examples of this at all, in fact
I am guessing at the location of this bit.
.hl2 global
        Unknown. I found no examples of this at all, in fact
I am guessing at the location of this bit.
.hl2 varying
        Unknown. I found no examples of this at all, in fact
I am guessing at the location of this bit.
.hl2 variable
        There are a variable number of arguments for this routine.
.hl2 based
        Structure or union is based? I guess this means this is
an outer level structure definition (i.e. one that isn't the member of
another structure).
.hl2 descriptor
        Variable is passed by descriptor.
.hl2 dimen
        Variable is an array. See HIDIM and LODIM for array bounds.
if the target language doesn't support different array bounds you could
subtract LODIM from HIDIM and modify as necessary for the target. For example
arrays in C start at zero, and you declare how many elements are in the
array, so you use [HIDIM - LODIM + 1]. See VARDIM flag.
.hl2 in
        The parameter is input (read only).
.hl2 out
        The parameter is output (write only).
.hl2 bottom
        This seems to be used to flag the last piece of data in an outer
level structure definition. I think this is here so that you can write a
language module in a non-recursive fashion. If you use recursion then
you wouldn't really need to use this flag.
.hl2 bound
        Unknown. I found no examples of this at all, in fact
I am guessing at the location of this bit.
.hl2 ref
        Variable is passed by reference. I am not sure if this means
anything, since things are generally passed by reference unless they
are specifically set to pass by value.
.hl2 fill
        This variable is a fill item, it isn't used for anything except
to take up space.
.hl2 default
This indicates that a default value should be applied. The default value is
stored in the 'default_value' field.. SDL/NOPARSE/DUMP doesn't display
it. To see an instance of it, check out SYS$GETMSG in STARLET.
.hl2 vardim
        Array bounds can vary. I assume this can't occur in the context
of a variable definition, at least not within a structure/union, since
it wouldn't make much sense.
.hl2 link
        I don't know what this is - I'll have to try and figure it out yet.
Look at SYS$MODIFY for an example of its use. In the 'fiche it uses something
like:
.literal
        ENTRY SYS$MODIFY ALIAS $MODIFY LINKAGE $RMSCALL PARAMETER (
                ANY NAMED FAB IN OUT, {/* TYPE(FAB),
                ADDRESS(ENTRY) NAMED ERR OPTIONAL, {/* TYPE(ASTADR),
                ADDRESS(ENTRY) NAMED SUC OPTIONAL {/* TYPE(ASTADR)
                ) RETURNS LONGWORD; {/* TYPE(CONDVALUE);
.end literal
The PREFIX field contains $RMSCALL, the NAKED field contains $MODIFY,
and the TAG field is set to L.
.hl2 optional
        Argument to function is optional.
.hl2 fixed
        Seems to have something to do with fills. I don't know what this is.
.hl2 generated
        Seems to have something to do with fills. I don't know what this is.
$EOD

