From 3be5d60b1e696cec40d7ba0a256e163fc5322c01 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 18 May 2024 22:16:58 -0400 Subject: [PATCH] Eliminate comparison costs. --- Analyser/Static/Acorn/StaticAnalyser.cpp | 3 - Analyser/Static/Acorn/Target.hpp | 1 - Machines/Acorn/Archimedes/Archimedes.cpp | 15 +-- Machines/Acorn/Archimedes/Keyboard.hpp | 110 ++++++++++++++++--- Machines/Acorn/Archimedes/KeyboardMapper.hpp | 49 +-------- 5 files changed, 103 insertions(+), 75 deletions(-) diff --git a/Analyser/Static/Acorn/StaticAnalyser.cpp b/Analyser/Static/Acorn/StaticAnalyser.cpp index 84a8a36a8..846503221 100644 --- a/Analyser/Static/Acorn/StaticAnalyser.cpp +++ b/Analyser/Static/Acorn/StaticAnalyser.cpp @@ -147,9 +147,6 @@ Analyser::Static::TargetList Analyser::Static::Acorn::GetTargets(const Media &me // Archimedes options, implicitly: ADFS, non-Hugo. targetArchimedes->media.disks = media.disks; - // Always try a shift-restart; it's worth a go. - targetArchimedes->should_shift_restart = true; - // Also look for the best possible startup program name, if it can be discerned. for(const auto &file: adfs_catalogue->files) { // Skip files that would have been caught by shift-restart if suitable. diff --git a/Analyser/Static/Acorn/Target.hpp b/Analyser/Static/Acorn/Target.hpp index 2f24c69d6..bcb63b217 100644 --- a/Analyser/Static/Acorn/Target.hpp +++ b/Analyser/Static/Acorn/Target.hpp @@ -35,7 +35,6 @@ struct ElectronTarget: public ::Analyser::Static::Target, public Reflection::Str }; struct ArchimedesTarget: public ::Analyser::Static::Target, public Reflection::StructImpl { - bool should_shift_restart = false; std::string main_program; ArchimedesTarget() : Analyser::Static::Target(Machine::Archimedes) {} diff --git a/Machines/Acorn/Archimedes/Archimedes.cpp b/Machines/Acorn/Archimedes/Archimedes.cpp index ad0cd51fc..f6b94c7b5 100644 --- a/Machines/Acorn/Archimedes/Archimedes.cpp +++ b/Machines/Acorn/Archimedes/Archimedes.cpp @@ -127,6 +127,8 @@ class ConcreteMachine: if(!target.media.disks.empty()) { autoload_phase_ = AutoloadPhase::WaitingForStartup; target_program_ = target.main_program; + + printf("Will seek %s?\n", target_program_.c_str()); } fill_pipeline(0); @@ -234,10 +236,10 @@ class ConcreteMachine: cursor_actions_.push_back(CursorAction::button(0, true)); cursor_actions_.push_back(CursorAction::wait(12'000'000)); cursor_actions_.push_back(CursorAction::button(0, false)); - cursor_actions_.push_back(CursorAction::move_to(64, 32)); cursor_actions_.push_back(CursorAction::set_phase( target_program_.empty() ? AutoloadPhase::Ended : AutoloadPhase::WaitingForDiskContents) ); + cursor_actions_.push_back(CursorAction::move_to(64, 36)); } // TODO: spot potential addition of extra program icon. @@ -255,6 +257,7 @@ class ConcreteMachine: desc = get_string(address + 20, flags & (1 << 8)); } + printf("%s == %s?\n", desc.c_str(), target_program_.c_str()); if(desc == target_program_) { uint32_t x1, y1, x2, y2; executor_.bus.read(address + 0, x1, false); @@ -265,10 +268,10 @@ class ConcreteMachine: autoload_phase_ = AutoloadPhase::OpeningProgram; // Some default icon sizing assumptions are baked in here. - const auto x_target = target_window_[0] + static_cast(x1) + 200; + const auto x_target = target_window_[0] + (static_cast(x1) + static_cast(x2)) / 2; const auto y_target = target_window_[1] + static_cast(y1) + 24; cursor_actions_.push_back(CursorAction::move_to( - x_target >> 2, + x_target >> 1, 256 - (y_target >> 2) )); cursor_actions_.push_back(CursorAction::button(0, true)); @@ -346,7 +349,7 @@ class ConcreteMachine: // A measure of where within the tip lies within // the default RISC OS cursor. constexpr int ActionPointOffset = 20; - constexpr int MaxStep = 8; + constexpr int MaxStep = 24; const auto position = executor_.bus.video().cursor_location(); if(!position) break; @@ -449,9 +452,7 @@ class ConcreteMachine: Archimedes::KeyboardMapper keyboard_mapper_; void set_key_state(uint16_t key, bool is_pressed) override { - const int row = Archimedes::KeyboardMapper::row(key); - const int column = Archimedes::KeyboardMapper::column(key); - executor_.bus.keyboard().set_key_state(row, column, is_pressed); + executor_.bus.keyboard().set_key_state(key, is_pressed); } // MARK: - MouseMachine. diff --git a/Machines/Acorn/Archimedes/Keyboard.hpp b/Machines/Acorn/Archimedes/Keyboard.hpp index f3783014b..617c87a65 100644 --- a/Machines/Acorn/Archimedes/Keyboard.hpp +++ b/Machines/Acorn/Archimedes/Keyboard.hpp @@ -13,14 +13,70 @@ #include "../../../Outputs/Log.hpp" #include "../../../Inputs/Mouse.hpp" +#include + namespace Archimedes { +namespace { +constexpr uint16_t map(int row, int column) { + return static_cast((row << 4) | column); +} + +constexpr uint8_t row(uint16_t key) { + return static_cast(key >> 4); +} + +constexpr uint8_t column(uint16_t key) { + return static_cast(key & 0xf); +} +} + +struct Key { + /// Named key codes that the machine wlll accept directly. + enum Value: uint16_t { + Escape = map(0, 0), F1 = map(0, 1), F2 = map(0, 2), F3 = map(0, 3), + F4 = map(0, 4), F5 = map(0, 5), F6 = map(0, 6), F7 = map(0, 7), + F8 = map(0, 8), F9 = map(0, 9), F10 = map(0, 10), F11 = map(0, 11), + F12 = map(0, 12), Print = map(0, 13), Scroll = map(0, 14), Break = map(0, 15), + + Tilde = map(1, 0), k1 = map(1, 1), k2 = map(1, 2), k3 = map(1, 3), + k4 = map(1, 4), k5 = map(1, 5), k6 = map(1, 6), k7 = map(1, 7), + k8 = map(1, 8), k9 = map(1, 9), k0 = map(1, 10), Hyphen = map(1, 11), + Equals = map(1, 12), GBPound = map(1, 13), Backspace = map(1, 14), Insert = map(1, 15), + + Home = map(2, 0), PageUp = map(2, 1), NumLock = map(2, 2), KeypadSlash = map(2, 3), + KeypadAsterisk = map(2, 4), KeypadHash = map(2, 5), Tab = map(2, 6), Q = map(2, 7), + W = map(2, 8), E = map(2, 9), R = map(2, 10), T = map(2, 11), + Y = map(2, 12), U = map(2, 13), I = map(2, 14), O = map(2, 15), + + P = map(3, 0), OpenSquareBracket = map(3, 1), CloseSquareBracket = map(3, 2), Backslash = map(3, 3), + Delete = map(3, 4), Copy = map(3, 5), PageDown = map(3, 6), Keypad7 = map(3, 7), + Keypad8 = map(3, 8), Keypad9 = map(3, 9), KeypadMinus = map(3, 10), LeftControl = map(3, 11), + A = map(3, 12), S = map(3, 13), D = map(3, 14), F = map(3, 15), + + G = map(4, 0), H = map(4, 1), J = map(4, 2), K = map(4, 3), + L = map(4, 4), Semicolon = map(4, 5), Quote = map(4, 6), Return = map(4, 7), + Keypad4 = map(4, 8), Keypad5 = map(4, 9), Keypad6 = map(4, 10), KeypadPlus = map(4, 11), + LeftShift = map(4, 12), /* unused */ Z = map(4, 14), X = map(4, 15), + + C = map(5, 0), V = map(5, 1), B = map(5, 2), N = map(5, 3), + M = map(5, 4), Comma = map(5, 5), FullStop = map(5, 6), ForwardSlash = map(5, 7), + RightShift = map(5, 8), Up = map(5, 9), Keypad1 = map(5, 10), Keypad2 = map(5, 11), + Keypad3 = map(5, 12), CapsLock = map(5, 13), LeftAlt = map(5, 14), Space = map(5, 15), + + RightAlt = map(6, 0), RightControl = map(6, 1), Left = map(6, 2), Down = map(6, 3), + Right = map(6, 4), Keypad0 = map(6, 5), KeypadDecimalPoint = map(6, 6), KeypadEnter = map(6, 7), + + Max = KeypadEnter, + }; +}; + // Resource for the keyboard protocol: https://github.com/tmk/tmk_keyboard/wiki/ACORN-ARCHIMEDES-Keyboard struct Keyboard { Keyboard(HalfDuplexSerial &serial) : serial_(serial), mouse_(*this) {} - void set_key_state(int row, int column, bool is_pressed) { - states_[row][column] = is_pressed; + void set_key_state(uint16_t key, bool is_pressed) { + states_[key] = is_pressed; if(!scan_keyboard_) { logger_.info().append("Ignored key event as key scanning disabled"); @@ -28,10 +84,20 @@ struct Keyboard { } // Don't waste bandwidth on repeating facts. - if(posted_states_[row][column] == is_pressed) return; + if(posted_states_[key] == is_pressed) return; // Post new key event. - enqueue_key_event(row, column, is_pressed); + enqueue_key_event(key, is_pressed); + consider_dequeue(); + } + + void set_mouse_button(uint8_t button, bool is_pressed) { + if(!scan_mouse_) { + return; + } + + // Post new key event. + enqueue_key_event(7, button, is_pressed); consider_dequeue(); } @@ -79,11 +145,17 @@ struct Keyboard { enqueue(0, 0); break; - case NACK: case SMAK: case MACK: case SACK: + case NACK: case SMAK: case MACK: case SACK: { + const bool was_scanning_keyboard = input & 1; scan_keyboard_ = input & 1; + if(!scan_keyboard_) { + posted_states_.reset(); + } else if(!was_scanning_keyboard) { + needs_state_check_ = true; + } scan_mouse_ = input & 2; logger_.info().append("ACK; keyboard:%d mouse:%d", scan_keyboard_, scan_mouse_); - break; + } break; default: if((input & 0b1111'0000) == 0b0100'0000) { @@ -142,10 +214,13 @@ struct Keyboard { if(state_ == State::Idle) { // If the key event queue is empty but keyboard scanning is enabled, check for // any disparity between posted keys states and actuals. - for(int row = 0; row < 16 && event_queue_.empty(); row++) { - for(int column = 0; column < 16 && event_queue_.empty(); column++) { - if(posted_states_[row][column] != states_[row][column]) { - enqueue_key_event(row, column, states_[row][column]); + if(needs_state_check_) { + needs_state_check_ = false; + if(states_ != posted_states_) { + for(size_t key = 0; key < Key::Max; key++) { + if(states_[key] != posted_states_[key]) { + enqueue_key_event(static_cast(key), states_[key]); + } } } } @@ -177,8 +252,9 @@ private: HalfDuplexSerial &serial_; Log::Logger logger_; - bool states_[16][16]{}; - bool posted_states_[16][16]{}; + std::bitset states_; + std::bitset posted_states_; + bool needs_state_check_ = false; bool scan_keyboard_ = false; bool scan_mouse_ = false; @@ -206,9 +282,12 @@ private: event_queue_.erase(event_queue_.begin()); return true; } - void enqueue_key_event(int row, int column, bool is_pressed) { + void enqueue_key_event(uint16_t key, bool is_pressed) { + posted_states_[key] = is_pressed; + enqueue_key_event(row(key), column(key), is_pressed); + } + void enqueue_key_event(uint8_t row, uint8_t column, bool is_pressed) { logger_.info().append("Posting row %d, column %d is now %s", row, column, is_pressed ? "pressed" : "released"); - posted_states_[row][column] = is_pressed; const uint8_t prefix = is_pressed ? 0b1100'0000 : 0b1101'0000; enqueue(static_cast(prefix | row), static_cast(prefix | column)); } @@ -227,7 +306,6 @@ private: static constexpr uint8_t SMAK = 0b0011'0011; // Last data byte acknowledge, enabling scanning and mouse. static constexpr uint8_t PRST = 0b0010'0001; // Does nothing. - struct Mouse: public Inputs::Mouse { Mouse(Keyboard &keyboard): keyboard_(keyboard) {} @@ -241,7 +319,7 @@ private: } virtual void set_button_pressed(int index, bool is_pressed) override { - keyboard_.set_key_state(7, index, is_pressed); + keyboard_.set_mouse_button(static_cast(index), is_pressed); } private: diff --git a/Machines/Acorn/Archimedes/KeyboardMapper.hpp b/Machines/Acorn/Archimedes/KeyboardMapper.hpp index eeeab129d..2bec57838 100644 --- a/Machines/Acorn/Archimedes/KeyboardMapper.hpp +++ b/Machines/Acorn/Archimedes/KeyboardMapper.hpp @@ -9,60 +9,13 @@ #pragma once #include "../../KeyboardMachine.hpp" +#include "Keyboard.hpp" namespace Archimedes { -static constexpr uint16_t map(int row, int column) { - return static_cast((row << 4) | column); -} - -/// Named key codes that the machine wlll accept directly. -enum Key: uint16_t { - Escape = map(0, 0), F1 = map(0, 1), F2 = map(0, 2), F3 = map(0, 3), - F4 = map(0, 4), F5 = map(0, 5), F6 = map(0, 6), F7 = map(0, 7), - F8 = map(0, 8), F9 = map(0, 9), F10 = map(0, 10), F11 = map(0, 11), - F12 = map(0, 12), Print = map(0, 13), Scroll = map(0, 14), Break = map(0, 15), - - Tilde = map(1, 0), k1 = map(1, 1), k2 = map(1, 2), k3 = map(1, 3), - k4 = map(1, 4), k5 = map(1, 5), k6 = map(1, 6), k7 = map(1, 7), - k8 = map(1, 8), k9 = map(1, 9), k0 = map(1, 10), Hyphen = map(1, 11), - Equals = map(1, 12), GBPound = map(1, 13), Backspace = map(1, 14), Insert = map(1, 15), - - Home = map(2, 0), PageUp = map(2, 1), NumLock = map(2, 2), KeypadSlash = map(2, 3), - KeypadAsterisk = map(2, 4), KeypadHash = map(2, 5), Tab = map(2, 6), Q = map(2, 7), - W = map(2, 8), E = map(2, 9), R = map(2, 10), T = map(2, 11), - Y = map(2, 12), U = map(2, 13), I = map(2, 14), O = map(2, 15), - - P = map(3, 0), OpenSquareBracket = map(3, 1), CloseSquareBracket = map(3, 2), Backslash = map(3, 3), - Delete = map(3, 4), Copy = map(3, 5), PageDown = map(3, 6), Keypad7 = map(3, 7), - Keypad8 = map(3, 8), Keypad9 = map(3, 9), KeypadMinus = map(3, 10), LeftControl = map(3, 11), - A = map(3, 12), S = map(3, 13), D = map(3, 14), F = map(3, 15), - - G = map(4, 0), H = map(4, 1), J = map(4, 2), K = map(4, 3), - L = map(4, 4), Semicolon = map(4, 5), Quote = map(4, 6), Return = map(4, 7), - Keypad4 = map(4, 8), Keypad5 = map(4, 9), Keypad6 = map(4, 10), KeypadPlus = map(4, 11), - LeftShift = map(4, 12), /* unused */ Z = map(4, 14), X = map(4, 14), - - C = map(5, 0), V = map(5, 1), B = map(5, 2), N = map(5, 3), - M = map(5, 4), Comma = map(5, 5), FullStop = map(5, 6), ForwardSlash = map(5, 7), - RightShift = map(5, 8), Up = map(5, 9), Keypad1 = map(5, 10), Keypad2 = map(5, 11), - Keypad3 = map(5, 12), CapsLock = map(5, 13), LeftAlt = map(5, 14), Space = map(5, 15), - - RightAlt = map(6, 0), RightControl = map(6, 1), Left = map(6, 2), Down = map(6, 3), - Right = map(6, 4), Keypad0 = map(6, 5), KeypadDecimalPoint = map(6, 6), KeypadEnter = map(6, 7), -}; - /// Converter from this emulator's custom definition of a generic keyboard to the machine-specific key set defined above. class KeyboardMapper: public MachineTypes::MappedKeyboardMachine::KeyboardMapper { public: - static constexpr int row(uint16_t key) { - return key >> 4; - } - - static constexpr int column(uint16_t key) { - return key & 0xf; - } - // Adapted from the A500 Series Technical Reference Manual. uint16_t mapped_key_for_key(Inputs::Keyboard::Key key) const override { using k = Inputs::Keyboard::Key;