/**************************************************/                    
/* PCPIP - CPM to PC-DOS FILE TRANSFER UTILITY    */
/*  Written by Eric Leininger                     */
/*             2647 Welsford Rd.                  */
/*             Columbus Ohio 43221                */
/*                                                */
/* Do whatever you want with this program,        */
/*  except please do not sell it or any parts     */
/*  of it.                                        */
/*                                                */
/* This program does direct calls to BIOS         */
/*  routines and as distributed will work only    */
/*  with Osborne 1 machines.                      */
/*                                                */
/* For those of you who know C well, sorry if     */
/*  some of my techniques are crude. One of these */
/*  days I'll read my manual.                     */
/*                                                */
/* Most of the technical information regarding    */
/*  how this works (i.e. what is the fat I keep   */
/*  talking about) can be found in the IBM DOS    */
/*  manual Appendix C.                            */
/*                                                */
/**************************************************/                    
/* Compile instructions:			  */
/*	-Developed using the C/80 compiler 2.0 	  */
/*	 and the M80 assembler			  */
/*	-Needs to be linked with:		  */
/*	 CLIB.REL - C80 Library			  */
/*	 DUMP.REL - debugging routines		  */
/*	 BISOCALL.REL - BIOS call interface	  */
/*	 PCLIB.REL - miscellaneous routines	  */
/*	           (some specific to PCPIP)	  */
/*	 PRINTF.REL - formatted print routines	  */
/*	 END.REL - end marker			  */
/**************************************************/
/* Change History:				  */
/*     Rev 0 12/29/82 - Original Release	  */
/*     Rev 1  8/09/83 - Added double sided	  */
/*     Rev 1.1 2/3/84 - Forced drive a: on	  */
/*                      IBM to CPM xfer because   */
/*			of problem vith vector    */
/* 	 		version 		  */
/*     Rev 2.0 3/10/84- Added support for DOS 	  */
/*                      2.0 (9 sectors/track)	  */
/**************************************************/
/* Known Problems:				  */
/*     -Does not look at all directory entries on */
/*      a double sided disk (its on the flip side)*/ 
/*     -Will not correctly display the size of the*/
/*	PC-Dos file if over 64K.		  */
/*     -Can create duplicate directory entries on */
/*	the PC-DOS disk.			  */
/*     -BIOS errors are not trapped		  */
/**************************************************/ 		
/* Note undocumented features:			  */
/*	-Entering 'y' before hiting return after  */ 
/*       inserting disks invokes the DEBUG mode.  */
/*	-Entering 's' at menu displays the FAT    */
/*       chain       				  */
/**************************************************/ 

#include PRINTF.H
#define BDOS 5
static char *copybuf;	/*  copy buffer        */ 
static char *dirbuf;	/*  point to pc dir    */
static char *fat;	/*  point to file      */
                 	/*   allocation table  */

static char debug;	/*  y for debug = yes  */
static int  f1,ferror;	/*  file flags         */

static char disktyp;   	/*  s=single; d=double */
static int  secclus;    /*  sectors/cluster    */
static int  cluslen;    /*  cluster length     */
static int  secoff;     /*  sector offset of   */
                        /*   first cluster           */
static int  dostyp;    	/*  1=1.1; 2=2.0       */
static int  sectrk;     /*  sectors/track      */
static int  fatsec;    	/*  first fat sector   */
static int  dirsec;    	/*  first dir sector   */
static int  dirnum;    	/*  number of directory*/
                       	/*   entries           */
static int  maxfat;    	/*  highest cluster    */

static int  side,track,sector;
                        /* these always refer  */
                        /* to the osborne disk */
                        /* (I think)           */

main(argc,argv)
int argc; char *argv[];
{	static int f2,c,d;
        static int dev;
/*	static char *cpmver,cpmvec; */
 
	printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); 
	printf("Program PCPIP  - Osborne Rev 2.0\n\n\n"); 
	printf("CPM / PC-DOS file transfer utility\n");
	printf("   Developed by Eric Leininger\n\n\n");

	printf("Insert CPM disk in drive A,\n");
	printf("       PC disk in drive B \n\n");
	printf("Hit return to continue...\n");
	c = getone();
	debug = c;       /* if "y" then debug on */

