Migrate the Northstar code to the new framework.

This commit is contained in:
David Given
2021-05-24 23:20:59 +02:00
parent 0f83082cf0
commit bf4831be9f
22 changed files with 259 additions and 120 deletions

View File

@@ -125,6 +125,7 @@ at least, check the CRC so what data's there is probably good.
| [DVK MX](doc/disk-mx.md) | 🦖 | | Soviet PDP-11 clone |
| [VDS Eco1](doc/disk-eco1.md) | 🦖 | | 8" mixed format |
| [Micropolis](doc/disk-micropolis.md) | 🦄 | | Micropolis 100tpi drives |
| [Northstar(doc/disk-northstar.md) | 🦖 | 🦖 | 5.25" hard sectors |
| [TI DS990 FD1000](doc/disk-tids990.md) | 🦄 | 🦄 | 8" |
| [Victor 9000](doc/disk-victor9k.md) | 🦖 | | 8" |
| [Zilog MCZ](doc/disk-zilogmcz.md) | 🦖 | | 8" _and_ hard sectors |

View File

@@ -2,8 +2,6 @@
#include "northstar.h"
#include "sectorset.h"
FlagGroup northstarEncoderFlags;
#define GAP_FILL_SIZE_SD 30
#define PRE_HEADER_GAP_FILL_SIZE_SD 9
#define GAP_FILL_SIZE_DD 62

View File

@@ -28,10 +28,14 @@
#define SECTOR_TYPE_MFM (0)
#define SECTOR_TYPE_FM (1)
class NorthstarEncoderProto;
class NorthstarDecoderProto;
class NorthstarDecoder : public AbstractDecoder
{
public:
NorthstarDecoder()
NorthstarDecoder(const NorthstarDecoderProto& config):
_config(config)
{
_sectorType = SECTOR_TYPE_MFM;
}
@@ -43,6 +47,7 @@ public:
std::set<unsigned> requiredSectors(Track& track) const;
private:
const NorthstarDecoderProto& _config;
uint8_t _sectorType;
uint8_t _hardSectorId;
};
@@ -50,8 +55,15 @@ private:
class NorthstarEncoder : public AbstractEncoder
{
public:
NorthstarEncoder(const NorthstarEncoderProto& config):
_config(config)
{}
virtual ~NorthstarEncoder() {}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
private:
const NorthstarEncoderProto& _config;
};
extern FlagGroup northstarEncoderFlags;

View File

@@ -32,7 +32,7 @@ fluxengine read northstar
To read a single-sided North Star floppy, run:
```
fluxengine read northstar -s:0
fluxengine read northstar -heads 0
```
You should end up with a `northstar.nsi` with a file size dependent on the floppy
@@ -53,15 +53,11 @@ sure that the drive's spindle speed is adjusted to exactly 300RPM.
To write a double-sided North Star floppy, run:
```
fluxengine write northstar -i image_to_write.nsi
```
To write a single-sided North Star floppy, run:
```
fluxengine write northstar -i image_to_write.nsi -d:s=0
fluxengine write <format> -i image_to_write.nsi
```
...where `<format>` is `northstar87`, `northstar175` or `northstar350`
depending on the format you want to write.
Useful references
-----------------

View File

@@ -13,6 +13,7 @@
#include "arch/macintosh/macintosh.h"
#include "arch/micropolis/micropolis.h"
#include "arch/mx/mx.h"
#include "arch/northstar/northstar.h"
#include "arch/tids990/tids990.h"
#include "arch/victor9k/victor9k.h"
#include "arch/zilogmcz/zilogmcz.h"
@@ -69,6 +70,9 @@ std::unique_ptr<AbstractDecoder> AbstractDecoder::create(const DecoderProto& con
case DecoderProto::kZilogmcz:
return std::unique_ptr<AbstractDecoder>(new ZilogMczDecoder(config.zilogmcz()));
case DecoderProto::kNorthstar:
return std::unique_ptr<AbstractDecoder>(new NorthstarDecoder(config.northstar()));
default:
Error() << "no input disk format specified";
}

View File

@@ -11,6 +11,7 @@ import "arch/ibm/ibm.proto";
import "arch/macintosh/macintosh.proto";
import "arch/micropolis/micropolis.proto";
import "arch/mx/mx.proto";
import "arch/northstar/northstar.proto";
import "arch/tids990/tids990.proto";
import "arch/victor9k/victor9k.proto";
import "arch/zilogmcz/zilogmcz.proto";
@@ -42,6 +43,7 @@ message DecoderProto {
Tids990DecoderProto tids990 = 16;
Victor9kDecoderProto victor9k = 17;
ZilogMczDecoderProto zilogmcz = 18;
NorthstarDecoderProto northstar = 24;
}
optional FluxSinkProto copy_flux_to = 19

View File

@@ -7,6 +7,7 @@
#include "arch/c64/c64.h"
#include "arch/ibm/ibm.h"
#include "arch/macintosh/macintosh.h"
#include "arch/northstar/northstar.h"
#include "arch/tids990/tids990.h"
#include "lib/encoders/encoders.pb.h"
#include "protocol.h"
@@ -30,6 +31,9 @@ std::unique_ptr<AbstractEncoder> AbstractEncoder::create(const EncoderProto& con
case EncoderProto::kC64:
return std::unique_ptr<AbstractEncoder>(new Commodore64Encoder(config.c64()));
case EncoderProto::kNorthstar:
return std::unique_ptr<AbstractEncoder>(new NorthstarEncoder(config.northstar()));
default:
Error() << "no input disk format specified";
}

View File

@@ -5,6 +5,7 @@ import "arch/brother/brother.proto";
import "arch/c64/c64.proto";
import "arch/ibm/ibm.proto";
import "arch/macintosh/macintosh.proto";
import "arch/northstar/northstar.proto";
import "arch/tids990/tids990.proto";
//import "lib/common.proto";
@@ -16,5 +17,6 @@ message EncoderProto {
MacintoshEncoderProto macintosh = 6;
Tids990EncoderProto tids990 = 7;
Commodore64EncoderProto c64 = 8;
NorthstarEncoderProto northstar = 9;
}
}

