/* DMS-15 (eventually also 3/4) Copy floppy diskettes via a hard disk
   partition.  */

#include "stdio.h"

#define version  2
#define revision 0
#define patch	'H'

/* ******************** TABLE OF CONTENTS  (Approximate) *********************
 
    Program Description 		 1 
    Update History			 2 
    Equates				 2 
    Global Variable Definitions 	 3,4
    main()				 4
    Abnormal ends			 4
    User Interface (ask for drives etc)  5,6
    Disk i/o functions			 6-9
    Lower level user i/o		 9,10
    BIOS interface			 10,11
    Debugging aid			 12

**********************	 Program Descripition	*****************************
 
A total rewrite in C by Dave Bradburn 7/83.
  Original FDHDCOPY in Z80 assembly language by Doug Brentlinger
  with assistance of Dave Stein, 1/82
 
At this point the program is only intended to work with minifloppies.  That
  is, it must be run on a Fox or a DMS-15. 

System(0-2), Data(2-159), or All tracks can be copied.
 
The user must have a separate partition on the hard disk (local OR network)
  to use this program.	The partition name must be 'SCRATCH'.  If there is
  no such partition, use ALLOC to create it.  Since it must be at least as
  large as the Fox-floppy, specify a size of 3 to create a 1-Meg partition.
 
It is recommended that the user have a separate user table entry.  A good
  format for that entry would be:
 
      Name   COPY
  password   (You may want to control access.)
  Defaults   A:work partition - (copy program is here).
	     B:SCRATCH
	     C:M0
	     D:u:  (unassigned)
 
The user loads MINICOPY from drive A.  The floppy to be copied is C and the
  hard disk partition to be used is B.	Other arrangements are possible but
  in any case there must be one drive assigned to the Fox-floppy and one
  assigned to SCRATCH.	 SCRATCH can be a local or a network partition.

The program fills its buffer with data from the floppy (16k, i.e. 4 tracks).
  The  buffer is then written to the harddisk.	This process continues until
  all specified tracks are written to the hard disk.
Next, 16k at a time is read from the hard disk and written to the floppy.  The
  data is re-read from the floppy for verification.  If the compare function
  finds that the data read does not match the data written, an error message
  is sent to the user and the program is restarted.
 
The user can do multiple copies of a floppy disk.  When the program has
  finished the first copy, it will ask the user if she wants to repeat the
  copy.  Entering 'Y' will cause the copy to be repeated using the data
  already on the harddisk.
 
Program loops until user asks out or until aborted by a ^C.

There is a debugging aid that provides a list of the addresses of all the
functions and global variables.  To invoke it, start the program with the
command MINICOPY DEBUG.  If it is desired that the program exit to CP/M 
after printing this information, enter MINICOPY DEBUG EXIT.  (This latter
facility is useful in case the output is redirected to a file or printer,
in which case the prompts for console input would not be visible.)  Output
can be redirected by putting >filename.ext or >lst: on the end of the
command line, e.g.  MINICOPY DEBUG EXIT >DEBUG.XRF or MINICOPY DEBUG EXIT >PRN:

**********************	 Update History  **********************************
					 
 
Vers.	 Date	 Whom		Differences from previous version
-----	 ----	 ----		---------------------------------
1.01   20 May 82  MdG  Corrected an error in the computation of buffer
			addresses.  Bufbegin was being added to the buffer
			length instead of bufbase.
1.01A  20 May 82  MdG  Updated the code to agree with the new cpmMAP.
   'B' 25 May 82       The original drive was not restored correctly. Fixed.
   'C' 26 July 82      Add a table of contents, reorganize and document the
			subroutines.  Correct a problem in the copy routine
			that caused program to read a second floppy disk
			incorrectly.
   'D' 8 July 83  DSB  Conditional assbys to work with DMS-15
		      
2.00A 19 July 83  DSB  Rewritten in C for DMS-15.  A FEW CRUCIAL ASSUMPTIONS!
		       Track zero must have the same format as the other
			tracks.  This is valid for minifloppies ONLY!!!
   'B' 22 July 83      Clean messages, rename to MINICOPY.
		       Wait for return before beginning a repeat copy. 
		       Give the user a graceful way out after the copy.
		       Give chance to retry copy with a fresh disk if
			a bad track aborted the previous attempt.
   'C'	9 August 83    Don't print number of k transferred except for floppies.
			(Hard disk info judged redundant and confusing)
   'D' 10 August 83    Rewrite "compare" function in assby.  The old compare
			took just long enough for the floppy head to unload.
   'E' 10 August 83    Don't do track translations for minifloppies.  Time
			tests show translation doesn't make a damn.
   'F' 11 August 83    Disk_to_disk re-selects logged drive after transfer.
			Per Doug B., the message "On which drive is ..." has
			been changed to "Which drive has... ".
   'G' 12 August 83    Use lockstring to make sure only one person runs this
			on the net at one time.  Many one-line functions (e.g.
			BIOS calls) turned into macros.   Any logical drive
			can be used (not just ABCD) if BIOS seldsk permits it
			to be selected (failures can be retried).	   
   'H' 16 August 83    Fixed a bug in the macro hd_avail -- put parens around
			the whole thing, otherwise the expression hd_avail==0
			would be true if not on the net.
 Here we go: */
       