/*      Get Buffer Space (total of 7K)             */
	copybuf = alloc(2560); /* clusters in/out  */
        dirbuf = alloc(3584);  /* pc-dos directory */
        fat = alloc(1024);     /* pc-dos fat       */

/*	Read Directory on PC disk   */
	pcdirin();

/*      Option Menu                 */
	while (c != 'e')
	   {
           printf("\nSource for copy? (c=CPM, p=PC, r=reset, e=EXIT)\n");	
	   c = getone();
	
/*         Search and copy to CPM            */
	   if (c == 'p')
	      srchdir();

/*         Copy to PC                       */
	   if (c == 'c') {
	      oztopc();
	      c = 'c';
	   }	

/*         Reset for disk change            */
	   if (c == 'r')
	      {
       	      printf("Change disks and hit return to continue...\n");
	      c = getone();
	      debug = c;	
	      pcdirin();
	      }

/*         List cluster chain                */
           if (c == 's')
	      lstch();
	   }

	printf("\n\n*** End of PCPIP ***\n\n\n");
	exit();


}


pcdirin()
/* read fat, determine disk type, and then read   */
/* the directory.                                 */  
{	int dev,cluster;
	char *secadd;	

	reset();

/*    Read File Allocation Table (first copy)   */
        foò (sectoò ½ 4» sectoò <½ 11» sectoò++)
		{ dev = 1;
		seldev(dev);	
		track = 0;
		settrk(track);
		secadd= fat+((sector-4)*128);
		setdma(secadd);
		setsec(sector);
		cpmread();
		}

/*     determine disk type                      */
/*       (single or double, 8 or 9 tracks)      */
	disktyp = '?';
	cluster = getfat(0,fat);
	if (cluster == 0x0FFE)
	  {
          printf("..Single sided diskette, 8 sectors/track..\n");
	  disktyp = 's';
          dostyp = 1;
	  cluslen = 512;
          dirsec = 12;
          dirnum = 64;
          maxfat = 314;
          sectrk = 8;
          secclus = 1;
          secoff = 7;
          }
	if (cluster == 0x0FFF)
	  {
          printf("..Double sided diskette, 8 sectors/track..\n\n");
	  printf("  Note that PC to CPM file transfers will\n");
	  printf("  fail if any data is on side 2. Also only\n");
	  printf("  80 of the 112 directory entries are \n");
	  printf("  available.\n");
	  disktyp = 'd';
          dostyp = 1;
	  cluslen = 1024;
          dirsec = 12;
          dirnum = 80;
          maxfat = 316;
          sectrk = 8;
          secclus = 2;
          secoff = 10;
	  }

	if (cluster == 0x0FFC)
	  {
          printf("..Single sided diskette, 9 sectors/track..\n");
	  printf("  Note that only the root directory\n");
	  printf("  is available.\n");
	  disktyp = 's';
          dostyp = 2;
	  cluslen = 512;
          dirsec = 20;
          dirnum = 64;
          maxfat = 352;
          sectrk = 9;
          secclus = 1;
          secoff = 9;
	  }
	if (cluster == 0x0FFD)
	  {
          printf("..Double sided diskette, 9 sectors/track..\n\n");
	  printf("  Note that PC to CPM file transfers will\n");
	  printf("  fail if any data is on side 2, only 64\n");
	  printf("  of the 112 root directory entries are \n");
	  printf("  available (sub-directories are not\n");
          printf("  supported).\n");
	  disktyp = 'd';
          dostyp = 2;
	  cluslen = 1024;
          dirsec = 20;
          dirnum = 64;
          maxfat = 355;
          sectrk = 9;
          secclus = 2;
          secoff = 12;
	  }
	if (disktyp == '?')
	  {
	  printf("*** ERROR - UNRECOGNIZED DISK ***\n");
	  printf("  First fat entry = %x\n",cluster);
	  exit();
	  }

/*    Read directory sectors                     */
	printf("\nPlease wait - reading IBM directory...");
        foò (sectoò ½ dirsec; sectoò <½ dirsec - 1 + dirnum/4; sectoò++)
		{ dev = 1;
		seldev(dev);	
		track = 0;
		settrk(track);
		secadd= dirbuf+((sector-dirsec)*128);
		setdma(secadd);
		setsec(sector);
		cpmread();
		}
	printf("  Thank you.\n");
	
	return;
}

