Compare commits

...

13 Commits

Author SHA1 Message Date
David Given
8e5e4d3f5b Remove the obsolete northstar image writer and update the profiles. 2021-06-12 17:24:26 +02:00
David Given
3b81e84e05 Sort of make the LDBS writer work; however, I think there are problems with the
LDBS geometry autodetection because it assumes things which aren't necessarily
true in our output images.
2021-06-12 15:34:37 +02:00
David Given
b8c198329d Add the image reading geometry mapper; overhaul the reader to use it; convert
the .img imagewriter; remove the d64 imagewriter (as this can now be
represented with img).
2021-06-09 23:07:32 +02:00
David Given
1b85464b85 Convert the JV3 and IMD image readers; overhaul the IMD reader. 2021-06-09 23:00:48 +02:00
David Given
9231325073 Merge from master. 2021-06-05 11:16:42 +02:00
David Given
644f592df3 Make the JV3 imagereader work again; allow the imagereader to act as a geometry
mapper for formats which have track/side/sector information.
2021-05-30 23:57:14 +02:00
David Given
dee44bf481 Fix a bytes bug when creating slices of slices with a non-zero offset. 2021-05-30 23:56:35 +02:00
David Given
1fcb8c7179 Rename dgmapper.cc to something more sensible. 2021-05-29 00:05:10 +02:00
David Given
1440c3c8d6 Make the Macintosh encoder work again; convert the diskcopy reader to work with
the new geometry system.
2021-05-29 00:04:13 +02:00
David Given
2935c2d925 Replace the d64 image reader with an appropriate img reader configuration. 2021-05-27 23:47:43 +02:00
David Given
ebc25f9758 Merge from master. 2021-05-27 23:39:53 +02:00
David Given
99b9145a0d Convert the imagereader to a simple block fetcher and hook up the geometry
mapper to it. Seems to work given some basic testing.
2021-05-27 00:06:50 +02:00
David Given
e7348eaa4f Added a functioning (I hope) sketch of the disassembling geometry mapper code. 2021-05-26 00:00:52 +02:00
83 changed files with 2073 additions and 1145 deletions

View File

@@ -14,6 +14,7 @@ class Fluxmap;
class SectorSet;
class AmigaDecoderProto;
class AmigaEncoderProto;
class DisassemblingGeometryMapper;
class AmigaDecoder : public AbstractDecoder
{
@@ -30,15 +31,18 @@ public:
class AmigaEncoder : public AbstractEncoder
{
public:
AmigaEncoder(const AmigaEncoderProto& config):
_config(config) {}
AmigaEncoder(const AmigaEncoderProto& config, const DisassemblingGeometryMapper& mapper):
_config(config),
_mapper(mapper)
{}
virtual ~AmigaEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide);
private:
const AmigaEncoderProto& _config;
const DisassemblingGeometryMapper& _mapper;
};
extern uint32_t amigaChecksum(const Bytes& bytes);

View File

@@ -6,6 +6,7 @@
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
#include "geometry/geometry.h"
FlagGroup amigaEncoderFlags;
@@ -99,8 +100,7 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
write_bits(bits, cursor, dataBits);
}
std::unique_ptr<Fluxmap> AmigaEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
std::unique_ptr<Fluxmap> AmigaEncoder::encode(int physicalTrack, int physicalSide)
{
if ((physicalTrack < 0) || (physicalTrack >= AMIGA_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
@@ -114,8 +114,9 @@ std::unique_ptr<Fluxmap> AmigaEncoder::encode(
for (int sectorId=0; sectorId<AMIGA_SECTORS_PER_TRACK; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
const auto* sectorData = _mapper.get(physicalTrack, physicalSide, sectorId);
if (sectorData)
write_sector(bits, cursor, sectorData);
}
if (cursor >= bits.size())

View File

@@ -18,6 +18,7 @@ class SectorSet;
class Fluxmap;
class BrotherDecoderProto;
class BrotherEncoderProto;
class DisassemblingGeometryMapper;
class BrotherDecoder : public AbstractDecoder
{
@@ -33,17 +34,19 @@ public:
class BrotherEncoder : public AbstractEncoder
{
public:
BrotherEncoder(const BrotherEncoderProto& config):
_config(config)
BrotherEncoder(const BrotherEncoderProto& config, const DisassemblingGeometryMapper& mapper):
_config(config),
_mapper(mapper)
{}
virtual ~BrotherEncoder() {}
private:
const BrotherEncoderProto& _config;
const DisassemblingGeometryMapper& _mapper;
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide);
};
#endif

View File

@@ -7,6 +7,7 @@
#include "sectorset.h"
#include "writer.h"
#include "arch/brother/brother.pb.h"
#include "geometry/geometry.h"
FlagGroup brotherEncoderFlags;
@@ -127,8 +128,7 @@ static int charToInt(char c)
return 10 + tolower(c) - 'a';
}
std::unique_ptr<Fluxmap> BrotherEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
std::unique_ptr<Fluxmap> BrotherEncoder::encode(int physicalTrack, int physicalSide)
{
int logicalTrack;
if (physicalSide != 0)
@@ -163,7 +163,7 @@ std::unique_ptr<Fluxmap> BrotherEncoder::encode(
double dataMs = headerMs + postHeaderSpacingMs;
unsigned dataCursor = dataMs*1e3 / clockRateUs;
const auto& sectorData = allSectors.get(logicalTrack, 0, sectorId);
const auto& sectorData = _mapper.get(logicalTrack, 0, sectorId);
fillBitmapTo(bits, cursor, headerCursor, { true, false });
write_sector_header(bits, cursor, logicalTrack, sectorId);

View File

@@ -32,6 +32,7 @@ class Sector;
class Fluxmap;
class Commodore64DecoderProto;
class Commodore64EncoderProto;
class DisassemblingGeometryMapper;
class Commodore64Decoder : public AbstractDecoder
{
@@ -47,17 +48,19 @@ public:
class Commodore64Encoder : public AbstractEncoder
{
public:
Commodore64Encoder(const Commodore64EncoderProto& config):
_config(config)
Commodore64Encoder(const Commodore64EncoderProto& config, const DisassemblingGeometryMapper& mapper):
_config(config),
_mapper(mapper)
{}
virtual ~Commodore64Encoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide);
private:
const Commodore64EncoderProto& _config;
const DisassemblingGeometryMapper& _mapper;
};
#endif

View File

@@ -9,6 +9,7 @@
#include "writer.h"
#include "fmt/format.h"
#include "arch/c64/c64.pb.h"
#include "geometry/geometry.h"
#include <ctype.h>
#include "bytes.h"
@@ -293,8 +294,7 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
}
}
std::unique_ptr<Fluxmap> Commodore64Encoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
std::unique_ptr<Fluxmap> Commodore64Encoder::encode(int physicalTrack, int physicalSide)
{
/* The format ID Character # 1 and # 2 are in the .d64 image only present
* in track 18 sector zero which contains the BAM info in byte 162 and 163.
@@ -305,7 +305,7 @@ std::unique_ptr<Fluxmap> Commodore64Encoder::encode(
if ((physicalTrack < 0) || (physicalTrack >= C64_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
const auto& sectorData = allSectors.get(C64_BAM_TRACK, 0, 0); //Read de BAM to get the DISK ID bytes
const auto* sectorData = _mapper.get(C64_BAM_TRACK, 0, 0); //Read de BAM to get the DISK ID bytes
ByteReader br(sectorData->data);
br.seek(162); //goto position of the first Disk ID Byte
@@ -325,7 +325,7 @@ std::unique_ptr<Fluxmap> Commodore64Encoder::encode(
unsigned numSectors = sectorsForTrack(physicalTrack);
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
const auto& sectorData = _mapper.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
}

View File

@@ -7,6 +7,7 @@
#include "sectorset.h"
#include "writer.h"
#include "arch/ibm/ibm.pb.h"
#include "geometry/geometry.h"
#include "fmt/format.h"
#include <ctype.h>
@@ -99,8 +100,7 @@ void IbmEncoder::getTrackFormat(IbmEncoderProto::TrackdataProto& trackdata, unsi
}
}
std::unique_ptr<Fluxmap> IbmEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
std::unique_ptr<Fluxmap> IbmEncoder::encode(int physicalTrack, int physicalSide)
{
IbmEncoderProto::TrackdataProto trackdata;
getTrackFormat(trackdata, physicalTrack, physicalSide);
@@ -165,7 +165,7 @@ std::unique_ptr<Fluxmap> IbmEncoder::encode(
writeFillerBytes(trackdata.gap3(), gapFill);
first = false;
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
const auto* sectorData = _mapper.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
{
/* If there are any missing sectors, this is an empty track. */

View File

@@ -30,6 +30,8 @@ struct IbmIdam
uint8_t crc[2];
};
class DisassemblingGeometryMapper;
class IbmDecoder : public AbstractDecoder
{
public:
@@ -52,14 +54,15 @@ private:
class IbmEncoder : public AbstractEncoder
{
public:
IbmEncoder(const IbmEncoderProto& config):
_config(config)
IbmEncoder(const IbmEncoderProto& config, const DisassemblingGeometryMapper& mapper):
_config(config),
_mapper(mapper)
{}
virtual ~IbmEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide);
private:
void writeRawBits(uint32_t data, int width);
@@ -69,6 +72,7 @@ private:
private:
const IbmEncoderProto& _config;
const DisassemblingGeometryMapper& _mapper;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;

View File

@@ -6,21 +6,11 @@
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
#include "geometry/geometry.h"
#include "fmt/format.h"
#include "arch/macintosh/macintosh.pb.h"
#include <ctype.h>
FlagGroup macintoshEncoderFlags;
static DoubleFlag postIndexGapUs(
{ "--post-index-gap-us" },
"Post-index gap before first sector header (microseconds).",
0);
static DoubleFlag clockCompensation(
{ "--clock-compensation-factor" },
"Scale the output clock by this much.",
1.0);
static bool lastBit;
static double clockRateUsForTrack(unsigned track)
@@ -210,25 +200,25 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
write_bits(bits, cursor, 0xdeaaff, 3*8);
}
std::unique_ptr<Fluxmap> MacintoshEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
std::unique_ptr<Fluxmap> MacintoshEncoder::encode(int physicalTrack, int physicalSide)
{
if ((physicalTrack < 0) || (physicalTrack >= MAC_TRACKS_PER_DISK))
return std::unique_ptr<Fluxmap>();
double clockRateUs = clockRateUsForTrack(physicalTrack) * clockCompensation;
int bitsPerRevolution = 200000.0 / clockRateUs;
double clockRateUs = clockRateUsForTrack(physicalTrack) * _config.clock_compensation_factor();
int bitsPerRevolution = 200000.0 / _config.clock_compensation_factor();
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
fillBitmapTo(bits, cursor, postIndexGapUs / clockRateUs, { true, false });
fillBitmapTo(bits, cursor, _config.post_index_gap_us() / _config.clock_compensation_factor(), { true, false });
lastBit = false;
unsigned numSectors = sectorsForTrack(physicalTrack);
for (int sectorId=0; sectorId<numSectors; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
const auto* sectorData = _mapper.get(physicalTrack, physicalSide, sectorId);
if (sectorData)
write_sector(bits, cursor, sectorData);
}
if (cursor >= bits.size())

View File

@@ -17,6 +17,7 @@ class Sector;
class Fluxmap;
class MacintoshDecoderProto;
class MacintoshEncoderProto;
class DisassemblingGeometryMapper;
class MacintoshDecoder : public AbstractDecoder
{
@@ -34,11 +35,19 @@ public:
class MacintoshEncoder : public AbstractEncoder
{
public:
MacintoshEncoder(const MacintoshEncoderProto&) {}
MacintoshEncoder(const MacintoshEncoderProto& config, const DisassemblingGeometryMapper& mapper):
_config(config),
_mapper(mapper)
{}
virtual ~MacintoshEncoder() {}
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide);
private:
const MacintoshEncoderProto& _config;
const DisassemblingGeometryMapper& _mapper;
};
extern FlagGroup macintoshEncoderFlags;

View File

@@ -1,5 +1,13 @@
syntax = "proto2";
message MacintoshDecoderProto {}
message MacintoshEncoderProto {}
import "lib/common.proto";
message MacintoshDecoderProto {}
message MacintoshEncoderProto {
optional double post_index_gap_us = 1
[default=0.0, (help)="Post-index gap before first sector header."];
optional double clock_compensation_factor = 2
[default=1.0, (help)="Scale the output clock by this much."];
}

View File

@@ -1,6 +1,7 @@
#include "globals.h"
#include "northstar.h"
#include "sectorset.h"
#include "geometry/geometry.h"
#define GAP_FILL_SIZE_SD 30
#define PRE_HEADER_GAP_FILL_SIZE_SD 9
@@ -95,8 +96,7 @@ static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector
}
}
std::unique_ptr<Fluxmap> NorthstarEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
std::unique_ptr<Fluxmap> NorthstarEncoder::encode(int physicalTrack, int physicalSide)
{
int bitsPerRevolution = 100000;
double clockRateUs = 4.00;
@@ -104,7 +104,7 @@ std::unique_ptr<Fluxmap> NorthstarEncoder::encode(
if ((physicalTrack < 0) || (physicalTrack >= 35))
return std::unique_ptr<Fluxmap>();
const auto& sector = allSectors.get(physicalTrack, physicalSide, 0);
const auto* sector = _mapper.get(physicalTrack, physicalSide, 0);
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) {
bitsPerRevolution /= 2; // FM
@@ -117,7 +117,7 @@ std::unique_ptr<Fluxmap> NorthstarEncoder::encode(
for (int sectorId = 0; sectorId < 10; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
const auto* sectorData = _mapper.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
}

View File

@@ -30,6 +30,7 @@
class NorthstarEncoderProto;
class NorthstarDecoderProto;
class DisassemblingGeometryMapper;
class NorthstarDecoder : public AbstractDecoder
{
@@ -55,15 +56,17 @@ private:
class NorthstarEncoder : public AbstractEncoder
{
public:
NorthstarEncoder(const NorthstarEncoderProto& config):
_config(config)
NorthstarEncoder(const NorthstarEncoderProto& config, const DisassemblingGeometryMapper& mapper):
_config(config),
_mapper(mapper)
{}
virtual ~NorthstarEncoder() {}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide);
private:
const NorthstarEncoderProto& _config;
const DisassemblingGeometryMapper& _mapper;
};
extern FlagGroup northstarEncoderFlags;

View File

@@ -6,6 +6,7 @@
#include "crc.h"
#include "sectorset.h"
#include "writer.h"
#include "geometry/geometry.h"
#include "arch/tids990/tids990.pb.h"
#include <fmt/format.h>
@@ -49,8 +50,7 @@ static uint8_t decodeUint16(uint16_t raw)
return decodeFmMfm(b.toBits())[0];
}
std::unique_ptr<Fluxmap> Tids990Encoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
std::unique_ptr<Fluxmap> Tids990Encoder::encode(int physicalTrack, int physicalSide)
{
double clockRateUs = 1e3 / _config.clock_rate_khz() / 2.0;
int bitsPerRevolution = (_config.track_length_ms() * 1000.0) / clockRateUs;
@@ -70,7 +70,7 @@ std::unique_ptr<Fluxmap> Tids990Encoder::encode(
writeBytes(_config.gap3_bytes(), 0x55);
first = false;
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
const auto* sectorData = _mapper.get(physicalTrack, physicalSide, sectorId);
if (!sectorData)
Error() << fmt::format("format tried to find sector {} which wasn't in the input file", sectorId);

View File

@@ -11,6 +11,7 @@ class Fluxmap;
class Track;
class Tids990DecoderProto;
class Tids990EncoderProto;
class DisassemblingGeometryMapper;
class Tids990Decoder : public AbstractDecoder
{
@@ -26,8 +27,9 @@ public:
class Tids990Encoder : public AbstractEncoder
{
public:
Tids990Encoder(const Tids990EncoderProto& config):
_config(config)
Tids990Encoder(const Tids990EncoderProto& config, const DisassemblingGeometryMapper& mapper):
_config(config),
_mapper(mapper)
{}
virtual ~Tids990Encoder() {}
@@ -38,10 +40,11 @@ private:
void writeSync();
public:
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide);
private:
const Tids990EncoderProto& _config;
const DisassemblingGeometryMapper& _mapper;
std::vector<bool> _bits;
unsigned _cursor;
bool _lastBit;

View File

@@ -32,9 +32,12 @@ fluxengine read northstar
To read a single-sided North Star floppy, run:
```
fluxengine read northstar -heads 0
fluxengine read <format> -heads 0
```
...where `<format>` is `northstar87`, `northstar175` or `northstar350`
depending on the format you want to read.
You should end up with a `northstar.nsi` with a file size dependent on the floppy
disk type:

View File

@@ -65,6 +65,14 @@ Bytes::Bytes(std::shared_ptr<std::vector<uint8_t>> data, unsigned start, unsigne
_high(end)
{}
Bytes::Bytes(std::istream& stream, size_t size):
_data(createVector(size)),
_low(0),
_high(size)
{
stream.read((char*)begin(), size);
}
Bytes* Bytes::operator = (const Bytes& other)
{
_data = other._data;
@@ -130,14 +138,14 @@ uint8_t& Bytes::operator [] (unsigned pos)
Bytes Bytes::slice(unsigned start, unsigned len) const
{
start += _low;
unsigned end = start + len;
if (start >= _high)
unsigned datastart = _low + start;
unsigned dataend = datastart + len;
if (datastart >= _high)
{
/* Asking for a completely out-of-range slice --- just return zeroes. */
return Bytes(len);
}
else if (end > _high)
else if (dataend > _high)
{
/* Can't share the buffer, as we need to zero-pad the end. */
Bytes b(len);
@@ -147,7 +155,7 @@ Bytes Bytes::slice(unsigned start, unsigned len) const
else
{
/* Use the magic of shared_ptr to share the data. */
Bytes b(_data, start, end);
Bytes b(_data, datastart, dataend);
return b;
}
}

View File

@@ -16,6 +16,7 @@ public:
Bytes(std::initializer_list<uint8_t> data);
Bytes(std::shared_ptr<std::vector<uint8_t>> data);
Bytes(std::shared_ptr<std::vector<uint8_t>> data, unsigned start, unsigned end);
Bytes(std::istream& stream, size_t len);
Bytes* operator = (const Bytes& other);
@@ -64,6 +65,16 @@ public:
void writeToFile(const std::string& filename) const;
void writeTo(std::ostream& stream) const;
template <class T> std::vector<T> toVector() const
{
std::vector<T> v(size());
for (int i=0; i<size(); i++)
v[i] = (*this)[i];
return v;
}
std::vector<uint8_t> toVector() const;
private:
std::shared_ptr<std::vector<uint8_t>> _data;
unsigned _low;
@@ -296,6 +307,11 @@ public:
return *this += stream;
}
ByteWriter& append(std::istream& stream, size_t len)
{
return *this += Bytes(stream, len);
}
private:
Bytes& _bytes;
};

View File

@@ -12,10 +12,14 @@ extend google.protobuf.FieldOptions {
optional string help = 50000;
}
extend google.protobuf.EnumValueOptions {
optional string ehelp = 50000;
}
enum IndexMode {
INDEXMODE_DRIVE = 0;
INDEXMODE_300 = 1;
INDEXMODE_360 = 2;
INDEXMODE_DRIVE = 0 [(ehelp) = "source index pulses from drive"];
INDEXMODE_300 = 1 [(ehelp) = "source index pulses from fake 300RPM source"];
INDEXMODE_360 = 2 [(ehelp) = "source index pulses from fake 360RPM source"];
}

View File

@@ -7,6 +7,7 @@ import "lib/imagewriter/imagewriter.proto";
import "lib/fluxsource/fluxsource.proto";
import "lib/fluxsink/fluxsink.proto";
import "lib/usb/usb.proto";
import "lib/geometry/geometry.proto";
import "lib/common.proto";
message InputProto {
@@ -24,15 +25,16 @@ message OutputProto {
}
message ConfigProto {
optional string comment = 8;
optional string comment = 1;
optional InputProto input = 1;
optional OutputProto output = 2;
optional EncoderProto encoder = 3;
optional DecoderProto decoder = 4;
optional UsbProto usb = 5;
optional InputProto input = 2;
optional OutputProto output = 3;
optional EncoderProto encoder = 4;
optional DecoderProto decoder = 5;
optional UsbProto usb = 6;
optional GeometryProto geometry = 7;
optional RangeProto cylinders = 6;
optional RangeProto heads = 7;
optional RangeProto cylinders = 8;
optional RangeProto heads = 9;
}

View File

@@ -12,27 +12,28 @@
#include "lib/encoders/encoders.pb.h"
#include "protocol.h"
std::unique_ptr<AbstractEncoder> AbstractEncoder::create(const EncoderProto& config)
std::unique_ptr<AbstractEncoder> AbstractEncoder::create(
const EncoderProto& config, const DisassemblingGeometryMapper& mapper)
{
switch (config.format_case())
{
case EncoderProto::kAmiga:
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config.amiga()));
return std::unique_ptr<AbstractEncoder>(new AmigaEncoder(config.amiga(), mapper));
case EncoderProto::kIbm:
return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config.ibm()));
return std::unique_ptr<AbstractEncoder>(new IbmEncoder(config.ibm(), mapper));
case EncoderProto::kBrother:
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config.brother()));
return std::unique_ptr<AbstractEncoder>(new BrotherEncoder(config.brother(), mapper));
case EncoderProto::kMacintosh:
return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config.macintosh()));
return std::unique_ptr<AbstractEncoder>(new MacintoshEncoder(config.macintosh(), mapper));
case EncoderProto::kC64:
return std::unique_ptr<AbstractEncoder>(new Commodore64Encoder(config.c64()));
return std::unique_ptr<AbstractEncoder>(new Commodore64Encoder(config.c64(), mapper));
case EncoderProto::kNorthstar:
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config.northstar()));
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config.northstar(), mapper));
default:
Error() << "no input disk format specified";

