Skip to main content

i8051_disassembler/
command.rs

1use std::io;
2
3use crate::address::{AddressSpace, AddressValue};
4use crate::db::{Equivalent, Error, Function};
5use serde::{Deserialize, Serialize};
6
7pub trait Environment {
8    fn load_file_bytes(
9        &self,
10        file: &str,
11        offset: usize,
12        size: AddressValue,
13    ) -> Result<Vec<u8>, io::Error>;
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub enum Command {
18    SetLabel {
19        space: AddressSpace,
20        offset: AddressValue,
21        label: Option<String>,
22    },
23    SetEquivalent {
24        space: AddressSpace,
25        offset: AddressValue,
26        equivalent: Equivalent,
27    },
28    AutoDisassemble {
29        space: AddressSpace,
30        start: AddressValue,
31    },
32    ClearEquivalents {
33        space: AddressSpace,
34        offset: AddressValue,
35        size: AddressValue,
36    },
37    SetComment {
38        space: AddressSpace,
39        offset: AddressValue,
40        comment: Option<String>,
41    },
42    MapBytes {
43        space: AddressSpace,
44        offset: AddressValue,
45        file: String,
46        file_offset: usize,
47        size: AddressValue,
48    },
49    ClearBytes {
50        space: AddressSpace,
51        offset: AddressValue,
52        size: AddressValue,
53    },
54    SetConstantBytes {
55        space: AddressSpace,
56        offset: AddressValue,
57        size: AddressValue,
58        value: u8,
59    },
60    SetFunction {
61        space: AddressSpace,
62        offset: AddressValue,
63        function: Option<Function>,
64    },
65}
66
67impl Command {
68    pub fn set_label(space: AddressSpace, offset: AddressValue, label: impl Into<String>) -> Self {
69        Self::SetLabel {
70            space,
71            offset,
72            label: Some(label.into()),
73        }
74    }
75
76    pub fn clear_label(space: AddressSpace, offset: AddressValue) -> Self {
77        Self::SetLabel {
78            space,
79            offset,
80            label: None,
81        }
82    }
83
84    pub fn auto_disassemble(space: AddressSpace, start: AddressValue) -> Self {
85        Self::AutoDisassemble { space, start }
86    }
87
88    pub fn set_equivalent(
89        space: AddressSpace,
90        offset: AddressValue,
91        equivalent: Equivalent,
92    ) -> Self {
93        Self::SetEquivalent {
94            space,
95            offset,
96            equivalent,
97        }
98    }
99
100    pub fn clear_equivalents(
101        space: AddressSpace,
102        offset: AddressValue,
103        size: AddressValue,
104    ) -> Self {
105        Self::ClearEquivalents {
106            space,
107            offset,
108            size,
109        }
110    }
111
112    pub fn set_comment(
113        space: AddressSpace,
114        offset: AddressValue,
115        comment: impl Into<String>,
116    ) -> Self {
117        Self::SetComment {
118            space,
119            offset,
120            comment: Some(comment.into()),
121        }
122    }
123
124    pub fn clear_comment(space: AddressSpace, offset: AddressValue) -> Self {
125        Self::SetComment {
126            space,
127            offset,
128            comment: None,
129        }
130    }
131
132    pub fn map_bytes(
133        space: AddressSpace,
134        offset: AddressValue,
135        file: impl Into<String>,
136        file_offset: usize,
137        size: AddressValue,
138    ) -> Self {
139        Self::MapBytes {
140            space,
141            offset,
142            file: file.into(),
143            file_offset,
144            size,
145        }
146    }
147
148    pub fn clear_bytes(space: AddressSpace, offset: AddressValue, size: AddressValue) -> Self {
149        Self::ClearBytes {
150            space,
151            offset,
152            size,
153        }
154    }
155
156    pub fn set_constant_bytes(
157        space: AddressSpace,
158        offset: AddressValue,
159        size: AddressValue,
160        value: u8,
161    ) -> Self {
162        Self::SetConstantBytes {
163            space,
164            offset,
165            size,
166            value,
167        }
168    }
169
170    pub fn set_function(space: AddressSpace, offset: AddressValue, function: Function) -> Self {
171        Self::SetFunction {
172            space,
173            offset,
174            function: Some(function),
175        }
176    }
177
178    pub fn clear_function(space: AddressSpace, offset: AddressValue) -> Self {
179        Self::SetFunction {
180            space,
181            offset,
182            function: None,
183        }
184    }
185
186    pub fn apply(
187        self,
188        db: &mut crate::db::Db,
189        env: Option<&dyn Environment>,
190    ) -> Result<Vec<Command>, crate::db::Error> {
191        match self {
192            Command::SetLabel {
193                space,
194                offset,
195                label,
196            } => {
197                let region = db.region_mut(space);
198                let before = region.get_label(offset).map(str::to_owned);
199                match label.as_deref() {
200                    Some(label) => region.set_label(offset, label),
201                    None => region.clear_label(offset),
202                }
203                Ok(vec![Command::SetLabel {
204                    space,
205                    offset,
206                    label: before,
207                }])
208            }
209            Command::SetEquivalent {
210                space,
211                offset,
212                equivalent,
213            } => {
214                let region = db.region_mut(space);
215                let span = region.equivalent_span(offset, &equivalent)?;
216                let before = region.snapshot_equivalents(offset, span);
217                region.set_equivalent(offset, equivalent)?;
218                let mut undo = vec![Command::ClearEquivalents {
219                    space,
220                    offset,
221                    size: span,
222                }];
223                for (start, range) in before {
224                    undo.push(Command::SetEquivalent {
225                        space,
226                        offset: start,
227                        equivalent: range.equivalent,
228                    });
229                }
230                Ok(undo)
231            }
232            Command::AutoDisassemble { space, start } => {
233                let region = db.region_mut(space);
234                let commands = region.auto_disassemble(start);
235                Ok(commands
236                    .into_iter()
237                    .map(|addr| Command::ClearEquivalents {
238                        space,
239                        offset: addr,
240                        size: 1,
241                    })
242                    .collect())
243            }
244            Command::ClearEquivalents {
245                space,
246                offset,
247                size,
248            } => {
249                let region = db.region_mut(space);
250                let before = region.snapshot_equivalents(offset, size);
251                region.clear_equivalents(offset, size);
252                let mut undo = Vec::new();
253                for (start, range) in before {
254                    undo.push(Command::SetEquivalent {
255                        space,
256                        offset: start,
257                        equivalent: range.equivalent,
258                    });
259                }
260                Ok(undo)
261            }
262            Command::SetComment {
263                space,
264                offset,
265                comment,
266            } => {
267                let region = db.region_mut(space);
268                let before = region.get_comment(offset).map(str::to_owned);
269                match comment.as_deref() {
270                    Some(comment) => region.set_comment(offset, comment),
271                    None => region.clear_comment(offset),
272                }
273                Ok(vec![Command::SetComment {
274                    space,
275                    offset,
276                    comment: before,
277                }])
278            }
279            Command::MapBytes {
280                space,
281                offset,
282                file,
283                file_offset,
284                size,
285            } => {
286                let region = db.region_mut(space);
287                let Some(env) = env else {
288                    return Err(Error::NoEnvironment);
289                };
290                let bytes = env
291                    .load_file_bytes(&file, file_offset, size)
292                    .map_err(Error::Io)?;
293                let size = bytes.len() as AddressValue;
294                let before = region.snapshot_byte_ranges(offset, size);
295                region.map_bytes(&file, file_offset, offset, &bytes);
296                Ok(undo_byte_ranges(space, offset, size, before))
297            }
298            Command::ClearBytes {
299                space,
300                offset,
301                size,
302            } => {
303                let region = db.region_mut(space);
304                let before = region.snapshot_byte_ranges(offset, size);
305                region.clear_bytes(offset, size);
306                Ok(undo_byte_ranges(space, offset, size, before))
307            }
308            Command::SetConstantBytes {
309                space,
310                offset,
311                size,
312                value,
313            } => {
314                let region = db.region_mut(space);
315                let before = region.snapshot_byte_ranges(offset, size);
316                region.set_constant(offset, size, value);
317                Ok(undo_byte_ranges(space, offset, size, before))
318            }
319            Command::SetFunction {
320                space,
321                offset,
322                function,
323            } => {
324                let region = db.region_mut(space);
325                let before = region.get_function(offset).cloned();
326                match function {
327                    Some(function) => region.set_function(function),
328                    None => region.clear_function(offset),
329                }
330                Ok(vec![Command::SetFunction {
331                    space,
332                    offset,
333                    function: before,
334                }])
335            }
336        }
337    }
338}
339
340fn undo_byte_ranges(
341    space: AddressSpace,
342    offset: AddressValue,
343    size: AddressValue,
344    ranges: Vec<(AddressValue, crate::region::ByteRange)>,
345) -> Vec<Command> {
346    use crate::region::ByteRange;
347
348    let mut undo = vec![Command::ClearBytes {
349        space,
350        offset,
351        size,
352    }];
353    for (start, range) in ranges {
354        match range {
355            ByteRange::Mapped(file, file_offset, data) => {
356                undo.push(Command::MapBytes {
357                    space,
358                    offset: start,
359                    file,
360                    file_offset,
361                    size: data.len() as AddressValue,
362                });
363            }
364            ByteRange::Constant(count, value) => {
365                undo.push(Command::SetConstantBytes {
366                    space,
367                    offset: start,
368                    size: count as AddressValue,
369                    value,
370                });
371            }
372        }
373    }
374    undo
375}