[comp.sources.misc] v04i119: MSDOS <-> COCO File Utilities

crds@ncoast.UUCP (Glenn A. Emelko) (10/11/88)

Posting-number: Volume 4, Issue 119
Submitted-by: "Glenn A. Emelko" <crds@ncoast.UUCP>
Archive-name: dos2coco

Based upon the response in the past week regarding my MSDOS<->COCO file
utilities, I've decided to post it to the net for all to see.  It's not
elegant by any means, and you'll probably find my assembler the easiest
to follow of all of it, but it does all work.  I submit this for use as
public domain, with the only restriction that it not be used for profit
by anyone in part or in whole without first contacting myself.  This is
in the true spirit of public domain, and I scoff at all of those people
who post "shareware," so there.

Glenn A. Emelko
crds@ncoast

snail-mail address:
305 Mentor Ave.
Painesville, OH 44077

Allright, here it is... Have fun...

#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	dirc.asm
#	dirc.c
#	fromc.c
#	toc.c
# This archive created: Sat Oct  8 08:06:14 1988
export PATH; PATH=/bin:$PATH
if test -f 'dirc.asm'
then
	echo shar: over-writing existing file "'dirc.asm'"
fi
cat << \SHAR_EOF > 'dirc.asm'
CSEG		SEGMENT PARA PUBLIC 'CODE'

;---------------
;
;	DIRC.COM -- all segments set to CSEG upon entry
;
;	USAGE:
;		DIRC [filename[.ext]]<CR>
;
;		[filename[.ext]] is used to specify the file(s) whose
;			directory you want to list.
;
;		The information provided includes the free space on the disk.
;			The freespace is listed both in Granules and Bytes.
;			The display for each file includes its size in bytes,
;				the number of granules it occupies, the file
;				type, and the file mode.
;
;		You may use the global characters ? and * in the filename and
;			extention parameters.
;
;		If you do not specify a filename, it defaults to *.*
;
;		If you do not specify a filename extention, it defaults to *.
;
;
;		To assemble this program, do the following:
;
;
;		masm dirc;
;		link dirc;
;		exe2bin dirc.exe dirc.com
;		erase dirc.exe
;
;---------------
		ASSUME	CS:CSEG,DS:CSEG,SS:CSEG,ES:CSEG

		ORG	100H
;---------------
;
;	ENTRY -- Link to start of program (past variables)
;
;---------------
ENTRY:		JMP	START
;---------------
;
;	TEXT -- Directory listing header
;
;---------------
DRIVENAME	EQU	'A'

TEXT		DB	' Volume in drive ',DRIVENAME,' is COCO disk',13,10
		DB	' Directory of COCO disk',13,10,10,'$'

DRIVENO		EQU	DRIVENAME-'A'
;---------------
;
;	TYPETAB, TYPEn -- Types for COCO directory entries
;
;---------------
TYPETAB		DW	TYPE0
		DW	TYPE1
		DW	TYPE2
		DW	TYPE3
TYPE0		DB	'BASIC    $'
TYPE1		DB	'Data     $'
TYPE2		DB	'Program  $'
TYPE3		DB	'Text     $'
;---------------
;
;	ASCII, ASCIIn -- File modes for COCO directory entries
;
;---------------
ASCII		DW	ASCII0
		DW	ASCII1
ASCII0		DB	'Binary$'
ASCII1		DB	'ASCII$'
;---------------
;
;	NUMFILES, GRANSFREE, BYTESFREE -- Text for directory ending printout
;
;---------------
NUMFILES	DB	' File(s)$'
GRANSFREE	DB	' Grans, $'
BYTESFREE	DB	' Bytes free$'
;---------------
;
;	NUMBUF -- Expansion buffer for binary to decinal conversions
;
;---------------
NUMBUF		DB	6 DUP (?)
;---------------
;
;	FCOUNT, GCOUNT -- File count and Granule count for dir info
;
;---------------
FCOUNT		DW	0
GCOUNT		DW	0
;---------------
;
;	FILAOK, FILOK -- For wildcarding, File always ok and this file ok
;
;---------------
FILAOK		DB	0
FILOK		DB	0
;---------------
;
;	GRANS, GSIZE, SECSIZE -- Number of grans on the disk, number of
;		bytes per gran, and sector size
;
;---------------
GRANS		DW	68
GSIZE		DW	2304
SECSIZE		DW	256
;---------------
;
;	NBSEC -- Save area for MSDOS bytes per sector (changed, must restore)
;
;---------------
NBSEC		DB	0
;---------------
;
;	FAT -- FAT information buffer read from COCO disk
;
;---------------
FAT		DB	256 DUP (?)
;---------------
;
;	DIRBUF -- Directory sector buffer read from COCO disk
;
;---------------
DIRBUF		DB	256 DUP (?)
;---------------
;
;	START -- Start of DIRC.COM
;
;---------------
START:		CALL	CHECKAOK		;Set filaok if all files are
						;to be listed
		MOV	DX,OFFSET TEXT		;Print out directory header
		MOV	AH,9
		INT	21H

		XOR	AX,AX			;Get MSDOS disk info pointer
		MOV	ES,AX
		LES	BX,ES:[78H]

		MOV	AL,ES:[BX+3]		;Get MSDOS bytes per sector
		MOV	NBSEC,AL		;and save it

		MOV	BYTE PTR ES:[BX+3],1	;Set bytes per sector to 256

		PUSH	CS			;Restore ES to CSEG
		POP	ES
;---------------
;
;	Do disk FAT read, 1 sector, buffer=FAT, track 17, sector 2, side 0
;		disk B:
;
;---------------
		MOV	AX,0201H
		MOV	BX,OFFSET FAT
		MOV	CX,1102H
		MOV	DX,DRIVENO
		INT	13H

		MOV	CX,9			;# of directory sectors