View File

@@ -5,17 +5,17 @@ class FluxSource;
class Fluxmap;
class SectorSet;
class EncoderProto;
class DisassemblingGeometryMapper;
class AbstractEncoder
{
public:
virtual ~AbstractEncoder() {}
static std::unique_ptr<AbstractEncoder> create(const EncoderProto& config);
static std::unique_ptr<AbstractEncoder> create(const EncoderProto& config, const DisassemblingGeometryMapper& mapper);
public:
virtual std::unique_ptr<Fluxmap> encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors) = 0;
virtual std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide) = 0;
};
#endif

137
lib/geometry/geometry.cc Normal file
View File

@@ -0,0 +1,137 @@
#include "globals.h"
#include "geometry/geometry.h"
#include "sectorset.h"
#include "sector.h"
#include "fmt/format.h"
#include <iostream>
#include <fstream>
void AssemblingGeometryMapper::put(const SectorSet& sectors)
{
for (const auto& sit : sectors.get())
put(*sit.second);
}
void AssemblingGeometryMapper::writeCsv(const SectorSet& sectors, const std::string& filename)
{
std::ofstream f(filename, std::ios::out);
if (!f.is_open())
Error() << "cannot open CSV report file";
f << "\"Physical track\","
"\"Physical side\","
"\"Logical track\","
"\"Logical side\","
"\"Logical sector\","
"\"Clock (ns)\","
"\"Header start (ns)\","
"\"Header end (ns)\","
"\"Data start (ns)\","
"\"Data end (ns)\","
"\"Raw data address (bytes)\","
"\"User payload length (bytes)\","
"\"Status\""
"\n";
for (const auto& it : sectors.get())
{
const auto& sector = it.second;
f << fmt::format("{},{},{},{},{},{},{},{},{},{},{},{},{}\n",
sector->physicalTrack,
sector->physicalSide,
sector->logicalTrack,
sector->logicalSide,
sector->logicalSector,
sector->clock,
sector->headerStartTime,
sector->headerEndTime,
sector->dataStartTime,
sector->dataEndTime,
sector->position.bytes,
sector->data.size(),
Sector::statusToString(sector->status)
);
}
}
void AssemblingGeometryMapper::printMap(const SectorSet& sectors)
{
unsigned numCylinders;
unsigned numHeads;
unsigned numSectors;
unsigned numBytes;
sectors.calculateSize(numCylinders, numHeads, numSectors, numBytes);
int badSectors = 0;
int missingSectors = 0;
int totalSectors = 0;
std::cout << " Tracks -> 1 2 3 ";
if (numCylinders > 40) {
std::cout << "4 5 6 7 8";
}
std::cout << std::endl;
std::cout << "H.SS 0123456789012345678901234567890123456789";
if (numCylinders > 40) {
std::cout << "01234567890123456789012345678901234567890123";
}
std::cout << std::endl;
for (int head = 0; head < numHeads; head++)
{
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
std::cout << fmt::format("{}.{:2} ", head, sectorId);
for (int track = 0; track < numCylinders; track++)
{
const auto& sector = sectors.get(track, head, sectorId);
if (!sector)
{
std::cout << 'X';
missingSectors++;
}
else
{
switch (sector->status)
{
case Sector::OK:
std::cout << '.';
break;
case Sector::BAD_CHECKSUM:
std::cout << 'B';
badSectors++;
break;
case Sector::CONFLICT:
std::cout << 'C';
badSectors++;
break;
default:
std::cout << '?';
break;
}
}
totalSectors++;
}
std::cout << std::endl;
}
}
int goodSectors = totalSectors - missingSectors - badSectors;
if (totalSectors == 0)
std::cout << "No sectors in output; skipping analysis" << std::endl;
else
{
std::cout << "Good sectors: " << goodSectors << "/" << totalSectors
<< " (" << (100*goodSectors/totalSectors) << "%)"
<< std::endl;
std::cout << "Missing sectors: " << missingSectors << "/" << totalSectors
<< " (" << (100*missingSectors/totalSectors) << "%)"
<< std::endl;
std::cout << "Bad sectors: " << badSectors << "/" << totalSectors
<< " (" << (100*badSectors/totalSectors) << "%)"
<< std::endl;
}
}

33
lib/geometry/geometry.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef DGMAPPER_H
#define DGMAPPER_H
class Sector;
class SectorSet;
class GeometryProto;
class ImageReader;
class ImageWriter;
class DisassemblingGeometryMapper
{
public:
virtual const Sector* get(unsigned cylinder, unsigned head, unsigned sector) const = 0;
};
class AssemblingGeometryMapper
{
public:
virtual void put(const Sector& sector) const = 0;
public:
void put(const SectorSet& sectors);
void printMap(const SectorSet& sectors);
void writeCsv(const SectorSet& sectors, const std::string& filename);
};
extern std::unique_ptr<DisassemblingGeometryMapper> createSimpleDisassemblingGeometryMapper(
const GeometryProto& proto, ImageReader& reader);
extern std::unique_ptr<AssemblingGeometryMapper> createSimpleAssemblingGeometryMapper(
const GeometryProto& proto, ImageWriter& reader);
#endif

