Bug Summary

File:dvdcompile.c
Location:line 177, column 5
Description:Value stored to 'canusesprm' is never read

Annotated Source Code

1/*
2 Code generation for the VM language
3*/
4/*
5 * Copyright (C) 2002 Scott Smith (trckjunky@users.sourceforge.net)
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or (at
10 * your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20 * MA 02110-1301 USA.
21 */
22
23#include "config.h"
24
25#include "compat.h"
26#include <assert.h>
27
28#include "dvdauthor.h"
29#include "da-internal.h"
30#include "dvdvm.h"
31
32
33struct vm_statement *dvd_vm_parsed_cmd;
34
35/* arbitrary implementation limits--should be adequate, given
36 the restrictions on the length of instruction sequences */
37#define MAXLABELS200 200
38#define MAXGOTOS200 200
39
40struct dvdlabel {
41 char *lname; /* label name */
42 unsigned char *code;
43 /* pointer into buf where label is defined or where goto instruction needs fixup */
44};
45
46static struct dvdlabel labels[MAXLABELS200];
47static struct dvdlabel gotos[MAXGOTOS200];
48static int numlabels=0, numgotos=0;
49
50static int negatecompare(int compareop)
51 /* returns the comparison with the opposite result. Assumes the op isn't BC ("&"). */
52 {
53 return compareop ^ 1 ^ ((compareop & 4) >> 1);
54 /* EQ <=> NE, GE <=> LT, GT <=> LE */
55 } /*negatecompare*/
56
57static int swapcompare(int compareop)
58 /* returns the equivalent comparison with the operands swapped. */
59 {
60 if (compareop < 4) /* BC, EQ, NE unchanged */
61 return compareop;
62 else
63 return compareop ^ 3; /* GE <=> LT, GT <=> LE */
64 } /*swapcompare*/
65
66static bool_Bool compile_usesreg(const struct vm_statement *cs, int target)
67 /* does cs reference the specified register. */
68 {
69 while (cs)
70 {
71 if (cs->op == VM_VAL)
72 return cs->i1 == target - 256;
73 if (compile_usesreg(cs->param, target))
74 return true1;
75 cs = cs->next;
76 } /*while*/
77 return false0;
78 } /*compile_usesreg*/
79
80static int nexttarget(int t)
81 /* returns the next register after t in the range I have reserved. Will fail
82 if it's all used, or if I haven't got a reserved range. */
83 {
84 if (!allowallreg)
85 {
86 if (t < 13)
87 return 13;
88 t++;
89 if (t < 16)
90 return t;
91 } /*if*/
92 fprintf(stderrstderr,"ERR: Expression is too complicated, ran out of registers\n");
93 exit(1);
94 } /*nexttarget*/
95
96// like nexttarget, but takes a VM_VAL argument
97static int nextval(int t)
98 {
99 if (t < -128)
100 return nexttarget(t + 256) - 256; /* it's a register, return next available register */
101 else
102 return nexttarget(-1) - 256; /* not a register, return first available register */
103 } /*nextval*/
104
105static unsigned char *compileop(unsigned char *buf, int target, int op, int val)
106 /* compiles a command to set the target GPRM to the result of the specified operation
107 on it and the specified value. */
108 {
109 if (op == VM_VAL && target == val + 256)
110 return buf; /* setting register to its same value => noop */
111 write8(buf, val >= 0 ? 0x70 : 0x60, 0x00, 0x00, target, val >= 0 ? (val >> 8) : 0x00, val, 0x00, 0x00);
112 /* target op= val (op to be filled in below) */
113 switch(op)
114 {
115 case VM_VAL: /* simple assignment */
116 buf[0] |= 1;
117 break;
118 case VM_ADD:
119 buf[0] |= 3;
120 break;
121 case VM_SUB:
122 buf[0] |= 4;
123 break;
124 case VM_MUL:
125 buf[0] |= 5;
126 break;
127 case VM_DIV:
128 buf[0] |= 6;
129 break;
130 case VM_MOD:
131 buf[0] |= 7;
132 break;
133 case VM_RND:
134 buf[0] |= 8;
135 break;
136 case VM_AND:
137 buf[0] |= 9;
138 break;
139 case VM_OR:
140 buf[0] |= 10;
141 break;
142 case VM_XOR:
143 buf[0] |= 11;
144 break;
145 default:
146 fprintf(stderrstderr, "ERR: Unknown op in compileop: %d\n", op);
147 exit(1);
148 } /*switch*/
149 return buf + 8;
150 } /*compileop*/
151
152static int issprmval(const struct vm_statement *v)
153 /* is operand v a reference to an SPRM value. */
154 {
155 return v->op == VM_VAL && v->i1 >= -128 && v->i1 < 0;
156 } /*issprmval*/
157
158static unsigned char *compileexpr(unsigned char *buf, int target, struct vm_statement *cs)
159 /* generates code to put the the value of an expression cs into GPRM target.
160 Returns pointer to after generated code. */
161 {
162 struct vm_statement *v, **vp;
163 int isassoc, canusesprm;
164 if (cs->op == VM_VAL) /* simple value reference */
165 return compileop(buf, target, VM_VAL, cs->i1); /* assign value to target */
166
167 isassoc = /* associative operator--just so happens these are also commutative */
168 cs->op == VM_ADD
169 ||
170 cs->op == VM_MUL
171 ||
172 cs->op == VM_AND
173 ||
174 cs->op == VM_OR
175 ||
176 cs->op == VM_XOR;
177 canusesprm = cs->op == VM_AND || cs->op == VM_OR || cs->op == VM_XOR;
Value stored to 'canusesprm' is never read
178 /* operations where the source may be an SPRM (also VM_VAL, but that was already dealt with) */
179
180 // if the target is an operator, move it to the front
181 if (isassoc)
182 {
183 for (vp = &cs->param->next; *vp; vp = &(vp[0]->next))
184 if (vp[0]->op == VM_VAL && vp[0]->i1 == target - 256)
185 {
186 v = *vp;
187 *vp = v->next; /* take out from its place in chain */
188 v->next = cs->param;
189 cs->param = v; /* and put the VM_VAL op on front of chain */
190 break;
191 } /*if*/
192 } /*if*/
193
194 if (compile_usesreg(cs->param->next, target))
195 {
196 /* cannot evaluate cs->param directly into target, because target is used
197 in evaluation */
198 const int t2 = nexttarget(target); /* need another register */
199 buf = compileexpr(buf, t2, cs); /* evaluate expr into t2 */
200 write8(buf, 0x61, 0x00, 0x00, target, 0x00, t2, 0x00, 0x00);
201 /* and then move value of t2 to target */
202 buf += 8;
203 if (t2 == 15)
204 /* just zapped a reserved register whose value might be misinterpreted elsewhere */
205 {
206 write8(buf, 0x71, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00);
207 /* g15 = 0 */
208 buf += 8;
209 } /*if*/
210 return buf;
211 } /*if*/
212
213 if (isassoc && cs->param->op == VM_VAL && cs->param->i1 != target - 256)
214 /* fixme: should "isassoc" be "canusesprm" instead? */
215 {
216 // if the first param is a value, then try to move a complex operation farther up or an SPRM access (if SPRM ops are not allowed)
217 for (vp = &cs->param->next; *vp; vp = &(vp[0]->next))
218 if (vp[0]->op != VM_VAL || issprmval(vp[0]))
219 {
220 v = *vp;
221 *vp = v->next; /* take out from its place in chain */
222 v->next = cs->param;
223 cs->param = v; /* and put the SPRM/non-VM_VAL op on front of chain */
224 break;
225 } /*if*/
226 } /*if*/
227
228 // special case -- rnd where the parameter is a reg or value
229 if (cs->op == VM_RND && cs->param->op == VM_VAL)
230 {
231 assert(cs->param->next == 0)((cs->param->next == 0) ? (void) (0) : __assert_fail ("cs->param->next == 0"
, "dvdcompile.c", 231, __PRETTY_FUNCTION__))
; /* only one operand */
232 return compileop(buf, target, cs->op, cs->param->i1);
233 } /*if*/
234
235 buf = compileexpr(buf, target, cs->param); /* use target for first/only operand */
236 if (cs->op == VM_RND)
237 {
238 assert(cs->param->next == 0)((cs->param->next == 0) ? (void) (0) : __assert_fail ("cs->param->next == 0"
, "dvdcompile.c", 238, __PRETTY_FUNCTION__))
; /* only one operand */
239 return compileop(buf, target, cs->op, target - 256);
240 /* operand from target, result into target */
241 }
242 else /* all other operators take two operands */
243 {
244 for (v = cs->param->next; v; v = v->next) /* process chain of operations */
245 {
246 if (v->op == VM_VAL && !issprmval(v))
247 buf = compileop(buf, target, cs->op, v->i1);
248 /* can simply put value straight into target */
249 else
250 {
251 const int t2 = nexttarget(target);
252 buf = compileexpr(buf, t2, v); /* put value of v into t2 */
253 buf = compileop(buf, target, cs->op, t2 - 256);
254 /* then value of that and target operated on by op into target */
255 if (t2 == 15)
256 {
257 /* just zapped a reserved register whose value might be misinterpreted elsewhere */
258 write8(buf, 0x71, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00);
259 /* g15 = 0 */
260 buf += 8;
261 } /*if*/
262 } /*if*/
263 } /*for*/
264 } /*if*/
265 return buf;
266 } /*compileexpr*/
267
268static unsigned char *compilebool
269 (
270 const unsigned char *obuf, /* start of buffer, for calculating instruction numbers */
271 unsigned char *buf, /* where to insert compiled instructions */
272 struct vm_statement *cs, /* expression to compile */
273 const unsigned char *iftrue, /* branch target for true */
274 const unsigned char *iffalse /* branch target for false */
275 )
276 /* compiles an expression that returns a true/false result, branching to iftrue or
277 iffalse depending. Returns pointer to after generated code. */
278 {
279 switch (cs->op)
280 {
281 case VM_EQ:
282 case VM_NE:
283 case VM_GTE:
284 case VM_GT:
285 case VM_LTE:
286 case VM_LT:
287 { /* the two operands are cs->param and cs->param->next */
288 int r1, r2, op;
289 op = cs->op - VM_EQ + 2;
290 /* convert to comparison encoding that can be inserted directly into instruction */
291 if (cs->param->op == VM_VAL)
292 r1 = cs->param->i1; /* value already here */
293 else /* cs->param is something more complex */
294 {
295 r1 = nextval(0); /* temporary place to put it */
296 buf = compileexpr(buf, r1 & 15, cs->param); /* put it there */
297 } /*if*/
298 /* at this point, r1 is literal/register containing first operand */
299 if (cs->param->next->op == VM_VAL && (r1 < 0 || cs->param->next->i1 < 0))
300 /* if one operand is a register and the other is a simple literal or register,
301 I can combine them directly */
302 r2 = cs->param->next->i1;
303 else /* not so simple */
304 {
305 r2 = nextval(r1);
306 buf = compileexpr(buf, r2 & 15, cs->param->next);
307 } /*if*/
308 /* at this point, r2 is literal/register containing second operand */
309 if (r1 >= 0)
310 {
311 /* literal value--swap with r2 */
312 const int t = r1;
313 r1 = r2;
314 r2 = t;
315 op = swapcompare(op);
316 } /*if*/
317 if (iffalse > iftrue)
318 {
319 /* make false branch the one earlier in buffer, in the hope I can fall through to it */
320 /* really only worth doing this if iffalse - buf == 8 */
321 const unsigned char * const t = iftrue;
322 iftrue = iffalse;
323 iffalse = t;
324 op = negatecompare(op);
325 } /*if*/
326 if (r2 >= 0)
327 write8(buf, 0x00, 0x81 | (op << 4), 0x00, r1, r2 >> 8, r2,0x00, 0x00);
328 /* if r1 op r2 then goto true branch */
329 else /* r1 and r2 both registers */
330 write8(buf, 0x00, 0x01 | (op << 4), 0x00, r1, 0x00, r2, 0x00, 0x00);
331 /* if r1 op r2 then goto true branch */
332 buf[7] = (iftrue - obuf) / 8 + 1; /* branch target instr nr */
333 buf += 8;
334 if (iffalse > buf)
335 {
336 /* can't fallthrough for false branch */
337 write8(buf, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
338 /* goto false branch */
339 buf[7] = (iffalse - obuf) / 8 + 1; /* branch target */
340 buf += 8;
341 } /*if*/
342 } /*case*/
343 break;
344
345 case VM_LOR:
346 case VM_LAND:
347 {
348 const int op = cs->op;
349 cs = cs->param;
350 while (cs->next) /* process chain of operands, doing short-cut evaluation */
351 {
352 /* compile this operand, branching to next or to final true/false destination
353 as appropriate */
354 unsigned char * n = buf + 8;
355 /* where to continue chain--assume it'll compile to one instruction to begin with */
356 while (true1) /* should loop no more than twice */
357 {
358 unsigned char * const nn = compilebool
359 (
360 /*obuf =*/ obuf,
361 /*buf =*/ buf,
362 /*cs =*/ cs,
363 /*iftrue =*/
364 op == VM_LAND ?
365 n /* continue AND-chain as long as conditions are true */
366 :
367 iftrue, /* no need to continue OR-chain on true */
368 /*iffalse =*/
369 op == VM_LOR ?
370 n /* continue OR-chain as long as conditions are false */
371 :
372 iffalse /* no need to continue AND-chain on false */
373 );
374 if (nn == n)
375 break;
376 /* too many instructions generated to fit */
377 n = nn; /* try again leaving this much room */
378 } /*while*/
379 buf = n;
380 cs = cs->next;
381 } /*while*/
382 buf = compilebool(obuf, buf, cs, iftrue, iffalse);
383 }
384 break;
385
386 case VM_NOT:
387 return compilebool(obuf, buf, cs->param, iffalse, iftrue);
388 /* re-enter myself with the operands swapped */
389
390 default:
391 fprintf(stderrstderr, "ERR: Unknown bool op: %d\n", cs->op);
392 exit(1);
393 } /*switch*/
394 return buf;
395 } /*compilebool*/
396
397// NOTE: curgroup is passed separately from curpgc, because in FPC, curpgc==NULL, but curgroup!=NULL
398static unsigned char *compilecs
399 (
400 const unsigned char *obuf,
401 unsigned char *buf,
402 const struct workset *ws,
403 const struct pgcgroup *curgroup,
404 const struct pgc *curpgc,
405 const struct vm_statement *cs,
406 vtypes ismenu /* needed to decide what kinds of jumps/calls are allowed */
407 )
408 /* compiles a parse tree into naive VM instructions: no optimization of conditionals,
409 and no fixup of gotos; these tasks are left to caller. */
410 {
411 bool_Bool lastif = false0;
412 while (cs)
413 {
414 if (cs->op != VM_NOP)
415 lastif = false0; /* no need for dummy target for last branch, I'll be providing a real one */
416 /* actually check for VM_NOP is unnecessary so long as no construct will
417 generate one */
418 switch (cs->op)
419 {
420 case VM_SET: /* cs->i1 is destination, cs->param is source */
421 switch (cs->i1)
422 {
423 case 0:
424 case 1:
425 case 2:
426 case 3:
427 case 4:
428 case 5:
429 case 6:
430 case 7:
431 case 8:
432 case 9:
433 case 10:
434 case 11:
435 case 12:
436 case 13:
437 case 14:
438 case 15: // set GPRM
439 buf = compileexpr(buf, cs->i1, cs->param);
440 break;
441
442 case 32 + 0:
443 case 32 + 1:
444 case 32 + 2:
445 case 32 + 3:
446 case 32 + 4:
447 case 32 + 5:
448 case 32 + 6:
449 case 32 + 7:
450 case 32 + 8:
451 case 32 + 9:
452 case 32 + 10:
453 case 32 + 11:
454 case 32 + 12:
455 case 32 + 13:
456 case 32 + 14:
457 case 32 + 15: // set GPRM, counter mode
458 if (cs->param->op == VM_VAL)
459 { // we can set counters to system registers
460 const int v = cs->param->i1; /* reg/literal value to set */
461 if (v < 0)
462 write8(buf, 0x43, 0x00, 0x00, v,0x00, 0x80 | (cs->i1 - 32), 0x00, 0x00);
463 /* SetGPRMMD indirect */
464 else
465 write8(buf, 0x53, 0x00, v / 256, v, 0x00, 0x80 | (cs->i1 - 32), 0x00, 0x00);
466 /* SetGPRMMD direct */
467 buf += 8;
468 }
469 else /* not so simple */
470 {
471 const int r = nexttarget(0); /* temporary place to put value */
472 buf = compileexpr(buf, r, cs->param); /* put it there */
473 write8(buf, 0x43, 0x00, 0x00, r, 0x00, 0x80 | (cs->i1 - 32), 0x00, 0x00);
474 /* SetGPRMMD indirect to r */
475 buf += 8;
476 } /*if*/
477 break;
478
479 case 128 + 1: // audio
480 case 128 + 2: // subtitle
481 case 128 + 3: // angle
482 if (cs->param->op == VM_VAL && !(cs->param->i1 >= -128 && cs->param->i1 < 0))
483 {
484 const int v = cs->param->i1;
485 write8(buf, v < 0 ? 0x41 : 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
486 /* SetSTN indirect/direct */
487 buf[(cs->i1 - 128) + 2] = 128 | v; // doesn't matter whether this is a register or a value
488 /* put new value/regnr into audio/subpicture/angle field as appropriate */
489 buf += 8;
490 }
491 else /* complex expression */
492 {
493 const int r = nexttarget(0);
494 buf = compileexpr(buf, r, cs->param);
495 write8(buf, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
496 /* SetSTN indirect */
497 buf[(cs->i1 - 128) + 2] = 128 | r;
498 /* put regnr containing new value into audio/subpicture/angle field as appropriate */
499 buf += 8;
500 } /*if*/
501 break;
502
503 case 128 + 8: // button
504 if (cs->param->op == VM_VAL && !(cs->param->i1 >= -128 && cs->param->i1 < 0))
505 {
506 const int v = cs->param->i1;
507 if (v > 0 && (v & 1023) != 0)
508 fprintf
509 (
510 stderrstderr,
511 "WARN: Button value is %d, but it should be a multiple of 1024\n"
512 "WARN: (button #1=1024, #2=2048, etc)\n",
513 v
514 );
515 if (v < 0)
516 write8(buf, 0x46, 0x00, 0x00, 0x00, 0x00, v, 0x00, 0x00);
517 /* SetHL_BTNN indirect */
518 else
519 write8(buf, 0x56, 0x00, 0x00, 0x00, v / 256, v, 0x00, 0x00);
520 /* SetHL_BTNN direct */
521 buf += 8;
522 }
523 else /* complex expression */
524 {
525 const int r = nexttarget(0);
526 buf = compileexpr(buf, r, cs->param);
527 write8(buf, 0x46, 0x00, 0x00, 0x00, 0x00, r, 0x00, 0x00);
528 /* SetHL_BTNN indirect */
529 buf += 8;
530 } /*if*/
531 break;
532
533 default:
534 fprintf(stderrstderr, "ERR: Cannot set SPRM %d\n", cs->i1 - 128);
535 return 0;
536 } /*switch*/
537 break;
538
539 case VM_IF: /* if-statement */
540 {
541 unsigned char * iftrue = buf + 8; /* initially try putting true branch here */
542 const unsigned char * iffalse = buf + 16; /* initially try putting false branch here */
543 unsigned char * end = buf + 16; /* initially assuming code will end here */
544 while (true1) /* should loop no more than twice */
545 {
546 unsigned char *lp, *ib, *e;
547 lp = compilecs(obuf, iftrue, ws, curgroup, curpgc, cs->param->next->param, ismenu);
548 /* the if-true part */
549 if (cs->param->next->next)
550 {
551 /* there's an else-part */
552 e = compilecs(obuf, lp + 8, ws, curgroup, curpgc, cs->param->next->next, ismenu);
553 /* compile the else-part, leaving room for following instr */
554 write8(lp, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, (e - obuf ) / 8 + 1);
555 /* insert a goto at the end of the if-true part to branch over the else-part */
556 lp += 8; /* include in true branch */
557 }
558 else
559 e = lp; /* code ends with true branch */
560 ib = compilebool(obuf, buf, cs->param, iftrue, iffalse);
561 /* put condition test at start */
562 if (!lp)
563 return 0;
564 /* at this point, ib is just after the condition test, lp is just after
565 the true branch, and e is just after the end of all the code */
566 if (ib == iftrue && lp == iffalse)
567 break; /* all fitted nicely */
568 /* didn't leave enough room for pieces next to each other, try again */
569 iftrue = ib; /* enough room for condition code */
570 iffalse = lp; /* enough room for true branch */
571 end = e;
572 } /*while*/
573 buf = end;
574 lastif = true1; // make sure reference statement is generated
575 }
576 break;
577
578 case VM_LABEL:
579 {
580 int i;
581 for (i = 0; i < numlabels; i++)
582 if (!strcasecmp(labels[i].lname, cs->s1))
583 {
584 fprintf(stderrstderr, "ERR: Duplicate label '%s'\n", cs->s1);
585 return 0;
586 } /*if; for*/
587 if (numlabels == MAXLABELS200)
588 {
589 fprintf(stderrstderr, "ERR: Too many labels\n");
590 return 0;
591 } /*if*/
592 labels[numlabels].lname = cs->s1;
593 labels[numlabels].code = buf; /* where label points to */
594 numlabels++;
595 lastif = true1; // make sure reference statement is generated
596 }
597 break;
598
599 case VM_GOTO:
600 if (numgotos == MAXGOTOS200)
601 {
602 fprintf(stderrstderr, "ERR: Too many gotos\n");
603 return 0;
604 } /*if*/
605 gotos[numgotos].lname = cs->s1;
606 gotos[numgotos].code = buf; /* point to instruction so it can be fixed up later */
607 numgotos++;
608 write8(buf, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
609 buf += 8;
610 break;
611
612 case VM_BREAK:
613 write8(buf, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
614 buf += 8;
615 break;
616
617 case VM_EXIT:
618 write8(buf, 0x30, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
619 buf += 8;
620 break;
621
622 case VM_LINK:
623 write8(buf, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, cs->i1);
624 buf += 8;
625 break;
626
627 case VM_JUMP:
628 {
629 int i1 = cs->i1; /* if nonzero, 1 for VMGM, or titleset nr + 1 */
630 int i2 = cs->i2; /* menu number or menu entry ID + 120 or title number + 128 */
631 /* cs->i3 is chapter number if nonzero and less than 65536;
632 or program number + 65536; or cell number + 131072 */
633
634 /* check for various disallowed combinations */
635 if (i1 == 1 && ismenu == VTYPE_VMGM)
636 {
637 // VMGM VMGM NOPGC NOCH
638 // VMGM VMGM NOPGC CHXX
639 // VMGM VMGM MPGC NOCH
640 // VMGM VMGM MPGC CHXX
641 // VMGM VMGM MEPGC NOCH
642 // VMGM VMGM MEPGC CHXX
643 // VMGM VMGM TPGC NOCH
644 // VMGM VMGM TPGC CHXX
645 i1 = 0; /* no need to explicitly specify VMGM */
646 } /*if*/
647 if
648 (
649 (
650 i2 > 0 && i2 < 128 /* jump to non-entry menu */
651 ||
652 i2 == 0 && i1 == 1 /* jump to VMGM */
653 )
654 &&
655 ismenu == VTYPE_VTS
656 )
657 {
658 // VTS NONE MPGC NOCH
659 // VTS VMGM MPGC NOCH
660 // VTS TS MPGC NOCH
661 // VTS NONE MPGC CHXX
662 // VTS VMGM MPGC CHXX
663 // VTS TS MPGC CHXX
664 // VTS NONE MEPGC NOCH
665 // VTS VMGM MEPGC NOCH
666 // VTS TS MEPGC NOCH
667 // VTS NONE MEPGC CHXX
668 // VTS VMGM MEPGC CHXX
669 // VTS TS MEPGC CHXX
670 // VTS VMGM NOPGC NOCH
671 // VTS VMGM NOPGC CHXX
672
673 fprintf(stderrstderr, "ERR: Cannot jump to a menu from a title, use 'call' instead\n");
674 return 0;
675 } /*if*/
676 if
677 (
678 i2 > 0
679 &&
680 i2 < 128 /* jump to non-entry menu */
681 &&
682 cs->i3 /* chapter/cell/program specified */
683 &&
684 ismenu != VTYPE_VTS
685 )
686 {
687 // VMGM NONE MPGC CHXX
688 // VMGM TS MPGC CHXX
689 // VMGM NONE MEPGC CHXX
690 // VMGM TS MEPGC CHXX
691 // VTSM NONE MPGC CHXX
692 // VTSM VMGM MPGC CHXX
693 // VTSM TS MPGC CHXX
694 // VTSM NONE MEPGC CHXX
695 // VTSM VMGM MEPGC CHXX
696 // VTSM TS MEPGC CHXX
697 fprintf(stderrstderr, "ERR: Cannot specify chapter when jumping to another menu\n");
698 return 0;
699 } /*if*/
700 if (i1 /*VMGM/titleset*/ && !i2 /*no PGC*/)
701 {
702 // VTSM VMGM NOPGC CHXX
703 // VTS TS NOPGC CHXX
704 // VTSM TS NOPGC CHXX
705 // VMGM TS NOPGC CHXX
706 // VTS TS NOPGC NOCH
707 // VTSM TS NOPGC NOCH
708 // VMGM TS NOPGC NOCH
709 fprintf(stderrstderr, "ERR: Cannot omit menu/title if specifying vmgm/titleset\n");
710 return 0;
711 } /*if*/
712 if
713 (
714 !i1 /*same VMGM/titleset*/
715 &&
716 !i2 /*same PGC*/
717 &&
718 !cs->i3 /*no chapter/cell/program*/
719 )
720 {
721 // VTS NONE NOPGC NOCH
722 // VTSM NONE NOPGC NOCH
723 // VMGM NONE NOPGC NOCH
724 fprintf(stderrstderr, "ERR: Nop jump statement\n");
725 return 0;
726 } /*if*/
727 if
728 (
729 i2 == 121 /*jump to FPC*/
730 &&
731 (
732 i1 >= 2 /*titleset*/
733 ||
734 (i1 == 0 /*current VMGM/titleset*/ && ismenu != VTYPE_VMGM)
735 )
736 )
737 {
738 fprintf(stderrstderr, "ERR: VMGM must be specified with FPC\n");
739 return 0;
740 } /*if*/
741
742 // *** ACTUAL COMPILING
743 if
744 (
745 i1 >= 2 /*titleset*/
746 &&
747 i2 >= 120
748 &&
749 i2 < 128 /*entry PGC*/
750 )
751 {
752 // VTSM TS MEPGC NOCH
753 // VMGM TS MEPGC NOCH
754 if (i2 == 120) /* "default" entry means "root" */
755 i2 = 123;
756 write8(buf, 0x30, 0x06, 0x00, 0x01, i1 - 1, 0x80 + (i2 - 120), 0x00, 0x00); buf += 8; // JumpSS VTSM vts 1 menu
757 }
758 else if
759 (
760 i1 >= 2 /*jump to titleset*/
761 ||
762 i1 == 1 /*jump to VMGM*/ && i2 >= 128 /*title*/
763 ||
764 ismenu == VTYPE_VMGM && i2 >= 128 /*title*/ && cs->i3 /*chapter/program/cell*/
765 )
766 {
767 // VMGM TS TPGC CHXX
768 // VTSM TS MPGC NOCH
769 // VMGM TS MPGC NOCH
770 // VTS TS TPGC NOCH
771 // VTSM TS TPGC NOCH
772 // VMGM TS TPGC NOCH
773 // VTS TS TPGC CHXX
774 // VTSM TS TPGC CHXX
775 // VTS VMGM TPGC NOCH
776 // VTSM VMGM TPGC NOCH
777 // VTS VMGM TPGC CHXX
778 // VTSM VMGM TPGC CHXX
779 // VMGM NONE TPGC CHXX
780 if (jumppad)
781 {
782 if (!i1)
783 i1 = 1; /* make VMGM explicit */
784 write8(buf, 0x71, 0x00, 0x00, 0x0F, i2, i1, 0x00, 0x00);
785 /* g15 = i2 << 8 | i1 */
786 buf += 8;
787 write8(buf, 0x71, 0x00, 0x00, 0x0E, 0x00, cs->i3, 0x00, 0x00);
788 /* g14 = cs->i3 */
789 buf += 8;
790 write8(buf, 0x30, ismenu != VTYPE_VTS ? 0x06 : 0x08, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00);
791 /* JumpSS/CallSS VMGM menu entry title */
792 buf += 8;
793 }
794 else
795 {
796 /* JumpTT allows directly jumping from VMGM to any titleset, but that
797 requires global title numbers, not titleset-local title numbers,
798 and I only work these out later when putting together the final VMGM,
799 which is why I can't handle it here without the jumppad */
800 fprintf(stderrstderr, "ERR: That form of jumping is not allowed\n");
801 return 0;
802 } /*if*/
803 }
804 else if (i1 == 1 /*jump to VMGM*/ || i2 == 121 /*jump to FPC*/)
805 {
806 // VTSM VMGM NOPGC NOCH
807 // VTSM VMGM MPGC NOCH
808 // VTSM VMGM MEPGC NOCH
809 // cannot error check jumps to the vmgm menu
810 if (!i2 || i2 == 120)
811 i2 = 122; /* must be title menu */
812 if (i2 < 120)
813 write8(buf, 0x30, 0x06, 0x00, i2, 0x00, 0xC0, 0x00, 0x00); // JumpSS VMGM pgcn
814 else
815 write8(buf, 0x30, 0x06, 0x00, 0x00, 0x00, i2 == 121 ? 0 : (0x40 + i2 - 120), 0x00, 0x00); // JumpSS FP or JumpSS VMGM menu
816 buf += 8;
817 }
818 else if (!i1 && !i2 && cs->i3)
819 {
820 int numc;
821 const char *des;
822
823 // VTS NONE NOPGC CHXX
824 // VTSM NONE NOPGC CHXX
825 // VMGM NONE NOPGC CHXX
826 if (curpgc == 0)
827 {
828 fprintf(stderrstderr, "ERR: Cannot jump to a chapter from a FPC\n");
829 return 0;
830 } /*if*/
831 if (cs->i3 < 65536 && ismenu != VTYPE_VTS)
832 {
833 fprintf(stderrstderr, "ERR: Menus do not have chapters\n");
834 return 0;
835 } /*if*/
836 switch (cs->i3 >> 16)
837 {
838 case 0:
839 numc = curpgc->numchapters;
840 des = "chapter";
841 break;
842 case 1:
843 numc = curpgc->numprograms;
844 des = "program";
845 break;
846 case 2:
847 numc = curpgc->numcells;
848 des = "cell";
849 break;
850 default:
851 numc = 0;
852 des = "<err>";
853 break;
854 } /*switch*/
855 if ((cs->i3 & 65535) > numc)
856 {
857 fprintf(stderrstderr, "ERR: Cannot jump to %s %d, only %d exist\n", des, cs->i3 & 65535, numc);
858 return 0;
859 } /*if*/
860 write8(buf, 0x20, 0x05 + (cs->i3 >> 16), 0x00, 0x00, 0x00, 0x00, 0x00, cs->i3); // LinkPTTN pttn, LinkPGCN pgn, or LinkCN cn
861 buf += 8;
862 }
863 else if (i2 < 128) /* menu */
864 {
865 // VTSM NONE MPGC NOCH
866 // VMGM NONE MPGC NOCH
867 // VTSM NONE MEPGC NOCH
868 // VMGM NONE MEPGC NOCH
869 if (!curgroup)
870 {
871 fprintf(stderrstderr,"ERR: Cannot jump to menu; none exist\n");
872 return 0;
873 }
874 else if (i2 >= 120 && i2 < 128) /* menu entry */
875 {
876 int i;
877 for (i = 0; i < curgroup->numpgcs; i++)
878 if (curgroup->pgcs[i]->entries & (1 << (i2 - 120)))
879 {
880 i2 = i + 1;
881 break;
882 } /*if; for*/
883 if (i2 >= 120)
884 {
885 fprintf(stderrstderr, "ERR: Cannot find PGC with entry %s\n", entries[i2 - 120]);
886 return 0;
887 } /*if*/
888 }
889 else /* non-entry menu */
890 {
891 if (i2 > curgroup->numpgcs)
892 {
893 fprintf(stderrstderr, "ERR: Cannot jump to menu PGC #%d, only %d exist\n", i2, curgroup->numpgcs);
894 return 0;
895 } /*if*/
896 } /*if*/
897 if (ismenu == VTYPE_VMGM)
898 {
899 // In case we are jumping from a FP to VMGM, we need to use a JumpSS
900 // instruction
901 write8(buf, 0x30, 0x06, 0x00, i2 & 127, 0x00, 0xc0, 0x00, 0x00); // JumpSS VMGM pgcn
902 }
903 else
904 write8(buf, 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, i2 & 127); // LinkPGCN pgcn
905 buf += 8;
906 }
907 else
908 {
909 // VMGM NONE TPGC NOCH
910 // VTS NONE TPGC NOCH
911 // VTSM NONE TPGC NOCH
912 // VTS NONE TPGC CHXX
913 // VTSM NONE TPGC CHXX
914 if (ismenu < VTYPE_VMGM) /* VTS or VTSM */
915 {
916 if (i2 - 128 > ws->titles->numpgcs) /* title nr */
917 {
918 fprintf
919 (
920 stderrstderr,
921 "ERR: Cannot jump to title #%d, only %d exist\n",
922 i2 - 128,
923 ws->titles->numpgcs
924 );
925 return 0;
926 } /*if*/
927 if (cs->i3 && cs->i3 > ws->titles->pgcs[i2 - 128 - 1]->numchapters)
928 {
929 fprintf
930 (
931 stderrstderr,
932 "ERR: Cannot jump to chapter %d of title %d, only %d exist\n",
933 cs->i3,
934 i2 - 128,
935 ws->titles->pgcs[i2 - 128 - 1]->numchapters
936 );
937 return 0;
938 } /*if*/
939 } /*if*/
940 write8
941 (
942 buf,
943 0x30,
944 ismenu == VTYPE_VMGM ?
945 0x02 /*JumpTT*/
946 : cs->i3 ?
947 0x05 /*JumpVTS_PTT*/
948 :
949 0x03 /*JumpVTS_TT*/,
950 0x00,
951 cs->i3, /* chapter if present */
952 0x00,
953 i2 - 128, /* title nr */
954 0x00,
955 0x00
956 );
957 buf += 8;
958 } /*if*/
959 }
960 break;
961
962 case VM_CALL:
963 {
964 /* cs->i1 if nonzero is 1 for VMGM, or titleset nr + 1 */
965 int i2 = cs->i2; /* menu number or menu entry ID + 120 or title number + 128 */
966 /* cs->i3 is chapter number if nonzero and less than 65536;
967 or program number + 65536; or cell number + 131072 */
968 int i4 = cs->i4; /* resume cell if specified, else zero */
969
970 // CALL's from <post> MUST have a resume cell
971 if (!i4)
972 i4 = 1;
973 if (ismenu != VTYPE_VTS)
974 {
975 // VTSM NONE NOPGC NOCH
976 // VMGM NONE NOPGC NOCH
977 // VTSM VMGM NOPGC NOCH
978 // VMGM VMGM NOPGC NOCH
979 // VTSM TS NOPGC NOCH
980 // VMGM TS NOPGC NOCH
981 // VTSM NONE NOPGC CHXX
982 // VMGM NONE NOPGC CHXX
983 // VTSM VMGM NOPGC CHXX
984 // VMGM VMGM NOPGC CHXX
985 // VTSM TS NOPGC CHXX
986 // VMGM TS NOPGC CHXX
987 // VTSM NONE MPGC NOCH
988 // VMGM NONE MPGC NOCH
989 // VTSM VMGM MPGC NOCH
990 // VMGM VMGM MPGC NOCH
991 // VTSM TS MPGC NOCH
992 // VMGM TS MPGC NOCH
993 // VTSM NONE MPGC CHXX
994 // VMGM NONE MPGC CHXX
995 // VTSM VMGM MPGC CHXX
996 // VMGM VMGM MPGC CHXX
997 // VTSM TS MPGC CHXX
998 // VMGM TS MPGC CHXX
999 // VTSM NONE MEPGC NOCH
1000 // VMGM NONE MEPGC NOCH
1001 // VTSM VMGM MEPGC NOCH
1002 // VMGM VMGM MEPGC NOCH
1003 // VTSM TS MEPGC NOCH
1004 // VMGM TS MEPGC NOCH
1005 // VTSM NONE MEPGC CHXX
1006 // VMGM NONE MEPGC CHXX
1007 // VTSM VMGM MEPGC CHXX
1008 // VMGM VMGM MEPGC CHXX
1009 // VTSM TS MEPGC CHXX
1010 // VMGM TS MEPGC CHXX
1011 // VTSM NONE TPGC NOCH
1012 // VMGM NONE TPGC NOCH
1013 // VTSM VMGM TPGC NOCH
1014 // VMGM VMGM TPGC NOCH
1015 // VTSM TS TPGC NOCH
1016 // VMGM TS TPGC NOCH
1017 // VTSM NONE TPGC CHXX
1018 // VMGM NONE TPGC CHXX
1019 // VTSM VMGM TPGC CHXX
1020 // VMGM VMGM TPGC CHXX
1021 // VTSM TS TPGC CHXX
1022 // VMGM TS TPGC CHXX
1023 fprintf(stderrstderr, "ERR: Cannot 'call' a menu from another menu, use 'jump' instead\n");
1024 return 0;
1025 } /*if*/
1026 if (i2 == 0 || i2 >= 128) /* title nr or no menu/title */
1027 {
1028 // VTS NONE NOPGC NOCH
1029 // VTS VMGM NOPGC NOCH
1030 // VTS TS NOPGC NOCH
1031 // VTS NONE NOPGC CHXX
1032 // VTS VMGM NOPGC CHXX
1033 // VTS TS NOPGC CHXX
1034 // VTS NONE TPGC NOCH
1035 // VTS VMGM TPGC NOCH
1036 // VTS TS TPGC NOCH
1037 // VTS NONE TPGC CHXX
1038 // VTS VMGM TPGC CHXX
1039 // VTS TS TPGC CHXX
1040
1041 fprintf(stderrstderr, "ERR: Cannot 'call' another title, use 'jump' instead\n");
1042 return 0;
1043 } /*if*/
1044 if (cs->i3 != 0) /* chapter/cell/program */
1045 {
1046 // VTS NONE MPGC CHXX
1047 // VTS VMGM MPGC CHXX
1048 // VTS TS MPGC CHXX
1049 // VTS NONE MEPGC CHXX
1050 // VTS VMGM MEPGC CHXX
1051 // VTS TS MEPGC CHXX
1052 fprintf(stderrstderr, "ERR: Cannot 'call' a chapter within a menu\n");
1053 return 0;
1054 } /*if*/
1055 if
1056 (
1057 i2 == 121 /*FPC*/
1058 &&
1059 (
1060 cs->i1 >= 2 /*titleset*/
1061 ||
1062 cs->i1 == 0 /*no VMGM/titleset*/ && ismenu != VTYPE_VMGM
1063 )
1064 )
1065 {
1066 fprintf(stderrstderr, "ERR: VMGM must be specified with FPC\n");
1067 return 0;
1068 } /*if*/
1069
1070 if (cs->i1 >= 2) /*titleset*/
1071 {
1072 // VTS TS MPGC NOCH
1073 // VTS TS MEPGC NOCH
1074 if (jumppad)
1075 {
1076 write8(buf, 0x71, 0x00, 0x00, 0x0F, i2, cs->i1, 0x00, 0x00); buf += 8;
1077 write8(buf, 0x71, 0x00, 0x00, 0x0E, 0x00, cs->i3, 0x00, 0x00); buf += 8;
1078 write8(buf, 0x30, 0x08, 0x00, 0x00, i4, 0x42, 0x00, 0x00); buf += 8;
1079 }
1080 else
1081 {
1082 fprintf(stderrstderr, "ERR: Cannot call to a menu in another titleset\n");
1083 return 0;
1084 } /*if*/
1085 }
1086 else if (cs->i1 == 0 && i2 < 120) /* non-entry menu in current VMGM/titleset */
1087 {
1088 // VTS NONE MPGC NOCH
1089 if (jumppad)
1090 {
1091 write8(buf, 0x71, 0x00, 0x00, 0x0F, i2, 0x00, 0x00, 0x00); buf += 8;
1092 write8(buf, 0x30, 0x08, 0x00, 0x00, i4, 0x87, 0x00, 0x00); buf += 8;
1093 }
1094 else
1095 {
1096 fprintf(stderrstderr, "ERR: Cannot call to a specific menu PGC, only an entry\n");
1097 return 0;
1098 } /*if*/
1099 }
1100 else if (cs->i1 == 1) /* jump to VMGM */
1101 {
1102 // VTS VMGM MPGC NOCH
1103 // VTS VMGM MEPGC NOCH
1104 // we cannot provide error checking when jumping to a VMGM
1105 if (i2 == 120) /* "default" entry means "title" */
1106 i2 = 122;
1107 if (i2 < 120)
1108 write8(buf, 0x30, 0x08, 0x00, i2, i4, 0xC0, 0x00, 0x00);
1109 else
1110 write8(buf, 0x30, 0x08, 0x00, 0x00, i4, i2 == 121 ? 0 : (0x40 + i2 - 120), 0x00, 0x00);
1111 buf += 8;
1112 }
1113 else
1114 {
1115 int i, j;
1116 // VTS NONE MEPGC NOCH
1117 if (i2 == 120)
1118 i2 = 127; /* "default" means chapter menu? */
1119 for (j = 0; j < ws->menus->numgroups; j++)
1120 {
1121 const struct pgcgroup * const pg = ws->menus->groups[j].pg;
1122 for (i = 0; i < pg->numpgcs; i++)
1123 if (pg->pgcs[i]->entries & (1 << (i2 - 120)))
1124 goto foundpgc;
1125 } /*for*/
1126 fprintf(stderrstderr, "ERR: Cannot find PGC with entry %s\n", entries[i2 - 120]);
1127 return 0;
1128 foundpgc:
1129 write8(buf, 0x30, 0x08, 0x00, 0x00, i4 /*rsm cell*/, 0x80 + i2 - 120 /*VTSM menu*/, 0x00, 0x00);
1130 /* CallSS VTSM menu, rsm_cell */
1131 buf += 8;
1132 } /*if*/
1133 }
1134 break;
1135
1136 case VM_NOP:
1137 /* nothing to do */
1138 break;
1139
1140 default:
1141 fprintf(stderrstderr,"ERR: Unsupported VM opcode %d\n",cs->op);
1142 return 0;
1143 } /*switch*/
1144 cs = cs->next;
1145 } /*while*/
1146 if (lastif)
1147 {
1148 /* need target for last branch */
1149 write8(buf, 0, 0, 0, 0, 0, 0, 0, 0); /* NOP */
1150 buf += 8;
1151 } /*if*/
1152 return buf;
1153 } /*compilecs*/
1154
1155static unsigned int extractif(const unsigned char *b)
1156 /* extracts the common bits from a conditional instruction into a format
1157 that can be reapplied to another instruction. */
1158 {
1159 switch (b[0] >> 4)
1160 {
1161 case 0:
1162 case 1:
1163 case 2:
1164 return
1165 (b[1] >> 4) << 24 /* the comparison op and the command to be performed */
1166 |
1167 b[3] << 16 /* operand 1 for the comparison */
1168 |
1169 b[4] << 8 /* operand 2, high byte */
1170 |
1171 b[5]; /* operand 2, low byte */
1172
1173 default:
1174 fprintf(stderrstderr, "ERR: Unhandled extractif scenario (%x), file bug\n", b[0]);
1175 exit(1);
1176 } /*switch*/
1177 } /*extractif*/
1178
1179static unsigned int negateif(unsigned int ifs)
1180 /* negates the comparison op part of a value returned from extractif. */
1181 {
1182 return
1183 ifs & 0x8ffffff /* remove comparison op */
1184 |
1185 negatecompare((ifs >> 24) & 7) << 24; /* replace with opposite comparison */
1186 } /*negateif*/
1187
1188static void applyif(unsigned char *b,unsigned int ifs)
1189 /* inserts the bits extracted by extractif into a new instruction. */
1190 {
1191 switch (b[0] >> 4)
1192 {
1193 case 0:
1194 case 1:
1195 case 2:
1196 b[5] = ifs;
1197 b[4] = ifs >> 8;
1198 b[3] = ifs >> 16;
1199 b[1] |= (ifs >> 24) << 4;
1200 break;
1201
1202 case 3:
1203 case 4:
1204 case 5:
1205 b[7] = ifs; /* assume ifs >> 8 & 255 is zero! */
1206 b[6] = ifs >> 16;
1207 b[1] |= (ifs >> 24) << 4;
1208 break;
1209
1210 case 6:
1211 case 7:
1212 b[7] = ifs;
1213 b[6] = ifs >> 8;
1214 b[2] = ifs >> 16;
1215 b[1] = (ifs >> 24) << 4;
1216 break;
1217
1218 default:
1219 fprintf(stderrstderr,"ERR: Unhandled applyif scenario (%x), file bug\n", b[0]);
1220 exit(1);
1221 } /*switch*/
1222 } /*applyif*/
1223
1224static bool_Bool ifcombinable(unsigned char b0 /* actually caller always passes 0 */, unsigned char b1, unsigned char b8)
1225 /* can the instruction whose first two bytes are b0 and b1 have its condition
1226 combined with the one whose first byte is b8. */
1227 {
1228 int iftype = -1;
1229 switch (b0 >> 4)
1230 {
1231 case 0:
1232 case 1:
1233 case 2:
1234 case 6:
1235 case 7:
1236 iftype = b1 >> 7; /* 1 if 2nd operand is immediate, 0 if it's a register */
1237 break;
1238 case 3:
1239 case 4:
1240 case 5:
1241 iftype = 0; /* 2nd operand always register */
1242 break;
1243 default:
1244 return false0;
1245 } /*switch*/
1246 switch (b8 >> 4)
1247 {
1248 case 0:
1249 case 1:
1250 case 2:
1251 case 6:
1252 case 7:
1253 return true1; /* can take both immediate and register 2nd operands */
1254 case 3:
1255 case 4:
1256 case 5:
1257 return iftype == 0; /* can only take register 2nd operand */
1258 default:
1259 return false0;
1260 } /*switch*/
1261 } /*ifcombinable*/
1262
1263static int countreferences(const unsigned char *buf, const unsigned char *end, int linenum)
1264 /* how many branches are there with destination linenum. Actually caller only cares
1265 whether result is zero or not. */
1266 {
1267 const unsigned char *b;
1268 int numref = 0;
1269 for (b = buf; b < end; b += 8)
1270 if (b[0] == 0 && (b[1] & 15) == 1 && b[7] == linenum)
1271 numref++;
1272 return numref;
1273 } /*countreferences*/
1274
1275static void deleteinstruction
1276 (
1277 const unsigned char *obuf, /* start of instruction buffer */
1278 unsigned char *buf, /* start of area where branch targets need adjusting */
1279 unsigned char **end, /* pointer to next free part of buffer, to be updated */
1280 unsigned char *b /* instruction to be deleted from buffer */
1281 )
1282 /* deletes an instruction from the buffer, and moves up the following ones,
1283 adjusting branches over the deleted instruction as appropriate. */
1284 {
1285 unsigned char *b2;
1286 const int linenum = (b - obuf) / 8 + 1;
1287 for (b2 = buf; b2 < *end; b2 += 8) /* adjust branches to following instructions */
1288 if (b2[0] == 0 && (b2[1] & 15) == 1 && b2[7] > linenum)
1289 b2[7]--;
1290 memmove(b, b + 8, *end - (b + 8));
1291 *end -= 8;
1292 memset(*end, 0, 8); // clean up tracks (so pgc structure is not polluted)
1293 } /*deleteinstruction*/
1294
1295void vm_optimize(const unsigned char *obuf, unsigned char *buf, unsigned char **end)
1296 /* does various peephole optimizations on the part of obuf from buf to *end.
1297 *end will be updated if unnecessary instructions are removed. */
1298 {
1299 unsigned char *b;
1300 again:
1301 for (b = buf; b < *end; b += 8)
1302 {
1303 const int curline = (b - obuf) / 8 + 1;
1304 // if
1305 // 1. this is a jump over one statement
1306 // 2. we can combine the statement with the if
1307 // 3. there are no references to the statement
1308 // then
1309 // combine statement with if, negate if, and replace statement with nop
1310 if
1311 (
1312 b[0] == 0
1313 &&
1314 (b[1] & 0x70) != 0 /* conditional */
1315 &&
1316 (b[1] & 15) == 1 /* cmd = goto */
1317 &&
1318 b[7] == curline + 2 // step 1
1319 &&
1320 (b[9] & 0x70) == 0 /* second instr not conditional */
1321 &&
1322 ifcombinable(b[0], b[1], b[8]) // step 2
1323 &&
1324 countreferences(buf, *end, curline + 1) == 0 // step 3
1325 )
1326 {
1327 const unsigned int ifs = negateif(extractif(b));
1328 memcpy(b, b + 8, 8); // move statement
1329 memset(b + 8, 0, 8); // replace with nop
1330 applyif(b, ifs);
1331 goto again;
1332 } /*if*/
1333 // 1. this is a NOP instruction
1334 // 2. there are more instructions after this OR there are no references here
1335 // then
1336 // delete instruction, fix goto labels
1337 if
1338 (
1339 b[0] == 0
1340 &&
1341 b[1] == 0
1342 &&
1343 b[2] == 0
1344 &&
1345 b[3] == 0
1346 &&
1347 b[4] == 0
1348 &&
1349 b[5] == 0
1350 &&
1351 b[6] == 0
1352 &&
1353 b[7] == 0 /* it's a NOP */
1354 &&
1355 (
1356 b + 8 != *end /* more instructions after this */
1357 ||
1358 countreferences(buf, *end, curline) == 0 /* no references here */
1359 )
1360 )
1361 {
1362 deleteinstruction(obuf, buf, end, b);
1363 goto again;
1364 } /*if*/
1365 // if
1366 // 1. the prev instruction is an UNCONDITIONAL jump/goto
1367 // 2. there are no references to the statement
1368 // then
1369 // delete instruction, fix goto labels
1370 if
1371 (
1372 b > buf
1373 &&
1374 (b[-8] >> 4) <= 3
1375 &&
1376 (b[-7] & 0x70) == 0
1377 &&
1378 (b[-7] & 15) != 0 /* previous was unconditional transfer */
1379 &&
1380 countreferences(buf, *end, curline) == 0 /* no references here */
1381 /* fixme: should also remove in the case where jump was to this instruction */
1382 )
1383 {
1384 /* remove dead code */
1385 deleteinstruction(obuf, buf, end, b);
1386 goto again;
1387 } /*if*/
1388 // if
1389 // 1. this instruction sets subtitle/angle/audio
1390 // 2. the next instruction sets subtitle/angle/audio
1391 // 3. they both set them the same way (i.e. immediate/indirect)
1392 // 4. there are no references to the second instruction
1393 // then
1394 // combine
1395 if
1396 (
1397 b + 8 != *end
1398 &&
1399 (b[0] & 0xEF) == 0x41
1400 &&
1401 b[1] == 0 // step 1
1402 &&
1403 b[0] == b[8]
1404 &&
1405 b[1] == b[9] // step 2 & 3
1406 &&
1407 countreferences(buf, *end, curline + 1) == 0
1408 )
1409 {
1410 if (b[8 + 3])
1411 b[3] = b[8 + 3];
1412 if (b[8 + 4])
1413 b[4] = b[8 + 4];
1414 if (b[8 + 5])
1415 b[5] = b[8 + 5];
1416 deleteinstruction(obuf, buf, end, b + 8);
1417 goto again;
1418 } /*if*/
1419 // if
1420 // 1. this instruction sets the button directly
1421 // 2. the next instruction is a link command (not NOP, not PGCN)
1422 // 3. there are no references to the second instruction
1423 // then
1424 // combine
1425 if
1426 (
1427 b + 8 != *end
1428 &&
1429 b[0] == 0x56
1430 &&
1431 b[1] == 0x00
1432 &&
1433 b[8] == 0x20
1434 &&
1435 (
1436 (b[8 + 1] & 0xf) == 5
1437 ||
1438 (b[8 + 1] & 0xf) == 6
1439 ||
1440 (b[8 + 1] & 0xf) == 7
1441 ||
1442 (b[8 + 1] & 0xf) == 1
1443 &&
1444 (b[8 + 7] & 0x1f) != 0
1445 )
1446 &&
1447 countreferences(buf, *end, curline + 1) == 0
1448 )
1449 {
1450 if (b[8 + 6] == 0)
1451 b[8 + 6] = b[4];
1452 deleteinstruction(obuf, buf, end, b);
1453 goto again;
1454 } /*if*/
1455 // if
1456 // 1. this instruction sets a GPRM/SPRM register
1457 // 2. the next instruction is a link command (not NOP)
1458 // 3. there are no references to the second instruction
1459 // then
1460 // combine
1461 if
1462 (
1463 b + 8 != *end
1464 &&
1465 ((b[0] & 0xE0) == 0x40 || (b[0] & 0xE0) == 0x60)
1466 &&
1467 (b[1] & 0x7f) == 0x00
1468 &&
1469 b[8] == 0x20
1470 &&
1471 (
1472 (b[8 + 1] & 0x7f) == 4
1473 ||
1474 (b[8 + 1] & 0x7f) == 5
1475 ||
1476 (b[8 + 1] & 0x7f) == 6
1477 ||
1478 (b[8 + 1] & 0x7f) == 7
1479 ||
1480 (b[8 + 1] & 0x7f) == 1
1481 &&
1482 (b[8 + 7] & 0x1f) != 0
1483 )
1484 &&
1485 countreferences(buf, *end, curline + 1) == 0
1486 )
1487 {
1488 b[1] = b[8 + 1];
1489 b[6] = b[8 + 6];
1490 b[7] = b[8 + 7];
1491 deleteinstruction(obuf, buf, end, b + 8);
1492 goto again;
1493 } /*if*/
1494 } /*for*/
1495 } /*vm_optimize*/
1496
1497unsigned char *vm_compile
1498 (
1499 const unsigned char *obuf, /* start of buffer for computing instruction numbers for branches */
1500 unsigned char *buf, /* where to insert new compiled code */
1501 const struct workset *ws,
1502 const struct pgcgroup *curgroup,
1503 const struct pgc *curpgc,
1504 const struct vm_statement *cs,
1505 vtypes ismenu
1506 )
1507 /* compiles the parse tree cs into actual VM instructions with optimization,
1508 and fixes up all the gotos. */
1509 {
1510 unsigned char *end;
1511 int i, j;
1512 numlabels = 0;
1513 numgotos = 0;
1514 end = compilecs(obuf, buf, ws, curgroup, curpgc, cs, ismenu);
1515 if (!end) /* error */
1516 return end;
1517 // fix goto references
1518 for (i = 0; i < numgotos; i++)
1519 {
1520 for (j = 0; j < numlabels; j++)
1521 if (!strcasecmp(gotos[i].lname,labels[j].lname))
1522 break;
1523 if (j == numlabels)
1524 {
1525 fprintf(stderrstderr, "ERR: Cannot find label %s\n", gotos[i].lname);
1526 return 0;
1527 } /*if*/
1528 gotos[i].code[7] = (labels[j].code - obuf) / 8 + 1;
1529 } /*for*/
1530 vm_optimize(obuf, buf, &end);
1531 return end;
1532 } /*vm_compile*/
1533
1534void dvdvmerror(const char *s)
1535 /* reports a parse error. */
1536 {
1537 extern char *dvdvmtext;
1538 fprintf(stderrstderr, "ERR: Parse error '%s' on token '%s'\n", s, dvdvmtext);
1539 exit(1);
1540 } /*dvdvmerror*/
1541
1542struct vm_statement *vm_parse(const char *b)
1543 /* parses a VM source string and returns the constructed parse tree. */
1544 {
1545 if (b)
1546 {
1547 const char * const cmd = strdup(b)(__extension__ (__builtin_constant_p (b) && ((size_t)
(const void *)((b) + 1) - (size_t)(const void *)(b) == 1) ? (
((const char *) (b))[0] == '\0' ? (char *) calloc ((size_t) 1
, (size_t) 1) : ({ size_t __len = strlen (b) + 1; char *__retval
= (char *) malloc (__len); if (__retval != ((void*)0)) __retval
= (char *) memcpy (__retval, b, __len); __retval; })) : __strdup
(b)))
;
1548 dvdvm_buffer_state buf = dvdvm_scan_string(cmd);
1549 dvd_vm_parsed_cmd = 0;
1550 if (dvdvmparse())
1551 {
1552 fprintf(stderrstderr, "ERR: Parser failed on code '%s'.\n", b);
1553 exit(1);
1554 } /*if*/
1555 if (!dvd_vm_parsed_cmd)
1556 {
1557 fprintf(stderrstderr, "ERR: Nothing parsed from '%s'\n", b);
1558 exit(1);
1559 } /*if*/
1560 dvdvm_delete_buffer(buf);
1561 free((void *)cmd);
1562 return dvd_vm_parsed_cmd;
1563 }
1564 else
1565 {
1566 // pieces of code in dvdauthor rely on a non-null vm_statement
1567 // meaning something significant. so if we parse an empty string,
1568 // we should return SOMETHING not null.
1569 // also, since the xml parser strips whitespace, we treat a
1570 // NULL string as an empty string
1571 struct vm_statement * const v = statement_new();
1572 v->op = VM_NOP;
1573 return v;
1574 } /*if*/
1575 } /*vm_parse*/