1
0
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:
Thomas Harte 2024-03-23 17:08:03 -04:00
parent eb34c38332
commit 5967ad0865

View File

@ -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.