c64_assembler/generator/
dasm.rs

1use crate::{
2    instruction::operation::Operation,
3    memory::{
4        address_mode::{AddressMode, Immediate},
5        define::{Define, Value},
6        user_count::UserCount,
7        ZeroPage,
8    },
9    validator::AssemblerResult,
10    Application, Function, Instructions, Module,
11};
12
13use super::Generator;
14
15/// Dasm source code generator
16pub struct DasmGenerator {
17    output: Vec<String>,
18    comment_column: usize,
19}
20
21impl Default for DasmGenerator {
22    fn default() -> Self {
23        DasmGenerator {
24            output: Vec::<String>::default(),
25            comment_column: 25,
26        }
27    }
28}
29
30impl Generator for DasmGenerator {
31    type Output = String;
32
33    fn generate(mut self, application: Application) -> AssemblerResult<Self::Output> {
34        self.line(format!("; --- Application: {} ---", application.name.to_uppercase()));
35        self.line("; NOTE: This file is generated, do not modify".to_string());
36        self.line_new();
37        self.line("  processor 6502".to_string());
38        self.line_new();
39
40        for define in &application.defines {
41            if !define.user_empty() {
42                self.add_define(define);
43            }
44        }
45        self.line_new();
46        self.line(format!("  org ${:04X}", application.entry_point));
47
48        for module in &application.modules {
49            self.module(&application, module);
50        }
51
52        Ok(self.output.join("\n"))
53    }
54}
55
56impl DasmGenerator {
57    fn module(&mut self, application: &Application, module: &Module) {
58        self.line_new();
59        self.line(format!("; --- Module begin: {} ---", module.name.to_uppercase()));
60        self.instructions(application, &module.instructions);
61        for function in &module.functions {
62            self.function(application, function);
63        }
64        self.line(format!("; --- Module end: {} ---", module.name.to_uppercase()));
65    }
66
67    fn function(&mut self, application: &Application, function: &Function) {
68        self.line_new();
69        self.line(format!("; --- Function begin: {} ---", function.name.to_uppercase()));
70        self.line_new();
71        for d in &function.documentation {
72            self.line(format!("; {}", d));
73        }
74        self.line(format!("{}:", function.name));
75        self.instructions(application, &function.instructions);
76
77        self.line(format!("; --- Function end: {} ---", function.name.to_uppercase()));
78    }
79
80    fn instructions(&mut self, _application: &Application, instructions: &Instructions) {
81        for instruction in &instructions.instructions {
82            let mut line: Vec<String> = vec![];
83            if let Operation::Label(_) = &instruction.operation {
84                self.line_new();
85            } else {
86                line.push("  ".to_string());
87            }
88
89            // Add operation
90            match &instruction.operation {
91                Operation::Raw(bytes) => {
92                    line.push(format!("byte ${:02X}", bytes[0]));
93                    for byte in bytes.iter().skip(1) {
94                        line.push(format!(", ${:02X}", byte));
95                    }
96                }
97                Operation::Label(label) => line.push(format!("{}:", label)),
98                _ => {
99                    line.push(instruction.operation.definition().unwrap().instruction.to_string());
100                }
101            }
102
103            // Add address mode
104            match &instruction.address_mode {
105                AddressMode::Implied => {}
106                AddressMode::Immediate(immediate) => match immediate {
107                    Immediate::Byte(byte) => line.push(format!(" #${byte:02X}")),
108                    Immediate::Low(address_reference) => line.push(format!(" #<{}", address_reference.name)),
109                    Immediate::High(address_reference) => line.push(format!(" #>{}", address_reference.name)),
110                },
111                AddressMode::Absolute(address_reference) | AddressMode::Relative(address_reference) => {
112                    line.push(format!(" {}", address_reference.name));
113                    if address_reference.offset != 0 {
114                        line.push(format!("+{}", address_reference.offset));
115                    }
116                }
117                AddressMode::AbsoluteX(address_reference) => {
118                    line.push(format!(" {}", address_reference.name));
119                    if address_reference.offset != 0 {
120                        line.push(format!("+{}", address_reference.offset));
121                    }
122                    line.push(",x".to_string());
123                }
124                AddressMode::AbsoluteY(address_reference) => {
125                    line.push(format!(" {}", address_reference.name));
126                    if address_reference.offset != 0 {
127                        line.push(format!("+{}", address_reference.offset));
128                    }
129                    line.push(",y".to_string());
130                }
131                AddressMode::IndexedIndirect(address_reference) => {
132                    line.push(format!(" ({}", address_reference.name));
133                    if address_reference.offset != 0 {
134                        line.push(format!("+{}", address_reference.offset));
135                    }
136                    line.push("),x".to_string());
137                }
138                AddressMode::IndirectIndexed(address_reference) => {
139                    line.push(format!(" ({}", address_reference.name));
140                    if address_reference.offset != 0 {
141                        line.push(format!("+{}", address_reference.offset));
142                    }
143                    line.push("),y".to_string());
144                }
145                AddressMode::Accumulator => line.push(" A".to_string()),
146                AddressMode::Indirect(address_reference) => {
147                    line.push(format!(" ({}", address_reference.name));
148                    if address_reference.offset != 0 {
149                        line.push(format!("+{}", address_reference.offset));
150                    }
151                    line.push(")".to_string());
152                }
153            }
154
155            if instruction.comments.is_empty() {
156                self.line(line.join(""));
157            } else {
158                let mut line_len = line.join("").len();
159                let add_comments_before =
160                    line_len > self.comment_column || matches!(&instruction.operation, Operation::Label(_label));
161
162                if add_comments_before {
163                    for comment in &instruction.comments {
164                        self.line(format!("  ; {}", comment));
165                    }
166                    self.line(line.join(""))
167                } else {
168                    for comment in &instruction.comments {
169                        let inset = vec![" "; self.comment_column - line_len].join("");
170                        line.push(format!("{}; {}", inset, comment));
171                        self.line(line.join(""));
172                        line.clear();
173                        line_len = 0;
174                    }
175                }
176            }
177        }
178    }
179
180    fn add_define(&mut self, define: &Define) {
181        let mut line = vec![];
182        line.push(define.name.clone());
183        line.push("=".to_string());
184        match &define.value {
185            Value::Address(address) => line.push(format!("${:04X}", address)),
186            Value::Zeropage(address) => line.push(format!("${:02X}", address.low())),
187        }
188
189        self.line(line.join(" "));
190    }
191}
192
193impl DasmGenerator {
194    fn line(&mut self, line: String) {
195        self.output.push(line);
196    }
197    fn line_new(&mut self) {
198        self.output.push(String::default());
199    }
200}