sourcepawn/jit/interpreter.cpp
changeset 4116 3e58428d988f
child 4156 319e80e8e97f
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/sourcepawn/jit/interpreter.cpp	Sun Sep 01 00:23:44 2013 -0700
     1.3 @@ -0,0 +1,965 @@
     1.4 +// vim: set ts=8 sts=2 sw=2 tw=99 et:
     1.5 +//
     1.6 +// This file is part of SourcePawn.
     1.7 +// 
     1.8 +// SourcePawn is free software: you can redistribute it and/or modify
     1.9 +// it under the terms of the GNU General Public License as published by
    1.10 +// the Free Software Foundation, either version 3 of the License, or
    1.11 +// (at your option) any later version.
    1.12 +// 
    1.13 +// SourcePawn is distributed in the hope that it will be useful,
    1.14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.16 +// GNU General Public License for more details.
    1.17 +// 
    1.18 +// You should have received a copy of the GNU General Public License
    1.19 +// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
    1.20 +#include <limits.h>
    1.21 +#include <string.h>
    1.22 +#include "interpreter.h"
    1.23 +#include "opcodes.h"
    1.24 +#include "watchdog_timer.h"
    1.25 +
    1.26 +#define STACK_MARGIN 64
    1.27 +
    1.28 +using namespace SourcePawn;
    1.29 +
    1.30 +static inline bool
    1.31 +IsValidOffset(uint32_t cip)
    1.32 +{
    1.33 +  return cip % 4 == 0;
    1.34 +}
    1.35 +
    1.36 +static inline cell_t
    1.37 +Read(const sp_plugin_t *plugin, cell_t offset)
    1.38 +{
    1.39 +  return *reinterpret_cast<cell_t *>(plugin->memory + offset);
    1.40 +}
    1.41 +
    1.42 +static inline void
    1.43 +Write(const sp_plugin_t *plugin, cell_t offset, cell_t value)
    1.44 +{
    1.45 +  *reinterpret_cast<cell_t *>(plugin->memory + offset) = value;
    1.46 +}
    1.47 +
    1.48 +static inline cell_t *
    1.49 +Jump(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t target)
    1.50 +{
    1.51 +  if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) {
    1.52 +    ctx->err = SP_ERROR_INVALID_INSTRUCTION;
    1.53 +    return NULL;
    1.54 +  }
    1.55 +
    1.56 +  return reinterpret_cast<cell_t *>(plugin->pcode + target);
    1.57 +}
    1.58 +
    1.59 +static inline cell_t *
    1.60 +JumpTarget(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *cip, bool cond)
    1.61 +{
    1.62 +  if (!cond)
    1.63 +    return cip + 1;
    1.64 +
    1.65 +  cell_t target = *cip;
    1.66 +  if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) {
    1.67 +    ctx->err = SP_ERROR_INVALID_INSTRUCTION;
    1.68 +    return NULL;
    1.69 +  }
    1.70 +
    1.71 +  cell_t *next = reinterpret_cast<cell_t *>(plugin->pcode + target);
    1.72 +  if (next < cip && !g_WatchdogTimer.HandleInterrupt()) {
    1.73 +    ctx->err = SP_ERROR_TIMEOUT;
    1.74 +    return NULL;
    1.75 +  }
    1.76 +
    1.77 +  return next;
    1.78 +}
    1.79 +
    1.80 +static inline bool
    1.81 +CheckAddress(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *stk, cell_t addr)
    1.82 +{
    1.83 +  if (uint32_t(addr) >= plugin->mem_size) {
    1.84 +    ctx->err = SP_ERROR_MEMACCESS;
    1.85 +    return false;
    1.86 +  }
    1.87 +
    1.88 +  if (addr < ctx->hp)
    1.89 +    return true;
    1.90 +
    1.91 +  if (reinterpret_cast<cell_t *>(plugin->memory + addr) < stk) {
    1.92 +    ctx->err = SP_ERROR_MEMACCESS;
    1.93 +    return false;
    1.94 +  }
    1.95 +
    1.96 +  return true;
    1.97 +}
    1.98 +
    1.99 +int
   1.100 +PopTrackerAndSetHeap(BaseRuntime *rt)
   1.101 +{
   1.102 +  sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
   1.103 +  tracker_t *trk = ctx->tracker;
   1.104 +  assert(trk->pCur > trk->pBase);
   1.105 +
   1.106 +  trk->pCur--;
   1.107 +  if (trk->pCur < trk->pBase)
   1.108 +    return SP_ERROR_TRACKER_BOUNDS;
   1.109 +
   1.110 +  ucell_t amt = *trk->pCur;
   1.111 +  if (amt > (ctx->hp - rt->plugin()->data_size))
   1.112 +    return SP_ERROR_HEAPMIN;
   1.113 +
   1.114 +  ctx->hp -= amt;
   1.115 +  return SP_ERROR_NONE;
   1.116 +}
   1.117 +
   1.118 +int
   1.119 +PushTracker(sp_context_t *ctx, size_t amount)
   1.120 +{
   1.121 +  tracker_t *trk = ctx->tracker;
   1.122 +
   1.123 +  if ((size_t)(trk->pCur - trk->pBase) >= trk->size)
   1.124 +    return SP_ERROR_TRACKER_BOUNDS;
   1.125 +
   1.126 +  if (trk->pCur + 1 - (trk->pBase + trk->size) == 0) {
   1.127 +    size_t disp = trk->size - 1;
   1.128 +    trk->size *= 2;
   1.129 +    trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t));
   1.130 +
   1.131 +    if (!trk->pBase)
   1.132 +      return SP_ERROR_TRACKER_BOUNDS;
   1.133 +
   1.134 +    trk->pCur = trk->pBase + disp;
   1.135 +  }
   1.136 +
   1.137 +  *trk->pCur++ = amount;
   1.138 +  return SP_ERROR_NONE;
   1.139 +}
   1.140 +
   1.141 +cell_t
   1.142 +NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params)
   1.143 +{
   1.144 +  cell_t save_sp = ctx->sp;
   1.145 +  cell_t save_hp = ctx->hp;
   1.146 +
   1.147 +  ctx->n_idx = native_idx;
   1.148 +
   1.149 +  sp_native_t *native = &ctx->plugin->natives[native_idx];
   1.150 +
   1.151 +  if (native->status == SP_NATIVE_UNBOUND) {
   1.152 +    ctx->n_err = SP_ERROR_INVALID_NATIVE;
   1.153 +    return 0;
   1.154 +  }
   1.155 +
   1.156 +  cell_t result = native->pfn(ctx->basecx, params);
   1.157 +
   1.158 +  if (ctx->n_err != SP_ERROR_NONE)
   1.159 +    return result;
   1.160 +
   1.161 +  if (save_sp != ctx->sp) {
   1.162 +    ctx->n_err = SP_ERROR_STACKLEAK;
   1.163 +    return result;
   1.164 +  }
   1.165 +  if (save_hp != ctx->hp) {
   1.166 +    ctx->n_err = SP_ERROR_HEAPLEAK;
   1.167 +    return result;
   1.168 +  }
   1.169 +
   1.170 +  return result;
   1.171 +}
   1.172 +
   1.173 +static inline bool
   1.174 +GenerateArray(BaseRuntime *rt, sp_context_t *ctx, cell_t dims, cell_t *stk, bool autozero)
   1.175 +{
   1.176 +  if (dims == 1) {
   1.177 +    uint32_t size = *stk;
   1.178 +    if (size == 0 || !ke::IsUint32MultiplySafe(size, 4)) {
   1.179 +      ctx->err = SP_ERROR_ARRAY_TOO_BIG;
   1.180 +      return false;
   1.181 +    }
   1.182 +    *stk = ctx->hp;
   1.183 +
   1.184 +    uint32_t bytes = size * 4;
   1.185 +
   1.186 +    ctx->hp += bytes;
   1.187 +    if (uintptr_t(ctx->plugin->memory + ctx->hp) >= uintptr_t(stk)) {
   1.188 +      ctx->err = SP_ERROR_HEAPLOW;
   1.189 +      return false;
   1.190 +    }
   1.191 +
   1.192 +    if ((ctx->err = PushTracker(ctx, bytes)) != SP_ERROR_NONE)
   1.193 +      return false;
   1.194 +
   1.195 +    if (autozero)
   1.196 +      memset(ctx->plugin->memory + ctx->hp, 0, bytes);
   1.197 +
   1.198 +    return true;
   1.199 +  }
   1.200 +
   1.201 +  if ((ctx->err = GenerateFullArray(rt, dims, stk, autozero)) != SP_ERROR_NONE)
   1.202 +    return false;
   1.203 +
   1.204 +  return true;
   1.205 +}
   1.206 +
   1.207 +int
   1.208 +Interpret(BaseRuntime *rt, uint32_t aCodeStart, cell_t *rval)
   1.209 +{
   1.210 +  const sp_plugin_t *plugin = rt->plugin();
   1.211 +  cell_t *code = reinterpret_cast<cell_t *>(plugin->pcode);
   1.212 +  cell_t *codeend = reinterpret_cast<cell_t *>(plugin->pcode + plugin->pcode_size);
   1.213 +
   1.214 +  if (!IsValidOffset(aCodeStart) || aCodeStart > plugin->pcode_size)
   1.215 +    return SP_ERROR_INVALID_INSTRUCTION;
   1.216 +
   1.217 +  sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
   1.218 +  ctx->err = SP_ERROR_NONE;
   1.219 +
   1.220 +  // Save the original frm. BaseContext won't, and if we error, we won't hit
   1.221 +  // the stack unwinding code.
   1.222 +  cell_t orig_frm = ctx->frm;
   1.223 +
   1.224 +  cell_t pri = 0;
   1.225 +  cell_t alt = 0;
   1.226 +  cell_t *cip = code + (aCodeStart / 4);
   1.227 +  cell_t *stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
   1.228 +
   1.229 +  for (;;) {
   1.230 +    if (cip >= codeend) {
   1.231 +      ctx->err = SP_ERROR_INVALID_INSTRUCTION;
   1.232 +      goto error;
   1.233 +    }
   1.234 +
   1.235 +#if 0
   1.236 +    SpewOpcode(plugin, reinterpret_cast<cell_t *>(plugin->pcode + aCodeStart), cip);
   1.237 +#endif
   1.238 +
   1.239 +    OPCODE op = (OPCODE)*cip++;
   1.240 +
   1.241 +    switch (op) {
   1.242 +      case OP_MOVE_PRI:
   1.243 +        pri = alt;
   1.244 +        break;
   1.245 +      case OP_MOVE_ALT:
   1.246 +        alt = pri;
   1.247 +        break;
   1.248 +
   1.249 +      case OP_XCHG:
   1.250 +      {
   1.251 +        cell_t tmp = pri;
   1.252 +        pri = alt;
   1.253 +        alt = tmp;
   1.254 +        break;
   1.255 +      }
   1.256 +
   1.257 +      case OP_ZERO:
   1.258 +        Write(plugin, *cip++, 0);
   1.259 +        break;
   1.260 +
   1.261 +      case OP_ZERO_S:
   1.262 +        Write(plugin, *cip++, 0);
   1.263 +        break;
   1.264 +
   1.265 +      case OP_PUSH_PRI:
   1.266 +        *--stk = pri;
   1.267 +        break;
   1.268 +      case OP_PUSH_ALT:
   1.269 +        *--stk = alt;
   1.270 +        break;
   1.271 +
   1.272 +      case OP_PUSH_C:
   1.273 +      case OP_PUSH2_C:
   1.274 +      case OP_PUSH3_C:
   1.275 +      case OP_PUSH4_C:
   1.276 +      case OP_PUSH5_C:
   1.277 +      {
   1.278 +        int n = 1;
   1.279 +        if (op >= OP_PUSH2_C)
   1.280 +          n = ((op - OP_PUSH2_C) / 4) + 2;
   1.281 +
   1.282 +        int i = 1;
   1.283 +        do {
   1.284 +          *--stk = *cip++;
   1.285 +        } while (i++ < n);
   1.286 +        break;
   1.287 +      }
   1.288 +
   1.289 +      case OP_PUSH_ADR:
   1.290 +      case OP_PUSH2_ADR:
   1.291 +      case OP_PUSH3_ADR:
   1.292 +      case OP_PUSH4_ADR:
   1.293 +      case OP_PUSH5_ADR:
   1.294 +      {
   1.295 +        int n = 1;
   1.296 +        if (op >= OP_PUSH2_ADR)
   1.297 +          n = ((op - OP_PUSH2_ADR) / 4) + 2;
   1.298 +
   1.299 +        int i = 1;
   1.300 +
   1.301 +        do {
   1.302 +          cell_t addr = ctx->frm + *cip++;
   1.303 +          *--stk = addr;
   1.304 +        } while (i++ < n);
   1.305 +        break;
   1.306 +      }
   1.307 +
   1.308 +      case OP_PUSH_S:
   1.309 +      case OP_PUSH2_S:
   1.310 +      case OP_PUSH3_S:
   1.311 +      case OP_PUSH4_S:
   1.312 +      case OP_PUSH5_S:
   1.313 +      {
   1.314 +        int n = 1;
   1.315 +        if (op >= OP_PUSH2_S)
   1.316 +          n = ((op - OP_PUSH2_S) / 4) + 2;
   1.317 +
   1.318 +        int i = 1;
   1.319 +        do {
   1.320 +          cell_t value = Read(plugin, ctx->frm + *cip++);
   1.321 +          *--stk = value;
   1.322 +        } while (i++ < n);
   1.323 +        break;
   1.324 +      }
   1.325 +
   1.326 +      case OP_PUSH:
   1.327 +      case OP_PUSH2:
   1.328 +      case OP_PUSH3:
   1.329 +      case OP_PUSH4:
   1.330 +      case OP_PUSH5:
   1.331 +      {
   1.332 +        int n = 1;
   1.333 +        if (op >= OP_PUSH2)
   1.334 +          n = ((op - OP_PUSH2) / 4) + 2;
   1.335 +
   1.336 +        int i = 1;
   1.337 +        do {
   1.338 +          cell_t value = Read(plugin, *cip++);
   1.339 +          *--stk = value;
   1.340 +        } while (i++ < n);
   1.341 +        break;
   1.342 +      }
   1.343 +
   1.344 +      case OP_ZERO_PRI:
   1.345 +        pri = 0;
   1.346 +        break;
   1.347 +      case OP_ZERO_ALT:
   1.348 +        alt = 0;
   1.349 +        break;
   1.350 +
   1.351 +      case OP_ADD:
   1.352 +        pri += alt;
   1.353 +        break;
   1.354 +
   1.355 +      case OP_SUB:
   1.356 +        pri -= alt;
   1.357 +        break;
   1.358 +
   1.359 +      case OP_SUB_ALT:
   1.360 +        pri = alt - pri;
   1.361 +        break;
   1.362 +
   1.363 +      case OP_PROC:
   1.364 +      {
   1.365 +        *--stk = ctx->frm;
   1.366 +        *--stk = 0;
   1.367 +        ctx->frm = uintptr_t(stk) - uintptr_t(plugin->memory);
   1.368 +        break;
   1.369 +      }
   1.370 +
   1.371 +      case OP_IDXADDR_B:
   1.372 +        pri <<= *cip++;
   1.373 +        pri += alt;
   1.374 +        break;
   1.375 +
   1.376 +      case OP_SHL:
   1.377 +        pri <<= alt;
   1.378 +        break;
   1.379 +
   1.380 +      case OP_SHR:
   1.381 +        pri = unsigned(pri) >> unsigned(alt);
   1.382 +        break;
   1.383 +
   1.384 +      case OP_SSHR:
   1.385 +        pri >>= alt;
   1.386 +        break;
   1.387 +
   1.388 +      case OP_SHL_C_PRI:
   1.389 +        pri <<= *cip++;
   1.390 +        break;
   1.391 +      case OP_SHL_C_ALT:
   1.392 +        alt <<= *cip++;
   1.393 +        break;
   1.394 +
   1.395 +      case OP_SHR_C_PRI:
   1.396 +        pri >>= *cip++;
   1.397 +        break;
   1.398 +      case OP_SHR_C_ALT:
   1.399 +        alt >>= *cip++;
   1.400 +        break;
   1.401 +
   1.402 +      case OP_SMUL:
   1.403 +        pri *= alt;
   1.404 +        break;
   1.405 +
   1.406 +      case OP_NOT:
   1.407 +        pri = pri ? 0 : 1;
   1.408 +        break;
   1.409 +
   1.410 +      case OP_NEG:
   1.411 +        pri = -pri;
   1.412 +        break;
   1.413 +
   1.414 +      case OP_XOR:
   1.415 +        pri ^= alt;
   1.416 +        break;
   1.417 +
   1.418 +      case OP_OR:
   1.419 +        pri |= alt;
   1.420 +        break;
   1.421 +
   1.422 +      case OP_AND:
   1.423 +        pri &= alt;
   1.424 +        break;
   1.425 +
   1.426 +      case OP_INVERT:
   1.427 +        pri = ~pri;
   1.428 +        break;
   1.429 +
   1.430 +      case OP_ADD_C:
   1.431 +        pri += *cip++;
   1.432 +        break;
   1.433 +
   1.434 +      case OP_SMUL_C:
   1.435 +        pri *= *cip++;
   1.436 +        break;
   1.437 +
   1.438 +      case OP_EQ:
   1.439 +        pri = pri == alt;
   1.440 +        break;
   1.441 +
   1.442 +      case OP_NEQ:
   1.443 +        pri = pri != alt;
   1.444 +        break;
   1.445 +
   1.446 +      case OP_SLESS:
   1.447 +        pri = pri < alt;
   1.448 +        break;
   1.449 +
   1.450 +      case OP_SLEQ:
   1.451 +        pri = pri <= alt;
   1.452 +        break;
   1.453 +
   1.454 +      case OP_SGRTR:
   1.455 +        pri = pri > alt;
   1.456 +        break;
   1.457 +        
   1.458 +      case OP_SGEQ:
   1.459 +        pri = pri >= alt;
   1.460 +        break;
   1.461 +
   1.462 +      case OP_EQ_C_PRI:
   1.463 +        pri = pri == *cip++;
   1.464 +        break;
   1.465 +      case OP_EQ_C_ALT:
   1.466 +        pri = alt == *cip++;
   1.467 +        break;
   1.468 +
   1.469 +      case OP_INC_PRI:
   1.470 +        pri++;
   1.471 +        break;
   1.472 +      case OP_INC_ALT:
   1.473 +        alt++;
   1.474 +        break;
   1.475 +
   1.476 +      case OP_INC:
   1.477 +      {
   1.478 +        cell_t offset = *cip++;
   1.479 +        Write(plugin, offset, Read(plugin, offset) + 1);
   1.480 +        break;
   1.481 +      }
   1.482 +
   1.483 +      case OP_INC_S:
   1.484 +      {
   1.485 +        cell_t offset = *cip++;
   1.486 +        cell_t value = Read(plugin, ctx->frm + offset);
   1.487 +        Write(plugin, ctx->frm + offset, value + 1);
   1.488 +        break;
   1.489 +      }
   1.490 +
   1.491 +      case OP_INC_I:
   1.492 +        if (!CheckAddress(plugin, ctx, stk, pri))
   1.493 +          goto error;
   1.494 +        Write(plugin, pri, Read(plugin, pri) + 1);
   1.495 +        break;
   1.496 +
   1.497 +      case OP_DEC_PRI:
   1.498 +        pri--;
   1.499 +        break;
   1.500 +      case OP_DEC_ALT:
   1.501 +        alt--;
   1.502 +        break;
   1.503 +
   1.504 +      case OP_DEC:
   1.505 +      {
   1.506 +        cell_t offset = *cip++;
   1.507 +        Write(plugin, offset, Read(plugin, offset) - 1);
   1.508 +        break;
   1.509 +      }
   1.510 +
   1.511 +      case OP_DEC_S:
   1.512 +      {
   1.513 +        cell_t offset = *cip++;
   1.514 +        cell_t value = Read(plugin, ctx->frm + offset);
   1.515 +        Write(plugin, ctx->frm + offset, value - 1);
   1.516 +        break;
   1.517 +      }
   1.518 +
   1.519 +      case OP_DEC_I:
   1.520 +        if (!CheckAddress(plugin, ctx, stk, pri))
   1.521 +          goto error;
   1.522 +        Write(plugin, pri, Read(plugin, pri) - 1);
   1.523 +        break;
   1.524 +
   1.525 +      case OP_LOAD_PRI:
   1.526 +        pri = Read(plugin, *cip++);
   1.527 +        break;
   1.528 +      case OP_LOAD_ALT:
   1.529 +        alt = Read(plugin, *cip++);
   1.530 +        break;
   1.531 +
   1.532 +      case OP_LOAD_S_PRI:
   1.533 +        pri = Read(plugin, ctx->frm + *cip++);
   1.534 +        break;
   1.535 +      case OP_LOAD_S_ALT:
   1.536 +        alt = Read(plugin, ctx->frm + *cip++);
   1.537 +        break;
   1.538 +
   1.539 +      case OP_LOAD_S_BOTH:
   1.540 +        pri = Read(plugin, ctx->frm + *cip++);
   1.541 +        alt = Read(plugin, ctx->frm + *cip++);
   1.542 +        break;
   1.543 +
   1.544 +      case OP_LREF_S_PRI:
   1.545 +      {
   1.546 +        pri = Read(plugin, ctx->frm + *cip++);
   1.547 +        pri = Read(plugin, pri);
   1.548 +        break;
   1.549 +      }
   1.550 +
   1.551 +      case OP_LREF_S_ALT:
   1.552 +      {
   1.553 +        alt = Read(plugin, ctx->frm + *cip++);
   1.554 +        alt = Read(plugin, alt);
   1.555 +        break;
   1.556 +      }
   1.557 +
   1.558 +      case OP_CONST_PRI:
   1.559 +        pri = *cip++;
   1.560 +        break;
   1.561 +      case OP_CONST_ALT:
   1.562 +        alt = *cip++;
   1.563 +        break;
   1.564 +
   1.565 +      case OP_ADDR_PRI:
   1.566 +        pri = ctx->frm + *cip++;
   1.567 +        break;
   1.568 +      case OP_ADDR_ALT:
   1.569 +        alt = ctx->frm + *cip++;
   1.570 +        break;
   1.571 +
   1.572 +      case OP_STOR_PRI:
   1.573 +        Write(plugin, *cip++, pri);
   1.574 +        break;
   1.575 +      case OP_STOR_ALT:
   1.576 +        Write(plugin, *cip++, alt);
   1.577 +        break;
   1.578 +
   1.579 +      case OP_STOR_S_PRI:
   1.580 +        Write(plugin, ctx->frm + *cip++, pri);
   1.581 +        break;
   1.582 +      case OP_STOR_S_ALT:
   1.583 +        Write(plugin, ctx->frm +*cip++, alt);
   1.584 +        break;
   1.585 +
   1.586 +      case OP_IDXADDR:
   1.587 +        pri = alt + pri * 4;
   1.588 +        break;
   1.589 +
   1.590 +      case OP_SREF_S_PRI:
   1.591 +      {
   1.592 +        cell_t offset = *cip++;
   1.593 +        cell_t addr = Read(plugin, ctx->frm + offset);
   1.594 +        Write(plugin, addr, pri);
   1.595 +        break;
   1.596 +      }
   1.597 +
   1.598 +      case OP_SREF_S_ALT:
   1.599 +      {
   1.600 +        cell_t offset = *cip++;
   1.601 +        cell_t addr = Read(plugin, ctx->frm + offset);
   1.602 +        Write(plugin, addr, alt);
   1.603 +        break;
   1.604 +      }
   1.605 +
   1.606 +      case OP_POP_PRI:
   1.607 +        pri = *stk++;
   1.608 +        break;
   1.609 +      case OP_POP_ALT:
   1.610 +        alt = *stk++;
   1.611 +        break;
   1.612 +
   1.613 +      case OP_SWAP_PRI:
   1.614 +      case OP_SWAP_ALT:
   1.615 +      {
   1.616 +        cell_t reg = (op == OP_SREF_S_PRI) ? pri : alt;
   1.617 +        cell_t temp = *stk;
   1.618 +        *stk = reg;
   1.619 +        reg = temp;
   1.620 +        break;
   1.621 +      }
   1.622 +
   1.623 +      case OP_LIDX:
   1.624 +        pri = alt + pri * 4;
   1.625 +        if (!CheckAddress(plugin, ctx, stk, pri))
   1.626 +          goto error;
   1.627 +        pri = Read(plugin, pri);
   1.628 +        break;
   1.629 +
   1.630 +      case OP_LIDX_B:
   1.631 +      {
   1.632 +        cell_t val = *cip++;
   1.633 +        pri = alt + (pri << val);
   1.634 +        if (!CheckAddress(plugin, ctx, stk, pri))
   1.635 +          goto error;
   1.636 +        pri = Read(plugin, pri);
   1.637 +        break;
   1.638 +      }
   1.639 +
   1.640 +      case OP_CONST:
   1.641 +      {
   1.642 +        cell_t offset = *cip++;
   1.643 +        cell_t value = *cip++;
   1.644 +        Write(plugin, offset, value);
   1.645 +        break;
   1.646 +      }
   1.647 +
   1.648 +      case OP_CONST_S:
   1.649 +      {
   1.650 +        cell_t offset = *cip++;
   1.651 +        cell_t value = *cip++;
   1.652 +        Write(plugin, ctx->frm + offset, value);
   1.653 +        break;
   1.654 +      }
   1.655 +
   1.656 +      case OP_LOAD_I:
   1.657 +        if (!CheckAddress(plugin, ctx, stk, pri))
   1.658 +          goto error;
   1.659 +        pri = Read(plugin, pri);
   1.660 +        break;
   1.661 +
   1.662 +      case OP_STOR_I:
   1.663 +        if (!CheckAddress(plugin, ctx, stk, alt))
   1.664 +          goto error;
   1.665 +        Write(plugin, alt, pri);
   1.666 +        break;
   1.667 +
   1.668 +      case OP_SDIV:
   1.669 +      case OP_SDIV_ALT:
   1.670 +      {
   1.671 +        cell_t dividend = (op == OP_SDIV) ? pri : alt;
   1.672 +        cell_t divisor = (op == OP_SDIV) ? alt : pri;
   1.673 +        if (divisor == 0) {
   1.674 +          ctx->err = SP_ERROR_DIVIDE_BY_ZERO;
   1.675 +          goto error;
   1.676 +        }
   1.677 +        if (dividend == INT_MIN && divisor == -1) {
   1.678 +          ctx->err = SP_ERROR_INTEGER_OVERFLOW;
   1.679 +          goto error;
   1.680 +        }
   1.681 +        pri = dividend / divisor;
   1.682 +        alt = dividend % divisor;
   1.683 +        break;
   1.684 +      }
   1.685 +      
   1.686 +      case OP_LODB_I:
   1.687 +      {
   1.688 +        cell_t val = *cip++;
   1.689 +        if (!CheckAddress(plugin, ctx, stk, pri))
   1.690 +          goto error;
   1.691 +        pri = Read(plugin, pri);
   1.692 +        if (val == 1)
   1.693 +          pri &= 0xff;
   1.694 +        else if (val == 2)
   1.695 +          pri &= 0xffff;
   1.696 +        break;
   1.697 +      }
   1.698 +
   1.699 +      case OP_STRB_I:
   1.700 +      {
   1.701 +        cell_t val = *cip++;
   1.702 +        if (!CheckAddress(plugin, ctx, stk, alt))
   1.703 +          goto error;
   1.704 +        if (val == 1)
   1.705 +          *reinterpret_cast<int8_t *>(plugin->memory + alt) = pri;
   1.706 +        else if (val == 2)
   1.707 +          *reinterpret_cast<int16_t *>(plugin->memory + alt) = pri;
   1.708 +        else if (val == 4)
   1.709 +          *reinterpret_cast<int32_t *>(plugin->memory + alt) = pri;
   1.710 +        break;
   1.711 +      }
   1.712 +
   1.713 +      case OP_RETN:
   1.714 +      {
   1.715 +        stk++;
   1.716 +        ctx->frm = *stk++;
   1.717 +        stk += *stk + 1;
   1.718 +        *rval = pri;
   1.719 +        ctx->err = SP_ERROR_NONE;
   1.720 +        goto done;
   1.721 +      }
   1.722 +
   1.723 +      case OP_MOVS:
   1.724 +      {
   1.725 +        uint8_t *src = plugin->memory + pri;
   1.726 +        uint8_t *dest = plugin->memory + alt;
   1.727 +        memcpy(dest, src, *cip++);
   1.728 +        break;
   1.729 +      }
   1.730 +
   1.731 +      case OP_FILL:
   1.732 +      {
   1.733 +        uint8_t *dest = plugin->memory + alt;
   1.734 +        memset(dest, pri, *cip++);
   1.735 +        break;
   1.736 +      }
   1.737 +
   1.738 +      case OP_STRADJUST_PRI:
   1.739 +        pri += 4;
   1.740 +        pri >>= 2;
   1.741 +        break;
   1.742 +
   1.743 +      case OP_STACK:
   1.744 +      {
   1.745 +        cell_t amount = *cip++;
   1.746 +        if (!IsValidOffset(amount)) {
   1.747 +          ctx->err = SP_ERROR_INVALID_INSTRUCTION;
   1.748 +          goto error;
   1.749 +        }
   1.750 +
   1.751 +        stk += amount / 4;
   1.752 +        if (amount > 0) {
   1.753 +          if (uintptr_t(stk) >= uintptr_t(plugin->memory + plugin->mem_size)) {
   1.754 +            ctx->err = SP_ERROR_STACKMIN;
   1.755 +            goto error;
   1.756 +          }
   1.757 +        } else {
   1.758 +          if (uintptr_t(stk) < uintptr_t(plugin->memory + ctx->hp + STACK_MARGIN)) {
   1.759 +            ctx->err = SP_ERROR_STACKLOW;
   1.760 +            goto error;
   1.761 +          }
   1.762 +        }
   1.763 +        break;
   1.764 +      }
   1.765 +
   1.766 +      case OP_HEAP:
   1.767 +      {
   1.768 +        cell_t amount = *cip++;
   1.769 +
   1.770 +        alt = ctx->hp;
   1.771 +        ctx->hp += amount;
   1.772 +
   1.773 +        if (amount > 0) {
   1.774 +          if (uintptr_t(plugin->memory + ctx->hp) > uintptr_t(stk)) {
   1.775 +            ctx->err = SP_ERROR_HEAPLOW;
   1.776 +            goto error;
   1.777 +          }
   1.778 +        } else {
   1.779 +          if (uint32_t(ctx->hp) < plugin->data_size) {
   1.780 +            ctx->err = SP_ERROR_HEAPMIN;
   1.781 +            goto error;
   1.782 +          }
   1.783 +        }
   1.784 +        break;
   1.785 +      }
   1.786 +
   1.787 +      case OP_JUMP:
   1.788 +        if ((cip = JumpTarget(plugin, ctx, cip, true)) == NULL)
   1.789 +          goto error;
   1.790 +        break;
   1.791 +
   1.792 +      case OP_JZER:
   1.793 +        if ((cip = JumpTarget(plugin, ctx, cip, pri == 0)) == NULL)
   1.794 +          goto error;
   1.795 +        break;
   1.796 +      case OP_JNZ:
   1.797 +        if ((cip = JumpTarget(plugin, ctx, cip, pri != 0)) == NULL)
   1.798 +          goto error;
   1.799 +        break;
   1.800 +
   1.801 +      case OP_JEQ:
   1.802 +        if ((cip = JumpTarget(plugin, ctx, cip, pri == alt)) == NULL)
   1.803 +          goto error;
   1.804 +        break;
   1.805 +      case OP_JNEQ:
   1.806 +        if ((cip = JumpTarget(plugin, ctx, cip, pri != alt)) == NULL)
   1.807 +          goto error;
   1.808 +        break;
   1.809 +      case OP_JSLESS:
   1.810 +        if ((cip = JumpTarget(plugin, ctx, cip, pri < alt)) == NULL)
   1.811 +          goto error;
   1.812 +        break;
   1.813 +      case OP_JSLEQ:
   1.814 +        if ((cip = JumpTarget(plugin, ctx, cip, pri <= alt)) == NULL)
   1.815 +          goto error;
   1.816 +        break;
   1.817 +      case OP_JSGRTR:
   1.818 +        if ((cip = JumpTarget(plugin, ctx, cip, pri > alt)) == NULL)
   1.819 +          goto error;
   1.820 +        break;
   1.821 +      case OP_JSGEQ:
   1.822 +        if ((cip = JumpTarget(plugin, ctx, cip, pri >= alt)) == NULL)
   1.823 +          goto error;
   1.824 +        break;
   1.825 +
   1.826 +      case OP_TRACKER_PUSH_C:
   1.827 +      {
   1.828 +        cell_t amount = *cip++;
   1.829 +        int error = PushTracker(ctx, amount * 4);
   1.830 +        if (error != SP_ERROR_NONE) {
   1.831 +          ctx->err = error;
   1.832 +          goto error;
   1.833 +        }
   1.834 +        break;
   1.835 +      }
   1.836 +
   1.837 +      case OP_TRACKER_POP_SETHEAP:
   1.838 +      {
   1.839 +        int error = PopTrackerAndSetHeap(rt);
   1.840 +        if (error != SP_ERROR_NONE) {
   1.841 +          ctx->err = error;
   1.842 +          goto error;
   1.843 +        }
   1.844 +        break;
   1.845 +      }
   1.846 +
   1.847 +      case OP_BREAK:
   1.848 +        ctx->cip = uintptr_t(cip - 1) - uintptr_t(plugin->pcode);
   1.849 +        break;
   1.850 +
   1.851 +      case OP_BOUNDS:
   1.852 +      {
   1.853 +        cell_t value = *cip++;
   1.854 +        if (uint32_t(pri) > uint32_t(value)) {
   1.855 +          ctx->err = SP_ERROR_ARRAY_BOUNDS;
   1.856 +          goto error;
   1.857 +        }
   1.858 +        break;
   1.859 +      }
   1.860 +
   1.861 +      case OP_CALL:
   1.862 +      {
   1.863 +        cell_t offset = *cip++;
   1.864 +
   1.865 +        if (!IsValidOffset(offset) || uint32_t(offset) >= plugin->pcode_size) {
   1.866 +          ctx->err = SP_ERROR_INSTRUCTION_PARAM;
   1.867 +          goto error;
   1.868 +        }
   1.869 +
   1.870 +        if (ctx->rp >= SP_MAX_RETURN_STACK) {
   1.871 +          ctx->err = SP_ERROR_STACKLOW;
   1.872 +          goto error;
   1.873 +        }
   1.874 +
   1.875 +        // For debugging.
   1.876 +        uintptr_t rcip = uintptr_t(cip - 2) - uintptr_t(plugin->pcode);
   1.877 +        ctx->rstk_cips[ctx->rp++] = rcip;
   1.878 +        ctx->cip = offset;
   1.879 +        ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
   1.880 +
   1.881 +        int err = Interpret(rt, offset, &pri);
   1.882 +
   1.883 +        stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
   1.884 +        ctx->cip = rcip;
   1.885 +        ctx->rp--;
   1.886 +
   1.887 +        if (err != SP_ERROR_NONE)
   1.888 +          goto error;
   1.889 +        break;
   1.890 +      }
   1.891 +
   1.892 +      case OP_GENARRAY:
   1.893 +      case OP_GENARRAY_Z:
   1.894 +      {
   1.895 +        cell_t val = *cip++;
   1.896 +        if (!GenerateArray(rt, ctx, val, stk, op == OP_GENARRAY_Z))
   1.897 +          goto error;
   1.898 +
   1.899 +        stk += (val - 1) * 4;
   1.900 +        break;
   1.901 +      }
   1.902 +
   1.903 +      case OP_SYSREQ_C:
   1.904 +      case OP_SYSREQ_N:
   1.905 +      {
   1.906 +        uint32_t native_index = *cip++;
   1.907 +
   1.908 +        if (native_index >= plugin->num_natives) {
   1.909 +          ctx->err = SP_ERROR_INSTRUCTION_PARAM;
   1.910 +          goto error;
   1.911 +        }
   1.912 +
   1.913 +        uint32_t num_params;
   1.914 +        if (op == OP_SYSREQ_N) {
   1.915 +          num_params = *cip++;
   1.916 +          *--stk = num_params;
   1.917 +        }
   1.918 +
   1.919 +        ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
   1.920 +        pri = NativeCallback(ctx, native_index, stk);
   1.921 +        if (ctx->n_err != SP_ERROR_NONE) {
   1.922 +          ctx->err = ctx->n_err;
   1.923 +          goto error;
   1.924 +        }
   1.925 +
   1.926 +        if (op == OP_SYSREQ_N)
   1.927 +          stk += num_params + 1;
   1.928 +        break;
   1.929 +      }
   1.930 +
   1.931 +      case OP_SWITCH:
   1.932 +      {
   1.933 +        cell_t offset = *cip++;
   1.934 +        cell_t *table = reinterpret_cast<cell_t *>(plugin->pcode + offset + sizeof(cell_t));
   1.935 +
   1.936 +        size_t ncases = *table++;
   1.937 +        cell_t target = *table++;   // default case
   1.938 +
   1.939 +        for (size_t i = 0; i < ncases; i++) {
   1.940 +          if (table[i * 2] == pri) {
   1.941 +            target = table[i * 2 + 1];
   1.942 +            break;
   1.943 +          }
   1.944 +        }
   1.945 +
   1.946 +        if ((cip = Jump(plugin, ctx, target)) == NULL)
   1.947 +          goto error;
   1.948 +        break;
   1.949 +      }
   1.950 +
   1.951 +      default:
   1.952 +      {
   1.953 +        ctx->err = SP_ERROR_INVALID_INSTRUCTION;
   1.954 +        goto error;
   1.955 +      }
   1.956 +    } // switch
   1.957 +  }
   1.958 +
   1.959 + done:
   1.960 +  assert(orig_frm == ctx->frm);
   1.961 +  ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
   1.962 +  return ctx->err;
   1.963 +
   1.964 + error:
   1.965 +  ctx->frm = orig_frm;
   1.966 +  goto done;
   1.967 +}
   1.968 +