Merge from master.

This commit is contained in:
David Given
2021-05-07 22:27:45 +02:00
20 changed files with 202 additions and 73 deletions

View File

@@ -149,7 +149,10 @@ std::unique_ptr<Fluxmap> IbmEncoder::encode(
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);
{
/* If there are any missing sectors, this is an empty track. */
return std::unique_ptr<Fluxmap>();
}
/* Writing the sector and data records are fantastically annoying.
* The CRC is calculated from the *very start* of the record, and

View File

@@ -46,6 +46,10 @@ You should end up with a `brother.img` which is 239616 bytes long.
Writing disks
-------------
Only 240kB disks can be written, currently ( [get in
touch](https://github.com/davidgiven/fluxengine/issues/new) if you want to
write 120kB disks).
Just do:
```

View File

@@ -49,6 +49,34 @@ You _can_ work with more than one FluxEngine at the same time, using different
invocations of the client; but be careful of USB bandwidth. If the devices are
connected via the same hub, the bandwidth will be shared.
### Logical and physical tracks
In general, FluxEngine will read one track off disk, and write it to one track
in a file, or vice versa. Sometimes these don't match. Two important FluxEngine
concepts are that of the _physical track_ and the _logical track_.
_Physical tracks_ are how FluxEngine locates tracks on the disk. The numbering
used by 80-track drives is always used, even if you actually have a 40-track
drive attached (this actually makes things simpler).
_Logical tracks_ are where the data is in the filesystem. This doesn't need to
match the physical track. The logical track number is usually encoded on the
disk itself in the sector header. FluxEngine uses this for placing the data in
the output file.
The most common situation where these won't match is when you have a 40-track
disk in an 80-track drive. Because each 40-track track is twice the width of an
80-track track, you'll see logical track 0 on physical tracks 0 and 1, and
logical track 1 on physical tracks 2 and 3, and logical track 2 on physical
tracks 4 and 5, etc.
When reading from a disk, this will usually take care of itself as disks are
mostly self-describing --- FluxEngine can tell which logical track data is
located at from the sector header. However, when writing to a disk, this isn't
the case, and you may to supply extra parameters to tell FluxEngine the mapping
from data in the image to physical tracks. This is most likely to happen when
using 40-track disks.
### Source and destination specifiers
When reading from or writing _flux_ (either from or to a real disk, or a flux
@@ -78,11 +106,11 @@ fluxengine read ibm -s fakedisk.flux:t=0-79:s=0
`some/files/:t=0-10`. There must be a files for a single disk only
in the directory.
Source and destination specifiers work entirely in *physical units*.
FluxEngine is intended to be connected to an 80 (or 82) track double sided
drive, and these are the units used. If the format you're trying to access
lays out its tracks differently, then you'll need a specifier which tells
FluxEngine how to find those tracks. See the 40-track disk example above.
Source and destination specifiers work entirely in *physical units*. As
described above, FluxEngine is intended to be connected to an 80 (or 82) track
double sided drive, and these are the units used. If the format you're trying
to access lays out its tracks differently, then you'll need a specifier which
tells FluxEngine how to find those tracks. See the 40-track disk example above.
If you _don't_ specify a modifier, you'll get the default, which should be
sensible for the command you're using.
@@ -104,6 +132,11 @@ exact format varies according to the extension:
geometry will be autodetected if left unspecified. For input files you
normally have to specify it.
If one logical track does not map directly onto on physical track, you can
change this with `:o=1:t=2`: `o` specifies the offset, and `t` specifies
the step. So, with this format, cylinder 1 in the image will be written to
track 3 on the disk.
- `.ldbs`: John Elliott's [LDBS disk image
format](http://www.seasip.info/Unix/LibDsk/ldbs.html), which is
consumable by the [libdsk](http://www.seasip.info/Unix/LibDsk/) suite of
@@ -152,7 +185,7 @@ disks, and have different magnetic properties. 3.5" drives can usually
autodetect what kind of medium is inserted into the drive based on the hole
in the disk casing, but 5.25" drives can't. As a result, you need to
explicitly tell FluxEngine on the command line whether you're using a high
density disk or not with the `--hd` flag.
density disk or not with the `-H` flag.
**If you don't do this, your disks may not read correctly and will _certainly_
fail to write correctly.**
@@ -164,6 +197,31 @@ case, and reading the disk label is much more reliable.
[Lots more information on high density vs double density disks can be found
here.](http://www.retrotechnology.com/herbs_stuff/guzis.html)
### 40-track disks and drives
These require special handling.
- reading a 40-track disk from an 80-track drive: everything should just work
via autodetection.
- writing a 40-track disk to an 80-track drive: you want to write _all
physical tracks_, so `-d :t=0-79`. For `.img` files you will also need `-i
:t=2` to set the mapping between logical tracks in the image and physical
tracks on the disk.
- reading a 40-track disk from a 40-track drive: use `--40-track` to tell
FluxEngine you have a 40-track drive; everything should just work via
autodetection.
- writing a 40-track disk to a 40-track drive: you want to write _even tracks
only_, so `-d :t=0-79x2`, and for `.img` files you will also need `-i
:t=2`.
The `--40-track` or `-4` option tells FluxEngine that it's plugged into a
40-track drive. It will assume that each step of the drive corresponds to two
physical tracks. Only even tracks are accessible in this mode.
### Other important flags
These flags apply to many operations and are useful for modifying the overall

View File

@@ -162,6 +162,8 @@ ImageSpec::ImageSpec(const DataSpec& spec)
if (!spec.has("c") && !spec.has("h") && !spec.has("s") && !spec.has("b"))
{
cylinders = heads = sectors = bytes = 0;
physicalOffset = 0;
physicalStep = 1;
initialised = false;
}
else
@@ -170,6 +172,8 @@ ImageSpec::ImageSpec(const DataSpec& spec)
heads = spec.at("h").only();
sectors = spec.at("s").only();
bytes = spec.at("b").only();
physicalOffset = spec.atOr("o", 0);
physicalStep = spec.atOr("t", 1);
initialised = true;
}
}
@@ -181,18 +185,22 @@ ImageSpec::ImageSpec(const DataSpec& spec)
for (const auto& e : spec.modifiers)
{
const auto name = e.second.name;
if ((name != "c") && (name != "h") && (name != "s") && (name != "b"))
Error() << fmt::format("unknown fluxspec modifier '{}'", name);
static const std::set<std::string> modifiers { "c", "h", "s", "b", "o", "t" };
if (modifiers.find(name) == modifiers.end())
Error() << fmt::format("unknown imagespec modifier '{}'", name);
}
}
ImageSpec::ImageSpec(const std::string filename,
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes):
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes,
int physicalOffset, int physicalStep):
filename(filename),
cylinders(cylinders),
heads(heads),
sectors(sectors),
bytes(bytes),
physicalOffset(physicalOffset),
physicalStep(physicalStep),
initialised(true)
{}

View File

@@ -48,6 +48,9 @@ public:
const Modifier& at(const std::string& mod) const;
bool has(const std::string& mod) const;
unsigned atOr(const std::string& mod, unsigned value) const
{ return has(mod) ? at(mod).only() : value; }
std::string filename;
std::map<std::string, Modifier> modifiers;
};
@@ -82,7 +85,8 @@ class ImageSpec
public:
ImageSpec(const DataSpec& dataspec);
ImageSpec(const std::string filename,
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes);
unsigned cylinders, unsigned heads, unsigned sectors, unsigned bytes,
int physicalOffset=0, int physicalStep=1);
public:
std::string filename;
@@ -90,6 +94,8 @@ public:
unsigned heads;
unsigned sectors;
unsigned bytes;
int physicalOffset;
int physicalStep;
bool initialised : 1;
};

View File

@@ -0,0 +1,15 @@
#include "globals.h"
#include "flags.h"
#include "flaggroups/fluxsourcesink.h"
FlagGroup fluxSourceSinkFlags;
SettableFlag fluxSourceSinkFortyTrack(
{ "--40-track", "-4" },
"indicates a 40 track drive");
SettableFlag fluxSourceSinkHighDensity(
{ "--high-density", "-H" },
"set the drive to high density mode");

View File

@@ -0,0 +1,13 @@
#ifndef FLUXSOURCESINK_H
#define FLUXSOURCESINK_H
#include "flags.h"
extern FlagGroup fluxSourceSinkFlags;
extern SettableFlag fluxSourceSinkFortyTrack;
extern SettableFlag fluxSourceSinkHighDensity;
#endif

View File

@@ -23,7 +23,6 @@ public:
virtual void writeFlux(int track, int side, Fluxmap& fluxmap) = 0;
};
extern void setHardwareFluxSinkDensity(bool high_density);
extern void setHardwareFluxSinkHardSectorCount(int sectorCount);
#endif

View File

@@ -3,14 +3,14 @@
#include "fluxmap.h"
#include "usb/usb.h"
#include "fluxsink/fluxsink.h"
#include "flaggroups/fluxsourcesink.h"
#include "fmt/format.h"
FlagGroup hardwareFluxSinkFlags = {
&fluxSourceSinkFlags,
&usbFlags,
};
static bool high_density = false;
static IntFlag indexMode(
{ "--write-index-mode" },
"index pulse source (0=drive, 1=300 RPM fake source, 2=360 RPM fake source",
@@ -21,11 +21,6 @@ static IntFlag hardSectorCount(
"number of hard sectors on the disk (0=soft sectors)",
0);
void setHardwareFluxSinkDensity(bool high_density)
{
::high_density = high_density;
}
void setHardwareFluxSinkHardSectorCount(int sectorCount)
{
::hardSectorCount.setDefaultValue(sectorCount);
@@ -39,7 +34,7 @@ public:
{
if (hardSectorCount != 0)
{
usbSetDrive(_drive, high_density, indexMode);
usbSetDrive(_drive, fluxSourceSinkHighDensity, indexMode);
std::cerr << "Measuring rotational speed... " << std::flush;
nanoseconds_t oneRevolution = usbGetRotationalPeriod(hardSectorCount);
_hardSectorThreshold = oneRevolution * 3 / (4 * hardSectorCount);
@@ -56,8 +51,15 @@ public:
public:
void writeFlux(int track, int side, Fluxmap& fluxmap)
{
usbSetDrive(_drive, high_density, indexMode);
usbSeek(track);
usbSetDrive(_drive, fluxSourceSinkHighDensity, indexMode);
if (fluxSourceSinkFortyTrack)
{
if (track & 1)
Error() << "cannot write to odd physical tracks in 40-track mode";
usbSeek(track / 2);
}
else
usbSeek(track);
return usbWrite(side, fluxmap.rawBytes(), _hardSectorThreshold);
}

View File

@@ -28,7 +28,6 @@ public:
};
extern void setHardwareFluxSourceRevolutions(double revolutions);
extern void setHardwareFluxSourceDensity(bool high_density);
extern void setHardwareFluxSourceSynced(bool synced);
extern void setHardwareFluxSourceHardSectorCount(int sectorCount);

View File

@@ -3,9 +3,11 @@
#include "fluxmap.h"
#include "usb/usb.h"
#include "fluxsource/fluxsource.h"
#include "flaggroups/fluxsourcesink.h"
#include "fmt/format.h"
FlagGroup hardwareFluxSourceFlags = {
&fluxSourceSinkFlags,
&usbFlags
};
@@ -29,20 +31,13 @@ static IntFlag hardSectorCount(
"number of hard sectors on the disk (0=soft sectors)",
0);
static bool high_density = false;
void setHardwareFluxSourceDensity(bool high_density)
{
::high_density = high_density;
}
class HardwareFluxSource : public FluxSource
{
public:
HardwareFluxSource(unsigned drive):
_drive(drive)
{
usbSetDrive(_drive, high_density, indexMode);
usbSetDrive(_drive, fluxSourceSinkHighDensity, indexMode);
std::cerr << "Measuring rotational speed... " << std::flush;
_oneRevolution = usbGetRotationalPeriod(hardSectorCount);
if (hardSectorCount != 0)
@@ -59,8 +54,16 @@ public:
public:
std::unique_ptr<Fluxmap> readFlux(int track, int side)
{
usbSetDrive(_drive, high_density, indexMode);
usbSeek(track);
usbSetDrive(_drive, fluxSourceSinkHighDensity, indexMode);
if (fluxSourceSinkFortyTrack)
{
if (track & 1)
Error() << "cannot read from odd physical tracks in 40-track mode";
usbSeek(track / 2);
}
else
usbSeek(track);
Bytes data = usbRead(
side, synced, revolutions * _oneRevolution, _hardSectorThreshold);
auto fluxmap = std::make_unique<Fluxmap>();

View File

@@ -30,6 +30,9 @@ public:
spec.sectors, spec.bytes,
spec.cylinders * trackSize / 1024)
<< std::endl;
if ((spec.physicalOffset != 0) || (spec.physicalStep != 1))
std::cout << fmt::format("logical to physical track mapping: physical = logical*{} + {}\n",
spec.physicalStep, spec.physicalOffset);
SectorSet sectors;
for (int track = 0; track < spec.cylinders; track++)
@@ -43,10 +46,12 @@ public:
Bytes data(spec.bytes);
inputFile.read((char*) data.begin(), spec.bytes);
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
int physicalTrack = track*spec.physicalStep + spec.physicalOffset;
std::unique_ptr<Sector>& sector = sectors.get(physicalTrack, head, sectorId);
sector.reset(new Sector);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalTrack = track;
sector->logicalTrack = track;
sector->physicalTrack = physicalTrack;
sector->logicalSide = sector->physicalSide = head;
sector->logicalSector = sectorId;
sector->data = data;

View File

@@ -58,10 +58,6 @@ static IntFlag 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",
@@ -117,8 +113,6 @@ std::vector<std::unique_ptr<Track>> readTracks()
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;

View File

@@ -27,10 +27,6 @@ static DataSpecFlag input(
"input image file to read from",
"");
static SettableFlag highDensityFlag(
{ "--high-density", "-H" },
"set the drive to high density mode");
static sqlite3* outdb;
void setWriterDefaultDest(const std::string& dest)
@@ -60,9 +56,6 @@ void writeTracks(
std::cout << "Writing to: " << dest << std::endl;
setHardwareFluxSourceDensity(highDensityFlag);
setHardwareFluxSinkDensity(highDensityFlag);
std::shared_ptr<FluxSink> fluxSink = FluxSink::create(spec);
for (const auto& location : spec.locations)
@@ -71,16 +64,21 @@ void writeTracks(
std::unique_ptr<Fluxmap> fluxmap = producer(location.track, location.side);
if (!fluxmap)
{
/* Create an empty fluxmap for writing. */
fluxmap.reset(new Fluxmap());
}
/* Erase this track rather than writing. */
/* Precompensation actually seems to make things worse, so let's leave
* it disabled for now. */
//fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2);
fluxSink->writeFlux(location.track, location.side, *fluxmap);
std::cout << fmt::format(
"{0} ms in {1} bytes", int(fluxmap->duration()/1e6), fluxmap->bytes()) << std::endl;
fluxmap.reset(new Fluxmap());
fluxSink->writeFlux(location.track, location.side, *fluxmap);
std::cout << "erased\n";
}
else
{
/* Precompensation actually seems to make things worse, so let's leave
* it disabled for now. */
//fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2);
fluxSink->writeFlux(location.track, location.side, *fluxmap);
std::cout << fmt::format(
"{0} ms in {1} bytes", int(fluxmap->duration()/1e6), fluxmap->bytes()) << std::endl;
}
}
}