;---------------
;
;	RLOOP -- Main loop for directory read
;
;---------------
RLOOP:		PUSH	CX			;Save loop counter

		MOV	AX,12			;Calc sector number (3-11)
		SUB	AX,CX
		MOV	CX,AX

		MOV	CH,17			;Track number 17
		MOV	BX,OFFSET DIRBUF	;Buffer=DIRBUF
		MOV	AX,0201H		;Read 1 sector
		MOV	DX,DRIVENO		;Head 0, disk B:
		INT	13H			;Do BIOS read

		MOV	CX,8			;Number of entries in sector
;---------------
;
;	PNEXT -- Secondary loop, for each file within directory sector
;
;---------------
PNEXT:		MOV	AL,[BX]			;Get entry first byte

		OR	AL,AL			;Zero, file has been killed
		JZ	NEXTENT

		CMP	AL,0FFH			;0FFh, no more in sector
		JZ	LASTENT

		CALL	CHECKFIL		;Check if entry is ok to list
						;result: filok=1, yes

		PUSH	CX			;Save count and buffer pointer
		PUSH	BX

		ADD	BX,11			;Offset to file info

		CMP	FILOK,1			;This file to be listed ?
		JNZ	NODISP1			;No, skip display

		SUB	BX,11			;Point to file name again

		INC	FCOUNT			;Count one more file listed

		MOV	CX,8			;Print out file name, 1 space,
		CALL	PSTRING			;File extention, and 4 more
		MOV	CX,1			;Spaces. Adjust BX to file
		CALL	SPACES			;Info
		MOV	CX,3
		CALL	PSTRING
		MOV	CX,4
		CALL	SPACES

NODISP1:	MOV	AX,[BX+1]		;AL=file mode, AH=first gran #
		MOV	CX,[BX+3]		;CX=# of bytes in last sector
		XCHG	CH,CL			;6809 format (HIGH, LOW)
		MOV	BL,[BX]			;BL is file type

		PUSH	AX			;Save mode, gran, and type
		PUSH	BX
		CALL	CALCDISP		;Calc file size, and display

		POP	BX			;Restore mode, gran, and type
		CMP	FILOK,1			;File ok to use?
		JNZ	NODISP2			;No, dont display

		XOR	BH,BH			;Find text for file type
		SHL	BX,1
		MOV	DX,TYPETAB[BX]

		MOV	AH,9			;Print text
		INT	21H

		POP	BX			;BL=file mode
		PUSH	BX			;Maintain stack integrety

		XOR	BH,BH			;Z=nonascii file type
		OR	BL,BL
		JZ	NONASCII

		MOV	BL,2			;Offset for ASCII message

NONASCII:	MOV	DX,ASCII[BX]		;Get message and print it
		MOV	AH,9
		INT	21H
		CALL	CRLF			;Followed by CRLF

NODISP2:	POP	AX			;Pop stack information
		POP	BX
		POP	CX			;Restore count for entries

NEXTENT:	ADD	BX,20H			;Point to next entry
		LOOP	PNEXT			;Loop for all 8 in sector

LASTENT:	POP	CX			;Restore outer loop count
		LOOP	RLOOPX			;Loop for all 9 sectors

		MOV	CX,3			;Last line, print disk info
		CALL	SPACES			;3 spaces in

		MOV	AX,FCOUNT		;Number of files
		CALL	MAKENUM			;Make numeric and print

		MOV	DX,OFFSET NUMFILES	;Print "File(s)" message
		MOV	AH,9
		INT	21H

		MOV	AX,GRANS		;Number of grans free
		CALL	MAKENUM			;Make numeric and print

		MOV	DX,OFFSET GRANSFREE	;Print "Grans" message
		MOV	AH,9
		INT	21H

		MOV	AX,GRANS		;Number of grans free
		MOV	CX,2304			;Compute bytes free
		MUL	CX
		CALL	MAKEBNUM		;Make big numeric and print

		MOV	DX,OFFSET BYTESFREE	;Print "Bytes" message
		MOV	AH,9
		INT	21H

		CALL	CRLF			;Do a CRLF

		XOR	AX,AX			;Point to MSDOS disk info
		MOV	ES,AX
		LES	BX,ES:[78H]

		MOV	AL,NBSEC		;Get MSDOS bytes per sector
		MOV	ES:[BX+3],AL		;Restore to old value

		MOV	AX,4C00H		;Exit to DOS, no errors
		INT	21H

RLOOPX:		JMP	RLOOP			;Vector for loop (too far)
;---------------
;
;	SPACES -- print CX spaces on screen
;
;---------------
SPACES:		MOV	AH,2
		MOV	DL,' '
		INT	21H
		LOOP	SPACES
		RET
;---------------
;
;	PSTRING -- print string at BX with length CX, return BX=BX+CX
;
;---------------
PSTRING:	MOV	DL,[BX]
		INC	BX
		MOV	AH,2
		INT	21H
		LOOP	PSTRING
		RET
;---------------
;
;	CRLF -- Do CRLF to screen, dont use any registers
;
;---------------
CRLF:		PUSH	AX
		PUSH	DX
		MOV	AH,2
		MOV	DL,13
		INT	21H
		MOV	DL,10
		INT	21H
		POP	DX
		POP	AX
		RET
;---------------
;
;	CALCDISP -- calculate file size, and display if necessary
;
;---------------
CALCDISP:	MOV	GCOUNT,0		;File currently has no grans
		JMP	CALCDISP1		;Initialized, continue...

NEXTGRAN:	ADD	CX,GSIZE		;Count more bytes in file

