From 73080d6c366afb7fecc20d86916ff13ec7137838 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 16:05:10 -0400 Subject: [PATCH] Added an easy way for disk controllers to clamp termination of written data exactly to the index hole. This commit also temporarily provides a whole load of extra logging and minor logic improvements from the 8272. I'm mid-flow on finding a particularly vicious error in its handling of writing; wait for the pull request. But, at least: now waits for the first part of a post-ID gap before writing data, and attempts partially to handle appearance of the index hole during writing a track. More work to do on that though. --- Components/1770/1770.cpp | 4 +- Components/8272/i8272.cpp | 69 +++++++++++++++++++++++--------- Components/8272/i8272.hpp | 2 +- Storage/Disk/DiskController.cpp | 13 ++++-- Storage/Disk/DiskController.hpp | 6 ++- Storage/Disk/PCMPatchedTrack.cpp | 7 +++- Storage/Disk/PCMPatchedTrack.hpp | 7 +++- 7 files changed, 77 insertions(+), 31 deletions(-) diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index 6c5d43834..45545f7ef 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -502,7 +502,7 @@ void WD1770::posit_event(int new_event_type) { } set_data_mode(DataMode::Writing); - begin_writing(); + begin_writing(false); for(int c = 0; c < (get_is_double_density() ? 12 : 6); c++) { write_byte(0); } @@ -694,7 +694,7 @@ void WD1770::posit_event(int new_event_type) { } WAIT_FOR_EVENT(Event1770::IndexHoleTarget); - begin_writing(); + begin_writing(true); index_hole_count_ = 0; write_track_write_loop: diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 9d7658319..9b71a84ee 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -193,25 +193,28 @@ void i8272::set_disk(std::shared_ptr disk, int drive) { #define CONCAT(x, y) PASTE(x, y) #define FIND_HEADER() \ + set_data_mode(DataMode::Scanning); \ CONCAT(find_header, __LINE__): WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole); \ - if(event_type == (int)Event::IndexHole) index_hole_limit_--; \ + if(event_type == (int)Event::IndexHole) { printf("I\n"); index_hole_limit_--; } \ else if(get_latest_token().type == Token::ID) goto CONCAT(header_found, __LINE__); \ \ if(index_hole_limit_) goto CONCAT(find_header, __LINE__); \ CONCAT(header_found, __LINE__): 0;\ #define FIND_DATA() \ + set_data_mode(DataMode::Scanning); \ CONCAT(find_data, __LINE__): WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole); \ if(event_type == (int)Event::Token && get_latest_token().type != Token::Data && get_latest_token().type != Token::DeletedData) goto CONCAT(find_data, __LINE__); #define READ_HEADER() \ distance_into_section_ = 0; \ - set_data_mode(Reading); \ + set_data_mode(DataMode::Reading); \ CONCAT(read_header, __LINE__): WAIT_FOR_EVENT(Event::Token); \ header_[distance_into_section_] = get_latest_token().byte_value; \ distance_into_section_++; \ if(distance_into_section_ < 6) goto CONCAT(read_header, __LINE__); \ - set_data_mode(Scanning); + +// printf("%02x -> %04x\n", header_[distance_into_section_], get_crc_generator().get_value()); \ #define SET_DRIVE_HEAD_MFM() \ active_drive_ = command_[1]&3; \ @@ -221,6 +224,14 @@ void i8272::set_disk(std::shared_ptr disk, int drive) { set_is_double_density(command_[0] & 0x40); \ invalidate_track(); +#define WAIT_FOR_BYTES(n) \ + distance_into_section_ = 0; \ + CONCAT(wait_bytes, __LINE__): WAIT_FOR_EVENT(Event::Token); \ + if(get_latest_token().type == Token::Byte) distance_into_section_++; \ + if(distance_into_section_ < (n)) goto CONCAT(wait_bytes, __LINE__); + +// printf("%02x\n", get_latest_token().byte_value); + #define LOAD_HEAD() \ if(!drives_[active_drive_].head_is_loaded[active_head_]) { \ drives_[active_drive_].head_is_loaded[active_head_] = true; \ @@ -241,6 +252,7 @@ void i8272::set_disk(std::shared_ptr disk, int drive) { } void i8272::posit_event(int event_type) { + if(event_type == (int)Event::IndexHole) index_hole_count_++; if(!(interesting_event_mask_ & event_type)) return; interesting_event_mask_ &= ~event_type; @@ -339,8 +351,7 @@ void i8272::posit_event(int event_type) { // Sets a maximum index hole limit of 2 then performs a find header/read header loop, continuing either until // the index hole limit is breached or a sector is found with a cylinder, head, sector and size equal to the // values in the internal registers. - index_hole_limit_ = 2; - set_data_mode(DataMode::Scanning); + index_hole_limit_ = 6; printf("Seeking %02x %02x %02x %02x\n", cylinder_, head_, sector_, size_); find_next_sector: FIND_HEADER(); @@ -350,12 +361,13 @@ void i8272::posit_event(int event_type) { SetNoData(); goto abort_read_write; } + printf("Header\n"); READ_HEADER(); if(get_crc_generator().get_value()) { // This implies a CRC error in the header; mark as such but continue. SetDataError(); } - printf("Considering %02x %02x %02x %02x\n", header_[0], header_[1], header_[2], header_[3]); + printf("Considering %02x %02x %02x %02x [%04x]\n", header_[0], header_[1], header_[2], header_[3], get_crc_generator().get_value()); if(header_[0] != cylinder_ || header_[1] != head_ || header_[2] != sector_ || header_[3] != size_) goto find_next_sector; // Branch to whatever is supposed to happen next @@ -450,6 +462,10 @@ void i8272::posit_event(int event_type) { if(!dma_mode_) SetNonDMAExecution(); SET_DRIVE_HEAD_MFM(); +// if(command_[2] == 0x16 && command_[3] == 0x00 && command_[4] == 0x06 && command_[5] == 0x02) { +// printf("?"); +// } + if(drives_[active_drive_].drive->get_is_read_only()) { SetNotWriteable(); goto abort_read_write; @@ -459,9 +475,10 @@ void i8272::posit_event(int event_type) { goto read_write_find_header; write_data_found_header: - begin_writing(); + WAIT_FOR_BYTES(get_is_double_density() ? 22 : 11); + begin_writing(true); - write_id_data_joiner((command_[0] & 0x1f) == CommandWriteDeletedData); + write_id_data_joiner((command_[0] & 0x1f) == CommandWriteDeletedData, true); SetDataDirectionFromProcessor(); SetDataRequest(); @@ -483,6 +500,7 @@ void i8272::posit_event(int event_type) { goto write_loop; } + printf("Wrote %d bytes\n", distance_into_section_); write_crc(); expects_input_ = false; WAIT_FOR_EVENT(Event::DataWritten); @@ -578,7 +596,8 @@ void i8272::posit_event(int event_type) { // Wait for the index hole. WAIT_FOR_EVENT(Event::IndexHole); - begin_writing(); + index_hole_count_ = 0; + begin_writing(true); // Write start-of-track. write_start_of_track(); @@ -595,20 +614,30 @@ void i8272::posit_event(int event_type) { expects_input_ = true; distance_into_section_ = 0; format_track_write_header: - WAIT_FOR_EVENT(Event::DataWritten); - // TODO: overrun? - header_[distance_into_section_] = input_; - write_byte(input_); - has_input_ = false; - distance_into_section_++; - if(distance_into_section_ < 4) { - SetDataRequest(); - goto format_track_write_header; + WAIT_FOR_EVENT((int)Event::DataWritten | (int)Event::IndexHole); + switch(event_type) { + case (int)Event::IndexHole: + SetOverrun(); + end_writing(); + goto abort_read_write; + break; + case (int)Event::DataWritten: + header_[distance_into_section_] = input_; + write_byte(input_); + has_input_ = false; + distance_into_section_++; + if(distance_into_section_ < 4) { + SetDataRequest(); + goto format_track_write_header; + } + break; } + + printf("W: %02x %02x %02x %02x, %04x\n", header_[0], header_[1], header_[2], header_[3], get_crc_generator().get_value()); write_crc(); // Write the sector body. - write_id_data_joiner(false); + write_id_data_joiner(false, false); write_n_bytes(128 << command_[2], command_[5]); write_crc(); @@ -617,7 +646,7 @@ void i8272::posit_event(int event_type) { // Consider repeating. sector_++; - if(sector_ < command_[3]) + if(sector_ < command_[3] && !index_hole_count_) goto format_track_write_sector; // Otherwise, pad out to the index hole. diff --git a/Components/8272/i8272.hpp b/Components/8272/i8272.hpp index deae4e56c..c224168aa 100644 --- a/Components/8272/i8272.hpp +++ b/Components/8272/i8272.hpp @@ -117,7 +117,7 @@ class i8272: public Storage::Disk::MFMController { // Transient storage and counters used while reading the disk. uint8_t header_[6]; int distance_into_section_; - int index_hole_limit_; + int index_hole_count_, index_hole_limit_; // Keeps track of the drive and head in use during commands. int active_drive_; diff --git a/Storage/Disk/DiskController.cpp b/Storage/Disk/DiskController.cpp index c07eea966..ad0a9303b 100644 --- a/Storage/Disk/DiskController.cpp +++ b/Storage/Disk/DiskController.cpp @@ -121,8 +121,9 @@ Storage::Time Controller::get_time_into_track() { #pragma mark - Writing -void Controller::begin_writing() { +void Controller::begin_writing(bool clamp_to_index_hole) { is_reading_ = false; + clamp_writing_to_index_hole_ = clamp_to_index_hole; write_segment_.length_of_a_bit = bit_length_ / rotational_multiplier_; write_segment_.data.clear(); @@ -148,9 +149,14 @@ void Controller::end_writing() { patched_track_ = std::dynamic_pointer_cast(track_); if(!patched_track_) { patched_track_.reset(new PCMPatchedTrack(track_)); + } else { + printf(""); } + } else { + printf(""); } - patched_track_->add_segment(write_start_time_, write_segment_); + patched_track_->add_segment(write_start_time_, write_segment_, clamp_writing_to_index_hole_); + cycles_since_index_hole_ %= 8000000 * clock_rate_multiplier_; invalidate_track(); // TEMPORARY: to force a seek } @@ -205,8 +211,7 @@ bool Controller::get_motor_on() { } void Controller::set_drive(std::shared_ptr drive) { - if(drive_ != drive) - { + if(drive_ != drive) { invalidate_track(); drive_ = drive; } diff --git a/Storage/Disk/DiskController.hpp b/Storage/Disk/DiskController.hpp index 59c111f59..399a92d84 100644 --- a/Storage/Disk/DiskController.hpp +++ b/Storage/Disk/DiskController.hpp @@ -72,8 +72,11 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop @c write_bit. They will be written with the length set via @c set_expected_bit_length. It is acceptable to supply a backlog of bits. Flux transition events will not be reported while writing. + + @param clamp_to_index_hole If @c true then writing will automatically be truncated by + the index hole. Writing will continue over the index hole otherwise. */ - void begin_writing(); + void begin_writing(bool clamp_to_index_hole); /*! Writes the bit @c value as the next in the PCM stream initiated by @c begin_writing. @@ -129,6 +132,7 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop bool motor_is_on_; bool is_reading_; + bool clamp_writing_to_index_hole_; std::shared_ptr patched_track_; PCMSegment write_segment_; Time write_start_time_; diff --git a/Storage/Disk/PCMPatchedTrack.cpp b/Storage/Disk/PCMPatchedTrack.cpp index f71f56e39..cf3b9e3d0 100644 --- a/Storage/Disk/PCMPatchedTrack.cpp +++ b/Storage/Disk/PCMPatchedTrack.cpp @@ -31,15 +31,18 @@ Track *PCMPatchedTrack::clone() { return new PCMPatchedTrack(*this); } -void PCMPatchedTrack::add_segment(const Time &start_time, const PCMSegment &segment) { +void PCMPatchedTrack::add_segment(const Time &start_time, const PCMSegment &segment, bool clamp_to_index_hole) { std::shared_ptr event_source(new PCMSegmentEventSource(segment)); Time zero(0); + Time one(1); Time end_time = start_time + event_source->get_length(); + if(clamp_to_index_hole && end_time > one) { + end_time = one; + } Period insertion_period(start_time, end_time, zero, event_source); // the new segment may wrap around, so divide it up into track-length parts if required - Time one = Time(1); assert(insertion_period.start_time <= one); while(insertion_period.end_time > one) { Time next_end_time = insertion_period.end_time - one; diff --git a/Storage/Disk/PCMPatchedTrack.hpp b/Storage/Disk/PCMPatchedTrack.hpp index 23e7bd3a1..7d14adf30 100644 --- a/Storage/Disk/PCMPatchedTrack.hpp +++ b/Storage/Disk/PCMPatchedTrack.hpp @@ -34,8 +34,13 @@ class PCMPatchedTrack: public Track { /*! Replaces whatever is currently on the track from @c start_position to @c start_position + segment length with the contents of @c segment. + + @param start_time The time at which this segment begins. Must be in the range [0, 1). + @param segment The PCM segment to add. + @param clamp_to_index_hole If @c true then the new segment will be truncated if it overruns the index hole; + it will otherwise write over the index hole and continue. */ - void add_segment(const Time &start_time, const PCMSegment &segment); + void add_segment(const Time &start_time, const PCMSegment &segment, bool clamp_to_index_hole); // To satisfy Storage::Disk::Track Event get_next_event();