/************************************************************************* readti -- Creates sector images from floppy disks written on the TI-99/4A and Geneve computer family Copyright (C) 2008 Michael Zapf (Michael.Zapf@mizapf.de) Copyright (C) 2011 Gerhard Wiesinger (gerhard@wiesinger.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . **************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #define COMMAND 0 #define PHEAD 1 #define LCYL 2 #define LHEAD 3 #define SECTOR 4 #define SECTSIZE 5 #define SECTPERCYL 6 #define GAP 7 #define SIZE2 8 #define FM 1 #define MFM 0 #define SINGLE 1 #define DOUBLE 2 struct geometry_t { int tracks; int sectors; int sides; int rate; int density; int stepping; } geometry; int verbose; struct floppy_raw_cmd raw_cmd; void recalibrate(int fd) { int tmp, size; int head = 0; int track = 0; raw_cmd.cmd[PHEAD] = head*4; // 0 and 4 are the sides raw_cmd.cmd[LHEAD] = head; raw_cmd.cmd_count = 2; raw_cmd.length = 0x100; size = raw_cmd.length; raw_cmd.rate = geometry.rate; raw_cmd.flags = FD_RAW_INTR | FD_RAW_NEED_SEEK; raw_cmd.cmd[COMMAND] = FD_RECALIBRATE; if (geometry.density==FM) raw_cmd.cmd[0] &= ~0x40; raw_cmd.cmd[LCYL] = track; raw_cmd.track = raw_cmd.cmd[LCYL]*geometry.stepping; tmp = ioctl( fd, FDRAWCMD, &raw_cmd ); if (verbose) printf("Recalibrate...\n"); if ( tmp < 0 ){ perror("error executing command"); exit(1); } } void loadsector(int fd, int fdout, int track, int sector, int head) { int tmp, size; char buffer[ 512 * 2 * 24 ]; raw_cmd.data = buffer; raw_cmd.cmd[PHEAD] = head*4; // 0 and 4 are the sides raw_cmd.cmd[LHEAD] = head; raw_cmd.cmd[SECTOR] = sector; raw_cmd.cmd[SECTSIZE] = 1; // 0 = 128, 1 = 256, 2 = 512, 3 = 1024 ... raw_cmd.cmd[SECTPERCYL] = geometry.sectors; raw_cmd.cmd[GAP] = 0x1b; // not needed, default raw_cmd.cmd[SIZE2] = 0xff; // not needed, default raw_cmd.cmd_count = 9; raw_cmd.length = 0x100; size = raw_cmd.length; raw_cmd.rate = geometry.rate; raw_cmd.flags = FD_RAW_READ | FD_RAW_INTR | FD_RAW_NEED_SEEK; raw_cmd.cmd[COMMAND] = FD_READ; if (geometry.density==FM) raw_cmd.cmd[0] &= ~0x40; raw_cmd.cmd[LCYL] = track; raw_cmd.track = raw_cmd.cmd[LCYL]*geometry.stepping; tmp = ioctl( fd, FDRAWCMD, &raw_cmd ); // printf("Reading track %d, head %d, status %2x\n", track, head, raw_cmd.reply[0]); if ( tmp < 0 ){ perror("error executing command"); exit(1); } if (raw_cmd.reply[0] > 0x07) { printf("Read error reading sector %d on track %d, side %d. Filling with 0xdead.\n", sector, track, head+1); wmemset((wchar_t*)buffer, 0xaddeadde, size/sizeof(wchar_t)); } write(fdout, buffer, size ); } void loadtrack(int fd, int fdout, int track, int head) { int tmp, size; char buffer[ 512 * 2 * 24 ]; raw_cmd.data = buffer; raw_cmd.cmd[PHEAD] = head*4; // 0 and 4 are the sides raw_cmd.cmd[LHEAD] = head; raw_cmd.cmd[SECTOR] = 0; raw_cmd.cmd[SECTSIZE] = 1; // 0 = 128, 1 = 256, 2 = 512, 3 = 1024 ... raw_cmd.cmd[SECTPERCYL] = geometry.sectors; raw_cmd.cmd[GAP] = 0x1b; // not needed, default raw_cmd.cmd[SIZE2] = 0xff; // not needed, default raw_cmd.cmd_count = 9; raw_cmd.length = 0x100 * geometry.sectors; size = raw_cmd.length; raw_cmd.rate = geometry.rate; raw_cmd.flags = FD_RAW_READ | FD_RAW_INTR | FD_RAW_NEED_SEEK; raw_cmd.cmd[COMMAND] = FD_READ; if (geometry.density==FM) raw_cmd.cmd[0] &= ~0x40; raw_cmd.cmd[LCYL] = track; raw_cmd.track = raw_cmd.cmd[LCYL]*geometry.stepping; tmp = ioctl( fd, FDRAWCMD, &raw_cmd ); // printf("Reading track %d, head %d, status %2x\n", track, head, raw_cmd.reply[0]); if ( tmp < 0 ){ perror("error executing command"); exit(1); } if (raw_cmd.reply[0] > 0x07) { int sector; if (verbose) printf("Read error reading track %d, side %d. Trying to read separate sectors.\n", track, head+1); recalibrate(fd); for (sector=0; sector < geometry.sectors; sector++) { loadsector(fd, fdout, track, sector, head); } } else write(fdout, buffer, size ); } unsigned char *try_readid(int fd, int rate, int head, int track, int single_density) { int tmp; int need_seek = 1; raw_cmd.cmd[COMMAND] = FD_READID; raw_cmd.cmd[PHEAD] = head*4; // 0 and 4 are the sides raw_cmd.cmd_count = 2; raw_cmd.rate = rate; raw_cmd.flags = FD_RAW_INTR; if (need_seek) raw_cmd.flags |= FD_RAW_NEED_SEEK; raw_cmd.track = track; if (single_density) raw_cmd.cmd[0] &= ~0x40; tmp = ioctl( fd, FDRAWCMD, &raw_cmd ); if ( tmp < 0 ){ perror("error executing command"); exit(1); } return raw_cmd.reply; } /* Note: This analysis cannot detect whether a disk is only 40 tracks when it was once formatted as 80 tracks */ void analyze_disk(int fd) { // fdrawcmd readid 0 rate=0 need_seek track=0 unsigned char *status; // Determine rate int rate, density; int sectors, i, heads, stepping, tracks; density = MFM; for (rate=0; rate < 4; rate++) { status = try_readid(fd, rate, 0, 0, density); if (status[0] < 0x08) break; } if (rate==4) { // Try single density density = FM; for (rate=0; rate < 4; rate++) { status = try_readid(fd, rate, 0, 0, density); if (status[0] < 0x08) break; } if (rate==4) { printf("Error ... could not determine rate. Bad floppy disk?\n"); exit(1); } } // Determine number of sides status = try_readid(fd, rate, 1, 0, density); if (status[0] < 0x08) heads = 2; else { // Try another track recalibrate(fd); status = try_readid(fd, rate, 1, 18, density); if (status[0] < 0x08) heads = 2; else heads = 1; } // Determine number of sectors per track sectors = 0; for (i=0; i < 50; i++) { status = try_readid(fd, rate, 0, 0, density); if (status[0] > 0x08) { printf("Error ... could not read a sector. Bad floppy disk?\n"); exit(1); } if (status[5] > sectors) sectors = status[5]; } sectors = sectors+1; // Determine single or double stepping status = try_readid(fd, rate, 0, 2, density); if (status[0] > 0x08) { printf("Error ... could not read a sector. Bad floppy disk?\n"); exit(1); } if (status[3]==2) { stepping=SINGLE; } else { if (status[3]==1) { stepping = DOUBLE; } else { printf("Error ... could not determine stepping. Bad floppy disk?\n"); exit(1); } } // Determine number of tracks // Heuristic: 80 tracks/40 tracks, or 70 tracks/35 tracks // Double stepping means a maximum of 40 or 35 tracks. tracks = 80; if (stepping==1) { status = try_readid(fd, rate, 0, tracks-1, density); if (status[0] > 0x08) { tracks = 70; status = try_readid(fd, rate, 0, tracks-1, density); if (status[0] > 0x08) { tracks = 40; status = try_readid(fd, rate, 0, tracks-1, density); if (status[0] > 0x08) { tracks = 35; status = try_readid(fd, rate, 0, tracks-1, density); if (status[0] > 0x08) { printf("Cannot determine last track. Bad floppy disk?\n"); exit(1); } } } } } else { // double stepping; 40 or 35 tracks = 40; status = try_readid(fd, rate, 0, 2*(tracks-1), density); if (status[0] > 0x08) { tracks = 35; status = try_readid(fd, rate, 0, 2*(tracks-1), density); if (status[0] > 0x08) { printf("Cannot determine last track. Bad floppy disk?\n"); exit(1); } } } geometry.rate = rate; geometry.density = density; geometry.sectors = sectors; geometry.sides = heads; geometry.stepping = stepping; geometry.tracks = tracks; if (verbose) { printf("Rate = %d\n", rate); printf("Single density = %d\n", density); printf("Sectors = %d\n", sectors); printf("Heads = %d\n", heads); printf("physical/logical tracks = %d\n", stepping); printf("Logical tracks = %d\n", tracks); } } int main(int argc, char **argv) { int fd, fdout, i; int index=1; verbose = 0; if (argc < 3) { printf("Usage: readti [-v] device (=/dev/fd0) outfile\n"); exit(1); } if (strcmp(argv[index],"-v")==0) { verbose = 1; index++; if (argc < 4) { printf("Usage: readti [-v] device (=/dev/fd0) outfile\n"); exit(1); } } fd = open(argv[index++], O_ACCMODE | O_NDELAY); if ( fd < 0 ){ perror("error opening floppy"); exit(1); } fdout = open(argv[index++], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if ( fdout < 0 ){ perror("error opening output file"); exit(1); } analyze_disk(fd); for (i=0; i < geometry.tracks; i++) { loadtrack(fd, fdout, i, 0); } if (geometry.sides==2) { for (i=geometry.tracks-1; i >=0; i--) { loadtrack(fd, fdout, i, 1); } } if (close(fd)) perror("close error for input file"); if (close(fdout)) perror("close error for output file"); return 0; }