CALCDISP1:	MOV	AL,AH			;Get gran number in AX
		XOR	AH,AH

		DEC	GRANS			;Drop gran from free space
		INC	GCOUNT			;And add gran to file size

		MOV	BX,OFFSET FAT		;Point to FAT info
		ADD	BX,AX			;Point to GRAN entry for file

		MOV	AH,[BX]			;Get next gran number or end
		OR	AH,AH			;Sign set, last gran in file
		JNS	NEXTGRAN

		XOR	AL,AL			;Compute number of bytes left
		AND	AH,3FH			;AH is number of sects, so
		DEC	AH			;AX is number of bytes.  Dont
		JS	NOADD			;Count last (CX had it)

		ADD	CX,AX			;More than 1 left, add 256*AH

NOADD:		MOV	AX,CX			;Get number of bytes in file
		CMP	FILOK,1			;Do we print this?
		JNZ	NODISP3			;No...

		CALL	MAKENUM			;Make numeric and print

		MOV	AX,GCOUNT		;Get gran count for file
		CALL	MAKENUM			;Make numeric and print

		MOV	CX,2			;Print 2 spaces
		CALL	SPACES

NODISP3:	RET				;Done with calcdisp
;---------------
;
;	MAKENUM -- Make AX a decimal value, and print it to the screen
;
;---------------
MAKENUM:	MOV	BX,OFFSET NUMBUF	;NUMBUF is area for conversion

		XOR	DX,DX			;DX:AX is number

		MOV	CX,10000		;Initial divisor

DIVLP1:		DIV	CX			;AX=AX/CX, DX is remainder

		ADD	AL,30H			;Convert it to ASCII

		MOV	[BX],AL			;And put it in buffer
		INC	BX

		MOV	AX,CX			;Divide divisor by 10
		PUSH	DX			;Save remainder
		MOV	CX,10
		XOR	DX,DX			;DX:AX is divisor
		DIV	CX
		MOV	CX,AX			;New divisor in CX
		POP	AX			;Old remainder in AX

		CMP	CX,1			;Divisor=1 (done indicator)
		JNZ	DIVLP1			;No, loop for next character

		MOV	CX,4			;# of possible leading 0's
;---------------
;
;	LZDROPCOM -- Drop leading zero's (change to spaces) for CX counts
;		     Also puts last character in buffer at [BX] for callers
;
;---------------
LZDROPCOM:	PUSH	CX			;Save number of possible 0's

		ADD	AL,30H			;Put last character in buffer
		MOV	[BX],AL

		MOV	BX,OFFSET NUMBUF	;Point to head of buffer

LZDROP:		CMP	BYTE PTR [BX],30H	;Is this character a 0?
		JNZ	DONE			;No, finished

		MOV	BYTE PTR [BX],' '	;Blank it
		INC	BX

		LOOP	LZDROP			;Go for CX characters

DONE:		MOV	BX,OFFSET NUMBUF	;Print NUMBUF for CX+1 chars
		POP	CX
		INC	CX
		CALL	PSTRING
		RET
;---------------
;
;	MAKENUM -- Make DX:AX a decimal value, and print it to the screen
;		   Max value is 199999
;---------------
MAKEBNUM:	MOV	BX,OFFSET NUMBUF	;Output buffer
		MOV	CX,10000		;Initial divisor

BDIVLP1:	DIV	CX			;AX DX:AX/CX, DX is remainder
		CMP	CX,10000		;If first division, make 2 dig
		JNZ	DDD1			;Not first...

		MOV	AH,30H			;Set first digit to 0
		CMP	AL,10			;2nd digit >9?
		JB	NOAH			;No

		MOV	AH,31H			;First digit=1
		SUB	AL,10			;2nd digit is AL-10

NOAH:		MOV	[BX],AH			;Place first digit
		INC	BX			;Position to second

DDD1:		ADD	AL,30H			;Convert this digit to ASCII

		MOV	[BX],AL			;Place digit in buffer
		INC	BX

		MOV	AX,CX			;Divisor=divisor/10
		PUSH	DX			;Save old remainder
		MOV	CX,10
		XOR	DX,DX			;DX:AX is divisor
		DIV	CX
		MOV	CX,AX			;CX is new divisor
		POP	AX			;AX is old remainder

		CMP	CX,1			;Divisor=1?
		JNZ	BDIVLP1			;No, continue

		MOV	CX,5			;Max number of leading zeros
		JMP	LZDROPCOM		;Go drop any leading zeros
;---------------
;
;	CHECKAOK -- check if all are ok, and if not, set up compare buffer for
;		    comparisons
;
;---------------
CHECKAOK:	MOV	DI,5DH			;Command line filespec
		MOV	CX,11			;Number of characters to scan

		MOV	FILAOK,1		;Preset value for true

		MOV	AX,3F20H		;Use both ? and space until it
						;Is determined what it is

CHECKAOK1:	CMP	BYTE PTR [DI],AL	;Is it the same as last?
		JZ	CHECK1			;Yes, ok so far
		CMP	BYTE PTR [DI],AH	;Alternate for ?
		JZ	CHECK1A			;Yes, ok so far

		MOV	FILAOK,0		;File is not always ok
		RET				;Done

CHECK1:		MOV	AH,AL			;Copy char, we know what they
						;All must be
		INC	DI			;Point to next char
		LOOP	CHECKAOK1		;Go for all 11 characters
		RET				;Done, file always ok

CHECK1A:	MOV	AL,AH			;Copy char, we know what they
		INC	DI			;All must be. point to next
		LOOP	CHECKAOK1		;And go for all 11 characters
		RET				;Done, file always ok
;---------------
;
;	CHECKFIL -- check a file to see if it matches comparison value
;
;---------------
CHECKFIL:	MOV	FILOK,1			;Set to true for now

		CMP	FILAOK,1		;Always ok?
		JNZ	CHECKFIL1		;No, check this one
		RET				;Yes, return with this one ok

CHECKFIL1:	PUSH	BX			;Save important regs
		PUSH	CX
		MOV	CX,11			;File spec length
		MOV	DI,5DH			;Offset to comparison string
