eva_asm/instruction/
stack.rs

1//! Stack Operations.
2
3use derive_more::Display;
4
5use super::Instruction;
6use crate::opcode::{Mnemonic, OpCode};
7
8/// Remove item from stack.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
10#[display("{}", self.opcode())]
11pub struct Pop;
12
13impl Instruction for Pop {
14    fn opcode(&self) -> OpCode {
15        OpCode::Known(Mnemonic::POP)
16    }
17}
18
19/// Place item on stack.
20/// The `N` constant signifies the type of the `PUSH` opcode (e.g. `Push<32>` => `PUSH32`).
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
22#[display("{}", self.opcode())]
23pub struct Push<const N: usize>([u8; N]);
24
25impl<const N: usize> Push<N> {
26    /// Compile time assertion to check if the size is correct.
27    const VALID: () = assert!(N <= 32, "immediate value size cannot be greater than 32");
28
29    /// Create a new `PUSH` instruction with an immediate value.
30    ///
31    /// # Example
32    /// ```
33    /// # use eva_asm::instruction::Push;
34    /// let push: Push<32> = Push::new([0; 32]);
35    /// ```
36    ///
37    /// This will fail to compile if the size of the immediate value is greater than 32.
38    ///
39    /// ```compile_fail
40    /// # use eva_asm::instruction::Push;
41    /// let push = Push::new([0; 33]); // compile fail!
42    /// ```
43    #[must_use]
44    pub const fn new(immediate: [u8; N]) -> Self {
45        // this produces a compile time error if the size is invalid.
46        () = Self::VALID;
47        Self(immediate)
48    }
49
50    /// Get a reference to the immediate value.
51    ///
52    /// # Example
53    /// ```
54    /// # use eva_asm::instruction::Push;
55    /// let push = Push::new([1, 3, 3, 7]);
56    /// assert_eq!(push.immediate(), &[1, 3, 3, 7]);
57    /// ```
58    #[must_use]
59    pub const fn immediate(&self) -> &[u8; N] {
60        &self.0
61    }
62
63    /// Return the size of the immediate value.
64    ///
65    /// # Example
66    /// ```
67    /// # use eva_asm::instruction::Push;
68    /// fn push_size<const N: usize>(push: &Push<N>) {
69    ///     assert_eq!(push.size() as usize, N);
70    /// }
71    /// ```
72    #[must_use]
73    #[expect(
74        clippy::cast_possible_truncation,
75        reason = "N cannot be greater than 32"
76    )]
77    pub const fn size(&self) -> u8 {
78        // N being less than or equal to 32 is being gated by the `new` function.
79        N as u8
80    }
81}
82
83impl<const N: usize> Instruction for Push<N> {
84    fn opcode(&self) -> OpCode {
85        match N {
86            0 => OpCode::Known(Mnemonic::PUSH0),
87            1 => OpCode::Known(Mnemonic::PUSH1),
88            2 => OpCode::Known(Mnemonic::PUSH2),
89            3 => OpCode::Known(Mnemonic::PUSH3),
90            4 => OpCode::Known(Mnemonic::PUSH4),
91            5 => OpCode::Known(Mnemonic::PUSH5),
92            6 => OpCode::Known(Mnemonic::PUSH6),
93            7 => OpCode::Known(Mnemonic::PUSH7),
94            8 => OpCode::Known(Mnemonic::PUSH8),
95            9 => OpCode::Known(Mnemonic::PUSH9),
96            10 => OpCode::Known(Mnemonic::PUSH10),
97            11 => OpCode::Known(Mnemonic::PUSH11),
98            12 => OpCode::Known(Mnemonic::PUSH12),
99            13 => OpCode::Known(Mnemonic::PUSH13),
100            14 => OpCode::Known(Mnemonic::PUSH14),
101            15 => OpCode::Known(Mnemonic::PUSH15),
102            16 => OpCode::Known(Mnemonic::PUSH16),
103            17 => OpCode::Known(Mnemonic::PUSH17),
104            18 => OpCode::Known(Mnemonic::PUSH18),
105            19 => OpCode::Known(Mnemonic::PUSH19),
106            20 => OpCode::Known(Mnemonic::PUSH20),
107            21 => OpCode::Known(Mnemonic::PUSH21),
108            22 => OpCode::Known(Mnemonic::PUSH22),
109            23 => OpCode::Known(Mnemonic::PUSH23),
110            24 => OpCode::Known(Mnemonic::PUSH24),
111            25 => OpCode::Known(Mnemonic::PUSH25),
112            26 => OpCode::Known(Mnemonic::PUSH26),
113            27 => OpCode::Known(Mnemonic::PUSH27),
114            28 => OpCode::Known(Mnemonic::PUSH28),
115            29 => OpCode::Known(Mnemonic::PUSH29),
116            30 => OpCode::Known(Mnemonic::PUSH30),
117            31 => OpCode::Known(Mnemonic::PUSH31),
118            32 => OpCode::Known(Mnemonic::PUSH32),
119            _ => unreachable!(),
120        }
121    }
122}
123
124/// Duplicate stack items.
125/// The `N` constant signifies the type of the `DUP` opcode (e.g. `Dup<16>` => `DUP16`).
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
127#[display("{}", self.opcode())]
128pub struct Dup<const N: u8> {
129    /// Private field to disallow struct creation outside of this module.
130    _private: (),
131}
132
133impl<const N: u8> Dup<N> {
134    /// Compile time assertion to check if `N` is correct.
135    const VALID: () = assert!(N >= 1 && N <= 16, "invalid DUP instruction");
136
137    /// Create a new `DUP` instruction with the specified type.
138    ///
139    /// # Example
140    /// ```
141    /// # use eva_asm::instruction::Dup;
142    /// let dup: Dup<10> = Dup::new();
143    /// ```
144    ///
145    /// This will fail to compile if the instruction is not correct.
146    ///
147    /// ```compile_fail
148    /// # use eva_asm::instruction::Dup;
149    /// let dup: Dup<30> = Dup::new(); // compile fail!
150    /// ```
151    #[must_use]
152    pub const fn new() -> Self {
153        () = Self::VALID;
154        Self { _private: () }
155    }
156}
157
158impl<const N: u8> Default for Dup<N> {
159    fn default() -> Self {
160        Self::new()
161    }
162}
163
164impl<const N: u8> Instruction for Dup<N> {
165    fn opcode(&self) -> OpCode {
166        match N {
167            1 => OpCode::Known(Mnemonic::DUP1),
168            2 => OpCode::Known(Mnemonic::DUP2),
169            3 => OpCode::Known(Mnemonic::DUP3),
170            4 => OpCode::Known(Mnemonic::DUP4),
171            5 => OpCode::Known(Mnemonic::DUP5),
172            6 => OpCode::Known(Mnemonic::DUP6),
173            7 => OpCode::Known(Mnemonic::DUP7),
174            8 => OpCode::Known(Mnemonic::DUP8),
175            9 => OpCode::Known(Mnemonic::DUP9),
176            10 => OpCode::Known(Mnemonic::DUP10),
177            11 => OpCode::Known(Mnemonic::DUP11),
178            12 => OpCode::Known(Mnemonic::DUP12),
179            13 => OpCode::Known(Mnemonic::DUP13),
180            14 => OpCode::Known(Mnemonic::DUP14),
181            15 => OpCode::Known(Mnemonic::DUP15),
182            16 => OpCode::Known(Mnemonic::DUP16),
183            _ => unreachable!(),
184        }
185    }
186}
187
188/// Exchange stack items.
189/// The `N` constant signifies the type of the `SWAP` opcode (e.g. `Swap<16>` => `SWAP16`).
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Display)]
191#[display("{}", self.opcode())]
192pub struct Swap<const N: u8> {
193    /// Private field to disallow struct creation outside of this module.
194    _private: (),
195}
196
197impl<const N: u8> Swap<N> {
198    /// Compile time assertion to check if `N` is correct.
199    const VALID: () = assert!(N >= 1 && N <= 16, "invalid SWAP instruction");
200
201    /// Create a new `SWAP` instruction with the specified type.
202    ///
203    /// # Example
204    /// ```
205    /// # use eva_asm::instruction::Swap;
206    /// let swap: Swap<10> = Swap::new();
207    /// ```
208    ///
209    /// This will fail to compile if the instruction is not correct.
210    ///
211    /// ```compile_fail
212    /// # use eva_asm::instruction::Swap;
213    /// let swap: Swap<30> = Swap::new(); // compile fail!
214    /// ```
215    #[must_use]
216    pub const fn new() -> Self {
217        () = Self::VALID;
218        Self { _private: () }
219    }
220}
221
222impl<const N: u8> Default for Swap<N> {
223    fn default() -> Self {
224        Self::new()
225    }
226}
227
228impl<const N: u8> Instruction for Swap<N> {
229    fn opcode(&self) -> OpCode {
230        match N {
231            1 => OpCode::Known(Mnemonic::SWAP1),
232            2 => OpCode::Known(Mnemonic::SWAP2),
233            3 => OpCode::Known(Mnemonic::SWAP3),
234            4 => OpCode::Known(Mnemonic::SWAP4),
235            5 => OpCode::Known(Mnemonic::SWAP5),
236            6 => OpCode::Known(Mnemonic::SWAP6),
237            7 => OpCode::Known(Mnemonic::SWAP7),
238            8 => OpCode::Known(Mnemonic::SWAP8),
239            9 => OpCode::Known(Mnemonic::SWAP9),
240            10 => OpCode::Known(Mnemonic::SWAP10),
241            11 => OpCode::Known(Mnemonic::SWAP11),
242            12 => OpCode::Known(Mnemonic::SWAP12),
243            13 => OpCode::Known(Mnemonic::SWAP13),
244            14 => OpCode::Known(Mnemonic::SWAP14),
245            15 => OpCode::Known(Mnemonic::SWAP15),
246            16 => OpCode::Known(Mnemonic::SWAP16),
247            _ => panic!("invalid Swap type"),
248        }
249    }
250}
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255
256    #[test]
257    fn push_sanity() {
258        let push = Push::new([1, 2, 3, 4]);
259        assert_eq!(push.size(), 4);
260        assert_eq!(push.immediate(), &[1, 2, 3, 4]);
261        assert_eq!(push.opcode(), Mnemonic::PUSH4);
262    }
263}