The Computer Journal, Issue 48
Z-System Corner
© Jay Sage
Reproduced with permission of author and publisher.
Patching MEX-Plus and The Word, Using ZEX
For this issue I have a large number of small subjects to cover. I will begin with the long-promised patches to MEX- Plus, the ones I lost when my hard disk crashed and then recovered when the hard disk miraculously resurrected itself. I haven't had time to incorporate all the corrections and additions that have been on my list, but this will be a good start on an on-going project.
While on the subject of patching, I will enlarge on the discussion of the patch to The Word Plus that I presented in issue #45. It turns out that at least two different versions of TW.COM are currently in circulation, and the original patch works only with one.
A couple of readers have requested help with ZEX. They are aware that Bridger Mitchell provided full documentation in his column in issue #38, but they pointed out that the subject is rather complex and would be clarified considerably by a few examples. Given the power of command alias scripts, I rarely have any need for ZEX, but I will present the two ZEX scripts that I do use regularly. Perhaps some readers will help out by sending me copies of some interesting scripts that they have developed that use the advanced features of ZEX5.
Finally, I will continue my discussion of ZMATE. Rather than listing the remaining commands that I did not cover last time, I will describe my special autoexec macro that allows me to pass macro commands to ZMATE from the operating system command line. This provides a very powerful interface between ZMATE and Z-System command alias scripts.
This is not the only information about ZMATE in this issue. I am happy to announce that we are inaugurating a new column by Clif Kinne on MATE macros.
One other quick announcement. The Z-System Software Update Service, produced by Bill Tishey and Chris McEwen, is expanding its services. ZSUS will now make up custom disks containing the full LBR releases of any files you request. There will also soon be special software collections. I believe the first will contain material of interest to assembly language programmers. I invite you to write to me for a new flyer or to look for announcements on BBSs.
Fixing Up MEX-Plus
As wonderful as I find MEX-Plus, there are quite a few bugs that need to be fixed and quite a few additional features that I would like to see added. I have just gotten started on this project and will describe my progress to date.
MEX Improvement List
Besides the items that my current patch deals with, the following items are on my list of needed improvements.
The CLONE command, which creates a new copy of MEX (i.e., a new COM file) with the current settings as the default, has the following minor bug: when it is entered without specifying a file name, it will make a file with extent "COM" and a file name of all spaces! In this case, it would be much more sensible if it either (1) defaulted to a name of MEX.COM or (2) refused to do anything.
There is a very annoying error in the way numerical variables are processed. When the variables have particular values, MEX reports a syntax error. So far I have not been able to determine the precise conditions under which the problem arises, but I hope that with careful study of the code I will figure out what is wrong. My guess is that some CPU flag is being mis-set or misinterpreted.
There is a minor bug in the DATE command. As things are now, TIME and DATE are identical functions, and both return the time in the VALUE variable. Obviously, DATE should return the current date.
In case you have tried to use the MENU.MOD module and have found that the HELP function does not work, don't worry. You are not doing anything wrong or missing any piece of the code. This function was implemented improperly or not implemented at all in the CP/M version of MEX-Plus. We probably will not be able to do much about this until we have figured out MEX's interface to the external modules.
I would like to mention something that caused me grief recently with one of my macros: WAIT STRING strings are limited to 16 characters! I don't think I would change this, but one must bear this limitation in mind.
One of the most important additions would be more string variables beyond the six (A..F) currently implemented. Even two more would benefit some of my scripts enormously.
Finally, I would like to replace the file name parser, which already recognizes the DU: prefix, with one that handles full Z-System file specifications.
My MEX Patch
My MEX patch is shown in Listing 1. It is heavily commented, and I will not repeat the details in the text. The patch makes three changes.
MEX has a STAT command that turns on or off a function that converts the backspace key into a rubout/delete character (which we will call DEL from now on). I have always assumed that this was included from olden times, when microcomputers sometimes had no DEL key and mainframe computers - which MEX was used to communicate with - required that key for some critical functions.
Today, the situation is generally quite different. First, almost all computers and terminals now have a DEL key. Second, with the Z-System, one often uses DEL interchangeably with backspace, and I have gotten quite used to that. When I call non-Z remote access systems, I find it a real nuisance when DEL does not work. That is why I decided to reverse the function of this STAT command.
Making the change itself was quite simple; the hard part was finding the right places in the code. Three changes are required, as shown at the end of Listing 1. One byte contains the keycode of the key to be intercepted, while another byte contains the keycode with which it is to be replaced. The third change is not needed for proper operation, but it is nice to have the STAT RUB message reflect the new function.
The next change is to the STAT BUF command. MEX very often has its commands return in the VALUE variable some information developed by that function. For example, the DIR function returns the number of matching files that were found. Unfortunately, the STAT BUF command displays information about the status of the capture and key macro buffers but does not return any information in VALUE.
I had written a script for calling up GEnie and capturing my email letters. When I relied on XON/XOFF flow control to pause GEnie's output while MEX was flushing its buffer, I found that some text was sometimes lost. To get around this, I manually closed and reopened the capture buffer. Rather than wasting the time to do this after each letter, I wanted to check the amount of space remaining in the capture buffer. If this space dropped below some value (I think I used 3K), then I would close and reopen the buffer.
Implementing this function required adding some new instructions to the code for the STAT BUF command. This raised two problems: how to tap into the original code and where to put the new instructions.
Let us start with the second problem. With some programs, new code can be added at the end of the original code. If the program places its run-time data at the end of its code, however, this can obviously present some difficulties. If one is patching in some new initialization code that is performed and can be discarded before the program starts to use its data space, then one can get away with this. When I add this kind of patch, I try to have it include code that patches out the patch and restores the original code. Otherwise, the program cannot safely be rerun using the GO command, since the patch code may have been overwritten by program data. Attempting to execute data has a way of wreaking havoc with the system!
In our present case, we are adding code that must be available at any time, and MEX certainly writes data into the free memory after its own code. Consequently, we have to find some space within the bounds of the original program. The technique I have used here is to steal nonessential internal data space.
MEX normally signs on with a very elaborate screen display that takes 343 bytes - enough room for a lot of patch code! We cannot delete all of it, but we can replace it with a much simpler signon message. The new one in the patch uses only 31 bytes (if I counted correctly). It lets us know that we are running the special version of MEX and tells us the date of the patch.
To tap into the original code, we choose a convenient point and put in a jump instruction to the patch area. The instruction that we replaced by that jump is the first instruction in the patch area. Then we add the additional code to save the capture buffer free space value into the VALUE variable. Finally, we return to the original code just past the jump we inserted.
The third change implemented in the patch concerns the BELL command. This command is convenient to alert the user to some event. It would be nice to have the bell ring repeatedly until the user signals that he is ready to go on. Unfortunately, the command "BELL 100" will insist on ringing the bell one hundred times. "BELL 0" will insist on ringing it 65,000 times! The only way one can interrupt the bell is to press control-c, but this aborts the entire script. The patch allows any keypress to abort the BELL command without aborting the entire script.
This patch works using a slightly different technique. The BELL code already contains a call to a routine that checks for control-c. We just replace it with a call to a new routine in the patch area that checks for any keypress. After this call, the original code performed a conditional jump to exit from the routine, but for reasons I don't remember, this path was never taken. For our new routine, the conditional has to be switched from a zero check to a nonzero check.
Patching TW.COM
Since we are already on the subject of patching, this seems like a good place for a follow-up discussion of my patch in issue #45 to make TW.COM, the master program of The Word Plus spell checking program, not stop and ask the user if the current configuration is acceptable. When we are running from an alias script, we just want to get on with the job.
Several people tried implementing my patch script and complained that it did not work properly. This was strange, because I have been using it constantly. Hal Vogel finally figured out what was going wrong. It seems that there are two versions of TW in current distribution. Mine, which came with WordStar Release 4, is version 1.21. Other people have a later version, 1.22. Both display a 1981 copyright, and it seems odd that the new version of WordStar would come with an older version of TW. It makes me wonder which version is better.
In any case, version 1.22 has three bytes more code before my second patch point, and so both the address of the poke and the address poked in have to be incremented by three. Here is the original TWPAT alias for version 1.21:
get 100 tw:tw.com
poke 103 c3 3b 01
poke 395 c3 2a 04
go $*
For version 1.22, only the second poke has to be changed:
poke 398 c3 2d 04.
In my original discussion, I did not describe how I figured out how to patch around the prompt. Hal Vogel asked about my method, so I will say a little about it here. It's the least I can do to thank him for solving the mystery.
I knew that the offending prompt began with "These are the current settings. So, I searched with a debugger for that text, and I found it starting at address 07FFH. Since it was part of a collection of such messages, all ending with a dollar sign, I knew that they were not part of in-line calls. Therefore, where the message was actually displayed there would almost certainly be an instruction loading the address of the message into one of the double registers.
I used the DSD debugger's search function to locate occurrences of the byte pair FF,07. The first one was part of the code sequence
041c ld de,07ff
041f call 17bd
0422 call 17cb
0425 cp ' '
0427 jp z,02a4
042a
It was pretty clear that 17bd was the routine for displaying a string whose address was in register pair DE and that 17cb was the routine to get an input character from the user. TW takes a space character to indicate that the user wants to make a change, which must be handled by the code at 02a4. Any other character would accept the default setup and continue at address 042a. Therefore, changing the code at 0423 to
0423 jp 042a
would by-pass the user's response and go right into the spell checking.
However, that change would still send the display of the prompt to the screen. Backing up and putting my replacement jump instruction at address 041d would suppress even the display of the prompt.
As I looked at still earlier code, I found instructions that displayed the current settings. Since we really did not need to have them sent to the screen, I continued to work back until I came to the beginning of that block of code at address 0395, where the message "Summary, Checking file..." was put up. By placing my jump instruction there, all of this extraneous code was by-passed.
You might be wondering how I found the place where the other patch was required. With the main patch installed, TW terminated with an error message about a corrupted file. Guessing that a check of that type would be near the beginning of the code, I just executed TW.COM under the debugger. The problem was clear very quickly.
Some Sample ZEX Scripts
As I mentioned earlier, Bridger Mitchell, author of ZEX version 5.0, covered the specifications for the program in his column in issue #38 of TCJ. Having just reread that column, I have to agree with the comments I received that a few examples are needed to appreciate what ZEX can do. I will assume that the reader is already familiar with the basic operation of ZEX and present the only two ZEX scripts that I use regularly.
One of the advantages I got from writing about these scripts is that, naturally, in the course of analyzing them I thought of new approaches and have significantly rewritten them. They do seem to work, but you should be warned that mistakes may have crept in.
If all one wants to do is run a sequence of command lines, I don't think that ZEX is the right approach (though there may be some exceptions that I can't think of now). On the other hand, ZEX is appropriate - in fact, necessary - when one wants not only to invoke a program but also to provide interactive input to the program. ZEX is also useful for creating fancy screen displays, since it has the ability to generate direct output to the console and to suppress output generated by running programs. My two sample scripts fall primarily in the former class.
My first script, MAP.ZEX, is shown in Listing 2. As you see, it is extremely simple. It is used to invoke the MAP utility that is part of the XBIOS extended BIOS for the SB180 computer. This utility can temporarily set each floppy drive to emulate one of a number of foreign formats.
I wanted to have simple alias commands, such as "KP4" or "MD3", that set the appropriate floppy to the Kaypro DSDD or Morrow MD3 format. Unfortunately, MAP is a purely interactive program; choices cannot be passed to it from the command line, and so ARUNZ is powerless. This is where ZEX comes in.
The first section of MAP.ZEX is an extended comment. When a '{' character appears in the first column of a line, all text up to the first closing brace character '}' is treated as a comment and ignored by ZEX. I strongly recommend using comments extensively, as with any other programming language.
The first non-blank, non-comment line invokes the MAP program, which I keep in directory B0:. The next line is the one that does the special job that only ZEX can do. Lines that begin with a '<' character in the first column are interpreted as program input by ZEX, which proceeds to emulate your typing at the keyboard. In this case, the first character emulated is 'G'. The first input that MAP expects is the letter of the drive to configure. Since all the foreign formats I use are for 48-tpi drives, I always use my G drive.
Next, MAP wants one to select a format by number. This we pass to the ZEX script as a parameter on the ZEX command line, and we represent it in the script with the symbol "$1". MAP then wants a carriage return. ZEX is supposed to ignore carriage returns in the script, and so we should include the special directive "|CR|". However, for reasons unknown to me, trial-and-error shows that this is not necessary here; a carriage return is sent automatically whether we want one or not.
MAP.ZEX is an incredibly simple script, but it provides a lot of power. I can now define the following ARUNZ aliases:
CLEAR=CLR zex b0:map 1;msg
KP4=KP10 /xxmap 8 KP4
MD3 /xxmap 9 MD3
TV,803 /xxmap 28 TV803
XXMAP zex b0:map $1;msg *** Drive G = $2 ***
MSG echo ^[f$*
These scripts implement the nicety of putting a message in the upper right special message area on my Wyse 50 terminal to remind me of the foreign format setting. It is not reasonable to try to remember the special escape sequence for programming this message area, so I provide the alias MSG to do this.
A second subroutine alias, XXMAP, saves space in the ALIAS.CMD file by handling commands common to a number of other aliases. It takes two parameters. The first is the format number for MAP, and it is passed to the MAP ZEX script. The second is the name of the format, and it is passed to the MSG alias. The other aliases, such as MD3, set up the desired disk format. The alias CLR or CLEAR restores drive G to its default SB180 format (MAP selection 1).
The second example script is called FORMAT.ZEX is shown in Listing 3. It works with another XBIOS utility program, FVCD (Format Verify Copy Duplicate). This program can format diskettes not only in the standard SB180 formats but also in several foreign formats. FVCD, like MAP, is a purely interactive program, but I want to be able to format, say, a Kaypro 2 disk by entering just "FORMAT KP2". Again, ZEX saves the day.
This script is much more complicated. Since I want all the formats to be handled by a common alias called FORMAT, the particular format will be designated by a command-line argument and not by the name of the command. This requires testing of the argument, a job that can be performed nicely by a sequence of flow-control commands. That much could be handled by a command alias, but since we will be providing interactive input anyway, we might as well have ZEX take care of everything.
As with MAP.ZEX, we begin the script with a comment block. Note that a double semicolon can be used to enter individual comments on a line. The first two executable lines test the first argument, "$1", to see if it is either "F" or "F:". The colon prefix before the parameter is included to force the argument to be treated as a string and not as a directory specification even if it contains a colon. For reasons I cannot entirely remember, the flow control string-equate testing command does not work properly if it begins with just ":". For that reason, I put the definite string first and the string with the parameter second.
Now we come to a very important ZEX directive: |if true|. This directive tells ZEX to ignore all characters up to the closing |endif| if the current flow state is false. You might wonder why one would need this, since the command processor would ignore commands anyway. The answer is that the script contains material other than commands. It contains interactive input and direct console output. The |if true| and |endif| directives make sure that these are ignored as well.
After ZEX processes a command, such as the line "b0:fvcd", and sends it to the command processor, it looks at the next line. If it begins with a '<' character in the first column, then all the characters on that line up to but not including the ending carriage return are loaded into a keystroke buffer and fed, as requested, as interactive input to the running program. In this example, we send "OSX TFF2", just as if we pressed those seven keys manually.
We also send a carriage return, specified by the |cr| directive. As noted with the MAP.ZEX script, ZEX automatically sends one carriage return, but we need two for this operation. We won't bother to explain what all those characters do in FVCD. To figure out what to include, one just runs the program manually and makes note of every key pressed.
This simulated input starts the first diskette formatting. When that operation is complete, FVCD wants more user input. Since the next ZEX line does not begin with a '<', ZEX no longer supplies the input and returns control to the user. More diskettes can be formatted manually.
When one eventually exits from FVCD, the ZEX script continues with the line "|say|". This turns on direct console output. Text, this time including carriage returns, line feeds, and almost all other characters in the file, is sent directly to the screen, until the terminating |end say| directive is encountered. The directive |"|, seen later in the script, is a more compact alternative to |say| and |end say|. It automatically toggles the console output state.
We could have used the ECHO command to display this message, but ZEX's direct screen output is easier and more convenient, as it is case sensitive. You just write things as you want them to look on the screen.
Once we are finished with the formatting, we can terminate the ZEX script immediately. This is done with the |abort| directive. It automatically returns the flow state to its original condition, so we do not have to worry about the fact the we have not executed the "FI" command that balances the "IF" command earlier.
The rest of the script just proceeds to test for other arguments and to pass the appropriate input to FVCD. The only one of these other cases worth commenting on is the manual formatting case near the end of the script. Here we simulate input only to put FVCD in format mode with automatic DateStamper initialization enabled but not to select any format or to start the operation. We must suppress the automatic carriage return that ZEX generates (otherwise formatting will start immediately in some default format). The |until| directive stops simulated input until a carriage return is entered.
My ZMATE Autoexec Macro
The two ZEX scripts were needed primarily because the two programs MAP.COM and FVCD.COM did not provide a facility for designating options on the command line. Ampro always made a particular point of providing such a facility with its utility programs, and I wish all authors would do that. It is nice to have interactive programs, but it should be possible to run them "batch mode" too.
With a word processor or editor one expects the command line tail to include the names of any files to work with. Indeed, this is ZMATE's default action. However, there are many situations in which one would like to do more. For example, one might want to start ZMATE with a macro already loaded into one of the auxiliary buffers. One might even want that macro to be executed immediately. That macro might even carry things to the point of closing the file and exiting from ZMATE! In that way one could use ZMATE to create custom text processors.
How is this done? In an earlier column I described the permanent macro area (PMA) in the ZMATE text editor. This buffer area in the code contains a set of macros that become a permanent part of ZMATE and are available at all times. The first macro in the PMA can be designated as an autoexec macro by placing a control-S in front of it instead of the usual control-X. As soon as ZMATE has loaded and initialized itself - and before it opens any files - it executes this autoexec macro.
Here is a very simple example of a text filter created using this ZMATE facility. Suppose we have a macro that opens a file, goes through it changing each of its characters to upper case, saves the file, and exits. If we put this macro into the PMA as the autoexec macro and then clone ZMATE with the command "XDupcase$", we will have a file called UPCASE.COM. Then we can enter a command like
upcase infile outfile
to perform the case conversion on a file. This is just a trivial example. With the full facilities of ZMATE macros, one can easily write some very powerful filters.
What I want to describe now is the autoexec macro that I have installed in my standard version of ZMATE (named EDIT.COM). We might call it an indirect macro, since it allows one to specify a macro for automatic execution on the command line. Some sample commands will illustrate how this works.
edit source.z80 $ b9e xicomment.mat$ bte
The part of the command up to a first dollar sign is taken as the file specification. In this case, the assembler source file SOURCE.Z80 will be opened. In addition, the macro following the dollar sign will be executed. This macro goes to edit buffer 9, reads in the file COMMENT.MAT containing a macro for reformatting assembly code comments, and then returns to the main editing buffer. The following ARUNZ alias can automate this (remember that one needs double dollar signs to represent a single '$' character in the expanded script):
ZED,IT edit $1.z80 $$ b9e xicomment.mat$$ bte
Now when I want to work on a Z80 assembler program file called PROG.Z80, I can enter the simple command
zed prog
Here is a sample command line for editing my ALIAS.CMD file with automatic searching for the alias definition we just showed.
aled zed
The definition for ALED is
ALED if nu $1;edit ram:alias.cmd;else;edit ram:alias.cmd $$ e s^m$1$$;fi
If no search string is given, the alias just edits the file. If a string argument is given, then ZMATE is passed a macro to search for the string at the beginning of a line. The caret followed by an 'm' is interpreted as a control-M or carriage return.
A fully commented version of the autoexec alias is given in Listing 4. Since I tend to use buffers from both ends, keeping text in the low-number buffers and macros in the high-numbered buffers, for the autoexec macro I use a middle buffer, number 5. The macro switches to it and then inserts the string argument passed on the ZMATE command line. ZMATE has the special string symbol control-A-colon for this. [In the listing, control characters are indicated by leading carets, and escape characters are shown as dollar signs. However, some of the characters really are dollar signs, and you will have to determine which are which by the context.]
The next block of code in the macro removes any leading space. If there is then nothing left, the main text buffer is reselected, and the macro is aborted.
The next task is to split any arguments passed into those that specify files and those that comprise a macro to execute. The code begins by checking whether the first character is a dollar sign and saving this information on the stack. If the first character is '$', then no edit file was specified, and the rest of the line is a macro. Otherwise, we try searching for a dollar sign. If one is found, everything before it - which specifies the files to open - is moved to buffer 0. If none is found, we will be at the end of the buffer, and the entire text will be treated as the file specification.
The next block of code performs some special interpretation on any macro command that remains. Since the CP/M command line imposes some limitations on the characters that can be passed, we provide means for indicating those that cannot be put into the command line. When a double quote character is found, it is deleted, and the next character is allowed to pass without interpretation. Otherwise, the following special conversions take place: (1) all alphabetic characters are converted from upper case to lower case; (2) dollar signs are converted to escape characters; (3) a caret is deleted and the character after it is converted into the corresponding control character.
Once the macro in buffer 5 has been fully interpreted, we switch back to the main editing buffer. If the Boolean value on the stack is true, then we open the files (using the "XF" macro) specified by the string in buffer 0. I don't think we talked about this before - at least not in detail - but this is an example of the way ZMATE can use the contents of buffers as string arguments for other macro commands. This greatly enhances ZMATE's power.
The macro completes its work with three more steps. Buffer 0 is cleared out. Then the macro in buffer 5 is executed. Finally, buffer 5 is cleared out. That's all for the macro, and that's all for this column! See you again in the next issue.
LISTING 1
; Program: MEXPAT.Z80
; Author: Jay Sage
; Created: June 10, 1990
; Last Modified: October 8, 1990
; This file contains a collection of patches made to Mex-
; Plus, version 1.65.
; The following patches are included:
;
; 1. Change the function of the backspace-to-rubout
; conversion function to do the reverse -- make
; pressing the rubout/delete key generate a backspace
; character. This patch could be used to perform the
; conversion of any single character into any other
; single character. This function is invoked by the
; STAT RUB [ON|OFF] command.
;
; 2. Augment the STAT BUFFER command so that the amount
; of free space in the capture buffer is returned in
; the VALUE variable. MEX scripts can then check on
; the remaining space and decide to flush the buffer
; manually at convenient times and to avoid overflow
; problems that can occur when relying on XON/XOFF
; control.
;
; 3. Augment the BELL command so that pressing any key
; will terminate it. The standard MEX BELL command
; can be cancelled only by control-C, but this
; terminates the entire script. Now the bell can
; sound until the operator presses a non-abort key, at
; which point the script will proceed.
; Addresses in standard code
ilprint equ 0724fh ; In-line print
hlsubde2 equ 0417ah ; Subtraction of DE from HL
capstats equ 06eafh ; CAPSTATS function
scrollconin equ 049d3h ; Console input
getconstat equ 049c5h ; Console status
bell2 equ 046c2h ; BELL2 entry point
val equ 00d64h ; VALUE variable
logo equ 02662h ; LOGO entry point
endlogo equ 027b9h ; First byte past LOGO code
saycap equ 06ea6h ; Point to patch in
; ...SAYCAPSTATS
bellins equ 046aeh + 10 ; Point to patch in BELL
oldkey equ 5351h ; Place where key typed by
; ...user is detected
newkey equ 535dh ; Place where new character
; ...is substituted
idstr equ 62a6h ; Place where STAT command
; ...message is defined
bs equ 8 ; Backspace character
lf equ 10 ; Linefeed
cr equ 13 ; Carriage return
rubout equ 127 ; Rubout character
; ---------------------------------------------------------
; Standard MEX-Plus has a very elaborate signon screen that
; affords a perfect place to put the code we need for our
; patches. The first thing we do is to replace the LOGO
; routine with a minimal message.
org logo
call ilprint
db cr,lf
db 'MEX v 1.65Z, 06/10/90'
db cr,lf,lf,0
ret
; Make the STAT BUFFER command put the amount of free
; capture buffer space into the VALUE variable. We do this
; by patching some extra code into the SAYCAPSTATS routine.
cappat:
call capstats ; Get status info on capture
; ...buffer
push hl
ld h,b ; Free space (BC) into HL
ld l,c
ld (val),hl ; Put it into VALUE
pop hl
jp capcont
; This is a new routine that checks to see if ANY key has
; been pressed, not just control-c. It will be used in the
; patched BELL routine.
chkchar:
call getconstat ; See if key pressed
ret z ; Return now if not
flush:
call scrollconin ; Read in the character
or a ; Set flag to nonzero
ret
; ---------------------------------------------------------
; Code for cutting into the original code in CAPSTATS
org saycap
jp cappat
capcont:
; ---------------------------------------------------------
; Code for patching the original code in BELL
org bellins
call chkchar ; See if key pressed
jp nz,bell2 ; If so, cancel BELL command
; ---------------------------------------------------------
; Patch to backspace-to-rubout conversion function
org oldkey
db rubout
org newkey
db bs
org idstr
; 'bs-to-rub conversn' ; String in standard MEX
db 'rub-to-bs conversn' ; You must fill same space
end
LISTING 2
{
MAP.ZEX
This ZEX5 script runs the XBIOS MAP utility that sets
a drive to a designated virtual disk format. Since
MAP itself does not allow command-line parameters, ZEX
is used to pass a parameter. We use foreign formats
only on the 48-tpi drive G: and hence always supply the
input "G". The argument to the script is the number of
the desired format.
}
b0:map
<G$1
LISTING 3
{
FORMAT.ZEX
This ZEX5 script invokes the XBIOS format program FVCD
and feeds it input to perform the desired diskette
formatting operation. The following arguments are
recognized:
<none> manual operation of FVCD
F or F: SB180 DSQD format in drive F
G or G: SB180 DSDD format in drive G
KP2 or KP2: Kaypro SSDD format in drive G
}
;; SB180 DSQD Formatting
if eq :f: :$1 ;; Test for "F:"
or eq :f :$1 ;; Test for "F"
|if true|
b0:fvcd
<OSXTFF2|cr|
|say|
Formatting in SB180 DSQD format complete.
|end say|
|abort|
|endif|
fi
;; SB180 DSDD Formatting
if eq :g: :$1 ;; Test for "G:"
or eq :g :$1 ;; Test for "G"
|if true|
b0:fvcd
<OSXTGF
|"|
Formatting in SB180 DSDD format complete.
|"|
|abort|
|endif|
fi
;; Kaypro SSDD Formatting
if eq :kp2: :$1 ;; Test for "KP2:"
or eq :kp2 :$1 ;; Test for "KP2"
|if true|
b0:fvcd
<OSXTGF6|cr|
|"|
Formatting in Kaypro 2 (SSDD) format complete.
|"|
|abort|
|endif|
fi
;; Manual Formatting
if eq :$1 : ;; Test for no argument
|if true|
b0:fvcd
<OSXTGF|until|
|"|
Formatting in interactive mode complete.
|"|
|abort|
|endif|
fi
;; We get here if an illegal argument was given
|"|
--> Illegal format specification given: $1 $2 $3
|"|
LISTING 4
;Autoexec Macro
; This macro allows ZMATE to be invoked with a macro on the
; command line. The syntax is as follows:
;
; ZMATE [infile [outfile]][$<macro>]
;
; The macro line is processed using a special syntax. All
; letters are normally converted to lower case; characters
; following a '^' are converted to the corresponding control
; character and dollar signs are converted to escape
; characters. However, a character following a '"' is taken
; literally so that upper case characters, dollar signs, and
; carets can be entered.
B5E ; go to buffer 5
I^A: ; read in command tail
A ; go to beginning
T ; tag it
[@T>"
M] ; move to first non-space, non-
; ...control character
#D ; delete the white space
@T=0{ ; IF nothing left
BTE ; go to T buffer
% ; terminate this macro
} ; ENDIF
@T="${ ; IF line starts with dollar sign
D ; delete it
0, ; push 0 (false ) onto stack
}{ ; ELSE
-1, ; push -1 (true) onto the stack
E ; suppress error trapping
S$$ ; search for a '$' separator
@E{ ; IF none found
Z ; go to end of buffer
}{ ; ELSE
-D ; delete the '$' separator
} ; ENDIF
#B0M ; move file name specs to buffer 0
} ; ENDIF
[ ; REPEAT
@T<"A!(@T>"Z)'{ ; IF upper case letter
@T!96R ; replace with lower case
^ ; loop back
} ; ENDIF
@T=""{ ; IF character is double quote
D ; delete it
M ; move past next character
^ ; loop back
} ; ENDIF
@T="^{ ; IF character is caret
D ; delete it
@T&31R ; convert next char to control
^ ; loop back
} ; ENDIF
@T="${ ; IF chara
cter is dollar sign
"$R ; replace it by escape char
^ ; loop back
} ; ENDIF
M ; (otherwise) move past the char
@T=0] ; UNTIL end of text
BTE ; go to T buffer
@S{ ; IF stack was true
XF^A@0$ ; open the file spec in buffer 0
} ; ENDIF
B0K ; clear buffer 0
.5 ; execute macro in buffer 5
B5K ; clear buffer 5
END OF LISTINGS
[This article was originally published in issue 48 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 1990, 1991 Socrates Press and respective authors]