/*
 * DD.C - Copyright (c) - George Farris, 2012
 * 
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *       
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *    
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA 02110-1301, USA.
 * 
 *	Version History: 
 * 	1.0	- 2012/01/01 - Initial version
 *  1.1 - 2012/01/05 - Dig drive letter out of filename so it uses the
 * 					   the correct drive when doing a bisoh(SELDSK).
 * 					 - Add version and date to help.
 *  1.2 - 2012/01/13 - Added serial port support which can be used with
 *                     Dwight's H89LDR program or h8trans/h8clxfer.
 * 
 */

#include <stdio.h>

#define VERSION		"1.2"
#define DATE		"2012/01/13"

/* print all debugging statements */
#define DEBUG		FALSE
#define DEBUG1		FALSE

/* Definitions to make minor language modifications to C. */
#define short char		/* Short is not supported directly */

/* BIOS Calls */
#define	HOME		8
#define	SELDSK		9	/* c=drv, A:=0 etc, hl=0 is error on return */
#define	SETTRK		10
#define	SETSEC		11
#define	SETDMA		12
#define DREAD		13	/* C=1 write now, return in A, 0 if ok */
#define DWRITE		14	/* Returns A=0 if ok */
#define SECTRN		16	/* Sector translate */
#define FLUSH		24	/* Returns A=0 if ok, 1 error, 2 R/O. */

/* BDOS calls */
#define GETDRV		25 	/* Returns current working drive */
#define GETLVEC 	24	/* Get Logged In Vector) */
#define SETDISK 	14	/* Set (Select) Disk */
#define GETDPARM 	31	/* Get Disk Parameter Block address */

/* Serial port defs */
#define SERPORT0	0xD8 /* 330Q */
#define SERPORT1	0xE0 /* 340Q */
#define SERPORT2	0xE8 /* 350Q */
#define DVH			0x01
#define LCNTL		0x03
#define MCNTL		0x04	
#define LSTAT		0x05
#define READY		0x01 /* status when char waiting in tx buffer */
#define TRKSIZE		2560 /* track size of H17 100k drive */

/* Disk drive info */
#define	PRNTMAP		1
#define NUMDRVS		7	/* max number of drives supported */
#define H17			17
#define	H37			37
#define H67			67

#define RD_WAIT()	while ((inp(port+LSTAT) & 0x0F) != READY) ;
#define WR_WAIT()	while ((inp(port+LSTAT) & 0x60) != 60) ;

char ser_buf[SECSIZ]; /* serial buffer, size as sector in stdio.h */
char trk_buf[TRKSIZE];

/* Struct that holds valid drive letter to cooresponding number */
/* QSBIOS supports 4 hard and 3 floppy drives. */
struct _vdrvs
{
	char letter;
	unsigned number;
	int dtype;
} vdrvs[NUMDRVS - 1];

	
/*  disk request parameters for BIOS-level Read/Writes */
struct _drb
{
	short dr_disk;		/* logical disk A = 0, B = 1... */
	unsigned dr_track;	/* track (for SETTRK) */
	unsigned dr_sector;	/* sector (for SETSEC) */
	char *dr_buffer;	/* buffer address (for SETDMA) */ 
};


/*==============================================================
 * setserial()  Set the serial port baud rate, parity etc
 *   in			port - serial port I/O address
 * 				rate - Baud rate to set port to
 *   returns	void
 * 
 * These settings are used by the h8trans, h8clxfer
 * or Dwight Elvey's H89LDR program.
 *==============================================================
 */
setserial(port,rate)
char port;
char rate;
{
	char junk;
	int r[8];
	
	initw(r, "9600,4800,0,2400,0,0,0,1200");
	if (rate == 0)
		rate = 0x0C; /* 9600 by default */
	printf("Setting serial port [%x] parameters [%d,8N2]...\n",port,r[(rate/12)-1]);	
	outp(port+LCNTL,0x0); 	/*line control */
	outp(port,0x0);		/* No interrupts */
	outp(port+MCNTL,0x0);	/* Init modem control */
	outp(port+LCNTL,0x80);
	outp(port,rate);		/* 0C=9600,18=4800,30=2400,60=1200 */
	outp(port+DVH,0x0);
	outp(port+LCNTL,0x07);	/* 8 bit, 2 stops */
	junk = inp(port+LSTAT);		/* clear junk from status */
	junk = inp(port);				/* clear junk from rx */
}