CHECKF2:	MOV	AL,[DI]			;Get first char to compare
		CMP	AL,'?'			;Wildcard?
		JZ	CHECKN			;Yes, this character always ok
		CMP	AL,[BX]			;No, check with current value
		JZ	CHECKN			;Yes, ok so far
		POP	CX			;No, restore registers
		POP	BX
		MOV	FILOK,0			;File is not ok
		RET				;Done

CHECKN:		INC	BX			;Point to next character
		INC	DI
		LOOP	CHECKF2			;Go for all 11 characters
		POP	CX			;Restore important registers
		POP	BX
		RET				;Done, file is ok

CSEG		ENDS

		END	ENTRY
SHAR_EOF
chmod +x 'dirc.asm'
if test -f 'dirc.c'
then
	echo shar: over-writing existing file "'dirc.c'"
fi
cat << \SHAR_EOF > 'dirc.c'
#include <dos.h>
#include <stdlib.h>

/*
 |
 |	DIRC.EXE
 |
 |	USAGE:
 |		DIRC [filename[.ext]]<CR>
 |
 |		[filename[.ext]] is used to specify the file(s) whose
 |			directory you want to list.
 |
 |		The information provided includes the free space on the disk.
 |			The freespace is listed both in Granules and Bytes.
 |			The display for each file includes its size in bytes,
 |				the number of granules it occupies, the file
 |				type, and the file mode.
 |
 |		You may use the global characters ? and * in the filename and
 |			extention parameters.
 |
 |		If you do not specify a filename, it defaults to *.*
 |
 |		If you do not specify a filename extention, it defaults to *.
 |
 |	Notes:  Currently sector errors are not dealt with very well, but
 |		then again, I've never seen one.  The funtions do return
 |		error values, but they are not checked.  Also note that
 |		this program was very closely based on my working assembler
 |		from about two years ago, and I just did a quick translation
 |		without doing much careful thought about improvements, of
 |		which there are probably many.
 */

#define	DRIVENAME	'A'
#define	DRIVENO		(DRIVENAME-'A')

char *typetab[4] = {	/* Types for COCO directory entries */
	"BASIC    ",
	"Data     ",
	"Program  ",
	"Text     "
};
char *asciitab[2] = {	/* File modes for COCO directory entries */
	"Binary",
	"ASCII"
};

int fcount, gcount;	/* File/Granule count for dir info */
int filaok, filok;	/* For wildcarding, always ok and this ok */

int grans;

#define GRANS	68	/* Number of grans on the disk */
#define GSIZE	2304	/* Number of bytes per granule */
#define SECSIZE	256	/* Number of bytes per sector */
#define DIRENT	32	/* Number of bytes per directory entry */

int nbsec;		/* Save area for MSDOS value (must restore)*/
char far *thisseg = " ";/* used to find data segment */
char fat[SECSIZE];		/* FAT information buffer from COCO disk */
char dirbuf[SECSIZE];	/* Directory sector buffer from COCO disk */

char fbuf[11] =		/* file buffer for comparison to fspec given*/
	 "???????????";

union REGS inregs;
union REGS outregs;
struct SREGS segregs;

main(argc,argv)		/*	main -- Start of DIRC.EXE execution */
int argc;		/* I did not use argc/argv in lieu of DOS's */
char *argv[];		/* PSP for sake of easy wildcard expansions */
{
	char far *(far *vec78h);	/* We need to play with the disk */
	char far *bpsvec;		/* control block area		 */

	int scnt, ecnt;			/* sector count and entry count	 */
	int mode, gran, lbytes, type;

	unsigned char *bufp;

	checkaok();			/* a little parsing on the PSP	 */

	printf(" Volume in drive %c is COCO disk\n",DRIVENAME);
	printf(" Directory of COCO disk\n\n");

	vec78h = (char far *(far *))(0x78);
				/* 0000:0078, MSDOS disk info pointer */
	bpsvec = 3 + *vec78h;	/* Get MSDOS bytes per sector ptr */

	nbsec = (int) *bpsvec;	/* Save byte for restore later */
	*bpsvec = (char) 1;	/* Set byte to 1 (256 bytes/sector) */

	fcount = 0;		/* initialize */
	grans = GRANS;		/* will be used to count down available */

	cread(17,2,fat,1);	/* Read coco sector, track 17 sec 2 */

	for(scnt=0;scnt<9;scnt++) {	/* Main loop for directory read */

	    cread(17,scnt+3,dirbuf,1);	/* Read directory sectors	*/

	    bufp = dirbuf;

	    for(ecnt=0;ecnt<8;ecnt++) if (*bufp!=0xff) {
						/* entries per sector = 8 */
						/* ...for each file entry: */
		if (*bufp) {
		    checkfil(bufp);		/* Check if it's ok to list */
						/* result: filok=1, yes */
		    if (filok) {
			fcount++;		/* count it, and print name */
			printf("%.8s %.3s    ",bufp,(char *) bufp+8);
		    }

		    mode = (int) *(bufp+12);
		    gran = (int) *(bufp+13);
		    lbytes = ( (256 * (int)(*(bufp+14))) +
			     (int)(*(bufp+15)) );/* Count is in 6809 format */
						 /* high, then low byte     */
		    type = (int) *(bufp+11);
		    calcdisp(mode,gran,lbytes,type);

		    if (filok) {
			printf("%s",typetab[type]);
			if (mode!=0) mode=1;	/* now simply ascii or not */
			printf("%s\n",asciitab[mode]);
		    }
		}
	    bufp += DIRENT;
	    }
	}
	printf("   %5d File(s)%5d Grans, %6ld Bytes free\n",
		fcount, grans,(long) grans*GSIZE);

	*bpsvec = (char) nbsec;	/* Set byte to MSDOS value */
}

/* calcdisp -- calculate file size, and display if necessary */

