1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-06 01:28:57 +00:00

Merge pull request #702 from TomHarte/NZStory

Corrects WD track-zero and write-protect flags.
This commit is contained in:
Thomas Harte 2019-12-24 22:22:24 -05:00 committed by GitHub
commit 42dd70dbff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 87 additions and 52 deletions

View File

@ -61,20 +61,28 @@ uint8_t WD1770::get_register(int address) {
status.interrupt_request = false;
});
uint8_t status =
(status_.write_protect ? Flag::WriteProtect : 0) |
(status_.crc_error ? Flag::CRCError : 0) |
(status_.busy ? Flag::Busy : 0);
(status_.crc_error ? Flag::CRCError : 0) |
(status_.busy ? Flag::Busy : 0);
// Per Jean Louis-Guérin's documentation:
//
// * the write-protect bit is locked into place by a type 2 or type 3 command, but is
// 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.
switch(status_.type) {
case Status::One:
status |=
(get_drive().get_is_track_zero() ? Flag::TrackZero : 0) |
(status_.seek_error ? Flag::SeekError : 0);
// TODO: index hole
(status_.track_zero ? Flag::TrackZero : 0) |
(status_.seek_error ? Flag::SeekError : 0) |
(get_drive().get_is_read_only() ? Flag::WriteProtect : 0) |
(get_drive().get_index_pulse() ? Flag::Index : 0);
break;
case Status::Two:
case Status::Three:
status |=
(status_.write_protect ? Flag::WriteProtect : 0) |
(status_.record_type ? Flag::RecordType : 0) |
(status_.lost_data ? Flag::LostData : 0) |
(status_.data_request ? Flag::DataRequest : 0) |
@ -91,7 +99,7 @@ uint8_t WD1770::get_register(int address) {
if(status_.type == Status::One)
status |= (status_.spin_up ? Flag::SpinUp : 0);
}
LOG("Returned status " << PADHEX(2) << int(status) << " of type " << 1+int(status_.type));
// LOG("Returned status " << PADHEX(2) << int(status) << " of type " << 1+int(status_.type));
return status;
}
case 1:
@ -192,6 +200,7 @@ void WD1770::posit_event(int new_event_type) {
update_status([] (Status &status) {
status.type = Status::One;
status.data_request = false;
status.spin_up = false;
});
} else {
if(!(interesting_event_mask_ & int(new_event_type))) return;
@ -217,6 +226,7 @@ void WD1770::posit_event(int new_event_type) {
update_status([] (Status &status) {
status.busy = true;
status.interrupt_request = false;
status.track_zero = false; // Always reset by a non-type 1; so reset regardless and set properly later.
});
LOG("Starting " << PADHEX(2) << int(command_));
@ -282,7 +292,7 @@ void WD1770::posit_event(int new_event_type) {
}
perform_seek_or_restore_command:
if(track_ == data_) goto verify;
if(track_ == data_) goto verify_seek;
step_direction_ = (data_ > track_);
adjust_track:
@ -291,7 +301,7 @@ void WD1770::posit_event(int new_event_type) {
perform_step:
if(!step_direction_ && get_drive().get_is_track_zero()) {
track_ = 0;
goto verify;
goto verify_seek;
}
get_drive().step(Storage::Disk::HeadPosition(step_direction_ ? 1 : -1));
Cycles::IntType time_to_wait;
@ -303,14 +313,17 @@ void WD1770::posit_event(int new_event_type) {
case 3: time_to_wait = (personality_ == P1772) ? 3 : 30; break;
}
WAIT_FOR_TIME(time_to_wait);
if(command_ >> 5) goto verify;
if(command_ >> 5) goto verify_seek;
goto perform_seek_or_restore_command;
perform_step_command:
if(command_ & 0x10) goto adjust_track;
goto perform_step;
verify:
verify_seek:
update_status([this] (Status &status) {
status.track_zero = get_drive().get_is_track_zero();
});
if(!(command_ & 0x04)) {
goto wait_for_command;
}

View File

@ -96,6 +96,7 @@ class WD1770: public Storage::Disk::MFMController {
bool data_request = false;
bool interrupt_request = false;
bool busy = false;
bool track_zero = false;
enum {
One, Two, Three
} type = One;

View File

@ -121,6 +121,7 @@ void DMAController::write(int address, uint16_t value) {
}
void DMAController::set_floppy_drive_selection(bool drive1, bool drive2, bool side2) {
// LOG("Selected: " << (drive1 ? "1" : "-") << (drive2 ? "2" : "-") << (side2 ? "s" : "-"));
fdc_.set_floppy_drive_selection(drive1, drive2, side2);
}
@ -190,11 +191,11 @@ int DMAController::bus_grant(uint16_t *ram, size_t size) {
// Check that the older buffer is full; stop if not.
if(!buffer_[active_buffer_ ^ 1].is_full) return 0;
#define b(i, n) " " << PADHEX(2) << buffer_[i].contents[n]
#define b(i, n) " " << PADHEX(2) << int(buffer_[i].contents[n])
#define b2(i, n) b(i, n) << b(i, n+1)
#define b4(i, n) b2(i, n) << b2(i, n+2)
#define b16(i) b4(i, 0) << b4(i, 4) << b4(i, 8) << b4(i, 12)
LOG("[1] to " << PADHEX(6) << address_ << b16(active_buffer_ ^ 1));
// LOG("[1] to " << PADHEX(6) << address_ << b16(active_buffer_ ^ 1));
for(int c = 0; c < 8; ++c) {
if(size_t(address_) < size) {
@ -210,7 +211,7 @@ int DMAController::bus_grant(uint16_t *ram, size_t size) {
// Check that the newer buffer is full; stop if not.
if(!buffer_[active_buffer_ ].is_full) return 8;
LOG("[2] to " << PADHEX(6) << address_ << b16(active_buffer_));
// LOG("[2] to " << PADHEX(6) << address_ << b16(active_buffer_));
#undef b16
#undef b4
#undef b2

View File

@ -63,7 +63,7 @@ void Drive::set_disk(const std::shared_ptr<Disk> &disk) {
update_clocking_observer();
}
bool Drive::has_disk() {
bool Drive::has_disk() const {
return has_disk_;
}
@ -71,7 +71,7 @@ ClockingHint::Preference Drive::preferred_clocking() {
return (!motor_is_on_ || !has_disk_) ? ClockingHint::Preference::None : ClockingHint::Preference::JustInTime;
}
bool Drive::get_is_track_zero() {
bool Drive::get_is_track_zero() const {
return head_position_ == HeadPosition(0);
}
@ -114,11 +114,11 @@ void Drive::set_head(int head) {
}
}
int Drive::get_head_count() {
int Drive::get_head_count() const {
return available_heads_;
}
bool Drive::get_tachometer() {
bool Drive::get_tachometer() const {
// I have made a guess here that the tachometer is a symmetric square wave;
// if that is correct then around 60 beats per rotation appears to be correct
// to proceed beyond the speed checks I've so far uncovered.
@ -126,22 +126,22 @@ bool Drive::get_tachometer() {
return int(get_rotation() * 2.0f * ticks_per_rotation) & 1;
}
float Drive::get_rotation() {
float Drive::get_rotation() const {
return get_time_into_track();
}
float Drive::get_time_into_track() {
float Drive::get_time_into_track() const {
// i.e. amount of time since the index hole was seen, as a proportion of a second,
// converted to a proportion of a rotation.
return float(cycles_since_index_hole_) / (float(get_input_clock_rate()) * rotational_multiplier_);
}
bool Drive::get_is_read_only() {
bool Drive::get_is_read_only() const {
if(disk_) return disk_->get_is_read_only();
return true;
}
bool Drive::get_is_ready() {
bool Drive::get_is_ready() const {
return ready_index_count_ == 2;
}
@ -164,10 +164,14 @@ void Drive::set_motor_on(bool motor_is_on) {
}
}
bool Drive::get_motor_on() {
bool Drive::get_motor_on() const {
return motor_is_on_;
}
bool Drive::get_index_pulse() const {
return index_pulse_remaining_ > Cycles(0);
}
void Drive::set_event_delegate(Storage::Disk::Drive::EventDelegate *delegate) {
event_delegate_ = delegate;
}
@ -178,6 +182,9 @@ void Drive::advance(const Cycles cycles) {
}
void Drive::run_for(const Cycles cycles) {
// Assumed: the index pulse pulses even if the drive has stopped spinning.
index_pulse_remaining_ = std::max(index_pulse_remaining_ - cycles, Cycles(0));
if(motor_is_on_) {
if(has_disk_) {
Time zero(0);
@ -258,6 +265,11 @@ void Drive::get_next_event(float duration_already_passed) {
current_event_.type = Track::Event::IndexHole;
}
// Begin a 2ms period of holding the index line pulse active if this is an index pulse event.
if(current_event_.type == Track::Event::IndexHole) {
index_pulse_remaining_ = Cycles((get_input_clock_rate() * 2) / 1000);
}
// divide interval, which is in terms of a single rotation of the disk, by rotation speed to
// convert it into revolutions per second; this is achieved by multiplying by rotational_multiplier_
float interval = std::max((current_event_.length - duration_already_passed) * rotational_multiplier_, 0.0f);
@ -384,7 +396,7 @@ void Drive::end_writing() {
}
}
bool Drive::is_writing() {
bool Drive::is_writing() const {
return !is_reading_;
}

View File

@ -36,12 +36,12 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
/*!
@returns @c true if a disk is currently inserted; @c false otherwise.
*/
bool has_disk();
bool has_disk() const;
/*!
@returns @c true if the drive head is currently at track zero; @c false otherwise.
*/
bool get_is_track_zero();
bool get_is_track_zero() const;
/*!
Steps the disk head the specified number of tracks. Positive numbers step inwards (i.e. away from track 0),
@ -57,17 +57,17 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
/*!
Gets the head count for this disk.
*/
int get_head_count();
int get_head_count() const;
/*!
@returns @c true if the inserted disk is read-only or no disk is inserted; @c false otherwise.
*/
bool get_is_read_only();
bool get_is_read_only() const;
/*!
@returns @c true if the drive is ready; @c false otherwise.
*/
bool get_is_ready();
bool get_is_ready() const;
/*!
Sets whether the disk motor is on.
@ -77,7 +77,12 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
/*!
@returns @c true if the motor is on; @c false otherwise.
*/
bool get_motor_on();
bool get_motor_on() const;
/*!
@returns @c true if the index pulse output is active; @c false otherwise.
*/
bool get_index_pulse() const;
/*!
Begins write mode, initiating a PCM sampled region of data. Bits should be written via
@ -104,7 +109,7 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
@returns @c true if the drive has received a call to begin_writing but not yet a call to
end_writing; @c false otherwise.
*/
bool is_writing();
bool is_writing() const;
/*!
Advances the drive by @c number_of_cycles cycles.
@ -163,7 +168,7 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
/*!
@returns the current value of the tachometer pulse offered by some drives.
*/
bool get_tachometer();
bool get_tachometer() const;
protected:
/*!
@ -180,7 +185,7 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
@returns the current rotation of the disk, a float in the half-open range
0.0 (the index hole) to 1.0 (back to the index hole, a whole rotation later).
*/
float get_rotation();
float get_rotation() const;
private:
// Drives contain an entire disk; from that a certain track
@ -210,6 +215,9 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
// Motor control state.
bool motor_is_on_ = false;
// Current state of the index pulse output.
Cycles index_pulse_remaining_;
// If the drive is not currently reading then it is writing. While writing
// it can optionally be told to clamp to the index hole.
bool is_reading_ = true;
@ -235,7 +243,7 @@ class Drive: public ClockingHint::Source, public TimedEventLoop {
void advance(const Cycles cycles) override;
// Helper for track changes.
float get_time_into_track();
float get_time_into_track() const;
// The target (if any) for track events.
EventDelegate *event_delegate_ = nullptr;

View File

@ -21,40 +21,40 @@ namespace Disk {
class HeadPosition {
public:
/// Creates an instance decribing position @c value at a resolution of @c scale ticks per track.
HeadPosition(int value, int scale) : position_(value * (4/scale)) {}
explicit HeadPosition(int value) : HeadPosition(value, 1) {}
HeadPosition() : HeadPosition(0) {}
constexpr HeadPosition(int value, int scale) : position_(value * (4/scale)) {}
constexpr explicit HeadPosition(int value) : HeadPosition(value, 1) {}
constexpr HeadPosition() : HeadPosition(0) {}
/// @returns the whole number part of the position.
int as_int() const { return position_ >> 2; }
constexpr int as_int() const { return position_ >> 2; }
/// @returns n where n/2 is the head position.
int as_half() const { return position_ >> 1; }
constexpr int as_half() const { return position_ >> 1; }
/// @returns n where n/4 is the head position.
int as_quarter() const { return position_; }
constexpr int as_quarter() const { return position_; }
/// @returns the head position at maximal but unspecified precision.
int as_largest() const { return as_quarter(); }
constexpr int as_largest() const { return as_quarter(); }
HeadPosition &operator +=(const HeadPosition &rhs) {
position_ += rhs.position_;
return *this;
}
bool operator ==(const HeadPosition &rhs) const {
constexpr bool operator ==(const HeadPosition &rhs) const {
return position_ == rhs.position_;
}
bool operator !=(const HeadPosition &rhs) const {
constexpr bool operator !=(const HeadPosition &rhs) const {
return position_ != rhs.position_;
}
bool operator <(const HeadPosition &rhs) const {
constexpr bool operator <(const HeadPosition &rhs) const {
return position_ < rhs.position_;
}
bool operator <=(const HeadPosition &rhs) const {
constexpr bool operator <=(const HeadPosition &rhs) const {
return position_ <= rhs.position_;
}
bool operator >(const HeadPosition &rhs) const {
constexpr bool operator >(const HeadPosition &rhs) const {
return position_ > rhs.position_;
}
bool operator >=(const HeadPosition &rhs) const {
constexpr bool operator >=(const HeadPosition &rhs) const {
return position_ >= rhs.position_;
}
@ -79,7 +79,7 @@ class Track {
int head;
HeadPosition position;
bool operator < (const Address &rhs) const {
constexpr bool operator < (const Address &rhs) const {
int largest_position = position.as_largest();
int rhs_largest_position = rhs.position.as_largest();
return std::tie(head, largest_position) < std::tie(rhs.head, rhs_largest_position);

View File

@ -46,11 +46,11 @@ void TimedEventLoop::run_for(const Cycles cycles) {
assert(cycles_until_event_ > 0);
}
Cycles::IntType TimedEventLoop::get_cycles_until_next_event() {
Cycles::IntType TimedEventLoop::get_cycles_until_next_event() const {
return std::max(cycles_until_event_, Cycles::IntType(0));
}
Cycles::IntType TimedEventLoop::get_input_clock_rate() {
Cycles::IntType TimedEventLoop::get_input_clock_rate() const {
return input_clock_rate_;
}

View File

@ -52,12 +52,12 @@ namespace Storage {
/*!
@returns the number of whole cycles remaining until the next event is triggered.
*/
Cycles::IntType get_cycles_until_next_event();
Cycles::IntType get_cycles_until_next_event() const;
/*!
@returns the input clock rate.
*/
Cycles::IntType get_input_clock_rate();
Cycles::IntType get_input_clock_rate() const;
protected:
/*!