View File

@@ -0,0 +1,27 @@
syntax = "proto2";
import "lib/common.proto";
message GeometryProto {
enum BlockOrdering {
ORDER_CHS = 0 [(ehelp) = "head 0 interleaved with head 1"];
ORDER_HCS = 1 [(ehelp) = "all of head 0, then all of head 1"];
ORDER_NSI = 2 [(ehelp) = "all of head 0, then all of head 1 in reverse order"];
}
message TrackdataProto {
optional int32 cylinder = 1 [(help) = "if set, this entry applies only to this cylinder"];
optional int32 head = 2 [(help) = "if set, this entry applies only to this head"];
optional int32 sector_size = 4 [(help) = "number of bytes in each sector of this track"];
optional int32 sectors = 3 [(help) = "number of sectors on this track"];
}
optional int32 cylinders = 1 [(help) = "number of cylinders in the image"];
optional int32 heads = 2 [(help) = "number of sides in the image"];
repeated TrackdataProto trackdata = 3 [(help) = "descriptions of each track"];
optional bool fortytrack = 4 [(help) = "set if this is for a forty-track (double stepped) geometry"];
optional BlockOrdering block_ordering = 5 [(help) = "order of blocks in the image"];
}

View File

@@ -0,0 +1,203 @@
#include "globals.h"
#include "imagereader/imagereader.h"
#include "imagewriter/imagewriter.h"
#include "sectorset.h"
#include "sector.h"
#include "geometry.h"
#include "lib/geometry/geometry.pb.h"
#include "fmt/format.h"
static std::string nameOf(GeometryProto::BlockOrdering ordering)
{
switch (ordering)
{
case GeometryProto::ORDER_CHS: return "CHS";
case GeometryProto::ORDER_HCS: return "HCS";
default: return fmt::format("order({})", ordering);
}
}
struct Trackdata
{
GeometryProto::TrackdataProto format;
int offset;
};
static void parse_geometry(std::map<std::pair<int, int>, Trackdata>& offsets, const GeometryProto& config)
{
int offset = 0;
auto getTrackFormat = [&](GeometryProto::TrackdataProto& trackdata, unsigned cylinder, unsigned head)
{
trackdata.Clear();
for (const GeometryProto::TrackdataProto& f : config.trackdata())
{
if (f.has_cylinder() && (f.cylinder() != cylinder))
continue;
if (f.has_head() && (f.head() != head))
continue;
trackdata.MergeFrom(f);
}
};
auto addBlock = [&](int cylinder, int head) {
Trackdata& trackdata = offsets[std::make_pair(cylinder, head)];
trackdata.offset = offset;
getTrackFormat(trackdata.format, cylinder, head);
offset += trackdata.format.sectors() * trackdata.format.sector_size();
};
switch (config.block_ordering())
{
case GeometryProto::ORDER_CHS:
for (int cylinder = 0; cylinder < config.cylinders(); cylinder++)
for (int head = 0; head < config.heads(); head++)
addBlock(cylinder, head);
break;
case GeometryProto::ORDER_HCS:
for (int head = 0; head < config.heads(); head++)
for (int cylinder = 0; cylinder < config.cylinders(); cylinder++)
addBlock(cylinder, head);
break;
case GeometryProto::ORDER_NSI:
for (int cylinder = 0; cylinder < config.cylinders(); cylinder++)
addBlock(cylinder, 0);
if (config.heads() == 2)
for (int cylinder = config.cylinders()-1; cylinder >= 0; cylinder--)
addBlock(cylinder, 1);
break;
}
std::cout << fmt::format("GEOM: input {} image of {} cylinders, {} heads\n",
nameOf(config.block_ordering()), config.cylinders(), config.heads());
}
class BaseGeometryMapper
{
protected:
BaseGeometryMapper(const GeometryProto& config):
_config(config)
{}
protected:
void getOffsetOf(unsigned cylinder, unsigned head, unsigned sector, off_t& offset, unsigned& sector_size) const
{
const auto tit = _offsets.find(std::make_pair(cylinder, head));
if (tit == _offsets.end())
{
offset = -1;
return;
}
const Trackdata& trackdata = tit->second;
sector_size = trackdata.format.sector_size();
offset = trackdata.offset + sector*sector_size;
}
protected:
const GeometryProto& _config;
mutable std::map<std::pair<int, int>, Trackdata> _offsets;
};
class SimpleDisassemblingGeometryMapper : public BaseGeometryMapper, public DisassemblingGeometryMapper
{
public:
SimpleDisassemblingGeometryMapper(const GeometryProto& config, ImageReader& reader):
BaseGeometryMapper(config),
_reader(reader)
{
_proxy = reader.getGeometryMapper();
if (_proxy)
{
std::cout << "GEOM: geometry being overridden by input image\n";
return;
}
parse_geometry(_offsets, config);
}
const Sector* get(unsigned cylinder, unsigned head, unsigned sector) const
{
if (_proxy)
return _proxy->get(cylinder, head, sector);
std::unique_ptr<Sector>& sit = _sectors.get(cylinder, head, sector);
if (!sit)
{
sit.reset(new Sector);
sit->status = Sector::MISSING;
sit->physicalTrack = sit->logicalTrack = cylinder;
sit->physicalSide = sit->logicalSide = head;
sit->logicalSector = sector;
off_t offset;
unsigned sector_size;
getOffsetOf(cylinder, head, sector, offset, sector_size);
if (offset == -1)
return nullptr;
sit->data = _reader.getBlock(offset, sector_size);
}
return sit.get();
}
private:
const ImageReader& _reader;
const DisassemblingGeometryMapper* _proxy;
mutable SectorSet _sectors;
};
class SimpleAssemblingGeometryMapper : public BaseGeometryMapper, public AssemblingGeometryMapper
{
public:
SimpleAssemblingGeometryMapper(const GeometryProto& config, ImageWriter& writer):
BaseGeometryMapper(config),
_writer(writer)
{
_proxy = writer.getGeometryMapper();
if (_proxy)
{
std::cout << "GEOM: geometry mapping not used by output image\n";
return;
}
parse_geometry(_offsets, config);
}
void put(const Sector& data) const
{
if (_proxy)
{
_proxy->put(data);
return;
}
off_t offset;
unsigned sector_size;
getOffsetOf(data.logicalTrack, data.logicalSide, data.logicalSector, offset, sector_size);
if (offset == -1)
{
std::cout << fmt::format("GEOM: sector {}.{}.{} discarded\n",
data.logicalTrack, data.logicalSide, data.logicalSector);
return;
}
_writer.putBlock(offset, sector_size, data.data);
}
private:
ImageWriter& _writer;
const AssemblingGeometryMapper* _proxy;
};
std::unique_ptr<DisassemblingGeometryMapper> createSimpleDisassemblingGeometryMapper(
const GeometryProto& config, ImageReader& reader)
{
return std::unique_ptr<DisassemblingGeometryMapper>(new SimpleDisassemblingGeometryMapper(config, reader));
}
std::unique_ptr<AssemblingGeometryMapper> createSimpleAssemblingGeometryMapper(
const GeometryProto& config, ImageWriter& writer)
{
return std::unique_ptr<AssemblingGeometryMapper>(new SimpleAssemblingGeometryMapper(config, writer));
}

View File

@@ -1,98 +0,0 @@
#include "globals.h"
#include "flags.h"
#include "sector.h"
#include "sectorset.h"
#include "imagereader/imagereader.h"
#include "fmt/format.h"
#include "proto.h"
#include <algorithm>
#include <iostream>
#include <fstream>
class D64ImageReader : public ImageReader
{
public:
D64ImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
SectorSet readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
inputFile.seekg(0, inputFile.beg);
uint32_t begin = inputFile.tellg();
inputFile.seekg(0, inputFile.end);
uint32_t end = inputFile.tellg();
uint32_t inputFileSize = (end-begin);
inputFile.seekg(0, inputFile.beg);
Bytes data;
data.writer() += inputFile;
ByteReader br(data);
unsigned numCylinders = 39;
unsigned numHeads = 1;
unsigned numSectors = 0;
std::cout << "reading D64 image\n"
<< fmt::format("{} cylinders, {} heads\n",
numCylinders, numHeads);
uint32_t offset = 0;
auto sectorsPerTrack = [&](int track) -> int
{
if (track < 17)
return 21;
if (track < 24)
return 19;
if (track < 30)
return 18;
return 17;
};
SectorSet sectors;
for (int track = 0; track < 40; track++)
{
int numSectors = sectorsPerTrack(track);
for (int head = 0; head < numHeads; head++)
{
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
if ((offset < inputFileSize))
{ //still data available sector OK
br.seek(offset);
Bytes payload = br.read(256);
offset += 256;
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
sector.reset(new Sector);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalTrack = track;
sector->logicalSide = sector->physicalSide = head;
sector->logicalSector = sectorId;
sector->data.writer().append(payload);
} else
{ //no more data in input file. Write sectors with status: DATA_MISSING
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
sector.reset(new Sector);
sector->status = Sector::DATA_MISSING;
sector->logicalTrack = sector->physicalTrack = track;
sector->logicalSide = sector->physicalSide = head;
sector->logicalSector = sectorId;
}
}
}
}
return sectors;
}
};
std::unique_ptr<ImageReader> ImageReader::createD64ImageReader(const ImageReaderProto& config)
{
return std::unique_ptr<ImageReader>(new D64ImageReader(config));
}

View File

