Skip to main content

cambridge_asm/
inst.rs

1// Copyright (c) 2021 Saadi Save
2// This Source Code Form is subject to the terms of the Mozilla Public
3// License, v. 2.0. If a copy of the MPL was not distributed with this
4// file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
6#![allow(clippy::module_name_repetitions)]
7
8use crate::exec::{ExecFunc, ExecInst};
9use std::{fmt::Display, ops::Deref, str::FromStr};
10
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14/// Represents all possible types of pseudoassembly operands
15#[derive(PartialEq, Debug, Clone, Eq, Hash)]
16#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
17pub enum Op {
18    Fail(String),
19    Acc,
20    Ix,
21    Cmp,
22    Ar,
23    Indirect(Box<Op>),
24    Addr(usize),
25    Literal(usize),
26    Gpr(usize),
27    MultiOp(Vec<Op>),
28    Null,
29}
30
31impl Op {
32    pub fn is_none(&self) -> bool {
33        matches!(self, Op::Null)
34    }
35
36    pub fn is_register(&self) -> bool {
37        matches!(self, Op::Acc | Op::Ix | Op::Ar | Op::Gpr(_))
38    }
39
40    pub fn is_read_write(&self) -> bool {
41        self.is_register()
42            || match self {
43                Op::Indirect(op) if op.is_read_write() => true,
44                _ => matches!(self, Op::Addr(_)),
45            }
46    }
47
48    pub fn is_usizeable(&self) -> bool {
49        self.is_read_write() || matches!(self, Op::Literal(_))
50    }
51
52    pub fn is_address(&self) -> bool {
53        matches!(self, Op::Addr(_) | Op::Indirect(_)) && self.is_read_write()
54    }
55}
56
57impl Display for Op {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        #[allow(clippy::enum_glob_use)]
60        use Op::*;
61
62        let s = match self {
63            Null => String::new(),
64            Acc => "ACC".into(),
65            Ix => "IX".into(),
66            Cmp => "CMP".into(),
67            Ar => "AR".into(),
68            Addr(x) => format!("{x}"),
69            Literal(x) => format!("#{x}"),
70            Indirect(op) => format!("({op})"),
71            Fail(x) => x.clone(),
72            Gpr(x) => format!("r{x}"),
73            MultiOp(v) => v
74                .iter()
75                .map(ToString::to_string)
76                .collect::<Vec<_>>()
77                .join(","),
78        };
79
80        f.write_str(&s)
81    }
82}
83
84fn get_literal(mut op: String) -> usize {
85    if op.starts_with('#') {
86        op.remove(0);
87
88        match op.chars().next().unwrap() {
89            'b' | 'B' => {
90                op.remove(0);
91                usize::from_str_radix(&op, 2).unwrap()
92            }
93            'x' | 'X' => {
94                op.remove(0);
95                usize::from_str_radix(&op, 16).unwrap()
96            }
97            'o' | 'O' => {
98                op.remove(0);
99                usize::from_str_radix(&op, 8).unwrap()
100            }
101            '0'..='9' => op.parse().unwrap(),
102            _ => unreachable!(),
103        }
104    } else {
105        panic!("Literal `{op}` is invalid")
106    }
107}
108
109fn get_reg_no(mut op: String) -> usize {
110    op = op.to_lowercase();
111    op.remove(0);
112
113    // Ensured by parser
114    op.parse().unwrap()
115}
116
117impl<T: Deref<Target = str>> From<T> for Op {
118    fn from(inp: T) -> Self {
119        fn get_op(inp: &str) -> Op {
120            #[allow(clippy::enum_glob_use)]
121            use Op::*;
122
123            if inp.is_empty() {
124                Null
125            } else if let Ok(x) = inp.parse() {
126                Addr(x)
127            } else if inp.contains('#') {
128                Literal(get_literal(inp.into()))
129            } else if inp.to_lowercase().starts_with('r')
130                && inp.trim_start_matches('r').chars().all(char::is_numeric)
131            {
132                let x = get_reg_no(inp.into());
133
134                if x > 29 {
135                    panic!("Only registers from r0 to r29 are allowed")
136                } else {
137                    Gpr(x)
138                }
139            } else {
140                match inp.to_lowercase().as_str() {
141                    "acc" => Acc,
142                    "cmp" => Cmp,
143                    "ix" => Ix,
144                    _ => Fail(inp.into()),
145                }
146            }
147        }
148
149        if inp.contains(',') {
150            Op::MultiOp(inp.split(',').map(get_op).collect())
151        } else {
152            get_op(&inp)
153        }
154    }
155}
156
157/// Trait for instruction sets
158///
159/// Implement this for custom instruction sets. Manual implementation is tedious,
160/// so use [`inst_set`] or [`extend`] macros if possible
161pub trait InstSet: FromStr + Display
162where
163    <Self as FromStr>::Err: Display,
164{
165    fn as_func_ptr(&self) -> ExecFunc;
166    fn id(&self) -> u64;
167    fn from_id(_: u64) -> Result<Self, <Self as FromStr>::Err>;
168}
169
170/// Macro to generate an instruction set
171///
172/// For an example, go to this [file](https://github.com/SaadiSave/cambridge-asm/blob/main/cambridge-asm/tests/int_test.rs)
173#[macro_export]
174macro_rules! inst_set {
175    ($(#[$outer:meta])* $vis:vis $name:ident { $( $inst:ident => $func:expr,)+ }) => {
176        inst_set! { $(#[$outer])* $vis $name use std; { $( $inst => $func,)+ } }
177    };
178    ($(#[$outer:meta])* $vis:vis $name:ident $using:item { $( $inst:ident => $func:expr,)+ }) => {
179        $(#[$outer])*
180        #[repr(u64)]
181        #[derive(Clone, Copy)]
182        $vis enum $name {
183            $($inst,)+
184        }
185
186        $(#[$outer])*
187        impl std::str::FromStr for $name {
188            type Err = String;
189
190            fn from_str(s: &str) -> Result<Self, Self::Err> {
191                match s.to_uppercase().as_str() {
192                    $( stringify!($inst) => Ok(Self::$inst),)+
193                    _ => Err(format!("{s} is not an instruction")),
194                }
195            }
196        }
197
198        $(#[$outer])*
199        impl std::fmt::Display for $name {
200            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201                f.write_str(match self {
202                    $(Self::$inst => stringify!($inst),)+
203                })
204            }
205        }
206
207        $(#[$outer])*
208        impl $crate::inst::InstSet for $name {
209            fn as_func_ptr(&self) -> $crate::exec::ExecFunc {
210                $using
211                match self {
212                    $(Self::$inst => $func,)+
213                }
214            }
215
216            fn id(&self) -> u64 {
217                *self as u64
218            }
219
220            fn from_id(id: u64) -> Result<Self, String> {
221                match id {
222                    $(x if x == Self::$inst as u64 => Ok(Self::$inst),)+
223                    _ => Err(format!("0x{:X} is not a valid instruction ID", id)),
224                }
225            }
226        }
227    };
228}
229
230/// Macro to extend an instruction set
231///
232/// For an example, go to this [file](https://github.com/SaadiSave/cambridge-asm/blob/main/cambridge-asm/tests/int_test.rs)
233///
234/// Due to language limitations (no `concat_ident!`), do not use this macro within the same file twice
235#[macro_export]
236macro_rules! extend {
237    ($(#[$outer:meta])* $vis:vis $name:ident extends $parent:ident { $( $inst:ident => $func:expr,)+ }) => {
238        extend! { $(#[$outer])* $vis $name extends $parent use std; { $( $inst => $func,)+ } }
239    };
240    ($(#[$outer:meta])* $vis:vis $name:ident extends $parent:ident $using:item { $( $inst:ident => $func:expr,)+ }) => {
241        $(#[$outer])*
242        $vis struct $name {
243            __private: extend_priv::Combined,
244        }
245
246        $(#[$outer])*
247        pub(crate) mod extend_priv {
248            use $crate::inst::InstSet;
249            use super::$parent;
250            #[repr(u64)]
251            #[derive(Clone, Copy)]
252            pub enum $name {
253                $($inst,)+
254                #[allow(non_camel_case_types)]
255                LAST_INST_MARKER,
256            }
257
258            impl std::str::FromStr for $name {
259                type Err = String;
260
261                fn from_str(s: &str) -> Result<Self, Self::Err> {
262                    match s.to_uppercase().as_str() {
263                        $( stringify!($inst) => Ok(Self::$inst),)+
264                        _ => Err(String::new()),
265                    }
266                }
267            }
268
269            impl $name {
270                fn id(self) -> u64 {
271                    self as u64
272                }
273
274                fn as_func_ptr(&self) -> $crate::exec::ExecFunc {
275                    $using
276                    match self {
277                        $(Self::$inst => $func,)+
278                        Self::LAST_INST_MARKER => panic!("This should never happen, report this as a bug"),
279                    }
280                }
281
282                fn from_id(id: u64) -> Result<Self, String> {
283                    match id {
284                        $(x if x == Self::$inst as u64 => Ok(Self::$inst),)+
285                        _ => Err(format!("0x{id:X} is not a valid instruction ID")),
286                    }
287                }
288            }
289
290            impl std::fmt::Display for $name {
291                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
292                    match self {
293                        $(Self::$inst => f.write_str(stringify!($inst)),)+
294                        Self::LAST_INST_MARKER => panic!("This should never happen, report this as a bug"),
295                    }
296                }
297            }
298
299            pub enum Combined {
300                Extension($name),
301                Parent($parent),
302            }
303
304            impl Combined {
305                const LAST_INST_MARKER: u64 = $name::LAST_INST_MARKER as u64;
306
307                pub fn id(&self) -> u64 {
308                    match self {
309                        Self::Extension(ext) => ext.id(),
310                        Self::Parent(parent) => Self::LAST_INST_MARKER + parent.id()
311                    }
312                }
313
314                pub fn from_id(id: u64) -> Result<Self, String> {
315                    if id >= $name::LAST_INST_MARKER as u64 {
316                        Ok(Combined::Parent($parent::from_id(id - Self::LAST_INST_MARKER)?))
317                    } else {
318                        Ok(Combined::Extension($name::from_id(id)?))
319                    }
320                }
321
322                pub fn as_func_ptr(&self) -> $crate::exec::ExecFunc {
323                    match self {
324                        Self::Extension(e) => e.as_func_ptr(),
325                        Self::Parent(p) => p.as_func_ptr(),
326                    }
327                }
328            }
329
330            impl std::str::FromStr for Combined {
331                type Err = String;
332
333                fn from_str(s: &str) -> Result<Self, Self::Err> {
334                    if let Ok(res) = s.parse::<$name>() {
335                        Ok(Combined::Extension(res))
336                    } else if let Ok(res) = s.parse::<$parent>() {
337                        Ok(Combined::Parent(res))
338                    } else {
339                        Err(format!("{s} is not an instruction"))
340                    }
341                }
342            }
343
344            impl std::fmt::Display for Combined {
345                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346                    match self {
347                        Self::Extension(e) => write!(f, "{e}"),
348                        Self::Parent(p) => write!(f, "{p}"),
349                    }
350                }
351            }
352        }
353
354        $(#[$outer])*
355        impl std::str::FromStr for $name {
356            type Err = String;
357
358            fn from_str(s: &str) -> Result<Self, Self::Err> {
359                Ok($name { __private: s.to_uppercase().as_str().parse::<extend_priv::Combined>()? })
360            }
361        }
362
363        $(#[$outer])*
364        impl std::fmt::Display for $name {
365            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366                write!(f, "{}", self.__private)
367            }
368        }
369
370        $(#[$outer])*
371        impl $crate::inst::InstSet for $name {
372            fn as_func_ptr(&self) -> $crate::exec::ExecFunc {
373                self.__private.as_func_ptr()
374            }
375
376            fn id(&self) -> u64 {
377                self.__private.id()
378            }
379
380            fn from_id(id: u64) -> Result<Self, String> {
381                Ok( Self { __private: extend_priv::Combined::from_id(id)? })
382            }
383        }
384    };
385}
386
387/// Post-parsing representation of an instruction
388pub struct Inst<T>
389where
390    T: InstSet,
391    <T as FromStr>::Err: Display,
392{
393    pub id: u64,
394    pub inst: T,
395    pub op: Op,
396}
397
398impl<T> Inst<T>
399where
400    T: InstSet,
401    <T as FromStr>::Err: Display,
402{
403    pub fn new(inst: T, op: Op) -> Self {
404        Self {
405            id: inst.id(),
406            op,
407            inst,
408        }
409    }
410
411    pub fn to_exec_inst(self) -> ExecInst {
412        ExecInst::new(self.id, self.inst.as_func_ptr(), self.op)
413    }
414}