Add CRC checking for IBM disks. Weirdly, my 1200kB disk appears to have deleted

sectors (id mark F8) on it, where the checksums don't match. Myserious...
This commit is contained in:
David Given
2018-10-22 02:45:18 +02:00
parent d7b2505720
commit acd2a9c23b
12 changed files with 167 additions and 56 deletions

17
lib/crc.cc Normal file
View File

@@ -0,0 +1,17 @@
#include "globals.h"
#include "crc.h"
uint16_t crc16(uint16_t poly, const uint8_t* start, const uint8_t* end)
{
uint16_t crc = 0xffff;
while (start != end)
{
crc ^= *start++ << 8;
for (int i=0; i < 8; i++)
crc = (crc & 0x8000) ? ((crc<<1)^poly) : (crc<<1);
}
return crc;
}

9
lib/crc.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef CRC_H
#define CRC_H
#define CCITT_POLY 0x1021
extern uint16_t crc16(uint16_t poly, const uint8_t* start, const uint8_t* end);
#endif

View File

@@ -32,7 +32,7 @@ std::vector<std::unique_ptr<Sector>> parseRecordsToSectorsBrother(const std::vec
std::vector<uint8_t> sectordata(BROTHER_DATA_RECORD_PAYLOAD);
memcpy(&sectordata[0], &record[1], BROTHER_DATA_RECORD_PAYLOAD);
auto sector = std::unique_ptr<Sector>(new Sector(nextTrack, 0, nextSector, sectordata));
auto sector = std::unique_ptr<Sector>(new Sector(Sector::OK, nextTrack, 0, nextSector, sectordata));
sectors.push_back(std::move(sector));
hasHeader = false;
break;

View File

@@ -20,7 +20,7 @@ struct IbmIdam
uint8_t side;
uint8_t sector;
uint8_t sectorSize;
uint16_t crcBE;
uint8_t crc[2];
};
/* Brother word processor format (or at least, one of them) */

View File

