.PAPER SIZE 56,64 .RIGHT MARGIN 64 .AUTOPARAGRAPH .FIGURE 15 .CENTER;Weird Tricks with the RSX Indirect Command Processor .FIGURE 7 .CENTER;- RX015 - .FIGURE 5 .CENTER;Thomas R. Wyant, III .CENTER;E. I. DuPont de Nemours .CENTER;Richmond, Virginia .FIGURE 2 .CENTER;Anthony E. Levan .CENTER;Systems Research Laboratories .CENTER;Dayton, Ohio .FIGURE 5 .CENTER;December 10, 1990 .PAGE .HL1 Housekeeping .HL2 Abstract This paper presents a compendium of advanced techniques for using the RSX Indirect Command Processor (ICP). These include the use of command procedure libraries, arrays and other structured data types, binary file I/O, screen handling both with and without FMS, error control, command line processing, and multiple precision arithmetic. This paper is aimed at an audience that is familiar with the RSX ICP, though "Introduction to the RSX Indirect Command File Processor" (RX009) should give sufficient background. .HL2 Caveats The current releases of RSX are assumed. The techniques presented here may or may not work under previous versions of RSX, or under IAS, P/OS, or RT. Since the things covered in this paper are "off the beaten track", they are more likely than usual to be affected by bugs or other differences between releases of the ICP. The examples cited all work under RSX-11M+ V4.1. I will mention bugs where I am aware of their existence. I am not the final authority on the ICP in all its multifarious versions. Errors in research and transcription do occur. I apologize in advance for these, but assume no responsibility for their consequences. The examples are all from working command procedures, which will be submitted to the Fall, 1990 RSX/IAS SIG tape. Some reformatting has been necessary to make the ICP code fit on the page properly. I have tried to preserve the functionality, but I have no way to test the example code out of context. .HL2 Acknowlegement Allen A. Watson's paper, "Nifty Things to Do with RSX Indirect Command Files", presented at the Spring, 1983 DECUS US Symposium, was both an inspiration and a reference for this paper. .HL1 Command Procedure Libraries It can be desirable to break a very large command procedure into separate modules. These will be easier to keep track of if they are kept in a Command Procedure Library. Examples include: .LITERAL LB:[1,2]INDSYS.CLB LB:[200,200]SYSGEN.CLB LB:[137,10]NETGEN.CLB .END LITERAL A Command Procedure Library is just a Universal Library (as documented in the LBR manual), created as follows: .LITERAL >LBR library.CLB/CR::::UNI:CMD .END LITERAL Once the command procedure library is created, modules can be inserted and removed just as they are for a macro or object library. Modules in a Command Procedure Library can be executed without extracting them from the library by: .LITERAL >@library/LB:module .END LITERAL Where: .LIST "o" .LE;"library" is the name of your command file library, with default file type ".CLB". If you omit this, the current library is assumed if the command is issued from inside a library, otherwise "module".CMD is used. The latter functionality is useful when debugging. .LE;"module" is the name of the module in the library to be executed. If you omit this, the module ".MAIN." is executed. .END LIST If you do not specify the /LB switch when invoking a Command Procedure, the ICP checks the attributes of the file to determine whether it is a Command Procedure Library. So, you can call your Command Procedure Library "library.CMD", and execute module .MAIN. by: .LITERAL >@library .END LITERAL You can store things other than Command Procedures in a Command Procedure Library. For instance, you could store the sources for a software package there, and have the .MAIN. module extract the sources and build the package. Under all releases of the ICP that support command procedure libraries, you can (officially) use the _.TESTFILE directive to test for the presence of a module in a Command Procedure Library or other universal library by: .LITERAL .TESTFILE library/LB:module .END LITERAL This returns the value of 1 in Special Numeric Symbol if the library file exists and the module is in it. However, some of the ICP releases involved (including at least M+ V2.1C) have a bug: will be 1 if the library file exists, regardless of whether the desired module is in it. .HL1 Structured Data Types Although the ICP has explicit support only for simple variables, there are a number of ways in which arrays and other structured data types may be built. All of these are based on building symbol names at execution time, using substitution on other symbols. An assortment of techniques is presented here. .HL2 Using a String Symbol as an Array Small arrays can be stored in a String Symbol and extracted by forming a substring. In order to do this, .LIST "o" .LE;All elements in the array must be the same size. .LE;The total size of all elements in the array must not exceed the maximum size of a String Symbol (132 bytes). .LE;The location of each element in the string must be manually calculated from the INDEX of the desired element and the SIZE of the elements, as follows: .LITERAL .SETN START (INDEX-1)*SIZE+1 .SETN END START+SIZE-1 .END LITERAL .END LIST This works best with arrays where the element is one byte long, as the index can be used directly as both the start and the end of the substring. .HL3 Example (from PRN.CMD) PRN.CMD is a utility designed to print a file on an LA-series printer connected to the printer port of a VT100- or VT200-series terminal. The horizontal pitch on an LA50 is set by an escape sequence of the form "[w". The following array translates any desired integer pitch from 1 through 16 to the nearest equivalent escape sequence argument: .LITERAL .SETS S$PCHR "5555568800224444" .END LITERAL When the desired horizontal pitch is determined (and stored in Numeric Symbol N$HPIT), the escape sequence required to set the printer to that horizontal pitch is built using the String Expression: .LITERAL ESCAPE+"["+S$PCHR[N$HPIT:N$HPIT]+"w" .END LITERAL .HL2 Using a String Symbol as an Attribute List As a variant on arrays, a String Symbol can be used to store a list of attributes to be associated with the name of the string symbol, or a portion thereof. These attributes can be tested for in place using the _.TEST directive, or extracted using the _.PARSE directive. In order to set up an attribute list: .LIST "o" .LE;The attributes are listed in the String Symbol's value, punctuated by a unique character. .LE;Attributes may be subdivided by using another unique character, to any level desired. .END LIST If the primary use of the attribute list is to search using _.TEST, it may be helpful to start the list off with a punctuating character. However, there is a special case if the attributes are all one byte long, and the only purpose of the list is to use it with the _.TEST directive: the punctuation may be omitted. .HL3 Example (from PRN.CMD) PRN.CMD is a utility designed to print a file on an LA-series printer connected to the printer port of a VT100- or VT200-series terminal. The command interface to PRN is screen driven. The arrow keys and (on the LK201 keyboard) the "Previous Screen" and "Next Screen" keys have different functions, but are implemented in basically the same way: a counter is incremented (or decremented) and clamped to a desired range. The key can also be used to amplify the action of an arrow key; this is done by adding (or subtracting) a number greater than one, which is dependent on what arrow key was used. All of this is accomplished by setting up String Symbols named after the six keys of interest. The escape sequence parser delivers control for all six keys to the same piece of code, which _.PARSEs the aforementioned String Symbols and plugs the derived attributes into its subsequent operations. The following attributes turn out to be needed: .LIST "o" .LE;The name of the Numeric Symbol updated by the key; .LE;The amplification factor to apply if is in effect; .LE;The sign of the operation on the Numeric Symbol modified by this key (+ or -); .LE;The limit beyond which the Numeric Symbol may not go; .LE;The type of test to make against this limit (eg: "<", ">", _._._.). .END LIST The String Symbols defining the keys are assembled from the concatenation of the appropriate attribute values, separated by commas: .LITERAL .SETS ARROWA "N$FLD,8,-,0.,<" .SETS ARROWB "N$FLD,8,+,N$FMAX,>" .SETS ARROWC "N$CVAL,10,+,N$CMAX,>" .SETS ARROWD "N$CVAL,10,-,N$CMIN,<" .SETS ARROW5 "N$SCR,8,-,0.,<" .SETS ARROW6 "N$SCR,8,+,N$SMAX,>" .END LITERAL These define (in order) the up, down, right, and left arrows, and the Previous and Next Screen keys. The following code is then used to execute all six keys (recognition of the keys is discussed later under "Screen Handling Without FMS"): .LITERAL .SETS KEY ARROW'CHAR' .PARSE KEY "," AXS FAC SGN LIM TST .IFT GOLD .SETN ESCA0 ESCA0*'FAC'. .SETN 'AXS' 'AXS''SGN'ESCA0 .IF 'AXS' 'TST' 'LIM' .SETN 'AXS' 'LIM' .END LITERAL This code is entered with: .LIST "o" .LE;CHAR containing the last character of the escape sequence that defines the key struck; this is forced to "5" or "6" for the Previous and Next Screen keys, which are named by a different convention than the arrow keys; .LE;ESCA0 containing the numeric value of the first argument in the escape sequence; this is forced to 1 for the Previous and Next Screen keys; .LE;GOLD set to if the key was the last key struck; otherwise it is false. .END LIST If (for example) the down arrow key is struck, the code is entered with CHAR _= "B", ESCA0 _= 1, and GOLD _= . After substitution, this gives: .LITERAL .SETS KEY ARROWB .PARSE KEY "," AXS FAC SGN LIM TST .IFT GOLD .SETN ESCA0 ESCA0*8. .SETN N$FLD N$FLD+ESCA0 .IF N$FLD > N$FMAX .SETN N$FLD N$FMAX .END LITERAL This has the effect of moving the cursor forward 1 field on the screen (or eight fields if the key is in effect). .HL2 Using Groups of Symbols as an Array An array of arbitrary size can be constructed by using a group of symbols with similar names. The names of symbols in this group consist of a constant part (which can be thought of as the array name) and a variable part (which can be thought of as the array subscript). The symbols in the array need not be the same type, and only those elements that are actually used need to be defined. An array can have more than one dimension, provided the naming convention for the elements is chosen appropriately. Array elements are referred to by using symbol substitution to construct the name of the element's symbol out of the array name and the symbol(s) used to index the array. Arrays can be indexed by symbols of any type. The formation of arrays in this manner is subject to the following restrictions: .LIST "o" .LE;The naming convention used to map array elements onto symbol names must never result in a symbol name more than six characters long. .LE;The naming convention must give rise to a unique symbol name for each element in an array. This is usually a problem only for multi-dimensional arrays, where the use of "%Rn" format control can be helpful. .LE;Symbol substitution can not be done on an arbitrary array element. The value of that array element must be assigned to another, "constant-named" symbol, which is used instead in the desired substitution. .END LIST .HL3 Example (from UPS.CMD) UPS.CMD is a command procedure to send a number of files (up to 15) to another person or persons over DECmail-11. A screen is displayed, with spaces for the user to enter the addressees, the subject matter, and the names of the files to send. All the files desired are built into a single temporary file, which is sent in batch mode. The file names are stored in an array of symbols, named S$FNnn, where "nn" is a two digit number from 00 through 14. All the file name fields on the screen are processed by the same code, which subtracts two from the field number to get the "nn" used to construct the name of the array element (subroutine ASKE is discussed under "Screen Handling Without FMS"). The file processing code follows: .LITERAL .; Prompt for the name of the next .; file. .SETN N$ROW N$FLD+6. .SETN N$FILE N$FLD-2. .SETS S$FILE S$FL'N$FILE%DR2Z' .GOSUB ASKE 40;'N$ROW%D';10\'S$FILE' .IFT .GOTO EXIT .; Convert the file name to .; uppercase. .DISABLE LOWERCASE .SETS TEXT "'TEXT%C'" .ENABLE LOWERCASE .; If the file name is null, go .; process the associated escape .; sequence, if any. .IF TEXT = "" .GOTO ESCPSI .; Check for existence of the file, .; and get the full file name. .SETS S$ERR "File 'TEXT' not found." .TESTFILE 'TEXT' .IF <> 1 .GOTO INPERR .; Store the file name in its slot in .; the array. .SETS S$FL'N$FILE%DR2Z' .; Redisplay the fully qualified file .; name. .SETS TEXT "40;'N$ROW%D';10" .GOSUB PLOTF 'TEXT'\'' .; Go handle the associated escape .; sequence, if any. .SETS S$ERR "" .GOTO ESCPSI .END LITERAL .HL2 Content-addressable Memory This is really just an extension of the concept of using a group of symbol names as an array. There is no logical reason why you can't use a String Symbol as an array subscript, provided the contents of the symbol give rise to a valid symbol name. The same consideration applies to allowing the array name to shrink to zero bytes. What you have left is a system where the entire symbol table is an array, and the symbol names are chosen to describe the information stored in the symbol. You should be aware that: .LIST "o" .LE;The String Symbol that contains the information to be looked up had better be validated first to be sure its contents represent a legal symbol name. .LE;You must check for existence of the symbol before you use it, as a random symbol name is probably NOT defined in the symbol table. .LE;Since it is probably not desirable to literally use the whole symbol table, a subset can be selected (eg: all symbols with alphanumeric names), and symbols used otherwise in the command procedure can be selected to fall outside this subset (eg: names with embedded dollar signs). .END LIST .HL3 Example (from CRASHDUMP.CMD) CRASHDUMP.CMD is a crash dump analyzer suitable for sites where the procedure for generating the crash dump analysis varies from system to system. This procedure accepts the name of the crashed system as input, and runs the appropriate analysis. This command file contains a table that lists, for each system, some help text, the name of the crash dump analyzer to use, the memory size, the crash device, the starting block on the crash device, and the name of the executive symbol table. This information is stored in a symbol named after the system, thus: .LITERAL .SETS FENNY "PDP-11/84;;256;DU;;" .SETS MARVIN "PDP-11/03;CDA42;28;DY;;" .SETS ZAPHOD "PDP-11/74;;1024;DR;;" .END LITERAL All other symbols used in this command procedure contain embedded dollar signs, so any alphanumeric symbol name is likely to be of interest. The reserved symbols COMMAN and P0 through P9 can be excluded by explicitly testing for them. When this command file is executed, it can simply prompt for the system name, insure that the entry is a valid symbol name (and is in fact the name of an existing symbol), and extract the information needed from symbol: .LITERAL .DISABLE LOWERCASE .ASKS [0:6] S$SYS What system name .ENABLE LOWERCASE .IFF .GOTO SYSERR .IFNDF 'S$SYS' .GOTO SYSERR .PARSE 'S$SYS' ";" ....... .END LITERAL .HL2 Searching a (Very) Sparse Array One of the disadvantages of using sparsely populated arrays is the inefficiency of weeding out nonexistent elements when iterating over the entire array. A better alternative may be to iterate over the entire symbol table, searching for elements of the array. DEC has provided Special String Symbol , which can be used to search the symbol table. Each time this symbol is referred to, it returns the name of the next symbol in the Symbol Table. The search is initialized by: .LITERAL .SETS "" .END LITERAL The end if the symbol table is indicated when returns a null string. If you intend to use this technique, you should make note of the following points: .LIST "o" .LE;You must refer to only once in each iteration, or you will skip symbols. A useful way to do this is: .LITERAL .SETS SYMNAM .END LITERAL .LE;The documentation of contains warnings about its availability for general use. My experience is that you might have to fiddle a bit to get it to work. See .LITERAL LB:[1,2]INDSYS/LB:INDDMP .END LITERAL for an example. I recommend against defining any new symbols inside the iteration loop. .END LIST .HL3 Example (from CRASHDUMP.CMD) CRASHDUMP.CMD is a crash dump analyzer suitable for sites where the procedure for generating the crash dump analysis varies from system to system. This procedure accepts the name of the crashed system as input, and runs the appropriate analysis. If the user input (obtained in the previous example) does not represent a valid system name, the symbol table is scanned, and a list of all valid system names is produced. This is accomplished by the following code: .LITERAL .SYSERR:.; ; ; System "'S$SYS'" is invalid. .SYSHLP:.; ; ; Valid system names are: .SETS "" .; loop through Symbol Table: .SYSHLL: .; Get next Symbol name. .SETS S$SYS .; If there is none, done. .IF S$SYS = "" .GOTO SYSASK .; If not alphanumeric, skip. .TEST S$SYS .IFF .GOTO SYSHLL .; If not a String Symbol, skip .TEST 'S$SYS' .IF <> 4 .GOTO SYSHLL .; If it is COMMAN or P0-P9, skip .TEST S$NOGO ",'S$SYS'," .IF > 0 .GOTO SYSHLL .; If it is a legal system name, .; pick it apart and display .; the identifying text; .PARSE 'S$SYS' ";" S$HELP S$JUNK ; 'S$SYS%L6' - 'S$HELP' .; Go get the next Symbol: .GOTO SYSHLL .END LITERAL .HL1 Binary File I/O I/O on files containing binary data can be done with the ICP, by converting the bytes in the file record to numeric values, and then assembling the byte values in ways appropriate to the field in which they occur. The ICP can not process records more than 132 bytes long, but otherwise any sequential file can be read. Files opened for output will have variable length records with "list" carriage control. RMSDES can be used to create sequential files with other attributes, which can then be opened by the ICP for appending data. The binary data is interpreted by using symbol substitution with %V format control to convert the bytes in the record to their numeric values. These bytes are then assembled in an appropriate manner to yield the data in the record. .HL2 Example (from SYMDMP.CMD) SYMDMP.CMD is a command file that reads a .OBJ or .STB file, and displays the types and values of the symbols it finds there. The decoding of a binary byte is relatively straightforward: .LITERAL .SETS S$B0 S$REC[5:5] .SETN O$FLG 'S$B0%V'&377 .END LITERAL This extracts byte 5 of the record and stores its value in the Numeric Symbol O$FLG. Binary words are processed by extracting two consecutive binary bytes, and combining them in the correct order: .LITERAL .SETS S$B0 S$REC[1:1] .SETS S$B1 S$REC[2:2] .SETN O$B0 'S$B0%V'&377 .SETN O$B1 'S$B1%V'&377 .SETN O$W O$B1*400+O$B0 .END LITERAL This extracts the binary word starting at byte 1 of the record, and stores it in Numeric Symbol O$W. RAD-50 words are processed in the same manner as binary words, and then converted to ASCII using %X format control: .LITERAL .SETS S$B0 S$REC[1:1] .SETS S$B1 S$REC[2:2] .SETN O$B0 'S$B0%V'&377 .SETN O$B1 'S$B1%V'&377 .SETN O$W O$B1*400+O$B0 .SETS S$SYM "'O$W%X'" .SETS S$B0 S$REC[3:3] .SETS S$B1 S$REC[4:4] .SETN O$B0 'S$B0%V'&377 .SETN O$B1 'S$B1%V'&377 .SETN O$W O$B1*400+O$B0 .SETS S$SYM "'S$SYM''O$W%X'" .END LITERAL This extracts the six RAD-50 characters stored in bytes one through four of the record, and converts them to ASCII in String Symbol S$SYM. .HL1 Screen Handling .HL2 Screen Handling With FMS The RSX-11M+ ICP comes with an interface to FMS-11. You can use this interface to generate a form-driven application. Demonstration of the FMS capability of the ICP is provided by: .LITERAL @LB:[1,2]INDSYS.CLB/LB:FMSDEM .END LITERAL In order to create your own FMS-11 driven command procedure: .LIST "o" .LE;Forms must be designed and inserted in a form library, just as for any FMS application. This implies the need for an FMS license. .LE;The _.FORM directive is used to display forms and gather input. .LE;You can use _.IFENABLED FMS to determine if FMS support is available. .LE;Special Numeric Symbol will contain the status code for the previous FMS operation. You can also (if necessary) use _.IFDF to determine whether _.IFENABLED FMS will produce a syntax error. .END LIST .HL2 Screen Handling Without FMS If you don't own RSX-11M+ and FMS-11, you can write your own screen handler for the ICP. This is not a trivial undertaking, and several points must be observed to get your screen handler to work: .LIST "o" .LE;The ICP will have to do all input through the _.ASKS directive. The prompt sequence of the _.ASKS directive is used to position the cursor for input. .LE;The ICP must be conditioned to accept escape sequences by issuing the .LITERAL .ENABLE ESCAPE-SEQUENCE .END LITERAL directive. .LE;The terminal driver must be conditioned to recognize escape sequences and pass them to the ICP by issuing the MCR command .LITERAL SET /ESCSEQ=TI: .END LITERAL or its DCL equivalent. .LE;You must issue the .LITERAL .DISABLE DISPLAY .END LITERAL ICP directive to prevent unwanted characters from being displayed on the screen by the _.ASKS directive. .LE;The ICP must parse the escape sequence off the end of the _.ASKS input string, and interpret it. .END LIST In addition to the above, there are several points which are not absolutely essential, but which are highly recommended to improve the performance and maintainability of your screen handler: .LIST "o" .LE;You should issue the .LITERAL .DISABLE DETACH .END LITERAL ICP directive, so that input from additional keystrokes will not be lost if you type faster than the ICP can process your input. Note that if you do this, programs run by the ICP may not have access to your terminal. .LE;Constant escape sequences (eg: home cursor, clear screen, setup sequences) should be assigned to appropriately named String Symbols on initialization. This enhances portability, and makes it easier to handle multiple terminal types. .LE;Variable escape sequences (eg: cursor postioning) should be generated in .GOSUB modules, for the same reasons. .LE;Literal escape sequences should not be embedded in the command file. If you violate this rule, you may not be able to TYPE the procedure on your terminal. .LE;Input should also be done in a .GOSUB module, so that the escape sequence can be easily parsed off the rest of the input. .LE;Avoid leaving the cursor on the last line of the screen. If you cannot avoid this, repaint the screen after the inevitable scroll-up. .END LIST If you want to get really sophisticated, you can .LITERAL SET /NOECHO=TI: .END LITERAL and have the ICP take care of displaying the characters on the screen. .HL3 Example (from PRN.CMD) PRN.CMD is a utility designed to print a file on an LA-series printer connected to the printer port of a VT100- or VT200-series terminal. Both the ICP and the terminal driver must be initialized to handle the escape sequences involved with screen input: .LITERAL .ENABLE SUBSTITUTION .DISABLE DISPLAY .DISABLE DETACH .ENABLE ESCAPE-SEQUENCE 'MCR'SET /LOWER=TI: 'MCR'SET /ESCSEQ=TI: 'MCR'SET /BUF=TI:132. .END LITERAL Next, a selection of control characters suitable for ASCII terminals is defined: .LITERAL .SETN NJUNK 16 ! Shift out .SETS SO "'NJUNK%V'" .SETN NJUNK 17 ! Shift in. .SETS SI "'NJUNK%V'" .SETN NJUNK 33 ! Escape .SETS ESCAPE "'NJUNK%V'" .SETN NJUNK 217 ! Single shift 3 .SETS SS3 "'NJUNK%V'" .SETN NJUNK 233 ! Ctrl Seq Init .SETS CSI "'NJUNK%V'" .END LITERAL After symbols have been defined for the individual control characters, control sequences to perform specific functions can be built. The following are suitable for ANSI terminals: .LITERAL .SETS HOME ESCAPE+"[H" !Home cursr .SETS CLEAR ESCAPE+"[J" !Clr screen .SETS CLRLIN ESCAPE+"[K"!Clear line .SETS BOLD ESCAPE+"[1m" !Bold video .SETS REV ESCAPE+"[7m" !Revers vid .SETS NML ESCAPE+"[m" !Normal vid .SETS BOTTOM ESCAPE+"[24;1H" .END LITERAL Last, a control sequence is defined to initialize the terminal to the desired state. The following initializes a DEC VT100 or VT200 series terminal by homing the cursor and clearing the screen, loading the normal ASCII character set into G0 and the graphics character set into G1, and selecting G0: .LITERAL .SETS INIT HOME+CLEAR+ESCAPE+"(B" .SETS INIT INIT+ESCAPE+")0"+SI .END LITERAL Now that the constant control sequences are taken care of, we need a subroutine to prompt for the current field. The example given below is entered by: .LITERAL .GOSUB ASKE size;line;column\text .END LITERAL This positions the cursor at the given line and column, and displays the given text in a reverse video field of width 'size'. The _.ASKS directive is used to get the response. The text part of the response is returned in String Symbol TEXT, and the escape sequence in String Symbol ESCSEQ. If TEXT is null, it is loaded with the input text string. Finally, the user's input is redisplayed (in upper case) in the field. The code to do all this is: .LITERAL .ASKE: .; Separate the command into its .; components. .PARSE COMMAN ";" FLDSIZ COMMAN .PARSE COMMAN "\" COMMAN FLDTXT .; Call on POSITN to build the escape .; sequence that positions the .; cursor. .GOSUB POSITN 'COMMAN%C' .; Build the prompt string for the .; field. .TEST FLDTXT .SETN PAD 'FLDSIZ'.- .SETS FLDTMP BLANKS[1:PAD] .SETS FLDTMP REV+FLDTXT+FLDTMP .SETS FLDTMP COMMAN+FLDTMP+COMMAN .; Get the input from the field. .SETS ESCSEQ "" .DISABLE LOWERCASE .ASKS [::FLDTXT] TEXT 'FLDTMP' .ENABLE LOWERCASE .; Select the processor for the next .; field. .SETS S$ERR "" .INC N$FLD .SETS S$GOTO "DISPAT" .IFT .RETURN .; Strip the terminating escape .; sequence from the field. .TEST TEXT ESCAPE .IF = 0 .TEST TEXT CSI .IF = 0 .TEST TEXT SS3 .IF = 0 .GOTO ASKX .SETS ESCSEQ TEXT[:*] .SETS TEXT TEXT[1:-1] .ASKX:.; .; If there was no text entered, .; supply the default. .IF TEXT = "" .SETS TEXT FLDTXT .; Redisplay the field. .TEST TEXT .SETN PAD 'FLDSIZ'.- .SETS FLDTMP BLANKS[1:PAD] .SETS FLDTMP REV+TEXT+FLDTMP .SETS FLDTMP COMMAN+FLDTMP+BOTTOM .SETS FLDTMP FLDTMP+NML+CLRLIN+HOME ;'FLDTMP' .RETURN .END LITERAL The above subroutine relies on subroutine POSITN to generate the escape sequence to position the cursor. The calling sequence for POSITN is .LITERAL .GOSUB POSITN line;column .END LITERAL and the escape sequence is returned in String Symbol COMMAN. The following is suitable for an ANSI compatible terminal: .LITERAL .POSITN: .SETS COMMAN "'COMMAN%C'" .SETS COMMAN ESCAPE+"["+COMMAN+"H" .RETURN .END LITERAL Once the text part of each field has been processed, the escape sequence that terminated it (if any) must be handled. This is done by a finite state machine, where each character of the escape sequence is dispatched for processing based on what it is and the current state of the system. In the example, the name of the current state is stored in String Symbol ESCTYP, and each character is handled by executing a _.GOTO to a label composed of the state name and the ASCII code for the character (in octal). The example parses ANSI escape sequences, composed of an introductory sequence ( or or ), some arguments (decimal numbers separated by semicolons), and a terminating character. The sequences "[" and "O" are recognized as alternates for and , respectively: .LITERAL .; Initialize the escape sequence .; parser: .ESCPSI: .SETS ESCTYP "INI" ! Parser "state" .SETN ESCAMX 0. ! Arguments .SETN ESCA0 0. ! First argument .; Main parser loop: .; Strip off the next character (if .; any), convert it to a number, and .; do a "computed" GO TO based on .; current parser state and character .; code: .ESCPSR: .IF ESCSEQ = "" .GOTO 'S$GOTO' .SETS CHAR ESCSEQ[1:1] .SETS ESCSEQ ESCSEQ[2:*] .SETN CVALUE 'CHAR%V' .ONERR ESCPSE .GOTO 'ESCTYP''CVALUE' .; Any unrecognized characters end .; up here. .ESCPSE:.; .SETN N$FLD N$OFLD .SETF GOLD .GOTO 'S$GOTO' .; First character = ; set .; state: .INI33:.; .SETS ESCTYP "ESC" .GOTO ESCPSR .; First character is , or .INI217:.; .; First was and second is .; "O"; set state: .ESC117:.; .SETS ESCTYP "SS3" .GOTO ESCPSR .; Got nnn~ = one of the "F" .; keys. Dispatch appropriately. .CSI176:.; .GOTO FKY'ESCA0%D' .; Got P = PF1 - use it as .; shift key: .SS3120:.; .SETN N$FLD N$OFLD .SETT GOLD .GOTO ESCPSI .END LITERAL .HL1 Error Control It can be useful to attempt an operation even though it may produce an error in the ICP. Although the ICP can not be set to ignore such errors, it can be set to dispatch them to the label of your choice for handling. To cause errors to be trapped to your error handler, issue the ICP directive: .LITERAL .ONERR label .END LITERAL The next error encountered will cause control to be transferred to the given label. Errors are divided into numbered classes, as described in the ICP documentation. You can set bits in Special Numeric Symbol to determine which classes are trapped to your error handler. Untrapped errors will cause the ICP to abort. By default, only Class 1 errors are trapped. On entry to the error handler, Special Numeric Symbol contains the Error Class Number of the error encountered. The _.ONERR directive must be reasserted after each error trapped. The Error Classes are pretty broad (there are only two), and don't tell you very much about what actually caused the fault. If you are expecting more than one source of error, you will need to build your own logic to distinguish between them. The manual says you should not resume processing after trapping a Class 2 Error, as the state of the ICP is indeterminate. I have found that it works in some cases, but recommend trying each case out before you build an application around it. .HL2 Example (from PRN.CMD) PRN.CMD is a utility designed to print a file on an LA-series printer connected to the printer port of a VT100- or VT200-series terminal. This example follows on from the one in the previous topic. The escape sequence parser may receive an escape sequence that it is not equipped to handle. It would be nice to recover from the ICP error that results. A trap for this is set in the _.ONERR directive just before the main parser loop dispatches the character. If the character turns out to be unrecognized, the escape sequence processing is aborted and reinitiated from that point. This will probably result in more aborts until the buffer is empty, or until an , , or is encountered. The code here also appeared in the previous example: .LITERAL .; Initialize the escape sequence .; parser: .ESCPSI: .SETS ESCTYP "INI" ! Parser "state" .SETN ESCAMX 0. ! Arguments .SETN ESCA0 0. ! First argument .; Main parser loop: .; Strip off the next character (if .; any), convert it to a number, and .; do a "computed" GO TO based on .; current parser state and character .; code: .ESCPSR: .IF ESCSEQ = "" .GOTO 'S$GOTO' .SETS CHAR ESCSEQ[1:1] .SETS ESCSEQ ESCSEQ[2:*] .SETN CVALUE 'CHAR%V' .ONERR ESCPSE .GOTO 'ESCTYP''CVALUE' .; Any unrecognized characters end .; up here. .ESCPSE:.; .SETN N$FLD N$OFLD .SETF GOLD .GOTO 'S$GOTO' .END LITERAL .HL1 Parsing an MCR- or DCL-like Syntax Command Files can be invoked in much the same way as a CLI command, and the parameters passed are available to the Command File in String Symbols P0-P9. Parsing these parameters normally takes place in two phases: .LIST "o" .LE;Parsing the file specification(s); .LE;Parsing the switches and options. .END LIST You need to design a command syntax that is both clear and easily parsed. Either MCR or DCL can serve as a model. You get a "nicer" parser if you can process all the file specifications in one loop, with an inner loop for the switches. This way, all the work can be done in one place. You may wish to check for a null command line, and get the information you need through _.ASKx directives. If the syntax for switches is properly defined, your switch parser code will be completely generic - that is, you can add switches without modifying the parser. This is done by: .LIST "o" .LE;Defining a consistent symbol name convention for storing switch settings. In the example, "V$xx" is used, where "xx" represents the switch name, and the Symbol Type of V$xx determines how the switch is processed. .LE;Defining a consistent and restricted switch syntax. In the example, no switch may have more than one argument. .END LIST .HL2 Example (from SYMDMP.CMD) SYMDMP.CMD is a command file that reads a .OBJ or .STB file, and displays the types and values of the symbols it finds there. The command syntax for SYMDMP (which also has an interactive mode) is .LITERAL >@SYMDMP outfile=infile/switches .END LITERAL in MCR syntax, or .LITERAL >@SYMDMP infile/switches outfile .END LITERAL in DCL syntax. The legal switches are: .LITERAL /BR - insert page breaks in the output file /SP - submit the output file to the print spooler .END LITERAL Switches may appear on either the input or the output file. The first part of the parsing process is to define all switches and their defaults, and separate the actual switch specifications from the rest of the command: .LITERAL .; Define and initialize the .; command switches: .SETF V$SP ! /SP (spool) .SETT V$BR ! /BR (page break) .; Determine processing mode .; (interactive or command line): .IF P1 = "" .GOTO PROMPT .; Get the file specs, from either .; MCR or DCL syntax: .IF P2 <> "" .GOTO SWIEXT .PARSE P1 "=" S$OUT S$FIL .IF S$FIL <> "" .GOTO SWIEXT .SETS S$FIL S$OUT .SETS S$OUT "" .; Peel the switches off the file .; specifications: .SWIEXT:.; .PARSE S$FIL "/" S$FIL S$SWIT .PARSE S$OUT "/" S$OUT S$JUNK .SETS S$SWIT "/"+S$SWIT+"/"+S$JUNK .END LITERAL Once the actual switches have been isolated, it is simple to loop through the list of them, checking existence and validating: .LITERAL .SWITLP: .IF S$SWIT = "" .GOTO PROCES .; Peel the next switch off, and .; get its arguments: .PARSE S$SWIT "/" S$SWX S$SWIT .PARSE S$SWX ":" S$SWX S$SWP .; Figure out whether it is asserted .; or negated: .SETT L$ASRT .IF S$SWX = "" .GOTO SWITLP .IF S$DSH = S$SWX[1:1] .GOTO SWITNM .IF S$NO <> S$SWX[1:2] .GOTO SWITAS .SETS S$SWX S$SWX[2:*] .SWITNM:.; .SETS S$SWX S$SWX[2:*] .SETF L$ASRT .SWITAS:.; .SETS S$SWX S$SWX[1:2] .; See if this switch has a .; corresponding V$sw symbol: .TEST S$SWX .IFF .GOTO SWIBAD .IFNDF V$'S$SWX' .GOTO SWIBAD .; Dispatch the rest based on the .; symbol type: .TEST V$'S$SWX' .GOTO SWIT'' .; Logical symbol. Set its value to .; the switch polarity: .SWIT0:.; .IF S$SWP <> "" .GOTO SWINPR .SETL V$'S$SWX' L$ASRT .GOTO SWITLP .; Numeric symbol. Set its value to .; the switch parameter: .SWIT2:.; .IFF L$ASRT .GOTO SWINNG .TEST S$SWP .IFF .GOTO SWIIVP .SETN V$'S$SWX' 'S$SWP' .GOTO SWITLP .; String symbol. Set its value to .; the switch parameter: .SWIT4:.; .IFF L$ASRT .GOTO SWINNG .SETS V$'S$SWX' S$SWP .GOTO SWITLP .END LITERAL .HL1 Multiple Precision Arithmetic The ICP is capable of doing arithmetic on 16-bit signed or unsigned integer values. Occasionally, this is not sufficient. Normally, access to the carry bit is necessary for extended precision, and this is not available in the ICP. However, if the operations are performed eight bits at a time, the ninth bit can be used as the carry bit in addition and subtraction. Since the product of two eight bit numbers is never more than sixteen bits, multiplication can also be done eight bits at a time, summing the cross products at the end. Division has to be done by the shift and subtract method, and is the slowest of the four conventional operations. Obviously, a 32 bit result can not be stored in a 16-bit Numeric Symbol. However, a pair of symbols will do nicely. I recommend the use of a two-element array, created as described earlier under "Using Groups of Symbols as an Array". Alternatively (or in addition), the values can be converted to decimal and stored in a String Symbol. To isolate the low-order byte of one of the operands, it suffices to perform a bitwise logical AND with the value 377 (octal). The high byte is obtained by dividing by 400 (octal). Assembling the resultant bytes into a word is the reverse of these steps. .HL2 Example (from BRU.CMD) BRU.CMD is a preprocessor for BRU, the Backup and Restore Utility. It prompts the user for how the operation is to be done, and constructs a BRU command (along with the necessary device allocations, mounts, dismounts, CON commands, and so on) based on the user's input and the current state of the system. One of the options available to the user is to initialize the output disk in a manner different than the input disk. For this option, it was desired to use the same algorithm to calculate initial and maximum index file size as is used by the INITIALIZE command. The maximum index file size depends on the volume size, and the calculation must be done in double precision. The following subroutine will add two 32 bit numbers, each stored in a pair of Numeric Symbols named by the convention "xxxxxn" where "xxxxx" is the double precision "variable" name passed to the subroutine, and "n" is 0 or 1. The calling sequence is: .LITERAL .GOSUB VADD variable variable .END LITERAL The sum is returned in the left-hand variable. The code to do this is: .LITERAL .VADD: .; Extract the variable names from .; the argument list. .SETS COMMAN "'COMMAN%C'" .PARSE COMMAN " " ST$A ST$B .SETN OT$B0 'ST$B'0 .SETN OT$B1 'ST$B'1 .; Separate the first addend into its .; constituent bytes. .SETN OT$A0 'ST$A'0&377 .SETN OT$A1 'ST$A'0/400&377 .SETN OT$A2 'ST$A'1&377 .SETN OT$A3 'ST$A'1/400&377 .; Separate the second addend into .; its constituent bytes. .SETN OT$B3 OT$B1/400&377 .SETN OT$B2 OT$B1&377 .SETN OT$B1 OT$B0/400&377 .SETN OT$B0 OT$B0&377 .; Add the corresponding bytes of the .; two addends, with carry. .SETN OT$C0 OT$A0+OT$B0 .SETN OT$C1 OT$A1+OT$B1 .SETN OT$C1 OT$C1+(OT$C0/400&377) .SETN OT$C2 OT$A2+OT$B2 .SETN OT$C2 OT$C2+(OT$C1/400&377) .SETN OT$C3 OT$A3+OT$B3 .SETN OT$C3 OT$C3+(OT$C2/400&377) .; Strip out the carry bits. .SETN OT$C0 OT$C0&377 .SETN OT$C1 OT$C1&377 .SETN OT$C2 OT$C2&377 .SETN OT$C3 OT$C3&377 .; Reconstitute the sum. .SETN 'ST$A'0 OT$C1*400+OT$C0 .SETN 'ST$A'1 OT$C3*400+OT$C2 .RETURN .END LITERAL .HL1 Bibliography .LITERAL "Nifty Things to Do with RSX Indirect Command Files" Allen A. Watson RSX/IAS SIG Symposium Handout Spring 1983 DECUS US Symposium "Nifty Things to Do with RSX Indirect Command Files" Allen A. Watson The DEC Professional March 1984 "RSX 11 System Management, A Beginner's Perspective" Arnold S. De Larisch Several examples of Indirect command files. LA50 Printer Programmer Reference Manual Documents printer escape sequences. RSX LB:[1,2]ICP.HLP On-line help file for ICP. Contains some information that is not in the manual. RSX LB:[1,2]INDSYS.CLB Sample command routines. RSX-11M/M-PLUS RMS-11 Utilities The reference for RMSDES. RSX-11M/M-Plus Task Builder Manual Documents object file layout. RSX-11M/M-PLUS Indirect Command Processor Manual The primary reference for the ICP under RSX. RSX-11 Utilities Manual Reference for the librarian task (LBR). RSX-11M-PLUS Guide to Writing an I/O Driver Reference for the UCB data structures. VT220 Programmer Pocket Guide Documents escape sequences for VT2xx terminals. .END LITERAL