diff --git a/Cargo.lock b/Cargo.lock index 2fd2d66..1fd40f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,64 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + [[package]] name = "spicei" version = "0.1.0" +dependencies = [ + "thiserror", +] + +[[package]] +name = "syn" +version = "2.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f11c217e1416d6f036b870f14e0413d480dbf28edbee1f877abaf0206af43bb7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01742297787513b79cf8e29d1056ede1313e2420b7b3b15d0a768b4921f549df" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/native/Cargo.toml b/native/Cargo.toml index ed315d2..f45f30d 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +thiserror = "1.0" diff --git a/native/src/decode.rs b/native/src/decode.rs new file mode 100644 index 0000000..b95e021 --- /dev/null +++ b/native/src/decode.rs @@ -0,0 +1,182 @@ +use thiserror::Error; + +use super::opcodes::*; + +#[derive(Debug, Error)] +pub enum DecodeError { + #[error("data truncated (len < {0})")] + DataTrunc(usize), + #[error("invalid len: {0}")] + Len(u8), + #[error("invalid instruction: {0:x}")] + Ins(u8), + #[error("invalid operator: {0:x}")] + Opr(u8), + #[error("invalid operand: {0:x}")] + Arg(u32), +} + +fn read_u8(bs: &[u8], i: usize) -> Result { + if bs.len() < i + 1 { + return Err(DecodeError::DataTrunc(i + 1)); + } + Ok(bs[i]) +} + +fn read_u16(bs: &[u8], i: usize) -> Result { + if bs.len() < i + 2 { + return Err(DecodeError::DataTrunc(i + 2)); + } + Ok(u16::from_le_bytes(bs[i..i + 2].try_into().unwrap()) as u32) +} + +fn read_u32(bs: &[u8], i: usize) -> Result { + if bs.len() < i + 4 { + return Err(DecodeError::DataTrunc(i + 4)); + } + Ok(u32::from_le_bytes(bs[i..i + 4].try_into().unwrap())) +} + +fn decode_opr(b: u8) -> Result { + match b { + 0x02 => Ok(Opr::MOV), + 0x12 => Ok(Opr::CON), + 0x22 => Ok(Opr::ADD), + 0x32 => Ok(Opr::SUB), + 0x42 => Ok(Opr::MUL), + 0x52 => Ok(Opr::DIV), + 0x62 => Ok(Opr::MOD), + 0x72 => Ok(Opr::NEG), + 0x82 => Ok(Opr::NOT), + 0xa2 => Ok(Opr::Cmp(Cmp::EQ)), + 0xb2 => Ok(Opr::Cmp(Cmp::NE)), + 0xc2 => Ok(Opr::Cmp(Cmp::LT)), + 0xd2 => Ok(Opr::Cmp(Cmp::GE)), + 0xe2 => Ok(Opr::Cmp(Cmp::GT)), + 0xf2 => Ok(Opr::Cmp(Cmp::LE)), + _ => Err(DecodeError::Opr(b)), + } +} + +fn decode_reg(w: u32, k: u32) -> Result { + Ok(Reg(((w >> k) & 0x3ff) as u16)) +} + +fn decode_const(w: u32, k: u32) -> Result { + if w & (0x400 << k) == 0 { + return Err(DecodeError::Arg(w)); + } + Ok(ConstIdx(((w >> k) & 0x3ff) as u16)) +} + +fn decode_argv(w: u32, k: u32) -> Result { + if w & (0x400 << k) != 0 { + decode_const(w, k).map(ArgV::K) + } else { + decode_reg(w, k).map(ArgV::R) + } +} + +pub fn decode_one(bs: &[u8]) -> Result<(Ins, usize), DecodeError> { + let b0 = read_u8(bs, 0)?; + + let ins; + match b0 & 0b111 { + 0b010 => { + let i; + if b0 & 0b1000 != 0 { + match decode_opr(b0 & !0b1000)? { + Opr::Cmp(cmp) => { + i = InsRV::Br(cmp); + } + _ => return Err(DecodeError::Opr(b0)), + } + } else { + i = InsRV::Op(decode_opr(b0)?); + }; + + let w = read_u32(bs, 0)?; + let a = decode_reg(w, 8)?; + let b = decode_argv(w, 18)?; + ins = Ins::RV(i, a, b); + } + + 0b011 => { + #[allow(unreachable_code, unused_variables)] + match b0 { + 0x3 => { + let (i, a, b) = todo!("LV"); + ins = Ins::LV(i, a, b); + } + + _ => { + let (i, a, b) = todo!("RL"); + ins = Ins::RL(i, a, b); + } + }; + } + + 0b101 => { + let i = match b0 & 0b11111 { + 0x05 => InsV::RET, + 0x0d => InsV::BTR, + _ => return Err(DecodeError::Ins(b0)), + }; + + let w = read_u16(bs, 0)?; + let a = decode_argv(w as u32, 8)?; + ins = Ins::V(i, a); + } + + 0b110 => { + let i = match b0 { + 0x06 => InsO::JMP, + _ => return Err(DecodeError::Ins(b0)), + }; + + let w = read_u32(bs, 0)?; + let o = (w as i32) >> 8; + ins = Ins::O(i, o); + } + + _ => return Err(DecodeError::Ins(b0)), + }; + + let len = match b0 & 0b11 { + 0 => return Err(DecodeError::Len(0)), + x => (x * 2) as usize, + }; + + Ok((ins, len)) +} + +/// Iterator adapter for [`decode_one`]. +#[derive(Clone)] +pub struct Decode<'a>(&'a [u8]); + +/// Decode all instructions from bytecode data. +pub fn decode(bs: &[u8]) -> Decode<'_> { + Decode(bs) +} + +impl Decode<'_> { + pub fn inner(&self) -> &[u8] { + self.0 + } +} + +impl Iterator for Decode<'_> { + type Item = Result; + + fn next(&mut self) -> Option { + let bs = self.0; + if bs.is_empty() { + return None; + } + + Some(decode_one(bs).map(|(ins, len)| { + self.0 = &bs[len..]; + ins + })) + } +} diff --git a/native/src/lib.rs b/native/src/lib.rs index 8189a4e..d343d02 100644 --- a/native/src/lib.rs +++ b/native/src/lib.rs @@ -1 +1,5 @@ +pub mod decode; pub mod opcodes; + +pub use decode::{decode, DecodeError}; +pub use opcodes::Ins; diff --git a/native/src/main.rs b/native/src/main.rs index b49dfb4..9a31b18 100644 --- a/native/src/main.rs +++ b/native/src/main.rs @@ -1,2 +1,38 @@ +use std::io::{BufReader, Read}; +use thiserror::Error; -fn main() {} +use spicei::*; + +#[derive(Debug, Error)] +enum Error { + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Decode(#[from] DecodeError), +} + +fn entry() -> Result<(), Error> { + let mut bytes = vec![]; + BufReader::new(std::io::stdin()).read_to_end(&mut bytes)?; + + for ins in decode(&bytes) { + let ins = ins?; + println!("{ins}"); + } + + Ok(()) +} + +fn main() { + use std::error::Error; + + if let Err(err) = entry() { + eprintln!("error: {err}"); + + let mut src = err.source(); + while let Some(err) = src { + println!("- {err}"); + src = err.source(); + } + } +}