[net.micro.atari16] Double-sided disk format and fonts

apratt@atari.UUcp (Allan Pratt) (07/25/86)

Double-sided disks are in the same logical format as single-sided disks:
the logical sector numbers all have the same interpretation.  The difference
is physical: the sectors are laid out on alternating sides.  Logical sectors
0 to 8 are Track 0, Head 0, Sector 1 through Sector 9. Then you go to the
Head 1 for logical 9 to 17, and back to Head 0 on the next track for 18 to
24.  Since the disk is bigger, the FAT is usually larger, or the cluster
size larger, or both, so you should be careful to interpret the information
in the boot sector correctly (sectors per fat, sectors per cluster, etc).

About Fonts:  There was a font-loader accessory from a magazine (Antic?)
which actually overwrote the font information in RAM.  This worked only
under RAM TOS (obviously).  I am not familiar with font loaders for ROM
TOS, except that they exist.  Perhaps somebody else on the net can speak up?

/----------------------------------------------\
| Opinions expressed above do not necessarily  |  -- Allan Pratt, Atari Corp.
| reflect those of Atari Corp. or anyone else. |     ...lll-lcc!atari!apratt
\----------------------------------------------/

braner@batcomputer.TN.CORNELL.EDU (braner) (07/30/86)

[]

It was bound to happen: a disk went bad on me and I did not
have a backup.  Using MUTIL I could see that the data was there
but the second FAT was damaged.  Using the "file attributes"
option of MUTIL I wrote down the starting sector (or cluster)
of each file I wanted to recover. (These cluster and sector are
the two last columns in the directory-entry list of MUTIL).
(I also wrote down the length of the file for later reference.)
Then I used the following program to read each file and save it
on a different drive (a RAMdisk, in my case).  This program will
work for MSDOS, by the way.  Note that the program reads by clusters,
so you will have some extra junk at the end of the file. If it is a
text file, use a text editor to remove it. Then compare the length
with the original length you wrote down.
Some day I'll try to write a program that will intelligently search
the disk for the required starting-sector information.
(MUTIL did the job but is a PAIN.)

Note: this program assumes a standard disk. If you have a damaged,
"copy protected" disk, well, you get what you deserve for buying
one of those things...

-------------------------------------------------------------

/*
 * READFILE - a program to read a file from off a damaged disk.
 * Uses information in the FAT and the boot sector.
 * The first sector (or cluster) of the file must be known.
 *
 *   By Moshe Braner,  860730.
 */

#include <stdio.h>
#include <osbind.h>

#define TRUE	1
#define FALSE	0
#define READ	2	/* ignore disk changes	*/
#define DRIVEA	0	/* always read drive A	*/
#define SECSIZE	1024	/* for up to 1K sectors	*/

/* read unsigned bytes and Intel-style integers */
#define US(p,o)	(p[o]&0xFF)
#define UI(p,o)	(US(p,o)+256*US(p,o+1))

/* translate between sectors and clusters */
#define stc(s)	((s-fuds)/spc+2)
#define cts(c)	((c-2)*spc+fuds)

/* offsets into the boot sector */
#define BPS	11
#define SPC	13
#define RES	14
#define FAT	16
#define DIR	17
#define SPF	22

/* The CLUSTER data structure: a linked list */

typedef struct CLUSTER {
	struct CLUSTER	*link;		/* link to next			*/	
	char		*data;		/* pointer to data in cluster	*/
} CLUSTER;


/***  GLOBALS  ***/

int	bps, spc, res, fat, dir, spf, bpc, bpf, fuds;
char	*fat1, *fat2;
CLUSTER	*clhead = NULL;


error(msg)
	char msg[];
{
	printf(msg);
	Cconin();
	exit(0);
}

/* Read the boot sector */

readboot()
{
	register long	status;
	register int	c;
	char		boot[SECSIZE];

	printf("\nReading boot sector\n");
	status = Rwabs (READ, boot, 1, 0, DRIVEA);
	if (status != 0) {
		printf("\nError reading boot sector!\n");
		printf("Continue? (y/n) ");
		fflush(stdout);
		if ((c=(int)Cconin())!='y' && c!='Y')
			exit(0);
		/* else guess the parameters */
			bps = 512;
			spc = 2;	/* 1 for SS 5.25" disks		*/
			res = 1;
			fat = 2;
			dir = 112;	/* 64 for SS 5.25" disks	*/
			spf = 5;	/* 2 for 5.25" disks, incl DS	*/
	}
	bps = UI(boot, BPS);		/* bytes per sector		*/
	spc = US(boot, SPC);		/* sectors per cluster		*/
	res = US(boot, RES);		/* no. of reserved sec		*/
	fat = US(boot, FAT);		/* no. of FAT copies		*/
	dir = UI(boot, DIR);		/* no. of dir entries		*/
	spf = US(boot, SPF);		/* sectors per FAT		*/
	bpc = bps * spc;		/* bytes per cluster		*/
	bpf = bps * spf;		/* bytes per FAT		*/
	fuds = dir * 32;		/* length of directory in bytes */
	fuds /= bps;			/* in sectors			*/
	fuds += res + fat*spf;		/* first usable data sector	*/
}

/* read (both) FAT areas */

