Blame src/libbluray/hdmv/hdmv_vm.c

Packit 5e46da
/*
Packit 5e46da
 * This file is part of libbluray
Packit 5e46da
 * Copyright (C) 2010-2017  Petri Hintukainen <phintuka@users.sourceforge.net>
Packit 5e46da
 *
Packit 5e46da
 * This library is free software; you can redistribute it and/or
Packit 5e46da
 * modify it under the terms of the GNU Lesser General Public
Packit 5e46da
 * License as published by the Free Software Foundation; either
Packit 5e46da
 * version 2.1 of the License, or (at your option) any later version.
Packit 5e46da
 *
Packit 5e46da
 * This library is distributed in the hope that it will be useful,
Packit 5e46da
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 5e46da
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 5e46da
 * Lesser General Public License for more details.
Packit 5e46da
 *
Packit 5e46da
 * You should have received a copy of the GNU Lesser General Public
Packit 5e46da
 * License along with this library. If not, see
Packit 5e46da
 * <http://www.gnu.org/licenses/>.
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#if HAVE_CONFIG_H
Packit 5e46da
#include "config.h"
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
#include "hdmv_vm.h"
Packit 5e46da
Packit 5e46da
#include "mobj_data.h"
Packit 5e46da
#include "hdmv_insn.h"
Packit 5e46da
#include "mobj_parse.h"
Packit 5e46da
#include "mobj_print.h"
Packit 5e46da
#include "../register.h"
Packit 5e46da
Packit 5e46da
#include "util/macro.h"
Packit 5e46da
#include "util/logging.h"
Packit 5e46da
#include "util/mutex.h"
Packit 5e46da
Packit 5e46da
#include <stdio.h>
Packit 5e46da
#include <stdlib.h>
Packit 5e46da
#include <string.h>
Packit 5e46da
#include <time.h>
Packit 5e46da
Packit 5e46da
Packit 5e46da
typedef struct {
Packit 5e46da
    time_t   time;
Packit 5e46da
    uint32_t mobj_id;
Packit 5e46da
} NV_TIMER;
Packit 5e46da
Packit 5e46da
struct hdmv_vm_s {
Packit 5e46da
Packit 5e46da
    BD_MUTEX       mutex;
Packit 5e46da
Packit 5e46da
    /* state */
Packit 5e46da
    uint32_t       pc;            /* program counter */
Packit 5e46da
    BD_REGISTERS  *regs;          /* player registers */
Packit 5e46da
    const MOBJ_OBJECT *object;    /* currently running object code */
Packit 5e46da
Packit 5e46da
    HDMV_EVENT     event[5];      /* pending events to return */
Packit 5e46da
Packit 5e46da
    NV_TIMER       nv_timer;      /* navigation timer */
Packit 5e46da
    uint64_t       rand;          /* RAND state */
Packit 5e46da
Packit 5e46da
    /* movie objects */
Packit 5e46da
    MOBJ_OBJECTS  *movie_objects; /* disc movie objects */
Packit 5e46da
    MOBJ_OBJECT   *ig_object;     /* current object from IG stream */
Packit 5e46da
Packit 5e46da
    /* object currently playing playlist */
Packit 5e46da
    const MOBJ_OBJECT *playing_object;
Packit 5e46da
    uint32_t     playing_pc;
Packit 5e46da
Packit 5e46da
    /* suspended object */
Packit 5e46da
    const MOBJ_OBJECT *suspended_object;
Packit 5e46da
    uint32_t     suspended_pc;
Packit 5e46da
Packit 5e46da
    /* Available titles. Used to validate CALL_TITLE/JUMP_TITLE. */
Packit 5e46da
    uint8_t  have_top_menu;
Packit 5e46da
    uint8_t  have_first_play;
Packit 5e46da
    uint16_t num_titles;
Packit 5e46da
};
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * save / restore VM state
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _save_state(HDMV_VM *p, uint32_t *s)
Packit 5e46da
{
Packit 5e46da
    memset(s, 0, sizeof(*s) * HDMV_STATE_SIZE);
Packit 5e46da
Packit 5e46da
    if (p->ig_object) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV | DBG_CRIT, "_save_state() failed: button object running\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
    if (p->object) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV | DBG_CRIT, "_save_state() failed: movie object running\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
    if (p->event[0].event != HDMV_EVENT_NONE) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV | DBG_CRIT, "_save_state() failed: unprocessed events\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (p->playing_object) {
Packit 5e46da
        s[0] = (uint32_t)(p->playing_object - p->movie_objects->objects);
Packit 5e46da
        s[1] = p->playing_pc;
Packit 5e46da
    } else {
Packit 5e46da
        s[0] = (uint32_t)-1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (p->suspended_object) {
Packit 5e46da
        s[2] = (uint32_t)(p->suspended_object - p->movie_objects->objects);
Packit 5e46da
        s[3] = p->suspended_pc;
Packit 5e46da
    } else {
Packit 5e46da
        s[2] = (uint32_t)-1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* nv timer ? */
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _restore_state(HDMV_VM *p, const uint32_t *s)
Packit 5e46da
{
Packit 5e46da
    if (s[0] == (uint32_t)-1) {
Packit 5e46da
        p->playing_object = NULL;
Packit 5e46da
    } else if (s[0] >= p->movie_objects->num_objects) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV | DBG_CRIT, "_restore_state() failed: invalid playing object index\n");
Packit 5e46da
        return -1;
Packit 5e46da
    } else {
Packit 5e46da
        p->playing_object = &p->movie_objects->objects[s[0]];
Packit 5e46da
    }
Packit 5e46da
    p->playing_pc = s[1];
Packit 5e46da
Packit 5e46da
    if (s[2] == (uint32_t)-1) {
Packit 5e46da
        p->suspended_object = NULL;
Packit 5e46da
    } else if (s[2] >= p->movie_objects->num_objects) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV | DBG_CRIT, "_restore_state() failed: invalid suspended object index\n");
Packit 5e46da
        return -1;
Packit 5e46da
    } else {
Packit 5e46da
        p->suspended_object = &p->movie_objects->objects[s[2]];
Packit 5e46da
    }
Packit 5e46da
    p->suspended_pc = s[3];
Packit 5e46da
Packit 5e46da
    p->object = NULL;
Packit 5e46da
    p->ig_object = NULL;
Packit 5e46da
    memset(p->event, 0, sizeof(p->event));
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int hdmv_vm_save_state(HDMV_VM *p, uint32_t *s)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
    bd_mutex_lock(&p->mutex);
Packit 5e46da
    result = _save_state(p, s);
