// Copyright (c) 2025 taitep // SPDX-License-Identifier: MIT // // This file is part of TRVE (https://gitea.taitep.se/taitep/trve) // See LICENSE file in the project root for full license text. use std::collections::VecDeque; use std::io; use std::os::fd::AsFd; use std::sync::{Arc, Mutex}; use std::thread; use std::time::Duration; use nix::fcntl::fcntl; use nix::fcntl::{FcntlArg, OFlag}; use trve::consts::{Addr, Byte}; use trve::exceptions::MemoryExceptionType; use trve::mem::MemDeviceInterface; /// byte 0: rx/tx /// byte 1: status (------rt, r=rxready, t=txready)/none pub struct BasicUart { rx_buf: Mutex>, } impl BasicUart { pub fn new() -> Self { // Make sure stdin is nonblocking let stdin = io::stdin(); let fd = stdin.as_fd(); let flags = fcntl(fd, FcntlArg::F_GETFL).unwrap(); fcntl( fd, FcntlArg::F_SETFL(OFlag::from_bits_truncate(flags) | OFlag::O_NONBLOCK), ) .unwrap(); BasicUart { rx_buf: Mutex::new(VecDeque::new()), } } pub fn spawn_poller(self, poll_interval: Duration) -> Arc { let shared = Arc::new(self); let uart_clone = shared.clone(); thread::spawn(move || { loop { uart_clone.poll(); thread::sleep(poll_interval); } }); shared } fn write(&self, byte: u8) { print!("{}", byte as char); } fn read(&self) -> u8 { self.rx_buf.lock().unwrap().pop_front().unwrap_or(0) } fn can_read(&self) -> bool { !self.rx_buf.lock().unwrap().is_empty() } pub fn poll(&self) { let mut rx_buf = self.rx_buf.lock().unwrap(); let mut buffer = [0u8; 1]; while let Ok(1) = nix::unistd::read(io::stdin().as_fd(), &mut buffer) { rx_buf.push_back(buffer[0]); } } } impl MemDeviceInterface for BasicUart { fn write_byte(&self, addr: Addr, value: Byte) -> Result<(), MemoryExceptionType> { match addr { 0 => { self.write(value); Ok(()) } _ => Err(MemoryExceptionType::AccessFault), } } fn read_byte(&self, addr: Addr) -> Result { match addr { 0 => Ok(self.read()), 1 => Ok(1 | (self.can_read() as u8) << 1), _ => Err(MemoryExceptionType::AccessFault), } } }