Dekejit/src/cpu/sysenv/io_vec.rs
2023-04-28 12:24:28 +02:00

82 lines
2.8 KiB
Rust

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<IOBuffer>, 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<u16> = 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 <potentially> 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(());
}
}