/* fdvol.c - A stripped down version of the MINIX vol program. It takes * a file and breaks into into floppy-sized chunks, writing each one raw * to a floppy. * * Usage: fdvol file drive KB [slow] * * Examples: fdvol 1440 a: foo.taz # 1.4 MB floppy a: * fdvol 1200 b: foo.taz # 1.2 MB floppy b: * fdvol slow 360 a: foo.taz # old machine * fdvol 1440 a: foo bar # concatenate * * The optional 'slow' parameter forces the program to write in units of 3 * sectors. Folk tradition has it that this works around some buggy BIOSes. * * This code borrows heavily from Mark Becker's RaWrite program. */ #include #include #include #include #include #include #define TEST 0 #if !TEST #include #include #include #include #include #endif #define FALSE 0 #define TRUE (!FALSE) #define SECTORSIZE 512 #define RESET 0 #define LAST 1 #define READ 2 #define WRITE 3 #define VERIFY 4 #define FORMAT 5 int done; char buffer[18*SECTORSIZE]; /* do I/O in units of up to 18 sectors */ char testbuf[SECTORSIZE]; /* to do a test read of the first sector */ int handler(void) { /* Catch CTRL-C and CTRL-Break. */ done = 1; return(0); } void msg(char (*s)) { /* Print an error message and quit. */ fprintf(stderr, "%s\n", s); _exit(1); } void Error(int status, int cyl, int head, int sector) { /* Identify the error code with a real error message. */ fprintf(stderr, "\nError occured while writing cyl %d, head=%d, sector=%d\n", cyl,head,sector+1); switch (status) { case 0x00: msg("Operation Successful"); break; case 0x01: msg("Bad command"); break; case 0x02: msg("Address mark not found"); break; case 0x03: msg("Attempt to write on write-protected disk"); break; case 0x04: msg("Sector not found"); break; case 0x05: msg("Reset failed (hard disk)"); break; case 0x06: msg("Disk changed since last operation"); break; case 0x07: msg("Drive parameter activity failed"); break; case 0x08: msg("DMA overrun"); break; case 0x09: msg("Attempt to DMA across 64K boundary"); break; case 0x0A: msg("Bad sector detected"); break; case 0x0B: msg("Bad track detected"); break; case 0x0C: msg("Unsupported track"); break; case 0x10: msg("Bad CRC/ECC on disk read"); break; case 0x11: msg("CRC/ECC corrected data error"); break; case 0x20: msg("Controller has failed"); break; case 0x40: msg("Seek operation failed"); break; case 0x80: msg("Attachment failed to respond"); break; case 0xAA: msg("Drive not ready (hard disk only"); break; case 0xBB: msg("Undefined error occurred (hard disk only)"); break; case 0xCC: msg("Write fault occurred"); break; case 0xE0: msg("Status error"); break; case 0xFF: msg("Sense operation failed"); break; } exit(1); } void main(int argc, char *argv[]) { int disknr = 1, fdin, count, drive, head, cyl, status, sector; int max_cyl, chunk, nsectors, ct; long offset, drive_size, r, cyl_size; char *p, c; int slow; int kbsize; char **files; int nfiles, i; #if !TEST /* Catch breaks. */ ctrlbrk(handler); #endif #if 0 /* Do we have to clear the screen? */ fprintf(stderr, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); #endif if (argc > 1 && strcmp(argv[1], "slow") == 0) { /* Lousy BIOS? */ slow = 1; argc--; argv++; } else { slow = 0; } /* Check the arguments for validity. */ if (argc < 4) msg("Usage: fdvol [slow] #kilobytes drive-letter file1 [file2 ...]"); kbsize = atoi(argv[1]); p = argv[2]; c = *p; if (c == 'a' || c == 'A') drive = 0; else if (c == 'b' || c == 'B') drive = 1; else msg("fdvol: Second parameter must be drive, either a: or b:"); files = argv + 3; nfiles = argc - 3; switch(kbsize) { case 360: cyl_size = 9*2*SECTORSIZE; /* bytes/cylinder */ max_cyl = 39; /* zero-based counting */ drive_size = cyl_size * (max_cyl+1); chunk = (!slow ? 9 * SECTORSIZE : 3 * SECTORSIZE); nsectors = chunk/SECTORSIZE; break; case 720: cyl_size = 9*2*SECTORSIZE; /* bytes/cylinder */ max_cyl = 79; /* zero-based counting */ drive_size = cyl_size * (max_cyl+1); chunk = (!slow ? 9 * SECTORSIZE : 3 * SECTORSIZE); nsectors = chunk/SECTORSIZE; break; case 1200: cyl_size = 15*2*SECTORSIZE; /* bytes/cylinder */ max_cyl = 79; /* zero-based counting */ drive_size = cyl_size * (max_cyl+1); chunk = (!slow ? 15 * SECTORSIZE : 3 * SECTORSIZE); nsectors = chunk/SECTORSIZE; break; case 1440: cyl_size = 18*2*SECTORSIZE; /* bytes/cylinder */ max_cyl = 79; /* zero-based counting */ drive_size = cyl_size * (max_cyl+1); chunk = (!slow ? 18 * SECTORSIZE : 3 * SECTORSIZE); nsectors = chunk/SECTORSIZE; break; default: msg("fdvol: First parameter must be one of: 360, 720, 1200, or 1440"); } #if !TEST biosdisk(RESET, drive, 0, 0, 0, 0, testbuf); #endif /* * Start writing data to diskette until there is no more data to write. * Optionally read and write in units of 3 sectors. Folk tradition says * that this makes fewer buggy BIOSes unhappy than doing a whole track at a * time. */ offset = 0; i = 0; fdin = -1; while(1) { if (done > 0) { if (done == 1) msg("User abort"); #if !TEST biosdisk(READ, drive, 0, 0, 1, 1, testbuf); /* Retract head */ #endif fprintf(stderr, "Done. \n"); exit(done == 1 ? 1 : 0); } /* Until a chunk is read. */ count = 0; while (count < chunk) { if (fdin == -1) { /* open next file */ #if !TEST _fmode = O_BINARY; #endif fdin = open(files[i], O_RDONLY); if (fdin < 0) { perror(files[i]); exit(1); } } /* read from file */ ct = read(fdin, buffer + count, chunk - count); if (ct < 0) { perror(files[i]); exit(1); } if (ct == 0) { /* end of file */ close(fdin); fdin = -1; /* choose next file */ if (++i >= nfiles) break; /* no more files */ } count += ct; } if (count == 0) { /* absolute EOF */ done = 2; continue; } if (count < chunk) { /* pad last track */ /* Pad out buffer with zeroes. */ p = &buffer[count]; while (p < &buffer[chunk]) *p++ = 0; done = 2; } r = offset % drive_size; if (r == 0) { /* An integral number of diskettes have been filled. Prompt. */ fprintf(stderr, "Please insert formatted diskette #%d in drive %c, then hit Enter%c\n", disknr, c, 7); disknr++; #if !TEST while(bioskey(1) == 0) ; /* wait for input */ if ((bioskey(0) & 0x7F) == 3) exit(1); /* CTRL-C */ biosdisk(READ, drive, 0, 0, 1, 1, testbuf); /* get it going */ #endif } /* Compute cyl, head, sector. */ cyl = r/cyl_size; r -= cyl * cyl_size; head = (r < cyl_size/2 ? 0 : 1); r -= head * cyl_size/2; sector = r/SECTORSIZE; fprintf(stderr, "Track: %2d Head: %d Sector: %2d File offset: %ld\r", cyl, head, sector+1,offset); #if !TEST status = biosdisk(WRITE, drive, head, cyl, sector+1, nsectors, buffer); if (status != 0) Error(status, cyl, head, sector); #else write(1, buffer, chunk); #endif offset += chunk; } }