mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-23 03:32:32 +00:00
Merge pull request #433 from TomHarte/ActivityReceiver
Introduces infrastructure for sending and receiving 'activity' notifications
This commit is contained in:
commit
afe222cb16
51
Activity/Observer.hpp
Normal file
51
Activity/Observer.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
//
|
||||
// ActivityObserver.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 07/05/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ActivityObserver_h
|
||||
#define ActivityObserver_h
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Activity {
|
||||
|
||||
/*!
|
||||
Provides a purely virtual base class for anybody that wants to receive notifications of
|
||||
'activity' — any feedback from an emulated system which a user could perceive other than
|
||||
through the machine's native audio and video outputs.
|
||||
|
||||
So: status LEDs, drive activity, etc. A receiver may choose to make appropriate noises
|
||||
and/or to show or unshow status indicators.
|
||||
*/
|
||||
class Observer {
|
||||
public:
|
||||
/// Announces to the receiver that there is an LED of name @c name.
|
||||
virtual void register_led(const std::string &name) = 0;
|
||||
|
||||
/// Announces to the receiver that there is a drive of name @c name.
|
||||
virtual void register_drive(const std::string &name) = 0;
|
||||
|
||||
/// Informs the receiver of the new state of the LED with name @c name.
|
||||
virtual void set_led_status(const std::string &name, bool lit) = 0;
|
||||
|
||||
enum class DriveEvent {
|
||||
StepNormal,
|
||||
StepBelowZero,
|
||||
StepBeyondMaximum
|
||||
};
|
||||
|
||||
/// Informs the receiver that the named event just occurred for the drive with name @c name.
|
||||
virtual void announce_drive_event(const std::string &name, DriveEvent event) = 0;
|
||||
|
||||
/// Informs the receiver of the motor-on status of the drive with name @c name.
|
||||
virtual void set_drive_motor_status(const std::string &name, bool is_on) = 0;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* ActivityObserver_h */
|
24
Activity/Source.hpp
Normal file
24
Activity/Source.hpp
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// ActivitySource.h
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 07/05/2018.
|
||||
// Copyright © 2018 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef ActivitySource_h
|
||||
#define ActivitySource_h
|
||||
|
||||
#include "Observer.hpp"
|
||||
|
||||
namespace Activity {
|
||||
|
||||
class Source {
|
||||
public:
|
||||
virtual void set_activity_observer(Observer *observer) = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif /* ActivitySource_h */
|
@ -22,6 +22,10 @@ MultiMachine::MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machin
|
||||
crt_machine_.set_delegate(this);
|
||||
}
|
||||
|
||||
Activity::Source *MultiMachine::activity_source() {
|
||||
return nullptr; // TODO
|
||||
}
|
||||
|
||||
ConfigurationTarget::Machine *MultiMachine::configuration_target() {
|
||||
if(has_picked_) {
|
||||
return machines_.front()->configuration_target();
|
||||
|
@ -50,6 +50,7 @@ class MultiMachine: public ::Machine::DynamicMachine, public MultiCRTMachine::De
|
||||
static bool would_collapse(const std::vector<std::unique_ptr<DynamicMachine>> &machines);
|
||||
MultiMachine(std::vector<std::unique_ptr<DynamicMachine>> &&machines);
|
||||
|
||||
Activity::Source *activity_source() override;
|
||||
ConfigurationTarget::Machine *configuration_target() override;
|
||||
CRTMachine::Machine *crt_machine() override;
|
||||
JoystickMachine::Machine *joystick_machine() override;
|
||||
|
@ -113,6 +113,9 @@ template <class T> class MOS6522: public MOS6522Base {
|
||||
/*! Gets a register value. */
|
||||
uint8_t get_register(int address);
|
||||
|
||||
/*! @returns the bus handler. */
|
||||
T &bus_handler();
|
||||
|
||||
private:
|
||||
T &bus_handler_;
|
||||
|
||||
|
@ -145,6 +145,10 @@ template <typename T> uint8_t MOS6522<T>::get_port_input(Port port, uint8_t outp
|
||||
return (input & ~output_mask) | (output & output_mask);
|
||||
}
|
||||
|
||||
template <typename T> T &MOS6522<T>::bus_handler() {
|
||||
return bus_handler_;
|
||||
}
|
||||
|
||||
// Delegate and communications
|
||||
template <typename T> void MOS6522<T>::reevaluate_interrupts() {
|
||||
bool new_interrupt_status = get_interrupt_line();
|
||||
|
@ -236,3 +236,7 @@ uint8_t DiskII::trigger_address(int address, uint8_t value) {
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
void DiskII::set_activity_observer(Activity::Observer *observer) {
|
||||
drives_[0].set_activity_observer(observer, "Drive 1", true);
|
||||
drives_[1].set_activity_observer(observer, "Drive 2", true);
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include "../../Storage/Disk/Disk.hpp"
|
||||
#include "../../Storage/Disk/Drive.hpp"
|
||||
|
||||
#include "../../Activity/Observer.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
@ -40,6 +42,8 @@ class DiskII:
|
||||
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
||||
bool is_sleeping() override;
|
||||
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
enum class Control {
|
||||
P0, P1, P2, P3,
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "../Utility/MemoryFuzzer.hpp"
|
||||
#include "../Utility/Typer.hpp"
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
@ -607,6 +608,10 @@ class FDC: public Intel::i8272::i8272 {
|
||||
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
|
||||
drive_->set_disk(disk);
|
||||
}
|
||||
|
||||
void set_activity_observer(Activity::Observer *observer) {
|
||||
drive_->set_activity_observer(observer, "Drive 1", true);
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -690,7 +695,8 @@ class ConcreteMachine:
|
||||
public Utility::TypeRecipient,
|
||||
public CPU::Z80::BusHandler,
|
||||
public Sleeper::SleepObserver,
|
||||
public Machine {
|
||||
public Machine,
|
||||
public Activity::Source {
|
||||
public:
|
||||
ConcreteMachine() :
|
||||
z80_(*this),
|
||||
@ -995,6 +1001,12 @@ class ConcreteMachine:
|
||||
return &keyboard_mapper_;
|
||||
}
|
||||
|
||||
// MARK: - Activity Source
|
||||
void set_activity_observer(Activity::Observer *observer) override {
|
||||
if(has_fdc_) fdc_.set_activity_observer(observer);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
inline void write_to_gate_array(uint8_t value) {
|
||||
switch(value >> 6) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "AppleII.hpp"
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
@ -24,6 +25,7 @@
|
||||
|
||||
#include "../../Analyser/Static/AppleII/Target.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
namespace {
|
||||
@ -34,7 +36,8 @@ class ConcreteMachine:
|
||||
public KeyboardMachine::Machine,
|
||||
public CPU::MOS6502::BusHandler,
|
||||
public Inputs::Keyboard,
|
||||
public AppleII::Machine {
|
||||
public AppleII::Machine,
|
||||
public Activity::Source {
|
||||
private:
|
||||
struct VideoBusHandler : public AppleII::Video::BusHandler {
|
||||
public:
|
||||
@ -62,8 +65,8 @@ class ConcreteMachine:
|
||||
speaker_.run_for(audio_queue_, cycles_since_audio_update_.divide(Cycles(audio_divider)));
|
||||
}
|
||||
void update_cards() {
|
||||
for(int c = 0; c < 7; ++c) {
|
||||
if(cards_[c]) cards_[c]->run_for(cycles_since_card_update_, stretched_cycles_since_card_update_);
|
||||
for(const auto &card : cards_) {
|
||||
if(card) card->run_for(cycles_since_card_update_, stretched_cycles_since_card_update_);
|
||||
}
|
||||
cycles_since_card_update_ = 0;
|
||||
stretched_cycles_since_card_update_ = 0;
|
||||
@ -80,7 +83,7 @@ class ConcreteMachine:
|
||||
Cycles cycles_since_audio_update_;
|
||||
|
||||
ROMMachine::ROMFetcher rom_fetcher_;
|
||||
std::unique_ptr<AppleII::Card> cards_[7];
|
||||
std::array<std::unique_ptr<AppleII::Card>, 7> cards_;
|
||||
Cycles cycles_since_card_update_;
|
||||
int stretched_cycles_since_card_update_ = 0;
|
||||
|
||||
@ -254,7 +257,7 @@ class ConcreteMachine:
|
||||
Decode the area conventionally used by cards for ROMs:
|
||||
0xCn00 — 0xCnff: card n.
|
||||
*/
|
||||
const int card_number = (address - 0xc100) >> 8;
|
||||
const size_t card_number = (address - 0xc100) >> 8;
|
||||
if(cards_[card_number]) {
|
||||
update_cards();
|
||||
cards_[card_number]->perform_bus_operation(operation, address & 0xff, value);
|
||||
@ -264,7 +267,7 @@ class ConcreteMachine:
|
||||
Decode the area conventionally used by cards for registers:
|
||||
C0n0--C0nF: card n - 8.
|
||||
*/
|
||||
const int card_number = (address - 0xc090) >> 4;
|
||||
const size_t card_number = (address - 0xc090) >> 4;
|
||||
if(cards_[card_number]) {
|
||||
update_cards();
|
||||
cards_[card_number]->perform_bus_operation(operation, 0x100 | (address&0xf), value);
|
||||
@ -372,6 +375,13 @@ class ConcreteMachine:
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// MARK: Activity::Source
|
||||
void set_activity_observer(Activity::Observer *observer) override {
|
||||
for(const auto &card: cards_) {
|
||||
if(card) card->set_activity_observer(observer);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "../../Processors/6502/6502.hpp"
|
||||
#include "../../ClockReceiver/ClockReceiver.hpp"
|
||||
#include "../../Activity/Observer.hpp"
|
||||
|
||||
namespace AppleII {
|
||||
|
||||
@ -21,6 +22,9 @@ class Card {
|
||||
|
||||
/*! Performs a bus operation; the card is implicitly selected. */
|
||||
virtual void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) = 0;
|
||||
|
||||
/*! Supplies a target for observers. */
|
||||
virtual void set_activity_observer(Activity::Observer *observer) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -40,3 +40,7 @@ void DiskIICard::run_for(Cycles cycles, int stretches) {
|
||||
void DiskIICard::set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive) {
|
||||
diskii_.set_disk(disk, drive);
|
||||
}
|
||||
|
||||
void DiskIICard::set_activity_observer(Activity::Observer *observer) {
|
||||
diskii_.set_activity_observer(observer);
|
||||
}
|
||||
|
@ -24,8 +24,11 @@ namespace AppleII {
|
||||
class DiskIICard: public Card {
|
||||
public:
|
||||
DiskIICard(const ROMMachine::ROMFetcher &rom_fetcher, bool is_16_sector);
|
||||
|
||||
void perform_bus_operation(CPU::MOS6502::BusOperation operation, uint16_t address, uint8_t *value) override;
|
||||
void run_for(Cycles cycles, int stretches) override;
|
||||
void set_activity_observer(Activity::Observer *observer) override;
|
||||
|
||||
void set_disk(const std::shared_ptr<Storage::Disk::Disk> &disk, int drive);
|
||||
|
||||
private:
|
||||
|
@ -108,6 +108,11 @@ void Machine::run_for(const Cycles cycles) {
|
||||
Storage::Disk::Controller::run_for(cycles);
|
||||
}
|
||||
|
||||
void MachineBase::set_activity_observer(Activity::Observer *observer) {
|
||||
drive_VIA_.bus_handler().set_activity_observer(observer);
|
||||
drive_->set_activity_observer(observer, "Drive", false);
|
||||
}
|
||||
|
||||
// MARK: - 6522 delegate
|
||||
|
||||
void MachineBase::mos6522_did_change_interrupt_status(void *mos6522) {
|
||||
@ -209,8 +214,6 @@ void DriveVIA::set_delegate(Delegate *delegate) {
|
||||
}
|
||||
|
||||
// write protect tab uncovered
|
||||
DriveVIA::DriveVIA() : port_b_(0xff), port_a_(0xff), delegate_(nullptr) {}
|
||||
|
||||
uint8_t DriveVIA::get_port_input(MOS::MOS6522::Port port) {
|
||||
return port ? port_b_ : port_a_;
|
||||
}
|
||||
@ -255,14 +258,22 @@ void DriveVIA::set_port_output(MOS::MOS6522::Port port, uint8_t value, uint8_t d
|
||||
delegate_->drive_via_did_set_data_density(this, (value >> 5)&3);
|
||||
}
|
||||
|
||||
// TODO: something with the drive LED
|
||||
// printf("LED: %s\n", value&8 ? "On" : "Off");
|
||||
// post the LED status
|
||||
if(observer_) observer_->set_led_status("Drive", !!(value&8));
|
||||
|
||||
previous_port_b_output_ = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DriveVIA::set_activity_observer(Activity::Observer *observer) {
|
||||
observer_ = observer;
|
||||
if(observer) {
|
||||
observer->register_led("Drive");
|
||||
observer->set_led_status("Drive", !!(previous_port_b_output_&8));
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SerialPort
|
||||
|
||||
void SerialPort::set_input(::Commodore::Serial::Line line, ::Commodore::Serial::LineLevel level) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "../../SerialBus.hpp"
|
||||
|
||||
#include "../../../../Activity/Source.hpp"
|
||||
#include "../../../../Storage/Disk/Disk.hpp"
|
||||
|
||||
#include "../../../../Storage/Disk/Controller/DiskController.hpp"
|
||||
@ -83,8 +84,6 @@ class DriveVIA: public MOS::MOS6522::IRQDelegatePortHandler {
|
||||
};
|
||||
void set_delegate(Delegate *);
|
||||
|
||||
DriveVIA();
|
||||
|
||||
uint8_t get_port_input(MOS::MOS6522::Port port);
|
||||
|
||||
void set_sync_detected(bool);
|
||||
@ -96,12 +95,15 @@ class DriveVIA: public MOS::MOS6522::IRQDelegatePortHandler {
|
||||
|
||||
void set_port_output(MOS::MOS6522::Port, uint8_t value, uint8_t direction_mask);
|
||||
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
uint8_t port_b_, port_a_;
|
||||
bool should_set_overflow_;
|
||||
bool drive_motor_;
|
||||
uint8_t previous_port_b_output_;
|
||||
Delegate *delegate_;
|
||||
uint8_t port_b_ = 0xff, port_a_ = 0xff;
|
||||
bool should_set_overflow_ = false;
|
||||
bool drive_motor_ = false;
|
||||
uint8_t previous_port_b_output_ = 0;
|
||||
Delegate *delegate_ = nullptr;
|
||||
Activity::Observer *observer_ = nullptr;
|
||||
};
|
||||
|
||||
/*!
|
||||
@ -135,6 +137,9 @@ class MachineBase:
|
||||
void drive_via_did_step_head(void *driveVIA, int direction);
|
||||
void drive_via_did_set_data_density(void *driveVIA, int density);
|
||||
|
||||
/// Attaches the activity observer to this C1540.
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
protected:
|
||||
CPU::MOS6502::Processor<MachineBase, false> m6502_;
|
||||
std::shared_ptr<Storage::Disk::Drive> drive_;
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "Keyboard.hpp"
|
||||
|
||||
#include "../../../Activity/Source.hpp"
|
||||
#include "../../ConfigurationTarget.hpp"
|
||||
#include "../../CRTMachine.hpp"
|
||||
#include "../../KeyboardMachine.hpp"
|
||||
@ -303,7 +304,8 @@ class ConcreteMachine:
|
||||
public Utility::TypeRecipient,
|
||||
public Storage::Tape::BinaryTapePlayer::Delegate,
|
||||
public Machine,
|
||||
public Sleeper::SleepObserver {
|
||||
public Sleeper::SleepObserver,
|
||||
public Activity::Source {
|
||||
public:
|
||||
ConcreteMachine() :
|
||||
m6502_(*this),
|
||||
@ -752,6 +754,11 @@ class ConcreteMachine:
|
||||
set_use_fast_tape();
|
||||
}
|
||||
|
||||
// MARK: - Activity Source
|
||||
void set_activity_observer(Activity::Observer *observer) override {
|
||||
if(c1540_) c1540_->set_activity_observer(observer);
|
||||
}
|
||||
|
||||
private:
|
||||
void update_video() {
|
||||
mos6560_->run_for(cycles_since_mos6560_update_.flush());
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define DynamicMachine_h
|
||||
|
||||
#include "../Configurable/Configurable.hpp"
|
||||
#include "../Activity/Source.hpp"
|
||||
#include "ConfigurationTarget.hpp"
|
||||
#include "CRTMachine.hpp"
|
||||
#include "JoystickMachine.hpp"
|
||||
@ -24,6 +25,8 @@ namespace Machine {
|
||||
*/
|
||||
struct DynamicMachine {
|
||||
virtual ~DynamicMachine() {}
|
||||
|
||||
virtual Activity::Source *activity_source() = 0;
|
||||
virtual ConfigurationTarget::Machine *configuration_target() = 0;
|
||||
virtual CRTMachine::Machine *crt_machine() = 0;
|
||||
virtual JoystickMachine::Machine *joystick_machine() = 0;
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "Electron.hpp"
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
@ -45,7 +46,8 @@ class ConcreteMachine:
|
||||
public Configurable::Device,
|
||||
public CPU::MOS6502::BusHandler,
|
||||
public Tape::Delegate,
|
||||
public Utility::TypeRecipient {
|
||||
public Utility::TypeRecipient,
|
||||
public Activity::Source {
|
||||
public:
|
||||
ConcreteMachine() :
|
||||
m6502_(*this),
|
||||
@ -215,12 +217,15 @@ class ConcreteMachine:
|
||||
|
||||
tape_.set_is_enabled((*value & 6) != 6);
|
||||
tape_.set_is_in_input_mode((*value & 6) == 0);
|
||||
tape_.set_is_running(((*value)&0x40) ? true : false);
|
||||
tape_.set_is_running((*value & 0x40) ? true : false);
|
||||
|
||||
// TODO: caps lock LED
|
||||
caps_led_state_ = !!(*value & 0x80);
|
||||
if(activity_observer_)
|
||||
activity_observer_->set_led_status(caps_led, caps_led_state_);
|
||||
}
|
||||
|
||||
// deliberate fallthrough
|
||||
// deliberate fallthrough; fe07 contains the display mode.
|
||||
|
||||
case 0xfe02: case 0xfe03:
|
||||
case 0xfe08: case 0xfe09: case 0xfe0a: case 0xfe0b:
|
||||
case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f:
|
||||
@ -322,8 +327,6 @@ class ConcreteMachine:
|
||||
if(!ram_[0x247] && service_call == 14) {
|
||||
tape_.set_delegate(nullptr);
|
||||
|
||||
// TODO: handle tape wrap around.
|
||||
|
||||
int cycles_left_while_plausibly_in_data = 50;
|
||||
tape_.clear_interrupts(Interrupt::ReceiveDataFull);
|
||||
while(!tape_.get_tape()->is_at_end()) {
|
||||
@ -477,6 +480,14 @@ class ConcreteMachine:
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
void set_activity_observer(Activity::Observer *observer) override {
|
||||
activity_observer_ = observer;
|
||||
if(activity_observer_) {
|
||||
activity_observer_->register_led(caps_led);
|
||||
activity_observer_->set_led_status(caps_led, caps_led_state_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// MARK: - Work deferral updates.
|
||||
inline void update_display() {
|
||||
@ -563,6 +574,11 @@ class ConcreteMachine:
|
||||
Outputs::Speaker::LowpassSpeaker<SoundGenerator> speaker_;
|
||||
|
||||
bool speaker_is_enabled_ = false;
|
||||
|
||||
// MARK: - Caps Lock status and the activity observer.
|
||||
const std::string caps_led = "CAPS";
|
||||
bool caps_led_state_ = false;
|
||||
Activity::Observer *activity_observer_ = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -53,3 +53,8 @@ void Plus3::set_motor_on(bool on) {
|
||||
// writing state, so plenty of work to do in general here.
|
||||
get_drive().set_motor_on(on);
|
||||
}
|
||||
|
||||
void Plus3::set_activity_observer(Activity::Observer *observer) {
|
||||
drives_[0]->set_activity_observer(observer, "Drive 1", true);
|
||||
drives_[1]->set_activity_observer(observer, "Drive 2", true);
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define Plus3_hpp
|
||||
|
||||
#include "../../Components/1770/1770.hpp"
|
||||
#include "../../Activity/Observer.hpp"
|
||||
|
||||
namespace Electron {
|
||||
|
||||
@ -19,6 +20,7 @@ class Plus3 : public WD::WD1770 {
|
||||
|
||||
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive);
|
||||
void set_control_register(uint8_t control);
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
void set_control_register(uint8_t control, uint8_t changes);
|
||||
|
@ -56,11 +56,12 @@ void DiskROM::run_for(HalfCycles half_cycles) {
|
||||
controller_cycles_ %= 715909;
|
||||
}
|
||||
|
||||
void DiskROM::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
|
||||
void DiskROM::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
|
||||
if(!drives_[drive]) {
|
||||
drives_[drive].reset(new Storage::Disk::Drive(8000000, 300, 2));
|
||||
drives_[drive]->set_head(selected_head_);
|
||||
if(drive == selected_drive_) set_drive(drives_[drive]);
|
||||
drives_[drive]->set_activity_observer(observer_, drive_name(drive), true);
|
||||
}
|
||||
drives_[drive]->set_disk(disk);
|
||||
}
|
||||
@ -69,3 +70,16 @@ void DiskROM::set_head_load_request(bool head_load) {
|
||||
// Magic!
|
||||
set_head_loaded(head_load);
|
||||
}
|
||||
|
||||
void DiskROM::set_activity_observer(Activity::Observer *observer) {
|
||||
size_t c = 0;
|
||||
observer_ = observer;
|
||||
for(auto &drive: drives_) {
|
||||
if(drive) drive->set_activity_observer(observer, drive_name(c), true);
|
||||
++c;
|
||||
}
|
||||
}
|
||||
|
||||
std::string DiskROM::drive_name(size_t index) {
|
||||
return "Drive " + std::to_string(index);
|
||||
}
|
||||
|
@ -11,9 +11,12 @@
|
||||
|
||||
#include "ROMSlotHandler.hpp"
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../../Components/1770/1770.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace MSX {
|
||||
@ -26,17 +29,20 @@ class DiskROM: public ROMSlotHandler, public WD::WD1770 {
|
||||
uint8_t read(uint16_t address) override;
|
||||
void run_for(HalfCycles half_cycles) override;
|
||||
|
||||
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive);
|
||||
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive);
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
const std::vector<uint8_t> &rom_;
|
||||
|
||||
long int controller_cycles_ = 0;
|
||||
int selected_drive_ = 0;
|
||||
size_t selected_drive_ = 0;
|
||||
int selected_head_ = 0;
|
||||
std::shared_ptr<Storage::Disk::Drive> drives_[4];
|
||||
std::array<std::shared_ptr<Storage::Disk::Drive>, 2> drives_;
|
||||
|
||||
void set_head_load_request(bool head_load) override;
|
||||
std::string drive_name(size_t index);
|
||||
Activity::Observer *observer_ = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "../../Storage/Tape/Parsers/MSX.hpp"
|
||||
#include "../../Storage/Tape/Tape.hpp"
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
@ -87,7 +88,8 @@ class ConcreteMachine:
|
||||
public KeyboardMachine::Machine,
|
||||
public Configurable::Device,
|
||||
public MemoryMap,
|
||||
public Sleeper::SleepObserver {
|
||||
public Sleeper::SleepObserver,
|
||||
public Activity::Source {
|
||||
public:
|
||||
ConcreteMachine():
|
||||
z80_(*this),
|
||||
@ -200,12 +202,14 @@ class ConcreteMachine:
|
||||
}
|
||||
|
||||
if(!media.disks.empty()) {
|
||||
DiskROM *disk_rom = dynamic_cast<DiskROM *>(memory_slots_[2].handler.get());
|
||||
int drive = 0;
|
||||
for(auto &disk : media.disks) {
|
||||
disk_rom->set_disk(disk, drive);
|
||||
drive++;
|
||||
if(drive == 2) break;
|
||||
DiskROM *disk_rom = get_disk_rom();
|
||||
if(disk_rom) {
|
||||
size_t drive = 0;
|
||||
for(auto &disk : media.disks) {
|
||||
disk_rom->set_disk(disk, drive);
|
||||
drive++;
|
||||
if(drive == 2) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -556,7 +560,18 @@ class ConcreteMachine:
|
||||
set_use_fast_tape();
|
||||
}
|
||||
|
||||
// MARK: - Activity::Source
|
||||
void set_activity_observer(Activity::Observer *observer) override {
|
||||
DiskROM *disk_rom = get_disk_rom();
|
||||
if(disk_rom) {
|
||||
disk_rom->set_activity_observer(observer);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
DiskROM *get_disk_rom() {
|
||||
return dynamic_cast<DiskROM *>(memory_slots_[2].handler.get());
|
||||
}
|
||||
void update_audio() {
|
||||
speaker_.run_for(audio_queue_, time_since_ay_update_.divide_cycles(Cycles(2)));
|
||||
}
|
||||
|
@ -22,10 +22,11 @@ Microdisc::Microdisc() : WD1770(P1793) {
|
||||
set_control_register(last_control_, 0xff);
|
||||
}
|
||||
|
||||
void Microdisc::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive) {
|
||||
void Microdisc::set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive) {
|
||||
if(!drives_[drive]) {
|
||||
drives_[drive].reset(new Storage::Disk::Drive(8000000, 300, 2));
|
||||
if(drive == selected_drive_) set_drive(drives_[drive]);
|
||||
drives_[drive]->set_activity_observer(observer_, drive_name(drive), false);
|
||||
}
|
||||
drives_[drive]->set_disk(disk);
|
||||
}
|
||||
@ -48,8 +49,8 @@ void Microdisc::set_control_register(uint8_t control, uint8_t changes) {
|
||||
// b4: side select
|
||||
if(changes & 0x10) {
|
||||
int head = (control & 0x10) ? 1 : 0;
|
||||
for(int c = 0; c < 4; c++) {
|
||||
if(drives_[c]) drives_[c]->set_head(head);
|
||||
for(auto &drive : drives_) {
|
||||
if(drive) drive->set_head(head);
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,10 +90,12 @@ uint8_t Microdisc::get_data_request_register() {
|
||||
}
|
||||
|
||||
void Microdisc::set_head_load_request(bool head_load) {
|
||||
head_load_request_ = head_load;
|
||||
|
||||
// The drive motors (at present: I believe **all drive motors** regardless of the selected drive) receive
|
||||
// the current head load request state.
|
||||
for(int c = 0; c < 4; c++) {
|
||||
if(drives_[c]) drives_[c]->set_motor_on(head_load);
|
||||
for(auto &drive : drives_) {
|
||||
if(drive) drive->set_motor_on(head_load);
|
||||
}
|
||||
|
||||
// A request to load the head results in a delay until the head is confirmed loaded. This delay is handled
|
||||
@ -103,6 +106,10 @@ void Microdisc::set_head_load_request(bool head_load) {
|
||||
head_load_request_counter_ = head_load_request_counter_target;
|
||||
set_head_loaded(head_load);
|
||||
}
|
||||
|
||||
if(observer_) {
|
||||
observer_->set_led_status("Microdisc", head_load);
|
||||
}
|
||||
}
|
||||
|
||||
void Microdisc::run_for(const Cycles cycles) {
|
||||
@ -116,3 +123,20 @@ void Microdisc::run_for(const Cycles cycles) {
|
||||
bool Microdisc::get_drive_is_ready() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Microdisc::set_activity_observer(Activity::Observer *observer) {
|
||||
observer_ = observer;
|
||||
if(observer) {
|
||||
observer->register_led("Microdisc");
|
||||
observer_->set_led_status("Microdisc", head_load_request_);
|
||||
}
|
||||
size_t c = 0;
|
||||
for(auto &drive : drives_) {
|
||||
if(drive) drive->set_activity_observer(observer, drive_name(c), false);
|
||||
++c;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Microdisc::drive_name(size_t index) {
|
||||
return "Drive " + std::to_string(index);
|
||||
}
|
||||
|
@ -10,6 +10,9 @@
|
||||
#define Microdisc_hpp
|
||||
|
||||
#include "../../Components/1770/1770.hpp"
|
||||
#include "../../Activity/Observer.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Oric {
|
||||
|
||||
@ -17,7 +20,7 @@ class Microdisc: public WD::WD1770 {
|
||||
public:
|
||||
Microdisc();
|
||||
|
||||
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, int drive);
|
||||
void set_disk(std::shared_ptr<Storage::Disk::Disk> disk, size_t drive);
|
||||
void set_control_register(uint8_t control);
|
||||
uint8_t get_interrupt_request_register();
|
||||
uint8_t get_data_request_register();
|
||||
@ -39,17 +42,23 @@ class Microdisc: public WD::WD1770 {
|
||||
inline void set_delegate(Delegate *delegate) { delegate_ = delegate; WD1770::set_delegate(delegate); }
|
||||
inline int get_paging_flags() { return paging_flags_; }
|
||||
|
||||
void set_activity_observer(Activity::Observer *observer);
|
||||
|
||||
private:
|
||||
void set_control_register(uint8_t control, uint8_t changes);
|
||||
void set_head_load_request(bool head_load);
|
||||
bool get_drive_is_ready();
|
||||
std::shared_ptr<Storage::Disk::Drive> drives_[4];
|
||||
int selected_drive_;
|
||||
std::array<std::shared_ptr<Storage::Disk::Drive>, 4> drives_;
|
||||
size_t selected_drive_;
|
||||
bool irq_enable_ = false;
|
||||
int paging_flags_ = BASICDisable;
|
||||
int head_load_request_counter_ = -1;
|
||||
bool head_load_request_ = false;
|
||||
Delegate *delegate_ = nullptr;
|
||||
uint8_t last_control_ = 0;
|
||||
Activity::Observer *observer_ = nullptr;
|
||||
|
||||
std::string drive_name(size_t index);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "Microdisc.hpp"
|
||||
#include "Video.hpp"
|
||||
|
||||
#include "../../Activity/Source.hpp"
|
||||
#include "../ConfigurationTarget.hpp"
|
||||
#include "../CRTMachine.hpp"
|
||||
#include "../KeyboardMachine.hpp"
|
||||
@ -200,6 +201,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
public Utility::TypeRecipient,
|
||||
public Storage::Tape::BinaryTapePlayer::Delegate,
|
||||
public Microdisc::Delegate,
|
||||
public Activity::Source,
|
||||
public Machine {
|
||||
|
||||
public:
|
||||
@ -329,10 +331,10 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
switch(disk_interface) {
|
||||
case Analyser::Static::Oric::Target::DiskInterface::Microdisc: {
|
||||
inserted = true;
|
||||
int drive_index = 0;
|
||||
size_t drive_index = 0;
|
||||
for(auto &disk : media.disks) {
|
||||
if(drive_index < 4) microdisc_.set_disk(disk, drive_index);
|
||||
drive_index++;
|
||||
++drive_index;
|
||||
}
|
||||
} break;
|
||||
case Analyser::Static::Oric::Target::DiskInterface::Pravetz: {
|
||||
@ -340,7 +342,7 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
int drive_index = 0;
|
||||
for(auto &disk : media.disks) {
|
||||
if(drive_index < 2) diskii_.set_disk(disk, drive_index);
|
||||
drive_index++;
|
||||
++drive_index;
|
||||
}
|
||||
} break;
|
||||
|
||||
@ -551,6 +553,18 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
||||
return selection_set;
|
||||
}
|
||||
|
||||
void set_activity_observer(Activity::Observer *observer) override {
|
||||
switch(disk_interface) {
|
||||
default: break;
|
||||
case Analyser::Static::Oric::Target::DiskInterface::Microdisc:
|
||||
microdisc_.set_activity_observer(observer);
|
||||
break;
|
||||
case Analyser::Static::Oric::Target::DiskInterface::Pravetz:
|
||||
diskii_.set_activity_observer(observer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const uint16_t basic_invisible_ram_top_ = 0xffff;
|
||||
const uint16_t basic_visible_ram_top_ = 0xbfff;
|
||||
|
@ -25,6 +25,10 @@ template<typename T> class TypedDynamicMachine: public ::Machine::DynamicMachine
|
||||
return *this;
|
||||
}
|
||||
|
||||
Activity::Source *activity_source() override {
|
||||
return get<Activity::Source>();
|
||||
}
|
||||
|
||||
ConfigurationTarget::Machine *configuration_target() override {
|
||||
return get<ConfigurationTarget::Machine>();
|
||||
}
|
||||
|
@ -863,6 +863,8 @@
|
||||
4B5073051DDD3B9400C48FBD /* ArrayBuilder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ArrayBuilder.cpp; sourceTree = "<group>"; };
|
||||
4B5073061DDD3B9400C48FBD /* ArrayBuilder.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = ArrayBuilder.hpp; sourceTree = "<group>"; };
|
||||
4B5073091DDFCFDF00C48FBD /* ArrayBuilderTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ArrayBuilderTests.mm; sourceTree = "<group>"; };
|
||||
4B51F70920A521D700AFA2C1 /* Source.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Source.hpp; sourceTree = "<group>"; };
|
||||
4B51F70A20A521D700AFA2C1 /* Observer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = Observer.hpp; sourceTree = "<group>"; };
|
||||
4B54C0BB1F8D8E790050900F /* KeyboardMachine.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = KeyboardMachine.cpp; sourceTree = "<group>"; };
|
||||
4B54C0BD1F8D8F450050900F /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = Keyboard.cpp; path = Oric/Keyboard.cpp; sourceTree = "<group>"; };
|
||||
4B54C0BE1F8D8F450050900F /* Keyboard.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = Keyboard.hpp; path = Oric/Keyboard.hpp; sourceTree = "<group>"; };
|
||||
@ -1961,6 +1963,16 @@
|
||||
path = 1540;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B51F70820A521D700AFA2C1 /* Activity */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4B51F70920A521D700AFA2C1 /* Source.hpp */,
|
||||
4B51F70A20A521D700AFA2C1 /* Observer.hpp */,
|
||||
);
|
||||
name = Activity;
|
||||
path = ../../Activity;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4B55CE551C3B7D360093A61B /* Documents */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -2697,6 +2709,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */,
|
||||
4B51F70820A521D700AFA2C1 /* Activity */,
|
||||
4B8944E2201967B4007DE474 /* Analyser */,
|
||||
4BB73EA01B587A5100552FC2 /* Clock Signal */,
|
||||
4BB73EB51B587A5100552FC2 /* Clock SignalTests */,
|
||||
|
@ -50,7 +50,12 @@ bool Drive::get_is_track_zero() {
|
||||
void Drive::step(HeadPosition offset) {
|
||||
HeadPosition old_head_position = head_position_;
|
||||
head_position_ += offset;
|
||||
if(head_position_ < HeadPosition(0)) head_position_ = HeadPosition(0);
|
||||
if(head_position_ < HeadPosition(0)) {
|
||||
head_position_ = HeadPosition(0);
|
||||
if(observer_) observer_->announce_drive_event(drive_name_, Activity::Observer::DriveEvent::StepBelowZero);
|
||||
} else {
|
||||
if(observer_) observer_->announce_drive_event(drive_name_, Activity::Observer::DriveEvent::StepNormal);
|
||||
}
|
||||
|
||||
// If the head moved, flush the old track.
|
||||
if(head_position_ != old_head_position) {
|
||||
@ -88,6 +93,14 @@ bool Drive::get_is_ready() {
|
||||
|
||||
void Drive::set_motor_on(bool motor_is_on) {
|
||||
motor_is_on_ = motor_is_on;
|
||||
|
||||
if(observer_) {
|
||||
observer_->set_drive_motor_status(drive_name_, motor_is_on_);
|
||||
if(announce_motor_led_) {
|
||||
observer_->set_led_status(drive_name_, motor_is_on_);
|
||||
}
|
||||
}
|
||||
|
||||
if(!motor_is_on) {
|
||||
ready_index_count_ = 0;
|
||||
if(disk_) disk_->flush_tracks();
|
||||
@ -265,3 +278,19 @@ void Drive::end_writing() {
|
||||
invalidate_track();
|
||||
}
|
||||
}
|
||||
|
||||
void Drive::set_activity_observer(Activity::Observer *observer, const std::string &name, bool add_motor_led) {
|
||||
observer_ = observer;
|
||||
announce_motor_led_ = add_motor_led;
|
||||
if(observer) {
|
||||
drive_name_ = name;
|
||||
|
||||
observer->register_drive(drive_name_);
|
||||
observer->set_drive_motor_status(drive_name_, motor_is_on_);
|
||||
|
||||
if(add_motor_led) {
|
||||
observer->register_led(drive_name_);
|
||||
observer->set_led_status(drive_name_, motor_is_on_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "Track/PCMPatchedTrack.hpp"
|
||||
|
||||
#include "../TimedEventLoop.hpp"
|
||||
#include "../../Activity/Observer.hpp"
|
||||
#include "../../ClockReceiver/Sleeper.hpp"
|
||||
|
||||
#include <memory>
|
||||
@ -122,6 +123,10 @@ class Drive: public Sleeper, public TimedEventLoop {
|
||||
// As per Sleeper.
|
||||
bool is_sleeping();
|
||||
|
||||
/// Adds an activity observer; it'll be notified of disk activity.
|
||||
/// The caller can specify whether to add an LED based on disk motor.
|
||||
void set_activity_observer(Activity::Observer *observer, const std::string &name, bool add_motor_led);
|
||||
|
||||
private:
|
||||
// Drives contain an entire disk; from that a certain track
|
||||
// will be currently under the head.
|
||||
@ -189,6 +194,11 @@ class Drive: public Sleeper, public TimedEventLoop {
|
||||
|
||||
void setup_track();
|
||||
void invalidate_track();
|
||||
|
||||
// Activity observer description.
|
||||
Activity::Observer *observer_ = nullptr;
|
||||
std::string drive_name_;
|
||||
bool announce_motor_led_ = false;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user