@@ -1,11 +1,15 @@
#include "globals.h"
#include "decoders.h"
#include "image.h"
#include "crc.h"
#include "fmt/format.h"
#include <string.h>
#include <arpa/inet.h>
static_assert(std::is_trivially_copyable<IbmIdam>::value);
std::vector<std::unique_ptr<Sector>> parseRecordsToSectorsIbm(const std::vector<std::vector<uint8_t>>& records)
std::vector<std::unique_ptr<Sector>> parseRecordsToSectorsIbm(
const std::vector<std::vector<uint8_t>>& records)
{
bool idamValid = false;
IbmIdam idam;
@@ -22,10 +26,12 @@ std::vector<std::unique_ptr<Sector>> parseRecordsToSectorsIbm(const std::vector<
case IBM_IDAM:
{
if (record.size() < sizeof(idam))
goto garbage;
break;
memcpy(&idam, &record[0], sizeof(idam));
idamValid = true;
/* TODO: check CRC! */
uint16_t crc = crc16(CCITT_POLY, (uint8_t*)&idam, (uint8_t*)&idam.crc);
uint16_t wantedCrc = (idam.crc[0]<<8) | idam.crc[1];
idamValid = (crc == wantedCrc);
break;
}
@@ -33,25 +39,25 @@ std::vector<std::unique_ptr<Sector>> parseRecordsToSectorsIbm(const std::vector<
case IBM_DAM2:
{
if (!idamValid)
goto garbage;
break;
unsigned size = 1 << (idam.sectorSize + 7);
if ((record.size()-IBM_DAM_LEN) < size)
goto garbage;
/* TODO: check CRC! */
if ((record.size()-IBM_DAM_LEN-2) < size)
break;
uint16_t crc = crc16(CCITT_POLY, &record[0], &record[size+4]);
uint16_t wantedCrc = (record[size+4] << 8) | record[size+5];
int status = (crc == wantedCrc) ? Sector::OK : Sector::BAD_CHECKSUM;
std::vector<uint8_t> sectordata(size);
memcpy(&sectordata[0], &record[4], size);
auto sector = std::unique_ptr<Sector>(new Sector(idam.cylinder, idam.side, idam.sector-1, sectordata));
auto sector = std::unique_ptr<Sector>(
new Sector(status, idam.cylinder, idam.side, idam.sector-1, sectordata));
sectors.push_back(std::move(sector));
idamValid = false;
break;
}
default:
garbage:
Error() << "garbage record on disk (this diagnostic needs improving)";
}
}

View File

@@ -12,6 +12,7 @@
typedef int nanoseconds_t;
extern double getCurrentTime();
extern void hexdump(std::ostream& stream, const std::vector<uint8_t>& buffer);
class Error
{

32
lib/hexdump.cc Normal file
View File

@@ -0,0 +1,32 @@
#include "globals.h"
#include "fmt/format.h"
void hexdump(std::ostream& stream, const std::vector<uint8_t>& buffer)
{
size_t pos = 0;
while (pos < buffer.size())
{
stream << fmt::format("{:05x} : ", pos);
for (int i=0; i<16; i++)
{
if ((pos+i) < buffer.size())
stream << fmt::format("{:02x} ", buffer.at(pos+i));
else
stream << "-- ";
}
stream << " : ";
for (int i=0; i<16; i++)
{
if ((pos+i) >= buffer.size())
break;
uint8_t c = buffer.at(pos+i);
stream << (isprint(c) ? (char)c : '.');
}
stream << std::endl;
pos += 16;
}
}

View File

@@ -15,10 +15,10 @@ void writeSectorsToFile(const std::vector<std::unique_ptr<Sector>>& sectors, con
size_t sectorSize = 0;
for (auto& sector : sectors)
{
trackCount = std::max(sector->track()+1, trackCount);
sideCount = std::max(sector->side()+1, sideCount);
sectorCount = std::max(sector->sector()+1, sectorCount);
sectorSize = std::max(sector->data().size(), sectorSize);
trackCount = std::max(sector->track+1, trackCount);
sideCount = std::max(sector->side+1, sideCount);
sectorCount = std::max(sector->sector+1, sectorCount);
sectorSize = std::max(sector->data.size(), sectorSize);
}
size_t sideSize = sectorCount * sectorSize;
@@ -35,7 +35,7 @@ void writeSectorsToFile(const std::vector<std::unique_ptr<Sector>>& sectors, con
for (auto& sector : sectors)
{
outputFile.seekp(sector->track()*trackSize + sector->side()*sideSize + sector->sector()*sectorSize, std::ios::beg);
outputFile.write((const char*) &sector->data().at(0), sector->data().size());
outputFile.seekp(sector->track*trackSize + sector->side*sideSize + sector->sector*sectorSize, std::ios::beg);
outputFile.write((const char*) &sector->data[0], sector->data.size());
}
}

View File

@@ -9,23 +9,25 @@
class Sector
{
public:
Sector(int track, int side, int sector, const std::vector<uint8_t>& data):
_track(track),
_side(side),
_sector(sector),
_data(data)
enum
{
OK,
BAD_CHECKSUM
};
Sector(int status, int track, int side, int sector, const std::vector<uint8_t>& data):
status(status),
track(track),
side(side),
sector(sector),
data(data)
{}
int track() const { return _track; }
int side() const { return _side; }
int sector() const { return _sector; }
const std::vector<uint8_t>& data() const { return _data; }
private:
const int _track;
const int _side;
const int _sector;
const std::vector<uint8_t> _data;
const int status;
const int track;
const int side;
const int sector;
const std::vector<uint8_t> data;
};
extern void writeSectorsToFile(const std::vector<std::unique_ptr<Sector>>& sectors, const std::string& filename);

View File

@@ -38,6 +38,8 @@ felib = shared_library('felib',
'lib/globals.cc',
'lib/usb.cc',
'lib/image.cc',
'lib/crc.cc',
'lib/hexdump.cc',
],
include_directories: [fmtinc],
link_with: [fmtlib],
@@ -56,8 +58,8 @@ decoderlib = shared_library('decoderlib',
'lib/decoders/brotherdecoder.cc',
'lib/decoders/brotherparser.cc',
],
include_directories: [feinc],
link_with: [felib]
include_directories: [feinc, fmtinc],
link_with: [felib, fmtlib]
)
decoderinc = include_directories('lib/decoders')

View File

@@ -35,7 +35,7 @@ int main(int argc, const char* argv[])
int size = 0;
for (auto& sector : sectors)
{
size += sector->data().size();
size += sector->data.size();
allSectors.push_back(std::move(sector));
}
std::cout << size << " bytes decoded." << std::endl;

View File

@@ -11,38 +11,80 @@ static StringFlag outputFilename(
"The output image file to write to.",
"ibm.img");
static SettableFlag dumpRecords(
{ "--dump-records" },
"Dump the parsed records.");
int main(int argc, const char* argv[])
{
Flag::parseFlags(argc, argv);
bool failures = false;
std::vector<std::unique_ptr<Sector>> allSectors;
for (auto& track : readTracks())
{
Fluxmap& fluxmap = track->read();
int retries = 5;
retry:
Fluxmap& fluxmap = track->read();
nanoseconds_t clockPeriod = fluxmap.guessClock();
std::cout << fmt::format(" {:.1f} us clock; ", (double)clockPeriod/1000.0) << std::flush;
nanoseconds_t clockPeriod = fluxmap.guessClock();
std::cout << fmt::format(" {:.1f} us clock; ", (double)clockPeriod/1000.0) << std::flush;
/* For MFM, the bit clock is half the detected clock. */
auto bitmap = decodeFluxmapToBits(fluxmap, clockPeriod/2);
std::cout << fmt::format("{} bytes encoded; ", bitmap.size()/8) << std::flush;
/* For MFM, the bit clock is half the detected clock. */
auto bitmap = decodeFluxmapToBits(fluxmap, clockPeriod/2);
std::cout << fmt::format("{} bytes encoded; ", bitmap.size()/8) << std::flush;
auto records = decodeBitsToRecordsMfm(bitmap);
std::cout << records.size() << " records." << std::endl;
auto records = decodeBitsToRecordsMfm(bitmap);
std::cout << records.size() << " records." << std::endl;
auto sectors = parseRecordsToSectorsIbm(records);
std::cout << " " << sectors.size() << " sectors; ";
auto sectors = parseRecordsToSectorsIbm(records);
std::cout << " " << sectors.size() << " sectors; ";
bool hasBadSectors = false;
for (auto& sector : sectors)
{
if (sector->status != Sector::OK)
{
std::cout << std::endl
<< " Bad CRC on sector " << sector->sector << "; ";
hasBadSectors = true;
}
}
if (hasBadSectors)
{
if (retries == 0)
failures = true;
else
{
std::cout << std::endl
<< " " << retries << " retries remaining" << std::endl;
retries--;
track->forceReread();
goto retry;
}
}
int size = 0;
for (auto& sector : sectors)
{
size += sector->data().size();
allSectors.push_back(std::move(sector));
}
std::cout << size << " bytes decoded." << std::endl;
int size = 0;
for (auto& sector : sectors)
{
size += sector->data.size();
allSectors.push_back(std::move(sector));
}
std::cout << size << " bytes decoded." << std::endl;
if (dumpRecords)
{
std::cout << "\nRaw records follow:\n\n";
for (auto record : records)
{
hexdump(std::cout, record);
std::cout << std::endl;
}
}
}
writeSectorsToFile(allSectors, outputFilename);
if (failures)
std::cerr << "Warning: some sectors could not be decoded." << std::endl;
return 0;
}