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", §or); 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)