@@ -9,114 +9,64 @@
#include <iostream>
#include <fstream>
#define SECTOR_SIZE 512
#define TAG_SIZE 12
class DiskCopyImageReader : public ImageReader
{
public:
DiskCopyImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
SectorSet readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
_if.open(_config.filename(), std::ios::in | std::ios::binary);
if (!_if.is_open())
Error() << "cannot open input file";
Bytes data;
data.writer() += inputFile;
ByteReader br(data);
Bytes header(_if, 0x54);
ByteReader br(header);
br.seek(1);
std::string label = br.read(data[0]);
uint8_t labelLen = br.read_8();
std::string label = br.read(labelLen);
br.seek(0x40);
uint32_t dataSize = br.read_be32();
uint32_t tagSize = br.read_be32();
_numSectors = dataSize / SECTOR_SIZE;
_sectorOffset = 0x54;
_tagOffset = _sectorOffset + dataSize;
br.seek(0x50);
uint8_t encoding = br.read_8();
uint8_t formatByte = br.read_8();
_if.seekg(0, std::ios::end);
std::cout << fmt::format("DISKCOPY: reading input image '{}' of {} sectors",
label, _numSectors);
}
unsigned numCylinders = 80;
unsigned numHeads = 2;
unsigned numSectors = 0;
bool mfm = false;
Bytes getBlock(size_t offset, size_t length) const
{
if ((length != SECTOR_SIZE) && (length != (SECTOR_SIZE+TAG_SIZE)))
Error() << fmt::format("diskcopy files only support sector lengths of {} and {}\n",
SECTOR_SIZE, (SECTOR_SIZE+TAG_SIZE));
switch (encoding)
unsigned sectorNum = offset / length;
if (offset % length)
Error() << fmt::format("unaligned sector read");
_if.seekg(_sectorOffset + SECTOR_SIZE*sectorNum);
Bytes data(_if, SECTOR_SIZE);
if (length != SECTOR_SIZE)
{
case 0: /* GCR CLV 400kB */
numHeads = 1;
break;
case 1: /* GCR CLV 800kB */
break;
case 2: /* MFM CAV 720kB */
numSectors = 9;
mfm = true;
break;
case 3: /* MFM CAV 1440kB */
numSectors = 18;
mfm = true;
break;
default:
Error() << fmt::format("don't understand DiskCopy disks of type {}", encoding);
_if.seekg(_tagOffset + TAG_SIZE*sectorNum);
data.writer().seekToEnd().append(_if, TAG_SIZE);
}
std::cout << "reading DiskCopy 4.2 image\n"
<< fmt::format("{} cylinders, {} heads; {}; {}\n",
numCylinders, numHeads,
mfm ? "MFM" : "GCR",
label);
auto sectorsPerTrack = [&](int track) -> int
{
if (mfm)
return numSectors;
if (track < 16)
return 12;
if (track < 32)
return 11;
if (track < 48)
return 10;
if (track < 64)
return 9;
return 8;
};
uint32_t dataPtr = 0x54;
uint32_t tagPtr = dataPtr + dataSize;
SectorSet sectors;
for (int track = 0; track < numCylinders; track++)
{
int numSectors = sectorsPerTrack(track);
for (int head = 0; head < numHeads; head++)
{
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
br.seek(dataPtr);
Bytes payload = br.read(512);
dataPtr += 512;
br.seek(tagPtr);
Bytes tag = br.read(12);
tagPtr += 12;
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
sector.reset(new Sector);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalTrack = track;
sector->logicalSide = sector->physicalSide = head;
sector->logicalSector = sectorId;
sector->data.writer().append(payload).append(tag);
}
}
}
return sectors;
return data;
}
private:
mutable std::ifstream _if;
unsigned _numSectors;
off_t _sectorOffset;
off_t _tagOffset;
};
std::unique_ptr<ImageReader> ImageReader::createDiskCopyImageReader(

View File

@@ -26,12 +26,6 @@ std::unique_ptr<ImageReader> ImageReader::create(const ImageReaderProto& config)
case ImageReaderProto::kJv3:
return ImageReader::createJv3ImageReader(config);
case ImageReaderProto::kD64:
return ImageReader::createD64ImageReader(config);
case ImageReaderProto::kNsi:
return ImageReader::createNsiImageReader(config);
default:
Error() << "bad input file config";
return std::unique_ptr<ImageReader>();
@@ -44,12 +38,13 @@ void ImageReader::updateConfigForFilename(ImageReaderProto* proto, const std::st
{
{".adf", [&]() { proto->mutable_img(); }},
{".jv3", [&]() { proto->mutable_jv3(); }},
{".d64", [&]() { proto->mutable_d64(); }},
{".d64", [&]() { proto->mutable_img(); }},
{".d81", [&]() { proto->mutable_img(); }},
{".diskcopy", [&]() { proto->mutable_diskcopy(); }},
{".img", [&]() { proto->mutable_img(); }},
{".st", [&]() { proto->mutable_img(); }},
{".nsi", [&]() { proto->mutable_nsi(); }},
{".nsi", [&]() { proto->mutable_img(); }},
{".imd", [&]() { proto->mutable_imd(); }},
};
for (const auto& it : formats)

View File

@@ -4,6 +4,7 @@
class SectorSet;
class ImageSpec;
class ImageReaderProto;
class DisassemblingGeometryMapper;
class ImageReader
{
@@ -16,15 +17,14 @@ public:
static void updateConfigForFilename(ImageReaderProto* proto, const std::string& filename);
public:
static std::unique_ptr<ImageReader> createD64ImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createDiskCopyImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createImgImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createJv3ImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createIMDImageReader(const ImageReaderProto& config);
static std::unique_ptr<ImageReader> createNsiImageReader(const ImageReaderProto& config);
public:
virtual SectorSet readImage() = 0;
virtual Bytes getBlock(size_t offset, size_t length) const = 0;
virtual const DisassemblingGeometryMapper* getGeometryMapper() const { return nullptr; }
protected:
const ImageReaderProto& _config;

View File

@@ -22,7 +22,6 @@ message DiskCopyInputProto {}
message ImdInputProto {}
message Jv3InputProto {}
message D64InputProto {}
message NsiInputProto {}
message ImageReaderProto {
optional string filename = 1 [(help) = "filename of input sector image"];
@@ -32,7 +31,6 @@ message ImageReaderProto {
ImdInputProto imd = 4;
Jv3InputProto jv3 = 5;
D64InputProto d64 = 6;
NsiInputProto nsi = 7;
}
}

View File

@@ -3,6 +3,7 @@
#include "sector.h"
#include "sectorset.h"
#include "imagereader/imagereader.h"
#include "geometry/geometry.h"
#include "lib/config.pb.h"
#include "fmt/format.h"
#include <algorithm>
@@ -50,16 +51,16 @@ static unsigned getModulationandSpeed(uint8_t flags, bool *mfm)
}
}
struct TrackHeader
struct Trackheader
{
uint8_t ModeValue;
uint8_t modeValue;
uint8_t track;
uint8_t Head;
uint8_t head;
uint8_t numSectors;
uint8_t SectorSize;
uint8_t sectorSize;
};
static unsigned getSectorSize(uint8_t flags)
static unsigned getsectorSize(uint8_t flags)
{
switch (flags)
{
@@ -81,178 +82,157 @@ static unsigned getSectorSize(uint8_t flags)
#define END_OF_FILE 0x1A
class IMDImageReader : public ImageReader
class IMDImageReader : public ImageReader, DisassemblingGeometryMapper
{
public:
IMDImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
SectorSet readImage()
/*
IMAGE FILE FORMAT
The overall layout of an ImageDisk .IMD image file is:
IMD v.vv: dd/mm/yyyy hh:mm:ss
Comment (ASCII only - unlimited size)
1A byte - ASCII EOF character
- For each track on the disk:
1 byte Mode value see getModulationspeed for definition
1 byte Cylinder
1 byte Head
1 byte number of sectors in track
1 byte sector size see getsectorsize for definition
sector numbering map
sector cylinder map (optional) definied in high byte of head (since head is 0 or 1)
sector head map (optional) definied in high byte of head (since head is 0 or 1)
sector data records
<End of file>
*/
{
//Read File
/*
* IMAGE FILE FORMAT
* The overall layout of an ImageDisk .IMD image file is:
* IMD v.vv: dd/mm/yyyy hh:mm:ss
* Comment (ASCII only - unlimited size)
* 1A byte - ASCII EOF character
* - For each track on the disk:
* 1 byte Mode value see getModulationspeed for definition
* 1 byte Cylinder
* 1 byte head
* 1 byte number of sectors in track
* 1 byte sector size see getsectorsize for definition
* sector numbering map
* sector cylinder map (optional) definied in high byte of head (since head is 0 or 1)
* sector head map (optional) definied in high byte of head (since head is 0 or 1)
* sector data records
* <End of file>
*/
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
Bytes data;
data.writer().append(inputFile);
ByteReader br(data);
//define some variables
bool mfm = false; //define coding just to show in comment for setting the right write parameters
inputFile.seekg(0, inputFile.end);
int inputFileSize = inputFile.tellg(); // determine filesize
inputFile.seekg(0, inputFile.beg);
Bytes data;
data.writer() += inputFile;
ByteReader br(data);
SectorSet sectors;
TrackHeader header = {0, 0, 0, 0, 0};
Trackheader header = {0, 0, 0, 0, 0};
unsigned n = 0;
unsigned headerPtr = 0;
unsigned Modulation_Speed = 0;
unsigned modulation_speed = 0;
unsigned sectorSize = 0;
std::string sector_skew;
int b;
unsigned char comment[8192]; //i choose a fixed value. dont know how to make dynamic arrays in C++. This should be enough
// Read comment
while ((b = br.read_8()) != EOF && b != 0x1A)
std::stringstream comment;
while (!br.eof())
{
comment[n++] = (unsigned char)b;
}
headerPtr = n; //set pointer to after comment
comment[n] = '\0'; // null-terminate the string
//write comment to screen
std::cout << "Comment in IMD image:\n"
<< fmt::format("{}\n",
comment);
//first read header
for (;;)
{
if (headerPtr >= inputFileSize-1)
{
int b = br.read_8();
if (b == 0x1a)
break;
}
header.ModeValue = br.read_8();
headerPtr++;
Modulation_Speed = getModulationandSpeed(header.ModeValue, &mfm);
if (b == '\r')
continue;
if (b == '\n')
b = 32;
comment << (char) b;
}
std::cout << fmt::format("IMD: comment: {}\n", comment.str());
while (!br.eof())
{
header.modeValue = br.read_8();
modulation_speed = getModulationandSpeed(header.modeValue, &mfm);
header.track = br.read_8();
headerPtr++;
header.Head = br.read_8();
headerPtr++;
header.head = br.read_8();
int physicalHead = header.head & 0x3f;
header.numSectors = br.read_8();
headerPtr++;
header.SectorSize = br.read_8();
headerPtr++;
sectorSize = getSectorSize(header.SectorSize);
header.sectorSize = br.read_8();
sectorSize = getsectorSize(header.sectorSize);
//Read optional cylinder map To Do
/* Read optional cylinder map */
//Read optional sector head map To Do
if (header.head & 0x80)
Error() << "cylinder map";
/* Read optional sector head map */
if (header.head & 0x40)
Error() << "sector head map";
//read sector numbering map
unsigned int sector_map[header.numSectors];
bool blnBaseOne = false;
sector_skew.clear();
for (b = 0; b < header.numSectors; b++)
for (int b = 0; b < header.numSectors; b++)
{
sector_map[b] = br.read_8();
sector_skew.push_back(sector_map[b] + '0');
if (b == 0) //first sector see if base is 0 or 1 Fluxengine wants 0
{
if (sector_map[b]==1)
{
blnBaseOne = true;
}
}
if (blnBaseOne==true)
{
sector_map[b] = (sector_map[b]-1);
if (sector_map[b]==1)
blnBaseOne = true;
}
headerPtr++;
if (blnBaseOne==true)
sector_map[b] = (sector_map[b]-1);
}
//read the sectors
for (int s = 0; s < header.numSectors; s++)
{
Bytes sectordata;
std::unique_ptr<Sector>& sector = sectors.get(header.track, header.Head, sector_map[s]);
std::unique_ptr<Sector>& sector = _sectors.get(header.track, physicalHead, sector_map[s]);
sector.reset(new Sector);
//read the status of the sector
unsigned int Status_Sector = br.read_8();
headerPtr++;
unsigned int sector_status = br.read_8();
switch (Status_Sector)
switch (sector_status)
{
case 0: /* Sector data unavailable - could not be read */
break;
case 1: /* Normal data: (Sector Size) bytes follow */
sectordata = br.read(sectorSize);
headerPtr += sectorSize;
sector->data.writer().append(sectordata);
{
Bytes sectordata = br.read(sectorSize);
sector->data = sectordata;
break;
}
case 2: /* Compressed: All bytes in sector have same value (xx) */
sectordata = br.read(1);
headerPtr++;
{
uint8_t sectordata = br.read_8();
sector->data.writer().append(sectordata);
ByteWriter bw(sector->data);
for (int k = 1; k < sectorSize; k++)
{
//fill data till sector is full
sector->data.writer().append(sectordata);
bw.write_8(sectordata);
}
break;
}
case 3: /* Normal data with "Deleted-Data address mark" */
break;
case 4: /* Compressed with "Deleted-Data address mark"*/
break;
case 5: /* Normal data read with data error */
break;
case 6: /* Compressed read with data error" */
break;
case 7: /* Deleted data read with data error" */
break;
case 8: /* Compressed, Deleted read with data error" */
break;
default:
Error() << fmt::format("don't understand IMD disks with sector status {}", Status_Sector);
Error() << fmt::format("don't understand IMD disks with sector status {}", sector_status);
}
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalTrack = header.track;
sector->logicalSide = sector->physicalSide = header.Head;
sector->logicalSide = sector->physicalSide = physicalHead;
sector->logicalSector = (sector_map[s]);
}
@@ -260,16 +240,28 @@ public:
//Write format detected in IMD image to screen to help user set the right write parameters
size_t headSize = header.numSectors * sectorSize;
size_t trackSize = headSize * (header.Head + 1);
size_t trackSize = headSize * (header.head + 1);
std::cout << "reading IMD image\n"
<< fmt::format("{} tracks, {} heads; {}; {} kbps; {} sectoren; sectorsize {}; sectormap {}; {} kB total \n",
header.track, header.Head + 1,
mfm ? "MFM" : "FM",
Modulation_Speed, header.numSectors, sectorSize, sector_skew, (header.track+1) * trackSize / 1024);
std::cout << fmt::format("IMD: reading image with {} tracks, {} heads, {};\n"
"IMD: {} kbps; {} sectors; sectorsize {}; sectormap {}; {} kB total\n",
header.track, header.head + 1,
mfm ? "MFM" : "FM",
modulation_speed, header.numSectors, sectorSize, sector_skew, (header.track+1) * trackSize / 1024);
return sectors;
}
}
Bytes getBlock(size_t offset, size_t length) const
{
throw "unimplemented";
}
const Sector* get(unsigned cylinder, unsigned head, unsigned sector) const
{
return _sectors.get(cylinder, head, sector);
}
private:
SectorSet _sectors;
};
std::unique_ptr<ImageReader> ImageReader::createIMDImageReader(

View File

@@ -14,62 +14,24 @@ class ImgImageReader : public ImageReader
public:
ImgImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
SectorSet readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
_if.open(_config.filename(), std::ios::in | std::ios::binary);
if (!_if.is_open())
Error() << "cannot open input file";
SectorSet sectors;
for (int track = 0; track < _config.img().tracks(); track++)
{
for (int side = 0; side < _config.img().sides(); side++)
{
ImgInputOutputProto::TrackdataProto trackdata;
getTrackFormat(trackdata, track, side);
_if.seekg(0, std::ios::end);
std::cout << fmt::format("IMG: reading input image of {} kB total\n",
_if.tellg() / 1024);
}
for (int sectorId = 0; sectorId < trackdata.sectors(); sectorId++)
{
Bytes data(trackdata.sector_size());
inputFile.read((char*) data.begin(), data.size());
std::unique_ptr<Sector>& sector = sectors.get(track, side, sectorId);
sector.reset(new Sector);
sector->status = Sector::OK;
sector->logicalTrack = track;
sector->physicalTrack = track * _config.img().physical_step() + _config.img().physical_offset();
sector->logicalSide = sector->physicalSide = side;
sector->logicalSector = sectorId;
sector->data = data;
}
}
if (inputFile.eof())
break;
}
std::cout << fmt::format("reading {} tracks, {} sides, {} kB total\n",
_config.img().tracks(), _config.img().sides(),
inputFile.tellg() / 1024);
return sectors;
Bytes getBlock(size_t offset, size_t length) const
{
_if.seekg(offset);
return Bytes(_if, length);
}
private:
void getTrackFormat(ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side)
{
trackdata.Clear();
for (const ImgInputOutputProto::TrackdataProto& f : _config.img().trackdata())
{
if (f.has_track() && (f.track() != track))
continue;
if (f.has_side() && (f.side() != side))
continue;
trackdata.MergeFrom(f);
}
}
mutable std::ifstream _if;
};
std::unique_ptr<ImageReader> ImageReader::createImgImageReader(

View File

@@ -3,8 +3,10 @@
#include "sector.h"
#include "sectorset.h"
#include "imagereader/imagereader.h"
#include "geometry/geometry.h"
#include "fmt/format.h"
#include "lib/config.pb.h"
#include "lib/imagereader/imagereader.pb.h"
#include <algorithm>
#include <iostream>
#include <fstream>
@@ -31,13 +33,6 @@
* } SectorHeader;
*/
struct SectorHeader
{
uint8_t track;
uint8_t sector;
uint8_t flags;
};
#define JV3_DENSITY 0x80 /* 1=dden, 0=sden */
#define JV3_DAM 0x60 /* data address mark code; see below */
#define JV3_SIDE 0x10 /* 0=side 0, 1=side 1 */
@@ -72,55 +67,51 @@ static unsigned getSectorSize(uint8_t flags)
}
}
Error() << "not reachable";
throw 0;
}
class Jv3ImageReader : public ImageReader
class Jv3ImageReader : public ImageReader, DisassemblingGeometryMapper
{
public:
Jv3ImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
SectorSet readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
std::ifstream stream(_config.filename(), std::ios::in | std::ios::binary);
if (!stream.is_open())
Error() << "cannot open input file";
inputFile.seekg( 0, std::ios::end);
unsigned inputFileSize = inputFile.tellg();
unsigned headerPtr = 0;
SectorSet sectors;
Bytes image;
image.writer() += stream;
ByteReader br(image);
off_t headerPtr = 0;
for (;;)
{
unsigned dataPtr = headerPtr + 2901*3 + 1;
if (dataPtr >= inputFileSize)
off_t dataPtr = headerPtr + 2901*3 + 1;
if (dataPtr >= image.size())
break;
br.seek(headerPtr);
for (unsigned i=0; i<2901; i++)
{
SectorHeader header = {0, 0, 0xff};
inputFile.seekg(headerPtr);
inputFile.read((char*) &header, 3);
unsigned sectorSize = getSectorSize(header.flags);
if ((header.flags & JV3_FREEF) != JV3_FREEF)
uint8_t track = br.read_8();
uint8_t sector = br.read_8();
uint8_t flags = br.read_8();
if ((flags & JV3_FREEF) != JV3_FREEF)
{
Bytes data(sectorSize);
inputFile.seekg(dataPtr);
inputFile.read((char*) data.begin(), sectorSize);
unsigned side = !!(flags & JV3_SIDE);
unsigned length = getSectorSize(flags);
unsigned head = !!(header.flags & JV3_SIDE);
std::unique_ptr<Sector>& sector = sectors.get(header.track, head, header.sector);
sector.reset(new Sector);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalTrack = header.track;
sector->logicalSide = sector->physicalSide = head;
sector->logicalSector = header.sector;
sector->data = data;
std::unique_ptr<Sector> s(new Sector);
s->status = (flags & JV3_ERROR) ? Sector::BAD_CHECKSUM : Sector::OK;
s->physicalTrack = s->logicalTrack = track;
s->physicalSide = s->logicalSide = side;
s->logicalSector = sector;
s->data = image.slice(dataPtr, length);
_sectors[std::make_tuple(track, side, sector)] = std::move(s);
dataPtr += length;
}
headerPtr += 3;
dataPtr += sectorSize;
}
/* dataPtr is now pointing at the beginning of the next chunk. */
@@ -128,8 +119,31 @@ public:
headerPtr = dataPtr;
}
return sectors;
std::cout << fmt::format("JV3: reading input image of {} sectors total\n",
_sectors.size());
}
const DisassemblingGeometryMapper* getGeometryMapper() const
{
return this;
}
Bytes getBlock(size_t offset, size_t length) const
{
throw "unimplemented";
}
const Sector* get(unsigned cylinder, unsigned head, unsigned sector) const
{
auto sit = _sectors.find(std::make_tuple(cylinder, head, sector));
if (sit == _sectors.end())
return nullptr;
return sit->second.get();
}
private:
std::map<std::tuple<int, int, int>, std::unique_ptr<Sector>> _sectors;
};
std::unique_ptr<ImageReader> ImageReader::createJv3ImageReader(const ImageReaderProto& config)

