lc3/
instruction.rs

1#![allow(dead_code)]
2use std::fmt::{Display, Formatter, Result as FmtResult};
3
4#[inline]
5fn sign_extend(x: i16, b: i16) -> i16 {
6    ((x & ((1 << b) - 1)) ^ (1 << (b - 1))) - (1 << (b - 1))
7}
8
9macro_rules! extract_bits {
10    ($data:ident[$offset:expr]) => {
11        (($data >> $offset) & 1) as i16
12    };
13    ($data:ident[$start:expr; $end:expr]) => {
14        (((1 << ($start - $end + 1)) - 1) & ($data >> $end)) as i16
15    };
16    (@extend $data:ident[$start:expr; $end:expr]) => {
17        sign_extend(
18            (((1 << ($start - $end + 1)) - 1) & ($data >> $end)) as i16,
19            $start - $end + 1,
20        )
21    };
22}
23
24/// Condition code for LC-3.
25/// Because the original LC-3 machine defines each condition codes
26/// as separated bits of PSR, we don't define it as enum,
27/// but struct with three boolean values.
28#[derive(Clone, Debug, PartialEq)]
29pub struct Condition {
30    pub n: bool,
31    pub z: bool,
32    pub p: bool,
33}
34
35impl Condition {
36    pub fn satisfies(&self, machine_cond: &Condition) -> bool {
37        (self.n && machine_cond.n) || (self.z && machine_cond.z) || (self.p && machine_cond.p)
38    }
39}
40
41impl Default for Condition {
42    fn default() -> Condition {
43        Condition {
44            n: true,
45            z: false,
46            p: false,
47        }
48    }
49}
50
51impl Eq for Condition {}
52
53/// A single LC-3 instruction.
54/// Except condition codes and is_immd values, all fields are set to
55/// i16 type for convenience.
56#[derive(Debug, PartialEq)]
57pub enum Instruction {
58    ADD { dst: i16, src1: i16, src2: i16 },
59    ADDi { dst: i16, src: i16, immd: i16 },
60    AND { dst: i16, src1: i16, src2: i16 },
61    ANDi { dst: i16, src: i16, immd: i16 },
62    BR { cond: Condition, offset: i16 },
63    JMP { base: i16 },
64    JSR { offset: i16 },
65    JSRR { base: i16 },
66    LD { dst: i16, offset: i16 },
67    LDI { dst: i16, offset: i16 },
68    LDR { dst: i16, base: i16, offset: i16 },
69    LEA { dst: i16, offset: i16 },
70    NOT { dst: i16, src: i16 },
71    RTI,
72    ST { src: i16, offset: i16 },
73    STI { src: i16, offset: i16 },
74    STR { src: i16, base: i16, offset: i16 },
75    RESERVED,
76    TRAP { vect: i16 },
77}
78
79impl Instruction {
80    /// Converts a single u16 value to LC-3 instruction.
81    /// Note that for some instructions that can be interpreted
82    /// in multiple types (e.g. JSR/JSRR), every field will be
83    /// initialized to defined bit array slice values
84    /// even if it won't be used.
85    pub fn from_u16(data: u16) -> Instruction {
86        match data >> 12 {
87            0b0000 => Instruction::BR {
88                cond: Condition {
89                    n: extract_bits!(data[11]) > 0,
90                    z: extract_bits!(data[10]) > 0,
91                    p: extract_bits!(data[9]) > 0,
92                },
93                offset: extract_bits!(@extend data[8;0]),
94            },
95            0b0001 => {
96                if extract_bits!(data[5]) > 0 {
97                    Instruction::ADDi {
98                        dst: extract_bits!(data[11;9]),
99                        src: extract_bits!(data[8;6]),
100                        immd: extract_bits!(@extend data[4;0]),
101                    }
102                } else {
103                    Instruction::ADD {
104                        dst: extract_bits!(data[11;9]),
105                        src1: extract_bits!(data[8;6]),
106                        src2: extract_bits!(data[2;0]),
107                    }
108                }
109            }
110            0b0010 => Instruction::LD {
111                dst: extract_bits!(data[11;9]),
112                offset: extract_bits!(@extend data[8;0]),
113            },
114            0b0011 => Instruction::ST {
115                src: extract_bits!(data[11;9]),
116                offset: extract_bits!(@extend data[8;0]),
117            },
118            0b0100 => {
119                if extract_bits!(data[11]) > 0 {
120                    Instruction::JSR {
121                        offset: extract_bits!(@extend data[10;0]),
122                    }
123                } else {
124                    Instruction::JSRR {
125                        base: extract_bits!(data[8;6]),
126                    }
127                }
128            }
129            0b0101 => {
130                if extract_bits!(data[5]) > 0 {
131                    Instruction::ANDi {
132                        dst: extract_bits!(data[11;9]),
133                        src: extract_bits!(data[8;6]),
134                        immd: extract_bits!(@extend data[4;0]),
135                    }
136                } else {
137                    Instruction::AND {
138                        dst: extract_bits!(data[11;9]),
139                        src1: extract_bits!(data[8;6]),
140                        src2: extract_bits!(data[2;0]),
141                    }
142                }
143            }
144            0b0110 => Instruction::LDR {
145                dst: extract_bits!(data[11;9]),
146                base: extract_bits!(data[8;6]),
147                offset: extract_bits!(@extend data[5;0]),
148            },
149            0b0111 => Instruction::STR {
150                src: extract_bits!(data[11;9]),
151                base: extract_bits!(data[8;6]),
152                offset: extract_bits!(@extend data[5;0]),
153            },
154            0b1000 => Instruction::RTI,
155            0b1001 => Instruction::NOT {
156                dst: extract_bits!(data[11;9]),
157                src: extract_bits!(data[8;6]),
158            },
159            0b1010 => Instruction::LDI {
160                dst: extract_bits!(data[11;9]),
161                offset: extract_bits!(@extend data[8;0]),
162            },
163            0b1011 => Instruction::STI {
164                src: extract_bits!(data[11;9]),
165                offset: extract_bits!(@extend data[8;0]),
166            },
167            0b1100 => Instruction::JMP {
168                base: extract_bits!(data[8;6]),
169            },
170            0b1101 => Instruction::RESERVED,
171            0b1110 => Instruction::LEA {
172                dst: extract_bits!(data[11;9]),
173                offset: extract_bits!(@extend data[8;0]),
174            },
175            0b1111 => Instruction::TRAP {
176                vect: extract_bits!(data[7;0]),
177            },
178            _ => unreachable!(),
179        }
180    }
181}
182
183impl Display for Instruction {
184    fn fmt(&self, f: &mut Formatter) -> FmtResult {
185        match self {
186            Instruction::ADD { dst, src1, src2 } => write!(f, "ADD r{}, r{}, r{}", dst, src1, src2),
187            Instruction::ADDi { dst, src, immd } => write!(f, "ADD r{}, r{}, #{}", dst, src, immd),
188            Instruction::AND { dst, src1, src2 } => write!(f, "AND r{}, r{}, r{}", dst, src1, src2),
189            Instruction::ANDi { dst, src, immd } => write!(f, "AND r{}, r{}, #{}", dst, src, immd),
190            Instruction::BR { cond, offset } => write!(
191                f,
192                "BR{}{}{} #{}",
193                if cond.n { "n" } else { "" },
194                if cond.z { "z" } else { "" },
195                if cond.p { "p" } else { "" },
196                offset
197            ),
198            Instruction::JMP { base } => write!(f, "JMP r{}", base),
199            Instruction::JSR { offset } => write!(f, "JSR #{}", offset),
200            Instruction::JSRR { base } => write!(f, "JSRR r{}", base),
201            Instruction::LD { dst, offset } => write!(f, "LD r{}, #{}", dst, offset),
202            Instruction::LDI { dst, offset } => write!(f, "LDI r{}, #{}", dst, offset),
203            Instruction::LDR { dst, base, offset } => {
204                write!(f, "LDR r{}, r{}, #{}", dst, base, offset)
205            }
206            Instruction::LEA { dst, offset } => write!(f, "LEA r{}, #{}", dst, offset),
207            Instruction::NOT { dst, src } => write!(f, "NOT r{}, r{}", dst, src),
208            Instruction::RTI => write!(f, "RTI"),
209            Instruction::ST { src, offset } => write!(f, "ST r{}, #{}", src, offset),
210            Instruction::STI { src, offset } => write!(f, "STI r{}, #{}", src, offset),
211            Instruction::STR { src, base, offset } => {
212                write!(f, "STR r{}, r{}, #{}", src, base, offset)
213            }
214            Instruction::RESERVED => write!(f, "RESERVED"),
215            Instruction::TRAP { vect } => write!(f, "TRAP x{:X}", vect),
216        }
217    }
218}
219
220impl Eq for Instruction {}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225
226    #[test]
227    fn test_sign_extend() {
228        assert_eq!(sign_extend(0b0000_0000_0000_0011 as i16, 2), -1);
229        assert_eq!(sign_extend(0b0000_0000_0000_0111 as i16, 3), -1);
230        assert_eq!(sign_extend(0b0000_0000_0000_0111 as i16, 4), 7);
231        assert_eq!(sign_extend(0b0000_0000_0001_0111 as i16, 4), 7);
232        assert_eq!(
233            sign_extend(0b0000_0000_0001_0111 as i16, 5),
234            0b1111_1111_1111_0111 as u16 as i16
235        );
236    }
237
238    #[test]
239    fn test_extract_bits() {
240        let data: u16 = 0b0010_1101_0010_0011;
241        assert_eq!(extract_bits!(data[0]), 1);
242        assert_eq!(extract_bits!(data[3]), 0);
243        assert_eq!(extract_bits!(data[0;0]), 1);
244        assert_eq!(extract_bits!(data[1;0]), 3);
245        assert_eq!(extract_bits!(data[16;12]), 2);
246        assert_eq!(extract_bits!(@extend data[1;0]), -1);
247        assert_eq!(
248            extract_bits!(@extend data[5;1]),
249            0b1111_0001 as u8 as i8 as i16 // TODO: It is too ugly...
250        );
251        assert_eq!(
252            extract_bits!(@extend data[13;8]),
253            0b1110_1101 as u8 as i8 as i16
254        );
255        assert_eq!(extract_bits!(@extend data[16;12]), 2);
256
257        let data1: u16 = 0xF025;
258        assert_eq!(extract_bits!(data1[7;0]), 37);
259    }
260
261    // TODO: Add more cases
262    #[test]
263    fn test_instruction_parse() {
264        assert_eq!(
265            Instruction::from_u16(0x1406),
266            Instruction::ADD {
267                src1: 0,
268                src2: 6,
269                dst: 2,
270            }
271        );
272        assert_eq!(
273            Instruction::from_u16(0xF020),
274            Instruction::TRAP { vect: 32 }
275        );
276        assert_eq!(
277            Instruction::from_u16(0x0402),
278            Instruction::BR {
279                cond: Condition {
280                    n: false,
281                    z: true,
282                    p: false,
283                },
284                offset: 2,
285            }
286        );
287    }
288
289    #[test]
290    fn test_instruction_fmt() {
291        assert_eq!(
292            format!(
293                "{}",
294                Instruction::ADD {
295                    dst: 1,
296                    src1: 0,
297                    src2: 2
298                }
299            ),
300            "ADD r1, r0, r2"
301        );
302        assert_eq!(
303            format!(
304                "{}",
305                Instruction::ADDi {
306                    dst: 1,
307                    src: 0,
308                    immd: -12,
309                }
310            ),
311            "ADD r1, r0, #-12"
312        );
313        assert_eq!(
314            format!(
315                "{}",
316                Instruction::AND {
317                    dst: 1,
318                    src1: 0,
319                    src2: 2
320                }
321            ),
322            "AND r1, r0, r2"
323        );
324        assert_eq!(
325            format!(
326                "{}",
327                Instruction::ANDi {
328                    dst: 1,
329                    src: 0,
330                    immd: -12,
331                }
332            ),
333            "AND r1, r0, #-12"
334        );
335        assert_eq!(
336            format!(
337                "{}",
338                Instruction::BR {
339                    cond: Condition {
340                        n: true,
341                        z: true,
342                        p: false
343                    },
344                    offset: -231,
345                },
346            ),
347            "BRnz #-231"
348        );
349        assert_eq!(format!("{}", Instruction::JMP { base: 3 }), "JMP r3");
350        assert_eq!(format!("{}", Instruction::JSR { offset: 142 }), "JSR #142");
351        assert_eq!(format!("{}", Instruction::JSRR { base: 4 }), "JSRR r4");
352        assert_eq!(
353            format!(
354                "{}",
355                Instruction::LD {
356                    dst: 2,
357                    offset: -24
358                }
359            ),
360            "LD r2, #-24"
361        );
362        assert_eq!(
363            format!("{}", Instruction::LDI { dst: 2, offset: 32 }),
364            "LDI r2, #32"
365        );
366        assert_eq!(
367            format!(
368                "{}",
369                Instruction::LDR {
370                    dst: 2,
371                    base: 3,
372                    offset: 24
373                }
374            ),
375            "LDR r2, r3, #24"
376        );
377        assert_eq!(
378            format!("{}", Instruction::LEA { dst: 3, offset: 21 }),
379            "LEA r3, #21"
380        );
381        assert_eq!(
382            format!("{}", Instruction::NOT { dst: 1, src: 0 }),
383            "NOT r1, r0"
384        );
385        assert_eq!(format!("{}", Instruction::RTI), "RTI");
386        assert_eq!(
387            format!(
388                "{}",
389                Instruction::ST {
390                    src: 2,
391                    offset: -24
392                }
393            ),
394            "ST r2, #-24"
395        );
396        assert_eq!(
397            format!("{}", Instruction::STI { src: 2, offset: 32 }),
398            "STI r2, #32"
399        );
400        assert_eq!(
401            format!(
402                "{}",
403                Instruction::STR {
404                    src: 2,
405                    base: 3,
406                    offset: 24
407                }
408            ),
409            "STR r2, r3, #24"
410        );
411        assert_eq!(format!("{}", Instruction::RESERVED), "RESERVED");
412        assert_eq!(format!("{}", Instruction::TRAP { vect: 0x25 }), "TRAP x25");
413    }
414}