/* ------------------------------------------------------- *** Template for Procedure Heading *** Name: Result: Errors: Globals: Macros: Procedures: Action: ------------------------------------------------------- */ /* : : : : : : : : : : : : : : : : : : : : : : : : : : : : ucsd2cpm -- Transfer '.TEXT' files from a UCSD directory formatted disk to a CP/M directory formatted disk. 1) header blocks removed 2) linefeeds added after carriage returns 3) indents converted to appropriate # of spaces 4) filler nulls removed 5) control-A's removed (added 2/13/80 hrm) : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ /* : : : : : : : : : : : : : : : : : : : : : : : : : : : : Constants : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ #define DLE 0x10 /* Data Link Escape -- indent flag */ #define CR 0x0D /* carriage return */ #define LF 0x0A /* linefeed */ #define NULL 0 /* ascii null */ #define CPM_EOF 0X1A /* CP/M ascii endfile mark */ #define TRUE 1 /* booleans */ #define FALSE 0 #define SEL_DSK 14 /* bdos function number */ #define SET_DMA 26 /* bdos function number */ #define SET_TRK 9 /* bios index number */ #define SET_SEC 10 /* bios index number */ #define READ 12 /* bios index number */ #define DUMMY 0 /* dummy parameter for bios routine */ #define EOF_F 0xFFFF /* end of file flag */ #define D_ENT_SZ 26 /* UCSD directory entry size */ #define D_TITLE 6 /* offset to entry title */ #define UCSD_NAM_SZ 20 /* size of the name part of ucsd dir entry */ #define UCSD_DIR_SZ 2048 /* size of UCSD directory in bytes */ #define SECT_SIZE 128 /* bytes per physical sector */ #define BLOK_SIZE 512 /* bytes per UCSD logical block */ #define SECT_PER_BLOK 4 /* physical sectors per logical block */ /* : : : : : : : : : : : : : : : : : : : : : : : : : : : : Globals -- these would be static if it were available : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ int lsn; /* logical sector number */ int lstlsn; /* last logical sector number */ int nbytes; /* number of bytes remaining in ucsd file buffer */ char ucsdbuf[BLOK_SIZE]; /* 1 block buffer for ucsd file */ char *ptr; /* pointer to current byte in ucsd file buffer */ /* : : : : : : : : : : : : : : : : : : : : : : : : : : : : Global structure type : : : : : : : : : : : : : : : : : : : : : : : : : : : : */ struct buf { int fd; int nleft; char *nextp; char buff[SECT_SIZE]; }; /* ------------------------------------------------------- Name: main(argc,argv) Result: --- Errors: invocation syntax Globals: lsn,lstlsn,nbytes,ptr,ucsdbuf Macros: CPM_FILE,UCSD_FILE,UCSD_NAM_SZ Procedures: puts(),exit(),force_upr(),printf() putchar(),close(),unlink(),strcpy() strcat(),getchar(),open(),copy() tolower() Action: Call copy() to copy a '.TEXT' file from a UCSD formatted disk in drive B to a user specified file name in drive A Handle invocation errors Handle case of already existing destination file ------------------------------------------------------- */ #define CPM_FILE argv[1] #define UCSD_FILE argv[2] main(argc,argv) int argc; char *argv[]; { int file_id; char ucsdname[UCSD_NAM_SZ]; lsn = lstlsn = nbytes = 0; /* init globals */ ptr = ucsdbuf; if( argc != 3 ) { puts("Proper invocation form is:\n\n"); puts("UCSD2CPM \n"); puts("This copies B:.TEXT to A:\n"); exit(1); } force_upr(UCSD_FILE); /* make upper case */ if( (file_id = open(CPM_FILE,0)) >= 0 ) { printf("%s already exists. Delete it ? ",CPM_FILE); if( tolower(getchar()) != 'y' ) { puts("\naborted\n"); exit(1); } putchar('\n'); close(file_id); unlink(CPM_FILE); } else close(file_id); strcpy(ucsdname,UCSD_FILE); strcat(ucsdname,".TEXT"); if(copy(ucsdname,CPM_FILE)) printf("\n\nno such file %s .. aborted\n",ucsdname); exit(0); } /* ------------------------------------------------------- Name: copy (ucsd_file,cpm_file) Result: TRUE if error , FALSE if transfer ok Errors: no such '.TEXT' file Globals: struct buf, Macros: TRUE,EOF_F,CR,DLE,NULL,CPM_EOF,FALSE Procedures: find_file(),fcreat(),getbyte() putc(),fflush(),fclose() Action: Find '.TEXT' file in UCSD directory on drive B Create user specified file on drive A copy file content from UCSD to CP/M car by char translating: Skip header block Ignore NULL's Add LF after CR Convert DLE to appropriate number of spaces close output file ------------------------------------------------------- */ copy(ucsd_file,cpm_file) char *ucsd_file,*cpm_file; { struct buf tofile; int c; if( ! find_file(ucsd_file) ) /* no such file */ return TRUE; fcreat(cpm_file,&tofile); while( (c = getbyte()) != EOF_F ) switch(c) { case CR: putc(CR,&tofile); /* LF after CR */ putc(LF,&tofile); break; case DLE: for( c = (getbyte() - 32); c; c-- ) putc(' ',&tofile); /* fill spaces */ break; case '\1': /* ignore ^A's */ case NULL: break; /* ignore nulls */ default: putc(c,&tofile); /* pass char unmodified */ break; } putc(CPM_EOF,&tofile); /* send cp/m ascii endfile */ fflush(&tofile); fclose(&tofile); return FALSE; /* signal transfer ok */ } /* ------------------------------------------------------- Name: getbyte() Result: next sequential byte from UCSD '.TEXT' file Errors: Globals: lsn,lstlsn,nbytes,ptr,ucsdbuf[] Macros: EOF_F,BLOK_SIZE,SECT_PER_BLOK Procedures: read_ucsd() Action: read block at a time pass along byte at a time return EOF_F if end of UCSD file ------------------------------------------------------- */ getbyte() { if( lsn > lstlsn) return EOF_F; if( nbytes-- ) return *ptr++; read_ucsd(ucsdbuf,lsn,1); nbytes = (BLOK_SIZE-1); ptr = ucsdbuf; lsn += SECT_PER_BLOK; return *ptr++; } /* ------------------------------------------------------- Name: fclose(file) Result: status of closing action Errors: --- Globals: struct buf Macros: --- Procedures: close() Action: file open for buffered input or output is closed ------------------------------------------------------- */ fclose(file) struct buf *file; { return close( file->fd ); } /* ------------------------------------------------------- Name: find_file(ucsd_name) Result: TRUE if found, FALSE if not found Errors: Globals: lsn,lstlsn Macros: D_ENT_SZ,SECT_PER_BLOK,D_TITLE TRUE,FALSE,UCSD_DIR_SZ Procedures: read_ucsd() Action: --- ------------------------------------------------------- */ find_file(ucsd_name) char *ucsd_name; { char ucsd_dir[UCSD_DIR_SZ]; char *dir,*saved_dir,*name; int name_len; name = ucsd_name; read_ucsd(ucsd_dir,(2*SECT_PER_BLOK),4); dir = &ucsd_dir[D_TITLE + D_ENT_SZ]; /* skip title block */ while ( dir < &ucsd_dir[UCSD_DIR_SZ] ) { saved_dir = dir; if( (name_len = *dir++) <=0 || name_len > 19 ) return FALSE; while( *dir++ == *name++ ) { if( --name_len ) /* continue comparison check */ ; else { /* entry found */ dir = saved_dir - D_TITLE; /* point to alloc. info. */ lsn = *dir++; lsn += *dir++ << 8; /* lsn = starting logical block number */ lsn += 2; /* bypass header blocks */ lsn *= SECT_PER_BLOK; /* 1'st logical sector number */ lstlsn = *dir++; lstlsn += *dir++ << 8; /* lstlsn = ending logical block # */ lstlsn *= SECT_PER_BLOK; /* lstlsn = last logical sector # */ return TRUE; /* indicate file found */ } } name = ucsd_name; /* reset comparison name pointer */ dir = saved_dir + D_ENT_SZ; /* point at next entry name */ } return FALSE; /* no such file */ } /* ------------------------------------------------------- Name: read_ucsd() Result: status of selecting drive A Errors: sector read error Globals: --- Macros: SEL_DSK,SET_DMA,SET_TRK,SET_SEC READ,DUMMY,SECT_SIZE Procedures: bios(),bdos(),printf(),exit() Action: Read count blocks from drive B into buf starting at UCSD logical record number rn ------------------------------------------------------- */ read_ucsd(buf,rn,count) char *buf; unsigned rn; int count; { char bios(); int seccnt; seccnt = count*4; bdos(SEL_DSK,1); while( seccnt-- ) { bdos(SET_DMA,buf); bios(SET_TRK,track(rn)); bios(SET_SEC,sector(rn)); if( bios(READ,DUMMY) ) { printf("read error @ track %2d sector %2d",track(rn),sector(rn)); exit(1); } buf += SECT_SIZE; rn++; } return bdos(SEL_DSK,0); } /* ------------------------------------------------------- Name: sector(rn) Result: absolute sector number Errors: --- Globals: --- Macros: --- Procedures: --- Action: convert logical record number to absolute sector maps logical records to physical sectors by selecting every second sector in order (accounting for the modulo 26 process) on the diskette except that at a track switchover point there is an additional 'gap' of 6 sectors (total of 7) to allow for the drive to seek. This is UCSD's attempt to minimize disk access time. ------------------------------------------------------- */ sector(rn) unsigned rn; { unsigned t1,t2,trk,t3,sect; t1 = rn % 26; t2 = t1 << 1; if(t1 > 12) t2++; trk = rn/26; /* zero based absolute track */ t3 = t2 + 6*trk; /* new logical sector number */ sect = t3 % 26; /* new zero based absolute sector */ return ++sect; /* one based absolute sector */ } /* ------------------------------------------------------- Name: track(rn) Result: absolute track number Errors: --- Globals: --- Macros: --- Procedures: --- Action: convert logical sector number to absolute track This is simply the modulo 26 process except that track 0 is not considered part of the logical sector space. ------------------------------------------------------- */ track(rn) unsigned rn; { return rn/26 + 1; } /* ------------------------------------------------------- Name: index(str,sstr) Result: position of string sstr in string str -1 if not a substring Errors: --- Globals: --- Macros: --- Procedures: --- Action: --- ------------------------------------------------------- */ index(str,sstr) char *str,*sstr; { int first_match; if( *sstr == 0 ) /* null string is a substring of all strings */ return 0; for( first_match=0; *str != *sstr; first_match++) if( *str == 0 ) return -1; else str++; while( *sstr ) if( *str++ != *sstr++ ) return -1; return first_match; } /* ------------------------------------------------------- Name: force_upr(string) Result: --- Errors: --- Globals: --- Macros: --- Procedures: --- Action: force each char of string to upper case ------------------------------------------------------- */ force_upr(string) char *string; { while(*string) *string = *string++; return; } /* ------------------------------------------------------- Name: print(string,n) Result: --- Errors: --- Globals: --- Macros: --- Procedures: putchar() Action: print n chars to console starting at char pointer string ------------------------------------------------------- */ print(string,n) char *string; int n; { while(n--) putchar(*string++); return; }