diff --git a/Storage/Disk/Encodings/MFM.cpp b/Storage/Disk/Encodings/MFM.cpp index d1740baeb..007fd0110 100644 --- a/Storage/Disk/Encodings/MFM.cpp +++ b/Storage/Disk/Encodings/MFM.cpp @@ -126,7 +126,8 @@ static uint8_t logarithmic_size_for_size(size_t size) case 256: return 1; case 512: return 2; case 1024: return 3; - case 2048: return 4; + case 2048: return 4; std::vector get_track(uint8_t track); + case 4196: return 5; } } @@ -267,7 +268,7 @@ Parser::Parser(bool is_mfm, const std::shared_ptr &track) drive->set_disk_with_track(track); } -std::shared_ptr Parser::get_sector(uint8_t track, uint8_t sector) +void Parser::seek_to_track(uint8_t track) { int difference = (int)track - (int)track_; track_ = track; @@ -279,10 +280,20 @@ std::shared_ptr Parser::get_sector(uint8_t trac for(int c = 0; c < difference; c++) step(direction); } +} +std::shared_ptr Parser::get_sector(uint8_t track, uint8_t sector) +{ + seek_to_track(track); return get_sector(sector); } +std::vector Parser::get_track(uint8_t track) +{ + seek_to_track(track); + return get_track(); +} + void Parser::process_input_bit(int value, unsigned int cycles_since_index_hole) { shift_register_ = ((shift_register_ << 1) | (unsigned int)value) & 0xffff; @@ -294,23 +305,132 @@ void Parser::process_index_hole() index_count_++; } +uint8_t Parser::get_byte_for_shift_value(uint16_t value) +{ + return (uint8_t)( + ((value&0x0001) >> 0) | + ((value&0x0004) >> 1) | + ((value&0x0010) >> 2) | + ((value&0x0040) >> 3) | + ((value&0x0100) >> 4) | + ((value&0x0400) >> 5) | + ((value&0x1000) >> 6) | + ((value&0x4000) >> 7));} + uint8_t Parser::get_next_byte() { bit_count_ = 0; while(bit_count_ < 16) run_for_cycles(1); - uint8_t byte = (uint8_t)( - ((shift_register_&0x0001) >> 0) | - ((shift_register_&0x0004) >> 1) | - ((shift_register_&0x0010) >> 2) | - ((shift_register_&0x0040) >> 3) | - ((shift_register_&0x0100) >> 4) | - ((shift_register_&0x0400) >> 5) | - ((shift_register_&0x1000) >> 6) | - ((shift_register_&0x4000) >> 7)); + uint8_t byte = get_byte_for_shift_value((uint16_t)shift_register_); crc_generator_.add(byte); return byte; } +std::vector Parser::get_track() +{ + std::vector result; + int distance_until_permissible_sync = 0; + uint8_t last_id[6]; + int last_id_pointer = 0; + bool next_is_type = false; + + // align to the next index hole + index_count_ = 0; + while(!index_count_) run_for_cycles(1); + + // capture every other bit until the next index hole + index_count_ = 0; + while(1) + { + // wait until either another bit or the index hole arrives + bit_count_ = 0; + bool found_sync = false; + while(!index_count_ && !found_sync && bit_count_ < 16) + { + int previous_bit_count = bit_count_; + run_for_cycles(1); + + if(!distance_until_permissible_sync && bit_count_ != previous_bit_count) + { + uint16_t low_shift_register = (shift_register_&0xffff); + if(is_mfm_) + { + found_sync = (low_shift_register == MFMIndexSync) || (low_shift_register == MFMSync); + } + else + { + found_sync = + (low_shift_register == FMIndexAddressMark) || + (low_shift_register == FMIDAddressMark) || + (low_shift_register == FMDataAddressMark) || + (low_shift_register == FMDeletedDataAddressMark); + } + } + } + + // if that was the index hole then finish + if(index_count_) + { + if(bit_count_) result.push_back(get_byte_for_shift_value((uint16_t)(shift_register_ << (16 - bit_count_)))); + break; + } + + // store whatever the current byte is + uint8_t byte_value = get_byte_for_shift_value((uint16_t)shift_register_); + result.push_back(byte_value); + if(last_id_pointer < 6) last_id[last_id_pointer++] = byte_value; + + // if no syncs are permissible here, decrement the waiting period and perform no further contemplation + bool found_id = false, found_data = false; + if(distance_until_permissible_sync) + { + distance_until_permissible_sync--; + } + else + { + if(found_sync) + { + if(is_mfm_) + { + next_is_type = true; + } + else + { + switch(shift_register_&0xffff) + { + case FMIDAddressMark: found_id = true; break; + case FMDataAddressMark: + case FMDeletedDataAddressMark: found_data = true; break; + } + } + } + else if(next_is_type) + { + switch(byte_value) + { + case MFMIDAddressByte: found_id = true; break; + case MFMDataAddressByte: + case MFMDeletedDataAddressByte: found_data = true; break; + } + } + } + + if(found_id) + { + distance_until_permissible_sync = 6; + last_id_pointer = 0; + } + + if(found_data) + { + distance_until_permissible_sync = 128 << last_id[3]; + } + } + + return result; +} + + std::shared_ptr Parser::get_next_sector() { std::shared_ptr sector(new Storage::Encodings::MFM::Sector); diff --git a/Storage/Disk/Encodings/MFM.hpp b/Storage/Disk/Encodings/MFM.hpp index f1545407d..c542f3154 100644 --- a/Storage/Disk/Encodings/MFM.hpp +++ b/Storage/Disk/Encodings/MFM.hpp @@ -73,6 +73,18 @@ class Parser: public Storage::Disk::Controller { */ std::shared_ptr get_sector(uint8_t track, uint8_t sector); + /*! + Attempts to read the track at @c track, starting from the index hole. + + Decodes data bits only; clocks are omitted. Synchronisation values begin a new + byte. If a synchronisation value begins partway through a byte then + synchronisation-contributing bits will appear both in the preceding byte and + in the next. + + @returns a vector of data found. + */ + std::vector get_track(uint8_t track); + private: Parser(bool is_mfm); @@ -84,11 +96,16 @@ class Parser: public Storage::Disk::Controller { NumberTheory::CRC16 crc_generator_; bool is_mfm_; + void seek_to_track(uint8_t track); void process_input_bit(int value, unsigned int cycles_since_index_hole); void process_index_hole(); uint8_t get_next_byte(); + + uint8_t get_byte_for_shift_value(uint16_t value); + std::shared_ptr get_next_sector(); std::shared_ptr get_sector(uint8_t sector); + std::vector get_track(); }; diff --git a/Storage/Disk/Formats/OricMFMDSK.cpp b/Storage/Disk/Formats/OricMFMDSK.cpp index c0de30f9e..49837c5bc 100644 --- a/Storage/Disk/Formats/OricMFMDSK.cpp +++ b/Storage/Disk/Formats/OricMFMDSK.cpp @@ -26,6 +26,11 @@ OricMFMDSK::OricMFMDSK(const char *file_name) : throw ErrorNotOricMFMDSK; } +OricMFMDSK::~OricMFMDSK() +{ + flush_updates(); +} + unsigned int OricMFMDSK::get_head_position_count() { return track_count_; @@ -36,7 +41,12 @@ unsigned int OricMFMDSK::get_head_count() return head_count_; } -std::shared_ptr OricMFMDSK::get_uncached_track_at_position(unsigned int head, unsigned int position) +bool OricMFMDSK::get_is_read_only() +{ + return is_read_only_; +} + +long OricMFMDSK::get_file_offset_for_position(unsigned int head, unsigned int position) { long seek_offset = 0; switch(geometry_type_) @@ -48,7 +58,12 @@ std::shared_ptr OricMFMDSK::get_uncached_track_at_position(unsigned int h seek_offset = (position * track_count_ * head_count_) + head; break; } - fseek(file_, (seek_offset * 6400) + 256, SEEK_SET); + return (seek_offset * 6400) + 256; +} + +std::shared_ptr OricMFMDSK::get_uncached_track_at_position(unsigned int head, unsigned int position) +{ + fseek(file_, get_file_offset_for_position(head, position), SEEK_SET); PCMSegment segment; @@ -115,3 +130,15 @@ std::shared_ptr OricMFMDSK::get_uncached_track_at_position(unsigned int h std::shared_ptr track(new PCMTrack(segment)); return track; } + +void OricMFMDSK::store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr &track, std::mutex &file_access_mutex) +{ + Storage::Encodings::MFM::Parser parser(true, track); + std::vector parsed_track = parser.get_track(0); + long file_offset = get_file_offset_for_position(head, position); + + std::lock_guard lock_guard(file_access_mutex); + fseek(file_, file_offset, SEEK_SET); + size_t track_size = std::min((size_t)6400, parsed_track.size()); + fwrite(parsed_track.data(), 1, track_size, file_); +} diff --git a/Storage/Disk/Formats/OricMFMDSK.hpp b/Storage/Disk/Formats/OricMFMDSK.hpp index f4d944a4b..53dac1904 100644 --- a/Storage/Disk/Formats/OricMFMDSK.hpp +++ b/Storage/Disk/Formats/OricMFMDSK.hpp @@ -27,6 +27,7 @@ class OricMFMDSK: public Disk, public Storage::FileHolder { @throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image. */ OricMFMDSK(const char *file_name); + ~OricMFMDSK(); enum { ErrorNotOricMFMDSK, @@ -35,9 +36,13 @@ class OricMFMDSK: public Disk, public Storage::FileHolder { // implemented to satisfy @c Disk unsigned int get_head_position_count(); unsigned int get_head_count(); + bool get_is_read_only(); private: + void store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr &track, std::mutex &file_access_mutex); std::shared_ptr get_uncached_track_at_position(unsigned int head, unsigned int position); + long get_file_offset_for_position(unsigned int head, unsigned int position); + uint32_t head_count_; uint32_t track_count_; uint32_t geometry_type_; diff --git a/Storage/FileHolder.cpp b/Storage/FileHolder.cpp index 1395da998..bd4c47305 100644 --- a/Storage/FileHolder.cpp +++ b/Storage/FileHolder.cpp @@ -93,7 +93,7 @@ void FileHolder::ensure_file_is_at_least_length(long length) if(bytes_to_write > 0) { uint8_t *empty = new uint8_t[bytes_to_write]; - memset(empty, 0, bytes_to_write); + memset(empty, 0, (size_t)bytes_to_write); fwrite(empty, sizeof(uint8_t), (size_t)bytes_to_write, file_); delete[] empty; }