srchdir()
/*    Decode directory                          */
{	int entry;
	int found;
	char used,erased,dir,never,*entryadd;	

	erased = 0xE5;
	dir = 0x2E;
	never = 0x00;
	for (entry = 0; entry <= dirnum -1; entry++)
		{ entryadd = dirbuf+(entry*32);
		used = *entryadd;
		if (used != erased && used != dir && used != never)
			{found++;
			getdir(found,entryadd);
			}
		}
	return;
}

getdir(found,entryadd)
int found;
char *entryadd;
{
	int bytes,size,cluster,*pint;
	char ans,fname[15],name[9],ext[4];
	bytes = 8;
	getstring(8,name,entryadd);
	entryadd = entryadd + 8;
	getstring(3,ext,entryadd);
 	cname(fname,name,ext);
	pint = entryadd + 20;
	size = *pint;
	pint = entryadd + 18;
	cluster = *pint;
/* need to decode 4 byte size fields */
	printf("\n\nFile = %s ; %d bytes ; %d = 1st cluster\n",fname,size,cluster);
	printf("Copy to disk A:?(y/n)\n");
	ans = getone();
	if (ans == 'y')
		{ pctooz(fname,cluster);
                }
	return;
}


pctooz(name,cluster)
int cluster;
char name[];
/**************************************************/                    
/* PCOZ                                           */
/* Copies the file 'name' to the osborne disk.    */
/* Uses the cluster to begin, then chains through */
/* the FAT for the remaining.                     */
/**************************************************/
{
	int fchan;
	char upline;
	upline = '\013';
	printf("\n\nCopying file %s PC => CPM\n",name);
	ferror = 0;
	fchan = file(name);
	if (ferror != 1)
	   {	
	   printf("  First cluster=%d\n",cluster);
	   while (ferror != 1 && cluster < maxfat)
        	{
		pcread(cluster);
		if (side == 0)
		   {	
		   ferror = write(fchan,copybuf,cluslen);
		   if (ferror != cluslen)
		     {
		     printf("\n\n*** WRITE ERROR, COPY ABORTED ***\n\n");
		     ferror = 1;
		     }
		    }
 
		cluster = getfat(cluster,fat);
		printf("%c  Next cluster=%d     \n",upline,cluster);
		}
           fclose(fchan);
	   }
	return;
}

pcread(cluster)
int cluster;
/**************************************************/                    
/* PCREAD                                         */
/* Reads the cluster requested into the copy      */
/*  buffer.					  */
/**************************************************/
{
	int dev,secr,check,check1;
	char ans,c,*secadd;

	dev = 1;

if (debug == 'y') printf("Reading cluster %d\n",cluster);

/* decode cluster to osborne side, track,         */
/* and sector                                     */
        osbsec(cluster);

        if (side == 1)
         {
	 printf("***OOPS - attempt to read flip side***\n");
	 printf(" ---Cluster skipped, copy continuing--\n"); 
	 printf("Hit return to continue\n");
	 c = getone();
	 }

	if (side != 1)
	 {
         for (secr = sector;secr < sector+cluslen/128;secr++)
           {
if (debug == 'y') printf("Reading Osborne track %d sector %d\n",track,secr);
           seldev(dev);	
	   settrk(track);
	   setsec(secr);
	   secadd = copybuf+(secr-sector)*128;
	   setdma(secadd);
	   cpmread();
	   }
         }

	return;
}