View File

@@ -1,109 +0,0 @@
/* Image reader for Northstar floppy disk images */
#include "globals.h"
#include "flags.h"
#include "sector.h"
#include "sectorset.h"
#include "imagereader/imagereader.h"
#include "fmt/format.h"
#include "lib/imagereader/imagereader.pb.h"
#include <algorithm>
#include <iostream>
#include <fstream>
class NsiImageReader : public ImageReader
{
public:
NsiImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
SectorSet readImage()
{
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
const auto begin = inputFile.tellg();
inputFile.seekg(0, std::ios::end);
const auto end = inputFile.tellg();
const auto fsize = (end - begin);
std::cout << "NSI: Autodetecting geometry based on file size: " << fsize << std::endl;
int numCylinders = 35;
int numSectors = 10;
int numHeads = 2;
int sectorSize = 512;
switch (fsize) {
case 358400:
numHeads = 2;
sectorSize = 512;
break;
case 179200:
numHeads = 1;
sectorSize = 512;
break;
case 89600:
numHeads = 1;
sectorSize = 256;
break;
default:
Error() << "NSI: unknown file size";
}
size_t trackSize = numSectors * sectorSize;
std::cout << fmt::format("reading {} tracks, {} heads, {} sectors, {} bytes per sector, {} kB total",
numCylinders, numHeads,
numSectors, sectorSize,
numCylinders * numHeads * trackSize / 1024)
<< std::endl;
SectorSet sectors;
unsigned sectorFileOffset;
for (int head = 0; head < numHeads; head++)
{
for (int track = 0; track < numCylinders; track++)
{
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
if (head == 0) { /* Head 0 is from track 0-34 */
sectorFileOffset = track * trackSize + sectorId * sectorSize;
}
else { /* Head 1 is from track 70-35 */
sectorFileOffset = (trackSize * numCylinders) + /* Skip over side 0 */
((numCylinders - track - 1) * trackSize) +
(sectorId * sectorSize); /* Sector offset from beginning of track. */
}
inputFile.seekg(sectorFileOffset, std::ios::beg);
Bytes data(sectorSize);
inputFile.read((char*) data.begin(), sectorSize);
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
sector.reset(new Sector);
sector->status = Sector::OK;
sector->logicalTrack = sector->physicalTrack = track;
sector->logicalSide = sector->physicalSide = head;
sector->logicalSector = sectorId;
sector->data = data;
}
}
}
return sectors;
}
};
std::unique_ptr<ImageReader> ImageReader::createNsiImageReader(
const ImageReaderProto& config)
{
return std::unique_ptr<ImageReader>(new NsiImageReader(config));
}

View File

@@ -1,62 +0,0 @@
#include "globals.h"
#include "flags.h"
#include "sector.h"
#include "sectorset.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "ldbs.h"
#include "lib/config.pb.h"
#include <algorithm>
#include <iostream>
#include <fstream>
static int sectors_per_track(int track)
{
if (track < 17)
return 21;
if (track < 24)
return 19;
if (track < 30)
return 18;
return 17;
}
class D64ImageWriter : public ImageWriter
{
public:
D64ImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
void writeImage(const SectorSet& sectors)
{
std::cout << "writing D64 triangular image\n";
std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "cannot open output file";
uint32_t offset = 0;
for (int track = 0; track < 40; track++)
{
int sectorCount = sectors_per_track(track);
for (int sectorId = 0; sectorId < sectorCount; sectorId++)
{
const auto& sector = sectors.get(track, 0, sectorId);
if (sector)
{
outputFile.seekp(offset);
outputFile.write((const char*) sector->data.cbegin(), 256);
}
offset += 256;
}
}
}
};
std::unique_ptr<ImageWriter> ImageWriter::createD64ImageWriter(const ImageWriterProto& config)
{
return std::unique_ptr<ImageWriter>(new D64ImageWriter(config));
}

View File

