python-on-a-chip online compiler

Dependencies:   mbed TSI

/media/uploads/va009039/p14p-f446re.png

more info: python-on-a-chip

Revision:
0:65f1469d6bfb
Child:
1:28afb064a41c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vm/interp.c	Sat Mar 02 11:54:20 2013 +0000
@@ -0,0 +1,2288 @@
+/*
+# This file is Copyright 2002 Dean Hall.
+# This file is part of the PyMite VM.
+# This file is licensed under the MIT License.
+# See the LICENSE file for details.
+*/
+
+
+#undef __FILE_ID__
+#define __FILE_ID__ 0x09
+
+
+/**
+ * \file
+ * \brief VM Interpreter
+ *
+ * VM interpreter operations.
+ */
+
+
+#include "pm.h"
+
+
+PmReturn_t
+interpret(const uint8_t returnOnNoThreads)
+{
+    PmReturn_t retval = PM_RET_OK;
+    pPmObj_t pobj1 = C_NULL;
+    pPmObj_t pobj2 = C_NULL;
+    pPmObj_t pobj3 = C_NULL;
+    int16_t t16 = 0;
+    int8_t t8 = 0;
+    uint8_t bc;
+    uint8_t objid, objid2;
+
+    /* Activate a thread the first time */
+    retval = interp_reschedule();
+    PM_RETURN_IF_ERROR(retval);
+
+    /* Interpret loop */
+    for (;;)
+    {
+        if (gVmGlobal.pthread == C_NULL)
+        {
+            if (returnOnNoThreads)
+            {
+                /* User chose to return on no threads left */
+                return retval;
+            }
+
+            /*
+             * Without a frame there is nothing to execute, so reschedule
+             * (possibly activating a recently added thread).
+             */
+            retval = interp_reschedule();
+            PM_BREAK_IF_ERROR(retval);
+            continue;
+        }
+
+        /* Reschedule threads if flag is true? */
+        if (gVmGlobal.reschedule)
+        {
+            retval = interp_reschedule();
+            PM_BREAK_IF_ERROR(retval);
+        }
+
+        /* Get byte; the func post-incrs PM_IP */
+        bc = mem_getByte(PM_FP->fo_memspace, &PM_IP);
+        switch (bc)
+        {
+            case POP_TOP:
+                pobj1 = PM_POP();
+                continue;
+
+            case ROT_TWO:
+                pobj1 = TOS;
+                TOS = TOS1;
+                TOS1 = pobj1;
+                continue;
+
+            case ROT_THREE:
+                pobj1 = TOS;
+                TOS = TOS1;
+                TOS1 = TOS2;
+                TOS2 = pobj1;
+                continue;
+
+            case DUP_TOP:
+                pobj1 = TOS;
+                PM_PUSH(pobj1);
+                continue;
+
+            case ROT_FOUR:
+                pobj1 = TOS;
+                TOS = TOS1;
+                TOS1 = TOS2;
+                TOS2 = TOS3;
+                TOS3 = pobj1;
+                continue;
+
+            case NOP:
+                continue;
+
+            case UNARY_POSITIVE:
+                /* Raise TypeError if TOS is not an int */
+                if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT)
+#ifdef HAVE_FLOAT
+                    && (OBJ_GET_TYPE(TOS) != OBJ_TYPE_FLT)
+#endif /* HAVE_FLOAT */
+                    )
+                {
+                    PM_RAISE(retval, PM_RET_EX_TYPE);
+                    break;
+                }
+
+                /* When TOS is an int, this is a no-op */
+                continue;
+
+            case UNARY_NEGATIVE:
+#ifdef HAVE_FLOAT
+                if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT)
+                {
+                    retval = float_negative(TOS, &pobj2);
+                }
+                else
+#endif /* HAVE_FLOAT */
+                {
+                    retval = int_negative(TOS, &pobj2);
+                }
+                PM_BREAK_IF_ERROR(retval);
+                TOS = pobj2;
+                continue;
+
+            case UNARY_NOT:
+                pobj1 = PM_POP();
+                if (obj_isFalse(pobj1))
+                {
+                    PM_PUSH(PM_TRUE);
+                }
+                else
+                {
+                    PM_PUSH(PM_FALSE);
+                }
+                continue;
+
+#ifdef HAVE_BACKTICK
+            /* #244 Add support for the backtick operation (UNARY_CONVERT) */
+            case UNARY_CONVERT:
+                retval = obj_repr(TOS, &pobj3);
+                PM_BREAK_IF_ERROR(retval);
+                TOS = pobj3;
+                continue;
+#endif /* HAVE_BACKTICK */
+
+            case UNARY_INVERT:
+                /* Raise TypeError if it's not an int */
+                if (OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT)
+                {
+                    PM_RAISE(retval, PM_RET_EX_TYPE);
+                    break;
+                }
+
+                /* Otherwise perform bit-wise complement */
+                retval = int_bitInvert(TOS, &pobj2);
+                PM_BREAK_IF_ERROR(retval);
+                TOS = pobj2;
+                continue;
+
+            case LIST_APPEND:
+                /* list_append will raise a TypeError if TOS1 is not a list */
+                retval = list_append(TOS1, TOS);
+                PM_SP -= 2;
+                continue;
+
+            case BINARY_POWER:
+            case INPLACE_POWER:
+
+#ifdef HAVE_FLOAT
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT)
+                    || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT))
+                {
+                    /* Calculate float power */
+                    retval = float_op(TOS1, TOS, &pobj3, 'P');
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+#endif /* HAVE_FLOAT */
+
+                /* Calculate integer power */
+                retval = int_pow(TOS1, TOS, &pobj3);
+                PM_BREAK_IF_ERROR(retval);
+
+                /* Set return value */
+                PM_SP--;
+                TOS = pobj3;
+                continue;
+
+            case GET_ITER:
+#ifdef HAVE_GENERATORS
+                /* Raise TypeError if TOS is an instance, but not iterable */
+                if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI)
+                {
+                    retval = class_getAttr(TOS, PM_NEXT_STR, &pobj1);
+                    if (retval != PM_RET_OK)
+                    {
+                        PM_RAISE(retval, PM_RET_EX_TYPE);
+                        break;
+                    }
+                }
+                else
+#endif /* HAVE_GENERATORS */
+                {
+                    /* Convert sequence to sequence-iterator */
+                    retval = seqiter_new(TOS, &pobj1);
+                    PM_BREAK_IF_ERROR(retval);
+
+                    /* Put sequence-iterator on top of stack */
+                    TOS = pobj1;
+                }
+                continue;
+
+            case BINARY_MULTIPLY:
+            case INPLACE_MULTIPLY:
+                /* If both objs are ints, perform the op */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                    && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT))
+                {
+                    retval = int_new(((pPmInt_t)TOS1)->val *
+                                     ((pPmInt_t)TOS)->val, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+
+#ifdef HAVE_FLOAT
+                else if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT)
+                         || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT))
+                {
+                    retval = float_op(TOS1, TOS, &pobj3, '*');
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+#endif /* HAVE_FLOAT */
+
+#ifdef HAVE_REPLICATION
+                /* If it's a list replication operation */
+                else if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                         && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_LST))
+                {
+                    t16 = (int16_t)((pPmInt_t)TOS)->val;
+                    if (t16 < 0)
+                    {
+                        t16 = 0;
+                    }
+
+                    retval = list_replicate(TOS1, t16, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+
+                /* If it's a tuple replication operation */
+                else if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                         && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_TUP))
+                {
+                    t16 = (int16_t)((pPmInt_t)TOS)->val;
+                    if (t16 < 0)
+                    {
+                        t16 = 0;
+                    }
+
+                    retval = tuple_replicate(TOS1, t16, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+
+                /* If it's a string replication operation */
+                else if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                         && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_STR))
+                {
+                    t16 = (int16_t)((pPmInt_t)TOS)->val;
+                    if (t16 < 0)
+                    {
+                        t16 = 0;
+                    }
+
+                    pobj2 = TOS1;
+                    pobj2 = (pPmObj_t)&((pPmString_t)pobj2)->val;
+                    retval = string_replicate(
+                        (uint8_t const **)(uint8_t *)&pobj2, t16, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+#endif /* HAVE_REPLICATION */
+
+                /* Otherwise raise a TypeError */
+                PM_RAISE(retval, PM_RET_EX_TYPE);
+                break;
+
+            case BINARY_DIVIDE:
+            case INPLACE_DIVIDE:
+            case BINARY_FLOOR_DIVIDE:
+            case INPLACE_FLOOR_DIVIDE:
+
+#ifdef HAVE_FLOAT
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT)
+                    || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT))
+                {
+                    retval = float_op(TOS1, TOS, &pobj3, '/');
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+#endif /* HAVE_FLOAT */
+
+                /* Otherwise perform operation */
+                retval = int_divmod(TOS1, TOS, '/', &pobj3);
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP--;
+                TOS = pobj3;
+                continue;
+
+            case BINARY_MODULO:
+            case INPLACE_MODULO:
+
+#ifdef HAVE_STRING_FORMAT
+                /* If it's a string, perform string format */
+                if (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_STR)
+                {
+                    retval = string_format((pPmString_t)TOS1, TOS, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+#endif /* HAVE_STRING_FORMAT */
+
+#ifdef HAVE_FLOAT
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT)
+                    || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT))
+                {
+                    retval = float_op(TOS1, TOS, &pobj3, '%');
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+#endif /* HAVE_FLOAT */
+
+                /* Otherwise perform operation */
+                retval = int_divmod(TOS1, TOS, '%', &pobj3);
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP--;
+                TOS = pobj3;
+                continue;
+
+            case STORE_MAP:
+                /* #213: Add support for Python 2.6 bytecodes */
+                C_ASSERT(OBJ_GET_TYPE(TOS2) == OBJ_TYPE_DIC);
+                retval = dict_setItem(TOS2, TOS, TOS1);
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP -= 2;
+                continue;
+
+            case BINARY_ADD:
+            case INPLACE_ADD:
+
+#ifdef HAVE_FLOAT
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT)
+                    || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT))
+                {
+                    retval = float_op(TOS1, TOS, &pobj3, '+');
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+#endif /* HAVE_FLOAT */
+
+                /* If both objs are ints, perform the op */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                    && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT))
+                {
+                    retval = int_new(((pPmInt_t)TOS1)->val +
+                                     ((pPmInt_t)TOS)->val, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+
+                /* #242: If both objs are strings, perform concatenation */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_STR)
+                    && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_STR))
+                {
+                    retval = string_concat((pPmString_t)TOS1,
+                                           (pPmString_t)TOS,
+                                           &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+
+                /* Otherwise raise a TypeError */
+                PM_RAISE(retval, PM_RET_EX_TYPE);
+                break;
+
+            case BINARY_SUBTRACT:
+            case INPLACE_SUBTRACT:
+
+#ifdef HAVE_FLOAT
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT)
+                    || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT))
+                {
+                    retval = float_op(TOS1, TOS, &pobj3, '-');
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+#endif /* HAVE_FLOAT */
+
+                /* If both objs are ints, perform the op */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                    && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT))
+                {
+                    retval = int_new(((pPmInt_t)TOS1)->val -
+                                     ((pPmInt_t)TOS)->val, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+
+                /* Otherwise raise a TypeError */
+                PM_RAISE(retval, PM_RET_EX_TYPE);
+                break;
+
+            case BINARY_SUBSCR:
+                /* Implements TOS = TOS1[TOS]. */
+
+                if (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_DIC)
+                {
+                    retval = dict_getItem(TOS1, TOS, &pobj3);
+                }
+                else
+                {
+                    /* Raise a TypeError if index is not an Integer or Bool */
+                    if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT)
+                        && (OBJ_GET_TYPE(TOS) != OBJ_TYPE_BOOL))
+                    {
+                        PM_RAISE(retval, PM_RET_EX_TYPE);
+                        break;
+                    }
+
+                    pobj1 = TOS1;
+#ifdef HAVE_BYTEARRAY
+                    /* If object is an instance, get the thing it contains */
+                    if (OBJ_GET_TYPE(pobj1) == OBJ_TYPE_CLI)
+                    {
+                        retval = dict_getItem((pPmObj_t)((pPmInstance_t)pobj1)->cli_attrs,
+                                              PM_NONE,
+                                              &pobj2);
+                        PM_RETURN_IF_ERROR(retval);
+                        pobj1 = pobj2;
+                    }
+#endif /* HAVE_BYTEARRAY */
+
+                    /* Ensure the index doesn't overflow */
+                    C_ASSERT(((pPmInt_t)TOS)->val <= 0x0000FFFF);
+                    t16 = (int16_t)((pPmInt_t)TOS)->val;
+
+                    retval = seq_getSubscript(pobj1, t16, &pobj3);
+                }
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP--;
+                TOS = pobj3;
+                continue;
+
+#ifdef HAVE_FLOAT
+            /* #213: Add support for Python 2.6 bytecodes */
+            case BINARY_TRUE_DIVIDE:
+            case INPLACE_TRUE_DIVIDE:
+
+                /* Perform division; float_op() checks for types and zero-div */
+                retval = float_op(TOS1, TOS, &pobj3, '/');
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP--;
+                TOS = pobj3;
+                continue;
+#endif /* HAVE_FLOAT */
+
+            case SLICE_0:
+                /* Implements TOS = TOS[:], push a copy of the sequence */
+
+                /* Create a copy if it is a list */
+                if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_LST)
+                {
+                    retval = list_copy(TOS, &pobj2);
+                    PM_BREAK_IF_ERROR(retval);
+
+                    TOS = pobj2;
+                }
+
+                /* If TOS is an immutable sequence leave it (no op) */
+
+                /* Raise a TypeError for types that can not be sliced */
+                else if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_STR)
+                         && (OBJ_GET_TYPE(TOS) != OBJ_TYPE_TUP))
+                {
+                    PM_RAISE(retval, PM_RET_EX_TYPE);
+                    break;
+                }
+                continue;
+
+            case STORE_SUBSCR:
+                /* Implements TOS1[TOS] = TOS2 */
+
+                /* If it's a list */
+                if (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_LST)
+                {
+                    /* Ensure subscr is an int or bool */
+                    if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT)
+                        && (OBJ_GET_TYPE(TOS) != OBJ_TYPE_BOOL))
+                    {
+                        PM_RAISE(retval, PM_RET_EX_TYPE);
+                        break;
+                    }
+
+                    /* Set the list item */
+                    retval = list_setItem(TOS1,
+                                          (int16_t)(((pPmInt_t)TOS)->val),
+                                          TOS2);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP -= 3;
+                    continue;
+                }
+
+                /* If it's a dict */
+                if (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_DIC)
+                {
+                    /* Set the dict item */
+                    retval = dict_setItem(TOS1, TOS, TOS2);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP -= 3;
+                    continue;
+                }
+
+#ifdef HAVE_BYTEARRAY
+                /* If object is an instance, get the thing it contains */
+                if (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_CLI)
+                {
+                    retval = dict_getItem((pPmObj_t)((pPmInstance_t)TOS1)->cli_attrs,
+                                          PM_NONE,
+                                          &pobj2);
+
+                    /* Raise TypeError if instance isn't a ByteArray */
+                    if ((retval == PM_RET_EX_KEY)
+                        || (OBJ_GET_TYPE(pobj2) != OBJ_TYPE_BYA))
+                    {
+                        PM_RAISE(retval, PM_RET_EX_TYPE);
+                        break;
+                    }
+                    PM_BREAK_IF_ERROR(retval);
+
+                    /* Ensure subscr is an int or bool */
+                    if ((OBJ_GET_TYPE(TOS) != OBJ_TYPE_INT)
+                        && (OBJ_GET_TYPE(TOS) != OBJ_TYPE_BOOL))
+                    {
+                        PM_RAISE(retval, PM_RET_EX_TYPE);
+                        break;
+                    }
+
+                    retval = bytearray_setItem(pobj2,
+                                               (int16_t)(((pPmInt_t)TOS)->val),
+                                               TOS2);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP -= 3;
+                    continue;
+                }
+#endif /* HAVE_BYTEARRAY */
+
+                /* TypeError for all else */
+                PM_RAISE(retval, PM_RET_EX_TYPE);
+                break;
+
+#ifdef HAVE_DEL
+            case DELETE_SUBSCR:
+
+                if ((OBJ_GET_TYPE(TOS1) == OBJ_TYPE_LST)
+                    && (OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT))
+                {
+                    retval = list_delItem(TOS1,
+                                          (int16_t)((pPmInt_t)TOS)->val);
+                }
+
+                else if ((OBJ_GET_TYPE(TOS1) == OBJ_TYPE_DIC)
+                         && (OBJ_GET_TYPE(TOS) <= OBJ_TYPE_HASHABLE_MAX))
+                {
+                    retval = dict_delItem(TOS1, TOS);
+                }
+
+                /* Raise TypeError if obj is not a list or dict */
+                else
+                {
+                    PM_RAISE(retval, PM_RET_EX_TYPE);
+                }
+
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP -= 2;
+                continue;
+#endif /* HAVE_DEL */
+
+            case BINARY_LSHIFT:
+            case INPLACE_LSHIFT:
+                /* If both objs are ints, perform the op */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                    && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT))
+                {
+                    retval = int_new(((pPmInt_t)TOS1)->val <<
+                                     ((pPmInt_t)TOS)->val, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+
+                /* Otherwise raise a TypeError */
+                PM_RAISE(retval, PM_RET_EX_TYPE);
+                break;
+
+            case BINARY_RSHIFT:
+            case INPLACE_RSHIFT:
+                /* If both objs are ints, perform the op */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                    && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT))
+                {
+                    retval = int_new(((pPmInt_t)TOS1)->val >>
+                                     ((pPmInt_t)TOS)->val, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+
+                /* Otherwise raise a TypeError */
+                PM_RAISE(retval, PM_RET_EX_TYPE);
+                break;
+
+            case BINARY_AND:
+            case INPLACE_AND:
+                /* If both objs are ints, perform the op */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                    && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT))
+                {
+                    retval = int_new(((pPmInt_t)TOS1)->val &
+                                     ((pPmInt_t)TOS)->val, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+
+                /* Otherwise raise a TypeError */
+                PM_RAISE(retval, PM_RET_EX_TYPE);
+                break;
+
+            case BINARY_XOR:
+            case INPLACE_XOR:
+                /* If both objs are ints, perform the op */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                    && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT))
+                {
+                    retval = int_new(((pPmInt_t)TOS1)->val ^
+                                     ((pPmInt_t)TOS)->val, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+
+                /* Otherwise raise a TypeError */
+                PM_RAISE(retval, PM_RET_EX_TYPE);
+                break;
+
+            case BINARY_OR:
+            case INPLACE_OR:
+                /* If both objs are ints, perform the op */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                    && (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT))
+                {
+                    retval = int_new(((pPmInt_t)TOS1)->val |
+                                     ((pPmInt_t)TOS)->val, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+
+                /* Otherwise raise a TypeError */
+                PM_RAISE(retval, PM_RET_EX_TYPE);
+                break;
+
+#ifdef HAVE_PRINT
+            case PRINT_EXPR:
+                /* Print interactive expression */
+                /* Fallthrough */
+
+            case PRINT_ITEM:
+                if (gVmGlobal.needSoftSpace && (bc == PRINT_ITEM))
+                {
+                    retval = plat_putByte(' ');
+                    PM_BREAK_IF_ERROR(retval);
+                }
+                gVmGlobal.needSoftSpace = C_TRUE;
+
+                /* Print out topmost stack element */
+                retval = obj_print(TOS, (uint8_t)(bc == PRINT_EXPR), C_FALSE);
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP--;
+                if (bc != PRINT_EXPR)
+                {
+                    continue;
+                }
+                /* If PRINT_EXPR, Fallthrough to print a newline */
+
+            case PRINT_NEWLINE:
+                gVmGlobal.needSoftSpace = C_FALSE;
+                if (gVmGlobal.somethingPrinted)
+                {
+                    retval = plat_putByte('\n');
+                    gVmGlobal.somethingPrinted = C_FALSE;
+                }
+                PM_BREAK_IF_ERROR(retval);
+                continue;
+#endif /* HAVE_PRINT */
+
+            case BREAK_LOOP:
+            {
+                pPmBlock_t pb1 = PM_FP->fo_blockstack;
+
+                /* Ensure there's a block */
+                C_ASSERT(pb1 != C_NULL);
+
+                /* Delete blocks until first loop block */
+                while ((pb1->b_type != B_LOOP) && (pb1->next != C_NULL))
+                {
+                    pobj2 = (pPmObj_t)pb1;
+                    pb1 = pb1->next;
+                    retval = heap_freeChunk(pobj2);
+                    PM_BREAK_IF_ERROR(retval);
+                }
+
+                /* Test again outside while loop */
+                PM_BREAK_IF_ERROR(retval);
+
+                /* Restore PM_SP */
+                PM_SP = pb1->b_sp;
+
+                /* Goto handler */
+                PM_IP = pb1->b_handler;
+
+                /* Pop and delete this block */
+                PM_FP->fo_blockstack = pb1->next;
+                retval = heap_freeChunk((pPmObj_t)pb1);
+                PM_BREAK_IF_ERROR(retval);
+            }
+                continue;
+
+            case LOAD_LOCALS:
+                /* Pushes local attrs dict of current frame */
+                /* WARNING: does not copy fo_locals to attrs */
+                PM_PUSH((pPmObj_t)PM_FP->fo_attrs);
+                continue;
+
+            case RETURN_VALUE:
+                /* Get expiring frame's TOS */
+                pobj2 = PM_POP();
+
+#if 0 /*__DEBUG__*/
+                /* #251: This safety check is disabled because it breaks ipm */
+                /* #109: Check that stack should now be empty */
+                /* If this is regular frame (not native and not a generator) */
+                if ((PM_FP != (pPmFrame_t)(&gVmGlobal.nativeframe)) &&
+                    !(PM_FP->fo_func->f_co->co_flags & CO_GENERATOR))
+                {
+                    /* An empty stack points one past end of locals */
+                    t8 = PM_FP->fo_func->f_co->co_nlocals;
+                    C_ASSERT(PM_SP == &(PM_FP->fo_locals[t8]));
+                }
+#endif /* __DEBUG__ */
+
+                /* Keep ref of expiring frame */
+                pobj1 = (pPmObj_t)PM_FP;
+                C_ASSERT(OBJ_GET_TYPE(pobj1) == OBJ_TYPE_FRM);
+
+                /* If no previous frame, quit thread */
+                if (PM_FP->fo_back == C_NULL)
+                {
+                    gVmGlobal.pthread->interpctrl = INTERP_CTRL_EXIT;
+                    retval = PM_RET_OK;
+                    break;
+                }
+
+                /* Otherwise return to previous frame */
+                PM_FP = PM_FP->fo_back;
+
+#ifdef HAVE_GENERATORS
+                /* If returning function was a generator */
+                if (((pPmFrame_t)pobj1)->fo_func->f_co->co_flags & CO_GENERATOR)
+                {
+                    /* Raise a StopIteration exception */
+                    PM_RAISE(retval, PM_RET_EX_STOP);
+                    break;
+                }
+#endif /* HAVE_GENERATORS */
+
+#ifdef HAVE_CLASSES
+                /*
+                 * If returning function was class initializer
+                 * do not push a return object
+                 */
+                if (((pPmFrame_t)pobj1)->fo_isInit)
+                {
+                    /* Raise TypeError if __init__ did not return None */
+                    if (pobj2 != PM_NONE)
+                    {
+                        PM_RAISE(retval, PM_RET_EX_TYPE);
+                        break;
+                    }
+                }
+                else
+#endif /* HAVE_CLASSES */
+
+                /*
+                 * Push frame's return val, except if the expiring frame
+                 * was due to an import statement
+                 */
+                if (!(((pPmFrame_t)pobj1)->fo_isImport))
+                {
+                    PM_PUSH(pobj2);
+                }
+
+                /* Deallocate expired frame */
+                PM_BREAK_IF_ERROR(heap_freeChunk(pobj1));
+                continue;
+
+#ifdef HAVE_IMPORTS
+            case IMPORT_STAR:
+                /* #102: Implement the remaining IMPORT_ bytecodes */
+                /* Expect a module on the top of the stack */
+                C_ASSERT(OBJ_GET_TYPE(TOS) == OBJ_TYPE_MOD);
+
+                /* Update PM_FP's attrs with those of the module on the stack */
+                retval = dict_update((pPmObj_t)PM_FP->fo_attrs,
+                                     (pPmObj_t)((pPmFunc_t)TOS)->f_attrs,
+                                     C_TRUE);
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP--;
+                continue;
+#endif /* HAVE_IMPORTS */
+
+#ifdef HAVE_GENERATORS
+            case YIELD_VALUE:
+                /* #207: Add support for the yield keyword */
+                /* Get expiring frame's TOS */
+                pobj1 = PM_POP();
+
+                /* Raise TypeError if __init__ did not return None */
+                /* (Yield means this is a generator) */
+                if ((PM_FP)->fo_isInit)
+                {
+                    PM_RAISE(retval, PM_RET_EX_TYPE);
+                    break;
+                }
+
+                /* Return to previous frame */
+                PM_FP = PM_FP->fo_back;
+
+                /* Push yield value onto caller's TOS */
+                PM_PUSH(pobj1);
+                continue;
+#endif /* HAVE_GENERATORS */
+
+            case POP_BLOCK:
+                /* Get ptr to top block */
+                pobj1 = (pPmObj_t)PM_FP->fo_blockstack;
+
+                /* If there's no block, raise SystemError */
+                C_ASSERT(pobj1 != C_NULL);
+
+                /* Pop block */
+                PM_FP->fo_blockstack = PM_FP->fo_blockstack->next;
+
+                /* Set stack to previous level, jump to code outside block */
+                PM_SP = ((pPmBlock_t)pobj1)->b_sp;
+                PM_IP = ((pPmBlock_t)pobj1)->b_handler;
+
+                PM_BREAK_IF_ERROR(heap_freeChunk(pobj1));
+                continue;
+
+#ifdef HAVE_CLASSES
+            case BUILD_CLASS:
+                /* Create and push new class */
+                retval = class_new(TOS, TOS1, TOS2, &pobj2);
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP -= 2;
+                TOS = pobj2;
+                continue;
+#endif /* HAVE_CLASSES */
+
+
+            /***************************************************
+             * All bytecodes after 90 (0x5A) have a 2-byte arg
+             * that needs to be swallowed using GET_ARG().
+             **************************************************/
+
+            case STORE_NAME:
+                /* Get name index */
+                t16 = GET_ARG();
+
+                /* Get key */
+                pobj2 = PM_FP->fo_func->f_co->co_names->val[t16];
+
+                /* Set key=val in current frame's attrs dict */
+                retval = dict_setItem((pPmObj_t)PM_FP->fo_attrs, pobj2, TOS);
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP--;
+                continue;
+
+#ifdef HAVE_DEL
+            case DELETE_NAME:
+                /* Get name index */
+                t16 = GET_ARG();
+
+                /* Get key */
+                pobj2 = PM_FP->fo_func->f_co->co_names->val[t16];
+
+                /* Remove key,val pair from current frame's attrs dict */
+                retval = dict_delItem((pPmObj_t)PM_FP->fo_attrs, pobj2);
+                PM_BREAK_IF_ERROR(retval);
+                continue;
+#endif /* HAVE_DEL */
+
+            case UNPACK_SEQUENCE:
+                /* Get ptr to sequence */
+                pobj1 = PM_POP();
+
+#ifdef HAVE_BYTEARRAY
+                /* If object is an instance, get the thing it contains */
+                if (OBJ_GET_TYPE(pobj1) == OBJ_TYPE_CLI)
+                {
+                    retval = dict_getItem((pPmObj_t)((pPmInstance_t)pobj1)->cli_attrs,
+                                          PM_NONE,
+                                          &pobj2);
+                    PM_RETURN_IF_ERROR(retval);
+                    pobj1 = pobj2;
+                }
+#endif /* HAVE_BYTEARRAY */
+
+                /*
+                 * Get the length of the sequence; this will
+                 * raise TypeError if obj is not a sequence.
+                 *
+                 * #59: Unpacking to a Dict shall not be supported
+                 */
+                retval = seq_getLength(pobj1, (uint16_t *)&t16);
+                if (retval != PM_RET_OK)
+                {
+                    GET_ARG();
+                    break;
+                }
+
+                /* Raise ValueError if seq length does not match num args */
+                if (t16 != GET_ARG())
+                {
+                    PM_RAISE(retval, PM_RET_EX_VAL);
+                    break;
+                }
+
+                /* Push sequence's objs onto stack */
+                for (; --t16 >= 0;)
+                {
+                    retval = seq_getSubscript(pobj1, t16, &pobj2);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_PUSH(pobj2);
+                }
+
+                /* Test again outside the for loop */
+                PM_BREAK_IF_ERROR(retval);
+                continue;
+
+            case FOR_ITER:
+                t16 = GET_ARG();
+
+#ifdef HAVE_GENERATORS
+                /* If TOS is an instance, call next method */
+                if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI)
+                {
+                    /* Get the next() func */
+                    retval = class_getAttr(TOS, PM_NEXT_STR, &pobj1);
+                    PM_BREAK_IF_ERROR(retval);
+
+                    /* Push the func and instance as an arg */
+                    pobj2 = TOS;
+                    PM_PUSH(pobj1);
+                    PM_PUSH(pobj2);
+                    t16 = 1;
+
+                    /* Ensure pobj1 is the func */
+                    goto CALL_FUNC_FOR_ITER;
+                }
+                else
+#endif /* HAVE_GENERATORS */
+                {
+                    /* Get the next item in the sequence iterator */
+                    retval = seqiter_getNext(TOS, &pobj2);
+                }
+
+                /* Catch StopIteration early: pop iterator and break loop */
+                if (retval == PM_RET_EX_STOP)
+                {
+                    PM_SP--;
+                    retval = PM_RET_OK;
+                    PM_IP += t16;
+                    continue;
+                }
+                PM_BREAK_IF_ERROR(retval);
+
+                /* Push the next item onto the stack */
+                PM_PUSH(pobj2);
+                continue;
+
+            case STORE_ATTR:
+                /* TOS.name = TOS1 */
+                /* Get names index */
+                t16 = GET_ARG();
+
+                /* Get attrs dict from obj */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FXN)
+                    || (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MOD))
+                {
+                    pobj2 = (pPmObj_t)((pPmFunc_t)TOS)->f_attrs;
+                }
+
+#ifdef HAVE_CLASSES
+                else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLO)
+                {
+                    pobj2 = (pPmObj_t)((pPmClass_t)TOS)->cl_attrs;
+                }
+                else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI)
+                {
+                    pobj2 = (pPmObj_t)((pPmInstance_t)TOS)->cli_attrs;
+                }
+                else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MTH)
+                {
+                    pobj2 = (pPmObj_t)((pPmMethod_t)TOS)->m_attrs;
+                }
+#endif /* HAVE_CLASSES */
+
+                /* Other types result in an AttributeError */
+                else
+                {
+                    PM_RAISE(retval, PM_RET_EX_ATTR);
+                    break;
+                }
+
+                /* If attrs is not a dict, raise SystemError */
+                if (OBJ_GET_TYPE(pobj2) != OBJ_TYPE_DIC)
+                {
+                    PM_RAISE(retval, PM_RET_EX_SYS);
+                    break;
+                }
+
+                /* Get name/key obj */
+                pobj3 = PM_FP->fo_func->f_co->co_names->val[t16];
+
+                /* Set key=val in obj's dict */
+                retval = dict_setItem(pobj2, pobj3, TOS1);
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP -= 2;
+                continue;
+
+#ifdef HAVE_DEL
+            case DELETE_ATTR:
+                /* del TOS.name */
+                /* Get names index */
+                t16 = GET_ARG();
+
+                /* Get attrs dict from obj */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FXN)
+                    || (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MOD))
+                {
+                    pobj2 = (pPmObj_t)((pPmFunc_t)TOS)->f_attrs;
+                }
+
+#ifdef HAVE_CLASSES
+                else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLO)
+                {
+                    pobj2 = (pPmObj_t)((pPmClass_t)TOS)->cl_attrs;
+                }
+                else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI)
+                {
+                    pobj2 = (pPmObj_t)((pPmInstance_t)TOS)->cli_attrs;
+                }
+                else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MTH)
+                {
+                    pobj2 = (pPmObj_t)((pPmMethod_t)TOS)->m_attrs;
+                }
+#endif /* HAVE_CLASSES */
+
+                /* Other types result in an AttributeError */
+                else
+                {
+                    PM_RAISE(retval, PM_RET_EX_ATTR);
+                    break;
+                }
+
+                /* If attrs is not a dict, raise SystemError */
+                if (OBJ_GET_TYPE(pobj2) != OBJ_TYPE_DIC)
+                {
+                    PM_RAISE(retval, PM_RET_EX_SYS);
+                    break;
+                }
+
+                /* Get name/key obj */
+                pobj3 = PM_FP->fo_func->f_co->co_names->val[t16];
+
+                /* Remove key,val from obj's dict */
+                retval = dict_delItem(pobj2, pobj3);
+
+                /* Raise an AttributeError if key is not found */
+                if (retval == PM_RET_EX_KEY)
+                {
+                    PM_RAISE(retval, PM_RET_EX_ATTR);
+                }
+
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP--;
+                continue;
+#endif /* HAVE_DEL */
+
+            case STORE_GLOBAL:
+                /* Get name index */
+                t16 = GET_ARG();
+
+                /* Get key */
+                pobj2 = PM_FP->fo_func->f_co->co_names->val[t16];
+
+                /* Set key=val in global dict */
+                retval = dict_setItem((pPmObj_t)PM_FP->fo_globals, pobj2, TOS);
+                PM_BREAK_IF_ERROR(retval);
+                PM_SP--;
+                continue;
+
+#ifdef HAVE_DEL
+            case DELETE_GLOBAL:
+                /* Get name index */
+                t16 = GET_ARG();
+
+                /* Get key */
+                pobj2 = PM_FP->fo_func->f_co->co_names->val[t16];
+
+                /* Remove key,val from globals */
+                retval = dict_delItem((pPmObj_t)PM_FP->fo_globals, pobj2);
+                PM_BREAK_IF_ERROR(retval);
+                continue;
+#endif /* HAVE_DEL */
+
+            case DUP_TOPX:
+                t16 = GET_ARG();
+                C_ASSERT(t16 <= 3);
+
+                pobj1 = TOS;
+                pobj2 = TOS1;
+                pobj3 = TOS2;
+                if (t16 >= 3)
+                    PM_PUSH(pobj3);
+                if (t16 >= 2)
+                    PM_PUSH(pobj2);
+                if (t16 >= 1)
+                    PM_PUSH(pobj1);
+                continue;
+
+            case LOAD_CONST:
+                /* Get const's index in CO */
+                t16 = GET_ARG();
+
+                /* Push const on stack */
+                PM_PUSH(PM_FP->fo_func->f_co->co_consts->val[t16]);
+                continue;
+
+            case LOAD_NAME:
+                /* Get name index */
+                t16 = GET_ARG();
+
+                /* Get name from names tuple */
+                pobj1 = PM_FP->fo_func->f_co->co_names->val[t16];
+
+                /* Get value from frame's attrs dict */
+                retval = dict_getItem((pPmObj_t)PM_FP->fo_attrs, pobj1, &pobj2);
+                if (retval == PM_RET_EX_KEY)
+                {
+                    /* Get val from globals */
+                    retval = dict_getItem((pPmObj_t)PM_FP->fo_globals,
+                                          pobj1, &pobj2);
+
+                    /* Check for name in the builtins module if it is loaded */
+                    if ((retval == PM_RET_EX_KEY) && (PM_PBUILTINS != C_NULL))
+                    {
+                        /* Get val from builtins */
+                        retval = dict_getItem(PM_PBUILTINS, pobj1, &pobj2);
+                        if (retval == PM_RET_EX_KEY)
+                        {
+                            /* Name not defined, raise NameError */
+                            PM_RAISE(retval, PM_RET_EX_NAME);
+                            break;
+                        }
+                    }
+                }
+                PM_BREAK_IF_ERROR(retval);
+                PM_PUSH(pobj2);
+                continue;
+
+            case BUILD_TUPLE:
+                /* Get num items */
+                t16 = GET_ARG();
+                retval = tuple_new(t16, &pobj1);
+                PM_BREAK_IF_ERROR(retval);
+
+                /* Fill tuple with ptrs to objs */
+                for (; --t16 >= 0;)
+                {
+                    ((pPmTuple_t)pobj1)->val[t16] = PM_POP();
+                }
+                PM_PUSH(pobj1);
+                continue;
+
+            case BUILD_LIST:
+                t16 = GET_ARG();
+                retval = list_new(&pobj1);
+                PM_BREAK_IF_ERROR(retval);
+                for (; --t16 >= 0;)
+                {
+                    /* Insert obj into list */
+                    heap_gcPushTempRoot(pobj1, &objid);
+                    retval = list_insert(pobj1, 0, TOS);
+                    heap_gcPopTempRoot(objid);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                }
+                /* Test again outside for loop */
+                PM_BREAK_IF_ERROR(retval);
+
+                /* push list onto stack */
+                PM_PUSH(pobj1);
+                continue;
+
+            case BUILD_MAP:
+                /* Argument is ignored */
+                t16 = GET_ARG();
+                retval = dict_new(&pobj1);
+                PM_BREAK_IF_ERROR(retval);
+                PM_PUSH(pobj1);
+                continue;
+
+            case LOAD_ATTR:
+                /* Implements TOS.attr */
+                t16 = GET_ARG();
+
+#ifdef HAVE_AUTOBOX
+                /* Autobox the object, if necessary */
+                retval = class_autobox(&TOS);
+                PM_BREAK_IF_ERROR(retval);
+#endif
+
+                /* Get attrs dict from obj */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FXN) ||
+                    (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MOD))
+                {
+                    pobj1 = (pPmObj_t)((pPmFunc_t)TOS)->f_attrs;
+                }
+
+#ifdef HAVE_CLASSES
+                else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLO)
+                {
+                    pobj1 = (pPmObj_t)((pPmClass_t)TOS)->cl_attrs;
+                }
+                else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI)
+                {
+                    pobj1 = (pPmObj_t)((pPmInstance_t)TOS)->cli_attrs;
+                }
+                else if (OBJ_GET_TYPE(TOS) == OBJ_TYPE_MTH)
+                {
+                    pobj1 = (pPmObj_t)((pPmMethod_t)TOS)->m_attrs;
+                }
+#endif /* HAVE_CLASSES */
+
+                /* Other types result in an AttributeError */
+                else
+                {
+                    PM_RAISE(retval, PM_RET_EX_ATTR);
+                    break;
+                }
+
+                /* If attrs is not a dict, raise SystemError */
+                if (OBJ_GET_TYPE(pobj1) != OBJ_TYPE_DIC)
+                {
+                    PM_RAISE(retval, PM_RET_EX_SYS);
+                    break;
+                }
+
+                /* Get name */
+                pobj2 = PM_FP->fo_func->f_co->co_names->val[t16];
+
+                /* Get attr with given name */
+                retval = dict_getItem(pobj1, pobj2, &pobj3);
+
+#ifdef HAVE_CLASSES
+                /*
+                 * If attr is not found and object is a class or instance,
+                 * try to get the attribute from the class attrs or parent(s)
+                 */
+                if ((retval == PM_RET_EX_KEY) &&
+                    ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLO)
+                        || (OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI)))
+                {
+                    retval = class_getAttr(TOS, pobj2, &pobj3);
+                }
+#endif /* HAVE_CLASSES */
+
+                /* Raise an AttributeError if key is not found */
+                if (retval == PM_RET_EX_KEY)
+                {
+                    PM_RAISE(retval, PM_RET_EX_ATTR);
+                }
+                PM_BREAK_IF_ERROR(retval);
+
+#ifdef HAVE_CLASSES
+                /* If obj is an instance and attr is a func, create method */
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_CLI) &&
+                    (OBJ_GET_TYPE(pobj3) == OBJ_TYPE_FXN))
+                {
+                    pobj2 = pobj3;
+                    retval = class_method(TOS, pobj2, &pobj3);
+                    PM_BREAK_IF_ERROR(retval);
+                }
+#endif /* HAVE_CLASSES */
+
+                /* Put attr on the stack */
+                TOS = pobj3;
+                continue;
+
+            case COMPARE_OP:
+                retval = PM_RET_OK;
+                t16 = GET_ARG();
+
+#ifdef HAVE_FLOAT
+                if ((OBJ_GET_TYPE(TOS) == OBJ_TYPE_FLT)
+                    || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_FLT))
+                {
+                    retval = float_compare(TOS1, TOS, &pobj3, (PmCompare_t)t16);
+                    PM_SP--;
+                    TOS = pobj3;
+                    continue;
+                }
+#endif /* HAVE_FLOAT */
+
+                /* Handle all integer-to-integer (or bool) comparisons */
+                if (((OBJ_GET_TYPE(TOS) == OBJ_TYPE_INT)
+                     || (OBJ_GET_TYPE(TOS) == OBJ_TYPE_BOOL))
+                    && ((OBJ_GET_TYPE(TOS1) == OBJ_TYPE_INT)
+                        || (OBJ_GET_TYPE(TOS1) == OBJ_TYPE_BOOL)))
+                {
+                    int32_t a = ((pPmInt_t)TOS1)->val;
+                    int32_t b = ((pPmInt_t)TOS)->val;
+
+                    switch (t16)
+                    {
+                        /* *INDENT-OFF* */
+                        case COMP_LT: t8 = (int8_t)(a <  b); break;
+                        case COMP_LE: t8 = (int8_t)(a <= b); break;
+                        case COMP_EQ: t8 = (int8_t)(a == b); break;
+                        case COMP_NE: t8 = (int8_t)(a != b); break;
+                        case COMP_GT: t8 = (int8_t)(a >  b); break;
+                        case COMP_GE: t8 = (int8_t)(a >= b); break;
+                        case COMP_IS: t8 = (int8_t)(TOS == TOS1); break;
+                        case COMP_IS_NOT: t8 = (int8_t)(TOS != TOS1);break;
+                        case COMP_IN:
+                        case COMP_NOT_IN:
+                            PM_RAISE(retval, PM_RET_EX_TYPE);
+                            break;
+
+                        default:
+                            /* Other compares are not yet supported */
+                            PM_RAISE(retval, PM_RET_EX_SYS);
+                            break;
+                        /* *INDENT-ON* */
+                    }
+                    PM_BREAK_IF_ERROR(retval);
+                    pobj3 = (t8) ? PM_TRUE : PM_FALSE;
+                }
+
+                /* Handle non-integer comparisons */
+                else
+                {
+                    retval = PM_RET_OK;
+                    switch (t16)
+                    {
+                        case COMP_EQ:
+                        case COMP_NE:
+                            /* Handle equality for non-int types */
+                            pobj3 = PM_FALSE;
+                            t8 = obj_compare(TOS, TOS1);
+                            if (((t8 == C_SAME) && (t16 == COMP_EQ))
+                                || ((t8 == C_DIFFER) && (t16 == COMP_NE)))
+                            {
+                                pobj3 = PM_TRUE;
+                            }
+                            break;
+
+                        case COMP_IN:
+                        case COMP_NOT_IN:
+                            /* Handle membership comparisons */
+                            pobj3 = PM_FALSE;
+                            retval = obj_isIn(TOS, TOS1);
+                            if (retval == PM_RET_OK)
+                            {
+                                if (t16 == COMP_IN)
+                                {
+                                    pobj3 = PM_TRUE;
+                                }
+                            }
+                            else if (retval == PM_RET_NO)
+                            {
+                                retval = PM_RET_OK;
+                                if (t16 == COMP_NOT_IN)
+                                {
+                                    pobj3 = PM_TRUE;
+                                }
+                            }
+                            break;
+
+                        case COMP_IS:
+                            pobj3 = (TOS == TOS1) ? PM_TRUE : PM_FALSE;
+                            break;
+
+                        case COMP_IS_NOT:
+                            pobj3 = (TOS != TOS1) ? PM_TRUE : PM_FALSE;
+                            break;
+
+                        default:
+                            /* Other comparisons are not implemented */
+                            PM_RAISE(retval, PM_RET_EX_SYS);
+                            break;
+                    }
+                    PM_BREAK_IF_ERROR(retval);
+                }
+                PM_SP--;
+                TOS = pobj3;
+                continue;
+
+            case IMPORT_NAME:
+                /* Get name index */
+                t16 = GET_ARG();
+
+                /* Get name String obj */
+                pobj1 = PM_FP->fo_func->f_co->co_names->val[t16];
+
+                /* Pop unused None object */
+                PM_SP--;
+
+                /* Ensure "level" is -1; no support for relative import yet */
+                C_ASSERT(obj_compare(TOS, PM_NEGONE) == C_SAME);
+
+                /* #178: Fix import so modules are reused */
+                /* Return the module if found in the modules dict (cache) */
+                retval = dict_getItem(PM_PBUILTINS, PM_MD_STR, &pobj3);
+                PM_BREAK_IF_ERROR(retval);
+                retval = dict_getItem(pobj3, pobj1, &pobj2);
+                if (retval == PM_RET_OK)
+                {
+                    TOS = pobj2;
+                    continue;
+                }
+                if (retval != PM_RET_EX_KEY)
+                {
+                    break;
+                }
+
+                /* Load module from image */
+                retval = mod_import(pobj1, &pobj2);
+                PM_BREAK_IF_ERROR(retval);
+
+                /* #178: Fix import so modules are reused */
+                /*
+                 * Store the module's attrs/globals under the module's name
+                 * in the global module dict (cache)
+                 */
+                heap_gcPushTempRoot(pobj2, &objid);
+                retval = dict_setItem(pobj3, pobj1, pobj2);
+                heap_gcPopTempRoot(objid);
+                PM_BREAK_IF_ERROR(retval);
+
+                /* Put Module on top of stack */
+                TOS = pobj2;
+
+                /* Code after here is a duplicate of CALL_FUNCTION */
+                /* Make frame object to interpret the module's root code */
+                heap_gcPushTempRoot(pobj2, &objid);
+                retval = frame_new(pobj2, &pobj3);
+                heap_gcPopTempRoot(objid);
+                PM_BREAK_IF_ERROR(retval);
+
+                /* No arguments to pass */
+
+                /* Keep ref to current frame */
+                ((pPmFrame_t)pobj3)->fo_back = PM_FP;
+
+                /* Handle to have None popped on return */
+                ((pPmFrame_t)pobj3)->fo_isImport = (uint8_t)1;
+
+                /* Set new frame */
+                PM_FP = (pPmFrame_t)pobj3;
+                continue;
+
+#ifdef HAVE_IMPORTS
+            case IMPORT_FROM:
+                /* #102: Implement the remaining IMPORT_ bytecodes */
+                /* Expect the module on the top of the stack */
+                C_ASSERT(OBJ_GET_TYPE(TOS) == OBJ_TYPE_MOD);
+                pobj1 = TOS;
+
+                /* Get the name of the object to import */
+                t16 = GET_ARG();
+                pobj2 = PM_FP->fo_func->f_co->co_names->val[t16];
+
+                /* Get the object from the module's attributes */
+                retval = dict_getItem((pPmObj_t)((pPmFunc_t)pobj1)->f_attrs,
+                                      pobj2, &pobj3);
+                PM_BREAK_IF_ERROR(retval);
+
+                /* Push the object onto the top of the stack */
+                PM_PUSH(pobj3);
+                continue;
+#endif /* HAVE_IMPORTS */
+
+            case JUMP_FORWARD:
+                t16 = GET_ARG();
+                PM_IP += t16;
+                continue;
+
+            case JUMP_IF_FALSE:
+                t16 = GET_ARG();
+                if (obj_isFalse(TOS))
+                {
+                    PM_IP += t16;
+                }
+                continue;
+
+            case JUMP_IF_TRUE:
+                t16 = GET_ARG();
+                if (!obj_isFalse(TOS))
+                {
+                    PM_IP += t16;
+                }
+                continue;
+
+            case JUMP_ABSOLUTE:
+            case CONTINUE_LOOP:
+                /* Get target offset (bytes) */
+                t16 = GET_ARG();
+
+                /* Jump to base_ip + arg */
+                PM_IP = PM_FP->fo_func->f_co->co_codeaddr + t16;
+                continue;
+
+            case LOAD_GLOBAL:
+                /* Get name */
+                t16 = GET_ARG();
+                pobj1 = PM_FP->fo_func->f_co->co_names->val[t16];
+
+                /* Try globals first */
+                retval = dict_getItem((pPmObj_t)PM_FP->fo_globals,
+                                      pobj1, &pobj2);
+
+                /* If that didn't work, try builtins */
+                if (retval == PM_RET_EX_KEY)
+                {
+                    retval = dict_getItem(PM_PBUILTINS, pobj1, &pobj2);
+
+                    /* No such global, raise NameError */
+                    if (retval == PM_RET_EX_KEY)
+                    {
+                        PM_RAISE(retval, PM_RET_EX_NAME);
+                        break;
+                    }
+                }
+                PM_BREAK_IF_ERROR(retval);
+                PM_PUSH(pobj2);
+                continue;
+
+            case SETUP_LOOP:
+            {
+                uint8_t *pchunk;
+
+                /* Get block span (bytes) */
+                t16 = GET_ARG();
+
+                /* Create block */
+                retval = heap_getChunk(sizeof(PmBlock_t), &pchunk);
+                PM_BREAK_IF_ERROR(retval);
+                pobj1 = (pPmObj_t)pchunk;
+                OBJ_SET_TYPE(pobj1, OBJ_TYPE_BLK);
+
+                /* Store current stack pointer */
+                ((pPmBlock_t)pobj1)->b_sp = PM_SP;
+
+                /* Default handler is to exit block/loop */
+                ((pPmBlock_t)pobj1)->b_handler = PM_IP + t16;
+                ((pPmBlock_t)pobj1)->b_type = B_LOOP;
+
+                /* Insert block into blockstack */
+                ((pPmBlock_t)pobj1)->next = PM_FP->fo_blockstack;
+                PM_FP->fo_blockstack = (pPmBlock_t)pobj1;
+                continue;
+            }
+
+            case LOAD_FAST:
+                t16 = GET_ARG();
+                PM_PUSH(PM_FP->fo_locals[t16]);
+                continue;
+
+            case STORE_FAST:
+                t16 = GET_ARG();
+                PM_FP->fo_locals[t16] = PM_POP();
+                continue;
+
+#ifdef HAVE_DEL
+            case DELETE_FAST:
+                t16 = GET_ARG();
+                PM_FP->fo_locals[t16] = PM_NONE;
+                continue;
+#endif /* HAVE_DEL */
+
+#ifdef HAVE_ASSERT
+            case RAISE_VARARGS:
+                t16 = GET_ARG();
+
+                /* Only supports taking 1 arg for now */
+                if (t16 != 1)
+                {
+                    PM_RAISE(retval, PM_RET_EX_SYS);
+                    break;
+                }
+
+                /* Load Exception class from builtins */
+                retval = dict_getItem(PM_PBUILTINS, PM_EXCEPTION_STR, &pobj2);
+                if (retval != PM_RET_OK)
+                {
+                    PM_RAISE(retval, PM_RET_EX_SYS);
+                    break;
+                }
+
+                /* Raise TypeError if TOS is not an instance of Exception */
+                pobj1 = TOS;
+                if ((OBJ_GET_TYPE(pobj1) != OBJ_TYPE_CLO)
+                    || !class_isSubclass(pobj1, pobj2))
+                {
+                    PM_RAISE(retval, PM_RET_EX_TYPE);
+                    break;
+                }
+
+                /* Push the traceback, parameter and exception object */
+                TOS = PM_NONE;
+                PM_PUSH(PM_NONE);
+                PM_PUSH(pobj1);
+
+                /* Get the exception's code attr */
+                retval = dict_getItem((pPmObj_t)((pPmClass_t)pobj1)->cl_attrs,
+                                      PM_CODE_STR, &pobj2);
+                PM_BREAK_IF_ERROR(retval);
+
+                /* Raise exception by breaking with retval set to code */
+                PM_RAISE(retval, (PmReturn_t)(((pPmInt_t)pobj2)->val & 0xFF));
+                break;
+#endif /* HAVE_ASSERT */
+
+            case CALL_FUNCTION:
+                /* Get num args */
+                t16 = GET_ARG();
+
+                /* Ensure no keyword args */
+                if ((t16 & (uint16_t)0xFF00) != 0)
+                {
+                    PM_RAISE(retval, PM_RET_EX_SYS);
+                    break;
+                }
+
+                /* Get the callable */
+                pobj1 = STACK(t16);
+
+                /* Useless push to get temp-roots stack level used in cleanup */
+                heap_gcPushTempRoot(pobj1, &objid);
+
+                C_DEBUG_PRINT(VERBOSITY_LOW,
+                    "interpret(), CALL_FUNCTION on <obj type=%d @ %p>\n",
+                    OBJ_GET_TYPE(pobj1), pobj1);
+
+#ifdef HAVE_GENERATORS
+                /* If the callable is a generator function (can't be native) */
+                if ((OBJ_GET_TYPE(pobj1) == OBJ_TYPE_FXN)
+                    && (OBJ_GET_TYPE(((pPmFunc_t)pobj1)->f_co) == OBJ_TYPE_COB)
+                    && (((pPmFunc_t)pobj1)->f_co->co_flags & CO_GENERATOR))
+                {
+#ifdef HAVE_DEFAULTARGS
+                    /* Num required args := argcount - num default args */
+                    t8 = ((pPmFunc_t)pobj1)->f_co->co_argcount;
+                    if (((pPmFunc_t)pobj1)->f_defaultargs != C_NULL)
+                    {
+                        t8 -= ((pPmTuple_t)((pPmFunc_t)pobj1)->f_defaultargs)->
+                            length;
+                    }
+
+                    /*
+                     * Raise a TypeError if num args passed
+                     * is more than allowed or less than required
+                     */
+                    if (((t16 & ((uint8_t)0xFF))
+                         > ((pPmFunc_t)pobj1)->f_co->co_argcount)
+                        || ((t16 & ((uint8_t)0xFF)) < t8))
+#else
+                    if ((t16 & ((uint8_t)0xFF)) !=
+                        ((pPmFunc_t)pobj1)->f_co->co_argcount)
+#endif /* HAVE_DEFAULTARGS */
+                    {
+                        PM_RAISE(retval, PM_RET_EX_TYPE);
+                        break;
+                    }
+
+                    /* Collect the function and arguments into a tuple */
+                    retval = tuple_new(t16 + 1, &pobj2);
+                    heap_gcPushTempRoot(pobj2, &objid2);
+                    PM_GOTO_IF_ERROR(retval, CALL_FUNC_CLEANUP);
+                    sli_memcpy((uint8_t *)&((pPmTuple_t)pobj2)->val,
+                               (uint8_t *)&STACK(t16),
+                               (t16 + 1) * sizeof(pPmObj_t));
+
+                    /* Remove old args, push func/args tuple as one arg */
+                    PM_SP -= t16;
+                    PM_PUSH(pobj2);
+                    t16 = 1;
+
+                    /* Set pobj1 and stack to create an instance of Generator */
+                    retval = dict_getItem(PM_PBUILTINS, PM_GENERATOR_STR,
+                                          &pobj1);
+                    C_ASSERT(retval == PM_RET_OK);
+                    STACK(t16) = pobj1;
+                }
+#endif /* HAVE_GENERATORS */
+
+#ifdef HAVE_CLASSES
+                /* If the callable is a class, create an instance of it */
+                if (OBJ_GET_TYPE(pobj1) == OBJ_TYPE_CLO)
+                {
+                    /* This marks that the original callable was a class */
+                    bc = 0;
+
+                    /* Replace class with new instance */
+                    retval = class_instantiate(pobj1, &pobj2);
+                    heap_gcPushTempRoot(pobj2, &objid2);
+                    STACK(t16) = pobj2;
+
+                    /* If __init__ does not exist */
+                    pobj3 = C_NULL;
+                    retval = class_getAttr(pobj1, PM_INIT_STR, &pobj3);
+                    if (retval == PM_RET_EX_KEY)
+                    {
+                        /* Raise TypeError if there are args */
+                        if (t16 > 0)
+                        {
+                            PM_RAISE(retval, PM_RET_EX_TYPE);
+                            goto CALL_FUNC_CLEANUP;
+                        }
+
+                        /* Otherwise, continue with instance */
+                        heap_gcPopTempRoot(objid);
+                        continue;
+                    }
+                    else if (retval != PM_RET_OK)
+                    {
+                        PM_GOTO_IF_ERROR(retval, CALL_FUNC_CLEANUP);
+                    }
+
+                    /* Slide the arguments up 1 slot in the stack */
+                    PM_SP++;
+                    for (t8 = 0; t8 < t16; t8++)
+                    {
+                        STACK(t8) = STACK(t8 + 1);
+                    }
+
+                    /* Convert __init__ to method, insert it as the callable */
+                    retval = class_method(pobj2, pobj3, &pobj1);
+                    PM_GOTO_IF_ERROR(retval, CALL_FUNC_CLEANUP);
+                    heap_gcPushTempRoot(pobj2, &objid2);
+                    STACK(t16) = pobj1;
+                    /* Fall through to call the method */
+                }
+
+                if (OBJ_GET_TYPE(pobj1) == OBJ_TYPE_MTH)
+                {
+                    /* Set the method's func to be the callable */
+                    STACK(t16) = (pPmObj_t)((pPmMethod_t)pobj1)->m_func;
+
+                    /* Slide the arguments up 1 slot in the stack */
+                    PM_SP++;
+                    for (t8 = 0; t8 < t16; t8++)
+                    {
+                        STACK(t8) = STACK(t8 + 1);
+                    }
+
+                    /* Insert instance as "self" arg to the method */
+                    STACK(t16++) = (pPmObj_t)((pPmMethod_t)pobj1)->m_instance;
+
+                    /* Refresh the callable */
+                    pobj1 = (pPmObj_t)((pPmMethod_t)pobj1)->m_func;
+                }
+#endif /* HAVE_CLASSES */
+
+#ifdef HAVE_GENERATORS
+CALL_FUNC_FOR_ITER:
+#endif /* HAVE_GENERATORS */
+                /* Raise a TypeError if object is not callable */
+                if (OBJ_GET_TYPE(pobj1) != OBJ_TYPE_FXN)
+                {
+                    PM_RAISE(retval, PM_RET_EX_TYPE);
+                    goto CALL_FUNC_CLEANUP;
+                }
+
+                /* If it is a regular func (not native) */
+                if (OBJ_GET_TYPE(((pPmFunc_t)pobj1)->f_co) == OBJ_TYPE_COB)
+                {
+                    /*
+                     * #132 Raise TypeError if num args does not match the
+                     * code object's expected argcount
+                     */
+
+#ifdef HAVE_DEFAULTARGS
+                    /* Num required args := argcount - num default args */
+                    t8 = ((pPmFunc_t)pobj1)->f_co->co_argcount;
+                    if (((pPmFunc_t)pobj1)->f_defaultargs != C_NULL)
+                    {
+                        t8 -= ((pPmTuple_t)((pPmFunc_t)pobj1)->f_defaultargs)->
+                            length;
+                    }
+
+                    /*
+                     * Raise a TypeError if num args passed
+                     * is more than allowed or less than required
+                     */
+                    if (((t16 & ((uint8_t)0xFF))
+                         > ((pPmFunc_t)pobj1)->f_co->co_argcount)
+                        || ((t16 & ((uint8_t)0xFF)) < t8))
+#else
+                    if ((t16 & ((uint8_t)0xFF)) !=
+                        ((pPmFunc_t)pobj1)->f_co->co_argcount)
+#endif /* HAVE_DEFAULTARGS */
+                    {
+                        PM_RAISE(retval, PM_RET_EX_TYPE);
+                        break;
+                    }
+
+                    /* Make frame object to run the func object */
+                    retval = frame_new(pobj1, &pobj2);
+                    heap_gcPushTempRoot(pobj2, &objid2);
+                    PM_GOTO_IF_ERROR(retval, CALL_FUNC_CLEANUP);
+
+#ifdef HAVE_CLASSES
+                    /*
+                     * If the original callable was a class, indicate that
+                     * the frame is running the initializer so that
+                     * its return object is checked for None and ignored.
+                     */
+                    if (bc == 0)
+                    {
+                        ((pPmFrame_t)pobj2)->fo_isInit = C_TRUE;
+                    }
+#endif /* HAVE_CLASSES */
+
+#ifdef HAVE_DEFAULTARGS
+                    /* If this func has default arguments, put them in place */
+                    if (((pPmFunc_t)pobj1)->f_defaultargs != C_NULL)
+                    {
+                        int8_t i = 0;
+
+                        /* Copy default args into the new frame's locals */
+                        for ( /* t8 set above */ ;
+                             t8 < ((pPmFunc_t)pobj1)->f_co->co_argcount; t8++)
+                        {
+                            ((pPmFrame_t)pobj2)->fo_locals[t8] =
+                                ((pPmTuple_t)((pPmFunc_t)pobj1)->
+                                 f_defaultargs)->val[i++];
+                        }
+                    }
+#endif /* HAVE_DEFAULTARGS */
+
+                    /* Pass args to new frame */
+                    while (--t16 >= 0)
+                    {
+                        /*
+                         * Pop args from stack right to left,
+                         * since args are pushed left to right,
+                         */
+                        ((pPmFrame_t)pobj2)->fo_locals[t16] = PM_POP();
+                    }
+
+#ifdef HAVE_CLOSURES
+                    /* #256: Add support for closures */
+                    /* Copy arguments that become cellvars */
+                    if (((pPmFunc_t)pobj1)->f_co->co_cellvars != C_NULL)
+                    {
+                        for (t8 = 0;
+                             t8 < ((pPmFunc_t)pobj1)->f_co->co_cellvars->length;
+                             t8++)
+                        {
+                            if (((pPmInt_t)((pPmFunc_t)pobj1)->
+                                f_co->co_cellvars->val[t8])->val >= 0)
+                            {
+                                ((pPmFrame_t)pobj2)->fo_locals[
+                                    ((pPmFunc_t)pobj1)->f_co->co_nlocals + t8] =
+                                    ((pPmFrame_t)pobj2)->fo_locals[
+                                        ((pPmInt_t)(((pPmFunc_t)pobj1)->
+                                            f_co->co_cellvars->val[t8]))->val
+                                    ];
+                            }
+                        }
+                    }
+
+                    /* Fill frame's freevars with references from closure */
+                    for (t8 = 0;
+                         t8 < ((pPmFunc_t)pobj1)->f_co->co_nfreevars;
+                         t8++)
+                    {
+                        C_ASSERT(((pPmFunc_t)pobj1)->f_closure != C_NULL);
+                        ((pPmFrame_t)pobj2)->fo_locals[
+                            ((pPmFunc_t)pobj1)->f_co->co_nlocals
+                            + ((((pPmFunc_t)pobj1)->f_co->co_cellvars == C_NULL) ? 0 : ((pPmFunc_t)pobj1)->f_co->co_cellvars->length)
+                            + t8] = ((pPmFunc_t)pobj1)->f_closure->val[t8];
+                    }
+#endif /* HAVE_CLOSURES */
+
+                    /* Pop func obj */
+                    pobj3 = PM_POP();
+
+                    /* Keep ref to current frame */
+                    ((pPmFrame_t)pobj2)->fo_back = PM_FP;
+
+                    /* Set new frame */
+                    PM_FP = (pPmFrame_t)pobj2;
+                }
+
+                /* If it's native func */
+                else if (OBJ_GET_TYPE(((pPmFunc_t)pobj1)->f_co) ==
+                         OBJ_TYPE_NOB)
+                {
+                    /* Set number of locals (arguments) */
+                    gVmGlobal.nativeframe.nf_numlocals = (uint8_t)t16;
+
+                    /* Pop args from stack */
+                    while (--t16 >= 0)
+                    {
+                        gVmGlobal.nativeframe.nf_locals[t16] = PM_POP();
+                    }
+
+                    /* Set flag, so the GC knows a native session is active */
+                    gVmGlobal.nativeframe.nf_active = C_TRUE;
+
+#ifdef HAVE_GC
+                    /* If the heap is low on memory, run the GC */
+                    if (heap_getAvail() < HEAP_GC_NF_THRESHOLD)
+                    {
+                        retval = heap_gcRun();
+                        PM_GOTO_IF_ERROR(retval, CALL_FUNC_CLEANUP);
+                    }
+#endif /* HAVE_GC */
+
+                    /* Pop the function object */
+                    PM_SP--;
+
+                    /* Get native function index */
+                    pobj2 = (pPmObj_t)((pPmFunc_t)pobj1)->f_co;
+                    t16 = ((pPmNo_t)pobj2)->no_funcindx;
+
+                    /*
+                     * CALL NATIVE FXN: pass caller's frame and numargs
+                     */
+                    /* Positive index is a stdlib func */
+                    if (t16 >= 0)
+                    {
+                        retval = std_nat_fxn_table[t16] (&PM_FP);
+                    }
+
+                    /* Negative index is a usrlib func */
+                    else
+                    {
+                        retval = usr_nat_fxn_table[-t16] (&PM_FP);
+                    }
+
+                    /*
+                     * RETURN FROM NATIVE FXN
+                     */
+
+                    /* Clear flag, so frame will not be marked by the GC */
+                    gVmGlobal.nativeframe.nf_active = C_FALSE;
+
+#ifdef HAVE_CLASSES
+                    /* If class's __init__ called, do not push a return obj */
+                    if (bc == 0)
+                    {
+                        /* Raise TypeError if returned obj was not None */
+                        if ((retval == PM_RET_OK)
+                            && (gVmGlobal.nativeframe.nf_stack != PM_NONE))
+                        {
+                            PM_RAISE(retval, PM_RET_EX_TYPE);
+                            goto CALL_FUNC_CLEANUP;
+                        }
+                    }
+                    else
+#endif /* HAVE_CLASSES */
+
+                    /* If the frame pointer was switched, do nothing to TOS */
+                    if (retval == PM_RET_FRAME_SWITCH)
+                    {
+                        retval = PM_RET_OK;
+                    }
+
+                    /* Otherwise, return the result from the native function */
+                    else
+                    {
+                        PM_PUSH(gVmGlobal.nativeframe.nf_stack);
+                    }
+                }
+CALL_FUNC_CLEANUP:
+                heap_gcPopTempRoot(objid);
+                PM_BREAK_IF_ERROR(retval);
+                continue;
+
+            case MAKE_FUNCTION:
+                /* Get num default args to fxn */
+                t16 = GET_ARG();
+
+                /*
+                 * The current frame's globals become the function object's
+                 * globals.  The current frame is the container object
+                 * of this new function object
+                 */
+                retval = func_new(TOS, (pPmObj_t)PM_FP->fo_globals, &pobj2);
+                PM_BREAK_IF_ERROR(retval);
+
+                /* Put any default args in a tuple */
+                if (t16 > 0)
+                {
+
+#ifdef HAVE_DEFAULTARGS
+                    heap_gcPushTempRoot(pobj2, &objid);
+                    retval = tuple_new(t16, &pobj3);
+                    heap_gcPopTempRoot(objid);
+                    PM_BREAK_IF_ERROR(retval);
+                    PM_SP--;
+                    while (--t16 >= 0)
+                    {
+                        ((pPmTuple_t)pobj3)->val[t16] = PM_POP();
+                    }
+
+                    /* Set func's default args */
+                    ((pPmFunc_t)pobj2)->f_defaultargs = (pPmTuple_t)pobj3;
+#else
+                    /* Default arguments not configured in pmfeatures.h */
+                    PM_RAISE(retval, PM_RET_EX_SYS);
+                    break;
+#endif /* HAVE_DEFAULTARGS */
+
+                }
+                else
+                {
+                    PM_SP--;
+                }
+
+                /* Push func obj */
+                PM_PUSH(pobj2);
+                continue;
+
+#ifdef HAVE_CLOSURES
+            case MAKE_CLOSURE:
+                /* Get number of default args */
+                t16 = GET_ARG();
+                retval = func_new(TOS, (pPmObj_t)PM_FP->fo_globals, &pobj2);
+                PM_BREAK_IF_ERROR(retval);
+
+                /* Set closure of the new function */
+                ((pPmFunc_t)pobj2)->f_closure = (pPmTuple_t)TOS1;
+                PM_SP -= 2;
+
+                /* Collect any default arguments into tuple */
+                if (t16 > 0)
+                {
+                    heap_gcPushTempRoot(pobj2, &objid);
+                    retval = tuple_new(t16, &pobj3);
+                    heap_gcPopTempRoot(objid);
+                    PM_BREAK_IF_ERROR(retval);
+
+                    while (--t16 >= 0)
+                    {
+                        ((pPmTuple_t)pobj3)->val[t16] = PM_POP();
+                    }
+                    ((pPmFunc_t)pobj2)->f_defaultargs = (pPmTuple_t)pobj3;
+                }
+
+                /* Push new func with closure */
+                PM_PUSH(pobj2);
+                continue;
+
+            case LOAD_CLOSURE:
+            case LOAD_DEREF:
+                /* Loads the i'th cell of free variable storage onto TOS */
+                t16 = GET_ARG();
+                pobj1 = PM_FP->fo_locals[PM_FP->fo_func->f_co->co_nlocals + t16];
+                if (pobj1 == C_NULL)
+                {
+                    PM_RAISE(retval, PM_RET_EX_SYS);
+                    break;
+                }
+                PM_PUSH(pobj1);
+                continue;
+
+            case STORE_DEREF:
+                /* Stores TOS into the i'th cell of free variable storage */
+                t16 = GET_ARG();
+                PM_FP->fo_locals[PM_FP->fo_func->f_co->co_nlocals + t16] = PM_POP();
+                continue;
+#endif /* HAVE_CLOSURES */
+
+
+            default:
+                /* SystemError, unknown or unimplemented opcode */
+                PM_RAISE(retval, PM_RET_EX_SYS);
+                break;
+        }
+
+#ifdef HAVE_GENERATORS
+        /* If got a StopIteration exception, check for a B_LOOP block */
+        if (retval == PM_RET_EX_STOP)
+        {
+            pobj1 = (pPmObj_t)PM_FP;
+            while ((retval == PM_RET_EX_STOP) && (pobj1 != C_NULL))
+            {
+                pobj2 = (pPmObj_t)((pPmFrame_t)pobj1)->fo_blockstack;
+                while ((retval == PM_RET_EX_STOP) && (pobj2 != C_NULL))
+                {
+                    if (((pPmBlock_t)pobj2)->b_type == B_LOOP)
+                    {
+                        /* Resume execution where the block handler says */
+                        /* Set PM_FP first, so PM_SP and PM_IP are set in the frame */
+                        PM_FP = (pPmFrame_t)pobj1;
+                        PM_SP = ((pPmBlock_t)pobj2)->b_sp;
+                        PM_IP = ((pPmBlock_t)pobj2)->b_handler;
+                        ((pPmFrame_t)pobj1)->fo_blockstack =
+                            ((pPmFrame_t)pobj1)->fo_blockstack->next;
+                        retval = PM_RET_OK;
+                        break;
+                    }
+
+                    pobj2 = (pPmObj_t)((pPmBlock_t)pobj2)->next;
+                }
+                pobj1 = (pPmObj_t)((pPmFrame_t)pobj1)->fo_back;
+            }
+            if (retval == PM_RET_OK)
+            {
+                continue;
+            }
+        }
+#endif /* HAVE_GENERATORS */
+
+        /*
+         * If execution reaches this point, it is because
+         * a return value (from above) is not OK or we should exit the thread
+         * (return of the function). In any case, remove the
+         * current thread and reschedule.
+         */
+        PM_REPORT_IF_ERROR(retval);
+
+        /* If this is the last thread, return the error code */
+        if ((gVmGlobal.threadList->length <= 1) && (retval != PM_RET_OK))
+        {
+            break;
+        }
+
+        retval = list_remove((pPmObj_t)gVmGlobal.threadList,
+                             (pPmObj_t)gVmGlobal.pthread);
+        gVmGlobal.pthread = C_NULL;
+        PM_BREAK_IF_ERROR(retval);
+
+        retval = interp_reschedule();
+        PM_BREAK_IF_ERROR(retval);
+    }
+
+    return retval;
+}
+
+
+PmReturn_t
+interp_reschedule(void)
+{
+    PmReturn_t retval = PM_RET_OK;
+    static uint8_t threadIndex = (uint8_t)0;
+    pPmObj_t pobj;
+
+    /* If there are no threads in the runnable list, null the active thread */
+    if (gVmGlobal.threadList->length == 0)
+    {
+        gVmGlobal.pthread = C_NULL;
+    }
+
+    /* Otherwise, get the next thread in the list (round robin) */
+    else
+    {
+        if (++threadIndex >= gVmGlobal.threadList->length)
+        {
+            threadIndex = (uint8_t)0;
+        }
+        retval = list_getItem((pPmObj_t)gVmGlobal.threadList, threadIndex,
+                              &pobj);
+        gVmGlobal.pthread = (pPmThread_t)pobj;
+        PM_RETURN_IF_ERROR(retval);
+    }
+
+    /* Clear flag to indicate a reschedule has occurred */
+    interp_setRescheduleFlag(0);
+    return retval;
+}
+
+
+PmReturn_t
+interp_addThread(pPmFunc_t pfunc)
+{
+    PmReturn_t retval;
+    pPmObj_t pframe;
+    pPmObj_t pthread;
+    uint8_t objid1, objid2;
+
+    /* Create a frame for the func */
+    retval = frame_new((pPmObj_t)pfunc, &pframe);
+    PM_RETURN_IF_ERROR(retval);
+
+    /* Create a thread with this new frame */
+    heap_gcPushTempRoot(pframe, &objid1);
+    retval = thread_new(pframe, &pthread);
+    if (retval != PM_RET_OK)
+    {
+        heap_gcPopTempRoot(objid1);
+        return retval;
+    }
+
+    /* Add thread to end of list */
+    heap_gcPushTempRoot(pthread, &objid2);
+    retval = list_append((pPmObj_t)gVmGlobal.threadList, pthread);
+    heap_gcPopTempRoot(objid1);
+    return retval;
+}
+
+
+void
+interp_setRescheduleFlag(uint8_t boolean)
+{
+    gVmGlobal.reschedule = boolean;
+}