calcdisp(mode,gran,lbytes,type)
int mode,gran,lbytes,type;
{
	int bytecnt;

	gcount = 0;
	bytecnt = lbytes;

	do {
		grans--;			/* one less granule avail  */
		gcount++;			/* one more allocated here */
		gran = (int) *(fat+gran);	/* point to next granule   */
		if (gran & 0x80) {		/* if high bit set, last   */
			gran = (gran & 0x3f)-1;	/* find remaining sectors  */
			if (gran>0) bytecnt += gran*SECSIZE;
			break;			/* done			   */
		}
		bytecnt += GSIZE;		/* add gran length	   */
	} while (gran>=0);			/* we'll actually break    */
	if (filok) printf("%5d%5d  ",bytecnt,gcount); /* print if needed   */
}


/* check if all are ok, and if not, set up compare buffer for comparisons */
checkaok()
{
	char far *pspfspec;
	int cnt;
	char chk1;
	char *nbuf;

	FP_SEG(pspfspec) = _psp;		/* Use the PSP, dos is nice */
	FP_OFF(pspfspec) = 0x5d;		/* to us on wildcards	    */

	chk1 = *pspfspec;			/* get first character	    */
	nbuf=fbuf;

	if (chk1==' ' || chk1=='?') {
		filaok=1;			/* if all blank or all '?'  */
		for(cnt=0;cnt<11;cnt++) {
			*nbuf = *pspfspec++;
			if (*nbuf++!=chk1) filaok = 0;
		}				/* if filaok then ALL files */
	} else filaok=0;
}

/*	checkfil -- check a file to see if it matches comparison value */
checkfil(nbuf)
char *nbuf;
{
	int cnt;
	filok = 1;
	if (filaok) return;

	for (cnt=0;cnt<11;cnt++)
		if ((*(fbuf+cnt) != '?') &&
		    (*(fbuf+cnt) != *(nbuf+cnt))) filok=0;
	return;
}

int cread(trk,sec,buf,cnt)
int trk,sec;
char *buf;
int cnt;
{
	int errcnt;
	char *temp;

	errcnt = 0;
	for(temp=buf;temp<buf+SECSIZE*cnt;temp++) *temp=0; /* zero buff */
	if (trk>39 || sec>18) return(-1);	/* don't read bad ptr's */

	while (errcnt<4) {
		inregs.x.ax = 0x0200+(cnt & 0xff);/* 2 = read, Count=cnt */
		inregs.x.bx = (int) buf;	/* pointer to buffer */
		inregs.x.cx = (int) (trk & 0xff)*256
				   +(sec & 0xff);/* Track , Sector */
		inregs.x.dx = DRIVENO;		/* Drive select byte */
		segregs.ds = FP_SEG(thisseg);
		segregs.es = FP_SEG(thisseg);

		int86x(0x13,&inregs,&outregs,&segregs);
		if (outregs.x.cflag==0) break;
		creset();
		errcnt++;
	}
	return(errcnt);
}

int creset()
{
	inregs.x.ax = 0x0000;		/* 2 = read function, Count=cnt */
	inregs.x.dx = DRIVENO;		/* Drive select byte */
	segregs.ds = FP_SEG(thisseg);
	segregs.es = FP_SEG(thisseg);

	int86x(0x13,&inregs,&outregs,&segregs);
	return(outregs.x.cflag);
}
SHAR_EOF
chmod +x 'dirc.c'
if test -f 'fromc.c'
then
	echo shar: over-writing existing file "'fromc.c'"
fi
cat << \SHAR_EOF > 'fromc.c'
#include <dos.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

/*
 |
 |	FROMC.EXE
 |
 |	USAGE:
 |		FROMC [filename[.ext]]<CR>
 |
 |		[filename[.ext]] is used to specify the file(s) which
 |			you want to get from COCO disks.
 |
 |		This function also has the side effect of giving you
 |			directory information as it copies (an added
 |			benefit).
 |
 |		The information provided includes the free space on the disk.
 |			The freespace is listed both in Granules and Bytes.
 |			The display for each file includes its size in bytes,
 |				the number of granules it occupies, the file
 |				type, and the file mode.
 |
 |		You may use the global characters ? and * in the filename and
 |			extention parameters.
 |
 |		If you do not specify a filename, it defaults to *.*
 |
 |		If you do not specify a filename extention, it defaults to *.
 |
 */

#define	DRIVENAME	'A'
#define	DRIVENO		(DRIVENAME-'A')

char *typetab[4] = {	/* Types for COCO directory entries */
	"BASIC    ",
	"Data     ",
	"Program  ",
	"Text     "
};
char *asciitab[2] = {	/* File modes for COCO directory entries */
	"Binary",
	"ASCII"
};

int fcount, gcount;	/* File/Granule count for dir info */
int filaok, filok;	/* For wildcarding, always ok and this ok */

int grans;

#define GRANS	68	/* Number of grans on the disk */
#define GSIZE	2304	/* Number of bytes per granule */
#define SECSIZE	256	/* Number of bytes per sector */
#define DIRENT	32	/* Number of bytes per directory entry */

int lastsec;
FILE *filptr;
char *fname = "            \r";

int nbsec;		/* Save area for MSDOS value (must restore)*/
char far *thisseg = " ";/* used to find data segment */
char fat[SECSIZE];		/* FAT information buffer from COCO disk */
char dirbuf[SECSIZE];	/* Directory sector buffer from COCO disk */

char fbuf[11] =		/* file buffer for comparison to fspec given*/
	 "???????????";

char inbuff[GSIZE];	/* Input buffer for granules */

union REGS inregs;
union REGS outregs;
struct SREGS segregs;