/* ************************ DEFINE CONSTANTS ***************************** */


#define b_home	    8	    /* equates for BIOS functions */
#define b_seldsk    9 
#define b_settrk    10
#define b_setsect   11
#define b_setdma    12
#define b_read	    13
#define b_write     14
#define b_lock	    32
#define b_CPMmap    33
#define b_setbyt    35

#define SDtype	    0 << 5	/* device codes in DPB */
#define DDtype	    1 << 5
#define HARDtype    2 << 5
#define NETtype     3 << 5
#define MINItype    5 << 5
#define DMS15type   6 << 5
#define IN	    'I' 
#define OUT	    'O'

#define LOGDSK	    4	     /* refers to the currently logged disk */
#define LOCKPTR     0x4A     /* pointer to lockstring */
#define LOCKRES     0x49     /* result of lock req */
#define LOCKOK	    0

#define FALSE	    0
#define TRUE	    1	     /* This damned compiler has a bug that prevents */
			     /* our saying !FALSE (for machine independence) */
#define GOODNEWS    TRUE
#define BADNEWS     FALSE

#define ever	    (;;)     /* This is so we can say "for ever" */

#define sectsize    128 	/* CP/M sector length */
#define buflen	    0x4000	/* great big buffer   */
#define max_tries   2		/* if you can't write to a floppy track after
				   trying twice, the user probably is going
				   to be disappointed with that disk anyway. */


/* ******************** DEFINE SYNTHETIC FUNCTIONS (MACROS) *************** */

#define hd_avail()	  ((hard_dpb->MEDIA != NETtype) || lock("SCRATCH"))
#define track_xlate(x,y)   y	/* no track translation in this version */
#define flop_to_hard()	  disk_to_disk(&flop_info, &hard_info)	 
#define hard_to_flop()	  disk_to_disk(&hard_info, &flop_info)	

#define seldsk(d)	bioshl(b_seldsk,d-'A',0) 
#define settrk(track)	bios(b_settrk,track,0) 
#define setsect(sect)	bios(b_setsect,sect,0) 
#define setdma(loc)	bios(b_setdma,loc,0) 
#define setbyt(len)	bios(b_setbyt,len,0) 
#define dread() 	bios(b_read,0,0) 
#define dwrite()	bios(b_write,0,0) 

/* *********************** DEFINE SPECIAL TYPES *************************** */

typedef unsigned trknum,size;	/* these types could get bigger than 32k  */
				/* and we hardly want negative disk sizes! */

/* ************************* DEFINE STRUCTURES **************************** */

struct DPB{
    char CONTROL;   /* control byte -- ignore for now */
    int  MCV;	    /* max check vector */
    int  MAV;	    /* max alloc vector */
    char NAME[8];   /* partition name */
    char DRIVE;     /* volume */
    char MEDIA;     /* media type */
    char UNIT;	    /* drive or partition number */
		    /* NOW THE REAL CP/M STUFF: */
    size SPT;	    /* Sectors Per Track */
    char BSH;	    /* Block Shift Factor */
    char BLM;	    /* BLock Mask */
    char EXM;	    /* EXtent Mask */
    int  DSM;	    /* Disk Storage Mask =  # of blocks - 1 */
    int  DRM;	    /* DiRectory Mask = # of dir entries -1 */
    int  AL;	    /* two bytes of bit-mapped alloc stuff */
    int  CKS;	    /* ChecKsum Size */
    int  OFF;	    /* OFFset for reserved tracks */
    };