/*==============================================================
 * rxtrack() - 	Receive one TRKSIZE worth of data
 *   in			port - serial port I/O address
 *   returns	void
 *==============================================================
 */
VOID rxtrack(port, tbuf)
char port;
char *tbuf;
{
	char *p;
	int len;
	
	p = tbuf;
	
	for (len=0; len<TRKSIZE; len++) /* 2560 bytes */
	{
		RD_WAIT()
		*p++ = inp(port);
	}
}

/*==============================================================
 * txtrack() - Send one TRKSIZE worth of data
 *   in			port - serial port I/O address
 *   returns	void
 *============================================================
 */
VOID txtrack(port, tbuf)
char port;
char *tbuf;
{
	char *p;
	int len;
	
	p = tbuf;
	
	for (len=0; len<TRKSIZE; len++)
	{
		WR_WAIT()
		outp(port,*p++);
	}
}


/*==============================================================
 * waitforcmd() - Wait for char from serial port
 *   in			port - serial port I/O address
 *   returns	c - the character from the port
 *============================================================
 */
char waitforcmd(port)
char port;
{
	char c;

	RD_WAIT()
	c = inp(port);
	#if DEBUG
		putchar(c);
	#endif
	return(c);
}

/*==============================================================
 * getvol() - Gets the volume number - not used for CP/M but we 
 * 			  have it here for compatibility with H89LDR
 *   in			port - serial port I/O address
 *   returns	The volume number
 *============================================================
 */
getvol(port)
char port;
{
	char vol;
	
	outp(port,0x00); /* vol not used in CP/M */
	WR_WAIT()
	putchar('T');
	outp(port,'T');

	RD_WAIT()
	vol = inp(port); /*	handshake */
	if (vol != 'V') {
		outp(port,'v');
	} else {
		outp(port,'V');
	}
	putchar('V');
	putchar('\n');
}



/*==============================================================
 * setvol() - Sets the volume number - not used for CP/M but we 
 * 			  have it here for compatibility with H89LDR
 *   in			port - serial port I/O address
 *   returns	The handshake character 'V'
 *============================================================
 */
setvol(port)
char port;
{
	char vol;
	
	vol = inp(port); /*	handshake */
	if (vol == 'V') {
		RD_WAIT()
		vol = inp(port);
	} else {
		outp(port,'v');
		return;
	}
	putchar('V');
	putchar(vol);
	putchar('\n');
	outp(port,'V');
}


/*==============================================================
 * setintlve() - Sets the interleave on the drive
 *   in			port - serial port I/O address
 *   returns	The handshake character 'I'
 *============================================================
 */
setintlve(port)
char port;
{
	char intlv;
	
	intlv = inp(port);
	if ( intlv == 'I') {
/*		while ((inp(port+LSTAT) & 0x0F) != READY)
			; */
		intlv = inp(port);
	}

	putchar('I');
	putchar(intlv);
	putchar('\n');
	outp(port,'I');
}


/*==============================================================
 * rd_disk() - read disk via BIOS call
 *   in			drb - disk request block
 *   returns	0 if error occurs
 * 				1 if data available
 * 
 * This function uses the parameters previously setup in the
 * incoming request block, and using the BIOS directly,
 * executes the disk read.
 *==============================================================
 */
rd_disk(drb)
struct _drb *drb;	/* disk request block (disk, track, sector, buffer */
{
	if (!set_disk(drb))	/* call SELDSK, SETTRK, SETSEC */
		return 0;	/* if SELDSK fails, indicate no data available */

	printf("Track %d - sector %02d", drb->dr_track, drb->dr_sector);
	putchar(0x0D); 

	if (bios(DREAD))	/* execute BIOS write */
		return 0; 		/* 0 for ERROR */

	return 1;		/* indicate data available */
}

/*==============================================================
 * wr_disk() - write disk via BIOS call
 *   in			drb - disk request block
 *   returns	0 if successful
 * 				1 if error occurs
 * 
 * This function uses the parameters previously setup in the
 * incoming request block, and using the BIOS directly,
 * executes the disk write.
 *==============================================================
 */
wr_disk(drb)
struct _drb *drb;	/* disk request block (disk, track, sector, buffer */
{
	if (!set_disk(drb))	/* call SELDSK, SETTRK, SETSEC */
		return 0;	/* if SELDSK fails, indicate no data available */

