sourcepawn/jit/interpreter.cpp
author Kyle Sanderson <kyle.leet@gmail.com>
Sun May 11 14:36:32 2014 -0700 (2014-05-11)
changeset 4358 52aa5fab9866
parent 4156 319e80e8e97f
permissions -rw-r--r--
Optimize native call if statically bound (bug 5842, r=ds,fyren f=dvander).
Original patch written by David Anderson (original r from Fyren).
- Updated to function with the current JIT compiler.
- Took in clean-ups from later-dated patches.
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
kyle@4358
   170
cell_t
kyle@4358
   171
BoundNativeCallback(sp_context_t *ctx, SPVM_NATIVE_FUNC pfn, cell_t *params)
kyle@4358
   172
{
kyle@4358
   173
  cell_t save_sp = ctx->sp;
kyle@4358
   174
  cell_t save_hp = ctx->hp;
kyle@4358
   175
kyle@4358
   176
  cell_t result = pfn(ctx->basecx, params);
kyle@4358
   177
kyle@4358
   178
  if (ctx->n_err != SP_ERROR_NONE)
kyle@4358
   179
    return result;
kyle@4358
   180
kyle@4358
   181
  if (save_sp != ctx->sp) {
kyle@4358
   182
    ctx->n_err = SP_ERROR_STACKLEAK;
kyle@4358
   183
    return result;
kyle@4358
   184
  }
kyle@4358
   185
  if (save_hp != ctx->hp) {
kyle@4358
   186
    ctx->n_err = SP_ERROR_HEAPLEAK;
kyle@4358
   187
    return result;
kyle@4358
   188
  }
kyle@4358
   189
kyle@4358
   190
  return result;
kyle@4358
   191
}
kyle@4358
   192
dvander@4116
   193
static inline bool
dvander@4116
   194
GenerateArray(BaseRuntime *rt, sp_context_t *ctx, cell_t dims, cell_t *stk, bool autozero)
dvander@4116
   195
{
dvander@4116
   196
  if (dims == 1) {
dvander@4116
   197
    uint32_t size = *stk;
dvander@4116
   198
    if (size == 0 || !ke::IsUint32MultiplySafe(size, 4)) {
dvander@4116
   199
      ctx->err = SP_ERROR_ARRAY_TOO_BIG;
dvander@4116
   200
      return false;
dvander@4116
   201
    }
dvander@4116
   202
    *stk = ctx->hp;
dvander@4116
   203
dvander@4116
   204
    uint32_t bytes = size * 4;
dvander@4116
   205
dvander@4116
   206
    ctx->hp += bytes;
dvander@4116
   207
    if (uintptr_t(ctx->plugin->memory + ctx->hp) >= uintptr_t(stk)) {
dvander@4116
   208
      ctx->err = SP_ERROR_HEAPLOW;
dvander@4116
   209
      return false;
dvander@4116
   210
    }
dvander@4116
   211
dvander@4116
   212
    if ((ctx->err = PushTracker(ctx, bytes)) != SP_ERROR_NONE)
dvander@4116
   213
      return false;
dvander@4116
   214
dvander@4116
   215
    if (autozero)
dvander@4116
   216
      memset(ctx->plugin->memory + ctx->hp, 0, bytes);
dvander@4116
   217
dvander@4116
   218
    return true;
dvander@4116
   219
  }
dvander@4116
   220
dvander@4116
   221
  if ((ctx->err = GenerateFullArray(rt, dims, stk, autozero)) != SP_ERROR_NONE)
dvander@4116
   222
    return false;
dvander@4116
   223
dvander@4116
   224
  return true;
dvander@4116
   225
}
dvander@4116
   226
dvander@4116
   227
int
dvander@4116
   228
