mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
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:
17
lib/crc.cc
Normal file
17
lib/crc.cc
Normal 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
9
lib/crc.h
Normal 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
|
||||
|
||||
@@ -32,7 +32,7 @@ std::vector<std::unique_ptr<Sector>> parseRecordsToSectorsBrother(const std::vec
|
||||
std::vector<uint8_t> sectordata(BROTHER_DATA_RECORD_PAYLOAD);
|
||||
memcpy(§ordata[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;
|
||||
|
||||
@@ -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) */
|
||||
|
||||
@@ -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(§ordata[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)";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
32
lib/hexdump.cc
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
12
lib/image.cc
12
lib/image.cc
@@ -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*) §or->data().at(0), sector->data().size());
|
||||
outputFile.seekp(sector->track*trackSize + sector->side*sideSize + sector->sector*sectorSize, std::ios::beg);
|
||||
outputFile.write((const char*) §or->data[0], sector->data.size());
|
||||
}
|
||||
}
|
||||
|
||||
32
lib/image.h
32
lib/image.h
@@ -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);
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user