Compare commits

...

1 Commits

Author SHA1 Message Date
David Given
f634988356 Add support for emitting index sync bytes to the Northstar encoder. This tells
the FluxEngine to wait for the next index pulse before continuing the write.
This also allows FluxEngine to decode its own encoded bytestreams, allowing
encodedecodetest to work.
2022-03-14 22:11:08 +01:00
6 changed files with 211 additions and 159 deletions

View File

@@ -88,16 +88,6 @@ public:
now = tell().ns(); now = tell().ns();
} }
/* Discard a possible partial sector at the end of the track.
* This partial sector could be mistaken for a conflicted sector, if
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (getFluxmapDuration() - 21e6)) {
seekToIndexMark();
return 0;
}
int msSinceIndex = std::round(now / 1e6); int msSinceIndex = std::round(now / 1e6);
/* Note that the seekToPattern ignores the sector pulses, so if /* Note that the seekToPattern ignores the sector pulses, so if
@@ -108,11 +98,6 @@ public:
nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN); nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN);
_sector->headerStartTime = tell().ns(); _sector->headerStartTime = tell().ns();
/* Discard a possible partial sector. */
if (_sector->headerStartTime > (getFluxmapDuration() - 21e6)) {
return 0;
}
int sectorFoundTimeRaw = std::round(_sector->headerStartTime / 1e6); int sectorFoundTimeRaw = std::round(_sector->headerStartTime / 1e6);
int sectorFoundTime; int sectorFoundTime;
@@ -132,7 +117,16 @@ public:
void decodeSectorRecord() override void decodeSectorRecord() override
{ {
nanoseconds_t before = tell().ns();
uint64_t id = toBytes(readRawBits(64)).reader().read_be64(); uint64_t id = toBytes(readRawBits(64)).reader().read_be64();
nanoseconds_t after = tell().ns();
/* Discard any sectors which span the end of a revolution. This can sometimes
* cause spurious bad sectors which can trigger conflicts. */
if (int(before / 200e9) != int(after / 200e9))
return;
unsigned recordSize, payloadSize, headerSize; unsigned recordSize, payloadSize, headerSize;
if (id == MFM_ID) { if (id == MFM_ID) {

View File

@@ -12,155 +12,165 @@
#define GAP_FILL_SIZE_DD 62 #define GAP_FILL_SIZE_DD 62
#define PRE_HEADER_GAP_FILL_SIZE_DD 16 #define PRE_HEADER_GAP_FILL_SIZE_DD 16
#define GAP1_FILL_BYTE (0x4F) #define GAP1_FILL_BYTE (0x4F)
#define GAP2_FILL_BYTE (0x4F) #define GAP2_FILL_BYTE (0x4F)
#define TOTAL_SECTOR_BYTES () #define TOTAL_SECTOR_BYTES ()
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const std::shared_ptr<const Sector>& sector) static void write_sector(std::vector<bool>& bits,
unsigned& cursor,
const std::shared_ptr<const Sector>& sector)
{ {
int preambleSize = 0; int preambleSize = 0;
int encodedSectorSize = 0; int encodedSectorSize = 0;
int gapFillSize = 0; int preHeaderGapFillSize = 0;
int preHeaderGapFillSize = 0;
bool doubleDensity; bool doubleDensity;
switch (sector->data.size()) { switch (sector->data.size())
case NORTHSTAR_PAYLOAD_SIZE_SD: {
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD; case NORTHSTAR_PAYLOAD_SIZE_SD:
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + NORTHSTAR_ENCODED_SECTOR_SIZE_SD + GAP_FILL_SIZE_SD; preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
gapFillSize = GAP_FILL_SIZE_SD; encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD +
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD; NORTHSTAR_ENCODED_SECTOR_SIZE_SD;
doubleDensity = false; preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
break; doubleDensity = false;
case NORTHSTAR_PAYLOAD_SIZE_DD: break;
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD + NORTHSTAR_ENCODED_SECTOR_SIZE_DD + GAP_FILL_SIZE_DD;
gapFillSize = GAP_FILL_SIZE_DD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
doubleDensity = true;
break;
default:
Error() << "unsupported sector size --- you must pick 256 or 512";
break;
}
int fullSectorSize = preambleSize + encodedSectorSize; case NORTHSTAR_PAYLOAD_SIZE_DD:
auto fullSector = std::make_shared<std::vector<uint8_t>>(); preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
fullSector->reserve(fullSectorSize); encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD +
NORTHSTAR_ENCODED_SECTOR_SIZE_DD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
doubleDensity = true;
break;
/* sector gap after index pulse */ default:
for (int i = 0; i < preHeaderGapFillSize; i++) Error() << "unsupported sector size --- you must pick 256 or 512";
fullSector->push_back(GAP1_FILL_BYTE); break;
}
/* sector preamble */ int fullSectorSize = preambleSize + encodedSectorSize;
for (int i = 0; i < preambleSize; i++) auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->push_back(0); fullSector->reserve(fullSectorSize);
Bytes sectorData; /* sector gap after index pulse */
if (sector->data.size() == encodedSectorSize) for (int i = 0; i < preHeaderGapFillSize; i++)
sectorData = sector->data; fullSector->push_back(GAP1_FILL_BYTE);
else {
ByteWriter writer(sectorData);
writer.write_8(0xFB); /* sync character */
if (doubleDensity == true) {
writer.write_8(0xFB); /* Double-density has two sync characters */
}
writer += sector->data;
if (doubleDensity == true) {
writer.write_8(northstarChecksum(sectorData.slice(2)));
} else {
writer.write_8(northstarChecksum(sectorData.slice(1)));
}
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
if (sector->logicalSector != 9) { /* sector preamble */
/* sector postamble */ for (int i = 0; i < preambleSize; i++)
for (int i = 0; i < gapFillSize; i++) fullSector->push_back(0);
fullSector->push_back(GAP2_FILL_BYTE);
if (fullSector->size() != fullSectorSize) Bytes sectorData;
Error() << "sector mismatched length (" << sector->data.size() << ") expected: " << fullSector->size() << " got " << fullSectorSize; if (sector->data.size() == encodedSectorSize)
} else { sectorData = sector->data;
/* sector postamble */ else
for (int i = 0; i < gapFillSize; i++) {
fullSector->push_back(GAP2_FILL_BYTE); ByteWriter writer(sectorData);
} writer.write_8(0xFB); /* sync character */
if (doubleDensity == true)
writer.write_8(0xFB); /* Double-density has two sync characters */
bool lastBit = false; writer += sector->data;
if (doubleDensity == true)
writer.write_8(northstarChecksum(sectorData.slice(2)));
else
writer.write_8(northstarChecksum(sectorData.slice(1)));
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
if (doubleDensity == true) { if (fullSector->size() != fullSectorSize)
encodeMfm(bits, cursor, fullSector, lastBit); Error() << "sector mismatched length (" << sector->data.size()
} << ") expected: " << fullSector->size() << " got "
else { << fullSectorSize;
encodeFm(bits, cursor, fullSector);
} /* A few bytes of sector postamble just so the blank skip area doesn't start
* immediately after the data. */
for (int i = 0; i < 10; i++)
fullSector->push_back(GAP2_FILL_BYTE);
bool lastBit = false;
if (doubleDensity == true)
encodeMfm(bits, cursor, fullSector, lastBit);
else
encodeFm(bits, cursor, fullSector);
} }
class NorthstarEncoder : public AbstractEncoder class NorthstarEncoder : public AbstractEncoder
{ {
public: public:
NorthstarEncoder(const EncoderProto& config): NorthstarEncoder(const EncoderProto& config):
AbstractEncoder(config), AbstractEncoder(config),
_config(config.northstar()) _config(config.northstar())
{} {
}
std::vector<std::shared_ptr<const Sector>> collectSectors(int physicalTrack, int physicalSide, const Image& image) override std::vector<std::shared_ptr<const Sector>> collectSectors(
{ int physicalTrack, int physicalSide, const Image& image) override
std::vector<std::shared_ptr<const Sector>> sectors; {
std::vector<std::shared_ptr<const Sector>> sectors;
if ((physicalTrack >= 0) && (physicalTrack < 35)) if ((physicalTrack >= 0) && (physicalTrack < 35))
{ {
for (int sectorId = 0; sectorId < 10; sectorId++) for (int sectorId = 0; sectorId < 10; sectorId++)
{ {
const auto& sector = image.get(physicalTrack, physicalSide, sectorId); const auto& sector =
if (sector) image.get(physicalTrack, physicalSide, sectorId);
sectors.push_back(sector); if (sector)
} sectors.push_back(sector);
} }
}
return sectors; return sectors;
} }
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, std::unique_ptr<Fluxmap> encode(int physicalTrack,
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override int physicalSide,
{ const std::vector<std::shared_ptr<const Sector>>& sectors,
int bitsPerRevolution = 100000; const Image& image) override
double clockRateUs = 4.00; {
int bitsPerRevolution = 100000;
double clockRateUs = 4.00;
if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty()) if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty())
return std::unique_ptr<Fluxmap>(); return std::unique_ptr<Fluxmap>();
const auto& sector = *sectors.begin(); const auto& sector = *sectors.begin();
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) { if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD)
bitsPerRevolution /= 2; // FM bitsPerRevolution /= 2; // FM
} else { else
clockRateUs /= 2.00; clockRateUs /= 2.00;
}
std::vector<bool> bits(bitsPerRevolution); auto fluxmap = std::make_unique<Fluxmap>();
unsigned cursor = 0; bool first = true;
for (const auto& sectorData : sectors)
{
if (!first)
fluxmap->appendIndex();
first = false;
for (const auto& sectorData : sectors) std::vector<bool> bits(bitsPerRevolution);
write_sector(bits, cursor, sectorData); unsigned cursor = 0;
write_sector(bits, cursor, sectorData);
fluxmap->appendBits(bits, clockRateUs * 1e3);
}
if (cursor > bits.size()) // if (fluxmap->duration() > 200e6)
Error() << "track data overrun"; // Error() << "track data overrun";
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap); return fluxmap;
fluxmap->appendBits(bits, clockRateUs * 1e3); }
return fluxmap;
}
private: private:
const NorthstarEncoderProto& _config; const NorthstarEncoderProto& _config;
}; };
std::unique_ptr<AbstractEncoder> createNorthstarEncoder(const EncoderProto& config) std::unique_ptr<AbstractEncoder> createNorthstarEncoder(
const EncoderProto& config)
{ {
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config)); return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config));
} }

