From cbf25a16dcf39245827250c7b3a8f6b91db0a461 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 2 Nov 2019 21:25:45 -0400 Subject: [PATCH] Adds relative mouse motion input. --- Machines/AtariST/AtariST.cpp | 16 ++++- Machines/AtariST/IntelligentKeyboard.cpp | 92 +++++++++++++++++++++--- Machines/AtariST/IntelligentKeyboard.hpp | 32 ++++++++- 3 files changed, 127 insertions(+), 13 deletions(-) diff --git a/Machines/AtariST/AtariST.cpp b/Machines/AtariST/AtariST.cpp index 22e559b41..7897b2690 100644 --- a/Machines/AtariST/AtariST.cpp +++ b/Machines/AtariST/AtariST.cpp @@ -9,6 +9,7 @@ #include "AtariST.hpp" #include "../CRTMachine.hpp" +#include "../MouseMachine.hpp" //#define LOG_TRACE #include "../../Processors/68000/68000.hpp" @@ -43,7 +44,8 @@ class ConcreteMachine: public ClockingHint::Observer, public Motorola::ACIA::ACIA::InterruptDelegate, public Motorola::MFP68901::MFP68901::InterruptDelegate, - public DMAController::InterruptDelegate { + public DMAController::InterruptDelegate, + public MouseMachine::Machine { public: ConcreteMachine(const Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : mc68000_(*this), @@ -113,6 +115,11 @@ class ConcreteMachine: } void run_for(const Cycles cycles) final { + // Give the keyboard an opportunity to consume any events. + if(!keyboard_needs_clock_) { + ikbd_.run_for(HalfCycles(0)); + } + mc68000_.run_for(cycles); } @@ -476,7 +483,12 @@ class ConcreteMachine: mc68000_.set_interrupt_level(0); } } -}; + + // MARK: - MouseMachine + Inputs::Mouse &get_mouse() final { + return ikbd_; + } + }; } } diff --git a/Machines/AtariST/IntelligentKeyboard.cpp b/Machines/AtariST/IntelligentKeyboard.cpp index b71a70f5c..fdf9cbaf0 100644 --- a/Machines/AtariST/IntelligentKeyboard.cpp +++ b/Machines/AtariST/IntelligentKeyboard.cpp @@ -13,6 +13,10 @@ using namespace Atari::ST; IntelligentKeyboard::IntelligentKeyboard(Serial::Line &input, Serial::Line &output) : output_line_(output) { input.set_read_delegate(this, Storage::Time(2, 15625)); output_line_.set_writer_clock_rate(15625); + + mouse_button_state_ = 0; + mouse_movement_[0] = 0; + mouse_movement_[1] = 0; } bool IntelligentKeyboard::serial_line_did_produce_bit(Serial::Line *, int bit) { @@ -36,12 +40,34 @@ ClockingHint::Preference IntelligentKeyboard::preferred_clocking() { } void IntelligentKeyboard::run_for(HalfCycles duration) { + // Take this opportunity to check for mouse and keyboard events, + // which will have been received asynchronously. + if(mouse_mode_ == MouseMode::Relative) { + const int captured_movement[2] = { mouse_movement_[0].load(), mouse_movement_[1].load() }; + const int captured_button_state = mouse_button_state_; + if( + (posted_button_state_ != captured_button_state) || + (abs(captured_movement[0]) >= mouse_threshold_[0]) || + (abs(captured_movement[1]) >= mouse_threshold_[1]) ) { + mouse_movement_[0] -= captured_movement[0]; + mouse_movement_[1] -= captured_movement[1]; + + post_relative_mouse_event(captured_movement[0], captured_movement[1]); + } + } else { + + } + output_line_.advance_writer(duration); } -void IntelligentKeyboard::output_byte(uint8_t value) { +void IntelligentKeyboard::output_bytes(std::initializer_list values) { // Wrap the value in a start and stop bit, and send it on its way. - output_line_.write(2, 10, 0x200 | (value << 1)); + for(auto value : values) { + output_line_.write(2, 10, 0x200 | (value << 1)); + } + + // TODO: this isn't thread safe! Might need this class to imply a poll? update_clocking_observer(); } @@ -128,7 +154,7 @@ void IntelligentKeyboard::reset() { // Reset should perform a self test, lasting at most 200ms, then post 0xf0. // Following that it should look for any keys that currently seem to be pressed. // Those are considered stuck and a break code is generated for them. - output_byte(0xf0); + output_bytes({0xf0}); } void IntelligentKeyboard::resume() { @@ -168,13 +194,61 @@ void IntelligentKeyboard::set_mouse_button_actions(uint8_t actions) { } void IntelligentKeyboard::interrogate_mouse_position() { - output_byte(0xf7); // Beginning of mouse response. - output_byte(0x00); // 0000dcba; a = right button down since last interrogation, b = right button up since, c/d = left button. - output_byte(0x00); // x motion: MSB, LSB - output_byte(0x00); - output_byte(0x00); // y motion: MSB, LSB - output_byte(0x00); + output_bytes({ + 0xf7, // Beginning of mouse response. + 0x00, // 0000dcba; a = right button down since last interrogation, b = right button up since, c/d = left button. + 0x00, // x motion: MSB, LSB + 0x00, + 0x00, // y motion: MSB, LSB + 0x00 + }); } +void IntelligentKeyboard::post_relative_mouse_event(int x, int y) { + posted_button_state_ = mouse_button_state_; + + // Break up the motion to impart, if it's too large. + do { + int stepped_motion[2] = { + (x >= -128 && x < 127) ? x : (x > 0 ? 127 : -128), + (y >= -128 && y < 127) ? y : (y > 0 ? 127 : -128), + }; + + output_bytes({ + uint8_t(0xf8 | posted_button_state_), // Command code is a function of button state. + uint8_t(stepped_motion[0]), + uint8_t(stepped_motion[1]), + }); + + x -= stepped_motion[0]; + y -= stepped_motion[1]; + } while(x || y); +} + +// MARK: - Mouse Input + +void IntelligentKeyboard::move(int x, int y) { + mouse_movement_[0] += x; + mouse_movement_[1] += y; +} + +int IntelligentKeyboard::get_number_of_buttons() { + return 2; +} + +void IntelligentKeyboard::set_button_pressed(int index, bool is_pressed) { + const auto mask = 1 << index; + if(is_pressed) { + mouse_button_state_ |= mask; + } else { + mouse_button_state_ &= ~mask; + } +} + +void IntelligentKeyboard::reset_all_buttons() { + mouse_button_state_ = 0; +} + +// MARK: - Joystick Output void IntelligentKeyboard::disable_joysticks() { } diff --git a/Machines/AtariST/IntelligentKeyboard.hpp b/Machines/AtariST/IntelligentKeyboard.hpp index ebb2e50b4..5a1c89e52 100644 --- a/Machines/AtariST/IntelligentKeyboard.hpp +++ b/Machines/AtariST/IntelligentKeyboard.hpp @@ -12,6 +12,10 @@ #include "../../ClockReceiver/ClockingHintSource.hpp" #include "../../Components/SerialPort/SerialPort.hpp" +#include "../../Inputs/Mouse.hpp" + +#include + namespace Atari { namespace ST { @@ -21,7 +25,8 @@ namespace ST { */ class IntelligentKeyboard: public Serial::Line::ReadDelegate, - public ClockingHint::Source { + public ClockingHint::Source, + public Inputs::Mouse { public: IntelligentKeyboard(Serial::Line &input, Serial::Line &output); ClockingHint::Preference preferred_clocking() final; @@ -33,7 +38,7 @@ class IntelligentKeyboard: int command_ = 0; Serial::Line &output_line_; - void output_byte(uint8_t value); + void output_bytes(std::initializer_list value); bool serial_line_did_produce_bit(Serial::Line *, int bit) final; // MARK: - Command dispatch. @@ -58,6 +63,29 @@ class IntelligentKeyboard: void set_mouse_button_actions(uint8_t actions); void interrogate_mouse_position(); + // Inputs::Mouse. + void move(int x, int y) final; + int get_number_of_buttons() final; + void set_button_pressed(int index, bool is_pressed) final; + void reset_all_buttons() final; + + enum class MouseMode { + Relative, Absolute + } mouse_mode_ = MouseMode::Relative; + + // Absolute positioning state. + int mouse_range_[2] = {0, 0}; + int mouse_scale_[2] = {0, 0}; + + // Relative positioning state. + int posted_button_state_ = 0; + int mouse_threshold_[2] = {1, 1}; + void post_relative_mouse_event(int x, int y); + + // Received mouse state. + std::atomic mouse_movement_[2]; + std::atomic mouse_button_state_; + // MARK: - Joystick. void disable_joysticks(); };