diff --git a/py/builtin.c b/py/builtin.c
index 5e201545201d35ba1727d322db01799f379c39b8..217dcc186df9a0acf0fdc91d6856cd2022e78219 100644
--- a/py/builtin.c
+++ b/py/builtin.c
@@ -362,17 +362,6 @@ STATIC mp_obj_t mp_builtin_print(uint n_args, const mp_obj_t *args, mp_map_t *kw
 
 MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_print_obj, 0, mp_builtin_print);
 
-STATIC mp_obj_t mp_builtin_range(uint n_args, const mp_obj_t *args) {
-    assert(1 <= n_args && n_args <= 3);
-    switch (n_args) {
-        case 1: return mp_obj_new_range(0, mp_obj_get_int(args[0]), 1);
-        case 2: return mp_obj_new_range(mp_obj_get_int(args[0]), mp_obj_get_int(args[1]), 1);
-        default: return mp_obj_new_range(mp_obj_get_int(args[0]), mp_obj_get_int(args[1]), mp_obj_get_int(args[2]));
-    }
-}
-
-MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_range_obj, 1, 3, mp_builtin_range);
-
 STATIC mp_obj_t mp_builtin_repr(mp_obj_t o_in) {
     vstr_t *vstr = vstr_new();
     mp_obj_print_helper((void (*)(void *env, const char *fmt, ...))vstr_printf, vstr, o_in, PRINT_REPR);
diff --git a/py/builtin.h b/py/builtin.h
index 9b2b9c9424b93ee19003e00bcf80d07e6db23ed7..5f5c888f8acf0637e923eb64c3bb797a42681d8d 100644
--- a/py/builtin.h
+++ b/py/builtin.h
@@ -31,7 +31,6 @@ MP_DECLARE_CONST_FUN_OBJ(mp_builtin_oct_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_ord_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_pow_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_print_obj);
-MP_DECLARE_CONST_FUN_OBJ(mp_builtin_range_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_repr_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_sorted_obj);
 MP_DECLARE_CONST_FUN_OBJ(mp_builtin_sum_obj);
diff --git a/py/builtintables.c b/py/builtintables.c
index c4c96ed35a9f5b18c0601b5bd861e9a122f28572..940647d25f634d817cf77c636475b1ddbd2dfc68 100644
--- a/py/builtintables.c
+++ b/py/builtintables.c
@@ -34,6 +34,7 @@ STATIC const mp_map_elem_t mp_builtin_object_table[] = {
 #if MICROPY_ENABLE_PROPERTY
     { MP_OBJ_NEW_QSTR(MP_QSTR_property), (mp_obj_t)&mp_type_property },
 #endif
+    { MP_OBJ_NEW_QSTR(MP_QSTR_range), (mp_obj_t)&mp_type_range },
     { MP_OBJ_NEW_QSTR(MP_QSTR_set), (mp_obj_t)&mp_type_set },
     { MP_OBJ_NEW_QSTR(MP_QSTR_str), (mp_obj_t)&mp_type_str },
     { MP_OBJ_NEW_QSTR(MP_QSTR_super), (mp_obj_t)&mp_type_super },
@@ -75,7 +76,6 @@ STATIC const mp_map_elem_t mp_builtin_object_table[] = {
     { MP_OBJ_NEW_QSTR(MP_QSTR_ord), (mp_obj_t)&mp_builtin_ord_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_pow), (mp_obj_t)&mp_builtin_pow_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_print), (mp_obj_t)&mp_builtin_print_obj },
-    { MP_OBJ_NEW_QSTR(MP_QSTR_range), (mp_obj_t)&mp_builtin_range_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_repr), (mp_obj_t)&mp_builtin_repr_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_sorted), (mp_obj_t)&mp_builtin_sorted_obj },
     { MP_OBJ_NEW_QSTR(MP_QSTR_sum), (mp_obj_t)&mp_builtin_sum_obj },
diff --git a/py/obj.h b/py/obj.h
index 9efec60de04e405f1859d50d5a25547a3769b153..7e1b7e598adc1e290c4548ef42373b26c4c930f7 100644
--- a/py/obj.h
+++ b/py/obj.h
@@ -272,6 +272,7 @@ extern const mp_obj_type_t mp_type_map; // map (the python builtin, not the dict
 extern const mp_obj_type_t mp_type_enumerate;
 extern const mp_obj_type_t mp_type_filter;
 extern const mp_obj_type_t mp_type_dict;
