diff --git a/py/bc0.h b/py/bc0.h
index 37c0bf7bb53cb8d10c5ce90d0aca5959076faf27..2c8c47bd2e5172728b6f27273255145882feff3f 100644
--- a/py/bc0.h
+++ b/py/bc0.h
@@ -63,8 +63,9 @@
 #define MP_BC_POP_EXCEPT         (0x55)
 #define MP_BC_UNWIND_JUMP        (0x56) // rel byte code offset, 16-bit signed, in excess; then a byte
 
-#define MP_BC_UNARY_OP           (0x60) // byte
-#define MP_BC_BINARY_OP          (0x61) // byte
+#define MP_BC_NOT                (0x60)
+#define MP_BC_UNARY_OP           (0x61) // byte
+#define MP_BC_BINARY_OP          (0x62) // byte
 
 #define MP_BC_BUILD_TUPLE        (0x70) // uint
 #define MP_BC_BUILD_LIST         (0x71) // uint
diff --git a/py/compile.c b/py/compile.c
index 5a2ef0b5c45e4732cc252dfa99d628cebe83cdc0..47741b8da8dd984afbce77a284f218f2667d36e2 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -1466,9 +1466,9 @@ void compile_for_stmt_optimised_range(compiler_t *comp, mp_parse_node_t pn_var,
     compile_node(comp, pn_var);
     compile_node(comp, pn_end);
     if (MP_PARSE_NODE_LEAF_ARG(pn_step) >= 0) {
-        EMIT_ARG(binary_op, RT_COMPARE_OP_LESS);
+        EMIT_ARG(binary_op, RT_BINARY_OP_LESS);
     } else {
-        EMIT_ARG(binary_op, RT_COMPARE_OP_MORE);
+        EMIT_ARG(binary_op, RT_BINARY_OP_MORE);
     }
     EMIT_ARG(pop_jump_if_true, top_label);
 
@@ -1605,7 +1605,7 @@ void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_except,
             }
             EMIT(dup_top);
             compile_node(comp, pns_exception_expr);
-            EMIT_ARG(binary_op, RT_COMPARE_OP_EXCEPTION_MATCH);
+            EMIT_ARG(binary_op, RT_BINARY_OP_EXCEPTION_MATCH);
             EMIT_ARG(pop_jump_if_false, end_finally_label);
         }
 
