00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00047 uint32_t pc;
00048 BD_REGISTERS *regs;
00049 MOBJ_OBJECT *object;
00050
00051 HDMV_EVENT event[5];
00052
00053 NV_TIMER nv_timer;
00054
00055
00056 MOBJ_OBJECTS *movie_objects;
00057 MOBJ_OBJECT *ig_object;
00058
00059
00060 MOBJ_OBJECT *playing_object;
00061 int playing_pc;
00062
00063
00064 MOBJ_OBJECT *suspended_object;
00065 int suspended_pc;
00066
00067
00068 INDX_ROOT *indx;
00069 };
00070
00071
00072
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
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
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
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
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
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
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
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
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
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
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
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
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
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
00565 if (dst & 0x80000000) {
00566 bd_psr_write(p->regs, PSR_PRIMARY_AUDIO_ID, (dst >> 16) & 0xfff);
00567 }
00568
00569
00570 if (src & 0x80000000) {
00571 bd_psr_write(p->regs, PSR_IG_STREAM_ID, (src >> 16) & 0xff);
00572 }
00573
00574
00575 if (src & 0x8000) {
00576 bd_psr_write(p->regs, PSR_ANGLE_NUMBER, src & 0xff);
00577 }
00578
00579
00580
00581 bd_psr_lock(p->regs);
00582
00583 uint32_t psr2 = bd_psr_read(p->regs, PSR_PG_STREAM);
00584
00585
00586 if (dst & 0x8000) {
00587 uint32_t text_st_num = dst & 0xfff;
00588 psr2 = text_st_num | (psr2 & 0xfffff000);
00589 }
00590
00591
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
00609
00610 bd_psr_lock(p->regs);
00611
00612 uint32_t psr14 = bd_psr_read(p->regs, PSR_SECONDARY_AUDIO_VIDEO);
00613
00614
00615 if (dst & 0x80000000) {
00616 uint32_t sec_video = dst & 0xff;
00617 psr14 = (sec_video << 8) | (psr14 & 0xffff00ff);
00618 }
00619
00620
00621 if (dst & 0x00800000) {
00622 uint32_t video_size = (dst >> 16) & 0xf;
00623 psr14 = (video_size << 24) | (psr14 & 0xf0ffffff);
00624 }
00625
00626
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
00638
00639 uint32_t psr2 = bd_psr_read(p->regs, PSR_PG_STREAM);
00640
00641
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
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) |
00663 ((dst & 0x80000000) >> 2) |
00664 ((src & 0x000000ff) << 16) |
00665 (dst & 0x0000ffff);
00666
00667 _queue_event(p, HDMV_EVENT_SET_BUTTON_PAGE, param);
00668
00669
00670 p->pc = 1 << 17;
00671
00672 return;
00673 }
00674
00675
00676 if (dst & 0x80000000) {
00677 bd_psr_write(p->regs, PSR_SELECTED_BUTTON_ID, dst & 0xffff);
00678 }
00679
00680
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
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
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
00709 if (p->ig_object) {
00710 _queue_event(p, HDMV_EVENT_POPUP_OFF, 1);
00711 }
00712 }
00713
00714
00715
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
00725 p->nv_timer.time = 0;
00726
00727 bd_psr_write(p->regs, PSR_NAV_TIMER, 0);
00728
00729 return;
00730 }
00731
00732
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
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
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
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
00816
00817
00818
00819
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
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
00839 uint64_t result = (uint64_t)a * b;
00840 return result < 0xffffffff ? result : 0xffffffff;
00841 }
00842
00843
00844
00845
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
00856 _fetch_operands(p, cmd, &dst, &src);
00857
00858
00859 _hdmv_trace_cmd(p->pc, cmd);
00860
00861
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;
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;
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
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;
00991
00992 default:
00993 BD_DEBUG(DBG_HDMV|DBG_CRIT, "[unknown group in opcode 0x%08x] ", *(uint32_t*)insn);
00994 break;
00995 }
00996
00997
00998 p->pc += inc_pc;
00999
01000 return 0;
01001 }
01002
01003
01004
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
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
01133 if (!_get_event(p, ev)) {
01134 return 0;
01135 }
01136
01137
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
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
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
01167 if (_hdmv_step(p) < 0) {
01168 p->object = NULL;
01169 return -1;
01170 }
01171
01172
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 }