fuel_asm/
macros.rs

1//! # The `impl_instructions!` macro
2//!
3//! The heart of this crate's implementation is the private `impl_instructions!` macro.
4//! This macro is used to generate the `Instruction` and `Opcode` types along with their
5//! implementations.
6//!
7//! The intention is to allow for having a single source of truth from which each of the
8//! instruction-related types and implementations are derived.
9//!
10//! Its usage looks like this:
11//!
12//! ```rust,ignore
13//! impl_instructions! {
14//!     "Adds two registers."
15//!     0x10 ADD add [RegId RegId RegId]
16//!     "Bitwise ANDs two registers."
17//!     0x11 AND and [RegId RegId RegId]
18//!     // ...
19//! }
20//! ```
21//!
22//! Each instruction's row includes:
23//!
24//! - A short docstring.
25//! - The Opcode byte value.
26//! - An uppercase identifier (for generating variants and types).
27//! - A lowercase identifier (for generating the shorthand instruction constructor).
28//! - The instruction layout (for the `new` and `unpack` functions).
29//!
30//! The following sections describe each of the items that are derived from the
31//! `impl_instructions!` table in more detail.
32//!
33//! ## The `Opcode` enum
34//!
35//! Represents the bytecode portion of an instruction.
36//!
37//! ```rust,ignore
38//! /// Solely the opcode portion of an instruction represented as a single byte.
39//! #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
40//! #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
41//! #[repr(u8)]
42//! pub enum Opcode {
43//!     /// Adds two registers.
44//!     ADD = 0x10,
45//!     /// Bitwise ANDs two registers.
46//!     AND = 0x11,
47//!     // ...
48//! }
49//! ```
50//!
51//! A `TryFrom<u8>` implementation is also provided, producing an `Err(InvalidOpcode)` in
52//! the case that the byte represents a reserved or undefined value.
53//!
54//! ```rust
55//! # use fuel_asm::{InvalidOpcode, Opcode};
56//! assert_eq!(Opcode::try_from(0x10), Ok(Opcode::ADD));
57//! assert_eq!(Opcode::try_from(0x11), Ok(Opcode::AND));
58//! assert_eq!(Opcode::try_from(0), Err(InvalidOpcode));
59//! ```
60//!
61//! ## The `Instruction` enum
62//!
63//! Represents a single, full instruction, discriminated by its `Opcode`.
64//!
65//! ```rust,ignore
66//! /// Representation of a single instruction for the interpreter.
67//! ///
68//! /// The opcode is represented in the tag (variant), or may be retrieved in the form of an
69//! /// `Opcode` byte using the `opcode` method.
70//! ///
71//! /// The register and immediate data associated with the instruction is represented within
72//! /// an inner unit type wrapper around the 3 remaining bytes.
73//! #[derive(Clone, Copy, Eq, Hash, PartialEq)]
74//! #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
75//! pub enum Instruction {
76//!     /// Adds two registers.
77//!     ADD(op::ADD),
78//!     /// Bitwise ANDs two registers.
79//!     AND(op::AND),
80//!     // ...
81//! }
82//! ```
83//!
84//! The `From<Instruction> for u32` (aka `RawInstruction`) and `TryFrom<u32> for
85//! Instruction` implementations can be found in the crate root.
86//!
87//! ## A unique unit type per operation
88//!
89//! In order to reduce the likelihood of misusing unrelated register IDs or immediate
90//! values, we generate a unique unit type for each type of operation (i.e instruction
91//! variant) and guard access to the relevant register IDs and immediate values behind
92//! each type's unique methods.
93//!
94//! These unique operation types are generated as follows within a dedicated `op` module:
95//!
96//! ```rust,ignore
97//! pub mod op {
98//!     //! Definitions and implementations for each unique instruction type, one for each
99//!     //! unique `Opcode` variant.
100//!
101//!     // A unique type for each operation.
102//!
103//!     /// Adds two registers.
104//!     pub struct ADD([u8; 3]);
105//!
106//!     /// Bitwise ANDs two registers.
107//!     pub struct AND([u8; 3]);
108//!
109//!     // ...
110//!
111//!     // An implementation for each unique type.
112//!
113//!     impl ADD {
114//!         pub const OPCODE: Opcode = Opcode::ADD;
115//!
116//!         /// Construct the instruction from its parts.
117//!         pub fn new(ra: RegId, rb: RegId, rc: RegId) -> Self {
118//!             Self(pack::bytes_from_ra_rb_rc(ra, rb, rc))
119//!         }
120//!
121//!         /// Convert the instruction into its parts.
122//!         pub fn unpack(self) -> (RegId, RegId, RegId) {
123//!             unpack::ra_rb_rc_from_bytes(self.0)
124//!         }
125//!     }
126//!
127//!     impl AND {
128//!         // ...
129//!     }
130//!
131//!     // ...
132//!
133//!     // A short-hand `Instruction` constructor for each operation to make it easier to
134//!     // hand-write assembly for tests and benchmarking. As these constructors are public and
135//!     // accept literal values, we check that the values are within range.
136//!
137//!     /// Adds two registers.
138//!     pub fn add(ra: u8, rb: u8, rc: u8) -> Instruction {
139//!         ADD::new(check_reg_id(ra), check_reg_id(rb), check_reg_id(rc)).into()
140//!     }
141//!
142//!     /// Bitwise ANDs two registers.
143//!     pub fn and(ra: u8, rb: u8, rc: u8) -> Instruction {
144//!         AND::new(check_reg_id(ra), check_reg_id(rb), check_reg_id(rc)).into()
145//!     }
146//!
147//!     // ...
148//! };
149//! ```
150//!
151//! ### Instruction Layout
152//!
153//! The function signatures of the `new` and `unpack` functions are derived from the
154//! instruction's data layout described in the `impl_instructions!` table.
155//!
156//! For example, the `unpack` method for `ADD` looks like this:
157//!
158//! ```rust,ignore
159//! // 0x10 ADD add [RegId RegId RegId]
160//! pub fn unpack(self) -> (RegId, RegId, RegId)
161//! ```
162//!
163//! While the `unpack` method for `ADDI` looks like this:
164//!
165//! ```rust,ignore
166//! // 0x50 ADDI addi [RegId RegId Imm12]
167//! pub fn unpack(self) -> (RegId, RegId, Imm12)
168//! ```
169//!
170//! ### Shorthand Constructors
171//!
172//! The shorthand instruction constructors (e.g. `add`, `and`, etc) are specifically
173//! designed to make it easier to handwrite assembly for tests or benchmarking. Unlike the
174//! `$OP::new` constructors which require typed register ID or immediate inputs, the
175//! instruction constructors allow for constructing `Instruction`s from convenient literal
176//! value inputs. E.g.
177//!
178//! ```rust
179//! use fuel_asm::{op, Instruction};
180//!
181//! // A sample program to perform ecrecover
182//! let program: Vec<Instruction> = vec![
183//!     op::move_(0x10, 0x01),     // set r[0x10] := $one
184//!     op::slli(0x20, 0x10, 5),   // set r[0x20] := `r[0x10] << 5 == 32`
185//!     op::slli(0x21, 0x10, 6),   // set r[0x21] := `r[0x10] << 6 == 64`
186//!     op::aloc(0x21),            // alloc `r[0x21] == 64` to the heap
187//!     op::addi(0x10, 0x07, 1),   // set r[0x10] := `$hp + 1` (allocated heap)
188//!     op::move_(0x11, 0x04),     // set r[0x11] := $ssp
189//!     op::add(0x12, 0x04, 0x20), // set r[0x12] := `$ssp + r[0x20]`
190//!     op::eck1(0x10, 0x11, 0x12),// recover public key in memory[r[0x10], 64]
191//!     op::ret(0x01),             // return `1`
192//! ];
193//! ```
194
195// Generate a shorthand free function named after the $op for constructing an
196// `Instruction`.
197macro_rules! op_constructor {
198    ($doc:literal $Op:ident $op:ident[$ra:ident : RegId]) => {
199        #[doc = $doc]
200        pub fn $op<A: CheckRegId>($ra: A) -> Instruction {
201            $Op::new($ra.check()).into()
202        }
203
204        #[cfg(feature = "typescript")]
205        const _: () = {
206            use super::*;
207
208            #[wasm_bindgen::prelude::wasm_bindgen]
209            #[doc = $doc]
210            pub fn $op($ra: u8) -> typescript::Instruction {
211                crate::op::$op($ra).into()
212            }
213        };
214    };
215    ($doc:literal $Op:ident $op:ident[$ra:ident : RegId $rb:ident : RegId]) => {
216        #[doc = $doc]
217        pub fn $op<A: CheckRegId, B: CheckRegId>($ra: A, $rb: B) -> Instruction {
218            $Op::new($ra.check(), $rb.check()).into()
219        }
220
221        #[cfg(feature = "typescript")]
222        const _: () = {
223            use super::*;
224
225            #[wasm_bindgen::prelude::wasm_bindgen]
226            #[doc = $doc]
227            pub fn $op($ra: u8, $rb: u8) -> typescript::Instruction {
228                crate::op::$op($ra, $rb).into()
229            }
230        };
231    };
232    (
233        $doc:literal
234        $Op:ident
235        $op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId]
236    ) => {
237        #[doc = $doc]
238        pub fn $op<A: CheckRegId, B: CheckRegId, C: CheckRegId>(
239            $ra: A,
240            $rb: B,
241            $rc: C,
242        ) -> Instruction {
243            $Op::new($ra.check(), $rb.check(), $rc.check()).into()
244        }
245
246        #[cfg(feature = "typescript")]
247        const _: () = {
248            use super::*;
249
250            #[wasm_bindgen::prelude::wasm_bindgen]
251            #[doc = $doc]
252            pub fn $op($ra: u8, $rb: u8, $rc: u8) -> typescript::Instruction {
253                crate::op::$op($ra, $rb, $rc).into()
254            }
255        };
256    };
257    (
258        $doc:literal
259        $Op:ident
260        $op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $rd:ident : RegId]
261    ) => {
262        #[doc = $doc]
263        pub fn $op<A: CheckRegId, B: CheckRegId, C: CheckRegId, D: CheckRegId>(
264            $ra: A,
265            $rb: B,
266            $rc: C,
267            $rd: D,
268        ) -> Instruction {
269            $Op::new($ra.check(), $rb.check(), $rc.check(), $rd.check()).into()
270        }
271
272        #[cfg(feature = "typescript")]
273        const _: () = {
274            use super::*;
275
276            #[wasm_bindgen::prelude::wasm_bindgen]
277            #[doc = $doc]
278            pub fn $op($ra: u8, $rb: u8, $rc: u8, $rd: u8) -> typescript::Instruction {
279                crate::op::$op($ra, $rb, $rc, $rd).into()
280            }
281        };
282    };
283    (
284        $doc:literal
285        $Op:ident
286        $op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $imm:ident : Imm06]
287    ) => {
288        #[doc = $doc]
289        pub fn $op<A: CheckRegId, B: CheckRegId, C: CheckRegId>(
290            $ra: A,
291            $rb: B,
292            $rc: C,
293            $imm: u8,
294        ) -> Instruction {
295            $Op::new($ra.check(), $rb.check(), $rc.check(), check_imm06($imm)).into()
296        }
297
298        #[cfg(feature = "typescript")]
299        const _: () = {
300            use super::*;
301
302            #[wasm_bindgen::prelude::wasm_bindgen]
303            #[doc = $doc]
304            pub fn $op($ra: u8, $rb: u8, $rc: u8, $imm: u8) -> typescript::Instruction {
305                crate::op::$op($ra, $rb, $rc, $imm).into()
306            }
307        };
308    };
309    (
310        $doc:literal
311        $Op:ident
312        $op:ident[$ra:ident : RegId $rb:ident : RegId $imm:ident : Imm12]
313    ) => {
314        #[doc = $doc]
315        pub fn $op<A: CheckRegId, B: CheckRegId>(
316            $ra: A,
317            $rb: B,
318            $imm: u16,
319        ) -> Instruction {
320            $Op::new($ra.check(), $rb.check(), check_imm12($imm)).into()
321        }
322
323        #[cfg(feature = "typescript")]
324        const _: () = {
325            use super::*;
326
327            #[wasm_bindgen::prelude::wasm_bindgen]
328            #[doc = $doc]
329            pub fn $op($ra: u8, $rb: u8, $imm: u16) -> typescript::Instruction {
330                crate::op::$op($ra, $rb, $imm).into()
331            }
332        };
333    };
334    ($doc:literal $Op:ident $op:ident[$ra:ident : RegId $imm:ident : Imm18]) => {
335        #[doc = $doc]
336        pub fn $op<A: CheckRegId>($ra: A, $imm: u32) -> Instruction {
337            $Op::new($ra.check(), check_imm18($imm)).into()
338        }
339
340        #[cfg(feature = "typescript")]
341        const _: () = {
342            use super::*;
343
344            #[wasm_bindgen::prelude::wasm_bindgen]
345            #[doc = $doc]
346            pub fn $op($ra: u8, $imm: u32) -> typescript::Instruction {
347                crate::op::$op($ra, $imm).into()
348            }
349        };
350    };
351    ($doc:literal $Op:ident $op:ident[$imm:ident : Imm24]) => {
352        #[doc = $doc]
353        pub fn $op($imm: u32) -> Instruction {
354            $Op::new(check_imm24($imm)).into()
355        }
356
357        #[cfg(feature = "typescript")]
358        const _: () = {
359            use super::*;
360
361            #[wasm_bindgen::prelude::wasm_bindgen]
362            #[doc = $doc]
363            pub fn $op($imm: u32) -> typescript::Instruction {
364                crate::op::$op($imm).into()
365            }
366        };
367    };
368    ($doc:literal $Op:ident $op:ident[]) => {
369        #[doc = $doc]
370        pub fn $op() -> Instruction {
371            $Op::new().into()
372        }
373
374        #[cfg(feature = "typescript")]
375        const _: () = {
376            use super::*;
377
378            #[wasm_bindgen::prelude::wasm_bindgen]
379            #[doc = $doc]
380            pub fn $op() -> typescript::Instruction {
381                crate::op::$op().into()
382            }
383        };
384    };
385}
386
387// Generate approriate `new` constructor for the instruction
388macro_rules! op_new {
389    // Generate a constructor based on the field layout.
390    ($Op:ident $ra:ident : RegId) => {
391        impl $Op {
392            /// Construct the instruction from its parts.
393            pub fn new($ra: RegId) -> Self {
394                Self(pack::bytes_from_ra($ra))
395            }
396        }
397
398        #[cfg(feature = "typescript")]
399        #[wasm_bindgen::prelude::wasm_bindgen]
400        impl $Op {
401            #[wasm_bindgen(constructor)]
402            /// Construct the instruction from its parts.
403            pub fn new_typescript($ra: RegId) -> Self {
404                Self::new($ra)
405            }
406        }
407    };
408    ($Op:ident $ra:ident : RegId $rb:ident : RegId) => {
409        impl $Op {
410            /// Construct the instruction from its parts.
411            pub fn new($ra: RegId, $rb: RegId) -> Self {
412                Self(pack::bytes_from_ra_rb($ra, $rb))
413            }
414        }
415
416        #[cfg(feature = "typescript")]
417        #[wasm_bindgen::prelude::wasm_bindgen]
418        impl $Op {
419            #[wasm_bindgen(constructor)]
420            /// Construct the instruction from its parts.
421            pub fn new_typescript($ra: RegId, $rb: RegId) -> Self {
422                Self::new($ra, $rb)
423            }
424        }
425    };
426    ($Op:ident $ra:ident : RegId $rb:ident : RegId $rc:ident : RegId) => {
427        impl $Op {
428            /// Construct the instruction from its parts.
429            pub fn new($ra: RegId, $rb: RegId, $rc: RegId) -> Self {
430                Self(pack::bytes_from_ra_rb_rc($ra, $rb, $rc))
431            }
432        }
433
434        #[cfg(feature = "typescript")]
435        #[wasm_bindgen::prelude::wasm_bindgen]
436        impl $Op {
437            #[wasm_bindgen(constructor)]
438            /// Construct the instruction from its parts.
439            pub fn new_typescript($ra: RegId, $rb: RegId, $rc: RegId) -> Self {
440                Self::new($ra, $rb, $rc)
441            }
442        }
443    };
444    (
445        $Op:ident $ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $rd:ident : RegId
446    ) => {
447        impl $Op {
448            /// Construct the instruction from its parts.
449            pub fn new($ra: RegId, $rb: RegId, $rc: RegId, $rd: RegId) -> Self {
450                Self(pack::bytes_from_ra_rb_rc_rd($ra, $rb, $rc, $rd))
451            }
452        }
453
454        #[cfg(feature = "typescript")]
455        #[wasm_bindgen::prelude::wasm_bindgen]
456        impl $Op {
457            #[wasm_bindgen(constructor)]
458            /// Construct the instruction from its parts.
459            pub fn new_typescript(
460                $ra: RegId,
461                $rb: RegId,
462                $rc: RegId,
463                $rd: RegId,
464            ) -> Self {
465                Self::new($ra, $rb, $rc, $rd)
466            }
467        }
468    };
469    (
470        $Op:ident
471        $ra:ident : RegId
472        $rb:ident : RegId
473        $rc:ident : RegId
474        $imm:ident : Imm06
475    ) => {
476        impl $Op {
477            /// Construct the instruction from its parts.
478            pub fn new($ra: RegId, $rb: RegId, $rc: RegId, $imm: Imm06) -> Self {
479                Self(pack::bytes_from_ra_rb_rc_imm06($ra, $rb, $rc, $imm))
480            }
481        }
482
483        #[cfg(feature = "typescript")]
484        #[wasm_bindgen::prelude::wasm_bindgen]
485        impl $Op {
486            #[wasm_bindgen(constructor)]
487            /// Construct the instruction from its parts.
488            pub fn new_typescript(
489                $ra: RegId,
490                $rb: RegId,
491                $rc: RegId,
492                $imm: Imm06,
493            ) -> Self {
494                Self::new($ra, $rb, $rc, $imm)
495            }
496        }
497    };
498    ($Op:ident $ra:ident : RegId $rb:ident : RegId $imm:ident : Imm12) => {
499        impl $Op {
500            /// Construct the instruction from its parts.
501            pub fn new($ra: RegId, $rb: RegId, $imm: Imm12) -> Self {
502                Self(pack::bytes_from_ra_rb_imm12($ra, $rb, $imm))
503            }
504        }
505
506        #[cfg(feature = "typescript")]
507        #[wasm_bindgen::prelude::wasm_bindgen]
508        impl $Op {
509            #[wasm_bindgen(constructor)]
510            /// Construct the instruction from its parts.
511            pub fn new_typescript($ra: RegId, $rb: RegId, $imm: Imm12) -> Self {
512                Self::new($ra, $rb, $imm)
513            }
514        }
515    };
516    ($Op:ident $ra:ident : RegId $imm:ident : Imm18) => {
517        impl $Op {
518            /// Construct the instruction from its parts.
519            pub fn new($ra: RegId, $imm: Imm18) -> Self {
520                Self(pack::bytes_from_ra_imm18($ra, $imm))
521            }
522        }
523
524        #[cfg(feature = "typescript")]
525        #[wasm_bindgen::prelude::wasm_bindgen]
526        impl $Op {
527            #[wasm_bindgen(constructor)]
528            /// Construct the instruction from its parts.
529            pub fn new_typescript($ra: RegId, $imm: Imm18) -> Self {
530                Self::new($ra, $imm)
531            }
532        }
533    };
534    ($Op:ident $imm:ident : Imm24) => {
535        impl $Op {
536            /// Construct the instruction from its parts.
537            pub fn new($imm: Imm24) -> Self {
538                Self(pack::bytes_from_imm24($imm))
539            }
540        }
541
542        #[cfg(feature = "typescript")]
543        #[wasm_bindgen::prelude::wasm_bindgen]
544        impl $Op {
545            #[wasm_bindgen(constructor)]
546            /// Construct the instruction from its parts.
547            pub fn new_typescript($imm: Imm24) -> Self {
548                Self::new($imm)
549            }
550        }
551    };
552    ($Op:ident) => {
553        impl $Op {
554            /// Construct the instruction.
555            #[allow(clippy::new_without_default)]
556            pub fn new() -> Self {
557                Self([0; 3])
558            }
559        }
560
561        #[cfg(feature = "typescript")]
562        #[wasm_bindgen::prelude::wasm_bindgen]
563        impl $Op {
564            #[wasm_bindgen(constructor)]
565            /// Construct the instruction.
566            #[allow(clippy::new_without_default)]
567            pub fn new_typescript() -> Self {
568                Self::new()
569            }
570        }
571    };
572}
573
574// Generate an accessor method for each field. Recurse based on layout.
575macro_rules! op_accessors {
576    ($Op:ident $ra:ident: RegId) => {
577        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
578        impl $Op {
579            /// Access the ID for register A.
580            pub fn ra(&self) -> RegId {
581                unpack::ra_from_bytes(self.0)
582            }
583        }
584    };
585    ($Op:ident $ra:ident: RegId $rb:ident: RegId) => {
586        op_accessors!($Op ra: RegId);
587
588        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
589        impl $Op {
590            /// Access the ID for register B.
591            pub fn rb(&self) -> RegId {
592                unpack::rb_from_bytes(self.0)
593            }
594        }
595    };
596    ($Op:ident $ra:ident: RegId $rb:ident: RegId $rc:ident: RegId) => {
597        op_accessors!($Op $ra: RegId $rb: RegId);
598
599        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
600        impl $Op {
601            /// Access the ID for register C.
602            pub fn rc(&self) -> RegId {
603                unpack::rc_from_bytes(self.0)
604            }
605        }
606    };
607    ($Op:ident $ra:ident: RegId $rb:ident: RegId $rc:ident: RegId $rd:ident: RegId) => {
608        op_accessors!($Op $ra: RegId $rb: RegId $rc: RegId);
609
610        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
611        impl $Op {
612            /// Access the ID for register D.
613            pub fn rd(&self) -> RegId {
614                unpack::rd_from_bytes(self.0)
615            }
616        }
617    };
618    ($Op:ident $ra:ident: RegId $rb:ident: RegId $rc:ident: RegId $imm:ident: Imm06) => {
619        op_accessors!($Op $ra: RegId rb: RegId $rc: RegId);
620
621        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
622        impl $Op {
623            /// Access the 6-bit immediate value.
624            pub fn imm06(&self) -> Imm06 {
625                unpack::imm06_from_bytes(self.0)
626            }
627        }
628    };
629    ($Op:ident $ra:ident: RegId $rb:ident: RegId $imm:ident: Imm12) => {
630        op_accessors!($Op $ra: RegId $rb: RegId);
631
632        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
633        impl $Op {
634            /// Access the 12-bit immediate value.
635            pub fn imm12(&self) -> Imm12 {
636                unpack::imm12_from_bytes(self.0)
637            }
638        }
639    };
640    ($Op:ident $ra:ident: RegId $imm:ident: Imm18) => {
641        op_accessors!($Op $ra: RegId);
642
643        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
644        impl $Op {
645            /// Access the 18-bit immediate value.
646            pub fn imm18(&self) -> Imm18 {
647                unpack::imm18_from_bytes(self.0)
648            }
649        }
650    };
651    ($Op:ident $ra:ident: Imm24) => {
652        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
653        impl $Op {
654            /// Access the 24-bit immediate value.
655            pub fn imm24(&self) -> Imm24 {
656                unpack::imm24_from_bytes(self.0)
657            }
658        }
659    };
660    ($Op:ident) => {};
661}
662
663// Generate a method for converting the instruction into its parts.
664macro_rules! op_unpack {
665    (RegId) => {
666        /// Convert the instruction into its parts.
667        pub fn unpack(self) -> RegId {
668            unpack::ra_from_bytes(self.0)
669        }
670    };
671    (RegId RegId) => {
672        /// Convert the instruction into its parts.
673        pub fn unpack(self) -> (RegId, RegId) {
674            unpack::ra_rb_from_bytes(self.0)
675        }
676    };
677    (RegId RegId RegId) => {
678        /// Convert the instruction into its parts.
679        pub fn unpack(self) -> (RegId, RegId, RegId) {
680            unpack::ra_rb_rc_from_bytes(self.0)
681        }
682    };
683    (RegId RegId RegId RegId) => {
684        /// Convert the instruction into its parts.
685        pub fn unpack(self) -> (RegId, RegId, RegId, RegId) {
686            unpack::ra_rb_rc_rd_from_bytes(self.0)
687        }
688    };
689    (RegId RegId RegId Imm06) => {
690        /// Convert the instruction into its parts.
691        pub fn unpack(self) -> (RegId, RegId, RegId, Imm06) {
692            unpack::ra_rb_rc_imm06_from_bytes(self.0)
693        }
694    };
695    (RegId RegId Imm12) => {
696        /// Convert the instruction into its parts.
697        pub fn unpack(self) -> (RegId, RegId, Imm12) {
698            unpack::ra_rb_imm12_from_bytes(self.0)
699        }
700    };
701    (RegId Imm18) => {
702        /// Convert the instruction into its parts.
703        pub fn unpack(self) -> (RegId, Imm18) {
704            unpack::ra_imm18_from_bytes(self.0)
705        }
706    };
707    (Imm24) => {
708        /// Convert the instruction into its parts.
709        pub fn unpack(self) -> Imm24 {
710            unpack::imm24_from_bytes(self.0)
711        }
712    };
713    () => {};
714}
715
716// Generate a method for checking that the reserved part of the
717// instruction is zero. This is private, as invalid instructions
718// cannot be constructed outside this crate.
719macro_rules! op_reserved_part {
720    (RegId) => {
721        pub(crate) fn reserved_part_is_zero(self) -> bool {
722            let (_, imm) = unpack::ra_imm18_from_bytes(self.0);
723            imm.0 == 0
724        }
725    };
726    (RegId RegId) => {
727        pub(crate) fn reserved_part_is_zero(self) -> bool {
728            let (_, _, imm) = unpack::ra_rb_imm12_from_bytes(self.0);
729            imm.0 == 0
730        }
731    };
732    (RegId RegId RegId) => {
733        pub(crate) fn reserved_part_is_zero(self) -> bool {
734            let (_, _, _, imm) = unpack::ra_rb_rc_imm06_from_bytes(self.0);
735            imm.0 == 0
736        }
737    };
738    (RegId RegId RegId RegId) => {
739        pub(crate) fn reserved_part_is_zero(self) -> bool {
740            true
741        }
742    };
743    (RegId RegId RegId Imm06) => {
744        pub(crate) fn reserved_part_is_zero(self) -> bool {
745            true
746        }
747    };
748    (RegId RegId Imm12) => {
749        pub(crate) fn reserved_part_is_zero(self) -> bool {
750            true
751        }
752    };
753    (RegId Imm18) => {
754        pub(crate) fn reserved_part_is_zero(self) -> bool {
755            true
756        }
757    };
758    (Imm24) => {
759        pub(crate) fn reserved_part_is_zero(self) -> bool {
760            true
761        }
762    };
763    () => {
764        pub(crate) fn reserved_part_is_zero(self) -> bool {
765            self.0 == [0; 3]
766        }
767    };
768}
769
770// Generate a private fn for use within the `Instruction::reg_ids` implementation.
771macro_rules! op_reg_ids {
772    (RegId) => {
773        pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
774            let ra = self.unpack();
775            [Some(ra), None, None, None]
776        }
777    };
778    (RegId RegId) => {
779        pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
780            let (ra, rb) = self.unpack();
781            [Some(ra), Some(rb), None, None]
782        }
783    };
784    (RegId RegId RegId) => {
785        pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
786            let (ra, rb, rc) = self.unpack();
787            [Some(ra), Some(rb), Some(rc), None]
788        }
789    };
790    (RegId RegId RegId RegId) => {
791        pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
792            let (ra, rb, rc, rd) = self.unpack();
793            [Some(ra), Some(rb), Some(rc), Some(rd)]
794        }
795    };
796    (RegId RegId RegId Imm06) => {
797        pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
798            let (ra, rb, rc, _) = self.unpack();
799            [Some(ra), Some(rb), Some(rc), None]
800        }
801    };
802    (RegId RegId Imm12) => {
803        pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
804            let (ra, rb, _) = self.unpack();
805            [Some(ra), Some(rb), None, None]
806        }
807    };
808    (RegId Imm18) => {
809        pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
810            let (ra, _) = self.unpack();
811            [Some(ra), None, None, None]
812        }
813    };
814    ($($rest:tt)*) => {
815        pub(super) fn reg_ids(&self) -> [Option<RegId>; 4] {
816            [None; 4]
817        }
818    };
819}
820
821// Generate test constructors that can be used to generate instructions from non-matching
822// input.
823#[cfg(test)]
824macro_rules! op_test_construct_fn {
825    (RegId) => {
826        /// Construct the instruction from all possible raw fields, ignoring inapplicable
827        /// ones.
828        pub fn test_construct(
829            ra: RegId,
830            _rb: RegId,
831            _rc: RegId,
832            _rd: RegId,
833            _imm: u32,
834        ) -> Self {
835            Self(pack::bytes_from_ra(ra))
836        }
837    };
838    (RegId RegId) => {
839        /// Construct the instruction from all possible raw fields, ignoring inapplicable
840        /// ones.
841        pub fn test_construct(
842            ra: RegId,
843            rb: RegId,
844            _rc: RegId,
845            _rd: RegId,
846            _imm: u32,
847        ) -> Self {
848            Self(pack::bytes_from_ra_rb(ra, rb))
849        }
850    };
851    (RegId RegId RegId) => {
852        /// Construct the instruction from all possible raw fields, ignoring inapplicable
853        /// ones.
854        pub fn test_construct(
855            ra: RegId,
856            rb: RegId,
857            rc: RegId,
858            _rd: RegId,
859            _imm: u32,
860        ) -> Self {
861            Self(pack::bytes_from_ra_rb_rc(ra, rb, rc))
862        }
863    };
864    (RegId RegId RegId RegId) => {
865        /// Construct the instruction from all possible raw fields, ignoring inapplicable
866        /// ones.
867        pub fn test_construct(
868            ra: RegId,
869            rb: RegId,
870            rc: RegId,
871            rd: RegId,
872            _imm: u32,
873        ) -> Self {
874            Self(pack::bytes_from_ra_rb_rc_rd(ra, rb, rc, rd))
875        }
876    };
877    (RegId RegId RegId Imm06) => {
878        /// Construct the instruction from all possible raw fields, ignoring inapplicable
879        /// ones.
880        pub fn test_construct(
881            ra: RegId,
882            rb: RegId,
883            rc: RegId,
884            _rd: RegId,
885            imm: u32,
886        ) -> Self {
887            Self(pack::bytes_from_ra_rb_rc_imm06(
888                ra,
889                rb,
890                rc,
891                Imm06::from(imm as u8),
892            ))
893        }
894    };
895    (RegId RegId Imm12) => {
896        /// Construct the instruction from all possible raw fields, ignoring inapplicable
897        /// ones.
898        pub fn test_construct(
899            ra: RegId,
900            rb: RegId,
901            _rc: RegId,
902            _rd: RegId,
903            imm: u32,
904        ) -> Self {
905            Self(pack::bytes_from_ra_rb_imm12(
906                ra,
907                rb,
908                Imm12::from(imm as u16),
909            ))
910        }
911    };
912    (RegId Imm18) => {
913        /// Construct the instruction from all possible raw fields, ignoring inapplicable
914        /// ones.
915        pub fn test_construct(
916            ra: RegId,
917            _rb: RegId,
918            _rc: RegId,
919            _rd: RegId,
920            imm: u32,
921        ) -> Self {
922            Self(pack::bytes_from_ra_imm18(ra, Imm18::from(imm)))
923        }
924    };
925    (Imm24) => {
926        /// Construct the instruction from all possible raw fields, ignoring inapplicable
927        /// ones.
928        pub fn test_construct(
929            _ra: RegId,
930            _rb: RegId,
931            _rc: RegId,
932            _rd: RegId,
933            imm: u32,
934        ) -> Self {
935            Self(pack::bytes_from_imm24(Imm24::from(imm)))
936        }
937    };
938    () => {
939        /// Construct the instruction from all possible raw fields, ignoring inapplicable
940        /// ones.
941        #[allow(clippy::new_without_default)]
942        pub fn test_construct(
943            _ra: RegId,
944            _rb: RegId,
945            _rc: RegId,
946            _rd: RegId,
947            _imm: u32,
948        ) -> Self {
949            Self([0; 3])
950        }
951    };
952}
953
954// Debug implementations for each instruction.
955macro_rules! op_debug_fmt {
956    ($Op:ident[$ra:ident : RegId]) => {
957        fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
958            let ra = self.unpack();
959            f.debug_struct(stringify!($Op))
960                .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
961                .finish()
962        }
963    };
964    ($Op:ident[$ra:ident : RegId $rb:ident : RegId]) => {
965        fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
966            let (ra, rb) = self.unpack();
967            f.debug_struct(stringify!($Op))
968                .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
969                .field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb)))
970                .finish()
971        }
972    };
973    ($Op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId]) => {
974        fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
975            let (ra, rb, rc) = self.unpack();
976            f.debug_struct(stringify!($Op))
977                .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
978                .field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb)))
979                .field(stringify!($rc), &format_args!("{:#02x}", u8::from(rc)))
980                .finish()
981        }
982    };
983    (
984        $Op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $rd:ident : RegId]
985    ) => {
986        fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
987            let (ra, rb, rc, rd) = self.unpack();
988            f.debug_struct(stringify!($Op))
989                .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
990                .field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb)))
991                .field(stringify!($rc), &format_args!("{:#02x}", u8::from(rc)))
992                .field(stringify!($rd), &format_args!("{:#02x}", u8::from(rd)))
993                .finish()
994        }
995    };
996    (
997        $Op:ident[$ra:ident : RegId $rb:ident : RegId $rc:ident : RegId $imm:ident : Imm06]
998    ) => {
999        fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1000            let (ra, rb, rc, imm) = self.unpack();
1001            f.debug_struct(stringify!($Op))
1002                .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
1003                .field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb)))
1004                .field(stringify!($rc), &format_args!("{:#02x}", u8::from(rc)))
1005                .field(stringify!($imm), &u8::from(imm))
1006                .finish()
1007        }
1008    };
1009    ($Op:ident[$ra:ident : RegId $rb:ident : RegId $imm:ident : Imm12]) => {
1010        fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1011            let (ra, rb, imm) = self.unpack();
1012            f.debug_struct(stringify!($Op))
1013                .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
1014                .field(stringify!($rb), &format_args!("{:#02x}", u8::from(rb)))
1015                .field(stringify!($imm), &u16::from(imm))
1016                .finish()
1017        }
1018    };
1019    ($Op:ident[$ra:ident : RegId $imm:ident : Imm18]) => {
1020        fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1021            let (ra, imm) = self.unpack();
1022            f.debug_struct(stringify!($Op))
1023                .field(stringify!($ra), &format_args!("{:#02x}", u8::from(ra)))
1024                .field(stringify!($imm), &u32::from(imm))
1025                .finish()
1026        }
1027    };
1028    ($Op:ident[$imm:ident : Imm24]) => {
1029        fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1030            let imm = self.unpack();
1031            f.debug_struct(stringify!($Op))
1032                .field(stringify!($imm), &u32::from(imm))
1033                .finish()
1034        }
1035    };
1036    ($Op:ident[]) => {
1037        fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1038            f.debug_struct(stringify!($Op)).finish()
1039        }
1040    };
1041}
1042
1043// Recursively declares a unique struct for each opcode.
1044macro_rules! decl_op_struct {
1045    ($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => {
1046        #[doc = $doc]
1047        #[derive(Clone, Copy, Eq, Hash, PartialEq)]
1048        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1049        #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)]
1050        pub struct $Op(pub (super) [u8; 3]);
1051        decl_op_struct!($($rest)*);
1052    };
1053    () => {};
1054}
1055
1056/// This macro is intentionaly private. See the module-level documentation for a thorough
1057/// explanation of how this macro works.
1058macro_rules! impl_instructions {
1059    // Define the `Opcode` enum.
1060    (decl_opcode_enum $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => {
1061        /// Solely the opcode portion of an instruction represented as a single byte.
1062        #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1063        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1064        #[repr(u8)]
1065        pub enum Opcode {
1066            $(
1067                #[doc = $doc]
1068                $Op = $ix,
1069            )*
1070        }
1071    };
1072
1073    // Define the `Instruction` enum.
1074    (decl_instruction_enum $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => {
1075        /// Representation of a single instruction for the interpreter.
1076        ///
1077        /// The opcode is represented in the tag (variant), or may be retrieved in the form of an
1078        /// `Opcode` byte using the `opcode` method.
1079        ///
1080        /// The register and immediate data associated with the instruction is represented within
1081        /// an inner unit type wrapper around the 3 remaining bytes.
1082        #[derive(Clone, Copy, Eq, Hash, PartialEq)]
1083        #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1084        pub enum Instruction {
1085            $(
1086                #[doc = $doc]
1087                $Op(op::$Op),
1088            )*
1089        }
1090    };
1091
1092    // Recursively generate a test constructor for each opcode
1093    (impl_opcode_test_construct $doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => {
1094        #[cfg(test)]
1095        #[allow(clippy::cast_possible_truncation)]
1096        impl crate::_op::$Op {
1097            op_test_construct_fn!($($field)*);
1098        }
1099        impl_instructions!(impl_opcode_test_construct $($rest)*);
1100    };
1101    (impl_opcode_test_construct) => {};
1102
1103    // Recursively generate a test constructor for each opcode
1104    (tests $doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => {
1105        op_test!($Op $op [$($field)*]);
1106        impl_instructions!(tests $($rest)*);
1107    };
1108    (tests) => {};
1109
1110    // Implement constructors and accessors for register and immediate values.
1111    (impl_op $doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*] $($rest:tt)*) => {
1112        impl $Op {
1113            /// The associated 8-bit Opcode value.
1114            pub const OPCODE: Opcode = Opcode::$Op;
1115        }
1116
1117        op_new!($Op $($fname: $field)*);
1118        op_accessors!($Op $($fname: $field)*);
1119
1120        impl $Op {
1121            op_unpack!($($field)*);
1122            op_reserved_part!($($field)*);
1123            op_reg_ids!($($field)*);
1124        }
1125
1126        op_constructor!($doc $Op $op [$($fname: $field)*]);
1127
1128        impl From<$Op> for [u8; 3] {
1129            fn from($Op(arr): $Op) -> Self {
1130                arr
1131            }
1132        }
1133
1134        impl From<$Op> for [u8; 4] {
1135            fn from($Op([a, b, c]): $Op) -> Self {
1136                [$Op::OPCODE as u8, a, b, c]
1137            }
1138        }
1139
1140        impl From<$Op> for u32 {
1141            fn from(op: $Op) -> Self {
1142                u32::from_be_bytes(op.into())
1143            }
1144        }
1145
1146        impl From<$Op> for Instruction {
1147            fn from(op: $Op) -> Self {
1148                Instruction::$Op(op)
1149            }
1150        }
1151
1152        #[cfg(feature = "typescript")]
1153        impl From<$Op> for typescript::Instruction {
1154            fn from(opcode: $Op) -> Self {
1155                typescript::Instruction::new(opcode.into())
1156            }
1157        }
1158
1159        impl core::fmt::Debug for $Op {
1160            op_debug_fmt!($Op [$($fname: $field)*]);
1161        }
1162
1163        impl_instructions!(impl_op $($rest)*);
1164    };
1165    (impl_op) => {};
1166
1167    // Implement functions for all opcode variants
1168    (impl_opcode $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => {
1169        impl core::convert::TryFrom<u8> for Opcode {
1170            type Error = InvalidOpcode;
1171            fn try_from(u: u8) -> Result<Self, Self::Error> {
1172                match u {
1173                    $(
1174                        $ix => Ok(Opcode::$Op),
1175                    )*
1176                    _ => Err(InvalidOpcode),
1177                }
1178            }
1179        }
1180
1181        impl Opcode {
1182            /// Construct the instruction from all possible raw fields, ignoring inapplicable ones.
1183            #[cfg(test)]
1184            pub fn test_construct(self, ra: RegId, rb: RegId, rc: RegId, rd: RegId, imm: u32) -> Instruction {
1185                match self {
1186                    $(
1187                        Self::$Op => Instruction::$Op(crate::_op::$Op::test_construct(ra, rb, rc, rd, imm)),
1188                    )*
1189                }
1190            }
1191        }
1192    };
1193
1194    // Implement accessors for register and immediate values.
1195    (impl_instruction $($doc:literal $ix:literal $Op:ident $op:ident [$($fname:ident: $field:ident)*])*) => {
1196        impl Instruction {
1197            /// This instruction's opcode.
1198            pub fn opcode(&self) -> Opcode {
1199                match self {
1200                    $(
1201                        Self::$Op(_) => Opcode::$Op,
1202                    )*
1203                }
1204            }
1205
1206            /// Unpacks all register IDs into a slice of options.
1207            pub fn reg_ids(&self) -> [Option<RegId>; 4] {
1208                match self {
1209                    $(
1210                        Self::$Op(op) => op.reg_ids(),
1211                    )*
1212                }
1213            }
1214        }
1215
1216        impl From<Instruction> for [u8; 4] {
1217            fn from(inst: Instruction) -> Self {
1218                match inst {
1219                    $(
1220                        Instruction::$Op(op) => op.into(),
1221                    )*
1222                }
1223            }
1224        }
1225
1226        #[cfg(feature = "typescript")]
1227        impl From<Instruction> for typescript::Instruction {
1228            fn from(inst: Instruction) -> Self {
1229                typescript::Instruction::new(inst)
1230            }
1231        }
1232
1233        impl core::convert::TryFrom<[u8; 4]> for Instruction {
1234            type Error = InvalidOpcode;
1235            fn try_from([op, a, b, c]: [u8; 4]) -> Result<Self, Self::Error> {
1236                match Opcode::try_from(op)? {
1237                    $(
1238                        Opcode::$Op => Ok(Self::$Op({
1239                            let op = op::$Op([a, b, c]);
1240                            if !op.reserved_part_is_zero() {
1241                                return Err(InvalidOpcode);
1242                            }
1243                            op
1244                        })),
1245                    )*
1246                }
1247            }
1248        }
1249
1250        impl core::fmt::Debug for Instruction {
1251            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
1252                match self {
1253                    $(
1254                        Self::$Op(op) => op.fmt(f),
1255                    )*
1256                }
1257            }
1258        }
1259    };
1260
1261    // Entrypoint to the macro, generates structs, methods, opcode enum and instruction enum
1262    // separately.
1263    ($($tts:tt)*) => {
1264        mod _op {
1265            use super::*;
1266            decl_op_struct!($($tts)*);
1267            impl_instructions!(impl_op $($tts)*);
1268        }
1269        impl_instructions!(decl_opcode_enum $($tts)*);
1270        impl_instructions!(decl_instruction_enum $($tts)*);
1271        impl_instructions!(impl_opcode $($tts)*);
1272        impl_instructions!(impl_instruction $($tts)*);
1273        impl_instructions!(impl_opcode_test_construct $($tts)*);
1274
1275
1276        #[cfg(test)]
1277        mod opcode_tests {
1278            use super::*;
1279            impl_instructions!(tests $($tts)*);
1280        }
1281    };
1282}
1283
1284/// Defines the enum with `TryFrom` trait implementation.
1285#[macro_export]
1286macro_rules! enum_try_from {
1287    (
1288        $(#[$meta:meta])* $vis:vis enum $name:ident {
1289            $($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)*
1290        },
1291        $from:ident
1292    ) => {
1293        $(#[$meta])*
1294        $vis enum $name {
1295            $($(#[$vmeta])* $vname $(= $val)?,)*
1296        }
1297
1298        impl core::convert::TryFrom<$from> for $name {
1299            type Error = $crate::PanicReason;
1300
1301            fn try_from(v: $from) -> Result<Self, Self::Error> {
1302                match v {
1303                    $(x if x == $name::$vname as $from => Ok($name::$vname),)*
1304                    _ => Err($crate::PanicReason::InvalidMetadataIdentifier),
1305                }
1306            }
1307        }
1308    }
1309}
1310
1311#[cfg(test)]
1312// Generate a test for the instruction.
1313macro_rules! op_test {
1314    ($Op:ident $op:ident[RegId]) => {
1315        #[test]
1316        fn $op() {
1317            crate::macros::test_reserved_part(Opcode::$Op, true, false, false, false);
1318        }
1319    };
1320    ($Op:ident $op:ident[RegId RegId]) => {
1321        #[test]
1322        fn $op() {
1323            crate::macros::test_reserved_part(Opcode::$Op, true, true, false, false);
1324        }
1325    };
1326    ($Op:ident $op:ident[RegId RegId RegId]) => {
1327        #[test]
1328        fn $op() {
1329            crate::macros::test_reserved_part(Opcode::$Op, true, true, true, false);
1330        }
1331    };
1332    ($Op:ident $op:ident[RegId RegId RegId RegId]) => {
1333        #[test]
1334        fn $op() {
1335            crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true);
1336        }
1337    };
1338    ($Op:ident $op:ident[RegId RegId RegId Imm06]) => {
1339        #[test]
1340        fn $op() {
1341            crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true);
1342        }
1343    };
1344    ($Op:ident $op:ident[RegId RegId Imm12]) => {
1345        #[test]
1346        fn $op() {
1347            crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true);
1348        }
1349    };
1350    ($Op:ident $op:ident[RegId Imm18]) => {
1351        #[test]
1352        fn $op() {
1353            crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true);
1354        }
1355    };
1356    ($Op:ident $op:ident[Imm24]) => {
1357        #[test]
1358        fn $op() {
1359            crate::macros::test_reserved_part(Opcode::$Op, true, true, true, true);
1360        }
1361    };
1362    ($Op:ident $op:ident[]) => {
1363        #[test]
1364        fn $op() {
1365            crate::macros::test_reserved_part(Opcode::$Op, false, false, false, false);
1366        }
1367    };
1368}
1369
1370#[cfg(test)]
1371fn bytes(a: u8, b: u8, c: u8, d: u8) -> [u8; 3] {
1372    use crate::RegId;
1373    crate::pack::bytes_from_ra_rb_rc_rd(
1374        RegId::new(a),
1375        RegId::new(b),
1376        RegId::new(c),
1377        RegId::new(d),
1378    )
1379}
1380
1381#[cfg(test)]
1382pub(crate) fn test_reserved_part(
1383    opcode: crate::Opcode,
1384    zero_should_pass: bool,
1385    first_should_pass: bool,
1386    second_should_pass: bool,
1387    third_should_pass: bool,
1388) {
1389    use crate::Instruction;
1390
1391    // Args: 0
1392    let [a, b, c] = bytes(0, 0, 0, 0);
1393    Instruction::try_from([opcode as u8, a, b, c]).unwrap();
1394    let [a, b, c] = bytes(1, 0, 0, 0);
1395    let zero_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok();
1396    assert_eq!(
1397        zero_should_pass, zero_is_error,
1398        "Opcode: {opcode:?} failed zero"
1399    );
1400
1401    // Args: 1
1402    let [a, b, c] = bytes(0, 0, 0, 0);
1403    Instruction::try_from([opcode as u8, a, b, c]).unwrap();
1404    let [a, b, c] = bytes(0, 1, 0, 0);
1405    let first_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok();
1406    assert_eq!(
1407        first_should_pass, first_is_error,
1408        "Opcode: {opcode:?} failed first"
1409    );
1410
1411    // Args: 2
1412    let [a, b, c] = bytes(0, 0, 0, 0);
1413    Instruction::try_from([opcode as u8, a, b, c]).unwrap();
1414    let [a, b, c] = bytes(0, 0, 1, 0);
1415    let second_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok();
1416    assert_eq!(
1417        second_should_pass, second_is_error,
1418        "Opcode: {opcode:?} failed second"
1419    );
1420
1421    // Args: 3
1422    let [a, b, c] = bytes(0, 0, 0, 0);
1423    Instruction::try_from([opcode as u8, a, b, c]).unwrap();
1424    let [a, b, c] = bytes(0, 0, 0, 1);
1425    let third_is_error = Instruction::try_from([opcode as u8, a, b, c]).is_ok();
1426    assert_eq!(
1427        third_should_pass, third_is_error,
1428        "Opcode: {opcode:?} failed third"
1429    );
1430}