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