mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +00:00
Merge pull request #763 from TomHarte/LogicalKeyboards
Adds support for 'logical' keyboard entry
This commit is contained in:
commit
1a539521f2
@ -36,6 +36,14 @@ void MultiKeyboardMachine::type_string(const std::string &string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MultiKeyboardMachine::can_type(char c) {
|
||||||
|
bool can_type = true;
|
||||||
|
for(const auto &machine: machines_) {
|
||||||
|
can_type &= machine->can_type(c);
|
||||||
|
}
|
||||||
|
return can_type;
|
||||||
|
}
|
||||||
|
|
||||||
Inputs::Keyboard &MultiKeyboardMachine::get_keyboard() {
|
Inputs::Keyboard &MultiKeyboardMachine::get_keyboard() {
|
||||||
return keyboard_;
|
return keyboard_;
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ class MultiKeyboardMachine: public KeyboardMachine::Machine {
|
|||||||
void clear_all_keys() final;
|
void clear_all_keys() final;
|
||||||
void set_key_state(uint16_t key, bool is_pressed) final;
|
void set_key_state(uint16_t key, bool is_pressed) final;
|
||||||
void type_string(const std::string &) final;
|
void type_string(const std::string &) final;
|
||||||
|
bool can_type(char c) final;
|
||||||
Inputs::Keyboard &get_keyboard() final;
|
Inputs::Keyboard &get_keyboard() final;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -786,7 +786,7 @@ template <bool has_fdc> class ConcreteMachine:
|
|||||||
public CRTMachine::Machine,
|
public CRTMachine::Machine,
|
||||||
public MediaTarget::Machine,
|
public MediaTarget::Machine,
|
||||||
public KeyboardMachine::MappedMachine,
|
public KeyboardMachine::MappedMachine,
|
||||||
public Utility::TypeRecipient,
|
public Utility::TypeRecipient<CharacterMapper>,
|
||||||
public CPU::Z80::BusHandler,
|
public CPU::Z80::BusHandler,
|
||||||
public ClockingHint::Observer,
|
public ClockingHint::Observer,
|
||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
@ -1079,16 +1079,19 @@ template <bool has_fdc> class ConcreteMachine:
|
|||||||
|
|
||||||
// MARK: - Keyboard
|
// MARK: - Keyboard
|
||||||
void type_string(const std::string &string) final {
|
void type_string(const std::string &string) final {
|
||||||
std::unique_ptr<CharacterMapper> mapper(new CharacterMapper());
|
Utility::TypeRecipient<CharacterMapper>::add_typer(string);
|
||||||
Utility::TypeRecipient::add_typer(string, std::move(mapper));
|
}
|
||||||
|
|
||||||
|
bool can_type(char c) final {
|
||||||
|
return Utility::TypeRecipient<CharacterMapper>::can_type(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
HalfCycles get_typer_delay() final {
|
HalfCycles get_typer_delay() final {
|
||||||
return Cycles(4000000); // Wait 1 second before typing.
|
return z80_.get_is_resetting() ? Cycles(3'400'000) : Cycles(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
HalfCycles get_typer_frequency() final {
|
HalfCycles get_typer_frequency() final {
|
||||||
return Cycles(160000); // Type one character per frame.
|
return Cycles(80'000); // Perform one key transition per frame.
|
||||||
}
|
}
|
||||||
|
|
||||||
// See header; sets a key as either pressed or released.
|
// See header; sets a key as either pressed or released.
|
||||||
@ -1231,7 +1234,8 @@ template <bool has_fdc> class ConcreteMachine:
|
|||||||
KeyboardState key_state_;
|
KeyboardState key_state_;
|
||||||
AmstradCPC::KeyboardMapper keyboard_mapper_;
|
AmstradCPC::KeyboardMapper keyboard_mapper_;
|
||||||
|
|
||||||
uint8_t ram_[1024 * 1024];
|
bool has_run_ = false;
|
||||||
|
uint8_t ram_[128 * 1024];
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) {
|
|||||||
/* ACK */ X, /* BEL */ X,
|
/* ACK */ X, /* BEL */ X,
|
||||||
/* BS */ KEYS(KeyDelete), /* HT */ X,
|
/* BS */ KEYS(KeyDelete), /* HT */ X,
|
||||||
/* LF */ KEYS(KeyReturn), /* VT */ X,
|
/* LF */ KEYS(KeyReturn), /* VT */ X,
|
||||||
/* FF */ X, /* CR */ X,
|
/* FF */ X, /* CR */ KEYS(KeyReturn),
|
||||||
/* SO */ X, /* SI */ X,
|
/* SO */ X, /* SI */ X,
|
||||||
/* DLE */ X, /* DC1 */ X,
|
/* DLE */ X, /* DC1 */ X,
|
||||||
/* DC2 */ X, /* DC3 */ X,
|
/* DC2 */ X, /* DC3 */ X,
|
||||||
@ -142,7 +142,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) {
|
|||||||
/* x */ KEYS(KeyX), /* y */ KEYS(KeyY),
|
/* x */ KEYS(KeyX), /* y */ KEYS(KeyY),
|
||||||
/* z */ KEYS(KeyZ), /* { */ X,
|
/* z */ KEYS(KeyZ), /* { */ X,
|
||||||
/* | */ SHIFT(KeyAt), /* } */ X,
|
/* | */ SHIFT(KeyAt), /* } */ X,
|
||||||
/* ~ */ X
|
/* ~ */ X, /* DEL */ KEYS(KeyDelete),
|
||||||
};
|
};
|
||||||
#undef KEYS
|
#undef KEYS
|
||||||
#undef SHIFT
|
#undef SHIFT
|
||||||
@ -150,3 +150,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) {
|
|||||||
|
|
||||||
return table_lookup_sequence_for_character(key_sequences, sizeof(key_sequences), character);
|
return table_lookup_sequence_for_character(key_sequences, sizeof(key_sequences), character);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CharacterMapper::needs_pause_after_key(uint16_t key) {
|
||||||
|
return key != KeyControl && key != KeyShift;
|
||||||
|
}
|
||||||
|
@ -38,7 +38,10 @@ struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct CharacterMapper: public ::Utility::CharacterMapper {
|
struct CharacterMapper: public ::Utility::CharacterMapper {
|
||||||
uint16_t *sequence_for_character(char character);
|
uint16_t *sequence_for_character(char character) override;
|
||||||
|
|
||||||
|
bool needs_pause_after_reset_all_keys() override { return false; }
|
||||||
|
bool needs_pause_after_key(uint16_t key) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -858,6 +858,11 @@ template <Analyser::Static::AppleII::Target::Model model> class ConcreteMachine:
|
|||||||
string_serialiser_ = std::make_unique<Utility::StringSerialiser>(string, true);
|
string_serialiser_ = std::make_unique<Utility::StringSerialiser>(string, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool can_type(char c) final {
|
||||||
|
// Make an effort to type the entire printable ASCII range.
|
||||||
|
return c >= 32 && c < 127;
|
||||||
|
}
|
||||||
|
|
||||||
// MARK:: Configuration options.
|
// MARK:: Configuration options.
|
||||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
std::vector<std::unique_ptr<Configurable::Option>> get_options() final {
|
||||||
return Apple::II::get_options();
|
return Apple::II::get_options();
|
||||||
|
@ -44,7 +44,7 @@ namespace ST {
|
|||||||
|
|
||||||
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
std::vector<std::unique_ptr<Configurable::Option>> get_options() {
|
||||||
return Configurable::standard_options(
|
return Configurable::standard_options(
|
||||||
static_cast<Configurable::StandardOptions>(Configurable::DisplayRGB | Configurable::DisplayCompositeColour | Configurable::QuickLoadTape)
|
static_cast<Configurable::StandardOptions>(Configurable::DisplayRGB | Configurable::DisplayCompositeColour)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
BIND(Z, KeyZ); BIND(X, KeyX); BIND(C, KeyC); BIND(V, KeyV);
|
BIND(Z, KeyZ); BIND(X, KeyX); BIND(C, KeyC); BIND(V, KeyV);
|
||||||
BIND(B, KeyB); BIND(N, KeyN); BIND(M, KeyM);
|
BIND(B, KeyB); BIND(N, KeyN); BIND(M, KeyM);
|
||||||
|
|
||||||
BIND(BackTick, KeyLeft);
|
BIND(BackTick, KeyLeftArrow);
|
||||||
BIND(Hyphen, KeyPlus);
|
BIND(Hyphen, KeyPlus);
|
||||||
BIND(Equals, KeyDash);
|
BIND(Equals, KeyDash);
|
||||||
BIND(F11, KeyGBP);
|
BIND(F11, KeyGBP);
|
||||||
@ -35,8 +35,8 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
BIND(CloseSquareBracket, KeyAsterisk);
|
BIND(CloseSquareBracket, KeyAsterisk);
|
||||||
|
|
||||||
BIND(Backslash, KeyRestore);
|
BIND(Backslash, KeyRestore);
|
||||||
BIND(Hash, KeyUp);
|
BIND(Hash, KeyUpArrow);
|
||||||
BIND(F10, KeyUp);
|
BIND(F10, KeyUpArrow);
|
||||||
|
|
||||||
BIND(Semicolon, KeyColon);
|
BIND(Semicolon, KeyColon);
|
||||||
BIND(Quote, KeySemicolon);
|
BIND(Quote, KeySemicolon);
|
||||||
@ -66,6 +66,14 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
BIND(F3, KeyF3);
|
BIND(F3, KeyF3);
|
||||||
BIND(F5, KeyF5);
|
BIND(F5, KeyF5);
|
||||||
BIND(F7, KeyF7);
|
BIND(F7, KeyF7);
|
||||||
|
|
||||||
|
// Mappings to virtual keys.
|
||||||
|
BIND(Left, KeyLeft);
|
||||||
|
BIND(Up, KeyUp);
|
||||||
|
BIND(F2, KeyF2);
|
||||||
|
BIND(F4, KeyF4);
|
||||||
|
BIND(F6, KeyF6);
|
||||||
|
BIND(F8, KeyF8);
|
||||||
}
|
}
|
||||||
#undef BIND
|
#undef BIND
|
||||||
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||||
|
@ -20,7 +20,7 @@ enum Key: uint16_t {
|
|||||||
Key2 = key(7, 0x01), Key4 = key(7, 0x02), Key6 = key(7, 0x04), Key8 = key(7, 0x08),
|
Key2 = key(7, 0x01), Key4 = key(7, 0x02), Key6 = key(7, 0x04), Key8 = key(7, 0x08),
|
||||||
Key0 = key(7, 0x10), KeyDash = key(7, 0x20), KeyHome = key(7, 0x40), KeyF7 = key(7, 0x80),
|
Key0 = key(7, 0x10), KeyDash = key(7, 0x20), KeyHome = key(7, 0x40), KeyF7 = key(7, 0x80),
|
||||||
KeyQ = key(6, 0x01), KeyE = key(6, 0x02), KeyT = key(6, 0x04), KeyU = key(6, 0x08),
|
KeyQ = key(6, 0x01), KeyE = key(6, 0x02), KeyT = key(6, 0x04), KeyU = key(6, 0x08),
|
||||||
KeyO = key(6, 0x10), KeyAt = key(6, 0x20), KeyUp = key(6, 0x40), KeyF5 = key(6, 0x80),
|
KeyO = key(6, 0x10), KeyAt = key(6, 0x20), KeyUpArrow = key(6, 0x40), KeyF5 = key(6, 0x80),
|
||||||
KeyCBM = key(5, 0x01), KeyS = key(5, 0x02), KeyF = key(5, 0x04), KeyH = key(5, 0x08),
|
KeyCBM = key(5, 0x01), KeyS = key(5, 0x02), KeyF = key(5, 0x04), KeyH = key(5, 0x08),
|
||||||
KeyK = key(5, 0x10), KeyColon = key(5, 0x20), KeyEquals = key(5, 0x40), KeyF3 = key(5, 0x80),
|
KeyK = key(5, 0x10), KeyColon = key(5, 0x20), KeyEquals = key(5, 0x40), KeyF3 = key(5, 0x80),
|
||||||
KeySpace = key(4, 0x01), KeyZ = key(4, 0x02), KeyC = key(4, 0x04), KeyB = key(4, 0x08),
|
KeySpace = key(4, 0x01), KeyZ = key(4, 0x02), KeyC = key(4, 0x04), KeyB = key(4, 0x08),
|
||||||
@ -29,12 +29,21 @@ enum Key: uint16_t {
|
|||||||
KeyN = key(3, 0x10), KeyComma = key(3, 0x20), KeySlash = key(3, 0x40), KeyDown = key(3, 0x80),
|
KeyN = key(3, 0x10), KeyComma = key(3, 0x20), KeySlash = key(3, 0x40), KeyDown = key(3, 0x80),
|
||||||
KeyControl = key(2, 0x01), KeyA = key(2, 0x02), KeyD = key(2, 0x04), KeyG = key(2, 0x08),
|
KeyControl = key(2, 0x01), KeyA = key(2, 0x02), KeyD = key(2, 0x04), KeyG = key(2, 0x08),
|
||||||
KeyJ = key(2, 0x10), KeyL = key(2, 0x20), KeySemicolon = key(2, 0x40), KeyRight = key(2, 0x80),
|
KeyJ = key(2, 0x10), KeyL = key(2, 0x20), KeySemicolon = key(2, 0x40), KeyRight = key(2, 0x80),
|
||||||
KeyLeft = key(1, 0x01), KeyW = key(1, 0x02), KeyR = key(1, 0x04), KeyY = key(1, 0x08),
|
KeyLeftArrow= key(1, 0x01), KeyW = key(1, 0x02), KeyR = key(1, 0x04), KeyY = key(1, 0x08),
|
||||||
KeyI = key(1, 0x10), KeyP = key(1, 0x20), KeyAsterisk = key(1, 0x40), KeyReturn = key(1, 0x80),
|
KeyI = key(1, 0x10), KeyP = key(1, 0x20), KeyAsterisk = key(1, 0x40), KeyReturn = key(1, 0x80),
|
||||||
Key1 = key(0, 0x01), Key3 = key(0, 0x02), Key5 = key(0, 0x04), Key7 = key(0, 0x08),
|
Key1 = key(0, 0x01), Key3 = key(0, 0x02), Key5 = key(0, 0x04), Key7 = key(0, 0x08),
|
||||||
Key9 = key(0, 0x10), KeyPlus = key(0, 0x20), KeyGBP = key(0, 0x40), KeyDelete = key(0, 0x80),
|
Key9 = key(0, 0x10), KeyPlus = key(0, 0x20), KeyGBP = key(0, 0x40), KeyDelete = key(0, 0x80),
|
||||||
|
|
||||||
KeyRestore = 0xfffd
|
// Virtual keys.
|
||||||
|
KeyUp = 0xfff0,
|
||||||
|
KeyLeft = 0xfff1,
|
||||||
|
KeyF2 = 0xfff2,
|
||||||
|
KeyF4 = 0xfff3,
|
||||||
|
KeyF6 = 0xfff4,
|
||||||
|
KeyF8 = 0xfff5,
|
||||||
|
|
||||||
|
// Physical keys not within the usual matrix.
|
||||||
|
KeyRestore = 0xfffd,
|
||||||
#undef key
|
#undef key
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -287,7 +287,7 @@ class ConcreteMachine:
|
|||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
public CPU::MOS6502::BusHandler,
|
public CPU::MOS6502::BusHandler,
|
||||||
public MOS::MOS6522::IRQDelegatePortHandler::Delegate,
|
public MOS::MOS6522::IRQDelegatePortHandler::Delegate,
|
||||||
public Utility::TypeRecipient,
|
public Utility::TypeRecipient<CharacterMapper>,
|
||||||
public Storage::Tape::BinaryTapePlayer::Delegate,
|
public Storage::Tape::BinaryTapePlayer::Delegate,
|
||||||
public Machine,
|
public Machine,
|
||||||
public ClockingHint::Observer,
|
public ClockingHint::Observer,
|
||||||
@ -479,10 +479,28 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void set_key_state(uint16_t key, bool is_pressed) final {
|
void set_key_state(uint16_t key, bool is_pressed) final {
|
||||||
if(key != KeyRestore)
|
if(key < 0xfff0) {
|
||||||
keyboard_via_port_handler_->set_key_state(key, is_pressed);
|
keyboard_via_port_handler_->set_key_state(key, is_pressed);
|
||||||
else
|
} else {
|
||||||
user_port_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !is_pressed);
|
switch(key) {
|
||||||
|
case KeyRestore:
|
||||||
|
user_port_via_.set_control_line_input(MOS::MOS6522::Port::A, MOS::MOS6522::Line::One, !is_pressed);
|
||||||
|
break;
|
||||||
|
#define ShiftedMap(source, target) \
|
||||||
|
case source: \
|
||||||
|
keyboard_via_port_handler_->set_key_state(KeyLShift, is_pressed); \
|
||||||
|
keyboard_via_port_handler_->set_key_state(target, is_pressed); \
|
||||||
|
break;
|
||||||
|
|
||||||
|
ShiftedMap(KeyUp, KeyDown);
|
||||||
|
ShiftedMap(KeyLeft, KeyRight);
|
||||||
|
ShiftedMap(KeyF2, KeyF1);
|
||||||
|
ShiftedMap(KeyF4, KeyF3);
|
||||||
|
ShiftedMap(KeyF6, KeyF5);
|
||||||
|
ShiftedMap(KeyF8, KeyF7);
|
||||||
|
#undef ShiftedMap
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_all_keys() final {
|
void clear_all_keys() final {
|
||||||
@ -645,7 +663,11 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void type_string(const std::string &string) final {
|
void type_string(const std::string &string) final {
|
||||||
Utility::TypeRecipient::add_typer(string, std::make_unique<CharacterMapper>());
|
Utility::TypeRecipient<CharacterMapper>::add_typer(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_type(char c) final {
|
||||||
|
return Utility::TypeRecipient<CharacterMapper>::can_type(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape) final {
|
void tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape) final {
|
||||||
|
@ -46,7 +46,7 @@ class ConcreteMachine:
|
|||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
public CPU::MOS6502::BusHandler,
|
public CPU::MOS6502::BusHandler,
|
||||||
public Tape::Delegate,
|
public Tape::Delegate,
|
||||||
public Utility::TypeRecipient,
|
public Utility::TypeRecipient<CharacterMapper>,
|
||||||
public Activity::Source {
|
public Activity::Source {
|
||||||
public:
|
public:
|
||||||
ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
ConcreteMachine(const Analyser::Static::Acorn::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||||
@ -346,10 +346,10 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cycles_since_display_update_ += Cycles(static_cast<int>(cycles));
|
cycles_since_display_update_ += Cycles(int(cycles));
|
||||||
cycles_since_audio_update_ += Cycles(static_cast<int>(cycles));
|
cycles_since_audio_update_ += Cycles(int(cycles));
|
||||||
if(cycles_since_audio_update_ > Cycles(16384)) update_audio();
|
if(cycles_since_audio_update_ > Cycles(16384)) update_audio();
|
||||||
tape_.run_for(Cycles(static_cast<int>(cycles)));
|
tape_.run_for(Cycles(int(cycles)));
|
||||||
|
|
||||||
cycles_until_display_interrupt_ -= cycles;
|
cycles_until_display_interrupt_ -= cycles;
|
||||||
if(cycles_until_display_interrupt_ < 0) {
|
if(cycles_until_display_interrupt_ < 0) {
|
||||||
@ -358,8 +358,8 @@ class ConcreteMachine:
|
|||||||
queue_next_display_interrupt();
|
queue_next_display_interrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(typer_) typer_->run_for(Cycles(static_cast<int>(cycles)));
|
if(typer_) typer_->run_for(Cycles(int(cycles)));
|
||||||
if(plus3_) plus3_->run_for(Cycles(4*static_cast<int>(cycles)));
|
if(plus3_) plus3_->run_for(Cycles(4*int(cycles)));
|
||||||
if(shift_restart_counter_) {
|
if(shift_restart_counter_) {
|
||||||
shift_restart_counter_ -= cycles;
|
shift_restart_counter_ -= cycles;
|
||||||
if(shift_restart_counter_ <= 0) {
|
if(shift_restart_counter_ <= 0) {
|
||||||
@ -405,15 +405,19 @@ class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
HalfCycles get_typer_delay() final {
|
HalfCycles get_typer_delay() final {
|
||||||
return m6502_.get_is_resetting() ? Cycles(625*25*128) : Cycles(0); // wait one second if resetting
|
return m6502_.get_is_resetting() ? Cycles(750'000) : Cycles(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
HalfCycles get_typer_frequency() final {
|
HalfCycles get_typer_frequency() final {
|
||||||
return Cycles(625*128*2); // accept a new character every two frames
|
return Cycles(60'000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void type_string(const std::string &string) final {
|
void type_string(const std::string &string) final {
|
||||||
Utility::TypeRecipient::add_typer(string, std::make_unique<CharacterMapper>());
|
Utility::TypeRecipient<CharacterMapper>::add_typer(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_type(char c) final {
|
||||||
|
return Utility::TypeRecipient<CharacterMapper>::can_type(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyboardMapper *get_keyboard_mapper() final {
|
KeyboardMapper *get_keyboard_mapper() final {
|
||||||
|
@ -66,7 +66,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) {
|
|||||||
/* ACK */ X, /* BEL */ X,
|
/* ACK */ X, /* BEL */ X,
|
||||||
/* BS */ KEYS(KeyDelete), /* HT */ X,
|
/* BS */ KEYS(KeyDelete), /* HT */ X,
|
||||||
/* LF */ KEYS(KeyReturn), /* VT */ X,
|
/* LF */ KEYS(KeyReturn), /* VT */ X,
|
||||||
/* FF */ X, /* CR */ X,
|
/* FF */ X, /* CR */ KEYS(KeyReturn),
|
||||||
/* SO */ X, /* SI */ X,
|
/* SO */ X, /* SI */ X,
|
||||||
/* DLE */ X, /* DC1 */ X,
|
/* DLE */ X, /* DC1 */ X,
|
||||||
/* DC2 */ X, /* DC3 */ X,
|
/* DC2 */ X, /* DC3 */ X,
|
||||||
@ -123,7 +123,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) {
|
|||||||
/* x */ SHIFT(KeyX), /* y */ SHIFT(KeyY),
|
/* x */ SHIFT(KeyX), /* y */ SHIFT(KeyY),
|
||||||
/* z */ SHIFT(KeyZ), /* { */ CTRL(KeyUp),
|
/* z */ SHIFT(KeyZ), /* { */ CTRL(KeyUp),
|
||||||
/* | */ SHIFT(KeyRight), /* } */ CTRL(KeyDown),
|
/* | */ SHIFT(KeyRight), /* } */ CTRL(KeyDown),
|
||||||
/* ~ */ CTRL(KeyLeft)
|
/* ~ */ CTRL(KeyLeft), /* DEL */ KEYS(KeyDelete),
|
||||||
};
|
};
|
||||||
#undef KEYS
|
#undef KEYS
|
||||||
#undef SHIFT
|
#undef SHIFT
|
||||||
@ -131,3 +131,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) {
|
|||||||
|
|
||||||
return table_lookup_sequence_for_character(key_sequences, sizeof(key_sequences), character);
|
return table_lookup_sequence_for_character(key_sequences, sizeof(key_sequences), character);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CharacterMapper::needs_pause_after_key(uint16_t key) {
|
||||||
|
return key != KeyControl && key != KeyShift && key != KeyFunc;
|
||||||
|
}
|
||||||
|
@ -38,7 +38,10 @@ struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct CharacterMapper: public ::Utility::CharacterMapper {
|
struct CharacterMapper: public ::Utility::CharacterMapper {
|
||||||
uint16_t *sequence_for_character(char character);
|
uint16_t *sequence_for_character(char character) override;
|
||||||
|
|
||||||
|
bool needs_pause_after_reset_all_keys() override { return false; }
|
||||||
|
bool needs_pause_after_key(uint16_t key) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -45,6 +45,11 @@ class Machine: public KeyActions {
|
|||||||
*/
|
*/
|
||||||
virtual void type_string(const std::string &);
|
virtual void type_string(const std::string &);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns @c true if this machine can type the character @c c as part of a @c type_string; @c false otherwise.
|
||||||
|
*/
|
||||||
|
virtual bool can_type(char c) { return false; }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Provides a destination for keyboard input.
|
Provides a destination for keyboard input.
|
||||||
*/
|
*/
|
||||||
|
@ -369,6 +369,11 @@ class ConcreteMachine:
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool can_type(char c) final {
|
||||||
|
// Make an effort to type the entire printable ASCII range.
|
||||||
|
return c >= 32 && c < 127;
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: MSX::MemoryMap
|
// MARK: MSX::MemoryMap
|
||||||
void map(int slot, std::size_t source_address, uint16_t destination_address, std::size_t length) final {
|
void map(int slot, std::size_t source_address, uint16_t destination_address, std::size_t length) final {
|
||||||
assert(!(destination_address & 8191));
|
assert(!(destination_address & 8191));
|
||||||
|
@ -224,7 +224,6 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
public CPU::MOS6502::BusHandler,
|
public CPU::MOS6502::BusHandler,
|
||||||
public MOS::MOS6522::IRQDelegatePortHandler::Delegate,
|
public MOS::MOS6522::IRQDelegatePortHandler::Delegate,
|
||||||
public Utility::TypeRecipient,
|
|
||||||
public Storage::Tape::BinaryTapePlayer::Delegate,
|
public Storage::Tape::BinaryTapePlayer::Delegate,
|
||||||
public DiskController::Delegate,
|
public DiskController::Delegate,
|
||||||
public ClockingHint::Observer,
|
public ClockingHint::Observer,
|
||||||
@ -590,6 +589,11 @@ template <Analyser::Static::Oric::Target::DiskInterface disk_interface> class Co
|
|||||||
string_serialiser_ = std::make_unique<Utility::StringSerialiser>(string, true);
|
string_serialiser_ = std::make_unique<Utility::StringSerialiser>(string, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool can_type(char c) final {
|
||||||
|
// Make an effort to type the entire printable ASCII range.
|
||||||
|
return c >= 32 && c < 127;
|
||||||
|
}
|
||||||
|
|
||||||
// DiskController::Delegate
|
// DiskController::Delegate
|
||||||
void disk_controller_did_change_paged_item(DiskController *controller) final {
|
void disk_controller_did_change_paged_item(DiskController *controller) final {
|
||||||
switch(controller->get_paged_item()) {
|
switch(controller->get_paged_item()) {
|
||||||
|
@ -8,63 +8,125 @@
|
|||||||
|
|
||||||
#include "Typer.hpp"
|
#include "Typer.hpp"
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
using namespace Utility;
|
using namespace Utility;
|
||||||
|
|
||||||
Typer::Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, std::unique_ptr<CharacterMapper> character_mapper, Delegate *delegate) :
|
Typer::Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, CharacterMapper &character_mapper, Delegate *delegate) :
|
||||||
frequency_(frequency),
|
frequency_(frequency),
|
||||||
counter_(-delay),
|
counter_(-delay),
|
||||||
delegate_(delegate),
|
delegate_(delegate),
|
||||||
character_mapper_(std::move(character_mapper)) {
|
character_mapper_(character_mapper) {
|
||||||
std::ostringstream string_stream;
|
// Retain only those characters that actually map to something.
|
||||||
string_stream << Typer::BeginString << string << Typer::EndString;
|
if(sequence_for_character(Typer::BeginString)) {
|
||||||
string_ = string_stream.str();
|
string_ += Typer::BeginString;
|
||||||
|
}
|
||||||
|
if(sequence_for_character(Typer::EndString)) {
|
||||||
|
string_ += Typer::EndString;
|
||||||
|
}
|
||||||
|
|
||||||
|
append(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Typer::run_for(const HalfCycles duration) {
|
void Typer::run_for(const HalfCycles duration) {
|
||||||
if(string_pointer_ < string_.size()) {
|
if(string_pointer_ >= string_.size()) {
|
||||||
if(counter_ < 0 && counter_ + duration >= 0) {
|
return;
|
||||||
if(!type_next_character()) {
|
}
|
||||||
delegate_->typer_reset(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
counter_ += duration;
|
if(counter_ < 0 && counter_ + duration >= 0) {
|
||||||
while(string_pointer_ < string_.size() && counter_ > frequency_) {
|
if(!type_next_character()) {
|
||||||
counter_ -= frequency_;
|
delegate_->typer_reset(this);
|
||||||
if(!type_next_character()) {
|
}
|
||||||
delegate_->typer_reset(this);
|
}
|
||||||
}
|
|
||||||
|
counter_ += duration;
|
||||||
|
while(string_pointer_ < string_.size() && counter_ > frequency_) {
|
||||||
|
counter_ -= frequency_;
|
||||||
|
if(!type_next_character()) {
|
||||||
|
delegate_->typer_reset(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Typer::try_type_next_character() {
|
void Typer::append(const std::string &string) {
|
||||||
uint16_t *sequence = character_mapper_->sequence_for_character(string_[string_pointer_]);
|
// Remove any characters that are already completely done;
|
||||||
|
// otherwise things may accumulate here indefinitely.
|
||||||
|
// Note that sequence_for_character may seek to look one backwards,
|
||||||
|
// so keep 'the character before' if there was one.
|
||||||
|
if(string_pointer_ > 1) {
|
||||||
|
string_.erase(string_.begin(), string_.begin() + ssize_t(string_pointer_) - 1);
|
||||||
|
string_pointer_ = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the final character in the string is not Typer::EndString
|
||||||
|
// then this machine doesn't need Begin and End, so don't worry about it.
|
||||||
|
ssize_t insertion_position = ssize_t(string_.size());
|
||||||
|
if(string_.back() == Typer::EndString) --insertion_position;
|
||||||
|
|
||||||
|
string_.reserve(string_.size() + string.size());
|
||||||
|
for(const char c : string) {
|
||||||
|
if(sequence_for_character(c)) {
|
||||||
|
string_.insert(string_.begin() + insertion_position, c);
|
||||||
|
++insertion_position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t *Typer::sequence_for_character(char c) const {
|
||||||
|
const uint16_t *const sequence = character_mapper_.sequence_for_character(c);
|
||||||
if(!sequence || sequence[0] == KeyboardMachine::MappedMachine::KeyNotMapped) {
|
if(!sequence || sequence[0] == KeyboardMachine::MappedMachine::KeyNotMapped) {
|
||||||
return false;
|
return nullptr;
|
||||||
|
}
|
||||||
|
return sequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t Typer::try_type_next_character() {
|
||||||
|
const uint16_t *const sequence = sequence_for_character(string_[string_pointer_]);
|
||||||
|
|
||||||
|
if(!sequence) {
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!phase_) delegate_->clear_all_keys();
|
// Advance phase.
|
||||||
else {
|
++phase_;
|
||||||
delegate_->set_key_state(sequence[phase_ - 1], true);
|
|
||||||
return sequence[phase_] != KeyboardMachine::MappedMachine::KeyEndSequence;
|
// If this is the start of the output sequence, start with a reset all keys.
|
||||||
|
// Then pause if either: (i) the machine requires it; or (ii) this is the same
|
||||||
|
// character that was just typed, in which case the gap in presses will need to
|
||||||
|
// be clear.
|
||||||
|
if(phase_ == 1) {
|
||||||
|
delegate_->clear_all_keys();
|
||||||
|
if(character_mapper_.needs_pause_after_reset_all_keys() ||
|
||||||
|
(string_pointer_ > 0 && string_[string_pointer_ - 1] == string_[string_pointer_])) {
|
||||||
|
return 0xffff; // Arbitrarily. Anything non-zero will do.
|
||||||
|
}
|
||||||
|
++phase_;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
// If the sequence is over, stop.
|
||||||
|
if(sequence[phase_ - 2] == KeyboardMachine::MappedMachine::KeyEndSequence) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, type the key.
|
||||||
|
delegate_->set_key_state(sequence[phase_ - 2], true);
|
||||||
|
|
||||||
|
return sequence[phase_ - 2];
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Typer::type_next_character() {
|
bool Typer::type_next_character() {
|
||||||
if(string_pointer_ == string_.size()) return false;
|
if(string_pointer_ == string_.size()) return false;
|
||||||
|
|
||||||
if(!try_type_next_character()) {
|
while(true) {
|
||||||
phase_ = 0;
|
const uint16_t key_pressed = try_type_next_character();
|
||||||
string_pointer_++;
|
|
||||||
if(string_pointer_ == string_.size()) return false;
|
if(!key_pressed) {
|
||||||
} else {
|
phase_ = 0;
|
||||||
phase_++;
|
++string_pointer_;
|
||||||
|
if(string_pointer_ == string_.size()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(character_mapper_.needs_pause_after_key(key_pressed)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -74,7 +136,8 @@ 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::MappedMachine::KeyNotMapped) return nullptr;
|
if(sequences[ucharacter][0] == KeyboardMachine::MappedMachine::KeyNotMapped) return nullptr;
|
||||||
return sequences[ucharacter];
|
return sequences[ucharacter];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +28,18 @@ class CharacterMapper {
|
|||||||
/// @returns The EndSequence-terminated sequence of keys that would cause @c character to be typed.
|
/// @returns The EndSequence-terminated sequence of keys that would cause @c character to be typed.
|
||||||
virtual uint16_t *sequence_for_character(char character) = 0;
|
virtual uint16_t *sequence_for_character(char character) = 0;
|
||||||
|
|
||||||
|
/// The typer will automatically reset all keys in between each sequence that it types.
|
||||||
|
/// By default it will pause for one key's duration when doing so. Character mappers
|
||||||
|
/// can eliminate that pause by overriding this method.
|
||||||
|
/// @returns @c true if the typer should pause after performing a reset; @c false otherwise.
|
||||||
|
virtual bool needs_pause_after_reset_all_keys() { return true; }
|
||||||
|
|
||||||
|
/// The typer will pause between every entry in a keyboard sequence. On some machines
|
||||||
|
/// that may not be necessary — it'll often depends on whether the machine needs time to
|
||||||
|
/// observe a modifier like shift before it sees the actual keypress.
|
||||||
|
/// @returns @c true if the typer should pause after forwarding @c key; @c false otherwise.
|
||||||
|
virtual bool needs_pause_after_key(uint16_t key) { return true; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typedef uint16_t KeySequence[16];
|
typedef uint16_t KeySequence[16];
|
||||||
|
|
||||||
@ -51,14 +63,21 @@ class Typer {
|
|||||||
public:
|
public:
|
||||||
class Delegate: public KeyboardMachine::KeyActions {
|
class Delegate: public KeyboardMachine::KeyActions {
|
||||||
public:
|
public:
|
||||||
|
/// Informs the delegate that this typer has reached the end of its content.
|
||||||
virtual void typer_reset(Typer *typer) = 0;
|
virtual void typer_reset(Typer *typer) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, std::unique_ptr<CharacterMapper> character_mapper, Delegate *delegate);
|
Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, CharacterMapper &character_mapper, Delegate *delegate);
|
||||||
|
|
||||||
|
/// Advances for @c duration.
|
||||||
void run_for(const HalfCycles duration);
|
void run_for(const HalfCycles duration);
|
||||||
|
|
||||||
|
/// Types the next character now, if there is one.
|
||||||
|
/// @returns @c true if there was anything left to type; @c false otherwise.
|
||||||
bool type_next_character();
|
bool type_next_character();
|
||||||
bool is_completed();
|
|
||||||
|
/// Adds the contents of @c str to the end of the current string.
|
||||||
|
void append(const std::string &str);
|
||||||
|
|
||||||
const char BeginString = 0x02; // i.e. ASCII start of text
|
const char BeginString = 0x02; // i.e. ASCII start of text
|
||||||
const char EndString = 0x03; // i.e. ASCII end of text
|
const char EndString = 0x03; // i.e. ASCII end of text
|
||||||
@ -72,20 +91,36 @@ class Typer {
|
|||||||
int phase_ = 0;
|
int phase_ = 0;
|
||||||
|
|
||||||
Delegate *delegate_;
|
Delegate *delegate_;
|
||||||
std::unique_ptr<CharacterMapper> character_mapper_;
|
CharacterMapper &character_mapper_;
|
||||||
|
|
||||||
bool try_type_next_character();
|
uint16_t try_type_next_character();
|
||||||
|
const uint16_t *sequence_for_character(char) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Provides a default base class for type recipients: classes that want to attach a single typer at a time and
|
Provides a default base class for type recipients: classes that want to attach a single typer at a time and
|
||||||
which may or may not want to nominate an initial delay and typing frequency.
|
which may or may not want to nominate an initial delay and typing frequency.
|
||||||
*/
|
*/
|
||||||
|
template <typename CMApper>
|
||||||
class TypeRecipient: public Typer::Delegate {
|
class TypeRecipient: public Typer::Delegate {
|
||||||
protected:
|
protected:
|
||||||
|
template <typename... Args> TypeRecipient(Args&&... args) : character_mapper(std::forward<Args>(args)...) {}
|
||||||
|
|
||||||
/// Attaches a typer to this class that will type @c string using @c character_mapper as a source.
|
/// Attaches a typer to this class that will type @c string using @c character_mapper as a source.
|
||||||
void add_typer(const std::string &string, std::unique_ptr<CharacterMapper> character_mapper) {
|
void add_typer(const std::string &string) {
|
||||||
typer_ = std::make_unique<Typer>(string, get_typer_delay(), get_typer_frequency(), std::move(character_mapper), this);
|
if(!typer_) {
|
||||||
|
typer_ = std::make_unique<Typer>(string, get_typer_delay(), get_typer_frequency(), character_mapper, this);
|
||||||
|
} else {
|
||||||
|
typer_->append(string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns @c true if the character mapper provides a mapping for @c c; @c false otherwise.
|
||||||
|
*/
|
||||||
|
bool can_type(char c) {
|
||||||
|
const auto sequence = character_mapper.sequence_for_character(c);
|
||||||
|
return sequence && sequence[0] != KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -108,6 +143,7 @@ class TypeRecipient: public Typer::Delegate {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Typer> previous_typer_;
|
std::unique_ptr<Typer> previous_typer_;
|
||||||
|
CMApper character_mapper;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,10 @@ uint16_t KeyboardMapper::mapped_key_for_key(Inputs::Keyboard::Key key) {
|
|||||||
BIND(FullStop, KeyDot);
|
BIND(FullStop, KeyDot);
|
||||||
BIND(Enter, KeyEnter);
|
BIND(Enter, KeyEnter);
|
||||||
BIND(Space, KeySpace);
|
BIND(Space, KeySpace);
|
||||||
|
|
||||||
|
// Virtual keys follow.
|
||||||
|
BIND(Backspace, KeyDelete);
|
||||||
|
BIND(Escape, KeyBreak);
|
||||||
}
|
}
|
||||||
#undef BIND
|
#undef BIND
|
||||||
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
return KeyboardMachine::MappedMachine::KeyNotMapped;
|
||||||
@ -46,7 +50,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) {
|
|||||||
/* ACK */ X, /* BEL */ X,
|
/* ACK */ X, /* BEL */ X,
|
||||||
/* BS */ SHIFT(Key0), /* HT */ X,
|
/* BS */ SHIFT(Key0), /* HT */ X,
|
||||||
/* LF */ KEYS(KeyEnter), /* VT */ X,
|
/* LF */ KEYS(KeyEnter), /* VT */ X,
|
||||||
/* FF */ X, /* CR */ X,
|
/* FF */ X, /* CR */ KEYS(KeyEnter),
|
||||||
/* SO */ X, /* SI */ X,
|
/* SO */ X, /* SI */ X,
|
||||||
/* DLE */ X, /* DC1 */ X,
|
/* DLE */ X, /* DC1 */ X,
|
||||||
/* DC2 */ X, /* DC3 */ X,
|
/* DC2 */ X, /* DC3 */ X,
|
||||||
@ -112,7 +116,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) {
|
|||||||
/* ACK */ X, /* BEL */ X,
|
/* ACK */ X, /* BEL */ X,
|
||||||
/* BS */ SHIFT(Key0), /* HT */ X,
|
/* BS */ SHIFT(Key0), /* HT */ X,
|
||||||
/* LF */ KEYS(KeyEnter), /* VT */ X,
|
/* LF */ KEYS(KeyEnter), /* VT */ X,
|
||||||
/* FF */ X, /* CR */ X,
|
/* FF */ X, /* CR */ KEYS(KeyEnter),
|
||||||
/* SO */ X, /* SI */ X,
|
/* SO */ X, /* SI */ X,
|
||||||
/* DLE */ X, /* DC1 */ X,
|
/* DLE */ X, /* DC1 */ X,
|
||||||
/* DC2 */ X, /* DC3 */ X,
|
/* DC2 */ X, /* DC3 */ X,
|
||||||
@ -179,3 +183,7 @@ uint16_t *CharacterMapper::sequence_for_character(char character) {
|
|||||||
else
|
else
|
||||||
return table_lookup_sequence_for_character(zx80_key_sequences, sizeof(zx80_key_sequences), character);
|
return table_lookup_sequence_for_character(zx80_key_sequences, sizeof(zx80_key_sequences), character);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CharacterMapper::needs_pause_after_key(uint16_t key) {
|
||||||
|
return key != KeyShift;
|
||||||
|
}
|
||||||
|
@ -23,6 +23,10 @@ enum Key: uint16_t {
|
|||||||
KeyP = 0x0500 | 0x01, KeyO = 0x0500 | 0x02, KeyI = 0x0500 | 0x04, KeyU = 0x0500 | 0x08, KeyY = 0x0500 | 0x10,
|
KeyP = 0x0500 | 0x01, KeyO = 0x0500 | 0x02, KeyI = 0x0500 | 0x04, KeyU = 0x0500 | 0x08, KeyY = 0x0500 | 0x10,
|
||||||
KeyEnter = 0x0600 | 0x01, KeyL = 0x0600 | 0x02, KeyK = 0x0600 | 0x04, KeyJ = 0x0600 | 0x08, KeyH = 0x0600 | 0x10,
|
KeyEnter = 0x0600 | 0x01, KeyL = 0x0600 | 0x02, KeyK = 0x0600 | 0x04, KeyJ = 0x0600 | 0x08, KeyH = 0x0600 | 0x10,
|
||||||
KeySpace = 0x0700 | 0x01, KeyDot = 0x0700 | 0x02, KeyM = 0x0700 | 0x04, KeyN = 0x0700 | 0x08, KeyB = 0x0700 | 0x10,
|
KeySpace = 0x0700 | 0x01, KeyDot = 0x0700 | 0x02, KeyM = 0x0700 | 0x04, KeyN = 0x0700 | 0x08, KeyB = 0x0700 | 0x10,
|
||||||
|
|
||||||
|
// Add some virtual keys; these do not exist on a real ZX80 or ZX81. They're just a convenience.
|
||||||
|
KeyDelete = 0x0801,
|
||||||
|
KeyBreak = 0x0802,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
||||||
@ -32,7 +36,9 @@ struct KeyboardMapper: public KeyboardMachine::MappedMachine::KeyboardMapper {
|
|||||||
class CharacterMapper: public ::Utility::CharacterMapper {
|
class CharacterMapper: public ::Utility::CharacterMapper {
|
||||||
public:
|
public:
|
||||||
CharacterMapper(bool is_zx81);
|
CharacterMapper(bool is_zx81);
|
||||||
uint16_t *sequence_for_character(char character);
|
uint16_t *sequence_for_character(char character) override;
|
||||||
|
|
||||||
|
bool needs_pause_after_key(uint16_t key) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_zx81_;
|
bool is_zx81_;
|
||||||
|
@ -62,11 +62,12 @@ template<bool is_zx81> class ConcreteMachine:
|
|||||||
public MediaTarget::Machine,
|
public MediaTarget::Machine,
|
||||||
public KeyboardMachine::MappedMachine,
|
public KeyboardMachine::MappedMachine,
|
||||||
public Configurable::Device,
|
public Configurable::Device,
|
||||||
public Utility::TypeRecipient,
|
public Utility::TypeRecipient<CharacterMapper>,
|
||||||
public CPU::Z80::BusHandler,
|
public CPU::Z80::BusHandler,
|
||||||
public Machine {
|
public Machine {
|
||||||
public:
|
public:
|
||||||
ConcreteMachine(const Analyser::Static::ZX8081::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
ConcreteMachine(const Analyser::Static::ZX8081::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
|
||||||
|
Utility::TypeRecipient<CharacterMapper>(is_zx81),
|
||||||
z80_(*this),
|
z80_(*this),
|
||||||
tape_player_(ZX8081ClockRate),
|
tape_player_(ZX8081ClockRate),
|
||||||
ay_(GI::AY38910::Personality::AY38910, audio_queue_),
|
ay_(GI::AY38910::Personality::AY38910, audio_queue_),
|
||||||
@ -340,15 +341,38 @@ template<bool is_zx81> class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void type_string(const std::string &string) final {
|
void type_string(const std::string &string) final {
|
||||||
Utility::TypeRecipient::add_typer(string, std::make_unique<CharacterMapper>(is_zx81));
|
Utility::TypeRecipient<CharacterMapper>::add_typer(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool can_type(char c) final {
|
||||||
|
return Utility::TypeRecipient<CharacterMapper>::can_type(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Keyboard
|
// MARK: - Keyboard
|
||||||
void set_key_state(uint16_t key, bool is_pressed) final {
|
void set_key_state(uint16_t key, bool is_pressed) final {
|
||||||
if(is_pressed)
|
const auto line = key >> 8;
|
||||||
key_states_[key >> 8] &= static_cast<uint8_t>(~key);
|
|
||||||
else
|
// Check for special cases.
|
||||||
key_states_[key >> 8] |= static_cast<uint8_t>(key);
|
if(line == 8) {
|
||||||
|
switch(key) {
|
||||||
|
case KeyDelete:
|
||||||
|
// Map delete to shift+0.
|
||||||
|
set_key_state(KeyShift, is_pressed);
|
||||||
|
set_key_state(Key0, is_pressed);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KeyBreak:
|
||||||
|
// Map break to shift+space.
|
||||||
|
set_key_state(KeyShift, is_pressed);
|
||||||
|
set_key_state(KeySpace, is_pressed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(is_pressed)
|
||||||
|
key_states_[line] &= uint8_t(~key);
|
||||||
|
else
|
||||||
|
key_states_[line] |= uint8_t(key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear_all_keys() final {
|
void clear_all_keys() final {
|
||||||
@ -373,8 +397,13 @@ template<bool is_zx81> class ConcreteMachine:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Typer timing
|
// MARK: - Typer timing
|
||||||
HalfCycles get_typer_delay() final { return Cycles(7000000); }
|
HalfCycles get_typer_delay() final {
|
||||||
HalfCycles get_typer_frequency() final { return Cycles(390000); }
|
return z80_.get_is_resetting() ? Cycles(7'000'000) : Cycles(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
HalfCycles get_typer_frequency() final {
|
||||||
|
return Cycles(146'250);
|
||||||
|
}
|
||||||
|
|
||||||
KeyboardMapper *get_keyboard_mapper() final {
|
KeyboardMapper *get_keyboard_mapper() final {
|
||||||
return &keyboard_mapper_;
|
return &keyboard_mapper_;
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
disableMainThreadChecker = "YES"
|
disableMainThreadChecker = "YES"
|
||||||
@ -58,6 +58,14 @@
|
|||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Master System/R-Type (NTSC).sms""
|
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Master System/R-Type (NTSC).sms""
|
||||||
|
isEnabled = "NO">
|
||||||
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "--logical-keyboard"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = ""/Users/thomasharte/Library/Mobile Documents/com~apple~CloudDocs/Desktop/Soft/Amstrad CPC/Amstrad CPC [TOSEC]/Amstrad CPC - Applications - [DSK] (TOSEC-v2011-08-31_CM)/Tasword (1984)(Tasman Software).dsk""
|
||||||
isEnabled = "YES">
|
isEnabled = "YES">
|
||||||
</CommandLineArgument>
|
</CommandLineArgument>
|
||||||
<CommandLineArgument
|
<CommandLineArgument
|
||||||
|
@ -67,7 +67,7 @@
|
|||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Release"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
enableASanStackUseAfterReturn = "YES"
|
enableASanStackUseAfterReturn = "YES"
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14113" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15705" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14113"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||||
@ -121,18 +121,6 @@
|
|||||||
<action selector="insertMedia:" target="-1" id="9Hs-9J-dlY"/>
|
<action selector="insertMedia:" target="-1" id="9Hs-9J-dlY"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="rXU-KX-GkZ"/>
|
|
||||||
<menuItem title="Page Setup…" enabled="NO" keyEquivalent="P" id="qIS-W8-SiK">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Print…" enabled="NO" keyEquivalent="p" id="aTl-1u-JFS">
|
|
||||||
<connections>
|
|
||||||
<action selector="printDocument:" target="-1" id="qaZ-4w-aoO"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
</items>
|
</items>
|
||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
@ -178,7 +166,12 @@
|
|||||||
<items>
|
<items>
|
||||||
<menuItem title="Use Keyboard as Keyboard" state="on" tag="100" keyEquivalent="k" id="TfX-0B-j4U">
|
<menuItem title="Use Keyboard as Keyboard" state="on" tag="100" keyEquivalent="k" id="TfX-0B-j4U">
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="useKeyboardAsKeyboard:" target="-1" id="6fl-fS-Oe9"/>
|
<action selector="useKeyboardAsPhysicalKeyboard:" target="-1" id="3NX-jl-F4z"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
|
<menuItem title="Map Typed Characters by Symbol" tag="102" keyEquivalent="l" id="Nzx-y4-1WD">
|
||||||
|
<connections>
|
||||||
|
<action selector="useKeyboardAsLogicalKeyboard:" target="-1" id="mo3-yB-wVB"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Use Keyboard as Joystick" tag="101" enabled="NO" keyEquivalent="j" id="5mn-ch-Xv6">
|
<menuItem title="Use Keyboard as Joystick" tag="101" enabled="NO" keyEquivalent="j" id="5mn-ch-Xv6">
|
||||||
@ -227,6 +220,7 @@
|
|||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
</items>
|
</items>
|
||||||
|
<point key="canvasLocation" x="140" y="154"/>
|
||||||
</menu>
|
</menu>
|
||||||
</objects>
|
</objects>
|
||||||
</document>
|
</document>
|
||||||
|
@ -531,8 +531,12 @@ class MachineDocument:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Joystick-via-the-keyboard selection
|
// MARK: Joystick-via-the-keyboard selection
|
||||||
@IBAction func useKeyboardAsKeyboard(_ sender: NSMenuItem?) {
|
@IBAction func useKeyboardAsPhysicalKeyboard(_ sender: NSMenuItem?) {
|
||||||
machine.inputMode = .keyboard
|
machine.inputMode = .keyboardPhysical
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func useKeyboardAsLogicalKeyboard(_ sender: NSMenuItem?) {
|
||||||
|
machine.inputMode = .keyboardLogical
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func useKeyboardAsJoystick(_ sender: NSMenuItem?) {
|
@IBAction func useKeyboardAsJoystick(_ sender: NSMenuItem?) {
|
||||||
@ -545,13 +549,22 @@ class MachineDocument:
|
|||||||
override func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
override func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool {
|
||||||
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.useKeyboardAsPhysicalKeyboard):
|
||||||
if machine == nil || !machine.hasExclusiveKeyboard {
|
if machine == nil || !machine.hasExclusiveKeyboard {
|
||||||
menuItem.state = .off
|
menuItem.state = .off
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
menuItem.state = machine.inputMode == .keyboard ? .on : .off
|
menuItem.state = machine.inputMode == .keyboardPhysical ? .on : .off
|
||||||
|
return true
|
||||||
|
|
||||||
|
case #selector(self.useKeyboardAsLogicalKeyboard):
|
||||||
|
if machine == nil || !machine.hasExclusiveKeyboard {
|
||||||
|
menuItem.state = .off
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
menuItem.state = machine.inputMode == .keyboardLogical ? .on : .off
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case #selector(self.useKeyboardAsJoystick):
|
case #selector(self.useKeyboardAsJoystick):
|
||||||
|
@ -28,8 +28,9 @@ typedef NS_ENUM(NSInteger, CSMachineVideoSignal) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
typedef NS_ENUM(NSInteger, CSMachineKeyboardInputMode) {
|
||||||
CSMachineKeyboardInputModeKeyboard,
|
CSMachineKeyboardInputModeKeyboardPhysical,
|
||||||
CSMachineKeyboardInputModeJoystick
|
CSMachineKeyboardInputModeKeyboardLogical,
|
||||||
|
CSMachineKeyboardInputModeJoystick,
|
||||||
};
|
};
|
||||||
|
|
||||||
@interface CSMissingROM: NSObject
|
@interface CSMissingROM: NSObject
|
||||||
|
@ -201,9 +201,10 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use the keyboard as a joystick if the machine has no keyboard, or if it has a 'non-exclusive' keyboard.
|
||||||
_inputMode =
|
_inputMode =
|
||||||
(_machine->keyboard_machine() && _machine->keyboard_machine()->get_keyboard().is_exclusive())
|
(_machine->keyboard_machine() && _machine->keyboard_machine()->get_keyboard().is_exclusive())
|
||||||
? CSMachineKeyboardInputModeKeyboard : CSMachineKeyboardInputModeJoystick;
|
? CSMachineKeyboardInputModeKeyboardPhysical : 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();
|
||||||
@ -429,7 +430,7 @@ 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(keyboard_machine && (self.inputMode == CSMachineKeyboardInputModeKeyboard || !keyboard_machine->get_keyboard().is_exclusive())) {
|
if(keyboard_machine && (self.inputMode != CSMachineKeyboardInputModeJoystick || !keyboard_machine->get_keyboard().is_exclusive())) {
|
||||||
Inputs::Keyboard::Key mapped_key = Inputs::Keyboard::Key::Help; // Make an innocuous default guess.
|
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;
|
#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
|
// Connect the Carbon-era Mac keyboard scancodes to Clock Signal's 'universal' enumeration in order
|
||||||
@ -503,9 +504,25 @@ struct ActivityObserver: public Activity::Observer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this is logical mode and this key maps to a symbol, supply it
|
||||||
|
// as something to type. If this isn't logical mode, or this key doesn't
|
||||||
|
// map to a symbol, pass it along as a standard press.
|
||||||
|
if(self.inputMode == CSMachineKeyboardInputModeKeyboardLogical) {
|
||||||
|
@synchronized(self) {
|
||||||
|
if(pressedKey && keyboard_machine->can_type(pressedKey)) {
|
||||||
|
if(isPressed) {
|
||||||
|
char string[2] = { pressedKey, 0 };
|
||||||
|
keyboard_machine->type_string(string);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@synchronized(self) {
|
@synchronized(self) {
|
||||||
keyboard.set_key_pressed(mapped_key, pressedKey, isPressed);
|
keyboard.set_key_pressed(mapped_key, pressedKey, isPressed);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -469,7 +469,7 @@ int main(int argc, char *argv[]) {
|
|||||||
ParsedArguments arguments = parse_arguments(argc, argv);
|
ParsedArguments arguments = parse_arguments(argc, argv);
|
||||||
|
|
||||||
// This may be printed either as
|
// This may be printed either as
|
||||||
const std::string usage_suffix = " [file] [OPTIONS] [--rompath={path to ROMs}] [--speed={speed multiplier, e.g. 1.5}]";
|
const std::string usage_suffix = " [file] [OPTIONS] [--rompath={path to ROMs}] [--speed={speed multiplier, e.g. 1.5}]"; /* [--logical-keyboard] */
|
||||||
|
|
||||||
// Print a help message if requested.
|
// Print a help message if requested.
|
||||||
if(arguments.selections.find("help") != arguments.selections.end() || arguments.selections.find("h") != arguments.selections.end()) {
|
if(arguments.selections.find("help") != arguments.selections.end() || arguments.selections.find("h") != arguments.selections.end()) {
|
||||||
@ -610,6 +610,12 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check whether a 'logical' keyboard has been requested.
|
||||||
|
constexpr bool logical_keyboard = false; //arguments.selections.find("logical-keyboard") != arguments.selections.end();
|
||||||
|
/* Logical keyboard entry is currently disabled; the attempt below to get logical input via SDL_GetKeyName is
|
||||||
|
too flawed — all letters are always capitals, shifted symbols are reported correctly on their first
|
||||||
|
press only, etc. I need to see whether other options are available. If not then SDL may not allow this feature.*/
|
||||||
|
|
||||||
// Wire up the best-effort updater, its delegate, and the speaker delegate.
|
// Wire up the best-effort updater, its delegate, and the speaker delegate.
|
||||||
machine_runner.machine = machine.get();
|
machine_runner.machine = machine.get();
|
||||||
machine_runner.machine_mutex = &machine_mutex;
|
machine_runner.machine_mutex = &machine_mutex;
|
||||||
@ -890,14 +896,24 @@ int main(int argc, char *argv[]) {
|
|||||||
const bool is_pressed = event.type == SDL_KEYDOWN;
|
const bool is_pressed = event.type == SDL_KEYDOWN;
|
||||||
|
|
||||||
if(keyboard_machine) {
|
if(keyboard_machine) {
|
||||||
|
// Grab the key's symbol.
|
||||||
|
char key_value = '\0';
|
||||||
|
const char *key_name = SDL_GetKeyName(event.key.keysym.sym);
|
||||||
|
if(key_name[0] >= 0 && key_name[1] == 0) key_value = key_name[0];
|
||||||
|
|
||||||
|
// If a logical mapping was selected and a symbol was found, type it.
|
||||||
|
if(logical_keyboard && key_value != '\0' && keyboard_machine->can_type(key_value)) {
|
||||||
|
if(is_pressed) {
|
||||||
|
char string[] = { key_value, 0 };
|
||||||
|
keyboard_machine->type_string(string);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, supply as a normal keypress.
|
||||||
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()) {
|
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);
|
keyboard_machine->get_keyboard().set_key_pressed(key, key_value, is_pressed);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -705,7 +705,7 @@ void ProcessorBase::set_reset_line(bool active) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ProcessorBase::get_is_resetting() {
|
bool ProcessorBase::get_is_resetting() {
|
||||||
return !!(interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn));
|
return interrupt_requests_ & (InterruptRequestFlags::Reset | InterruptRequestFlags::PowerOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessorBase::set_power_on(bool active) {
|
void ProcessorBase::set_power_on(bool active) {
|
||||||
|
@ -550,3 +550,7 @@ bool ProcessorBase::is_starting_new_instruction() {
|
|||||||
current_instruction_page_ == &base_page_ &&
|
current_instruction_page_ == &base_page_ &&
|
||||||
scheduled_program_counter_ == &base_page_.fetch_decode_execute[0];
|
scheduled_program_counter_ == &base_page_.fetch_decode_execute[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ProcessorBase::get_is_resetting() {
|
||||||
|
return request_status_ & (Interrupt::PowerOn | Interrupt::Reset);
|
||||||
|
}
|
||||||
|
@ -219,6 +219,13 @@ class ProcessorBase: public ProcessorStorage {
|
|||||||
*/
|
*/
|
||||||
inline void set_reset_line(bool value);
|
inline void set_reset_line(bool value);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Gets whether the Z80 would reset at the next opportunity.
|
||||||
|
|
||||||
|
@returns @c true if the line is logically active; @c false otherwise.
|
||||||
|
*/
|
||||||
|
bool get_is_resetting();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
This emulation automatically sets itself up in power-on state at creation, which has the effect of triggering a
|
This emulation automatically sets itself up in power-on state at creation, which has the effect of triggering a
|
||||||
reset at the first opportunity. Use @c reset_power_on to disable that behaviour.
|
reset at the first opportunity. Use @c reset_power_on to disable that behaviour.
|
||||||
|
Loading…
Reference in New Issue
Block a user