From 612599587bef71e1deca9a77a8e2e16e69b045de Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Mon, 28 Nov 2016 09:24:50 +1100
Subject: [PATCH] py: Factor out common code from assemblers into asmbase.[ch].

All assemblers should "derive" from mp_asm_base_t.
---
 py/asmarm.c          |  99 --------------------------------
 py/asmarm.h          |  20 ++-----
 py/asmbase.c         | 104 +++++++++++++++++++++++++++++++++
 py/asmbase.h         |  68 ++++++++++++++++++++++
 py/asmthumb.c        | 134 ++++++-------------------------------------
 py/asmthumb.h        |  21 ++-----
 py/asmx64.c          | 114 +++---------------------------------
 py/asmx64.h          |  24 +++-----
 py/asmx86.c          | 102 --------------------------------
 py/asmx86.h          |  24 +++-----
 py/emitinlinethumb.c |  18 +++---
 py/emitnative.c      |  98 ++++++++-----------------------
 py/py.mk             |   1 +
 13 files changed, 262 insertions(+), 565 deletions(-)
 create mode 100644 py/asmbase.c
 create mode 100644 py/asmbase.h

diff --git a/py/asmarm.c b/py/asmarm.c
index 2c389ac8c..663f86156 100644
--- a/py/asmarm.c
+++ b/py/asmarm.c
@@ -38,50 +38,6 @@
 
 #define SIGNED_FIT24(x) (((x) & 0xff800000) == 0) || (((x) & 0xff000000) == 0xff000000)
 
-struct _asm_arm_t {
-    uint pass;
-    mp_uint_t code_offset;
-    mp_uint_t code_size;
-    byte *code_base;
-    byte dummy_data[4];
-
-    mp_uint_t max_num_labels;
-    mp_uint_t *label_offsets;
-    uint push_reglist;
-    uint stack_adjust;
-};
-
-asm_arm_t *asm_arm_new(uint max_num_labels) {
-    asm_arm_t *as;
-
-    as = m_new0(asm_arm_t, 1);
-    as->max_num_labels = max_num_labels;
-    as->label_offsets = m_new(mp_uint_t, max_num_labels);
-
-    return as;
-}
-
-void asm_arm_free(asm_arm_t *as, bool free_code) {
-    if (free_code) {
-        MP_PLAT_FREE_EXEC(as->code_base, as->code_size);
-    }
-    m_del(mp_uint_t, as->label_offsets, as->max_num_labels);
-    m_del_obj(asm_arm_t, as);
-}
-
-void asm_arm_start_pass(asm_arm_t *as, uint pass) {
-    if (pass == ASM_ARM_PASS_COMPUTE) {
-        memset(as->label_offsets, -1, as->max_num_labels * sizeof(mp_uint_t));
-    } else if (pass == ASM_ARM_PASS_EMIT) {
-        MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size);
-        if (as->code_base == NULL) {
-            assert(0);
-        }
-    }
-    as->pass = pass;
-    as->code_offset = 0;
-}
-
 void asm_arm_end_pass(asm_arm_t *as) {
     if (as->pass == ASM_ARM_PASS_EMIT) {
 #ifdef __arm__
@@ -97,32 +53,6 @@ void asm_arm_end_pass(asm_arm_t *as) {
     }
 }
 
-// all functions must go through this one to emit bytes
-// if as->pass < ASM_ARM_PASS_EMIT, then this function only returns a buffer of 4 bytes length
-STATIC byte *asm_arm_get_cur_to_write_bytes(asm_arm_t *as, int num_bytes_to_write) {
-    if (as->pass < ASM_ARM_PASS_EMIT) {
-        as->code_offset += num_bytes_to_write;
-        return as->dummy_data;
-    } else {
-        assert(as->code_offset + num_bytes_to_write <= as->code_size);
-        byte *c = as->code_base + as->code_offset;
-        as->code_offset += num_bytes_to_write;
-        return c;
-    }
-}
-
-uint asm_arm_get_code_pos(asm_arm_t *as) {
-    return as->code_offset;
-}
-
-uint asm_arm_get_code_size(asm_arm_t *as) {
-    return as->code_size;
-}
-
-void *asm_arm_get_code(asm_arm_t *as) {
-    return as->code_base;
-}
-
 // Insert word into instruction flow
 STATIC void emit(asm_arm_t *as, uint op) {
     *(uint*)asm_arm_get_cur_to_write_bytes(as, 4) = op;
@@ -263,35 +193,6 @@ void asm_arm_pop(asm_arm_t *as, uint reglist) {
     emit_al(as, asm_arm_op_pop(reglist));
 }
 
-void asm_arm_label_assign(asm_arm_t *as, uint label) {
-    assert(label < as->max_num_labels);
-    if (as->pass < ASM_ARM_PASS_EMIT) {
-        // assign label offset
-        assert(as->label_offsets[label] == -1);
-        as->label_offsets[label] = as->code_offset;
-    } else {
-        // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT
-        assert(as->label_offsets[label] == as->code_offset);
-    }
-}
-
-void asm_arm_align(asm_arm_t* as, uint align) {
-    // TODO fill unused data with NOPs?
-    as->code_offset = (as->code_offset + align - 1) & (~(align - 1));
-}
-
-void asm_arm_data(asm_arm_t* as, uint bytesize, uint val) {
-    byte *c = asm_arm_get_cur_to_write_bytes(as, bytesize);
-    // only write to the buffer in the emit pass (otherwise we overflow dummy_data)
-    if (as->pass == ASM_ARM_PASS_EMIT) {
-        // little endian
-        for (uint i = 0; i < bytesize; i++) {
-            *c++ = val;
-            val >>= 8;
-        }
-    }
-}
-
 void asm_arm_mov_reg_reg(asm_arm_t *as, uint reg_dest, uint reg_src) {
     emit_al(as, asm_arm_op_mov_reg(reg_dest, reg_src));
 }
diff --git a/py/asmarm.h b/py/asmarm.h
index 15e7a047b..263721057 100644
--- a/py/asmarm.h
+++ b/py/asmarm.h
@@ -28,9 +28,7 @@
 #define __MICROPY_INCLUDED_PY_ASMARM_H__
 
 #include "py/misc.h"
-
-#define ASM_ARM_PASS_COMPUTE (1)
-#define ASM_ARM_PASS_EMIT    (2)
+#include "py/asmbase.h"
 
 #define ASM_ARM_REG_R0  (0)
 #define ASM_ARM_REG_R1  (1)
@@ -68,22 +66,16 @@
 #define ASM_ARM_CC_LE (0xd << 28)
 #define ASM_ARM_CC_AL (0xe << 28)
 
-typedef struct _asm_arm_t asm_arm_t;
+typedef struct _asm_arm_t {
+    mp_asm_base_t base;
+    uint push_reglist;
+    uint stack_adjust;
+} asm_arm_t;
 
-asm_arm_t *asm_arm_new(uint max_num_labels);
-void asm_arm_free(asm_arm_t *as, bool free_code);
-void asm_arm_start_pass(asm_arm_t *as, uint pass);
 void asm_arm_end_pass(asm_arm_t *as);
-uint asm_arm_get_code_pos(asm_arm_t *as);
-uint asm_arm_get_code_size(asm_arm_t *as);
-void *asm_arm_get_code(asm_arm_t *as);
 
 void asm_arm_entry(asm_arm_t *as, int num_locals);
 void asm_arm_exit(asm_arm_t *as);
-void asm_arm_label_assign(asm_arm_t *as, uint label);
-
-void asm_arm_align(asm_arm_t* as, uint align);
-void asm_arm_data(asm_arm_t* as, uint bytesize, uint val);
 
 void asm_arm_bkpt(asm_arm_t *as);
 
diff --git a/py/asmbase.c b/py/asmbase.c
new file mode 100644
index 000000000..916f7c5ec
--- /dev/null
+++ b/py/asmbase.c
@@ -0,0 +1,104 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "py/obj.h"
+#include "py/misc.h"
+#include "py/asmbase.h"
+
+#if MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB
+
+void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels) {
+    as->max_num_labels = max_num_labels;
+    as->label_offsets = m_new(size_t, max_num_labels);
+}
+
+void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code) {
+    if (free_code) {
+        MP_PLAT_FREE_EXEC(as->code_base, as->code_size);
+    }
+    m_del(size_t, as->label_offsets, as->max_num_labels);
+}
+
+void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) {
+    if (pass == MP_ASM_PASS_COMPUTE) {
+        // reset all labels
+        memset(as->label_offsets, -1, as->max_num_labels * sizeof(size_t));
+    } else if (pass == MP_ASM_PASS_EMIT) {
+        // allocating executable RAM is platform specific
+        MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size);
+        assert(as->code_base != NULL);
+    }
+    as->pass = pass;
+    as->code_offset = 0;
+}
+
+// all functions must go through this one to emit bytes
+// if as->pass < MP_ASM_PASS_EMIT, then this function returns dummy_data
+uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write) {
+    if (as->pass < MP_ASM_PASS_EMIT) {
+        as->code_offset += num_bytes_to_write;
+        return as->dummy_data;
+    } else {
+        assert(as->code_offset + num_bytes_to_write <= as->code_size);
+        uint8_t *c = as->code_base + as->code_offset;
+        as->code_offset += num_bytes_to_write;
+        return c;
+    }
+}
+
+void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label) {
+    assert(label < as->max_num_labels);
+    if (as->pass < MP_ASM_PASS_EMIT) {
+        // assign label offset
+        assert(as->label_offsets[label] == (size_t)-1);
+        as->label_offsets[label] = as->code_offset;
+    } else {
+        // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT
+        assert(as->label_offsets[label] == as->code_offset);
+    }
+}
+
+// align must be a multiple of 2
+void mp_asm_base_align(mp_asm_base_t* as, unsigned int align) {
+    as->code_offset = (as->code_offset + align - 1) & (~(align - 1));
+}
+
+// this function assumes a little endian machine
+void mp_asm_base_data(mp_asm_base_t* as, unsigned int bytesize, uintptr_t val) {
+    uint8_t *c = mp_asm_base_get_cur_to_write_bytes(as, bytesize);
+    // only write to the buffer in the emit pass (otherwise we may overflow dummy_data)
+    if (as->pass == MP_ASM_PASS_EMIT) {
+        for (unsigned int i = 0; i < bytesize; i++) {
+            *c++ = val;
+            val >>= 8;
+        }
+    }
+}
+
+#endif // MICROPY_EMIT_NATIVE || MICROPY_EMIT_INLINE_THUMB
diff --git a/py/asmbase.h b/py/asmbase.h
new file mode 100644
index 000000000..bfc8ac7ff
--- /dev/null
+++ b/py/asmbase.h
@@ -0,0 +1,68 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#ifndef MICROPY_INCLUDED_PY_ASMBASE_H
+#define MICROPY_INCLUDED_PY_ASMBASE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#define MP_ASM_PASS_COMPUTE (1)
+#define MP_ASM_PASS_EMIT    (2)
+
+typedef struct _mp_asm_base_t {
+    int pass;
+    size_t code_offset;
+    size_t code_size;
+    uint8_t *code_base;
+
+    size_t max_num_labels;
+    size_t *label_offsets;
+
+    // must be last in struct
+    uint8_t dummy_data[4];
+} mp_asm_base_t;
+
+void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels);
+void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code);
+void mp_asm_base_start_pass(mp_asm_base_t *as, int pass);
+uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write);
+void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label);
+void mp_asm_base_align(mp_asm_base_t* as, unsigned int align);
+void mp_asm_base_data(mp_asm_base_t* as, unsigned int bytesize, uintptr_t val);
+
+static inline size_t mp_asm_base_get_code_pos(mp_asm_base_t *as) {
+    return as->code_offset;
+}
+
+static inline size_t mp_asm_base_get_code_size(mp_asm_base_t *as) {
+    return as->code_size;
+}
+
+static inline void *mp_asm_base_get_code(mp_asm_base_t *as) {
+    return as->code_base;
+}
+
+#endif // MICROPY_INCLUDED_PY_ASMBASE_H
diff --git a/py/asmthumb.c b/py/asmthumb.c
index 1aae3d38e..705c15a18 100644
--- a/py/asmthumb.c
+++ b/py/asmthumb.c
@@ -42,49 +42,8 @@
 #define SIGNED_FIT12(x) (((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800)
 #define SIGNED_FIT23(x) (((x) & 0xffc00000) == 0) || (((x) & 0xffc00000) == 0xffc00000)
 
