COPYFAST.ASM Version 3.5

                             for

                   IBM 3741 Compatible Diskettes

               (Originally written by: Chuck Weingart  )
               (               2152 W. Iowa            )
               (               Chicago, Ill 60622      )

------------------------------------------------------------

This program is placed in the public domain. Selling or
licensing of this program is prohibited. (You are encour-
aged to give it away to all of your friends). If you make
changes that may benefit others, please send the new
version to the CPM Users Group, so all may have it.

------------------------------------------------------------


Modified:
       Version 1.0; by Chuck Weingart
               October 1980: after the public domain
               Tarbell disk copy program (does not
               require a Tarbell controller)
       Version 1.1; by Kelly Smith
               January 21, 1981:
               ( >>>modifications undocumented <<< )
       Version 1.2; C. Strom
               February 12, 1981: corrected typo
               in OBJMSG when SINGLE true; added
               a CR.
       Version 2.0; by Chuck Weingart
               February 16, 1981: Changed messages added
               in V1.1 and 1.2 back to upper case - some
               people have terminals that dont handle
               lower case.
       Version 2.1; C.W.
               February 18, 1981: added code to check
               console port for Ctrl-C abort condition
               during copy, and misc code to help in
               CP/M 2.2 systems that block and deblock
               from large sectors.
       Version 2.2; C.W.
               February 24, 1981: added auto size check.
               Added interleave table suitable for very
               fast disk controllers.
       Version 2.3; C.W.
               March 2, 1981: Change all references to
               EXITCP to EXIT so that user gets a chance
               to put in system disk before program re-
               boots CP/M.
       VERSION 3.0; C.W.
               March 6, 1981: Check if given number of
               buffers will overlay the BIOS and reduce
               accordingly.
               Moved comments to DOC file to keep the
               source editable in one pass, and add
               more explanation to the comments on
               sector sizes and read skewing.
       Version 3.1; C.W.
               March 10, 1981: Added TSKEW and code for
               track skewing to increase speed for fast
               controllers.
               Separated read and write error checking:
               NUMERR now applies to each track read,
               but to each entire write pass.
       Version 3.2; C.W.
               March 18, 1981: Corrected read skew bug
               noticed by Steve Bogolub. Added CR,LF to
               the read and write error messages that
               got deleted in version 1.1
               Added WRSWCH, WRCODE, WRTAB to help solve
               problems with CP/M 2.2 blocking with some
               manufacturers CBIOS routines.
       Version 3.3; C.W.
               March 22, 1981: Added DIFFTRK (ability to
               copy from different tracks) in order to be
               able to unpack UCSD Pascal disks.
       Version 3.4; C.W.
               May 9, 1981: Fix bug in read error recovery
               that caused bad read to repeat forever.
               (Took me two attempts. No branch to TRYRDA.)
               Changed NUMERR to 4.
       Version 3.5; C.W.
               May 16, 1981: Add parameter for specifying
               the range of tracks copied. Removed CRLF
               between source and object messages. Moved
               INIT message to one-time code. Display #
               of buffers used if insufficient space. Echo
               source and object drives. Home drives after
               the copy is complete (to flush buffers).


Note to subsequent modifiers:
       Please note the specific changes you make to the
       code, so that others can keep track what you have
       done, even if it is just lower case messages.

------------------------------------------------------------

This  program  will  copy the data area of one CP/M disk to
another (thats tracks 2 - 76), as  fast  as  possible.   All
data  written  is  read  back  to  verify that the write was
successful, and multiple tracks are copied in one pass,  for
speed.   The version as supplied assumes that the controller
CRC checking is sufficient for verification, but an assembly
option allows complete byte-by-byte comparison on  the  read
back.  It is possible to copy only part of a disk,  moving a
block of tracks from one disk to a different part of another.

