monistode_binutils/
definition.rs

1use std::collections::HashMap;
2
3use serde::Deserialize;
4
5#[derive(Debug, Deserialize)]
6pub struct RawRegisterGroup {
7    pub length: u8,
8    pub registers: Vec<String>,
9}
10
11#[derive(Debug, Deserialize)]
12#[serde(tag = "type")]
13pub enum RawArgumentDefinition {
14    #[serde(rename = "register")]
15    Register { group: String },
16    #[serde(rename = "register_address")]
17    RegisterAddress { group: String },
18    #[serde(rename = "data_address")]
19    DataAddress { bits: u8 },
20    #[serde(rename = "text_address")]
21    TextAddress { bits: u8 },
22    #[serde(rename = "padding")]
23    Padding { bits: u8 },
24    #[serde(rename = "immediate")]
25    Immediate { bits: u8 },
26}
27
28#[derive(Debug, Deserialize)]
29pub struct RawCommandDefinition {
30    pub mnemonic: String,
31    pub opcode: u8,
32    #[serde(default)]
33    pub arguments: Vec<RawArgumentDefinition>,
34}
35
36#[derive(Debug, Deserialize)]
37pub struct RawDefinition {
38    pub opcode_length: u8,
39    pub opcode_offset: u8,
40    pub text_byte_length: u8,
41    pub data_byte_length: u8,
42    pub text_address_size: u8,
43    pub data_address_size: u8,
44    pub register_groups: HashMap<String, RawRegisterGroup>,
45    pub commands: Vec<RawCommandDefinition>,
46}
47
48impl RawDefinition {
49    pub fn from_str(s: &str) -> Result<Self, serde_yaml::Error> {
50        serde_yaml::from_str(s)
51    }
52}
53
54#[derive(Debug, Clone)]
55pub struct RegisterGroup {
56    pub length: u8,
57    pub registers: Vec<String>,
58}
59
60impl From<RawRegisterGroup> for RegisterGroup {
61    fn from(raw: RawRegisterGroup) -> Self {
62        RegisterGroup {
63            length: raw.length,
64            registers: raw.registers,
65        }
66    }
67}
68
69#[derive(Debug)]
70pub enum ArgumentDefinition {
71    Register { group: RegisterGroup },
72    RegisterAddress { group: RegisterGroup },
73    DataAddress { bits: u8 },
74    TextAddress { bits: u8 },
75    Padding { bits: u8 },
76    Immediate { bits: u8 },
77}
78
79impl ArgumentDefinition {
80    pub fn size(&self) -> u8 {
81        match self {
82            ArgumentDefinition::Register { group } => group.length,
83            ArgumentDefinition::RegisterAddress { group } => group.length,
84            ArgumentDefinition::DataAddress { bits } => *bits,
85            ArgumentDefinition::TextAddress { bits } => *bits,
86            ArgumentDefinition::Padding { bits } => *bits,
87            ArgumentDefinition::Immediate { bits } => *bits,
88        }
89    }
90}
91
92impl TryFrom<(RawArgumentDefinition, HashMap<String, RegisterGroup>)> for ArgumentDefinition {
93    type Error = &'static str;
94
95    fn try_from(
96        raw: (RawArgumentDefinition, HashMap<String, RegisterGroup>),
97    ) -> Result<Self, Self::Error> {
98        match raw {
99            (RawArgumentDefinition::Register { group }, groups) => match groups.get(&group) {
100                Some(g) => Ok(ArgumentDefinition::Register { group: g.clone() }),
101                None => Err("Register group not found"),
102            },
103            (RawArgumentDefinition::RegisterAddress { group }, groups) => {
104                match groups.get(&group) {
105                    Some(g) => Ok(ArgumentDefinition::RegisterAddress { group: g.clone() }),
106                    None => Err("Register group not found"),
107                }
108            }
109            (RawArgumentDefinition::DataAddress { bits }, _) => {
110                Ok(ArgumentDefinition::DataAddress { bits })
111            }
112            (RawArgumentDefinition::TextAddress { bits }, _) => {
113                Ok(ArgumentDefinition::TextAddress { bits })
114            }
115            (RawArgumentDefinition::Padding { bits }, _) => {
116                Ok(ArgumentDefinition::Padding { bits })
117            }
118            (RawArgumentDefinition::Immediate { bits }, _) => {
119                Ok(ArgumentDefinition::Immediate { bits })
120            }
121        }
122    }
123}
124
125#[derive(Debug)]
126pub struct CommandDefinition {
127    pub mnemonic: String,
128    pub opcode: u8,
129    pub arguments: Vec<ArgumentDefinition>,
130}
131
132impl TryFrom<(RawCommandDefinition, HashMap<String, RegisterGroup>)> for CommandDefinition {
133    type Error = &'static str;
134
135    fn try_from(
136        raw: (RawCommandDefinition, HashMap<String, RegisterGroup>),
137    ) -> Result<Self, Self::Error> {
138        let (raw, groups) = raw;
139        let arguments = raw
140            .arguments
141            .into_iter()
142            .map(|a| ArgumentDefinition::try_from((a, groups.clone())))
143            .collect::<Result<Vec<ArgumentDefinition>, Self::Error>>()?;
144
145        Ok(CommandDefinition {
146            mnemonic: raw.mnemonic,
147            opcode: raw.opcode,
148            arguments,
149        })
150    }
151}
152
153impl CommandDefinition {
154    pub fn arguments_size(&self) -> u8 {
155        self.arguments.iter().map(|a| a.size()).sum()
156    }
157}
158
159#[derive(Debug)]
160pub struct Definition {
161    pub opcode_length: u8,
162    pub opcode_offset: u8,
163    pub text_byte_length: u8,
164    pub data_byte_length: u8,
165    pub address_size: u8,
166    pub register_groups: HashMap<String, RegisterGroup>,
167    pub commands: Vec<CommandDefinition>,
168}
169
170impl TryFrom<RawDefinition> for Definition {
171    type Error = String;
172
173    fn try_from(raw: RawDefinition) -> Result<Self, Self::Error> {
174        let register_groups: HashMap<String, RegisterGroup> = raw
175            .register_groups
176            .into_iter()
177            .map(|(k, v)| (k, RegisterGroup::from(v)))
178            .collect();
179
180        if raw.text_address_size != raw.data_address_size {
181            return Err("Differing text and data address sizes are not supported".to_string());
182        }
183
184        let definition = Definition {
185            opcode_length: raw.opcode_length,
186            opcode_offset: raw.opcode_offset,
187            text_byte_length: raw.text_byte_length,
188            data_byte_length: raw.data_byte_length,
189            address_size: raw.text_address_size,
190            register_groups: register_groups.clone(),
191            commands: raw
192                .commands
193                .into_iter()
194                .map(|c| CommandDefinition::try_from((c, register_groups.clone())))
195                .collect::<Result<Vec<CommandDefinition>, &'static str>>()?,
196        };
197
198        // Check whether all addresses are the same size
199        for command in &definition.commands {
200            for argument in &command.arguments {
201                match argument {
202                    ArgumentDefinition::DataAddress { bits } => {
203                        if *bits != definition.address_size {
204                            return Err("Data address size mismatch".to_string());
205                        }
206                    }
207                    ArgumentDefinition::TextAddress { bits } => {
208                        if *bits != definition.address_size {
209                            return Err("Text address size mismatch".to_string());
210                        }
211                    }
212                    _ => {}
213                }
214            }
215        }
216
217        // Check whether all commands have unique opcodes
218        let mut opcodes = HashMap::new();
219        for command in &definition.commands {
220            if opcodes.contains_key(&command.opcode) {
221                return Err(format!(
222                    "Duplicate opcode: {}, both for {} and {}",
223                    command.opcode, opcodes[&command.opcode], command.mnemonic
224                ));
225            }
226            opcodes.insert(command.opcode, &command.mnemonic);
227        }
228
229        // Check divisibility of command sizes by text byte length
230        for command in &definition.commands {
231            if (definition.opcode_length + command.arguments_size()) % definition.text_byte_length
232                != 0
233            {
234                return Err(format!(
235                    "Command size not divisible by text byte length: {} ({} bits)",
236                    command.mnemonic,
237                    command.arguments_size() + definition.opcode_length
238                ));
239            }
240        }
241
242        Ok(definition)
243    }
244}
245
246impl TryFrom<String> for Definition {
247    type Error = String;
248
249    fn try_from(s: String) -> Result<Self, Self::Error> {
250        let raw = RawDefinition::from_str(&s).map_err(|_| "Failed to parse YAML")?;
251        Definition::try_from(raw)
252    }
253}