summaryrefslogtreecommitdiff
path: root/src/devices/local_device.rs
diff options
context:
space:
mode:
authorBen Bridle <ben@derelict.engineering>2025-03-25 12:46:49 +1300
committerBen Bridle <ben@derelict.engineering>2025-03-25 12:48:49 +1300
commitab84ad75629b0a4124221023ca91411d2cd62a32 (patch)
tree0c333f06bec5270084aaec71cf173c798420207b /src/devices/local_device.rs
parent07ae3438917fd854a46924a410f6890cd0651f1b (diff)
downloadbedrock-pc-ab84ad75629b0a4124221023ca91411d2cd62a32.zip
Restructure program
This commit also includes changes to devices according to the latest devices specification, in particular the math and system devices.
Diffstat (limited to 'src/devices/local_device.rs')
-rw-r--r--src/devices/local_device.rs227
1 files changed, 0 insertions, 227 deletions
diff --git a/src/devices/local_device.rs b/src/devices/local_device.rs
deleted file mode 100644
index c6456de..0000000
--- a/src/devices/local_device.rs
+++ /dev/null
@@ -1,227 +0,0 @@
-use crate::*;
-
-use bedrock_core::*;
-
-use std::collections::VecDeque;
-use std::io::{BufRead, Stdout, Write};
-use std::sync::mpsc::{self, TryRecvError};
-
-
-pub struct LocalDevice {
- wake: bool,
-
- stdin_connected: bool,
- stdin_control: bool,
- stdin_rx: mpsc::Receiver<Vec<u8>>,
- stdin_queue: VecDeque<u8>,
- stdin_excess: VecDeque<u8>,
- stdout: Stdout,
-
- stdout_connected: bool,
-
- decode_stdin: bool,
- encode_stdout: bool,
- decode_buffer: Option<u8>,
-}
-
-impl LocalDevice {
- pub fn new(config: &EmulatorConfig) -> Self {
- // Fill input queue with initial transmission.
- let mut stdin_queue = VecDeque::new();
- if let Some(bytes) = &config.initial_transmission {
- for byte in bytes { stdin_queue.push_front(*byte) }
- }
- // Spawn a thread to enable non-blocking reads of stdin.
- let (stdin_tx, stdin_rx) = std::sync::mpsc::channel();
- std::thread::spawn(move || loop {
- let mut stdin = std::io::stdin().lock();
- match stdin.fill_buf() {
- Ok(buf) if !buf.is_empty() => {
- let length = buf.len();
- stdin_tx.send(buf.to_vec()).unwrap();
- stdin.consume(length);
- }
- _ => break,
- };
- });
-
- Self {
- wake: true,
-
- stdin_connected: true,
- stdin_control: false,
- stdin_rx,
- stdin_queue,
- stdin_excess: VecDeque::new(),
- stdout: std::io::stdout(),
-
- stdout_connected: true,
-
- decode_stdin: config.decode_stdin,
- encode_stdout: config.encode_stdout,
- decode_buffer: None,
- }
- }
-
- pub fn stdin_length(&mut self) -> u8 {
- self.fetch_stdin_data();
- self.stdin_queue.len().try_into().unwrap_or(u8::MAX)
- }
-
- pub fn stdin_enable(&mut self) {
- self.stdin_control = true;
- }
-
- pub fn stdin_read(&mut self) -> u8 {
- self.fetch_stdin_data();
- self.stdin_queue.pop_front().unwrap_or(0)
- }
-
- pub fn stdout_write(&mut self, value: u8) {
- macro_rules! hex {
- ($value:expr) => { match $value {
- 0x0..=0x9 => $value + b'0',
- 0xa..=0xf => $value - 0x0a + b'a',
- _ => unreachable!("Cannot encode value as hex digit: 0x{:02x}", $value),
- } };
- }
- if self.encode_stdout {
- let encoded = [hex!(value >> 4), hex!(value & 0xf), b' '];
- self.stdout_write_raw(&encoded);
- } else {
- self.stdout_write_raw(&[value]);
- };
- }
-
- fn stdout_write_raw(&mut self, bytes: &[u8]) {
- if let Err(_) = self.stdout.write_all(bytes) {
- if self.stdout_connected {
- self.stdout_connected = false;
- self.wake = true; // wake because stdout was disconnected.
- }
- }
- }
-
- pub fn stdout_disable(&mut self) {
- if self.encode_stdout {
- self.stdout_write_raw(&[b'\n']);
- }
- }
-
- pub fn fetch_stdin_data(&mut self) {
- while self.stdin_control {
- match self.stdin_excess.pop_front() {
- Some(byte) => self.fetch_byte(byte),
- None => break,
- }
- }
- match self.stdin_rx.try_recv() {
- Ok(tx) => {
- for byte in tx {
- match self.stdin_control {
- true => self.fetch_byte(byte),
- false => self.stdin_excess.push_back(byte),
- }
- }
- }
- Err(TryRecvError::Empty) => (),
- Err(TryRecvError::Disconnected) => {
- self.stdin_control = false;
- if self.stdin_connected {
- self.stdin_connected = false;
- self.wake = true; // wake because stdin was disconnected.
- }
- }
- }
- }
-
- fn fetch_byte(&mut self, byte: u8) {
- if self.decode_stdin {
- let decoded = match byte {
- b'0'..=b'9' => byte - b'0',
- b'a'..=b'f' => byte - b'a' + 0x0a,
- b'A'..=b'F' => byte - b'A' + 0x0a,
- b'\n' => {
- self.decode_buffer = None;
- self.stdin_control = false;
- self.wake = true; // wake because a transmission ended.
- return;
- },
- _ => return,
- };
- if let Some(high) = std::mem::take(&mut self.decode_buffer) {
- self.stdin_queue.push_back((high << 4) | decoded);
- self.wake = true; // wake because a byte was received.
- } else {
- self.decode_buffer = Some(decoded);
- }
- } else {
- self.stdin_queue.push_back(byte);
- self.wake = true; // wake because a byte was received.
- }
- }
-
- pub fn flush(&mut self) {
- let _ = self.stdout.flush();
- }
-}
-
-
-impl Drop for LocalDevice {
- fn drop(&mut self) {
- self.flush();
- }
-}
-
-
-impl Device for LocalDevice {
- fn read(&mut self, port: u8) -> u8 {
- match port {
- 0x0 => read_b!(self.stdin_connected),
- 0x1 => 0xff,
- 0x2 => read_b!(self.stdin_control),
- 0x3 => 0xff,
- 0x4 => self.stdin_length(),
- 0x5 => 0xff,
- 0x6 => self.stdin_read(),
- 0x7 => self.stdin_read(),
- 0x8 => todo!(),
- 0x9 => todo!(),
- 0xa => todo!(),
- 0xb => todo!(),
- 0xc => todo!(),
- 0xd => todo!(),
- 0xe => todo!(),
- 0xf => todo!(),
- _ => unreachable!(),
- }
- }
-
- fn write(&mut self, port: u8, value: u8) -> Option<Signal> {
- match port {
- 0x0 => (),
- 0x1 => (),
- 0x2 => self.stdin_enable(),
- 0x3 => self.stdout_disable(),
- 0x4 => self.stdin_queue.clear(),
- 0x5 => (),
- 0x6 => self.stdout_write(value),
- 0x7 => self.stdout_write(value),
- 0x8 => todo!(),
- 0x9 => todo!(),
- 0xa => todo!(),
- 0xb => todo!(),
- 0xc => todo!(),
- 0xd => todo!(),
- 0xe => todo!(),
- 0xf => todo!(),
- _ => unreachable!(),
- };
- return None;
- }
-
- fn wake(&mut self) -> bool {
- self.fetch_stdin_data();
- std::mem::take(&mut self.wake)
- }
-}