The  program  as  supplied will automatically determine the
size of the CP/M system and adjust  the  number  of  buffers
accordingly.  For example, it can copy four tracks at a pass
in a minimum (16K) system, and 18 tracks at a pass in a max-
imum system (64K).  A fixed-buffer option is also available:
the number of track buffers, and hence the minimum size, can
be  changed  to  suit your system.  Only standard CP/M CBIOS
calls are used to access the disk, no  BDOS  calls,  so  the
BDOS and CCP can be overlaid by the track buffers.  No other
CP/M  functions are assumed.  North Star CP/M or UCSD Pascal
users should be able to modify this program easily to run on
their systems.

COPYFAST will allow a disk to  be  copied  on  a  one-drive
system,  as  an  assembly option.  A version with 18 buffers
(64K) will require only 5 complete swaps.

------------------------------------------------------------

To run, just type: COPYFAST<cr>.   The  program  will  then
request the source and destination disks, (drives A - F) and
give  you a chance to put in the correct disks in the drives
before continuing (or quit by entering CTRL-C).  When  done,
the  program  will  ask  if  another  pair of disks is to be
copied, and the process repeated.

The  range  of  tracks  copied  can  also be specified in a
single character parameter after the program name.  That is:
COPYFAST X<cr> where X is one of the following values:

A       All     0-76            Entire disk
D       Data    2-76            CP/M data area
F       First   2               CP/M directory track
L       Last    76              Last track on disk
O       One     1               Track one, UCSD directory
P       Pascal  1-76            UCSD Pascal data area
S       System  0-1             CP/M bootstrap
Z       Zero    0               Track zero, UCSD bootstrap
               (Note: only complete tracks are copied)

Only  the  first character is checked, so the full word may
be spelled out, if desired.  Invalid letters will  cause  an
error  message,  and  the program will not run.  The default
range, currently 2  to  76,  is  given  in  the  program  at
locations 12DH and 12EH.

The console is checked often to see if the operator has en-
tered a CTRL-C, so it is possible to abort the program  even
during the actual copy.

If the number of buffers specified during the assembly is
too large, that is, if the CBIOS would be overlaid during
the copy, then a message will be issued and the program will
reduce the number of buffers to fit the system.

------------------------------------------------------------

This source can be assembled with the CP/M ASM or MAC.

COPYFAST  runs  on an 8080 or similar CPU, CP/M 1.3, 1.4 or
2.2,  or Cromemco CDOS disk operating systems  as  supplied,
and  does  not use any particular type of controller or disk
hardware, other than the "standard" 77 track, 26 sector-per-
track disk.  The latter two numbers can be easily changed in
the source.

The program currently assumes that the disk controller  can
read  and write the disk in just one revolution.  This means
that an entire track can  be  written  and  checked  in  two
revolutions.   Two  alternate interleave tables are included
in the source if this is not possible  with  your  hardware,
and any sector interleave can be used by changing the table.
An  assembly option is available to allow interleaved reads,
if your disk controller cannot keep up with the program. An-
other option is available if read interleaving is not used,
to increase the read speed by changing the first sector read
on each track so the read will start as soon as possible af-
ter the seek is complete.

This program  has  been  run  unaltered  on  a  Micromation
Doubler  disk  controller  with Shugart drives, the Califor-
nia Computer Systems model 2422A disk controller, a  Tarbell
single  density  controller board, and a Cromemco 4FDC board
(the latter two use a WD 1771 chip) with Persci drives,  and
the worst copy time was 122 seconds.  A typical time is more
like  56  seconds  (CCS  controller).  Faster hardware means
faster copies.

The program is written with all necessary constants in  eq-
ates,  so  modifying  it for some other type of sector size,
for example, should be fairly easy.  In fact, Roy  Lipscomb,
a  member  of  the  Chicago Area Computer Hobbyist Exchange,
wrote a program called FOTOCOPY  for  the  Thinkertoys  DJ2D
controller after asking me a few questions about the purpose
of  the  BIOS calls.  I believe he simply modified the CBIOS
jump table in the program to point to the on-board EPROM  of
the  DJ2D,  and  changed  the  various  equates  to what was
necessary for a 512 byte double-density  disk.   He  reports
really  fantastic  copy  speeds for that controller using my
methods.

