mirror of
				https://github.com/davidgiven/fluxengine.git
				synced 2025-10-24 11:11:02 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			304 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			304 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "globals.h"
 | |
| #include "flags.h"
 | |
| #include "usb/usb.h"
 | |
| #include "fluxsource/fluxsource.h"
 | |
| #include "fluxsink/fluxsink.h"
 | |
| #include "reader.h"
 | |
| #include "fluxmap.h"
 | |
| #include "sql.h"
 | |
| #include "dataspec.h"
 | |
| #include "decoders/decoders.h"
 | |
| #include "sector.h"
 | |
| #include "sectorset.h"
 | |
| #include "visualiser.h"
 | |
| #include "record.h"
 | |
| #include "bytes.h"
 | |
| #include "decoders/rawbits.h"
 | |
| #include "track.h"
 | |
| #include "imagewriter/imagewriter.h"
 | |
| #include "fmt/format.h"
 | |
| #include <iostream>
 | |
| #include <fstream>
 | |
| 
 | |
| FlagGroup readerFlags
 | |
| {
 | |
| 	&hardwareFluxSourceFlags,
 | |
| 	&sqliteFluxSinkFlags,
 | |
| 	&fluxmapReaderFlags,
 | |
| 	&visualiserFlags
 | |
| };
 | |
| 
 | |
| static DataSpecFlag source(
 | |
|     { "--source", "-s" },
 | |
|     "source for data",
 | |
|     ":t=0-79:s=0-1:d=0");
 | |
| 
 | |
| static DataSpecFlag output(
 | |
| 	{ "--output", "-o" },
 | |
| 	"output image file to write to",
 | |
| 	"");
 | |
| 
 | |
| static StringFlag destination(
 | |
|     { "--write-flux", "-f" },
 | |
|     "write the raw magnetic flux to this file",
 | |
|     "");
 | |
| 
 | |
| static StringFlag visualise(
 | |
| 	{ "--write-svg" },
 | |
| 	"write a visualisation of the disk to this file",
 | |
| 	"");
 | |
| 
 | |
| static SettableFlag justRead(
 | |
| 	{ "--just-read" },
 | |
| 	"just read the disk and do no further processing");
 | |
| 
 | |
| static SettableFlag dumpRecords(
 | |
| 	{ "--dump-records" },
 | |
| 	"Dump the parsed but undecoded records.");
 | |
| 
 | |
| static SettableFlag dumpSectors(
 | |
| 	{ "--dump-sectors" },
 | |
| 	"Dump the decoded sectors.");
 | |
| 
 | |
| static IntFlag retries(
 | |
| 	{ "--retries" },
 | |
| 	"How many times to retry each track in the event of a read failure.",
 | |
| 	5);
 | |
| 
 | |
| static SettableFlag highDensityFlag(
 | |
| 	{ "--high-density", "--hd" },
 | |
| 	"set the drive to high density mode");
 | |
| 
 | |
| static StringFlag csvFile(
 | |
| 	{ "--write-csv" },
 | |
| 	"write a CSV report of the disk state",
 | |
| 	"");
 | |
| 
 | |
| static std::unique_ptr<FluxSink> outputFluxSink;
 | |
| 
 | |
| void setReaderDefaultSource(const std::string& source)
 | |
| {
 | |
|     ::source.set(source);
 | |
| }
 | |
| 
 | |
| void setReaderDefaultOutput(const std::string& output)
 | |
| {
 | |
|     ::output.set(output);
 | |
| }
 | |
| 
 | |
| void setReaderRevolutions(int revolutions)
 | |
| {
 | |
| 	setHardwareFluxSourceRevolutions(revolutions);
 | |
| }
 | |
| 
 | |
| static void writeSectorsToFile(const SectorSet& sectors, const ImageSpec& spec)
 | |
| {
 | |
| 	std::unique_ptr<ImageWriter> writer(ImageWriter::create(sectors, spec));
 | |
| 	writer->adjustGeometry();
 | |
| 	writer->printMap();
 | |
| 	if (!csvFile.get().empty())
 | |
| 		writer->writeCsv(csvFile.get());
 | |
| 	writer->writeImage();
 | |
| }
 | |
| 
 | |
| void Track::readFluxmap()
 | |
