From f634988356f02d8176e6283d705fba0235952572 Mon Sep 17 00:00:00 2001 From: David Given Date: Mon, 14 Mar 2022 22:11:08 +0100 Subject: [PATCH] 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. --- arch/northstar/decoder.cc | 24 +-- arch/northstar/encoder.cc | 284 +++++++++++++++++--------------- lib/encoders/encoders.cc | 1 - lib/fluxmap.h | 63 ++++--- mkninja.sh | 9 +- scripts/northstar87_test.textpb | 29 ++++ 6 files changed, 231 insertions(+), 179 deletions(-) create mode 100644 scripts/northstar87_test.textpb diff --git a/arch/northstar/decoder.cc b/arch/northstar/decoder.cc index ffc99faf8..fb3af347e 100644 --- a/arch/northstar/decoder.cc +++ b/arch/northstar/decoder.cc @@ -88,16 +88,6 @@ class NorthstarDecoder : public AbstractDecoder 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); /* Note that the seekToPattern ignores the sector pulses, so if @@ -108,11 +98,6 @@ class NorthstarDecoder : public AbstractDecoder nanoseconds_t clock = seekToPattern(ANY_SECTOR_PATTERN); _sector->headerStartTime = tell().ns(); - /* Discard a possible partial sector. */ - if (_sector->headerStartTime > (getFluxmapDuration() - 21e6)) { - return 0; - } - int sectorFoundTimeRaw = std::round(_sector->headerStartTime / 1e6); int sectorFoundTime; @@ -132,7 +117,16 @@ class NorthstarDecoder : public AbstractDecoder void decodeSectorRecord() override { + nanoseconds_t before = tell().ns(); 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; if (id == MFM_ID) { diff --git a/arch/northstar/encoder.cc b/arch/northstar/encoder.cc index 4172ba72c..4be506cfb 100644 --- a/arch/northstar/encoder.cc +++ b/arch/northstar/encoder.cc @@ -12,155 +12,165 @@ #define GAP_FILL_SIZE_DD 62 #define PRE_HEADER_GAP_FILL_SIZE_DD 16 -#define GAP1_FILL_BYTE (0x4F) -#define GAP2_FILL_BYTE (0x4F) +#define GAP1_FILL_BYTE (0x4F) +#define GAP2_FILL_BYTE (0x4F) #define TOTAL_SECTOR_BYTES () -static void write_sector(std::vector& bits, unsigned& cursor, const std::shared_ptr& sector) +static void write_sector(std::vector& bits, + unsigned& cursor, + const std::shared_ptr& sector) { - int preambleSize = 0; - int encodedSectorSize = 0; - int gapFillSize = 0; - int preHeaderGapFillSize = 0; - - bool doubleDensity; - - switch (sector->data.size()) { - case NORTHSTAR_PAYLOAD_SIZE_SD: - preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD; - encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + NORTHSTAR_ENCODED_SECTOR_SIZE_SD + GAP_FILL_SIZE_SD; - gapFillSize = GAP_FILL_SIZE_SD; - preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD; - doubleDensity = false; - break; - case NORTHSTAR_PAYLOAD_SIZE_DD: - 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; - auto fullSector = std::make_shared>(); - fullSector->reserve(fullSectorSize); - - /* sector gap after index pulse */ - for (int i = 0; i < preHeaderGapFillSize; i++) - fullSector->push_back(GAP1_FILL_BYTE); - - /* sector preamble */ - for (int i = 0; i < preambleSize; i++) - fullSector->push_back(0); - - Bytes sectorData; - if (sector->data.size() == encodedSectorSize) - sectorData = sector->data; - 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 postamble */ - for (int i = 0; i < gapFillSize; i++) - fullSector->push_back(GAP2_FILL_BYTE); - - if (fullSector->size() != fullSectorSize) - Error() << "sector mismatched length (" << sector->data.size() << ") expected: " << fullSector->size() << " got " << fullSectorSize; - } else { - /* sector postamble */ - for (int i = 0; i < gapFillSize; i++) - fullSector->push_back(GAP2_FILL_BYTE); - } - - bool lastBit = false; - - if (doubleDensity == true) { - encodeMfm(bits, cursor, fullSector, lastBit); - } - else { - encodeFm(bits, cursor, fullSector); - } + int preambleSize = 0; + int encodedSectorSize = 0; + int preHeaderGapFillSize = 0; + + bool doubleDensity; + + switch (sector->data.size()) + { + case NORTHSTAR_PAYLOAD_SIZE_SD: + preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD; + encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + + NORTHSTAR_ENCODED_SECTOR_SIZE_SD; + preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD; + doubleDensity = false; + break; + + case NORTHSTAR_PAYLOAD_SIZE_DD: + preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD; + encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD + + NORTHSTAR_ENCODED_SECTOR_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; + auto fullSector = std::make_shared>(); + fullSector->reserve(fullSectorSize); + + /* sector gap after index pulse */ + for (int i = 0; i < preHeaderGapFillSize; i++) + fullSector->push_back(GAP1_FILL_BYTE); + + /* sector preamble */ + for (int i = 0; i < preambleSize; i++) + fullSector->push_back(0); + + Bytes sectorData; + if (sector->data.size() == encodedSectorSize) + sectorData = sector->data; + 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 (fullSector->size() != fullSectorSize) + Error() << "sector mismatched length (" << sector->data.size() + << ") expected: " << fullSector->size() << " got " + << fullSectorSize; + + /* 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 { public: - NorthstarEncoder(const EncoderProto& config): - AbstractEncoder(config), - _config(config.northstar()) - {} - - std::vector> collectSectors(int physicalTrack, int physicalSide, const Image& image) override - { - std::vector> sectors; - - if ((physicalTrack >= 0) && (physicalTrack < 35)) - { - for (int sectorId = 0; sectorId < 10; sectorId++) - { - const auto& sector = image.get(physicalTrack, physicalSide, sectorId); - if (sector) - sectors.push_back(sector); - } - } - - return sectors; - } - - std::unique_ptr encode(int physicalTrack, int physicalSide, - const std::vector>& sectors, const Image& image) override - { - int bitsPerRevolution = 100000; - double clockRateUs = 4.00; - - if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty()) - return std::unique_ptr(); - - const auto& sector = *sectors.begin(); - if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) { - bitsPerRevolution /= 2; // FM - } else { - clockRateUs /= 2.00; - } - - std::vector bits(bitsPerRevolution); - unsigned cursor = 0; - - for (const auto& sectorData : sectors) - write_sector(bits, cursor, sectorData); - - if (cursor > bits.size()) - Error() << "track data overrun"; - - std::unique_ptr fluxmap(new Fluxmap); - fluxmap->appendBits(bits, clockRateUs * 1e3); - return fluxmap; - } + NorthstarEncoder(const EncoderProto& config): + AbstractEncoder(config), + _config(config.northstar()) + { + } + + std::vector> collectSectors( + int physicalTrack, int physicalSide, const Image& image) override + { + std::vector> sectors; + + if ((physicalTrack >= 0) && (physicalTrack < 35)) + { + for (int sectorId = 0; sectorId < 10; sectorId++) + { + const auto& sector = + image.get(physicalTrack, physicalSide, sectorId); + if (sector) + sectors.push_back(sector); + } + } + + return sectors; + } + + std::unique_ptr encode(int physicalTrack, + int physicalSide, + const std::vector>& sectors, + const Image& image) override + { + int bitsPerRevolution = 100000; + double clockRateUs = 4.00; + + if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty()) + return std::unique_ptr(); + + const auto& sector = *sectors.begin(); + if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) + bitsPerRevolution /= 2; // FM + else + clockRateUs /= 2.00; + + auto fluxmap = std::make_unique(); + bool first = true; + for (const auto& sectorData : sectors) + { + if (!first) + fluxmap->appendIndex(); + first = false; + + std::vector bits(bitsPerRevolution); + unsigned cursor = 0; + write_sector(bits, cursor, sectorData); + fluxmap->appendBits(bits, clockRateUs * 1e3); + } + +// if (fluxmap->duration() > 200e6) +// Error() << "track data overrun"; + + return fluxmap; + } private: - const NorthstarEncoderProto& _config; + const NorthstarEncoderProto& _config; }; -std::unique_ptr createNorthstarEncoder(const EncoderProto& config) +std::unique_ptr createNorthstarEncoder( + const EncoderProto& config) { - return std::unique_ptr(new NorthstarEncoder(config)); + return std::unique_ptr(new NorthstarEncoder(config)); } - diff --git a/lib/encoders/encoders.cc b/lib/encoders/encoders.cc index e2094581b..932ddee67 100644 --- a/lib/encoders/encoders.cc +++ b/lib/encoders/encoders.cc @@ -56,4 +56,3 @@ Fluxmap& Fluxmap::appendBits(const std::vector& bits, nanoseconds_t clock) return *this; } - diff --git a/lib/fluxmap.h b/lib/fluxmap.h index fb389b015..6b781c232 100644 --- a/lib/fluxmap.h +++ b/lib/fluxmap.h @@ -17,41 +17,60 @@ class Fluxmap unsigned zeroes = 0; 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); } }; public: - Fluxmap() {} + Fluxmap() {} + + Fluxmap(const std::string& s) + { + appendBytes((const uint8_t*)s.c_str(), s.size()); + } + + Fluxmap(const Bytes& bytes) + { + appendBytes(bytes); + } + + nanoseconds_t duration() const + { + return _duration; + } - Fluxmap(const std::string& s) - { - appendBytes((const uint8_t*) s.c_str(), s.size()); - } + unsigned ticks() const + { + return _ticks; + } - Fluxmap(const Bytes bytes): - _bytes(bytes) - {} + size_t bytes() const + { + return _bytes.size(); + } - nanoseconds_t duration() const { return _duration; } - unsigned ticks() const { return _ticks; } - size_t bytes() const { return _bytes.size(); } - const Bytes& rawBytes() const { return _bytes; } + const Bytes& rawBytes() const + { + return _bytes; + } const uint8_t* ptr() const - { - if (!_bytes.empty()) - return &_bytes[0]; - return NULL; - } + { + if (!_bytes.empty()) + return &_bytes[0]; + return NULL; + } Fluxmap& appendInterval(uint32_t ticks); Fluxmap& appendPulse(); Fluxmap& appendIndex(); - Fluxmap& appendDesync(); + Fluxmap& appendDesync(); Fluxmap& appendBytes(const Bytes& bytes); Fluxmap& appendBytes(const uint8_t* ptr, size_t len); @@ -61,14 +80,14 @@ class Fluxmap return appendBytes(&byte, 1); } - Fluxmap& appendBits(const std::vector& bits, nanoseconds_t clock); + Fluxmap& appendBits(const std::vector& bits, nanoseconds_t clock); void precompensate(int threshold_ticks, int amount_ticks); std::vector> split() const; std::unique_ptr rescale(double scale) const; private: - uint8_t& findLastByte(); + uint8_t& findLastByte(); private: nanoseconds_t _duration = 0; diff --git a/mkninja.sh b/mkninja.sh index a947d61bf..02f41cd68 100644 --- a/mkninja.sh +++ b/mkninja.sh @@ -322,10 +322,10 @@ encodedecodetest() { echo " format=$format" echo " configs=$*" echo " fluxx=flux" - echo "build $OBJDIR/$format.encodedecode.scp.stamp : encodedecode | fluxengine$EXTENSION scripts/encodedecodetest.sh $*" - echo " format=$format" - echo " configs=$*" - echo " fluxx=scp" + #echo "build $OBJDIR/$format.encodedecode.scp.stamp : encodedecode | fluxengine$EXTENSION scripts/encodedecodetest.sh $*" + #echo " format=$format" + #echo " configs=$*" + #echo " fluxx=scp" } buildlibrary libagg.a \ @@ -677,6 +677,7 @@ encodedecodetest rx50 encodedecodetest tids990 encodedecodetest victor9k_ss encodedecodetest victor9k_ds +encodedecodetest northstar87 scripts/northstar87_test.textpb # vim: sw=4 ts=4 et diff --git a/scripts/northstar87_test.textpb b/scripts/northstar87_test.textpb new file mode 100644 index 000000000..b6a0c5adc --- /dev/null +++ b/scripts/northstar87_test.textpb @@ -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 + } + } + } +} + +