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

@@ -347,7 +347,7 @@ ReadResult readGroup(FluxSourceIteratorHolder& fluxSourceIteratorHolder,
ReadResult result = BAD_AND_CAN_NOT_RETRY;
for (unsigned offset = 0; offset < trackInfo->groupSize;
offset += Layout::getHeadWidth())
offset += Layout::getHeadWidth())
{
log(BeginReadOperationLogMessage{
trackInfo->physicalTrack + offset, trackInfo->physicalSide});
@@ -404,7 +404,7 @@ void writeTracks(FluxSink& fluxSink,
for (;;)
{
for (int offset = 0; offset < trackInfo->groupSize;
offset += Layout::getHeadWidth())
offset += Layout::getHeadWidth())
{
unsigned physicalTrack = trackInfo->physicalTrack + offset;
@@ -734,7 +734,7 @@ void readDiskCommand(
if (globalConfig()->decoder().has_write_csv_to())
writer.writeCsv(
*diskflux->image, globalConfig()->decoder().write_csv_to());
writer.writeMappedImage(*diskflux->image);
writer.writeImage(*diskflux->image);
}
void rawReadDiskCommand(FluxSource& fluxsource, FluxSink& fluxsink)

View File

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

View File

@@ -22,8 +22,9 @@ message LayoutProto
enum Order
{
UNDEFINED = 0;
CHS = 1;
HCS = 2;
CHS = 1; // sort by cylinder, then head, then sector -- libdsk 'alt'
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
@@ -54,10 +55,12 @@ message LayoutProto
[ default = 0, (help) = "number of tracks in image" ];
optional int32 sides = 3
[ 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" ];
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" ];
optional FormatType format_type = 6
optional FormatType format_type = 7
[ default = FORMATTYPE_UNKNOWN, (help) = "Format type of image" ];
}

View File

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

View File

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

View File

@@ -2,6 +2,7 @@
#define LAYOUT_H
#include "lib/data/flux.h"
#include "lib/config/layout.pb.h"
class SectorListProto;
class TrackInfo;
@@ -39,7 +40,9 @@ public:
/* Returns a series of <track, side> pairs representing the filesystem
* ordering of the disk, in logical numbers. */
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. */
static std::shared_ptr<const TrackInfo> getLayoutOfTrack(

View File

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

View File

@@ -93,7 +93,7 @@ public:
auto physical = layoutdata->mutable_physical();
for (int sectorInTrack = 0; sectorInTrack < currentSectorsInTrack;
sectorInTrack++)
sectorInTrack++)
{
Bytes sectorHeader(0x10);
inputFile.read(

View File

@@ -63,26 +63,3 @@ std::unique_ptr<ImageReader> ImageReader::create(const ImageReaderProto& 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:
ImageReader(const ImageReaderProto& config);
virtual ~ImageReader(){};
virtual ~ImageReader() {};
public:
static std::unique_ptr<ImageReader> create(Config& config);
@@ -51,15 +51,10 @@ public:
return _extraConfig;
}
/* Directly reads the image. */
/* Reads the image. */
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:
const ImageReaderProto& _config;
ConfigProto _extraConfig;

View File

@@ -2,7 +2,12 @@ syntax = "proto2";
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 ImdInputProto {}
@@ -15,16 +20,12 @@ message FdiInputProto {}
message D88InputProto {}
message NfdInputProto {}
// NEXT_TAG: 15
// NEXT_TAG: 14
message ImageReaderProto
{
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"];
optional ImgInputOutputProto img = 2;

View File

@@ -301,7 +301,7 @@ public:
switch (Status_Sector)
{
// clang-format off
// clang-format off
/* 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

View File

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

View File

@@ -65,15 +65,15 @@ public:
uint32_t trackOffset = 688;
for (int track = 0; track < geometry.numTracks * geometry.numSides;
track++)
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 = geometry.firstSector;
sectorId <= geometry.numSectors;
sectorId++)
sectorId <= geometry.numSectors;
sectorId++)
{
const auto& sector = image.get(track >> 1, side, sectorId);
if (sector)

View File

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

View File

@@ -67,12 +67,8 @@ message ImdOutputProto
message ImageWriterProto
{
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" ];
optional ImgInputOutputProto img = 2;

View File

@@ -354,7 +354,7 @@ public:
// determine how the optional cylinder map looks like
// write the corresponding logical ID
for (int sectorId = 0; sectorId < numSectorsinTrack;
sectorId++)
sectorId++)
{
// const auto& sector = sectors.get(track, head,
// sectorId);
@@ -372,7 +372,7 @@ public:
// determine how the optional head map looks like
// write the corresponding logical ID
for (int sectorId = 0; sectorId < numSectorsinTrack;
sectorId++)
sectorId++)
{
// const auto& sector = sectors.get(track, head,
// sectorId);
@@ -440,7 +440,7 @@ public:
}
switch (sector->status)
{
// clang-format off
// clang-format off
/* 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

View File

@@ -31,7 +31,12 @@ public:
if (!outputFile.is_open())
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 side = p.second;
@@ -39,6 +44,10 @@ public:
auto trackLayout = Layout::getLayoutOfTrack(track, side);
for (int sectorId : trackLayout->naturalSectorOrder)
{
if (in_filesystem_order)
sectorId =
trackLayout->filesystemToNaturalSectorMap.at(sectorId);
const auto& sector = image.get(track, side, sectorId);
if (sector)
sector->data.slice(0, trackLayout->sectorSize)

View File

@@ -62,7 +62,7 @@ public:
int actualSectors = 0;
for (int sectorId = 0; sectorId < geometry.numSectors;
sectorId++)
sectorId++)
{
const auto& sector = image.get(track, side, sectorId);
if (sector)
@@ -81,7 +81,7 @@ public:
trackHeaderWriter.write_le16(0); /* approximate track length */
for (int sectorId = 0; sectorId < geometry.numSectors;
sectorId++)
sectorId++)
{
const auto& sector = image.get(track, side, sectorId);
if (sector)

View File

@@ -44,7 +44,7 @@ public:
unsigned sectorFileOffset;
for (int track = 0; track < geometry.numTracks * geometry.numSides;
track++)
track++)
{
int side = (track < geometry.numTracks) ? 0 : 1;
for (int sectorId = 0; sectorId < geometry.numSectors; sectorId++)

View File

@@ -38,7 +38,7 @@ public:
unsigned sectorFileOffset;
for (int track = 0; track < geometry.numTracks * geometry.numSides;
track++)
track++)
{
int side = (track < geometry.numTracks) ? 0 : 1;

View File

@@ -128,7 +128,7 @@ class CbmfsFilesystem : public Filesystem
usedBlocks = 0;
unsigned block = 0;
for (int track = 0; track < globalConfig()->layout().tracks();
track++)
track++)
{
uint8_t blocks = br.read_8();
uint32_t bitmap = br.read_le24();

View File

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

View File

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

View File

@@ -154,7 +154,4 @@ message FilesystemProto
optional MicrodosProto microdos = 15;
optional ZDosProto zdos = 16;
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);
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 fluxSink = FluxSink::create(globalConfig());

View File

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

View File

@@ -23,6 +23,7 @@ formats = [
"hplif",
"ibm",
"icl30",
"juku",
"mac",
"micropolis",
"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 {
sector_size: 275
}
order: HCS
filesystem_track_order: HCS
image_track_order: HCS
}
decoder {

View File

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

View File

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