vax_disassembler/
lib.rs

1#![doc = include_str!("../README.md")]
2#![forbid(future_incompatible)]
3#![warn(missing_docs, missing_debug_implementations, bare_trait_objects)]
4
5pub mod error;
6pub mod opcode;
7pub mod operand;
8
9use std::io::Read;
10pub use crate::{
11    error::{Error, Result},
12    opcode::{
13        AccessType,
14        DataType,
15        DataValue,
16        ReadDataValue,
17        WriteDataValue,
18        Instruction,
19        Opcode,
20    },
21    operand::{IndexedOperand, Operand, ReadOperand, Register},
22};
23
24/// Extends [`Read`] with a method to read a VAX instruction ([`Instruction`]).
25///
26/// # Examples
27///
28/// ```rust
29/// use vax_disassembler::ReadMacro32;
30/// use std::io::Cursor;
31///
32/// macro_rules! disassemble {
33///     ($buf: expr, $text: expr) => {
34///         let instruction = Cursor::new($buf).disassemble().unwrap();
35///         assert_eq!(&format!("{}", instruction), $text);
36///     };
37/// }
38///
39/// disassemble!([0x05], "RSB");
40/// disassemble!([0xE9, 0x50, 0x15], "BLBC    R0, 21");
41/// disassemble!([0xDE, 0x44, 0xC4, 0x00, 0xFE, 0x55], "MOVAL   W^-512(R4)[R4], R5");
42/// disassemble!([0xCB, 0x8F, 0xFF, 0xFF, 0xFF, 0x00, 0x50, 0x53], "BICL3   #16777215, R0, R3");
43/// disassemble!([0x28, 0x8F, 0x00, 0x02, 0x61, 0xC1, 0x00, 0xFE], "MOVC3   #512, (R1), W^-512(R1)");
44/// ```
45pub trait ReadMacro32: Read {
46    /// Read VAX MACRO32 machine code from a reader and disassemble it into a single
47    /// [`Instruction`].
48    ///
49    /// # Examples
50    ///
51    /// ```rust
52    /// use vax_disassembler::ReadMacro32;
53    /// use std::io::Cursor;
54    ///
55    /// macro_rules! disassemble {
56    ///     ($buf: expr, $text: expr) => {
57    ///         let instruction = Cursor::new($buf).disassemble().unwrap();
58    ///         assert_eq!(&format!("{}", instruction), $text);
59    ///     };
60    /// }
61    ///
62    /// disassemble!([0x01], "NOP");
63    /// disassemble!([0x30, 0x00, 0xFE], "BSBW    -512");
64    /// disassemble!([0xDE, 0x44, 0x64, 0x55], "MOVAL   (R4)[R4], R5");
65    /// disassemble!([0xEF, 0x0C, 0x14, 0x56, 0x57], "EXTZV   S^#12, S^#20, R6, R7");
66    /// disassemble!([0x2C, 0x50, 0x61, 0x20, 0x52, 0x63], "MOVC5   R0, (R1), S^#32, R2, (R3)");
67    /// ```
68    fn disassemble(&mut self) -> Result<Instruction> {
69        let mut buf = [0; 2];
70        self.read_exact(&mut buf[0..1])?;
71        let opcode = match buf[0] {
72            0xFD..=0xFF => {
73                self.read_exact(&mut buf[1..2])?;
74                let opcode = u16::from_le_bytes(buf);
75                Opcode::try_from(opcode)?
76            }
77            opcode => Opcode::try_from(opcode as u16)?,
78        };
79        let mut instruction = Instruction::from(opcode);
80        let operands = instruction.operands_mut();
81        for (index, (access, size)) in opcode.operand_specs().iter().enumerate() {
82            operands[index] = self.read_operand(*access, *size)?;
83        }
84        Ok(instruction)
85    }
86}
87
88/// All types that implement `Read` get methods defined in `ReadMacro32` for free.
89impl<R: Read + ?Sized> ReadMacro32 for R {}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    macro_rules! test_disassembly {
96        ($input: expr, $output: expr) => {
97            let mut reader = std::io::Cursor::new($input);
98            let output = reader.disassemble().unwrap();
99            assert_eq!(&format!("{}", output), $output);
100        }
101    }
102
103    #[test]
104    fn simple_tests() {
105        test_disassembly!([0x00], "HALT");
106        test_disassembly!([0x01], "NOP");
107        test_disassembly!([0x02], "REI");
108        test_disassembly!([0x03], "BPT");
109        test_disassembly!([0x05], "RSB");
110        test_disassembly!([0x10, 20], "BSBB    20");
111        test_disassembly!([0x11, 128], "BRB     -128");
112    }
113}