rust decoder POC
This commit is contained in:
parent
80f5ab654b
commit
18cc66697e
|
@ -2,6 +2,64 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
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]]
|
[[package]]
|
||||||
name = "spicei"
|
name = "spicei"
|
||||||
version = "0.1.0"
|
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"
|
||||||
|
|
|
@ -4,3 +4,4 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
thiserror = "1.0"
|
||||||
|
|
|
@ -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
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,5 @@
|
||||||
|
pub mod decode;
|
||||||
pub mod opcodes;
|
pub mod opcodes;
|
||||||
|
|
||||||
|
pub use decode::{decode, DecodeError};
|
||||||
|
pub use opcodes::Ins;
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue