From 328291ae3e90668b112755336fcafb7d5cd1275f Mon Sep 17 00:00:00 2001 From: dogeystamp Date: Sun, 7 Jan 2024 21:13:03 -0500 Subject: [PATCH] implement mem-map keyboard registers - also trap: GETC --- src/main.rs | 4 ++-- src/vm/instruction.rs | 7 ++++++- src/vm/memory.rs | 22 ++++++++++++++++------ src/vm/mod.rs | 10 ++++------ src/vm/terminal_io.rs | 24 +++++++++++++++--------- 5 files changed, 43 insertions(+), 24 deletions(-) diff --git a/src/main.rs b/src/main.rs index f7ab8af..eaecd22 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,9 +9,9 @@ use std::env; fn main() { let args: Vec<_> = env::args().collect(); - let term = terminal_io::TerminalIO::new(); + let mut term = terminal_io::TerminalIO::new(); - let mut vm = VM::new(&term); + let mut vm = VM::new(&mut term); vm.read_program(args.get(1).expect("No program file given")); vm.execute(); } diff --git a/src/vm/instruction.rs b/src/vm/instruction.rs index c080876..ee7f8cd 100644 --- a/src/vm/instruction.rs +++ b/src/vm/instruction.rs @@ -275,7 +275,7 @@ fn op_trap(vm: &mut VM, instr: u16) { let trap_vector = instr & 0xff; match trap_vector { - 0x20 => todo!("GETC"), + 0x20 => trap_getc(vm), 0x21 => todo!("OUT"), 0x22 => trap_puts(vm), 0x23 => todo!("IN"), @@ -297,3 +297,8 @@ fn trap_puts(vm: &mut VM) { idx += 1; } } + +fn trap_getc(vm: &mut VM) { + while vm.mem.get_mem(0xFE00) & 1 == 0 {} + vm.registers.r0 = vm.mem.get_mem(0xFE02) & 0xFF; +} diff --git a/src/vm/memory.rs b/src/vm/memory.rs index eb1da07..b5bfc57 100644 --- a/src/vm/memory.rs +++ b/src/vm/memory.rs @@ -10,14 +10,16 @@ use super::terminal_io; pub const MEM_SIZE: usize = 1 << 16; -pub struct Memory { +pub struct Memory<'a> { data: [u16; MEM_SIZE], + io: &'a mut dyn terminal_io::KeyboardIO, } -impl Memory { - pub fn new() -> Memory { +impl Memory<'_> { + pub fn new(keyboard_io: &mut dyn terminal_io::KeyboardIO) -> Memory { Memory { data: [0; MEM_SIZE], + io: keyboard_io, } } @@ -25,11 +27,19 @@ impl Memory { self.data[addr as usize] = val; } - pub fn get_mem(&self, addr: u16) -> u16 { + pub fn get_mem(&mut self, addr: u16) -> u16 { if addr >= 0xFE00 { - match addr { + return match addr { + 0xFE00 => self.io.check_key() as u16, + 0xFE02 => { + let key = match self.io.get_key() { + Some(key) => key as u16, + None => self.data[0xFE02], + }; + return key; + } _ => unimplemented!("mem-map: {:#X}", addr), - } + }; } return self.data[addr as usize]; } diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 6b4d65e..08b551f 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -114,22 +114,20 @@ impl Registers { // NOTE // https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html // tl;dr the 'a is like a generic type name except it means that for some "lifetime" 'a, we will -// hold a reference to `io` and we promise not to have it outlive the struct +// hold a reference to `io` and we promise not to not have the struct outlive it // this helps prevent dangling references pub struct VM<'a> { - mem: memory::Memory, + mem: memory::Memory<'a>, registers: Registers, - io: &'a dyn terminal_io::KeyboardIO, running: bool, } impl VM<'_> { - pub fn new(keyboard_io: &impl terminal_io::KeyboardIO) -> VM { + pub fn new(keyboard_io: &mut dyn terminal_io::KeyboardIO) -> VM { VM { - mem: memory::Memory::new(), + mem: memory::Memory::new(keyboard_io), registers: Registers::new(), running: false, - io: keyboard_io } } diff --git a/src/vm/terminal_io.rs b/src/vm/terminal_io.rs index 82462b8..288025e 100644 --- a/src/vm/terminal_io.rs +++ b/src/vm/terminal_io.rs @@ -11,7 +11,7 @@ use libc::STDIN_FILENO; extern crate ctrlc; use std::io; -use std::io::BufRead; +use std::io::Read; use std::sync::mpsc; use std::sync::mpsc::Receiver; use std::thread; @@ -23,6 +23,8 @@ use std::thread; pub trait KeyboardIO { /// Poll stdin for a keypress fn get_key(&mut self) -> Option; + /// Peek to see if there is a key + fn check_key(&mut self) -> bool; } pub struct TerminalIO { @@ -40,13 +42,10 @@ impl TerminalIO { fn spawn_stdin_channel() -> Receiver { // https://stackoverflow.com/questions/30012995 let (tx, rx) = mpsc::channel::(); - let mut buffer = Vec::new(); + let mut buffer: [u8; 1] = [0]; thread::spawn(move || loop { - buffer.clear(); - let _ = io::stdin().lock().read_until(1, &mut buffer); - for c in &buffer { - let _ = tx.send(*c); - } + let _ = io::stdin().lock().read_exact(&mut buffer); + let _ = tx.send(buffer[0]); }); rx } @@ -61,11 +60,18 @@ impl Drop for TerminalIO { impl KeyboardIO for TerminalIO { fn get_key(&mut self) -> Option { match self.stdin_channel.try_recv() { - Ok(key) => return Some(key), - Err(mpsc::TryRecvError::Empty) => return None, + Ok(key) => Some(key), + Err(mpsc::TryRecvError::Empty) => None, Err(mpsc::TryRecvError::Disconnected) => panic!("terminal keyboard stream broke"), } } + + fn check_key(&mut self) -> bool { + match self.stdin_channel.try_iter().peekable().peek() { + Some(data) => true, + None => false, + } + } } ////////////////