sourcepawn/jit/interpreter.cpp
author David Anderson <dvander@alliedmods.net>
Tue Oct 29 18:58:34 2013 -0400 (2013-10-29)
changeset 4156 319e80e8e97f
parent 4116 3e58428d988f
child 4358 52aa5fab9866
permissions -rw-r--r--
Fix some memory errors (bug 5904, r=ds).
dvander@4116
     1
// vim: set ts=8 sts=2 sw=2 tw=99 et:
dvander@4116
     2
//
dvander@4116
     3
// This file is part of SourcePawn.
dvander@4116
     4
// 
dvander@4116
     5
// SourcePawn is free software: you can redistribute it and/or modify
dvander@4116
     6
// it under the terms of the GNU General Public License as published by
dvander@4116
     7
// the Free Software Foundation, either version 3 of the License, or
dvander@4116
     8
// (at your option) any later version.
dvander@4116
     9
// 
dvander@4116
    10
// SourcePawn is distributed in the hope that it will be useful,
dvander@4116
    11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
dvander@4116
    12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
dvander@4116
    13
// GNU General Public License for more details.
dvander@4116
    14
// 
dvander@4116
    15
// You should have received a copy of the GNU General Public License
dvander@4116
    16
// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
dvander@4116
    17
#include <limits.h>
dvander@4116
    18
#include <string.h>
dvander@4116
    19
#include "interpreter.h"
dvander@4116
    20
#include "opcodes.h"
dvander@4116
    21
#include "watchdog_timer.h"
dvander@4116
    22
dvander@4116
    23
#define STACK_MARGIN 64
dvander@4116
    24
dvander@4116
    25
using namespace SourcePawn;
dvander@4116
    26
dvander@4116
    27
static inline bool
dvander@4116
    28
IsValidOffset(uint32_t cip)
dvander@4116
    29
{
dvander@4116
    30
  return cip % 4 == 0;
dvander@4116
    31
}
dvander@4116
    32
dvander@4116
    33
static inline cell_t
dvander@4116
    34
Read(const sp_plugin_t *plugin, cell_t offset)
dvander@4116
    35
{
dvander@4116
    36
  return *reinterpret_cast<cell_t *>(plugin->memory + offset);
dvander@4116
    37
}
dvander@4116
    38
dvander@4116
    39
static inline void
dvander@4116
    40
Write(const sp_plugin_t *plugin, cell_t offset, cell_t value)
dvander@4116
    41
{
dvander@4116
    42
  *reinterpret_cast<cell_t *>(plugin->memory + offset) = value;
dvander@4116
    43
}
dvander@4116
    44
dvander@4116
    45
static inline cell_t *
dvander@4116
    46
Jump(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t target)
dvander@4116
    47
{
dvander@4116
    48
  if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) {
dvander@4116
    49
    ctx->err = SP_ERROR_INVALID_INSTRUCTION;
dvander@4116
    50
    return NULL;
dvander@4116
    51
  }
dvander@4116
    52
dvander@4116
    53
  return reinterpret_cast<cell_t *>(plugin->pcode + target);
dvander@4116
    54
}
dvander@4116
    55
dvander@4116
    56
static inline cell_t *
dvander@4116
    57
JumpTarget(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *cip, bool cond)
dvander@4116
    58
{
dvander@4116
    59
  if (!cond)
dvander@4116
    60
    return cip + 1;
dvander@4116
    61
dvander@4116
    62
  cell_t target = *cip;
dvander@4116
    63
  if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) {
dvander@4116
    64
    ctx->err = SP_ERROR_INVALID_INSTRUCTION;
dvander@4116
    65
    return NULL;
dvander@4116
    66
  }
dvander@4116
    67
dvander@4116
    68
  cell_t *next = reinterpret_cast<cell_t *>(plugin->pcode + target);
dvander@4116
    69
  if (next < cip && !g_WatchdogTimer.HandleInterrupt()) {
dvander@4116
    70
    ctx->err = SP_ERROR_TIMEOUT;
dvander@4116
    71
    return NULL;
dvander@4116
    72
  }
dvander@4116
    73
dvander@4116
    74
  return next;
dvander@4116
    75
}
dvander@4116
    76
dvander@4116
    77
static inline bool
dvander@4116
    78
CheckAddress(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *stk, cell_t addr)
dvander@4116
    79
{
dvander@4116
    80
  if (uint32_t(addr) >= plugin->mem_size) {
dvander@4116
    81
    ctx->err = SP_ERROR_MEMACCESS;
dvander@4116
    82
    return false;
dvander@4116
    83
  }
dvander@4116
    84
dvander@4116
    85
  if (addr < ctx->hp)
dvander@4116
    86
    return true;
dvander@4116
    87
dvander@4116
    88
  if (reinterpret_cast<cell_t *>(plugin->memory + addr) < stk) {
dvander@4116
    89
    ctx->err = SP_ERROR_MEMACCESS;
dvander@4116
    90
    return false;
dvander@4116
    91
  }
dvander@4116
    92
dvander@4116
    93
  return true;
dvander@4116
    94
}
dvander@4116
    95
dvander@4116
    96
int
dvander@4116
    97
PopTrackerAndSetHeap(BaseRuntime *rt)
dvander@4116
    98
{
dvander@4116
    99
  sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
dvander@4116
   100
  tracker_t *trk = ctx->tracker;
dvander@4116
   101
  assert(trk->pCur > trk->pBase);
dvander@4116
   102
dvander@4116
   103
  trk->pCur--;
dvander@4116
   104
  if (trk->pCur < trk->pBase)
dvander@4116
   105
    return SP_ERROR_TRACKER_BOUNDS;
dvander@4116
   106
dvander@4116
   107
  ucell_t amt = *trk->pCur;
dvander@4116
   108
  if (amt > (ctx->hp - rt->plugin()->data_size))
dvander@4116
   109
    return SP_ERROR_HEAPMIN;
dvander@4116
   110
dvander@4116
   111
  ctx->hp -= amt;
dvander@4116
   112
  return SP_ERROR_NONE;
dvander@4116
   113
}
dvander@4116
   114
