/** * Allocate and initialize an interpret context. * Called from a process prior to Interpret(). * @param gsort which sort of code * @param hCode Handle to code to execute * @param event Causal event * @param hpoly Associated polygon (if any) * @param actorId Associated actor (if any) * @param pinvo Associated inventory object */ INT_CONTEXT *InitInterpretContext(GSORT gsort, SCNHANDLE hCode, TINSEL_EVENT event, HPOLYGON hpoly, int actorid, INV_OBJECT *pinvo, int myEscape) { INT_CONTEXT *ic; ic = AllocateInterpretContext(gsort); // Previously parameters to Interpret() ic->hCode = hCode; LockCode(ic); ic->event = event; ic->hPoly = hpoly; ic->idActor = actorid; ic->pinvo = pinvo; // Previously local variables in Interpret() ic->bHalt = false; // set to exit interpeter ic->escOn = myEscape > 0; ic->myEscape = myEscape; ic->sp = 0; ic->bp = ic->sp + 1; ic->ip = 0; // start of code ic->resumeState = RES_NOT; return ic; }
/** * Allocate and initialize an interpret context with restored data. */ INT_CONTEXT *RestoreInterpretContext(INT_CONTEXT *ric) { INT_CONTEXT *ic; ic = AllocateInterpretContext(GS_NONE); // Sort will soon be overridden memcpy(ic, ric, sizeof(INT_CONTEXT)); ic->pProc = g_scheduler->getCurrentProcess(); ic->resumeState = RES_1; LockCode(ic); return ic; }
//----------------------------------------------------------------------------- //! //----------------------------------------------------------------------------- void tSiriusController::CreateSiriusActions() { // Select from all available channels action m_pSelectFromAllAction = new tAction(QIcon(), tr("Subscribed", "Sirius subscribed channels menu selection"), this ); Connect( m_pSelectFromAllAction, SIGNAL( triggered() ), this, SLOT( SelectChannels() ), Qt::QueuedConnection ); // Select from favorites only action m_pSelectFromFavoritesAction = new tAction(QIcon(), tr("Favorites", "Sirius user favorite channels menu selection"), this ); Connect( m_pSelectFromFavoritesAction, SIGNAL( triggered() ), this, SLOT(SelectFavoriteChannels() ) ); RefreshFavoriteChannels(tAudioSettings::Instance()->Favorites()); // Channels status action m_pAllChannelStatusAction = new tAction(QIcon(), tr("All", "Sirius all channels menu selection"), this ); Connect( m_pAllChannelStatusAction, SIGNAL( triggered() ), this, SLOT( AllChannelStatus() ) ); // Select by category action m_pCategoryAction = new tAction( tr( "By category", "Sirius channel categories menu selection" ), this ); Connect( m_pCategoryAction, SIGNAL( triggered() ), this, SLOT( SelectCategory() )) ; // Channels SubMenu m_pChannelsSubMenuAction = new tAction( tr( "Channels", "Sirius channel menu selection" ), this ); m_pChannelsSubMenuAction->AppendSubAction( m_pSelectFromAllAction ); m_pChannelsSubMenuAction->AppendSubAction( m_pSelectFromFavoritesAction ); m_pChannelsSubMenuAction->AppendSubAction( m_pAllChannelStatusAction ); m_pChannelsSubMenuAction->AppendSubAction( m_pCategoryAction ); //when channels is selected we need to know this so we can refresh lists and whether a menu selection is grayed out or not Connect( m_pChannelsSubMenuAction, SIGNAL( triggered() ), this, SLOT( ChannelsSubmenuSelected() ) ); //the above works for Cougar, but is not working for HDS, so in contextMenuEvent I update the channels menu m_MenuActionList << m_pChannelsSubMenuAction; m_pOptionsAction = new tAction( tr( "Options" ), this ); // Lock code action m_pLockCodeAction = new tAction( tr( "Change lock code" ), this ); Connect( m_pLockCodeAction, SIGNAL( triggered() ), this, SLOT( LockCode() ), Qt::QueuedConnection ); m_MenuActionList << m_pLockCodeAction; // Lock current channel action m_pLockChannelAction = new tAction( tr("Lock channel"), this ); m_pLockChannelAction->setCheckable( true ); Connect( m_pLockChannelAction, SIGNAL( toggled( bool ) ), this, SLOT( LockChannel( bool ) ) ); m_MenuActionList << m_pLockChannelAction; }
/** * Interprets the PCODE instructions in the code array. */ void Interpret(CORO_PARAM, INT_CONTEXT *ic) { do { int tmp, tmp2; int ip = ic->ip; const WorkaroundEntry *wkEntry = ic->fragmentPtr; if (wkEntry == NULL) { // Check to see if a workaround fragment needs to be executed for (wkEntry = workaroundList; wkEntry->script != NULL; ++wkEntry) { if ((wkEntry->version == TinselVersion) && (wkEntry->hCode == ic->hCode) && (wkEntry->ip == ip) && (!TinselV1 || (wkEntry->scnFlag == ((_vm->getFeatures() & GF_SCNFILES) != 0)))) { // Point to start of workaround fragment ip = 0; break; } } if (wkEntry->script == NULL) wkEntry = NULL; } byte opcode = (byte)GetBytes(ic->code, wkEntry, ip, 0); if (TinselV0 && ((opcode & OPMASK) > OP_IMM)) opcode += 3; debug(7, "ip=%d Opcode %d (-> %d)", ic->ip, opcode, opcode & OPMASK); switch (opcode & OPMASK) { case OP_HALT: // end of program ic->bHalt = true; break; case OP_IMM: // loads immediate data onto stack case OP_STR: // loads string handle onto stack case OP_FILM: // loads film handle onto stack case OP_CDFILM: // loads film handle onto stack case OP_FONT: // loads font handle onto stack case OP_PAL: // loads palette handle onto stack ic->stack[++ic->sp] = Fetch(opcode, ic->code, wkEntry, ip); break; case OP_ZERO: // loads zero onto stack ic->stack[++ic->sp] = 0; break; case OP_ONE: // loads one onto stack ic->stack[++ic->sp] = 1; break; case OP_MINUSONE: // loads minus one onto stack ic->stack[++ic->sp] = -1; break; case OP_LOAD: // loads local variable onto stack ic->stack[++ic->sp] = ic->stack[ic->bp + Fetch(opcode, ic->code, wkEntry, ip)]; break; case OP_GLOAD: // loads global variable onto stack tmp = Fetch(opcode, ic->code, wkEntry, ip); assert(0 <= tmp && tmp < numGlobals); ic->stack[++ic->sp] = pGlobals[tmp]; break; case OP_STORE: // pops stack and stores in local variable ic->stack[ic->bp + Fetch(opcode, ic->code, wkEntry, ip)] = ic->stack[ic->sp--]; break; case OP_GSTORE: // pops stack and stores in global variable tmp = Fetch(opcode, ic->code, wkEntry, ip); assert(0 <= tmp && tmp < numGlobals); pGlobals[tmp] = ic->stack[ic->sp--]; break; case OP_CALL: // procedure call tmp = Fetch(opcode, ic->code, wkEntry, ip); //assert(0 <= tmp && tmp < codeSize); // TODO: Verify jumps are not out of bounds ic->stack[ic->sp + 1] = 0; // static link ic->stack[ic->sp + 2] = ic->bp; // dynamic link ic->stack[ic->sp + 3] = ip; // return address ic->bp = ic->sp + 1; // set new base pointer ip = tmp; // set ip to procedure address break; case OP_LIBCALL: // library procedure or function call tmp = Fetch(opcode, ic->code, wkEntry, ip); // NOTE: Interpret() itself is not using the coroutine facilities, // but still accepts a CORO_PARAM, so from the outside it looks // like a coroutine. In fact it may still acts as a kind of "proxy" // for some underlying coroutine. To enable this, we just pass on // 'coroParam' to CallLibraryRoutine(). If we then detect that // coroParam was set to a non-zero value, this means that some // coroutine code did run at some point, and we are now supposed // to sleep or die -- hence, we 'return' if coroParam != 0. // // This works because Interpret() is fully re-entrant: If we return // now and are later called again, then we will end up in the very // same spot (i.e. here). // // The reasons we do it this way, instead of turning Interpret into // a 'proper' coroutine are (1) we avoid implementation problems // (CORO_INVOKE involves adding 'case' statements, but Interpret // already has a huge switch/case, so that would not work out of the // box), (2) we incurr less overhead, (3) it's easier to debug, // (4) it's simply cool ;). tmp2 = CallLibraryRoutine(coroParam, tmp, &ic->stack[ic->sp], ic, &ic->resumeState); if (coroParam) return; if (!TinselV0) ic->sp += tmp2; LockCode(ic); if (TinselV2 && (ic->resumeState == RES_1)) ic->resumeState = RES_NOT; break; case OP_RET: // procedure return ic->sp = ic->bp - 1; // restore stack ip = ic->stack[ic->sp + 3]; // return address ic->bp = ic->stack[ic->sp + 2]; // restore previous base pointer break; case OP_ALLOC: // allocate storage on stack ic->sp += (int32)Fetch(opcode, ic->code, wkEntry, ip); break; case OP_JUMP: // unconditional jump ip = Fetch(opcode, ic->code, wkEntry, ip); wkEntry = NULL; // In case a jump occurs from a workaround break; case OP_JMPFALSE: // conditional jump tmp = Fetch(opcode, ic->code, wkEntry, ip); if (ic->stack[ic->sp--] == 0) { // condition satisfied - do the jump ip = tmp; wkEntry = NULL; // In case a jump occurs from a workaround } break; case OP_JMPTRUE: // conditional jump tmp = Fetch(opcode, ic->code, wkEntry, ip); if (ic->stack[ic->sp--] != 0) { // condition satisfied - do the jump ip = tmp; wkEntry = NULL; // In case a jump occurs from a workaround } break; case OP_EQUAL: // tests top two items on stack for equality case OP_LESS: // tests top two items on stack case OP_LEQUAL: // tests top two items on stack case OP_NEQUAL: // tests top two items on stack case OP_GEQUAL: // tests top two items on stack case OP_GREAT: // tests top two items on stack case OP_LOR: // logical or of top two items on stack and replaces with result case OP_LAND: // logical ands top two items on stack and replaces with result // pop one operand ic->sp--; assert(ic->sp >= 0); tmp = ic->stack[ic->sp]; tmp2 = ic->stack[ic->sp + 1]; // replace other operand with result of operation switch (opcode) { case OP_EQUAL: tmp = (tmp == tmp2); break; case OP_LESS: tmp = (tmp < tmp2); break; case OP_LEQUAL: tmp = (tmp <= tmp2); break; case OP_NEQUAL: tmp = (tmp != tmp2); break; case OP_GEQUAL: tmp = (tmp >= tmp2); break; case OP_GREAT: tmp = (tmp > tmp2); break; case OP_LOR: tmp = (tmp || tmp2); break; case OP_LAND: tmp = (tmp && tmp2); break; } ic->stack[ic->sp] = tmp; break; case OP_PLUS: // adds top two items on stack and replaces with result case OP_MINUS: // subs top two items on stack and replaces with result case OP_MULT: // multiplies top two items on stack and replaces with result case OP_DIV: // divides top two items on stack and replaces with result case OP_MOD: // divides top two items on stack and replaces with modulus case OP_AND: // bitwise ands top two items on stack and replaces with result case OP_OR: // bitwise ors top two items on stack and replaces with result case OP_EOR: // bitwise exclusive ors top two items on stack and replaces with result // pop one operand ic->sp--; assert(ic->sp >= 0); tmp = ic->stack[ic->sp]; tmp2 = ic->stack[ic->sp + 1]; // replace other operand with result of operation switch (opcode) { case OP_PLUS: tmp += tmp2; break; case OP_MINUS: tmp -= tmp2; break; case OP_MULT: tmp *= tmp2; break; case OP_DIV: tmp /= tmp2; break; case OP_MOD: tmp %= tmp2; break; case OP_AND: tmp &= tmp2; break; case OP_OR: tmp |= tmp2; break; case OP_EOR: tmp ^= tmp2; break; } ic->stack[ic->sp] = tmp; break; case OP_NOT: // logical nots top item on stack ic->stack[ic->sp] = !ic->stack[ic->sp]; break; case OP_COMP: // complements top item on stack ic->stack[ic->sp] = ~ic->stack[ic->sp]; break; case OP_NEG: // negates top item on stack ic->stack[ic->sp] = -ic->stack[ic->sp]; break; case OP_DUP: // duplicates top item on stack ic->stack[ic->sp + 1] = ic->stack[ic->sp]; ic->sp++; break; case OP_ESCON: bNoPause = true; ic->escOn = true; ic->myEscape = GetEscEvents(); break; case OP_ESCOFF: ic->escOn = false; ic->myEscape = 0; break; default: error("Interpret() - Unknown opcode"); } // check for stack under-overflow assert(ic->sp >= 0 && ic->sp < PCODE_STACK_SIZE); ic->ip = ip; ic->fragmentPtr = wkEntry; } while (!ic->bHalt); // make sure stack is unwound assert(ic->sp == 0); FreeInterpretContextPi(ic); }