1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-16 19:08:08 +00:00

Attempts to introduce sleeping for the Disk II.

This commit is contained in:
Thomas Harte 2018-04-29 17:51:10 -04:00
parent a9d4fe0b41
commit 10c0e687f5
8 changed files with 82 additions and 41 deletions

View File

@ -30,7 +30,7 @@ class Sleeper {
class SleepObserver { class SleepObserver {
public: public:
/// Called to inform an observer that the component @c component has either gone to sleep or become awake. /// Called to inform an observer that the component @c component has either gone to sleep or become awake.
virtual void set_component_is_sleeping(void *component, bool is_sleeping) = 0; virtual void set_component_is_sleeping(Sleeper *component, bool is_sleeping) = 0;
}; };
/// Registers @c observer as the new sleep observer; /// Registers @c observer as the new sleep observer;

View File

@ -13,14 +13,17 @@
using namespace Apple; using namespace Apple;
namespace { namespace {
const uint8_t input_command = 0x1; const uint8_t input_command = 0x1; // i.e. Q6
const uint8_t input_mode = 0x2; const uint8_t input_mode = 0x2; // i.e. Q7
const uint8_t input_flux = 0x4; const uint8_t input_flux = 0x4;
} }
DiskII::DiskII() : DiskII::DiskII() :
drives_{{2000000, 300, 1}, {2045454, 300, 1}} inputs_(input_command),
drives_{{2045454, 300, 1}, {2045454, 300, 1}}
{ {
drives_[0].set_sleep_observer(this);
drives_[1].set_sleep_observer(this);
} }
void DiskII::set_control(Control control, bool on) { void DiskII::set_control(Control control, bool on) {
@ -38,7 +41,7 @@ void DiskII::set_control(Control control, bool on) {
break; break;
} }
// printf("%0x: Set control %d %s\n", stepper_mask_, control, on ? "on" : "off"); printf("%0x: Set control %d %s\n", stepper_mask_, control, on ? "on" : "off");
// If the stepper magnet selections have changed, and any is on, see how // If the stepper magnet selections have changed, and any is on, see how
// that moves the head. // that moves the head.
@ -62,6 +65,7 @@ void DiskII::set_control(Control control, bool on) {
void DiskII::set_mode(Mode mode) { void DiskII::set_mode(Mode mode) {
// printf("Set mode %d\n", mode); // printf("Set mode %d\n", mode);
inputs_ = (inputs_ & ~input_mode) | ((mode == Mode::Write) ? input_mode : 0); inputs_ = (inputs_ & ~input_mode) | ((mode == Mode::Write) ? input_mode : 0);
set_controller_can_sleep();
} }
void DiskII::select_drive(int drive) { void DiskII::select_drive(int drive) {
@ -75,11 +79,13 @@ void DiskII::set_data_register(uint8_t value) {
// printf("Set data register (?)\n"); // printf("Set data register (?)\n");
inputs_ |= input_command; inputs_ |= input_command;
data_register_ = value; data_register_ = value;
set_controller_can_sleep();
} }
uint8_t DiskII::get_shift_register() { uint8_t DiskII::get_shift_register() {
// if(shift_register_ & 0x80) printf("[%02x] ", shift_register_); // if(shift_register_ & 0x80) printf("[%02x] ", shift_register_);
inputs_ &= ~input_command; inputs_ &= ~input_command;
set_controller_can_sleep();
return shift_register_; return shift_register_;
} }
@ -97,41 +103,56 @@ void DiskII::run_for(const Cycles cycles) {
The bytes in the P6 ROM has the high four bits reversed compared to the BAPD charts, so you will have to reverse them after fetching the byte. The bytes in the P6 ROM has the high four bits reversed compared to the BAPD charts, so you will have to reverse them after fetching the byte.
*/ */
// TODO: optimise the resting state. if(is_sleeping()) return;
int integer_cycles = cycles.as_int(); int integer_cycles = cycles.as_int();
while(integer_cycles--) {
const int address =
(inputs_ << 2) |
((shift_register_&0x80) >> 6) |
((state_&0x2) >> 1) |
((state_&0x1) << 7) |
((state_&0x4) << 4) |
((state_&0x8) << 2);
inputs_ |= input_flux;
const uint8_t update = state_machine_[static_cast<std::size_t>(address)]; if(!controller_can_sleep_) {
state_ = update >> 4; while(integer_cycles--) {
state_ = ((state_ & 0x8) ? 0x1 : 0x0) | ((state_ & 0x4) ? 0x2 : 0x0) | ((state_ & 0x2) ? 0x4 : 0x0) | ((state_ & 0x1) ? 0x8 : 0x0); const int address =
(inputs_ << 2) |
((shift_register_&0x80) >> 6) |
((state_&0x2) >> 1) |
((state_&0x1) << 7) |
((state_&0x4) << 4) |
((state_&0x8) << 2);
inputs_ |= input_flux;
uint8_t command = update & 0xf; const uint8_t update = state_machine_[static_cast<std::size_t>(address)];
switch(command) { state_ = update >> 4;
case 0x0: shift_register_ = 0; break; // clear state_ = ((state_ & 0x8) ? 0x1 : 0x0) | ((state_ & 0x4) ? 0x2 : 0x0) | ((state_ & 0x2) ? 0x4 : 0x0) | ((state_ & 0x1) ? 0x8 : 0x0);
case 0x9: shift_register_ = static_cast<uint8_t>(shift_register_ << 1); break; // shift left, bringing in a zero
case 0xd: shift_register_ = static_cast<uint8_t>((shift_register_ << 1) | 1); break; // shift left, bringing in a one uint8_t command = update & 0xf;
case 0xb: shift_register_ = data_register_; break; // load switch(command) {
case 0xa: case 0x0: shift_register_ = 0; break; // clear
shift_register_ = (shift_register_ >> 1) | (is_write_protected() ? 0x80 : 0x00); case 0x9: shift_register_ = static_cast<uint8_t>(shift_register_ << 1); break; // shift left, bringing in a zero
break; // shift right, bringing in write protected status case 0xd: shift_register_ = static_cast<uint8_t>((shift_register_ << 1) | 1); break; // shift left, bringing in a one
default: break; case 0xb: shift_register_ = data_register_; break; // load
case 0xa:
shift_register_ = (shift_register_ >> 1) | (is_write_protected() ? 0x80 : 0x00);
break; // shift right, bringing in write protected status
default: break;
}
// TODO: surely there's a less heavyweight solution than this?
if(!drive_is_sleeping_[0]) drives_[0].run_for(Cycles(1));
if(!drive_is_sleeping_[1]) drives_[1].run_for(Cycles(1));
} }
} else {
// printf(" -> %02x performing %02x (address was %02x)\n", state_, command, address); if(!drive_is_sleeping_[0]) drives_[0].run_for(cycles);
if(!drive_is_sleeping_[1]) drives_[1].run_for(cycles);
// TODO: surely there's a less heavyweight solution than this?
drives_[0].run_for(Cycles(1));
drives_[1].run_for(Cycles(1));
} }
set_controller_can_sleep();
}
void DiskII::set_controller_can_sleep() {
// Permit the controller to sleep if it's in sense write protect mode, and the shift register
// has already filled with the result of shifting eight times.
controller_can_sleep_ =
(inputs_ == (input_command | input_flux)) &&
(shift_register_ == (is_write_protected() ? 0xff : 0x00));
if(is_sleeping()) update_sleep_observer();
} }
bool DiskII::is_write_protected() { bool DiskII::is_write_protected() {
@ -140,7 +161,6 @@ bool DiskII::is_write_protected() {
void DiskII::set_state_machine(const std::vector<uint8_t> &state_machine) { void DiskII::set_state_machine(const std::vector<uint8_t> &state_machine) {
state_machine_ = state_machine; state_machine_ = state_machine;
// run_for(Cycles(15));
// TODO: shuffle ordering here? // TODO: shuffle ordering here?
} }
@ -151,5 +171,16 @@ void DiskII::set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int driv
void DiskII::process_event(const Storage::Disk::Track::Event &event) { void DiskII::process_event(const Storage::Disk::Track::Event &event) {
if(event.type == Storage::Disk::Track::Event::FluxTransition) { if(event.type == Storage::Disk::Track::Event::FluxTransition) {
inputs_ &= ~input_flux; inputs_ &= ~input_flux;
set_controller_can_sleep();
} }
} }
void DiskII::set_component_is_sleeping(Sleeper *component, bool is_sleeping) {
drive_is_sleeping_[0] = drives_[0].is_sleeping();
drive_is_sleeping_[1] = drives_[1].is_sleeping();
update_sleep_observer();
}
bool DiskII::is_sleeping() {
return controller_can_sleep_ && drive_is_sleeping_[0] && drive_is_sleeping_[1];
}

View File

@ -10,6 +10,7 @@
#define DiskII_hpp #define DiskII_hpp
#include "../../ClockReceiver/ClockReceiver.hpp" #include "../../ClockReceiver/ClockReceiver.hpp"
#include "../../ClockReceiver/Sleeper.hpp"
#include "../../Storage/Disk/Disk.hpp" #include "../../Storage/Disk/Disk.hpp"
#include "../../Storage/Disk/Drive.hpp" #include "../../Storage/Disk/Drive.hpp"
@ -22,7 +23,10 @@ namespace Apple {
/*! /*!
Provides an emulation of the Apple Disk II. Provides an emulation of the Apple Disk II.
*/ */
class DiskII: public Storage::Disk::Drive::EventDelegate { class DiskII:
public Storage::Disk::Drive::EventDelegate,
public Sleeper::SleepObserver,
public Sleeper {
public: public:
DiskII(); DiskII();
@ -43,9 +47,11 @@ class DiskII: public Storage::Disk::Drive::EventDelegate {
void set_state_machine(const std::vector<uint8_t> &); void set_state_machine(const std::vector<uint8_t> &);
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive); void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
bool is_sleeping() override;
private: private:
void process_event(const Storage::Disk::Track::Event &event) override; void process_event(const Storage::Disk::Track::Event &event) override;
void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override;
uint8_t state_ = 0; uint8_t state_ = 0;
uint8_t inputs_ = 0; uint8_t inputs_ = 0;
@ -58,7 +64,11 @@ class DiskII: public Storage::Disk::Drive::EventDelegate {
bool is_write_protected(); bool is_write_protected();
std::vector<uint8_t> state_machine_; std::vector<uint8_t> state_machine_;
Storage::Disk::Drive drives_[2]; Storage::Disk::Drive drives_[2];
bool drive_is_sleeping_[2];
bool controller_can_sleep_ = false;
int active_drive_ = 0; int active_drive_ = 0;
void set_controller_can_sleep();
}; };
} }

View File

@ -961,7 +961,7 @@ class ConcreteMachine:
return true; return true;
} }
void set_component_is_sleeping(void *component, bool is_sleeping) override final { void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override final {
fdc_is_sleeping_ = fdc_.is_sleeping(); fdc_is_sleeping_ = fdc_.is_sleeping();
tape_player_is_sleeping_ = tape_player_.is_sleeping(); tape_player_is_sleeping_ = tape_player_.is_sleeping();
} }

View File

@ -747,7 +747,7 @@ class ConcreteMachine:
return selection_set; return selection_set;
} }
void set_component_is_sleeping(void *component, bool is_sleeping) override { void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override {
tape_is_sleeping_ = is_sleeping; tape_is_sleeping_ = is_sleeping;
set_use_fast_tape(); set_use_fast_tape();
} }

View File

@ -551,7 +551,7 @@ class ConcreteMachine:
} }
// MARK: - Sleeper // MARK: - Sleeper
void set_component_is_sleeping(void *component, bool is_sleeping) override { void set_component_is_sleeping(Sleeper *component, bool is_sleeping) override {
tape_player_is_sleeping_ = tape_player_.is_sleeping(); tape_player_is_sleeping_ = tape_player_.is_sleeping();
set_use_fast_tape(); set_use_fast_tape();
} }

View File

@ -22,7 +22,7 @@ Controller::Controller(Cycles clock_rate) :
set_drive(empty_drive_); set_drive(empty_drive_);
} }
void Controller::set_component_is_sleeping(void *component, bool is_sleeping) { void Controller::set_component_is_sleeping(Sleeper *component, bool is_sleeping) {
update_sleep_observer(); update_sleep_observer();
} }

View File

@ -113,7 +113,7 @@ class Controller: public DigitalPhaseLockedLoop::Delegate, public Drive::EventDe
std::shared_ptr<Drive> empty_drive_; std::shared_ptr<Drive> empty_drive_;
void set_component_is_sleeping(void *component, bool is_sleeping); void set_component_is_sleeping(Sleeper *component, bool is_sleeping);
// for Drive::EventDelegate // for Drive::EventDelegate
void process_event(const Track::Event &event); void process_event(const Track::Event &event);