mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
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.
This commit is contained in:
@@ -88,16 +88,6 @@ public:
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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<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 encodedSectorSize = 0;
|
||||
int gapFillSize = 0;
|
||||
int preHeaderGapFillSize = 0;
|
||||
int preambleSize = 0;
|
||||
int encodedSectorSize = 0;
|
||||
int preHeaderGapFillSize = 0;
|
||||
|
||||
bool doubleDensity;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
|
||||
int fullSectorSize = preambleSize + encodedSectorSize;
|
||||
auto fullSector = std::make_shared<std::vector<uint8_t>>();
|
||||
fullSector->reserve(fullSectorSize);
|
||||
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;
|
||||
|
||||
/* sector gap after index pulse */
|
||||
for (int i = 0; i < preHeaderGapFillSize; i++)
|
||||
fullSector->push_back(GAP1_FILL_BYTE);
|
||||
default:
|
||||
Error() << "unsupported sector size --- you must pick 256 or 512";
|
||||
break;
|
||||
}
|
||||
|
||||
/* sector preamble */
|
||||
for (int i = 0; i < preambleSize; i++)
|
||||
fullSector->push_back(0);
|
||||
int fullSectorSize = preambleSize + encodedSectorSize;
|
||||
auto fullSector = std::make_shared<std::vector<uint8_t>>();
|
||||
fullSector->reserve(fullSectorSize);
|
||||
|
||||
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);
|
||||
/* sector gap after index pulse */
|
||||
for (int i = 0; i < preHeaderGapFillSize; i++)
|
||||
fullSector->push_back(GAP1_FILL_BYTE);
|
||||
|
||||
if (sector->logicalSector != 9) {
|
||||
/* sector postamble */
|
||||
for (int i = 0; i < gapFillSize; i++)
|
||||
fullSector->push_back(GAP2_FILL_BYTE);
|
||||
/* sector preamble */
|
||||
for (int i = 0; i < preambleSize; i++)
|
||||
fullSector->push_back(0);
|
||||
|
||||
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);
|
||||
}
|
||||
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 */
|
||||
|
||||
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) {
|
||||
encodeMfm(bits, cursor, fullSector, lastBit);
|
||||
}
|
||||
else {
|
||||
encodeFm(bits, cursor, fullSector);
|
||||
}
|
||||
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())
|
||||
{}
|
||||
NorthstarEncoder(const EncoderProto& config):
|
||||
AbstractEncoder(config),
|
||||
_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>> sectors;
|
||||
std::vector<std::shared_ptr<const Sector>> collectSectors(
|
||||
int physicalTrack, int physicalSide, const Image& image) override
|
||||
{
|
||||
std::vector<std::shared_ptr<const Sector>> 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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
return sectors;
|
||||
}
|
||||
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors, const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 100000;
|
||||
double clockRateUs = 4.00;
|
||||
std::unique_ptr<Fluxmap> encode(int physicalTrack,
|
||||
int physicalSide,
|
||||
const std::vector<std::shared_ptr<const Sector>>& sectors,
|
||||
const Image& image) override
|
||||
{
|
||||
int bitsPerRevolution = 100000;
|
||||
double clockRateUs = 4.00;
|
||||
|
||||
if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty())
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
if ((physicalTrack < 0) || (physicalTrack >= 35) || sectors.empty())
|
||||
return std::unique_ptr<Fluxmap>();
|
||||
|
||||
const auto& sector = *sectors.begin();
|
||||
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) {
|
||||
bitsPerRevolution /= 2; // FM
|
||||
} else {
|
||||
clockRateUs /= 2.00;
|
||||
}
|
||||
const auto& sector = *sectors.begin();
|
||||
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD)
|
||||
bitsPerRevolution /= 2; // FM
|
||||
else
|
||||
clockRateUs /= 2.00;
|
||||
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
auto fluxmap = std::make_unique<Fluxmap>();
|
||||
bool first = true;
|
||||
for (const auto& sectorData : sectors)
|
||||
{
|
||||
if (!first)
|
||||
fluxmap->appendIndex();
|
||||
first = false;
|
||||
|
||||
for (const auto& sectorData : sectors)
|
||||
write_sector(bits, cursor, sectorData);
|
||||
std::vector<bool> bits(bitsPerRevolution);
|
||||
unsigned cursor = 0;
|
||||
write_sector(bits, cursor, sectorData);
|
||||
fluxmap->appendBits(bits, clockRateUs * 1e3);
|
||||
}
|
||||
|
||||
if (cursor > bits.size())
|
||||
Error() << "track data overrun";
|
||||
// if (fluxmap->duration() > 200e6)
|
||||
// Error() << "track data overrun";
|
||||
|
||||
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
|
||||
fluxmap->appendBits(bits, clockRateUs * 1e3);
|
||||
return fluxmap;
|
||||
}
|
||||
return fluxmap;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -56,4 +56,3 @@ Fluxmap& Fluxmap::appendBits(const std::vector<bool>& bits, nanoseconds_t clock)
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,41 +17,60 @@ public:
|
||||
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 std::string& s)
|
||||
{
|
||||
appendBytes((const uint8_t*)s.c_str(), s.size());
|
||||
}
|
||||
|
||||
Fluxmap(const Bytes bytes):
|
||||
_bytes(bytes)
|
||||
{}
|
||||
Fluxmap(const Bytes& bytes)
|
||||
{
|
||||
appendBytes(bytes);
|
||||
}
|
||||
|
||||
nanoseconds_t duration() const { return _duration; }
|
||||
unsigned ticks() const { return _ticks; }
|
||||
size_t bytes() const { return _bytes.size(); }
|
||||
const Bytes& rawBytes() const { return _bytes; }
|
||||
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 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 @@ public:
|
||||
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);
|
||||
std::vector<std::unique_ptr<const Fluxmap>> split() const;
|
||||
std::unique_ptr<const Fluxmap> rescale(double scale) const;
|
||||
|
||||
private:
|
||||
uint8_t& findLastByte();
|
||||
uint8_t& findLastByte();
|
||||
|
||||
private:
|
||||
nanoseconds_t _duration = 0;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
29
scripts/northstar87_test.textpb
Normal file
29
scripts/northstar87_test.textpb
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user