Merge branch 'northstar' of https://github.com/hharte/fluxengine into hharte-northstar

This commit is contained in:
David Given
2021-05-24 21:39:51 +02:00
10 changed files with 684 additions and 8 deletions

View File

@@ -79,6 +79,7 @@ CY_ISR(index_irq_cb)
* the track. */
static bool hardsec_index_irq_primed = false;
static uint32_t hardsec_last_pulse_time = 0;
uint32_t index_pulse_duration = clock - hardsec_last_pulse_time;
if (!hardsec_index_threshold)
{
@@ -87,12 +88,18 @@ CY_ISR(index_irq_cb)
}
else
{
index_irq = hardsec_index_irq_primed;
/* It's only an index pulse if the previous pulse is less than
* the threshold.
*/
index_irq = (index_pulse_duration <= hardsec_index_threshold) ?
hardsec_index_irq_primed : false;
if (index_irq)
hardsec_index_irq_primed = false;
else
hardsec_index_irq_primed =
clock - hardsec_last_pulse_time <= hardsec_index_threshold;
index_pulse_duration <= hardsec_index_threshold;
hardsec_last_pulse_time = clock;
}
@@ -275,7 +282,6 @@ static void seek_to(int track)
CyWdtClear();
}
CyDelay(STEP_SETTLING_TIME);
TK43_REG_Write(track < 43); /* high if 0..42, low if 43 or up */
print("finished seek");
}
@@ -305,7 +311,7 @@ static void cmd_measure_speed(struct measurespeed_frame* f)
while (!index_irq)
{
elapsed = clock - start_clock;
if (elapsed > 1000)
if (elapsed > 1500)
{
elapsed = 0;
break;
@@ -416,7 +422,6 @@ static void cmd_read(struct read_frame* f)
seek_to(current_track);
SIDE_REG_Write(f->side);
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
/* Do slow setup *before* we go into the real-time bit. */
{
@@ -562,7 +567,6 @@ static void cmd_write(struct write_frame* f)
seek_to(current_track);
SIDE_REG_Write(f->side);
STEP_REG_Write(f->side); /* for drives which multiplex SIDE and DIR */
SEQUENCER_CONTROL_Write(1); /* put the sequencer into reset */
{
uint8_t i = CyEnterCriticalSection();
@@ -627,7 +631,6 @@ static void cmd_write(struct write_frame* f)
/* Wait for the index marker. While this happens, the DMA engine
* will prime the FIFO. */
hardsec_index_threshold = f->hardsec_threshold_ms;
index_irq = false;
while (!index_irq)
@@ -693,7 +696,7 @@ abort:
static void cmd_erase(struct erase_frame* f)
{
SIDE_REG_Write(f->side);
seek_to(current_track);
seek_to(current_track);
/* Disk is now spinning. */
print("start erasing");

175
arch/northstar/decoder.cc Normal file
View File

@@ -0,0 +1,175 @@
/* Decoder for North Star 10-sector hard-sectored disks.
*
* Supports both single- and double-density. For the sector format and
* checksum algorithm, see pp. 33 of the North Star Double Density Controller
* manual:
*
* http://bitsavers.org/pdf/northstar/boards/Northstar_MDS-A-D_1978.pdf
*
* North Star disks do not contain any track/head/sector information
* encoded in the sector record. For this reason, we have to be absolutely
* sure that the hardSectorId is correct.
*/
#include "globals.h"
#include "fluxmap.h"
#include "decoders/fluxmapreader.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "northstar.h"
#include "bytes.h"
#include "fmt/format.h"
/*
* MFM sectors have 32 bytes of 00's followed by two sync characters,
* specified in the North Star MDS manual as 0xFBFB.
*
* This is true for most disks; however, I found a few disks, including an
* original North Star DOS/BASIC v2.2.1 DQ disk) that uses 0xFBnn, where
* nn is an incrementing pattern.
*
* 00 00 00 F B
* 0000 0000 0000 0000 0000 0000 0101 0101 0100 0101
* A A A A A A 5 5 4 5
*/
static const FluxPattern MFM_PATTERN(64, 0xAAAAAAAAAAAA5545LL);
/* FM sectors have 16 bytes of 00's followed by 0xFB.
* 00 FB
* 0000 0000 1111 1111 1110 1111
* A A F F E F
*/
static const FluxPattern FM_PATTERN(64, 0xAAAAAAAAAAAAFFEFLL);
const FluxMatchers ANY_SECTOR_PATTERN(
{
&MFM_PATTERN,
&FM_PATTERN,
}
);
/* Search for FM or MFM sector record */
AbstractDecoder::RecordType NorthstarDecoder::advanceToNextRecord()
{
nanoseconds_t now = _fmr->tell().ns();
/* For all but the first sector, seek to the next sector pulse.
* The first sector does not contain the sector pulse in the fluxmap.
*/
if (now != 0) {
_fmr->seekToIndexMark();
now = _fmr->tell().ns();
}
/* Discard a possible partial sector at the end of the track.
* This partial sector could be mistaken for a conflicted sector, if
* whatever data read happens to match the checksum of 0, which is
* rare, but has been observed on some disks.
*/
if (now > (_fmr->getDuration() - 21e6)) {
_fmr->seekToIndexMark();
return(UNKNOWN_RECORD);
}
int msSinceIndex = std::round(now / 1e6);
const FluxMatcher* matcher = nullptr;
/* Note that the seekToPattern ignores the sector pulses, so if
* a sector is not found for some reason, the seek will advance
* past one or more sector pulses. For this reason, calculate
* _hardSectorId after the sector header is found.
*/
_sector->clock = _fmr->seekToPattern(ANY_SECTOR_PATTERN, matcher);
int sectorFoundTimeRaw = std::round((_fmr->tell().ns()) / 1e6);
int sectorFoundTime;
/* Round time to the nearest 20ms */
if ((sectorFoundTimeRaw % 20) < 10) {
sectorFoundTime = (sectorFoundTimeRaw / 20) * 20;
}
else {
sectorFoundTime = ((sectorFoundTimeRaw + 20) / 20) * 20;
}
/* Calculate the sector ID based on time since the index */
_hardSectorId = (sectorFoundTime / 20) % 10;
// std::cout << fmt::format(
// "Sector ID {}: hole at {}ms, sector start at {}ms",
// _hardSectorId, msSinceIndex, sectorFoundTimeRaw) << std::endl;
if (matcher == &MFM_PATTERN) {
_sectorType = SECTOR_TYPE_MFM;
readRawBits(48);
return SECTOR_RECORD;
}
if (matcher == &FM_PATTERN) {
_sectorType = SECTOR_TYPE_FM;
readRawBits(48);
return SECTOR_RECORD;
}
return UNKNOWN_RECORD;
}
/* Checksum is initially 0.
* For each data byte, XOR with the current checksum.
* Rotate checksum left, carrying bit 7 to bit 0.
*/
uint8_t northstarChecksum(const Bytes& bytes) {
ByteReader br(bytes);
uint8_t checksum = 0;
while (!br.eof()) {
checksum ^= br.read_8();
checksum = ((checksum << 1) | ((checksum >> 7)));
}
return checksum;
}
void NorthstarDecoder::decodeSectorRecord()
{
unsigned recordSize, payloadSize, headerSize;
if (_sectorType == SECTOR_TYPE_MFM) {
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_DD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_DD;
headerSize = NORTHSTAR_HEADER_SIZE_DD;
}
else {
recordSize = NORTHSTAR_ENCODED_SECTOR_SIZE_SD;
payloadSize = NORTHSTAR_PAYLOAD_SIZE_SD;
headerSize = NORTHSTAR_HEADER_SIZE_SD;
}
auto rawbits = readRawBits(recordSize * 16);
auto bytes = decodeFmMfm(rawbits).slice(0, recordSize);
ByteReader br(bytes);
uint8_t sync_char;
_sector->logicalSide = _sector->physicalSide;
_sector->logicalSector = _hardSectorId;
_sector->logicalTrack = _sector->physicalTrack;
sync_char = br.read_8(); /* Sync char: 0xFB */
if (_sectorType == SECTOR_TYPE_MFM) {
sync_char = br.read_8();/* MFM second Sync char, usually 0xFB */
}
_sector->data = br.read(payloadSize);
uint8_t wantChecksum = br.read_8();
uint8_t gotChecksum = northstarChecksum(bytes.slice(headerSize, payloadSize));
_sector->status = (wantChecksum == gotChecksum) ? Sector::OK : Sector::BAD_CHECKSUM;
}
std::set<unsigned> NorthstarDecoder::requiredSectors(Track& track) const
{
static std::set<unsigned> sectors = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
return sectors;
}

132
arch/northstar/encoder.cc Normal file
View File

@@ -0,0 +1,132 @@
#include "globals.h"
#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
#define PRE_HEADER_GAP_FILL_SIZE_DD 16
#define GAP1_FILL_BYTE (0x4F)
#define GAP2_FILL_BYTE (0x4F)
#define TOTAL_SECTOR_BYTES ()
static void write_sector(std::vector<bool>& bits, unsigned& cursor, const Sector* sector)
{
int preambleSize = 0;
int encodedSectorSize = 0;
int gapFillSize = 0;
int preHeaderGapFillSize = 0;
bool doubleDensity;
switch (sector->data.size()) {
case NORTHSTAR_PAYLOAD_SIZE_SD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_SD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_SD + NORTHSTAR_ENCODED_SECTOR_SIZE_SD + GAP_FILL_SIZE_SD;
gapFillSize = GAP_FILL_SIZE_SD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_SD;
doubleDensity = false;
break;
case NORTHSTAR_PAYLOAD_SIZE_DD:
preambleSize = NORTHSTAR_PREAMBLE_SIZE_DD;
encodedSectorSize = PRE_HEADER_GAP_FILL_SIZE_DD + NORTHSTAR_ENCODED_SECTOR_SIZE_DD + GAP_FILL_SIZE_DD;
gapFillSize = GAP_FILL_SIZE_DD;
preHeaderGapFillSize = PRE_HEADER_GAP_FILL_SIZE_DD;
doubleDensity = true;
break;
default:
Error() << "unsupported sector size --- you must pick 256 or 512";
break;
}
int fullSectorSize = preambleSize + encodedSectorSize;
auto fullSector = std::make_shared<std::vector<uint8_t>>();
fullSector->reserve(fullSectorSize);
/* sector gap after index pulse */
for (int i = 0; i < preHeaderGapFillSize; i++)
fullSector->push_back(GAP1_FILL_BYTE);
/* sector preamble */
for (int i = 0; i < preambleSize; i++)
fullSector->push_back(0);
Bytes sectorData;
if (sector->data.size() == encodedSectorSize)
sectorData = sector->data;
else {
ByteWriter writer(sectorData);
writer.write_8(0xFB); /* sync character */
if (doubleDensity == true) {
writer.write_8(0xFB); /* Double-density has two sync characters */
}
writer += sector->data;
if (doubleDensity == true) {
writer.write_8(northstarChecksum(sectorData.slice(2)));
} else {
writer.write_8(northstarChecksum(sectorData.slice(1)));
}
}
for (uint8_t b : sectorData)
fullSector->push_back(b);
if (sector->logicalSector != 9) {
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
if (fullSector->size() != fullSectorSize)
Error() << "sector mismatched length (" << sector->data.size() << ") expected: " << fullSector->size() << " got " << fullSectorSize;
} else {
/* sector postamble */
for (int i = 0; i < gapFillSize; i++)
fullSector->push_back(GAP2_FILL_BYTE);
}
bool lastBit = false;
if (doubleDensity == true) {
encodeMfm(bits, cursor, fullSector, lastBit);
}
else {
encodeFm(bits, cursor, fullSector);
}
}
std::unique_ptr<Fluxmap> NorthstarEncoder::encode(
int physicalTrack, int physicalSide, const SectorSet& allSectors)
{
int bitsPerRevolution = 100000;
double clockRateUs = 4.00;
if ((physicalTrack < 0) || (physicalTrack >= 35))
return std::unique_ptr<Fluxmap>();
const auto& sector = allSectors.get(physicalTrack, physicalSide, 0);
if (sector->data.size() == NORTHSTAR_PAYLOAD_SIZE_SD) {
bitsPerRevolution /= 2; // FM
} else {
clockRateUs /= 2.00;
}
std::vector<bool> bits(bitsPerRevolution);
unsigned cursor = 0;
for (int sectorId = 0; sectorId < 10; sectorId++)
{
const auto& sectorData = allSectors.get(physicalTrack, physicalSide, sectorId);
write_sector(bits, cursor, sectorData);
}
if (cursor > bits.size())
Error() << "track data overrun";
std::unique_ptr<Fluxmap> fluxmap(new Fluxmap);
fluxmap->appendBits(bits, clockRateUs * 1e3);
return fluxmap;
}

View File

@@ -0,0 +1,60 @@
#ifndef NORTHSTAR_H
#define NORTHSTAR_H
/* Northstar floppies are 10-hard sectored disks with a sector format as follows:
*
* |----------------------------------|
* | SYNC Byte | Payload | Checksum |
* |------------+----------+----------|
* | 1 (0xFB) | 256 (SD) | 1 |
* | 2 (0xFBFB) | 512 (DD) | |
* |----------------------------------|
*
*/
#include "decoders/decoders.h"
#include "encoders/encoders.h"
#define NORTHSTAR_PREAMBLE_SIZE_SD (16)
#define NORTHSTAR_PREAMBLE_SIZE_DD (32)
#define NORTHSTAR_HEADER_SIZE_SD (1)
#define NORTHSTAR_HEADER_SIZE_DD (2)
#define NORTHSTAR_PAYLOAD_SIZE_SD (256)
#define NORTHSTAR_PAYLOAD_SIZE_DD (512)
#define NORTHSTAR_CHECKSUM_SIZE (1)
#define NORTHSTAR_ENCODED_SECTOR_SIZE_SD (NORTHSTAR_HEADER_SIZE_SD + NORTHSTAR_PAYLOAD_SIZE_SD + NORTHSTAR_CHECKSUM_SIZE)
#define NORTHSTAR_ENCODED_SECTOR_SIZE_DD (NORTHSTAR_HEADER_SIZE_DD + NORTHSTAR_PAYLOAD_SIZE_DD + NORTHSTAR_CHECKSUM_SIZE)
#define SECTOR_TYPE_MFM (0)
#define SECTOR_TYPE_FM (1)
class NorthstarDecoder : public AbstractDecoder
{
public:
NorthstarDecoder()
{
_sectorType = SECTOR_TYPE_MFM;
}
virtual ~NorthstarDecoder() {}
RecordType advanceToNextRecord();
void decodeSectorRecord();
std::set<unsigned> requiredSectors(Track& track) const;
private:
uint8_t _sectorType;
uint8_t _hardSectorId;
};
class NorthstarEncoder : public AbstractEncoder
{
public:
virtual ~NorthstarEncoder() {}
std::unique_ptr<Fluxmap> encode(int physicalTrack, int physicalSide, const SectorSet& allSectors);
};
extern FlagGroup northstarEncoderFlags;
extern uint8_t northstarChecksum(const Bytes& bytes);
#endif /* NORTHSTAR */

72
doc/disk-northstar.md Normal file
View File

@@ -0,0 +1,72 @@
Disk: Northstar
================
Northstar Floppy disks use 10-sector hard sectored disks with either FM or MFM
encoding. They may be single- or double-sided. Each of the 10 sectors contains
256 (FM) or 512 (MFM) bytes of data. The disk has 35 cylinders, with tracks 0-
34 on side 0, and tracks 35-69 on side 1. Tracks on side 1 are numbered "back-
wards" in that track 35 corresponds to cylinder 34, side 1, and track 69
corresponds to cylinder 0, side 1.
The Northstar sector format does not include any head positioning information.
As such, reads from Northstar floppies need to by synchronized with the index
pulse, in order to properly identify the sector being read. This is handled
automatically by FluxEngine.
Due to the nature of the track ordering on side 1, an .nsi image reader and
writer are provided for double-sided disks. The .nsi image writer supports
both single- and double-sided disks; however single-sided .nsi images are
equivalent to .img images.
Reading disks
-------------
You must use a 48-TPI (40-track) 300RPM 5.25" floppy drive.
To read a double-sided North Star floppy, run:
```
fluxengine read northstar
```
To read a single-sided North Star floppy, run:
```
fluxengine read northstar -s:0
```
You should end up with a `northstar.nsi` with a file size dependent on the floppy
disk type:
| Disk Type | File Size (bytes) |
| ----------------------------------- | ------- |
| Single-Sided, Single-Density (SSSD) | 89,600 |
| Single-Sided, Double-Density (SSDD) | 179,200 |
| Double-Sided, Double-Density (DSDD) | 358,400 |
Writing disks
-------------
You must use a 48-TPI (40-track) 300RPM 5.25" floppy drive and make
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
```
Useful references
-----------------
- [MICRO-DISK SYSTEM MDS-A-D DOUBLE DENSITY Manual][northstar_mds].
Page 33 documents sector format for single- and double-density.
[northstar_mds]: http://bitsavers.org/pdf/northstar/boards/Northstar_MDS-A-D_1978.pdf

View File

@@ -88,6 +88,11 @@ public:
_pos = pos;
}
int getDuration(void)
{
return (_fluxmap.duration());
}
uint8_t getNextEvent(unsigned& ticks);
unsigned findEvent(uint8_t bits);
unsigned readInterval(nanoseconds_t clock); /* with debounce support */

View File

@@ -0,0 +1,104 @@
/* Image reader for Northstar floppy disk images */
#include "globals.h"
#include "flags.h"
#include "dataspec.h"
#include "sector.h"
#include "sectorset.h"
#include "imagereader/imagereader.h"
#include "fmt/format.h"
#include <algorithm>
#include <iostream>
#include <fstream>
class NsiImageReader : public ImageReader
{
public:
NsiImageReader(const ImageSpec& spec):
ImageReader(spec)
{}
SectorSet readImage()
{
std::ifstream inputFile(spec.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);
std::cout << "NSI: Autodetecting geometry based on file size: " << fsize << std::endl;
spec.cylinders = 35;
spec.sectors = 10;
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;
}
}
size_t trackSize = spec.sectors * spec.bytes;
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)
<< std::endl;
SectorSet sectors;
unsigned sectorFileOffset;
for (int head = 0; head < spec.heads; head++)
{
for (int track = 0; track < spec.cylinders; track++)
{
for (int sectorId = 0; sectorId < spec.sectors; sectorId++)
{
if (head == 0) { /* Head 0 is from track 0-34 */
sectorFileOffset = track * trackSize + sectorId * spec.bytes;
}
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. */
}
inputFile.seekg(sectorFileOffset, std::ios::beg);
Bytes data(spec.bytes);
inputFile.read((char*) data.begin(), spec.bytes);
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 ImageSpec& spec)
{
return std::unique_ptr<ImageReader>(new NsiImageReader(spec));
}

View File

@@ -0,0 +1,78 @@
#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 <algorithm>
#include <iostream>
#include <fstream>
class NSIImageWriter : public ImageWriter
{
public:
NSIImageWriter(const SectorSet& sectors, const ImageSpec& spec):
ImageWriter(sectors, spec)
{}
void writeImage()
{
unsigned numCylinders = spec.cylinders;
unsigned numHeads = spec.heads;
unsigned numSectors = spec.sectors;
unsigned numTracks = numCylinders * numHeads;
unsigned numBytes = spec.bytes;
int head;
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.";
std::cout << fmt::format("Writing {} cylinders, {} heads, {} sectors, {} ({} bytes/sector), {} kB total",
numCylinders, numHeads,
numSectors, numBytes == 256 ? "SD" : "DD", numBytes,
numTracks * trackSize / 1024)
<< std::endl;
std::ofstream outputFile(spec.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++)
{
head = (track < numCylinders) ? 0 : 1;
for (int sectorId = 0; sectorId < numSectors; sectorId++)
{
const auto& sector = sectors.get(track % numCylinders, head, sectorId);
if (sector)
{
if (head == 0) { /* Side 0 is from track 0-34 */
sectorFileOffset = track * trackSize + sectorId * numBytes;
}
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. */
}
outputFile.seekp(sectorFileOffset, std::ios::beg);
sector->data.slice(0, numBytes).writeTo(outputFile);
}
}
}
}
};
std::unique_ptr<ImageWriter> ImageWriter::createNSIImageWriter(
const SectorSet& sectors, const ImageSpec& spec)
{
return std::unique_ptr<ImageWriter>(new NSIImageWriter(sectors, spec));
}

27
src/fe-readnorthstar.cc Normal file
View File

@@ -0,0 +1,27 @@
#include "globals.h"
#include "flags.h"
#include "reader.h"
#include "fluxmap.h"
#include "encoders/encoders.h"
#include "decoders/decoders.h"
#include "sector.h"
#include "sectorset.h"
#include "record.h"
#include "northstar/northstar.h"
#include "fmt/format.h"
static FlagGroup flags { &readerFlags };
int mainReadNorthstar(int argc, const char* argv[])
{
setReaderDefaultSource(":t=0-34");
setReaderDefaultOutput("northstar.nsi");
setReaderHardSectorCount(10);
setReaderFluxSourceSynced(true);
flags.parseFlags(argc, argv);
NorthstarDecoder decoder;
readDiskCommand(decoder);
return 0;
}

20
src/fe-writenorthstar.cc Normal file
View File

@@ -0,0 +1,20 @@
#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;
}