The Computer Journal, Issue 37

Z-System Corner

© Jay Sage

Reproduced with permission of author and publisher.

The main topic for this column will be the second installment of the discussion of ZFILER, the Z-System filer shell (Yes, I'm going to fool you all by actually doing as I promised last time!). As usual, there are several other items I would like to discuss briefly first. The original list included the following: (1) a Z-Node update; (2) a hint on patching those hardware-specific utilities provided by computer manufacturers that don't work right under NZ-COM so that they will work; (3) my views on the appropriate way for Z-System programs to be coded for compatibility with various stages of evolution of ZCPR3; (4) an update on making PRL files without a PRL-capable linker; and (5) a suggestion to programmers for how to deal with bad-directory-specification errors under Z-System. As usual, including all this material put TCJ's ink supply at risk, and I had room only for the first two items. Now that I have finished the article and am coming back to hone this section, I also have to add that I did not have room to complete the ZFILER discussion; the topics of customization and configuration will have to wait until another time.

Z-Node Update

As I mentioned in a previous issue, I have been hard at work trying to survey the Z-Node remote access systems (RASs) and to revitalize the network. It was Echelon's creation of that network that first got me started as a Z-System activist, and I continue to feel that it is the single most important source of mutual support for users and developers of the Z-System.

My list of currently active nodes is reproduced in Listing 1:


                              Z-Node List #49
                     Sorted by State/Area Code/Exchange

Revised Z-Node list as of December 31, 1988.  An "R" in the left column
indicates a node that has registered with Z Systems Associates.  Report
any changes or corrections to Jay Sage at Z-Node #3 in Boston (or by mail
to 1435 Centre St., Newton Centre, MA 02159-2469).

  NODE      SYSOP         CITY        STATE  ZIP    RAS Phone      PCP     Verified

   57 Steve Kitahata    Gardena         CA  90247  213/532-3336  CALAN/24  11/01/88
R   2 Al Hawley         Los Angeles     CA  90056  213/670-9465  CALAN/24  12/11/88
   35 Norman L. Beeler  Sunnyvale       CA  94086, 408/245-1420  CASJO/12  11/01/88
   25 Douglas Thom      San Jose        CA  95129  408/253-1309  CASJO/12  11/01/88
   35 Norman L. Beeler  Sunnyvale       CA  94086, 408/735-0176  CASJO/12
R   9 Roger Warren      San Diego       CA  92109  619/270-3148  CASDI/24  12/11/88
R  66 Dave Vanhorn      Costa Mesa      CA  92696  714/546-5407  CASAN/12  10/30/88
R  81 Robert Cooper     Lancaster       CA  93535  805/949-6404            12/29/88
R  36 Richard Mead      Pasadena        CA  91105  818/799-1632            11/01/88

   27 Charlie Hoffman   Tampa           FL  33629  813/831-7276  FLTAM/24  10/30/88
R  15 Richard Jacobson  Chicago         IL  60606  312/649-1730  ILCHI/24  11/01/88
R   3 Jay Sage          Newton Centre   MA  02159  617/965-7259  MABOS/24  12/31/88
   80 Paul Harmon       Minneapolis     MN  55407  612/560-9122  MNMIN/12    down

R  33 Jim Sands         Enid            OK  73703  405/237-9282            11/01/88
R  58 Kent R. Mason     Oklahoma City   OK  73107  405/943-8638

R   4 Ken Jones         Salem           OR  97305  503/370-7655            09/15/88
   60 Bob Peddicord     Selma           OR  97538  503/597-2852            11/01/88
R  24 Ben Grey          Portland        OR  97229  503/644-4621  ORPOR/12  12/25/88

R   6 Robert Dean       Drexel Hill     PA  19026  215-623-4040  PAPHI/24  12/10/88
R  38 Robert Paddock    Franklin        PA  16323  814/437-5647            11/01/88

   77 Pat Price         Austin          TX  78745  512/444-8691            10/31/88
R  45 Robert K. Reid    Houston         TX  77088  713/937-8886  TXHOU/24  10/03/88

   12 Norm Gregory      Seattle         WA  98122  206/325-1325  WASEA/24  11/01/88
R  78 Gar K. Nelson     Olympia         WA  98502  206/943-4842            09/10/88

R  65 Barron McIntire   Cheyenne        WY  82007  307/638-1917            12/12/88

R   5 Christian Poirier Montreal Quebec   H1G 5G5 CANADA  514/324-9031     12/10/88
R  40 Terry Smythe  Winnipeg    Manitoba  R3N 0T2 CANADA  204/832-4593     11/01/88
   18 Bruce Smith   Mississauga Ontario   L5E 2E5 CANADA  416/823-4521

   26 Robert Kuhmann  Belle Etoile, par St. Martin de la Brasque
                      84760 FRANCE, 11-33-90-77-60-15 (from USA)

   69 R.C. Page      Waltham Abbey, Essex, EN9 3EE, ENGLAND