	printf("Track %d - sector %02d", drb->dr_track, drb->dr_sector);
	putchar(0x0D); 

	if (bios(DWRITE))	/* execute BIOS write */
		return 1; 

	return 0;		/* indicate data available */
}


/*==============================================================
 * set_disk() - Setup track, sector, buffer for BIOS I/O
 *   in			drb - disk request block
 *   returns	0 if error occurs
 * 				1 if successful
 * 
 * This function sets up the BIOS variables in anticipation of 
 * a subsequent disk read or write. 
 *==============================================================
 */
short set_disk(drb)
struct _drb *drb;	/* disk request block (disk, track, sector, buffer) */
{
	short **skewtab;	/* skewtab -> disk parameter header -> skewtable */

	if ( !(skewtab = biosh(SELDSK,drb->dr_disk,0xFFFF)) )
	{
		printf("SELDSK Failed...\n");
		return 0;	/*  invalid disk  */
	}

	bios(SETTRK,drb->dr_track);		/* set track */
	bios(SETSEC,drb->dr_sector);	/* set sector */
	bios(SETDMA,drb->dr_buffer);	/* set buffer address */

	return 1;		/* indicate no problems */
}


/*==============================================================
 * drv_map() - Displays a map of all the drives currently active.
 *   in			display - if screen display is required.
 *   returns 	void
 *==============================================================
 */
drv_map(display)
char display;
{
	unsigned disk;
	unsigned adisks;	/* bit map for active disks */
	char c;
	char **dph;		/* disk parameter header */
	
	if (display)
		printf("CP/M Drive map with number and type...\n");
	adisks = 0x007F;	/* assume all disks to search */
	/* Make calls to the BIOS SELDSK routine to make sure that
	all of the active disk drives indeed do have disk tables
	for them in the BIOS. If they don't, turn off the corresponding
	bits in the bit map. */

	/* search from A: to G: */
	for (disk = 0; disk < 7; disk++)
	{
		if ( !((1 << disk) & adisks) )	/* avoid unnecessary selects */
			continue;
		
		dph = biosh(SELDSK,disk);  /* BDS C seems not to like putting this inside an if() */
		if (dph == 0)	/* make BIOS SELDSK call */		{			/* returns 0 if invalid disk */
			/* turn OFF corresponding bit in mask
			by ANDing it with bit mask having
			all the other bits set = 1. */
			adisks &= ((1 << disk) ^ 0x007F);
		} else {
			vdrvs[disk].number = disk;
			vdrvs[disk].dtype = drvtype(dph);
			if (display)
			{
				if (vdrvs[disk].dtype != 0)
					printf("%c: - %x - H%d Drive\n",vdrvs[disk].letter,vdrvs[disk].number,vdrvs[disk].dtype);
				else
					printf("%c: - %x - Not mounted\n",vdrvs[disk].letter,vdrvs[disk].number);
			}
		}
	}
	#if DEBUG		
		printf("Disk map, adisks -> %x\n", adisks);
	#endif
	if (display)
		printf("\nNOTE: Drive may show \"not mounted\" or not at all if it \nhasn't been used since boot.\n");
}


/*==============================================================
 * drvtype() - Tries to dig out the type of drive based on the
 *             size of the sector in the CP/M DPB
 *   in			dph - pointer to the DPH.
 *   returns 	Drive type int.
 *==============================================================
 */
char drvtype(dph)
char **dph;
{
	int d;
	
	d = **(dph+5); /* first byte of the DPB */
	
	#if DEBUG
		printf("DPH->%x  DPB->SECTORS %x\n",*dph,d);
	#endif
	
	if (d == 20)
		return(H17);
	else if (d == 32)
		return(H37);
	else if (d == 64)
		return(H67);
	else
		return(0);
}


/*==============================================================
 * getdrvnum() - Return the drive number
 *   in			drv - the drive letter
 *   returns	unsigned drive number
 * 
 * Returns the drive number used by CP/M associated with the 
 * cooresponding drive letter.  CP/M BIOS calls need an unsigned
 * drive number , ususally in BC registers.
 * ============================================================*/
unsigned getdrvnum(drv)
char drv;
{
	char i;
	
	drv_map(0);	/* get the drive map, no display */
	for (i=0; i<NUMDRVS; i++)
	{
		if (vdrvs[i].letter == drv)
			return(vdrvs[i].number);
	}
	return (ERROR); 
}


