Merge from master.

This commit is contained in:
David Given
2025-07-27 20:48:27 +01:00
33 changed files with 216 additions and 125 deletions

View File

@@ -734,7 +734,7 @@ void readDiskCommand(
if (globalConfig()->decoder().has_write_csv_to()) if (globalConfig()->decoder().has_write_csv_to())
writer.writeCsv( writer.writeCsv(
*diskflux->image, globalConfig()->decoder().write_csv_to()); *diskflux->image, globalConfig()->decoder().write_csv_to());
writer.writeMappedImage(*diskflux->image); writer.writeImage(*diskflux->image);
} }
void rawReadDiskCommand(FluxSource& fluxsource, FluxSink& fluxsink) void rawReadDiskCommand(FluxSource& fluxsource, FluxSink& fluxsink)

View File

@@ -46,7 +46,7 @@ class Flag
{ {
public: public:
Flag(const std::vector<std::string>& names, const std::string helptext); Flag(const std::vector<std::string>& names, const std::string helptext);
virtual ~Flag(){}; virtual ~Flag() {};
void checkInitialised() const void checkInitialised() const
{ {

View File

@@ -22,8 +22,9 @@ message LayoutProto
enum Order enum Order
{ {
UNDEFINED = 0; UNDEFINED = 0;
CHS = 1; CHS = 1; // sort by cylinder, then head, then sector -- libdsk 'alt'
HCS = 2; HCS = 2; // sort by head, then cylinder, then sector -- libdsk 'outout'
HCS_RH1 = 3; // as HCS, except the cylinder count for head 1 is reversed -- libdsk 'outback'
} }
message LayoutdataProto message LayoutdataProto
@@ -54,10 +55,12 @@ message LayoutProto
[ default = 0, (help) = "number of tracks in image" ]; [ default = 0, (help) = "number of tracks in image" ];
optional int32 sides = 3 optional int32 sides = 3
[ default = 0, (help) = "number of sides in image" ]; [ default = 0, (help) = "number of sides in image" ];
optional Order order = 4 optional Order filesystem_track_order = 4
[ default = CHS, (help) = "the order of sectors in the filesystem" ]; [ default = CHS, (help) = "the order of sectors in the filesystem" ];
optional bool swap_sides = 5 optional Order image_track_order = 5
[ default = CHS, (help) = "the order of sectors in disk images" ];
optional bool swap_sides = 6
[ default = false, (help) = "the sides are inverted on this disk" ]; [ default = false, (help) = "the sides are inverted on this disk" ];
optional FormatType format_type = 6 optional FormatType format_type = 7
[ default = FORMATTYPE_UNKNOWN, (help) = "Format type of image" ]; [ default = FORMATTYPE_UNKNOWN, (help) = "Format type of image" ];
} }

View File

@@ -2,6 +2,7 @@
#include "lib/data/sector.h" #include "lib/data/sector.h"
#include "lib/data/image.h" #include "lib/data/image.h"
#include "lib/data/layout.h" #include "lib/data/layout.h"
#include "lib/config/config.h"
Image::Image() {} Image::Image() {}
@@ -25,7 +26,8 @@ void Image::clear()
void Image::createBlankImage() void Image::createBlankImage()
{ {
clear(); clear();
for (const auto& trackAndHead : Layout::getTrackOrdering()) for (const auto& trackAndHead : Layout::getTrackOrdering(
globalConfig()->layout().filesystem_track_order()))
{ {
unsigned track = trackAndHead.first; unsigned track = trackAndHead.first;
unsigned side = trackAndHead.second; unsigned side = trackAndHead.second;

View File

@@ -108,21 +108,21 @@ void Layout::getBounds(
} }
std::vector<std::pair<int, int>> Layout::getTrackOrdering( std::vector<std::pair<int, int>> Layout::getTrackOrdering(
unsigned guessedTracks, unsigned guessedSides) LayoutProto::Order ordering, unsigned guessedTracks, unsigned guessedSides)
{ {
auto layout = globalConfig()->layout(); auto layout = globalConfig()->layout();
int tracks = layout.has_tracks() ? layout.tracks() : guessedTracks; int tracks = layout.has_tracks() ? layout.tracks() : guessedTracks;
int sides = layout.has_sides() ? layout.sides() : guessedSides; int sides = layout.has_sides() ? layout.sides() : guessedSides;
std::vector<std::pair<int, int>> ordering; std::vector<std::pair<int, int>> trackList;
switch (layout.order()) switch (ordering)
{ {
case LayoutProto::CHS: case LayoutProto::CHS:
{ {
for (int track = 0; track < tracks; track++) for (int track = 0; track < tracks; track++)
{ {
for (int side = 0; side < sides; side++) for (int side = 0; side < sides; side++)
ordering.push_back(std::make_pair(track, side)); trackList.push_back(std::make_pair(track, side));
} }
break; break;
} }
@@ -132,16 +132,30 @@ std::vector<std::pair<int, int>> Layout::getTrackOrdering(
for (int side = 0; side < sides; side++) for (int side = 0; side < sides; side++)
{ {
for (int track = 0; track < tracks; track++) for (int track = 0; track < tracks; track++)
ordering.push_back(std::make_pair(track, side)); trackList.push_back(std::make_pair(track, side));
}
break;
}
case LayoutProto::HCS_RH1:
{
for (int side = 0; side < sides; side++)
{
if (side == 0)
for (int track = 0; track < tracks; track++)
trackList.push_back(std::make_pair(track, side));
if (side == 1)
for (int track = tracks; track >= 0; track--)
trackList.push_back(std::make_pair(track - 1, side));
} }
break; break;
} }
default: default:
error("LAYOUT: invalid track ordering"); error("LAYOUT: invalid track trackList");
} }
return ordering; return trackList;
} }
std::vector<unsigned> Layout::expandSectorList( std::vector<unsigned> Layout::expandSectorList(

View File

@@ -2,6 +2,7 @@
#define LAYOUT_H #define LAYOUT_H
#include "lib/data/flux.h" #include "lib/data/flux.h"
#include "lib/config/layout.pb.h"
class SectorListProto; class SectorListProto;
class TrackInfo; class TrackInfo;
@@ -39,7 +40,9 @@ public:
/* Returns a series of <track, side> pairs representing the filesystem /* Returns a series of <track, side> pairs representing the filesystem
* ordering of the disk, in logical numbers. */ * ordering of the disk, in logical numbers. */
static std::vector<std::pair<int, int>> getTrackOrdering( static std::vector<std::pair<int, int>> getTrackOrdering(
unsigned guessedTracks = 0, unsigned guessedSides = 0); LayoutProto::Order ordering,
unsigned guessedTracks = 0,
unsigned guessedSides = 0);
/* Returns the layout of a given track. */ /* Returns the layout of a given track. */
static std::shared_ptr<const TrackInfo> getLayoutOfTrack( static std::shared_ptr<const TrackInfo> getLayoutOfTrack(

View File

@@ -98,10 +98,10 @@ public:
} }
protected: protected:
virtual void beginTrack(){}; virtual void beginTrack() {};
virtual nanoseconds_t advanceToNextRecord() = 0; virtual nanoseconds_t advanceToNextRecord() = 0;
virtual void decodeSectorRecord() = 0; virtual void decodeSectorRecord() = 0;
virtual void decodeDataRecord(){}; virtual void decodeDataRecord() {};
const DecoderProto& _config; const DecoderProto& _config;
std::shared_ptr<TrackDataFlux> _trackdata; std::shared_ptr<TrackDataFlux> _trackdata;

View File

@@ -63,26 +63,3 @@ std::unique_ptr<ImageReader> ImageReader::create(const ImageReaderProto& config)
} }
ImageReader::ImageReader(const ImageReaderProto& config): _config(config) {} ImageReader::ImageReader(const ImageReaderProto& config): _config(config) {}
std::unique_ptr<Image> ImageReader::readMappedImage()
{
auto rawImage = readImage();
if (!_config.filesystem_sector_order())
return rawImage;
log("READER: converting from filesystem sector order to disk order");
std::set<std::shared_ptr<const Sector>> sectors;
for (const auto& e : *rawImage)
{
auto trackLayout =
Layout::getLayoutOfTrack(e->logicalTrack, e->logicalSide);
auto newSector = std::make_shared<Sector>();
*newSector = *e;
newSector->logicalSector =
trackLayout->filesystemToNaturalSectorMap.at(e->logicalSector);
sectors.insert(newSector);
}
return std::make_unique<Image>(sectors);
}

View File

@@ -13,7 +13,7 @@ class ImageReader
{ {
public: public:
ImageReader(const ImageReaderProto& config); ImageReader(const ImageReaderProto& config);
virtual ~ImageReader(){}; virtual ~ImageReader() {};
public: public:
static std::unique_ptr<ImageReader> create(Config& config); static std::unique_ptr<ImageReader> create(Config& config);
@@ -51,15 +51,10 @@ public:
return _extraConfig; return _extraConfig;
} }
/* Directly reads the image. */ /* Reads the image. */
virtual std::unique_ptr<Image> readImage() = 0; virtual std::unique_ptr<Image> readImage() = 0;
/* Reads the image, and then applies any optional mapping to go from
* filesystem ordering to disk ordering. */
std::unique_ptr<Image> readMappedImage();
protected: protected:
const ImageReaderProto& _config; const ImageReaderProto& _config;
ConfigProto _extraConfig; ConfigProto _extraConfig;

View File

@@ -2,7 +2,12 @@ syntax = "proto2";
import "lib/config/common.proto"; import "lib/config/common.proto";
message ImgInputOutputProto {} message ImgInputOutputProto {
optional bool filesystem_sector_order = 1 [
(help) = "read/write sector image in filesystem order",
default = false
];
}
message DiskCopyInputProto {} message DiskCopyInputProto {}
message ImdInputProto {} message ImdInputProto {}
@@ -15,16 +20,12 @@ message FdiInputProto {}
message D88InputProto {} message D88InputProto {}
message NfdInputProto {} message NfdInputProto {}
// NEXT_TAG: 15 // NEXT_TAG: 14
message ImageReaderProto message ImageReaderProto
{ {
optional string filename = 1 [ (help) = "filename of input sector image" ]; optional string filename = 1 [ (help) = "filename of input sector image" ];
optional bool filesystem_sector_order = 13 [
(help) = "read/write sector image in filesystem order",
default = false
];
optional ImageReaderWriterType type = 14 optional ImageReaderWriterType type = 13
[default = IMAGETYPE_NOT_SET, (help) = "input image type"]; [default = IMAGETYPE_NOT_SET, (help) = "input image type"];
optional ImgInputOutputProto img = 2; optional ImgInputOutputProto img = 2;

View File

@@ -31,8 +31,11 @@ public:
"IMG: bad configuration; did you remember to set the " "IMG: bad configuration; did you remember to set the "
"tracks, sides and trackdata fields in the layout?"); "tracks, sides and trackdata fields in the layout?");
bool in_filesystem_order = _config.img().filesystem_sector_order();
std::unique_ptr<Image> image(new Image); std::unique_ptr<Image> image(new Image);
for (const auto& p : Layout::getTrackOrdering()) for (const auto& p : Layout::getTrackOrdering(
in_filesystem_order ? layout.filesystem_track_order()
: layout.image_track_order()))
{ {
int track = p.first; int track = p.first;
int side = p.second; int side = p.second;
@@ -46,6 +49,9 @@ public:
Bytes data(trackLayout->sectorSize); Bytes data(trackLayout->sectorSize);
inputFile.read((char*)data.begin(), data.size()); inputFile.read((char*)data.begin(), data.size());
if (in_filesystem_order)
sectorId =
trackLayout->filesystemToNaturalSectorMap.at(sectorId);
const auto& sector = image->put(track, side, sectorId); const auto& sector = image->put(track, side, sectorId);
sector->status = Sector::OK; sector->status = Sector::OK;
sector->data = data; sector->data = data;

View File

@@ -173,27 +173,3 @@ void ImageWriter::printMap(const Image& image)
<< std::endl; << std::endl;
} }
} }
void ImageWriter::writeMappedImage(const Image& image)
{
if (_config.filesystem_sector_order())
{
log("WRITER: converting from disk sector order to filesystem order");
std::set<std::shared_ptr<const Sector>> sectors;
for (const auto& e : image)
{
auto trackLayout =
Layout::getLayoutOfTrack(e->logicalTrack, e->logicalSide);
auto newSector = std::make_shared<Sector>();
*newSector = *e;
newSector->logicalSector =
trackLayout->naturalToFilesystemSectorMap.at(e->logicalSector);
sectors.insert(newSector);
}
writeImage(Image(sectors));
}
else
writeImage(image);
}

View File

@@ -9,7 +9,7 @@ class ImageWriter
{ {
public: public:
ImageWriter(const ImageWriterProto& config); ImageWriter(const ImageWriterProto& config);
virtual ~ImageWriter(){}; virtual ~ImageWriter() {};
public: public:
static std::unique_ptr<ImageWriter> create(Config& config); static std::unique_ptr<ImageWriter> create(Config& config);
@@ -37,11 +37,8 @@ public:
void writeCsv(const Image& sectors, const std::string& filename); void writeCsv(const Image& sectors, const std::string& filename);
/* Writes a raw image. */ /* Writes a raw image. */
virtual void writeImage(const Image& sectors) = 0;
/* Writes an image applying any optional mapping from disk sector numbering virtual void writeImage(const Image& sectors) = 0;
* to filesystem sector numbering. */
void writeMappedImage(const Image& sectors);
protected: protected:
const ImageWriterProto& _config; const ImageWriterProto& _config;

View File

@@ -67,12 +67,8 @@ message ImdOutputProto
message ImageWriterProto message ImageWriterProto
{ {
optional string filename = 1 [ (help) = "filename of output sector image" ]; optional string filename = 1 [ (help) = "filename of output sector image" ];
optional bool filesystem_sector_order = 10 [
(help) = "read/write sector image in filesystem order",
default = false
];
optional ImageReaderWriterType type = 11 optional ImageReaderWriterType type = 10
[ default = IMAGETYPE_NOT_SET, (help) = "image writer type" ]; [ default = IMAGETYPE_NOT_SET, (help) = "image writer type" ];
optional ImgInputOutputProto img = 2; optional ImgInputOutputProto img = 2;

View File

@@ -31,7 +31,12 @@ public:
if (!outputFile.is_open()) if (!outputFile.is_open())
error("cannot open output file"); error("cannot open output file");
for (const auto& p : Layout::getTrackOrdering(tracks, sides)) bool in_filesystem_order = _config.img().filesystem_sector_order();
for (const auto& p : Layout::getTrackOrdering(
in_filesystem_order ? layout.filesystem_track_order()
: layout.image_track_order(),
tracks,
sides))
{ {
int track = p.first; int track = p.first;
int side = p.second; int side = p.second;
@@ -39,6 +44,10 @@ public:
auto trackLayout = Layout::getLayoutOfTrack(track, side); auto trackLayout = Layout::getLayoutOfTrack(track, side);
for (int sectorId : trackLayout->naturalSectorOrder) for (int sectorId : trackLayout->naturalSectorOrder)
{ {
if (in_filesystem_order)
sectorId =
trackLayout->filesystemToNaturalSectorMap.at(sectorId);
const auto& sector = image.get(track, side, sectorId); const auto& sector = image.get(track, side, sectorId);
if (sector) if (sector)
sector->data.slice(0, trackLayout->sectorSize) sector->data.slice(0, trackLayout->sectorSize)

View File

@@ -44,14 +44,14 @@ public:
void flushChanges() override void flushChanges() override
{ {
_writer->writeMappedImage(*_image); _writer->writeImage(*_image);
_changed = false; _changed = false;
} }
void discardChanges() override void discardChanges() override
{ {
if (_reader) if (_reader)
_image = _reader->readMappedImage(); _image = _reader->readImage();
else else
{ {
_image = std::make_shared<Image>(); _image = std::make_shared<Image>();

View File

@@ -160,8 +160,8 @@ Filesystem::Filesystem(std::shared_ptr<SectorInterface> sectors):
"information"); "information");
unsigned block = 0; unsigned block = 0;
for (const auto& p : for (const auto& p : Layout::getTrackOrdering(
Layout::getTrackOrdering(layout.tracks(), layout.sides())) layout.filesystem_track_order(), layout.tracks(), layout.sides()))
{ {
int track = p.first; int track = p.first;
int side = p.second; int side = p.second;

View File

@@ -154,7 +154,4 @@ message FilesystemProto
optional MicrodosProto microdos = 15; optional MicrodosProto microdos = 15;
optional ZDosProto zdos = 16; optional ZDosProto zdos = 16;
optional RolandFsProto roland = 17; optional RolandFsProto roland = 17;
optional SectorListProto sector_order = 9
[ (help) = "specify the filesystem order of sectors" ];
} }

View File

@@ -68,7 +68,7 @@ int mainWrite(int argc, const char* argv[])
flags.parseFlagsWithConfigFiles(argc, argv, formats); flags.parseFlagsWithConfigFiles(argc, argv, formats);
auto reader = ImageReader::create(globalConfig()); auto reader = ImageReader::create(globalConfig());
std::shared_ptr<Image> image = reader->readMappedImage(); std::shared_ptr<Image> image = reader->readImage();
auto encoder = Arch::createEncoder(globalConfig()); auto encoder = Arch::createEncoder(globalConfig());
auto fluxSink = FluxSink::create(globalConfig()); auto fluxSink = FluxSink::create(globalConfig());

View File

@@ -96,7 +96,8 @@ option_group {
format_type: FORMATTYPE_80TRACK format_type: FORMATTYPE_80TRACK
tracks: 80 tracks: 80
sides: 2 sides: 2
order: HCS image_track_order: HCS
filesystem_track_order: HCS
layoutdata { layoutdata {
sector_size: 256 sector_size: 256
physical { physical {
@@ -136,12 +137,16 @@ option_group {
config { config {
image_reader { image_reader {
img {
filesystem_sector_order: true filesystem_sector_order: true
} }
}
image_writer { image_writer {
img {
filesystem_sector_order: true filesystem_sector_order: true
} }
}
filesystem { filesystem {
type: APPLEDOS type: APPLEDOS
@@ -179,12 +184,16 @@ option_group {
config { config {
image_reader { image_reader {
img {
filesystem_sector_order: true filesystem_sector_order: true
} }
}
image_writer { image_writer {
img {
filesystem_sector_order: true filesystem_sector_order: true
} }
}
filesystem { filesystem {
type: PRODOS type: PRODOS
@@ -221,12 +230,16 @@ option_group {
config { config {
image_reader { image_reader {
img {
filesystem_sector_order: true filesystem_sector_order: true
} }
}
image_writer { image_writer {
img {
filesystem_sector_order: true filesystem_sector_order: true
} }
}
filesystem { filesystem {
type: CPMFS type: CPMFS

View File

@@ -23,6 +23,7 @@ formats = [
"hplif", "hplif",
"ibm", "ibm",
"icl30", "icl30",
"juku",
"mac", "mac",
"micropolis", "micropolis",
"ms2000", "ms2000",

99
src/formats/juku.textpb Normal file
View File

@@ -0,0 +1,99 @@
shortname: 'Juku E5104'
comment: 'CP/M'
documentation:
<<<
Juku E5104 is an Estonian school computer from late 1980s and
early 1990s. It was designed by EKTA in 1985, and starting
from 1988 produced in Narva "Baltijets" factory. Arguably
the school computer was technically outdated already when
released, but still occupies a precious spot in the memories
of a whole generation of Estonian IT professionals.
The system uses dual 5.25 inch ИЗОТ ЕС5323 (IZOT ES5323)
diskette drive with regular MFM encoded DSDD. The disks have
a sector skew factor 2 and tracks start from outside of the
diskette _for both sides_, which is a combination that somewhat
complicates reading CP/M filesystem content with common tools.
Mostly 800kB (786kB) DSDD disks were used, but there are also
400kB (386kB) SSDD floppies in circulation.
## References (all in Estonian)
- [How to read the content of Juku disks?](https://github.com/infoaed/juku3000/blob/master/docs/kettad.md)
- [List of recovered Juku software](https://github.com/infoaed/juku3000/blob/master/docs/tarkvara-kataloog.md)
- [System disks for E5104](https://elektroonikamuuseum.ee/juku_arvuti_tarkvara.html)
>>>
image_reader {
filename: "image.juk"
type: IMAGETYPE_IMG
}
image_writer {
filename: "image.juk"
type: IMAGETYPE_IMG
}
encoder {
ibm {}
}
decoder {
ibm {}
}
layout {
format_type: FORMATTYPE_80TRACK
tracks: 80
sides: 2
filesystem_track_order: HCS
layoutdata {
sector_size: 512
physical {
start_sector: 1
count: 10
}
filesystem {
start_sector: 1
count: 10
skew: 2
}
}
}
option_group {
comment: "$formats"
option {
name: "800"
comment: '800kB 80-track 10-sector DSDD'
set_by_default: true
}
option {
name: "400"
comment: '400kB 80-track 10-sector SSDD'
config {
layout {
sides: 1
}
}
}
}
filesystem {
type: CPMFS
cpmfs {
filesystem_start {
side: 0
track: 2
}
block_size: 4096
dir_entries: 128
}
}

View File

@@ -124,7 +124,8 @@ option {
layoutdata { layoutdata {
sector_size: 275 sector_size: 275
} }
order: HCS filesystem_track_order: HCS
image_track_order: HCS
} }
decoder { decoder {

View File

@@ -38,7 +38,8 @@ layout {
format_type: FORMATTYPE_80TRACK format_type: FORMATTYPE_80TRACK
tracks: 80 tracks: 80
sides: 2 sides: 2
order: HCS image_track_order: HCS
filesystem_track_order: HCS
swap_sides: true swap_sides: true
layoutdata { layoutdata {
sector_size: 1024 sector_size: 1024

View File

@@ -142,7 +142,7 @@ public:
[this]() [this]()
{ {
auto image = auto image =
GetContext().GetImageReader()->readMappedImage(); GetContext().GetImageReader()->readImage();
auto* encoder = GetContext().GetEncoder(); auto* encoder = GetContext().GetEncoder();
auto* fluxSink = GetContext().GetFluxSink(); auto* fluxSink = GetContext().GetFluxSink();
@@ -260,7 +260,7 @@ public:
QueueJob( QueueJob(
[image, this]() [image, this]()
{ {
GetContext().GetImageWriter()->writeMappedImage(*image); GetContext().GetImageWriter()->writeImage(*image);
}); });
} }
catch (const ErrorException& e) catch (const ErrorException& e)