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}