@@ -166,6 +166,9 @@ public:
image.writeToFile(_config.filename());
}
void putBlock(size_t offset, size_t length, const Bytes& data)
{ throw "unimplemented"; }
};
std::unique_ptr<ImageWriter> ImageWriter::createDiskCopyImageWriter(

View File

@@ -17,17 +17,13 @@ std::unique_ptr<ImageWriter> ImageWriter::create(const ImageWriterProto& config)
case ImageWriterProto::kImg:
return ImageWriter::createImgImageWriter(config);
case ImageWriterProto::kD64:
return ImageWriter::createD64ImageWriter(config);
case ImageWriterProto::kLdbs:
return ImageWriter::createLDBSImageWriter(config);
#if 0
case ImageWriterProto::kDiskcopy:
return ImageWriter::createDiskCopyImageWriter(config);
case ImageWriterProto::kNsi:
return ImageWriter::createNsiImageWriter(config);
#endif
default:
Error() << "bad output image config";
@@ -40,13 +36,13 @@ void ImageWriter::updateConfigForFilename(ImageWriterProto* proto, const std::st
static const std::map<std::string, std::function<void(void)>> formats =
{
{".adf", [&]() { proto->mutable_img(); }},
{".d64", [&]() { proto->mutable_d64(); }},
{".d64", [&]() { proto->mutable_img(); }},
{".d81", [&]() { proto->mutable_img(); }},
{".diskcopy", [&]() { proto->mutable_diskcopy(); }},
{".img", [&]() { proto->mutable_img(); }},
{".ldbs", [&]() { proto->mutable_ldbs(); }},
{".st", [&]() { proto->mutable_img(); }},
{".nsi", [&]() { proto->mutable_nsi(); }},
{".nsi", [&]() { proto->mutable_img(); }},
};
for (const auto& it : formats)
@@ -66,125 +62,3 @@ ImageWriter::ImageWriter(const ImageWriterProto& config):
_config(config)
{}
void ImageWriter::writeCsv(const SectorSet& sectors, const std::string& filename)
{
std::ofstream f(filename, std::ios::out);
if (!f.is_open())
Error() << "cannot open CSV report file";
f << "\"Physical track\","
"\"Physical side\","
"\"Logical track\","
"\"Logical side\","
"\"Logical sector\","
"\"Clock (ns)\","
"\"Header start (ns)\","
"\"Header end (ns)\","
"\"Data start (ns)\","
"\"Data end (ns)\","
"\"Raw data address (bytes)\","
"\"User payload length (bytes)\","
"\"Status\""
"\n";
for (const auto& it : sectors.get())
{
const auto& sector = it.second;
f << fmt::format("{},{},{},{},{},{},{},{},{},{},{},{},{}\n",
sector->physicalTrack,
sector->physicalSide,
sector->logicalTrack,
sector->logicalSide,
sector->logicalSector,
sector->clock,
sector->headerStartTime,
sector->headerEndTime,
sector->dataStartTime,
sector->dataEndTime,
sector->position.bytes,
sector->data.size(),
Sector::statusToString(sector->status)
);
}
}
void ImageWriter::printMap(const SectorSet& sectors)
{
unsigned numCylinders;
unsigned numHeads;
unsigned numSectors;
unsigned numBytes;
sectors.calculateSize(numCylinders, numHeads, numSectors, numBytes);
int badSectors = 0;
int missingSectors = 0;
int totalSectors = 0;
std::cout << " Tracks -> 1 2 3 ";
if (numCylinders > 40) {
std::cout << "4 5 6 7 8";
}
std::cout << std::endl;
std::cout << "H.SS 0123456789012345678901234567890123456789";
if (numCylinders > 40) {
std::cout << "01234567890123456789012345678901234567890123";
}
std::cout << std::endl;
for (int head = 0; head < numHeads; head++)
{
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
std::cout << fmt::format("{}.{:2} ", head, sectorId);
for (int track = 0; track < numCylinders; track++)
{
const auto& sector = sectors.get(track, head, sectorId);
if (!sector)
{
std::cout << 'X';
missingSectors++;
}
else
{
switch (sector->status)
{
case Sector::OK:
std::cout << '.';
break;
case Sector::BAD_CHECKSUM:
std::cout << 'B';
badSectors++;
break;
case Sector::CONFLICT:
std::cout << 'C';
badSectors++;
break;
default:
std::cout << '?';
break;
}
}
totalSectors++;
}
std::cout << std::endl;
}
}
int goodSectors = totalSectors - missingSectors - badSectors;
if (totalSectors == 0)
std::cout << "No sectors in output; skipping analysis" << std::endl;
else
{
std::cout << "Good sectors: " << goodSectors << "/" << totalSectors
<< " (" << (100*goodSectors/totalSectors) << "%)"
<< std::endl;
std::cout << "Missing sectors: " << missingSectors << "/" << totalSectors
<< " (" << (100*missingSectors/totalSectors) << "%)"
<< std::endl;
std::cout << "Bad sectors: " << badSectors << "/" << totalSectors
<< " (" << (100*badSectors/totalSectors) << "%)"
<< std::endl;
}
}

View File

@@ -3,6 +3,7 @@
class SectorSet;
class ImageWriterProto;
class AssemblingGeometryMapper;
class ImageWriter
{
@@ -18,17 +19,14 @@ public:
const ImageWriterProto& config);
static std::unique_ptr<ImageWriter> createLDBSImageWriter(
const ImageWriterProto& config);
static std::unique_ptr<ImageWriter> createD64ImageWriter(
const ImageWriterProto& config);
static std::unique_ptr<ImageWriter> createDiskCopyImageWriter(
const ImageWriterProto& config);
static std::unique_ptr<ImageWriter> createNsiImageWriter(
const ImageWriterProto& config);
public:
void printMap(const SectorSet& sectors);
void writeCsv(const SectorSet& sectors, const std::string& filename);
virtual void writeImage(const SectorSet& sectors) = 0;
virtual void putBlock(size_t offset, size_t length, const Bytes& data) = 0;
virtual const AssemblingGeometryMapper* getGeometryMapper() const { return nullptr; }
protected:
const ImageWriterProto& _config;

View File

@@ -3,19 +3,15 @@ syntax = "proto2";
import "lib/imagereader/imagereader.proto";
import "lib/common.proto";
message D64OutputProto {}
message LDBSOutputProto {}
message DiskCopyOutputProto {}
message NsiOutputProto {}
message ImageWriterProto {
optional string filename = 1 [(help) = "filename of output sector image"];
oneof format {
ImgInputOutputProto img = 2;
D64OutputProto d64 = 3;
LDBSOutputProto ldbs = 4;
DiskCopyOutputProto diskcopy = 5;
NsiOutputProto nsi = 6;
}
}

View File

@@ -14,63 +14,27 @@ class ImgImageWriter : public ImageWriter
public:
ImgImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
void writeImage(const SectorSet& sectors)
{
unsigned autoTracks;
unsigned autoSides;
unsigned autoSectors;
unsigned autoBytes;
sectors.calculateSize(autoTracks, autoSides, autoSectors, autoBytes);
_of.open(_config.filename(), std::ios::out | std::ios::trunc | std::ios::binary);
if (!_of.is_open())
Error() << "cannot open input file";
}
int tracks = _config.img().has_tracks() ? _config.img().tracks() : autoTracks;
int sides = _config.img().has_sides() ? _config.img().sides() : autoSides;
~ImgImageWriter()
{
_of.seekp(0, std::ios::end);
std::cout << fmt::format("IMG: written output image of {} kB total\n",
_of.tellp() / 1024);
}
std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "cannot open output file";
for (int track = 0; track < tracks; track++)
{
for (int side = 0; side < sides; side++)
{
ImgInputOutputProto::TrackdataProto trackdata;
getTrackFormat(trackdata, track, side);
int numSectors = trackdata.has_sectors() ? trackdata.sectors() : autoSectors;
int sectorSize = trackdata.has_sector_size() ? trackdata.sector_size() : autoBytes;
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = sectors.get(track, side, sectorId);
if (sector)
sector->data.slice(0, sectorSize).writeTo(outputFile);
else
outputFile.seekp(sectorSize, std::ios::cur);
}
}
}
std::cout << fmt::format("wrote {} tracks, {} sides, {} kB total\n",
tracks, sides,
outputFile.tellp() / 1024);
void putBlock(size_t offset, size_t length, const Bytes& data)
{
_of.seekp(offset);
data.slice(0, length).writeTo(_of);
}
private:
void getTrackFormat(ImgInputOutputProto::TrackdataProto& trackdata, unsigned track, unsigned side)
{
trackdata.Clear();
for (const ImgInputOutputProto::TrackdataProto& f : _config.img().trackdata())
{
if (f.has_track() && (f.track() != track))
continue;
if (f.has_side() && (f.side() != side))
continue;
trackdata.MergeFrom(f);
}
}
std::ofstream _of;
};
std::unique_ptr<ImageWriter> ImageWriter::createImgImageWriter(

View File

@@ -3,6 +3,7 @@
#include "sector.h"
#include "sectorset.h"
#include "imagewriter/imagewriter.h"
#include "geometry/geometry.h"
#include "fmt/format.h"
#include "ldbs.h"
#include "lib/config.pb.h"
@@ -10,14 +11,15 @@
#include <iostream>
#include <fstream>
class LDBSImageWriter : public ImageWriter
class LDBSImageWriter : public ImageWriter, public AssemblingGeometryMapper
{
public:
LDBSImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
{
}
void writeImage(const SectorSet& sectors)
~LDBSImageWriter()
{
LDBS ldbs;
@@ -25,13 +27,29 @@ public:
unsigned numHeads;
unsigned numSectors;
unsigned numBytes;
sectors.calculateSize(numCylinders, numHeads, numSectors, numBytes);
_sectors.calculateSize(numCylinders, numHeads, numSectors, numBytes);
std::cout << fmt::format("writing {} tracks, {} heads, {} sectors, {} bytes per sector",
std::cout << fmt::format("LDBS: writing {} tracks, {} heads, {} sectors, {} bytes per sector",
numCylinders, numHeads,
numSectors, numBytes)
<< std::endl;
Bytes geomBlock;
ByteWriter geomBlockWriter(geomBlock);
geomBlockWriter.write_8(0); /* alternating sides */
geomBlockWriter.write_le16(numCylinders);
geomBlockWriter.write_8(numHeads);
geomBlockWriter.write_8(numSectors);
geomBlockWriter.write_8(0); /* first sector ID */
geomBlockWriter.write_le16(numBytes);
geomBlockWriter.write_8(0); /* data rate */
geomBlockWriter.write_8(0); /* read/write gap */
geomBlockWriter.write_8(0); /* format gap */
geomBlockWriter.write_8(0); /* recording mode */
geomBlockWriter.write_8(0); /* complement flag */
geomBlockWriter.write_8(0); /* disable multitrack read/writes */
geomBlockWriter.write_8(0); /* do not skip deleted data */
Bytes trackDirectory;
ByteWriter trackDirectoryWriter(trackDirectory);
int trackDirectorySize = 0;
@@ -47,7 +65,7 @@ public:
int actualSectors = 0;
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = sectors.get(track, head, sectorId);
const auto& sector = _sectors.get(track, head, sectorId);
if (sector)
actualSectors++;
}
@@ -63,7 +81,7 @@ public:
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = sectors.get(track, head, sectorId);
const auto& sector = _sectors.get(track, head, sectorId);
if (sector)
{
uint32_t sectorLabel = (('S') << 24) | ((track & 0xff) << 16) | (head << 8) | sectorId;
@@ -92,13 +110,37 @@ public:
}
}
trackDirectoryWriter.write_be32(LDBS_GEOM_BLOCK);
trackDirectoryWriter.write_le32(ldbs.put(geomBlock, LDBS_GEOM_BLOCK));
trackDirectorySize++;
trackDirectoryWriter.seek(0);
trackDirectoryWriter.write_le16(trackDirectorySize);
uint32_t trackDirectoryAddress = ldbs.put(trackDirectory, LDBS_TRACK_BLOCK);
Bytes data = ldbs.write(trackDirectoryAddress);
data.writeToFile(_config.filename());
}
std::cout << fmt::format("LDBS: written output image of {} kB total\n",
data.size() / 1024);
}
const AssemblingGeometryMapper* getGeometryMapper() const
{
return this;
}
void put(const Sector& sector) const
{
auto& ptr = _sectors.get(sector.logicalTrack, sector.logicalSide, sector.logicalSector);
ptr.reset(new Sector(sector));
}
void putBlock(size_t offset, size_t length, const Bytes& data)
{ throw "unimplemented"; }
private:
mutable SectorSet _sectors;
};
std::unique_ptr<ImageWriter> ImageWriter::createLDBSImageWriter(const ImageWriterProto& config)

View File

@@ -1,70 +0,0 @@
#include "globals.h"
#include "flags.h"
#include "sector.h"
#include "sectorset.h"
#include "imagewriter/imagewriter.h"
#include "fmt/format.h"
#include "decoders/decoders.h"
#include "arch/northstar/northstar.h"
#include "lib/imagewriter/imagewriter.pb.h"
#include <algorithm>
#include <iostream>
#include <fstream>
class NsiImageWriter : public ImageWriter
{
public:
NsiImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
void writeImage(const SectorSet& sectors)
{
unsigned autoTracks;
unsigned autoSides;
unsigned autoSectors;
unsigned autoBytes;
sectors.calculateSize(autoTracks, autoSides, autoSectors, autoBytes);
size_t trackSize = autoSectors * autoBytes;
std::cout << fmt::format("Writing {} cylinders, {} heads, {} sectors, {} ({} bytes/sector), {} kB total",
autoTracks, autoSides,
autoSectors, autoBytes == 256 ? "SD" : "DD", autoBytes,
autoTracks * trackSize / 1024)
<< std::endl;
std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary);
if (!outputFile.is_open())
Error() << "cannot open output file";
unsigned sectorFileOffset;
for (int track = 0; track < autoTracks * autoSides; track++)
{
int head = (track < autoTracks) ? 0 : 1;
for (int sectorId = 0; sectorId < autoSectors; sectorId++)
{
const auto& sector = sectors.get(track % autoTracks, head, sectorId);
if (sector)
{
if (head == 0) { /* Side 0 is from track 0-34 */
sectorFileOffset = track * trackSize + sectorId * autoBytes;
}
else { /* Side 1 is from track 70-35 */
sectorFileOffset = (autoBytes * autoSectors * autoTracks) + /* Skip over side 0 */
((autoTracks - 1) - (track % autoTracks)) * (autoBytes * autoSectors) +
(sectorId * autoBytes); /* Sector offset from beginning of track. */
}
outputFile.seekp(sectorFileOffset, std::ios::beg);
sector->data.slice(0, autoBytes).writeTo(outputFile);
}
}
}
}
};
std::unique_ptr<ImageWriter> ImageWriter::createNsiImageWriter(
const ImageWriterProto& config)
{
return std::unique_ptr<ImageWriter>(new NsiImageWriter(config));
}

View File

@@ -11,6 +11,7 @@ class Bytes;
#define LDBS_FILE_TYPE 0x44534B02 /* "DSK\02" */
#define LDBS_BLOCK_MAGIC 0x4C444201 /* "LDB\01" */
#define LDBS_TRACK_BLOCK 0x44495201 /* "DIR\01" */
#define LDBS_GEOM_BLOCK 0x47454f4d /* "GEOM" */
class LDBS
{

View File

@@ -14,6 +14,7 @@
#include "decoders/rawbits.h"
#include "track.h"
#include "imagewriter/imagewriter.h"
#include "geometry/geometry.h"
#include "fmt/format.h"
#include "proto.h"
#include "lib/decoders/decoders.pb.h"
@@ -62,7 +63,7 @@ static void replace_sector(std::unique_ptr<Sector>& replacing, Sector& replaceme
}
}
void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWriter& writer)
void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, AssemblingGeometryMapper& geometryMapper)
{
if (config.decoder().has_copy_flux_to())
outputFluxSink = FluxSink::create(config.decoder().copy_flux_to());
@@ -186,10 +187,10 @@ void readDiskCommand(FluxSource& fluxsource, AbstractDecoder& decoder, ImageWrit
}
}
writer.printMap(allSectors);
geometryMapper.printMap(allSectors);
if (config.decoder().has_write_csv_to())
writer.writeCsv(allSectors, config.decoder().write_csv_to());
writer.writeImage(allSectors);
geometryMapper.writeCsv(allSectors, config.decoder().write_csv_to());
geometryMapper.put(allSectors);
if (failures)
std::cerr << "Warning: some sectors could not be decoded." << std::endl;

View File

@@ -5,12 +5,12 @@ class AbstractDecoder;
class FluxSink;
class FluxSource;
class Fluxmap;
class ImageWriter;
class AssemblingGeometryMapper;
class Track;
extern std::vector<std::unique_ptr<Track>> readTracks();
extern void readDiskCommand(FluxSource& source, AbstractDecoder& decoder, ImageWriter& writer);
extern void readDiskCommand(FluxSource& source, AbstractDecoder& decoder, AssemblingGeometryMapper& geometryMapper);
extern void rawReadDiskCommand(FluxSource& source, FluxSink& sink);
#endif

View File

@@ -65,13 +65,12 @@ void fillBitmapTo(std::vector<bool>& bitmap,
}
}
void writeDiskCommand(ImageReader& imageReader, AbstractEncoder& encoder, FluxSink& fluxSink)
void writeDiskCommand(AbstractEncoder& encoder, FluxSink& fluxSink)
{
SectorSet allSectors = imageReader.readImage();
writeTracks(fluxSink,
[&](int track, int side) -> std::unique_ptr<Fluxmap>
{
return encoder.encode(track, side, allSectors);
return encoder.encode(track, side);
}
);
}

View File

@@ -13,7 +13,7 @@ extern void fillBitmapTo(std::vector<bool>& bitmap,
unsigned& cursor, unsigned terminateAt,
const std::vector<bool>& pattern);
extern void writeDiskCommand(ImageReader& imageReader, AbstractEncoder& encoder, FluxSink& fluxSink);
extern void writeDiskCommand(AbstractEncoder& encoder, FluxSink& fluxSink);
extern void writeRawDiskCommand(FluxSource& fluxSource, FluxSink& fluxSink);
#endif

View File

