MaxZ80 - Chapter 9

A program that copies a file needs to know the name of the source file and
optionally the name to give the copy and whether to overwrite an existing
target. CP/M uses the 128 bytes of memory starting at hex location 80 for
several purposes. This "Transient Buffer Area" (TBA) provides a place to
store what is typed after a command. CP/M puts the tail of your command into
the TBA.

MOTYL.BAS and CMD.MAC (Chapter 3 and 4) rely on the TBA. Here are two lines
of code from CMD.MAC.

 LD A,(0080H)     ;COMMAND TAIL LENGTH IN A
 LD HL,0081H

CP/M figures out the number of characters in your command tail and puts this
number into location hex 80. From hex 81 up to hex FF, CP/M puts a copy of
the command tail itself. The Z80 microprocessor's registers are addressed in
assembler language with the letters A, F, B, C, D, E, H and L. Certain pairs
of registers can be used together, which is useful when you need to track 16
bit addresses. HL is the H and L register pair. The above two lines are
storing the length in register A and the address that holds the tail itself
in the HL register pair.

Joe Wright, who was mentioned in Chapter 7 when we talked about NZ-TOOL.DEF,
NZ-TOOL.BOX, CHICKEN.PAS and the speed field in the environment record, wrote
CPY.PAS, a file copy program. He used Turbo Pascal 3.0, which has a builtin
ParamStr() function. ParamStr(0) returns the name of the command, ParamStr(1)
returns the first element in the tail of the command, ParamStr(2) returns the
second, etc. The builtin ParamStr() parses what it finds in the Transient
Buffer Area.

Turbo Pascal 2.0, which is what we use here, does not have a builtin
ParamStr() function, so I wrote one.

CPY DEVELOP:EBC.ASC FLOPPY:FLIER.NEW

should copy the file EBC.ASC onto my floppy and call it FLIER.NEW. It made
the copy but when a directory was done on the floppy a file called FLIER.NE
(no 'W') was found. This was due to the fact that the above command is longer
than 32 characters which is a problem because CP/M also uses the bytes in the
TBA from offset 32 on for another purpose and this was what was causing the
target file to lose its "W."

Z-System supports a "Multiple Command Line Buffer." Commands are separated
with a semicolon. The Multiple Command Line Buffer is like the TBA, but it's
about twice as long and is used only for command line tracking.

I rewrote ParamStr() to use the Z-System segment that supports the Multiple
Command Line Buffer (see PARAMSTR.INC). It uses the ENVREC record structure
and the variable Z3EADR Absolute $0109 which is a pointer to this structure.
Although ParamStr() was later revised to use a different method than what we
show below, it is still worth mentioning the older approach.

MCLPTR = ^MCLREC;
MCLREC =
  Record
     NXTCHR  : Integer;
     MCLMAX  : Byte;
     MCL     : String[203];
  End;

The first three bytes of this structure are overhead to track the address of
the first character in the next command to run (NXTCHR) and to hold the size
of the maximum number of characters allowed.

Function GETMCL:Integer;   { get multiple command line }
Begin getmcl := Ord(z3eadr^.z3cl) End;

Function ParamStr(i:integer):str80;
Var
  j,j1,lcmd,lc,p      : Integer;
  whatsleft           : str80;
  cl                  : mclptr;  { from nz-tool.box }
Begin
  cl := Ptr(getmcl);  { point to multiple command line, }
  { using nz-tool.box }
  { determine end of command and its offset }
  { in multiple command line } 
  lc := cl^.nxtchr - 1; j := lc - Ord(cl) - 3; lcmd := 0;
  While (cl^.mcl[j]<>';') And (j<>0) Do Begin { back up to start of command }
   j:=j-1;lcmd:=lcmd+1;End;
  { load work string with it }
  For j1:=j+1 To j+1+lcmd-1 Do
    whatsleft[j1-(j+1)+1]:=cl^.mcl[j1];
  whatsleft[0]:=Chr(lcmd);
  p:=Pos(' ',whatsleft);                   { locate first space }
  If (i>0) And (p=0) Then whatsleft:='';
  While (i<>0) And (p<>0) Do Begin         { grab the ith parm }
    i:=i-1;                                { decrement parm counter }
    Delete(whatsleft,1,p);                 { lop off the leading parm }
    p:=Pos(' ',whatsleft);                 { locate next space }
  End;
  If p=0 Then                              { if at end, use what's left }
    ParamStr:=whatsleft
  Else
    ParamStr:=Copy(whatsleft,1,p-1);       { else, what's at the beginning }
End;

CPY was written to demonstrate how to write programs in a high level language
and take advantage of Z-System. It makes no attempt to be both CP/M and
Z-System compatible, unlike other programs which we'll encounter later. It
exits gracefully if Z-System is not detected by invoking z3exist (see Chapter
7).

Begin {ning of CPY.PAS}
 If not z3exist Then Begin Write('Z3 not found.',cr,lf); Goto 100; End;

To test the behaviour of CPY under CP/M, you may issue the command

NZCPM

which will exit from Z-System and put you in a CP/M environment, or, more
accurately, a "ZCPR1" environment. To get to user area 6, type

6:

Then type

CPY

To get back to Z-System, type Ctrl-Break and then select the B option
(reBoot).