mirror of
https://github.com/davidgiven/fluxengine.git
synced 2025-10-31 11:17:01 -07:00
Merge from master.
This commit is contained in:
78
lib/build.mk
Normal file
78
lib/build.mk
Normal file
@@ -0,0 +1,78 @@
|
||||
LIBFLUXENGINE_SRCS = \
|
||||
lib/decoders/fluxdecoder.cc \
|
||||
lib/decoders/fluxmapreader.cc \
|
||||
lib/decoders/fmmfm.cc \
|
||||
lib/decoders/decoders.cc \
|
||||
lib/encoders/encoders.cc \
|
||||
lib/fluxsink/aufluxsink.cc \
|
||||
lib/fluxsink/fl2fluxsink.cc \
|
||||
lib/fluxsink/fluxsink.cc \
|
||||
lib/fluxsink/hardwarefluxsink.cc \
|
||||
lib/fluxsink/scpfluxsink.cc \
|
||||
lib/fluxsink/vcdfluxsink.cc \
|
||||
lib/fluxsource/cwffluxsource.cc \
|
||||
lib/fluxsource/erasefluxsource.cc \
|
||||
lib/fluxsource/fl2fluxsource.cc \
|
||||
lib/fluxsource/fluxsource.cc \
|
||||
lib/fluxsource/hardwarefluxsource.cc \
|
||||
lib/fluxsource/kryoflux.cc \
|
||||
lib/fluxsource/kryofluxfluxsource.cc \
|
||||
lib/fluxsource/scpfluxsource.cc \
|
||||
lib/fluxsource/testpatternfluxsource.cc \
|
||||
lib/imagereader/d64imagereader.cc \
|
||||
lib/imagereader/d88imagereader.cc \
|
||||
lib/imagereader/dimimagereader.cc \
|
||||
lib/imagereader/diskcopyimagereader.cc \
|
||||
lib/imagereader/fdiimagereader.cc \
|
||||
lib/imagereader/imagereader.cc \
|
||||
lib/imagereader/imdimagereader.cc \
|
||||
lib/imagereader/imgimagereader.cc \
|
||||
lib/imagereader/jv3imagereader.cc \
|
||||
lib/imagereader/nfdimagereader.cc \
|
||||
lib/imagereader/nsiimagereader.cc \
|
||||
lib/imagereader/td0imagereader.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/imagewriter/rawimagewriter.cc \
|
||||
lib/imagewriter/d88imagewriter.cc \
|
||||
lib/usb/fluxengineusb.cc \
|
||||
lib/usb/greaseweazle.cc \
|
||||
lib/usb/greaseweazleusb.cc \
|
||||
lib/usb/serial.cc \
|
||||
lib/usb/usb.cc \
|
||||
lib/usb/usbfinder.cc \
|
||||
lib/ldbs.cc \
|
||||
lib/logger.cc \
|
||||
lib/proto.cc \
|
||||
lib/bitmap.cc \
|
||||
lib/bytes.cc \
|
||||
lib/crc.cc \
|
||||
lib/fluxmap.cc \
|
||||
lib/readerwriter.cc \
|
||||
lib/sector.cc \
|
||||
lib/csvreader.cc \
|
||||
lib/globals.cc \
|
||||
lib/utils.cc \
|
||||
lib/flags.cc \
|
||||
lib/hexdump.cc \
|
||||
lib/mapper.cc \
|
||||
lib/image.cc \
|
||||
lib/imginputoutpututils.cc \
|
||||
|
||||
LIBFLUXENGINE_OBJS = $(patsubst %.cc, $(OBJDIR)/%.o, $(LIBFLUXENGINE_SRCS))
|
||||
$(LIBFLUXENGINE_SRCS): | $(PROTO_HDRS)
|
||||
LIBFLUXENGINE_LIB = $(OBJDIR)/libfluxengine.a
|
||||
LIBFLUXENGINE_CFLAGS =
|
||||
LIBFLUXENGINE_LDFLAGS = $(LIBFLUXENGINE_LIB)
|
||||
|
||||
$(LIBFLUXENGINE_LIB): $(LIBFLUXENGINE_OBJS)
|
||||
|
||||
$(LIBFLUXENGINE_OBJS): CFLAGS += $(LIBARCH_CFLAGS)
|
||||
$(LIBFLUXENGINE_OBJS): CFLAGS += $(LIBUSBP_CFLAGS)
|
||||
$(LIBFLUXENGINE_OBJS): CFLAGS += $(PROTO_CFLAGS)
|
||||
|
||||
$(call use-pkgconfig, $(LIBFLUXENGINE_LIB), $(LIBFLUXENGINE_OBJS), fmt)
|
||||
@@ -19,7 +19,7 @@ import "arch/zilogmcz/zilogmcz.proto";
|
||||
import "lib/fluxsink/fluxsink.proto";
|
||||
import "lib/common.proto";
|
||||
|
||||
//NEXT: 29
|
||||
//NEXT: 30
|
||||
message DecoderProto {
|
||||
optional double pulse_debounce_threshold = 1 [default = 0.30,
|
||||
(help) = "ignore pulses with intervals shorter than this, in fractions of a clock"];
|
||||
@@ -61,5 +61,7 @@ message DecoderProto {
|
||||
(help) = "how many times to retry each track in the event of a read failure"];
|
||||
optional string write_csv_to = 23
|
||||
[(help) = "if set, write a CSV report of the disk state"];
|
||||
optional bool skip_unnecessary_tracks = 29 [default = true,
|
||||
(help) = "don't read tracks if we already have all necessary sectors"];
|
||||
}
|
||||
|
||||
|
||||
@@ -12,44 +12,44 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
static unsigned getModulationandSpeed(uint8_t flags, bool *mfm)
|
||||
static unsigned getModulationandSpeed(uint8_t flags, bool *fm)
|
||||
{
|
||||
switch (flags)
|
||||
{
|
||||
case 0: /* 500 kbps FM */
|
||||
//clockRateKhz.setDefaultValue(250);
|
||||
*mfm = false;
|
||||
*fm = true;
|
||||
return 500;
|
||||
break;
|
||||
|
||||
case 1: /* 300 kbps FM */
|
||||
*mfm = false;
|
||||
*fm = true;
|
||||
return 300;
|
||||
|
||||
break;
|
||||
|
||||
case 2: /* 250 kbps FM */
|
||||
*mfm = false;
|
||||
*fm = true;
|
||||
return 250;
|
||||
break;
|
||||
|
||||
case 3: /* 500 kbps MFM */
|
||||
*mfm = true;
|
||||
*fm = false;
|
||||
return 500;
|
||||
break;
|
||||
|
||||
case 4: /* 300 kbps MFM */
|
||||
*mfm = true;
|
||||
*fm = false;
|
||||
return 300;
|
||||
break;
|
||||
|
||||
case 5: /* 250 kbps MFM */
|
||||
*mfm = true;
|
||||
*fm = false;
|
||||
return 250;
|
||||
break;
|
||||
|
||||
default:
|
||||
Error() << fmt::format("don't understand IMD disks with this modulation and speed {}", flags);
|
||||
Error() << fmt::format("IMD: don't understand IMD disks with this modulation and speed {}", flags);
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
@@ -88,9 +88,7 @@ static unsigned getSectorSize(uint8_t flags)
|
||||
class IMDImageReader : public ImageReader
|
||||
{
|
||||
public:
|
||||
IMDImageReader(const ImageReaderProto& config):
|
||||
ImageReader(config)
|
||||
{}
|
||||
IMDImageReader(const ImageReaderProto& config): ImageReader(config) {}
|
||||
|
||||
std::unique_ptr<Image> readImage()
|
||||
/*
|
||||
@@ -115,9 +113,9 @@ public:
|
||||
//Read File
|
||||
std::ifstream inputFile(_config.filename(), std::ios::in | std::ios::binary);
|
||||
if (!inputFile.is_open())
|
||||
Error() << "cannot open input file";
|
||||
Error() << "IMD: cannot open input file";
|
||||
//define some variables
|
||||
bool mfm = false; //define coding just to show in comment for setting the right write parameters
|
||||
bool fm = 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);
|
||||
@@ -126,34 +124,41 @@ public:
|
||||
ByteReader br(data);
|
||||
std::unique_ptr<Image> image(new Image);
|
||||
TrackHeader header = {0, 0, 0, 0, 0};
|
||||
TrackHeader previousheader = {0, 0, 0, 0, 0};
|
||||
|
||||
unsigned n = 0;
|
||||
unsigned headerPtr = 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)
|
||||
{
|
||||
comment[n++] = (unsigned char)b;
|
||||
}
|
||||
headerPtr = n; //set pointer to after comment
|
||||
comment[n] = '\0'; // null-terminate the string
|
||||
//write comment to screen
|
||||
Logger() << fmt::format("IMD: comment: {}", comment);
|
||||
std::string sector_skew;
|
||||
|
||||
int b;
|
||||
std::string comment;
|
||||
bool blnOptionalCylinderMap = false;
|
||||
bool blnOptionalHeadMap = false;
|
||||
int trackSectorSize = -1;
|
||||
// Read comment
|
||||
comment.clear();
|
||||
while ((b = br.read_8()) != EOF && b != END_OF_FILE)
|
||||
{
|
||||
comment.push_back(b);
|
||||
n++;
|
||||
}
|
||||
headerPtr = n; //set pointer to after comment
|
||||
Logger() << "Comment in IMD file:"
|
||||
<< fmt::format("{}",
|
||||
comment);
|
||||
|
||||
//first read header
|
||||
for (;;)
|
||||
{
|
||||
if (headerPtr >= inputFileSize-1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
//first read header
|
||||
header.ModeValue = br.read_8();
|
||||
headerPtr++;
|
||||
Modulation_Speed = getModulationandSpeed(header.ModeValue, &mfm);
|
||||
Modulation_Speed = getModulationandSpeed(header.ModeValue, &fm);
|
||||
header.track = br.read_8();
|
||||
headerPtr++;
|
||||
header.Head = br.read_8();
|
||||
@@ -164,87 +169,239 @@ public:
|
||||
headerPtr++;
|
||||
sectorSize = getSectorSize(header.SectorSize);
|
||||
|
||||
//Read optional track map To Do
|
||||
|
||||
//Read optional sector head map To Do
|
||||
|
||||
unsigned optionalsector_map[header.numSectors];
|
||||
//The Sector Cylinder Map has one entry for each sector, and contains the logical Cylinder ID for the corresponding sector in the Sector Numbering Map.
|
||||
if (header.Head & SEC_CYL_MAP_FLAG)
|
||||
{
|
||||
//Read optional cylinder map
|
||||
for (b = 0; b < header.numSectors; b++)
|
||||
{
|
||||
optionalsector_map[b] = br.read_8();
|
||||
headerPtr++;
|
||||
}
|
||||
blnOptionalCylinderMap = true; //set bool so we know there is an optional cylinder map
|
||||
header.Head = header.Head^SEC_CYL_MAP_FLAG; //remove flag 10000001 ^ 10000000 = 00000001 and 10000000 ^ 10000000 = 00000000
|
||||
}
|
||||
//Read optional sector head map
|
||||
//The Sector Head Map has one entry for each sector, and contains the logical Head ID for the corresponding sector in the Sector Numbering Map.
|
||||
unsigned optionalhead_map[header.numSectors];
|
||||
if (header.Head & SEC_HEAD_MAP_FLAG)
|
||||
{
|
||||
//Read optional sector head map
|
||||
for (b = 0; b < header.numSectors; b++)
|
||||
{
|
||||
optionalhead_map[b] = br.read_8();
|
||||
headerPtr++;
|
||||
}
|
||||
blnOptionalHeadMap = true; //set bool so we know there is an optional head map
|
||||
header.Head = header.Head^SEC_HEAD_MAP_FLAG; //remove flag 01000001 ^ 01000001 = 00000001 and 01000000 ^ 0100000 = 00000000 for writing sector head later
|
||||
}
|
||||
//read sector numbering map
|
||||
std::vector<unsigned> sector_map(header.numSectors);
|
||||
sector_skew.clear();
|
||||
for (int b = 0; b < header.numSectors; b++)
|
||||
{
|
||||
sector_map[b] = br.read_8();
|
||||
headerPtr++;
|
||||
sector_skew.clear();
|
||||
for (b = 0; b < header.numSectors; b++)
|
||||
{
|
||||
uint8_t t;
|
||||
t = br.read_8();
|
||||
sector_skew.push_back(t);
|
||||
headerPtr++;
|
||||
}
|
||||
|
||||
auto ibm = config.mutable_encoder()->mutable_ibm();
|
||||
|
||||
auto trackdata = ibm->add_trackdata();
|
||||
trackdata->set_target_clock_period_us(1e3 / Modulation_Speed);
|
||||
trackdata->set_target_rotational_period_ms(200);
|
||||
if (trackSectorSize < 0)
|
||||
{
|
||||
trackSectorSize = sectorSize;
|
||||
// this is the first sector we've read, use it settings for
|
||||
// per-track data
|
||||
trackdata->set_track(header.track);
|
||||
trackdata->set_head(header.Head);
|
||||
trackdata->set_sector_size(sectorSize);
|
||||
trackdata->set_use_fm(fm);
|
||||
}
|
||||
else if (trackSectorSize != sectorSize)
|
||||
{
|
||||
Error() << "IMD: multiple sector sizes per track are "
|
||||
"currently unsupported";
|
||||
}
|
||||
auto sectors = trackdata->mutable_sectors();
|
||||
|
||||
//read the sectors
|
||||
for (int s = 0; s < header.numSectors; s++)
|
||||
{
|
||||
Bytes sectordata;
|
||||
const auto& sector = image->put(header.track, header.Head, sector_map[s]);
|
||||
Bytes compressed(sectorSize);
|
||||
int SectorID;
|
||||
SectorID = sector_skew[s];
|
||||
const auto& sector = image->put(header.track, header.Head, SectorID);
|
||||
sector->logicalSector = SectorID;
|
||||
//read the status of the sector
|
||||
unsigned int Status_Sector = br.read_8();
|
||||
headerPtr++;
|
||||
|
||||
switch (Status_Sector)
|
||||
{
|
||||
case 0: /* Sector data unavailable - could not be read */
|
||||
break;
|
||||
/*fluxengine knows of a few sector statussen but not all of the statussen in IMD.
|
||||
* // the statussen are in sector.h. Translation to fluxengine is as follows:
|
||||
* Statussen fluxengine | Status IMD
|
||||
*--------------------------------------------------------------------------------------------------------------------
|
||||
* OK, | 1, 2 (Normal data: (Sector Size) of (compressed) bytes follow)
|
||||
* BAD_CHECKSUM, | 5, 6, 7, 8
|
||||
* MISSING, sector not found | 0 (Sector data unavailable - could not be read)
|
||||
* DATA_MISSING, sector present but no data found | 3, 4
|
||||
* CONFLICT, |
|
||||
* INTERNAL_ERROR |
|
||||
*/
|
||||
case 0: /* Sector data unavailable - could not be read */
|
||||
|
||||
case 1: /* Normal data: (Sector Size) bytes follow */
|
||||
sectordata = br.read(sectorSize);
|
||||
headerPtr += sectorSize;
|
||||
sector->data.writer().append(sectordata);
|
||||
break;
|
||||
sector->status = Sector::MISSING;
|
||||
break;
|
||||
|
||||
case 2: /* Compressed: All bytes in sector have same value (xx) */
|
||||
sectordata = br.read(1);
|
||||
headerPtr++;
|
||||
sector->data.writer().append(sectordata);
|
||||
case 1: /* Normal data: (Sector Size) bytes follow */
|
||||
sectordata = br.read(sectorSize);
|
||||
headerPtr += sectorSize;
|
||||
sector->data.writer().append(sectordata);
|
||||
sector->status = Sector::OK;
|
||||
break;
|
||||
|
||||
for (int k = 1; k < sectorSize; k++)
|
||||
{
|
||||
//fill data till sector is full
|
||||
sector->data.writer().append(sectordata);
|
||||
}
|
||||
break;
|
||||
case 2: /* Compressed: All bytes in sector have same value (xx) */
|
||||
compressed[0] = br.read_8();
|
||||
headerPtr++;
|
||||
for (int k = 1; k < sectorSize; k++)
|
||||
{
|
||||
//fill data till sector is full
|
||||
br.seek(headerPtr);
|
||||
compressed[k] = br.read_8();
|
||||
}
|
||||
sector->data.writer().append(compressed);
|
||||
sector->status = Sector::OK;
|
||||
break;
|
||||
|
||||
case 3: /* Normal data with "Deleted-Data address mark" */
|
||||
case 4: /* Compressed with "Deleted-Data address mark"*/
|
||||
case 5: /* Normal data read with data error */
|
||||
case 6: /* Compressed read with data error" */
|
||||
case 7: /* Deleted data read with data error" */
|
||||
case 8: /* Compressed, Deleted read with data error" */
|
||||
default:
|
||||
Error() << fmt::format("don't understand IMD disks with sector status {}", Status_Sector);
|
||||
}
|
||||
sector->status = Sector::OK;
|
||||
sector->logicalTrack = header.track;
|
||||
sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(header.track);
|
||||
sector->logicalSide = sector->physicalHead = header.Head;
|
||||
sector->logicalSector = (sector_map[s]);
|
||||
case 3: /* Normal data with "Deleted-Data address mark" */
|
||||
sector->status = Sector::DATA_MISSING;
|
||||
sectordata = br.read(sectorSize);
|
||||
headerPtr += sectorSize;
|
||||
sector->data.writer().append(sectordata);
|
||||
break;
|
||||
|
||||
case 4: /* Compressed with "Deleted-Data address mark"*/
|
||||
compressed[0] = br.read_8();
|
||||
headerPtr++;
|
||||
for (int k = 1; k < sectorSize; k++)
|
||||
{
|
||||
//fill data till sector is full
|
||||
br.seek(headerPtr);
|
||||
compressed[k] = br.read_8();
|
||||
}
|
||||
sector->data.writer().append(compressed);
|
||||
sector->status = Sector::DATA_MISSING;
|
||||
break;
|
||||
|
||||
case 5: /* Normal data read with data error*/
|
||||
sectordata = br.read(sectorSize);
|
||||
headerPtr += sectorSize;
|
||||
sector->status = Sector::BAD_CHECKSUM;
|
||||
sector->data.writer().append(sectordata);
|
||||
break;
|
||||
|
||||
case 6: /* Compressed read with data error*/
|
||||
compressed[0] = br.read_8();
|
||||
headerPtr++;
|
||||
for (int k = 1; k < sectorSize; k++)
|
||||
{
|
||||
//fill data till sector is full
|
||||
br.seek(headerPtr);
|
||||
compressed[k] = br.read_8();
|
||||
}
|
||||
sector->data.writer().append(compressed);
|
||||
sector->status = Sector::BAD_CHECKSUM;
|
||||
break;
|
||||
|
||||
case 7: /* Deleted data read with data error*/
|
||||
sectordata = br.read(sectorSize);
|
||||
headerPtr += sectorSize;
|
||||
sector->status = Sector::BAD_CHECKSUM;
|
||||
sector->data.writer().append(sectordata);
|
||||
break;
|
||||
|
||||
case 8: /* Compressed, Deleted read with data error*/
|
||||
compressed[0] = br.read_8();
|
||||
headerPtr++;
|
||||
for (int k = 1; k < sectorSize; k++)
|
||||
{
|
||||
//fill data till sector is full
|
||||
br.seek(headerPtr);
|
||||
compressed[k] = br.read_8();
|
||||
}
|
||||
sector->data.writer().append(compressed);
|
||||
sector->status = Sector::BAD_CHECKSUM;
|
||||
break;
|
||||
|
||||
default:
|
||||
Error() << fmt::format("IMD: Don't understand IMD files with sector status {}, track {}, sector {}", Status_Sector, header.track, s);
|
||||
}
|
||||
if (blnOptionalCylinderMap) //there was een optional cylinder map. write it to the sector
|
||||
//The Sector Cylinder Map has one entry for each sector, and contains the logical Cylinder ID for the corresponding sector in the Sector Numbering Map.
|
||||
{
|
||||
sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(header.track);
|
||||
sector->logicalTrack = optionalsector_map[s];
|
||||
blnOptionalCylinderMap = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sector->logicalTrack = header.track;
|
||||
sector->physicalTrack = Mapper::remapTrackLogicalToPhysical(header.track);
|
||||
}
|
||||
if (blnOptionalHeadMap) //there was een optional head map. write it to the sector
|
||||
//The Sector Head Map has one entry for each sector, and contains the logical Head ID for the corresponding sector in the Sector Numbering Map.
|
||||
{
|
||||
sector->physicalHead = header.Head;
|
||||
sector->logicalSide = optionalhead_map[s];
|
||||
blnOptionalHeadMap = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sector->logicalSide = header.Head;
|
||||
sector->physicalHead = header.Head;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//Write format detected in IMD image to screen to help user set the right write parameters
|
||||
|
||||
if (config.encoder().format_case() != EncoderProto::FormatCase::FORMAT_NOT_SET)
|
||||
Logger() << "IMD: overriding configured format";
|
||||
|
||||
image->setGeometry({
|
||||
.numTracks = header.track,
|
||||
.numSides = header.Head + 1U,
|
||||
.numSectors = header.numSectors,
|
||||
.sectorSize = sectorSize
|
||||
});
|
||||
|
||||
size_t headSize = header.numSectors * sectorSize;
|
||||
size_t trackSize = headSize * (header.Head + 1);
|
||||
image->calculateSize();
|
||||
const Geometry& geometry = image->getGeometry();
|
||||
size_t headSize = ((header.numSectors) * (sectorSize));
|
||||
size_t trackSize = (headSize * (header.Head + 1));
|
||||
|
||||
Logger() << fmt::format("IMD: {} tracks, {} heads; {}; {} 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);
|
||||
|
||||
Logger() << "IMD: read "
|
||||
<< fmt::format("{} tracks, {} heads; {}; {} kbps; {} sectoren; sectorsize {}; {} kB total.",
|
||||
header.track + 1, header.Head + 1,
|
||||
fm ? "FM" : "MFM",
|
||||
Modulation_Speed, header.numSectors, sectorSize, (header.track+1) * trackSize / 1024);
|
||||
|
||||
if (!config.has_heads())
|
||||
{
|
||||
auto* heads = config.mutable_heads();
|
||||
heads->set_start(0);
|
||||
heads->set_end(geometry.numSides - 1);
|
||||
}
|
||||
|
||||
if (!config.has_tracks())
|
||||
{
|
||||
auto* tracks = config.mutable_tracks();
|
||||
tracks->set_start(0);
|
||||
tracks->set_end(geometry.numTracks - 1);
|
||||
}
|
||||
|
||||
return image;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
124
lib/imagewriter/d88imagewriter.cc
Normal file
124
lib/imagewriter/d88imagewriter.cc
Normal file
@@ -0,0 +1,124 @@
|
||||
#include "globals.h"
|
||||
#include "flags.h"
|
||||
#include "sector.h"
|
||||
#include "imagewriter/imagewriter.h"
|
||||
#include "image.h"
|
||||
#include "lib/config.pb.h"
|
||||
#include "imginputoutpututils.h"
|
||||
#include "fmt/format.h"
|
||||
#include "logger.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
static int countl_zero(uint32_t value)
|
||||
{
|
||||
int count = 0;
|
||||
while (!(value & 0x8000000))
|
||||
{
|
||||
value <<= 1;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
class D88ImageWriter : public ImageWriter
|
||||
{
|
||||
public:
|
||||
D88ImageWriter(const ImageWriterProto& config):
|
||||
ImageWriter(config)
|
||||
{}
|
||||
|
||||
void writeImage(const Image& image)
|
||||
{
|
||||
const Geometry geometry = image.getGeometry();
|
||||
|
||||
int tracks = geometry.numTracks;
|
||||
int sides = geometry.numSides;
|
||||
|
||||
std::ofstream outputFile(_config.filename(), std::ios::out | std::ios::binary);
|
||||
if (!outputFile.is_open())
|
||||
Error() << "cannot open output file";
|
||||
|
||||
Bytes header;
|
||||
ByteWriter headerWriter(header);
|
||||
for(int i = 0; i < 26; i++) {
|
||||
headerWriter.write_8(0x0); // image name + reserved bytes
|
||||
}
|
||||
headerWriter.write_8(0x00); // not write protected
|
||||
if (geometry.numTracks > 42) {
|
||||
headerWriter.write_8(0x20); // 2HD
|
||||
} else {
|
||||
headerWriter.write_8(0x00); // 2D
|
||||
}
|
||||
headerWriter.write_le32(0); // disk size (will be overridden at the end of writing)
|
||||
for (int i = 0; i < 164; i++) {
|
||||
headerWriter.write_le32(0); // track pointer (will be overridden in track loop)
|
||||
}
|
||||
header.writeTo(outputFile);
|
||||
|
||||
uint32_t trackOffset = 688;
|
||||
|
||||
for (int track = 0; track < geometry.numTracks * geometry.numSides; track++)
|
||||
{
|
||||
headerWriter.seek(0x20 + 4 * track);
|
||||
headerWriter.write_le32(trackOffset);
|
||||
int side = track & 1;
|
||||
std::vector<std::shared_ptr<const Sector>> sectors;
|
||||
for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++)
|
||||
{
|
||||
const auto& sector = image.get(track >> 1, side, sectorId);
|
||||
if (sector)
|
||||
{
|
||||
sectors.push_back(sector);
|
||||
}
|
||||
}
|
||||
std::sort(begin(sectors),
|
||||
end(sectors),
|
||||
[](std::shared_ptr<const Sector> a, std::shared_ptr<const Sector> b) { return a->position < b->position; });
|
||||
for (auto& sector : sectors)
|
||||
{
|
||||
Bytes sectorBytes;
|
||||
ByteWriter sectorWriter(sectorBytes);
|
||||
sectorWriter.write_8(sector->logicalTrack);
|
||||
sectorWriter.write_8(sector->logicalSide);
|
||||
sectorWriter.write_8(sector->logicalSector);
|
||||
sectorWriter.write_8(24 - countl_zero(uint32_t(sector->data.size())));
|
||||
sectorWriter.write_le16(sectors.size());
|
||||
sectorWriter.write_8(0x00); // always write mfm
|
||||
sectorWriter.write_8(0x00); // always write not deleted data
|
||||
if (sector->status == Sector::Status::BAD_CHECKSUM) {
|
||||
sectorWriter.write_8(0xB0);
|
||||
} else {
|
||||
sectorWriter.write_8(0x00);
|
||||
}
|
||||
sectorWriter.write_8(0x00); // reserved
|
||||
sectorWriter.write_8(0x00);
|
||||
sectorWriter.write_8(0x00);
|
||||
sectorWriter.write_8(0x00);
|
||||
sectorWriter.write_8(0x00);
|
||||
sectorWriter.write_le16(sector->data.size());
|
||||
sectorBytes.writeTo(outputFile);
|
||||
sector->data.writeTo(outputFile);
|
||||
trackOffset += sectorBytes.size();
|
||||
trackOffset += sector->data.size();
|
||||
}
|
||||
}
|
||||
|
||||
headerWriter.seek(0x1c);
|
||||
headerWriter.write_le32(outputFile.tellp());
|
||||
outputFile.seekp(0);
|
||||
header.writeTo(outputFile);
|
||||
|
||||
Logger() << fmt::format("D88: wrote {} tracks, {} sides, {} kB total",
|
||||
tracks, sides,
|
||||
outputFile.tellp() / 1024);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
std::unique_ptr<ImageWriter> ImageWriter::createD88ImageWriter(
|
||||
const ImageWriterProto& config)
|
||||
{
|
||||
return std::unique_ptr<ImageWriter>(new D88ImageWriter(config));
|
||||
}
|
||||
@@ -32,6 +32,9 @@ std::unique_ptr<ImageWriter> ImageWriter::create(const ImageWriterProto& config)
|
||||
case ImageWriterProto::kRaw:
|
||||
return ImageWriter::createRawImageWriter(config);
|
||||
|
||||
case ImageWriterProto::kD88:
|
||||
return ImageWriter::createD88ImageWriter(config);
|
||||
|
||||
default:
|
||||
Error() << "bad output image config";
|
||||
return std::unique_ptr<ImageWriter>();
|
||||
@@ -45,6 +48,7 @@ void ImageWriter::updateConfigForFilename(ImageWriterProto* proto, const std::st
|
||||
{".adf", [](auto* proto) { proto->mutable_img(); }},
|
||||
{".d64", [](auto* proto) { proto->mutable_d64(); }},
|
||||
{".d81", [](auto* proto) { proto->mutable_img(); }},
|
||||
{".d88", [](auto* proto) { proto->mutable_d88(); }},
|
||||
{".diskcopy", [](auto* proto) { proto->mutable_diskcopy(); }},
|
||||
{".dsk", [](auto* proto) { proto->mutable_img(); }},
|
||||
{".img", [](auto* proto) { proto->mutable_img(); }},
|
||||
|
||||
@@ -20,6 +20,7 @@ public:
|
||||
static std::unique_ptr<ImageWriter> createLDBSImageWriter(const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createNsiImageWriter(const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createRawImageWriter(const ImageWriterProto& config);
|
||||
static std::unique_ptr<ImageWriter> createD88ImageWriter(const ImageWriterProto& config);
|
||||
|
||||
public:
|
||||
void printMap(const Image& sectors);
|
||||
|
||||
@@ -30,6 +30,7 @@ message LDBSOutputProto {
|
||||
message DiskCopyOutputProto {}
|
||||
message NsiOutputProto {}
|
||||
message RawOutputProto {}
|
||||
message D88OutputProto {}
|
||||
|
||||
message ImageWriterProto {
|
||||
optional string filename = 1 [(help) = "filename of output sector image"];
|
||||
@@ -40,6 +41,7 @@ message ImageWriterProto {
|
||||
DiskCopyOutputProto diskcopy = 5;
|
||||
NsiOutputProto nsi = 6;
|
||||
RawOutputProto raw = 7;
|
||||
D88OutputProto d88 = 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,12 @@ enum ReadResult
|
||||
BAD_AND_CAN_NOT_RETRY
|
||||
};
|
||||
|
||||
enum BadSectorsState
|
||||
{
|
||||
HAS_NO_BAD_SECTORS,
|
||||
HAS_BAD_SECTORS
|
||||
};
|
||||
|
||||
/* In order to allow rereads in file-based flux sources, we need to persist the
|
||||
* FluxSourceIterator (as that's where the state for which read to return is
|
||||
* held). This class handles that. */
|
||||
@@ -108,8 +114,7 @@ static std::set<std::shared_ptr<const Sector>> collectSectors(
|
||||
return sector_set;
|
||||
}
|
||||
|
||||
/* Returns true if the result contains bad sectors. */
|
||||
bool combineRecordAndSectors(
|
||||
BadSectorsState combineRecordAndSectors(
|
||||
TrackFlux& trackFlux, AbstractDecoder& decoder, const Location& location)
|
||||
{
|
||||
std::set<std::shared_ptr<const Sector>> track_sectors;
|
||||
@@ -128,12 +133,12 @@ bool combineRecordAndSectors(
|
||||
|
||||
trackFlux.sectors = collectSectors(track_sectors);
|
||||
if (trackFlux.sectors.empty())
|
||||
return true;
|
||||
return HAS_BAD_SECTORS;
|
||||
for (const auto& sector : trackFlux.sectors)
|
||||
if (sector->status != Sector::OK)
|
||||
return true;
|
||||
return HAS_BAD_SECTORS;
|
||||
|
||||
return false;
|
||||
return HAS_NO_BAD_SECTORS;
|
||||
}
|
||||
|
||||
ReadResult readGroup(FluxSourceIteratorHolder& fluxSourceIteratorHolder,
|
||||
@@ -163,9 +168,13 @@ ReadResult readGroup(FluxSourceIteratorHolder& fluxSourceIteratorHolder,
|
||||
|
||||
auto trackdataflux = decoder.decodeToSectors(fluxmap, location);
|
||||
trackFlux.trackDatas.push_back(trackdataflux);
|
||||
if (!combineRecordAndSectors(trackFlux, decoder, location))
|
||||
return GOOD_READ;
|
||||
if (fluxSourceIterator.hasNext())
|
||||
if (combineRecordAndSectors(trackFlux, decoder, location) == HAS_NO_BAD_SECTORS)
|
||||
{
|
||||
result = GOOD_READ;
|
||||
if (config.decoder().skip_unnecessary_tracks())
|
||||
return result;
|
||||
}
|
||||
else if (fluxSourceIterator.hasNext())
|
||||
result = BAD_AND_CAN_RETRY;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,9 +75,10 @@ extern Bytes stripPartialRotation(const Bytes& fldata);
|
||||
/*
|
||||
* CMD_SET_BUS CODES
|
||||
*/
|
||||
#define BUS_NONE 0
|
||||
#define BUS_IBMPC 1
|
||||
#define BUS_SHUGART 2
|
||||
#define BUS_NONE 0
|
||||
#define BUS_IBMPC 1
|
||||
#define BUS_SHUGART 2
|
||||
#define BUS_APPLE2 3
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -160,7 +160,7 @@ public:
|
||||
}
|
||||
|
||||
case FLUXOP_SPACE:
|
||||
_serial->readBytes(4);
|
||||
ticks_gw += read_28();
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -3,7 +3,7 @@ syntax = "proto2";
|
||||
import "lib/common.proto";
|
||||
|
||||
message GreaseWeazleProto {
|
||||
enum BusType {
|
||||
enum BusType { /* note that these must match CMD_SET_BUS codes */
|
||||
BUSTYPE_INVALID = 0;
|
||||
IBMPC = 1;
|
||||
SHUGART = 2;
|
||||
|
||||
Reference in New Issue
Block a user