@@ -1912,21 +1912,7 @@ void compile_and_test(compiler_t *comp, mp_parse_node_struct_t *pns) {
 
 void compile_not_test_2(compiler_t *comp, mp_parse_node_struct_t *pns) {
     compile_node(comp, pns->nodes[0]);
-#if MICROPY_EMIT_CPYTHON
     EMIT_ARG(unary_op, RT_UNARY_OP_NOT);
-#else
-    // eliminate use of NOT byte code
-    int l_load_false = comp_next_label(comp);
-    int l_done = comp_next_label(comp);
-    int stack_size = EMIT(get_stack_size);
-    EMIT_ARG(pop_jump_if_true, l_load_false);
-    EMIT_ARG(load_const_tok, MP_TOKEN_KW_TRUE);
-    EMIT_ARG(jump, l_done);
-    EMIT_ARG(label_assign, l_load_false);
-    EMIT_ARG(load_const_tok, MP_TOKEN_KW_FALSE);
-    EMIT_ARG(label_assign, l_done);
-    EMIT_ARG(set_stack_size, stack_size); // force stack size since it counts 1 pop and 2 pushes statically, but really it's 1 pop and 1 push dynamically
-#endif
 }
 
 void compile_comparison(compiler_t *comp, mp_parse_node_struct_t *pns) {
@@ -1947,26 +1933,26 @@ void compile_comparison(compiler_t *comp, mp_parse_node_struct_t *pns) {
         if (MP_PARSE_NODE_IS_TOKEN(pns->nodes[i])) {
             rt_binary_op_t op;
             switch (MP_PARSE_NODE_LEAF_ARG(pns->nodes[i])) {
-                case MP_TOKEN_OP_LESS: op = RT_COMPARE_OP_LESS; break;
-                case MP_TOKEN_OP_MORE: op = RT_COMPARE_OP_MORE; break;
-                case MP_TOKEN_OP_DBL_EQUAL: op = RT_COMPARE_OP_EQUAL; break;
-                case MP_TOKEN_OP_LESS_EQUAL: op = RT_COMPARE_OP_LESS_EQUAL; break;
-                case MP_TOKEN_OP_MORE_EQUAL: op = RT_COMPARE_OP_MORE_EQUAL; break;
-                case MP_TOKEN_OP_NOT_EQUAL: op = RT_COMPARE_OP_NOT_EQUAL; break;
-                case MP_TOKEN_KW_IN: op = RT_COMPARE_OP_IN; break;
-                default: assert(0); op = RT_COMPARE_OP_LESS; // shouldn't happen
+                case MP_TOKEN_OP_LESS: op = RT_BINARY_OP_LESS; break;
+                case MP_TOKEN_OP_MORE: op = RT_BINARY_OP_MORE; break;
+                case MP_TOKEN_OP_DBL_EQUAL: op = RT_BINARY_OP_EQUAL; break;
+                case MP_TOKEN_OP_LESS_EQUAL: op = RT_BINARY_OP_LESS_EQUAL; break;
+                case MP_TOKEN_OP_MORE_EQUAL: op = RT_BINARY_OP_MORE_EQUAL; break;
+                case MP_TOKEN_OP_NOT_EQUAL: op = RT_BINARY_OP_NOT_EQUAL; break;
+                case MP_TOKEN_KW_IN: op = RT_BINARY_OP_IN; break;
+                default: assert(0); op = RT_BINARY_OP_LESS; // shouldn't happen
             }
             EMIT_ARG(binary_op, op);
         } else if (MP_PARSE_NODE_IS_STRUCT(pns->nodes[i])) {
             mp_parse_node_struct_t *pns2 = (mp_parse_node_struct_t*)pns->nodes[i];
             int kind = MP_PARSE_NODE_STRUCT_KIND(pns2);
             if (kind == PN_comp_op_not_in) {
-                EMIT_ARG(binary_op, RT_COMPARE_OP_NOT_IN);
+                EMIT_ARG(binary_op, RT_BINARY_OP_NOT_IN);
             } else if (kind == PN_comp_op_is) {
                 if (MP_PARSE_NODE_IS_NULL(pns2->nodes[0])) {
-                    EMIT_ARG(binary_op, RT_COMPARE_OP_IS);
+                    EMIT_ARG(binary_op, RT_BINARY_OP_IS);
                 } else {
-                    EMIT_ARG(binary_op, RT_COMPARE_OP_IS_NOT);
+                    EMIT_ARG(binary_op, RT_BINARY_OP_IS_NOT);
                 }
             } else {
                 // shouldn't happen
diff --git a/py/emitbc.c b/py/emitbc.c
index a76e59336396d1b0e92e868152b1a9ba8296d001..e22a8b47ae7079b9dbb61023d282cd4e992981eb 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -600,13 +600,32 @@ static void emit_bc_pop_except(emit_t *emit) {
 }
 
 static void emit_bc_unary_op(emit_t *emit, rt_unary_op_t op) {
-    emit_pre(emit, 0);
-    emit_write_byte_code_byte_byte(emit, MP_BC_UNARY_OP, op);
+    if (op == RT_UNARY_OP_NOT) {
+        emit_pre(emit, 0);
+        emit_write_byte_code_byte_byte(emit, MP_BC_UNARY_OP, RT_UNARY_OP_BOOL);
+        emit_pre(emit, 0);
+        emit_write_byte_code_byte(emit, MP_BC_NOT);
+    } else {
+        emit_pre(emit, 0);
+        emit_write_byte_code_byte_byte(emit, MP_BC_UNARY_OP, op);
+    }
 }
 
 static void emit_bc_binary_op(emit_t *emit, rt_binary_op_t op) {
+    bool invert = false;
+    if (op == RT_BINARY_OP_NOT_IN) {
+        invert = true;
+        op = RT_BINARY_OP_IN;
+    } else if (op == RT_BINARY_OP_IS_NOT) {
+        invert = true;
+        op = RT_BINARY_OP_IS;
+    }
     emit_pre(emit, -1);
     emit_write_byte_code_byte_byte(emit, MP_BC_BINARY_OP, op);
+    if (invert) {
+        emit_pre(emit, 0);
+        emit_write_byte_code_byte(emit, MP_BC_NOT);
+    }
 }
 
 static void emit_bc_build_tuple(emit_t *emit, int n_args) {
diff --git a/py/emitcpy.c b/py/emitcpy.c
index 1e507a581936eada6142d24187f28275745846fd..bf8abd1c93a985ddd7bdc8a1cf1180772160b6ee 100644
--- a/py/emitcpy.c
+++ b/py/emitcpy.c
@@ -545,10 +545,10 @@ static void emit_cpy_unary_op(emit_t *emit, rt_unary_op_t op) {
     emit_pre(emit, 0, 1);
     if (emit->pass == PASS_3) {
         switch (op) {
-            case RT_UNARY_OP_NOT: printf("UNARY_NOT\n"); break;
             case RT_UNARY_OP_POSITIVE: printf("UNARY_POSITIVE\n"); break;
             case RT_UNARY_OP_NEGATIVE: printf("UNARY_NEGATIVE\n"); break;
             case RT_UNARY_OP_INVERT: printf("UNARY_INVERT\n"); break;
+            case RT_UNARY_OP_NOT: printf("UNARY_NOT\n"); break;
             default: assert(0);
         }
     }
@@ -589,17 +589,17 @@ static void emit_cpy_binary_op(emit_t *emit, rt_binary_op_t op) {
             case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: printf("INPLACE_TRUE_DIVIDE\n"); break;
             case RT_BINARY_OP_INPLACE_MODULO: printf("INPLACE_MODULO\n"); break;
             case RT_BINARY_OP_INPLACE_POWER: printf("INPLACE_POWER\n"); break;
-            case RT_COMPARE_OP_LESS: printf("COMPARE_OP <\n"); break;
-            case RT_COMPARE_OP_MORE: printf("COMPARE_OP >\n"); break;
-            case RT_COMPARE_OP_EQUAL: printf("COMPARE_OP ==\n"); break;
-            case RT_COMPARE_OP_LESS_EQUAL: printf("COMPARE_OP <=\n"); break;
-            case RT_COMPARE_OP_MORE_EQUAL: printf("COMPARE_OP >=\n"); break;
-            case RT_COMPARE_OP_NOT_EQUAL: printf("COMPARE_OP !=\n"); break;
-            case RT_COMPARE_OP_IN: printf("COMPARE_OP in\n"); break;
-            case RT_COMPARE_OP_NOT_IN: printf("COMPARE_OP not in\n"); break;
-            case RT_COMPARE_OP_IS: printf("COMPARE_OP is\n"); break;
-            case RT_COMPARE_OP_IS_NOT: printf("COMPARE_OP is not\n"); break;
-            case RT_COMPARE_OP_EXCEPTION_MATCH: printf("COMPARE_OP exception match\n"); break;
+            case RT_BINARY_OP_LESS: printf("COMPARE_OP <\n"); break;
+            case RT_BINARY_OP_MORE: printf("COMPARE_OP >\n"); break;
+            case RT_BINARY_OP_EQUAL: printf("COMPARE_OP ==\n"); break;
+            case RT_BINARY_OP_LESS_EQUAL: printf("COMPARE_OP <=\n"); break;
+            case RT_BINARY_OP_MORE_EQUAL: printf("COMPARE_OP >=\n"); break;
+            case RT_BINARY_OP_NOT_EQUAL: printf("COMPARE_OP !=\n"); break;
+            case RT_BINARY_OP_IN: printf("COMPARE_OP in\n"); break;
+            case RT_BINARY_OP_IS: printf("COMPARE_OP is\n"); break;
+            case RT_BINARY_OP_EXCEPTION_MATCH: printf("COMPARE_OP exception match\n"); break;
+            case RT_BINARY_OP_NOT_IN: printf("COMPARE_OP not in\n"); break;
+            case RT_BINARY_OP_IS_NOT: printf("COMPARE_OP is not\n"); break;
             default: assert(0);
         }
     }
diff --git a/py/emitnative.c b/py/emitnative.c
index 9e8ae1a99bf0f7653bb763aa82021cad2e82c443..8968db4be621cd5545555308c7436543329e189b 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -1017,7 +1017,7 @@ static void emit_native_binary_op(emit_t *emit, rt_binary_op_t op) {
             asm_thumb_add_reg_reg_reg(emit->as, REG_ARG_2, REG_ARG_2, REG_ARG_3);
 #endif
             emit_post_push_reg(emit, VTYPE_INT, REG_ARG_2);
-        } else if (op == RT_COMPARE_OP_LESS) {
+        } else if (op == RT_BINARY_OP_LESS) {
 #if N_X64
             asm_x64_xor_r64_to_r64(emit->as, REG_RET, REG_RET);
             asm_x64_cmp_r64_with_r64(emit->as, REG_ARG_3, REG_ARG_2);
diff --git a/py/obj.c b/py/obj.c
index 33d64c894f23d58c7ef6a2a20f103711fe99f0be..e21596fdf501df530511cc0bcbf1d656023dc034 100644
--- a/py/obj.c
+++ b/py/obj.c
@@ -116,7 +116,7 @@ bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2) {
                 // If o2 is long int, dispatch to its virtual methods
                 mp_obj_base_t *o = o2;
                 if (o->type->binary_op != NULL) {
-                    mp_obj_t r = o->type->binary_op(RT_COMPARE_OP_EQUAL, o2, o1);
+                    mp_obj_t r = o->type->binary_op(RT_BINARY_OP_EQUAL, o2, o1);
                     return r == mp_const_true ? true : false;
                 }
             }
@@ -127,7 +127,7 @@ bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2) {
     } else {
         mp_obj_base_t *o = o1;
         if (o->type->binary_op != NULL) {
-            mp_obj_t r = o->type->binary_op(RT_COMPARE_OP_EQUAL, o1, o2);
+            mp_obj_t r = o->type->binary_op(RT_BINARY_OP_EQUAL, o1, o2);
             if (r != MP_OBJ_NULL) {
                 return r == mp_const_true ? true : false;
             }
diff --git a/py/objdict.c b/py/objdict.c
index 934eb506504593463659a5ab432fe05c4edb7993..bb851d1024b59871b95c9008c6c903c0ef19d275 100644
--- a/py/objdict.c
+++ b/py/objdict.c
@@ -65,11 +65,10 @@ static mp_obj_t dict_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
                 return elem->value;
             }
         }
-        case RT_COMPARE_OP_IN:
-        case RT_COMPARE_OP_NOT_IN:
+        case RT_BINARY_OP_IN:
         {
             mp_map_elem_t *elem = mp_map_lookup(&o->map, rhs_in, MP_MAP_LOOKUP);
-            return MP_BOOL((op == RT_COMPARE_OP_IN) ^ (elem == NULL));
+            return MP_BOOL(elem != NULL);
         }
         default:
             // op not supported
@@ -380,7 +379,7 @@ static mp_obj_t dict_view_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
     /* only supported for the 'keys' kind until sets and dicts are refactored */
     mp_obj_dict_view_t *o = lhs_in;
     if (o->kind != MP_DICT_VIEW_KEYS) return NULL;
-    if (op != RT_COMPARE_OP_IN && op != RT_COMPARE_OP_NOT_IN) return NULL;
+    if (op != RT_BINARY_OP_IN) return NULL;
     return dict_binary_op(op, o->dict, rhs_in);
 }
 
diff --git a/py/objfloat.c b/py/objfloat.c
index 9caeaf768662e53fb8c988fab19dad67d336a750..ed6260681c6d4d8aa1d7b76859fd2aa7179b42e1 100644
--- a/py/objfloat.c
+++ b/py/objfloat.c
@@ -101,10 +101,10 @@ mp_obj_t mp_obj_float_binary_op(int op, mp_float_t lhs_val, mp_obj_t rhs_in) {
         case RT_BINARY_OP_TRUE_DIVIDE:
         case RT_BINARY_OP_INPLACE_TRUE_DIVIDE: lhs_val /= rhs_val; break;
 
-        case RT_COMPARE_OP_LESS: return MP_BOOL(lhs_val < rhs_val);
-        case RT_COMPARE_OP_MORE: return MP_BOOL(lhs_val > rhs_val);
-        case RT_COMPARE_OP_LESS_EQUAL: return MP_BOOL(lhs_val <= rhs_val);
-        case RT_COMPARE_OP_MORE_EQUAL: return MP_BOOL(lhs_val >= rhs_val);
+        case RT_BINARY_OP_LESS: return MP_BOOL(lhs_val < rhs_val);
+        case RT_BINARY_OP_MORE: return MP_BOOL(lhs_val > rhs_val);
+        case RT_BINARY_OP_LESS_EQUAL: return MP_BOOL(lhs_val <= rhs_val);
+        case RT_BINARY_OP_MORE_EQUAL: return MP_BOOL(lhs_val >= rhs_val);
 
         return NULL; // op not supported
     }
diff --git a/py/objint_longlong.c b/py/objint_longlong.c
index a59bcf061e7904dd14f5b7c3170a06ee5c415046..83db6033f43333d5e2c67131ddf6465af25ccc1b 100644
--- a/py/objint_longlong.c
+++ b/py/objint_longlong.c
@@ -103,17 +103,17 @@ mp_obj_t int_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
         case RT_BINARY_OP_INPLACE_RSHIFT:
             lhs->val >>= (int)rhs_val; return lhs;
 
-        case RT_COMPARE_OP_LESS:
+        case RT_BINARY_OP_LESS:
             return MP_BOOL(lhs->val < rhs_val);
-        case RT_COMPARE_OP_MORE:
+        case RT_BINARY_OP_MORE:
             return MP_BOOL(lhs->val > rhs_val);
-        case RT_COMPARE_OP_LESS_EQUAL:
+        case RT_BINARY_OP_LESS_EQUAL:
             return MP_BOOL(lhs->val <= rhs_val);
-        case RT_COMPARE_OP_MORE_EQUAL:
+        case RT_BINARY_OP_MORE_EQUAL:
             return MP_BOOL(lhs->val >= rhs_val);
-        case RT_COMPARE_OP_EQUAL:
+        case RT_BINARY_OP_EQUAL:
             return MP_BOOL(lhs->val == rhs_val);
-        case RT_COMPARE_OP_NOT_EQUAL:
+        case RT_BINARY_OP_NOT_EQUAL:
             return MP_BOOL(lhs->val != rhs_val);
 
         default:
diff --git a/py/objlist.c b/py/objlist.c
index 81040e3b8ed2ddbfe87745ea0c7057a30bc152fb..b28ca81279e9c9d1046325f0968a8abfda773195 100644
--- a/py/objlist.c
+++ b/py/objlist.c
@@ -67,7 +67,7 @@ static mp_obj_t list_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp
     return NULL;
 }
 
-// Don't pass RT_COMPARE_OP_NOT_EQUAL here
+// Don't pass RT_BINARY_OP_NOT_EQUAL here
 static bool list_cmp_helper(int op, mp_obj_t self_in, mp_obj_t another_in) {
     assert(MP_OBJ_IS_TYPE(self_in, &list_type));
     if (!MP_OBJ_IS_TYPE(another_in, &list_type)) {
@@ -75,19 +75,19 @@ static bool list_cmp_helper(int op, mp_obj_t self_in, mp_obj_t another_in) {
     }
     mp_obj_list_t *self = self_in;
     mp_obj_list_t *another = another_in;
-    if (op == RT_COMPARE_OP_EQUAL && self->len != another->len) {
+    if (op == RT_BINARY_OP_EQUAL && self->len != another->len) {
         return false;
     }
 
     // Let's deal only with > & >=
-    if (op == RT_COMPARE_OP_LESS || op == RT_COMPARE_OP_LESS_EQUAL) {
+    if (op == RT_BINARY_OP_LESS || op == RT_BINARY_OP_LESS_EQUAL) {
         mp_obj_t t = self;
         self = another;
         another = t;
-        if (op == RT_COMPARE_OP_LESS) {
-            op = RT_COMPARE_OP_MORE;
+        if (op == RT_BINARY_OP_LESS) {
+            op = RT_BINARY_OP_MORE;
         } else {
-            op = RT_COMPARE_OP_MORE_EQUAL;
+            op = RT_BINARY_OP_MORE_EQUAL;
         }
     }
 
@@ -96,7 +96,7 @@ static bool list_cmp_helper(int op, mp_obj_t self_in, mp_obj_t another_in) {
     bool rel_status;
     for (int i = 0; i < len; i++) {
         eq_status = mp_obj_equal(self->items[i], another->items[i]);
-        if (op == RT_COMPARE_OP_EQUAL && !eq_status) {
+        if (op == RT_BINARY_OP_EQUAL && !eq_status) {
             return false;
         }
         rel_status = (rt_binary_op(op, self->items[i], another->items[i]) == mp_const_true);
@@ -113,7 +113,7 @@ static bool list_cmp_helper(int op, mp_obj_t self_in, mp_obj_t another_in) {
                 // ... then longer list length wins (we deal only with >)
                 return false;
             }
-        } else if (op == RT_COMPARE_OP_MORE) {
+        } else if (op == RT_BINARY_OP_MORE) {
             // Otherwise, if we have strict relation, equality means failure
             return false;
         }
@@ -169,14 +169,14 @@ static mp_obj_t list_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
             mp_seq_multiply(o->items, sizeof(*o->items), o->len, n, s->items);
             return s;
         }
-        case RT_COMPARE_OP_EQUAL:
-        case RT_COMPARE_OP_LESS:
-        case RT_COMPARE_OP_LESS_EQUAL:
-        case RT_COMPARE_OP_MORE:
-        case RT_COMPARE_OP_MORE_EQUAL:
+        case RT_BINARY_OP_EQUAL:
+        case RT_BINARY_OP_LESS:
+        case RT_BINARY_OP_LESS_EQUAL:
+        case RT_BINARY_OP_MORE:
+        case RT_BINARY_OP_MORE_EQUAL:
             return MP_BOOL(list_cmp_helper(op, lhs, rhs));
-        case RT_COMPARE_OP_NOT_EQUAL:
-            return MP_BOOL(!list_cmp_helper(RT_COMPARE_OP_EQUAL, lhs, rhs));
+        case RT_BINARY_OP_NOT_EQUAL:
+            return MP_BOOL(!list_cmp_helper(RT_BINARY_OP_EQUAL, lhs, rhs));
 
         default:
             // op not supported
@@ -237,7 +237,7 @@ static mp_obj_t list_pop(uint n_args, const mp_obj_t *args) {
 
 // TODO make this conform to CPython's definition of sort
 static void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn, bool reversed) {
-    int op = reversed ? RT_COMPARE_OP_MORE : RT_COMPARE_OP_LESS;
+    int op = reversed ? RT_BINARY_OP_MORE : RT_BINARY_OP_LESS;
     while (head < tail) {
         mp_obj_t *h = head - 1;
         mp_obj_t *t = tail;
diff --git a/py/objset.c b/py/objset.c
index cf4545c2571112ae4dc0f5af700a37c386dc52de..05079cf55b08e7cc9d4b5e436e2e6221bd9a4c98 100644
--- a/py/objset.c
+++ b/py/objset.c
@@ -396,24 +396,23 @@ static mp_obj_t set_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
         return set_intersect(lhs, rhs);
     case RT_BINARY_OP_INPLACE_SUBTRACT:
         return set_diff(2, args);
-    case RT_COMPARE_OP_LESS:
+    case RT_BINARY_OP_LESS:
         return set_issubset_proper(lhs, rhs);
-    case RT_COMPARE_OP_MORE:
+    case RT_BINARY_OP_MORE:
         return set_issuperset_proper(lhs, rhs);
-    case RT_COMPARE_OP_EQUAL:
+    case RT_BINARY_OP_EQUAL:
         return set_equal(lhs, rhs);
-    case RT_COMPARE_OP_LESS_EQUAL:
+    case RT_BINARY_OP_LESS_EQUAL:
         return set_issubset(lhs, rhs);
-    case RT_COMPARE_OP_MORE_EQUAL:
+    case RT_BINARY_OP_MORE_EQUAL:
         return set_issuperset(lhs, rhs);
-    case RT_COMPARE_OP_NOT_EQUAL:
+    case RT_BINARY_OP_NOT_EQUAL:
         return MP_BOOL(set_equal(lhs, rhs) == mp_const_false);
-    case RT_COMPARE_OP_IN:
-    case RT_COMPARE_OP_NOT_IN:
+    case RT_BINARY_OP_IN:
     {
         mp_obj_set_t *o = lhs;
         mp_obj_t elem = mp_set_lookup(&o->set, rhs, MP_MAP_LOOKUP);
-        return MP_BOOL((op == RT_COMPARE_OP_IN) ^ (elem == NULL));
+        return MP_BOOL(elem != NULL);
     }
     default:
         // op not supported
diff --git a/py/objstr.c b/py/objstr.c
index fa658ac4d7c8fba4cb9f9a9ef956260750339ea5..3f6aa483e299bf2746bb66d300cbbcf88d272c50 100644
--- a/py/objstr.c
+++ b/py/objstr.c
@@ -168,12 +168,11 @@ mp_obj_t str_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
             }
             break;
 
-        case RT_COMPARE_OP_IN:
-        case RT_COMPARE_OP_NOT_IN:
+        case RT_BINARY_OP_IN:
             /* NOTE `a in b` is `b.__contains__(a)` */
             if (MP_OBJ_IS_STR(rhs_in)) {
                 GET_STR_DATA_LEN(rhs_in, rhs_data, rhs_len);
-                return MP_BOOL((op == RT_COMPARE_OP_IN) ^ (find_subbytes(lhs_data, lhs_len, rhs_data, rhs_len) == NULL));
+                return MP_BOOL(find_subbytes(lhs_data, lhs_len, rhs_data, rhs_len) != NULL);
             }
             break;
 
diff --git a/py/objtype.c b/py/objtype.c
index 45992b23a352db8cf96f9d32e1861c8be266aa88..60163253446a9ea5b310cc6c1169e8738b4b329f 100644
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -117,6 +117,7 @@ static mp_obj_t class_make_new(mp_obj_t self_in, uint n_args, uint n_kw, const m
 }
 
 static const qstr unary_op_method_name[] = {
+    [RT_UNARY_OP_NOT] = MP_QSTR_, // don't need to implement this
     [RT_UNARY_OP_BOOL] = MP_QSTR___bool__,
     [RT_UNARY_OP_LEN] = MP_QSTR___len__,
     //[RT_UNARY_OP_POSITIVE,
@@ -168,18 +169,16 @@ static const qstr binary_op_method_name[] = {
     RT_BINARY_OP_INPLACE_TRUE_DIVIDE,
     RT_BINARY_OP_INPLACE_MODULO,
     RT_BINARY_OP_INPLACE_POWER,
-    RT_COMPARE_OP_LESS,
-    RT_COMPARE_OP_MORE,
-    RT_COMPARE_OP_EQUAL,
-    RT_COMPARE_OP_LESS_EQUAL,
-    RT_COMPARE_OP_MORE_EQUAL,
-    RT_COMPARE_OP_NOT_EQUAL,
-    RT_COMPARE_OP_IN,
-    RT_COMPARE_OP_NOT_IN,
-    RT_COMPARE_OP_IS,
-    RT_COMPARE_OP_IS_NOT,
+    RT_BINARY_OP_LESS,
+    RT_BINARY_OP_MORE,
+    RT_BINARY_OP_EQUAL,
+    RT_BINARY_OP_LESS_EQUAL,
+    RT_BINARY_OP_MORE_EQUAL,
+    RT_BINARY_OP_NOT_EQUAL,
+    RT_BINARY_OP_IN,
+    RT_BINARY_OP_IS,
     */
-    [RT_COMPARE_OP_EXCEPTION_MATCH] = MP_QSTR_, // not implemented, used to make sure array has full size
+    [RT_BINARY_OP_EXCEPTION_MATCH] = MP_QSTR_, // not implemented, used to make sure array has full size
 };
 
 static mp_obj_t class_binary_op(int op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
diff --git a/py/runtime.c b/py/runtime.c
index 77e596c5d0f0c9f37334cf70c348d6453f7f2b42..d22fba8f008be2acf084bed860737e5d10f08396 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -478,6 +478,7 @@ void rt_store_global(qstr qstr, mp_obj_t obj) {
 
 mp_obj_t rt_unary_op(int op, mp_obj_t arg) {
     DEBUG_OP_printf("unary %d %p\n", op, arg);
+
     if (MP_OBJ_IS_SMALL_INT(arg)) {
         mp_small_int_t val = MP_OBJ_SMALL_INT_VALUE(arg);
         switch (op) {
@@ -516,28 +517,23 @@ mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
     //   then fail
     // note that list does not implement + or +=, so that inplace_concat is reached first for +=
 
-    // deal with is, is not
-    if (op == RT_COMPARE_OP_IS) {
+    // deal with is
+    if (op == RT_BINARY_OP_IS) {
         // TODO: may need to handle strings specially, CPython appears to
         // assume all strings are interned (so "is" == "==" for strings)
         return MP_BOOL(lhs == rhs);
     }
-    if (op == RT_COMPARE_OP_IS_NOT) {
-        // TODO: may need to handle strings specially, CPython appears to
-        // assume all strings are interned (so "is" == "==" for strings)
-        return MP_BOOL(lhs != rhs);
-    }
 
     // deal with == and != for all types
-    if (op == RT_COMPARE_OP_EQUAL || op == RT_COMPARE_OP_NOT_EQUAL) {
+    if (op == RT_BINARY_OP_EQUAL || op == RT_BINARY_OP_NOT_EQUAL) {
         if (mp_obj_equal(lhs, rhs)) {
-            if (op == RT_COMPARE_OP_EQUAL) {
+            if (op == RT_BINARY_OP_EQUAL) {
                 return mp_const_true;
             } else {
                 return mp_const_false;
             }
         } else {
-            if (op == RT_COMPARE_OP_EQUAL) {
+            if (op == RT_BINARY_OP_EQUAL) {
                 return mp_const_false;
             } else {
                 return mp_const_true;
@@ -546,7 +542,7 @@ mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
     }
 
     // deal with exception_match for all types
-    if (op == RT_COMPARE_OP_EXCEPTION_MATCH) {
+    if (op == RT_BINARY_OP_EXCEPTION_MATCH) {
         // TODO properly! at the moment it just compares the exception identifier for equality
         if (MP_OBJ_IS_TYPE(lhs, &exception_type) && MP_OBJ_IS_TYPE(rhs, &exception_type)) {
             if (mp_obj_exception_get_type(lhs) == mp_obj_exception_get_type(rhs)) {
@@ -604,10 +600,10 @@ mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
                     lhs_val = ans;
                     break;
                 }
-                case RT_COMPARE_OP_LESS: return MP_BOOL(lhs_val < rhs_val); break;
-                case RT_COMPARE_OP_MORE: return MP_BOOL(lhs_val > rhs_val); break;
-                case RT_COMPARE_OP_LESS_EQUAL: return MP_BOOL(lhs_val <= rhs_val); break;
-                case RT_COMPARE_OP_MORE_EQUAL: return MP_BOOL(lhs_val >= rhs_val); break;
+                case RT_BINARY_OP_LESS: return MP_BOOL(lhs_val < rhs_val); break;
+                case RT_BINARY_OP_MORE: return MP_BOOL(lhs_val > rhs_val); break;
+                case RT_BINARY_OP_LESS_EQUAL: return MP_BOOL(lhs_val <= rhs_val); break;
+                case RT_BINARY_OP_MORE_EQUAL: return MP_BOOL(lhs_val >= rhs_val); break;
 
                 default: assert(0);
             }
@@ -625,12 +621,12 @@ mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
         }
     }
 
-    /* deal with `in` and `not in`
+    /* deal with `in`
      *
      * NOTE `a in b` is `b.__contains__(a)`, hence why the generic dispatch
      * needs to go below
      */
-    if (op == RT_COMPARE_OP_IN || op == RT_COMPARE_OP_NOT_IN) {
+    if (op == RT_BINARY_OP_IN) {
         mp_obj_type_t *type = mp_obj_get_type(rhs);
         if (type->binary_op != NULL) {
             mp_obj_t res = type->binary_op(op, rhs, lhs);
@@ -644,10 +640,10 @@ mp_obj_t rt_binary_op(int op, mp_obj_t lhs, mp_obj_t rhs) {
             mp_obj_t iter = rt_getiter(rhs);
             while ((next = rt_iternext(iter)) != mp_const_stop_iteration) {
                 if (mp_obj_equal(next, lhs)) {
-                    return MP_BOOL(op == RT_COMPARE_OP_IN);
+                    return mp_const_true;
                 }
             }
-            return MP_BOOL(op != RT_COMPARE_OP_IN);
+            return mp_const_false;
         }
 
         nlr_jump(mp_obj_new_exception_msg_varg(
diff --git a/py/runtime0.h b/py/runtime0.h
index a0f553a8969410d33f3194cd2896dbb82a16b1cc..9edf7ec0e7b8d64b18a82becc1b8274a1b2d3c48 100644
--- a/py/runtime0.h
+++ b/py/runtime0.h
@@ -4,7 +4,7 @@ typedef enum {
     RT_UNARY_OP_POSITIVE,
     RT_UNARY_OP_NEGATIVE,
     RT_UNARY_OP_INVERT,
-    // Used only for CPython-compatible codegeneration
+    // these are not supported by the runtime and must be synthesised by the emitter
     RT_UNARY_OP_NOT,
 } rt_unary_op_t;
 
@@ -34,18 +34,19 @@ typedef enum {
     RT_BINARY_OP_INPLACE_TRUE_DIVIDE,
     RT_BINARY_OP_INPLACE_MODULO,
     RT_BINARY_OP_INPLACE_POWER,
-    // TODO probably should rename these COMPARE->BINARY
-    RT_COMPARE_OP_LESS,
-    RT_COMPARE_OP_MORE,
-    RT_COMPARE_OP_EQUAL,
-    RT_COMPARE_OP_LESS_EQUAL,
-    RT_COMPARE_OP_MORE_EQUAL,
-    RT_COMPARE_OP_NOT_EQUAL,
-    RT_COMPARE_OP_IN,
-    RT_COMPARE_OP_NOT_IN,
-    RT_COMPARE_OP_IS,
-    RT_COMPARE_OP_IS_NOT,
-    RT_COMPARE_OP_EXCEPTION_MATCH,
+    // these should return a bool
+    RT_BINARY_OP_LESS,
+    RT_BINARY_OP_MORE,
+    RT_BINARY_OP_EQUAL,
+    RT_BINARY_OP_LESS_EQUAL,
+    RT_BINARY_OP_MORE_EQUAL,
+    RT_BINARY_OP_NOT_EQUAL,
+    RT_BINARY_OP_IN,
+    RT_BINARY_OP_IS,
+    RT_BINARY_OP_EXCEPTION_MATCH,
+    // these are not supported by the runtime and must be synthesised by the emitter
+    RT_BINARY_OP_NOT_IN,
+    RT_BINARY_OP_IS_NOT,
 } rt_binary_op_t;
 
 typedef enum {
diff --git a/py/showbc.c b/py/showbc.c
index 4c19a6becc43f09f8613f0908d23bad20747b612..d765c81a5f6ff78fb2914971be7ee4a0813ea93e 100644
--- a/py/showbc.c
+++ b/py/showbc.c
@@ -263,12 +263,14 @@ void mp_byte_code_print(const byte *ip, int len) {
                 printf("POP_EXCEPT");
                 break;
 
-                /*
+            case MP_BC_NOT:
+                printf("NOT");
+                break;
+
             case MP_BC_UNARY_OP:
                 unum = *ip++;
-                *sp = rt_unary_op(unum, *sp);
+                printf("UNARY_OP " UINT_FMT, unum);
                 break;
-                */
 
             case MP_BC_BINARY_OP:
                 unum = *ip++;
diff --git a/py/vm.c b/py/vm.c
index 748e0e935bd4e66d2b1d4fddce8fe4de9eb10451..1d47076dfb9a1b1725cbc09bd7b636dbe25644e6 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -428,6 +428,14 @@ unwind_jump:
                         //sp -= 3; // pop 3 exception values
                         break;
 
+                    case MP_BC_NOT:
+                        if (TOP() == mp_const_true) {
+                            SET_TOP(mp_const_false);
+                        } else {
+                            SET_TOP(mp_const_true);
+                        }
+                        break;
+
                     case MP_BC_UNARY_OP:
                         unum = *ip++;
                         SET_TOP(rt_unary_op(unum, TOP()));