Interpret(BaseRuntime *rt, uint32_t aCodeStart, cell_t *rval)
dvander@4116
   229
{
dvander@4116
   230
  const sp_plugin_t *plugin = rt->plugin();
dvander@4116
   231
  cell_t *code = reinterpret_cast<cell_t *>(plugin->pcode);
dvander@4116
   232
  cell_t *codeend = reinterpret_cast<cell_t *>(plugin->pcode + plugin->pcode_size);
dvander@4116
   233
dvander@4116
   234
  if (!IsValidOffset(aCodeStart) || aCodeStart > plugin->pcode_size)
dvander@4116
   235
    return SP_ERROR_INVALID_INSTRUCTION;
dvander@4116
   236
dvander@4116
   237
  sp_context_t *ctx = rt->GetBaseContext()->GetCtx();
dvander@4116
   238
  ctx->err = SP_ERROR_NONE;
dvander@4116
   239
dvander@4116
   240
  // Save the original frm. BaseContext won't, and if we error, we won't hit
dvander@4116
   241
  // the stack unwinding code.
dvander@4116
   242
  cell_t orig_frm = ctx->frm;
dvander@4116
   243
dvander@4116
   244
  cell_t pri = 0;
dvander@4116
   245
  cell_t alt = 0;
dvander@4116
   246
  cell_t *cip = code + (aCodeStart / 4);
dvander@4116
   247
  cell_t *stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
dvander@4116
   248
dvander@4116
   249
  for (;;) {
dvander@4116
   250
    if (cip >= codeend) {
dvander@4116
   251
      ctx->err = SP_ERROR_INVALID_INSTRUCTION;
dvander@4116
   252
      goto error;
dvander@4116
   253
    }
dvander@4116
   254
dvander@4116
   255
#if 0
dvander@4116
   256
    SpewOpcode(plugin, reinterpret_cast<cell_t *>(plugin->pcode + aCodeStart), cip);
dvander@4116
   257
#endif
dvander@4116
   258
dvander@4116
   259
    OPCODE op = (OPCODE)*cip++;
dvander@4116
   260
dvander@4116
   261
    switch (op) {
dvander@4116
   262
      case OP_MOVE_PRI:
dvander@4116
   263
        pri = alt;
dvander@4116
   264
        break;
dvander@4116
   265
      case OP_MOVE_ALT:
dvander@4116
   266
        alt = pri;
dvander@4116
   267
        break;
dvander@4116
   268
dvander@4116
   269
      case OP_XCHG:
dvander@4116
   270
      {
dvander@4116
   271
        cell_t tmp = pri;
dvander@4116
   272
        pri = alt;
dvander@4116
   273
        alt = tmp;
dvander@4116
   274
        break;
dvander@4116
   275
      }
dvander@4116
   276
dvander@4116
   277
      case OP_ZERO:
dvander@4116
   278
        Write(plugin, *cip++, 0);
dvander@4116
   279
        break;
dvander@4116
   280
dvander@4116
   281
      case OP_ZERO_S:
dvander@4156
   282
        Write(plugin, ctx->frm + *cip++, 0);
dvander@4116
   283
        break;
dvander@4116
   284
dvander@4116
   285
      case OP_PUSH_PRI:
dvander@4116
   286
        *--stk = pri;
dvander@4116
   287
        break;
dvander@4116
   288
      case OP_PUSH_ALT:
dvander@4116
   289
        *--stk = alt;
dvander@4116
   290
        break;
dvander@4116
   291
dvander@4116
   292
      case OP_PUSH_C:
dvander@4116
   293
      case OP_PUSH2_C:
dvander@4116
   294
      case OP_PUSH3_C:
dvander@4116
   295
      case OP_PUSH4_C:
dvander@4116
   296
      case OP_PUSH5_C:
dvander@4116
   297
      {
dvander@4116
   298
        int n = 1;
dvander@4116
   299
        if (op >= OP_PUSH2_C)
dvander@4116
   300
          n = ((op - OP_PUSH2_C) / 4) + 2;
dvander@4116
   301
dvander@4116
   302
        int i = 1;
dvander@4116
   303
        do {
dvander@4116
   304
          *--stk = *cip++;
dvander@4116
   305
        } while (i++ < n);
dvander@4116
   306
        break;
dvander@4116
   307
      }
dvander@4116
   308
dvander@4116
   309
      case OP_PUSH_ADR:
dvander@4116
   310
      case OP_PUSH2_ADR:
dvander@4116
   311
      case OP_PUSH3_ADR:
dvander@4116
   312
      case OP_PUSH4_ADR:
dvander@4116
   313
      case OP_PUSH5_ADR:
dvander@4116
   314
      {
dvander@4116
   315
        int n = 1;
dvander@4116
   316
        if (op >= OP_PUSH2_ADR)
dvander@4116
   317
          n = ((op - OP_PUSH2_ADR) / 4) + 2;
dvander@4116
   318
dvander@4116
   319
        int i = 1;
dvander@4116
   320
dvander@4116
   321
        do {
dvander@4116
   322
          cell_t addr = ctx->frm + *cip++;
dvander@4116
   323
          *--stk = addr;
dvander@4116
   324
        } while (i++ < n);
dvander@4116
   325
        break;
dvander@4116
   326
      }
dvander@4116
   327
dvander@4116
   328
      case OP_PUSH_S:
dvander@4116
   329
      case OP_PUSH2_S:
dvander@4116
   330
      case OP_PUSH3_S:
dvander@4116
   331
      case OP_PUSH4_S:
dvander@4116
   332
      case OP_PUSH5_S:
dvander@4116
   333
      {
dvander@4116
   334
        int n = 1;
dvander@4116
   335
        if (op >= OP_PUSH2_S)
dvander@4116
   336
          n = ((op - OP_PUSH2_S) / 4) + 2;
dvander@4116
   337
dvander@4116
   338
        int i = 1;
dvander@4116
   339
        do {
dvander@4116
   340
          cell_t value = Read(plugin, ctx->frm + *cip++);
dvander@4116
   341
          *--stk = value;
dvander@4116
   342
        } while (i++ < n);
dvander@4116
   343
        break;
dvander@4116
   344
      }
dvander@4116
   345
dvander@4116
   346
      case OP_PUSH:
dvander@4116
   347
      case OP_PUSH2:
dvander@4116
   348
      case OP_PUSH3:
dvander@4116
   349
      case OP_PUSH4:
dvander@4116
   350
      case OP_PUSH5:
dvander@4116
   351
      {
dvander@4116
   352
        int n = 1;
dvander@4116
   353
        if (op >= OP_PUSH2)
dvander@4116
   354
          n = ((op - OP_PUSH2) / 4) + 2;
dvander@4116
   355
dvander@4116
   356
        int i = 1;
dvander@4116
   357
        do {
dvander@4116
   358
          cell_t value = Read(plugin, *cip++);
dvander@4116
   359
          *--stk = value;
dvander@4116
   360
        } while (i++ < n);
dvander@4116
   361
        break;
dvander@4116
   362
      }
dvander@4116
   363
dvander@4116
   364
      case OP_ZERO_PRI:
dvander@4116
   365
        pri = 0;
dvander@4116
   366
        break;
dvander@4116
   367
      case OP_ZERO_ALT:
dvander@4116
   368
        alt = 0;
dvander@4116
   369
        break;
dvander@4116
   370
dvander@4116
   371
      case OP_ADD:
dvander@4116
   372
        pri += alt;
dvander@4116
   373
        break;
dvander@4116
   374
dvander@4116
   375
      case OP_SUB:
dvander@4116
   376
        pri -= alt;
dvander@4116
   377
        break;
dvander@4116
   378
dvander@4116
   379
      case OP_SUB_ALT:
dvander@4116
   380
        pri = alt - pri;
dvander@4116
   381
        break;
dvander@4116
   382
dvander@4116
   383
      case OP_PROC:
dvander@4116
   384
      {
dvander@4116
   385
        *--stk = ctx->frm;
dvander@4116
   386
        *--stk = 0;
dvander@4116
   387
        ctx->frm = uintptr_t(stk) - uintptr_t(plugin->memory);
dvander@4116
   388
        break;
dvander@4116
   389
      }
dvander@4116
   390
dvander@4116
   391
      case OP_IDXADDR_B:
dvander@4116
   392
        pri <<= *cip++;
dvander@4116
   393
        pri += alt;
dvander@4116
   394
        break;
dvander@4116
   395
dvander@4116
   396
      case OP_SHL:
dvander@4116
   397
        pri <<= alt;
dvander@4116
   398
        break;
dvander@4116
   399
dvander@4116
   400
      case OP_SHR:
dvander@4116
   401
        pri = unsigned(pri) >> unsigned(alt);
dvander@4116
   402
        break;
dvander@4116
   403
dvander@4116
   404
      case OP_SSHR:
dvander@4116
   405
        pri >>= alt;
dvander@4116
   406
        break;
dvander@4116
   407
dvander@4116
   408
      case OP_SHL_C_PRI:
dvander@4116
   409
        pri <<= *cip++;
dvander@4116
   410
        break;
dvander@4116
   411
      case OP_SHL_C_ALT:
dvander@4116
   412
        alt <<= *cip++;
dvander@4116
   413
        break;
dvander@4116
   414
dvander@4116
   415
      case OP_SHR_C_PRI:
dvander@4116
   416
        pri >>= *cip++;
dvander@4116
   417
        break;
dvander@4116
   418
      case OP_SHR_C_ALT:
dvander@4116
   419
        alt >>= *cip++;
dvander@4116
   420
        break;
dvander@4116
   421
dvander@4116
   422
      case OP_SMUL:
dvander@4116
   423
        pri *= alt;
dvander@4116
   424
        break;
dvander@4116
   425
dvander@4116
   426
      case OP_NOT:
dvander@4116
   427
        pri = pri ? 0 : 1;
dvander@4116
   428
        break;
dvander@4116
   429
dvander@4116
   430
      case OP_NEG:
dvander@4116
   431
        pri = -pri;
dvander@4116
   432
        break;
dvander@4116
   433
dvander@4116
   434
      case OP_XOR:
dvander@4116
   435
        pri ^= alt;
dvander@4116
   436
        break;
dvander@4116
   437
dvander@4116
   438
      case OP_OR:
dvander@4116
   439
        pri |= alt;
dvander@4116
   440
        break;
dvander@4116
   441
dvander@4116
   442
      case OP_AND:
dvander@4116
   443
        pri &= alt;
dvander@4116
   444
        break;
dvander@4116
   445
dvander@4116
   446
      case OP_INVERT:
dvander@4116
   447
        pri = ~pri;
dvander@4116
   448
        break;
dvander@4116
   449
dvander@4116
   450
      case OP_ADD_C:
dvander@4116
   451
        pri += *cip++;
dvander@4116
   452
        break;
dvander@4116
   453
dvander@4116
   454
      case OP_SMUL_C:
dvander@4116
   455
        pri *= *cip++;
dvander@4116
   456
        break;
dvander@4116
   457
dvander@4116
   458
      case OP_EQ:
dvander@4116
   459
        pri = pri == alt;
dvander@4116
   460
        break;
dvander@4116
   461
dvander@4116
   462
      case OP_NEQ:
dvander@4116
   463
        pri = pri != alt;
dvander@4116
   464
        break;
dvander@4116
   465
dvander@4116
   466
      case OP_SLESS:
dvander@4116
   467
        pri = pri < alt;
dvander@4116
   468
        break;
dvander@4116
   469
dvander@4116
   470
      case OP_SLEQ:
dvander@4116
   471
        pri = pri <= alt;
dvander@4116
   472
        break;
dvander@4116
   473
dvander@4116
   474
      case OP_SGRTR:
dvander@4116
   475
        pri = pri > alt;
dvander@4116
   476
        break;
dvander@4116
   477
        
dvander@4116
   478
      case OP_SGEQ:
dvander@4116
   479
        pri = pri >= alt;
dvander@4116
   480
        break;
dvander@4116
   481
dvander@4116
   482
      case OP_EQ_C_PRI:
dvander@4116
   483
        pri = pri == *cip++;
dvander@4116
   484
        break;
dvander@4116
   485
      case OP_EQ_C_ALT:
dvander@4116
   486
        pri = alt == *cip++;
dvander@4116
   487
        break;
dvander@4116
   488
dvander@4116
   489
      case OP_INC_PRI:
dvander@4116
   490
        pri++;
dvander@4116
   491
        break;
dvander@4116
   492
      case OP_INC_ALT:
dvander@4116
   493
        alt++;
dvander@4116
   494
        break;
dvander@4116
   495
dvander@4116
   496
      case OP_INC:
dvander@4116
   497
      {
dvander@4116
   498
        cell_t offset = *cip++;
dvander@4116
   499
        Write(plugin, offset, Read(plugin, offset) + 1);
dvander@4116
   500
        break;
dvander@4116
   501
      }
dvander@4116
   502
dvander@4116
   503
      case OP_INC_S:
dvander@4116
   504
      {
dvander@4116
   505
        cell_t offset = *cip++;
dvander@4116
   506
        cell_t value = Read(plugin, ctx->frm + offset);
dvander@4116
   507
        Write(plugin, ctx->frm + offset, value + 1);
dvander@4116
   508
        break;
dvander@4116
   509
      }
dvander@4116
   510
dvander@4116
   511
      case OP_INC_I:
dvander@4116
   512
        if (!CheckAddress(plugin, ctx, stk, pri))
dvander@4116
   513
          goto error;
dvander@4116
   514
        Write(plugin, pri, Read(plugin, pri) + 1);
dvander@4116
   515
        break;
dvander@4116
   516
dvander@4116
   517
      case OP_DEC_PRI:
dvander@4116
   518
        pri--;
dvander@4116
   519
        break;
dvander@4116
   520
      case OP_DEC_ALT:
dvander@4116
   521
        alt--;
dvander@4116
   522
        break;
dvander@4116
   523
dvander@4116
   524
      case OP_DEC:
dvander@4116
   525
      {
dvander@4116
   526
        cell_t offset = *cip++;
dvander@4116
   527
        Write(plugin, offset, Read(plugin, offset) - 1);
dvander@4116
   528
        break;
dvander@4116
   529
      }
dvander@4116
   530
dvander@4116
   531
      case OP_DEC_S:
dvander@4116
   532
      {
dvander@4116
   533
        cell_t offset = *cip++;
dvander@4116
   534
        cell_t value = Read(plugin, ctx->frm + offset);
dvander@4116
   535
        Write(plugin, ctx->frm + offset, value - 1);
dvander@4116
   536
        break;
dvander@4116
   537
      }
dvander@4116
   538
dvander@4116
   539
      case OP_DEC_I:
dvander@4116
   540
        if (!CheckAddress(plugin, ctx, stk, pri))
dvander@4116
   541
          goto error;
dvander@4116
   542
        Write(plugin, pri, Read(plugin, pri) - 1);
dvander@4116
   543
        break;
dvander@4116
   544
dvander@4116
   545
      case OP_LOAD_PRI:
dvander@4116
   546
        pri = Read(plugin, *cip++);
dvander@4116
   547
        break;
dvander@4116
   548
      case OP_LOAD_ALT:
dvander@4116
   549
        alt = Read(plugin, *cip++);
dvander@4116
   550
        break;
dvander@4116
   551
dvander@4116
   552
      case OP_LOAD_S_PRI:
dvander@4116
   553
        pri = Read(plugin, ctx->frm + *cip++);
dvander@4116
   554
        break;
dvander@4116
   555
      case OP_LOAD_S_ALT:
dvander@4116
   556
        alt = Read(plugin, ctx->frm + *cip++);
dvander@4116
   557
        break;
dvander@4116
   558
dvander@4116
   559
      case OP_LOAD_S_BOTH:
dvander@4116
   560
        pri = Read(plugin, ctx->frm + *cip++);
dvander@4116
   561
        alt = Read(plugin, ctx->frm + *cip++);
dvander@4116
   562
        break;
dvander@4116
   563
dvander@4116
   564
      case OP_LREF_S_PRI:
dvander@4116
   565
      {
dvander@4116
   566
        pri = Read(plugin, ctx->frm + *cip++);
dvander@4116
   567
        pri = Read(plugin, pri);
dvander@4116
   568
        break;
dvander@4116
   569
      }
dvander@4116
   570
dvander@4116
   571
      case OP_LREF_S_ALT:
dvander@4116
   572
      {
dvander@4116
   573
        alt = Read(plugin, ctx->frm + *cip++);
dvander@4116
   574
        alt = Read(plugin, alt);
dvander@4116
   575
        break;
dvander@4116
   576
      }
dvander@4116
   577
dvander@4116
   578
      case OP_CONST_PRI:
dvander@4116
   579
        pri = *cip++;
dvander@4116
   580
        break;
dvander@4116
   581
      case OP_CONST_ALT:
dvander@4116
   582
        alt = *cip++;
dvander@4116
   583
        break;
dvander@4116
   584
dvander@4116
   585
      case OP_ADDR_PRI:
dvander@4116
   586
        pri = ctx->frm + *cip++;
dvander@4116
   587
        break;
dvander@4116
   588
      case OP_ADDR_ALT:
dvander@4116
   589
        alt = ctx->frm + *cip++;
dvander@4116
   590
        break;
dvander@4116
   591
dvander@4116
   592
      case OP_STOR_PRI:
dvander@4116
   593
        Write(plugin, *cip++, pri);
dvander@4116
   594
        break;
dvander@4116
   595
      case OP_STOR_ALT:
dvander@4116
   596
        Write(plugin, *cip++, alt);
dvander@4116
   597
        break;
dvander@4116
   598
dvander@4116
   599
      case OP_STOR_S_PRI:
dvander@4116
   600
        Write(plugin, ctx->frm + *cip++, pri);
dvander@4116
   601
        break;
dvander@4116
   602
      case OP_STOR_S_ALT:
dvander@4116
   603
        Write(plugin, ctx->frm +*cip++, alt);
dvander@4116
   604
        break;
dvander@4116
   605
dvander@4116
   606
      case OP_IDXADDR:
dvander@4116
   607
        pri = alt + pri * 4;
dvander@4116
   608
        break;
dvander@4116
   609
dvander@4116
   610
      case OP_SREF_S_PRI:
dvander@4116
   611
      {
dvander@4116
   612
        cell_t offset = *cip++;
dvander@4116
   613
        cell_t addr = Read(plugin, ctx->frm + offset);
dvander@4116
   614
        Write(plugin, addr, pri);
dvander@4116
   615
        break;
dvander@4116
   616
      }
dvander@4116
   617
dvander@4116
   618
      case OP_SREF_S_ALT:
dvander@4116
   619
      {
dvander@4116
   620
        cell_t offset = *cip++;
dvander@4116
   621
        cell_t addr = Read(plugin, ctx->frm + offset);
dvander@4116
   622
        Write(plugin, addr, alt);
dvander@4116
   623
        break;
dvander@4116
   624
      }
dvander@4116
   625
dvander@4116
   626
      case OP_POP_PRI:
dvander@4116
   627
        pri = *stk++;
dvander@4116
   628
        break;
dvander@4116
   629
      case OP_POP_ALT:
dvander@4116
   630
        alt = *stk++;
dvander@4116
   631
        break;
dvander@4116
   632
dvander@4116
   633
      case OP_SWAP_PRI:
dvander@4116
   634
      case OP_SWAP_ALT:
dvander@4116
   635
      {
dvander@4116
   636
        cell_t reg = (op == OP_SREF_S_PRI) ? pri : alt;
dvander@4116
   637
        cell_t temp = *stk;
dvander@4116
   638
        *stk = reg;
dvander@4116
   639
        reg = temp;
dvander@4116
   640
        break;
dvander@4116
   641
      }
dvander@4116
   642
dvander@4116
   643
      case OP_LIDX:
dvander@4116
   644
        pri = alt + pri * 4;
dvander@4116
   645
        if (!CheckAddress(plugin, ctx, stk, pri))
dvander@4116
   646
          goto error;
dvander@4116
   647
        pri = Read(plugin, pri);
dvander@4116
   648
        break;
dvander@4116
   649
dvander@4116
   650
      case OP_LIDX_B:
dvander@4116
   651
      {
dvander@4116
   652
        cell_t val = *cip++;
dvander@4116
   653
        pri = alt + (pri << val);
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
dvander@4116
   660
      case OP_CONST:
dvander@4116
   661
      {
dvander@4116
   662
        cell_t offset = *cip++;
dvander@4116
   663
        cell_t value = *cip++;
dvander@4116
   664
        Write(plugin, offset, value);
dvander@4116
   665
        break;
dvander@4116
   666
      }
dvander@4116
   667
dvander@4116
   668
      case OP_CONST_S:
dvander@4116
   669
      {
dvander@4116
   670
        cell_t offset = *cip++;
dvander@4116
   671
        cell_t value = *cip++;
dvander@4116
   672
        Write(plugin, ctx->frm + offset, value);
dvander@4116
   673
        break;
dvander@4116
   674
      }
dvander@4116
   675
dvander@4116
   676
      case OP_LOAD_I:
dvander@4116
   677
        if (!CheckAddress(plugin, ctx, stk, pri))
dvander@4116
   678
          goto error;
dvander@4116
   679
        pri = Read(plugin, pri);
dvander@4116
   680
        break;
dvander@4116
   681
dvander@4116
   682
      case OP_STOR_I:
dvander@4116
   683
        if (!CheckAddress(plugin, ctx, stk, alt))
dvander@4116
   684
          goto error;
dvander@4116
   685
        Write(plugin, alt, pri);
dvander@4116
   686
        break;
dvander@4116
   687
dvander@4116
   688
      case OP_SDIV:
dvander@4116
   689
      case OP_SDIV_ALT:
dvander@4116
   690
      {
dvander@4116
   691
        cell_t dividend = (op == OP_SDIV) ? pri : alt;
dvander@4116
   692
        cell_t divisor = (op == OP_SDIV) ? alt : pri;
dvander@4116
   693
        if (divisor == 0) {
dvander@4116
   694
          ctx->err = SP_ERROR_DIVIDE_BY_ZERO;
dvander@4116
   695
          goto error;
dvander@4116
   696
        }
dvander@4116
   697
        if (dividend == INT_MIN && divisor == -1) {
dvander@4116
   698
          ctx->err = SP_ERROR_INTEGER_OVERFLOW;
dvander@4116
   699
          goto error;
dvander@4116
   700
        }
dvander@4116
   701
        pri = dividend / divisor;
dvander@4116
   702
        alt = dividend % divisor;
dvander@4116
   703
        break;
dvander@4116
   704
      }
dvander@4116
   705
      
dvander@4116
   706
      case OP_LODB_I:
dvander@4116
   707
      {
dvander@4116
   708
        cell_t val = *cip++;
dvander@4116
   709
        if (!CheckAddress(plugin, ctx, stk, pri))
dvander@4116
   710
          goto error;
dvander@4116
   711
        pri = Read(plugin, pri);
dvander@4116
   712
        if (val == 1)
dvander@4116
   713
          pri &= 0xff;
dvander@4116
   714
        else if (val == 2)
dvander@4116
   715
          pri &= 0xffff;
dvander@4116
   716
        break;
dvander@4116
   717
      }
dvander@4116
   718
dvander@4116
   719
      case OP_STRB_I:
dvander@4116
   720
      {
dvander@4116
   721
        cell_t val = *cip++;
dvander@4116
   722
        if (!CheckAddress(plugin, ctx, stk, alt))
dvander@4116
   723
          goto error;
dvander@4116
   724
        if (val == 1)
dvander@4116
   725
          *reinterpret_cast<int8_t *>(plugin->memory + alt) = pri;
dvander@4116
   726
        else if (val == 2)
dvander@4116
   727
          *reinterpret_cast<int16_t *>(plugin->memory + alt) = pri;
dvander@4116
   728
        else if (val == 4)
dvander@4116
   729
          *reinterpret_cast<int32_t *>(plugin->memory + alt) = pri;
dvander@4116
   730
        break;
dvander@4116
   731
      }
dvander@4116
   732
dvander@4116
   733
      case OP_RETN:
dvander@4116
   734
      {
dvander@4116
   735
        stk++;
dvander@4116
   736
        ctx->frm = *stk++;
dvander@4116
   737
        stk += *stk + 1;
dvander@4116
   738
        *rval = pri;
dvander@4116
   739
        ctx->err = SP_ERROR_NONE;
dvander@4116
   740
        goto done;
dvander@4116
   741
      }
dvander@4116
   742
dvander@4116
   743
      case OP_MOVS:
dvander@4116
   744
      {
dvander@4116
   745
        uint8_t *src = plugin->memory + pri;
dvander@4116
   746
        uint8_t *dest = plugin->memory + alt;
dvander@4116
   747
        memcpy(dest, src, *cip++);
dvander@4116
   748
        break;
dvander@4116
   749
      }
dvander@4116
   750
dvander@4116
   751
      case OP_FILL:
dvander@4116
   752
      {
dvander@4116
   753
        uint8_t *dest = plugin->memory + alt;
dvander@4116
   754
        memset(dest, pri, *cip++);
dvander@4116
   755
        break;
dvander@4116
   756
      }
dvander@4116
   757
dvander@4116
   758
      case OP_STRADJUST_PRI:
dvander@4116
   759
        pri += 4;
dvander@4116
   760
        pri >>= 2;
dvander@4116
   761
        break;
dvander@4116
   762
dvander@4116
   763
      case OP_STACK:
dvander@4116
   764
      {
dvander@4116
   765
        cell_t amount = *cip++;
dvander@4116
   766
        if (!IsValidOffset(amount)) {
dvander@4116
   767
          ctx->err = SP_ERROR_INVALID_INSTRUCTION;
dvander@4116
   768
          goto error;
dvander@4116
   769
        }
dvander@4116
   770
dvander@4116
   771
        stk += amount / 4;
dvander@4116
   772
        if (amount > 0) {
dvander@4116
   773
          if (uintptr_t(stk) >= uintptr_t(plugin->memory + plugin->mem_size)) {
dvander@4116
   774
            ctx->err = SP_ERROR_STACKMIN;
dvander@4116
   775
            goto error;
dvander@4116
   776
          }
dvander@4116
   777
        } else {
dvander@4116
   778
          if (uintptr_t(stk) < uintptr_t(plugin->memory + ctx->hp + STACK_MARGIN)) {
dvander@4116
   779
            ctx->err = SP_ERROR_STACKLOW;
dvander@4116
   780
            goto error;
dvander@4116
   781
          }
dvander@4116
   782
        }
dvander@4116
   783
        break;
dvander@4116
   784
      }
dvander@4116
   785
dvander@4116
   786
      case OP_HEAP:
dvander@4116
   787
      {
dvander@4116
   788
        cell_t amount = *cip++;
dvander@4116
   789
dvander@4116
   790
        alt = ctx->hp;
dvander@4116
   791
        ctx->hp += amount;
dvander@4116
   792
dvander@4116
   793
        if (amount > 0) {
dvander@4116
   794
          if (uintptr_t(plugin->memory + ctx->hp) > uintptr_t(stk)) {
dvander@4116
   795
            ctx->err = SP_ERROR_HEAPLOW;
dvander@4116
   796
            goto error;
dvander@4116
   797
          }
dvander@4116
   798
        } else {
dvander@4116
   799
          if (uint32_t(ctx->hp) < plugin->data_size) {
dvander@4116
   800
            ctx->err = SP_ERROR_HEAPMIN;
dvander@4116
   801
            goto error;
dvander@4116
   802
          }
dvander@4116
   803
        }
dvander@4116
   804
        break;
dvander@4116
   805
      }
dvander@4116
   806
dvander@4116
   807
      case OP_JUMP:
dvander@4116
   808
        if ((cip = JumpTarget(plugin, ctx, cip, true)) == NULL)
dvander@4116
   809
          goto error;
dvander@4116
   810
        break;
dvander@4116
   811
dvander@4116
   812
      case OP_JZER:
dvander@4116
   813
        if ((cip = JumpTarget(plugin, ctx, cip, pri == 0)) == NULL)
dvander@4116
   814
          goto error;
dvander@4116
   815
        break;
dvander@4116
   816
      case OP_JNZ:
dvander@4116
   817
        if ((cip = JumpTarget(plugin, ctx, cip, pri != 0)) == NULL)
dvander@4116
   818
          goto error;
dvander@4116
   819
        break;
dvander@4116
   820
dvander@4116
   821
      case OP_JEQ:
dvander@4116
   822
        if ((cip = JumpTarget(plugin, ctx, cip, pri == alt)) == NULL)
dvander@4116
   823
          goto error;
dvander@4116
   824
        break;
dvander@4116
   825
      case OP_JNEQ:
dvander@4116
   826
        if ((cip = JumpTarget(plugin, ctx, cip, pri != alt)) == NULL)
dvander@4116
   827
          goto error;
dvander@4116
   828
        break;
dvander@4116
   829
      case OP_JSLESS:
dvander@4116
   830
        if ((cip = JumpTarget(plugin, ctx, cip, pri < alt)) == NULL)
dvander@4116
   831
          goto error;
dvander@4116
   832
        break;
dvander@4116
   833
      case OP_JSLEQ:
dvander@4116
   834
        if ((cip = JumpTarget(plugin, ctx, cip, pri <= alt)) == NULL)
dvander@4116
   835
          goto error;
dvander@4116
   836
        break;
dvander@4116
   837
      case OP_JSGRTR:
dvander@4116
   838
        if ((cip = JumpTarget(plugin, ctx, cip, pri > alt)) == NULL)
dvander@4116
   839
          goto error;
dvander@4116
   840
        break;
dvander@4116
   841
      case OP_JSGEQ:
dvander@4116
   842
        if ((cip = JumpTarget(plugin, ctx, cip, pri >= alt)) == NULL)
dvander@4116
   843
          goto error;
dvander@4116
   844
        break;
dvander@4116
   845
dvander@4116
   846
      case OP_TRACKER_PUSH_C:
dvander@4116
   847
      {
dvander@4116
   848
        cell_t amount = *cip++;
dvander@4116
   849
        int error = PushTracker(ctx, amount * 4);
dvander@4116
   850
        if (error != SP_ERROR_NONE) {
dvander@4116
   851
          ctx->err = error;
dvander@4116
   852
          goto error;
dvander@4116
   853
        }
dvander@4116
   854
        break;
dvander@4116
   855
      }
dvander@4116
   856
dvander@4116
   857
      case OP_TRACKER_POP_SETHEAP:
dvander@4116
   858
      {
dvander@4116
   859
        int error = PopTrackerAndSetHeap(rt);
dvander@4116
   860
        if (error != SP_ERROR_NONE) {
dvander@4116
   861
          ctx->err = error;
dvander@4116
   862
          goto error;
dvander@4116
   863
        }
dvander@4116
   864
        break;
dvander@4116
   865
      }
dvander@4116
   866
dvander@4116
   867
      case OP_BREAK:
dvander@4116
   868
        ctx->cip = uintptr_t(cip - 1) - uintptr_t(plugin->pcode);
dvander@4116
   869
        break;
dvander@4116
   870
dvander@4116
   871
      case OP_BOUNDS:
dvander@4116
   872
      {
dvander@4116
   873
        cell_t value = *cip++;
dvander@4116
   874
        if (uint32_t(pri) > uint32_t(value)) {
dvander@4116
   875
          ctx->err = SP_ERROR_ARRAY_BOUNDS;
dvander@4116
   876
          goto error;
dvander@4116
   877
        }
dvander@4116
   878
        break;
dvander@4116
   879
      }
dvander@4116
   880
dvander@4116
   881
      case OP_CALL:
dvander@4116
   882
      {
dvander@4116
   883
        cell_t offset = *cip++;
dvander@4116
   884
dvander@4116
   885
        if (!IsValidOffset(offset) || uint32_t(offset) >= plugin->pcode_size) {
dvander@4116
   886
          ctx->err = SP_ERROR_INSTRUCTION_PARAM;
dvander@4116
   887
          goto error;
dvander@4116
   888
        }
dvander@4116
   889
dvander@4116
   890
        if (ctx->rp >= SP_MAX_RETURN_STACK) {
dvander@4116
   891
          ctx->err = SP_ERROR_STACKLOW;
dvander@4116
   892
          goto error;
dvander@4116
   893
        }
dvander@4116
   894
dvander@4116
   895
        // For debugging.
dvander@4116
   896
        uintptr_t rcip = uintptr_t(cip - 2) - uintptr_t(plugin->pcode);
dvander@4116
   897
        ctx->rstk_cips[ctx->rp++] = rcip;
dvander@4116
   898
        ctx->cip = offset;
dvander@4116
   899
        ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
dvander@4116
   900
dvander@4116
   901
        int err = Interpret(rt, offset, &pri);
dvander@4116
   902
dvander@4116
   903
        stk = reinterpret_cast<cell_t *>(plugin->memory + ctx->sp);
dvander@4116
   904
        ctx->cip = rcip;
dvander@4116
   905
        ctx->rp--;
dvander@4116
   906
dvander@4116
   907
        if (err != SP_ERROR_NONE)
dvander@4116
   908
          goto error;
dvander@4116
   909
        break;
dvander@4116
   910
      }
dvander@4116
   911
dvander@4116
   912
      case OP_GENARRAY:
dvander@4116
   913
      case OP_GENARRAY_Z:
dvander@4116
   914
      {
dvander@4116
   915
        cell_t val = *cip++;
dvander@4116
   916
        if (!GenerateArray(rt, ctx, val, stk, op == OP_GENARRAY_Z))
dvander@4116
   917
          goto error;
dvander@4116
   918
dvander@4116
   919
        stk += (val - 1) * 4;
dvander@4116
   920
        break;
dvander@4116
   921
      }
dvander@4116
   922
dvander@4116
   923
      case OP_SYSREQ_C:
dvander@4116
   924
      case OP_SYSREQ_N:
dvander@4116
   925
      {
dvander@4116
   926
        uint32_t native_index = *cip++;
dvander@4116
   927
dvander@4116
   928
        if (native_index >= plugin->num_natives) {
dvander@4116
   929
          ctx->err = SP_ERROR_INSTRUCTION_PARAM;
dvander@4116
   930
          goto error;
dvander@4116
   931
        }
dvander@4116
   932
dvander@4116
   933
        uint32_t num_params;
dvander@4116
   934
        if (op == OP_SYSREQ_N) {
dvander@4116
   935
          num_params = *cip++;
dvander@4116
   936
          *--stk = num_params;
dvander@4116
   937
        }
dvander@4116
   938
dvander@4116
   939
        ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
dvander@4116
   940
        pri = NativeCallback(ctx, native_index, stk);
dvander@4116
   941
        if (ctx->n_err != SP_ERROR_NONE) {
dvander@4116
   942
          ctx->err = ctx->n_err;
dvander@4116
   943
          goto error;
dvander@4116
   944
        }
dvander@4116
   945
dvander@4116
   946
        if (op == OP_SYSREQ_N)
dvander@4116
   947
          stk += num_params + 1;
dvander@4116
   948
        break;
dvander@4116
   949
      }
dvander@4116
   950
dvander@4116
   951
      case OP_SWITCH:
dvander@4116
   952
      {
dvander@4116
   953
        cell_t offset = *cip++;
dvander@4116
   954
        cell_t *table = reinterpret_cast<cell_t *>(plugin->pcode + offset + sizeof(cell_t));
dvander@4116
   955
dvander@4116
   956
        size_t ncases = *table++;
dvander@4116
   957
        cell_t target = *table++;   // default case
dvander@4116
   958
dvander@4116
   959
        for (size_t i = 0; i < ncases; i++) {
dvander@4116
   960
          if (table[i * 2] == pri) {
dvander@4116
   961
            target = table[i * 2 + 1];
dvander@4116
   962
            break;
dvander@4116
   963
          }
dvander@4116
   964
        }
dvander@4116
   965
dvander@4116
   966
        if ((cip = Jump(plugin, ctx, target)) == NULL)
dvander@4116
   967
          goto error;
dvander@4116
   968
        break;
dvander@4116
   969
      }
dvander@4116
   970
dvander@4116
   971
      default:
dvander@4116
   972
      {
dvander@4116
   973
        ctx->err = SP_ERROR_INVALID_INSTRUCTION;
dvander@4116
   974
        goto error;
dvander@4116
   975
      }
dvander@4116
   976
    } // switch
dvander@4116
   977
  }
dvander@4116
   978
dvander@4116
   979
 done:
dvander@4116
   980
  assert(orig_frm == ctx->frm);
dvander@4116
   981
  ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory);
dvander@4116
   982
  return ctx->err;
dvander@4116
   983
dvander@4116
   984
 error:
dvander@4116
   985
  ctx->frm = orig_frm;
dvander@4116
   986
  goto done;
dvander@4116
   987
}
dvander@4116
   988