82 lines
2.8 KiB
Rust
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(());
|
|
}
|
|
}
|