Packit 5e46da
    bd_mutex_unlock(&p->mutex);
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void hdmv_vm_restore_state(HDMV_VM *p, const uint32_t *s)
Packit 5e46da
{
Packit 5e46da
    bd_mutex_lock(&p->mutex);
Packit 5e46da
    _restore_state(p, s);
Packit 5e46da
    bd_mutex_unlock(&p->mutex);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * registers: PSR and GPR access
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#define PSR_FLAG 0x80000000
Packit 5e46da
Packit 5e46da
static int _is_valid_reg(uint32_t reg)
Packit 5e46da
{
Packit 5e46da
    if (reg & PSR_FLAG) {
Packit 5e46da
        if (reg & ~0x8000007f) {
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
    }  else {
Packit 5e46da
        if (reg & ~0x00000fff) {
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
    return 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _store_reg(HDMV_VM *p, uint32_t reg, uint32_t val)
Packit 5e46da
{
Packit 5e46da
    if (!_is_valid_reg(reg)) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "_store_reg(): invalid register 0x%x\n", reg);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (reg & PSR_FLAG) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "_store_reg(): storing to PSR is not allowed\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }  else {
Packit 5e46da
        return bd_gpr_write(p->regs, reg, val);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static uint32_t _read_reg(HDMV_VM *p, uint32_t reg)
Packit 5e46da
{
Packit 5e46da
    if (!_is_valid_reg(reg)) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "_read_reg(): invalid register 0x%x\n", reg);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (reg & PSR_FLAG) {
Packit 5e46da
        return bd_psr_read(p->regs, reg & 0x7f);
Packit 5e46da
    } else {
Packit 5e46da
        return bd_gpr_read(p->regs, reg);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static uint32_t _read_setstream_regs(HDMV_VM *p, uint32_t val)
Packit 5e46da
{
Packit 5e46da
    uint32_t flags = val & 0xf000f000;
Packit 5e46da
    uint32_t reg0 = val & 0xfff;
Packit 5e46da
    uint32_t reg1 = (val >> 16) & 0xfff;
Packit 5e46da
Packit 5e46da
    uint32_t val0 = bd_gpr_read(p->regs, reg0) & 0x0fff;
Packit 5e46da
    uint32_t val1 = bd_gpr_read(p->regs, reg1) & 0x0fff;
Packit 5e46da
Packit 5e46da
    return flags | val0 | (val1 << 16);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static uint32_t _read_setbuttonpage_reg(HDMV_VM *p, uint32_t val)
Packit 5e46da
{
Packit 5e46da
    uint32_t flags = val & 0xc0000000;
Packit 5e46da
    uint32_t reg0  = val & 0x00000fff;
Packit 5e46da
Packit 5e46da
    uint32_t val0  = bd_gpr_read(p->regs, reg0) & 0x3fffffff;
Packit 5e46da
Packit 5e46da
    return flags | val0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _store_result(HDMV_VM *p, MOBJ_CMD *cmd, uint32_t src, uint32_t dst, uint32_t src0, uint32_t dst0)
Packit 5e46da
{
Packit 5e46da
    int ret = 0;
Packit 5e46da
Packit 5e46da
    /* store result to destination register(s) */
Packit 5e46da
    if (dst != dst0) {
Packit 5e46da
        if (cmd->insn.imm_op1) {
Packit 5e46da
            BD_DEBUG(DBG_HDMV|DBG_CRIT, "storing to imm !\n");
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
        ret = _store_reg(p, cmd->dst, dst);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (src != src0) {
Packit 5e46da
        if (cmd->insn.imm_op1) {
Packit 5e46da
            BD_DEBUG(DBG_HDMV|DBG_CRIT, "storing to imm !\n");
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
        ret += _store_reg(p, cmd->src, src);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return ret;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static uint32_t _fetch_operand(HDMV_VM *p, int setstream, int setbuttonpage, int imm, uint32_t value)
Packit 5e46da
{
Packit 5e46da
    if (imm) {
Packit 5e46da
        return value;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (setstream) {
Packit 5e46da
        return _read_setstream_regs(p, value);
Packit 5e46da
Packit 5e46da
    } else if (setbuttonpage) {
Packit 5e46da
        return _read_setbuttonpage_reg(p, value);
Packit 5e46da
Packit 5e46da
    } else {
Packit 5e46da
        return _read_reg(p, value);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _fetch_operands(HDMV_VM *p, MOBJ_CMD *cmd, uint32_t *dst, uint32_t *src)
Packit 5e46da
{
Packit 5e46da
    HDMV_INSN *insn = &cmd->insn;
Packit 5e46da
Packit 5e46da
    int setstream = (insn->grp     == INSN_GROUP_SET &&
Packit 5e46da
                     insn->sub_grp == SET_SETSYSTEM  &&
Packit 5e46da
                     (  insn->set_opt == INSN_SET_STREAM ||
Packit 5e46da
                        insn->set_opt == INSN_SET_SEC_STREAM));
Packit 5e46da
    int setbuttonpage = (insn->grp     == INSN_GROUP_SET &&
Packit 5e46da
                         insn->sub_grp == SET_SETSYSTEM  &&
Packit 5e46da
                         insn->set_opt == INSN_SET_BUTTON_PAGE);
Packit 5e46da
Packit 5e46da
    *dst = *src = 0;
Packit 5e46da
Packit 5e46da
    if (insn->op_cnt > 0) {
Packit 5e46da
        *dst = _fetch_operand(p, setstream, setbuttonpage, insn->imm_op1, cmd->dst);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (insn->op_cnt > 1) {
Packit 5e46da
        *src = _fetch_operand(p, setstream, setbuttonpage, insn->imm_op2, cmd->src);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * event queue
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _get_event(HDMV_VM *p, HDMV_EVENT *ev)
Packit 5e46da
{
Packit 5e46da
    if (p->event[0].event != HDMV_EVENT_NONE) {
Packit 5e46da
        *ev = p->event[0];
Packit 5e46da
        memmove(p->event, p->event + 1, sizeof(p->event) - sizeof(p->event[0]));
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    ev->event = HDMV_EVENT_NONE;
Packit 5e46da
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _queue_event(HDMV_VM *p, hdmv_event_e event, uint32_t param)
Packit 5e46da
{
Packit 5e46da
    unsigned i;
Packit 5e46da
    for (i = 0; i < sizeof(p->event) / sizeof(p->event[0]) - 1; i++) {
Packit 5e46da
        if (p->event[i].event == HDMV_EVENT_NONE) {
Packit 5e46da
            p->event[i].event = event;
Packit 5e46da
            p->event[i].param = param;
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_HDMV|DBG_CRIT, "_queue_event(%d, %d): queue overflow !\n", event, param);
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * vm init
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
HDMV_VM *hdmv_vm_init(struct bd_disc *disc, BD_REGISTERS *regs,
Packit 5e46da
                      unsigned num_titles, unsigned first_play_available, unsigned top_menu_available)
Packit 5e46da
{
Packit 5e46da
    HDMV_VM *p = calloc(1, sizeof(HDMV_VM));
Packit 5e46da
Packit 5e46da
    if (!p) {
Packit 5e46da
        BD_DEBUG(DBG_CRIT, "out of memory\n");
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* read movie objects */
Packit 5e46da
    p->movie_objects = mobj_get(disc);
Packit 5e46da
    if (!p->movie_objects) {
Packit 5e46da
        X_FREE(p);
Packit 5e46da
        return NULL;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    p->regs         = regs;
Packit 5e46da
    p->num_titles      = num_titles;
Packit 5e46da
    p->have_top_menu   = top_menu_available;
Packit 5e46da
    p->have_first_play = first_play_available;
Packit 5e46da
#ifdef DEBUG
Packit 5e46da
    p->rand = 1;
Packit 5e46da
#else
Packit 5e46da
    p->rand = time(NULL);
Packit 5e46da
#endif
Packit 5e46da
Packit 5e46da
    bd_mutex_init(&p->mutex);
Packit 5e46da
Packit 5e46da
    return  p;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _free_ig_object(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    if (p->ig_object) {
Packit 5e46da
        X_FREE(p->ig_object->cmds);
Packit 5e46da
        X_FREE(p->ig_object);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
void hdmv_vm_free(HDMV_VM **p)
Packit 5e46da
{
Packit 5e46da
    if (p && *p) {
Packit 5e46da
Packit 5e46da
        bd_mutex_destroy(&(*p)->mutex);
Packit 5e46da
Packit 5e46da
        mobj_free(&(*p)->movie_objects);
Packit 5e46da
Packit 5e46da
        _free_ig_object(*p);
Packit 5e46da
Packit 5e46da
        X_FREE(*p);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * suspend/resume ("function call")
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _suspended_at_play_pl(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    int play_pl = 0;
Packit 5e46da
    if (p && p->suspended_object) {
Packit 5e46da
        MOBJ_CMD  *cmd  = &p->suspended_object->cmds[p->suspended_pc];
Packit 5e46da
        HDMV_INSN *insn = &cmd->insn;
Packit 5e46da
        play_pl = (insn->grp     == INSN_GROUP_BRANCH &&
Packit 5e46da
                   insn->sub_grp == BRANCH_PLAY  &&
Packit 5e46da
                   (  insn->branch_opt == INSN_PLAY_PL ||
Packit 5e46da
                      insn->branch_opt == INSN_PLAY_PL_PI ||
Packit 5e46da
                      insn->branch_opt == INSN_PLAY_PL_PM));
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return play_pl;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _suspend_for_play_pl(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    if (p->playing_object) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV|DBG_CRIT, "_suspend_for_play_pl(): object already playing playlist !\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    p->playing_object = p->object;
Packit 5e46da
    p->playing_pc     = p->pc;
Packit 5e46da
Packit 5e46da
    p->object = NULL;
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _resume_from_play_pl(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    if (!p->playing_object) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV|DBG_CRIT, "_resume_from_play_pl(): object not playing playlist !\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    p->object = p->playing_object;
Packit 5e46da
    p->pc     = p->playing_pc + 1;
Packit 5e46da
Packit 5e46da
    p->playing_object = NULL;
Packit 5e46da
Packit 5e46da
    _free_ig_object(p);
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _suspend_object(HDMV_VM *p, int psr_backup)
Packit 5e46da
{
Packit 5e46da
    BD_DEBUG(DBG_HDMV, "_suspend_object()\n");
Packit 5e46da
Packit 5e46da
    if (p->suspended_object) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "_suspend_object: object already suspended !\n");
Packit 5e46da
        // [execute the call, discard old suspended object (10.2.4.2.2)].
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (psr_backup) {
Packit 5e46da
        bd_psr_save_state(p->regs);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (p->ig_object) {
Packit 5e46da
        if (!p->playing_object) {
Packit 5e46da
            BD_DEBUG(DBG_HDMV|DBG_CRIT, "_suspend_object: IG object tries to suspend, no playing object !\n");
Packit 5e46da
            return;
Packit 5e46da
        }
Packit 5e46da
        p->suspended_object = p->playing_object;
Packit 5e46da
        p->suspended_pc     = p->playing_pc;
Packit 5e46da
Packit 5e46da
        p->playing_object = NULL;
Packit 5e46da
Packit 5e46da
    } else {
Packit 5e46da
Packit 5e46da
        if (p->playing_object) {
Packit 5e46da
            BD_DEBUG(DBG_HDMV|DBG_CRIT, "_suspend_object: Movie object tries to suspend, also playing object present !\n");
Packit 5e46da
            return;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        p->suspended_object = p->object;
Packit 5e46da
        p->suspended_pc     = p->pc;
Packit 5e46da
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    p->object = NULL;
Packit 5e46da
Packit 5e46da
    _free_ig_object(p);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _resume_object(HDMV_VM *p, int psr_restore)
Packit 5e46da
{
Packit 5e46da
    if (!p->suspended_object) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV|DBG_CRIT, "_resume_object: no suspended object!\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    p->object = NULL;
Packit 5e46da
    p->playing_object = NULL;
Packit 5e46da
    _free_ig_object(p);
Packit 5e46da
Packit 5e46da
    if (psr_restore) {
Packit 5e46da
        /* check if suspended in play_pl */
Packit 5e46da
        if (_suspended_at_play_pl(p)) {
Packit 5e46da
            BD_DEBUG(DBG_HDMV, "resuming playlist playback\n");
Packit 5e46da
            p->playing_object = p->suspended_object;
Packit 5e46da
            p->playing_pc     = p->suspended_pc;
Packit 5e46da
            p->suspended_object = NULL;
Packit 5e46da
            bd_psr_restore_state(p->regs);
Packit 5e46da
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
        bd_psr_restore_state(p->regs);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    p->object = p->suspended_object;
Packit 5e46da
    p->pc     = p->suspended_pc + 1;
Packit 5e46da
Packit 5e46da
    p->suspended_object = NULL;
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_HDMV, "resuming object %ld at %d\n",
Packit 5e46da
             (long)(p->object - p->movie_objects->objects),
Packit 5e46da
             p->pc);
Packit 5e46da
Packit 5e46da
    _queue_event(p, HDMV_EVENT_PLAY_STOP, 0);
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * branching
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _is_valid_title(HDMV_VM *p, uint32_t title)
Packit 5e46da
{
Packit 5e46da
    if (title == 0) {
Packit 5e46da
        return p->have_top_menu;
Packit 5e46da
    }
Packit 5e46da
    if (title == 0xffff) {
Packit 5e46da
        return p->have_first_play;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return title > 0 && title <= p->num_titles;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _jump_object(HDMV_VM *p, uint32_t object)
Packit 5e46da
{
Packit 5e46da
    if (object >= p->movie_objects->num_objects) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV|DBG_CRIT, "_jump_object(): invalid object %u\n", object);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_HDMV, "_jump_object(): jumping to object %u\n", object);
Packit 5e46da
Packit 5e46da
    _queue_event(p, HDMV_EVENT_PLAY_STOP, 0);
Packit 5e46da
Packit 5e46da
    _free_ig_object(p);
Packit 5e46da
Packit 5e46da
    p->playing_object = NULL;
Packit 5e46da
Packit 5e46da
    p->pc     = 0;
Packit 5e46da
    p->object = &p->movie_objects->objects[object];
Packit 5e46da
Packit 5e46da
    /* suspended object is not discarded */
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _jump_title(HDMV_VM *p, uint32_t title)
Packit 5e46da
{
Packit 5e46da
    if (_is_valid_title(p, title)) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "_jump_title(%u)\n", title);
Packit 5e46da
Packit 5e46da
        /* discard suspended object */
Packit 5e46da
        p->suspended_object = NULL;
Packit 5e46da
        p->playing_object = NULL;
Packit 5e46da
        bd_psr_reset_backup_registers(p->regs);
Packit 5e46da
Packit 5e46da
        _queue_event(p, HDMV_EVENT_TITLE, title);
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_HDMV|DBG_CRIT, "_jump_title(%u): invalid title number\n", title);
Packit 5e46da
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _call_object(HDMV_VM *p, uint32_t object)
Packit 5e46da
{
Packit 5e46da
    if (object >= p->movie_objects->num_objects) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV|DBG_CRIT, "_call_object(): invalid object %u\n", object);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_HDMV, "_call_object(%u)\n", object);
Packit 5e46da
Packit 5e46da
    _suspend_object(p, 1);
Packit 5e46da
Packit 5e46da
    return _jump_object(p, object);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _call_title(HDMV_VM *p, uint32_t title)
Packit 5e46da
{
Packit 5e46da
    if (_is_valid_title(p, title)) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "_call_title(%u)\n", title);
Packit 5e46da
Packit 5e46da
        _suspend_object(p, 1);
Packit 5e46da
Packit 5e46da
        _queue_event(p, HDMV_EVENT_TITLE, title);
Packit 5e46da
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_HDMV|DBG_CRIT, "_call_title(%u): invalid title number\n", title);
Packit 5e46da
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * playback control
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static int _play_at(HDMV_VM *p, int playlist, int playitem, int playmark)
Packit 5e46da
{
Packit 5e46da
    if (p->ig_object && playlist >= 0) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV | DBG_CRIT, "play_at(list %d, item %d, mark %d): "
Packit 5e46da
              "playlist change not allowed in interactive composition\n",
Packit 5e46da
              playlist, playitem, playmark);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (!p->ig_object && playlist < 0) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV | DBG_CRIT, "play_at(list %d, item %d, mark %d): "
Packit 5e46da
              "playlist not given in movie object (link commands not allowed)\n",
Packit 5e46da
              playlist, playitem, playmark);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_HDMV, "play_at(list %d, item %d, mark %d)\n",
Packit 5e46da
          playlist, playitem, playmark);
Packit 5e46da
Packit 5e46da
    if (playlist >= 0) {
Packit 5e46da
        _queue_event(p, HDMV_EVENT_PLAY_PL, playlist);
Packit 5e46da
        _suspend_for_play_pl(p);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (playitem >= 0) {
Packit 5e46da
        _queue_event(p, HDMV_EVENT_PLAY_PI, playitem);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    if (playmark >= 0) {
Packit 5e46da
        _queue_event(p, HDMV_EVENT_PLAY_PM, playmark);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _play_stop(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    if (!p->ig_object) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV | DBG_CRIT, "_play_stop() not allowed in movie object\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_HDMV, "_play_stop()\n");
Packit 5e46da
    _queue_event(p, HDMV_EVENT_PLAY_STOP, 1);
Packit 5e46da
Packit 5e46da
    /* terminate IG object. Continue executing movie object.  */
Packit 5e46da
    if (_resume_from_play_pl(p) < 0) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV|DBG_CRIT, "_play_stop(): resuming movie object failed !\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * SET/SYSTEM setstream
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _set_stream(HDMV_VM *p, uint32_t dst, uint32_t src)
Packit 5e46da
{
Packit 5e46da
    BD_DEBUG(DBG_HDMV, "_set_stream(0x%x, 0x%x)\n", dst, src);
Packit 5e46da
Packit 5e46da
    /* primary audio stream */
Packit 5e46da
    if (dst & 0x80000000) {
Packit 5e46da
        bd_psr_write(p->regs, PSR_PRIMARY_AUDIO_ID, (dst >> 16) & 0xfff);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* IG stream */
Packit 5e46da
    if (src & 0x80000000) {
Packit 5e46da
        bd_psr_write(p->regs, PSR_IG_STREAM_ID, (src >> 16) & 0xff);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* angle number */
Packit 5e46da
    if (src & 0x8000) {
Packit 5e46da
        bd_psr_write(p->regs, PSR_ANGLE_NUMBER, src & 0xff);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* PSR2 */
Packit 5e46da
Packit 5e46da
    bd_psr_lock(p->regs);
Packit 5e46da
Packit 5e46da
    uint32_t psr2 = bd_psr_read(p->regs, PSR_PG_STREAM);
Packit 5e46da
Packit 5e46da
    /* PG TextST stream number */
Packit 5e46da
    if (dst & 0x8000) {
Packit 5e46da
        uint32_t text_st_num = dst & 0xfff;
Packit 5e46da
        psr2 = text_st_num | (psr2 & 0xfffff000);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* Update PG TextST stream display flag */
Packit 5e46da
    uint32_t disp_s_flag = (dst & 0x4000) << 17;
Packit 5e46da
    psr2 = disp_s_flag | (psr2 & 0x7fffffff);
Packit 5e46da
Packit 5e46da
    bd_psr_write(p->regs, PSR_PG_STREAM, psr2);
Packit 5e46da
Packit 5e46da
    bd_psr_unlock(p->regs);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _set_sec_stream(HDMV_VM *p, uint32_t dst, uint32_t src)
Packit 5e46da
{
Packit 5e46da
    BD_DEBUG(DBG_HDMV, "_set_sec_stream(0x%x, 0x%x)\n", dst, src);
Packit 5e46da
Packit 5e46da
    uint32_t disp_v_flag   = (dst >> 30) & 1;
Packit 5e46da
    uint32_t disp_a_flag   = (src >> 30) & 1;
Packit 5e46da
    uint32_t text_st_flags = (src >> 13) & 3;
Packit 5e46da
Packit 5e46da
    /* PSR14 */
Packit 5e46da
Packit 5e46da
    bd_psr_lock(p->regs);
Packit 5e46da
Packit 5e46da
    uint32_t psr14 = bd_psr_read(p->regs, PSR_SECONDARY_AUDIO_VIDEO);
Packit 5e46da
Packit 5e46da
    /* secondary video */
Packit 5e46da
    if (dst & 0x80000000) {
Packit 5e46da
      uint32_t sec_video = dst & 0xff;
Packit 5e46da
      psr14 = (sec_video << 8) | (psr14 & 0xffff00ff);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* secondary video size */
Packit 5e46da
    if (dst & 0x00800000) {
Packit 5e46da
      uint32_t video_size = (dst >> 16) & 0xf;
Packit 5e46da
      psr14 = (video_size << 24) | (psr14 & 0xf0ffffff);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* secondary audio */
Packit 5e46da
    if (src & 0x80000000) {
Packit 5e46da
      uint32_t sec_audio = (src >> 16) & 0xff;
Packit 5e46da
      psr14 = sec_audio | (psr14 & 0xffffff00);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    psr14 = (disp_v_flag << 31) | (psr14 & 0x7fffffff);
Packit 5e46da
    psr14 = (disp_a_flag << 30) | (psr14 & 0xbfffffff);
Packit 5e46da
Packit 5e46da
    bd_psr_write(p->regs, PSR_SECONDARY_AUDIO_VIDEO, psr14);
Packit 5e46da
Packit 5e46da
    /* PSR2 */
Packit 5e46da
Packit 5e46da
    uint32_t psr2  = bd_psr_read(p->regs, PSR_PG_STREAM);
Packit 5e46da
Packit 5e46da
    /* PiP PG TextST stream */
Packit 5e46da
    if (src & 0x8000) {
Packit 5e46da
        uint32_t stream = src & 0xfff;
Packit 5e46da
        psr2 = (stream << 16) | (psr2 & 0xf000ffff);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    psr2 = (text_st_flags << 30) | (psr2 & 0x3fffffff);
Packit 5e46da
Packit 5e46da
    bd_psr_write(p->regs, PSR_PG_STREAM, psr2);
Packit 5e46da
Packit 5e46da
    bd_psr_unlock(p->regs);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _set_stream_ss(HDMV_VM *p, uint32_t dst, uint32_t src)
Packit 5e46da
{
Packit 5e46da
    BD_DEBUG(DBG_HDMV, "_set_stream_ss(0x%x, 0x%x)\n", dst, src);
Packit 5e46da
Packit 5e46da
    if (!(bd_psr_read(p->regs, PSR_3D_STATUS) & 1)) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "_set_stream_ss ignored (PSR22 indicates 2D mode)\n");
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_HDMV, "_set_stream_ss(0x%x, 0x%x) unimplemented\n", dst, src);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _setsystem_0x10(HDMV_VM *p, uint32_t dst, uint32_t src)
Packit 5e46da
{
Packit 5e46da
    BD_DEBUG(DBG_HDMV, "_set_psr103(0x%x, 0x%x)\n", dst, src);
Packit 5e46da
Packit 5e46da
    bd_psr_lock(p->regs);
Packit 5e46da
Packit 5e46da
    /* just a guess ... */
Packit 5e46da
    //bd_psr_write(p->regs, 104, 0);
Packit 5e46da
    bd_psr_write(p->regs, 103, dst);
Packit 5e46da
Packit 5e46da
    bd_psr_unlock(p->regs);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * SET/SYSTEM navigation control
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _set_button_page(HDMV_VM *p, uint32_t dst, uint32_t src)
Packit 5e46da
{
Packit 5e46da
    if (p->ig_object) {
Packit 5e46da
        uint32_t param;
Packit 5e46da
        param =  (src & 0xc0000000) |        /* page and effects flags */
Packit 5e46da
                ((dst & 0x80000000) >> 2) |  /* button flag */
Packit 5e46da
                ((src & 0x000000ff) << 16) | /* page id */
Packit 5e46da
                 (dst & 0x0000ffff);         /* button id */
Packit 5e46da
Packit 5e46da
         _queue_event(p, HDMV_EVENT_SET_BUTTON_PAGE, param);
Packit 5e46da
Packit 5e46da
         /* terminate */
Packit 5e46da
         p->pc = 1 << 17;
Packit 5e46da
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* selected button */
Packit 5e46da
    if (dst & 0x80000000) {
Packit 5e46da
        bd_psr_write(p->regs, PSR_SELECTED_BUTTON_ID, dst & 0xffff);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* active page */
Packit 5e46da
    if (src & 0x80000000) {
Packit 5e46da
        bd_psr_write(p->regs, PSR_MENU_PAGE_ID, src & 0xff);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _enable_button(HDMV_VM *p, uint32_t dst, int enable)
Packit 5e46da
{
Packit 5e46da
    /* not valid in movie objects */
Packit 5e46da
    if (p->ig_object) {
Packit 5e46da
        if (enable) {
Packit 5e46da
            _queue_event(p, HDMV_EVENT_ENABLE_BUTTON,  dst);
Packit 5e46da
        } else {
Packit 5e46da
            _queue_event(p, HDMV_EVENT_DISABLE_BUTTON, dst);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _set_still_mode(HDMV_VM *p, int enable)
Packit 5e46da
{
Packit 5e46da
    /* not valid in movie objects */
Packit 5e46da
    if (p->ig_object) {
Packit 5e46da
        _queue_event(p, HDMV_EVENT_STILL, enable);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _popup_off(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    /* not valid in movie objects */
Packit 5e46da
    if (p->ig_object) {
Packit 5e46da
        _queue_event(p, HDMV_EVENT_POPUP_OFF, 1);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * SET/SYSTEM 3D mode
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _set_output_mode(HDMV_VM *p, uint32_t dst)
Packit 5e46da
{
Packit 5e46da
    if ((bd_psr_read(p->regs, PSR_PROFILE_VERSION) & 0x130240) != 0x130240) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "_set_output_mode ignored (not running as profile 5 player)\n");
Packit 5e46da
        return;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_psr_lock(p->regs);
Packit 5e46da
Packit 5e46da
    uint32_t psr22 = bd_psr_read(p->regs, PSR_3D_STATUS);
Packit 5e46da
Packit 5e46da
    /* update output mode (bit 0). PSR22 bits 1 and 2 are subtitle alignment (_set_stream_ss()) */
Packit 5e46da
    if (dst & 1) {
Packit 5e46da
      psr22 |= 1;
Packit 5e46da
    } else {
Packit 5e46da
      psr22 &= ~1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_psr_write(p->regs, PSR_3D_STATUS, psr22);
Packit 5e46da
Packit 5e46da
    bd_psr_unlock(p->regs);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * navigation timer
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _set_nv_timer(HDMV_VM *p, uint32_t dst, uint32_t src)
Packit 5e46da
{
Packit 5e46da
  uint32_t mobj_id = dst & 0xffff;
Packit 5e46da
  uint32_t timeout = src & 0xffff;
Packit 5e46da
Packit 5e46da
  if (!timeout) {
Packit 5e46da
    /* cancel timer */
Packit 5e46da
    p->nv_timer.time = 0;
Packit 5e46da
Packit 5e46da
    bd_psr_write(p->regs, PSR_NAV_TIMER, 0);
Packit 5e46da
Packit 5e46da
    return;
Packit 5e46da
  }
Packit 5e46da
Packit 5e46da
  /* validate params */
Packit 5e46da
  if (mobj_id >= p->movie_objects->num_objects) {
Packit 5e46da
      BD_DEBUG(DBG_HDMV|DBG_CRIT, "_set_nv_timer(): invalid object id (%d) !\n", mobj_id);
Packit 5e46da
      return;
Packit 5e46da
  }
Packit 5e46da
  if (timeout > 300) {
Packit 5e46da
      BD_DEBUG(DBG_HDMV|DBG_CRIT, "_set_nv_timer(): invalid timeout (%d) !\n", timeout);
Packit 5e46da
      return;
Packit 5e46da
  }
Packit 5e46da
Packit 5e46da
  BD_DEBUG(DBG_HDMV | DBG_CRIT, "_set_nv_timer(): navigation timer not implemented !\n");
Packit 5e46da
Packit 5e46da
  /* set expiration time */
Packit 5e46da
  p->nv_timer.time = time(NULL);
Packit 5e46da
  p->nv_timer.time += timeout;
Packit 5e46da
Packit 5e46da
  p->nv_timer.mobj_id = mobj_id;
Packit 5e46da
Packit 5e46da
  bd_psr_write(p->regs, PSR_NAV_TIMER, timeout);
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/* Unused function.
Packit 5e46da
 * Commenting out to disable "‘_check_nv_timer’ defined but not used" warning
Packit 5e46da
static int _check_nv_timer(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    if (p->nv_timer.time > 0) {
Packit 5e46da
        time_t now = time(NULL);
Packit 5e46da
Packit 5e46da
        if (now >= p->nv_timer.time) {
Packit 5e46da
            BD_DEBUG(DBG_HDMV, "navigation timer expired, jumping to object %d\n", p->nv_timer.mobj_id);
Packit 5e46da
Packit 5e46da
            bd_psr_write(p->regs, PSR_NAV_TIMER, 0);
Packit 5e46da
Packit 5e46da
            p->nv_timer.time = 0;
Packit 5e46da
            _jump_object(p, p->nv_timer.mobj_id);
Packit 5e46da
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        bd_psr_write(p->regs, PSR_NAV_TIMER, (p->nv_timer.time - now));
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
*/
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * trace
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
static void _hdmv_trace_cmd(int pc, MOBJ_CMD *cmd)
Packit 5e46da
{
Packit 5e46da
    if (bd_get_debug_mask() & DBG_HDMV) {
Packit 5e46da
        char buf[384], *dst = buf;
Packit 5e46da
Packit 5e46da
        dst += sprintf(dst, "%04d:  ", pc);
Packit 5e46da
Packit 5e46da
        /*dst +=*/ mobj_sprint_cmd(dst, cmd);
Packit 5e46da
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "%s\n", buf);
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static void _hdmv_trace_res(uint32_t new_src, uint32_t new_dst, uint32_t orig_src, uint32_t orig_dst)
Packit 5e46da
{
Packit 5e46da
    if (bd_get_debug_mask() & DBG_HDMV) {
Packit 5e46da
Packit 5e46da
        if (new_dst != orig_dst || new_src != orig_src) {
Packit 5e46da
            char buf[384], *dst = buf;
Packit 5e46da
Packit 5e46da
            dst += sprintf(dst, "    :  [");
Packit 5e46da
            if (new_dst != orig_dst) {
Packit 5e46da
                dst += sprintf(dst, " dst 0x%x <== 0x%x ", orig_dst, new_dst);
Packit 5e46da
            }
Packit 5e46da
            if (new_src != orig_src) {
Packit 5e46da
                dst += sprintf(dst, " src 0x%x <== 0x%x ", orig_src, new_src);
Packit 5e46da
            }
Packit 5e46da
            /*dst +=*/ sprintf(dst, "]");
Packit 5e46da
Packit 5e46da
            BD_DEBUG(DBG_HDMV, "%s\n", buf);
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * interpreter
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * tools
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
#define SWAP_u32(a, b) do { uint32_t tmp = a; a = b; b = tmp; } while(0)
Packit 5e46da
Packit 5e46da
static inline uint32_t RAND_u32(HDMV_VM *p, uint32_t range)
Packit 5e46da
{
Packit 5e46da
    p->rand = p->rand * UINT64_C(6364136223846793005) + UINT64_C(1);
Packit 5e46da
Packit 5e46da
    if (range == 0) {
Packit 5e46da
      BD_DEBUG(DBG_HDMV|DBG_CRIT, "RAND_u32: invalid range (0)\n");
Packit 5e46da
      return 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    return ((uint32_t)(p->rand >> 32)) % range + 1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static inline uint32_t ADD_u32(uint32_t a, uint32_t b)
Packit 5e46da
{
Packit 5e46da
  /* overflow -> saturate */
Packit 5e46da
  uint64_t result = (uint64_t)a + b;
Packit 5e46da
  return result < 0xffffffff ? (uint32_t)result : 0xffffffff;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static inline uint32_t MUL_u32(uint32_t a, uint32_t b)
Packit 5e46da
{
Packit 5e46da
  /* overflow -> saturate */
Packit 5e46da
  uint64_t result = (uint64_t)a * b;
Packit 5e46da
  return result < 0xffffffff ? (uint32_t)result : 0xffffffff;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * _hdmv_step()
Packit 5e46da
 *  - execute next instruction from current program
Packit 5e46da
 */
Packit 5e46da
static int _hdmv_step(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    MOBJ_CMD  *cmd  = &p->object->cmds[p->pc];
Packit 5e46da
    HDMV_INSN *insn = &cmd->insn;
Packit 5e46da
    uint32_t   src  = 0;
Packit 5e46da
    uint32_t   dst  = 0;
Packit 5e46da
    int        inc_pc = 1;
Packit 5e46da
Packit 5e46da
    /* fetch operand values */
Packit 5e46da
    _fetch_operands(p, cmd, &dst, &src;;
Packit 5e46da
Packit 5e46da
    /* trace */
Packit 5e46da
    _hdmv_trace_cmd(p->pc, cmd);
Packit 5e46da
Packit 5e46da
    /* execute */
Packit 5e46da
    switch (insn->grp) {
Packit 5e46da
        case INSN_GROUP_BRANCH:
Packit 5e46da
            switch (insn->sub_grp) {
Packit 5e46da
                case BRANCH_GOTO:
Packit 5e46da
                    if (insn->op_cnt > 1) {
Packit 5e46da
                        BD_DEBUG(DBG_HDMV|DBG_CRIT, "too many operands in BRANCH/GOTO opcode 0x%08x\n", *(uint32_t*)insn);
Packit 5e46da
                    }
Packit 5e46da
                    switch (insn->branch_opt) {
Packit 5e46da
                        case INSN_NOP:                      break;
Packit 5e46da
                        case INSN_GOTO:  p->pc   = dst - 1; break;
Packit 5e46da
                        case INSN_BREAK: p->pc   = 1 << 17; break;
Packit 5e46da
                        default:
Packit 5e46da
                            BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown BRANCH/GOTO option %d in opcode 0x%08x\n",
Packit 5e46da
                                     insn->branch_opt, *(uint32_t*)insn);
Packit 5e46da
                            break;
Packit 5e46da
                    }
Packit 5e46da
                    break;
Packit 5e46da
                case BRANCH_JUMP:
Packit 5e46da
                    if (insn->op_cnt > 1) {
Packit 5e46da
                        BD_DEBUG(DBG_HDMV|DBG_CRIT, "too many operands in BRANCH/JUMP opcode 0x%08x\n", *(uint32_t*)insn);
Packit 5e46da
                    }
Packit 5e46da
                    switch (insn->branch_opt) {
Packit 5e46da
                        case INSN_JUMP_TITLE:  _jump_title(p, dst); break;
Packit 5e46da
                        case INSN_CALL_TITLE:  _call_title(p, dst); break;
Packit 5e46da
                        case INSN_RESUME:      _resume_object(p, 1);   break;
Packit 5e46da
                        case INSN_JUMP_OBJECT: if (!_jump_object(p, dst)) { inc_pc = 0; } break;
Packit 5e46da
                        case INSN_CALL_OBJECT: if (!_call_object(p, dst)) { inc_pc = 0; } break;
Packit 5e46da
                        default:
Packit 5e46da
                            BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown BRANCH/JUMP option %d in opcode 0x%08x\n",
Packit 5e46da
                                     insn->branch_opt, *(uint32_t*)insn);
Packit 5e46da
                            break;
Packit 5e46da
                    }
Packit 5e46da
                    break;
Packit 5e46da
                case BRANCH_PLAY:
Packit 5e46da
                    switch (insn->branch_opt) {
Packit 5e46da
                        case INSN_PLAY_PL:      _play_at(p, dst,  -1,  -1); break;
Packit 5e46da
                        case INSN_PLAY_PL_PI:   _play_at(p, dst, src,  -1); break;
Packit 5e46da
                        case INSN_PLAY_PL_PM:   _play_at(p, dst,  -1, src); break;
Packit 5e46da
                        case INSN_LINK_PI:      _play_at(p,  -1, dst,  -1); break;
Packit 5e46da
                        case INSN_LINK_MK:      _play_at(p,  -1,  -1, dst); break;
Packit 5e46da
                        case INSN_TERMINATE_PL: if (!_play_stop(p)) { inc_pc = 0; } break;
Packit 5e46da
                        default:
Packit 5e46da
                            BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown BRANCH/PLAY option %d in opcode 0x%08x\n",
Packit 5e46da
                                     insn->branch_opt, *(uint32_t*)insn);
Packit 5e46da
                            break;
Packit 5e46da
                    }
Packit 5e46da
                    break;
Packit 5e46da
Packit 5e46da
                default:
Packit 5e46da
                    BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown BRANCH subgroup %d in opcode 0x%08x\n",
Packit 5e46da
                             insn->sub_grp, *(uint32_t*)insn);
Packit 5e46da
                    break;
Packit 5e46da
            }
Packit 5e46da
            break; /* INSN_GROUP_BRANCH */
Packit 5e46da
Packit 5e46da
        case INSN_GROUP_CMP:
Packit 5e46da
            if (insn->op_cnt < 2) {
Packit 5e46da
                BD_DEBUG(DBG_HDMV|DBG_CRIT, "missing operand in BRANCH/JUMP opcode 0x%08x\n", *(uint32_t*)insn);
Packit 5e46da
            }
Packit 5e46da
            switch (insn->cmp_opt) {
Packit 5e46da
                case INSN_BC: p->pc += !!(dst & ~src); break;
Packit 5e46da
                case INSN_EQ: p->pc += !(dst == src); break;
Packit 5e46da
                case INSN_NE: p->pc += !(dst != src); break;
Packit 5e46da
                case INSN_GE: p->pc += !(dst >= src); break;
Packit 5e46da
                case INSN_GT: p->pc += !(dst >  src); break;
Packit 5e46da
                case INSN_LE: p->pc += !(dst <= src); break;
Packit 5e46da
                case INSN_LT: p->pc += !(dst <  src); break;
Packit 5e46da
                default:
Packit 5e46da
                    BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown COMPARE option %d in opcode 0x%08x\n",
Packit 5e46da
                             insn->cmp_opt, *(uint32_t*)insn);
Packit 5e46da
                    break;
Packit 5e46da
            }
Packit 5e46da
            break; /* INSN_GROUP_CMP */
Packit 5e46da
Packit 5e46da
        case INSN_GROUP_SET:
Packit 5e46da
            switch (insn->sub_grp) {
Packit 5e46da
                case SET_SET: {
Packit 5e46da
                    uint32_t src0 = src;
Packit 5e46da
                    uint32_t dst0 = dst;
Packit 5e46da
Packit 5e46da
                    if (insn->op_cnt < 2) {
Packit 5e46da
                        BD_DEBUG(DBG_HDMV|DBG_CRIT, "missing operand in SET/SET opcode 0x%08x\n", *(uint32_t*)insn);
Packit 5e46da
                    }
Packit 5e46da
                    switch (insn->set_opt) {
Packit 5e46da
                        case INSN_MOVE:   dst  = src;         break;
Packit 5e46da
                        case INSN_SWAP:   SWAP_u32(src, dst);   break;
Packit 5e46da
                        case INSN_SUB:    dst  = dst > src ? dst - src :          0; break;
Packit 5e46da
                        case INSN_DIV:    dst  = src > 0   ? dst / src : 0xffffffff; break;
Packit 5e46da
                        case INSN_MOD:    dst  = src > 0   ? dst % src : 0xffffffff; break;
Packit 5e46da
                        case INSN_ADD:    dst  = ADD_u32(src, dst);  break;
Packit 5e46da
                        case INSN_MUL:    dst  = MUL_u32(dst, src);  break;
Packit 5e46da
                        case INSN_RND:    dst  = RAND_u32(p, src);   break;
Packit 5e46da
                        case INSN_AND:    dst &= src;         break;
Packit 5e46da
                        case INSN_OR:     dst |= src;         break;
Packit 5e46da
                        case INSN_XOR:    dst ^= src;         break;
Packit 5e46da
                        case INSN_BITSET: dst |=  (1 << src); break;
Packit 5e46da
                        case INSN_BITCLR: dst &= ~(1 << src); break;
Packit 5e46da
                        case INSN_SHL:    dst <<= src;        break;
Packit 5e46da
                        case INSN_SHR:    dst >>= src;        break;
Packit 5e46da
                        default:
Packit 5e46da
                            BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown SET option %d in opcode 0x%08x\n",
Packit 5e46da
                                     insn->set_opt, *(uint32_t*)insn);
Packit 5e46da
                            break;
Packit 5e46da
                    }
Packit 5e46da
Packit 5e46da
                    /* store result(s) */
Packit 5e46da
                    if (dst != dst0 || src != src0) {
Packit 5e46da
Packit 5e46da
                        _hdmv_trace_res(src, dst, src0, dst0);
Packit 5e46da
Packit 5e46da
                        _store_result(p, cmd, src, dst, src0, dst0);
Packit 5e46da
                    }
Packit 5e46da
                    break;
Packit 5e46da
                }
Packit 5e46da
                case SET_SETSYSTEM:
Packit 5e46da
                    switch (insn->set_opt) {
Packit 5e46da
                        case INSN_SET_STREAM:      _set_stream     (p, dst, src); break;
Packit 5e46da
                        case INSN_SET_SEC_STREAM:  _set_sec_stream (p, dst, src); break;
Packit 5e46da
                        case INSN_SET_NV_TIMER:    _set_nv_timer   (p, dst, src); break;
Packit 5e46da
                        case INSN_SET_BUTTON_PAGE: _set_button_page(p, dst, src); break;
Packit 5e46da
                        case INSN_ENABLE_BUTTON:   _enable_button  (p, dst,   1); break;
Packit 5e46da
                        case INSN_DISABLE_BUTTON:  _enable_button  (p, dst,   0); break;
Packit 5e46da
                        case INSN_POPUP_OFF:       _popup_off      (p);           break;
Packit 5e46da
                        case INSN_STILL_ON:        _set_still_mode (p,   1);      break;
Packit 5e46da
                        case INSN_STILL_OFF:       _set_still_mode (p,   0);      break;
Packit 5e46da
                        case INSN_SET_OUTPUT_MODE: _set_output_mode(p, dst);      break;
Packit 5e46da
                        case INSN_SET_STREAM_SS:   _set_stream_ss  (p, dst, src); break;
Packit 5e46da
                        case INSN_SETSYSTEM_0x10:  _setsystem_0x10 (p, dst, src); break;
Packit 5e46da
                        default:
Packit 5e46da
                            BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown SETSYSTEM option %d in opcode 0x%08x\n", insn->set_opt, *(uint32_t*)insn);
Packit 5e46da
                            break;
Packit 5e46da
                    }
Packit 5e46da
                    break;
Packit 5e46da
                default:
Packit 5e46da
                    BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown SET subgroup %d in opcode 0x%08x\n",
Packit 5e46da
                             insn->sub_grp, *(uint32_t*)insn);
Packit 5e46da
                    break;
Packit 5e46da
            }
Packit 5e46da
            break; /* INSN_GROUP_SET */
Packit 5e46da
Packit 5e46da
        default:
Packit 5e46da
            BD_DEBUG(DBG_HDMV|DBG_CRIT, "unknown operation group %d in opcode 0x%08x\n",
Packit 5e46da
                     insn->grp, *(uint32_t*)insn);
Packit 5e46da
            break;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* inc program counter to next instruction */
Packit 5e46da
    p->pc += inc_pc;
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/*
Packit 5e46da
 * interface
Packit 5e46da
 */
Packit 5e46da
Packit 5e46da
int hdmv_vm_select_object(HDMV_VM *p, uint32_t object)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    if (!p) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&p->mutex);
Packit 5e46da
Packit 5e46da
    result = _jump_object(p, object);
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&p->mutex);
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
static int _set_object(HDMV_VM *p, int num_nav_cmds, void *nav_cmds)
Packit 5e46da
{
Packit 5e46da
    MOBJ_OBJECT *ig_object = calloc(1, sizeof(MOBJ_OBJECT));
Packit 5e46da
    if (!ig_object) {
Packit 5e46da
        BD_DEBUG(DBG_CRIT, "out of memory\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    ig_object->num_cmds = num_nav_cmds;
Packit 5e46da
    ig_object->cmds     = calloc(num_nav_cmds, sizeof(MOBJ_CMD));
Packit 5e46da
    if (!ig_object->cmds) {
Packit 5e46da
        BD_DEBUG(DBG_CRIT, "out of memory\n");
Packit 5e46da
        X_FREE(ig_object);
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    memcpy(ig_object->cmds, nav_cmds, num_nav_cmds * sizeof(MOBJ_CMD));
Packit 5e46da
Packit 5e46da
    p->pc        = 0;
Packit 5e46da
    p->ig_object = ig_object;
Packit 5e46da
    p->object    = ig_object;
Packit 5e46da
Packit 5e46da
    return 0;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int hdmv_vm_set_object(HDMV_VM *p, int num_nav_cmds, void *nav_cmds)
Packit 5e46da
{
Packit 5e46da
    int result = -1;
Packit 5e46da
Packit 5e46da
    if (!p) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&p->mutex);
Packit 5e46da
Packit 5e46da
    p->object = NULL;
Packit 5e46da
Packit 5e46da
    _free_ig_object(p);
Packit 5e46da
Packit 5e46da
    if (nav_cmds && num_nav_cmds > 0) {
Packit 5e46da
        result = _set_object(p, num_nav_cmds, nav_cmds);
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&p->mutex);
Packit 5e46da
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int hdmv_vm_get_event(HDMV_VM *p, HDMV_EVENT *ev)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
    bd_mutex_lock(&p->mutex);
Packit 5e46da
Packit 5e46da
    result = _get_event(p, ev);
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&p->mutex);
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int hdmv_vm_running(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    if (!p) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&p->mutex);
Packit 5e46da
Packit 5e46da
    result = !!p->object;
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&p->mutex);
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
uint32_t hdmv_vm_get_uo_mask(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    uint32_t     mask = 0;
Packit 5e46da
    const MOBJ_OBJECT *o = NULL;
Packit 5e46da
Packit 5e46da
    if (!p) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&p->mutex);
Packit 5e46da
Packit 5e46da
    if ((o = (p->object && !p->ig_object) ? p->object : (p->playing_object ? p->playing_object : p->suspended_object))) {
Packit 5e46da
        mask |= o->menu_call_mask;
Packit 5e46da
        mask |= o->title_search_mask << 1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&p->mutex);
Packit 5e46da
    return mask;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int hdmv_vm_resume(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    if (!p) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&p->mutex);
Packit 5e46da
Packit 5e46da
    result = _resume_from_play_pl(p);
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&p->mutex);
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int hdmv_vm_suspend_pl(HDMV_VM *p)
Packit 5e46da
{
Packit 5e46da
    int result = -1;
Packit 5e46da
Packit 5e46da
    if (!p) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&p->mutex);
Packit 5e46da
Packit 5e46da
    if (p->object || p->ig_object) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "hdmv_vm_suspend_pl(): HDMV VM is still running\n");
Packit 5e46da
Packit 5e46da
    } else if (!p->playing_object) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "hdmv_vm_suspend_pl(): No playing object\n");
Packit 5e46da
Packit 5e46da
    } else if (!p->playing_object->resume_intention_flag) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV, "hdmv_vm_suspend_pl(): no resume intention flag\n");
Packit 5e46da
Packit 5e46da
        p->playing_object = NULL;
Packit 5e46da
        result = 0;
Packit 5e46da
Packit 5e46da
    } else {
Packit 5e46da
        p->suspended_object = p->playing_object;
Packit 5e46da
        p->suspended_pc     = p->playing_pc;
Packit 5e46da
Packit 5e46da
        p->playing_object = NULL;
Packit 5e46da
Packit 5e46da
        bd_psr_save_state(p->regs);
Packit 5e46da
        result = 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&p->mutex);
Packit 5e46da
    return result;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
/* terminate program after MAX_LOOP instructions */
Packit 5e46da
#define MAX_LOOP 1000000
Packit 5e46da
Packit 5e46da
static int _vm_run(HDMV_VM *p, HDMV_EVENT *ev)
Packit 5e46da
{
Packit 5e46da
    int max_loop = MAX_LOOP;
Packit 5e46da
Packit 5e46da
    /* pending events ? */
Packit 5e46da
    if (!_get_event(p, ev)) {
Packit 5e46da
        return 0;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    /* valid program ? */
Packit 5e46da
    if (!p->object) {
Packit 5e46da
        BD_DEBUG(DBG_HDMV|DBG_CRIT, "hdmv_vm_run(): no object selected\n");
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    while (--max_loop > 0) {
Packit 5e46da
Packit 5e46da
        /* suspended ? */
Packit 5e46da
        if (!p->object) {
Packit 5e46da
            BD_DEBUG(DBG_HDMV, "hdmv_vm_run(): object suspended\n");
Packit 5e46da
            _get_event(p, ev);
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* terminated ? */
Packit 5e46da
        if (p->pc >= p->object->num_cmds) {
Packit 5e46da
            BD_DEBUG(DBG_HDMV, "terminated with PC=%d\n", p->pc);
Packit 5e46da
            p->object = NULL;
Packit 5e46da
            ev->event = HDMV_EVENT_END;
Packit 5e46da
Packit 5e46da
            if (p->ig_object) {
Packit 5e46da
                ev->event = HDMV_EVENT_IG_END;
Packit 5e46da
                _free_ig_object(p);
Packit 5e46da
            }
Packit 5e46da
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* next instruction */
Packit 5e46da
        if (_hdmv_step(p) < 0) {
Packit 5e46da
            p->object = NULL;
Packit 5e46da
            return -1;
Packit 5e46da
        }
Packit 5e46da
Packit 5e46da
        /* events ? */
Packit 5e46da
        if (!_get_event(p, ev)) {
Packit 5e46da
            return 0;
Packit 5e46da
        }
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    BD_DEBUG(DBG_HDMV|DBG_CRIT, "hdmv_vm: infinite program ? terminated after %d instructions.\n", MAX_LOOP);
Packit 5e46da
    p->object = NULL;
Packit 5e46da
    return -1;
Packit 5e46da
}
Packit 5e46da
Packit 5e46da
int hdmv_vm_run(HDMV_VM *p, HDMV_EVENT *ev)
Packit 5e46da
{
Packit 5e46da
    int result;
Packit 5e46da
Packit 5e46da
    if (!p) {
Packit 5e46da
        return -1;
Packit 5e46da
    }
Packit 5e46da
Packit 5e46da
    bd_mutex_lock(&p->mutex);
Packit 5e46da
Packit 5e46da
    result = _vm_run(p, ev);
Packit 5e46da
Packit 5e46da
    bd_mutex_unlock(&p->mutex);
Packit 5e46da
    return result;
Packit 5e46da
}