implement mem-map keyboard registers

- also trap: GETC
This commit is contained in:
dogeystamp 2024-01-07 21:13:03 -05:00
parent 25a77ecfb7
commit 328291ae3e
Signed by: dogeystamp
GPG Key ID: 7225FE3592EFFA38
5 changed files with 43 additions and 24 deletions

View File

@ -9,9 +9,9 @@ use std::env;
fn main() { fn main() {
let args: Vec<_> = env::args().collect(); 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.read_program(args.get(1).expect("No program file given"));
vm.execute(); vm.execute();
} }

View File

@ -275,7 +275,7 @@ fn op_trap(vm: &mut VM, instr: u16) {
let trap_vector = instr & 0xff; let trap_vector = instr & 0xff;
match trap_vector { match trap_vector {
0x20 => todo!("GETC"), 0x20 => trap_getc(vm),
0x21 => todo!("OUT"), 0x21 => todo!("OUT"),
0x22 => trap_puts(vm), 0x22 => trap_puts(vm),
0x23 => todo!("IN"), 0x23 => todo!("IN"),
@ -297,3 +297,8 @@ fn trap_puts(vm: &mut VM) {
idx += 1; 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;
}

View File

@ -10,14 +10,16 @@ use super::terminal_io;
pub const MEM_SIZE: usize = 1 << 16; pub const MEM_SIZE: usize = 1 << 16;
pub struct Memory { pub struct Memory<'a> {
data: [u16; MEM_SIZE], data: [u16; MEM_SIZE],
io: &'a mut dyn terminal_io::KeyboardIO,
} }
impl Memory { impl Memory<'_> {
pub fn new() -> Memory { pub fn new(keyboard_io: &mut dyn terminal_io::KeyboardIO) -> Memory {
Memory { Memory {
data: [0; MEM_SIZE], data: [0; MEM_SIZE],
io: keyboard_io,
} }
} }
@ -25,11 +27,19 @@ impl Memory {
self.data[addr as usize] = val; 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 { 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), _ => unimplemented!("mem-map: {:#X}", addr),
} };
} }
return self.data[addr as usize]; return self.data[addr as usize];
} }

View File

@ -114,22 +114,20 @@ impl Registers {
// NOTE // NOTE
// https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html // 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 // 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 // this helps prevent dangling references
pub struct VM<'a> { pub struct VM<'a> {
mem: memory::Memory, mem: memory::Memory<'a>,
registers: Registers, registers: Registers,
io: &'a dyn terminal_io::KeyboardIO,
running: bool, running: bool,
} }
impl VM<'_> { impl VM<'_> {
pub fn new(keyboard_io: &impl terminal_io::KeyboardIO) -> VM { pub fn new(keyboard_io: &mut dyn terminal_io::KeyboardIO) -> VM {
VM { VM {
mem: memory::Memory::new(), mem: memory::Memory::new(keyboard_io),
registers: Registers::new(), registers: Registers::new(),
running: false, running: false,
io: keyboard_io
} }
} }

View File

@ -11,7 +11,7 @@ use libc::STDIN_FILENO;
extern crate ctrlc; extern crate ctrlc;
use std::io; use std::io;
use std::io::BufRead; use std::io::Read;
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::mpsc::Receiver; use std::sync::mpsc::Receiver;
use std::thread; use std::thread;
@ -23,6 +23,8 @@ use std::thread;
pub trait KeyboardIO { pub trait KeyboardIO {
/// Poll stdin for a keypress /// Poll stdin for a keypress
fn get_key(&mut self) -> Option<u8>; fn get_key(&mut self) -> Option<u8>;
/// Peek to see if there is a key
fn check_key(&mut self) -> bool;
} }
pub struct TerminalIO { pub struct TerminalIO {
@ -40,13 +42,10 @@ impl TerminalIO {
fn spawn_stdin_channel() -> Receiver<u8> { fn spawn_stdin_channel() -> Receiver<u8> {
// https://stackoverflow.com/questions/30012995 // https://stackoverflow.com/questions/30012995
let (tx, rx) = mpsc::channel::<u8>(); let (tx, rx) = mpsc::channel::<u8>();
let mut buffer = Vec::new(); let mut buffer: [u8; 1] = [0];
thread::spawn(move || loop { thread::spawn(move || loop {
buffer.clear(); let _ = io::stdin().lock().read_exact(&mut buffer);
let _ = io::stdin().lock().read_until(1, &mut buffer); let _ = tx.send(buffer[0]);
for c in &buffer {
let _ = tx.send(*c);
}
}); });
rx rx
} }
@ -61,11 +60,18 @@ impl Drop for TerminalIO {
impl KeyboardIO for TerminalIO { impl KeyboardIO for TerminalIO {
fn get_key(&mut self) -> Option<u8> { fn get_key(&mut self) -> Option<u8> {
match self.stdin_channel.try_recv() { match self.stdin_channel.try_recv() {
Ok(key) => return Some(key), Ok(key) => Some(key),
Err(mpsc::TryRecvError::Empty) => return None, Err(mpsc::TryRecvError::Empty) => None,
Err(mpsc::TryRecvError::Disconnected) => panic!("terminal keyboard stream broke"), 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,
}
}
} }
//////////////// ////////////////