/*----------------------------------------------------------------------*\ | | | DISK COPY PROGRAM | | ================= | | | | Created: November 4,1982 | | ------- | | | | Last Modified: January 15,1982 | | ------------- | | | | Description: | | ----------- This program will copy all or portions of | | one disk to another compatible disk. | | | | Needed Files: | | ------------ , | | | | Must be linked to the 'C' runtime library. | | ------------------------------------------ | | | | (c) COPYRIGHT DIGITAL RESEARCH 1983 | | ALL RIGHTS RESERVED | | | \*----------------------------------------------------------------------*/ char *copyrt="CP/M-68K(tm), Version 1.1, Copyright 1983, Digital Research"; char *serial="XXXX-0000-654321"; /************************************************************************\ * Here are the Include Files * \************************************************************************/ /********************************/ #include /* Standard I/O library */ /********************************/ /************************************************************************\ * DEFINES for Copy.c * \************************************************************************/ /********************************/ #define ON 1 /* Option is valid for this copy*/ #define OFF 0 /* Option is not valid */ #define BAD 0 /* Illegal mode flag */ #define MATCH 0 /* used by strcmp */ #define SDRIVE 1 /* source drive */ #define DDRIVE 2 /* destination drive */ #define _ALL 0 /* copy whole disk */ #define _BOOT 1 /* copy boot tracks */ #define _FILES 2 /* copy non-boot tracks */ #define _END 3 /* end this program */ #define RDERR 0 /* read error flag */ #define WRERR 1 /* write error flag */ #define NULLB '\0' /* NULL byte */ #define isupper(c) ('A' <= (c) && (c) <= 'Z')/*upper case */ #define islower(c) ('a' <= (c) && (c) <= 'z')/*lower case */ #define tolower(c) (isupper(c) ? ((c)+040):(c))/* */ #define toupper(c) (islower(c) ? ((c)-040):(c))/* */ /********************************/ /************************************************************************\ * GLOBAL definitions * \************************************************************************/ /********************************/ GLOBAL BYTE *choice[] = /* Copy Modes available */ {"all","boot","files","end"}; /* ---------------------- */ /********************************/ GLOBAL BYTE mode; /* Hold the current copy mode */ /********************************/ /* ------- OPTIONS ------- */ GLOBAL BYTE verbose = ON; /* Prompt the user if ON */ GLOBAL BYTE verify = OFF; /* Check newly written track */ /* againist source track. */ /********************************/ /* Prompts........... */ GLOBAL BYTE *msg1 = "Enter SOURCE drive: "; /* SDRIVE */ GLOBAL BYTE *msg2 = "Enter DESTINATION drive: "; /* DDRIVE */ GLOBAL BYTE *msg3 = "READ error ----> "; /* Read error */ GLOBAL BYTE *msg4 = "WRITE error ----> "; /* Write error */ /********************************/ /********************************/ GLOBAL BYTE *bracket = "["; /* Marks beginning of option */ /* indicators on the cmd line */ /********************************/ GLOBAL struct _dpb /* Define disk parm block */ { /*------------------------------*/ WORD spt; /* Logical sectors per track */ BYTE bsh; /* Allocation block shift factor*/ BYTE blm; /* Block mask */ BYTE exm; /* Extent mask */ BYTE dpbjunk; /* Alignment byte */ WORD dsm; /* Total storage of the disk */ WORD drm; /* Total directory entries */ BYTE al0; /* Flag reserved for dir blocks */ BYTE al1; /* Flag reserved for dir blocks */ WORD cks; /* Directory check vector size */ WORD off; /* The number of reserved tracks*/ }; /* */ /********************************/ GLOBAL struct _dph /* Define disk parm header */ { /*------------------------------*/ BYTE *xltp; /* Pointer to sector trans table*/ WORD dphscr[3]; /* Scratchpad values for BDOS */ BYTE *dirbufp; /* Pointer to dir scratchpad */ struct _dpb *dpbp; /* Pointer to disk parm block */ BYTE *csvp; /* Pointer to scratchpad area */ BYTE *alvp; /* Pointer to scratchpad area */ }; /********************************/ EXTERN LONG __BDOS(); /* BDOS Caller in CLIB */ #define bdos(a,b) (struct _dph*)__BDOS(a,b) /* Declare a bogus function */ /********************************/ GLOBAL struct _biospb /* Bios Pararmeter Block */ { /*------------------------------*/ UWORD funcnum; /* Bios function number */ LONG parm1; /* Bios parameter 1 */ LONG parm2; /* Bios parameter 2 */ }; /* */ GLOBAL struct _biospb biospb; /* Define it */ /********************************/ GLOBAL BYTE trkbuf[128*26*6]; /* buffer big enough to hold 6 */ /* tracks of bytes */ GLOBAL BYTE verbuf[128*26*6]; /* buffer for verification opt */ /********************************/ /************************************************************************\ * FUNCTION definitions * \************************************************************************/ /********************************/ UWORD illegal(); /* Decides if the mode is a */ /* legal one. */ /*------------------------------*/ VOID get_mode(); /* If the mode was not there */ /* or an illegal one,get_mode */ /* will print a menu and keep */ /* prompting the user for a */ /* legal mode. */ /*------------------------------*/ BYTE get_drive(); /* Reads in a drive character */ /* from the console. If the */ /* drive is in the range(A-P), */ /* the drive is returned. If */ /* not,the value BAD is returned*/ /*------------------------------*/ VOID wait_for(); /* Holds the copy program in a */ /* wait state so the user can */ /* make sure his or her disk is */ /* in the proper drive. */ /*------------------------------*/ BYTE scan(); /* Picks off the source and */ /* destination drives from the */ /* command line. If they do */ /* not exist,or the specified */ /* drives are illegal,scan */ /* prompts the user. */ /*------------------------------*/ VOID get_options(); /* Scans the command line until */ /* a '[' is found. A list of */ /* of options is checked. Flags */ /* are turned on by the option */ /* indicators found after the */ /* square bracket. */ /*------------------------------*/ VOID copy(); /* If the two disk parameter */ /* blocks are identical,a track */ /* for track copy is made. */ /* If an serious error occurs, */ /* the copy is aborted and the */ /* program ends. */ /********************************/ VOID read_wr(); /* This routine does the actual */ /* reading and writing of the */ /* compatible disks. */ /********************************/ /************************************************************************/ /************************************************************************\ * * * BEGIN MAIN PROGRAM * * * \************************************************************************/ main(argc,argv) WORD argc; BYTE *argv[]; { BYTE answer,source,dest,optfound; optfound = FALSE; printf("Copy Ver 1.1\n"); /* Copy version message */ /*------------------------------*\ | If there is an mode on | | on the command line or the | | command line mode is not | | recognized,print a menu | | and have the user input a | | a legal copy mode. | \*------------------------------*/ argc--; argv++; /* point to first arg after cmd */ if(illegal(*argv)) get_mode(); else /* point to 2nd arg past cmd */ { argv++; argc--; } if(mode == _END) cpyexit(); /*------------------------------*\ | Look for the '[' as the | | start of the options. | | This allows the following | | | | copy [options | | ==================== | | or | | copy [options | | ============= | | | | I will not allow the drives| | to be specified after the | | options though!!!!! | \*------------------------------*/ if(strncmp(bracket,*argv,1) == MATCH) { argc = 0; get_options(*argv++); source = scan(argc,*argv++,SDRIVE); dest = scan(argc,*argv,DDRIVE); } else /*------------------------------*\ | Otherwise I assume that the | | command line looks like | | this: | | | | copy
[opt | | ========================== | \*------------------------------*/ { source = scan(argc--,*argv++,SDRIVE); dest = scan(argc--,*argv,DDRIVE); if(strncmp(bracket,*argv,1) == MATCH) { optfound = TRUE; get_options(*argv); } while(argc-- > 0) /* scan cmd line to find a '[' */ if(strncmp(bracket,*++argv,1) == MATCH && (!optfound)) { get_options(*argv); break; } else printf("\nExtraneous argument ignored: %s",*argv); } if(source == dest) { printf("\nSource and Destination must be different"); cpyexit(); } /*------------------------------*\ | REPEAT the copy as many | | times as the user wishes | \*------------------------------*/ while(TRUE) { if(verbose) wait_for(source,dest); copy((toupper(source) - 'A'),(toupper(dest) - 'A')); printf("\nCopy complete"); if(verbose) printf("\nDo you wish to repeat the copy? "); else break; answer = __BDOS(1,(long)0); if(toupper(answer) != 'Y') break; } } /************************/ UWORD illegal(_mode) /* Check command line */ /* aganist mode table */ /* Return mode code if */ /* found,return false */ /* if the mode is not */ /* found in the table */ /************************/ BYTE *_mode; { REG UWORD i; for(i = 0;i <= 3;i++) if(strcmp(_mode,choice[i]) == MATCH) { mode = i; return(FALSE); } mode = -1; return(TRUE); } /************************/ VOID get_mode() /* Prompt the user for */ /* a legal mode. Keep */ /* prompting until you */ /* get one. */ /************************/ { REG UWORD i; BYTE buf[256]; printf("\nMODE\tFUNCTION\n"); printf("\nALL\tCopy the whole disk"); printf("\nBOOT\tCopy the boot tracks"); printf("\nFILES\tCopy the non-boot tracks"); printf("\nEND\tEnd this program\n"); do { printf("\nEnter your copy mode: "); gets(buf); for(i = 0;buf[i] != NULLB;i++) buf[i] = tolower(buf[i]); } while(illegal(buf)); } /************************/ BYTE get_drive() /* Read in a drive. */ /************************/ { BYTE drive[10]; gets(drive); if(strlen(drive) > 1) return(BAD); drive[0] = toupper(drive[0]); if(drive[0] < 'A' || drive[0] > 'P') return(BAD); else return(drive[0]); } /************************/ VOID wait_for(source,dest) /* Wait for the user */ /* to put the disks */ /* in the proper drives*/ /************************/ BYTE source; BYTE dest; { BYTE wait_buf[5]; BYTE *upchoice[3]; upchoice[0] = "ALL"; upchoice[1] = "BOOT"; upchoice[2] = "FILES"; wait_buf[0] = 3; do { printf("\n(^C to ABORT)"); printf("\nRETURN to copy %s",upchoice[mode]); printf(" from %c to %c",source,dest); __BDOS(10,&wait_buf[0]); } while(wait_buf[1] != 0); } /************************/ BYTE scan(argc,argv,which_drive) /* Scan the command */ /* for the drives. */ /* If I can't find them*/ /* or they are illegal */ /* keep prompting the */ /* user for them. */ /************************/ WORD argc; BYTE *argv; UWORD which_drive; { BYTE *prompt,drive; if(argc > 0 && strlen(argv) == 1) { sscanf(argv,"%c",&drive); drive = toupper(drive); if(drive >= 'A' && drive <= 'P') return(drive); } switch(which_drive) { case SDRIVE : prompt = &msg1[0]; break; case DDRIVE : prompt = &msg2[0]; } printf("\n"); do { printf("%s",prompt); } while((drive = get_drive()) == BAD); return(drive); } /************************/ VOID get_options(argv) /* Scan cmd line for */ /* supported options */ /************************/ BYTE *argv; { argv++; /* skip the square bracket */ while(*argv != NULLB) { switch(*argv) { case 'A' : case 'a' : verbose = OFF; break; case 'v' : case 'V' : verify = ON; break; case ']' : return(0); default : printf("\nUndefined option: %c",*argv); } argv++; } } /************************/ VOID copy(source,dest) /* Select the disks. */ /* If I can't select or*/ /* the disk have diff- */ /* erent parameter blks*/ /* then print error msg*/ /************************/ BYTE source; BYTE dest; { REG struct _dpb *dskpblk; REG struct _dph *h1,*h2; REG LONG *ptr1,*ptr2; REG LONG endtrk,begtrk; LONG numtrks,temp; BYTE *sectran; REG UWORD i; biospb.funcnum = 9; /* Select the disks */ biospb.parm1 = (long)source; /* first the source disk */ biospb.parm2 = (long)0; /* set least sig bit to 0 */ if((h1 = bdos(50,&biospb))!= NULL) { biospb.parm1 = (long)dest; if((h2 = bdos(50,&biospb))!= NULL) { /*----------------------------------------------*/ /* check the parameter blocks(must be identical)*/ /*----------------------------------------------*/ dskpblk = (*h1).dpbp; /* pointer to dpb */ sectran = (*h1).xltp; /* pointer to sect tran tb*/ h1 = (*h1).dpbp; /* h1 points to dpb */ ptr1 = (long)h1; /* compare long words */ h2 = (*h2).dpbp; /* h2 points to dpb */ ptr2 = (long)h2; /* compare long words */ for(i = 0;i <= 3;i++) { if(*ptr1 != *ptr2) { printf("\nIncompatible disks"); cpyexit(); } ptr1++; ptr2++; } } else { printf("\nDisk select error on Destination"); cpyexit(); } } else { printf("\nDisk select error on Source"); cpyexit(); } /*------------------------------------------------------*\ | | | Compute beginning and ending track | | | \*------------------------------------------------------*/ temp = (((*dskpblk).dsm + 1)*((*dskpblk).blm + 1)); numtrks = temp/(*dskpblk).spt; if((numtrks*(*dskpblk).spt) < temp) numtrks++; numtrks += (*dskpblk).off; switch(mode) { case _ALL : begtrk = 0; endtrk = numtrks - 1; break; case _BOOT: begtrk = 0; endtrk = (*dskpblk).off - 1; break; case _FILES: begtrk = (*dskpblk).off; endtrk = numtrks - 1; } read_wr(source,dest,begtrk,endtrk,sectran,dskpblk); } /************************************************************************/ /* */ /* Do the actual read write operations */ /* =================================== */ /* */ /************************************************************************/ VOID read_wr(source,dest,begtrk,endtrk,sectran,dskpblk) /********************************/ BYTE source; /* source disk */ BYTE dest; /* destination disk */ REG LONG begtrk; /* beginning track to copy */ REG LONG endtrk; /* ending track to copy */ REG BYTE *sectran; /* sector translate table */ struct _dpb *dskpblk; /* ptr to disk parameter block */ /********************************/ { REG BYTE *dma; REG UWORD i,k; printf("\n***Copying Tracks***\n"); for(i = begtrk;i <= endtrk;i++) { dma = &trkbuf; biospb.funcnum = 12; /* set the dma address */ biospb.parm1 = dma; /* set it to beginning of trkbuf*/ biospb.parm2 = 0; __BDOS(50,&biospb); /* call the bios via the bdos */ biospb.funcnum = 9; /* select the source diskette */ biospb.parm1 = (long)source; biospb.parm2 = (long)0xFFFFFFFF; __BDOS(50,&biospb); /* call the bios via the bdos */ biospb.funcnum = 10; /* set the track */ biospb.parm1 = (long)(i); biospb.parm2 = 0; __BDOS(50,&biospb); /* call bios via bdos */ /*----------------------------------------------*\ | Read a track's worth of sectors | \*----------------------------------------------*/ for(k = 0;k < (*dskpblk).spt;k++) { biospb.funcnum = 16; /* translate sector*/ biospb.parm1 = (long)k; /* logical sect */ biospb.parm2 = sectran; biospb.parm1 = __BDOS(50,&biospb); biospb.funcnum = 11; /* set physical sec*/ biospb.parm2 = 0; __BDOS(50,&biospb); /* call the bios */ /*--------------------------------------*\ | READ a sector | \*--------------------------------------*/ biospb.funcnum = 13; if(__BDOS(50,&biospb) != 0) error(i,k,RDERR); dma += 128; biospb.funcnum = 12; biospb.parm1 = dma; __BDOS(50,&biospb); } dma = &trkbuf; biospb.funcnum = 12; /* reset the dma addr to trkbuf */ biospb.parm1 = dma; biospb.parm2 = 0; __BDOS(50,&biospb); /* call the bios */ biospb.funcnum = 9; /* select the destination disk */ biospb.parm1 = (long)dest; biospb.parm2 = (long)0xFFFFFFFF; __BDOS(50,&biospb); /* call the bios */ /*----------------------------------------------*\ | Write out a Track's worth of Sectors | \*----------------------------------------------*/ for(k = 0;k < (*dskpblk).spt;k++) { biospb.funcnum = 16; biospb.parm1 = (long)k; biospb.parm2 = sectran; biospb.parm1 = __BDOS(50,&biospb); biospb.funcnum = 11; biospb.parm2 = 0; __BDOS(50,&biospb); /*--------------------------------------*\ | Write a Sector | \*--------------------------------------*/ biospb.funcnum = 14; biospb.parm1 = (long)2; if(__BDOS(50,&biospb) != 0) error(i,k,WRERR); dma += 128; biospb.funcnum = 12; biospb.parm1 = dma; __BDOS(50,&biospb); } if(verify) { dma = &verbuf; biospb.funcnum = 12; biospb.parm1 = dma; __BDOS(50,&biospb); /* reset dma addr */ for(k = 0;k < (*dskpblk).spt;k++) { biospb.funcnum = 16; biospb.parm1 = (long)k; biospb.parm2 = sectran; biospb.parm1 = __BDOS(50,&biospb); biospb.funcnum = 11; biospb.parm2 = 0; __BDOS(50,&biospb); /*------------------------------*\ | Read a sector | \*------------------------------*/ biospb.funcnum = 13; if(__BDOS(50,&biospb) != 0) error(i,k,RDERR); dma += 128; biospb.funcnum = 12; biospb.parm1 = dma; __BDOS(50,&biospb); } /*--------------------------------------*\ | Verify the TRACK write | \*--------------------------------------*/ for(k = 0;k < (((*dskpblk).spt)*128);k++) if(trkbuf[k] != verbuf[k]) error(i,(k/128),WRERR); } printf("\r %d",i); } } /************************/ error(track,sector,phase) /* Read/Write Error */ /* handler */ /************************/ UWORD track; UWORD sector; BYTE phase; { REG BYTE *addr; if(phase == RDERR) addr = &msg3[0]; else addr = &msg4[0]; printf("\n\n\n%s",addr); printf("Track: %d Sector: %d",track,sector); cpyexit(); } cpyexit() /* bios select what the bdos thinks is the default disk */ { biospb.funcnum = 9; biospb.parm1 = __BDOS(25,(long)0); biospb.parm2 = 0; bdos(50,&biospb); exit(0); }