1 /*
2 * This file is part of libbluray
3 * Copyright (C) 2010-2017 Petri Hintukainen <phintuka@users.sourceforge.net>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see
17 * <http://www.gnu.org/licenses/>.
18 */
19
20 #if HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "hdmv_vm.h"
25
26 #include "mobj_data.h"
27 #include "hdmv_insn.h"
28 #include "mobj_parse.h"
29 #include "mobj_print.h"
30 #include "../register.h"
31
32 #include "util/macro.h"
33 #include "util/logging.h"
34 #include "util/mutex.h"
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40
41
42 typedef struct {
43 time_t time;
44 uint32_t mobj_id;
45 } NV_TIMER;
46
47 struct hdmv_vm_s {
48
49 BD_MUTEX mutex;
50
51 /* state */
52 uint32_t pc; /* program counter */
53 BD_REGISTERS *regs; /* player registers */
54 const MOBJ_OBJECT *object; /* currently running object code */
55
56 HDMV_EVENT event[5]; /* pending events to return */
57
58 NV_TIMER nv_timer; /* navigation timer */
59 uint64_t rand; /* RAND state */
60
61 /* movie objects */
62 MOBJ_OBJECTS *movie_objects; /* disc movie objects */
63 MOBJ_OBJECT *ig_object; /* current object from IG stream */
64
65 /* object currently playing playlist */
66 const MOBJ_OBJECT *playing_object;
67 uint32_t playing_pc;
68
69 /* suspended object */
70 const MOBJ_OBJECT *suspended_object;
71 uint32_t suspended_pc;
72
73 /* Available titles. Used to validate CALL_TITLE/JUMP_TITLE. */
74 uint8_t have_top_menu;
75 uint8_t have_first_play;
76 uint16_t num_titles;
77 };
78
79 /*
80 * save / restore VM state
81 */
82
_save_state(HDMV_VM * p,uint32_t * s)83 static int _save_state(HDMV_VM *p, uint32_t *s)
84 {
85 memset(s, 0, sizeof(*s) * HDMV_STATE_SIZE);
86
87 if (p->ig_object) {
88 BD_DEBUG(DBG_HDMV | DBG_CRIT, "_save_state() failed: button object running\n");
89 return -1;
90 }
91 if (p->object) {
92 BD_DEBUG(DBG_HDMV | DBG_CRIT, "_save_state() failed: movie object running\n");
93 return -1;
94 }
95 if (p->event[0].event != HDMV_EVENT_NONE) {
96 BD_DEBUG(DBG_HDMV | DBG_CRIT, "_save_state() failed: unprocessed events\n");
97 return -1;
98 }
99
100 if (p->playing_object) {
101 s[0] = (uint32_t)(p->playing_object - p->movie_objects->objects);
102 s[1] = p->playing_pc;
103 } else {
104 s[0] = (uint32_t)-1;
105 }
106
107 if (p->suspended_object) {
108 s[2] = (uint32_t)(p->suspended_object - p->movie_objects->objects);
109 s[3] = p->suspended_pc;
110 } else {
111 s[2] = (uint32_t)-1;
112 }
113
114 /* nv timer ? */
115
116 return 0;
117 }
118
_restore_state(HDMV_VM * p,const uint32_t * s)119 static int _restore_state(HDMV_VM *p, const uint32_t *s)
120 {
121 if (s[0] == (uint32_t)-1) {
122 p->playing_object = NULL;
123 } else if (s[0] >= p->movie_objects->num_objects) {
124 BD_DEBUG(DBG_HDMV | DBG_CRIT, "_restore_state() failed: invalid playing object index\n");
125 return -1;
126 } else {
127 p->playing_object = &p->movie_objects->objects[s[0]];
128 }
129 p->playing_pc = s[1];
130
131 if (s[2] == (uint32_t)-1) {
132 p->suspended_object = NULL;
133 } else if (s[2] >= p->movie_objects->num_objects) {
134 BD_DEBUG(DBG_HDMV | DBG_CRIT, "_restore_state() failed: invalid suspended object index\n");
135 return -1;
136 } else {
137 p->suspended_object = &p->movie_objects->objects[s[2]];
138 }
139 p->suspended_pc = s[3];
140
141 p->object = NULL;
142 p->ig_object = NULL;
143 memset(p->event, 0, sizeof(p->event));
144
145 return 0;
146 }
147
hdmv_vm_save_state(HDMV_VM * p,uint32_t * s)148 int hdmv_vm_save_state(HDMV_VM *p, uint32_t *s)
149 {
150 int result;
151 bd_mutex_lock(&p->mutex);
152 result = _save_state(p, s);
153 bd_mutex_unlock(&p->mutex);
154 return result;
155 }
156
hdmv_vm_restore_state(HDMV_VM * p,const uint32_t * s)157 void hdmv_vm_restore_state(HDMV_VM *p, const uint32_t *s)
158 {
159 bd_mutex_lock(&p->mutex);
160 _restore_state(p, s);
161 bd_mutex_unlock(&p->mutex);
162 }
163
164
165 /*
166 * registers: PSR and GPR access
167 */
168
169 #define PSR_FLAG 0x80000000
170
_is_valid_reg(uint32_t reg)171 static int _is_valid_reg(uint32_t reg)
172 {
173 if (reg & PSR_FLAG) {
174 if (reg & ~0x8000007f) {
175 return 0;
176 }
177 } else {
178 if (reg & ~0x00000fff) {
179 return 0;
180 }
181 }
182 return 1;
183 }
184
_store_reg(HDMV_VM * p,uint32_t reg,uint32_t val)185 static int _store_reg(HDMV_VM *p, uint32_t reg, uint32_t val)
186 {
187 if (!_is_valid_reg(reg)) {
188 BD_DEBUG(DBG_HDMV, "_store_reg(): invalid register 0x%x\n", reg);
189 return -1;
190 }
191
192 if (reg & PSR_FLAG) {
193 BD_DEBUG(DBG_HDMV, "_store_reg(): storing to PSR is not allowed\n");
194 return -1;
195 } else {
196 return bd_gpr_write(p->regs, reg, val);
197 }
198 }
199
_read_reg(HDMV_VM * p,uint32_t reg)200 static uint32_t _read_reg(HDMV_VM *p, uint32_t reg)
201 {
202 if (!_is_valid_reg(reg)) {
203 BD_DEBUG(DBG_HDMV, "_read_reg(): invalid register 0x%x\n", reg);
204 return 0;
205 }
206
207 if (reg & PSR_FLAG) {
208 return bd_psr_read(p->regs, reg & 0x7f);
209 } else {
210 return bd_gpr_read(p->regs, reg);
211 }
212 }
213
_read_setstream_regs(HDMV_VM * p,uint32_t val)214 static uint32_t _read_setstream_regs(HDMV_VM *p, uint32_t val)
215 {
216 uint32_t flags = val & 0xf000f000;
217 uint32_t reg0 = val & 0xfff;
218 uint32_t reg1 = (val >> 16) & 0xfff;
219
220 uint32_t val0 = bd_gpr_read(p->regs, reg0) & 0x0fff;
221 uint32_t val1 = bd_gpr_read(p->regs, reg1) & 0x0fff;
222
223 return flags | val0 | (val1 << 16);
224 }
225
_read_setbuttonpage_reg(HDMV_VM * p,uint32_t val)226 static uint32_t _read_setbuttonpage_reg(HDMV_VM *p, uint32_t val)
227 {
228 uint32_t flags = val & 0xc0000000;
229 uint32_t reg0 = val & 0x00000fff;
230
231 uint32_t val0 = bd_gpr_read(p->regs, reg0) & 0x3fffffff;
232
233 return flags | val0;
234 }
235
_store_result(HDMV_VM * p,MOBJ_CMD * cmd,uint32_t src,uint32_t dst,uint32_t src0,uint32_t dst0)236 static int _store_result(HDMV_VM *p, MOBJ_CMD *cmd, uint32_t src, uint32_t dst, uint32_t src0, uint32_t dst0)
237 {
238 int ret = 0;
239
240 /* store result to destination register(s) */
241 if (dst != dst0) {
242 if (cmd->insn.imm_op1) {
243 BD_DEBUG(DBG_HDMV|DBG_CRIT, "storing to imm !\n");
244 return -1;
245 }
246 ret = _store_reg(p, cmd->dst, dst);
247 }
248
249 if (src != src0) {
250 if (cmd->insn.imm_op1) {
251 BD_DEBUG(DBG_HDMV|DBG_CRIT, "storing to imm !\n");
252 return -1;
253 }
254 ret += _store_reg(p, cmd->src, src);
255 }
256
257 return ret;
258 }
259
_fetch_operand(HDMV_VM * p,int setstream,int setbuttonpage,int imm,uint32_t value)260 static uint32_t _fetch_operand(HDMV_VM *p, int setstream, int setbuttonpage, int imm, uint32_t value)
261 {
262 if (imm) {
263 return value;
264 }
265
266 if (setstream) {
267 return _read_setstream_regs(p, value);
268
269 } else if (setbuttonpage) {
270 return _read_setbuttonpage_reg(p, value);
271
272 } else {
273 return _read_reg(p, value);
274 }
275 }
276
_fetch_operands(HDMV_VM * p,MOBJ_CMD * cmd,uint32_t * dst,uint32_t * src)277 static void _fetch_operands(HDMV_VM *p, MOBJ_CMD *cmd, uint32_t *dst, uint32_t *src)
278 {
279 HDMV_INSN *insn = &cmd->insn;
280
281 int setstream = (insn->grp == INSN_GROUP_SET &&
282 insn->sub_grp == SET_SETSYSTEM &&
283 ( insn->set_opt == INSN_SET_STREAM ||
284 insn->set_opt == INSN_SET_SEC_STREAM));
285 int setbuttonpage = (insn->grp == INSN_GROUP_SET &&
286 insn->sub_grp == SET_SETSYSTEM &&
287 insn->set_opt == INSN_SET_BUTTON_PAGE);
288
289 *dst = *src = 0;
290
291 if (insn->op_cnt > 0) {
292 *dst = _fetch_operand(p, setstream, setbuttonpage, insn->imm_op1, cmd->dst);
293 }
294
295 if (insn->op_cnt > 1) {
296 *src = _fetch_operand(p, setstream, setbuttonpage, insn->imm_op2, cmd->src);
297 }
298 }
299
300 /*
301 * event queue
302 */
303
hdmv_event_str(hdmv_event_e event)304 const char *hdmv_event_str(hdmv_event_e event)
305 {
306 switch (event) {
307 #define EVENT_ENTRY(e) case e : return #e
308 EVENT_ENTRY(HDMV_EVENT_NONE);
309 EVENT_ENTRY(HDMV_EVENT_END);
310 EVENT_ENTRY(HDMV_EVENT_IG_END);
311 EVENT_ENTRY(HDMV_EVENT_TITLE);
312 EVENT_ENTRY(HDMV_EVENT_PLAY_PL);
313 EVENT_ENTRY(HDMV_EVENT_PLAY_PI);
314 EVENT_ENTRY(HDMV_EVENT_PLAY_PM);
315 EVENT_ENTRY(HDMV_EVENT_PLAY_STOP);
316 EVENT_ENTRY(HDMV_EVENT_STILL);
317 EVENT_ENTRY(HDMV_EVENT_SET_BUTTON_PAGE);
318 EVENT_ENTRY(HDMV_EVENT_ENABLE_BUTTON);
319 EVENT_ENTRY(HDMV_EVENT_DISABLE_BUTTON);
320 EVENT_ENTRY(HDMV_EVENT_POPUP_OFF);
321 #undef EVENT_ENTRY
322 }
323 return "???";
324 }
325
_get_event(HDMV_VM * p,HDMV_EVENT * ev)326 static int _get_event(HDMV_VM *p, HDMV_EVENT *ev)
327 {
328 if (p->event[0].event != HDMV_EVENT_NONE) {
329 *ev = p->event[0];
330 memmove(p->event, p->event + 1, sizeof(p->event) - sizeof(p->event[0]));
331 return 0;
332 }
333
334 ev->event = HDMV_EVENT_NONE;
335
336 return -1;
337 }
338
_queue_event(HDMV_VM * p,hdmv_event_e event,uint32_t param)339 static int _queue_event(HDMV_VM *p, hdmv_event_e event, uint32_t param)
340 {
341 unsigned i;
342 for (i = 0; i < sizeof(p->event) / sizeof(p->event[0]) - 1; i++) {
343 if (p->event[i].event == HDMV_EVENT_NONE) {
344 p->event[i].event = event;
345 p->event[i].param = param;
346 return 0;
347 }
348 }
349
350 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_queue_event(%d:%s, %d): queue overflow !\n", event, hdmv_event_str(event), param);
351 return -1;
352 }
353
354 /*
355 * vm init
356 */
357
hdmv_vm_init(struct bd_disc * disc,BD_REGISTERS * regs,unsigned num_titles,unsigned first_play_available,unsigned top_menu_available)358 HDMV_VM *hdmv_vm_init(struct bd_disc *disc, BD_REGISTERS *regs,
359 unsigned num_titles, unsigned first_play_available, unsigned top_menu_available)
360 {
361 HDMV_VM *p = calloc(1, sizeof(HDMV_VM));
362
363 if (!p) {
364 BD_DEBUG(DBG_CRIT, "out of memory\n");
365 return NULL;
366 }
367
368 /* read movie objects */
369 p->movie_objects = mobj_get(disc);
370 if (!p->movie_objects) {
371 X_FREE(p);
372 return NULL;
373 }
374
375 p->regs = regs;
376 p->num_titles = num_titles;
377 p->have_top_menu = top_menu_available;
378 p->have_first_play = first_play_available;
379 #ifdef DEBUG
380 p->rand = 1;
381 #else
382 p->rand = time(NULL);
383 #endif
384
385 bd_mutex_init(&p->mutex);
386
387 return p;
388 }
389
_free_ig_object(HDMV_VM * p)390 static void _free_ig_object(HDMV_VM *p)
391 {
392 if (p->ig_object) {
393 X_FREE(p->ig_object->cmds);
394 X_FREE(p->ig_object);
395 }
396 }
397
hdmv_vm_free(HDMV_VM ** p)398 void hdmv_vm_free(HDMV_VM **p)
399 {
400 if (p && *p) {
401
402 bd_mutex_destroy(&(*p)->mutex);
403
404 mobj_free(&(*p)->movie_objects);
405
406 _free_ig_object(*p);
407
408 X_FREE(*p);
409 }
410 }
411
412 /*
413 * suspend/resume ("function call")
414 */
415
_suspended_at_play_pl(HDMV_VM * p)416 static int _suspended_at_play_pl(HDMV_VM *p)
417 {
418 int play_pl = 0;
419 if (p && p->suspended_object) {
420 MOBJ_CMD *cmd = &p->suspended_object->cmds[p->suspended_pc];
421 HDMV_INSN *insn = &cmd->insn;
422 play_pl = (insn->grp == INSN_GROUP_BRANCH &&
423 insn->sub_grp == BRANCH_PLAY &&
424 ( insn->branch_opt == INSN_PLAY_PL ||
425 insn->branch_opt == INSN_PLAY_PL_PI ||
426 insn->branch_opt == INSN_PLAY_PL_PM));
427 }
428
429 return play_pl;
430 }
431
_suspend_for_play_pl(HDMV_VM * p)432 static int _suspend_for_play_pl(HDMV_VM *p)
433 {
434 if (p->playing_object) {
435 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_suspend_for_play_pl(): object already playing playlist !\n");
436 return -1;
437 }
438
439 p->playing_object = p->object;
440 p->playing_pc = p->pc;
441
442 p->object = NULL;
443
444 return 0;
445 }
446
_resume_from_play_pl(HDMV_VM * p)447 static int _resume_from_play_pl(HDMV_VM *p)
448 {
449 if (!p->playing_object) {
450 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_resume_from_play_pl(): object not playing playlist !\n");
451 return -1;
452 }
453
454 p->object = p->playing_object;
455 p->pc = p->playing_pc + 1;
456
457 p->playing_object = NULL;
458
459 _free_ig_object(p);
460
461 return 0;
462 }
463
_suspend_object(HDMV_VM * p,int psr_backup)464 static void _suspend_object(HDMV_VM *p, int psr_backup)
465 {
466 BD_DEBUG(DBG_HDMV, "_suspend_object()\n");
467
468 if (p->suspended_object) {
469 BD_DEBUG(DBG_HDMV, "_suspend_object: object already suspended !\n");
470 // [execute the call, discard old suspended object (10.2.4.2.2)].
471 }
472
473 if (psr_backup) {
474 bd_psr_save_state(p->regs);
475 }
476
477 if (p->ig_object) {
478 if (!p->playing_object) {
479 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_suspend_object: IG object tries to suspend, no playing object !\n");
480 return;
481 }
482 p->suspended_object = p->playing_object;
483 p->suspended_pc = p->playing_pc;
484
485 p->playing_object = NULL;
486
487 } else {
488
489 if (p->playing_object) {
490 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_suspend_object: Movie object tries to suspend, also playing object present !\n");
491 return;
492 }
493
494 p->suspended_object = p->object;
495 p->suspended_pc = p->pc;
496
497 }
498
499 p->object = NULL;
500
501 _free_ig_object(p);
502 }
503
_resume_object(HDMV_VM * p,int psr_restore)504 static int _resume_object(HDMV_VM *p, int psr_restore)
505 {
506 if (!p->suspended_object) {
507 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_resume_object: no suspended object!\n");
508 return -1;
509 }
510
511 p->object = NULL;
512 p->playing_object = NULL;
513 _free_ig_object(p);
514
515 if (psr_restore) {
516 /* check if suspended in play_pl */
517 if (_suspended_at_play_pl(p)) {
518 BD_DEBUG(DBG_HDMV, "resuming playlist playback\n");
519 p->playing_object = p->suspended_object;
520 p->playing_pc = p->suspended_pc;
521 p->suspended_object = NULL;
522 bd_psr_restore_state(p->regs);
523
524 return 0;
525 }
526 bd_psr_restore_state(p->regs);
527 }
528
529 p->object = p->suspended_object;
530 p->pc = p->suspended_pc + 1;
531
532 p->suspended_object = NULL;
533
534 BD_DEBUG(DBG_HDMV, "resuming object %ld at %d\n",
535 (long)(p->object - p->movie_objects->objects),
536 p->pc);
537
538 _queue_event(p, HDMV_EVENT_PLAY_STOP, 0);
539
540 return 0;
541 }
542
543
544 /*
545 * branching
546 */
547
_is_valid_title(HDMV_VM * p,uint32_t title)548 static int _is_valid_title(HDMV_VM *p, uint32_t title)
549 {
550 if (title == 0) {
551 return p->have_top_menu;
552 }
553 if (title == 0xffff) {
554 return p->have_first_play;
555 }
556
557 return title > 0 && title <= p->num_titles;
558 }
559
_jump_object(HDMV_VM * p,uint32_t object)560 static int _jump_object(HDMV_VM *p, uint32_t object)
561 {
562 if (object >= p->movie_objects->num_objects) {
563 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_jump_object(): invalid object %u\n", object);
564 return -1;
565 }
566
567 BD_DEBUG(DBG_HDMV, "_jump_object(): jumping to object %u\n", object);
568
569 _queue_event(p, HDMV_EVENT_PLAY_STOP, 0);
570
571 _free_ig_object(p);
572
573 p->playing_object = NULL;
574
575 p->pc = 0;
576 p->object = &p->movie_objects->objects[object];
577
578 /* suspended object is not discarded */
579
580 return 0;
581 }
582
_jump_title(HDMV_VM * p,uint32_t title)583 static int _jump_title(HDMV_VM *p, uint32_t title)
584 {
585 if (_is_valid_title(p, title)) {
586 BD_DEBUG(DBG_HDMV, "_jump_title(%u)\n", title);
587
588 /* discard suspended object */
589 p->suspended_object = NULL;
590 p->playing_object = NULL;
591 bd_psr_reset_backup_registers(p->regs);
592
593 _queue_event(p, HDMV_EVENT_TITLE, title);
594 return 0;
595 }
596
597 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_jump_title(%u): invalid title number\n", title);
598
599 return -1;
600 }
601
_call_object(HDMV_VM * p,uint32_t object)602 static int _call_object(HDMV_VM *p, uint32_t object)
603 {
604 if (object >= p->movie_objects->num_objects) {
605 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_call_object(): invalid object %u\n", object);
606 return -1;
607 }
608
609 BD_DEBUG(DBG_HDMV, "_call_object(%u)\n", object);
610
611 _suspend_object(p, 1);
612
613 return _jump_object(p, object);
614 }
615
_call_title(HDMV_VM * p,uint32_t title)616 static int _call_title(HDMV_VM *p, uint32_t title)
617 {
618 if (_is_valid_title(p, title)) {
619 BD_DEBUG(DBG_HDMV, "_call_title(%u)\n", title);
620
621 _suspend_object(p, 1);
622
623 _queue_event(p, HDMV_EVENT_TITLE, title);
624
625 return 0;
626 }
627
628 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_call_title(%u): invalid title number\n", title);
629
630 return -1;
631 }
632
633 /*
634 * playback control
635 */
636
_play_at(HDMV_VM * p,int playlist,int playitem,int playmark)637 static int _play_at(HDMV_VM *p, int playlist, int playitem, int playmark)
638 {
639 if (p->ig_object && playlist >= 0) {
640 BD_DEBUG(DBG_HDMV | DBG_CRIT, "play_at(list %d, item %d, mark %d): "
641 "playlist change not allowed in interactive composition\n",
642 playlist, playitem, playmark);
643 return -1;
644 }
645
646 if (!p->ig_object && playlist < 0) {
647 BD_DEBUG(DBG_HDMV | DBG_CRIT, "play_at(list %d, item %d, mark %d): "
648 "playlist not given in movie object (link commands not allowed)\n",
649 playlist, playitem, playmark);
650 return -1;
651 }
652
653 BD_DEBUG(DBG_HDMV, "play_at(list %d, item %d, mark %d)\n",
654 playlist, playitem, playmark);
655
656 if (playlist >= 0) {
657 _queue_event(p, HDMV_EVENT_PLAY_PL, playlist);
658 _suspend_for_play_pl(p);
659 }
660
661 if (playitem >= 0) {
662 _queue_event(p, HDMV_EVENT_PLAY_PI, playitem);
663 }
664
665 if (playmark >= 0) {
666 _queue_event(p, HDMV_EVENT_PLAY_PM, playmark);
667 }
668
669 return 0;
670 }
671
_play_stop(HDMV_VM * p)672 static int _play_stop(HDMV_VM *p)
673 {
674 if (!p->ig_object) {
675 BD_DEBUG(DBG_HDMV | DBG_CRIT, "_play_stop() not allowed in movie object\n");
676 return -1;
677 }
678
679 BD_DEBUG(DBG_HDMV, "_play_stop()\n");
680 _queue_event(p, HDMV_EVENT_PLAY_STOP, 1);
681
682 /* terminate IG object. Continue executing movie object. */
683 if (_resume_from_play_pl(p) < 0) {
684 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_play_stop(): resuming movie object failed !\n");
685 return -1;
686 }
687
688 return 0;
689 }
690
691 /*
692 * SET/SYSTEM setstream
693 */
694
_set_stream(HDMV_VM * p,uint32_t dst,uint32_t src)695 static void _set_stream(HDMV_VM *p, uint32_t dst, uint32_t src)
696 {
697 BD_DEBUG(DBG_HDMV, "_set_stream(0x%x, 0x%x)\n", dst, src);
698
699 /* primary audio stream */
700 if (dst & 0x80000000) {
701 bd_psr_write(p->regs, PSR_PRIMARY_AUDIO_ID, (dst >> 16) & 0xfff);
702 }
703
704 /* IG stream */
705 if (src & 0x80000000) {
706 bd_psr_write(p->regs, PSR_IG_STREAM_ID, (src >> 16) & 0xff);
707 }
708
709 /* angle number */
710 if (src & 0x8000) {
711 bd_psr_write(p->regs, PSR_ANGLE_NUMBER, src & 0xff);
712 }
713
714 /* PSR2 */
715
716 bd_psr_lock(p->regs);
717
718 uint32_t psr2 = bd_psr_read(p->regs, PSR_PG_STREAM);
719
720 /* PG TextST stream number */
721 if (dst & 0x8000) {
722 uint32_t text_st_num = dst & 0xfff;
723 psr2 = text_st_num | (psr2 & 0xfffff000);
724 }
725
726 /* Update PG TextST stream display flag */
727 uint32_t disp_s_flag = (dst & 0x4000) << 17;
728 psr2 = disp_s_flag | (psr2 & 0x7fffffff);
729
730 bd_psr_write(p->regs, PSR_PG_STREAM, psr2);
731
732 bd_psr_unlock(p->regs);
733 }
734
_set_sec_stream(HDMV_VM * p,uint32_t dst,uint32_t src)735 static void _set_sec_stream(HDMV_VM *p, uint32_t dst, uint32_t src)
736 {
737 BD_DEBUG(DBG_HDMV, "_set_sec_stream(0x%x, 0x%x)\n", dst, src);
738
739 uint32_t disp_v_flag = (dst >> 30) & 1;
740 uint32_t disp_a_flag = (src >> 30) & 1;
741 uint32_t text_st_flags = (src >> 13) & 3;
742
743 /* PSR14 */
744
745 bd_psr_lock(p->regs);
746
747 uint32_t psr14 = bd_psr_read(p->regs, PSR_SECONDARY_AUDIO_VIDEO);
748
749 /* secondary video */
750 if (dst & 0x80000000) {
751 uint32_t sec_video = dst & 0xff;
752 psr14 = (sec_video << 8) | (psr14 & 0xffff00ff);
753 }
754
755 /* secondary video size */
756 if (dst & 0x00800000) {
757 uint32_t video_size = (dst >> 16) & 0xf;
758 psr14 = (video_size << 24) | (psr14 & 0xf0ffffff);
759 }
760
761 /* secondary audio */
762 if (src & 0x80000000) {
763 uint32_t sec_audio = (src >> 16) & 0xff;
764 psr14 = sec_audio | (psr14 & 0xffffff00);
765 }
766
767 psr14 = (disp_v_flag << 31) | (psr14 & 0x7fffffff);
768 psr14 = (disp_a_flag << 30) | (psr14 & 0xbfffffff);
769
770 bd_psr_write(p->regs, PSR_SECONDARY_AUDIO_VIDEO, psr14);
771
772 /* PSR2 */
773
774 uint32_t psr2 = bd_psr_read(p->regs, PSR_PG_STREAM);
775
776 /* PiP PG TextST stream */
777 if (src & 0x8000) {
778 uint32_t stream = src & 0xfff;
779 psr2 = (stream << 16) | (psr2 & 0xf000ffff);
780 }
781
782 psr2 = (text_st_flags << 30) | (psr2 & 0x3fffffff);
783
784 bd_psr_write(p->regs, PSR_PG_STREAM, psr2);
785
786 bd_psr_unlock(p->regs);
787 }
788
_set_stream_ss(HDMV_VM * p,uint32_t dst,uint32_t src)789 static void _set_stream_ss(HDMV_VM *p, uint32_t dst, uint32_t src)
790 {
791 BD_DEBUG(DBG_HDMV, "_set_stream_ss(0x%x, 0x%x)\n", dst, src);
792
793 if (!(bd_psr_read(p->regs, PSR_3D_STATUS) & 1)) {
794 BD_DEBUG(DBG_HDMV, "_set_stream_ss ignored (PSR22 indicates 2D mode)\n");
795 return;
796 }
797
798 BD_DEBUG(DBG_HDMV, "_set_stream_ss(0x%x, 0x%x) unimplemented\n", dst, src);
799 }
800
_setsystem_0x10(HDMV_VM * p,uint32_t dst,uint32_t src)801 static void _setsystem_0x10(HDMV_VM *p, uint32_t dst, uint32_t src)
802 {
803 BD_DEBUG(DBG_HDMV, "_set_psr103(0x%x, 0x%x)\n", dst, src);
804
805 bd_psr_lock(p->regs);
806
807 /* just a guess ... */
808 //bd_psr_write(p->regs, 104, 0);
809 bd_psr_write(p->regs, 103, dst);
810
811 bd_psr_unlock(p->regs);
812 }
813
814 /*
815 * SET/SYSTEM navigation control
816 */
817
_set_button_page(HDMV_VM * p,uint32_t dst,uint32_t src)818 static void _set_button_page(HDMV_VM *p, uint32_t dst, uint32_t src)
819 {
820 if (p->ig_object) {
821 uint32_t param;
822 param = (src & 0xc0000000) | /* page and effects flags */
823 ((dst & 0x80000000) >> 2) | /* button flag */
824 ((src & 0x000000ff) << 16) | /* page id */
825 (dst & 0x0000ffff); /* button id */
826
827 _queue_event(p, HDMV_EVENT_SET_BUTTON_PAGE, param);
828
829 /* terminate */
830 p->pc = 1 << 17;
831
832 return;
833 }
834
835 /* selected button */
836 if (dst & 0x80000000) {
837 bd_psr_write(p->regs, PSR_SELECTED_BUTTON_ID, dst & 0xffff);
838 }
839
840 /* active page */
841 if (src & 0x80000000) {
842 bd_psr_write(p->regs, PSR_MENU_PAGE_ID, src & 0xff);
843 }
844 }
845
_enable_button(HDMV_VM * p,uint32_t dst,int enable)846 static void _enable_button(HDMV_VM *p, uint32_t dst, int enable)
847 {
848 /* not valid in movie objects */
849 if (p->ig_object) {
850 if (enable) {
851 _queue_event(p, HDMV_EVENT_ENABLE_BUTTON, dst);
852 } else {
853 _queue_event(p, HDMV_EVENT_DISABLE_BUTTON, dst);
854 }
855 }
856 }
857
_set_still_mode(HDMV_VM * p,int enable)858 static void _set_still_mode(HDMV_VM *p, int enable)
859 {
860 /* not valid in movie objects */
861 if (p->ig_object) {
862 _queue_event(p, HDMV_EVENT_STILL, enable);
863 }
864 }
865
_popup_off(HDMV_VM * p)866 static void _popup_off(HDMV_VM *p)
867 {
868 /* not valid in movie objects */
869 if (p->ig_object) {
870 _queue_event(p, HDMV_EVENT_POPUP_OFF, 1);
871 }
872 }
873
874 /*
875 * SET/SYSTEM 3D mode
876 */
877
_set_output_mode(HDMV_VM * p,uint32_t dst)878 static void _set_output_mode(HDMV_VM *p, uint32_t dst)
879 {
880 if ((bd_psr_read(p->regs, PSR_PROFILE_VERSION) & 0x130240) != 0x130240) {
881 BD_DEBUG(DBG_HDMV, "_set_output_mode ignored (not running as profile 5 player)\n");
882 return;
883 }
884
885 bd_psr_lock(p->regs);
886
887 uint32_t psr22 = bd_psr_read(p->regs, PSR_3D_STATUS);
888
889 /* update output mode (bit 0). PSR22 bits 1 and 2 are subtitle alignment (_set_stream_ss()) */
890 if (dst & 1) {
891 psr22 |= 1;
892 } else {
893 psr22 &= ~1;
894 }
895
896 bd_psr_write(p->regs, PSR_3D_STATUS, psr22);
897
898 bd_psr_unlock(p->regs);
899 }
900
901 /*
902 * navigation timer
903 */
904
_set_nv_timer(HDMV_VM * p,uint32_t dst,uint32_t src)905 static void _set_nv_timer(HDMV_VM *p, uint32_t dst, uint32_t src)
906 {
907 uint32_t mobj_id = dst & 0xffff;
908 uint32_t timeout = src & 0xffff;
909
910 if (!timeout) {
911 /* cancel timer */
912 p->nv_timer.time = 0;
913
914 bd_psr_write(p->regs, PSR_NAV_TIMER, 0);
915
916 return;
917 }
918
919 /* validate params */
920 if (mobj_id >= p->movie_objects->num_objects) {
921 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_set_nv_timer(): invalid object id (%d) !\n", mobj_id);
922 return;
923 }
924 if (timeout > 300) {
925 BD_DEBUG(DBG_HDMV|DBG_CRIT, "_set_nv_timer(): invalid timeout (%d) !\n", timeout);
926 return;
927 }
928
929 BD_DEBUG(DBG_HDMV | DBG_CRIT, "_set_nv_timer(): navigation timer not implemented !\n");
930
931 /* set expiration time */
932 p->nv_timer.time = time(NULL);
933 p->nv_timer.time += timeout;
934
935 p->nv_timer.mobj_id = mobj_id;
936
937 bd_psr_write(p->regs, PSR_NAV_TIMER, timeout);
938 }
939
940 /* Unused function.
941 * Commenting out to disable "‘_check_nv_timer’ defined but not used" warning
942 static int _check_nv_timer(HDMV_VM *p)
943 {
944 if (p->nv_timer.time > 0) {
945 time_t now = time(NULL);
946
947 if (now >= p->nv_timer.time) {
948 BD_DEBUG(DBG_HDMV, "navigation timer expired, jumping to object %d\n", p->nv_timer.mobj_id);
949
950 bd_psr_write(p->regs, PSR_NAV_TIMER, 0);
951
952 p->nv_timer.time = 0;
953 _jump_object(p, p->nv_timer.mobj_id);
954
955 return 0;
956 }
957
958 bd_psr_write(p->regs, PSR_NAV_TIMER, (p->nv_timer.time - now));
959 }
960
961 return -1;
962 }
963 */
964
965 /*
966 * trace
967 */
968
_hdmv_trace_cmd(int pc,MOBJ_CMD * cmd)969 static void _hdmv_trace_cmd(int pc, MOBJ_CMD *cmd)
970 {
971 if (bd_get_debug_mask() & DBG_HDMV) {
972 char buf[384], *dst = buf;
973
974 dst += sprintf(dst, "%04d: ", pc);
975
976 /*dst +=*/ mobj_sprint_cmd(dst, cmd);
977
978 BD_DEBUG(DBG_HDMV, "%s\n", buf);
979 }
980 }
981
_hdmv_trace_res(uint32_t new_src,uint32_t new_dst,uint32_t orig_src,uint32_t orig_dst)982 static void _hdmv_trace_res(uint32_t new_src, uint32_t new_dst, uint32_t orig_src, uint32_t orig_dst)
983 {
984 if (bd_get_debug_mask() & DBG_HDMV) {
985
986 if (new_dst != orig_dst || new_src != orig_src) {
987 char buf[384], *dst = buf;
988
989 dst += sprintf(dst, " : [");
990 if (new_dst != orig_dst) {
991 dst += sprintf(dst, " dst 0x%x <== 0x%x ", orig_dst, new_dst);
992 }
993 if (new_src != orig_src) {
994 dst += sprintf(dst, " src 0x%x <== 0x%x ", orig_src, new_src);
995 }
996 /*dst +=*/ sprintf(dst, "]");
997
998 BD_DEBUG(DBG_HDMV, "%s\n", buf);
999 }
1000 }
1001 }
1002
1003 /*
1004 * interpreter
1005 */
1006
1007 /*
1008 * tools
1009 */
1010
1011 #define SWAP_u32(a, b) do { uint32_t tmp = a; a = b; b = tmp; } while(0)
1012
RAND_u32(HDMV_VM * p,uint32_t range)1013 static inline uint32_t RAND_u32(HDMV_VM *p, uint32_t range)
1014 {
1015 p->rand = p->rand * UINT64_C(6364136223846793005) + UINT64_C(1);
1016
1017 if (range == 0) {
1018 BD_DEBUG(DBG_HDMV|DBG_CRIT, "RAND_u32: invalid range (0)\n");
1019 return 1;
1020 }
1021
1022 return ((uint32_t)(p->rand >> 32)) % range + 1;
1023 }
1024
ADD_u32(uint32_t a,uint32_t b)1025 static inline uint32_t ADD_u32(uint32_t a, uint32_t b)
1026 {
1027 /* overflow -> saturate */
1028 uint64_t result = (uint64_t)a + b;
1029 return result < 0xffffffff ? (uint32_t)result : 0xffffffff;
1030 }
1031
MUL_u32(uint32_t a,uint32_t b)1032 static inline uint32_t MUL_u32(uint32_t a, uint32_t b)
1033 {
1034 /* overflow -> saturate */
1035 uint64_t result = (uint64_t)a * b;
1036 return result < 0xffffffff ? (uint32_t)result : 0xffffffff;
1037 }
1038
1039 /*
1040 * _hdmv_step()
1041 * - execute next instruction from current program
1042 */
_hdmv_step(HDMV_VM * p)1043 static int _hdmv_step(HDMV_VM *p)
1044 {
1045 MOBJ_CMD *cmd = &p->object->cmds[p->pc];
1046 HDMV_INSN *insn = &cmd->insn;
1047 uint32_t src = 0;
1048 uint32_t dst = 0;
1049 int inc_pc = 1;
1050
1051 /* fetch operand values */
1052 _fetch_operands(p, cmd, &dst, &src);
1053
1054 /* trace */
1055 _hdmv_trace_cmd(p->pc, cmd);
1056
1057 /* execute */
1058 switch (insn->grp) {
1059 case INSN_GROUP_BRANCH:
1060 switch (insn->sub_grp) {
1061 case BRANCH_GOTO:
1062 if (insn->op_cnt > 1) {
1063 BD_DEBUG(DBG_HDMV|DBG_CRIT, "too many operands in BRANCH/GOTO opcode 0x%08x\n", *(uint32_t*)insn);
1064 }
1065 switch (insn->branch_opt) {
1066 case INSN_NOP: break;
1067 case INSN_GOTO: p->pc = dst - 1; break;
1068 case INSN_BREAK: p->pc = 1 << 17; break;
1069 default:
1070 BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown BRANCH/GOTO option %d in opcode 0x%08x\n",
1071 insn->branch_opt, *(uint32_t*)insn);
1072 break;
1073 }
1074 break;
1075 case BRANCH_JUMP:
1076 if (insn->op_cnt > 1) {
1077 BD_DEBUG(DBG_HDMV|DBG_CRIT, "too many operands in BRANCH/JUMP opcode 0x%08x\n", *(uint32_t*)insn);
1078 }
1079 switch (insn->branch_opt) {
1080 case INSN_JUMP_TITLE: _jump_title(p, dst); break;
1081 case INSN_CALL_TITLE: _call_title(p, dst); break;
1082 case INSN_RESUME: _resume_object(p, 1); break;
1083 case INSN_JUMP_OBJECT: if (!_jump_object(p, dst)) { inc_pc = 0; } break;
1084 case INSN_CALL_OBJECT: if (!_call_object(p, dst)) { inc_pc = 0; } break;
1085 default:
1086 BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown BRANCH/JUMP option %d in opcode 0x%08x\n",
1087 insn->branch_opt, *(uint32_t*)insn);
1088 break;
1089 }
1090 break;
1091 case BRANCH_PLAY:
1092 switch (insn->branch_opt) {
1093 case INSN_PLAY_PL: _play_at(p, dst, -1, -1); break;
1094 case INSN_PLAY_PL_PI: _play_at(p, dst, src, -1); break;
1095 case INSN_PLAY_PL_PM: _play_at(p, dst, -1, src); break;
1096 case INSN_LINK_PI: _play_at(p, -1, dst, -1); break;
1097 case INSN_LINK_MK: _play_at(p, -1, -1, dst); break;
1098 case INSN_TERMINATE_PL: if (!_play_stop(p)) { inc_pc = 0; } break;
1099 default:
1100 BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown BRANCH/PLAY option %d in opcode 0x%08x\n",
1101 insn->branch_opt, *(uint32_t*)insn);
1102 break;
1103 }
1104 break;
1105
1106 default:
1107 BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown BRANCH subgroup %d in opcode 0x%08x\n",
1108 insn->sub_grp, *(uint32_t*)insn);
1109 break;
1110 }
1111 break; /* INSN_GROUP_BRANCH */
1112
1113 case INSN_GROUP_CMP:
1114 if (insn->op_cnt < 2) {
1115 BD_DEBUG(DBG_HDMV|DBG_CRIT, "missing operand in BRANCH/JUMP opcode 0x%08x\n", *(uint32_t*)insn);
1116 }
1117 switch (insn->cmp_opt) {
1118 case INSN_BC: p->pc += !!(dst & ~src); break;
1119 case INSN_EQ: p->pc += !(dst == src); break;
1120 case INSN_NE: p->pc += !(dst != src); break;
1121 case INSN_GE: p->pc += !(dst >= src); break;
1122 case INSN_GT: p->pc += !(dst > src); break;
1123 case INSN_LE: p->pc += !(dst <= src); break;
1124 case INSN_LT: p->pc += !(dst < src); break;
1125 default:
1126 BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown COMPARE option %d in opcode 0x%08x\n",
1127 insn->cmp_opt, *(uint32_t*)insn);
1128 break;
1129 }
1130 break; /* INSN_GROUP_CMP */
1131
1132 case INSN_GROUP_SET:
1133 switch (insn->sub_grp) {
1134 case SET_SET: {
1135 uint32_t src0 = src;
1136 uint32_t dst0 = dst;
1137
1138 if (insn->op_cnt < 2) {
1139 BD_DEBUG(DBG_HDMV|DBG_CRIT, "missing operand in SET/SET opcode 0x%08x\n", *(uint32_t*)insn);
1140 }
1141 switch (insn->set_opt) {
1142 case INSN_MOVE: dst = src; break;
1143 case INSN_SWAP: SWAP_u32(src, dst); break;
1144 case INSN_SUB: dst = dst > src ? dst - src : 0; break;
1145 case INSN_DIV: dst = src > 0 ? dst / src : 0xffffffff; break;
1146 case INSN_MOD: dst = src > 0 ? dst % src : 0xffffffff; break;
1147 case INSN_ADD: dst = ADD_u32(src, dst); break;
1148 case INSN_MUL: dst = MUL_u32(dst, src); break;
1149 case INSN_RND: dst = RAND_u32(p, src); break;
1150 case INSN_AND: dst &= src; break;
1151 case INSN_OR: dst |= src; break;
1152 case INSN_XOR: dst ^= src; break;
1153 case INSN_BITSET: dst |= (1 << src); break;
1154 case INSN_BITCLR: dst &= ~(1 << src); break;
1155 case INSN_SHL: dst <<= src; break;
1156 case INSN_SHR: dst >>= src; break;
1157 default:
1158 BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown SET option %d in opcode 0x%08x\n",
1159 insn->set_opt, *(uint32_t*)insn);
1160 break;
1161 }
1162
1163 /* store result(s) */
1164 if (dst != dst0 || src != src0) {
1165
1166 _hdmv_trace_res(src, dst, src0, dst0);
1167
1168 _store_result(p, cmd, src, dst, src0, dst0);
1169 }
1170 break;
1171 }
1172 case SET_SETSYSTEM:
1173 switch (insn->set_opt) {
1174 case INSN_SET_STREAM: _set_stream (p, dst, src); break;
1175 case INSN_SET_SEC_STREAM: _set_sec_stream (p, dst, src); break;
1176 case INSN_SET_NV_TIMER: _set_nv_timer (p, dst, src); break;
1177 case INSN_SET_BUTTON_PAGE: _set_button_page(p, dst, src); break;
1178 case INSN_ENABLE_BUTTON: _enable_button (p, dst, 1); break;
1179 case INSN_DISABLE_BUTTON: _enable_button (p, dst, 0); break;
1180 case INSN_POPUP_OFF: _popup_off (p); break;
1181 case INSN_STILL_ON: _set_still_mode (p, 1); break;
1182 case INSN_STILL_OFF: _set_still_mode (p, 0); break;
1183 case INSN_SET_OUTPUT_MODE: _set_output_mode(p, dst); break;
1184 case INSN_SET_STREAM_SS: _set_stream_ss (p, dst, src); break;
1185 case INSN_SETSYSTEM_0x10: _setsystem_0x10 (p, dst, src); break;
1186 default:
1187 BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown SETSYSTEM option %d in opcode 0x%08x\n", insn->set_opt, *(uint32_t*)insn);
1188 break;
1189 }
1190 break;
1191 default:
1192 BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown SET subgroup %d in opcode 0x%08x\n",
1193 insn->sub_grp, *(uint32_t*)insn);
1194 break;
1195 }
1196 break; /* INSN_GROUP_SET */
1197
1198 default:
1199 BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown operation group %d in opcode 0x%08x\n",
1200 insn->grp, *(uint32_t*)insn);
1201 break;
1202 }
1203
1204 /* inc program counter to next instruction */
1205 p->pc += inc_pc;
1206
1207 return 0;
1208 }
1209
1210 /*
1211 * interface
1212 */
1213
hdmv_vm_select_object(HDMV_VM * p,uint32_t object)1214 int hdmv_vm_select_object(HDMV_VM *p, uint32_t object)
1215 {
1216 int result;
1217
1218 if (!p) {
1219 return -1;
1220 }
1221
1222 bd_mutex_lock(&p->mutex);
1223
1224 result = _jump_object(p, object);
1225
1226 bd_mutex_unlock(&p->mutex);
1227 return result;
1228 }
1229
_set_object(HDMV_VM * p,int num_nav_cmds,void * nav_cmds)1230 static int _set_object(HDMV_VM *p, int num_nav_cmds, void *nav_cmds)
1231 {
1232 MOBJ_OBJECT *ig_object = calloc(1, sizeof(MOBJ_OBJECT));
1233 if (!ig_object) {
1234 BD_DEBUG(DBG_CRIT, "out of memory\n");
1235 return -1;
1236 }
1237
1238 ig_object->num_cmds = num_nav_cmds;
1239 ig_object->cmds = calloc(num_nav_cmds, sizeof(MOBJ_CMD));
1240 if (!ig_object->cmds) {
1241 BD_DEBUG(DBG_CRIT, "out of memory\n");
1242 X_FREE(ig_object);
1243 return -1;
1244 }
1245
1246 memcpy(ig_object->cmds, nav_cmds, num_nav_cmds * sizeof(MOBJ_CMD));
1247
1248 p->pc = 0;
1249 p->ig_object = ig_object;
1250 p->object = ig_object;
1251
1252 return 0;
1253 }
1254
hdmv_vm_set_object(HDMV_VM * p,int num_nav_cmds,void * nav_cmds)1255 int hdmv_vm_set_object(HDMV_VM *p, int num_nav_cmds, void *nav_cmds)
1256 {
1257 int result = -1;
1258
1259 if (!p) {
1260 return -1;
1261 }
1262
1263 bd_mutex_lock(&p->mutex);
1264
1265 p->object = NULL;
1266
1267 _free_ig_object(p);
1268
1269 if (nav_cmds && num_nav_cmds > 0) {
1270 result = _set_object(p, num_nav_cmds, nav_cmds);
1271 }
1272
1273 bd_mutex_unlock(&p->mutex);
1274
1275 return result;
1276 }
1277
hdmv_vm_get_event(HDMV_VM * p,HDMV_EVENT * ev)1278 int hdmv_vm_get_event(HDMV_VM *p, HDMV_EVENT *ev)
1279 {
1280 int result;
1281 bd_mutex_lock(&p->mutex);
1282
1283 result = _get_event(p, ev);
1284
1285 bd_mutex_unlock(&p->mutex);
1286 return result;
1287 }
1288
hdmv_vm_running(HDMV_VM * p)1289 int hdmv_vm_running(HDMV_VM *p)
1290 {
1291 int result;
1292
1293 if (!p) {
1294 return 0;
1295 }
1296
1297 bd_mutex_lock(&p->mutex);
1298
1299 result = !!p->object;
1300
1301 bd_mutex_unlock(&p->mutex);
1302 return result;
1303 }
1304
hdmv_vm_get_uo_mask(HDMV_VM * p)1305 uint32_t hdmv_vm_get_uo_mask(HDMV_VM *p)
1306 {
1307 uint32_t mask = 0;
1308 const MOBJ_OBJECT *o = NULL;
1309
1310 if (!p) {
1311 return 0;
1312 }
1313
1314 bd_mutex_lock(&p->mutex);
1315
1316 if ((o = (p->object && !p->ig_object) ? p->object : (p->playing_object ? p->playing_object : p->suspended_object))) {
1317 mask |= o->menu_call_mask;
1318 mask |= o->title_search_mask << 1;
1319 }
1320
1321 bd_mutex_unlock(&p->mutex);
1322 return mask;
1323 }
1324
hdmv_vm_resume(HDMV_VM * p)1325 int hdmv_vm_resume(HDMV_VM *p)
1326 {
1327 int result;
1328
1329 if (!p) {
1330 return -1;
1331 }
1332
1333 bd_mutex_lock(&p->mutex);
1334
1335 result = _resume_from_play_pl(p);
1336
1337 bd_mutex_unlock(&p->mutex);
1338 return result;
1339 }
1340
hdmv_vm_suspend_pl(HDMV_VM * p)1341 int hdmv_vm_suspend_pl(HDMV_VM *p)
1342 {
1343 int result = -1;
1344
1345 if (!p) {
1346 return -1;
1347 }
1348
1349 bd_mutex_lock(&p->mutex);
1350
1351 if (p->object || p->ig_object) {
1352 BD_DEBUG(DBG_HDMV, "hdmv_vm_suspend_pl(): HDMV VM is still running\n");
1353
1354 } else if (!p->playing_object) {
1355 BD_DEBUG(DBG_HDMV, "hdmv_vm_suspend_pl(): No playing object\n");
1356
1357 } else if (!p->playing_object->resume_intention_flag) {
1358 BD_DEBUG(DBG_HDMV, "hdmv_vm_suspend_pl(): no resume intention flag\n");
1359
1360 p->playing_object = NULL;
1361 result = 0;
1362
1363 } else {
1364 p->suspended_object = p->playing_object;
1365 p->suspended_pc = p->playing_pc;
1366
1367 p->playing_object = NULL;
1368
1369 bd_psr_save_state(p->regs);
1370 result = 0;
1371 }
1372
1373 bd_mutex_unlock(&p->mutex);
1374 return result;
1375 }
1376
1377 /* terminate program after MAX_LOOP instructions */
1378 #define MAX_LOOP 1000000
1379
_vm_run(HDMV_VM * p,HDMV_EVENT * ev)1380 static int _vm_run(HDMV_VM *p, HDMV_EVENT *ev)
1381 {
1382 int max_loop = MAX_LOOP;
1383
1384 /* pending events ? */
1385 if (!_get_event(p, ev)) {
1386 return 0;
1387 }
1388
1389 /* valid program ? */
1390 if (!p->object) {
1391 BD_DEBUG(DBG_HDMV|DBG_CRIT, "hdmv_vm_run(): no object selected\n");
1392 return -1;
1393 }
1394
1395 while (--max_loop > 0) {
1396
1397 /* suspended ? */
1398 if (!p->object) {
1399 BD_DEBUG(DBG_HDMV, "hdmv_vm_run(): object suspended\n");
1400 _get_event(p, ev);
1401 return 0;
1402 }
1403
1404 /* terminated ? */
1405 if (p->pc >= p->object->num_cmds) {
1406 BD_DEBUG(DBG_HDMV, "terminated with PC=%d\n", p->pc);
1407 p->object = NULL;
1408 ev->event = HDMV_EVENT_END;
1409
1410 if (p->ig_object) {
1411 ev->event = HDMV_EVENT_IG_END;
1412 _free_ig_object(p);
1413 }
1414
1415 return 0;
1416 }
1417
1418 /* next instruction */
1419 if (_hdmv_step(p) < 0) {
1420 p->object = NULL;
1421 return -1;
1422 }
1423
1424 /* events ? */
1425 if (!_get_event(p, ev)) {
1426 return 0;
1427 }
1428 }
1429
1430 BD_DEBUG(DBG_HDMV|DBG_CRIT, "hdmv_vm: infinite program ? terminated after %d instructions.\n", MAX_LOOP);
1431 p->object = NULL;
1432 return -1;
1433 }
1434
hdmv_vm_run(HDMV_VM * p,HDMV_EVENT * ev)1435 int hdmv_vm_run(HDMV_VM *p, HDMV_EVENT *ev)
1436 {
1437 int result;
1438
1439 if (!p) {
1440 return -1;
1441 }
1442
1443 bd_mutex_lock(&p->mutex);
1444
1445 result = _vm_run(p, ev);
1446
1447 bd_mutex_unlock(&p->mutex);
1448 return result;
1449 }
1450