TRSCMDFF.TXT (= TRS-80 CMD File Format) ------------ - "Roy's Technical Corner", Issue 4 Roy Soltoff "THE LDOS QUARTERLY", April 1, 1982, Vol.1, No.4, p.42 (Edited by Emmanuel ROCHE.) Roy's Technical Corner ---------------------- This is the fourth in my regular series of articles on LDOS technical subjects. The first explained the Data Address Mark convention used in LDOS. The second detailed the functions of the @PARAM vector and how you can ease the development of your assembly language programming efforts by incorporating @PARAM to parse command line parameters. The third dealt with device independence and its implementation on the Models I and III. Commencing with this issue, my series has been named, "Roy's Technical Corner" - and you thought RTC meant Real Time Clock, ha! One of the many interesting things unique to LDOS, amongst the many TRS-80 DOSes, is our extensive use of distinct record types in load modules. Load modules, you say? What's a load module? Let us set the record straight on this one, and clear up some of the semantics concerning load modules (according to Pete Barbutti, semantics is the study of salmon and ticks). A load module is simply a file that contains information on where it is to load into memory. It is usually loaded by the SYSTEM loader. If it can be directly executed as a program, it then becomes known as an executable load module (ELM). The usual term that has been applied to such a file is "CMD". That is because a directly executable load module is thought of as a command. We further use the default file extension of /CMD for these command files (ever wonder where Command File Utility got its name?). Another problem of semantics arises when we consider the output of the DUMP library command. The default file extension used is /CIM, short for core image. This has been an unfortunate specification because the core image dump is constructed exactly like a load module. It, in fact, IS a load module file. The term core-image, assumed originally coined under TRSDOS by Randy Cook, does in fact mean executable load modules on main frame computers. It is generally NOT the case with the TRS-80, although it can be. I have problems with the use of the CIM extension, in light of the extensive use of CMD as a directly executable module. Perhaps we shall change the default in DUMP to something more meaningful. Let us get back to the issue at hand. A load module has been said to include certain "loading" information pertinent to a loader routine. This is in contrast to a data file, a BASIC program, an ASSEMBLER source file, etc., which contain no such information. Think of the load module as a sequence of RECORDS. Note that I did not say an ordered sequence. Thus, the implication is that the records do not have to be in an ascending order (contiguous load addresses). Each record stands by itself and can be dealt with by a loader. The records must have some indicator as to what TYPE of record they are. This TYPE code is used to denote a record as a HEADER, a TRANSFER ADDRESS, an ISAM DIRECTORY, a LOAD RECORD, or other meaningful structure. A record must also have a LENGTH which is the length of the data area field. Under TRS-80 operating systems, the length is constrained to a one-byte value, and can be from 1-256 in value. The remaining part of the record is its DATA AREA and is used to store program code, directory information, messages, etc. I have identified three different fields of information for the record; TYPE CODE, LENGTH BYTE, DATA AREA. If you are familiar with BASIC random access files, you will see the similarity in the fielding of records - except that, in this case, we have variable-length sequentially-accessed records (with partitioned data sets, we also have variable-length indexed sequential accessed records). Let me now build a table of record types used in LDOS. After that is accomplished, I will then fill in the details where necessary. TYPE DATA AREA ---- ----------- 01 Object code (load block) 02 Transfer address 03 04 End of partitioned data set member 05 Load module header 06 Partitioned data set header 07 Patch name header 08 ISAM directory entry 09 0A End of ISAM directory 0B 0C PDS directory entry 0D 0E End of PDS directory 0F 10 Yanked load block 1x 1F Copyright block Any code above X'1F' is invalid as a record type. In addition, any code not listed in the above table is reserved for future use. For example, codes 11-18 are reserved for relocatable code module structures as defined in the LC implementation. As an aside, Tandy uses a type code of X'03' in lieu of the transfer address type code of X'02' to indicate a load module that is not executable. Let us look at a sample file. Start by listing the first sector of LBASIC via LIST LBASIC/CMD.BASIC (H). Notice it starts out with: 05 06 4C 42 41 53 49 43 1F 32 43 ... . . L B A S I C . . C ... What you have here is a load module header (TYPE=05). The length byte (LENGTH=06) follows the TYPE code. The 6-byte DATA AREA field is the header name. ALL RECORDS FOLLOW THIS "FIELDING" ORDER. A record is organized with a TYPE, LENGTH, DATA sequence. The X'1F' begins the second record. Quick now, what kind is it? It happens to be a copyright record with a LENGTH of X'32' or 50 decimal bytes. Incidentally, the TYPE=1F record is generated automatically by the "COM" pseudo-op in EDAS, the assembler used to maintain LDOS. Note that each record begins with the TYPE code, and that the first byte following the end of a record is always the TYPE code of the next record. The only exception is when a TYPE code indicates the end of a file. If you look further in the record displayed at relative position X'3C', or if you count 50 bytes down from the "C" of "Copyright", you will see: 01 A1 00 4E C3 8E 5B ... The record TYPE is a load block (TYPE=01), and the length of the data area is X'A1', or 161 data bytes. The 2-byte field following the LENGTH is the starting load address for the rest of the field. This is a special case. Since the LENGTH value includes the 2-byte load address, a length of x'03' would indicate only 1 load byte. A length of x'04' would indicate 2 load bytes. A length of X'FF' would indicate 253 load bytes. A length of X'00' would indicate 254 load bytes. To be able to have a data area with up to 256 bytes of loadable data, the LENGTH values of x'01' and X'02' are indicative of 255 and 256 load bytes, respectively. This is accomplished by having the system loader decrement the length value by 2 when reading a load address. The resultant value becomes the true length of the loadable data. If you let the BASIC listing proceed to the end of the file, the last 4 bytes should appear as: 02 02 C9 52 This will represent the TRANSFER ADDRESS record (TYPE=02). Again, we have a LENGTH byte which shows a 2-byte data field. The data field contains the transfer address or entry point to the program in standard low-order, high- order sequence. Of course, the transfer address value that you will see is dependent on which LBASIC you are listing. These are the 4 types that you will find in other operating systems. Now, it is time to get down to the nitty gritty. List the first record of SYS6/SYS via LIST SYS6/SYS.SYSTEM (H) {note: if you are not using 5.1.1, then the password to use is WOLVES}. Look at the area just past the copyright. You will see something like this: 08 06 1E 00 52 00 00 DB 08 06 21 ... The TYPE code of X'08' indicates an ISAM DIRECTORY RECORD. The LENGTH byte denotes a DATA area of 6 bytes. After the 6th byte, you will see another TYPE=08 starting another ISAM directory record. SYS6 is a partioned data set. The TYPE=08 records are its directory. In LDOS, the directory data area is used by the SYSTEM loader to locate where a particular member can be found in the file. The data area has sub-fields as follows. The first byte (1E) is the ISAM entry number. This value is provided to the SYSTEM loader by the SYS1 command line parsing routine upon the discovery of a library command request. The entry number is the PDS member that will execute your request. The SYSTEM loader will search the PDS directory for a match. The next 2-byte sub-field is the transfer address of the member. The transfer address is contained in the directory so that more than one transfer address can be applied to a member (i.e., a member can have multiple entry points). The 3-byte field remaining is the triad pointer which points to the first byte of the member. The triad pointer is composed of the Next Record Number (NRN) and Relative Byte Offset for the member's first byte. Consult the LDOS Technical Reference Guide File Control Block section for more information on these values. Thus, you have 6 bytes of data as specified by the LENGTH byte. In the PDS utility offered by MISOSYS, the ISAM directory record has a length of 9 because it includes a 3-byte sub-field which contains the TRUE length of the member. That piece of information is needed in many PDS commands. While you are looking at the first sector of SYS6, proceed to the first byte following the last ISAM directory record. You will observe the sequence: 0A 01 00 04 01 00 01 02 00 52 ... The TYPE=0A indicates that it is the end of a PDS directory. The SYSTEM loader uses this to discover that the requested ISAM entry happens to not be in the file being examined. If it gets to the TYPE=0A byte without a match on the ISAM number, the member is not in the directory. The LENGTH=01 is needed because ALL load module records MUST have a length byte. The DATA area contains only a single byte, X'00'. We cannot indicate a null record because a length byte of X'00' indicates 256 data area bytes. Thus, the X'0A' record type must have a minimum of 1 byte in its data area. The record following is a TYPE=04 to indicate the end of a PDS member. This record serves but one purpose when used immediately following the directory - it will result in a load file format error if you attempt to execute SYS6 or SYS7 as if they were CMD files. When not expecting a partitioned data set file, the SYSTEM loader will ignore record types other than X'01' and X'02' except for the X'04'. The file reading will terminate at the X'04' with the above-mentioned error message. The record type X'04' is usually used at the end of a partitioned data set member. If you list through SYS6, you will discover that each member ends with "04 01 00". LDOS uses this code in lieu of the transfer address code because the SYSTEM loader needs to take action different from that when a standard load file has been completely loaded. Also, the transfer address for the member is stored in the ISAM directory itself. The next record type to discuss is that used in a PDS MEMBER DIRECTORY. If you have purchased the PDS utility from MISOSYS, list it in hex. Notice that it starts with X'06' in lieu of an X'05' which is the normal header type for a load module. Well, PDS uses the X'06' in certain PDS commands to note whether the target file is a partitioned data set compatible with PDS utilities. There is a bit set in the LDOS system directory to indicate that a file is a PDS; however, that is to be used in a future release of PDS. If you list past the front end loader, you will see the start of the PDS MEMBER DIRECTORY at relative 0001:14. It reads as follows: 0C 0B 64 69 72 20 20 20 20 20 01 01 7A 0C ... . . d i r . . z . ... The TYPE=0C indicates a PDS member directory record. The LENGTH byte specifies that the data area is an 11-byte field. The DATA AREA is subfielded as an 8-byte member name (in lower case), a 1-byte ISAM number that is used to match up with a corresponding ISAM directory record) and a 2-byte field of member data. The first byte uses bit position 7 to indicate a data member, in contrast to an executable CMD program. Bit positions 4-6 are reserved for future use. Bits 0-3 and the next byte contain the date that the member was added to the PDS, and is in a format identical to that explained as DIR+1 and DIR+2 in the DIRECTORY RECORD section of the LDOS Technical Reference manual. As you look through the PDS member directory, you will get to the "0E 01 00" record which indicates the end of the MEMBER directory. The front end loader uses this to note whether the requested member is in the PDS. The ISAM directory follows. One last little one to wrap up is the record types associated with the PATCH utility. When you apply an X-patch to a file, the name of the patch file is used as a header name with a record type of X'07'. Thus, if you want to YANK the patch, the PATCH program can read through the file and search for a like- named header. If a matching header is found, PATCH will proceed as follows. Since it may be impossible to remove the patch without bubbling up any code blocks following the patch (another patch maybe?), PATCH will change the TYPE=01 records to TYPE=10 records. The TYPE=l0 records will not be loaded by the SYSTEM loader, but will be considered as non-loadable comment records. It is, thus, possible to "un-yank" a yanked patch; however, this feature is not implemented in the PATCH utility. There we have it, the relatively complete explanation for load module format records. Send in your requests for the next issues's RTC column. Here is where you can obtain detailed information on the technical aspects of LDOS. Regards until next time. By the way, this entire article was composed using LED, the LDOS Text Editor, and the latest in professional products from Logical Systems, Inc. After all text editing was done and a print copy was needed, LSCRIPT was used to print the document. EOF