struct DISK{
    char drive; 	/* A,B,C,D */
    size trksiz;	/* bytes on a track */
    trknum tracks;	/* tracks on the disk */
    size k;		/* from which we can figure how many k */
    trknum first_trk;	/* first track to copy to or from */
    trknum max_trk;	/* last track to copy to or from */
    trknum cur_trk;	/* counter of where we are now */
    size secio; 	/* how many sectors BIOS will let us read at once */
    struct DPB *dpb;	/* pointer to rest of info  */
    };


/* ************************  GLOBAL VARIABLES  **************************** */

struct DPB *flop_dpb, *askflop();   /* pointers to DPB'S */
struct DPB *hard_dpb,	*askhard();
struct DISK flop_info;
struct DISK hard_info;

char *cpmmap();
char bigbuf[buflen]; /* holding space for disk transfer */
char verbuf[4096];   /* This is for reading back a floppy track to verify. */
		     /* Program has to make sure it doesn't read too much, */
		     /* e.g. it had better not try to read an hd track here. */

/* ******************************* MAIN ************************************ */

main(ac,av)
int ac;
char *av[];
{
    size blocks_per_drive, sectors_per_block;
    char a;

    if(ac>1)	    /* check for debug request on command line */
	debug(ac,av);
    printf("\n\nMINICOPY vers. %d.%d%c\n",version,revision,patch);

    for(flop_dpb= NULL; flop_dpb==NULL;)
	flop_dpb= askflop();	      /* get floppy drive assignment */
    for(hard_dpb= NULL; hard_dpb==NULL;)
	hard_dpb= askhard();	      /* get hard disk partition assmnt */

    if(hd_avail() == FALSE)
	lock_death();		      /* two net users can't run at once */

    fill_in_params(&flop_info);       /* figure track lengths and so forth */
    fill_in_params(&hard_info);

    if(flop_info.k  > hard_info.k) /* hd must be at least as big as floppy! */
	size_death();		  /*   (blow up and warmboot if it isn't.) */

    for ever
    {
	ask_which();		  /* which tracks to copy and how many? */
	flop_to_hard(); 	  /* copy onto the hard disk */
    
	a = oktocopy(); 	  /* ask user if ok to go on */
	while(a == 'Y')
	{
	    hard_to_flop();	    /* move data down onto floppy */
	    a = oktorepeat();	    /* ask about doing it again  */
	}     
	if (a == 'X')	/* break out if so desired */
	    break;
	printf("\nInsert the next floppy to read from.");
	printf("\nPress RETURN when you have done this.");
	while((a=upin()) != 0x0D);
    } /* loop until user gets tired of it */
   
    out();
}/* main */
    
/* *********************** NORMAL END ********************************** */

out()
{
    restore();	    /* fix disk assignment for sure */
    printf("\n\nExiting to CP/M\n");
    exit();
}

/* ********************** ABNORMAL ENDS ******************************** */


size_death()	 /* abort the program if SCRATCH isn't big enough */
{
    printf("\nYou cannot perform this copy because the floppy has %u k bytes",
	      flop_info.k);
    printf("\nand the hard disk partition has only %u k bytes.",hard_info.k);
    printf("\nTo use this program it will be necessary to allocate a larger");
    printf("\nSCRATCH partition on the hard disk");
    out();
} /* size_death */



lock_death()	/* abort the program if another net user is running it */
{
/*  printf("\nlock death.  hd_dpb->MEDIA = 0x%x, NETtype = 0x%x, unequal: %d",
	    hd_dpb->MEDIA,NETtype,hd_dpb != NETtype);
    printf("\nhd_avail = 0x%x",hd_avail());
*/  printf("\nYou cannot use the network SCRATCH partition because someone");
    printf("\nelse is using it.  Please wait a while and try again.\n");
    out();
} /* lock_death */

/* ********************* UPPER-LEVEL USER INTERFACE ********************** */


