e23d8b03c12b76f833a93a019932592788f42f9f
[mw/milkymist.git] / software / libfpvm / fpvm.c
1 /*
2  * Milkymist VJ SoC (Software)
3  * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <string.h>
19 #include <stdio.h>
20 #include <base/version.h>
21
22 #include <fpvm/is.h>
23 #include <fpvm/fpvm.h>
24
25 #include "ast.h"
26 #include "parser_helper.h"
27
28 const char *fpvm_version()
29 {
30         return VERSION;
31 }
32
33 void fpvm_init(struct fpvm_fragment *fragment, int vector_mode)
34 {
35         fragment->last_error[0] = 0;
36
37         fragment->nbindings = 3;
38         fragment->bindings[0].isvar = 1;
39         fragment->bindings[0].b.v[0] = '_';
40         fragment->bindings[0].b.v[1] = 'X';
41         fragment->bindings[0].b.v[2] = 'i';
42         fragment->bindings[0].b.v[3] = 0;
43         fragment->bindings[1].isvar = 1;
44         fragment->bindings[1].b.v[0] = '_';
45         fragment->bindings[1].b.v[1] = 'Y';
46         fragment->bindings[1].b.v[2] = 'i';
47         fragment->bindings[1].b.v[3] = 0;
48         /* Prevent binding of R2 (we need it for "if") */
49         fragment->bindings[2].isvar = 1;
50         fragment->bindings[2].b.v[0] = 0;
51
52         fragment->ntbindings = 2;
53         fragment->tbindings[0].reg = -1;
54         fragment->tbindings[0].sym[0] = '_';
55         fragment->tbindings[0].sym[1] = 'X';
56         fragment->tbindings[0].sym[2] = 'o';
57         fragment->tbindings[0].sym[3] = 0;
58         fragment->tbindings[1].reg = -2;
59         fragment->tbindings[1].sym[0] = '_';
60         fragment->tbindings[1].sym[1] = 'Y';
61         fragment->tbindings[1].sym[2] = 'o';
62         fragment->tbindings[1].sym[3] = 0;
63
64         fragment->nrenamings = 0;
65         
66         fragment->next_sur = -3;
67         fragment->ninstructions = 0;
68
69         fragment->bind_mode = 0;
70         fragment->vector_mode = vector_mode;
71 }
72
73 int fpvm_bind(struct fpvm_fragment *fragment, const char *sym)
74 {
75         if(fragment->nbindings == FPVM_MAXBINDINGS) {
76                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Failed to allocate register for variable: %s", sym);
77                 return FPVM_INVALID_REG;
78         }
79         fragment->bindings[fragment->nbindings].isvar = 1;
80         strcpy(fragment->bindings[fragment->nbindings].b.v, sym);
81         return fragment->nbindings++;
82 }
83
84 void fpvm_set_xin(struct fpvm_fragment *fragment, const char *sym)
85 {
86         strcpy(fragment->bindings[0].b.v, sym);
87 }
88
89 void fpvm_set_yin(struct fpvm_fragment *fragment, const char *sym)
90 {
91         strcpy(fragment->bindings[1].b.v, sym);
92 }
93
94 void fpvm_set_xout(struct fpvm_fragment *fragment, const char *sym)
95 {
96         strcpy(fragment->tbindings[0].sym, sym);
97 }
98
99 void fpvm_set_yout(struct fpvm_fragment *fragment, const char *sym)
100 {
101         strcpy(fragment->tbindings[1].sym, sym);
102 }
103
104 static int lookup(struct fpvm_fragment *fragment, const char *sym)
105 {
106         int i;
107
108         for(i=0;i<fragment->nrenamings;i++)
109                 if(strcmp(sym, fragment->renamings[i].sym) == 0)
110                         return fragment->renamings[i].reg;
111         for(i=0;i<fragment->nbindings;i++)
112                 if(fragment->bindings[i].isvar &&
113                         (strcmp(sym, fragment->bindings[i].b.v) == 0))
114                         return i;
115         for(i=0;i<fragment->ntbindings;i++)
116                 if(strcmp(sym, fragment->tbindings[i].sym) == 0)
117                         return fragment->tbindings[i].reg;
118         return FPVM_INVALID_REG;
119 }
120
121 static int tbind(struct fpvm_fragment *fragment, const char *sym)
122 {
123         if(fragment->ntbindings == FPVM_MAXTBINDINGS) {
124                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Failed to allocate register for variable: %s", sym);
125                 return FPVM_INVALID_REG;
126         }
127         fragment->tbindings[fragment->ntbindings].reg = fragment->next_sur;
128         strcpy(fragment->tbindings[fragment->ntbindings].sym, sym);
129         fragment->ntbindings++;
130         return fragment->next_sur--;
131 }
132
133 static int rename(struct fpvm_fragment *fragment, const char *sym, int reg)
134 {
135         int i;
136         
137         for(i=0;i<fragment->nrenamings;i++)
138                 if(strcmp(sym, fragment->renamings[i].sym) == 0) {
139                         fragment->renamings[i].reg = reg;
140                         return 1;
141                 }
142         if(fragment->nrenamings == FPVM_MAXRENAMINGS) {
143                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Failed to allocate renamed register for variable: %s", sym);
144                 return 0;
145         }
146         fragment->renamings[fragment->nrenamings].reg = reg;
147         strcpy(fragment->renamings[fragment->nrenamings].sym, sym);
148         fragment->nrenamings++;
149         return 1;
150 }
151
152 static int sym_to_reg(struct fpvm_fragment *fragment, const char *sym, int *created)
153 {
154         int r;
155         if(created) *created = 0;
156         r = lookup(fragment, sym);
157         if(r == FPVM_INVALID_REG) {
158                 if(created) *created = 1;
159                 if(fragment->bind_mode)
160                         r = fpvm_bind(fragment, sym);
161                 else
162                         r = tbind(fragment, sym);
163         }
164         return r;
165 }
166
167 static int const_to_reg(struct fpvm_fragment *fragment, float c)
168 {
169         int i;
170
171         for(i=0;i<fragment->nbindings;i++) {
172                 if(!fragment->bindings[i].isvar &&
173                         (fragment->bindings[i].b.c == c))
174                         return i;
175         }
176         /* not already bound */
177         if(fragment->nbindings == FPVM_MAXBINDINGS) {
178                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Failed to allocate register for constant");
179                 return FPVM_INVALID_REG;
180         }
181         fragment->bindings[fragment->nbindings].isvar = 0;
182         fragment->bindings[fragment->nbindings].b.c = c;
183         return fragment->nbindings++;
184 }
185
186 static int add_isn(struct fpvm_fragment *fragment, int opcode, int opa, int opb, int dest)
187 {
188         int len;
189
190         len = fragment->ninstructions;
191         if(len >= FPVM_MAXCODELEN) {
192                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Ran out of program space");
193                 return 0;
194         }
195
196         fragment->code[len].opa = opa;
197         fragment->code[len].opb = opb;
198         fragment->code[len].opcode = opcode;
199         fragment->code[len].dest = dest;
200
201         fragment->ninstructions++;
202         return 1;
203 }
204
205 static int operator2opcode(const char *operator)
206 {
207         if(strcmp(operator, "+") == 0) return FPVM_OPCODE_FADD;
208         if(strcmp(operator, "-") == 0) return FPVM_OPCODE_FSUB;
209         if(strcmp(operator, "*") == 0) return FPVM_OPCODE_FMUL;
210         if(strcmp(operator, "abs") == 0) return FPVM_OPCODE_FABS;
211         if(strcmp(operator, "isin") == 0) return FPVM_OPCODE_SIN;
212         if(strcmp(operator, "icos") == 0) return FPVM_OPCODE_COS;
213         if(strcmp(operator, "above") == 0) return FPVM_OPCODE_ABOVE;
214         if(strcmp(operator, "equal") == 0) return FPVM_OPCODE_EQUAL;
215         if(strcmp(operator, "i2f") == 0) return FPVM_OPCODE_I2F;
216         if(strcmp(operator, "f2i") == 0) return FPVM_OPCODE_F2I;
217         if(strcmp(operator, "if") == 0) return FPVM_OPCODE_IF;
218         if(strcmp(operator, "tsign") == 0) return FPVM_OPCODE_TSIGN;
219         if(strcmp(operator, "quake") == 0) return FPVM_OPCODE_QUAKE;
220         else return -1;
221 }
222
223 static int add_inv_sqrt_step(struct fpvm_fragment *fragment, int reg_y, int reg_x, int reg_out)
224 {
225         int reg_onehalf;
226         int reg_twohalf;
227         int reg_yy;
228         int reg_hx;
229         int reg_hxyy;
230         int reg_sub;
231
232         reg_yy = fragment->next_sur--;
233         reg_hx = fragment->next_sur--;
234         reg_hxyy = fragment->next_sur--;
235         reg_sub = fragment->next_sur--;
236
237         reg_onehalf = const_to_reg(fragment, 0.5f);
238         if(reg_onehalf == FPVM_INVALID_REG) return 0;
239         reg_twohalf = const_to_reg(fragment, 1.5f);
240         if(reg_twohalf == FPVM_INVALID_REG) return 0;
241
242         if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_y, reg_y, reg_yy)) return 0;
243         if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_onehalf, reg_x, reg_hx)) return 0;
244         if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_hx, reg_yy, reg_hxyy)) return 0;
245         if(!add_isn(fragment, FPVM_OPCODE_FSUB, reg_twohalf, reg_hxyy, reg_sub)) return 0;
246         if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_sub, reg_y, reg_out)) return 0;
247
248         return 1;
249 }
250
251 static int add_inv_sqrt(struct fpvm_fragment *fragment, int reg_in, int reg_out)
252 {
253         int reg_y, reg_y2;
254
255         reg_y = fragment->next_sur--;
256         reg_y2 = fragment->next_sur--;
257         
258         if(!add_isn(fragment, FPVM_OPCODE_QUAKE, reg_in, 0, reg_y)) return 0;
259         if(!add_inv_sqrt_step(fragment, reg_y, reg_in, reg_y2)) return 0;
260         if(!add_inv_sqrt_step(fragment, reg_y2, reg_in, reg_out)) return 0;
261         
262         return 1;
263 }
264
265 static int add_int(struct fpvm_fragment *fragment, int reg_in, int reg_out)
266 {
267         int reg_i;
268         
269         reg_i = fragment->next_sur--;
270         if(!add_isn(fragment, FPVM_OPCODE_F2I, reg_in, 0, reg_i)) return FPVM_INVALID_REG;
271         if(!add_isn(fragment, FPVM_OPCODE_I2F, reg_i, 0, reg_out)) return FPVM_INVALID_REG;
272         return 1;
273 }
274
275 /*
276  * Compiles a node.
277  * Returns the register the result of the node gets written to,
278  * and FPVM_INVALID_REG in case of error.
279  * If reg != FPVM_INVALID_REG,
280  * it forces the result to be written to this particular register.
281  */
282 static int compile(struct fpvm_fragment *fragment, int reg, struct ast_node *node)
283 {
284         int opa, opb;
285         int opcode;
286
287         if(node->label[0] == 0) {
288                 /* AST node is a constant */
289                 opa = const_to_reg(fragment, node->contents.constant);
290                 if(opa == FPVM_INVALID_REG) return FPVM_INVALID_REG;
291                 if(reg != FPVM_INVALID_REG) {
292                         if(!add_isn(fragment, FPVM_OPCODE_COPY, opa, 0, reg)) return FPVM_INVALID_REG;
293                 } else
294                         reg = opa;
295                 return reg;
296         }
297         if(node->contents.branches.a == NULL) {
298                 /* AST node is a variable */
299                 if(fragment->bind_mode) {
300                         opa = sym_to_reg(fragment, node->label, NULL);
301                         if(opa == FPVM_INVALID_REG) return FPVM_INVALID_REG;
302                 } else {
303                         opa = lookup(fragment, node->label);
304                         if((opa == FPVM_INVALID_REG)||(opa == fragment->final_dest)) {
305                                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Reading unbound variable: %s", node->label);
306                                 return FPVM_INVALID_REG;
307                         }
308                 }
309                 if(reg != FPVM_INVALID_REG) {
310                         if(!add_isn(fragment, FPVM_OPCODE_COPY, opa, 0, reg)) return FPVM_INVALID_REG;
311                 } else
312                         reg = opa;
313                 return reg;
314         }
315         /* AST node is an operator or function */
316         if(strcmp(node->label, "if") == 0) {
317                 /*
318                  * "if" must receive a special treatment.
319                  * It is implemented as a ternary function,
320                  * but its first parameter is hardwired to R2 (FPVM_REG_IFB) and implicit.
321                  * We must compute the other parameters first, as they may clobber R2.
322                  */
323                 opa = compile(fragment, FPVM_INVALID_REG, node->contents.branches.b);
324                 if(opa == FPVM_INVALID_REG) return FPVM_INVALID_REG;
325                 opb = compile(fragment, FPVM_INVALID_REG, node->contents.branches.c);
326                 if(opb == FPVM_INVALID_REG) return FPVM_INVALID_REG;
327                 if(compile(fragment, FPVM_REG_IFB, node->contents.branches.a) == FPVM_INVALID_REG)
328                         return FPVM_INVALID_REG;
329         } else {
330                 opa = compile(fragment, FPVM_INVALID_REG, node->contents.branches.a);
331                 if(opa == FPVM_INVALID_REG) return FPVM_INVALID_REG;
332                 opb = 0;
333                 if(node->contents.branches.b != NULL) {
334                         opb = compile(fragment, FPVM_INVALID_REG, node->contents.branches.b);
335                         if(opb == FPVM_INVALID_REG) return FPVM_INVALID_REG;
336                 }
337         }
338
339         if(reg == FPVM_INVALID_REG) reg = fragment->next_sur--;
340         if(strcmp(node->label, "below") == 0) {
341                 /*
342                  * "below" is like "above", but with reversed operands.
343                  */
344                 if(!add_isn(fragment, FPVM_OPCODE_ABOVE, opb, opa, reg)) return FPVM_INVALID_REG;
345         } else if((strcmp(node->label, "sin") == 0)||(strcmp(node->label, "cos") == 0)) {
346                 /*
347                  * Trigo functions are implemented with several instructions.
348                  * We must convert the floating point argument in radians
349                  * to an integer expressed in 1/8192 turns for FPVM.
350                  */
351                 int reg_const;
352                 int reg_mul;
353                 int reg_f2i;
354
355                 if(strcmp(node->label, "sin") == 0)
356                         opcode = FPVM_OPCODE_SIN;
357                 else
358                         opcode = FPVM_OPCODE_COS;
359
360                 reg_const = const_to_reg(fragment, FPVM_TRIG_CONV);
361                 if(reg_const == FPVM_INVALID_REG) return FPVM_INVALID_REG;
362                 reg_mul = fragment->next_sur--;
363                 reg_f2i = fragment->next_sur--;
364
365                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_const, opa, reg_mul)) return FPVM_INVALID_REG;
366                 if(!add_isn(fragment, FPVM_OPCODE_F2I, reg_mul, 0, reg_f2i)) return FPVM_INVALID_REG;
367                 if(!add_isn(fragment, opcode, reg_f2i, 0, reg)) return FPVM_INVALID_REG;
368         } else if(strcmp(node->label, "sqrt") == 0) {
369                 /*
370                  * Square root is implemented with a variant of the Quake III algorithm.
371                  * See http://en.wikipedia.org/wiki/Fast_inverse_square_root
372                  * sqrt(x) = x*(1/sqrt(x))
373                  */
374                 int reg_invsqrt;
375                 reg_invsqrt = fragment->next_sur--;
376                 if(!add_inv_sqrt(fragment, opa, reg_invsqrt)) return FPVM_INVALID_REG;
377                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, opa, reg_invsqrt, reg)) return FPVM_INVALID_REG;
378         } else if(strcmp(node->label, "invsqrt") == 0) {
379                 if(!add_inv_sqrt(fragment, opa, reg)) return FPVM_INVALID_REG;
380         } else if(strcmp(node->label, "/") == 0) {
381                 /*
382                  * Floating point division is implemented as
383                  * a/b = a*(1/sqrt(b))*(1/sqrt(b))
384                  */
385                 int reg_a2;
386                 int reg_b2;
387                 int reg_invsqrt;
388                 int reg_invsqrt2;
389                 
390                 reg_a2 = fragment->next_sur--;
391                 reg_b2 = fragment->next_sur--;
392                 reg_invsqrt = fragment->next_sur--;
393                 reg_invsqrt2 = fragment->next_sur--;
394
395                 /* Transfer the sign of the result to a and make b positive */
396                 if(!add_isn(fragment, FPVM_OPCODE_TSIGN, opa, opb, reg_a2)) return FPVM_INVALID_REG;
397                 if(!add_isn(fragment, FPVM_OPCODE_FABS, opb, 0, reg_b2)) return FPVM_INVALID_REG;
398                 
399                 if(!add_inv_sqrt(fragment, reg_b2, reg_invsqrt)) return FPVM_INVALID_REG;
400                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_invsqrt, reg_invsqrt, reg_invsqrt2)) return FPVM_INVALID_REG;
401                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_invsqrt2, reg_a2, reg)) return FPVM_INVALID_REG;
402         } else if(strcmp(node->label, "%") == 0) {
403                 int reg_invsqrt;
404                 int reg_invsqrt2;
405                 int reg_div;
406                 int reg_idiv;
407                 int reg_bidiv;
408
409                 reg_invsqrt = fragment->next_sur--;
410                 reg_invsqrt2 = fragment->next_sur--;
411                 reg_div = fragment->next_sur--;
412                 reg_idiv = fragment->next_sur--;
413                 reg_bidiv = fragment->next_sur--;
414
415                 if(!add_inv_sqrt(fragment, opb, reg_invsqrt)) return FPVM_INVALID_REG;
416                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_invsqrt, reg_invsqrt, reg_invsqrt2)) return FPVM_INVALID_REG;
417                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, reg_invsqrt2, opa, reg_div)) return FPVM_INVALID_REG;
418                 if(!add_int(fragment, reg_div, reg_idiv)) return FPVM_INVALID_REG;
419                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, opb, reg_idiv, reg_bidiv)) return FPVM_INVALID_REG;
420                 if(!add_isn(fragment, FPVM_OPCODE_FSUB, opa, reg_bidiv, reg)) return FPVM_INVALID_REG;
421         } else if(strcmp(node->label, "min") == 0) {
422                 if(!add_isn(fragment, FPVM_OPCODE_ABOVE, opa, opb, FPVM_REG_IFB)) return FPVM_INVALID_REG;
423                 if(!add_isn(fragment, FPVM_OPCODE_IF, opb, opa, reg)) return FPVM_INVALID_REG;
424         } else if(strcmp(node->label, "max") == 0) {
425                 if(!add_isn(fragment, FPVM_OPCODE_ABOVE, opa, opb, FPVM_REG_IFB)) return FPVM_INVALID_REG;
426                 if(!add_isn(fragment, FPVM_OPCODE_IF, opa, opb, reg)) return FPVM_INVALID_REG;
427         } else if(strcmp(node->label, "sqr") == 0) {
428                 if(!add_isn(fragment, FPVM_OPCODE_FMUL, opa, opa, reg)) return FPVM_INVALID_REG;
429         } else if(strcmp(node->label, "int") == 0) {
430                 if(!add_int(fragment, opa, reg)) return FPVM_INVALID_REG;
431         } else {
432                 /* Normal case */
433                 opcode = operator2opcode(node->label);
434                 if(opcode < 0) {
435                         snprintf(fragment->last_error, FPVM_MAXERRLEN, "Operation not supported: %s", node->label);
436                         return FPVM_INVALID_REG;
437                 }
438                 if(!add_isn(fragment, opcode, opa, opb, reg)) return FPVM_INVALID_REG;
439         }
440
441         return reg;
442 }
443
444 struct fpvm_backup {
445         int ntbindings;
446         int next_sur;
447         int ninstructions;
448 };
449
450 static void fragment_backup(struct fpvm_fragment *fragment, struct fpvm_backup *backup)
451 {
452         backup->ntbindings = fragment->ntbindings;
453         backup->next_sur = fragment->next_sur;
454         backup->ninstructions = fragment->ninstructions;
455 }
456
457 static void fragment_restore(struct fpvm_fragment *fragment, struct fpvm_backup *backup)
458 {
459         fragment->ntbindings = backup->ntbindings;
460         fragment->next_sur = backup->next_sur;
461         fragment->ninstructions = backup->ninstructions;
462 }
463
464 int fpvm_assign(struct fpvm_fragment *fragment, const char *dest, const char *expr)
465 {
466         struct ast_node *n;
467         int dest_reg;
468         struct fpvm_backup backup;
469         int created;
470         int use_renaming;
471
472         n = fpvm_parse(expr);
473         if(n == NULL) {
474                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Parse error");
475                 return 0;
476         }
477
478         fragment_backup(fragment, &backup);
479
480         use_renaming = fragment->vector_mode
481                 && (strcmp(dest, fragment->tbindings[0].sym) != 0) /* do not rename output X and Y */
482                 && (strcmp(dest, fragment->tbindings[1].sym) != 0);
483         if(use_renaming) {
484                 dest_reg = fragment->next_sur;
485                 fragment->next_sur--;
486                 created = 1;
487         } else
488                 dest_reg = sym_to_reg(fragment, dest, &created);
489         if(dest_reg == FPVM_INVALID_REG) {
490                 snprintf(fragment->last_error, FPVM_MAXERRLEN, "Failed to allocate register for destination");
491                 fpvm_parse_free(n);
492                 fragment_restore(fragment, &backup);
493                 return 0;
494         }
495
496         if(created)
497                 fragment->final_dest = dest_reg;
498         else
499                 fragment->final_dest = FPVM_INVALID_REG;
500         if(compile(fragment, dest_reg, n) == FPVM_INVALID_REG) {
501                 fpvm_parse_free(n);
502                 fragment_restore(fragment, &backup);
503                 return 0;
504         }
505         if(use_renaming) {
506                 if(!rename(fragment, dest, dest_reg)) {
507                         fpvm_parse_free(n);
508                         fragment_restore(fragment, &backup);
509                         return 0;
510                 }
511         }
512
513         fpvm_parse_free(n);
514         return 1;
515 }
516
517 void fpvm_get_references(struct fpvm_fragment *fragment, int *references)
518 {
519         int i;
520
521         for(i=0;i<FPVM_MAXBINDINGS;i++)
522                 references[i] = 0;
523         for(i=0;i<fragment->ninstructions;i++) {
524                 if(fragment->code[i].opcode == FPVM_OPCODE_IF)
525                         references[2] = 1;
526                 if(fragment->code[i].opa > 0)
527                         references[fragment->code[i].opa] = 1;
528                 if(fragment->code[i].opb > 0)
529                         references[fragment->code[i].opb] = 1;
530                 if(fragment->code[i].dest > 0)
531                         references[fragment->code[i].dest] = 1;
532         }
533 }
534
535 int fpvm_finalize(struct fpvm_fragment *fragment)
536 {
537         if(fragment->vector_mode)
538                 return add_isn(fragment, FPVM_OPCODE_VECTOUT, -1, -2, 0);
539         else
540                 return 1;
541 }
542
543 void fpvm_print_opcode(int opcode)
544 {
545         switch(opcode) {
546                 case FPVM_OPCODE_NOP:     printf("NOP     "); break;
547                 case FPVM_OPCODE_FADD:    printf("FADD    "); break;
548                 case FPVM_OPCODE_FSUB:    printf("FSUB    "); break;
549                 case FPVM_OPCODE_FMUL:    printf("FMUL    "); break;
550                 case FPVM_OPCODE_FABS:    printf("FABS    "); break;
551                 case FPVM_OPCODE_F2I:     printf("F2I     "); break;
552                 case FPVM_OPCODE_I2F:     printf("I2F     "); break;
553                 case FPVM_OPCODE_VECTOUT: printf("VECTOUT "); break;
554                 case FPVM_OPCODE_SIN:     printf("SIN     "); break;
555                 case FPVM_OPCODE_COS:     printf("COS     "); break;
556                 case FPVM_OPCODE_ABOVE:   printf("ABOVE   "); break;
557                 case FPVM_OPCODE_EQUAL:   printf("EQUAL   "); break;
558                 case FPVM_OPCODE_COPY:    printf("COPY    "); break;
559                 case FPVM_OPCODE_IF:      printf("IF<R2>  "); break;
560                 case FPVM_OPCODE_TSIGN:   printf("TSIGN   "); break;
561                 case FPVM_OPCODE_QUAKE:   printf("QUAKE   "); break;
562                 default:                  printf("XXX     "); break;
563         }
564 }
565
566 int fpvm_get_arity(int opcode)
567 {
568         switch(opcode) {
569                 case FPVM_OPCODE_IF:
570                         return 3;
571                 case FPVM_OPCODE_FADD:
572                 case FPVM_OPCODE_FSUB:
573                 case FPVM_OPCODE_FMUL:
574                 case FPVM_OPCODE_VECTOUT:
575                 case FPVM_OPCODE_EQUAL:
576                 case FPVM_OPCODE_ABOVE:
577                 case FPVM_OPCODE_TSIGN:
578                         return 2;
579                 case FPVM_OPCODE_FABS:
580                 case FPVM_OPCODE_F2I:
581                 case FPVM_OPCODE_I2F:
582                 case FPVM_OPCODE_SIN:
583                 case FPVM_OPCODE_COS:
584                 case FPVM_OPCODE_COPY:
585                 case FPVM_OPCODE_QUAKE:
586                         return 1;
587                 default:
588                         return 0;
589         }
590 }
591
592 void fpvm_dump(struct fpvm_fragment *fragment)
593 {
594         int i;
595         
596         printf("== Permanent bindings:\n");
597         for(i=0;i<fragment->nbindings;i++) {
598                 printf("R%04d ", i);
599                 if(fragment->bindings[i].isvar)
600                         printf("%s\n", fragment->bindings[i].b.v);
601                 else
602                         #ifdef PRINTF_FLOAT
603                         printf("%f\n", fragment->bindings[i].b.c);
604                         #else
605                         printf("%f\n", &fragment->bindings[i].b.c);
606                         #endif
607         }
608         printf("== Transient bindings:\n");
609         for(i=0;i<fragment->ntbindings;i++) {
610                 printf("R%04d ", fragment->tbindings[i].reg);
611                 printf("%s\n", fragment->tbindings[i].sym);
612         }
613         printf("== Code:\n");
614         for(i=0;i<fragment->ninstructions;i++) {
615                 printf("%04d: ", i);
616                 fpvm_print_opcode(fragment->code[i].opcode);
617                 switch(fpvm_get_arity(fragment->code[i].opcode)) {
618                         case 3:
619                         case 2:
620                                 printf("R%04d,R%04d ", fragment->code[i].opa, fragment->code[i].opb);
621                                 break;
622                         case 1:
623                                 printf("R%04d       ", fragment->code[i].opa);
624                                 break;
625                         case 0:
626                                 printf("            ");
627                                 break;
628                 }
629                 if(fragment->code[i].dest != 0)
630                         printf("-> R%04d", fragment->code[i].dest);
631                 printf("\n");
632         }
633 }