Compare commits

...

16 Commits

Author SHA1 Message Date
David Given
46f1b0aef4 Merge pull request #104 from davidgiven/sampler
Fix a nasty sampler bug leading to corrupted data
2019-08-28 00:23:21 +02:00
David Given
9923d67a7c Merge pull request #103 from davidgiven/visualiser
Add a simple disk visualiser.
2019-08-28 00:20:49 +02:00
David Given
99335a84fd Add documentation for the visualiser. 2019-08-28 00:19:18 +02:00
David Given
c266779433 Fix a bug where index pulses where being turned into flux pulses on read,
leading to completely broken data whenever an index pulse happened.
2019-08-27 23:58:07 +02:00
David Given
bdcc12cd53 Correctly import M_PI. 2019-08-27 23:08:13 +02:00
David Given
7988d0fe24 Don't replace bad sectors with more bad sectors. This means that if a sector is
permanently bad, the one which wins and goes into the output SectorSet is the
first one, not the last one. Frequently the last sector is truncated by the end
of read and so it isn't useful.
2019-08-27 22:39:24 +02:00
David Given
27f5c294b1 The visualiser period can now be specified in a flag. 2019-08-27 01:21:49 +02:00
David Given
b9a53e0d1c First draft of the visualiser. 2019-08-27 01:07:57 +02:00
David Given
f8b6d5e6fb Merge. 2019-08-25 00:24:34 +02:00
David Given
04ff31c348 Add a flag to the IBM decoder to tell it to ignore the logical sector IDs (some
formats don't use these).
2019-08-25 00:24:02 +02:00
David Given
77b4aebd1b Fix crashing bug when reading Kryoflux streams. 2019-08-24 23:53:22 +02:00
David Given
4056364300 Merge pull request #99 from davidgiven/extensions
Validate image extensions before reading, not after.
2019-08-22 22:09:42 +02:00
David Given
60bfe050d3 Refactor the way image extensions are handled to be generally cleaner. Add
support for validating ImageSpecs before we actually want to read/write an
image, so as to allow us to check the extension *before* wasting time reading a
disk. Make .d81 an alias of .img.
2019-08-21 00:45:10 +02:00
David Given
28d0ce765e Merge pull request #93 from davidgiven/hex
Add precompiled firmware
2019-08-15 22:26:50 +02:00
David Given
4954d33307 Add documentation for using the precompiled firmware. 2019-08-15 22:19:30 +02:00
David Given
55f3354287 Add precompiled hex for the firmware. 2019-08-15 21:52:11 +02:00
24 changed files with 4954 additions and 37 deletions

View File

@@ -15,7 +15,7 @@ install:
build_script: build_script:
- make - make
- zip -9 fluxengine.zip fluxengine.exe brother120tool.exe - zip -9 fluxengine.zip fluxengine.exe brother120tool.exe FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex
artifacts: artifacts:
- path: fluxengine.zip - path: fluxengine.zip

View File

File diff suppressed because it is too large Load Diff

View File

@@ -530,31 +530,38 @@
<CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3"> <CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3">
<CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1"> <CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED" persistent=""> <CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED" persistent="">
<Hidden v="False" /> <Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330> </CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2"> <CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2">
<dependencies> <dependencies>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1"> <CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_aliases.h" persistent="Generated_Source\PSoC5\LED_aliases.h"> <CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_aliases.h" persistent="Generated_Source\PSoC5\LED_aliases.h">
<Hidden v="False" /> <Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330> </CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="HEADER;;;;" /> <build_action v="HEADER;;;;" />
<PropertyDeltas /> <PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b> </CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1"> <CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED.c" persistent="Generated_Source\PSoC5\LED.c"> <CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED.c" persistent="Generated_Source\PSoC5\LED.c">
<Hidden v="False" /> <Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330> </CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="SOURCE_C;CortexM3;;;" /> <build_action v="SOURCE_C;CortexM3;;;" />
<PropertyDeltas /> <PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b> </CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1"> <CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED.h" persistent="Generated_Source\PSoC5\LED.h"> <CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED.h" persistent="Generated_Source\PSoC5\LED.h">
<Hidden v="False" /> <Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330> </CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="HEADER;;;;" /> <build_action v="HEADER;;;;" />
<PropertyDeltas /> <PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b> </CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_PM.c" persistent="Generated_Source\PSoC5\LED_PM.c">
<Hidden v="True" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="SOURCE_C;CortexM3;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
</dependencies> </dependencies>
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089> </CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8> </CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
@@ -3282,6 +3289,39 @@
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8> </CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
<filters /> <filters />
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0> </CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
<CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFolderSerialize" version="3">
<CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtBaseContainerSerialize" version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_PIN" persistent="">
<Hidden v="False" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<CyGuid_0820c2e7-528d-4137-9a08-97257b946089 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemListSerialize" version="2">
<dependencies>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_PIN_aliases.h" persistent="Generated_Source\PSoC5\LED_PIN_aliases.h">
<Hidden v="False" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="HEADER;;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_PIN.c" persistent="Generated_Source\PSoC5\LED_PIN.c">
<Hidden v="False" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="SOURCE_C;CortexM3;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
<CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtFileSerialize" version="3" xml_contents_version="1">
<CyGuid_31768f72-0253-412b-af77-e7dba74d1330 type_name="CyDesigner.Common.ProjMgmt.Model.CyPrjMgmtItemSerialize" version="2" name="LED_PIN.h" persistent="Generated_Source\PSoC5\LED_PIN.h">
<Hidden v="False" />
</CyGuid_31768f72-0253-412b-af77-e7dba74d1330>
<build_action v="HEADER;;;;" />
<PropertyDeltas />
</CyGuid_8b8ab257-35d3-4473-b57b-36315200b38b>
</dependencies>
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
<filters />
</CyGuid_ebc4f06d-207f-49c2-a540-72acf4adabc0>
</dependencies> </dependencies>
</CyGuid_0820c2e7-528d-4137-9a08-97257b946089> </CyGuid_0820c2e7-528d-4137-9a08-97257b946089>
</CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8> </CyGuid_2f73275c-45bf-46ba-b3b1-00a2fe0c8dd8>
@@ -3700,6 +3740,6 @@
</ignored_deps> </ignored_deps>
</CyGuid_495451fe-d201-4d01-b22d-5d3f5609ac37> </CyGuid_495451fe-d201-4d01-b22d-5d3f5609ac37>
<boot_component v="" /> <boot_component v="" />
<current_generation v="60" /> <current_generation v="68" />
</CyGuid_fec8f9e8-2365-4bdb-96d3-a4380222e01b> </CyGuid_fec8f9e8-2365-4bdb-96d3-a4380222e01b>
</CyXmlSerializer> </CyXmlSerializer>

View File

Binary file not shown.

View File

Binary file not shown.

View File

@@ -134,6 +134,9 @@ void IbmDecoder::decodeSectorRecord()
uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5)); uint16_t gotCrc = crc16(CCITT_POLY, bytes.slice(0, _currentHeaderLength + 5));
if (wantCrc == gotCrc) if (wantCrc == gotCrc)
_sector->status = Sector::DATA_MISSING; /* correct but unintuitive */ _sector->status = Sector::DATA_MISSING; /* correct but unintuitive */
if (_ignoreSideByte)
_sector->logicalSide = _sector->physicalSide;
} }
void IbmDecoder::decodeDataRecord() void IbmDecoder::decodeDataRecord()