struct DPB *		  /* ask the user for floppy drive and return a */
askflop()		  /* pointer to that drive's DPB in the BIOS */
{
    char *mapptr;	  /* make a char ptr so we can do byte math */

    printf("\nWhich drive has the floppy to be copied (A,B,etc)? ");
    flop_info.drive = askdrive();

    mapptr = cpmmap(flop_info.drive); /* get ptr to cpm dpb for floppy drive */
    mapptr -= 16;		      /* point at beginning of DMS dpb */
    flop_dpb = mapptr;		      /* now we have the real dpb base */
    if (flop_dpb -> MEDIA != MINItype)
    {
	printf("\nError - drive %c is not a minifloppy.",flop_info.drive);
	flop_dpb = NULL;
    }
    flop_info.secio = flop_dpb->SPT;/* we can i/o a whole track if we want */
    flop_info.dpb = flop_dpb;	    /* make link to other struct */
    return(flop_dpb);
} /* askflop */



struct DPB *		/* ask user for the hd drive and return ptr to DPB */
askhard()
{
    char a = '\0';
    char *mapptr;	  /* make a char ptr so we can do byte math */
    char med;
    int i;

    printf("\nWhich drive has the hard disk partition (A,B,etc)? ");
    hard_info.drive = askdrive();
    mapptr = cpmmap(hard_info.drive);  /* get ptr to cpm dpb for floppy drive */
    mapptr -= 16;		     /* point at beginning of DMS dpb */
    hard_dpb = mapptr;		       /* now we have the real dpb base */
    med = hard_dpb->MEDIA;
    printf("\n");
    if (((med != HARDtype) && (med != NETtype) &&(med != DMS15type))
	|| (diffstr(&(hard_dpb->NAME),"SCRATCH ",8)))
    {
      printf("\nError- drive %c must be assigned to SCRATCH.",hard_info.drive);
      hard_dpb = NULL;
    }
    hard_info.secio = 1;      /* hd i/o must be read one sector at a time */
    hard_info.dpb = hard_dpb;
    return(hard_dpb);
} /* askhard */



ask_which()    /* ask the user which tracks are to be copied */
{
    char a = '\0';

    printf("\n");
    printf("\nPlease enter S  to copy SYSTEM tracks");
    printf("\n             D  to copy DATA tracks");
    printf("\n             A  to copy ALL tracks");
    printf("\nYour choice: ");
    for(a=upin(); !((a == 'S') || (a == 'D') || (a == 'A')); a=upin())
	printf("\nS, D, or A:  ");
    switch(a)
    {
	case 'S' : flop_info.first_trk = 0;
		   flop_info.max_trk = flop_dpb->OFF -1;
		   break;
	case 'D' : flop_info.first_trk = flop_dpb->OFF;
		   flop_info.max_trk = flop_info.tracks-1;
		   break;
	case 'A' : flop_info.first_trk = 0; 
		   flop_info.max_trk = flop_info.tracks-1;
		   break;
    }
    hard_info.first_trk = 0;	/* this is always the same */
    hard_info.max_trk = hard_info.tracks-1;
} /* ask lower & upper limits on tracks to be copied */



oktocopy()	    /* ask user if we can go ahead and write to floppy */
{		    /* return upper-case Y if yes, upper-case N if no */
    char a;

    printf("\n\nRemove the diskette you read from and insert a blank one.");
    printf("\nPress RETURN when you have done this.");
    while((a=upin())!= 0x0D);
    printf("\n\nDo you want to continue the copy (Y/N)? ");
    return(a=yorn());
} /* oktocopy */



oktorepeat()	    /* ask user if she'd like yet another copy */
{		    /* return upper-case Y if yes, upper-case N if no */
    char a,b;	    /* and upper-case X if exit to CP/M requested     */

    printf("\n\nDo you want to make another copy of this diskette (Y/N)? ");

    if ((a=yorn()) == 'Y')
    {
	printf("\n\nInsert the diskette you want to write onto.");
	printf("\nPress RETURN when you have done this.");
	while((b=upin())!= 0x0D);
    } /* give the user a chance to catch breath before thrashing disk */
    else
    {
	printf("\nDo you want to make copies of a different diskette (Y/N)? ");
	if ((b=yorn()) == 'N')	 /* no means exit to CP/M requested */
	    a = 'X';
    } /* if no repeat desired, find out whether a new copy is desired */
    return(a);
} /* oktorepeat */



bad_track_msg(device)	  /* give word of bad track on floppy */
struct DISK *device;
{
    trknum bad_track;

    bad_track = track_xlate((device->dpb)->MEDIA, device->cur_trk);
    printf("\n\nBad track here! Giving up after %d tries on track %u",
	    max_tries,bad_track);
    return(BADNEWS);
} /* bad_track_msg */