-struct _asm_thumb_t {
-    mp_uint_t pass;
-    mp_uint_t code_offset;
-    mp_uint_t code_size;
-    byte *code_base;
-    byte dummy_data[4];
-
-    mp_uint_t max_num_labels;
-    mp_uint_t *label_offsets;
-    mp_uint_t push_reglist;
-    mp_uint_t stack_adjust;
-};
-
-asm_thumb_t *asm_thumb_new(uint max_num_labels) {
-    asm_thumb_t *as;
-
-    as = m_new0(asm_thumb_t, 1);
-    as->max_num_labels = max_num_labels;
-    as->label_offsets = m_new(mp_uint_t, max_num_labels);
-
-    return as;
-}
-
-void asm_thumb_free(asm_thumb_t *as, bool free_code) {
-    if (free_code) {
-        MP_PLAT_FREE_EXEC(as->code_base, as->code_size);
-    }
-    m_del(mp_uint_t, as->label_offsets, as->max_num_labels);
-    m_del_obj(asm_thumb_t, as);
-}
-
-void asm_thumb_start_pass(asm_thumb_t *as, uint pass) {
-    if (pass == ASM_THUMB_PASS_COMPUTE) {
-        memset(as->label_offsets, -1, as->max_num_labels * sizeof(mp_uint_t));
-    } else if (pass == ASM_THUMB_PASS_EMIT) {
-        MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size);
-        if (as->code_base == NULL) {
-            assert(0);
-        }
-        //printf("code_size: %u\n", as->code_size);
-    }
-    as->pass = pass;
-    as->code_offset = 0;
+static inline byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int n) {
+    return mp_asm_base_get_cur_to_write_bytes(&as->base, n);
 }
 
 void asm_thumb_end_pass(asm_thumb_t *as) {
@@ -92,7 +51,7 @@ void asm_thumb_end_pass(asm_thumb_t *as) {
     // could check labels are resolved...
 
     #if defined(MCU_SERIES_F7)
-    if (as->pass == ASM_THUMB_PASS_EMIT) {
+    if (as->base.pass == MP_ASM_PASS_EMIT) {
         // flush D-cache, so the code emited is stored in memory
         SCB_CleanDCache_by_Addr((uint32_t*)as->code_base, as->code_size);
         // invalidate I-cache
@@ -101,33 +60,6 @@ void asm_thumb_end_pass(asm_thumb_t *as) {
     #endif
 }
 
-// all functions must go through this one to emit bytes
-// if as->pass < ASM_THUMB_PASS_EMIT, then this function only returns a buffer of 4 bytes length
-STATIC byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int num_bytes_to_write) {
-    //printf("emit %d\n", num_bytes_to_write);
-    if (as->pass < ASM_THUMB_PASS_EMIT) {
-        as->code_offset += num_bytes_to_write;
-        return as->dummy_data;
-    } else {
-        assert(as->code_offset + num_bytes_to_write <= as->code_size);
-        byte *c = as->code_base + as->code_offset;
-        as->code_offset += num_bytes_to_write;
-        return c;
-    }
-}
-
-uint asm_thumb_get_code_pos(asm_thumb_t *as) {
-    return as->code_offset;
-}
-
-uint asm_thumb_get_code_size(asm_thumb_t *as) {
-    return as->code_size;
-}
-
-void *asm_thumb_get_code(asm_thumb_t *as) {
-    return as->code_base;
-}
-
 /*
 STATIC void asm_thumb_write_byte_1(asm_thumb_t *as, byte b1) {
     byte *c = asm_thumb_get_cur_to_write_bytes(as, 1);
@@ -223,39 +155,9 @@ void asm_thumb_exit(asm_thumb_t *as) {
     asm_thumb_op16(as, OP_POP_RLIST_PC(as->push_reglist));
 }
 
-void asm_thumb_label_assign(asm_thumb_t *as, uint label) {
-    assert(label < as->max_num_labels);
-    if (as->pass < ASM_THUMB_PASS_EMIT) {
-        // assign label offset
-        assert(as->label_offsets[label] == -1);
-        as->label_offsets[label] = as->code_offset;
-    } else {
-        // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT
-        //printf("l%d: (at %d=%ld)\n", label, as->label_offsets[label], as->code_offset);
-        assert(as->label_offsets[label] == as->code_offset);
-    }
-}
-
-void asm_thumb_align(asm_thumb_t* as, uint align) {
-    // TODO fill unused data with NOPs?
-    as->code_offset = (as->code_offset + align - 1) & (~(align - 1));
-}
-
-void asm_thumb_data(asm_thumb_t* as, uint bytesize, uint val) {
-    byte *c = asm_thumb_get_cur_to_write_bytes(as, bytesize);
-    // only write to the buffer in the emit pass (otherwise we overflow dummy_data)
-    if (as->pass == ASM_THUMB_PASS_EMIT) {
-        // little endian
-        for (uint i = 0; i < bytesize; i++) {
-            *c++ = val;
-            val >>= 8;
-        }
-    }
-}
-
 STATIC mp_uint_t get_label_dest(asm_thumb_t *as, uint label) {
-    assert(label < as->max_num_labels);
-    return as->label_offsets[label];
+    assert(label < as->base.max_num_labels);
+    return as->base.label_offsets[label];
 }
 
 void asm_thumb_op16(asm_thumb_t *as, uint op) {
@@ -309,10 +211,10 @@ void asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_
 
 bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) {
     mp_uint_t dest = get_label_dest(as, label);
-    mp_int_t rel = dest - as->code_offset;
+    mp_int_t rel = dest - as->base.code_offset;
     rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
     asm_thumb_op16(as, OP_B_N(rel));
-    return as->pass != ASM_THUMB_PASS_EMIT || SIGNED_FIT12(rel);
+    return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT12(rel);
 }
 
 #define OP_BCC_N(cond, byte_offset) (0xd000 | ((cond) << 8) | (((byte_offset) >> 1) & 0x00ff))
@@ -323,11 +225,11 @@ bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) {
 
 bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) {
     mp_uint_t dest = get_label_dest(as, label);
-    mp_int_t rel = dest - as->code_offset;
+    mp_int_t rel = dest - as->base.code_offset;
     rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
     if (!wide) {
         asm_thumb_op16(as, OP_BCC_N(cond, rel));
-        return as->pass != ASM_THUMB_PASS_EMIT || SIGNED_FIT9(rel);
+        return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT9(rel);
     } else {
         asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel));
         return true;
@@ -339,10 +241,10 @@ bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) {
 
 bool asm_thumb_bl_label(asm_thumb_t *as, uint label) {
     mp_uint_t dest = get_label_dest(as, label);
-    mp_int_t rel = dest - as->code_offset;
+    mp_int_t rel = dest - as->base.code_offset;
     rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
     asm_thumb_op32(as, OP_BL_HI(rel), OP_BL_LO(rel));
-    return as->pass != ASM_THUMB_PASS_EMIT || SIGNED_FIT23(rel);
+    return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT23(rel);
 }
 
 void asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32) {
@@ -367,13 +269,13 @@ void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32) {
 // TODO this is very inefficient, improve it!
 void asm_thumb_mov_reg_i32_aligned(asm_thumb_t *as, uint reg_dest, int i32) {
     // align on machine-word + 2
-    if ((as->code_offset & 3) == 0) {
+    if ((as->base.code_offset & 3) == 0) {
         asm_thumb_op16(as, ASM_THUMB_OP_NOP);
     }
     // jump over the i32 value (instruction prefetch adds 2 to PC)
     asm_thumb_op16(as, OP_B_N(2));
     // store i32 on machine-word aligned boundary
-    asm_thumb_data(as, 4, i32);
+    mp_asm_base_data(&as->base, 4, i32);
     // do the actual load of the i32 value
     asm_thumb_mov_reg_i32_optimised(as, reg_dest, i32);
 }
@@ -384,14 +286,14 @@ void asm_thumb_mov_reg_i32_aligned(asm_thumb_t *as, uint reg_dest, int i32) {
 void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num, uint rlo_src) {
     assert(rlo_src < ASM_THUMB_REG_R8);
     int word_offset = local_num;
-    assert(as->pass < ASM_THUMB_PASS_EMIT || word_offset >= 0);
+    assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0);
     asm_thumb_op16(as, OP_STR_TO_SP_OFFSET(rlo_src, word_offset));
 }
 
 void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num) {
     assert(rlo_dest < ASM_THUMB_REG_R8);
     int word_offset = local_num;
-    assert(as->pass < ASM_THUMB_PASS_EMIT || word_offset >= 0);
+    assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0);
     asm_thumb_op16(as, OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset));
 }
 
@@ -400,7 +302,7 @@ void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num) {
 void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num) {
     assert(rlo_dest < ASM_THUMB_REG_R8);
     int word_offset = local_num;
-    assert(as->pass < ASM_THUMB_PASS_EMIT || word_offset >= 0);
+    assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0);
     asm_thumb_op16(as, OP_ADD_REG_SP_OFFSET(rlo_dest, word_offset));
 }
 
@@ -410,7 +312,7 @@ void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num)
 
 void asm_thumb_b_label(asm_thumb_t *as, uint label) {
     mp_uint_t dest = get_label_dest(as, label);
-    mp_int_t rel = dest - as->code_offset;
+    mp_int_t rel = dest - as->base.code_offset;
     rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
     if (dest != (mp_uint_t)-1 && rel <= -4) {
         // is a backwards jump, so we know the size of the jump on the first pass
@@ -429,7 +331,7 @@ void asm_thumb_b_label(asm_thumb_t *as, uint label) {
 
 void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) {
     mp_uint_t dest = get_label_dest(as, label);
-    mp_int_t rel = dest - as->code_offset;
+    mp_int_t rel = dest - as->base.code_offset;
     rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
     if (dest != (mp_uint_t)-1 && rel <= -4) {
         // is a backwards jump, so we know the size of the jump on the first pass
diff --git a/py/asmthumb.h b/py/asmthumb.h
index 43d6c4286..c03b00da3 100644
--- a/py/asmthumb.h
+++ b/py/asmthumb.h
@@ -27,9 +27,7 @@
 #define __MICROPY_INCLUDED_PY_ASMTHUMB_H__
 
 #include "py/misc.h"
-
-#define ASM_THUMB_PASS_COMPUTE (1)
-#define ASM_THUMB_PASS_EMIT    (2)
+#include "py/asmbase.h"
 
 #define ASM_THUMB_REG_R0  (0)
 #define ASM_THUMB_REG_R1  (1)
@@ -64,24 +62,17 @@
 #define ASM_THUMB_CC_GT (0xc)
 #define ASM_THUMB_CC_LE (0xd)
 
-typedef struct _asm_thumb_t asm_thumb_t;
+typedef struct _asm_thumb_t {
+    mp_asm_base_t base;
+    uint32_t push_reglist;
+    uint32_t stack_adjust;
+} asm_thumb_t;
 
-asm_thumb_t *asm_thumb_new(uint max_num_labels);
-void asm_thumb_free(asm_thumb_t *as, bool free_code);
-void asm_thumb_start_pass(asm_thumb_t *as, uint pass);
 void asm_thumb_end_pass(asm_thumb_t *as);
-uint asm_thumb_get_code_pos(asm_thumb_t *as);
-uint asm_thumb_get_code_size(asm_thumb_t *as);
-void *asm_thumb_get_code(asm_thumb_t *as);
 
 void asm_thumb_entry(asm_thumb_t *as, int num_locals);
 void asm_thumb_exit(asm_thumb_t *as);
 
-void asm_thumb_label_assign(asm_thumb_t *as, uint label);
-
-void asm_thumb_align(asm_thumb_t* as, uint align);
-void asm_thumb_data(asm_thumb_t* as, uint bytesize, uint val);
-
 // argument order follows ARM, in general dest is first
 // note there is a difference between movw and mov.w, and many others!
 
diff --git a/py/asmx64.c b/py/asmx64.c
index 5e23a594e..c9dad2a66 100644
--- a/py/asmx64.c
+++ b/py/asmx64.c
@@ -116,80 +116,8 @@
 #define UNSIGNED_FIT32(x) (((x) & 0xffffffff00000000) == 0)
 #define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)
 
-struct _asm_x64_t {
-    uint pass;
-    mp_uint_t code_offset;
-    mp_uint_t code_size;
-    byte *code_base;
-    byte dummy_data[8];
-
-    mp_uint_t max_num_labels;
-    mp_uint_t *label_offsets;
-    int num_locals;
-};
-
-asm_x64_t *asm_x64_new(mp_uint_t max_num_labels) {
-    asm_x64_t *as;
-
-    as = m_new0(asm_x64_t, 1);
-    as->max_num_labels = max_num_labels;
-    as->label_offsets = m_new(mp_uint_t, max_num_labels);
-
-    return as;
-}
-
-void asm_x64_free(asm_x64_t *as, bool free_code) {
-    if (free_code) {
-        MP_PLAT_FREE_EXEC(as->code_base, as->code_size);
-    }
-    m_del(mp_uint_t, as->label_offsets, as->max_num_labels);
-    m_del_obj(asm_x64_t, as);
-}
-
-void asm_x64_start_pass(asm_x64_t *as, uint pass) {
-    if (pass == ASM_X64_PASS_COMPUTE) {
-        // reset all labels
-        memset(as->label_offsets, -1, as->max_num_labels * sizeof(mp_uint_t));
-    } if (pass == ASM_X64_PASS_EMIT) {
-        MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size);
-        if (as->code_base == NULL) {
-            assert(0);
-        }
-        //printf("code_size: %u\n", as->code_size);
-    }
-    as->pass = pass;
-    as->code_offset = 0;
-}
-
-void asm_x64_end_pass(asm_x64_t *as) {
-    // could check labels are resolved...
-    (void)as;
-}
-
-// all functions must go through this one to emit bytes
-STATIC byte *asm_x64_get_cur_to_write_bytes(asm_x64_t *as, int num_bytes_to_write) {
-    //printf("emit %d\n", num_bytes_to_write);
-    if (as->pass < ASM_X64_PASS_EMIT) {
-        as->code_offset += num_bytes_to_write;
-        return as->dummy_data;
-    } else {
-        assert(as->code_offset + num_bytes_to_write <= as->code_size);
-        byte *c = as->code_base + as->code_offset;
-        as->code_offset += num_bytes_to_write;
-        return c;
-    }
-}
-
-mp_uint_t asm_x64_get_code_pos(asm_x64_t *as) {
-    return as->code_offset;
-}
-
-mp_uint_t asm_x64_get_code_size(asm_x64_t *as) {
-    return as->code_size;
-}
-
-void *asm_x64_get_code(asm_x64_t *as) {
-    return as->code_base;
+static inline byte *asm_x64_get_cur_to_write_bytes(asm_x64_t *as, int n) {
+    return mp_asm_base_get_cur_to_write_bytes(&as->base, n);
 }
 
 STATIC void asm_x64_write_byte_1(asm_x64_t *as, byte b1) {
@@ -230,21 +158,6 @@ STATIC void asm_x64_write_word64(asm_x64_t *as, int64_t w64) {
     c[7] = IMM64_L7(w64);
 }
 
-// align must be a multiple of 2
-void asm_x64_align(asm_x64_t* as, mp_uint_t align) {
-    // TODO fill unused data with NOPs?
-    as->code_offset = (as->code_offset + align - 1) & (~(align - 1));
-}
-
-void asm_x64_data(asm_x64_t* as, mp_uint_t bytesize, mp_uint_t val) {
-    byte *c = asm_x64_get_cur_to_write_bytes(as, bytesize);
-    // machine is little endian
-    for (uint i = 0; i < bytesize; i++) {
-        *c++ = val;
-        val >>= 8;
-    }
-}
-
 /* unused
 STATIC void asm_x64_write_word32_to(asm_x64_t *as, int offset, int w32) {
     byte* c;
@@ -440,7 +353,7 @@ void asm_x64_mov_i64_to_r64_optimised(asm_x64_t *as, int64_t src_i64, int dest_r
 // src_i64 is stored as a full word in the code, and aligned to machine-word boundary
 void asm_x64_mov_i64_to_r64_aligned(asm_x64_t *as, int64_t src_i64, int dest_r64) {
     // mov instruction uses 2 bytes for the instruction, before the i64
-    while (((as->code_offset + 2) & (WORD_SIZE - 1)) != 0) {
+    while (((as->base.code_offset + 2) & (WORD_SIZE - 1)) != 0) {
         asm_x64_nop(as);
     }
     asm_x64_mov_i64_to_r64(as, src_i64, dest_r64);
@@ -552,27 +465,14 @@ void asm_x64_setcc_r8(asm_x64_t *as, int jcc_type, int dest_r8) {
     asm_x64_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R64(0) | MODRM_RM_REG | MODRM_RM_R64(dest_r8));
 }
 
-void asm_x64_label_assign(asm_x64_t *as, mp_uint_t label) {
-    assert(label < as->max_num_labels);
-    if (as->pass < ASM_X64_PASS_EMIT) {
-        // assign label offset
-        assert(as->label_offsets[label] == (mp_uint_t)-1);
-        as->label_offsets[label] = as->code_offset;
-    } else {
-        // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT
-        //printf("l%d: (at %ld=%ld)\n", label, as->label_offsets[label], as->code_offset);
-        assert(as->label_offsets[label] == as->code_offset);
-    }
-}
-
 STATIC mp_uint_t get_label_dest(asm_x64_t *as, mp_uint_t label) {
-    assert(label < as->max_num_labels);
-    return as->label_offsets[label];
+    assert(label < as->base.max_num_labels);
+    return as->base.label_offsets[label];
 }
 
 void asm_x64_jmp_label(asm_x64_t *as, mp_uint_t label) {
     mp_uint_t dest = get_label_dest(as, label);
-    mp_int_t rel = dest - as->code_offset;
+    mp_int_t rel = dest - as->base.code_offset;
     if (dest != (mp_uint_t)-1 && rel < 0) {
         // is a backwards jump, so we know the size of the jump on the first pass
         // calculate rel assuming 8 bit relative jump
@@ -594,7 +494,7 @@ void asm_x64_jmp_label(asm_x64_t *as, mp_uint_t label) {
 
 void asm_x64_jcc_label(asm_x64_t *as, int jcc_type, mp_uint_t label) {
     mp_uint_t dest = get_label_dest(as, label);
-    mp_int_t rel = dest - as->code_offset;
+    mp_int_t rel = dest - as->base.code_offset;
     if (dest != (mp_uint_t)-1 && rel < 0) {
         // is a backwards jump, so we know the size of the jump on the first pass
         // calculate rel assuming 8 bit relative jump
diff --git a/py/asmx64.h b/py/asmx64.h
index 6fbc2c906..daf5b3d0b 100644
--- a/py/asmx64.h
+++ b/py/asmx64.h
@@ -28,6 +28,7 @@
 
 #include "py/mpconfig.h"
 #include "py/misc.h"
+#include "py/asmbase.h"
 
 // AMD64 calling convention is:
 //  - args pass in: RDI, RSI, RDX, RCX, R08, R09
@@ -41,9 +42,6 @@
 // NOTE: this is a change from the old convention used in this file and
 // some functions still use the old (reverse) convention.
 
-#define ASM_X64_PASS_COMPUTE (1)
-#define ASM_X64_PASS_EMIT    (2)
-
 #define ASM_X64_REG_RAX (0)
 #define ASM_X64_REG_RCX (1)
 #define ASM_X64_REG_RDX (2)
@@ -72,18 +70,15 @@
 #define ASM_X64_CC_JLE (0xe) // less or equal, signed
 #define ASM_X64_CC_JG  (0xf) // greater, signed
 
-typedef struct _asm_x64_t asm_x64_t;
-
-asm_x64_t* asm_x64_new(mp_uint_t max_num_labels);
-void asm_x64_free(asm_x64_t* as, bool free_code);
-void asm_x64_start_pass(asm_x64_t *as, uint pass);
-void asm_x64_end_pass(asm_x64_t *as);
-mp_uint_t asm_x64_get_code_pos(asm_x64_t *as);
-mp_uint_t asm_x64_get_code_size(asm_x64_t* as);
-void* asm_x64_get_code(asm_x64_t* as);
+typedef struct _asm_x64_t {
+    mp_asm_base_t base;
+    byte dummy_data[4]; // in addition to dummy_data in base
+    int num_locals;
+} asm_x64_t;
 
-void asm_x64_align(asm_x64_t *as, mp_uint_t align);
-void asm_x64_data(asm_x64_t *as, mp_uint_t bytesize, mp_uint_t val);
+static inline void asm_x64_end_pass(asm_x64_t *as) {
+    (void)as;
+}
 
 void asm_x64_nop(asm_x64_t* as);
 void asm_x64_push_r64(asm_x64_t* as, int src_r64);
@@ -111,7 +106,6 @@ void asm_x64_mul_r64_r64(asm_x64_t* as, int dest_r64, int src_r64);
 void asm_x64_cmp_r64_with_r64(asm_x64_t* as, int src_r64_a, int src_r64_b);
 void asm_x64_test_r8_with_r8(asm_x64_t* as, int src_r64_a, int src_r64_b);
 void asm_x64_setcc_r8(asm_x64_t* as, int jcc_type, int dest_r8);
-void asm_x64_label_assign(asm_x64_t* as, mp_uint_t label);
 void asm_x64_jmp_label(asm_x64_t* as, mp_uint_t label);
 void asm_x64_jcc_label(asm_x64_t* as, int jcc_type, mp_uint_t label);
 void asm_x64_entry(asm_x64_t* as, int num_locals);
diff --git a/py/asmx86.c b/py/asmx86.c
index 40958826f..cb9b30d40 100644
--- a/py/asmx86.c
+++ b/py/asmx86.c
@@ -100,80 +100,6 @@
 
 #define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)
 
-struct _asm_x86_t {
-    uint pass;
-    mp_uint_t code_offset;
-    mp_uint_t code_size;
-    byte *code_base;
-    byte dummy_data[8];
-
-    mp_uint_t max_num_labels;
-    mp_uint_t *label_offsets;
-    int num_locals;
-};
-
-asm_x86_t *asm_x86_new(mp_uint_t max_num_labels) {
-    asm_x86_t *as;
-
-    as = m_new0(asm_x86_t, 1);
-    as->max_num_labels = max_num_labels;
-    as->label_offsets = m_new(mp_uint_t, max_num_labels);
-
-    return as;
-}
-
-void asm_x86_free(asm_x86_t *as, bool free_code) {
-    if (free_code) {
-        MP_PLAT_FREE_EXEC(as->code_base, as->code_size);
-    }
-    m_del(mp_uint_t, as->label_offsets, as->max_num_labels);
-    m_del_obj(asm_x86_t, as);
-}
-
-void asm_x86_start_pass(asm_x86_t *as, mp_uint_t pass) {
-    if (pass == ASM_X86_PASS_COMPUTE) {
-        // reset all labels
-        memset(as->label_offsets, -1, as->max_num_labels * sizeof(mp_uint_t));
-    } else if (pass == ASM_X86_PASS_EMIT) {
-        MP_PLAT_ALLOC_EXEC(as->code_offset, (void**)&as->code_base, &as->code_size);
-        if (as->code_base == NULL) {
-            assert(0);
-        }
-    }
-    as->pass = pass;
-    as->code_offset = 0;
-}
-
-void asm_x86_end_pass(asm_x86_t *as) {
-    (void)as;
-}
-
-// all functions must go through this one to emit bytes
-STATIC byte *asm_x86_get_cur_to_write_bytes(asm_x86_t *as, int num_bytes_to_write) {
-    //printf("emit %d\n", num_bytes_to_write);
-    if (as->pass < ASM_X86_PASS_EMIT) {
-        as->code_offset += num_bytes_to_write;
-        return as->dummy_data;
-    } else {
-        assert(as->code_offset + num_bytes_to_write <= as->code_size);
-        byte *c = as->code_base + as->code_offset;
-        as->code_offset += num_bytes_to_write;
-        return c;
-    }
-}
-
-mp_uint_t asm_x86_get_code_pos(asm_x86_t *as) {
-    return as->code_offset;
-}
-
-mp_uint_t asm_x86_get_code_size(asm_x86_t *as) {
-    return as->code_size;
-}
-
-void *asm_x86_get_code(asm_x86_t *as) {
-    return as->code_base;
-}
-
 STATIC void asm_x86_write_byte_1(asm_x86_t *as, byte b1) {
     byte* c = asm_x86_get_cur_to_write_bytes(as, 1);
     c[0] = b1;
@@ -200,21 +126,6 @@ STATIC void asm_x86_write_word32(asm_x86_t *as, int w32) {
     c[3] = IMM32_L3(w32);
 }
 
-// align must be a multiple of 2
-void asm_x86_align(asm_x86_t* as, mp_uint_t align) {
-    // TODO fill unused data with NOPs?
-    as->code_offset = (as->code_offset + align - 1) & (~(align - 1));
-}
-
-void asm_x86_data(asm_x86_t* as, mp_uint_t bytesize, mp_uint_t val) {
-    byte *c = asm_x86_get_cur_to_write_bytes(as, bytesize);
-    // machine is little endian
-    for (uint i = 0; i < bytesize; i++) {
-        *c++ = val;
-        val >>= 8;
-    }
-}
-
 STATIC void asm_x86_write_r32_disp(asm_x86_t *as, int r32, int disp_r32, int disp_offset) {
     assert(disp_r32 != ASM_X86_REG_ESP);
 
@@ -419,19 +330,6 @@ void asm_x86_setcc_r8(asm_x86_t *as, mp_uint_t jcc_type, int dest_r8) {
     asm_x86_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r8));
 }
 
-void asm_x86_label_assign(asm_x86_t *as, mp_uint_t label) {
-    assert(label < as->max_num_labels);
-    if (as->pass < ASM_X86_PASS_EMIT) {
-        // assign label offset
-        assert(as->label_offsets[label] == (mp_uint_t)-1);
-        as->label_offsets[label] = as->code_offset;
-    } else {
-        // ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT
-        //printf("l%d: (at %d=%ld)\n", label, as->label_offsets[label], as->code_offset);
-        assert(as->label_offsets[label] == as->code_offset);
-    }
-}
-
 STATIC mp_uint_t get_label_dest(asm_x86_t *as, mp_uint_t label) {
     assert(label < as->max_num_labels);
     return as->label_offsets[label];
diff --git a/py/asmx86.h b/py/asmx86.h
index e0c57722a..85352fff3 100644
--- a/py/asmx86.h
+++ b/py/asmx86.h
@@ -28,6 +28,7 @@
 
 #include "py/mpconfig.h"
 #include "py/misc.h"
+#include "py/asmbase.h"
 
 // x86 cdecl calling convention is:
 //  - args passed on the stack in reverse order
@@ -42,9 +43,6 @@
 // NOTE: this is a change from the old convention used in this file and
 // some functions still use the old (reverse) convention.
 
-#define ASM_X86_PASS_COMPUTE (1)
-#define ASM_X86_PASS_EMIT    (2)
-
 #define ASM_X86_REG_EAX (0)
 #define ASM_X86_REG_ECX (1)
 #define ASM_X86_REG_EDX (2)
@@ -75,18 +73,15 @@
 #define ASM_X86_CC_JLE (0xe) // less or equal, signed
 #define ASM_X86_CC_JG  (0xf) // greater, signed
 
-typedef struct _asm_x86_t asm_x86_t;
-
-asm_x86_t* asm_x86_new(mp_uint_t max_num_labels);
-void asm_x86_free(asm_x86_t* as, bool free_code);
-void asm_x86_start_pass(asm_x86_t *as, mp_uint_t pass);
-void asm_x86_end_pass(asm_x86_t *as);
-mp_uint_t asm_x86_get_code_pos(asm_x86_t *as);
-mp_uint_t asm_x86_get_code_size(asm_x86_t* as);
-void* asm_x86_get_code(asm_x86_t* as);
+typedef struct _asm_x86_t {
+    mp_asm_base_t base;
+    byte dummy_data[4]; // in addition to dummy_data in base
+    int num_locals;
+} asm_x86_t;
 
-void asm_x86_align(asm_x86_t *as, mp_uint_t align);
-void asm_x86_data(asm_x86_t *as, mp_uint_t bytesize, mp_uint_t val);
+static inline void asm_x86_end_pass(asm_x86_t *as) {
+    (void)as;
+}
 
 void asm_x86_mov_r32_r32(asm_x86_t* as, int dest_r32, int src_r32);
 void asm_x86_mov_i32_to_r32(asm_x86_t *as, int32_t src_i32, int dest_r32);
@@ -108,7 +103,6 @@ void asm_x86_mul_r32_r32(asm_x86_t* as, int dest_r32, int src_r32);
 void asm_x86_cmp_r32_with_r32(asm_x86_t* as, int src_r32_a, int src_r32_b);
 void asm_x86_test_r8_with_r8(asm_x86_t* as, int src_r32_a, int src_r32_b);
 void asm_x86_setcc_r8(asm_x86_t* as, mp_uint_t jcc_type, int dest_r8);
-void asm_x86_label_assign(asm_x86_t* as, mp_uint_t label);
 void asm_x86_jmp_label(asm_x86_t* as, mp_uint_t label);
 void asm_x86_jcc_label(asm_x86_t* as, mp_uint_t jcc_type, mp_uint_t label);
 void asm_x86_entry(asm_x86_t* as, mp_uint_t num_locals);
diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c
index 90a68caa6..760ec8cc0 100644
--- a/py/emitinlinethumb.c
+++ b/py/emitinlinethumb.c
@@ -63,13 +63,15 @@ emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels) {
     emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t);
     emit->max_num_labels = max_num_labels;
     emit->label_lookup = m_new(qstr, max_num_labels);
-    emit->as = asm_thumb_new(max_num_labels);
+    emit->as = m_new0(asm_thumb_t, 1);
+    mp_asm_base_init(&emit->as->base, max_num_labels);
     return emit;
 }
 
 void emit_inline_thumb_free(emit_inline_asm_t *emit) {
     m_del(qstr, emit->label_lookup, emit->max_num_labels);
-    asm_thumb_free(emit->as, false);
+    mp_asm_base_deinit(&emit->as->base, false);
+    m_del_obj(asm_thumb_t, emit->as);
     m_del_obj(emit_inline_asm_t, emit);
 }
 
@@ -80,7 +82,7 @@ STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pa
     if (emit->pass == MP_PASS_CODE_SIZE) {
         memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr));
     }
-    asm_thumb_start_pass(emit->as, pass == MP_PASS_EMIT ? ASM_THUMB_PASS_EMIT : ASM_THUMB_PASS_COMPUTE);
+    mp_asm_base_start_pass(&emit->as->base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE);
     asm_thumb_entry(emit->as, 0);
 }
 
@@ -89,9 +91,9 @@ STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit, mp_uint_t type_s
     asm_thumb_end_pass(emit->as);
 
     if (emit->pass == MP_PASS_EMIT) {
-        void *f = asm_thumb_get_code(emit->as);
+        void *f = mp_asm_base_get_code(&emit->as->base);
         mp_emit_glue_assign_native(emit->scope->raw_code, MP_CODE_NATIVE_ASM, f,
-            asm_thumb_get_code_size(emit->as), NULL, emit->scope->num_pos_args, 0, type_sig);
+            mp_asm_base_get_code_size(&emit->as->base), NULL, emit->scope->num_pos_args, 0, type_sig);
     }
 }
 
@@ -125,16 +127,16 @@ STATIC bool emit_inline_thumb_label(emit_inline_asm_t *emit, mp_uint_t label_num
         }
     }
     emit->label_lookup[label_num] = label_id;
-    asm_thumb_label_assign(emit->as, label_num);
+    mp_asm_base_label_assign(&emit->as->base, label_num);
     return true;
 }
 
 STATIC void emit_inline_thumb_align(emit_inline_asm_t *emit, mp_uint_t align) {
-    asm_thumb_align(emit->as, align);
+    mp_asm_base_align(&emit->as->base, align);
 }
 
 STATIC void emit_inline_thumb_data(emit_inline_asm_t *emit, mp_uint_t bytesize, mp_uint_t val) {
-    asm_thumb_data(emit->as, bytesize, val);
+    mp_asm_base_data(&emit->as->base, bytesize, val);
 }
 
 typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t;
diff --git a/py/emitnative.c b/py/emitnative.c
index 46e1076a9..d19dfe467 100644
--- a/py/emitnative.c
+++ b/py/emitnative.c
@@ -91,24 +91,11 @@
 #define REG_LOCAL_3 ASM_X64_REG_R13
 #define REG_LOCAL_NUM (3)
 
-#define ASM_PASS_COMPUTE    ASM_X64_PASS_COMPUTE
-#define ASM_PASS_EMIT       ASM_X64_PASS_EMIT
-
 #define ASM_T               asm_x64_t
-#define ASM_NEW             asm_x64_new
-#define ASM_FREE            asm_x64_free
-#define ASM_GET_CODE        asm_x64_get_code
-#define ASM_GET_CODE_POS    asm_x64_get_code_pos
-#define ASM_GET_CODE_SIZE   asm_x64_get_code_size
-#define ASM_START_PASS      asm_x64_start_pass
 #define ASM_END_PASS        asm_x64_end_pass
 #define ASM_ENTRY           asm_x64_entry
 #define ASM_EXIT            asm_x64_exit
 
-#define ASM_ALIGN           asm_x64_align
-#define ASM_DATA            asm_x64_data
-
-#define ASM_LABEL_ASSIGN    asm_x64_label_assign
 #define ASM_JUMP            asm_x64_jmp_label
 #define ASM_JUMP_IF_REG_ZERO(as, reg, label) \
     do { \
@@ -236,24 +223,11 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = {
 #define REG_LOCAL_3 ASM_X86_REG_EDI
 #define REG_LOCAL_NUM (3)
 
-#define ASM_PASS_COMPUTE    ASM_X86_PASS_COMPUTE
-#define ASM_PASS_EMIT       ASM_X86_PASS_EMIT
-
 #define ASM_T               asm_x86_t
-#define ASM_NEW             asm_x86_new
-#define ASM_FREE            asm_x86_free
-#define ASM_GET_CODE        asm_x86_get_code
-#define ASM_GET_CODE_POS    asm_x86_get_code_pos
-#define ASM_GET_CODE_SIZE   asm_x86_get_code_size
-#define ASM_START_PASS      asm_x86_start_pass
 #define ASM_END_PASS        asm_x86_end_pass
 #define ASM_ENTRY           asm_x86_entry
 #define ASM_EXIT            asm_x86_exit
 
-#define ASM_ALIGN           asm_x86_align
-#define ASM_DATA            asm_x86_data
-
-#define ASM_LABEL_ASSIGN    asm_x86_label_assign
 #define ASM_JUMP            asm_x86_jmp_label
 #define ASM_JUMP_IF_REG_ZERO(as, reg, label) \
     do { \
@@ -331,24 +305,11 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = {
 #define REG_LOCAL_3 ASM_THUMB_REG_R6
 #define REG_LOCAL_NUM (3)
 
-#define ASM_PASS_COMPUTE    ASM_THUMB_PASS_COMPUTE
-#define ASM_PASS_EMIT       ASM_THUMB_PASS_EMIT
-
 #define ASM_T               asm_thumb_t
-#define ASM_NEW             asm_thumb_new
-#define ASM_FREE            asm_thumb_free
-#define ASM_GET_CODE        asm_thumb_get_code
-#define ASM_GET_CODE_POS    asm_thumb_get_code_pos
-#define ASM_GET_CODE_SIZE   asm_thumb_get_code_size
-#define ASM_START_PASS      asm_thumb_start_pass
 #define ASM_END_PASS        asm_thumb_end_pass
 #define ASM_ENTRY           asm_thumb_entry
 #define ASM_EXIT            asm_thumb_exit
 
-#define ASM_ALIGN           asm_thumb_align
-#define ASM_DATA            asm_thumb_data
-
-#define ASM_LABEL_ASSIGN    asm_thumb_label_assign
 #define ASM_JUMP            asm_thumb_b_label
 #define ASM_JUMP_IF_REG_ZERO(as, reg, label) \
     do { \
@@ -425,24 +386,11 @@ STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = {
 #define REG_LOCAL_3 ASM_ARM_REG_R6
 #define REG_LOCAL_NUM (3)
 
-#define ASM_PASS_COMPUTE    ASM_ARM_PASS_COMPUTE
-#define ASM_PASS_EMIT       ASM_ARM_PASS_EMIT
-
 #define ASM_T               asm_arm_t
-#define ASM_NEW             asm_arm_new
-#define ASM_FREE            asm_arm_free
-#define ASM_GET_CODE        asm_arm_get_code
-#define ASM_GET_CODE_POS    asm_arm_get_code_pos
-#define ASM_GET_CODE_SIZE   asm_arm_get_code_size
-#define ASM_START_PASS      asm_arm_start_pass
 #define ASM_END_PASS        asm_arm_end_pass
 #define ASM_ENTRY           asm_arm_entry
 #define ASM_EXIT            asm_arm_exit
 
-#define ASM_ALIGN           asm_arm_align
-#define ASM_DATA            asm_arm_data
-
-#define ASM_LABEL_ASSIGN    asm_arm_label_assign
 #define ASM_JUMP            asm_arm_b_label
 #define ASM_JUMP_IF_REG_ZERO(as, reg, label) \
     do { \
@@ -582,12 +530,14 @@ struct _emit_t {
 emit_t *EXPORT_FUN(new)(mp_obj_t *error_slot, mp_uint_t max_num_labels) {
     emit_t *emit = m_new0(emit_t, 1);
     emit->error_slot = error_slot;
-    emit->as = ASM_NEW(max_num_labels);
+    emit->as = m_new0(ASM_T, 1);
+    mp_asm_base_init(&emit->as->base, max_num_labels);
     return emit;
 }
 
 void EXPORT_FUN(free)(emit_t *emit) {
-    ASM_FREE(emit->as, false);
+    mp_asm_base_deinit(&emit->as->base, false);
+    m_del_obj(ASM_T, emit->as);
     m_del(vtype_kind_t, emit->local_vtype, emit->local_vtype_alloc);
     m_del(stack_info_t, emit->stack_info, emit->stack_info_alloc);
     m_del_obj(emit_t, emit);
@@ -679,7 +629,7 @@ STATIC void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop
         emit->stack_info[i].vtype = VTYPE_UNBOUND;
     }
 
-    ASM_START_PASS(emit->as, pass == MP_PASS_EMIT ? ASM_PASS_EMIT : ASM_PASS_COMPUTE);
+    mp_asm_base_start_pass(&emit->as->base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE);
 
     // generate code for entry to function
 
@@ -824,21 +774,21 @@ STATIC void emit_native_end_pass(emit_t *emit) {
     }
 
     if (!emit->do_viper_types) {
-        emit->prelude_offset = ASM_GET_CODE_POS(emit->as);
-        ASM_DATA(emit->as, 1, emit->scope->scope_flags);
-        ASM_DATA(emit->as, 1, emit->scope->num_pos_args);
-        ASM_DATA(emit->as, 1, emit->scope->num_kwonly_args);
-        ASM_DATA(emit->as, 1, emit->scope->num_def_pos_args);
+        emit->prelude_offset = mp_asm_base_get_code_pos(&emit->as->base);
+        mp_asm_base_data(&emit->as->base, 1, emit->scope->scope_flags);
+        mp_asm_base_data(&emit->as->base, 1, emit->scope->num_pos_args);
+        mp_asm_base_data(&emit->as->base, 1, emit->scope->num_kwonly_args);
+        mp_asm_base_data(&emit->as->base, 1, emit->scope->num_def_pos_args);
 
         // write code info
         #if MICROPY_PERSISTENT_CODE
-        ASM_DATA(emit->as, 1, 5);
-        ASM_DATA(emit->as, 1, emit->scope->simple_name);
-        ASM_DATA(emit->as, 1, emit->scope->simple_name >> 8);
-        ASM_DATA(emit->as, 1, emit->scope->source_file);
-        ASM_DATA(emit->as, 1, emit->scope->source_file >> 8);
+        mp_asm_base_data(&emit->as->base, 1, 5);
+        mp_asm_base_data(&emit->as->base, 1, emit->scope->simple_name);
+        mp_asm_base_data(&emit->as->base, 1, emit->scope->simple_name >> 8);
+        mp_asm_base_data(&emit->as->base, 1, emit->scope->source_file);
+        mp_asm_base_data(&emit->as->base, 1, emit->scope->source_file >> 8);
         #else
-        ASM_DATA(emit->as, 1, 1);
+        mp_asm_base_data(&emit->as->base, 1, 1);
         #endif
 
         // bytecode prelude: initialise closed over variables
@@ -846,13 +796,13 @@ STATIC void emit_native_end_pass(emit_t *emit) {
             id_info_t *id = &emit->scope->id_info[i];
             if (id->kind == ID_INFO_KIND_CELL) {
                 assert(id->local_num < 255);
-                ASM_DATA(emit->as, 1, id->local_num); // write the local which should be converted to a cell
+                mp_asm_base_data(&emit->as->base, 1, id->local_num); // write the local which should be converted to a cell
             }
         }
-        ASM_DATA(emit->as, 1, 255); // end of list sentinel
+        mp_asm_base_data(&emit->as->base, 1, 255); // end of list sentinel
 
-        ASM_ALIGN(emit->as, ASM_WORD_SIZE);
-        emit->const_table_offset = ASM_GET_CODE_POS(emit->as);
+        mp_asm_base_align(&emit->as->base, ASM_WORD_SIZE);
+        emit->const_table_offset = mp_asm_base_get_code_pos(&emit->as->base);
 
         // write argument names as qstr objects
         // see comment in corresponding part of emitbc.c about the logic here
@@ -865,7 +815,7 @@ STATIC void emit_native_end_pass(emit_t *emit) {
                     break;
                 }
             }
-            ASM_DATA(emit->as, ASM_WORD_SIZE, (mp_uint_t)MP_OBJ_NEW_QSTR(qst));
+            mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (mp_uint_t)MP_OBJ_NEW_QSTR(qst));
         }
 
     }
@@ -878,8 +828,8 @@ STATIC void emit_native_end_pass(emit_t *emit) {
     }
 
     if (emit->pass == MP_PASS_EMIT) {
-        void *f = ASM_GET_CODE(emit->as);
-        mp_uint_t f_len = ASM_GET_CODE_SIZE(emit->as);
+        void *f = mp_asm_base_get_code(&emit->as->base);
+        mp_uint_t f_len = mp_asm_base_get_code_size(&emit->as->base);
 
         // compute type signature
         // note that the lower 4 bits of a vtype are tho correct MP_NATIVE_TYPE_xxx
@@ -1255,7 +1205,7 @@ STATIC void emit_native_label_assign(emit_t *emit, mp_uint_t l) {
     emit_native_pre(emit);
     // need to commit stack because we can jump here from elsewhere
     need_stack_settled(emit);
-    ASM_LABEL_ASSIGN(emit->as, l);
+    mp_asm_base_label_assign(&emit->as->base, l);
     emit_post(emit);
 }
 
diff --git a/py/py.mk b/py/py.mk
index ec67ac2a0..87a860f75 100644
--- a/py/py.mk
+++ b/py/py.mk
@@ -120,6 +120,7 @@ PY_O_BASENAME = \
 	compile.o \
 	emitcommon.o \
 	emitbc.o \
+	asmbase.o \
 	asmx64.o \
 	emitnx64.o \
 	asmx86.o \
-- 
GitLab