/*==============================================================
 * from_drv() - Gets image from drive and writes to file or port
 *              See h8clxfer.py for comm details.
 *   in			fd 	- File descripter of open disk file 
 * 				drv	- Drive number of diskette to write to
 * 				port - Serial port to use, FALSE if none.
 * 				cwd - Current working drive for open file (fd)
 *   returns	ERROR
 *==============================================================
 */
from_drv(fd,drv,port,cwd)
int fd;
unsigned drv;
char port;
char cwd;
{
	unsigned i,j,k;
	unsigned offset;
	char buffer[SECSIZ], trkbuf[TRKSIZE];
	struct _drb drb;		/* disk request (for BIOS read/write) */
	int nbl;
	char cmd;
	
	#if DEBUG
		printf("Clear buffer %x \n", buffer);
		printf("Drive is %x...\n",drv);
	#endif
	if (port)
	{	
		#if DEBUG
			printf("Polling port %x for start \n", port);
		#endif
		
		printf("Waiting for Receive command from serial link...\n");
		cmd = 'X';
		while (cmd != 'R')
		{
			cmd = waitforcmd(port);
			#if DEBUG
				printf("Got -> %c\n", cmd);
			#endif
		
			switch (cmd)
			{
				case 'R':
					break;
				case 'T':	
					getvol(port);		/* get the volume number */
					break;
			}
		}
	}
	
	#if DEBUG
		printf("Beginning loop %x \n", buffer);
	#endif
	for (i=0; i<40; i++) /* for 40 tracks */
	{
		if (port)
		{
			RD_WAIT()			/* local wait loop is slightly faster */
			cmd = inp(port);	/* than calling waitforcmd() */
			if ( cmd != 'R')
			{
				putchar(cmd);
				return;
			}
		}

		offset = 0; /* trk_buf index offset */
		for (j=1; j<=20; j++) /* 20 128byte sectors per track on H17 drive */
		{	
			drb.dr_disk = drv;	/* set up disk request */
			drb.dr_track = i;
			drb.dr_sector = j;
			drb.dr_buffer = buffer;
		
			if (!rd_disk(&drb))		/* issue read command */
				return ERROR;	 /* indicate error - no data available */
			
			movmem(buffer, &trkbuf[offset], SECSIZ);
			offset = offset + SECSIZ;	
		}

		#if DEBUG1
			for (k=0; k<128; k++)
				printf("%02x ",trkbuf[k]);
			putchar('\n');
		#endif

		if (port)
		{
			txtrack(port, trkbuf); /* send a track  */
			outp(port, 'R');
		} else {
			/* must select the drive fd is writing to */
			biosh(SELDSK,cwd); 
		
			nbl = write(fd,trkbuf,20);
			if (nbl == ERROR)
			{
				printf("ERROR: writing disk file, nbl -> %d ...\n",nbl);
				exit(1);
			}
		}
	}
}


/*==============================================================
 * to_drv() - Gets image from file or port and writes to drive.
 *   in		fd 	 - File descripter of open disk file.
 * 			drv	 - Drive number of diskette to write to.
 * 			port - Serial port to use, FALSE if none.
 * 			cwd  - Current working drive for open file (fd).
 *==============================================================
 */
to_drv(fd,drv,port,cwd)
int fd;
unsigned drv;
char port;
char cwd;
{
	unsigned i,j;
	unsigned offset;
	char buffer[SECSIZ], trkbuf[TRKSIZE];
	struct _drb drb;		/* disk request (for BIOS read/write) */
	int nbl;
	int k;
	char cmd;

	#if DEBUG
		printf("Clear buffer %x \n", buffer);
		printf("Drive is %x...\n",drv);
	#endif

	drb.dr_disk = drv;	/* set up disk request drive*/

	if (port)
	{
		printf("Waiting for Write command from serial link...\n");
		cmd = 'X';  /* init cmd to anything but W,V or I */
		while (cmd != 'W')
		{
			cmd = waitforcmd(port);
		
			#if DEBUG
				printf("Got -> %c\n", cmd);
			#endif
		
			switch (cmd)
			{
				case 'W':
					break;
				case 'V':	
					setvol(port);		/* set the volume number */
					break;
				case 'I':	
					setintlve(port);	/* set the interleave */
					break;
			}
		}
	}


	for (i=0; i<40; i++) /* for 40 tracks */
	{
		if (port)
		{
			RD_WAIT()			/* local wait loop is slightly faster */
			cmd = inp(port);	/* than calling waitforcmd() */
			if ( cmd == 'W')
				rxtrack(port, trkbuf); /* get a track and store in buffer */
			else {
				putchar(cmd);
				return;
			}
		} else {
			biosh(SELDSK,cwd);
			
			nbl = read(fd, trkbuf, 20);
			if (nbl <=0 )
			{
				printf("ERROR: reading disk file, nbl -> %d ...\n",nbl);
				exit(1);
			}
		}

		#if DEBUG1
			for (k=0; k<128; k++)
				printf("%02x ",trkbuf[k]);
			putchar('\n');
		#endif
		
		offset = 0; /* trkbuf index offset */
		
		for (j=1; j<=20; j++) /* 20 128byte sectors per track on H17 drive */
		{	
			movmem(&trkbuf[offset], buffer, SECSIZ);
			offset = offset + SECSIZ;	
		
			drb.dr_track = i;  /* set up disk request track,sector etc */
			drb.dr_sector = j;
			drb.dr_buffer = buffer;
		
			if (wr_disk(&drb))		/* issue write command */
				return -1;	 /* indicate error - no data available */
		}
		
		if (port)
		{
			outp(port, 'W');
		}
	}
	
}