+extern const mp_obj_type_t mp_type_range;
 extern const mp_obj_type_t mp_type_set;
 extern const mp_obj_type_t mp_type_slice;
 extern const mp_obj_type_t mp_type_zip;
@@ -346,8 +347,6 @@ mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type);
 mp_obj_t mp_obj_new_exception_args(const mp_obj_type_t *exc_type, uint n_args, const mp_obj_t *args);
 mp_obj_t mp_obj_new_exception_msg(const mp_obj_type_t *exc_type, const char *msg);
 mp_obj_t mp_obj_new_exception_msg_varg(const mp_obj_type_t *exc_type, const char *fmt, ...); // counts args by number of % symbols in fmt, excluding %%; can only handle void* sizes (ie no float/double!)
-mp_obj_t mp_obj_new_range(int start, int stop, int step);
-mp_obj_t mp_obj_new_range_iterator(int cur, int stop, int step);
 mp_obj_t mp_obj_new_fun_bc(uint scope_flags, qstr *args, uint n_args, mp_obj_t def_args, const byte *code);
 mp_obj_t mp_obj_new_fun_asm(uint n_args, void *fun);
 mp_obj_t mp_obj_new_gen_wrap(mp_obj_t fun);
diff --git a/py/objrange.c b/py/objrange.c
index 64d92457b74bf3443faf6abb8ed401905aafda08..45f83580d1e9a73b321a08b2f9c4b92d5f64bdcb 100644
--- a/py/objrange.c
+++ b/py/objrange.c
@@ -5,38 +5,7 @@
 #include "mpconfig.h"
 #include "qstr.h"
 #include "obj.h"
-
-/******************************************************************************/
-/* range                                                                      */
-
-typedef struct _mp_obj_range_t {
-    mp_obj_base_t base;
-    // TODO make these values generic objects or something
-    machine_int_t start;
-    machine_int_t stop;
-    machine_int_t step;
-} mp_obj_range_t;
-
-STATIC mp_obj_t range_getiter(mp_obj_t o_in) {
-    mp_obj_range_t *o = o_in;
-    return mp_obj_new_range_iterator(o->start, o->stop, o->step);
-}
-
-STATIC const mp_obj_type_t range_type = {
-    { &mp_type_type} ,
-    .name = MP_QSTR_range,
-    .getiter = range_getiter,
-};
-
-// range is a class and instances are immutable sequence objects
-mp_obj_t mp_obj_new_range(int start, int stop, int step) {
-    mp_obj_range_t *o = m_new_obj(mp_obj_range_t);
-    o->base.type = &range_type;
-    o->start = start;
-    o->stop = stop;
-    o->step = step;
-    return o;
-}
+#include "runtime.h"
 
 /******************************************************************************/
 /* range iterator                                                             */
@@ -75,3 +44,47 @@ mp_obj_t mp_obj_new_range_iterator(int cur, int stop, int step) {
     o->step = step;
     return o;
 }
+
+/******************************************************************************/
+/* range                                                                      */
+
+typedef struct _mp_obj_range_t {
+    mp_obj_base_t base;
+    // TODO make these values generic objects or something
+    machine_int_t start;
+    machine_int_t stop;
+    machine_int_t step;
+} mp_obj_range_t;
+
+STATIC mp_obj_t range_make_new(mp_obj_t type_in, uint n_args, uint n_kw, const mp_obj_t *args) {
+    mp_check_nargs(n_args, 1, 3, n_kw, false);
+
+    mp_obj_range_t *o = m_new_obj(mp_obj_range_t);
+    o->base.type = &mp_type_range;
+    o->start = 0;
+    o->step = 1;
+
+    if (n_args == 1) {
+        o->stop = mp_obj_get_int(args[0]);
+    } else {
+        o->start = mp_obj_get_int(args[0]);
+        o->stop = mp_obj_get_int(args[1]);
+        if (n_args == 3) {
+            o->step = mp_obj_get_int(args[2]);
+        }
+    }
+
+    return o;
+}
+
+STATIC mp_obj_t range_getiter(mp_obj_t o_in) {
+    mp_obj_range_t *o = o_in;
+    return mp_obj_new_range_iterator(o->start, o->stop, o->step);
+}
+
+const mp_obj_type_t mp_type_range = {
+    { &mp_type_type },
+    .name = MP_QSTR_range,
+    .make_new = range_make_new,
+    .getiter = range_getiter,
+};