1
0
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:
Thomas Harte 2018-05-12 17:45:06 -04:00 committed by GitHub
commit afe222cb16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 363 additions and 48 deletions

51
Activity/Observer.hpp Normal file
View 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
View 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 */

View File

@ -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();

View File

@ -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;

View File

@ -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_;

View File

@ -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();

View File

@ -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);
}

View File

@ -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,

View File

@ -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) {

View File

@ -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);
}
}
};
}

View File

@ -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) {}
};
}

View File

@ -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);
}

View File

@ -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:

View File

@ -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) {

View File

@ -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_;

View File

@ -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());

View File

@ -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;

View File

@ -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;
};
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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;
};
}

View File

@ -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)));
}

View File

@ -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);
}

View File

@ -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);
};
}

View File

@ -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;

View File

@ -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>();
}

View File

@ -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 */,

View File

@ -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_);
}
}
}

View File

@ -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;
};