Table of Contents 1 Abstract. 2 Introduction. 3 Primer overview. 4 Program 1 - A Date/Time printer. 5 Program 2 - Print directory of DTA0. 6 Register conventions. 7 Input UUOs. 8 Program 3 - Print a directory from any DECtAPE. 9 LEXINT - The 'L' in TULIP. 10 Program 4 - The last one. 11 SIXSRC and coroutines. 12 Command files. 13 Odds and ends. 14 Future plans and possibilities. 15 Reference manual. Author Eric Werme Date February, 1975 File TULIP.RND Dedication This manual is dedicated to Ed Taft who wrote TULIP while an undergraduate student at Harvard (and who tells me I should also give credit to George Mealy who was his project advisor). I hope I never find another piece of code so deserving of good documentation in such an undocumented state! TULIP - an IO package for MACRO-10 programs Page 2 ABSTRACT 1.0 ABSTRACT An LUUO driven, byte (or character) oriented IO package is described. Capabilities include octal or decimal output with leading spaces or zeros, SIXBIT or ASCII strings with extensive formatting capabilities, IO error detection with limited handling but excellent reporting, and a character oriented lexical analyzer. This is the only stuffy section in the whole manual. TULIP - an IO package for MACRO-10 programs Page 3 INTRODUCTION 2.0 INTRODUCTION One of the main reasons why most computer languages have runtime systems is to provide an easy means of doing IO. Without this aid, writing even the simplest program suddenly can become a tedious affair, especially if error handling must be done. Assembler and BLISS programmers have long been aware of these problems but for the most part have simply put up with them. Those who decide to try their hand at an IO package generally wind up with something too simple or small to be used by others, or (if they have the time) some monster containing so much that it is difficult to learn and often puts restrictions on what the programmer is allowed to do himself. (Assigning channel numbers and error recovery are common problems.) TULIP (The Uuo/Lexint Io Package) is designed for use with MACRO-10 programs, and perhaps could be interfaced to BLISS, but at the loss of some features. (A simple solution I've found to that problem is to rewrite BLISS programs in MACRO. The improvement is phenomenal!) The TULIP distribution consists of several files for both the IO package and documentation: TULIP.MAC - A universal file that supplies AC, UUO, and macro definitions that enable the TULIP features to be used. This file should be used with any program that uses TULIP. TULLIB.MAC - The TULIP library and is searched at load time with every program. It consists of two modules: UUO - The UUO handler itself. Always loaded. LEXINT - The lexical interpreter. Loaded only if the symbol LEXINT is declared external. TULIP.RND - The RUNOFF (version 10 or later, NOT 7B) documentation file that produces this manual. EXAMPL.MAC - A library of four MACRO-10 programs that are described in detail in the following primer. The listing of EXAMPL.MAC should be appended to TULIP.DOC produced by RUNOFF to provide complete documentation. One technique that most programmers overlook or are afraid to use are the LUUOs, the 31 instructions (opcodes 1-37) that trap to the user program instead of the monitor. A UUO can pass a surprising amount of information - the 5 bits of opcode, 4 of accumulator and 18 of address (after indexing and indirection) provide a way to pass a lot in a single PDP-10 word. In many programs, the IO routines are the most frequently called, and just the code needed to load specific registers and call the routines take a considerable amount of space, whereas one UUO has enough room to pass the information one or two registers commonly do in a conventional IO package. For example, instead of loading one register with a number to print and another with the field width, a TULIP UUO passes the width from 0 to 15 spaces in the accumulator field and points to the data in the address field. What might have taken three TULIP - an IO package for MACRO-10 programs Page 4 INTRODUCTION instructions is accomplished with one UUO. Because TULIP UUOs point to their data, a temporary accumulator may be used as a loop counter inside a low level loop without worrying about whether or not any IO calls will clobber it or need it as a parameter. While UUO handlers do need accumulators, any saving and restoring may now take place at a single point in the program instead of either at the caller or the IO routine itself. This is not to say UUOs are without problems - there is new overhead processing, both from the execution of the UUO (which is a rather slow instruction, slower than a MOVEM/PUSHJ pair), and time spent decoding and dispatching to the various UUO routines. Still, this time is small in some cases compared to the time the monitor spends doing the actual IO. All in all, I think they are worth the expense considering what they buy. (By the way, in cases where high speed IO is a necessity, you can do it yourself, while using TULIP to report any errors....) TULIP - an IO package for MACRO-10 programs Page 5 PRIMER OVERVIEW 3.0 PRIMER OVERVIEW As mentioned before, this document is both a primer and a reference manual. Programmers typically don't write good manuals as they prefer to spend their time programming. Furthermore, the mapping from program to document results in a reference manual, so generally people learning a new system do so the hard way. Having been on both the receiving and giving ends, I have attempted to do things right for a change. (By the way, if you think DEC turns out bad documentation, try to read IBM's sometime - PSISER and IPCF excluded!) This primer is based on the development of a program that prints DECtape directories, starting with one so simple it doesn't even use a DECtape (or print a directory) and culminating with one that has a real command string processor with command files, full word switches, and multiple input and output files. Besides describing TULIP, I will also do a little philosophizing about MACRO programming along the way. A word of warning - I will assume a fairly complete knowledge of MACRO-10 and the PDP-10 instruction set, so keep a copy of the phone book (the assembler language handbook) handy. TULIP uses DEC's C.MAC parameter file, so you should also be familiar with that. TULIP - an IO package for MACRO-10 programs Page 6 PROGRAM 1 - A DATE/TIME PRINTER 4.0 PROGRAM 1 - A DATE/TIME PRINTER Our first program simply prints the current date and time, which we will use for the heading on our directories. The date is printed twice, first using a simple mapping from conventional to TULIP IO, then again using the full capabilities of TULIP. For the time being, do not worry about accumulator definitions too much. The full set will be well described later on. Here are all the UUOs that we will use in this program: START - Actually a MACRO, this does a SALL pseudo op to suppress printing the expansion of the MOVX class of MACROs from C.MAC, and then calls an initialization routine in the UUO handler which does a RESET UUO, clears the flag register, and initializes the internal data structure. This should be called between the time the stack pointer is set up and the first TULIP UUO is executed. WDEC length,addr - Outputs an unsigned decimal number from location in a field at least characters long. For now, assume that all UUOs access the TTY; how to change that will be discussed in a section or two. Free format output is simply achieved by making be zero, or by leaving it out altogether as is usually the case. WDECI length,number - Like WDEC but prints the number in the address field of the UUO. WOCT and WOCTI - Not used, but like WDEC and WDECI, only with output in octal. WCHI character - Outputs the character given by the effective address field of the instruction. W2CHI B28+ - Outputs the two right justified characters given by the effective address field. The symbol CRLF is defined in TULIP.MAC and provides the main use for W2CHI. Note that quoted characters (e. g. W2CHI "HI") may also be used. WCH and W2CH - Not used, but acts like WCHI and W2CHI, only printing from . WNAME addr - Prints the SIXBIT word in without trailing spaces. Good for outputing device names. DISIX [next,,[SIXBIT\string\] inst 1 inst 2 ........ inst n] - Outputs a SIXBIT string while performing special actions as the following characters are processed: # Output a CRLF. (No CRLF in SIXBIT). TULIP - an IO package for MACRO-10 programs Page 7 PROGRAM 1 - A DATE/TIME PRINTER $ Output a tab. (No TAB either!) & Switch current case conversion. String output is initially in upper case mode, but may be shifted to lower and back again with this character. This allows you to write a program on a terminal no smarter than a model 33 TTY yet still use lower case messages with a minimum of grief. (Not to mention preventing continually updating updated programs to change upper case messages into lower case!) % Execute the next instruction in the list, which will normally be a TULIP UUO to output something like a number or file name. It may also be a PUSHJ to a routine to do more complicated output than one UUO allows, or it could even be a normal instruction readying something to be printed. " Quoting character. Output the next character as is with no special processing. Needed to print any of the characters in this list. ! End of string. Needed because NULL in SIXBIT is the space character. The next instruction to be executed will be from location or will be immediately after the DISIX if is omitted. DIASC [....] - Not used, but acts like DISIX, only with an ASCIZ string. The only edit character used (or needed) is CTRL-A, which acts like % does in DISIX. The initialization for Program 1 is as simple as it can be. The first three lines comprising section 1 are necessary and do it all. Section 2 is the first pass at a date printer using only the simplest of the UUOs and without taking advantage of the full capabilities of TULIP or the PDP-10 hardware. This is for two reasons: first, it makes each instruction easier to follow; and second, it makes for a more direct mapping from conventional IO packages that normally require instructions like the ADDI T2,1 since they do not generally have routines for printing a number one (or 64) greater than passed. Section 3 is a much more polished date printer and uses the full capabilities of TULIP. Not surprisingly, another advantage of TULIP pops up in the process. Since we can use all the temporary registers without needing them for parameters, we can calculate all the data first then print it in one fell swoop. By putting indexing to good advantage we can save an instruction and have TULIP print the contents of a register modified by a constant, which is a feature with more uses than in just date printers! There is one minor difference between this routine and the previous one - this one doesn't print the CRLF, instead it lets the time print on the same line. Note that to include the CRLF all that need be done is to add a # just before the ! in the DISIX edit string. TULIP - an IO package for MACRO-10 programs Page 8 PROGRAM 1 - A DATE/TIME PRINTER Section 4 as promised prints the time, pretty much the same way as the date printer works, but using a couple extra features. Here we start using the C.MAC macros, both for arithmetic use and flag manipulation. (To those who think the IDIVX macro is a bit too much, compare 60*60*1000 with 2^18!) LZEFLG is the only flag defined in the UUO processor and directs TULIP to print numbers with leading zeros when it is on. Also, we now use WDEC to format not only the hours, minutes, and seconds in the standard two characters each format, but also to print the thousanths which needs the leading zeros. There isn't much to say about section 5. From here out it's pretty standard. The MAKLST macro deserves a little study, as it uses the IRP pseudo op. Both IRP and IRPC are extremely powerful - this is just their simplest use. Date and time routines should be subroutines (even if you expect to use them only once [philosophy]), and since we're writing a program that will be printing several dates in a list, each month should have an equal length abbreviation. In the time routine, who gives a damn about the thousanths of a second? The purpose of Program 1 was to give an example of TULIP with as little overhead as absolutely necessary. Program 2 will scatter in a little more and turn the date/time routines into something practical. TULIP - an IO package for MACRO-10 programs Page 9 PROGRAM 2 - PRINT DIRECTORY FOR DTA0 5.0 PROGRAM 2 - PRINT DIRECTORY FOR DTA0 Program 2 actually goes out to a tape and prints its directory. For now, the choice is rather limited (DTA0) and the output format equally so (no choice between long and short forms). There's a little more overhead in the program such as the title and subtitle, and the code is now in the HISEG and is reentrant. Various symbols are defined to setup IO channels, push down list length, and the characteristics of DECtapes and their directories. A new output UUO is used that comes in two forms: WSIX len,addr - This form outputs a SIXBIT string characters long starting from location . No editing (as done with DISIX) will be done, making this UUO good for our use of printing file names and extensions. WSIX addr - This form outputs an indefinite length string starting at location . All editing characters will apply, including % which will probably cause the program to bomb. Don't try it! WSIX has a close relative that isn't used in Program 2: WASC addr - Output an ASCIZ string that starts in location . CTRL-A, the only DIASC editing character will not be specially processed. The length field may not be specified. There are two DISIX UUOs that transfer to location CPOPJ when done. This and CPOPJ1 are routines in the UUO handler that provide a mechanism to do a non-skip or skip return from subroutines when normal methods are inconvenient. Both are generally reached by conditional jumps, e. g. 'JUMPE T1,CPOPJ##'. One of three macros, POINTR, that simplify byte pointer usage is used within PRTDIR to extract the date field from the extension word. All three rely on byte masks which are conceptually much simpler to use than the usual bit positions. I after I wrote this, I discovered that all three macros were defined in C.MAC and not in TULIP.MAC as I had thought. I hereby present this with the hope it will act as a seed for the documentation of C.MAC sometime in the future. WID (mask) - Returns the width of mask in bits. [WID(7777)=^D12] POS (mask) - Returns the position of the rightmost bit in mask. [POS(7777)=^D35] POINTR (addr,mask) - Generates a byte pointer via POINT WID(mask),addr,POS(mask) [POINTR(DIREXT(P1),7777)=POINT 12,DIREXT(P1),35] The most important changes involve the setup for simple file IO, namely the FILE macro, FIGET, and FREL UUOs. The entire set of file utility UUOs follows the description of the FILE macro: FILE chan,dir,loblk, - Associated with each file is one or two HISEG file blocks containing the defaults for entries in their corresponding LOSEG file block. This macro sets TULIP - an IO package for MACRO-10 programs Page 10 PROGRAM 2 - PRINT DIRECTORY FOR DTA0 up the HISEG block based on these parameters: chan - The software IO channel number. dir - I for input, O for output. If both input and output are done on an IO channel, there must be one file block for each direction. loblk - Address of the LOSEG file block paired with this HISEG file block. spec(arg) - Specify nonstandard defaults, and will be a list comprised from some of the following macros: (brackets denote the defaults) DEV (n) [DSK] Device to use. NAME (n) [blank] File name. EXT (n) [blank] Extension. PPN (n) [0] Project - programmer number. Commas cannot be used in because the macro will think they mark the end of that parameter, so to specify [1,4], you must use a form like PPN(1000004), PPN(1B17+4), PPN(SYSPPN), or use angle brackets as done for INST below. STATUS (n) [0] Initial file status. )> [PUSHJ P,I1BYTE or PUSHJ P,O1BYTE] Instruction which will be used to read or write all bytes. See the register convention section for an explanation of what data is in the UUO handler registers. The defaults (and those below) are routines in the UUO handler. OTHER (l) [none] Address of the opposite direction loseg block, and is necessary if both input and output are done on this channel. OPEN (l) [ILERI1 and ILERO1] Location to transfer to on OPEN error. LOOKUP and ENTER (l) [ILERI2 and ILERO2] LOOKUP and ENTER failure address. INPUT and OUTPUT (l) [ILERI3 and ILERO3] IO errors except end of file. EOF (l) [ILERI3] End of file. Warning: each specifier must be immediately preceded by its comma or angle bracket. TULIP - an IO package for MACRO-10 programs Page 11 PROGRAM 2 - PRINT DIRECTORY FOR DTA0 With each FILE macro goes the corresponding loseg block, FBSIZE words long, which is where runtime information such as the OPEN, LOOKUP, and buffer header blocks are kept. The example used in Program 2 is rather simple, so here's the somewhat more complicated use of file blocks needed for accessing a disk file in update mode: DSKFIH: FILE DCHN,I,DSKFIL, DSKFOH: FILE DCHN,O,DSKFOL DSKFIL: BLOCK FBSIZE DSKFOL: BLOCK FBSIZE The address field of all the file utility UUOs points to a file block. In all but one case (FSETUP) they point to the loseg block. FSETUP - Generate the loseg block from the default data in the hiseg block. FIGET and FOGET - Does an OPEN on the device in the loseg file block. FLOOK and FENT - Does a LOOKUP or ENTER on the file in the loseg block. FIOPEN and FOOPEN - Does both OPEN and either LOOKUP or ENTER. FICLS and FOCLS - Does CLOSE on channel. FREL - Does RELEASE on channel. FICLOS and FOCLOS - Does CLOSE and RELEASE on channel. So, to handle the IO for our update mode disk file above, the program section will look somewhat like: FSETUP DSKFIH ;READY THE LOSEG FILE BLOCKS FSETUP DSKFOH FIOPEN DSKFIL ;OPEN, LOOKUP FENT DSKFOL ;ENTER TO GET INTO UPDATE MODE ;DO ALL IO FICLOS DSKFIL ;CLOSE UP The FILE macro as described only sets up defaults in the loseg block. To do anything other than that requires that the program set up the pertinent data itself. The complete list is in the reference manual, but the more important locations are: FILDEV - Device name FILNAM - File name FILEXT - Extension FILPPN - Project - programmer number of the file's disk area TULIP - an IO package for MACRO-10 programs Page 12 REGISTER CONVENTIONS 6.0 REGISTER CONVENTIONS Program 2 also started to make complex use of registers, so this is a good place to give the full description. Most of the PDP-10 registers are defined by TULIP and are in these catagories: Name Range Use F 0 Flags T1-T4 1-4 Temporary P1-P4 5-10 Permanent 11-13 Unassigned U1-U3 14-16 Used by UUO handler (but preserved) P 17 Stack pointer P is the stack pointer, used for subroutine calls and temporary storage. The SAVE and RESTORE macros make the latter easier to do. Both take an angle bracketed list of registers or memory locations; SAVE will push their data on the stack and RESTORE will pop it back. The parameter list to RESTORE must be opposite that used for SAVE, i. e. if a SAVE is done, RESTORE must be used to restore them in the right order. As a special (and not very necessary) frill, the CALL and RETURN macros are available to replace PUSHJ P, and POPJ P, making them useful mainly for ex-FORTRAN programmers with fond memories of a useless language. Sigh. We system wizards do get cynical in our old age.... A new instruction, MCALL, has been defined for those cases where the CALL UUO is necessary, mainly when software that uses local UUOs (negative CALLIs) is also expected to run elsewhere. C.MAC provides an interesting instruction, PJRST, which is opdeffed to be nothing more than a JRST! Nevertheless, it does serve an extremely important place in maintaining the clarity of assembler programs. Although this paragraph really falls under the 'assumed knowledge of C.MAC', I feel compelled to add it because I know of no place that really defines PJRST well, and I have seen some programs recently that misuse it. PJRST says, "This routine is done and has no more to do except call another, so I will simply jump there and its POPJ will be my return". Technically, this means that 'PJRST ' should be used whenever it can replace the following sequence: PUSHJ P,addr POPJ P, AOS (P) POPJ P, Therefore, PJRST is not meant to transfer to a 'subroutine' that expects data on the top of the stack instead of a PCword (as if a PUSH was done before the PJRST), but there is an exception. PJRST may go to code that references solely the stack, either to prepare for a skip return or to restore registers. Generally these routines have 'POPJ' in their names to advertise their function (CPOPJ and TPOPJ1 are common examples). T1-T4 are the temporary, scratch pad type registers and are used for most of the calculations done by a program. They are also extensively used for passing and returning parameters to and from TULIP - an IO package for MACRO-10 programs Page 13 REGISTER CONVENTIONS subroutines. Technically, any subroutine may clobber T1-T4, but most programs (including these sample ones) will often advertise which registers will be used. Most of the time it works out okay, the main exception being when a program becomes so large that it is difficult to trace all possible routine flows to see which registers will be wiped out. Generally, problems with large programs are not a result of just their size, but poor design and nonadherance to simple protocols such as these [philospohy (sort of)]. P1-P4 are permanent registers and normally are not clobbered by subroutines, the main exception being on a subroutine call made to set one to a value for use in later subroutines. To facilitate saving and restoring these, four subroutines (SAVE1 to SAVE4) are provided that save that number of registers (e. g. SAVE3 saves P1-P3), then call the caller. When the caller finally returns, control goes back to the save routine which restores the registers and does the real return. This has great advantages for subroutines that have several exit points since you're freed of the hassle of chasing down each one whenever the subroutine is changed to save more or fewer registers. However, there is one limitation. Subroutines that call these must exit with either a skip or non-skip return, anything else will probably bomb the program! In general, if you always assume T1-T4 get clobbered by any routine you call (except SAVE1-SAVE4) and never let a subroutine clobber P1-P4, you should have no trouble with register usage. (But note that DIRLOP relies on DATTHN not clobbering T4....) U1-U3 may not be used in such a way as to require TULIP to reference them as memory locations (i. e. WDECI (U1) is okay, but WDEC U1 is not), nor may they be used at all in the DISIX UUO instruction list (or subroutines those instructions call). Basically, if you don't use them, you'll be safe. However, when a program does its own IO via FILE's INST submacro, it must access or pass data, and these registers are set up as follows: U1 Holds byte (either set or read by routine). U2 Holds address of file block for this operation. U3 Unused, may be used by routine. The unassigned registers 11-13 are available to the programmer for any use he has, which generally is to access a global data structure. The flag register, F, currently has only one bit defined (LZEFLG) leaving the remaining 35 to the program which may be defined via the FLAG macro. FLAG requires a list of symbol names and will assign a bit from 1B35 to 1B0 to each symbol, requiring that they be accessed via the C.MAC macros (TXNE, etc.). Since bits are assigned from right to left, this normally leaves the sign bit available for any high speed flag requirements (in the sense that it may be used to transfer control in a single instruction (JUMPL) instead of the two the other flags require (TXNE/JRST)). TULIP - an IO package for MACRO-10 programs Page 14 INPUT UUOs 7.0 INPUT UUOs So far we have seen most of the output UUOs, but have said nothing about input. The next two programs will do input via TULIP, yet will not execute the UUOs. This paradox will be resolved in the next section, but since an understanding of TULIP input will be helpful, the UUOs are described here (each points to a memory location). RCH Read the next character (or any size byte) into . CCH Reread the current character (returned by the last RCH) into . LCH Reread the last character (returned by the next to last RCH) into . Input may not be backed up more than one character; attempts to do so will be ignored. The following UUOs are meant for use only with ASCII characters: RFLG Put the character flags (below) for the character in into +1. RCHF, CCHF, and LCHF - Act like RCH, CCH, and LCH followed by RFLG; hence read a character into and the flags into +1. Since there are several characters that cannot be used as parameters to a macro because they will confuse parameter decoding, the following symbols are defined so they may be easily used: NULL Null (0) BELL Control-G (7) TAB Tab (11) LF Linefeed (12) VT Vertical tab (13) FF Formfeed (14) CR Carriage return (15) CTRLZ Control-Z (32) ESC Escape (33) ALT Altmode (33) [Long past time to bury 175 and 176!] DBLQ Double quote (") SNGLQ Single quote (') LPAREN Left parenthesis '(' RPAREN Right parenthesis ')' COMMA Comma (,) SEMI Semi-colon (;) LANGLE Left angle bracket (<) RANGLE Right angle bracket (>) LSQUAR Left square bracket ([) RSQUAR Right square bracket (]) RUBOUT Yes (177) CRLF CR, LF right justified (B28+LF) TULIP - an IO package for MACRO-10 programs Page 15 INPUT UUOs Associated with most ASCII characters are one or more flags which are used to simplify character processing and parsing. The currently defined flags are: LETTER A-Z, upper and lower case. DIGIT 0-9. BLANK Space and tab. BREAK Linefeed, vert. tab, formfeed, escape, CTRL-G, CTRL-Z. LGLSIX Codes 40-137. (Valid SIXBIT characters.) TULIP - an IO package for MACRO-10 programs Page 16 PROGRAM 3 - PRINT A DIRECTORY FROM ANY DECTAPE. 8.0 PROGRAM 3 - PRINT A DIRECTORY FROM ANY DECTAPE. One of the new additions in Program 3 is that it has a version number. TULIP is involved in this too and with three macros allows any version number processing needed. VERSION (version,update,edit,who) - This puts the program version in location .JBVER using the standard DEC format which is fully described in the phone book. In addition, symbols %VVERS, %VUPDA, %VEDIT, and %VWHO are all defined with the appropriate data. VERSION also references the symbol %%%TLP, TULIP's major version number, to force it into the symbol table so the loader will complain if programs assembled with very different versions of TULIP.MAC are used. This is done as a warning if new UUOs or register assignments have been made, hinting at possible imcompatibilities. VERSION (version,update,edit,who,symbol) - [Like the previous sample call, but with a fifth parameter.] Instead of storing the version number in .JBVER, this will define to be the assembled value and is primarily meant to be used within libraries since .JBVER may not. In this manner, TULLIB.MAC defines %UUO and %LEXINT to be the versions of those two modules. VERSTR - Defined by VERSION, this expands to simply a call on the XX macro (below) using same parameters as were used for VERSION. XX ver,upd,edit,who - Defined in the program, this macro allows it to make printable strings or pretty TITLE and SUBTTL statements containing the version number. This need not be at the beginning as is done in program 3, but may be anywhere. In fact, for the purists, one could define XX at the begining of the program and call via VERSTR to set up the program title, then redfine XX later in the constants area to put the version message there! If using three macros seems a little more complicated than necessary, it is done to allow the version number to be changed by modifying only one line in the program. A couple new UUOs are used, FOSEL and its mate, FISEL. As might be expected these are actually file utility UUOs, but as they are not involved with the monitor IO UUOs I didn't bother to describe them in that section. TULIP is designed to have one input stream and one output stream in use at any given time (which is generally ideal for tasks like copying files), and these streams are set up with any file utility UUO except FSETUP and FREL. (FISEL, FIOPEN, FIGET, FLOOK, FICLOS, and FICLS all make the input stream be from the file block they point to and FOSEL, FOGET, FENT, FOCLOS, and FOCLS all make the output stream be from theirs.) To reslect the default TTCALL IO blocks, FISEL 0 and FOSEL 0 will do the job. FOSEL is used here because FIGET has already done the OPEN, so there is no reason to do everything a FOOPEN would. There is a second output stream which may be used with a special subset of UUOs and is meant for use as an error reporting stream, normally using TTCALL IO. (This is that 'allowable' use of TTCALL mentioned a couple of sections ago. It is especially handy for programs that may output to any device TULIP - an IO package for MACRO-10 programs Page 17 PROGRAM 3 - PRINT A DIRECTORY FROM ANY DECTAPE. (including TTY) but always want error messages to go to TTY. To use buffered mode IO for TTY error messages, you must either allow the TTY to be used by only the error routines or intercept attempts to open it and redirect output to the previously opened TTY file.) All that FISEL and FOSEL do is simply put their address fields in locations IFILE and OFILE, respectively. Both these locations are defined within TULLIB and normally will be of little interest to programmers. However, at times they are most useful as in saving the contents of either of them to allow a subroutine to temporarily redirect data flow and restore it before returning to the caller. Also, when debugging programs, there are many instances where it is very handy to redirect output to TTCALL so you can see what the beast is doing. DDT makes for a very simple means of manually clearing OFILE to do exactly that. The error stream also has its own file block location, EFILE, but since there is no 'FESEL' UUO, if the program wants to redirect the error stream, it must do so explicitly. Program 3 does all its own IO error handling because TULIP would only print an error message and stop the program. Instead, Program 3 will print the same message but then restart. There are four string printing UUOs that direct output to the error device: EWSIX and EWASC - Like WSIX and WASC. The length (the AC field) may not be specified. EDISIX and EDIASC - Like DISIX and DIASC, except that all TULIP output UUOs in the instruction list will print on the error device, meaning you can use UUOs like WDEC and friends. Furthermore, there are ten UUOs that report failures on the monitor IO UUOs. Half of them print on the error device and precede each message with '? Device ' for OPEN failures or '? :.[] ()' for all others. The rest print on the normal output device without the message header. The names of the error device UUOs are prefixed with 'ERR' and the normal output device ones with 'WER'. All take the address of the low segment file block involved in the address field. Input open errors: ERRIOP, WERIOP and Output open errors: ERROOP, WEROOP Currently the two pairs are identical since an OPEN failure is direction independent. Associated messages: not available does not exist TULIP - an IO package for MACRO-10 programs Page 18 PROGRAM 3 - PRINT A DIRECTORY FROM ANY DECTAPE. LOOKUP errors: ERRLK, WERLK and ENTER errors: ERRENT, WERENT - Different processing is done for error type (LOOKUP/ENTER) and device type (disk, DECtape, and other) which should solve the confusion resulting from programs like LIB40 complaining about full tapes when the disk runs out of space! ERRLK and WERLK may also be used to report GETSEG and RUN UUO failures, but the pertinent data must first be copied into the file block (device, file, extension, and PPN). Extended LOOKUP blocks are not handled so data must be copied from the extended block into the file block before executing this UUO. Associated messages: (0) File not found (0) Illegal file name (1) User File Directory not found (2) Protection violation (2) Directory full (3) File being modified (4) Already existing filename (5) Illegal UUO sequence (6) Transmission error (6) UFD or Rib error (7) Not a save file (10) Insufficient core (11) Device not available (12) No such device (13) GETSEG UUO illegal (14) Disk full or quota exceeded (15) Write-lock error (16) Insuffcient monitor table space (17) Partial allocation only (20) Block not free on allocation (21) Attempt to supercede directory (22) Attempt to delete directory (23) Sub File Directory not found (24) Search list empty (25) SFD nested too deeply (26) No-create for specified path (27) Segment not in swap area (..) Unexpected error INPUT errors: ERRIN, WERIN and OUTPUT errors: ERROUT, WEROUT - The entire 18 bit file status is printed followed by the appropriate error message. Should more than one error bit be set, only the first applicable message in the list will be printed. Again, different processing is done as in the LOOKUP/ENTER routines. TULIP - an IO package for MACRO-10 programs Page 19 PROGRAM 3 - PRINT A DIRECTORY FROM ANY DECTAPE. Associated messages: (400000) Write lock error (200000) Device error (100000) Checksum or parity error (40000) Block or block # too large (40000) Tape full (40000) Disk full or quota exceeded (20000) End of file (.....) Unexpected error TULIP - an IO package for MACRO-10 programs Page 20 LEXINT - THE 'L' IN TULIP. 9.0 LEXINT - THE 'L' IN TULIP. Despite the size of the last section, the major addition in Program 3 is the lexical analysis to parse a device name. Usage here is as simple as possible and is designed to show how to use it with as little overhead as possible (like what we did in program 1). Three basic things are necessary: a call on LEXINT to do the parse, a production table to drive LEXINT which describes how to parse the input stream, and a set of action routines which manipulate the parsed data. The heart and most interesting piece is the production table which is set up with the aid of three macros: TBLBEG label - This must be at the begining of a production table and is used to initialize internal symbols that are of no interest to us.