/*************************************************************************
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;
}