From 79257c8757a07f4b964d5f93eeb11cbc19e4a2e6 Mon Sep 17 00:00:00 2001 From: Ramid Khan Date: Mon, 15 Jan 2024 12:52:03 +1100 Subject: [PATCH] Implement open, read, write, close in compatible environments The raw_io feature can be disabled on non-compatible environments like Windows --- Cargo.lock | 6 +- crates/mipsy/Cargo.toml | 7 +- crates/mipsy/src/main.rs | 156 ++++++++---------- crates/mipsy_interactive/Cargo.toml | 9 +- .../mipsy_interactive/src/interactive/mod.rs | 58 ++++--- .../src/interactive/runtime_handler.rs | 50 ++++-- crates/mipsy_lib/src/error/runtime/mod.rs | 8 + crates/mipsy_parser/src/misc.rs | 4 +- 8 files changed, 169 insertions(+), 129 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bcd8136..b1a880e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -776,9 +776,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.146" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "libgit2-sys" @@ -843,6 +843,7 @@ version = "0.1.0" dependencies = [ "clap", "colored", + "libc", "mipsy_instructions", "mipsy_interactive", "mipsy_lib", @@ -879,6 +880,7 @@ dependencies = [ "colored", "ctrlc", "dirs 3.0.2", + "libc", "mipsy_instructions", "mipsy_lib", "mipsy_parser", diff --git a/crates/mipsy/Cargo.toml b/crates/mipsy/Cargo.toml index 121a334..731d945 100644 --- a/crates/mipsy/Cargo.toml +++ b/crates/mipsy/Cargo.toml @@ -7,15 +7,20 @@ build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["raw_io"] +raw_io = ["libc", "mipsy_interactive/raw_io"] + [dependencies] mipsy_lib = { version = "0.1.0", path = "../mipsy_lib" } mipsy_parser = { version = "0.1.0", path = "../mipsy_parser" } -mipsy_interactive = { version = "0.1.0", path = "../mipsy_interactive" } +mipsy_interactive = { version = "0.1.0", path = "../mipsy_interactive", default-features = false } mipsy_utils = { version = "0.1.0", path = "../mipsy_utils" } mipsy_instructions = { version = "0.1.0", path = "../mipsy_instructions", features = ["rt_yaml"] } clap = { version = "4.0.4", features = ["derive", "wrap_help"] } # cli arg parsing colored = "2" # for ansi colors text_io = "0.1.8" # to read values in, w/out per line +libc = { version = "0.2.152", optional = true } [build-dependencies] vergen = { version = "7.5.1", default-features = false, features = ["git"] } # for version info diff --git a/crates/mipsy/src/main.rs b/crates/mipsy/src/main.rs index 4a82413..971e4d3 100644 --- a/crates/mipsy/src/main.rs +++ b/crates/mipsy/src/main.rs @@ -384,97 +384,62 @@ fn main() { let character: char = get_input_eof("character").unwrap_or('\0'); runtime = guard(character as u8); } + #[cfg(feature = "raw_io")] + Open(args, guard) => { + use std::ffi::CString; + + let name = CString::new(args.path) + .expect("runtime should have removed \\0"); + let fd = unsafe { + libc::open(name.as_ptr(), args.flags as i32, args.mode) + }; + runtime = guard(fd); + } + #[cfg(feature = "raw_io")] + Read(args, guard) => { + let mut vec = vec![0u8; args.len as usize]; + let read = unsafe { + libc::read(args.fd as i32, vec.as_mut_ptr().cast(), vec.len()) + }; + runtime = guard((read as i32, vec)); + } + #[cfg(feature = "raw_io")] + Write(args, guard) => { + let write = unsafe { + libc::write( + args.fd as i32, + args.buf.as_ptr().cast(), + args.buf.len(), + ) + } as i32; + runtime = guard(write); + } + #[cfg(feature = "raw_io")] + Close(args, guard) => { + let close = unsafe { libc::close(args.fd as i32) } as i32; + runtime = guard(close); + } + #[allow(unreachable_patterns)] // fall-through Open(_args, guard) => { - // TODO: implement file open for mipsy cli frontend - runtime = guard(-1); - runtime.timeline_mut().pop_last_state(); - println!(); - RuntimeError::new(Error::InvalidSyscall { - syscall: SYS13_OPEN, - reason: InvalidSyscallReason::Unimplemented, - }) - .show_error( - ErrorContext::Binary, - files - .iter() - .map(|(tag, content)| { - (Rc::from(&**tag), Rc::from(&**content)) - }) - .collect(), - &iset, - &binary, - &runtime, - ); - process::exit(1); + disabled_raw_io(SYS13_OPEN, guard(-1), files, iset, binary); } + #[allow(unreachable_patterns)] // fall-through Read(_args, guard) => { - // TODO: implement file read for mipsy cli frontend - runtime = guard((-1, Vec::new())); - runtime.timeline_mut().pop_last_state(); - println!(); - RuntimeError::new(Error::InvalidSyscall { - syscall: SYS14_READ, - reason: InvalidSyscallReason::Unimplemented, - }) - .show_error( - ErrorContext::Binary, - files - .iter() - .map(|(tag, content)| { - (Rc::from(&**tag), Rc::from(&**content)) - }) - .collect(), - &iset, - &binary, - &runtime, + disabled_raw_io( + SYS14_READ, + guard((-1, vec![])), + files, + iset, + binary, ); - process::exit(1); } + #[allow(unreachable_patterns)] // fall-through Write(_args, guard) => { - // TODO: implement file write for mipsy cli frontend - runtime = guard(-1); - runtime.timeline_mut().pop_last_state(); - println!(); - RuntimeError::new(Error::InvalidSyscall { - syscall: SYS15_WRITE, - reason: InvalidSyscallReason::Unimplemented, - }) - .show_error( - ErrorContext::Binary, - files - .iter() - .map(|(tag, content)| { - (Rc::from(&**tag), Rc::from(&**content)) - }) - .collect(), - &iset, - &binary, - &runtime, - ); - process::exit(1); + disabled_raw_io(SYS15_WRITE, guard(-1), files, iset, binary); } + #[allow(unreachable_patterns)] // fall-through Close(_args, guard) => { - // TODO: implement file close for mipsy cli frontend - runtime = guard(-1); - runtime.timeline_mut().pop_last_state(); - println!(); - RuntimeError::new(Error::InvalidSyscall { - syscall: SYS16_CLOSE, - reason: InvalidSyscallReason::Unimplemented, - }) - .show_error( - ErrorContext::Binary, - files - .iter() - .map(|(tag, content)| { - (Rc::from(&**tag), Rc::from(&**content)) - }) - .collect(), - &iset, - &binary, - &runtime, - ); - process::exit(1); + disabled_raw_io(SYS16_CLOSE, guard(-1), files, iset, binary); } ExitStatus(args, _new_runtime) => { std::process::exit(args.exit_code); @@ -514,6 +479,31 @@ fn main() { } } +fn disabled_raw_io( + syscall: i32, + mut runtime: Runtime, + files: Vec<(String, String)>, + iset: InstSet, + binary: Binary, +) -> ! { + runtime.timeline_mut().pop_last_state(); + let err = RuntimeError::new(Error::InvalidSyscall { + syscall, + reason: InvalidSyscallReason::Disabled, + }); + err.show_error( + ErrorContext::Binary, + files + .iter() + .map(|(tag, content)| (Rc::from(&**tag), Rc::from(&**content))) + .collect(), + &iset, + &binary, + &runtime, + ); + process::exit(1); +} + fn read_string(_max_len: u32) -> String { loop { let input: String = get_input("string", true); diff --git a/crates/mipsy_interactive/Cargo.toml b/crates/mipsy_interactive/Cargo.toml index b1f7c32..e0a9a5a 100644 --- a/crates/mipsy_interactive/Cargo.toml +++ b/crates/mipsy_interactive/Cargo.toml @@ -4,11 +4,12 @@ version = "0.1.0" authors = ["insou22 "] edition = "2021" -[lib] -path = "src/lib.rs" - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["raw_io"] +raw_io = ["libc"] + [dependencies] mipsy_lib = { version = "0.1.0", path = "../mipsy_lib" } mipsy_parser = { version = "0.1.0", path = "../mipsy_parser" } @@ -25,3 +26,5 @@ text_io = "0.1.8" # to read values in, w/out per line dirs = "3.0" # for user config directory ctrlc = { version = "3.0", features = ["termination"] } # for interrupt handling during execution termsize = "0.1" # to get terminal width info + +libc = { version = "0.2.152", optional = true } diff --git a/crates/mipsy_interactive/src/interactive/mod.rs b/crates/mipsy_interactive/src/interactive/mod.rs index fe19ecf..f1bef58 100644 --- a/crates/mipsy_interactive/src/interactive/mod.rs +++ b/crates/mipsy_interactive/src/interactive/mod.rs @@ -422,77 +422,85 @@ impl State { let value = runtime_handler::sys12_read_char(verbose); self.runtime = guard(value); } + #[cfg(feature = "raw_io")] + Open(args, guard) => { + let value = runtime_handler::sys13_open(verbose, args); + self.runtime = guard(value); + } + #[cfg(feature = "raw_io")] + Read(args, guard) => { + let value = runtime_handler::sys14_read(verbose, args); + self.runtime = guard(value); + } + #[cfg(feature = "raw_io")] + Write(args, guard) => { + let value = runtime_handler::sys15_write(verbose, args); + self.runtime = guard(value); + } + #[cfg(feature = "raw_io")] + Close(args, guard) => { + let value = runtime_handler::sys16_close(verbose, args); + self.runtime = guard(value); + } + #[allow(unreachable_patterns)] // fall-through Open(_args, guard) => { - // TODO: implement file open for mipsy interactive frontend - let mut new_runtime = guard(-1); new_runtime.timeline_mut().pop_last_state(); self.runtime = new_runtime; + return Err(CommandError::RuntimeError { mipsy_error: MipsyError::Runtime(RuntimeError::new( Error::InvalidSyscall { syscall: SYS13_OPEN, - reason: InvalidSyscallReason::Unimplemented, + reason: InvalidSyscallReason::Disabled, }, )), }); - - // let value = runtime_handler::sys13_open(verbose, args); - // self.runtime = Some(guard(value)); } + #[allow(unreachable_patterns)] // fall-through Read(_args, guard) => { - // TODO: implement file read for mipsy interactive frontend - - let mut new_runtime = guard((-1, Vec::new())); + let mut new_runtime = guard((-1, vec![])); new_runtime.timeline_mut().pop_last_state(); self.runtime = new_runtime; + return Err(CommandError::RuntimeError { mipsy_error: MipsyError::Runtime(RuntimeError::new( Error::InvalidSyscall { syscall: SYS14_READ, - reason: InvalidSyscallReason::Unimplemented, + reason: InvalidSyscallReason::Disabled, }, )), }); - - // let value = runtime_handler::sys14_read(verbose, args); - // self.runtime = Some(guard(value)); } + #[allow(unreachable_patterns)] // fall-through Write(_args, guard) => { - // TODO: implement file write for mipsy interactive frontend - let mut new_runtime = guard(-1); new_runtime.timeline_mut().pop_last_state(); self.runtime = new_runtime; + return Err(CommandError::RuntimeError { mipsy_error: MipsyError::Runtime(RuntimeError::new( Error::InvalidSyscall { syscall: SYS15_WRITE, - reason: InvalidSyscallReason::Unimplemented, + reason: InvalidSyscallReason::Disabled, }, )), }); - - // let value = runtime_handler::sys15_write(verbose, args); - // self.runtime = Some(guard(value)); } + #[allow(unreachable_patterns)] // fall-through Close(_args, guard) => { - // TODO: implement file close for mipsy interactive frontend - let mut new_runtime = guard(-1); new_runtime.timeline_mut().pop_last_state(); self.runtime = new_runtime; + return Err(CommandError::RuntimeError { mipsy_error: MipsyError::Runtime(RuntimeError::new( Error::InvalidSyscall { syscall: SYS16_CLOSE, - reason: InvalidSyscallReason::Unimplemented, + reason: InvalidSyscallReason::Disabled, }, )), }); - - // let value = runtime_handler::sys16_close(verbose, args); - // self.runtime = Some(guard(value)); } ExitStatus(args, new_runtime) => { self.runtime = new_runtime; diff --git a/crates/mipsy_interactive/src/interactive/runtime_handler.rs b/crates/mipsy_interactive/src/interactive/runtime_handler.rs index b63c7ec..7f13638 100644 --- a/crates/mipsy_interactive/src/interactive/runtime_handler.rs +++ b/crates/mipsy_interactive/src/interactive/runtime_handler.rs @@ -8,6 +8,7 @@ use std::{ use super::{prompt, TargetWatch}; use colored::*; +#[cfg(feature = "raw_io")] use mipsy_lib::runtime::{CloseArgs, OpenArgs, ReadArgs, WriteArgs}; use std::io::Write; use text_io::try_read; @@ -267,26 +268,49 @@ pub(crate) fn sys12_read_char(verbose: bool) -> u8 { character as u8 } -// TODO: implement file handling in mipsy interactive +#[cfg(feature = "raw_io")] +pub(crate) fn sys13_open(verbose: bool, args: OpenArgs) -> i32 { + use std::ffi::CString; -#[allow(unused)] -pub(crate) fn sys13_open(_verbose: bool, _args: OpenArgs) -> i32 { - todo!() + let name = CString::new(args.path).expect("runtime should have removed \\0"); + + if verbose { + prompt::syscall_nl( + 13, + format!("open({:?}, {}, {})", name, args.flags, args.mode), + ); + } + + unsafe { libc::open(name.as_ptr(), args.flags as i32, args.mode) } } -#[allow(unused)] -pub(crate) fn sys14_read(_verbose: bool, _args: ReadArgs) -> (i32, Vec) { - todo!() +#[cfg(feature = "raw_io")] +pub(crate) fn sys14_read(verbose: bool, args: ReadArgs) -> (i32, Vec) { + if verbose { + prompt::syscall_nl(14, format!("read({}, {})", args.fd, args.len)); + } + + let mut vec = vec![0u8; args.len as usize]; + let read = unsafe { libc::read(args.fd as i32, vec.as_mut_ptr().cast(), args.len as usize) }; + (read as i32, vec) } -#[allow(unused)] -pub(crate) fn sys15_write(_verbose: bool, _args: WriteArgs) -> i32 { - todo!() +#[cfg(feature = "raw_io")] +pub(crate) fn sys15_write(verbose: bool, args: WriteArgs) -> i32 { + if verbose { + prompt::syscall_nl(15, format!("write({}, {:?})", args.fd, args.buf)); + } + + unsafe { libc::write(args.fd as i32, args.buf.as_ptr().cast(), args.buf.len()) as i32 } } -#[allow(unused)] -pub(crate) fn sys16_close(_verbose: bool, _args: CloseArgs) -> i32 { - todo!() +#[cfg(feature = "raw_io")] +pub(crate) fn sys16_close(verbose: bool, args: CloseArgs) -> i32 { + if verbose { + prompt::syscall_nl(16, format!("close({})", args.fd)); + } + + unsafe { libc::close(args.fd as i32) as i32 } } pub(crate) fn sys17_exit_status(verbose: bool, val: i32) { diff --git a/crates/mipsy_lib/src/error/runtime/mod.rs b/crates/mipsy_lib/src/error/runtime/mod.rs index 0f7e9d2..2c81680 100644 --- a/crates/mipsy_lib/src/error/runtime/mod.rs +++ b/crates/mipsy_lib/src/error/runtime/mod.rs @@ -99,6 +99,7 @@ pub enum AlignmentRequirement { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub enum InvalidSyscallReason { Unimplemented, // Invalid becasue we don't have an implementation for it but it does exist + Disabled, // The syscall is disabled (compilation flag) Unknown, // Invalid because it doesn't exist to begin with } @@ -980,6 +981,13 @@ impl Error { syscall.to_string().bold() )); } + InvalidSyscallReason::Disabled => { + // $v0 is a valid value, but mipsy doesn't implement the operation + error.push_str(&format!( + "\nthe syscall number `{}` is disabled; try enabling some features\n", + syscall.to_string().bold() + )); + } InvalidSyscallReason::Unknown => { // $v0 is an invalid value, mips doesn't define this syscall error.push_str(&format!( diff --git a/crates/mipsy_parser/src/misc.rs b/crates/mipsy_parser/src/misc.rs index 599919d..e04cabd 100644 --- a/crates/mipsy_parser/src/misc.rs +++ b/crates/mipsy_parser/src/misc.rs @@ -52,8 +52,8 @@ fn leftover_tokens_strip_multispace(i: Span<'_>, file_name: Option>) -> } } -const IDENT_FIRST_CHAR: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"; -const IDENT_CONTD_CHARS: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789."; +const IDENT_FIRST_CHAR: &str = "$ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"; +const IDENT_CONTD_CHARS: &str = "$ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789."; pub fn escape_char(char: char) -> String { match char {