dvander@4116
   115
int
dvander@4116
   116
PushTracker(sp_context_t *ctx, size_t amount)
dvander@4116
   117
{
dvander@4116
   118
  tracker_t *trk = ctx->tracker;
dvander@4116
   119
dvander@4116
   120
  if ((size_t)(trk->pCur - trk->pBase) >= trk->size)
dvander@4116
   121
    return SP_ERROR_TRACKER_BOUNDS;
dvander@4116
   122
dvander@4116
   123
  if (trk->pCur + 1 - (trk->pBase + trk->size) == 0) {
dvander@4116
   124
    size_t disp = trk->size - 1;
dvander@4116
   125
    trk->size *= 2;
dvander@4116
   126
    trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t));
dvander@4116
   127
dvander@4116
   128
    if (!trk->pBase)
dvander@4116
   129
      return SP_ERROR_TRACKER_BOUNDS;
dvander@4116
   130
dvander@4116
   131
    trk->pCur = trk->pBase + disp;
dvander@4116
   132
  }
dvander@4116
   133
dvander@4116
   134
  *trk->pCur++ = amount;
dvander@4116
   135
  return SP_ERROR_NONE;
dvander@4116
   136
}
dvander@4116
   137
dvander@4116
   138
cell_t
dvander@4116
   139
NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params)
dvander@4116
   140
{
dvander@4116
   141
  cell_t save_sp = ctx->sp;
dvander@4116
   142
  cell_t save_hp = ctx->hp;
dvander@4116
   143
dvander@4116
   144
  ctx->n_idx = native_idx;
dvander@4116
   145
dvander@4116
   146
  sp_native_t *native = &ctx->plugin->natives[native_idx];
dvander@4116
   147
dvander@4116
   148
  if (native->status == SP_NATIVE_UNBOUND) {
dvander@4116
   149
    ctx->n_err = SP_ERROR_INVALID_NATIVE;
dvander@4116
   150
    return 0;
dvander@4116
   151
  }
dvander@4116
   152
dvander@4116
   153
  cell_t result = native->pfn(ctx->basecx, params);
dvander@4116
   154
dvander@4116
   155
  if (ctx->n_err != SP_ERROR_NONE)
dvander@4116
   156
    return result;
dvander@4116
   157
dvander@4116
   158
  if (save_sp != ctx->sp) {
dvander@4116
   159
    ctx->n_err = SP_ERROR_STACKLEAK;
dvander@4116
   160
    return result;
dvander@4116
   161
  }
dvander@4116
   162
  if (save_hp != ctx->hp) {
dvander@4116
   163
    ctx->n_err = SP_ERROR_HEAPLEAK;
dvander@4116
   164
    return result;
dvander@4116
   165
  }
dvander@4116
   166
dvander@4116
   167
  return result;
dvander@4116
   168
}
dvander@4116
   169
dvander@4116
   170
static inline bool
dvander@4116
   171
GenerateArray(BaseRuntime *rt, sp_context_t *ctx, cell_t dims, cell_t *stk, bool autozero)
dvander@4116
   172
{
dvander@4116
   173
  if (dims == 1) {
dvander@4116
   174
    uint32_t size = *stk;
dvander@4116
   175
    if (size == 0 || !ke::IsUint32MultiplySafe(size, 4)) {
dvander@4116
   176
      ctx->err = SP_ERROR_ARRAY_TOO_BIG;
dvander@4116
   177
      return false;
dvander@4116
   178
    }
dvander@4116
   179
    *stk = ctx->hp;
dvander@4116
   180
dvander@4116
   181
    uint32_t bytes = size * 4;
dvander@4116
   182
dvander@4116
   183
    ctx->hp += bytes;
dvander@4116
   184
    if (uintptr_t(ctx->plugin->memory + ctx->hp) >= uintptr_t(stk)) {
dvander@4116
   185
      ctx->err = SP_ERROR_HEAPLOW;
dvander@4116
   186
      return false;
dvander@4116
   187
    }
dvander@4116
   188
dvander@4116
   189
    if ((ctx->err = PushTracker(ctx, bytes)) != SP_ERROR_NONE)
dvander@4116
   190
      return false;
dvander@4116
   191
dvander@4116
   192
    if (autozero)
dvander@4116
   193
      memset(ctx->plugin->memory + ctx->hp, 0, bytes);
dvander@4116
   194
dvander@4116
   195
    return true;
dvander@4116
   196
  }
dvander@4116
   197
dvander@4116
   198
  if ((ctx->err = GenerateFullArray(rt, dims, stk, autozero)) != SP_ERROR_NONE)
dvander@4116
   199
    return false;
dvander@4116
   200
dvander@4116
   201
  return true;
dvander@4116
   202
}
dvander@4116
   203
dvander@4116
   204
int
dvander@4116
   205
