From 7d132f81f7a680ee2dae74eb7bfe9c8159ea2ce7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 10:50:28 -0400 Subject: [PATCH 01/14] Increased logging by quite a distance and made an attempt once again to allow the processor some time to supply the first byte when writing before declaring overrun. --- Components/8272/i8272.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 03a565f16..9d4796931 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -315,12 +315,12 @@ void i8272::posit_event(int event_type) { // values in the internal registers. index_hole_limit_ = 2; set_data_mode(DataMode::Scanning); -// printf("Seeking %02x %02x %02x %02x\n", cylinder_, head_, sector_, size_); + printf("Seeking %02x %02x %02x %02x\n", cylinder_, head_, sector_, size_); find_next_sector: FIND_HEADER(); if(!index_hole_limit_) { // Two index holes have passed wihout finding the header sought. -// printf("Not found\n"); + printf("Not found\n"); SetNoData(); goto abort_read_write; } @@ -329,17 +329,18 @@ void i8272::posit_event(int event_type) { // 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\n", header_[0], header_[1], header_[2], header_[3]); 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 -// printf("Proceeding\n"); + printf("Proceeding\n"); switch(command_[0] & 0x1f) { case 0x06: // read data case 0x0c: // read deleted data goto read_data_found_header; case 0x05: // write data + goto write_data_found_header; case 0x09: // write deleted data goto write_data_found_header; } @@ -351,7 +352,7 @@ void i8272::posit_event(int event_type) { // Performs the read data or read deleted data command. read_data: - printf("Read data [%02x %02x %02x %02x ... %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[8]); + printf("Read [deleted] data [%02x %02x %02x %02x ... %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[8]); if(!dma_mode_) SetNonDMAExecution(); SET_DRIVE_HEAD_MFM(); read_next_data: @@ -419,7 +420,7 @@ void i8272::posit_event(int event_type) { goto post_st012chrn; write_data: - printf("Write [deleted] data\n"); + printf("Write [deleted] data [%02x %02x %02x %02x ... %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[8]); if(!dma_mode_) SetNonDMAExecution(); SET_DRIVE_HEAD_MFM(); @@ -438,7 +439,6 @@ void i8272::posit_event(int event_type) { SetDataDirectionFromProcessor(); SetDataRequest(); - WAIT_FOR_EVENT(Event::DataWritten); expects_input_ = true; distance_into_section_ = 0; @@ -462,6 +462,11 @@ void i8272::posit_event(int event_type) { WAIT_FOR_EVENT(Event::DataWritten); end_writing(); + if(sector_ != command_[6] && !ControlMark()) { + sector_++; + goto write_next_data; + } + goto post_st012chrn; // Performs the read ID command. From 9d77f336114a6cb3e94d8da40e361905408559d5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 11:06:10 -0400 Subject: [PATCH 02/14] Dealt with another source of repeating magic constants: the command numbers. --- Components/8272/i8272.cpp | 88 +++++++++++++++++++++++++-------------- 1 file changed, 57 insertions(+), 31 deletions(-) diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 9d4796931..9d7658319 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -52,6 +52,29 @@ using namespace Intel::i8272; #define SetBadCylinder() (status_[2] |= 0x02) #define SetMissingDataAddressMark() (status_[2] |= 0x01) +namespace { + const uint8_t CommandReadData = 0x06; + const uint8_t CommandReadDeletedData = 0x0c; + + const uint8_t CommandWriteData = 0x05; + const uint8_t CommandWriteDeletedData = 0x09; + + const uint8_t CommandReadTrack = 0x02; + const uint8_t CommandReadID = 0x0a; + const uint8_t CommandFormatTrack = 0x0d; + + const uint8_t CommandScanLow = 0x11; + const uint8_t CommandScanLowOrEqual = 0x19; + const uint8_t CommandScanHighOrEqual = 0x1d; + + const uint8_t CommandRecalibrate = 0x07; + const uint8_t CommandSeek = 0x0f; + + const uint8_t CommandSenseInterruptStatus = 0x08; + const uint8_t CommandSpecify = 0x03; + const uint8_t CommandSenseDriveStatus = 0x04; +} + i8272::i8272(BusHandler &bus_handler, Cycles clock_rate, int clock_rate_multiplier, int revolutions_per_minute) : Storage::Disk::MFMController(clock_rate, clock_rate_multiplier, revolutions_per_minute), bus_handler_(bus_handler), @@ -260,11 +283,11 @@ void i8272::posit_event(int event_type) { // If this is not clearly a command that's safe to carry out in parallel to a seek, end all seeks. switch(command_[0] & 0x1f) { - case 0x03: // specify - case 0x04: // sense drive status - case 0x07: // recalibrate - case 0x08: // sense interrupt status - case 0x0f: // seek + case CommandSpecify: + case CommandSenseDriveStatus: + case CommandSenseInterruptStatus: + case CommandRecalibrate: + case CommandSeek: break; default: @@ -279,27 +302,30 @@ void i8272::posit_event(int event_type) { // Jump to the proper place. switch(command_[0] & 0x1f) { - case 0x06: // read data - case 0x0c: // read deleted data + case CommandReadData: + case CommandReadDeletedData: goto read_data; - case 0x05: // write data - case 0x09: // write deleted data + case CommandWriteData: + case CommandWriteDeletedData: goto write_data; - case 0x02: goto read_track; - case 0x0a: goto read_id; - case 0x0d: goto format_track; - case 0x11: goto scan_low; - case 0x19: goto scan_low_or_equal; - case 0x1d: goto scan_high_or_equal; - case 0x07: goto recalibrate; - case 0x08: goto sense_interrupt_status; - case 0x03: goto specify; - case 0x04: goto sense_drive_status; - case 0x0f: goto seek; + case CommandReadTrack: goto read_track; + case CommandReadID: goto read_id; + case CommandFormatTrack: goto format_track; - default: goto invalid; + case CommandScanLow: goto scan_low; + case CommandScanLowOrEqual: goto scan_low_or_equal; + case CommandScanHighOrEqual: goto scan_high_or_equal; + + case CommandRecalibrate: goto recalibrate; + case CommandSeek: goto seek; + + case CommandSenseInterruptStatus: goto sense_interrupt_status; + case CommandSpecify: goto specify; + case CommandSenseDriveStatus: goto sense_drive_status; + + default: goto invalid; } // Decodes drive, head and density, loads the head, loads the internal cylinder, head, sector and size registers, @@ -335,16 +361,16 @@ void i8272::posit_event(int event_type) { // Branch to whatever is supposed to happen next printf("Proceeding\n"); switch(command_[0] & 0x1f) { - case 0x06: // read data - case 0x0c: // read deleted data + case CommandReadData: + case CommandReadDeletedData: goto read_data_found_header; - case 0x05: // write data - goto write_data_found_header; - case 0x09: // write deleted data + case CommandWriteData: // write data + case CommandWriteDeletedData: // write deleted data goto write_data_found_header; } + // Sets abnormal termination of the current command and proceeds to an ST0, ST1, ST2, C, H, R, N result phase. abort_read_write: SetAbnormalTermination(); @@ -352,7 +378,7 @@ void i8272::posit_event(int event_type) { // Performs the read data or read deleted data command. read_data: - printf("Read [deleted] data [%02x %02x %02x %02x ... %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[8]); + printf("Read [deleted] data [%02x %02x %02x %02x ... %02x %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[6], command_[8]); if(!dma_mode_) SetNonDMAExecution(); SET_DRIVE_HEAD_MFM(); read_next_data: @@ -362,7 +388,7 @@ void i8272::posit_event(int event_type) { // flag doesn't match the sort the command was looking for. read_data_found_header: FIND_DATA(); - if((get_latest_token().type == Token::Data) != ((command_[0]&0xf) == 0x6)) { + if((get_latest_token().type == Token::Data) != ((command_[0] & 0x1f) == CommandReadData)) { if(!(command_[0]&0x20)) { // SK is not set; set the error flag but read this sector before finishing. SetControlMark(); @@ -420,7 +446,7 @@ void i8272::posit_event(int event_type) { goto post_st012chrn; write_data: - printf("Write [deleted] data [%02x %02x %02x %02x ... %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[8]); + printf("Write [deleted] data [%02x %02x %02x %02x ... %02x %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[6], command_[8]); if(!dma_mode_) SetNonDMAExecution(); SET_DRIVE_HEAD_MFM(); @@ -435,7 +461,7 @@ void i8272::posit_event(int event_type) { write_data_found_header: begin_writing(); - write_id_data_joiner((command_[0] & 0x1f) == 0x09); + write_id_data_joiner((command_[0] & 0x1f) == CommandWriteDeletedData); SetDataDirectionFromProcessor(); SetDataRequest(); @@ -462,7 +488,7 @@ void i8272::posit_event(int event_type) { WAIT_FOR_EVENT(Event::DataWritten); end_writing(); - if(sector_ != command_[6] && !ControlMark()) { + if(sector_ != command_[6]) { sector_++; goto write_next_data; } From 944222eba432e6a9fa5b5eeb8d14a14dbcbf5538 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 15:29:23 -0400 Subject: [PATCH 03/14] Added: `write_id_data_joiner` can now be instructed not to write the first portion of gap. Which makes more sense as an option, to avoiding splicing errors. --- Storage/Disk/MFMDiskController.cpp | 8 ++++---- Storage/Disk/MFMDiskController.hpp | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Storage/Disk/MFMDiskController.cpp b/Storage/Disk/MFMDiskController.cpp index 4b30c8ff6..1ae81df9a 100644 --- a/Storage/Disk/MFMDiskController.cpp +++ b/Storage/Disk/MFMDiskController.cpp @@ -199,15 +199,15 @@ void MFMController::write_id_joiner() { } } -void MFMController::write_id_data_joiner(bool is_deleted) { +void MFMController::write_id_data_joiner(bool is_deleted, bool skip_first_gap) { if(get_is_double_density()) { - write_n_bytes(22, 0x4e); + if(!skip_first_gap) write_n_bytes(22, 0x4e); write_n_bytes(12, 0x00); for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMSync); get_crc_generator().set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue); write_byte(is_deleted ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte); } else { - write_n_bytes(11, 0xff); + if(!skip_first_gap) write_n_bytes(11, 0xff); write_n_bytes(6, 0x00); get_crc_generator().reset(); get_crc_generator().add(is_deleted ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte); @@ -227,7 +227,7 @@ void MFMController::write_start_of_track() { if(get_is_double_density()) { write_n_bytes(80, 0x4e); write_n_bytes(12, 0x00); - for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMIndexSync); + for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMIndexSync); write_byte(Storage::Encodings::MFM::IndexAddressByte); write_n_bytes(50, 0x4e); } else { diff --git a/Storage/Disk/MFMDiskController.hpp b/Storage/Disk/MFMDiskController.hpp index b5bef5bd2..206246ba2 100644 --- a/Storage/Disk/MFMDiskController.hpp +++ b/Storage/Disk/MFMDiskController.hpp @@ -125,11 +125,12 @@ class MFMController: public Controller { void write_id_joiner(); /*! - Writes everything that should, per the spec, appear after the ID's CRC, up to and + Writes at most what should, per the spec, appear after the ID's CRC, up to and including the mark that indicates the beginning of data, appropriately seeding - the CRC generator. + the CRC generator; if @c skip_first_gap is set then the initial gap after the + CRC isn't written. */ - void write_id_data_joiner(bool is_deleted); + void write_id_data_joiner(bool is_deleted, bool skip_first_gap); /*! Writes the gap expected after a sector's data CRC and before the beginning of the From 9541a2a5f06dd2e3ddbca4164c0587542474eff7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 15:54:09 -0400 Subject: [PATCH 04/14] Corrections: `seek_to` now takes the `segment_start_time` into account, correcting a windowing error where segments overlay other segments. Also added some asserts while bug hunting, and corrected the steps taken when inserting a longer-than-a-track segment so that each is correctly windowed. --- Storage/Disk/PCMPatchedTrack.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Storage/Disk/PCMPatchedTrack.cpp b/Storage/Disk/PCMPatchedTrack.cpp index db865b331..f71f56e39 100644 --- a/Storage/Disk/PCMPatchedTrack.cpp +++ b/Storage/Disk/PCMPatchedTrack.cpp @@ -8,6 +8,8 @@ #include "PCMPatchedTrack.hpp" +#include + using namespace Storage::Disk; PCMPatchedTrack::PCMPatchedTrack(std::shared_ptr underlying_track) : @@ -38,11 +40,13 @@ void PCMPatchedTrack::add_segment(const Time &start_time, const PCMSegment &segm // 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; insertion_period.end_time = one; insert_period(insertion_period); + insertion_period.segment_start_time += one; insertion_period.start_time = zero; insertion_period.end_time = next_end_time; } @@ -127,8 +131,7 @@ void PCMPatchedTrack::insert_period(const Period &period) { } } -Track::Event PCMPatchedTrack::get_next_event() -{ +Track::Event PCMPatchedTrack::get_next_event() { const Time one(1); const Time zero(0); Time extra_time(0); @@ -184,11 +187,14 @@ Track::Event PCMPatchedTrack::get_next_event() Storage::Time PCMPatchedTrack::seek_to(const Time &time_since_index_hole) { // start at the beginning and continue while segments end before reaching the time sought active_period_ = periods_.begin(); - while(active_period_->end_time < time_since_index_hole) active_period_++; + while(active_period_->end_time < time_since_index_hole) { + assert(active_period_ != periods_.end()); + active_period_++; + } // allow whatever storage represents the period found to perform its seek if(active_period_->event_source) - current_time_ = active_period_->event_source->seek_to(time_since_index_hole - active_period_->start_time) + active_period_->start_time; + current_time_ = active_period_->event_source->seek_to(active_period_->segment_start_time + time_since_index_hole - active_period_->start_time) + active_period_->start_time; else current_time_ = underlying_track_->seek_to(time_since_index_hole); return current_time_; From 73080d6c366afb7fecc20d86916ff13ec7137838 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 16:05:10 -0400 Subject: [PATCH 05/14] 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(); From c196f0018f924300948c8dc4f55ca5fc580f557c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 16:15:09 -0400 Subject: [PATCH 06/14] Upped the assert quotient. --- Storage/Disk/DiskController.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Storage/Disk/DiskController.cpp b/Storage/Disk/DiskController.cpp index ad0a9303b..cdee750b7 100644 --- a/Storage/Disk/DiskController.cpp +++ b/Storage/Disk/DiskController.cpp @@ -8,6 +8,7 @@ #include "DiskController.hpp" #include "../../NumberTheory/Factors.hpp" +#include using namespace Storage::Disk; @@ -32,8 +33,10 @@ void Controller::setup_track() { Time offset; Time track_time_now = get_time_into_track(); + assert(track_time_now >= Time(0) && current_event_.length <= Time(1)); if(track_) { Time time_found = track_->seek_to(track_time_now); + assert(time_found >= Time(0) && time_found <= track_time_now); offset = track_time_now - time_found; } @@ -93,7 +96,9 @@ void Controller::get_next_event(const Time &duration_already_passed) { // divide interval, which is in terms of a single rotation of the disk, by rotation speed to // convert it into revolutions per second; this is achieved by multiplying by rotational_multiplier_ - set_next_event_time_interval((current_event_.length - duration_already_passed) * rotational_multiplier_); + assert(current_event_.length <= Time(1) && current_event_.length >= Time(0)); + Time interval = (current_event_.length - duration_already_passed) * rotational_multiplier_; + set_next_event_time_interval(interval); } void Controller::process_next_event() From 7b92b235e10168f842d0809d8f0e30675694a44a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 16:25:46 -0400 Subject: [PATCH 07/14] Further upped `assert`s, thereby discovering the mistake I'd recently introduced: seeking properly within the event source as per its potential left-clipping, but then not allowing for that in the calculated current time. --- Storage/Disk/PCMPatchedTrack.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Storage/Disk/PCMPatchedTrack.cpp b/Storage/Disk/PCMPatchedTrack.cpp index cf3b9e3d0..d1cb9eed7 100644 --- a/Storage/Disk/PCMPatchedTrack.cpp +++ b/Storage/Disk/PCMPatchedTrack.cpp @@ -195,11 +195,14 @@ Storage::Time PCMPatchedTrack::seek_to(const Time &time_since_index_hole) { active_period_++; } - // allow whatever storage represents the period found to perform its seek + // allow whatever storage represents the period found to perform its seek; calculation for periods + // with an event source is, in effect: seek_to(offset_into_segment + distance_into_period) - offset_into_segment. if(active_period_->event_source) - current_time_ = active_period_->event_source->seek_to(active_period_->segment_start_time + time_since_index_hole - active_period_->start_time) + active_period_->start_time; + current_time_ = active_period_->event_source->seek_to(active_period_->segment_start_time + time_since_index_hole - active_period_->start_time) + active_period_->start_time - active_period_->segment_start_time; else current_time_ = underlying_track_->seek_to(time_since_index_hole); + + assert(current_time_ <= time_since_index_hole); return current_time_; } From 75a9d2bb334601d2277ed08cde20377b55c29c93 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 20:12:01 -0400 Subject: [PATCH 08/14] Started withdrawing logging, reduced index hole count back to the correct number and tried to increase `read_data` rigour. --- Components/8272/i8272.cpp | 95 ++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 37 deletions(-) diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 9b71a84ee..2347b3617 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -204,7 +204,9 @@ void i8272::set_disk(std::shared_ptr disk, int drive) { #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__); + if(event_type == (int)Event::Token) { \ + if(get_latest_token().type == Token::Byte) goto CONCAT(find_data, __LINE__); \ + } #define READ_HEADER() \ distance_into_section_ = 0; \ @@ -351,27 +353,33 @@ 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_ = 6; - printf("Seeking %02x %02x %02x %02x\n", cylinder_, head_, sector_, size_); + index_hole_limit_ = 2; +// printf("Seeking %02x %02x %02x %02x\n", cylinder_, head_, sector_, size_); find_next_sector: FIND_HEADER(); if(!index_hole_limit_) { // Two index holes have passed wihout finding the header sought. - printf("Not found\n"); +// printf("Not found\n"); SetNoData(); - goto abort_read_write; + goto abort; } - printf("Header\n"); + index_hole_count_ = 0; +// printf("Header\n"); READ_HEADER(); + if(index_hole_count_) { + // This implies an index hole was sighted within the header. Error out. + SetEndOfCylinder(); + goto abort; + } 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 [%04x]\n", header_[0], header_[1], header_[2], header_[3], get_crc_generator().get_value()); +// 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 - printf("Proceeding\n"); +// printf("Proceeding\n"); switch(command_[0] & 0x1f) { case CommandReadData: case CommandReadDeletedData: @@ -383,11 +391,6 @@ void i8272::posit_event(int event_type) { } - // Sets abnormal termination of the current command and proceeds to an ST0, ST1, ST2, C, H, R, N result phase. - abort_read_write: - SetAbnormalTermination(); - goto post_st012chrn; - // Performs the read data or read deleted data command. read_data: printf("Read [deleted] data [%02x %02x %02x %02x ... %02x %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[6], command_[8]); @@ -400,14 +403,27 @@ void i8272::posit_event(int event_type) { // flag doesn't match the sort the command was looking for. read_data_found_header: FIND_DATA(); - if((get_latest_token().type == Token::Data) != ((command_[0] & 0x1f) == CommandReadData)) { - if(!(command_[0]&0x20)) { - // SK is not set; set the error flag but read this sector before finishing. - SetControlMark(); + if(event_type == (int)Event::Token) { + if(get_latest_token().type != Token::Data && get_latest_token().type != Token::DeletedData) { + // Something other than a data mark came next — impliedly an ID or index mark. + SetMissingAddressMark(); + SetMissingDataAddressMark(); + goto abort; // TODO: or read_next_data? } else { - // SK is set; skip this sector. - goto read_next_data; + if((get_latest_token().type == Token::Data) != ((command_[0] & 0x1f) == CommandReadData)) { + if(!(command_[0]&0x20)) { + // SK is not set; set the error flag but read this sector before finishing. + SetControlMark(); + } else { + // SK is set; skip this sector. + goto read_next_data; + } + } } + } else { + // An index hole appeared before the data mark. + SetEndOfCylinder(); + goto abort; // TODO: or read_next_data? } distance_into_section_ = 0; @@ -418,12 +434,14 @@ void i8272::posit_event(int event_type) { // // TODO: consider DTL. read_data_get_byte: - WAIT_FOR_EVENT(Event::Token); - result_stack_.push_back(get_latest_token().byte_value); - distance_into_section_++; - SetDataRequest(); - SetDataDirectionToProcessor(); - WAIT_FOR_EVENT((int)Event8272::ResultEmpty | (int)Event::Token | (int)Event::IndexHole); + WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole); + if(event_type == (int)Event::Token) { + result_stack_.push_back(get_latest_token().byte_value); + distance_into_section_++; + SetDataRequest(); + SetDataDirectionToProcessor(); + WAIT_FOR_EVENT((int)Event8272::ResultEmpty | (int)Event::Token | (int)Event::IndexHole); + } switch(event_type) { case (int)Event8272::ResultEmpty: // The caller read the byte in time; proceed as normal. ResetDataRequest(); @@ -431,9 +449,11 @@ void i8272::posit_event(int event_type) { break; case (int)Event::Token: // The caller hasn't read the old byte yet and a new one has arrived SetOverrun(); - goto abort_read_write; + goto abort; break; case (int)Event::IndexHole: + SetEndOfCylinder(); + goto abort; break; } @@ -444,7 +464,7 @@ void i8272::posit_event(int event_type) { // This implies a CRC error in the sector; mark as such and temrinate. SetDataError(); SetDataFieldDataError(); - goto abort_read_write; + goto abort; } // check whether that's it: either the final requested sector has been read, or because @@ -462,13 +482,9 @@ 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; + goto abort; } write_next_data: @@ -490,7 +506,7 @@ void i8272::posit_event(int event_type) { if(!has_input_) { SetOverrun(); end_writing(); - goto abort_read_write; + goto abort; } write_byte(input_); has_input_ = false; @@ -527,7 +543,7 @@ void i8272::posit_event(int event_type) { FIND_HEADER(); if(!index_hole_limit_) { SetNoData(); - goto abort_read_write; + goto abort; } READ_HEADER(); @@ -557,7 +573,7 @@ void i8272::posit_event(int event_type) { if(!index_hole_limit_) { if(!sector_) { SetMissingAddressMark(); - goto abort_read_write; + goto abort; } else { goto post_st012chrn; } @@ -589,7 +605,7 @@ void i8272::posit_event(int event_type) { SET_DRIVE_HEAD_MFM(); if(drives_[active_drive_].drive->get_is_read_only()) { SetNotWriteable(); - goto abort_read_write; + goto abort; } LOAD_HEAD(); @@ -619,7 +635,7 @@ void i8272::posit_event(int event_type) { case (int)Event::IndexHole: SetOverrun(); end_writing(); - goto abort_read_write; + goto abort; break; case (int)Event::DataWritten: header_[distance_into_section_] = input_; @@ -777,6 +793,11 @@ void i8272::posit_event(int event_type) { result_stack_.push_back(0x80); goto post_result; + // Sets abnormal termination of the current command and proceeds to an ST0, ST1, ST2, C, H, R, N result phase. + abort: + SetAbnormalTermination(); + goto post_st012chrn; + // Posts ST0, ST1, ST2, C, H, R and N as a result phase. post_st012chrn: SCHEDULE_HEAD_UNLOAD(); From 709257a0c532b42e8aff1d4ef032e79d2e8a4c8b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 20:16:56 -0400 Subject: [PATCH 09/14] Quick fix: also treat reception of sync as a reason not to stop looking for a data address mark. --- Components/8272/i8272.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 2347b3617..aeb492b5d 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -205,7 +205,7 @@ void i8272::set_disk(std::shared_ptr disk, int drive) { set_data_mode(DataMode::Scanning); \ CONCAT(find_data, __LINE__): WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole); \ if(event_type == (int)Event::Token) { \ - if(get_latest_token().type == Token::Byte) goto CONCAT(find_data, __LINE__); \ + if(get_latest_token().type == Token::Byte || get_latest_token().type == Token::Sync) goto CONCAT(find_data, __LINE__); \ } #define READ_HEADER() \ From 56de5cb2b307361266085682c581fa7340faf67c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 20:18:32 -0400 Subject: [PATCH 10/14] Removed one further logging event. --- Components/8272/i8272.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index aeb492b5d..63384b708 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -195,7 +195,7 @@ void i8272::set_disk(std::shared_ptr disk, int drive) { #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) { printf("I\n"); index_hole_limit_--; } \ + if(event_type == (int)Event::IndexHole) { 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__); \ @@ -216,8 +216,6 @@ void i8272::set_disk(std::shared_ptr disk, int drive) { distance_into_section_++; \ if(distance_into_section_ < 6) goto CONCAT(read_header, __LINE__); \ -// printf("%02x -> %04x\n", header_[distance_into_section_], get_crc_generator().get_value()); \ - #define SET_DRIVE_HEAD_MFM() \ active_drive_ = command_[1]&3; \ active_head_ = (command_[1] >> 2)&1; \ @@ -232,8 +230,6 @@ void i8272::set_disk(std::shared_ptr disk, int drive) { 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; \ From d12c834f9cec2cf4fb87120813a4c4ea577680bb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 20:35:33 -0400 Subject: [PATCH 11/14] Increased amount reported. --- Components/8272/i8272.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 63384b708..26e1367f0 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -812,7 +812,7 @@ void i8272::posit_event(int event_type) { // Posts whatever is in result_stack_ as a result phase. Be aware that it is a stack — the // last thing in it will be returned first. post_result: - printf("Result to %02x: ", command_[0] & 0x1f); + printf("Result to %02x, main %02x: ", command_[0] & 0x1f, main_status_); for(size_t c = 0; c < result_stack_.size(); c++) { printf("%02x ", result_stack_[result_stack_.size() - 1 - c]); } From aefbafa18dfff8d666047f76f191e9e25dc5e30a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 21:49:10 -0400 Subject: [PATCH 12/14] Centralised the 8272's actions of setting the non-DMA execution flag, picking the drive and head and loading the head for accessing commands, and switched error flag if read ID doesn't find anything. --- Components/8272/i8272.cpp | 56 +++++++++++++++++++-------------------- Components/8272/i8272.hpp | 1 + 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/Components/8272/i8272.cpp b/Components/8272/i8272.cpp index 26e1367f0..e6b79f217 100644 --- a/Components/8272/i8272.cpp +++ b/Components/8272/i8272.cpp @@ -293,23 +293,38 @@ void i8272::posit_event(int event_type) { // If this is not clearly a command that's safe to carry out in parallel to a seek, end all seeks. switch(command_[0] & 0x1f) { - case CommandSpecify: - case CommandSenseDriveStatus: - case CommandSenseInterruptStatus: - case CommandRecalibrate: - case CommandSeek: + case CommandReadData: + case CommandReadDeletedData: + case CommandWriteData: + case CommandWriteDeletedData: + case CommandReadTrack: + case CommandReadID: + case CommandFormatTrack: + case CommandScanLow: + case CommandScanLowOrEqual: + case CommandScanHighOrEqual: + is_access_command_ = true; break; default: - for(int c = 0; c < 4; c++) { - if(drives_[c].phase == Drive::Seeking) { - drives_[c].phase = Drive::NotSeeking; - drives_seeking_--; - } - } + is_access_command_ = false; break; } + if(is_access_command_) { + for(int c = 0; c < 4; c++) { + if(drives_[c].phase == Drive::Seeking) { + drives_[c].phase = Drive::NotSeeking; + drives_seeking_--; + } + } + // Establishes the drive and head being addressed, and whether in double density mode; populates the internal + // cylinder, head, sector and size registers from the command stream. + if(!dma_mode_) SetNonDMAExecution(); + SET_DRIVE_HEAD_MFM(); + LOAD_HEAD(); + } + // Jump to the proper place. switch(command_[0] & 0x1f) { case CommandReadData: @@ -342,9 +357,6 @@ void i8272::posit_event(int event_type) { // and searches for a sector that meets those criteria. If one is found, inspects the instruction in use and // jumps to an appropriate handler. read_write_find_header: - // Establishes the drive and head being addressed, and whether in double density mode; populates the internal - // cylinder, head, sector and size registers from the command stream. - LOAD_HEAD(); // 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 @@ -390,8 +402,6 @@ void i8272::posit_event(int event_type) { // Performs the read data or read deleted data command. read_data: printf("Read [deleted] data [%02x %02x %02x %02x ... %02x %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[6], command_[8]); - if(!dma_mode_) SetNonDMAExecution(); - SET_DRIVE_HEAD_MFM(); read_next_data: goto read_write_find_header; @@ -475,8 +485,6 @@ void i8272::posit_event(int event_type) { write_data: printf("Write [deleted] data [%02x %02x %02x %02x ... %02x %02x]\n", command_[2], command_[3], command_[4], command_[5], command_[6], command_[8]); - if(!dma_mode_) SetNonDMAExecution(); - SET_DRIVE_HEAD_MFM(); if(drives_[active_drive_].drive->get_is_read_only()) { SetNotWriteable(); @@ -528,9 +536,7 @@ void i8272::posit_event(int event_type) { // Performs the read ID command. read_id: // Establishes the drive and head being addressed, and whether in double density mode. - printf("Read ID\n"); - SET_DRIVE_HEAD_MFM(); - LOAD_HEAD(); + printf("Read ID [%02x %02x]\n", command_[0], command_[1]); // Sets a maximum index hole limit of 2 then waits either until it finds a header mark or sees too many index holes. // If a header mark is found, reads in the following bytes that produce a header. Otherwise branches to data not found. @@ -538,7 +544,7 @@ void i8272::posit_event(int event_type) { read_id_find_next_sector: FIND_HEADER(); if(!index_hole_limit_) { - SetNoData(); + SetMissingAddressMark(); goto abort; } READ_HEADER(); @@ -554,8 +560,6 @@ void i8272::posit_event(int event_type) { // Performs read track. read_track: printf("Read track [%02x %02x %02x %02x]\n", command_[2], command_[3], command_[4], command_[5]); - if(!dma_mode_) SetNonDMAExecution(); - SET_DRIVE_HEAD_MFM(); // Wait for the index hole. WAIT_FOR_EVENT(Event::IndexHole); @@ -597,15 +601,11 @@ void i8272::posit_event(int event_type) { // Performs format [/write] track. format_track: printf("Format track\n"); - if(!dma_mode_) SetNonDMAExecution(); - SET_DRIVE_HEAD_MFM(); if(drives_[active_drive_].drive->get_is_read_only()) { SetNotWriteable(); goto abort; } - LOAD_HEAD(); - // Wait for the index hole. WAIT_FOR_EVENT(Event::IndexHole); index_hole_count_ = 0; diff --git a/Components/8272/i8272.hpp b/Components/8272/i8272.hpp index c224168aa..44dc899af 100644 --- a/Components/8272/i8272.hpp +++ b/Components/8272/i8272.hpp @@ -66,6 +66,7 @@ class i8272: public Storage::Disk::MFMController { void posit_event(int type); int interesting_event_mask_; int resume_point_; + bool is_access_command_; // The counter used for ::Timer events. int delay_time_; From 6cfc3daacb6c225f35b0412348dcd9e5d012fc71 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 21:52:12 -0400 Subject: [PATCH 13/14] Introduced a test within the disk controller so as not to request illegal tracks from disks, instead automatically substituting an 'unformatted' track. Which is just empty. --- .../Clock Signal.xcodeproj/project.pbxproj | 6 ++++ Storage/Disk/Disk.cpp | 3 ++ Storage/Disk/DiskController.cpp | 13 ++++---- Storage/Disk/PCMPatchedTrack.hpp | 2 +- Storage/Disk/UnformattedTrack.cpp | 26 ++++++++++++++++ Storage/Disk/UnformattedTrack.hpp | 30 +++++++++++++++++++ 6 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 Storage/Disk/UnformattedTrack.cpp create mode 100644 Storage/Disk/UnformattedTrack.hpp diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index b77bb0dd8..10b16ea4c 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -121,6 +121,7 @@ 4BAB62B51D327F7E00DF5BA0 /* G64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B31D327F7E00DF5BA0 /* G64.cpp */; }; 4BAB62B81D3302CA00DF5BA0 /* PCMTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */; }; 4BACC5B11F3DFF7C0037C015 /* CharacterMapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */; }; + 4BAD9B961F43D7E900724854 /* UnformattedTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BAD9B941F43D7E900724854 /* UnformattedTrack.cpp */; }; 4BB17D4E1ED7909F00ABD1E1 /* tests.expected.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */; }; 4BB17D4F1ED7909F00ABD1E1 /* tests.in.json in Resources */ = {isa = PBXBuildFile; fileRef = 4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */; }; 4BB298F11B587D8400A49093 /* start in Resources */ = {isa = PBXBuildFile; fileRef = 4BB297E51B587D8300A49093 /* start */; }; @@ -673,6 +674,8 @@ 4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMTrack.hpp; sourceTree = ""; }; 4BACC5AF1F3DFF7C0037C015 /* CharacterMapper.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CharacterMapper.cpp; path = AmstradCPC/CharacterMapper.cpp; sourceTree = ""; }; 4BACC5B01F3DFF7C0037C015 /* CharacterMapper.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CharacterMapper.hpp; path = AmstradCPC/CharacterMapper.hpp; sourceTree = ""; }; + 4BAD9B941F43D7E900724854 /* UnformattedTrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UnformattedTrack.cpp; sourceTree = ""; }; + 4BAD9B951F43D7E900724854 /* UnformattedTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = UnformattedTrack.hpp; sourceTree = ""; }; 4BB06B211F316A3F00600C7A /* ForceInline.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ForceInline.h; sourceTree = ""; }; 4BB17D4C1ED7909F00ABD1E1 /* tests.expected.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.expected.json; path = FUSE/tests.expected.json; sourceTree = ""; }; 4BB17D4D1ED7909F00ABD1E1 /* tests.in.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = tests.in.json; path = FUSE/tests.in.json; sourceTree = ""; }; @@ -1552,6 +1555,7 @@ 4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */, 4B121F961E060CF000BFDA12 /* PCMSegment.cpp */, 4BAB62B61D3302CA00DF5BA0 /* PCMTrack.cpp */, + 4BAD9B941F43D7E900724854 /* UnformattedTrack.cpp */, 4B0BE4271D3481E700D5256B /* DigitalPhaseLockedLoop.hpp */, 4BAB62AC1D3272D200DF5BA0 /* Disk.hpp */, 4B6C73BC1D387AE500AFCFCA /* DiskController.hpp */, @@ -1560,6 +1564,7 @@ 4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */, 4B121F971E060CF000BFDA12 /* PCMSegment.hpp */, 4BAB62B71D3302CA00DF5BA0 /* PCMTrack.hpp */, + 4BAD9B951F43D7E900724854 /* UnformattedTrack.hpp */, 4BB697CF1D4BA44900248BDF /* Encodings */, 4BAB62B21D327F7E00DF5BA0 /* Formats */, 4B3FE75F1F3CF6BA00448EE4 /* Parsers */, @@ -2709,6 +2714,7 @@ 4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4B95FA9D1F11893B0008E395 /* ZX8081OptionsPanel.swift in Sources */, + 4BAD9B961F43D7E900724854 /* UnformattedTrack.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4B8378DC1F336631005CA9E4 /* CharacterMapper.cpp in Sources */, 4B8378E51F3378C4005CA9E4 /* CharacterMapper.cpp in Sources */, diff --git a/Storage/Disk/Disk.cpp b/Storage/Disk/Disk.cpp index 406e99312..5844e1bea 100644 --- a/Storage/Disk/Disk.cpp +++ b/Storage/Disk/Disk.cpp @@ -28,6 +28,9 @@ void Disk::set_track_at_position(unsigned int head, unsigned int position, const } std::shared_ptr Disk::get_track_at_position(unsigned int head, unsigned int position) { + if(head >= get_head_count()) return nullptr; + if(position >= get_head_position_count()) return nullptr; + int address = get_id_for_track_at_position(head, position); std::map>::iterator cached_track = cached_tracks_.find(address); if(cached_track != cached_tracks_.end()) return cached_track->second; diff --git a/Storage/Disk/DiskController.cpp b/Storage/Disk/DiskController.cpp index cdee750b7..d90156816 100644 --- a/Storage/Disk/DiskController.cpp +++ b/Storage/Disk/DiskController.cpp @@ -7,6 +7,7 @@ // #include "DiskController.hpp" +#include "UnformattedTrack.hpp" #include "../../NumberTheory/Factors.hpp" #include @@ -30,15 +31,17 @@ Controller::Controller(Cycles clock_rate, int clock_rate_multiplier, int revolut void Controller::setup_track() { track_ = drive_->get_track(); + if(!track_) { + track_.reset(new UnformattedTrack); + } Time offset; Time track_time_now = get_time_into_track(); assert(track_time_now >= Time(0) && current_event_.length <= Time(1)); - if(track_) { - Time time_found = track_->seek_to(track_time_now); - assert(time_found >= Time(0) && time_found <= track_time_now); - offset = track_time_now - time_found; - } + + Time time_found = track_->seek_to(track_time_now); + assert(time_found >= Time(0) && time_found <= track_time_now); + offset = track_time_now - time_found; get_next_event(offset); } diff --git a/Storage/Disk/PCMPatchedTrack.hpp b/Storage/Disk/PCMPatchedTrack.hpp index 7d14adf30..842f16d0f 100644 --- a/Storage/Disk/PCMPatchedTrack.hpp +++ b/Storage/Disk/PCMPatchedTrack.hpp @@ -9,7 +9,7 @@ #ifndef PCMPatchedTrack_hpp #define PCMPatchedTrack_hpp -#include "PCMTrack.hpp" +#include "Disk.hpp" #include "PCMSegment.hpp" namespace Storage { diff --git a/Storage/Disk/UnformattedTrack.cpp b/Storage/Disk/UnformattedTrack.cpp new file mode 100644 index 000000000..b501bc8a0 --- /dev/null +++ b/Storage/Disk/UnformattedTrack.cpp @@ -0,0 +1,26 @@ +// +// UnformattedTrack.cpp +// Clock Signal +// +// Created by Thomas Harte on 15/08/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#include "UnformattedTrack.hpp" + +using namespace Storage::Disk; + +Track::Event UnformattedTrack::get_next_event() { + Track::Event event; + event.type = Event::IndexHole; + event.length = Time(1); + return event; +} + +Storage::Time UnformattedTrack::seek_to(const Time &time_since_index_hole) { + return Time(0); +} + +Track *UnformattedTrack::clone() { + return new UnformattedTrack; +} diff --git a/Storage/Disk/UnformattedTrack.hpp b/Storage/Disk/UnformattedTrack.hpp new file mode 100644 index 000000000..cdddbc8c1 --- /dev/null +++ b/Storage/Disk/UnformattedTrack.hpp @@ -0,0 +1,30 @@ +// +// UnformattedTrack.hpp +// Clock Signal +// +// Created by Thomas Harte on 15/08/2017. +// Copyright © 2017 Thomas Harte. All rights reserved. +// + +#ifndef UnformattedTrack_hpp +#define UnformattedTrack_hpp + +#include "Disk.hpp" + +namespace Storage { +namespace Disk { + +/*! + A subclass of @c Track with no contents. Just an index hole. +*/ +class UnformattedTrack: public Track { + public: + Event get_next_event(); + Time seek_to(const Time &time_since_index_hole); + Track *clone(); +}; + +} +} + +#endif /* UnformattedTrack_hpp */ From 468770b382d1216cae7ef7046aee25940cccf7c0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 15 Aug 2017 22:06:58 -0400 Subject: [PATCH 14/14] Removed debugging nonsense. --- Storage/Disk/DiskController.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Storage/Disk/DiskController.cpp b/Storage/Disk/DiskController.cpp index d90156816..6a7ae1f85 100644 --- a/Storage/Disk/DiskController.cpp +++ b/Storage/Disk/DiskController.cpp @@ -157,11 +157,7 @@ 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_, clamp_writing_to_index_hole_); cycles_since_index_hole_ %= 8000000 * clock_rate_multiplier_;