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