For this issue I am going to live up to a long-standing tradition: onceì
again I am not going to cover the material that I said I was. Last time Iì
presented half the new material on ARUNZ and said I would cover the secondì
half this time. Well, I am not going to. ARUNZ is now up to version 'N'ì
(it was 'J' last time). Until it settles down a bit, it is probably futileì
to try to describe it. Besides that, I am getting a little bored with theì
subject (though obviously not with the program), and perhaps you are, too.
Though living up to tradition, I am going to reverse a trend. For some timeì
now my columns have been getting longer and longer. This time I really amì
going to write a short one. Besides the fact that because of me Art Carlsonì
is apparently running low on printer's ink, I am just about written out,ì
having just completed the manuals for NZ-COM and Z3PLUS.
NZ-COM and Z3PLUS
Those manuals started out being simple affairs, but I just don't seem to beì
able to get my obsession with thoroughness and completeness under control,ì
and they soon turned into full-fledged books about the respective productì
and Z-System in general. I've been burning the midnight (actually, 2 am)ì
oil for the past two or three months. Each manual now runs about 80 pages! ì
No wonder I don't have many words left in my system at this point.
Though somewhat reluctant to indulge in self-praise, I have to say that theì
manuals are really quite good, and the products (NZ-COM and Z3PLUS) areì
absolutely fantastic. Joe Wright (NZ-COM), Bridger Mitchell (Z3PLUS), and Iì
have had a very enjoyable and highly productive partnership in this effort. ì
I sincerely urge you all to buy the automatic, universal, dynamic Z-Systemì
appropriate for your computer: NZ-COM for CP/M-2.2 computers and Z3PLUS forì
CP/M-Plus computers. Both are $69.95 from Sage Microsystems East,ì
Plu*Perfect Systems, or Alpha Systems (see ads in TCJ).
After the experience bringing these products to market, I will no longerì
laugh so heartily when I hear stories about Borland or Lotus or Ashton-Tateì
not delivering their products on the promised dates. Hopefully you haveì
lost your TCJ issue #32 and have forgotten that I wrote there, and I quote:ì
"By the time you read this, they will definitely be available." In issueì
#33 I said, "the two new dynamic Z-Systems that will have been released byì
the time you read this." That almost made a double liar out of me. ì
Luckily, issue #33 was delivered just enough behind schedule to let thatì
statement squeak through -- barely.
With respect to NZ-COM, Joe Wright and I have agreed to publicly blame eachì
other for the delay. Actually, following common practice, we originallyì
both agreed to blame Bridger Mitchell, though he, of course, had nothing toìèdo with the NZ-COM delay (Z3PLUS is another story). However, now thatì
Bridger has a TCJ column, too, we worried that such a slander might not goì
unanswered. Anyway, Joe can blame me for not getting the manual done onì
time, and I can blame Joe for not writing the code to conform to myì
description of it in the manual! You can readily see that no one shouldì
ever get involved in a product development all by himself. Always make sureì
there is someone else to blame.
The truth of the matter is that we really thought the coding was complete byì
early April and that a simple manual could be knocked off in a weekì
(naive!!). In fact, as I alluded to above, the scope of the manual keptì
expanding. At the same time, as we attempted to describe the programs veryì
precisely, we discovered a number of deficiencies in the code. Some codingì
limitations that we thought we would accept in the current version of NZ-COMì
and would upgrade later just didn't seem acceptable any more once we wroteì
them down on paper. As a result, we have really skipped version 1 of NZ-COMì
and have gone directly to version 2. There are quite a few exciting newì
features beyond what I described in issue #32; many will appeal especiallyì
to those with a penchant for the unconventional (but I won't say any moreì
about them now).
PRL Files and Type-4 Programs
One of the new features introduced with ZCPR34 is the type-4 program. Aì
number of questions have been appearing in messages on Z-Nodes, so I thoughtì
I would say a few words on this subject.
Just to refresh your memory, ordinary CP/M program files are loadedì
beginning at an address of 100H. This was also true of Z-System programs. ì
They differ from standard CP/M programs in that the code starts with aì
special header. One item in the header is the text string 'Z3ENV'ì
beginning with the fourth byte of the program. This string is used toì
identify the program as a Z-System program.
After the text string comes a number, now called the program type identifier. ì
If the number is 2, then the so-called environment descriptor is includedì
in the first page of the program file. These type-2 programs are rarelyì
seen today. If the number is 1, then the program was designed to run in aì
ZCPR3 system with a permanent operating system memory buffer containing theì
environment descriptor. The program only has to store the address of thatì
descriptor, and it can then adapt to any system in which it is run.
The environment or ENV address is stored in the two bytes immediatelyì
following the program type byte. Prior to ZCPR version 3.3, the address hadì
to be installed into programs using a utility like Z3INS before they couldì
be used. Starting with ZCPR33, the command processor installs the value asì
part of the process of loading the file from disk.
With ZCPR33, the type-3 program was introduced. These programs are notì
limited to execution at 100H, as all previous programs had been. The twoì
bytes after the ENV address contain the address at which the code isìèdesigned to run. The Z33 command processor examines the program type byte,ì
and if it is 3, it reads the load address from the header and proceeds toì
load the program to the designated address and execute it there.
The type-3 program made it possible to load and run programs at addressesì
other than 100H, but the address at which any given program file would runì
was still fixed. In his column in the last issue, Bridger Mitchellì
described a fascinating and remarkable program structure that allows aì
program to run at whatever address it is loaded to. That same idea is theì
basis for the new type-4 program. Bridger's ANYWHERE program could beì
loaded manually to any address and then executed. Type-4 programs areì
loaded automatically by the command processor to the highest address inì
memory at which they can run without overwriting the command processor orì
any resident system extension (RSX) that is present. I would like toì
provide some additional details on how type-4 programs work and how they areì
constructed.
As with ANYWHERE, type-4 programs are derived from so-called (and, asì
Bridger pointed out, mis-named) page-relocatable or PRL files. Bridgerì
defined and described those files in his column in the last issue, butì
another shot at it probably won't hurt. I will approach the subjectì
somewhat differently -- with a concrete example. Consider the short andì
simple program in Listing 1. It is set up for a starting address of 100H. ì
Fig. 1 shows the binary image of the sort one would see if the program wereì
loaded with a debugger (e.g., DDT) and displayed.
If we change the argument of the ORG directive from 100H to 200H andì
assemble again, we get the results shown in Listing 2 and Fig. 2. Youì
should examine those results and note the things that have stayed the sameì
and the things that have changed. Note in particular that only three bytesì
in the code have actually changed. One is the high order byte of theì
address of the initial jump instruction. The destination of that jump is inì
the program, and, since the program has moved up by 100H, the jump addressì
has increased by an identical amount. The second change is in the data wordì
containing the entry point address. Obviously that address changes when theì
program origin is changed. The third change is in the value loaded into theì
DE register pair. It is the address of the message string, which isì
likewise a part of the program.
Note that the argument of the jump to DOS has not changed. It is anì
absolute address outside the program. Therefore, it does not change.
Now let's look at a PRL file for the same program. I am not aware of anyì
assemblers that can produce a PRL file directly. The usual procedure is toì
remove the ORG statement from the source code, assemble the program to a RELì
(normal relocatable format), and then use a linker to generate the PRL fileì
from the REL file. Fig. 3 shows the binary image of a PRL file producedì
using the SLR virtual-memory linker SLRNK+. Unfortunately, inexpensiveì
linkers, such as SLRNK and ZLINK, are not able to produce PRL files. Laterì
we will show you a method, though somewhat tedious, that allows you toì
construct a reasonable approximation to a PRL file using an ordinaryì
assembler (no linker at all).è
SLRNK+ actually cannot produce a PRL directly using the source code asì
listed. The SLR manual discusses in a somewhat opaque way the technique forì
generating a correct PRL file. The problem is that the one-page nearlyì
empty header at the beginning of the program is not generated. Joe Wrightì
invented the trick of linking in the file SLR.REL derived by assemblingì
source code with the sole statement
DS 256
This allocates one page of memory. The PRL file is produced by the linkerì
command
SLRNK+ TEST/K,SLR,TEST,/E
The term "TEST/K" defines the output file, the term "SLR" allocates theì
empty header, and the term "TEST" links in the actual program code.
You should notice in Fig. 3 the following things. First, the PRL fileì
begins with a one-page header, which is entirely zero except for a word atì
address 101H (you can't tell from this example that it is a word, but itì
is). This word is the length of the code, 001BH or 27 decimal in thisì
example. The program code itself begins on the next page (200H) and is theì
same as the code in Fig. 1.
The other new bytes in the PRL file are those that follow the last byte ofì
the program code. These bytes comprise the relocation bitmap that Bridgerì
Mitchell described in his column in the previous issue of TCJ. The firstì
byte is 20H, which expanded to binary is 00100000. This means that theì
third byte in the program code is the high byte of an address that must beì
relocated to make the program code execute at an address other than 100H. ì
Indeed, the third byte is the address to which the JP instruction jumps. ì
The second byte in the bitmap is 08H or 00001000 binary. This tells us thatì
the 13th byte in the program code is an address that has to be relocatedì
when the program is relocated. Indeed, this is the address of the start ofì
the program in the Z-header. The third byte in the bitmap is 01H orì
00000001 binary. It tells us that byte 23 is an address. If we lookì
carefully, this is the address part of the "LD DE,MSG" instruction.
How Does ZCPR34 Load and Execute a Type-4
Bridger Mitchell explained last time in some detail how a PRL file can beì
relocated to run at any address. It really is not necessary to understandì
all the details. The basic idea is that the bitmap tells a loader whichì
bytes in the code to adjust.
The Z34 command processor has a special mechanism for processing type-4ì
programs. After the command processor has located a transient program, itì
loads the first record of the file into the default buffer at 80h. Here itì
can examine it to see what kind of program it is. If it is a standard CP/Mì
program or a type-1 or type-2 Z-System program, it sets up the load addressìèas 100H and proceeds to load the entire file into memory, starting over withì
the first record. If it is a type-3 program, the same procedure is followedì
except that the load address is extracted from the program header.
With the type-4 program things are not so simple, because the load addressì
has to be calculated and the code has to be relocated. Z34 gets these tasksì
accomplished in a very clever and tricky way. It could have done all theì
work itself, but that would have added a lot of code to the commandì
processor. Instead, we took advantage of the fact that a PRL file has thatì
two-record header with almost nothing in it. To make a type-4 program, weì
overlay onto this header a special loader program. Z34 executes the codeì
there to calculate the load address and then to perform the code relocation.
The loader is available in HEX format (TYP4LDR.HEX) and can be grafted ontoì
the PRL file using the command
MLOAD file=file.PRL,TYP4LDR
where 'file' is the name of the PRL file that you want to convert to a type-4 executable file.
By putting the loader code in the program rather than in the commandì
processor, we provide additional flexibility. TYP4LDR calculates theì
highest address in the TPA to which a program can be loaded, but otherì
loaders could return the address of the RCP or FCP and make possible self-installing modules. Clever users will undoubtedly come up with some otherì
interesting applications that use special header code.
How Do We Make a PRL File
The easiest way to make a PRL file and from that a type-4 program is with aì
capable linker like LINK from Digital Research or SLRNK+ from SLR Systems. ì
LINK came with my CP/M-Plus computer; I do not know how much it costs or howì
to obtain it otherwise. SLRNK+, which offers many very useful and powerfulì
features besides the ability to make PRL files, costs $195. For someone whoì
wants to experiment casually with type-4 programming, this is probably tooì
much money to spend. If you are not going to do it very often and don'tì
mind a little work, you can hand craft a PRL file using a debugger like DDT. ì
I will take you through the procedure using our sample program from Listingì
1.
Making the bitmap is the hard part of the procedure. You should key in theì
program called MAKEPRL.Z80 in Listing 3 and assemble it to a HEX file. Weì
will use that code in the debugger first to make a "byte-map" and then toì
convert the byte-map into a bitmap. We assume that we have alreadyì
assembled versions of the program with ORGs of 100H and 200H.
To construct the PRL file, we invoke the debugger (assumed to be DDT) andì
issue the commands shown in Fig. 4. The first pair of commands loads theì
utility program MAKEPRL. The next two lines load the version of our programì
that was assembled to run at 100H. At this point we have to note the "nextì
load address" reported by the debugger (I suggest you write it down). Nowì
we load the version of the program assembled to run at 200H so that itìèfollows right on the end of the 100H version. To do this, we use an offsetì
value in the "R" command that is 100H lower than the "next address" that wasì
reported a moment ago.
There is one other very important step we need to perform at this point. ì
MAKEPRL has to be told the address at which the second program image wasì
loaded. The value is patched in at address 10EH using the commands shown inì
Fig. 4 starting with "S10E". For our example program, the next address isì
reported as 0280. Therefore, low-nextaddr is 80 and the high-nextaddris 02.
Now we let MAKEPRL do the hard part by running it with the "G" command. ì
When it is finished, we need to examine the value in the HL register, sinceì
it tells us the next address after the bitmap. After leaving DDT we have toì
save the code image from 100H up to but not including that address. For theì
example program, the value in HL is reported to be 290H. Since we areì
presumably running Z34 and have the type-4 SAVE program, we save the resultì
using the command
SAVE 100-28F PRLTEST.COM
If you do not have the type-4 SAVE,you will have to calculate the number ofì
sectors to save.
Fig. 4 lists one DDT command that we did not discuss. The "F103,1FF,0"
command fills the part of the header after the code size word with zeros.
This makes the file look prettier, but it is not absolutely necessary,
especially if you are later going to overlay the type-4 loader as described
below.
The PRL files made this way can be used to make type-4 programs, and theyì
can be used in Bridger Mitchell's ANYWHERE program. However, we shouldì
point out that these PRL files are not as efficient as those produced by aì
linker. We assumed that the code in the COM files extended to the end ofì
the last record in the file.
Perhaps you can build on my simple method and figure out how to extend it toì
produce an optimal PRL file just like the one from SLRNK+. It would alsoì
not be too difficult to write a program using routines in SYSLIB to read inì
the pair of COM files and generate a PRL file from them completelyì
automatically. The most elegant method for doing this would use random-record writes. I invite readers to send me such a program.