main(argc,argv)		/*	main -- Start of DIRC.EXE execution */
int argc;
char *argv[];
{
	char far *(far *vec78h);
	char far *bpsvec;

	int scnt, ecnt;
	int mode, gran, lbytes, type;

	unsigned char *bufp;

	checkaok();

	printf(" Volume in drive %c is COCO disk\n",DRIVENAME);
	printf(" Copying files from COCO disk\n\n");

	vec78h = (char far *(far *))(0x78);
				/* 0000:0078, MSDOS disk info pointer */
	bpsvec = 3 + *vec78h;	/* Get MSDOS bytes per sector ptr */

	nbsec = (int) *bpsvec;	/* Save byte for restore later */
	*bpsvec = (char) 1;	/* Set byte to 1 (256 bytes/sector) */

	fcount = 0;		/* initialize */
	grans = GRANS;

	cread(17,2,fat,1);

	for(scnt=0;scnt<9;scnt++) {	/* Main loop for directory read */

	    cread(17,scnt+3,dirbuf,1);

	    bufp = dirbuf;

	    for(ecnt=0;ecnt<8;ecnt++) if (*bufp!=0xff) {
						/* entries per sector = 8 */
						/* ...for each file entry: */
		if (*bufp) {
		    checkfil(bufp);		/* Check if it's ok to list */
						/* result: filok=1, yes */
		    if (filok) {
			fcount++;		/* count it, and print name */
			printf("Copying %.8s %.3s    ",bufp,(char *) bufp+8);
		    }

		    mode = (int) *(bufp+12);
		    gran = (int) *(bufp+13);
		    lbytes = ( (256 * (int)(*(bufp+14))) +
			     (int)(*(bufp+15)) );/* Count is in 6809 format */
						 /* high, then low byte */
		    type = (int) *(bufp+11);
		    calcdisp(mode,gran,lbytes,type,bufp);

		    if (filok) {
			printf("%s",typetab[type]);
			if (mode!=0) mode=1;	/* now simply ascii or not */
			printf("%s\n",asciitab[mode]);
		    }
		}
	    bufp += DIRENT;
	    }
	}
	printf("Done:     %5d File(s) copied\n",fcount);

	*bpsvec = (char) nbsec;	/* Set byte to MSDOS value */
}

/* calcdisp -- calculate file size, and display if necessary */

calcdisp(mode,gran,lbytes,type,name)
int mode,gran,lbytes,type;
char *name;
{
	int bytecnt,tgran;

	gcount = 0;
	bytecnt = lbytes;

	if (filok) openfile(name);

	lastsec = lbytes;

	do {
		tgran = gran;
		grans--;
		gcount++;
		gran = (int) *(fat+gran);
		if (gran & 0x80) {
			if (filok) copylast(tgran,gran & 0x3f);
			gran = (gran & 0x3f)-1;
			if (gran>0) bytecnt += gran*SECSIZE;
			gran=-1;
			break;
		}
		if (filok) copygran(tgran);
		bytecnt += (int) GSIZE;
	} while (gran>=0);
	if (filok) printf("%5d%5d  ",bytecnt,gcount);
}


/* check if all are ok, and if not, set up compare buffer for comparisons */
checkaok()
{
	char far *pspfspec;
	int cnt;
	char chk1;
	char *nbuf;

	FP_SEG(pspfspec) = _psp;
	FP_OFF(pspfspec) = 0x5d;

	chk1 = *pspfspec;
	nbuf=fbuf;

	if (chk1==' ' || chk1=='?') filaok=1; else filaok=0;

	for(cnt=0;cnt<11;cnt++) {
		*nbuf = *pspfspec++;
		if (*nbuf++!=chk1) filaok = 0;
	}
}

/*	CHECKFIL -- check a file to see if it matches comparison value */
checkfil(nbuf)
char *nbuf;
{
	int cnt;
	filok = 1;
	if (filaok) return;

	for (cnt=0;cnt<11;cnt++)
		if ((*(fbuf+cnt) != '?') &&
		    (*(fbuf+cnt) != *(nbuf+cnt))) filok=0;
	return;
}

openfile(name)
char *name;
{
	int cnt;
	char *fptr;

	fptr = fname;

	for(cnt=0;cnt<8;cnt++) *fptr++ = *(name+cnt);
	while (*(fptr-1)==' ' && fptr>fname) fptr--;
	*fptr++ = '.';
	for(cnt=8;cnt<11;cnt++) *fptr++ = *(name+cnt);
	while ((*(fptr-1)==' ' || *(fptr-1)=='.') && fptr>fname) fptr--;
	*fptr = 0;

	filptr = fopen(fname,"w+b");
}

copygran(gran)
int gran;
{
	int secstart;

	if (gran>=34) gran += 2;
	secstart = (gran & 1)*9+1;
	gran /= 2;

	cread(gran,secstart,inbuff,9);

	fwrite(inbuff,GSIZE,1,filptr);
}

copylast(gran,nsec)
int gran,nsec;
{
	int secstart;

	if (gran>=34) gran+=2;
	secstart = (gran & 1)*9+1;
	gran /= 2;

	cread(gran,secstart,inbuff,9);

	nsec -= 1;
	if (nsec<0) nsec=0;

	fwrite(inbuff,SECSIZE*nsec+lastsec,1,filptr);

	fclose(filptr);
}

int cread(trk,sec,buf,cnt)
int trk,sec;
char *buf;
int cnt;
{
	int errcnt;
	char *temp;

	errcnt = 0;
	for(temp=buf;temp<buf+SECSIZE*cnt;temp++) *temp=0;
	if (trk>39 || sec>18) return(-1);

	while (errcnt<4) {
		inregs.x.ax = 0x0200+(cnt & 0xff);/* 2 = read, Count=cnt */
		inregs.x.bx = (int) buf;	/* pointer to buffer */
		inregs.x.cx = (int) (trk & 0xff)*256
				   +(sec & 0xff);/* Track , Sector */
		inregs.x.dx = DRIVENO;		/* Drive select byte */
		segregs.ds = FP_SEG(thisseg);
		segregs.es = FP_SEG(thisseg);

		int86x(0x13,&inregs,&outregs,&segregs);
		if (outregs.x.cflag==0) break;
		creset();
		errcnt++;
	}
	return(errcnt);
}

