Beispiel #1
0
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; } */
}
Beispiel #2
0
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= &parameters[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);
}
Beispiel #4
0
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(&sector_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(&sector_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(&sector_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);
		}
	}
}
Beispiel #5
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);
		}
	}
}
Beispiel #6
0
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;
}
Beispiel #7
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);
}