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:
parent
a9d4fe0b41
commit
10c0e687f5
@ -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;
|
||||||
|
@ -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];
|
||||||
|
}
|
||||||
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user