The Computer Journal, Issue 39

Z-System Corner

© Jay Sage

Reproduced with permission of author and publisher.

For this issue I will discuss some unique new capabilities made possible by NZCOM that have already proved their value and that I hope will be exploited to a much greater extent in the future. I originally had several other issues on my agenda, but Lee A. Hart sent me such an interesting article that I wanted to leave plenty of room for it.

System Enhancements Using NZCOM

I'm afraid that many people think of NZCOM as just a way for unskilled users to get Z-System running on their computers. It's true that NZCOM accomplishes that, but, as I have asserted often before, NZCOM does much more than that. It offers possibilities that a manually installed Z-System cannot achieve. I would like to describe one of them here and will do so first in the context of a problem that arose with the new ZSDOS and ZDDOS disk operating systems on some computers.

Because CP/M was created originally for the 8080 microprocessor, a number of BIOS implementors (the BIOS, or Basic Input/Output System, is the hardware-dependent part of CP/M) felt that they could make free use of the additional registers introduced with the Z80 chip. These registers included two index registers, called IX and IY, and a duplicate set of the standard registers denoted with primes on the names (e.g., B' or HL').

This was perhaps excusable at the time, but it is poor programming practice for an operating system to change anything other than what is explicitly indicated in the specifications. Most BIOS writers who have used the Zilog registers have been careful to restore the original values before exit from the BIOS routines. Unfortunately, a few BIOSes fail to do that. Among them are the following: Epson QX10, Zorba, Televideo 803 and TPC-1, Oneac On, and the Osborne Executive. The Bondwell is on the suspect list, and there are probably others we don't know about yet.

The (mis)use of these registers poses no problem for programs written to run on the 8080, but today we are rapidly moving beyond the limitations of the 8080 and making extensive use of the Z80 registers to pack more power into operating system components and application programs. Today, the Z-System, true to its name, is intended to run only on the Z80 or upwardly compatible processors like the HD64180, Z180, or Z280.

Several users who purchased ZDOS (that is ZSDOS and ZDDOS) found that it would not work properly on their computers. An investigation turned up the fact that the BIOSes in those computers were modifying the index registers. This also explained why those same users had been experiencing strange problems with JetLDR, Bridger Mitchell's superb Z-System module loader. It also explained some mysterious problems I was having with a number of programs (for example, EDITNDR) on my Televideo 803! The question was what to do about the problem.

In the ancient days, when computers always came with the source to their BIOS and their owners were always intimately familiar with the procedures for rebuilding their operating systems, the solution would have been to rewrite the BIOS with the proper PUSH IX and POP IX instructions to preserve the index register values. But what could we do today for nonprogrammers and those without BIOS source code? NZCOM provided the answer quite nicely!