int creset()
{
	inregs.x.ax = 0x0000;		/* 2 = read function, Count=cnt */
	inregs.x.dx = DRIVENO;		/* Drive select byte */
	segregs.ds = FP_SEG(thisseg);
	segregs.es = FP_SEG(thisseg);

	int86x(0x13,&inregs,&outregs,&segregs);
	return(outregs.x.cflag);
}
SHAR_EOF
chmod +x 'fromc.c'
if test -f 'toc.c'
then
	echo shar: over-writing existing file "'toc.c'"
fi
cat << \SHAR_EOF > 'toc.c'
#include <dos.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <io.h>

/*
 |
 |	TOC.EXE
 |
 |	USAGE:
 |		TOC filename.ext {BDPT} {AB}<CR>
 |
 |		filename.ext is used to specify the file which you want
 |			to transfer.  NO wildcards are supported, and you
 |			MUST include a file type and mode.
 |
 |		This is a derivitive of DIRC.C (can you tell?)
 */

#define	DRIVENAME	'A'
#define	DRIVENO		(DRIVENAME-'A')

char *typetab[4] = {	/* Types for COCO directory entries */
	"BASIC    ",
	"Data     ",
	"Program  ",
	"Text     "
};
char *asciitab[2] = {	/* File modes for COCO directory entries */
	"Binary",
	"ASCII"
};

int fcount, gcount;	/* File/Granule count for dir info */
int filaok, filok;	/* For wildcarding, always ok and this ok */

int grans;

#define GRANS	68	/* Number of grans on the disk */
#define GSIZE	2304	/* Number of bytes per granule */
#define SECSIZE	256	/* Number of bytes per sector */
#define DIRENT	32	/* Number of bytes per directory entry */

int lastsec;
FILE *filptr;
char *fname = "            \r";

int nbsec;		/* Save area for MSDOS value (must restore)*/
char far *thisseg = " ";/* used to find data segment */
char fat[SECSIZE];		/* FAT information buffer from COCO disk */
char dirbuf[SECSIZE];	/* Directory sector buffer from COCO disk */

char fbuf[11] =		/* file buffer for comparison to fspec given*/
	 "???????????";

char inbuff[GSIZE];	/* Input buffer for granules */

union REGS inregs;
union REGS outregs;
struct SREGS segregs;

unsigned long int numb;
unsigned int numg,numrs,numrb;

main(argc,argv)		/*	main -- Start of DIRC.EXE execution */
int argc;
char *argv[];
{
	char far *(far *vec78h);
	char far *bpsvec;

	int scnt, ecnt, cnt, lcnt;
	unsigned int mode, gran, lbytes, type;

	unsigned char *bufp;

	if (argc!=4) {
		printf("Usage:   toc (fspec) {BDPT} {AB}\n");
		exit(1);
	}
	switch (*argv[2]) {
		case 'b':
		case 'B': {
			type = 0;
			break;
		}
		case 'd':
		case 'D': {
			type = 1;
			break;
		}
		case 'p':
		case 'P': {
			type = 2;
			break;
		}
		case 't':
		case 'T': {
			type = 3;
			break;
		}
		default: {
			printf("Bad parameter: %c\n",*argv[2]);
			exit(1);
		}
	}
	switch (*argv[3]) {
		case 'a':
		case 'A': {
			mode = 1;
			break;
		}
		case 'b':
		case 'B': {
			mode = 0;
			break;
		}
		default: {
			printf("Bad parameter: %c\n",*argv[3]);
			exit(1);
		}
	}
	checkaok();
	openfile(fbuf);
	numb = filelength(fileno(filptr));
	numg = (int) (numb/GSIZE)+1;
	numrb = numb - (numg-1)*GSIZE;
	numrs = (int) (numrb/SECSIZE)+1;
	numrb -= (numrs-1)*SECSIZE;

	printf(" Copying %s to COCO disk in drive %c\n",fname,DRIVENAME);
	printf("    Type = %s",typetab[type]);
	printf("    Mode = %s",asciitab[mode]);
	printf("    %d Grans\n\n",numg);
	vec78h = (char far *(far *))(0x78);
				/* 0000:0078, MSDOS disk info pointer */
	bpsvec = 3 + *vec78h;	/* Get MSDOS bytes per sector ptr */

	nbsec = (int) *bpsvec;	/* Save byte for restore later */
	*bpsvec = (char) 1;	/* Set byte to 1 (256 bytes/sector) */

	fcount = 0;		/* initialize */
	grans = GRANS;

	cread(17,2,fat,1);

	for(cnt=0;cnt<GRANS;cnt++)
		if (*(fat+cnt)!=(char) 0xff) grans--;

	if (grans<numg) {
		printf(" Not enough space:  Only %d grans free\n\n",grans);
	} else {
	    for(scnt=0;scnt<9;scnt++) {	/* Main loop for directory read */
		cread(17,scnt+3,dirbuf,1);
		bufp = dirbuf;
		for(ecnt=0;ecnt<8;ecnt++) if (*bufp!=0xff) {
			if (*bufp==0) *bufp=0xff; else bufp+=DIRENT;
		}
		if (*bufp==0xff) break;
	    }
	    if (*bufp!=0xff) {
		printf("No more directory entries left.\n\n");
		exit(1);
	    }
	    lcnt = -1;
	    for(cnt=0;cnt<11;cnt++) *(bufp+cnt) = *(fbuf+cnt);
	    for(cnt=11;cnt<DIRENT;cnt++) *(bufp+cnt) = (char) 0;
	    *(bufp+11) = (char) type;
	    *(bufp+12) = (char) mode*0xff;
	    *(bufp+15) = (char) numrb;
	    for(cnt=0;cnt<GRANS;cnt++)
		if (*(fat+cnt)==(char) 0xff) {
		    if (lcnt<0) *(bufp+13)=(char) cnt;
		    	else	*(fat+lcnt)=(char) cnt;
		    lcnt=cnt;
		    if (numg > 1) {
			copygran(cnt);
		    } else {
			copylast(cnt,numrs);
			break;
		    }
		    numg--;
		}
	    *(fat+cnt)=(char) 0xc0+numrs;
	    cwrite(17,scnt+3,dirbuf,1);
	    cwrite(17,2,fat,1);
	    fclose(filptr);
	}
	*bpsvec = (char) nbsec;	/* Set byte to MSDOS value */
}