View File

@@ -29,6 +29,9 @@ std::unique_ptr<ImageReader> ImageReader::create(const ImageReaderProto& 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>();
@@ -46,6 +49,7 @@ void ImageReader::updateConfigForFilename(ImageReaderProto* proto, const std::st
{".diskcopy", [&]() { proto->mutable_diskcopy(); }},
{".img", [&]() { proto->mutable_img(); }},
{".st", [&]() { proto->mutable_img(); }},
{".nsi", [&]() { proto->mutable_nsi(); }},
};
for (const auto& it : formats)

View File

@@ -21,6 +21,7 @@ public:
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;

View File

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

View File

@@ -2,11 +2,11 @@
#include "globals.h"
#include "flags.h"
#include "dataspec.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>
@@ -14,73 +14,78 @@
class NsiImageReader : public ImageReader
{
public:
NsiImageReader(const ImageSpec& spec):
ImageReader(spec)
NsiImageReader(const ImageReaderProto& config):
ImageReader(config)
{}
SectorSet readImage()
{
std::ifstream inputFile(spec.filename, std::ios::in | std::ios::binary);
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
if (!inputFile.is_open())
Error() << "cannot open input file";
if ((spec.cylinders == 0) && (spec.heads == 0) && (spec.sectors == 0) && (spec.bytes == 0)) {
const auto begin = inputFile.tellg();
inputFile.seekg(0, std::ios::end);
const auto end = inputFile.tellg();
const auto fsize = (end - begin);
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;
std::cout << "NSI: Autodetecting geometry based on file size: " << fsize << std::endl;
spec.cylinders = 35;
spec.sectors = 10;
int numCylinders = 35;
int numSectors = 10;
int numHeads = 2;
int sectorSize = 512;
switch (fsize) {
case 358400:
spec.heads = 2;
spec.bytes = 512;
break;
case 179200:
spec.heads = 1;
spec.bytes = 512;
break;
case 89600:
spec.heads = 1;
spec.bytes = 256;
break;
}
}
switch (fsize) {
case 358400:
numHeads = 2;
sectorSize = 512;
break;
size_t trackSize = spec.sectors * spec.bytes;
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",
spec.cylinders, spec.heads,
spec.sectors, spec.bytes,
spec.cylinders * spec.heads * trackSize / 1024)
numCylinders, numHeads,
numSectors, sectorSize,
numCylinders * numHeads * trackSize / 1024)
<< std::endl;
SectorSet sectors;
unsigned sectorFileOffset;
for (int head = 0; head < spec.heads; head++)
for (int head = 0; head < numHeads; head++)
{
for (int track = 0; track < spec.cylinders; track++)
for (int track = 0; track < numCylinders; track++)
{
for (int sectorId = 0; sectorId < spec.sectors; sectorId++)
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
if (head == 0) { /* Head 0 is from track 0-34 */
sectorFileOffset = track * trackSize + sectorId * spec.bytes;
sectorFileOffset = track * trackSize + sectorId * sectorSize;
}
else { /* Head 1 is from track 70-35 */
sectorFileOffset = (trackSize * spec.cylinders) + /* Skip over side 0 */
((spec.cylinders - track - 1) * trackSize) +
(sectorId * spec.bytes); /* Sector offset from beginning of track. */
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(spec.bytes);
inputFile.read((char*) data.begin(), spec.bytes);
Bytes data(sectorSize);
inputFile.read((char*) data.begin(), sectorSize);
std::unique_ptr<Sector>& sector = sectors.get(track, head, sectorId);
sector.reset(new Sector);
@@ -97,8 +102,8 @@ public:
};
std::unique_ptr<ImageReader> ImageReader::createNsiImageReader(
const ImageSpec& spec)
const ImageReaderProto& config)
{
return std::unique_ptr<ImageReader>(new NsiImageReader(spec));
return std::unique_ptr<ImageReader>(new NsiImageReader(config));
}

View File

@@ -26,6 +26,9 @@ std::unique_ptr<ImageWriter> ImageWriter::create(const ImageWriterProto& config)
case ImageWriterProto::kDiskcopy:
return ImageWriter::createDiskCopyImageWriter(config);
case ImageWriterProto::kNsi:
return ImageWriter::createNsiImageWriter(config);
default:
Error() << "bad output image config";
return std::unique_ptr<ImageWriter>();
@@ -43,6 +46,7 @@ void ImageWriter::updateConfigForFilename(ImageWriterProto* proto, const std::st
{".img", [&]() { proto->mutable_img(); }},
{".ldbs", [&]() { proto->mutable_ldbs(); }},
{".st", [&]() { proto->mutable_img(); }},
{".nsi", [&]() { proto->mutable_nsi(); }},
};
for (const auto& it : formats)

View File

@@ -22,6 +22,8 @@ public:
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);

View File

@@ -6,6 +6,7 @@ import "lib/common.proto";
message D64OutputProto {}
message LDBSOutputProto {}
message DiskCopyOutputProto {}
message NsiOutputProto {}
message ImageWriterProto {
optional string filename = 1 [(help) = "filename of output sector image"];
@@ -14,6 +15,7 @@ message ImageWriterProto {
D64OutputProto d64 = 3;
LDBSOutputProto ldbs = 4;
DiskCopyOutputProto diskcopy = 5;
NsiOutputProto nsi = 6;
}
}

View File

@@ -1,78 +1,70 @@
#include "globals.h"
#include "flags.h"
#include "dataspec.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
class NsiImageWriter : public ImageWriter
{
public:
NSIImageWriter(const SectorSet& sectors, const ImageSpec& spec):
ImageWriter(sectors, spec)
NsiImageWriter(const ImageWriterProto& config):
ImageWriter(config)
{}
void writeImage()
void writeImage(const SectorSet& sectors)
{
unsigned numCylinders = spec.cylinders;
unsigned numHeads = spec.heads;
unsigned numSectors = spec.sectors;
unsigned numTracks = numCylinders * numHeads;
unsigned numBytes = spec.bytes;
int head;
unsigned autoTracks;
unsigned autoSides;
unsigned autoSectors;
unsigned autoBytes;
sectors.calculateSize(autoTracks, autoSides, autoSectors, autoBytes);
size_t trackSize = numSectors * numBytes;
if ((numBytes != 256) && (numBytes != 512) && (numBytes != 257) && (numBytes != 513))
Error() << "Sector size must be 256 or 512.";
if (numCylinders != 35)
Error() << "Number of cylinders must be 35.";
size_t trackSize = autoSectors * autoBytes;
std::cout << fmt::format("Writing {} cylinders, {} heads, {} sectors, {} ({} bytes/sector), {} kB total",
numCylinders, numHeads,
numSectors, numBytes == 256 ? "SD" : "DD", numBytes,
numTracks * trackSize / 1024)
autoTracks, autoSides,
autoSectors, autoBytes == 256 ? "SD" : "DD", autoBytes,
autoTracks * trackSize / 1024)
<< std::endl;
std::ofstream outputFile(spec.filename, std::ios::out | std::ios::binary);
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 < numCylinders * numHeads; track++)
for (int track = 0; track < autoTracks * autoSides; track++)
{
head = (track < numCylinders) ? 0 : 1;
for (int sectorId = 0; sectorId < numSectors; sectorId++)
int head = (track < autoTracks) ? 0 : 1;
for (int sectorId = 0; sectorId < autoSectors; sectorId++)
{
const auto& sector = sectors.get(track % numCylinders, head, 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 * numBytes;
sectorFileOffset = track * trackSize + sectorId * autoBytes;
}
else { /* Side 1 is from track 70-35 */
sectorFileOffset = (numBytes * numSectors * numCylinders) + /* Skip over side 0 */
((numCylinders - 1) - (track % numCylinders)) * (numBytes * numSectors) +
(sectorId * numBytes); /* Sector offset from beginning of track. */
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, numBytes).writeTo(outputFile);
sector->data.slice(0, autoBytes).writeTo(outputFile);
}
}
}
}
};
std::unique_ptr<ImageWriter> ImageWriter::createNSIImageWriter(
const SectorSet& sectors, const ImageSpec& spec)
std::unique_ptr<ImageWriter> ImageWriter::createNsiImageWriter(
const ImageWriterProto& config)
{
return std::unique_ptr<ImageWriter>(new NSIImageWriter(sectors, spec));
return std::unique_ptr<ImageWriter>(new NsiImageWriter(config));
}

