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 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]
376pub 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}