From aafdff49bec6dcfed00e0df5b3d007ccf106682c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 9 Jan 2018 22:13:04 -0500 Subject: [PATCH] Implements the ugly stuff of converting a DMK back to flux. --- Storage/Disk/DiskImage/Formats/DMK.cpp | 133 +++++++++++++++++++++++-- 1 file changed, 123 insertions(+), 10 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/DMK.cpp b/Storage/Disk/DiskImage/Formats/DMK.cpp index 8ea7db34e..39eac8df1 100644 --- a/Storage/Disk/DiskImage/Formats/DMK.cpp +++ b/Storage/Disk/DiskImage/Formats/DMK.cpp @@ -8,8 +8,30 @@ #include "DMK.hpp" +#include "../../Encodings/MFM/Constants.hpp" +#include "../../Encodings/MFM/Encoder.hpp" +#include "../../Track/PCMTrack.hpp" + using namespace Storage::Disk; +namespace { + +std::unique_ptr new_encoder(Storage::Disk::PCMSegment &segment, bool is_double_density) { + std::unique_ptr encoder; + + if(is_double_density) { + encoder = Storage::Encodings::MFM::GetMFMEncoder(segment.data); + segment.length_of_a_bit = Storage::Encodings::MFM::MFMBitLength; + } else { + encoder = Storage::Encodings::MFM::GetFMEncoder(segment.data); + segment.length_of_a_bit = Storage::Encodings::MFM::FMBitLength; + } + + return encoder; +} + +} + DMK::DMK(const char *file_name) : file_(file_name) { // Determine whether this DMK represents a read-only disk (whether intentionally, @@ -48,7 +70,9 @@ int DMK::get_head_count() { } bool DMK::get_is_read_only() { - return is_read_only_; + return true; + // Given that track serialisation is not yet implemented, treat all DMKs as read-only. +// return is_read_only_; } long DMK::get_file_offset_for_position(Track::Address address) { @@ -58,18 +82,107 @@ long DMK::get_file_offset_for_position(Track::Address address) { std::shared_ptr<::Storage::Disk::Track> DMK::get_track_at_position(::Storage::Disk::Track::Address address) { file_.seek(get_file_offset_for_position(address), SEEK_SET); + // Read the IDAM table. uint16_t idam_locations[64]; - std::size_t index = 0; - for(std::size_t c = 0; c < sizeof(idam_locations); ++c) { - idam_locations[index] = file_.get16le(); - if((idam_locations[index] & 0x7fff) >= 128) { - index++; + std::size_t idam_count = 0; + for(std::size_t c = 0; c < sizeof(idam_locations) / sizeof(*idam_locations); ++c) { + idam_locations[idam_count] = file_.get16le(); + if((idam_locations[idam_count] & 0x7fff) >= 128) { + idam_count++; } } - // `index` is now the final (sensical) entry in the IDAM location table. - // TODO: parse track contents. - printf("number of IDAMs: %d", index); + // Grab the rest of the track. + std::vector track = file_.read(static_cast(track_length_ - 0x80)); - return nullptr; + // Default to outputting double density unless the disk doesn't support it. + bool is_double_density = !is_purely_single_density_; + std::vector segments; + std::unique_ptr encoder; + segments.emplace_back(); + encoder = new_encoder(segments.back(), is_double_density); + + std::size_t idam_pointer = 0; + + const std::size_t track_length = static_cast(track_length_) - 0x80; + std::size_t track_pointer = 0; + while(track_pointer < track_length) { + // Determine bytes left until next IDAM. + std::size_t destination; + if(idam_pointer != idam_count) { + destination = (idam_locations[idam_pointer] & 0x7fff) - 0x80; + } else { + destination = track_length; + } + + // Output every intermediate byte. + if(!is_double_density && !is_purely_single_density_) { + is_double_density = true; + segments.emplace_back(); + encoder = new_encoder(segments.back(), is_double_density); + } + while(track_pointer < destination) { + encoder->add_byte(track[track_pointer]); + track_pointer++; + } + + // Exit now if that's it. + if(destination == track_length) break; + + // Being now located at the IDAM, check for a change of encoding. + bool next_is_double_density = !!(idam_locations[idam_pointer] & 0x8000); + if(next_is_double_density != is_double_density) { + is_double_density = next_is_double_density; + segments.emplace_back(); + encoder = new_encoder(segments.back(), is_double_density); + } + + // Now at the IDAM, which will always be an FE regardless of FM/MFM encoding, + // presumably through misunderstanding of the designer? Write out a real IDAM + // for the current density, then the rest of the ID — four bytes for the address + // plus two for the CRC. Keep a copy of the header while we're here, so that the + // size of the sector is known momentarily. + std::size_t step_rate = (!is_double_density && !is_purely_single_density_) ? 2 : 1; + encoder->add_ID_address_mark(); + uint8_t header[6]; + for(int c = 0; c < 6; ++c) { + track_pointer += step_rate; + encoder->add_byte(track[track_pointer]); + header[c] = track[track_pointer]; + } + track_pointer += step_rate; + + // Now write out as many bytes as are found prior to an FB or F8 (same comment as + // above: those are the FM-esque marks, but it seems as though transcription to MFM + // is implicit). + while(true) { + uint8_t next_byte = track[track_pointer]; + track_pointer += step_rate; + if(next_byte == 0xfb || next_byte == 0xf8) { + // Write a data or deleted data address mark. + if(next_byte == 0xfb) encoder->add_data_address_mark(); + else encoder->add_deleted_data_address_mark(); + break; + } + encoder->add_byte(next_byte); + } + + // Now write out a data mark (the file format appears to leave these implicit?), + // then the sector contents plus the CRC. + encoder->add_data_address_mark(); + int sector_size = 2 + (128 << header[3]); + while(sector_size--) { + encoder->add_byte(track[track_pointer]); + track_pointer += step_rate; + } + + idam_pointer++; + } + + // All segments should be exactly their number of bits in length. + for(auto &segment : segments) { + segment.number_of_bits = static_cast(segment.data.size() * 8); + } + + return std::make_shared(segments); }