The only bad comments I have heard to date is that the pro-
gram  isnt  fast with the Txxxxxl and Txxxxxxxxy versions of
CP/M.  The two cases I got personally involved  with  turned
out  to  be  very much the same thing: poorly written sector
blocking/deblocking routines.  In one case, I suggested that
the interleave table be  changed  to  1,2,3,4,9,10,11,12,...
in  a  512  byte  (128*4)  sector.  That way, the first four
"sectors" fit into the first physical sector, and  then  the
four "sectors" in the third physical sector are done, and so
on.   The  second case turned out to be a bug in the DEBLOCK
code that Digital Research is suggesting  be  used  in  CP/M
2.x.   The  source  they  are  distributing will miss blocks
and/or cause unnecessary I/O to the disk.  If you use  their
code, do so with care.

One manufacturer of controller boards  is  supposed  to  be
supplying  a  CBIOS  that  checks the density of the disk on
every single track.  Obviously, that will really  slow  down
everything, including COPYFAST.

Note: for CP/M 2.x users with single drive systems and sec-
tor sizes greater than 128, the program now homes  the  disk
before swapping the disk (at label STARTL).  This is to give
the CBIOS a chance to write the last sector.  If homeing the
drive is not enough to cause your CBIOS to empty the buffer,
then  add  any  code at that point that will do so or change
the CBIOS.  I recommend the latter, since homing  the  drive
is  only  done  in  such special cases such as a reboot or a
disk change.

------------------------------------------------------------

Quick course in floppy disk functions:

The  IBM 3741 floppy disk has 77 tracks, or positions where
the read/write head can be.  There is always a counter some-
where in the BIOS and/or disk controller  board  that  gives
the  current  position  of  the currently selected disk.  It
usually takes about 15ms to move the head one track  (in  or
out),  and can take up to 120ms to move the heads from track
76 to track 0.  Track zero, by the way, is  at  the  outside
edge of the disk.  The diskette rotates at 360 RPM, or 167ms
per  revolution.   The  standard format specifies that there
are 26 128-byte sectors, or records, on each track.

A little quick arithmetic will give you the results that it
takes maybe 6ms or slightly longer for one  sector  to  pass
under  the head.  Since the disks are always rotating, there
is always  some sector now under, or about to be under,  the
head.   It  can also take an entire sector for a floppy disk
controller to find where it is, assuming that the disk  head
was suddenly and randomly loaded, or moved to another track.
Furthermore,  many  floppy disk controllers cannot even read
one sector after another, or  perhaps  they  can  read,  but
cannot  write that fast (since writing requires that some of
the gap bytes be written before and after  the  data,  where
reading  can  start  just  after the address mark, and stops
when the first gap byte after the data stops.)

Another major consideration for copy programs  is  that  it
takes  maybe  30ms for the head of a disk drive to be loaded
onto the surface of the diskette, and you dont  like  to  do
that often, since the impact could cause wear.

What does this all mean?  Well, suppose you want to write a
copy  program.   First, you have to tell the controller what
track to start at, and to load the head.   That  might  take
30ms to load the head and 50ms to move the head to the right
track.   Now, you want to read sector one, but sector one is
not there right now.  You might have to wait 80ms for sector
one to come around.  (there is no CBIOS  command  for  "read
the  next  sector,  whatever  that  is" and some controllers
cannot do it at all) After you have read sector  one,  maybe
you then want to read sector two.  Unfortunately, you waited
too  long  to  decide,  and  the head is now halfway through
sector two.  So, the controller waits 166ms for the start of
sector two to come sround again.  You would have been better
off if you asked for the odd sectors first  (1,3,5...)   and
then  gotten  the even sectors on the next revolution.  That
would take only 334ms all in all.  That is  what  is  called
read interleaving, or skewing for short.

There  is  another  type  of skewing, called track skewing.
That occurs when you have read the last  sector,  sector  26
for  example.   Now,  you order the disk to move the head to
the next track.  That took 15ms, and in that time sectors 1,
2, and part of 3 rotated by under the head.  So, if you tell
the controller to read sector 1, then you have to wait until
sector 1 rotates by again, about 150ms.  If you could  start
this  read  at  sector 4, and read everyting sfter that, you
would not waste  as  much  time.   That's  track  (or  seek)
skewing.