/*  ********** UPPER-LEVEL DISK INPUT AND OUTPUT FUNCTIONS  **************** */


fill_in_params(device)	    /* figure relevant numbers for a disk */
struct DISK *device;	    /*	 (e.g. # of tracks, size, etc.)   */
{
   /* figure tracks as (total sectors / sectors per track) + reserved tracks */
    device->tracks = (1<<(device->dpb)->BSH) * ((device->dpb)->DSM+1)
		       / (device->dpb)->SPT;
    device->tracks += (device->dpb)->OFF;
    device->trksiz = sectsize * (device->dpb)->SPT;  /* how long is a track */

    device->k = (device->trksiz >> 10) * device->tracks;

/*  printf("\n\nDrive %c:",device->drive);
    printf(" %u tracks of %u bytes each = %u k bytes total", 
	    device->tracks,device->trksiz,device->k);
*/
} /* fill_in_params (for one disk) */



disk_to_disk(source,dest)   /* transfer a bufferful, return good or bad news */
struct DISK *source;
struct DISK *dest;
{
    trknum trks_to_do, trks_that_fit;
    int chunk, total_chunks;
    int news;

    flop_info.cur_trk = flop_info.first_trk;	/* start at beginnning */
    hard_info.cur_trk = hard_info.first_trk; /* do this for symmetry, though */
				      /*  hd really always begins at track 0 */
    printf("\n");

    /* figure out how many buffersful we'll need.  Since we're using integer  
       math we need to make sure partial buffers get counted!  There are ways
       to do this in fewer lines and with only one division step (mod operator
       not needed), but I'm writing it this way for clarity since this step
       is hardly the rate-limiting step for this program. */

    trks_to_do = flop_info.max_trk - flop_info.first_trk;
    trks_that_fit = buflen / flop_info.trksiz;
    total_chunks = (trks_to_do / trks_that_fit);

    if (trks_to_do % trks_that_fit)
	total_chunks++;

    for(chunk=0; chunk < total_chunks; chunk++)
    {
	move_block(IN,source);		   /* get it	   */
	news = move_block(OUT,dest);	   /* well put it! */
	if(news == BADNEWS)
	    break;
    }
    restore();		/* repair disk assignment */
    return(news);	/* good or bad */
} /* disk to disk */



move_block(direction,device)	/* move the block, return good or bad news */
char direction;
struct DISK *device;
{
    trknum track;
    size k_done,k_per_track;
    char media;
    char *which_way, *what_disk;
    int ok, tries;

    /* set up the variables that don't need to be refigured on each pass */

    media = (device->dpb)->MEDIA;
    k_per_track = device->trksiz >> 10;

    /* set up the messages that tell the user what we're doing */
    switch(direction)
    {
	case IN :   which_way = "read from"; break;
	case OUT:   which_way = "written to"; break;
	default :   which_way = "ignored by";
    }
    switch((device->dpb)->MEDIA)
    {
	case  SDtype   : what_disk = "floppy    ";break;
	case  DDtype   : what_disk = "floppy    ";break;
	case  HARDtype : what_disk = "hard disk ";break;
	case  NETtype  : what_disk = "hard disk ";break;
	case  MINItype : what_disk = "floppy    ";break;
	case  DMS15type: what_disk = "hard disk ";break;
	default        : what_disk = "unknown media";
    } 
     
    /* Get down to business: select the right disk and start doing the i/o. */

    seldsk(device->drive);

    for(track = 0; (track < buflen/device->trksiz) && 
				(device->cur_trk <= device->max_trk); track++)
    {
	for(tries=0, ok= FALSE; (ok == FALSE) && (tries <= max_tries); tries++)
	   ok = xfer_trk(bigbuf+(track*device->trksiz),direction,device,media);

	if (!ok)	    /* abort copy if a track failed */
	    break;

	device->cur_trk++;  /* we did it, increment to next one. */

	/* Now placate the user by printing how many k we've done */
	/* (Hard disk info has been if'ed out as confusing to user) */
	k_done = (device->cur_trk - device->first_track) * k_per_track;
	switch((device->dpb)->MEDIA)
	{
	    case  SDtype   :
	    case  DDtype   :
	    case  MINItype :
		     printf("%uk bytes %s %s\015",k_done,which_way,what_disk);
	    default	   : ;
	} 
    } /* do one track at a time until end of buffer */

