From 99c21925f48fdbfc3c87abf2b24a18c1bbf080d3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 15 Feb 2021 15:00:12 -0500 Subject: [PATCH] Makes attempt at keyboard mapping. --- Machines/Apple/ADB/Keyboard.cpp | 238 +++++++++++++++++++++---- Machines/Apple/ADB/Keyboard.hpp | 96 ++++++++++ Machines/Apple/AppleIIgs/ADB.hpp | 4 + Machines/Apple/AppleIIgs/AppleIIgs.cpp | 17 ++ 4 files changed, 319 insertions(+), 36 deletions(-) diff --git a/Machines/Apple/ADB/Keyboard.cpp b/Machines/Apple/ADB/Keyboard.cpp index 3d1139d0f..c76faee32 100644 --- a/Machines/Apple/ADB/Keyboard.cpp +++ b/Machines/Apple/ADB/Keyboard.cpp @@ -13,46 +13,212 @@ using namespace Apple::ADB; Keyboard::Keyboard(Bus &bus) : ReactiveDevice(bus, 2) {} void Keyboard::perform_command(const Command &command) { - if(command.type == Command::Type::Talk) { - switch(command.reg) { - case 0: - // Post up to two key events, or nothing if there are - // no events pending. - // - // Events are: - // - // b7 = 0 for down 1, for up; - // b6–b0: key code (mostly 7-bit ASCII) -// post_response({0x00, 0x00}); - break; + switch(command.type) { + case Command::Type::Reset: + modifiers_ = 0xffff; + case Command::Type::Flush: { + std::lock_guard lock_guard(keys_mutex_); + pending_events_.clear(); + } break; - case 2: - /* - In all cases below: 0 = pressed/on; 1 = released/off. + case Command::Type::Talk: + switch(command.reg) { + case 0: { + // Post up to two key events, or nothing if there are + // no events pending. + std::lock_guard lock_guard(keys_mutex_); - b15: None (reserved) - b14: Delete - b13: Caps lock - b12: Reset - b11: Control - b10: Shift - b9: Option - b8: Command + if(!pending_events_.empty()) { + if(pending_events_.size() > 1) { + post_response({pending_events_[0], pending_events_[1]}); + pending_events_.erase(pending_events_.begin(), pending_events_.begin()+2); + } else { + // Two bytes are required; provide a key up of the fictional + // key zero as the second. + // That's arbitrary; verify with real machines. + post_response({pending_events_[0], 0x80}); + pending_events_.clear(); + } + } + } break; - -- From here onwards, available only on the extended keyboard. + case 2: { + std::lock_guard lock_guard(keys_mutex_); + post_response({uint8_t(modifiers_ >> 8), uint8_t(modifiers_)}); + } break; - b7: Num lock/clear - b6: Scroll lock - b5–3: None (reserved) - b2: Scroll Lock LED - b1: Caps Lock LED - b0: Num Lock LED - */ - post_response({0xff, 0xff}); - break; + default: break; + } + break; - default: break; - } - return; + default: break; + } +} + +bool Keyboard::set_key_pressed(Key key, bool is_pressed) { + // ADB keyboard events: low 7 bits are a key code; bit 7 is either 0 for pressed or 1 for released. + std::lock_guard lock_guard(keys_mutex_); + pending_events_.push_back(uint8_t(key) | (is_pressed ? 0x00 : 0x80)); + pressed_keys_[size_t(key)] = is_pressed; + + // Track modifier state also. + + /* + In all cases below: 0 = pressed/on; 1 = released/off. + + b15: None (reserved) + b14: Delete + b13: Caps lock + b12: Reset + b11: Control + b10: Shift + b9: Option + b8: Command + + -- From here onwards, available only on the extended keyboard. + + b7: Num lock/clear + b6: Scroll lock + b5–3: None (reserved) + b2: Scroll Lock LED + b1: Caps Lock LED + b0: Num Lock LED + */ + +#define SetModifierBit(x) modifiers_ = (modifiers_ & ~x) | (is_pressed ? 0 : x); +#define ToggleModifierBit(x) if(is_pressed) modifiers_ ^= x; + switch(key) { + default: break; + case Key::Delete: SetModifierBit(0x4000); break; + case Key::CapsLock: ToggleModifierBit(0x2000); break; + case Key::Power: SetModifierBit(0x1000); break; + + case Key::LeftControl: + case Key::RightControl: + SetModifierBit(0x0800); + break; + + case Key::LeftShift: + case Key::RightShift: + SetModifierBit(0x0400); + break; + + case Key::LeftOption: + case Key::RightOption: + SetModifierBit(0x0200); + break; + + case Key::Command: + SetModifierBit(0x0100); + break; + + case Key::KeypadClear: ToggleModifierBit(0x0080); break; + case Key::Help: ToggleModifierBit(0x0040); break; + } +#undef SetModifierBit + + return true; +} + +void Keyboard::clear_all_keys() { + // For all keys currently marked as down, enqueue key-up actions. + std::lock_guard lock_guard(keys_mutex_); + for(size_t key = 0; key < pressed_keys_.size(); key++) { + if(pressed_keys_[key]) { + pending_events_.push_back(0x80 | uint8_t(key)); + pressed_keys_[key] = false; + } + } + + // Mark all modifiers as released. + modifiers_ |= 0xfff8; +} + +// MARK: - KeyboardMapper + +uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) const { + using Key = Inputs::Keyboard::Key; + using ADBKey = Apple::ADB::Key; + switch(key) { + default: return MachineTypes::MappedKeyboardMachine::KeyNotMapped; + +#define Bind(x, y) case Key::x: return uint16_t(ADBKey::y) +#define BindDirect(x) Bind(x, x) + + BindDirect(BackTick); + BindDirect(k1); BindDirect(k2); BindDirect(k3); BindDirect(k4); BindDirect(k5); + BindDirect(k6); BindDirect(k7); BindDirect(k8); BindDirect(k9); BindDirect(k0); + + BindDirect(Help); + BindDirect(Home); + BindDirect(PageUp); + BindDirect(Delete); + BindDirect(End); + BindDirect(PageDown); + + BindDirect(Escape); + BindDirect(Hyphen); + BindDirect(Equals); + BindDirect(Backspace); + BindDirect(Tab); + + BindDirect(F1); BindDirect(F2); BindDirect(F3); BindDirect(F4); + BindDirect(F5); BindDirect(F6); BindDirect(F7); BindDirect(F8); + BindDirect(F9); BindDirect(F10); BindDirect(F11); BindDirect(F12); + + BindDirect(Q); BindDirect(W); BindDirect(E); BindDirect(R); + BindDirect(T); BindDirect(Y); BindDirect(U); BindDirect(I); + BindDirect(O); BindDirect(P); BindDirect(A); BindDirect(S); + BindDirect(D); BindDirect(F); BindDirect(G); BindDirect(H); + BindDirect(J); BindDirect(K); BindDirect(L); BindDirect(Z); + BindDirect(X); BindDirect(C); BindDirect(V); BindDirect(B); + BindDirect(N); BindDirect(M); + + BindDirect(OpenSquareBracket); + BindDirect(CloseSquareBracket); + BindDirect(Semicolon); + BindDirect(Quote); + BindDirect(Comma); + BindDirect(FullStop); + BindDirect(ForwardSlash); + + BindDirect(CapsLock); + BindDirect(LeftShift); BindDirect(RightShift); + BindDirect(LeftControl); BindDirect(RightControl); + BindDirect(LeftOption); BindDirect(RightOption); + Bind(LeftMeta, Command); Bind(RightMeta, Command); + + BindDirect(Space); + BindDirect(Backslash); + Bind(Enter, Return); + + BindDirect(Left); BindDirect(Right); + BindDirect(Up); BindDirect(Down); + + Bind(KeypadDelete, KeypadClear); + BindDirect(KeypadEquals); + BindDirect(KeypadSlash); + BindDirect(KeypadAsterisk); + BindDirect(KeypadMinus); + BindDirect(KeypadPlus); + BindDirect(KeypadEnter); + BindDirect(KeypadDecimalPoint); + + BindDirect(Keypad9); + BindDirect(Keypad8); + BindDirect(Keypad7); + BindDirect(Keypad6); + BindDirect(Keypad5); + BindDirect(Keypad4); + BindDirect(Keypad3); + BindDirect(Keypad2); + BindDirect(Keypad1); + BindDirect(Keypad0); + + // Leaving unmapped: + // Power, F13, F14, F15 + +#undef BindDirect +#undef Bind } } diff --git a/Machines/Apple/ADB/Keyboard.hpp b/Machines/Apple/ADB/Keyboard.hpp index be2714cf3..47adf1f4e 100644 --- a/Machines/Apple/ADB/Keyboard.hpp +++ b/Machines/Apple/ADB/Keyboard.hpp @@ -10,16 +10,112 @@ #define Keyboard_hpp #include "ReactiveDevice.hpp" +#include "../../../Inputs/Keyboard.hpp" +#include "../../KeyboardMachine.hpp" + +#include +#include +#include +#include namespace Apple { namespace ADB { +/*! + Defines the keycodes that could be passed directly via set_key_pressed; these + are based on the Apple Extended Keyboard. +*/ +enum class Key: uint16_t { + /* + These are transcribed from Page 19-11 of + the Macintosh Family Hardware Reference. + */ + BackTick = 0x32, + k1 = 0x12, k2 = 0x13, k3 = 0x14, k4 = 0x15, k5 = 0x17, + k6 = 0x16, k7 = 0x1a, k8 = 0x1c, k9 = 0x19, k0 = 0x1d, + + Help = 0x72, + Home = 0x73, + PageUp = 0x74, + Delete = 0x75, + End = 0x77, + PageDown = 0x79, + + Escape = 0x35, + Hyphen = 0x1b, + Equals = 0x18, + Backspace = 0x33, + Tab = 0x30, + Power = 0x7f, + + F1 = 0x7a, F2 = 0x78, F3 = 0x63, F4 = 0x76, + F5 = 0x60, F6 = 0x61, F7 = 0x62, F8 = 0x64, + F9 = 0x65, F10 = 0x6d, F11 = 0x67, F12 = 0x6f, + F13 = 0x69, F14 = 0x6b, F15 = 0x71, + + Q = 0x0c, W = 0x0d, E = 0x0e, R = 0x0f, T = 0x11, Y = 0x10, U = 0x20, I = 0x22, O = 0x1f, P = 0x23, + A = 0x00, S = 0x01, D = 0x02, F = 0x03, G = 0x05, H = 0x04, J = 0x26, K = 0x28, L = 0x25, + Z = 0x06, X = 0x07, C = 0x08, V = 0x09, B = 0x0b, N = 0x2d, M = 0x2e, + + OpenSquareBracket = 0x21, + CloseSquareBracket = 0x1e, + Semicolon = 0x29, + Quote = 0x27, + Comma = 0x2b, + FullStop = 0x2f, + ForwardSlash = 0x2c, + + CapsLock = 0x39, + LeftShift = 0x38, RightShift = 0x7b, + LeftControl = 0x36, RightControl = 0x7d, + LeftOption = 0x3a, RightOption = 0x7c, + Command = 0x37, + + Space = 0x31, + Backslash = 0x2a, + Return = 0x24, + + Left = 0x3b, + Right = 0x3c, + Up = 0x3e, + Down = 0x3d, + + KeypadClear = 0x47, + KeypadEquals = 0x51, + KeypadSlash = 0x4b, + KeypadAsterisk = 0x43, + KeypadMinus = 0x4e, + KeypadPlus = 0x45, + KeypadEnter = 0x4c, + KeypadDecimalPoint = 0x41, + + Keypad9 = 0x5c, Keypad8 = 0x5b, Keypad7 = 0x59, + Keypad6 = 0x58, Keypad5 = 0x57, Keypad4 = 0x56, + Keypad3 = 0x55, Keypad2 = 0x54, Keypad1 = 0x53, + Keypad0 = 0x52, +}; + class Keyboard: public ReactiveDevice { public: Keyboard(Bus &); + bool set_key_pressed(Key key, bool is_pressed); + void clear_all_keys(); + private: void perform_command(const Command &command) override; + + std::mutex keys_mutex_; + std::array pressed_keys_; + std::vector pending_events_; + uint16_t modifiers_ = 0xffff; +}; + +/*! + Provides a mapping from idiomatic PC keys to ADB keys. +*/ +class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper { + uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) const final; }; } diff --git a/Machines/Apple/AppleIIgs/ADB.hpp b/Machines/Apple/AppleIIgs/ADB.hpp index eea4a0c29..ec10c0ba2 100644 --- a/Machines/Apple/AppleIIgs/ADB.hpp +++ b/Machines/Apple/AppleIIgs/ADB.hpp @@ -46,6 +46,10 @@ class GLU: public InstructionSet::M50740::PortHandler { void set_vertical_blank(bool); + Apple::ADB::Keyboard &keyboard() { + return keyboard_; + } + private: InstructionSet::M50740::Executor executor_; diff --git a/Machines/Apple/AppleIIgs/AppleIIgs.cpp b/Machines/Apple/AppleIIgs/AppleIIgs.cpp index d4f6da0ca..3a92c5b12 100644 --- a/Machines/Apple/AppleIIgs/AppleIIgs.cpp +++ b/Machines/Apple/AppleIIgs/AppleIIgs.cpp @@ -51,6 +51,7 @@ class ConcreteMachine: public MachineTypes::MediaTarget, public MachineTypes::ScanProducer, public MachineTypes::TimedMachine, + public MachineTypes::MappedKeyboardMachine, public CPU::MOS6502Esque::BusHandler { public: @@ -909,6 +910,19 @@ class ConcreteMachine: m65816_.set_irq_line(video_.last_valid()->get_interrupt_line() || sound_glu_.get_interrupt_line()); } + // MARK: - Keyboard input. + KeyboardMapper *get_keyboard_mapper() final { + return &keyboard_mapper_; + } + + void set_key_state(uint16_t key, bool is_pressed) final { + adb_glu_.last_valid()->keyboard().set_key_pressed(Apple::ADB::Key(key), is_pressed); + } + + void clear_all_keys() final { + adb_glu_.last_valid()->keyboard().clear_all_keys(); + } + private: CPU::WDC65816::Processor m65816_; MemoryMap memory_; @@ -965,6 +979,9 @@ class ConcreteMachine: }; friend AudioUpdater; + // MARK: - Keyboard. + Apple::ADB::KeyboardMapper keyboard_mapper_; + // MARK: - Cards. // TODO: most of cards.