1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 08:49:37 +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);
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:

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 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<Storage::Disk::Disk> 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<Storage::Disk::Disk> 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.

View File

@ -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_;

View File

@ -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<PCMPatchedTrack>(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> drive) {
if(drive_ != drive)
{
if(drive_ != drive) {
invalidate_track();
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.
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<PCMPatchedTrack> patched_track_;
PCMSegment write_segment_;
Time write_start_time_;

View File

@ -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<PCMSegmentEventSource> 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;

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
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();