dbsdk_vu_asm/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{parse::Parse, parse_macro_input, Ident, Lit};
4
5const MAX_INPUT_SLOTS: u32 = 8;
6const MAX_CONSTANT_SLOTS: u32 = 16;
7
8struct VUProgram {
9    instructions: Vec<u32>
10}
11
12fn parse_reg(id: Ident) -> syn::Result<u32> {
13    let str = id.to_string();
14    match str.as_str() {
15        "r0" => Ok(0),
16        "r1" => Ok(1),
17        "r2" => Ok(2),
18        "r3" => Ok(3),
19        "r4" => Ok(4),
20        "r5" => Ok(5),
21        "r6" => Ok(6),
22        "r7" => Ok(7),
23        "r8" => Ok(8),
24        "r9" => Ok(9),
25        "r10" => Ok(10),
26        "r11" => Ok(11),
27        "r12" => Ok(12),
28        "r13" => Ok(13),
29        "r14" => Ok(14),
30        "r15" => Ok(15),
31        _ => Err(syn::Error::new(id.span().into(), "Invalid register identifier"))
32    }
33}
34
35fn parse_output(id: Ident) -> syn::Result<u32> {
36    let str = id.to_string();
37    match str.as_str() {
38        "pos" => Ok(0),
39        "tex" => Ok(1),
40        "col" => Ok(2),
41        "ocol" => Ok(3),
42        _ => Err(syn::Error::new(id.span().into(), "Invalid vertex output identifier"))
43    }
44}
45
46fn parse_sub(ch: char, id: Ident) -> syn::Result<u32> {
47    match ch {
48        'x' => Ok(0),
49        'y' => Ok(1),
50        'z' => Ok(2),
51        'w' => Ok(3),
52        'r' => Ok(0),
53        'g' => Ok(1),
54        'b' => Ok(2),
55        'a' => Ok(3),
56        _ => Err(syn::Error::new(id.span().into(), "Invalid shuffle subscript"))
57    }
58}
59
60fn parse_swizzle(id: Ident) -> syn::Result<(u32, u32, u32, u32)> {
61    let str = id.to_string();
62    if str.len() != 4 {
63        return Err(syn::Error::new(id.span().into(), "Invalid shuffle subscript"));
64    }
65
66    let x = parse_sub(str.chars().nth(0).unwrap(), id.clone())?;
67    let y = parse_sub(str.chars().nth(1).unwrap(), id.clone())?;
68    let z = parse_sub(str.chars().nth(2).unwrap(), id.clone())?;
69    let w = parse_sub(str.chars().nth(3).unwrap(), id.clone())?;
70
71    return Ok((x, y, z, w))
72}
73
74fn encode_instr(op: u32, d: u32, s: u32, sx: u32, sy: u32, sz: u32, sw: u32, m: u32) -> u32 {
75    (op & 0x3F)         |
76    ((d & 0xF) << 6)    |
77    ((s & 0xF) << 10)   |
78    ((sx & 3) << 14)    |
79    ((sy & 3) << 16)    |
80    ((sz & 3) << 18)    |
81    ((sw & 3) << 20)    |
82    ((m & 0xF) << 22)
83}
84
85impl Parse for VUProgram {
86    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
87        let mut instr = Vec::new();
88
89        while !input.is_empty() {
90            let op = input.parse::<Ident>()?;
91            let op_str = op.to_string();
92
93            match op_str.as_str() {
94                "ld" => {
95                    let dst = input.parse::<Ident>()?;
96                    let dst = parse_reg(dst)?;
97
98                    let src_token = input.parse::<Lit>()?;
99                    let src = match src_token.clone() {
100                        Lit::Int(v) => v,
101                        _ => {
102                            return Err(syn::Error::new(src_token.span().into(), "Invalid argument"));
103                        }
104                    }.base10_parse::<u32>()?;
105
106                    if src >= MAX_INPUT_SLOTS {
107                        return Err(syn::Error::new(src_token.span().into(), "Input vertex slot index out of range"));
108                    }
109
110                    instr.push(encode_instr(0, dst, src, 0, 0, 0, 0, 0));
111                }
112                "st" => {
113                    let dst = input.parse::<Ident>()?;
114                    let dst = parse_output(dst)?;
115
116                    let src = input.parse::<Ident>()?;
117                    let src = parse_reg(src)?;
118
119                    instr.push(encode_instr(1, dst, src, 0, 0, 0, 0, 0));
120                }
121                "ldc" => {
122                    let dst = input.parse::<Ident>()?;
123                    let dst = parse_reg(dst)?;
124
125                    let src_token = input.parse::<Lit>()?;
126                    let src = match src_token.clone() {
127                        Lit::Int(v) => v,
128                        _ => {
129                            return Err(syn::Error::new(src_token.span().into(), "Invalid argument"));
130                        }
131                    }.base10_parse::<u32>()?;
132
133                    if src >= MAX_CONSTANT_SLOTS {
134                        return Err(syn::Error::new(src_token.span().into(), "Input constant slot index out of range"));
135                    }
136
137                    instr.push(encode_instr(2, dst, src, 0, 0, 0, 0, 0));
138                }
139                "add" => {
140                    let dst = input.parse::<Ident>()?;
141                    let dst = parse_reg(dst)?;
142
143                    let src = input.parse::<Ident>()?;
144                    let src = parse_reg(src)?;
145
146                    instr.push(encode_instr(3, dst, src, 0, 0, 0, 0, 0));
147                }
148                "sub" => {
149                    let dst = input.parse::<Ident>()?;
150                    let dst = parse_reg(dst)?;
151
152                    let src = input.parse::<Ident>()?;
153                    let src = parse_reg(src)?;
154
155                    instr.push(encode_instr(4, dst, src, 0, 0, 0, 0, 0));
156                }
157                "mul" => {
158                    let dst = input.parse::<Ident>()?;
159                    let dst = parse_reg(dst)?;
160
161                    let src = input.parse::<Ident>()?;
162                    let src = parse_reg(src)?;
163
164                    instr.push(encode_instr(5, dst, src, 0, 0, 0, 0, 0));
165                }
166                "div" => {
167                    let dst = input.parse::<Ident>()?;
168                    let dst = parse_reg(dst)?;
169
170                    let src = input.parse::<Ident>()?;
171                    let src = parse_reg(src)?;
172
173                    instr.push(encode_instr(6, dst, src, 0, 0, 0, 0, 0));
174                }
175                "dot" => {
176                    let dst = input.parse::<Ident>()?;
177                    let dst = parse_reg(dst)?;
178
179                    let src = input.parse::<Ident>()?;
180                    let src = parse_reg(src)?;
181
182                    instr.push(encode_instr(7, dst, src, 0, 0, 0, 0, 0));
183                }
184                "abs" => {
185                    let dst = input.parse::<Ident>()?;
186                    let dst = parse_reg(dst)?;
187
188                    let src = input.parse::<Ident>()?;
189                    let src = parse_reg(src)?;
190
191                    instr.push(encode_instr(8, dst, src, 0, 0, 0, 0, 0));
192                }
193                "sign" => {
194                    let dst = input.parse::<Ident>()?;
195                    let dst = parse_reg(dst)?;
196
197                    let src = input.parse::<Ident>()?;
198                    let src = parse_reg(src)?;
199
200                    instr.push(encode_instr(9, dst, src, 0, 0, 0, 0, 0));
201                }
202                "sqrt" => {
203                    let dst = input.parse::<Ident>()?;
204                    let dst = parse_reg(dst)?;
205
206                    let src = input.parse::<Ident>()?;
207                    let src = parse_reg(src)?;
208
209                    instr.push(encode_instr(10, dst, src, 0, 0, 0, 0, 0));
210                }
211                "pow" => {
212                    let dst = input.parse::<Ident>()?;
213                    let dst = parse_reg(dst)?;
214
215                    let src = input.parse::<Ident>()?;
216                    let src = parse_reg(src)?;
217
218                    instr.push(encode_instr(11, dst, src, 0, 0, 0, 0, 0));
219                }
220                "exp" => {
221                    let dst = input.parse::<Ident>()?;
222                    let dst = parse_reg(dst)?;
223
224                    let src = input.parse::<Ident>()?;
225                    let src = parse_reg(src)?;
226
227                    instr.push(encode_instr(12, dst, src, 0, 0, 0, 0, 0));
228                }
229                "log" => {
230                    let dst = input.parse::<Ident>()?;
231                    let dst = parse_reg(dst)?;
232
233                    let src = input.parse::<Ident>()?;
234                    let src = parse_reg(src)?;
235
236                    instr.push(encode_instr(13, dst, src, 0, 0, 0, 0, 0));
237                }
238                "min" => {
239                    let dst = input.parse::<Ident>()?;
240                    let dst = parse_reg(dst)?;
241
242                    let src = input.parse::<Ident>()?;
243                    let src = parse_reg(src)?;
244
245                    instr.push(encode_instr(14, dst, src, 0, 0, 0, 0, 0));
246                }
247                "max" => {
248                    let dst = input.parse::<Ident>()?;
249                    let dst = parse_reg(dst)?;
250
251                    let src = input.parse::<Ident>()?;
252                    let src = parse_reg(src)?;
253
254                    instr.push(encode_instr(15, dst, src, 0, 0, 0, 0, 0));
255                }
256                "sin" => {
257                    let dst = input.parse::<Ident>()?;
258                    let dst = parse_reg(dst)?;
259
260                    let src = input.parse::<Ident>()?;
261                    let src = parse_reg(src)?;
262
263                    instr.push(encode_instr(16, dst, src, 0, 0, 0, 0, 0));
264                }
265                "cos" => {
266                    let dst = input.parse::<Ident>()?;
267                    let dst = parse_reg(dst)?;
268
269                    let src = input.parse::<Ident>()?;
270                    let src = parse_reg(src)?;
271
272                    instr.push(encode_instr(17, dst, src, 0, 0, 0, 0, 0));
273                }
274                "tan" => {
275                    let dst = input.parse::<Ident>()?;
276                    let dst = parse_reg(dst)?;
277
278                    let src = input.parse::<Ident>()?;
279                    let src = parse_reg(src)?;
280
281                    instr.push(encode_instr(18, dst, src, 0, 0, 0, 0, 0));
282                }
283                "asin" => {
284                    let dst = input.parse::<Ident>()?;
285                    let dst = parse_reg(dst)?;
286
287                    let src = input.parse::<Ident>()?;
288                    let src = parse_reg(src)?;
289
290                    instr.push(encode_instr(19, dst, src, 0, 0, 0, 0, 0));
291                }
292                "acos" => {
293                    let dst = input.parse::<Ident>()?;
294                    let dst = parse_reg(dst)?;
295
296                    let src = input.parse::<Ident>()?;
297                    let src = parse_reg(src)?;
298
299                    instr.push(encode_instr(20, dst, src, 0, 0, 0, 0, 0));
300                }
301                "atan" => {
302                    let dst = input.parse::<Ident>()?;
303                    let dst = parse_reg(dst)?;
304
305                    let src = input.parse::<Ident>()?;
306                    let src = parse_reg(src)?;
307
308                    instr.push(encode_instr(21, dst, src, 0, 0, 0, 0, 0));
309                }
310                "atan2" => {
311                    let dst = input.parse::<Ident>()?;
312                    let dst = parse_reg(dst)?;
313
314                    let src = input.parse::<Ident>()?;
315                    let src = parse_reg(src)?;
316
317                    instr.push(encode_instr(22, dst, src, 0, 0, 0, 0, 0));
318                }
319                "shf" => {
320                    let dst = input.parse::<Ident>()?;
321                    let dst = parse_reg(dst)?;
322
323                    let src = input.parse::<Ident>()?;
324                    let src = parse_reg(src)?;
325
326                    let sw = input.parse::<Ident>()?;
327                    let sw = parse_swizzle(sw)?;
328
329                    let mask_token = input.parse::<Lit>()?;
330                    let mask = match mask_token.clone() {
331                        Lit::Int(v) => v,
332                        _ => {
333                            return Err(syn::Error::new(mask_token.span().into(), "Invalid argument"));
334                        }
335                    }.base10_parse::<u32>()?;
336
337                    if mask & 0b1111 != mask {
338                        return Err(syn::Error::new(mask_token.span().into(), "Mask value out of range (expected 4-bit value)"));
339                    }
340
341                    instr.push(encode_instr(23, dst, src, sw.0, sw.1, sw.2, sw.3, mask));
342                }
343                "mulm" => {
344                    let dst = input.parse::<Ident>()?;
345                    let dst = parse_reg(dst)?;
346
347                    let src = input.parse::<Ident>()?;
348                    let src = parse_reg(src)?;
349
350                    instr.push(encode_instr(24, dst, src, 0, 0, 0, 0, 0));
351                }
352                "end" => {
353                    instr.push(0x3F);
354                }
355                _ => {
356                    return Err(syn::Error::new(op.span().into(), "Invalid opcode"));
357                }
358            };
359        }
360
361        // terminate with "end" instruction
362        if instr.len() < 64 {
363            instr.push(0x3F);
364        }
365        else if instr.len() > 64 {
366            return Err(syn::Error::new(input.span().into(), "Program too large (must be no more than 64 instructions)"));
367        }
368        
369        Ok(VUProgram {
370            instructions: instr
371        })
372    }
373}
374
375#[proc_macro]
376/// Takes Dreambox VU assembly as input and produces an inline array of encoded VU instructions
377pub fn vu_asm(input: TokenStream) -> TokenStream {
378    let input_stream = parse_macro_input!(input as VUProgram);
379    let instr = input_stream.instructions;
380
381    quote! {
382        [
383            #(#instr),*
384        ]
385    }.into()
386}