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 +