use std::io::stdin; use crate::{ cpu::{ram::MAX_MEM, registers::Register}, loader::{loader::find_and_read_string, unloader::make_string}, }; use super::*; // first working environment, we get input from stdin and we write output // to a string. // // using strings to singal errors kinda sucks. // TODO: Fix this #[derive(Debug, Default)] pub struct IOBuffer { pub output: String, } impl Sys for IOBuffer { fn call(cpu: &mut CPU, r: Register, c: Word) -> CPUResult<()> { println!("called: {}", c); match c { // 0: write an integer to output 0 => { let i = cpu.regs.get(r).ok_or(ExecErr::InvalidRegister)?; cpu.env.output.push_str(&format!("{}", i)); } // 1: read an integer to some register 1 => { let mut buf = String::new(); stdin() .read_line(&mut buf) .map_err(|_| ExecErr::SyscallError("Cannot read stdin".to_owned()))?; let n: Word = buf .parse() .map_err(|_| ExecErr::SyscallError("Cannot read number".to_owned()))?; cpu.regs.write(r, n).ok_or(ExecErr::InvalidRegister)?; } // 2: reads a string from input and writes it to some location. 2 => { let mut buf = String::new(); stdin() .read_line(&mut buf) .map_err(|_| ExecErr::SyscallError("Cannot read stdin".to_owned()))?; let s: Vec = make_string(&buf); let start = cpu.regs.get(r).ok_or(ExecErr::InvalidRegister)?; cpu.ram .write_array(&s[..], start) .ok_or(ExecErr::SyscallError("Cannot write slice".to_owned()))?; } // 3: prints a string, reading it from memory. // r must contain the address of the string. // the string needs to be null-delimited. 3 => { let pos = cpu.regs.get(r).ok_or(ExecErr::InvalidRegister)?; // we slice from start to the end. // why? good question. The find_and_read_string // will short circuit as soon as it finds a null terminator, // which might never be found. let data = cpu .ram .slice(pos, MAX_MEM as Word - 1) .ok_or(ExecErr::InvalidMemoryAddr)?; let (s, _) = find_and_read_string(&data) .map_err(|_p| ExecErr::SyscallError("parse error!".to_owned()))?; cpu.env.output.push_str(&s); } _ => return Err(ExecErr::InvalidSyscall), } return Ok(()); } }