1use std::{
4 cell::RefCell,
5 collections::{BTreeSet, HashMap},
6 env, fmt,
7 fs::File,
8 io::Read,
9 iter::Iterator,
10 path::PathBuf,
11 rc::Rc,
12};
13
14use crate::{
15 context::CommonContext,
16 directive::{Directive, Operand},
17 document::{document, Document},
18 expr::Expr,
19 instruction::{operation::Operation, InstructionOps},
20};
21
22use failure::{bail, Error};
23use maplit::{btreeset, hashmap};
24use strum_macros::Display;
25
26pub type Paths = BTreeSet<PathBuf>;
27
28#[derive(Clone, Copy, PartialEq, Eq, Debug)]
30pub struct CodePoint {
31 pub line_num: usize,
32 pub num: usize,
33}
34
35impl fmt::Display for CodePoint {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 write!(f, "line: {}", self.line_num)
38 }
39}
40
41#[derive(Clone, PartialEq, Eq, Debug)]
42pub enum DataDefine {
43 Db,
44 Dw,
45 Dd,
46 Dq,
47}
48
49#[derive(Clone, PartialEq, Eq, Debug)]
50pub enum Item {
51 ReserveData(i64),
52 Data(DataDefine, Vec<Operand>),
53 Def(String, Expr),
54 Undef(String),
55 Set(String, Expr),
56 Pragma(Vec<Operand>),
57 Instruction(Operation, Vec<InstructionOps>),
58 Label(String),
59}
60
61#[derive(Clone, Copy, PartialEq, Eq, Debug, Display)]
62#[strum(serialize_all = "lowercase")]
63pub enum SegmentType {
64 Code,
65 Data,
66 Eeprom,
67}
68
69#[derive(Clone, PartialEq, Eq, Debug)]
70pub struct Segment {
71 pub items: Vec<(CodePoint, Item)>,
72 pub t: SegmentType,
73 pub address: u32,
75}
76
77impl Segment {
78 pub fn new(t: SegmentType) -> Self {
79 Self {
80 items: vec![],
81 t,
82 address: 0,
83 }
84 }
85
86 pub fn is_empty(&self) -> bool {
87 self.items.is_empty()
88 }
89}
90
91#[derive(Clone, PartialEq, Eq, Debug)]
92pub struct ParseResult {
93 pub segments: Vec<Segment>,
95 pub macroses: HashMap<String, Vec<(CodePoint, String)>>,
97 pub messages: Vec<String>,
99}
100
101impl ParseResult {
102 pub fn new() -> Self {
103 Self {
104 segments: vec![],
105 macroses: hashmap! {},
106 messages: vec![],
107 }
108 }
109}
110
111#[derive(Clone, PartialEq, Eq, Debug)]
112pub struct Macro {
113 pub name: RefCell<String>,
114 pub macroses: RefCell<HashMap<String, Vec<(CodePoint, String)>>>,
115}
116
117impl Macro {
118 pub fn new() -> Self {
119 Self {
120 name: RefCell::new(String::new()),
121 macroses: RefCell::new(hashmap! {}),
122 }
123 }
124}
125
126#[derive(Clone, PartialEq, Eq, Debug)]
127pub struct ParseContext {
128 pub current_path: PathBuf,
129 pub include_paths: RefCell<Paths>,
130 pub common_context: CommonContext,
132 pub segments: Rc<RefCell<Vec<Rc<RefCell<Segment>>>>>,
134 pub macros: Rc<Macro>,
136 pub messages: Rc<RefCell<Vec<String>>>,
138}
139
140impl ParseContext {
141 pub fn new(
142 current_path: PathBuf,
143 include_paths: RefCell<Paths>,
144 common_context: CommonContext,
145 ) -> Self {
146 Self {
147 current_path,
148 include_paths,
149 common_context,
150 segments: Rc::new(RefCell::new(vec![Rc::new(RefCell::new(Segment::new(
151 SegmentType::Code,
152 )))])),
153 macros: Rc::new(Macro {
154 name: RefCell::new(String::new()),
155 macroses: RefCell::new(hashmap! {}),
156 }),
157 messages: Rc::new(RefCell::new(vec![])),
158 }
159 }
160
161 pub fn add_segment(&self, segment: Segment) {
162 self.segments
163 .borrow_mut()
164 .push(Rc::new(RefCell::new(segment)));
165 }
166
167 pub fn last_segment(&self) -> Option<Rc<RefCell<Segment>>> {
168 if let Some(item) = self.segments.borrow().last() {
169 Some(Rc::clone(item))
170 } else {
171 None
172 }
173 }
174
175 pub fn push_to_last(&self, item: (CodePoint, Item)) {
176 self.last_segment().unwrap().borrow_mut().items.push(item);
177 }
178
179 pub fn as_parse_result(&self) -> ParseResult {
180 let segments = self
181 .segments
182 .borrow()
183 .iter()
184 .filter(|x| !x.borrow().is_empty())
185 .map(|x| x.borrow().clone())
186 .collect();
187 let macroses = self.macros.macroses.borrow().clone();
188 let messages = self.messages.borrow().clone();
189
190 ParseResult {
191 segments,
192 macroses,
193 messages,
194 }
195 }
196}
197
198pub fn parse_str(input: &str, common_context: &CommonContext) -> Result<ParseResult, Error> {
199 let context = ParseContext::new(
200 env::current_dir()?,
201 RefCell::new(btreeset! {}),
202 common_context.clone(),
203 );
204
205 parse(input, &context)?;
206
207 Ok(context.as_parse_result())
208}
209
210pub fn parse_file(
211 path: PathBuf,
212 paths: Paths,
213 common_context: &CommonContext,
214) -> Result<ParseResult, Error> {
215 let context = ParseContext::new(path, RefCell::new(paths), common_context.clone());
216
217 parse_file_internal(&context)?;
218
219 Ok(context.as_parse_result())
220}
221
222pub fn parse_file_internal(context: &ParseContext) -> Result<(), Error> {
223 let ParseContext {
224 current_path,
225 include_paths,
226 common_context,
227 segments,
228 macros,
229 messages,
230 } = context.clone();
231 let include_paths = include_paths.borrow_mut();
232
233 let current_path = if !current_path.as_path().exists() {
234 let mut new_path = PathBuf::new();
235 for parent in include_paths.iter() {
236 let mut full_path = parent.clone();
237 full_path.push(current_path.clone());
238 if full_path.as_path().exists() {
239 new_path = full_path;
240 break;
241 }
242 }
243
244 if new_path == PathBuf::new() {
245 current_path
246 } else {
247 new_path
248 }
249 } else {
250 current_path
251 };
252
253 let mut file = match File::open(¤t_path) {
254 Ok(file) => file,
255 Err(err) => bail!(
256 "Cannot read file {} because: {}",
257 current_path.to_string_lossy(),
258 err
259 ),
260 };
261
262 let mut include_paths = include_paths.clone();
263 if let Some(parent) = current_path.parent() {
264 if let None = include_paths.get(parent) {
265 include_paths.insert(parent.to_path_buf());
266 }
267 }
268
269 let mut source = String::new();
270 file.read_to_string(&mut source)?;
271
272 let include_paths = RefCell::new(include_paths);
273
274 let context = ParseContext {
275 current_path,
276 include_paths,
277 common_context,
278 segments,
279 macros,
280 messages,
281 };
282
283 parse(source.as_str(), &context)?;
284
285 Ok(())
286}
287
288#[derive(Clone, Copy, PartialEq, Eq, Debug)]
289pub enum NextItem {
290 NewLine,
291 EndIf,
292 EndMacro,
293 EndFile,
294}
295
296fn skip<'a>(
297 iter: &mut dyn Iterator<Item = (usize, &'a str)>,
298 context: &ParseContext,
299 ni: NextItem,
300) -> Option<(usize, &'a str)> {
301 let mut scoup_count = 0;
302 match ni {
303 NextItem::NewLine => iter.next(),
304 NextItem::EndFile => None,
305 other => {
306 let mut ret = None;
307 if ni == NextItem::EndMacro {
308 let name = context.macros.name.borrow().clone();
309 let mut items = vec![];
310 while let Some((line_num, line)) = iter.next() {
311 if let Ok(item) = document::line(line) {
312 if let Document::DirectiveLine(_, directive, _) = item {
313 if other == NextItem::EndMacro && directive == Directive::EndMacro
314 || directive == Directive::EndM
315 {
316 ret = iter.next();
317 break;
318 }
319 }
320 }
321 items.push((CodePoint { line_num, num: 3 }, line.to_string()));
322 }
323 context.macros.macroses.borrow_mut().insert(name, items);
324 } else {
325 while let Some((num, line)) = iter.next() {
326 if let Ok(item) = document::line(line) {
327 if let Document::DirectiveLine(_, directive, _) = item {
328 if other == NextItem::EndIf {
329 if directive == Directive::If
330 || directive == Directive::IfDef
331 || directive == Directive::IfNDef
332 {
333 scoup_count += 1;
334 } else if directive == Directive::Endif
335 || directive == Directive::Else
336 || directive == Directive::ElIf
337 {
338 if scoup_count == 0 {
339 ret = if directive == Directive::ElIf {
340 Some((num, line))
341 } else {
342 iter.next()
343 };
344 break;
345 } else {
346 if directive == Directive::Endif {
347 scoup_count -= 1;
348 }
349 }
350 }
351 }
352 }
353 }
354 }
355 }
356 ret
357 }
358 }
359}
360
361pub fn parse(input: &str, context: &ParseContext) -> Result<(), Error> {
362 let mut lines = input.lines().enumerate();
363
364 parse_iter(&mut lines, context)
365}
366
367pub fn parse_iter<'a>(
368 iter: &mut dyn Iterator<Item = (usize, &'a str)>,
369 context: &ParseContext,
370) -> Result<(), Error> {
371 let mut next_item = NextItem::NewLine;
372
373 loop {
374 if let Some((line_num, line)) = skip(iter, context, next_item) {
375 next_item = NextItem::NewLine; let line_num = line_num + 1;
377 let parsed_item = document::line(line);
378 if let Ok(item) = parsed_item {
379 match item {
380 Document::Label(name) => {
381 context.push_to_last((CodePoint { line_num, num: 1 }, Item::Label(name)));
382 }
383 Document::CodeLine(label, op, op_args) => {
384 if let Some(label) = *label {
385 if let Document::Label(name) = label {
386 context.push_to_last((
387 CodePoint { line_num, num: 1 },
388 Item::Label(name),
389 ));
390 }
391 }
392 context.last_segment().unwrap().borrow_mut().items.push((
393 CodePoint { line_num, num: 2 },
394 Item::Instruction(op, op_args),
395 ));
396 }
397 Document::DirectiveLine(label, d, d_op_args) => {
398 if let Some(label) = *label {
399 if let Document::Label(name) = label {
400 context.push_to_last((
401 CodePoint { line_num, num: 1 },
402 Item::Label(name),
403 ));
404 }
405 }
406 let item = d.parse(&d_op_args, &context, CodePoint { line_num, num: 2 })?;
407 next_item = item;
408 }
409 Document::EmptyLine => {}
410 _ => {}
411 }
412 } else {
413 bail!(
414 "failed to parse {} with error: {:?}",
415 CodePoint { line_num, num: 1 },
416 parsed_item
417 );
418 }
419 } else {
420 break;
421 }
422 }
423
424 Ok(())
425}
426
427#[cfg(test)]
428mod parser_tests {
429 use super::*;
430 use crate::{
431 expr::{BinaryExpr, BinaryOperator, UnaryExpr, UnaryOperator},
432 instruction::{
433 operation::{BranchT, SFlags},
434 register::{Reg16, Reg8},
435 IndexOps, InstructionOps,
436 },
437 };
438
439 use maplit::hashmap;
440
441 #[test]
442 fn check_empty() {
443 let parse_result = parse_str("", &CommonContext::new());
444
445 assert_eq!(parse_result.is_ok(), true);
446 assert_eq!(parse_result.unwrap(), ParseResult::new());
447
448 let parse_result = parse_str("\t\r\n\t \t\r\n\n", &CommonContext::new());
449 assert_eq!(parse_result.is_ok(), true);
450 assert_eq!(parse_result.unwrap(), ParseResult::new());
451 }
452
453 #[test]
454 fn check_wrong() {
455 let parse_result = parse_str("bla bla bla bla", &CommonContext::new());
456 assert_eq!(parse_result.is_err(), true);
457 }
458
459 #[test]
460 fn check_comment() {
461 let parse_result = parse_str(";bla bla bla bla", &CommonContext::new());
462 assert_eq!(parse_result.unwrap(), ParseResult::new());
463
464 let parse_result = parse_str(";;bla bla bla bla", &CommonContext::new());
465 assert_eq!(parse_result.unwrap(), ParseResult::new());
466 }
467
468 #[test]
469 fn check_label() {
470 let parse_result = parse_str("good_point:", &CommonContext::new());
471 assert_eq!(
472 parse_result.unwrap(),
473 ParseResult {
474 segments: vec![Segment {
475 items: vec![(
476 CodePoint {
477 line_num: 1,
478 num: 1
479 },
480 Item::Label("good_point".to_string())
481 )],
482 t: SegmentType::Code,
483 address: 0
484 }],
485 macroses: hashmap! {},
486 messages: vec![],
487 }
488 );
489
490 let parse_result = parse_str("good_point:\ngood_point2:", &CommonContext::new());
491 assert_eq!(
492 parse_result.unwrap(),
493 ParseResult {
494 segments: vec![Segment {
495 items: vec![
496 (
497 CodePoint {
498 line_num: 1,
499 num: 1
500 },
501 Item::Label("good_point".to_string())
502 ),
503 (
504 CodePoint {
505 line_num: 2,
506 num: 1
507 },
508 Item::Label("good_point2".to_string())
509 )
510 ],
511 t: SegmentType::Code,
512 address: 0
513 }],
514 macroses: hashmap! {},
515 messages: vec![],
516 }
517 );
518
519 let parse_result = parse_str("good_point:; this is simple comment", &CommonContext::new());
520 assert_eq!(
521 parse_result.unwrap(),
522 ParseResult {
523 segments: vec![Segment {
524 items: vec![(
525 CodePoint {
526 line_num: 1,
527 num: 1
528 },
529 Item::Label("good_point".to_string())
530 )],
531 t: SegmentType::Code,
532 address: 0
533 }],
534 macroses: hashmap! {},
535 messages: vec![],
536 }
537 );
538
539 let parse_result = parse_str("bad_point: bla bla bla", &CommonContext::new());
540 assert_eq!(parse_result.is_err(), true);
541
542 let parse_result = parse_str("bad_point::", &CommonContext::new());
543 assert_eq!(parse_result.is_err(), true);
544
545 let parse_result = parse_str("bad_point: bla bla bla", &CommonContext::new());
546 assert_eq!(parse_result.is_err(), true);
547
548 let parse_result = parse_str("bad_point: bad_point2:", &CommonContext::new());
549 assert_eq!(parse_result.is_err(), true);
550 }
551
552 #[test]
553 fn check_non_argument_commands() {
554 let parse_result = parse_str("nop\nnop\nnop\nret", &CommonContext::new());
555 assert_eq!(
556 parse_result.unwrap(),
557 ParseResult {
558 segments: vec![Segment {
559 items: vec![
560 (
561 CodePoint {
562 line_num: 1,
563 num: 2
564 },
565 Item::Instruction(Operation::Nop, vec![])
566 ),
567 (
568 CodePoint {
569 line_num: 2,
570 num: 2
571 },
572 Item::Instruction(Operation::Nop, vec![])
573 ),
574 (
575 CodePoint {
576 line_num: 3,
577 num: 2
578 },
579 Item::Instruction(Operation::Nop, vec![])
580 ),
581 (
582 CodePoint {
583 line_num: 4,
584 num: 2
585 },
586 Item::Instruction(Operation::Ret, vec![])
587 ),
588 ],
589 t: SegmentType::Code,
590 address: 0
591 }],
592 macroses: hashmap! {},
593 messages: vec![],
594 }
595 );
596
597 let parse_result = parse_str("label:\nnop\nnop\nret", &CommonContext::new());
598 assert_eq!(
599 parse_result.unwrap(),
600 ParseResult {
601 segments: vec![Segment {
602 items: vec![
603 (
604 CodePoint {
605 line_num: 1,
606 num: 1
607 },
608 Item::Label("label".to_string())
609 ),
610 (
611 CodePoint {
612 line_num: 2,
613 num: 2
614 },
615 Item::Instruction(Operation::Nop, vec![])
616 ),
617 (
618 CodePoint {
619 line_num: 3,
620 num: 2
621 },
622 Item::Instruction(Operation::Nop, vec![])
623 ),
624 (
625 CodePoint {
626 line_num: 4,
627 num: 2
628 },
629 Item::Instruction(Operation::Ret, vec![])
630 ),
631 ],
632 t: SegmentType::Code,
633 address: 0
634 }],
635 macroses: hashmap! {},
636 messages: vec![],
637 }
638 );
639
640 let parse_result = parse_str("seh\nclh\nnop\ncli", &CommonContext::new());
641 assert_eq!(
642 parse_result.unwrap(),
643 ParseResult {
644 segments: vec![Segment {
645 items: vec![
646 (
647 CodePoint {
648 line_num: 1,
649 num: 2
650 },
651 Item::Instruction(Operation::Se(SFlags::H), vec![])
652 ),
653 (
654 CodePoint {
655 line_num: 2,
656 num: 2
657 },
658 Item::Instruction(Operation::Cl(SFlags::H), vec![])
659 ),
660 (
661 CodePoint {
662 line_num: 3,
663 num: 2
664 },
665 Item::Instruction(Operation::Nop, vec![])
666 ),
667 (
668 CodePoint {
669 line_num: 4,
670 num: 2
671 },
672 Item::Instruction(Operation::Cl(SFlags::I), vec![])
673 ),
674 ],
675 t: SegmentType::Code,
676 address: 0
677 }],
678 macroses: hashmap! {},
679 messages: vec![],
680 }
681 );
682 }
683
684 #[test]
685 fn check_one_argument_commands() {
686 let parse_result = parse_str("push r0\nlsl r0\nswap r0\npop r1", &CommonContext::new());
687 assert_eq!(
688 parse_result.unwrap(),
689 ParseResult {
690 segments: vec![Segment {
691 items: vec![
692 (
693 CodePoint {
694 line_num: 1,
695 num: 2
696 },
697 Item::Instruction(Operation::Push, vec![InstructionOps::R8(Reg8::R0)])
698 ),
699 (
700 CodePoint {
701 line_num: 2,
702 num: 2
703 },
704 Item::Instruction(Operation::Lsl, vec![InstructionOps::R8(Reg8::R0)])
705 ),
706 (
707 CodePoint {
708 line_num: 3,
709 num: 2
710 },
711 Item::Instruction(Operation::Swap, vec![InstructionOps::R8(Reg8::R0)])
712 ),
713 (
714 CodePoint {
715 line_num: 4,
716 num: 2
717 },
718 Item::Instruction(Operation::Pop, vec![InstructionOps::R8(Reg8::R1)])
719 ),
720 ],
721 t: SegmentType::Code,
722 address: 0
723 }],
724 macroses: hashmap! {},
725 messages: vec![],
726 }
727 );
728
729 let parse_result = parse_str("tst r1\nbrpl exit\nrjmp error\n", &CommonContext::new());
730 assert_eq!(
731 parse_result.unwrap(),
732 ParseResult {
733 segments: vec![Segment {
734 items: vec![
735 (
736 CodePoint {
737 line_num: 1,
738 num: 2
739 },
740 Item::Instruction(Operation::Tst, vec![InstructionOps::R8(Reg8::R1)])
741 ),
742 (
743 CodePoint {
744 line_num: 2,
745 num: 2
746 },
747 Item::Instruction(
748 Operation::Br(BranchT::Pl),
749 vec![InstructionOps::E(Expr::Ident("exit".to_string()))]
750 )
751 ),
752 (
753 CodePoint {
754 line_num: 3,
755 num: 2
756 },
757 Item::Instruction(
758 Operation::Rjmp,
759 vec![InstructionOps::E(Expr::Ident("error".to_string()))]
760 )
761 ),
762 ],
763 t: SegmentType::Code,
764 address: 0
765 }],
766 macroses: hashmap! {},
767 messages: vec![],
768 }
769 );
770 }
771
772 #[test]
773 fn check_two_argument_commands() {
774 let parse_result = parse_str(
775 "ldi r16, 1 << 2 | 1 << 1\nmov r0, r16\n subi r16, (-1)\nsts data, r16",
776 &CommonContext::new(),
777 );
778 assert_eq!(
779 parse_result.unwrap(),
780 ParseResult {
781 segments: vec![Segment {
782 items: vec![
783 (
784 CodePoint {
785 line_num: 1,
786 num: 2
787 },
788 Item::Instruction(
789 Operation::Ldi,
790 vec![
791 InstructionOps::R8(Reg8::R16),
792 InstructionOps::E(Expr::Binary(Box::new(BinaryExpr {
793 left: Expr::Binary(Box::new(BinaryExpr {
794 left: Expr::Const(1),
795 operator: BinaryOperator::ShiftLeft,
796 right: Expr::Const(2),
797 })),
798 operator: BinaryOperator::BitwiseOr,
799 right: Expr::Binary(Box::new(BinaryExpr {
800 left: Expr::Const(1),
801 operator: BinaryOperator::ShiftLeft,
802 right: Expr::Const(1),
803 })),
804 })))
805 ]
806 )
807 ),
808 (
809 CodePoint {
810 line_num: 2,
811 num: 2
812 },
813 Item::Instruction(
814 Operation::Mov,
815 vec![InstructionOps::R8(Reg8::R0), InstructionOps::R8(Reg8::R16)]
816 )
817 ),
818 (
819 CodePoint {
820 line_num: 3,
821 num: 2
822 },
823 Item::Instruction(
824 Operation::Subi,
825 vec![
826 InstructionOps::R8(Reg8::R16),
827 InstructionOps::E(Expr::Unary(Box::new(UnaryExpr {
828 operator: UnaryOperator::Minus,
829 expr: Expr::Const(1)
830 })))
831 ]
832 )
833 ),
834 (
835 CodePoint {
836 line_num: 4,
837 num: 2
838 },
839 Item::Instruction(
840 Operation::Sts,
841 vec![
842 InstructionOps::E(Expr::Ident("data".to_string())),
843 InstructionOps::R8(Reg8::R16)
844 ]
845 )
846 ),
847 ],
848 t: SegmentType::Code,
849 address: 0
850 }],
851 macroses: hashmap! {},
852 messages: vec![],
853 }
854 );
855
856 let parse_result = parse_str(
857 "ld r17, X\nld r18, Y+\nld r19, -Z\nst X+, r19",
858 &CommonContext::new(),
859 );
860 assert_eq!(
861 parse_result.unwrap(),
862 ParseResult {
863 segments: vec![Segment {
864 items: vec![
865 (
866 CodePoint {
867 line_num: 1,
868 num: 2
869 },
870 Item::Instruction(
871 Operation::Ld,
872 vec![
873 InstructionOps::R8(Reg8::R17),
874 InstructionOps::Index(IndexOps::None(Reg16::X))
875 ]
876 )
877 ),
878 (
879 CodePoint {
880 line_num: 2,
881 num: 2
882 },
883 Item::Instruction(
884 Operation::Ld,
885 vec![
886 InstructionOps::R8(Reg8::R18),
887 InstructionOps::Index(IndexOps::PostIncrement(Reg16::Y))
888 ]
889 )
890 ),
891 (
892 CodePoint {
893 line_num: 3,
894 num: 2
895 },
896 Item::Instruction(
897 Operation::Ld,
898 vec![
899 InstructionOps::R8(Reg8::R19),
900 InstructionOps::Index(IndexOps::PreDecrement(Reg16::Z))
901 ]
902 )
903 ),
904 (
905 CodePoint {
906 line_num: 4,
907 num: 2
908 },
909 Item::Instruction(
910 Operation::St,
911 vec![
912 InstructionOps::Index(IndexOps::PostIncrement(Reg16::X)),
913 InstructionOps::R8(Reg8::R19)
914 ]
915 )
916 ),
917 ],
918 t: SegmentType::Code,
919 address: 0
920 }],
921 macroses: hashmap! {},
922 messages: vec![],
923 }
924 );
925
926 let parse_result = parse_str("ldd r25, Z+2\nstd Z+6, r24", &CommonContext::new());
927 assert_eq!(
928 parse_result.unwrap(),
929 ParseResult {
930 segments: vec![Segment {
931 items: vec![
932 (
933 CodePoint {
934 line_num: 1,
935 num: 2
936 },
937 Item::Instruction(
938 Operation::Ldd,
939 vec![
940 InstructionOps::R8(Reg8::R25),
941 InstructionOps::Index(IndexOps::PostIncrementE(
942 Reg16::Z,
943 Expr::Const(2)
944 ))
945 ]
946 )
947 ),
948 (
949 CodePoint {
950 line_num: 2,
951 num: 2
952 },
953 Item::Instruction(
954 Operation::Std,
955 vec![
956 InstructionOps::Index(IndexOps::PostIncrementE(
957 Reg16::Z,
958 Expr::Const(6)
959 )),
960 InstructionOps::R8(Reg8::R24)
961 ]
962 )
963 ),
964 ],
965 t: SegmentType::Code,
966 address: 0
967 }],
968 macroses: hashmap! {},
969 messages: vec![],
970 }
971 );
972 }
973}