#include "globals.h" #include "flags.h" #include "dataspec.h" #include "sector.h" #include "sectorset.h" #include "imagewriter/imagewriter.h" #include "fmt/format.h" #include "ldbs.h" #include #include #include static const char LABEL[] = "FluxEngine image"; static void write_and_update_checksum(ByteWriter& bw, uint32_t& checksum, const Bytes& data) { ByteReader br(data); while (!br.eof()) { uint32_t i = br.read_be16(); checksum += i; checksum = (checksum >> 1) | (checksum << 31); bw.write_be16(i); } } class DiskCopyImageWriter : public ImageWriter { public: DiskCopyImageWriter(const SectorSet& sectors, const ImageSpec& spec): ImageWriter(sectors, spec) {} void writeImage() { bool mfm = false; if (spec.bytes == 524) { /* GCR disk */ } else if (spec.bytes == 512) { /* MFM disk */ mfm = true; } else Error() << "this image is not compatible with the DiskCopy 4.2 format"; std::cout << "writing DiskCopy 4.2 image\n" << fmt::format("{} tracks, {} heads, {} sectors, {} bytes per sector; {}\n", spec.cylinders, spec.heads, spec.sectors, spec.bytes, mfm ? "MFM" : "GCR"); auto sectors_per_track = [&](int track) -> int { if (mfm) return spec.sectors; if (track < 16) return 12; if (track < 32) return 11; if (track < 48) return 10; if (track < 64) return 9; return 8; }; Bytes image; ByteWriter bw(image); /* Write the actual sectr data. */ uint32_t dataChecksum = 0; uint32_t tagChecksum = 0; uint32_t offset = 0x54; uint32_t sectorDataStart = offset; for (int track = 0; track < spec.cylinders; track++) { for (int head = 0; head < spec.heads; head++) { int sectorCount = sectors_per_track(track); for (int sectorId = 0; sectorId < sectorCount; sectorId++) { const auto& sector = sectors.get(track, head, sectorId); if (sector) { bw.seek(offset); write_and_update_checksum(bw, dataChecksum, sector->data.slice(0, 512)); } offset += 512; } } } uint32_t sectorDataEnd = offset; if (!mfm) { for (int track = 0; track < spec.cylinders; track++) { for (int head = 0; head < spec.heads; head++) { int sectorCount = sectors_per_track(track); for (int sectorId = 0; sectorId < sectorCount; sectorId++) { const auto& sector = sectors.get(track, head, sectorId); if (sector) { bw.seek(offset); write_and_update_checksum(bw, tagChecksum, sector->data.slice(512, 12)); } offset += 12; } } } } uint32_t tagDataEnd = offset; /* Write the header. */ uint8_t encoding; uint8_t format; if (mfm) { format = 0x22; if (spec.sectors == 18) encoding = 3; else encoding = 2; } else { if (spec.heads == 2) { encoding = 1; format = 0x22; } else { encoding = 0; format = 0x02; } } bw.seek(0); bw.write_8(sizeof(LABEL)); bw.append(LABEL); bw.seek(0x40); bw.write_be32(sectorDataEnd - sectorDataStart); /* data size */ bw.write_be32(tagDataEnd - sectorDataEnd); /* tag size */ bw.write_be32(dataChecksum); /* data checksum */ bw.write_be32(tagChecksum); /* tag checksum */ bw.write_8(encoding); /* encoding */ bw.write_8(format); /* format byte */ bw.write_be16(0x0100); /* magic number */ image.writeToFile(spec.filename); } }; std::unique_ptr ImageWriter::createDiskCopyImageWriter( const SectorSet& sectors, const ImageSpec& spec) { return std::unique_ptr(new DiskCopyImageWriter(sectors, spec)); }