View File

@@ -56,4 +56,3 @@ Fluxmap& Fluxmap::appendBits(const std::vector<bool>& bits, nanoseconds_t clock)
return *this; return *this;
} }

View File

@@ -17,41 +17,60 @@ public:
unsigned zeroes = 0; unsigned zeroes = 0;
nanoseconds_t ns() const nanoseconds_t ns() const
{ return ticks * NS_PER_TICK; } {
return ticks * NS_PER_TICK;
}
operator std::string () { operator std::string()
{
return fmt::format("[b:{}, t:{}, z:{}]", bytes, ticks, zeroes); return fmt::format("[b:{}, t:{}, z:{}]", bytes, ticks, zeroes);
} }
}; };
public: public:
Fluxmap() {} Fluxmap() {}
Fluxmap(const std::string& s) Fluxmap(const std::string& s)
{ {
appendBytes((const uint8_t*) s.c_str(), s.size()); appendBytes((const uint8_t*)s.c_str(), s.size());
} }
Fluxmap(const Bytes bytes): Fluxmap(const Bytes& bytes)
_bytes(bytes) {
{} appendBytes(bytes);
}
nanoseconds_t duration() const { return _duration; } nanoseconds_t duration() const
unsigned ticks() const { return _ticks; } {
size_t bytes() const { return _bytes.size(); } return _duration;
const Bytes& rawBytes() const { return _bytes; } }
unsigned ticks() const
{
return _ticks;
}
size_t bytes() const
{
return _bytes.size();
}
const Bytes& rawBytes() const
{
return _bytes;
}
const uint8_t* ptr() const const uint8_t* ptr() const
{ {
if (!_bytes.empty()) if (!_bytes.empty())
return &_bytes[0]; return &_bytes[0];
return NULL; return NULL;
} }
Fluxmap& appendInterval(uint32_t ticks); Fluxmap& appendInterval(uint32_t ticks);
Fluxmap& appendPulse(); Fluxmap& appendPulse();
Fluxmap& appendIndex(); Fluxmap& appendIndex();
Fluxmap& appendDesync(); Fluxmap& appendDesync();
Fluxmap& appendBytes(const Bytes& bytes); Fluxmap& appendBytes(const Bytes& bytes);
Fluxmap& appendBytes(const uint8_t* ptr, size_t len); Fluxmap& appendBytes(const uint8_t* ptr, size_t len);
@@ -61,14 +80,14 @@ public:
return appendBytes(&byte, 1); return appendBytes(&byte, 1);
} }
Fluxmap& appendBits(const std::vector<bool>& bits, nanoseconds_t clock); Fluxmap& appendBits(const std::vector<bool>& bits, nanoseconds_t clock);
void precompensate(int threshold_ticks, int amount_ticks); void precompensate(int threshold_ticks, int amount_ticks);
std::vector<std::unique_ptr<const Fluxmap>> split() const; std::vector<std::unique_ptr<const Fluxmap>> split() const;
std::unique_ptr<const Fluxmap> rescale(double scale) const; std::unique_ptr<const Fluxmap> rescale(double scale) const;
private: private:
uint8_t& findLastByte(); uint8_t& findLastByte();
private: private:
nanoseconds_t _duration = 0; nanoseconds_t _duration = 0;