As I explained in a column long ago, NZCOM works by creating what I call a virtual BIOS (I'll call it VBIOS) lower in memory in order to open up space for the Z-System modules between it and the real BIOS (often called the custom BIOS or CBIOS). The source for this virtual BIOS is available. Except for the warmboot code and some minor complications for IOP (input/output processor) support, the standard NZCOM VBIOS module just vectors calls that come to it up to the real BIOS.

But no one says this is all it is allowed to do. ZDOS authors Cam Cotrill and Hal Bower found it quite easy to surround the vectors with code to save and restore registers. First they released ZSNZBI11.LBR, which contained the source and ZRL file (loadable by NZCOM) for a VBIOS that preserved the IX and IY registers for all disk function calls. Later they discovered that some of the machines changed the index registers even for console I/O function calls, and others changed the alternate registers. They then wrote ZSNZBI12.LBR, whose VBIOS preserves all the registers for all BIOS functions.

Instead of having the virtual BIOS routines jump directly to the real BIOS, they jump to an intermediate entry point. For example, the list-status vector in the jump table has a JP ILSTST (intermediate list status), and the code at ILSTST is

ILSTST: LD      A,45
JR DOBIOS

The offset for the BIOS function is placed in the A register and then control is transferred to a general BIOS-calling routine shown in Table 1 that implements the register protection. The routine JPHL referenced there contains only the code line JP (HL), which vectors the CPU off to the BIOS with a return to the DOBIOS code.

DOBIOS: LD      HL,CBIOS     ; Start with address of real BIOS 
ADD A,L ; Add offset in A (never a
LD L,A ; ..carry since on page boundary)
EXX ; Swap to alternate registers
LD (HLP),HL ; Save them all in memory
LD (DEP),DE
LD (BCP),BC

LD (IXREG),IX ; Save index registers, too
LD (IYREG),IY
EXX ; Back to regular registers
EX AF,AF' ; Swap to alternate PSW
PUSH AF ; Save it on stack
EX AF,AF' ; Back to original PSW
CALL JPHL ; Actually call the BIOS!
EXX ; Restore alternate and index
LD HL,(HLP) ; ..registers
LD DE,(DEP)
LD BC,(BCP)
LD IX,(IXREG)
EX AF,AF' ; Alternate PSW, too
POP AF
EX AF,AF'
RET

; Register save area

BCP: DEFS 2 ; BC'
DEP: DEFS 2 ; DE'
HLP: DEFS 2 ; HL'
IXREG: DEFS 2 ; IX
IYREG: DEFS 2 ; IY

END

Table 1.
Code from ZSNZBI12.Z80 that preserves all index and alternate registers across BIOS calls in NZCOM.

To use this replacement VBIOS, you have to run MKZCM and create an NZCOM system with 4 records allocated for the BIOS instead of the standard 2. Because the BIOS must start on a page rather than just a record boundary, MKZCM will sometimes make automatic adjustments to the BIOS size. Therefore, you should specify changes in MKZCM starting with the higher-numbered modules; the adjustment in the BIOS allocation should be made last. If an attempt to enter a value of 4 results in MKZCM using 5, then you could go back (if you don't like wasting memory) and make one of the other modules (such as the NDR) one record larger and then respecify a 4-record BIOS.

There are many other ways that NZCOM can be used to introduce system enhancements without having to make changes in the real BIOS. As an example, we will show how to add support for the drive vector in environment descriptors of type 80H and above (implemented with NZCOM and Z3PLUS). The drive vector is a 16-bit value stored as a word beginning at offset 34H in the environment descriptor. It specifies which disk drives are actually implemented on a system. The lowest order bit in the word is for drive A and the highest for drive P. A zero in a bit position indicates that the corresponding drive is not available. For example, on my SB180 the bytes at addresses ENV+34H and ENV+35H were 7FH and 00H, respectively. Thus the word value is 007FH or 0000,0000,0111,1111 binary, indicating that I had drives A, B, C, D, E, F, and G. When the hard disk E partition developed a problem that made it unusable, I changed the 7FH value to 6FH, thereby disabling drive E. You can display the drive vector using menu selection 3 in the SHOW program (ZSHOW for Z3PLUS).

The ZCPR34 command processor knows about the drive vector and will not allow command references to unsupported drives. But what about programs that you run? Unfortunately, the BIOS generally knows only which drives are potentially implemented, and it may try to access a nonexistent drive. When 'smart' BIOSes encounter this problem, they often prompt the user as to what to do next. This is fine if you are sitting at the console and can take appropriate action to recover, but if the system is being run remotely, there is generally no way for the remote user to recover. In fact, because the 'smart' BIOS uses direct hardware calls to display the "Abort, Retry, Ignore?" message rather than calls through the BIOS vector table, the remote user does not even see the message and just thinks the system has crashed. My Z-Node got hung once that way when I forgot to put a diskette back into a floppy drive. A caller attempted to access it, and when I got home, the BIOS was dutifully beeping at me and waiting for me to tell it what to do.

My first stab at writing a VBIOS that observes the drive vector restrictions is shown in Table 2. Now that I have read Lee Hart's column, I am sure that this code can be made shorter, faster, or both! But I will leave that as an exercise for the reader. (I already see one place where I could save a byte.)

The listing assumes you are starting with the ZSNZBIO described earlier. Just add the extra equate for DRVEC under the /_ENV_/ common block and replace the simple ISELDK (intermediate select disk) code with the slightly more complex version shown in the Table. I put this on my Televideo, and the results were most pleasant. Now when I attempt to access a nonexistent drive, the system does not force a direct-CBIOS warmboot that drops me out of NZCOM.

; Add DRVEC definition in the ENV common

COMMON /_ENV_/
Z3ENV:
DRVEC EQU Z3ENV+34H ; Drive vector
CCP EQU Z3ENV+3FH
DOS EQU Z3ENV+42H

; Modify ISELDK as follows and place it after the DOBIOS code and before
; the data area for register storage.

ISELDK: LD HL,(DRVEC) ; Get drive vector
LD A,16 ; Subtract requested drive
SUB C ; .. from 16

LD B,A ; .. and put into B
ISELDK1:
ADD HL,HL ; Move bits left into carry
DJNZ ISELDK1 ; Loop 16-<drive> times
LD HL,0 ; BIOS return code for invalid drive
RET NC ; Return if drive vector bit not set
LD A,27 ; Otherwise, use CBIOS function
JR DOBIOS ; .. at offset 27

Table 2.
Code added to virtual BIOS to support the environment drive vector at the BIOS level.

These two examples of system enhancements by no means exhaust the possibilities. One can implement all kinds of additional features and drivers right in the NZCOM VBIOS. Joe Wright suggested early during NZCOM development that one should create an absolutely stripped down CBIOS, one that contains only the functions that are absolutely necessary to get the system running and then implement all the bells and whistles in the VBIOS. These extra features would include things like RAM-disk drivers, keyboard type-ahead buffers, logical drive swapping facilities, and disk error recovery management routines. With this strategy, one can actually achieve a larger TPA with an NZCOM system than one had under the standard CP/M system, since the CBIOS can be made smaller and the fancy features dropped when a larger TPA is more important.

For example, the "Abort, Retry, Ignore" message should be implemented in the VBIOS, with the CBIOS returning from disk errors with standard error codes. With the normal VBIOS, the error will simply be passed back to the DOS, which will report the error in its usual way ("BDOS ERROR on..." in the case of the Digital Research BDOS). A more elaborate VBIOS can detect the error, report it to the user, and allow the operation to be retried. When the system is running in remote mode, either the simpler VBIOS can be used or the prompt can be vectored properly through the jump table so that the remote user will be able to deal with the problem.

Similarly, one should be able to handle the swapping of logical drive names in the VBIOS. There are a couple of pitfalls to watch out for, however. If you change logical names, you better make sure that the disk system is reset, probably both before and after the swap. You also better make sure that NZCOM can still find its CCP file, which is normally kept in directory A15:. If you swap the A drive without providing a copy of this CCP in the new A drive, you'll be in serious trouble. Of course, the swapping would be handled by a utility program, and it would worry about these requirements. The VBIOS would simply have the code for translating references to a logical drive value in register C into a possibly different physical drive value.

I hope that this short discussion has given some of you ideas for imaginative applications of the new capability offered by the NZCOM virtual BIOS. If so, I would love to hear about them and to see sample code.


[This article was originally published in issue 39 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]