Interpret(BaseRuntime *rt, uint32_t aCodeStart, cell_t *rval)
dvander@4116
   206
{
dvander@4116
   207
  const sp_plugin_t *plugin = rt->plugin();
dvander@4116
   208
  cell_t *code = reinterpret_cast<cell_t *>(plugin->pcode);
dvander@4116
   209
  cell_t *codeend = reinterpret_cast<cell_t *>(plugin->pcode + plugin->pcode_size);
dvander@4116
   210
dvander@4116
   211
  if (!IsValidOffset(aCodeStart) || aCodeStart > plugin->pcode_size)
dvander@4116
   212
    return SP_ERROR_INVALID_INSTRUCTION;
dvander@4116
   213
dvander@4116
   214
  sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
dvander@4116
   215
  ctx->err = SP_ERROR_NONE;
dvander@4116
   216
dvander@4116
   217
  // Save the original frm. BaseContext won't, and if we error, we won't hit
dvander@4116
   218
  // the stack unwinding code.
dvander@4116
   219
  cell_t orig_frm = ctx->frm;
dvander@4116
   220
dvander@4116
   221
  cell_t pri = 0;
dvander@4116
   222
  cell_t alt = 0;
dvander@4116
   223
  cell_t *cip = code + (aCodeStart / 4);
dvander@4116
   224
  cell_t *stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
dvander@4116
   225
dvander@4116
   226
  for (;;) {
dvander@4116
   227
    if (cip >= codeend) {
dvander@4116
   228
      ctx->err = SP_ERROR_INVALID_INSTRUCTION;
dvander@4116
   229
      goto error;
dvander@4116
   230
    }
dvander@4116
   231
dvander@4116
   232
#if 0
dvander@4116
   233
    SpewOpcode(plugin, reinterpret_cast<cell_t *>(plugin->pcode + aCodeStart), cip);
dvander@4116
   234
#endif
dvander@4116
   235
dvander@4116
   236
    OPCODE op = (OPCODE)*cip++;
dvander@4116
   237
dvander@4116
   238
    switch (op) {
dvander@4116
   239
      case OP_MOVE_PRI:
dvander@4116
   240
        pri = alt;
dvander@4116
   241
        break;
dvander@4116
   242
      case OP_MOVE_ALT:
dvander@4116
   243
        alt = pri;
dvander@4116
   244
        break;
dvander@4116
   245
dvander@4116
   246
      case OP_XCHG:
dvander@4116
   247
      {
dvander@4116
   248
        cell_t tmp = pri;
dvander@4116
   249
        pri = alt;
dvander@4116
   250
        alt = tmp;
dvander@4116
   251
        break;
dvander@4116
   252
      }
dvander@4116
   253
dvander@4116
   254
      case OP_ZERO:
dvander@4116
   255
        Write(plugin, *cip++, 0);
dvander@4116
   256
        break;
dvander@4116
   257
dvander@4116
   258
      case OP_ZERO_S:
dvander@4156
   259
        Write(plugin, ctx->frm + *cip++, 0);
dvander@4116
   260
        break;
dvander@4116
   261
dvander@4116
   262
      case OP_PUSH_PRI:
dvander@4116
   263
        *--stk = pri;
dvander@4116
   264
        break;
dvander@4116
   265
      case OP_PUSH_ALT:
dvander@4116
   266
        *--stk = alt;
dvander@4116
   267
        break;
dvander@4116
   268
dvander@4116
   269
      case OP_PUSH_C:
dvander@4116
   270
      case OP_PUSH2_C:
dvander@4116
   271
      case OP_PUSH3_C:
dvander@4116
   272
      case OP_PUSH4_C:
dvander@4116
   273
      case OP_PUSH5_C:
dvander@4116
   274
      {
dvander@4116
   275
        int n = 1;
dvander@4116
   276
        if (op >= OP_PUSH2_C)
dvander@4116
   277
          n = ((op - OP_PUSH2_C) / 4) + 2;
dvander@4116
   278
dvander@4116
   279
        int i = 1;
dvander@4116
   280
        do {
dvander@4116
   281
          *--stk = *cip++;
dvander@4116
   282
        } while (i++ < n);
dvander@4116
   283
        break;
dvander@4116
   284
      }
dvander@4116
   285
dvander@4116
   286
      case OP_PUSH_ADR:
dvander@4116
   287
      case OP_PUSH2_ADR:
dvander@4116
   288
      case OP_PUSH3_ADR:
dvander@4116
   289
      case OP_PUSH4_ADR:
dvander@4116
   290
      case OP_PUSH5_ADR:
dvander@4116
   291
      {
dvander@4116
   292
        int n = 1;
dvander@4116
   293
        if (op >= OP_PUSH2_ADR)
dvander@4116
   294
          n = ((op - OP_PUSH2_ADR) / 4) + 2;
dvander@4116
   295
dvander@4116
   296
        int i = 1;
dvander@4116
   297
dvander@4116
   298
        do {
dvander@4116
   299
          cell_t addr = ctx->frm + *cip++;
dvander@4116
   300
          *--stk = addr;
dvander@4116
   301
        } while (i++ < n);
dvander@4116
   302
        break;
dvander@4116
   303
      }
dvander@4116
   304
dvander@4116
   305
      case OP_PUSH_S:
dvander@4116
   306
      case OP_PUSH2_S:
dvander@4116
   307
      case OP_PUSH3_S:
dvander@4116
   308
      case OP_PUSH4_S:
dvander@4116
   309
      case OP_PUSH5_S:
dvander@4116
   310
      {
dvander@4116
   311
        int n = 1;
dvander@4116
   312
        if (op >= OP_PUSH2_S)
dvander@4116
   313
          n = ((op - OP_PUSH2_S) / 4) + 2;
dvander@4116
   314
dvander@4116
   315
        int i = 1;
dvander@4116
   316
        do {
dvander@4116
   317
          cell_t value = Read(plugin, ctx->frm + *cip++);
dvander@4116
   318
          *--stk = value;
dvander@4116
   319
        } while (i++ < n);
dvander@4116
   320
        break;
dvander@4116
   321
      }
dvander@4116
   322
dvander@4116
   323
      case OP_PUSH:
dvander@4116
   324
      case OP_PUSH2:
dvander@4116
   325
      case OP_PUSH3:
dvander@4116
   326
      case OP_PUSH4:
dvander@4116
   327
      case OP_PUSH5:
dvander@4116
   328
      {
dvander@4116
   329
        int n = 1;
dvander@4116
   330
        if (op >= OP_PUSH2)
dvander@4116
   331
          n = ((op - OP_PUSH2) / 4) + 2;
dvander@4116
   332
dvander@4116
   333
        int i = 1;
dvander@4116
   334
        do {
dvander@4116
   335
          cell_t value = Read(plugin, *cip++);
dvander@4116
   336
          *--stk = value;
dvander@4116
   337
        } while (i++ < n);
dvander@4116
   338
        break;
dvander@4116
   339
      }
dvander@4116
   340
dvander@4116
   341
      case OP_ZERO_PRI:
dvander@4116
   342
        pri = 0;
dvander@4116
   343
        break;
dvander@4116
   344
      case OP_ZERO_ALT:
dvander@4116
   345
        alt = 0;
dvander@4116
   346
        break;
dvander@4116
   347
dvander@4116
   348
      case OP_ADD:
dvander@4116
   349
        pri += alt;
dvander@4116
   350
        break;
dvander@4116
   351
dvander@4116
   352
      case OP_SUB:
dvander@4116
   353
        pri -= alt;
dvander@4116
   354
        break;
dvander@4116
   355
dvander@4116
   356
      case OP_SUB_ALT:
dvander@4116
   357
        pri = alt - pri;
dvander@4116
   358
        break;
dvander@4116
   359
dvander@4116
   360
      case OP_PROC:
dvander@4116
   361
      {
dvander@4116
   362
        *--stk = ctx->frm;
dvander@4116
   363
        *--stk = 0;
dvander@4116
   364
        ctx->frm = uintptr_t(stk) - uintptr_t(plugin->memory);
dvander@4116
   365
        break;
dvander@4116
   366
      }
dvander@4116
   367
dvander@4116
   368
      case OP_IDXADDR_B:
dvander@4116
   369
        pri <<= *cip++;
dvander@4116
   370
        pri += alt;
dvander@4116
   371
        break;
dvander@4116
   372
dvander@4116
   373
      case OP_SHL:
dvander@4116
   374
        pri <<= alt;
dvander@4116
   375
        break;
dvander@4116
   376
dvander@4116
   377
      case OP_SHR:
dvander@4116
   378
        pri = unsigned(pri) >> unsigned(alt);
dvander@4116
   379
        break;
dvander@4116
   380
dvander@4116
   381
      case OP_SSHR:
dvander@4116
   382
        pri >>= alt;
dvander@4116
   383
        break;
dvander@4116
   384
dvander@4116
   385
      case OP_SHL_C_PRI:
dvander@4116
   386
        pri <<= *cip++;
dvander@4116
   387
        break;
dvander@4116
   388
      case OP_SHL_C_ALT:
dvander@4116
   389
        alt <<= *cip++;
dvander@4116
   390
        break;
dvander@4116
   391
dvander@4116
   392
      case OP_SHR_C_PRI:
dvander@4116
   393
        pri >>= *cip++;
dvander@4116
   394
        break;
dvander@4116
   395
      case OP_SHR_C_ALT:
dvander@4116
   396
        alt >>= *cip++;
dvander@4116
   397
        break;
dvander@4116
   398
dvander@4116
   399
      case OP_SMUL:
dvander@4116
   400
        pri *= alt;
dvander@4116
   401
        break;
dvander@4116
   402
dvander@4116
   403
      case OP_NOT:
dvander@4116
   404
        pri = pri ? 0 : 1;
dvander@4116
   405
        break;
dvander@4116
   406
dvander@4116
   407
      case OP_NEG:
dvander@4116
   408
        pri = -pri;
dvander@4116
   409
        break;
dvander@4116
   410
dvander@4116
   411
      case OP_XOR:
dvander@4116
   412
        pri ^= alt;
dvander@4116
   413
        break;
dvander@4116
   414
dvander@4116
   415
      case OP_OR:
dvander@4116
   416
        pri |= alt;
dvander@4116
   417
        break;
dvander@4116
   418
dvander@4116
   419
      case OP_AND:
dvander@4116
   420
        pri &= alt;
dvander@4116
   421
        break;
dvander@4116
   422
dvander@4116
   423
      case OP_INVERT:
dvander@4116
   424
        pri = ~pri;
dvander@4116
   425
        break;
dvander@4116
   426
dvander@4116
   427
      case OP_ADD_C:
dvander@4116
   428
        pri += *cip++;
dvander@4116
   429
        break;
dvander@4116
   430
dvander@4116
   431
      case OP_SMUL_C:
dvander@4116
   432
        pri *= *cip++;
dvander@4116
   433
        break;
dvander@4116
   434
dvander@4116
   435
      case OP_EQ:
dvander@4116
   436
        pri = pri == alt;
dvander@4116
   437
        break;
dvander@4116
   438
dvander@4116
   439
      case OP_NEQ:
dvander@4116
   440
        pri = pri != alt;
dvander@4116
   441
        break;
dvander@4116
   442
dvander@4116
   443
      case OP_SLESS:
dvander@4116
   444
        pri = pri < alt;
dvander@4116
   445
        break;
dvander@4116
   446
dvander@4116
   447
      case OP_SLEQ:
dvander@4116
   448
        pri = pri <= alt;
dvander@4116
   449
        break;
dvander@4116
   450
dvander@4116
   451
      case OP_SGRTR:
dvander@4116
   452
        pri = pri > alt;
dvander@4116
   453
        break;
dvander@4116
   454
        
dvander@4116
   455
      case OP_SGEQ:
dvander@4116
   456
        pri = pri >= alt;
dvander@4116
   457
        break;
dvander@4116
   458
dvander@4116
   459
      case OP_EQ_C_PRI:
dvander@4116
   460
        pri = pri == *cip++;
dvander@4116
   461
        break;
dvander@4116
   462
      case OP_EQ_C_ALT:
dvander@4116
   463
        pri = alt == *cip++;
dvander@4116
   464
        break;
dvander@4116
   465
dvander@4116
   466
      case OP_INC_PRI:
dvander@4116
   467
        pri++;
dvander@4116
   468
        break;
dvander@4116
   469
      case OP_INC_ALT:
dvander@4116
   470
        alt++;
dvander@4116
   471
        break;
dvander@4116
   472
dvander@4116
   473
      case OP_INC:
dvander@4116
   474
      {
dvander@4116
   475
        cell_t offset = *cip++;
dvander@4116
   476
        Write(plugin, offset, Read(plugin, offset) + 1);
dvander@4116
   477
        break;
dvander@4116
   478
      }
dvander@4116
   479
dvander@4116
   480
      case OP_INC_S:
dvander@4116
   481
      {
dvander@4116
   482
        cell_t offset = *cip++;
dvander@4116
   483
        cell_t value = Read(plugin, ctx->frm + offset);
dvander@4116
   484
        Write(plugin, ctx->frm + offset, value + 1);
dvander@4116
   485
        break;
dvander@4116
   486
      }
dvander@4116
   487
dvander@4116
   488
      case OP_INC_I:
dvander@4116
   489
        if (!CheckAddress(plugin, ctx, stk, pri))
dvander@4116
   490
          goto error;
dvander@4116
   491
        Write(plugin, pri, Read(plugin, pri) + 1);
dvander@4116
   492
        break;
dvander@4116
   493
dvander@4116
   494
      case OP_DEC_PRI:
dvander@4116
   495
        pri--;
dvander@4116
   496
        break;
dvander@4116
   497
      case OP_DEC_ALT:
dvander@4116
   498
        alt--;
dvander@4116
   499
        break;
dvander@4116
   500
dvander@4116
   501
      case OP_DEC:
dvander@4116
   502
      {
dvander@4116
   503
        cell_t offset = *cip++;
dvander@4116
   504
        Write(plugin, offset, Read(plugin, offset) - 1);
dvander@4116
   505
        break;
dvander@4116
   506
      }
dvander@4116
   507
dvander@4116
   508
      case OP_DEC_S:
dvander@4116
   509
      {
dvander@4116
   510
        cell_t offset = *cip++;
dvander@4116
   511
        cell_t value = Read(plugin, ctx->frm + offset);
dvander@4116
   512
        Write(plugin, ctx->frm + offset, value - 1);
dvander@4116
   513
        break;
dvander@4116
   514
      }
dvander@4116
   515
dvander@4116
   516
      case OP_DEC_I:
dvander@4116
   517
        if (!CheckAddress(plugin, ctx, stk, pri))
dvander@4116
   518
          goto error;
dvander@4116
   519
        Write(plugin, pri, Read(plugin, pri) - 1);
dvander@4116
   520
        break;
dvander@4116
   521
dvander@4116
   522
      case OP_LOAD_PRI:
dvander@4116
   523
        pri = Read(plugin, *cip++);
dvander@4116
   524
        break;
dvander@4116
   525
      case OP_LOAD_ALT:
dvander@4116
   526
        alt = Read(plugin, *cip++);
dvander@4116
   527
        break;
dvander@4116
   528
dvander@4116
   529
      case OP_LOAD_S_PRI:
dvander@4116
   530
        pri = Read(plugin, ctx->frm + *cip++);
dvander@4116
   531
        break;
dvander@4116
   532
      case OP_LOAD_S_ALT:
dvander@4116
   533
        alt = Read(plugin, ctx->frm + *cip++);
dvander@4116
   534
        break;
dvander@4116
   535
dvander@4116
   536
      case OP_LOAD_S_BOTH:
dvander@4116
   537
        pri = Read(plugin, ctx->frm + *cip++);
dvander@4116
   538
        alt = Read(plugin, ctx->frm + *cip++);
dvander@4116
   539
        break;
dvander@4116
   540
dvander@4116
   541
      case OP_LREF_S_PRI:
dvander@4116
   542
      {
dvander@4116
   543
        pri = Read(plugin, ctx->frm + *cip++);
dvander@4116
   544
        pri = Read(plugin, pri);
dvander@4116
   545
        break;
dvander@4116
   546
      }
dvander@4116
   547
dvander@4116
   548
      case OP_LREF_S_ALT:
dvander@4116
   549
      {
dvander@4116
   550
        alt = Read(plugin, ctx->frm + *cip++);
dvander@4116
   551
        alt = Read(plugin, alt);
dvander@4116
   552
        break;
dvander@4116
   553
      }
dvander@4116
   554
dvander@4116
   555
      case OP_CONST_PRI:
dvander@4116
   556
        pri = *cip++;
dvander@4116
   557
        break;
dvander@4116
   558
      case OP_CONST_ALT:
dvander@4116
   559
        alt = *cip++;
dvander@4116
   560
        break;
dvander@4116
   561
dvander@4116
   562
      case OP_ADDR_PRI:
dvander@4116
   563
        pri = ctx->frm + *cip++;
dvander@4116
   564
        break;
dvander@4116
   565
      case OP_ADDR_ALT:
dvander@4116
   566
        alt = ctx->frm + *cip++;
dvander@4116
   567
        break;
dvander@4116
   568
dvander@4116
   569
      case OP_STOR_PRI:
dvander@4116
   570
        Write(plugin, *cip++, pri);
dvander@4116
   571
        break;
dvander@4116
   572
      case OP_STOR_ALT:
dvander@4116
   573
        Write(plugin, *cip++, alt);
dvander@4116
   574
        break;
dvander@4116
   575
dvander@4116
   576
      case OP_STOR_S_PRI:
dvander@4116
   577
        Write(plugin, ctx->frm + *cip++, pri);
dvander@4116
   578
        break;
dvander@4116
   579
      case OP_STOR_S_ALT:
dvander@4116
   580
        Write(plugin, ctx->frm +*cip++, alt);
dvander@4116
   581
        break;
dvander@4116
   582
dvander@4116
   583
      case OP_IDXADDR:
dvander@4116
   584
        pri = alt + pri * 4;
dvander@4116
   585
        break;
dvander@4116
   586
dvander@4116
   587
      case OP_SREF_S_PRI:
dvander@4116
   588
      {
dvander@4116
   589
        cell_t offset = *cip++;
dvander@4116
   590
        cell_t addr = Read(plugin, ctx->frm + offset);
dvander@4116
   591
        Write(plugin, addr, pri);
dvander@4116
   592
        break;
dvander@4116
   593
      }
dvander@4116
   594
dvander@4116
   595
      case OP_SREF_S_ALT:
dvander@4116
   596
      {
dvander@4116
   597
        cell_t offset = *cip++;
dvander@4116
   598
        cell_t addr = Read(plugin, ctx->frm + offset);
dvander@4116
   599
        Write(plugin, addr, alt);
dvander@4116
   600
        break;
dvander@4116
   601
      }
dvander@4116
   602
dvander@4116
   603
      case OP_POP_PRI:
dvander@4116
   604
        pri = *stk++;
dvander@4116
   605
        break;
dvander@4116
   606
      case OP_POP_ALT:
dvander@4116
   607
        alt = *stk++;
dvander@4116
   608
        break;
dvander@4116
   609
dvander@4116
   610
      case OP_SWAP_PRI:
dvander@4116
   611
      case OP_SWAP_ALT:
dvander@4116
   612
      {
dvander@4116
   613
        cell_t reg = (op == OP_SREF_S_PRI) ? pri : alt;
dvander@4116
   614
        cell_t temp = *stk;
dvander@4116
   615
        *stk = reg;
dvander@4116
   616
        reg = temp;
dvander@4116
   617
        break;
dvander@4116
   618
      }
dvander@4116
   619
dvander@4116
   620
      case OP_LIDX:
dvander@4116
   621
        pri = alt + pri * 4;
dvander@4116
   622
        if (!CheckAddress(plugin, ctx, stk, pri))
dvander@4116
   623
          goto error;
dvander@4116
   624
        pri = Read(plugin, pri);
dvander@4116
   625
        break;
dvander@4116
   626
dvander@4116
   627
      case OP_LIDX_B:
dvander@4116
   628
      {
dvander@4116
   629
        cell_t val = *cip++;
dvander@4116
   630
        pri = alt + (pri << val);
dvander@4116
   631
        if (!CheckAddress(plugin, ctx, stk, pri))
dvander@4116
   632
          goto error;
dvander@4116
   633
        pri = Read(plugin, pri);
dvander@4116
   634
        break;
dvander@4116
   635
      }
dvander@4116
   636
dvander@4116
   637
      case OP_CONST:
dvander@4116
   638
      {
dvander@4116
   639
        cell_t offset = *cip++;
dvander@4116
   640
        cell_t value = *cip++;
dvander@4116
   641
        Write(plugin, offset, value);
dvander@4116
   642
        break;
dvander@4116
   643
      }
dvander@4116
   644
dvander@4116
   645
      case OP_CONST_S:
dvander@4116
   646
      {
dvander@4116
   647
        cell_t offset = *cip++;
dvander@4116
   648
        cell_t value = *cip++;
dvander@4116
   649
        Write(plugin, ctx->frm + offset, value);
dvander@4116
   650
        break;
dvander@4116
   651
      }
dvander@4116
   652
dvander@4116
   653
      case OP_LOAD_I:
dvander@4116
   654
        if (!CheckAddress(plugin, ctx, stk, pri))
dvander@4116
   655
          goto error;
dvander@4116
   656
        pri = Read(plugin, pri);
dvander@4116
   657
        break;
dvander@4116
   658
dvander@4116
   659
      case OP_STOR_I:
dvander@4116
   660
        if (!CheckAddress(plugin, ctx, stk, alt))
dvander@4116
   661
          goto error;
dvander@4116
   662
        Write(plugin, alt, pri);
dvander@4116
   663
        break;
dvander@4116
   664
dvander@4116
   665
      case OP_SDIV:
dvander@4116
   666
      case OP_SDIV_ALT:
dvander@4116
   667
      {
dvander@4116
   668
        cell_t dividend = (op == OP_SDIV) ? pri : alt;
dvander@4116
   669
        cell_t divisor = (op == OP_SDIV) ? alt : pri;
dvander@4116
   670
        if (divisor == 0) {
dvander@4116
   671
          ctx->err = SP_ERROR_DIVIDE_BY_ZERO;
dvander@4116
   672
          goto error;
dvander@4116
   673
        }
dvander@4116
   674
        if (dividend == INT_MIN && divisor == -1) {
dvander@4116
   675
          ctx->err = SP_ERROR_INTEGER_OVERFLOW;
dvander@4116
   676
          goto error;
dvander@4116
   677
        }
dvander@4116
   678
        pri = dividend / divisor;
dvander@4116
   679
        alt = dividend % divisor;
dvander@4116
   680
        break;
dvander@4116
   681
      }
dvander@4116
   682
      
dvander@4116
   683
      case OP_LODB_I:
dvander@4116
   684
      {
dvander@4116
   685
        cell_t val = *cip++;
dvander@4116
   686
        if (!CheckAddress(plugin, ctx, stk, pri))
dvander@4116
   687
          goto error;
dvander@4116
   688
        pri = Read(plugin, pri);
dvander@4116
   689
        if (val == 1)
dvander@4116
   690
          pri &= 0xff;
dvander@4116
   691
        else if (val == 2)
dvander@4116
   692
          pri &= 0xffff;
dvander@4116
   693
        break;
dvander@4116
   694
      }
dvander@4116
   695
dvander@4116
   696
      case OP_STRB_I:
dvander@4116
   697
      {
dvander@4116
   698
        cell_t val = *cip++;
dvander@4116
   699
        if (!CheckAddress(plugin, ctx, stk, alt))
dvander@4116
   700
          goto error;
dvander@4116
   701
        if (val == 1)
dvander@4116
   702
          *reinterpret_cast<int8_t *>(plugin->memory + alt) = pri;
dvander@4116
   703
        else if (val == 2)
dvander@4116
   704
          *reinterpret_cast<int16_t *>(plugin->memory + alt) = pri;
dvander@4116
   705
        else if (val == 4)
dvander@4116
   706
          *reinterpret_cast<int32_t *>(plugin->memory + alt) = pri;
dvander@4116
   707
        break;
dvander@4116
   708
      }
dvander@4116
   709
dvander@4116
   710
      case OP_RETN:
dvander@4116
   711
      {
dvander@4116
   712
        stk++;
dvander@4116
   713
        ctx->frm = *stk++;
dvander@4116
   714
        stk += *stk + 1;
dvander@4116
   715
        *rval = pri;
dvander@4116
   716
        ctx->err = SP_ERROR_NONE;
dvander@4116
   717
        goto done;
dvander@4116
   718
      }
dvander@4116
   719
dvander@4116
   720
      case OP_MOVS:
dvander@4116
   721
      {
dvander@4116
   722
        uint8_t *src = plugin->memory + pri;
dvander@4116
   723
        uint8_t *dest = plugin->memory + alt;
dvander@4116
   724
        memcpy(dest, src, *cip++);
dvander@4116
   725
        break;
dvander@4116
   726
      }
dvander@4116
   727
dvander@4116
   728
      case OP_FILL:
dvander@4116
   729
      {
dvander@4116
   730
        uint8_t *dest = plugin->memory + alt;
dvander@4116
   731
        memset(dest, pri, *cip++);
dvander@4116
   732
        break;
dvander@4116
   733
      }
dvander@4116
   734
dvander@4116
   735
      case OP_STRADJUST_PRI:
dvander@4116
   736
        pri += 4;
dvander@4116
   737
        pri >>= 2;
dvander@4116
   738
        break;
dvander@4116
   739
dvander@4116
   740
      case OP_STACK:
dvander@4116
   741
      {
dvander@4116
   742
        cell_t amount = *cip++;
dvander@4116
   743
        if (!IsValidOffset(amount)) {
dvander@4116
   744
          ctx->err = SP_ERROR_INVALID_INSTRUCTION;
dvander@4116
   745
          goto error;
dvander@4116
   746
        }
dvander@4116
   747
dvander@4116
   748
        stk += amount / 4;
dvander@4116
   749
        if (amount > 0) {
dvander@4116
   750
          if (uintptr_t(stk) >= uintptr_t(plugin->memory + plugin->mem_size)) {
dvander@4116
   751
            ctx->err = SP_ERROR_STACKMIN;
dvander@4116
   752
            goto error;
dvander@4116
   753
          }
dvander@4116
   754
        } else {
dvander@4116
   755
          if (uintptr_t(stk) < uintptr_t(plugin->memory + ctx->hp + STACK_MARGIN)) {
dvander@4116
   756
            ctx->err = SP_ERROR_STACKLOW;
dvander@4116
   757
            goto error;
dvander@4116
   758
          }
dvander@4116
   759
        }
dvander@4116
   760
        break;
dvander@4116
   761
      }
dvander@4116
   762
dvander@4116
   763
      case OP_HEAP:
dvander@4116
   764
      {
dvander@4116
   765
        cell_t amount = *cip++;
dvander@4116
   766
dvander@4116
   767
        alt = ctx->hp;
dvander@4116
   768
        ctx->hp += amount;
dvander@4116
   769
dvander@4116
   770
        if (amount > 0) {
dvander@4116
   771
          if (uintptr_t(plugin->memory + ctx->hp) > uintptr_t(stk)) {
dvander@4116
   772
            ctx->err = SP_ERROR_HEAPLOW;
dvander@4116
   773
            goto error;
dvander@4116
   774
          }
dvander@4116
   775
        } else {
dvander@4116
   776
          if (uint32_t(ctx->hp) < plugin->data_size) {
dvander@4116
   777
            ctx->err = SP_ERROR_HEAPMIN;
dvander@4116
   778
            goto error;
dvander@4116
   779
          }
dvander@4116
   780
        }
dvander@4116
   781
        break;
dvander@4116
   782
      }
dvander@4116
   783
dvander@4116
   784
      case OP_JUMP:
dvander@4116
   785
        if ((cip = JumpTarget(plugin, ctx, cip, true)) == NULL)
dvander@4116
   786
          goto error;
dvander@4116
   787
        break;
dvander@4116
   788
dvander@4116
   789
      case OP_JZER:
dvander@4116
   790
        if ((cip = JumpTarget(plugin, ctx, cip, pri == 0)) == NULL)
dvander@4116
   791
          goto error;
dvander@4116
   792
        break;
dvander@4116
   793
      case OP_JNZ:
dvander@4116
   794
        if ((cip = JumpTarget(plugin, ctx, cip, pri != 0)) == NULL)
dvander@4116
   795
          goto error;
dvander@4116
   796
        break;
dvander@4116
   797
dvander@4116
   798
      case OP_JEQ:
dvander@4116
   799
        if ((cip = JumpTarget(plugin, ctx, cip, pri == alt)) == NULL)
dvander@4116
   800
          goto error;
dvander@4116
   801
        break;
dvander@4116
   802
      case OP_JNEQ:
dvander@4116
   803
        if ((cip = JumpTarget(plugin, ctx, cip, pri != alt)) == NULL)
dvander@4116
   804
          goto error;
dvander@4116
   805
        break;
dvander@4116
   806
      case OP_JSLESS:
dvander@4116
   807
        if ((cip = JumpTarget(plugin, ctx, cip, pri < alt)) == NULL)
dvander@4116
   808
          goto error;
dvander@4116
   809
        break;
dvander@4116
   810
      case OP_JSLEQ:
dvander@4116
   811
        if ((cip = JumpTarget(plugin, ctx, cip, pri <= alt)) == NULL)
dvander@4116
   812
          goto error;
dvander@4116
   813
        break;
dvander@4116
   814
      case OP_JSGRTR:
dvander@4116
   815
        if ((cip = JumpTarget(plugin, ctx, cip, pri > alt)) == NULL)
dvander@4116
   816
          goto error;
dvander@4116
   817
        break;
dvander@4116
   818
      case OP_JSGEQ:
dvander@4116
   819
        if ((cip = JumpTarget(plugin, ctx, cip, pri >= alt)) == NULL)
dvander@4116
   820
          goto error;
dvander@4116
   821
        break;
dvander@4116
   822
dvander@4116
   823
      case OP_TRACKER_PUSH_C:
dvander@4116
   824
      {
dvander@4116
   825
        cell_t amount = *cip++;
dvander@4116
   826
        int error = PushTracker(ctx, amount * 4);
dvander@4116
   827
        if (error != SP_ERROR_NONE) {
dvander@4116
   828
          ctx->err = error;
dvander@4116
   829
          goto error;
dvander@4116
   830
        }
dvander@4116
   831
        break;
dvander@4116
   832
      }
dvander@4116
   833
dvander@4116
   834
      case OP_TRACKER_POP_SETHEAP:
dvander@4116
   835
      {
dvander@4116
   836
        int error = PopTrackerAndSetHeap(rt);
dvander@4116
   837
        if (error != SP_ERROR_NONE) {
dvander@4116
   838
          ctx->err = error;
dvander@4116
   839
          goto error;
dvander@4116
   840
        }
dvander@4116
   841
        break;
dvander@4116
   842
      }
dvander@4116
   843
dvander@4116
   844
      case OP_BREAK:
dvander@4116
   845
        ctx->cip = uintptr_t(cip - 1) - uintptr_t(plugin->pcode);
dvander@4116
   846
        break;
dvander@4116
   847
dvander@4116
   848
      case OP_BOUNDS:
dvander@4116
   849
      {
dvander@4116
   850
        cell_t value = *cip++;
dvander@4116
   851
        if (uint32_t(pri) > uint32_t(value)) {
dvander@4116
   852
          ctx->err = SP_ERROR_ARRAY_BOUNDS;
dvander@4116
   853
          goto error;
dvander@4116
   854
        }
dvander@4116
   855
        break;
dvander@4116
   856
      }
dvander@4116
   857
dvander@4116
   858
      case OP_CALL:
dvander@4116
   859
      {
dvander@4116
   860
        cell_t offset = *cip++;
dvander@4116
   861
dvander@4116
   862
        if (!IsValidOffset(offset) || uint32_t(offset) >= plugin->pcode_size) {
dvander@4116
   863
          ctx->err = SP_ERROR_INSTRUCTION_PARAM;
dvander@4116
   864
          goto error;
dvander@4116
   865
        }
dvander@4116
   866
dvander@4116
   867
        if (ctx->rp >= SP_MAX_RETURN_STACK) {
dvander@4116
   868
          ctx->err = SP_ERROR_STACKLOW;
dvander@4116
   869
          goto error;
dvander@4116
   870
        }
dvander@4116
   871
dvander@4116
   872
        // For debugging.
dvander@4116
   873
        uintptr_t rcip = uintptr_t(cip - 2) - uintptr_t(plugin->pcode);
dvander@4116
   874
        ctx->rstk_cips[ctx->rp++] = rcip;
dvander@4116
   875
        ctx->cip = offset;
dvander@4116
   876
        ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
dvander@4116
   877
dvander@4116
   878
        int err = Interpret(rt, offset, &pri);
dvander@4116
   879
dvander@4116
   880
        stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
dvander@4116
   881
        ctx->cip = rcip;
dvander@4116
   882
        ctx->rp--;
dvander@4116
   883
dvander@4116
   884
        if (err != SP_ERROR_NONE)
dvander@4116
   885
          goto error;
dvander@4116
   886
        break;
dvander@4116
   887
      }
dvander@4116
   888
dvander@4116
   889
      case OP_GENARRAY:
dvander@4116
   890
      case OP_GENARRAY_Z:
dvander@4116
   891
      {
dvander@4116
   892
        cell_t val = *cip++;
dvander@4116
   893
        if (!GenerateArray(rt, ctx, val, stk, op == OP_GENARRAY_Z))
dvander@4116
   894
          goto error;
dvander@4116
   895
dvander@4116
   896
        stk += (val - 1) * 4;
dvander@4116
   897
        break;
dvander@4116
   898
      }
dvander@4116
   899
dvander@4116
   900
      case OP_SYSREQ_C:
dvander@4116
   901
      case OP_SYSREQ_N:
dvander@4116
   902
      {
dvander@4116
   903
        uint32_t native_index = *cip++;
dvander@4116
   904
dvander@4116
   905
        if (native_index >= plugin->num_natives) {
dvander@4116
   906
          ctx->err = SP_ERROR_INSTRUCTION_PARAM;
dvander@4116
   907
          goto error;
dvander@4116
   908
        }
dvander@4116
   909
dvander@4116
   910
        uint32_t num_params;
dvander@4116
   911
        if (op == OP_SYSREQ_N) {
dvander@4116
   912
          num_params = *cip++;
dvander@4116
   913
          *--stk = num_params;
dvander@4116
   914
        }
dvander@4116
   915
dvander@4116
   916
        ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
dvander@4116
   917
        pri = NativeCallback(ctx, native_index, stk);
dvander@4116
   918
        if (ctx->n_err != SP_ERROR_NONE) {
dvander@4116
   919
          ctx->err = ctx->n_err;
dvander@4116
   920
          goto error;
dvander@4116
   921
        }
dvander@4116
   922
dvander@4116
   923
        if (op == OP_SYSREQ_N)
dvander@4116
   924
          stk += num_params + 1;
dvander@4116
   925
        break;
dvander@4116
   926
      }
dvander@4116
   927
dvander@4116
   928
      case OP_SWITCH:
dvander@4116
   929
      {
dvander@4116
   930
        cell_t offset = *cip++;
dvander@4116
   931
        cell_t *table = reinterpret_cast<cell_t *>(plugin->pcode + offset + sizeof(cell_t));
dvander@4116
   932
dvander@4116
   933
        size_t ncases = *table++;
dvander@4116
   934
        cell_t target = *table++;   // default case
dvander@4116
   935
dvander@4116
   936
        for (size_t i = 0; i < ncases; i++) {
dvander@4116
   937
          if (table[i * 2] == pri) {
dvander@4116
   938
            target = table[i * 2 + 1];
dvander@4116
   939
            break;
dvander@4116
   940
          }
dvander@4116
   941
        }
dvander@4116
   942
dvander@4116
   943
        if ((cip = Jump(plugin, ctx, target)) == NULL)
dvander@4116
   944
          goto error;
dvander@4116
   945
        break;
dvander@4116
   946
      }
dvander@4116
   947
dvander@4116
   948
      default:
dvander@4116
   949
      {
dvander@4116
   950
        ctx->err = SP_ERROR_INVALID_INSTRUCTION;
dvander@4116
   951
        goto error;
dvander@4116
   952
      }
dvander@4116
   953
    } // switch
dvander@4116
   954
  }
dvander@4116
   955
dvander@4116
   956
 done:
dvander@4116
   957
  assert(orig_frm == ctx->frm);
dvander@4116
   958
  ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
dvander@4116
   959
  return ctx->err;
dvander@4116
   960
dvander@4116
   961
 error:
dvander@4116
   962
  ctx->frm = orig_frm;
dvander@4116
   963
  goto done;
dvander@4116
   964
}
dvander@4116
   965