From 1ea4f0d79dcf5f7ab7aa2bd03e42bbcef7d99b40 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 31 Dec 2016 16:01:44 -0500 Subject: [PATCH 1/6] Made an attempt to implement 'write track' and ensure that 'write sector' can't end without announcing that it has ended writing. --- Components/1770/1770.cpp | 208 +++++++++++++++++++++++++++++++++++---- 1 file changed, 188 insertions(+), 20 deletions(-) diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index 70e23637c..e5e2a39a9 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -292,24 +292,6 @@ void WD1770::process_write_completed() posit_event(Event::DataWritten); } - -// +------+----------+-------------------------+ -// ! ! ! BITS ! -// ! TYPE ! COMMAND ! 7 6 5 4 3 2 1 0 ! -// +------+----------+-------------------------+ -// ! 1 ! Restore ! 0 0 0 0 h v r1 r0 ! -// ! 1 ! Seek ! 0 0 0 1 h v r1 r0 ! -// ! 1 ! Step ! 0 0 1 u h v r1 r0 ! -// ! 1 ! Step-in ! 0 1 0 u h v r1 r0 ! -// ! 1 ! Step-out ! 0 1 1 u h v r1 r0 ! -// ! 2 ! Rd sectr ! 1 0 0 m h E 0 0 ! -// ! 2 ! Wt sectr ! 1 0 1 m h E P a0 ! -// ! 3 ! Rd addr ! 1 1 0 0 h E 0 0 ! -// ! 3 ! Rd track ! 1 1 1 0 h E 0 0 ! -// ! 3 ! Wt track ! 1 1 1 1 h E P 0 ! -// ! 4 ! Forc int ! 1 1 0 1 i3 i2 i1 i0 ! -// +------+----------+-------------------------+ - #define WAIT_FOR_EVENT(mask) resume_point_ = __LINE__; interesting_event_mask_ = mask; return; case __LINE__: #define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; interesting_event_mask_ = Event::Timer; delay_time_ = ms * 8000; if(delay_time_) return; case __LINE__: #define WAIT_FOR_BYTES(count) resume_point_ = __LINE__; interesting_event_mask_ = Event::Token; distance_into_section_ = 0; return; case __LINE__: if(latest_token_.type == Token::Byte) distance_into_section_++; if(distance_into_section_ < count) { interesting_event_mask_ = Event::Token; return; } @@ -338,6 +320,22 @@ void WD1770::process_write_completed() WAIT_FOR_EVENT(Event::IndexHoleTarget); \ status_.spin_up = true; +// +--------+----------+-------------------------+ +// ! ! ! BITS ! +// ! TYPE ! COMMAND ! 7 6 5 4 3 2 1 0 ! +// +--------+----------+-------------------------+ +// ! 1 ! Restore ! 0 0 0 0 h v r1 r0 ! +// ! 1 ! Seek ! 0 0 0 1 h v r1 r0 ! +// ! 1 ! Step ! 0 0 1 u h v r1 r0 ! +// ! 1 ! Step-in ! 0 1 0 u h v r1 r0 ! +// ! 1 ! Step-out ! 0 1 1 u h v r1 r0 ! +// ! 2 ! Rd sectr ! 1 0 0 m h E 0 0 ! +// ! 2 ! Wt sectr ! 1 0 1 m h E P a0 ! +// ! 3 ! Rd addr ! 1 1 0 0 h E 0 0 ! +// ! 3 ! Rd track ! 1 1 1 0 h E 0 0 ! +// ! 3 ! Wt track ! 1 1 1 1 h E P 0 ! +// ! 4 ! Forc int ! 1 1 0 1 i3 i2 i1 i0 ! +// +--------+----------+-------------------------+ void WD1770::posit_event(Event new_event_type) { @@ -375,6 +373,17 @@ void WD1770::posit_event(Event new_event_type) /* Type 1 entry point. */ +// +--------+----------+-------------------------+ +// ! ! ! BITS ! +// ! TYPE ! COMMAND ! 7 6 5 4 3 2 1 0 ! +// +--------+----------+-------------------------+ +// ! 1 ! Restore ! 0 0 0 0 h v r1 r0 ! +// ! 1 ! Seek ! 0 0 0 1 h v r1 r0 ! +// ! 1 ! Step ! 0 0 1 u h v r1 r0 ! +// ! 1 ! Step-in ! 0 1 0 u h v r1 r0 ! +// ! 1 ! Step-out ! 0 1 1 u h v r1 r0 ! +// +--------+----------+-------------------------+ + begin_type_1: // Set initial flags, skip spin-up if possible. update_status([] (Status &status) { @@ -471,7 +480,14 @@ void WD1770::posit_event(Event new_event_type) if(distance_into_section_ == 7) { data_mode_ = DataMode::Scanning; - // TODO: CRC check + if(crc_generator_.get_value()) + { + update_status([] (Status &status) { + status.crc_error = true; + }); + goto verify_read_data; + } + if(header_[0] == track_) { printf("Reached track %d\n", track_); @@ -489,6 +505,14 @@ void WD1770::posit_event(Event new_event_type) /* Type 2 entry point. */ +// +--------+----------+-------------------------+ +// ! ! ! BITS ! +// ! TYPE ! COMMAND ! 7 6 5 4 3 2 1 0 ! +// +--------+----------+-------------------------+ +// ! 2 ! Rd sectr ! 1 0 0 m h E 0 0 ! +// ! 2 ! Wt sectr ! 1 0 1 m h E P a0 ! +// +--------+----------+-------------------------+ + begin_type_2: update_status([] (Status &status) { status.type = Status::Two; @@ -696,6 +720,7 @@ void WD1770::posit_event(Event new_event_type) WAIT_FOR_EVENT(Event::DataWritten); if(status_.data_request) { + end_writing(); update_status([] (Status &status) { status.lost_data = true; }); @@ -722,12 +747,155 @@ void WD1770::posit_event(Event new_event_type) printf("Wrote sector %d\n", sector_); goto wait_for_command; + + /* + Type 3 entry point. + */ +// +--------+----------+-------------------------+ +// ! ! ! BITS ! +// ! TYPE ! COMMAND ! 7 6 5 4 3 2 1 0 ! +// +--------+----------+-------------------------+ +// ! 3 ! Rd addr ! 1 1 0 0 h E 0 0 ! +// ! 3 ! Rd track ! 1 1 1 0 h E 0 0 ! +// ! 3 ! Wt track ! 1 1 1 1 h E P 0 ! +// +--------+----------+-------------------------+ begin_type_3: update_status([] (Status &status) { status.type = Status::Three; }); - printf("!!!TODO: type 3 commands!!!\n"); + switch(command_ >> 4) + { + case 0xa: goto begin_read_address; + case 0xc: goto begin_read_track; + case 0xf: goto begin_write_track; + } + begin_read_address: + printf("!!!TODO: read address!!!\n"); + + begin_read_track: + printf("!!!TODO: read track!!!\n"); + + begin_write_track: + update_status([] (Status &status) { + status.data_request = false; + status.lost_data = false; + }); + set_motor_on(true); + if(!(command_ & 0x08)) goto write_track_test_delay; + + index_hole_count_ = 0; + write_track_test_index_hole_count: + WAIT_FOR_EVENT(Event::IndexHoleTarget); + if(index_hole_count_target_ < 6) goto write_track_test_index_hole_count; + + write_track_test_delay: + if(!(command_&0x04)) goto write_track_test_write_protect; + WAIT_FOR_TIME(30); + + write_track_test_write_protect: + if(get_drive_is_read_only()) + { + update_status([] (Status &status) { + status.write_protect = true; + }); + goto wait_for_command; + } + + update_status([] (Status &status) { + status.data_request = true; + }); + WAIT_FOR_BYTES(3); + if(status_.data_request) + { + update_status([] (Status &status) { + status.lost_data = true; + }); + goto wait_for_command; + } + + WAIT_FOR_EVENT(Event::IndexHoleTarget); + begin_writing(); + index_hole_count_ = 0; + + write_track_write_loop: + if(is_double_density_) + { + switch(data_) + { + case 0xf5: + write_raw_short(Storage::Encodings::MFM::MFMSync); + crc_generator_.set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue); + break; + case 0xf6: + write_raw_short(Storage::Encodings::MFM::MFMIndexSync); + break; + case 0xff: { + uint16_t crc = crc_generator_.get_value(); + write_byte(crc >> 8); + write_byte(crc & 0xff); + } break; + default: + write_byte(data_); + break; + } + } + else + { + switch(data_) + { + case 0xf8: case 0xf9: case 0xfa: case 0xfb: + case 0xfd: case 0xfe: + // clock is 0xc7 = 1010 0000 0010 1010 = 0xa022 + write_raw_short( + (uint16_t)( + 0xa022 | + ((data_ & 0x80) << 7) | + ((data_ & 0x40) << 6) | + ((data_ & 0x20) << 5) | + ((data_ & 0x10) << 4) | + ((data_ & 0x08) << 3) | + ((data_ & 0x04) << 2) | + ((data_ & 0x02) << 1) | + (data_ & 0x01) + ) + ); + crc_generator_.reset(); + crc_generator_.add(data_); + break; + case 0xfc: + write_raw_short(Storage::Encodings::MFM::FMIndexAddressMark); + break; + case 0xf7: { + uint16_t crc = crc_generator_.get_value(); + write_byte(crc >> 8); + write_byte(crc & 0xff); + } break; + default: + write_byte(data_); + break; + } + } + + update_status([] (Status &status) { + status.data_request = true; + }); + WAIT_FOR_EVENT(Event::DataWritten); + if(status_.data_request) + { + update_status([] (Status &status) { + status.lost_data = true; + }); + end_writing(); + goto wait_for_command; + } + if(index_hole_count_) + { + end_writing(); + goto wait_for_command; + } + + goto write_track_write_loop; END_SECTION() } From c994fa39f647694b1442ecbeb2dea202311f7785 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 31 Dec 2016 16:18:30 -0500 Subject: [PATCH 2/6] Ensured spin-up doesn't occur if there's no motor line. --- Components/1770/1770.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index e5e2a39a9..4c9294156 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -782,7 +782,7 @@ void WD1770::posit_event(Event new_event_type) status.lost_data = false; }); set_motor_on(true); - if(!(command_ & 0x08)) goto write_track_test_delay; + if(!(command_ & 0x08) || !has_motor_on_line()) goto write_track_test_delay; index_hole_count_ = 0; write_track_test_index_hole_count: From f94f34f0536b8885a7bf2d563e2878ef9fef2412 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Jan 2017 20:39:19 -0500 Subject: [PATCH 3/6] Made an attempt at read track. Which means process_input_bit can't just swallow syncs any more; it now reports them as tokens of type ::Sync. --- Components/1770/1770.cpp | 73 +++++++++++++++++++++++++--------- Components/1770/1770.hpp | 2 +- Storage/Disk/Encodings/MFM.hpp | 3 ++ 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index 4c9294156..c9c9ee0d2 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -165,9 +165,9 @@ void WD1770::process_input_bit(int value, unsigned int cycles_since_index_hole) shift_register_ = (shift_register_ << 1) | value; bits_since_token_++; - Token::Type token_type = Token::Byte; if(data_mode_ == DataMode::Scanning) { + Token::Type token_type = Token::Byte; if(!is_double_density_) { switch(shift_register_ & 0xffff) @@ -175,22 +175,22 @@ void WD1770::process_input_bit(int value, unsigned int cycles_since_index_hole) case Storage::Encodings::MFM::FMIndexAddressMark: token_type = Token::Index; crc_generator_.reset(); - crc_generator_.add(Storage::Encodings::MFM::IndexAddressByte); + crc_generator_.add(latest_token_.byte_value = Storage::Encodings::MFM::IndexAddressByte); break; case Storage::Encodings::MFM::FMIDAddressMark: token_type = Token::ID; crc_generator_.reset(); - crc_generator_.add(Storage::Encodings::MFM::IDAddressByte); + crc_generator_.add(latest_token_.byte_value = Storage::Encodings::MFM::IDAddressByte); break; case Storage::Encodings::MFM::FMDataAddressMark: token_type = Token::Data; crc_generator_.reset(); - crc_generator_.add(Storage::Encodings::MFM::DataAddressByte); + crc_generator_.add(latest_token_.byte_value = Storage::Encodings::MFM::DataAddressByte); break; case Storage::Encodings::MFM::FMDeletedDataAddressMark: token_type = Token::DeletedData; crc_generator_.reset(); - crc_generator_.add(Storage::Encodings::MFM::DeletedDataAddressByte); + crc_generator_.add(latest_token_.byte_value = Storage::Encodings::MFM::DeletedDataAddressByte); break; default: break; @@ -203,12 +203,18 @@ void WD1770::process_input_bit(int value, unsigned int cycles_since_index_hole) case Storage::Encodings::MFM::MFMIndexSync: bits_since_token_ = 0; is_awaiting_marker_value_ = true; - return; + + token_type = Token::Sync; + latest_token_.byte_value = Storage::Encodings::MFM::MFMIndexSyncByteValue; + break; case Storage::Encodings::MFM::MFMSync: bits_since_token_ = 0; is_awaiting_marker_value_ = true; crc_generator_.set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue); - return; + + token_type = Token::Sync; + latest_token_.byte_value = Storage::Encodings::MFM::MFMSyncByteValue; + break; default: break; } @@ -763,6 +769,26 @@ void WD1770::posit_event(Event new_event_type) update_status([] (Status &status) { status.type = Status::Three; }); + if(!has_motor_on_line() && !has_head_load_line()) goto type3_test_delay; + + if(has_motor_on_line()) goto begin_type3_spin_up; + goto begin_type3_load_head; + + begin_type3_load_head: + set_head_load_request(true); + if(head_is_loaded_) goto type3_test_delay; + WAIT_FOR_EVENT(Event::HeadLoad); + goto type3_test_delay; + + begin_type3_spin_up: + if((command_&0x08) || get_motor_on()) goto type3_test_delay; + SPIN_UP(); + + type3_test_delay: + if(!(command_&0x04)) goto test_type3_type; + WAIT_FOR_TIME(30); + + test_type3_type: switch(command_ >> 4) { case 0xa: goto begin_read_address; @@ -774,24 +800,33 @@ void WD1770::posit_event(Event new_event_type) printf("!!!TODO: read address!!!\n"); begin_read_track: - printf("!!!TODO: read track!!!\n"); + WAIT_FOR_EVENT(Event::IndexHole); + index_hole_count_ = 0; + + read_track_read_byte: + WAIT_FOR_EVENT(Event::Token | Event::IndexHole); + if(index_hole_count_) + { + goto wait_for_command; + } + if(status_.data_request) + { + update_status([] (Status &status) { + status.lost_data = true; + }); + goto wait_for_command; + } + data_ = latest_token_.byte_value; + update_status([] (Status &status) { + status.data_request = true; + }); + goto read_track_read_byte; begin_write_track: update_status([] (Status &status) { status.data_request = false; status.lost_data = false; }); - set_motor_on(true); - if(!(command_ & 0x08) || !has_motor_on_line()) goto write_track_test_delay; - - index_hole_count_ = 0; - write_track_test_index_hole_count: - WAIT_FOR_EVENT(Event::IndexHoleTarget); - if(index_hole_count_target_ < 6) goto write_track_test_index_hole_count; - - write_track_test_delay: - if(!(command_&0x04)) goto write_track_test_write_protect; - WAIT_FOR_TIME(30); write_track_test_write_protect: if(get_drive_is_read_only()) diff --git a/Components/1770/1770.hpp b/Components/1770/1770.hpp index e06bcac8b..27170dc51 100644 --- a/Components/1770/1770.hpp +++ b/Components/1770/1770.hpp @@ -104,7 +104,7 @@ class WD1770: public Storage::Disk::Controller { int shift_register_; struct Token { enum Type { - Index, ID, Data, DeletedData, Byte + Index, ID, Data, DeletedData, Sync, Byte } type; uint8_t byte_value; } latest_token_; diff --git a/Storage/Disk/Encodings/MFM.hpp b/Storage/Disk/Encodings/MFM.hpp index ed63c02d4..f482c93bd 100644 --- a/Storage/Disk/Encodings/MFM.hpp +++ b/Storage/Disk/Encodings/MFM.hpp @@ -33,6 +33,9 @@ const uint16_t MFMIndexSync = 0x5224; // data 0xc2, with a missing clock at 0 const uint16_t MFMSync = 0x4489; // data 0xa1, with a missing clock at 0x0020 => 0100 0100 1010 1001 without 0010 0000 const uint16_t MFMPostSyncCRCValue = 0xcdb4; // the value the CRC generator should have after encountering three 0xa1s +const uint8_t MFMIndexSyncByteValue = 0xc2; +const uint8_t MFMSyncByteValue = 0xa1; + struct Sector { uint8_t track, side, sector; std::vector data; From 0df9ce5a764ce55a0565d9ea7cdace0912c77aa6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Jan 2017 20:55:09 -0500 Subject: [PATCH 4/6] Made an attempt at read address. So superficially that leaves only the force interrupts. --- Components/1770/1770.cpp | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index c9c9ee0d2..f12490b59 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -797,7 +797,45 @@ void WD1770::posit_event(Event new_event_type) } begin_read_address: - printf("!!!TODO: read address!!!\n"); + index_hole_count_ = 0; + distance_into_section_ = 0; + + read_address_get_header: + WAIT_FOR_EVENT(Event::IndexHole | Event::Token); + if(new_event_type == Event::Token) + { + if(!distance_into_section_ && latest_token_.type == Token::ID) {data_mode_ = DataMode::Reading; distance_into_section_++; } + else if(distance_into_section_ && distance_into_section_ < 7 && latest_token_.type == Token::Byte) + { + if(status_.data_request) + { + update_status([] (Status &status) { + status.lost_data = true; + }); + goto wait_for_command; + } + header_[distance_into_section_ - 1] = data_ = latest_token_.byte_value; + track_ = header_[0]; + update_status([] (Status &status) { + status.data_request = true; + }); + distance_into_section_++; + + if(distance_into_section_ == 7) + { + goto wait_for_command; + } + } + } + + if(index_hole_count_ == 6) + { + update_status([] (Status &status) { + status.record_not_found = true; + }); + goto wait_for_command; + } + goto read_address_get_header; begin_read_track: WAIT_FOR_EVENT(Event::IndexHole); From 044c920a5b05ed680a5f312a44f0d3f1cedb0972 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Jan 2017 20:56:52 -0500 Subject: [PATCH 5/6] Made it more explicit that there are no unhandled cases. --- Components/1770/1770.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index f12490b59..25582f18f 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -789,12 +789,9 @@ void WD1770::posit_event(Event new_event_type) WAIT_FOR_TIME(30); test_type3_type: - switch(command_ >> 4) - { - case 0xa: goto begin_read_address; - case 0xc: goto begin_read_track; - case 0xf: goto begin_write_track; - } + if(!(command_&0x20)) goto begin_read_address; + if(!(command_&0x10)) goto begin_read_track; + goto begin_write_track; begin_read_address: index_hole_count_ = 0; From eca3995481bb46d686fd5103854a5437d7f259ec Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Jan 2017 21:00:25 -0500 Subject: [PATCH 6/6] Added a CRC check for read address, ensured CRC, lost data and record not found are initially reset. --- Components/1770/1770.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Components/1770/1770.cpp b/Components/1770/1770.cpp index 25582f18f..2ea36cdf4 100644 --- a/Components/1770/1770.cpp +++ b/Components/1770/1770.cpp @@ -768,6 +768,9 @@ void WD1770::posit_event(Event new_event_type) begin_type_3: update_status([] (Status &status) { status.type = Status::Three; + status.crc_error = false; + status.lost_data = false; + status.record_not_found = false; }); if(!has_motor_on_line() && !has_head_load_line()) goto type3_test_delay; @@ -820,6 +823,12 @@ void WD1770::posit_event(Event new_event_type) if(distance_into_section_ == 7) { + if(crc_generator_.get_value()) + { + update_status([] (Status &status) { + status.crc_error = true; + }); + } goto wait_for_command; } }