In order to make a copy in the shortest time, this program
does all of the following:

1. Reads as many tracks as possible into core, and then
       switches to the other drive to write. This will
       minimize the number of times the head on each
       drive has to be loaded.
2. Does all reads as fast as possible, with any skew that
       is needed to achieve that. The fastest possible is
       no skewing at all, of course. The skewing pattern
       is in a table for easy manipulation.
3. Does all writes as fast as possible, with any skew that
       is needed. The write skew does not have to be the
       same as the read skew, because some controllers can
       read faster than they can write.
4. Allows track skewing on the main read so that the track
       can be read with as little rotational delay as poss-
       ible. The read after the write does not need this
       skewing, because there is no movement between the
       write and the following read.

The program does one thing that slows it down: it checks
the data that is written by reading it back. If you are very
confident of the reliability of your system, controller,
and disks you could skip that step. I think that starts at
label WT3 in the program, and the code could be commented
out. On a normal controller, that step adds about 12 sec-
onds to the copy time, and I think it is worth it.

------------------------------------------------------------

       Assembly-time variables that can be changed:

SINGLE: TRUE for single drive copy program

RSKEW:  TRUE  if  read  interleaving  needed.  If FALSE, the
       program will  read the sectors sequentially  (1,2,3,
       4,...)   When  TRUE, you should modify the read skew
       table (READTAB) to fit your particular requirements.
       Read  skewing is used on the initial read and on the
       read-after-write check.

TRSKW:  TRUE if  track to track read skewing is desired when
       there is  no read skewing.  TRSKW  may be TRUE  only
       when RSKEW is  FALSE.  TSKEW gives  the value of the
       track sector skew.

DOCOMP: TRUE  if  byte-by-byte   comparison  is  desired  on
       read-after-write check.

WRSWCH: TRUE if it is necessary to pass your CBIOS different
       values in reg. C during writes. See the CP/M 2.2 Al-
       teration Guide for the purpose of this value in reg.
       C.  If TRUE,  it will be necessary to edit the WRTAB
       table to fit your particular requirements. If FALSE,
       the value in  reg. C will  be that  given in WRCODE,
       normally 2.

NUMERR: The  number of errors before counting as a permanent
       error.  The error counter applies  independently  to
       each  track  read,  but applies to each entire write
       pass.  That is, the program will try  to  read  each
       track NUMERR times, and will quit and then go to the
       next track.  However, when
writing, the program will
       stop writing when NUMERR errors have occurred on the
       current   pass,   and  then  it  will  start  a  new
       read/write pass at the track after  the  track  with
       the  write error.  The read and write error counters
       are independent, since they are supposed to  be  two
       different disks.

SDLAST: the number of sectors per track. This value also de-
       termines the size of the  READTAB, WRITAB, and WRTAB
       tables.

TSKEW:  The  value  of  the  track to track read skew.  This
       applies only if  RSKEW is FALSE, that  is,  no  read
       sector  skewing.   This  is  to  allow  even  faster
       reads when a fast disk controller chip is used.  The
       reason is this: after the last sector on  the  track
       is  read (usually 26), the program must move the arm
       on the disk to the next track.  This takes time,  of
       course,  and  so the disk has rotated a bit.  So, if
       the next  sector  wanted  is  the  first,  then  the
       program  has  to  wait until the disk has rotated a-
       round all the way back to the  start.   But  if  the
       program  could  then start with sector 8, say, there
       would be no waiting.  Of course, the  program  would
       have  to read sectors 1-7  after reading  number 26,
       and then move the arm.  So,  then  it  should  start
       reading  the next track at sector 13, and so on.  In
       this case, the value of TSKEW  is  7.   The  program
       will  always  start  a  pass on sector one, but will
       not  start at sector one if the track skew  gives  a
       value  greater  than SDLAST.  That is, track skewing
       is a modulus  function.  If TSKEW  is  7,  the poss-
       ible  starting sectors are  1,8,15,22,3,10,17,... if
       SDLAST is 26.  Track skewing is used only during the
       initial  read  from  the  source   disk.    On   the
       read-after-write check, the write skew table is used
       to compensate for the head load and seek times. This
       value is ignored if TRSKW is FALSE.