View File

@@ -31,8 +31,9 @@ struct IbmIdam
class IbmDecoder : public AbstractDecoder class IbmDecoder : public AbstractDecoder
{ {
public: public:
IbmDecoder(unsigned sectorBase): IbmDecoder(unsigned sectorBase, bool ignoreSideByte=false):
_sectorBase(sectorBase) _sectorBase(sectorBase),
_ignoreSideByte(ignoreSideByte)
{} {}
RecordType advanceToNextRecord(); RecordType advanceToNextRecord();
@@ -41,6 +42,7 @@ public:
private: private:
unsigned _sectorBase; unsigned _sectorBase;
bool _ignoreSideByte;
unsigned _currentSectorSize; unsigned _currentSectorSize;
unsigned _currentHeaderLength; unsigned _currentHeaderLength;
}; };

View File

@@ -103,7 +103,43 @@ the unconnected pins and solder a short piece of wire to a GND pin on the
board. Alternatively you'll need to splice it into your drive's power supply board. Alternatively you'll need to splice it into your drive's power supply
cable somehow. (The black one.) cable somehow. (The black one.)
## Building the firmware ## Programming the board
You've got two options here. You can either use the precompiled firmware
supplied with the source, or else install the Cypress SDK and build it
yourself. If you want to hack the firmware source you need the latter, but
if you trust me to do it for you use the precompiled firmware. In either
case you'll need Windows and have to install some Cypress stuff.
**Before you read this:** If you're on Windows, good news! You can download a
precompiled version of the FluxEngine client and precompiled firmware [from
the GitHub releases
page](https://github.com/davidgiven/fluxengine/releases/latest). Simply unzip
it somewhere and run the `.exe` files from a `cmd` window (or other shell).
Follow the instructions below to program the board with the firmware.
### Using the precompiled firmware
On your Windows machine, [install the PSoC
Programmer](https://www.cypress.com/products/psoc-programming-solutions).
**Note:** _not_ the Cypress Programmer, which is for a different board!
Cypress will make you register.
Once done, run it. Plug the blunt end of the FluxEngine board into a USB
port (the end which is a USB connector). The programmer should detect it
and report it as a KitProg. You may be prompted to upgrade the programmer
hardware; if so, follow the instructions and do it.
Now go to File -> File Load and open
`FluxEngine.cydsn/CortexM3/ARM_GCC_541/Release/FluxEngine.hex` in the
project. If you're on Windows, the precompiled zipfile also contains a copy
of this file. Press the Program button (the one in the toolbar marked with a
down arrow). Stuff will happen and you should be left with three green boxes
in the status bar and 'Programming Succeeded' at the top of the log window.
You're done. You can unplug the board and close the programmer.
### Building the firmware yourself
On your Windows machine, [install the Cypress SDK and CY8CKIT-059 On your Windows machine, [install the Cypress SDK and CY8CKIT-059
BSP](http://www.cypress.com/documentation/development-kitsboards/cy8ckit-059-psoc-5lp-prototyping-kit-onboard-programmer-and). BSP](http://www.cypress.com/documentation/development-kitsboards/cy8ckit-059-psoc-5lp-prototyping-kit-onboard-programmer-and).
@@ -118,7 +154,7 @@ tutorial and making the LED on your board flash. It'll tell you where all the
controls are and how to program the board. Remember that the big end of the controls are and how to program the board. Remember that the big end of the
board plugs into your computer for programming. board plugs into your computer for programming.
When you're ready, open the `FluxEngine.cydsn/FluxEngine.cywrk` workspace, When you're ready, open the `FluxEngine.cydsn/FluxEngine.cyprj` project,
pick 'Program' from the menu, and the firmware should compile and be pick 'Program' from the menu, and the firmware should compile and be
programmed onto your board. programmed onto your board.
@@ -139,11 +175,6 @@ the port and proceed normally.
## Building the client ## Building the client
**Before you read this:** If you're on Windows, good news! You can download a
*precompiled version of the FluxEngine client [from the GitHub releases
*page](https://github.com/davidgiven/fluxengine/releases/latest). Simply unzip
*it somewhere and run it from a `cmd` window (or other shell).
The client software is where the intelligence, such as it is, is. It's pretty The client software is where the intelligence, such as it is, is. It's pretty
generic libusb stuff and should build and run on Windows, Linux and OSX as generic libusb stuff and should build and run on Windows, Linux and OSX as
well, although on Windows it'll need MSYS2 and mingw32. You'll need to well, although on Windows it'll need MSYS2 and mingw32. You'll need to

View File

@@ -225,6 +225,21 @@ Commands which normally take `--source` or `--dest` get a sensible default if
left unspecified. `fluxengine read ibm` on its own will read drive 0 and left unspecified. `fluxengine read ibm` on its own will read drive 0 and
write an `ibm.img` file. write an `ibm.img` file.
## Visualisation
When doing a read (either from a real disk or from a flux file) you can use
`--write-svg=output.svg` to write out a graphical visualisation of where the
sectors are on the disk. Here's a IBM PC 1232kB disk:
![A disk visualisation](./visualiser.svg)
Blue represents data, light blue a header, and red is a bad sector. Side zero
is on the left and side one is on the right.
The visualiser is extremely primitive and you have to explicitly tell it how
big your disk is, in milliseconds. The default is 200ms (for a normal 3.5"
disk). For a 5.25" disk, use `--visualiser-period=166`.
## Extra programs ## Extra programs
Supplied with FluxEngine, but not part of FluxEngine, are some little tools I Supplied with FluxEngine, but not part of FluxEngine, are some little tools I

1
doc/visualiser.svg Normal file
View File

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 394 KiB

View File

@@ -43,19 +43,25 @@ void AbstractDecoder::decodeToSectors(Track& track)
recordStart = fmr.tell(); recordStart = fmr.tell();
decodeSectorRecord(); decodeSectorRecord();
pushRecord(recordStart, fmr.tell()); Fluxmap::Position recordEnd = fmr.tell();
pushRecord(recordStart, recordEnd);
if (sector.status == Sector::DATA_MISSING) if (sector.status == Sector::DATA_MISSING)
{ {
/* The data is in a separate record. */ /* The data is in a separate record. */
sector.headerStartTime = recordStart.ns();
sector.headerEndTime = recordEnd.ns();
r = advanceToNextRecord(); r = advanceToNextRecord();
if (r == DATA_RECORD) if (r == DATA_RECORD)
{ {
recordStart = fmr.tell(); recordStart = fmr.tell();
decodeDataRecord(); decodeDataRecord();
pushRecord(recordStart, fmr.tell()); recordEnd = fmr.tell();
pushRecord(recordStart, recordEnd);
} }
} }
sector.dataStartTime = recordStart.ns();
sector.dataEndTime = recordEnd.ns();
if (sector.status != Sector::MISSING) if (sector.status != Sector::MISSING)
track.sectors.push_back(sector); track.sectors.push_back(sector);

View File

@@ -24,7 +24,7 @@ public:
void recalibrate() {} void recalibrate() {}
private: private:
const std::string& _path; const std::string _path;
}; };
std::unique_ptr<FluxSource> FluxSource::createStreamFluxSource(const std::string& path) std::unique_ptr<FluxSource> FluxSource::createStreamFluxSource(const std::string& path)

View File

@@ -22,7 +22,7 @@ extern void hexdumpForSrp16(std::ostream& stream, const Bytes& bytes);
class Error class Error
{ {
public: public:
~Error() [[ noreturn ]] ~Error()
{ {
std::cerr << "Error: " << _stream.str() << std::endl; std::cerr << "Error: " << _stream.str() << std::endl;
exit(1); exit(1);

View File

@@ -7,6 +7,13 @@
#include "imagereader/imagereader.h" #include "imagereader/imagereader.h"
#include "fmt/format.h" #include "fmt/format.h"
std::map<std::string, ImageReader::Constructor> ImageReader::formats =
{
{".adf", ImageReader::createImgImageReader},
{".d81", ImageReader::createImgImageReader},
{".img", ImageReader::createImgImageReader},
};
static bool ends_with(const std::string& value, const std::string& ending) static bool ends_with(const std::string& value, const std::string& ending)
{ {
if (ending.size() > value.size()) if (ending.size() > value.size())
@@ -14,15 +21,29 @@ static bool ends_with(const std::string& value, const std::string& ending)
return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
} }
std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec) ImageReader::Constructor ImageReader::findConstructor(const ImageSpec& spec)
{ {
const auto& filename = spec.filename; const auto& filename = spec.filename;
if (ends_with(filename, ".img") || ends_with(filename, ".adf")) for (const auto& e : formats)
return createImgImageReader(spec); {
if (ends_with(filename, e.first))
return e.second;
}
Error() << "unrecognised image filename extension"; return NULL;
return std::unique_ptr<ImageReader>(); }
std::unique_ptr<ImageReader> ImageReader::create(const ImageSpec& spec)
{
verifyImageSpec(spec);
return findConstructor(spec)(spec);
}
void ImageReader::verifyImageSpec(const ImageSpec& spec)
{
if (!findConstructor(spec))
Error() << "unrecognised image filename extension";
} }
ImageReader::ImageReader(const ImageSpec& spec): ImageReader::ImageReader(const ImageSpec& spec):

View File

@@ -12,10 +12,21 @@ public:
public: public:
static std::unique_ptr<ImageReader> create(const ImageSpec& spec); static std::unique_ptr<ImageReader> create(const ImageSpec& spec);
static void verifyImageSpec(const ImageSpec& spec);
private: private:
typedef
std::function<
std::unique_ptr<ImageReader>(const ImageSpec& spec)
>
Constructor;
static std::map<std::string, Constructor> formats;
static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec); static std::unique_ptr<ImageReader> createImgImageReader(const ImageSpec& spec);
static Constructor findConstructor(const ImageSpec& spec);
public: public:
virtual SectorSet readImage() = 0; virtual SectorSet readImage() = 0;

View File

@@ -7,6 +7,15 @@
#include "imagewriter/imagewriter.h" #include "imagewriter/imagewriter.h"
#include "fmt/format.h" #include "fmt/format.h"
std::map<std::string, ImageWriter::Constructor> ImageWriter::formats =
{
{".adf", ImageWriter::createImgImageWriter},
{".d64", ImageWriter::createD64ImageWriter},
{".d81", ImageWriter::createImgImageWriter},
{".img", ImageWriter::createImgImageWriter},
{".ldbs", ImageWriter::createLDBSImageWriter},
};
static bool ends_with(const std::string& value, const std::string& ending) static bool ends_with(const std::string& value, const std::string& ending)
{ {
if (ending.size() > value.size()) if (ending.size() > value.size())
@@ -14,19 +23,29 @@ static bool ends_with(const std::string& value, const std::string& ending)
return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
} }
std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const ImageSpec& spec) ImageWriter::Constructor ImageWriter::findConstructor(const ImageSpec& spec)
{ {
const auto& filename = spec.filename; const auto& filename = spec.filename;
if (ends_with(filename, ".img") || ends_with(filename, ".adf")) for (const auto& e : formats)
return createImgImageWriter(sectors, spec); {
else if (ends_with(filename, ".ldbs")) if (ends_with(filename, e.first))
return createLDBSImageWriter(sectors, spec); return e.second;
else if (ends_with(filename, ".d64")) }
return createD64ImageWriter(sectors, spec);
Error() << "unrecognised image filename extension"; return NULL;
return std::unique_ptr<ImageWriter>(); }
std::unique_ptr<ImageWriter> ImageWriter::create(const SectorSet& sectors, const ImageSpec& spec)
{
verifyImageSpec(spec);
return findConstructor(spec)(sectors, spec);
}
void ImageWriter::verifyImageSpec(const ImageSpec& spec)
{
if (!findConstructor(spec))
Error() << "unrecognised image filename extension";
} }
ImageWriter::ImageWriter(const SectorSet& sectors, const ImageSpec& spec): ImageWriter::ImageWriter(const SectorSet& sectors, const ImageSpec& spec):

View File

@@ -12,8 +12,17 @@ public:
public: public:
static std::unique_ptr<ImageWriter> create(const SectorSet& sectors, const ImageSpec& spec); static std::unique_ptr<ImageWriter> create(const SectorSet& sectors, const ImageSpec& spec);
static void verifyImageSpec(const ImageSpec& filename);
private: private:
typedef
std::function<
std::unique_ptr<ImageWriter>(const SectorSet& sectors, const ImageSpec& spec)
>
Constructor;
static std::map<std::string, Constructor> formats;
static std::unique_ptr<ImageWriter> createImgImageWriter( static std::unique_ptr<ImageWriter> createImgImageWriter(
const SectorSet& sectors, const ImageSpec& spec); const SectorSet& sectors, const ImageSpec& spec);
static std::unique_ptr<ImageWriter> createLDBSImageWriter( static std::unique_ptr<ImageWriter> createLDBSImageWriter(
@@ -21,6 +30,8 @@ private:
static std::unique_ptr<ImageWriter> createD64ImageWriter( static std::unique_ptr<ImageWriter> createD64ImageWriter(
const SectorSet& sectors, const ImageSpec& spec); const SectorSet& sectors, const ImageSpec& spec);
static Constructor findConstructor(const ImageSpec& spec);
public: public:
virtual void adjustGeometry(); virtual void adjustGeometry();
void printMap(); void printMap();

View File

@@ -9,14 +9,16 @@
#include "decoders/decoders.h" #include "decoders/decoders.h"
#include "sector.h" #include "sector.h"
#include "sectorset.h" #include "sectorset.h"
#include "visualiser.h"
#include "record.h" #include "record.h"
#include "image.h" #include "image.h"
#include "bytes.h" #include "bytes.h"
#include "decoders/rawbits.h" #include "decoders/rawbits.h"
#include "track.h" #include "track.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h" #include "fmt/format.h"
FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags }; FlagGroup readerFlags { &hardwareFluxSourceFlags, &fluxmapReaderFlags, &visualiserFlags };
static DataSpecFlag source( static DataSpecFlag source(
{ "--source", "-s" }, { "--source", "-s" },
@@ -33,6 +35,11 @@ static StringFlag destination(
"write the raw magnetic flux to this file", "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( static SettableFlag justRead(
{ "--just-read" }, { "--just-read" },
"just read the disk and do no further processing"); "just read the disk and do no further processing");
@@ -142,7 +149,7 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme
return; return;
} }
} }
if (!replacing || (replacing->status != Sector::OK)) if (!replacing || ((replacing->status != Sector::OK) && (replacement.status == Sector::OK)))
{ {
if (!replacing) if (!replacing)
replacing.reset(new Sector); replacing.reset(new Sector);
@@ -153,7 +160,8 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme
void readDiskCommand(AbstractDecoder& decoder) void readDiskCommand(AbstractDecoder& decoder)
{ {
const ImageSpec outputSpec(output); const ImageSpec outputSpec(output);
ImageWriter::verifyImageSpec(outputSpec);
bool failures = false; bool failures = false;
SectorSet allSectors; SectorSet allSectors;
auto tracks = readTracks(); auto tracks = readTracks();
@@ -251,6 +259,9 @@ void readDiskCommand(AbstractDecoder& decoder)
std::cout << size << " bytes decoded." << std::endl; std::cout << size << " bytes decoded." << std::endl;
} }
if (!visualise.get().empty())
visualiseSectorsToFile(allSectors, visualise.get());
writeSectorsToFile(allSectors, outputSpec); writeSectorsToFile(allSectors, outputSpec);
if (failures) if (failures)
std::cerr << "Warning: some sectors could not be decoded." << std::endl; std::cerr << "Warning: some sectors could not be decoded." << std::endl;

View File

@@ -27,6 +27,10 @@ public:
Status status = Status::INTERNAL_ERROR; Status status = Status::INTERNAL_ERROR;
Fluxmap::Position position; Fluxmap::Position position;
nanoseconds_t clock = 0; nanoseconds_t clock = 0;
nanoseconds_t headerStartTime = 0;
nanoseconds_t headerEndTime = 0;
nanoseconds_t dataStartTime = 0;
nanoseconds_t dataEndTime = 0;
int physicalTrack = 0; int physicalTrack = 0;
int physicalSide = 0; int physicalSide = 0;
int logicalTrack = 0; int logicalTrack = 0;

View File

@@ -17,6 +17,9 @@ public:
std::unique_ptr<Sector>& get(int track, int head, int sector); std::unique_ptr<Sector>& get(int track, int head, int sector);
Sector* get(int track, int head, int sector) const; Sector* get(int track, int head, int sector) const;
const std::map<const key_t, std::unique_ptr<Sector>>& get() const
{ return _data; }
void calculateSize( void calculateSize(
unsigned& numTracks, unsigned& numHeads, unsigned& numSectors, unsigned& numTracks, unsigned& numHeads, unsigned& numSectors,
unsigned& sectorSize) const; unsigned& sectorSize) const;

95
lib/visualiser.cc Normal file
View File

@@ -0,0 +1,95 @@
#define _USE_MATH_DEFINES
#include "globals.h"
#include "image.h"
#include "sector.h"
#include "sectorset.h"
#include "visualiser.h"
#include "fmt/format.h"
#include "flags.h"
#include <iostream>
#include <fstream>
#include <math.h>
FlagGroup visualiserFlags;
static IntFlag period(
{ "--visualiser-period" },
"rotational period for use by the visualiser (milliseconds)",
200);
static const int SIZE = 480;
static const int BORDER = 10;
static const int RADIUS = (SIZE/2) - (BORDER/2);
static const int CORE = 50;
static const int TRACKS = 83;
static const double TRACK_SPACING = double(RADIUS-CORE) / TRACKS;
void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename)
{
std::cout << "writing visualisation\n";
std::ofstream f(filename, std::ios::out);
if (!f.is_open())
Error() << "cannot open visualisation file";
f << fmt::format("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"{0} {1} {2} {3}\">",
0, 0, SIZE*2, SIZE);
const double radians_per_ns = 2.0*M_PI / (period*1e6);
auto drawSide = [&](int side)
{
f << fmt::format("<g transform='matrix(1 0 0 -1 {} {})'>", SIZE/2 + (side*SIZE), SIZE/2);
f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='none' fill='#ccc'/>", RADIUS);
for (int physicalTrack = 0; physicalTrack < TRACKS; physicalTrack++)
{
double radius = CORE + physicalTrack*TRACK_SPACING;
f << fmt::format("<circle cx='0' cy='0' r='{}' stroke='#888' stroke-width='0.5' fill='none'/>", radius);
auto drawArc = [&](const std::unique_ptr<Sector>& sector, nanoseconds_t start, nanoseconds_t end, const std::string& colour)
{
start %= period*1000000;
end %= period*1000000;
if (end < start)
end += period*1000000;
double theta1 = start * radians_per_ns;
double theta2 = end * radians_per_ns;
int large = (theta2 - theta1) >= M_PI;
f << fmt::format("\n<!-- {} {} = {} {} -->", start, end, theta1, theta2);
f << fmt::format("<path fill='none' stroke='{}' stroke-width='1.5' d='", colour);
f << fmt::format("M {} {} ", cos(theta1)*radius, sin(theta1)*radius);
f << fmt::format("A {0} {0} 0 {3} 1 {1} {2}", radius, cos(theta2)*radius, sin(theta2)*radius, large);
f << fmt::format("'><title>Track {} Head {} Sector {}; {}ms to {}ms</title></path>",
sector->logicalTrack, sector->logicalSide, sector->logicalSector,
start/1e6, end/1e6);
};
/* Sadly, SectorSets aren't indexable by physical track. */
for (const auto& e : sectors.get())
{
const auto& sector = e.second;
if ((sector->physicalSide == side) && (sector->physicalTrack == physicalTrack))
{
const char* colour = "#f00";
if (sector->status == Sector::OK)
colour = "#00f";
if (sector->headerStartTime && sector->headerEndTime)
drawArc(sector, sector->headerStartTime, sector->headerEndTime, "#0ff");
if (sector->dataStartTime && sector->dataEndTime)
drawArc(sector, sector->dataStartTime, sector->dataEndTime, colour);
}
}
}
f << "</g>";
};
f << fmt::format("<rect x='0' y='0' width='{}' height='{}' stroke='none' fill='#fff'/>", SIZE*2, SIZE);
drawSide(0);
drawSide(1);
f << "</svg>";
}

12
lib/visualiser.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef VISUALISER_H
#define VISUALISER_H
#include "flags.h"
class SectorSet;
extern FlagGroup visualiserFlags;
extern void visualiseSectorsToFile(const SectorSet& sectors, const std::string& filename);
#endif

View File

@@ -183,6 +183,7 @@ buildlibrary libbackend.a \
lib/sectorset.cc \ lib/sectorset.cc \
lib/sql.cc \ lib/sql.cc \
lib/usb.cc \ lib/usb.cc \
lib/visualiser.cc \
lib/writer.cc \ lib/writer.cc \
buildlibrary libfrontend.a \ buildlibrary libfrontend.a \

View File

@@ -17,13 +17,18 @@ static IntFlag sectorIdBase(
"Sector ID of the first sector.", "Sector ID of the first sector.",
1); 1);
static BoolFlag ignoreSideByte(
{ "--ignore-side-byte" },
"Ignore the side byte in the sector ID, and use the physical side instead.",
false);
int mainReadIBM(int argc, const char* argv[]) int mainReadIBM(int argc, const char* argv[])
{ {
setReaderDefaultSource(":t=0-79:s=0-1"); setReaderDefaultSource(":t=0-79:s=0-1");
setReaderDefaultOutput("ibm.img"); setReaderDefaultOutput("ibm.img");
flags.parseFlags(argc, argv); flags.parseFlags(argc, argv);
IbmDecoder decoder(sectorIdBase); IbmDecoder decoder(sectorIdBase, ignoreSideByte);
readDiskCommand(decoder); readDiskCommand(decoder);
return 0; return 0;
} }