R  62 Lindsay Allen  Perth, Western AUSTRALIA 6153       61-9-450-0200     12/21/88
   50 Mark Little    Alice Springs, N.T. AUSTRALIA 5750  61-089-528-852


I have added three new columns to Echelon's original format. The one on the far right shows the last date on which operation of the system was verified. The column to its left indicates for nodes accessible by PC-Pursuit the code for the outdial city and the highest bit rate supported for that city.

At this point I have at least attempted (usually several times) to call every North American Z-Node on Echelon's old list. Where contact was made, I requested that the sysops register with Z Systems Associates, and the ones who have done so are designated by an "R" in the leftmost column. For this listing I have retained a number of systems that seemed still to be interested in the Z-System but have not yet registered. However, if I do not hear from them, they will be dropped from the next list. So, if you use one of those nodes (or one of the nodes I have already dropped), please let the sysop know that you want him to continue as a Z-Node, and suggest that he delay no longer in registering. Once we have all the sysops' names and addresses, we can start to think about things like a software distribution chain to make it easier for the nodes to stay current with Z-System software developments. Many of the boards I called had only very old versions of programs.

I would like to extend a special welcome to several new Z-Nodes, and I look forward to doing this in each column as more new nodes come on line. Bob Dean has for some time run the excellent Drexel Hill NorthStar system in Drexel Hill, Pennsylvania, just outside Philadelphia. When I saw what an enthusiastic Z-System supporter he was, I asked Bob if he would like to become a Z-Node. He was delighted and has joined the network as node number 6. Ted Harmon in Minneapolis has been working for some time at getting his node (#80) up, and I hope that he will be in regular operation by the time you read this. So far I have not succeeded in connecting with his node.

Bob Cooper in Ventura, California, is the newest node (#81), and from many voice conversations with him during the past couple of months I know how enthusiastic Bob is. His node is no in full scale operation. Since newly commissioned systems generally have fewer callers than established systems, their sysops would, I am sure, especially appreciate your calls.

Patching Programs for NZ-COM

As I described in an earlier column, NZ-COM creates a Z-System automatically from the host CP/M-2.2 system by setting up a virtual system underneath the original one and forwarding calls presented to the virtual BIOS (basic input/output system, the hardware-specific portion of the operating system code) to the "real" BIOS except for warm boots, which are intercepted to prevent a reloading of the host CP/M system. This produces a software environment that is indistinguishable from a manually installed Z-System, and all programs that adhere to CP/M or Z-System standards should run perfectly.

There is, however, a class of programs that generally do not follow those rules. These are most often utilities supplied by the manufacturer of the computer to perform special operations, such as configuration of the hardware. They usually make assumptions about the internals of the operating system code - in most cases, the BIOS - under which they are running. (Regrettably, they usually take no steps to verify that the environment is what they expect - see Bridger Mitchell's column in TCJ #36.)

Programs of this type generally do not run correctly under NZ-COM, just as they would not run correctly if the user rewrote his or her BIOS without taking into account the assumptions the manufacturer made as to the location of certain data structures in the BIOS. (This same problem is less likely to occur, I believe, in a Z3PLUS Z-System running under CP/M-Plus, because Z3PLUS operates as an RSX, which was a fully defined system facility under CP/M-Plus. Manufacturers' configuration utilities are more likely to understand RSXs and operate correctly under them.)

There are two approaches to dealing with this challenge. In many cases the configuration utilities are used only when the system is initially set up (and the newly configured system is then stored on the system tracks of the boot disk). In other cases the configuration utilities are used only when the system is cold booted (i.e., powered up). These situations pose no problem, since the hardware utilities can be run under standard CP/M before the NZCOM command is issued to invoke the Z-System.

In some cases, however, the configuration utilities are needed on a more regular basis. Utilities for setting baud rates, screen attributes, or printer characteristics may fall into this class. These situations can present a considerable nuisance to the computer user, who easily becomes so accustomed to the facilities of Z-System that he or she nearly loses the ability to operate under vanilla CP/M. I can suggest two possible solutions here.

One approach is to put the configuration utility in a directory that is not on the path (or to give it a new name) and invoke it indirectly by way of an alias. The alias would initiate a SUBMIT batch operation, as described in the NZ-COM manual, that would first remove the NZ-COM system using the NZCPM command, then run the configuration utility under vanilla CP/M, and finally reload the standard NZ-COM system. (If you are very clever, you can probably make an ARUNZ alias figure out which of several standard versions of NZ-COM is running and automatically reload it.) This approach will give the appearance of successful operation under NZ-COM of a utility that actually cannot run under it. The main penalty is the extra time it takes to exit from and return to the NZ-COM system. There is also a problem if you have loaded a module (RCP, FCP, NDR, etc.) that is not the one in your standard configuration. It will be lost.

The second approach is to make the utility work properly under NZ-COM. In many cases I have been able to accomplish this without the source code for the utility by using the technique described below. But be forewarned; the technique will not always work.

Most of these BIOS-specific utilities determine the address of the data structures to be modified by adding an offset to the BIOS warm boot entry point whose address is obtained from the warm boot vector (jump instruction) stored at address 0000H in a CP/M system. Usually the instruction LD HL,(0001) is used to load the address into the HL register. The problem is that under NZ-COM this vector points to the NZ-COM virtual BIOS, and offsets from it generally fall right in the middle of one of the Z-System modules. Not only does the utility fail to make the desired change to the machine's real BIOS; it even corrupts some other code, resulting in behavior that ranges from unpredictably bizarre to instantly catastrophic.

The simplest corrective patch consists of replacing the LD HL,(0001) indirect load instruction with a LD HL,WBOOT direct load instruction, where WBOOT is the actual warm boot entry point address of the real BIOS. This kind of patch is performed by using some utility to scan the utility's code for occurrences of the three-byte sequence 2A (load HL indirect immediate), 01, 00 (the immediate address 0001H). ZPATCH is a natural candidate for performing the search, but it unfortunately uses 00 as its string terminator and thus cannot search for a zero byte. Perhaps Steve Cohen will eliminate this minor shortcoming in a future version of ZPATCH (hint, hint - I know you're reading this column, Steve).

The next step is to replace the 2A byte with 21, the direct load opcode. The other two bytes, 01 and 00, are replaced by the BIOS address that you have determined previously (perhaps by looking at the contents of memory location 0001H while running normal CP/M). The low byte is entered first in place of the 01 (it will always be 03). The second byte will be a some relatively large number, almost always with a first hex character of D, E, or F.

Blindly replacing sequences as described above does have its risks. Without careful inspection you cannot be sure that the sequences are being used to perform the assumed function. If you are an experienced coder, you can use a disassembler (such as the one built into debuggers like DDT and DDTZ) to examine the code. The LD HL,(0001) should be followed fairly soon by an ADD HL,DE or ADD HL,BC to add the offset to the BIOS structure to be modified. There is also always the possibility that the utility gets the address it needs in some other way (for example, LD A,(0002) will get the page address of the BIOS).

The procedure I just described "hardwires" the utility to a BIOS at a specific address. This is fine until you someday set up a new CP/M host system with a different BIOS starting address or until you give this modified version to a friend with a different BIOS. By then you will have forgotten all about these patches and will be pulling your hair out trying to figure out why the utility that worked perfectly before is now misbehaving. By then you will also have forgotten exactly what was patched and will not know how to fix the utility.

A more sophisticated patch will allow the program to work with a BIOS at any address. This approach follows Bridger Mitchell's philosophy of "know your environment." The patch checks to see if it is running under NZ-COM and makes the changes only when it is.

Source code for this patch, which can be applied using the MLOAD utility, is given in Listing 2:


This is macro code that defines an ENV and TCAP for a CP/M system.

; AUTHOR:       Jay Sage
; DATE:         June 16, 1991

; --------------------------------------------------------

; System configuration information (***** USER EDIT *****)

cpumhz  equ     4               ; CPU speed in MHz

; Operating system addresses and sizes.

biospg  equ     0d1h            ; Page where BIOS starts

bios    equ     100h * biospg
doss    equ     28              ; Size of DOS in records
dos     equ     bios - 80h * doss
ccps    equ     16              ; Size of CCP in records
ccp     equ     dos - 80h * ccps

; Information about drives and user areas available

;               PONMLKJIHGFEDCBA
drvec   equ     0000000000001111B
highdsk equ     'D'             ; Letter of highest drive
maxdisk equ     highdsk - '@'   ; Highest drive (A=1)
maxuser equ     31              ; Highest user area

; Data about console screen and printers

crtwid  equ     80              ; Width of CRT screen
crtlen  equ     24              ; Number of lines on screen
crtuse  equ     crtlen -2       ; Number of lines to use

prtwid  equ     80              ; Printer width
prtlen  equ     66              ; Printer total length
prtuse  equ     prtlen - 8      ; Printer lines to use
prtff   equ     1               ; Formfeed flag (1 if used)
; --------------------------------------------------------

; Here is a macro to define and internal ENV for use
; under CP/M.

cpmenv  macro

        jp      0       ; Dummy jump address

        db      'Z3ENV' ; Environment ID
        db      81h     ; ENV type

        dw      0       ; external path
        db      0       ; elements in path

        dw      0       ; RCP address
        db      0       ; number of records in RCP


        dw      intenv  ; ZCPR3 Environment Descriptor
        db      2       ; number of records in ENV


        db      cpumhz  ; Processor Speed in MHz

        db      maxdisk ; maximum disk
        db      maxuser ; maximum user

        db      1       ; 1=OK to accept DU, 0=not OK

        db      0,0

        db      crtwid  ; width of CRT
        db      crtlen  ; number of lines on CRT
        db      crtuse  ; number of lines of text on CRT

        dw      drvec
        db      0

        db      prtwid  ; data for printer
        db      prtlen
        db      prtuse
        db      prtff

        db      0,0,0,0

        dw      ccp
        db      ccps

        dw      dos
        db      doss

        dw      bios

        db      'SH      '      ; shell variable filename
        db      'VAR'           ; shell variable filetype

        db      '        '      ; filename 1
        db      '   '           ; filetype 1


;  Fill unused space with nulls

        rept    128-($-intenv)
        db      0

;  End of Environment Descriptor -- beginning of TCAP

; ***** USER EDIT *****

; Extended Termcap Data

ESC     EQU     27      ; ASCII escape character

; I have adopted the convention that a terminal name is
; terminated with a space character, therefore no spaces
; within the name. Also that the terminal name is unique
; in the first eight characters.

NZTCAP: DB      'WYSE-50D     ' ; Terminal name (13 bytes)

; The Graphics section is no longer fixed so we must
; provide an offset to it.  One byte is sufficient for a
; two-record TCAP.

        DB      GOELD-NZTCAP    ; Offset to GOELD

; Bit 7 of B14 indicates the new Extended TCAP.  Bits 6-0
; are undefined.

        DB      10000000B       ; Extended TCAP

; B15 b0        Standout        0 = dim, 1 = inverse
; B15 b1        Power Up Delay  0 = None, 1 = 10-sec delay
; B15 b2        No Wrap         0 = Line Wrap, 1 = No Wrap
; B15 b3        No Scroll       0 = Scroll, 1 = No Scroll
; B15 b4        ANSI            0 = ASCII, 1 = ANSI

        DB      00000111B

        DB      'K'-'@'         ; Cursor up
        DB      'J'-'@'         ; Cursor down
        DB      'L'-'@'         ; Cursor right
        DB      'H'-'@'         ; Cursor left

        DB      00              ; Clear-screen delay
        DB      00              ; Cursor movement delay
        DB      00              ; Clear-to-end-of-line delay

; Strings start here.

        DB      ESC,'+',0       ; Clear-screen string
        DB      ESC,'=%+ %+ ',0 ; Cursor movement string
        DB      ESC,'T',0       ; Clear-to-end-of-line
        DB      ESC,')',0       ; Standout-on string
        DB      ESC,'(',0       ; Standout-end string
        DB      0               ; Terminal init string
        DB      ESC,'(',0       ; Terminal deinit string

; Extensions to Standard Z3TCAP

        DB      ESC,'R',0       ; Line Delete
        DB      ESC,'E',0       ; Line Insert
        DB      ESC,'Y',0       ; Clear-to-end-of-screen

; Set Attribute strings once again included.

        DB      ESC,'G',0       ; Set Attributes
        DB      '0248',0        ; Attributes

; These two allow reading the Terminal's screen.

        DB      ESC,'?',0       ; Read current cursor pos
        DB      ESC,'6',0       ; Read line until cursor

; Graphics start here.

GOELD:  DB      0               ; On/Off Delay

; Graphics strings offset from Delay value.

        DB      ESC,'H',2,0     ; Graphics mode On
        DB      ESC,'H',3,0     ; Graphics mode Off
        DB      ESC,'`0',0      ; Cursor Off
        DB      ESC,'`1',0      ; Cursor On

; Graphics Characters

        DB      '2'             ; Upper left corner
        DB      '3'             ; Upper right corner
        DB      '1'             ; Lower left corner
        DB      '5'             ; Lower right corner
        DB      ':'             ; Horizontal line
        DB      '6'             ; Vertical line
        DB      '7'             ; Full block
        DB      ';'             ; Hashed block
        DB      '0'             ; Upper intersect
        DB      '='             ; Lower intersect
        DB      '8'             ; Mid intersect
        DB      '9'             ; Right intersect
        DB      '4'             ; Left intersect

;  Fill unused space with nulls

        REPT    128-($-NZTCAP)
        DB      0

;  End of NZTCAPD



There are several pieces of information that you will have to determine in advance and enter into the patch code. I have put all that information at the front of the patch using macros where appropriate. If you do not have a macro assembler, you can always put the material directly into the code where the macros are called instead.

First, as before, you have to determine all the addresses at which indirect loads from address 0001 have to be changed to direct loads. These values have to be placed in the patch address table in the patch code. Since the patch will be added to the end of the existing utility code, you will also have to determine that address. You can calculate this from the file size of the COM file in records as displayed either by STAT or by SD with the "C" option. Alternatively, you can read the COM file into a debugger and note the next free address it reports. This address must be entered as the value of the symbol PATCHADDR.

Most of the utility programs I have patched this way start at 100H with a jump to the actual working code. The destination address of that jump must be determined and entered as the value of the symbol STARTADDR. If the utility does not begin with a jump, then you will have to examine the code at 100H and determine the instructions that occupy the first three or more bytes. These instructions should be entered into the REPLACED macro in the patch. The address of the next instruction after the ones replaced should be entered as the value for STARTADDR.

Once you have put all the necessary data into the UTILPAT.Z80 source code, it should be assembled to a HEX file. Then the patch can be added to UTIL.COM to make NEWUTIL.COM by using the following command:


Be sure to save the original program, and test the new version carefully. One additional word of caution. Some utilities cannot be expected to work under NZ-COM no matter what you do. For example, a utility that takes the running CP/M system and writes it to the system tracks will fail because under NZ-COM the only part of the CP/M system that is still present is the BIOS. For the same reason, programs that try to patch the BDOS will fail.

ZFILER, Installment 2

Last time we covered most of the built-in functions and had left the macro commands for this time. One built-in function was also deferred, the option command "O", and we will take up that subject first.

The Option Command

When the option command letter "O" is pressed, a special options screen is displayed. Eleven operating characteristics can be changed from a menu with the following appearance (approximately):

A. single replace query        Y
B. group replace query Y
C. archive replace query N
D. verify query Y
E. verify default Y
F. suppress SYS files Y
G. sort by file name N
H. set copied file attributes Y
I. use dest file attributes Y
J. archive destination Y
K. search path for CMD file N

We will explain the meaning of each of these options in a moment. First a few words about the mechanics. While the options menu is displayed, pressing the index letter at the left will cause the setting of the corresponding option to be toggled, and the new state will be shown in the column at the right. The listing above shows the initial state of the options in my personal version of ZFILER. When you are finished toggling options, just press carriage return to return to the main ZFILER menu. These option settings are stored in the ZFILER shell stack entry and will thus continue in effect through all ZFILER operations until the command "X" is used to terminate the shell.

The first three options concern how ZFILER responds when copying (or moving) files and a file of the same name already exists in the destination directory. Item A applies when individual files are copied (commands "C" and "M"); item B applies when a group copy is performed (commands "GC" and "GM"); and item C applies when performing an archiving operation (command "GA"). If the option is "YES", then ZFILER will prompt one before existing files are erased and give one the chance to cancel the operation for that file, leaving the existing file intact. If the option is toggled to "NO", then existing files will be overwritten without even a message.

The next two options affect the verification of the copied file in the destination directory. Item D determines whether or not the user will be asked about verification. If this option is set to "N", then the state of option E will determine whether or not verification is performed on file copies. If this option is set to "Y", then before each copy, move, group copy, or group move, ZFILER will put up the prompt "Verify (Y/N)?".

The next two options affect the way files are displayed on the screen. If item F is set to "Y", then files with the "system" or SYS attribute will be suppressed, that is, not included among the selected files on which ZFILER acts. This is a reasonable choice for this option, since the most common use of the SYS attribute is to make the files disappear from consideration during file maintenance and display operations. Item G on the options menu determines whether files are sorted first by name and then by type or vice versa. Changing this option is presently equivalent to the "A" command from the main ZFILER command menu.

The next three options concern how file attributes are treated when files are copied. One possibility is to create new files with a clean slate of attributes (that is, all attributes reset: not read-only, not SYS, not archived). This is what will happen when option H is set to "N" (but note option J, which may override this). When the attributes of the destination file are to be set, they can be set in two possible ways. If a file of the same name existed in the destination directory, then its file attributes could be used for the copy that replaces it. This is what will be done if option I is set to "Y". If option I is set to "N" or if there was no matching file in the destination directory, then the attributes will be set to match those of the source file.

Option J can set a special override for the archive or ARC attribute. If the option is set to "N", then the ARC attribute is treated just like the other attributes according to options H and I. If option J is set to YES, then the destination file always has its ARC attribute set.

There was at one time a great deal of controversy over the way the ARC attribute is handled under ZFILER. At one time it was always reset, so that the destination file would be marked as not backed up. Another school of thought asserted that, on the contrary, the file was backed up, since there was a copy of it on the source disk from which the file was copied. That latter argument made considerable sense in the case of copying files from a master disk to a RAM disk before a work session. Here it was certainly important to start with all files marked with the ARC attribute so that one could easily tell at the end of the session which files had been modified so that they could be copied back to the permanent storage medium.

All in all, I never understood this controversy. Both approaches clearly have merit, and since ZFILER supports both, I saw no reason for all the argument. In a future version of ZFILER, I think I would like to add a flag word that would indicate which drives should automatically set the ARC flag when the J option is set to YES. That way, the option could be made to apply to RAM drives only.

The final item on the option menu, option K, determines how the macro command file ZFILER.CMD (see discussion below) will be located. There are two choices. If option K is set to YES, then ZFILER will look for it first in the currently displayed directory and then along the entire ZCPR3 search path. This option is useful if one wants to have different macro command files that apply to specific directory areas. Alternatively, if option K is set to NO, then ZFILER locates the CMD file without using the path. Depending on how ZFILER is configured (this will be discussed another time), the file will be sought either in the root directory of the path (the last directory specified on the search path) or in a specific drive/user area coded into ZF.COM. This alternative results in faster operation, especially if the specified directory resides on a RAM disk.

The options controlled by the option menu can also be permanently changed in the ZFILER program file using a patching utility like ZPATCH. In the first page of the file, you will see the ascii string "OPT:". The eleven bytes following this string contain the startup values for the eleven options. Patch a byte to 00 for NO or FF for YES.


Although ZFILER can accomplish many tasks using its built-in functions, its real power comes from the macro facility, which allows it to be extended to include any functions that can be performed using combinations of other programs. This is where ZFILER really makes use of its power as a shell. First I will describe how the macro facility is used, and then I will describe how the user defines the macro functions. As with the built-in functions, macro functions can operate either on single files or on groups of files. The single-file macro facility is well developed and was already present in nearly the same form in VFILER; the group macro facility is new with ZFILER and has not been fully developed yet.

Invoking Macros

One way to initiate a macro operation on the pointed-to file is to press the macro invocation key, which is normally the escape key. A prompt of "Macro:" will appear after the normal ZFILER command prompt. At this point you have several choices. If you know the key corresponding to the macro you want to run, then you can simply press that key. ZFILER will then construct a command line and pass it on to the command processor for execution. If ZFILER is configured for instant macro operation (it generally is), then macros associated with the number keys "0" through "9" can be initiated without the macro invocation key; the number key entered alone at the main ZFILER command prompt will generate the macro function.

If you press the macro invocation key a second time, a user-created help screen will be displayed. This screen generally lists the available macro functions. You can now press the key for the desired function, or you can press carriage return to cancel the macro operation and return to the main ZFILER menu. The help menu screen will also be displayed if you press the "#" key. This is a holdover from VFILER and arises in part because of the structure of the file in which the macros are defined (more on this shortly).

Group macros are invoked in a similar way from the group function command line. After you have tagged a group of files, press the "G" key to enter group mode. The prompt will list only the built-in group functions, but if you press the macro invocation key, you can proceed as described above for single-file macro operations, except that the macro function will be performed on each of the tagged files.

The group macro facility works a little differently than the single-file macro facility. Since the command line would generally not be long enough to contain the commands for all the tagged files, the group macro facility works by writing out a batch file for processing by ZEX or SUBMIT. In this way there is virtually no limit to the number of files on which group macros can operate.

There are many configurable options (described below) that are associated with the group macro operation. These include the name of the ZEX or SUB batch file, the directory to which it is written, and the command line that ZFILER generates to initiate the batch operation. The NZ-COM version of ZFILER uses a file called ZFILER.ZEX and the command line "ZEX ZFILER". The Z3PLUS version, under which ZEX will not run, uses a file called ZFILER.SUB and a command line of "SUBMIT ZFILER".

Since macros (and the main menu "Z" function) work by passing commands to the command processor, file tags will be lost in the process, and when ZFILER resumes operation, it starts afresh. In a future version of ZFILER, I hope to preserve the tag information by having it optionally written to a temporary file (the shell stack entry is far too small) and read back in when ZFILER resumes.

Defining Macros - The CMD File

Now let's learn how to define the macro functions we want. As I indicated earlier, the macros are defined in a file called ZFILER.CMD (the ZFILER ComManD file). In the version of ZFILER distributed with NZ-COM and Z3PLUS, the CMD file is searched for in the root directory of the ZCPR3 command search path. As described earlier, the option menu allows the entire path to be used. There are also some additional configurable options that will be discussed another time. You must be sure to put your ZFILER.CMD file in the appropriate directory. If the file cannot be located, you will still get the macro prompt, but, after you have specified a macro key, the error message "ZFILER.CMD NOT Found" will be displayed.

The ZFILER.CMD file is an ordinary text file that you can create with any editor or wordprocessor that can make plain ascii files (WordStar in nondocument mode, for example). The CMD file has two parts. The first part contains the macro command definitions; the second contains the help screen (described earlier).

In the first part of the CMD file, each line defines a macro. The character in the first column is the key associated with that definition (case does not matter). Macros can be associated with the 10 number keys, 26 letter keys, and all printable special characters except for "#" (explained below). The space character and all control characters are not allowed. Owing to an oversight, the rubout character can be associated with a macro!

After the character that names the macro there can be any number of blanks (including zero). If the first non-blank character is "!", then the "strike any key" (shell-wait) prompt will appear before ZFILER puts up the file display after a macro command is run. This should be used whenever the macro will leave information on the screen that you will want to read. After the "!" there can again be any number of spaces. Any remaining text on the line is taken as the script for the macro command.

The second part of the CMD file starts when a "#" character is found in the first column (hence the exclusion of that character as a macro name). Once that character appears, all remaining text, including text on the line, will be used as the help screen. Since ZFILER will add some information to the display (the name of the pointed-to file and a prompt), you will generally want to keep the help screen to no more than 20 lines, including an extra blank line at the end for spacing. With some experimentation you will get the hang of designing this screen.

Macro Scripts

ZFILER macro scripts are similar to those in ARUNZ and in the other menu shells (MENU, VMENU, FMANAGER) in that parameter expressions can appear. The critical parameters - the ones that implement functions that cannot be achieved any other way - are those that convey information about the directory currently displayed by ZFILER and about the pointed-to file. Parameters consist of a "$" character followed by one of the characters listed below.

User prompt parameters

 ' User input prompt
" User input prompt

Parameters for directories

- currently displayed directory
C DIR form
D Drive letter
U User number
- home directory (from which ZFILER was invoked)
H DU form
R Home DIR

Parameters for pointed-to file

 P Full information (DU:FN.FT)
F File name (FN.FT)
N File name only
T File type only

Special parameters

 ! GO substitution indicator
$ The dollar character

The parameters are listed in a special order above, and we will explain that later. First we will just present the meaning for each parameter.

The parameter expressions $" and $' are used to display a prompt message to the user and to read in a response string. Single and double quotes are equivalent. Once the prompt parameter has been detected, all subsequent characters up to one of the quote characters are displayed as the user prompt. Thus, if I am not mistaken, there is presently no way to put either quote character into the prompt. The end of the line or the end of the file will also terminate the prompt.

No special character interpretation is performed while expanding the prompt. If you want to make fancy screens, you can include escape sequences and some control characters (obviously carriage return won't work). In the future, ZFILER should be enhanced to provide a means to generate all control characters, to allow special characters to invoke screen functions based on the current terminal definition, and to expand directory and file parameters in the prompt.

Now for the directory parameters. Parameters C, D, and U return information about the currently displayed directory, while H and R return information about the home directory, the one from which ZFILER was originally invoked. PLEASE NOTE: macros always operate from the home directory. The reason for this is that ZFILER can display directories with user numbers higher than 15 even when it is not possible to log into these areas. If you want to operate in the displayed directory, then your script must include an explicit directory-change command of the form "$D$U:" at the beginning (or "$C:" if your system requires the use of named directories) and a command of the form "$H:" (or "$R:") at the end.

One special note about the parameters that return directory names. If the directory has no name, then the string "NONAME" is returned. This will presumably not match any actual name and will lead, one hopes, to a benign error condition. These parameters are included only for systems that do not allow directories to be indicated using the DU form (I hope that few if any systems are set up this way).

Now we come to the four file name parameters. They allow us to generate easily the complete file specification or any part of it. Note that "$F" is not quite the same as "$N.$T". The latter always contains a dot; the former does not if the file has no file type.

Finally, we have two special parameters. "$$" is included to allow a dollar sign character to be entered into the script. "$!" is a control parameter that is used only when a group macro is executed. If it is placed immediately before a token (string of contiguous characters), then that token will be replaced by the string "GO" on all but the first expansion of the script. This allows group macro scripts to operate faster by avoiding repetitive loading of a COM file. It must be used with great care and consideration, however, for reasons that I will not go into here.

Rules for Script Expansion

ZFILER follows a specific sequence of steps when expanding a script, one that gives it a special feature that, I would guess, few users are aware of. The first step in the expansion is to process only the user-input prompt parameters, substituting for the prompt whatever the user entered in response. This results in a modified script that is then processed by the second step in the expansion. Because the expansion is handled this way, the user input Scan include ZFILER script parameters! Thus the script can be used to write a script. You will see an example of this later.

The second step in the expansion is to substitute values for the directory parameters, which are a kind of constant. They do not change as a function of the pointed-to file. Finally, in a third step, the remaining parameters are expanded. For group macros, this final step in the expansion is repeated for each of the tagged files. The file parameters are expanded differently for each file, and, starting with the second tagged file, the "$!" parameter causes "GO" substitution.

Macro Examples

Listing 3 shows an example of a ZFILER.CMD file, one designed to illustrate some techniques of macro writing:


Q   ql $p

U ! if $t=?q?;$!sys:uf $p $d$u:;else;$!sys:uncr $p $d$u:;fi

S ! $!sfa $p $" SFA Options (/o,o.. o=ARC,-ARC,R/O,R/W,SYS,DIR): "
K ! $d$u:;$!crunch $f $"Destination directory (DU:) -- ";$h:
B   $d$u:;crunch $f $"Destination directory (DU:) -- ";sfa $f /arc;$h:

M ! /move $p $"Destination directory for move: "

X ! $d$u:;:$n $" Command Tail: ";$h:
Z ! $d$u:;$" Command to perform on file: " $f $" Tail: ";$h:
0 ! $"Enter ZFILER macro script: "

0. on-line macro        A. set Archive bit              N. NULU
1. LPUT                 B. Backup (cr/sfa)              O.
2. Z80ASM to COM        C. CRC                          P. Protect
3. Z80ASM to REL        D. Date display                 Q. QL
4. Compare Files        E. Edit                         R.
5.                      F.                              S. SFA
6.                      G.                              T. Type
7.                      H.                              U. Uncompress
8.                      I.                              V. VLU
9.                      J.                              W.
                        K. Krunch                       X. eXecute
                        L. LDIR                         Y.
                        M. Move                         Z. run command

$!  ZEX 'GO'            $D  DRIVE               $P  DU:FN.FT    $F  FN.FT
$".."  PROMPT           $U  USER                $N  FN          $T  FT
$'..'  PROMPT           $H  HOME


While writing this article, I discovered that one can include blank lines as shown to make the CMD file easier to read. The help screen part of the listing is taken from my personal script file (which, I have to confess, I have not really worked very hard at). The macro definition part of the listing includes only a few of the definitions.

The macro "Q" is included to illustrate a very simple, but useful, type of macro. It invokes the very powerful file typing program QL (quick look) on the pointed-to file. This is handy when you want more powerful viewing capability than that offered by the built-in "V" command. QL can handle crunched files and libraries, and it can display text or hex forward or backward.

Macro "U" uncompresses a file. It illustrates a more complex script that involves flow control and parameters that extract individual components of the pointed-to file name. It tests the file type to see if the middle letter is "Q" or "Z". In the former case, it unsqueezes the file; in the latter, it uncrunches it. The uncompressed file it put into the source file's directory.

Macros S, K, and B illustrate the use of input prompting. The first one allows the user to specify the file attributes to be set. Note that the prompt includes a helpful reminder of the syntax required by SFA.

Macro K crunches files to a user-specified destination. It also illustrates how one logs into the currently displayed directory. I do this here so that a null answer to the prompt (i.e., just a carriage return) will result in the crunched files being placed in the currently displayed directory rather than in the home directory, as would otherwise be the case (since that is where the macro runs from, remember). As a result, however, this macro will not operate properly in user areas above 15 under BGii or versions of the command processor that do not allow logging into high user areas.

Macro B performs a slightly more complex function. It not only compresses the pointed-to file to a specified destination directory, but it then marks the source file as having been backed up. A combination of the group archive built-in command (to tag files that need backing up) and a group macro B (to perform the backup) gives the ZFILER user a way to back up files in crunched form on the backup disk.

Macro M is included to show that a ZFILER macro, when it needs to do something more complex than it is capable of doing all by itself, can pass the task to an ARUNZ alias. The MOVE alias first determines whether the source and destination are on the same drive. In that case, MOVE.COM is used to perform the move. Otherwise, the source file is copied to the destination and then deleted. What we have, therefore, is a MOVE command that frees the user of the responsibility of worrying about which drives are involved - another example of how Z-System can free you from considerations that need not concern you, that do not require human intelligence to decide.

The final three macro examples are execution macros. Macro X causes the pointed-to file to be executed. A more sophisticated version might check to make sure that the file type is COM. I opted for the flexibility of pointing, for example, to PROGRAM.Z80 and having PROGRAM.COM run. If there is no COM file with a matching name, the error handler will take care of things. You will note the leading colon before the "$n" parameter. It makes sure that the current directory is searched even if it is not on the path. Prompted input is used to allow a command tail to be included.

The Z macro performs a user-specified function on the pointed-to file. Two separate user prompts allow both the command and a command tail to be given. For example, if you wanted to squeeze the file to A0:, you would enter "SQ" in response to the first prompt and "A0:" in response to the second.

The 0 macro illustrates how the response to a prompt can be used as a ZFILER script. This macro takes care of all those functions we forgot to include in ZFILER.CMD. The whole macro is just prompted input, and whatever we answer will be run as a script. I use this function so often that I put it on a number key so that it can be invoked with a single key rather than the usual pair. Also, as you may have noticed, I include in the macro help screen a list of the parameters that can be used.

The only real limitation of this macro-to-write-a-macro approach is that prompted input cannot be included in the response. As I write this, however, it occurs to me that this limitation could be overcome by recursively parsing the prompt parameters until none remain, and only then going on to the subsequent macro expansion steps.

Well, I was going to discuss patching and configuring ZFILER, but this article is already too long, so that will just have to wait for another time. I hope that this article will help you get more out of ZFILER. See you in the next issue!

[This article was originally published in issue 37 of The Computer Journal, P.O. Box 12, South Plainfield, NJ 07080-0012 and is reproduced with the permission of the author and the publisher. Further reproduction for non-commercial purposes is authorized. This copyright notice must be retained. (c) Copyright 1989, 1991 Socrates Press and respective authors]