mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 01:32:55 +00:00
Sketch out whole protocol, albeit faulty.
This commit is contained in:
parent
eb34c38332
commit
5967ad0865
@ -17,46 +17,146 @@ struct Keyboard {
|
|||||||
Keyboard(HalfDuplexSerial &serial) : serial_(serial) {}
|
Keyboard(HalfDuplexSerial &serial) : serial_(serial) {}
|
||||||
|
|
||||||
void set_key_state(int row, int column, bool is_pressed) {
|
void set_key_state(int row, int column, bool is_pressed) {
|
||||||
|
// if(!scan_keyboard_) return;
|
||||||
|
// TODO: scan_keyboard_ seems to end up in the wrong state. Investigate.
|
||||||
|
|
||||||
const uint8_t prefix = is_pressed ? 0b1100'0000 : 0b1101'0000;
|
const uint8_t prefix = is_pressed ? 0b1100'0000 : 0b1101'0000;
|
||||||
queue_.push_back(static_cast<uint8_t>(prefix | row));
|
enqueue(static_cast<uint8_t>(prefix | row), static_cast<uint8_t>(prefix | column));
|
||||||
queue_.push_back(static_cast<uint8_t>(prefix | column));
|
consider_dequeue();
|
||||||
dequeue_next();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void update() {
|
void update() {
|
||||||
if(serial_.events(KeyboardParty) & HalfDuplexSerial::Receive) {
|
if(serial_.events(KeyboardParty) & HalfDuplexSerial::Receive) {
|
||||||
|
const auto reset = [&]() {
|
||||||
|
serial_.output(KeyboardParty, HRST);
|
||||||
|
phase_ = Phase::Idle;
|
||||||
|
};
|
||||||
|
|
||||||
const uint8_t input = serial_.input(KeyboardParty);
|
const uint8_t input = serial_.input(KeyboardParty);
|
||||||
switch(input) {
|
|
||||||
case HRST:
|
// A reset command is always accepted, usurping any other state.
|
||||||
// TODO:
|
if(input == HRST) {
|
||||||
case RAK1:
|
phase_ = Phase::ExpectingRAK1;
|
||||||
case RAK2:
|
event_queue_.clear();
|
||||||
|
serial_.output(KeyboardParty, HRST);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(phase_) {
|
||||||
|
case Phase::Idle:
|
||||||
|
switch(input) {
|
||||||
|
case RQID: // Post keyboard ID.
|
||||||
|
serial_.output(KeyboardParty, 0x81); // Declare this to be a UK keyboard.
|
||||||
|
phase_ = Phase::ExpectingACK;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PRST: // "1-byte command, does nothing."
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RQMP:
|
||||||
|
// TODO: real mouse data.
|
||||||
|
enqueue(0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if((input & 0b1111'0000) == 0b0100'0000) {
|
||||||
|
// RQPD; request to echo the low nibble.
|
||||||
|
serial_.output(KeyboardParty, 0b1110'0000 | (input & 0b1111));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(input & 0b1111'1000)) {
|
||||||
|
// LEDS: should set LEd outputs.
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Phase::ExpectingRAK1:
|
||||||
|
if(input != RAK1) {
|
||||||
|
reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
serial_.output(KeyboardParty, input);
|
serial_.output(KeyboardParty, input);
|
||||||
|
phase_ = Phase::ExpectingRAK2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RQID:
|
case Phase::ExpectingRAK2:
|
||||||
serial_.output(KeyboardParty, 0x81); // TODO: what keyboard type?
|
if(input != RAK2) {
|
||||||
|
reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
serial_.output(KeyboardParty, input);
|
||||||
|
phase_ = Phase::ExpectingACK;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BACK:
|
case Phase::ExpectingBACK:
|
||||||
dequeue_next();
|
if(input != BACK) {
|
||||||
|
reset();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
phase_ = Phase::ExpectingACK;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case Phase::ExpectingACK:
|
||||||
printf("Keyboard declines to respond to %02x\n", input);
|
switch(input) {
|
||||||
|
default:
|
||||||
|
reset();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NACK:
|
||||||
|
scan_keyboard_ = scan_mouse_ = false;
|
||||||
|
break;
|
||||||
|
case SMAK:
|
||||||
|
scan_keyboard_ = scan_mouse_ = true;
|
||||||
|
break;
|
||||||
|
case MACK:
|
||||||
|
scan_keyboard_ = false;
|
||||||
|
scan_mouse_ = true;
|
||||||
|
break;
|
||||||
|
case SACK:
|
||||||
|
scan_keyboard_ = true;
|
||||||
|
scan_mouse_ = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
phase_ = Phase::Idle;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
consider_dequeue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void consider_dequeue() {
|
||||||
|
if(phase_ == Phase::Idle) {
|
||||||
|
dequeue_next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HalfDuplexSerial &serial_;
|
HalfDuplexSerial &serial_;
|
||||||
|
|
||||||
std::vector<uint8_t> queue_;
|
bool scan_keyboard_ = false;
|
||||||
void dequeue_next() {
|
bool scan_mouse_ = false;
|
||||||
if(queue_.empty()) return;
|
enum class Phase {
|
||||||
serial_.output(KeyboardParty, queue_[0]);
|
ExpectingRAK1,
|
||||||
queue_.erase(queue_.begin());
|
ExpectingRAK2,
|
||||||
|
ExpectingBACK,
|
||||||
|
ExpectingACK,
|
||||||
|
Idle,
|
||||||
|
} phase_ = Phase::Idle;
|
||||||
|
|
||||||
|
std::vector<uint8_t> event_queue_;
|
||||||
|
void enqueue(uint8_t first, uint8_t second) {
|
||||||
|
event_queue_.push_back(first);
|
||||||
|
event_queue_.push_back(second);
|
||||||
|
}
|
||||||
|
bool dequeue_next() {
|
||||||
|
// To consider: a cheaper approach to the queue than this; in practice events
|
||||||
|
// are 'rare' so it's not high priority.
|
||||||
|
if(event_queue_.empty()) return false;
|
||||||
|
serial_.output(KeyboardParty, event_queue_[0]);
|
||||||
|
event_queue_.erase(event_queue_.begin());
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr uint8_t HRST = 0b1111'1111; // Keyboard reset.
|
static constexpr uint8_t HRST = 0b1111'1111; // Keyboard reset.
|
||||||
|
Loading…
Reference in New Issue
Block a user