| {
 | |
| 	std::cout << fmt::format("{0:>3}.{1}: ", physicalTrack, physicalSide) << std::flush;
 | |
| 	fluxmap = fluxsource->readFlux(physicalTrack, physicalSide);
 | |
| 	std::cout << fmt::format(
 | |
| 		"{0} ms in {1} bytes\n",
 | |
|             fluxmap->duration()/1e6,
 | |
|             fluxmap->bytes());
 | |
| 	if (outputFluxSink)
 | |
| 		outputFluxSink->writeFlux(physicalTrack, physicalSide, *fluxmap);
 | |
| }
 | |
| 
 | |
| std::vector<std::unique_ptr<Track>> readTracks()
 | |
| {
 | |
|     const FluxSpec spec(source);
 | |
| 
 | |
|     std::cout << "Reading from: " << source << std::endl;
 | |
| 
 | |
| 	setHardwareFluxSourceDensity(highDensityFlag);
 | |
| 
 | |
| 	if (!destination.get().empty())
 | |
| 	{
 | |
| 		std::cout << "Writing a copy of the flux to " << destination.get() << std::endl;
 | |
| 		outputFluxSink = FluxSink::createSqliteFluxSink(destination.get());
 | |
| 	}
 | |
| 
 | |
| 	std::shared_ptr<FluxSource> fluxSource = FluxSource::create(spec);
 | |
| 
 | |
| 	std::vector<std::unique_ptr<Track>> tracks;
 | |
|     for (const auto& location : spec.locations)
 | |
| 	{
 | |
| 		auto track = std::make_unique<Track>(location.track, location.side);
 | |
| 		track->fluxsource = fluxSource;
 | |
| 		tracks.push_back(std::move(track));
 | |
| 	}
 | |
| 
 | |
| 	if (justRead)
 | |
| 	{
 | |
| 		for (auto& track : tracks)
 | |
| 			track->readFluxmap();
 | |
| 		
 | |
| 		std::cout << "--just-read specified, halting now" << std::endl;
 | |
| 		exit(0);
 | |
| 	}
 | |
| 
 | |
| 	return tracks;
 | |
| }
 | |
| 
 | |
| static bool conflictable(Sector::Status status)
 | |
| {
 | |
| 	return (status == Sector::OK) || (status == Sector::CONFLICT);
 | |
| }
 | |
| 
 | |
| static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replacement)
 | |