View File

@@ -272,6 +272,7 @@ buildproto libproto.a \
arch/macintosh/macintosh.proto \
arch/micropolis/micropolis.proto \
arch/mx/mx.proto \
arch/northstar/northstar.proto \
arch/tids990/tids990.proto \
arch/victor9k/victor9k.proto \
arch/zilogmcz/zilogmcz.proto \
@@ -305,6 +306,8 @@ buildlibrary libbackend.a \
arch/macintosh/encoder.cc \
arch/micropolis/decoder.cc \
arch/mx/decoder.cc \
arch/northstar/decoder.cc \
arch/northstar/encoder.cc \
arch/tids990/decoder.cc \
arch/tids990/encoder.cc \
arch/victor9k/decoder.cc \
@@ -342,11 +345,13 @@ buildlibrary libbackend.a \
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 \
@@ -365,8 +370,8 @@ READABLES="\
acornadfs \
acorndfs \
aeslanier \
ampro \
amiga \
ampro \
apple2 \
atarist \
brother \
@@ -379,6 +384,7 @@ READABLES="\
macintosh \
micropolis \
mx \
northstar \
tids990 \
victor9k \
zilogmcz \
@@ -386,17 +392,6 @@ READABLES="\
WRITABLES="\
amiga \
brother120 \
brother240 \
ibm1440 \
ibm720 \
ibm180_525 \
ibm360_525 \
ibm720_525 \
ibm1200_525 \
commodore1541 \
commodore1581 \
hplif770 \
atarist360 \
atarist370 \
atarist400 \
@@ -405,7 +400,21 @@ WRITABLES="\
atarist740 \
atarist800 \
atarist820 \
brother120 \
brother240 \
commodore1541 \
commodore1581 \
hplif770 \
ibm1200_525 \
ibm1440 \
ibm180_525 \
ibm360_525 \
ibm720 \
ibm720_525 \
macintosh \
northstar87 \
northstar175 \
northstar350 \
tids990 \
"

