mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-01 02:31:00 +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;
|
||||
|
||||
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) {
|
||||
KeyboardMachine::Machine *keyboard_machine = machine->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_) {
|
||||
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);
|
||||
observed_keys_.insert(machine->get_keyboard().observed_keys().begin(), machine->get_keyboard().observed_keys().end());
|
||||
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.
|
||||
*/
|
||||
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:
|
||||
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 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;
|
||||
|
||||
private:
|
||||
std::vector<::KeyboardMachine::Machine *> machines_;
|
||||
Inputs::Keyboard &get_keyboard() override;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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_);
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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<Key> &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<std::size_t>(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::Key> &Keyboard::observed_keys() {
|
||||
return observed_keys_;
|
||||
}
|
||||
|
||||
bool Keyboard::is_exclusive() {
|
||||
return is_exclusive_;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define Keyboard_hpp
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
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<Key> &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<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.
|
||||
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<Key> observed_keys_;
|
||||
std::vector<bool> key_states_;
|
||||
Delegate *delegate_ = nullptr;
|
||||
bool is_exclusive_ = true;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -757,7 +757,7 @@ class i8255PortHandler : public Intel::i8255::PortHandler {
|
||||
template <bool has_fdc> class ConcreteMachine:
|
||||
public CRTMachine::Machine,
|
||||
public MediaTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
public KeyboardMachine::MappedMachine,
|
||||
public Utility::TypeRecipient,
|
||||
public CPU::Z80::BusHandler,
|
||||
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) {
|
||||
#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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -40,7 +40,7 @@ namespace {
|
||||
template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
||||
public CRTMachine::Machine,
|
||||
public MediaTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
public KeyboardMachine::MappedMachine,
|
||||
public CPU::MOS6502::BusHandler,
|
||||
public Inputs::Keyboard,
|
||||
public AppleII::Machine,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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<float>(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<std::unique_ptr<Configurable::Option>> get_options() override {
|
||||
return Sega::MasterSystem::get_options();
|
||||
@ -388,10 +422,13 @@ class ConcreteMachine:
|
||||
Outputs::Speaker::LowpassSpeaker<TI::SN76489> speaker_;
|
||||
|
||||
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_sn76489_update_;
|
||||
HalfCycles time_until_interrupt_;
|
||||
HalfCycles time_until_debounce_;
|
||||
|
||||
uint8_t ram_[8*1024];
|
||||
uint8_t bios_[8*1024];
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -194,7 +194,7 @@ class VIAPortHandler: public MOS::MOS6522::IRQDelegatePortHandler {
|
||||
template <Analyser::Static::Oric::Target::DiskInterface disk_interface> 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,
|
||||
|
@ -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<std::size_t>((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];
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -60,7 +60,7 @@ std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||
template<bool is_zx81> class ConcreteMachine:
|
||||
public CRTMachine::Machine,
|
||||
public MediaTarget::Machine,
|
||||
public KeyboardMachine::Machine,
|
||||
public KeyboardMachine::MappedMachine,
|
||||
public Configurable::Device,
|
||||
public Utility::TypeRecipient,
|
||||
public CPU::Z80::BusHandler,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user