/*==============================================================
 * do_copy() - Do the copy in the correct direction
 *   in 	 todrv - read or write image to floppy. 
 *           drv   - drive letter. 
 *           fname - file name or serial port.
 * 			 baud  - baud rate to set port to.
 *   returns void
 *==============================================================
 */
do_copy(todrv,drv,fname,baud)
char todrv;
char drv;
char *fname;
char baud;
{
	int fd;
	int e;
	int port;
	char i,j;
	char nblocks;
	char buffer[TRKSIZE];
	unsigned drvnum;
	char cwd;
	
	port = FALSE;
	fd = FALSE;
	
	if (strcmp(fname, "TTYS0:") == 0)
		port = SERPORT0;
	if (strcmp(fname, "TTYS1:") == 0)
		port = SERPORT1;	
	if (strcmp(fname, "TTYS2:") == 0)
		port = SERPORT2;


	if (fname[1] == ':' )
		cwd = getdrvnum(fname[0]);
	else
		cwd = bdos(GETDRV);
	
	if (todrv)
	{
		printf("Imaging drive [%c:] from file [%s]...\n", drv, fname);
		
		if (port)
			setserial(port,baud);
		else {
			fd = open(fname, 0);
			if (fd == ERROR)
			{
				printf("ERROR: Can't open file for reading...\n");
				exit(1);
			}
		} 
		
		drvnum = getdrvnum(drv); /* if this is first drive select crashes */
		if (drvnum == ERROR)
		{
			printf("Invalid drive selected [%c - %x] or door open...\n",drv,drvnum);
			exit(1);
		}
		
		e = to_drv(fd, drvnum, port, cwd);
		
		if (e == ERROR)
		{
			printf("ERROR: Problem writing to drive...\n");
			exit(1);
		}			
		close(fd);
		
	} else {
		printf("Creating image [%s] from drive [%c:]...\n",fname, drv);
		
		if (port)
			setserial(port,baud);
		else {
			fd = creat(fname);
			if (fd == ERROR)
			{
				printf("ERROR: Can't open file for writing...\n");
				exit(1);
			}
		}
		drvnum = getdrvnum(drv);
		if (drvnum == ERROR)
		{
			printf("Invalid drive selected or door open...\n");
			exit(1);
		}

		e = from_drv(fd, drvnum, port, cwd);
		
		if (e == ERROR)
		{
			printf("ERROR: Problem reading from drive...\n");
			exit(1);
		}

		close(fd);
	}
}


/*==============================================================
 * exists() - Check for valid and existing file name
 *   in		 fname - Name of file to check if exists.
 *   returns TRUE if exists, FALSE otherwise
 *==============================================================
 */
exists(fname)
char *fname;
{
	int fd;
	
	fd = open(fname, 0);
	if (fd == ERROR) {
		close(fd);
		return(1);
	} else
		return(ERROR); /*error */
}


/*==============================================================
 * main() - Yup we start here
 * 
 * 
 *==============================================================
 */
