1 //! Pulley opcodes (without operands). 2 3 macro_rules! define_opcode { 4 ( 5 $( 6 $( #[$attr:meta] )* 7 $snake_name:ident = $name:ident $( { 8 $( 9 $( #[$field_attr:meta] )* 10 $field:ident : $field_ty:ty 11 ),* 12 } )? ; 13 )* 14 ) => { 15 /// An opcode without its immediates and operands. 16 #[repr(u8)] 17 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 18 pub enum Opcode { 19 $( 20 $( #[$attr] )* 21 $name, 22 )* 23 /// The extended-op opcode. An `ExtendedOpcode` follows this opcode. 24 ExtendedOp, 25 } 26 27 impl Opcode { 28 /// The value of the maximum defined opcode. 29 pub const MAX: u8 = Opcode::ExtendedOp as u8; 30 } 31 } 32 } 33 for_each_op!(define_opcode); 34 35 impl Opcode { 36 /// Create a new `Opcode` from the given byte. 37 /// 38 /// Returns `None` if `byte` is not a valid opcode. 39 pub fn new(byte: u8) -> Option<Self> { 40 if byte <= Self::MAX { 41 Some(unsafe { Self::unchecked_new(byte) }) 42 } else { 43 None 44 } 45 } 46 47 /// Like `new` but does not check whether `byte` is a valid opcode. 48 /// 49 /// # Safety 50 /// 51 /// It is unsafe to pass a `byte` that is not a valid opcode. 52 pub unsafe fn unchecked_new(byte: u8) -> Self { 53 debug_assert!(byte <= Self::MAX); 54 unsafe { core::mem::transmute(byte) } 55 } 56 } 57 58 macro_rules! define_extended_opcode { 59 ( 60 $( 61 $( #[$attr:meta] )* 62 $snake_name:ident = $name:ident $( { $( $field:ident : $field_ty:ty ),* } )? ; 63 )* 64 ) => { 65 /// An extended opcode. 66 #[repr(u16)] 67 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 68 pub enum ExtendedOpcode { 69 $( 70 $( #[$attr] )* 71 $name, 72 )* 73 } 74 75 impl ExtendedOpcode { 76 /// The value of the maximum defined extended opcode. 77 pub const MAX: u16 = $( 78 if true { 1 } else { ExtendedOpcode::$name as u16 } + 79 )* 0 - 1; 80 } 81 }; 82 } 83 for_each_extended_op!(define_extended_opcode); 84 85 impl ExtendedOpcode { 86 /// Create a new `ExtendedOpcode` from the given bytes. 87 /// 88 /// Returns `None` if `bytes` is not a valid extended opcode. 89 pub fn new(bytes: u16) -> Option<Self> { 90 if bytes <= Self::MAX { 91 Some(unsafe { Self::unchecked_new(bytes) }) 92 } else { 93 None 94 } 95 } 96 97 /// Like `new` but does not check whether `bytes` is a valid opcode. 98 /// 99 /// # Safety 100 /// 101 /// It is unsafe to pass `bytes` that is not a valid opcode. 102 pub unsafe fn unchecked_new(byte: u16) -> Self { 103 debug_assert!(byte <= Self::MAX); 104 unsafe { core::mem::transmute(byte) } 105 } 106 } 107 108 #[cfg(test)] 109 mod tests { 110 use super::*; 111 112 #[test] 113 fn max_values() { 114 assert!(Opcode::new(Opcode::MAX).is_some()); 115 assert!(Opcode::new(Opcode::MAX + 1).is_none()); 116 assert!(ExtendedOpcode::new(ExtendedOpcode::MAX).is_some()); 117 assert!(ExtendedOpcode::new(ExtendedOpcode::MAX + 1).is_none()); 118 } 119 } 120