SECSIZ: number  of  bytes per sector.  Read the comments re-
       garding the sector size below if you plan  to  adapt
       the  program to some other type of disk format.  For
       CP/M compatible BIOS routines, this is always 128.

WRCODE: If WRSWCH is FALSE,  this is the value passed to the
       CBIOS sector write routine in reg. C.  See the  CP/M
       2.2 Alteration Guide for details of this value,  but
       it is normally 2 for this application.

FIRSTRK:the first track  copied.  If the  starting track for
       the source and object disks are not identical,  then
       this figure  applies to the object disk.  Note: this
       value is  normally  the track where CP/M  places the
       directory.  It is assumed that the  bootstrap starts
       at track 0 and ends on track FIRSTRK-1.

LASTRK: the last track copied plus one. The number of tracks
       copied is normally LASTRK-FIRSTRK.

       These latter two values specify the copy range,  and
       the program can  be run in other ways  by the param-
       eter given when COPYFAST  is first invoked.  FIRSTRK
       and LASTRK  specify how the  parameter will actually
       be interpreted.

All    0-(Lastrk-1)            Entire disk
Data   Firstrk-(Lastrk-1)      CP/M data area
First  Firstrk                 CP/M directory track
Last   (Lastrk-1)              Last track on disk
One    1                       Track one, UCSD directory
Pascal 1-(Lastrk-1)            UCSD Pascal data area
System 0-(Firstrk-1)           CP/M bootstrap
Zero   0                       Track zero, UCSD bootstrap
               (Note: only complete tracks are copied)

BUFFNU: the  number of full track buffers that will fit into
       your system.  This figure includes the space used by
       the read-back buffers, if used.  Zero or minimum 2.

       If  you  wish auto-size determination, set BUFFNU to
       zero, and the program will use all space up to,  but
       not  including the CBIOS routines.  With the current
       specification of 26 sectors per track,  the  program
       will  require  at  least  39K  for  buffers alone if
       BUFFNU is 12, and so should  run  in  a  42K  system
       minimum.   (12  *  128  * 26 = 39936 plus 3K for the
       CBIOS and program).   I  recommend  that  BUFFNU  be
       minimum   of   2   if  you  do  not  want  auto-size
       determination.

DIFFTRK: is the difference between the source and the object
       track numbers.  That is, this quantity is  added  to
       the  object  track  number  to  get the source track
       number.  This is so that the program can move tracks
       around from one disk to another.  This  is  normally
       zero.   DIFFTRK is ignored in a single-disk program.

       For example, to copy tracks 25 to  49  from  a  UCSD
       Pascal  disk to tracks 0 to 24 of an empty disk, set
       FIRSTRK to zero, LASTRK to 25 (24 plus 1), and  then
       DIFFTRK has to be 25(source) - 0(object), or 25.

       DIFFTRK is used  only when the default copy range is
       used.  If one of the range  parameters is specified,
       then DIFFTRK will be set to zero automatically.

The last four  quantities are in  single-byte  constants in
the program, and can be modified by DDT to alter the charac-
teristics of the program to suit your needs.  They are found
at label TRKSRT in the program, at locations :
       FIRSTRK         12D
       LASTRK          12E
       BUFFNU          12F
       DIFFTRK         130
I change these values  to create various  versions of Copy
programs. These  values are defaults, and they are not used
if the  range parameter is  specified,  (or in  the case of
BUFFNU, if CP/M is too small).


Note: some distributors  of  CP/M  (such  as  Micromation),
supply a CBIOS that alters the BDOS routines, whether or not
the  BDOS  is  present  there.   This  means that you cannot
safely let COPYFAST take all space up to the CBIOS,  because
the  CBIOS will modify the contents in the highest buffer in
the mistaken idea that the  BDOS  is  there.   This  usually
means that the CBIOS cannot be used for UCSD Pascal, either.


