From a8645f80bfb0485ad88c3841215cd7247e26caad Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 24 Oct 2018 21:59:30 -0400 Subject: [PATCH] Introduces 'non-exclusive' emulator-space keyboards. i.e. sets of keys that don't amount to an entire keyboard in the modern sense. Experimentally used by the Master System for its reset key. --- .../Implementation/MultiKeyboardMachine.cpp | 14 +- .../Implementation/MultiKeyboardMachine.hpp | 3 +- Components/9918/9918.cpp | 17 ++ Components/9918/9918.hpp | 10 ++ Inputs/Keyboard.cpp | 16 +- Inputs/Keyboard.hpp | 21 ++- Machines/AmstradCPC/AmstradCPC.cpp | 2 +- Machines/AmstradCPC/Keyboard.cpp | 6 +- Machines/AmstradCPC/Keyboard.hpp | 2 +- Machines/AppleII/AppleII.cpp | 2 +- Machines/Commodore/Vic-20/Keyboard.cpp | 8 +- Machines/Commodore/Vic-20/Keyboard.hpp | 2 +- Machines/Commodore/Vic-20/Vic20.cpp | 2 +- Machines/Electron/Electron.cpp | 2 +- Machines/Electron/Keyboard.cpp | 8 +- Machines/Electron/Keyboard.hpp | 2 +- Machines/KeyboardMachine.cpp | 10 +- Machines/KeyboardMachine.hpp | 24 ++- Machines/MSX/Keyboard.cpp | 2 +- Machines/MSX/Keyboard.hpp | 2 +- Machines/MSX/MSX.cpp | 2 +- Machines/MasterSystem/MasterSystem.cpp | 41 ++++- Machines/Oric/Keyboard.cpp | 8 +- Machines/Oric/Keyboard.hpp | 2 +- Machines/Oric/Oric.cpp | 2 +- Machines/Utility/Typer.cpp | 6 +- Machines/ZX8081/Keyboard.cpp | 8 +- Machines/ZX8081/Keyboard.hpp | 2 +- Machines/ZX8081/ZX8081.cpp | 2 +- .../Documents/MachineDocument.swift | 2 +- .../Mac/Clock Signal/Machine/CSMachine.h | 2 +- .../Mac/Clock Signal/Machine/CSMachine.mm | 153 +++++++++--------- .../Clock SignalTests/MasterSystemVDPTests.mm | 20 ++- OSBindings/SDL/main.cpp | 18 ++- 34 files changed, 280 insertions(+), 143 deletions(-) diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp index a74e0cde7..24c666336 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.cpp @@ -35,9 +35,13 @@ void MultiKeyboardMachine::type_string(const std::string &string) { } } -void MultiKeyboardMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) { - for(const auto &machine: machines_) { - uint16_t mapped_key = machine->get_keyboard_mapper()->mapped_key_for_key(key); - if(mapped_key != KeyNotMapped) machine->set_key_state(mapped_key, is_pressed); - } +Inputs::Keyboard &MultiKeyboardMachine::get_keyboard() { + return keyboard_; } + +//void MultiKeyboardMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) { +// for(const auto &machine: machines_) { +// uint16_t mapped_key = machine->get_keyboard_mapper()->mapped_key_for_key(key); +// if(mapped_key != KeyNotMapped) machine->set_key_state(mapped_key, is_pressed); +// } +//} diff --git a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp index f049b4bac..6a0af3cc7 100644 --- a/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp +++ b/Analyser/Dynamic/MultiMachine/Implementation/MultiKeyboardMachine.hpp @@ -32,10 +32,11 @@ class MultiKeyboardMachine: public KeyboardMachine::Machine { void clear_all_keys() override; void set_key_state(uint16_t key, bool is_pressed) override; void type_string(const std::string &) override; - void keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) override; + Inputs::Keyboard &get_keyboard() override; private: std::vector<::KeyboardMachine::Machine *> machines_; + Inputs::Keyboard keyboard_; }; } diff --git a/Components/9918/9918.cpp b/Components/9918/9918.cpp index abf9745c4..f9cc32bcb 100644 --- a/Components/9918/9918.cpp +++ b/Components/9918/9918.cpp @@ -702,6 +702,23 @@ HalfCycles TMS9918::get_time_until_interrupt() { return half_cycles_before_internal_cycles(std::min(local_cycles_until_line_interrupt, time_until_frame_interrupt)); } +HalfCycles TMS9918::get_time_until_line(int line) { + if(line < 0) line += mode_timing_.total_lines; + + int cycles_to_next_interrupt_threshold = mode_timing_.line_interrupt_position - write_pointer_.column; + int line_of_next_interrupt_threshold = write_pointer_.row; + if(cycles_to_next_interrupt_threshold <= 0) { + cycles_to_next_interrupt_threshold += 342; + ++line_of_next_interrupt_threshold; + } + + if(line_of_next_interrupt_threshold > line) { + line += mode_timing_.total_lines; + } + + return half_cycles_before_internal_cycles(cycles_to_next_interrupt_threshold + (line - line_of_next_interrupt_threshold)*342); +} + bool TMS9918::get_interrupt_line() { return ((status_ & StatusInterrupt) && generate_interrupts_) || (enable_line_interrupts_ && line_interrupt_pending_); } diff --git a/Components/9918/9918.hpp b/Components/9918/9918.hpp index 6e6225d47..e607d9947 100644 --- a/Components/9918/9918.hpp +++ b/Components/9918/9918.hpp @@ -74,6 +74,16 @@ class TMS9918: public Base { */ HalfCycles get_time_until_interrupt(); + /*! + Returns the amount of time until the nominated line interrupt position is + reached on line @c line. If no line interrupt position is defined for + this VDP, returns the time until the 'beginning' of that line, whatever + that may mean. + + @line is relative to the first pixel line of the display and may be negative. + */ + HalfCycles get_time_until_line(int line); + /*! @returns @c true if the interrupt line is currently active; @c false otherwise. */ diff --git a/Inputs/Keyboard.cpp b/Inputs/Keyboard.cpp index 02afb249b..e90f1f306 100644 --- a/Inputs/Keyboard.cpp +++ b/Inputs/Keyboard.cpp @@ -10,7 +10,13 @@ using namespace Inputs; -Keyboard::Keyboard() {} +Keyboard::Keyboard() { + for(int k = 0; k < int(Key::Help); ++k) { + observed_keys_.insert(Key(k)); + } +} + +Keyboard::Keyboard(const std::set &observed_keys) : observed_keys_(observed_keys), is_exclusive_(false) {} void Keyboard::set_key_pressed(Key key, char value, bool is_pressed) { std::size_t key_offset = static_cast(key); @@ -36,3 +42,11 @@ bool Keyboard::get_key_state(Key key) { if(key_offset >= key_states_.size()) return false; return key_states_[key_offset]; } + +const std::set &Keyboard::observed_keys() { + return observed_keys_; +} + +bool Keyboard::is_exclusive() { + return is_exclusive_; +} diff --git a/Inputs/Keyboard.hpp b/Inputs/Keyboard.hpp index 10e798b27..e6f0f07b6 100644 --- a/Inputs/Keyboard.hpp +++ b/Inputs/Keyboard.hpp @@ -10,6 +10,7 @@ #define Keyboard_hpp #include +#include namespace Inputs { @@ -20,8 +21,6 @@ namespace Inputs { */ class Keyboard { public: - Keyboard(); - enum class Key { Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PrintScreen, ScrollLock, Pause, BackTick, k1, k2, k3, k4, k5, k6, k7, k8, k9, k0, Hyphen, Equals, BackSpace, @@ -39,10 +38,26 @@ class Keyboard { Help }; + /// Constructs a Keyboard that declares itself to observe all keys. + Keyboard(); + + /// Constructs a Keyboard that declares itself to observe only members of @c observed_keys. + Keyboard(const std::set &observed_keys); + // Host interface. virtual void set_key_pressed(Key key, char value, bool is_pressed); virtual void reset_all_keys(); + /// @returns a set of all Keys that this keyboard responds to. + virtual const std::set &observed_keys(); + + /* + @returns @c true if this keyboard, on its original machine, looked + like a complete keyboard — i.e. if a user would expect this keyboard + to be the only thing a real keyboard maps to. + */ + virtual bool is_exclusive(); + // Delegate interface. struct Delegate { virtual void keyboard_did_change_key(Keyboard *keyboard, Key key, bool is_pressed) = 0; @@ -52,8 +67,10 @@ class Keyboard { bool get_key_state(Key key); private: + std::set observed_keys_; std::vector key_states_; Delegate *delegate_ = nullptr; + bool is_exclusive_ = true; }; } diff --git a/Machines/AmstradCPC/AmstradCPC.cpp b/Machines/AmstradCPC/AmstradCPC.cpp index a6a8a20f9..2a3935039 100644 --- a/Machines/AmstradCPC/AmstradCPC.cpp +++ b/Machines/AmstradCPC/AmstradCPC.cpp @@ -757,7 +757,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler { template class ConcreteMachine: public CRTMachine::Machine, public MediaTarget::Machine, - public KeyboardMachine::Machine, + public KeyboardMachine::MappedMachine, public Utility::TypeRecipient, public CPU::Z80::BusHandler, public ClockingHint::Observer, diff --git a/Machines/AmstradCPC/Keyboard.cpp b/Machines/AmstradCPC/Keyboard.cpp index ac31eb44b..e756fb106 100644 --- a/Machines/AmstradCPC/Keyboard.cpp +++ b/Machines/AmstradCPC/Keyboard.cpp @@ -75,9 +75,9 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { } uint16_t *CharacterMapper::sequence_for_character(char character) { -#define KEYS(...) {__VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence} -#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence} -#define X {KeyboardMachine::Machine::KeyNotMapped} +#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} +#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} +#define X {KeyboardMachine::MappedMachine::KeyNotMapped} static KeySequence key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, diff --git a/Machines/AmstradCPC/Keyboard.hpp b/Machines/AmstradCPC/Keyboard.hpp index c6a230a83..65544faba 100644 --- a/Machines/AmstradCPC/Keyboard.hpp +++ b/Machines/AmstradCPC/Keyboard.hpp @@ -33,7 +33,7 @@ enum Key: uint16_t { #undef Line }; -struct KeyboardMapper: public KeyboardMachine::Machine::KeyboardMapper { +struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); }; diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index 3c993aacc..63371a316 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -40,7 +40,7 @@ namespace { template class ConcreteMachine: public CRTMachine::Machine, public MediaTarget::Machine, - public KeyboardMachine::Machine, + public KeyboardMachine::MappedMachine, public CPU::MOS6502::BusHandler, public Inputs::Keyboard, public AppleII::Machine, diff --git a/Machines/Commodore/Vic-20/Keyboard.cpp b/Machines/Commodore/Vic-20/Keyboard.cpp index f43a26470..30bda5b20 100644 --- a/Machines/Commodore/Vic-20/Keyboard.cpp +++ b/Machines/Commodore/Vic-20/Keyboard.cpp @@ -68,13 +68,13 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(F7, KeyF7); } #undef BIND - return KeyboardMachine::Machine::KeyNotMapped; + return KeyboardMachine::MappedMachine::KeyNotMapped; } uint16_t *CharacterMapper::sequence_for_character(char character) { -#define KEYS(...) {__VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence} -#define SHIFT(...) {KeyLShift, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence} -#define X {KeyboardMachine::Machine::KeyNotMapped} +#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} +#define SHIFT(...) {KeyLShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} +#define X {KeyboardMachine::MappedMachine::KeyNotMapped} static KeySequence key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, diff --git a/Machines/Commodore/Vic-20/Keyboard.hpp b/Machines/Commodore/Vic-20/Keyboard.hpp index 3529830dd..a847e14f8 100644 --- a/Machines/Commodore/Vic-20/Keyboard.hpp +++ b/Machines/Commodore/Vic-20/Keyboard.hpp @@ -38,7 +38,7 @@ enum Key: uint16_t { #undef key }; -struct KeyboardMapper: public KeyboardMachine::Machine::KeyboardMapper { +struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); }; diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index d56a21c88..9a28bb588 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -281,7 +281,7 @@ class Joystick: public Inputs::ConcreteJoystick { class ConcreteMachine: public CRTMachine::Machine, public MediaTarget::Machine, - public KeyboardMachine::Machine, + public KeyboardMachine::MappedMachine, public JoystickMachine::Machine, public Configurable::Device, public CPU::MOS6502::BusHandler, diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 295d85248..74dc06721 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -42,7 +42,7 @@ class ConcreteMachine: public Machine, public CRTMachine::Machine, public MediaTarget::Machine, - public KeyboardMachine::Machine, + public KeyboardMachine::MappedMachine, public Configurable::Device, public CPU::MOS6502::BusHandler, public Tape::Delegate, diff --git a/Machines/Electron/Keyboard.cpp b/Machines/Electron/Keyboard.cpp index d9293cf33..36d8422ec 100644 --- a/Machines/Electron/Keyboard.cpp +++ b/Machines/Electron/Keyboard.cpp @@ -56,10 +56,10 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { } uint16_t *CharacterMapper::sequence_for_character(char character) { -#define KEYS(...) {__VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence} -#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence} -#define CTRL(...) {KeyControl, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence} -#define X {KeyboardMachine::Machine::KeyNotMapped} +#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} +#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} +#define CTRL(...) {KeyControl, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} +#define X {KeyboardMachine::MappedMachine::KeyNotMapped} static KeySequence key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, diff --git a/Machines/Electron/Keyboard.hpp b/Machines/Electron/Keyboard.hpp index 9bb59d795..a0e23e7c4 100644 --- a/Machines/Electron/Keyboard.hpp +++ b/Machines/Electron/Keyboard.hpp @@ -33,7 +33,7 @@ enum Key: uint16_t { KeyBreak = 0xfffd, }; -struct KeyboardMapper: public KeyboardMachine::Machine::KeyboardMapper { +struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); }; diff --git a/Machines/KeyboardMachine.cpp b/Machines/KeyboardMachine.cpp index 5048cdf71..5d7084028 100644 --- a/Machines/KeyboardMachine.cpp +++ b/Machines/KeyboardMachine.cpp @@ -10,27 +10,27 @@ using namespace KeyboardMachine; -Machine::Machine() { +MappedMachine::MappedMachine() { keyboard_.set_delegate(this); } -void Machine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) { +void MappedMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) { uint16_t mapped_key = get_keyboard_mapper()->mapped_key_for_key(key); if(mapped_key != KeyNotMapped) set_key_state(mapped_key, is_pressed); } -void Machine::reset_all_keys(Inputs::Keyboard *keyboard) { +void MappedMachine::reset_all_keys(Inputs::Keyboard *keyboard) { // TODO: unify naming. clear_all_keys(); } -Inputs::Keyboard &Machine::get_keyboard() { +Inputs::Keyboard &MappedMachine::get_keyboard() { return keyboard_; } void Machine::type_string(const std::string &) { } -Machine::KeyboardMapper *Machine::get_keyboard_mapper() { +MappedMachine::KeyboardMapper *MappedMachine::get_keyboard_mapper() { return nullptr; } diff --git a/Machines/KeyboardMachine.hpp b/Machines/KeyboardMachine.hpp index c8b00bf44..6206a301a 100644 --- a/Machines/KeyboardMachine.hpp +++ b/Machines/KeyboardMachine.hpp @@ -33,13 +33,10 @@ struct KeyActions { }; /*! - Describes the full functionality of being an emulated machine with a keyboard: not just being - able to receive key actions, but being able to vend a generic keyboard and a keyboard mapper. + Describes an emulated machine which exposes a keyboard and accepts a typed string. */ -class Machine: public Inputs::Keyboard::Delegate, public KeyActions { +class Machine: public KeyActions { public: - Machine(); - /*! Causes the machine to attempt to type the supplied string. @@ -50,7 +47,16 @@ class Machine: public Inputs::Keyboard::Delegate, public KeyActions { /*! Provides a destination for keyboard input. */ - virtual Inputs::Keyboard &get_keyboard(); + virtual Inputs::Keyboard &get_keyboard() = 0; +}; + +/*! + Provides a base class for machines that want to provide a keyboard mapper, + allowing automatic mapping from keyboard inputs to KeyActions. +*/ +class MappedMachine: public Inputs::Keyboard::Delegate, public Machine { + public: + MappedMachine(); /*! A keyboard mapper attempts to provide a physical mapping between host keys and emulated keys. @@ -76,6 +82,12 @@ class Machine: public Inputs::Keyboard::Delegate, public KeyActions { */ virtual KeyboardMapper *get_keyboard_mapper(); + /*! + Provides a keyboard that obtains this machine's keyboard mapper, maps + the key and supplies it via the KeyActions. + */ + virtual Inputs::Keyboard &get_keyboard() override; + private: void keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) override; void reset_all_keys(Inputs::Keyboard *keyboard) override; diff --git a/Machines/MSX/Keyboard.cpp b/Machines/MSX/Keyboard.cpp index a55783eb4..177e6f60e 100644 --- a/Machines/MSX/Keyboard.cpp +++ b/Machines/MSX/Keyboard.cpp @@ -57,5 +57,5 @@ uint16_t MSX::KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { default: break; } #undef BIND - return KeyboardMachine::Machine::KeyNotMapped; + return KeyboardMachine::MappedMachine::KeyNotMapped; } diff --git a/Machines/MSX/Keyboard.hpp b/Machines/MSX/Keyboard.hpp index 78314fcfb..c9607f846 100644 --- a/Machines/MSX/Keyboard.hpp +++ b/Machines/MSX/Keyboard.hpp @@ -33,7 +33,7 @@ enum Key: uint16_t { #undef Line }; -struct KeyboardMapper: public KeyboardMachine::Machine::KeyboardMapper { +struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); }; diff --git a/Machines/MSX/MSX.cpp b/Machines/MSX/MSX.cpp index 24fe36c73..d7a34cf3a 100644 --- a/Machines/MSX/MSX.cpp +++ b/Machines/MSX/MSX.cpp @@ -139,7 +139,7 @@ class ConcreteMachine: public CPU::Z80::BusHandler, public CRTMachine::Machine, public MediaTarget::Machine, - public KeyboardMachine::Machine, + public KeyboardMachine::MappedMachine, public Configurable::Device, public JoystickMachine::Machine, public MemoryMap, diff --git a/Machines/MasterSystem/MasterSystem.cpp b/Machines/MasterSystem/MasterSystem.cpp index 7b3d5c8d2..6eaf28446 100644 --- a/Machines/MasterSystem/MasterSystem.cpp +++ b/Machines/MasterSystem/MasterSystem.cpp @@ -15,6 +15,7 @@ #include "../CRTMachine.hpp" #include "../JoystickMachine.hpp" +#include "../KeyboardMachine.hpp" #include "../../ClockReceiver/ForceInline.hpp" @@ -81,6 +82,8 @@ class ConcreteMachine: public Machine, public CPU::Z80::BusHandler, public CRTMachine::Machine, + public KeyboardMachine::Machine, + public Inputs::Keyboard::Delegate, public Configurable::Device, public JoystickMachine::Machine { @@ -94,7 +97,8 @@ class ConcreteMachine: (target.model == Target::Model::SG1000) ? TI::SN76489::Personality::SN76489 : TI::SN76489::Personality::SMS, audio_queue_, sn76489_divider), - speaker_(sn76489_) { + speaker_(sn76489_), + keyboard_({Inputs::Keyboard::Key::Enter, Inputs::Keyboard::Key::Escape}) { // Pick the clock rate based on the region. const double clock_rate = target.region == Target::Region::Europe ? 3546893.0 : 3579540.0; speaker_.set_input_rate(static_cast(clock_rate / sn76489_divider)); @@ -146,6 +150,8 @@ class ConcreteMachine: // Apple a relatively low low-pass filter. More guidance needed here. speaker_.set_high_frequency_cutoff(8000); + + keyboard_.set_delegate(this); } ~ConcreteMachine() { @@ -164,6 +170,8 @@ class ConcreteMachine: (region_ == Target::Region::Europe) ? TI::TMS::TVStandard::PAL : TI::TMS::TVStandard::NTSC); get_crt()->set_video_signal(Outputs::CRT::VideoSignal::Composite); + + time_until_debounce_ = vdp_->get_time_until_line(-1); } void close_output() override { @@ -213,7 +221,7 @@ class ConcreteMachine: } if(write_pointers_[address >> 10]) write_pointers_[address >> 10][address & 1023] = *cycle.value; - else LOG("Ignored write to ROM"); +// else LOG("Ignored write to ROM"); break; case CPU::Z80::PartialMachineCycle::Input: @@ -320,6 +328,15 @@ class ConcreteMachine: } } + // The pause button is debounced and takes effect only one line before pixels + // begin; time_until_debounce_ keeps track of the time until then. + time_until_debounce_ -= cycle.length; + if(time_until_debounce_ <= HalfCycles(0)) { + z80_.set_non_maskable_interrupt_line(pause_is_pressed_); + update_video(); + time_until_debounce_ = vdp_->get_time_until_line(-1); + } + return HalfCycles(0); } @@ -333,6 +350,23 @@ class ConcreteMachine: return joysticks_; } + // MARK: - Keyboard (i.e. the pause and reset buttons). + Inputs::Keyboard &get_keyboard() override { + return keyboard_; + } + + void keyboard_did_change_key(Inputs::Keyboard *, Inputs::Keyboard::Key key, bool is_pressed) override { + if(key == Inputs::Keyboard::Key::Enter) { + pause_is_pressed_ = is_pressed; + } else if(key == Inputs::Keyboard::Key::Escape) { + reset_is_pressed_ = is_pressed; + } + } + + + void reset_all_keys(Inputs::Keyboard *) override { + } + // MARK: - Configuration options. std::vector> get_options() override { return Sega::MasterSystem::get_options(); @@ -388,10 +422,13 @@ class ConcreteMachine: Outputs::Speaker::LowpassSpeaker speaker_; std::vector> joysticks_; + Inputs::Keyboard keyboard_; + bool reset_is_pressed_ = false, pause_is_pressed_ = false; HalfCycles time_since_vdp_update_; HalfCycles time_since_sn76489_update_; HalfCycles time_until_interrupt_; + HalfCycles time_until_debounce_; uint8_t ram_[8*1024]; uint8_t bios_[8*1024]; diff --git a/Machines/Oric/Keyboard.cpp b/Machines/Oric/Keyboard.cpp index d6151c68d..0dea3bc57 100644 --- a/Machines/Oric/Keyboard.cpp +++ b/Machines/Oric/Keyboard.cpp @@ -48,13 +48,13 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { } #undef BIND - return KeyboardMachine::Machine::KeyNotMapped; + return KeyboardMachine::MappedMachine::KeyNotMapped; } uint16_t *CharacterMapper::sequence_for_character(char character) { -#define KEYS(...) {__VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence} -#define SHIFT(...) {KeyLeftShift, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence} -#define X {KeyboardMachine::Machine::KeyNotMapped} +#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} +#define SHIFT(...) {KeyLeftShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} +#define X {KeyboardMachine::MappedMachine::KeyNotMapped} static KeySequence key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, diff --git a/Machines/Oric/Keyboard.hpp b/Machines/Oric/Keyboard.hpp index 4c3b1c544..6f2f9342e 100644 --- a/Machines/Oric/Keyboard.hpp +++ b/Machines/Oric/Keyboard.hpp @@ -35,7 +35,7 @@ enum Key: uint16_t { KeyNMI = 0xfffd, }; -struct KeyboardMapper: public KeyboardMachine::Machine::KeyboardMapper { +struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); }; diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 2d2d7e040..1527973cb 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -194,7 +194,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler { template class ConcreteMachine: public CRTMachine::Machine, public MediaTarget::Machine, - public KeyboardMachine::Machine, + public KeyboardMachine::MappedMachine, public Configurable::Device, public CPU::MOS6502::BusHandler, public MOS::MOS6522::IRQDelegatePortHandler::Delegate, diff --git a/Machines/Utility/Typer.cpp b/Machines/Utility/Typer.cpp index da1131894..18a847968 100644 --- a/Machines/Utility/Typer.cpp +++ b/Machines/Utility/Typer.cpp @@ -43,14 +43,14 @@ void Typer::run_for(const HalfCycles duration) { bool Typer::try_type_next_character() { uint16_t *sequence = character_mapper_->sequence_for_character(string_[string_pointer_]); - if(!sequence || sequence[0] == KeyboardMachine::Machine::KeyNotMapped) { + if(!sequence || sequence[0] == KeyboardMachine::MappedMachine::KeyNotMapped) { return false; } if(!phase_) delegate_->clear_all_keys(); else { delegate_->set_key_state(sequence[phase_ - 1], true); - return sequence[phase_] != KeyboardMachine::Machine::KeyEndSequence; + return sequence[phase_] != KeyboardMachine::MappedMachine::KeyEndSequence; } return true; @@ -75,6 +75,6 @@ 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(sequences[ucharacter][0] == KeyboardMachine::Machine::KeyNotMapped) return nullptr; + if(sequences[ucharacter][0] == KeyboardMachine::MappedMachine::KeyNotMapped) return nullptr; return sequences[ucharacter]; } diff --git a/Machines/ZX8081/Keyboard.cpp b/Machines/ZX8081/Keyboard.cpp index e24eb693b..e3d177164 100644 --- a/Machines/ZX8081/Keyboard.cpp +++ b/Machines/ZX8081/Keyboard.cpp @@ -30,15 +30,15 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) { BIND(Space, KeySpace); } #undef BIND - return KeyboardMachine::Machine::KeyNotMapped; + return KeyboardMachine::MappedMachine::KeyNotMapped; } CharacterMapper::CharacterMapper(bool is_zx81) : is_zx81_(is_zx81) {} uint16_t *CharacterMapper::sequence_for_character(char character) { -#define KEYS(...) {__VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence} -#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence} -#define X {KeyboardMachine::Machine::KeyNotMapped} +#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} +#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence} +#define X {KeyboardMachine::MappedMachine::KeyNotMapped} static KeySequence zx81_key_sequences[] = { /* NUL */ X, /* SOH */ X, /* STX */ X, /* ETX */ X, diff --git a/Machines/ZX8081/Keyboard.hpp b/Machines/ZX8081/Keyboard.hpp index 01bd9c8f5..412281a81 100644 --- a/Machines/ZX8081/Keyboard.hpp +++ b/Machines/ZX8081/Keyboard.hpp @@ -25,7 +25,7 @@ enum Key: uint16_t { KeySpace = 0x0700 | 0x01, KeyDot = 0x0700 | 0x02, KeyM = 0x0700 | 0x04, KeyN = 0x0700 | 0x08, KeyB = 0x0700 | 0x10, }; -struct KeyboardMapper: public KeyboardMachine::Machine::KeyboardMapper { +struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper { uint16_t mapped_key_for_key(Inputs::Keyboard::Key key); }; diff --git a/Machines/ZX8081/ZX8081.cpp b/Machines/ZX8081/ZX8081.cpp index 4fe9ddbcb..dfc59bb3d 100644 --- a/Machines/ZX8081/ZX8081.cpp +++ b/Machines/ZX8081/ZX8081.cpp @@ -60,7 +60,7 @@ std::vector> get_options() { template class ConcreteMachine: public CRTMachine::Machine, public MediaTarget::Machine, - public KeyboardMachine::Machine, + public KeyboardMachine::MappedMachine, public Configurable::Device, public Utility::TypeRecipient, public CPU::Z80::BusHandler, diff --git a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift index e3fcf2808..930c53470 100644 --- a/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift +++ b/OSBindings/Mac/Clock Signal/Documents/MachineDocument.swift @@ -301,7 +301,7 @@ class MachineDocument: if let menuItem = item as? NSMenuItem { switch item.action { case #selector(self.useKeyboardAsKeyboard): - if machine == nil || !machine.hasKeyboard { + if machine == nil || !machine.hasExclusiveKeyboard { menuItem.state = .off return false } diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h index 1e40183d1..eb4d820d9 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.h +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.h @@ -76,7 +76,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) { - (bool)supportsVideoSignal:(CSMachineVideoSignal)videoSignal; // Input control. -@property (nonatomic, readonly) BOOL hasKeyboard; +@property (nonatomic, readonly) BOOL hasExclusiveKeyboard; @property (nonatomic, readonly) BOOL hasJoystick; @property (nonatomic, assign) CSMachineKeyboardInputMode inputMode; @property (nonatomic, nullable) CSJoystickManager *joystickManager; diff --git a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm index 3493501f3..7305fee4e 100644 --- a/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm +++ b/OSBindings/Mac/Clock Signal/Machine/CSMachine.mm @@ -91,7 +91,9 @@ struct ActivityObserver: public Activity::Observer { _machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(), error)); if(!_machine) return nil; - _inputMode = _machine->keyboard_machine() ? CSMachineKeyboardInputModeKeyboard : CSMachineKeyboardInputModeJoystick; + _inputMode = + (_machine->keyboard_machine() && _machine->keyboard_machine()->get_keyboard().is_exclusive()) + ? CSMachineKeyboardInputModeKeyboard : CSMachineKeyboardInputModeJoystick; _leds = [[NSMutableArray alloc] init]; Activity::Source *const activity_source = _machine->activity_source(); @@ -301,80 +303,85 @@ struct ActivityObserver: public Activity::Observer { - (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed { auto keyboard_machine = _machine->keyboard_machine(); - if(self.inputMode == CSMachineKeyboardInputModeKeyboard && keyboard_machine) { - // Don't pass anything on if this is not new information. - if(_depressedKeys[key] == !!isPressed) return; - _depressedKeys[key] = !!isPressed; + if(keyboard_machine && (self.inputMode == CSMachineKeyboardInputModeKeyboard || !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 + // to pass into the platform-neutral realm. + switch(key) { + BIND(VK_ANSI_0, k0); BIND(VK_ANSI_1, k1); BIND(VK_ANSI_2, k2); BIND(VK_ANSI_3, k3); BIND(VK_ANSI_4, k4); + BIND(VK_ANSI_5, k5); BIND(VK_ANSI_6, k6); BIND(VK_ANSI_7, k7); BIND(VK_ANSI_8, k8); BIND(VK_ANSI_9, k9); - // Pick an ASCII code, if any. - char pressedKey = '\0'; - if(characters.length) { - unichar firstCharacter = [characters characterAtIndex:0]; - if(firstCharacter < 128) { - pressedKey = (char)firstCharacter; - } + BIND(VK_ANSI_Q, Q); BIND(VK_ANSI_W, W); BIND(VK_ANSI_E, E); BIND(VK_ANSI_R, R); BIND(VK_ANSI_T, T); + BIND(VK_ANSI_Y, Y); BIND(VK_ANSI_U, U); BIND(VK_ANSI_I, I); BIND(VK_ANSI_O, O); BIND(VK_ANSI_P, P); + + BIND(VK_ANSI_A, A); BIND(VK_ANSI_S, S); BIND(VK_ANSI_D, D); BIND(VK_ANSI_F, F); BIND(VK_ANSI_G, G); + BIND(VK_ANSI_H, H); BIND(VK_ANSI_J, J); BIND(VK_ANSI_K, K); BIND(VK_ANSI_L, L); + + BIND(VK_ANSI_Z, Z); BIND(VK_ANSI_X, X); BIND(VK_ANSI_C, C); BIND(VK_ANSI_V, V); + BIND(VK_ANSI_B, B); BIND(VK_ANSI_N, N); BIND(VK_ANSI_M, M); + + BIND(VK_F1, F1); BIND(VK_F2, F2); BIND(VK_F3, F3); BIND(VK_F4, F4); + BIND(VK_F5, F5); BIND(VK_F6, F6); BIND(VK_F7, F7); BIND(VK_F8, F8); + BIND(VK_F9, F9); BIND(VK_F10, F10); BIND(VK_F11, F11); BIND(VK_F12, F12); + + BIND(VK_ANSI_Keypad0, KeyPad0); BIND(VK_ANSI_Keypad1, KeyPad1); BIND(VK_ANSI_Keypad2, KeyPad2); + BIND(VK_ANSI_Keypad3, KeyPad3); BIND(VK_ANSI_Keypad4, KeyPad4); BIND(VK_ANSI_Keypad5, KeyPad5); + BIND(VK_ANSI_Keypad6, KeyPad6); BIND(VK_ANSI_Keypad7, KeyPad7); BIND(VK_ANSI_Keypad8, KeyPad8); + BIND(VK_ANSI_Keypad9, KeyPad9); + + BIND(VK_ANSI_Equal, Equals); BIND(VK_ANSI_Minus, Hyphen); + BIND(VK_ANSI_RightBracket, CloseSquareBracket); BIND(VK_ANSI_LeftBracket, OpenSquareBracket); + BIND(VK_ANSI_Quote, Quote); BIND(VK_ANSI_Grave, BackTick); + + BIND(VK_ANSI_Semicolon, Semicolon); + BIND(VK_ANSI_Backslash, BackSlash); BIND(VK_ANSI_Slash, ForwardSlash); + BIND(VK_ANSI_Comma, Comma); BIND(VK_ANSI_Period, FullStop); + + BIND(VK_ANSI_KeypadDecimal, KeyPadDecimalPoint); BIND(VK_ANSI_KeypadEquals, KeyPadEquals); + BIND(VK_ANSI_KeypadMultiply, KeyPadAsterisk); BIND(VK_ANSI_KeypadDivide, KeyPadSlash); + BIND(VK_ANSI_KeypadPlus, KeyPadPlus); BIND(VK_ANSI_KeypadMinus, KeyPadMinus); + BIND(VK_ANSI_KeypadClear, KeyPadDelete); BIND(VK_ANSI_KeypadEnter, KeyPadEnter); + + BIND(VK_Return, Enter); BIND(VK_Tab, Tab); + BIND(VK_Space, Space); BIND(VK_Delete, BackSpace); + BIND(VK_Control, LeftControl); BIND(VK_Option, LeftOption); + BIND(VK_Command, LeftMeta); BIND(VK_Shift, LeftShift); + BIND(VK_RightControl, RightControl); BIND(VK_RightOption, RightOption); + BIND(VK_Escape, Escape); BIND(VK_CapsLock, CapsLock); + BIND(VK_Home, Home); BIND(VK_End, End); + BIND(VK_PageUp, PageUp); BIND(VK_PageDown, PageDown); + + BIND(VK_RightShift, RightShift); + BIND(VK_Help, Help); + BIND(VK_ForwardDelete, Delete); + + BIND(VK_LeftArrow, Left); BIND(VK_RightArrow, Right); + BIND(VK_DownArrow, Down); BIND(VK_UpArrow, Up); } - - @synchronized(self) { - Inputs::Keyboard &keyboard = keyboard_machine->get_keyboard(); - - // Connect the Carbon-era Mac keyboard scancodes to Clock Signal's 'universal' enumeration in order - // to pass into the platform-neutral realm. -#define BIND(source, dest) case source: keyboard.set_key_pressed(Inputs::Keyboard::Key::dest, pressedKey, isPressed); break - switch(key) { - BIND(VK_ANSI_0, k0); BIND(VK_ANSI_1, k1); BIND(VK_ANSI_2, k2); BIND(VK_ANSI_3, k3); BIND(VK_ANSI_4, k4); - BIND(VK_ANSI_5, k5); BIND(VK_ANSI_6, k6); BIND(VK_ANSI_7, k7); BIND(VK_ANSI_8, k8); BIND(VK_ANSI_9, k9); - - BIND(VK_ANSI_Q, Q); BIND(VK_ANSI_W, W); BIND(VK_ANSI_E, E); BIND(VK_ANSI_R, R); BIND(VK_ANSI_T, T); - BIND(VK_ANSI_Y, Y); BIND(VK_ANSI_U, U); BIND(VK_ANSI_I, I); BIND(VK_ANSI_O, O); BIND(VK_ANSI_P, P); - - BIND(VK_ANSI_A, A); BIND(VK_ANSI_S, S); BIND(VK_ANSI_D, D); BIND(VK_ANSI_F, F); BIND(VK_ANSI_G, G); - BIND(VK_ANSI_H, H); BIND(VK_ANSI_J, J); BIND(VK_ANSI_K, K); BIND(VK_ANSI_L, L); - - BIND(VK_ANSI_Z, Z); BIND(VK_ANSI_X, X); BIND(VK_ANSI_C, C); BIND(VK_ANSI_V, V); - BIND(VK_ANSI_B, B); BIND(VK_ANSI_N, N); BIND(VK_ANSI_M, M); - - BIND(VK_F1, F1); BIND(VK_F2, F2); BIND(VK_F3, F3); BIND(VK_F4, F4); - BIND(VK_F5, F5); BIND(VK_F6, F6); BIND(VK_F7, F7); BIND(VK_F8, F8); - BIND(VK_F9, F9); BIND(VK_F10, F10); BIND(VK_F11, F11); BIND(VK_F12, F12); - - BIND(VK_ANSI_Keypad0, KeyPad0); BIND(VK_ANSI_Keypad1, KeyPad1); BIND(VK_ANSI_Keypad2, KeyPad2); - BIND(VK_ANSI_Keypad3, KeyPad3); BIND(VK_ANSI_Keypad4, KeyPad4); BIND(VK_ANSI_Keypad5, KeyPad5); - BIND(VK_ANSI_Keypad6, KeyPad6); BIND(VK_ANSI_Keypad7, KeyPad7); BIND(VK_ANSI_Keypad8, KeyPad8); - BIND(VK_ANSI_Keypad9, KeyPad9); - - BIND(VK_ANSI_Equal, Equals); BIND(VK_ANSI_Minus, Hyphen); - BIND(VK_ANSI_RightBracket, CloseSquareBracket); BIND(VK_ANSI_LeftBracket, OpenSquareBracket); - BIND(VK_ANSI_Quote, Quote); BIND(VK_ANSI_Grave, BackTick); - - BIND(VK_ANSI_Semicolon, Semicolon); - BIND(VK_ANSI_Backslash, BackSlash); BIND(VK_ANSI_Slash, ForwardSlash); - BIND(VK_ANSI_Comma, Comma); BIND(VK_ANSI_Period, FullStop); - - BIND(VK_ANSI_KeypadDecimal, KeyPadDecimalPoint); BIND(VK_ANSI_KeypadEquals, KeyPadEquals); - BIND(VK_ANSI_KeypadMultiply, KeyPadAsterisk); BIND(VK_ANSI_KeypadDivide, KeyPadSlash); - BIND(VK_ANSI_KeypadPlus, KeyPadPlus); BIND(VK_ANSI_KeypadMinus, KeyPadMinus); - BIND(VK_ANSI_KeypadClear, KeyPadDelete); BIND(VK_ANSI_KeypadEnter, KeyPadEnter); - - BIND(VK_Return, Enter); BIND(VK_Tab, Tab); - BIND(VK_Space, Space); BIND(VK_Delete, BackSpace); - BIND(VK_Control, LeftControl); BIND(VK_Option, LeftOption); - BIND(VK_Command, LeftMeta); BIND(VK_Shift, LeftShift); - BIND(VK_RightControl, RightControl); BIND(VK_RightOption, RightOption); - BIND(VK_Escape, Escape); BIND(VK_CapsLock, CapsLock); - BIND(VK_Home, Home); BIND(VK_End, End); - BIND(VK_PageUp, PageUp); BIND(VK_PageDown, PageDown); - - BIND(VK_RightShift, RightShift); - BIND(VK_Help, Help); - BIND(VK_ForwardDelete, Delete); - - BIND(VK_LeftArrow, Left); BIND(VK_RightArrow, Right); - BIND(VK_DownArrow, Down); BIND(VK_UpArrow, Up); - } #undef BIND + + Inputs::Keyboard &keyboard = keyboard_machine->get_keyboard(); + + if(keyboard.observed_keys().find(mapped_key) != keyboard.observed_keys().end()) { + // Don't pass anything on if this is not new information. + if(_depressedKeys[key] == !!isPressed) return; + _depressedKeys[key] = !!isPressed; + + // Pick an ASCII code, if any. + char pressedKey = '\0'; + if(characters.length) { + unichar firstCharacter = [characters characterAtIndex:0]; + if(firstCharacter < 128) { + pressedKey = (char)firstCharacter; + } + } + + @synchronized(self) { + keyboard.set_key_pressed(mapped_key, pressedKey, isPressed); + } + return; } - return; } auto joystick_machine = _machine->joystick_machine(); @@ -536,8 +543,8 @@ struct ActivityObserver: public Activity::Observer { return !!_machine->joystick_machine(); } -- (BOOL)hasKeyboard { - return !!_machine->keyboard_machine(); +- (BOOL)hasExclusiveKeyboard { + return !!_machine->keyboard_machine() && _machine->keyboard_machine()->get_keyboard().is_exclusive(); } #pragma mark - Activity observation diff --git a/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm b/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm index de25c951a..509a0af1d 100644 --- a/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm +++ b/OSBindings/Mac/Clock SignalTests/MasterSystemVDPTests.mm @@ -116,7 +116,7 @@ NSAssert(vdp.get_interrupt_line(), @"Interrupt line wasn't set when promised"); } -- (void)testPrediction { +- (void)testInterruptPrediction { TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP); for(int c = 0; c < 256; ++c) { @@ -134,7 +134,7 @@ vdp.set_register(1, 0x8a); // Now run through an entire frame... - int half_cycles = 262*224*2; + int half_cycles = 262*228*2; int last_time_until_interrupt = vdp.get_time_until_interrupt().as_int(); while(half_cycles--) { // Validate that an interrupt happened if one was expected, and clear anything that's present. @@ -157,4 +157,20 @@ } } +- (void)testTimeUntilLine { + TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP); + + int time_until_line = vdp.get_time_until_line(-1).as_int(); + for(int c = 0; c < 262*228*5; ++c) { + vdp.run_for(HalfCycles(1)); + + const int time_remaining_until_line = vdp.get_time_until_line(-1).as_int(); + --time_until_line; + if(time_until_line) { + NSAssert(time_remaining_until_line == time_until_line, @"Discontinuity found in distance-to-line prediction; expected %d but got %d", time_until_line, time_remaining_until_line); + } + time_until_line = time_remaining_until_line; + } +} + @end diff --git a/OSBindings/SDL/main.cpp b/OSBindings/SDL/main.cpp index ea5b21a4c..80e541bfa 100644 --- a/OSBindings/SDL/main.cpp +++ b/OSBindings/SDL/main.cpp @@ -610,7 +610,7 @@ int main(int argc, char *argv[]) { case SDL_KEYDOWN: // Syphon off the key-press if it's control+shift+V (paste). if(event.key.keysym.sym == SDLK_v && (SDL_GetModState()&KMOD_CTRL) && (SDL_GetModState()&KMOD_SHIFT)) { - KeyboardMachine::Machine *keyboard_machine = machine->keyboard_machine(); + const auto keyboard_machine = machine->keyboard_machine(); if(keyboard_machine) { keyboard_machine->type_string(SDL_GetClipboardText()); break; @@ -686,7 +686,7 @@ int main(int argc, char *argv[]) { SDL_ShowCursor((fullscreen_mode&SDL_WINDOW_FULLSCREEN_DESKTOP) ? SDL_DISABLE : SDL_ENABLE); // Announce a potential discontinuity in keyboard input. - auto keyboard_machine = machine->keyboard_machine(); + const auto keyboard_machine = machine->keyboard_machine(); if(keyboard_machine) { keyboard_machine->get_keyboard().reset_all_keys(); } @@ -695,17 +695,19 @@ int main(int argc, char *argv[]) { const bool is_pressed = event.type == SDL_KEYDOWN; - KeyboardMachine::Machine *const keyboard_machine = machine->keyboard_machine(); + const auto keyboard_machine = machine->keyboard_machine(); if(keyboard_machine) { Inputs::Keyboard::Key key = Inputs::Keyboard::Key::Space; if(!KeyboardKeyForSDLScancode(event.key.keysym.scancode, key)) break; - 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(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; + keyboard_machine->get_keyboard().set_key_pressed(key, key_value, is_pressed); + break; + } } JoystickMachine::Machine *const joystick_machine = machine->joystick_machine();