/* * Copyright (C) 2003, 2004, 2005, 2014, 2015 Filip Pizlo. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY FILIP PIZLO ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FILIP PIZLO OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef FP_GPC_H #define FP_GPC_H #include "tsf.h" #include /* this enumeration must contain the same instructions in the same order * as gpc_spec.in. */ enum gpc_cell { /* figure this might as well be here for completeness */ GPC_I_NOP, /* label instruction. this is translated into a nop. but * it contains a label 'number' as an operand. these numbers * do not have to be unique. whenever a label is used, it will be * translated into a jump to the nearest label instruction that has * that label as its operand, by searching either in the forward or * backward direction, depending on whether or not it is a forward * or backward jump. */ GPC_I_LABEL, /* we wrap up the useful subset of tsf_internal.h's converting * copy macros and then add conversion to them. the operands are * as follows: * COPY_xTOyz_INCabc * The effect is that the pointer at the top of the stack has * offset added to it; in the case of INCDST, this serves as the * source, while for INCSRC it serves as the destination. */ GPC_I_COPY_HTONC_INCDST, GPC_I_COPY_HTONS_INCDST, GPC_I_COPY_HTONL_INCDST, GPC_I_COPY_HTONLL_INCDST, GPC_I_COPY_HTONF_INCDST, GPC_I_COPY_HTOND_INCDST, GPC_I_COPY_HTONCHOICE_TO_UI8_INCDST, /* the rules: * -> if we do not change size or reduce size, we can ignore the * size. * -> if we expand, then the signedness of the original determines * whether or not we do sign expansion. * -> if expanding floats to ints, the signedness of the target * matters (though I'm not sure why - this is only a conservative * estimate based on looking at the things that gcc does for * float to int conversion). */ GPC_I_COPY_NTOHC_TO_BIT_INCSRC, GPC_I_COPY_NTOHC_INCSRC, GPC_I_COPY_NTOHC_TO_S_Z_INCSRC, GPC_I_COPY_NTOHC_TO_S_E_INCSRC, GPC_I_COPY_NTOHC_TO_L_Z_INCSRC, GPC_I_COPY_NTOHC_TO_L_E_INCSRC, GPC_I_COPY_NTOHC_TO_LL_Z_INCSRC, GPC_I_COPY_NTOHC_TO_LL_E_INCSRC, GPC_I_COPY_NTOHC_TO_F_Z_INCSRC, GPC_I_COPY_NTOHC_TO_F_E_INCSRC, GPC_I_COPY_NTOHC_TO_D_Z_INCSRC, GPC_I_COPY_NTOHC_TO_D_E_INCSRC, GPC_I_COPY_NTOHS_TO_BIT_INCSRC, GPC_I_COPY_NTOHS_TO_C_INCSRC, GPC_I_COPY_NTOHS_INCSRC, GPC_I_COPY_NTOHS_TO_L_Z_INCSRC, GPC_I_COPY_NTOHS_TO_L_E_INCSRC, GPC_I_COPY_NTOHS_TO_LL_Z_INCSRC, GPC_I_COPY_NTOHS_TO_LL_E_INCSRC, GPC_I_COPY_NTOHS_TO_F_Z_INCSRC, GPC_I_COPY_NTOHS_TO_F_E_INCSRC, GPC_I_COPY_NTOHS_TO_D_Z_INCSRC, GPC_I_COPY_NTOHS_TO_D_E_INCSRC, GPC_I_COPY_NTOHL_TO_BIT_INCSRC, GPC_I_COPY_NTOHL_TO_C_INCSRC, GPC_I_COPY_NTOHL_TO_S_INCSRC, GPC_I_COPY_NTOHL_INCSRC, GPC_I_COPY_NTOHL_TO_LL_Z_INCSRC, GPC_I_COPY_NTOHL_TO_LL_E_INCSRC, GPC_I_COPY_NTOHL_TO_F_Z_INCSRC, GPC_I_COPY_NTOHL_TO_F_E_INCSRC, GPC_I_COPY_NTOHL_TO_D_Z_INCSRC, GPC_I_COPY_NTOHL_TO_D_E_INCSRC, GPC_I_COPY_NTOHLL_TO_BIT_INCSRC, GPC_I_COPY_NTOHLL_TO_C_INCSRC, GPC_I_COPY_NTOHLL_TO_S_INCSRC, GPC_I_COPY_NTOHLL_TO_L_INCSRC, GPC_I_COPY_NTOHLL_INCSRC, GPC_I_COPY_NTOHLL_TO_F_Z_INCSRC, GPC_I_COPY_NTOHLL_TO_F_E_INCSRC, GPC_I_COPY_NTOHLL_TO_D_Z_INCSRC, GPC_I_COPY_NTOHLL_TO_D_E_INCSRC, GPC_I_COPY_NTOHF_TO_BIT_INCSRC, GPC_I_COPY_NTOHF_TO_C_Z_INCSRC, GPC_I_COPY_NTOHF_TO_C_E_INCSRC, GPC_I_COPY_NTOHF_TO_S_Z_INCSRC, GPC_I_COPY_NTOHF_TO_S_E_INCSRC, GPC_I_COPY_NTOHF_TO_L_Z_INCSRC, GPC_I_COPY_NTOHF_TO_L_E_INCSRC, GPC_I_COPY_NTOHF_TO_LL_Z_INCSRC, GPC_I_COPY_NTOHF_TO_LL_E_INCSRC, GPC_I_COPY_NTOHF_INCSRC, GPC_I_COPY_NTOHF_TO_D_INCSRC, GPC_I_COPY_NTOHD_TO_BIT_INCSRC, GPC_I_COPY_NTOHD_TO_C_Z_INCSRC, GPC_I_COPY_NTOHD_TO_C_E_INCSRC, GPC_I_COPY_NTOHD_TO_S_Z_INCSRC, GPC_I_COPY_NTOHD_TO_S_E_INCSRC, GPC_I_COPY_NTOHD_TO_L_Z_INCSRC, GPC_I_COPY_NTOHD_TO_L_E_INCSRC, GPC_I_COPY_NTOHD_TO_LL_Z_INCSRC, GPC_I_COPY_NTOHD_TO_LL_E_INCSRC, GPC_I_COPY_NTOHD_TO_F_INCSRC, GPC_I_COPY_NTOHD_INCSRC, /* below are convert and copy routines between two pointers and * offsets; these are designed for primitives. the data is copied * from the given offset from the topmost pointer to the given offset * from the second-to-topmost pointer. call these guys like so: * GPC_I_COPY_x_TO_y */ GPC_I_COPY_BIT, /* makes non-zero values one - good for converting to and from bits */ GPC_I_COPY_BIT_TO_S, GPC_I_COPY_BIT_TO_L, GPC_I_COPY_BIT_TO_LL, GPC_I_COPY_BIT_TO_F, GPC_I_COPY_BIT_TO_D, GPC_I_COPY_C, GPC_I_COPY_C_TO_S_Z, GPC_I_COPY_C_TO_S_E, GPC_I_COPY_C_TO_L_Z, GPC_I_COPY_C_TO_L_E, GPC_I_COPY_C_TO_LL_Z, GPC_I_COPY_C_TO_LL_E, GPC_I_COPY_C_TO_F_Z, GPC_I_COPY_C_TO_F_E, GPC_I_COPY_C_TO_D_Z, GPC_I_COPY_C_TO_D_E, GPC_I_COPY_S_TO_BIT, GPC_I_COPY_S_TO_C, GPC_I_COPY_S, GPC_I_COPY_S_TO_L_Z, GPC_I_COPY_S_TO_L_E, GPC_I_COPY_S_TO_LL_Z, GPC_I_COPY_S_TO_LL_E, GPC_I_COPY_S_TO_F_Z, GPC_I_COPY_S_TO_F_E, GPC_I_COPY_S_TO_D_Z, GPC_I_COPY_S_TO_D_E, GPC_I_COPY_L_TO_BIT, GPC_I_COPY_L_TO_C, GPC_I_COPY_L_TO_S, GPC_I_COPY_L, GPC_I_COPY_L_TO_LL_Z, GPC_I_COPY_L_TO_LL_E, GPC_I_COPY_L_TO_F_Z, GPC_I_COPY_L_TO_F_E, GPC_I_COPY_L_TO_D_Z, GPC_I_COPY_L_TO_D_E, GPC_I_COPY_LL_TO_BIT, GPC_I_COPY_LL_TO_C, GPC_I_COPY_LL_TO_S, GPC_I_COPY_LL_TO_L, GPC_I_COPY_LL, GPC_I_COPY_LL_TO_F_Z, GPC_I_COPY_LL_TO_F_E, GPC_I_COPY_LL_TO_D_Z, GPC_I_COPY_LL_TO_D_E, GPC_I_COPY_F_TO_BIT, GPC_I_COPY_F_TO_C_Z, GPC_I_COPY_F_TO_C_E, GPC_I_COPY_F_TO_S_Z, GPC_I_COPY_F_TO_S_E, GPC_I_COPY_F_TO_L_Z, GPC_I_COPY_F_TO_L_E, GPC_I_COPY_F_TO_LL_Z, GPC_I_COPY_F_TO_LL_E, GPC_I_COPY_F, GPC_I_COPY_F_TO_D, GPC_I_COPY_D_TO_BIT, GPC_I_COPY_D_TO_C_Z, GPC_I_COPY_D_TO_C_E, GPC_I_COPY_D_TO_S_Z, GPC_I_COPY_D_TO_S_E, GPC_I_COPY_D_TO_L_Z, GPC_I_COPY_D_TO_L_E, GPC_I_COPY_D_TO_LL_Z, GPC_I_COPY_D_TO_LL_E, GPC_I_COPY_D_TO_F, GPC_I_COPY_D, /* note that there are no 64-bit set operations. this is the * result of cells possibly being 32-bit instead of 64-bit. */ /* set operations. these take a constant off the stream and * shove it into the requested offset from the ptr * on top of the stack. use it like so: * GPC_I_SET_XX */ GPC_I_SET_C, GPC_I_SET_S, GPC_I_SET_L, /* set operations. these take a constant off the stream and * shove it into the requested offset from the ptr that is * second from top of the stack. use it like so: * GPC_I_SET_2ND_XX */ GPC_I_SET_2ND_C, GPC_I_SET_2ND_S, GPC_I_SET_2ND_L, /* note that the read instructions below all increment the * current buffer pointer register. */ /* do a bitvector read. the tsf_native_bitvector_t structure is * at the given offset from the topmost pointer. call this like so: * GPC_I_BITVECTOR_READ */ GPC_I_BITVECTOR_READ, /* bitvector read that converts the bitvector to something other than * a bitvector */ GPC_I_BITVECTOR_READ_TO_C, GPC_I_BITVECTOR_READ_TO_S, GPC_I_BITVECTOR_READ_TO_L, GPC_I_BITVECTOR_READ_TO_LL, GPC_I_BITVECTOR_READ_TO_F, GPC_I_BITVECTOR_READ_TO_D, /* bitvector read that converts something other than a bitvector into * a bitvector */ GPC_I_BITVECTOR_READ_FROM_C, GPC_I_BITVECTOR_READ_FROM_S, GPC_I_BITVECTOR_READ_FROM_L, GPC_I_BITVECTOR_READ_FROM_LL, GPC_I_BITVECTOR_READ_FROM_F, GPC_I_BITVECTOR_READ_FROM_D, GPC_I_BITVECTOR_READ_FROM_TSF_INTEGER_ARRAY, GPC_I_BITVECTOR_READ_FROM_TSF_LONG_ARRAY, /* do a bitvector write. the tsf_native_bitvector_t structure is at * the given offset from the topmost pointer. call this like so: * GPC_I_BITVECTOR_WRITE */ GPC_I_BITVECTOR_WRITE, /* do a byte array read. the tsf_native_array_t structure is at the * given offset from the topmost pointer. the length is multiplied * by the given multiplier to obtain the size of the array in bytes. * call this like so: * GPC_I_BYTE_ARRAY_READ */ GPC_I_BYTE_ARRAY_READ, /* do a byte array write. the tsf_native_array_t structure is at the * given offset from the topmost pointer. the length is multiplied * by the given multiplier to obtain the size of the array in bytes. * call this like so: * GPC_I_BYTE_ARRAY_WRITE */ GPC_I_BYTE_ARRAY_WRITE, /* What follows are instructions for supporting TSF integers. See the * comment above the declaration of TSF_TK_INTEGER in tsf.h. */ /* skip a TSF integer. */ GPC_I_TSF_INTEGER_SKIP, GPC_I_TSF_UNSIGNED_SKIP, GPC_I_TSF_LONG_SKIP, /* read a TSF integer and convert to a variety of things. */ GPC_I_TSF_INTEGER_READ_TO_BIT, GPC_I_TSF_INTEGER_READ_TO_C, GPC_I_TSF_INTEGER_READ_TO_S, GPC_I_TSF_INTEGER_READ, GPC_I_TSF_INTEGER_READ_TO_LL, GPC_I_TSF_INTEGER_READ_TO_F, GPC_I_TSF_INTEGER_READ_TO_D, GPC_I_TSF_UNSIGNED_READ_SUB1, /* reads onto the stack, not into memory. */ GPC_I_TSF_LONG_READ_TO_BIT, GPC_I_TSF_LONG_READ_TO_C, GPC_I_TSF_LONG_READ_TO_S, GPC_I_TSF_LONG_READ_TO_L, GPC_I_TSF_LONG_READ, GPC_I_TSF_LONG_READ_TO_F, GPC_I_TSF_LONG_READ_TO_D, /* increment the size register for a TSF integer. */ GPC_I_TSF_INTEGER_SIZE, GPC_I_TSF_UNSIGNED_PLUS1_SIZE, GPC_I_TSF_LONG_SIZE, /* write a TSF integer. */ GPC_I_TSF_INTEGER_WRITE, GPC_I_TSF_UNSIGNED_PLUS1_WRITE, GPC_I_TSF_LONG_WRITE, /* set the string to the empty string. the char* is at the given * offset from the topmost pointer. */ GPC_I_STRING_SETEMPTY, /* do a string read. the char* is at the given offset from the * topmost pointer. */ GPC_I_STRING_READ, /* do a string skip. */ GPC_I_STRING_SKIP, /* calculate the size for a string. the char* is at the given offset * from the topmost pointer. */ GPC_I_STRING_SIZE, /* do a string write. the char* is at the given offset from the * topmost pointer. */ GPC_I_STRING_WRITE, /* do a string copy. the second to topmost pointer plus the * destination offset points to the destination char*, while the * topmost pointer plus source offset points to the source char*. * call this like so: * GPC_I_STRING_COPY */ GPC_I_STRING_COPY, /* set the buffer to a buffer just containing void. the tsf_buffer_t* * is at the given offset frm the topmost pointer. */ GPC_I_ANY_SETEMPTY, /* do an any read. the tsf_buffer_t* is at the given offset from the * topmost pointer. */ GPC_I_ANY_READ, /* do an any skip. */ GPC_I_ANY_SKIP, /* calculate the size of an any. the tsf_buffer_t* is at the given * offset frm the topmost pointer. */ GPC_I_ANY_SIZE, /* do an any write. the tsf_buffer_t* is at the given offset from the * topmost pointer. */ GPC_I_ANY_WRITE, /* do an any copy. the second to topmost pointer plus the destination * offset points to the destination tsf_buffer_t*, while the topmost * pointer plus source offset points to the source tsf_buffer_t*. call * this like so: * GPC_I_ANY_COPY */ GPC_I_ANY_COPY, /* do a memcpy from one data structure to another. the * destination is second to topmost, while the source is topmost. * the operands are: offset from destination, offset from source, and * the size. */ GPC_I_MEMCPY, /* copy an array. the second to topmost pointer plus the * destination offset points to the destination tsf_native_array_t, * while the topmost pointer plus source offset points to the source * tsf_native_array_t. the other operand is the multiplier that * specifies the number of bytes for each array element. call this * like so: * GPC_I_COPY_ARRAY */ GPC_I_COPY_ARRAY, /* copy a bitvector. the second to topmost pointer plus the * destination offset points to the destination tsf_native_bitvector_t, * while the topmost pointer plus source offset points to the source * tsf_native_bitvector_t. call this like so: * GPC_I_COPY_ARRAY */ GPC_I_COPY_BITVECTOR, /* copy between bitvectors and other types of arrays */ GPC_I_COPY_BITVECTOR_FROM_C, GPC_I_COPY_BITVECTOR_FROM_S, GPC_I_COPY_BITVECTOR_FROM_L, GPC_I_COPY_BITVECTOR_FROM_LL, GPC_I_COPY_BITVECTOR_FROM_F, GPC_I_COPY_BITVECTOR_FROM_D, GPC_I_COPY_BITVECTOR_TO_C, GPC_I_COPY_BITVECTOR_TO_S, GPC_I_COPY_BITVECTOR_TO_L, GPC_I_COPY_BITVECTOR_TO_LL, GPC_I_COPY_BITVECTOR_TO_F, GPC_I_COPY_BITVECTOR_TO_D, /* read a 8-bit value and place it on the stack. if the value was * 255, it is instead converted to UINT32_MAX. */ GPC_I_READC_TO_CHOICE, /* read a 32-bit value and place it at the top of the stack. */ GPC_I_READL, /* do an array length write. operand is the offset from the * topmost pointer to the 32-bit array length. relevant * bounds checks are done. */ GPC_I_ARRAY_LEN_WRITE_FIELD, /* do an array length read. operand is the offset from the * topmost pointer to the 32-bit array length. relevant * bounds checks are done. */ GPC_I_ARRAY_LEN_READ_FIELD, /* do an array length read. the array length is pushed onto * the stack. relevant bounds checks are done. */ GPC_I_ARRAY_LEN_READ_LOCAL, /* do an array length read. discard the array length. */ GPC_I_ARRAY_LEN_SKIP, /* if the 8-bit value pointed to by the specified offset from * the topmost pointer is non-zero, set set specified bit in * the current buffer location. otherwise reset it. the * buffer pointer is not advanced. you call this thing as * follows: * GPC_I_BIT_MASK_WRITE */ GPC_I_BIT_MASK_WRITE, /* mask out the requested bit from the current buffer location * without advancing the pointer. if set, then set the 8-bit * value pointed to by the specified offset from the topmost * pointer to 1; otherwise, set it to 0. this instruction is * used as follows: * GPC_I_BIT_MASK_READ */ GPC_I_BIT_MASK_READ, /* same but store it in something other than a byte. */ GPC_I_BIT_MASK_READ_TO_S, GPC_I_BIT_MASK_READ_TO_L, GPC_I_BIT_MASK_READ_TO_LL, GPC_I_BIT_MASK_READ_TO_F, GPC_I_BIT_MASK_READ_TO_D, /* write a bit stored in a byte. the idea is that the value * is 'normalized', or converted to either a 1 or 0 prior to * being placed into the buffer. the only argument is the * offset. */ GPC_I_BIT_WRITE, /* read a bit store it in a byte. the idea is that the value is * 'normalized', or converted to either a 1 or 0 prior to being * placed into the user's data structure. the only argument is * the offset. */ GPC_I_BIT_READ, /* read a bit store it in something other than a byte. */ GPC_I_BIT_READ_TO_S, GPC_I_BIT_READ_TO_L, GPC_I_BIT_READ_TO_LL, GPC_I_BIT_READ_TO_F, GPC_I_BIT_READ_TO_D, /* skip some number of bytes. operand is the number of bytes. */ GPC_I_SKIP, /* skip an array; the operands are the size of each element, and * an offset from the topmost pointer. the array length is found * at that offset. call this like so: * GPC_I_ARRAY_BC_AND_SKIP_FIELD */ GPC_I_ARRAY_BC_AND_SKIP_FIELD, /* skip an array; this instruction reads the array length, does * relevant bounds checks, and skips the array. all it needs to * know is the number of bytes required by each array element. * call this like so: * GPC_I_ARRAY_SKIP */ GPC_I_ARRAY_SKIP, /* skip a bitvector; the operand is the offset from the topmost * pointer where the number of bits can be found. */ GPC_I_BITVECTOR_BC_AND_SKIP_FIELD, /* skip a bitvector; this instruction reads the array length, does * relevant bounds checks, and skips the bitvector. this * instruction takes no operands. */ GPC_I_BITVECTOR_SKIP, /* increment the size register by the specified number of bytes. */ GPC_I_INC_SIZE, /* examine the array length at the given offset from the topmost * pointer, and increment the size register by the number of * bytes that this array length would occupy in the stream. */ GPC_I_INC_SIZE_ARRAY_LEN, /* increment the size register by the size of the array, where the * second operand is the size in bytes of each element and the * first operand is an offset to a tsf_native_array_t structure. */ GPC_I_INC_SIZE_ARRAY, /* increment the size register to accomodate the number of bits * as specified by the 32-bit value at the given offset from the * topmost pointer. */ GPC_I_INC_SIZE_BITVECTOR, /* bounds check. bounds check failure leads to termination of * the interpreter with an error. operand is the number of * bytes we expect to be able to see. */ GPC_I_BC, /* array bounds check. bounds check failure leads to termination * of the interpreter with an error. operands are a size * constant and the offset from the topmost pointer to the * length of the array (32-bit). if constant*array_len bytes * are not available, the bounds check fails. call this like so: * GPC_I_ARRAY_BC_FIELD */ GPC_I_ARRAY_BC_FIELD, /* array bounds check. bounds check failure leads to termination * of the interpreter with an error. operand is a size * constant. the array length is the topmost stack element. if * constant*array_len bytes are not available, the bounds check * fails. */ GPC_I_ARRAY_BC_LOCAL, /* bitvector bounds check. bounds check failure leads to termination * of the interpreter with an error. the operand is the offset * from the topmost pointer to a 32-bit number of bits. if * (num_bits+7)>>3 bytes are not available, the bounds check fails. */ GPC_I_BITVECTOR_BC_FIELD, /* bitvector bounds check. bounds check failure leads to termination * of the interpreter with an error. the number of bits is the * topmost element on the stack. if (num_bits+7)>>3 bytes are not * available, the bounds check fails. */ GPC_I_BITVECTOR_BC_LOCAL, /* malloc a buffer. will be automatically deallocated upon * interpreter failure or if it is not returned. if allocation * fails, the interpreter fails. the allocation size is determined * by the size register. performing this allocation automatically sets * the end register so that bounds checks can be performed. */ GPC_I_MALLOC_BUF, /* pop the buffer off the stack. the current pointer, the buffer * pointer, the end pointer, the keep bit, and the types pointer are * all set. however, size is not set (so you cannot do a MALLOC_BUF * after this call). */ GPC_I_POP_BUF, /* destroy a buffer. */ GPC_I_DESTROY_BUFFER, /* create an output map. */ GPC_I_MAKE_OUT_MAP, /* examine the second-to-top-most pointer. if it is NULL, then perform a * region alloc and replace the second-to-top-most pointer with the result. * if the region pointer was NULL, then it is replaced with the result as * well, and the region is put into keep mode. the only operand is the * size. */ GPC_I_ALLOC_ROOT_MAYBE_2ND, /* allocate and push. this is a region alloc. failure here * leads to termination of the interpreter. the operand is * the size. */ GPC_I_ALLOC, /* allocate and push array. this is a region alloc. failure here * leads to termination of the interpreter. the operands are * the element size and the offset from the topmost pointer to * the tsf_native_array_t. tsf_native_array_t::len should be set * already, and is used to determine how many bytes to alloc. * tsf_native_array_t::data will be set to the same thing that * gets pushed -- that is, the newly allocated array. call this like * so: * GPC_I_ALLOC_ARRAY */ GPC_I_ALLOC_ARRAY, /* allocate and push array. this is a region alloc. failure here * leads to termination of the interpreter. the operands are * the element size and the offset from the second to topmost pointer to * the tsf_native_array_t. tsf_native_array_t::len should be set * already, and is used to determine how many bytes to alloc. * tsf_native_array_t::data will be set to the same thing that * gets pushed -- that is, the newly allocated array. call this like * so: * GPC_I_ALLOC_ARRAY */ GPC_I_ALLOC_ARRAY_2ND, /* call a function of the form void(*)(void*), passing it the given * offset from the topmost pointer. call it like so: * GPC_I_CALL */ GPC_I_CALL, /* add callback to current region. callback must be of the form * void(*)(void*). the argument is computed as an offset from the * topmost pointer. call it like so: * GPC_I_ADD_CBACK */ GPC_I_ADD_CBACK, /* call free with the top of the stack. */ GPC_I_FREE_IMMEDIATE, /* call free with the the pointer loaded from stack top + offset. */ GPC_I_FREE, /* store the topmost pointer at the given offset from the second-to- * topmost pointer. */ GPC_I_STORE_PTR, /* store the topmost pointer at the given offset from the third-to- * topmost pointer. */ GPC_I_STORE_PTR_2ND, /* store the topmost value at the given offset from the second-to- * topmost pointer. */ GPC_I_STORE_VAL, /* push a pointer onto the stack. operand is an * offset from current top-most pointer where this pointer * can be found. */ GPC_I_PUSH_PTR, /* push a pointer onto the stack. operand is an * offset from current second-to-top-most pointer where this pointer * can be found. */ GPC_I_PUSH_PTR_2ND, /* push a pointer onto the stack. operand is an * offset from current third-to-top-most pointer where this pointer * can be found. */ GPC_I_PUSH_PTR_3RD, /* push a 32-bit value onto the stack. operand is an * offset from current top-most pointer where this * value can be found. */ GPC_I_PUSH_VAL, /* dup the second-to-topmost pointer */ GPC_I_DUP_PTR_2ND, /* pop a pointer or value from the stack. */ GPC_I_POP, /* pop two things off the stack. */ GPC_I_TWO_POP, /* pop three things off the stack. */ GPC_I_THREE_POP, /* first, save the topmost pointer in a register. then, add the * 32-bit value stored at an offset from the second to * topmost pointer multiplied by the supplied constant to the topmost * pointer. finally, push the saved pointer onto the stack. * this weird instruction looks like this: * GPC_I_MULADD_PTR * What the heck does this instruction really do? It sets you up * for a pointer loop! */ GPC_I_REPUSH_MULADD_PTR, /* first, save the topmost pointer in a register. then, add the * 32-bit value stored at an offset from the third to * topmost pointer multiplied by the supplied constant to the topmost * pointer. finally, push the saved pointer onto the stack. * this weird instruction looks like this: * GPC_I_MULADD_PTR */ GPC_I_REPUSH_MULADD_PTR_2ND, /* compare (less-than) the topmost pointer to the next-to-topmost * pointer. if the comparison fails, jump to the given label. * this is a forward jump. */ GPC_I_COMPFAILJUMP, /* compare (less-than) the second to topmost pointer to the third * to topmost pointer. if the comparison fails, jump to the given * label. this is a forward jump. */ GPC_I_COMPFAILJUMP_2ND, /* add the given constant to the topmost pointer, compare it * (less-than) to the next-to-topmost pointer, and if the * comparison is true, jump to the given label. note that * this is a backward branch, so the label is looked for * backwards, not forwards. here's how to call it: * GPC_I_ADDCOMPJUMP