1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-25 01:32:55 +00:00
CLK/Machines/Atari/ST/IntelligentKeyboard.hpp
2024-01-16 23:34:46 -05:00

198 lines
5.8 KiB
C++

//
// IntelligentKeyboard.hpp
// Clock Signal
//
// Created by Thomas Harte on 02/11/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#pragma once
#include "../../../ClockReceiver/ClockingHintSource.hpp"
#include "../../../Components/Serial/Line.hpp"
#include "../../KeyboardMachine.hpp"
#include "../../../Inputs/Joystick.hpp"
#include "../../../Inputs/Mouse.hpp"
#include <atomic>
#include <mutex>
#include <memory>
#include <vector>
namespace Atari::ST {
enum class Key: uint16_t {
Escape = 1,
k1, k2, k3, k4, k5, k6, k7, k8, k9, k0, Hyphen, Equals, Backspace,
Tab, Q, W, E, R, T, Y, U, I, O, P, OpenSquareBracket, CloseSquareBracket, Return,
Control, A, S, D, F, G, H, J, K, L, Semicolon, Quote, BackTick,
LeftShift, Backslash, Z, X, C, V, B, N, M, Comma, FullStop, ForwardSlash, RightShift,
/* 0x37 is unused. */
Alt = 0x38, Space, CapsLock, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10,
/* Various gaps follow. */
Home = 0x47, Up,
KeypadMinus = 0x4a, Left,
Right = 0x4d, KeypadPlus,
Down = 0x50,
Insert = 0x52, Delete,
ISO = 0x60, Undo, Help, KeypadOpenBracket, KeypadCloseBracket, KeypadDivide, KeypadMultiply,
Keypad7, Keypad8, Keypad9, Keypad4, Keypad5, Keypad6, Keypad1, Keypad2, Keypad3, Keypad0, KeypadDecimalPoint,
KeypadEnter,
Joystick1Button = 0x74, // These keycodes are used only in joystick keycode mode.
Joystick2Button = 0x75,
};
static_assert(uint16_t(Key::RightShift) == 0x36, "RightShift should have key code 0x36; check intermediate entries");
static_assert(uint16_t(Key::F10) == 0x44, "F10 should have key code 0x44; check intermediate entries");
static_assert(uint16_t(Key::KeypadEnter) == 0x72, "KeypadEnter should have key code 0x72; check intermediate entries");
/*!
A receiver for the Atari ST's "intelligent keyboard" commands, which actually cover
keyboard input and output and mouse handling.
*/
class IntelligentKeyboard:
public Serial::Line<false>::ReadDelegate,
public ClockingHint::Source,
public Inputs::Mouse {
public:
IntelligentKeyboard(Serial::Line<false> &input, Serial::Line<false> &output);
ClockingHint::Preference preferred_clocking() const final;
void run_for(HalfCycles duration);
void set_key_state(Key key, bool is_pressed);
class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper {
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) const final;
};
const std::vector<std::unique_ptr<Inputs::Joystick>> &get_joysticks() {
return joysticks_;
}
private:
// MARK: - Key queue.
std::mutex key_queue_mutex_;
std::vector<uint8_t> key_queue_;
// MARK: - Serial line state.
int bit_count_ = 0;
int command_ = 0;
Serial::Line<false> &output_line_;
void output_bytes(std::initializer_list<uint8_t> value);
bool serial_line_did_produce_bit(Serial::Line<false> *, int bit) final;
// MARK: - Command dispatch.
std::vector<uint8_t> command_sequence_;
void dispatch_command(uint8_t command);
// MARK: - Flow control.
void reset();
void resume();
void pause();
// MARK: - Mouse.
void disable_mouse();
void set_relative_mouse_position_reporting();
void set_absolute_mouse_position_reporting(uint16_t max_x, uint16_t max_y);
void set_mouse_position(uint16_t x, uint16_t y);
void set_mouse_keycode_reporting(uint8_t delta_x, uint8_t delta_y);
void set_mouse_threshold(uint8_t x, uint8_t y);
void set_mouse_scale(uint8_t x, uint8_t y);
void set_mouse_y_downward();
void set_mouse_y_upward();
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, Disabled
} mouse_mode_ = MouseMode::Relative;
// Absolute positioning state.
int mouse_range_[2] = {320, 200};
int mouse_scale_[2] = {1, 1};
int mouse_position_[2] = {0, 0};
int mouse_y_multiplier_ = 1;
// 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<int> mouse_movement_[2]{0, 0};
std::atomic<int> mouse_button_state_{0};
std::atomic<int> mouse_button_events_{0};
// MARK: - Joystick.
void disable_joysticks();
void set_joystick_event_mode();
void set_joystick_interrogation_mode();
void set_joystick_monitoring_mode(uint8_t rate);
void set_joystick_fire_button_monitoring_mode();
struct VelocityThreshold {
uint8_t threshold;
uint8_t prior_rate;
uint8_t post_rate;
};
void set_joystick_keycode_mode(VelocityThreshold horizontal, VelocityThreshold vertical);
void interrogate_joysticks();
void clear_joystick_events();
enum class JoystickMode {
Disabled, Event, Interrogation, KeyCode
} joystick_mode_ = JoystickMode::Event;
class Joystick: public Inputs::ConcreteJoystick {
public:
Joystick() :
ConcreteJoystick({
Input(Input::Up),
Input(Input::Down),
Input(Input::Left),
Input(Input::Right),
Input(Input::Fire, 0),
}) {}
void did_set_input(const Input &input, bool is_active) final {
uint8_t mask = 0;
switch(input.type) {
default: return;
case Input::Up: mask = 0x01; break;
case Input::Down: mask = 0x02; break;
case Input::Left: mask = 0x04; break;
case Input::Right: mask = 0x08; break;
case Input::Fire: mask = 0x80; break;
}
if(is_active) state_ |= mask; else state_ &= ~mask;
}
uint8_t get_state() {
returned_state_ = state_;
return state_;
}
bool has_event() {
return returned_state_ != state_;
}
uint8_t event_mask() {
return returned_state_ ^ state_;
}
private:
uint8_t state_ = 0x00;
uint8_t returned_state_ = 0x00;
};
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
};
}