gbf_core/
instruction.rs

1#![deny(missing_docs)]
2
3use serde::{Deserialize, Serialize};
4
5use crate::opcode::Opcode;
6use crate::operand::Operand;
7use crate::utils::Gs2BytecodeAddress;
8
9/// Represents an instruction in a bytecode system.
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
11pub struct Instruction {
12    /// The opcode of the instruction.
13    pub opcode: Opcode,
14
15    /// The address of the instruction.
16    pub address: Gs2BytecodeAddress,
17
18    /// The operand of the instruction, if any.
19    pub operand: Option<Operand>,
20}
21
22impl Instruction {
23    /// Create a new `Instruction`.
24    ///
25    /// # Arguments
26    /// - `opcode`: The opcode of the instruction.
27    /// - `address`: The address of the instruction.
28    ///
29    /// # Returns
30    /// - A new `Instruction` instance.
31    pub fn new(opcode: Opcode, address: usize) -> Self {
32        Self {
33            opcode,
34            address,
35            operand: None,
36        }
37    }
38
39    /// Create a new `Instruction` with an operand.
40    ///
41    /// # Arguments
42    /// - `opcode`: The opcode of the instruction.
43    /// - `address`: The address of the instruction.
44    /// - `operand`: The operand of the instruction.
45    ///
46    /// # Returns
47    /// - A new `Instruction` instance.
48    pub fn new_with_operand(opcode: Opcode, address: usize, operand: Operand) -> Self {
49        Self {
50            opcode,
51            address,
52            operand: Some(operand),
53        }
54    }
55
56    /// Set the operand of the instruction.
57    ///
58    /// # Arguments
59    /// - `operand`: The operand to set.
60    ///
61    /// # Example
62    /// ```
63    /// use gbf_core::instruction::Instruction;
64    /// use gbf_core::operand::Operand;
65    /// use gbf_core::opcode::Opcode;
66    ///
67    /// let mut instruction = Instruction::new(Opcode::PushNumber, 0);
68    /// instruction.set_operand(Operand::new_number(42));
69    /// ```
70    pub fn set_operand(&mut self, operand: Operand) {
71        self.operand = Some(operand);
72    }
73}
74
75/// Implement the `Display` trait for `Instruction`.
76impl std::fmt::Display for Instruction {
77    /// Convert the Instruction to a string
78    ///
79    /// # Returns
80    /// - A string representation of the instruction.
81    ///
82    /// # Example
83    /// ```
84    /// use gbf_core::instruction::Instruction;
85    /// use gbf_core::operand::Operand;
86    /// use gbf_core::opcode::Opcode;
87    ///
88    /// let instruction = Instruction::new_with_operand(Opcode::PushNumber, 0, Operand::new_number(42));
89    /// let string = instruction.to_string();
90    /// assert_eq!(string, "PushNumber 0x2a");
91    /// ```
92    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
93        match &self.operand {
94            Some(operand) => write!(f, "{} {}", self.opcode, operand),
95            None => write!(f, "{}", self.opcode),
96        }
97    }
98}
99
100impl Default for Instruction {
101    fn default() -> Self {
102        Self {
103            // TODO: Change this to a more appropriate default opcode
104            opcode: Opcode::ConvertToFloat,
105            address: 0,
106            operand: None,
107        }
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use crate::opcode::Opcode;
115
116    #[test]
117    fn instruction_to_string() {
118        let instruction = Instruction::new(Opcode::PushNumber, 0);
119        assert_eq!(instruction.to_string(), "PushNumber");
120
121        let instruction =
122            Instruction::new_with_operand(Opcode::PushNumber, 0, Operand::new_number(42));
123        assert_eq!(instruction.to_string(), "PushNumber 0x2a");
124
125        let instruction = Instruction::new_with_operand(
126            Opcode::PushString,
127            0,
128            Operand::new_string("Hello, world!"),
129        );
130        assert_eq!(instruction.to_string(), "PushString Hello, world!");
131
132        let instruction =
133            Instruction::new_with_operand(Opcode::PushNumber, 0, Operand::new_float("3.14"));
134        assert_eq!(instruction.to_string(), "PushNumber 3.14");
135    }
136}