mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-09 01:10:10 +00:00
9cc747b3e2
(Having made exactly this mistake with the ZX Spectrum)
134 lines
3.5 KiB
C++
134 lines
3.5 KiB
C++
//
|
|
// Typer.cpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 19/06/2016.
|
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#include "Typer.hpp"
|
|
|
|
using namespace Utility;
|
|
|
|
Typer::Typer(const std::string &string, HalfCycles delay, HalfCycles frequency, CharacterMapper &character_mapper, Delegate *delegate) :
|
|
frequency_(frequency),
|
|
counter_(-delay),
|
|
delegate_(delegate),
|
|
character_mapper_(character_mapper) {
|
|
// Retain only those characters that actually map to something.
|
|
if(sequence_for_character(Typer::BeginString)) {
|
|
string_ += Typer::BeginString;
|
|
}
|
|
if(sequence_for_character(Typer::EndString)) {
|
|
string_ += Typer::EndString;
|
|
}
|
|
|
|
append(string);
|
|
}
|
|
|
|
void Typer::run_for(const HalfCycles duration) {
|
|
if(string_pointer_ >= string_.size()) {
|
|
return;
|
|
}
|
|
|
|
if(counter_ < 0 && counter_ + duration >= 0) {
|
|
if(!type_next_character()) {
|
|
delegate_->typer_reset(this);
|
|
}
|
|
}
|
|
|
|
counter_ += duration;
|
|
while(string_pointer_ < string_.size() && counter_ > frequency_) {
|
|
counter_ -= frequency_;
|
|
if(!type_next_character()) {
|
|
delegate_->typer_reset(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Typer::append(const std::string &string) {
|
|
// 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] == MachineTypes::MappedKeyboardMachine::KeyNotMapped) {
|
|
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;
|
|
}
|
|
|
|
// Advance phase.
|
|
++phase_;
|
|
|
|
// 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_;
|
|
}
|
|
|
|
// If the sequence is over, stop.
|
|
if(sequence[phase_ - 2] == MachineTypes::MappedKeyboardMachine::KeyEndSequence) {
|
|
return 0;
|
|
}
|
|
|
|
// Otherwise, type the key.
|
|
delegate_->set_key_state(sequence[phase_ - 2], true);
|
|
|
|
return sequence[phase_ - 2];
|
|
}
|
|
|
|
bool Typer::type_next_character() {
|
|
if(string_pointer_ == string_.size()) return false;
|
|
|
|
while(true) {
|
|
const uint16_t key_pressed = try_type_next_character();
|
|
|
|
if(!key_pressed) {
|
|
phase_ = 0;
|
|
++string_pointer_;
|
|
if(string_pointer_ == string_.size()) return false;
|
|
}
|
|
|
|
if(character_mapper_.needs_pause_after_key(key_pressed)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|