void ATA_DEVICE::write_data(unsigned data) { if (!loaded()) return; if ((reg.devhead ^ device_id) & 0x10) return; if (/* (reg.status & (STATUS_DRQ | STATUS_BSY)) != STATUS_DRQ ||*/ transptr >= transcount) return; *(unsigned short*)(transbf + transptr*2) = (unsigned short)data; transptr++; if (transptr < transcount) return; // look to state, prepare next block if (state == S_WRITE_SECTORS) { write_sectors(); return; } if (state == S_FORMAT_TRACK) { format_track(); return; } if (state == S_RECV_PACKET) { handle_atapi_packet(); return; } /* if (state == S_MODE_SELECT) { exec_mode_select(); return; } */ }
void Gen_Format360(short drive) { short head, track, i; char msg[60]; sprintf(msg, "Preparing disk in drive %c - please wait", 'A' + drive); Gen_ShowMessage(msg); sleep(2); for (track = 0; track < 40; track++) for (head = 0; head < 2; head++) format_track(drive, head, track); Gen_WriteSector(drive, 0, 0, 1, GF360buf); /* Write boot sector */ /* Write first sectors of FATs */ GF360buf[0] = 0xFd; /* f9 for 720k */ GF360buf[1] = 0xff; GF360buf[2] = 0xff; for (i = 3; i < 512; i++) GF360buf[i] = 0; Gen_WriteSector(drive, 0, 0, 2, GF360buf); Gen_WriteSector(drive, 0, 0, 4, GF360buf); /* Write other FAT sectors and root directory */ for (i = 0; i < 3; i++) GF360buf[i] = 0; Gen_WriteSector(drive, 0, 0, 3, GF360buf); Gen_WriteSector(drive, 0, 0, 5, GF360buf); for (i = 1; i < 512; i++) GF360buf[i] = 0xF6; Gen_WriteSector(drive, 0, 0, 6, GF360buf); Gen_WriteSector(drive, 0, 0, 7, GF360buf); Gen_WriteSector(drive, 0, 0, 8, GF360buf); Gen_WriteSector(drive, 0, 0, 9, GF360buf); Gen_WriteSector(drive, 1, 0, 1, GF360buf); Gen_WriteSector(drive, 1, 0, 2, GF360buf); Gen_ClearMessage(); }
void format_device(unsigned drive, unsigned type, int verify) { int ffd, vfd; char *fmt_dev, *ver_dev; struct stat st; unsigned cyl, head; unsigned nr_cyls; type_parameters_t *tparams= ¶meters[type - 1]; int verbose= isatty(1); fmt_dev= tmpnam(nil); if (mknod(fmt_dev, S_IFBLK | 0700, fl_makedev(drive, type, 1)) < 0) { fprintf(stderr, "format: making format device failed: %s\n", strerror(errno)); exit(1); } if ((ffd= open(fmt_dev, O_WRONLY)) < 0 || fstat(ffd, &st) < 0) { report(fmt_dev); (void) unlink(fmt_dev); exit(1); } (void) unlink(fmt_dev); if (st.st_rdev != fl_makedev(drive, type, 1)) { /* Someone is trying to trick me. */ exit(1); } if (verify) { ver_dev= tmpnam(nil); if (mknod(ver_dev, S_IFBLK | 0700, fl_makedev(drive, type, 0)) < 0) { fprintf(stderr, "format: making verify device failed: %s\n", strerror(errno)); exit(1); } if ((vfd= open(ver_dev, O_RDONLY)) < 0) { report(ver_dev); (void) unlink(ver_dev); exit(1); } (void) unlink(ver_dev); } nr_cyls= tparams->media_size * (1024 / SECTOR_SIZE) / NR_HEADS / tparams->fmt_params.sectors_per_track; if (verbose) { printf("Formatting a %uk diskette in a %uk drive\n", tparams->media_size, tparams->drive_size); } for (cyl= 0; cyl < nr_cyls; cyl++) { for (head= 0; head < NR_HEADS; head++) { if (verbose) { printf(" Cyl. %2u, Head %u\r", cyl, head); fflush(stdout); } /* After formatting a track we are too late to format * the next track. So we can sleep at most 1/6 sec to * allow the above printf to get displayed before we * lock Minix into the floppy driver again. */ if (verbose) usleep(50000); /* 1/20 sec will do. */ format_track(ffd, type, cyl, head); if (verify) verify_track(vfd, type, cyl, head); } } if (verbose) fputc('\n', stdout); }
void omti8621_device::do_command(const UINT8 cdb[], const UINT16 cdb_length) { UINT8 lun = get_lun(cdb); omti_disk_image_device *disk = our_disks[lun]; int command_duration = 0; // ms log_command( cdb, cdb_length); // default to read status and status is successful completion omti_state = OMTI_STATE_STATUS; status_port |= OMTI_STATUS_IO | OMTI_STATUS_CD; command_status = lun ? OMTI_COMMAND_STATUS_LUN : 0; if (mask_port & OMTI_MASK_INTE) { set_interrupt(CLEAR_LINE); } if (!disk->m_image->exists()) { command_status |= OMTI_COMMAND_STATUS_ERROR; // no such drive } switch (cdb[0]) { case OMTI_CMD_TEST_DRIVE_READY: // 0x00 if (!disk->m_image->exists()) { set_sense_data(OMTI_SENSE_CODE_DRIVE_NOT_READY, cdb); } break; case OMTI_CMD_RECALIBRATE: // 0x01 break; case OMTI_CMD_REQUEST_SENSE: // 0x03 set_data_transfer(sense_data, sizeof(sense_data)); break; case OMTI_CMD_READ_VERIFY: // 0x05 check_disk_address(cdb); break; case OMTI_CMD_FORMAT_TRACK: // 0x06 format_track(cdb); break; case OMTI_CMD_FORMAT_BAD_TRACK: // 0x07 diskaddr_format_bad_track = get_disk_address(cdb); break; case OMTI_CMD_READ: // 0x08 if (check_disk_address(cdb)) { // read data from controller read_sectors_from_disk(get_disk_address(cdb), cdb[4], lun); set_data_transfer(§or_buffer[0], OMTI_DISK_SECTOR_SIZE*cdb[4]); } break; case OMTI_CMD_WRITE: // 0x0A log_data(); if (check_disk_address(cdb)) { write_sectors_to_disk(get_disk_address(cdb), cdb[4], lun); } break; case OMTI_CMD_SEEK: // 0x0B check_disk_address(cdb); break; case OMTI_CMD_READ_SECTOR_BUFFER: // 0x0E set_data_transfer(§or_buffer[0], OMTI_DISK_SECTOR_SIZE*cdb[4]); break; case OMTI_CMD_WRITE_SECTOR_BUFFER: // 0x0F log_data(); break; case OMTI_CMD_COPY: // 0x20 if (check_disk_address(cdb) && check_disk_address(cdb+4)) { // copy sectors copy_sectors (get_disk_address(cdb+4), get_disk_address(cdb), cdb[4], lun); } break; case OMTI_CMD_READ_ESDI_DEFECT_LIST: // 0x37 set_esdi_defect_list(get_lun(cdb), cdb[1] & 0x1f); set_data_transfer(disk->m_esdi_defect_list, sizeof(disk->m_esdi_defect_list)); break; #if 0 // this command seems unused by Domain/OS, and it's unclear what the intent of the code is (it makes some versions of GCC quite unhappy) case OMTI_CMD_ASSIGN_ALTERNATE_TRACK: // 0x11 log_data(); alternate_track_address[0] = get_disk_track(cdb); alternate_track_address[1] = get_disk_track(alternate_track_buffer-1); break; #endif case OMTI_CMD_READ_DATA_TO_BUFFER: // 0x1E if (check_disk_address(cdb)) { // read data from controller read_sectors_from_disk (get_disk_address(cdb), cdb[4], lun); // Domain/OS doesn't expect zero access time command_duration += 1; // 1 ms is enough, average time would be 30 ms) } break; case OMTI_CMD_WRITE_DATA_FROM_BUFFER: // 0x1F log_data(); if (check_disk_address(cdb)) { write_sectors_to_disk(get_disk_address(cdb), cdb[4], lun); } break; case OMTI_CMD_RAM_DIAGNOSTICS: // 0xE0 break; case OMTI_CMD_CONTROLLER_INT_DIAGNOSTIC: // 0xE4 break; case OMTI_CMD_READ_LONG: // 0xE5 if (check_disk_address(cdb)) { // read data from controller read_sectors_from_disk(get_disk_address(cdb), cdb[4], lun); set_data_transfer(§or_buffer[0], OMTI_DISK_SECTOR_SIZE+6); } break; case OMTI_CMD_WRITE_LONG: // 0xE6 log_data(); if (check_disk_address(cdb)) { UINT32 diskaddr = get_disk_address(cdb); write_sectors_to_disk(diskaddr, cdb[4], lun); // this will spoil the ECC code diskaddr_ecc_error = diskaddr; } break; case OMTI_CMD_READ_CONFIGURATION: // 0xEC set_configuration_data(get_lun(cdb)); set_data_transfer(disk->m_config_data, sizeof(disk->m_config_data)); break; case OMTI_CMD_INVALID_COMMAND: // 0xFF set_sense_data(OMTI_SENSE_CODE_INVALID_COMMAND, cdb); command_status |= OMTI_COMMAND_STATUS_ERROR; break; default: LOG(("do_command: UNEXPECTED command %02x",cdb[0])); set_sense_data(OMTI_SENSE_CODE_INVALID_COMMAND, cdb); command_status |= OMTI_COMMAND_STATUS_ERROR; break; } if (mask_port & OMTI_MASK_INTE) { // if (omti_state != OMTI_STATE_STATUS) { // LOG(("do_command: UNEXPECTED omti_state %02x",omti_state)); // } status_port |= OMTI_STATUS_IREQ; if (command_duration == 0) { set_interrupt(ASSERT_LINE); } else { // FIXME: should delay omti_state and status_port as well m_timer->adjust(attotime::from_msec(command_duration), 0); } } }
static void do_command(omti8621_state *state, const UINT8 cdb[], const UINT16 cdb_length) { UINT8 lun = get_lun(cdb); disk_data *disk = state->disk[lun]; int command_duration = 0; // ms log_command( state, cdb, cdb_length); // default to read status and status is successful completion state->omti_state = OMTI_STATE_STATUS; state->status_port |= OMTI_STATUS_IO | OMTI_STATUS_CD; state->command_status = lun ? OMTI_COMMAND_STATUS_LUN : 0; if (state->mask_port & OMTI_MASK_INTE) { set_interrupt(state, CLEAR_LINE); } if (!disk->image->exists()) { state->command_status |= OMTI_COMMAND_STATUS_ERROR; // no such drive } switch (cdb[0]) { case OMTI_CMD_TEST_DRIVE_READY: // 0x00 if (!disk->image->exists()) { set_sense_data(state, OMTI_SENSE_CODE_DRIVE_NOT_READY, cdb); } break; case OMTI_CMD_RECALIBRATE: // 0x01 break; case OMTI_CMD_REQUEST_SENSE: // 0x03 set_data_transfer(state, state->sense_data, sizeof(state->sense_data)); break; case OMTI_CMD_READ_VERIFY: // 0x05 check_disk_address(state, cdb); break; case OMTI_CMD_FORMAT_TRACK: // 0x06 format_track(state, cdb); break; case OMTI_CMD_FORMAT_BAD_TRACK: // 0x07 state->diskaddr_format_bad_track = get_disk_address(state, cdb); break; case OMTI_CMD_READ: // 0x08 if (check_disk_address(state, cdb)) { // read data from controller read_sectors_from_disk(state, get_disk_address(state, cdb), cdb[4], lun); set_data_transfer(state, state->sector_buffer, OMTI_DISK_SECTOR_SIZE*cdb[4]); } break; case OMTI_CMD_WRITE: // 0x0A log_data(state); if (check_disk_address(state, cdb)) { write_sectors_to_disk(state, get_disk_address(state, cdb), cdb[4], lun); } break; case OMTI_CMD_SEEK: // 0x0B check_disk_address(state, cdb); break; case OMTI_CMD_READ_SECTOR_BUFFER: // 0x0E set_data_transfer(state, state->sector_buffer, OMTI_DISK_SECTOR_SIZE*cdb[4]); break; case OMTI_CMD_WRITE_SECTOR_BUFFER: // 0x0F log_data(state); break; case OMTI_CMD_COPY: // 0x20 if (check_disk_address(state, cdb) && check_disk_address(state, cdb+4)) { // copy sectors copy_sectors (state, get_disk_address(state, cdb+4), get_disk_address(state, cdb), cdb[4], lun); } break; case OMTI_CMD_READ_ESDI_DEFECT_LIST: // 0x37 set_esdi_defect_list(state, get_lun(cdb), cdb[1] & 0x1f); set_data_transfer(state, disk->esdi_defect_list, sizeof(disk->esdi_defect_list)); break; case OMTI_CMD_ASSIGN_ALTERNATE_TRACK: // 0x11 log_data(state); state->alternate_track_address[0] = get_disk_track(state, cdb); state->alternate_track_address[1] = get_disk_track(state, state->alternate_track_buffer-1);; break; case OMTI_CMD_READ_DATA_TO_BUFFER: // 0x1E if (check_disk_address(state, cdb)) { // read data from controller read_sectors_from_disk (state, get_disk_address(state, cdb), cdb[4], lun); // Domain/OS doesn't expect zero access time command_duration += 1; // 1 ms is enough, average time would be 30 ms) } break; case OMTI_CMD_WRITE_DATA_FROM_BUFFER: // 0x1F log_data(state); if (check_disk_address(state, cdb)) { write_sectors_to_disk(state, get_disk_address(state, cdb), cdb[4], lun); } break; case OMTI_CMD_RAM_DIAGNOSTICS: // 0xE0 break; case OMTI_CMD_CONTROLLER_INT_DIAGNOSTIC: // 0xE4 break; case OMTI_CMD_READ_LONG: // 0xE5 if (check_disk_address(state, cdb)) { // read data from controller read_sectors_from_disk(state, get_disk_address(state, cdb), cdb[4], lun); set_data_transfer(state, state->sector_buffer, OMTI_DISK_SECTOR_SIZE+6); } break; case OMTI_CMD_WRITE_LONG: // 0xE6 log_data(state); if (check_disk_address(state, cdb)) { UINT32 diskaddr = get_disk_address(state, cdb); write_sectors_to_disk(state, diskaddr, cdb[4], lun); // this will spoil the ECC code state->diskaddr_ecc_error = diskaddr; } break; case OMTI_CMD_READ_CONFIGURATION: // 0xEC set_configuration_data(state, get_lun(cdb)); set_data_transfer(state, disk->config_data, sizeof(disk->config_data)); break; case OMTI_CMD_INVALID_COMMAND: // 0xFF set_sense_data(state, OMTI_SENSE_CODE_INVALID_COMMAND, cdb); state->command_status |= OMTI_COMMAND_STATUS_ERROR; break; default: LOG(("do_command: UNEXPECTED command %02x",cdb[0])); set_sense_data(state, OMTI_SENSE_CODE_INVALID_COMMAND, cdb); state->command_status |= OMTI_COMMAND_STATUS_ERROR; break; } if (state->mask_port & OMTI_MASK_INTE) { // if (state->omti_state != OMTI_STATE_STATUS) { // LOG(("do_command: UNEXPECTED omti_state %02x",state->omti_state)); // } state->status_port |= OMTI_STATUS_IREQ; if (command_duration == 0) { set_interrupt(state, ASSERT_LINE); } else { // FIXME: should delay state->omti_state and state->status_port as well state->device->machine().scheduler().timer_set(attotime::from_msec(command_duration), FUNC(set_interrupt_caba), 0, state); } } }
int main(int argc, char **argv) { enum fd_drivetype type; struct fd_type fdt, newft, *fdtp; struct stat sb; #define MAXPRINTERRS 10 struct fdc_status fdcs[MAXPRINTERRS]; int format, fill, quiet, verify, verify_only, confirm; int fd, c, i, track, error, tracks_per_dot, bytes_per_track, errs; int flags; char *fmtstring, *device; const char *name, *descr; format = quiet = verify_only = confirm = 0; verify = 1; fill = 0xf6; fmtstring = 0; while((c = getopt(argc, argv, "F:f:nqs:vy")) != -1) switch(c) { case 'F': /* fill byte */ if (getnum(optarg, &fill)) { fprintf(stderr, "Bad argument %s to -F option; must be numeric\n", optarg); usage(); } break; case 'f': /* format in kilobytes */ if (getnum(optarg, &format)) { fprintf(stderr, "Bad argument %s to -f option; must be numeric\n", optarg); usage(); } break; case 'n': /* don't verify */ verify = 0; break; case 'q': /* quiet */ quiet = 1; break; case 's': /* format string with detailed options */ fmtstring = optarg; break; case 'v': /* verify only */ verify = 1; verify_only = 1; break; case 'y': /* confirm */ confirm = 1; break; default: usage(); } if(optind != argc - 1) usage(); if (stat(argv[optind], &sb) == -1 && errno == ENOENT) { /* try prepending _PATH_DEV */ device = malloc(strlen(argv[optind]) + sizeof(_PATH_DEV) + 1); if (device == 0) errx(EX_UNAVAILABLE, "out of memory"); strcpy(device, _PATH_DEV); strcat(device, argv[optind]); if (stat(device, &sb) == -1) { free(device); device = argv[optind]; /* let it fail below */ } } else { device = argv[optind]; } if ((fd = open(device, O_RDWR | O_NONBLOCK)) < 0) err(EX_OSERR, "open(%s)", device); /* * Device initialization. * * First, get the device type descriptor. This tells us about * the media geometry data we need to format a medium. It also * lets us know quickly whether the device name actually points * to a floppy disk drive. * * Then, obtain any drive options. We're mainly interested to * see whether we're currently working on a device with media * density autoselection (FDOPT_AUTOSEL). Then, we add the * device option to tell the kernel not to log media errors, * since we can handle them ourselves. If the device does * media density autoselection, we then need to set the device * type appropriately, since by opening with O_NONBLOCK we * told the driver to bypass media autoselection (otherwise we * wouldn't stand a chance to format an unformatted or damaged * medium). We do not attempt to set the media type on any * other devices since this is a privileged operation. For the * same reason, specifying -f and -s options is only possible * for autoselecting devices. * * Finally, we are ready to turn off O_NONBLOCK, and start to * actually format something. */ if(ioctl(fd, FD_GTYPE, &fdt) < 0) errx(EX_OSERR, "not a floppy disk: %s", device); if (ioctl(fd, FD_GDTYPE, &type) == -1) err(EX_OSERR, "ioctl(FD_GDTYPE)"); if (format) { getname(type, &name, &descr); fdtp = get_fmt(format, type); if (fdtp == 0) errx(EX_USAGE, "unknown format %d KB for drive type %s", format, name); fdt = *fdtp; } if (fmtstring) { parse_fmt(fmtstring, type, fdt, &newft); fdt = newft; } if (ioctl(fd, FD_STYPE, &fdt) < 0) err(EX_OSERR, "ioctl(FD_STYPE)"); if ((flags = fcntl(fd, F_GETFL, 0)) == -1) err(EX_OSERR, "fcntl(F_GETFL)"); flags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1) err(EX_OSERR, "fcntl(F_SETFL)"); bytes_per_track = fdt.sectrac * (128 << fdt.secsize); /* XXX 20/40 = 0.5 */ tracks_per_dot = (fdt.tracks * fdt.heads + 20) / 40; if (verify_only) { if(!quiet) printf("Verify %dK floppy `%s'.\n", fdt.tracks * fdt.heads * bytes_per_track / 1024, device); } else if(!quiet && !confirm) { printf("Format %dK floppy `%s'? (y/n): ", fdt.tracks * fdt.heads * bytes_per_track / 1024, device); if(!yes()) { printf("Not confirmed.\n"); return (EX_UNAVAILABLE); } } /* * Formatting. */ if(!quiet) { printf("Processing "); for (i = 0; i < (fdt.tracks * fdt.heads) / tracks_per_dot; i++) putchar('-'); printf("\rProcessing "); fflush(stdout); } error = errs = 0; for (track = 0; track < fdt.tracks * fdt.heads; track++) { if (!verify_only) { format_track(fd, track / fdt.heads, fdt.sectrac, track % fdt.heads, fdt.trans, fdt.f_gap, fdt.secsize, fill, fdt.f_inter, track % fdt.heads? fdt.offset_side2: 0); if(!quiet && !((track + 1) % tracks_per_dot)) { putchar('F'); fflush(stdout); } } if (verify) { if (verify_track(fd, track, bytes_per_track) < 0) { error = 1; if (errs < MAXPRINTERRS && errno == EIO) { if (ioctl(fd, FD_GSTAT, fdcs + errs) == -1) errx(EX_IOERR, "floppy IO error, but no FDC status"); errs++; } } if(!quiet && !((track + 1) % tracks_per_dot)) { if (!verify_only) putchar('\b'); if (error) { putchar('E'); error = 0; } else putchar('V'); fflush(stdout); } } } if(!quiet) printf(" done.\n"); if (!quiet && errs) { fflush(stdout); fprintf(stderr, "Errors encountered:\nCyl Head Sect Error\n"); for (i = 0; i < errs && i < MAXPRINTERRS; i++) { fprintf(stderr, " %2d %2d %2d ", fdcs[i].status[3], fdcs[i].status[4], fdcs[i].status[5]); printstatus(fdcs + i, 1); putc('\n', stderr); } if (errs >= MAXPRINTERRS) fprintf(stderr, "(Further errors not printed.)\n"); } return errs != 0; }
int main(int argc, char **argv) { int inputfd = -1, c, fdn = 0, i,j,fd; int bpt, verbose=1, nbytes=0, track; int interactive = 1; const char *device= "/dev/fd0"; char *trackbuf = 0,*vrfybuf = 0; struct fd_type fdt; FILE *tty; setbuf(stdout,0); while((c = getopt(argc, argv, "d:f:vy")) != -1) switch(c) { case 'd': /* Which drive */ device = optarg; break; case 'f': /* input file */ if (inputfd >= 0) close(inputfd); inputfd = open(optarg,O_RDONLY); if (inputfd < 0) err(1, "%s", optarg); break; case 'v': /* Toggle verbosity */ verbose = !verbose; break; case 'y': /* Don't confirm? */ interactive = 0; break; case '?': default: usage(); } if (inputfd < 0) inputfd = 0; if (!isatty(1)) interactive = 0; if(optind < argc) usage(); tty = fopen(_PATH_TTY,"r+"); if(!tty) err(1, _PATH_TTY); setbuf(tty,0); for(j=1;j > 0;) { fdn++; if (interactive) { fprintf(tty, "Please insert floppy #%d in drive %s and press return >", fdn,device); while(1) { i = getc(tty); if(i == '\n') break; } } if((fd = open(device, O_RDWR)) < 0) err(1, "%s", device); if(ioctl(fd, FD_GTYPE, &fdt) < 0) errx(1, "not a floppy disk: %s", device); bpt = fdt.sectrac * (1<<fdt.secsize) * 128; if(!trackbuf) { trackbuf = malloc(bpt); if(!trackbuf) errx(1, "malloc"); } if(!vrfybuf) { vrfybuf = malloc(bpt); if(!vrfybuf) errx(1, "malloc"); } if(fdn == 1) { if(verbose) { printf("Format: %d cylinders, %d heads, %d sectors, %d bytes = %dkb\n", fdt.tracks,fdt.heads,fdt.sectrac,(1<<fdt.secsize) * 128, fdt.tracks*bpt*fdt.heads/1024); } memset(trackbuf,0,bpt); for(j=0;inputfd >= 0 && j<bpt;j+=i) { if(!(i = read(inputfd,trackbuf+j,bpt-j))) { close(inputfd); inputfd = -1; break; } nbytes += i; } } for (track = 0; track < fdt.tracks * fdt.heads; track++) { if(verbose) printf("\r%3d ",fdt.tracks * fdt.heads-track); if(verbose) putc((j ? 'I':'Z'),stdout); format_track(fd, track / fdt.heads, fdt.sectrac, track % fdt.heads, fdt.trans, fdt.f_gap, fdt.secsize, 0xe6, fdt.f_inter); if(verbose) putc('F',stdout); if (lseek (fd, (long) track*bpt, 0) < 0) err(1, "lseek"); if (write (fd, trackbuf, bpt) != bpt) err(1, "write"); if(verbose) putc('W',stdout); if (lseek (fd, (long) track*bpt, 0) < 0) err(1, "lseek"); if (read (fd, vrfybuf, bpt) != bpt) err(1, "read"); if(verbose) putc('R',stdout); if (memcmp(trackbuf,vrfybuf,bpt)) err(1, "compare"); if(verbose) putc('C',stdout); memset(trackbuf,0,bpt); for(j=0;inputfd >= 0 && j<bpt;j+=i) { if(!(i = read(inputfd,trackbuf+j,bpt-j))) { close(inputfd); inputfd = -1; break; } nbytes += i; } } close(fd); putc('\r',stdout); } if(verbose) printf("%d bytes on %d flopp%s\n",nbytes,fdn,fdn==1?"y":"ies"); exit(0); }