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:
parent
9541a2a5f0
commit
73080d6c36
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user