osbsec(cluster)
/* decode cluster number to osborne side, track,  */
/* and sector                                     */
int(cluster);
{
	int logsec,check;

/* find pc-dos logical sector                     */ 
        logsec = cluster - 2;
        logsec = logsec * secclus;
        logsec = logsec + secoff;
/* find pc-dos track and sector                   */
        track = logsec / sectrk;
        sector = logsec - track * sectrk;
/* convert to osborne track and sector            */         
 	sector = sector * 4;
	side = 0;
	if (disktyp == 'd')
          {
	  check = track / 2;
	  if (track != check * 2)
	    side = 1;
	  track = track /2;
	  }

/* return                                        */

	return;

}


lstch()
{      	int sect,nexts,empty;
	empty = 0;
	for (sect = 1;sect < maxfat;sect++)
	  { nexts = getfat(sect,fat);
	    printf("FAT of %d = %d \n",sect,nexts);
	    if(nexts == 0)
	      empty++;
	    if(empty > 35)
	      sect = maxfat;	 
	  }
	return;
}

getnext(cluster)
/* Get the next free cluster  */
int(cluster);
	{
	int i,j,next;
if (debug == 'y') printf("Looking for free cluster at %d\n",cluster);
	next = cluster;
	while (next != 0 && cluster <= maxfat)
	   {
	   next = getfat(cluster,fat);

/* 	Check for flip side       */
           osbsec(cluster);
if (debug == 'y') printf("Cluster %d, side %d \n",cluster,side);
           if (side == 1)
              next = 1;

	   cluster++;
	   }

if (debug == 'y') printf("Found free cluster at %d\n",cluster-1);

	if(next != 0)
	   {
	   printf("***ERROR - PC disk full***\n");
	   ferror = 1;
	   }

	return(cluster-1);
	}




file(fname)
char fname[];
/**************************************************/                    
/* FILE                                           */
/*                                                */
/* This routine opens the file 'fname' and        */
/* the channel number.                            */
/*                                                */
/**************************************************/
{	int i;
	char ans,dname[15];
/*  First, see if file exists                     */
/*	i = fopen(fname,"r");
	if (i > 0)
	   {
	   fclose(i);
	   printf("File exists on drive A:\n");
	   printf("  OK to overwrite? (Y/N)\n");
 	   ans = getone();
	   if (ans != 'y')
	      {
	      ferror = 1;
	      return;			
              }
           } */

/*   Now open for output                          */
/* Force to drive A:                              */
	for ( i = 0; i <= 13; i++)
	  dname[i+2] = fname[i];
	dname[0] = 'A';
	dname[1] = ':';
	i = fopen(fname,"w");
	if (i > 0) return i;
	printf("Can't Open: %s\n",dname);
	ferror = 1;
	return;
} 



oztopc()
	{
	char name[15];
	int i,max;
	max = 14;
	printf("\nOK, CPM to PC file transfer\n");
	printf("Enter file name:");
	istring(&name[0],max);
	ozpc(name);
	printf("...Copy complete\n");
	return;
	}


pcdirout()
/*  Rewrite directory and FAT on PC disk          */
{	int base,dev,fatsize;
	char *secadd;	

/*    Write directory sectors                     */
        foò (sectoò ½ dirsec» sectoò <½ dirsec-1+dirnum/4» sectoò++)
		{ dev = 1;
		seldev(dev);	
		track = 0;
		settrk(track);
		secadd= dirbuf+((sector-dirsec)*128);
		setdma(secadd);
		setsec(sector);
		cpmput();
		}

/*    Write File Allocation Table  (two copies)  */
	fatsize = dostyp * 4;
	for (base = 4; base <= 4 + fatsize;base = base + fatsize)
	   {
           foò (sectoò ½ base» sectoò <½ base-1+fatsize» sectoò++)
	      { dev = 1;
	      seldev(dev);	
	      track = 0;
	      settrk(track);
	      secadd= fat+((sector-base)*128);
	      setdma(secadd);
	      setsec(sector);
	      cpmput();
	      }
	   }

	return;
}

