From bd45c1c96370673c401cdc41c4340aa6c94c076d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 17:34:21 -0500 Subject: [PATCH 01/28] Adds `append` and generally seeks to improve string accumulation. --- Machines/Utility/Typer.cpp | 76 +++++++++++++++++++++++++++----------- Machines/Utility/Typer.hpp | 10 ++++- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/Machines/Utility/Typer.cpp b/Machines/Utility/Typer.cpp index 18a847968..20d5a345e 100644 --- a/Machines/Utility/Typer.cpp +++ b/Machines/Utility/Typer.cpp @@ -8,8 +8,6 @@ #include "Typer.hpp" -#include - using namespace Utility; Typer::Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, std::unique_ptr character_mapper, Delegate *delegate) : @@ -17,33 +15,69 @@ Typer::Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, counter_(-delay), delegate_(delegate), character_mapper_(std::move(character_mapper)) { - std::ostringstream string_stream; - string_stream << Typer::BeginString << string << Typer::EndString; - string_ = string_stream.str(); + // Retain only those characters that actually map to something. + if(sequence_for_character(Typer::BeginString)) { + string_ += Typer::BeginString; + } + if(sequence_for_character(Typer::EndString)) { + string_ += Typer::EndString; + } + + append(string); } void Typer::run_for(const HalfCycles duration) { - if(string_pointer_ < string_.size()) { - if(counter_ < 0 && counter_ + duration >= 0) { - if(!type_next_character()) { - delegate_->typer_reset(this); - } - } + if(string_pointer_ >= string_.size()) { + return; + } - counter_ += duration; - while(string_pointer_ < string_.size() && counter_ > frequency_) { - counter_ -= frequency_; - if(!type_next_character()) { - delegate_->typer_reset(this); - } + if(counter_ < 0 && counter_ + duration >= 0) { + if(!type_next_character()) { + delegate_->typer_reset(this); + } + } + + counter_ += duration; + while(string_pointer_ < string_.size() && counter_ > frequency_) { + counter_ -= frequency_; + if(!type_next_character()) { + delegate_->typer_reset(this); } } } -bool Typer::try_type_next_character() { - uint16_t *sequence = character_mapper_->sequence_for_character(string_[string_pointer_]); +void Typer::append(const std::string &string) { + // Remove any characters that are already completely done; + // otherwise things may accumulate here indefinitely. + string_.erase(string_.begin(), string_.begin() + ssize_t(string_pointer_)); + string_pointer_ = 0; + // If the final character in the string is not Typer::EndString + // then this machine doesn't need Begin and End, so don't worry about it. + ssize_t insertion_position = ssize_t(string_.size()); + if(string_.back() == Typer::EndString) --insertion_position; + + string_.reserve(string_.size() + string.size()); + for(const char c : string) { + if(sequence_for_character(c)) { + string_.insert(string_.begin() + insertion_position, c); + ++insertion_position; + } + } +} + +const uint16_t *Typer::sequence_for_character(char c) const { + const uint16_t *const sequence = character_mapper_->sequence_for_character(c); if(!sequence || sequence[0] == KeyboardMachine::MappedMachine::KeyNotMapped) { + return nullptr; + } + return sequence; +} + +bool Typer::try_type_next_character() { + const uint16_t *const sequence = sequence_for_character(string_[string_pointer_]); + + if(!sequence) { return false; } @@ -61,10 +95,10 @@ bool Typer::type_next_character() { if(!try_type_next_character()) { phase_ = 0; - string_pointer_++; + ++string_pointer_; if(string_pointer_ == string_.size()) return false; } else { - phase_++; + ++phase_; } return true; diff --git a/Machines/Utility/Typer.hpp b/Machines/Utility/Typer.hpp index fbf11a6e8..90eb21d9b 100644 --- a/Machines/Utility/Typer.hpp +++ b/Machines/Utility/Typer.hpp @@ -51,14 +51,21 @@ class Typer { public: class Delegate: public KeyboardMachine::KeyActions { public: + /// Informs the delegate that this typer has reached the end of its content. virtual void typer_reset(Typer *typer) = 0; }; Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, std::unique_ptr character_mapper, Delegate *delegate); + /// Advances for @c duration. void run_for(const HalfCycles duration); + + /// Types the next character now, if there is one. + /// @returns @c true if there was anything left to type; @c false otherwise. bool type_next_character(); - bool is_completed(); + + /// Adds the contents of @c str to the end of the current string. + void append(const std::string &str); const char BeginString = 0x02; // i.e. ASCII start of text const char EndString = 0x03; // i.e. ASCII end of text @@ -75,6 +82,7 @@ class Typer { std::unique_ptr character_mapper_; bool try_type_next_character(); + const uint16_t *sequence_for_character(char) const; }; /*! From 4594a3c02b2f0df3bfd8397609048c45fa257f92 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 18:12:32 -0500 Subject: [PATCH 02/28] Ensures final thing in a key sequence is fully typed; adds ability to quicken input. --- Machines/AmstradCPC/Keyboard.cpp | 4 +++ Machines/AmstradCPC/Keyboard.hpp | 5 +++- Machines/Utility/Typer.cpp | 46 +++++++++++++++++++++++--------- Machines/Utility/Typer.hpp | 14 +++++++++- 4 files changed, 54 insertions(+), 15 deletions(-) diff --git a/Machines/AmstradCPC/Keyboard.cpp b/Machines/AmstradCPC/Keyboard.cpp index f3476505f..58340d4cc 100644 --- a/Machines/AmstradCPC/Keyboard.cpp +++ b/Machines/AmstradCPC/Keyboard.cpp @@ -150,3 +150,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) { return table_lookup_sequence_for_character(key_sequences, sizeof(key_sequences), character); } + +bool CharacterMapper::needs_pause_after_key(uint16_t key) { + return key != KeyControl && key != KeyShift; +} diff --git a/Machines/AmstradCPC/Keyboard.hpp b/Machines/AmstradCPC/Keyboard.hpp index 65544faba..5aadbf5e3 100644 --- a/Machines/AmstradCPC/Keyboard.hpp +++ b/Machines/AmstradCPC/Keyboard.hpp @@ -38,7 +38,10 @@ struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { }; struct CharacterMapper: public ::Utility::CharacterMapper { - uint16_t *sequence_for_character(char character); + uint16_t *sequence_for_character(char character) override; + + bool needs_pause_after_reset_all_keys() override { return false; } + bool needs_pause_after_key(uint16_t key) override; }; }; diff --git a/Machines/Utility/Typer.cpp b/Machines/Utility/Typer.cpp index 20d5a345e..3c35ab0f7 100644 --- a/Machines/Utility/Typer.cpp +++ b/Machines/Utility/Typer.cpp @@ -74,31 +74,51 @@ const uint16_t *Typer::sequence_for_character(char c) const { return sequence; } -bool Typer::try_type_next_character() { +uint16_t Typer::try_type_next_character() { const uint16_t *const sequence = sequence_for_character(string_[string_pointer_]); if(!sequence) { - return false; + return 0; } - if(!phase_) delegate_->clear_all_keys(); - else { - delegate_->set_key_state(sequence[phase_ - 1], true); - return sequence[phase_] != KeyboardMachine::MappedMachine::KeyEndSequence; + // If this is the start of the output sequence, start with a reset all keys. + // Then pause unless the caracter mapper says not to. + if(!phase_) { + delegate_->clear_all_keys(); + if(character_mapper_->needs_pause_after_reset_all_keys()) { + return 0xffff; // Arbitrarily. Anything non-zero will do. + } } - return true; + // Advance phase. + ++phase_; + + // If the sequence is over, stop. + if(sequence[phase_ - 1] == KeyboardMachine::MappedMachine::KeyEndSequence) { + return 0; + } + + // Otherwise, type the key. + delegate_->set_key_state(sequence[phase_ - 1], true); + + return sequence[phase_ - 1]; } bool Typer::type_next_character() { if(string_pointer_ == string_.size()) return false; - if(!try_type_next_character()) { - phase_ = 0; - ++string_pointer_; - if(string_pointer_ == string_.size()) return false; - } else { - ++phase_; + while(true) { + const uint16_t key_pressed = try_type_next_character(); + + if(!key_pressed) { + phase_ = 0; + ++string_pointer_; + if(string_pointer_ == string_.size()) return false; + } + + if(character_mapper_->needs_pause_after_key(key_pressed)) { + break; + } } return true; diff --git a/Machines/Utility/Typer.hpp b/Machines/Utility/Typer.hpp index 90eb21d9b..19b872a9f 100644 --- a/Machines/Utility/Typer.hpp +++ b/Machines/Utility/Typer.hpp @@ -28,6 +28,18 @@ class CharacterMapper { /// @returns The EndSequence-terminated sequence of keys that would cause @c character to be typed. virtual uint16_t *sequence_for_character(char character) = 0; + /// The typer will automatically reset all keys in between each sequence that it types. + /// By default it will pause for one key's duration when doing so. Character mappers + /// can eliminate that pause by overriding this method. + /// @returns @c true if the typer should pause after performing a reset; @c false otherwise. + virtual bool needs_pause_after_reset_all_keys() { return true; } + + /// The typer will pause between every entry in a keyboard sequence. On some machines + /// that may not be necessary — it'll often depends on whether the machine needs time to + /// observe a modifier like shift before it sees the actual keypress. + /// @returns @c true if the typer should pause after forwarding @c key; @c false otherwise. + virtual bool needs_pause_after_key(uint16_t key) { return true; } + protected: typedef uint16_t KeySequence[16]; @@ -81,7 +93,7 @@ class Typer { Delegate *delegate_; std::unique_ptr character_mapper_; - bool try_type_next_character(); + uint16_t try_type_next_character(); const uint16_t *sequence_for_character(char) const; }; From 8a5c4e384afdd88267da4de6ab68116786bfa906 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 18:13:05 -0500 Subject: [PATCH 03/28] Minimises typer timing. --- Machines/AmstradCPC/AmstradCPC.cpp | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index a6b4a2375..517e75cfa 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -1052,6 +1052,7 @@ template class ConcreteMachine: /// Wires virtual-dispatched CRTMachine run_for requests to the static Z80 method. void run_for(const Cycles cycles) final { + has_run_ = true; z80_.run_for(cycles); } @@ -1079,16 +1080,20 @@ template class ConcreteMachine: // MARK: - Keyboard void type_string(const std::string &string) final { - std::unique_ptr mapper(new CharacterMapper()); - Utility::TypeRecipient::add_typer(string, std::move(mapper)); + if(typer_) { + typer_->append(string); + } else { + std::unique_ptr mapper(new CharacterMapper()); + Utility::TypeRecipient::add_typer(string, std::move(mapper)); + } } HalfCycles get_typer_delay() final { - return Cycles(4000000); // Wait 1 second before typing. + return has_run_ ? Cycles(0) : Cycles(3'400'000); } HalfCycles get_typer_frequency() final { - return Cycles(160000); // Type one character per frame. + return Cycles(80'000); // Perform one key transition per frame. } // See header; sets a key as either pressed or released. @@ -1231,7 +1236,8 @@ template class ConcreteMachine: KeyboardState key_state_; AmstradCPC::KeyboardMapper keyboard_mapper_; - uint8_t ram_[1024 * 1024]; + bool has_run_ = false; + uint8_t ram_[128 * 1024]; }; } From 4572c86f0f33ca62e8bb6a9a6a0c77b4ee1a1912 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 18:17:39 -0500 Subject: [PATCH 04/28] Adds a third keyboard input mode, which maps to posting things as a typer. --- .../Documents/MachineDocument.swift | 4 ++-- .../Mac/Clock Signal/Machine/CSMachine.h | 5 +++-- .../Mac/Clock Signal/Machine/CSMachine.mm | 19 +++++++++++++++---- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index e517113e5..14bc46168 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -532,7 +532,7 @@ class MachineDocument: // MARK: Joystick-via-the-keyboard selection @IBAction func useKeyboardAsKeyboard(_ sender: NSMenuItem?) { - machine.inputMode = .keyboard + machine.inputMode = .keyboardPhysical } @IBAction func useKeyboardAsJoystick(_ sender: NSMenuItem?) { @@ -551,7 +551,7 @@ class MachineDocument: return false } - menuItem.state = machine.inputMode == .keyboard ? .on : .off + menuItem.state = machine.inputMode == .keyboardPhysical ? .on : .off return true case #selector(self.useKeyboardAsJoystick): diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index d4830b7f5..a437e5f4d 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -28,8 +28,9 @@ typedef NS_ENUM(NSInteger, CSMachineVideoSignal) { }; typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) { - CSMachineKeyboardInputModeKeyboard, - CSMachineKeyboardInputModeJoystick + CSMachineKeyboardInputModeKeyboardPhysical, + CSMachineKeyboardInputModeKeyboardLogical, + CSMachineKeyboardInputModeJoystick, }; @interface CSMissingROM: NSObject diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index a671a87b7..4a125c679 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -201,9 +201,10 @@ struct ActivityObserver: public Activity::Observer { return nil; } + // Use the keyboard as a joystick if the machine has no keyboard, or if it has a 'non-exclusive' keyboard. _inputMode = (_machine->keyboard_machine() && _machine->keyboard_machine()->get_keyboard().is_exclusive()) - ? CSMachineKeyboardInputModeKeyboard : CSMachineKeyboardInputModeJoystick; + ? CSMachineKeyboardInputModeKeyboardPhysical : CSMachineKeyboardInputModeJoystick; _leds = [[NSMutableArray alloc] init]; Activity::Source *const activity_source = _machine->activity_source(); @@ -429,7 +430,7 @@ struct ActivityObserver: public Activity::Observer { - (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed { auto keyboard_machine = _machine->keyboard_machine(); - if(keyboard_machine && (self.inputMode == CSMachineKeyboardInputModeKeyboard || !keyboard_machine->get_keyboard().is_exclusive())) { + if(keyboard_machine && (self.inputMode != CSMachineKeyboardInputModeJoystick || !keyboard_machine->get_keyboard().is_exclusive())) { Inputs::Keyboard::Key mapped_key = Inputs::Keyboard::Key::Help; // Make an innocuous default guess. #define BIND(source, dest) case source: mapped_key = Inputs::Keyboard::Key::dest; break; // Connect the Carbon-era Mac keyboard scancodes to Clock Signal's 'universal' enumeration in order @@ -503,9 +504,19 @@ struct ActivityObserver: public Activity::Observer { } } - @synchronized(self) { - keyboard.set_key_pressed(mapped_key, pressedKey, isPressed); + if(self.inputMode == CSMachineKeyboardInputModeKeyboardLogical) { + if(isPressed) { + @synchronized(self) { + char string[2] = { pressedKey, 0 }; + keyboard_machine->type_string(string); + } + } + } else { + @synchronized(self) { + keyboard.set_key_pressed(mapped_key, pressedKey, isPressed); + } } + return; } } From d5e781e8e14bf7f1c4da06f558730f5de78ef24f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 18:30:58 -0500 Subject: [PATCH 05/28] Adds macOS UI option to use logical keyboard input. --- .../Mac/Clock Signal/Base.lproj/MainMenu.xib | 26 +++++++------------ .../Documents/MachineDocument.swift | 17 ++++++++++-- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib index 43bcadf11..36741cd62 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -121,18 +121,6 @@ - - - - - - - - - - - - @@ -176,9 +164,14 @@ - + - + + + + + + @@ -227,6 +220,7 @@ + diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index 14bc46168..f60789319 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -531,10 +531,14 @@ class MachineDocument: } // MARK: Joystick-via-the-keyboard selection - @IBAction func useKeyboardAsKeyboard(_ sender: NSMenuItem?) { + @IBAction func useKeyboardAsPhysicalKeyboard(_ sender: NSMenuItem?) { machine.inputMode = .keyboardPhysical } + @IBAction func useKeyboardAsLogicalKeyboard(_ sender: NSMenuItem?) { + machine.inputMode = .keyboardLogical + } + @IBAction func useKeyboardAsJoystick(_ sender: NSMenuItem?) { machine.inputMode = .joystick } @@ -545,7 +549,7 @@ class MachineDocument: override func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool { if let menuItem = item as? NSMenuItem { switch item.action { - case #selector(self.useKeyboardAsKeyboard): + case #selector(self.useKeyboardAsPhysicalKeyboard): if machine == nil || !machine.hasExclusiveKeyboard { menuItem.state = .off return false @@ -554,6 +558,15 @@ class MachineDocument: menuItem.state = machine.inputMode == .keyboardPhysical ? .on : .off return true + case #selector(self.useKeyboardAsLogicalKeyboard): + if machine == nil || !machine.hasExclusiveKeyboard { + menuItem.state = .off + return false + } + + menuItem.state = machine.inputMode == .keyboardLogical ? .on : .off + return true + case #selector(self.useKeyboardAsJoystick): if machine == nil || !machine.hasJoystick { menuItem.state = .off From f25683ebec44e5934ce95c324d1a75ffd3257531 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 18:35:13 -0500 Subject: [PATCH 06/28] Fixes off-by-one range test. --- Machines/Utility/Typer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Machines/Utility/Typer.cpp b/Machines/Utility/Typer.cpp index 3c35ab0f7..549ad728c 100644 --- a/Machines/Utility/Typer.cpp +++ b/Machines/Utility/Typer.cpp @@ -128,7 +128,7 @@ bool Typer::type_next_character() { uint16_t *CharacterMapper::table_lookup_sequence_for_character(KeySequence *sequences, std::size_t length, char character) { std::size_t ucharacter = static_cast((unsigned char)character); - if(ucharacter > (length / sizeof(KeySequence))) return nullptr; + if(ucharacter >= (length / sizeof(KeySequence))) return nullptr; if(sequences[ucharacter][0] == KeyboardMachine::MappedMachine::KeyNotMapped) return nullptr; return sequences[ucharacter]; } From 54b3e511e9c76e55307750b36671688618c38bc0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 18:40:41 -0500 Subject: [PATCH 07/28] Extends mapping slightly for potential duplicate delete and return. --- Machines/AmstradCPC/Keyboard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Machines/AmstradCPC/Keyboard.cpp b/Machines/AmstradCPC/Keyboard.cpp index 58340d4cc..8b49d9d61 100644 --- a/Machines/AmstradCPC/Keyboard.cpp +++ b/Machines/AmstradCPC/Keyboard.cpp @@ -85,7 +85,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) { /* ACK */ X, /* BEL */ X, /* BS */ KEYS(KeyDelete), /* HT */ X, /* LF */ KEYS(KeyReturn), /* VT */ X, - /* FF */ X, /* CR */ X, + /* FF */ X, /* CR */ KEYS(KeyReturn), /* SO */ X, /* SI */ X, /* DLE */ X, /* DC1 */ X, /* DC2 */ X, /* DC3 */ X, @@ -142,7 +142,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) { /* x */ KEYS(KeyX), /* y */ KEYS(KeyY), /* z */ KEYS(KeyZ), /* { */ X, /* | */ SHIFT(KeyAt), /* } */ X, - /* ~ */ X + /* ~ */ X, /* DEL */ KEYS(KeyDelete), }; #undef KEYS #undef SHIFT From 346d80e30bf50ad45a78707361a15af8d274f24b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 18:51:55 -0500 Subject: [PATCH 08/28] Corrects phase counting for machines that pause after clear. Which is all of them by default. --- Machines/Utility/Typer.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Machines/Utility/Typer.cpp b/Machines/Utility/Typer.cpp index 549ad728c..b0d06e3c1 100644 --- a/Machines/Utility/Typer.cpp +++ b/Machines/Utility/Typer.cpp @@ -81,27 +81,28 @@ uint16_t Typer::try_type_next_character() { return 0; } + // Advance phase. + ++phase_; + // If this is the start of the output sequence, start with a reset all keys. // Then pause unless the caracter mapper says not to. - if(!phase_) { + if(phase_ == 1) { delegate_->clear_all_keys(); if(character_mapper_->needs_pause_after_reset_all_keys()) { return 0xffff; // Arbitrarily. Anything non-zero will do. } + ++phase_; } - // Advance phase. - ++phase_; - // If the sequence is over, stop. - if(sequence[phase_ - 1] == KeyboardMachine::MappedMachine::KeyEndSequence) { + if(sequence[phase_ - 2] == KeyboardMachine::MappedMachine::KeyEndSequence) { return 0; } // Otherwise, type the key. - delegate_->set_key_state(sequence[phase_ - 1], true); + delegate_->set_key_state(sequence[phase_ - 2], true); - return sequence[phase_ - 1]; + return sequence[phase_ - 2]; } bool Typer::type_next_character() { From 99229df01721b0bdfd0e3229eb6c0fdfd086d27c Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 18:52:12 -0500 Subject: [PATCH 09/28] Slightly improves syntax. --- Machines/AmstradCPC/AmstradCPC.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 517e75cfa..243aef88c 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -1083,8 +1083,7 @@ template class ConcreteMachine: if(typer_) { typer_->append(string); } else { - std::unique_ptr mapper(new CharacterMapper()); - Utility::TypeRecipient::add_typer(string, std::move(mapper)); + Utility::TypeRecipient::add_typer(string, std::make_unique()); } } From 672c59f970e38c3de46beb95300d7e6ed87b1d9f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 18:52:47 -0500 Subject: [PATCH 10/28] Adds use of `append` with typer. --- Machines/Electron/Electron.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index a5c890207..8caeaf437 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -346,10 +346,10 @@ class ConcreteMachine: } } - cycles_since_display_update_ += Cycles(static_cast(cycles)); - cycles_since_audio_update_ += Cycles(static_cast(cycles)); + cycles_since_display_update_ += Cycles(int(cycles)); + cycles_since_audio_update_ += Cycles(int(cycles)); if(cycles_since_audio_update_ > Cycles(16384)) update_audio(); - tape_.run_for(Cycles(static_cast(cycles))); + tape_.run_for(Cycles(int(cycles))); cycles_until_display_interrupt_ -= cycles; if(cycles_until_display_interrupt_ < 0) { @@ -358,8 +358,8 @@ class ConcreteMachine: queue_next_display_interrupt(); } - if(typer_) typer_->run_for(Cycles(static_cast(cycles))); - if(plus3_) plus3_->run_for(Cycles(4*static_cast(cycles))); + if(typer_) typer_->run_for(Cycles(int(cycles))); + if(plus3_) plus3_->run_for(Cycles(4*int(cycles))); if(shift_restart_counter_) { shift_restart_counter_ -= cycles; if(shift_restart_counter_ <= 0) { @@ -413,7 +413,11 @@ class ConcreteMachine: } void type_string(const std::string &string) final { - Utility::TypeRecipient::add_typer(string, std::make_unique()); + if(typer_) { + typer_->append(string); + } else { + Utility::TypeRecipient::add_typer(string, std::make_unique()); + } } KeyboardMapper *get_keyboard_mapper() final { From 41d20623423ebc821f07401dec840d597ed9e1cf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 19:22:54 -0500 Subject: [PATCH 11/28] Ensures that sequences of the same character are broken up properly. --- Machines/Utility/Typer.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Machines/Utility/Typer.cpp b/Machines/Utility/Typer.cpp index b0d06e3c1..fcad3a060 100644 --- a/Machines/Utility/Typer.cpp +++ b/Machines/Utility/Typer.cpp @@ -49,8 +49,12 @@ void Typer::run_for(const HalfCycles duration) { void Typer::append(const std::string &string) { // Remove any characters that are already completely done; // otherwise things may accumulate here indefinitely. - string_.erase(string_.begin(), string_.begin() + ssize_t(string_pointer_)); - string_pointer_ = 0; + // Note that sequence_for_character may seek to look one backwards, + // so keep 'the character before' if there was one. + if(string_pointer_ > 1) { + string_.erase(string_.begin(), string_.begin() + ssize_t(string_pointer_) - 1); + string_pointer_ = 1; + } // If the final character in the string is not Typer::EndString // then this machine doesn't need Begin and End, so don't worry about it. @@ -85,10 +89,13 @@ uint16_t Typer::try_type_next_character() { ++phase_; // If this is the start of the output sequence, start with a reset all keys. - // Then pause unless the caracter mapper says not to. + // Then pause if either: (i) the machine requires it; or (ii) this is the same + // character that was just typed, in which case the gap in presses will need to + // be clear. if(phase_ == 1) { delegate_->clear_all_keys(); - if(character_mapper_->needs_pause_after_reset_all_keys()) { + if(character_mapper_->needs_pause_after_reset_all_keys() || + (string_pointer_ > 0 && string_[string_pointer_ - 1] == string_[string_pointer_])) { return 0xffff; // Arbitrarily. Anything non-zero will do. } ++phase_; From 3c103506c976179324e38cfd7fdc2f795b54c189 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 19:26:15 -0500 Subject: [PATCH 12/28] Optimises Electron typer speed. --- Machines/Electron/Electron.cpp | 4 ++-- Machines/Electron/Keyboard.cpp | 8 ++++++-- Machines/Electron/Keyboard.hpp | 5 ++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 8caeaf437..7bf5d5505 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -405,11 +405,11 @@ class ConcreteMachine: } HalfCycles get_typer_delay() final { - return m6502_.get_is_resetting() ? Cycles(625*25*128) : Cycles(0); // wait one second if resetting + return m6502_.get_is_resetting() ? Cycles(750'000) : Cycles(0); } HalfCycles get_typer_frequency() final { - return Cycles(625*128*2); // accept a new character every two frames + return Cycles(60'000); } void type_string(const std::string &string) final { diff --git a/Machines/Electron/Keyboard.cpp b/Machines/Electron/Keyboard.cpp index e54454171..1ecd358f4 100644 --- a/Machines/Electron/Keyboard.cpp +++ b/Machines/Electron/Keyboard.cpp @@ -66,7 +66,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) { /* ACK */ X, /* BEL */ X, /* BS */ KEYS(KeyDelete), /* HT */ X, /* LF */ KEYS(KeyReturn), /* VT */ X, - /* FF */ X, /* CR */ X, + /* FF */ X, /* CR */ KEYS(KeyReturn), /* SO */ X, /* SI */ X, /* DLE */ X, /* DC1 */ X, /* DC2 */ X, /* DC3 */ X, @@ -123,7 +123,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) { /* x */ SHIFT(KeyX), /* y */ SHIFT(KeyY), /* z */ SHIFT(KeyZ), /* { */ CTRL(KeyUp), /* | */ SHIFT(KeyRight), /* } */ CTRL(KeyDown), - /* ~ */ CTRL(KeyLeft) + /* ~ */ CTRL(KeyLeft), /* DEL */ KEYS(KeyDelete), }; #undef KEYS #undef SHIFT @@ -131,3 +131,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) { return table_lookup_sequence_for_character(key_sequences, sizeof(key_sequences), character); } + +bool CharacterMapper::needs_pause_after_key(uint16_t key) { + return key != KeyControl && key != KeyShift && key != KeyFunc; +} diff --git a/Machines/Electron/Keyboard.hpp b/Machines/Electron/Keyboard.hpp index 04a3b76cd..39c36d3ac 100644 --- a/Machines/Electron/Keyboard.hpp +++ b/Machines/Electron/Keyboard.hpp @@ -38,7 +38,10 @@ struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { }; struct CharacterMapper: public ::Utility::CharacterMapper { - uint16_t *sequence_for_character(char character); + uint16_t *sequence_for_character(char character) override; + + bool needs_pause_after_reset_all_keys() override { return false; } + bool needs_pause_after_key(uint16_t key) override; }; }; From b971e2a42c36787507e92ed935cc5c4695ac82d4 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 19:58:25 -0500 Subject: [PATCH 13/28] Adds get_is_resetting to the Z80, eliminating the CPC's custom version. --- Machines/AmstradCPC/AmstradCPC.cpp | 3 +-- Processors/6502/Implementation/6502Implementation.hpp | 2 +- Processors/Z80/Implementation/Z80Storage.cpp | 4 ++++ Processors/Z80/Z80.hpp | 7 +++++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 243aef88c..71e659ab3 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -1052,7 +1052,6 @@ template class ConcreteMachine: /// Wires virtual-dispatched CRTMachine run_for requests to the static Z80 method. void run_for(const Cycles cycles) final { - has_run_ = true; z80_.run_for(cycles); } @@ -1088,7 +1087,7 @@ template class ConcreteMachine: } HalfCycles get_typer_delay() final { - return has_run_ ? Cycles(0) : Cycles(3'400'000); + return z80_.get_is_resetting() ? Cycles(3'400'000) : Cycles(0); } HalfCycles get_typer_frequency() final { diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index c32f544e2..be8c07d66 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -705,7 +705,7 @@ void ProcessorBase::set_reset_line(bool active) { } bool ProcessorBase::get_is_resetting() { - return !!(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn)); + return interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn); } void ProcessorBase::set_power_on(bool active) { diff --git a/Processors/Z80/Implementation/Z80Storage.cpp b/Processors/Z80/Implementation/Z80Storage.cpp index 66c13cb62..14bb76de8 100644 --- a/Processors/Z80/Implementation/Z80Storage.cpp +++ b/Processors/Z80/Implementation/Z80Storage.cpp @@ -550,3 +550,7 @@ bool ProcessorBase::is_starting_new_instruction() { current_instruction_page_ == &base_page_ && scheduled_program_counter_ == &base_page_.fetch_decode_execute[0]; } + +bool ProcessorBase::get_is_resetting() { + return request_status_ & (Interrupt::PowerOn | Interrupt::Reset); +} diff --git a/Processors/Z80/Z80.hpp b/Processors/Z80/Z80.hpp index d0ee4a1a9..718ebea27 100644 --- a/Processors/Z80/Z80.hpp +++ b/Processors/Z80/Z80.hpp @@ -219,6 +219,13 @@ class ProcessorBase: public ProcessorStorage { */ inline void set_reset_line(bool value); + /*! + Gets whether the Z80 would reset at the next opportunity. + + @returns @c true if the line is logically active; @c false otherwise. + */ + bool get_is_resetting(); + /*! This emulation automatically sets itself up in power-on state at creation, which has the effect of triggering a reset at the first opportunity. Use @c reset_power_on to disable that behaviour. From 6e4bd4f5052a2c20a2c1adde7e4f074a08a3cda8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 19:58:56 -0500 Subject: [PATCH 14/28] Ensures new text is appended to any existing buffer. TODO: move this into add_typer? --- Machines/Commodore/Vic-20/Vic20.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 2440c1fdb..072d8612a 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -645,7 +645,11 @@ class ConcreteMachine: } void type_string(const std::string &string) final { - Utility::TypeRecipient::add_typer(string, std::make_unique()); + if(typer_) { + typer_->append(string); + } else { + Utility::TypeRecipient::add_typer(string, std::make_unique()); + } } void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape) final { From b33f568fddcf32a3fd380d1255434a022231b2e6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 19:59:51 -0500 Subject: [PATCH 15/28] Makes basic typing adaptations. --- Machines/ZX8081/Keyboard.cpp | 4 ++-- Machines/ZX8081/Keyboard.hpp | 2 +- Machines/ZX8081/ZX8081.cpp | 15 ++++++++++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Machines/ZX8081/Keyboard.cpp b/Machines/ZX8081/Keyboard.cpp index e3d177164..f8bc3f5a4 100644 --- a/Machines/ZX8081/Keyboard.cpp +++ b/Machines/ZX8081/Keyboard.cpp @@ -46,7 +46,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) { /* ACK */ X, /* BEL */ X, /* BS */ SHIFT(Key0), /* HT */ X, /* LF */ KEYS(KeyEnter), /* VT */ X, - /* FF */ X, /* CR */ X, + /* FF */ X, /* CR */ KEYS(KeyEnter), /* SO */ X, /* SI */ X, /* DLE */ X, /* DC1 */ X, /* DC2 */ X, /* DC3 */ X, @@ -112,7 +112,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) { /* ACK */ X, /* BEL */ X, /* BS */ SHIFT(Key0), /* HT */ X, /* LF */ KEYS(KeyEnter), /* VT */ X, - /* FF */ X, /* CR */ X, + /* FF */ X, /* CR */ KEYS(KeyEnter), /* SO */ X, /* SI */ X, /* DLE */ X, /* DC1 */ X, /* DC2 */ X, /* DC3 */ X, diff --git a/Machines/ZX8081/Keyboard.hpp b/Machines/ZX8081/Keyboard.hpp index 412281a81..da878d61e 100644 --- a/Machines/ZX8081/Keyboard.hpp +++ b/Machines/ZX8081/Keyboard.hpp @@ -32,7 +32,7 @@ struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { class CharacterMapper: public ::Utility::CharacterMapper { public: CharacterMapper(bool is_zx81); - uint16_t *sequence_for_character(char character); + uint16_t *sequence_for_character(char character) override; private: bool is_zx81_; diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 16d1f1db5..bc2ec98c3 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -340,7 +340,11 @@ template class ConcreteMachine: } void type_string(const std::string &string) final { - Utility::TypeRecipient::add_typer(string, std::make_unique(is_zx81)); + if(typer_) { + typer_->append(string); + } else { + Utility::TypeRecipient::add_typer(string, std::make_unique(is_zx81)); + } } // MARK: - Keyboard @@ -373,8 +377,13 @@ template class ConcreteMachine: } // MARK: - Typer timing - HalfCycles get_typer_delay() final { return Cycles(7000000); } - HalfCycles get_typer_frequency() final { return Cycles(390000); } + HalfCycles get_typer_delay() final { + return z80_.get_is_resetting() ? Cycles(7'000'000) : Cycles(0); + } + + HalfCycles get_typer_frequency() final { + return Cycles(390'000); + } KeyboardMapper *get_keyboard_mapper() final { return &keyboard_mapper_; From 32b2026734c284ee0224c657df36cb684b7cab1b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 20:01:21 -0500 Subject: [PATCH 16/28] Alters shortcut. --- OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib index 36741cd62..4b186732e 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib @@ -169,7 +169,7 @@ - + From 86a09b5e7ddccd65bd7dac8bd8090ef232d578ae Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 22:31:45 -0500 Subject: [PATCH 17/28] Slightly improves ZX80 and ZX81 typing speed. --- Machines/ZX8081/Keyboard.cpp | 4 ++++ Machines/ZX8081/Keyboard.hpp | 2 ++ Machines/ZX8081/ZX8081.cpp | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Machines/ZX8081/Keyboard.cpp b/Machines/ZX8081/Keyboard.cpp index f8bc3f5a4..a84fac93d 100644 --- a/Machines/ZX8081/Keyboard.cpp +++ b/Machines/ZX8081/Keyboard.cpp @@ -179,3 +179,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) { else return table_lookup_sequence_for_character(zx80_key_sequences, sizeof(zx80_key_sequences), character); } + +bool CharacterMapper::needs_pause_after_key(uint16_t key) { + return key != KeyShift; +} diff --git a/Machines/ZX8081/Keyboard.hpp b/Machines/ZX8081/Keyboard.hpp index da878d61e..53482542f 100644 --- a/Machines/ZX8081/Keyboard.hpp +++ b/Machines/ZX8081/Keyboard.hpp @@ -34,6 +34,8 @@ class CharacterMapper: public ::Utility::CharacterMapper { CharacterMapper(bool is_zx81); uint16_t *sequence_for_character(char character) override; + bool needs_pause_after_key(uint16_t key) override; + private: bool is_zx81_; }; diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index bc2ec98c3..9c67055bc 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -382,7 +382,7 @@ template class ConcreteMachine: } HalfCycles get_typer_frequency() final { - return Cycles(390'000); + return Cycles(146'250); } KeyboardMapper *get_keyboard_mapper() final { From 560394fead7e3a8a3fc35f418bc01b2a9ed85cc0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 22:37:15 -0500 Subject: [PATCH 18/28] Ensures keys without symbols are forwarded. --- .../Mac/Clock Signal/Machine/CSMachine.mm | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 4a125c679..db83751cd 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -504,17 +504,23 @@ struct ActivityObserver: public Activity::Observer { } } + // If this is logical mode and this key maps to a symbol, supply it + // as something to type. If this isn't logical mode, or this key doesn't + // map to a symbol, pass it along as a standard press. if(self.inputMode == CSMachineKeyboardInputModeKeyboardLogical) { - if(isPressed) { - @synchronized(self) { - char string[2] = { pressedKey, 0 }; - keyboard_machine->type_string(string); + if(pressedKey) { + if(isPressed) { + @synchronized(self) { + char string[2] = { pressedKey, 0 }; + keyboard_machine->type_string(string); + } } + return; } - } else { - @synchronized(self) { - keyboard.set_key_pressed(mapped_key, pressedKey, isPressed); - } + } + + @synchronized(self) { + keyboard.set_key_pressed(mapped_key, pressedKey, isPressed); } return; From 0705a99ea0fd281ee0c59dd530c00d5f7ba01d81 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 22:51:42 -0500 Subject: [PATCH 19/28] Adds a virtual delete key to the ZX80 and ZX81. --- Machines/ZX8081/Keyboard.cpp | 3 +++ Machines/ZX8081/Keyboard.hpp | 3 +++ Machines/ZX8081/ZX8081.cpp | 21 +++++++++++++++++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/Machines/ZX8081/Keyboard.cpp b/Machines/ZX8081/Keyboard.cpp index a84fac93d..fde1b7938 100644 --- a/Machines/ZX8081/Keyboard.cpp +++ b/Machines/ZX8081/Keyboard.cpp @@ -28,6 +28,9 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(FullStop, KeyDot); BIND(Enter, KeyEnter); BIND(Space, KeySpace); + + // Virtual keys follow. + BIND(Backspace, KeyDelete); } #undef BIND return KeyboardMachine::MappedMachine::KeyNotMapped; diff --git a/Machines/ZX8081/Keyboard.hpp b/Machines/ZX8081/Keyboard.hpp index 53482542f..2eca45080 100644 --- a/Machines/ZX8081/Keyboard.hpp +++ b/Machines/ZX8081/Keyboard.hpp @@ -23,6 +23,9 @@ enum Key: uint16_t { KeyP = 0x0500 | 0x01, KeyO = 0x0500 | 0x02, KeyI = 0x0500 | 0x04, KeyU = 0x0500 | 0x08, KeyY = 0x0500 | 0x10, KeyEnter = 0x0600 | 0x01, KeyL = 0x0600 | 0x02, KeyK = 0x0600 | 0x04, KeyJ = 0x0600 | 0x08, KeyH = 0x0600 | 0x10, KeySpace = 0x0700 | 0x01, KeyDot = 0x0700 | 0x02, KeyM = 0x0700 | 0x04, KeyN = 0x0700 | 0x08, KeyB = 0x0700 | 0x10, + + // Add some virtual keys; these do not exist on a real ZX80 or ZX81. They're just a convenience. + KeyDelete = 0x0801, }; struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 9c67055bc..4bc876763 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -349,10 +349,23 @@ template class ConcreteMachine: // MARK: - Keyboard void set_key_state(uint16_t key, bool is_pressed) final { - if(is_pressed) - key_states_[key >> 8] &= static_cast(~key); - else - key_states_[key >> 8] |= static_cast(key); + const auto line = key >> 8; + + // Check for special cases. + if(line == 8) { + switch(key) { + case KeyDelete: + // Map delete to shift+0. + set_key_state(KeyShift, is_pressed); + set_key_state(Key0, is_pressed); + break; + } + } else { + if(is_pressed) + key_states_[line] &= uint8_t(~key); + else + key_states_[line] |= uint8_t(key); + } } void clear_all_keys() final { From 77c0cc8b5fab28a4c1437d03fe231162e83814e5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 23:07:14 -0500 Subject: [PATCH 20/28] Provisionally adds logical keyboard support to SDL. --- OSBindings/SDL/main.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index dec48042a..9bbfaad5d 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -469,7 +469,7 @@ int main(int argc, char *argv[]) { ParsedArguments arguments = parse_arguments(argc, argv); // This may be printed either as - const std::string usage_suffix = " [file] [OPTIONS] [--rompath={path to ROMs}] [--speed={speed multiplier, e.g. 1.5}]"; + const std::string usage_suffix = " [file] [OPTIONS] [--rompath={path to ROMs}] [--speed={speed multiplier, e.g. 1.5}] [--logical-keyboard]"; // Print a help message if requested. if(arguments.selections.find("help") != arguments.selections.end() || arguments.selections.find("h") != arguments.selections.end()) { @@ -610,6 +610,9 @@ int main(int argc, char *argv[]) { } } + // Check whether a 'logical' keyboard has been requested. + const bool logical_keyboard = arguments.selections.find("logical_keyboard") != arguments.selections.end(); + // Wire up the best-effort updater, its delegate, and the speaker delegate. machine_runner.machine = machine.get(); machine_runner.machine_mutex = &machine_mutex; @@ -890,14 +893,21 @@ int main(int argc, char *argv[]) { const bool is_pressed = event.type == SDL_KEYDOWN; if(keyboard_machine) { + // Grab the key's symbol. + char key_value = '\0'; + const char *key_name = SDL_GetKeyName(event.key.keysym.sym); + if(key_name[0] >= 0) key_value = key_name[0]; + + // If a logical mapping was selected and a symbol was found, type it. + if(logical_keyboard && key_value != '\0') { + keyboard_machine->type_string(string); + break; + } + + // Otherwise, supply as a normal keypress. Inputs::Keyboard::Key key = Inputs::Keyboard::Key::Space; if(!KeyboardKeyForSDLScancode(event.key.keysym.scancode, key)) break; - if(keyboard_machine->get_keyboard().observed_keys().find(key) != keyboard_machine->get_keyboard().observed_keys().end()) { - char key_value = '\0'; - const char *key_name = SDL_GetKeyName(event.key.keysym.sym); - if(key_name[0] >= 0) key_value = key_name[0]; - keyboard_machine->get_keyboard().set_key_pressed(key, key_value, is_pressed); break; } From 9273e9b6ed560a8cd14420790b9c93e0e4273f11 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 29 Feb 2020 23:11:02 -0500 Subject: [PATCH 21/28] Adds a second virtual key, for break. --- Machines/ZX8081/Keyboard.cpp | 1 + Machines/ZX8081/Keyboard.hpp | 1 + Machines/ZX8081/ZX8081.cpp | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/Machines/ZX8081/Keyboard.cpp b/Machines/ZX8081/Keyboard.cpp index fde1b7938..5dac570e4 100644 --- a/Machines/ZX8081/Keyboard.cpp +++ b/Machines/ZX8081/Keyboard.cpp @@ -31,6 +31,7 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { // Virtual keys follow. BIND(Backspace, KeyDelete); + BIND(Escape, KeyBreak); } #undef BIND return KeyboardMachine::MappedMachine::KeyNotMapped; diff --git a/Machines/ZX8081/Keyboard.hpp b/Machines/ZX8081/Keyboard.hpp index 2eca45080..294787c9b 100644 --- a/Machines/ZX8081/Keyboard.hpp +++ b/Machines/ZX8081/Keyboard.hpp @@ -26,6 +26,7 @@ enum Key: uint16_t { // Add some virtual keys; these do not exist on a real ZX80 or ZX81. They're just a convenience. KeyDelete = 0x0801, + KeyBreak = 0x0802, }; struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 4bc876763..c628a74ce 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -359,6 +359,12 @@ template class ConcreteMachine: set_key_state(KeyShift, is_pressed); set_key_state(Key0, is_pressed); break; + + case KeyBreak: + // Map break to shift+space. + set_key_state(KeyShift, is_pressed); + set_key_state(KeySpace, is_pressed); + break; } } else { if(is_pressed) From 611182910a0b3f279bb224cb3d5a768c9cc6e20f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Mar 2020 18:44:26 -0500 Subject: [PATCH 22/28] Slightly rejigs character mapper ownership. --- Machines/AmstradCPC/AmstradCPC.cpp | 8 ++------ Machines/Commodore/Vic-20/Vic20.cpp | 8 ++------ Machines/Electron/Electron.cpp | 8 ++------ Machines/Oric/Oric.cpp | 1 - Machines/Utility/Typer.cpp | 10 +++++----- Machines/Utility/Typer.hpp | 16 ++++++++++++---- Machines/ZX8081/ZX8081.cpp | 9 +++------ 7 files changed, 26 insertions(+), 34 deletions(-) diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 71e659ab3..9a8fa5f0e 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -786,7 +786,7 @@ template class ConcreteMachine: public CRTMachine::Machine, public MediaTarget::Machine, public KeyboardMachine::MappedMachine, - public Utility::TypeRecipient, + public Utility::TypeRecipient, public CPU::Z80::BusHandler, public ClockingHint::Observer, public Configurable::Device, @@ -1079,11 +1079,7 @@ template class ConcreteMachine: // MARK: - Keyboard void type_string(const std::string &string) final { - if(typer_) { - typer_->append(string); - } else { - Utility::TypeRecipient::add_typer(string, std::make_unique()); - } + Utility::TypeRecipient::add_typer(string); } HalfCycles get_typer_delay() final { diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 072d8612a..beebf4b35 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -287,7 +287,7 @@ class ConcreteMachine: public Configurable::Device, public CPU::MOS6502::BusHandler, public MOS::MOS6522::IRQDelegatePortHandler::Delegate, - public Utility::TypeRecipient, + public Utility::TypeRecipient, public Storage::Tape::BinaryTapePlayer::Delegate, public Machine, public ClockingHint::Observer, @@ -645,11 +645,7 @@ class ConcreteMachine: } void type_string(const std::string &string) final { - if(typer_) { - typer_->append(string); - } else { - Utility::TypeRecipient::add_typer(string, std::make_unique()); - } + Utility::TypeRecipient::add_typer(string); } void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape) final { diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 7bf5d5505..76a3d8fff 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -46,7 +46,7 @@ class ConcreteMachine: public Configurable::Device, public CPU::MOS6502::BusHandler, public Tape::Delegate, - public Utility::TypeRecipient, + public Utility::TypeRecipient, public Activity::Source { public: ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : @@ -413,11 +413,7 @@ class ConcreteMachine: } void type_string(const std::string &string) final { - if(typer_) { - typer_->append(string); - } else { - Utility::TypeRecipient::add_typer(string, std::make_unique()); - } + Utility::TypeRecipient::add_typer(string); } KeyboardMapper *get_keyboard_mapper() final { diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index b25162f6e..081d9ef58 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -224,7 +224,6 @@ template class Co public Configurable::Device, public CPU::MOS6502::BusHandler, public MOS::MOS6522::IRQDelegatePortHandler::Delegate, - public Utility::TypeRecipient, public Storage::Tape::BinaryTapePlayer::Delegate, public DiskController::Delegate, public ClockingHint::Observer, diff --git a/Machines/Utility/Typer.cpp b/Machines/Utility/Typer.cpp index fcad3a060..a1a85aa88 100644 --- a/Machines/Utility/Typer.cpp +++ b/Machines/Utility/Typer.cpp @@ -10,11 +10,11 @@ using namespace Utility; -Typer::Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, std::unique_ptr character_mapper, Delegate *delegate) : +Typer::Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, CharacterMapper &character_mapper, Delegate *delegate) : frequency_(frequency), counter_(-delay), delegate_(delegate), - character_mapper_(std::move(character_mapper)) { + character_mapper_(character_mapper) { // Retain only those characters that actually map to something. if(sequence_for_character(Typer::BeginString)) { string_ += Typer::BeginString; @@ -71,7 +71,7 @@ void Typer::append(const std::string &string) { } const uint16_t *Typer::sequence_for_character(char c) const { - const uint16_t *const sequence = character_mapper_->sequence_for_character(c); + const uint16_t *const sequence = character_mapper_.sequence_for_character(c); if(!sequence || sequence[0] == KeyboardMachine::MappedMachine::KeyNotMapped) { return nullptr; } @@ -94,7 +94,7 @@ uint16_t Typer::try_type_next_character() { // be clear. if(phase_ == 1) { delegate_->clear_all_keys(); - if(character_mapper_->needs_pause_after_reset_all_keys() || + if(character_mapper_.needs_pause_after_reset_all_keys() || (string_pointer_ > 0 && string_[string_pointer_ - 1] == string_[string_pointer_])) { return 0xffff; // Arbitrarily. Anything non-zero will do. } @@ -124,7 +124,7 @@ bool Typer::type_next_character() { if(string_pointer_ == string_.size()) return false; } - if(character_mapper_->needs_pause_after_key(key_pressed)) { + if(character_mapper_.needs_pause_after_key(key_pressed)) { break; } } diff --git a/Machines/Utility/Typer.hpp b/Machines/Utility/Typer.hpp index 19b872a9f..31ef4ec5a 100644 --- a/Machines/Utility/Typer.hpp +++ b/Machines/Utility/Typer.hpp @@ -67,7 +67,7 @@ class Typer { virtual void typer_reset(Typer *typer) = 0; }; - Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, std::unique_ptr character_mapper, Delegate *delegate); + Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, CharacterMapper &character_mapper, Delegate *delegate); /// Advances for @c duration. void run_for(const HalfCycles duration); @@ -91,7 +91,7 @@ class Typer { int phase_ = 0; Delegate *delegate_; - std::unique_ptr character_mapper_; + CharacterMapper &character_mapper_; uint16_t try_type_next_character(); const uint16_t *sequence_for_character(char) const; @@ -101,11 +101,18 @@ class Typer { Provides a default base class for type recipients: classes that want to attach a single typer at a time and which may or may not want to nominate an initial delay and typing frequency. */ +template class TypeRecipient: public Typer::Delegate { protected: + template TypeRecipient(Args&&... args) : character_mapper(std::forward(args)...) {} + /// Attaches a typer to this class that will type @c string using @c character_mapper as a source. - void add_typer(const std::string &string, std::unique_ptr character_mapper) { - typer_ = std::make_unique(string, get_typer_delay(), get_typer_frequency(), std::move(character_mapper), this); + void add_typer(const std::string &string) { + if(!typer_) { + typer_ = std::make_unique(string, get_typer_delay(), get_typer_frequency(), character_mapper, this); + } else { + typer_->append(string); + } } /*! @@ -128,6 +135,7 @@ class TypeRecipient: public Typer::Delegate { private: std::unique_ptr previous_typer_; + CMApper character_mapper; }; } diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index c628a74ce..d82777bfe 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -62,11 +62,12 @@ template class ConcreteMachine: public MediaTarget::Machine, public KeyboardMachine::MappedMachine, public Configurable::Device, - public Utility::TypeRecipient, + public Utility::TypeRecipient, public CPU::Z80::BusHandler, public Machine { public: ConcreteMachine(const Analyser::Static::ZX8081::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) : + Utility::TypeRecipient(is_zx81), z80_(*this), tape_player_(ZX8081ClockRate), ay_(GI::AY38910::Personality::AY38910, audio_queue_), @@ -340,11 +341,7 @@ template class ConcreteMachine: } void type_string(const std::string &string) final { - if(typer_) { - typer_->append(string); - } else { - Utility::TypeRecipient::add_typer(string, std::make_unique(is_zx81)); - } + Utility::TypeRecipient::add_typer(string); } // MARK: - Keyboard From ed1809208828b636db2d581860488fb87b18693d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Mar 2020 20:25:12 -0500 Subject: [PATCH 23/28] Extends logic for when to fall back on standard keypress logic even in logical mode. --- .../MultiMachine/Implementation/MultiKeyboardMachine.cpp | 8 ++++++++ .../MultiMachine/Implementation/MultiKeyboardMachine.hpp | 1 + Machines/AmstradCPC/AmstradCPC.cpp | 4 ++++ Machines/Apple/AppleII/AppleII.cpp | 5 +++++ Machines/Commodore/Vic-20/Vic20.cpp | 4 ++++ Machines/Electron/Electron.cpp | 4 ++++ Machines/KeyboardMachine.hpp | 5 +++++ Machines/MSX/MSX.cpp | 5 +++++ Machines/Oric/Oric.cpp | 5 +++++ Machines/Utility/Typer.cpp | 1 + Machines/Utility/Typer.hpp | 8 ++++++++ Machines/ZX8081/ZX8081.cpp | 4 ++++ OSBindings/Mac/Clock Signal/Machine/CSMachine.mm | 8 ++++---- OSBindings/SDL/main.cpp | 3 ++- 14 files changed, 60 insertions(+), 5 deletions(-) diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp index 560d18be3..42d0c0835 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp @@ -36,6 +36,14 @@ void MultiKeyboardMachine::type_string(const std::string &string) { } } +bool MultiKeyboardMachine::can_type(char c) { + bool can_type = true; + for(const auto &machine: machines_) { + can_type &= machine->can_type(c); + } + return can_type; +} + Inputs::Keyboard &MultiKeyboardMachine::get_keyboard() { return keyboard_; } diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp index 274380002..a828546fe 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp @@ -51,6 +51,7 @@ class MultiKeyboardMachine: public KeyboardMachine::Machine { void clear_all_keys() final; void set_key_state(uint16_t key, bool is_pressed) final; void type_string(const std::string &) final; + bool can_type(char c) final; Inputs::Keyboard &get_keyboard() final; }; diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index 9a8fa5f0e..ec6565d74 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -1082,6 +1082,10 @@ template class ConcreteMachine: Utility::TypeRecipient::add_typer(string); } + bool can_type(char c) final { + return Utility::TypeRecipient::can_type(c); + } + HalfCycles get_typer_delay() final { return z80_.get_is_resetting() ? Cycles(3'400'000) : Cycles(0); } diff --git a/Machines/Apple/AppleII/AppleII.cpp b/Machines/Apple/AppleII/AppleII.cpp index 90f53048a..0e1a3c037 100644 --- a/Machines/Apple/AppleII/AppleII.cpp +++ b/Machines/Apple/AppleII/AppleII.cpp @@ -858,6 +858,11 @@ template class ConcreteMachine: string_serialiser_ = std::make_unique(string, true); } + bool can_type(char c) final { + // Make an effort to type the entire printable ASCII range. + return c >= 32 && c < 127; + } + // MARK:: Configuration options. std::vector> get_options() final { return Apple::II::get_options(); diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index beebf4b35..0697d5e96 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -648,6 +648,10 @@ class ConcreteMachine: Utility::TypeRecipient::add_typer(string); } + bool can_type(char c) final { + return Utility::TypeRecipient::can_type(c); + } + void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape) final { keyboard_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !tape->get_input()); } diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 76a3d8fff..89c6793b8 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -416,6 +416,10 @@ class ConcreteMachine: Utility::TypeRecipient::add_typer(string); } + bool can_type(char c) final { + return Utility::TypeRecipient::can_type(c); + } + KeyboardMapper *get_keyboard_mapper() final { return &keyboard_mapper_; } diff --git a/Machines/KeyboardMachine.hpp b/Machines/KeyboardMachine.hpp index 84fcd517f..e4af3adb1 100644 --- a/Machines/KeyboardMachine.hpp +++ b/Machines/KeyboardMachine.hpp @@ -45,6 +45,11 @@ class Machine: public KeyActions { */ virtual void type_string(const std::string &); + /*! + @returns @c true if this machine can type the character @c c as part of a @c type_string; @c false otherwise. + */ + virtual bool can_type(char c) { return false; } + /*! Provides a destination for keyboard input. */ diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 150f9eeb5..9f8fccb0d 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -369,6 +369,11 @@ class ConcreteMachine: ); } + bool can_type(char c) final { + // Make an effort to type the entire printable ASCII range. + return c >= 32 && c < 127; + } + // MARK: MSX::MemoryMap void map(int slot, std::size_t source_address, uint16_t destination_address, std::size_t length) final { assert(!(destination_address & 8191)); diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 081d9ef58..21d54f189 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -589,6 +589,11 @@ template class Co string_serialiser_ = std::make_unique(string, true); } + bool can_type(char c) final { + // Make an effort to type the entire printable ASCII range. + return c >= 32 && c < 127; + } + // DiskController::Delegate void disk_controller_did_change_paged_item(DiskController *controller) final { switch(controller->get_paged_item()) { diff --git a/Machines/Utility/Typer.cpp b/Machines/Utility/Typer.cpp index a1a85aa88..efdda2036 100644 --- a/Machines/Utility/Typer.cpp +++ b/Machines/Utility/Typer.cpp @@ -140,3 +140,4 @@ uint16_t *CharacterMapper::table_lookup_sequence_for_character(KeySequence *sequ if(sequences[ucharacter][0] == KeyboardMachine::MappedMachine::KeyNotMapped) return nullptr; return sequences[ucharacter]; } + diff --git a/Machines/Utility/Typer.hpp b/Machines/Utility/Typer.hpp index 31ef4ec5a..867d055c8 100644 --- a/Machines/Utility/Typer.hpp +++ b/Machines/Utility/Typer.hpp @@ -115,6 +115,14 @@ class TypeRecipient: public Typer::Delegate { } } + /*! + @returns @c true if the character mapper provides a mapping for @c c; @c false otherwise. + */ + bool can_type(char c) { + const auto sequence = character_mapper.sequence_for_character(c); + return sequence && sequence[0] != KeyboardMachine::MappedMachine::KeyNotMapped; + } + /*! Provided in order to conform to that part of the Typer::Delegate interface that goes above and beyond KeyboardMachine::Machine; responds to the end of typing by clearing all keys. diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index d82777bfe..8afde474e 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -344,6 +344,10 @@ template class ConcreteMachine: Utility::TypeRecipient::add_typer(string); } + bool can_type(char c) final { + return Utility::TypeRecipient::can_type(c); + } + // MARK: - Keyboard void set_key_state(uint16_t key, bool is_pressed) final { const auto line = key >> 8; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index db83751cd..35f5dfb81 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -508,14 +508,14 @@ struct ActivityObserver: public Activity::Observer { // as something to type. If this isn't logical mode, or this key doesn't // map to a symbol, pass it along as a standard press. if(self.inputMode == CSMachineKeyboardInputModeKeyboardLogical) { - if(pressedKey) { - if(isPressed) { - @synchronized(self) { + @synchronized(self) { + if(pressedKey && keyboard_machine->can_type(pressedKey)) { + if(isPressed) { char string[2] = { pressedKey, 0 }; keyboard_machine->type_string(string); } + return; } - return; } } diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index 9bbfaad5d..63c13feaf 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -899,7 +899,8 @@ int main(int argc, char *argv[]) { if(key_name[0] >= 0) key_value = key_name[0]; // If a logical mapping was selected and a symbol was found, type it. - if(logical_keyboard && key_value != '\0') { + if(logical_keyboard && key_value != '\0' && keyboard_machine->can_type(key_value)) { + char string[] = { key_value, 0 }; keyboard_machine->type_string(string); break; } From 575d0da4d17ef3f94a60f4dae6b48e2c1181c99d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Mar 2020 21:31:40 -0500 Subject: [PATCH 24/28] Attempts better to describe options. --- OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib index 4b186732e..8fe74fa34 100644 --- a/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib +++ b/OSBindings/Mac/Clock Signal/Base.lproj/MainMenu.xib @@ -164,12 +164,12 @@ - + - + From 535634daca66d707323149678ff7833772f89434 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Mar 2020 21:42:30 -0500 Subject: [PATCH 25/28] Introduces virtual left and up keys for the Vic-20. Thereby allowing all cursor keys to be mapped. --- Machines/Commodore/Vic-20/Keyboard.cpp | 8 +++++--- Machines/Commodore/Vic-20/Keyboard.hpp | 11 ++++++++--- Machines/Commodore/Vic-20/Vic20.cpp | 19 ++++++++++++++++--- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Machines/Commodore/Vic-20/Keyboard.cpp b/Machines/Commodore/Vic-20/Keyboard.cpp index e868c9772..2d4152b7b 100644 --- a/Machines/Commodore/Vic-20/Keyboard.cpp +++ b/Machines/Commodore/Vic-20/Keyboard.cpp @@ -24,7 +24,7 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(Z, KeyZ); BIND(X, KeyX); BIND(C, KeyC); BIND(V, KeyV); BIND(B, KeyB); BIND(N, KeyN); BIND(M, KeyM); - BIND(BackTick, KeyLeft); + BIND(BackTick, KeyLeftArrow); BIND(Hyphen, KeyPlus); BIND(Equals, KeyDash); BIND(F11, KeyGBP); @@ -35,8 +35,8 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(CloseSquareBracket, KeyAsterisk); BIND(Backslash, KeyRestore); - BIND(Hash, KeyUp); - BIND(F10, KeyUp); + BIND(Hash, KeyUpArrow); + BIND(F10, KeyUpArrow); BIND(Semicolon, KeyColon); BIND(Quote, KeySemicolon); @@ -56,6 +56,8 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(Right, KeyRight); BIND(Down, KeyDown); + BIND(Left, KeyLeft); + BIND(Up, KeyUp); BIND(Enter, KeyReturn); BIND(Space, KeySpace); diff --git a/Machines/Commodore/Vic-20/Keyboard.hpp b/Machines/Commodore/Vic-20/Keyboard.hpp index 7437f3420..ec775fd96 100644 --- a/Machines/Commodore/Vic-20/Keyboard.hpp +++ b/Machines/Commodore/Vic-20/Keyboard.hpp @@ -20,7 +20,7 @@ enum Key: uint16_t { Key2 = key(7, 0x01), Key4 = key(7, 0x02), Key6 = key(7, 0x04), Key8 = key(7, 0x08), Key0 = key(7, 0x10), KeyDash = key(7, 0x20), KeyHome = key(7, 0x40), KeyF7 = key(7, 0x80), KeyQ = key(6, 0x01), KeyE = key(6, 0x02), KeyT = key(6, 0x04), KeyU = key(6, 0x08), - KeyO = key(6, 0x10), KeyAt = key(6, 0x20), KeyUp = key(6, 0x40), KeyF5 = key(6, 0x80), + KeyO = key(6, 0x10), KeyAt = key(6, 0x20), KeyUpArrow = key(6, 0x40), KeyF5 = key(6, 0x80), KeyCBM = key(5, 0x01), KeyS = key(5, 0x02), KeyF = key(5, 0x04), KeyH = key(5, 0x08), KeyK = key(5, 0x10), KeyColon = key(5, 0x20), KeyEquals = key(5, 0x40), KeyF3 = key(5, 0x80), KeySpace = key(4, 0x01), KeyZ = key(4, 0x02), KeyC = key(4, 0x04), KeyB = key(4, 0x08), @@ -29,12 +29,17 @@ enum Key: uint16_t { KeyN = key(3, 0x10), KeyComma = key(3, 0x20), KeySlash = key(3, 0x40), KeyDown = key(3, 0x80), KeyControl = key(2, 0x01), KeyA = key(2, 0x02), KeyD = key(2, 0x04), KeyG = key(2, 0x08), KeyJ = key(2, 0x10), KeyL = key(2, 0x20), KeySemicolon = key(2, 0x40), KeyRight = key(2, 0x80), - KeyLeft = key(1, 0x01), KeyW = key(1, 0x02), KeyR = key(1, 0x04), KeyY = key(1, 0x08), + KeyLeftArrow= key(1, 0x01), KeyW = key(1, 0x02), KeyR = key(1, 0x04), KeyY = key(1, 0x08), KeyI = key(1, 0x10), KeyP = key(1, 0x20), KeyAsterisk = key(1, 0x40), KeyReturn = key(1, 0x80), Key1 = key(0, 0x01), Key3 = key(0, 0x02), Key5 = key(0, 0x04), Key7 = key(0, 0x08), Key9 = key(0, 0x10), KeyPlus = key(0, 0x20), KeyGBP = key(0, 0x40), KeyDelete = key(0, 0x80), - KeyRestore = 0xfffd + // Virtual keys. + KeyUp = 0xfff0, + KeyLeft = 0xfff1, + + // Physical keys not within the usual matrix. + KeyRestore = 0xfffd, #undef key }; diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 0697d5e96..5123f394d 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -479,10 +479,23 @@ class ConcreteMachine: } void set_key_state(uint16_t key, bool is_pressed) final { - if(key != KeyRestore) + if(key < 0xfff0) { keyboard_via_port_handler_->set_key_state(key, is_pressed); - else - user_port_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !is_pressed); + } else { + switch(key) { + case KeyRestore: + user_port_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !is_pressed); + break; + case KeyUp: + keyboard_via_port_handler_->set_key_state(KeyLShift, is_pressed); + keyboard_via_port_handler_->set_key_state(KeyDown, is_pressed); + break; + case KeyLeft: + keyboard_via_port_handler_->set_key_state(KeyLShift, is_pressed); + keyboard_via_port_handler_->set_key_state(KeyRight, is_pressed); + break; + } + } } void clear_all_keys() final { From 90e6bef6d7182dda1a2a5a061a33dec8dc4ff551 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Mar 2020 21:47:28 -0500 Subject: [PATCH 26/28] Adds virtual keys for F2, F4, F6 and F8. --- Machines/Commodore/Vic-20/Keyboard.cpp | 10 ++++++++-- Machines/Commodore/Vic-20/Keyboard.hpp | 4 ++++ Machines/Commodore/Vic-20/Vic20.cpp | 19 ++++++++++++------- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/Machines/Commodore/Vic-20/Keyboard.cpp b/Machines/Commodore/Vic-20/Keyboard.cpp index 2d4152b7b..017c47cb2 100644 --- a/Machines/Commodore/Vic-20/Keyboard.cpp +++ b/Machines/Commodore/Vic-20/Keyboard.cpp @@ -56,8 +56,6 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(Right, KeyRight); BIND(Down, KeyDown); - BIND(Left, KeyLeft); - BIND(Up, KeyUp); BIND(Enter, KeyReturn); BIND(Space, KeySpace); @@ -68,6 +66,14 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(F3, KeyF3); BIND(F5, KeyF5); BIND(F7, KeyF7); + + // Mappings to virtual keys. + BIND(Left, KeyLeft); + BIND(Up, KeyUp); + BIND(F2, KeyF2); + BIND(F4, KeyF4); + BIND(F6, KeyF6); + BIND(F8, KeyF8); } #undef BIND return KeyboardMachine::MappedMachine::KeyNotMapped; diff --git a/Machines/Commodore/Vic-20/Keyboard.hpp b/Machines/Commodore/Vic-20/Keyboard.hpp index ec775fd96..8e66d2278 100644 --- a/Machines/Commodore/Vic-20/Keyboard.hpp +++ b/Machines/Commodore/Vic-20/Keyboard.hpp @@ -37,6 +37,10 @@ enum Key: uint16_t { // Virtual keys. KeyUp = 0xfff0, KeyLeft = 0xfff1, + KeyF2 = 0xfff2, + KeyF4 = 0xfff3, + KeyF6 = 0xfff4, + KeyF8 = 0xfff5, // Physical keys not within the usual matrix. KeyRestore = 0xfffd, diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 5123f394d..94e328c00 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -486,14 +486,19 @@ class ConcreteMachine: case KeyRestore: user_port_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !is_pressed); break; - case KeyUp: - keyboard_via_port_handler_->set_key_state(KeyLShift, is_pressed); - keyboard_via_port_handler_->set_key_state(KeyDown, is_pressed); - break; - case KeyLeft: - keyboard_via_port_handler_->set_key_state(KeyLShift, is_pressed); - keyboard_via_port_handler_->set_key_state(KeyRight, is_pressed); +#define ShiftedMap(source, target) \ + case source: \ + keyboard_via_port_handler_->set_key_state(KeyLShift, is_pressed); \ + keyboard_via_port_handler_->set_key_state(target, is_pressed); \ break; + + ShiftedMap(KeyUp, KeyDown); + ShiftedMap(KeyLeft, KeyRight); + ShiftedMap(KeyF2, KeyF1); + ShiftedMap(KeyF4, KeyF3); + ShiftedMap(KeyF6, KeyF5); + ShiftedMap(KeyF8, KeyF7); +#undef ShiftedMap } } } From b2c07b3110284874f3bbba72d1827a667fe380ef Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Mar 2020 22:10:41 -0500 Subject: [PATCH 27/28] The Atari ST doesn't offer quick loading. --- Machines/Atari/ST/AtariST.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index c8056e9e1..7be6a7747 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -44,7 +44,7 @@ namespace ST { std::vector> get_options() { return Configurable::standard_options( - static_cast(Configurable::DisplayRGB | Configurable::DisplayCompositeColour | Configurable::QuickLoadTape) + static_cast(Configurable::DisplayRGB | Configurable::DisplayCompositeColour) ); } From 2db30a91c67cae7b8471009a511ad0e9564c4067 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 1 Mar 2020 23:11:14 -0500 Subject: [PATCH 28/28] 'Corrects' but disables SDL logical keyboard entry. I'm just not sure that SDL supports what I want. --- .../xcschemes/Clock Signal Kiosk.xcscheme | 10 +++++++++- .../xcshareddata/xcschemes/Clock Signal.xcscheme | 2 +- OSBindings/SDL/main.cpp | 15 ++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme index 3fddce824..4cd4a9880 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal Kiosk.xcscheme @@ -31,7 +31,7 @@ + + + + = 0) key_value = key_name[0]; + if(key_name[0] >= 0 && key_name[1] == 0) key_value = key_name[0]; // If a logical mapping was selected and a symbol was found, type it. if(logical_keyboard && key_value != '\0' && keyboard_machine->can_type(key_value)) { - char string[] = { key_value, 0 }; - keyboard_machine->type_string(string); + if(is_pressed) { + char string[] = { key_value, 0 }; + keyboard_machine->type_string(string); + } break; }