mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
643 lines
17 KiB
C++
643 lines
17 KiB
C++
//-----------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) 2015-2017 David Hill
|
|
//
|
|
// See COPYING for license information.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Thread execution.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "Thread.hpp"
|
|
|
|
#include "Array.hpp"
|
|
#include "Code.hpp"
|
|
#include "Environment.hpp"
|
|
#include "Function.hpp"
|
|
#include "Jump.hpp"
|
|
#include "Module.hpp"
|
|
#include "Scope.hpp"
|
|
#include "Script.hpp"
|
|
|
|
|
|
//----------------------------------------------------------------------------|
|
|
// Macros |
|
|
//
|
|
|
|
//
|
|
// ACSVM_DynamicGoto
|
|
//
|
|
// If nonzero, enables use of dynamic goto labels in core interpreter loop.
|
|
// Currently, only gcc syntax is supported.
|
|
//
|
|
#ifndef ACSVM_DynamicGoto
|
|
#if defined(__GNUC__)
|
|
#define ACSVM_DynamicGoto 1
|
|
#else
|
|
#define ACSVM_DynamicGoto 0
|
|
#endif
|
|
#endif
|
|
|
|
//
|
|
// BranchTo
|
|
//
|
|
#define BranchTo(target) \
|
|
do \
|
|
{ \
|
|
codePtr = &module->codeV[(target)]; \
|
|
CountBranch(); \
|
|
} \
|
|
while(0)
|
|
|
|
//
|
|
// CountBranch
|
|
//
|
|
// Used to limit the number of branches to prevent infinite no-delay loops.
|
|
//
|
|
#define CountBranch() \
|
|
if(branches && !--branches) \
|
|
{ \
|
|
env->printKill(this, static_cast<Word>(KillType::BranchLimit), 0); \
|
|
goto thread_stop; \
|
|
} \
|
|
else \
|
|
((void)0)
|
|
|
|
//
|
|
// DeclCase
|
|
//
|
|
#if ACSVM_DynamicGoto
|
|
#define DeclCase(name) case_Code##name
|
|
#else
|
|
#define DeclCase(name) case static_cast<Word>(Code::name)
|
|
#endif
|
|
|
|
//
|
|
// NextCase
|
|
//
|
|
#if ACSVM_DynamicGoto
|
|
#define NextCase() goto *cases[*codePtr++]
|
|
#else
|
|
#define NextCase() goto next_case
|
|
#endif
|
|
|
|
//
|
|
// Op_*
|
|
//
|
|
|
|
#define Op_AddU(lop) (dataStk.drop(), (lop) += dataStk[0])
|
|
#define Op_AndU(lop) (dataStk.drop(), (lop) &= dataStk[0])
|
|
#define Op_CmpI_GE(lop) (dataStk.drop(), OpFunc_CmpI_GE(lop, dataStk[0]))
|
|
#define Op_CmpI_GT(lop) (dataStk.drop(), OpFunc_CmpI_GT(lop, dataStk[0]))
|
|
#define Op_CmpI_LE(lop) (dataStk.drop(), OpFunc_CmpI_LE(lop, dataStk[0]))
|
|
#define Op_CmpI_LT(lop) (dataStk.drop(), OpFunc_CmpI_LT(lop, dataStk[0]))
|
|
#define Op_CmpU_EQ(lop) (dataStk.drop(), OpFunc_CmpU_EQ(lop, dataStk[0]))
|
|
#define Op_CmpU_NE(lop) (dataStk.drop(), OpFunc_CmpU_NE(lop, dataStk[0]))
|
|
#define Op_DecU(lop) (--(lop))
|
|
#define Op_DivI(lop) (dataStk.drop(), OpFunc_DivI(lop, dataStk[0]))
|
|
#define Op_DivX(lop) (dataStk.drop(), OpFunc_DivX(lop, dataStk[0]))
|
|
#define Op_Drop(lop) (dataStk.drop(), (lop) = dataStk[0])
|
|
#define Op_IncU(lop) (++(lop))
|
|
#define Op_LAnd(lop) (dataStk.drop(), OpFunc_LAnd(lop, dataStk[0]))
|
|
#define Op_LOrI(lop) (dataStk.drop(), OpFunc_LOrI(lop, dataStk[0]))
|
|
#define Op_ModI(lop) (dataStk.drop(), OpFunc_ModI(lop, dataStk[0]))
|
|
#define Op_MulU(lop) (dataStk.drop(), (lop) *= dataStk[0])
|
|
#define Op_MulX(lop) (dataStk.drop(), OpFunc_MulX(lop, dataStk[0]))
|
|
#define Op_OrIU(lop) (dataStk.drop(), (lop) |= dataStk[0])
|
|
#define Op_OrXU(lop) (dataStk.drop(), (lop) ^= dataStk[0])
|
|
#define Op_ShLU(lop) (dataStk.drop(), (lop) <<= dataStk[0] & 31)
|
|
#define Op_ShRI(lop) (dataStk.drop(), OpFunc_ShRI(lop, dataStk[0]))
|
|
#define Op_SubU(lop) (dataStk.drop(), (lop) -= dataStk[0])
|
|
|
|
//
|
|
// OpSet
|
|
//
|
|
#define OpSet(op) \
|
|
DeclCase(op##_GblArr): \
|
|
Op_##op(scopeGbl->arrV[*codePtr++][dataStk[1]]); dataStk.drop(); \
|
|
NextCase(); \
|
|
DeclCase(op##_GblReg): \
|
|
Op_##op(scopeGbl->regV[*codePtr++]); \
|
|
NextCase(); \
|
|
DeclCase(op##_HubArr): \
|
|
Op_##op(scopeHub->arrV[*codePtr++][dataStk[1]]); dataStk.drop(); \
|
|
NextCase(); \
|
|
DeclCase(op##_HubReg): \
|
|
Op_##op(scopeHub->regV[*codePtr++]); \
|
|
NextCase(); \
|
|
DeclCase(op##_LocArr): \
|
|
Op_##op(localArr[*codePtr++][dataStk[1]]); dataStk.drop(); \
|
|
NextCase(); \
|
|
DeclCase(op##_LocReg): \
|
|
Op_##op(localReg[*codePtr++]); \
|
|
NextCase(); \
|
|
DeclCase(op##_ModArr): \
|
|
Op_##op((*scopeMod->arrV[*codePtr++])[dataStk[1]]); dataStk.drop(); \
|
|
NextCase(); \
|
|
DeclCase(op##_ModReg): \
|
|
Op_##op(*scopeMod->regV[*codePtr++]); \
|
|
NextCase()
|
|
|
|
|
|
//----------------------------------------------------------------------------|
|
|
// Static Functions |
|
|
//
|
|
|
|
namespace ACSVM
|
|
{
|
|
//
|
|
// OpFunc_CmpI_GE
|
|
//
|
|
static inline void OpFunc_CmpI_GE(Word &lop, Word rop)
|
|
{
|
|
lop = static_cast<SWord>(lop) >= static_cast<SWord>(rop);
|
|
}
|
|
|
|
//
|
|
// OpFunc_CmpI_GT
|
|
//
|
|
static inline void OpFunc_CmpI_GT(Word &lop, Word rop)
|
|
{
|
|
lop = static_cast<SWord>(lop) > static_cast<SWord>(rop);
|
|
}
|
|
|
|
//
|
|
// OpFunc_CmpI_LE
|
|
//
|
|
static inline void OpFunc_CmpI_LE(Word &lop, Word rop)
|
|
{
|
|
lop = static_cast<SWord>(lop) <= static_cast<SWord>(rop);
|
|
}
|
|
|
|
//
|
|
// OpFunc_CmpI_LT
|
|
//
|
|
static inline void OpFunc_CmpI_LT(Word &lop, Word rop)
|
|
{
|
|
lop = static_cast<SWord>(lop) < static_cast<SWord>(rop);
|
|
}
|
|
|
|
//
|
|
// OpFunc_CmpU_EQ
|
|
//
|
|
static inline void OpFunc_CmpU_EQ(Word &lop, Word rop)
|
|
{
|
|
lop = lop == rop;
|
|
}
|
|
|
|
//
|
|
// OpFunc_CmpU_NE
|
|
//
|
|
static inline void OpFunc_CmpU_NE(Word &lop, Word rop)
|
|
{
|
|
lop = lop != rop;
|
|
}
|
|
|
|
//
|
|
// OpFunc_DivI
|
|
//
|
|
static inline void OpFunc_DivI(Word &lop, Word rop)
|
|
{
|
|
lop = rop ? static_cast<SWord>(lop) / static_cast<SWord>(rop) : 0;
|
|
}
|
|
|
|
//
|
|
// OpFunc_DivX
|
|
//
|
|
static inline void OpFunc_DivX(Word &lop, Word rop)
|
|
{
|
|
if(rop)
|
|
lop = (SDWord(SWord(lop)) << 16) / SWord(rop);
|
|
else
|
|
lop = 0;
|
|
}
|
|
|
|
//
|
|
// OpFunc_LAnd
|
|
//
|
|
static inline void OpFunc_LAnd(Word &lop, Word rop)
|
|
{
|
|
lop = lop && rop;
|
|
}
|
|
|
|
//
|
|
// OpFunc_LOrI
|
|
//
|
|
static inline void OpFunc_LOrI(Word &lop, Word rop)
|
|
{
|
|
lop = lop || rop;
|
|
}
|
|
|
|
//
|
|
// OpFunc_ModI
|
|
//
|
|
static inline void OpFunc_ModI(Word &lop, Word rop)
|
|
{
|
|
lop = rop ? static_cast<SWord>(lop) % static_cast<SWord>(rop) : 0;
|
|
}
|
|
|
|
//
|
|
// OpFunc_MulX
|
|
//
|
|
static inline void OpFunc_MulX(Word &lop, Word rop)
|
|
{
|
|
lop = DWord(SDWord(SWord(lop)) * SWord(rop)) >> 16;
|
|
}
|
|
|
|
//
|
|
// OpFunc_ShRI
|
|
//
|
|
static inline void OpFunc_ShRI(Word &lop, Word rop)
|
|
{
|
|
// TODO: Implement this without relying on sign-extending shift.
|
|
lop = static_cast<SWord>(lop) >> (rop & 31);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------|
|
|
// Extern Functions |
|
|
//
|
|
|
|
namespace ACSVM
|
|
{
|
|
//
|
|
// Thread::exec
|
|
//
|
|
void Thread::exec()
|
|
{
|
|
if(delay && --delay)
|
|
return;
|
|
|
|
auto branches = env->branchLimit;
|
|
|
|
exec_intr:
|
|
switch(state.state)
|
|
{
|
|
case ThreadState::Inactive: return;
|
|
case ThreadState::Stopped: goto thread_stop;
|
|
case ThreadState::Paused: return;
|
|
|
|
case ThreadState::Running:
|
|
if(delay)
|
|
return;
|
|
break;
|
|
|
|
case ThreadState::WaitScrI:
|
|
if(scopeMap->isScriptActive(scopeMap->findScript(state.data)))
|
|
return;
|
|
state = ThreadState::Running;
|
|
break;
|
|
|
|
case ThreadState::WaitScrS:
|
|
if(scopeMap->isScriptActive(scopeMap->findScript(scopeMap->getString(state.data))))
|
|
return;
|
|
state = ThreadState::Running;
|
|
break;
|
|
|
|
case ThreadState::WaitTag:
|
|
if(!module->env->checkTag(state.type, state.data))
|
|
return;
|
|
state = ThreadState::Running;
|
|
break;
|
|
}
|
|
|
|
#if ACSVM_DynamicGoto
|
|
static void const *const cases[] =
|
|
{
|
|
#define ACSVM_CodeList(name, ...) &&case_Code##name,
|
|
#include "CodeList.hpp"
|
|
};
|
|
#endif
|
|
|
|
#if ACSVM_DynamicGoto
|
|
NextCase();
|
|
#else
|
|
next_case: switch(*codePtr++)
|
|
#endif
|
|
{
|
|
DeclCase(Nop):
|
|
NextCase();
|
|
|
|
DeclCase(Kill):
|
|
module->env->printKill(this, codePtr[0], codePtr[1]);
|
|
goto thread_stop;
|
|
|
|
//================================================
|
|
// Binary operator codes.
|
|
//
|
|
|
|
OpSet(AddU);
|
|
OpSet(AndU);
|
|
OpSet(DivI);
|
|
OpSet(ModI);
|
|
OpSet(MulU);
|
|
OpSet(OrIU);
|
|
OpSet(OrXU);
|
|
OpSet(ShLU);
|
|
OpSet(ShRI);
|
|
OpSet(SubU);
|
|
|
|
DeclCase(AddU): Op_AddU(dataStk[1]); NextCase();
|
|
DeclCase(AndU): Op_AndU(dataStk[1]); NextCase();
|
|
DeclCase(CmpI_GE): Op_CmpI_GE(dataStk[1]); NextCase();
|
|
DeclCase(CmpI_GT): Op_CmpI_GT(dataStk[1]); NextCase();
|
|
DeclCase(CmpI_LE): Op_CmpI_LE(dataStk[1]); NextCase();
|
|
DeclCase(CmpI_LT): Op_CmpI_LT(dataStk[1]); NextCase();
|
|
DeclCase(CmpU_EQ): Op_CmpU_EQ(dataStk[1]); NextCase();
|
|
DeclCase(CmpU_NE): Op_CmpU_NE(dataStk[1]); NextCase();
|
|
DeclCase(DivI): Op_DivI(dataStk[1]); NextCase();
|
|
DeclCase(DivX): Op_DivX(dataStk[1]); NextCase();
|
|
DeclCase(LAnd): Op_LAnd(dataStk[1]); NextCase();
|
|
DeclCase(LOrI): Op_LOrI(dataStk[1]); NextCase();
|
|
DeclCase(ModI): Op_ModI(dataStk[1]); NextCase();
|
|
DeclCase(MulU): Op_MulU(dataStk[1]); NextCase();
|
|
DeclCase(MulX): Op_MulX(dataStk[1]); NextCase();
|
|
DeclCase(OrIU): Op_OrIU(dataStk[1]); NextCase();
|
|
DeclCase(OrXU): Op_OrXU(dataStk[1]); NextCase();
|
|
DeclCase(ShLU): Op_ShLU(dataStk[1]); NextCase();
|
|
DeclCase(ShRI): Op_ShRI(dataStk[1]); NextCase();
|
|
DeclCase(SubU): Op_SubU(dataStk[1]); NextCase();
|
|
|
|
//================================================
|
|
// Call codes.
|
|
//
|
|
|
|
DeclCase(Call_Lit):
|
|
{
|
|
Function *func;
|
|
|
|
func = *codePtr < module->functionV.size() ? module->functionV[*codePtr] : nullptr;
|
|
++codePtr;
|
|
|
|
do_call:
|
|
if(!func) {BranchTo(0); NextCase();}
|
|
|
|
// Reserve stack space.
|
|
callStk.reserve(CallStkSize);
|
|
dataStk.reserve(DataStkSize);
|
|
|
|
// Push call frame.
|
|
callStk.push({codePtr, module, scopeMod, localArr.size(), localReg.size()});
|
|
|
|
// Apply function data.
|
|
codePtr = &func->module->codeV[func->codeIdx];
|
|
module = func->module;
|
|
scopeMod = scopeMap->getModuleScope(module);
|
|
localArr.alloc(func->locArrC);
|
|
localReg.alloc(func->locRegC);
|
|
|
|
// Read arguments.
|
|
dataStk.drop(func->argC);
|
|
memcpy(&localReg[0], &dataStk[0], func->argC * sizeof(Word));
|
|
|
|
NextCase();
|
|
|
|
DeclCase(Call_Stk):
|
|
dataStk.drop();
|
|
func = env->getFunction(dataStk[0]);
|
|
goto do_call;
|
|
}
|
|
|
|
DeclCase(CallFunc):
|
|
{
|
|
Word argc = *codePtr++;
|
|
Word func = *codePtr++;
|
|
dataStk.drop(argc);
|
|
if(env->callFunc(this, func, &dataStk[0], argc))
|
|
goto exec_intr;
|
|
}
|
|
NextCase();
|
|
|
|
DeclCase(CallFunc_Lit):
|
|
{
|
|
Word argc = *codePtr++;
|
|
Word func = *codePtr++;
|
|
Word const *argv = codePtr;
|
|
codePtr += argc;
|
|
if(env->callFunc(this, func, argv, argc))
|
|
goto exec_intr;
|
|
}
|
|
NextCase();
|
|
|
|
DeclCase(CallSpec):
|
|
{
|
|
Word argc = *codePtr++;
|
|
Word spec = *codePtr++;
|
|
dataStk.drop(argc);
|
|
env->callSpec(this, spec, &dataStk[0], argc);
|
|
}
|
|
NextCase();
|
|
|
|
DeclCase(CallSpec_Lit):
|
|
{
|
|
Word argc = *codePtr++;
|
|
Word spec = *codePtr++;
|
|
Word const *argv = codePtr;
|
|
codePtr += argc;
|
|
env->callSpec(this, spec, argv, argc);
|
|
}
|
|
NextCase();
|
|
|
|
DeclCase(CallSpec_R1):
|
|
{
|
|
Word argc = *codePtr++;
|
|
Word spec = *codePtr++;
|
|
dataStk.drop(argc);
|
|
dataStk.push(env->callSpec(this, spec, &dataStk[0], argc));
|
|
}
|
|
NextCase();
|
|
|
|
DeclCase(Retn):
|
|
// If no call frames left, terminate the thread.
|
|
if(callStk.empty())
|
|
goto thread_stop;
|
|
|
|
// Apply call frame.
|
|
codePtr = callStk[1].codePtr;
|
|
module = callStk[1].module;
|
|
scopeMod = callStk[1].scopeMod;
|
|
localArr.free(callStk[1].locArrC);
|
|
localReg.free(callStk[1].locRegC);
|
|
|
|
// Drop call frame.
|
|
callStk.drop();
|
|
|
|
NextCase();
|
|
|
|
//================================================
|
|
// Drop codes.
|
|
//
|
|
|
|
OpSet(Drop);
|
|
|
|
DeclCase(Drop_Nul):
|
|
dataStk.drop();
|
|
NextCase();
|
|
|
|
DeclCase(Drop_ScrRet):
|
|
dataStk.drop();
|
|
result = dataStk[0];
|
|
NextCase();
|
|
|
|
//================================================
|
|
// Jump codes.
|
|
//
|
|
|
|
DeclCase(Jcnd_Lit):
|
|
if(dataStk[1] == *codePtr++)
|
|
{
|
|
dataStk.drop();
|
|
BranchTo(*codePtr);
|
|
}
|
|
else
|
|
++codePtr;
|
|
NextCase();
|
|
|
|
DeclCase(Jcnd_Nil):
|
|
if(dataStk.drop(), dataStk[0])
|
|
++codePtr;
|
|
else
|
|
BranchTo(*codePtr);
|
|
NextCase();
|
|
|
|
DeclCase(Jcnd_Tab):
|
|
if(auto jump = module->jumpMapV[*codePtr++].table.find(dataStk[1]))
|
|
{
|
|
dataStk.drop();
|
|
BranchTo(*jump);
|
|
}
|
|
NextCase();
|
|
|
|
DeclCase(Jcnd_Tru):
|
|
if(dataStk.drop(), dataStk[0])
|
|
BranchTo(*codePtr);
|
|
else
|
|
++codePtr;
|
|
NextCase();
|
|
|
|
DeclCase(Jump_Lit):
|
|
BranchTo(*codePtr);
|
|
NextCase();
|
|
|
|
DeclCase(Jump_Stk):
|
|
dataStk.drop();
|
|
BranchTo(dataStk[0] < module->jumpV.size() ? module->jumpV[dataStk[0]].codeIdx : 0);
|
|
NextCase();
|
|
|
|
//================================================
|
|
// Push codes.
|
|
//
|
|
|
|
DeclCase(Pfun_Lit):
|
|
if(*codePtr < module->functionV.size())
|
|
dataStk.push(module->functionV[*codePtr]->idx);
|
|
else
|
|
dataStk.push(0);
|
|
++codePtr;
|
|
NextCase();
|
|
|
|
DeclCase(Pstr_Stk):
|
|
if(dataStk[1] < module->stringV.size())
|
|
dataStk[1] = ~module->stringV[dataStk[1]]->idx;
|
|
NextCase();
|
|
|
|
DeclCase(Push_GblArr): dataStk[1] = scopeGbl->arrV[*codePtr++].find(dataStk[1]); NextCase();
|
|
DeclCase(Push_GblReg): dataStk.push(scopeGbl->regV[*codePtr++]); NextCase();
|
|
DeclCase(Push_HubArr): dataStk[1] = scopeHub->arrV[*codePtr++].find(dataStk[1]); NextCase();
|
|
DeclCase(Push_HubReg): dataStk.push(scopeHub->regV[*codePtr++]); NextCase();
|
|
DeclCase(Push_Lit): dataStk.push(*codePtr++); NextCase();
|
|
DeclCase(Push_LitArr): for(auto i = *codePtr++; i--;) dataStk.push(*codePtr++); NextCase();
|
|
DeclCase(Push_LocArr): dataStk[1] = localArr[*codePtr++].find(dataStk[1]); NextCase();
|
|
DeclCase(Push_LocReg): dataStk.push(localReg[*codePtr++]); NextCase();
|
|
DeclCase(Push_ModArr): dataStk[1] = scopeMod->arrV[*codePtr++]->find(dataStk[1]); NextCase();
|
|
DeclCase(Push_ModReg): dataStk.push(*scopeMod->regV[*codePtr++]); NextCase();
|
|
|
|
DeclCase(Push_StrArs):
|
|
dataStk.drop();
|
|
dataStk[1] = scopeMap->getString(dataStk[1])->get(dataStk[0]);
|
|
NextCase();
|
|
|
|
//================================================
|
|
// Script control codes.
|
|
//
|
|
|
|
DeclCase(ScrDelay):
|
|
dataStk.drop();
|
|
delay = dataStk[0];
|
|
goto exec_intr;
|
|
|
|
DeclCase(ScrDelay_Lit):
|
|
delay = *codePtr++;
|
|
goto exec_intr;
|
|
|
|
DeclCase(ScrHalt):
|
|
state = ThreadState::Paused;
|
|
goto exec_intr;
|
|
|
|
DeclCase(ScrRestart):
|
|
BranchTo(script->codeIdx);
|
|
NextCase();
|
|
|
|
DeclCase(ScrTerm):
|
|
goto thread_stop;
|
|
|
|
DeclCase(ScrWaitI):
|
|
dataStk.drop();
|
|
state = {ThreadState::WaitScrI, dataStk[0]};
|
|
goto exec_intr;
|
|
|
|
DeclCase(ScrWaitI_Lit):
|
|
state = {ThreadState::WaitScrI, *codePtr++};
|
|
goto exec_intr;
|
|
|
|
DeclCase(ScrWaitS):
|
|
dataStk.drop();
|
|
state = {ThreadState::WaitScrS, dataStk[0]};
|
|
goto exec_intr;
|
|
|
|
DeclCase(ScrWaitS_Lit):
|
|
state = {ThreadState::WaitScrS, *codePtr++};
|
|
goto exec_intr;
|
|
|
|
//================================================
|
|
// Stack control codes.
|
|
//
|
|
|
|
DeclCase(Copy):
|
|
{auto temp = dataStk[1]; dataStk.push(temp);}
|
|
NextCase();
|
|
|
|
DeclCase(Swap):
|
|
std::swap(dataStk[2], dataStk[1]);
|
|
NextCase();
|
|
|
|
//================================================
|
|
// Unary operator codes.
|
|
//
|
|
|
|
OpSet(DecU);
|
|
OpSet(IncU);
|
|
|
|
DeclCase(InvU):
|
|
dataStk[1] = ~dataStk[1];
|
|
NextCase();
|
|
|
|
DeclCase(NegI):
|
|
dataStk[1] = ~dataStk[1] + 1;
|
|
NextCase();
|
|
|
|
DeclCase(NotU):
|
|
dataStk[1] = !dataStk[1];
|
|
NextCase();
|
|
}
|
|
|
|
thread_stop:
|
|
stop();
|
|
}
|
|
}
|
|
|
|
// EOF
|
|
|