rust decoder POC
This commit is contained in:
parent
80f5ab654b
commit
18cc66697e
|
@ -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"
|
||||
|
|
|
@ -4,3 +4,4 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[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 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