diff --git a/py/bc0.h b/py/bc0.h
index 2dfdc701195a34ea5e31ff45ae0d1ffc242b1a39..0a2d9092f977304006dc07e6e911dea9bd0280f3 100644
--- a/py/bc0.h
+++ b/py/bc0.h
@@ -117,6 +117,6 @@
 #define MP_BC_LOAD_FAST_MULTI            (0xb0) // + N(16)
 #define MP_BC_STORE_FAST_MULTI           (0xc0) // + N(16)
 #define MP_BC_UNARY_OP_MULTI             (0xd0) // + op(6)
-#define MP_BC_BINARY_OP_MULTI            (0xd6) // + op(35)
+#define MP_BC_BINARY_OP_MULTI            (0xd6) // + op(36)
 
 #endif // __MICROPY_INCLUDED_PY_BC0_H__
diff --git a/py/modbuiltins.c b/py/modbuiltins.c
index ec40056e82d35c8c2e13cf8aa1658641782622c4..16b0e320ca4c9fc799ade04420dede5c7c14e04e 100644
--- a/py/modbuiltins.c
+++ b/py/modbuiltins.c
@@ -230,44 +230,7 @@ STATIC mp_obj_t mp_builtin_dir(mp_uint_t n_args, const mp_obj_t *args) {
 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_dir_obj, 0, 1, mp_builtin_dir);
 
 STATIC mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in) {
-    // TODO handle big int
-    if (MP_OBJ_IS_SMALL_INT(o1_in) && MP_OBJ_IS_SMALL_INT(o2_in)) {
-        mp_int_t i1 = MP_OBJ_SMALL_INT_VALUE(o1_in);
-        mp_int_t i2 = MP_OBJ_SMALL_INT_VALUE(o2_in);
-        if (i2 == 0) {
-            #if MICROPY_PY_BUILTINS_FLOAT
-            zero_division_error:
-            #endif
-            nlr_raise(mp_obj_new_exception_msg(&mp_type_ZeroDivisionError, "division by zero"));
-        }
-        mp_obj_t args[2];
-        args[0] = MP_OBJ_NEW_SMALL_INT(mp_small_int_floor_divide(i1, i2));
-        args[1] = MP_OBJ_NEW_SMALL_INT(mp_small_int_modulo(i1, i2));
-        return mp_obj_new_tuple(2, args);
-    #if MICROPY_PY_BUILTINS_FLOAT
-    } else if (MP_OBJ_IS_TYPE(o1_in, &mp_type_float) || MP_OBJ_IS_TYPE(o2_in, &mp_type_float)) {
-        mp_float_t f1 = mp_obj_get_float(o1_in);
-        mp_float_t f2 = mp_obj_get_float(o2_in);
-        if (f2 == 0.0) {
-            goto zero_division_error;
-        }
-        mp_obj_float_divmod(&f1, &f2);
-        mp_obj_t tuple[2] = {
-            mp_obj_new_float(f1),
-            mp_obj_new_float(f2),
-        };
-        return mp_obj_new_tuple(2, tuple);
-    #endif
-    } else {
-        if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
-            nlr_raise(mp_obj_new_exception_msg(&mp_type_TypeError,
-                "unsupported operand type(s) for divmod()"));
-        } else {
-            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
-                "unsupported operand type(s) for divmod(): '%s' and '%s'",
-                mp_obj_get_type_str(o1_in), mp_obj_get_type_str(o2_in)));
-        }
-    }
+    return mp_binary_op(MP_BINARY_OP_DIVMOD, o1_in, o2_in);
 }
 MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_divmod_obj, mp_builtin_divmod);
 
diff --git a/py/obj.h b/py/obj.h
index 7fa2dca098db343c77490307ef92226695ff6df0..7275886c76ddf7d4aeb0fb3845918cc871ba8bbc 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -575,7 +575,6 @@ typedef struct _mp_obj_float_t {
 } mp_obj_float_t;
 mp_float_t mp_obj_float_get(mp_obj_t self_in);
 mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs); // can return MP_OBJ_NULL if op not supported
