c64_assembler_macro/
lib.rs1use proc_macro::{TokenStream, TokenTree};
2
3#[proc_macro]
4pub fn application(input: TokenStream) -> TokenStream {
5 let mut lines = Vec::<String>::default();
7 lines.push("{".to_string());
8 lines.push(" use c64_assembler::builder::{*};".to_string());
9 lines.push(" ApplicationBuilder::default()".to_string());
10 let mut iter = input.into_iter();
11 while let Some(tree) = iter.next() {
12 if let TokenTree::Ident(identifier) = tree {
13 let name = identifier.to_string();
14 if name == "name" {
15 let mut line = Vec::<String>::default();
16 line.push(" .name(".to_string());
17 let _eq = iter.next().unwrap();
18 if let Some(TokenTree::Literal(application_name)) = iter.next() {
19 line.push(format!("{application_name})"));
20 lines.push(line.join(""));
21 }
22 }
23 if name == "entry_point" {
24 let mut line = Vec::<String>::default();
25 line.push(" .entry_point(".to_string());
26 let _eq = iter.next().unwrap();
27 if let Some(TokenTree::Literal(entry_point)) = iter.next() {
28 line.push(format!("{entry_point})"));
29 lines.push(line.join(""));
30 }
31 }
32 if name == "include_vic2_defines" {
33 lines.push(" .include_vic2_defines()".to_string());
34 }
35 if name == "include_sid_defines" {
36 lines.push(" .include_sid_defines()".to_string());
37 }
38 if name == "module" {
39 let _eq = iter.next().unwrap();
40 if let Some(TokenTree::Group(sub_tree)) = iter.next() {
41 lines.push(" .module(".to_string());
42 lines.push(build_module(sub_tree.stream()));
43 lines.push(" )".to_string());
44 }
45 }
46 }
47 }
48 lines.push(" .build()".to_string());
49 lines.push("}".to_string());
50
51 lines.join("\n").parse().unwrap()
53}
54
55fn build_module(input: TokenStream) -> String {
56 let mut lines = Vec::<String>::default();
57 lines.push(" ModuleBuilder::default()".to_string());
58 let mut iter = input.into_iter();
59 while let Some(tree) = iter.next() {
60 if let TokenTree::Ident(identifier) = tree {
61 let name = identifier.to_string();
62 if name == "name" {
63 let mut line = Vec::<String>::default();
64 line.push(" .name(".to_string());
65 let _eq = iter.next().unwrap();
66 if let Some(TokenTree::Literal(module_name)) = iter.next() {
67 line.push(format!("{module_name})"));
68 lines.push(line.join(""));
69 }
70 }
71 if name == "instructions" {
72 let _eq = iter.next().unwrap();
73 if let Some(TokenTree::Group(sub_tree)) = iter.next() {
74 lines.push(" .instructions(".to_string());
75 lines.push(build_instructions(sub_tree.stream()));
76 lines.push(" )".to_string());
77 }
78 }
79 if name == "function" {
80 let _eq = iter.next().unwrap();
81 if let Some(TokenTree::Group(sub_tree)) = iter.next() {
82 lines.push(" .function(".to_string());
83 lines.push(build_function(sub_tree.stream()));
84 lines.push(" )".to_string());
85 }
86 }
87 }
88 }
89 lines.push(" .build()".to_string());
90 lines.join("\n")
91}
92
93fn build_function(input: TokenStream) -> String {
94 let mut lines = Vec::<String>::default();
95 lines.push(" FunctionBuilder::default()".to_string());
96 let mut iter = input.into_iter();
97 while let Some(tree) = iter.next() {
98 if let TokenTree::Ident(identifier) = tree {
99 let name = identifier.to_string();
100 if name == "name" {
101 let mut line = Vec::<String>::default();
102 line.push(" .name(".to_string());
103 let _eq = iter.next().unwrap();
104 if let Some(TokenTree::Literal(function_name)) = iter.next() {
105 line.push(format!("{function_name})"));
106 lines.push(line.join(""));
107 }
108 }
109 if name == "instructions" {
110 let _eq = iter.next().unwrap();
111 if let Some(TokenTree::Group(sub_tree)) = iter.next() {
112 lines.push(" .instructions(".to_string());
113 lines.push(build_instructions(sub_tree.stream()));
114 lines.push(" )".to_string());
115 }
116 }
117 }
118 }
119 lines.push(" .build()".to_string());
120 lines.join("\n")
121}
122fn build_address_mode(
139 line: &mut Vec<String>,
140 tokens: &[TokenTree],
141 allow_immediate: bool,
142 allow_accumulator: bool,
143 allow_absolute: bool,
144 allow_indirect: bool,
145) -> usize {
146 match tokens.first().unwrap() {
147 TokenTree::Punct(punct) => {
148 if punct.to_string() == "#" {
149 assert!(allow_immediate);
150 return build_address_mode_imm(line, &tokens[1..]) + 1;
151 }
152 }
153 TokenTree::Ident(ident) => {
154 if ident.to_string() == *"a" {
155 assert!(allow_accumulator);
156 return build_address_mode_accumulator(line, tokens);
157 }
158 assert!(allow_absolute);
159 return build_address_mode_absolute(line, tokens);
160 }
161 TokenTree::Group(_group) => {
162 assert!(allow_indirect);
163 return build_address_mode_indirect(line, &tokens[0..]);
164 }
165 _ => todo!("HUH!"),
166 }
167 0
168}
169
170fn build_address_mode_accumulator(line: &mut Vec<String>, _tokens: &[TokenTree]) -> usize {
171 line.push("_acc()".to_string());
172 1
173}
174
175fn build_address_mode_absolute(line: &mut Vec<String>, tokens: &[TokenTree]) -> usize {
176 let mut num_tokens = 1;
177 let address = if let Some(TokenTree::Ident(ident)) = tokens.first() {
178 ident.to_string()
179 } else {
180 "".to_string()
181 };
182 line.push("_addr".to_string());
183 let mut offset = 0;
184 let mut index = "".to_string();
185 if let Some(TokenTree::Punct(p)) = tokens.get(1) {
186 if p.as_char() == '+' {
187 if let Some(TokenTree::Literal(l)) = tokens.get(2) {
188 offset = l.to_string().parse::<u16>().unwrap();
189 }
190 num_tokens = 3;
191 }
192 if p.as_char() == ',' {
193 if let Some(TokenTree::Ident(identifier)) = tokens.get(2) {
194 index = identifier.to_string();
195 }
196 num_tokens = 3;
197 }
198 }
199 if (offset) > 0 {
200 line.push("_offs".to_string());
201 }
202 if !index.is_empty() {
203 line.push(format!("_{}", index));
204 }
205
206 line.push("(".to_string());
207 line.push(format!("\"{}\"", address));
208 if offset > 0 {
209 line.push(format!(", {}", offset));
210 }
211 line.push(")".to_string());
212 num_tokens
213}
214
215fn build_address_mode_indirect(line: &mut Vec<String>, tokens: &[TokenTree]) -> usize {
216 let mut is_indirect_indexed = false;
217 let mut is_indexed_indirect = false;
218 let mut address = String::new();
219 if let TokenTree::Group(group) = &tokens[0] {
220 for token in group.stream() {
221 if let TokenTree::Ident(identifier) = &token {
222 address = identifier.to_string();
223 }
224 if let TokenTree::Punct(punct) = &token {
225 if punct.as_char() == ',' {
226 is_indexed_indirect = true;
227 break;
228 }
229 }
230 }
231 }
232 if let Some(TokenTree::Punct(punct)) = &tokens.get(1) {
233 if punct.as_char() == ',' {
234 is_indirect_indexed = true;
235 }
236 }
237
238 if is_indexed_indirect {
239 line.push(format!("_ind_x(\"{address}\")"));
240 1
241 } else if is_indirect_indexed {
242 line.push(format!("_ind_y(\"{address}\")"));
243 3
244 } else {
245 line.push(format!("_ind(\"{address}\")"));
246 1
247 }
248}
249
250fn build_address_mode_imm(line: &mut Vec<String>, tokens: &[TokenTree]) -> usize {
251 let mut num_tokens = 0;
252 line.push("_imm".to_string());
253 let mut is_hex = false;
254 let mut is_low = false;
255 let mut is_high = false;
256 for token in tokens {
257 num_tokens += 1;
258 match token {
259 TokenTree::Punct(punct) => {
260 if punct.to_string() == "$" {
261 is_hex = true;
262 }
263 if punct.to_string() == "<" {
264 is_low = true;
265 }
266 if punct.to_string() == ">" {
267 is_high = true;
268 }
269 }
270 TokenTree::Literal(value) => {
271 if is_hex {
272 line.push(format!("(0x{})", value));
273 } else {
274 line.push(format!("({})", value));
275 }
276 break;
277 }
278 TokenTree::Ident(value) => {
279 if is_low {
280 line.push(format!("_low(\"{}\")", value));
281 } else if is_high {
282 line.push(format!("_high(\"{}\")", value));
283 } else if is_hex {
284 line.push(format!("(0x{})", value));
285 } else {
286 line.push(format!("(\"{}\")", value));
287 }
288 break;
289 }
290 _ => todo!(),
291 }
292 }
293 num_tokens
294}
295
296fn build_instructions(input: TokenStream) -> String {
297 let mut lines = Vec::<String>::default();
298 lines.push(" InstructionBuilder::default()".to_string());
299 let tokens = input.into_iter().collect::<Vec<TokenTree>>();
300 let mut sub_start = 0;
301 for i in 0..tokens.len() {
302 if i < sub_start {
303 continue;
304 }
305 let token = &tokens[i];
306 if let TokenTree::Ident(identifier) = token {
307 let name = identifier.to_string();
308 match name.as_str() {
309 "include_basic_header" => {
310 lines.push(" .add_basic_header()".to_string());
311 sub_start = i + 1;
312 }
313
314 "asl" | "lsr" | "ror" | "rol" => {
315 let mut line = Vec::default();
316 line.push(format!(" .{name}"));
317 let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], false, true, true, false);
318 lines.push(line.join(""));
319 sub_start = i + 1 + add_tokens_parsed;
320 }
321 "adc" | "and" | "cmp" | "cpx" | "cpy" | "eor" | "lda" | "ldx" | "ldy" | "ora" | "sbc" => {
322 let mut line = Vec::default();
323 line.push(format!(" .{name}"));
324 let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], true, false, true, true);
325 lines.push(line.join(""));
326 sub_start = i + 1 + add_tokens_parsed;
327 }
328
329 "bcc" | "bcs" | "beq" | "bmi" | "bne" | "bpl" | "bvc" | "bvs" | "jsr" | "bit" | "dec" | "inc" => {
330 let mut line = Vec::default();
331 line.push(format!(" .{name}"));
332 let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], false, false, true, false);
333 lines.push(line.join(""));
334 sub_start = i + 1 + add_tokens_parsed;
335 }
336
337 "sta" | "stx" | "sty" | "jmp" => {
338 let mut line = Vec::default();
339 line.push(format!(" .{name}"));
340 let add_tokens_parsed = build_address_mode(&mut line, &tokens[i + 1..], false, false, true, true);
341 lines.push(line.join(""));
342 sub_start = i + 1 + add_tokens_parsed;
343 }
344
345 "brk" | "cld" | "cli" | "clv" | "dex" | "dey" | "inx" | "iny" | "nop" | "pha" | "psr" | "pla"
346 | "php" | "plp" | "rti" | "sec" | "sed" | "sei" | "tax" | "tay" | "tsx" | "txa" | "txs" | "tya"
347 | "clc" | "rts" => {
348 lines.push(format!(" .{name}()"));
349 sub_start = i + 1;
350 }
351
352 &_ => {
353 lines.push(format!(" .label(\"{name}\")"));
354 sub_start = i + 2;
355 }
356 }
357 }
358 }
359
360 lines.push(" .build()".to_string());
361 lines.join("\n")
362}
363
364#[proc_macro]
365pub fn module(input: TokenStream) -> TokenStream {
366 let mut lines = Vec::<String>::default();
368 lines.push("{".to_string());
369 lines.push(" use c64_assembler::builder::{*};".to_string());
370 lines.push(build_module(input));
371 lines.push("}".to_string());
372 lines.join("\n").parse().unwrap()
374}
375
376#[proc_macro]
377pub fn function(input: TokenStream) -> TokenStream {
378 let mut lines = Vec::<String>::default();
380 lines.push("{".to_string());
381 lines.push(" use c64_assembler::builder::{*};".to_string());
382 lines.push(build_function(input));
383 lines.push("}".to_string());
384 lines.join("\n").parse().unwrap()
386}
387
388#[proc_macro]
389pub fn instructions(input: TokenStream) -> TokenStream {
390 let mut lines = Vec::<String>::default();
392 lines.push("{".to_string());
393 lines.push(" use c64_assembler::builder::{*};".to_string());
394 lines.push(build_instructions(input));
395 lines.push("}".to_string());
396 lines.join("\n").parse().unwrap()
398}