1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 16:31:31 +00:00
CLK/Machines/Acorn/Archimedes/HalfDuplexSerial.hpp

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;
}