mirror of
https://github.com/TomHarte/CLK.git
synced 2024-10-16 11:23:51 +00:00
Merge pull request #573 from TomHarte/SmallKeyboard
Extends the concept of a 'keyboard' to sets of keys less than a full keyboard in size
This commit is contained in:
commit
38c130df2b
@ -10,7 +10,8 @@
|
|||||||
|
|
||||||
using namespace Analyser::Dynamic;
|
using namespace Analyser::Dynamic;
|
||||||
|
|
||||||
MultiKeyboardMachine::MultiKeyboardMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) {
|
MultiKeyboardMachine::MultiKeyboardMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines) :
|
||||||
|
keyboard_(machines_) {
|
||||||
for(const auto &machine: machines) {
|
for(const auto &machine: machines) {
|
||||||
KeyboardMachine::Machine *keyboard_machine = machine->keyboard_machine();
|
KeyboardMachine::Machine *keyboard_machine = machine->keyboard_machine();
|
||||||
if(keyboard_machine) machines_.push_back(keyboard_machine);
|
if(keyboard_machine) machines_.push_back(keyboard_machine);
|
||||||
@ -35,9 +36,34 @@ void MultiKeyboardMachine::type_string(const std::string &string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultiKeyboardMachine::keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) {
|
Inputs::Keyboard &MultiKeyboardMachine::get_keyboard() {
|
||||||
|
return keyboard_;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiKeyboardMachine::MultiKeyboard::MultiKeyboard(const std::vector<::KeyboardMachine::Machine *> &machines)
|
||||||
|
: machines_(machines) {
|
||||||
for(const auto &machine: machines_) {
|
for(const auto &machine: machines_) {
|
||||||
uint16_t mapped_key = machine->get_keyboard_mapper()->mapped_key_for_key(key);
|
observed_keys_.insert(machine->get_keyboard().observed_keys().begin(), machine->get_keyboard().observed_keys().end());
|
||||||
if(mapped_key != KeyNotMapped) machine->set_key_state(mapped_key, is_pressed);
|
is_exclusive_ |= machine->get_keyboard().is_exclusive();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MultiKeyboardMachine::MultiKeyboard::set_key_pressed(Key key, char value, bool is_pressed) {
|
||||||
|
for(const auto &machine: machines_) {
|
||||||
|
machine->get_keyboard().set_key_pressed(key, value, is_pressed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiKeyboardMachine::MultiKeyboard::reset_all_keys() {
|
||||||
|
for(const auto &machine: machines_) {
|
||||||
|
machine->get_keyboard().reset_all_keys();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::set<Inputs::Keyboard::Key> &MultiKeyboardMachine::MultiKeyboard::observed_keys() {
|
||||||
|
return observed_keys_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MultiKeyboardMachine::MultiKeyboard::is_exclusive() {
|
||||||
|
return is_exclusive_;
|
||||||
|
}
|
||||||
|
@ -25,6 +25,25 @@ namespace Dynamic {
|
|||||||
order of delivered messages.
|
order of delivered messages.
|
||||||
*/
|
*/
|
||||||
class MultiKeyboardMachine: public KeyboardMachine::Machine {
|
class MultiKeyboardMachine: public KeyboardMachine::Machine {
|
||||||
|
private:
|
||||||
|
std::vector<::KeyboardMachine::Machine *> machines_;
|
||||||
|
|
||||||
|
class MultiKeyboard: public Inputs::Keyboard {
|
||||||
|
public:
|
||||||
|
MultiKeyboard(const std::vector<::KeyboardMachine::Machine *> &machines);
|
||||||
|
|
||||||
|
void set_key_pressed(Key key, char value, bool is_pressed) override;
|
||||||
|
void reset_all_keys() override;
|
||||||
|
const std::set<Key> &observed_keys() override;
|
||||||
|
bool is_exclusive() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::vector<::KeyboardMachine::Machine *> &machines_;
|
||||||
|
std::set<Key> observed_keys_;
|
||||||
|
bool is_exclusive_ = false;
|
||||||
|
};
|
||||||
|
MultiKeyboard keyboard_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MultiKeyboardMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
MultiKeyboardMachine(const std::vector<std::unique_ptr<::Machine::DynamicMachine>> &machines);
|
||||||
|
|
||||||
@ -32,10 +51,7 @@ class MultiKeyboardMachine: public KeyboardMachine::Machine {
|
|||||||
void clear_all_keys() override;
|
void clear_all_keys() override;
|
||||||
void set_key_state(uint16_t key, bool is_pressed) override;
|
void set_key_state(uint16_t key, bool is_pressed) override;
|
||||||
void type_string(const std::string &) 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_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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));
|
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() {
|
bool TMS9918::get_interrupt_line() {
|
||||||
return ((status_ & StatusInterrupt) && generate_interrupts_) || (enable_line_interrupts_ && line_interrupt_pending_);
|
return ((status_ & StatusInterrupt) && generate_interrupts_) || (enable_line_interrupts_ && line_interrupt_pending_);
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,16 @@ class TMS9918: public Base {
|
|||||||
*/
|
*/
|
||||||
HalfCycles get_time_until_interrupt();
|
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.
|
@returns @c true if the interrupt line is currently active; @c false otherwise.
|
||||||
*/
|
*/
|
||||||
|
@ -10,7 +10,13 @@
|
|||||||
|
|
||||||
using namespace Inputs;
|
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<Key> &observed_keys) : observed_keys_(observed_keys), is_exclusive_(false) {}
|
||||||
|
|
||||||
void Keyboard::set_key_pressed(Key key, char value, bool is_pressed) {
|
void Keyboard::set_key_pressed(Key key, char value, bool is_pressed) {
|
||||||
std::size_t key_offset = static_cast<std::size_t>(key);
|
std::size_t key_offset = static_cast<std::size_t>(key);
|
||||||
@ -36,3 +42,11 @@ bool Keyboard::get_key_state(Key key) {
|
|||||||
if(key_offset >= key_states_.size()) return false;
|
if(key_offset >= key_states_.size()) return false;
|
||||||
return key_states_[key_offset];
|
return key_states_[key_offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::set<Keyboard::Key> &Keyboard::observed_keys() {
|
||||||
|
return observed_keys_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Keyboard::is_exclusive() {
|
||||||
|
return is_exclusive_;
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#define Keyboard_hpp
|
#define Keyboard_hpp
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
namespace Inputs {
|
namespace Inputs {
|
||||||
|
|
||||||
@ -20,8 +21,6 @@ namespace Inputs {
|
|||||||
*/
|
*/
|
||||||
class Keyboard {
|
class Keyboard {
|
||||||
public:
|
public:
|
||||||
Keyboard();
|
|
||||||
|
|
||||||
enum class Key {
|
enum class Key {
|
||||||
Escape, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, PrintScreen, ScrollLock, Pause,
|
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,
|
BackTick, k1, k2, k3, k4, k5, k6, k7, k8, k9, k0, Hyphen, Equals, BackSpace,
|
||||||
@ -39,10 +38,26 @@ class Keyboard {
|
|||||||
Help
|
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<Key> &observed_keys);
|
||||||
|
|
||||||
// Host interface.
|
// Host interface.
|
||||||
virtual void set_key_pressed(Key key, char value, bool is_pressed);
|
virtual void set_key_pressed(Key key, char value, bool is_pressed);
|
||||||
virtual void reset_all_keys();
|
virtual void reset_all_keys();
|
||||||
|
|
||||||
|
/// @returns a set of all Keys that this keyboard responds to.
|
||||||
|
virtual const std::set<Key> &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.
|
// Delegate interface.
|
||||||
struct Delegate {
|
struct Delegate {
|
||||||
virtual void keyboard_did_change_key(Keyboard *keyboard, Key key, bool is_pressed) = 0;
|
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);
|
bool get_key_state(Key key);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::set<Key> observed_keys_;
|
||||||
std::vector<bool> key_states_;
|
std::vector<bool> key_states_;
|
||||||
Delegate *delegate_ = nullptr;
|
Delegate *delegate_ = nullptr;
|
||||||
|
bool is_exclusive_ = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -757,7 +757,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
|||||||
template <bool has_fdc> class ConcreteMachine:
|
template <bool has_fdc> class ConcreteMachine:
|
||||||
public CRTMachine::Machine,
|
public CRTMachine::Machine,
|
||||||
public MediaTarget::Machine,
|
public MediaTarget::Machine,
|
||||||
public KeyboardMachine::Machine,
|
public KeyboardMachine::MappedMachine,
|
||||||
public Utility::TypeRecipient,
|
public Utility::TypeRecipient,
|
||||||
public CPU::Z80::BusHandler,
|
public CPU::Z80::BusHandler,
|
||||||
public ClockingHint::Observer,
|
public ClockingHint::Observer,
|
||||||
|
@ -75,9 +75,9 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
||||||
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence}
|
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
|
||||||
#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence}
|
#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
|
||||||
#define X {KeyboardMachine::Machine::KeyNotMapped}
|
#define X {KeyboardMachine::MappedMachine::KeyNotMapped}
|
||||||
static KeySequence key_sequences[] = {
|
static KeySequence key_sequences[] = {
|
||||||
/* NUL */ X, /* SOH */ X,
|
/* NUL */ X, /* SOH */ X,
|
||||||
/* STX */ X, /* ETX */ X,
|
/* STX */ X, /* ETX */ X,
|
||||||
|
@ -33,7 +33,7 @@ enum Key: uint16_t {
|
|||||||
#undef Line
|
#undef Line
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KeyboardMapper: public KeyboardMachine::Machine::KeyboardMapper {
|
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
||||||
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
|
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ namespace {
|
|||||||
template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||||
public CRTMachine::Machine,
|
public CRTMachine::Machine,
|
||||||
public MediaTarget::Machine,
|
public MediaTarget::Machine,
|
||||||
public KeyboardMachine::Machine,
|
public KeyboardMachine::MappedMachine,
|
||||||
public CPU::MOS6502::BusHandler,
|
public CPU::MOS6502::BusHandler,
|
||||||
public Inputs::Keyboard,
|
public Inputs::Keyboard,
|
||||||
public AppleII::Machine,
|
public AppleII::Machine,
|
||||||
|
@ -68,13 +68,13 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
BIND(F7, KeyF7);
|
BIND(F7, KeyF7);
|
||||||
}
|
}
|
||||||
#undef BIND
|
#undef BIND
|
||||||
return KeyboardMachine::Machine::KeyNotMapped;
|
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
||||||
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence}
|
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
|
||||||
#define SHIFT(...) {KeyLShift, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence}
|
#define SHIFT(...) {KeyLShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
|
||||||
#define X {KeyboardMachine::Machine::KeyNotMapped}
|
#define X {KeyboardMachine::MappedMachine::KeyNotMapped}
|
||||||
static KeySequence key_sequences[] = {
|
static KeySequence key_sequences[] = {
|
||||||
/* NUL */ X, /* SOH */ X,
|
/* NUL */ X, /* SOH */ X,
|
||||||
/* STX */ X, /* ETX */ X,
|
/* STX */ X, /* ETX */ X,
|
||||||
|
@ -38,7 +38,7 @@ enum Key: uint16_t {
|
|||||||
#undef key
|
#undef key
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KeyboardMapper: public KeyboardMachine::Machine::KeyboardMapper {
|
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
||||||
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
|
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -281,7 +281,7 @@ class Joystick: public Inputs::ConcreteJoystick {
|
|||||||
class ConcreteMachine:
|
class ConcreteMachine:
|
||||||
public CRTMachine::Machine,
|
public CRTMachine::Machine,
|
||||||
public MediaTarget::Machine,
|
public MediaTarget::Machine,
|
||||||
public KeyboardMachine::Machine,
|
public KeyboardMachine::MappedMachine,
|
||||||
public JoystickMachine::Machine,
|
public JoystickMachine::Machine,
|
||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
public CPU::MOS6502::BusHandler,
|
public CPU::MOS6502::BusHandler,
|
||||||
|
@ -42,7 +42,7 @@ class ConcreteMachine:
|
|||||||
public Machine,
|
public Machine,
|
||||||
public CRTMachine::Machine,
|
public CRTMachine::Machine,
|
||||||
public MediaTarget::Machine,
|
public MediaTarget::Machine,
|
||||||
public KeyboardMachine::Machine,
|
public KeyboardMachine::MappedMachine,
|
||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
public CPU::MOS6502::BusHandler,
|
public CPU::MOS6502::BusHandler,
|
||||||
public Tape::Delegate,
|
public Tape::Delegate,
|
||||||
|
@ -56,10 +56,10 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
||||||
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence}
|
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
|
||||||
#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence}
|
#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
|
||||||
#define CTRL(...) {KeyControl, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence}
|
#define CTRL(...) {KeyControl, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
|
||||||
#define X {KeyboardMachine::Machine::KeyNotMapped}
|
#define X {KeyboardMachine::MappedMachine::KeyNotMapped}
|
||||||
static KeySequence key_sequences[] = {
|
static KeySequence key_sequences[] = {
|
||||||
/* NUL */ X, /* SOH */ X,
|
/* NUL */ X, /* SOH */ X,
|
||||||
/* STX */ X, /* ETX */ X,
|
/* STX */ X, /* ETX */ X,
|
||||||
|
@ -33,7 +33,7 @@ enum Key: uint16_t {
|
|||||||
KeyBreak = 0xfffd,
|
KeyBreak = 0xfffd,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KeyboardMapper: public KeyboardMachine::Machine::KeyboardMapper {
|
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
||||||
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
|
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,27 +10,27 @@
|
|||||||
|
|
||||||
using namespace KeyboardMachine;
|
using namespace KeyboardMachine;
|
||||||
|
|
||||||
Machine::Machine() {
|
MappedMachine::MappedMachine() {
|
||||||
keyboard_.set_delegate(this);
|
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);
|
uint16_t mapped_key = get_keyboard_mapper()->mapped_key_for_key(key);
|
||||||
if(mapped_key != KeyNotMapped) set_key_state(mapped_key, is_pressed);
|
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.
|
// TODO: unify naming.
|
||||||
clear_all_keys();
|
clear_all_keys();
|
||||||
}
|
}
|
||||||
|
|
||||||
Inputs::Keyboard &Machine::get_keyboard() {
|
Inputs::Keyboard &MappedMachine::get_keyboard() {
|
||||||
return keyboard_;
|
return keyboard_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Machine::type_string(const std::string &) {
|
void Machine::type_string(const std::string &) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Machine::KeyboardMapper *Machine::get_keyboard_mapper() {
|
MappedMachine::KeyboardMapper *MappedMachine::get_keyboard_mapper() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -33,13 +33,10 @@ struct KeyActions {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Describes the full functionality of being an emulated machine with a keyboard: not just being
|
Describes an emulated machine which exposes a keyboard and accepts a typed string.
|
||||||
able to receive key actions, but being able to vend a generic keyboard and a keyboard mapper.
|
|
||||||
*/
|
*/
|
||||||
class Machine: public Inputs::Keyboard::Delegate, public KeyActions {
|
class Machine: public KeyActions {
|
||||||
public:
|
public:
|
||||||
Machine();
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Causes the machine to attempt to type the supplied string.
|
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.
|
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.
|
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();
|
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:
|
private:
|
||||||
void keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) override;
|
void keyboard_did_change_key(Inputs::Keyboard *keyboard, Inputs::Keyboard::Key key, bool is_pressed) override;
|
||||||
void reset_all_keys(Inputs::Keyboard *keyboard) override;
|
void reset_all_keys(Inputs::Keyboard *keyboard) override;
|
||||||
|
@ -57,5 +57,5 @@ uint16_t MSX::KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
#undef BIND
|
#undef BIND
|
||||||
return KeyboardMachine::Machine::KeyNotMapped;
|
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ enum Key: uint16_t {
|
|||||||
#undef Line
|
#undef Line
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KeyboardMapper: public KeyboardMachine::Machine::KeyboardMapper {
|
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
||||||
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
|
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ class ConcreteMachine:
|
|||||||
public CPU::Z80::BusHandler,
|
public CPU::Z80::BusHandler,
|
||||||
public CRTMachine::Machine,
|
public CRTMachine::Machine,
|
||||||
public MediaTarget::Machine,
|
public MediaTarget::Machine,
|
||||||
public KeyboardMachine::Machine,
|
public KeyboardMachine::MappedMachine,
|
||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
public JoystickMachine::Machine,
|
public JoystickMachine::Machine,
|
||||||
public MemoryMap,
|
public MemoryMap,
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include "../CRTMachine.hpp"
|
#include "../CRTMachine.hpp"
|
||||||
#include "../JoystickMachine.hpp"
|
#include "../JoystickMachine.hpp"
|
||||||
|
#include "../KeyboardMachine.hpp"
|
||||||
|
|
||||||
#include "../../ClockReceiver/ForceInline.hpp"
|
#include "../../ClockReceiver/ForceInline.hpp"
|
||||||
|
|
||||||
@ -81,6 +82,8 @@ class ConcreteMachine:
|
|||||||
public Machine,
|
public Machine,
|
||||||
public CPU::Z80::BusHandler,
|
public CPU::Z80::BusHandler,
|
||||||
public CRTMachine::Machine,
|
public CRTMachine::Machine,
|
||||||
|
public KeyboardMachine::Machine,
|
||||||
|
public Inputs::Keyboard::Delegate,
|
||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
public JoystickMachine::Machine {
|
public JoystickMachine::Machine {
|
||||||
|
|
||||||
@ -94,7 +97,8 @@ class ConcreteMachine:
|
|||||||
(target.model == Target::Model::SG1000) ? TI::SN76489::Personality::SN76489 : TI::SN76489::Personality::SMS,
|
(target.model == Target::Model::SG1000) ? TI::SN76489::Personality::SN76489 : TI::SN76489::Personality::SMS,
|
||||||
audio_queue_,
|
audio_queue_,
|
||||||
sn76489_divider),
|
sn76489_divider),
|
||||||
speaker_(sn76489_) {
|
speaker_(sn76489_),
|
||||||
|
keyboard_({Inputs::Keyboard::Key::Enter, Inputs::Keyboard::Key::Escape}) {
|
||||||
// Pick the clock rate based on the region.
|
// Pick the clock rate based on the region.
|
||||||
const double clock_rate = target.region == Target::Region::Europe ? 3546893.0 : 3579540.0;
|
const double clock_rate = target.region == Target::Region::Europe ? 3546893.0 : 3579540.0;
|
||||||
speaker_.set_input_rate(static_cast<float>(clock_rate / sn76489_divider));
|
speaker_.set_input_rate(static_cast<float>(clock_rate / sn76489_divider));
|
||||||
@ -146,6 +150,8 @@ class ConcreteMachine:
|
|||||||
|
|
||||||
// Apple a relatively low low-pass filter. More guidance needed here.
|
// Apple a relatively low low-pass filter. More guidance needed here.
|
||||||
speaker_.set_high_frequency_cutoff(8000);
|
speaker_.set_high_frequency_cutoff(8000);
|
||||||
|
|
||||||
|
keyboard_.set_delegate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
~ConcreteMachine() {
|
~ConcreteMachine() {
|
||||||
@ -164,6 +170,8 @@ class ConcreteMachine:
|
|||||||
(region_ == Target::Region::Europe) ?
|
(region_ == Target::Region::Europe) ?
|
||||||
TI::TMS::TVStandard::PAL : TI::TMS::TVStandard::NTSC);
|
TI::TMS::TVStandard::PAL : TI::TMS::TVStandard::NTSC);
|
||||||
get_crt()->set_video_signal(Outputs::CRT::VideoSignal::Composite);
|
get_crt()->set_video_signal(Outputs::CRT::VideoSignal::Composite);
|
||||||
|
|
||||||
|
time_until_debounce_ = vdp_->get_time_until_line(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_output() override {
|
void close_output() override {
|
||||||
@ -213,7 +221,7 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(write_pointers_[address >> 10]) write_pointers_[address >> 10][address & 1023] = *cycle.value;
|
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;
|
break;
|
||||||
|
|
||||||
case CPU::Z80::PartialMachineCycle::Input:
|
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);
|
return HalfCycles(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,6 +350,23 @@ class ConcreteMachine:
|
|||||||
return joysticks_;
|
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.
|
// MARK: - Configuration options.
|
||||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() override {
|
std::vector<std::unique_ptr<Configurable::Option>> get_options() override {
|
||||||
return Sega::MasterSystem::get_options();
|
return Sega::MasterSystem::get_options();
|
||||||
@ -388,10 +422,13 @@ class ConcreteMachine:
|
|||||||
Outputs::Speaker::LowpassSpeaker<TI::SN76489> speaker_;
|
Outputs::Speaker::LowpassSpeaker<TI::SN76489> speaker_;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
std::vector<std::unique_ptr<Inputs::Joystick>> joysticks_;
|
||||||
|
Inputs::Keyboard keyboard_;
|
||||||
|
bool reset_is_pressed_ = false, pause_is_pressed_ = false;
|
||||||
|
|
||||||
HalfCycles time_since_vdp_update_;
|
HalfCycles time_since_vdp_update_;
|
||||||
HalfCycles time_since_sn76489_update_;
|
HalfCycles time_since_sn76489_update_;
|
||||||
HalfCycles time_until_interrupt_;
|
HalfCycles time_until_interrupt_;
|
||||||
|
HalfCycles time_until_debounce_;
|
||||||
|
|
||||||
uint8_t ram_[8*1024];
|
uint8_t ram_[8*1024];
|
||||||
uint8_t bios_[8*1024];
|
uint8_t bios_[8*1024];
|
||||||
|
@ -48,13 +48,13 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
}
|
}
|
||||||
#undef BIND
|
#undef BIND
|
||||||
|
|
||||||
return KeyboardMachine::Machine::KeyNotMapped;
|
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
||||||
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence}
|
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
|
||||||
#define SHIFT(...) {KeyLeftShift, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence}
|
#define SHIFT(...) {KeyLeftShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
|
||||||
#define X {KeyboardMachine::Machine::KeyNotMapped}
|
#define X {KeyboardMachine::MappedMachine::KeyNotMapped}
|
||||||
static KeySequence key_sequences[] = {
|
static KeySequence key_sequences[] = {
|
||||||
/* NUL */ X, /* SOH */ X,
|
/* NUL */ X, /* SOH */ X,
|
||||||
/* STX */ X, /* ETX */ X,
|
/* STX */ X, /* ETX */ X,
|
||||||
|
@ -35,7 +35,7 @@ enum Key: uint16_t {
|
|||||||
KeyNMI = 0xfffd,
|
KeyNMI = 0xfffd,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KeyboardMapper: public KeyboardMachine::Machine::KeyboardMapper {
|
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
||||||
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
|
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
|
|||||||
template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class ConcreteMachine:
|
template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class ConcreteMachine:
|
||||||
public CRTMachine::Machine,
|
public CRTMachine::Machine,
|
||||||
public MediaTarget::Machine,
|
public MediaTarget::Machine,
|
||||||
public KeyboardMachine::Machine,
|
public KeyboardMachine::MappedMachine,
|
||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
public CPU::MOS6502::BusHandler,
|
public CPU::MOS6502::BusHandler,
|
||||||
public MOS::MOS6522::IRQDelegatePortHandler::Delegate,
|
public MOS::MOS6522::IRQDelegatePortHandler::Delegate,
|
||||||
|
@ -43,14 +43,14 @@ void Typer::run_for(const HalfCycles duration) {
|
|||||||
bool Typer::try_type_next_character() {
|
bool Typer::try_type_next_character() {
|
||||||
uint16_t *sequence = character_mapper_->sequence_for_character(string_[string_pointer_]);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!phase_) delegate_->clear_all_keys();
|
if(!phase_) delegate_->clear_all_keys();
|
||||||
else {
|
else {
|
||||||
delegate_->set_key_state(sequence[phase_ - 1], true);
|
delegate_->set_key_state(sequence[phase_ - 1], true);
|
||||||
return sequence[phase_] != KeyboardMachine::Machine::KeyEndSequence;
|
return sequence[phase_] != KeyboardMachine::MappedMachine::KeyEndSequence;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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) {
|
uint16_t *CharacterMapper::table_lookup_sequence_for_character(KeySequence *sequences, std::size_t length, char character) {
|
||||||
std::size_t ucharacter = static_cast<std::size_t>((unsigned char)character);
|
std::size_t ucharacter = static_cast<std::size_t>((unsigned char)character);
|
||||||
if(ucharacter > (length / sizeof(KeySequence))) return nullptr;
|
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];
|
return sequences[ucharacter];
|
||||||
}
|
}
|
||||||
|
@ -30,15 +30,15 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
BIND(Space, KeySpace);
|
BIND(Space, KeySpace);
|
||||||
}
|
}
|
||||||
#undef BIND
|
#undef BIND
|
||||||
return KeyboardMachine::Machine::KeyNotMapped;
|
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterMapper::CharacterMapper(bool is_zx81) : is_zx81_(is_zx81) {}
|
CharacterMapper::CharacterMapper(bool is_zx81) : is_zx81_(is_zx81) {}
|
||||||
|
|
||||||
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
uint16_t *CharacterMapper::sequence_for_character(char character) {
|
||||||
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence}
|
#define KEYS(...) {__VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
|
||||||
#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::Machine::KeyEndSequence}
|
#define SHIFT(...) {KeyShift, __VA_ARGS__, KeyboardMachine::MappedMachine::KeyEndSequence}
|
||||||
#define X {KeyboardMachine::Machine::KeyNotMapped}
|
#define X {KeyboardMachine::MappedMachine::KeyNotMapped}
|
||||||
static KeySequence zx81_key_sequences[] = {
|
static KeySequence zx81_key_sequences[] = {
|
||||||
/* NUL */ X, /* SOH */ X,
|
/* NUL */ X, /* SOH */ X,
|
||||||
/* STX */ X, /* ETX */ X,
|
/* STX */ X, /* ETX */ X,
|
||||||
|
@ -25,7 +25,7 @@ enum Key: uint16_t {
|
|||||||
KeySpace = 0x0700 | 0x01, KeyDot = 0x0700 | 0x02, KeyM = 0x0700 | 0x04, KeyN = 0x0700 | 0x08, KeyB = 0x0700 | 0x10,
|
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);
|
uint16_t mapped_key_for_key(Inputs::Keyboard::Key key);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
|||||||
template<bool is_zx81> class ConcreteMachine:
|
template<bool is_zx81> class ConcreteMachine:
|
||||||
public CRTMachine::Machine,
|
public CRTMachine::Machine,
|
||||||
public MediaTarget::Machine,
|
public MediaTarget::Machine,
|
||||||
public KeyboardMachine::Machine,
|
public KeyboardMachine::MappedMachine,
|
||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
public Utility::TypeRecipient,
|
public Utility::TypeRecipient,
|
||||||
public CPU::Z80::BusHandler,
|
public CPU::Z80::BusHandler,
|
||||||
|
@ -301,7 +301,7 @@ class MachineDocument:
|
|||||||
if let menuItem = item as? NSMenuItem {
|
if let menuItem = item as? NSMenuItem {
|
||||||
switch item.action {
|
switch item.action {
|
||||||
case #selector(self.useKeyboardAsKeyboard):
|
case #selector(self.useKeyboardAsKeyboard):
|
||||||
if machine == nil || !machine.hasKeyboard {
|
if machine == nil || !machine.hasExclusiveKeyboard {
|
||||||
menuItem.state = .off
|
menuItem.state = .off
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
|||||||
- (bool)supportsVideoSignal:(CSMachineVideoSignal)videoSignal;
|
- (bool)supportsVideoSignal:(CSMachineVideoSignal)videoSignal;
|
||||||
|
|
||||||
// Input control.
|
// Input control.
|
||||||
@property (nonatomic, readonly) BOOL hasKeyboard;
|
@property (nonatomic, readonly) BOOL hasExclusiveKeyboard;
|
||||||
@property (nonatomic, readonly) BOOL hasJoystick;
|
@property (nonatomic, readonly) BOOL hasJoystick;
|
||||||
@property (nonatomic, assign) CSMachineKeyboardInputMode inputMode;
|
@property (nonatomic, assign) CSMachineKeyboardInputMode inputMode;
|
||||||
@property (nonatomic, nullable) CSJoystickManager *joystickManager;
|
@property (nonatomic, nullable) CSJoystickManager *joystickManager;
|
||||||
|
@ -91,7 +91,9 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
_machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(), error));
|
_machine.reset(Machine::MachineForTargets(_analyser.targets, CSROMFetcher(), error));
|
||||||
if(!_machine) return nil;
|
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];
|
_leds = [[NSMutableArray alloc] init];
|
||||||
Activity::Source *const activity_source = _machine->activity_source();
|
Activity::Source *const activity_source = _machine->activity_source();
|
||||||
@ -301,26 +303,11 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
|
|
||||||
- (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed {
|
- (void)setKey:(uint16_t)key characters:(NSString *)characters isPressed:(BOOL)isPressed {
|
||||||
auto keyboard_machine = _machine->keyboard_machine();
|
auto keyboard_machine = _machine->keyboard_machine();
|
||||||
if(self.inputMode == CSMachineKeyboardInputModeKeyboard && keyboard_machine) {
|
if(keyboard_machine && (self.inputMode == CSMachineKeyboardInputModeKeyboard || !keyboard_machine->get_keyboard().is_exclusive())) {
|
||||||
// Don't pass anything on if this is not new information.
|
Inputs::Keyboard::Key mapped_key = Inputs::Keyboard::Key::Help; // Make an innocuous default guess.
|
||||||
if(_depressedKeys[key] == !!isPressed) return;
|
#define BIND(source, dest) case source: mapped_key = Inputs::Keyboard::Key::dest; break;
|
||||||
_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) {
|
|
||||||
Inputs::Keyboard &keyboard = keyboard_machine->get_keyboard();
|
|
||||||
|
|
||||||
// Connect the Carbon-era Mac keyboard scancodes to Clock Signal's 'universal' enumeration in order
|
// Connect the Carbon-era Mac keyboard scancodes to Clock Signal's 'universal' enumeration in order
|
||||||
// to pass into the platform-neutral realm.
|
// 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) {
|
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_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_5, k5); BIND(VK_ANSI_6, k6); BIND(VK_ANSI_7, k7); BIND(VK_ANSI_8, k8); BIND(VK_ANSI_9, k9);
|
||||||
@ -373,9 +360,29 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
BIND(VK_DownArrow, Down); BIND(VK_UpArrow, Up);
|
BIND(VK_DownArrow, Down); BIND(VK_UpArrow, Up);
|
||||||
}
|
}
|
||||||
#undef BIND
|
#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();
|
auto joystick_machine = _machine->joystick_machine();
|
||||||
if(self.inputMode == CSMachineKeyboardInputModeJoystick && joystick_machine) {
|
if(self.inputMode == CSMachineKeyboardInputModeJoystick && joystick_machine) {
|
||||||
@ -536,8 +543,8 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
return !!_machine->joystick_machine();
|
return !!_machine->joystick_machine();
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)hasKeyboard {
|
- (BOOL)hasExclusiveKeyboard {
|
||||||
return !!_machine->keyboard_machine();
|
return !!_machine->keyboard_machine() && _machine->keyboard_machine()->get_keyboard().is_exclusive();
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Activity observation
|
#pragma mark - Activity observation
|
||||||
|
@ -116,7 +116,7 @@
|
|||||||
NSAssert(vdp.get_interrupt_line(), @"Interrupt line wasn't set when promised");
|
NSAssert(vdp.get_interrupt_line(), @"Interrupt line wasn't set when promised");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testPrediction {
|
- (void)testInterruptPrediction {
|
||||||
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
|
TI::TMS::TMS9918 vdp(TI::TMS::Personality::SMSVDP);
|
||||||
|
|
||||||
for(int c = 0; c < 256; ++c) {
|
for(int c = 0; c < 256; ++c) {
|
||||||
@ -134,7 +134,7 @@
|
|||||||
vdp.set_register(1, 0x8a);
|
vdp.set_register(1, 0x8a);
|
||||||
|
|
||||||
// Now run through an entire frame...
|
// 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();
|
int last_time_until_interrupt = vdp.get_time_until_interrupt().as_int();
|
||||||
while(half_cycles--) {
|
while(half_cycles--) {
|
||||||
// Validate that an interrupt happened if one was expected, and clear anything that's present.
|
// 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
|
@end
|
||||||
|
@ -610,7 +610,7 @@ int main(int argc, char *argv[]) {
|
|||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
// Syphon off the key-press if it's control+shift+V (paste).
|
// 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)) {
|
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) {
|
if(keyboard_machine) {
|
||||||
keyboard_machine->type_string(SDL_GetClipboardText());
|
keyboard_machine->type_string(SDL_GetClipboardText());
|
||||||
break;
|
break;
|
||||||
@ -686,7 +686,7 @@ int main(int argc, char *argv[]) {
|
|||||||
SDL_ShowCursor((fullscreen_mode&SDL_WINDOW_FULLSCREEN_DESKTOP) ? SDL_DISABLE : SDL_ENABLE);
|
SDL_ShowCursor((fullscreen_mode&SDL_WINDOW_FULLSCREEN_DESKTOP) ? SDL_DISABLE : SDL_ENABLE);
|
||||||
|
|
||||||
// Announce a potential discontinuity in keyboard input.
|
// Announce a potential discontinuity in keyboard input.
|
||||||
auto keyboard_machine = machine->keyboard_machine();
|
const auto keyboard_machine = machine->keyboard_machine();
|
||||||
if(keyboard_machine) {
|
if(keyboard_machine) {
|
||||||
keyboard_machine->get_keyboard().reset_all_keys();
|
keyboard_machine->get_keyboard().reset_all_keys();
|
||||||
}
|
}
|
||||||
@ -695,11 +695,12 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
const bool is_pressed = event.type == SDL_KEYDOWN;
|
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) {
|
if(keyboard_machine) {
|
||||||
Inputs::Keyboard::Key key = Inputs::Keyboard::Key::Space;
|
Inputs::Keyboard::Key key = Inputs::Keyboard::Key::Space;
|
||||||
if(!KeyboardKeyForSDLScancode(event.key.keysym.scancode, key)) break;
|
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';
|
char key_value = '\0';
|
||||||
const char *key_name = SDL_GetKeyName(event.key.keysym.sym);
|
const char *key_name = SDL_GetKeyName(event.key.keysym.sym);
|
||||||
if(key_name[0] >= 0) key_value = key_name[0];
|
if(key_name[0] >= 0) key_value = key_name[0];
|
||||||
@ -707,6 +708,7 @@ int main(int argc, char *argv[]) {
|
|||||||
keyboard_machine->get_keyboard().set_key_pressed(key, key_value, is_pressed);
|
keyboard_machine->get_keyboard().set_key_pressed(key, key_value, is_pressed);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
JoystickMachine::Machine *const joystick_machine = machine->joystick_machine();
|
JoystickMachine::Machine *const joystick_machine = machine->joystick_machine();
|
||||||
if(joystick_machine) {
|
if(joystick_machine) {
|
||||||
|
Loading…
Reference in New Issue
Block a user