avra_lib/instruction/
mod.rs

1pub mod operation;
2pub mod register;
3
4use std::fmt;
5
6use crate::expr::Expr;
7
8use crate::instruction::{
9    operation::{BranchT, Operation},
10    register::{Reg16, Reg8},
11};
12
13use crate::context::Context;
14use byteorder::{ByteOrder, LittleEndian};
15use failure::{bail, Error};
16
17/// Index register operation type
18#[derive(Clone, PartialEq, Eq, Debug)]
19pub enum IndexOps {
20    /// None - {Index}
21    None(Reg16),
22    /// Post-Increment - {Index}+
23    PostIncrement(Reg16),
24    /// Post-Increment with expr - {Index}+(expr)
25    PostIncrementE(Reg16, Expr),
26    /// Pre-Decrement - -{Index}
27    PreDecrement(Reg16),
28}
29
30impl fmt::Display for IndexOps {
31    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32        match self {
33            IndexOps::None(r16) => write!(f, "{}", r16),
34            IndexOps::PostIncrement(r16) => write!(f, "{}+", r16),
35            IndexOps::PreDecrement(r16) => write!(f, "-{}", r16),
36            IndexOps::PostIncrementE(r16, expr) => write!(f, "{}+{}", r16, expr),
37        }
38    }
39}
40
41#[derive(Clone, PartialEq, Eq, Debug)]
42pub enum InstructionOps {
43    R8(Reg8),
44    Index(IndexOps),
45    E(Expr),
46}
47
48impl fmt::Display for InstructionOps {
49    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50        match self {
51            InstructionOps::R8(r8) => write!(f, "{}", r8),
52            InstructionOps::Index(index) => write!(f, "{}", index),
53            InstructionOps::E(expr) => write!(f, "{}", expr),
54        }
55    }
56}
57
58impl InstructionOps {
59    fn get_r8(&self, constants: &dyn Context) -> Result<Reg8, Error> {
60        match self {
61            Self::R8(reg8) => Ok(*reg8),
62            Self::E(Expr::Ident(name)) => match constants.get_def(name).map(|x| x.clone()) {
63                Some(reg8) => Ok(reg8),
64                None => bail!("No found {} in .defs", name),
65            },
66            _ => {
67                bail!("Argument must be R0-R16");
68            }
69        }
70    }
71
72    fn get_expr(&self) -> Result<Expr, Error> {
73        match self {
74            Self::E(expr) => Ok(expr.clone()),
75            _ => {
76                bail!("Argument must be valid expression");
77            }
78        }
79    }
80
81    fn get_index(&self) -> Result<IndexOps, Error> {
82        match self {
83            Self::Index(index) => Ok(index.clone()),
84            _ => {
85                bail!("Argument must be valid expression");
86            }
87        }
88    }
89}
90
91/// Process instruction - parse arguments and construct
92/// correct byte representation
93pub fn process(
94    op: &Operation,
95    op_args: &Vec<InstructionOps>,
96    current_address: u32,
97    constants: &dyn Context,
98) -> Result<Vec<u8>, Error> {
99    let mut finalized_opcode = vec![];
100
101    let mut opcode = op.info(constants).op_code;
102    let mut opcode_2part = 0u16;
103    let mut long_opcode = false;
104
105    match op {
106        Operation::Add
107        | Operation::Adc
108        | Operation::Sub
109        | Operation::Sbc
110        | Operation::And
111        | Operation::Or
112        | Operation::Eor
113        | Operation::Cpse
114        | Operation::Cp
115        | Operation::Cpc
116        | Operation::Mov
117        | Operation::Mul => {
118            let d = op_args[0].get_r8(constants)?;
119            opcode |= d.number() << 4;
120
121            let r = op_args[1].get_r8(constants)?;
122            opcode |= (r.number() & 0x10) << 5 | r.number() & 0x0f;
123        }
124        Operation::Adiw | Operation::Sbiw => {
125            let d = op_args[0].get_r8(constants)?;
126            let d = d.number();
127            if !(d == 24 || d == 26 || d == 28 || d == 30) {
128                bail!("{:?} can only use registers R24, R26, R28 or R30", op);
129            }
130            opcode |= ((d - 24) / 2) << 4;
131
132            let k = op_args[1].get_expr()?;
133            let k = k.get_byte(constants)? as i8;
134            if k < 0 || k > 63 {
135                bail!("Constant out of range (0 <= k <= 63");
136            }
137            let k = k as u16;
138            opcode |= (k & 0x30) << 2 | k & 0x0f;
139        }
140        Operation::Subi
141        | Operation::Sbci
142        | Operation::Andi
143        | Operation::Ori
144        | Operation::Sbr
145        | Operation::Cbr
146        | Operation::Cpi
147        | Operation::Ldi => {
148            let d = op_args[0].get_r8(constants)?;
149            if d.number() < 16 {
150                bail!("{:?} can only use a high register (r16 - r31)", op);
151            }
152            opcode |= (d.number() & 0x0f) << 4;
153
154            let k = op_args[1].get_expr()?;
155            let mut k: u16 = k.get_byte(constants)? as u16;
156            match op {
157                Operation::Cbr => {
158                    k = 0xff - k;
159                }
160                _ => {}
161            }
162            opcode |= (k & 0xf0) << 4 | (k & 0x0f);
163        }
164        Operation::Com
165        | Operation::Neg
166        | Operation::Inc
167        | Operation::Dec
168        | Operation::Push
169        | Operation::Pop
170        | Operation::Lsr
171        | Operation::Ror
172        | Operation::Asr
173        | Operation::Swap => {
174            let r = op_args[0].get_r8(constants)?;
175            opcode |= r.number() << 4;
176        }
177        Operation::Tst | Operation::Clr | Operation::Lsl | Operation::Rol => {
178            let r = op_args[0].get_r8(constants)?;
179            opcode |= r.number() << 4;
180            opcode |= (r.number() & 0x10) << 5 | r.number() & 0x0f;
181        }
182        Operation::Ser => {
183            let r = op_args[0].get_r8(constants)?;
184            if r.number() < 16 {
185                bail!("{:?} can only use a high register (r16 - r31)", op);
186            }
187            opcode |= (r.number() & 0x0f) << 4;
188        }
189        Operation::Muls => {
190            let d = op_args[0].get_r8(constants)?;
191            if d.number() < 16 {
192                bail!("{:?} can only use a high register (r16 - r31)", op);
193            }
194            opcode |= (d.number() & 0x0f) << 4;
195
196            let r = op_args[1].get_r8(constants)?;
197            if r.number() < 16 {
198                bail!("{:?} can only use a high register (r16 - r31)", op);
199            }
200            opcode |= r.number() & 0x0f;
201        }
202        Operation::Mulsu | Operation::Fmul | Operation::Fmuls | Operation::Fmulsu => {
203            let d = op_args[0].get_r8(constants)?;
204            if d.number() < 16 {
205                bail!("{:?} can only use registers (r16 - r23)", op);
206            }
207            opcode |= (d.number() & 0x07) << 4;
208
209            let r = op_args[1].get_r8(constants)?;
210            if r.number() < 16 {
211                bail!("{:?} can only use registers (r16 - r23)", op);
212            }
213            opcode |= r.number() & 0x07;
214        }
215        Operation::Rjmp | Operation::Rcall => {
216            let k = op_args[0].get_expr()?;
217            let k = k.run(constants)?;
218            let rel = k - (current_address as i64 + 1);
219            if rel < -2048 || rel > 2047 {
220                bail!("Relative address out of range (-2048 <= k <= 2047)");
221            }
222            opcode |= (rel as u16) & 0x0fff;
223        }
224        Operation::Jmp | Operation::Call => {
225            let k = op_args[0].get_expr()?;
226            let k = k.run(constants)?;
227            if k < 0 || k > 4194303 {
228                bail!("Address out of range (0 <= k <= 4194303)");
229            }
230
231            let k = k as u64;
232            opcode |= ((k & 0x3e0000) >> 13 | (k & 0x010000) >> 16) as u16;
233            opcode_2part = (k & 0xffff) as u16;
234
235            long_opcode = true;
236        }
237        Operation::Br(s) => {
238            let mut index = 0;
239            opcode |= match s {
240                BranchT::Bs | BranchT::Bc => {
241                    let s = op_args[index].get_expr()?;
242                    index += 1;
243                    s.get_bit_index(constants)? as u16
244                }
245                _ => 0,
246            };
247            let s = s.number();
248            opcode |= s;
249
250            let k = op_args[index].get_expr()?;
251            let k = k.run(constants)?;
252            let rel = k - (current_address as i64 + 1);
253            if rel < -64 || rel > 63 {
254                bail!("Relative address out of range (-64 <= k <= 63)");
255            }
256            opcode |= ((rel as u16) & 0x7f) << 3;
257        }
258        Operation::Movw => {
259            let d = op_args[0].get_r8(constants)?;
260            if d.number() < 16 && d.number() % 2 != 0 {
261                bail!("{:?} can only use a even numbered for Rd", op);
262            }
263            opcode |= (d.number() / 2) << 4;
264
265            let r = op_args[1].get_r8(constants)?;
266            if r.number() < 16 && r.number() % 2 != 0 {
267                bail!("{:?} can only use a even numbered for Rr", op);
268            }
269            opcode |= r.number() / 2;
270        }
271
272        Operation::Lds | Operation::Sts => {
273            let (r, k) = if let Operation::Lds = op {
274                (op_args[0].get_r8(constants)?, op_args[1].get_expr()?)
275            } else {
276                (op_args[1].get_r8(constants)?, op_args[0].get_expr()?)
277            };
278            opcode |= r.number() << 4;
279
280            let k = k.run(constants)?;
281
282            if constants.get_device().is_avr8l() {
283                if k < 40 || k > 0xbf {
284                    bail!("Address out of range (0x40 <= k <= 0xbf)");
285                }
286
287                let k = k as u16;
288                opcode |= (k & 0x40) << 2 | (k & 0x30) << 5 | (k & 0x0f);
289            } else {
290                if k < 0 || k > 65535 {
291                    bail!("Address out of range (0 <= k <= 65535)");
292                }
293
294                opcode_2part = (k as u16) & 0xffff;
295
296                long_opcode = true;
297            }
298        }
299        Operation::Ld | Operation::St | Operation::Ldd | Operation::Std => {
300            let (r, i) = match op {
301                Operation::Ld | Operation::Ldd => {
302                    (op_args[0].get_r8(constants)?, op_args[1].get_index()?)
303                }
304                Operation::St | Operation::Std => {
305                    (op_args[1].get_r8(constants)?, op_args[0].get_index()?)
306                }
307                _ => {
308                    bail!("Unsupported variant!");
309                }
310            };
311            opcode |= r.number() << 4;
312
313            let reg_value = |i| match i {
314                Reg16::X => 0b1100,
315                Reg16::Y => 0b1000,
316                Reg16::Z => 0b0000,
317            };
318
319            opcode |= match i {
320                IndexOps::None(r16) => {
321                    0b00 | if r16 == Reg16::X { 0x1000 } else { 0x0 } | reg_value(r16)
322                }
323                IndexOps::PostIncrement(r16) => 0b01 | 0x1000 | reg_value(r16),
324                IndexOps::PreDecrement(r16) => 0b10 | 0x1000 | reg_value(r16),
325                IndexOps::PostIncrementE(r16, expr) => {
326                    let mut op = match r16 {
327                        Reg16::X => {
328                            bail!("Index regist must be Y or Z");
329                        }
330                        Reg16::Y => 0b1000,
331                        Reg16::Z => 0b0000,
332                    };
333                    let k = expr.get_byte(constants)? as i8;
334                    if k < 0 || k > 63 {
335                        bail!("K out of range (0 <= P <= 63");
336                    }
337                    let k = k as u16;
338                    op |= (k & 0x20) << 8 | (k & 0x18) << 7 | k & 0x07;
339                    op
340                }
341            };
342        }
343        Operation::Lpm | Operation::Elpm => {
344            if op_args.len() == 0 {
345                opcode = if let Operation::Lpm = op {
346                    0b_0101_1100_1000
347                } else {
348                    0b_0101_1101_1000
349                }
350            } else {
351                let r = op_args[0].get_r8(constants)?;
352                opcode |= r.number() << 4;
353
354                let i = op_args[1].get_index()?;
355
356                opcode |= match i {
357                    IndexOps::None(Reg16::Z) => 0b100,
358                    IndexOps::PostIncrement(Reg16::Z) => 0b101,
359                    _ => {
360                        bail!("Other options are not allowed to lpm/elpm");
361                    }
362                };
363
364                opcode |= if let Operation::Elpm = op { 0b10 } else { 0b0 };
365            }
366        }
367        Operation::In | Operation::Out => {
368            let (r, k) = if let Operation::In = op {
369                (op_args[0].get_r8(constants)?, op_args[1].get_expr()?)
370            } else {
371                (op_args[1].get_r8(constants)?, op_args[0].get_expr()?)
372            };
373            opcode |= r.number() << 4;
374
375            let k = k.get_byte(constants)? as i8;
376            if k < 0 || k > 63 {
377                bail!("I/O out of range (0 <= P <= 63");
378            }
379            let k = k as u16;
380            opcode |= (k & 0x30) << 5 | k & 0x0f;
381        }
382        Operation::Sbrc | Operation::Sbrs | Operation::Bst | Operation::Bld => {
383            opcode |= op_args[0].get_r8(constants)?.number() << 4;
384
385            let b = op_args[1].get_expr()?;
386            let b: u16 = b.get_bit_index(constants)? as u16;
387
388            opcode |= b;
389        }
390        Operation::Sbi | Operation::Cbi | Operation::Sbis | Operation::Sbic => {
391            let k = op_args[0].get_expr()?.get_byte(constants)? as i8;
392            if k < 0 || k > 31 {
393                bail!("I/O out of range (0 <= P <= 31");
394            }
395            let k = k as u16;
396
397            opcode |= k << 3;
398
399            let b = op_args[1].get_expr()?;
400            let b: u16 = b.get_bit_index(constants)? as u16;
401
402            opcode |= b;
403        }
404        Operation::Bset | Operation::Bclr => {
405            let k = op_args[0].get_expr()?;
406            let k: u16 = k.get_bit_index(constants)? as u16;
407            opcode |= k << 4;
408        }
409        Operation::Se(k) | Operation::Cl(k) => {
410            let k = k.number();
411            opcode |= k << 4;
412        }
413        _ => {}
414    }
415    // Write result opcode to finalized opcode
416    let mut result = [0, 0];
417    LittleEndian::write_u16(&mut result, opcode);
418    finalized_opcode.extend(&result);
419
420    // Write append part for long opcodes
421    if long_opcode {
422        let mut result = [0, 0];
423        LittleEndian::write_u16(&mut result, opcode_2part);
424        finalized_opcode.extend(&result);
425    }
426
427    Ok(finalized_opcode)
428}
429
430#[cfg(test)]
431mod parser_tests {
432    use super::*;
433    use crate::{
434        document::{document, Document},
435        expr::{BinaryExpr, BinaryOperator, Expr},
436        instruction::operation::{BranchT, Operation, SFlags},
437    };
438
439    #[test]
440    fn operation_test() {
441        assert_eq!(document::operation("mov"), Ok(Operation::Mov));
442
443        assert_eq!(document::operation("breq"), Ok(Operation::Br(BranchT::Eq)));
444
445        assert_eq!(document::operation("sei"), Ok(Operation::Se(SFlags::I)));
446
447        assert_eq!(document::operation("clt"), Ok(Operation::Cl(SFlags::T)));
448
449        assert_eq!(
450            document::operation("outi"),
451            Ok(Operation::Custom("outi".to_string()))
452        );
453
454        assert_eq!(
455            document::operation("brtt"),
456            Ok(Operation::Custom("brtt".to_string()))
457        );
458    }
459
460    #[test]
461    fn reg8_test() {
462        assert_eq!(document::reg8("r0"), Ok(Reg8::R0));
463
464        assert_eq!(document::reg8("r21"), Ok(Reg8::R21));
465    }
466
467    #[test]
468    fn reg8_rev_convert_test() {
469        assert_eq!((Reg8::R0).to_string(), String::from("r0"));
470
471        assert_eq!((Reg8::R21).to_string(), String::from("r21"));
472    }
473
474    #[test]
475    fn reg16_test() {
476        assert_eq!(document::reg16("X"), Ok(Reg16::X));
477
478        assert_eq!(document::reg16("Y"), Ok(Reg16::Y));
479
480        assert_eq!(document::reg16("Z"), Ok(Reg16::Z));
481    }
482
483    #[test]
484    fn reg16_rev_convert_test() {
485        assert_eq!((Reg16::X).to_string(), String::from("x"));
486
487        assert_eq!((Reg16::Y).to_string(), String::from("y"));
488
489        assert_eq!((Reg16::Z).to_string(), String::from("z"));
490    }
491
492    #[test]
493    fn index_ops_test() {
494        assert_eq!(document::index_ops("X"), Ok(IndexOps::None(Reg16::X)));
495
496        assert_eq!(
497            document::index_ops("-Y"),
498            Ok(IndexOps::PreDecrement(Reg16::Y))
499        );
500
501        assert_eq!(
502            document::index_ops("X+"),
503            Ok(IndexOps::PostIncrement(Reg16::X))
504        );
505
506        assert_eq!(
507            document::index_ops("Z+4"),
508            Ok(IndexOps::PostIncrementE(Reg16::Z, Expr::Const(4)))
509        );
510    }
511
512    #[test]
513    fn index_ops_rev_convert_test() {
514        assert_eq!((IndexOps::None(Reg16::X)).to_string(), String::from("x"));
515
516        assert_eq!(
517            (IndexOps::PreDecrement(Reg16::Y)).to_string(),
518            String::from("-y")
519        );
520
521        assert_eq!(
522            (IndexOps::PostIncrement(Reg16::X)).to_string(),
523            String::from("x+")
524        );
525
526        assert_eq!(
527            (IndexOps::PostIncrementE(Reg16::Z, Expr::Const(4))).to_string(),
528            String::from("z+4")
529        );
530    }
531
532    #[test]
533    fn instruction_ops_test() {
534        assert_eq!(
535            document::instruction_ops("r0"),
536            Ok(InstructionOps::R8(Reg8::R0))
537        );
538
539        assert_eq!(
540            document::instruction_ops("t"),
541            Ok(InstructionOps::E(Expr::Ident("t".to_string())))
542        );
543
544        assert_eq!(
545            document::instruction_ops("Y+24"),
546            Ok(InstructionOps::Index(IndexOps::PostIncrementE(
547                Reg16::Y,
548                Expr::Const(24)
549            )))
550        );
551
552        assert_eq!(
553            document::instruction_ops("zl"),
554            Ok(InstructionOps::E(Expr::Ident("zl".to_string())))
555        );
556    }
557
558    #[test]
559    fn op_list_test() {
560        assert_eq!(document::op_list(""), Ok(vec![]));
561
562        assert_eq!(
563            document::op_list("a\t, b,c  ,\td"),
564            Ok(vec![
565                InstructionOps::E(Expr::Ident("a".to_string())),
566                InstructionOps::E(Expr::Ident("b".to_string())),
567                InstructionOps::E(Expr::Ident("c".to_string())),
568                InstructionOps::E(Expr::Ident("d".to_string())),
569            ])
570        );
571
572        assert_eq!(
573            document::op_list("a,b,c,d"),
574            Ok(vec![
575                InstructionOps::E(Expr::Ident("a".to_string())),
576                InstructionOps::E(Expr::Ident("b".to_string())),
577                InstructionOps::E(Expr::Ident("c".to_string())),
578                InstructionOps::E(Expr::Ident("d".to_string())),
579            ])
580        );
581    }
582
583    #[test]
584    fn instruction_line_test() {
585        assert_eq!(
586            document::instruction_line("cli"),
587            Ok(Document::CodeLine(
588                Box::new(None),
589                Operation::Cl(SFlags::I),
590                vec![]
591            ))
592        );
593
594        assert_eq!(
595            document::instruction_line("push r0"),
596            Ok(Document::CodeLine(
597                Box::new(None),
598                Operation::Push,
599                vec![InstructionOps::R8(Reg8::R0)]
600            ))
601        );
602
603        assert_eq!(
604            document::instruction_line("ldi r16, 1 << 4 | 1 << 2"),
605            Ok(Document::CodeLine(
606                Box::new(None),
607                Operation::Ldi,
608                vec![
609                    InstructionOps::R8(Reg8::R16),
610                    InstructionOps::E(Expr::Binary(Box::new(BinaryExpr {
611                        left: Expr::Binary(Box::new(BinaryExpr {
612                            left: Expr::Const(1),
613                            operator: BinaryOperator::ShiftLeft,
614                            right: Expr::Const(4)
615                        })),
616                        operator: BinaryOperator::BitwiseOr,
617                        right: Expr::Binary(Box::new(BinaryExpr {
618                            left: Expr::Const(1),
619                            operator: BinaryOperator::ShiftLeft,
620                            right: Expr::Const(2)
621                        }))
622                    })))
623                ]
624            ))
625        );
626
627        assert_eq!(
628            document::instruction_line("ldi r18, high(t)"),
629            Ok(Document::CodeLine(
630                Box::new(None),
631                Operation::Ldi,
632                vec![
633                    InstructionOps::R8(Reg8::R18),
634                    InstructionOps::E(Expr::Func(
635                        Box::new(Expr::Ident("high".to_string())),
636                        Box::new(Expr::Ident("t".to_string()))
637                    ))
638                ]
639            ))
640        );
641
642        assert_eq!(
643            document::instruction_line("ld r19, X+"),
644            Ok(Document::CodeLine(
645                Box::new(None),
646                Operation::Ld,
647                vec![
648                    InstructionOps::R8(Reg8::R19),
649                    InstructionOps::Index(IndexOps::PostIncrement(Reg16::X))
650                ]
651            ))
652        );
653
654        assert_eq!(
655            document::instruction_line("outi UDR0, r16, 1 << 4 | 1 << 2"),
656            Ok(Document::CodeLine(
657                Box::new(None),
658                Operation::Custom("outi".to_string()),
659                vec![
660                    InstructionOps::E(Expr::Ident("UDR0".to_string())),
661                    InstructionOps::R8(Reg8::R16),
662                    InstructionOps::E(Expr::Binary(Box::new(BinaryExpr {
663                        left: Expr::Binary(Box::new(BinaryExpr {
664                            left: Expr::Const(1),
665                            operator: BinaryOperator::ShiftLeft,
666                            right: Expr::Const(4)
667                        })),
668                        operator: BinaryOperator::BitwiseOr,
669                        right: Expr::Binary(Box::new(BinaryExpr {
670                            left: Expr::Const(1),
671                            operator: BinaryOperator::ShiftLeft,
672                            right: Expr::Const(2)
673                        }))
674                    })))
675                ]
676            ))
677        );
678
679        assert_eq!(
680            document::instruction_line("sei ; enable interrupts"),
681            Ok(Document::CodeLine(
682                Box::new(None),
683                Operation::Se(SFlags::I),
684                vec![]
685            ))
686        );
687
688        assert_eq!(
689            document::instruction_line("exit: ret"),
690            Ok(Document::CodeLine(
691                Box::new(Some(Document::Label("exit".to_string()))),
692                Operation::Ret,
693                vec![]
694            ))
695        );
696
697        assert_eq!(
698            document::instruction_line("fail: reti; exit from interrupt"),
699            Ok(Document::CodeLine(
700                Box::new(Some(Document::Label("fail".to_string()))),
701                Operation::Reti,
702                vec![]
703            ))
704        );
705    }
706}