Skip to main content

rs_asm6805/
assembler.rs

1use crate::lexer::{Lexer, Token};
2use crate::opcodes::*;
3use crate::symbol_table::{SourceLine, SymbolTable};
4use crate::timex::{TIMEX_TABLE, TIMEX6_TABLE};
5use crate::utils::*;
6use std::cell::RefCell;
7use std::fs;
8use std::path::Path;
9use std::rc::Rc;
10use thiserror::Error;
11
12#[derive(Error, Debug)]
13pub enum AssemblerError {
14    #[error("Parse error at line {line}: {message}")]
15    ParseError { line: usize, message: String },
16    #[error("Symbol error: {message}")]
17    SymbolError { message: String },
18    #[error("Value out of range: {message}")]
19    RangeError { message: String },
20    #[error("IO error: {message}")]
21    IoError { message: String },
22}
23
24pub struct Assembler {
25    symbol_table: SymbolTable,
26    source_lines: Vec<Rc<RefCell<SourceLine>>>,
27    current_line: usize,
28    filename: String,
29    include_path: String,
30    errors: Vec<String>,
31    max_line: usize,
32}
33
34#[allow(unused_assignments)]
35impl Assembler {
36    pub fn new(filename: String) -> Self {
37        // Determine include path from the input filename directory + include
38        let base_dir = if let Some(dir) = Path::new(&filename).parent() {
39            dir.to_string_lossy().to_string()
40        } else {
41            ".".to_string()
42        };
43        let include_path = Path::new(&base_dir)
44            .join("include")
45            .to_string_lossy()
46            .to_string();
47
48        let mut assembler = Self {
49            symbol_table: SymbolTable::new(),
50            source_lines: Vec::new(),
51            current_line: 0,
52            filename,
53            include_path,
54            errors: Vec::new(),
55            max_line: 0,
56        };
57
58        // Initialize keywords
59        for kw in KEYWORDS {
60            assembler
61                .symbol_table
62                .define_keyword(kw.name, kw.value as i32);
63        }
64
65        assembler
66    }
67
68    pub fn reset_errors(&mut self) {
69        self.errors.clear();
70    }
71
72    pub fn get_errors(&self) -> &[String] {
73        &self.errors
74    }
75
76    pub fn add_source_line(&mut self, line_num: usize) {
77        while self.source_lines.len() <= line_num {
78            self.source_lines
79                .push(Rc::new(RefCell::new(SourceLine::new())));
80        }
81
82        if line_num >= self.max_line {
83            self.max_line = line_num + 1;
84        }
85
86        let offset = if line_num == 0 {
87            0x0110
88        } else {
89            let prev_line = &self.source_lines[line_num - 1];
90            let prev_ref = prev_line.borrow();
91            prev_ref.get_offset() + prev_ref.get_byte_count() as i32
92        };
93
94        self.source_lines[line_num]
95            .borrow_mut()
96            .set_offset(offset, false);
97    }
98
99    pub fn parse_line(&mut self, line: &str, line_number: usize) -> Result<(), AssemblerError> {
100        let mut lexer = Lexer::new(expand_tabs(line, 8));
101        let mut data = Vec::new();
102        let mut opcode = 0u16;
103
104        self.current_line = line_number;
105
106        // Reset line state
107        {
108            let source_line_rc = self.source_lines[line_number].clone();
109            let source_line_weak = Rc::downgrade(&source_line_rc);
110            let mut source_line = source_line_rc.borrow_mut();
111            source_line.reset_references(source_line_weak.clone());
112            source_line.set_definition(None, source_line_weak);
113            source_line.set_relative(-1);
114        }
115
116        let mut token = lexer.get_token();
117        let mut label = String::new();
118
119        // Check for label
120        if let Token::Symbol(name) = &token {
121            if self.symbol_table.lookup_keyword(name).is_none() {
122                label = name.clone();
123                token = lexer.get_token();
124                // Skip colon if present
125                if token == Token::Colon {
126                    token = lexer.get_token();
127                }
128            }
129        }
130
131        // Check for opcode
132        if let Token::Symbol(name) = &token {
133            if let Some(op_value) = self.symbol_table.lookup_keyword(name) {
134                opcode = op_value as u16;
135                token = lexer.get_token();
136            }
137        }
138
139        // Define label if present and not EQU
140        if (opcode >> 8) != (CAT_EQU >> 8) && !label.is_empty() {
141            let source_line_weak = Rc::downgrade(&self.source_lines[line_number]);
142            let offset = self.source_lines[line_number].borrow().get_offset();
143            let symbol = self
144                .symbol_table
145                .define_symbol(&label, source_line_weak, offset);
146            if symbol.borrow().get_definition_count() != 1 {
147                self.emit_error(&format!("Symbol {} defined on more than one line", label));
148            }
149        }
150
151        // Process instruction based on category
152        match opcode >> 8 {
153            cat if cat == (CAT_INH >> 8) => {
154                data.push((opcode & 0xff) as u8);
155            }
156            cat if cat == (CAT_REL >> 8) => {
157                let (new_token, value) = self.parse_level8(&mut lexer, token)?;
158                token = new_token;
159                data.push((opcode & 0xff) as u8);
160
161                self.source_lines[line_number]
162                    .borrow_mut()
163                    .set_relative(value);
164                let branch_distance =
165                    value - self.source_lines[line_number].borrow().get_offset() - 2;
166
167                if branch_distance > 0x7f || branch_distance < -0x80 {
168                    self.emit_error(&format!(
169                        "Branch target out of range (Distance={})",
170                        branch_distance
171                    ));
172                }
173                data.push((branch_distance & 0xff) as u8);
174            }
175            cat if cat == (CAT_BSC >> 8) || cat == (CAT_BSCX >> 8) => {
176                if cat == (CAT_BSCX >> 8) {
177                    let (new_token, bit_num) = self.parse_level8(&mut lexer, token)?;
178                    token = new_token;
179                    opcode += (bit_num * 2) as u16;
180                    if token == Token::Comma {
181                        token = lexer.get_token();
182                    }
183                }
184
185                let (new_token, value) = self.parse_level8(&mut lexer, token)?;
186                token = new_token;
187                data.push((opcode & 0xff) as u8);
188
189                if value > 0xff || value < 0 {
190                    self.emit_error(&format!(
191                        "BSET/BCLR target ({:04x}) not in zero page",
192                        value
193                    ));
194                }
195                data.push((value & 0xff) as u8);
196            }
197            cat if cat == (CAT_BTB >> 8) || cat == (CAT_BTBX >> 8) => {
198                if cat == (CAT_BTBX >> 8) {
199                    let (new_token, bit_num) = self.parse_level8(&mut lexer, token)?;
200                    token = new_token;
201                    opcode += (bit_num * 2) as u16;
202                    if token == Token::Comma {
203                        token = lexer.get_token();
204                    }
205                }
206
207                data.push((opcode & 0xff) as u8);
208
209                let (new_token, value) = self.parse_level8(&mut lexer, token)?;
210                token = new_token;
211                if value > 0xff || value < 0 {
212                    self.emit_error(&format!(
213                        "BRSET/BRCLR target ({:04x}) not in zero page",
214                        value
215                    ));
216                }
217                data.push((value & 0xff) as u8);
218
219                if token == Token::Comma {
220                    token = lexer.get_token();
221                }
222
223                let (new_token, branch_value) = self.parse_level8(&mut lexer, token)?;
224                token = new_token;
225                self.source_lines[line_number]
226                    .borrow_mut()
227                    .set_relative(branch_value);
228                let branch_distance =
229                    branch_value - self.source_lines[line_number].borrow().get_offset() - 3;
230                data.push((branch_distance & 0xff) as u8);
231
232                if branch_distance > 0x7f || branch_distance < -0x80 {
233                    self.emit_error(&format!(
234                        "Branch target out of range (Distance={})",
235                        branch_distance
236                    ));
237                }
238            }
239            cat if cat == (CAT_IMM_DIR_EXT_IX1_IX1_IX >> 8) => {
240                if token == Token::Immed {
241                    data.push((opcode & 0xff) as u8);
242                    token = lexer.get_token();
243                    let (new_token, value) = self.parse_level8(&mut lexer, token)?;
244                    token = new_token;
245                    if value > 0xff || value < 0 {
246                        self.emit_error(&format!("Operand ({:04x}) out of range 00-FF", value));
247                    }
248                    data.push((value & 0xff) as u8);
249                } else {
250                    self.process_addressing_mode(&mut lexer, &mut token, &mut data, opcode)?;
251                }
252            }
253            cat if cat == (CAT_DIR_EXT_IX1_IX1_IX >> 8) => {
254                self.process_addressing_mode(&mut lexer, &mut token, &mut data, opcode)?;
255            }
256            cat if cat == (CAT_DIR_IX1_IX >> 8) => {
257                self.process_dir_ix_addressing(&mut lexer, &mut token, &mut data, opcode)?;
258            }
259            cat if cat == (CAT_DB >> 8) => {
260                loop {
261                    let (new_token, value) = self.parse_level8(&mut lexer, token)?;
262                    token = new_token;
263                    if value > 0xff || value < 0 {
264                        self.emit_error(&format!("Operand ({:04x}) out of range $00-$FF", value));
265                    }
266                    data.push((value & 0xff) as u8);
267
268                    if token != Token::Comma {
269                        break;
270                    }
271                    token = lexer.get_token();
272                }
273            }
274            cat if cat == (CAT_DW >> 8) => {
275                loop {
276                    let (new_token, value) = self.parse_level8(&mut lexer, token)?;
277                    token = new_token;
278                    data.push(((value >> 8) & 0xff) as u8);
279                    data.push((value & 0xff) as u8);
280
281                    if token != Token::Comma {
282                        break;
283                    }
284                    token = lexer.get_token();
285                }
286            }
287            cat if cat == (CAT_EQU >> 8) => {
288                if label.is_empty() {
289                    self.emit_error("No label for EQU statement");
290                } else {
291                    let (new_token, value) = self.parse_level8(&mut lexer, token)?;
292                    token = new_token;
293
294                    let source_line_weak = Rc::downgrade(&self.source_lines[line_number]);
295                    let symbol = self
296                        .symbol_table
297                        .define_symbol(&label, source_line_weak, value);
298
299                    if symbol.borrow().get_definition_count() != 1 {
300                        self.emit_error(&format!("Symbol {} defined on more than one line", label));
301                    }
302                }
303            }
304            cat if cat == (CAT_ORIGIN >> 8) => {
305                let (new_token, value) = self.parse_level8(&mut lexer, token)?;
306                token = new_token;
307                self.source_lines[line_number]
308                    .borrow_mut()
309                    .set_offset(value, true);
310            }
311            cat if cat == (CAT_INCLUDE >> 8) => {
312                if let Token::String(filename) = token {
313                    // Look up the filename as a symbol to avoid processing the same file multiple times
314                    let symbol = self.symbol_table.lookup_symbol(&filename);
315                    if symbol.borrow().get_value() == 0 {
316                        // File hasn't been processed yet
317                        match self.read_include_file(&filename) {
318                            Ok(content) => {
319                                let content_bytes = content.len();
320                                // Mark file as processed
321                                let source_line_weak =
322                                    Rc::downgrade(&self.source_lines[line_number]);
323                                symbol.borrow_mut().set_value(1);
324                                symbol.borrow_mut().add_definition(source_line_weak);
325
326                                // Set the bytes read for listing annotation
327                                self.source_lines[line_number]
328                                    .borrow_mut()
329                                    .set_include_bytes_read(content_bytes);
330
331                                // Process each line for EQU statements
332                                for line in content.lines() {
333                                    let mut include_lexer = Lexer::new(expand_tabs(line, 8));
334                                    let mut include_token = include_lexer.get_token();
335
336                                    if let Token::Symbol(symbol_name) = include_token {
337                                        include_token = include_lexer.get_token();
338                                        if let Token::Symbol(equ_name) = &include_token {
339                                            if equ_name.to_uppercase() == "EQU" {
340                                                include_token = include_lexer.get_token();
341                                                if let Ok((_, value)) =
342                                                    self.parse_level8(&mut include_lexer, include_token)
343                                                {
344                                                    self.symbol_table.define_symbol(
345                                                        &symbol_name,
346                                                        Rc::downgrade(
347                                                            &self.source_lines[line_number],
348                                                        ),
349                                                        value,
350                                                    );
351                                                }
352                                            }
353                                        }
354                                    }
355                                }
356                            }
357                            Err(e) => {
358                                self.emit_error(&format!("Unable to open '{}': {}", filename, e));
359                            }
360                        }
361                    }
362                    token = lexer.get_token();
363                } else {
364                    self.emit_error("Include file must be enclosed in quotes");
365                }
366            }
367            cat if cat == (CAT_ASCII >> 8) => {
368                if let Token::String(s) = token {
369                    for ch in s.chars() {
370                        data.push((ch as u8) & 0x7f);
371                    }
372                    token = lexer.get_token();
373                }
374            }
375            cat if cat == (CAT_TIMEX >> 8) => {
376                if let Token::String(s) = token {
377                    for ch in s.chars() {
378                        let idx = (ch as u8 & 0x7f) as usize;
379                        if idx < TIMEX_TABLE.len() {
380                            data.push(TIMEX_TABLE[idx]);
381                        }
382                    }
383                    token = lexer.get_token();
384                }
385            }
386            cat if cat == (CAT_TIMEX6 >> 8) => {
387                if let Token::String(s) = token {
388                    // TIMEX6 always produces exactly 6 bytes, padding with spaces if needed
389                    let mut chars: Vec<char> = s.chars().collect();
390                    chars.resize(6, ' '); // Pad with spaces to make exactly 6 characters
391
392                    for ch in chars.iter().take(6) {
393                        let idx = (*ch as u8 & 0x7f) as usize;
394                        if idx < TIMEX6_TABLE.len() {
395                            data.push(TIMEX6_TABLE[idx]);
396                        } else {
397                            data.push(0x1d); // Default value for unknown characters
398                        }
399                    }
400                    token = lexer.get_token();
401                }
402            }
403            0 => {
404                // Label only line
405            }
406            _ => {
407                self.emit_error("Unrecognized opcode");
408            }
409        }
410
411        if token != Token::End {
412            self.emit_error("Junk past the end of the instruction");
413        }
414
415        // Set the data for this line
416        let delta = self.source_lines[line_number]
417            .borrow_mut()
418            .set_data(&data);
419        self.source_lines[line_number]
420            .borrow_mut()
421            .mark_dirty(false);
422
423        // Adjust offsets for subsequent lines
424        if delta != 0 {
425            let current_offset = self.source_lines[line_number].borrow().get_offset();
426            for i in (line_number + 1)..self.max_line {
427                self.source_lines[i].borrow_mut().adjust_offset(delta);
428                let rel_offset = self.source_lines[i].borrow().get_relative_offset();
429                if rel_offset >= 0 && rel_offset < current_offset {
430                    self.source_lines[i].borrow_mut().mark_dirty(true);
431                }
432            }
433            for i in (0..line_number).rev() {
434                if self.source_lines[i].borrow().get_offset() > current_offset {
435                    self.source_lines[i].borrow_mut().mark_dirty(true);
436                }
437            }
438        }
439
440        Ok(())
441    }
442
443    fn process_addressing_mode(
444        &mut self,
445        lexer: &mut Lexer,
446        token: &mut Token,
447        data: &mut Vec<u8>,
448        opcode: u16,
449    ) -> Result<(), AssemblerError> {
450        let value = if *token == Token::Comma {
451            0
452        } else {
453            let (new_token, val) = self.parse_level8(lexer, token.clone())?;
454            *token = new_token;
455            val
456        };
457
458        if *token == Token::Comma {
459            // Indexed mode
460            *token = lexer.get_token();
461            if let Token::Symbol(name) = token {
462                if stricmp(name, "X") != 0 {
463                    self.emit_error("Missing X after ,");
464                } else {
465                    *token = lexer.get_token();
466                }
467            }
468
469            if value == 0 {
470                // IX Mode
471                data.push(((opcode & 0xff) + 0x50) as u8);
472            } else if value >= -256 && value < 256 {
473                // IX1 Mode
474                data.push(((opcode & 0xff) + 0x40) as u8);
475                data.push((value & 0xff) as u8);
476            } else {
477                // IX2 Mode
478                data.push(((opcode & 0xff) + 0x30) as u8);
479                data.push(((value >> 8) & 0xff) as u8);
480                data.push((value & 0xff) as u8);
481            }
482        } else {
483            // Direct or Extended mode
484            if value >= -256 && value < 256 {
485                // DIR Mode
486                data.push(((opcode & 0xff) + 0x10) as u8);
487                data.push((value & 0xff) as u8);
488            } else {
489                // EXT Mode
490                data.push(((opcode & 0xff) + 0x20) as u8);
491                data.push(((value >> 8) & 0xff) as u8);
492                data.push((value & 0xff) as u8);
493            }
494        }
495
496        Ok(())
497    }
498
499    fn process_dir_ix_addressing(
500        &mut self,
501        lexer: &mut Lexer,
502        token: &mut Token,
503        data: &mut Vec<u8>,
504        opcode: u16,
505    ) -> Result<(), AssemblerError> {
506        let value = if *token == Token::Comma {
507            0
508        } else {
509            let (new_token, val) = self.parse_level8(lexer, token.clone())?;
510            *token = new_token;
511            val
512        };
513
514        if *token == Token::Comma {
515            // Indexed mode
516            *token = lexer.get_token();
517            if let Token::Symbol(name) = token {
518                if stricmp(name, "X") != 0 {
519                    self.emit_error("Missing X after ,");
520                } else {
521                    *token = lexer.get_token();
522                }
523            }
524
525            if value == 0 {
526                // IX Mode
527                data.push(((opcode & 0xff) + 0x40) as u8);
528            } else {
529                // IX1 Mode
530                data.push(((opcode & 0xff) + 0x30) as u8);
531                if value > 0xff || value < 0 {
532                    self.emit_error(&format!("Operand ({:04x}) out of range $00-$FF", value));
533                }
534                data.push((value & 0xff) as u8);
535            }
536        } else {
537            // DIR Mode
538            data.push((opcode & 0xff) as u8);
539            if value > 0xff || value < 0 {
540                self.emit_error(&format!("Operand ({:04x}) out of range $00-$FF", value));
541            }
542            data.push((value & 0xff) as u8);
543        }
544
545        Ok(())
546    }
547
548    // Expression parsing methods (recursive descent parser)
549    fn parse_level1(
550        &mut self,
551        lexer: &mut Lexer,
552        token: Token,
553    ) -> Result<(Token, i32), AssemblerError> {
554        match token {
555            Token::LParen => {
556                let next_token = lexer.get_token();
557                let (token, value) = self.parse_level8(lexer, next_token)?;
558                if token == Token::RParen {
559                    Ok((lexer.get_token(), value))
560                } else {
561                    Ok((token, value))
562                }
563            }
564            Token::Const(val) => Ok((lexer.get_token(), val)),
565            Token::Symbol(name) => {
566                let source_line_weak = Rc::downgrade(&self.source_lines[self.current_line]);
567                let symbol = self
568                    .symbol_table
569                    .lookup_symbol_with_reference(&name, source_line_weak);
570                let value = symbol.borrow().get_value();
571                if symbol.borrow().get_definition_count() == 0 {
572                    self.emit_error(&format!("Undefined symbol '{}' Value={}", name, value));
573                }
574                Ok((lexer.get_token(), value))
575            }
576            Token::Star => {
577                let value = self.source_lines[self.current_line].borrow().get_offset();
578                Ok((lexer.get_token(), value))
579            }
580            Token::String(_) | Token::Comma | Token::End => Ok((token, 0)),
581            _ => {
582                self.emit_error("Unable to parse this line");
583                Ok((token, 0))
584            }
585        }
586    }
587
588    fn parse_level2(
589        &mut self,
590        lexer: &mut Lexer,
591        token: Token,
592    ) -> Result<(Token, i32), AssemblerError> {
593        if token == Token::Not {
594            let next_token = lexer.get_token();
595            let (token, value) = self.parse_level1(lexer, next_token)?;
596            Ok((token, !value))
597        } else {
598            self.parse_level1(lexer, token)
599        }
600    }
601
602    fn parse_level3(
603        &mut self,
604        lexer: &mut Lexer,
605        token: Token,
606    ) -> Result<(Token, i32), AssemblerError> {
607        let (mut token, mut value) = self.parse_level2(lexer, token)?;
608        if matches!(token, Token::Star | Token::Divide) {
609            let op = token;
610            let next_token = lexer.get_token();
611            let (new_token, right_value) = self.parse_level3(lexer, next_token)?;
612            token = new_token;
613            if op == Token::Star {
614                value *= right_value;
615            } else {
616                if right_value == 0 {
617                    self.emit_error("Division by zero");
618                    value = 0;
619                } else {
620                    value /= right_value;
621                }
622            }
623        }
624        Ok((token, value))
625    }
626
627    fn parse_level4(
628        &mut self,
629        lexer: &mut Lexer,
630        token: Token,
631    ) -> Result<(Token, i32), AssemblerError> {
632        let (mut token, mut value) = self.parse_level3(lexer, token)?;
633        if matches!(token, Token::Plus | Token::Minus) {
634            let op = token;
635            let next_token = lexer.get_token();
636            let (new_token, right_value) = self.parse_level4(lexer, next_token)?;
637            token = new_token;
638            if op == Token::Plus {
639                value += right_value;
640            } else {
641                value -= right_value;
642            }
643        }
644        Ok((token, value))
645    }
646
647    fn parse_level5(
648        &mut self,
649        lexer: &mut Lexer,
650        token: Token,
651    ) -> Result<(Token, i32), AssemblerError> {
652        let (mut token, mut value) = self.parse_level4(lexer, token)?;
653        if matches!(token, Token::LShift | Token::RShift) {
654            let op = token;
655            let next_token = lexer.get_token();
656            let (new_token, right_value) = self.parse_level5(lexer, next_token)?;
657            token = new_token;
658            if op == Token::LShift {
659                value <<= right_value;
660            } else {
661                value >>= right_value;
662            }
663        }
664        Ok((token, value))
665    }
666
667    fn parse_level6(
668        &mut self,
669        lexer: &mut Lexer,
670        token: Token,
671    ) -> Result<(Token, i32), AssemblerError> {
672        let (mut token, mut value) = self.parse_level5(lexer, token)?;
673        if token == Token::And {
674            let next_token = lexer.get_token();
675            let (new_token, right_value) = self.parse_level6(lexer, next_token)?;
676            token = new_token;
677            value &= right_value;
678        }
679        Ok((token, value))
680    }
681
682    fn parse_level7(
683        &mut self,
684        lexer: &mut Lexer,
685        token: Token,
686    ) -> Result<(Token, i32), AssemblerError> {
687        let (mut token, mut value) = self.parse_level6(lexer, token)?;
688        if token == Token::Xor {
689            let next_token = lexer.get_token();
690            let (new_token, right_value) = self.parse_level7(lexer, next_token)?;
691            token = new_token;
692            value ^= right_value;
693        }
694        Ok((token, value))
695    }
696
697    fn parse_level8(
698        &mut self,
699        lexer: &mut Lexer,
700        token: Token,
701    ) -> Result<(Token, i32), AssemblerError> {
702        let (mut token, mut value) = self.parse_level7(lexer, token)?;
703        if token == Token::Or {
704            let next_token = lexer.get_token();
705            let (new_token, right_value) = self.parse_level8(lexer, next_token)?;
706            token = new_token;
707            value |= right_value;
708        }
709        Ok((token, value))
710    }
711
712    fn read_include_file(&self, filename: &str) -> Result<String, std::io::Error> {
713        let include_file_path = Path::new(&self.include_path).join(filename);
714        fs::read_to_string(include_file_path)
715    }
716
717    fn emit_error(&mut self, message: &str) {
718        let error_msg = format!(
719            "{}({}): {}",
720            self.filename,
721            self.current_line + 1,
722            message
723        );
724        self.errors.push(error_msg);
725    }
726
727    pub fn get_hex(&self) -> String {
728        let mut result = String::new();
729        for line in &self.source_lines[..self.max_line] {
730            let line_ref = line.borrow();
731            let data = line_ref.get_data();
732            for byte in data {
733                result.push_str(&format!("{:02x}", byte));
734            }
735        }
736        result
737    }
738
739    pub fn generate_listing(&self, source_data: &[String]) -> Vec<String> {
740        let mut result = Vec::new();
741
742        for (line_num, line) in self.source_lines.iter().enumerate().take(self.max_line) {
743            let line_ref = line.borrow();
744            let offset = line_ref.get_offset();
745            let data = line_ref.get_data();
746            let source_line = source_data.get(line_num).map(|s| s.as_str()).unwrap_or("");
747
748            let listing_line = if data.is_empty() {
749                // Check if this line has an include file annotation
750                if let Some(bytes_read) = line_ref.include_bytes_read {
751                    format!(
752                        "{:4}|  {} bytes read  |{}",
753                        line_num + 1,
754                        bytes_read,
755                        source_line
756                    )
757                }
758                // Check if this line has a symbol definition (like EQU)
759                else if let Some(symbol_def) = &line_ref.definition {
760                    if let Some(symbol) = symbol_def.upgrade() {
761                        let value = symbol.borrow().get_value();
762                        format!(
763                            "{:4}|     ={:04x}      |{}",
764                            line_num + 1,
765                            value,
766                            source_line
767                        )
768                    } else {
769                        format!("{:4}|                |{}", line_num + 1, source_line)
770                    }
771                } else {
772                    format!("{:4}|                |{}", line_num + 1, source_line)
773                }
774            } else if data.len() == 1 {
775                format!(
776                    "{:4}| {:04x}: {:02x}       |{}",
777                    line_num + 1,
778                    offset,
779                    data[0],
780                    source_line
781                )
782            } else if data.len() == 2 {
783                format!(
784                    "{:4}| {:04x}: {:02x} {:02x}    |{}",
785                    line_num + 1,
786                    offset,
787                    data[0],
788                    data[1],
789                    source_line
790                )
791            } else {
792                format!(
793                    "{:4}| {:04x}: {:02x} {:02x} {:02x} |{}",
794                    line_num + 1,
795                    offset,
796                    data[0],
797                    data[1],
798                    data[2],
799                    source_line
800                )
801            };
802
803            result.push(listing_line);
804        }
805
806        result
807    }
808}
809
810pub fn assemble(
811    filename: String,
812    source_data: Vec<String>,
813) -> Result<(Vec<String>, String, Vec<String>), Vec<AssemblerError>> {
814    let mut assembler = Assembler::new(filename);
815    let mut errors = Vec::new();
816
817    // Pass 1
818    assembler.reset_errors();
819    for (line_num, line) in source_data.iter().enumerate() {
820        assembler.add_source_line(line_num);
821        if let Err(e) = assembler.parse_line(&expand_tabs(line, 8), line_num) {
822            errors.push(e);
823        }
824    }
825
826    // Pass 2
827    assembler.reset_errors();
828    for (line_num, line) in source_data.iter().enumerate() {
829        if let Err(e) = assembler.parse_line(&expand_tabs(line, 8), line_num) {
830            errors.push(e);
831        }
832    }
833    // Loop until no more changes
834    let mut dirty = true;
835    while dirty {
836        dirty = false;
837        assembler.reset_errors();
838        for line_num in 0..assembler.max_line {
839            if assembler.source_lines[line_num].borrow().is_dirty() {
840                dirty = true;
841                if let Err(e) =
842                    assembler.parse_line(&expand_tabs(&source_data[line_num], 8), line_num)
843                {
844                    errors.push(e);
845                }
846            }
847        }
848    }
849
850    // Final pass to catch any remaining errors
851    assembler.reset_errors();
852    for line_num in 0..assembler.max_line {
853        if let Err(e) = assembler.parse_line(&expand_tabs(&source_data[line_num], 8), line_num) {
854            errors.push(e);
855        }
856    }
857
858    if !errors.is_empty() {
859        // Could return here, but TS version continues, so we will too.
860    }
861
862    let error_strings: Vec<String> = assembler.get_errors().to_vec();
863    let hex_output = assembler.get_hex();
864    let listing = assembler.generate_listing(&source_data);
865
866    Ok((error_strings, hex_output, listing))
867}