From a8325b6bcea2bf50f41a156693e679c30425eaa4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 2 Oct 2025 17:10:27 -0400 Subject: [PATCH] Add BBC joysticks. --- Components/uPD7002/uPD7002.cpp | 4 ++ Machines/Acorn/BBCMicro/BBCMicro.cpp | 65 +++++++++++++++++++++++++--- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/Components/uPD7002/uPD7002.cpp b/Components/uPD7002/uPD7002.cpp index a205edfda..2dea77006 100644 --- a/Components/uPD7002/uPD7002.cpp +++ b/Components/uPD7002/uPD7002.cpp @@ -79,3 +79,7 @@ void uPD7002::set_interrupt(const bool value) { interrupt_ = value; if(delegate_) delegate_->did_change_interrupt_status(*this); } + +void uPD7002::set_input(const int channel, const float value) { + inputs_[channel] = value; +} diff --git a/Machines/Acorn/BBCMicro/BBCMicro.cpp b/Machines/Acorn/BBCMicro/BBCMicro.cpp index fb32bded4..cca5ec3eb 100644 --- a/Machines/Acorn/BBCMicro/BBCMicro.cpp +++ b/Machines/Acorn/BBCMicro/BBCMicro.cpp @@ -46,6 +46,37 @@ namespace BBCMicro { namespace { using Logger = Log::Logger; +/*! + Provides an analogue joystick with a single fire button. +*/ +class Joystick: public Inputs::ConcreteJoystick { +public: + Joystick(NEC::uPD7002 &adc, const int first_channel) : + ConcreteJoystick({ + Input(Input::Horizontal), + Input(Input::Vertical), + Input(Input::Fire) + }), + adc_(adc), + first_channel_(first_channel) {} + + void did_set_input(const Input &input, const float value) final { + adc_.set_input(first_channel_ + (input.type == Input::Vertical), value); + } + + void did_set_input(const Input &, const bool is_active) final { + fire_ = is_active; + } + + bool fire() const { + return fire_; + } + +private: + NEC::uPD7002 &adc_; + const int first_channel_; + bool fire_ = false; +}; /*! Combines an SN76489 with an appropriate asynchronous queue and filtering speaker. */ @@ -118,8 +149,14 @@ struct SystemVIAPortHandler; using SystemVIA = MOS::MOS6522::MOS6522; struct SystemVIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler { - SystemVIAPortHandler(Audio &audio, VideoBaseAddress &video_base, SystemVIA &via, const bool run_disk) : - audio_(audio), video_base_(video_base), via_(via) + SystemVIAPortHandler( + Audio &audio, + VideoBaseAddress &video_base, + SystemVIA &via, + const std::vector> &joysticks, + const bool run_disk + ) : + audio_(audio), video_base_(video_base), via_(via), joysticks_(joysticks) { set_key_flag(6, run_disk); } @@ -183,9 +220,12 @@ struct SystemVIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler { if(port == MOS::MOS6522::Port::B) { // TODO: // - // b4/5: joystick fire buttons; - // b6/7: speech interrupt/ready inputs. - return 0x3f; // b6 = b7 = 0 => no speech hardware. + // b4/5: joystick fire buttons (0 = pressed); + // b6/7: speech interrupt/ready inputs. (0 expected if no speech hardware) + return + 0xf | + (static_cast(joysticks_[0].get())->fire() ? 0x00 : 0x10) | + (static_cast(joysticks_[1].get())->fire() ? 0x00 : 0x20); } if(latch_ & LatchFlags::KeyboardIsScanning) { @@ -284,6 +324,8 @@ private: bool caps_led_state_ = false; bool shift_led_state_ = false; Activity::Observer *activity_observer_ = nullptr; + + const std::vector> &joysticks_; }; /*! @@ -571,6 +613,7 @@ class ConcreteMachine: public Activity::Source, public Machine, public MachineTypes::AudioProducer, + public MachineTypes::JoystickMachine, public MachineTypes::MappedKeyboardMachine, public MachineTypes::MediaTarget, public MachineTypes::ScanProducer, @@ -586,7 +629,7 @@ public: const ROMMachine::ROMFetcher &rom_fetcher ) : m6502_(*this), - system_via_port_handler_(audio_, crtc_bus_handler_, system_via_, target.should_shift_restart), + system_via_port_handler_(audio_, crtc_bus_handler_, system_via_, joysticks_, target.should_shift_restart), user_via_(user_via_port_handler_), system_via_(system_via_port_handler_), crtc_bus_handler_(ram_.data(), system_via_), @@ -596,6 +639,10 @@ public: { set_clock_rate(2'000'000); + // Install two joysticks. + joysticks_.emplace_back(new Joystick(adc_, 0)); + joysticks_.emplace_back(new Joystick(adc_, 2)); + system_via_port_handler_.set_interrupt_delegate(this); user_via_port_handler_.set_interrupt_delegate(this); adc_.set_delegate(this); @@ -1000,6 +1047,12 @@ private: void wd1770_did_change_output(WD::WD1770 &) override { m6502_.set_nmi_line(wd1770_.get_interrupt_request_line() || wd1770_.get_data_request_line()); } + + // MARK: - Joysticks + std::vector> joysticks_; + const std::vector> &get_joysticks() override { + return joysticks_; + } }; }