iridium/assembler/
instruction_parsers.rs

1use std::fmt;
2
3use byteorder::{LittleEndian, WriteBytesExt};
4use nom::types::CompleteStr;
5
6use assembler::comment_parsers::comment;
7use assembler::label_parsers::label_declaration;
8use assembler::opcode_parsers::*;
9use assembler::operand_parsers::operand;
10use assembler::{SymbolTable, Token};
11use instruction;
12
13const MAX_I16: i32 = 32768;
14const MIN_I16: i32 = -32768;
15
16#[derive(Debug, PartialEq)]
17pub struct AssemblerInstruction {
18    pub opcode: Option<Token>,
19    pub label: Option<Token>,
20    pub directive: Option<Token>,
21    pub operand1: Option<Token>,
22    pub operand2: Option<Token>,
23    pub operand3: Option<Token>,
24}
25
26impl AssemblerInstruction {
27    pub fn to_bytes(&self, symbols: &SymbolTable) -> Vec<u8> {
28        let mut results: Vec<u8> = vec![];
29        if let Some(ref token) = self.opcode {
30            match token {
31                Token::Op { code } => match code {
32                    _ => {
33                        let b: u8 = (*code).into();
34                        results.push(b);
35                    }
36                },
37                _ => {
38                    println!("Non-opcode found in opcode field");
39                }
40            }
41        }
42        for operand in &[&self.operand1, &self.operand2, &self.operand3] {
43            if let Some(token) = operand {
44                AssemblerInstruction::extract_operand(token, &mut results, symbols);
45            }
46        }
47        while results.len() < 4 {
48            results.push(0);
49        }
50        
51        results
52    }
53
54    pub fn is_label(&self) -> bool {
55        self.label.is_some()
56    }
57
58    pub fn is_opcode(&self) -> bool {
59        self.opcode.is_some()
60    }
61
62    pub fn is_integer_needs_splitting(&self) -> bool {
63        if let Some(ref op) = self.opcode {
64            match op {
65                Token::Op{ code } => {
66                    match code {
67                        instruction::Opcode::LOAD => {
68                            if let Some(ref first_half) = self.operand2 {
69                                match first_half {
70                                    Token::IntegerOperand{ ref value } => {
71                                        if *value > MAX_I16 || *value < MIN_I16 {
72                                            return true;
73                                        }
74                                        return false;
75                                    },
76                                    _ => {
77                                        return false;
78                                    }
79                                }
80                            }
81                            return true;
82                        },
83                        _ => { return false; }
84                    }
85                }
86                _ => { return false; }
87            }
88        }
89        false
90    }
91
92    pub fn is_directive(&self) -> bool {
93        self.directive.is_some()
94    }
95
96    pub fn get_integer_value(&self) -> Option<i16> {
97        if let Some(ref operand) = self.operand2 {
98            match operand {
99                Token::IntegerOperand{ ref value } => {
100                    return Some(*value as i16)
101                },
102                _ => {
103                    return None
104                }
105            }
106        }
107        None
108    }
109
110    pub fn get_register_number(&self) -> Option<u8> {
111        match self.operand1 {
112            Some(ref reg_token) => {
113                match reg_token {
114                    Token::Register{ ref reg_num } => {
115                        Some(reg_num.clone())
116                    },
117                    _ => { None }
118                }
119            },
120            None => { None }
121        }
122    }
123
124    pub fn set_opernand_two(&mut self, t: Token) {
125        self.operand2 = Some(t)
126    }
127
128    pub fn set_operand_three(&mut self, t: Token) {
129        self.operand3 = Some(t)
130    }
131
132    /// Checks if the AssemblyInstruction has any operands at all
133    pub fn has_operands(&self) -> bool {
134        self.operand1.is_some() || self.operand2.is_some() || self.operand3.is_some()
135    }
136
137    pub fn get_directive_name(&self) -> Option<String> {
138        match &self.directive {
139            Some(d) => match d {
140                Token::Directive { name } => Some(name.to_string()),
141                _ => None,
142            },
143            None => None,
144        }
145    }
146
147    pub fn get_string_constant(&self) -> Option<String> {
148        match &self.operand1 {
149            Some(d) => match d {
150                Token::IrString { name } => Some(name.to_string()),
151                _ => None,
152            },
153            None => None,
154        }
155    }
156
157    pub fn get_i32_constant(&self) -> Option<i32> {
158        match &self.operand1 {
159            Some(d) => match d {
160                Token::IntegerOperand { value } => Some(*value),
161                _ => None,
162            },
163            None => None,
164        }
165    }
166
167    pub fn get_label_name(&self) -> Option<String> {
168        match &self.label {
169            Some(l) => match l {
170                Token::LabelDeclaration { name } => Some(name.clone()),
171                _ => None,
172            },
173            None => None,
174        }
175    }
176
177    fn extract_operand(t: &Token, results: &mut Vec<u8>, symbols: &SymbolTable) {
178        match t {
179            Token::Register { reg_num } => {
180                results.push(*reg_num);
181            },
182            Token::IntegerOperand { value } => {
183                let mut wtr = vec![];
184                wtr.write_i16::<LittleEndian>(*value as i16).unwrap();
185                results.push(wtr[1]);
186                results.push(wtr[0]);
187            },
188            Token::LabelUsage { name } => {
189                if let Some(value) = symbols.symbol_value(name) {
190                    let mut wtr = vec![];
191                    wtr.write_u32::<LittleEndian>(value).unwrap();
192                    results.push(wtr[1]);
193                    results.push(wtr[0]);
194                } else {
195                    error!("No value found for {:?}", name);
196                }
197            }
198            _ => {
199                error!("Opcode found in operand field: {:#?}", t);
200            }
201        };
202    }
203}
204
205impl fmt::Display for AssemblerInstruction {
206    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207        write!(f, "(Label: {:?} Opcode: {:?} Directive: {:?} Operand #1: {:?} Operand #2: {:?} Operand #3: {:?})", self.label, self.opcode, self.directive, self.operand1, self.operand2, self.operand3)
208    }
209}
210
211named!(instruction_combined<CompleteStr, AssemblerInstruction>,
212    do_parse!(
213        opt!(comment) >>
214        l: opt!(label_declaration) >>
215        o: opcode >>
216        o1: opt!(operand) >>
217        o2: opt!(operand) >>
218        o3: opt!(operand) >>
219        opt!(comment) >>
220        (
221            {
222            AssemblerInstruction{
223                opcode: Some(o),
224                label: l,
225                directive: None,
226                operand1: o1,
227                operand2: o2,
228                operand3: o3,
229            }
230            }
231        )
232    )
233);
234
235/// Will try to parse out any of the Instruction forms
236named!(pub instruction<CompleteStr, AssemblerInstruction>,
237    do_parse!(
238        ins: alt!(
239            instruction_combined
240        ) >>
241        (
242            ins
243        )
244    )
245);
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250    use instruction::Opcode;
251
252    #[test]
253    fn test_parse_instruction_form_one() {
254        let result = instruction_combined(CompleteStr("load $0 #100\n"));
255        assert_eq!(
256            result,
257            Ok((
258                CompleteStr(""),
259                AssemblerInstruction {
260                    opcode: Some(Token::Op { code: Opcode::LOAD }),
261                    label: None,
262                    directive: None,
263                    operand1: Some(Token::Register { reg_num: 0 }),
264                    operand2: Some(Token::IntegerOperand { value: 100 }),
265                    operand3: None
266                }
267            ))
268        );
269    }
270
271    #[test]
272    fn test_parse_instruction_form_one_with_label() {
273        let result = instruction_combined(CompleteStr("load $0 @test1\n"));
274        assert_eq!(
275            result,
276            Ok((
277                CompleteStr(""),
278                AssemblerInstruction {
279                    opcode: Some(Token::Op { code: Opcode::LOAD }),
280                    label: None,
281                    directive: None,
282                    operand1: Some(Token::Register { reg_num: 0 }),
283                    operand2: Some(Token::LabelUsage {
284                        name: "test1".to_string()
285                    }),
286                    operand3: None
287                }
288            ))
289        );
290    }
291
292    #[test]
293    fn test_parse_instruction_form_two() {
294        let result = instruction_combined(CompleteStr("hlt"));
295        assert_eq!(
296            result,
297            Ok((
298                CompleteStr(""),
299                AssemblerInstruction {
300                    opcode: Some(Token::Op { code: Opcode::HLT }),
301                    label: None,
302                    directive: None,
303                    operand1: None,
304                    operand2: None,
305                    operand3: None
306                }
307            ))
308        );
309    }
310
311    #[test]
312    fn test_parse_instruction_form_three() {
313        let result = instruction_combined(CompleteStr("add $0 $1 $2\n"));
314        assert_eq!(
315            result,
316            Ok((
317                CompleteStr(""),
318                AssemblerInstruction {
319                    opcode: Some(Token::Op { code: Opcode::ADD }),
320                    label: None,
321                    directive: None,
322                    operand1: Some(Token::Register { reg_num: 0 }),
323                    operand2: Some(Token::Register { reg_num: 1 }),
324                    operand3: Some(Token::Register { reg_num: 2 }),
325                }
326            ))
327        );
328    }
329
330    #[test]
331    fn test_parse_instruction_with_comment_one() {
332        let result = instruction_combined(CompleteStr("; this is a test\nadd $0 $1 $2\n"));
333        assert_eq!(
334            result,
335            Ok((
336                CompleteStr(""),
337                AssemblerInstruction {
338                    opcode: Some(Token::Op { code: Opcode::ADD }),
339                    label: None,
340                    directive: None,
341                    operand1: Some(Token::Register { reg_num: 0 }),
342                    operand2: Some(Token::Register { reg_num: 1 }),
343                    operand3: Some(Token::Register { reg_num: 2 }),
344                }
345            ))
346        );
347    }
348
349    #[test]
350    fn test_parse_instruction_with_comment_two() {
351        let result = instruction_combined(CompleteStr("add $0 $1 $2 ; this is a test\n"));
352        assert_eq!(
353            result,
354            Ok((
355                CompleteStr(""),
356                AssemblerInstruction {
357                    opcode: Some(Token::Op { code: Opcode::ADD }),
358                    label: None,
359                    directive: None,
360                    operand1: Some(Token::Register { reg_num: 0 }),
361                    operand2: Some(Token::Register { reg_num: 1 }),
362                    operand3: Some(Token::Register { reg_num: 2 }),
363                }
364            ))
365        );
366    }
367
368    #[test]
369    fn test_parse_cloop() {
370        let result = instruction_combined(CompleteStr("cloop #10\n"));
371        assert_eq!(
372            result,
373            Ok((
374                CompleteStr(""),
375                AssemblerInstruction {
376                    opcode: Some(Token::Op {
377                        code: Opcode::CLOOP
378                    }),
379                    label: None,
380                    directive: None,
381                    operand1: Some(Token::IntegerOperand { value: 10 }),
382                    operand2: None,
383                    operand3: None
384                }
385            ))
386        );
387    }
388}