@@ -282,6 +282,7 @@ buildproto libproto.a \
lib/encoders/encoders.proto \
lib/fluxsource/fluxsource.proto \
lib/fluxsink/fluxsink.proto \
lib/geometry/geometry.proto \
lib/imagereader/imagereader.proto \
lib/imagewriter/imagewriter.proto \
lib/usb/usb.proto \
@@ -337,21 +338,19 @@ buildlibrary libbackend.a \
lib/fluxsource/scpfluxsource.cc \
lib/fluxsource/sqlitefluxsource.cc \
lib/fluxsource/testpatternfluxsource.cc \
lib/geometry/simplemapper.cc \
lib/geometry/geometry.cc \
lib/globals.cc \
lib/hexdump.cc \
lib/imagereader/d64imagereader.cc \
lib/imagereader/diskcopyimagereader.cc \
lib/imagereader/imagereader.cc \
lib/imagereader/imdimagereader.cc \
lib/imagereader/imgimagereader.cc \
lib/imagereader/jv3imagereader.cc \
lib/imagereader/nsiimagereader.cc \
lib/imagewriter/d64imagewriter.cc \
lib/imagewriter/diskcopyimagewriter.cc \
lib/imagewriter/imagewriter.cc \
lib/imagewriter/imgimagewriter.cc \
lib/imagewriter/ldbsimagewriter.cc \
lib/imagewriter/nsiimagewriter.cc \
lib/ldbs.cc \
lib/proto.cc \
lib/reader.cc \
@@ -384,7 +383,9 @@ READABLES="\
macintosh \
micropolis \
mx \
northstar \
northstar87 \
northstar175 \
northstar350 \
tids990 \
victor9k \
zilogmcz \
@@ -411,7 +412,8 @@ WRITABLES="\
ibm360_525 \
ibm720 \
ibm720_525 \
macintosh \
macintosh400 \
macintosh800 \
northstar87 \
northstar175 \
northstar350 \

View File

@@ -13,6 +13,7 @@
#include "arch/brother/brother.h"
#include "arch/ibm/ibm.h"
#include "imagewriter/imagewriter.h"
#include "geometry/geometry.h"
#include "fmt/format.h"
#include "fluxengine.h"
#include <google/protobuf/text_format.h>
@@ -78,10 +79,11 @@ int mainRead(int argc, const char* argv[])
Error() << "you cannot copy flux to a hardware device";
std::unique_ptr<FluxSource> fluxSource(FluxSource::create(config.input().flux()));
std::unique_ptr<AbstractDecoder> decoder(AbstractDecoder::create(config.decoder()));
std::unique_ptr<ImageWriter> writer(ImageWriter::create(config.output().image()));
std::unique_ptr<AssemblingGeometryMapper> geometryMapper(createSimpleAssemblingGeometryMapper(config.geometry(), *writer));
std::unique_ptr<AbstractDecoder> decoder(AbstractDecoder::create(config.decoder()));
readDiskCommand(*fluxSource, *decoder, *writer);
readDiskCommand(*fluxSource, *decoder, *geometryMapper);
return 0;
}

View File

@@ -13,6 +13,7 @@
#include "arch/ibm/ibm.h"
#include "imagereader/imagereader.h"
#include "fluxengine.h"
#include "geometry/geometry.h"
#include "fmt/format.h"
#include <google/protobuf/text_format.h>
#include <fstream>
@@ -65,10 +66,11 @@ int mainWrite(int argc, const char* argv[])
Error() << "incomplete config (did you remember to specify the format?)";
std::unique_ptr<ImageReader> reader(ImageReader::create(config.input().image()));
std::unique_ptr<AbstractEncoder> encoder(AbstractEncoder::create(config.encoder()));
std::unique_ptr<DisassemblingGeometryMapper> geometryMapper(createSimpleDisassemblingGeometryMapper(config.geometry(), *reader));
std::unique_ptr<AbstractEncoder> encoder(AbstractEncoder::create(config.encoder(), *geometryMapper));
std::unique_ptr<FluxSink> fluxSink(FluxSink::create(config.output().flux()));
writeDiskCommand(*reader, *encoder, *fluxSink);
writeDiskCommand(*encoder, *fluxSink);
return 0;
}

View File