ozpc(name)
char name[];
{	
	int i,fchan,cread;
	int length,cluster,start,next;
	char upline,dname[17];
	upline = '\013';
/* Force name to drive A:                        */
	for (i = 0; i <= 14; i++)
	 dname[i] = name[i];
        if (dname[1] != ':') {
	  for (i = 14; i >= 0; i--)
	    dname[i+2] = dname[i];
	}
	dname[0] = 'A';
	dname[1] = ':';
/* make sure drive is not specified on PC-DOS dir */
	for (i = 0; i <= 14; i++)
	 name[i] = dname[i+2];
	printf("\n\nCopying file %s   (CPM => PC)\n",dname);

	ferror = 0;
	fchan = fopen(dname,"rb");
	if (fchan == 0)
	   {
	   printf("*** Can't open file ***\n");
	   return;
	   }

/* Find first empty cluster                    */
	start = getnext(2);
/* And go to it                               */
	printf("\n");
	next = start;
	length = 0;
	cread = read(fchan,copybuf,cluslen);
	while (cread > 0 && ferror ==0) {
printf("%c Have read %d, cluster = %d, next = %d\n",upline,cread,cluster,next);
		cluster = next;
		next = pcwrite(cluster);
		length = length + cread;	
		cread = read(fchan,copybuf,cluslen);
		}
		 
printf("End of read, last read was %d, last write to %d\n",cread,cluster);

	if (ferror ==0);
  	   {
           pclose(cluster);
	   makedir(name,start,length);

/* rewrite fat and directory */
	   pcdirout();

	   printf("\nCopy complete\n");
	   }

	fclose(fchan);
	return;
} 

makedir(name,start,length)
char name[];
int  start,length;	
	{
	char c;
	int i,entry;
	char used,erased,never,*entryadd,*namadd;	

	int found;
	int bytes,size,*pint;
	char ans,;

	printf("Make PC directory entry...\n");
	printf("   Name = %s\n",name);
	printf("   First Sector = %d\n",start);
	printf("   Length = %d\n",length);

/* First get address of first available directory */
/*  entry                                         */


	erased = 0xE5;
	never = 0x00;
	for (entry = 0; entry <= 63; entry++)
		{ entryadd = dirbuf+(entry*32);
		used = *entryadd;
		if (used == erased || used == never)
			break;
		}

/* Now format new entry                          */

	for (i = 0;i <= 31;i++)
	  *(entryadd + i) = '\0';
	pint = entryadd + 26;
	*pint = start;
	pint = entryadd + 28; 
	*pint = length;

	for (i = 0;i <= 10;i++)
	  *(entryadd + i) = ' ';

	namadd = entryadd;
	for (i = 0;i <= 12; i++)
	   {
	   if (name[i] != '.' && name[i] != '\0')
	      {
	      *namadd = name[i];
	      namadd = namadd +1;
	      }

	   if (name[i] == '.')
	      namadd = entryadd + 8;

	   if (name[i] == '\0')
	      break;

	   }
		
if (debug == 'y') dump(32,entryadd,"New dir entry = ");
	return;
	}


pclose(cluster)
int cluster;
	{
	char c;
	printf("Close PC fat chain...\n");
	printf("   Last Sector = %d\n",cluster);
	pchain(cluster,0XFFF,fat);
	return;
	}

pcwrite(cluster)
int cluster;
/**************************************************/                    
/* PCWRITE                                        */
/* Writes the cluster requested from the copy     */
/*  buffer                                        */
/**************************************************/
{
	int next,dev,secr;
	char ans,*secadd;
if (debug == 'y') printf("Writing cluster %d\n",cluster);

	dev = 1;

	osbsec(cluster);

	for (secr = sector;secr < sector+cluslen/128;secr++)
           {
if (debug == 'y') printf("Writing Osborne track %d sector %d\n",track,secr);
           seldev(dev);	
	   settrk(track);
	   setsec(secr);
	   secadd = copybuf+(secr-sector)*128;
	   setdma(secadd);
	   cpmput();
	   }
	next = getnext(cluster+1);

	pchain(cluster,next,fat);

	return(next);
}

/*  ************* END OF PCPIP.C *************  */