Note:  if you are modifying this program for use with other
than 128 byte sectors, and this has been done  successfully,
you  MUST  change the code at labels RT3, WT3, and WT4.  The
change generally consists of adding a few more DAD  H  lines
to  the code.  There are currently 7 of these, since 128 = 2
to the 7th power.  If you are going to  be  processing  1024
byte  physical sectors, then add 3 more DAD H instruction to
each of the three places indicated, because 1024 = 2  ^  10.
Do  not  change  the  number if you are running with a CBIOS
written to  deblock  the  physical  sectors  into  128  byte
logical  sectors,  as  all  CP/M 1.x and 2.x compatible BIOS
routines do.  In such cases, the  number  of  "sectors"  per
track is changed instead, exactly the same as CP/M itself is
told.   The  CBIOS in that case will put the logical sectors
into one physical sector before doing the write.  It will of
course be necessary to change  the  sector  skew  tables  to
something   that   matches  the  capabilities  of  the  disk
controller and the blocksize.  You must  understand  exactly
how your CBIOS blocks and deblocks from the physical sectors
before you can make up a good skew table.

-------------------------------------------------------------

Alternate interleave table for slower controllers.

The sectors are listed in the order they will be written to
disk.  NOTE: the peculiar ordering is due to the  fact  that
some  disk  controllers cannot switch between writing sector
26 and reading sector 1 in time - so the table  begins  with
25,  proceed up every other sector, and ends with number 24.
While the head is passing sectors 25  and  26,  the  program
will  be  switching to read back the entire track.  There is
generally no problem starting with sector 25, because simply
moving the head after the previous read on most drives  will
take  about  one half revolution.  This table was determined
empirically using Shugart drives and doing actual tests with
two different types of controller  boards,  and  it  is  the
fastest variation found.  Change at your own risk.

       DB      25,1,3,5,7,9,11,13,15,17,19,21,23
       DB      26,2,4,6,8,10,12,14,16,18,20,22,24

The  following  table  is  recommended  for   those   whose
controllers  cannot write even every other sector.  There is
no peculiar starting sector here because the interleave ends
on sector 24, and that is the same as the table above.  This
might be a better choice for a "universal" table.

       DB      1,4,7,10,13,16,19,22,25
       DB      2,5,8,11,14,17,20,23,26
       DB      3,6,9,12,15,18,21,24

It is likely that if you require one of the alternate write
skew tables, then you will also have to have read skew.  So,
you should also make RSKEW equal to TRUE and modify the read
skew table to run as fast as possible.

-------------------------------------------------------------

Users  of  double-density  controllers may be interested in
what I did with mine.  My BIOS is written so that  a  double
density disk is considered as two single disks, that I refer
to  as  "the  front  half"  and "the back half".  With three
drives, A, B, and C are the fronts, and D, E, and F are  the
back.   A single density disk is only front.  Each track has
3328 bytes (26*128) bytes of data for the  front  half,  and
3328  bytes for the back half.  The way COPYFAST works makes
it possible  to  copy  the  front  half  to  the  back,  and
vice-versa,  and  I  dont  have  a  lot of funny versions of
programs written exclusively for a particular density  disk.
Two "disks" per floppy also means twice the directory space.
My  BIOS  is also written to check the disk density whenever
the disk in HOMEd, which COPYFAST does before starting.  The
only hardware dependencies in my  system  are  in  the  disk
initialization program and in the BIOS.

------------------------------------------------------------

Suggestions for improvements:

1. Ask if you really wanted to copy a disk if track 2
  (the directory) was completely empty i.e. all E5's
2. Better error checking .. the program reports only
  one comparison error per track right now.
3. Have an option where it only compares two disks to
  see if they are the same.
4. Check for CP/M 2. If so, use the disk attribute table
  for information like number of sectors, number of
  tracks, etc.
5. Could this thing be modified for hard disk backups?