.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. .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 . 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 : 8; } 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 .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.