    if (ok)
	return(GOODNEWS);
    else
    {
	bad_track_msg(device);	 /* couldn't do the track. die. */
	return(BADNEWS);
    }

} /* move_block (move between disk and buffer) */


xfer_trk(buf,direction,device,media)
char *buf;
char direction;
struct DISK *device;
char media;
{
    trknum real_track;
    int sector;
    int ios_needed, io_op;
    int ok;

    real_track = track_xlate(media,device->cur_trk);/* speed up Fox-floppies */
    settrk(real_track); 			 /* do as few ios as */
    ios_needed = (device->dpb)->SPT / device->secio;  /* we possibly can */
    for(io_op = 0; io_op < ios_needed; io_op++)  /* in doing 1 track */
    {
	sector = (io_op * device->secio) + 1;
	setdma(buf + sectsize*(sector-1));
	setsect(sector);
	setbyt(sectsize*device->secio); /* get as much as allowed */
	switch(direction)
	{
	    case IN :	dread(); break;
	    case OUT:	dwrite(); break;
	    default :	printf("\nBad direction in xfer: 0x%x",
				 direction);
	}
    } /* read or write a track in the largest increments BIOS will permit */

    if((media == MINItype) && (direction == OUT))   /* verify */
    {
	xfer_trk(verbuf,IN,device,media);
	ok = !diffstr(buf,verbuf,device->trksiz);
    }
    else
	ok = TRUE;	    /* if not floppy write, it's probably fine */

    return(ok);
} /* xfer_trk */
   
/* ************************************************************

track_xlate(media,track)    /* perform adjustments to speed up floppy	
char media;		    /* 0,  1,2,3,....,158, 159 becomes	 
trknum track;		    /* 0,159,1,158,...,79, 80  so fewer steps	
{
    if(media==MINItype)
    {
	if(track & 0x01)
	    track = 159 - (track/2);
	else
	    track /=2;
    }
    return(track);
} ****************************** */


/* ************* USER I/O, STRING MANIPULATION AND SO FORTH **************** */

askdrive()	/* Get a logical drive name from the user */
{
    char a;

    for (a=upin(); seldsk(a) == FALSE; a=upin())
	printf("\nNo such logical drive on this system. Try again: ");
    restore();
    return(a);
} /* askdrive (general input for drive letters) */


#asm
	public diffstr_     ;  compare two buffers, return true if match