-void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y);
 
 // complex
 void mp_obj_complex_get(mp_obj_t self_in, mp_float_t *real, mp_float_t *imag);
diff --git a/py/objfloat.c b/py/objfloat.c
index ffb3c7196486d3083f735b2cf85b1c964ffb2171..0af41990ff8af760206e1a0ddba3840ef5308cff 100644
--- a/py/objfloat.c
+++ b/py/objfloat.c
@@ -126,6 +126,41 @@ mp_float_t mp_obj_float_get(mp_obj_t self_in) {
     return self->value;
 }
 
+STATIC void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y) {
+    // logic here follows that of CPython
+    // https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
+    // x == (x//y)*y + (x%y)
+    // divmod(x, y) == (x//y, x%y)
+    mp_float_t mod = MICROPY_FLOAT_C_FUN(fmod)(*x, *y);
+    mp_float_t div = (*x - mod) / *y;
+
+    // Python specs require that mod has same sign as second operand
+    if (mod == 0.0) {
+        mod = MICROPY_FLOAT_C_FUN(copysign)(0.0, *y);
+    } else {
+        if ((mod < 0.0) != (*y < 0.0)) {
+            mod += *y;
+            div -= 1.0;
+        }
+    }
+
+    mp_float_t floordiv;
+    if (div == 0.0) {
+        // if division is zero, take the correct sign of zero
+        floordiv = MICROPY_FLOAT_C_FUN(copysign)(0.0, *x / *y);
+    } else {
+        // Python specs require that x == (x//y)*y + (x%y)
+        floordiv = MICROPY_FLOAT_C_FUN(floor)(div);
+        if (div - floordiv > 0.5) {
+            floordiv += 1.0;
+        }
+    }
+
+    // return results
+    *x = floordiv;
+    *y = mod;
+}
+
 mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs_in) {
     mp_float_t rhs_val = mp_obj_get_float(rhs_in); // can be any type, this function will convert to float (if possible)
     switch (op) {
@@ -170,6 +205,17 @@ mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs_i
             break;
         case MP_BINARY_OP_POWER:
         case MP_BINARY_OP_INPLACE_POWER: lhs_val = MICROPY_FLOAT_C_FUN(pow)(lhs_val, rhs_val); break;
+        case MP_BINARY_OP_DIVMOD: {
+            if (rhs_val == 0) {
+                goto zero_division_error;
+            }
+            mp_obj_float_divmod(&lhs_val, &rhs_val);
+            mp_obj_t tuple[2] = {
+                mp_obj_new_float(lhs_val),
+                mp_obj_new_float(rhs_val),
+            };
+            return mp_obj_new_tuple(2, tuple);
+        }
         case MP_BINARY_OP_LESS: return MP_BOOL(lhs_val < rhs_val);
         case MP_BINARY_OP_MORE: return MP_BOOL(lhs_val > rhs_val);
         case MP_BINARY_OP_EQUAL: return MP_BOOL(lhs_val == rhs_val);
@@ -182,39 +228,4 @@ mp_obj_t mp_obj_float_binary_op(mp_uint_t op, mp_float_t lhs_val, mp_obj_t rhs_i
     return mp_obj_new_float(lhs_val);
 }
 
-void mp_obj_float_divmod(mp_float_t *x, mp_float_t *y) {
-    // logic here follows that of CPython
-    // https://docs.python.org/3/reference/expressions.html#binary-arithmetic-operations
-    // x == (x//y)*y + (x%y)
-    // divmod(x, y) == (x//y, x%y)
-    mp_float_t mod = MICROPY_FLOAT_C_FUN(fmod)(*x, *y);
-    mp_float_t div = (*x - mod) / *y;
-
-    // Python specs require that mod has same sign as second operand
-    if (mod == 0.0) {
-        mod = MICROPY_FLOAT_C_FUN(copysign)(0.0, *y);
-    } else {
-        if ((mod < 0.0) != (*y < 0.0)) {
-            mod += *y;
-            div -= 1.0;
-        }
-    }
-
-    mp_float_t floordiv;
-    if (div == 0.0) {
-        // if division is zero, take the correct sign of zero
-        floordiv = MICROPY_FLOAT_C_FUN(copysign)(0.0, *x / *y);
-    } else {
-        // Python specs require that x == (x//y)*y + (x%y)
-        floordiv = MICROPY_FLOAT_C_FUN(floor)(div);
-        if (div - floordiv > 0.5) {
-            floordiv += 1.0;
-        }
-    }
-
-    // return results
-    *x = floordiv;
-    *y = mod;
-}
-
 #endif // MICROPY_PY_BUILTINS_FLOAT
diff --git a/py/objtype.c b/py/objtype.c
index f593271fb506b2ed17f9c4de4887d67d1cd17a20..06a067dcdc0ad2e20044eec82cc40466b265f0c0 100644
--- a/py/objtype.c
+++ b/py/objtype.c
@@ -400,6 +400,7 @@ const qstr mp_binary_op_method_name[] = {
     /*
     MP_BINARY_OP_MODULO,
     MP_BINARY_OP_POWER,
+    MP_BINARY_OP_DIVMOD,
     MP_BINARY_OP_INPLACE_OR,
     MP_BINARY_OP_INPLACE_XOR,
     MP_BINARY_OP_INPLACE_AND,
diff --git a/py/runtime.c b/py/runtime.c
index 886146fa87dd8114f2ce3620e23b84066b2dbc8e..501ca435a046ee41f65ef9ec5e3c44c41501d39e 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -441,6 +441,17 @@ mp_obj_t mp_binary_op(mp_uint_t op, mp_obj_t lhs, mp_obj_t rhs) {
                     lhs = mp_obj_new_int_from_ll(MP_OBJ_SMALL_INT_VALUE(lhs));
                     goto generic_binary_op;
 
+                case MP_BINARY_OP_DIVMOD: {
+                    if (rhs_val == 0) {
+                        goto zero_division;
+                    }
+                    // to reduce stack usage we don't pass a temp array of the 2 items
+                    mp_obj_tuple_t *tuple = mp_obj_new_tuple(2, NULL);
+                    tuple->items[0] = MP_OBJ_NEW_SMALL_INT(mp_small_int_floor_divide(lhs_val, rhs_val));
+                    tuple->items[1] = MP_OBJ_NEW_SMALL_INT(mp_small_int_modulo(lhs_val, rhs_val));
+                    return tuple;
+                }
+
                 case MP_BINARY_OP_LESS: return MP_BOOL(lhs_val < rhs_val); break;
                 case MP_BINARY_OP_MORE: return MP_BOOL(lhs_val > rhs_val); break;
                 case MP_BINARY_OP_LESS_EQUAL: return MP_BOOL(lhs_val <= rhs_val); break;
diff --git a/py/runtime0.h b/py/runtime0.h
index 65c7df0ae37e80c1119d5d0680c265d3d396e6f3..cdcb6e3dca2398b7080d54223fa150de6f7a3eb8 100644
--- a/py/runtime0.h
+++ b/py/runtime0.h
@@ -74,29 +74,30 @@ typedef enum {
 
     MP_BINARY_OP_MODULO,
     MP_BINARY_OP_POWER,
+    MP_BINARY_OP_DIVMOD, // not emitted by the compiler but supported by the runtime
     MP_BINARY_OP_INPLACE_OR,
     MP_BINARY_OP_INPLACE_XOR,
-    MP_BINARY_OP_INPLACE_AND,
 
+    MP_BINARY_OP_INPLACE_AND,
     MP_BINARY_OP_INPLACE_LSHIFT,
     MP_BINARY_OP_INPLACE_RSHIFT,
     MP_BINARY_OP_INPLACE_ADD,
     MP_BINARY_OP_INPLACE_SUBTRACT,
-    MP_BINARY_OP_INPLACE_MULTIPLY,
 
+    MP_BINARY_OP_INPLACE_MULTIPLY,
     MP_BINARY_OP_INPLACE_FLOOR_DIVIDE,
     MP_BINARY_OP_INPLACE_TRUE_DIVIDE,
     MP_BINARY_OP_INPLACE_MODULO,
     MP_BINARY_OP_INPLACE_POWER,
+
     // these should return a bool
     MP_BINARY_OP_LESS,
-
     MP_BINARY_OP_MORE,
     MP_BINARY_OP_EQUAL,
     MP_BINARY_OP_LESS_EQUAL,
     MP_BINARY_OP_MORE_EQUAL,
-    MP_BINARY_OP_NOT_EQUAL,
 
+    MP_BINARY_OP_NOT_EQUAL,
     MP_BINARY_OP_IN,
     MP_BINARY_OP_IS,
     MP_BINARY_OP_EXCEPTION_MATCH,
diff --git a/py/vm.c b/py/vm.c
index 5ae41806896d69117b1e2a8f17f3afbb215b1c71..d6ff15356e7c7210fde70e5c7cc75077c5b7d76c 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -1199,7 +1199,7 @@ yield:
                     } else if (ip[-1] < MP_BC_UNARY_OP_MULTI + 6) {
                         SET_TOP(mp_unary_op(ip[-1] - MP_BC_UNARY_OP_MULTI, TOP()));
                         DISPATCH();
-                    } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + 35) {
+                    } else if (ip[-1] < MP_BC_BINARY_OP_MULTI + 36) {
                         mp_obj_t rhs = POP();
                         mp_obj_t lhs = TOP();
                         SET_TOP(mp_binary_op(ip[-1] - MP_BC_BINARY_OP_MULTI, lhs, rhs));
diff --git a/py/vmentrytable.h b/py/vmentrytable.h
index 7c8f81bf30144ca0c0c0c70feee1207faec07c80..413914b6efb1d5be988d2bb1ad7e5f40031fc044 100644
--- a/py/vmentrytable.h
+++ b/py/vmentrytable.h
@@ -112,7 +112,7 @@ static void* entry_table[256] = {
     [MP_BC_LOAD_FAST_MULTI ... MP_BC_LOAD_FAST_MULTI + 15] = &&entry_MP_BC_LOAD_FAST_MULTI,
     [MP_BC_STORE_FAST_MULTI ... MP_BC_STORE_FAST_MULTI + 15] = &&entry_MP_BC_STORE_FAST_MULTI,
     [MP_BC_UNARY_OP_MULTI ... MP_BC_UNARY_OP_MULTI + 5] = &&entry_MP_BC_UNARY_OP_MULTI,
-    [MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + 34] = &&entry_MP_BC_BINARY_OP_MULTI,
+    [MP_BC_BINARY_OP_MULTI ... MP_BC_BINARY_OP_MULTI + 35] = &&entry_MP_BC_BINARY_OP_MULTI,
 };
 
 #if __clang__
diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp
index 6231d3fe9684567407b63d6a0ce23545ee0614a0..fc3d85f9daf5fa58465166b646259330712b9257 100644
--- a/tests/cmdline/cmd_showbc.py.exp
+++ b/tests/cmdline/cmd_showbc.py.exp
@@ -93,21 +93,21 @@ arg names:
 69 LOAD_DEREF 14
 71 DUP_TOP
 72 ROT_THREE
-73 BINARY_OP 26 __eq__
+73 BINARY_OP 27 __eq__
 74 JUMP_IF_FALSE_OR_POP 82
 77 LOAD_FAST 1
-78 BINARY_OP 26 __eq__
+78 BINARY_OP 27 __eq__
 79 JUMP 84
 82 ROT_TWO
 83 POP_TOP
 84 STORE_FAST 10
 85 LOAD_FAST 0
 86 LOAD_DEREF 14
-88 BINARY_OP 26 __eq__
+88 BINARY_OP 27 __eq__
 89 JUMP_IF_FALSE_OR_POP 96
 92 LOAD_DEREF 14
 94 LOAD_FAST 1
-95 BINARY_OP 26 __eq__
+95 BINARY_OP 27 __eq__
 96 UNARY_OP 0
 97 NOT
 98 STORE_FAST 10