readfat()
{
	register long	status;
	register int	c;
	register int	err1 = FALSE;
	register int	err2 = FALSE;

	fat1 = malloc(spf*bps);
	if (fat1 == NULL)
		error("\nNot enough memory!\n");
	printf("\nReading FAT (#1)\n");
	status = Rwabs (READ, fat1, spf, res, DRIVEA);
	if (status != 0) {
		printf("\nError reading FAT #1.\n");
		err1 = TRUE;
	}
	if (fat > 1) {
		fat2 = malloc(spf*bps);
		if (fat2 == NULL)
			error("\nNot enough memory!\n");
		printf("\nReading FAT #2\n");
		status = Rwabs (READ, fat2, spf, res+spf, DRIVEA);
		if (status != 0) {
			printf("\nError reading FAT #2.\n");
			err2 = TRUE;
			fat2 = fat1;
		}
	} else {
		err2 = TRUE;
		fat2 = fat1;
	}
	if (err1==TRUE && err2==TRUE)
		error("\nCannot proceed without FAT!\n");
	if (err1)
		fat1 = fat2;
}

/* compare the 2 FATs */

compfats()
{
	register char	*p1, *p2;
	register int	i, c;

	if (fat1 == fat2)	/* only one FAT */
		return;

	p1 = fat1;
	p2 = fat2;
	for (i=0; i<bpf; i++) {
		if (*p1++ != *p2++) {
			printf("\n2 FATs differ at byte 0x%x!\n", i);
			printf("Which one to use? (1/2/0 to abort) ");
			fflush(stdout);
			if ((c=(int)Cconin()) == '0')
				exit(0);
			if (c == '2')
				fat1 = fat2;
			break;
		}
	}
}

/* read next cluster from FAT */

int
nxtclus(cluster)
	register int	cluster;
{
	register int	next, offset;

	offset = cluster*3;
	offset >>= 1;		/* offset into FAT = 3/2 * cluster	*/
	next = UI(fat1, offset);
	if (cluster&0x01)	/* if cluster no. odd			*/
		next >>= 4;
	next &= 0xFFF;		/* the 12-bit value of FAT entry	*/
	return (next);
}

/* allocate memory for another cluster */

CLUSTER *
clspace(clp)
	CLUSTER *clp;
{
	register CLUSTER	*ncp;
	register char		*dp;

	dp = malloc(bpc);
	if (dp == NULL)
		return (NULL);
	ncp = (CLUSTER *) malloc(sizeof(CLUSTER));
	if (ncp == NULL) {
		free(dp);
		return (NULL);
	} else {
		if (clp == NULL)
			clhead = ncp;
		else
			clp->link = ncp;
		ncp->link = NULL;
		ncp->data = dp;
	}
	return (ncp);
}

main()
{
	register int	i, c;
	register char	*dp;
	int	sknown, sector, cluster;
	long	status;
	CLUSTER	*clp;
	FILE	*fp;
	char	filename[80];

	printf("\nREADFILE   MB 8607\n\n");
	printf("Insert disk in drive A, press return: ");
	fflush(stdout);
	while ((c=(int)Cconin())!='\n' && c!='\r');
	readboot();
	readfat();
	compfats();
loop:
	printf("\nDo you know the cluster or sector number? (c/s) ");
	fflush(stdout);
	if (Cconis())
		Crawcin();
	if ((c=(int)Cconin())!='c' && c!='C')
		sknown = TRUE;
	else
		sknown = FALSE;
	printf("\nFirst cluster or sector of file: ");
	fflush(stdout);
	scanf("%d", &sector);
	if (sknown)
		cluster = stc(sector);
	else {
		cluster = sector;
		sector = cts(cluster);
	}
	clp = NULL;
	
	do {			/*  while not EOF */
		clp = clspace(clp);
		if (clp == NULL)
			error("\nNot enough memory!\n");
		printf("reading cluster at logical sector #%d\n", sector);
		status = Rwabs(READ, clp->data, spc, sector, DRIVEA);
		if (status != 0) {
			printf("\nRead error on data cluster!\n");
			printf("Continue? (y/n) ");
			fflush(stdout);
			if ((c=(int)Cconin())!='y' && c!='Y')
				exit(0);
		}
		cluster = nxtclus(cluster);
		sector = cts(cluster);
	} while (cluster < 0x0FF1);	/* not EOF */

	clp = clhead;
	if (clp == NULL)
		error("\nNothing read!\n");
	printf("\nPathname to save as: ");
	fflush(stdout);
	scanf("%s", filename);
	fp = fopen(filename, "bw");	/* binary write (Megamax) */
	if (fp == NULL)
		error("\nError opening file!\n");
	while (clp != NULL) {
		dp = clp->data;
		for(i=0; i<bpc; i++) {
			c = *dp++;
			putc(c, fp);
		}
		clp = clp->link;
	}
	fclose(fp);

	printf("Read another file? (y/n) ");
	fflush(stdout);
	if ((c=(int)Cconin())=='y' || c=='Y')
		goto loop;
}

------------------------------------------------------------

- Moshe Braner

Corson Hall, Cornell University, Ithaca NY 14853
(607) 272-3487

For electronic mail, my address is:

	braner@amvax.tn.cornell.edu   (ARPANET)
	braner%amvax.tn.cornell.edu@WISCVM.BITNET (Bitnet)
	{decvax,ihnp4,cmcl2,vax135}!cornell!amvax!braner (USENET)