View File

@@ -233,6 +233,7 @@ buildlibrary libbackend.a \
lib/decoders/fluxmapreader.cc \
lib/decoders/fmmfm.cc \
lib/encoders/encoders.cc \
lib/flaggroups/fluxsourcesink.cc \
lib/flags.cc \
lib/fluxmap.cc \
lib/fluxsink/fluxsink.cc \

View File

@@ -7,11 +7,13 @@
#include "writer.h"
#include "protocol.h"
#include "fmt/format.h"
#include "flaggroups/fluxsourcesink.h"
#include "dep/agg/include/agg2d.h"
#include "dep/stb/stb_image_write.h"
#include <fstream>
static FlagGroup flags = {
&fluxSourceSinkFlags,
&usbFlags,
};
@@ -186,8 +188,15 @@ int mainAnalyseDriveResponse(int argc, const char* argv[])
if (spec.locations.size() != 1)
Error() << "the destination dataspec must contain exactly one track (two sides count as two tracks)";
usbSetDrive(spec.drive, false, F_INDEX_REAL);
usbSeek(spec.locations[0].track);
usbSetDrive(spec.drive, fluxSourceSinkHighDensity, F_INDEX_REAL);
int track = spec.locations[0].track;
if (fluxSourceSinkFortyTrack)
{
if (track & 1)
Error() << "you can only seek to even tracks on a 40-track disk";
track /= 2;
}
usbSeek(track);
std::cout << "Measuring rotational speed...\n";
nanoseconds_t period = usbGetRotationalPeriod(0);

