MythTV  0.26-pre
hdmv_vm.c
Go to the documentation of this file.
00001 /*
00002  * This file is part of libbluray
00003  * Copyright (C) 2010  hpi1
00004  *
00005  * This library is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU Lesser General Public
00007  * License as published by the Free Software Foundation; either
00008  * version 2.1 of the License, or (at your option) any later version.
00009  *
00010  * This library is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Lesser General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU Lesser General Public
00016  * License along with this library. If not, see
00017  * <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include "hdmv_vm.h"
00021 
00022 #include "mobj_parse.h"
00023 #include "hdmv_insn.h"
00024 #include "../register.h"
00025 
00026 #include "../bdnav/index_parse.h"
00027 #include "util/macro.h"
00028 #include "util/strutl.h"
00029 #include "util/logging.h"
00030 #include "util/mutex.h"
00031 
00032 #include <stdlib.h>
00033 #include <string.h>
00034 #include <time.h>
00035 
00036 
00037 typedef struct {
00038     time_t   time;
00039     uint32_t mobj_id;
00040 } NV_TIMER;
00041 
00042 struct hdmv_vm_s {
00043 
00044     BD_MUTEX       mutex;
00045 
00046     /* state */
00047     uint32_t       pc;            /* program counter */
00048     BD_REGISTERS  *regs;          /* player registers */
00049     MOBJ_OBJECT   *object;        /* currently running object code */
00050 
00051     HDMV_EVENT     event[5];      /* pending events to return */
00052 
00053     NV_TIMER       nv_timer;      /* navigation timer */
00054 
00055     /* movie objects */
00056     MOBJ_OBJECTS  *movie_objects; /* disc movie objects */
00057     MOBJ_OBJECT   *ig_object;     /* current object from IG stream */
00058 
00059     /* object currently playing playlist */
00060     MOBJ_OBJECT *playing_object;
00061     int          playing_pc;
00062 
00063     /* suspended object */
00064     MOBJ_OBJECT *suspended_object;
00065     int          suspended_pc;
00066 
00067     /* disc index (used to verify CALL_TITLE/JUMP_TITLE) */
00068     INDX_ROOT   *indx;
00069 };
00070 
00071 /*
00072  * registers: PSR and GPR access
00073  */
00074 
00075 #define PSR_FLAG 0x80000000
00076 
00077 static int _is_valid_reg(uint32_t reg)
00078 {
00079     if (reg & PSR_FLAG) {
00080         if (reg & ~0x8000007f) {
00081             return 0;
00082         }
00083     }  else {
00084         if (reg & ~0x00000fff) {
00085             return 0;
00086         }
00087     }
00088     return 1;
00089 }
00090 
00091 static int _store_reg(HDMV_VM *p, uint32_t reg, uint32_t val)
00092 {
00093     if (!_is_valid_reg(reg)) {
00094         BD_DEBUG(DBG_HDMV, "_store_reg(): invalid register 0x%x\n", reg);
00095         return -1;
00096     }
00097 
00098     if (reg & PSR_FLAG) {
00099         BD_DEBUG(DBG_HDMV, "_store_reg(): storing to PSR is not allowed\n");
00100         return -1;
00101     }  else {
00102         return bd_gpr_write(p->regs, reg, val);
00103     }
00104 }
00105 
00106 static uint32_t _read_reg(HDMV_VM *p, uint32_t reg)
00107 {
00108     if (!_is_valid_reg(reg)) {
00109         BD_DEBUG(DBG_HDMV, "_read_reg(): invalid register 0x%x\n", reg);
00110         return 0;
00111     }
00112 
00113     if (reg & PSR_FLAG) {
00114         return bd_psr_read(p->regs, reg & 0x7f);
00115     } else {
00116         return bd_gpr_read(p->regs, reg);
00117     }
00118 }
00119 
00120 static uint32_t _read_setstream_regs(HDMV_VM *p, uint32_t val)
00121 {
00122     uint32_t flags = val & 0xf000f000;
00123     uint32_t reg0 = val & 0xfff;
00124     uint32_t reg1 = (val >> 16) & 0xfff;
00125 
00126     uint32_t val0 = bd_gpr_read(p->regs, reg0) & 0x0fff;
00127     uint32_t val1 = bd_gpr_read(p->regs, reg1) & 0x0fff;
00128 
00129     return flags | val0 | (val1 << 16);
00130 }
00131 
00132 static uint32_t _read_setbuttonpage_reg(HDMV_VM *p, uint32_t val)
00133 {
00134     uint32_t flags = val & 0xc0000000;
00135     uint32_t reg0  = val & 0x00000fff;
00136 
00137     uint32_t val0  = bd_gpr_read(p->regs, reg0) & 0x3fffffff;
00138 
00139     return flags | val0;
00140 }
00141 
00142 static int _store_result(HDMV_VM *p, MOBJ_CMD *cmd, uint32_t src, uint32_t dst, uint32_t src0, uint32_t dst0)
00143 {
00144     int ret = 0;
00145 
00146     /* store result to destination register(s) */
00147     if (dst != dst0) {
00148         if (cmd->insn.imm_op1) {
00149             BD_DEBUG(DBG_HDMV|DBG_CRIT, "ERROR: storing to imm ! ");
00150             return -1;
00151         }
00152         ret = _store_reg(p, cmd->dst, dst);
00153     }
00154 
00155     if (src != src0) {
00156         if (cmd->insn.imm_op1) {
00157             BD_DEBUG(DBG_HDMV|DBG_CRIT, "ERROR: storing to imm ! ");
00158             return -1;
00159         }
00160         ret += _store_reg(p, cmd->src, src);
00161     }
00162 
00163     return ret;
00164 }
00165 
00166 static uint32_t _fetch_operand(HDMV_VM *p, int setstream, int setbuttonpage, int imm, uint32_t value)
00167 {
00168     if (imm) {
00169         return value;
00170     }
00171 
00172     if (setstream) {
00173         return _read_setstream_regs(p, value);
00174 
00175     } else if (setbuttonpage) {
00176         return _read_setbuttonpage_reg(p, value);
00177 
00178     } else {
00179         return _read_reg(p, value);
00180     }
00181 }
00182 
00183 static void _fetch_operands(HDMV_VM *p, MOBJ_CMD *cmd, uint32_t *dst, uint32_t *src)
00184 {
00185     HDMV_INSN *insn = &cmd->insn;
00186 
00187     int setstream = (insn->grp     == INSN_GROUP_SET &&
00188                      insn->sub_grp == SET_SETSYSTEM  &&
00189                      (  insn->set_opt == INSN_SET_STREAM ||
00190                         insn->set_opt == INSN_SET_SEC_STREAM));
00191     int setbuttonpage = (insn->grp     == INSN_GROUP_SET &&
00192                          insn->sub_grp == SET_SETSYSTEM  &&
00193                          insn->set_opt == INSN_SET_BUTTON_PAGE);
00194 
00195     *dst = *src = 0;
00196 
00197     if (insn->op_cnt > 0) {
00198         *dst = _fetch_operand(p, setstream, setbuttonpage, insn->imm_op1, cmd->dst);
00199     }
00200 
00201     if (insn->op_cnt > 1) {
00202         *src = _fetch_operand(p, setstream, setbuttonpage, insn->imm_op2, cmd->src);
00203     }
00204 }
00205 
00206 /*
00207  * event queue
00208  */
00209 
00210 static int _get_event(HDMV_VM *p, HDMV_EVENT *ev)
00211 {
00212     if (p->event[0].event != HDMV_EVENT_NONE) {
00213         *ev = p->event[0];
00214         memmove(p->event, p->event + 1, sizeof(p->event) - sizeof(p->event[0]));
00215         return 0;
00216     }
00217 
00218     ev->event = HDMV_EVENT_NONE;
00219 
00220     return -1;
00221 }
00222 
00223 static int _queue_event(HDMV_VM *p, uint32_t event, uint32_t param)
00224 {
00225     unsigned i;
00226     for (i = 0; i < sizeof(p->event) / sizeof(p->event[0]) - 1; i++) {
00227         if (p->event[i].event == HDMV_EVENT_NONE) {
00228             p->event[i].event = event;
00229             p->event[i].param = param;
00230             return 0;
00231         }
00232     }
00233 
00234     BD_DEBUG(DBG_HDMV|DBG_CRIT, "_queue_event(%d, %d): queue overflow !\n", event, param);
00235     return -1;
00236 }
00237 
00238 /*
00239  * vm init
00240  */
00241 
00242 HDMV_VM *hdmv_vm_init(const char *disc_root, BD_REGISTERS *regs, INDX_ROOT *indx)
00243 {
00244     HDMV_VM *p = calloc(1, sizeof(HDMV_VM));
00245     char *file;
00246 
00247     /* read movie objects */
00248     file = str_printf("%s/BDMV/MovieObject.bdmv", disc_root);
00249     p->movie_objects = mobj_parse(file);
00250     X_FREE(file);
00251     if (!p->movie_objects) {
00252         X_FREE(p);
00253         return NULL;
00254     }
00255 
00256     p->regs         = regs;
00257     p->indx         = indx;
00258 
00259     bd_mutex_init(&p->mutex);
00260 
00261     return  p;
00262 }
00263 
00264 static void _free_ig_object(HDMV_VM *p)
00265 {
00266     if (p->ig_object) {
00267         X_FREE(p->ig_object->cmds);
00268         X_FREE(p->ig_object);
00269     }
00270 }
00271 
00272 void hdmv_vm_free(HDMV_VM **p)
00273 {
00274     if (p && *p) {
00275 
00276         bd_mutex_destroy(&(*p)->mutex);
00277 
00278         mobj_free(&(*p)->movie_objects);
00279 
00280         _free_ig_object(*p);
00281 
00282         X_FREE(*p);
00283     }
00284 }
00285 
00286 /*
00287  * suspend/resume ("function call")
00288  */
00289 
00290 static int _suspended_at_play_pl(HDMV_VM *p)
00291 {
00292     int play_pl = 0;
00293     if (p && p->suspended_object) {
00294         MOBJ_CMD  *cmd  = &p->suspended_object->cmds[p->suspended_pc];
00295         HDMV_INSN *insn = &cmd->insn;
00296         play_pl = (insn->grp     == INSN_GROUP_BRANCH &&
00297                    insn->sub_grp == BRANCH_PLAY  &&
00298                    (  insn->branch_opt == INSN_PLAY_PL ||
00299                       insn->branch_opt == INSN_PLAY_PL_PI ||
00300                       insn->branch_opt == INSN_PLAY_PL_PM));
00301     }
00302 
00303     return play_pl;
00304 }
00305 
00306 static int _suspend_for_play_pl(HDMV_VM *p)
00307 {
00308     if (p->playing_object) {
00309         BD_DEBUG(DBG_HDMV|DBG_CRIT, "_suspend_for_play_pl(): object already playing playlist !\n");
00310         return -1;
00311     }
00312 
00313     p->playing_object = p->object;
00314     p->playing_pc     = p->pc;
00315 
00316     p->object = NULL;
00317 
00318     return 0;
00319 }
00320 
00321 static int _resume_from_play_pl(HDMV_VM *p)
00322 {
00323     if (!p->playing_object) {
00324         BD_DEBUG(DBG_HDMV|DBG_CRIT, "_resume_from_play_pl(): object not playing playlist !\n");
00325         return -1;
00326     }
00327 
00328     p->object = p->playing_object;
00329     p->pc     = p->playing_pc + 1;
00330 
00331     p->playing_object = NULL;
00332 
00333     _free_ig_object(p);
00334 
00335     return 0;
00336 }
00337 
00338 static void _suspend_object(HDMV_VM *p, int psr_backup)
00339 {
00340     BD_DEBUG(DBG_HDMV, "_suspend_object()\n");
00341 
00342     if (p->suspended_object) {
00343         BD_DEBUG(DBG_HDMV, "_suspend_object: object already suspended !\n");
00344         // [execute the call, discard old suspended object (10.2.4.2.2)].
00345     }
00346 
00347     if (psr_backup) {
00348         bd_psr_save_state(p->regs);
00349     }
00350 
00351     if (p->ig_object) {
00352         if (!p->playing_object) {
00353             BD_DEBUG(DBG_HDMV|DBG_CRIT, "_suspend_object: IG object tries to suspend, no playing object !\n");
00354             return;
00355         }
00356         p->suspended_object = p->playing_object;
00357         p->suspended_pc     = p->playing_pc;
00358 
00359         p->playing_object = NULL;
00360 
00361     } else {
00362 
00363         if (p->playing_object) {
00364             BD_DEBUG(DBG_HDMV|DBG_CRIT, "_suspend_object: Movie object tries to suspend, also playing object present !\n");
00365             return;
00366         }
00367 
00368         p->suspended_object = p->object;
00369         p->suspended_pc     = p->pc;
00370 
00371     }
00372 
00373     p->object = NULL;
00374 
00375     _free_ig_object(p);
00376 }
00377 
00378 static int _resume_object(HDMV_VM *p, int psr_restore)
00379 {
00380     if (!p->suspended_object) {
00381         BD_DEBUG(DBG_HDMV|DBG_CRIT, "_resume_object: no suspended object!\n");
00382         return -1;
00383     }
00384 
00385     p->object = NULL;
00386     p->playing_object = NULL;
00387     _free_ig_object(p);
00388 
00389     if (psr_restore) {
00390         /* check if suspended in play_pl */
00391         if (_suspended_at_play_pl(p)) {
00392             BD_DEBUG(DBG_HDMV, "resuming playlist playback\n");
00393             p->playing_object = p->suspended_object;
00394             p->playing_pc     = p->suspended_pc;
00395             p->suspended_object = NULL;
00396             bd_psr_restore_state(p->regs);
00397 
00398             return 0;
00399         }
00400         bd_psr_restore_state(p->regs);
00401     }
00402 
00403     p->object = p->suspended_object;
00404     p->pc     = p->suspended_pc + 1;
00405 
00406     p->suspended_object = NULL;
00407 
00408     BD_DEBUG(DBG_HDMV, "resuming object %p at %d\n", p->object, p->pc);
00409 
00410     _queue_event(p, HDMV_EVENT_PLAY_STOP, 0);
00411 
00412     return 0;
00413 }
00414 
00415 
00416 /*
00417  * branching
00418  */
00419 
00420 static int _is_valid_title(HDMV_VM *p, int title)
00421 {
00422     if (title == 0 || title == 0xffff) {
00423         INDX_PLAY_ITEM *pi = (!title) ? &p->indx->top_menu : &p->indx->first_play;
00424 
00425         if (pi->object_type == indx_object_type_hdmv &&  pi->hdmv.id_ref == 0xffff) {
00426             /* no top menu or first play title (5.2.3.3) */
00427             return 0;
00428         }
00429         return 1;
00430     }
00431 
00432     return title > 0 && title <= p->indx->num_titles;
00433 }
00434 
00435 static int _jump_object(HDMV_VM *p, int object)
00436 {
00437     if (object < 0 || object >= p->movie_objects->num_objects) {
00438         BD_DEBUG(DBG_HDMV|DBG_CRIT, "_jump_object(): invalid object %d\n", object);
00439         return -1;
00440     }
00441 
00442     BD_DEBUG(DBG_HDMV, "_jump_object(): jumping to object %d\n", object);
00443 
00444     _queue_event(p, HDMV_EVENT_PLAY_STOP, 0);
00445 
00446     _free_ig_object(p);
00447 
00448     p->playing_object = NULL;
00449 
00450     p->pc     = 0;
00451     p->object = &p->movie_objects->objects[object];
00452 
00453     /* suspended object is not discarded */
00454 
00455     return 0;
00456 }
00457 
00458 static int _jump_title(HDMV_VM *p, int title)
00459 {
00460     if (_is_valid_title(p, title)) {
00461         BD_DEBUG(DBG_HDMV, "_jump_title(%d)\n", title);
00462 
00463         /* discard suspended object */
00464         p->suspended_object = NULL;
00465         p->playing_object = NULL;
00466         bd_psr_reset_backup_registers(p->regs);
00467 
00468         _queue_event(p, HDMV_EVENT_TITLE, title);
00469         return 0;
00470     }
00471 
00472     BD_DEBUG(DBG_HDMV|DBG_CRIT, "_jump_title(%d): invalid title number\n", title);
00473 
00474     return -1;
00475 }
00476 
00477 static int _call_object(HDMV_VM *p, int object)
00478 {
00479     BD_DEBUG(DBG_HDMV, "_call_object(%d)\n", object);
00480 
00481     _queue_event(p, HDMV_EVENT_PLAY_STOP, 0);
00482     _suspend_object(p, 1);
00483 
00484     return _jump_object(p, object);
00485 }
00486 
00487 static int _call_title(HDMV_VM *p, int title)
00488 {
00489     if (_is_valid_title(p, title)) {
00490         BD_DEBUG(DBG_HDMV, "_call_title(%d)\n", title);
00491 
00492         _suspend_object(p, 1);
00493 
00494         _queue_event(p, HDMV_EVENT_TITLE, title);
00495 
00496         return 0;
00497     }
00498 
00499     BD_DEBUG(DBG_HDMV|DBG_CRIT, "_call_title(%d): invalid title number\n", title);
00500 
00501     return -1;
00502 }
00503 
00504 /*
00505  * playback control
00506  */
00507 
00508 static int _play_at(HDMV_VM *p, int playlist, int playitem, int playmark)
00509 {
00510     if (p->ig_object && playlist >= 0) {
00511         BD_DEBUG(DBG_HDMV, "play_at(list %d, item %d, mark %d): "
00512               "playlist change not allowed in interactive composition\n",
00513               playlist, playitem, playmark);
00514         return -1;
00515     }
00516 
00517     if (!p->ig_object && playlist < 0) {
00518         BD_DEBUG(DBG_HDMV, "play_at(list %d, item %d, mark %d): "
00519               "playlist not given in movie object (link commands not allowed)\n",
00520               playlist, playitem, playmark);
00521         return -1;
00522     }
00523 
00524     BD_DEBUG(DBG_HDMV, "play_at(list %d, item %d, mark %d)\n",
00525           playlist, playitem, playmark);
00526 
00527     if (playlist >= 0) {
00528         _queue_event(p, HDMV_EVENT_PLAY_PL, playlist);
00529         _suspend_for_play_pl(p);
00530     }
00531 
00532     if (playitem >= 0) {
00533         _queue_event(p, HDMV_EVENT_PLAY_PI, playitem);
00534     }
00535 
00536     if (playmark >= 0) {
00537         _queue_event(p, HDMV_EVENT_PLAY_PM, playmark);
00538     }
00539 
00540     return 0;
00541 }
00542 
00543 static int _play_stop(HDMV_VM *p)
00544 {
00545     if (!p->ig_object) {
00546         BD_DEBUG(DBG_HDMV, "_play_stop() not allowed in movie object\n");
00547         return -1;
00548     }
00549 
00550     BD_DEBUG(DBG_HDMV, "_play_stop()\n");
00551     _queue_event(p, HDMV_EVENT_PLAY_STOP, 0);
00552 
00553     return 0;
00554 }
00555 
00556 /*
00557  * SET/SYSTEM setstream
00558  */
00559 
00560 static void _set_stream(HDMV_VM *p, uint32_t dst, uint32_t src)
00561 {
00562     BD_DEBUG(DBG_HDMV, "_set_stream(0x%x, 0x%x)\n", dst, src);
00563 
00564     /* primary audio stream */
00565     if (dst & 0x80000000) {
00566         bd_psr_write(p->regs, PSR_PRIMARY_AUDIO_ID, (dst >> 16) & 0xfff);
00567     }
00568 
00569     /* IG stream */
00570     if (src & 0x80000000) {
00571         bd_psr_write(p->regs, PSR_IG_STREAM_ID, (src >> 16) & 0xff);
00572     }
00573 
00574     /* angle number */
00575     if (src & 0x8000) {
00576         bd_psr_write(p->regs, PSR_ANGLE_NUMBER, src & 0xff);
00577     }
00578 
00579     /* PSR2 */
00580 
00581     bd_psr_lock(p->regs);
00582 
00583     uint32_t psr2 = bd_psr_read(p->regs, PSR_PG_STREAM);
00584 
00585     /* PG TextST stream number */
00586     if (dst & 0x8000) {
00587         uint32_t text_st_num = dst & 0xfff;
00588         psr2 = text_st_num | (psr2 & 0xfffff000);
00589     }
00590 
00591     /* Update PG TextST stream display flag */
00592     uint32_t disp_s_flag = (dst & 0x4000) << 17;
00593     psr2 = disp_s_flag | (psr2 & 0x7fffffff);
00594 
00595     bd_psr_write(p->regs, PSR_PG_STREAM, psr2);
00596 
00597     bd_psr_unlock(p->regs);
00598 }
00599 
00600 static void _set_sec_stream(HDMV_VM *p, uint32_t dst, uint32_t src)
00601 {
00602     BD_DEBUG(DBG_HDMV, "_set_sec_stream(0x%x, 0x%x)\n", dst, src);
00603 
00604     uint32_t disp_v_flag   = (dst >> 30) & 1;
00605     uint32_t disp_a_flag   = (src >> 30) & 1;
00606     uint32_t text_st_flags = (src >> 13) & 3;
00607 
00608     /* PSR14 */
00609 
00610     bd_psr_lock(p->regs);
00611 
00612     uint32_t psr14 = bd_psr_read(p->regs, PSR_SECONDARY_AUDIO_VIDEO);
00613 
00614     /* secondary video */
00615     if (dst & 0x80000000) {
00616       uint32_t sec_video = dst & 0xff;
00617       psr14 = (sec_video << 8) | (psr14 & 0xffff00ff);
00618     }
00619 
00620     /* secondary video size */
00621     if (dst & 0x00800000) {
00622       uint32_t video_size = (dst >> 16) & 0xf;
00623       psr14 = (video_size << 24) | (psr14 & 0xf0ffffff);
00624     }
00625 
00626     /* secondary audio */
00627     if (src & 0x80000000) {
00628       uint32_t sec_audio = (src >> 16) & 0xff;
00629       psr14 = sec_audio | (psr14 & 0xffffff00);
00630     }
00631 
00632     psr14 = (disp_v_flag << 31) | (psr14 & 0x7fffffff);
00633     psr14 = (disp_a_flag << 30) | (psr14 & 0xbfffffff);
00634 
00635     bd_psr_write(p->regs, PSR_SECONDARY_AUDIO_VIDEO, psr14);
00636 
00637     /* PSR2 */
00638 
00639     uint32_t psr2  = bd_psr_read(p->regs, PSR_PG_STREAM);
00640 
00641     /* PiP PG TextST stream */
00642     if (src & 0x8000) {
00643         uint32_t stream = src & 0xfff;
00644         psr2 = (stream << 16) | (psr2 & 0xf000ffff);
00645     }
00646 
00647     psr2 = (text_st_flags << 30) | (psr2 & 0x3fffffff);
00648 
00649     bd_psr_write(p->regs, PSR_PG_STREAM, psr2);
00650 
00651     bd_psr_unlock(p->regs);
00652 }
00653 
00654 /*
00655  * SET/SYSTEM navigation control
00656  */
00657 
00658 static void _set_button_page(HDMV_VM *p, uint32_t dst, uint32_t src)
00659 {
00660     if (p->ig_object) {
00661         uint32_t param;
00662         param =  (src & 0xc0000000) |        /* page and effects flags */
00663                 ((dst & 0x80000000) >> 2) |  /* button flag */
00664                 ((src & 0x000000ff) << 16) | /* page id */
00665                  (dst & 0x0000ffff);         /* button id */
00666 
00667          _queue_event(p, HDMV_EVENT_SET_BUTTON_PAGE, param);
00668 
00669          /* terminate */
00670          p->pc = 1 << 17;
00671 
00672         return;
00673     }
00674 
00675     /* selected button */
00676     if (dst & 0x80000000) {
00677         bd_psr_write(p->regs, PSR_SELECTED_BUTTON_ID, dst & 0xffff);
00678     }
00679 
00680     /* active page */
00681     if (src & 0x80000000) {
00682         bd_psr_write(p->regs, PSR_MENU_PAGE_ID, src & 0xff);
00683     }
00684 }
00685 
00686 static void _enable_button(HDMV_VM *p, uint32_t dst, int enable)
00687 {
00688     /* not valid in movie objects */
00689     if (p->ig_object) {
00690         if (enable) {
00691             _queue_event(p, HDMV_EVENT_ENABLE_BUTTON,  dst);
00692         } else {
00693             _queue_event(p, HDMV_EVENT_DISABLE_BUTTON, dst);
00694         }
00695     }
00696 }
00697 
00698 static void _set_still_mode(HDMV_VM *p, int enable)
00699 {
00700     /* not valid in movie objects */
00701     if (p->ig_object) {
00702         _queue_event(p, HDMV_EVENT_STILL, enable);
00703     }
00704 }
00705 
00706 static void _popup_off(HDMV_VM *p)
00707 {
00708     /* not valid in movie objects */
00709     if (p->ig_object) {
00710         _queue_event(p, HDMV_EVENT_POPUP_OFF, 1);
00711     }
00712 }
00713 
00714 /*
00715  * navigation timer
00716  */
00717 
00718 static void _set_nv_timer(HDMV_VM *p, uint32_t dst, uint32_t src)
00719 {
00720   uint32_t mobj_id = dst & 0xffff;
00721   uint32_t timeout = src & 0xffff;
00722 
00723   if (!timeout) {
00724     /* cancel timer */
00725     p->nv_timer.time = 0;
00726 
00727     bd_psr_write(p->regs, PSR_NAV_TIMER, 0);
00728 
00729     return;
00730   }
00731 
00732   /* validate params */
00733   if (mobj_id >= p->movie_objects->num_objects) {
00734       BD_DEBUG(DBG_HDMV|DBG_CRIT, "_set_nv_timer(): invalid object id (%d) !\n", mobj_id);
00735       return;
00736   }
00737   if (timeout > 300) {
00738       BD_DEBUG(DBG_HDMV|DBG_CRIT, "_set_nv_timer(): invalid timeout (%d) !\n", timeout);
00739       return;
00740   }
00741 
00742   /* set expiration time */
00743   p->nv_timer.time = time(NULL);
00744   p->nv_timer.time += timeout;
00745 
00746   p->nv_timer.mobj_id = mobj_id;
00747 
00748   bd_psr_write(p->regs, PSR_NAV_TIMER, timeout);
00749 }
00750 
00751 /* Unused function.
00752  * Commenting out to disable "‘_check_nv_timer’ defined but not used" warning
00753 static int _check_nv_timer(HDMV_VM *p)
00754 {
00755     if (p->nv_timer.time > 0) {
00756         time_t now = time(NULL);
00757 
00758         if (now >= p->nv_timer.time) {
00759             BD_DEBUG(DBG_HDMV, "navigation timer expired, jumping to object %d\n", p->nv_timer.mobj_id);
00760 
00761             bd_psr_write(p->regs, PSR_NAV_TIMER, 0);
00762 
00763             p->nv_timer.time = 0;
00764             _jump_object(p, p->nv_timer.mobj_id);
00765 
00766             return 0;
00767         }
00768 
00769         bd_psr_write(p->regs, PSR_NAV_TIMER, (p->nv_timer.time - now));
00770     }
00771 
00772     return -1;
00773 }
00774 */
00775 
00776 /*
00777  * trace
00778  */
00779 
00780 static void _hdmv_trace_cmd(int pc, MOBJ_CMD *cmd)
00781 {
00782     if (bd_get_debug_mask() & DBG_HDMV) {
00783         char buf[384], *dst = buf;
00784 
00785         dst += sprintf(dst, "%04d:  ", pc);
00786 
00787         dst += mobj_sprint_cmd(dst, cmd);
00788 
00789         BD_DEBUG(DBG_HDMV, "%s\n", buf);
00790     }
00791 }
00792 
00793 static void _hdmv_trace_res(uint32_t new_src, uint32_t new_dst, uint32_t orig_src, uint32_t orig_dst)
00794 {
00795     if (bd_get_debug_mask() & DBG_HDMV) {
00796 
00797         if (new_dst != orig_dst || new_src != orig_src) {
00798             char buf[384], *dst = buf;
00799 
00800             dst += sprintf(dst, "    :  [");
00801             if (new_dst != orig_dst) {
00802                 dst += sprintf(dst, " dst 0x%x <== 0x%x ", orig_dst, new_dst);
00803             }
00804             if (new_src != orig_src) {
00805                 dst += sprintf(dst, " src 0x%x <== 0x%x ", orig_src, new_src);
00806             }
00807             dst += sprintf(dst, "]");
00808 
00809             BD_DEBUG(DBG_HDMV, "%s\n", buf);
00810         }
00811     }
00812 }
00813 
00814 /*
00815  * interpreter
00816  */
00817 
00818 /*
00819  * tools
00820  */
00821 
00822 #define SWAP_u32(a, b) do { uint32_t tmp = a; a = b; b = tmp; } while(0)
00823 
00824 static inline uint32_t RAND_u32(uint32_t range)
00825 {
00826   return range > 0 ? rand() % range + 1 : 0;
00827 }
00828 
00829 static inline uint32_t ADD_u32(uint32_t a, uint32_t b)
00830 {
00831   /* overflow -> saturate */
00832   uint64_t result = (uint64_t)a + b;
00833   return result < 0xffffffff ? result : 0xffffffff;
00834 }
00835 
00836 static inline uint32_t MUL_u32(uint32_t a, uint32_t b)
00837 {
00838   /* overflow -> saturate */
00839   uint64_t result = (uint64_t)a * b;
00840   return result < 0xffffffff ? result : 0xffffffff;
00841 }
00842 
00843 /*
00844  * _hdmv_step()
00845  *  - execute next instruction from current program
00846  */
00847 static int _hdmv_step(HDMV_VM *p)
00848 {
00849     MOBJ_CMD  *cmd  = &p->object->cmds[p->pc];
00850     HDMV_INSN *insn = &cmd->insn;
00851     uint32_t   src  = 0;
00852     uint32_t   dst  = 0;
00853     int        inc_pc = 1;
00854 
00855     /* fetch operand values */
00856     _fetch_operands(p, cmd, &dst, &src);
00857 
00858     /* trace */
00859     _hdmv_trace_cmd(p->pc, cmd);
00860 
00861     /* execute */
00862     switch (insn->grp) {
00863         case INSN_GROUP_BRANCH:
00864             switch (insn->sub_grp) {
00865                 case BRANCH_GOTO:
00866                     if (insn->op_cnt > 1) {
00867                         BD_DEBUG(DBG_HDMV|DBG_CRIT, "[too many operands in BRANCH/GOTO opcode 0x%08x] ", *(uint32_t*)insn);
00868                     }
00869                     switch (insn->branch_opt) {
00870                         case INSN_NOP:                      break;
00871                         case INSN_GOTO:  p->pc   = dst - 1; break;
00872                         case INSN_BREAK: p->pc   = 1 << 17; break;
00873                         default:
00874                             BD_DEBUG(DBG_HDMV|DBG_CRIT, "[unknown BRANCH/GOTO option in opcode 0x%08x] ", *(uint32_t*)insn);
00875                             break;
00876                     }
00877                     break;
00878                 case BRANCH_JUMP:
00879                     if (insn->op_cnt > 1) {
00880                         BD_DEBUG(DBG_HDMV|DBG_CRIT, "[too many operands in BRANCH/JUMP opcode 0x%08x] ", *(uint32_t*)insn);
00881                     }
00882                     switch (insn->branch_opt) {
00883                         case INSN_JUMP_TITLE:  _jump_title(p, dst); break;
00884                         case INSN_CALL_TITLE:  _call_title(p, dst); break;
00885                         case INSN_RESUME:      _resume_object(p, 1);   break;
00886                         case INSN_JUMP_OBJECT: if (!_jump_object(p, dst)) { inc_pc = 0; } break;
00887                         case INSN_CALL_OBJECT: if (!_call_object(p, dst)) { inc_pc = 0; } break;
00888                         default:
00889                             BD_DEBUG(DBG_HDMV|DBG_CRIT, "[unknown BRANCH/JUMP option in opcode 0x%08x] ", *(uint32_t*)insn);
00890                             break;
00891                     }
00892                     break;
00893                 case BRANCH_PLAY:
00894                     switch (insn->branch_opt) {
00895                         case INSN_PLAY_PL:      _play_at(p, dst,  -1,  -1); break;
00896                         case INSN_PLAY_PL_PI:   _play_at(p, dst, src,  -1); break;
00897                         case INSN_PLAY_PL_PM:   _play_at(p, dst,  -1, src); break;
00898                         case INSN_TERMINATE_PL: _play_stop(p);              break;
00899                         case INSN_LINK_PI:      _play_at(p,  -1, dst,  -1); break;
00900                         case INSN_LINK_MK:      _play_at(p,  -1,  -1, dst); break;
00901                         default:
00902                             BD_DEBUG(DBG_HDMV|DBG_CRIT, "[unknown BRANCH/PLAY option in opcode 0x%08x] ", *(uint32_t*)insn);
00903                             break;
00904                     }
00905                     break;
00906 
00907                 default:
00908                     BD_DEBUG(DBG_HDMV|DBG_CRIT, "[unknown BRANCH subgroup in opcode 0x%08x] ", *(uint32_t*)insn);
00909                     break;
00910             }
00911             break; /* INSN_GROUP_BRANCH */
00912 
00913         case INSN_GROUP_CMP:
00914             if (insn->op_cnt < 2) {
00915                 BD_DEBUG(DBG_HDMV|DBG_CRIT, "missing operand in BRANCH/JUMP opcode 0x%08x] ", *(uint32_t*)insn);
00916             }
00917             switch (insn->cmp_opt) {
00918                 case INSN_BC: p->pc += !!(dst & ~src); break;
00919                 case INSN_EQ: p->pc += !(dst == src); break;
00920                 case INSN_NE: p->pc += !(dst != src); break;
00921                 case INSN_GE: p->pc += !(dst >= src); break;
00922                 case INSN_GT: p->pc += !(dst >  src); break;
00923                 case INSN_LE: p->pc += !(dst <= src); break;
00924                 case INSN_LT: p->pc += !(dst <  src); break;
00925                 default:
00926                     BD_DEBUG(DBG_HDMV|DBG_CRIT, "[unknown COMPARE option in opcode 0x%08x] ", *(uint32_t*)insn);
00927                     break;
00928             }
00929             break; /* INSN_GROUP_CMP */
00930 
00931         case INSN_GROUP_SET:
00932             switch (insn->sub_grp) {
00933                 case SET_SET: {
00934                     uint32_t src0 = src;
00935                     uint32_t dst0 = dst;
00936 
00937                     if (insn->op_cnt < 2) {
00938                         BD_DEBUG(DBG_HDMV|DBG_CRIT, "missing operand in SET/SET opcode 0x%08x] ", *(uint32_t*)insn);
00939                     }
00940                     switch (insn->set_opt) {
00941                         case INSN_MOVE:   dst  = src;         break;
00942                         case INSN_SWAP:   SWAP_u32(src, dst);   break;
00943                         case INSN_SUB:    dst  = dst > src ? dst - src :          0; break;
00944                         case INSN_DIV:    dst  = src > 0   ? dst / src : 0xffffffff; break;
00945                         case INSN_MOD:    dst  = src > 0   ? dst % src : 0xffffffff; break;
00946                         case INSN_ADD:    dst  = ADD_u32(src, dst);  break;
00947                         case INSN_MUL:    dst  = MUL_u32(dst, src);  break;
00948                         case INSN_RND:    dst  = RAND_u32(src);      break;
00949                         case INSN_AND:    dst &= src;         break;
00950                         case INSN_OR:     dst |= src;         break;
00951                         case INSN_XOR:    dst ^= src;         break;
00952                         case INSN_BITSET: dst |=  (1 << src); break;
00953                         case INSN_BITCLR: dst &= ~(1 << src); break;
00954                         case INSN_SHL:    dst <<= src;        break;
00955                         case INSN_SHR:    dst >>= src;        break;
00956                         default:
00957                             BD_DEBUG(DBG_HDMV|DBG_CRIT, "[unknown SET option in opcode 0x%08x] ", *(uint32_t*)insn);
00958                             break;
00959                     }
00960 
00961                     /* store result(s) */
00962                     if (dst != dst0 || src != src0) {
00963 
00964                         _hdmv_trace_res(src, dst, src0, dst0);
00965 
00966                         _store_result(p, cmd, src, dst, src0, dst0);
00967                     }
00968                     break;
00969                 }
00970                 case SET_SETSYSTEM:
00971                     switch (insn->set_opt) {
00972                         case INSN_SET_STREAM:      _set_stream     (p, dst, src); break;
00973                         case INSN_SET_SEC_STREAM:  _set_sec_stream (p, dst, src); break;
00974                         case INSN_SET_NV_TIMER:    _set_nv_timer   (p, dst, src); break;
00975                         case INSN_SET_BUTTON_PAGE: _set_button_page(p, dst, src); break;
00976                         case INSN_ENABLE_BUTTON:   _enable_button  (p, dst,   1); break;
00977                         case INSN_DISABLE_BUTTON:  _enable_button  (p, dst,   0); break;
00978                         case INSN_POPUP_OFF:       _popup_off      (p);           break;
00979                         case INSN_STILL_ON:        _set_still_mode (p,   1);      break;
00980                         case INSN_STILL_OFF:       _set_still_mode (p,   0);      break;
00981                         default:
00982                             BD_DEBUG(DBG_HDMV|DBG_CRIT, "[unknown SETSYSTEM option in opcode 0x%08x] ", *(uint32_t*)insn);
00983                             break;
00984                     }
00985                     break;
00986                 default:
00987                     BD_DEBUG(DBG_HDMV|DBG_CRIT, "[unknown SET subgroup in opcode 0x%08x] ", *(uint32_t*)insn);
00988                     break;
00989             }
00990             break; /* INSN_GROUP_SET */
00991 
00992         default:
00993             BD_DEBUG(DBG_HDMV|DBG_CRIT, "[unknown group in opcode 0x%08x] ", *(uint32_t*)insn);
00994             break;
00995     }
00996 
00997     /* inc program counter to next instruction */
00998     p->pc += inc_pc;
00999 
01000     return 0;
01001 }
01002 
01003 /*
01004  * interface
01005  */
01006 
01007 int hdmv_vm_select_object(HDMV_VM *p, int object)
01008 {
01009     int result;
01010     bd_mutex_lock(&p->mutex);
01011 
01012     result = _jump_object(p, object);
01013 
01014     bd_mutex_unlock(&p->mutex);
01015     return result;
01016 }
01017 
01018 int hdmv_vm_set_object(HDMV_VM *p, int num_nav_cmds, void *nav_cmds)
01019 {
01020     int result = -1;
01021     bd_mutex_lock(&p->mutex);
01022 
01023     p->object = NULL;
01024 
01025     _free_ig_object(p);
01026 
01027     if (nav_cmds && num_nav_cmds > 0) {
01028         MOBJ_OBJECT *ig_object = calloc(1, sizeof(MOBJ_OBJECT));
01029         ig_object->num_cmds = num_nav_cmds;
01030         ig_object->cmds     = calloc(num_nav_cmds, sizeof(MOBJ_CMD));
01031         memcpy(ig_object->cmds, nav_cmds, num_nav_cmds * sizeof(MOBJ_CMD));
01032 
01033         p->pc        = 0;
01034         p->ig_object = ig_object;
01035         p->object    = ig_object;
01036 
01037         result = 0;
01038     }
01039 
01040     bd_mutex_unlock(&p->mutex);
01041 
01042     return result;
01043 }
01044 
01045 int hdmv_vm_get_event(HDMV_VM *p, HDMV_EVENT *ev)
01046 {
01047     int result;
01048     bd_mutex_lock(&p->mutex);
01049 
01050     result = _get_event(p, ev);
01051 
01052     bd_mutex_unlock(&p->mutex);
01053     return result;
01054 }
01055 
01056 int hdmv_vm_running(HDMV_VM *p)
01057 {
01058     int result;
01059     bd_mutex_lock(&p->mutex);
01060 
01061     result = !!p->object;
01062 
01063     bd_mutex_unlock(&p->mutex);
01064     return result;
01065 }
01066 
01067 uint32_t hdmv_vm_get_uo_mask(HDMV_VM *p)
01068 {
01069     uint32_t     mask = 0;
01070     MOBJ_OBJECT *o    = NULL;
01071 
01072     bd_mutex_lock(&p->mutex);
01073 
01074     if ((o = p->object ? p->object : (p->playing_object ? p->playing_object : p->suspended_object))) {
01075         mask |= o->menu_call_mask;
01076         mask |= o->title_search_mask << 1;
01077     }
01078 
01079     bd_mutex_unlock(&p->mutex);
01080     return mask;
01081 }
01082 
01083 int hdmv_vm_resume(HDMV_VM *p)
01084 {
01085     int result;
01086     bd_mutex_lock(&p->mutex);
01087 
01088     result = _resume_from_play_pl(p);
01089 
01090     bd_mutex_unlock(&p->mutex);
01091     return result;
01092 }
01093 
01094 int hdmv_vm_suspend_pl(HDMV_VM *p)
01095 {
01096     int result = -1;
01097     bd_mutex_lock(&p->mutex);
01098 
01099     if (p->object || p->ig_object) {
01100         BD_DEBUG(DBG_HDMV, "hdmv_vm_suspend_pl(): HDMV VM is still running\n");
01101 
01102     } else if (!p->playing_object) {
01103         BD_DEBUG(DBG_HDMV, "hdmv_vm_suspend_pl(): No playing object\n");
01104 
01105     } else if (!p->playing_object->resume_intention_flag) {
01106         BD_DEBUG(DBG_HDMV, "hdmv_vm_suspend_pl(): no resume intention flag\n");
01107 
01108         p->playing_object = NULL;
01109         result = 0;
01110 
01111     } else {
01112         p->suspended_object = p->playing_object;
01113         p->suspended_pc     = p->playing_pc;
01114 
01115         p->playing_object = NULL;
01116 
01117         bd_psr_save_state(p->regs);
01118         result = 0;
01119     }
01120 
01121     bd_mutex_unlock(&p->mutex);
01122     return result;
01123 }
01124 
01125 /* terminate program after MAX_LOOP instructions */
01126 #define MAX_LOOP 1000000
01127 
01128 static int _vm_run(HDMV_VM *p, HDMV_EVENT *ev)
01129 {
01130     int max_loop = MAX_LOOP;
01131 
01132     /* pending events ? */
01133     if (!_get_event(p, ev)) {
01134         return 0;
01135     }
01136 
01137     /* valid program ? */
01138     if (!p->object) {
01139         BD_DEBUG(DBG_HDMV|DBG_CRIT, "hdmv_vm_run(): no object selected\n");
01140         return -1;
01141     }
01142 
01143     while (--max_loop > 0) {
01144 
01145         /* suspended ? */
01146         if (!p->object) {
01147             BD_DEBUG(DBG_HDMV, "hdmv_vm_run(): object suspended\n");
01148             _get_event(p, ev);
01149             return 0;
01150         }
01151 
01152         /* terminated ? */
01153         if (p->pc >= p->object->num_cmds) {
01154             BD_DEBUG(DBG_HDMV, "terminated with PC=%d\n", p->pc);
01155             p->object = NULL;
01156             ev->event = HDMV_EVENT_END;
01157 
01158             if (p->ig_object) {
01159                 ev->event = HDMV_EVENT_IG_END;
01160                 _free_ig_object(p);
01161             }
01162 
01163             return 0;
01164         }
01165 
01166         /* next instruction */
01167         if (_hdmv_step(p) < 0) {
01168             p->object = NULL;
01169             return -1;
01170         }
01171 
01172         /* events ? */
01173         if (!_get_event(p, ev)) {
01174             return 0;
01175         }
01176     }
01177 
01178     BD_DEBUG(DBG_HDMV|DBG_CRIT, "hdmv_vm: infinite program ? terminated after %d instructions.\n", MAX_LOOP);
01179     p->object = NULL;
01180     return -1;
01181 }
01182 
01183 int hdmv_vm_run(HDMV_VM *p, HDMV_EVENT *ev)
01184 {
01185     int result;
01186     bd_mutex_lock(&p->mutex);
01187 
01188     result = _vm_run(p, ev);
01189 
01190     bd_mutex_unlock(&p->mutex);
01191     return result;
01192 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends