R60 Z-System Corner Programming for Compatibility: Z-System and CP/M Jay Sage As noted in the sidebar to my columns, I serve as the Z­ System sysop on GEnie. My principal duty is to conduct a¨ Real Time Conference (RTC) at 10 pm Eastern time on the¨ first Wednesday of each month. These sessions are group¨ chats, and they give GEnie callers a chance to ask questions¨ and make comments. When I accepted this position with¨ GEnie, I had expected that many Z-System enthusiasts would¨ take advantage of the opportunity to discuss Z-System issues¨ with me, but it has not turned out that way. Relatively few¨ people show up for these sessions. They have not been without value, however, and one of the¨ most valuable took place this past June. David Goodenough,¨ the author of QTERM (the program for Z80 computers that is¨ currently the number-one subject of interest), was in¨ attendance as usual, and he and I got into a discussion¨ about the lack of sharing and commonality between the Z­ System community and the community of vanilla CP/M users. The Issue of Compatibility The discussion started when I mentioned that we were¨ beginning work on a replacement for BYE. David asked if it¨ would work under CP/M, and I replied that it would¨ absolutely require a Z-System. David found this very¨ annoying and complained about the way the Z community¨ ignores the large part of the 8-bit computing world that,¨ for whatever reason, sticks with standard CP/M. In the case of BYE, I do not accept David's criticism. ¨ The main motivation for writing a new version of BYE is that¨ many of the functions and a large part of the code in BYE is¨ completely unnecessary on a Z-System. For example, Z­ Systems are inherently secure and do not need BYE to provide¨ security. Furthermore, the externally accessible multiple¨ command line makes it possible for BYE to have the operating¨ system perform functions that are currently included in the¨ BYE code. A BYE for Z-Systems could be significantly smaller and¨ more flexible than a BYE for CP/M, and we would not want to¨ carry the overhead demanded by CP/M. David's response to¨ that was to question, then, why he should carry any overhead¨ for Z-System in the programs he writes (since he never uses¨ any form of Z-System). I think the difference is that BYE is a piece of resident¨ code, something that becomes part of the operating system of¨ the computer. As such, it is much more important for the¨ code to be as short as possible. In an application or¨ utility program, however, the importance of keeping code¨ short is much less, and the importance of compatibility and¨ wide-ranging applicability is much greater. With respect to¨ those kinds of programs I think that David is absolutely¨ right, and we should be making a greater effort at¨ compatibility. That is the issue I will address here. Why Should We Want Compatibility? Compatibility, like motherhood, is almost good by¨ definition! Obviously, a great deal of creative effort is¨ going into Z-System development, and it is best if the¨ fruits of that effort can be shared by the largest number of¨ people. But there are also some very specific reasons why¨ we in the Z community should be interested in compatibility¨ with CP/M. One reason that affects Z users directly is that with¨ NZCOM and Z3PLUS we are not locked into Z-System, and we are¨ much more likely to drop back to CP/M to perform some tasks. ¨ Sometimes we are going to use an application that is a real¨ memory hog. At other times we have to use a program that¨ cannot operate under Z-System, such as Uniform (for working¨ with foreign-format diskettes). Uniform works by¨ introducing patches directly into the operating system code,¨ and it will work, therefore, only when the machine is¨ running the exact version of CP/M for which it was written. ¨ (I have made perfunctory attempts at finding a way to adapt¨ Uniform to NZCOM, but so far I have not succeeded.) It¨ would be very convenient if our familiar Z-System tools¨ would still work after we dropped back to CP/M. And we¨ certainly don't want them causing a system crash if we¨ invoke them by accident under CP/M. A second advantage, and one that David Goodenough raised¨ to encourage me to pursue greater compatibility, is that¨ people still using vanilla CP/M would get a taste of what Z­ System can offer. Under CP/M, Z-System tools would require¨ an onerous patching process. If more CP/M users realized¨ the kinds of programs that would become available to them¨ directly, with no need for patching, they would be more¨ likely to upgrade to Z-System. What Does Compatibility Demand? What do we mean when we talk about Z-System programs¨ being compatible with CP/M, and what coding requirements¨ would such compatibility impose? As Bridger Mitchell pointed out long ago in his "Advanced¨ CP/M" column that used to appear in TCJ (and which we all¨ hope will reappear in the future), all programs should¨ examine the environment in which they have been called on to¨ operate. Then, they must either adapt properly to the¨ environment or terminate gracefully with an appropriate¨ message. Two basic things that programs might have to determine¨ before they attempt to perform their function are: (1) the¨ kind of CPU -- 8080/8085, Z80, 64180, Z280; and (2) the kind¨ of operating system -- CP/M-2.2, CP/M-Plus, Z-System. In¨ the case of a Z-System, it might be further necessary to¨ determine the version of ZCPR3 (3.0, 3.3, 3.4) and the type¨ of implementation (manual, NZCOM, Z3PLUS). The Z-System is a far more varied object than CP/M, and¨ one's determination of the environment has only begun when a¨ Z-System has been detected. The simplest form of Z-System,¨ I suppose, would have only one module: the ENV descriptor. ¨ The other modules and facilities in Z-System's long list¨ are, I believe, all optional: named directory register¨ (NDR), multiple command line, shell stack, flow control¨ package (FCP), message buffer, command search path, and so¨ on. Because of the range of Z-System implementations that are¨ possible, it is already incumbent on Z programs to make sure¨ that the facilities they expect or depend on are, in fact,¨ available. Most of the routines in the assembly language¨ programming libraries used to write Z programs (i.e., VLIB,¨ Z3LIB, and SYSLIB) already return error codes when the¨ facility requested is not available. All Z programs should¨ check these return codes and proceed in appropriate ways¨ when things don't work out as intended. What should be expect from Z programs when they are run¨ under CP/M? At the very least, as I mentioned earlier, Z¨ programs should absolutely be safe under CP/M. We might¨ settle for a simple return to the CP/M command prompt, but¨ ideally, programs should report that they could not run and¨ why. It is quite unnerving to invoke a program and just get¨ the command prompt back. A higher level of compatibility would be for the Z¨ program to perform those of its functions that make sense¨ under CP/M. When running under Z-System the program might¨ recognize named directories and return information in Z­ System message buffer registers; under CP/M, only DU:¨ directory references could be handled, and information that¨ would normally be recorded in the message buffer would be¨ discarded. The highest level of compatibility, which we will explore¨ in more detail later, is to have the program perform its¨ full range of operations under CP/M by including an internal¨ ENV module (including the TCAP component, which tells how to¨ operate the console terminal). In this way, the program¨ would think it was running under Z-System even when actually¨ running under CP/M. I have not had time to think through all these issues¨ fully, and my views may be refined with further thought and¨ input from others. At the moment, I can see only one¨ fundamental difference between a minimum capability Z-System¨ and a CP/M system. That is the difference in command¨ processors, and specifically the more sophisticated parsing¨ of the command line that ZCPR3 performs. The first two tokens on the command line are treated as¨ file references, and the default file control blocks (FCBs)¨ at 5CH and 6CH are filled in with information about those¨ two files. With ZCPR3, directory references of the form DU:¨ and, if named directories are implemented, DIR: are¨ recognized. Besides placing the proper drive letter into¨ the FCB, the user number for the file is placed into a¨ special location. Under CP/M, these operations will not take place, and an¨ internal ENV will not help. In fact, the internal ENV,¨ which will make the program think that it is running under¨ Z-System, could cause problems. To make programs that rely¨ on the default FCB data work, we would have to add¨ significant additional code to the program. First, we would¨ have to detect the use of the internal ENV (this would be¨ easy), and then we would have to provide alternative code¨ for manually parsing the filename tokens into the FCBs. ¨ While this might represent a significant extra burden in the¨ code, I would recommend that we do this in the future for¨ those programs that can afford the extra code and whose¨ functionality is not already available in a standard CP/M¨ program. Where Does Compatibility Stand Now? David Goodenough was under the impression Z-System¨ programmers totally ignore CP/M and that nearly all Z-System¨ programs will not work under CP/M. This impression is not¨ really fair. First of all, a great many Z programs -- perhaps even the¨ majority -- could not possibly run under CP/M because their¨ function would make no sense under CP/M. Here are a few¨ examples: (1) PATH or ZPATH: configures the Z-System search path for COM files; (2) PWD: reports the names and associated drive/users of currently defined named directories; (3) SALIAS: full-screen tool for defining stand-alone multiple-command-line aliases; (4) ZEX: a sophisticated batch processor that feeds commands to the multiple command line and uses the message buffer for control communication; (5) ADIR: displays the names of alias scripts defined for the ARUNZ extended command processor; (6) LSH: a full-screen history shell and command-line editor. It was interesting to see what happened when these¨ programs were operated under CP/M (which I could do easily¨ on my Televideo 803H, which runs NZCOM). ZPATH politely¨ reported that there was no ZCPR3 path, and PWD announced¨ that there was no NDR (named directory register) allocated. ¨ ZEX gave a more general message indicating that the¨ facilities it required for operation were not available. ¨ These responses were all acceptable and reasonable, and they¨ meet the requirements I outlined above. ADIR did not do so well. It tried to run and ended up¨ accessing a bogus drive, from which I had to recover by¨ pressing control-C. SALIAS, to my surprise, did even worse. ¨ Although one time it gave me the message "TCAP?", indicating¨ that it was checking the TCAP for adequate terminal support,¨ all the other times it crashed and locked up the system. So¨ did LSH. Obviously, these programs are not checking¨ properly that the memory referenced by the embedded ENV¨ pointer actually contains a Z-System environment (ENV)¨ module; they must just be plowing on ahead. I tried a lot of other programs of this sort, and in¨ almost all cases they failed in an acceptable way. Many¨ gave messages; others simply returned to the command prompt¨ without having done anything (at least nothing apparent, so¨ I assume they did not do any harm). Not all Z-System programs manipulate or take specific¨ advantage of Z-System facilities; There are many Z programs¨ that perform functions that make perfect sense under¨ standard CP/M. Some authors have taken great care to ensure¨ that their programs will work in both environments. Hal¨ Bower's COPY program (derived from MCOPY) that comes with¨ the ZSDOS disk-operating-system replacement is a good¨ example of this. Here are some other examples of programs¨ whose function makes some sense under CP/M: (1) FF: FindFile searches all drives and user areas for specified file names; (2) DIFF: performs a byte-by-byte comparison of two files; (3) CD: changes the logged drive/user; (4) LBREXT: extracts member files from LBR files and optionally uncompresses them; (5) LPUT: builds a new LBR file from the files named on the command line. How did these fare under CP/M? Although FF performs a¨ function that would be useful under standard CP/M, it is¨ coded to make mandatory use of Z-System features, and it¨ delivers an error message when one attempts to use it under¨ CP/M. This is a good example of a program that probably should¨ be upgraded to CP/M compatibility. Obviously, FF would not¨ recognize or report named directories, and it would have to¨ assume that all user areas are available. As for the drives¨ it should search, it would have to be configured manually,¨ and I don't see why ZCNFG, the program that is currently¨ used under Z-System to configure it, could not perform this¨ function under CP/M, too. DIFF, like FF, gives an error message when it is invoked¨ under CP/M (in fact, it requires ZCPR33 or later). DIFF¨ makes extensive use of Z-System facilities. It determines¨ the dimensions of the terminal display, stores certain¨ results of the file comparison in Z-System registers, and¨ performs advanced error-recovery operations when the program¨ encounters certain conditions. DIFF also extracts and¨ compares the date stamps for the two files. Thus, although¨ the essential function of DIFF would be useful under CP/M,¨ it might be difficult to make the code work without those Z¨ facilities. It would be worth looking into, I think. CD (Change Directory) is primarily intended to log into¨ another drive and user area using a named-directory¨ reference and to run a special alias when it gets there. ¨ Under CP/M it currently appears to do nothing but return to¨ the command prompt. It should be made to give an error¨ message. It might even be reasonable for it to accept DU:¨ syntax to log into the indicated area under CP/M. The functions of LBREXT are totally appropriate for CP/M,¨ and there is no reason why it should not work perfectly¨ under CP/M. It almost does. The following command works¨ fine: LBREXT B3:LIBRARY C1:EXTRACT.FIL As is perhaps to be expected, it does have problems if one¨ tries to use a named directory reference. The one real¨ mistake I noticed in the code is that it is coded to¨ determine its own name (for use in the help screen) by¨ looking in the Z-System external FCB. Under CP/M, garbage¨ appears. Apparently, the code does not verify that the¨ system has an external FCB. This is a mistake even for¨ operation under Z-System. LPUT, which also performs a function that is entirely¨ appropriate for CP/M, misbehaves seriously under CP/M. It¨ tries to work, but it gets its user areas confused. Since¨ it always assumes user 0 for the LBR file, even when one is¨ given explicitly in the command, my guess is that it is¨ using the default FCB for the first command line token. ¨ Second, for the list of files to be put into the library,¨ LPUT assumes user 0 unless one is given explicitly, in which¨ case it is recognized correctly. This suggests that it has¨ not determined properly what its logged-in (default) user¨ area is. Small changes in the code could probably correct¨ these problems. I probably should have clarified one thing earlier in¨ this discussion. I have not done these experiments strictly¨ under CP/M but rather under ZCPR2, and this may have¨ affected the results. A stripped-down ZCPR2 is the most¨ primitive system I will consider running. It is a direct,¨ drop-in replacement for the Digital Research command¨ processor; nothing else in the system changes. I can¨ imagine (albeit with some difficulty) why someone would¨ still today refuse to use Z-System (it does take up some¨ memory), but it is completely beyond me as to why anyone¨ would continue to use the DRI CP/M-2.2 CCP. Compatibility Via an Internal Environment I was so intrigued by the possibility of making Z¨ programs run under CP/M even when they required full-screen¨ terminal capabilities that I could not wait to experiment¨ with some real code. Mind you, this is no small thing. I¨ have been so busy with other activities that I have done¨ virtually no code writing for years! My influence on the Z¨ community has been only as a mentor. This time I just could¨ not wait for someone else to perform the experiments. As the subject for my first tests, I chose the disk¨ utility program, DU3. There were two reasons for this. ¨ First, this is a program whose CP/M counterpart lacks some¨ of the most useful features of DU3, and I have long wished¨ that it would work after I dropped down to CP/M. Second, I¨ had just put up on my Z-Node a new version, DU34, that Gorm¨ Helt-Hansen of Denmark had sent to me, so I knew I had¨ current source code to work with. There are quite a few possible approaches to patching in¨ an internal ENV. I am going to start by discussing a little¨ test program that I wrote after I had already succeeded with¨ DU34 (now DU35). Rather than trying to abstract from the DU¨ code, it was easier to write a simple demonstration program. ¨ ZTEST, shown in Listing 1, includes only enough¨ functionality to prove that it works. The key idea is to place in the code a two-record module¨ containing the ENV (1 record) and the TCAP (1 record). The¨ ENV pointer in the program header is initialized to point to¨ this internal environment. Then, when the program is¨ executed under CP/M, that is the ENV that the program will see and use. On the other hand, when the program is run¨ under a modern Z-System (ZCPR33 or later), the command¨ processor will poke the address of the true (external) Z-System ENV into that pointer, and the program will then see¨ and use the real environment. The most difficult part of this project was writing the¨ CPMENV.LIB code. The ENV part was pretty straightforward. ¨ Almost all the module addresses and lengths are zero! The¨ part that took some time was the TCAP. I finally located a¨ good source-code version of the latest TCAP standard and was¨ able to import it. A condensed version is shown in Listing¨ 2. For patching DU3, I took a slightly simpler approach that¨ would not have required modifying the source code at all. ¨ In fact, I did make a few changes to correct some errors I¨ noticed in the version 3.4 code and to make a cosmetic¨ change. For one thing, the names of some library routines¨ had mistakenly shorted to six characters, rendering the code¨ unusable with the SLR tools in SLR mode. Using the full¨ names should be equally acceptable to M80/L80. The most serious error was the inclusion of a large block¨ of initialized data near the end of the data segment (DSEG). ¨ This resulted in a COM file substantially larger than¨ necessary, since all the uninitialized data now had to be¨ loaded into the COM file. I moved that initialized data to¨ the beginning of the DSEG to join the other initialized¨ data. I called the new version DU35. Aside from those modifications, which were made for other¨ reasons, the patching process actually starts with the same¨ assembled REL file as would have been used to generate the¨ standard Z-System version of the program. When I linked it¨ with the libraries (VLIB, Z3LIB, and SYSLIB), I made note of¨ where the data segment (DSEG) started. It was at 2D48H. To¨ make room for an internal ENV from 2D50H to 2E4FH, I simply¨ relinked the program with the DSEG specified as 2E50H. Now¨ all I had to do was to patch in the ENV code. Initially I did this manually using the command GET 100 :DU35.COM to load DU35.COM into memory. Then I used a second GET¨ command to load an assembled version of the dummy ENV and¨ TCAP to the proper address: GET 2D50 :CPMENV.COM To make sure that the ENV address in this ENV module was¨ self-consistent, I poked the correct value in at ENV+1BH: POKE 2D6B 50 2D Next, I had to poke the ENV address into the pointer at¨ 109H. The command was: POKE 109 50 2D Now the memory image had the correct code, and I just had to¨ save the file: :SAVE 100-2E50 DU.COM I presented the procedure above to illustrate how handy¨ the Z-System GET, POKE, and transient SAVE.COM programs can¨ be when doing this kind of work. (The peek command, P, also¨ came in handy to let me see what I was doing.) To make the¨ process easier for other people to carry out, I then¨ developed a patch program called DU-CPM.Z80. Excerpts are¨ shown in Listing 3, where the installation procedure is¨ described. Conclusion I hope this column will inspire Z program authors to take¨ a careful look at their programs to see how they can be made¨ better behaved and more compatible with standard CP/M. I¨ hope it will also inspire CP/M programs to think about the¨ advantages that Z-System offers to many Z80 computer users¨ and to devote the effort required to allow their CP/M¨ programs to run effectively under Z-System as well. We will¨ all benefit by bringing the CP/M and Z-System communities¨ closer together.