vax_disassembler/
opcode.rs

1//! # MACRO32 Opcodes
2
3use std::{
4    convert::TryFrom,
5    fmt::{self, Display, Formatter},
6    io::{Read, Write},
7};
8use vax_floating::{FFloating, DFloating, GFloating, HFloating};
9use crate::{
10    error::{Error, Result},
11    operand::{Operand},
12};
13
14/// # VAX Operand Access Type
15#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16pub enum AccessType {
17    /// # Address
18    ///
19    /// The address of the specified operand in the form of a longword is the actual instruction
20    /// operand. The specified operand is not accessed directly although the instruction may
21    /// subsequently use the address to access that operand.
22    Address,
23    /// # Branch
24    ///
25    /// No operand is accessed. The operand specifier itself is a branch displacement.
26    Branch,
27    /// # Modify
28    ///
29    /// The specified operand is read, potentially modified, and written. This is not done under a
30    /// memory interlock.
31    Modify,
32    /// # Read
33    ///
34    /// The specified operand is read only.
35    Read,
36    /// # Variable-length bit field base address
37    ///
38    /// This is the same as address access type except for register mode. In register mode, the
39    /// field is contained in register n designated by the operand specifier (or register n + 1
40    /// concatenated with register n). This access type is a special variant of the address access
41    /// type.
42    VariableBitField,
43    /// # Write
44    ///
45    /// The specified operand is written only.
46    Write,
47}
48
49macro_rules! define_data_functions {
50    ($(($desc: ident, $count: expr, $out: ty),)+) => {
51        /// # VAX Data Types
52        #[derive(Copy, Clone, Debug, PartialEq, Eq)]
53        pub enum DataType {
54            $(
55                #[doc = concat!("The VAX ", stringify!($desc), " data type. Reads into a Rust ",
56                    stringify!($out), " value.")]
57                $desc,
58            )+
59        }
60
61        /// # Vax Data Values
62        #[derive(Copy, Clone, Debug, PartialEq, Eq)]
63        pub enum DataValue {
64            $(
65                #[doc = concat!("The VAX ", stringify!($desc), " data type. Reads into a `",
66                    stringify!($out), "` value.")]
67                $desc($out),
68            )+
69        }
70
71        impl DataValue {
72            /// Return the [`DataType`] for this `DataValue`.
73            ///
74            /// # Examples
75            ///
76            /// ```rust
77            /// use vax_disassembler::opcode::{DataType, DataValue};
78            /// use vax_floating::{FFloating, DFloating, GFloating, HFloating};
79            ///
80            $(
81                #[doc = concat!("assert_eq!(DataValue::", stringify!($desc), "(", stringify!($out),
82                    "::MAX).data_type(), DataType::", stringify!($desc), ");")]
83            )+
84            /// ```
85            pub fn data_type(&self) -> DataType {
86                match self {
87                    $(
88                    DataValue::$desc(_) => DataType::$desc,
89                    )+
90                }
91            }
92        }
93
94        impl Display for DataValue {
95            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
96                match self {
97                    $(
98                    DataValue::$desc(value) => value.fmt(f),
99                    )+
100                }
101            }
102        }
103
104        /// Extends [`Read`] with a method to read a VAX data type ([`DataType`]) into the enum
105        /// [`DataValue`].
106        ///
107        /// # Examples
108        ///
109        /// ```rust
110        /// use vax_disassembler::{DataType, DataValue, ReadDataValue};
111        /// use std::io::Cursor;
112        ///
113        /// for quadword in (i64::MIN..=i64::MAX).step_by(u64::MAX as usize / 241) {
114        ///     let mut reader = Cursor::new(quadword.to_le_bytes());
115        ///     let data_value = reader.read_data_value(DataType::Quadword).unwrap();
116        ///     assert_eq!(data_value, DataValue::Quadword(quadword));
117        /// }
118        /// ```
119        pub trait ReadDataValue: Read {
120            /// Reads in a specified VAX data type ([`DataType`]) and returns the [`DataValue`].
121            ///
122            /// # Examples
123            ///
124            /// ```rust
125            /// use vax_disassembler::{DataType, DataValue, ReadDataValue};
126            /// use std::io::Cursor;
127            ///
128            /// for byte in i8::MIN..=i8::MAX {
129            ///     let mut reader = Cursor::new([byte as u8]);
130            ///     let data_value = reader.read_data_value(DataType::Byte).unwrap();
131            ///     assert_eq!(data_value, DataValue::Byte(byte));
132            /// }
133            /// for word in (i16::MIN..=i16::MAX).step_by(u16::MAX as usize / 257) {
134            ///     let mut reader = Cursor::new(word.to_le_bytes());
135            ///     let data_value = reader.read_data_value(DataType::Word).unwrap();
136            ///     assert_eq!(data_value, DataValue::Word(word));
137            /// }
138            /// for longword in (i32::MIN..=i32::MAX).step_by(u32::MAX as usize / 251) {
139            ///     let mut reader = Cursor::new(longword.to_le_bytes());
140            ///     let data_value = reader.read_data_value(DataType::Longword).unwrap();
141            ///     assert_eq!(data_value, DataValue::Longword(longword));
142            /// }
143            /// ```
144            fn read_data_value(&mut self, data_type: DataType) -> Result<DataValue> {
145                match data_type {
146                    $(
147                        DataType::$desc => {
148                            let mut buf = [0; $count];
149                            self.read_exact(&mut buf)?;
150                            Ok(DataValue::$desc(<$out>::from_le_bytes(buf)))
151                        }
152                    )+
153                }
154            }
155        }
156
157        /// All types that implement `Read` get methods defined in `ReadDataValue` for free.
158        impl<R: Read + ?Sized> ReadDataValue for R {}
159
160        /// Extends [`Write`] with a method to write a VAX data value ([`DataValue`]).
161        ///
162        /// # Examples
163        ///
164        /// ```rust
165        /// use vax_disassembler::{DataValue, WriteDataValue};
166        /// use std::io::Cursor;
167        ///
168        /// for quadword in (i64::MIN..=i64::MAX).step_by(u64::MAX as usize / 241) {
169        ///     let value = DataValue::Quadword(quadword);
170        ///     let mut writer = Vec::with_capacity(8);
171        ///     writer.write_data_value(value).unwrap();
172        ///     assert_eq!(&writer, &quadword.to_le_bytes());
173        /// }
174        /// ```
175        pub trait WriteDataValue: Write {
176            /// Writes in a specified VAX data value (`DataValue`).
177            ///
178            /// # Examples
179            ///
180            /// ```rust
181            /// use vax_disassembler::{DataType, DataValue, WriteDataValue};
182            /// use std::io::Cursor;
183            ///
184            /// for byte in i8::MIN..=i8::MAX {
185            ///     let value = DataValue::Byte(byte);
186            ///     let mut writer = Vec::with_capacity(1);
187            ///     writer.write_data_value(value).unwrap();
188            ///     assert_eq!(&writer, &[byte as u8]);
189            /// }
190            /// for word in (i16::MIN..=i16::MAX).step_by(u16::MAX as usize / 257) {
191            ///     let value = DataValue::Word(word);
192            ///     let mut writer = Vec::with_capacity(2);
193            ///     writer.write_data_value(value).unwrap();
194            ///     assert_eq!(&writer, &word.to_le_bytes());
195            /// }
196            /// for longword in (i32::MIN..=i32::MAX).step_by(u32::MAX as usize / 251) {
197            ///     let value = DataValue::Longword(longword);
198            ///     let mut writer = Vec::with_capacity(2);
199            ///     writer.write_data_value(value).unwrap();
200            ///     assert_eq!(&writer, &longword.to_le_bytes());
201            /// }
202            /// ```
203            fn write_data_value(&mut self, data_value: DataValue) -> Result<()> {
204                match data_value {
205                    $(
206                        DataValue::$desc(value) => {
207                            let mut buf = [0; $count];
208                            buf.copy_from_slice(&value.to_le_bytes());
209                            self.write_all(&buf)?;
210                            Ok(())
211                        }
212                    )+
213                }
214            }
215        }
216
217        /// All types that implement `Write` get methods defined in `WriteDataValue` for free.
218        impl<W: Write + ?Sized> WriteDataValue for W {}
219
220        #[cfg(test)]
221        pub(crate) mod data_value_tests {
222            use super::*;
223            use proptest::prelude::*;
224
225            pub fn data_value() -> impl Strategy<Value = DataValue> {
226                prop_oneof![
227                    $(
228                        any::<$out>().prop_map(DataValue::$desc),
229                    )+
230                ]
231            }
232        }
233    };
234}
235
236define_data_functions!{
237    (Byte, 1, i8),
238    (Word, 2, i16),
239    (Longword, 4, i32),
240    (Quadword, 8, i64),
241    (Octoword, 16, i128),
242    (Floating, 4, FFloating),
243    (Double, 8, DFloating),
244    (GFloating, 8, GFloating),
245    (HFloating, 16, HFloating),
246}
247
248macro_rules! operand_count {
249    ($_: ident) => {1};
250}
251
252macro_rules! access_type {
253    (ab) => {AccessType::Address};
254    (aw) => {AccessType::Address};
255    (al) => {AccessType::Address};
256    (aq) => {AccessType::Address};
257    (ao) => {AccessType::Address};
258    (af) => {AccessType::Address};
259    (ad) => {AccessType::Address};
260    (ag) => {AccessType::Address};
261    (ah) => {AccessType::Address};
262    (bb) => {AccessType::Branch};
263    (bw) => {AccessType::Branch};
264    (bl) => {AccessType::Branch};
265    (mb) => {AccessType::Modify};
266    (mw) => {AccessType::Modify};
267    (ml) => {AccessType::Modify};
268    (mq) => {AccessType::Modify};
269    (mo) => {AccessType::Modify};
270    (mf) => {AccessType::Modify};
271    (md) => {AccessType::Modify};
272    (mg) => {AccessType::Modify};
273    (mh) => {AccessType::Modify};
274    (rb) => {AccessType::Read};
275    (rw) => {AccessType::Read};
276    (rl) => {AccessType::Read};
277    (rq) => {AccessType::Read};
278    (ro) => {AccessType::Read};
279    (rf) => {AccessType::Read};
280    (rd) => {AccessType::Read};
281    (rg) => {AccessType::Read};
282    (rh) => {AccessType::Read};
283    (vb) => {AccessType::VariableBitField};
284    (wb) => {AccessType::Write};
285    (ww) => {AccessType::Write};
286    (wl) => {AccessType::Write};
287    (wq) => {AccessType::Write};
288    (wo) => {AccessType::Write};
289    (wf) => {AccessType::Write};
290    (wd) => {AccessType::Write};
291    (wg) => {AccessType::Write};
292    (wh) => {AccessType::Write};
293}
294
295macro_rules! data_type {
296    (ab) => {DataType::Byte};
297    (aw) => {DataType::Word};
298    (al) => {DataType::Longword};
299    (aq) => {DataType::Quadword};
300    (ao) => {DataType::Octoword};
301    (af) => {DataType::Floating};
302    (ad) => {DataType::Double};
303    (ag) => {DataType::GFloating};
304    (ah) => {DataType::HFloating};
305    (bb) => {DataType::Byte};
306    (bw) => {DataType::Word};
307    (bl) => {DataType::Longword};
308    (mb) => {DataType::Byte};
309    (mw) => {DataType::Word};
310    (ml) => {DataType::Longword};
311    (mq) => {DataType::Quadword};
312    (mo) => {DataType::Octoword};
313    (mf) => {DataType::Floating};
314    (md) => {DataType::Double};
315    (mg) => {DataType::GFloating};
316    (mh) => {DataType::HFloating};
317    (rb) => {DataType::Byte};
318    (rw) => {DataType::Word};
319    (rl) => {DataType::Longword};
320    (rq) => {DataType::Quadword};
321    (ro) => {DataType::Octoword};
322    (rf) => {DataType::Floating};
323    (rd) => {DataType::Double};
324    (rg) => {DataType::GFloating};
325    (rh) => {DataType::HFloating};
326    (vb) => {DataType::Byte};
327    (wb) => {DataType::Byte};
328    (ww) => {DataType::Word};
329    (wl) => {DataType::Longword};
330    (wq) => {DataType::Quadword};
331    (wo) => {DataType::Octoword};
332    (wf) => {DataType::Floating};
333    (wd) => {DataType::Double};
334    (wg) => {DataType::GFloating};
335    (wh) => {DataType::HFloating};
336}
337
338macro_rules! count_operands {
339    (()) => {0};
340    (($n1:ident.$s1:ident)) => {1};
341    (($n1:ident.$s1:ident, $n2:ident.$s2:ident, $n3:ident.$s3:ident, $n4:ident.$s4:ident, $n5:ident.$s5:ident, $n6:ident.$s6:ident)) => {6};
342    (($($_:ident.$spec: ident),+)) => {
343        (0 $( + operand_count!($spec) )+)
344    };
345}
346
347macro_rules! commafy {
348    ($s1: expr,) => { $s1 };
349    ($s1: expr, $($s2: expr,)+) => { concat!($s1, $(", ", $s2,)+) };
350}
351
352macro_rules! operands_string {
353    (()) => {""};
354    (($($name:ident.$spec: ident),+)) => {
355        commafy!(
356        $(
357            concat!(stringify!($name), ".", stringify!($spec)),
358        )+
359        )
360    };
361}
362
363macro_rules! operands_slice {
364    (()) => {&[]};
365    (($($_:ident.$spec: ident),+)) => {&[
366        $(
367            (access_type!($spec), data_type!($spec)),
368        )+
369    ]};
370}
371
372macro_rules! process_opcodes {
373    (opcodes: {$(($word: expr, $opcode: ident, $operands: tt, $desc: expr),)*},
374     duplicates: {$(($dup_word: expr, $dup_opcode: ident, $dup_operands: tt, $dup_desc: expr),)*}) => {
375        /// # VAX Opcode
376        #[derive(Copy, Clone, Debug, PartialEq, Eq)]
377        #[repr(u16)]
378        pub enum Opcode {
379            $(
380                #[doc = $desc]
381                ///
382                #[doc = concat!("Opcode ", stringify!($opcode), " = ", stringify!($word))]
383                $opcode,
384            )*
385            $(
386                #[doc = $dup_desc]
387                ///
388                #[doc = concat!("Opcode ", stringify!($dup_opcode), " = ", stringify!($dup_word))]
389                $dup_opcode,
390            )*
391        }
392
393        impl Opcode {
394            /// Return a reference to an array with the access type and data type of the operands
395            /// for a specific opcode.
396            ///
397            /// # Examples
398            ///
399            /// ```rust
400            /// use vax_disassembler::opcode::{AccessType, DataType, Opcode};
401            ///
402            /// assert_eq!(Opcode::HALT.operand_specs().len(), 0);
403            /// assert_eq!(Opcode::HALT.operand_specs(), &[]);
404            /// assert_eq!(Opcode::BRB.operand_specs().len(), 1);
405            /// assert_eq!(Opcode::BRB.operand_specs(), &[(AccessType::Branch, DataType::Byte)]);
406            /// assert_eq!(Opcode::MOVC5.operand_specs().len(), 5);
407            /// assert_eq!(Opcode::MOVC5.operand_specs(), &[
408            ///     (AccessType::Read, DataType::Word),
409            ///     (AccessType::Address, DataType::Byte),
410            ///     (AccessType::Read, DataType::Byte),
411            ///     (AccessType::Read, DataType::Word),
412            ///     (AccessType::Address, DataType::Byte),
413            /// ]);
414            /// ```
415            pub fn operand_specs(&self) -> &[(AccessType, DataType)] {
416                use Opcode::*;
417                match self {
418                    $(
419                        $opcode => operands_slice!($operands),
420                    )*
421                    $(
422                        $dup_opcode => operands_slice!($dup_operands),
423                    )*
424                }
425            }
426
427            /// Internal function used by Display to place the number of spaces after the opcode.
428            fn tab(&self) -> &'static str {
429                use Opcode::*;
430                static SPACES: [&'static str; 8] = [
431                    "        ", "       ", "      ", "     ", "    ", "   ", "  ", " ",
432                ];
433                match self {
434                    $(
435                        $opcode => SPACES[stringify!($opcode).len()],
436                    )+
437                    $(
438                        $dup_opcode => SPACES[stringify!($dup_opcode).len()],
439                    )+
440                }
441
442            }
443        }
444
445        impl TryFrom<u16> for Opcode {
446            type Error = Error;
447
448            /// Convert from a u16 value into the corresponding `Opcode`.
449            ///
450            /// The majority of MACRO32 opcodes are one-byte opcodes, and the u8 value as a u16
451            /// will translate into that opcode. If the first byte is 0xFD, 0xFE, or 0xFF, then it
452            /// is a two-byte opcode. In those cases, the little-endian interpretation of the two
453            /// byte is used for the conversion (i.e. [0xFD, 0x7C] (the CLRO opcode) is 0x7CFD).
454            ///
455            /// # Examples
456            ///
457            /// ```rust
458            /// use vax_disassembler::opcode::{Opcode};
459            /// use std::convert::TryFrom;
460            ///
461            /// assert_eq!(Opcode::try_from(1).unwrap(), Opcode::NOP);
462            /// assert_eq!(Opcode::try_from(3).unwrap(), Opcode::BPT);
463            /// assert_eq!(Opcode::try_from(0x7c).unwrap(), Opcode::CLRQ);
464            /// assert_eq!(Opcode::try_from(0x7cfd).unwrap(), Opcode::CLRO);
465            /// ```
466            fn try_from(value: u16) -> Result<Self> {
467                use $crate::opcode::Opcode::*;
468                match value {
469                    $(
470                        $word => Ok($opcode),
471                    )*
472                    other => Err(Error::InvalidOpcode(other))
473                }
474            }
475        }
476
477        impl From<&Opcode> for u16 {
478            /// Convert from a `Opcode` reference into the corresponding u16 value.
479            ///
480            /// # Examples
481            ///
482            /// ```rust
483            /// use vax_disassembler::opcode::{Opcode};
484            /// use std::convert::TryFrom;
485            ///
486            /// assert_eq!(u16::from(&Opcode::NOP), 1);
487            /// assert_eq!(u16::from(&Opcode::BPT), 3);
488            /// assert_eq!(u16::from(&Opcode::CLRQ), 0x7c);
489            /// assert_eq!(u16::from(&Opcode::CLRD), 0x7c);
490            /// assert_eq!(u16::from(&Opcode::CLRO), 0x7cfd);
491            /// assert_eq!(u16::from(&Opcode::CLRH), 0x7cfd);
492            /// ```
493            fn from(opcode: &Opcode) -> Self {
494                match opcode {
495                    $(
496                        Opcode::$opcode => $word,
497                    )+
498                    $(
499                        Opcode::$dup_opcode => $dup_word,
500                    )+
501                }
502            }
503        }
504
505        impl Display for Opcode {
506            /// Formats the value using the given formatter.
507            ///
508            /// # Examples
509            ///
510            /// ```rust
511            /// use vax_disassembler::opcode::{Instruction, Opcode};
512            /// use vax_disassembler::operand::{Operand, Register};
513            ///
514            /// 
515            /// assert_eq!(&format!("{}", Opcode::NOP), "NOP");
516            /// assert_eq!(&format!("{}", Opcode::MOVC5) , "MOVC5");
517            /// assert_eq!(&format!("{}", Opcode::PUSHAW) , "PUSHAW");
518            /// assert_eq!(&format!("{}", Opcode::CVTFB) , "CVTFB");
519            /// assert_eq!(&format!("{}", Opcode::INSQUE) , "INSQUE");
520            /// ```
521            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
522                use Opcode::*;
523                match self {
524                    $(
525                        $opcode => f.write_str(stringify!($opcode)),
526                    )+
527                    $(
528                        $dup_opcode => f.write_str(stringify!($dup_opcode)),
529                    )+
530                }
531            }
532        }
533
534        /// # VAX Instruction
535        ///
536        /// The VAX architecture has a variable-length instruction format. An instruction specifies
537        /// an operation and 0 to 6 operands. An operand specifier determines how an operand is
538        /// accessed. An operand specifier consists of an addressing mode specifier and, if needed,
539        /// a specifier extension, immediate data, or an address.
540        ///
541        /// ## Examples
542        ///
543        /// ```rust
544        /// use vax_disassembler::{
545        ///     Instruction,
546        ///     Operand,
547        ///     ReadMacro32,
548        ///     operand::Register,
549        /// };
550        /// use std::io::Cursor;
551        ///
552        /// let movc5_buf = [0x2C, 0x50, 0x61, 0x20, 0x52, 0x63];
553        /// let instruction = Cursor::new(movc5_buf).disassemble().unwrap();
554        /// assert_eq!(instruction, Instruction::MOVC5([
555        ///     Operand::Register(Register::R0),
556        ///     Operand::RegisterDeferred(Register::R1),
557        ///     Operand::Literal(0x20),
558        ///     Operand::Register(Register::R2),
559        ///     Operand::RegisterDeferred(Register::R3),
560        /// ]));
561        /// assert_eq!(&format!("{}", instruction), "MOVC5   R0, (R1), S^#32, R2, (R3)");
562        /// ```
563        #[derive(Copy, Clone, Debug, PartialEq, Eq)]
564        pub enum Instruction {
565            $(
566                #[doc = $desc]
567                ///
568                #[doc = concat!("Format: ", stringify!($opcode), "   ", operands_string!($operands))]
569                $opcode([Operand; count_operands!($operands)]),
570            )*
571            $(
572                /// ## Format:
573                #[doc = concat!("`opcode\t", operands_string!($dup_operands), "`")]
574                ///
575                /// ## Opcode:
576                #[doc = concat!("`", stringify!($dup_word), "\t", stringify!($dup_opcode), "\t", $dup_desc, "`")]
577                $dup_opcode([Operand; count_operands!($dup_operands)]),
578            )*
579        }
580
581        impl Instruction {
582            /// Return the [`Opcode`] for this `Instruction`.
583            ///
584            /// # Examples
585            /// ```rust
586            /// use vax_disassembler::opcode::{Instruction, Opcode};
587            /// use vax_disassembler::operand::{Operand, Register};
588            ///
589            /// assert_eq!(Instruction::RSB([]).opcode(), Opcode::RSB);
590            /// assert_eq!(Instruction::TSTL([Operand::Register(Register::R0)]).opcode(), Opcode::TSTL);
591            /// ```
592            pub fn opcode(&self) -> Opcode {
593                match self {
594                    $(
595                        Instruction::$opcode(_) => Opcode::$opcode,
596                    )+
597                    $(
598                        Instruction::$dup_opcode(_) => Opcode::$dup_opcode,
599                    )+
600                }
601            }
602
603            /// Return a slice with all of the operands for this instruction.
604            ///
605            /// # Examples
606            /// ```rust
607            /// use vax_disassembler::opcode::{Instruction, Opcode};
608            /// use vax_disassembler::operand::{Operand, Register};
609            ///
610            /// // MOVC5 R0, (R1), S^#^A/ /, R2, (R3)
611            /// let instruction = Instruction::MOVC5([
612            ///     Operand::Register(Register::R0),
613            ///     Operand::RegisterDeferred(Register::R1),
614            ///     Operand::Literal(b' '),
615            ///     Operand::Register(Register::R2),
616            ///     Operand::RegisterDeferred(Register::R3),
617            /// ]);
618            ///
619            /// let mut operands = instruction.operands().iter();
620            /// assert_eq!(operands.next().unwrap(), &Operand::Register(Register::R0));
621            /// assert_eq!(operands.next().unwrap(), &Operand::RegisterDeferred(Register::R1));
622            /// assert_eq!(operands.next().unwrap(), &Operand::Literal(b' '));
623            /// assert_eq!(operands.next().unwrap(), &Operand::Register(Register::R2));
624            /// assert_eq!(operands.next().unwrap(), &Operand::RegisterDeferred(Register::R3));
625            /// assert!(operands.next().is_none());
626            /// ```
627            pub fn operands(&self) -> &[Operand] {
628                match self {
629                    $(
630                        Instruction::$opcode(ref slice) => slice,
631                    )+
632                    $(
633                        Instruction::$dup_opcode(ref slice) => slice,
634                    )+
635                }
636            }
637
638            pub(crate) fn operands_mut(&mut self) -> &mut [Operand] {
639                match self {
640                    $(
641                        Instruction::$opcode(ref mut slice) => slice,
642                    )+
643                    $(
644                        Instruction::$dup_opcode(ref mut slice) => slice,
645                    )+
646                }
647            }
648        }
649
650        impl From<Opcode> for Instruction {
651            fn from(opcode: Opcode) -> Self {
652                match opcode {
653                    $(
654                        Opcode::$opcode => Instruction::$opcode([Operand::Undefined; count_operands!($operands)]),
655                    )+
656                    $(
657                        Opcode::$dup_opcode => Instruction::$dup_opcode([Operand::Undefined; count_operands!($dup_operands)]),
658                    )+
659                }
660            }
661        }
662
663        impl Display for Instruction {
664            /// Formats the value using the given formatter.
665            ///
666            /// # Examples
667            ///
668            /// ```rust
669            /// use vax_disassembler::opcode::{Instruction, Opcode};
670            /// use vax_disassembler::operand::{Operand, Register};
671            ///
672            /// assert_eq!(&format!("{}", Instruction::NOP([])), "NOP");
673            /// assert_eq!(
674            ///     &format!("{}", Instruction::MOVC5([
675            ///         Operand::Register(Register::R0),
676            ///         Operand::RegisterDeferred(Register::R1),
677            ///         Operand::Literal(b' '),
678            ///         Operand::Register(Register::R2),
679            ///         Operand::RegisterDeferred(Register::R3),
680            ///     ])),
681            ///     "MOVC5   R0, (R1), S^#32, R2, (R3)"
682            /// );
683            /// ```
684            fn fmt(&self, f: &mut Formatter) -> fmt::Result {
685                let opcode = self.opcode();
686                write!(f, "{:?}", opcode)?;
687                let mut comma = opcode.tab();
688                for operand in self.operands() {
689                    write!(f, "{}{}", comma, operand)?;
690                    comma = ", ";
691                }
692                Ok(())
693            }
694        }
695
696        #[cfg(test)]
697        mod opcode_tests {
698            use super::*;
699
700            #[test]
701            pub fn verify_u16_to_opcode() {
702                $(
703                    assert_eq!(Opcode::try_from($word).unwrap(), Opcode::$opcode);
704                )*
705            }
706
707            #[test]
708            pub fn verify_duplicate_opcodes() {
709                $(
710                    let opcode_u16 = u16::from(&Opcode::$dup_opcode);
711                    assert_ne!(Opcode::$dup_opcode, Opcode::try_from(opcode_u16).unwrap());
712                )+
713            }
714        }
715    };
716}
717
718// This calls the process_opcodes with the VAX opcode list.
719include!("process_opcodes.in");
720
721#[cfg(test)]
722mod tests {
723    #[test]
724    fn verify_u16_to_opcode() {
725        super::opcode_tests::verify_u16_to_opcode();
726    }
727
728}