rust decoder POC

This commit is contained in:
tali 2023-12-17 16:21:35 -05:00
parent 80f5ab654b
commit 18cc66697e
5 changed files with 282 additions and 1 deletions

58
Cargo.lock generated
View File

@ -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"

View File

@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2021"
[dependencies]
thiserror = "1.0"

182
native/src/decode.rs Normal file
View File

@ -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<u8, DecodeError> {
if bs.len() < i + 1 {
return Err(DecodeError::DataTrunc(i + 1));
}
Ok(bs[i])
}
fn read_u16(bs: &[u8], i: usize) -> Result<u32, DecodeError> {
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<u32, DecodeError> {
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<Opr, DecodeError> {
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<Reg, DecodeError> {
Ok(Reg(((w >> k) & 0x3ff) as u16))
}
fn decode_const(w: u32, k: u32) -> Result<ConstIdx, DecodeError> {
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<ArgV, DecodeError> {
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<Ins, DecodeError>;
fn next(&mut self) -> Option<Self::Item> {
let bs = self.0;
if bs.is_empty() {
return None;
}
Some(decode_one(bs).map(|(ins, len)| {
self.0 = &bs[len..];
ins
}))
}
}

View File

@ -1 +1,5 @@
pub mod decode;
pub mod opcodes;
pub use decode::{decode, DecodeError};
pub use opcodes::Ins;

View File

@ -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();
}
}
}