@@ -9,7 +9,7 @@ input {
output {
image {
filename: "commodore1541.d64"
d64 {}
img {}
}
}
@@ -17,6 +17,139 @@ decoder {
c64 {}
}
geometry {
cylinders: 41
heads: 1
trackdata {
sectors: 17
sector_size: 256
}
# Cylinders 0..16: 21 sectors
# Cylinders 17..23: 19 sectors
# Cylinders 24..29: 18 sectors
# Cylinders 30..41: 17 sectors (i.e. all the rest)
trackdata {
cylinder: 0
sectors: 21
}
trackdata {
cylinder: 1
sectors: 21
}
trackdata {
cylinder: 2
sectors: 21
}
trackdata {
cylinder: 3
sectors: 21
}
trackdata {
cylinder: 4
sectors: 21
}
trackdata {
cylinder: 5
sectors: 21
}
trackdata {
cylinder: 6
sectors: 21
}
trackdata {
cylinder: 7
sectors: 21
}
trackdata {
cylinder: 8
sectors: 21
}
trackdata {
cylinder: 9
sectors: 21
}
trackdata {
cylinder: 10
sectors: 21
}
trackdata {
cylinder: 11
sectors: 21
}
trackdata {
cylinder: 12
sectors: 21
}
trackdata {
cylinder: 13
sectors: 21
}
trackdata {
cylinder: 14
sectors: 21
}
trackdata {
cylinder: 15
sectors: 21
}
trackdata {
cylinder: 16
sectors: 21
}
trackdata {
cylinder: 17
sectors: 19
}
trackdata {
cylinder: 18
sectors: 19
}
trackdata {
cylinder: 19
sectors: 19
}
trackdata {
cylinder: 20
sectors: 19
}
trackdata {
cylinder: 21
sectors: 19
}
trackdata {
cylinder: 22
sectors: 19
}
trackdata {
cylinder: 23
sectors: 19
}
trackdata {
cylinder: 24
sectors: 18
}
trackdata {
cylinder: 25
sectors: 18
}
trackdata {
cylinder: 26
sectors: 18
}
trackdata {
cylinder: 27
sectors: 18
}
trackdata {
cylinder: 28
sectors: 18
}
trackdata {
cylinder: 29
sectors: 18
}
}
cylinders {
start: 0
end: 79

View File

@@ -0,0 +1,43 @@
comment: 'Northstar 175kB 5.25" 35-track single-sided double-density hard-sectored'
input {
flux {
drive {
hard_sector_count: 10
sync_with_index: 1
}
}
}
output {
image {
filename: "northstar.nsi"
img {}
}
}
decoder {
northstar {}
}
geometry {
cylinders: 35
heads: 1
block_ordering: ORDER_NSI
trackdata {
sectors: 10
sector_size: 512
}
}
cylinders {
start: 0
end: 39
}
heads {
start: 0
end: 0
}

View File

@@ -1,4 +1,4 @@
comment: 'Northstar 87kB/175kB/350kB 5.25" 35-track 10-sector hard sectored'
comment: 'Northstar 350kB 5.25" 35-track double-sided double-density hard-sectored'
input {
flux {
@@ -12,7 +12,7 @@ input {
output {
image {
filename: "northstar.nsi"
nsi {}
img {}
}
}
@@ -20,9 +20,19 @@ decoder {
northstar {}
}
geometry {
cylinders: 35
heads: 2
block_ordering: ORDER_NSI
trackdata {
sectors: 10
sector_size: 512
}
}
cylinders {
start: 0
end: 34
end: 39
}
heads {

View File

@@ -0,0 +1,42 @@
comment: 'Northstar 87.5kB 5.25" 35-track single-sided single-density hard-sectored'
input {
flux {
drive {
hard_sector_count: 10
sync_with_index: 1
}
}
}
output {
image {
filename: "northstar.nsi"
img {}
}
}
decoder {
northstar {}
}
geometry {
cylinders: 35
heads: 1
block_ordering: ORDER_NSI
trackdata {
sectors: 10
sector_size: 256
}
}
cylinders {
start: 0
end: 39
}
heads {
start: 0
end: 0
}

View File

@@ -3,14 +3,7 @@ comment: 'Amiga 880kB 3.5" double sided'
input {
image {
filename: "amiga.adf"
img {
tracks: 80
sides: 2
trackdata {
sectors: 11
sector_size: 512
}
}
img {}
}
}
@@ -18,6 +11,15 @@ output {
flux {}
}
geometry {
cylinders: 80
heads: 2
trackdata {
sectors: 11
sector_size: 512
}
}
encoder {
amiga {}
}

View File

@@ -3,14 +3,7 @@ comment: 'Atari ST 360kB 3.5" 80-track 9-sector single sided'
input {
image {
filename: "atarist360.st"
img {
tracks: 80
sides: 1
trackdata {
sectors: 9
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 80
heads: 1
trackdata {
sectors: 9
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'Atari ST 370kB 3.5" 82-track 9-sector single sided'
input {
image {
filename: "atarist370.st"
img {
tracks: 82
sides: 1
trackdata {
sectors: 9
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 82
heads: 1
trackdata {
sectors: 9
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'Atari ST 400kB 3.5" 80-track 10-sector single sided'
input {
image {
filename: "atarist400.st"
img {
tracks: 80
sides: 1
trackdata {
sectors: 10
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 80
heads: 1
trackdata {
sectors: 10
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'Atari ST 410kB 3.5" 82-track 10-sector single sided'
input {
image {
filename: "atarist410.st"
img {
tracks: 82
sides: 1
trackdata {
sectors: 10
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 82
heads: 1
trackdata {
sectors: 10
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'Atari ST 720kB 3.5" 80-track 9-sector double sided'
input {
image {
filename: "atarist720.st"
img {
tracks: 80
sides: 2
trackdata {
sectors: 9
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 80
heads: 2
trackdata {
sectors: 9
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'Atari ST 740kB 3.5" 82-track 9-sector double sided'
input {
image {
filename: "atarist740.st"
img {
tracks: 82
sides: 2
trackdata {
sectors: 9
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 82
heads: 2
trackdata {
sectors: 9
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'Atari ST 800kB 3.5" 80-track 10-sector double sided'
input {
image {
filename: "atarist800.st"
img {
tracks: 80
sides: 2
trackdata {
sectors: 10
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 80
heads: 2
trackdata {
sectors: 10
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'Atari ST 820kB 3.5" 82-track 10-sector double sided'
input {
image {
filename: "atarist820.st"
img {
tracks: 82
sides: 2
trackdata {
sectors: 10
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 82
heads: 2
trackdata {
sectors: 10
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'Brother 120kB 3.5" 39-track GCR disks'
input {
image {
filename: "brother120.img"
img {
tracks: 39
sides: 1
trackdata {
sectors: 12
sector_size: 256
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 39
heads: 1
trackdata {
sectors: 12
sector_size: 256
}
}
encoder {
brother {
format: BROTHER120

View File

@@ -3,14 +3,7 @@ comment: 'Brother 240kB 3.5" 78-track GCR disks'
input {
image {
filename: "brother240.img"
img {
tracks: 78
sides: 1
trackdata {
sectors: 12
sector_size: 256
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 78
heads: 1
trackdata {
sectors: 12
sector_size: 256
}
}
encoder {
brother {}
}

View File

@@ -2,8 +2,8 @@ comment: 'Commodore 1541 170kB 5.25" GCR disks'
input {
image {
filename: "commodore1541.img"
d64 {}
filename: "commodore1541.d64"
img {}
}
}
@@ -17,9 +17,142 @@ encoder {
c64 {}
}
geometry {
cylinders: 41
heads: 1
trackdata {
sectors: 17
sector_size: 256
}
# Cylinders 0..16: 21 sectors
# Cylinders 17..23: 19 sectors
# Cylinders 24..29: 18 sectors
# Cylinders 30..41: 17 sectors (i.e. all the rest)
trackdata {
cylinder: 0
sectors: 21
}
trackdata {
cylinder: 1
sectors: 21
}
trackdata {
cylinder: 2
sectors: 21
}
trackdata {
cylinder: 3
sectors: 21
}
trackdata {
cylinder: 4
sectors: 21
}
trackdata {
cylinder: 5
sectors: 21
}
trackdata {
cylinder: 6
sectors: 21
}
trackdata {
cylinder: 7
sectors: 21
}
trackdata {
cylinder: 8
sectors: 21
}
trackdata {
cylinder: 9
sectors: 21
}
trackdata {
cylinder: 10
sectors: 21
}
trackdata {
cylinder: 11
sectors: 21
}
trackdata {
cylinder: 12
sectors: 21
}
trackdata {
cylinder: 13
sectors: 21
}
trackdata {
cylinder: 14
sectors: 21
}
trackdata {
cylinder: 15
sectors: 21
}
trackdata {
cylinder: 16
sectors: 21
}
trackdata {
cylinder: 17
sectors: 19
}
trackdata {
cylinder: 18
sectors: 19
}
trackdata {
cylinder: 19
sectors: 19
}
trackdata {
cylinder: 20
sectors: 19
}
trackdata {
cylinder: 21
sectors: 19
}
trackdata {
cylinder: 22
sectors: 19
}
trackdata {
cylinder: 23
sectors: 19
}
trackdata {
cylinder: 24
sectors: 18
}
trackdata {
cylinder: 25
sectors: 18
}
trackdata {
cylinder: 26
sectors: 18
}
trackdata {
cylinder: 27
sectors: 18
}
trackdata {
cylinder: 28
sectors: 18
}
trackdata {
cylinder: 29
sectors: 18
}
}
cylinders {
start: 0
end: 79
end: 81
}
heads {

View File

@@ -3,14 +3,7 @@ comment: 'Commodore 1581 800kB 3.5" MFM disks'
input {
image {
filename: "commodore1581.d81"
img {
tracks: 80
sides: 2
trackdata {
sectors: 10
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 80
heads: 2
trackdata {
sectors: 10
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'Hewlett-Packard LIF 770kB 3.5" disks'
input {
image {
filename: "hplif770.img"
img {
tracks: 77
sides: 2
trackdata {
sectors: 5
sector_size: 1024
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 77
heads: 2
trackdata {
sectors: 5
sector_size: 1024
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'PC 1200kB 5.25" 80-track 15-sector double-sided'
input {
image {
filename: "ibm1200_525.img"
img {
tracks: 80
sides: 2
trackdata {
sectors: 15
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 80
heads: 2
trackdata {
sectors: 15
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'PC 1440kB 3.5" 80-track 18-sector double-sided'
input {
image {
filename: "ibm1440.img"
img {
tracks: 80
sides: 2
trackdata {
sectors: 18
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 80
heads: 2
trackdata {
sectors: 18
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,15 +3,7 @@ comment: 'PC 180kB 5.25" 40-track 9-sector single-sided'
input {
image {
filename: "ibm180_525.img"
img {
tracks: 40
sides: 1
physical_step: 2
trackdata {
sectors: 9
sector_size: 512
}
}
img {}
}
}
@@ -21,6 +13,15 @@ output {
}
}
geometry {
cylinders: 40
heads: 1
trackdata {
sectors: 9
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,15 +3,7 @@ comment: 'PC 360kB 5.25" 40-track 9-sector double-sided'
input {
image {
filename: "ibm360_525.img"
img {
tracks: 40
sides: 2
physical_step: 2
trackdata {
sectors: 9
sector_size: 512
}
}
img {}
}
}
@@ -21,6 +13,15 @@ output {
}
}
geometry {
cylinders: 40
heads: 2
trackdata {
sectors: 9
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'PC 720kB 3.5" 80-track 9-sector double-sided'
input {
image {
filename: "ibm720.img"
img {
tracks: 80
sides: 2
trackdata {
sectors: 9
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 80
heads: 2
trackdata {
sectors: 9
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -3,14 +3,7 @@ comment: 'PC 720kB 5.25" 80-track 9-sector double-sided'
input {
image {
filename: "ibm720_525.img"
img {
tracks: 80
sides: 2
trackdata {
sectors: 9
sector_size: 512
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 80
heads: 2
trackdata {
sectors: 9
sector_size: 512
}
}
encoder {
ibm {
trackdata {

View File

@@ -1,29 +0,0 @@
comment: 'Macintosh 800kB 3.5" GCR double-sided'
input {
image {
filename: "macintosh.diskcopy"
diskcopy {}
}
}
output {
flux {
drive {}
}
}
encoder {
macintosh {}
}
cylinders {
start: 0
end: 79
}
heads {
start: 0
end: 1
}

View File

@@ -0,0 +1,300 @@
comment: 'Macintosh 400kB 3.5" GCR single-sided'
input {
image {
filename: "macintosh.diskcopy"
diskcopy {}
}
}
output {
flux {
drive {}
}
}
encoder {
macintosh {}
}
geometry {
cylinders: 80
heads: 1
trackdata {
sectors: 8
sector_size: 524
}
# Cylinders 0..15: 12 sectors
# 16..31: 11 sectors
# 32..47: 10 sectors
# 48..63: 9 sectors
# the rest: 8 sectors
trackdata {
cylinder: 0
sectors: 12
}
trackdata {
cylinder: 1
sectors: 12
}
trackdata {
cylinder: 2
sectors: 12
}
trackdata {
cylinder: 3
sectors: 12
}
trackdata {
cylinder: 4
sectors: 12
}
trackdata {
cylinder: 5
sectors: 12
}
trackdata {
cylinder: 6
sectors: 12
}
trackdata {
cylinder: 7
sectors: 12
}
trackdata {
cylinder: 8
sectors: 12
}
trackdata {
cylinder: 9
sectors: 12
}
trackdata {
cylinder: 10
sectors: 12
}
trackdata {
cylinder: 11
sectors: 12
}
trackdata {
cylinder: 12
sectors: 12
}
trackdata {
cylinder: 13
sectors: 12
}
trackdata {
cylinder: 14
sectors: 12
}
trackdata {
cylinder: 15
sectors: 12
}
trackdata {
cylinder: 16
sectors: 11
}
trackdata {
cylinder: 17
sectors: 11
}
trackdata {
cylinder: 18
sectors: 11
}
trackdata {
cylinder: 19
sectors: 11
}
trackdata {
cylinder: 20
sectors: 11
}
trackdata {
cylinder: 21
sectors: 11
}
trackdata {
cylinder: 22
sectors: 11
}
trackdata {
cylinder: 23
sectors: 11
}
trackdata {
cylinder: 24
sectors: 11
}
trackdata {
cylinder: 25
sectors: 11
}
trackdata {
cylinder: 26
sectors: 11
}
trackdata {
cylinder: 27
sectors: 11
}
trackdata {
cylinder: 28
sectors: 11
}
trackdata {
cylinder: 29
sectors: 11
}
trackdata {
cylinder: 30
sectors: 11
}
trackdata {
cylinder: 31
sectors: 11
}
trackdata {
cylinder: 32
sectors: 10
}
trackdata {
cylinder: 33
sectors: 10
}
trackdata {
cylinder: 34
sectors: 10
}
trackdata {
cylinder: 35
sectors: 10
}
trackdata {
cylinder: 36
sectors: 10
}
trackdata {
cylinder: 37
sectors: 10
}
trackdata {
cylinder: 38
sectors: 10
}
trackdata {
cylinder: 39
sectors: 10
}
trackdata {
cylinder: 40
sectors: 10
}
trackdata {
cylinder: 41
sectors: 10
}
trackdata {
cylinder: 42
sectors: 10
}
trackdata {
cylinder: 43
sectors: 10
}
trackdata {
cylinder: 44
sectors: 10
}
trackdata {
cylinder: 45
sectors: 10
}
trackdata {
cylinder: 46
sectors: 10
}
trackdata {
cylinder: 47
sectors: 10
}
trackdata {
cylinder: 48
sectors: 9
}
trackdata {
cylinder: 49
sectors: 9
}
trackdata {
cylinder: 50
sectors: 9
}
trackdata {
cylinder: 51
sectors: 9
}
trackdata {
cylinder: 52
sectors: 9
}
trackdata {
cylinder: 53
sectors: 9
}
trackdata {
cylinder: 54
sectors: 9
}
trackdata {
cylinder: 55
sectors: 9
}
trackdata {
cylinder: 56
sectors: 9
}
trackdata {
cylinder: 57
sectors: 9
}
trackdata {
cylinder: 58
sectors: 9
}
trackdata {
cylinder: 59
sectors: 9
}
trackdata {
cylinder: 60
sectors: 9
}
trackdata {
cylinder: 61
sectors: 9
}
trackdata {
cylinder: 62
sectors: 9
}
trackdata {
cylinder: 63
sectors: 9
}
}
cylinders {
start: 0
end: 79
}
heads {
start: 0
end: 0
}

View File

@@ -0,0 +1,300 @@
comment: 'Macintosh 800kB 3.5" GCR double-sided'
input {
image {
filename: "macintosh.diskcopy"
diskcopy {}
}
}
output {
flux {
drive {}
}
}
encoder {
macintosh {}
}
geometry {
cylinders: 80
heads: 2
trackdata {
sectors: 8
sector_size: 524
}
# Cylinders 0..15: 12 sectors
# 16..31: 11 sectors
# 32..47: 10 sectors
# 48..63: 9 sectors
# the rest: 8 sectors
trackdata {
cylinder: 0
sectors: 12
}
trackdata {
cylinder: 1
sectors: 12
}
trackdata {
cylinder: 2
sectors: 12
}
trackdata {
cylinder: 3
sectors: 12
}
trackdata {
cylinder: 4
sectors: 12
}
trackdata {
cylinder: 5
sectors: 12
}
trackdata {
cylinder: 6
sectors: 12
}
trackdata {
cylinder: 7
sectors: 12
}
trackdata {
cylinder: 8
sectors: 12
}
trackdata {
cylinder: 9
sectors: 12
}
trackdata {
cylinder: 10
sectors: 12
}
trackdata {
cylinder: 11
sectors: 12
}
trackdata {
cylinder: 12
sectors: 12
}
trackdata {
cylinder: 13
sectors: 12
}
trackdata {
cylinder: 14
sectors: 12
}
trackdata {
cylinder: 15
sectors: 12
}
trackdata {
cylinder: 16
sectors: 11
}
trackdata {
cylinder: 17
sectors: 11
}
trackdata {
cylinder: 18
sectors: 11
}
trackdata {
cylinder: 19
sectors: 11
}
trackdata {
cylinder: 20
sectors: 11
}
trackdata {
cylinder: 21
sectors: 11
}
trackdata {
cylinder: 22
sectors: 11
}
trackdata {
cylinder: 23
sectors: 11
}
trackdata {
cylinder: 24
sectors: 11
}
trackdata {
cylinder: 25
sectors: 11
}
trackdata {
cylinder: 26
sectors: 11
}
trackdata {
cylinder: 27
sectors: 11
}
trackdata {
cylinder: 28
sectors: 11
}
trackdata {
cylinder: 29
sectors: 11
}
trackdata {
cylinder: 30
sectors: 11
}
trackdata {
cylinder: 31
sectors: 11
}
trackdata {
cylinder: 32
sectors: 10
}
trackdata {
cylinder: 33
sectors: 10
}
trackdata {
cylinder: 34
sectors: 10
}
trackdata {
cylinder: 35
sectors: 10
}
trackdata {
cylinder: 36
sectors: 10
}
trackdata {
cylinder: 37
sectors: 10
}
trackdata {
cylinder: 38
sectors: 10
}
trackdata {
cylinder: 39
sectors: 10
}
trackdata {
cylinder: 40
sectors: 10
}
trackdata {
cylinder: 41
sectors: 10
}
trackdata {
cylinder: 42
sectors: 10
}
trackdata {
cylinder: 43
sectors: 10
}
trackdata {
cylinder: 44
sectors: 10
}
trackdata {
cylinder: 45
sectors: 10
}
trackdata {
cylinder: 46
sectors: 10
}
trackdata {
cylinder: 47
sectors: 10
}
trackdata {
cylinder: 48
sectors: 9
}
trackdata {
cylinder: 49
sectors: 9
}
trackdata {
cylinder: 50
sectors: 9
}
trackdata {
cylinder: 51
sectors: 9
}
trackdata {
cylinder: 52
sectors: 9
}
trackdata {
cylinder: 53
sectors: 9
}
trackdata {
cylinder: 54
sectors: 9
}
trackdata {
cylinder: 55
sectors: 9
}
trackdata {
cylinder: 56
sectors: 9
}
trackdata {
cylinder: 57
sectors: 9
}
trackdata {
cylinder: 58
sectors: 9
}
trackdata {
cylinder: 59
sectors: 9
}
trackdata {
cylinder: 60
sectors: 9
}
trackdata {
cylinder: 61
sectors: 9
}
trackdata {
cylinder: 62
sectors: 9
}
trackdata {
cylinder: 63
sectors: 9
}
}
cylinders {
start: 0
end: 79
}
heads {
start: 0
end: 1
}

View File

@@ -3,7 +3,7 @@ comment: 'Northstar 175kB 5.25" 35-track single-sided double-density hard-sector
input {
image {
filename: "northstar.nsi"
nsi {}
img {}
}
}
@@ -15,6 +15,16 @@ output {
}
}
geometry {
cylinders: 35
heads: 1
block_ordering: ORDER_NSI
trackdata {
sectors: 10
sector_size: 512
}
}
encoder {
northstar {}
}

View File

@@ -3,7 +3,7 @@ comment: 'Northstar 350kB 5.25" 35-track double-sided double-density hard-sector
input {
image {
filename: "northstar.nsi"
nsi {}
img {}
}
}
@@ -15,6 +15,16 @@ output {
}
}
geometry {
cylinders: 35
heads: 2
block_ordering: ORDER_NSI
trackdata {
sectors: 10
sector_size: 512
}
}
encoder {
northstar {}
}

View File

@@ -3,7 +3,7 @@ comment: 'Northstar 87.5kB 5.25" 35-track single-sided single-density hard-secto
input {
image {
filename: "northstar.nsi"
nsi {}
img {}
}
}
@@ -15,6 +15,16 @@ output {
}
}
geometry {
cylinders: 35
heads: 1
block_ordering: ORDER_NSI
trackdata {
sectors: 10
sector_size: 256
}
}
encoder {
northstar {}
}

View File

@@ -3,14 +3,7 @@ comment: 'Texas Instruments DS990 1126kB 8" double-sided'
input {
image {
filename: "tids990.img"
img {
tracks: 77
sides: 2
trackdata {
sectors: 26
sector_size: 288
}
}
img {}
}
}
@@ -20,6 +13,15 @@ output {
}
}
geometry {
cylinders: 77
heads: 2
trackdata {
sectors: 26
sector_size: 288
}
}
encoder {
tids990 {}
}

View File

@@ -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)
{
@@ -113,6 +116,13 @@ static void test_slice()
bs = b.slice(4, 2);
assert((bs == Bytes{ 0, 0 }));
bs = b.slice(2, 2);
assert((bs == Bytes{ 3, 0 }));
bs = b.slice(1, 4);
bs = bs.slice(1, 4);
AssertThat(bs.toVector<int>(), Equals(Bytes{ 3, 0, 0, 0 }.toVector<int>()));
}
static void test_tobits()