1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 16:31:31 +00:00

Merge pull request #195 from TomHarte/8272Tidying

Brings the 1770 into usage of those parts factored out of it for the 8272
This commit is contained in:
Thomas Harte 2017-08-14 16:34:15 -04:00 committed by GitHub
commit 113da93796
6 changed files with 180 additions and 344 deletions

View File

@ -25,29 +25,16 @@ WD1770::Status::Status() :
busy(false) {} busy(false) {}
WD1770::WD1770(Personality p) : WD1770::WD1770(Personality p) :
Storage::Disk::Controller(8000000, 16, 300), Storage::Disk::MFMController(8000000, 16, 300),
crc_generator_(0x1021, 0xffff), interesting_event_mask_((int)Event1770::Command),
interesting_event_mask_(Event::Command),
resume_point_(0), resume_point_(0),
delay_time_(0), delay_time_(0),
index_hole_count_target_(-1), index_hole_count_target_(-1),
is_awaiting_marker_value_(false),
data_mode_(DataMode::Scanning),
delegate_(nullptr), delegate_(nullptr),
personality_(p), personality_(p),
head_is_loaded_(false) { head_is_loaded_(false) {
set_is_double_density(false); set_is_double_density(false);
posit_event(Event::Command); posit_event((int)Event1770::Command);
}
void WD1770::set_is_double_density(bool is_double_density) {
is_double_density_ = is_double_density;
Storage::Time bit_length;
bit_length.length = 1;
bit_length.clock_rate = is_double_density ? 500000 : 250000;
set_expected_bit_length(bit_length);
if(!is_double_density) is_awaiting_marker_value_ = false;
} }
void WD1770::set_register(int address, uint8_t value) { void WD1770::set_register(int address, uint8_t value) {
@ -60,7 +47,7 @@ void WD1770::set_register(int address, uint8_t value) {
}); });
} else { } else {
command_ = value; command_ = value;
posit_event(Event::Command); posit_event((int)Event1770::Command);
} }
} }
break; break;
@ -131,148 +118,24 @@ void WD1770::run_for(const Cycles cycles) {
unsigned int number_of_cycles = (unsigned int)cycles.as_int(); unsigned int number_of_cycles = (unsigned int)cycles.as_int();
if(delay_time_ <= number_of_cycles) { if(delay_time_ <= number_of_cycles) {
delay_time_ = 0; delay_time_ = 0;
posit_event(Event::Timer); posit_event((int)Event1770::Timer);
} else { } else {
delay_time_ -= number_of_cycles; delay_time_ -= number_of_cycles;
} }
} }
} }
void WD1770::process_input_bit(int value, unsigned int cycles_since_index_hole) { #define WAIT_FOR_EVENT(mask) resume_point_ = __LINE__; interesting_event_mask_ = (int)mask; return; case __LINE__:
if(data_mode_ == DataMode::Writing) return; #define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; delay_time_ = ms * 8000; WAIT_FOR_EVENT(Event1770::Timer);
#define WAIT_FOR_BYTES(count) resume_point_ = __LINE__; distance_into_section_ = 0; WAIT_FOR_EVENT(Event::Token); if(get_latest_token().type == Token::Byte) distance_into_section_++; if(distance_into_section_ < count) { interesting_event_mask_ = (int)Event::Token; return; }
shift_register_ = (shift_register_ << 1) | value;
bits_since_token_++;
if(data_mode_ == DataMode::Scanning) {
Token::Type token_type = Token::Byte;
if(!is_double_density_) {
switch(shift_register_ & 0xffff) {
case Storage::Encodings::MFM::FMIndexAddressMark:
token_type = Token::Index;
crc_generator_.reset();
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(latest_token_.byte_value = Storage::Encodings::MFM::IDAddressByte);
break;
case Storage::Encodings::MFM::FMDataAddressMark:
token_type = Token::Data;
crc_generator_.reset();
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(latest_token_.byte_value = Storage::Encodings::MFM::DeletedDataAddressByte);
break;
default:
break;
}
} else {
switch(shift_register_ & 0xffff) {
case Storage::Encodings::MFM::MFMIndexSync:
bits_since_token_ = 0;
is_awaiting_marker_value_ = true;
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);
token_type = Token::Sync;
latest_token_.byte_value = Storage::Encodings::MFM::MFMSyncByteValue;
break;
default:
break;
}
}
if(token_type != Token::Byte) {
latest_token_.type = token_type;
bits_since_token_ = 0;
posit_event(Event::Token);
return;
}
}
if(bits_since_token_ == 16) {
latest_token_.type = Token::Byte;
latest_token_.byte_value = (uint8_t)(
((shift_register_ & 0x0001) >> 0) |
((shift_register_ & 0x0004) >> 1) |
((shift_register_ & 0x0010) >> 2) |
((shift_register_ & 0x0040) >> 3) |
((shift_register_ & 0x0100) >> 4) |
((shift_register_ & 0x0400) >> 5) |
((shift_register_ & 0x1000) >> 6) |
((shift_register_ & 0x4000) >> 7));
bits_since_token_ = 0;
if(is_awaiting_marker_value_ && is_double_density_) {
is_awaiting_marker_value_ = false;
switch(latest_token_.byte_value) {
case Storage::Encodings::MFM::IndexAddressByte:
latest_token_.type = Token::Index;
break;
case Storage::Encodings::MFM::IDAddressByte:
latest_token_.type = Token::ID;
break;
case Storage::Encodings::MFM::DataAddressByte:
latest_token_.type = Token::Data;
break;
case Storage::Encodings::MFM::DeletedDataAddressByte:
latest_token_.type = Token::DeletedData;
break;
default: break;
}
}
crc_generator_.add(latest_token_.byte_value);
posit_event(Event::Token);
return;
}
}
void WD1770::process_index_hole() {
index_hole_count_++;
posit_event(Event::IndexHole);
if(index_hole_count_target_ == index_hole_count_) {
posit_event(Event::IndexHoleTarget);
index_hole_count_target_ = -1;
}
// motor power-down
if(index_hole_count_ == 9 && !status_.busy && has_motor_on_line()) {
set_motor_on(false);
}
// head unload
if(index_hole_count_ == 15 && !status_.busy && has_head_load_line()) {
set_head_load_request(false);
}
}
void WD1770::process_write_completed() {
posit_event(Event::DataWritten);
}
#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; case __LINE__: if(delay_time_) return;
#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; }
#define BEGIN_SECTION() switch(resume_point_) { default: #define BEGIN_SECTION() switch(resume_point_) { default:
#define END_SECTION() 0; } #define END_SECTION() 0; }
#define READ_ID() \ #define READ_ID() \
if(new_event_type == Event::Token) { \ if(new_event_type == (int)Event::Token) { \
if(!distance_into_section_ && latest_token_.type == Token::ID) {data_mode_ = DataMode::Reading; distance_into_section_++; } \ if(!distance_into_section_ && get_latest_token().type == Token::ID) {set_data_mode(DataMode::Reading); distance_into_section_++; } \
else if(distance_into_section_ && distance_into_section_ < 7 && latest_token_.type == Token::Byte) { \ else if(distance_into_section_ && distance_into_section_ < 7 && get_latest_token().type == Token::Byte) { \
header_[distance_into_section_ - 1] = latest_token_.byte_value; \ header_[distance_into_section_ - 1] = get_latest_token().byte_value; \
distance_into_section_++; \ distance_into_section_++; \
} \ } \
} }
@ -285,7 +148,7 @@ void WD1770::process_write_completed() {
set_motor_on(true); \ set_motor_on(true); \
index_hole_count_ = 0; \ index_hole_count_ = 0; \
index_hole_count_target_ = 6; \ index_hole_count_target_ = 6; \
WAIT_FOR_EVENT(Event::IndexHoleTarget); \ WAIT_FOR_EVENT(Event1770::IndexHoleTarget); \
status_.spin_up = true; status_.spin_up = true;
// +--------+----------+-------------------------+ // +--------+----------+-------------------------+
@ -305,7 +168,25 @@ void WD1770::process_write_completed() {
// ! 4 ! Forc int ! 1 1 0 1 i3 i2 i1 i0 ! // ! 4 ! Forc int ! 1 1 0 1 i3 i2 i1 i0 !
// +--------+----------+-------------------------+ // +--------+----------+-------------------------+
void WD1770::posit_event(Event new_event_type) { void WD1770::posit_event(int new_event_type) {
if(new_event_type == (int)Event::IndexHole) {
index_hole_count_++;
if(index_hole_count_target_ == index_hole_count_) {
posit_event((int)Event1770::IndexHoleTarget);
index_hole_count_target_ = -1;
}
// motor power-down
if(index_hole_count_ == 9 && !status_.busy && has_motor_on_line()) {
set_motor_on(false);
}
// head unload
if(index_hole_count_ == 15 && !status_.busy && has_head_load_line()) {
set_head_load_request(false);
}
}
if(!(interesting_event_mask_ & (int)new_event_type)) return; if(!(interesting_event_mask_ & (int)new_event_type)) return;
interesting_event_mask_ &= ~new_event_type; interesting_event_mask_ &= ~new_event_type;
@ -315,7 +196,7 @@ void WD1770::posit_event(Event new_event_type) {
// Wait for a new command, branch to the appropriate handler. // Wait for a new command, branch to the appropriate handler.
wait_for_command: wait_for_command:
printf("Idle...\n"); printf("Idle...\n");
data_mode_ = DataMode::Scanning; set_data_mode(DataMode::Scanning);
index_hole_count_ = 0; index_hole_count_ = 0;
update_status([] (Status &status) { update_status([] (Status &status) {
@ -323,7 +204,7 @@ void WD1770::posit_event(Event new_event_type) {
status.interrupt_request = true; status.interrupt_request = true;
}); });
WAIT_FOR_EVENT(Event::Command); WAIT_FOR_EVENT(Event1770::Command);
update_status([] (Status &status) { update_status([] (Status &status) {
status.busy = true; status.busy = true;
@ -372,7 +253,7 @@ void WD1770::posit_event(Event new_event_type) {
} }
set_head_load_request(true); set_head_load_request(true);
if(head_is_loaded_) goto test_type1_type; if(head_is_loaded_) goto test_type1_type;
WAIT_FOR_EVENT(Event::HeadLoad); WAIT_FOR_EVENT(Event1770::HeadLoad);
goto test_type1_type; goto test_type1_type;
begin_type1_spin_up: begin_type1_spin_up:
@ -429,7 +310,7 @@ void WD1770::posit_event(Event new_event_type) {
distance_into_section_ = 0; distance_into_section_ = 0;
verify_read_data: verify_read_data:
WAIT_FOR_EVENT(Event::IndexHole | Event::Token); WAIT_FOR_EVENT((int)Event::IndexHole | (int)Event::Token);
READ_ID(); READ_ID();
if(index_hole_count_ == 6) { if(index_hole_count_ == 6) {
@ -439,8 +320,8 @@ void WD1770::posit_event(Event new_event_type) {
goto wait_for_command; goto wait_for_command;
} }
if(distance_into_section_ == 7) { if(distance_into_section_ == 7) {
data_mode_ = DataMode::Scanning; set_data_mode(DataMode::Scanning);
if(crc_generator_.get_value()) { if(get_crc_generator().get_value()) {
update_status([] (Status &status) { update_status([] (Status &status) {
status.crc_error = true; status.crc_error = true;
}); });
@ -491,7 +372,7 @@ void WD1770::posit_event(Event new_event_type) {
begin_type2_load_head: begin_type2_load_head:
set_head_load_request(true); set_head_load_request(true);
if(head_is_loaded_) goto test_type2_delay; if(head_is_loaded_) goto test_type2_delay;
WAIT_FOR_EVENT(Event::HeadLoad); WAIT_FOR_EVENT(Event1770::HeadLoad);
goto test_type2_delay; goto test_type2_delay;
begin_type2_spin_up: begin_type2_spin_up:
@ -513,7 +394,7 @@ void WD1770::posit_event(Event new_event_type) {
} }
type2_get_header: type2_get_header:
WAIT_FOR_EVENT(Event::IndexHole | Event::Token); WAIT_FOR_EVENT((int)Event::IndexHole | (int)Event::Token);
READ_ID(); READ_ID();
if(index_hole_count_ == 5) { if(index_hole_count_ == 5) {
@ -525,11 +406,11 @@ void WD1770::posit_event(Event new_event_type) {
} }
if(distance_into_section_ == 7) { if(distance_into_section_ == 7) {
printf("Considering %d/%d\n", header_[0], header_[2]); printf("Considering %d/%d\n", header_[0], header_[2]);
data_mode_ = DataMode::Scanning; set_data_mode(DataMode::Scanning);
if( header_[0] == track_ && header_[2] == sector_ && if( header_[0] == track_ && header_[2] == sector_ &&
(has_motor_on_line() || !(command_&0x02) || ((command_&0x08) >> 3) == header_[1])) { (has_motor_on_line() || !(command_&0x02) || ((command_&0x08) >> 3) == header_[1])) {
printf("Found %d/%d\n", header_[0], header_[2]); printf("Found %d/%d\n", header_[0], header_[2]);
if(crc_generator_.get_value()) { if(get_crc_generator().get_value()) {
printf("CRC error; back to searching\n"); printf("CRC error; back to searching\n");
update_status([] (Status &status) { update_status([] (Status &status) {
status.crc_error = true; status.crc_error = true;
@ -554,20 +435,20 @@ void WD1770::posit_event(Event new_event_type) {
type2_read_data: type2_read_data:
WAIT_FOR_EVENT(Event::Token); WAIT_FOR_EVENT(Event::Token);
// TODO: timeout // TODO: timeout
if(latest_token_.type == Token::Data || latest_token_.type == Token::DeletedData) { if(get_latest_token().type == Token::Data || get_latest_token().type == Token::DeletedData) {
update_status([this] (Status &status) { update_status([this] (Status &status) {
status.record_type = (latest_token_.type == Token::DeletedData); status.record_type = (get_latest_token().type == Token::DeletedData);
}); });
distance_into_section_ = 0; distance_into_section_ = 0;
data_mode_ = DataMode::Reading; set_data_mode(DataMode::Reading);
goto type2_read_byte; goto type2_read_byte;
} }
goto type2_read_data; goto type2_read_data;
type2_read_byte: type2_read_byte:
WAIT_FOR_EVENT(Event::Token); WAIT_FOR_EVENT(Event::Token);
if(latest_token_.type != Token::Byte) goto type2_read_byte; if(get_latest_token().type != Token::Byte) goto type2_read_byte;
data_ = latest_token_.byte_value; data_ = get_latest_token().byte_value;
update_status([] (Status &status) { update_status([] (Status &status) {
status.lost_data |= status.data_request; status.lost_data |= status.data_request;
status.data_request = true; status.data_request = true;
@ -581,11 +462,11 @@ void WD1770::posit_event(Event new_event_type) {
type2_check_crc: type2_check_crc:
WAIT_FOR_EVENT(Event::Token); WAIT_FOR_EVENT(Event::Token);
if(latest_token_.type != Token::Byte) goto type2_read_byte; if(get_latest_token().type != Token::Byte) goto type2_read_byte;
header_[distance_into_section_] = latest_token_.byte_value; header_[distance_into_section_] = get_latest_token().byte_value;
distance_into_section_++; distance_into_section_++;
if(distance_into_section_ == 2) { if(distance_into_section_ == 2) {
if(crc_generator_.get_value()) { if(get_crc_generator().get_value()) {
printf("CRC error; terminating\n"); printf("CRC error; terminating\n");
update_status([this] (Status &status) { update_status([this] (Status &status) {
status.crc_error = true; status.crc_error = true;
@ -616,24 +497,24 @@ void WD1770::posit_event(Event new_event_type) {
goto wait_for_command; goto wait_for_command;
} }
WAIT_FOR_BYTES(1); WAIT_FOR_BYTES(1);
if(is_double_density_) { if(get_is_double_density()) {
WAIT_FOR_BYTES(11); WAIT_FOR_BYTES(11);
} }
data_mode_ = DataMode::Writing; set_data_mode(DataMode::Writing);
begin_writing(); begin_writing();
for(int c = 0; c < (is_double_density_ ? 12 : 6); c++) { for(int c = 0; c < (get_is_double_density() ? 12 : 6); c++) {
write_byte(0); write_byte(0);
} }
WAIT_FOR_EVENT(Event::DataWritten); WAIT_FOR_EVENT(Event::DataWritten);
if(is_double_density_) { if(get_is_double_density()) {
crc_generator_.set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue); get_crc_generator().set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue);
for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMSync); for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMSync);
write_byte((command_&0x01) ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte); write_byte((command_&0x01) ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
} else { } else {
crc_generator_.reset(); get_crc_generator().reset();
crc_generator_.add((command_&0x01) ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte); get_crc_generator().add((command_&0x01) ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
write_raw_short((command_&0x01) ? Storage::Encodings::MFM::FMDeletedDataAddressMark : Storage::Encodings::MFM::FMDataAddressMark); write_raw_short((command_&0x01) ? Storage::Encodings::MFM::FMDeletedDataAddressMark : Storage::Encodings::MFM::FMDataAddressMark);
} }
@ -669,11 +550,8 @@ void WD1770::posit_event(Event new_event_type) {
goto type2_write_loop; goto type2_write_loop;
type2_write_crc: { type2_write_crc:
uint16_t crc = crc_generator_.get_value(); write_crc();
write_byte(crc >> 8);
write_byte(crc & 0xff);
}
write_byte(0xff); write_byte(0xff);
WAIT_FOR_EVENT(Event::DataWritten); WAIT_FOR_EVENT(Event::DataWritten);
end_writing(); end_writing();
@ -712,7 +590,7 @@ void WD1770::posit_event(Event new_event_type) {
begin_type3_load_head: begin_type3_load_head:
set_head_load_request(true); set_head_load_request(true);
if(head_is_loaded_) goto type3_test_delay; if(head_is_loaded_) goto type3_test_delay;
WAIT_FOR_EVENT(Event::HeadLoad); WAIT_FOR_EVENT(Event1770::HeadLoad);
goto type3_test_delay; goto type3_test_delay;
begin_type3_spin_up: begin_type3_spin_up:
@ -733,17 +611,17 @@ void WD1770::posit_event(Event new_event_type) {
distance_into_section_ = 0; distance_into_section_ = 0;
read_address_get_header: read_address_get_header:
WAIT_FOR_EVENT(Event::IndexHole | Event::Token); WAIT_FOR_EVENT((int)Event::IndexHole | (int)Event::Token);
if(new_event_type == Event::Token) { if(new_event_type == (int)Event::Token) {
if(!distance_into_section_ && latest_token_.type == Token::ID) {data_mode_ = DataMode::Reading; distance_into_section_++; } if(!distance_into_section_ && get_latest_token().type == Token::ID) {set_data_mode(DataMode::Reading); distance_into_section_++; }
else if(distance_into_section_ && distance_into_section_ < 7 && latest_token_.type == Token::Byte) { else if(distance_into_section_ && distance_into_section_ < 7 && get_latest_token().type == Token::Byte) {
if(status_.data_request) { if(status_.data_request) {
update_status([] (Status &status) { update_status([] (Status &status) {
status.lost_data = true; status.lost_data = true;
}); });
goto wait_for_command; goto wait_for_command;
} }
header_[distance_into_section_ - 1] = data_ = latest_token_.byte_value; header_[distance_into_section_ - 1] = data_ = get_latest_token().byte_value;
track_ = header_[0]; track_ = header_[0];
update_status([] (Status &status) { update_status([] (Status &status) {
status.data_request = true; status.data_request = true;
@ -751,7 +629,7 @@ void WD1770::posit_event(Event new_event_type) {
distance_into_section_++; distance_into_section_++;
if(distance_into_section_ == 7) { if(distance_into_section_ == 7) {
if(crc_generator_.get_value()) { if(get_crc_generator().get_value()) {
update_status([] (Status &status) { update_status([] (Status &status) {
status.crc_error = true; status.crc_error = true;
}); });
@ -774,7 +652,7 @@ void WD1770::posit_event(Event new_event_type) {
index_hole_count_ = 0; index_hole_count_ = 0;
read_track_read_byte: read_track_read_byte:
WAIT_FOR_EVENT(Event::Token | Event::IndexHole); WAIT_FOR_EVENT((int)Event::Token | (int)Event::IndexHole);
if(index_hole_count_) { if(index_hole_count_) {
goto wait_for_command; goto wait_for_command;
} }
@ -784,7 +662,7 @@ void WD1770::posit_event(Event new_event_type) {
}); });
goto wait_for_command; goto wait_for_command;
} }
data_ = latest_token_.byte_value; data_ = get_latest_token().byte_value;
update_status([] (Status &status) { update_status([] (Status &status) {
status.data_request = true; status.data_request = true;
}); });
@ -815,25 +693,23 @@ void WD1770::posit_event(Event new_event_type) {
goto wait_for_command; goto wait_for_command;
} }
WAIT_FOR_EVENT(Event::IndexHoleTarget); WAIT_FOR_EVENT(Event1770::IndexHoleTarget);
begin_writing(); begin_writing();
index_hole_count_ = 0; index_hole_count_ = 0;
write_track_write_loop: write_track_write_loop:
if(is_double_density_) { if(get_is_double_density()) {
switch(data_) { switch(data_) {
case 0xf5: case 0xf5:
write_raw_short(Storage::Encodings::MFM::MFMSync); write_raw_short(Storage::Encodings::MFM::MFMSync);
crc_generator_.set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue); get_crc_generator().set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue);
break; break;
case 0xf6: case 0xf6:
write_raw_short(Storage::Encodings::MFM::MFMIndexSync); write_raw_short(Storage::Encodings::MFM::MFMIndexSync);
break; break;
case 0xff: { case 0xff:
uint16_t crc = crc_generator_.get_value(); write_crc();
write_byte(crc >> 8); break;
write_byte(crc & 0xff);
} break;
default: default:
write_byte(data_); write_byte(data_);
break; break;
@ -856,17 +732,15 @@ void WD1770::posit_event(Event new_event_type) {
(data_ & 0x01) (data_ & 0x01)
) )
); );
crc_generator_.reset(); get_crc_generator().reset();
crc_generator_.add(data_); get_crc_generator().add(data_);
break; break;
case 0xfc: case 0xfc:
write_raw_short(Storage::Encodings::MFM::FMIndexAddressMark); write_raw_short(Storage::Encodings::MFM::FMIndexAddressMark);
break; break;
case 0xf7: { case 0xf7:
uint16_t crc = crc_generator_.get_value(); write_crc();
write_byte(crc >> 8); break;
write_byte(crc & 0xff);
} break;
default: default:
write_byte(data_); write_byte(data_);
break; break;
@ -910,27 +784,5 @@ void WD1770::set_head_load_request(bool head_load) {}
void WD1770::set_head_loaded(bool head_loaded) { void WD1770::set_head_loaded(bool head_loaded) {
head_is_loaded_ = head_loaded; head_is_loaded_ = head_loaded;
if(head_loaded) posit_event(Event::HeadLoad); if(head_loaded) posit_event((int)Event1770::HeadLoad);
}
void WD1770::write_bit(int bit) {
if(is_double_density_) {
Controller::write_bit(!bit && !last_bit_);
Controller::write_bit(!!bit);
last_bit_ = bit;
} else {
Controller::write_bit(true);
Controller::write_bit(!!bit);
}
}
void WD1770::write_byte(uint8_t byte) {
for(int c = 0; c < 8; c++) write_bit((byte << c)&0x80);
crc_generator_.add(byte);
}
void WD1770::write_raw_short(uint16_t value) {
for(int c = 0; c < 16; c++) {
Controller::write_bit(!!((value << c)&0x8000));
}
} }

View File

@ -9,8 +9,7 @@
#ifndef _770_hpp #ifndef _770_hpp
#define _770_hpp #define _770_hpp
#include "../../Storage/Disk/DiskController.hpp" #include "../../Storage/Disk/MFMDiskController.hpp"
#include "../../NumberTheory/CRC.hpp"
namespace WD { namespace WD {
@ -18,7 +17,7 @@ namespace WD {
Provides an emulation of various Western Digital drive controllers, including the Provides an emulation of various Western Digital drive controllers, including the
WD1770, WD1772, FDC1773 and FDC1793. WD1770, WD1772, FDC1773 and FDC1793.
*/ */
class WD1770: public Storage::Disk::Controller { class WD1770: public Storage::Disk::MFMController {
public: public:
enum Personality { enum Personality {
P1770, // implies automatic motor-on management, with Type 2 commands offering a spin-up disable P1770, // implies automatic motor-on management, with Type 2 commands offering a spin-up disable
@ -34,7 +33,7 @@ class WD1770: public Storage::Disk::Controller {
WD1770(Personality p); WD1770(Personality p);
/// Sets the value of the double-density input; when @c is_double_density is @c true, reads and writes double-density format data. /// Sets the value of the double-density input; when @c is_double_density is @c true, reads and writes double-density format data.
void set_is_double_density(bool is_double_density); using Storage::Disk::MFMController::set_is_double_density;
/// Writes @c value to the register at @c address. Only the low two bits of the address are decoded. /// Writes @c value to the register at @c address. Only the low two bits of the address are decoded.
void set_register(int address, uint8_t value); void set_register(int address, uint8_t value);
@ -107,66 +106,31 @@ class WD1770: public Storage::Disk::Controller {
int index_hole_count_; int index_hole_count_;
int index_hole_count_target_; int index_hole_count_target_;
int bits_since_token_;
int distance_into_section_; int distance_into_section_;
bool is_awaiting_marker_value_;
int step_direction_; int step_direction_;
void update_status(std::function<void(Status &)> updater); void update_status(std::function<void(Status &)> updater);
// Tokeniser
enum DataMode {
Scanning,
Reading,
Writing
} data_mode_;
bool is_double_density_;
int shift_register_;
struct Token {
enum Type {
Index, ID, Data, DeletedData, Sync, Byte
} type;
uint8_t byte_value;
} latest_token_;
// Events // Events
enum Event: int { enum Event1770: int {
Command = (1 << 0), // Indicates receipt of a new command. Command = (1 << 3), // Indicates receipt of a new command.
Token = (1 << 1), // Indicates recognition of a new token in the flux stream. Interrogate latest_token_ for details. HeadLoad = (1 << 4), // Indicates the head has been loaded (1973 only).
IndexHole = (1 << 2), // Indicates the passing of a physical index hole.
HeadLoad = (1 << 3), // Indicates the head has been loaded (1973 only).
DataWritten = (1 << 4), // Indicates that all queued bits have been written
Timer = (1 << 5), // Indicates that the delay_time_-powered timer has timed out. Timer = (1 << 5), // Indicates that the delay_time_-powered timer has timed out.
IndexHoleTarget = (1 << 6) // Indicates that index_hole_count_ has reached index_hole_count_target_. IndexHoleTarget = (1 << 6) // Indicates that index_hole_count_ has reached index_hole_count_target_.
}; };
void posit_event(Event type); void posit_event(int type);
int interesting_event_mask_; int interesting_event_mask_;
int resume_point_; int resume_point_;
unsigned int delay_time_; unsigned int delay_time_;
// Output
int last_bit_;
void write_bit(int bit);
void write_byte(uint8_t byte);
void write_raw_short(uint16_t value);
// ID buffer // ID buffer
uint8_t header_[6]; uint8_t header_[6];
// CRC generator
NumberTheory::CRC16 crc_generator_;
// 1793 head-loading logic // 1793 head-loading logic
bool head_is_loaded_; bool head_is_loaded_;
// delegate // delegate
Delegate *delegate_; Delegate *delegate_;
// Storage::Disk::Controller
virtual void process_input_bit(int value, unsigned int cycles_since_index_hole);
virtual void process_index_hole();
virtual void process_write_completed();
}; };
} }

View File

@ -427,39 +427,11 @@ void i8272::posit_event(int event_type) {
write_data_found_header: write_data_found_header:
begin_writing(); begin_writing();
if(get_is_double_density()) { write_id_data_joiner((command_[0] & 0x1f) == 0x09);
for(int c = 0; c < 50; c++) {
write_byte(0x4e);
}
for(int c = 0; c < 12; c++) {
write_byte(0x00);
}
} else {
for(int c = 0; c < 11; c++) {
write_byte(0xff);
}
for(int c = 0; c < 6; c++) {
write_byte(0x00);
}
}
WAIT_FOR_EVENT(Event::DataWritten);
{
bool is_deleted = (command_[0] & 0x1f) == 0x09;
if(get_is_double_density()) {
get_crc_generator().set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue);
for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMSync);
write_byte(is_deleted ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
} else {
get_crc_generator().reset();
get_crc_generator().add(is_deleted ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
write_raw_short(is_deleted ? Storage::Encodings::MFM::FMDeletedDataAddressMark : Storage::Encodings::MFM::FMDataAddressMark);
}
}
SetDataDirectionFromProcessor(); SetDataDirectionFromProcessor();
SetDataRequest(); SetDataRequest();
WAIT_FOR_EVENT(Event::DataWritten);
expects_input_ = true; expects_input_ = true;
distance_into_section_ = 0; distance_into_section_ = 0;
@ -571,32 +543,12 @@ void i8272::posit_event(int event_type) {
begin_writing(); begin_writing();
// Write start-of-track. // Write start-of-track.
if(get_is_double_density()) { write_start_of_track();
for(int c = 0; c < 80; c++) write_byte(0x4e);
for(int c = 0; c < 12; c++) write_byte(0x00);
for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMIndexSync);
write_byte(Storage::Encodings::MFM::IndexAddressByte);
for(int c = 0; c < 50; c++) write_byte(0x4e);
} else {
for(int c = 0; c < 40; c++) write_byte(0xff);
for(int c = 0; c < 6; c++) write_byte(0x00);
write_raw_short(Storage::Encodings::MFM::FMIndexAddressMark);
for(int c = 0; c < 26; c++) write_byte(0xff);
}
WAIT_FOR_EVENT(Event::DataWritten); WAIT_FOR_EVENT(Event::DataWritten);
sector_ = 0; sector_ = 0;
format_track_write_sector: format_track_write_sector:
if(get_is_double_density()) { write_id_joiner();
for(int c = 0; c < 12; c++) write_byte(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(Storage::Encodings::MFM::IDAddressByte);
} else {
for(int c = 0; c < 6; c++) write_byte(0x00);
get_crc_generator().reset();
write_raw_short(Storage::Encodings::MFM::FMIDAddressMark);
}
// Write the sector header, obtaining its contents // Write the sector header, obtaining its contents
// from the processor. // from the processor.
@ -618,29 +570,12 @@ void i8272::posit_event(int event_type) {
write_crc(); write_crc();
// Write the sector body. // Write the sector body.
if(get_is_double_density()) { write_id_data_joiner(false);
for(int c = 0; c < 22; c++) write_byte(0x4e); write_n_bytes(128 << command_[2], command_[5]);
for(int c = 0; c < 12; c++) write_byte(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(Storage::Encodings::MFM::DataAddressByte);
} else {
for(int c = 0; c < 11; c++) write_byte(0xff);
for(int c = 0; c < 6; c++) write_byte(0x00);
get_crc_generator().reset();
write_raw_short(Storage::Encodings::MFM::FMDataAddressMark);
}
for(int c = 0; c < (128 << command_[2]); c++) {
write_byte(command_[5]);
}
write_crc(); write_crc();
// Write the prescribed gap. // Write the prescribed gap.
if(get_is_double_density()) { write_n_bytes(command_[4], get_is_double_density() ? 0x4e : 0xff);
for(int c = 0; c < command_[4]; c++) write_byte(0x4e);
} else {
for(int c = 0; c < command_[4]; c++) write_byte(0xff);
}
// Consider repeating. // Consider repeating.
sector_++; sector_++;

View File

@ -10,7 +10,6 @@
#define i8272_hpp #define i8272_hpp
#include "../../Storage/Disk/MFMDiskController.hpp" #include "../../Storage/Disk/MFMDiskController.hpp"
#include "../../Storage/Disk/Drive.hpp"
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>

View File

@ -181,3 +181,59 @@ void MFMController::write_crc() {
write_byte(crc >> 8); write_byte(crc >> 8);
write_byte(crc & 0xff); write_byte(crc & 0xff);
} }
void MFMController::write_n_bytes(int quantity, uint8_t value) {
while(quantity--) write_byte(value);
}
void MFMController::write_id_joiner() {
if(get_is_double_density()) {
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(Storage::Encodings::MFM::IDAddressByte);
} else {
write_n_bytes(6, 0x00);
get_crc_generator().reset();
write_raw_short(Storage::Encodings::MFM::FMIDAddressMark);
}
}
void MFMController::write_id_data_joiner(bool is_deleted) {
if(get_is_double_density()) {
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);
write_n_bytes(6, 0x00);
get_crc_generator().reset();
get_crc_generator().add(is_deleted ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
write_raw_short(is_deleted ? Storage::Encodings::MFM::FMDeletedDataAddressMark : Storage::Encodings::MFM::FMDataAddressMark);
}
}
void MFMController::write_post_data_gap() {
if(get_is_double_density()) {
write_n_bytes(54, 0x4e);
} else {
write_n_bytes(27, 0xff);
}
}
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);
write_byte(Storage::Encodings::MFM::IndexAddressByte);
write_n_bytes(50, 0x4e);
} else {
write_n_bytes(40, 0xff);
write_n_bytes(6, 0x00);
write_raw_short(Storage::Encodings::MFM::FMIndexAddressMark);
write_n_bytes(26, 0xff);
}
}

View File

@ -113,6 +113,36 @@ class MFMController: public Controller {
*/ */
void write_crc(); void write_crc();
/*!
Calls @c write_byte with @c value, @c quantity times.
*/
void write_n_bytes(int quantity, uint8_t value);
/*!
Writes everything that should, per the spec, appear prior to the address contained
in an ID mark proper gaps and the ID mark and appropriate seeds the CRC generator.
*/
void write_id_joiner();
/*!
Writes everything that 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.
*/
void write_id_data_joiner(bool is_deleted);
/*!
Writes the gap expected after a sector's data CRC and before the beginning of the
next ID joiner.
*/
void write_post_data_gap();
/*!
Writes everything that should, per the spec, following the index hole and prior
to any sectors.
*/
void write_start_of_track();
private: private:
// Storage::Disk::Controller // Storage::Disk::Controller
virtual void process_input_bit(int value, unsigned int cycles_since_index_hole); virtual void process_input_bit(int value, unsigned int cycles_since_index_hole);