mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Merge from master.
This commit is contained in:
@@ -123,15 +123,16 @@ public:
|
||||
/* Read and decode data. */
|
||||
|
||||
auto readApple8 = [&]() {
|
||||
auto result = readRaw8();
|
||||
while(result & 0x80 == 0) {
|
||||
auto result = 0;
|
||||
while((result & 0x80) == 0) {
|
||||
auto b = readRawBits(1);
|
||||
if(b.empty()) break;
|
||||
result = (result << 1) | b[0];
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
constexpr unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH + 2;
|
||||
constexpr unsigned recordLength = APPLE2_ENCODED_SECTOR_LENGTH+2;
|
||||
uint8_t bytes[recordLength];
|
||||
for(auto &byte : bytes) {
|
||||
byte = readApple8();
|
||||
|
||||
@@ -57,6 +57,7 @@ public:
|
||||
int logicalTrack = physicalTrack / 2;
|
||||
double clockRateUs = 4.;
|
||||
|
||||
// apple2 drives spin at 300rpm. 300/minute in us = 200000.
|
||||
int bitsPerRevolution = 200000.0 / clockRateUs;
|
||||
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
@@ -110,26 +111,28 @@ private:
|
||||
write_bits(encode_data_gcr(value), 8);
|
||||
};
|
||||
|
||||
auto write_ff40 = [&]() {
|
||||
write_bits(0xff0, 12);
|
||||
};
|
||||
|
||||
auto write_ff36 = [&]() {
|
||||
write_bits(0xff << 2, 10);
|
||||
};
|
||||
|
||||
auto write_ff32 = [&]() {
|
||||
write_bits(0xff, 8);
|
||||
// The special "FF40" sequence is used to synchronize the receiving
|
||||
// shift register. It's written as "1111 1111 00"; FF indicates the
|
||||
// 8 consecutive 1-bits, while "40" indicates the total number of
|
||||
// microseconds.
|
||||
auto write_ff40 = [&](int n=1) {
|
||||
for(;n--;) {
|
||||
write_bits(0xff << 2, 10);
|
||||
}
|
||||
};
|
||||
|
||||
// There is data to encode to disk.
|
||||
if ((sector.data.size() != APPLE2_SECTOR_LENGTH))
|
||||
Error() << fmt::format("unsupported sector size {} --- you must pick 256", sector.data.size());
|
||||
|
||||
// Write address syncing leader : A sequence of "FF40"s followed by an "FF32", 5 to 40 of them
|
||||
// "FF40" seems to indicate that the actual data written is "1111 1111 0000" i.e., 8 1s and a total of 40 microseconds
|
||||
for(int i=0; i<4; i++) { write_ff40(); }
|
||||
write_ff32();
|
||||
// Write address syncing leader : A sequence of "FF40"s; 5 of them
|
||||
// are said to suffice to synchronize the decoder.
|
||||
// "FF40" indicates that the actual data written is "1111
|
||||
// 1111 00" i.e., 8 1s and a total of 40 microseconds
|
||||
//
|
||||
// In standard formatting, the first logical sector apparently gets
|
||||
// extra padding.
|
||||
write_ff40(sector.logicalSector == 0 ? 32 : 8);
|
||||
|
||||
// Write address field: APPLE2_SECTOR_RECORD + sector identifier + DE AA EB
|
||||
write_bits(APPLE2_SECTOR_RECORD, 24);
|
||||
@@ -139,13 +142,8 @@ private:
|
||||
write_gcr44(volume_id ^ sector.logicalTrack ^ sector.logicalSector);
|
||||
write_bits(0xDEAAEB, 24);
|
||||
|
||||
// Write the "zip": a gap of 50 (we actually do 52, hopefully it's OK).
|
||||
// In real HW this is actually turning OFF the write head for 50 cycles
|
||||
write_bits(0, 13);
|
||||
|
||||
// Write data syncing leader: FF40 x4 + FF36 + APPLE2_DATA_RECORD + sector data + sum + DE AA EB (+ mystery bits cut off of the scan?)
|
||||
for(int i=0; i<4; i++) write_ff40();
|
||||
write_ff36();
|
||||
// Write data syncing leader: FF40 + APPLE2_DATA_RECORD + sector data + sum + DE AA EB (+ mystery bits cut off of the scan?)
|
||||
write_ff40(8);
|
||||
write_bits(APPLE2_DATA_RECORD, 24);
|
||||
|
||||
// Convert the sector data to GCR, append the checksum, and write it out
|
||||
|
||||
@@ -78,13 +78,22 @@ static int encode_data_gcr(uint8_t data)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void write_byte(std::vector<bool>& bits, unsigned& cursor, uint8_t b)
|
||||
{
|
||||
write_bits(bits, cursor, encode_data_gcr(b>>4), 5);
|
||||
write_bits(bits, cursor, encode_data_gcr(b), 5);
|
||||
}
|
||||
|
||||
static void write_bytes(std::vector<bool>& bits, unsigned& cursor, const Bytes& bytes)
|
||||
{
|
||||
for (uint8_t b : bytes)
|
||||
{
|
||||
write_bits(bits, cursor, encode_data_gcr(b>>4), 5);
|
||||
write_bits(bits, cursor, encode_data_gcr(b), 5);
|
||||
}
|
||||
write_byte(bits, cursor, b);
|
||||
}
|
||||
|
||||
static void write_gap(std::vector<bool>& bits, unsigned& cursor, int length)
|
||||
{
|
||||
for (int i = 0; i < length/10; i++)
|
||||
write_byte(bits, cursor, '0');
|
||||
}
|
||||
|
||||
static void write_sector(std::vector<bool>& bits, unsigned& cursor,
|
||||
@@ -102,8 +111,8 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor,
|
||||
(uint8_t)(encodedTrack + encodedSector),
|
||||
});
|
||||
|
||||
write_gap(bits, cursor, trackdata.post_header_gap_bits());
|
||||
|
||||
write_zero_bits(bits, cursor, trackdata.post_header_gap_bits());
|
||||
write_one_bits(bits, cursor, trackdata.pre_data_sync_bits());
|
||||
write_bits(bits, cursor, VICTOR9K_DATA_RECORD, 10);
|
||||
|
||||
@@ -112,8 +121,7 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor,
|
||||
Bytes checksum(2);
|
||||
checksum.writer().write_le16(sumBytes(sector.data));
|
||||
write_bytes(bits, cursor, checksum);
|
||||
|
||||
write_zero_bits(bits, cursor, trackdata.post_data_gap_bits());
|
||||
write_gap(bits, cursor, trackdata.post_data_gap_bits());
|
||||
}
|
||||
|
||||
class Victor9kEncoder : public AbstractEncoder
|
||||
|
||||
@@ -8,11 +8,13 @@ class DecoderProto;
|
||||
|
||||
/* ... 1101 0101 0111
|
||||
* ^^ ^^^^ ^^^^ ten bit IO byte */
|
||||
#define VICTOR9K_SECTOR_RECORD 0xfffffd57
|
||||
#define VICTOR9K_SECTOR_RECORD 0xfffffd57
|
||||
#define VICTOR9K_HEADER_ID 0x7
|
||||
|
||||
/* ... 1101 0100 1001
|
||||
* ^^ ^^^^ ^^^^ ten bit IO byte */
|
||||
#define VICTOR9K_DATA_RECORD 0xfffffd49
|
||||
#define VICTOR9K_DATA_ID 0x8
|
||||
|
||||
#define VICTOR9K_SECTOR_LENGTH 512
|
||||
|
||||
|
||||
@@ -187,12 +187,12 @@ install some support packages.
|
||||
|
||||
- For Linux (this is Ubuntu, but this should apply to Debian too):
|
||||
`ninja-build`, `libusb-1.0-0-dev`, `libsqlite3-dev`, `zlib1g-dev`,
|
||||
`libudev-dev`.
|
||||
`libudev-dev`, `protobuf-compiler`, `libwxgtk3.0-gtk3-dev`.
|
||||
- For OSX with Homebrew: `ninja`, `libusb`, `pkg-config`, `sqlite`,
|
||||
`protobuf`.
|
||||
`protobuf`, `truncate`, `wxwidgets`.
|
||||
- For Windows with MSYS2: `make`, `ninja`, `mingw-w64-i686-libusb`,
|
||||
`mingw-w64-i686-protobuf`, `mingw-w64-i686-sqlite3`, `mingw-w64-i686-zlib`,
|
||||
`mingw-w64-i686-gcc`.
|
||||
`mingw-w64-i686-gcc`, `vim`, `diffutils`, `mingw-w64-i686-wxWidgets`.
|
||||
|
||||
These lists are not necessarily exhaustive --- please [get in
|
||||
touch](https://github.com/davidgiven/fluxengine/issues/new) if I've missed
|
||||
|
||||
@@ -111,13 +111,13 @@ with an obvious directory and allocation table. I have reversed engineered
|
||||
a very simple tool for extracting files from it. To show the directory, do:
|
||||
|
||||
```
|
||||
.obj/brother120tool image.img
|
||||
brother120tool image.img
|
||||
```
|
||||
|
||||
To extract a file, do:
|
||||
|
||||
```
|
||||
.obj/brother120tool image.img filename
|
||||
brother120tool image.img filename
|
||||
```
|
||||
|
||||
Wildcards are supported, so use `'*'` for the filename (remember to quote it)
|
||||
@@ -131,6 +131,18 @@ format](https://mathesoft.eu/brother-wp-1-dokumente/) and has produced a
|
||||
RTF](https://mathesoft.eu/sdm_downloads/wp2rtf/). This will only work on WP-1
|
||||
files.
|
||||
|
||||
To create a disk image (note: this creates a _new_ disk image, overwriting the
|
||||
previous image), do:
|
||||
|
||||
```
|
||||
brother120tool --create image.img filename1 filename2...
|
||||
```
|
||||
|
||||
Any files whose names begin with an asterisk (`*`) will be marked as hidden. If
|
||||
the file is named `*boot`, then a boot sector will be created which will load
|
||||
and run the file at 0x7000 if the machine is started with CODE+Q pressed. So
|
||||
far this has only been confirmed to work on a WP-1.
|
||||
|
||||
Any questions? please [get in
|
||||
touch](https://github.com/davidgiven/fluxengine/issues/new).
|
||||
|
||||
|
||||
@@ -317,7 +317,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 `--flux_source/sink.drive.high_density` configuration setting.
|
||||
not with the `--drive.high_density` configuration setting.
|
||||
**If you don't do this, your disks may not read correctly and will _certainly_
|
||||
fail to write correctly.**
|
||||
|
||||
@@ -334,14 +334,14 @@ here.](http://www.retrotechnology.com/herbs_stuff/guzis.html)
|
||||
These flags apply to many operations and are useful for modifying the overall
|
||||
behaviour.
|
||||
|
||||
- `--flux_source.drive.revolutions=X`
|
||||
- `--drive.revolutions=X`
|
||||
|
||||
When reading, spin the disk X times. X
|
||||
can be a floating point number. The default is usually 1.2. Some formats
|
||||
default to 1. Increasing the number will sample more data, and can be
|
||||
useful on dubious disks to try and get a better read.
|
||||
|
||||
- `--flux_source.drive.sync_with_index=true|false`
|
||||
- `--drive.sync_with_index=true|false`
|
||||
|
||||
Wait for an index pulse
|
||||
before starting to read the disk. (Ignored for write operations.) By
|
||||
@@ -349,7 +349,7 @@ behaviour.
|
||||
disk problems it's helpful to have all your data start at the same place
|
||||
each time.
|
||||
|
||||
- `--flux_source.drive.index_source=X`, `--flux_sink.drive.index_source=X`
|
||||
- `--drive.index_source=X`
|
||||
|
||||
Set the source of index pulses when reading or writing respectively. This
|
||||
is for use with drives which don't produce index pulse data. `X` can be
|
||||
|
||||
18
lib/bytes.cc
18
lib/bytes.cc
@@ -160,6 +160,24 @@ Bytes Bytes::slice(unsigned start) const
|
||||
return slice(start, len);
|
||||
}
|
||||
|
||||
std::vector<Bytes> Bytes::split(uint8_t separator) const
|
||||
{
|
||||
std::vector<Bytes> vector;
|
||||
|
||||
int lastEnd = 0;
|
||||
for (int i=0; i<size(); i++)
|
||||
{
|
||||
if ((*this)[i] == separator)
|
||||
{
|
||||
vector.push_back(this->slice(lastEnd, i-lastEnd));
|
||||
lastEnd = i + 1;
|
||||
}
|
||||
}
|
||||
vector.push_back(this->slice(lastEnd));
|
||||
|
||||
return vector;
|
||||
}
|
||||
|
||||
std::vector<bool> Bytes::toBits() const
|
||||
{
|
||||
std::vector<bool> bits;
|
||||
|
||||
@@ -51,6 +51,8 @@ public:
|
||||
Bytes& clear()
|
||||
{ resize(0); return *this; }
|
||||
|
||||
std::vector<Bytes> split(uint8_t separator) const;
|
||||
|
||||
Bytes slice(unsigned start, unsigned len) const;
|
||||
Bytes slice(unsigned start) const;
|
||||
Bytes swab() const;
|
||||
|
||||
@@ -7,10 +7,11 @@ import "lib/imagewriter/imagewriter.proto";
|
||||
import "lib/fluxsource/fluxsource.proto";
|
||||
import "lib/fluxsink/fluxsink.proto";
|
||||
import "lib/usb/usb.proto";
|
||||
import "lib/drive.proto";
|
||||
import "lib/mapper.proto";
|
||||
import "lib/common.proto";
|
||||
|
||||
// NEXT_TAG: 15
|
||||
// NEXT_TAG: 16
|
||||
message ConfigProto {
|
||||
optional string comment = 8;
|
||||
optional bool is_extension = 13;
|
||||
@@ -20,6 +21,7 @@ message ConfigProto {
|
||||
|
||||
optional FluxSourceProto flux_source = 10;
|
||||
optional FluxSinkProto flux_sink = 11;
|
||||
optional DriveProto drive = 15;
|
||||
|
||||
optional EncoderProto encoder = 3;
|
||||
optional DecoderProto decoder = 4;
|
||||
|
||||
14
lib/drive.proto
Normal file
14
lib/drive.proto
Normal file
@@ -0,0 +1,14 @@
|
||||
syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
message DriveProto {
|
||||
optional int32 drive = 1 [default = 0, (help) = "which drive to write to (0 or 1)"];
|
||||
optional IndexMode index_mode = 2 [default = INDEXMODE_DRIVE, (help) = "index pulse source"];
|
||||
optional int32 hard_sector_count = 3 [default = 0, (help) = "number of hard sectors on disk"];
|
||||
optional bool high_density = 4 [default = true, (help) = "set if this is a high density disk"];
|
||||
optional bool sync_with_index = 5 [default = false, (help) = "start reading at index mark"];
|
||||
optional double revolutions = 6 [default = 1.2, (help) = "number of revolutions to read"];
|
||||
}
|
||||
|
||||
|
||||
@@ -6,12 +6,13 @@ enum FluxMagic {
|
||||
|
||||
enum FluxFileVersion {
|
||||
VERSION_1 = 1;
|
||||
VERSION_2 = 2;
|
||||
}
|
||||
|
||||
message TrackFluxProto {
|
||||
optional int32 cylinder = 1;
|
||||
optional int32 head = 2;
|
||||
optional bytes flux = 3;
|
||||
repeated bytes flux = 3;
|
||||
}
|
||||
|
||||
message FluxFileProto {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
Fluxmap& Fluxmap::appendBytes(const Bytes& bytes)
|
||||
{
|
||||
if (bytes.size() == 0)
|
||||
return *this;
|
||||
if (bytes.size() == 0)
|
||||
return *this;
|
||||
|
||||
return appendBytes(&bytes[0], bytes.size());
|
||||
}
|
||||
@@ -18,7 +18,7 @@ Fluxmap& Fluxmap::appendBytes(const uint8_t* ptr, size_t len)
|
||||
while (len--)
|
||||
{
|
||||
uint8_t byte = *ptr++;
|
||||
_ticks += byte & 0x3f;
|
||||
_ticks += byte & 0x3f;
|
||||
bw.write_8(byte);
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ Fluxmap& Fluxmap::appendBytes(const uint8_t* ptr, size_t len)
|
||||
|
||||
uint8_t& Fluxmap::findLastByte()
|
||||
{
|
||||
if (_bytes.empty())
|
||||
appendByte(0x00);
|
||||
return *(_bytes.end() - 1);
|
||||
if (_bytes.empty())
|
||||
appendByte(0x00);
|
||||
return *(_bytes.end() - 1);
|
||||
}
|
||||
|
||||
Fluxmap& Fluxmap::appendInterval(uint32_t ticks)
|
||||
@@ -46,19 +46,19 @@ Fluxmap& Fluxmap::appendInterval(uint32_t ticks)
|
||||
|
||||
Fluxmap& Fluxmap::appendPulse()
|
||||
{
|
||||
findLastByte() |= 0x80;
|
||||
findLastByte() |= 0x80;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fluxmap& Fluxmap::appendIndex()
|
||||
{
|
||||
findLastByte() |= 0x40;
|
||||
findLastByte() |= 0x40;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fluxmap& Fluxmap::appendDesync()
|
||||
{
|
||||
appendByte(F_DESYNC);
|
||||
appendByte(F_DESYNC);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -66,65 +66,66 @@ void Fluxmap::precompensate(int threshold_ticks, int amount_ticks)
|
||||
{
|
||||
uint8_t junk = 0xff;
|
||||
|
||||
for (unsigned i=0; i<_bytes.size(); i++)
|
||||
for (unsigned i = 0; i < _bytes.size(); i++)
|
||||
{
|
||||
uint8_t& prev = (i == 0) ? junk : _bytes[i-1];
|
||||
uint8_t prevticks = prev & 0x3f;
|
||||
uint8_t& prev = (i == 0) ? junk : _bytes[i - 1];
|
||||
uint8_t prevticks = prev & 0x3f;
|
||||
uint8_t currticks = _bytes[i] & 0x3f;
|
||||
|
||||
if (currticks < (3*threshold_ticks))
|
||||
if (currticks < (3 * threshold_ticks))
|
||||
{
|
||||
if ((prevticks <= threshold_ticks) && (currticks > threshold_ticks))
|
||||
{
|
||||
/* 01001; move the previous bit backwards. */
|
||||
if (prevticks >= (1+amount_ticks))
|
||||
if (prevticks >= (1 + amount_ticks))
|
||||
prev -= amount_ticks;
|
||||
if (currticks <= (0x7f-amount_ticks))
|
||||
if (currticks <= (0x7f - amount_ticks))
|
||||
currticks += amount_ticks;
|
||||
}
|
||||
else if ((prevticks > threshold_ticks) && (currticks <= threshold_ticks))
|
||||
else if ((prevticks > threshold_ticks) &&
|
||||
(currticks <= threshold_ticks))
|
||||
{
|
||||
/* 00101; move the current bit forwards. */
|
||||
if (prevticks <= (0x7f-amount_ticks))
|
||||
if (prevticks <= (0x7f - amount_ticks))
|
||||
prev += amount_ticks;
|
||||
if (currticks >= (1+amount_ticks))
|
||||
if (currticks >= (1 + amount_ticks))
|
||||
currticks -= amount_ticks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Fluxmap> Fluxmap::split() {
|
||||
std::vector<Fluxmap> maps;
|
||||
Fluxmap map;
|
||||
for (unsigned i=0; i<_bytes.size(); i++) {
|
||||
if (_bytes[i] == F_DESYNC) {
|
||||
if (i > 0)
|
||||
maps.push_back(map);
|
||||
map = Fluxmap();
|
||||
} else {
|
||||
map.appendByte(_bytes[i]);
|
||||
}
|
||||
std::vector<std::unique_ptr<const Fluxmap>> Fluxmap::split() const
|
||||
{
|
||||
std::vector<std::unique_ptr<const Fluxmap>> maps;
|
||||
auto bytesVector = rawBytes().split(F_DESYNC);
|
||||
|
||||
for (auto bytes : bytesVector)
|
||||
{
|
||||
if (bytes.size() != 0)
|
||||
maps.push_back(std::move(std::make_unique<Fluxmap>(bytes)));
|
||||
}
|
||||
maps.push_back(map);
|
||||
|
||||
return maps;
|
||||
}
|
||||
|
||||
void Fluxmap::rescale(double scale) {
|
||||
if (scale != 1.0) {
|
||||
auto bytesOrig = _bytes;
|
||||
_bytes = Bytes();
|
||||
_duration = 0;
|
||||
_ticks = 0;
|
||||
int lastEvent = 0;
|
||||
for (unsigned i=0; i<bytesOrig.size(); i++)
|
||||
std::unique_ptr<const Fluxmap> Fluxmap::rescale(double scale) const
|
||||
{
|
||||
if (scale == 1.0)
|
||||
return std::make_unique<Fluxmap>(rawBytes());
|
||||
|
||||
auto newFluxmap = std::make_unique<Fluxmap>();
|
||||
int lastEvent = 0;
|
||||
for (uint8_t b : _bytes)
|
||||
{
|
||||
lastEvent += b & 0x3f;
|
||||
if (b & 0xc0)
|
||||
{
|
||||
lastEvent += bytesOrig[i] & 0x3f;
|
||||
if (bytesOrig[i] & 0xc0) {
|
||||
appendInterval(lastEvent * scale + 0.5);
|
||||
findLastByte() |= bytesOrig[i] & 0xc0;
|
||||
lastEvent = 0;
|
||||
}
|
||||
newFluxmap->appendInterval(lastEvent * scale + 0.5);
|
||||
newFluxmap->findLastByte() |= b & 0xc0;
|
||||
lastEvent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return newFluxmap;
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ public:
|
||||
Fluxmap& appendBits(const std::vector<bool>& bits, nanoseconds_t clock);
|
||||
|
||||
void precompensate(int threshold_ticks, int amount_ticks);
|
||||
std::vector<Fluxmap> split();
|
||||
void rescale(double scale);
|
||||
std::vector<std::unique_ptr<const Fluxmap>> split() const;
|
||||
std::unique_ptr<const Fluxmap> rescale(double scale) const;
|
||||
|
||||
private:
|
||||
uint8_t& findLastByte();
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
void writeFlux(int cylinder, int head, Fluxmap& fluxmap)
|
||||
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override
|
||||
{
|
||||
unsigned totalTicks = fluxmap.ticks() + 2;
|
||||
unsigned channels = _config.index_markers() ? 2 : 1;
|
||||
|
||||
@@ -33,13 +33,14 @@ public:
|
||||
{
|
||||
FluxFileProto proto;
|
||||
proto.set_magic(FluxMagic::MAGIC);
|
||||
proto.set_version(FluxFileVersion::VERSION_1);
|
||||
proto.set_version(FluxFileVersion::VERSION_2);
|
||||
for (const auto& e : _data)
|
||||
{
|
||||
auto track = proto.add_track();
|
||||
track->set_cylinder(e.first.first);
|
||||
track->set_head(e.first.second);
|
||||
track->set_flux(e.second);
|
||||
for (const auto& fluxBytes : e.second)
|
||||
track->add_flux(fluxBytes);
|
||||
}
|
||||
|
||||
if (!proto.SerializeToOstream(&_of))
|
||||
@@ -50,9 +51,10 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
void writeFlux(int cylinder, int head, Fluxmap& fluxmap)
|
||||
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override
|
||||
{
|
||||
_data[std::make_pair(cylinder, head)] = fluxmap.rawBytes();
|
||||
auto& vector = _data[std::make_pair(cylinder, head)];
|
||||
vector.push_back(fluxmap.rawBytes());
|
||||
}
|
||||
|
||||
operator std::string () const
|
||||
@@ -63,7 +65,7 @@ public:
|
||||
private:
|
||||
std::string _filename;
|
||||
std::ofstream _of;
|
||||
std::map<std::pair<unsigned, unsigned>, Bytes> _data;
|
||||
std::map<std::pair<unsigned, unsigned>, std::vector<Bytes>> _data;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSink> FluxSink::createFl2FluxSink(const Fl2FluxSinkProto& config)
|
||||
|
||||
@@ -40,7 +40,7 @@ void FluxSink::updateConfigForFilename(FluxSinkProto* proto, const std::string&
|
||||
{ std::regex("^(.*\\.scp)$"), [](auto& s, auto* proto) { proto->mutable_scp()->set_filename(s); }},
|
||||
{ std::regex("^vcd:(.*)$"), [](auto& s, auto* proto) { proto->mutable_vcd()->set_directory(s); }},
|
||||
{ std::regex("^au:(.*)$"), [](auto& s, auto* proto) { proto->mutable_au()->set_directory(s); }},
|
||||
{ std::regex("^drive:(.*)"), [](auto& s, auto* proto) { proto->mutable_drive()->set_drive(std::stoi(s)); }},
|
||||
{ std::regex("^drive:(.*)"), [](auto& s, auto* proto) { proto->mutable_drive(); config.mutable_drive()->set_drive(std::stoi(s)); }},
|
||||
};
|
||||
|
||||
for (const auto& it : formats)
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
static void updateConfigForFilename(FluxSinkProto* proto, const std::string& filename);
|
||||
|
||||
public:
|
||||
virtual void writeFlux(int track, int side, Fluxmap& fluxmap) = 0;
|
||||
virtual void writeFlux(int track, int side, const Fluxmap& fluxmap) = 0;
|
||||
|
||||
virtual operator std::string () const = 0;
|
||||
};
|
||||
|
||||
@@ -2,12 +2,7 @@ syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
message HardwareFluxSinkProto {
|
||||
optional IndexMode index_mode = 1 [default = INDEXMODE_DRIVE, (help) = "index pulse source"];
|
||||
optional int32 hard_sector_count = 2 [(help) = "number of hard sectors on the disk"];
|
||||
optional bool high_density = 3 [default = true, (help) = "set if this is a high density disk"];
|
||||
optional int32 drive = 4 [default = 0, (help) = "which drive to write to (0 or 1)"];
|
||||
}
|
||||
message HardwareFluxSinkProto {}
|
||||
|
||||
message AuFluxSinkProto {
|
||||
optional string directory = 1 [default = "aufiles", (help) = "directory to write .au files to"];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "flags.h"
|
||||
#include "fluxmap.h"
|
||||
#include "logger.h"
|
||||
#include "proto.h"
|
||||
#include "usb/usb.h"
|
||||
#include "fluxsink/fluxsink.h"
|
||||
#include "lib/fluxsink/fluxsink.pb.h"
|
||||
@@ -10,19 +11,19 @@
|
||||
class HardwareFluxSink : public FluxSink
|
||||
{
|
||||
public:
|
||||
HardwareFluxSink(const HardwareFluxSinkProto& config):
|
||||
_config(config)
|
||||
HardwareFluxSink(const HardwareFluxSinkProto& conf):
|
||||
_config(conf)
|
||||
{
|
||||
if (config.has_hard_sector_count())
|
||||
if (config.drive().has_hard_sector_count())
|
||||
{
|
||||
nanoseconds_t oneRevolution;
|
||||
int retries = 5;
|
||||
usbSetDrive(_config.drive(), _config.high_density(), _config.index_mode());
|
||||
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
|
||||
Logger() << BeginSpeedOperationLogMessage();
|
||||
|
||||
do {
|
||||
oneRevolution = usbGetRotationalPeriod(_config.hard_sector_count());
|
||||
_hardSectorThreshold = oneRevolution * 3 / (4 * _config.hard_sector_count());
|
||||
oneRevolution = usbGetRotationalPeriod(config.drive().hard_sector_count());
|
||||
_hardSectorThreshold = oneRevolution * 3 / (4 * config.drive().hard_sector_count());
|
||||
retries--;
|
||||
} while ((oneRevolution == 0) && (retries > 0));
|
||||
|
||||
@@ -41,9 +42,9 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
void writeFlux(int track, int side, Fluxmap& fluxmap)
|
||||
void writeFlux(int track, int side, const Fluxmap& fluxmap) override
|
||||
{
|
||||
usbSetDrive(_config.drive(), _config.high_density(), _config.index_mode());
|
||||
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
|
||||
#if 0
|
||||
if (fluxSourceSinkFortyTrack)
|
||||
{
|
||||
@@ -60,7 +61,7 @@ public:
|
||||
|
||||
operator std::string () const
|
||||
{
|
||||
return fmt::format("drive {}", _config.drive());
|
||||
return fmt::format("drive {}", config.drive().drive());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "lib/fluxsink/fluxsink.pb.h"
|
||||
#include "proto.h"
|
||||
#include "fmt/format.h"
|
||||
#include "fluxmap.h"
|
||||
#include "scp.h"
|
||||
#include <fstream>
|
||||
#include <sys/stat.h>
|
||||
@@ -79,7 +80,7 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
void writeFlux(int cylinder, int head, Fluxmap& fluxmap)
|
||||
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override
|
||||
{
|
||||
ByteWriter trackdataWriter(_trackdata);
|
||||
trackdataWriter.seekToEnd();
|
||||
@@ -97,9 +98,7 @@ public:
|
||||
trackheader.header.track_id[2] = 'K';
|
||||
trackheader.header.strack = strack;
|
||||
|
||||
auto lastFluxmap = fluxmap.split().back();
|
||||
|
||||
FluxmapReader fmr(lastFluxmap);
|
||||
FluxmapReader fmr(fluxmap);
|
||||
Bytes fluxdata;
|
||||
ByteWriter fluxdataWriter(fluxdata);
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
{}
|
||||
|
||||
public:
|
||||
void writeFlux(int cylinder, int head, Fluxmap& fluxmap)
|
||||
void writeFlux(int cylinder, int head, const Fluxmap& fluxmap) override
|
||||
{
|
||||
mkdir(_config.directory().c_str(), 0744);
|
||||
std::ofstream of(
|
||||
|
||||
@@ -30,7 +30,7 @@ struct CwfTrack
|
||||
uint8_t length[4]; // little-endian
|
||||
};
|
||||
|
||||
class CwfFluxSource : public FluxSource
|
||||
class CwfFluxSource : public TrivialFluxSource
|
||||
{
|
||||
public:
|
||||
CwfFluxSource(const CwfFluxSourceProto& config):
|
||||
@@ -79,11 +79,11 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> readFlux(int track, int side)
|
||||
std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side)
|
||||
{
|
||||
const auto& p = _trackOffsets.find(std::make_pair(track, side));
|
||||
if (p == _trackOffsets.end())
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
return std::make_unique<const Fluxmap>();
|
||||
|
||||
off_t pos = p->second.first;;
|
||||
size_t length = p->second.second;
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
#include "lib/fluxsource/fluxsource.pb.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
class EraseFluxSource : public FluxSource
|
||||
class EraseFluxSource : public TrivialFluxSource
|
||||
{
|
||||
public:
|
||||
EraseFluxSource(const EraseFluxSourceProto& config) {}
|
||||
~EraseFluxSource() {}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> readFlux(int track, int side)
|
||||
std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side) override
|
||||
{
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
return std::unique_ptr<const Fluxmap>();
|
||||
}
|
||||
|
||||
void recalibrate() {}
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
|
||||
std::unique_ptr<FluxSource> FluxSource::createEraseFluxSource(const EraseFluxSourceProto& config)
|
||||
{
|
||||
return std::unique_ptr<FluxSource>(new EraseFluxSource(config));
|
||||
return std::make_unique<EraseFluxSource>(config);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,8 +5,46 @@
|
||||
#include "fluxsource/fluxsource.h"
|
||||
#include "proto.h"
|
||||
#include "fmt/format.h"
|
||||
#include "fluxmap.h"
|
||||
#include <fstream>
|
||||
|
||||
class Fl2FluxSourceIterator : public FluxSourceIterator
|
||||
{
|
||||
public:
|
||||
Fl2FluxSourceIterator(const TrackFluxProto& proto):
|
||||
_proto(proto)
|
||||
{}
|
||||
|
||||
bool hasNext() const override
|
||||
{
|
||||
return _count < _proto.flux_size();
|
||||
}
|
||||
|
||||
std::unique_ptr<const Fluxmap> next() override
|
||||
{
|
||||
auto bytes = _proto.flux(_count);
|
||||
_count++;
|
||||
return std::make_unique<Fluxmap>(bytes);
|
||||
}
|
||||
|
||||
private:
|
||||
const TrackFluxProto& _proto;
|
||||
int _count = 0;
|
||||
};
|
||||
|
||||
class EmptyFluxSourceIterator : public FluxSourceIterator
|
||||
{
|
||||
bool hasNext() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<const Fluxmap> next() override
|
||||
{
|
||||
Error() << "no flux to read";
|
||||
}
|
||||
};
|
||||
|
||||
class Fl2FluxSource : public FluxSource
|
||||
{
|
||||
public:
|
||||
@@ -20,18 +58,19 @@ public:
|
||||
|
||||
if (!_proto.ParseFromIstream(&ifs))
|
||||
Error() << "unable to read input file";
|
||||
upgradeFluxFile();
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> readFlux(int cylinder, int head)
|
||||
std::unique_ptr<FluxSourceIterator> readFlux(int cylinder, int head) override
|
||||
{
|
||||
for (const auto& track : _proto.track())
|
||||
{
|
||||
if ((track.cylinder() == cylinder) && (track.head() == head))
|
||||
return std::make_unique<Fluxmap>(track.flux());
|
||||
return std::make_unique<Fl2FluxSourceIterator>(track);
|
||||
}
|
||||
|
||||
return std::make_unique<Fluxmap>();
|
||||
return std::make_unique<EmptyFluxSourceIterator>();
|
||||
}
|
||||
|
||||
void recalibrate() {}
|
||||
@@ -43,6 +82,31 @@ private:
|
||||
Error() << fmt::format("FL2 read I/O error: {}", strerror(errno));
|
||||
}
|
||||
|
||||
void upgradeFluxFile()
|
||||
{
|
||||
if (_proto.version() == FluxFileVersion::VERSION_1)
|
||||
{
|
||||
/* Change a flux datastream with multiple segments separated by F_DESYNC into multiple
|
||||
* flux segments. */
|
||||
|
||||
for (auto& track : *_proto.mutable_track())
|
||||
{
|
||||
if (track.flux_size() != 0)
|
||||
{
|
||||
Fluxmap oldFlux(track.flux(0));
|
||||
|
||||
track.clear_flux();
|
||||
for (const auto& flux : oldFlux.split())
|
||||
track.add_flux(flux->rawBytes());
|
||||
}
|
||||
}
|
||||
|
||||
_proto.set_version(FluxFileVersion::VERSION_2);
|
||||
}
|
||||
if (_proto.version() > FluxFileVersion::VERSION_2)
|
||||
Error() << fmt::format("this is a version {} flux file, but this build of the client can only handle up to version {} --- please upgrade", _proto.version(), FluxFileVersion::VERSION_2);
|
||||
}
|
||||
|
||||
private:
|
||||
const Fl2FluxSourceProto& _config;
|
||||
FluxFileProto _proto;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "fluxsource/fluxsource.h"
|
||||
#include "fluxmap.h"
|
||||
#include "lib/config.pb.h"
|
||||
#include "proto.h"
|
||||
#include "utils.h"
|
||||
@@ -56,7 +57,7 @@ void FluxSource::updateConfigForFilename(FluxSourceProto* proto, const std::stri
|
||||
{ std::regex("^erase:$"), [](auto& s, auto* proto) { proto->mutable_erase(); }},
|
||||
{ std::regex("^kryoflux:(.*)$"), [](auto& s, auto* proto) { proto->mutable_kryoflux()->set_directory(s); }},
|
||||
{ std::regex("^testpattern:(.*)"), [](auto& s, auto* proto) { proto->mutable_test_pattern(); }},
|
||||
{ std::regex("^drive:(.*)"), [](auto& s, auto* proto) { proto->mutable_drive()->set_drive(std::stoi(s)); }},
|
||||
{ std::regex("^drive:(.*)"), [](auto& s, auto* proto) { proto->mutable_drive(); config.mutable_drive()->set_drive(std::stoi(s)); }},
|
||||
};
|
||||
|
||||
for (const auto& it : formats)
|
||||
@@ -72,5 +73,36 @@ void FluxSource::updateConfigForFilename(FluxSourceProto* proto, const std::stri
|
||||
Error() << fmt::format("unrecognised flux filename '{}'", filename);
|
||||
}
|
||||
|
||||
class TrivialFluxSourceIterator : public FluxSourceIterator
|
||||
{
|
||||
public:
|
||||
TrivialFluxSourceIterator(TrivialFluxSource* fluxSource, int cylinder, int head):
|
||||
_fluxSource(fluxSource),
|
||||
_cylinder(cylinder),
|
||||
_head(head)
|
||||
{}
|
||||
|
||||
bool hasNext() const override
|
||||
{
|
||||
return !!_fluxSource;
|
||||
}
|
||||
|
||||
std::unique_ptr<const Fluxmap> next() override
|
||||
{
|
||||
auto fluxmap = _fluxSource->readSingleFlux(_cylinder, _head);
|
||||
_fluxSource = nullptr;
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
TrivialFluxSource* _fluxSource;
|
||||
int _cylinder;
|
||||
int _head;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSourceIterator> TrivialFluxSource::readFlux(int cylinder, int head)
|
||||
{
|
||||
return std::make_unique<TrivialFluxSourceIterator>(this, cylinder, head);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,15 @@ class KryofluxFluxSourceProto;
|
||||
class ScpFluxSourceProto;
|
||||
class TestPatternFluxSourceProto;
|
||||
|
||||
class FluxSourceIterator
|
||||
{
|
||||
public:
|
||||
virtual ~FluxSourceIterator() {}
|
||||
|
||||
virtual bool hasNext() const = 0;
|
||||
virtual std::unique_ptr<const Fluxmap> next() = 0;
|
||||
};
|
||||
|
||||
class FluxSource
|
||||
{
|
||||
public:
|
||||
@@ -33,9 +42,16 @@ public:
|
||||
static void updateConfigForFilename(FluxSourceProto* proto, const std::string& filename);
|
||||
|
||||
public:
|
||||
virtual std::unique_ptr<Fluxmap> readFlux(int track, int side) = 0;
|
||||
virtual std::unique_ptr<FluxSourceIterator> readFlux(int track, int side) = 0;
|
||||
virtual void recalibrate() {}
|
||||
virtual bool retryable() { return false; }
|
||||
virtual bool isHardware() { return false; }
|
||||
};
|
||||
|
||||
class TrivialFluxSource : public FluxSource
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<FluxSourceIterator> readFlux(int track, int side);
|
||||
virtual std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,15 +2,7 @@ syntax = "proto2";
|
||||
|
||||
import "lib/common.proto";
|
||||
|
||||
message HardwareFluxSourceProto {
|
||||
optional int32 drive = 1 [default = 0, (help) = "which drive to read from"];
|
||||
optional double revolutions = 2 [default = 1.2, (help) = "number of revolutions to read"];
|
||||
|
||||
optional bool sync_with_index = 3 [default = false, (help) = "start reading at index mark"];
|
||||
optional IndexMode index_mode = 4 [default = INDEXMODE_DRIVE, (help) = "index pulse source"];
|
||||
optional int32 hard_sector_count = 5 [default = 0, (help) = "number of hard sectors on disk"];
|
||||
optional bool high_density = 6 [default = true, (help) = "set if this is a high density disk"];
|
||||
}
|
||||
message HardwareFluxSourceProto {}
|
||||
|
||||
message TestPatternFluxSourceProto {
|
||||
optional double interval_us = 1 [default = 4.0, (help) = "interval between pulses"];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "flags.h"
|
||||
#include "fluxmap.h"
|
||||
#include "logger.h"
|
||||
#include "proto.h"
|
||||
#include "usb/usb.h"
|
||||
#include "fluxsource/fluxsource.h"
|
||||
#include "lib/fluxsource/fluxsource.pb.h"
|
||||
@@ -9,54 +10,84 @@
|
||||
|
||||
class HardwareFluxSource : public FluxSource
|
||||
{
|
||||
private:
|
||||
class HardwareFluxSourceIterator : public FluxSourceIterator
|
||||
{
|
||||
public:
|
||||
HardwareFluxSourceIterator(
|
||||
const HardwareFluxSource& fluxsource, int cylinder, int head):
|
||||
_fluxsource(fluxsource),
|
||||
_cylinder(cylinder),
|
||||
_head(head)
|
||||
{
|
||||
}
|
||||
|
||||
bool hasNext() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<const Fluxmap> next()
|
||||
{
|
||||
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
|
||||
usbSeek(_cylinder);
|
||||
|
||||
Bytes data = usbRead(_head,
|
||||
config.drive().sync_with_index(),
|
||||
config.drive().revolutions() * _fluxsource._oneRevolution,
|
||||
_fluxsource._hardSectorThreshold);
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
fluxmap->appendBytes(data);
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
private:
|
||||
const HardwareFluxSource& _fluxsource;
|
||||
int _cylinder;
|
||||
int _head;
|
||||
};
|
||||
|
||||
public:
|
||||
HardwareFluxSource(const HardwareFluxSourceProto& config):
|
||||
_config(config)
|
||||
HardwareFluxSource(const HardwareFluxSourceProto& conf): _config(conf)
|
||||
{
|
||||
int retries = 5;
|
||||
usbSetDrive(_config.drive(), _config.high_density(), _config.index_mode());
|
||||
Logger() << BeginSpeedOperationLogMessage();
|
||||
|
||||
do {
|
||||
_oneRevolution = usbGetRotationalPeriod(_config.hard_sector_count());
|
||||
if (_config.hard_sector_count() != 0)
|
||||
_hardSectorThreshold = _oneRevolution * 3 / (4 * _config.hard_sector_count());
|
||||
usbSetDrive(config.drive().drive(), config.drive().high_density(), config.drive().index_mode());
|
||||
Logger() << BeginSpeedOperationLogMessage();
|
||||
|
||||
do
|
||||
{
|
||||
_oneRevolution =
|
||||
usbGetRotationalPeriod(config.drive().hard_sector_count());
|
||||
if (config.drive().hard_sector_count() != 0)
|
||||
_hardSectorThreshold =
|
||||
_oneRevolution * 3 / (4 * config.drive().hard_sector_count());
|
||||
else
|
||||
_hardSectorThreshold = 0;
|
||||
|
||||
retries--;
|
||||
} while ((_oneRevolution == 0) && (retries > 0));
|
||||
|
||||
if (_oneRevolution == 0) {
|
||||
Error() << "Failed\nIs a disk in the drive?";
|
||||
}
|
||||
if (_oneRevolution == 0)
|
||||
Error() << "Failed\nIs a disk in the drive?";
|
||||
|
||||
Logger() << EndSpeedOperationLogMessage { _oneRevolution };
|
||||
Logger() << EndSpeedOperationLogMessage{_oneRevolution};
|
||||
}
|
||||
|
||||
~HardwareFluxSource()
|
||||
{
|
||||
}
|
||||
~HardwareFluxSource() {}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> readFlux(int track, int side)
|
||||
std::unique_ptr<FluxSourceIterator> readFlux(int cylinder, int head) override
|
||||
{
|
||||
usbSetDrive(_config.drive(), _config.high_density(), _config.index_mode());
|
||||
usbSeek(track);
|
||||
|
||||
Bytes data = usbRead(
|
||||
side, _config.sync_with_index(), _config.revolutions() * _oneRevolution, _hardSectorThreshold);
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
fluxmap->appendBytes(data);
|
||||
return fluxmap;
|
||||
return std::make_unique<HardwareFluxSourceIterator>(
|
||||
*this, cylinder, head);
|
||||
}
|
||||
|
||||
void recalibrate()
|
||||
void recalibrate() override
|
||||
{
|
||||
usbRecalibrate();
|
||||
}
|
||||
|
||||
bool retryable()
|
||||
bool isHardware() override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -67,10 +98,8 @@ private:
|
||||
nanoseconds_t _hardSectorThreshold;
|
||||
};
|
||||
|
||||
std::unique_ptr<FluxSource> FluxSource::createHardwareFluxSource(const HardwareFluxSourceProto& config)
|
||||
std::unique_ptr<FluxSource> FluxSource::createHardwareFluxSource(
|
||||
const HardwareFluxSourceProto& config)
|
||||
{
|
||||
return std::unique_ptr<FluxSource>(new HardwareFluxSource(config));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "lib/fluxsource/fluxsource.pb.h"
|
||||
#include "fluxsource/fluxsource.h"
|
||||
|
||||
class KryofluxFluxSource : public FluxSource
|
||||
class KryofluxFluxSource : public TrivialFluxSource
|
||||
{
|
||||
public:
|
||||
KryofluxFluxSource(const KryofluxFluxSourceProto& config):
|
||||
@@ -12,7 +12,7 @@ public:
|
||||
{}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> readFlux(int track, int side)
|
||||
std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side) override
|
||||
{
|
||||
return readStream(_path, track, side);
|
||||
}
|
||||
@@ -25,5 +25,5 @@ private:
|
||||
|
||||
std::unique_ptr<FluxSource> FluxSource::createKryofluxFluxSource(const KryofluxFluxSourceProto& config)
|
||||
{
|
||||
return std::unique_ptr<FluxSource>(new KryofluxFluxSource(config));
|
||||
return std::make_unique<KryofluxFluxSource>(config);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ static int strackno(int track, int side)
|
||||
return (track << 1) | side;
|
||||
}
|
||||
|
||||
class ScpFluxSource : public FluxSource
|
||||
class ScpFluxSource : public TrivialFluxSource
|
||||
{
|
||||
public:
|
||||
ScpFluxSource(const ScpFluxSourceProto& config):
|
||||
@@ -55,14 +55,14 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> readFlux(int track, int side)
|
||||
std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side) override
|
||||
{
|
||||
int strack = strackno(track, side);
|
||||
if (strack >= ARRAY_SIZE(_header.track))
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
return std::make_unique<Fluxmap>();
|
||||
uint32_t offset = Bytes(_header.track[strack], 4).reader().read_le32();
|
||||
if (offset == 0)
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
return std::make_unique<Fluxmap>();
|
||||
|
||||
ScpTrackHeader trackheader;
|
||||
_if.seekg(offset, std::ios::beg);
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
revs[revolution] = trackrev;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
nanoseconds_t pending = 0;
|
||||
unsigned inputBytes = 0;
|
||||
for (int revolution = 0; revolution < _header.revolutions; revolution++)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "lib/fluxsource/fluxsource.pb.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
class TestPatternFluxSource : public FluxSource
|
||||
class TestPatternFluxSource : public TrivialFluxSource
|
||||
{
|
||||
public:
|
||||
TestPatternFluxSource(const TestPatternFluxSourceProto& config):
|
||||
@@ -14,9 +14,9 @@ public:
|
||||
~TestPatternFluxSource() {}
|
||||
|
||||
public:
|
||||
std::unique_ptr<Fluxmap> readFlux(int track, int side)
|
||||
std::unique_ptr<const Fluxmap> readSingleFlux(int track, int side) override
|
||||
{
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
|
||||
while (fluxmap->duration() < (_config.sequence_length_us()*1000000.0))
|
||||
{
|
||||
@@ -35,7 +35,7 @@ private:
|
||||
|
||||
std::unique_ptr<FluxSource> FluxSource::createTestPatternFluxSource(const TestPatternFluxSourceProto& config)
|
||||
{
|
||||
return std::unique_ptr<FluxSource>(new TestPatternFluxSource(config));
|
||||
return std::make_unique<TestPatternFluxSource>(config);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +34,8 @@ extern void hexdumpForSrp16(std::ostream& stream, const Bytes& bytes);
|
||||
struct ErrorException
|
||||
{
|
||||
const std::string message;
|
||||
|
||||
void print() const;
|
||||
};
|
||||
|
||||
class Error
|
||||
|
||||
@@ -67,24 +67,16 @@ public:
|
||||
if (mediaFlag == 0x20)
|
||||
{
|
||||
Logger() << "D88: high density mode";
|
||||
if (config.flux_sink().dest_case() ==
|
||||
FluxSinkProto::DestCase::kDrive)
|
||||
{
|
||||
config.mutable_flux_sink()->mutable_drive()->set_high_density(
|
||||
true);
|
||||
}
|
||||
if (!config.drive().has_drive())
|
||||
config.mutable_drive()->set_high_density(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger() << "D88: single/double density mode";
|
||||
physicalStep = 2;
|
||||
clockRate = 300;
|
||||
if (config.flux_sink().dest_case() ==
|
||||
FluxSinkProto::DestCase::kDrive)
|
||||
{
|
||||
config.mutable_flux_sink()->mutable_drive()->set_high_density(
|
||||
false);
|
||||
}
|
||||
if (!config.drive().has_drive())
|
||||
config.mutable_drive()->set_high_density(false);
|
||||
}
|
||||
|
||||
std::unique_ptr<Image> image(new Image);
|
||||
|
||||
@@ -57,10 +57,8 @@ public:
|
||||
auto ibm = config.mutable_encoder()->mutable_ibm();
|
||||
config.mutable_cylinders()->set_end(0);
|
||||
Logger() << "NFD: HD 1.2MB mode";
|
||||
if (config.flux_sink().dest_case() == FluxSinkProto::DestCase::kDrive)
|
||||
{
|
||||
config.mutable_flux_sink()->mutable_drive()->set_high_density(true);
|
||||
}
|
||||
if (!config.drive().has_drive())
|
||||
config.mutable_drive()->set_high_density(true);
|
||||
|
||||
std::unique_ptr<Image> image(new Image);
|
||||
for (int track = 0; track < 163; track++)
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
ConfigProto config = []() {
|
||||
ConfigProto config;
|
||||
config.mutable_flux_source()->mutable_drive()->set_drive(0);
|
||||
config.mutable_flux_sink()->mutable_drive()->set_drive(0);
|
||||
config.mutable_drive()->set_drive(0);
|
||||
config.mutable_drive()->set_drive(0);
|
||||
return config;
|
||||
}();
|
||||
|
||||
|
||||
@@ -23,11 +23,10 @@
|
||||
|
||||
static std::unique_ptr<FluxSink> outputFluxSink;
|
||||
|
||||
static std::shared_ptr<Fluxmap> readFluxmap(FluxSource& fluxsource, unsigned cylinder, unsigned head)
|
||||
static std::shared_ptr<const Fluxmap> readFluxmap(FluxSourceIterator& fluxsourceIterator, unsigned cylinder, unsigned head)
|
||||
{
|
||||
Logger() << BeginReadOperationLogMessage { cylinder, head };
|
||||
std::shared_ptr<Fluxmap> fluxmap = fluxsource.readFlux(cylinder, head);
|
||||
fluxmap->rescale(1.0/config.flux_source().rescale());
|
||||
auto fluxmap = fluxsourceIterator.next()->rescale(1.0/config.flux_source().rescale());
|
||||
Logger() << EndReadOperationLogMessage()
|
||||
<< fmt::format("{0:.0} ms in {1} bytes", fluxmap->duration()/1e6, fluxmap->bytes());
|
||||
return fluxmap;
|
||||
@@ -108,9 +107,11 @@ std::shared_ptr<const DiskFlux> readDiskCommand(FluxSource& fluxsource, Abstract
|
||||
std::set<std::shared_ptr<const Record>> track_records;
|
||||
Fluxmap totalFlux;
|
||||
|
||||
for (int retry = config.decoder().retries(); retry >= 0; retry--)
|
||||
auto fluxsourceIterator = fluxsource.readFlux(cylinder, head);
|
||||
int retry = 0;
|
||||
while (fluxsourceIterator->hasNext())
|
||||
{
|
||||
auto fluxmap = readFluxmap(fluxsource, cylinder, head);
|
||||
auto fluxmap = readFluxmap(*fluxsourceIterator, cylinder, head);
|
||||
totalFlux.appendDesync().appendBytes(fluxmap->rawBytes());
|
||||
|
||||
auto trackdataflux =
|
||||
@@ -155,13 +156,17 @@ std::shared_ptr<const DiskFlux> readDiskCommand(FluxSource& fluxsource, Abstract
|
||||
if (!hasBadSectors)
|
||||
break;
|
||||
|
||||
if (!fluxsource.retryable())
|
||||
if (!fluxsourceIterator->hasNext())
|
||||
break;
|
||||
if (retry == 0)
|
||||
Logger() << fmt::format("giving up");
|
||||
else
|
||||
Logger()
|
||||
<< fmt::format("retrying; {} retries remaining", retry);
|
||||
if (fluxsource.isHardware())
|
||||
{
|
||||
retry++;
|
||||
if (retry == 0)
|
||||
Logger() << fmt::format("giving up");
|
||||
else
|
||||
Logger()
|
||||
<< fmt::format("retrying; {} retries remaining", retry);
|
||||
}
|
||||
}
|
||||
|
||||
if (outputFluxSink)
|
||||
@@ -236,7 +241,8 @@ void rawReadDiskCommand(FluxSource& fluxsource, FluxSink& fluxsink)
|
||||
for (int head : iterate(config.heads()))
|
||||
{
|
||||
testForEmergencyStop();
|
||||
auto fluxmap = readFluxmap(fluxsource, cylinder, head);
|
||||
auto fluxsourceIterator = fluxsource.readFlux(cylinder, head);
|
||||
auto fluxmap = readFluxmap(*fluxsourceIterator, cylinder, head);
|
||||
fluxsink.writeFlux(cylinder, head, *fluxmap);
|
||||
}
|
||||
}
|
||||
|
||||
51
lib/utils.cc
51
lib/utils.cc
@@ -4,6 +4,12 @@
|
||||
bool emergencyStop = false;
|
||||
|
||||
static const char* WHITESPACE = " \t\n\r\f\v";
|
||||
static const char* SEPARATORS = "/\\";
|
||||
|
||||
void ErrorException::print() const
|
||||
{
|
||||
std::cerr << message << '\n';
|
||||
}
|
||||
|
||||
bool beginsWith(const std::string& value, const std::string& ending)
|
||||
{
|
||||
@@ -17,31 +23,52 @@ bool endsWith(const std::string& value, const std::string& ending)
|
||||
{
|
||||
if (ending.size() > value.size())
|
||||
return false;
|
||||
|
||||
std::string lowercase(ending.size(), 0);
|
||||
std::transform(value.rbegin(), value.rbegin() + ending.size(), lowercase.begin(), [](unsigned char c){ return std::tolower(c); });
|
||||
std::transform(value.rbegin(),
|
||||
value.rbegin() + ending.size(),
|
||||
lowercase.begin(),
|
||||
[](unsigned char c)
|
||||
{
|
||||
return std::tolower(c);
|
||||
});
|
||||
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin()) ||
|
||||
std::equal(ending.rbegin(), ending.rend(), lowercase.begin());
|
||||
std::equal(ending.rbegin(), ending.rend(), lowercase.begin());
|
||||
}
|
||||
|
||||
void leftTrimWhitespace(std::string& value)
|
||||
std::string leftTrimWhitespace(std::string value)
|
||||
{
|
||||
value.erase(0, value.find_first_not_of(WHITESPACE));
|
||||
value.erase(0, value.find_first_not_of(WHITESPACE));
|
||||
return value;
|
||||
}
|
||||
|
||||
void rightTrimWhitespace(std::string& value)
|
||||
std::string rightTrimWhitespace(std::string value)
|
||||
{
|
||||
value.erase(value.find_last_not_of(WHITESPACE) + 1);
|
||||
value.erase(value.find_last_not_of(WHITESPACE) + 1);
|
||||
return value;
|
||||
}
|
||||
|
||||
void trimWhitespace(std::string& value)
|
||||
std::string trimWhitespace(const std::string& value)
|
||||
{
|
||||
leftTrimWhitespace(value);
|
||||
rightTrimWhitespace(value);
|
||||
return leftTrimWhitespace(rightTrimWhitespace(value));
|
||||
}
|
||||
|
||||
std::string getLeafname(const std::string& value)
|
||||
{
|
||||
constexpr char sep = '/';
|
||||
|
||||
size_t i = value.find_last_of(SEPARATORS);
|
||||
if (i != std::string::npos)
|
||||
{
|
||||
return value.substr(i + 1, value.length() - i);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void testForEmergencyStop()
|
||||
{
|
||||
if (emergencyStop)
|
||||
throw EmergencyStopException();
|
||||
if (emergencyStop)
|
||||
throw EmergencyStopException();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,10 @@
|
||||
|
||||
extern bool beginsWith(const std::string& value, const std::string& beginning);
|
||||
extern bool endsWith(const std::string& value, const std::string& ending);
|
||||
extern void leftTrimWhitespace(std::string& value);
|
||||
extern void rightTrimWhitespace(std::string& value);
|
||||
extern void trimWhitespace(std::string& value);
|
||||
extern std::string leftTrimWhitespace(std::string value);
|
||||
extern std::string rightTrimWhitespace(std::string value);
|
||||
extern std::string trimWhitespace(const std::string& value);
|
||||
extern std::string getLeafname(const std::string& value);
|
||||
|
||||
/* If set, any running job will terminate as soon as possible (with an error).
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "proto.h"
|
||||
|
||||
void writeTracks(FluxSink& fluxSink,
|
||||
const std::function<std::unique_ptr<Fluxmap>(int track, int side)> producer)
|
||||
const std::function<std::unique_ptr<const Fluxmap>(int track, int side)> producer)
|
||||
{
|
||||
for (unsigned cylinder : iterate(config.cylinders()))
|
||||
{
|
||||
@@ -28,7 +28,7 @@ void writeTracks(FluxSink& fluxSink,
|
||||
testForEmergencyStop();
|
||||
Logger() << BeginWriteOperationLogMessage{cylinder, head};
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap = producer(cylinder, head);
|
||||
auto fluxmap = producer(cylinder, head);
|
||||
if (!fluxmap)
|
||||
{
|
||||
/* Erase this track rather than writing. */
|
||||
@@ -39,11 +39,11 @@ void writeTracks(FluxSink& fluxSink,
|
||||
}
|
||||
else
|
||||
{
|
||||
fluxmap->rescale(config.flux_sink().rescale());
|
||||
auto scaled = fluxmap->rescale(config.flux_sink().rescale());
|
||||
/* Precompensation actually seems to make things worse, so let's
|
||||
* leave it disabled for now. */
|
||||
// fluxmap->precompensate(PRECOMPENSATION_THRESHOLD_TICKS, 2);
|
||||
fluxSink.writeFlux(cylinder, head, *fluxmap);
|
||||
fluxSink.writeFlux(cylinder, head, *scaled);
|
||||
Logger() << fmt::format("{0} ms in {1} bytes",
|
||||
int(fluxmap->duration() / 1e6),
|
||||
fluxmap->bytes());
|
||||
@@ -100,8 +100,7 @@ void writeTracksAndVerify(FluxSink& fluxSink,
|
||||
fluxmap->bytes());
|
||||
|
||||
Logger() << BeginReadOperationLogMessage{cylinder, head};
|
||||
std::shared_ptr<Fluxmap> writtenFluxmap =
|
||||
fluxSource.readFlux(cylinder, head);
|
||||
std::shared_ptr<const Fluxmap> writtenFluxmap = fluxSource.readFlux(cylinder, head)->next();
|
||||
Logger() << EndReadOperationLogMessage()
|
||||
<< fmt::format("verifying {0} ms in {1} bytes",
|
||||
int(writtenFluxmap->duration() / 1e6),
|
||||
@@ -188,8 +187,8 @@ void writeDiskCommand(
|
||||
void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink)
|
||||
{
|
||||
writeTracks(fluxSink,
|
||||
[&](int track, int side) -> std::unique_ptr<Fluxmap>
|
||||
[&](int track, int side) -> std::unique_ptr<const Fluxmap>
|
||||
{
|
||||
return fluxSource.readFlux(track, side);
|
||||
return fluxSource.readFlux(track, side)->next();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ class FluxSink;
|
||||
class Image;
|
||||
|
||||
extern void writeTracks(FluxSink& fluxSink,
|
||||
const std::function<std::unique_ptr<Fluxmap>(int track, int side)>
|
||||
const std::function<std::unique_ptr<const Fluxmap>(int track, int side)>
|
||||
producer);
|
||||
|
||||
extern void fillBitmapTo(std::vector<bool>& bitmap,
|
||||
|
||||
@@ -382,6 +382,7 @@ buildproto libconfig.a \
|
||||
lib/common.proto \
|
||||
lib/config.proto \
|
||||
lib/decoders/decoders.proto \
|
||||
lib/drive.proto \
|
||||
lib/encoders/encoders.proto \
|
||||
lib/fluxsink/fluxsink.proto \
|
||||
lib/fluxsource/fluxsource.proto \
|
||||
@@ -644,6 +645,7 @@ runtest fmmfm-test tests/fmmfm.cc
|
||||
runtest greaseweazle-test tests/greaseweazle.cc
|
||||
runtest kryoflux-test tests/kryoflux.cc
|
||||
runtest ldbs-test tests/ldbs.cc
|
||||
runtest utils-test tests/utils.cc
|
||||
runtest proto-test -I$OBJDIR/proto \
|
||||
-d $OBJDIR/proto/libconfig.def \
|
||||
-d $OBJDIR/proto/libtestproto.def \
|
||||
|
||||
@@ -89,7 +89,7 @@ enum
|
||||
{
|
||||
F_BIT_PULSE = 0x80,
|
||||
F_BIT_INDEX = 0x40,
|
||||
F_DESYNC = 0x00,
|
||||
F_DESYNC = 0x00, /* obsolete */
|
||||
F_EOF = 0x100 /* synthetic, only produced by library */
|
||||
};
|
||||
|
||||
|
||||
@@ -204,14 +204,15 @@ static void draw_x_graticules(Agg2D& painter, double x1, double y1, double x2, d
|
||||
|
||||
int mainAnalyseDriveResponse(int argc, const char* argv[])
|
||||
{
|
||||
config.mutable_flux_source()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, {});
|
||||
|
||||
if (!config.flux_sink().has_drive())
|
||||
Error() << "this only makes sense with a real disk drive";
|
||||
|
||||
usbSetDrive(config.flux_sink().drive().drive(),
|
||||
config.flux_sink().drive().high_density(),
|
||||
config.flux_sink().drive().index_mode());
|
||||
usbSetDrive(config.drive().drive(),
|
||||
config.drive().high_density(),
|
||||
config.drive().index_mode());
|
||||
usbSeek(destCylinder);
|
||||
|
||||
std::cout << "Measuring rotational speed...\n";
|
||||
|
||||
@@ -201,10 +201,11 @@ static nanoseconds_t guessClock(const Fluxmap& fluxmap)
|
||||
|
||||
int mainInspect(int argc, const char* argv[])
|
||||
{
|
||||
config.mutable_flux_source()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, {});
|
||||
|
||||
std::unique_ptr<FluxSource> fluxSource(FluxSource::create(config.flux_source()));
|
||||
const auto fluxmap = fluxSource->readFlux(cylinderFlag, headFlag);
|
||||
const auto fluxmap = fluxSource->readFlux(cylinderFlag, headFlag)->next();
|
||||
|
||||
std::cout << fmt::format("0x{:x} bytes of data in {:.3f}ms\n",
|
||||
fluxmap->bytes(),
|
||||
|
||||
@@ -61,6 +61,7 @@ int mainRawRead(int argc, const char* argv[])
|
||||
|
||||
if (argc == 1)
|
||||
showProfiles("rawread", formats);
|
||||
config.mutable_flux_source()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, formats);
|
||||
|
||||
if (config.flux_sink().has_drive())
|
||||
|
||||
@@ -65,6 +65,7 @@ int mainRawWrite(int argc, const char* argv[])
|
||||
|
||||
if (argc == 1)
|
||||
showProfiles("rawwrite", formats);
|
||||
config.mutable_flux_sink()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, formats);
|
||||
|
||||
if (config.flux_source().has_drive())
|
||||
|
||||
@@ -67,6 +67,7 @@ int mainRead(int argc, const char* argv[])
|
||||
{
|
||||
if (argc == 1)
|
||||
showProfiles("read", formats);
|
||||
config.mutable_flux_source()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, formats);
|
||||
|
||||
if (config.decoder().copy_flux_to().has_drive())
|
||||
|
||||
@@ -23,8 +23,8 @@ int mainRpm(int argc, const char* argv[])
|
||||
if (!config.flux_source().has_drive())
|
||||
Error() << "this only makes sense with a real disk drive";
|
||||
|
||||
usbSetDrive(config.flux_source().drive().drive(), false, config.flux_source().drive().index_mode());
|
||||
nanoseconds_t period = usbGetRotationalPeriod(config.flux_source().drive().hard_sector_count());
|
||||
usbSetDrive(config.drive().drive(), false, config.drive().index_mode());
|
||||
nanoseconds_t period = usbGetRotationalPeriod(config.drive().hard_sector_count());
|
||||
if (period != 0)
|
||||
std::cout << "Rotational period is " << period/1000000 << " ms (" << 60e9/period << " rpm)" << std::endl;
|
||||
else
|
||||
|
||||
@@ -30,7 +30,7 @@ int mainSeek(int argc, const char* argv[])
|
||||
if (!config.flux_source().has_drive())
|
||||
Error() << "this only makes sense with a real disk drive";
|
||||
|
||||
usbSetDrive(config.flux_source().drive().drive(), false, config.flux_source().drive().index_mode());
|
||||
usbSetDrive(config.drive().drive(), false, config.drive().index_mode());
|
||||
usbSeek(cylinder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,8 @@ int mainWrite(int argc, const char* argv[])
|
||||
{
|
||||
if (argc == 1)
|
||||
showProfiles("write", formats);
|
||||
config.mutable_flux_sink()->mutable_drive();
|
||||
config.mutable_flux_source()->mutable_drive();
|
||||
flags.parseFlagsWithConfigFiles(argc, argv, formats);
|
||||
|
||||
std::unique_ptr<ImageReader> reader(ImageReader::create(config.image_reader()));
|
||||
|
||||
@@ -148,7 +148,7 @@ int main(int argc, const char* argv[])
|
||||
}
|
||||
catch (const ErrorException& e)
|
||||
{
|
||||
std::cerr << e.message << '\n';
|
||||
e.print();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'PC 1200kB 5.25" 80-track 15-sector DSHD'
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Japanese PC 1232kB 5.25"/3.5" 77-track 8-sector DSHD'
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Micropolis MetaFloppy Mod I 143kB 5.25" SSDD hard-sectored'
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Micropolis MetaFloppy Mod I 287kB 5.25" DSDD hard-sectored'
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Micropolis MetaFloppy Mod II 315kB 5.25" SSDD hard-sectored'
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Micropolis MetaFloppy Mod II 630kB 5.25" DSDD hard-sectored'
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 16
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'N88-BASIC 5.25"/3.5" 77-track 26-sector DSHD'
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -10,17 +10,9 @@ image_writer {
|
||||
nsi {}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: 1
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: true
|
||||
}
|
||||
|
||||
encoder {
|
||||
|
||||
@@ -10,17 +10,9 @@ image_writer {
|
||||
nsi {}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: 1
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: true
|
||||
}
|
||||
|
||||
encoder {
|
||||
|
||||
@@ -10,17 +10,9 @@ image_writer {
|
||||
nsi {}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: 1
|
||||
}
|
||||
}
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
}
|
||||
drive {
|
||||
hard_sector_count: 10
|
||||
sync_with_index: true
|
||||
}
|
||||
|
||||
encoder {
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
comment: 'Digital RX50 400kB 5.25" 80-track 10-sector SSQD'
|
||||
|
||||
flux_sink {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
}
|
||||
|
||||
flux_source {
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
drive {
|
||||
high_density: true
|
||||
}
|
||||
|
||||
image_reader {
|
||||
|
||||
@@ -287,12 +287,12 @@ image_writer {
|
||||
encoder {
|
||||
victor9k {
|
||||
trackdata {
|
||||
original_data_rate_khz: 500
|
||||
post_index_gap_us: 1000.0
|
||||
pre_header_sync_bits: 120
|
||||
post_header_gap_bits: 48
|
||||
original_data_rate_khz: 468
|
||||
post_index_gap_us: 500.0
|
||||
pre_header_sync_bits: 150
|
||||
post_header_gap_bits: 60
|
||||
pre_data_sync_bits: 40
|
||||
post_data_gap_bits: 200
|
||||
post_data_gap_bits: 300
|
||||
}
|
||||
trackdata {
|
||||
head: 0
|
||||
|
||||
@@ -143,11 +143,11 @@ image_writer {
|
||||
encoder {
|
||||
victor9k {
|
||||
trackdata {
|
||||
original_data_rate_khz: 500
|
||||
post_index_gap_us: 1000.0
|
||||
pre_header_sync_bits: 60
|
||||
post_header_gap_bits: 90
|
||||
pre_data_sync_bits: 50
|
||||
original_data_rate_khz: 468
|
||||
post_index_gap_us: 500.0
|
||||
pre_header_sync_bits: 150
|
||||
post_header_gap_bits: 60
|
||||
pre_data_sync_bits: 40
|
||||
post_data_gap_bits: 300
|
||||
}
|
||||
trackdata {
|
||||
|
||||
@@ -242,10 +242,7 @@ void MainWindow::PrepareConfig()
|
||||
void MainWindow::SetHighDensity()
|
||||
{
|
||||
bool hd = highDensityToggle->GetValue();
|
||||
if (config.flux_source().has_drive())
|
||||
config.mutable_flux_source()->mutable_drive()->set_high_density(hd);
|
||||
if (config.flux_sink().has_drive())
|
||||
config.mutable_flux_sink()->mutable_drive()->set_high_density(hd);
|
||||
config.mutable_drive()->set_high_density(hd);
|
||||
}
|
||||
|
||||
void MainWindow::ShowConfig()
|
||||
@@ -261,7 +258,7 @@ void MainWindow::ApplyCustomSettings()
|
||||
for (int i = 0; i < additionalSettingsEntry->GetNumberOfLines(); i++)
|
||||
{
|
||||
auto setting = additionalSettingsEntry->GetLineText(i).ToStdString();
|
||||
trimWhitespace(setting);
|
||||
setting = trimWhitespace(setting);
|
||||
if (setting.size() == 0)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "globals.h"
|
||||
#include "bytes.h"
|
||||
#include "snowhouse/snowhouse.h"
|
||||
|
||||
using namespace snowhouse;
|
||||
|
||||
static void check_oob(Bytes& b, unsigned pos)
|
||||
{
|
||||
@@ -115,6 +118,49 @@ static void test_slice()
|
||||
assert((bs == Bytes{ 0, 0 }));
|
||||
}
|
||||
|
||||
static void test_split()
|
||||
{
|
||||
AssertThat(
|
||||
(Bytes{ }).split(0),
|
||||
Equals(std::vector<Bytes> {
|
||||
Bytes{}
|
||||
}));
|
||||
|
||||
AssertThat(
|
||||
(Bytes{ 0 }).split(0),
|
||||
Equals(std::vector<Bytes> {
|
||||
Bytes{},
|
||||
Bytes{}
|
||||
}));
|
||||
|
||||
AssertThat(
|
||||
(Bytes{ 1 }).split(0),
|
||||
Equals(std::vector<Bytes> {
|
||||
Bytes{ 1 }
|
||||
}));
|
||||
|
||||
AssertThat(
|
||||
(Bytes{ 1, 0 }).split(0),
|
||||
Equals(std::vector<Bytes> {
|
||||
Bytes{ 1 },
|
||||
Bytes{ }
|
||||
}));
|
||||
|
||||
AssertThat(
|
||||
(Bytes{ 0, 1 }).split(0),
|
||||
Equals(std::vector<Bytes> {
|
||||
Bytes{ },
|
||||
Bytes{ 1 }
|
||||
}));
|
||||
|
||||
AssertThat(
|
||||
(Bytes{ 1, 0, 1 }).split(0),
|
||||
Equals(std::vector<Bytes> {
|
||||
Bytes{ 1 },
|
||||
Bytes{ 1 }
|
||||
}));
|
||||
}
|
||||
|
||||
static void test_tobits()
|
||||
{
|
||||
Bytes b = {1, 2};
|
||||
@@ -139,6 +185,7 @@ int main(int argc, const char* argv[])
|
||||
test_reads();
|
||||
test_writes();
|
||||
test_slice();
|
||||
test_split();
|
||||
test_tobits();
|
||||
test_tostring();
|
||||
return 0;
|
||||
|
||||
47
tests/utils.cc
Normal file
47
tests/utils.cc
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "globals.h"
|
||||
#include "utils.h"
|
||||
#include "snowhouse/snowhouse.h"
|
||||
|
||||
using namespace snowhouse;
|
||||
|
||||
static void testLeftTrim()
|
||||
{
|
||||
AssertThat(leftTrimWhitespace("string"), Equals("string"));
|
||||
AssertThat(leftTrimWhitespace(" string"), Equals("string"));
|
||||
AssertThat(leftTrimWhitespace(" string "), Equals("string "));
|
||||
AssertThat(leftTrimWhitespace("string "), Equals("string "));
|
||||
}
|
||||
|
||||
static void testRightTrim()
|
||||
{
|
||||
AssertThat(rightTrimWhitespace("string"), Equals("string"));
|
||||
AssertThat(rightTrimWhitespace(" string"), Equals(" string"));
|
||||
AssertThat(rightTrimWhitespace(" string "), Equals(" string"));
|
||||
AssertThat(rightTrimWhitespace("string "), Equals("string"));
|
||||
}
|
||||
|
||||
static void testTrim()
|
||||
{
|
||||
AssertThat(trimWhitespace("string"), Equals("string"));
|
||||
AssertThat(trimWhitespace(" string"), Equals("string"));
|
||||
AssertThat(trimWhitespace(" string "), Equals("string"));
|
||||
AssertThat(trimWhitespace("string "), Equals("string"));
|
||||
}
|
||||
|
||||
static void testLeafname()
|
||||
{
|
||||
AssertThat(getLeafname(""), Equals(""));
|
||||
AssertThat(getLeafname("filename"), Equals("filename"));
|
||||
AssertThat(getLeafname("path/filename"), Equals("filename"));
|
||||
AssertThat(getLeafname("/path/path/filename"), Equals("filename"));
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
testLeftTrim();
|
||||
testRightTrim();
|
||||
testTrim();
|
||||
testLeafname();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
#include "globals.h"
|
||||
#include "bytes.h"
|
||||
#include "fmt/format.h"
|
||||
#include "utils.h"
|
||||
#include <fstream>
|
||||
#include "fnmatch.h"
|
||||
|
||||
/* Theoretical maximum number of sectors. */
|
||||
static const int SECTOR_COUNT = 640;
|
||||
/* Number of sectors on a 120kB disk. */
|
||||
static constexpr int SECTOR_COUNT = 468;
|
||||
|
||||
/* Start sector for data (after the directory */
|
||||
static constexpr int DATA_START_SECTOR = 14;
|
||||
|
||||
/* Size of a sector */
|
||||
static constexpr int SECTOR_SIZE = 256;
|
||||
|
||||
/* Number of dirents in a directory. */
|
||||
static constexpr int DIRECTORY_SIZE = 128;
|
||||
|
||||
struct Dirent
|
||||
{
|
||||
@@ -14,46 +25,220 @@ struct Dirent
|
||||
int sectorCount;
|
||||
};
|
||||
|
||||
static std::ifstream inputFile;
|
||||
static std::fstream file;
|
||||
static std::map<std::string, std::unique_ptr<Dirent>> directory;
|
||||
static std::map<uint16_t, uint16_t> allocationTable;
|
||||
|
||||
static std::string& rtrim(std::string& s, const char* t = " \t\n\r\f\v")
|
||||
{
|
||||
s.erase(s.find_last_not_of(t) + 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
void syntax()
|
||||
{
|
||||
std::cout << "Syntax: brother120tool <image> [<filenames...>]\n"
|
||||
"If you specify a filename, it's extracted into the current directory.\n"
|
||||
"Wildcards are allowed. If you don't, the directory is listed instead.\n";
|
||||
" brother120tool --create <image> <filenames...>\n"
|
||||
"If you specify a filename, it's extracted into the current "
|
||||
"directory.\n"
|
||||
"Wildcards are allowed. If you don't, the directory is listed "
|
||||
"instead.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void readDirectory()
|
||||
{
|
||||
for (int i=0; i<0x80; i++)
|
||||
for (int i = 0; i < DIRECTORY_SIZE; i++)
|
||||
{
|
||||
inputFile.seekg(i*16, std::ifstream::beg);
|
||||
file.seekg(i * 16, std::ifstream::beg);
|
||||
|
||||
uint8_t buffer[16];
|
||||
inputFile.read((char*) buffer, sizeof(buffer));
|
||||
Bytes buffer(16);
|
||||
file.read((char*)&buffer[0], buffer.size());
|
||||
if (buffer[0] == 0xf0)
|
||||
continue;
|
||||
|
||||
std::string filename((const char*)buffer, 8);
|
||||
filename = rtrim(filename);
|
||||
|
||||
ByteReader br(buffer);
|
||||
std::string filename = br.read(8);
|
||||
filename = filename.substr(0, filename.find(" "));
|
||||
|
||||
std::unique_ptr<Dirent> dirent(new Dirent);
|
||||
dirent->filename = filename;
|
||||
dirent->type = buffer[8];
|
||||
dirent->startSector = buffer[9]*256 + buffer[10];
|
||||
dirent->sectorCount = buffer[11];
|
||||
dirent->type = br.read_8();
|
||||
dirent->startSector = br.read_be16();
|
||||
dirent->sectorCount = br.read_8();
|
||||
directory[filename] = std::move(dirent);
|
||||
}
|
||||
}
|
||||
|
||||
void writeDirectory()
|
||||
{
|
||||
Bytes buffer(2048);
|
||||
ByteWriter bw(buffer);
|
||||
|
||||
int count = 0;
|
||||
for (const auto& it : directory)
|
||||
{
|
||||
const auto& dirent = it.second;
|
||||
|
||||
if (count == DIRECTORY_SIZE)
|
||||
Error() << "too many files on disk";
|
||||
|
||||
bw.append(dirent->filename);
|
||||
for (int i = dirent->filename.size(); i < 8; i++)
|
||||
bw.write_8(' ');
|
||||
|
||||
bw.write_8(dirent->type);
|
||||
bw.write_be16(dirent->startSector);
|
||||
bw.write_8(dirent->sectorCount);
|
||||
bw.write_be32(0); /* unknown */
|
||||
count++;
|
||||
}
|
||||
|
||||
static const Bytes padding(15);
|
||||
while (count < DIRECTORY_SIZE)
|
||||
{
|
||||
bw.write_8(0xf0);
|
||||
bw.append(padding);
|
||||
count++;
|
||||
}
|
||||
|
||||
file.seekp(0, std::ifstream::beg);
|
||||
buffer.writeTo(file);
|
||||
}
|
||||
|
||||
void writeBootSector(const Dirent& dirent, uint16_t checksum)
|
||||
{
|
||||
uint8_t sslo = dirent.startSector & 0xff;
|
||||
uint8_t sshi = dirent.startSector >> 8;
|
||||
uint8_t scnt = dirent.sectorCount;
|
||||
uint8_t end = 0x70 + scnt;
|
||||
uint8_t cklo = checksum & 0xff;
|
||||
uint8_t ckhi = checksum >> 8;
|
||||
|
||||
uint8_t machineCode[] =
|
||||
{
|
||||
/* 6000 */ 0x55, /* magic number? */
|
||||
/* 6001 */ 0xf3, /* di */
|
||||
/* 6002 */ 0x31, 0x00, 0x00, /* ld sp, $0000 */
|
||||
/* 6005 */ 0x3e, 0x00, /* ld a, $00 */
|
||||
/* 6007 */ 0xed, 0x39, 0x34, /* out0 ($34), a */
|
||||
/* 600a */ 0x3e, 0x0f, /* ld a, $0f */
|
||||
/* 600c */ 0xed, 0x39, 0x0a, /* out0 ($0a), a */
|
||||
/* 600f */ 0x3e, 0x00, /* ld a,$00 */
|
||||
/* 6011 */ 0xed, 0x39, 0xd8, /* out0 ($d8),a */
|
||||
/* 6014 */ 0x3e, 0xe7, /* ld a,$e7 */
|
||||
/* 6016 */ 0xed, 0x39, 0x3a, /* out0 ($3a),a */
|
||||
/* 6019 */ 0x3e, 0x16, /* ld a,$16 */
|
||||
/* 601b */ 0xed, 0x39, 0x38, /* out0 ($38),a */
|
||||
/* 601e */ 0x3e, 0x20, /* ld a,$20 */
|
||||
/* 6020 */ 0xed, 0x39, 0x39, /* out0 ($39),a */
|
||||
/* 6023 */ 0x01, sslo, sshi, /* ld bc, start sector */
|
||||
/* 6026 */ 0x21, 0x00, 0xf8, /* ld hl,$f800 */
|
||||
/* 6029 */ 0x1e, scnt, /* ld e, sector count */
|
||||
/* 602b */ 0x16, 0x70, /* ld d,$70 */
|
||||
/* 602d */ 0xc5, /* push bc */
|
||||
/* 602e */ 0xd5, /* push de */
|
||||
/* 602f */ 0xe5, /* push hl */
|
||||
/* 6030 */ 0x3e, 0x06, /* ld a,$06 */
|
||||
/* 6032 */ 0xef, /* rst $28 */
|
||||
/* 6033 */ 0xda, 0xd3, 0x60, /* jp c,$60d3 */
|
||||
/* 6036 */ 0xe1, /* pop hl */
|
||||
/* 6037 */ 0xd1, /* pop de */
|
||||
/* 6038 */ 0xc1, /* pop bc */
|
||||
/* 6039 */ 0x3e, 0x00, /* ld a,$00 */
|
||||
/* 603b */ 0xed, 0x39, 0x20, /* out0 ($20),a */
|
||||
/* 603e */ 0x3e, 0x58, /* ld a,$58 */
|
||||
/* 6040 */ 0xed, 0x39, 0x21, /* out0 ($21),a */
|
||||
/* 6043 */ 0x3e, 0x02, /* ld a,$02 */
|
||||
/* 6045 */ 0xed, 0x39, 0x22, /* out0 ($22),a */
|
||||
/* 6048 */ 0x3e, 0x00, /* ld a,$00 */
|
||||
/* 604a */ 0xed, 0x39, 0x23, /* out0 ($23),a */
|
||||
/* 604d */ 0x7a, /* ld a,d */
|
||||
/* 604e */ 0xed, 0x39, 0x24, /* out0 ($24),a */
|
||||
/* 6051 */ 0x3e, 0x02, /* ld a,$02 */
|
||||
/* 6053 */ 0xed, 0x39, 0x25, /* out0 ($25),a */
|
||||
/* 6056 */ 0x3e, 0x00, /* ld a,$00 */
|
||||
/* 6058 */ 0xed, 0x39, 0x26, /* out0 ($26),a */
|
||||
/* 605b */ 0x3e, 0x01, /* ld a,$01 */
|
||||
/* 605d */ 0xed, 0x39, 0x27, /* out0 ($27),a */
|
||||
/* 6060 */ 0x3e, 0x02, /* ld a,$02 */
|
||||
/* 6062 */ 0xed, 0x39, 0x31, /* out0 ($31),a */
|
||||
/* 6065 */ 0x3e, 0x40, /* ld a,$40 */
|
||||
/* 6067 */ 0xed, 0x39, 0x30, /* out0 ($30),a */
|
||||
/* 606a */ 0x03, /* inc bc */
|
||||
/* 606b */ 0x14, /* inc d */
|
||||
/* 606c */ 0x1d, /* dec e */
|
||||
/* 606d */ 0x7b, /* ld a,e */
|
||||
/* 606e */ 0xfe, 0x00, /* cp $00 */
|
||||
/* 6070 */ 0x20, 0xbb, /* jr nz,$602d */
|
||||
/* 6072 */ 0x3e, 0x02, /* ld a,$02 */
|
||||
/* 6074 */ 0xef, /* rst $28 */
|
||||
/* 6075 */ 0x3e, 0x20, /* ld a,$20 */
|
||||
/* 6077 */ 0xed, 0x39, 0x38, /* out0 ($38),a */
|
||||
/* 607a */ 0x3e, 0x0c, /* ld a,$0c */
|
||||
/* 607c */ 0xed, 0x39, 0x39, /* out0 ($39),a */
|
||||
/* 607f */ 0x3e, 0x64, /* ld a,$64 */
|
||||
/* 6081 */ 0xed, 0x39, 0x3a, /* out0 ($3a),a */
|
||||
/* 6084 */ 0x3e, 0x0f, /* ld a,$0f */
|
||||
/* 6086 */ 0xed, 0x39, 0x0a, /* out0 ($0a),a */
|
||||
|
||||
/* checksum routine */
|
||||
/* 6089 */ 0x01, 0x00, 0x70, /* ld bc,$7000 */
|
||||
/* 608c */ 0x11, 0x00, 0x00, /* ld de,$0000 */
|
||||
/* 608f */ 0x21, 0x00, 0x00, /* ld hl,$0000 */
|
||||
/* 6092 */ 0x0a, /* ld a,(bc) */
|
||||
/* 6093 */ 0x5f, /* ld e,a */
|
||||
/* 6094 */ 0x19, /* add hl,de */
|
||||
/* 6095 */ 0x03, /* inc bc */
|
||||
/* 6096 */ 0x79, /* ld a,c */
|
||||
/* 6097 */ 0xfe, 0x00, /* cp $00 */
|
||||
/* 6099 */ 0x20, 0xf7, /* jr nz,$6092 */
|
||||
/* 609b */ 0x78, /* ld a,b */
|
||||
/* 609c */ 0xfe, end, /* cp end page */
|
||||
/* 609e */ 0x20, 0xf2, /* jr nz,$6092 */
|
||||
/* 60a0 */ 0x11, 0xf3, 0x60, /* ld de,$60f3 */
|
||||
/* 60a3 */ 0x1a, /* ld a,(de) */
|
||||
/* 60a4 */ 0xbd, /* cp l */
|
||||
/* 60a5 */ 0x20, 0x2f, /* jr nz,$60d6 */
|
||||
/* 60a7 */ 0x13, /* inc de */
|
||||
/* 60a8 */ 0x1a, /* ld a,(de) */
|
||||
/* 60a9 */ 0xbc, /* cp h */
|
||||
/* 60aa */ 0x20, 0x2a, /* jr nz,$60d6 */
|
||||
|
||||
/* reset and execute */
|
||||
/* 60ac */ 0x3e, 0xff, /* ld a,$ff */
|
||||
/* 60ae */ 0xed, 0x39, 0x88, /* out0 ($88),a */
|
||||
/* 60b1 */ 0x01, 0xff, 0x0f, /* ld bc,$0fff */
|
||||
/* 60b4 */ 0x21, 0x00, 0x40, /* ld hl,$4000 */
|
||||
/* 60b7 */ 0x11, 0x01, 0x40, /* ld de,$4001 */
|
||||
/* 60ba */ 0x36, 0x00, /* ld (hl),$00 */
|
||||
/* 60bc */ 0xed, 0xb0, /* ldir */
|
||||
/* 60be */ 0x01, 0xff, 0x0f, /* ld bc,$0fff */
|
||||
/* 60c1 */ 0x21, 0x00, 0x50, /* ld hl,$5000 */
|
||||
/* 60c4 */ 0x11, 0x01, 0x50, /* ld de,$5001 */
|
||||
/* 60c7 */ 0x36, 0x20, /* ld (hl),$20 */
|
||||
/* 60c9 */ 0xed, 0xb0, /* ldir */
|
||||
/* 60cb */ 0x3e, 0xfe, /* ld a,$fe */
|
||||
/* 60cd */ 0xed, 0x39, 0x88, /* out0 ($88),a */
|
||||
/* 60d0 */ 0xc3, 0x00, 0x70, /* jp $7000 */
|
||||
|
||||
/* 60d3 */ 0xe1, /* pop hl */
|
||||
/* 60d4 */ 0xd1, /* pop de */
|
||||
/* 60d5 */ 0xc1, /* pop bc */
|
||||
/* 60d6 */ 0x01, 0x00, 0x00, /* ld bc,$0000 */
|
||||
/* 60d9 */ 0x0b, /* dec bc */
|
||||
/* 60da */ 0x3e, 0xfe, /* ld a,$fe */
|
||||
/* 60dc */ 0xed, 0x39, 0x90, /* out0 ($90),a */
|
||||
/* 60df */ 0x78, /* ld a,b */
|
||||
/* 60e0 */ 0xb1, /* or c */
|
||||
/* 60e1 */ 0x20, 0xf6, /* jr nz,$60d9 */
|
||||
/* 60e3 */ 0x31, 0x00, 0x00, /* ld sp,$0000 */
|
||||
/* 60e6 */ 0x3e, 0xff, /* ld a,$ff */
|
||||
/* 60e8 */ 0xed, 0x39, 0x90, /* out0 ($90),a */
|
||||
/* 60eb */ 0x3e, 0x0f, /* ld a,$0f */
|
||||
/* 60ed */ 0xed, 0x39, 0x0a, /* out0 ($0a),a */
|
||||
/* 60f0 */ 0xc3, 0x00, 0x00, /* jp $0000 */
|
||||
/* 60f3 */ cklo, ckhi, /* checksum */
|
||||
};
|
||||
|
||||
file.seekp(0xc00, std::ifstream::beg);
|
||||
file.write((char*) machineCode, sizeof(machineCode));
|
||||
}
|
||||
|
||||
static bool isValidFile(const Dirent& dirent)
|
||||
{
|
||||
return (dirent.filename[0] & 0x80) == 0;
|
||||
@@ -61,17 +246,47 @@ static bool isValidFile(const Dirent& dirent)
|
||||
|
||||
void readAllocationTable()
|
||||
{
|
||||
for (int sector=14; sector!=SECTOR_COUNT; sector++)
|
||||
for (int sector = 1; sector != SECTOR_COUNT; sector++)
|
||||
{
|
||||
inputFile.seekg((sector-1)*2 + 0x800, std::ifstream::beg);
|
||||
uint8_t buffer[2];
|
||||
inputFile.read((char*) buffer, sizeof(buffer));
|
||||
file.seekg((sector - 1) * 2 + 0x800, std::ifstream::beg);
|
||||
Bytes buffer(2);
|
||||
file.read((char*)&buffer[0], buffer.size());
|
||||
|
||||
uint16_t nextSector = (buffer[0]*256) + buffer[1];
|
||||
uint16_t nextSector = buffer.reader().read_be16();
|
||||
allocationTable[sector] = nextSector;
|
||||
}
|
||||
}
|
||||
|
||||
void writeAllocationTable()
|
||||
{
|
||||
Bytes buffer(SECTOR_COUNT * 2);
|
||||
ByteWriter bw(buffer);
|
||||
|
||||
for (int sector = 1; sector < (DATA_START_SECTOR - 2); sector++)
|
||||
bw.write_le16(sector + 1);
|
||||
bw.write_le16(0xffff);
|
||||
bw.write_le16(0xffff);
|
||||
for (int sector = DATA_START_SECTOR; sector != SECTOR_COUNT; sector++)
|
||||
bw.write_be16(allocationTable[sector]);
|
||||
|
||||
file.seekp(0x800, std::ifstream::beg);
|
||||
buffer.writeTo(file);
|
||||
}
|
||||
|
||||
uint16_t allocateSector()
|
||||
{
|
||||
for (int sector = DATA_START_SECTOR; sector != SECTOR_COUNT; sector++)
|
||||
{
|
||||
if (allocationTable[sector] == 0)
|
||||
{
|
||||
allocationTable[sector] = 0xffff;
|
||||
return sector;
|
||||
}
|
||||
}
|
||||
Error() << "unable to allocate sector --- disk full";
|
||||
return 0;
|
||||
}
|
||||
|
||||
void checkConsistency()
|
||||
{
|
||||
/* Verify that we more-or-less understand the format by fscking the disk. */
|
||||
@@ -82,22 +297,26 @@ void checkConsistency()
|
||||
const Dirent& dirent = *i.second;
|
||||
if (!isValidFile(dirent))
|
||||
continue;
|
||||
|
||||
|
||||
int count = 0;
|
||||
uint16_t sector = dirent.startSector;
|
||||
while ((sector != 0xffff) && (sector != 0))
|
||||
{
|
||||
if (bitmap[sector])
|
||||
std::cout << fmt::format("warning: sector {} appears to be multiply used\n", sector);
|
||||
std::cout << fmt::format(
|
||||
"warning: sector {} appears to be multiply used\n", sector);
|
||||
bitmap[sector] = true;
|
||||
sector = allocationTable[sector];
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count != dirent.sectorCount)
|
||||
std::cout <<
|
||||
fmt::format("Warning: file '{}' claims to be {} sectors long but its chain is {}\n",
|
||||
dirent.filename, dirent.sectorCount, count);
|
||||
std::cout << fmt::format(
|
||||
"Warning: file '{}' claims to be {} sectors long but its chain "
|
||||
"is {}\n",
|
||||
dirent.filename,
|
||||
dirent.sectorCount,
|
||||
count);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,26 +326,86 @@ void listDirectory()
|
||||
{
|
||||
const Dirent& dirent = *i.second;
|
||||
std::cout << fmt::format("{:9} {:6.2f}kB type {}: ",
|
||||
dirent.filename,
|
||||
(double)dirent.sectorCount / 4.0,
|
||||
dirent.type);
|
||||
dirent.filename,
|
||||
(double)dirent.sectorCount / 4.0,
|
||||
dirent.type);
|
||||
|
||||
if (isValidFile(dirent))
|
||||
std::cout << fmt::format("{} sectors starting at sector {}",
|
||||
dirent.sectorCount, dirent.startSector);
|
||||
dirent.sectorCount,
|
||||
dirent.startSector);
|
||||
else
|
||||
std::cout << "DELETED";
|
||||
std::cout << "DELETED";
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void insertFile(const std::string& filename)
|
||||
{
|
||||
auto leafname = getLeafname(filename);
|
||||
if (leafname.size() > 8)
|
||||
Error() << "filename too long";
|
||||
std::cout << fmt::format("Inserting '{}'\n", leafname);
|
||||
|
||||
std::ifstream inputFile(filename, std::ios::in | std::ios::binary);
|
||||
if (!inputFile)
|
||||
Error() << fmt::format(
|
||||
"unable to open input file: {}", strerror(errno));
|
||||
|
||||
if (directory.find(leafname) != directory.end())
|
||||
Error() << fmt::format("duplicate filename: {}", leafname);
|
||||
|
||||
auto dirent = std::make_unique<Dirent>();
|
||||
dirent->filename = leafname;
|
||||
dirent->type = (leafname.find('*') != std::string::npos);
|
||||
dirent->startSector = 0xffff;
|
||||
dirent->sectorCount = 0;
|
||||
|
||||
uint16_t lastSector = 0xffff;
|
||||
uint16_t checksum = 0;
|
||||
while (!inputFile.eof())
|
||||
{
|
||||
uint8_t buffer[SECTOR_SIZE];
|
||||
inputFile.read((char*) buffer, sizeof(buffer));
|
||||
for (int i=0; i<inputFile.gcount(); i++)
|
||||
checksum += buffer[i];
|
||||
if (inputFile.gcount() == 0)
|
||||
break;
|
||||
if (inputFile.bad())
|
||||
Error() << fmt::format("I/O error on read: {}", strerror(errno));
|
||||
|
||||
uint16_t thisSector = allocateSector();
|
||||
if (lastSector == 0xffff)
|
||||
dirent->startSector = thisSector;
|
||||
else
|
||||
allocationTable[lastSector] = thisSector;
|
||||
dirent->sectorCount++;
|
||||
|
||||
file.seekp((thisSector - 1) * 0x100, std::ifstream::beg);
|
||||
file.write((char*) buffer, sizeof(buffer));
|
||||
if (file.bad())
|
||||
Error() << fmt::format("I/O error on write: {}", strerror(errno));
|
||||
|
||||
lastSector = thisSector;
|
||||
}
|
||||
|
||||
if (leafname == "*boot")
|
||||
{
|
||||
std::cout << fmt::format(
|
||||
"Writing boot sector with checksum 0x{:04x}\n", checksum);
|
||||
writeBootSector(*dirent, checksum);
|
||||
}
|
||||
|
||||
directory[leafname] = std::move(dirent);
|
||||
}
|
||||
|
||||
void extractFile(const std::string& pattern)
|
||||
{
|
||||
for (const auto& i : directory)
|
||||
{
|
||||
const Dirent& dirent = *i.second;
|
||||
if (dirent.type != 0)
|
||||
if ((dirent.type == 0xf0) || (dirent.type == 0xe5))
|
||||
continue;
|
||||
|
||||
if (fnmatch(pattern.c_str(), dirent.filename.c_str(), 0))
|
||||
@@ -137,31 +416,56 @@ void extractFile(const std::string& pattern)
|
||||
std::ofstream outputFile(dirent.filename,
|
||||
std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
if (!outputFile)
|
||||
Error() << fmt::format("unable to open output file: {}", strerror(errno));
|
||||
Error() << fmt::format(
|
||||
"unable to open output file: {}", strerror(errno));
|
||||
|
||||
uint16_t sector = dirent.startSector;
|
||||
while ((sector != 0) && (sector != 0xffff))
|
||||
{
|
||||
uint8_t buffer[256];
|
||||
inputFile.seekg((sector-1) * 0x100, std::ifstream::beg);
|
||||
if (!inputFile.read((char*) buffer, sizeof(buffer)))
|
||||
Error() << fmt::format("I/O error on read: {}", strerror(errno));
|
||||
if (!outputFile.write((const char*) buffer, sizeof(buffer)))
|
||||
Error() << fmt::format("I/O error on write: {}", strerror(errno));
|
||||
file.seekg((sector - 1) * 0x100, std::ifstream::beg);
|
||||
if (!file.read((char*)buffer, sizeof(buffer)))
|
||||
Error() << fmt::format(
|
||||
"I/O error on read: {}", strerror(errno));
|
||||
if (!outputFile.write((const char*)buffer, sizeof(buffer)))
|
||||
Error() << fmt::format(
|
||||
"I/O error on write: {}", strerror(errno));
|
||||
|
||||
sector = allocationTable[sector];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
static void doCreate(int argc, const char* argv[])
|
||||
{
|
||||
if (argc < 3)
|
||||
syntax();
|
||||
|
||||
file.open(argv[1], std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
if (!file.is_open())
|
||||
Error() << fmt::format("cannot open output file '{}'", argv[1]);
|
||||
|
||||
file.seekp(SECTOR_COUNT * SECTOR_SIZE - 1, std::ifstream::beg);
|
||||
file.put(0);
|
||||
|
||||
for (int i = 2; i < argc; i++)
|
||||
insertFile(argv[i]);
|
||||
|
||||
writeDirectory();
|
||||
writeAllocationTable();
|
||||
checkConsistency();
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
static void doExtract(int argc, const char* argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
syntax();
|
||||
|
||||
inputFile.open(argv[1], std::ios::in | std::ios::binary);
|
||||
if (!inputFile.is_open())
|
||||
Error() << fmt::format("cannot open input file '{}'", argv[1]);
|
||||
|
||||
file.open(argv[1], std::ios::in | std::ios::binary);
|
||||
if (!file.is_open())
|
||||
Error() << fmt::format("cannot open input file '{}'", argv[1]);
|
||||
|
||||
readDirectory();
|
||||
readAllocationTable();
|
||||
@@ -171,10 +475,26 @@ int main(int argc, const char* argv[])
|
||||
listDirectory();
|
||||
else
|
||||
{
|
||||
for (int i=2; i<argc; i++)
|
||||
for (int i = 2; i < argc; i++)
|
||||
extractFile(argv[i]);
|
||||
}
|
||||
|
||||
inputFile.close();
|
||||
file.close();
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
try
|
||||
{
|
||||
if ((argc > 1) && (strcmp(argv[1], "--create") == 0))
|
||||
doCreate(argc - 1, argv + 1);
|
||||
else
|
||||
doExtract(argc, argv);
|
||||
}
|
||||
catch (const ErrorException& e)
|
||||
{
|
||||
e.print();
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -26,31 +26,39 @@ void putbyte(uint32_t offset, uint8_t value)
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
syntax();
|
||||
|
||||
inputFile.open(argv[1], std::ios::in | std::ios::out | std::ios::binary);
|
||||
if (!inputFile.is_open())
|
||||
Error() << fmt::format("cannot open input file '{}'", argv[1]);
|
||||
try
|
||||
{
|
||||
if (argc < 2)
|
||||
syntax();
|
||||
|
||||
inputFile.open(argv[1], std::ios::in | std::ios::out | std::ios::binary);
|
||||
if (!inputFile.is_open())
|
||||
Error() << fmt::format("cannot open input file '{}'", argv[1]);
|
||||
|
||||
uint8_t b1 = getbyte(0x015);
|
||||
uint8_t b2 = getbyte(0x100);
|
||||
if ((b1 == 0x58) && (b2 == 0x58))
|
||||
{
|
||||
std::cerr << "Flipping from Brother to DOS.\n";
|
||||
putbyte(0x015, 0xf0);
|
||||
putbyte(0x100, 0xf0);
|
||||
}
|
||||
else if ((b1 == 0xf0) && (b2 == 0xf0))
|
||||
{
|
||||
std::cerr << "Flipping from DOS to Brother.\n";
|
||||
putbyte(0x015, 0x58);
|
||||
putbyte(0x100, 0x58);
|
||||
}
|
||||
else
|
||||
Error() << "Unknown image format.";
|
||||
uint8_t b1 = getbyte(0x015);
|
||||
uint8_t b2 = getbyte(0x100);
|
||||
if ((b1 == 0x58) && (b2 == 0x58))
|
||||
{
|
||||
std::cerr << "Flipping from Brother to DOS.\n";
|
||||
putbyte(0x015, 0xf0);
|
||||
putbyte(0x100, 0xf0);
|
||||
}
|
||||
else if ((b1 == 0xf0) && (b2 == 0xf0))
|
||||
{
|
||||
std::cerr << "Flipping from DOS to Brother.\n";
|
||||
putbyte(0x015, 0x58);
|
||||
putbyte(0x100, 0x58);
|
||||
}
|
||||
else
|
||||
Error() << "Unknown image format.";
|
||||
|
||||
inputFile.close();
|
||||
return 0;
|
||||
inputFile.close();
|
||||
return 0;
|
||||
}
|
||||
catch (const ErrorException& e)
|
||||
{
|
||||
e.print();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -255,59 +255,67 @@ static void translateFluxVersion2(Fluxmap& fluxmap, const Bytes& bytes)
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
if ((argc != 2) || (strcmp(argv[1], "--help") == 0))
|
||||
syntax();
|
||||
try
|
||||
{
|
||||
if ((argc != 2) || (strcmp(argv[1], "--help") == 0))
|
||||
syntax();
|
||||
|
||||
std::string filename = argv[1];
|
||||
if (!isSqlite(filename))
|
||||
{
|
||||
std::cout << "File is up to date.\n";
|
||||
exit(0);
|
||||
}
|
||||
std::string filename = argv[1];
|
||||
if (!isSqlite(filename))
|
||||
{
|
||||
std::cout << "File is up to date.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
std::string outFilename = filename + ".out.flux";
|
||||
auto db = sqlOpen(filename, SQLITE_OPEN_READONLY);
|
||||
int version = sqlGetVersion(db);
|
||||
std::string outFilename = filename + ".out.flux";
|
||||
auto db = sqlOpen(filename, SQLITE_OPEN_READONLY);
|
||||
int version = sqlGetVersion(db);
|
||||
|
||||
{
|
||||
auto fluxsink = FluxSink::createFl2FluxSink(outFilename);
|
||||
for (const auto& locations : sqlFindFlux(db))
|
||||
{
|
||||
unsigned cylinder = locations.first;
|
||||
unsigned head = locations.second;
|
||||
Bytes bytes = sqlReadFluxBytes(db, cylinder, head);
|
||||
Fluxmap fluxmap;
|
||||
switch (version)
|
||||
{
|
||||
case FLUX_VERSION_2:
|
||||
translateFluxVersion2(fluxmap, bytes);
|
||||
break;
|
||||
{
|
||||
auto fluxsink = FluxSink::createFl2FluxSink(outFilename);
|
||||
for (const auto& locations : sqlFindFlux(db))
|
||||
{
|
||||
unsigned cylinder = locations.first;
|
||||
unsigned head = locations.second;
|
||||
Bytes bytes = sqlReadFluxBytes(db, cylinder, head);
|
||||
Fluxmap fluxmap;
|
||||
switch (version)
|
||||
{
|
||||
case FLUX_VERSION_2:
|
||||
translateFluxVersion2(fluxmap, bytes);
|
||||
break;
|
||||
|
||||
case FLUX_VERSION_3:
|
||||
fluxmap.appendBytes(bytes);
|
||||
break;
|
||||
case FLUX_VERSION_3:
|
||||
fluxmap.appendBytes(bytes);
|
||||
break;
|
||||
|
||||
default:
|
||||
Error() << fmt::format(
|
||||
"you cannot upgrade version {} files (please file a "
|
||||
"bug)",
|
||||
version);
|
||||
}
|
||||
fluxsink->writeFlux(cylinder, head, fluxmap);
|
||||
std::cout << '.' << std::flush;
|
||||
}
|
||||
default:
|
||||
Error() << fmt::format(
|
||||
"you cannot upgrade version {} files (please file a "
|
||||
"bug)",
|
||||
version);
|
||||
}
|
||||
fluxsink->writeFlux(cylinder, head, fluxmap);
|
||||
std::cout << '.' << std::flush;
|
||||
}
|
||||
|
||||
std::cout << "Writing output file...\n";
|
||||
}
|
||||
std::cout << "Writing output file...\n";
|
||||
}
|
||||
|
||||
sqlite3_close(db);
|
||||
sqlite3_close(db);
|
||||
|
||||
if (remove(filename.c_str()) != 0)
|
||||
Error() << fmt::format(
|
||||
"couldn't remove input file: {}", strerror(errno));
|
||||
if (remove(filename.c_str()) != 0)
|
||||
Error() << fmt::format(
|
||||
"couldn't remove input file: {}", strerror(errno));
|
||||
|
||||
if (rename(outFilename.c_str(), filename.c_str()) != 0)
|
||||
Error() << fmt::format(
|
||||
"couldn't replace input file: {}", strerror(errno));
|
||||
return 0;
|
||||
if (rename(outFilename.c_str(), filename.c_str()) != 0)
|
||||
Error() << fmt::format(
|
||||
"couldn't replace input file: {}", strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
catch (const ErrorException& e)
|
||||
{
|
||||
e.print();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user