#!/usr/bin/perl -w # # readoricfd - read an Oric floppy # AYM 2000-10-19 # # SYNOPSIS # readoricfd --help # readoricfd [-d num] [-h num] # # DESCRIPTION # Reads an entire Oric floppy sector by sector and dumps its # contents to stdout. # # Note that, unlike readdsk, this program dumps floppies in # normal (cylinder, head, sector) order. Because of this, the # images produced by this program can't be used directly by # Euphoric. Euphoric expects (head, cylinder, sector) order. # # If an error occurs, readoricfd exits with a non-zero code # and leaves behind a file named readoricfd..res whose # content may help to diagnose the cause of the error. # # readoricfd is work in progress. # # OPTIONS # --help # Print a short usage message and exit successfully. # # -d # Use drive number and device /dev/fd. The # default is to use drive 0 and /dev/fd0. # # -h # Number of heads (1 or 2). The default is 1. # # BUGS # This is a quick and dirty hack. # # After it terminates, leaves the floppy device in a state # unsuitable for reading "normal" floppies. # # Read errors cause the program to abort. Should retry. # # Should be able to save in twisted format. # # PLATFORMS # Linux on a PC. Might work with other platforms if they have # a 82077-compatible FDC and Fdutils # . # # LEGAL # Copyright André Majorel 2000-10-20. Distributed under the # terms of the GPL. Absolutely no warranty. use strict; use Getopt::Long; sub err; my $self = 'readoricfd'; my $GAP = 10; # Whatever my $SPT = 17; # Sectors per track my $DRIVE_NUM = 0; # 0 through 3 my $DRIVE_DEV = '/dev/fd0'; # Would better match $DRIVE_NUM my $RATE = 2; # Data rate my $CYLS = 41; # Cylinders my $HEADS = 1; # Or two for Jasmin II... my $BPS = 256; # Bytes per sector Getopt::Long::config "bundling", "no_ignore_case"; { my $drive; my $help; GetOptions ("help" => \$help, "d=i" => \$drive, "h=i" => \$HEADS) or err "syntax error" and exit 1; if (scalar @ARGV > 0) { err "too many arguments"; exit 1; } if ($help) { print "$self - read an Oric floppy\n"; print "\nUsage:\n"; print " $self --help\n"; print " $self [-d num] [-h count]\n"; print "\nOptions:\n"; print " --help Print this message and exit successfully\n"; print " -d Use drive# and device /dev/fd (default 0)\n"; print " -h Assume heads (default 1)\n"; exit 0; } if (defined $drive) { if ($drive < 0) { err "negative drive number"; exit 1; } $DRIVE_NUM = $drive; $DRIVE_DEV = "/dev/fd$drive"; } } if ($HEADS < 1) { err "number of heads lesser than 1"; exit 1; } my $outfile = "-"; open OUT, ">$outfile" or err "$outfile: $!" and exit 1; if (-t OUT) { err "output file is a terminal"; exit 1; } { my $cmd = "setfdprm $DRIVE_DEV dd cyl=$CYLS head=$HEADS sect=$SPT ssize=$BPS gap=$GAP dtr=$RATE"; print STDERR "$cmd\n"; system $cmd and die "setfdprm error\n"; } #system "floppycontrol --inverted_dcl"; { my $cmd = "fdrawcmd drive=$DRIVE_DEV rate=$RATE recalibrate $DRIVE_NUM"; print STDERR "$cmd\n"; system $cmd and die "fdrawcmd recalibrate error\n"; } for (my $cyl = 0; $cyl < $CYLS; $cyl++) { if ($cyl > 0) { my $cmd = "fdrawcmd drive=$DRIVE_DEV rate=$RATE seek $DRIVE_NUM $cyl"; print STDERR "$cmd\n"; system $cmd and die "fdrawcmd seek error\n"; } for (my $head = 0; $head < $HEADS; $head++) { my $drvsel = $DRIVE_NUM | ($head << 2); my $szcod = log ($BPS / 128) / log 2; my $nsectors = $SPT; my $nbytes = $SPT * $BPS; my $cmd = "fdrawcmd drive=$DRIVE_DEV"; $cmd .= " rate=$RATE" if (defined $RATE); $cmd .= " length=$nbytes"; # Important $cmd .= " read $drvsel $cyl $head 1 $szcod $nsectors $GAP 0xff"; print STDERR "$cmd\n"; my $resfile = "$self.$$.res"; my $datfile = "$self.$$.dat"; my $r = system "$cmd >$datfile 2>$resfile"; if ($r) { err "$cyl.$head: fdrawcmd error (returned $?)"; next; } # Parse the results { open RES, "<$resfile" or err "$cyl.$head: $resfile: $!" and exit 1; my $line; my $val; # Parse 1st line (remaining= count) $line = ; if (! defined $line || $line !~ /^remaining= 0$/) { err "$cyl.$head: fdrawcmd: line 1: expected \"remaining= 0\", got \"$line\""; exit 1; } # Parse 2nd line (ST0) $line = ; if (! defined $line || $line !~ /^0: ([0-9a-fA-F]{1,2})$/) { err "$cyl.$head: fdrawcmd: line 2: expected \"0: xx\", got \"$line\""; exit 1; } $val = hex $1; if (($val & 0xc0) != 0 || ($val & 0x03) != $DRIVE_NUM || ($val & 0x04) != ($head << 2)) { err "$cyl.$head: read error (ST0 $val)"; } # Parse 3rd line (ST1) $line = ; if (! defined $line || $line !~ /^1: ([0-9a-fA-F]{1,2})$/) { err "$cyl.$head: fdrawcmd: line 3: expected \"1: xx\", got \"$line\""; exit 1; } $val = hex $1; if ($val & 0x80) { err "$cyl.$head: read error: end of cyl (ST1 $val)"; exit 1; } if ($val & 0x20) { err "$cyl.$head: read error: CRC error (ST1 $val)"; exit 1; } if ($val & 0x10) { err "$cyl.$head: read error: buffer overrun (ST1 $val)"; exit 1; } if ($val & 0x04) { err "$cyl.$head: read error: sector not found (ST1 $val)"; exit 1; } if ($val & 0x02) { err "$cyl.$head: read error: no writable (ST1 $val)"; # Huh ? exit 1; } if ($val & 0x01) { err "$cyl.$head: read error: missing AM (ST1 $val)"; exit 1; } # Parse 4th line (ST2) $line = ; if (! defined $line || $line !~ /^2: ([0-9a-fA-F]{1,2})$/) { err "$cyl.$head: fdrawcmd: line 4: expected \"2: xx\", got \"$line\""; exit 1; } $val = hex $1; if ($val & 0x40) { err "$cyl.$head: read error: deleted DAM (ST2 $val)"; exit 1; } if ($val & 0x20) { err "$cyl.$head: read error: CRC error in data (ST2 $val)"; exit 1; } if ($val & 0x10) { err "$cyl.$head: read error: wrong cylinder (ST2 $val)"; exit 1; } if ($val & 0x02) { err "$cyl.$head: read error: bad cylinder (ST2 $val)"; exit 1; } if ($val & 0x01) { err "$cyl.$head: read error: missing DAM (ST2 $val)"; exit 1; } # The remainder of fdrawcmd's output is not interesting # for us so don't even parse it. close RES; } # It should be now safe to assume that the whole track has # been correctly read. open DATA, "<$datfile" or err "$cyl.$head: $datfile: $!" and exit 1; print OUT or err "$outfile: write error ($!)" and exit 1; close DATA; unlink $resfile; unlink $datfile; } } exit 0; sub err { print STDERR $self, ': ', @_, "\n"; }