| {
 | |
| 	if (replacing && conflictable(replacing->status) && conflictable(replacement.status))
 | |
| 	{
 | |
| 		if (replacement.data != replacing->data)
 | |
| 		{
 | |
| 			std::cout << std::endl
 | |
| 						<< "       multiple conflicting copies of sector " << replacing->logicalSector
 | |
| 						<< " seen; ";
 | |
| 			replacing->status = Sector::CONFLICT;
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 	if (!replacing || ((replacing->status != Sector::OK) && (replacement.status == Sector::OK)))
 | |
| 	{
 | |
| 		if (!replacing)
 | |
| 			replacing.reset(new Sector);
 | |
| 		*replacing = replacement;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void readDiskCommand(AbstractDecoder& decoder)
 | |
| {
 | |
| 	const ImageSpec outputSpec(output);
 | |
| 	ImageWriter::verifyImageSpec(outputSpec);
 | |
|         
 | |
| 	bool failures = false;
 | |
| 	SectorSet allSectors;
 | |
| 	auto tracks = readTracks();
 | |
|     for (const auto& track : tracks)
 | |
| 	{
 | |
| 		std::map<int, std::unique_ptr<Sector>> readSectors;
 | |
| 		for (int retry = ::retries; retry >= 0; retry--)
 | |
| 		{
 | |
| 			track->readFluxmap();
 | |
| 			decoder.decodeToSectors(*track);
 | |
| 
 | |
| 			std::cout << "       ";
 | |
| 				std::cout << fmt::format("{} records, {} sectors; ",
 | |
| 					track->rawrecords.size(),
 | |
| 					track->sectors.size());
 | |
| 				if (track->sectors.size() > 0)
 | |
| 					std::cout << fmt::format("{:.2f}us clock ({:.0f}kHz); ",
 | |
| 						track->sectors.begin()->clock / 1000.0,
 | |
|                         1000000.0 / track->sectors.begin()->clock);
 | |
| 
 | |
| 				for (auto& sector : track->sectors)
 | |
| 				{
 | |
| 					auto& replacing = readSectors[sector.logicalSector];
 | |
| 					replace_sector(replacing, sector);
 | |
| 				}
 | |
| 
 | |
| 				bool hasBadSectors = false;
 | |
| 				std::set<unsigned> requiredSectors = decoder.requiredSectors(*track);
 | |
| 				for (const auto& i : readSectors)
 | |
| 				{
 | |
| 					const auto& sector = i.second;
 | |
| 					requiredSectors.erase(sector->logicalSector);
 | |
| 
 | |
| 					if (sector->status != Sector::OK)
 | |
| 					{
 | |
| 						std::cout << std::endl
 | |
| 								<< "       Failed to read sector " << sector->logicalSector
 | |
| 								<< " (" << Sector::statusToString((Sector::Status)sector->status) << "); ";
 | |
| 						hasBadSectors = true;
 | |
| 					}
 | |
| 				}
 | |
| 				for (unsigned logicalSector : requiredSectors)
 | |
| 				{
 | |
| 					std::cout << "\n"
 | |
| 					          << "       Required sector " << logicalSector << " missing; ";
 | |
| 					hasBadSectors = true;
 | |
| 				}
 | |
| 
 | |
| 				if (hasBadSectors)
 | |
| 					failures = false;
 | |
| 
 | |
| 				std::cout << std::endl
 | |
| 						<< "       ";
 | |
| 
 | |
| 				if (!hasBadSectors)
 | |
| 					break;
 | |
| 
 | |
| 			if (!track->fluxsource->retryable())
 | |
| 				break;
 | |
|             if (retry == 0)
 | |
|                 std::cout << "giving up" << std::endl
 | |
|                           << "       ";
 | |
|             else
 | |
| 				std::cout << retry << " retries remaining" << std::endl;
 | |
| 		}
 | |
| 
 | |
| 		if (dumpRecords)
 | |
| 		{
 | |
| 			std::cout << "\nRaw (undecoded) records follow:\n\n";
 | |
| 			for (auto& record : track->rawrecords)
 | |
| 			{
 | |
| 				std::cout << fmt::format("I+{:.2f}us with {:.2f}us clock\n",
 | |
|                             record.position.ns() / 1000.0, record.clock / 1000.0);
 | |
| 				hexdump(std::cout, record.data);
 | |
| 				std::cout << std::endl;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
|         if (dumpSectors)
 | |
|         {
 | |
|             std::cout << "\nDecoded sectors follow:\n\n";
 | |
|             for (auto& sector : track->sectors)
 | |
|             {
 | |
| 				std::cout << fmt::format("{}.{:02}.{:02}: I+{:.2f}us with {:.2f}us clock: status {}\n",
 | |
|                             sector.logicalTrack, sector.logicalSide, sector.logicalSector,
 | |
|                             sector.position.ns() / 1000.0, sector.clock / 1000.0,
 | |
| 							sector.status);
 | |
| 				hexdump(std::cout, sector.data);
 | |
| 				std::cout << std::endl;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         int size = 0;
 | |
| 		bool printedTrack = false;
 | |
|         for (auto& i : readSectors)
 | |
|         {
 | |
| 			auto& sector = i.second;
 | |
| 			if (sector)
 | |
| 			{
 | |
| 				if (!printedTrack)
 | |
| 				{
 | |
| 					std::cout << fmt::format("logical track {}.{}; ", sector->logicalTrack, sector->logicalSide);
 | |
| 					printedTrack = true;
 | |
| 				}
 | |
| 
 | |
| 				size += sector->data.size();
 | |
| 
 | |
| 				std::unique_ptr<Sector>& replacing = allSectors.get(sector->logicalTrack, sector->logicalSide, sector->logicalSector);
 | |
| 				replace_sector(replacing, *sector);
 | |
| 			}
 | |
|         }
 | |
|         std::cout << size << " bytes decoded." << std::endl;
 | |
|     }
 | |
| 
 | |
| 	if (!visualise.get().empty())
 | |
| 		visualiseSectorsToFile(allSectors, visualise.get());
 | |
| 	
 | |
|     writeSectorsToFile(allSectors, outputSpec);
 | |
| 	if (failures)
 | |
| 		std::cerr << "Warning: some sectors could not be decoded." << std::endl;
 | |
| }
 |