1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-08-15 14:27:29 +00:00

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.
This commit is contained in:
Thomas Harte
2017-08-15 16:05:10 -04:00
parent 9541a2a5f0
commit 73080d6c36
7 changed files with 77 additions and 31 deletions

View File

@@ -502,7 +502,7 @@ void WD1770::posit_event(int new_event_type) {
} }
set_data_mode(DataMode::Writing); set_data_mode(DataMode::Writing);
begin_writing(); begin_writing(false);
for(int c = 0; c < (get_is_double_density() ? 12 : 6); c++) { for(int c = 0; c < (get_is_double_density() ? 12 : 6); c++) {
write_byte(0); write_byte(0);
} }
@@ -694,7 +694,7 @@ void WD1770::posit_event(int new_event_type) {
} }
WAIT_FOR_EVENT(Event1770::IndexHoleTarget); WAIT_FOR_EVENT(Event1770::IndexHoleTarget);
begin_writing(); begin_writing(true);
index_hole_count_ = 0; index_hole_count_ = 0;
write_track_write_loop: write_track_write_loop:

View File

@@ -193,25 +193,28 @@ void i8272::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
#define CONCAT(x, y) PASTE(x, y) #define CONCAT(x, y) PASTE(x, y)
#define FIND_HEADER() \ #define FIND_HEADER() \
set_data_mode(DataMode::Scanning); \
CONCAT(find_header, __LINE__): WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole); \ 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__); \ else if(get_latest_token().type == Token::ID) goto CONCAT(header_found, __LINE__); \
\ \
if(index_hole_limit_) goto CONCAT(find_header, __LINE__); \ if(index_hole_limit_) goto CONCAT(find_header, __LINE__); \
CONCAT(header_found, __LINE__): 0;\ CONCAT(header_found, __LINE__): 0;\
#define FIND_DATA() \ #define FIND_DATA() \
set_data_mode(DataMode::Scanning); \
CONCAT(find_data, __LINE__): WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole); \ 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 && get_latest_token().type != Token::Data && get_latest_token().type != Token::DeletedData) goto CONCAT(find_data, __LINE__);
#define READ_HEADER() \ #define READ_HEADER() \
distance_into_section_ = 0; \ distance_into_section_ = 0; \
set_data_mode(Reading); \ set_data_mode(DataMode::Reading); \
CONCAT(read_header, __LINE__): WAIT_FOR_EVENT(Event::Token); \ CONCAT(read_header, __LINE__): WAIT_FOR_EVENT(Event::Token); \
header_[distance_into_section_] = get_latest_token().byte_value; \ header_[distance_into_section_] = get_latest_token().byte_value; \
distance_into_section_++; \ distance_into_section_++; \
if(distance_into_section_ < 6) goto CONCAT(read_header, __LINE__); \ 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() \ #define SET_DRIVE_HEAD_MFM() \
active_drive_ = command_[1]&3; \ active_drive_ = command_[1]&3; \
@@ -221,6 +224,14 @@ void i8272::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
set_is_double_density(command_[0] & 0x40); \ set_is_double_density(command_[0] & 0x40); \
invalidate_track(); 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() \ #define LOAD_HEAD() \
if(!drives_[active_drive_].head_is_loaded[active_head_]) { \ if(!drives_[active_drive_].head_is_loaded[active_head_]) { \
drives_[active_drive_].head_is_loaded[active_head_] = true; \ drives_[active_drive_].head_is_loaded[active_head_] = true; \
@@ -241,6 +252,7 @@ void i8272::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
} }
void i8272::posit_event(int event_type) { void i8272::posit_event(int event_type) {
if(event_type == (int)Event::IndexHole) index_hole_count_++;
if(!(interesting_event_mask_ & event_type)) return; if(!(interesting_event_mask_ & event_type)) return;
interesting_event_mask_ &= ~event_type; 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 // 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 // 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. // values in the internal registers.
index_hole_limit_ = 2; index_hole_limit_ = 6;
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_next_sector:
FIND_HEADER(); FIND_HEADER();
@@ -350,12 +361,13 @@ void i8272::posit_event(int event_type) {
SetNoData(); SetNoData();
goto abort_read_write; goto abort_read_write;
} }
printf("Header\n");
READ_HEADER(); READ_HEADER();
if(get_crc_generator().get_value()) { if(get_crc_generator().get_value()) {
// This implies a CRC error in the header; mark as such but continue. // This implies a CRC error in the header; mark as such but continue.
SetDataError(); 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; 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 // Branch to whatever is supposed to happen next
@@ -450,6 +462,10 @@ void i8272::posit_event(int event_type) {
if(!dma_mode_) SetNonDMAExecution(); if(!dma_mode_) SetNonDMAExecution();
SET_DRIVE_HEAD_MFM(); 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()) { if(drives_[active_drive_].drive->get_is_read_only()) {
SetNotWriteable(); SetNotWriteable();
goto abort_read_write; goto abort_read_write;
@@ -459,9 +475,10 @@ void i8272::posit_event(int event_type) {
goto read_write_find_header; goto read_write_find_header;
write_data_found_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(); SetDataDirectionFromProcessor();
SetDataRequest(); SetDataRequest();
@@ -483,6 +500,7 @@ void i8272::posit_event(int event_type) {
goto write_loop; goto write_loop;
} }
printf("Wrote %d bytes\n", distance_into_section_);
write_crc(); write_crc();
expects_input_ = false; expects_input_ = false;
WAIT_FOR_EVENT(Event::DataWritten); WAIT_FOR_EVENT(Event::DataWritten);
@@ -578,7 +596,8 @@ void i8272::posit_event(int event_type) {
// Wait for the index hole. // Wait for the index hole.
WAIT_FOR_EVENT(Event::IndexHole); WAIT_FOR_EVENT(Event::IndexHole);
begin_writing(); index_hole_count_ = 0;
begin_writing(true);
// Write start-of-track. // Write start-of-track.
write_start_of_track(); write_start_of_track();
@@ -595,20 +614,30 @@ void i8272::posit_event(int event_type) {
expects_input_ = true; expects_input_ = true;
distance_into_section_ = 0; distance_into_section_ = 0;
format_track_write_header: format_track_write_header:
WAIT_FOR_EVENT(Event::DataWritten); WAIT_FOR_EVENT((int)Event::DataWritten | (int)Event::IndexHole);
// TODO: overrun? switch(event_type) {
header_[distance_into_section_] = input_; case (int)Event::IndexHole:
write_byte(input_); SetOverrun();
has_input_ = false; end_writing();
distance_into_section_++; goto abort_read_write;
if(distance_into_section_ < 4) { break;
SetDataRequest(); case (int)Event::DataWritten:
goto format_track_write_header; 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_crc();
// Write the sector body. // Write the sector body.
write_id_data_joiner(false); write_id_data_joiner(false, false);
write_n_bytes(128 << command_[2], command_[5]); write_n_bytes(128 << command_[2], command_[5]);
write_crc(); write_crc();
@@ -617,7 +646,7 @@ void i8272::posit_event(int event_type) {
// Consider repeating. // Consider repeating.
sector_++; sector_++;
if(sector_ < command_[3]) if(sector_ < command_[3] && !index_hole_count_)
goto format_track_write_sector; goto format_track_write_sector;
// Otherwise, pad out to the index hole. // Otherwise, pad out to the index hole.

View File

@@ -117,7 +117,7 @@ class i8272: public Storage::Disk::MFMController {
// Transient storage and counters used while reading the disk. // Transient storage and counters used while reading the disk.
uint8_t header_[6]; uint8_t header_[6];
int distance_into_section_; 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. // Keeps track of the drive and head in use during commands.
int active_drive_; int active_drive_;

View File

@@ -121,8 +121,9 @@ Storage::Time Controller::get_time_into_track() {
#pragma mark - Writing #pragma mark - Writing
void Controller::begin_writing() { void Controller::begin_writing(bool clamp_to_index_hole) {
is_reading_ = false; is_reading_ = false;
clamp_writing_to_index_hole_ = clamp_to_index_hole;
write_segment_.length_of_a_bit = bit_length_ / rotational_multiplier_; write_segment_.length_of_a_bit = bit_length_ / rotational_multiplier_;
write_segment_.data.clear(); write_segment_.data.clear();
@@ -148,9 +149,14 @@ void Controller::end_writing() {
patched_track_ = std::dynamic_pointer_cast<PCMPatchedTrack>(track_); patched_track_ = std::dynamic_pointer_cast<PCMPatchedTrack>(track_);
if(!patched_track_) { if(!patched_track_) {
patched_track_.reset(new PCMPatchedTrack(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 invalidate_track(); // TEMPORARY: to force a seek
} }
@@ -205,8 +211,7 @@ bool Controller::get_motor_on() {
} }
void Controller::set_drive(std::shared_ptr<Drive> drive) { void Controller::set_drive(std::shared_ptr<Drive> drive) {
if(drive_ != drive) if(drive_ != drive) {
{
invalidate_track(); invalidate_track();
drive_ = drive; drive_ = drive;
} }

View File

@@ -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. @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 It is acceptable to supply a backlog of bits. Flux transition events will not be reported
while writing. 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. 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 motor_is_on_;
bool is_reading_; bool is_reading_;
bool clamp_writing_to_index_hole_;
std::shared_ptr<PCMPatchedTrack> patched_track_; std::shared_ptr<PCMPatchedTrack> patched_track_;
PCMSegment write_segment_; PCMSegment write_segment_;
Time write_start_time_; Time write_start_time_;

View File

@@ -31,15 +31,18 @@ Track *PCMPatchedTrack::clone() {
return new PCMPatchedTrack(*this); 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<PCMSegmentEventSource> event_source(new PCMSegmentEventSource(segment)); std::shared_ptr<PCMSegmentEventSource> event_source(new PCMSegmentEventSource(segment));
Time zero(0); Time zero(0);
Time one(1);
Time end_time = start_time + event_source->get_length(); 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); 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 // 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); assert(insertion_period.start_time <= one);
while(insertion_period.end_time > one) { while(insertion_period.end_time > one) {
Time next_end_time = insertion_period.end_time - one; Time next_end_time = insertion_period.end_time - one;

View File

@@ -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 Replaces whatever is currently on the track from @c start_position to @c start_position + segment length
with the contents of @c segment. 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 // To satisfy Storage::Disk::Track
Event get_next_event(); Event get_next_event();