2016-12-01 12:41:52 +00:00
|
|
|
//
|
2016-09-17 22:01:00 +00:00
|
|
|
// 1770.cpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 17/09/2016.
|
2018-05-13 19:19:52 +00:00
|
|
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
2016-09-17 22:01:00 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
#include "1770.hpp"
|
2018-06-19 02:37:19 +00:00
|
|
|
|
2017-09-25 01:40:43 +00:00
|
|
|
#include "../../Storage/Disk/Encodings/MFM/Constants.hpp"
|
2018-06-19 02:37:19 +00:00
|
|
|
#include "../../Outputs/Log.hpp"
|
2016-09-20 02:06:56 +00:00
|
|
|
|
2024-01-19 15:36:09 +00:00
|
|
|
namespace {
|
2024-01-19 19:16:13 +00:00
|
|
|
Log::Logger<Log::Source::WDFDC> logger;
|
2024-01-19 15:36:09 +00:00
|
|
|
}
|
|
|
|
|
2016-09-20 02:06:56 +00:00
|
|
|
using namespace WD;
|
|
|
|
|
2016-11-26 15:29:30 +00:00
|
|
|
WD1770::WD1770(Personality p) :
|
2017-09-11 02:56:05 +00:00
|
|
|
Storage::Disk::MFMController(8000000),
|
2017-03-26 18:34:47 +00:00
|
|
|
personality_(p),
|
2019-12-23 01:59:20 +00:00
|
|
|
interesting_event_mask_(int(Event1770::Command)) {
|
2016-09-23 01:28:23 +00:00
|
|
|
set_is_double_density(false);
|
2019-12-23 01:59:20 +00:00
|
|
|
posit_event(int(Event1770::Command));
|
2016-09-20 02:06:56 +00:00
|
|
|
}
|
|
|
|
|
2020-01-05 18:40:02 +00:00
|
|
|
void WD1770::write(int address, uint8_t value) {
|
2017-03-26 18:34:47 +00:00
|
|
|
switch(address&3) {
|
|
|
|
case 0: {
|
|
|
|
if((value&0xf0) == 0xd0) {
|
2018-01-08 00:42:38 +00:00
|
|
|
if(value == 0xd0) {
|
|
|
|
// Force interrupt **immediately**.
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Force interrupt immediately");
|
2019-12-23 01:59:20 +00:00
|
|
|
posit_event(int(Event1770::ForceInterrupt));
|
2018-01-08 00:42:38 +00:00
|
|
|
} else {
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.error().append("TODO: force interrupt");
|
2018-01-08 00:42:38 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.type = Status::One;
|
|
|
|
});
|
|
|
|
}
|
2017-03-26 18:34:47 +00:00
|
|
|
} else {
|
2016-11-25 12:51:39 +00:00
|
|
|
command_ = value;
|
2019-12-23 01:59:20 +00:00
|
|
|
posit_event(int(Event1770::Command));
|
2016-11-25 12:51:39 +00:00
|
|
|
}
|
|
|
|
}
|
2016-09-20 19:56:31 +00:00
|
|
|
break;
|
|
|
|
case 1: track_ = value; break;
|
|
|
|
case 2: sector_ = value; break;
|
2016-12-25 17:31:38 +00:00
|
|
|
case 3:
|
|
|
|
data_ = value;
|
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.data_request = false;
|
|
|
|
});
|
|
|
|
break;
|
2016-09-20 19:56:31 +00:00
|
|
|
}
|
2016-09-20 02:06:56 +00:00
|
|
|
}
|
|
|
|
|
2020-01-05 18:40:02 +00:00
|
|
|
uint8_t WD1770::read(int address) {
|
2017-03-26 18:34:47 +00:00
|
|
|
switch(address&3) {
|
|
|
|
default: {
|
2016-12-07 02:16:29 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.interrupt_request = false;
|
|
|
|
});
|
2016-12-01 03:26:02 +00:00
|
|
|
uint8_t status =
|
2019-12-25 00:57:12 +00:00
|
|
|
(status_.crc_error ? Flag::CRCError : 0) |
|
|
|
|
(status_.busy ? Flag::Busy : 0);
|
2019-12-25 02:43:20 +00:00
|
|
|
|
|
|
|
// Per Jean Louis-Guérin's documentation:
|
|
|
|
//
|
2023-05-12 18:14:45 +00:00
|
|
|
// * the write-protect bit is locked into place by a type 2 or type 3 command, but is
|
2019-12-25 02:43:20 +00:00
|
|
|
// read live after a type 1.
|
|
|
|
// * the track 0 bit is captured during a type 1 instruction and lost upon any other type,
|
|
|
|
// it is not live sampled.
|
2017-03-26 18:34:47 +00:00
|
|
|
switch(status_.type) {
|
2016-12-01 03:26:02 +00:00
|
|
|
case Status::One:
|
|
|
|
status |=
|
2019-12-25 02:43:20 +00:00
|
|
|
(status_.track_zero ? Flag::TrackZero : 0) |
|
2019-12-25 00:57:12 +00:00
|
|
|
(status_.seek_error ? Flag::SeekError : 0) |
|
2019-12-25 01:53:37 +00:00
|
|
|
(get_drive().get_is_read_only() ? Flag::WriteProtect : 0) |
|
|
|
|
(get_drive().get_index_pulse() ? Flag::Index : 0);
|
2016-12-01 03:26:02 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Status::Two:
|
|
|
|
case Status::Three:
|
|
|
|
status |=
|
2019-12-25 00:57:12 +00:00
|
|
|
(status_.write_protect ? Flag::WriteProtect : 0) |
|
2016-12-01 03:26:02 +00:00
|
|
|
(status_.record_type ? Flag::RecordType : 0) |
|
|
|
|
(status_.lost_data ? Flag::LostData : 0) |
|
2016-12-01 12:41:52 +00:00
|
|
|
(status_.data_request ? Flag::DataRequest : 0) |
|
2016-12-01 03:26:02 +00:00
|
|
|
(status_.record_not_found ? Flag::RecordNotFound : 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
if(!has_motor_on_line()) {
|
2017-09-10 23:23:23 +00:00
|
|
|
status |= get_drive().get_is_ready() ? 0 : Flag::NotReady;
|
2016-12-02 02:13:16 +00:00
|
|
|
if(status_.type == Status::One)
|
|
|
|
status |= (head_is_loaded_ ? Flag::HeadLoaded : 0);
|
2017-03-26 18:34:47 +00:00
|
|
|
} else {
|
2017-09-10 23:23:23 +00:00
|
|
|
status |= (get_drive().get_motor_on() ? Flag::MotorOn : 0);
|
2016-12-01 03:26:02 +00:00
|
|
|
if(status_.type == Status::One)
|
|
|
|
status |= (status_.spin_up ? Flag::SpinUp : 0);
|
|
|
|
}
|
2024-01-19 16:45:29 +00:00
|
|
|
// logger.info().append("Returned status %02x of type %d", status, 1+int(status_.type));
|
2016-12-01 03:26:02 +00:00
|
|
|
return status;
|
|
|
|
}
|
2019-12-24 02:31:46 +00:00
|
|
|
case 1:
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Returned track %d", track_);
|
2019-12-24 02:31:46 +00:00
|
|
|
return track_;
|
|
|
|
case 2:
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Returned sector %d", sector_);
|
2019-12-24 02:31:46 +00:00
|
|
|
return sector_;
|
2016-12-01 12:41:52 +00:00
|
|
|
case 3:
|
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.data_request = false;
|
|
|
|
});
|
|
|
|
return data_;
|
2016-09-20 19:56:31 +00:00
|
|
|
}
|
2016-09-20 02:06:56 +00:00
|
|
|
}
|
|
|
|
|
2017-07-28 02:05:29 +00:00
|
|
|
void WD1770::run_for(const Cycles cycles) {
|
2017-07-25 01:19:05 +00:00
|
|
|
Storage::Disk::Controller::run_for(cycles);
|
2016-09-25 00:12:45 +00:00
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
if(delay_time_) {
|
2019-10-30 02:36:29 +00:00
|
|
|
const auto number_of_cycles = cycles.as_integral();
|
2017-03-26 18:34:47 +00:00
|
|
|
if(delay_time_ <= number_of_cycles) {
|
2016-09-25 00:12:45 +00:00
|
|
|
delay_time_ = 0;
|
2019-12-23 01:59:20 +00:00
|
|
|
posit_event(int(Event1770::Timer));
|
2017-03-26 18:34:47 +00:00
|
|
|
} else {
|
2016-09-25 00:12:45 +00:00
|
|
|
delay_time_ -= number_of_cycles;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-23 01:59:20 +00:00
|
|
|
#define WAIT_FOR_EVENT(mask) resume_point_ = __LINE__; interesting_event_mask_ = int(mask); return; case __LINE__:
|
2017-08-14 20:32:53 +00:00
|
|
|
#define WAIT_FOR_TIME(ms) resume_point_ = __LINE__; delay_time_ = ms * 8000; WAIT_FOR_EVENT(Event1770::Timer);
|
2019-12-23 01:59:20 +00:00
|
|
|
#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; }
|
2016-09-25 00:12:45 +00:00
|
|
|
#define BEGIN_SECTION() switch(resume_point_) { default:
|
2017-11-12 21:30:23 +00:00
|
|
|
#define END_SECTION() (void)0; }
|
2016-09-25 00:12:45 +00:00
|
|
|
|
2016-09-25 13:54:49 +00:00
|
|
|
#define READ_ID() \
|
2019-12-22 22:58:33 +00:00
|
|
|
if(new_event_type == int(Event::Token)) { \
|
|
|
|
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 && get_latest_token().type == Token::Byte) { \
|
2017-08-14 20:32:53 +00:00
|
|
|
header_[distance_into_section_ - 1] = get_latest_token().byte_value; \
|
2019-12-22 22:58:33 +00:00
|
|
|
++distance_into_section_; \
|
2016-09-25 13:54:49 +00:00
|
|
|
} \
|
|
|
|
}
|
2016-09-25 00:12:45 +00:00
|
|
|
|
2016-09-25 18:11:22 +00:00
|
|
|
#define CONCATENATE(x, y) x ## y
|
|
|
|
#define INDIRECT_CONCATENATE(x, y) TOKENPASTE(x, y)
|
|
|
|
#define LINE_LABEL INDIRECT_CONCATENATE(label, __LINE__)
|
|
|
|
|
|
|
|
#define SPIN_UP() \
|
2017-09-12 02:10:56 +00:00
|
|
|
set_motor_on(true); \
|
2016-09-25 18:11:22 +00:00
|
|
|
index_hole_count_ = 0; \
|
|
|
|
index_hole_count_target_ = 6; \
|
2017-08-14 20:32:53 +00:00
|
|
|
WAIT_FOR_EVENT(Event1770::IndexHoleTarget); \
|
2016-12-01 03:26:02 +00:00
|
|
|
status_.spin_up = true;
|
2016-09-25 18:11:22 +00:00
|
|
|
|
2018-09-10 01:02:31 +00:00
|
|
|
// +--------+----------+-------------------------+
|
|
|
|
// ! ! ! 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 !
|
|
|
|
// +--------+----------+-------------------------+
|
2016-12-02 01:12:22 +00:00
|
|
|
|
2017-08-14 20:32:53 +00:00
|
|
|
void WD1770::posit_event(int new_event_type) {
|
2019-12-23 01:59:20 +00:00
|
|
|
if(new_event_type == int(Event::IndexHole)) {
|
2017-08-14 20:32:53 +00:00
|
|
|
index_hole_count_++;
|
|
|
|
if(index_hole_count_target_ == index_hole_count_) {
|
2019-12-23 01:59:20 +00:00
|
|
|
posit_event(int(Event1770::IndexHoleTarget));
|
2017-08-14 20:32:53 +00:00
|
|
|
index_hole_count_target_ = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// motor power-down
|
|
|
|
if(index_hole_count_ == 9 && !status_.busy && has_motor_on_line()) {
|
2017-09-12 02:10:56 +00:00
|
|
|
set_motor_on(false);
|
2017-08-14 20:32:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// head unload
|
|
|
|
if(index_hole_count_ == 15 && !status_.busy && has_head_load_line()) {
|
|
|
|
set_head_load_request(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-23 01:59:20 +00:00
|
|
|
if(new_event_type == int(Event1770::ForceInterrupt)) {
|
2018-01-08 00:42:38 +00:00
|
|
|
interesting_event_mask_ = 0;
|
|
|
|
resume_point_ = 0;
|
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.type = Status::One;
|
2018-01-08 01:02:40 +00:00
|
|
|
status.data_request = false;
|
2019-12-25 00:37:37 +00:00
|
|
|
status.spin_up = false;
|
2018-01-08 00:42:38 +00:00
|
|
|
});
|
|
|
|
} else {
|
2019-12-23 01:59:20 +00:00
|
|
|
if(!(interesting_event_mask_ & int(new_event_type))) return;
|
2018-01-08 00:42:38 +00:00
|
|
|
interesting_event_mask_ &= ~new_event_type;
|
|
|
|
}
|
2016-09-25 00:12:45 +00:00
|
|
|
|
|
|
|
BEGIN_SECTION()
|
|
|
|
|
|
|
|
// Wait for a new command, branch to the appropriate handler.
|
2018-01-08 00:42:38 +00:00
|
|
|
case 0:
|
2016-09-25 00:12:45 +00:00
|
|
|
wait_for_command:
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Idle...");
|
2017-08-14 20:32:53 +00:00
|
|
|
set_data_mode(DataMode::Scanning);
|
2016-09-25 02:43:12 +00:00
|
|
|
index_hole_count_ = 0;
|
2016-12-01 03:39:55 +00:00
|
|
|
|
2016-12-01 12:41:52 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.busy = false;
|
2016-12-07 02:16:29 +00:00
|
|
|
status.interrupt_request = true;
|
2016-12-01 12:41:52 +00:00
|
|
|
});
|
|
|
|
|
2017-08-14 20:32:53 +00:00
|
|
|
WAIT_FOR_EVENT(Event1770::Command);
|
2016-12-01 12:41:52 +00:00
|
|
|
|
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.busy = true;
|
2016-12-07 02:16:29 +00:00
|
|
|
status.interrupt_request = false;
|
2019-12-25 02:43:20 +00:00
|
|
|
status.track_zero = false; // Always reset by a non-type 1; so reset regardless and set properly later.
|
2016-12-01 12:41:52 +00:00
|
|
|
});
|
|
|
|
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Starting %02x", command_);
|
2016-12-01 03:26:02 +00:00
|
|
|
|
2016-09-25 00:12:45 +00:00
|
|
|
if(!(command_ & 0x80)) goto begin_type_1;
|
|
|
|
if(!(command_ & 0x40)) goto begin_type_2;
|
|
|
|
goto begin_type_3;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Type 1 entry point.
|
|
|
|
*/
|
2018-09-10 01:02:31 +00:00
|
|
|
// +--------+----------+-------------------------+
|
|
|
|
// ! ! ! 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 !
|
|
|
|
// +--------+----------+-------------------------+
|
2016-12-31 21:01:44 +00:00
|
|
|
|
2016-09-25 00:12:45 +00:00
|
|
|
begin_type_1:
|
|
|
|
// Set initial flags, skip spin-up if possible.
|
2016-12-01 12:41:52 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.type = Status::One;
|
|
|
|
status.seek_error = false;
|
|
|
|
status.crc_error = false;
|
|
|
|
status.data_request = false;
|
|
|
|
});
|
2016-12-01 03:26:02 +00:00
|
|
|
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Step/Seek/Restore with track %d data %d", track_, data_);
|
2016-12-02 01:12:22 +00:00
|
|
|
if(!has_motor_on_line() && !has_head_load_line()) goto test_type1_type;
|
2016-09-25 00:12:45 +00:00
|
|
|
|
2016-12-02 01:12:22 +00:00
|
|
|
if(has_motor_on_line()) goto begin_type1_spin_up;
|
|
|
|
goto begin_type1_load_head;
|
|
|
|
|
|
|
|
begin_type1_load_head:
|
2017-03-26 18:34:47 +00:00
|
|
|
if(!(command_&0x08)) {
|
2016-12-02 01:12:22 +00:00
|
|
|
set_head_load_request(false);
|
|
|
|
goto test_type1_type;
|
|
|
|
}
|
|
|
|
set_head_load_request(true);
|
|
|
|
if(head_is_loaded_) goto test_type1_type;
|
2017-08-14 20:32:53 +00:00
|
|
|
WAIT_FOR_EVENT(Event1770::HeadLoad);
|
2016-12-02 01:12:22 +00:00
|
|
|
goto test_type1_type;
|
|
|
|
|
|
|
|
begin_type1_spin_up:
|
2021-06-22 03:26:55 +00:00
|
|
|
if((command_&0x08) || get_drive().get_motor_on()) {
|
|
|
|
set_motor_on(true);
|
|
|
|
goto test_type1_type;
|
|
|
|
}
|
2016-09-25 18:11:22 +00:00
|
|
|
SPIN_UP();
|
2016-09-25 00:12:45 +00:00
|
|
|
|
|
|
|
test_type1_type:
|
|
|
|
// Set step direction if this is a step in or out.
|
|
|
|
if((command_ >> 5) == 2) step_direction_ = 1;
|
|
|
|
if((command_ >> 5) == 3) step_direction_ = 0;
|
|
|
|
if((command_ >> 5) != 0) goto perform_step_command;
|
|
|
|
|
2016-09-25 02:43:12 +00:00
|
|
|
// This is now definitely either a seek or a restore; if it's a restore then set track to 0xff and data to 0x00.
|
2017-03-26 18:34:47 +00:00
|
|
|
if(!(command_ & 0x10)) {
|
2016-09-25 02:43:12 +00:00
|
|
|
track_ = 0xff;
|
|
|
|
data_ = 0;
|
|
|
|
}
|
2016-09-25 00:12:45 +00:00
|
|
|
|
|
|
|
perform_seek_or_restore_command:
|
2019-12-25 02:43:20 +00:00
|
|
|
if(track_ == data_) goto verify_seek;
|
2016-09-25 02:04:54 +00:00
|
|
|
step_direction_ = (data_ > track_);
|
2016-09-25 00:12:45 +00:00
|
|
|
|
|
|
|
adjust_track:
|
2019-12-23 01:59:20 +00:00
|
|
|
if(step_direction_) ++track_; else --track_;
|
2016-09-25 00:12:45 +00:00
|
|
|
|
|
|
|
perform_step:
|
2017-09-10 23:23:23 +00:00
|
|
|
if(!step_direction_ && get_drive().get_is_track_zero()) {
|
2016-09-25 00:12:45 +00:00
|
|
|
track_ = 0;
|
2019-12-25 02:43:20 +00:00
|
|
|
goto verify_seek;
|
2016-09-25 00:12:45 +00:00
|
|
|
}
|
2018-05-07 03:17:36 +00:00
|
|
|
get_drive().step(Storage::Disk::HeadPosition(step_direction_ ? 1 : -1));
|
2019-10-30 02:36:29 +00:00
|
|
|
Cycles::IntType time_to_wait;
|
2017-03-26 18:34:47 +00:00
|
|
|
switch(command_ & 3) {
|
2016-09-20 19:56:31 +00:00
|
|
|
default:
|
2016-12-01 03:26:02 +00:00
|
|
|
case 0: time_to_wait = 6; break;
|
|
|
|
case 1: time_to_wait = 12; break;
|
|
|
|
case 2: time_to_wait = (personality_ == P1772) ? 2 : 20; break;
|
|
|
|
case 3: time_to_wait = (personality_ == P1772) ? 3 : 30; break;
|
2016-09-20 19:56:31 +00:00
|
|
|
}
|
2016-09-25 00:16:23 +00:00
|
|
|
WAIT_FOR_TIME(time_to_wait);
|
2019-12-25 02:43:20 +00:00
|
|
|
if(command_ >> 5) goto verify_seek;
|
2016-09-25 00:12:45 +00:00
|
|
|
goto perform_seek_or_restore_command;
|
2016-09-21 02:14:33 +00:00
|
|
|
|
2016-09-25 00:12:45 +00:00
|
|
|
perform_step_command:
|
|
|
|
if(command_ & 0x10) goto adjust_track;
|
|
|
|
goto perform_step;
|
2016-09-21 02:14:33 +00:00
|
|
|
|
2019-12-25 02:43:20 +00:00
|
|
|
verify_seek:
|
|
|
|
update_status([this] (Status &status) {
|
|
|
|
status.track_zero = get_drive().get_is_track_zero();
|
|
|
|
});
|
2017-03-26 18:34:47 +00:00
|
|
|
if(!(command_ & 0x04)) {
|
2016-09-25 00:12:45 +00:00
|
|
|
goto wait_for_command;
|
|
|
|
}
|
2016-09-23 01:25:31 +00:00
|
|
|
|
2016-09-25 13:54:49 +00:00
|
|
|
index_hole_count_ = 0;
|
2016-09-26 01:44:46 +00:00
|
|
|
distance_into_section_ = 0;
|
2016-09-25 13:54:49 +00:00
|
|
|
|
|
|
|
verify_read_data:
|
2019-12-23 01:59:20 +00:00
|
|
|
WAIT_FOR_EVENT(int(Event::IndexHole) | int(Event::Token));
|
2016-09-25 13:54:49 +00:00
|
|
|
READ_ID();
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
if(index_hole_count_ == 6) {
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Nothing found to verify");
|
2016-12-01 12:41:52 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.seek_error = true;
|
|
|
|
});
|
2016-09-25 13:54:49 +00:00
|
|
|
goto wait_for_command;
|
|
|
|
}
|
2017-03-26 18:34:47 +00:00
|
|
|
if(distance_into_section_ == 7) {
|
2019-12-23 00:58:02 +00:00
|
|
|
distance_into_section_ = 0;
|
2017-08-14 20:32:53 +00:00
|
|
|
set_data_mode(DataMode::Scanning);
|
2019-12-23 00:58:02 +00:00
|
|
|
|
2017-08-14 20:32:53 +00:00
|
|
|
if(get_crc_generator().get_value()) {
|
2016-12-31 21:01:44 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.crc_error = true;
|
|
|
|
});
|
|
|
|
goto verify_read_data;
|
|
|
|
}
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
if(header_[0] == track_) {
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Reached track %d", track_);
|
2016-12-01 12:41:52 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.crc_error = false;
|
|
|
|
});
|
2016-09-25 13:54:49 +00:00
|
|
|
goto wait_for_command;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto verify_read_data;
|
2016-09-25 00:12:45 +00:00
|
|
|
|
2016-09-25 02:04:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
Type 2 entry point.
|
|
|
|
*/
|
2018-09-10 01:02:31 +00:00
|
|
|
// +--------+----------+-------------------------+
|
|
|
|
// ! ! ! 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 !
|
|
|
|
// +--------+----------+-------------------------+
|
2016-12-31 21:01:44 +00:00
|
|
|
|
2016-09-25 00:12:45 +00:00
|
|
|
begin_type_2:
|
2016-12-01 12:41:52 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.type = Status::Two;
|
|
|
|
status.lost_data = false;
|
|
|
|
status.record_not_found = false;
|
|
|
|
status.write_protect = false;
|
|
|
|
status.record_type = false;
|
|
|
|
status.data_request = false;
|
|
|
|
});
|
2016-09-25 02:29:02 +00:00
|
|
|
distance_into_section_ = 0;
|
2016-12-01 03:26:02 +00:00
|
|
|
|
2016-12-02 01:12:22 +00:00
|
|
|
if((command_&0x08) && has_motor_on_line()) goto test_type2_delay;
|
2021-06-22 03:26:55 +00:00
|
|
|
if(!has_motor_on_line() && !has_head_load_line()) {
|
|
|
|
if(has_motor_on_line()) set_motor_on(true);
|
|
|
|
goto test_type2_delay;
|
|
|
|
}
|
2016-12-02 01:12:22 +00:00
|
|
|
|
|
|
|
if(has_motor_on_line()) goto begin_type2_spin_up;
|
|
|
|
goto begin_type2_load_head;
|
2016-09-25 02:04:54 +00:00
|
|
|
|
2016-12-02 01:12:22 +00:00
|
|
|
begin_type2_load_head:
|
|
|
|
set_head_load_request(true);
|
|
|
|
if(head_is_loaded_) goto test_type2_delay;
|
2017-08-14 20:32:53 +00:00
|
|
|
WAIT_FOR_EVENT(Event1770::HeadLoad);
|
2016-12-02 01:12:22 +00:00
|
|
|
goto test_type2_delay;
|
|
|
|
|
|
|
|
begin_type2_spin_up:
|
2017-09-10 23:23:23 +00:00
|
|
|
if(get_drive().get_motor_on()) goto test_type2_delay;
|
2016-09-25 02:04:54 +00:00
|
|
|
// Perform spin up.
|
2016-09-25 18:11:22 +00:00
|
|
|
SPIN_UP();
|
2016-09-25 02:04:54 +00:00
|
|
|
|
|
|
|
test_type2_delay:
|
|
|
|
index_hole_count_ = 0;
|
|
|
|
if(!(command_ & 0x04)) goto test_type2_write_protection;
|
|
|
|
WAIT_FOR_TIME(30);
|
|
|
|
|
|
|
|
test_type2_write_protection:
|
2017-09-10 23:23:23 +00:00
|
|
|
if(command_&0x20 && get_drive().get_is_read_only()) {
|
2016-12-01 12:41:52 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.write_protect = true;
|
|
|
|
});
|
2016-09-25 02:04:54 +00:00
|
|
|
goto wait_for_command;
|
|
|
|
}
|
|
|
|
|
2019-12-23 00:58:02 +00:00
|
|
|
distance_into_section_ = 0;
|
|
|
|
set_data_mode(DataMode::Scanning);
|
|
|
|
|
2016-09-25 02:04:54 +00:00
|
|
|
type2_get_header:
|
2019-12-23 01:59:20 +00:00
|
|
|
WAIT_FOR_EVENT(int(Event::IndexHole) | int(Event::Token));
|
2016-09-25 13:54:49 +00:00
|
|
|
READ_ID();
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
if(index_hole_count_ == 5) {
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Failed to find sector %d", sector_);
|
2016-12-01 12:41:52 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.record_not_found = true;
|
|
|
|
});
|
2016-09-25 02:04:54 +00:00
|
|
|
goto wait_for_command;
|
|
|
|
}
|
2017-03-26 18:34:47 +00:00
|
|
|
if(distance_into_section_ == 7) {
|
2019-12-23 00:58:02 +00:00
|
|
|
distance_into_section_ = 0;
|
2017-08-14 20:32:53 +00:00
|
|
|
set_data_mode(DataMode::Scanning);
|
2019-12-23 00:58:02 +00:00
|
|
|
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Considering %d/%d", header_[0], header_[2]);
|
2017-03-26 18:34:47 +00:00
|
|
|
if( header_[0] == track_ && header_[2] == sector_ &&
|
|
|
|
(has_motor_on_line() || !(command_&0x02) || ((command_&0x08) >> 3) == header_[1])) {
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Found %d/%d", header_[0], header_[2]);
|
2017-08-14 20:32:53 +00:00
|
|
|
if(get_crc_generator().get_value()) {
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("CRC error; back to searching");
|
2016-12-28 23:29:37 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.crc_error = true;
|
|
|
|
});
|
|
|
|
goto type2_get_header;
|
|
|
|
}
|
|
|
|
|
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.crc_error = false;
|
|
|
|
});
|
2016-09-25 13:54:49 +00:00
|
|
|
goto type2_read_or_write_data;
|
|
|
|
}
|
|
|
|
}
|
2016-09-25 02:04:54 +00:00
|
|
|
goto type2_get_header;
|
|
|
|
|
2016-09-25 00:12:45 +00:00
|
|
|
|
2016-09-25 02:04:54 +00:00
|
|
|
type2_read_or_write_data:
|
2016-09-25 02:29:02 +00:00
|
|
|
if(command_&0x20) goto type2_write_data;
|
|
|
|
goto type2_read_data;
|
|
|
|
|
|
|
|
type2_read_data:
|
|
|
|
WAIT_FOR_EVENT(Event::Token);
|
|
|
|
// TODO: timeout
|
2017-08-14 20:32:53 +00:00
|
|
|
if(get_latest_token().type == Token::Data || get_latest_token().type == Token::DeletedData) {
|
2016-12-01 12:41:52 +00:00
|
|
|
update_status([this] (Status &status) {
|
2017-08-14 20:32:53 +00:00
|
|
|
status.record_type = (get_latest_token().type == Token::DeletedData);
|
2016-12-01 12:41:52 +00:00
|
|
|
});
|
2016-09-25 02:29:02 +00:00
|
|
|
distance_into_section_ = 0;
|
2017-08-14 20:32:53 +00:00
|
|
|
set_data_mode(DataMode::Reading);
|
2016-09-25 02:29:02 +00:00
|
|
|
goto type2_read_byte;
|
|
|
|
}
|
|
|
|
goto type2_read_data;
|
|
|
|
|
|
|
|
type2_read_byte:
|
|
|
|
WAIT_FOR_EVENT(Event::Token);
|
2017-08-14 20:32:53 +00:00
|
|
|
if(get_latest_token().type != Token::Byte) goto type2_read_byte;
|
|
|
|
data_ = get_latest_token().byte_value;
|
2016-12-01 12:41:52 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.lost_data |= status.data_request;
|
|
|
|
status.data_request = true;
|
|
|
|
});
|
2016-09-25 02:29:02 +00:00
|
|
|
distance_into_section_++;
|
2020-01-18 18:40:50 +00:00
|
|
|
if(distance_into_section_ == 128 << (header_[3]&3)) {
|
2016-09-25 02:35:34 +00:00
|
|
|
distance_into_section_ = 0;
|
|
|
|
goto type2_check_crc;
|
|
|
|
}
|
|
|
|
goto type2_read_byte;
|
|
|
|
|
|
|
|
type2_check_crc:
|
|
|
|
WAIT_FOR_EVENT(Event::Token);
|
2017-08-14 20:32:53 +00:00
|
|
|
if(get_latest_token().type != Token::Byte) goto type2_read_byte;
|
|
|
|
header_[distance_into_section_] = get_latest_token().byte_value;
|
2016-09-25 02:35:34 +00:00
|
|
|
distance_into_section_++;
|
2017-03-26 18:34:47 +00:00
|
|
|
if(distance_into_section_ == 2) {
|
2019-12-23 00:58:02 +00:00
|
|
|
distance_into_section_ = 0;
|
|
|
|
set_data_mode(DataMode::Scanning);
|
|
|
|
|
2017-08-14 20:32:53 +00:00
|
|
|
if(get_crc_generator().get_value()) {
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("CRC error; terminating");
|
2020-05-30 04:37:06 +00:00
|
|
|
update_status([] (Status &status) {
|
2016-12-28 23:29:37 +00:00
|
|
|
status.crc_error = true;
|
|
|
|
});
|
|
|
|
goto wait_for_command;
|
2016-12-26 21:46:26 +00:00
|
|
|
}
|
|
|
|
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Finished reading sector %d", sector_);
|
2019-12-23 00:58:02 +00:00
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
if(command_ & 0x10) {
|
2016-09-25 02:29:02 +00:00
|
|
|
sector_++;
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Advancing to search for sector %d", sector_);
|
2016-09-25 02:29:02 +00:00
|
|
|
goto test_type2_write_protection;
|
|
|
|
}
|
|
|
|
goto wait_for_command;
|
|
|
|
}
|
2016-09-25 02:35:34 +00:00
|
|
|
goto type2_check_crc;
|
|
|
|
|
2016-09-25 02:29:02 +00:00
|
|
|
|
|
|
|
type2_write_data:
|
2016-12-25 17:31:38 +00:00
|
|
|
WAIT_FOR_BYTES(2);
|
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.data_request = true;
|
|
|
|
});
|
|
|
|
WAIT_FOR_BYTES(9);
|
2017-03-26 18:34:47 +00:00
|
|
|
if(status_.data_request) {
|
2016-12-25 17:31:38 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.lost_data = true;
|
|
|
|
});
|
|
|
|
goto wait_for_command;
|
|
|
|
}
|
|
|
|
WAIT_FOR_BYTES(1);
|
2017-08-14 20:32:53 +00:00
|
|
|
if(get_is_double_density()) {
|
2016-12-25 17:31:38 +00:00
|
|
|
WAIT_FOR_BYTES(11);
|
|
|
|
}
|
|
|
|
|
2017-08-14 20:32:53 +00:00
|
|
|
set_data_mode(DataMode::Writing);
|
2017-08-15 20:05:10 +00:00
|
|
|
begin_writing(false);
|
2017-08-14 20:32:53 +00:00
|
|
|
for(int c = 0; c < (get_is_double_density() ? 12 : 6); c++) {
|
2016-12-26 00:18:45 +00:00
|
|
|
write_byte(0);
|
2016-12-25 17:31:38 +00:00
|
|
|
}
|
2016-12-26 01:15:07 +00:00
|
|
|
WAIT_FOR_EVENT(Event::DataWritten);
|
|
|
|
|
2017-08-14 20:32:53 +00:00
|
|
|
if(get_is_double_density()) {
|
|
|
|
get_crc_generator().set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue);
|
2016-12-28 23:48:50 +00:00
|
|
|
for(int c = 0; c < 3; c++) write_raw_short(Storage::Encodings::MFM::MFMSync);
|
2016-12-31 20:25:11 +00:00
|
|
|
write_byte((command_&0x01) ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
|
2017-03-26 18:34:47 +00:00
|
|
|
} else {
|
2017-08-14 20:32:53 +00:00
|
|
|
get_crc_generator().reset();
|
|
|
|
get_crc_generator().add((command_&0x01) ? Storage::Encodings::MFM::DeletedDataAddressByte : Storage::Encodings::MFM::DataAddressByte);
|
2016-12-26 01:19:47 +00:00
|
|
|
write_raw_short((command_&0x01) ? Storage::Encodings::MFM::FMDeletedDataAddressMark : Storage::Encodings::MFM::FMDataAddressMark);
|
2016-12-26 01:15:07 +00:00
|
|
|
}
|
|
|
|
|
2016-12-25 20:46:49 +00:00
|
|
|
WAIT_FOR_EVENT(Event::DataWritten);
|
|
|
|
distance_into_section_ = 0;
|
|
|
|
|
|
|
|
type2_write_loop:
|
2016-12-26 17:48:49 +00:00
|
|
|
/*
|
|
|
|
This deviates from the data sheet slightly since that would prima facie request one more byte
|
2018-05-13 19:34:31 +00:00
|
|
|
of data than is actually written; the last time around the loop it has transferred from the
|
2016-12-26 17:48:49 +00:00
|
|
|
data register to the data shift register, set data request, written the byte, checked that data
|
|
|
|
request has been satified, then finally considers whether all bytes are done. Based on both
|
|
|
|
natural expectations and the way that emulated machines responded, I believe that to be a
|
|
|
|
documentation error.
|
|
|
|
*/
|
|
|
|
write_byte(data_);
|
2016-12-25 20:46:49 +00:00
|
|
|
distance_into_section_++;
|
2020-01-18 18:40:50 +00:00
|
|
|
if(distance_into_section_ == 128 << (header_[3]&3)) {
|
2016-12-29 02:24:19 +00:00
|
|
|
goto type2_write_crc;
|
2016-12-25 20:46:49 +00:00
|
|
|
}
|
|
|
|
|
2016-12-29 02:24:19 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.data_request = true;
|
|
|
|
});
|
|
|
|
WAIT_FOR_EVENT(Event::DataWritten);
|
2017-03-26 18:34:47 +00:00
|
|
|
if(status_.data_request) {
|
2017-09-15 02:30:40 +00:00
|
|
|
end_writing();
|
2016-12-25 20:46:49 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.lost_data = true;
|
|
|
|
});
|
|
|
|
goto wait_for_command;
|
|
|
|
}
|
|
|
|
|
2016-12-25 21:13:05 +00:00
|
|
|
goto type2_write_loop;
|
|
|
|
|
2017-08-14 20:32:53 +00:00
|
|
|
type2_write_crc:
|
|
|
|
write_crc();
|
2016-12-26 00:18:45 +00:00
|
|
|
write_byte(0xff);
|
2016-12-25 20:46:49 +00:00
|
|
|
WAIT_FOR_EVENT(Event::DataWritten);
|
2017-09-15 02:30:40 +00:00
|
|
|
end_writing();
|
2016-09-25 00:12:45 +00:00
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
if(command_ & 0x10) {
|
2016-12-25 20:46:49 +00:00
|
|
|
sector_++;
|
|
|
|
goto test_type2_write_protection;
|
|
|
|
}
|
2024-01-19 15:36:09 +00:00
|
|
|
logger.info().append("Wrote sector %d", sector_);
|
2016-12-25 20:46:49 +00:00
|
|
|
goto wait_for_command;
|
|
|
|
|
2016-12-31 21:01:44 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
Type 3 entry point.
|
|
|
|
*/
|
2018-09-10 01:02:31 +00:00
|
|
|
// +--------+----------+-------------------------+
|
|
|
|
// ! ! ! 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 !
|
|
|
|
// +--------+----------+-------------------------+
|
2016-09-25 00:12:45 +00:00
|
|
|
begin_type_3:
|
2016-12-01 12:41:52 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.type = Status::Three;
|
2017-01-02 02:00:25 +00:00
|
|
|
status.crc_error = false;
|
|
|
|
status.lost_data = false;
|
|
|
|
status.record_not_found = false;
|
2016-12-01 12:41:52 +00:00
|
|
|
});
|
2017-01-02 01:39:19 +00:00
|
|
|
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;
|
2017-08-14 20:32:53 +00:00
|
|
|
WAIT_FOR_EVENT(Event1770::HeadLoad);
|
2017-01-02 01:39:19 +00:00
|
|
|
goto type3_test_delay;
|
|
|
|
|
|
|
|
begin_type3_spin_up:
|
2017-09-10 23:23:23 +00:00
|
|
|
if((command_&0x08) || get_drive().get_motor_on()) goto type3_test_delay;
|
2017-01-02 01:39:19 +00:00
|
|
|
SPIN_UP();
|
|
|
|
|
|
|
|
type3_test_delay:
|
|
|
|
if(!(command_&0x04)) goto test_type3_type;
|
|
|
|
WAIT_FOR_TIME(30);
|
|
|
|
|
|
|
|
test_type3_type:
|
2017-01-02 01:56:52 +00:00
|
|
|
if(!(command_&0x20)) goto begin_read_address;
|
|
|
|
if(!(command_&0x10)) goto begin_read_track;
|
|
|
|
goto begin_write_track;
|
2016-12-31 21:01:44 +00:00
|
|
|
|
|
|
|
begin_read_address:
|
2017-01-02 01:55:09 +00:00
|
|
|
index_hole_count_ = 0;
|
|
|
|
distance_into_section_ = 0;
|
|
|
|
|
|
|
|
read_address_get_header:
|
2019-12-23 01:59:20 +00:00
|
|
|
WAIT_FOR_EVENT(int(Event::IndexHole) | int(Event::Token));
|
|
|
|
if(new_event_type == int(Event::Token)) {
|
2017-08-14 20:32:53 +00:00
|
|
|
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 && get_latest_token().type == Token::Byte) {
|
2017-03-26 18:34:47 +00:00
|
|
|
if(status_.data_request) {
|
2017-01-02 01:55:09 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.lost_data = true;
|
|
|
|
});
|
|
|
|
goto wait_for_command;
|
|
|
|
}
|
2017-08-14 20:32:53 +00:00
|
|
|
header_[distance_into_section_ - 1] = data_ = get_latest_token().byte_value;
|
2017-01-02 01:55:09 +00:00
|
|
|
track_ = header_[0];
|
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.data_request = true;
|
|
|
|
});
|
2019-12-23 00:58:02 +00:00
|
|
|
++distance_into_section_;
|
2017-01-02 01:55:09 +00:00
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
if(distance_into_section_ == 7) {
|
2019-12-23 00:58:02 +00:00
|
|
|
distance_into_section_ = 0;
|
|
|
|
|
2017-08-14 20:32:53 +00:00
|
|
|
if(get_crc_generator().get_value()) {
|
2017-01-02 02:00:25 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.crc_error = true;
|
|
|
|
});
|
|
|
|
}
|
2017-01-02 01:55:09 +00:00
|
|
|
goto wait_for_command;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
if(index_hole_count_ == 6) {
|
2017-01-02 01:55:09 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.record_not_found = true;
|
|
|
|
});
|
|
|
|
goto wait_for_command;
|
|
|
|
}
|
|
|
|
goto read_address_get_header;
|
2016-12-31 21:01:44 +00:00
|
|
|
|
|
|
|
begin_read_track:
|
2017-01-02 01:39:19 +00:00
|
|
|
WAIT_FOR_EVENT(Event::IndexHole);
|
|
|
|
index_hole_count_ = 0;
|
|
|
|
|
|
|
|
read_track_read_byte:
|
2019-12-23 01:59:20 +00:00
|
|
|
WAIT_FOR_EVENT(int(Event::Token) | int(Event::IndexHole));
|
2017-03-26 18:34:47 +00:00
|
|
|
if(index_hole_count_) {
|
2017-01-02 01:39:19 +00:00
|
|
|
goto wait_for_command;
|
|
|
|
}
|
2017-03-26 18:34:47 +00:00
|
|
|
if(status_.data_request) {
|
2017-01-02 01:39:19 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.lost_data = true;
|
|
|
|
});
|
|
|
|
goto wait_for_command;
|
|
|
|
}
|
2017-08-14 20:32:53 +00:00
|
|
|
data_ = get_latest_token().byte_value;
|
2017-01-02 01:39:19 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.data_request = true;
|
|
|
|
});
|
|
|
|
goto read_track_read_byte;
|
2016-12-31 21:01:44 +00:00
|
|
|
|
|
|
|
begin_write_track:
|
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.data_request = false;
|
|
|
|
status.lost_data = false;
|
|
|
|
});
|
|
|
|
|
2017-09-10 23:23:23 +00:00
|
|
|
if(get_drive().get_is_read_only()) {
|
2016-12-31 21:01:44 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.write_protect = true;
|
|
|
|
});
|
|
|
|
goto wait_for_command;
|
|
|
|
}
|
|
|
|
|
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.data_request = true;
|
|
|
|
});
|
|
|
|
WAIT_FOR_BYTES(3);
|
2017-03-26 18:34:47 +00:00
|
|
|
if(status_.data_request) {
|
2016-12-31 21:01:44 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.lost_data = true;
|
|
|
|
});
|
|
|
|
goto wait_for_command;
|
|
|
|
}
|
|
|
|
|
2017-08-14 20:32:53 +00:00
|
|
|
WAIT_FOR_EVENT(Event1770::IndexHoleTarget);
|
2017-08-15 20:05:10 +00:00
|
|
|
begin_writing(true);
|
2016-12-31 21:01:44 +00:00
|
|
|
index_hole_count_ = 0;
|
|
|
|
|
|
|
|
write_track_write_loop:
|
2017-08-14 20:32:53 +00:00
|
|
|
if(get_is_double_density()) {
|
2017-03-26 18:34:47 +00:00
|
|
|
switch(data_) {
|
2016-12-31 21:01:44 +00:00
|
|
|
case 0xf5:
|
|
|
|
write_raw_short(Storage::Encodings::MFM::MFMSync);
|
2017-08-14 20:32:53 +00:00
|
|
|
get_crc_generator().set_value(Storage::Encodings::MFM::MFMPostSyncCRCValue);
|
2016-12-31 21:01:44 +00:00
|
|
|
break;
|
|
|
|
case 0xf6:
|
|
|
|
write_raw_short(Storage::Encodings::MFM::MFMIndexSync);
|
|
|
|
break;
|
2017-08-14 20:32:53 +00:00
|
|
|
case 0xff:
|
|
|
|
write_crc();
|
|
|
|
break;
|
2016-12-31 21:01:44 +00:00
|
|
|
default:
|
|
|
|
write_byte(data_);
|
|
|
|
break;
|
|
|
|
}
|
2017-03-26 18:34:47 +00:00
|
|
|
} else {
|
|
|
|
switch(data_) {
|
2016-12-31 21:01:44 +00:00
|
|
|
case 0xf8: case 0xf9: case 0xfa: case 0xfb:
|
|
|
|
case 0xfd: case 0xfe:
|
|
|
|
// clock is 0xc7 = 1010 0000 0010 1010 = 0xa022
|
|
|
|
write_raw_short(
|
2019-12-23 01:59:20 +00:00
|
|
|
uint16_t(
|
2016-12-31 21:01:44 +00:00
|
|
|
0xa022 |
|
|
|
|
((data_ & 0x80) << 7) |
|
|
|
|
((data_ & 0x40) << 6) |
|
|
|
|
((data_ & 0x20) << 5) |
|
|
|
|
((data_ & 0x10) << 4) |
|
|
|
|
((data_ & 0x08) << 3) |
|
|
|
|
((data_ & 0x04) << 2) |
|
|
|
|
((data_ & 0x02) << 1) |
|
|
|
|
(data_ & 0x01)
|
|
|
|
)
|
|
|
|
);
|
2017-08-14 20:32:53 +00:00
|
|
|
get_crc_generator().reset();
|
|
|
|
get_crc_generator().add(data_);
|
2016-12-31 21:01:44 +00:00
|
|
|
break;
|
|
|
|
case 0xfc:
|
|
|
|
write_raw_short(Storage::Encodings::MFM::FMIndexAddressMark);
|
|
|
|
break;
|
2017-08-14 20:32:53 +00:00
|
|
|
case 0xf7:
|
|
|
|
write_crc();
|
|
|
|
break;
|
2016-12-31 21:01:44 +00:00
|
|
|
default:
|
|
|
|
write_byte(data_);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.data_request = true;
|
|
|
|
});
|
|
|
|
WAIT_FOR_EVENT(Event::DataWritten);
|
2017-03-26 18:34:47 +00:00
|
|
|
if(status_.data_request) {
|
2016-12-31 21:01:44 +00:00
|
|
|
update_status([] (Status &status) {
|
|
|
|
status.lost_data = true;
|
|
|
|
});
|
2017-09-15 02:30:40 +00:00
|
|
|
end_writing();
|
2016-12-31 21:01:44 +00:00
|
|
|
goto wait_for_command;
|
|
|
|
}
|
2017-03-26 18:34:47 +00:00
|
|
|
if(index_hole_count_) {
|
2017-09-15 02:30:40 +00:00
|
|
|
end_writing();
|
2016-12-31 21:01:44 +00:00
|
|
|
goto wait_for_command;
|
|
|
|
}
|
2016-09-25 00:12:45 +00:00
|
|
|
|
2016-12-31 21:01:44 +00:00
|
|
|
goto write_track_write_loop;
|
2016-09-25 00:12:45 +00:00
|
|
|
|
|
|
|
END_SECTION()
|
2016-09-21 02:14:33 +00:00
|
|
|
}
|
2016-11-21 05:21:49 +00:00
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
void WD1770::update_status(std::function<void(Status &)> updater) {
|
2019-10-31 02:59:32 +00:00
|
|
|
const Status old_status = status_;
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
if(delegate_) {
|
2016-12-01 12:41:52 +00:00
|
|
|
updater(status_);
|
2019-10-29 01:21:35 +00:00
|
|
|
const bool did_change =
|
2016-12-01 12:41:52 +00:00
|
|
|
(status_.busy != old_status.busy) ||
|
2019-10-29 01:13:06 +00:00
|
|
|
(status_.data_request != old_status.data_request) ||
|
|
|
|
(status_.interrupt_request != old_status.interrupt_request);
|
2016-12-01 12:41:52 +00:00
|
|
|
if(did_change) delegate_->wd1770_did_change_output(this);
|
2019-10-31 02:59:32 +00:00
|
|
|
} else updater(status_);
|
|
|
|
|
|
|
|
if(status_.busy != old_status.busy) update_clocking_observer();
|
2016-11-21 05:21:49 +00:00
|
|
|
}
|
2016-12-02 01:12:22 +00:00
|
|
|
|
2020-05-30 04:37:06 +00:00
|
|
|
void WD1770::set_head_load_request(bool) {}
|
|
|
|
void WD1770::set_motor_on(bool) {}
|
2016-12-02 01:12:22 +00:00
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
void WD1770::set_head_loaded(bool head_loaded) {
|
2016-12-02 01:12:22 +00:00
|
|
|
head_is_loaded_ = head_loaded;
|
2019-12-23 01:59:20 +00:00
|
|
|
if(head_loaded) posit_event(int(Event1770::HeadLoad));
|
2016-12-26 01:15:07 +00:00
|
|
|
}
|
2019-10-31 02:59:32 +00:00
|
|
|
|
2020-05-10 01:22:51 +00:00
|
|
|
bool WD1770::get_head_loaded() const {
|
2020-01-16 04:39:15 +00:00
|
|
|
return head_is_loaded_;
|
|
|
|
}
|
|
|
|
|
2020-05-10 01:22:51 +00:00
|
|
|
ClockingHint::Preference WD1770::preferred_clocking() const {
|
2019-10-31 02:59:32 +00:00
|
|
|
if(status_.busy) return ClockingHint::Preference::RealTime;
|
|
|
|
return Storage::Disk::MFMController::preferred_clocking();
|
|
|
|
}
|