main (argc, argv)
int argc; 
char *argv[]; 
{
	char i,j,k;		/* loop vars */		
	char fname[16]; /* drive and name */
	char buffer[SECSIZ];	
	char cmd[4];	/* command (left of == sign */
	char arg[16];	/* command argument (right of = sign) */
	char drv;		/* drive letter to image to or from */
	char todrv;		/* copy to a drive or read from a drive */
	int fd, nbl;	/* file descriptor and block count */
	unsigned drive;
	char validdrv;	/* true if a valid drive is input */
	int tmp;		/* scratch var */
	char port;		/* serial port to use */
	char brate;		/* baud rate in hex 0C=9600,18=4800,30=2400,60=1200 */

	brate = 0;
	validdrv = FALSE;
	todrv = FALSE;
	/* initialize drive letters */
	for (i=0; i<7; i++)
	{
		vdrvs[i].letter = 'A'+i;
		vdrvs[i].number = 0;
		vdrvs[i].dtype = 0;		
	}

	if (argc == 1)
	{
		printf("Disk dump for CP/M - Version %s - %s...\n", VERSION, DATE);
		printf("dd - Is used for reading and writing H8D disk images to and \n");
		printf("     from an H17, 100k, hard sector diskette drive.\n");
		printf("     It is modelled on the UNIX utility of the same name.\n\n");
		printf("Usage:\n");
		printf("     dd map - Print map of disk drives - must have FORMATTED floppy inserted.\n");
		printf("     dd if=<file|drive|port> of=<drive|file|port> - copies images.\n\n");
		printf("Example:\n");
		printf("     dd if=e: of=cpmboot.h8d - Makes image from E: drive called cpmboot.h8d\n");
		printf("     dd if=mydisk.h8d of=d:  - Images D: drive from file mydisk.h8d.\n");
		printf("     dd if=ttys1 of=a:       - Images A: drive from serial port 340Q.\n\n");
		printf("Ports:\n");
		printf("     ttys0: - H8-4 serial port at 330Q | 0xD8\n");		
		printf("     ttys1: - H8-4 serial port at 340Q | 0xE0\n");		
		printf("     ttys2: - H8-4 serial port at 350Q | 0xE8\n");				
		exit(0);
	}
	
	for (i=1; i < argc; i++ )
	{
		/* get the command */
		for (j=0; j<3; j++)
		{
			cmd[j] = argv[i][j];
		}
		cmd[3] = '\0';
		
		if (argc > 2)
			strcpy(arg, &argv[i][3]);
		
		if (strcmp(cmd, "MAP" ) == 0)
		{
			drv_map(PRNTMAP);
			exit(0); /* we're done */
		}
		
		/* Get input parameter, file, drive or serial port */
		if (strcmp(cmd, "IF=" ) == 0)
		{
			/* if drive letter */
			if (strlen(arg) == 2 && arg[1] == ':' )
			{
				todrv = FALSE;
				drv = arg[0];
				if (drv < 'A' || drv > 'G')
				{
					printf("ERROR: Drive letter must be between A: and G:...\n"); 
					exit(1);
				}
				validdrv = TRUE;
			} else {
				strcpy(fname, arg);
				if (arg[5] != ':')	/* not a serial port, check if the file exists */
					if (!exists(fname))
					{
						printf("ERROR: file does not exist...\n");
						exit(1);	
					}
			}
		}
		
		/* Get output parameter, file, drive or serial port */
		if (strcmp(cmd, "OF=" ) == 0)
		{
			/* if drive letter */
			if (strlen(arg) == 2 && arg[1] == ':' )
			{
				todrv = TRUE;
				drv = arg[0];
				if (drv < 'A' || drv > 'G')
				{
					printf("ERROR: Drive letter must be between A: and G:...\n"); 
					exit(1);
				}
				validdrv = TRUE;
			} else {
				strcpy(fname, arg);
			}
		}

		/* Change the baud rate of the serial port (8250 divisors)*/
		if (strcmp(cmd, "BR=" ) == 0)
		{
			tmp = atoi(arg);
			switch (tmp)
			{
				case 9600:
					brate = 0x0c;
					break;
				case 4800:
					brate = 0x18;
					break;
				case 2400:
					brate = 0x30;
					break;
				case 1200:
					brate = 0x60;
					break;
				default:
					printf("ERROR: Not a valid baud rate...\n");
					printf("       Must be 9600, 4800, 2400 or 1200...\n");
					exit(1);
			}
		}
	}
	
	if (validdrv)
		do_copy(todrv, drv, fname, brate);
	else
		printf("Must provide valid drive letter...\n");
}