diffstr_:		    ;	 (like strncmp but doesn`t stop on null
	lxi	H,2	    ;	  and only returns 0 (match) or 1 (no match)
	dad	SP	    ; pull arguments off the stack
	push	B	    ; you always have to preserve BC however
	mov	E,M
	inx	H	    ; first argument is address of a buffer
	mov	D,M	    ; first arg saved, we`ll get it soon
	push	D

	inx	H	    ; second argument into DE
	mov	E,M	    ; (the other buffer address)
	inx	H
	mov	D,M
	inx	H
	mov	C,M	    ; third argument (len) into BC
	inx	H
	mov	B,M
	xchg		    ; buffer 2 into HL
	pop	D	    ; buffer 1 into D

.lp:	mov	A,B	    ; BC is the loop counter
	ora	C
	jz	.yep
	ldax	D
	cmp	M
	jnz	.nope	    ; ah, ha! a byte that doesn`t match up.
	inx	H	    ; here we differ from strncmp in that we
	inx	D	    ; don`t quit if accum holds a null here.
	dcx	B
	jmp	.lp	    ; jump around and loop again until done.

.yep:	pop	B
	lxi	H,0	    ; 0 for a match.
	ret

.nope:	pop	B	    ; we won`t discriminate according to which
	lxi	H,1	    ; argument is larger, we just set a 1 if
	mov	A,L	    ; they`re different.
	ora	A
	ret

#endasm


yorn()		    /* get a Y or N response */
{
    char a;
    while (((a=upin()) != 'Y') && (a != 'N'))
	printf("\nRe-enter Y or N only: ");
    return(a);
} /* yorn */


upin()		    /* get a character and make it upper case */
{
    char a;
    a = toupper(bdos(1,0));
    if(a==3) out();
    return(a);
} /* upin */


/* *********** BIOS INTERFACES, SOMETIMES W/ A LITTLE TRANSLATION ********** */

/* The following functions are defined as macros near the top of the program:

seldsk(d)
settrk(track)
setsect(sect)
setdma(loc)
setbyt(len)
dread()
dwrite()

*/
     
char *cpmmap(drive)
char drive;
{
    int rethl;
    char *cpmdpb;

    seldsk(drive);
    rethl = bioshl(b_CPMmap,0,0);
    cpmdpb = rethl+1;
    restore();
    return(cpmdpb);
} /* call BIOS cpmmap and return ptr to real CP/M DPB */


lock(string)	/* make a lockstring in the form BIOS wants it */
char *string;
{
    char lockst[13];
    char **plockst = LOCKPTR;
    char *result = LOCKRES;
    int i;

    for(i=0; i < 13; i++)
	lockst[i] = '\0';

    for(i=0; (i < 12) && (lockst[i+1] = string[i]); i++)
	;

    lockst[0] = i;
				/* store pointer in 4A, get result in 49h */
    *plockst = &lockst;
    bios(b_lock,0,0);
    return(*result == LOCKOK);

} /* BIOS net lock */


restore()		/* restore origina
 disk selection */
{
    char *logdsk = LOGDSK;
    seldsk((*logdsk & 0x0F) + 'A');	   /* restore disk select */
}

/* *************************** DEBUGGING HELP ***************************** */

debug(ac,av)
int ac;
char *av[];
{
    unsigned main(), size_death(), lock_death(), bad_track_msg();
    unsigned askflop(), askhard(), ask_which(), oktocopy(), oktorepeat();
    unsigned fill_in_params();
    unsigned disk_to_disk(), move_block(), xfer_trk();/* track_xlate();*/
    unsigned askdrive(), diffstr(), yorn(), upin();
    unsigned cpmmap(), lock();

    if(((ac == 2) || (ac == 3)) && (strcmp(av[1],"DEBUG") == 0))
    {
	printf("\n\nMINICOPY vers. %d.%d%c  FUNCTIONS AND GLOBAL VARIABLES",
		version,revision,patch);
	printf("\n");
	printf("\n       FUNCTIONS:");
	printf("\nmain            %x",main);
	printf("\nsize_death      %x",size_death); 
	printf("\nlock_death      %x",lock_death);
	printf("\naskflop         %x",askflop); 
	printf("\naskhard         %x",askhard); 
	printf("\nask_which       %x",ask_which); 
	printf("\noktocopy        %x",oktocopy); 
	printf("\noktorepeat      %x",oktorepeat); 
	printf("\nbad_track_msg   %x",bad_track_msg); 
	printf("\nfill_in_params  %x",fill_in_params); 
	printf("\ndisk_to_disk    %x",disk_to_disk);
	printf("\nmove_block      %x",move_block); 
	printf("\nxfer_trk        %x",xfer_trk); 
/*	printf("\ntrack_xlate     %x",track_xlate);  */
	printf("\naskdrive        %x",askdrive); 
	printf("\ndiffstr         %x",diffstr); 
	printf("\nyorn            %x",yorn); 
	printf("\nupin            %x",upin); 
	printf("\ncpmmap          %x",cpmmap); 
	printf("\nlock            %x",lock);
	printf("\ndebug           %x",debug); 

	printf("\n\n       GLOBAL VARIABLES:");
	printf("\nflop_dpb        %x  (pointer to struct)",&flop_dpb);
	printf("\nhard_dpb        %x  (pointer to struct)",&hard_dpb);
	printf("\nflop_info       %x  (struct)",&flop_info);
	printf("\nhard_info       %x  (struct)",&hard_info);
	printf("\nbigbuf          %x  (array of char)",bigbuf);
	printf("\nverbuf          %x  (array of char)",verbuf);
	printf("\n");

	if((ac == 3) && (strcmp(av[2],"EXIT") == 0))
	   out();			    /* Output might be redirected  */
					     /* in which case we can't wait */
	else				     /* for control-C or anything.  */
	   printf("\nThis program doesn't understand '%s'.\n",av[2]);

    } /* if there was a command and it was "DEBUG" */
    else
    {
	printf("\nThe only command options recognized are 'DEBUG'");
	printf(" or 'DEBUG EXIT'.");
    }
} /* debug */
se
    {
	printf("\nThe only command options recognized are 'DEBUG'");
	printf(" or 'DEBUG EXIT'.")