mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-01 01:29:03 +00:00
89 lines
2.2 KiB
C++
89 lines
2.2 KiB
C++
|
//
|
||
|
// HalfDuplexSerial.hpp
|
||
|
// Clock Signal
|
||
|
//
|
||
|
// Created by Thomas Harte on 20/03/2024.
|
||
|
// Copyright © 2024 Thomas Harte. All rights reserved.
|
||
|
//
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
namespace Archimedes {
|
||
|
|
||
|
/// Models a half-duplex serial link between two parties, framing bytes with one start bit and two stop bits.
|
||
|
struct HalfDuplexSerial {
|
||
|
static constexpr uint16_t ShiftMask = 0b1111'1110'0000'0000;
|
||
|
|
||
|
/// Enqueues @c value for output.
|
||
|
void output(int party, uint8_t value) {
|
||
|
parties_[party].output_count = 11;
|
||
|
parties_[party].input = 0x7ff;
|
||
|
parties_[party].output = uint16_t((value << 1) | ShiftMask);
|
||
|
}
|
||
|
|
||
|
/// @returns The last observed input.
|
||
|
uint8_t input(int party) const {
|
||
|
return uint8_t(parties_[party].input >> 1);
|
||
|
}
|
||
|
|
||
|
static constexpr uint8_t Receive = 1 << 0;
|
||
|
static constexpr uint8_t Transmit = 1 << 1;
|
||
|
|
||
|
/// @returns A bitmask of events that occurred during the last shift.
|
||
|
uint8_t events(int party) {
|
||
|
const auto result = parties_[party].events;
|
||
|
parties_[party].events = 0;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
bool is_outputting(int party) const {
|
||
|
return parties_[party].output_count != 11;
|
||
|
}
|
||
|
|
||
|
/// Updates the shifters on both sides of the serial link.
|
||
|
void shift() {
|
||
|
const uint16_t next = parties_[0].output & parties_[1].output & 1;
|
||
|
|
||
|
for(int c = 0; c < 2; c++) {
|
||
|
if(parties_[c].output_count) {
|
||
|
--parties_[c].output_count;
|
||
|
if(!parties_[c].output_count) {
|
||
|
parties_[c].events |= Transmit;
|
||
|
parties_[c].input_count = -1;
|
||
|
}
|
||
|
parties_[c].output = (parties_[c].output >> 1) | ShiftMask;
|
||
|
} else {
|
||
|
// Check for a start bit.
|
||
|
if(parties_[c].input_count == -1 && !next) {
|
||
|
parties_[c].input_count = 0;
|
||
|
}
|
||
|
|
||
|
// Shift in if currently observing.
|
||
|
if(parties_[c].input_count >= 0 && parties_[c].input_count < 11) {
|
||
|
parties_[c].input = uint16_t((parties_[c].input >> 1) | (next << 10));
|
||
|
|
||
|
++parties_[c].input_count;
|
||
|
if(parties_[c].input_count == 11) {
|
||
|
parties_[c].events |= Receive;
|
||
|
parties_[c].input_count = -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
struct Party {
|
||
|
int output_count = 0;
|
||
|
int input_count = -1;
|
||
|
uint16_t output = 0xffff;
|
||
|
uint16_t input = 0;
|
||
|
uint8_t events = 0;
|
||
|
} parties_[2];
|
||
|
};
|
||
|
|
||
|
static constexpr int IOCParty = 0;
|
||
|
static constexpr int KeyboardParty = 1;
|
||
|
|
||
|
}
|