|
MythTV
0.26-pre
|
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 }
1.7.6.1