#![allow(clippy::module_name_repetitions)]
use crate::inst::{InstSet, Op};
use std::{
collections::BTreeMap,
fmt::{Debug, Display, Formatter, Result as FmtResult},
io as stdio,
str::FromStr,
};
#[allow(clippy::needless_pass_by_value, clippy::enum_glob_use)]
pub mod arith;
#[allow(clippy::needless_pass_by_value, clippy::enum_glob_use)]
pub mod io;
#[allow(clippy::needless_pass_by_value, clippy::enum_glob_use)]
pub mod mov;
#[allow(clippy::needless_pass_by_value, clippy::enum_glob_use)]
pub mod cmp;
#[allow(clippy::needless_pass_by_value, clippy::enum_glob_use)]
pub mod bitman;
#[allow(clippy::enum_glob_use)]
mod error;
mod memory;
mod debug;
#[allow(clippy::enum_glob_use)]
mod inst;
pub use error::{PasmError, PasmResult, Source};
pub use memory::Memory;
pub use inst::{ExecFunc, ExecInst};
pub use debug::DebugInfo;
pub struct Io {
pub read: Box<dyn stdio::Read>,
pub write: Box<dyn stdio::Write>,
}
#[macro_export]
macro_rules! make_io {
($read:expr, $write:expr) => {{
$crate::exec::Io {
read: Box::new($read),
write: Box::new($write),
}
}};
}
impl Debug for Io {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str("<struct Io>")
}
}
impl Default for Io {
fn default() -> Self {
Self {
read: Box::new(stdio::stdin()),
write: Box::new(stdio::stdout()),
}
}
}
#[derive(Debug, Default)]
pub struct Context {
pub cmp: bool,
pub mar: usize,
pub acc: usize,
pub ix: usize,
pub flow_override_reg: bool,
pub mem: Memory,
pub ret: usize,
pub gprs: [usize; 30],
pub end: bool,
pub io: Io,
}
impl Context {
pub fn new(mem: Memory) -> Self {
Self {
mem,
..Self::default()
}
}
pub fn with_io(mem: Memory, io: Io) -> Self {
Self {
mem,
io,
..Self::default()
}
}
#[inline]
pub fn override_flow_control(&mut self) {
self.flow_override_reg = true;
}
#[inline]
pub fn get_mut_register(&mut self, op: &Op) -> &mut usize {
match op {
Op::Acc => &mut self.acc,
Op::Ix => &mut self.ix,
Op::Ar => &mut self.ret,
Op::Gpr(x) => &mut self.gprs[*x],
_ => unreachable!(),
}
}
#[inline]
pub fn get_register(&self, op: &Op) -> usize {
match op {
Op::Acc => self.acc,
Op::Ix => self.ix,
Op::Ar => self.ret,
Op::Gpr(x) => self.gprs[*x],
_ => unreachable!(),
}
}
#[inline]
pub fn modify(&mut self, op: &Op, f: impl Fn(&mut usize)) -> PasmResult {
match op {
Op::Addr(x) => f(self.mem.get_mut(x)?),
op if op.is_register() => f(self.get_mut_register(op)),
_ => unreachable!(),
}
Ok(())
}
}
impl Display for Context {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str("Context {\n")?;
writeln!(f, "{:>6}: {}", "mar", self.mar)?;
writeln!(f, "{:>6}: {}", "acc", self.acc)?;
writeln!(f, "{:>6}: {}", "ix", self.ix)?;
writeln!(f, "{:>6}: {}", "cmp", self.cmp)?;
write!(f, "{:>6}: [", "gprs")?;
for (idx, val) in self.gprs.iter().enumerate() {
if idx == self.gprs.len() - 1 {
writeln!(f, "r{idx} = {val}]")?;
} else {
write!(f, "r{idx} = {val}, ")?;
}
}
writeln!(f, "{:>6}: Memory {{", "mem")?;
for (addr, entry) in self.mem.iter() {
writeln!(f, "{addr:>8}: {entry},")?;
}
writeln!(f, "{:>6}}}", "")?;
f.write_str("}")
}
}
pub type ExTree = BTreeMap<usize, ExecInst>;
pub struct Executor {
pub debug_info: DebugInfo,
pub source: Source,
pub prog: ExTree,
pub ctx: Context,
count: u64,
}
pub enum Status {
Complete,
Continue,
Error(PasmError),
}
impl Executor {
pub fn new(
source: impl Into<Source>,
prog: ExTree,
ctx: Context,
debug_info: DebugInfo,
) -> Self {
Self {
debug_info,
source: source.into(),
prog,
ctx,
count: 0,
}
}
pub fn step<T>(&mut self) -> Status
where
T: InstSet,
<T as FromStr>::Err: Display,
{
if self.ctx.mar == self.prog.len() || self.ctx.end {
Status::Complete
} else {
self.count += 1;
let inst = if let Some(inst) = self.prog.get(&self.ctx.mar) {
inst
} else {
panic!("Unable to fetch instruction. Please report this as a bug with full debug logs attached.")
};
trace!(
"Executing instruction {} {}",
T::from_func_ptr(inst.func).unwrap_or_else(|msg| panic!("{msg}")),
inst.op
);
match (inst.func)(&mut self.ctx, &inst.op) {
Ok(_) => {
if self.ctx.flow_override_reg {
self.ctx.flow_override_reg = false;
} else {
self.ctx.mar += 1;
}
Status::Continue
}
Err(e) => Status::Error(e),
}
}
}
pub fn exec<T>(&mut self)
where
T: InstSet,
<T as FromStr>::Err: Display,
{
let err = loop {
match self.step::<T>() {
Status::Complete => break None,
Status::Continue => continue,
Status::Error(e) => break Some(e),
}
};
if let Some(e) = err {
self.source
.handle_err(&mut self.ctx.io.write, &e, self.ctx.mar)
.unwrap();
} else {
info!("Total instructions executed: {}", self.count);
}
}
pub fn display<T>(&self) -> Result<String, <T as FromStr>::Err>
where
T: InstSet,
<T as FromStr>::Err: Display,
{
use std::fmt::Write;
let mut s = String::new();
s.reserve(self.prog.len() * 15);
let _ = writeln!(s, "Executor {{");
for (addr, ExecInst { op, func }) in &self.prog {
let _ = writeln!(s, "{addr:>6}: {func} {op}", func = T::from_func_ptr(*func)?);
}
s.push('}');
Ok(s)
}
}
impl Display for Executor {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str("Executor {")?;
for (addr, ExecInst { op, .. }) in &self.prog {
writeln!(f, "{addr:>6}: {op}", op = op)?;
}
f.write_str("}")
}
}
impl Debug for Executor {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
f.debug_struct("Executor")
.field("source", &self.source)
.field(
"prog",
&self
.prog
.iter()
.map(|(addr, ExecInst { op, .. })| (addr, op))
.collect::<Vec<_>>(),
)
.field("ctx", &self.ctx)
.field("count", &self.count)
.finish()
}
}
#[cfg(test)]
#[test]
fn exec() {
use crate::parse;
use std::collections::BTreeMap;
let prog: BTreeMap<usize, ExecInst> = BTreeMap::from(
[
(0, ExecInst::new(arith::inc, "202".into())),
(1, ExecInst::new(arith::add, "203,201".into())),
(2, ExecInst::new(cmp::cmp, "203,204".into())),
(3, ExecInst::new(cmp::jpn, "0".into())),
(4, ExecInst::new(mov::ldd, "202".into())),
(5, ExecInst::new(io::end, "".into())),
],
);
let mem: BTreeMap<usize, usize> =
BTreeMap::from([(200, 0), (201, 5), (202, 0), (203, 0), (204, 15)]);
let mut exec = Executor::new(
"None",
prog,
Context::new(Memory::new(mem)),
DebugInfo::default(),
);
exec.exec::<parse::DefaultSet>();
assert_eq!(exec.ctx.acc, 3);
}