lminc/
assembly.rs

1use core::fmt;
2
3use crate::{helper::case_insensitive::Str, num3::ThreeDigitNumber};
4
5#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
6#[repr(u16)]
7/// The assembly instructions
8pub enum Instruction<Data> {
9    /// Add the contents of the memory at the specified address / label to the register
10    ADD(Data) = 100,
11    /// Subtract the contents of the memory at the specified address / label from the register,
12    /// setting the negative flag if the result underflows otherwise clearing it
13    SUB(Data) = 200,
14
15    /// Store the register in the memory at the specified address / label
16    STO(Data) = 300,
17    /// Load the memory at the specified address / label into the register
18    LDA(Data) = 500,
19
20    /// Go to the specified address / label
21    BR(Data) = 600,
22    /// If the register is zero, go to the specified address / label
23    BRZ(Data) = 700,
24    /// If the negative flag is not set, go to the specified address / label
25    BRP(Data) = 800,
26
27    /// Take an input and store it in the register
28    IN = 901,
29    /// Output the register
30    OUT = 902,
31    #[cfg(feature = "extended")]
32    /// Take a char input and store it in the register
33    INA = 911,
34    #[cfg(feature = "extended")]
35    /// Output the register as a char
36    OUTA = 912,
37
38    #[default]
39    /// Halt the computer
40    HLT = 1,
41
42    #[cfg(feature = "extended")]
43    /// Enable extended mode
44    EXT = 10,
45
46    /// Store the specified data
47    DAT(Data) = 0,
48}
49
50impl<Data> Instruction<Data> {
51    /// Get the op-code of the [Instruction]
52    pub const fn op_code(&self) -> ThreeDigitNumber {
53        let op_code = unsafe { *(self as *const Self).cast::<u16>() };
54        if op_code == 1 {
55            ThreeDigitNumber::ZERO
56        } else {
57            unsafe { ThreeDigitNumber::from_unchecked(op_code) }
58        }
59    }
60}
61
62#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
63pub struct InstructionWithLabel<'a, Data> {
64    pub label: Option<&'a str>,
65    pub instruction: Instruction<Data>,
66}
67
68pub type RawInstruction = Instruction<ThreeDigitNumber>;
69
70#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
71pub enum NumberOrLabel<'a> {
72    Number(ThreeDigitNumber),
73    Label(&'a str),
74}
75
76#[derive(Clone, Copy, Debug, PartialEq, Eq)]
77pub enum InvalidInstructionError {
78    InvalidInstruction,
79}
80
81impl fmt::Display for InvalidInstructionError {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        match self {
84            Self::InvalidInstruction => write!(f, "Failed to parse an unknown instruction!"),
85        }
86    }
87}
88
89#[cfg(feature = "std")]
90impl std::error::Error for InvalidInstructionError {}
91
92impl TryFrom<&str> for Instruction<()> {
93    type Error = InvalidInstructionError;
94
95    fn try_from(value: &str) -> Result<Self, Self::Error> {
96        match Str::from(value) {
97            i if i == "ADD" => Ok(Self::ADD(())),
98            i if i == "SUB" => Ok(Self::SUB(())),
99
100            i if i == "STO" || i == "STA" => Ok(Self::STO(())),
101            i if i == "LDA" => Ok(Self::LDA(())),
102
103            i if i == "BR" || i == "BRA" => Ok(Self::BR(())),
104            i if i == "BRZ" => Ok(Self::BRZ(())),
105            i if i == "BRP" => Ok(Self::BRP(())),
106
107            i if i == "IN" || i == "INP" => Ok(Self::IN),
108            i if i == "OUT" => Ok(Self::OUT),
109            #[cfg(feature = "extended")]
110            i if i == "INA" => Ok(Self::INA),
111            #[cfg(feature = "extended")]
112            i if i == "OTA" => Ok(Self::OUTA),
113
114            i if i == "HLT" => Ok(Self::HLT),
115
116            #[cfg(feature = "extended")]
117            i if i == "EXT" => Ok(Self::EXT),
118
119            i if i == "DAT" => Ok(Self::DAT(())),
120
121            _ => Err(InvalidInstructionError::InvalidInstruction),
122        }
123    }
124}
125
126impl<'a> From<&'a str> for NumberOrLabel<'a> {
127    fn from(value: &'a str) -> Self {
128        value
129            .parse::<u16>()
130            .ok()
131            .and_then(|number| ThreeDigitNumber::try_from(number).ok())
132            .map_or(Self::Label(value), Self::Number)
133    }
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
137/// A data presence error
138pub enum Error {
139    /// The instruction expected data but did not receive any
140    ExpectedData,
141    /// The instruction did not expect data but did receive some
142    UnexpectedData,
143}
144
145impl fmt::Display for Error {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        match self {
148            Self::ExpectedData => write!(f, "Expected label / number!"),
149            Self::UnexpectedData => write!(f, "Unexpected label / number!"),
150        }
151    }
152}
153
154#[cfg(feature = "std")]
155impl std::error::Error for Error {}
156
157impl Instruction<()> {
158    // This cannot be const as the destructor for Data may not be const
159    #[allow(clippy::missing_const_for_fn)]
160    /// Try to insert data into an instruction
161    ///
162    /// # Errors
163    /// See [Error]
164    pub fn try_insert_data<Data>(self, data: Option<Data>) -> Result<Instruction<Data>, Error> {
165        use Error::{ExpectedData, UnexpectedData};
166        #[cfg(feature = "extended")]
167        use Instruction::{ADD, BR, BRP, BRZ, DAT, EXT, HLT, IN, INA, LDA, OUT, OUTA, STO, SUB};
168        #[cfg(not(feature = "extended"))]
169        use Instruction::{ADD, BR, BRP, BRZ, DAT, HLT, IN, LDA, OUT, STO, SUB};
170
171        match (self, data) {
172            (ADD(()), Some(data)) => Ok(ADD(data)),
173            (ADD(()), None) => Err(ExpectedData),
174            (SUB(()), Some(data)) => Ok(SUB(data)),
175            (SUB(()), None) => Err(ExpectedData),
176
177            (STO(()), Some(data)) => Ok(STO(data)),
178            (STO(()), None) => Err(ExpectedData),
179            (LDA(()), Some(data)) => Ok(LDA(data)),
180            (LDA(()), None) => Err(ExpectedData),
181
182            (BR(()), Some(data)) => Ok(BR(data)),
183            (BR(()), None) => Err(ExpectedData),
184            (BRP(()), Some(data)) => Ok(BRP(data)),
185            (BRP(()), None) => Err(ExpectedData),
186            (BRZ(()), Some(data)) => Ok(BRZ(data)),
187            (BRZ(()), None) => Err(ExpectedData),
188
189            (IN, Some(_)) => Err(UnexpectedData),
190            (IN, None) => Ok(IN),
191            (OUT, Some(_)) => Err(UnexpectedData),
192            (OUT, None) => Ok(OUT),
193            #[cfg(feature = "extended")]
194            (INA, Some(_)) => Err(UnexpectedData),
195            #[cfg(feature = "extended")]
196            (INA, None) => Ok(INA),
197            #[cfg(feature = "extended")]
198            (OUTA, Some(_)) => Err(UnexpectedData),
199            #[cfg(feature = "extended")]
200            (OUTA, None) => Ok(OUTA),
201
202            (HLT, Some(_)) => Err(UnexpectedData),
203            (HLT, None) => Ok(HLT),
204
205            #[cfg(feature = "extended")]
206            (EXT, Some(_)) => Err(UnexpectedData),
207            #[cfg(feature = "extended")]
208            (EXT, None) => Ok(EXT),
209
210            (DAT(()), Some(data)) => Ok(DAT(data)),
211            (DAT(()), None) => Err(ExpectedData),
212        }
213    }
214}
215
216impl<Data> Instruction<Data> {
217    /// Add a label to an instruction
218    pub const fn add_label(self, label: Option<&str>) -> InstructionWithLabel<Data> {
219        InstructionWithLabel {
220            label,
221            instruction: self,
222        }
223    }
224}