View File

@@ -322,10 +322,10 @@ encodedecodetest() {
echo " format=$format" echo " format=$format"
echo " configs=$*" echo " configs=$*"
echo " fluxx=flux" echo " fluxx=flux"
echo "build $OBJDIR/$format.encodedecode.scp.stamp : encodedecode | fluxengine$EXTENSION scripts/encodedecodetest.sh $*" #echo "build $OBJDIR/$format.encodedecode.scp.stamp : encodedecode | fluxengine$EXTENSION scripts/encodedecodetest.sh $*"
echo " format=$format" #echo " format=$format"
echo " configs=$*" #echo " configs=$*"
echo " fluxx=scp" #echo " fluxx=scp"
} }
buildlibrary libagg.a \ buildlibrary libagg.a \
@@ -677,6 +677,7 @@ encodedecodetest rx50
encodedecodetest tids990 encodedecodetest tids990
encodedecodetest victor9k_ss encodedecodetest victor9k_ss
encodedecodetest victor9k_ds encodedecodetest victor9k_ds
encodedecodetest northstar87 scripts/northstar87_test.textpb
# vim: sw=4 ts=4 et # vim: sw=4 ts=4 et

View File

@@ -0,0 +1,29 @@
image_reader {
img {
tracks: 35
sides: 1
trackdata {
sector_size: 256
sector_range {
start_sector: 0
sector_count: 10
}
}
}
}
image_writer {
img {
tracks: 35
sides: 1
trackdata {
sector_size: 256
sector_range {
start_sector: 0
sector_count: 10
}
}
}
}