ch8alib/codegen/assembler.rs
1/*
2 * assembler.rs
3 * Defines a class that assembles Chip-8 assembly code
4 * Created on 12/21/2019
5 * Created by Andrew Davis
6 *
7 * Copyright (C) 2019 Andrew Davis
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23//crate import statement
24extern crate ch8_isa;
25
26//usage statements
27use super::AddrTable;
28use super::Preprocessor;
29use super::super::lex::AsmLexer;
30use super::super::lex::Token;
31use super::super::lex::TokenType;
32use super::super::error::AsmError;
33use super::super::error::ParseError;
34use super::super::error::OpcodeError;
35use super::super::error::ArgError;
36use super::super::error::SkipError;
37use ch8_isa::codegen::Binary;
38use ch8_isa::codegen::Instruction;
39use ch8_isa::data;
40
41/// Assembles Chip-8 binaries
42pub struct Assembler {
43 /// Lexes the source code
44 lexer: AsmLexer,
45
46 /// The address table for labels in the source code
47 addrs: AddrTable,
48
49 /// The current `Token` being assembled
50 cur_token: Token,
51
52 /// The binary being created
53 binary: Binary
54}
55
56//implementation
57impl Assembler {
58 /// Constructs a new `Assembler` instance
59 ///
60 /// # Arguments
61 ///
62 /// * `code` - The source code to be assembled
63 /// * `name` - The name of the assembled binary
64 ///
65 /// # Returns
66 ///
67 /// A new `Assembler` instance with the given properties,
68 /// wrapped in a `Result`
69 pub fn new(code: &str, name: &str) -> Result<Self, AsmError> {
70 //create the binary
71 let bin = match Binary::new(name) {
72 Ok(b) => b,
73 Err(be) => return Err(AsmError::Binary(be))
74 };
75
76 //create the preprocessor
77 let mut prep = match Preprocessor::new(code) {
78 Ok(p) => p,
79 Err(le) => return Err(AsmError::Lexer(le))
80 };
81
82 //preprocess the code
83 let new_addrs = match prep.process() {
84 Ok(at) => at,
85 Err(le) => return Err(AsmError::Lexer(le))
86 };
87
88 //create the lexer
89 let mut lex = AsmLexer::new(code);
90
91 //get the first token
92 let tok = match lex.get_next_token() {
93 Ok(t) => t,
94 Err(le) => return Err(AsmError::Lexer(le))
95 };
96
97 //and return the instance
98 return Ok(Assembler {
99 lexer: lex,
100 addrs: new_addrs,
101 cur_token: tok,
102 binary: bin
103 });
104 }
105
106 /// Assembles source code into a binary
107 ///
108 /// # Returns
109 ///
110 /// The assembled binary, wrapped in a `Result`
111 pub fn assemble(&mut self) -> Result<&mut Binary, AsmError> {
112 //loop and generate code
113 loop {
114 //check for an EOF token
115 if self.cur_token.get_type() == TokenType::EndOfInput {
116 break;
117 }
118
119 //handle different tokens
120 if self.cur_token.get_type() == TokenType::Instruction {
121 //process the instruction
122 let instr = self.instruction()?;
123
124 //and add it to the binary
125 match self.binary.add_instruction(&instr) {
126 Ok(()) => {},
127 Err(be) => return Err(AsmError::Binary(be))
128 };
129 } else if self.cur_token.get_type() == TokenType::BinLit {
130 //get the byte
131 let b = self.bin_lit()?;
132
133 //and write it to the binary
134 match self.binary.add_byte(b) {
135 Ok(()) => {},
136 Err(be) => return Err(AsmError::Binary(be))
137 };
138 } else if self.cur_token.get_type() == TokenType::DecLit {
139 //get the decimal literal
140 let dec = self.dec_lit()?;
141
142 //determine what size the literal is
143 if dec > 0xFF {
144 match self.binary.add_word(dec) {
145 Ok(()) => {},
146 Err(be) => return Err(AsmError::Binary(be))
147 };
148 } else {
149 match self.binary.add_byte(dec as u8) {
150 Ok(()) => {},
151 Err(be) => return Err(AsmError::Binary(be))
152 };
153 }
154 } else if self.cur_token.get_type() == TokenType::HexLit {
155 //get the decimal literal
156 let hex = self.hex_lit()?;
157
158 //determine what size the literal is
159 if hex > 0xFF {
160 match self.binary.add_word(hex) {
161 Ok(()) => {},
162 Err(be) => return Err(AsmError::Binary(be))
163 };
164 } else {
165 match self.binary.add_byte(hex as u8) {
166 Ok(()) => {},
167 Err(be) => return Err(AsmError::Binary(be))
168 };
169 }
170 } else if self.cur_token.get_type() == TokenType::LblDef {
171 self.eat(&TokenType::LblDef)?;
172 }
173 }
174
175 //and return the binary
176 return Ok(&mut self.binary);
177 }
178
179 /// Verifies the current token and gets the next token
180 ///
181 /// # Argument
182 ///
183 /// * `ttype` - The token type to verify against
184 ///
185 /// # Returns
186 ///
187 /// `Ok` if no errors occur, `Err(ParseError)` if the token
188 /// is not verified properly, or `Err(LexerError)` if an
189 /// unknown character is found
190 fn eat(&mut self, ttype: &TokenType ) -> Result<(), AsmError> {
191 //check the type
192 if self.cur_token.get_type() == *ttype {
193 let lr = self.lexer.get_next_token();
194 return match lr {
195 Err(le) => Err(AsmError::Lexer(le)),
196 Ok(t) => {
197 self.cur_token = t;
198 Ok(())
199 }
200 };
201 } else {
202 return Err(AsmError::Parser(ParseError::new(
203 ttype, &self.cur_token.get_type(),
204 self.lexer.get_line(),
205 self.lexer.get_column())));
206 }
207 }
208
209 /// Assembles an instruction
210 ///
211 /// # Returns
212 ///
213 /// The assembled instruction, wrapped in a `Result`
214 fn instruction(&mut self) -> Result<Instruction, AsmError> {
215 //save the current token
216 let save_token = self.cur_token.clone();
217
218 //parse the instruction
219 self.eat(&TokenType::Instruction)?;
220
221 //get the instruction string
222 let instr = save_token.get_value().as_text().unwrap();
223
224 //and generate the instruction object
225 return match instr.as_str() {
226 "CLS" => Ok(Instruction::CLS),
227 "RET" => Ok(Instruction::RET),
228 "JMP" => {
229 //get the label address
230 let addr = self.label()?;
231
232 //create the data
233 let data = data::JmpData::new(addr);
234
235 //and return the instruction
236 Ok(Instruction::JMP(data))
237 },
238 "CALL" => {
239 //get the label address
240 let addr = self.label()?;
241
242 //create the data
243 let data = data::CallData::new(addr);
244
245 //and return the instruction
246 Ok(Instruction::CALL(data))
247 },
248 "SKIP" => {
249 //parse the period
250 self.eat(&TokenType::Period)?;
251
252 //get the skip type
253 let st = self.skiptype()?;
254
255 //get the first argument
256 let vx = self.register()?;
257
258 //ensure that VX is not the I register
259 if vx == data::Register::I {
260 return Err(AsmError::Argument(
261 ArgError::new(&vx, "SKIP",
262 self.lexer.get_line(),
263 self.lexer.get_column())));
264 }
265
266 //parse a possible comma
267 if (st == data::SkipType::Equals) ||
268 (st == data::SkipType::NotEquals) {
269 self.eat(&TokenType::Comma)?;
270 }
271
272 //save the tokentype
273 let ttype = self.cur_token.get_type().clone();
274
275 //and generate the instruction
276 if ttype == TokenType::Register {
277 //parse the register
278 let vy = self.register()?;
279
280 //ensure that VY is not the I register
281 if vy == data::Register::I {
282 return Err(AsmError::Argument(
283 ArgError::new(&vy, "SKIP",
284 self.lexer.get_line(),
285 self.lexer.get_column())));
286 }
287
288 //ensure that the skip type is not a key variant
289 if (st == data::SkipType::KeyUp) ||
290 (st == data::SkipType::KeyDown) {
291 return Err(AsmError::Skip(
292 SkipError::new(st,
293 self.lexer.get_line(),
294 self.lexer.get_column())));
295 }
296
297 //create the data
298 let data = data::SkipData::with_register(vx,
299 vy, st);
300
301 //and return it
302 Ok(Instruction::SKIP(data))
303 } else if (ttype == TokenType::HexLit) ||
304 (ttype == TokenType::DecLit) ||
305 (ttype == TokenType::BinLit) {
306
307 //get the value
308 let nn16 = self.constant()?;
309 let nn = nn16 as u8;
310
311 //ensure that the skip type is not a key variant
312 if (st == data::SkipType::KeyUp) ||
313 (st == data::SkipType::KeyDown) {
314 return Err(AsmError::Skip(
315 SkipError::new(st,
316 self.lexer.get_line(),
317 self.lexer.get_column())));
318 }
319
320 //create the data
321 let data = data::SkipData::with_constant(vx, nn, st);
322
323 //and return the instruction
324 Ok(Instruction::SKIP(data))
325 } else {
326 //ensure that st is a key type
327 if (st != data::SkipType::KeyUp) &&
328 (st != data::SkipType::KeyDown) {
329 return Err(AsmError::Skip(
330 SkipError::new(st,
331 self.lexer.get_line(),
332 self.lexer.get_column())));
333 }
334
335 let data = data::SkipData::with_key(vx, st);
336
337 Ok(Instruction::SKIP(data))
338 }
339 },
340 "MOV" => {
341 //get the destination register
342 let vx = self.register()?;
343
344 //parse the comma
345 self.eat(&TokenType::Comma)?;
346
347 //save the token type
348 let ttype = self.cur_token.get_type().clone();
349
350 //handle source objects
351 if ttype == TokenType::Register {
352 //ensure that the VX register is not the I register
353 if vx == data::Register::I {
354 return Err(AsmError::Argument(
355 ArgError::new(&vx, "MOV",
356 self.lexer.get_line(),
357 self.lexer.get_column())));
358 }
359
360 //get the source register
361 let vy = self.register()?;
362
363 //ensure that the VY register is not the I register
364 if vy == data::Register::I {
365 return Err(AsmError::Argument(
366 ArgError::new(&vy, "MOV",
367 self.lexer.get_line(),
368 self.lexer.get_column())));
369 }
370
371 //construct the data
372 let data = data::MovData::with_register(vx, vy);
373
374 //and return the instruction
375 Ok(Instruction::MOV(data))
376 } else if ttype == TokenType::Label {
377 //ensure that vx is the `I` register
378 if vx != data::Register::I {
379 return Err(AsmError::Argument(
380 ArgError::new(&vx, "MOV",
381 self.lexer.get_line(),
382 self.lexer.get_column())));
383 }
384
385 //parse the label
386 let addr = self.label()?;
387
388 //assemble the data
389 let data = data::MovData::with_constant(vx, addr);
390
391 //and return the instruction
392 Ok(Instruction::MOV(data))
393 } else {
394 //get the constant
395 let cst = self.constant()?;
396
397 //construct the data
398 let data = data::MovData::with_constant(vx, cst);
399
400 //and return the instruction
401 Ok(Instruction::MOV(data))
402 }
403 },
404 "ADD" => {
405 //get the destination register
406 let vx = self.register()?;
407
408 //parse the comma
409 self.eat(&TokenType::Comma)?;
410
411 //determine the second argument type
412 if self.cur_token.get_type() == TokenType::Register {
413 //get the second register
414 let vy = self.register()?;
415
416 //ensure that VY is not the I register
417 if vy == data::Register::I {
418 return Err(AsmError::Argument(
419 ArgError::new(&vy, "ADD",
420 self.lexer.get_line(),
421 self.lexer.get_column())));
422 }
423
424 //construct the data
425 let data = data::AddData::with_register(vx, vy);
426
427 //and return the instruction
428 return Ok(Instruction::ADD(data));
429 } else {
430 //get the constant
431 let cst = self.constant()?;
432
433 //ensure that VX is not the I register
434 if vx == data::Register::I {
435 return Err(AsmError::Argument(
436 ArgError::new(&vx, "ADD",
437 self.lexer.get_line(),
438 self.lexer.get_column())));
439 }
440
441 //construct the data
442 let data = data::AddData::with_constant(vx, cst);
443
444 //and return the instruction
445 Ok(Instruction::ADD(data))
446 }
447 },
448 "OR" => {
449 //get the first argument register
450 let vx = self.register()?;
451
452 //ensure that it is not the I register
453 if vx == data::Register::I {
454 return Err(AsmError::Argument(
455 ArgError::new(&vx, "OR",
456 self.lexer.get_line(),
457 self.lexer.get_column())));
458 }
459
460 //parse the comma
461 self.eat(&TokenType::Comma)?;
462
463 //get the second argument register
464 let vy = self.register()?;
465
466 //ensure that it is not the I register
467 if vy == data::Register::I {
468 return Err(AsmError::Argument(
469 ArgError::new(&vy, "OR",
470 self.lexer.get_line(),
471 self.lexer.get_column())));
472 }
473
474 //construct the data
475 let data = data::OrData::new(vx, vy);
476
477 //and return the instruction
478 Ok(Instruction::OR(data))
479 },
480 "AND" => {
481 //get the first argument register
482 let vx = self.register()?;
483
484 //ensure that it is not the I register
485 if vx == data::Register::I {
486 return Err(AsmError::Argument(
487 ArgError::new(&vx, "AND",
488 self.lexer.get_line(),
489 self.lexer.get_column())));
490 }
491
492 //parse the comma
493 self.eat(&TokenType::Comma)?;
494
495 //get the second argument register
496 let vy = self.register()?;
497
498 //ensure that it is not the I register
499 if vy == data::Register::I {
500 return Err(AsmError::Argument(
501 ArgError::new(&vy, "AND",
502 self.lexer.get_line(),
503 self.lexer.get_column())));
504 }
505
506 //construct the data
507 let data = data::AndData::new(vx, vy);
508
509 //and return the instruction
510 Ok(Instruction::AND(data))
511 },
512 "XOR" => {
513 //get the first argument register
514 let vx = self.register()?;
515
516 //ensure that it is not the I register
517 if vx == data::Register::I {
518 return Err(AsmError::Argument(
519 ArgError::new(&vx, "XOR",
520 self.lexer.get_line(),
521 self.lexer.get_column())));
522 }
523
524 //parse the comma
525 self.eat(&TokenType::Comma)?;
526
527 //get the second argument register
528 let vy = self.register()?;
529
530 //ensure that it is not the I register
531 if vy == data::Register::I {
532 return Err(AsmError::Argument(
533 ArgError::new(&vy, "XOR",
534 self.lexer.get_line(),
535 self.lexer.get_column())));
536 }
537
538 //construct the data
539 let data = data::XorData::new(vx, vy);
540
541 //and return the instruction
542 Ok(Instruction::XOR(data))
543 },
544 "SUB" => {
545 //get the first argument register
546 let vx = self.register()?;
547
548 //ensure that it is not the I register
549 if vx == data::Register::I {
550 return Err(AsmError::Argument(
551 ArgError::new(&vx, "SUB",
552 self.lexer.get_line(),
553 self.lexer.get_column())));
554 }
555
556 //parse the comma
557 self.eat(&TokenType::Comma)?;
558
559 //get the second argument register
560 let vy = self.register()?;
561
562 //ensure that it is not the I register
563 if vy == data::Register::I {
564 return Err(AsmError::Argument(
565 ArgError::new(&vy, "SUB",
566 self.lexer.get_line(),
567 self.lexer.get_column())));
568 }
569
570 //construct the data
571 let data = data::SubData::new(vx, vy);
572
573 //and return the instruction
574 Ok(Instruction::SUB(data))
575 },
576 "SHR" => {
577 //get the argument register
578 let vx = self.register()?;
579
580 //ensure that it does not refer to the I register
581 if vx == data::Register::I {
582 return Err(AsmError::Argument(
583 ArgError::new(&vx, "SHR",
584 self.lexer.get_line(),
585 self.lexer.get_column())));
586 }
587
588 //construct the data
589 let data = data::ShrData::new(vx);
590
591 //and return the instruction
592 Ok(Instruction::SHR(data))
593 },
594 "SUBN" => {
595 //get the first argument register
596 let vx = self.register()?;
597
598 //ensure that it is not the I register
599 if vx == data::Register::I {
600 return Err(AsmError::Argument(
601 ArgError::new(&vx, "SUBN",
602 self.lexer.get_line(),
603 self.lexer.get_column())));
604 }
605
606 //parse the comma
607 self.eat(&TokenType::Comma)?;
608
609 //get the second argument register
610 let vy = self.register()?;
611
612 //ensure that it is not the I register
613 if vy == data::Register::I {
614 return Err(AsmError::Argument(
615 ArgError::new(&vy, "SUBN",
616 self.lexer.get_line(),
617 self.lexer.get_column())));
618 }
619
620 //construct the data
621 let data = data::SubnData::new(vx, vy);
622
623 //and return the instruction
624 Ok(Instruction::SUBN(data))
625 },
626 "SHL" => {
627 //get the argument register
628 let vx = self.register()?;
629
630 //ensure that it does not refer to the I register
631 if vx == data::Register::I {
632 return Err(AsmError::Argument(
633 ArgError::new(&vx, "SHL",
634 self.lexer.get_line(),
635 self.lexer.get_column())));
636 }
637
638 //construct the data
639 let data = data::ShlData::new(vx);
640
641 //and return the instruction
642 Ok(Instruction::SHL(data))
643 },
644 "JPC" => {
645 //determine which type of argument to use
646 if self.cur_token.get_type() == TokenType::Label {
647 let addr = self.label()?;
648 let data = data::JpcData::new(addr);
649 Ok(Instruction::JPC(data))
650 } else {
651 let addr = self.constant()?;
652 let data = data::JpcData::new(addr);
653 Ok(Instruction::JPC(data))
654 }
655 },
656 "RAND" => {
657 //get the destination register
658 let vx = self.register()?;
659
660 //ensure that it does not refer to the I register
661 if vx == data::Register::I {
662 return Err(AsmError::Argument(
663 ArgError::new(&vx, "RAND",
664 self.lexer.get_line(),
665 self.lexer.get_column())));
666 }
667
668 //parse the comma
669 self.eat(&TokenType::Comma)?;
670
671 //get the limiting value
672 let nn16 = self.constant()?;
673 let nn = (nn16 & 0xFF) as u8;
674
675 //construct the data
676 let data = data::RandData::new(vx, nn);
677
678 //and return the instruction
679 Ok(Instruction::RAND(data))
680 },
681 "DRAW" => {
682 //get the X register
683 let vx = self.register()?;
684
685 //ensure that it's not the I register
686 if vx == data::Register::I {
687 return Err(AsmError::Argument(
688 ArgError::new(&vx, "DRAW",
689 self.lexer.get_line(),
690 self.lexer.get_column())));
691 }
692
693 //parse the comma
694 self.eat(&TokenType::Comma)?;
695
696 //get the Y register
697 let vy = self.register()?;
698
699 //ensure that it's not the I register
700 if vy == data::Register::I {
701 return Err(AsmError::Argument(
702 ArgError::new(&vy, "DRAW",
703 self.lexer.get_line(),
704 self.lexer.get_column())));
705 }
706
707 //parse the comma
708 self.eat(&TokenType::Comma)?;
709
710 //get the height
711 let h16 = self.constant()?;
712 let h = (h16 & 0xF) as u8;
713
714 //construct the data
715 let data = data::DrawData::new(vx, vy, h);
716
717 //and return the instruction
718 Ok(Instruction::DRAW(data))
719 },
720 "GDL" => {
721 //get the argument register
722 let vx = self.register()?;
723
724 //ensure that it does not refer to the I register
725 if vx == data::Register::I {
726 return Err(AsmError::Argument(
727 ArgError::new(&vx, "GDL",
728 self.lexer.get_line(),
729 self.lexer.get_column())));
730 }
731
732 //construct the data
733 let data = data::GdlData::new(vx);
734
735 //and return the instruction
736 Ok(Instruction::GDL(data))
737 },
738 "KEY" => {
739 //get the argument register
740 let vx = self.register()?;
741
742 //ensure that it does not refer to the I register
743 if vx == data::Register::I {
744 return Err(AsmError::Argument(
745 ArgError::new(&vx, "KEY",
746 self.lexer.get_line(),
747 self.lexer.get_column())));
748 }
749
750 //construct the data
751 let data = data::KeyData::new(vx);
752
753 //and return the instruction
754 Ok(Instruction::KEY(data))
755 },
756 "SDL" => {
757 //get the argument register
758 let vx = self.register()?;
759
760 //ensure that it does not refer to the I register
761 if vx == data::Register::I {
762 return Err(AsmError::Argument(
763 ArgError::new(&vx, "SDL",
764 self.lexer.get_line(),
765 self.lexer.get_column())));
766 }
767
768 //construct the data
769 let data = data::SdlData::new(vx);
770
771 //and return the instruction
772 Ok(Instruction::SDL(data))
773 },
774 "SND" => {
775 //get the argument register
776 let vx = self.register()?;
777
778 //ensure that it does not refer to the I register
779 if vx == data::Register::I {
780 return Err(AsmError::Argument(
781 ArgError::new(&vx, "SND",
782 self.lexer.get_line(),
783 self.lexer.get_column())));
784 }
785
786 //construct the data
787 let data = data::SndData::new(vx);
788
789 //and return the instruction
790 Ok(Instruction::SND(data))
791 },
792 "SCH" => {
793 //get the argument register
794 let vx = self.register()?;
795
796 //ensure that it does not refer to the I register
797 if vx == data::Register::I {
798 return Err(AsmError::Argument(
799 ArgError::new(&vx, "SCH",
800 self.lexer.get_line(),
801 self.lexer.get_column())));
802 }
803
804 //construct the data
805 let data = data::SchData::new(vx);
806
807 //and return the instruction
808 Ok(Instruction::SCH(data))
809 },
810 "BCD" => {
811 //get the argument register
812 let vx = self.register()?;
813
814 //ensure that it does not refer to the I register
815 if vx == data::Register::I {
816 return Err(AsmError::Argument(
817 ArgError::new(&vx, "BCD",
818 self.lexer.get_line(),
819 self.lexer.get_column())));
820 }
821
822 //construct the data
823 let data = data::BcdData::new(vx);
824
825 //and return the instruction
826 Ok(Instruction::BCD(data))
827 },
828 "RDP" => {
829 //get the argument register
830 let vx = self.register()?;
831
832 //ensure that it does not refer to the I register
833 if vx == data::Register::I {
834 return Err(AsmError::Argument(
835 ArgError::new(&vx, "RDP",
836 self.lexer.get_line(),
837 self.lexer.get_column())));
838 }
839
840 //construct the data
841 let data = data::RdpData::new(vx);
842
843 //and return the instruction
844 Ok(Instruction::RDP(data))
845 },
846 "RLD" => {
847 //get the argument register
848 let vx = self.register()?;
849
850 //ensure that it does not refer to the I register
851 if vx == data::Register::I {
852 return Err(AsmError::Argument(
853 ArgError::new(&vx, "RLD",
854 self.lexer.get_line(),
855 self.lexer.get_column())));
856 }
857
858 //construct the data
859 let data = data::RldData::new(vx);
860
861 //and return the instruction
862 Ok(Instruction::RLD(data))
863 },
864 _ => Err(AsmError::Opcode(OpcodeError::new(instr.as_str(),
865 self.lexer.get_line(),
866 self.lexer.get_column())))
867 };
868 }
869
870 /// Assembles a constant literal
871 ///
872 /// # Returns
873 ///
874 /// The constant literal, wrapped in a `Result`
875 fn constant(&mut self) -> Result<u16, AsmError> {
876 //handle different types
877 if self.cur_token.get_type() == TokenType::DecLit {
878 let dec = self.dec_lit()?;
879 return Ok(dec);
880 } else if self.cur_token.get_type() == TokenType::HexLit {
881 let hex = self.hex_lit()?;
882 return Ok(hex);
883 } else { //binary literal expected
884 let bin = self.bin_lit()?;
885 return Ok(bin as u16);
886 }
887 }
888
889 /// Assembles a label reference
890 ///
891 /// # Returns
892 ///
893 /// The address of the label, wrapped in a `Result`
894 fn label(&mut self) -> Result<u16, AsmError> {
895 //save the token
896 let save_token = self.cur_token.clone();
897
898 //parse the label
899 self.eat(&TokenType::Label)?;
900
901 //get the label string
902 let lstr = save_token.get_value().as_text().unwrap();
903
904 //and get the address
905 return match self.addrs.get_entry(&lstr) {
906 Ok(addr) => Ok(addr),
907 Err(ae) => Err(AsmError::Address(ae))
908 };
909 }
910
911
912 /// Assembles a register reference
913 ///
914 /// # Returns
915 ///
916 /// The name of the assembled register, wrapped in a `Result`
917 fn register(&mut self) -> Result<data::Register, AsmError> {
918 //save the current token
919 let save_token = self.cur_token.clone();
920
921 //parse the register
922 self.eat(&TokenType::Register)?;
923
924 //get the text from the token
925 let rtext = save_token.get_value().as_text().unwrap();
926
927 //get the first and second chars of that text
928 let fchar = rtext.chars().nth(0).unwrap();
929
930 //and generate the register
931 if fchar == 'I' {
932 return Ok(data::Register::I);
933 } else {
934 let schar = rtext.chars().nth(1).unwrap();
935 return match schar {
936 '0' => Ok(data::Register::V0),
937 '1' => Ok(data::Register::V1),
938 '2' => Ok(data::Register::V2),
939 '3' => Ok(data::Register::V3),
940 '4' => Ok(data::Register::V4),
941 '5' => Ok(data::Register::V5),
942 '6' => Ok(data::Register::V6),
943 '7' => Ok(data::Register::V7),
944 '8' => Ok(data::Register::V8),
945 '9' => Ok(data::Register::V9),
946 'A' => Ok(data::Register::VA),
947 'B' => Ok(data::Register::VB),
948 'C' => Ok(data::Register::VC),
949 'D' => Ok(data::Register::VD),
950 'E' => Ok(data::Register::VE),
951 'F' => Ok(data::Register::VF),
952 _ => panic!("Bad register index {}", schar)
953 };
954 }
955 }
956
957 /// Assembles a skip condition
958 ///
959 /// # Returns
960 ///
961 /// The assembled `SkipType`, wrapped in a `Result`
962 fn skiptype(&mut self) -> Result<data::SkipType, AsmError> {
963 //save the current token
964 let save_token = self.cur_token.clone();
965
966 //get the next token
967 self.eat(&TokenType::SkipCond)?;
968
969 //get the skip string
970 let skstr = save_token.get_value().as_text().unwrap();
971
972 //and generate the skip condition
973 return match skstr.as_str() {
974 "EQ" => Ok(data::SkipType::Equals),
975 "NE" => Ok(data::SkipType::NotEquals),
976 "KD" => Ok(data::SkipType::KeyDown),
977 "KU" => Ok(data::SkipType::KeyUp),
978 _ => panic!("Unknown skip type {}", skstr)
979 };
980 }
981
982 /// Assembles a decimal integer literal
983 ///
984 /// # Returns
985 ///
986 /// The assembled literal, wrapped in a `Result`
987 fn dec_lit(&mut self) -> Result<u16, AsmError> {
988 //save the current token
989 let save_token = self.cur_token.clone();
990
991 //parse the literal
992 self.eat(&TokenType::DecLit)?;
993
994 //and return the token value
995 return Ok(save_token.get_value().as_word().unwrap())
996 }
997
998 /// Assembles a hex integer literal
999 ///
1000 /// # Returns
1001 ///
1002 /// The assembled literal, wrapped in a `Result`
1003 fn hex_lit(&mut self) -> Result<u16, AsmError> {
1004 //save the current token
1005 let save_token = self.cur_token.clone();
1006
1007 //parse the literal
1008 self.eat(&TokenType::HexLit)?;
1009
1010 //and return the token value
1011 return Ok(save_token.get_value().as_word().unwrap());
1012 }
1013
1014 /// Assembles a binary literal
1015 ///
1016 /// # Returns
1017 ///
1018 /// The assembled literal, wrapped in a `Result`
1019 fn bin_lit(&mut self) -> Result<u8, AsmError> {
1020 //save the current token
1021 let save_token = self.cur_token.clone();
1022
1023 //parse the literal
1024 self.eat(&TokenType::BinLit)?;
1025
1026 //and return the token value
1027 return Ok(save_token.get_value().as_byte().unwrap());
1028 }
1029}
1030
1031//unit tests
1032#[cfg(test)]
1033mod tests {
1034 //import the Assembler struct
1035 use super::*;
1036
1037 //define the program
1038 const CODE: &str = "CLS
1039 MOV V0, #0
1040 MOV V1, #15
1041 KEY V2
1042 SCH V2
1043 _loop:
1044 DRAW V0, V1, #5
1045 ADD V0, #1
1046 JMP _loop";
1047
1048 //this test checks code generation
1049 #[test]
1050 fn test_codegen() {
1051 let mut asm = Assembler::new(CODE, "test.c8").unwrap();
1052 let mut i = asm.instruction().unwrap();
1053 match i {
1054 Instruction::CLS => {},
1055 _ => panic!("Expected CLS")
1056 };
1057 i = asm.instruction().unwrap();
1058 match i {
1059 Instruction::MOV(_data) => {},
1060 _ => panic!("Expected MOV")
1061 };
1062 i = asm.instruction().unwrap();
1063 match i {
1064 Instruction::MOV(_data) => {},
1065 _ => panic!("Expected MOV")
1066 };
1067 i = asm.instruction().unwrap();
1068 match i {
1069 Instruction::KEY(_data) => {},
1070 _ => panic!("Expected KEY")
1071 };
1072 i = asm.instruction().unwrap();
1073 match i {
1074 Instruction::SCH(_data) => {},
1075 _ => panic!("Expected SCH")
1076 };
1077 asm.eat(&TokenType::LblDef).unwrap();
1078 i = asm.instruction().unwrap();
1079 match i {
1080 Instruction::DRAW(_data) => {},
1081 _ => panic!("Expected DRAW")
1082 };
1083 i = asm.instruction().unwrap();
1084 match i {
1085 Instruction::ADD(_data) => {},
1086 _ => panic!("Expected ADD")
1087 };
1088 i = asm.instruction().unwrap();
1089 match i {
1090 Instruction::JMP(_data) => {},
1091 _ => panic!("Expected JMP")
1092 };
1093 }
1094}
1095
1096//end of file