Article 5454 of alt.sources: Xref: chemabs alt.sources:5454 comp.sys.apple2:35097 Newsgroups: alt.sources,comp.sys.apple2 Path: chemabs!malgudi.oar.net!caen!destroyer!ubc-cs!phillips From: phillips@cs.ubc.ca (George Phillips) Subject: afid - Apple DOS 3.3 disk image utility Message-ID: <1992Jun17.230439.7880@cs.ubc.ca> Sender: usenet@cs.ubc.ca (Usenet News) Organization: Computer Science, University of B.C., Vancouver, B.C., Canada Date: Wed, 17 Jun 92 23:04:39 GMT afid is a utility which allows you to manipulate Apple ][ DOS 3.3 disk images. To wit, you can read and write files to and from a disk image. The documentation is right at the start of the shar archive so read on for more details. #! /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: # afid.doc # afid.c # This archive created: Wed Jun 17 16:01:47 1992 export PATH; PATH=/bin:$PATH if test -f 'afid.doc' then echo shar: will not over-write existing file "'afid.doc'" else cat << \SHAR_EOF > 'afid.doc' This file describes version 1.0 of afid. The latest version should be available for anonymous FTP from cs.ubc.ca in /ftp/local/src/afid. afid is a utility which allows you to manipulate an Apple ][ DOS 3.3 disk image. It currently doesn't handle Prodos disk images and only reads and writes 35 track, 16 sector diskette images. afid provides a simple command line interface. You start it up by giving it the name of a disk image: afid file.dsk It then reads the disk image into memory, does a few sanity checks and gives you the "] " prompt. You can use the following commands: catalog - show a list of the files on the disk image free - display a map of free sectors and the number of free sectors quit, exit - leave afid binary - no translation of file data is done during copies (this is the default) text - CR <-> LF and Apple <-> ASCII translations are done to file data reorder - switch disk sector order between DOS 3.3 (default) and Prodos read file - copy the given file from the disk image to a UNIX file write file - copy the given UNIX file onto the disk image save file-image - save the in-memory disk image to the given UNIX file Notes: "save" is important, and changes made with "write" will not be permanent until you save the disk image. File names are translated to all uppercase on the Apple disk and to all lowercase on the UNIX end. This is convenient, but could be annoying. I'm not sure that the text translation is done correctly. I just empirically judged that twiddling the top bit was the right thing to do. Binary file copy is a little hokey; pay no mind to the extra stuff it spits out at you. Disk block ordering can be a little tricky. Basically, if you make the disk image on a machine running Prodos, you'll have to re-order the sectors. This means that probably anything extracted from a Shrinkit archive will need to be reordered. It can be hard to tell since the most obvious symptom is a shortened catalog which is not a lot different from a normally short catalog. The free command spits out some crap about the sector mask. You can ignore it, but knowledgable folks could help me out. I gather that the mask allows you to specify sectors which cannot be used, but I've never seen a mask that was usable. What's wrong? I've compiled afid on a Sun sparcstation with gcc and on a PC with Turbo-C 2.0. It should be fairly portable if your machine has enough memory. I hope this inspires someone to create a Prodos version. Things to do: The most obvious extension is file operations such as type changing and deletion. If the disk fills up, you're left with a partial file on the disk. The file system is still stable at that point, but not deleting the failed file is pretty messy. File reads should do something smart like paying attention to the file type. A warning that the disk blocks are out of order could be put in. Options to switch the file-name mapping on and off would be nice. A file-duplicating mechanism could be useful if you ever should run into files with holes. Sector allocation is not done in the same way as DOS 3.3 (not so bad) and may not be very efficient at times (not so good). The code could use some cleaning up, for sure. I'd try and completely localize things like the VTOC accesses and come up with a few master directory scanning and file scanning loops instead of duplicating the code here and there. I'm looking towards getting a Deluxe Option Board for my PC. I know it can read apple ][ disks into bit images. Should I get the board and can figure out the format of those bit images, you can bet afid will be enhanced. This should provide a very nice file transfer path: diskette -> bit file image -> afid -> bit file image -> diskette. This is pretty plausible, but real pie in the sky would be a program that could work directly with the apple ][ disk on your PC with the option board. Probably won't happen as I doubt programming information on the option board will be easy to find. SHAR_EOF fi # end of overwriting check if test -f 'afid.c' then echo shar: will not over-write existing file "'afid.c'" else cat << \SHAR_EOF > 'afid.c' /* * afid -- like the Apple ][ program called FID, a file handling utility * * We operate on files which contain entire Apple ][ diskettes from * track 0, sector 0 to track 34, sector 15. To keep things simple, * the entire disk image is slurped into memory. I assume the standard * disk geometry of 35 tracks and 16 sectors when initializing, but * otherwise the code should operate on different geometries. Different * geometries are a chicken and egg problem as the geometry info is * defined to be on track 0x11, sector 0, but you need the sector size * to figure out where track 0x11 is! I have tried to make reasonable * extrapolations to account for different geometry sizes (e.g., put * as many directory entries per sector as possible). * * Since I'll want to run this on an MS-DOS machine, I am careful to * allocate memory in small chunks, use an ANSI C compiler and use * longs when the numbers will get bigger than 16 bits (i.e., the * number of bytes per disk). * * This is version 1.0. * * Author: * George Phillips * Department of Computer Science * University of British Columbia */ #include #include struct track { unsigned char** sector; }; struct disk { int bytes_per_sector; int sectors_per_track; int tracks_per_disk; struct track* track; }; /* handy disk address macros for getting at particular bytes and words */ #define byteat(d, t, s, n) ((d)->track[(t)].sector[(s)][(n)]) #define wordat(d, t, s, n) (byteat(d, t, s, n) + byteat(d, t, s, (n) + 1) * 256) void build_disk_mem(struct disk*); void read_disk(struct disk*, FILE*); void verify_geometry(struct disk*); void catalog(struct disk*); int apptoascii(int), asciitoapp(int); void* alloc(int n); void show_freemap(struct disk*); int isfree(struct disk*, int, int); int allocate_sector(struct disk*, int*, int*, int); void alloc_sector(struct disk*, int, int); void free_sector(struct disk*, int, int); void zero_sector(struct disk*, int, int); struct afile { int filetype; struct disk* d; int ts_s, ts_t, ts_ent, dt_t, dt_s, dt_pos; int eof, secgroup, dir_t, dir_s, dir_off; }; int aopen(struct disk*, char*, struct afile*, char*); int aclose(struct afile*); int agetc(struct afile*); int aputc(struct afile*, int); long agetw(struct afile*); main(int argc, char* argv[]) { struct disk d; FILE* fp; struct afile af; int textmode = 0; switch (argc) { default: fprintf(stderr, "usage: afid disk-image-file\n"); exit(1); case 2: if (!(fp = fopen(argv[1], "rb"))) { perror("fopen"); fprintf(stderr, "couldn't open %s\n", argv[1]); exit(1); } break; /* this doesn't work with my current command line interface */ /* case 1: fp = stdin; break; */ } d.bytes_per_sector = 256; d.sectors_per_track = 16; d.tracks_per_disk = 35; build_disk_mem(&d); read_disk(&d, fp); verify_geometry(&d); for (;;) { char *cmd, buf[1024], *p, *arg; printf("] "); fflush(stdout); if (!fgets(buf, 1024, stdin)) break; for (p = buf; *p && isspace(*p); p++) ; cmd = p; for (; *p && !isspace(*p); p++) ; if (*p) *p++ = '\0'; for (; *p && isspace(*p); p++) ; arg = p; for (; *p && *p != '\n'; p++) ; *p = '\0'; if (!*cmd) continue; if (!strcmp(cmd, "catalog")) catalog(&d); else if (!strcmp(cmd, "quit") || !strcmp(cmd, "exit")) break; else if (!strcmp(cmd, "free")) show_freemap(&d); else if (!strcmp(cmd, "text")) textmode = 1; else if (!strcmp(cmd, "binary")) textmode = 0; else if (!strcmp(cmd, "reorder")) { int t, s; void* temp; for (t = 0; t < d.tracks_per_disk; t++) { for (s = 1; s < d.sectors_per_track / 2; s++) { temp = d.track[t].sector[s]; d.track[t].sector[s] = d.track[t].sector[d.sectors_per_track - s - 1]; d.track[t].sector[d.sectors_per_track - s - 1] = temp; } } } else if (!strcmp(cmd, "read")) { FILE* fp; fp = fopen(arg, textmode ? "w" : "wb"); for (p = arg; *p; p++) { if (islower(*p)) *p = toupper(*p); if (*p == '/') arg = p + 1; } if (!aopen(&d, arg, &af, textmode ? "rt" : "rb")) fprintf(stderr, "couldn't open ][ file %s\n", arg); else { int n, ch; long start, len; if (!textmode) { printf("%s is a type %c file\n", arg, af.filetype); start = agetw(&af); len = agetw(&af); if (fp) { fputc(start & 255, fp); fputc((start >> 8) & 255, fp); fputc(len & 255, fp); fputc((len >> 8) & 255, fp); } printf("it is %ld bytes long (starts at %d)\n", len, start); n = 4; while ((ch = agetc(&af)) != EOF) { if (fp) fputc(ch, fp); n++; } printf("or is that %d bytes long\n", n); } else { while ((ch = agetc(&af)) != EOF) { if (ch == '\0') break; ch = apptoascii(ch); if (ch == '\r') ch = '\n'; if (fp) fputc(ch, fp); } } if (fp) fclose(fp); if (aclose(&af) == EOF) fprintf(stderr, "error on closing ][ file %s\n", arg); } } else if (!strcmp(cmd, "write")) { if (!(fp = fopen(arg, textmode ? "r" : "rb"))) fprintf(stderr, "couldn't open unix file %s\n", arg); else { for (p = arg; *p; p++) { if (islower(*p)) *p = toupper(*p); if (*p == '/') arg = p + 1; } if (!aopen(&d, arg, &af, textmode ? "wt" : "wb")) { fprintf(stderr, "couldn't open ][ file %s\n", arg); fclose(fp); } else { int ch; if (!textmode) { while ((ch = fgetc(fp)) != EOF) { if (aputc(&af, ch) == EOF) { fprintf(stderr, "error on write to ][ file %s\n", arg); break; } } } else { while ((ch = fgetc(fp)) != EOF) { if (ch == '\n') ch = '\r'; ch = asciitoapp(ch); if (aputc(&af, ch) == EOF) { fprintf(stderr, "error on write to ][ file %s\n", arg); break; } } /* HACK! :-( check that error code! */ aputc(&af, '\0'); } if (aclose(&af) == EOF) fprintf(stderr, "error on closing ][ file %s\n", arg); } } } else if (!strcmp(cmd, "save")) { int t, s; if (!(fp = fopen(arg, "wb"))) fprintf(stderr, "can't write to unix disk image %s\n", arg); else { for (t = 0; t < d.tracks_per_disk; t++) { for (s = 0; s < d.sectors_per_track; s++) { if (fwrite(d.track[t].sector[s], d.bytes_per_sector, 1, fp) != 1) { fprintf(stderr, "error while writing unix disk image %s\n", arg); fclose(fp); fp = 0; t = d.tracks_per_disk; break; } } } if (fp && fclose(fp) == EOF) fprintf(stderr, "error while writing unix disk image %s\n", arg); } } else fprintf(stderr, "command %s not understood\n", cmd); } fclose(fp); exit(0); } /* allocate memory for the disk structure based on the geometry fields */ void build_disk_mem(struct disk* d) { int t, s; void* secmem; d->track = alloc(sizeof(struct track) * d->tracks_per_disk); for (t = 0; t < d->tracks_per_disk; t++) { secmem = alloc(d->bytes_per_sector * d->sectors_per_track); d->track[t].sector = alloc(sizeof(unsigned char*) * d->sectors_per_track); for (s = 0; s < d->sectors_per_track; s++) #ifdef __TURBOC__ d->track[t].sector[s] = (char*)secmem + s * d->bytes_per_sector; #else d->track[t].sector[s] = secmem + s * d->bytes_per_sector; #endif } } /* slurp all the sectors from the disk image into memory */ void read_disk(struct disk* d, FILE* fp) { int t, s; char c; for (t = 0; t < d->tracks_per_disk; t++) { for (s = 0; s < d->sectors_per_track; s++) { if (fread(d->track[t].sector[s], d->bytes_per_sector, 1, fp) != 1) { fprintf(stderr, "EOF while reading disk!\n"); exit(1); } } } if (fread(&c, 1, 1, fp) == 1) fprintf(stderr, "warning: extra data in disk-image\n"); } /* do a sanity check between our pre-defined geometry and the geometry * information in the volume table of contents (VTOC, track 0x11, sector 0). * Print the DOS version number of the disk, just for fun. */ void verify_geometry(struct disk* d) { int tracks_per_disk, sectors_per_track, bytes_per_sector; tracks_per_disk = byteat(d, 0x11, 0, 0x34); sectors_per_track = byteat(d, 0x11, 0, 0x35); bytes_per_sector = wordat(d, 0x11, 0, 0x36); printf("DOS release number: %d\n", byteat(d, 0x11, 0, 3)); if (tracks_per_disk != d->tracks_per_disk || sectors_per_track != d->sectors_per_track || bytes_per_sector != d->bytes_per_sector) { fprintf(stderr, "afid: warning: initial disk geometry isn't the same as the disk geometry\n"); fprintf(stderr, "information that is stored on the disk.\n"); fprintf(stderr, "initial: %d tracks, %d sectors, %d bytes per sector\n", d->tracks_per_disk, d->sectors_per_track, d->bytes_per_sector); fprintf(stderr, "on disk: %d tracks, %d sectors, %d bytes per sector\n", tracks_per_disk, sectors_per_track, bytes_per_sector); } } /* print out what sectors have been allocated */ void show_freemap(struct disk* d) { int s, t, tens, prev, total, allocated; printf("Sectors Tracks 0 -> %d, * = sector allocated:\n", d->tracks_per_disk); printf(" "); for (t = 0, prev = 0; t < d->tracks_per_disk; t++) { tens = (t / 10) % 10; printf("%c", tens == prev ? ' ' : '0' + tens); prev = tens; } printf("\n "); for (t = 0; t < d->tracks_per_disk; t++) { printf("%d", t % 10); } printf("\n"); total = d->tracks_per_disk * d->sectors_per_track; allocated = 0; for (s = 0; s < d->sectors_per_track; s++) { printf("%2d ", s); for (t = 0; t < d->tracks_per_disk; t++) { if (isfree(d, t, s)) putchar(' '); else { putchar('*'); allocated++; } } printf("\n"); } printf("%d total sectors, %d allocated, %d free\n", total, allocated, total - allocated); printf("sector mask: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x %s\n", byteat(d, 0x11, 0, 0x30), byteat(d, 0x11, 0, 0x31), byteat(d, 0x11, 0, 0x32), byteat(d, 0x11, 0, 0x33), "is this the reasonable 0xffff0000?"); } /* display the directory of files on the disk */ void catalog(struct disk* d) { int t, s, nt, ns, dir, filetype, i, eod; t = 0x11; s = 0; printf("disk volume %d\n", byteat(d, t, s, 6)); while ((nt = byteat(d, t, s, 1)) && (ns = byteat(d, t, s, 2))) { t = nt; s = ns; eod = 0; for (dir = 0xb; dir + 35 <= d->bytes_per_sector; dir += 35) { if (byteat(d, t, s, dir) == 0xff) continue; if (byteat(d, t, s, dir) == 0) { eod = 1; break; } filetype = byteat(d, t, s, dir + 2); putchar((filetype & 0x80) ? '*' : ' '); switch (filetype & 0x7f) { case 0: putchar('T'); break; case 1: putchar('I'); break; case 2: putchar('A'); break; case 4: putchar('B'); break; default: putchar('?'); break; } printf(" %03d ", wordat(d, t, s, dir + 0x21)); for (i = 3; i < 0x20; i++) putchar(apptoascii(byteat(d, t, s, dir + i))); putchar('\n'); } if (eod) break; } } /* aopen -- open an Apple ][ file (for reading only), return !0 if successful */ /* would be nice to share directory reading stuff with catalog() */ /* I assume that I cannot extend the directory and that the entire directory * has been pre-allocated and pre-initialized by Apple DOS. I also try * to initialize things in the same way that Apple DOS would (e.g., every * file will contain at least 2 sectors). */ int aopen(struct disk* d, char* filename, struct afile* af, char* mode) { int t, s, nt, ns, ft, fs, dir, i, eod, found; int firstfree, fft, ffs, ffoff; char* p; t = 0x11; s = 0; found = 0; firstfree = 0; while ((nt = byteat(d, t, s, 1)) && (ns = byteat(d, t, s, 2))) { t = nt; s = ns; eod = 0; for (dir = 0xb; dir + 35 <= d->bytes_per_sector; dir += 35) { if (byteat(d, t, s, dir) == 0xff) { if (!firstfree) { firstfree = 1; fft = t; ffs = s; ffoff = dir; } continue; } if (byteat(d, t, s, dir) == 0) { if (!firstfree) { firstfree = 1; fft = t; ffs = s; ffoff = dir; } eod = 1; break; } found = 1; for (p = filename, i = 3; *p && i < 0x20; i++, p++) { if (*p != apptoascii(byteat(d, t, s, dir + i))) { found = 0; break; } } if (found) { for (; i < 0x20; i++) { if (apptoascii(byteat(d, t, s, dir + i)) != ' ') found = 0; } } if (found) break; } if (eod || found) break; } if (!found) { int tt, ts, fs, ft; char* p; if (*mode == 'r' || !firstfree) return 0; if (!allocate_sector(d, &tt, &ts, -1)) return 0; if (!allocate_sector(d, &ft, &fs, t)) { free_sector(d, tt, ts); return 0; } zero_sector(d, tt, ts); zero_sector(d, ft, fs); /* fill in directory entry */ byteat(d, fft, ffs, ffoff) = tt; byteat(d, fft, ffs, ffoff + 1) = ts; byteat(d, fft, ffs, ffoff + 2) = mode[1] == 'b' ? 4 : 0; for (i = 3; i <= 0x20; i++) byteat(d, fft, ffs, ffoff + i) = asciitoapp(' '); for (i = 3, p = filename; *p; i++, p++) byteat(d, fft, ffs, ffoff + i) = asciitoapp(*p); byteat(d, fft, ffs, ffoff + 0x21) = 2; byteat(d, fft, ffs, ffoff + 0x22) = 0; /* fill in first track + sector list sector */ byteat(d, tt, ts, 1) = 0; byteat(d, tt, ts, 2) = 0; byteat(d, tt, ts, 5) = 0; byteat(d, tt, ts, 6) = 0; byteat(d, tt, ts, 0xc) = ft; byteat(d, tt, ts, 0xd) = fs; /* fudge some values so the fallthough will be right */ t = fft; s = ffs; dir = ffoff; } /* enforce write protection on files */ if (*mode == 'w' && (byteat(d, t, s, dir +2) & 0x80)) return 0; switch (byteat(d, t, s, dir + 2) & 0x7f) { case 0: af->filetype = 'T'; break; case 1: af->filetype = 'I'; break; case 2: af->filetype = 'A'; break; case 4: af->filetype = 'B'; break; default: af->filetype = '?'; break; } /* note which disk this file is on */ af->d = d; /* keep track of where we are in the track/sector list */ af->ts_t = byteat(d, t, s, dir); af->ts_s = byteat(d, t, s, dir + 1); /* initialize so that first read initializes other stuff */ af->ts_ent = -1; af->dt_pos = d->bytes_per_sector; /* these variables are only used by the writing routines */ af->eof = 0; af->secgroup = 0; af->dir_t = t; af->dir_s = s; af->dir_off = dir; return 1; } /* close the apple ][ file */ /* returns 0 on success, EOF on failure */ /* For now, there is nothing to do, but later versions may require this * in order to fix up the disk (i.e., because of sector pre-allocation). * Also, other versions may wish to truncate the file! */ int aclose(struct afile* af) { return 0; } /* get a character from the open apple ][ file */ /* return the character or EOF on end of file */ int agetc(struct afile* af) { int ch, t, s; if (af->ts_t == 0 && af->ts_s == 0) return EOF; if (af->dt_pos >= af->d->bytes_per_sector) { af->dt_pos = 0; if (++af->ts_ent >= byteat(af->d, 0x11, 0, 0x27)) { af->ts_ent = 0; t = byteat(af->d, af->ts_t, af->ts_s, 1); s = byteat(af->d, af->ts_t, af->ts_s, 2); af->ts_t = t; af->ts_s = s; if (t == 0 && s == 0) return EOF; } af->dt_t = byteat(af->d, af->ts_t, af->ts_s, 12 + af->ts_ent * 2); af->dt_s = byteat(af->d, af->ts_t, af->ts_s, 12 + af->ts_ent * 2 + 1); } if (af->dt_t == 0 && af->dt_s == 0) return EOF; ch = byteat(af->d, af->dt_t, af->dt_s, af->dt_pos); af->dt_pos++; return ch; } /* put a character into the open apple ][ file */ /* returns either the character written or EOF if some error occured */ /* I don't keep enough information around to give really good hints * about where to allocate the next sector. No big deal, really. */ int aputc(struct afile* af, int ch) { int t, s; if (af->eof) return EOF; if (af->dt_pos >= af->d->bytes_per_sector) { af->dt_pos = 0; if (++af->ts_ent >= byteat(af->d, 0x11, 0, 0x27)) { af->ts_ent = 0; t = byteat(af->d, af->ts_t, af->ts_s, 1); s = byteat(af->d, af->ts_t, af->ts_s, 2); af->secgroup += byteat(af->d, 0x11, 0, 0x27); if (t == 0 && s == 0) { if (!allocate_sector(af->d, &t, &s, af->ts_t)) { af->eof = 1; return EOF; } byteat(af->d, af->dir_t, af->dir_s, af->dir_off + 0x21)++; byteat(af->d, af->ts_t, af->ts_s, 1) = t; byteat(af->d, af->ts_t, af->ts_s, 2) = s; zero_sector(af->d, t, s); byteat(af->d, t, s, 5) = af->secgroup & 255; byteat(af->d, t, s, 6) = (af->secgroup >> 8) & 255; } af->ts_t = t; af->ts_s = s; } af->dt_t = byteat(af->d, af->ts_t, af->ts_s, 12 + af->ts_ent * 2); af->dt_s = byteat(af->d, af->ts_t, af->ts_s, 12 + af->ts_ent * 2 + 1); } if (af->dt_t == 0 && af->dt_s == 0) { if (!allocate_sector(af->d, &af->dt_t, &af->dt_s, af->ts_t)) { af->eof = 1; return EOF; } zero_sector(af->d, af->dt_t, af->dt_s); byteat(af->d, af->dir_t, af->dir_s, af->dir_off + 0x21)++; byteat(af->d, af->ts_t, af->ts_s, 12 + af->ts_ent * 2) = af->dt_t; byteat(af->d, af->ts_t, af->ts_s, 12 + af->ts_ent * 2 + 1) = af->dt_s; } byteat(af->d, af->dt_t, af->dt_s, af->dt_pos) = ch; af->dt_pos++; return ch; } /* get a 2 byte word (little endian, of course) from an apple ][ file */ long agetw(struct afile* af) { int b1, b2; if ((b1 = agetc(af)) == EOF || (b2 = agetc(af)) == EOF) return EOF; return b1 + b2 * 256; } /* take an Apple II character code and convert it to ASCII */ /* the current implementation is workable but NOT right */ int apptoascii(int c) { return c & 0x7f; } /* take an ASCII character code and convert it to Apple II code */ /* like apptoascii(), this is just a workable but NOT right implementation */ int asciitoapp(int c) { return c | 0x80; } /* sector allocation + de-allocation routines */ /* return non-zero if the given sector has not been allocated, 0 if free */ /* this routine is quite dependent on 16 sectors per track */ int isfree(struct disk* d, int t, int s) { return byteat(d, 0x11, 0, 0x38 + t * 4 + (s < 8)) & /* free map */ (1 << (s & 7)); /* right bit in map */ /* This gives the sector mask which should be ANDed into the above, but the sector masks on disks I've seen seem bogus (they should be 0xff) but typically have only 1 or 2 bits set(!) byteat(d, 0x11, 0, 0x30 + (s < 8)) */ } /* allocate a sector for use. If pref_track is valid, the sector will * be allocated from the given track, if possible. If pref_track is * is invalid (e.g., < 0), then a search is made for an empty track * and if none is found then we just look for the first sector we can * find. * * As with the other sector routines, 16 sectors per track is hard-coded. */ int allocate_sector(struct disk* d, int* t, int* s, int pref_track) { int i, j, found_track; if (pref_track < 0 || pref_track >= d->tracks_per_disk) { found_track = 0; for (i = 0; i < d->tracks_per_disk; i++) { if (byteat(d, 0x11, 0, 0x38 + i * 4) == 0xff && byteat(d, 0x11, 0, 0x38 + i * 4 + 1) == 0xff) { found_track = 1; break; } } if (found_track) { alloc_sector(d, *t = i, *s = 15); return 1; } } else { for (i = d->sectors_per_track - 1; i >= 0; i--) { if (isfree(d, pref_track, i)) { alloc_sector(d, *t = pref_track, *s = i); return 1; } } } for (i = 0; i < d->tracks_per_disk; i++) { for (j = d->sectors_per_track - 1; j >= 0; j--) { if (isfree(d, i, j)) { alloc_sector(d, *t = i, *s = j); return 1; } } } return 0; } /* make the given sector as in use */ void alloc_sector(struct disk* d, int t, int s) { byteat(d, 0x11, 0, 0x38 + t * 4 + (s < 8)) &= ~ (1 << (s & 7)); } /* mark the given sector as free */ void free_sector(struct disk* d, int t, int s) { byteat(d, 0x11, 0, 0x38 + t * 4 + (s < 8)) |= 1 << (s & 7); } /* clear an entire sector to zeros */ void zero_sector(struct disk* d, int t, int s) { int i; for (i = 0; i < d->bytes_per_sector; i++) byteat(d, t, s, i) = 0; } void* alloc(int n) { void* p; extern void* malloc(int n); if (!(p = malloc(n))) { fprintf(stderr, "afid: out of memory\n"); exit(1); } return p; } SHAR_EOF fi # end of overwriting check # End of shell archive exit 0