/* check if all are ok, and if not, set up compare buffer for comparisons */
checkaok()
{
	char far *pspfspec;
	int cnt;
	char *nbuf;

	FP_SEG(pspfspec) = _psp;
	FP_OFF(pspfspec) = 0x5d;

	filok = 0;
	filaok = 1;
	nbuf=fbuf;

	for(cnt=0;cnt<11;cnt++) {
		*nbuf = *pspfspec++;
		if (*nbuf!=' ') filok = 1;
		if (*nbuf++=='?') filaok = 0;
	}
	if (filaok==0) {
		printf("Wildcards not currently supported\n");
		exit(1);
	}
	if (filok==0) {
		printf("No file name given\n");
		exit(1);
	}
}

int cwrite(trk,sec,buf,cnt)
int trk,sec;
char *buf;
int cnt;
{
	int errcnt,seccnt;
	if (trk>39 || sec>18) return(-1);

	for(seccnt=0;seccnt<cnt;seccnt++) {
	    errcnt = 0;

	    while (errcnt<4) {
		inregs.x.ax = 0x0301;		/* 3 = write, Count=cnt */
		inregs.x.bx = (int) buf+SECSIZE*seccnt;
						/* pointer to buffer */
		inregs.x.cx = (int) (trk & 0xff)*256
				   +((sec+seccnt) & 0xff);
						/* Track , Sector */
		inregs.x.dx = DRIVENO;		/* Drive select byte */
		segregs.ds = FP_SEG(thisseg);
		segregs.es = FP_SEG(thisseg);

		int86x(0x13,&inregs,&outregs,&segregs);
		if (outregs.x.cflag==0) break;
		creset();
		errcnt++;
	    }
	    if (errcnt==4)
		printf(" Error writing track %d sector %d\n",trk,sec+seccnt);
	}
	return(0);
}

int cread(trk,sec,buf,cnt)
int trk,sec;
char *buf;
int cnt;
{
	int errcnt;
	char *temp;

	errcnt = 0;
	for(temp=buf;temp<buf+SECSIZE*cnt;temp++) *temp=0; /* zero buff */
	if (trk>39 || sec>18) return(-1);

	while (errcnt<4) {
		inregs.x.ax = 0x0200+(cnt & 0xff);/* 2 = read, Count=cnt */
		inregs.x.bx = (int) buf;	/* pointer to buffer */
		inregs.x.cx = (int) (trk & 0xff)*256
				   +(sec & 0xff);/* Track , Sector */
		inregs.x.dx = DRIVENO;		/* Drive select byte */
		segregs.ds = FP_SEG(thisseg);
		segregs.es = FP_SEG(thisseg);

		int86x(0x13,&inregs,&outregs,&segregs);
		if (outregs.x.cflag==0) break;
		creset();
		errcnt++;
	}
	return(errcnt);
}

int creset()
{
	inregs.x.ax = 0x0000;		/* 2 = read function, Count=cnt */
	inregs.x.dx = DRIVENO;		/* Drive select byte */
	segregs.ds = FP_SEG(thisseg);
	segregs.es = FP_SEG(thisseg);

	int86x(0x13,&inregs,&outregs,&segregs);
	return(outregs.x.cflag);
}

openfile(name)
char *name;
{
	int cnt;
	char *fptr;

	fptr = fname;

	for(cnt=0;cnt<8;cnt++) *fptr++ = *(name+cnt);	/* parse filename */
	while (*(fptr-1)==' ' && fptr>fname) fptr--;
	*fptr++ = '.';					/* put in '.'	  */
	for(cnt=8;cnt<11;cnt++) *fptr++ = *(name+cnt);
	while ((*(fptr-1)==' ' || *(fptr-1)=='.') && fptr>fname) fptr--;
	*fptr = 0;

	if ((filptr = fopen(fname,"rb"))==NULL) {
		printf("File not found\n");
		exit(1);
	}
}

copygran(gran)
int gran;
{
	int secstart;

	if (gran>=34) gran += 2;		/* adjust around directory */
	secstart = (gran & 1)*9+1;		/* Find start sector	   */
	gran /= 2;				/* Now is track pointer	   */
	fread(inbuff,GSIZE,1,filptr);		/* read in file from DOS   */
	cwrite(gran,secstart,inbuff,9);		/* write out file to coco  */
	cwrite(gran,secstart,inbuff,9);		/* I did have some write   */
						/* errors and this does	   */
						/* cure them, probably the */
						/* lack of delay on first  */
						/* write causes a problem  */
						/* anyone else?		   */
}

copylast(gran,nsec)
int gran,nsec;
{
	int secstart;

	if (gran>=34) gran+=2;			/* skip directory */
	secstart = (gran & 1)*9+1;		/* find sector	  */
	gran /= 2;				/* find track	  */

	fread(inbuff,GSIZE,1,filptr);		/* read source	  */

	cwrite(gran,secstart,inbuff,9);		/* write destination */
}
SHAR_EOF
chmod +x 'toc.c'
#	End of shell archive
exit 0