View File

@@ -1,20 +0,0 @@
#include "globals.h"
#include "flags.h"
#include "decoders/decoders.h"
#include "northstar/northstar.h"
#include "writer.h"
static FlagGroup flags { &writerFlags, &northstarEncoderFlags };
int mainWriteNorthstar(int argc, const char* argv[])
{
setWriterDefaultDest(":t=0-34");
setWriterHardSectorCount(10);
flags.parseFlags(argc, argv);
NorthstarEncoder encoder;
writeDiskCommand(encoder);
return 0;
}

View File

@@ -0,0 +1,31 @@
comment: 'Northstar 87kB/175kB/350kB 5.25" 35-track 10-sector hard sectored'
input {
flux {
drive {
hard_sector_count: 10
}
}
}
output {
image {
filename: "northstar.nsi"
nsi {}
}
}
decoder {
micropolis {}
}
cylinders {
start: 0
end: 34
}
heads {
start: 0
end: 1
}

View File

@@ -0,0 +1,30 @@
comment: 'Northstar 175kB 5.25" 35-track single-sided double-density hard-sectored'
input {
image {
filename: "northstar.nsi"
nsi {}
}
}
output {
flux {
drive {}
}
}
encoder {
northstar {}
}
cylinders {
start: 0
end: 39
}
heads {
start: 0
end: 0
}

View File

@@ -0,0 +1,29 @@
comment: 'Northstar 350kB 5.25" 35-track double-sided double-density hard-sectored'
input {
image {
filename: "northstar.nsi"
nsi {}
}
}
output {
flux {
drive {}
}
}
encoder {
northstar {}
}
cylinders {
start: 0
end: 39
}
heads {
start: 0
end: 1
}

View File

@@ -0,0 +1,29 @@
comment: 'Northstar 87kB 5.25" 35-track single-sided single-density hard-sectored'
input {
image {
filename: "northstar.nsi"
nsi {}
}
}
output {
flux {
drive {}
}
}
encoder {
northstar {}
}
cylinders {
start: 0
end: 39
}
heads {
start: 0
end: 0
}