View File

@@ -22,10 +22,6 @@ static StringFlag output(
"output AU file to write",
"output.au");
static SettableFlag highDensityFlag(
{ "--high-density", "--hd" },
"set the drive to high density mode");
static SettableFlag withIndex(
{ "--with-index" },
"place index markers in the right hand channel");
@@ -41,7 +37,6 @@ int mainConvertFluxToAu(int argc, const char* argv[])
const auto& location = *(locations.begin());
std::cerr << "Reading source flux...\n";
setHardwareFluxSourceDensity(highDensityFlag);
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(spec);
const auto& fluxmap = fluxsource->readFlux(location.track, location.side);
unsigned totalTicks = fluxmap->ticks() + 2;

View File

@@ -22,10 +22,6 @@ static StringFlag output(
"output VCD file to write",
"output.vcd");
static SettableFlag highDensityFlag(
{ "--high-density", "--hd" },
"set the drive to high density mode");
int mainConvertFluxToVcd(int argc, const char* argv[])
{
flags.parseFlags(argc, argv);
@@ -37,7 +33,6 @@ int mainConvertFluxToVcd(int argc, const char* argv[])
const auto& location = *(locations.begin());
std::cerr << "Reading source flux...\n";
setHardwareFluxSourceDensity(highDensityFlag);
std::shared_ptr<FluxSource> fluxsource = FluxSource::create(spec);
const auto& fluxmap = fluxsource->readFlux(location.track, location.side);

View File

@@ -1,9 +1,11 @@
#include "globals.h"
#include "flags.h"
#include "usb/usb.h"
#include "flaggroups/fluxsourcesink.h"
#include "protocol.h"
static FlagGroup flags = {
&fluxSourceSinkFlags,
&usbFlags,
};
@@ -22,6 +24,13 @@ int mainSeek(int argc, const char* argv[])
flags.parseFlags(argc, argv);
usbSetDrive(drive, false, F_INDEX_REAL);
usbSeek(track);
if (fluxSourceSinkFortyTrack)
{
if (track & 1)
Error() << "you can only seek to even tracks on a 40-track drive";
usbSeek(track / 2);
}
else
usbSeek(track);
return 0;
}

View File

@@ -111,15 +111,28 @@ static void test_fluxspec(void)
static void test_imagespec(void)
{
DataSpec spec("foo:c=9:h=2:s=99:b=256");
{
DataSpec spec("foo:c=9:h=2:s=99:b=256");
ImageSpec ispec(spec);
assert(ispec.filename == "foo");
assert(ispec.cylinders == 9);
assert(ispec.heads == 2);
assert(ispec.sectors == 99);
assert(ispec.bytes = 256);
assert(ispec.bytes == 256);
assert(ispec.physicalOffset == 0);
assert(ispec.physicalStep == 1);
}
{
DataSpec spec("foo:c=9:h=2:s=99:b=256:o=2:t=9");
ImageSpec ispec(spec);
assert(ispec.filename == "foo");
assert(ispec.cylinders == 9);
assert(ispec.heads == 2);
assert(ispec.sectors == 99);
assert(ispec.bytes == 256);
assert(ispec.physicalOffset == 2);
assert(ispec.physicalStep == 9);
}
}