rs6502/assembler/
parser.rs

1use std;
2use std::collections::HashMap;
3use std::iter::Peekable;
4
5use byteorder::{ByteOrder, LittleEndian};
6
7use ::opcodes::{AddressingMode, OpCode};
8use assembler::token::{ImmediateBase, LexerToken, ParserToken};
9
10#[derive(Debug, PartialEq)]
11pub struct ParserError {
12    pub message: String,
13}
14
15impl ParserError {
16    fn expected_immediate(line: u32) -> ParserError {
17        ParserError::from(format!("Immediate value expected. Line {}", line))
18    }
19
20    fn expected_instruction(line: u32) -> ParserError {
21        ParserError::from(format!("Instruction expected. Line {}", line))
22    }
23
24    fn invalid_opcode_addressing_mode_combination(line: u32) -> ParserError {
25        ParserError::from(format!("Invalid addressing mode for opcode. Line {}", line))
26    }
27
28    fn unexpected_eol(line: u32) -> ParserError {
29        ParserError::from(format!("Unexpected end of line. Line {}", line))
30    }
31
32    fn expected_eol(line: u32) -> ParserError {
33        ParserError::from(format!("Expected end of line. Line {}", line))
34    }
35
36    fn cannot_parse_address(line: u32) -> ParserError {
37        ParserError::from(format!("Unable to parse address. Line {}", line))
38    }
39
40    fn unexpected_token(line: u32) -> ParserError {
41        ParserError::from(format!("Unexpected token. Line {}", line))
42    }
43
44    fn address_out_of_bounds(line: u32) -> ParserError {
45        ParserError::from(format!("Address too large. Line {}", line))
46    }
47
48    fn expected_address(line: u32) -> ParserError {
49        ParserError::from(format!("Unexpected token, expected address. Line {}", line))
50    }
51
52    fn cannot_parse_immediate(line: u32) -> ParserError {
53        ParserError::from(format!("Unable to parse immedate value. Line {}", line))
54    }
55
56    fn unknown_identifier(line: u32) -> ParserError {
57        ParserError::from(format!("Unknown identifier. Line {}", line))
58    }
59}
60
61impl From<String> for ParserError {
62    fn from(error: String) -> ParserError {
63        ParserError { message: error }
64    }
65}
66
67impl<'a> From<&'a str> for ParserError {
68    fn from(error: &str) -> ParserError {
69        ParserError { message: error.into() }
70    }
71}
72
73#[derive(Clone, Debug, PartialEq)]
74pub struct Variable(LexerToken);
75
76pub struct Parser {
77    symbol_table: HashMap<String, Variable>,
78    line: u32,
79}
80
81/// Parser processes a list of 6502 Assembly tokens
82impl Parser {
83    pub fn new() -> Parser {
84        Parser {
85            symbol_table: HashMap::new(),
86            line: 0,
87        }
88    }
89
90    pub fn parse(&mut self, tokens: Vec<Vec<LexerToken>>) -> Result<Vec<ParserToken>, ParserError> {
91        let mut result = Vec::new();
92
93        for line in &tokens {
94            let mut added_label = false;
95            self.line += 1;
96
97            let mut peeker = line.iter().peekable();
98
99            // Skip blank lines
100            if let None = peeker.peek() {
101                continue;
102            }
103
104            let next = *peeker.peek().unwrap();
105
106            if let &LexerToken::Ident(ref ident) = next {
107                // Check if this is an opcode
108                if Self::is_opcode(ident.clone()) {
109                    // Yep its an opcode, lets figure out its addressing mode
110                    let mut opcode = self.consume_opcode(&mut peeker, ident.clone())?;
111                    result.append(&mut opcode);
112                } else {
113                    // Skip the ident and we'll check what is next
114                    let original_ident = peeker.next().unwrap();
115                    // if there is nothing else - lets mark this as a Label and move on
116                    if let None = peeker.peek() {
117                        result.push(ParserToken::Label(ident.clone()));
118                        continue;
119                    }
120
121                    // A colon after the ident also indicates a label
122                    let next = *peeker.peek().unwrap();
123                    if let &LexerToken::Colon = next {
124                        result.push(ParserToken::Label(ident.clone()));
125                        continue;
126                    }
127
128                    // Is the next one a label as well? Thats an error:
129                    if let &LexerToken::Ident(ref ident) = next {
130                        // Lets add the original as a label
131                        if let &LexerToken::Ident(ref original_ident) = original_ident {
132                            result.push(ParserToken::Label(original_ident.clone()));
133                        }
134
135                        if !Self::is_opcode(ident.clone()) {
136                            return Err(ParserError::expected_instruction(self.line));
137                        } else {
138                            // Oh it is an opcode after the label - consume it
139                            let mut opcode = self.consume_opcode(&mut peeker, ident.clone())?;
140                            result.append(&mut opcode);
141                        }
142                    } else if let &LexerToken::Assignment = next {
143                        // Its a variable assignment - lets store the variable in the symbol table
144                        peeker.next(); // Jump the assignment operator
145                        if let None = peeker.peek() {
146                            return Err(ParserError::unexpected_eol(self.line));
147                        }
148
149                        let next = *peeker.peek().unwrap();
150                        if let &LexerToken::Address(ref address) = next {
151                            self.symbol_table
152                                .insert(ident.clone(),
153                                        Variable(LexerToken::Address(address.clone())));
154                        } else if let &LexerToken::Ident(ref var_ident) = next {
155                            // Its another variable
156                            self.symbol_table
157                                .insert(ident.clone(),
158                                        Variable(LexerToken::Ident(var_ident.clone())));
159                        }
160                    }
161                }
162            } else if let &LexerToken::Period = next {
163                // Its a directive? Lets make sure:
164                peeker.next();
165                if let None = peeker.peek() {
166                    return Err(ParserError::unexpected_eol(self.line));
167                }
168
169                let next = *peeker.peek().unwrap();
170                if let &LexerToken::Ident(ref directive) = next {
171                    // Lets check if its a valid directive:
172                    let directive = directive.to_uppercase();
173                    match &directive[..] {
174                        "ORG" => {
175                            result.push(self.consume_org_directive(&mut peeker)?);
176                        }
177                        "BYTE" => {
178                            result.push(self.consume_byte_directive(&mut peeker)?);
179                        }
180                        _ => return Err(ParserError::unknown_identifier(self.line)),
181                    }
182                }
183            }
184        }
185
186        Ok(result)
187    }
188
189    fn is_opcode<S>(mnemonic: S) -> bool
190        where S: Into<String>
191    {
192        if let Some(opcode) = OpCode::from_mnemonic(mnemonic) {
193            true
194        } else {
195            false
196        }
197    }
198
199    fn consume_opcode<'a, I, S>(&mut self,
200                                mut peeker: &mut Peekable<I>,
201                                ident: S)
202                                -> Result<Vec<ParserToken>, ParserError>
203        where I: Iterator<Item = &'a LexerToken>,
204              S: Into<String> + std::fmt::Display + Clone
205    {
206        // Jump over the opcode
207        peeker.next();
208
209        // If there is nothing else after this opcode.. lets check if there is
210        // a matching opcode with an implied addressing mode
211        if let None = peeker.peek() {
212            if let Some(opcode) =
213                   OpCode::from_mnemonic_and_addressing_mode(ident.clone(), AddressingMode::Implied) {
214                return Ok(vec![ParserToken::OpCode(opcode)]);
215            } else if let Some(opcode) =
216                          OpCode::from_mnemonic_and_addressing_mode(ident.clone(),
217                                                                    AddressingMode::Accumulator) {
218                return Ok(vec![ParserToken::OpCode(opcode)]);
219            } else {
220                return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
221            }
222        } else {
223            // Check the next token, is it an address or identifier?
224            let mut next = (*peeker.peek().unwrap()).clone();
225            next = if let LexerToken::Ident(ref label) = next {
226                // Lets see if its a variable?
227                if let Ok(variable) = self.get_variable_value(label.clone()) {
228                    variable.clone().0
229                } else {
230                    // takes care of this later
231                    let ident = ident.clone().into().to_uppercase();
232                    let addressing_mode = if ident == "JMP" || ident == "JSR" {
233                        AddressingMode::Absolute
234                    } else {
235                        AddressingMode::Relative
236                    };
237
238                    if let Some(opcode) =
239                           OpCode::from_mnemonic_and_addressing_mode(ident.clone(), addressing_mode) {
240                        return Ok(vec![ParserToken::OpCode(opcode),
241                                       ParserToken::LabelArg(label.clone())]);
242                    } else {
243                        return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
244                    }
245                }
246            } else {
247                next.clone()
248            };
249            if let LexerToken::Address(ref address) = next {
250                // Its an address. What sort of address?
251                if address.len() <= 4 {
252                    // Its zero-page or absolute.. lets try and convert it to a raw byte
253                    let addressing_mode = if address.len() <= 2 {
254                        // Its a 1 byte address
255                        AddressingMode::ZeroPage
256                    } else {
257                        AddressingMode::Absolute
258                    };
259                    let bytes = self.parse_address_bytes(address)?;
260                    // consume the address and peek what is next:
261                    peeker.next();
262                    if let None = peeker.peek() {
263                        // Nothing else.. find an opcode with this ident and addressing mode
264                        if let Some(opcode) =
265                               OpCode::from_mnemonic_and_addressing_mode(ident, addressing_mode) {
266                            // We found one..
267                            let mut final_vec = vec![ParserToken::OpCode(opcode)];
268                            // Push the address bytes into the result
269                            for b in bytes {
270                                final_vec.push(ParserToken::RawByte(b));
271                            }
272                            return Ok(final_vec);
273                        } else {
274                            return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
275                        }
276                    }
277
278                    // There is something after this address - if its
279                    // a comma, then we're peachy. If its something else.. Thats
280                    // an error.
281                    let next = *peeker.peek().unwrap();
282                    if let &LexerToken::Comma = next {
283                        // Yes, its a comma. Consume it and check what is next
284                        peeker.next();
285                        // If theres nothing after the comma thats an error
286                        if let None = peeker.peek() {
287                            return Err(ParserError::unexpected_eol(self.line));
288                        }
289
290                        let next = *peeker.peek().unwrap();
291                        if let &LexerToken::Ident(ref register) = next {
292                            let register = register.to_uppercase();
293                            if register != "X" && register != "Y" {
294                                return Err(ParserError::unexpected_token(self.line));
295                            }
296                            let addressing_mode = if register == "X" {
297                                if addressing_mode == AddressingMode::ZeroPage {
298                                    AddressingMode::ZeroPageX
299                                } else {
300                                    AddressingMode::AbsoluteX
301                                }
302                            } else {
303                                if addressing_mode == AddressingMode::ZeroPage {
304                                    AddressingMode::ZeroPageY
305                                } else {
306                                    AddressingMode::AbsoluteY
307                                }
308                            };
309                            if let Some(opcode) =
310                                   OpCode::from_mnemonic_and_addressing_mode(ident, addressing_mode) {
311                                // We found one..
312                                let mut final_vec = vec![ParserToken::OpCode(opcode)];
313                                // Push the address bytes into the result
314                                for b in bytes {
315                                    final_vec.push(ParserToken::RawByte(b));
316                                }
317                                return Ok(final_vec);
318                            } else {
319                                return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
320                            }
321                        } else {
322                            return Err(ParserError::unexpected_token(self.line));
323                        }
324                    } else {
325                        return Err(ParserError::unexpected_token(self.line));
326                    }
327                    let next = *peeker.peek().unwrap();
328
329                } else {
330                    return Err(ParserError::cannot_parse_address(self.line));
331                }
332            } else if let LexerToken::OpenParenthesis = next {
333                // We're moving into Indirect memory addressing
334                let addressing_mode = AddressingMode::Indirect;
335                peeker.next(); // skip the opening paren
336
337                // If we have nothing else, thats an error
338                if let None = peeker.peek() {
339                    return Err(ParserError::unexpected_eol(self.line));
340                }
341
342                // Is the next thing an address?
343                let mut next = (*peeker.peek().unwrap()).clone();
344                next = if let LexerToken::Ident(ref label) = next {
345                    // Lets see if its a variable?
346                    if let Ok(variable) = self.get_variable_value(label.clone()) {
347                        variable.clone().0
348                    } else {
349                        next.clone()
350                    }
351                } else {
352                    next.clone()
353                };
354                if let LexerToken::Address(ref address) = next {
355                    if address.len() > 4 {
356                        return Err(ParserError::address_out_of_bounds(self.line));
357                    }
358
359                    let bytes = self.parse_address_bytes(address)?;
360
361                    // The address is the right length - lets jump over that and peek next
362                    peeker.next();
363                    if let None = peeker.peek() {
364                        return Err(ParserError::unexpected_eol(self.line));
365                    }
366                    let next = *peeker.peek().unwrap();
367                    if let &LexerToken::Comma = next {
368                        // If its a comma - lets target IndirectX
369                        peeker.next(); // skip the comma
370                        if let None = peeker.peek() {
371                            return Err(ParserError::unexpected_eol(self.line));
372                        }
373
374                        let next = *peeker.peek().unwrap();
375                        if let &LexerToken::Ident(ref register) = next {
376                            let register = register.to_uppercase();
377                            if register != "X" {
378                                return Err(ParserError::unexpected_token(self.line));
379                            }
380
381                            peeker.next(); // Jump over the X
382
383                            if let None = peeker.peek() {
384                                return Err(ParserError::unexpected_eol(self.line));
385                            }
386
387                            let next = *peeker.peek().unwrap();
388                            if let &LexerToken::CloseParenthesis = next {
389                                peeker.next();
390                                // Lets make sure we can find an appropriate opcode
391                                if let Some(opcode) = OpCode::from_mnemonic_and_addressing_mode(ident, AddressingMode::IndirectX) {
392                                    // We have everything we need now.. lets return an IndirectX opcode
393                                    // accompanied by the address
394                                    return Ok(vec![ParserToken::OpCode(opcode), ParserToken::RawByte(bytes[0])]);
395                                } else {
396                                    return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
397                                }
398                            } else {
399                                return Err(ParserError::unexpected_token(self.line));
400                            }
401                        } else {
402                            return Err(ParserError::unexpected_token(self.line));
403                        }
404                    } else if let &LexerToken::CloseParenthesis = next {
405                        // We're headed for Indirect or IndirectY ..
406                        let addressing_mode = AddressingMode::IndirectY;
407                        peeker.next(); // Skip the closing paren
408
409                        if let None = peeker.peek() {
410                            // If this is the end.. then lets check if this
411                            // is the indirect jump: JMP ($0000)
412                            if let Some(opcode) = OpCode::from_mnemonic_and_addressing_mode(ident, AddressingMode::Indirect) {
413                                // Yep, we've found the only Indirect opcode
414                                // Lets make sure the address is 16-bit
415                                if address.len() != 4 {
416                                    return Err(ParserError::address_out_of_bounds(self.line));
417                                }
418                                let mut final_vec = vec![ParserToken::OpCode(opcode)];
419                                for b in bytes {
420                                    final_vec.push(ParserToken::RawByte(b));
421                                }
422                                return Ok(final_vec);
423                            } else {
424                                return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
425                            }
426                        }
427
428                        // Lets check for a comma
429                        let next = *peeker.peek().unwrap();
430                        if let &LexerToken::Comma = next {
431                            // Great, lets continue
432                            peeker.next();  // Skip the comma
433                            if let None = peeker.peek() {
434                                return Err(ParserError::unexpected_eol(self.line));
435                            }
436
437                            let next = *peeker.peek().unwrap();
438                            if let &LexerToken::Ident(ref register) = next {
439                                let register = register.to_uppercase();
440                                // If its not IndirectY .. thats a problem
441                                if register != "Y" {
442                                    return Err(ParserError::unexpected_token(self.line));
443                                }
444                                if let Some(opcode) = OpCode::from_mnemonic_and_addressing_mode(ident, AddressingMode::IndirectY) {
445                                    // Yep, we've found the only Indirect opcode
446                                    let mut final_vec = vec![ParserToken::OpCode(opcode)];
447                                    for b in bytes {
448                                        final_vec.push(ParserToken::RawByte(b));
449                                    }
450
451                                    return Ok(final_vec);
452                                } else {
453                                    return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
454                                }
455                            } else {
456                                return Err(ParserError::unexpected_token(self.line));
457                            }
458                        } else {
459                            return Err(ParserError::unexpected_token(self.line));
460                        }
461                    } else {
462                        return Err(ParserError::unexpected_token(self.line));
463                    }
464                } else {
465                    return Err(ParserError::cannot_parse_address(self.line));
466                }
467            } else if let LexerToken::Immediate(ref immediate, base) = next {
468                peeker.next(); // Jump over the immediate
469                if let Ok(val) = u8::from_str_radix(&immediate[..],
470                                                    if base == ImmediateBase::Base10 {
471                                                        10
472                                                    } else {
473                                                        16
474                                                    }) {
475                    if let Some(opcode) =
476                           OpCode::from_mnemonic_and_addressing_mode(ident,
477                                                                     AddressingMode::Immediate) {
478                        return Ok(vec![ParserToken::OpCode(opcode), ParserToken::RawByte(val)]);
479                    } else {
480                        return Err(ParserError::invalid_opcode_addressing_mode_combination(self.line));
481                    }
482                } else {
483                    return Err(ParserError::cannot_parse_immediate(self.line));
484                }
485            } else {
486                return Err(ParserError::expected_address(self.line));
487            }
488        }
489
490        unreachable!();
491    }
492
493    fn consume_org_directive<'a, I>(&mut self,
494                                    mut peeker: &mut Peekable<I>)
495                                    -> Result<ParserToken, ParserError>
496        where I: Iterator<Item = &'a LexerToken>
497    {
498        // Jump over the directive
499        peeker.next();
500        if let None = peeker.peek() {
501            return Err(ParserError::expected_address(self.line));
502        }
503
504        let next = peeker.next().unwrap();
505
506        if let &LexerToken::Address(ref address) = next {
507            let bytes = self.parse_address_bytes(address)?;
508            return Ok(ParserToken::OrgDirective(LittleEndian::read_u16(&bytes)));
509        } else {
510            return Err(ParserError::expected_address(self.line));
511        }
512    }
513
514    fn consume_byte_directive<'a, I>(&mut self,
515                                     mut peeker: &mut Peekable<I>)
516                                     -> Result<ParserToken, ParserError>
517        where I: Iterator<Item = &'a LexerToken>
518    {
519        let mut result = Vec::new();
520
521        // Jump over the directive
522        peeker.next();
523        if let None = peeker.peek() {
524            return Err(ParserError::expected_immediate(self.line));
525        }
526
527        loop {
528            let mut next = peeker.next().unwrap();
529            if let &LexerToken::Ident(ref ident) = next {
530                let variable = self.get_variable_value(ident.clone())?;
531                if let LexerToken::Immediate(ref value, base) = variable.0 {
532                    let immediate = self.unwrap_immediate(&value[..], base);
533                    result.push(immediate);
534                } else {
535                    return Err(ParserError::expected_immediate(self.line));
536                }
537            } else if let &LexerToken::Immediate(ref value, base) = next {
538                let immediate = self.unwrap_immediate(&value[..], base);
539                result.push(immediate);
540            } else {
541                return Err(ParserError::expected_immediate(self.line));
542            }
543
544            // Check if the next thing is a comma. If it is, consume it and go again
545            if let None = peeker.peek() {
546                break;
547            }
548
549            let next = peeker.next().unwrap();
550            if let &LexerToken::Comma = next {
551                // Awesome, go again
552            } else {
553                break;
554            }
555        }
556
557        Ok(ParserToken::RawBytes(result))
558    }
559
560    fn unwrap_immediate<S>(&self, value: S, base: ImmediateBase) -> u8
561        where S: Into<String>
562    {
563        let base = match base {
564            ImmediateBase::Base10 => 10,
565            ImmediateBase::Base16 => 16,
566        };
567
568        let value = value.into();
569        let immediate = u8::from_str_radix(&value[..], base).unwrap();
570
571        immediate
572    }
573
574    fn parse_address_bytes(&self, address: &str) -> Result<Vec<u8>, ParserError> {
575        if let Ok(addr) = u16::from_str_radix(address, 16) {
576            if address.len() <= 2 {
577                return Ok(vec![addr as u8]);
578            } else {
579                let low_byte = addr as u8;
580                let high_byte = (addr >> 0x08) as u8;
581                return Ok(vec![low_byte, high_byte]);
582            }
583        } else {
584            Err(ParserError::cannot_parse_address(self.line))
585        }
586    }
587
588    fn get_variable_value<S>(&self, ident: S) -> Result<Variable, ParserError>
589        where S: Into<String>
590    {
591        let ident = ident.into();
592
593        if let Some(ref var) = self.symbol_table.get(&ident) {
594            let var = var.clone();
595            if let LexerToken::Ident(ref ident) = var.0 {
596                // If this is _yet another_ variable .. recursively find its value:
597                return self.get_variable_value(ident.clone());
598            } else {
599                return Ok(Variable(var.clone().0));
600            }
601        } else {
602            return Err(ParserError::unknown_identifier(self.line));
603        }
604    }
605}
606
607#[cfg(test)]
608mod tests {
609    use super::*;
610    use ::assembler::token::{ImmediateBase, LexerToken, ParserToken};
611    use ::opcodes::{AddressingMode, OpCode};
612
613    #[test]
614    fn can_parse_labels_via_lonely_label() {
615        let tokens = vec![vec![LexerToken::Ident("MAIN".into())],
616                          vec![LexerToken::Ident("START".into())]];
617
618        let mut parser = Parser::new();
619        let result = parser.parse(tokens).unwrap();
620
621        assert_eq!(&[ParserToken::Label("MAIN".into()), ParserToken::Label("START".into())],
622                   &result[..]);
623    }
624
625    #[test]
626    fn can_parse_labels_via_colon_terminator() {
627        let tokens = vec![vec![LexerToken::Ident("MAIN".into())], vec![LexerToken::Colon]];
628
629        let mut parser = Parser::new();
630        let result = parser.parse(tokens).unwrap();
631
632        assert_eq!(&[ParserToken::Label("MAIN".into())], &result[..]);
633    }
634
635    #[test]
636    fn can_parse_opcodes_after_labels_on_one_line() {
637        let tokens = vec![vec![LexerToken::Ident("MAIN".into()),
638                               LexerToken::Ident("LDA".into()),
639                               LexerToken::Address("4400".into())]];
640
641        let mut parser = Parser::new();
642        let result = parser.parse(tokens).unwrap();
643
644        assert_eq!(&[ParserToken::Label("MAIN".into()),
645                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("LDA", AddressingMode::Absolute).unwrap()),
646                     ParserToken::RawByte(0),
647                     ParserToken::RawByte(68)],
648                   &result[..]);
649    }
650
651    #[test]
652    fn can_parse_double_labels_on_one_line() {
653        let tokens = vec![vec![LexerToken::Ident("MAIN".into()),
654                               LexerToken::Ident("START".into())]];
655
656        let mut parser = Parser::new();
657        let result = parser.parse(tokens);
658
659        assert_eq!(Err(ParserError::expected_instruction(1)), result);
660    }
661
662    #[test]
663    fn can_parse_opcode_with_implied_addressing_mode() {
664        let tokens = vec![vec![LexerToken::Ident("CLC".into())]];
665
666        let mut parser = Parser::new();
667        let result = parser.parse(tokens).unwrap();
668
669        assert_eq!(&[ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("CLC", AddressingMode::Implied).unwrap())], &result[..]);
670    }
671
672    #[test]
673    fn can_parse_opcode_with_correct_absolute_x_addressing_mode() {
674        let tokens = vec![vec![LexerToken::Ident("MAIN".into()),
675                               LexerToken::Ident("LDA".into()),
676                               LexerToken::Address("4400".into()),
677                               LexerToken::Comma,
678                               LexerToken::Ident("X".into())]];
679
680        let mut parser = Parser::new();
681        let result = parser.parse(tokens).unwrap();
682
683        assert_eq!(&[ParserToken::Label("MAIN".into()),
684                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("LDA", AddressingMode::AbsoluteX).unwrap()),
685                     ParserToken::RawByte(0),
686                     ParserToken::RawByte(68)],
687                   &result[..]);
688    }
689
690    #[test]
691    fn errors_on_incorrect_zero_page_y_usage() {
692        // LDA does not support the ZeroPageY addressing mode
693        let tokens = vec![vec![LexerToken::Ident("LDA".into()),
694                               LexerToken::Address("44".into()),
695                               LexerToken::Comma,
696                               LexerToken::Ident("Y".into())]];
697
698        let mut parser = Parser::new();
699        let result = parser.parse(tokens);
700
701        assert_eq!(Err(ParserError::invalid_opcode_addressing_mode_combination(1)),
702                   result);
703    }
704
705    #[test]
706    fn can_handle_correct_zero_page_y_usage() {
707        // LDX does support the ZeroPageY addressing mode
708        let tokens = vec![vec![LexerToken::Ident("LDX".into()),
709                               LexerToken::Address("44".into()),
710                               LexerToken::Comma,
711                               LexerToken::Ident("Y".into())]];
712
713        let mut parser = Parser::new();
714        let result = parser.parse(tokens).unwrap();
715
716        assert_eq!(&[
717                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("LDX", AddressingMode::ZeroPageY).unwrap()),
718                     ParserToken::RawByte(68)],
719                   &result[..]);
720    }
721
722    #[test]
723    fn can_parse_indirect_x_addressing() {
724        let tokens = vec![vec![LexerToken::Ident("LDA".into()),
725                               LexerToken::OpenParenthesis,
726                               LexerToken::Address("44".into()),
727                               LexerToken::Comma,
728                               LexerToken::Ident("X".into()),
729                               LexerToken::CloseParenthesis]];
730
731        let mut parser = Parser::new();
732        let result = parser.parse(tokens).unwrap();
733
734        assert_eq!(&[
735                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("LDA", AddressingMode::IndirectX).unwrap()),
736                     ParserToken::RawByte(68)],
737                   &result[..]);
738    }
739
740    #[test]
741    fn errors_on_incorrect_indirect_x_addressing() {
742        let tokens = vec![vec![LexerToken::Ident("LDA".into()),
743                               LexerToken::OpenParenthesis,
744                               LexerToken::Address("44".into()),
745                               LexerToken::Comma,
746                               LexerToken::Ident("B".into()),
747                               LexerToken::CloseParenthesis]];
748
749        let mut parser = Parser::new();
750        let result = parser.parse(tokens);
751
752        assert_eq!(Err(ParserError::unexpected_token(1)), result);
753    }
754
755    #[test]
756    fn errors_on_indirect_addressing_early_eol() {
757        let tokens = vec![vec![LexerToken::Ident("LDA".into()),
758                               LexerToken::OpenParenthesis,
759                               LexerToken::Address("44".into()),
760                               LexerToken::Comma,
761                               LexerToken::Ident("X".into())]];
762
763        let mut parser = Parser::new();
764        let result = parser.parse(tokens);
765
766        assert_eq!(Err(ParserError::unexpected_eol(1)), result);
767    }
768
769    #[test]
770    fn can_parse_indirect_jump_instruction() {
771        let tokens = vec![vec![LexerToken::Ident("JMP".into()),
772                               LexerToken::OpenParenthesis,
773                               LexerToken::Address("4400".into()),
774                               LexerToken::CloseParenthesis]];
775
776        let mut parser = Parser::new();
777        let result = parser.parse(tokens).unwrap();
778
779        assert_eq!(&[
780                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("JMP", AddressingMode::Indirect).unwrap()),
781                     ParserToken::RawByte(0),
782                     ParserToken::RawByte(68)],
783                   &result[..]);
784    }
785
786    #[test]
787    fn errors_on_eight_bit_indirect_jump_instruction() {
788        let tokens = vec![vec![LexerToken::Ident("JMP".into()),
789                               LexerToken::OpenParenthesis,
790                               LexerToken::Address("44".into()),
791                               LexerToken::CloseParenthesis]];
792
793        let mut parser = Parser::new();
794        let result = parser.parse(tokens);
795
796        assert_eq!(Err(ParserError::address_out_of_bounds(1)), result);
797    }
798
799    #[test]
800    fn can_parse_implied_stack_instructions() {
801        let tokens = vec![vec![LexerToken::Ident("PHA".into())]];
802
803        let mut parser = Parser::new();
804        let result = parser.parse(tokens).unwrap();
805
806        assert_eq!(&[
807                     ParserToken::OpCode(OpCode::from_mnemonic_and_addressing_mode("PHA", AddressingMode::Implied).unwrap())],
808                   &result[..]);
809    }
810
811    #[test]
812    fn errors_on_incorrect_opcode_addressing_mode_with_variable() {
813        let tokens = vec![vec![LexerToken::Ident("MAIN_ADDRESS".into()),
814                               LexerToken::Assignment,
815                               LexerToken::Address("00".into())],
816                          vec![LexerToken::Ident("JMP".into()),
817                               LexerToken::Ident("MAIN_ADDRESS".into())]];
818
819        let mut parser = Parser::new();
820        let result = parser.parse(tokens);
821
822        assert_eq!(Err(ParserError::invalid_opcode_addressing_mode_combination(2)),
823                   result);
824    }
825
826    #[test]
827    fn can_parse_directives() {
828        let tokens = vec![vec![LexerToken::Period,
829                               LexerToken::Ident("ORG".into()),
830                               LexerToken::Address("C000".into())]];
831
832        let mut parser = Parser::new();
833        let result = parser.parse(tokens).unwrap();
834
835        assert_eq!(&[ParserToken::OrgDirective(0xC000)], &result[..]);
836    }
837}