/* ** vmexec.cpp ** **--------------------------------------------------------------------------- ** Copyright -2016 Randy Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include #include #include "v_video.h" #include "s_soundinternal.h" #include "basics.h" //#include "r_state.h" #include "stats.h" #include "vmintern.h" #include "types.h" #include "basics.h" #include "texturemanager.h" #include "palutil.h" extern cycle_t VMCycles[10]; extern int VMCalls[10]; // THe sprite ID to string cast is game specific so let's do it with a callback to remove the dependency and allow easier reuse. void (*VM_CastSpriteIDToString)(FString* a, unsigned int b) = [](FString* a, unsigned int b) { a->Format("%d", b); }; // intentionally implemented in a different source file to prevent inlining. #if 0 void ThrowVMException(VMException *x); #endif #define IMPLEMENT_VMEXEC #if !defined(COMPGOTO) && defined(__GNUC__) #define COMPGOTO 1 #endif #if COMPGOTO #define OP(x) x #define NEXTOP do { pc++; unsigned op = pc->op; a = pc->a; goto *ops[op]; } while(0) #else #define OP(x) case OP_##x #define NEXTOP pc++; break #endif #define luai_nummod(a,b) ((a) - floor((a)/(b))*(b)) #define A (pc[0].a) #define B (pc[0].b) #define C (pc[0].c) #define Cs (pc[0].cs) #define BC (pc[0].i16u) #define BCs (pc[0].i16) #define ABCs (pc[0].i24) #define JMPOFS(x) ((x)->i24) #define KC (konstd[C]) #define RC (reg.d[C]) #define PA (reg.a[A]) #define PB (reg.a[B]) #define ASSERTD(x) assert((unsigned)(x) < f->NumRegD) #define ASSERTF(x) assert((unsigned)(x) < f->NumRegF) #define ASSERTA(x) assert((unsigned)(x) < f->NumRegA) #define ASSERTS(x) assert((unsigned)(x) < f->NumRegS) #define ASSERTKD(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstD) #define ASSERTKF(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstF) #define ASSERTKA(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstA) #define ASSERTKS(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstS) #define CMPJMP(test) \ if ((test) == (a & CMP_CHECK)) { \ assert(pc[1].op == OP_JMP); \ pc += 1 + JMPOFS(pc+1); \ } else { \ pc += 1; \ } #define GETADDR(a,o,x) \ if (a == NULL) { ThrowAbortException(x, nullptr); return 0; } \ ptr = (VM_SBYTE *)a + o #ifdef NDEBUG #define WAS_NDEBUG 1 #else #define WAS_NDEBUG 0 #endif #if WAS_NDEBUG #undef NDEBUG #endif #undef assert #include struct VMExec_Checked { #include "vmexec.h" }; #if WAS_NDEBUG #define NDEBUG #endif #if !WAS_NDEBUG #define NDEBUG #endif #undef assert #include struct VMExec_Unchecked { #include "vmexec.h" }; #if !WAS_NDEBUG #undef NDEBUG #endif #undef assert #include int (*VMExec)(VMFunction *func, VMValue *params, int numparams, VMReturn *ret, int numret) = #ifdef NDEBUG VMExec_Unchecked::Exec #else VMExec_Checked::Exec #endif ; // Note: If the VM is being used in multiple threads, this should be declared as thread_local. // ZDoom doesn't need this at the moment so this is disabled. thread_local VMFrameStack GlobalVMStack; //=========================================================================== // // VMSelectEngine // // Selects the VM engine, either checked or unchecked. Default will decide // based on the NDEBUG preprocessor definition. // //=========================================================================== void VMSelectEngine(EVMEngine engine) { switch (engine) { case VMEngine_Default: #ifdef NDEBUG VMExec = VMExec_Unchecked::Exec; #else #endif VMExec = VMExec_Checked::Exec; break; case VMEngine_Unchecked: VMExec = VMExec_Unchecked::Exec; break; case VMEngine_Checked: VMExec = VMExec_Checked::Exec; break; } } //=========================================================================== // // VMFillParams // // Takes parameters from the parameter stack and stores them in the callee's // registers. // //=========================================================================== void VMFillParams(VMValue *params, VMFrame *callee, int numparam) { unsigned int regd, regf, regs, rega; VMScriptFunction *calleefunc = static_cast(callee->Func); const VMRegisters calleereg(callee); assert(calleefunc != NULL && !(calleefunc->VarFlags & VARF_Native)); assert(numparam == calleefunc->NumArgs); assert(REGT_INT == 0 && REGT_FLOAT == 1 && REGT_STRING == 2 && REGT_POINTER == 3); regd = regf = regs = rega = 0; const uint8_t *reginfo = calleefunc->RegTypes; assert(reginfo != nullptr); for (int i = 0; i < calleefunc->NumArgs; ++i, reginfo++) { // copy all parameters to the local registers. VMValue &p = params[i]; if (*reginfo < REGT_STRING) { if (*reginfo == REGT_INT) { calleereg.d[regd++] = p.i; } else // p.Type == REGT_FLOAT { calleereg.f[regf++] = p.f; } } else if (*reginfo == REGT_STRING) { calleereg.s[regs++] = p.s(); } else { assert(*reginfo == REGT_POINTER); calleereg.a[rega++] = p.a; } } } #ifndef NDEBUG bool AssertObject(void * ob) { auto obj = (DObject*)ob; if (obj == nullptr) return true; #ifdef _MSC_VER __try { return obj->MagicID == DObject::MAGIC_ID; } __except (1) { return false; } #else // No SEH on non-Microsoft compilers. :( return obj->MagicID == DObject::MAGIC_ID; #endif } #endif